diff --git a/bridges/bin/rialto/node/Cargo.toml b/bridges/bin/rialto/node/Cargo.toml index 08b152b682..c5453468f7 100644 --- a/bridges/bin/rialto/node/Cargo.toml +++ b/bridges/bin/rialto/node/Cargo.toml @@ -17,6 +17,7 @@ structopt = "0.3.21" bp-message-lane = { path = "../../../primitives/message-lane" } bp-runtime = { path = "../../../primitives/runtime" } +bp-rialto = { path = "../../../primitives/rialto" } pallet-message-lane-rpc = { path = "../../../modules/message-lane/rpc" } rialto-runtime = { path = "../runtime" } diff --git a/bridges/bin/rialto/node/src/chain_spec.rs b/bridges/bin/rialto/node/src/chain_spec.rs index 939dcc5e3f..229fbd1cc9 100644 --- a/bridges/bin/rialto/node/src/chain_spec.rs +++ b/bridges/bin/rialto/node/src/chain_spec.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . +use bp_rialto::derive_account_from_millau_id; use rialto_runtime::{ AccountId, AuraConfig, BalancesConfig, BridgeKovanConfig, BridgeMillauConfig, BridgeRialtoPoAConfig, GenesisConfig, GrandpaConfig, SessionConfig, SessionKeys, Signature, SudoConfig, SystemConfig, WASM_BINARY, @@ -121,6 +122,10 @@ impl Alternative { get_account_id_from_seed::("Ferdie//stash"), get_account_id_from_seed::("George//stash"), get_account_id_from_seed::("Harry//stash"), + derive_account_from_millau_id(bp_runtime::SourceAccount::Root), + derive_account_from_millau_id(bp_runtime::SourceAccount::Account( + get_account_id_from_seed::("Dave"), + )), ], true, ) @@ -191,3 +196,22 @@ fn load_kovan_bridge_config() -> BridgeKovanConfig { initial_validators: rialto_runtime::kovan::genesis_validators(), } } + +#[test] +fn derived_dave_account_is_as_expected() { + let dave = get_account_id_from_seed::("Dave"); + let derived: AccountId = derive_account_from_millau_id(bp_runtime::SourceAccount::Account(dave)); + assert_eq!( + derived.to_string(), + "5Hg7WQyk8C1FmPzxY3xSjR7S6zZZC5sAL35vMr6NpW17jBhQ".to_string() + ); +} + +#[test] +fn derived_root_account_is_as_expected() { + let root: AccountId = derive_account_from_millau_id(bp_runtime::SourceAccount::Root); + assert_eq!( + root.to_string(), + "5HYYwXQvxhgdcBYs6kzqfK1HW6M3UF3Kh4YM7j288yiqbhnt".to_string() + ); +} diff --git a/bridges/modules/call-dispatch/src/lib.rs b/bridges/modules/call-dispatch/src/lib.rs index 6e7d26f545..19c728162e 100644 --- a/bridges/modules/call-dispatch/src/lib.rs +++ b/bridges/modules/call-dispatch/src/lib.rs @@ -211,8 +211,10 @@ impl, I: Instance> MessageDispatch for Module { // prepare dispatch origin let origin_account = match message.origin { CallOrigin::SourceRoot => { - let encoded_id = derive_account_id::(bridge, SourceAccount::Root); - T::AccountIdConverter::convert(encoded_id) + let hex_id = derive_account_id::(bridge, SourceAccount::Root); + let target_id = T::AccountIdConverter::convert(hex_id); + frame_support::debug::trace!("Root Account: {:?}", &target_id); + target_id } CallOrigin::TargetAccount(source_account_id, target_public, target_signature) => { let mut signed_message = Vec::new(); @@ -232,18 +234,24 @@ impl, I: Instance> MessageDispatch for Module { return; } + frame_support::debug::trace!("Target Account: {:?}", &target_account); target_account } CallOrigin::SourceAccount(source_account_id) => { - let encoded_id = derive_account_id(bridge, SourceAccount::Account(source_account_id)); - T::AccountIdConverter::convert(encoded_id) + let hex_id = derive_account_id(bridge, SourceAccount::Account(source_account_id)); + let target_id = T::AccountIdConverter::convert(hex_id); + frame_support::debug::trace!("Source Account: {:?}", &target_id); + target_id } }; // finally dispatch message let origin = RawOrigin::Signed(origin_account).into(); + + frame_support::debug::trace!("Message being dispatched is: {:?}", &message.call); let dispatch_result = message.call.dispatch(origin); let actual_call_weight = extract_actual_weight(&dispatch_result, &dispatch_info); + frame_support::debug::trace!( "Message {:?}/{:?} has been dispatched. Weight: {} of {}. Result: {:?}", bridge, diff --git a/bridges/primitives/rialto/src/lib.rs b/bridges/primitives/rialto/src/lib.rs index 30b1f0ae79..458a3abc0a 100644 --- a/bridges/primitives/rialto/src/lib.rs +++ b/bridges/primitives/rialto/src/lib.rs @@ -25,7 +25,7 @@ use bp_runtime::Chain; use frame_support::{weights::Weight, RuntimeDebug}; use sp_core::Hasher as HasherT; use sp_runtime::{ - traits::{BlakeTwo256, IdentifyAccount, Verify}, + traits::{BlakeTwo256, Convert, IdentifyAccount, Verify}, MultiSignature, MultiSigner, }; use sp_std::prelude::*; @@ -112,12 +112,25 @@ pub type Balance = u128; /// Convert a 256-bit hash into an AccountId. pub struct AccountIdConverter; -impl sp_runtime::traits::Convert for AccountIdConverter { +impl Convert for AccountIdConverter { fn convert(hash: sp_core::H256) -> AccountId { hash.to_fixed_bytes().into() } } +// We use this to get the account on Rialto (target) which is derived from Millau's (source) +// account. We do this so we can fund the derived account on Rialto at Genesis to it can pay +// transaction fees. +// +// The reason we can use the same `AccountId` type for both chains is because they share the same +// development seed phrase. +// +// Note that this should only be used for testing. +pub fn derive_account_from_millau_id(id: bp_runtime::SourceAccount) -> AccountId { + let encoded_id = bp_runtime::derive_account_id(*b"mlau", id); + AccountIdConverter::convert(encoded_id) +} + sp_api::decl_runtime_apis! { /// API for querying information about Rialto headers from the Bridge Pallet instance. /// diff --git a/bridges/relays/substrate/src/cli.rs b/bridges/relays/substrate/src/cli.rs index 0119359cde..d3662ba268 100644 --- a/bridges/relays/substrate/src/cli.rs +++ b/bridges/relays/substrate/src/cli.rs @@ -101,12 +101,15 @@ pub enum Command { /// Hex-encoded lane id. #[structopt(long)] lane: HexLaneId, - /// Message type. - #[structopt(long, possible_values = &ToRialtoMessage::variants())] - message: ToRialtoMessage, /// Delivery and dispatch fee. #[structopt(long)] fee: bp_millau::Balance, + /// Message type. + #[structopt(subcommand)] + message: ToRialtoMessage, + /// The origin to use when dispatching the message on the target chain. + #[structopt(long, possible_values = &Origins::variants())] + origin: Origins, }, /// Serve given lane of Rialto -> Millau messages. RialtoMessagesToMillau { @@ -144,12 +147,18 @@ pub enum Command { }, } -arg_enum! { - #[derive(Debug)] - /// All possible messages that may be delivered to the Rialto chain. - pub enum ToRialtoMessage { - Remark, - } +/// All possible messages that may be delivered to the Rialto chain. +#[derive(StructOpt, Debug)] +pub enum ToRialtoMessage { + /// Make an on-chain remark (comment). + Remark, + /// Transfer the specified `amount` of native tokens to a particular `recipient`. + Transfer { + #[structopt(long)] + recipient: bp_rialto::AccountId, + #[structopt(long)] + amount: bp_rialto::Balance, + }, } arg_enum! { @@ -160,6 +169,16 @@ arg_enum! { } } +arg_enum! { + #[derive(Debug)] + /// The origin to use when dispatching the message on the target chain. + pub enum Origins { + Root, + Target, + Source, + } +} + /// Lane id. #[derive(Debug)] pub struct HexLaneId(LaneId); diff --git a/bridges/relays/substrate/src/main.rs b/bridges/relays/substrate/src/main.rs index 8258f985a7..789105a27b 100644 --- a/bridges/relays/substrate/src/main.rs +++ b/bridges/relays/substrate/src/main.rs @@ -248,6 +248,8 @@ async fn run_command(command: cli::Command) -> Result<(), String> { lane, message, fee, + origin, + .. } => { let millau_client = MillauClient::new(ConnectionParams { host: millau.millau_host, @@ -277,33 +279,51 @@ async fn run_command(command: cli::Command) -> Result<(), String> { .as_bytes() .to_vec(), )), + cli::ToRialtoMessage::Transfer { recipient, amount } => { + rialto_runtime::Call::Balances(rialto_runtime::BalancesCall::transfer(recipient, amount)) + } }; - let rialto_call_weight = rialto_call.get_dispatch_info().weight; + let rialto_call_weight = rialto_call.get_dispatch_info().weight; let millau_sender_public: bp_millau::AccountSigner = millau_sign.signer.public().clone().into(); let millau_account_id: bp_millau::AccountId = millau_sender_public.into_account(); let rialto_origin_public = rialto_sign.signer.public(); - let mut rialto_origin_signature_message = Vec::new(); - rialto_call.encode_to(&mut rialto_origin_signature_message); - millau_account_id.encode_to(&mut rialto_origin_signature_message); - let rialto_origin_signature = rialto_sign.signer.sign(&rialto_origin_signature_message); + let payload = match origin { + cli::Origins::Root => MessagePayload { + spec_version: rialto_runtime::VERSION.spec_version, + weight: rialto_call_weight, + origin: CallOrigin::SourceRoot, + call: rialto_call.encode(), + }, + cli::Origins::Source => MessagePayload { + spec_version: rialto_runtime::VERSION.spec_version, + weight: rialto_call_weight, + origin: CallOrigin::SourceAccount(millau_account_id), + call: rialto_call.encode(), + }, + cli::Origins::Target => { + let mut rialto_origin_signature_message = Vec::new(); + rialto_call.encode_to(&mut rialto_origin_signature_message); + millau_account_id.encode_to(&mut rialto_origin_signature_message); + let rialto_origin_signature = rialto_sign.signer.sign(&rialto_origin_signature_message); - let millau_call = - millau_runtime::Call::BridgeRialtoMessageLane(millau_runtime::MessageLaneCall::send_message( - lane.into(), MessagePayload { spec_version: rialto_runtime::VERSION.spec_version, weight: rialto_call_weight, origin: CallOrigin::TargetAccount( - millau_account_id, + millau_account_id.clone(), rialto_origin_public.into(), rialto_origin_signature.into(), ), call: rialto_call.encode(), - }, - fee, - )); + } + } + }; + + let millau_call = millau_runtime::Call::BridgeRialtoMessageLane( + millau_runtime::MessageLaneCall::send_message(lane.into(), payload, fee), + ); let signed_millau_call = Millau::sign_transaction( &millau_client,