diff --git a/cxrml/bridge/btc/src/keys.rs b/cxrml/bridge/btc/src/keys.rs index 47383fa34bf54..417711368813d 100644 --- a/cxrml/bridge/btc/src/keys.rs +++ b/cxrml/bridge/btc/src/keys.rs @@ -67,8 +67,8 @@ pub struct Address { pub hash: AddressHash, } -impl From<&ScriptAddress> for Address { - fn from(address: &ScriptAddress) -> Self { +impl<'a> From<&'a ScriptAddress> for Address { + fn from(address: &'a ScriptAddress) -> Self { let network = if NETWORK_ID == 1 { Network::Testnet } else { Network::Mainnet }; Address { kind: address.kind, diff --git a/cxrml/bridge/btc/src/script/script.rs b/cxrml/bridge/btc/src/script/script.rs index 84c250bbc5f9a..5142c8dcc432b 100644 --- a/cxrml/bridge/btc/src/script/script.rs +++ b/cxrml/bridge/btc/src/script/script.rs @@ -33,8 +33,8 @@ pub struct ScriptAddress { pub hash: AddressHash, } -impl From<&keys::Address> for ScriptAddress { - fn from(address: &keys::Address) -> Self { +impl<'a> From<&'a keys::Address> for ScriptAddress { + fn from(address: &'a keys::Address) -> Self { match address.kind { keys::Type::P2PKH => ScriptAddress::new_p2pkh(address.hash.clone()), keys::Type::P2SH => ScriptAddress::new_p2sh(address.hash.clone()), diff --git a/cxrml/financialrecords/src/lib.rs b/cxrml/financialrecords/src/lib.rs index cb7c070df9988..77a7552afdaa5 100644 --- a/cxrml/financialrecords/src/lib.rs +++ b/cxrml/financialrecords/src/lib.rs @@ -44,12 +44,13 @@ extern crate cxrml_tokenbalances as tokenbalances; mod tests; use rstd::prelude::*; -use rstd::result::Result; -use runtime_support::dispatch::Result as DispatchResult; +use rstd::result::Result as StdResult; +use runtime_support::dispatch::Result; use runtime_support::{StorageMap, StorageValue}; use runtime_primitives::traits::OnFinalise; -use cxsupport::StorageDoubleMap; +use system::ensure_signed; + use tokenbalances::Symbol; pub trait Trait: tokenbalances::Trait { @@ -59,11 +60,11 @@ pub trait Trait: tokenbalances::Trait { decl_module! { pub struct Module for enum Call where origin: T::Origin { - // no call for this module + fn withdraw(origin, sym: Symbol, value: T::TokenBalance) -> Result; /// set deposit fee, call by ROOT - fn set_deposit_fee(val: T::Balance) -> DispatchResult; + fn set_deposit_fee(val: T::Balance) -> Result; /// set withdrawal fee, call by ROOT - fn set_withdrawal_fee(val: T::Balance) -> DispatchResult; + fn set_withdrawal_fee(val: T::Balance) -> Result; } } @@ -208,41 +209,48 @@ decl_storage! { trait Store for Module as FinancialRecords { /// Record list length of every account pub RecordsLenOf get(records_len_of): map T::AccountId => u32; + /// Record list for every account, use accountid and index to index the record of account + pub RecordsOf: map (T::AccountId, u32) => Option>; + /// Last deposit index of a account and related symbol + pub LastDepositIndexOf get(last_deposit_index_of): map (T::AccountId, Symbol) => Option; + /// Last withdrawal index of a account and related symbol + pub LastWithdrawalIndexOf get(last_withdrawal_index_of): map (T::AccountId, Symbol) => Option; + /// Fee for deposit, can change by Root pub DepositFee get(deposit_fee) config(): T::Balance; /// Fee for withdrawal, can change by Root pub WithdrawalFee get(withdrawal_fee) config(): T::Balance; } } -/// Record list for every account, use accountid and index to index the record of account -pub(crate) struct RecordsOf(::rstd::marker::PhantomData); - -impl StorageDoubleMap for RecordsOf { - type Key1 = T::AccountId; - type Key2 = u32; - type Value = Record; - const PREFIX: &'static [u8] = b"FinancialRecords RecordsOf"; -} - -/// Last deposit index of a account and related symbol -pub(crate) struct LastDepositIndexOf(::rstd::marker::PhantomData); - -impl StorageDoubleMap for LastDepositIndexOf { - type Key1 = T::AccountId; - type Key2 = Symbol; - type Value = u32; - const PREFIX: &'static [u8] = b"FinancialRecords LastDepositIndexOf"; -} - -/// Last withdrawal index of a account and related symbol -pub(crate) struct LastWithdrawalIndexOf(::rstd::marker::PhantomData); +///// Record list for every account, use accountid and index to index the record of account +//pub(crate) struct RecordsOf(::rstd::marker::PhantomData); +// +//impl StorageDoubleMap for RecordsOf { +// type Key1 = T::AccountId; +// type Key2 = u32; +// type Value = Record; +// const PREFIX: &'static [u8] = b"FinancialRecords RecordsOf"; +//} -impl StorageDoubleMap for LastWithdrawalIndexOf { - type Key1 = T::AccountId; - type Key2 = Symbol; - type Value = u32; - const PREFIX: &'static [u8] = b"FinancialRecords LastWithdrawalIndexOf"; -} +///// Last deposit index of a account and related symbol +//pub(crate) struct LastDepositIndexOf(::rstd::marker::PhantomData); +// +//impl StorageDoubleMap for LastDepositIndexOf { +// type Key1 = T::AccountId; +// type Key2 = Symbol; +// type Value = u32; +// const PREFIX: &'static [u8] = b"FinancialRecords LastDepositIndexOf"; +//} +// +///// Last withdrawal index of a account and related symbol +//pub(crate) struct LastWithdrawalIndexOf(::rstd::marker::PhantomData); +// +//impl StorageDoubleMap for LastWithdrawalIndexOf { +// type Key1 = T::AccountId; +// type Key2 = Symbol; +// type Value = u32; +// const PREFIX: &'static [u8] = b"FinancialRecords LastWithdrawalIndexOf"; +//} impl Module { @@ -251,16 +259,20 @@ impl Module { >::deposit_event(::Event::from(event).into()); } // public call - fn set_deposit_fee(val: T::Balance) -> DispatchResult { + fn set_deposit_fee(val: T::Balance) -> Result { >::put(val); Self::deposit_event(RawEvent::SetDepositFee(val)); Ok(()) } - fn set_withdrawal_fee(val: T::Balance) -> DispatchResult { + fn set_withdrawal_fee(val: T::Balance) -> Result { >::put(val); Self::deposit_event(RawEvent::SetWithdrawalFee(val)); Ok(()) } + fn withdraw(origin: T::Origin, sym: Symbol, value: T::TokenBalance) -> Result { + let who = ensure_signed(origin)?; + Self::withdrawal(&who, &sym, value) + } } impl Module { @@ -269,7 +281,7 @@ impl Module { let mut records: Vec> = Vec::new(); let len: u32 = Self::records_len_of(who); for i in 0..len { - if let Some(r) = >::get(who.clone(), i) { + if let Some(r) = >::get(&(who.clone(), i)) { records.push(r); } } @@ -282,32 +294,29 @@ impl Module { None } else { let index = len - 1; - >::get(who.clone(), index).map(|r| (index, r)) + >::get(&(who.clone(), index)).map(|r| (index, r)) } } - pub fn last_deposit_index_of(who: &T::AccountId, sym: &Symbol) -> Option { - >::get(who.clone(), sym.clone()) - } - - pub fn last_withdrawal_index_of(who: &T::AccountId, sym: &Symbol) -> Option { - >::get(who.clone(), sym.clone()) - } pub fn last_deposit_of(who: &T::AccountId, sym: &Symbol) -> Option<(u32, RecordT)> { - Self::last_deposit_index_of(who, sym).and_then(|index| { - >::get(who.clone(), index).map(|r| (index, r)) + Self::last_deposit_index_of((who.clone(), sym.clone())).and_then(|index| { + >::get(&(who.clone(), index)).map(|r| (index, r)) }) } pub fn last_withdrawal_of(who: &T::AccountId, sym: &Symbol) -> Option<(u32, RecordT)> { - Self::last_withdrawal_index_of(who, sym).and_then(|index| { - >::get(who.clone(), index).map(|r| (index, r)) + Self::last_withdrawal_index_of((who.clone(), sym.clone())).and_then(|index| { + >::get(&(who.clone(), index)).map(|r| (index, r)) }) } /// deposit/withdrawal pre-process - fn before(who: &T::AccountId, sym: &Symbol, is_withdrawal: bool) -> Result<(), &'static str> { + fn before(who: &T::AccountId, sym: &Symbol, is_withdrawal: bool) -> Result { + if sym.as_slice() == T::CHAINX_SYMBOL { + return Err("can't deposit/withdrawal chainx token"); + } + let r = if is_withdrawal { match Self::last_deposit_of(who, sym) { None => return Err("the account has no deposit record for this token yet"), @@ -331,16 +340,17 @@ impl Module { } /// insert a new record for a account, notice the record state must be init state - fn new_record(who: &T::AccountId, record: &RecordT) -> Result { + fn new_record(who: &T::AccountId, record: &RecordT) -> StdResult { if !record.is_init() { return Err("new record should be Invalid state first"); } let len: u32 = Self::records_len_of(who); - >::insert(who.clone(), len, record.clone()); + >::insert(&(who.clone(), len), record.clone()); >::insert(who, len + 1); // len is more than 1 to max index + let key = (who.clone(), record.symbol()); match record.action() { - Action::Deposit(_) => >::insert(who.clone(), record.symbol(), len), - Action::Withdrawal(_) => >::insert(who.clone(), record.symbol(), len), + Action::Deposit(_) => >::insert(&key, len), + Action::Withdrawal(_) => >::insert(&key, len), } Ok(len) } @@ -348,20 +358,20 @@ impl Module { impl Module { /// deposit, notice this func has include deposit_init and deposit_finish (not wait for block confirm process) - pub fn deposit(who: &T::AccountId, sym: &Symbol, balance: T::TokenBalance) -> DispatchResult { + pub fn deposit(who: &T::AccountId, sym: &Symbol, balance: T::TokenBalance) -> Result { let index = Self::deposit_with_index(who, sym, balance)?; Self::deposit_finish_with_index(who, index, true).map(|_| ()) } /// withdrawal, notice this func has include withdrawal_init and withdrawal_locking - pub fn withdrawal(who: &T::AccountId, sym: &Symbol, balance: T::TokenBalance) -> DispatchResult { + fn withdrawal(who: &T::AccountId, sym: &Symbol, balance: T::TokenBalance) -> Result { let index = Self::withdrawal_with_index(who, sym, balance)?; Self::withdrawal_locking_with_index(who, index).map(|_| ()) } /// withdrawal finish, let the locking token destroy - pub fn withdrawal_finish(who: &T::AccountId, sym: &Symbol, success: bool) -> DispatchResult { - let r = Self::last_withdrawal_index_of(who, sym); + pub fn withdrawal_finish(who: &T::AccountId, sym: &Symbol, success: bool) -> Result { + let r = Self::last_withdrawal_index_of(&(who.clone(), sym.clone())); if r.is_none() { return Err("have not executed withdrawal() or withdrawal_init() yet for this record"); } @@ -369,33 +379,33 @@ impl Module { } /// deposit init, use for record a deposit process begin, usually for start block confirm process - pub fn deposit_init(who: &T::AccountId, sym: &Symbol, balance: T::TokenBalance) -> DispatchResult { + pub fn deposit_init(who: &T::AccountId, sym: &Symbol, balance: T::TokenBalance) -> Result { Self::deposit_with_index(who, sym, balance).map(|_| ()) } /// deposit finish, use for change the deposit record to final, success mark the deposit if success - pub fn deposit_finish(who: &T::AccountId, sym: &Symbol, success: bool) -> DispatchResult { - let r = Self::last_deposit_index_of(who, sym); + pub fn deposit_finish(who: &T::AccountId, sym: &Symbol, success: bool) -> Result { + let r = Self::last_deposit_index_of(&(who.clone(), sym.clone())); if r.is_none() { return Err("have not executed deposit_init() yet for this record"); } Self::deposit_finish_with_index(who, r.unwrap(), success).map(|_| ()) } - /// withdrawal init, use for record a withdrawal start, should call withdrawal_locking after it - pub fn withdrawal_init(who: &T::AccountId, sym: &Symbol, balance: T::TokenBalance) -> DispatchResult { - Self::withdrawal_with_index(who, sym, balance).map(|_| ()) - } - /// change the free token to locking state - pub fn withdrawal_locking(who: &T::AccountId, sym: &Symbol) -> DispatchResult { - let r = Self::last_withdrawal_index_of(who, sym); - if r.is_none() { - return Err("have not executed withdrawal() or withdrawal_init() yet for this record"); - } - Self::withdrawal_locking_with_index(who, r.unwrap()).map(|_| ()) - } +// /// withdrawal init, use for record a withdrawal start, should call withdrawal_locking after it +// fn withdrawal_init(who: &T::AccountId, sym: &Symbol, balance: T::TokenBalance) -> Result { +// Self::withdrawal_with_index(who, sym, balance).map(|_| ()) +// } +// /// change the free token to locking state +// fn withdrawal_locking(who: &T::AccountId, sym: &Symbol) -> Result { +// let r = Self::last_withdrawal_index_of(&(who.clone(), sym.clone())); +// if r.is_none() { +// return Err("have not executed withdrawal() or withdrawal_init() yet for this record"); +// } +// Self::withdrawal_locking_with_index(who, r.unwrap()).map(|_| ()) +// } /// deposit init, notice this func return index to show the index of records for this account - pub fn deposit_with_index(who: &T::AccountId, sym: &Symbol, balance: T::TokenBalance) -> Result { + pub fn deposit_with_index(who: &T::AccountId, sym: &Symbol, balance: T::TokenBalance) -> StdResult { Self::before(who, sym, false)?; >::is_valid_token(sym)?; @@ -411,8 +421,9 @@ impl Module { Ok(index) } /// deposit finish, should use index to find the old deposit record, success flag mark the success - pub fn deposit_finish_with_index(who: &T::AccountId, index: u32, success: bool) -> Result { - if let Some(ref mut r) = >::get(who.clone(), index) { + pub fn deposit_finish_with_index(who: &T::AccountId, index: u32, success: bool) -> StdResult { + let key = (who.clone(), index); + if let Some(ref mut r) = >::get(&key) { if r.is_finish() { return Err("the deposit record should not be a finish state"); } @@ -436,19 +447,19 @@ impl Module { } _ => return Err("err action type in deposit_finish"), } - >::insert(who.clone(), index, r.clone()); + >::insert(&key, r.clone()); Ok(index) } else { return Err("the deposit record for this (accountid, index) not exist"); } } /// withdrawal init, notice this func return index to show the index of records for this account - pub fn withdrawal_with_index(who: &T::AccountId, sym: &Symbol, balance: T::TokenBalance) -> Result { + pub fn withdrawal_with_index(who: &T::AccountId, sym: &Symbol, balance: T::TokenBalance) -> StdResult { Self::before(who, sym, true)?; >::is_valid_token_for(who, sym)?; // check token balance - if >::free_token_of(who, sym) < balance { + if >::free_token(&(who.clone(), sym.clone())) < balance { return Err("not enough free token to withdraw"); } @@ -463,8 +474,9 @@ impl Module { Ok(index) } /// withdrawal lock, should use index to find out which record to change to locking state - pub fn withdrawal_locking_with_index(who: &T::AccountId, index: u32) -> Result { - if let Some(ref mut r) = >::get(who.clone(), index) { + pub fn withdrawal_locking_with_index(who: &T::AccountId, index: u32) -> StdResult { + let key = (who.clone(), index); + if let Some(ref mut r) = >::get(&key) { if r.is_finish() { return Err("the deposit record should not be a finish state"); } @@ -478,7 +490,7 @@ impl Module { WithdrawalState::Invalid => { *state = WithdrawalState::Locking; - >::lock_destroy_token(who, &sym, bal)?; + >::reserve(who, &sym, bal)?; Self::deposit_event(RawEvent::WithdrawalLocking(who.clone(), index, sym, bal, >::block_number())); } @@ -487,7 +499,7 @@ impl Module { } _ => return Err("err action type in deposit_finish"), } - >::insert(who.clone(), index, r.clone()); + >::insert(&key, r.clone()); Ok(index) } else { @@ -495,8 +507,9 @@ impl Module { } } /// withdrawal finish, should use index to find out which record to changed to final, success flag mark success, if false, release the token to free - pub fn withdrawal_finish_with_index(who: &T::AccountId, index: u32, success: bool) -> Result { - if let Some(ref mut r) = >::get(who.clone(), index) { + pub fn withdrawal_finish_with_index(who: &T::AccountId, index: u32, success: bool) -> StdResult { + let key = (who.clone(), index); + if let Some(ref mut r) = >::get(&key) { if r.is_finish() { return Err("the deposit record should not be a finish state"); } @@ -518,7 +531,7 @@ impl Module { } else { *state = WithdrawalState::Failed; - >::unlock_destroy_token(who, &sym, bal)?; + >::unreserve(who, &sym, bal)?; Self::deposit_event(RawEvent::WithdrawalFailed(who.clone(), index, sym, bal, >::block_number())); } @@ -528,45 +541,11 @@ impl Module { } _ => return Err("err action type in deposit_finish") } - >::insert(who.clone(), index, r.clone()); + >::insert(&key, r.clone()); Ok(index) } else { return Err("the withdrawal record for this (accountid, index) not exist"); } } -} - - -//#[cfg(feature = "std")] -//#[derive(Serialize, Deserialize)] -//#[serde(rename_all = "camelCase")] -//#[serde(deny_unknown_fields)] -///// The genesis block configuration type. This is a simple default-capable struct that -///// contains any fields with which this module can be configured at genesis time. -//pub struct GenesisConfig { -// pub deposit_fee: T::Balance, -// pub withdrawal_fee: T::Balance, -//} -// -//#[cfg(feature = "std")] -//impl Default for GenesisConfig { -// fn default() -> Self { -// GenesisConfig { -// deposit_fee: Default::default(), -// withdrawal_fee: Default::default(), -// } -// } -//} -// -//#[cfg(feature = "std")] -//impl runtime_primitives::BuildStorage for GenesisConfig -//{ -// fn build_storage(self) -> ::std::result::Result { -// use codec::Encode; -// Ok(map![ -// Self::hash(>::key()).to_vec() => self.deposit_fee.encode(), -// Self::hash(>::key()).to_vec() => self.withdrawal_fee.encode() -// ]) -// } -//} +} \ No newline at end of file diff --git a/cxrml/financialrecords/src/tests.rs b/cxrml/financialrecords/src/tests.rs index 69c830b40d73c..08ced2a999bcc 100644 --- a/cxrml/financialrecords/src/tests.rs +++ b/cxrml/financialrecords/src/tests.rs @@ -9,7 +9,7 @@ use runtime_io; use runtime_io::with_externalities; use super::*; -use tokenbalances::Token; +use tokenbalances::{Token, SymbolString, DescString, Precision}; impl_outer_origin! { pub enum Origin for Test {} @@ -43,16 +43,15 @@ impl cxsupport::Trait for Test {} // define tokenbalances module type pub type TokenBalance = u128; -pub type Precision = u32; impl tokenbalances::Trait for Test { + const CHAINX_SYMBOL: SymbolString = b"pcx"; + const CHAINX_PRECISION: Precision = 8; + const CHAINX_TOKEN_DESC: DescString = b"this is pcx for mock"; type TokenBalance = TokenBalance; - type Precision = Precision; type Event = (); } -pub type TestPrecision = ::Precision; - // This function basically just builds a genesis storage key/value store according to // our desired mockup. pub fn new_test_ext() -> runtime_io::TestExternalities { @@ -68,8 +67,8 @@ pub fn new_test_ext() -> runtime_io::TestExternalities { reclaim_rebate: 0, }.build_storage().unwrap()); // token balance - let t: Token = Token::new(b"x-btc".to_vec(), b"btc token".to_vec(), 8); - let t2: Token = Token::new(b"x-eth".to_vec(), b"eth token".to_vec(), 4); + let t: Token = Token::new(b"x-btc".to_vec(), b"btc token".to_vec(), 8); + let t2: Token = Token::new(b"x-eth".to_vec(), b"eth token".to_vec(), 4); r.extend(tokenbalances::GenesisConfig:: { token_list: vec![ @@ -139,10 +138,13 @@ fn test_normal2() { assert_eq!(FinancialRecords::records_len_of(&a), 4); - assert_eq!(FinancialRecords::last_deposit_index_of(&a, &btc_symbol).unwrap(), 0); - assert_eq!(FinancialRecords::last_withdrawal_index_of(&a, &btc_symbol).unwrap(), 2); - assert_eq!(FinancialRecords::last_deposit_index_of(&a, ð_symbol).unwrap(), 1); - assert_eq!(FinancialRecords::last_withdrawal_index_of(&a, ð_symbol).unwrap(), 3); + let key1 = (a, btc_symbol.clone()); + let key2 = (a, eth_symbol.clone()); + + assert_eq!(FinancialRecords::last_deposit_index_of(&key1).unwrap(), 0); + assert_eq!(FinancialRecords::last_withdrawal_index_of(&key1).unwrap(), 2); + assert_eq!(FinancialRecords::last_deposit_index_of(&key2).unwrap(), 1); + assert_eq!(FinancialRecords::last_withdrawal_index_of(&key2).unwrap(), 3); assert_eq!(Balances::free_balance(&a), 960); }) @@ -171,15 +173,15 @@ fn test_last_not_finish() { assert_ok!(FinancialRecords::deposit(&a, &btc_symbol, 50)); // 3. deposit success assert_eq!(TokenBalances::total_token_of(&a, &btc_symbol), 150); - assert_eq!(TokenBalances::free_token_of(&a, &btc_symbol), 100); + assert_eq!(TokenBalances::free_token(&(a.clone(), btc_symbol.clone())), 100); assert_ok!(FinancialRecords::withdrawal_finish(&a, &btc_symbol, false)); // 4. withdrawal failed - assert_eq!(TokenBalances::free_token_of(&a, &btc_symbol), 150); + assert_eq!(TokenBalances::free_token(&(a.clone(), btc_symbol.clone())), 150); assert_ok!(FinancialRecords::withdrawal(&a, &btc_symbol, 25)); assert_ok!(FinancialRecords::withdrawal_finish(&a, &btc_symbol, true)); // destroy token here 5. withdrawal success assert_eq!(FinancialRecords::records_len_of(&a), 5); - assert_eq!(TokenBalances::free_token_of(&a, &btc_symbol), 125); + assert_eq!(TokenBalances::free_token(&(a.clone(), btc_symbol.clone())), 125); assert_eq!(TokenBalances::total_token(&btc_symbol), 225); @@ -228,7 +230,8 @@ fn test_multi_sym() { let a: u64 = 1; // accountid let btc_symbol = b"x-btc".to_vec(); let eth_symbol = b"x-eth".to_vec(); - + let key1 =(a, btc_symbol.clone()); + let key2 =(a, eth_symbol.clone()); assert_err!(FinancialRecords::withdrawal_finish(&a, &btc_symbol, true), "have not executed withdrawal() or withdrawal_init() yet for this record"); assert_err!(FinancialRecords::withdrawal(&a, &btc_symbol, 50), "the account has no deposit record for this token yet"); @@ -237,9 +240,9 @@ fn test_multi_sym() { assert_ok!(FinancialRecords::deposit(&a, ð_symbol, 100)); // eth 100 index = 1 assert_ok!(FinancialRecords::deposit(&a, &btc_symbol, 100)); // btc 200 index = 2 - assert_eq!(FinancialRecords::last_deposit_index_of(&a, &btc_symbol), Some(2)); - assert_eq!(FinancialRecords::last_deposit_index_of(&a, ð_symbol), Some(1)); - assert_eq!(FinancialRecords::last_withdrawal_index_of(&a, ð_symbol), None); + assert_eq!(FinancialRecords::last_deposit_index_of(&key1), Some(2)); + assert_eq!(FinancialRecords::last_deposit_index_of(&key2), Some(1)); + assert_eq!(FinancialRecords::last_withdrawal_index_of(&key2), None); assert_eq!(TokenBalances::total_token_of(&a, &btc_symbol), 200); assert_eq!(TokenBalances::total_token_of(&a, ð_symbol), 100); @@ -250,12 +253,12 @@ fn test_multi_sym() { assert_ok!(FinancialRecords::withdrawal(&a, ð_symbol, 50)); // parallel withdraw index = 4 - assert_eq!(TokenBalances::free_token_of(&a, &btc_symbol), 150); - assert_eq!(TokenBalances::free_token_of(&a, ð_symbol), 50); + assert_eq!(TokenBalances::free_token(&(a.clone(), btc_symbol.clone())), 150); + assert_eq!(TokenBalances::free_token(&(a.clone(), eth_symbol.clone())), 50); assert_ok!(FinancialRecords::deposit(&a, ð_symbol, 50)); // deposit while withdraw index = 5 - assert_eq!(TokenBalances::free_token_of(&a, ð_symbol), 100); + assert_eq!(TokenBalances::free_token(&(a.clone(), eth_symbol.clone())), 100); assert_ok!(FinancialRecords::withdrawal_finish(&a, &btc_symbol, true)); assert_ok!(FinancialRecords::withdrawal_finish(&a, ð_symbol, false)); @@ -263,12 +266,11 @@ fn test_multi_sym() { assert_eq!(TokenBalances::total_token_of(&a, &btc_symbol), 150); assert_eq!(TokenBalances::total_token_of(&a, ð_symbol), 150); - assert_eq!(FinancialRecords::last_deposit_index_of(&a, &btc_symbol), Some(2)); - assert_eq!(FinancialRecords::last_deposit_index_of(&a, ð_symbol), Some(5)); - - assert_eq!(FinancialRecords::last_withdrawal_index_of(&a, &btc_symbol), Some(3)); - assert_eq!(FinancialRecords::last_withdrawal_index_of(&a, ð_symbol), Some(4)); + assert_eq!(FinancialRecords::last_deposit_index_of(&key1), Some(2)); + assert_eq!(FinancialRecords::last_deposit_index_of(&key2), Some(5)); + assert_eq!(FinancialRecords::last_withdrawal_index_of(&key1), Some(3)); + assert_eq!(FinancialRecords::last_withdrawal_index_of(&key2), Some(4)); assert_eq!(FinancialRecords::records_len_of(&a), 6); }) diff --git a/cxrml/multisig/src/lib.rs b/cxrml/multisig/src/lib.rs index 328ecee605e4c..17cfb003c47b0 100644 --- a/cxrml/multisig/src/lib.rs +++ b/cxrml/multisig/src/lib.rs @@ -521,20 +521,6 @@ impl Module { } } -//#[cfg(feature = "std")] -//#[derive(Serialize, Deserialize)] -//#[serde(rename_all = "camelCase")] -//#[serde(deny_unknown_fields)] -///// The genesis block configuration type. This is a simple default-capable struct that -///// contains any fields with which this module can be configured at genesis time. -//pub struct GenesisConfig { -// pub genesis_multi_sig: Vec<(T::AccountId, Vec<(T::AccountId, bool)>, u32, T::Balance)>, -// pub deploy_fee: T::Balance, -// pub exec_fee: T::Balance, -// pub confirm_fee: T::Balance, -// pub balances_config: balances::GenesisConfig, -//} - #[cfg(feature = "std")] pub struct BalancesConfigCopy (balances::GenesisConfig); @@ -557,53 +543,3 @@ impl BalancesConfigCopy { self.0 } } -// -//#[cfg(feature = "std")] -//impl Default for GenesisConfig { -// fn default() -> Self { -// GenesisConfig { -// genesis_multi_sig: vec![], -// deploy_fee: Default::default(), -// exec_fee: Default::default(), -// confirm_fee: Default::default(), -// // balances config -// balances_config: Default::default(), -// } -// } -//} -// -// -//#[cfg(feature = "std")] -//impl runtime_primitives::BuildStorage for GenesisConfig -//{ -// fn build_storage(self) -> ::std::result::Result { -// use codec::Encode; -// use runtime_io::with_externalities; -// use substrate_primitives::{Blake2Hasher, RlpCodec}; -// -// let mut r: runtime_primitives::StorageMap = map![ -// Self::hash(>::key()).to_vec() => self.deploy_fee.encode(), -// Self::hash(>::key()).to_vec() => self.exec_fee.encode(), -// Self::hash(>::key()).to_vec() => self.confirm_fee.encode() -// ]; -// -// let mut src_r = self.balances_config.build_storage().unwrap(); -// src_r.extend(r.clone()); // add multisig fee -// let mut tmp_storage: runtime_io::TestExternalities = src_r.into(); -// let genesis = self.genesis_multi_sig.clone(); -// -// with_externalities(&mut tmp_storage, || { -// for (deployer, owners, required_num, value) in genesis { -// if let Err(e) = >::deploy_for(&deployer, owners, required_num, value) { -// panic!(e) -// } -// // >::inc_account_nonce(&deployer); -// } -// }); -// -// let map: runtime_primitives::StorageMap = tmp_storage.into(); -// r.extend(map); -// -// Ok(r) -// } -//} \ No newline at end of file diff --git a/cxrml/tokenbalances/src/lib.rs b/cxrml/tokenbalances/src/lib.rs index 8022b36ca1ca7..5867c8b3675a4 100644 --- a/cxrml/tokenbalances/src/lib.rs +++ b/cxrml/tokenbalances/src/lib.rs @@ -43,24 +43,37 @@ use runtime_support::{StorageValue, StorageMap, Parameter}; use runtime_support::dispatch::Result; use primitives::traits::{SimpleArithmetic, As, Member, CheckedAdd, CheckedSub, OnFinalise}; -use cxsupport::StorageDoubleMap; +//use cxsupport::StorageDoubleMap; // substrate mod use system::ensure_signed; use balances::address::Address; use balances::EnsureAccountLiquid; + +//#[cfg(feature = "std")] +//pub type SymbolString = ::std::borrow::Cow<'static, [u8]>; +//#[cfg(not(feature = "std"))] +pub type SymbolString = &'static [u8]; + +//#[cfg(feature = "std")] +//pub type TokenString = SymbolString; +//#[cfg(not(feature = "std"))] +pub type DescString = SymbolString; + pub trait Trait: balances::Trait + cxsupport::Trait { + const CHAINX_SYMBOL: SymbolString; + const CHAINX_PRECISION: Precision; + const CHAINX_TOKEN_DESC: DescString; /// The token balance. - type TokenBalance: Parameter + Member + Codec + SimpleArithmetic + As + As + As + As + As + As + Copy + Default; - /// The token precision, for example, btc, 1BTC=1000mBTC=1000000Bits, and decide a precision for btc - type Precision: Parameter + Member + Codec + As + As + As + As + Copy + Default; + type TokenBalance: Parameter + Member + Codec + SimpleArithmetic + As + As + Copy + Default; /// Event type Event: From> + Into<::Event>; } pub type Symbol = Vec; pub type TokenDesc = Vec; +pub type Precision = u16; const MAX_SYMBOL_LEN: usize = 32; const MAX_TOKENDESC_LEN: usize = 128; @@ -102,9 +115,7 @@ pub fn is_valid_token_desc(v: &[u8]) -> Result { /// Token struct. #[derive(PartialEq, Eq, Clone, Encode, Decode, Default)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] -pub struct Token where - Precision: As + As + As + As + Copy, -{ +pub struct Token { /// Validator should ensure this many more slashes than is necessary before being unstaked. symbol: Symbol, /// token description @@ -113,10 +124,8 @@ pub struct Token where precision: Precision, } -impl Token where - Precision: As + As + As + As + Copy, -{ - pub fn new(symbol: Symbol, token_desc: TokenDesc, precision: Precision) -> Token { +impl Token { + pub fn new(symbol: Symbol, token_desc: TokenDesc, precision: Precision) -> Self { Token { symbol, token_desc, precision } } @@ -146,7 +155,7 @@ impl Token where decl_module! { pub struct Module for enum Call where origin: T::Origin { /// register_token to module, should allow by root - fn register_token(token: Token, free: T::TokenBalance, locked: T::TokenBalance) -> Result; + fn register_token(token: Token, free: T::TokenBalance, reversed: T::TokenBalance) -> Result; /// transfer between account fn transfer_token(origin, dest: Address, sym: Symbol, value: T::TokenBalance) -> Result; // set transfer token fee @@ -158,7 +167,6 @@ decl_event!( pub enum Event where ::AccountId, ::TokenBalance, - ::Precision, ::Balance { /// register new token (token.symbol(), token.token_desc, token.precision) @@ -167,10 +175,11 @@ decl_event!( CancelToken(Symbol), /// issue succeeded (who, symbol, balance) IssueToken(AccountId, Symbol, TokenBalance), + // TODO /// lock destroy (who, symbol, balance) - LockToken(AccountId, Symbol, TokenBalance), + ReverseToken(AccountId, Symbol, TokenBalance), /// unlock destroy (who, symbol, balance) - UnlockToken(AccountId, Symbol, TokenBalance), + UnreverseToken(AccountId, Symbol, TokenBalance), /// destroy DestroyToken(AccountId, Symbol, TokenBalance), /// Transfer succeeded (from, to, symbol, value, fees). @@ -183,63 +192,59 @@ decl_event!( decl_storage! { trait Store for Module as TokenBalances { /// supported token list - pub TokenListMap get(token_list_map): map u32 => (bool, Symbol); + pub TokenListMap get(token_list_map): map u32 => Symbol; /// supported token list length pub TokenListLen get(token_list_len): u32; /// token info for every token, key is token symbol - pub TokenInfo get(token_info): map Symbol => Token; + pub TokenInfo get(token_info): map Symbol => Option<(Token, bool)>; + /// total free token of a symbol pub TotalFreeToken get(total_free_token): map Symbol => T::TokenBalance; + + pub FreeToken: map (T::AccountId, Symbol) => T::TokenBalance; + /// total locked token of a symbol - pub TotalLockedToken get(total_locked_token): map Symbol => T::TokenBalance; + pub TotalReservedToken get(total_reserved_token): map Symbol => T::TokenBalance; + + pub ReservedToken get(reserved_token): map (T::AccountId, Symbol) => T::TokenBalance; /// token list of a account - pub TokenListOf get(token_list_of): map T::AccountId => Vec; + pub TokenListOf get(token_list_of): map T::AccountId => Vec = [T::CHAINX_SYMBOL.to_vec()].to_vec(); /// transfer token fee pub TransferTokenFee get(transfer_token_fee) config(): T::Balance; } add_extra_genesis { - config(token_list): Vec<(Token, T::TokenBalance, T::TokenBalance)>; + config(token_list): Vec<(Token, T::TokenBalance, T::TokenBalance)>; build( |storage: &mut primitives::StorageMap, config: &GenesisConfig| { use codec::Encode; + let mut list_count = 0_u32; + // insert chainx token symbol + let chainx: Symbol = T::CHAINX_SYMBOL.to_vec(); + storage.insert(GenesisConfig::::hash(&>::key_for(&list_count)).to_vec(), chainx.clone().encode()); + // token info + let t: Token = Token::new(chainx.clone(), T::CHAINX_TOKEN_DESC.to_vec(), T::CHAINX_PRECISION); + storage.insert(GenesisConfig::::hash(&>::key_for(&chainx)).to_vec(), (t, true).encode()); + list_count += 1; + // 0 token list length - storage.insert(GenesisConfig::::hash(>::key()).to_vec(), config.token_list.len().encode()); - for (index, (token, free_token, locked_token)) in config.token_list.iter().enumerate() { + storage.insert(GenesisConfig::::hash(&>::key()).to_vec(), (config.token_list.len() as u32 + list_count).encode()); + for (index, (token, free_token, reserved_token)) in config.token_list.iter().enumerate() { // token.is_valid().map_err(|e| e.to_string())?; // 1 token balance storage.insert(GenesisConfig::::hash(&>::key_for(token.symbol())).to_vec(), free_token.encode()); - storage.insert(GenesisConfig::::hash(&>::key_for(token.symbol())).to_vec(), locked_token.encode()); - // 2 token info - storage.insert(GenesisConfig::::hash(&>::key_for(token.symbol())).to_vec(), token.encode()); - // 3 token list map - storage.insert(GenesisConfig::::hash(&>::key_for(index as u32)).to_vec(), (true, token.symbol()).encode()); + storage.insert(GenesisConfig::::hash(&>::key_for(token.symbol())).to_vec(), reserved_token.encode()); + // 2 token list map + storage.insert(GenesisConfig::::hash(&>::key_for(index as u32 + list_count)).to_vec(), token.symbol().encode()); + // 3 token info + storage.insert(GenesisConfig::::hash(&>::key_for(token.symbol())).to_vec(), (token, true).encode()); } } ); } } -// account token storage -pub(crate) struct FreeTokenOf(::rstd::marker::PhantomData); - -pub(crate) struct LockedTokenOf(::rstd::marker::PhantomData); - -impl StorageDoubleMap for FreeTokenOf { - type Key1 = T::AccountId; - type Key2 = Symbol; - type Value = T::TokenBalance; - const PREFIX: &'static [u8] = b"TokenBalances FreeTokenOf"; -} - -impl StorageDoubleMap for LockedTokenOf { - type Key1 = T::AccountId; - type Key2 = Symbol; - type Value = T::TokenBalance; - const PREFIX: &'static [u8] = b"TokenBalances LockedTokenOf"; -} - // This trait expresses what should happen when the block is finalised. impl OnFinalise for Module { fn on_finalise(_: T::BlockNumber) { @@ -256,22 +261,26 @@ impl Module { impl Module { // token storage - pub fn free_token_of(who: &T::AccountId, symbol: &Symbol) -> T::TokenBalance { - >::get_or_default(who.clone(), symbol.clone()) - } - - pub fn locked_token_of(who: &T::AccountId, symbol: &Symbol) -> T::TokenBalance { - >::get_or_default(who.clone(), symbol.clone()) + pub fn free_token(who_sym: &(T::AccountId, Symbol)) -> T::TokenBalance { + if who_sym.1.as_slice() == T::CHAINX_SYMBOL { + As::sa(balances::FreeBalance::::get(&who_sym.0).as_()) + } else { + >::get(who_sym) + } } /// The combined token balance of `who` for symbol. pub fn total_token_of(who: &T::AccountId, symbol: &Symbol) -> T::TokenBalance { - Self::free_token_of(who, symbol) + Self::locked_token_of(who, symbol) + Self::free_token(&(who.clone(), symbol.clone())) + Self::reserved_token((who.clone(), symbol.clone())) } /// tatal_token of a token symbol pub fn total_token(symbol: &Symbol) -> T::TokenBalance { - Self::total_free_token(symbol) + Self::total_locked_token(symbol) + if symbol.as_slice() == T::CHAINX_SYMBOL { + As::sa(balances::TotalIssuance::::get().as_()) + } else { + Self::total_free_token(symbol) + Self::total_reserved_token(symbol) + } } } @@ -279,10 +288,11 @@ impl Module { // token symol // public call /// register a token into token list ans init - pub fn register_token(token: Token, free: T::TokenBalance, locked: T::TokenBalance) -> Result { + pub fn register_token(token: Token, free: T::TokenBalance, reserved: T::TokenBalance) -> Result { token.is_valid()?; - Self::add_token(&token.symbol(), free, locked)?; - >::insert(token.symbol(), token.clone()); + let sym = token.symbol(); + Self::add_token(&sym, free, reserved)?; + >::insert(&sym, (token.clone(), true)); Self::deposit_event(RawEvent::RegisterToken(token.symbol(), token.token_desc(), token.precision())); Ok(()) @@ -296,38 +306,52 @@ impl Module { Ok(()) } - /// retuan all token list with valid flag - pub fn all_token_list() -> Vec<(bool, Symbol)> { + pub fn token_list() -> Vec { let len: u32 = >::get(); - let mut v: Vec<(bool, Symbol)> = Vec::new(); + let mut v: Vec = Vec::new(); for i in 0..len { - let (flag, symbol) = >::get(i); - if symbol != b"".to_vec() { - v.push((flag, symbol)); - } + let symbol = >::get(i); + v.push(symbol); } v } - /// return valid token list, only valid token - pub fn token_list() -> Vec { - Self::all_token_list().into_iter() - .filter(|(flag, _)| *flag == true) - .map(|(_, sym)| sym) + pub fn valid_token_list() -> Vec { + Self::token_list().into_iter() + .filter(|s| { + if let Some(t) = TokenInfo::::get(s) { + t.1 + } else { false } + }) .collect() } +// /// return valid token list, only valid token +// pub fn token_list() -> Vec { +// Self::all_token_list().into_iter() +// .filter(|(flag, _)| *flag == true) +// .map(|(_, sym)| sym) +// .collect() +// } + pub fn is_valid_token(symbol: &Symbol) -> Result { is_valid_symbol(symbol)?; - if Self::token_list().contains(symbol) { - Ok(()) - } else { - Err("not in the valid token list") + if let Some(info) = TokenInfo::::get(symbol) { + if info.1 == true { + return Ok(()); + } + return Err("not a valid token"); } + Err("not a registered token") +// if Self::token_list().contains(symbol) { +// Ok(()) +// } else { +// Err("not in the valid token list") +// } } pub fn is_valid_token_for(who: &T::AccountId, symbol: &Symbol) -> Result { - is_valid_symbol(symbol)?; + Self::is_valid_token(symbol)?; if Self::token_list_of(who).contains(symbol) { Ok(()) } else { @@ -335,53 +359,47 @@ impl Module { } } - fn add_token(symbol: &Symbol, free: T::TokenBalance, locked: T::TokenBalance) -> Result { - is_valid_symbol(symbol)?; - let list = Self::all_token_list(); - if !list.iter().find(|(_, sym)| *sym == *symbol).is_none() { + fn add_token(symbol: &Symbol, free: T::TokenBalance, reserved: T::TokenBalance) -> Result { + +// let list = Self::all_token_list(); +// if !list.iter().find(|(_, sym)| *sym == *symbol).is_none() { +// return Err("already has this token symbol"); +// } + if TokenInfo::::exists(symbol) { return Err("already has this token symbol"); } let len: u32 = >::get(); // mark new symbol valid - >::insert(len, (true, symbol.clone())); + >::insert(len, symbol.clone()); >::put(len + 1); - Self::init_token_balance(symbol, free, locked); + Self::init_token_balance(symbol, free, reserved); Ok(()) } fn remove_token(symbol: &Symbol) -> Result { is_valid_symbol(symbol)?; - let list = Self::token_list(); - - let index = if let Some(i) = list.iter().position(|sym| *sym == *symbol) { - i +// let list = Self::token_list(); + if let Some(mut info) = TokenInfo::::get(symbol) { + info.1 = false; + TokenInfo::::insert(symbol.clone(), info); + Ok(()) } else { - return Err("this token symbol dose not register yet or is invalid"); - }; - - >::mutate(index as u32, |value| { - let (ref mut flag, _) = *value; - *flag = false; - }); - - // do not remove token balance from storage - // Self::remove_token_balance(); - - Ok(()) + Err("this token symbol dose not register yet or is invalid") + } } - fn init_token_balance(symbol: &Symbol, free: T::TokenBalance, locked: T::TokenBalance) { + fn init_token_balance(symbol: &Symbol, free: T::TokenBalance, reserved: T::TokenBalance) { >::insert(symbol, free); - >::insert(symbol, locked); + >::insert(symbol, reserved); } - #[allow(dead_code)] + #[allow(unused)] fn remove_token_balance(symbol: &Symbol) { >::remove(symbol); - >::remove(symbol); + >::remove(symbol); } } @@ -393,203 +411,198 @@ impl Module { } /// issue from real coin to chainx token, notice it become free token directly - pub fn issue(who: &T::AccountId, symbol: &Symbol, balance: T::TokenBalance) -> Result { - is_valid_symbol(symbol)?; + pub fn issue(who: &T::AccountId, symbol: &Symbol, value: T::TokenBalance) -> Result { + if symbol.as_slice() == T::CHAINX_SYMBOL { + return Err("can't issue chainx token"); + } + Self::is_valid_token(symbol)?; ::EnsureAccountLiquid::ensure_account_liquid(who)?; - // increase for all, overflow would exist at this point - Self::increase_total_free_token_by(symbol, balance)?; - - // init for account + // get storage + let key = (who.clone(), symbol.clone()); + let total_free_token = TotalFreeToken::::get(symbol); + let free_token = FreeToken::::get(&key); + // check + let new_free_token = match free_token.checked_add(&value) { + Some(b) => b, + None => return Err("free token too high to issue"), + }; + let new_total_free_token = match total_free_token.checked_add(&value) { + Some(b) => b, + None => return Err("total free token too high to issue"), + }; + // set to storage Self::init_token_for(who, symbol); - // increase for this account - Self::increase_account_free_token_by(who, symbol, balance)?; + TotalFreeToken::::insert(symbol, new_total_free_token); + FreeToken::::insert(&key, new_free_token); - Self::deposit_event(RawEvent::IssueToken(who.clone(), symbol.clone(), balance)); + Self::deposit_event(RawEvent::IssueToken(who.clone(), symbol.clone(), value)); Ok(()) } - /// destroy token must be lock first, and become locked state - pub fn lock_destroy_token(who: &T::AccountId, symbol: &Symbol, balance: T::TokenBalance) -> Result { - is_valid_symbol(symbol)?; - Self::is_valid_token(symbol)?; + pub fn destroy(who: &T::AccountId, symbol: &Symbol, value: T::TokenBalance) -> Result { + if symbol.as_slice() == T::CHAINX_SYMBOL { + return Err("can't destroy chainx token"); + } Self::is_valid_token_for(who, symbol)?; - ::EnsureAccountLiquid::ensure_account_liquid(who)?; + //TODO validator + + // get storage + let key = (who.clone(), symbol.clone()); + let total_reserved_token = TotalReservedToken::::get(symbol); + let reserved_token = ReservedToken::::get(&key); + // check + let new_reserved_token = match reserved_token.checked_sub(&value) { + Some(b) => b, + None => return Err("reserved token too low to destroy"), + }; + let new_total_reserved_token = match total_reserved_token.checked_sub(&value) { + Some(b) => b, + None => return Err("total reserved token too low to destroy"), + }; + // set to storage + TotalReservedToken::::insert(symbol, new_total_reserved_token); + ReservedToken::::insert(&key, new_reserved_token); - // for all token - if Self::total_free_token(symbol) < balance { - return Err("not enough free token to lock"); - } - // for account - if Self::free_token_of(who, symbol) < balance { - return Err("not enough free token to lock for this account"); - } - // modify store - // for all token - // would exist if overflow - Self::decrease_total_free_token_by(symbol, balance)?; - Self::increase_total_locked_token_by(symbol, balance)?; - // for account - Self::decrease_account_free_token_by(who, symbol, balance)?; - Self::increase_account_locked_token_by(who, symbol, balance)?; - - Self::deposit_event(RawEvent::LockToken(who.clone(), symbol.clone(), balance)); + Self::deposit_event(RawEvent::DestroyToken(who.clone(), symbol.clone(), value)); Ok(()) } - /// unlock locked token if destroy failed - pub fn unlock_destroy_token(who: &T::AccountId, symbol: &Symbol, balance: T::TokenBalance) -> Result { - is_valid_symbol(symbol)?; - Self::is_valid_token(symbol)?; + pub fn reserve(who: &T::AccountId, symbol: &Symbol, value: T::TokenBalance) -> Result { Self::is_valid_token_for(who, symbol)?; - ::EnsureAccountLiquid::ensure_account_liquid(who)?; - - // for all token - if Self::total_locked_token(symbol) < balance { - return Err("not enough locked token to unlock"); - } - // for account - if Self::locked_token_of(who, symbol) < balance { - return Err("not enough locked token to lock for this account"); + //TODO validator + + let key = (who.clone(), symbol.clone()); + // for chainx + if symbol.as_slice() == T::CHAINX_SYMBOL { + let value: T::Balance = As::sa(value.as_() as u64); // change to balance for balances module + let free_token: T::Balance = balances::FreeBalance::::get(who); + let reserved_token = ReservedToken::::get(&key); + let total_reserved_token = TotalReservedToken::::get(symbol); + match free_token.checked_sub(&value) { + Some(b) => b, + None => return Err("chainx free token too low to reserve"), + }; + let val: T::TokenBalance = As::sa(value.as_() as u128); // tokenbalance is large than balance + let new_reserved_token = match reserved_token.checked_add(&val) { + Some(b) => b, + None => return Err("chainx reserved token too high to reserve"), + }; + let new_total_reserved_token = match total_reserved_token.checked_add(&val) { + Some(b) => b, + None => return Err("chainx total reserved token too high to reserve"), + }; + // would subtract freebalance and add to reversed balance + balances::Module::::reserve(who, value)?; + ReservedToken::::insert(key, new_reserved_token); + TotalReservedToken::::insert(symbol, new_total_reserved_token); + + Self::deposit_event(RawEvent::ReverseToken(who.clone(), T::CHAINX_SYMBOL.to_vec(), val)); + return Ok(()); } - // modify store - // for all token - // would exist if overflow - Self::decrease_total_locked_token_by(symbol, balance)?; - Self::increase_total_free_token_by(symbol, balance)?; - // for account - Self::decrease_account_locked_token_by(who, symbol, balance)?; - Self::increase_account_free_token_by(who, symbol, balance)?; - - Self::deposit_event(RawEvent::UnlockToken(who.clone(), symbol.clone(), balance)); - Ok(()) - } - - /// real destroy token, only decrease in account locked token - pub fn destroy(who: &T::AccountId, symbol: &Symbol, balance: T::TokenBalance) -> Result { - is_valid_symbol(symbol)?; - Self::is_valid_token(symbol)?; - Self::is_valid_token_for(who, symbol)?; - ::EnsureAccountLiquid::ensure_account_liquid(who)?; + // for other token + // get from storage + let total_free_token = TotalFreeToken::::get(symbol); + let total_reserved_token = TotalReservedToken::::get(symbol); + let free_token = FreeToken::::get(&key); + let reserved_token = ReservedToken::::get(&key); + // test overflow + let new_free_token = match free_token.checked_sub(&value) { + Some(b) => b, + None => return Err("free token too low to reserve"), + }; + let new_reserved_token = match reserved_token.checked_add(&value) { + Some(b) => b, + None => return Err("reserved token too high to reserve"), + }; + let new_total_free_token = match total_free_token.checked_sub(&value) { + Some(b) => b, + None => return Err("total free token too low to reserve"), + }; + let new_total_reserved_token = match total_reserved_token.checked_add(&value) { + Some(b) => b, + None => return Err("total reserved token too high to reserve"), + }; + // set to storage + TotalFreeToken::::insert(symbol, new_total_free_token); + TotalReservedToken::::insert(symbol, new_total_reserved_token); + FreeToken::::insert(&key, new_free_token); + ReservedToken::::insert(&key, new_reserved_token); - // for all token - if Self::total_locked_token(symbol) < balance { - return Err("not enough locked token to destroy"); - } - // for account - if Self::locked_token_of(who, symbol) < balance { - return Err("not enough locked token to destroy for this account"); - } - // destroy token - // for all token - // would exist if overflow - Self::decrease_total_locked_token_by(symbol, balance)?; - // for account - Self::decrease_account_locked_token_by(who, symbol, balance)?; - - Self::deposit_event(RawEvent::DestroyToken(who.clone(), symbol.clone(), balance)); + Self::deposit_event(RawEvent::ReverseToken(who.clone(), symbol.clone(), value)); Ok(()) } - // token calc - /// Increase TotalFreeToken by Value. - fn increase_total_free_token_by(symbol: &Symbol, value: T::TokenBalance) -> Result { - is_valid_symbol(symbol)?; - if let Some(v) = Self::total_free_token(symbol).checked_add(&value) { - >::mutate(symbol, |b: &mut T::TokenBalance| { - *b = v; - }); - Ok(()) - } else { - Err("Overflow in increase_total_free_token_by") - } - } - /// Decrease TotalFreeToken by Value. - fn decrease_total_free_token_by(symbol: &Symbol, value: T::TokenBalance) -> Result { - is_valid_symbol(symbol)?; - if let Some(v) = Self::total_free_token(symbol).checked_sub(&value) { - >::mutate(symbol, |b: &mut T::TokenBalance| { - *b = v; - }); - Ok(()) - } else { - Err("Overflow in decrease_total_free_token_by") + pub fn unreserve(who: &T::AccountId, symbol: &Symbol, value: T::TokenBalance) -> Result { + Self::is_valid_token_for(who, symbol)?; + ::EnsureAccountLiquid::ensure_account_liquid(who)?; + //TODO validator + + let key = (who.clone(), symbol.clone()); + // for chainx + if symbol.as_slice() == T::CHAINX_SYMBOL { + let value: T::Balance = As::sa(value.as_() as u64); // change to balance for balances module + let free_token: T::Balance = balances::FreeBalance::::get(who); + let reserved_token = ReservedToken::::get(&key); + let total_reserved_token = TotalReservedToken::::get(symbol); + match free_token.checked_add(&value) { + Some(b) => b, + None => return Err("chainx free token too high to unreserve"), + }; + let val: T::TokenBalance = As::sa(value.as_() as u128); // tokenbalance is large than balance + let new_reserved_token = match reserved_token.checked_sub(&val) { + Some(b) => b, + None => return Err("chainx reserved token too low to unreserve"), + }; + let new_total_reserved_token = match total_reserved_token.checked_sub(&val) { + Some(b) => b, + None => return Err("chainx total reserved token too low to unreserve"), + }; + // would subtract reservedbalance and add to free balance + balances::Module::::unreserve(who, value); + ReservedToken::::insert(key, new_reserved_token); + TotalReservedToken::::insert(symbol, new_total_reserved_token); + + Self::deposit_event(RawEvent::UnreverseToken(who.clone(), T::CHAINX_SYMBOL.to_vec(), val)); + return Ok(()); } - } - /// Increase TotalLockedToken by Value. - fn increase_total_locked_token_by(symbol: &Symbol, value: T::TokenBalance) -> Result { - is_valid_symbol(symbol)?; - if let Some(v) = Self::total_locked_token(symbol).checked_add(&value) { - >::mutate(symbol, |b: &mut T::TokenBalance| { - *b = v; - }); - Ok(()) - } else { - Err("Overflow in increase_total_locked_token_by") - } - } - /// Decrease TotalLockedToken by Value. - fn decrease_total_locked_token_by(symbol: &Symbol, value: T::TokenBalance) -> Result { - is_valid_symbol(symbol)?; - if let Some(v) = Self::total_locked_token(symbol).checked_sub(&value) { - >::mutate(symbol, |b: &mut T::TokenBalance| { - *b = v; - }); - Ok(()) - } else { - Err("Overflow in decrease_total_locked_token_by") - } - } + // for other token + // get from storage + let total_free_token = TotalFreeToken::::get(symbol); + let total_reserved_token = TotalReservedToken::::get(symbol); + let free_token = FreeToken::::get(&key); + let reserved_token = ReservedToken::::get(&key); + // test overflow + let new_free_token = match free_token.checked_add(&value) { + Some(b) => b, + None => return Err("free token too high to unreserve"), + }; + let new_reserved_token = match reserved_token.checked_sub(&value) { + Some(b) => b, + None => return Err("reserved token too low to unreserve"), + }; + let new_total_free_token = match total_free_token.checked_add(&value) { + Some(b) => b, + None => return Err("total free token too high to unreserve"), + }; + let new_total_reserved_token = match total_reserved_token.checked_sub(&value) { + Some(b) => b, + None => return Err("total reserved token too low to unreserve"), + }; + // set to storage + TotalFreeToken::::insert(symbol, new_total_free_token); + TotalReservedToken::::insert(symbol, new_total_reserved_token); + FreeToken::::insert(&key, new_free_token); + ReservedToken::::insert(&key, new_reserved_token); - /// Increase FreeToken balance to account for a symbol by Value. - fn increase_account_free_token_by(who: &T::AccountId, symbol: &Symbol, value: T::TokenBalance) -> Result { - is_valid_symbol(symbol)?; - let b: T::TokenBalance = Self::free_token_of(who, symbol); - if let Some(v) = b.checked_add(&value) { - >::insert(who.clone(), symbol.clone(), v); - Ok(()) - } else { - Err("Overflow in increase_account_free_token_by for account") - } - } - /// Decrease FreeToken balance to account for a symbol by Value. - fn decrease_account_free_token_by(who: &T::AccountId, symbol: &Symbol, value: T::TokenBalance) -> Result { - is_valid_symbol(symbol)?; - let b: T::TokenBalance = Self::free_token_of(who, symbol); - if let Some(v) = b.checked_sub(&value) { - >::insert(who.clone(), symbol.clone(), v); - Ok(()) - } else { - Err("Overflow in decrease_account_free_token_by for account") - } - } - /// Increase LockedToken balance to account for a symbol by Value. - fn increase_account_locked_token_by(who: &T::AccountId, symbol: &Symbol, value: T::TokenBalance) -> Result { - is_valid_symbol(symbol)?; - let b: T::TokenBalance = Self::locked_token_of(who, symbol); - if let Some(v) = b.checked_add(&value) { - >::insert(who.clone(), symbol.clone(), v); - Ok(()) - } else { - Err("Overflow in increase_account_locked_token_by for account") - } - } - /// Decrease LockedToken balance to account for a symbol by Value. - fn decrease_account_locked_token_by(who: &T::AccountId, symbol: &Symbol, value: T::TokenBalance) -> Result { - is_valid_symbol(symbol)?; - let b: T::TokenBalance = Self::locked_token_of(who, symbol); - if let Some(v) = b.checked_sub(&value) { - >::insert(who.clone(), symbol.clone(), v); - Ok(()) - } else { - Err("Overflow in decrease_account_locked_token_by for account") - } + Self::deposit_event(RawEvent::UnreverseToken(who.clone(), symbol.clone(), value)); + Ok(()) } } @@ -597,25 +610,38 @@ impl Module { // public call /// transfer token between accountid, notice the fee is chainx pub fn transfer_token(origin: T::Origin, dest: balances::Address, sym: Symbol, value: T::TokenBalance) -> Result { - is_valid_symbol(&sym)?; + if sym.as_slice() == T::CHAINX_SYMBOL { + return Err("not allow to transfer chainx use transfer_token"); + } let transactor = ensure_signed(origin)?; - let dest = >::lookup(dest)?; - - Self::is_valid_token(&sym)?; Self::is_valid_token_for(&transactor, &sym)?; - // Self::is_valid_token_for(&dest, &sym)?; + let dest = >::lookup(dest)?; Self::init_token_for(&dest, &sym); - +// let fee = Self::transfer_token_fee(); + + let key_from = (transactor.clone(), sym.clone()); + let key_to = (dest.clone(), sym.clone()); + let sender = &transactor; let receiver = &dest; >::handle_fee_after(sender, fee, true, || { - if Self::free_token_of(&sender, &sym) < value { - return Err("transactor's free token balance too low, can't transfer this token"); - } + // get storage + let from_token = FreeToken::::get(&key_from); + let to_token = FreeToken::::get(&key_to); + // check + let new_from_token = match from_token.checked_sub(&value) { + Some(b) => b, + None => return Err("free token too low to send value"), + }; + let new_to_token = match to_token.checked_add(&value) { + Some(b) => b, + None => return Err("destination free token too high to receive value"), + }; if sender != receiver { - Self::decrease_account_free_token_by(sender, &sym, value)?; - Self::increase_account_free_token_by(receiver, &sym, value)?; + // set to storage + FreeToken::::insert(&key_from, new_from_token); + FreeToken::::insert(&key_to, new_to_token); Self::deposit_event(RawEvent::TransferToken(sender.clone(), receiver.clone(), sym.clone(), value, fee)); } Ok(()) diff --git a/cxrml/tokenbalances/src/mock.rs b/cxrml/tokenbalances/src/mock.rs index b9a3efde2d225..d28dfdd6091f7 100644 --- a/cxrml/tokenbalances/src/mock.rs +++ b/cxrml/tokenbalances/src/mock.rs @@ -7,7 +7,7 @@ use primitives::traits::BlakeTwo256; use primitives::testing::{Digest, DigestItem, Header}; use runtime_io; -use {GenesisConfig, Module, Trait, system, balances, cxsupport, Token}; +use super::*; impl_outer_origin! { pub enum Origin for Test {} @@ -41,16 +41,15 @@ impl cxsupport::Trait for Test {} // define tokenbalances module type pub type TokenBalance = u128; -pub type Precision = u32; impl Trait for Test { + const CHAINX_SYMBOL: SymbolString = b"pcx"; + const CHAINX_PRECISION: Precision = 8; + const CHAINX_TOKEN_DESC: DescString = b"this is pcx for mock"; type TokenBalance = TokenBalance; - type Precision = Precision; type Event = (); } -pub type TestPrecision = ::Precision; - pub type TokenBalances = Module; pub type Balances = balances::Module; @@ -69,9 +68,34 @@ pub fn new_test_ext() -> runtime_io::TestExternalities { reclaim_rebate: 0, }.build_storage().unwrap()); // token - let t: Token = Token::new(b"x-btc".to_vec(), b"btc token".to_vec(), 8); - let t2: Token = Token::new(b"x-eth".to_vec(), b"eth token".to_vec(), 4); + let t: Token = Token::new(b"x-btc".to_vec(), b"btc token".to_vec(), 8); + let t2: Token = Token::new(b"x-eth".to_vec(), b"eth token".to_vec(), 4); + + r.extend(GenesisConfig:: { + token_list: vec![ + (t, 100, 0), + (t2, 100, 0), + ], + transfer_token_fee: 10, + }.build_storage().unwrap()); + r.into() +} +pub fn new_test_ext2() -> runtime_io::TestExternalities { + let mut r = system::GenesisConfig::::default().build_storage().unwrap(); + // balance + r.extend(balances::GenesisConfig:: { + balances: vec![(1, 1000), (2, 510)], + transaction_base_fee: 0, + transaction_byte_fee: 0, + existential_deposit: 0, + transfer_fee: 0, + creation_fee: 0, + reclaim_rebate: 0, + }.build_storage().unwrap()); + // token + let t: Token = Token::new(b"x-btc".to_vec(), b"btc token".to_vec(), 8); + let t2: Token = Token::new(b"x-eth".to_vec(), b"eth token".to_vec(), 4); r.extend(GenesisConfig:: { token_list: vec![ diff --git a/cxrml/tokenbalances/src/tests.rs b/cxrml/tokenbalances/src/tests.rs index 0315479d11fac..529a3c2128064 100644 --- a/cxrml/tokenbalances/src/tests.rs +++ b/cxrml/tokenbalances/src/tests.rs @@ -13,15 +13,19 @@ fn test_genesis() { let eth_symbol = b"x-eth".to_vec(); assert_eq!(TokenBalances::token_list(), vec![ + Test::CHAINX_SYMBOL.to_vec(), btc_symbol.clone(), eth_symbol.clone(), ]); - assert_eq!(TokenBalances::token_info(btc_symbol.clone()).precision(), 8); - assert_eq!(TokenBalances::token_info(eth_symbol.clone()).precision(), 4); + assert_eq!(TokenBalances::token_info(btc_symbol.clone()).unwrap().0.precision(), 8); + assert_eq!(TokenBalances::token_info(eth_symbol.clone()).unwrap().0.precision(), 4); assert_eq!(TokenBalances::total_free_token(btc_symbol.clone()), 100); - assert_eq!(TokenBalances::total_locked_token(btc_symbol.clone()), 0); + assert_eq!(TokenBalances::total_reserved_token(btc_symbol.clone()), 0); + + // chainx symbol for every user + assert_eq!(TokenBalances::token_list_of(&0), [Test::CHAINX_SYMBOL.to_vec()].to_vec()); }); } @@ -31,28 +35,29 @@ fn test_register() { let t_sym: Symbol = b"x-eos".to_vec(); //slice_to_u8_8(b"x-eos"); let t_desc: TokenDesc = b"eos token".to_vec(); //slice_to_u8_32(b"eos token"); let precision = 4; - let t: Token = Token::new(t_sym.clone(), t_desc, precision); + let t: Token = Token::new(t_sym.clone(), t_desc, precision); assert_eq!(TokenBalances::register_token(t, 0, 0), Ok(())); - assert_eq!(TokenBalances::token_list_len(), 3); - assert_eq!(TokenBalances::token_list_map(2), (true, t_sym.clone())); + assert_eq!(TokenBalances::token_list_len(), 4); + assert_eq!(TokenBalances::token_list_map(3), t_sym.clone()); let btc_symbol = b"x-btc".to_vec(); //b"x-btc".to_vec(); let eth_symbol = b"x-eth".to_vec(); //slice_to_u8_8(b"x-eth"); assert_eq!(TokenBalances::token_list(), vec![ + Test::CHAINX_SYMBOL.to_vec(), btc_symbol.clone(), eth_symbol.clone(), t_sym.clone(), ]); assert_eq!(TokenBalances::total_free_token(t_sym.clone()), 0); - assert_eq!(TokenBalances::token_info(t_sym.clone()).precision(), 4); + assert_eq!(TokenBalances::token_info(t_sym.clone()).unwrap().0.precision(), 4); // test err branch let btc_t = Token::new(btc_symbol.clone(), b"btc token".to_vec(), 4); assert_noop!(TokenBalances::register_token(btc_t, 0, 0), "already has this token symbol"); - assert_eq!(TokenBalances::token_list_len(), 3); - assert_eq!(TokenBalances::token_list_map(3), (false, b"".to_vec())); + assert_eq!(TokenBalances::token_list_len(), 4); + assert_eq!(TokenBalances::token_list_map(4), b"".to_vec()); }) } @@ -62,26 +67,26 @@ fn test_remove() { // register a new token let t_sym: Symbol = b"x-eos".to_vec(); let t_desc: TokenDesc = b"eos token".to_vec(); - let precision: TestPrecision = 4; - let t: Token = Token::new(t_sym.clone(), t_desc, precision); + let precision: Precision = 4; + let t: Token = Token::new(t_sym.clone(), t_desc, precision); assert_eq!(TokenBalances::register_token(t.clone(), 0, 0), Ok(())); - assert_eq!(TokenBalances::token_list_map(2), (true, t_sym.clone())); + assert_eq!(TokenBalances::token_list_map(3), t_sym.clone()); // remove it assert_eq!(TokenBalances::cancel_token(&t_sym.clone()), Ok(())); - assert_eq!(TokenBalances::token_list_map(2), (false, t_sym.clone())); - assert_eq!(TokenBalances::token_list_len(), 3); // length not modify + assert_eq!(TokenBalances::token_list_map(3), t_sym.clone()); + assert_eq!(TokenBalances::token_list_len(), 4); // length not modify - assert_noop!(TokenBalances::cancel_token(&t_sym.clone()), "this token symbol dose not register yet or is invalid"); // re-register, but must be failed assert_noop!(TokenBalances::register_token(t.clone(), 0, 0), "already has this token symbol"); // create new token symbol - let t_new: Token = Token { symbol: b"x-eos2".to_vec(), ..t }; + let t_new: Token = Token { symbol: b"x-eos2".to_vec(), ..t }; + assert_noop!(TokenBalances::cancel_token(&t_new.symbol), "this token symbol dose not register yet or is invalid"); assert_eq!(TokenBalances::register_token(t_new.clone(), 0, 0), Ok(())); - assert_eq!(TokenBalances::token_list_map(2), (false, t_sym.clone())); - assert_eq!(TokenBalances::token_list_map(3), (true, t_new.symbol)); - assert_eq!(TokenBalances::token_list_len(), 4); + assert_eq!(TokenBalances::token_list_map(3), t_sym.clone()); + assert_eq!(TokenBalances::token_list_map(4), t_new.symbol); + assert_eq!(TokenBalances::token_list_len(), 5); }) } @@ -91,15 +96,15 @@ fn test_total_balance() { let btc_symbol = b"x-btc".to_vec(); assert_eq!(TokenBalances::total_token(&btc_symbol.clone()), 100); - TokenBalances::increase_total_free_token_by(&btc_symbol.clone(), 100).unwrap(); + TokenBalances::issue(&0, &btc_symbol, 100).unwrap(); assert_eq!(TokenBalances::total_token(&btc_symbol.clone()), 200); - TokenBalances::increase_total_locked_token_by(&btc_symbol.clone(), 50).unwrap(); + TokenBalances::issue(&0, &btc_symbol, 50).unwrap(); + TokenBalances::reserve(&0, &btc_symbol, 50).unwrap(); assert_eq!(TokenBalances::total_token(&btc_symbol.clone()), 250); - TokenBalances::decrease_total_locked_token_by(&btc_symbol.clone(), 25).unwrap(); - TokenBalances::decrease_total_free_token_by(&btc_symbol.clone(), 15).unwrap(); - assert_eq!(TokenBalances::total_token(&btc_symbol.clone()), 210); + TokenBalances::destroy(&0, &btc_symbol, 25).unwrap(); + assert_eq!(TokenBalances::total_token(&btc_symbol.clone()), 225); }) } @@ -108,14 +113,18 @@ fn test_account_balance() { with_externalities(&mut new_test_ext(), || { let a: u64 = 1; // accountid let btc_symbol = b"x-btc".to_vec(); - assert_eq!(TokenBalances::free_token_of(&a, &btc_symbol.clone()), 0); - assert_eq!(TokenBalances::locked_token_of(&a, &btc_symbol.clone()), 0); - assert_eq!(TokenBalances::total_token_of(&a, &btc_symbol.clone()), 0); - - TokenBalances::increase_account_free_token_by(&a, &btc_symbol.clone(), 100).unwrap(); + let key = (a, btc_symbol.clone()); + assert_eq!(TokenBalances::free_token(&key), 0); + assert_eq!(TokenBalances::reserved_token(&key), 0); + assert_eq!(TokenBalances::total_token_of(&a, &btc_symbol), 0); + + TokenBalances::issue(&a, &btc_symbol, 100).unwrap(); + assert_eq!(TokenBalances::free_token(&key), 100); + assert_eq!(TokenBalances::reserved_token(&key), 0); assert_eq!(TokenBalances::total_token_of(&a, &btc_symbol.clone()), 100); - TokenBalances::decrease_account_free_token_by(&a, &btc_symbol.clone(), 50).unwrap(); + TokenBalances::reserve(&a, &btc_symbol, 50).unwrap(); + TokenBalances::destroy(&a, &btc_symbol, 50).unwrap(); assert_eq!(TokenBalances::total_token_of(&a, &btc_symbol.clone()), 50); }) } @@ -125,25 +134,26 @@ fn test_normal_issue_and_destroy() { with_externalities(&mut new_test_ext(), || { let a: u64 = 1; // accountid let btc_symbol = b"x-btc".to_vec(); + let key = (a, btc_symbol.clone()); // issue TokenBalances::issue(&a, &btc_symbol.clone(), 50).unwrap(); assert_eq!(TokenBalances::total_token_of(&a, &btc_symbol.clone()), 50); assert_eq!(TokenBalances::total_token(&btc_symbol.clone()), 150); - // lock - TokenBalances::lock_destroy_token(&a, &btc_symbol.clone(), 25).unwrap(); - assert_eq!(TokenBalances::locked_token_of(&a, &btc_symbol.clone()), 25); - assert_eq!(TokenBalances::free_token_of(&a, &btc_symbol.clone()), 25); + // reserve + TokenBalances::reserve(&a, &btc_symbol.clone(), 25).unwrap(); + assert_eq!(TokenBalances::reserved_token(&key), 25); + assert_eq!(TokenBalances::free_token(&key), 25); assert_eq!(TokenBalances::total_token_of(&a, &btc_symbol.clone()), 50); - assert_eq!(TokenBalances::total_locked_token(&btc_symbol.clone()), 25); + assert_eq!(TokenBalances::total_reserved_token(&btc_symbol.clone()), 25); // destroy TokenBalances::destroy(&a, &btc_symbol.clone(), 25).unwrap(); - assert_eq!(TokenBalances::locked_token_of(&a, &btc_symbol.clone()), 0); - assert_eq!(TokenBalances::free_token_of(&a, &btc_symbol.clone()), 25); + assert_eq!(TokenBalances::reserved_token(&key), 0); + assert_eq!(TokenBalances::free_token(&key), 25); assert_eq!(TokenBalances::total_token_of(&a, &btc_symbol.clone()), 25); - assert_eq!(TokenBalances::total_locked_token(&btc_symbol.clone()), 0); + assert_eq!(TokenBalances::total_reserved_token(&btc_symbol.clone()), 0); assert_eq!(TokenBalances::total_token(&btc_symbol.clone()), 125); }) } @@ -153,25 +163,27 @@ fn test_unlock_issue_and_destroy2() { with_externalities(&mut new_test_ext(), || { let a: u64 = 1; // accountid let btc_symbol = b"x-btc".to_vec(); + let key = (a, btc_symbol.clone()); // issue TokenBalances::issue(&a, &btc_symbol.clone(), 50).unwrap(); assert_eq!(TokenBalances::total_token_of(&a, &btc_symbol.clone()), 50); assert_eq!(TokenBalances::total_token(&btc_symbol.clone()), 150); - // lock - TokenBalances::lock_destroy_token(&a, &btc_symbol.clone(), 25).unwrap(); - assert_eq!(TokenBalances::locked_token_of(&a, &btc_symbol.clone()), 25); - assert_eq!(TokenBalances::free_token_of(&a, &btc_symbol.clone()), 25); + + // reserve + TokenBalances::reserve(&a, &btc_symbol.clone(), 25).unwrap(); + assert_eq!(TokenBalances::reserved_token(&key), 25); + assert_eq!(TokenBalances::free_token(&key), 25); assert_eq!(TokenBalances::total_token_of(&a, &btc_symbol.clone()), 50); - assert_eq!(TokenBalances::total_locked_token(&btc_symbol.clone()), 25); + assert_eq!(TokenBalances::total_reserved_token(&btc_symbol.clone()), 25); - // unlock - TokenBalances::unlock_destroy_token(&a, &btc_symbol.clone(), 10).unwrap(); - assert_eq!(TokenBalances::locked_token_of(&a, &btc_symbol.clone()), 15); - assert_eq!(TokenBalances::free_token_of(&a, &btc_symbol.clone()), 35); + // unreserve + TokenBalances::unreserve(&a, &btc_symbol.clone(), 10).unwrap(); + assert_eq!(TokenBalances::reserved_token(&key), 15); + assert_eq!(TokenBalances::free_token(&key), 35); assert_eq!(TokenBalances::total_token_of(&a, &btc_symbol.clone()), 50); - assert_eq!(TokenBalances::total_locked_token(&btc_symbol.clone()), 15); + assert_eq!(TokenBalances::total_reserved_token(&btc_symbol.clone()), 15); }) } @@ -186,12 +198,12 @@ fn test_error_issue_and_destroy1() { assert_eq!(TokenBalances::total_token(&btc_symbol.clone()), 150); // destroy first // destroy - assert_err!(TokenBalances::destroy(&a, &btc_symbol.clone(), 25), "not enough locked token to destroy"); - // lock + assert_err!(TokenBalances::destroy(&a, &btc_symbol.clone(), 25), "reserved token too low to destroy"); + // reserve assert_eq!(TokenBalances::total_free_token(&btc_symbol.clone()), 150); - assert_err!(TokenBalances::lock_destroy_token(&a, &btc_symbol.clone(), 100), "not enough free token to lock for this account"); + assert_err!(TokenBalances::reserve(&a, &btc_symbol.clone(), 100), "free token too low to reserve"); // lock first - assert_ok!(TokenBalances::lock_destroy_token(&a, &btc_symbol.clone(), 25)); + assert_ok!(TokenBalances::reserve(&a, &btc_symbol.clone(), 25)); // destroy assert_ok!(TokenBalances::destroy(&a, &btc_symbol.clone(), 25)); }) @@ -204,12 +216,12 @@ fn test_error_issue_and_destroy2() { let btc_symbol = b"x-btc".to_vec(); // issue TokenBalances::issue(&a, &btc_symbol.clone(), 50).unwrap(); - assert_eq!(TokenBalances::total_token_of(&a, &btc_symbol.clone()), 50); + assert_eq!(TokenBalances::total_token_of(&a, &btc_symbol), 50); assert_eq!(TokenBalances::total_token(&btc_symbol.clone()), 150); // overflow let i: i32 = -1; - assert_err!(TokenBalances::lock_destroy_token(&a, &btc_symbol.clone(), i as TokenBalance), "not enough free token to lock"); - assert_err!(TokenBalances::issue(&a, &btc_symbol.clone(), i as TokenBalance), "Overflow in increase_total_free_token_by"); + assert_err!(TokenBalances::reserve(&a, &btc_symbol.clone(), i as TokenBalance), "free token too low to reserve"); + assert_err!(TokenBalances::issue(&a, &btc_symbol.clone(), i as TokenBalance), "free token too high to issue"); }) } @@ -220,13 +232,13 @@ fn test_error_issue_and_destroy3() { let btc_symbol = b"x-btc".to_vec(); // lock or destroy without init assert_err!(TokenBalances::destroy(&a, &btc_symbol.clone(), 25), "not a existed token in this account token list"); - assert_err!(TokenBalances::lock_destroy_token(&a, &btc_symbol.clone(), 25), "not a existed token in this account token list"); + assert_err!(TokenBalances::reserve(&a, &btc_symbol.clone(), 25), "not a existed token in this account token list"); TokenBalances::issue(&a, &btc_symbol.clone(), 0).unwrap(); - assert_err!(TokenBalances::destroy(&a, &btc_symbol.clone(), 25), "not enough locked token to destroy"); - assert_err!(TokenBalances::lock_destroy_token(&a, &btc_symbol.clone(), 25), "not enough free token to lock for this account"); + assert_err!(TokenBalances::destroy(&a, &btc_symbol.clone(), 25), "reserved token too low to destroy"); + assert_err!(TokenBalances::reserve(&a, &btc_symbol.clone(), 25), "free token too low to reserve"); TokenBalances::issue(&a, &btc_symbol.clone(), 100).unwrap(); - assert_ok!(TokenBalances::lock_destroy_token(&a, &btc_symbol.clone(), 25)); + assert_ok!(TokenBalances::reserve(&a, &btc_symbol.clone(), 25)); assert_ok!(TokenBalances::destroy(&a, &btc_symbol.clone(), 25)); }) } @@ -244,10 +256,10 @@ fn test_transfer() { // sum not change assert_eq!(TokenBalances::total_free_token(&btc_symbol.clone()), 150); assert_eq!(TokenBalances::total_token_of(&a, &btc_symbol.clone()), 25); - assert_eq!(TokenBalances::free_token_of(&b, &btc_symbol.clone()), 25); + assert_eq!(TokenBalances::free_token(&(b, btc_symbol.clone())), 25); assert_eq!(Balances::free_balance(&a), 990); - assert_err!(TokenBalances::transfer_token(Some(a).into(), b.into(), btc_symbol.clone().clone(), 50), "transactor's free token balance too low, can't transfer this token") + assert_err!(TokenBalances::transfer_token(Some(a).into(), b.into(), btc_symbol.clone().clone(), 50), "free token too low to send value") }) } @@ -279,11 +291,11 @@ fn test_transfer_err() { TokenBalances::transfer_token(Some(b).into(), a.into(), btc_symbol.clone().clone(), 25).unwrap(); // sum not change assert_eq!(TokenBalances::total_free_token(&btc_symbol.clone()), 150); - assert_eq!(TokenBalances::free_token_of(&b, &btc_symbol.clone()), 25); + assert_eq!(TokenBalances::free_token(&(b, btc_symbol.clone())), 25); assert_eq!(TokenBalances::total_token_of(&a, &btc_symbol.clone()), 25); assert_eq!(Balances::free_balance(&b), 500); - assert_err!(TokenBalances::transfer_token(Some(b).into(), a.into(), btc_symbol.clone().clone(), 1), + assert_err!(TokenBalances::transfer_token(Some(b).into(), a.into(), btc_symbol.clone(), 1), "chainx balance is not enough after this tx, not allow to be killed at here"); assert_eq!(Balances::free_balance(&b), 500); }) @@ -305,9 +317,57 @@ fn test_char_valid() { let sym = b"23jfkldae(".to_vec(); assert_err!(TokenBalances::transfer_token(origin, to.clone(), sym, 10), "not a valid symbol char for number, capital/small letter or '-', '.', '|', '~'"); - let t: Token = Token::new(b"x-btc2".to_vec(), b"btc token fdsfsdfasfasdfasdfasdfasdfasdfasdfjaskldfjalskdjflk;asjdfklasjkldfjalksdjfklasjflkdasjflkjkladsjfkrtewtewrtwertrjhjwretywertwertwerrtwerrtwerrtwertwelasjdfklsajdflkaj".to_vec(), 8); + let t: Token = Token::new(b"x-btc2".to_vec(), b"btc token fdsfsdfasfasdfasdfasdfasdfasdfasdfjaskldfjalskdjflk;asjdfklasjkldfjalksdjfklasjflkdasjflkjkladsjfkrtewtewrtwertrjhjwretywertwertwerrtwerrtwerrtwertwelasjdfklsajdflkaj".to_vec(), 8); assert_err!(TokenBalances::register_token(t, 0, 0), "token desc length too long"); - let t: Token = Token::new(b"x-btc?".to_vec(), b"btc token".to_vec(), 8); + let t: Token = Token::new(b"x-btc?".to_vec(), b"btc token".to_vec(), 8); assert_err!(TokenBalances::register_token(t, 0, 0), "not a valid symbol char for number, capital/small letter or '-', '.', '|', '~'") }) +} + +#[test] +fn test_chainx() { + with_externalities(&mut new_test_ext2(), || { + let a: u64 = 1; // accountid + let b: u64 = 2; // accountid + let sym = Test::CHAINX_SYMBOL.to_vec(); + assert_err!(TokenBalances::issue(&a, &sym, 100), "can't issue chainx token"); + + assert_ok!(TokenBalances::reserve(&a, &sym, 100)); + assert_eq!(Balances::free_balance(&a), 900); + assert_eq!(Balances::reserved_balance(&a), 100); + assert_eq!(TokenBalances::reserved_token(&(a, sym.clone())), 100); + + assert_ok!(TokenBalances::unreserve(&a, &sym, 50)); + assert_eq!(Balances::free_balance(&a), 950); + assert_eq!(TokenBalances::reserved_token(&(a, sym.clone())), 50); + assert_eq!(Balances::reserved_balance(&a), 50); + assert_err!(TokenBalances::destroy(&a, &sym, 50), "can't destroy chainx token"); + + assert_err!(TokenBalances::transfer_token(Some(b).into(), a.into(), sym.clone(), 1), "not allow to transfer chainx use transfer_token"); + }) +} + +#[test] +fn test_chainx_err() { + with_externalities(&mut new_test_ext2(), || { + let a: u64 = 1; // accountid + let sym = Test::CHAINX_SYMBOL.to_vec(); + + assert_err!(TokenBalances::reserve(&a, &sym, 2000), "chainx free token too low to reserve"); + assert_err!(TokenBalances::unreserve(&a, &sym, 10), "chainx reserved token too low to unreserve"); + + let i: i32 = -1; + let larger_balance: TokenBalance = (i as u64) as u128 + 2; + + assert_eq!(larger_balance, 18446744073709551617); + assert_eq!(larger_balance as u64, 1); + + assert_ok!(TokenBalances::reserve(&a, &sym, larger_balance)); + assert_eq!(Balances::free_balance(&a), 999); + + let i: i32 = -1; + let max_balance: TokenBalance = i as u128; + assert_eq!(max_balance as u64, 18446744073709551615); + assert_err!(TokenBalances::reserve(&a, &sym, max_balance), "chainx free token too low to reserve"); + }) } \ No newline at end of file diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index e53320259d74b..fb7e7a28cddcd 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -207,8 +207,10 @@ impl council::motions::Trait for Runtime { } impl tokenbalances::Trait for Runtime { + const CHAINX_SYMBOL: tokenbalances::SymbolString = b"pcx"; + const CHAINX_PRECISION: tokenbalances::Precision = 8; + const CHAINX_TOKEN_DESC: tokenbalances::DescString = b"pcx token for ChainX"; type TokenBalance = TokenBalance; - type Precision = Precision; type Event = Event; }