diff --git a/bridges/README.md b/bridges/README.md index ac3e49b94c6a..3d3b177bbaae 100644 --- a/bridges/README.md +++ b/bridges/README.md @@ -10,6 +10,11 @@ Substrate chains. 🚧 The bridges are currently under construction - a hardhat is recommended beyond this point 🚧 +**IMPORTANT**: this documentation is outdated and it is mostly related to the previous version of our +bridge. Right there's an ongoing work to make our bridge work with XCM messages. Old bridge is still +available at [encoded-calls-messaging](https://github.com/paritytech/parity-bridges-common/releases/tag/encoded-calls-messaging) +tag. + ## Contents - [Installation](#installation) diff --git a/bridges/bin/millau/runtime/Cargo.toml b/bridges/bin/millau/runtime/Cargo.toml index 7fd2db7eee67..80d961388831 100644 --- a/bridges/bin/millau/runtime/Cargo.toml +++ b/bridges/bin/millau/runtime/Cargo.toml @@ -17,17 +17,14 @@ serde = { version = "1.0", optional = true, features = ["derive"] } # Bridge dependencies bp-header-chain = { path = "../../../primitives/header-chain", default-features = false } -bp-message-dispatch = { path = "../../../primitives/message-dispatch", default-features = false } bp-messages = { path = "../../../primitives/messages", default-features = false } bp-millau = { path = "../../../primitives/chain-millau", default-features = false } bp-rialto = { path = "../../../primitives/chain-rialto", default-features = false } bp-runtime = { path = "../../../primitives/runtime", default-features = false } bp-westend = { path = "../../../primitives/chain-westend", default-features = false } bridge-runtime-common = { path = "../../runtime-common", default-features = false } -pallet-bridge-dispatch = { path = "../../../modules/dispatch", default-features = false } pallet-bridge-grandpa = { path = "../../../modules/grandpa", default-features = false } pallet-bridge-messages = { path = "../../../modules/messages", default-features = false } -pallet-bridge-token-swap = { path = "../../../modules/token-swap", default-features = false } pallet-shift-session-manager = { path = "../../../modules/shift-session-manager", default-features = false } # Substrate Dependencies @@ -77,7 +74,6 @@ default = ["std"] std = [ "beefy-primitives/std", "bp-header-chain/std", - "bp-message-dispatch/std", "bp-messages/std", "bp-millau/std", "bp-rialto/std", @@ -93,10 +89,8 @@ std = [ "pallet-balances/std", "pallet-beefy/std", "pallet-beefy-mmr/std", - "pallet-bridge-dispatch/std", "pallet-bridge-grandpa/std", "pallet-bridge-messages/std", - "pallet-bridge-token-swap/std", "pallet-grandpa/std", "pallet-mmr/std", "pallet-randomness-collective-flip/std", @@ -129,6 +123,5 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "libsecp256k1", "pallet-bridge-messages/runtime-benchmarks", - "pallet-bridge-token-swap/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] diff --git a/bridges/bin/millau/runtime/src/lib.rs b/bridges/bin/millau/runtime/src/lib.rs index 99bb8ae33c11..4d44f34b624b 100644 --- a/bridges/bin/millau/runtime/src/lib.rs +++ b/bridges/bin/millau/runtime/src/lib.rs @@ -50,7 +50,7 @@ use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{Block as BlockT, IdentityLookup, Keccak256, NumberFor, OpaqueKeys}, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, FixedPointNumber, FixedU128, MultiSignature, MultiSigner, Perquintill, + ApplyExtrinsicResult, FixedPointNumber, FixedU128, Perquintill, }; use sp_std::prelude::*; #[cfg(feature = "std")] @@ -225,18 +225,6 @@ impl pallet_beefy::Config for Runtime { type BeefyId = BeefyId; } -impl pallet_bridge_dispatch::Config for Runtime { - type Event = Event; - type BridgeMessageId = (bp_messages::LaneId, bp_messages::MessageNonce); - type Call = Call; - type CallFilter = frame_support::traits::Everything; - type EncodedCall = crate::rialto_messages::FromRialtoEncodedCall; - type SourceChainAccountId = bp_rialto::AccountId; - type TargetChainAccountPublic = MultiSigner; - type TargetChainSignature = MultiSignature; - type AccountIdConverter = bp_millau::AccountIdConverter; -} - impl pallet_grandpa::Config for Runtime { type Event = Event; type Call = Call; @@ -471,38 +459,13 @@ impl pallet_bridge_messages::Config for Runtime { GetDeliveryConfirmationTransactionFee, >; type OnMessageAccepted = (); - type OnDeliveryConfirmed = - pallet_bridge_token_swap::Pallet; + type OnDeliveryConfirmed = (); type SourceHeaderChain = crate::rialto_messages::Rialto; type MessageDispatch = crate::rialto_messages::FromRialtoMessageDispatch; type BridgedChainId = RialtoChainId; } -parameter_types! { - pub const TokenSwapMessagesLane: bp_messages::LaneId = *b"swap"; -} - -/// Instance of the with-Rialto token swap pallet. -pub type WithRialtoTokenSwapInstance = (); - -impl pallet_bridge_token_swap::Config for Runtime { - type Event = Event; - type WeightInfo = (); - - type BridgedChainId = RialtoChainId; - type OutboundMessageLaneId = TokenSwapMessagesLane; - #[cfg(not(feature = "runtime-benchmarks"))] - type MessagesBridge = pallet_bridge_messages::Pallet; - #[cfg(feature = "runtime-benchmarks")] - type MessagesBridge = bp_messages::source_chain::NoopMessagesBridge; - type ThisCurrency = pallet_balances::Pallet; - type FromSwapToThisAccountIdConverter = bp_rialto::AccountIdConverter; - - type BridgedChain = bp_rialto::Rialto; - type FromBridgedToThisAccountIdConverter = bp_millau::AccountIdConverter; -} - construct_runtime!( pub enum Runtime where Block = Block, @@ -532,9 +495,7 @@ construct_runtime!( // Rialto bridge modules. BridgeRialtoGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage}, - BridgeDispatch: pallet_bridge_dispatch::{Pallet, Event}, BridgeRialtoMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, - BridgeRialtoTokenSwap: pallet_bridge_token_swap::{Pallet, Call, Storage, Event, Origin}, // Westend bridge modules. BridgeWestendGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Config, Storage}, @@ -806,7 +767,6 @@ impl_runtime_apis! { let mut list = Vec::::new(); - list_benchmark!(list, extra, pallet_bridge_token_swap, BridgeRialtoTokenSwap); list_benchmark!(list, extra, pallet_bridge_messages, MessagesBench::); list_benchmark!(list, extra, pallet_bridge_grandpa, BridgeRialtoGrandpa); @@ -878,8 +838,6 @@ impl_runtime_apis! { ) -> (rialto_messages::FromRialtoMessagesProof, Weight) { prepare_message_proof::( params, - &VERSION, - Balance::MAX / 100, ) } @@ -891,33 +849,11 @@ impl_runtime_apis! { ) } - fn is_message_dispatched(nonce: bp_messages::MessageNonce) -> bool { - frame_system::Pallet::::events() - .into_iter() - .map(|event_record| event_record.event) - .any(|event| matches!( - event, - Event::BridgeDispatch(pallet_bridge_dispatch::Event::::MessageDispatched( - _, ([0, 0, 0, 0], nonce_from_event), _, - )) if nonce_from_event == nonce - )) + fn is_message_dispatched(_nonce: bp_messages::MessageNonce) -> bool { + true } } - use pallet_bridge_token_swap::benchmarking::Config as TokenSwapConfig; - - impl TokenSwapConfig for Runtime { - fn initialize_environment() { - let relayers_fund_account = pallet_bridge_messages::relayer_fund_account_id::< - bp_millau::AccountId, - bp_millau::AccountIdConverter, - >(); - pallet_balances::Pallet::::make_free_balance_be( - &relayers_fund_account, - Balance::MAX / 100, - ); - } - } add_benchmark!( params, @@ -926,37 +862,12 @@ impl_runtime_apis! { MessagesBench:: ); add_benchmark!(params, batches, pallet_bridge_grandpa, BridgeRialtoGrandpa); - add_benchmark!(params, batches, pallet_bridge_token_swap, BridgeRialtoTokenSwap); Ok(batches) } } } -/// Rialto account ownership digest from Millau. -/// -/// The byte vector returned by this function should be signed with a Rialto account private key. -/// This way, the owner of `millau_account_id` on Millau proves that the Rialto account private key -/// is also under his control. -pub fn millau_to_rialto_account_ownership_digest( - rialto_call: &Call, - millau_account_id: AccountId, - rialto_spec_version: SpecVersion, -) -> sp_std::vec::Vec -where - Call: codec::Encode, - AccountId: codec::Encode, - SpecVersion: codec::Encode, -{ - pallet_bridge_dispatch::account_ownership_digest( - rialto_call, - millau_account_id, - rialto_spec_version, - bp_runtime::MILLAU_CHAIN_ID, - bp_runtime::RIALTO_CHAIN_ID, - ) -} - #[cfg(test)] mod tests { use super::*; diff --git a/bridges/bin/millau/runtime/src/rialto_messages.rs b/bridges/bin/millau/runtime/src/rialto_messages.rs index d925d805dd04..9b2c62375714 100644 --- a/bridges/bin/millau/runtime/src/rialto_messages.rs +++ b/bridges/bin/millau/runtime/src/rialto_messages.rs @@ -33,7 +33,7 @@ use frame_support::{ }; use scale_info::TypeInfo; use sp_runtime::{traits::Saturating, FixedPointNumber, FixedU128}; -use sp_std::{convert::TryFrom, ops::RangeInclusive}; +use sp_std::convert::TryFrom; /// Initial value of `RialtoToMillauConversionRate` parameter. pub const INITIAL_RIALTO_TO_MILLAU_CONVERSION_RATE: FixedU128 = @@ -49,19 +49,14 @@ parameter_types! { } /// Message payload for Millau -> Rialto messages. -pub type ToRialtoMessagePayload = - messages::source::FromThisChainMessagePayload; +pub type ToRialtoMessagePayload = messages::source::FromThisChainMessagePayload; /// Message verifier for Millau -> Rialto messages. pub type ToRialtoMessageVerifier = messages::source::FromThisChainMessageVerifier; /// Message payload for Rialto -> Millau messages. -pub type FromRialtoMessagePayload = - messages::target::FromBridgedChainMessagePayload; - -/// Encoded Millau Call as it comes from Rialto. -pub type FromRialtoEncodedCall = messages::target::FromBridgedChainEncodedMessageCall; +pub type FromRialtoMessagePayload = messages::target::FromBridgedChainMessagePayload; /// Messages proof for Rialto -> Millau messages. pub type FromRialtoMessagesProof = messages::target::FromBridgedChainMessagesProof; @@ -120,19 +115,7 @@ impl messages::ThisChainWithMessages for Millau { type Call = crate::Call; fn is_message_accepted(send_origin: &Self::Origin, lane: &LaneId) -> bool { - // lanes 0x00000000 && 0x00000001 are accepting any paid messages, while - // `TokenSwapMessageLane` only accepts messages from token swap pallet - let token_swap_dedicated_lane = crate::TokenSwapMessagesLane::get(); - match *lane { - [0, 0, 0, 0] | [0, 0, 0, 1] => send_origin.linked_account().is_some(), - _ if *lane == token_swap_dedicated_lane => matches!( - send_origin.caller, - crate::OriginCaller::BridgeRialtoTokenSwap( - pallet_bridge_token_swap::RawOrigin::TokenSwap { .. } - ) - ), - _ => false, - } + (*lane == [0, 0, 0, 0] || *lane == [0, 0, 0, 1]) && send_origin.linked_account().is_some() } fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { @@ -189,19 +172,8 @@ impl messages::BridgedChainWithMessages for Rialto { bp_rialto::Rialto::max_extrinsic_size() } - fn message_weight_limits(_message_payload: &[u8]) -> RangeInclusive { - // we don't want to relay too large messages + keep reserve for future upgrades - let upper_limit = messages::target::maximal_incoming_message_dispatch_weight( - bp_rialto::Rialto::max_extrinsic_weight(), - ); - - // we're charging for payload bytes in `WithRialtoMessageBridge::transaction_payment` - // function - // - // this bridge may be used to deliver all kind of messages, so we're not making any - // assumptions about minimal dispatch weight here - - 0..=upper_limit + fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { + true } fn estimate_delivery_transaction( @@ -296,12 +268,6 @@ impl SenderOrigin for crate::Origin { crate::OriginCaller::system(frame_system::RawOrigin::Root) | crate::OriginCaller::system(frame_system::RawOrigin::None) => crate::RootAccountForPayments::get(), - crate::OriginCaller::BridgeRialtoTokenSwap( - pallet_bridge_token_swap::RawOrigin::TokenSwap { - ref swap_account_at_this_chain, - .. - }, - ) => Some(swap_account_at_this_chain.clone()), _ => None, } } diff --git a/bridges/bin/rialto/runtime/Cargo.toml b/bridges/bin/rialto/runtime/Cargo.toml index 99ab1f9a6915..ccfa65090e6d 100644 --- a/bridges/bin/rialto/runtime/Cargo.toml +++ b/bridges/bin/rialto/runtime/Cargo.toml @@ -18,13 +18,11 @@ serde = { version = "1.0", optional = true, features = ["derive"] } # Bridge dependencies bp-header-chain = { path = "../../../primitives/header-chain", default-features = false } -bp-message-dispatch = { path = "../../../primitives/message-dispatch", default-features = false } bp-messages = { path = "../../../primitives/messages", default-features = false } bp-millau = { path = "../../../primitives/chain-millau", default-features = false } bp-rialto = { path = "../../../primitives/chain-rialto", default-features = false } bp-runtime = { path = "../../../primitives/runtime", default-features = false } bridge-runtime-common = { path = "../../runtime-common", default-features = false } -pallet-bridge-dispatch = { path = "../../../modules/dispatch", default-features = false } pallet-bridge-grandpa = { path = "../../../modules/grandpa", default-features = false } pallet-bridge-messages = { path = "../../../modules/messages", default-features = false } pallet-shift-session-manager = { path = "../../../modules/shift-session-manager", default-features = false } @@ -85,7 +83,6 @@ default = ["std"] std = [ "beefy-primitives/std", "bp-header-chain/std", - "bp-message-dispatch/std", "bp-messages/std", "bp-millau/std", "bp-rialto/std", @@ -103,7 +100,6 @@ std = [ "pallet-balances/std", "pallet-beefy/std", "pallet-beefy-mmr/std", - "pallet-bridge-dispatch/std", "pallet-bridge-grandpa/std", "pallet-bridge-messages/std", "pallet-grandpa/std", diff --git a/bridges/bin/rialto/runtime/src/lib.rs b/bridges/bin/rialto/runtime/src/lib.rs index 2334d45f340e..4d2602a20a60 100644 --- a/bridges/bin/rialto/runtime/src/lib.rs +++ b/bridges/bin/rialto/runtime/src/lib.rs @@ -51,7 +51,7 @@ use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{AccountIdLookup, Block as BlockT, Keccak256, NumberFor, OpaqueKeys}, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, FixedPointNumber, FixedU128, MultiSignature, MultiSigner, Perquintill, + ApplyExtrinsicResult, FixedPointNumber, FixedU128, Perquintill, }; use sp_std::{collections::btree_map::BTreeMap, prelude::*}; #[cfg(feature = "std")] @@ -251,18 +251,6 @@ impl pallet_beefy::Config for Runtime { type BeefyId = BeefyId; } -impl pallet_bridge_dispatch::Config for Runtime { - type Event = Event; - type BridgeMessageId = (bp_messages::LaneId, bp_messages::MessageNonce); - type Call = Call; - type CallFilter = frame_support::traits::Everything; - type EncodedCall = crate::millau_messages::FromMillauEncodedCall; - type SourceChainAccountId = bp_millau::AccountId; - type TargetChainAccountPublic = MultiSigner; - type TargetChainSignature = MultiSignature; - type AccountIdConverter = bp_rialto::AccountIdConverter; -} - impl pallet_grandpa::Config for Runtime { type Event = Event; type Call = Call; @@ -505,7 +493,6 @@ construct_runtime!( // Millau bridge modules. BridgeMillauGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage}, - BridgeDispatch: pallet_bridge_dispatch::{Pallet, Event}, BridgeMillauMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, // Parachain modules. @@ -920,30 +907,6 @@ impl_runtime_apis! { } } -/// Millau account ownership digest from Rialto. -/// -/// The byte vector returned by this function should be signed with a Millau account private key. -/// This way, the owner of `rialto_account_id` on Rialto proves that the 'millau' account private -/// key is also under his control. -pub fn rialto_to_millau_account_ownership_digest( - millau_call: &Call, - rialto_account_id: AccountId, - millau_spec_version: SpecVersion, -) -> sp_std::vec::Vec -where - Call: codec::Encode, - AccountId: codec::Encode, - SpecVersion: codec::Encode, -{ - pallet_bridge_dispatch::account_ownership_digest( - millau_call, - rialto_account_id, - millau_spec_version, - bp_runtime::RIALTO_CHAIN_ID, - bp_runtime::MILLAU_CHAIN_ID, - ) -} - #[cfg(test)] mod tests { use super::*; diff --git a/bridges/bin/rialto/runtime/src/millau_messages.rs b/bridges/bin/rialto/runtime/src/millau_messages.rs index 44348383f1d5..8088ee56ac75 100644 --- a/bridges/bin/rialto/runtime/src/millau_messages.rs +++ b/bridges/bin/rialto/runtime/src/millau_messages.rs @@ -33,7 +33,7 @@ use frame_support::{ }; use scale_info::TypeInfo; use sp_runtime::{traits::Saturating, FixedPointNumber, FixedU128}; -use sp_std::{convert::TryFrom, ops::RangeInclusive}; +use sp_std::convert::TryFrom; /// Initial value of `MillauToRialtoConversionRate` parameter. pub const INITIAL_MILLAU_TO_RIALTO_CONVERSION_RATE: FixedU128 = @@ -49,19 +49,14 @@ parameter_types! { } /// Message payload for Rialto -> Millau messages. -pub type ToMillauMessagePayload = - messages::source::FromThisChainMessagePayload; +pub type ToMillauMessagePayload = messages::source::FromThisChainMessagePayload; /// Message verifier for Rialto -> Millau messages. pub type ToMillauMessageVerifier = messages::source::FromThisChainMessageVerifier; /// Message payload for Millau -> Rialto messages. -pub type FromMillauMessagePayload = - messages::target::FromBridgedChainMessagePayload; - -/// Encoded Rialto Call as it comes from Millau. -pub type FromMillauEncodedCall = messages::target::FromBridgedChainEncodedMessageCall; +pub type FromMillauMessagePayload = messages::target::FromBridgedChainMessagePayload; /// Call-dispatch based message dispatch for Millau -> Rialto messages. pub type FromMillauMessageDispatch = messages::target::FromBridgedChainMessageDispatch< @@ -177,19 +172,8 @@ impl messages::BridgedChainWithMessages for Millau { bp_millau::Millau::max_extrinsic_size() } - fn message_weight_limits(_message_payload: &[u8]) -> RangeInclusive { - // we don't want to relay too large messages + keep reserve for future upgrades - let upper_limit = messages::target::maximal_incoming_message_dispatch_weight( - bp_millau::Millau::max_extrinsic_weight(), - ); - - // we're charging for payload bytes in `WithMillauMessageBridge::transaction_payment` - // function - // - // this bridge may be used to deliver all kind of messages, so we're not making any - // assumptions about minimal dispatch weight here - - 0..=upper_limit + fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { + true } fn estimate_delivery_transaction( @@ -308,100 +292,15 @@ impl MessagesParameter for RialtoToMillauMessagesParameter { #[cfg(test)] mod tests { use super::*; - use crate::{ - AccountId, Call, DbWeight, ExistentialDeposit, MillauGrandpaInstance, Runtime, SystemCall, - SystemConfig, WithMillauMessagesInstance, VERSION, - }; - use bp_message_dispatch::CallOrigin; - use bp_messages::{ - target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, - MessageKey, - }; - use bp_runtime::{derive_account_id, messages::DispatchFeePayment, Chain, SourceAccount}; + use crate::{DbWeight, MillauGrandpaInstance, Runtime, WithMillauMessagesInstance}; + use bp_runtime::Chain; use bridge_runtime_common::{ assert_complete_bridge_types, integrity::{ assert_complete_bridge_constants, AssertBridgeMessagesPalletConstants, AssertBridgePalletNames, AssertChainConstants, AssertCompleteBridgeConstants, }, - messages::target::{FromBridgedChainEncodedMessageCall, FromBridgedChainMessagePayload}, }; - use frame_support::{ - traits::Currency, - weights::{GetDispatchInfo, WeightToFeePolynomial}, - }; - use sp_runtime::traits::Convert; - - #[test] - fn transfer_happens_when_dispatch_fee_is_paid_at_target_chain() { - // this test actually belongs to the `bridge-runtime-common` crate, but there we have no - // mock runtime. Making another one there just for this test, given that both crates - // live n single repo is an overkill - let mut ext: sp_io::TestExternalities = - SystemConfig::default().build_storage::().unwrap().into(); - ext.execute_with(|| { - let bridge = MILLAU_CHAIN_ID; - let call: Call = SystemCall::set_heap_pages { pages: 64 }.into(); - let dispatch_weight = call.get_dispatch_info().weight; - let dispatch_fee = ::WeightToFee::calc( - &dispatch_weight, - ); - assert!(dispatch_fee > 0); - - // create relayer account with minimal balance - let relayer_account: AccountId = [1u8; 32].into(); - let initial_amount = ExistentialDeposit::get(); - let _ = as Currency>::deposit_creating( - &relayer_account, - initial_amount, - ); - - // create dispatch account with minimal balance + dispatch fee - let dispatch_account = derive_account_id::< - ::SourceChainAccountId, - >(bridge, SourceAccount::Root); - let dispatch_account = - ::AccountIdConverter::convert( - dispatch_account, - ); - let _ = as Currency>::deposit_creating( - &dispatch_account, - initial_amount + dispatch_fee, - ); - - // dispatch message with intention to pay dispatch fee at the target chain - FromMillauMessageDispatch::dispatch( - &relayer_account, - DispatchMessage { - key: MessageKey { lane_id: Default::default(), nonce: 0 }, - data: DispatchMessageData { - payload: Ok(FromBridgedChainMessagePayload:: { - spec_version: VERSION.spec_version, - weight: dispatch_weight, - origin: CallOrigin::SourceRoot, - dispatch_fee_payment: DispatchFeePayment::AtTargetChain, - call: FromBridgedChainEncodedMessageCall::new(call.encode()), - }), - fee: 1, - }, - }, - ); - - // ensure that fee has been transferred from dispatch to relayer account - assert_eq!( - as Currency>::free_balance( - &relayer_account - ), - initial_amount + dispatch_fee, - ); - assert_eq!( - as Currency>::free_balance( - &dispatch_account - ), - initial_amount, - ); - }); - } #[test] fn ensure_rialto_message_lane_weights_are_correct() { @@ -490,69 +389,4 @@ mod tests { .0, ); } - - #[test] - #[ignore] - fn no_stack_overflow_when_decoding_nested_call_during_dispatch() { - // this test is normally ignored, because it only makes sense to run it in release mode - - let mut ext: sp_io::TestExternalities = - SystemConfig::default().build_storage::().unwrap().into(); - ext.execute_with(|| { - let bridge = MILLAU_CHAIN_ID; - - let mut call: Call = SystemCall::set_heap_pages { pages: 64 }.into(); - - for _i in 0..3000 { - call = Call::Sudo(pallet_sudo::Call::sudo { call: Box::new(call) }); - } - - let dispatch_weight = 500; - let dispatch_fee = ::WeightToFee::calc( - &dispatch_weight, - ); - assert!(dispatch_fee > 0); - - // create relayer account with minimal balance - let relayer_account: AccountId = [1u8; 32].into(); - let initial_amount = ExistentialDeposit::get(); - let _ = as Currency>::deposit_creating( - &relayer_account, - initial_amount, - ); - - // create dispatch account with minimal balance + dispatch fee - let dispatch_account = derive_account_id::< - ::SourceChainAccountId, - >(bridge, SourceAccount::Root); - let dispatch_account = - ::AccountIdConverter::convert( - dispatch_account, - ); - let _ = as Currency>::deposit_creating( - &dispatch_account, - initial_amount + dispatch_fee, - ); - - // dispatch message with intention to pay dispatch fee at the target chain - // - // this is where the stack overflow has happened before the fix has been applied - FromMillauMessageDispatch::dispatch( - &relayer_account, - DispatchMessage { - key: MessageKey { lane_id: Default::default(), nonce: 0 }, - data: DispatchMessageData { - payload: Ok(FromBridgedChainMessagePayload:: { - spec_version: VERSION.spec_version, - weight: dispatch_weight, - origin: CallOrigin::SourceRoot, - dispatch_fee_payment: DispatchFeePayment::AtTargetChain, - call: FromBridgedChainEncodedMessageCall::new(call.encode()), - }), - fee: 1, - }, - }, - ); - }); - } } diff --git a/bridges/bin/runtime-common/Cargo.toml b/bridges/bin/runtime-common/Cargo.toml index 6ca7de5ac43d..7e8609071f86 100644 --- a/bridges/bin/runtime-common/Cargo.toml +++ b/bridges/bin/runtime-common/Cargo.toml @@ -9,17 +9,15 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } -ed25519-dalek = { version = "1.0", default-features = false, optional = true } hash-db = { version = "0.15.2", default-features = false } +log = { version = "0.4.14", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } static_assertions = { version = "1.1", optional = true } # Bridge dependencies -bp-message-dispatch = { path = "../../primitives/message-dispatch", default-features = false } bp-messages = { path = "../../primitives/messages", default-features = false } bp-runtime = { path = "../../primitives/runtime", default-features = false } -pallet-bridge-dispatch = { path = "../../modules/dispatch", default-features = false } pallet-bridge-grandpa = { path = "../../modules/grandpa", default-features = false } pallet-bridge-messages = { path = "../../modules/messages", default-features = false } @@ -40,14 +38,13 @@ sp-version = { git = "https://github.com/paritytech/substrate", branch = "master [features] default = ["std"] std = [ - "bp-message-dispatch/std", "bp-messages/std", "bp-runtime/std", "codec/std", "frame-support/std", "frame-system/std", "hash-db/std", - "pallet-bridge-dispatch/std", + "log/std", "pallet-bridge-grandpa/std", "pallet-bridge-messages/std", "pallet-transaction-payment/std", @@ -60,7 +57,6 @@ std = [ "sp-trie/std", ] runtime-benchmarks = [ - "ed25519-dalek/u64_backend", "pallet-balances", "pallet-bridge-grandpa/runtime-benchmarks", "pallet-bridge-messages/runtime-benchmarks", diff --git a/bridges/bin/runtime-common/README.md b/bridges/bin/runtime-common/README.md deleted file mode 100644 index 5f2298cd787d..000000000000 --- a/bridges/bin/runtime-common/README.md +++ /dev/null @@ -1,181 +0,0 @@ -# Helpers for Messages Module Integration - -The [`messages`](./src/messages.rs) module of this crate contains a bunch of helpers for integrating -messages module into your runtime. Basic prerequisites of these helpers are: -- we're going to bridge Substrate-based chain with another Substrate-based chain; -- both chains have [messages module](../../modules/messages/README.md), Substrate bridge - module and the [call dispatch module](../../modules/dispatch/README.md); -- all message lanes are identical and may be used to transfer the same messages; -- the messages sent over the bridge are dispatched using - [call dispatch module](../../modules/dispatch/README.md); -- the messages are `bp_message_dispatch::MessagePayload` structures, where `call` field is - encoded `Call` of the target chain. This means that the `Call` is opaque to the - [messages module](../../modules/messages/README.md) instance at the source chain. - It is pre-encoded by the message submitter; -- all proofs in the [messages module](../../modules/messages/README.md) transactions are - based on the storage proofs from the bridged chain: storage proof of the outbound message (value - from the `pallet_bridge_messages::Store::MessagePayload` map), storage proof of the outbound lane - state (value from the `pallet_bridge_messages::Store::OutboundLanes` map) and storage proof of the - inbound lane state (value from the `pallet_bridge_messages::Store::InboundLanes` map); -- storage proofs are built at the finalized headers of the corresponding chain. So all message lane - transactions with proofs are verifying storage proofs against finalized chain headers from - Substrate bridge module. - -**IMPORTANT NOTE**: after reading this document, you may refer to our test runtimes -([rialto_messages.rs](../millau/runtime/src/rialto_messages.rs) and/or -[millau_messages.rs](../rialto/runtime/src/millau_messages.rs)) to see how to use these helpers. - -## Contents -- [`MessageBridge` Trait](#messagebridge-trait) -- [`ChainWithMessages` Trait ](#ChainWithMessages-trait) -- [Helpers for the Source Chain](#helpers-for-the-source-chain) -- [Helpers for the Target Chain](#helpers-for-the-target-chain) - -## `MessageBridge` Trait - -The essence of your integration will be a struct that implements a `MessageBridge` trait. It has -single method (`MessageBridge::bridged_balance_to_this_balance`), used to convert from bridged chain -tokens into this chain tokens. The bridge also requires two associated types to be specified - -`ThisChain` and `BridgedChain`. - -Worth to say that if you're going to use hardcoded constant (conversion rate) in the -`MessageBridge::bridged_balance_to_this_balance` method (or in any other method of -`ThisChainWithMessages` or `BridgedChainWithMessages` traits), then you should take a -look at the -[messages parameters functionality](../../modules/messages/README.md#Non-Essential-Functionality). -They allow pallet owner to update constants more frequently than runtime upgrade happens. - -## `ChainWithMessages` Trait - -The trait is quite simple and can easily be implemented - you just need to specify types used at the -corresponding chain. There is single exception, though (it may be changed in the future): - -- `ChainWithMessages::MessagesInstance`: this is used to compute runtime storage keys. There - may be several instances of messages pallet, included in the Runtime. Every instance stores - messages and these messages stored under different keys. When we are verifying storage proofs from - the bridged chain, we should know which instance we're talking to. This is fine, but there's - significant inconvenience with that - this chain runtime must have the same messages pallet - instance. This does not necessarily mean that we should use the same instance on both chains - - this instance may be used to bridge with another chain/instance, or may not be used at all. - -## `ThisChainWithMessages` Trait - -This trait represents this chain from bridge point of view. Let's review every method of this trait: - -- `ThisChainWithMessages::is_message_accepted`: is used to check whether given lane accepts - messages. The send-message origin is passed to the function, so you may e.g. verify that only - given pallet is able to send messages over selected lane. **IMPORTANT**: if you assume that the - message must be paid by the sender, you must ensure that the sender origin has linked the account - for paying message delivery and dispatch fee. - -- `ThisChainWithMessages::maximal_pending_messages_at_outbound_lane`: you should return maximal - number of pending (undelivered) messages from this function. Returning small values would require - relayers to operate faster and could make message sending logic more complicated. On the other - hand, returning large values could lead to chain state growth. - -- `ThisChainWithMessages::estimate_delivery_confirmation_transaction`: you'll need to return - estimated size and dispatch weight of the delivery confirmation transaction (that happens on - this chain) from this function. - -- `ThisChainWithMessages::transaction_payment`: you'll need to return fee that the submitter - must pay for given transaction on this chain. Normally, you would use transaction payment pallet - for this. However, if your chain has non-zero fee multiplier set, this would mean that the - payment will be computed using current value of this multiplier. But since this transaction - will be submitted in the future, you may want to choose other value instead. Otherwise, - non-altruistic relayer may choose not to submit this transaction until number of transactions - will decrease. - -## `BridgedChainWithMessages` Trait - -This trait represents this chain from bridge point of view. Let's review every method of this trait: - -- `BridgedChainWithMessages::maximal_extrinsic_size`: you will need to return the maximal - extrinsic size of the target chain from this function. - -- `MessageBridge::message_weight_limits`: you'll need to return a range of - dispatch weights that the outbound message may take at the target chain. Please keep in mind that - our helpers assume that the message is an encoded call of the target chain. But we never decode - this call at the source chain. So you can't simply get dispatch weight from pre-dispatch - information. Instead there are two options to prepare this range: if you know which calls are to - be sent over your bridge, then you may just return weight ranges for these particular calls. - Otherwise, if you're going to accept all kinds of calls, you may just return range `[0; maximal - incoming message dispatch weight]`. If you choose the latter, then you shall remember that the - delivery transaction itself has some weight, so you can't accept messages with weight equal to - maximal weight of extrinsic at the target chain. In our test chains, we reject all messages that - have declared dispatch weight larger than 50% of the maximal bridged extrinsic weight. - -- `MessageBridge::estimate_delivery_transaction`: you will need to return estimated dispatch weight and - size of the delivery transaction that delivers a given message to the target chain. The transaction - weight must or must not include the weight of pay-dispatch-fee operation, depending on the value - of `include_pay_dispatch_fee_cost` argument. - -- `MessageBridge::transaction_payment`: you'll need to return fee that the submitter - must pay for given transaction on bridged chain. The best case is when you have the same conversion - formula on both chains - then you may just reuse the `ThisChainWithMessages::transaction_payment` - implementation. Otherwise, you'll need to hardcode this formula into your runtime. - -## Helpers for the Source Chain - -The helpers for the Source Chain reside in the `source` submodule of the -[`messages`](./src/messages.rs) module. The structs are: `FromThisChainMessagePayload`, -`FromBridgedChainMessagesDeliveryProof`, `FromThisChainMessageVerifier`. And the helper functions -are: `maximal_message_size`, `verify_chain_message`, `verify_messages_delivery_proof` and -`estimate_message_dispatch_and_delivery_fee`. - -`FromThisChainMessagePayload` is a message that the sender sends through our bridge. It is the -`bp_message_dispatch::MessagePayload`, where `call` field is encoded target chain call. So -at this chain we don't see internals of this call - we just know its size. - -`FromThisChainMessageVerifier` is an implementation of `bp_messages::LaneMessageVerifier`. It -has following checks in its `verify_message` method: - -1. it'll verify that the used outbound lane is enabled in our runtime; - -1. it'll reject messages if there are too many undelivered outbound messages at this lane. The - sender need to wait while relayers will do their work before sending the message again; - -1. it'll reject a message if it has the wrong dispatch origin declared. Like if the submitter is not - the root of this chain, but it tries to dispatch the message at the target chain using - `bp_message_dispatch::CallOrigin::SourceRoot` origin. Or he has provided wrong signature - in the `bp_message_dispatch::CallOrigin::TargetAccount` origin; - -1. it'll reject a message if the delivery and dispatch fee that the submitter wants to pay is lesser - than the fee that is computed using the `estimate_message_dispatch_and_delivery_fee` function. - -`estimate_message_dispatch_and_delivery_fee` returns a minimal fee that the submitter needs to pay -for sending a given message. The fee includes: payment for the delivery transaction at the target -chain, payment for delivery confirmation transaction on this chain, payment for `Call` dispatch at -the target chain and relayer interest. - -`FromBridgedChainMessagesDeliveryProof` holds the lane identifier and the storage proof of this -inbound lane state at the bridged chain. This also holds the hash of the target chain header, that -was used to generate this storage proof. The proof is verified by the -`verify_messages_delivery_proof`, which simply checks that the target chain header is finalized -(using Substrate bridge module) and then reads the inbound lane state from the proof. - -`verify_chain_message` function checks that the message may be delivered to the bridged chain. There -are two main checks: - -1. that the message size is less than or equal to the `2/3` of maximal extrinsic size at the target - chain. We leave `1/3` for signed extras and for the storage proof overhead; - -1. that the message dispatch weight is less than or equal to the `1/2` of maximal normal extrinsic - weight at the target chain. We leave `1/2` for the delivery transaction overhead. - -## Helpers for the Target Chain - -The helpers for the target chain reside in the `target` submodule of the -[`messages`](./src/messages.rs) module. The structs are: `FromBridgedChainMessagePayload`, -`FromBridgedChainMessagesProof`, `FromBridgedChainMessagesProof`. And the helper functions are: -`maximal_incoming_message_dispatch_weight`, `maximal_incoming_message_size` and -`verify_messages_proof`. - -`FromBridgedChainMessagePayload` corresponds to the `FromThisChainMessagePayload` at the bridged -chain. We expect that messages with this payload are stored in the `OutboundMessages` storage map of -the [messages module](../../modules/messages/README.md). This map is used to build -`FromBridgedChainMessagesProof`. The proof holds the lane id, range of message nonces included in -the proof, storage proof of `OutboundMessages` entries and the hash of bridged chain header that has -been used to build the proof. Additionally, there's storage proof may contain the proof of outbound -lane state. It may be required to prune `relayers` entries at this chain (see -[messages module documentation](../../modules/messages/README.md#What-about-other-Constants-in-the-Messages-Module-Configuration-Trait) -for details). This proof is verified by the `verify_messages_proof` function. diff --git a/bridges/bin/runtime-common/src/integrity.rs b/bridges/bin/runtime-common/src/integrity.rs index ab517566a0fe..603f385f29ed 100644 --- a/bridges/bin/runtime-common/src/integrity.rs +++ b/bridges/bin/runtime-common/src/integrity.rs @@ -120,10 +120,10 @@ macro_rules! assert_bridge_messages_pallet_types( use pallet_bridge_messages::Config as MessagesConfig; use static_assertions::assert_type_eq_all; - assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundPayload, FromThisChainMessagePayload<$bridge>); + assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundPayload, FromThisChainMessagePayload); assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundMessageFee, BalanceOf>); - assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundPayload, FromBridgedChainMessagePayload<$bridge>); + assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundPayload, FromBridgedChainMessagePayload); assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundMessageFee, BalanceOf>); assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundRelayer, AccountIdOf>); diff --git a/bridges/bin/runtime-common/src/messages.rs b/bridges/bin/runtime-common/src/messages.rs index 6bcaae4d3657..f7eb0b2975d6 100644 --- a/bridges/bin/runtime-common/src/messages.rs +++ b/bridges/bin/runtime-common/src/messages.rs @@ -20,32 +20,21 @@ //! pallet is used to dispatch incoming messages. Message identified by a tuple //! of to elements - message lane id and message nonce. -use bp_message_dispatch::MessageDispatch as _; use bp_messages::{ source_chain::LaneMessageVerifier, target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages}, InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce, OutboundLaneData, }; -use bp_runtime::{ - messages::{DispatchFeePayment, MessageDispatchResult}, - ChainId, Size, StorageProofChecker, -}; -use codec::{Decode, DecodeLimit, Encode}; -use frame_support::{ - traits::{Currency, ExistenceRequirement}, - weights::{Weight, WeightToFeePolynomial}, - RuntimeDebug, -}; +use bp_runtime::{messages::MessageDispatchResult, ChainId, Size, StorageProofChecker}; +use codec::{Decode, Encode}; +use frame_support::{traits::Currency, weights::Weight, RuntimeDebug}; use hash_db::Hasher; use scale_info::TypeInfo; use sp_runtime::{ - traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedDiv, CheckedMul, Saturating, Zero}, + traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedDiv, CheckedMul, Saturating}, FixedPointNumber, FixedPointOperand, FixedU128, }; -use sp_std::{ - cmp::PartialOrd, convert::TryFrom, fmt::Debug, marker::PhantomData, ops::RangeInclusive, - vec::Vec, -}; +use sp_std::{cmp::PartialOrd, convert::TryFrom, fmt::Debug, marker::PhantomData, vec::Vec}; use sp_trie::StorageProof; /// Bidirectional message bridge. @@ -136,16 +125,9 @@ pub trait BridgedChainWithMessages: ChainWithMessages { /// Maximal extrinsic size at Bridged chain. fn maximal_extrinsic_size() -> u32; - /// Returns feasible weights range for given message payload at the Bridged chain. - /// - /// If message is being sent with the weight that is out of this range, then it - /// should be rejected. - /// - /// Weights returned from this function shall not include transaction overhead - /// (like weight of signature and signed extensions verification), because they're - /// already accounted by the `weight_of_delivery_transaction`. So this function should - /// return pure call dispatch weights range. - fn message_weight_limits(message_payload: &[u8]) -> RangeInclusive; + /// Returns `true` if message dispatch weight is withing expected limits. `false` means + /// that the message is too heavy to be sent over the bridge and shall be rejected. + fn verify_dispatch_weight(message_payload: &[u8]) -> bool; /// Estimate size and weight of single message delivery transaction at the Bridged chain. fn estimate_delivery_transaction( @@ -218,12 +200,7 @@ pub mod source { pub type BridgedChainOpaqueCall = Vec; /// Message payload for This -> Bridged chain messages. - pub type FromThisChainMessagePayload = bp_message_dispatch::MessagePayload< - AccountIdOf>, - SignerOf>, - SignatureOf>, - BridgedChainOpaqueCall, - >; + pub type FromThisChainMessagePayload = Vec; /// Messages delivery proof from bridged chain: /// @@ -260,7 +237,6 @@ pub mod source { /// This verifier assumes following: /// /// - all message lanes are equivalent, so all checks are the same; - /// - messages are being dispatched using `pallet-bridge-dispatch` pallet on the target chain. /// /// Following checks are made: /// @@ -288,7 +264,7 @@ pub mod source { LaneMessageVerifier< OriginOf>, AccountIdOf>, - FromThisChainMessagePayload, + FromThisChainMessagePayload, BalanceOf>, > for FromThisChainMessageVerifier where @@ -305,7 +281,7 @@ pub mod source { delivery_and_dispatch_fee: &BalanceOf>, lane: &LaneId, lane_outbound_data: &OutboundLaneData, - payload: &FromThisChainMessagePayload, + payload: &FromThisChainMessagePayload, ) -> Result<(), Self::Error> { // reject message if lane is blocked if !ThisChain::::is_message_accepted(submitter, lane) { @@ -321,24 +297,6 @@ pub mod source { return Err(TOO_MANY_PENDING_MESSAGES) } - // Do the dispatch-specific check. We assume that the target chain uses - // `Dispatch`, so we verify the message accordingly. - let raw_origin_or_err: Result< - frame_system::RawOrigin>>, - OriginOf>, - > = submitter.clone().into(); - if let Ok(raw_origin) = raw_origin_or_err { - pallet_bridge_dispatch::verify_message_origin(&raw_origin, payload) - .map(drop) - .map_err(|_| BAD_ORIGIN)?; - } else { - // so what it means that we've failed to convert origin to the - // `frame_system::RawOrigin`? now it means that the custom pallet origin has - // been used to send the message. Do we need to verify it? The answer is no, - // because pallet may craft any origin (e.g. root) && we can't verify whether it - // is valid, or not. - }; - let minimal_fee_in_this_tokens = estimate_message_dispatch_and_delivery_fee::( payload, B::RELAYER_FEE_PERCENT, @@ -365,10 +323,9 @@ pub mod source { /// may be 'mined' by the target chain. But the lane may have its own checks (e.g. fee /// check) that would reject message (see `FromThisChainMessageVerifier`). pub fn verify_chain_message( - payload: &FromThisChainMessagePayload, + payload: &FromThisChainMessagePayload, ) -> Result<(), &'static str> { - let weight_limits = BridgedChain::::message_weight_limits(&payload.call); - if !weight_limits.contains(&payload.weight.into()) { + if !BridgedChain::::verify_dispatch_weight(payload) { return Err("Incorrect message weight declared") } @@ -382,7 +339,7 @@ pub mod source { // is enormously large, it should be several dozens/hundreds of bytes. The delivery // transaction also contains signatures and signed extensions. Because of this, we reserve // 1/3 of the the maximal extrinsic weight for this data. - if payload.call.len() > maximal_message_size::() as usize { + if payload.len() > maximal_message_size::() as usize { return Err("The message is too large to be sent over the lane") } @@ -395,7 +352,7 @@ pub mod source { /// The fee is paid in This chain Balance, but we use Bridged chain balance to avoid additional /// conversions. Returns `None` if overflow has happened. pub fn estimate_message_dispatch_and_delivery_fee( - payload: &FromThisChainMessagePayload, + payload: &FromThisChainMessagePayload, relayer_fee_percent: u32, bridged_to_this_conversion_rate: Option, ) -> Result>, &'static str> { @@ -403,13 +360,8 @@ pub mod source { // // if we're going to pay dispatch fee at the target chain, then we don't include weight // of the message dispatch in the delivery transaction cost - let pay_dispatch_fee_at_target_chain = - payload.dispatch_fee_payment == DispatchFeePayment::AtTargetChain; - let delivery_transaction = BridgedChain::::estimate_delivery_transaction( - &payload.encode(), - pay_dispatch_fee_at_target_chain, - if pay_dispatch_fee_at_target_chain { 0.into() } else { payload.weight.into() }, - ); + let delivery_transaction = + BridgedChain::::estimate_delivery_transaction(&payload.encode(), true, 0.into()); let delivery_transaction_fee = BridgedChain::::transaction_payment(delivery_transaction); // the fee (in This tokens) of all transactions that are made on This chain @@ -477,20 +429,8 @@ pub mod source { pub mod target { use super::*; - /// Call origin for Bridged -> This chain messages. - pub type FromBridgedChainMessageCallOrigin = bp_message_dispatch::CallOrigin< - AccountIdOf>, - SignerOf>, - SignatureOf>, - >; - /// Decoded Bridged -> This message payload. - pub type FromBridgedChainMessagePayload = bp_message_dispatch::MessagePayload< - AccountIdOf>, - SignerOf>, - SignatureOf>, - FromBridgedChainEncodedMessageCall>>, - >; + pub type FromBridgedChainMessagePayload = Vec; /// Messages proof from bridged chain: /// @@ -522,35 +462,6 @@ pub mod target { } } - /// Encoded Call of This chain as it is transferred over bridge. - /// - /// Our Call is opaque (`Vec`) for Bridged chain. So it is encoded, prefixed with - /// vector length. Custom decode implementation here is exactly to deal with this. - #[derive(Decode, Encode, RuntimeDebug, PartialEq)] - pub struct FromBridgedChainEncodedMessageCall { - encoded_call: Vec, - _marker: PhantomData, - } - - impl FromBridgedChainEncodedMessageCall { - /// Create encoded call. - pub fn new(encoded_call: Vec) -> Self { - FromBridgedChainEncodedMessageCall { encoded_call, _marker: PhantomData::default() } - } - } - - impl From> - for Result - { - fn from(encoded_call: FromBridgedChainEncodedMessageCall) -> Self { - DecodedCall::decode_with_depth_limit( - sp_api::MAX_EXTRINSIC_DEPTH, - &mut &encoded_call.encoded_call[..], - ) - .map_err(drop) - } - } - /// Dispatching Bridged -> This chain messages. #[derive(RuntimeDebug, Clone, Copy)] pub struct FromBridgedChainMessageDispatch { @@ -563,60 +474,33 @@ pub mod target { where BalanceOf>: Saturating + FixedPointOperand, ThisDispatchInstance: 'static, - ThisRuntime: pallet_bridge_dispatch::Config< - ThisDispatchInstance, - BridgeMessageId = (LaneId, MessageNonce), - > + pallet_transaction_payment::Config, + ThisRuntime: pallet_transaction_payment::Config, ::OnChargeTransaction: pallet_transaction_payment::OnChargeTransaction< ThisRuntime, Balance = BalanceOf>, >, ThisCurrency: Currency>, Balance = BalanceOf>>, - pallet_bridge_dispatch::Pallet: - bp_message_dispatch::MessageDispatch< - AccountIdOf>, - (LaneId, MessageNonce), - Message = FromBridgedChainMessagePayload, - >, { - type DispatchPayload = FromBridgedChainMessagePayload; + type DispatchPayload = FromBridgedChainMessagePayload; fn dispatch_weight( - message: &DispatchMessage>>, + _message: &DispatchMessage>>, ) -> frame_support::weights::Weight { - message.data.payload.as_ref().map(|payload| payload.weight).unwrap_or(0) + 0 } fn dispatch( - relayer_account: &AccountIdOf>, + _relayer_account: &AccountIdOf>, message: DispatchMessage>>, ) -> MessageDispatchResult { let message_id = (message.key.lane_id, message.key.nonce); - pallet_bridge_dispatch::Pallet::::dispatch( - B::BRIDGED_CHAIN_ID, - B::THIS_CHAIN_ID, - message_id, - message.data.payload.map_err(drop), - |dispatch_origin, dispatch_weight| { - let unadjusted_weight_fee = ThisRuntime::WeightToFee::calc(&dispatch_weight); - let fee_multiplier = - pallet_transaction_payment::Pallet::::next_fee_multiplier(); - let adjusted_weight_fee = - fee_multiplier.saturating_mul_int(unadjusted_weight_fee); - if !adjusted_weight_fee.is_zero() { - ThisCurrency::transfer( - dispatch_origin, - relayer_account, - adjusted_weight_fee, - ExistenceRequirement::AllowDeath, - ) - .map_err(drop) - } else { - Ok(()) - } - }, - ) + log::trace!(target: "runtime::bridge-dispatch", "Incoming message {:?}: {:?}", message_id, message.data.payload); + MessageDispatchResult { + dispatch_result: true, + unspent_weight: 0, + dispatch_fee_paid_during_dispatch: false, + } } } @@ -813,7 +697,8 @@ mod tests { const THIS_CHAIN_WEIGHT_TO_BALANCE_RATE: Weight = 2; const BRIDGED_CHAIN_WEIGHT_TO_BALANCE_RATE: Weight = 4; const BRIDGED_CHAIN_TO_THIS_CHAIN_BALANCE_RATE: u32 = 6; - const BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT: Weight = 2048; + const BRIDGED_CHAIN_MIN_EXTRINSIC_WEIGHT: usize = 5; + const BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT: usize = 2048; const BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE: u32 = 1024; /// Bridge that is deployed on ThisChain and allows sending/receiving messages to/from @@ -1016,7 +901,7 @@ mod tests { unreachable!() } - fn message_weight_limits(_message_payload: &[u8]) -> RangeInclusive { + fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { unreachable!() } @@ -1074,10 +959,9 @@ mod tests { BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE } - fn message_weight_limits(message_payload: &[u8]) -> RangeInclusive { - let begin = - std::cmp::min(BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT, message_payload.len() as Weight); - begin..=BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT + fn verify_dispatch_weight(message_payload: &[u8]) -> bool { + message_payload.len() >= BRIDGED_CHAIN_MIN_EXTRINSIC_WEIGHT && + message_payload.len() <= BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT } fn estimate_delivery_transaction( @@ -1102,57 +986,16 @@ mod tests { OutboundLaneData::default() } - #[test] - fn message_from_bridged_chain_is_decoded() { - // the message is encoded on the bridged chain - let message_on_bridged_chain = - source::FromThisChainMessagePayload:: { - spec_version: 1, - weight: 100, - origin: bp_message_dispatch::CallOrigin::SourceRoot, - dispatch_fee_payment: DispatchFeePayment::AtTargetChain, - call: ThisChainCall::Transfer.encode(), - } - .encode(); - - // and sent to this chain where it is decoded - let message_on_this_chain = - target::FromBridgedChainMessagePayload::::decode( - &mut &message_on_bridged_chain[..], - ) - .unwrap(); - assert_eq!( - message_on_this_chain, - target::FromBridgedChainMessagePayload:: { - spec_version: 1, - weight: 100, - origin: bp_message_dispatch::CallOrigin::SourceRoot, - dispatch_fee_payment: DispatchFeePayment::AtTargetChain, - call: target::FromBridgedChainEncodedMessageCall::::new( - ThisChainCall::Transfer.encode(), - ), - } - ); - assert_eq!(Ok(ThisChainCall::Transfer), message_on_this_chain.call.into()); - } - const TEST_LANE_ID: &LaneId = b"test"; const MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE: MessageNonce = 32; - fn regular_outbound_message_payload() -> source::FromThisChainMessagePayload - { - source::FromThisChainMessagePayload:: { - spec_version: 1, - weight: 100, - origin: bp_message_dispatch::CallOrigin::SourceRoot, - dispatch_fee_payment: DispatchFeePayment::AtSourceChain, - call: vec![42], - } + fn regular_outbound_message_payload() -> source::FromThisChainMessagePayload { + vec![42] } #[test] fn message_fee_is_checked_by_verifier() { - const EXPECTED_MINIMAL_FEE: u32 = 5500; + const EXPECTED_MINIMAL_FEE: u32 = 2860; // payload of the This -> Bridged chain message let payload = regular_outbound_message_payload(); @@ -1167,25 +1010,6 @@ mod tests { Ok(ThisChainBalance(EXPECTED_MINIMAL_FEE)), ); - // let's check if estimation is less than hardcoded, if dispatch is paid at target chain - let mut payload_with_pay_on_target = regular_outbound_message_payload(); - payload_with_pay_on_target.dispatch_fee_payment = DispatchFeePayment::AtTargetChain; - let fee_at_source = - source::estimate_message_dispatch_and_delivery_fee::( - &payload_with_pay_on_target, - OnThisChainBridge::RELAYER_FEE_PERCENT, - None, - ) - .expect( - "estimate_message_dispatch_and_delivery_fee failed for pay-at-target-chain message", - ); - assert!( - fee_at_source < EXPECTED_MINIMAL_FEE.into(), - "Computed fee {:?} without prepaid dispatch must be less than the fee with prepaid dispatch {}", - fee_at_source, - EXPECTED_MINIMAL_FEE, - ); - // and now check that the verifier checks the fee assert_eq!( source::FromThisChainMessageVerifier::::verify_message( @@ -1207,80 +1031,6 @@ mod tests { .is_ok(),); } - #[test] - fn should_disallow_root_calls_from_regular_accounts() { - // payload of the This -> Bridged chain message - let payload = source::FromThisChainMessagePayload:: { - spec_version: 1, - weight: 100, - origin: bp_message_dispatch::CallOrigin::SourceRoot, - dispatch_fee_payment: DispatchFeePayment::AtSourceChain, - call: vec![42], - }; - - // and now check that the verifier checks the fee - assert_eq!( - source::FromThisChainMessageVerifier::::verify_message( - &ThisChainOrigin(Ok(frame_system::RawOrigin::Signed(ThisChainAccountId(0)))), - &ThisChainBalance(1_000_000), - TEST_LANE_ID, - &test_lane_outbound_data(), - &payload, - ), - Err(source::BAD_ORIGIN) - ); - assert_eq!( - source::FromThisChainMessageVerifier::::verify_message( - &ThisChainOrigin(Ok(frame_system::RawOrigin::None)), - &ThisChainBalance(1_000_000), - TEST_LANE_ID, - &test_lane_outbound_data(), - &payload, - ), - Err(source::BAD_ORIGIN) - ); - assert!(source::FromThisChainMessageVerifier::::verify_message( - &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)), - &ThisChainBalance(1_000_000), - TEST_LANE_ID, - &test_lane_outbound_data(), - &payload, - ) - .is_ok(),); - } - - #[test] - fn should_verify_source_and_target_origin_matching() { - // payload of the This -> Bridged chain message - let payload = source::FromThisChainMessagePayload:: { - spec_version: 1, - weight: 100, - origin: bp_message_dispatch::CallOrigin::SourceAccount(ThisChainAccountId(1)), - dispatch_fee_payment: DispatchFeePayment::AtSourceChain, - call: vec![42], - }; - - // and now check that the verifier checks the fee - assert_eq!( - source::FromThisChainMessageVerifier::::verify_message( - &ThisChainOrigin(Ok(frame_system::RawOrigin::Signed(ThisChainAccountId(0)))), - &ThisChainBalance(1_000_000), - TEST_LANE_ID, - &test_lane_outbound_data(), - &payload, - ), - Err(source::BAD_ORIGIN) - ); - assert!(source::FromThisChainMessageVerifier::::verify_message( - &ThisChainOrigin(Ok(frame_system::RawOrigin::Signed(ThisChainAccountId(1)))), - &ThisChainBalance(1_000_000), - TEST_LANE_ID, - &test_lane_outbound_data(), - &payload, - ) - .is_ok(),); - } - #[test] fn message_is_rejected_when_sent_using_disabled_lane() { assert_eq!( @@ -1315,58 +1065,42 @@ mod tests { #[test] fn verify_chain_message_rejects_message_with_too_small_declared_weight() { - assert!(source::verify_chain_message::( - &source::FromThisChainMessagePayload:: { - spec_version: 1, - weight: 5, - origin: bp_message_dispatch::CallOrigin::SourceRoot, - dispatch_fee_payment: DispatchFeePayment::AtSourceChain, - call: vec![1, 2, 3, 4, 5, 6], - }, - ) + assert!(source::verify_chain_message::(&vec![ + 42; + BRIDGED_CHAIN_MIN_EXTRINSIC_WEIGHT - + 1 + ]) .is_err()); } #[test] fn verify_chain_message_rejects_message_with_too_large_declared_weight() { - assert!(source::verify_chain_message::( - &source::FromThisChainMessagePayload:: { - spec_version: 1, - weight: BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT + 1, - origin: bp_message_dispatch::CallOrigin::SourceRoot, - dispatch_fee_payment: DispatchFeePayment::AtSourceChain, - call: vec![1, 2, 3, 4, 5, 6], - }, - ) + assert!(source::verify_chain_message::(&vec![ + 42; + BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT - + 1 + ]) .is_err()); } #[test] fn verify_chain_message_rejects_message_too_large_message() { - assert!(source::verify_chain_message::( - &source::FromThisChainMessagePayload:: { - spec_version: 1, - weight: BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT, - origin: bp_message_dispatch::CallOrigin::SourceRoot, - dispatch_fee_payment: DispatchFeePayment::AtSourceChain, - call: vec![0; source::maximal_message_size::() as usize + 1], - }, - ) + assert!(source::verify_chain_message::(&vec![ + 0; + source::maximal_message_size::() + as usize + 1 + ],) .is_err()); } #[test] fn verify_chain_message_accepts_maximal_message() { assert_eq!( - source::verify_chain_message::( - &source::FromThisChainMessagePayload:: { - spec_version: 1, - weight: BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT, - origin: bp_message_dispatch::CallOrigin::SourceRoot, - dispatch_fee_payment: DispatchFeePayment::AtSourceChain, - call: vec![0; source::maximal_message_size::() as _], - }, - ), + source::verify_chain_message::(&vec![ + 0; + source::maximal_message_size::() + as _ + ],), Ok(()), ); } diff --git a/bridges/bin/runtime-common/src/messages_api.rs b/bridges/bin/runtime-common/src/messages_api.rs index b09a88e62795..274d2ec2e00f 100644 --- a/bridges/bin/runtime-common/src/messages_api.rs +++ b/bridges/bin/runtime-common/src/messages_api.rs @@ -16,10 +16,9 @@ //! Helpers for implementing various message-related runtime API mthods. -use crate::messages::{source::FromThisChainMessagePayload, MessageBridge}; +use crate::messages::MessageBridge; use bp_messages::{LaneId, MessageDetails, MessageNonce}; -use codec::Decode; use sp_std::vec::Vec; /// Implementation of the `To*OutboundLaneApi::message_details`. @@ -37,14 +36,12 @@ where .filter_map(|nonce| { let message_data = pallet_bridge_messages::Pallet::::outbound_message_data(lane, nonce)?; - let decoded_payload = - FromThisChainMessagePayload::::decode(&mut &message_data.payload[..]).ok()?; Some(MessageDetails { nonce, - dispatch_weight: decoded_payload.weight, + dispatch_weight: 0, size: message_data.payload.len() as _, delivery_and_dispatch_fee: message_data.fee, - dispatch_fee_payment: decoded_payload.dispatch_fee_payment, + dispatch_fee_payment: bp_runtime::messages::DispatchFeePayment::AtTargetChain, }) }) .collect() diff --git a/bridges/bin/runtime-common/src/messages_benchmarking.rs b/bridges/bin/runtime-common/src/messages_benchmarking.rs index 7c9f50e8ea0c..f789bee9f7b1 100644 --- a/bridges/bin/runtime-common/src/messages_benchmarking.rs +++ b/bridges/bin/runtime-common/src/messages_benchmarking.rs @@ -27,13 +27,8 @@ use crate::messages::{ }; use bp_messages::{storage_keys, MessageData, MessageKey, MessagePayload}; -use bp_runtime::{messages::DispatchFeePayment, ChainId}; use codec::Encode; -use ed25519_dalek::{PublicKey, SecretKey, Signer, KEYPAIR_LENGTH, SECRET_KEY_LENGTH}; -use frame_support::{ - traits::Currency, - weights::{GetDispatchInfo, Weight}, -}; +use frame_support::weights::{GetDispatchInfo, Weight}; use pallet_bridge_messages::benchmarking::{ MessageDeliveryProofParams, MessageParams, MessageProofParams, ProofSize, }; @@ -41,49 +36,16 @@ use sp_core::Hasher; use sp_runtime::traits::{Header, IdentifyAccount, MaybeSerializeDeserialize, Zero}; use sp_std::{fmt::Debug, prelude::*}; use sp_trie::{record_all_keys, trie_types::TrieDBMutV1, LayoutV1, MemoryDB, Recorder, TrieMut}; -use sp_version::RuntimeVersion; - -/// Return this chain account, used to dispatch message. -pub fn dispatch_account() -> AccountIdOf> -where - B: MessageBridge, - SignerOf>: - From + IdentifyAccount>>, -{ - let this_raw_public = PublicKey::from(&dispatch_account_secret()); - let this_public: SignerOf> = - sp_core::ed25519::Public::from_raw(this_raw_public.to_bytes()).into(); - this_public.into_account() -} - -/// Return public key of this chain account, used to dispatch message. -pub fn dispatch_account_secret() -> SecretKey { - // key from the repo example (https://docs.rs/ed25519-dalek/1.0.1/ed25519_dalek/struct.SecretKey.html) - SecretKey::from_bytes(&[ - 157, 097, 177, 157, 239, 253, 090, 096, 186, 132, 074, 244, 146, 236, 044, 196, 068, 073, - 197, 105, 123, 050, 105, 025, 112, 059, 172, 003, 028, 174, 127, 096, - ]) - .expect("harcoded key is valid") -} /// Prepare outbound message for the `send_message` call. pub fn prepare_outbound_message( params: MessageParams>>, -) -> FromThisChainMessagePayload +) -> FromThisChainMessagePayload where B: MessageBridge, BalanceOf>: From, { - let message_payload = vec![0; params.size as usize]; - let dispatch_origin = bp_message_dispatch::CallOrigin::SourceAccount(params.sender_account); - - FromThisChainMessagePayload:: { - spec_version: 0, - weight: params.size as _, - origin: dispatch_origin, - call: message_payload, - dispatch_fee_payment: DispatchFeePayment::AtSourceChain, - } + vec![0; params.size as usize] } /// Prepare proof of messages for the `receive_messages_proof` call. @@ -92,8 +54,6 @@ where /// proof. pub fn prepare_message_proof( params: MessageProofParams, - version: &RuntimeVersion, - endow_amount: BalanceOf>, ) -> (FromBridgedChainMessagesProof>>, Weight) where R: frame_system::Config>> @@ -115,51 +75,10 @@ where + From + IdentifyAccount>>, { - // we'll be dispatching the same call at This chain - let remark = match params.size { + let message_payload = match params.size { ProofSize::Minimal(ref size) => vec![0u8; *size as _], _ => vec![], }; - let call: CallOf> = frame_system::Call::remark { remark }.into(); - let call_weight = call.get_dispatch_info().weight; - - // message payload needs to be signed, because we use `TargetAccount` call origin - // (which is 'heaviest' to verify) - let bridged_account_id: AccountIdOf> = [0u8; 32].into(); - let (this_raw_public, this_raw_signature) = ed25519_sign( - &call, - &bridged_account_id, - version.spec_version, - B::BRIDGED_CHAIN_ID, - B::THIS_CHAIN_ID, - ); - let this_public: SignerOf> = - sp_core::ed25519::Public::from_raw(this_raw_public).into(); - let this_signature: SignatureOf> = - sp_core::ed25519::Signature::from_raw(this_raw_signature).into(); - - // if dispatch fee is paid at this chain, endow relayer account - if params.dispatch_fee_payment == DispatchFeePayment::AtTargetChain { - assert_eq!(this_public.clone().into_account(), dispatch_account::()); - pallet_balances::Pallet::::make_free_balance_be( - &this_public.clone().into_account(), - endow_amount, - ); - } - - // prepare message payload that is stored in the Bridged chain storage - let message_payload = bp_message_dispatch::MessagePayload { - spec_version: version.spec_version, - weight: call_weight, - origin: bp_message_dispatch::CallOrigin::< - AccountIdOf>, - SignerOf>, - SignatureOf>, - >::TargetAccount(bridged_account_id, this_public, this_signature), - dispatch_fee_payment: params.dispatch_fee_payment.clone(), - call: call.encode(), - } - .encode(); // finally - prepare storage proof and update environment let (state_root, storage_proof) = @@ -174,11 +93,7 @@ where nonces_start: *params.message_nonces.start(), nonces_end: *params.message_nonces.end(), }, - call_weight - .checked_mul( - params.message_nonces.end().saturating_sub(*params.message_nonces.start()) + 1, - ) - .expect("too many messages requested by benchmark"), + 0, ) } @@ -312,40 +227,6 @@ where bridged_header_hash } -/// Generate ed25519 signature to be used in -/// `pallet_brdige_call_dispatch::CallOrigin::TargetAccount`. -/// -/// Returns public key of the signer and the signature itself. -fn ed25519_sign( - target_call: &impl Encode, - source_account_id: &impl Encode, - target_spec_version: u32, - source_chain_id: ChainId, - target_chain_id: ChainId, -) -> ([u8; 32], [u8; 64]) { - let target_secret = dispatch_account_secret(); - let target_public: PublicKey = (&target_secret).into(); - - let mut target_pair_bytes = [0u8; KEYPAIR_LENGTH]; - target_pair_bytes[..SECRET_KEY_LENGTH].copy_from_slice(&target_secret.to_bytes()); - target_pair_bytes[SECRET_KEY_LENGTH..].copy_from_slice(&target_public.to_bytes()); - let target_pair = - ed25519_dalek::Keypair::from_bytes(&target_pair_bytes).expect("hardcoded pair is valid"); - - let signature_message = pallet_bridge_dispatch::account_ownership_digest( - target_call, - source_account_id, - target_spec_version, - source_chain_id, - target_chain_id, - ); - let target_origin_signature = target_pair - .try_sign(&signature_message) - .expect("Ed25519 try_sign should not fail in benchmarks"); - - (target_public.to_bytes(), target_origin_signature.to_bytes()) -} - /// Populate trie with dummy keys+values until trie has at least given size. fn grow_trie(mut root: H::Out, mdb: &mut MemoryDB, trie_size: ProofSize) -> H::Out { let (iterations, leaf_size, minimal_trie_size) = match trie_size { diff --git a/bridges/modules/dispatch/Cargo.toml b/bridges/modules/dispatch/Cargo.toml deleted file mode 100644 index c2e8c74da828..000000000000 --- a/bridges/modules/dispatch/Cargo.toml +++ /dev/null @@ -1,42 +0,0 @@ -[package] -name = "pallet-bridge-dispatch" -description = "A Substrate Runtime module that dispatches a bridge message, treating it simply as encoded Call" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2021" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } -log = { version = "0.4.14", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } - -# Bridge dependencies - -bp-message-dispatch = { path = "../../primitives/message-dispatch", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } - -# Substrate Dependencies - -frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } - -[dev-dependencies] -sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } - -[features] -default = ["std"] -std = [ - "bp-message-dispatch/std", - "bp-runtime/std", - "frame-support/std", - "frame-system/std", - "log/std", - "scale-info/std", - "sp-core/std", - "sp-runtime/std", - "sp-std/std", -] diff --git a/bridges/modules/dispatch/README.md b/bridges/modules/dispatch/README.md deleted file mode 100644 index 068ff1167f7d..000000000000 --- a/bridges/modules/dispatch/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# Call Dispatch Module - -The call dispatch module has a single internal (only callable by other runtime modules) entry point -for dispatching encoded calls (`pallet_bridge_dispatch::Module::dispatch`). Every dispatch -(successful or not) emits a corresponding module event. The module doesn't have any call-related -requirements - they may come from the bridged chain over some message lane, or they may be crafted -locally. But in this document we'll mostly talk about this module in the context of bridges. - -Every message that is being dispatched has three main characteristics: -- `bridge` is the 4-bytes identifier of the bridge where this message comes from. This may be the - identifier of the bridged chain (like `b"rlto"` for messages coming from `Rialto`), or the - identifier of the bridge itself (`b"rimi"` for `Rialto` <-> `Millau` bridge); -- `id` is the unique id of the message within the given bridge. For messages coming from the - [messages module](../messages/README.md), it may worth to use a tuple - `(LaneId, MessageNonce)` to identify a message; -- `message` is the `bp_message_dispatch::MessagePayload` structure. The `call` field is set - to the (potentially) encoded `Call` of this chain. - -The easiest way to understand what is happening when a `Call` is being dispatched, is to look at the -module events set: - -- `MessageRejected` event is emitted if a message has been rejected even before it has reached the - module. Dispatch then is called just to reflect the fact that message has been received, but we - have failed to pre-process it (e.g. because we have failed to decode `MessagePayload` structure - from the proof); -- `MessageVersionSpecMismatch` event is emitted if current runtime specification version differs - from the version that has been used to encode the `Call`. The message payload has the - `spec_version`, that is filled by the message submitter. If this value differs from the current - runtime version, dispatch mechanism rejects to dispatch the message. Without this check, we may - decode the wrong `Call` for example if method arguments were changed; -- `MessageCallDecodeFailed` event is emitted if we have failed to decode `Call` from the payload. - This may happen if the submitter has provided incorrect value in the `call` field, or if source - chain storage has been corrupted. The `Call` is decoded after `spec_version` check, so we'll never - try to decode `Call` from other runtime version; -- `MessageSignatureMismatch` event is emitted if submitter has chose to dispatch message using - specified this chain account (`bp_message_dispatch::CallOrigin::TargetAccount` origin), - but he has failed to prove that he owns the private key for this account; -- `MessageCallRejected` event is emitted if the module has been deployed with some call filter and - this filter has rejected the `Call`. In your bridge you may choose to reject all messages except - e.g. balance transfer calls; -- `MessageWeightMismatch` event is emitted if the message submitter has specified invalid `Call` - dispatch weight in the `weight` field of the message payload. The value of this field is compared - to the pre-dispatch weight of the decoded `Call`. If it is less than the actual pre-dispatch - weight, the dispatch is rejected. Keep in mind, that even if post-dispatch weight will be less - than specified, the submitter still have to declare (and pay for) the maximal possible weight - (that is the pre-dispatch weight); -- `MessageDispatchPaymentFailed` event is emitted if the message submitter has selected to pay - dispatch fee at the target chain, but has failed to do that; -- `MessageDispatched` event is emitted if the message has passed all checks and we have actually - dispatched it. The dispatch may still fail, though - that's why we are including the dispatch - result in the event payload. - -When we talk about module in context of bridges, these events are helping in following cases: - -1. when the message submitter has access to the state of both chains and wants to monitor what has - happened with his message. Then he could use the message id (that he gets from the - [messages module events](../messages/README.md#General-Information)) to filter events of - call dispatch module at the target chain and actually see what has happened with his message; - -1. when the message submitter only has access to the source chain state (for example, when sender is - the runtime module at the source chain). In this case, your bridge may have additional mechanism - to deliver dispatch proofs (which are storage proof of module events) back to the source chain, - thus allowing the submitter to see what has happened with his messages. diff --git a/bridges/modules/dispatch/src/lib.rs b/bridges/modules/dispatch/src/lib.rs deleted file mode 100644 index 1e030b733205..000000000000 --- a/bridges/modules/dispatch/src/lib.rs +++ /dev/null @@ -1,1084 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Runtime module which takes care of dispatching messages received over the bridge. -//! -//! The messages are interpreted directly as runtime `Call`. We attempt to decode -//! them and then dispatch as usual. To prevent compatibility issues, the Calls have -//! to include a `spec_version`. This will be checked before dispatch. In the case of -//! a successful dispatch an event is emitted. - -#![cfg_attr(not(feature = "std"), no_std)] -// Generated by `decl_event!` -#![allow(clippy::unused_unit)] - -use bp_message_dispatch::{CallOrigin, MessageDispatch, MessagePayload, SpecVersion}; -use bp_runtime::{ - derive_account_id, - messages::{DispatchFeePayment, MessageDispatchResult}, - ChainId, SourceAccount, -}; -use codec::Encode; -use frame_support::{ - dispatch::Dispatchable, - ensure, - traits::{Contains, Get}, - weights::{extract_actual_weight, GetDispatchInfo}, -}; -use frame_system::RawOrigin; -use sp_runtime::traits::{BadOrigin, Convert, IdentifyAccount, MaybeDisplay, Verify}; -use sp_std::{fmt::Debug, prelude::*}; - -pub use pallet::*; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::config] - pub trait Config: frame_system::Config { - /// The overarching event type. - type Event: From> + IsType<::Event>; - /// Id of the message. Whenever message is passed to the dispatch module, it emits - /// event with this id + dispatch result. Could be e.g. (LaneId, MessageNonce) if - /// it comes from the messages module. - type BridgeMessageId: Parameter; - /// Type of account ID on source chain. - type SourceChainAccountId: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + Ord; - /// Type of account public key on target chain. - type TargetChainAccountPublic: Parameter + IdentifyAccount; - /// Type of signature that may prove that the message has been signed by - /// owner of `TargetChainAccountPublic`. - type TargetChainSignature: Parameter + Verify; - /// The overarching dispatch call type. - type Call: Parameter - + GetDispatchInfo - + Dispatchable< - Origin = ::Origin, - PostInfo = frame_support::dispatch::PostDispatchInfo, - >; - /// Pre-dispatch filter for incoming calls. - /// - /// The pallet will filter all incoming calls right before they're dispatched. If this - /// filter rejects the call, special event (`Event::MessageCallRejected`) is emitted. - type CallFilter: Contains<>::Call>; - /// The type that is used to wrap the `Self::Call` when it is moved over bridge. - /// - /// The idea behind this is to avoid `Call` conversion/decoding until we'll be sure - /// that all other stuff (like `spec_version`) is ok. If we would try to decode - /// `Call` which has been encoded using previous `spec_version`, then we might end - /// up with decoding error, instead of `MessageVersionSpecMismatch`. - type EncodedCall: Decode + Encode + Into>::Call, ()>>; - /// A type which can be turned into an AccountId from a 256-bit hash. - /// - /// Used when deriving target chain AccountIds from source chain AccountIds. - type AccountIdConverter: sp_runtime::traits::Convert; - } - - type BridgeMessageIdOf = >::BridgeMessageId; - - #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] - pub struct Pallet(PhantomData<(T, I)>); - - #[pallet::hooks] - impl, I: 'static> Hooks> for Pallet {} - - #[pallet::call] - impl, I: 'static> Pallet {} - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event, I: 'static = ()> { - /// Message has been rejected before reaching dispatch. - MessageRejected(ChainId, BridgeMessageIdOf), - /// Message has been rejected by dispatcher because of spec version mismatch. - /// Last two arguments are: expected and passed spec version. - MessageVersionSpecMismatch(ChainId, BridgeMessageIdOf, SpecVersion, SpecVersion), - /// Message has been rejected by dispatcher because of weight mismatch. - /// Last two arguments are: expected and passed call weight. - MessageWeightMismatch(ChainId, BridgeMessageIdOf, Weight, Weight), - /// Message signature mismatch. - MessageSignatureMismatch(ChainId, BridgeMessageIdOf), - /// We have failed to decode Call from the message. - MessageCallDecodeFailed(ChainId, BridgeMessageIdOf), - /// The call from the message has been rejected by the call filter. - MessageCallRejected(ChainId, BridgeMessageIdOf), - /// The origin account has failed to pay fee for dispatching the message. - MessageDispatchPaymentFailed( - ChainId, - BridgeMessageIdOf, - ::AccountId, - Weight, - ), - /// Message has been dispatched with given result. - MessageDispatched(ChainId, BridgeMessageIdOf, DispatchResult), - /// Phantom member, never used. Needed to handle multiple pallet instances. - _Dummy(PhantomData), - } -} - -impl, I: 'static> MessageDispatch for Pallet { - type Message = MessagePayload< - T::SourceChainAccountId, - T::TargetChainAccountPublic, - T::TargetChainSignature, - T::EncodedCall, - >; - - fn dispatch_weight(message: &Self::Message) -> bp_message_dispatch::Weight { - message.weight - } - - fn dispatch Result<(), ()>>( - source_chain: ChainId, - target_chain: ChainId, - id: T::BridgeMessageId, - message: Result, - pay_dispatch_fee: P, - ) -> MessageDispatchResult { - // emit special even if message has been rejected by external component - let message = match message { - Ok(message) => message, - Err(_) => { - log::trace!( - target: "runtime::bridge-dispatch", - "Message {:?}/{:?}: rejected before actual dispatch", - source_chain, - id, - ); - Self::deposit_event(Event::MessageRejected(source_chain, id)); - return MessageDispatchResult { - dispatch_result: false, - unspent_weight: 0, - dispatch_fee_paid_during_dispatch: false, - } - }, - }; - - // verify spec version - // (we want it to be the same, because otherwise we may decode Call improperly) - let mut dispatch_result = MessageDispatchResult { - dispatch_result: false, - unspent_weight: message.weight, - dispatch_fee_paid_during_dispatch: false, - }; - let expected_version = ::Version::get().spec_version; - if message.spec_version != expected_version { - log::trace!( - "Message {:?}/{:?}: spec_version mismatch. Expected {:?}, got {:?}", - source_chain, - id, - expected_version, - message.spec_version, - ); - Self::deposit_event(Event::MessageVersionSpecMismatch( - source_chain, - id, - expected_version, - message.spec_version, - )); - return dispatch_result - } - - // now that we have spec version checked, let's decode the call - let call = match message.call.into() { - Ok(call) => call, - Err(_) => { - log::trace!( - target: "runtime::bridge-dispatch", - "Failed to decode Call from message {:?}/{:?}", - source_chain, - id, - ); - Self::deposit_event(Event::MessageCallDecodeFailed(source_chain, id)); - return dispatch_result - }, - }; - - // prepare dispatch origin - let origin_account = match message.origin { - CallOrigin::SourceRoot => { - let hex_id = - derive_account_id::(source_chain, SourceAccount::Root); - let target_id = T::AccountIdConverter::convert(hex_id); - log::trace!(target: "runtime::bridge-dispatch", "Root Account: {:?}", &target_id); - target_id - }, - CallOrigin::TargetAccount(source_account_id, target_public, target_signature) => { - let digest = account_ownership_digest( - &call, - source_account_id, - message.spec_version, - source_chain, - target_chain, - ); - - let target_account = target_public.into_account(); - if !target_signature.verify(&digest[..], &target_account) { - log::trace!( - target: "runtime::bridge-dispatch", - "Message {:?}/{:?}: origin proof is invalid. Expected account: {:?} from signature: {:?}", - source_chain, - id, - target_account, - target_signature, - ); - Self::deposit_event(Event::MessageSignatureMismatch(source_chain, id)); - return dispatch_result - } - - log::trace!(target: "runtime::bridge-dispatch", "Target Account: {:?}", &target_account); - target_account - }, - CallOrigin::SourceAccount(source_account_id) => { - let hex_id = - derive_account_id(source_chain, SourceAccount::Account(source_account_id)); - let target_id = T::AccountIdConverter::convert(hex_id); - log::trace!(target: "runtime::bridge-dispatch", "Source Account: {:?}", &target_id); - target_id - }, - }; - - // filter the call - if !T::CallFilter::contains(&call) { - log::trace!( - target: "runtime::bridge-dispatch", - "Message {:?}/{:?}: the call ({:?}) is rejected by filter", - source_chain, - id, - call, - ); - Self::deposit_event(Event::MessageCallRejected(source_chain, id)); - return dispatch_result - } - - // verify weight - // (we want passed weight to be at least equal to pre-dispatch weight of the call - // because otherwise Calls may be dispatched at lower price) - let dispatch_info = call.get_dispatch_info(); - let expected_weight = dispatch_info.weight; - if message.weight < expected_weight { - log::trace!( - target: "runtime::bridge-dispatch", - "Message {:?}/{:?}: passed weight is too low. Expected at least {:?}, got {:?}", - source_chain, - id, - expected_weight, - message.weight, - ); - Self::deposit_event(Event::MessageWeightMismatch( - source_chain, - id, - expected_weight, - message.weight, - )); - return dispatch_result - } - - // pay dispatch fee right before dispatch - let pay_dispatch_fee_at_target_chain = - message.dispatch_fee_payment == DispatchFeePayment::AtTargetChain; - if pay_dispatch_fee_at_target_chain && - pay_dispatch_fee(&origin_account, message.weight).is_err() - { - log::trace!( - target: "runtime::bridge-dispatch", - "Failed to pay dispatch fee for dispatching message {:?}/{:?} with weight {}", - source_chain, - id, - message.weight, - ); - Self::deposit_event(Event::MessageDispatchPaymentFailed( - source_chain, - id, - origin_account, - message.weight, - )); - return dispatch_result - } - dispatch_result.dispatch_fee_paid_during_dispatch = pay_dispatch_fee_at_target_chain; - - // finally dispatch message - let origin = RawOrigin::Signed(origin_account).into(); - - log::trace!(target: "runtime::bridge-dispatch", "Message being dispatched is: {:.4096?}", &call); - let result = call.dispatch(origin); - let actual_call_weight = extract_actual_weight(&result, &dispatch_info); - dispatch_result.dispatch_result = result.is_ok(); - dispatch_result.unspent_weight = message.weight.saturating_sub(actual_call_weight); - - log::trace!( - target: "runtime::bridge-dispatch", - "Message {:?}/{:?} has been dispatched. Weight: {} of {}. Result: {:?}. Call dispatch result: {:?}", - source_chain, - id, - actual_call_weight, - message.weight, - dispatch_result, - result, - ); - - Self::deposit_event(Event::MessageDispatched( - source_chain, - id, - result.map(drop).map_err(|e| e.error), - )); - - dispatch_result - } -} - -/// Check if the message is allowed to be dispatched on the target chain given the sender's origin -/// on the source chain. -/// -/// For example, if a message is sent from a "regular" account on the source chain it will not be -/// allowed to be dispatched as Root on the target chain. This is a useful check to do on the source -/// chain _before_ sending a message whose dispatch will be rejected on the target chain. -pub fn verify_message_origin< - SourceChainAccountId, - TargetChainAccountPublic, - TargetChainSignature, - Call, ->( - sender_origin: &RawOrigin, - message: &MessagePayload< - SourceChainAccountId, - TargetChainAccountPublic, - TargetChainSignature, - Call, - >, -) -> Result, BadOrigin> -where - SourceChainAccountId: PartialEq + Clone, -{ - match message.origin { - CallOrigin::SourceRoot => { - ensure!(sender_origin == &RawOrigin::Root, BadOrigin); - Ok(None) - }, - CallOrigin::TargetAccount(ref source_account_id, _, _) => { - ensure!(sender_origin == &RawOrigin::Signed(source_account_id.clone()), BadOrigin); - Ok(Some(source_account_id.clone())) - }, - CallOrigin::SourceAccount(ref source_account_id) => { - ensure!( - sender_origin == &RawOrigin::Signed(source_account_id.clone()) || - sender_origin == &RawOrigin::Root, - BadOrigin - ); - Ok(Some(source_account_id.clone())) - }, - } -} - -/// Target account ownership digest from the source chain. -/// -/// The byte vector returned by this function will be signed with a target chain account -/// private key. This way, the owner of `source_account_id` on the source chain proves that -/// the target chain account private key is also under his control. -pub fn account_ownership_digest( - call: &Call, - source_account_id: AccountId, - target_spec_version: SpecVersion, - source_chain_id: ChainId, - target_chain_id: ChainId, -) -> Vec -where - Call: Encode, - AccountId: Encode, - SpecVersion: Encode, -{ - let mut proof = Vec::new(); - call.encode_to(&mut proof); - source_account_id.encode_to(&mut proof); - target_spec_version.encode_to(&mut proof); - source_chain_id.encode_to(&mut proof); - target_chain_id.encode_to(&mut proof); - - proof -} - -#[cfg(test)] -mod tests { - // From construct_runtime macro - #![allow(clippy::from_over_into)] - - use super::*; - use codec::Decode; - use frame_support::{parameter_types, weights::Weight}; - use frame_system::{EventRecord, Phase}; - use scale_info::TypeInfo; - use sp_core::H256; - use sp_runtime::{ - testing::Header, - traits::{BlakeTwo256, IdentityLookup}, - Perbill, - }; - - type AccountId = u64; - type BridgeMessageId = [u8; 4]; - - const SOURCE_CHAIN_ID: ChainId = *b"srce"; - const TARGET_CHAIN_ID: ChainId = *b"trgt"; - - #[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] - pub struct TestAccountPublic(AccountId); - - impl IdentifyAccount for TestAccountPublic { - type AccountId = AccountId; - - fn into_account(self) -> AccountId { - self.0 - } - } - - #[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] - pub struct TestSignature(AccountId); - - impl Verify for TestSignature { - type Signer = TestAccountPublic; - - fn verify>(&self, _msg: L, signer: &AccountId) -> bool { - self.0 == *signer - } - } - - pub struct AccountIdConverter; - - impl sp_runtime::traits::Convert for AccountIdConverter { - fn convert(hash: H256) -> AccountId { - hash.to_low_u64_ne() - } - } - - type Block = frame_system::mocking::MockBlock; - type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; - - use crate as call_dispatch; - - frame_support::construct_runtime! { - pub enum TestRuntime where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Dispatch: call_dispatch::{Pallet, Call, Event}, - } - } - - parameter_types! { - pub const BlockHashCount: u64 = 250; - pub const MaximumBlockWeight: Weight = 1024; - pub const MaximumBlockLength: u32 = 2 * 1024; - pub const AvailableBlockRatio: Perbill = Perbill::one(); - } - - impl frame_system::Config for TestRuntime { - type Origin = Origin; - type Index = u64; - type Call = Call; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type Header = Header; - type Event = Event; - type BlockHashCount = BlockHashCount; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type BaseCallFilter = frame_support::traits::Everything; - type SystemWeightInfo = (); - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; - } - - impl Config for TestRuntime { - type Event = Event; - type BridgeMessageId = BridgeMessageId; - type SourceChainAccountId = AccountId; - type TargetChainAccountPublic = TestAccountPublic; - type TargetChainSignature = TestSignature; - type Call = Call; - type CallFilter = TestCallFilter; - type EncodedCall = EncodedCall; - type AccountIdConverter = AccountIdConverter; - } - - #[derive(Decode, Encode)] - pub struct EncodedCall(Vec); - - impl From for Result { - fn from(call: EncodedCall) -> Result { - Call::decode(&mut &call.0[..]).map_err(drop) - } - } - - pub struct TestCallFilter; - - impl Contains for TestCallFilter { - fn contains(call: &Call) -> bool { - !matches!(*call, Call::System(frame_system::Call::fill_block { .. })) - } - } - - const TEST_SPEC_VERSION: SpecVersion = 0; - const TEST_WEIGHT: Weight = 1_000_000_000; - - fn new_test_ext() -> sp_io::TestExternalities { - let t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - sp_io::TestExternalities::new(t) - } - - fn prepare_message( - origin: CallOrigin, - call: Call, - ) -> as MessageDispatch< - AccountId, - ::BridgeMessageId, - >>::Message { - MessagePayload { - spec_version: TEST_SPEC_VERSION, - weight: TEST_WEIGHT, - origin, - dispatch_fee_payment: DispatchFeePayment::AtSourceChain, - call: EncodedCall(call.encode()), - } - } - - fn prepare_root_message( - call: Call, - ) -> as MessageDispatch< - AccountId, - ::BridgeMessageId, - >>::Message { - prepare_message(CallOrigin::SourceRoot, call) - } - - fn prepare_target_message( - call: Call, - ) -> as MessageDispatch< - AccountId, - ::BridgeMessageId, - >>::Message { - let origin = CallOrigin::TargetAccount(1, TestAccountPublic(1), TestSignature(1)); - prepare_message(origin, call) - } - - fn prepare_source_message( - call: Call, - ) -> as MessageDispatch< - AccountId, - ::BridgeMessageId, - >>::Message { - let origin = CallOrigin::SourceAccount(1); - prepare_message(origin, call) - } - - #[test] - fn should_fail_on_spec_version_mismatch() { - new_test_ext().execute_with(|| { - let id = [0; 4]; - - const BAD_SPEC_VERSION: SpecVersion = 99; - let mut message = prepare_root_message(Call::System(frame_system::Call::remark { - remark: vec![1, 2, 3], - })); - let weight = message.weight; - message.spec_version = BAD_SPEC_VERSION; - - System::set_block_number(1); - let result = Dispatch::dispatch( - SOURCE_CHAIN_ID, - TARGET_CHAIN_ID, - id, - Ok(message), - |_, _| unreachable!(), - ); - assert_eq!(result.unspent_weight, weight); - assert!(!result.dispatch_result); - - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: Event::Dispatch( - call_dispatch::Event::::MessageVersionSpecMismatch( - SOURCE_CHAIN_ID, - id, - TEST_SPEC_VERSION, - BAD_SPEC_VERSION - ) - ), - topics: vec![], - }], - ); - }); - } - - #[test] - fn should_fail_on_weight_mismatch() { - new_test_ext().execute_with(|| { - let id = [0; 4]; - let call = Call::System(frame_system::Call::set_heap_pages { pages: 42 }); - let call_weight = call.get_dispatch_info().weight; - let mut message = prepare_root_message(call); - message.weight = 7; - assert!(call_weight > 7, "needed for test to actually trigger a weight mismatch"); - - System::set_block_number(1); - let result = Dispatch::dispatch( - SOURCE_CHAIN_ID, - TARGET_CHAIN_ID, - id, - Ok(message), - |_, _| unreachable!(), - ); - assert_eq!(result.unspent_weight, 7); - assert!(!result.dispatch_result); - - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: Event::Dispatch( - call_dispatch::Event::::MessageWeightMismatch( - SOURCE_CHAIN_ID, - id, - call_weight, - 7, - ) - ), - topics: vec![], - }], - ); - }); - } - - #[test] - fn should_fail_on_signature_mismatch() { - new_test_ext().execute_with(|| { - let id = [0; 4]; - - let call_origin = CallOrigin::TargetAccount(1, TestAccountPublic(1), TestSignature(99)); - let message = prepare_message( - call_origin, - Call::System(frame_system::Call::remark { remark: vec![1, 2, 3] }), - ); - let weight = message.weight; - - System::set_block_number(1); - let result = Dispatch::dispatch( - SOURCE_CHAIN_ID, - TARGET_CHAIN_ID, - id, - Ok(message), - |_, _| unreachable!(), - ); - assert_eq!(result.unspent_weight, weight); - assert!(!result.dispatch_result); - - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: Event::Dispatch( - call_dispatch::Event::::MessageSignatureMismatch( - SOURCE_CHAIN_ID, - id - ) - ), - topics: vec![], - }], - ); - }); - } - - #[test] - fn should_emit_event_for_rejected_messages() { - new_test_ext().execute_with(|| { - let id = [0; 4]; - - System::set_block_number(1); - Dispatch::dispatch( - SOURCE_CHAIN_ID, - TARGET_CHAIN_ID, - id, - Err(()), - |_, _| unreachable!(), - ); - - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: Event::Dispatch(call_dispatch::Event::::MessageRejected( - SOURCE_CHAIN_ID, - id - )), - topics: vec![], - }], - ); - }); - } - - #[test] - fn should_fail_on_call_decode() { - new_test_ext().execute_with(|| { - let id = [0; 4]; - - let mut message = prepare_root_message(Call::System(frame_system::Call::remark { - remark: vec![1, 2, 3], - })); - let weight = message.weight; - message.call.0 = vec![]; - - System::set_block_number(1); - let result = Dispatch::dispatch( - SOURCE_CHAIN_ID, - TARGET_CHAIN_ID, - id, - Ok(message), - |_, _| unreachable!(), - ); - assert_eq!(result.unspent_weight, weight); - assert!(!result.dispatch_result); - - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: Event::Dispatch( - call_dispatch::Event::::MessageCallDecodeFailed( - SOURCE_CHAIN_ID, - id - ) - ), - topics: vec![], - }], - ); - }); - } - - #[test] - fn should_emit_event_for_rejected_calls() { - new_test_ext().execute_with(|| { - let id = [0; 4]; - - let call = - Call::System(frame_system::Call::fill_block { ratio: Perbill::from_percent(75) }); - let weight = call.get_dispatch_info().weight; - let mut message = prepare_root_message(call); - message.weight = weight; - - System::set_block_number(1); - let result = Dispatch::dispatch( - SOURCE_CHAIN_ID, - TARGET_CHAIN_ID, - id, - Ok(message), - |_, _| unreachable!(), - ); - assert_eq!(result.unspent_weight, weight); - assert!(!result.dispatch_result); - - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: Event::Dispatch( - call_dispatch::Event::::MessageCallRejected( - SOURCE_CHAIN_ID, - id - ) - ), - topics: vec![], - }], - ); - }); - } - - #[test] - fn should_emit_event_for_unpaid_calls() { - new_test_ext().execute_with(|| { - let id = [0; 4]; - - let mut message = prepare_root_message(Call::System(frame_system::Call::remark { - remark: vec![1, 2, 3], - })); - let weight = message.weight; - message.dispatch_fee_payment = DispatchFeePayment::AtTargetChain; - - System::set_block_number(1); - let result = - Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message), |_, _| { - Err(()) - }); - assert_eq!(result.unspent_weight, weight); - assert!(!result.dispatch_result); - - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: Event::Dispatch( - call_dispatch::Event::::MessageDispatchPaymentFailed( - SOURCE_CHAIN_ID, - id, - AccountIdConverter::convert(derive_account_id::( - SOURCE_CHAIN_ID, - SourceAccount::Root - )), - TEST_WEIGHT, - ) - ), - topics: vec![], - }], - ); - }); - } - - #[test] - fn should_dispatch_calls_paid_at_target_chain() { - new_test_ext().execute_with(|| { - let id = [0; 4]; - - let mut message = prepare_root_message(Call::System(frame_system::Call::remark { - remark: vec![1, 2, 3], - })); - message.dispatch_fee_payment = DispatchFeePayment::AtTargetChain; - - System::set_block_number(1); - let result = Dispatch::dispatch( - SOURCE_CHAIN_ID, - TARGET_CHAIN_ID, - id, - Ok(message), - |_, _| Ok(()), - ); - assert!(result.dispatch_fee_paid_during_dispatch); - assert!(result.dispatch_result); - - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: Event::Dispatch(call_dispatch::Event::::MessageDispatched( - SOURCE_CHAIN_ID, - id, - Ok(()) - )), - topics: vec![], - }], - ); - }); - } - - #[test] - fn should_return_dispatch_failed_flag_if_dispatch_happened_but_failed() { - new_test_ext().execute_with(|| { - let id = [0; 4]; - - let call = Call::System(frame_system::Call::set_heap_pages { pages: 1 }); - let message = prepare_target_message(call); - - System::set_block_number(1); - let result = Dispatch::dispatch( - SOURCE_CHAIN_ID, - TARGET_CHAIN_ID, - id, - Ok(message), - |_, _| unreachable!(), - ); - assert!(!result.dispatch_fee_paid_during_dispatch); - assert!(!result.dispatch_result); - - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: Event::Dispatch(call_dispatch::Event::::MessageDispatched( - SOURCE_CHAIN_ID, - id, - Err(sp_runtime::DispatchError::BadOrigin) - )), - topics: vec![], - }], - ); - }) - } - - #[test] - fn should_dispatch_bridge_message_from_root_origin() { - new_test_ext().execute_with(|| { - let id = [0; 4]; - let message = prepare_root_message(Call::System(frame_system::Call::remark { - remark: vec![1, 2, 3], - })); - - System::set_block_number(1); - let result = Dispatch::dispatch( - SOURCE_CHAIN_ID, - TARGET_CHAIN_ID, - id, - Ok(message), - |_, _| unreachable!(), - ); - assert!(!result.dispatch_fee_paid_during_dispatch); - assert!(result.dispatch_result); - - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: Event::Dispatch(call_dispatch::Event::::MessageDispatched( - SOURCE_CHAIN_ID, - id, - Ok(()) - )), - topics: vec![], - }], - ); - }); - } - - #[test] - fn should_dispatch_bridge_message_from_target_origin() { - new_test_ext().execute_with(|| { - let id = [0; 4]; - - let call = Call::System(frame_system::Call::remark { remark: vec![] }); - let message = prepare_target_message(call); - - System::set_block_number(1); - let result = Dispatch::dispatch( - SOURCE_CHAIN_ID, - TARGET_CHAIN_ID, - id, - Ok(message), - |_, _| unreachable!(), - ); - assert!(!result.dispatch_fee_paid_during_dispatch); - assert!(result.dispatch_result); - - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: Event::Dispatch(call_dispatch::Event::::MessageDispatched( - SOURCE_CHAIN_ID, - id, - Ok(()) - )), - topics: vec![], - }], - ); - }) - } - - #[test] - fn should_dispatch_bridge_message_from_source_origin() { - new_test_ext().execute_with(|| { - let id = [0; 4]; - - let call = Call::System(frame_system::Call::remark { remark: vec![] }); - let message = prepare_source_message(call); - - System::set_block_number(1); - let result = Dispatch::dispatch( - SOURCE_CHAIN_ID, - TARGET_CHAIN_ID, - id, - Ok(message), - |_, _| unreachable!(), - ); - assert!(!result.dispatch_fee_paid_during_dispatch); - assert!(result.dispatch_result); - - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: Event::Dispatch(call_dispatch::Event::::MessageDispatched( - SOURCE_CHAIN_ID, - id, - Ok(()) - )), - topics: vec![], - }], - ); - }) - } - - #[test] - fn origin_is_checked_when_verifying_sending_message_using_source_root_account() { - let call = Call::System(frame_system::Call::remark { remark: vec![] }); - let message = prepare_root_message(call); - - // When message is sent by Root, CallOrigin::SourceRoot is allowed - assert!(matches!(verify_message_origin(&RawOrigin::Root, &message), Ok(None))); - - // when message is sent by some real account, CallOrigin::SourceRoot is not allowed - assert!(matches!(verify_message_origin(&RawOrigin::Signed(1), &message), Err(BadOrigin))); - } - - #[test] - fn origin_is_checked_when_verifying_sending_message_using_target_account() { - let call = Call::System(frame_system::Call::remark { remark: vec![] }); - let message = prepare_target_message(call); - - // When message is sent by Root, CallOrigin::TargetAccount is not allowed - assert!(matches!(verify_message_origin(&RawOrigin::Root, &message), Err(BadOrigin))); - - // When message is sent by some other account, it is rejected - assert!(matches!(verify_message_origin(&RawOrigin::Signed(2), &message), Err(BadOrigin))); - - // When message is sent by a real account, it is allowed to have origin - // CallOrigin::TargetAccount - assert!(matches!(verify_message_origin(&RawOrigin::Signed(1), &message), Ok(Some(1)))); - } - - #[test] - fn origin_is_checked_when_verifying_sending_message_using_source_account() { - let call = Call::System(frame_system::Call::remark { remark: vec![] }); - let message = prepare_source_message(call); - - // Sending a message from the expected origin account works - assert!(matches!(verify_message_origin(&RawOrigin::Signed(1), &message), Ok(Some(1)))); - - // If we send a message from a different account, it is rejected - assert!(matches!(verify_message_origin(&RawOrigin::Signed(2), &message), Err(BadOrigin))); - - // The Root account is allowed to assume any expected origin account - assert!(matches!(verify_message_origin(&RawOrigin::Root, &message), Ok(Some(1)))); - } -} diff --git a/bridges/modules/messages/Cargo.toml b/bridges/modules/messages/Cargo.toml index eaacf2aeb45f..47d41dba1760 100644 --- a/bridges/modules/messages/Cargo.toml +++ b/bridges/modules/messages/Cargo.toml @@ -16,7 +16,6 @@ serde = { version = "1.0.101", optional = true, features = ["derive"] } # Bridge dependencies -bp-message-dispatch = { path = "../../primitives/message-dispatch", default-features = false } bp-messages = { path = "../../primitives/messages", default-features = false } bp-runtime = { path = "../../primitives/runtime", default-features = false } @@ -36,7 +35,6 @@ pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "m [features] default = ["std"] std = [ - "bp-message-dispatch/std", "bp-messages/std", "bp-runtime/std", "codec/std", diff --git a/bridges/modules/token-swap/Cargo.toml b/bridges/modules/token-swap/Cargo.toml deleted file mode 100644 index d3a60d347290..000000000000 --- a/bridges/modules/token-swap/Cargo.toml +++ /dev/null @@ -1,59 +0,0 @@ -[package] -name = "pallet-bridge-token-swap" -description = "An Substrate pallet that allows parties on different chains (bridged using messages pallet) to swap their tokens" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2021" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } -log = { version = "0.4.14", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } -serde = { version = "1.0", optional = true } - -# Bridge dependencies - -bp-message-dispatch = { path = "../../primitives/message-dispatch", default-features = false } -bp-messages = { path = "../../primitives/messages", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } -bp-token-swap = { path = "../../primitives/token-swap", default-features = false } -pallet-bridge-dispatch = { path = "../dispatch", default-features = false } -pallet-bridge-messages = { path = "../messages", default-features = false } - -# Substrate Dependencies - -frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } -frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } - -[dev-dependencies] -pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } - -[features] -default = ["std"] -std = [ - "codec/std", - "bp-message-dispatch/std", - "bp-messages/std", - "bp-runtime/std", - "bp-token-swap/std", - "frame-support/std", - "frame-system/std", - "log/std", - "pallet-bridge-dispatch/std", - "pallet-bridge-messages/std", - "scale-info/std", - "serde", - "sp-core/std", - "sp-io/std", - "sp-runtime/std", - "sp-std/std", -] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", -] diff --git a/bridges/modules/token-swap/src/benchmarking.rs b/bridges/modules/token-swap/src/benchmarking.rs deleted file mode 100644 index 878cb20993a9..000000000000 --- a/bridges/modules/token-swap/src/benchmarking.rs +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Token-swap pallet benchmarking. - -use crate::{ - swap_account_id, target_account_at_this_chain, BridgedAccountIdOf, BridgedAccountPublicOf, - BridgedAccountSignatureOf, BridgedBalanceOf, Call, Origin, Pallet, ThisChainBalance, - TokenSwapCreationOf, TokenSwapOf, -}; - -use bp_token_swap::{TokenSwap, TokenSwapCreation, TokenSwapState, TokenSwapType}; -use codec::{Decode, Encode}; -use frame_benchmarking::{account, benchmarks_instance_pallet}; -use frame_support::{traits::Currency, Parameter}; -use frame_system::RawOrigin; -use sp_core::H256; -use sp_io::hashing::blake2_256; -use sp_runtime::traits::{Bounded, TrailingZeroInput}; -use sp_std::{boxed::Box, vec::Vec}; - -const SEED: u32 = 0; - -/// Trait that must be implemented by runtime. -pub trait Config: crate::Config { - /// Initialize environment for token swap. - fn initialize_environment(); -} - -benchmarks_instance_pallet! { - where_clause { - where - Origin: Into, - BridgedAccountPublicOf: Decode + Parameter, - BridgedAccountSignatureOf: Decode, - } - - // - // Benchmarks that are used directly by the runtime. - // - - // Benchmark `create_swap` extrinsic. - // - // This benchmark assumes that message is **NOT** actually sent. Instead we're using `send_message_weight` - // from the `WeightInfoExt` trait. - // - // There aren't any factors that affect `create_swap` performance, so everything - // is straightforward here. - create_swap { - T::initialize_environment(); - - let sender = funded_account::("source_account_at_this_chain", 0); - let swap: TokenSwapOf = test_swap::(sender.clone(), true); - let swap_creation: TokenSwapCreationOf = test_swap_creation::(); - }: create_swap( - RawOrigin::Signed(sender.clone()), - swap, - Box::new(swap_creation) - ) - verify { - assert!(crate::PendingSwaps::::contains_key(test_swap_hash::(sender, true))); - } - - // Benchmark `claim_swap` extrinsic with the worst possible conditions: - // - // * swap is locked until some block, so current block number is read. - claim_swap { - T::initialize_environment(); - - let sender: T::AccountId = account("source_account_at_this_chain", 0, SEED); - crate::PendingSwaps::::insert( - test_swap_hash::(sender.clone(), false), - TokenSwapState::Confirmed, - ); - - let swap: TokenSwapOf = test_swap::(sender.clone(), false); - let claimer = target_account_at_this_chain::(&swap); - let token_swap_account = swap_account_id::(&swap); - T::ThisCurrency::make_free_balance_be(&token_swap_account, ThisChainBalance::::max_value()); - }: claim_swap(RawOrigin::Signed(claimer), swap) - verify { - assert!(!crate::PendingSwaps::::contains_key(test_swap_hash::(sender, false))); - } - - // Benchmark `cancel_swap` extrinsic with the worst possible conditions: - // - // * swap is locked until some block, so current block number is read. - cancel_swap { - T::initialize_environment(); - - let sender: T::AccountId = account("source_account_at_this_chain", 0, SEED); - crate::PendingSwaps::::insert( - test_swap_hash::(sender.clone(), false), - TokenSwapState::Failed, - ); - - let swap: TokenSwapOf = test_swap::(sender.clone(), false); - let token_swap_account = swap_account_id::(&swap); - T::ThisCurrency::make_free_balance_be(&token_swap_account, ThisChainBalance::::max_value()); - - }: cancel_swap(RawOrigin::Signed(sender.clone()), swap) - verify { - assert!(!crate::PendingSwaps::::contains_key(test_swap_hash::(sender, false))); - } -} - -/// Returns test token swap. -fn test_swap, I: 'static>(sender: T::AccountId, is_create: bool) -> TokenSwapOf { - TokenSwap { - swap_type: TokenSwapType::LockClaimUntilBlock( - if is_create { 10u32.into() } else { 0u32.into() }, - 0.into(), - ), - source_balance_at_this_chain: source_balance_to_swap::(), - source_account_at_this_chain: sender, - target_balance_at_bridged_chain: target_balance_to_swap::(), - target_account_at_bridged_chain: target_account_at_bridged_chain::(), - } -} - -/// Returns test token swap hash. -fn test_swap_hash, I: 'static>(sender: T::AccountId, is_create: bool) -> H256 { - test_swap::(sender, is_create).using_encoded(blake2_256).into() -} - -/// Returns test token swap creation params. -fn test_swap_creation, I: 'static>() -> TokenSwapCreationOf -where - BridgedAccountPublicOf: Decode, - BridgedAccountSignatureOf: Decode, -{ - TokenSwapCreation { - target_public_at_bridged_chain: target_public_at_bridged_chain::(), - swap_delivery_and_dispatch_fee: swap_delivery_and_dispatch_fee::(), - bridged_chain_spec_version: 0, - bridged_currency_transfer: Vec::new(), - bridged_currency_transfer_weight: 0, - bridged_currency_transfer_signature: bridged_currency_transfer_signature::(), - } -} - -/// Account that has some balance. -fn funded_account, I: 'static>(name: &'static str, index: u32) -> T::AccountId { - let account: T::AccountId = account(name, index, SEED); - T::ThisCurrency::make_free_balance_be(&account, ThisChainBalance::::max_value()); - account -} - -/// Currency transfer message fee. -fn swap_delivery_and_dispatch_fee, I: 'static>() -> ThisChainBalance { - ThisChainBalance::::max_value() / 4u32.into() -} - -/// Balance at the source chain that we're going to swap. -fn source_balance_to_swap, I: 'static>() -> ThisChainBalance { - ThisChainBalance::::max_value() / 2u32.into() -} - -/// Balance at the target chain that we're going to swap. -fn target_balance_to_swap, I: 'static>() -> BridgedBalanceOf { - BridgedBalanceOf::::max_value() / 2u32.into() -} - -/// Public key of `target_account_at_bridged_chain`. -fn target_public_at_bridged_chain, I: 'static>() -> BridgedAccountPublicOf -where - BridgedAccountPublicOf: Decode, -{ - BridgedAccountPublicOf::::decode(&mut TrailingZeroInput::zeroes()) - .expect("failed to decode `BridgedAccountPublicOf` from zeroes") -} - -/// Signature of `target_account_at_bridged_chain` over message. -fn bridged_currency_transfer_signature, I: 'static>() -> BridgedAccountSignatureOf -where - BridgedAccountSignatureOf: Decode, -{ - BridgedAccountSignatureOf::::decode(&mut TrailingZeroInput::zeroes()) - .expect("failed to decode `BridgedAccountSignatureOf` from zeroes") -} - -/// Account at the bridged chain that is participating in the swap. -fn target_account_at_bridged_chain, I: 'static>() -> BridgedAccountIdOf { - account("target_account_at_bridged_chain", 0, SEED) -} diff --git a/bridges/modules/token-swap/src/lib.rs b/bridges/modules/token-swap/src/lib.rs deleted file mode 100644 index 81059ee1fd5f..000000000000 --- a/bridges/modules/token-swap/src/lib.rs +++ /dev/null @@ -1,1195 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Runtime module that allows token swap between two parties acting on different chains. -//! -//! The swap is made using message lanes between This (where `pallet-bridge-token-swap` pallet -//! is deployed) and some other Bridged chain. No other assumptions about the Bridged chain are -//! made, so we don't need it to have an instance of the `pallet-bridge-token-swap` pallet deployed. -//! -//! There are four accounts participating in the swap: -//! -//! 1) account of This chain that has signed the `create_swap` transaction and has balance on This -//! chain. We'll be referring to this account as `source_account_at_this_chain`; -//! -//! 2) account of the Bridged chain that is sending the `claim_swap` message from the Bridged to -//! This chain. This account has balance on Bridged chain and is willing to swap these tokens to -//! This chain tokens of the `source_account_at_this_chain`. We'll be referring to this account -//! as `target_account_at_bridged_chain`; -//! -//! 3) account of the Bridged chain that is indirectly controlled by the -//! `source_account_at_this_chain`. We'll be referring this account as -//! `source_account_at_bridged_chain`; -//! -//! 4) account of This chain that is indirectly controlled by the `target_account_at_bridged_chain`. -//! We'll be referring this account as `target_account_at_this_chain`. -//! -//! So the tokens swap is an intention of `source_account_at_this_chain` to swap his -//! `source_balance_at_this_chain` tokens to the `target_balance_at_bridged_chain` tokens owned by -//! `target_account_at_bridged_chain`. The swap process goes as follows: -//! -//! 1) the `source_account_at_this_chain` account submits the `create_swap` transaction on This -//! chain; -//! -//! 2) the tokens transfer message that would transfer `target_balance_at_bridged_chain` -//! tokens from the `target_account_at_bridged_chain` to the `source_account_at_bridged_chain`, -//! is sent over the bridge; -//! -//! 3) when transfer message is delivered and dispatched, the pallet receives notification; -//! -//! 4) if message has been successfully dispatched, the `target_account_at_bridged_chain` sends the -//! message that would transfer `source_balance_at_this_chain` tokens to his -//! `target_account_at_this_chain` account; -//! -//! 5) if message dispatch has failed, the `source_account_at_this_chain` may submit the -//! `cancel_swap` transaction and return his `source_balance_at_this_chain` back to his account. -//! -//! While swap is pending, the `source_balance_at_this_chain` tokens are owned by the special -//! temporary `swap_account_at_this_chain` account. It is destroyed upon swap completion. - -#![cfg_attr(not(feature = "std"), no_std)] - -use bp_messages::{ - source_chain::{MessagesBridge, OnDeliveryConfirmed}, - DeliveredMessages, LaneId, MessageNonce, -}; -use bp_runtime::{messages::DispatchFeePayment, ChainId}; -use bp_token_swap::{ - RawBridgedTransferCall, TokenSwap, TokenSwapCreation, TokenSwapState, TokenSwapType, -}; -use codec::{Decode, Encode}; -use frame_support::{ - fail, - traits::{Currency, ExistenceRequirement}, - weights::PostDispatchInfo, - RuntimeDebug, -}; -use scale_info::TypeInfo; -use sp_core::H256; -use sp_io::hashing::blake2_256; -use sp_runtime::traits::{Convert, Saturating}; -use sp_std::{boxed::Box, marker::PhantomData}; -use weights::WeightInfo; - -pub use weights_ext::WeightInfoExt; - -#[cfg(test)] -mod mock; - -#[cfg(feature = "runtime-benchmarks")] -pub mod benchmarking; - -pub mod weights; -pub mod weights_ext; - -pub use pallet::*; - -/// Name of the `PendingSwaps` storage map. -pub const PENDING_SWAPS_MAP_NAME: &str = "PendingSwaps"; - -/// Origin for the token swap pallet. -#[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo)] -pub enum RawOrigin { - /// The call is originated by the token swap account. - TokenSwap { - /// Id of the account that has started the swap. - source_account_at_this_chain: AccountId, - /// Id of the account that holds the funds during this swap. The message fee is paid from - /// this account funds. - swap_account_at_this_chain: AccountId, - }, - /// Dummy to manage the fact we have instancing. - _Phantom(PhantomData), -} - -// comes from #[pallet::event] -#[allow(clippy::unused_unit)] -#[frame_support::pallet] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::config] - pub trait Config: frame_system::Config { - /// The overarching event type. - type Event: From> + IsType<::Event>; - /// Benchmarks results from runtime we're plugged into. - type WeightInfo: WeightInfoExt; - - /// Id of the bridge with the Bridged chain. - type BridgedChainId: Get; - /// The identifier of outbound message lane on This chain used to send token transfer - /// messages to the Bridged chain. - /// - /// It is highly recommended to use dedicated lane for every instance of token swap - /// pallet. Messages delivery confirmation callback is implemented in the way that - /// for every confirmed message, there is (at least) a storage read. Which mean, - /// that if pallet will see unrelated confirmations, it'll just burn storage-read - /// weight, achieving nothing. - type OutboundMessageLaneId: Get; - /// Messages bridge with Bridged chain. - type MessagesBridge: MessagesBridge< - Self::Origin, - Self::AccountId, - >::Balance, - MessagePayloadOf, - >; - - /// This chain Currency used in the tokens swap. - type ThisCurrency: Currency; - /// Converter from raw hash (derived from swap) to This chain account. - type FromSwapToThisAccountIdConverter: Convert; - - /// The chain we're bridged to. - type BridgedChain: bp_runtime::Chain; - /// Converter from raw hash (derived from Bridged chain account) to This chain account. - type FromBridgedToThisAccountIdConverter: Convert; - } - - /// Tokens balance at This chain. - pub type ThisChainBalance = <>::ThisCurrency as Currency< - ::AccountId, - >>::Balance; - - /// Type of the Bridged chain. - pub type BridgedChainOf = >::BridgedChain; - /// Tokens balance type at the Bridged chain. - pub type BridgedBalanceOf = bp_runtime::BalanceOf>; - /// Account identifier type at the Bridged chain. - pub type BridgedAccountIdOf = bp_runtime::AccountIdOf>; - /// Account public key type at the Bridged chain. - pub type BridgedAccountPublicOf = bp_runtime::AccountPublicOf>; - /// Account signature type at the Bridged chain. - pub type BridgedAccountSignatureOf = bp_runtime::SignatureOf>; - - /// Bridge message payload used by the pallet. - pub type MessagePayloadOf = bp_message_dispatch::MessagePayload< - ::AccountId, - BridgedAccountPublicOf, - BridgedAccountSignatureOf, - RawBridgedTransferCall, - >; - /// Type of `TokenSwap` used by the pallet. - pub type TokenSwapOf = TokenSwap< - BlockNumberFor, - ThisChainBalance, - ::AccountId, - BridgedBalanceOf, - BridgedAccountIdOf, - >; - /// Type of `TokenSwapCreation` used by the pallet. - pub type TokenSwapCreationOf = TokenSwapCreation< - BridgedAccountPublicOf, - ThisChainBalance, - BridgedAccountSignatureOf, - >; - - #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] - #[pallet::without_storage_info] - pub struct Pallet(PhantomData<(T, I)>); - - #[pallet::hooks] - impl, I: 'static> Hooks> for Pallet {} - - #[pallet::call] - impl, I: 'static> Pallet - where - BridgedAccountPublicOf: Parameter, - Origin: Into, - { - /// Start token swap procedure. - /// - /// The dispatch origin for this call must be exactly the - /// `swap.source_account_at_this_chain` account. - /// - /// Method arguments are: - /// - /// - `swap` - token swap intention; - /// - `swap_creation_params` - additional parameters required to start tokens swap. - /// - /// The `source_account_at_this_chain` MUST have enough balance to cover both token swap and - /// message transfer. Message fee may be estimated using corresponding `OutboundLaneApi` of - /// This runtime. - /// - /// **WARNING**: the submitter of this transaction is responsible for verifying: - /// - /// 1) that the `swap_creation_params.bridged_currency_transfer` represents a valid token - /// transfer call that transfers `swap.target_balance_at_bridged_chain` to his - /// `swap.source_account_at_bridged_chain` account; - /// - /// 2) that either the `swap.source_account_at_bridged_chain` already exists, or the - /// `swap.target_balance_at_bridged_chain` is above existential deposit of the Bridged - /// chain; - /// - /// 3) the `swap_creation_params.target_public_at_bridged_chain` matches the - /// `swap.target_account_at_bridged_chain`; - /// - /// 4) the `bridged_currency_transfer_signature` is valid and generated by the owner of - /// the `swap_creation_params.target_public_at_bridged_chain` account (read more - /// about [`CallOrigin::TargetAccount`]). - /// - /// Violating rule#1 will lead to losing your `source_balance_at_this_chain` tokens. - /// Violating other rules will lead to losing message fees for this and other transactions + - /// losing fees for message transfer. - #[allow(clippy::boxed_local)] - #[pallet::weight( - T::WeightInfo::create_swap() - .saturating_add(T::WeightInfo::send_message_weight( - &&swap_creation_params.bridged_currency_transfer[..], - T::DbWeight::get(), - )) - )] - pub fn create_swap( - origin: OriginFor, - swap: TokenSwapOf, - swap_creation_params: Box>, - ) -> DispatchResultWithPostInfo { - let TokenSwapCreation { - target_public_at_bridged_chain, - swap_delivery_and_dispatch_fee, - bridged_chain_spec_version, - bridged_currency_transfer, - bridged_currency_transfer_weight, - bridged_currency_transfer_signature, - } = *swap_creation_params; - - // ensure that the `origin` is the same account that is mentioned in the `swap` - // intention - let origin_account = ensure_signed(origin)?; - ensure!( - origin_account == swap.source_account_at_this_chain, - Error::::MismatchedSwapSourceOrigin, - ); - - // remember weight components - let base_weight = T::WeightInfo::create_swap(); - - // we can't exchange less than existential deposit (the temporary `swap_account` account - // won't be created then) - // - // the same can also happen with the `swap.bridged_balance`, but we can't check it - // here (without additional knowledge of the Bridged chain). So it is the `origin` - // responsibility to check that the swap is valid. - ensure!( - swap.source_balance_at_this_chain >= T::ThisCurrency::minimum_balance(), - Error::::TooLowBalanceOnThisChain, - ); - - // if the swap is replay-protected, then we need to ensure that we have not yet passed - // the specified block yet - match swap.swap_type { - TokenSwapType::TemporaryTargetAccountAtBridgedChain => (), - TokenSwapType::LockClaimUntilBlock(block_number, _) => ensure!( - block_number >= frame_system::Pallet::::block_number(), - Error::::SwapPeriodIsFinished, - ), - } - - let swap_account = swap_account_id::(&swap); - let actual_send_message_weight = frame_support::storage::with_transaction( - || -> sp_runtime::TransactionOutcome> { - // funds are transferred from This account to the temporary Swap account - let transfer_result = T::ThisCurrency::transfer( - &swap.source_account_at_this_chain, - &swap_account, - // saturating_add is ok, or we have the chain where single holder owns all - // tokens - swap.source_balance_at_this_chain - .saturating_add(swap_delivery_and_dispatch_fee), - // if we'll allow account to die, then he'll be unable to `cancel_claim` - // if something won't work - ExistenceRequirement::KeepAlive, - ); - if let Err(err) = transfer_result { - log::error!( - target: "runtime::bridge-token-swap", - "Failed to transfer This chain tokens for the swap {:?} to Swap account ({:?}): {:?}", - swap, - swap_account, - err, - ); - - return sp_runtime::TransactionOutcome::Rollback(Err( - Error::::FailedToTransferToSwapAccount.into(), - )) - } - - // the transfer message is sent over the bridge. The message is supposed to be a - // `Currency::transfer` call on the bridged chain, but no checks are made - it - // is the transaction submitter to ensure it is valid. - let send_message_result = T::MessagesBridge::send_message( - RawOrigin::TokenSwap { - source_account_at_this_chain: swap.source_account_at_this_chain.clone(), - swap_account_at_this_chain: swap_account.clone(), - } - .into(), - T::OutboundMessageLaneId::get(), - bp_message_dispatch::MessagePayload { - spec_version: bridged_chain_spec_version, - weight: bridged_currency_transfer_weight, - origin: bp_message_dispatch::CallOrigin::TargetAccount( - swap_account, - target_public_at_bridged_chain, - bridged_currency_transfer_signature, - ), - dispatch_fee_payment: DispatchFeePayment::AtTargetChain, - call: bridged_currency_transfer, - }, - swap_delivery_and_dispatch_fee, - ); - let sent_message = match send_message_result { - Ok(sent_message) => sent_message, - Err(err) => { - log::error!( - target: "runtime::bridge-token-swap", - "Failed to send token transfer message for swap {:?} to the Bridged chain: {:?}", - swap, - err, - ); - - return sp_runtime::TransactionOutcome::Rollback(Err( - Error::::FailedToSendTransferMessage.into(), - )) - }, - }; - - // remember that we have started the swap - let swap_hash = swap.using_encoded(blake2_256).into(); - let insert_swap_result = - PendingSwaps::::try_mutate(swap_hash, |maybe_state| { - if maybe_state.is_some() { - return Err(()) - } - - *maybe_state = Some(TokenSwapState::Started); - Ok(()) - }); - if insert_swap_result.is_err() { - log::error!( - target: "runtime::bridge-token-swap", - "Failed to start token swap {:?}: the swap is already started", - swap, - ); - - return sp_runtime::TransactionOutcome::Rollback(Err( - Error::::SwapAlreadyStarted.into(), - )) - } - - log::trace!( - target: "runtime::bridge-token-swap", - "The swap {:?} (hash {:?}) has been started", - swap, - swap_hash, - ); - - // remember that we're waiting for the transfer message delivery confirmation - PendingMessages::::insert(sent_message.nonce, swap_hash); - - // finally - emit the event - Self::deposit_event(Event::SwapStarted(swap_hash, sent_message.nonce)); - - sp_runtime::TransactionOutcome::Commit(Ok(sent_message.weight)) - }, - )?; - - Ok(PostDispatchInfo { - actual_weight: Some(base_weight.saturating_add(actual_send_message_weight)), - pays_fee: Pays::Yes, - }) - } - - /// Claim previously reserved `source_balance_at_this_chain` by - /// `target_account_at_this_chain`. - /// - /// **WARNING**: the correct way to call this function is to call it over the messages - /// bridge with dispatch origin set to - /// `pallet_bridge_dispatch::CallOrigin::SourceAccount(target_account_at_bridged_chain)`. - /// - /// This should be called only when successful transfer confirmation has been received. - #[pallet::weight(T::WeightInfo::claim_swap())] - pub fn claim_swap( - origin: OriginFor, - swap: TokenSwapOf, - ) -> DispatchResultWithPostInfo { - // ensure that the `origin` is controlled by the `swap.target_account_at_bridged_chain` - let origin_account = ensure_signed(origin)?; - let target_account_at_this_chain = target_account_at_this_chain::(&swap); - ensure!(origin_account == target_account_at_this_chain, Error::::InvalidClaimant,); - - // ensure that the swap is confirmed - let swap_hash = swap.using_encoded(blake2_256).into(); - let swap_state = PendingSwaps::::get(swap_hash); - match swap_state { - Some(TokenSwapState::Started) => fail!(Error::::SwapIsPending), - Some(TokenSwapState::Confirmed) => { - let is_claim_allowed = match swap.swap_type { - TokenSwapType::TemporaryTargetAccountAtBridgedChain => true, - TokenSwapType::LockClaimUntilBlock(block_number, _) => - block_number < frame_system::Pallet::::block_number(), - }; - - ensure!(is_claim_allowed, Error::::SwapIsTemporaryLocked); - }, - Some(TokenSwapState::Failed) => fail!(Error::::SwapIsFailed), - None => fail!(Error::::SwapIsInactive), - } - - complete_claim::(swap, swap_hash, origin_account, Event::SwapClaimed(swap_hash)) - } - - /// Return previously reserved `source_balance_at_this_chain` back to the - /// `source_account_at_this_chain`. - /// - /// This should be called only when transfer has failed at Bridged chain and we have - /// received notification about that. - #[pallet::weight(T::WeightInfo::cancel_swap())] - pub fn cancel_swap( - origin: OriginFor, - swap: TokenSwapOf, - ) -> DispatchResultWithPostInfo { - // ensure that the `origin` is the same account that is mentioned in the `swap` - // intention - let origin_account = ensure_signed(origin)?; - ensure!( - origin_account == swap.source_account_at_this_chain, - Error::::MismatchedSwapSourceOrigin, - ); - - // ensure that the swap has failed - let swap_hash = swap.using_encoded(blake2_256).into(); - let swap_state = PendingSwaps::::get(swap_hash); - match swap_state { - Some(TokenSwapState::Started) => fail!(Error::::SwapIsPending), - Some(TokenSwapState::Confirmed) => fail!(Error::::SwapIsConfirmed), - Some(TokenSwapState::Failed) => { - // we allow canceling swap even before lock period is over - the - // `source_account_at_this_chain` has already paid for nothing and it is up to - // him to decide whether he want to try again - }, - None => fail!(Error::::SwapIsInactive), - } - - complete_claim::(swap, swap_hash, origin_account, Event::SwapCanceled(swap_hash)) - } - } - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event, I: 'static = ()> { - /// Tokens swap has been started and message has been sent to the bridged message. - /// - /// The payload is the swap hash and the transfer message nonce. - SwapStarted(H256, MessageNonce), - /// Token swap has been claimed. - SwapClaimed(H256), - /// Token swap has been canceled. - SwapCanceled(H256), - } - - #[pallet::error] - pub enum Error { - /// The account that has submitted the `start_claim` doesn't match the - /// `TokenSwap::source_account_at_this_chain`. - MismatchedSwapSourceOrigin, - /// The swap balance in This chain tokens is below existential deposit and can't be made. - TooLowBalanceOnThisChain, - /// Transfer from This chain account to temporary Swap account has failed. - FailedToTransferToSwapAccount, - /// Transfer from the temporary Swap account to the derived account of Bridged account has - /// failed. - FailedToTransferFromSwapAccount, - /// The message to transfer tokens on Target chain can't be sent. - FailedToSendTransferMessage, - /// The same swap is already started. - SwapAlreadyStarted, - /// Swap outcome is not yet received. - SwapIsPending, - /// Someone is trying to claim swap that has failed. - SwapIsFailed, - /// Claiming swap is not allowed. - /// - /// Now the only possible case when you may get this error, is when you're trying to claim - /// swap with `TokenSwapType::LockClaimUntilBlock` before lock period is over. - SwapIsTemporaryLocked, - /// Swap period is finished and you can not restart it. - /// - /// Now the only possible case when you may get this error, is when you're trying to start - /// swap with `TokenSwapType::LockClaimUntilBlock` after lock period is over. - SwapPeriodIsFinished, - /// Someone is trying to cancel swap that has been confirmed. - SwapIsConfirmed, - /// Someone is trying to claim/cancel swap that is either not started or already - /// claimed/canceled. - SwapIsInactive, - /// The swap claimant is invalid. - InvalidClaimant, - } - - /// Origin for the token swap pallet. - #[pallet::origin] - pub type Origin = RawOrigin<::AccountId, I>; - - /// Pending token swaps states. - #[pallet::storage] - pub type PendingSwaps, I: 'static = ()> = - StorageMap<_, Identity, H256, TokenSwapState>; - - /// Pending transfer messages. - #[pallet::storage] - pub type PendingMessages, I: 'static = ()> = - StorageMap<_, Identity, MessageNonce, H256>; - - impl, I: 'static> OnDeliveryConfirmed for Pallet { - fn on_messages_delivered(lane: &LaneId, delivered_messages: &DeliveredMessages) -> Weight { - // we're only interested in our lane messages - if *lane != T::OutboundMessageLaneId::get() { - return 0 - } - - // so now we're dealing with our lane messages. Ideally we'll have dedicated lane - // and every message from `delivered_messages` is actually our transfer message. - // But it may be some shared lane (which is not recommended). - let mut reads = 0; - let mut writes = 0; - for message_nonce in delivered_messages.begin..=delivered_messages.end { - reads += 1; - if let Some(swap_hash) = PendingMessages::::take(message_nonce) { - writes += 1; - - let token_swap_state = - if delivered_messages.message_dispatch_result(message_nonce) { - TokenSwapState::Confirmed - } else { - TokenSwapState::Failed - }; - - log::trace!( - target: "runtime::bridge-token-swap", - "The dispatch of swap {:?} has been completed with {:?} status", - swap_hash, - token_swap_state, - ); - - PendingSwaps::::insert(swap_hash, token_swap_state); - } - } - - ::DbWeight::get().reads_writes(reads, writes) - } - } - - /// Returns temporary account id used to lock funds during swap on This chain. - pub(crate) fn swap_account_id, I: 'static>( - swap: &TokenSwapOf, - ) -> T::AccountId { - T::FromSwapToThisAccountIdConverter::convert(swap.using_encoded(blake2_256).into()) - } - - /// Expected target account representation on This chain (aka `target_account_at_this_chain`). - pub(crate) fn target_account_at_this_chain, I: 'static>( - swap: &TokenSwapOf, - ) -> T::AccountId { - T::FromBridgedToThisAccountIdConverter::convert(bp_runtime::derive_account_id( - T::BridgedChainId::get(), - bp_runtime::SourceAccount::Account(swap.target_account_at_bridged_chain.clone()), - )) - } - - /// Complete claim with given outcome. - pub(crate) fn complete_claim, I: 'static>( - swap: TokenSwapOf, - swap_hash: H256, - destination_account: T::AccountId, - event: Event, - ) -> DispatchResultWithPostInfo { - let swap_account = swap_account_id::(&swap); - frame_support::storage::with_transaction( - || -> sp_runtime::TransactionOutcome> { - // funds are transferred from the temporary Swap account to the destination account - let transfer_result = T::ThisCurrency::transfer( - &swap_account, - &destination_account, - swap.source_balance_at_this_chain, - ExistenceRequirement::AllowDeath, - ); - if let Err(err) = transfer_result { - log::error!( - target: "runtime::bridge-token-swap", - "Failed to transfer This chain tokens for the swap {:?} from the Swap account {:?} to {:?}: {:?}", - swap, - swap_account, - destination_account, - err, - ); - - return sp_runtime::TransactionOutcome::Rollback(Err( - Error::::FailedToTransferFromSwapAccount.into(), - )) - } - - log::trace!( - target: "runtime::bridge-token-swap", - "The swap {:?} (hash {:?}) has been completed with {} status", - swap, - swap_hash, - match event { - Event::SwapClaimed(_) => "claimed", - Event::SwapCanceled(_) => "canceled", - _ => "", - }, - ); - - // forget about swap - PendingSwaps::::remove(swap_hash); - - // finally - emit the event - Pallet::::deposit_event(event); - - sp_runtime::TransactionOutcome::Commit(Ok(Ok(().into()))) - }, - )? - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::mock::*; - use frame_support::{assert_noop, assert_ok, storage::generator::StorageMap}; - - const CAN_START_BLOCK_NUMBER: u64 = 10; - const CAN_CLAIM_BLOCK_NUMBER: u64 = CAN_START_BLOCK_NUMBER + 1; - - const BRIDGED_CHAIN_ACCOUNT: BridgedAccountId = 3; - const BRIDGED_CHAIN_SPEC_VERSION: u32 = 4; - const BRIDGED_CHAIN_CALL_WEIGHT: Balance = 5; - - fn bridged_chain_account_public() -> BridgedAccountPublic { - 1.into() - } - - fn bridged_chain_account_signature() -> BridgedAccountSignature { - sp_runtime::testing::TestSignature(2, Vec::new()) - } - - fn test_swap() -> TokenSwapOf { - bp_token_swap::TokenSwap { - swap_type: TokenSwapType::LockClaimUntilBlock(CAN_START_BLOCK_NUMBER, 0.into()), - source_balance_at_this_chain: 100, - source_account_at_this_chain: THIS_CHAIN_ACCOUNT, - target_balance_at_bridged_chain: 200, - target_account_at_bridged_chain: BRIDGED_CHAIN_ACCOUNT, - } - } - - fn test_swap_creation() -> TokenSwapCreationOf { - TokenSwapCreation { - target_public_at_bridged_chain: bridged_chain_account_public(), - swap_delivery_and_dispatch_fee: SWAP_DELIVERY_AND_DISPATCH_FEE, - bridged_chain_spec_version: BRIDGED_CHAIN_SPEC_VERSION, - bridged_currency_transfer: test_transfer(), - bridged_currency_transfer_weight: BRIDGED_CHAIN_CALL_WEIGHT, - bridged_currency_transfer_signature: bridged_chain_account_signature(), - } - } - - fn test_swap_hash() -> H256 { - test_swap().using_encoded(blake2_256).into() - } - - fn test_transfer() -> RawBridgedTransferCall { - vec![OK_TRANSFER_CALL] - } - - fn start_test_swap() { - assert_ok!(Pallet::::create_swap( - mock::Origin::signed(THIS_CHAIN_ACCOUNT), - test_swap(), - Box::new(TokenSwapCreation { - target_public_at_bridged_chain: bridged_chain_account_public(), - swap_delivery_and_dispatch_fee: SWAP_DELIVERY_AND_DISPATCH_FEE, - bridged_chain_spec_version: BRIDGED_CHAIN_SPEC_VERSION, - bridged_currency_transfer: test_transfer(), - bridged_currency_transfer_weight: BRIDGED_CHAIN_CALL_WEIGHT, - bridged_currency_transfer_signature: bridged_chain_account_signature(), - }), - )); - } - - fn receive_test_swap_confirmation(success: bool) { - Pallet::::on_messages_delivered( - &OutboundMessageLaneId::get(), - &DeliveredMessages::new(MESSAGE_NONCE, success), - ); - } - - #[test] - fn create_swap_fails_if_origin_is_incorrect() { - run_test(|| { - assert_noop!( - Pallet::::create_swap( - mock::Origin::signed(THIS_CHAIN_ACCOUNT + 1), - test_swap(), - Box::new(test_swap_creation()), - ), - Error::::MismatchedSwapSourceOrigin - ); - }); - } - - #[test] - fn create_swap_fails_if_this_chain_balance_is_below_existential_deposit() { - run_test(|| { - let mut swap = test_swap(); - swap.source_balance_at_this_chain = ExistentialDeposit::get() - 1; - assert_noop!( - Pallet::::create_swap( - mock::Origin::signed(THIS_CHAIN_ACCOUNT), - swap, - Box::new(test_swap_creation()), - ), - Error::::TooLowBalanceOnThisChain - ); - }); - } - - #[test] - fn create_swap_fails_if_currency_transfer_to_swap_account_fails() { - run_test(|| { - let mut swap = test_swap(); - swap.source_balance_at_this_chain = THIS_CHAIN_ACCOUNT_BALANCE + 1; - assert_noop!( - Pallet::::create_swap( - mock::Origin::signed(THIS_CHAIN_ACCOUNT), - swap, - Box::new(test_swap_creation()), - ), - Error::::FailedToTransferToSwapAccount - ); - }); - } - - #[test] - fn create_swap_fails_if_send_message_fails() { - run_test(|| { - let mut transfer = test_transfer(); - transfer[0] = BAD_TRANSFER_CALL; - let mut swap_creation = test_swap_creation(); - swap_creation.bridged_currency_transfer = transfer; - assert_noop!( - Pallet::::create_swap( - mock::Origin::signed(THIS_CHAIN_ACCOUNT), - test_swap(), - Box::new(swap_creation), - ), - Error::::FailedToSendTransferMessage - ); - }); - } - - #[test] - fn create_swap_fails_if_swap_is_active() { - run_test(|| { - assert_ok!(Pallet::::create_swap( - mock::Origin::signed(THIS_CHAIN_ACCOUNT), - test_swap(), - Box::new(test_swap_creation()), - )); - - assert_noop!( - Pallet::::create_swap( - mock::Origin::signed(THIS_CHAIN_ACCOUNT), - test_swap(), - Box::new(test_swap_creation()), - ), - Error::::SwapAlreadyStarted - ); - }); - } - - #[test] - fn create_swap_fails_if_trying_to_start_swap_after_lock_period_is_finished() { - run_test(|| { - frame_system::Pallet::::set_block_number(CAN_START_BLOCK_NUMBER + 1); - assert_noop!( - Pallet::::create_swap( - mock::Origin::signed(THIS_CHAIN_ACCOUNT), - test_swap(), - Box::new(test_swap_creation()), - ), - Error::::SwapPeriodIsFinished - ); - }); - } - - #[test] - fn create_swap_succeeds_if_trying_to_start_swap_at_lock_period_end() { - run_test(|| { - frame_system::Pallet::::set_block_number(CAN_START_BLOCK_NUMBER); - assert_ok!(Pallet::::create_swap( - mock::Origin::signed(THIS_CHAIN_ACCOUNT), - test_swap(), - Box::new(test_swap_creation()), - )); - }); - } - - #[test] - fn create_swap_succeeds() { - run_test(|| { - frame_system::Pallet::::set_block_number(1); - frame_system::Pallet::::reset_events(); - - assert_ok!(Pallet::::create_swap( - mock::Origin::signed(THIS_CHAIN_ACCOUNT), - test_swap(), - Box::new(test_swap_creation()), - )); - - let swap_hash = test_swap_hash(); - assert_eq!(PendingSwaps::::get(swap_hash), Some(TokenSwapState::Started)); - assert_eq!(PendingMessages::::get(MESSAGE_NONCE), Some(swap_hash)); - assert_eq!( - pallet_balances::Pallet::::free_balance(&swap_account_id::< - TestRuntime, - (), - >(&test_swap())), - test_swap().source_balance_at_this_chain + SWAP_DELIVERY_AND_DISPATCH_FEE, - ); - assert!( - frame_system::Pallet::::events().iter().any(|e| e.event == - crate::mock::Event::TokenSwap(crate::Event::SwapStarted( - swap_hash, - MESSAGE_NONCE, - ))), - "Missing SwapStarted event: {:?}", - frame_system::Pallet::::events(), - ); - }); - } - - #[test] - fn claim_swap_fails_if_origin_is_incorrect() { - run_test(|| { - assert_noop!( - Pallet::::claim_swap( - mock::Origin::signed( - 1 + target_account_at_this_chain::(&test_swap()) - ), - test_swap(), - ), - Error::::InvalidClaimant - ); - }); - } - - #[test] - fn claim_swap_fails_if_swap_is_pending() { - run_test(|| { - PendingSwaps::::insert(test_swap_hash(), TokenSwapState::Started); - - assert_noop!( - Pallet::::claim_swap( - mock::Origin::signed(target_account_at_this_chain::( - &test_swap() - )), - test_swap(), - ), - Error::::SwapIsPending - ); - }); - } - - #[test] - fn claim_swap_fails_if_swap_is_failed() { - run_test(|| { - PendingSwaps::::insert(test_swap_hash(), TokenSwapState::Failed); - - assert_noop!( - Pallet::::claim_swap( - mock::Origin::signed(target_account_at_this_chain::( - &test_swap() - )), - test_swap(), - ), - Error::::SwapIsFailed - ); - }); - } - - #[test] - fn claim_swap_fails_if_swap_is_inactive() { - run_test(|| { - assert_noop!( - Pallet::::claim_swap( - mock::Origin::signed(target_account_at_this_chain::( - &test_swap() - )), - test_swap(), - ), - Error::::SwapIsInactive - ); - }); - } - - #[test] - fn claim_swap_fails_if_currency_transfer_from_swap_account_fails() { - run_test(|| { - frame_system::Pallet::::set_block_number(CAN_CLAIM_BLOCK_NUMBER); - PendingSwaps::::insert(test_swap_hash(), TokenSwapState::Confirmed); - - assert_noop!( - Pallet::::claim_swap( - mock::Origin::signed(target_account_at_this_chain::( - &test_swap() - )), - test_swap(), - ), - Error::::FailedToTransferFromSwapAccount - ); - }); - } - - #[test] - fn claim_swap_fails_before_lock_period_is_completed() { - run_test(|| { - start_test_swap(); - receive_test_swap_confirmation(true); - - frame_system::Pallet::::set_block_number(CAN_CLAIM_BLOCK_NUMBER - 1); - - assert_noop!( - Pallet::::claim_swap( - mock::Origin::signed(target_account_at_this_chain::( - &test_swap() - )), - test_swap(), - ), - Error::::SwapIsTemporaryLocked - ); - }); - } - - #[test] - fn claim_swap_succeeds() { - run_test(|| { - start_test_swap(); - receive_test_swap_confirmation(true); - - frame_system::Pallet::::set_block_number(CAN_CLAIM_BLOCK_NUMBER); - frame_system::Pallet::::reset_events(); - - assert_ok!(Pallet::::claim_swap( - mock::Origin::signed(target_account_at_this_chain::(&test_swap())), - test_swap(), - )); - - let swap_hash = test_swap_hash(); - assert_eq!(PendingSwaps::::get(swap_hash), None); - assert_eq!( - pallet_balances::Pallet::::free_balance(&swap_account_id::< - TestRuntime, - (), - >(&test_swap())), - 0, - ); - assert_eq!( - pallet_balances::Pallet::::free_balance( - &target_account_at_this_chain::(&test_swap()), - ), - test_swap().source_balance_at_this_chain, - ); - assert!( - frame_system::Pallet::::events().iter().any(|e| e.event == - crate::mock::Event::TokenSwap(crate::Event::SwapClaimed(swap_hash,))), - "Missing SwapClaimed event: {:?}", - frame_system::Pallet::::events(), - ); - }); - } - - #[test] - fn cancel_swap_fails_if_origin_is_incorrect() { - run_test(|| { - start_test_swap(); - receive_test_swap_confirmation(false); - - assert_noop!( - Pallet::::cancel_swap( - mock::Origin::signed(THIS_CHAIN_ACCOUNT + 1), - test_swap() - ), - Error::::MismatchedSwapSourceOrigin - ); - }); - } - - #[test] - fn cancel_swap_fails_if_swap_is_pending() { - run_test(|| { - start_test_swap(); - - assert_noop!( - Pallet::::cancel_swap( - mock::Origin::signed(THIS_CHAIN_ACCOUNT), - test_swap() - ), - Error::::SwapIsPending - ); - }); - } - - #[test] - fn cancel_swap_fails_if_swap_is_confirmed() { - run_test(|| { - start_test_swap(); - receive_test_swap_confirmation(true); - - assert_noop!( - Pallet::::cancel_swap( - mock::Origin::signed(THIS_CHAIN_ACCOUNT), - test_swap() - ), - Error::::SwapIsConfirmed - ); - }); - } - - #[test] - fn cancel_swap_fails_if_swap_is_inactive() { - run_test(|| { - assert_noop!( - Pallet::::cancel_swap( - mock::Origin::signed(THIS_CHAIN_ACCOUNT), - test_swap() - ), - Error::::SwapIsInactive - ); - }); - } - - #[test] - fn cancel_swap_fails_if_currency_transfer_from_swap_account_fails() { - run_test(|| { - start_test_swap(); - receive_test_swap_confirmation(false); - let _ = pallet_balances::Pallet::::slash( - &swap_account_id::(&test_swap()), - test_swap().source_balance_at_this_chain, - ); - - assert_noop!( - Pallet::::cancel_swap( - mock::Origin::signed(THIS_CHAIN_ACCOUNT), - test_swap() - ), - Error::::FailedToTransferFromSwapAccount - ); - }); - } - - #[test] - fn cancel_swap_succeeds() { - run_test(|| { - start_test_swap(); - receive_test_swap_confirmation(false); - - frame_system::Pallet::::set_block_number(1); - frame_system::Pallet::::reset_events(); - - assert_ok!(Pallet::::cancel_swap( - mock::Origin::signed(THIS_CHAIN_ACCOUNT), - test_swap() - )); - - let swap_hash = test_swap_hash(); - assert_eq!(PendingSwaps::::get(swap_hash), None); - assert_eq!( - pallet_balances::Pallet::::free_balance(&swap_account_id::< - TestRuntime, - (), - >(&test_swap())), - 0, - ); - assert_eq!( - pallet_balances::Pallet::::free_balance(&THIS_CHAIN_ACCOUNT), - THIS_CHAIN_ACCOUNT_BALANCE - SWAP_DELIVERY_AND_DISPATCH_FEE, - ); - assert!( - frame_system::Pallet::::events().iter().any(|e| e.event == - crate::mock::Event::TokenSwap(crate::Event::SwapCanceled(swap_hash,))), - "Missing SwapCanceled event: {:?}", - frame_system::Pallet::::events(), - ); - }); - } - - #[test] - fn messages_delivery_confirmations_are_accepted() { - run_test(|| { - start_test_swap(); - assert_eq!( - PendingMessages::::get(MESSAGE_NONCE), - Some(test_swap_hash()) - ); - assert_eq!( - PendingSwaps::::get(test_swap_hash()), - Some(TokenSwapState::Started) - ); - - // when unrelated messages are delivered - let mut messages = DeliveredMessages::new(MESSAGE_NONCE - 2, true); - messages.note_dispatched_message(false); - Pallet::::on_messages_delivered( - &OutboundMessageLaneId::get(), - &messages, - ); - assert_eq!( - PendingMessages::::get(MESSAGE_NONCE), - Some(test_swap_hash()) - ); - assert_eq!( - PendingSwaps::::get(test_swap_hash()), - Some(TokenSwapState::Started) - ); - - // when message we're interested in is accompanied by a bunch of other messages - let mut messages = DeliveredMessages::new(MESSAGE_NONCE - 1, false); - messages.note_dispatched_message(true); - messages.note_dispatched_message(false); - Pallet::::on_messages_delivered( - &OutboundMessageLaneId::get(), - &messages, - ); - assert_eq!(PendingMessages::::get(MESSAGE_NONCE), None); - assert_eq!( - PendingSwaps::::get(test_swap_hash()), - Some(TokenSwapState::Confirmed) - ); - }); - } - - #[test] - fn storage_keys_computed_properly() { - assert_eq!( - PendingSwaps::::storage_map_final_key(test_swap_hash()), - bp_token_swap::storage_keys::pending_swaps_key("TokenSwap", test_swap_hash()).0, - ); - } -} diff --git a/bridges/modules/token-swap/src/mock.rs b/bridges/modules/token-swap/src/mock.rs deleted file mode 100644 index ece7b16acc91..000000000000 --- a/bridges/modules/token-swap/src/mock.rs +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -use crate as pallet_bridge_token_swap; -use crate::MessagePayloadOf; - -use bp_messages::{ - source_chain::{MessagesBridge, SendMessageArtifacts}, - LaneId, MessageNonce, -}; -use bp_runtime::ChainId; -use frame_support::weights::Weight; -use sp_core::H256; -use sp_runtime::{ - testing::Header as SubstrateHeader, - traits::{BlakeTwo256, IdentityLookup}, - Perbill, -}; - -pub type AccountId = u64; -pub type Balance = u64; -pub type Block = frame_system::mocking::MockBlock; -pub type BridgedAccountId = u64; -pub type BridgedAccountPublic = sp_runtime::testing::UintAuthorityId; -pub type BridgedAccountSignature = sp_runtime::testing::TestSignature; -pub type BridgedBalance = u64; -pub type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; - -pub const OK_TRANSFER_CALL: u8 = 1; -pub const BAD_TRANSFER_CALL: u8 = 2; -pub const MESSAGE_NONCE: MessageNonce = 3; - -pub const THIS_CHAIN_ACCOUNT: AccountId = 1; -pub const THIS_CHAIN_ACCOUNT_BALANCE: Balance = 100_000; - -pub const SWAP_DELIVERY_AND_DISPATCH_FEE: Balance = 1; - -frame_support::construct_runtime! { - pub enum TestRuntime where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Event}, - TokenSwap: pallet_bridge_token_swap::{Pallet, Call, Event, Origin}, - } -} - -frame_support::parameter_types! { - pub const BlockHashCount: u64 = 250; - pub const MaximumBlockWeight: Weight = 1024; - pub const MaximumBlockLength: u32 = 2 * 1024; - pub const AvailableBlockRatio: Perbill = Perbill::one(); -} - -impl frame_system::Config for TestRuntime { - type Origin = Origin; - type Index = u64; - type Call = Call; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type Header = SubstrateHeader; - type Event = Event; - type BlockHashCount = BlockHashCount; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type BaseCallFilter = frame_support::traits::Everything; - type SystemWeightInfo = (); - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; -} - -frame_support::parameter_types! { - pub const ExistentialDeposit: u64 = 10; - pub const MaxReserves: u32 = 50; -} - -impl pallet_balances::Config for TestRuntime { - type MaxLocks = (); - type Balance = Balance; - type DustRemoval = (); - type Event = Event; - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = frame_system::Pallet; - type WeightInfo = (); - type MaxReserves = MaxReserves; - type ReserveIdentifier = [u8; 8]; -} - -frame_support::parameter_types! { - pub const BridgedChainId: ChainId = *b"inst"; - pub const OutboundMessageLaneId: LaneId = *b"lane"; -} - -impl pallet_bridge_token_swap::Config for TestRuntime { - type Event = Event; - type WeightInfo = (); - - type BridgedChainId = BridgedChainId; - type OutboundMessageLaneId = OutboundMessageLaneId; - type MessagesBridge = TestMessagesBridge; - - type ThisCurrency = pallet_balances::Pallet; - type FromSwapToThisAccountIdConverter = TestAccountConverter; - - type BridgedChain = BridgedChain; - type FromBridgedToThisAccountIdConverter = TestAccountConverter; -} - -pub struct BridgedChain; - -impl bp_runtime::Chain for BridgedChain { - type BlockNumber = u64; - type Hash = H256; - type Hasher = BlakeTwo256; - type Header = sp_runtime::generic::Header; - - type AccountId = BridgedAccountId; - type Balance = BridgedBalance; - type Index = u64; - type Signature = BridgedAccountSignature; - - fn max_extrinsic_size() -> u32 { - unreachable!() - } - fn max_extrinsic_weight() -> Weight { - unreachable!() - } -} - -pub struct TestMessagesBridge; - -impl MessagesBridge> - for TestMessagesBridge -{ - type Error = (); - - fn send_message( - sender: Origin, - lane: LaneId, - message: MessagePayloadOf, - delivery_and_dispatch_fee: Balance, - ) -> Result { - assert_eq!(lane, OutboundMessageLaneId::get()); - assert_eq!(delivery_and_dispatch_fee, SWAP_DELIVERY_AND_DISPATCH_FEE); - match sender.caller { - OriginCaller::TokenSwap(_) => (), - _ => panic!("unexpected origin"), - } - match message.call[0] { - OK_TRANSFER_CALL => Ok(SendMessageArtifacts { nonce: MESSAGE_NONCE, weight: 0 }), - BAD_TRANSFER_CALL => Err(()), - _ => unreachable!(), - } - } -} - -pub struct TestAccountConverter; - -impl sp_runtime::traits::Convert for TestAccountConverter { - fn convert(hash: H256) -> AccountId { - hash.to_low_u64_ne() - } -} - -/// Run pallet test. -pub fn run_test(test: impl FnOnce() -> T) -> T { - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - pallet_balances::GenesisConfig:: { - balances: vec![(THIS_CHAIN_ACCOUNT, THIS_CHAIN_ACCOUNT_BALANCE)], - } - .assimilate_storage(&mut t) - .unwrap(); - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(test) -} diff --git a/bridges/modules/token-swap/src/weights.rs b/bridges/modules/token-swap/src/weights.rs deleted file mode 100644 index 51c5d99de9c5..000000000000 --- a/bridges/modules/token-swap/src/weights.rs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Autogenerated weights for `pallet_bridge_token_swap` -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2021-12-28, STEPS: 50, REPEAT: 20 -//! LOW RANGE: [], HIGH RANGE: [] -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled -//! CHAIN: Some("dev"), DB CACHE: 128 - -// Executed Command: -// target/release/millau-bridge-node -// benchmark -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=pallet_bridge_token_swap -// --extrinsic=* -// --execution=wasm -// --wasm-execution=Compiled -// --heap-pages=4096 -// --output=./modules/token-swap/src/weights.rs -// --template=./.maintain/millau-weight-template.hbs - -#![allow(clippy::all)] -#![allow(unused_parens)] -#![allow(unused_imports)] - -use frame_support::{ - traits::Get, - weights::{constants::RocksDbWeight, Weight}, -}; -use sp_std::marker::PhantomData; - -/// Weight functions needed for `pallet_bridge_token_swap`. -pub trait WeightInfo { - fn create_swap() -> Weight; - fn claim_swap() -> Weight; - fn cancel_swap() -> Weight; -} - -/// Weights for `pallet_bridge_token_swap` using the Millau node and recommended hardware. -pub struct MillauWeight(PhantomData); -impl WeightInfo for MillauWeight { - fn create_swap() -> Weight { - (90_368_000 as Weight) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(4 as Weight)) - } - fn claim_swap() -> Weight { - (88_397_000 as Weight) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) - } - fn cancel_swap() -> Weight { - (91_253_000 as Weight) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) - } -} - -// For backwards compatibility and tests -impl WeightInfo for () { - fn create_swap() -> Weight { - (90_368_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(4 as Weight)) - } - fn claim_swap() -> Weight { - (88_397_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) - } - fn cancel_swap() -> Weight { - (91_253_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) - } -} diff --git a/bridges/modules/token-swap/src/weights_ext.rs b/bridges/modules/token-swap/src/weights_ext.rs deleted file mode 100644 index 2d27c76cbe68..000000000000 --- a/bridges/modules/token-swap/src/weights_ext.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Weight-related utilities. - -use crate::weights::WeightInfo; - -use bp_runtime::Size; -use frame_support::weights::{RuntimeDbWeight, Weight}; - -/// Extended weight info. -pub trait WeightInfoExt: WeightInfo { - // Functions that are directly mapped to extrinsics weights. - - /// Weight of message send extrinsic. - fn send_message_weight(message: &impl Size, db_weight: RuntimeDbWeight) -> Weight; -} - -impl WeightInfoExt for () { - fn send_message_weight(message: &impl Size, db_weight: RuntimeDbWeight) -> Weight { - <() as pallet_bridge_messages::WeightInfoExt>::send_message_weight(message, db_weight) - } -} - -impl WeightInfoExt for crate::weights::MillauWeight { - fn send_message_weight(message: &impl Size, db_weight: RuntimeDbWeight) -> Weight { - <() as pallet_bridge_messages::WeightInfoExt>::send_message_weight(message, db_weight) - } -} diff --git a/bridges/primitives/chain-millau/src/lib.rs b/bridges/primitives/chain-millau/src/lib.rs index ff8d53859535..281ea471a2cf 100644 --- a/bridges/primitives/chain-millau/src/lib.rs +++ b/bridges/primitives/chain-millau/src/lib.rs @@ -269,9 +269,6 @@ pub const WITH_MILLAU_MESSAGES_PALLET_NAME: &str = "BridgeMillauMessages"; /// Name of the Rialto->Millau (actually DOT->KSM) conversion rate stored in the Millau runtime. pub const RIALTO_TO_MILLAU_CONVERSION_RATE_PARAMETER_NAME: &str = "RialtoToMillauConversionRate"; -/// Name of the With-Rialto token swap pallet instance in the Millau runtime. -pub const WITH_RIALTO_TOKEN_SWAP_PALLET_NAME: &str = "BridgeRialtoTokenSwap"; - /// Name of the `MillauFinalityApi::best_finalized` runtime method. pub const BEST_FINALIZED_MILLAU_HEADER_METHOD: &str = "MillauFinalityApi_best_finalized"; diff --git a/bridges/primitives/message-dispatch/Cargo.toml b/bridges/primitives/message-dispatch/Cargo.toml deleted file mode 100644 index 02b0912894da..000000000000 --- a/bridges/primitives/message-dispatch/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "bp-message-dispatch" -description = "Primitives of bridge messages dispatch modules." -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2021" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" - -[dependencies] -bp-runtime = { path = "../runtime", default-features = false } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } - -# Substrate Dependencies - -frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-runtime/std", - "codec/std", - "frame-support/std", - "scale-info/std", - "sp-std/std", -] diff --git a/bridges/primitives/message-dispatch/src/lib.rs b/bridges/primitives/message-dispatch/src/lib.rs deleted file mode 100644 index 07e448ee7ae6..000000000000 --- a/bridges/primitives/message-dispatch/src/lib.rs +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! A common interface for all Bridge Message Dispatch modules. - -#![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs)] - -use bp_runtime::{ - messages::{DispatchFeePayment, MessageDispatchResult}, - ChainId, Size, -}; -use codec::{Decode, Encode}; -use frame_support::RuntimeDebug; -use scale_info::TypeInfo; -use sp_std::prelude::*; - -/// Message dispatch weight. -pub type Weight = u64; - -/// Spec version type. -pub type SpecVersion = u32; - -/// A generic trait to dispatch arbitrary messages delivered over the bridge. -pub trait MessageDispatch { - /// A type of the message to be dispatched. - type Message: codec::Decode; - - /// Estimate dispatch weight. - /// - /// This function must: (1) be instant and (2) return correct upper bound - /// of dispatch weight. - fn dispatch_weight(message: &Self::Message) -> Weight; - - /// Dispatches the message internally. - /// - /// `source_chain` indicates the chain where the message came from. - /// `target_chain` indicates the chain where message dispatch happens. - /// - /// `id` is a short unique identifier of the message. - /// - /// If message is `Ok`, then it should be dispatched. If it is `Err`, then it's just - /// a sign that some other component has rejected the message even before it has - /// reached `dispatch` method (right now this may only be caused if we fail to decode - /// the whole message). - /// - /// Returns unspent dispatch weight. - fn dispatch Result<(), ()>>( - source_chain: ChainId, - target_chain: ChainId, - id: BridgeMessageId, - message: Result, - pay_dispatch_fee: P, - ) -> MessageDispatchResult; -} - -/// Origin of a Call when it is dispatched on the target chain. -/// -/// The source chain can (and should) verify that the message can be dispatched on the target chain -/// with a particular origin given the source chain's origin. This can be done with the -/// `verify_message_origin()` function. -#[derive(RuntimeDebug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] -pub enum CallOrigin { - /// Call is sent by the Root origin on the source chain. On the target chain it is dispatched - /// from a derived account. - /// - /// The derived account represents the source Root account on the target chain. This is useful - /// if the target chain needs some way of knowing that a call came from a privileged origin on - /// the source chain (maybe to allow a configuration change for example). - SourceRoot, - - /// Call is sent by `SourceChainAccountId` on the source chain. On the target chain it is - /// dispatched from an account controlled by a private key on the target chain. - /// - /// The account can be identified by `TargetChainAccountPublic`. The proof that the - /// `SourceChainAccountId` controls `TargetChainAccountPublic` is the `TargetChainSignature` - /// over `(Call, SourceChainAccountId, TargetChainSpecVersion, SourceChainBridgeId).encode()`. - /// - /// NOTE sending messages using this origin (or any other) does not have replay protection! - /// The assumption is that both the source account and the target account is controlled by - /// the same entity, so source-chain replay protection is sufficient. - /// As a consequence, it's extremely important for the target chain user to never produce - /// a signature with their target-private key on something that could be sent over the bridge, - /// i.e. if the target user signs `(, Call::Transfer(X, 5))` - /// The owner of `some-source-account-id` can send that message multiple times, which would - /// result with multiple transfer calls being dispatched on the target chain. - /// So please, NEVER USE YOUR PRIVATE KEY TO SIGN SOMETHING YOU DON'T FULLY UNDERSTAND! - TargetAccount(SourceChainAccountId, TargetChainAccountPublic, TargetChainSignature), - - /// Call is sent by the `SourceChainAccountId` on the source chain. On the target chain it is - /// dispatched from a derived account ID. - /// - /// The account ID on the target chain is derived from the source account ID. This is useful if - /// you need a way to represent foreign accounts on this chain for call dispatch purposes. - /// - /// Note that the derived account does not need to have a private key on the target chain. This - /// origin can therefore represent proxies, pallets, etc. as well as "regular" accounts. - SourceAccount(SourceChainAccountId), -} - -/// Message payload type used by dispatch module. -#[derive(RuntimeDebug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] -pub struct MessagePayload< - SourceChainAccountId, - TargetChainAccountPublic, - TargetChainSignature, - Call, -> { - /// Runtime specification version. We only dispatch messages that have the same - /// runtime version. Otherwise we risk to misinterpret encoded calls. - pub spec_version: SpecVersion, - /// Weight of the call, declared by the message sender. If it is less than actual - /// static weight, the call is not dispatched. - pub weight: Weight, - /// Call origin to be used during dispatch. - pub origin: CallOrigin, - /// Where the fee for dispatching message is paid? - pub dispatch_fee_payment: DispatchFeePayment, - /// The call itself. - pub call: Call, -} - -impl Size - for MessagePayload> -{ - fn size_hint(&self) -> u32 { - self.call.len() as _ - } -} diff --git a/bridges/primitives/runtime/src/lib.rs b/bridges/primitives/runtime/src/lib.rs index 1d8a40339ab0..da6f376ae4f4 100644 --- a/bridges/primitives/runtime/src/lib.rs +++ b/bridges/primitives/runtime/src/lib.rs @@ -136,15 +136,15 @@ pub trait Size { fn size_hint(&self) -> u32; } -impl Size for &[u8] { +impl Size for () { fn size_hint(&self) -> u32 { - self.len() as _ + 0 } } -impl Size for () { +impl Size for Vec { fn size_hint(&self) -> u32 { - 0 + self.len() as _ } } diff --git a/bridges/primitives/token-swap/Cargo.toml b/bridges/primitives/token-swap/Cargo.toml deleted file mode 100644 index 7ef5845d2aba..000000000000 --- a/bridges/primitives/token-swap/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "bp-token-swap" -description = "Primitives of the pallet-bridge-token-swap pallet" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2021" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } - -# Bridge Dependencies - -bp-runtime = { path = "../runtime", default-features = false } - -# Substrate Dependencies - -frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } - -[dev-dependencies] -hex = "0.4" -hex-literal = "0.3" - -[features] -default = ["std"] -std = [ - "bp-runtime/std", - "codec/std", - "frame-support/std", - "scale-info/std", - "sp-core/std", - "sp-io/std", - "sp-std/std", -] diff --git a/bridges/primitives/token-swap/src/lib.rs b/bridges/primitives/token-swap/src/lib.rs deleted file mode 100644 index 79363e5477a4..000000000000 --- a/bridges/primitives/token-swap/src/lib.rs +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -pub mod storage_keys; - -use codec::{Decode, Encode}; -use frame_support::{weights::Weight, RuntimeDebug}; -use scale_info::TypeInfo; -use sp_core::{H256, U256}; -use sp_io::hashing::blake2_256; -use sp_std::vec::Vec; - -/// Pending token swap state. -#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)] -pub enum TokenSwapState { - /// The swap has been started using the `start_claim` call, but we have no proof that it has - /// happened at the Bridged chain. - Started, - /// The swap has happened at the Bridged chain and may be claimed by the Bridged chain party - /// using the `claim_swap` call. - Confirmed, - /// The swap has failed at the Bridged chain and This chain party may cancel it using the - /// `cancel_swap` call. - Failed, -} - -/// Token swap type. -/// -/// Different swap types give a different guarantees regarding possible swap -/// replay protection. -#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)] -pub enum TokenSwapType { - /// The `target_account_at_bridged_chain` is temporary and only have funds for single swap. - /// - /// ***WARNING**: if `target_account_at_bridged_chain` still exists after the swap has been - /// completed (either by claiming or canceling), the `source_account_at_this_chain` will be - /// able to restart the swap again and repeat the swap until `target_account_at_bridged_chain` - /// depletes. - TemporaryTargetAccountAtBridgedChain, - /// This swap type prevents `source_account_at_this_chain` from restarting the swap after it - /// has been completed. There are two consequences: - /// - /// 1) the `source_account_at_this_chain` won't be able to call `start_swap` after given - /// ; 2) the `target_account_at_bridged_chain` won't be able to call - /// `claim_swap` (over the bridge) before block ``. - /// - /// The second element is the nonce of the swap. You must care about its uniqueness if you're - /// planning to perform another swap with exactly the same parameters (i.e. same amount, same - /// accounts, same `ThisBlockNumber`) to avoid collisions. - LockClaimUntilBlock(ThisBlockNumber, U256), -} - -/// An intention to swap `source_balance_at_this_chain` owned by `source_account_at_this_chain` -/// to `target_balance_at_bridged_chain` owned by `target_account_at_bridged_chain`. -/// -/// **IMPORTANT NOTE**: this structure is always the same during single token swap. So even -/// when chain changes, the meaning of This and Bridged are still used to point to the same chains. -/// This chain is always the chain where swap has been started. And the Bridged chain is the other -/// chain. -#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)] -pub struct TokenSwap -{ - /// The type of the swap. - pub swap_type: TokenSwapType, - /// This chain balance to be swapped with `target_balance_at_bridged_chain`. - pub source_balance_at_this_chain: ThisBalance, - /// Account id of the party acting at This chain and owning the `source_account_at_this_chain`. - pub source_account_at_this_chain: ThisAccountId, - /// Bridged chain balance to be swapped with `source_balance_at_this_chain`. - pub target_balance_at_bridged_chain: BridgedBalance, - /// Account id of the party acting at the Bridged chain and owning the - /// `target_balance_at_bridged_chain`. - pub target_account_at_bridged_chain: BridgedAccountId, -} - -impl - TokenSwap -where - TokenSwap: - Encode, -{ - /// Returns hash, used to identify this token swap. - pub fn hash(&self) -> H256 { - self.using_encoded(blake2_256).into() - } -} - -/// SCALE-encoded `Currency::transfer` call on the bridged chain. -pub type RawBridgedTransferCall = Vec; - -/// Token swap creation parameters. -#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)] -pub struct TokenSwapCreation { - /// Public key of the `target_account_at_bridged_chain` account used to verify - /// `bridged_currency_transfer_signature`. - pub target_public_at_bridged_chain: BridgedAccountPublic, - /// Fee that the `source_account_at_this_chain` is ready to pay for the tokens - /// transfer message delivery and dispatch. - pub swap_delivery_and_dispatch_fee: ThisChainBalance, - /// Specification version of the Bridged chain. - pub bridged_chain_spec_version: u32, - /// SCALE-encoded tokens transfer call at the Bridged chain. - pub bridged_currency_transfer: RawBridgedTransferCall, - /// Dispatch weight of the tokens transfer call at the Bridged chain. - pub bridged_currency_transfer_weight: Weight, - /// The signature of the `target_account_at_bridged_chain` for the message - /// returned by the `pallet_bridge_dispatch::account_ownership_digest()` function call. - pub bridged_currency_transfer_signature: BridgedAccountSignature, -} diff --git a/bridges/primitives/token-swap/src/storage_keys.rs b/bridges/primitives/token-swap/src/storage_keys.rs deleted file mode 100644 index d0aafc0d5c27..000000000000 --- a/bridges/primitives/token-swap/src/storage_keys.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Storage keys of bridge token swap pallet. - -use frame_support::Identity; -use sp_core::{storage::StorageKey, H256}; - -/// Name of the `PendingSwaps` storage map. -pub const PENDING_SWAPS_MAP_NAME: &str = "PendingSwaps"; - -/// Storage key of `PendingSwaps` value with given token swap hash. -pub fn pending_swaps_key(pallet_prefix: &str, token_swap_hash: H256) -> StorageKey { - bp_runtime::storage_map_final_key::( - pallet_prefix, - PENDING_SWAPS_MAP_NAME, - token_swap_hash.as_ref(), - ) -} - -#[cfg(test)] -mod tests { - use super::*; - use hex_literal::hex; - - #[test] - fn pending_swaps_key_computed_properly() { - // If this test fails, then something has been changed in module storage that may break - // all previous swaps. - let storage_key = pending_swaps_key("BridgeTokenSwap", [42u8; 32].into()).0; - assert_eq!( - storage_key, - hex!("76276da64e7a4f454760eedeb4bad11adca2227fef56ad07cc424f1f5d128b9a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a").to_vec(), - "Unexpected storage key: {}", - hex::encode(&storage_key), - ); - } -} diff --git a/bridges/relays/bin-substrate/Cargo.toml b/bridges/relays/bin-substrate/Cargo.toml index 018fbfd2dded..1d1166d1a9d1 100644 --- a/bridges/relays/bin-substrate/Cargo.toml +++ b/bridges/relays/bin-substrate/Cargo.toml @@ -25,24 +25,20 @@ strum = { version = "0.21.0", features = ["derive"] } bp-header-chain = { path = "../../primitives/header-chain" } bp-kusama = { path = "../../primitives/chain-kusama" } bp-messages = { path = "../../primitives/messages" } -bp-message-dispatch = { path = "../../primitives/message-dispatch" } bp-millau = { path = "../../primitives/chain-millau" } bp-polkadot = { path = "../../primitives/chain-polkadot" } bp-rialto = { path = "../../primitives/chain-rialto" } bp-rialto-parachain = { path = "../../primitives/chain-rialto-parachain" } bp-rococo = { path = "../../primitives/chain-rococo" } bp-runtime = { path = "../../primitives/runtime" } -bp-token-swap = { path = "../../primitives/token-swap" } bp-westend = { path = "../../primitives/chain-westend" } bp-wococo = { path = "../../primitives/chain-wococo" } bridge-runtime-common = { path = "../../bin/runtime-common" } finality-relay = { path = "../finality" } messages-relay = { path = "../messages" } millau-runtime = { path = "../../bin/millau/runtime" } -pallet-bridge-dispatch = { path = "../../modules/dispatch" } pallet-bridge-grandpa = { path = "../../modules/grandpa" } pallet-bridge-messages = { path = "../../modules/messages" } -pallet-bridge-token-swap = { path = "../../modules/token-swap" } relay-kusama-client = { path = "../client-kusama" } relay-millau-client = { path = "../client-millau" } relay-polkadot-client = { path = "../client-polkadot" } diff --git a/bridges/relays/bin-substrate/src/chains/kusama.rs b/bridges/relays/bin-substrate/src/chains/kusama.rs index 9cdc6cd125e0..2b18475d680a 100644 --- a/bridges/relays/bin-substrate/src/chains/kusama.rs +++ b/bridges/relays/bin-substrate/src/chains/kusama.rs @@ -14,82 +14,46 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -use anyhow::anyhow; -use bp_message_dispatch::{CallOrigin, MessagePayload}; +use bp_messages::LaneId; use bp_runtime::EncodedOrDecodedCall; -use codec::Decode; -use frame_support::weights::{DispatchClass, DispatchInfo, Pays, Weight}; use relay_kusama_client::Kusama; +use relay_substrate_client::BalanceOf; use sp_version::RuntimeVersion; use crate::cli::{ bridge, - encode_call::{self, Call, CliEncodeCall}, - encode_message, - send_message::{self, DispatchFeePayment}, + encode_message::{CliEncodeMessage, RawMessage}, CliChain, }; -/// Weight of the `system::remark` call at Kusama. -/// -/// This weight is larger (x2) than actual weight at current Kusama runtime to avoid unsuccessful -/// calls in the future. But since it is used only in tests (and on test chains), this is ok. -pub(crate) const SYSTEM_REMARK_CALL_WEIGHT: Weight = 2 * 1_345_000; - -impl CliEncodeCall for Kusama { - fn encode_call(call: &Call) -> anyhow::Result> { - Ok(match call { - Call::Raw { data } => EncodedOrDecodedCall::Encoded(data.0.clone()), - Call::Remark { remark_payload, .. } => relay_kusama_client::runtime::Call::System( - relay_kusama_client::runtime::SystemCall::remark( - remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(), - ), - ) - .into(), - Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } => - match *bridge_instance_index { - bridge::KUSAMA_TO_POLKADOT_INDEX => { - let payload = Decode::decode(&mut &*payload.0)?; - relay_kusama_client::runtime::Call::BridgePolkadotMessages( - relay_kusama_client::runtime::BridgePolkadotMessagesCall::send_message( - lane.0, payload, fee.0, - ), - ) - .into() - }, - _ => anyhow::bail!( - "Unsupported target bridge pallet with instance index: {}", - bridge_instance_index +impl CliEncodeMessage for Kusama { + fn encode_send_message_call( + lane: LaneId, + payload: RawMessage, + fee: BalanceOf, + bridge_instance_index: u8, + ) -> anyhow::Result> { + Ok(match bridge_instance_index { + bridge::KUSAMA_TO_POLKADOT_INDEX => + relay_kusama_client::runtime::Call::BridgePolkadotMessages( + relay_kusama_client::runtime::BridgePolkadotMessagesCall::send_message( + lane, payload, fee, ), - }, - _ => anyhow::bail!("Unsupported Kusama call: {:?}", call), + ) + .into(), + _ => anyhow::bail!( + "Unsupported target bridge pallet with instance index: {}", + bridge_instance_index + ), }) } - - fn get_dispatch_info(call: &EncodedOrDecodedCall) -> anyhow::Result { - match *call { - EncodedOrDecodedCall::Decoded(relay_kusama_client::runtime::Call::System( - relay_kusama_client::runtime::SystemCall::remark(_), - )) => Ok(DispatchInfo { - weight: crate::chains::kusama::SYSTEM_REMARK_CALL_WEIGHT, - class: DispatchClass::Normal, - pays_fee: Pays::Yes, - }), - _ => anyhow::bail!("Unsupported Kusama call: {:?}", call), - } - } } impl CliChain for Kusama { const RUNTIME_VERSION: RuntimeVersion = bp_kusama::VERSION; type KeyPair = sp_core::sr25519::Pair; - type MessagePayload = MessagePayload< - bp_kusama::AccountId, - bp_polkadot::AccountPublic, - bp_polkadot::Signature, - Vec, - >; + type MessagePayload = Vec; fn ss58_format() -> u16 { sp_core::crypto::Ss58AddressFormat::from( @@ -97,39 +61,4 @@ impl CliChain for Kusama { ) .into() } - - fn encode_message( - message: encode_message::MessagePayload, - ) -> anyhow::Result { - match message { - encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0) - .map_err(|e| anyhow!("Failed to decode Kusama's MessagePayload: {:?}", e)), - encode_message::MessagePayload::Call { mut call, mut sender, dispatch_weight } => { - type Source = Kusama; - type Target = relay_polkadot_client::Polkadot; - - sender.enforce_chain::(); - let spec_version = Target::RUNTIME_VERSION.spec_version; - let origin = CallOrigin::SourceAccount(sender.raw_id()); - encode_call::preprocess_call::( - &mut call, - bridge::KUSAMA_TO_POLKADOT_INDEX, - ); - let call = Target::encode_call(&call)?; - let dispatch_weight = dispatch_weight.map(Ok).unwrap_or_else(|| { - Err(anyhow::format_err!( - "Please specify dispatch weight of the encoded Polkadot call" - )) - })?; - - Ok(send_message::message_payload( - spec_version, - dispatch_weight, - origin, - &call, - DispatchFeePayment::AtSourceChain, - )) - }, - } - } } diff --git a/bridges/relays/bin-substrate/src/chains/millau.rs b/bridges/relays/bin-substrate/src/chains/millau.rs index 1fc1e8308ef4..a9e99771f592 100644 --- a/bridges/relays/bin-substrate/src/chains/millau.rs +++ b/bridges/relays/bin-substrate/src/chains/millau.rs @@ -18,106 +18,46 @@ use crate::cli::{ bridge, - encode_call::{self, Call, CliEncodeCall}, - encode_message, - send_message::{self, DispatchFeePayment}, + encode_message::{CliEncodeMessage, RawMessage}, CliChain, }; -use anyhow::anyhow; -use bp_message_dispatch::{CallOrigin, MessagePayload}; +use bp_messages::LaneId; use bp_runtime::EncodedOrDecodedCall; -use codec::Decode; -use frame_support::weights::{DispatchInfo, GetDispatchInfo}; use relay_millau_client::Millau; +use relay_substrate_client::BalanceOf; use sp_version::RuntimeVersion; -impl CliEncodeCall for Millau { - fn encode_call(call: &Call) -> anyhow::Result> { - Ok(match call { - Call::Raw { data } => Self::Call::decode(&mut &*data.0)?.into(), - Call::Remark { remark_payload, .. } => - millau_runtime::Call::System(millau_runtime::SystemCall::remark { - remark: remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(), - }) - .into(), - Call::Transfer { recipient, amount } => - millau_runtime::Call::Balances(millau_runtime::BalancesCall::transfer { - dest: recipient.raw_id(), - value: amount.cast(), - }) - .into(), - Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } => - match *bridge_instance_index { - bridge::MILLAU_TO_RIALTO_INDEX => { - let payload = Decode::decode(&mut &*payload.0)?; - millau_runtime::Call::BridgeRialtoMessages( - millau_runtime::MessagesCall::send_message { - lane_id: lane.0, - payload, - delivery_and_dispatch_fee: fee.cast(), - }, - ) - .into() - }, - _ => anyhow::bail!( - "Unsupported target bridge pallet with instance index: {}", - bridge_instance_index - ), +impl CliEncodeMessage for Millau { + fn encode_send_message_call( + lane: LaneId, + payload: RawMessage, + fee: BalanceOf, + bridge_instance_index: u8, + ) -> anyhow::Result> { + Ok(match bridge_instance_index { + bridge::MILLAU_TO_RIALTO_INDEX => millau_runtime::Call::BridgeRialtoMessages( + millau_runtime::MessagesCall::send_message { + lane_id: lane, + payload, + delivery_and_dispatch_fee: fee, }, + ) + .into(), + _ => anyhow::bail!( + "Unsupported target bridge pallet with instance index: {}", + bridge_instance_index + ), }) } - - fn get_dispatch_info(call: &EncodedOrDecodedCall) -> anyhow::Result { - Ok(call.to_decoded()?.get_dispatch_info()) - } } impl CliChain for Millau { const RUNTIME_VERSION: RuntimeVersion = millau_runtime::VERSION; type KeyPair = sp_core::sr25519::Pair; - type MessagePayload = MessagePayload< - bp_millau::AccountId, - bp_rialto::AccountSigner, - bp_rialto::Signature, - Vec, - >; + type MessagePayload = Vec; fn ss58_format() -> u16 { millau_runtime::SS58Prefix::get() as u16 } - - // TODO [#854|#843] support multiple bridges? - fn encode_message( - message: encode_message::MessagePayload, - ) -> anyhow::Result { - match message { - encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0) - .map_err(|e| anyhow!("Failed to decode Millau's MessagePayload: {:?}", e)), - encode_message::MessagePayload::Call { mut call, mut sender, dispatch_weight } => { - type Source = Millau; - type Target = relay_rialto_client::Rialto; - - sender.enforce_chain::(); - let spec_version = Target::RUNTIME_VERSION.spec_version; - let origin = CallOrigin::SourceAccount(sender.raw_id()); - encode_call::preprocess_call::( - &mut call, - bridge::MILLAU_TO_RIALTO_INDEX, - ); - let call = Target::encode_call(&call)?; - let dispatch_weight = dispatch_weight.map(Ok).unwrap_or_else(|| { - call.to_decoded().map(|call| call.get_dispatch_info().weight) - })?; - - Ok(send_message::message_payload( - spec_version, - dispatch_weight, - origin, - &call, - DispatchFeePayment::AtSourceChain, - )) - }, - } - } } diff --git a/bridges/relays/bin-substrate/src/chains/mod.rs b/bridges/relays/bin-substrate/src/chains/mod.rs index 16901143e19f..9b3416f901cf 100644 --- a/bridges/relays/bin-substrate/src/chains/mod.rs +++ b/bridges/relays/bin-substrate/src/chains/mod.rs @@ -41,95 +41,28 @@ mod wococo; #[cfg(test)] mod tests { - use crate::cli::{encode_call, send_message}; + use crate::cli::encode_message; use bp_messages::source_chain::TargetHeaderChain; use bp_runtime::Chain as _; use codec::Encode; - use frame_support::dispatch::GetDispatchInfo; use relay_millau_client::Millau; use relay_rialto_client::Rialto; use relay_substrate_client::{SignParam, TransactionSignScheme, UnsignedTransaction}; - use sp_core::Pair; - use sp_runtime::traits::{IdentifyAccount, Verify}; #[test] - fn millau_signature_is_valid_on_rialto() { - let millau_sign = relay_millau_client::SigningParams::from_string("//Dave", None).unwrap(); - - let call = - rialto_runtime::Call::System(rialto_runtime::SystemCall::remark { remark: vec![] }); - - let millau_public: bp_millau::AccountSigner = millau_sign.public().into(); - let millau_account_id: bp_millau::AccountId = millau_public.into_account(); - - let digest = millau_runtime::millau_to_rialto_account_ownership_digest( - &call, - millau_account_id, - rialto_runtime::VERSION.spec_version, - ); - - let rialto_signer = - relay_rialto_client::SigningParams::from_string("//Dave", None).unwrap(); - let signature = rialto_signer.sign(&digest); - - assert!(signature.verify(&digest[..], &rialto_signer.public())); - } - - #[test] - fn rialto_signature_is_valid_on_millau() { - let rialto_sign = relay_rialto_client::SigningParams::from_string("//Dave", None).unwrap(); - - let call = - millau_runtime::Call::System(millau_runtime::SystemCall::remark { remark: vec![] }); - - let rialto_public: bp_rialto::AccountSigner = rialto_sign.public().into(); - let rialto_account_id: bp_rialto::AccountId = rialto_public.into_account(); - - let digest = rialto_runtime::rialto_to_millau_account_ownership_digest( - &call, - rialto_account_id, - millau_runtime::VERSION.spec_version, - ); - - let millau_signer = - relay_millau_client::SigningParams::from_string("//Dave", None).unwrap(); - let signature = millau_signer.sign(&digest); - - assert!(signature.verify(&digest[..], &millau_signer.public())); - } - - #[test] - fn maximal_rialto_to_millau_message_arguments_size_is_computed_correctly() { + fn maximal_rialto_to_millau_message_size_is_computed_correctly() { use rialto_runtime::millau_messages::Millau; - let maximal_remark_size = encode_call::compute_maximal_message_arguments_size( + let maximal_message_size = encode_message::compute_maximal_message_size( bp_rialto::Rialto::max_extrinsic_size(), bp_millau::Millau::max_extrinsic_size(), ); - let call: millau_runtime::Call = - millau_runtime::SystemCall::remark { remark: vec![42; maximal_remark_size as _] } - .into(); - let payload = send_message::message_payload( - Default::default(), - call.get_dispatch_info().weight, - bp_message_dispatch::CallOrigin::SourceRoot, - &call, - send_message::DispatchFeePayment::AtSourceChain, - ); - assert_eq!(Millau::verify_message(&payload), Ok(())); + let message = vec![42; maximal_message_size as _]; + assert_eq!(Millau::verify_message(&message), Ok(())); - let call: millau_runtime::Call = - millau_runtime::SystemCall::remark { remark: vec![42; (maximal_remark_size + 1) as _] } - .into(); - let payload = send_message::message_payload( - Default::default(), - call.get_dispatch_info().weight, - bp_message_dispatch::CallOrigin::SourceRoot, - &call, - send_message::DispatchFeePayment::AtSourceChain, - ); - assert!(Millau::verify_message(&payload).is_err()); + let message = vec![42; (maximal_message_size + 1) as _]; + assert!(Millau::verify_message(&message).is_err()); } #[test] @@ -141,65 +74,6 @@ mod tests { "We can't actually send maximal messages to Rialto from Millau, because Millau extrinsics can't be that large", ) } - - #[test] - fn maximal_rialto_to_millau_message_dispatch_weight_is_computed_correctly() { - use rialto_runtime::millau_messages::Millau; - - let maximal_dispatch_weight = send_message::compute_maximal_message_dispatch_weight( - bp_millau::Millau::max_extrinsic_weight(), - ); - let call: millau_runtime::Call = - rialto_runtime::SystemCall::remark { remark: vec![] }.into(); - - let payload = send_message::message_payload( - Default::default(), - maximal_dispatch_weight, - bp_message_dispatch::CallOrigin::SourceRoot, - &call, - send_message::DispatchFeePayment::AtSourceChain, - ); - assert_eq!(Millau::verify_message(&payload), Ok(())); - - let payload = send_message::message_payload( - Default::default(), - maximal_dispatch_weight + 1, - bp_message_dispatch::CallOrigin::SourceRoot, - &call, - send_message::DispatchFeePayment::AtSourceChain, - ); - assert!(Millau::verify_message(&payload).is_err()); - } - - #[test] - fn maximal_weight_fill_block_to_rialto_is_generated_correctly() { - use millau_runtime::rialto_messages::Rialto; - - let maximal_dispatch_weight = send_message::compute_maximal_message_dispatch_weight( - bp_rialto::Rialto::max_extrinsic_weight(), - ); - let call: rialto_runtime::Call = - millau_runtime::SystemCall::remark { remark: vec![] }.into(); - - let payload = send_message::message_payload( - Default::default(), - maximal_dispatch_weight, - bp_message_dispatch::CallOrigin::SourceRoot, - &call, - send_message::DispatchFeePayment::AtSourceChain, - ); - assert_eq!(Rialto::verify_message(&payload), Ok(())); - - let payload = send_message::message_payload( - Default::default(), - maximal_dispatch_weight + 1, - bp_message_dispatch::CallOrigin::SourceRoot, - &call, - send_message::DispatchFeePayment::AtSourceChain, - ); - assert!(Rialto::verify_message(&payload).is_err()); - } - #[test] fn rialto_tx_extra_bytes_constant_is_correct() { let rialto_call = diff --git a/bridges/relays/bin-substrate/src/chains/polkadot.rs b/bridges/relays/bin-substrate/src/chains/polkadot.rs index 7ae1cbc47777..3ef7523f2a53 100644 --- a/bridges/relays/bin-substrate/src/chains/polkadot.rs +++ b/bridges/relays/bin-substrate/src/chains/polkadot.rs @@ -14,82 +14,46 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -use anyhow::anyhow; -use bp_message_dispatch::{CallOrigin, MessagePayload}; +use bp_messages::LaneId; use bp_runtime::EncodedOrDecodedCall; -use codec::Decode; -use frame_support::weights::{DispatchClass, DispatchInfo, Pays, Weight}; use relay_polkadot_client::Polkadot; +use relay_substrate_client::BalanceOf; use sp_version::RuntimeVersion; use crate::cli::{ bridge, - encode_call::{self, Call, CliEncodeCall}, - encode_message, - send_message::{self, DispatchFeePayment}, + encode_message::{CliEncodeMessage, RawMessage}, CliChain, }; -/// Weight of the `system::remark` call at Polkadot. -/// -/// This weight is larger (x2) than actual weight at current Polkadot runtime to avoid unsuccessful -/// calls in the future. But since it is used only in tests (and on test chains), this is ok. -pub(crate) const SYSTEM_REMARK_CALL_WEIGHT: Weight = 2 * 1_345_000; - -impl CliEncodeCall for Polkadot { - fn encode_call(call: &Call) -> anyhow::Result> { - Ok(match call { - Call::Raw { data } => EncodedOrDecodedCall::Encoded(data.0.clone()), - Call::Remark { remark_payload, .. } => relay_polkadot_client::runtime::Call::System( - relay_polkadot_client::runtime::SystemCall::remark( - remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(), - ), - ) - .into(), - Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } => - match *bridge_instance_index { - bridge::POLKADOT_TO_KUSAMA_INDEX => { - let payload = Decode::decode(&mut &*payload.0)?; - relay_polkadot_client::runtime::Call::BridgeKusamaMessages( - relay_polkadot_client::runtime::BridgeKusamaMessagesCall::send_message( - lane.0, payload, fee.0, - ), - ) - .into() - }, - _ => anyhow::bail!( - "Unsupported target bridge pallet with instance index: {}", - bridge_instance_index +impl CliEncodeMessage for Polkadot { + fn encode_send_message_call( + lane: LaneId, + payload: RawMessage, + fee: BalanceOf, + bridge_instance_index: u8, + ) -> anyhow::Result> { + Ok(match bridge_instance_index { + bridge::POLKADOT_TO_KUSAMA_INDEX => + relay_polkadot_client::runtime::Call::BridgeKusamaMessages( + relay_polkadot_client::runtime::BridgeKusamaMessagesCall::send_message( + lane, payload, fee, ), - }, - _ => anyhow::bail!("Unsupported Polkadot call: {:?}", call), + ) + .into(), + _ => anyhow::bail!( + "Unsupported target bridge pallet with instance index: {}", + bridge_instance_index + ), }) } - - fn get_dispatch_info(call: &EncodedOrDecodedCall) -> anyhow::Result { - match *call { - EncodedOrDecodedCall::Decoded(relay_polkadot_client::runtime::Call::System( - relay_polkadot_client::runtime::SystemCall::remark(_), - )) => Ok(DispatchInfo { - weight: crate::chains::polkadot::SYSTEM_REMARK_CALL_WEIGHT, - class: DispatchClass::Normal, - pays_fee: Pays::Yes, - }), - _ => anyhow::bail!("Unsupported Polkadot call: {:?}", call), - } - } } impl CliChain for Polkadot { const RUNTIME_VERSION: RuntimeVersion = bp_polkadot::VERSION; type KeyPair = sp_core::sr25519::Pair; - type MessagePayload = MessagePayload< - bp_polkadot::AccountId, - bp_kusama::AccountPublic, - bp_kusama::Signature, - Vec, - >; + type MessagePayload = Vec; fn ss58_format() -> u16 { sp_core::crypto::Ss58AddressFormat::from( @@ -97,39 +61,4 @@ impl CliChain for Polkadot { ) .into() } - - fn encode_message( - message: encode_message::MessagePayload, - ) -> anyhow::Result { - match message { - encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0) - .map_err(|e| anyhow!("Failed to decode Polkadot's MessagePayload: {:?}", e)), - encode_message::MessagePayload::Call { mut call, mut sender, dispatch_weight } => { - type Source = Polkadot; - type Target = relay_kusama_client::Kusama; - - sender.enforce_chain::(); - let spec_version = Target::RUNTIME_VERSION.spec_version; - let origin = CallOrigin::SourceAccount(sender.raw_id()); - encode_call::preprocess_call::( - &mut call, - bridge::POLKADOT_TO_KUSAMA_INDEX, - ); - let call = Target::encode_call(&call)?; - let dispatch_weight = dispatch_weight.map(Ok).unwrap_or_else(|| { - Err(anyhow::format_err!( - "Please specify dispatch weight of the encoded Kusama call" - )) - })?; - - Ok(send_message::message_payload( - spec_version, - dispatch_weight, - origin, - &call, - DispatchFeePayment::AtSourceChain, - )) - }, - } - } } diff --git a/bridges/relays/bin-substrate/src/chains/rialto.rs b/bridges/relays/bin-substrate/src/chains/rialto.rs index 8f26a64a4e32..0255a055a574 100644 --- a/bridges/relays/bin-substrate/src/chains/rialto.rs +++ b/bridges/relays/bin-substrate/src/chains/rialto.rs @@ -18,105 +18,46 @@ use crate::cli::{ bridge, - encode_call::{self, Call, CliEncodeCall}, - encode_message, - send_message::{self, DispatchFeePayment}, + encode_message::{CliEncodeMessage, RawMessage}, CliChain, }; -use anyhow::anyhow; -use bp_message_dispatch::{CallOrigin, MessagePayload}; +use bp_messages::LaneId; use bp_runtime::EncodedOrDecodedCall; -use codec::Decode; -use frame_support::weights::{DispatchInfo, GetDispatchInfo}; use relay_rialto_client::Rialto; +use relay_substrate_client::BalanceOf; use sp_version::RuntimeVersion; -impl CliEncodeCall for Rialto { - fn encode_call(call: &Call) -> anyhow::Result> { - Ok(match call { - Call::Raw { data } => Self::Call::decode(&mut &*data.0)?.into(), - Call::Remark { remark_payload, .. } => - rialto_runtime::Call::System(rialto_runtime::SystemCall::remark { - remark: remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(), - }) - .into(), - Call::Transfer { recipient, amount } => - rialto_runtime::Call::Balances(rialto_runtime::BalancesCall::transfer { - dest: recipient.raw_id().into(), - value: amount.0, - }) - .into(), - Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } => - match *bridge_instance_index { - bridge::RIALTO_TO_MILLAU_INDEX => { - let payload = Decode::decode(&mut &*payload.0)?; - rialto_runtime::Call::BridgeMillauMessages( - rialto_runtime::MessagesCall::send_message { - lane_id: lane.0, - payload, - delivery_and_dispatch_fee: fee.0, - }, - ) - .into() - }, - _ => anyhow::bail!( - "Unsupported target bridge pallet with instance index: {}", - bridge_instance_index - ), +impl CliEncodeMessage for Rialto { + fn encode_send_message_call( + lane: LaneId, + payload: RawMessage, + fee: BalanceOf, + bridge_instance_index: u8, + ) -> anyhow::Result> { + Ok(match bridge_instance_index { + bridge::RIALTO_TO_MILLAU_INDEX => rialto_runtime::Call::BridgeMillauMessages( + rialto_runtime::MessagesCall::send_message { + lane_id: lane, + payload, + delivery_and_dispatch_fee: fee, }, + ) + .into(), + _ => anyhow::bail!( + "Unsupported target bridge pallet with instance index: {}", + bridge_instance_index + ), }) } - - fn get_dispatch_info(call: &EncodedOrDecodedCall) -> anyhow::Result { - Ok(call.to_decoded()?.get_dispatch_info()) - } } impl CliChain for Rialto { const RUNTIME_VERSION: RuntimeVersion = rialto_runtime::VERSION; type KeyPair = sp_core::sr25519::Pair; - type MessagePayload = MessagePayload< - bp_rialto::AccountId, - bp_millau::AccountSigner, - bp_millau::Signature, - Vec, - >; + type MessagePayload = Vec; fn ss58_format() -> u16 { rialto_runtime::SS58Prefix::get() as u16 } - - fn encode_message( - message: encode_message::MessagePayload, - ) -> anyhow::Result { - match message { - encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0) - .map_err(|e| anyhow!("Failed to decode Rialto's MessagePayload: {:?}", e)), - encode_message::MessagePayload::Call { mut call, mut sender, dispatch_weight } => { - type Source = Rialto; - type Target = relay_millau_client::Millau; - - sender.enforce_chain::(); - let spec_version = Target::RUNTIME_VERSION.spec_version; - let origin = CallOrigin::SourceAccount(sender.raw_id()); - encode_call::preprocess_call::( - &mut call, - bridge::RIALTO_TO_MILLAU_INDEX, - ); - let call = Target::encode_call(&call)?; - let dispatch_weight = dispatch_weight.map(Ok).unwrap_or_else(|| { - call.to_decoded().map(|call| call.get_dispatch_info().weight) - })?; - - Ok(send_message::message_payload( - spec_version, - dispatch_weight, - origin, - &call, - DispatchFeePayment::AtSourceChain, - )) - }, - } - } } diff --git a/bridges/relays/bin-substrate/src/chains/rialto_parachain.rs b/bridges/relays/bin-substrate/src/chains/rialto_parachain.rs index 0ed39faa543b..8e87c3ede706 100644 --- a/bridges/relays/bin-substrate/src/chains/rialto_parachain.rs +++ b/bridges/relays/bin-substrate/src/chains/rialto_parachain.rs @@ -16,63 +16,17 @@ //! Rialto parachain specification for CLI. -use crate::cli::{ - encode_call::{Call, CliEncodeCall}, - encode_message, CliChain, -}; -use bp_message_dispatch::MessagePayload; -use bp_runtime::EncodedOrDecodedCall; -use codec::Decode; -use frame_support::weights::{DispatchInfo, GetDispatchInfo}; +use crate::cli::CliChain; use relay_rialto_parachain_client::RialtoParachain; use sp_version::RuntimeVersion; -impl CliEncodeCall for RialtoParachain { - fn encode_call(call: &Call) -> anyhow::Result> { - Ok(match call { - Call::Raw { data } => Self::Call::decode(&mut &*data.0)?.into(), - Call::Remark { remark_payload, .. } => rialto_parachain_runtime::Call::System( - rialto_parachain_runtime::SystemCall::remark { - remark: remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(), - }, - ) - .into(), - Call::Transfer { recipient, amount } => rialto_parachain_runtime::Call::Balances( - rialto_parachain_runtime::BalancesCall::transfer { - dest: recipient.raw_id().into(), - value: amount.0, - }, - ) - .into(), - Call::BridgeSendMessage { .. } => { - anyhow::bail!("Bridge messages are not (yet) supported here",) - }, - }) - } - - fn get_dispatch_info(call: &EncodedOrDecodedCall) -> anyhow::Result { - Ok(call.to_decoded()?.get_dispatch_info()) - } -} - impl CliChain for RialtoParachain { const RUNTIME_VERSION: RuntimeVersion = rialto_parachain_runtime::VERSION; type KeyPair = sp_core::sr25519::Pair; - type MessagePayload = MessagePayload< - bp_rialto_parachain::AccountId, - bp_millau::AccountSigner, - bp_millau::Signature, - Vec, - >; + type MessagePayload = Vec; fn ss58_format() -> u16 { rialto_parachain_runtime::SS58Prefix::get() as u16 } - - fn encode_message( - _message: encode_message::MessagePayload, - ) -> anyhow::Result { - anyhow::bail!("Not supported") - } } diff --git a/bridges/relays/bin-substrate/src/chains/rococo.rs b/bridges/relays/bin-substrate/src/chains/rococo.rs index ceef4c1f532c..0d2b800e79bf 100644 --- a/bridges/relays/bin-substrate/src/chains/rococo.rs +++ b/bridges/relays/bin-substrate/src/chains/rococo.rs @@ -14,119 +14,47 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -use anyhow::anyhow; -use bp_message_dispatch::{CallOrigin, MessagePayload}; +use bp_messages::LaneId; use bp_runtime::EncodedOrDecodedCall; -use codec::Decode; -use frame_support::weights::{DispatchClass, DispatchInfo, Pays, Weight}; use relay_rococo_client::Rococo; +use relay_substrate_client::BalanceOf; use sp_version::RuntimeVersion; use crate::cli::{ bridge, - encode_call::{self, Call, CliEncodeCall}, - encode_message, - send_message::{self, DispatchFeePayment}, + encode_message::{CliEncodeMessage, RawMessage}, CliChain, }; -/// Weight of the `system::remark` call at Rococo. -/// -/// This weight is larger (x2) than actual weight at current Rococo runtime to avoid unsuccessful -/// calls in the future. But since it is used only in tests (and on test chains), this is ok. -pub(crate) const SYSTEM_REMARK_CALL_WEIGHT: Weight = 2 * 1_345_000; - -impl CliEncodeCall for Rococo { - fn encode_call(call: &Call) -> anyhow::Result> { - Ok(match call { - Call::Raw { data } => EncodedOrDecodedCall::Encoded(data.0.clone()), - Call::Remark { remark_payload, .. } => relay_rococo_client::runtime::Call::System( - relay_rococo_client::runtime::SystemCall::remark( - remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(), - ), - ) - .into(), - Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } => - match *bridge_instance_index { - bridge::ROCOCO_TO_WOCOCO_INDEX => { - let payload = Decode::decode(&mut &*payload.0)?; - relay_rococo_client::runtime::Call::BridgeWococoMessages( - relay_rococo_client::runtime::BridgeWococoMessagesCall::send_message( - lane.0, payload, fee.0, - ), - ) - .into() - }, - _ => anyhow::bail!( - "Unsupported target bridge pallet with instance index: {}", - bridge_instance_index +impl CliEncodeMessage for Rococo { + fn encode_send_message_call( + lane: LaneId, + payload: RawMessage, + fee: BalanceOf, + bridge_instance_index: u8, + ) -> anyhow::Result> { + Ok(match bridge_instance_index { + bridge::ROCOCO_TO_WOCOCO_INDEX => + relay_rococo_client::runtime::Call::BridgeWococoMessages( + relay_rococo_client::runtime::BridgeWococoMessagesCall::send_message( + lane, payload, fee, ), - }, - _ => anyhow::bail!("The call is not supported"), + ) + .into(), + _ => anyhow::bail!( + "Unsupported target bridge pallet with instance index: {}", + bridge_instance_index + ), }) } - - fn get_dispatch_info(call: &EncodedOrDecodedCall) -> anyhow::Result { - match *call { - EncodedOrDecodedCall::Decoded(relay_rococo_client::runtime::Call::System( - relay_rococo_client::runtime::SystemCall::remark(_), - )) => Ok(DispatchInfo { - weight: SYSTEM_REMARK_CALL_WEIGHT, - class: DispatchClass::Normal, - pays_fee: Pays::Yes, - }), - _ => anyhow::bail!("Unsupported Rococo call: {:?}", call), - } - } } - impl CliChain for Rococo { const RUNTIME_VERSION: RuntimeVersion = bp_rococo::VERSION; type KeyPair = sp_core::sr25519::Pair; - type MessagePayload = MessagePayload< - bp_rococo::AccountId, - bp_wococo::AccountPublic, - bp_wococo::Signature, - Vec, - >; + type MessagePayload = Vec; fn ss58_format() -> u16 { 42 } - - fn encode_message( - message: encode_message::MessagePayload, - ) -> anyhow::Result { - match message { - encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0) - .map_err(|e| anyhow!("Failed to decode Rococo's MessagePayload: {:?}", e)), - encode_message::MessagePayload::Call { mut call, mut sender, dispatch_weight } => { - type Source = Rococo; - type Target = relay_wococo_client::Wococo; - - sender.enforce_chain::(); - let spec_version = Target::RUNTIME_VERSION.spec_version; - let origin = CallOrigin::SourceAccount(sender.raw_id()); - encode_call::preprocess_call::( - &mut call, - bridge::ROCOCO_TO_WOCOCO_INDEX, - ); - let call = Target::encode_call(&call)?; - let dispatch_weight = dispatch_weight.map(Ok).unwrap_or_else(|| { - Err(anyhow::format_err!( - "Please specify dispatch weight of the encoded Wococo call" - )) - })?; - - Ok(send_message::message_payload( - spec_version, - dispatch_weight, - origin, - &call, - DispatchFeePayment::AtSourceChain, - )) - }, - } - } } diff --git a/bridges/relays/bin-substrate/src/chains/westend.rs b/bridges/relays/bin-substrate/src/chains/westend.rs index 8d3b5db9ab37..eb578ddb7834 100644 --- a/bridges/relays/bin-substrate/src/chains/westend.rs +++ b/bridges/relays/bin-substrate/src/chains/westend.rs @@ -16,8 +16,7 @@ //! Westend chain specification for CLI. -use crate::cli::{encode_message, CliChain}; -use anyhow::anyhow; +use crate::cli::CliChain; use relay_westend_client::Westend; use sp_version::RuntimeVersion; @@ -25,7 +24,7 @@ impl CliChain for Westend { const RUNTIME_VERSION: RuntimeVersion = bp_westend::VERSION; type KeyPair = sp_core::sr25519::Pair; - type MessagePayload = (); + type MessagePayload = Vec; fn ss58_format() -> u16 { sp_core::crypto::Ss58AddressFormat::from( @@ -33,10 +32,4 @@ impl CliChain for Westend { ) .into() } - - fn encode_message( - _message: encode_message::MessagePayload, - ) -> anyhow::Result { - Err(anyhow!("Sending messages from Westend is not yet supported.")) - } } diff --git a/bridges/relays/bin-substrate/src/chains/wococo.rs b/bridges/relays/bin-substrate/src/chains/wococo.rs index 46dec2a3c90e..8edb132a83ab 100644 --- a/bridges/relays/bin-substrate/src/chains/wococo.rs +++ b/bridges/relays/bin-substrate/src/chains/wococo.rs @@ -14,113 +14,48 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -use anyhow::anyhow; -use bp_message_dispatch::{CallOrigin, MessagePayload}; +use bp_messages::LaneId; use bp_runtime::EncodedOrDecodedCall; -use codec::Decode; -use frame_support::weights::{DispatchClass, DispatchInfo, Pays}; +use relay_substrate_client::BalanceOf; use relay_wococo_client::Wococo; use sp_version::RuntimeVersion; use crate::cli::{ bridge, - encode_call::{self, Call, CliEncodeCall}, - encode_message, - send_message::{self, DispatchFeePayment}, + encode_message::{CliEncodeMessage, RawMessage}, CliChain, }; -impl CliEncodeCall for Wococo { - fn encode_call(call: &Call) -> anyhow::Result> { - Ok(match call { - Call::Raw { data } => EncodedOrDecodedCall::Encoded(data.0.clone()), - Call::Remark { remark_payload, .. } => relay_wococo_client::runtime::Call::System( - relay_wococo_client::runtime::SystemCall::remark( - remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(), - ), - ) - .into(), - Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } => - match *bridge_instance_index { - bridge::WOCOCO_TO_ROCOCO_INDEX => { - let payload = Decode::decode(&mut &*payload.0)?; - relay_wococo_client::runtime::Call::BridgeRococoMessages( - relay_wococo_client::runtime::BridgeRococoMessagesCall::send_message( - lane.0, payload, fee.0, - ), - ) - .into() - }, - _ => anyhow::bail!( - "Unsupported target bridge pallet with instance index: {}", - bridge_instance_index +impl CliEncodeMessage for Wococo { + fn encode_send_message_call( + lane: LaneId, + payload: RawMessage, + fee: BalanceOf, + bridge_instance_index: u8, + ) -> anyhow::Result> { + Ok(match bridge_instance_index { + bridge::WOCOCO_TO_ROCOCO_INDEX => + relay_wococo_client::runtime::Call::BridgeRococoMessages( + relay_wococo_client::runtime::BridgeRococoMessagesCall::send_message( + lane, payload, fee, ), - }, - _ => anyhow::bail!("The call is not supported"), + ) + .into(), + _ => anyhow::bail!( + "Unsupported target bridge pallet with instance index: {}", + bridge_instance_index + ), }) } - - fn get_dispatch_info(call: &EncodedOrDecodedCall) -> anyhow::Result { - match *call { - EncodedOrDecodedCall::Decoded(relay_wococo_client::runtime::Call::System( - relay_wococo_client::runtime::SystemCall::remark(_), - )) => Ok(DispatchInfo { - weight: crate::chains::rococo::SYSTEM_REMARK_CALL_WEIGHT, - class: DispatchClass::Normal, - pays_fee: Pays::Yes, - }), - _ => anyhow::bail!("Unsupported Wococo call: {:?}", call), - } - } } impl CliChain for Wococo { const RUNTIME_VERSION: RuntimeVersion = bp_wococo::VERSION; type KeyPair = sp_core::sr25519::Pair; - type MessagePayload = MessagePayload< - bp_wococo::AccountId, - bp_rococo::AccountPublic, - bp_rococo::Signature, - Vec, - >; + type MessagePayload = Vec; fn ss58_format() -> u16 { 42 } - - fn encode_message( - message: encode_message::MessagePayload, - ) -> anyhow::Result { - match message { - encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0) - .map_err(|e| anyhow!("Failed to decode Wococo's MessagePayload: {:?}", e)), - encode_message::MessagePayload::Call { mut call, mut sender, dispatch_weight } => { - type Source = Wococo; - type Target = relay_rococo_client::Rococo; - - sender.enforce_chain::(); - let spec_version = Target::RUNTIME_VERSION.spec_version; - let origin = CallOrigin::SourceAccount(sender.raw_id()); - encode_call::preprocess_call::( - &mut call, - bridge::WOCOCO_TO_ROCOCO_INDEX, - ); - let call = Target::encode_call(&call)?; - let dispatch_weight = dispatch_weight.map(Ok).unwrap_or_else(|| { - Err(anyhow::format_err!( - "Please specify dispatch weight of the encoded Rococo call" - )) - })?; - - Ok(send_message::message_payload( - spec_version, - dispatch_weight, - origin, - &call, - DispatchFeePayment::AtSourceChain, - )) - }, - } - } } diff --git a/bridges/relays/bin-substrate/src/cli/bridge.rs b/bridges/relays/bin-substrate/src/cli/bridge.rs index 2eb836a84a75..c8476725fff2 100644 --- a/bridges/relays/bin-substrate/src/cli/bridge.rs +++ b/bridges/relays/bin-substrate/src/cli/bridge.rs @@ -73,12 +73,9 @@ macro_rules! select_full_bridge { // Send-message / Estimate-fee #[allow(unused_imports)] use bp_rialto::TO_RIALTO_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD; - // Send-message - #[allow(unused_imports)] - use millau_runtime::millau_to_rialto_account_ownership_digest as account_ownership_digest; $generic - } + }, FullBridge::RialtoToMillau => { type Source = relay_rialto_client::Rialto; #[allow(dead_code)] @@ -96,12 +93,8 @@ macro_rules! select_full_bridge { #[allow(unused_imports)] use bp_millau::TO_MILLAU_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD; - // Send-message - #[allow(unused_imports)] - use rialto_runtime::rialto_to_millau_account_ownership_digest as account_ownership_digest; - $generic - } + }, FullBridge::RococoToWococo => { type Source = relay_rococo_client::Rococo; #[allow(dead_code)] @@ -118,12 +111,9 @@ macro_rules! select_full_bridge { // Send-message / Estimate-fee #[allow(unused_imports)] use bp_wococo::TO_WOCOCO_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD; - // Send-message - #[allow(unused_imports)] - use relay_rococo_client::runtime::rococo_to_wococo_account_ownership_digest as account_ownership_digest; $generic - } + }, FullBridge::WococoToRococo => { type Source = relay_wococo_client::Wococo; #[allow(dead_code)] @@ -140,12 +130,9 @@ macro_rules! select_full_bridge { // Send-message / Estimate-fee #[allow(unused_imports)] use bp_rococo::TO_ROCOCO_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD; - // Send-message - #[allow(unused_imports)] - use relay_wococo_client::runtime::wococo_to_rococo_account_ownership_digest as account_ownership_digest; $generic - } + }, FullBridge::KusamaToPolkadot => { type Source = relay_kusama_client::Kusama; #[allow(dead_code)] @@ -162,12 +149,9 @@ macro_rules! select_full_bridge { // Send-message / Estimate-fee #[allow(unused_imports)] use bp_polkadot::TO_POLKADOT_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD; - // Send-message - #[allow(unused_imports)] - use relay_kusama_client::runtime::kusama_to_polkadot_account_ownership_digest as account_ownership_digest; $generic - } + }, FullBridge::PolkadotToKusama => { type Source = relay_polkadot_client::Polkadot; #[allow(dead_code)] @@ -184,12 +168,9 @@ macro_rules! select_full_bridge { // Send-message / Estimate-fee #[allow(unused_imports)] use bp_kusama::TO_KUSAMA_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD; - // Send-message - #[allow(unused_imports)] - use relay_polkadot_client::runtime::polkadot_to_kusama_account_ownership_digest as account_ownership_digest; $generic - } + }, } }; } diff --git a/bridges/relays/bin-substrate/src/cli/derive_account.rs b/bridges/relays/bin-substrate/src/cli/derive_account.rs deleted file mode 100644 index 5b809eb69f22..000000000000 --- a/bridges/relays/bin-substrate/src/cli/derive_account.rs +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -use crate::{ - cli::{bridge::FullBridge, AccountId}, - select_full_bridge, -}; -use relay_substrate_client::Chain; -use structopt::StructOpt; -use strum::VariantNames; - -/// Given a source chain `AccountId`, derive the corresponding `AccountId` for the target chain. -/// -/// The (derived) target chain `AccountId` is going to be used as dispatch origin of the call -/// that has been sent over the bridge. -/// This account can also be used to receive target-chain funds (or other form of ownership), -/// since messages sent over the bridge will be able to spend these. -#[derive(StructOpt)] -pub struct DeriveAccount { - /// A bridge instance to initialize. - #[structopt(possible_values = FullBridge::VARIANTS, case_insensitive = true)] - bridge: FullBridge, - /// Source-chain address to derive Target-chain address from. - account: AccountId, -} - -impl DeriveAccount { - /// Parse CLI arguments and derive account. - /// - /// Returns both the Source account in correct SS58 format and the derived account. - fn derive_account(&self) -> (AccountId, AccountId) { - select_full_bridge!(self.bridge, { - let mut account = self.account.clone(); - account.enforce_chain::(); - let acc = bp_runtime::SourceAccount::Account(account.raw_id()); - let id = derive_account(acc); - let derived_account = AccountId::from_raw::(id); - (account, derived_account) - }) - } - - /// Run the command. - pub async fn run(self) -> anyhow::Result<()> { - select_full_bridge!(self.bridge, { - let (account, derived_account) = self.derive_account(); - println!("Source address:\n{} ({})", account, Source::NAME); - println!("->Corresponding (derived) address:\n{} ({})", derived_account, Target::NAME,); - - Ok(()) - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn derive_account_cli(bridge: &str, account: &str) -> (AccountId, AccountId) { - DeriveAccount::from_iter(vec!["derive-account", bridge, account]).derive_account() - } - - #[test] - fn should_derive_accounts_correctly() { - // given - let rialto = "5sauUXUfPjmwxSgmb3tZ5d6yx24eZX4wWJ2JtVUBaQqFbvEU"; - let millau = "752paRyW1EGfq9YLTSSqcSJ5hqnBDidBmaftGhBo8fy6ypW9"; - - // when - let (rialto_parsed, rialto_derived) = derive_account_cli("rialto-to-millau", rialto); - let (millau_parsed, millau_derived) = derive_account_cli("millau-to-rialto", millau); - let (millau2_parsed, millau2_derived) = derive_account_cli("millau-to-rialto", rialto); - - // then - assert_eq!(format!("{}", rialto_parsed), rialto); - assert_eq!(format!("{}", millau_parsed), millau); - assert_eq!(format!("{}", millau2_parsed), millau); - - assert_eq!( - format!("{}", rialto_derived), - "74GNQjmkcfstRftSQPJgMREchqHM56EvAUXRc266cZ1NYVW5" - ); - assert_eq!( - format!("{}", millau_derived), - "5rERgaT1Z8nM3et2epA5i1VtEBfp5wkhwHtVE8HK7BRbjAH2" - ); - assert_eq!(millau_derived, millau2_derived); - } -} diff --git a/bridges/relays/bin-substrate/src/cli/encode_call.rs b/bridges/relays/bin-substrate/src/cli/encode_call.rs deleted file mode 100644 index e288e2c13d6c..000000000000 --- a/bridges/relays/bin-substrate/src/cli/encode_call.rs +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -use crate::{ - cli::{ - bridge::FullBridge, AccountId, Balance, CliChain, ExplicitOrMaximal, HexBytes, HexLaneId, - }, - select_full_bridge, -}; -use bp_runtime::EncodedOrDecodedCall; -use frame_support::weights::DispatchInfo; -use relay_substrate_client::Chain; -use structopt::StructOpt; -use strum::VariantNames; - -/// Encode source chain runtime call. -#[derive(StructOpt, Debug)] -pub struct EncodeCall { - /// A bridge instance to encode call for. - #[structopt(possible_values = FullBridge::VARIANTS, case_insensitive = true)] - bridge: FullBridge, - #[structopt(flatten)] - call: Call, -} - -/// All possible messages that may be delivered to generic Substrate chain. -/// -/// Note this enum may be used in the context of both Source (as part of `encode-call`) -/// and Target chain (as part of `encode-message/send-message`). -#[derive(StructOpt, Debug, PartialEq, Eq)] -pub enum Call { - /// Raw bytes for the message - Raw { - /// Raw, SCALE-encoded message - data: HexBytes, - }, - /// Make an on-chain remark (comment). - Remark { - /// Explicit remark payload. - #[structopt(long, conflicts_with("remark-size"))] - remark_payload: Option, - /// Remark size. If not passed, small UTF8-encoded string is generated by relay as remark. - #[structopt(long, conflicts_with("remark-payload"))] - remark_size: Option>, - }, - /// Transfer the specified `amount` of native tokens to a particular `recipient`. - Transfer { - /// Address of an account to receive the transfer. - #[structopt(long)] - recipient: AccountId, - /// Amount of target tokens to send in target chain base currency units. - #[structopt(long)] - amount: Balance, - }, - /// A call to the specific Bridge Messages pallet to queue message to be sent over a bridge. - BridgeSendMessage { - /// An index of the bridge instance which represents the expected target chain. - #[structopt(skip = 255)] - bridge_instance_index: u8, - /// Hex-encoded lane id that should be served by the relay. Defaults to `00000000`. - #[structopt(long, default_value = "00000000")] - lane: HexLaneId, - /// Raw SCALE-encoded Message Payload to submit to the messages pallet. - /// - /// This can be obtained by encoding call for the target chain. - #[structopt(long)] - payload: HexBytes, - /// Declared delivery and dispatch fee in base source-chain currency units. - #[structopt(long)] - fee: Balance, - }, -} - -pub trait CliEncodeCall: Chain { - /// Encode a CLI call. - fn encode_call(call: &Call) -> anyhow::Result>; - - /// Get dispatch info for the call. - fn get_dispatch_info(call: &EncodedOrDecodedCall) -> anyhow::Result; -} - -impl EncodeCall { - fn encode(&mut self) -> anyhow::Result { - select_full_bridge!(self.bridge, { - preprocess_call::(&mut self.call, self.bridge.bridge_instance_index()); - let call = Source::encode_call(&self.call)?; - - let encoded = HexBytes::encode(&call); - - log::info!(target: "bridge", "Generated {} call: {:#?}", Source::NAME, call); - log::info!(target: "bridge", "Weight of {} call: {}", Source::NAME, Source::get_dispatch_info(&call) - .map(|dispatch_info| format!("{}", dispatch_info.weight)) - .unwrap_or_else(|_| "".to_string()) - ); - log::info!(target: "bridge", "Encoded {} call: {:?}", Source::NAME, encoded); - - Ok(encoded) - }) - } - - /// Run the command. - pub async fn run(mut self) -> anyhow::Result<()> { - println!("{:?}", self.encode()?); - Ok(()) - } -} - -/// Prepare the call to be passed to [`CliEncodeCall::encode_call`]. -/// -/// This function will fill in all optional and missing pieces and will make sure that -/// values are converted to bridge-specific ones. -/// -/// Most importantly, the method will fill-in [`bridge_instance_index`] parameter for -/// target-chain specific calls. -pub(crate) fn preprocess_call( - call: &mut Call, - bridge_instance: u8, -) { - match *call { - Call::Raw { .. } => {}, - Call::Remark { ref remark_size, ref mut remark_payload } => - if remark_payload.is_none() { - *remark_payload = Some(HexBytes(generate_remark_payload( - remark_size, - compute_maximal_message_arguments_size( - Source::max_extrinsic_size(), - Target::max_extrinsic_size(), - ), - ))); - }, - Call::Transfer { ref mut recipient, .. } => { - recipient.enforce_chain::(); - }, - Call::BridgeSendMessage { ref mut bridge_instance_index, .. } => { - *bridge_instance_index = bridge_instance; - }, - }; -} - -fn generate_remark_payload( - remark_size: &Option>, - maximal_allowed_size: u32, -) -> Vec { - match remark_size { - Some(ExplicitOrMaximal::Explicit(remark_size)) => vec![0; *remark_size], - Some(ExplicitOrMaximal::Maximal) => vec![0; maximal_allowed_size as _], - None => format!( - "Unix time: {}", - std::time::SystemTime::now() - .duration_since(std::time::SystemTime::UNIX_EPOCH) - .unwrap_or_default() - .as_secs(), - ) - .as_bytes() - .to_vec(), - } -} - -pub(crate) fn compute_maximal_message_arguments_size( - maximal_source_extrinsic_size: u32, - maximal_target_extrinsic_size: u32, -) -> u32 { - // assume that both signed extensions and other arguments fit 1KB - let service_tx_bytes_on_source_chain = 1024; - let maximal_source_extrinsic_size = - maximal_source_extrinsic_size - service_tx_bytes_on_source_chain; - let maximal_call_size = bridge_runtime_common::messages::target::maximal_incoming_message_size( - maximal_target_extrinsic_size, - ); - let maximal_call_size = if maximal_call_size > maximal_source_extrinsic_size { - maximal_source_extrinsic_size - } else { - maximal_call_size - }; - - // bytes in Call encoding that are used to encode everything except arguments - let service_bytes = 1 + 1 + 4; - maximal_call_size - service_bytes -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::cli::send_message::SendMessage; - - #[test] - fn should_encode_transfer_call() { - // given - let mut encode_call = EncodeCall::from_iter(vec![ - "encode-call", - "rialto-to-millau", - "transfer", - "--amount", - "12345", - "--recipient", - "5sauUXUfPjmwxSgmb3tZ5d6yx24eZX4wWJ2JtVUBaQqFbvEU", - ]); - - // when - let hex = encode_call.encode().unwrap(); - - // then - assert_eq!( - format!("{:?}", hex), - "0x040000d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27de5c0" - ); - } - - #[test] - fn should_encode_remark_with_default_payload() { - // given - let mut encode_call = - EncodeCall::from_iter(vec!["encode-call", "rialto-to-millau", "remark"]); - - // when - let hex = encode_call.encode().unwrap(); - - // then - assert!(format!("{:?}", hex).starts_with("0x000154556e69782074696d653a")); - } - - #[test] - fn should_encode_remark_with_explicit_payload() { - // given - let mut encode_call = EncodeCall::from_iter(vec![ - "encode-call", - "rialto-to-millau", - "remark", - "--remark-payload", - "1234", - ]); - - // when - let hex = encode_call.encode().unwrap(); - - // then - assert_eq!(format!("{:?}", hex), "0x0001081234"); - } - - #[test] - fn should_encode_remark_with_size() { - // given - let mut encode_call = EncodeCall::from_iter(vec![ - "encode-call", - "rialto-to-millau", - "remark", - "--remark-size", - "12", - ]); - - // when - let hex = encode_call.encode().unwrap(); - - // then - assert_eq!(format!("{:?}", hex), "0x000130000000000000000000000000"); - } - - #[test] - fn should_disallow_both_payload_and_size() { - // when - let err = EncodeCall::from_iter_safe(vec![ - "encode-call", - "rialto-to-millau", - "remark", - "--remark-payload", - "1234", - "--remark-size", - "12", - ]) - .unwrap_err(); - - // then - assert_eq!(err.kind, structopt::clap::ErrorKind::ArgumentConflict); - - let info = err.info.unwrap(); - assert!( - info.contains(&"remark-payload".to_string()) | - info.contains(&"remark-size".to_string()) - ) - } - - #[test] - fn should_encode_raw_call() { - // given - let mut encode_call = EncodeCall::from_iter(vec![ - "encode-call", - "rialto-to-millau", - "raw", - "040000d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27de5c0", - ]); - - // when - let hex = encode_call.encode().unwrap(); - - // then - assert_eq!( - format!("{:?}", hex), - "0x040000d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27de5c0" - ); - } - - #[async_std::test] - async fn should_encode_bridge_send_message_call() { - // given - let encode_message = SendMessage::from_iter(vec![ - "send-message", - "millau-to-rialto", - "--source-port", - "10946", - "--source-signer", - "//Alice", - "--target-signer", - "//Alice", - "--origin", - "Target", - "remark", - ]) - .encode_payload() - .await - .unwrap(); - - let mut encode_call = EncodeCall::from_iter(vec![ - "encode-call", - "rialto-to-millau", - "bridge-send-message", - "--fee", - "12345", - "--payload", - format!("{:}", &HexBytes::encode(&encode_message)).as_str(), - ]); - - // when - let call_hex = encode_call.encode().unwrap(); - - // then - assert!(format!("{:?}", call_hex).starts_with( - "0x0f030000000001000000000000000000000001d43593c715fdd31c61141abd04a99fd6822c8558854cc\ - de39a5684e7a56da27d01d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01" - )) - } -} diff --git a/bridges/relays/bin-substrate/src/cli/encode_message.rs b/bridges/relays/bin-substrate/src/cli/encode_message.rs index 677fc29ef153..e74d6da766d5 100644 --- a/bridges/relays/bin-substrate/src/cli/encode_message.rs +++ b/bridges/relays/bin-substrate/src/cli/encode_message.rs @@ -14,107 +14,80 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -use crate::{ - cli::{bridge::FullBridge, AccountId, CliChain, HexBytes}, - select_full_bridge, -}; -use frame_support::weights::Weight; +use crate::cli::{ExplicitOrMaximal, HexBytes}; +use bp_messages::LaneId; +use bp_runtime::EncodedOrDecodedCall; +use relay_substrate_client::Chain; use structopt::StructOpt; -use strum::VariantNames; -/// Generic message payload. +/// All possible messages that may be delivered to generic Substrate chain. +/// +/// Note this enum may be used in the context of both Source (as part of `encode-call`) +/// and Target chain (as part of `encode-message/send-message`). #[derive(StructOpt, Debug, PartialEq, Eq)] -pub enum MessagePayload { - /// Raw, SCALE-encoded `MessagePayload`. +pub enum Message { + /// Raw bytes for the message. Raw { - /// Hex-encoded SCALE data. + /// Raw message bytes. data: HexBytes, }, - /// Construct message to send over the bridge. - Call { - /// Message details. - #[structopt(flatten)] - call: crate::cli::encode_call::Call, - /// SS58 encoded Source account that will send the payload. - #[structopt(long)] - sender: AccountId, - /// Weight of the call. - /// - /// It must be specified if the chain runtime is not bundled with the relay, or if - /// you want to override bundled weight. - #[structopt(long)] - dispatch_weight: Option, + /// Message with given size. + Sized { + /// Sized of the message. + size: ExplicitOrMaximal, }, } -/// A `MessagePayload` to encode. -#[derive(StructOpt)] -pub struct EncodeMessage { - /// A bridge instance to initialize. - #[structopt(possible_values = FullBridge::VARIANTS, case_insensitive = true)] - bridge: FullBridge, - #[structopt(flatten)] - payload: MessagePayload, -} - -impl EncodeMessage { - /// Run the command. - pub fn encode(self) -> anyhow::Result { - select_full_bridge!(self.bridge, { - let payload = - Source::encode_message(self.payload).map_err(|e| anyhow::format_err!("{}", e))?; - Ok(HexBytes::encode(&payload)) - }) - } +/// Raw, SCALE-encoded message payload used in expected deployment. +pub type RawMessage = Vec; - /// Run the command. - pub async fn run(self) -> anyhow::Result<()> { - let payload = self.encode()?; - println!("{:?}", payload); - Ok(()) - } +pub trait CliEncodeMessage: Chain { + /// Encode a send message call. + fn encode_send_message_call( + lane: LaneId, + message: RawMessage, + fee: Self::Balance, + bridge_instance_index: u8, + ) -> anyhow::Result>; } -#[cfg(test)] -mod tests { - use super::*; - use sp_core::crypto::Ss58Codec; - - #[test] - fn should_encode_raw_message() { - // given - let msg = "01000000e88514000000000002d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d003c040130000000000000000000000000"; - let encode_message = - EncodeMessage::from_iter(vec!["encode-message", "rialto-to-millau", "raw", msg]); - - // when - let hex = encode_message.encode().unwrap(); - - // then - assert_eq!(format!("{:?}", hex), format!("0x{}", msg)); - } - - #[test] - fn should_encode_remark_with_size() { - // given - let sender = sp_keyring::AccountKeyring::Alice.to_account_id().to_ss58check(); - let encode_message = EncodeMessage::from_iter(vec![ - "encode-message", - "rialto-to-millau", - "call", - "--sender", - &sender, - "--dispatch-weight", - "42", - "remark", - "--remark-size", - "12", - ]); +/// Encode message payload passed through CLI flags. +pub(crate) fn encode_message( + message: &Message, +) -> anyhow::Result { + Ok(match message { + Message::Raw { ref data } => data.0.clone(), + Message::Sized { ref size } => match *size { + ExplicitOrMaximal::Explicit(size) => vec![42; size as usize], + ExplicitOrMaximal::Maximal => { + let maximal_size = compute_maximal_message_size( + Source::max_extrinsic_size(), + Target::max_extrinsic_size(), + ); + vec![42; maximal_size as usize] + }, + }, + }) +} - // when - let hex = encode_message.encode().unwrap(); +/// Compute maximal message size, given max extrinsic size at source and target chains. +pub(crate) fn compute_maximal_message_size( + maximal_source_extrinsic_size: u32, + maximal_target_extrinsic_size: u32, +) -> u32 { + // assume that both signed extensions and other arguments fit 1KB + let service_tx_bytes_on_source_chain = 1024; + let maximal_source_extrinsic_size = + maximal_source_extrinsic_size - service_tx_bytes_on_source_chain; + let maximal_message_size = + bridge_runtime_common::messages::target::maximal_incoming_message_size( + maximal_target_extrinsic_size, + ); + let maximal_message_size = if maximal_message_size > maximal_source_extrinsic_size { + maximal_source_extrinsic_size + } else { + maximal_message_size + }; - // then - assert_eq!(format!("{:?}", hex), "0x010000002a0000000000000002d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d003c000130000000000000000000000000"); - } + maximal_message_size } diff --git a/bridges/relays/bin-substrate/src/cli/estimate_fee.rs b/bridges/relays/bin-substrate/src/cli/estimate_fee.rs index bab625314e82..bf13996ec52b 100644 --- a/bridges/relays/bin-substrate/src/cli/estimate_fee.rs +++ b/bridges/relays/bin-substrate/src/cli/estimate_fee.rs @@ -17,7 +17,7 @@ use crate::{ cli::{ bridge::FullBridge, relay_headers_and_messages::CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO, - Balance, CliChain, HexBytes, HexLaneId, SourceConnectionParams, + Balance, HexBytes, HexLaneId, SourceConnectionParams, }, select_full_bridge, }; @@ -48,7 +48,7 @@ pub struct EstimateFee { conversion_rate_override: Option, /// Payload to send over the bridge. #[structopt(flatten)] - payload: crate::cli::encode_message::MessagePayload, + payload: crate::cli::encode_message::Message, } /// A way to override conversion rate between bridge tokens. @@ -82,8 +82,8 @@ impl EstimateFee { select_full_bridge!(bridge, { let source_client = source.to_client::().await?; let lane = lane.into(); - let payload = - Source::encode_message(payload).map_err(|e| anyhow::format_err!("{:?}", e))?; + let payload = crate::cli::encode_message::encode_message::(&payload) + .map_err(|e| anyhow::format_err!("{:?}", e))?; let fee = estimate_message_delivery_and_dispatch_fee::( &source_client, @@ -219,14 +219,10 @@ async fn do_estimate_message_delivery_and_dispatch_fee #[cfg(test)] mod tests { use super::*; - use crate::cli::{encode_call, RuntimeVersionType, SourceRuntimeVersionParams}; - use sp_core::crypto::Ss58Codec; + use crate::cli::{RuntimeVersionType, SourceRuntimeVersionParams}; #[test] fn should_parse_cli_options() { - // given - let alice = sp_keyring::AccountKeyring::Alice.to_account_id().to_ss58check(); - // when let res = EstimateFee::from_iter(vec![ "estimate_fee", @@ -235,13 +231,7 @@ mod tests { "1234", "--conversion-rate-override", "42.5", - "call", - "--sender", - &alice, - "--dispatch-weight", - "42", - "remark", - "--remark-payload", + "raw", "1234", ]); @@ -262,13 +252,8 @@ mod tests { source_transaction_version: None, } }, - payload: crate::cli::encode_message::MessagePayload::Call { - sender: alice.parse().unwrap(), - call: encode_call::Call::Remark { - remark_payload: Some(HexBytes(vec![0x12, 0x34])), - remark_size: None, - }, - dispatch_weight: Some(42), + payload: crate::cli::encode_message::Message::Raw { + data: HexBytes(vec![0x12, 0x34]) } } ); diff --git a/bridges/relays/bin-substrate/src/cli/mod.rs b/bridges/relays/bin-substrate/src/cli/mod.rs index df57537f588e..fddae4ee7abb 100644 --- a/bridges/relays/bin-substrate/src/cli/mod.rs +++ b/bridges/relays/bin-substrate/src/cli/mod.rs @@ -20,19 +20,16 @@ use std::convert::TryInto; use codec::{Decode, Encode}; use relay_substrate_client::ChainRuntimeVersion; -use sp_runtime::app_crypto::Ss58Codec; use structopt::{clap::arg_enum, StructOpt}; use strum::{EnumString, EnumVariantNames}; use bp_messages::LaneId; pub(crate) mod bridge; -pub(crate) mod encode_call; pub(crate) mod encode_message; pub(crate) mod estimate_fee; pub(crate) mod send_message; -mod derive_account; mod init_bridge; mod register_parachain; mod reinit_bridge; @@ -40,7 +37,6 @@ mod relay_headers; mod relay_headers_and_messages; mod relay_messages; mod resubmit_transactions; -mod swap_tokens; /// Parse relay CLI args. pub fn parse_args() -> Command { @@ -83,25 +79,10 @@ pub enum Command { /// The message is being sent to the source chain, delivered to the target chain and dispatched /// there. SendMessage(send_message::SendMessage), - /// Generate SCALE-encoded `Call` for choosen network. - /// - /// The call can be used either as message payload or can be wrapped into a transaction - /// and executed on the chain directly. - EncodeCall(encode_call::EncodeCall), - /// Generate SCALE-encoded `MessagePayload` object that can be sent over selected bridge. - /// - /// The `MessagePayload` can be then fed to `Messages::send_message` function and sent over - /// the bridge. - EncodeMessage(encode_message::EncodeMessage), /// Estimate Delivery and Dispatch Fee required for message submission to messages pallet. EstimateFee(estimate_fee::EstimateFee), - /// Given a source chain `AccountId`, derive the corresponding `AccountId` for the target - /// chain. - DeriveAccount(derive_account::DeriveAccount), /// Resubmit transactions with increased tip if they are stalled. ResubmitTransactions(resubmit_transactions::ResubmitTransactions), - /// Swap tokens using token-swap bridge. - SwapTokens(swap_tokens::SwapTokens), /// Register parachain. RegisterParachain(register_parachain::RegisterParachain), } @@ -134,12 +115,8 @@ impl Command { Self::InitBridge(arg) => arg.run().await?, Self::ReinitBridge(arg) => arg.run().await?, Self::SendMessage(arg) => arg.run().await?, - Self::EncodeCall(arg) => arg.run().await?, - Self::EncodeMessage(arg) => arg.run().await?, Self::EstimateFee(arg) => arg.run().await?, - Self::DeriveAccount(arg) => arg.run().await?, Self::ResubmitTransactions(arg) => arg.run().await?, - Self::SwapTokens(arg) => arg.run().await?, Self::RegisterParachain(arg) => arg.run().await?, } Ok(()) @@ -184,66 +161,7 @@ impl Balance { } } -/// Generic account id with custom parser. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct AccountId { - account: sp_runtime::AccountId32, - ss58_format: sp_core::crypto::Ss58AddressFormat, -} - -impl std::fmt::Display for AccountId { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(fmt, "{}", self.account.to_ss58check_with_version(self.ss58_format)) - } -} - -impl std::str::FromStr for AccountId { - type Err = String; - - fn from_str(s: &str) -> Result { - let (account, ss58_format) = sp_runtime::AccountId32::from_ss58check_with_version(s) - .map_err(|err| format!("Unable to decode SS58 address: {:?}", err))?; - Ok(Self { account, ss58_format }) - } -} - -const SS58_FORMAT_PROOF: &str = "u16 -> Ss58Format is infallible; qed"; - -impl AccountId { - /// Create new SS58-formatted address from raw account id. - pub fn from_raw(account: sp_runtime::AccountId32) -> Self { - Self { account, ss58_format: T::ss58_format().try_into().expect(SS58_FORMAT_PROOF) } - } - - /// Enforces formatting account to be for given [`CliChain`] type. - /// - /// This will change the `ss58format` of the account to match the requested one. - /// Note that a warning will be produced in case the current format does not match - /// the requested one, but the conversion always succeeds. - pub fn enforce_chain(&mut self) { - let original = self.clone(); - self.ss58_format = T::ss58_format().try_into().expect(SS58_FORMAT_PROOF); - log::debug!("{} SS58 format: {} (RAW: {})", self, self.ss58_format, self.account); - if original.ss58_format != self.ss58_format { - log::warn!( - target: "bridge", - "Address {} does not seem to match {}'s SS58 format (got: {}, expected: {}).\nConverted to: {}", - original, - T::NAME, - original.ss58_format, - self.ss58_format, - self, - ) - } - } - - /// Returns the raw (no SS58-prefixed) account id. - pub fn raw_id(&self) -> sp_runtime::AccountId32 { - self.account.clone() - } -} - -/// Bridge-supported network definition. +// Bridge-supported network definition. /// /// Used to abstract away CLI commands. pub trait CliChain: relay_substrate_client::Chain { @@ -262,11 +180,6 @@ pub trait CliChain: relay_substrate_client::Chain { /// Numeric value of SS58 format. fn ss58_format() -> u16; - - /// Construct message payload to be sent over the bridge. - fn encode_message( - message: crate::cli::encode_message::MessagePayload, - ) -> anyhow::Result; } /// Lane id. @@ -623,29 +536,8 @@ declare_chain_options!(Parachain, parachain); #[cfg(test)] mod tests { - use std::str::FromStr; - - use sp_core::Pair; - use super::*; - - #[test] - fn should_format_addresses_with_ss58_format() { - // given - let rialto1 = "5sauUXUfPjmwxSgmb3tZ5d6yx24eZX4wWJ2JtVUBaQqFbvEU"; - let rialto2 = "5rERgaT1Z8nM3et2epA5i1VtEBfp5wkhwHtVE8HK7BRbjAH2"; - let millau1 = "752paRyW1EGfq9YLTSSqcSJ5hqnBDidBmaftGhBo8fy6ypW9"; - let millau2 = "74GNQjmkcfstRftSQPJgMREchqHM56EvAUXRc266cZ1NYVW5"; - - let expected = vec![rialto1, rialto2, millau1, millau2]; - - // when - let parsed = expected.iter().map(|s| AccountId::from_str(s).unwrap()).collect::>(); - - let actual = parsed.iter().map(|a| format!("{}", a)).collect::>(); - - assert_eq!(actual, expected) - } + use sp_core::Pair; #[test] fn hex_bytes_display_matches_from_str_for_clap() { diff --git a/bridges/relays/bin-substrate/src/cli/register_parachain.rs b/bridges/relays/bin-substrate/src/cli/register_parachain.rs index c761a5dd1a60..79515b31dcfa 100644 --- a/bridges/relays/bin-substrate/src/cli/register_parachain.rs +++ b/bridges/relays/bin-substrate/src/cli/register_parachain.rs @@ -15,8 +15,7 @@ // along with Parity Bridges Common. If not, see . use crate::cli::{ - swap_tokens::wait_until_transaction_is_finalized, Balance, ParachainConnectionParams, - RelaychainConnectionParams, RelaychainSigningParams, + Balance, ParachainConnectionParams, RelaychainConnectionParams, RelaychainSigningParams, }; use codec::Encode; @@ -30,7 +29,8 @@ use polkadot_runtime_common::{ }; use polkadot_runtime_parachains::paras::ParaLifecycle; use relay_substrate_client::{ - AccountIdOf, CallOf, Chain, Client, SignParam, TransactionSignScheme, UnsignedTransaction, + AccountIdOf, CallOf, Chain, Client, HashOf, SignParam, Subscription, TransactionSignScheme, + TransactionStatusOf, UnsignedTransaction, }; use rialto_runtime::SudoCall; use sp_core::{ @@ -270,6 +270,46 @@ impl RegisterParachain { } } +/// Wait until transaction is included into finalized block. +/// +/// Returns the hash of the finalized block with transaction. +pub(crate) async fn wait_until_transaction_is_finalized( + subscription: Subscription>, +) -> anyhow::Result> { + loop { + let transaction_status = subscription.next().await?; + match transaction_status { + Some(TransactionStatusOf::::FinalityTimeout(_)) | + Some(TransactionStatusOf::::Usurped(_)) | + Some(TransactionStatusOf::::Dropped) | + Some(TransactionStatusOf::::Invalid) | + None => + return Err(anyhow::format_err!( + "We've been waiting for finalization of {} transaction, but it now has the {:?} status", + C::NAME, + transaction_status, + )), + Some(TransactionStatusOf::::Finalized(block_hash)) => { + log::trace!( + target: "bridge", + "{} transaction has been finalized at block {}", + C::NAME, + block_hash, + ); + return Ok(block_hash) + }, + _ => { + log::trace!( + target: "bridge", + "Received intermediate status of {} transaction: {:?}", + C::NAME, + transaction_status, + ); + }, + } + } +} + /// Wait until parachain state is changed. async fn wait_para_state( relay_client: &Client, diff --git a/bridges/relays/bin-substrate/src/cli/reinit_bridge.rs b/bridges/relays/bin-substrate/src/cli/reinit_bridge.rs index dc902055d20b..79869ad03151 100644 --- a/bridges/relays/bin-substrate/src/cli/reinit_bridge.rs +++ b/bridges/relays/bin-substrate/src/cli/reinit_bridge.rs @@ -20,7 +20,7 @@ use crate::{ polkadot_headers_to_kusama::PolkadotFinalityToKusama, }, cli::{ - swap_tokens::wait_until_transaction_is_finalized, SourceConnectionParams, + register_parachain::wait_until_transaction_is_finalized, SourceConnectionParams, TargetConnectionParams, TargetSigningParams, }, }; diff --git a/bridges/relays/bin-substrate/src/cli/send_message.rs b/bridges/relays/bin-substrate/src/cli/send_message.rs index ddb1ff59b5d0..53187009d3e2 100644 --- a/bridges/relays/bin-substrate/src/cli/send_message.rs +++ b/bridges/relays/bin-substrate/src/cli/send_message.rs @@ -16,18 +16,14 @@ use crate::cli::{ bridge::FullBridge, - encode_call::{self, CliEncodeCall}, + encode_message::{self, CliEncodeMessage}, estimate_fee::{estimate_message_delivery_and_dispatch_fee, ConversionRateOverride}, - Balance, ExplicitOrMaximal, HexBytes, HexLaneId, Origins, SourceConnectionParams, - SourceSigningParams, TargetConnectionParams, TargetSigningParams, + Balance, HexBytes, HexLaneId, SourceConnectionParams, SourceSigningParams, }; -use bp_message_dispatch::{CallOrigin, MessagePayload}; -use bp_runtime::Chain as _; use codec::Encode; -use frame_support::weights::Weight; use relay_substrate_client::{Chain, SignParam, TransactionSignScheme, UnsignedTransaction}; use sp_core::{Bytes, Pair}; -use sp_runtime::{traits::IdentifyAccount, AccountId32, MultiSignature, MultiSigner}; +use sp_runtime::AccountId32; use std::fmt::Debug; use structopt::StructOpt; use strum::{EnumString, EnumVariantNames, VariantNames}; @@ -61,8 +57,6 @@ pub struct SendMessage { source: SourceConnectionParams, #[structopt(flatten)] source_sign: SourceSigningParams, - #[structopt(flatten)] - target_sign: TargetSigningParams, /// Hex-encoded lane id. Defaults to `00000000`. #[structopt(long, default_value = "00000000")] lane: HexLaneId, @@ -72,104 +66,20 @@ pub struct SendMessage { /// your message won't be relayed. #[structopt(long)] conversion_rate_override: Option, - /// Where dispatch fee is paid? - #[structopt( - long, - possible_values = DispatchFeePayment::VARIANTS, - case_insensitive = true, - default_value = "at-source-chain", - )] - dispatch_fee_payment: DispatchFeePayment, - /// Dispatch weight of the message. If not passed, determined automatically. - #[structopt(long)] - dispatch_weight: Option>, /// Delivery and dispatch fee in source chain base currency units. If not passed, determined /// automatically. #[structopt(long)] fee: Option, /// Message type. #[structopt(subcommand)] - message: crate::cli::encode_call::Call, - /// The origin to use when dispatching the message on the target chain. Defaults to - /// `SourceAccount`. - #[structopt(long, possible_values = &Origins::variants(), default_value = "Source")] - origin: Origins, - - // Normally we don't need to connect to the target chain to send message. But for testing - // we may want to use **actual** `spec_version` of the target chain when composing a message. - // Then we'll need to read version from the target chain node. - #[structopt(flatten)] - target: TargetConnectionParams, + message: crate::cli::encode_message::Message, } impl SendMessage { - pub async fn encode_payload( - &mut self, - ) -> anyhow::Result>> { - crate::select_full_bridge!(self.bridge, { - let SendMessage { - source_sign, - target_sign, - ref mut message, - dispatch_fee_payment, - dispatch_weight, - origin, - bridge, - .. - } = self; - - let source_sign = source_sign.to_keypair::()?; - - encode_call::preprocess_call::(message, bridge.bridge_instance_index()); - let target_call = Target::encode_call(message)?; - let target_spec_version = self.target.selected_chain_spec_version::().await?; - - let payload = { - let target_call_weight = prepare_call_dispatch_weight( - dispatch_weight, - || { - Ok(ExplicitOrMaximal::Explicit( - Target::get_dispatch_info(&target_call)?.weight, - )) - }, - compute_maximal_message_dispatch_weight(Target::max_extrinsic_weight()), - )?; - let source_sender_public: MultiSigner = source_sign.public().into(); - let source_account_id = source_sender_public.into_account(); - - message_payload( - target_spec_version, - target_call_weight, - match origin { - Origins::Source => CallOrigin::SourceAccount(source_account_id), - Origins::Target => { - let target_sign = target_sign.to_keypair::()?; - let digest = account_ownership_digest( - &target_call, - source_account_id.clone(), - target_spec_version, - ); - let target_origin_public = target_sign.public(); - let digest_signature = target_sign.sign(&digest); - CallOrigin::TargetAccount( - source_account_id, - target_origin_public.into(), - digest_signature.into(), - ) - }, - }, - &target_call, - *dispatch_fee_payment, - ) - }; - Ok(payload) - }) - } - /// Run the command. - pub async fn run(mut self) -> anyhow::Result<()> { + pub async fn run(self) -> anyhow::Result<()> { crate::select_full_bridge!(self.bridge, { - let payload = self.encode_payload().await?; + let payload = encode_message::encode_message::(&self.message)?; let source_client = self.source.to_client::().await?; let source_sign = self.source_sign.to_keypair::()?; @@ -189,14 +99,13 @@ impl SendMessage { .await? as _, ), }; - let dispatch_weight = payload.weight; let payload_len = payload.encode().len(); - let send_message_call = Source::encode_call(&encode_call::Call::BridgeSendMessage { - bridge_instance_index: self.bridge.bridge_instance_index(), - lane: self.lane, - payload: HexBytes::encode(&payload), - fee, - })?; + let send_message_call = Source::encode_send_message_call( + self.lane.0, + payload, + fee.cast().into(), + self.bridge.bridge_instance_index(), + )?; let source_genesis_hash = *source_client.genesis_hash(); let (spec_version, transaction_version) = @@ -228,11 +137,10 @@ impl SendMessage { log::info!( target: "bridge", - "Sending message to {}. Lane: {:?}. Size: {}. Dispatch weight: {}. Fee: {}", + "Sending message to {}. Lane: {:?}. Size: {}. Fee: {}", Target::NAME, lane, payload_len, - dispatch_weight, fee, ); log::info!( @@ -260,66 +168,15 @@ impl SendMessage { } } -fn prepare_call_dispatch_weight( - user_specified_dispatch_weight: &Option>, - weight_from_pre_dispatch_call: impl Fn() -> anyhow::Result>, - maximal_allowed_weight: Weight, -) -> anyhow::Result { - match user_specified_dispatch_weight - .clone() - .map(Ok) - .unwrap_or_else(weight_from_pre_dispatch_call)? - { - ExplicitOrMaximal::Explicit(weight) => Ok(weight), - ExplicitOrMaximal::Maximal => Ok(maximal_allowed_weight), - } -} - -pub(crate) fn message_payload( - spec_version: u32, - weight: Weight, - origin: CallOrigin, - call: &impl Encode, - dispatch_fee_payment: DispatchFeePayment, -) -> MessagePayload> -where - SAccountId: Encode + Debug, - TPublic: Encode + Debug, - TSignature: Encode + Debug, -{ - // Display nicely formatted call. - let payload = MessagePayload { - spec_version, - weight, - origin, - dispatch_fee_payment: dispatch_fee_payment.into(), - call: HexBytes::encode(call), - }; - - log::info!(target: "bridge", "Created Message Payload: {:#?}", payload); - log::info!(target: "bridge", "Encoded Message Payload: {:?}", HexBytes::encode(&payload)); - - // re-pack to return `Vec` - let MessagePayload { spec_version, weight, origin, dispatch_fee_payment, call } = payload; - MessagePayload { spec_version, weight, origin, dispatch_fee_payment, call: call.0 } -} - -pub(crate) fn compute_maximal_message_dispatch_weight(maximal_extrinsic_weight: Weight) -> Weight { - bridge_runtime_common::messages::target::maximal_incoming_message_dispatch_weight( - maximal_extrinsic_weight, - ) -} - #[cfg(test)] mod tests { use super::*; - use crate::cli::CliChain; - use hex_literal::hex; + use crate::cli::ExplicitOrMaximal; - #[async_std::test] - async fn send_remark_rialto_to_millau() { + #[test] + fn send_raw_rialto_to_millau() { // given - let mut send_message = SendMessage::from_iter(vec![ + let send_message = SendMessage::from_iter(vec![ "send-message", "rialto-to-millau", "--source-port", @@ -328,117 +185,48 @@ mod tests { "//Alice", "--conversion-rate-override", "0.75", - "remark", - "--remark-payload", - "1234", + "raw", + "dead", ]); - // when - let payload = send_message.encode_payload().await.unwrap(); - // then + assert_eq!(send_message.bridge, FullBridge::RialtoToMillau); + assert_eq!(send_message.source.source_port, 1234); + assert_eq!(send_message.source_sign.source_signer, Some("//Alice".into())); assert_eq!( - payload, - MessagePayload { - spec_version: relay_millau_client::Millau::RUNTIME_VERSION.spec_version, - weight: 0, - origin: CallOrigin::SourceAccount( - sp_keyring::AccountKeyring::Alice.to_account_id() - ), - dispatch_fee_payment: bp_runtime::messages::DispatchFeePayment::AtSourceChain, - call: hex!("0001081234").to_vec(), - } + send_message.conversion_rate_override, + Some(ConversionRateOverride::Explicit(0.75)) ); - } - - #[async_std::test] - async fn send_remark_millau_to_rialto() { - // given - let mut send_message = SendMessage::from_iter(vec![ - "send-message", - "millau-to-rialto", - "--source-port", - "1234", - "--source-signer", - "//Alice", - "--origin", - "Target", - "--target-signer", - "//Bob", - "--conversion-rate-override", - "metric", - "remark", - "--remark-payload", - "1234", - ]); - - // when - let payload = send_message.encode_payload().await.unwrap(); - - // then - // Since signatures are randomized we extract it from here and only check the rest. - let signature = match payload.origin { - CallOrigin::TargetAccount(_, _, ref sig) => sig.clone(), - _ => panic!("Unexpected `CallOrigin`: {:?}", payload), - }; assert_eq!( - payload, - MessagePayload { - spec_version: relay_millau_client::Millau::RUNTIME_VERSION.spec_version, - weight: 0, - origin: CallOrigin::TargetAccount( - sp_keyring::AccountKeyring::Alice.to_account_id(), - sp_keyring::AccountKeyring::Bob.into(), - signature, - ), - dispatch_fee_payment: bp_runtime::messages::DispatchFeePayment::AtSourceChain, - call: hex!("0001081234").to_vec(), - } + send_message.message, + crate::cli::encode_message::Message::Raw { data: HexBytes(vec![0xDE, 0xAD]) } ); } #[test] - fn accepts_send_message_command_without_target_sign_options() { - // given - let send_message = SendMessage::from_iter_safe(vec![ - "send-message", - "rialto-to-millau", - "--source-port", - "1234", - "--source-signer", - "//Alice", - "--origin", - "Target", - "remark", - "--remark-payload", - "1234", - ]); - - assert!(send_message.is_ok()); - } - - #[async_std::test] - async fn accepts_non_default_dispatch_fee_payment() { + fn send_sized_rialto_to_millau() { // given - let mut send_message = SendMessage::from_iter(vec![ + let send_message = SendMessage::from_iter(vec![ "send-message", "rialto-to-millau", "--source-port", "1234", "--source-signer", "//Alice", - "--dispatch-fee-payment", - "at-target-chain", - "remark", + "--conversion-rate-override", + "metric", + "sized", + "max", ]); - // when - let payload = send_message.encode_payload().await.unwrap(); - // then + assert_eq!(send_message.bridge, FullBridge::RialtoToMillau); + assert_eq!(send_message.source.source_port, 1234); + assert_eq!(send_message.source_sign.source_signer, Some("//Alice".into())); + assert_eq!(send_message.conversion_rate_override, Some(ConversionRateOverride::Metric)); assert_eq!( - payload.dispatch_fee_payment, - bp_runtime::messages::DispatchFeePayment::AtTargetChain + send_message.message, + crate::cli::encode_message::Message::Sized { size: ExplicitOrMaximal::Maximal } ); } } diff --git a/bridges/relays/bin-substrate/src/cli/swap_tokens.rs b/bridges/relays/bin-substrate/src/cli/swap_tokens.rs deleted file mode 100644 index 1467df3711c3..000000000000 --- a/bridges/relays/bin-substrate/src/cli/swap_tokens.rs +++ /dev/null @@ -1,869 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Tokens swap using token-swap bridge pallet. - -// TokenSwapBalances fields are never directly accessed, but the whole struct is printed -// to show token swap progress -#![allow(dead_code)] - -use codec::Encode; -use num_traits::One; -use rand::random; -use structopt::StructOpt; -use strum::{EnumString, EnumVariantNames, VariantNames}; - -use frame_support::dispatch::GetDispatchInfo; -use relay_substrate_client::{ - AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, CallOf, Chain, ChainWithBalances, - Client, Error as SubstrateError, HashOf, SignParam, SignatureOf, Subscription, - TransactionSignScheme, TransactionStatusOf, UnsignedTransaction, -}; -use sp_core::{blake2_256, storage::StorageKey, Bytes, Pair, U256}; -use sp_runtime::traits::{Convert, Header as HeaderT}; - -use crate::cli::{ - estimate_fee::ConversionRateOverride, Balance, CliChain, SourceConnectionParams, - SourceSigningParams, TargetConnectionParams, TargetSigningParams, -}; - -/// Swap tokens. -#[derive(StructOpt, Debug, PartialEq)] -pub struct SwapTokens { - /// A bridge instance to use in token swap. - #[structopt(possible_values = SwapTokensBridge::VARIANTS, case_insensitive = true)] - bridge: SwapTokensBridge, - - #[structopt(flatten)] - source: SourceConnectionParams, - #[structopt(flatten)] - source_sign: SourceSigningParams, - - #[structopt(flatten)] - target: TargetConnectionParams, - #[structopt(flatten)] - target_sign: TargetSigningParams, - - #[structopt(subcommand)] - swap_type: TokenSwapType, - /// Source chain balance that source signer wants to swap. - #[structopt(long)] - source_balance: Balance, - /// Target chain balance that target signer wants to swap. - #[structopt(long)] - target_balance: Balance, - /// A way to override conversion rate from target to source tokens. - /// - /// If not specified, conversion rate from runtime storage is used. It may be obsolete and - /// your message won't be relayed. - #[structopt(long)] - target_to_source_conversion_rate_override: Option, - /// A way to override conversion rate from source to target tokens. - /// - /// If not specified, conversion rate from runtime storage is used. It may be obsolete and - /// your message won't be relayed. - #[structopt(long)] - source_to_target_conversion_rate_override: Option, -} - -/// Token swap type. -#[derive(StructOpt, Debug, PartialEq, Eq, Clone)] -pub enum TokenSwapType { - /// The `target_sign` is temporary and only have funds for single swap. - NoLock, - /// This swap type prevents `source_signer` from restarting the swap after it has been - /// completed. - LockUntilBlock { - /// Number of blocks before the swap expires. - #[structopt(long)] - blocks_before_expire: u32, - /// Unique swap nonce. - #[structopt(long)] - swap_nonce: Option, - }, -} - -/// Swap tokens bridge. -#[derive(Debug, EnumString, EnumVariantNames, PartialEq)] -#[strum(serialize_all = "kebab_case")] -pub enum SwapTokensBridge { - /// Use token-swap pallet deployed at Millau to swap tokens with Rialto. - MillauToRialto, -} - -macro_rules! select_bridge { - ($bridge: expr, $generic: tt) => { - match $bridge { - SwapTokensBridge::MillauToRialto => { - type Source = relay_millau_client::Millau; - type Target = relay_rialto_client::Rialto; - const SOURCE_SPEC_VERSION: u32 = millau_runtime::VERSION.spec_version; - const TARGET_SPEC_VERSION: u32 = rialto_runtime::VERSION.spec_version; - - type FromSwapToThisAccountIdConverter = bp_rialto::AccountIdConverter; - - use bp_millau::{ - derive_account_from_rialto_id as derive_source_account_from_target_account, - TO_MILLAU_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_TARGET_TO_SOURCE_MESSAGE_FEE_METHOD, - WITH_RIALTO_TOKEN_SWAP_PALLET_NAME as TOKEN_SWAP_PALLET_NAME, - }; - use bp_rialto::{ - derive_account_from_millau_id as derive_target_account_from_source_account, - TO_RIALTO_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_SOURCE_TO_TARGET_MESSAGE_FEE_METHOD, - }; - - const SOURCE_CHAIN_ID: bp_runtime::ChainId = bp_runtime::MILLAU_CHAIN_ID; - const TARGET_CHAIN_ID: bp_runtime::ChainId = bp_runtime::RIALTO_CHAIN_ID; - - const SOURCE_TO_TARGET_LANE_ID: bp_messages::LaneId = *b"swap"; - const TARGET_TO_SOURCE_LANE_ID: bp_messages::LaneId = [0, 0, 0, 0]; - - $generic - }, - } - }; -} - -impl SwapTokens { - /// Run the command. - pub async fn run(self) -> anyhow::Result<()> { - select_bridge!(self.bridge, { - let source_client = self.source.to_client::().await?; - let source_sign = self.source_sign.to_keypair::()?; - let target_client = self.target.to_client::().await?; - let target_sign = self.target_sign.to_keypair::()?; - let target_to_source_conversion_rate_override = - self.target_to_source_conversion_rate_override; - let source_to_target_conversion_rate_override = - self.source_to_target_conversion_rate_override; - - // names of variables in this function are matching names used by the - // `pallet-bridge-token-swap` - - // prepare token swap intention - let token_swap = self - .prepare_token_swap::(&source_client, &source_sign, &target_sign) - .await?; - - // group all accounts that will be used later - let accounts = TokenSwapAccounts { - source_account_at_bridged_chain: derive_target_account_from_source_account( - bp_runtime::SourceAccount::Account( - token_swap.source_account_at_this_chain.clone(), - ), - ), - target_account_at_this_chain: derive_source_account_from_target_account( - bp_runtime::SourceAccount::Account( - token_swap.target_account_at_bridged_chain.clone(), - ), - ), - source_account_at_this_chain: token_swap.source_account_at_this_chain.clone(), - target_account_at_bridged_chain: token_swap.target_account_at_bridged_chain.clone(), - swap_account: FromSwapToThisAccountIdConverter::convert( - token_swap.using_encoded(blake2_256).into(), - ), - }; - - // account balances are used to demonstrate what's happening :) - let initial_balances = - read_account_balances(&accounts, &source_client, &target_client).await?; - - // before calling something that may fail, log what we're trying to do - log::info!(target: "bridge", "Starting swap: {:?}", token_swap); - log::info!(target: "bridge", "Swap accounts: {:?}", accounts); - log::info!(target: "bridge", "Initial account balances: {:?}", initial_balances); - - // - // Step 1: swap is created - // - - // prepare `Currency::transfer` call that will happen at the target chain - let bridged_currency_transfer: CallOf = pallet_balances::Call::transfer { - dest: accounts.source_account_at_bridged_chain.clone().into(), - value: token_swap.target_balance_at_bridged_chain, - } - .into(); - let bridged_currency_transfer_weight = - bridged_currency_transfer.get_dispatch_info().weight; - - // sign message - let bridged_chain_spec_version = TARGET_SPEC_VERSION; - let signature_payload = pallet_bridge_dispatch::account_ownership_digest( - &bridged_currency_transfer, - &accounts.swap_account, - &bridged_chain_spec_version, - SOURCE_CHAIN_ID, - TARGET_CHAIN_ID, - ); - let bridged_currency_transfer_signature: SignatureOf = - target_sign.sign(&signature_payload).into(); - - // prepare `create_swap` call - let target_public_at_bridged_chain: AccountPublicOf = - target_sign.public().into(); - let swap_delivery_and_dispatch_fee = - crate::cli::estimate_fee::estimate_message_delivery_and_dispatch_fee::< - Source, - Target, - _, - >( - &source_client, - target_to_source_conversion_rate_override, - ESTIMATE_SOURCE_TO_TARGET_MESSAGE_FEE_METHOD, - SOURCE_TO_TARGET_LANE_ID, - bp_message_dispatch::MessagePayload { - spec_version: TARGET_SPEC_VERSION, - weight: bridged_currency_transfer_weight, - origin: bp_message_dispatch::CallOrigin::TargetAccount( - accounts.swap_account.clone(), - target_public_at_bridged_chain.clone(), - bridged_currency_transfer_signature.clone(), - ), - dispatch_fee_payment: - bp_runtime::messages::DispatchFeePayment::AtTargetChain, - call: bridged_currency_transfer.encode(), - }, - ) - .await?; - let create_swap_call: CallOf = pallet_bridge_token_swap::Call::create_swap { - swap: token_swap.clone(), - swap_creation_params: Box::new(bp_token_swap::TokenSwapCreation { - target_public_at_bridged_chain, - swap_delivery_and_dispatch_fee, - bridged_chain_spec_version, - bridged_currency_transfer: bridged_currency_transfer.encode(), - bridged_currency_transfer_weight, - bridged_currency_transfer_signature, - }), - } - .into(); - - // start tokens swap - let source_genesis_hash = *source_client.genesis_hash(); - let create_swap_signer = source_sign.clone(); - let (spec_version, transaction_version) = - source_client.simple_runtime_version().await?; - let swap_created_at = wait_until_transaction_is_finalized::( - source_client - .submit_and_watch_signed_extrinsic( - accounts.source_account_at_this_chain.clone(), - move |_, transaction_nonce| { - Ok(Bytes( - Source::sign_transaction(SignParam { - spec_version, - transaction_version, - genesis_hash: source_genesis_hash, - signer: create_swap_signer, - era: relay_substrate_client::TransactionEra::immortal(), - unsigned: UnsignedTransaction::new( - create_swap_call.into(), - transaction_nonce, - ), - })? - .encode(), - )) - }, - ) - .await?, - ) - .await?; - - // read state of swap after it has been created - let token_swap_hash = token_swap.hash(); - let token_swap_storage_key = bp_token_swap::storage_keys::pending_swaps_key( - TOKEN_SWAP_PALLET_NAME, - token_swap_hash, - ); - match read_token_swap_state(&source_client, swap_created_at, &token_swap_storage_key) - .await? - { - Some(bp_token_swap::TokenSwapState::Started) => { - log::info!(target: "bridge", "Swap has been successfully started"); - let intermediate_balances = - read_account_balances(&accounts, &source_client, &target_client).await?; - log::info!(target: "bridge", "Intermediate balances: {:?}", intermediate_balances); - }, - Some(token_swap_state) => - return Err(anyhow::format_err!( - "Fresh token swap has unexpected state: {:?}", - token_swap_state, - )), - None => return Err(anyhow::format_err!("Failed to start token swap")), - }; - - // - // Step 2: message is being relayed to the target chain and dispathed there - // - - // wait until message is dispatched at the target chain and dispatch result delivered - // back to source chain - let token_swap_state = wait_until_token_swap_state_is_changed( - &source_client, - &token_swap_storage_key, - bp_token_swap::TokenSwapState::Started, - ) - .await?; - let is_transfer_succeeded = match token_swap_state { - Some(bp_token_swap::TokenSwapState::Started) => { - unreachable!("wait_until_token_swap_state_is_changed only returns if state is not Started; qed",) - }, - None => - return Err(anyhow::format_err!("Fresh token swap has disappeared unexpectedly")), - Some(bp_token_swap::TokenSwapState::Confirmed) => { - log::info!( - target: "bridge", - "Transfer has been successfully dispatched at the target chain. Swap can be claimed", - ); - true - }, - Some(bp_token_swap::TokenSwapState::Failed) => { - log::info!( - target: "bridge", - "Transfer has been dispatched with an error at the target chain. Swap can be canceled", - ); - false - }, - }; - - // by this time: (1) token swap account has been created and (2) if transfer has been - // successfully dispatched, both target chain balances have changed - let intermediate_balances = - read_account_balances(&accounts, &source_client, &target_client).await?; - log::info!(target: "bridge", "Intermediate balances: {:?}", intermediate_balances); - - // transfer has been dispatched, but we may need to wait until block where swap can be - // claimed/canceled - if let bp_token_swap::TokenSwapType::LockClaimUntilBlock( - ref last_available_block_number, - _, - ) = token_swap.swap_type - { - wait_until_swap_unlocked( - &source_client, - last_available_block_number + BlockNumberOf::::one(), - ) - .await?; - } - - // - // Step 3: we may now claim or cancel the swap - // - - if is_transfer_succeeded { - log::info!(target: "bridge", "Claiming the swap"); - - // prepare `claim_swap` message that will be sent over the bridge - let claim_swap_call: CallOf = - pallet_bridge_token_swap::Call::claim_swap { swap: token_swap }.into(); - let claim_swap_message = bp_message_dispatch::MessagePayload { - spec_version: SOURCE_SPEC_VERSION, - weight: claim_swap_call.get_dispatch_info().weight, - origin: bp_message_dispatch::CallOrigin::SourceAccount( - accounts.target_account_at_bridged_chain.clone(), - ), - dispatch_fee_payment: bp_runtime::messages::DispatchFeePayment::AtSourceChain, - call: claim_swap_call.encode(), - }; - let claim_swap_delivery_and_dispatch_fee = - crate::cli::estimate_fee::estimate_message_delivery_and_dispatch_fee::< - Target, - Source, - _, - >( - &target_client, - source_to_target_conversion_rate_override, - ESTIMATE_TARGET_TO_SOURCE_MESSAGE_FEE_METHOD, - TARGET_TO_SOURCE_LANE_ID, - claim_swap_message.clone(), - ) - .await?; - let send_message_call: CallOf = - pallet_bridge_messages::Call::send_message { - lane_id: TARGET_TO_SOURCE_LANE_ID, - payload: claim_swap_message, - delivery_and_dispatch_fee: claim_swap_delivery_and_dispatch_fee, - } - .into(); - - // send `claim_swap` message - let target_genesis_hash = *target_client.genesis_hash(); - let (spec_version, transaction_version) = - target_client.simple_runtime_version().await?; - let _ = wait_until_transaction_is_finalized::( - target_client - .submit_and_watch_signed_extrinsic( - accounts.target_account_at_bridged_chain.clone(), - move |_, transaction_nonce| { - Ok(Bytes( - Target::sign_transaction(SignParam { - spec_version, - transaction_version, - genesis_hash: target_genesis_hash, - signer: target_sign, - era: relay_substrate_client::TransactionEra::immortal(), - unsigned: UnsignedTransaction::new( - send_message_call.into(), - transaction_nonce, - ), - })? - .encode(), - )) - }, - ) - .await?, - ) - .await?; - - // wait until swap state is updated - let token_swap_state = wait_until_token_swap_state_is_changed( - &source_client, - &token_swap_storage_key, - bp_token_swap::TokenSwapState::Confirmed, - ) - .await?; - if token_swap_state != None { - return Err(anyhow::format_err!( - "Confirmed token swap state has been changed to {:?} unexpectedly", - token_swap_state - )) - } - } else { - log::info!(target: "bridge", "Cancelling the swap"); - let cancel_swap_call: CallOf = - pallet_bridge_token_swap::Call::cancel_swap { swap: token_swap.clone() }.into(); - let (spec_version, transaction_version) = - source_client.simple_runtime_version().await?; - let _ = wait_until_transaction_is_finalized::( - source_client - .submit_and_watch_signed_extrinsic( - accounts.source_account_at_this_chain.clone(), - move |_, transaction_nonce| { - Ok(Bytes( - Source::sign_transaction(SignParam { - spec_version, - transaction_version, - genesis_hash: source_genesis_hash, - signer: source_sign, - era: relay_substrate_client::TransactionEra::immortal(), - unsigned: UnsignedTransaction::new( - cancel_swap_call.into(), - transaction_nonce, - ), - })? - .encode(), - )) - }, - ) - .await?, - ) - .await?; - } - - // print final balances - let final_balances = - read_account_balances(&accounts, &source_client, &target_client).await?; - log::info!(target: "bridge", "Final account balances: {:?}", final_balances); - - Ok(()) - }) - } - - /// Prepare token swap intention. - async fn prepare_token_swap( - &self, - source_client: &Client, - source_sign: &Source::KeyPair, - target_sign: &Target::KeyPair, - ) -> anyhow::Result< - bp_token_swap::TokenSwap< - BlockNumberOf, - BalanceOf, - AccountIdOf, - BalanceOf, - AccountIdOf, - >, - > - where - AccountIdOf: From<::Public>, - AccountIdOf: From<::Public>, - BalanceOf: From, - BalanceOf: From, - { - // accounts that are directly controlled by participants - let source_account_at_this_chain: AccountIdOf = source_sign.public().into(); - let target_account_at_bridged_chain: AccountIdOf = target_sign.public().into(); - - // balances that we're going to swap - let source_balance_at_this_chain: BalanceOf = self.source_balance.cast().into(); - let target_balance_at_bridged_chain: BalanceOf = self.target_balance.cast().into(); - - // prepare token swap intention - Ok(bp_token_swap::TokenSwap { - swap_type: self.prepare_token_swap_type(source_client).await?, - source_balance_at_this_chain, - source_account_at_this_chain: source_account_at_this_chain.clone(), - target_balance_at_bridged_chain, - target_account_at_bridged_chain: target_account_at_bridged_chain.clone(), - }) - } - - /// Prepare token swap type. - async fn prepare_token_swap_type( - &self, - source_client: &Client, - ) -> anyhow::Result>> { - match self.swap_type { - TokenSwapType::NoLock => - Ok(bp_token_swap::TokenSwapType::TemporaryTargetAccountAtBridgedChain), - TokenSwapType::LockUntilBlock { blocks_before_expire, ref swap_nonce } => { - let blocks_before_expire: BlockNumberOf = blocks_before_expire.into(); - let current_source_block_number = *source_client.best_header().await?.number(); - Ok(bp_token_swap::TokenSwapType::LockClaimUntilBlock( - current_source_block_number + blocks_before_expire, - swap_nonce.unwrap_or_else(|| { - U256::from(random::()).overflowing_mul(U256::from(random::())).0 - }), - )) - }, - } - } -} - -/// Accounts that are participating in the swap. -#[derive(Debug)] -struct TokenSwapAccounts { - source_account_at_this_chain: ThisAccountId, - source_account_at_bridged_chain: BridgedAccountId, - target_account_at_bridged_chain: BridgedAccountId, - target_account_at_this_chain: ThisAccountId, - swap_account: ThisAccountId, -} - -/// Swap accounts balances. -#[derive(Debug)] -struct TokenSwapBalances { - source_account_at_this_chain_balance: Option, - source_account_at_bridged_chain_balance: Option, - target_account_at_bridged_chain_balance: Option, - target_account_at_this_chain_balance: Option, - swap_account_balance: Option, -} - -/// Read swap accounts balances. -async fn read_account_balances( - accounts: &TokenSwapAccounts, AccountIdOf>, - source_client: &Client, - target_client: &Client, -) -> anyhow::Result, BalanceOf>> { - Ok(TokenSwapBalances { - source_account_at_this_chain_balance: read_account_balance( - source_client, - &accounts.source_account_at_this_chain, - ) - .await?, - source_account_at_bridged_chain_balance: read_account_balance( - target_client, - &accounts.source_account_at_bridged_chain, - ) - .await?, - target_account_at_bridged_chain_balance: read_account_balance( - target_client, - &accounts.target_account_at_bridged_chain, - ) - .await?, - target_account_at_this_chain_balance: read_account_balance( - source_client, - &accounts.target_account_at_this_chain, - ) - .await?, - swap_account_balance: read_account_balance(source_client, &accounts.swap_account).await?, - }) -} - -/// Read account balance. -async fn read_account_balance( - client: &Client, - account: &AccountIdOf, -) -> anyhow::Result>> { - match client.free_native_balance(account.clone()).await { - Ok(balance) => Ok(Some(balance)), - Err(SubstrateError::AccountDoesNotExist) => Ok(None), - Err(error) => Err(anyhow::format_err!( - "Failed to read balance of {} account {:?}: {:?}", - C::NAME, - account, - error, - )), - } -} - -/// Wait until transaction is included into finalized block. -/// -/// Returns the hash of the finalized block with transaction. -pub(crate) async fn wait_until_transaction_is_finalized( - subscription: Subscription>, -) -> anyhow::Result> { - loop { - let transaction_status = subscription.next().await?; - match transaction_status { - Some(TransactionStatusOf::::FinalityTimeout(_)) | - Some(TransactionStatusOf::::Usurped(_)) | - Some(TransactionStatusOf::::Dropped) | - Some(TransactionStatusOf::::Invalid) | - None => - return Err(anyhow::format_err!( - "We've been waiting for finalization of {} transaction, but it now has the {:?} status", - C::NAME, - transaction_status, - )), - Some(TransactionStatusOf::::Finalized(block_hash)) => { - log::trace!( - target: "bridge", - "{} transaction has been finalized at block {}", - C::NAME, - block_hash, - ); - return Ok(block_hash) - }, - _ => { - log::trace!( - target: "bridge", - "Received intermediate status of {} transaction: {:?}", - C::NAME, - transaction_status, - ); - }, - } - } -} - -/// Waits until token swap state is changed from `Started` to something else. -async fn wait_until_token_swap_state_is_changed( - client: &Client, - swap_state_storage_key: &StorageKey, - previous_token_swap_state: bp_token_swap::TokenSwapState, -) -> anyhow::Result> { - log::trace!(target: "bridge", "Waiting for token swap state change"); - loop { - async_std::task::sleep(C::AVERAGE_BLOCK_INTERVAL).await; - - let best_block = client.best_finalized_header_number().await?; - let best_block_hash = client.block_hash_by_number(best_block).await?; - log::trace!(target: "bridge", "Inspecting {} block {}/{}", C::NAME, best_block, best_block_hash); - - let token_swap_state = - read_token_swap_state(client, best_block_hash, swap_state_storage_key).await?; - match token_swap_state { - Some(new_token_swap_state) if new_token_swap_state == previous_token_swap_state => {}, - _ => { - log::trace!( - target: "bridge", - "Token swap state has been changed from {:?} to {:?}", - previous_token_swap_state, - token_swap_state, - ); - return Ok(token_swap_state) - }, - } - } -} - -/// Waits until swap can be claimed or canceled. -async fn wait_until_swap_unlocked( - client: &Client, - required_block_number: BlockNumberOf, -) -> anyhow::Result<()> { - log::trace!(target: "bridge", "Waiting for token swap unlock"); - loop { - async_std::task::sleep(C::AVERAGE_BLOCK_INTERVAL).await; - - let best_block = client.best_finalized_header_number().await?; - let best_block_hash = client.block_hash_by_number(best_block).await?; - if best_block >= required_block_number { - return Ok(()) - } - - log::trace!(target: "bridge", "Skipping {} block {}/{}", C::NAME, best_block, best_block_hash); - } -} - -/// Read state of the active token swap. -async fn read_token_swap_state( - client: &Client, - at_block: C::Hash, - swap_state_storage_key: &StorageKey, -) -> anyhow::Result> { - Ok(client.storage_value(swap_state_storage_key.clone(), Some(at_block)).await?) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::cli::{RuntimeVersionType, SourceRuntimeVersionParams, TargetRuntimeVersionParams}; - - #[test] - fn swap_tokens_millau_to_rialto_no_lock() { - let swap_tokens = SwapTokens::from_iter(vec![ - "swap-tokens", - "millau-to-rialto", - "--source-host", - "127.0.0.1", - "--source-port", - "9000", - "--source-signer", - "//Alice", - "--source-balance", - "8000000000", - "--target-host", - "127.0.0.1", - "--target-port", - "9001", - "--target-signer", - "//Bob", - "--target-balance", - "9000000000", - "no-lock", - ]); - - assert_eq!( - swap_tokens, - SwapTokens { - bridge: SwapTokensBridge::MillauToRialto, - source: SourceConnectionParams { - source_host: "127.0.0.1".into(), - source_port: 9000, - source_secure: false, - source_runtime_version: SourceRuntimeVersionParams { - source_version_mode: RuntimeVersionType::Bundle, - source_spec_version: None, - source_transaction_version: None, - } - }, - source_sign: SourceSigningParams { - source_signer: Some("//Alice".into()), - source_signer_password: None, - source_signer_file: None, - source_signer_password_file: None, - source_transactions_mortality: None, - }, - target: TargetConnectionParams { - target_host: "127.0.0.1".into(), - target_port: 9001, - target_secure: false, - target_runtime_version: TargetRuntimeVersionParams { - target_version_mode: RuntimeVersionType::Bundle, - target_spec_version: None, - target_transaction_version: None, - } - }, - target_sign: TargetSigningParams { - target_signer: Some("//Bob".into()), - target_signer_password: None, - target_signer_file: None, - target_signer_password_file: None, - target_transactions_mortality: None, - }, - swap_type: TokenSwapType::NoLock, - source_balance: Balance(8000000000), - target_balance: Balance(9000000000), - target_to_source_conversion_rate_override: None, - source_to_target_conversion_rate_override: None, - } - ); - } - - #[test] - fn swap_tokens_millau_to_rialto_lock_until() { - let swap_tokens = SwapTokens::from_iter(vec![ - "swap-tokens", - "millau-to-rialto", - "--source-host", - "127.0.0.1", - "--source-port", - "9000", - "--source-signer", - "//Alice", - "--source-balance", - "8000000000", - "--target-host", - "127.0.0.1", - "--target-port", - "9001", - "--target-signer", - "//Bob", - "--target-balance", - "9000000000", - "--target-to-source-conversion-rate-override", - "metric", - "--source-to-target-conversion-rate-override", - "84.56", - "lock-until-block", - "--blocks-before-expire", - "1", - ]); - - assert_eq!( - swap_tokens, - SwapTokens { - bridge: SwapTokensBridge::MillauToRialto, - source: SourceConnectionParams { - source_host: "127.0.0.1".into(), - source_port: 9000, - source_secure: false, - source_runtime_version: SourceRuntimeVersionParams { - source_version_mode: RuntimeVersionType::Bundle, - source_spec_version: None, - source_transaction_version: None, - } - }, - source_sign: SourceSigningParams { - source_signer: Some("//Alice".into()), - source_signer_password: None, - source_signer_file: None, - source_signer_password_file: None, - source_transactions_mortality: None, - }, - target: TargetConnectionParams { - target_host: "127.0.0.1".into(), - target_port: 9001, - target_secure: false, - target_runtime_version: TargetRuntimeVersionParams { - target_version_mode: RuntimeVersionType::Bundle, - target_spec_version: None, - target_transaction_version: None, - } - }, - target_sign: TargetSigningParams { - target_signer: Some("//Bob".into()), - target_signer_password: None, - target_signer_file: None, - target_signer_password_file: None, - target_transactions_mortality: None, - }, - swap_type: TokenSwapType::LockUntilBlock { - blocks_before_expire: 1, - swap_nonce: None, - }, - source_balance: Balance(8000000000), - target_balance: Balance(9000000000), - target_to_source_conversion_rate_override: Some(ConversionRateOverride::Metric), - source_to_target_conversion_rate_override: Some(ConversionRateOverride::Explicit( - 84.56 - )), - } - ); - } -} diff --git a/bridges/relays/client-kusama/Cargo.toml b/bridges/relays/client-kusama/Cargo.toml index 8ab3a27fd7c9..f6aee9dae220 100644 --- a/bridges/relays/client-kusama/Cargo.toml +++ b/bridges/relays/client-kusama/Cargo.toml @@ -15,13 +15,11 @@ scale-info = { version = "2.1.1", features = ["derive"] } bp-header-chain = { path = "../../primitives/header-chain" } bp-kusama = { path = "../../primitives/chain-kusama" } -bp-message-dispatch = { path = "../../primitives/message-dispatch" } bp-messages = { path = "../../primitives/messages" } bp-polkadot = { path = "../../primitives/chain-polkadot" } bp-polkadot-core = { path = "../../primitives/polkadot-core" } bp-runtime = { path = "../../primitives/runtime" } bridge-runtime-common = { path = "../../bin/runtime-common" } -pallet-bridge-dispatch = { path = "../../modules/dispatch" } # Substrate Dependencies diff --git a/bridges/relays/client-kusama/src/runtime.rs b/bridges/relays/client-kusama/src/runtime.rs index 59a919e6cb97..370486d1b834 100644 --- a/bridges/relays/client-kusama/src/runtime.rs +++ b/bridges/relays/client-kusama/src/runtime.rs @@ -27,30 +27,6 @@ use sp_runtime::FixedU128; /// Unchecked Kusama extrinsic. pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic; -/// Polkadot account ownership digest from Kusama. -/// -/// The byte vector returned by this function should be signed with a Polkadot account private key. -/// This way, the owner of `kusama_account_id` on Kusama proves that the Polkadot account private -/// key is also under his control. -pub fn kusama_to_polkadot_account_ownership_digest( - polkadot_call: &Call, - kusama_account_id: AccountId, - polkadot_spec_version: SpecVersion, -) -> Vec -where - Call: codec::Encode, - AccountId: codec::Encode, - SpecVersion: codec::Encode, -{ - pallet_bridge_dispatch::account_ownership_digest( - polkadot_call, - kusama_account_id, - polkadot_spec_version, - bp_runtime::KUSAMA_CHAIN_ID, - bp_runtime::POLKADOT_CHAIN_ID, - ) -} - /// Kusama Runtime `Call` enum. /// /// The enum represents a subset of possible `Call`s we can send to Kusama chain. @@ -115,16 +91,7 @@ pub enum BridgePolkadotMessagesCall { #[codec(index = 2)] update_pallet_parameter(BridgePolkadotMessagesParameter), #[codec(index = 3)] - send_message( - LaneId, - bp_message_dispatch::MessagePayload< - bp_kusama::AccountId, - bp_polkadot::AccountId, - bp_polkadot::AccountPublic, - Vec, - >, - bp_kusama::Balance, - ), + send_message(LaneId, Vec, bp_kusama::Balance), #[codec(index = 5)] receive_messages_proof( bp_polkadot::AccountId, diff --git a/bridges/relays/client-polkadot/Cargo.toml b/bridges/relays/client-polkadot/Cargo.toml index 1d61f0b4cdce..d52ddd9adf4c 100644 --- a/bridges/relays/client-polkadot/Cargo.toml +++ b/bridges/relays/client-polkadot/Cargo.toml @@ -15,13 +15,11 @@ scale-info = { version = "2.1.1", features = ["derive"] } bp-header-chain = { path = "../../primitives/header-chain" } bp-kusama = { path = "../../primitives/chain-kusama" } -bp-message-dispatch = { path = "../../primitives/message-dispatch" } bp-messages = { path = "../../primitives/messages" } bp-polkadot = { path = "../../primitives/chain-polkadot" } bp-polkadot-core = { path = "../../primitives/polkadot-core" } bp-runtime = { path = "../../primitives/runtime" } bridge-runtime-common = { path = "../../bin/runtime-common" } -pallet-bridge-dispatch = { path = "../../modules/dispatch" } # Substrate Dependencies diff --git a/bridges/relays/client-polkadot/src/runtime.rs b/bridges/relays/client-polkadot/src/runtime.rs index fa45115a6b5c..15421d94c699 100644 --- a/bridges/relays/client-polkadot/src/runtime.rs +++ b/bridges/relays/client-polkadot/src/runtime.rs @@ -27,30 +27,6 @@ use sp_runtime::FixedU128; /// Unchecked Polkadot extrinsic. pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic; -/// Kusama account ownership digest from Polkadot. -/// -/// The byte vector returned by this function should be signed with a Kusama account private key. -/// This way, the owner of `kusam_account_id` on Polkadot proves that the Kusama account private key -/// is also under his control. -pub fn polkadot_to_kusama_account_ownership_digest( - kusama_call: &Call, - kusam_account_id: AccountId, - kusama_spec_version: SpecVersion, -) -> Vec -where - Call: codec::Encode, - AccountId: codec::Encode, - SpecVersion: codec::Encode, -{ - pallet_bridge_dispatch::account_ownership_digest( - kusama_call, - kusam_account_id, - kusama_spec_version, - bp_runtime::POLKADOT_CHAIN_ID, - bp_runtime::KUSAMA_CHAIN_ID, - ) -} - /// Polkadot Runtime `Call` enum. /// /// The enum represents a subset of possible `Call`s we can send to Polkadot chain. @@ -115,16 +91,7 @@ pub enum BridgeKusamaMessagesCall { #[codec(index = 2)] update_pallet_parameter(BridgeKusamaMessagesParameter), #[codec(index = 3)] - send_message( - LaneId, - bp_message_dispatch::MessagePayload< - bp_polkadot::AccountId, - bp_kusama::AccountId, - bp_kusama::AccountPublic, - Vec, - >, - bp_polkadot::Balance, - ), + send_message(LaneId, Vec, bp_polkadot::Balance), #[codec(index = 5)] receive_messages_proof( bp_kusama::AccountId, diff --git a/bridges/relays/client-rococo/Cargo.toml b/bridges/relays/client-rococo/Cargo.toml index a2d84c496077..e806ad594c21 100644 --- a/bridges/relays/client-rococo/Cargo.toml +++ b/bridges/relays/client-rococo/Cargo.toml @@ -15,13 +15,11 @@ scale-info = { version = "2.1.1", features = ["derive"] } bridge-runtime-common = { path = "../../bin/runtime-common" } bp-header-chain = { path = "../../primitives/header-chain" } -bp-message-dispatch = { path = "../../primitives/message-dispatch" } bp-messages = { path = "../../primitives/messages" } bp-polkadot-core = { path = "../../primitives/polkadot-core" } bp-rococo = { path = "../../primitives/chain-rococo" } bp-runtime = { path = "../../primitives/runtime" } bp-wococo = { path = "../../primitives/chain-wococo" } -pallet-bridge-dispatch = { path = "../../modules/dispatch" } pallet-bridge-messages = { path = "../../modules/messages" } # Substrate Dependencies diff --git a/bridges/relays/client-rococo/src/runtime.rs b/bridges/relays/client-rococo/src/runtime.rs index b13808059964..249e75a709ff 100644 --- a/bridges/relays/client-rococo/src/runtime.rs +++ b/bridges/relays/client-rococo/src/runtime.rs @@ -26,30 +26,6 @@ use scale_info::TypeInfo; /// Unchecked Rococo extrinsic. pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic; -/// Wococo account ownership digest from Rococo. -/// -/// The byte vector returned by this function should be signed with a Wococo account private key. -/// This way, the owner of `rococo_account_id` on Rococo proves that the Wococo account private key -/// is also under his control. -pub fn rococo_to_wococo_account_ownership_digest( - wococo_call: &Call, - rococo_account_id: AccountId, - wococo_spec_version: SpecVersion, -) -> Vec -where - Call: codec::Encode, - AccountId: codec::Encode, - SpecVersion: codec::Encode, -{ - pallet_bridge_dispatch::account_ownership_digest( - wococo_call, - rococo_account_id, - wococo_spec_version, - bp_runtime::ROCOCO_CHAIN_ID, - bp_runtime::WOCOCO_CHAIN_ID, - ) -} - /// Rococo Runtime `Call` enum. /// /// The enum represents a subset of possible `Call`s we can send to Rococo chain. @@ -107,16 +83,7 @@ pub enum BridgeGrandpaWococoCall { #[allow(non_camel_case_types)] pub enum BridgeWococoMessagesCall { #[codec(index = 3)] - send_message( - LaneId, - bp_message_dispatch::MessagePayload< - bp_rococo::AccountId, - bp_wococo::AccountId, - bp_wococo::AccountPublic, - Vec, - >, - bp_rococo::Balance, - ), + send_message(LaneId, Vec, bp_rococo::Balance), #[codec(index = 5)] receive_messages_proof( bp_wococo::AccountId, diff --git a/bridges/relays/client-wococo/Cargo.toml b/bridges/relays/client-wococo/Cargo.toml index d9d892a9206d..b322849d94ad 100644 --- a/bridges/relays/client-wococo/Cargo.toml +++ b/bridges/relays/client-wococo/Cargo.toml @@ -14,13 +14,11 @@ scale-info = { version = "2.1.1", default-features = false, features = ["derive" # Bridge dependencies bridge-runtime-common = { path = "../../bin/runtime-common" } bp-header-chain = { path = "../../primitives/header-chain" } -bp-message-dispatch = { path = "../../primitives/message-dispatch" } bp-messages = { path = "../../primitives/messages" } bp-polkadot-core = { path = "../../primitives/polkadot-core" } bp-rococo = { path = "../../primitives/chain-rococo" } bp-runtime = { path = "../../primitives/runtime" } bp-wococo = { path = "../../primitives/chain-wococo" } -pallet-bridge-dispatch = { path = "../../modules/dispatch" } pallet-bridge-messages = { path = "../../modules/messages" } # Substrate Dependencies diff --git a/bridges/relays/client-wococo/src/runtime.rs b/bridges/relays/client-wococo/src/runtime.rs index b28e053086b1..f7b9c03bf9cd 100644 --- a/bridges/relays/client-wococo/src/runtime.rs +++ b/bridges/relays/client-wococo/src/runtime.rs @@ -26,30 +26,6 @@ use scale_info::TypeInfo; /// Unchecked Wococo extrinsic. pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic; -/// Rococo account ownership digest from Wococo. -/// -/// The byte vector returned by this function should be signed with a Rococo account private key. -/// This way, the owner of `wococo_account_id` on Rococo proves that the Rococo account private key -/// is also under his control. -pub fn wococo_to_rococo_account_ownership_digest( - rococo_call: &Call, - wococo_account_id: AccountId, - rococo_spec_version: SpecVersion, -) -> Vec -where - Call: codec::Encode, - AccountId: codec::Encode, - SpecVersion: codec::Encode, -{ - pallet_bridge_dispatch::account_ownership_digest( - rococo_call, - wococo_account_id, - rococo_spec_version, - bp_runtime::WOCOCO_CHAIN_ID, - bp_runtime::ROCOCO_CHAIN_ID, - ) -} - /// Wococo Runtime `Call` enum. /// /// The enum represents a subset of possible `Call`s we can send to Rococo chain. @@ -107,16 +83,7 @@ pub enum BridgeGrandpaRococoCall { #[allow(non_camel_case_types)] pub enum BridgeRococoMessagesCall { #[codec(index = 3)] - send_message( - LaneId, - bp_message_dispatch::MessagePayload< - bp_rococo::AccountId, - bp_wococo::AccountId, - bp_wococo::AccountPublic, - Vec, - >, - bp_rococo::Balance, - ), + send_message(LaneId, Vec, bp_rococo::Balance), #[codec(index = 5)] receive_messages_proof( bp_rococo::AccountId,