Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add add_credit_by_traffic and set_atmos_pubkey #83

Merged
merged 9 commits into from Oct 18, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
32 changes: 32 additions & 0 deletions pallets/credit/src/lib.rs
Expand Up @@ -116,6 +116,7 @@ pub trait CreditInterface<AccountId, Balance> {
) -> (Option<(Balance, Balance)>, Weight);
fn get_top_referee_reward(account_id: &AccountId) -> (Balance, Weight);
fn update_credit(micropayment: (AccountId, Balance));
fn update_credit_by_traffic(server: AccountId);
}

#[frame_support::pallet]
Expand Down Expand Up @@ -707,6 +708,37 @@ pub mod pallet {
}
}
}

/// update credit score by traffic
fn update_credit_by_traffic(server_id: T::AccountId) {
let onboard_era = Self::get_onboard_era(&server_id);
if onboard_era.is_none() {
// credit is not updated if the device is never online
return;
}
let current_era = Self::get_current_era();
// if this is the first update, we use onboard era as the last update era
let last_credit_update_era =
Self::last_credit_update(&server_id).unwrap_or(onboard_era.unwrap());
let eras = (current_era - last_credit_update_era) as u64;
if eras >= 2 {
let cap: u64 = T::CreditCapTwoEras::get() as u64;
let new_credit = Self::get_credit_score(&server_id)
.unwrap_or(0)
.saturating_add(cap);
if Self::_update_credit(&server_id, new_credit) {
LastCreditUpdate::<T>::insert(&server_id, current_era);
Self::update_credit_history(&server_id, current_era);
} else {
log!(
error,
"failed to update credit {} for server_id: {:?}",
new_credit,
server_id.clone()
);
}
}
}
}
}

Expand Down
24 changes: 24 additions & 0 deletions pallets/credit/src/tests.rs
Expand Up @@ -233,6 +233,30 @@ fn update_credit() {
});
}

#[test]
fn update_credit_by_traffic() {
new_test_ext().execute_with(|| {
Credit::update_credit_by_traffic(1);
assert_eq!(Credit::user_credit(&1).unwrap().credit, 0);

assert_ok!(DeeperNode::im_online(Origin::signed(1)));
Credit::update_credit_by_traffic(1);
assert_eq!(Credit::user_credit(&1).unwrap().credit, 0);

run_to_block(BLOCKS_PER_ERA * 2);
Credit::update_credit_by_traffic(1);
assert_eq!(Credit::user_credit(&1).unwrap().credit, 1); // 0 + 1

run_to_block(BLOCKS_PER_ERA * 3);
Credit::update_credit_by_traffic(1);
assert_eq!(Credit::user_credit(&1).unwrap().credit, 1); // 1 + 0

run_to_block(BLOCKS_PER_ERA * 4);
Credit::update_credit_by_traffic(1);
assert_eq!(Credit::user_credit(&1).unwrap().credit, 2); // 1 + 1
});
}

#[test]
fn get_reward_work() {
new_test_ext().execute_with(|| {
Expand Down
70 changes: 69 additions & 1 deletion pallets/micropayment/src/lib.rs
Expand Up @@ -134,6 +134,16 @@ pub mod pallet {
pub(super) type TotalMicropaymentChannelBalance<T: Config> =
StorageMap<_, Blake2_128Concat, T::AccountId, BalanceOf<T>, OptionQuery>;

// atmos_nonce indicates the next available value;
#[pallet::storage]
#[pallet::getter(fn atmos_nonce)]
pub(super) type AtmosNonce<T: Config> =
StorageMap<_, Blake2_128Concat, T::AccountId, u64, OptionQuery>;

#[pallet::storage]
#[pallet::getter(fn atmos_accountid)]
pub(super) type AtmosAccountid<T: Config> = StorageValue<_, T::AccountId>;

// Pallets use events to inform users when important changes are made.
// https://substrate.dev/docs/en/knowledgebase/runtime/events
#[pallet::event]
Expand Down Expand Up @@ -169,6 +179,8 @@ pub mod pallet {
SessionError,
// Invalid signature
InvalidSignature,
/// Invalid atomos nonce
InvalidAtomosNonce,
}

#[pallet::hooks]
Expand Down Expand Up @@ -426,6 +438,36 @@ pub mod pallet {
Self::deposit_event(Event::ClaimPayment(client, server, amount));
Ok(().into())
}

#[pallet::weight(10000)]
pub fn add_credit_by_traffic(
origin: OriginFor<T>,
nonce: u64,
signature: Vec<u8>,
) -> DispatchResultWithPostInfo {
let server = ensure_signed(origin)?;

let atmos_nonce_of_server = Self::atmos_nonce(&server).unwrap_or_default();
ensure!(
nonce == atmos_nonce_of_server,
Error::<T>::InvalidAtomosNonce
);

Self::verify_atomos_signature(nonce, &signature, server.clone())?;
AtmosNonce::<T>::insert(&server, atmos_nonce_of_server + 1u64);
T::CreditInterface::update_credit_by_traffic(server.clone());
Ok(().into())
}

#[pallet::weight(10000)]
pub fn set_atmos_pubkey(
origin: OriginFor<T>,
pubkey: T::AccountId,
) -> DispatchResultWithPostInfo {
ensure_root(origin)?;
<AtmosAccountid<T>>::put(pubkey);
Ok(().into())
}
}

impl<T: Config> Pallet<T> {
Expand Down Expand Up @@ -468,7 +510,33 @@ pub mod pallet {
Ok(().into())
}

/// construct data from |server_addr|session_id|amount| and hash it
pub fn verify_atomos_signature(
nonce: u64,
signature: &Vec<u8>,
sender: T::AccountId,
) -> DispatchResultWithPostInfo {
let mut pk = [0u8; 32];
let atomos_accountid = Self::atmos_accountid().unwrap_or_default();
pk.copy_from_slice(&atomos_accountid.encode());
let pub_key = sr25519::Public::from_raw(pk);

let mut sig = [0u8; 64];
sig.copy_from_slice(&signature);
let sig = sr25519::Signature::from_slice(&sig);

let mut data = Vec::new();
data.extend_from_slice(&atomos_accountid.encode());
data.extend_from_slice(&nonce.to_be_bytes());
data.extend_from_slice(&sender.encode());
let msg = sp_io::hashing::blake2_256(&data);

let verified = sr25519_verify(&sig, &msg, &pub_key);
ensure!(verified, Error::<T>::InvalidSignature);

Ok(().into())
}

// construct data from |server_addr|session_id|amount| and hash it
pub fn construct_byte_array_and_hash(
address: &T::AccountId,
nonce: u64,
Expand Down
50 changes: 48 additions & 2 deletions pallets/micropayment/src/tests.rs
Expand Up @@ -16,9 +16,12 @@
use super::Chan;
use crate::{mock::*, testing_utils::*, Error};
use frame_support::{
assert_ok,
assert_noop, assert_ok,
codec::Encode,
dispatch::{DispatchError, DispatchErrorWithPostInfo},
error::BadOrigin,
};
use frame_system::RawOrigin;
use hex_literal::hex;
use sp_core::sr25519::{Public, Signature};
use sp_io::crypto::sr25519_verify;
Expand Down Expand Up @@ -239,7 +242,50 @@ fn claim_payment() {
}

#[test]
fn blake2_hash() {
fn add_credit_by_traffic() {
new_test_ext().execute_with(|| {
// OK
assert_ok!(Micropayment::set_atmos_pubkey(
RawOrigin::Root.into(),
bob(),
));

let nonce: u64 = 0;

let mut data = Vec::new();
data.extend_from_slice(&bob().encode());
data.extend_from_slice(&nonce.to_be_bytes());
data.extend_from_slice(&alice().encode());
let _msg = sp_io::hashing::blake2_256(&data);

let signature: [u8; 64] = hex!("5071a1a526b1d2d1833e4de43d1ce22ad3506de2e10ee4a9c18c0b310c54286b9cb10bfb4ee12be6b93e91337de0fa2ea2edd787d083db36211109bdc8438989");
assert_ok!(Micropayment::add_credit_by_traffic(
Origin::signed(alice()),
nonce, signature.into()
));

});
}

#[test]
fn set_atmos_pubkey() {
new_test_ext().execute_with(|| {
// OK
assert_ok!(Micropayment::set_atmos_pubkey(
RawOrigin::Root.into(),
bob(),
));

// BadOrigin
assert_noop!(
Micropayment::set_atmos_pubkey(Origin::signed(alice()), bob(),),
BadOrigin
);
});
}

#[test]
fn test_blake2_hash() {
let bob: [u8; 32] = [
142, 175, 4, 21, 22, 135, 115, 99, 38, 201, 254, 161, 126, 37, 252, 82, 135, 97, 54, 147,
201, 18, 144, 156, 178, 38, 170, 71, 148, 242, 106, 72,
Expand Down
120 changes: 120 additions & 0 deletions scripts/add_credit_by_traffic.js
@@ -0,0 +1,120 @@
const { ApiPromise, WsProvider, Keyring } = require("@polkadot/api");
const { blake2AsU8a, secp256k1KeypairFromSeed, cryptoWaitReady } = require("@polkadot/util-crypto");
const to = require("await-to-js").default;
const stringToU8a = require("@polkadot/util/string/toU8a").default;
const BN = require("bn.js");
const Utils = require("./utils.js");

//const serverHost = "wss://138.68.229.14:443";
//const serverHost = "wss://10.168.98.1:443";
const serverHost = "ws://127.0.0.1:9944";
const DPR = new BN("1000000000000000", 10); // base = 1e15 according to frontend apps, in code it's 1e14, fix it later;
const ONE_MILLION = 1000000;
// convert number in "atom" unit to DPR unit
// return float
function atomToDPR(amt) {
return amt / DPR;
}

// convert number in DPR unit to smallest "atom" unit
function DPRToAtom(amt) {
return BigInt(parseInt(amt * ONE_MILLION)) * BigInt(DPR / ONE_MILLION);
}

function toHexString(byteArray) {
return Array.from(byteArray, function (byte) {
return ("0" + (byte & 0xff).toString(16)).slice(-2);
}).join("");
}

// nonce:u64, session_id:u32
function construct_byte_array(addr, nonce, senderPubkey) {
let arr = [];
nonce = new BN(nonce.toString(), 10);
nonce = nonce.toArray("be", 8);
arr.push(...addr, ...nonce, ...senderPubkey);
return arr;
}

function claimPayment(api, sender, receiver, nonceNum, sessionIdNum, amtNum) {
return new Promise(function (resolve, reject) {
let nonce = new BN(nonceNum.toString(), 10);
let sessionId = new BN(sessionIdNum.toString(), 10);
let amount = new BN(amtNum.toString(), 10);
let amt = amount.mul(DPR);
let res = construct_byte_array(receiver.publicKey, nonce, sessionId, amt);
let msg = blake2AsU8a(res);
let signature = sender.sign(msg);
let hexsig = toHexString(signature);
console.log(`nonce: ${nonce}, session_id: ${sessionId}, amt: ${amount}, signature: ${hexsig}`);
api.tx.micropayment.claimPayment(sender.address, sessionId, amt, "0x" + hexsig).signAndSend(receiver, {nonce: -1}, ({ events = [], status }) => {
//console.log("Transaction status:", status.type);
if (status.isInBlock) {
//console.log("Included at block hash", status.asInBlock.toHex());
//console.log("Events:");
events.forEach(({ event: { data, method, section }, phase }) => {
//console.log("\t", phase.toString(), `: ${section}.${method}`, data.toString());
});
} else if (status.isFinalized) {
//console.log("Finalized block hash", status.asFinalized.toHex());
resolve();
}
});
});
}

async function addCreditByTrafficTest() {
//connect to chain
const api = await Utils.get_api(serverHost);
// accounts
const keyring = new Keyring({ type: "sr25519" });
const alice = keyring.addFromUri("//Alice");
const bob = keyring.addFromUri("//Bob");
const charlie = keyring.addFromUri("//Charlie");

// sudo set Atomos Accountid
let root = alice;
let atomsKeypair = keyring.addFromUri("rookie pet ramp sniff vibrant silent liquid burden push shield police ripple");
//set balance
await api.tx.sudo.sudo(
api.tx.balances.setBalance(atomsKeypair.address, new BN("2000000000000000000", 10), 0) // 2DPR
).signAndSend(root, { nonce: -1 });
//set atomos pubkey
await api.tx.sudo.sudo(
api.tx.micropayment.setAtmosPubkey(atomsKeypair.address)
).signAndSend(root, { nonce: -1 });

//set charliecreditData
const creditData = {
campaign_id: 0,
credit: 300,
initial_credit_level: 3,
rank_in_initial_credit_level: 1,
number_of_referees: 0,
current_credit_level: 3,
reward_eras: 270,
};
await api.tx.sudo.sudo(
api.tx.credit.addOrUpdateCreditData(alice.address, creditData)
).signAndSend(root, { nonce: -1 });
//charlie iamOnlie
await api.tx.deeperNode.imOnline().signAndSend(alice, { nonce: -1 });

//await Utils.sleep(1000* 60 * 19);

// charlie send message and signature which is signed by atomsKeypair
let alice_nonce_option = await api.query.micropayment.atmosNonce(alice.address);
let alice_nonce = alice_nonce_option.unwrapOr(0);

let res = construct_byte_array(atomsKeypair.publicKey, alice_nonce, alice.publicKey);
let msg = blake2AsU8a(res);
let signature = atomsKeypair.sign(msg);
let hexsig = "0x" + toHexString(signature);
console.log(`nonce: ${alice_nonce}, signature: ${hexsig}`);
await api.tx.micropayment.addCreditByTraffic(alice_nonce, hexsig)
.signAndSend(alice, { nonce: -1 });

await Utils.sleep(1000* 60 * 3);
}

addCreditByTrafficTest().catch(console.error).finally(() => process.exit());