From 8fd7f39c2aef2908264143cf8b460b45efa49237 Mon Sep 17 00:00:00 2001 From: Shaopeng Wang Date: Tue, 1 Sep 2020 15:39:43 +1200 Subject: [PATCH 01/13] Impl xtokens. --- Cargo.dev.toml | 1 + xtokens/Cargo.toml | 47 +++++++ xtokens/src/lib.rs | 321 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 369 insertions(+) create mode 100644 xtokens/Cargo.toml create mode 100644 xtokens/src/lib.rs diff --git a/Cargo.dev.toml b/Cargo.dev.toml index 76d67fe61..dc29db2bf 100644 --- a/Cargo.dev.toml +++ b/Cargo.dev.toml @@ -10,4 +10,5 @@ members = [ "traits", "utilities", "vesting", + "xtokens", ] diff --git a/xtokens/Cargo.toml b/xtokens/Cargo.toml new file mode 100644 index 000000000..8b007118b --- /dev/null +++ b/xtokens/Cargo.toml @@ -0,0 +1,47 @@ +[package] +name = "orml-xtokens" +description = "Crosschain token transfer" +repository = "https://github.com/open-web3-stack/open-runtime-module-library/tree/master/tokens" +license = "Apache-2.0" +version = "0.1.3-dev" +authors = ["Laminar Developers "] +edition = "2018" + +[dependencies] +serde = { version = "1.0.111", optional = true } +codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +sp-runtime = { version = "2.0.0-rc5", default-features = false } +sp-io = { version = "2.0.0-rc5", default-features = false } +sp-std = { version = "2.0.0-rc5", default-features = false } + +frame-support = { version = "2.0.0-rc5", default-features = false } +frame-system = { version = "2.0.0-rc5", default-features = false } + +orml-traits = { path = "../traits", version = "0.1.3-dev", default-features = false } + +cumulus-primitives = { git = "https://github.com/paritytech/cumulus", default-features = false } +cumulus-upward-message = { git = "https://github.com/paritytech/cumulus", default-features = false } + +# TODO: switch to polkadot master branch once ready +polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "rococo-branch", default-features = false } + +[dev-dependencies] +sp-core = { version = "2.0.0-rc5", default-features = false } + +clear_on_drop = { version = "0.2.4", features = ["no_cc"] } # https://github.com/paritytech/substrate/issues/4179 + +[features] +default = ["std"] +std = [ + "serde", + "codec/std", + "sp-runtime/std", + "sp-std/std", + "sp-io/std", + "frame-support/std", + "frame-system/std", + "orml-traits/std", + "cumulus-primitives/std", + "cumulus-upward-message/std", + "polkadot-parachain/std", +] diff --git a/xtokens/src/lib.rs b/xtokens/src/lib.rs new file mode 100644 index 000000000..ecbd0892b --- /dev/null +++ b/xtokens/src/lib.rs @@ -0,0 +1,321 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::{Decode, Encode}; +use frame_support::{decl_error, decl_event, decl_module, decl_storage, traits::Get, Parameter}; +use frame_system::ensure_signed; +use orml_traits::MultiCurrency; +use sp_runtime::{ + traits::{AtLeast32BitUnsigned, Convert, MaybeSerializeDeserialize, Member}, + DispatchResult, RuntimeDebug, +}; +use sp_std::{ + convert::{TryFrom, TryInto}, + prelude::*, +}; + +use cumulus_primitives::{ + relay_chain::{Balance as RelayChainBalance, DownwardMessage}, + xcmp::{XCMPMessageHandler, XCMPMessageSender}, + DownwardMessageHandler, ParaId, UpwardMessageOrigin, UpwardMessageSender, +}; +use cumulus_upward_message::BalancesMessage; +use polkadot_parachain::primitives::AccountIdConversion; + +#[derive(Encode, Decode, Eq, PartialEq, Clone, Copy, RuntimeDebug)] +/// Identity of chain. +pub enum ChainId { + /// The relay chain. + RelayChain, + /// A parachain. + ParaChain(ParaId), +} + +#[derive(Encode, Decode, Eq, PartialEq, Clone, RuntimeDebug)] +/// Identity of cross chain currency. +pub struct XCurrencyId { + /// The owner chain of the currency. For instance, the owner chain of DOT is + /// Polkadot. + chain_id: ChainId, + /// The identity of the currency. + currency_id: Vec, +} + +#[derive(Encode, Decode, RuntimeDebug)] +pub enum XCMPTokenMessage { + /// Token transfer. [x_currency_id, para_id, dest, amount] + Transfer(XCurrencyId, ParaId, AccountId, Balance), +} + +pub trait Trait: frame_system::Trait { + type Event: From> + Into<::Event>; + + /// The balance type. + type Balance: Parameter + Member + AtLeast32BitUnsigned + Default + Copy + MaybeSerializeDeserialize; + + /// Convertor to convert between `T::Balance` and relay chain balance. + type BalanceConvertor: Convert + Convert; + + /// The currency ID type + type CurrencyId: Parameter + Member + Copy + MaybeSerializeDeserialize + Ord + Into> + TryFrom>; + + /// Currency Id of relay chain. + type RelayChainCurrencyId: Get; + + /// The `MultiCurrency` impl for tokens. + type Currency: MultiCurrency; + + /// Parachain ID. + type ParaId: Get; + + /// The sender of XCMP message. + type XCMPMessageSender: XCMPMessageSender>; + + /// The sender of upward message(to relay chain). + type UpwardMessageSender: UpwardMessageSender; + + /// The upward message type used by parachain runtime. + type UpwardMessage: codec::Codec + BalancesMessage; +} + +decl_storage! { + trait Store for Module as XTokens {} +} + +decl_event! { + pub enum Event where + ::AccountId, + ::Balance, + XCurrencyId = XCurrencyId, + { + /// Transferred to relay chain. [src, dest, amount] + TransferredToRelayChain(AccountId, AccountId, Balance), + + /// Received transfer from relay chain. [dest, amount] + ReceivedTransferFromRelayChain(AccountId, Balance), + + /// Transferred to parachain. [x_currency_id, src, para_id, dest, amount] + TransferredToParachain(XCurrencyId, AccountId, ParaId, AccountId, Balance), + + /// Received transfer from parachain. [x_currency_id, para_id, dest, amount] + ReceivedTransferFromParachain(XCurrencyId, ParaId, AccountId, Balance), + } +} + +decl_error! { + /// Error for xtokens module. + pub enum Error for Module { + /// Insufficient balance to transfer. + InsufficientBalance, + /// Invalid currency ID. + InvalidCurrencyId, + } +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + type Error = Error; + + fn deposit_event() = default; + + /// Transfer relay chain tokens to relay chain. + #[weight = 10] + pub fn transfer_to_relay_chain(origin, dest: T::AccountId, amount: T::Balance) { + let who = ensure_signed(origin)?; + Self::do_transfer_to_relay_chain(&who, &dest, amount)?; + Self::deposit_event(Event::::TransferredToRelayChain(who, dest, amount)); + } + + /// Transfer tokens to parachain. + /// + /// TODO: document the way of unknown currency handling + #[weight = 10] + pub fn transfer_to_parachain( + origin, + x_currency_id: XCurrencyId, + para_id: ParaId, + dest: T::AccountId, + amount: T::Balance, + ) { + let who = ensure_signed(origin)?; + Self::do_transfer_to_parachain(x_currency_id.clone(), &who, para_id, &dest, amount)?; + Self::deposit_event(Event::::TransferredToParachain(x_currency_id, who, para_id, dest, amount)); + } + } +} + +impl Module { + fn do_transfer_to_relay_chain(who: &T::AccountId, dest: &T::AccountId, amount: T::Balance) -> DispatchResult { + T::Currency::withdraw(T::RelayChainCurrencyId::get(), who, amount)?; + let msg = T::UpwardMessage::transfer(dest.clone(), T::BalanceConvertor::convert(amount)); + T::UpwardMessageSender::send_upward_message(&msg, UpwardMessageOrigin::Signed).expect("Should not fail; qed"); + Ok(()) + } + + fn do_transfer_to_parachain( + x_currency_id: XCurrencyId, + src: &T::AccountId, + para_id: ParaId, + dest: &T::AccountId, + amount: T::Balance, + ) -> DispatchResult { + match x_currency_id.chain_id { + ChainId::RelayChain => { + Self::transfer_relay_chain_tokens_to_parachain(x_currency_id.clone(), src, para_id, dest, amount) + } + ChainId::ParaChain(token_owner) => { + if T::ParaId::get() == token_owner { + Self::transfer_owned_tokens_to_parachain(x_currency_id, src, para_id, dest, amount) + } else { + if let Ok(currency_id) = x_currency_id.currency_id.clone().try_into() { + Self::transfer_known_tokens_to_parachain( + token_owner, + currency_id, + x_currency_id, + src, + para_id, + dest, + amount, + ) + } else { + //TODO: handle unknown tokens + Ok(()) + } + } + } + } + } + + /// Transfer relay chain tokens to another parachain. + /// + /// 1. Withdraw `src` balance. + /// 2. Transfer in relay chain: from self parachain account to `para_id` + /// account. 3. Notify `para_id` the transfer. + fn transfer_relay_chain_tokens_to_parachain( + x_currency_id: XCurrencyId, + src: &T::AccountId, + para_id: ParaId, + dest: &T::AccountId, + amount: T::Balance, + ) -> DispatchResult { + let para_account = para_id.into_account(); + + T::Currency::withdraw(T::RelayChainCurrencyId::get(), src, amount)?; + + let msg = T::UpwardMessage::transfer(para_account, T::BalanceConvertor::convert(amount)); + T::UpwardMessageSender::send_upward_message(&msg, UpwardMessageOrigin::Signed).expect("Should not fail; qed"); + + T::XCMPMessageSender::send_xcmp_message( + para_id, + &XCMPTokenMessage::Transfer(x_currency_id, para_id, dest.clone(), amount), + ) + .expect("Should not fail; qed"); + + Ok(()) + } + + /// Transfer tokens owned by self parachain to another parachain. + /// + /// 1. Transfer from `src` to `para_id` account. + /// 2. Notify `para_id` the transfer. + fn transfer_owned_tokens_to_parachain( + x_currency_id: XCurrencyId, + src: &T::AccountId, + para_id: ParaId, + dest: &T::AccountId, + amount: T::Balance, + ) -> DispatchResult { + let para_account = para_id.into_account(); + let currency_id: T::CurrencyId = x_currency_id + .currency_id + .clone() + .try_into() + .map_err(|_| Error::::InvalidCurrencyId)?; + T::Currency::transfer(currency_id, src, ¶_account, amount)?; + + T::XCMPMessageSender::send_xcmp_message( + para_id, + &XCMPTokenMessage::Transfer(x_currency_id, para_id, dest.clone(), amount), + ) + .expect("Should not fail; qed"); + + Ok(()) + } + + /// Transfer known tokens to another parachain. + /// + /// 1. Withdraw from `src`. + /// 2. Notify token owner parachain the transfer. (Token owner chain would + /// further notify `para_id`) + fn transfer_known_tokens_to_parachain( + token_owner: ParaId, + currency_id: T::CurrencyId, + x_currency_id: XCurrencyId, + src: &T::AccountId, + para_id: ParaId, + dest: &T::AccountId, + amount: T::Balance, + ) -> DispatchResult { + T::Currency::withdraw(currency_id, src, amount)?; + + T::XCMPMessageSender::send_xcmp_message( + token_owner, + &XCMPTokenMessage::Transfer(x_currency_id, para_id, dest.clone(), amount), + ) + .expect("Should not fail; qed"); + + Ok(()) + } +} + +/// This is a hack to convert from one generic type to another where we are sure +/// that both are the same type/use the same encoding. +fn convert_hack(input: &impl Encode) -> O { + input.using_encoded(|e| Decode::decode(&mut &e[..]).expect("Must be compatible; qed")) +} + +impl DownwardMessageHandler for Module { + fn handle_downward_message(msg: &DownwardMessage) { + if let DownwardMessage::TransferInto(dest, amount, _) = msg { + let dest: T::AccountId = convert_hack(dest); + let amount: T::Balance = >::convert(*amount); + // Should not fail, but if it does, there is nothing we could do. + let _ = T::Currency::deposit(T::RelayChainCurrencyId::get(), &dest, amount); + + Self::deposit_event(Event::::ReceivedTransferFromRelayChain(dest, amount)); + } + } +} + +impl XCMPMessageHandler> for Module { + fn handle_xcmp_message(src: ParaId, msg: &XCMPTokenMessage) { + match msg { + XCMPTokenMessage::Transfer(x_currency_id, para_id, dest, amount) => { + match x_currency_id.chain_id { + ChainId::RelayChain => { + // Relay chain tokens. Should not fail, but if it does, there is nothing we + // could do. + let _ = T::Currency::deposit(T::RelayChainCurrencyId::get(), &dest, *amount); + } + ChainId::ParaChain(token_owner) => { + if T::ParaId::get() == token_owner { + //TODO: handle as owner + // 1. transfer between para accounts + // 2. notify the `para_id` + } else { + //TODO: handle as non-owner + // - known tokens + // - unknown tokens + } + } + } + + Self::deposit_event(Event::::ReceivedTransferFromParachain( + x_currency_id.clone(), + src, + dest.clone(), + *amount, + )); + } + } + } +} From 59c0e9cd91204de51966c7f53d3eb1ea7ba14c83 Mon Sep 17 00:00:00 2001 From: Shaopeng Wang Date: Tue, 1 Sep 2020 16:42:16 +1200 Subject: [PATCH 02/13] XCMP token message handling. --- xtokens/src/lib.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/xtokens/src/lib.rs b/xtokens/src/lib.rs index ecbd0892b..9a00385bf 100644 --- a/xtokens/src/lib.rs +++ b/xtokens/src/lib.rs @@ -278,7 +278,7 @@ impl DownwardMessageHandler for Module { if let DownwardMessage::TransferInto(dest, amount, _) = msg { let dest: T::AccountId = convert_hack(dest); let amount: T::Balance = >::convert(*amount); - // Should not fail, but if it does, there is nothing we could do. + // Should not fail, but if it does, there is nothing can be done. let _ = T::Currency::deposit(T::RelayChainCurrencyId::get(), &dest, amount); Self::deposit_event(Event::::ReceivedTransferFromRelayChain(dest, amount)); @@ -298,13 +298,24 @@ impl XCMPMessageHandler> fo } ChainId::ParaChain(token_owner) => { if T::ParaId::get() == token_owner { - //TODO: handle as owner + // Handle owned tokens: // 1. transfer between para accounts // 2. notify the `para_id` + let src_para_account = src.into_account(); + // Should not fail, but if it does, there is nothing can be done. + let _ = Self::transfer_owned_tokens_to_parachain( + x_currency_id.clone(), + &src_para_account, + *para_id, + dest, + *amount, + ); + } else if let Ok(currency_id) = x_currency_id.currency_id.clone().try_into() { + // Handle known tokens. + // Should not fail, but if it does, there is nothing can be done. + let _ = T::Currency::deposit(currency_id, dest, *amount); } else { - //TODO: handle as non-owner - // - known tokens - // - unknown tokens + //TODO: Handle unknown tokens. } } } From f9629bc04526963fc141a70e75b79495fe21b131 Mon Sep 17 00:00:00 2001 From: Shaopeng Wang Date: Tue, 1 Sep 2020 16:43:16 +1200 Subject: [PATCH 03/13] Use Substrate rc6 dependencies. --- xtokens/Cargo.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/xtokens/Cargo.toml b/xtokens/Cargo.toml index 8b007118b..9f023dae8 100644 --- a/xtokens/Cargo.toml +++ b/xtokens/Cargo.toml @@ -10,12 +10,12 @@ edition = "2018" [dependencies] serde = { version = "1.0.111", optional = true } codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } -sp-runtime = { version = "2.0.0-rc5", default-features = false } -sp-io = { version = "2.0.0-rc5", default-features = false } -sp-std = { version = "2.0.0-rc5", default-features = false } +sp-runtime = { version = "2.0.0-rc6", default-features = false } +sp-io = { version = "2.0.0-rc6", default-features = false } +sp-std = { version = "2.0.0-rc6", default-features = false } -frame-support = { version = "2.0.0-rc5", default-features = false } -frame-system = { version = "2.0.0-rc5", default-features = false } +frame-support = { version = "2.0.0-rc6", default-features = false } +frame-system = { version = "2.0.0-rc6", default-features = false } orml-traits = { path = "../traits", version = "0.1.3-dev", default-features = false } @@ -26,7 +26,7 @@ cumulus-upward-message = { git = "https://github.com/paritytech/cumulus", defaul polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "rococo-branch", default-features = false } [dev-dependencies] -sp-core = { version = "2.0.0-rc5", default-features = false } +sp-core = { version = "2.0.0-rc6", default-features = false } clear_on_drop = { version = "0.2.4", features = ["no_cc"] } # https://github.com/paritytech/substrate/issues/4179 From 599f76d6b3439afd6dea1d8d525735a56aefd7e0 Mon Sep 17 00:00:00 2001 From: Shaopeng Wang Date: Tue, 1 Sep 2020 17:08:20 +1200 Subject: [PATCH 04/13] Handle unknown tokens transfer and recieving. --- xtokens/src/lib.rs | 48 +++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/xtokens/src/lib.rs b/xtokens/src/lib.rs index 9a00385bf..efbcc92ab 100644 --- a/xtokens/src/lib.rs +++ b/xtokens/src/lib.rs @@ -5,7 +5,7 @@ use frame_support::{decl_error, decl_event, decl_module, decl_storage, traits::G use frame_system::ensure_signed; use orml_traits::MultiCurrency; use sp_runtime::{ - traits::{AtLeast32BitUnsigned, Convert, MaybeSerializeDeserialize, Member}, + traits::{AtLeast32BitUnsigned, CheckedSub, Convert, MaybeSerializeDeserialize, Member, Saturating}, DispatchResult, RuntimeDebug, }; use sp_std::{ @@ -78,7 +78,10 @@ pub trait Trait: frame_system::Trait { } decl_storage! { - trait Store for Module as XTokens {} + trait Store for Module as XTokens { + /// Balances of currencies not known to self parachain. + UnknownBalances: double_map hasher(blake2_128_concat) T::AccountId, hasher(blake2_128_concat) Vec => T::Balance; + } } decl_event! { @@ -126,8 +129,6 @@ decl_module! { } /// Transfer tokens to parachain. - /// - /// TODO: document the way of unknown currency handling #[weight = 10] pub fn transfer_to_parachain( origin, @@ -166,20 +167,7 @@ impl Module { if T::ParaId::get() == token_owner { Self::transfer_owned_tokens_to_parachain(x_currency_id, src, para_id, dest, amount) } else { - if let Ok(currency_id) = x_currency_id.currency_id.clone().try_into() { - Self::transfer_known_tokens_to_parachain( - token_owner, - currency_id, - x_currency_id, - src, - para_id, - dest, - amount, - ) - } else { - //TODO: handle unknown tokens - Ok(()) - } + Self::transfer_non_owned_tokens_to_parachain(token_owner, x_currency_id, src, para_id, dest, amount) } } } @@ -213,7 +201,8 @@ impl Module { Ok(()) } - /// Transfer tokens owned by self parachain to another parachain. + /// Transfer parachain tokens "owned" by self parachain to another + /// parachain. /// /// 1. Transfer from `src` to `para_id` account. /// 2. Notify `para_id` the transfer. @@ -241,21 +230,29 @@ impl Module { Ok(()) } - /// Transfer known tokens to another parachain. + /// Transfer parachain tokens not "owned" by self chain to another + /// parachain. /// /// 1. Withdraw from `src`. /// 2. Notify token owner parachain the transfer. (Token owner chain would /// further notify `para_id`) - fn transfer_known_tokens_to_parachain( + fn transfer_non_owned_tokens_to_parachain( token_owner: ParaId, - currency_id: T::CurrencyId, x_currency_id: XCurrencyId, src: &T::AccountId, para_id: ParaId, dest: &T::AccountId, amount: T::Balance, ) -> DispatchResult { - T::Currency::withdraw(currency_id, src, amount)?; + if let Ok(currency_id) = x_currency_id.currency_id.clone().try_into() { + // Known currency, withdraw from src. + T::Currency::withdraw(currency_id, src, amount)?; + } else { + // Unknown currency, update balance. + UnknownBalances::::try_mutate(src, &x_currency_id.currency_id, |total| { + total.checked_sub(&amount).ok_or(Error::::InsufficientBalance) + })?; + } T::XCMPMessageSender::send_xcmp_message( token_owner, @@ -315,7 +312,10 @@ impl XCMPMessageHandler> fo // Should not fail, but if it does, there is nothing can be done. let _ = T::Currency::deposit(currency_id, dest, *amount); } else { - //TODO: Handle unknown tokens. + // Handle unknown tokens. + UnknownBalances::::mutate(dest, x_currency_id.currency_id.clone(), |total| { + *total = total.saturating_add(*amount) + }); } } } From e6503bdc1c66f4e7746123bb927ff4524ab4b916 Mon Sep 17 00:00:00 2001 From: Shaopeng Wang Date: Tue, 1 Sep 2020 17:09:38 +1200 Subject: [PATCH 05/13] Remove unnecessary clone. --- xtokens/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xtokens/src/lib.rs b/xtokens/src/lib.rs index efbcc92ab..57efe201c 100644 --- a/xtokens/src/lib.rs +++ b/xtokens/src/lib.rs @@ -161,7 +161,7 @@ impl Module { ) -> DispatchResult { match x_currency_id.chain_id { ChainId::RelayChain => { - Self::transfer_relay_chain_tokens_to_parachain(x_currency_id.clone(), src, para_id, dest, amount) + Self::transfer_relay_chain_tokens_to_parachain(x_currency_id, src, para_id, dest, amount) } ChainId::ParaChain(token_owner) => { if T::ParaId::get() == token_owner { From 0553c2d8cd12f25f3a8228f6e42504ca7a3836c4 Mon Sep 17 00:00:00 2001 From: Shaopeng Wang Date: Wed, 2 Sep 2020 15:35:33 +1200 Subject: [PATCH 06/13] Add mock. --- xtokens/Cargo.toml | 3 +- xtokens/src/lib.rs | 25 ++++-- xtokens/src/mock.rs | 194 +++++++++++++++++++++++++++++++++++++++++++ xtokens/src/tests.rs | 12 +++ 4 files changed, 226 insertions(+), 8 deletions(-) create mode 100644 xtokens/src/mock.rs create mode 100644 xtokens/src/tests.rs diff --git a/xtokens/Cargo.toml b/xtokens/Cargo.toml index 9f023dae8..5ffb44308 100644 --- a/xtokens/Cargo.toml +++ b/xtokens/Cargo.toml @@ -18,6 +18,7 @@ frame-support = { version = "2.0.0-rc6", default-features = false } frame-system = { version = "2.0.0-rc6", default-features = false } orml-traits = { path = "../traits", version = "0.1.3-dev", default-features = false } +orml-utilities = { path = "../utilities", version = "0.1.3-dev", default-features = false } cumulus-primitives = { git = "https://github.com/paritytech/cumulus", default-features = false } cumulus-upward-message = { git = "https://github.com/paritytech/cumulus", default-features = false } @@ -27,8 +28,8 @@ polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = [dev-dependencies] sp-core = { version = "2.0.0-rc6", default-features = false } - clear_on_drop = { version = "0.2.4", features = ["no_cc"] } # https://github.com/paritytech/substrate/issues/4179 +orml-tokens = { path = "../tokens", version = "0.1.3-dev" } [features] default = ["std"] diff --git a/xtokens/src/lib.rs b/xtokens/src/lib.rs index 57efe201c..ed762df01 100644 --- a/xtokens/src/lib.rs +++ b/xtokens/src/lib.rs @@ -3,7 +3,6 @@ use codec::{Decode, Encode}; use frame_support::{decl_error, decl_event, decl_module, decl_storage, traits::Get, Parameter}; use frame_system::ensure_signed; -use orml_traits::MultiCurrency; use sp_runtime::{ traits::{AtLeast32BitUnsigned, CheckedSub, Convert, MaybeSerializeDeserialize, Member, Saturating}, DispatchResult, RuntimeDebug, @@ -21,6 +20,12 @@ use cumulus_primitives::{ use cumulus_upward_message::BalancesMessage; use polkadot_parachain::primitives::AccountIdConversion; +use orml_traits::MultiCurrency; +use orml_utilities::with_transaction_result; + +mod mock; +mod tests; + #[derive(Encode, Decode, Eq, PartialEq, Clone, Copy, RuntimeDebug)] /// Identity of chain. pub enum ChainId { @@ -123,9 +128,12 @@ decl_module! { /// Transfer relay chain tokens to relay chain. #[weight = 10] pub fn transfer_to_relay_chain(origin, dest: T::AccountId, amount: T::Balance) { - let who = ensure_signed(origin)?; - Self::do_transfer_to_relay_chain(&who, &dest, amount)?; - Self::deposit_event(Event::::TransferredToRelayChain(who, dest, amount)); + with_transaction_result(|| { + let who = ensure_signed(origin)?; + Self::do_transfer_to_relay_chain(&who, &dest, amount)?; + Self::deposit_event(Event::::TransferredToRelayChain(who, dest, amount)); + Ok(()) + })?; } /// Transfer tokens to parachain. @@ -137,9 +145,12 @@ decl_module! { dest: T::AccountId, amount: T::Balance, ) { - let who = ensure_signed(origin)?; - Self::do_transfer_to_parachain(x_currency_id.clone(), &who, para_id, &dest, amount)?; - Self::deposit_event(Event::::TransferredToParachain(x_currency_id, who, para_id, dest, amount)); + with_transaction_result(|| { + let who = ensure_signed(origin)?; + Self::do_transfer_to_parachain(x_currency_id.clone(), &who, para_id, &dest, amount)?; + Self::deposit_event(Event::::TransferredToParachain(x_currency_id, who, para_id, dest, amount)); + Ok(()) + })?; } } } diff --git a/xtokens/src/mock.rs b/xtokens/src/mock.rs new file mode 100644 index 000000000..10452876f --- /dev/null +++ b/xtokens/src/mock.rs @@ -0,0 +1,194 @@ +//! Mocks for the xtokens module. + +#![cfg(test)] + +use frame_support::{impl_outer_event, impl_outer_origin, parameter_types}; +use frame_system as system; +use serde::{Deserialize, Serialize}; +use sp_core::H256; +use sp_runtime::{testing::Header, traits::IdentityLookup, Perbill}; +use sp_std::cell::RefCell; + +use super::*; + +type AccountId = u128; +pub type Balance = u128; + +impl_outer_origin! { + pub enum Origin for Runtime {} +} + +mod xtokens { + pub use crate::Event; +} + +impl_outer_event! { + pub enum TestEvent for Runtime { + frame_system, + orml_tokens, + xtokens, + } +} + +// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Runtime; +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); +} + +impl frame_system::Trait for Runtime { + type Origin = Origin; + type Call = (); + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type Event = TestEvent; + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + type AvailableBlockRatio = AvailableBlockRatio; + type Version = (); + type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BlockExecutionWeight = (); + type ExtrinsicBaseWeight = (); + type MaximumExtrinsicWeight = (); + type BaseCallFilter = (); + type SystemWeightInfo = (); +} + +#[repr(u8)] +#[derive(Encode, Decode, Serialize, Deserialize, Eq, PartialEq, Copy, Clone, RuntimeDebug, PartialOrd, Ord)] +pub enum CurrencyId { + Owned = 0, + BTC, + DOT, +} +impl Into> for CurrencyId { + fn into(self) -> Vec { + vec![self as u8] + } +} + +impl TryFrom> for CurrencyId { + type Error = (); + + fn try_from(v: Vec) -> Result { + if v.len() == 1 { + let num = v[0]; + match num { + 0 => return Ok(CurrencyId::Owned), + 1 => return Ok(CurrencyId::BTC), + 2 => return Ok(CurrencyId::DOT), + _ => return Err(()), + }; + } + Err(()) + } +} + +impl orml_tokens::Trait for Runtime { + type Event = TestEvent; + type Balance = Balance; + type Amount = i128; + type CurrencyId = CurrencyId; + type OnReceived = (); + type WeightInfo = (); +} +pub type Tokens = orml_tokens::Module; + +parameter_types! { + pub const RelayChainCurrencyId: CurrencyId = CurrencyId::DOT; + pub MockParaId: ParaId = 0.into(); +} + +impl Trait for Runtime { + type Event = TestEvent; + type Balance = Balance; + type BalanceConvertor = BalanceConvertor; + type CurrencyId = CurrencyId; + type RelayChainCurrencyId = RelayChainCurrencyId; + type Currency = Tokens; + type ParaId = MockParaId; + type XCMPMessageSender = MockXCMPMessageSender; + type UpwardMessageSender = MockUpwardMessageSender; + type UpwardMessage = MockUpwardMessage; +} + +pub type XTokens = Module; + +pub struct MockXCMPMessageSender; +impl XCMPMessageSender> for MockXCMPMessageSender { + fn send_xcmp_message(_dest: ParaId, _msg: &XCMPTokenMessage) -> Result<(), ()> { + Ok(()) + } +} + +#[derive(Encode, Decode)] +pub struct MockUpwardMessage(AccountId, Balance); +impl BalancesMessage for MockUpwardMessage { + fn transfer(dest: AccountId, amount: Balance) -> Self { + MockUpwardMessage(dest, amount) + } +} + +pub struct MockUpwardMessageSender; +impl UpwardMessageSender for MockUpwardMessageSender { + fn send_upward_message(_msg: &MockUpwardMessage, _origin: UpwardMessageOrigin) -> Result<(), ()> { + Ok(()) + } +} + +pub struct BalanceConvertor; +impl Convert for BalanceConvertor { + fn convert(x: u128) -> u128 { + x + } +} + +pub const ALICE: AccountId = 1; +pub const BOB: AccountId = 2; + +pub struct ExtBuilder { + endowed_accounts: Vec<(AccountId, CurrencyId, Balance)>, +} + +impl Default for ExtBuilder { + fn default() -> Self { + Self { + endowed_accounts: vec![], + } + } +} + +impl ExtBuilder { + pub fn balances(mut self, endowed_accounts: Vec<(AccountId, CurrencyId, Balance)>) -> Self { + self.endowed_accounts = endowed_accounts; + self + } + + pub fn build(self) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + orml_tokens::GenesisConfig:: { + endowed_accounts: self.endowed_accounts, + } + .assimilate_storage(&mut t) + .unwrap(); + + t.into() + } +} diff --git a/xtokens/src/tests.rs b/xtokens/src/tests.rs new file mode 100644 index 000000000..13a172417 --- /dev/null +++ b/xtokens/src/tests.rs @@ -0,0 +1,12 @@ +//! Unit tests for the xtokens module. + +#![cfg(test)] + +use super::*; +use frame_support::{assert_noop, assert_ok}; +use mock::*; + +#[test] +fn transfer_to_relay_chain_works() { + ExtBuilder::default().build().execute_with(|| {}); +} From b0b25c6c2e437a0eec38e43e04f198c90264a977 Mon Sep 17 00:00:00 2001 From: Shaopeng Wang Date: Wed, 2 Sep 2020 16:22:48 +1200 Subject: [PATCH 07/13] XCMP and upward message mocks. --- xtokens/src/lib.rs | 2 +- xtokens/src/mock.rs | 29 +++++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/xtokens/src/lib.rs b/xtokens/src/lib.rs index ed762df01..b5ad40afa 100644 --- a/xtokens/src/lib.rs +++ b/xtokens/src/lib.rs @@ -45,7 +45,7 @@ pub struct XCurrencyId { currency_id: Vec, } -#[derive(Encode, Decode, RuntimeDebug)] +#[derive(Encode, Decode, Eq, PartialEq, Clone, RuntimeDebug)] pub enum XCMPTokenMessage { /// Token transfer. [x_currency_id, para_id, dest, amount] Transfer(XCurrencyId, ParaId, AccountId, Balance), diff --git a/xtokens/src/mock.rs b/xtokens/src/mock.rs index 10452876f..12debc074 100644 --- a/xtokens/src/mock.rs +++ b/xtokens/src/mock.rs @@ -125,17 +125,26 @@ impl Trait for Runtime { type UpwardMessageSender = MockUpwardMessageSender; type UpwardMessage = MockUpwardMessage; } - pub type XTokens = Module; +thread_local! { + static XCMP_MESSAGES: RefCell)>> = RefCell::new(None); +} + pub struct MockXCMPMessageSender; +impl MockXCMPMessageSender { + pub fn is_msg_sent(dest: ParaId, msg: XCMPTokenMessage) -> bool { + XCMP_MESSAGES.with(|v| v.borrow().clone()) == Some((dest, msg)) + } +} impl XCMPMessageSender> for MockXCMPMessageSender { - fn send_xcmp_message(_dest: ParaId, _msg: &XCMPTokenMessage) -> Result<(), ()> { + fn send_xcmp_message(dest: ParaId, msg: &XCMPTokenMessage) -> Result<(), ()> { + XCMP_MESSAGES.with(|v| *v.borrow_mut() = Some((dest, msg.clone()))); Ok(()) } } -#[derive(Encode, Decode)] +#[derive(Encode, Decode, Eq, PartialEq, Clone)] pub struct MockUpwardMessage(AccountId, Balance); impl BalancesMessage for MockUpwardMessage { fn transfer(dest: AccountId, amount: Balance) -> Self { @@ -143,9 +152,18 @@ impl BalancesMessage for MockUpwardMessage { } } +thread_local! { + static UPWARD_MESSAGES: RefCell> = RefCell::new(None); +} pub struct MockUpwardMessageSender; +impl MockUpwardMessageSender { + pub fn is_msg_sent(msg: MockUpwardMessage) -> bool { + UPWARD_MESSAGES.with(|v| v.borrow().clone()) == Some(msg) + } +} impl UpwardMessageSender for MockUpwardMessageSender { - fn send_upward_message(_msg: &MockUpwardMessage, _origin: UpwardMessageOrigin) -> Result<(), ()> { + fn send_upward_message(msg: &MockUpwardMessage, _origin: UpwardMessageOrigin) -> Result<(), ()> { + UPWARD_MESSAGES.with(|v| *v.borrow_mut() = Some(msg.clone())); Ok(()) } } @@ -189,6 +207,9 @@ impl ExtBuilder { .assimilate_storage(&mut t) .unwrap(); + XCMP_MESSAGES.with(|v| *v.borrow_mut() = None); + UPWARD_MESSAGES.with(|v| *v.borrow_mut() = None); + t.into() } } From c4706243e9de8f6e26905e14297b1fcb31926661 Mon Sep 17 00:00:00 2001 From: Shaopeng Wang Date: Wed, 2 Sep 2020 17:55:22 +1200 Subject: [PATCH 08/13] Add unit tests for transfer. --- xtokens/src/lib.rs | 18 +++-- xtokens/src/mock.rs | 39 +++++++++- xtokens/src/tests.rs | 172 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 219 insertions(+), 10 deletions(-) diff --git a/xtokens/src/lib.rs b/xtokens/src/lib.rs index b5ad40afa..20afa05b8 100644 --- a/xtokens/src/lib.rs +++ b/xtokens/src/lib.rs @@ -40,9 +40,16 @@ pub enum ChainId { pub struct XCurrencyId { /// The owner chain of the currency. For instance, the owner chain of DOT is /// Polkadot. - chain_id: ChainId, + pub chain_id: ChainId, /// The identity of the currency. - currency_id: Vec, + pub currency_id: Vec, +} + +#[cfg(test)] +impl XCurrencyId { + pub fn new(chain_id: ChainId, currency_id: Vec) -> Self { + XCurrencyId { chain_id, currency_id } + } } #[derive(Encode, Decode, Eq, PartialEq, Clone, RuntimeDebug)] @@ -85,7 +92,7 @@ pub trait Trait: frame_system::Trait { decl_storage! { trait Store for Module as XTokens { /// Balances of currencies not known to self parachain. - UnknownBalances: double_map hasher(blake2_128_concat) T::AccountId, hasher(blake2_128_concat) Vec => T::Balance; + pub UnknownBalances get(fn unknown_balances): double_map hasher(blake2_128_concat) T::AccountId, hasher(blake2_128_concat) Vec => T::Balance; } } @@ -260,8 +267,9 @@ impl Module { T::Currency::withdraw(currency_id, src, amount)?; } else { // Unknown currency, update balance. - UnknownBalances::::try_mutate(src, &x_currency_id.currency_id, |total| { - total.checked_sub(&amount).ok_or(Error::::InsufficientBalance) + UnknownBalances::::try_mutate(src, &x_currency_id.currency_id, |total| -> DispatchResult { + *total = total.checked_sub(&amount).ok_or(Error::::InsufficientBalance)?; + Ok(()) })?; } diff --git a/xtokens/src/mock.rs b/xtokens/src/mock.rs index 12debc074..6346ff13c 100644 --- a/xtokens/src/mock.rs +++ b/xtokens/src/mock.rs @@ -67,6 +67,7 @@ impl frame_system::Trait for Runtime { type BaseCallFilter = (); type SystemWeightInfo = (); } +pub type System = system::Module; #[repr(u8)] #[derive(Encode, Decode, Serialize, Deserialize, Eq, PartialEq, Copy, Clone, RuntimeDebug, PartialOrd, Ord)] @@ -98,6 +99,10 @@ impl TryFrom> for CurrencyId { } } +pub fn unknown_currency_id() -> Vec { + vec![10] +} + impl orml_tokens::Trait for Runtime { type Event = TestEvent; type Balance = Balance; @@ -133,7 +138,7 @@ thread_local! { pub struct MockXCMPMessageSender; impl MockXCMPMessageSender { - pub fn is_msg_sent(dest: ParaId, msg: XCMPTokenMessage) -> bool { + pub fn msg_sent(dest: ParaId, msg: XCMPTokenMessage) -> bool { XCMP_MESSAGES.with(|v| v.borrow().clone()) == Some((dest, msg)) } } @@ -145,7 +150,7 @@ impl XCMPMessageSender> for MockXCMPMessage } #[derive(Encode, Decode, Eq, PartialEq, Clone)] -pub struct MockUpwardMessage(AccountId, Balance); +pub struct MockUpwardMessage(pub AccountId, pub Balance); impl BalancesMessage for MockUpwardMessage { fn transfer(dest: AccountId, amount: Balance) -> Self { MockUpwardMessage(dest, amount) @@ -157,7 +162,7 @@ thread_local! { } pub struct MockUpwardMessageSender; impl MockUpwardMessageSender { - pub fn is_msg_sent(msg: MockUpwardMessage) -> bool { + pub fn msg_sent(msg: MockUpwardMessage) -> bool { UPWARD_MESSAGES.with(|v| v.borrow().clone()) == Some(msg) } } @@ -178,6 +183,26 @@ impl Convert for BalanceConvertor { pub const ALICE: AccountId = 1; pub const BOB: AccountId = 2; +pub const PARA_ONE_ID: u32 = 1; + +pub fn para_one_id() -> ParaId { + PARA_ONE_ID.into() +} + +pub fn para_one_account() -> AccountId { + para_one_id().into_account() +} + +pub const PARA_TWO_ID: u32 = 2; + +pub fn para_two_id() -> ParaId { + PARA_TWO_ID.into() +} + +pub fn para_two_account() -> AccountId { + para_two_id().into_account() +} + pub struct ExtBuilder { endowed_accounts: Vec<(AccountId, CurrencyId, Balance)>, } @@ -196,6 +221,14 @@ impl ExtBuilder { self } + pub fn one_hundred_for_alice(self) -> Self { + self.balances(vec![ + (ALICE, CurrencyId::Owned, 100), + (ALICE, CurrencyId::DOT, 100), + (ALICE, CurrencyId::BTC, 100), + ]) + } + pub fn build(self) -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::default() .build_storage::() diff --git a/xtokens/src/tests.rs b/xtokens/src/tests.rs index 13a172417..fcf6871f9 100644 --- a/xtokens/src/tests.rs +++ b/xtokens/src/tests.rs @@ -3,10 +3,178 @@ #![cfg(test)] use super::*; -use frame_support::{assert_noop, assert_ok}; use mock::*; +use frame_support::{assert_noop, assert_ok}; + #[test] fn transfer_to_relay_chain_works() { - ExtBuilder::default().build().execute_with(|| {}); + ExtBuilder::default().one_hundred_for_alice().build().execute_with(|| { + assert_ok!(XTokens::transfer_to_relay_chain(Origin::signed(ALICE), BOB, 50)); + + assert_eq!(Tokens::free_balance(CurrencyId::DOT, &ALICE), 50); + assert!(MockUpwardMessageSender::msg_sent(MockUpwardMessage(BOB, 50))); + + // let event = + // TestEvent::xtokens(RawEvent::TransferredToRelayChain(ALICE, BOB, + // 50)); assert!(System::events().iter().any(|record| record.event == + // event)); + }); +} + +#[test] +fn transfer_to_relay_chain_fails_if_insufficient_balance() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + XTokens::transfer_to_relay_chain(Origin::signed(ALICE), BOB, 50), + orml_tokens::Error::::BalanceTooLow + ); + }); +} + +#[test] +fn transfer_relay_chain_tokens_to_parachain_works() { + ExtBuilder::default().one_hundred_for_alice().build().execute_with(|| { + let x_currency_id = XCurrencyId::new(ChainId::RelayChain, vec![0]); + assert_ok!(XTokens::transfer_to_parachain( + Origin::signed(ALICE), + x_currency_id.clone(), + para_one_id(), + BOB, + 50 + )); + + assert_eq!(Tokens::free_balance(CurrencyId::DOT, &ALICE), 50); + assert!(MockUpwardMessageSender::msg_sent(MockUpwardMessage( + para_one_account(), + 50 + ))); + assert!(MockXCMPMessageSender::msg_sent( + para_one_id(), + XCMPTokenMessage::Transfer(x_currency_id.clone(), para_one_id(), BOB, 50) + )); + + // let event = + // TestEvent::xtokens(RawEvent::TransferredToParachain(x_currency_id, + // ALICE, para_one_id(), BOB, 50)); assert!(System::events().iter(). + // any(|record| record.event == event)); + }); +} + +#[test] +fn transfer_relay_chain_tokens_to_parachain_fails_if_insufficient_balance() { + ExtBuilder::default().build().execute_with(|| { + let x_currency_id = XCurrencyId::new(ChainId::RelayChain, vec![0]); + assert_noop!( + XTokens::transfer_to_parachain(Origin::signed(ALICE), x_currency_id, para_one_id(), BOB, 50), + orml_tokens::Error::::BalanceTooLow + ); + }); +} + +#[test] +fn transfer_owned_tokens_to_parachain_works() { + ExtBuilder::default().one_hundred_for_alice().build().execute_with(|| { + let x_currency_id = XCurrencyId::new(ChainId::ParaChain(MockParaId::get()), CurrencyId::Owned.into()); + assert_ok!(XTokens::transfer_to_parachain( + Origin::signed(ALICE), + x_currency_id.clone(), + para_one_id(), + BOB, + 50 + )); + + assert_eq!(Tokens::free_balance(CurrencyId::Owned, &ALICE), 50); + assert_eq!(Tokens::free_balance(CurrencyId::Owned, ¶_one_account()), 50); + assert!(MockXCMPMessageSender::msg_sent( + para_one_id(), + XCMPTokenMessage::Transfer(x_currency_id, para_one_id(), BOB, 50) + )); + }); +} + +#[test] +fn transfer_owned_tokens_to_parachain_fails_if_unrecognized_currency_id() { + ExtBuilder::default().one_hundred_for_alice().build().execute_with(|| { + let x_currency_id = XCurrencyId::new(ChainId::ParaChain(MockParaId::get()), unknown_currency_id()); + assert_noop!( + XTokens::transfer_to_parachain(Origin::signed(ALICE), x_currency_id, para_one_id(), BOB, 50), + Error::::InvalidCurrencyId + ); + }); +} + +#[test] +fn transfer_owned_tokens_to_parachain_fails_if_insufficient_balance() { + ExtBuilder::default().build().execute_with(|| { + let x_currency_id = XCurrencyId::new(ChainId::ParaChain(MockParaId::get()), CurrencyId::Owned.into()); + assert_noop!( + XTokens::transfer_to_parachain(Origin::signed(ALICE), x_currency_id, para_one_id(), BOB, 50), + orml_tokens::Error::::BalanceTooLow, + ); + }); +} + +#[test] +fn transfer_known_non_owned_tokens_to_parachain_works() { + ExtBuilder::default().one_hundred_for_alice().build().execute_with(|| { + let x_currency_id = XCurrencyId::new(ChainId::ParaChain(para_one_id()), CurrencyId::BTC.into()); + assert_ok!(XTokens::transfer_to_parachain( + Origin::signed(ALICE), + x_currency_id.clone(), + para_two_id(), + BOB, + 50 + )); + + assert_eq!(Tokens::free_balance(CurrencyId::BTC, &ALICE), 50); + assert!(MockXCMPMessageSender::msg_sent( + para_one_id(), + XCMPTokenMessage::Transfer(x_currency_id, para_two_id(), BOB, 50) + )); + }); +} + +#[test] +fn transfer_known_non_owned_tokens_fails_if_insufficient_balance() { + ExtBuilder::default().build().execute_with(|| { + let x_currency_id = XCurrencyId::new(ChainId::ParaChain(para_one_id()), CurrencyId::BTC.into()); + assert_noop!( + XTokens::transfer_to_parachain(Origin::signed(ALICE), x_currency_id, para_two_id(), BOB, 50), + orml_tokens::Error::::BalanceTooLow + ); + }); +} + +#[test] +fn transfer_unknown_non_owned_tokens_to_parachain_works() { + ExtBuilder::default().one_hundred_for_alice().build().execute_with(|| { + >::insert(ALICE, unknown_currency_id(), 100); + + let x_currency_id = XCurrencyId::new(ChainId::ParaChain(para_one_id()), unknown_currency_id()); + assert_ok!(XTokens::transfer_to_parachain( + Origin::signed(ALICE), + x_currency_id.clone(), + para_two_id(), + BOB, + 50 + )); + + assert_eq!(XTokens::unknown_balances(ALICE, unknown_currency_id()), 50); + assert!(MockXCMPMessageSender::msg_sent( + para_one_id(), + XCMPTokenMessage::Transfer(x_currency_id, para_two_id(), BOB, 50) + )); + }); +} + +#[test] +fn transfer_unknown_non_owned_tokens_fails_if_insufficient_balance() { + ExtBuilder::default().build().execute_with(|| { + let x_currency_id = XCurrencyId::new(ChainId::ParaChain(para_one_id()), unknown_currency_id()); + assert_noop!( + XTokens::transfer_to_parachain(Origin::signed(ALICE), x_currency_id, para_two_id(), BOB, 50), + Error::::InsufficientBalance + ); + }); } From 0c09b0f6891d2d50069df4430fc5c752e461d169 Mon Sep 17 00:00:00 2001 From: Shaopeng Wang Date: Wed, 2 Sep 2020 17:57:31 +1200 Subject: [PATCH 09/13] Return early if transfer to self parachain. --- xtokens/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/xtokens/src/lib.rs b/xtokens/src/lib.rs index 20afa05b8..b9366ce70 100644 --- a/xtokens/src/lib.rs +++ b/xtokens/src/lib.rs @@ -154,8 +154,14 @@ decl_module! { ) { with_transaction_result(|| { let who = ensure_signed(origin)?; + + if para_id == T::ParaId::get() { + return Ok(()); + } + Self::do_transfer_to_parachain(x_currency_id.clone(), &who, para_id, &dest, amount)?; Self::deposit_event(Event::::TransferredToParachain(x_currency_id, who, para_id, dest, amount)); + Ok(()) })?; } From 4ee4877a5f2d71a68f2cd43534909a8d27ac79d2 Mon Sep 17 00:00:00 2001 From: Shaopeng Wang Date: Thu, 3 Sep 2020 11:41:39 +1200 Subject: [PATCH 10/13] XCMP and upward message handling unit tests. --- xtokens/Cargo.toml | 1 + xtokens/src/lib.rs | 32 +++++++--- xtokens/src/tests.rs | 145 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+), 10 deletions(-) diff --git a/xtokens/Cargo.toml b/xtokens/Cargo.toml index 5ffb44308..8b3059596 100644 --- a/xtokens/Cargo.toml +++ b/xtokens/Cargo.toml @@ -29,6 +29,7 @@ polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = [dev-dependencies] sp-core = { version = "2.0.0-rc6", default-features = false } clear_on_drop = { version = "0.2.4", features = ["no_cc"] } # https://github.com/paritytech/substrate/issues/4179 +polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", branch = "rococo-branch" } orml-tokens = { path = "../tokens", version = "0.1.3-dev" } [features] diff --git a/xtokens/src/lib.rs b/xtokens/src/lib.rs index b9366ce70..5529ac139 100644 --- a/xtokens/src/lib.rs +++ b/xtokens/src/lib.rs @@ -230,6 +230,8 @@ impl Module { /// /// 1. Transfer from `src` to `para_id` account. /// 2. Notify `para_id` the transfer. + /// + /// NOTE - `para_id` must not be self parachain. fn transfer_owned_tokens_to_parachain( x_currency_id: XCurrencyId, src: &T::AccountId, @@ -321,17 +323,27 @@ impl XCMPMessageHandler> fo ChainId::ParaChain(token_owner) => { if T::ParaId::get() == token_owner { // Handle owned tokens: - // 1. transfer between para accounts - // 2. notify the `para_id` + // If `para_id` is self parachain: + // 1. Transfer from `src` para account to `dest` account. + // else (`para_id` is not self parachain): + // 1. transfer between para accounts + // 2. notify the `para_id` let src_para_account = src.into_account(); - // Should not fail, but if it does, there is nothing can be done. - let _ = Self::transfer_owned_tokens_to_parachain( - x_currency_id.clone(), - &src_para_account, - *para_id, - dest, - *amount, - ); + if *para_id == T::ParaId::get() { + if let Ok(currency_id) = x_currency_id.currency_id.clone().try_into() { + // Should not fail, but if it does, there is nothing can be done. + let _ = T::Currency::transfer(currency_id, &src_para_account, dest, *amount); + } + } else { + // Should not fail, but if it does, there is nothing can be done. + let _ = Self::transfer_owned_tokens_to_parachain( + x_currency_id.clone(), + &src_para_account, + *para_id, + dest, + *amount, + ); + } } else if let Ok(currency_id) = x_currency_id.currency_id.clone().try_into() { // Handle known tokens. // Should not fail, but if it does, there is nothing can be done. diff --git a/xtokens/src/tests.rs b/xtokens/src/tests.rs index fcf6871f9..88cf0c38a 100644 --- a/xtokens/src/tests.rs +++ b/xtokens/src/tests.rs @@ -90,6 +90,11 @@ fn transfer_owned_tokens_to_parachain_works() { para_one_id(), XCMPTokenMessage::Transfer(x_currency_id, para_one_id(), BOB, 50) )); + + // let event = + // TestEvent::xtokens(RawEvent::TransferredToParachain(x_currency_id, + // ALICE, para_one_id(), BOB, 50)); assert!(System::events().iter(). + // any(|record| record.event == event)); }); } @@ -132,6 +137,11 @@ fn transfer_known_non_owned_tokens_to_parachain_works() { para_one_id(), XCMPTokenMessage::Transfer(x_currency_id, para_two_id(), BOB, 50) )); + + // let event = + // TestEvent::xtokens(RawEvent::TransferredToParachain(x_currency_id, + // ALICE, para_one_id(), BOB, 50)); assert!(System::events().iter(). + // any(|record| record.event == event)); }); } @@ -165,6 +175,11 @@ fn transfer_unknown_non_owned_tokens_to_parachain_works() { para_one_id(), XCMPTokenMessage::Transfer(x_currency_id, para_two_id(), BOB, 50) )); + + // let event = + // TestEvent::xtokens(RawEvent::TransferredToParachain(x_currency_id, + // ALICE, para_one_id(), BOB, 50)); assert!(System::events().iter(). + // any(|record| record.event == event)); }); } @@ -178,3 +193,133 @@ fn transfer_unknown_non_owned_tokens_fails_if_insufficient_balance() { ); }); } + +#[test] +fn handle_downward_message_works() { + ExtBuilder::default().build().execute_with(|| { + let dest: polkadot_core_primitives::AccountId = [0; 32].into(); + let msg = DownwardMessage::TransferInto(dest.clone(), 50, [0; 32]); + XTokens::handle_downward_message(&msg); + + let dest_account = convert_hack(&dest); + assert_eq!(Tokens::free_balance(CurrencyId::DOT, &dest_account), 50); + + // let event = + // TestEvent::xtokens(RawEvent::ReceivedTransferFromRelayChain(ALICE, + // 50)); assert!(System::events().iter().any(|record| record.event == + // event)); + }); +} + +#[test] +fn handle_xcmp_message_works_for_relay_chain_tokens() { + ExtBuilder::default().build().execute_with(|| { + let x_currency_id = XCurrencyId::new(ChainId::RelayChain, vec![0]); + let msg = XCMPTokenMessage::Transfer(x_currency_id.clone(), MockParaId::get(), ALICE, 50); + XTokens::handle_xcmp_message(para_one_id(), &msg); + + assert_eq!(Tokens::free_balance(CurrencyId::DOT, &ALICE), 50); + + // let event = + // TestEvent::xtokens(RawEvent:: + // ReceivedTransferFromParachain(x_currency_id, para_one_id(), ALICE, + // 50)); assert!(System::events().iter().any(|record| record.event == + // event)); + }); +} + +#[test] +fn handle_xcmp_message_works_for_owned_parachain_tokens() { + // transfer from para_one to para_two + ExtBuilder::default() + .balances(vec![(para_one_account(), CurrencyId::Owned, 100)]) + .build() + .execute_with(|| { + let x_currency_id = XCurrencyId::new(ChainId::ParaChain(MockParaId::get()), CurrencyId::Owned.into()); + let msg = XCMPTokenMessage::Transfer(x_currency_id.clone(), para_two_id(), ALICE, 50); + XTokens::handle_xcmp_message(para_one_id(), &msg); + + assert_eq!(Tokens::free_balance(CurrencyId::Owned, ¶_one_account()), 50); + assert_eq!(Tokens::free_balance(CurrencyId::Owned, ¶_two_account()), 50); + + MockXCMPMessageSender::msg_sent(para_two_id(), msg); + + // let event = + // TestEvent::xtokens(RawEvent:: + // ReceivedTransferFromParachain(x_currency_id, para_one_id(), + // ALICE, 50)); assert!(System::events().iter().any(|record| + // record.event == event)); + }); +} + +#[test] +fn handle_xcmp_message_works_for_owned_parachain_tokens_and_self_parachain_as_dest() { + // transfer from para_one to self parachain + ExtBuilder::default() + .balances(vec![(para_one_account(), CurrencyId::Owned, 100)]) + .build() + .execute_with(|| { + let x_currency_id = XCurrencyId::new(ChainId::ParaChain(MockParaId::get()), CurrencyId::Owned.into()); + let msg = XCMPTokenMessage::Transfer(x_currency_id.clone(), MockParaId::get(), ALICE, 50); + XTokens::handle_xcmp_message(para_one_id(), &msg); + + assert_eq!(Tokens::free_balance(CurrencyId::Owned, ¶_one_account()), 50); + assert_eq!(Tokens::free_balance(CurrencyId::Owned, &ALICE), 50); + + // let event = + // TestEvent::xtokens(RawEvent:: + // ReceivedTransferFromParachain(x_currency_id, para_one_id(), + // ALICE, 50)); assert!(System::events().iter().any(|record| + // record.event == event)); + }); +} + +#[test] +fn handle_xcmp_message_works_for_owned_parachain_tokens_with_invalid_currency() { + ExtBuilder::default() + .balances(vec![(para_one_account(), CurrencyId::Owned, 100)]) + .build() + .execute_with(|| { + fn handle() -> sp_std::result::Result<(), ()> { + let x_currency_id = XCurrencyId::new(ChainId::ParaChain(MockParaId::get()), unknown_currency_id()); + let msg = XCMPTokenMessage::Transfer(x_currency_id.clone(), MockParaId::get(), ALICE, 50); + XTokens::handle_xcmp_message(para_one_id(), &msg); + Err(()) + } + assert_noop!(handle(), ()); + }); +} + +#[test] +fn handle_xcmp_message_works_for_non_owned_known_parachain_tokens() { + ExtBuilder::default().build().execute_with(|| { + let x_currency_id = XCurrencyId::new(ChainId::ParaChain(para_one_id()), CurrencyId::BTC.into()); + let msg = XCMPTokenMessage::Transfer(x_currency_id.clone(), MockParaId::get(), ALICE, 50); + XTokens::handle_xcmp_message(para_one_id(), &msg); + + assert_eq!(Tokens::free_balance(CurrencyId::BTC, &ALICE), 50); + + // let event = + // TestEvent::xtokens(RawEvent:: + // ReceivedTransferFromParachain(x_currency_id, para_one_id(), ALICE, + // 50)); assert!(System::events().iter().any(|record| record.event == + // event)); + }); +} + +#[test] +fn handle_xcmp_message_works_for_non_owned_unknown_parachain_tokens() { + ExtBuilder::default().build().execute_with(|| { + let x_currency_id = XCurrencyId::new(ChainId::ParaChain(para_one_id()), unknown_currency_id()); + let msg = XCMPTokenMessage::Transfer(x_currency_id.clone(), MockParaId::get(), ALICE, 50); + XTokens::handle_xcmp_message(para_one_id(), &msg); + + assert_eq!(XTokens::unknown_balances(ALICE, unknown_currency_id()), 50); + + // let event = + // TestEvent::xtokens(RawEvent:: + // ReceivedTransferFromParachain(x_currency_id, para_one_id(), ALICE, + // 50)); assert!(System::events().iter().any(|record| record.event == + // event)); + }); +} From 0e108108774944376a4ba22f3a995a4f625123f1 Mon Sep 17 00:00:00 2001 From: Shaopeng Wang Date: Thu, 3 Sep 2020 12:27:39 +1200 Subject: [PATCH 11/13] Event checks in unit tests. --- xtokens/src/tests.rs | 148 ++++++++++++++++++++++++++++--------------- 1 file changed, 96 insertions(+), 52 deletions(-) diff --git a/xtokens/src/tests.rs b/xtokens/src/tests.rs index 88cf0c38a..d923abe3d 100644 --- a/xtokens/src/tests.rs +++ b/xtokens/src/tests.rs @@ -10,15 +10,15 @@ use frame_support::{assert_noop, assert_ok}; #[test] fn transfer_to_relay_chain_works() { ExtBuilder::default().one_hundred_for_alice().build().execute_with(|| { + System::set_block_number(1); + assert_ok!(XTokens::transfer_to_relay_chain(Origin::signed(ALICE), BOB, 50)); assert_eq!(Tokens::free_balance(CurrencyId::DOT, &ALICE), 50); assert!(MockUpwardMessageSender::msg_sent(MockUpwardMessage(BOB, 50))); - // let event = - // TestEvent::xtokens(RawEvent::TransferredToRelayChain(ALICE, BOB, - // 50)); assert!(System::events().iter().any(|record| record.event == - // event)); + let event = TestEvent::xtokens(RawEvent::TransferredToRelayChain(ALICE, BOB, 50)); + assert!(System::events().iter().any(|record| record.event == event)); }); } @@ -35,6 +35,8 @@ fn transfer_to_relay_chain_fails_if_insufficient_balance() { #[test] fn transfer_relay_chain_tokens_to_parachain_works() { ExtBuilder::default().one_hundred_for_alice().build().execute_with(|| { + System::set_block_number(1); + let x_currency_id = XCurrencyId::new(ChainId::RelayChain, vec![0]); assert_ok!(XTokens::transfer_to_parachain( Origin::signed(ALICE), @@ -54,10 +56,14 @@ fn transfer_relay_chain_tokens_to_parachain_works() { XCMPTokenMessage::Transfer(x_currency_id.clone(), para_one_id(), BOB, 50) )); - // let event = - // TestEvent::xtokens(RawEvent::TransferredToParachain(x_currency_id, - // ALICE, para_one_id(), BOB, 50)); assert!(System::events().iter(). - // any(|record| record.event == event)); + let event = TestEvent::xtokens(RawEvent::TransferredToParachain( + x_currency_id, + ALICE, + para_one_id(), + BOB, + 50, + )); + assert!(System::events().iter().any(|record| record.event == event)); }); } @@ -75,6 +81,8 @@ fn transfer_relay_chain_tokens_to_parachain_fails_if_insufficient_balance() { #[test] fn transfer_owned_tokens_to_parachain_works() { ExtBuilder::default().one_hundred_for_alice().build().execute_with(|| { + System::set_block_number(1); + let x_currency_id = XCurrencyId::new(ChainId::ParaChain(MockParaId::get()), CurrencyId::Owned.into()); assert_ok!(XTokens::transfer_to_parachain( Origin::signed(ALICE), @@ -88,13 +96,17 @@ fn transfer_owned_tokens_to_parachain_works() { assert_eq!(Tokens::free_balance(CurrencyId::Owned, ¶_one_account()), 50); assert!(MockXCMPMessageSender::msg_sent( para_one_id(), - XCMPTokenMessage::Transfer(x_currency_id, para_one_id(), BOB, 50) + XCMPTokenMessage::Transfer(x_currency_id.clone(), para_one_id(), BOB, 50) )); - // let event = - // TestEvent::xtokens(RawEvent::TransferredToParachain(x_currency_id, - // ALICE, para_one_id(), BOB, 50)); assert!(System::events().iter(). - // any(|record| record.event == event)); + let event = TestEvent::xtokens(RawEvent::TransferredToParachain( + x_currency_id, + ALICE, + para_one_id(), + BOB, + 50, + )); + assert!(System::events().iter().any(|record| record.event == event)); }); } @@ -123,6 +135,8 @@ fn transfer_owned_tokens_to_parachain_fails_if_insufficient_balance() { #[test] fn transfer_known_non_owned_tokens_to_parachain_works() { ExtBuilder::default().one_hundred_for_alice().build().execute_with(|| { + System::set_block_number(1); + let x_currency_id = XCurrencyId::new(ChainId::ParaChain(para_one_id()), CurrencyId::BTC.into()); assert_ok!(XTokens::transfer_to_parachain( Origin::signed(ALICE), @@ -135,13 +149,17 @@ fn transfer_known_non_owned_tokens_to_parachain_works() { assert_eq!(Tokens::free_balance(CurrencyId::BTC, &ALICE), 50); assert!(MockXCMPMessageSender::msg_sent( para_one_id(), - XCMPTokenMessage::Transfer(x_currency_id, para_two_id(), BOB, 50) + XCMPTokenMessage::Transfer(x_currency_id.clone(), para_two_id(), BOB, 50) )); - // let event = - // TestEvent::xtokens(RawEvent::TransferredToParachain(x_currency_id, - // ALICE, para_one_id(), BOB, 50)); assert!(System::events().iter(). - // any(|record| record.event == event)); + let event = TestEvent::xtokens(RawEvent::TransferredToParachain( + x_currency_id, + ALICE, + para_two_id(), + BOB, + 50, + )); + assert!(System::events().iter().any(|record| record.event == event)); }); } @@ -159,6 +177,8 @@ fn transfer_known_non_owned_tokens_fails_if_insufficient_balance() { #[test] fn transfer_unknown_non_owned_tokens_to_parachain_works() { ExtBuilder::default().one_hundred_for_alice().build().execute_with(|| { + System::set_block_number(1); + >::insert(ALICE, unknown_currency_id(), 100); let x_currency_id = XCurrencyId::new(ChainId::ParaChain(para_one_id()), unknown_currency_id()); @@ -173,13 +193,17 @@ fn transfer_unknown_non_owned_tokens_to_parachain_works() { assert_eq!(XTokens::unknown_balances(ALICE, unknown_currency_id()), 50); assert!(MockXCMPMessageSender::msg_sent( para_one_id(), - XCMPTokenMessage::Transfer(x_currency_id, para_two_id(), BOB, 50) + XCMPTokenMessage::Transfer(x_currency_id.clone(), para_two_id(), BOB, 50) )); - // let event = - // TestEvent::xtokens(RawEvent::TransferredToParachain(x_currency_id, - // ALICE, para_one_id(), BOB, 50)); assert!(System::events().iter(). - // any(|record| record.event == event)); + let event = TestEvent::xtokens(RawEvent::TransferredToParachain( + x_currency_id, + ALICE, + para_two_id(), + BOB, + 50, + )); + assert!(System::events().iter().any(|record| record.event == event)); }); } @@ -197,6 +221,8 @@ fn transfer_unknown_non_owned_tokens_fails_if_insufficient_balance() { #[test] fn handle_downward_message_works() { ExtBuilder::default().build().execute_with(|| { + System::set_block_number(1); + let dest: polkadot_core_primitives::AccountId = [0; 32].into(); let msg = DownwardMessage::TransferInto(dest.clone(), 50, [0; 32]); XTokens::handle_downward_message(&msg); @@ -204,27 +230,29 @@ fn handle_downward_message_works() { let dest_account = convert_hack(&dest); assert_eq!(Tokens::free_balance(CurrencyId::DOT, &dest_account), 50); - // let event = - // TestEvent::xtokens(RawEvent::ReceivedTransferFromRelayChain(ALICE, - // 50)); assert!(System::events().iter().any(|record| record.event == - // event)); + let event = TestEvent::xtokens(RawEvent::ReceivedTransferFromRelayChain(dest_account, 50)); + assert!(System::events().iter().any(|record| record.event == event)); }); } #[test] fn handle_xcmp_message_works_for_relay_chain_tokens() { ExtBuilder::default().build().execute_with(|| { + System::set_block_number(1); + let x_currency_id = XCurrencyId::new(ChainId::RelayChain, vec![0]); let msg = XCMPTokenMessage::Transfer(x_currency_id.clone(), MockParaId::get(), ALICE, 50); XTokens::handle_xcmp_message(para_one_id(), &msg); assert_eq!(Tokens::free_balance(CurrencyId::DOT, &ALICE), 50); - // let event = - // TestEvent::xtokens(RawEvent:: - // ReceivedTransferFromParachain(x_currency_id, para_one_id(), ALICE, - // 50)); assert!(System::events().iter().any(|record| record.event == - // event)); + let event = TestEvent::xtokens(RawEvent::ReceivedTransferFromParachain( + x_currency_id, + para_one_id(), + ALICE, + 50, + )); + assert!(System::events().iter().any(|record| record.event == event)); }); } @@ -235,6 +263,8 @@ fn handle_xcmp_message_works_for_owned_parachain_tokens() { .balances(vec![(para_one_account(), CurrencyId::Owned, 100)]) .build() .execute_with(|| { + System::set_block_number(1); + let x_currency_id = XCurrencyId::new(ChainId::ParaChain(MockParaId::get()), CurrencyId::Owned.into()); let msg = XCMPTokenMessage::Transfer(x_currency_id.clone(), para_two_id(), ALICE, 50); XTokens::handle_xcmp_message(para_one_id(), &msg); @@ -244,11 +274,13 @@ fn handle_xcmp_message_works_for_owned_parachain_tokens() { MockXCMPMessageSender::msg_sent(para_two_id(), msg); - // let event = - // TestEvent::xtokens(RawEvent:: - // ReceivedTransferFromParachain(x_currency_id, para_one_id(), - // ALICE, 50)); assert!(System::events().iter().any(|record| - // record.event == event)); + let event = TestEvent::xtokens(RawEvent::ReceivedTransferFromParachain( + x_currency_id, + para_one_id(), + ALICE, + 50, + )); + assert!(System::events().iter().any(|record| record.event == event)); }); } @@ -259,6 +291,8 @@ fn handle_xcmp_message_works_for_owned_parachain_tokens_and_self_parachain_as_de .balances(vec![(para_one_account(), CurrencyId::Owned, 100)]) .build() .execute_with(|| { + System::set_block_number(1); + let x_currency_id = XCurrencyId::new(ChainId::ParaChain(MockParaId::get()), CurrencyId::Owned.into()); let msg = XCMPTokenMessage::Transfer(x_currency_id.clone(), MockParaId::get(), ALICE, 50); XTokens::handle_xcmp_message(para_one_id(), &msg); @@ -266,11 +300,13 @@ fn handle_xcmp_message_works_for_owned_parachain_tokens_and_self_parachain_as_de assert_eq!(Tokens::free_balance(CurrencyId::Owned, ¶_one_account()), 50); assert_eq!(Tokens::free_balance(CurrencyId::Owned, &ALICE), 50); - // let event = - // TestEvent::xtokens(RawEvent:: - // ReceivedTransferFromParachain(x_currency_id, para_one_id(), - // ALICE, 50)); assert!(System::events().iter().any(|record| - // record.event == event)); + let event = TestEvent::xtokens(RawEvent::ReceivedTransferFromParachain( + x_currency_id, + para_one_id(), + ALICE, + 50, + )); + assert!(System::events().iter().any(|record| record.event == event)); }); } @@ -293,33 +329,41 @@ fn handle_xcmp_message_works_for_owned_parachain_tokens_with_invalid_currency() #[test] fn handle_xcmp_message_works_for_non_owned_known_parachain_tokens() { ExtBuilder::default().build().execute_with(|| { + System::set_block_number(1); + let x_currency_id = XCurrencyId::new(ChainId::ParaChain(para_one_id()), CurrencyId::BTC.into()); let msg = XCMPTokenMessage::Transfer(x_currency_id.clone(), MockParaId::get(), ALICE, 50); XTokens::handle_xcmp_message(para_one_id(), &msg); assert_eq!(Tokens::free_balance(CurrencyId::BTC, &ALICE), 50); - // let event = - // TestEvent::xtokens(RawEvent:: - // ReceivedTransferFromParachain(x_currency_id, para_one_id(), ALICE, - // 50)); assert!(System::events().iter().any(|record| record.event == - // event)); + let event = TestEvent::xtokens(RawEvent::ReceivedTransferFromParachain( + x_currency_id, + para_one_id(), + ALICE, + 50, + )); + assert!(System::events().iter().any(|record| record.event == event)); }); } #[test] fn handle_xcmp_message_works_for_non_owned_unknown_parachain_tokens() { ExtBuilder::default().build().execute_with(|| { + System::set_block_number(1); + let x_currency_id = XCurrencyId::new(ChainId::ParaChain(para_one_id()), unknown_currency_id()); let msg = XCMPTokenMessage::Transfer(x_currency_id.clone(), MockParaId::get(), ALICE, 50); XTokens::handle_xcmp_message(para_one_id(), &msg); assert_eq!(XTokens::unknown_balances(ALICE, unknown_currency_id()), 50); - // let event = - // TestEvent::xtokens(RawEvent:: - // ReceivedTransferFromParachain(x_currency_id, para_one_id(), ALICE, - // 50)); assert!(System::events().iter().any(|record| record.event == - // event)); + let event = TestEvent::xtokens(RawEvent::ReceivedTransferFromParachain( + x_currency_id, + para_one_id(), + ALICE, + 50, + )); + assert!(System::events().iter().any(|record| record.event == event)); }); } From d197b4246f42d2e9e38b917e10d70125877efeaf Mon Sep 17 00:00:00 2001 From: Shaopeng Wang Date: Thu, 3 Sep 2020 13:48:24 +1200 Subject: [PATCH 12/13] Update GH Action. --- .github/workflows/test.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index eeffe866e..6fa2472bb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,18 +24,23 @@ jobs: - name: Install toolchain uses: actions-rs/toolchain@v1 with: - profile: minimal toolchain: nightly-2020-05-07 - components: rustfmt + components: rustfmt, clippy target: wasm32-unknown-unknown override: true default: true + - name: Show rustc version + run: rustc --version + - name: Show clippy version + run: cargo clippy --version - name: Install Wasm toolchain run: rustup target add wasm32-unknown-unknown - name: Check format run: make dev-format-check - - name: Install clippy - run: rustup component add clippy + - name: TEMP cargo clean + run: cargo clean + - name: TEMP cargo check + run: cargo check --all - name: Run clippy run: cargo clippy -- -D warnings - name: Update From d3650f3dcb890677e9025d66a6c85c27f3944857 Mon Sep 17 00:00:00 2001 From: Shaopeng Wang Date: Wed, 9 Sep 2020 14:17:40 +1200 Subject: [PATCH 13/13] Change RelayChain balance convertor. --- xtokens/Cargo.toml | 2 +- xtokens/src/lib.rs | 13 ++++++++----- xtokens/src/mock.rs | 3 ++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/xtokens/Cargo.toml b/xtokens/Cargo.toml index 8b3059596..2681492ba 100644 --- a/xtokens/Cargo.toml +++ b/xtokens/Cargo.toml @@ -29,7 +29,7 @@ polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = [dev-dependencies] sp-core = { version = "2.0.0-rc6", default-features = false } clear_on_drop = { version = "0.2.4", features = ["no_cc"] } # https://github.com/paritytech/substrate/issues/4179 -polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", branch = "rococo-branch" } +polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", branch = "rococo-branch", default-features = false } orml-tokens = { path = "../tokens", version = "0.1.3-dev" } [features] diff --git a/xtokens/src/lib.rs b/xtokens/src/lib.rs index 5529ac139..f0a897f1a 100644 --- a/xtokens/src/lib.rs +++ b/xtokens/src/lib.rs @@ -64,8 +64,11 @@ pub trait Trait: frame_system::Trait { /// The balance type. type Balance: Parameter + Member + AtLeast32BitUnsigned + Default + Copy + MaybeSerializeDeserialize; - /// Convertor to convert between `T::Balance` and relay chain balance. - type BalanceConvertor: Convert + Convert; + /// Convertor `RelayChainBalance` to `Balance`. + type FromRelayChainBalance: Convert; + + /// Convertor `Balance` to `RelayChainBalance`. + type ToRelayChainBalance: Convert; /// The currency ID type type CurrencyId: Parameter + Member + Copy + MaybeSerializeDeserialize + Ord + Into> + TryFrom>; @@ -171,7 +174,7 @@ decl_module! { impl Module { fn do_transfer_to_relay_chain(who: &T::AccountId, dest: &T::AccountId, amount: T::Balance) -> DispatchResult { T::Currency::withdraw(T::RelayChainCurrencyId::get(), who, amount)?; - let msg = T::UpwardMessage::transfer(dest.clone(), T::BalanceConvertor::convert(amount)); + let msg = T::UpwardMessage::transfer(dest.clone(), T::ToRelayChainBalance::convert(amount)); T::UpwardMessageSender::send_upward_message(&msg, UpwardMessageOrigin::Signed).expect("Should not fail; qed"); Ok(()) } @@ -213,7 +216,7 @@ impl Module { T::Currency::withdraw(T::RelayChainCurrencyId::get(), src, amount)?; - let msg = T::UpwardMessage::transfer(para_account, T::BalanceConvertor::convert(amount)); + let msg = T::UpwardMessage::transfer(para_account, T::ToRelayChainBalance::convert(amount)); T::UpwardMessageSender::send_upward_message(&msg, UpwardMessageOrigin::Signed).expect("Should not fail; qed"); T::XCMPMessageSender::send_xcmp_message( @@ -301,7 +304,7 @@ impl DownwardMessageHandler for Module { fn handle_downward_message(msg: &DownwardMessage) { if let DownwardMessage::TransferInto(dest, amount, _) = msg { let dest: T::AccountId = convert_hack(dest); - let amount: T::Balance = >::convert(*amount); + let amount: T::Balance = T::FromRelayChainBalance::convert(*amount); // Should not fail, but if it does, there is nothing can be done. let _ = T::Currency::deposit(T::RelayChainCurrencyId::get(), &dest, amount); diff --git a/xtokens/src/mock.rs b/xtokens/src/mock.rs index 6346ff13c..189aa6ec0 100644 --- a/xtokens/src/mock.rs +++ b/xtokens/src/mock.rs @@ -121,7 +121,8 @@ parameter_types! { impl Trait for Runtime { type Event = TestEvent; type Balance = Balance; - type BalanceConvertor = BalanceConvertor; + type ToRelayChainBalance = BalanceConvertor; + type FromRelayChainBalance = BalanceConvertor; type CurrencyId = CurrencyId; type RelayChainCurrencyId = RelayChainCurrencyId; type Currency = Tokens;