From 81d4eb9ea66eaef06d12974fb49f1a955a26ac4e Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Wed, 7 Apr 2021 19:50:16 -0400 Subject: [PATCH] Westend Mock Runtime and Client (#874) * Copy-Pasta Call code from mock Rococo to mock Westend runtime * Update Westend client to include signing and call related code * Add missing part of license header * Move `account_info_storage_key` to `polkadot-core` crate --- bridges/primitives/chain-rococo/Cargo.toml | 5 -- bridges/primitives/chain-rococo/src/lib.rs | 43 +---------- bridges/primitives/chain-westend/Cargo.toml | 4 ++ bridges/primitives/chain-westend/src/lib.rs | 18 +++++ bridges/primitives/polkadot-core/Cargo.toml | 3 + bridges/primitives/polkadot-core/src/lib.rs | 34 ++++++++- bridges/relays/client-rococo/src/lib.rs | 9 ++- bridges/relays/client-westend/src/lib.rs | 80 +++++++++++++++++++-- 8 files changed, 143 insertions(+), 53 deletions(-) diff --git a/bridges/primitives/chain-rococo/Cargo.toml b/bridges/primitives/chain-rococo/Cargo.toml index 667b11275f..acdbfc6c51 100644 --- a/bridges/primitives/chain-rococo/Cargo.toml +++ b/bridges/primitives/chain-rococo/Cargo.toml @@ -15,22 +15,17 @@ bp-polkadot-core = { path = "../polkadot-core", default-features = false } bp-runtime = { path = "../runtime", default-features = false } # Substrate Based Dependencies -frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-api = { 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 } sp-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -[dev-dependencies] -hex = "0.4" - [features] default = ["std"] std = [ "bp-messages/std", "bp-polkadot-core/std", "bp-runtime/std", - "frame-support/std", "parity-scale-codec/std", "sp-api/std", "sp-runtime/std", diff --git a/bridges/primitives/chain-rococo/src/lib.rs b/bridges/primitives/chain-rococo/src/lib.rs index a9c014238d..38dd92561b 100644 --- a/bridges/primitives/chain-rococo/src/lib.rs +++ b/bridges/primitives/chain-rococo/src/lib.rs @@ -21,12 +21,14 @@ #![allow(clippy::unnecessary_mut_passed)] use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState, Weight}; -use frame_support::{Blake2_128Concat, StorageHasher, Twox128}; use sp_std::prelude::*; use sp_version::RuntimeVersion; pub use bp_polkadot_core::*; +/// Rococo Chain +pub type Rococo = PolkadotLike; + pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic; pub const VERSION: RuntimeVersion = RuntimeVersion { @@ -55,29 +57,6 @@ impl sp_runtime::traits::Dispatchable for Call { } } -/// Return a storage key for account data. -/// -/// This is based on FRAME storage-generation code from Substrate: -/// https://github.com/paritytech/substrate/blob/c939ceba381b6313462d47334f775e128ea4e95d/frame/support/src/storage/generator/map.rs#L74 -/// The equivalent command to invoke in case full `Runtime` is known is this: -/// `let key = frame_system::Account::::storage_map_final_key(&account_id);` -pub fn account_info_storage_key(id: &AccountId) -> Vec { - let module_prefix_hashed = Twox128::hash(b"System"); - let storage_prefix_hashed = Twox128::hash(b"Account"); - let key_hashed = parity_scale_codec::Encode::using_encoded(id, Blake2_128Concat::hash); - - let mut final_key = Vec::with_capacity(module_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.len()); - - final_key.extend_from_slice(&module_prefix_hashed[..]); - final_key.extend_from_slice(&storage_prefix_hashed[..]); - final_key.extend_from_slice(&key_hashed); - - final_key -} - -/// Rococo Chain -pub type Rococo = PolkadotLike; - // We use this to get the account on Rococo (target) which is derived from Westend's (source) // account. pub fn derive_account_from_westend_id(id: bp_runtime::SourceAccount) -> AccountId { @@ -165,19 +144,3 @@ sp_api::decl_runtime_apis! { fn unrewarded_relayers_state(lane: LaneId) -> UnrewardedRelayersState; } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn should_generate_storage_key() { - let acc = [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, - ] - .into(); - let key = account_info_storage_key(&acc); - assert_eq!(hex::encode(key), "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da92dccd599abfe1920a1cff8a7358231430102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"); - } -} diff --git a/bridges/primitives/chain-westend/Cargo.toml b/bridges/primitives/chain-westend/Cargo.toml index 3552141da5..c96e8703d8 100644 --- a/bridges/primitives/chain-westend/Cargo.toml +++ b/bridges/primitives/chain-westend/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] +parity-scale-codec = { version = "2.0.0", default-features = false, features = ["derive"] } # Bridge Dependencies bp-messages = { path = "../messages", default-features = false } @@ -15,6 +16,7 @@ bp-runtime = { path = "../runtime", default-features = false } # Substrate Based Dependencies sp-api = { 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 } sp-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -24,7 +26,9 @@ std = [ "bp-messages/std", "bp-polkadot-core/std", "bp-runtime/std", + "parity-scale-codec/std", "sp-api/std", + "sp-runtime/std", "sp-std/std", "sp-version/std", ] diff --git a/bridges/primitives/chain-westend/src/lib.rs b/bridges/primitives/chain-westend/src/lib.rs index c4f4b0b644..3b52534afa 100644 --- a/bridges/primitives/chain-westend/src/lib.rs +++ b/bridges/primitives/chain-westend/src/lib.rs @@ -29,6 +29,8 @@ pub use bp_polkadot_core::*; /// Westend Chain pub type Westend = PolkadotLike; +pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic; + /// Runtime version. pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: sp_version::create_runtime_str!("westend"), @@ -40,6 +42,22 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { transaction_version: 5, }; +#[derive(parity_scale_codec::Encode, parity_scale_codec::Decode, Debug, PartialEq, Eq, Clone)] +pub enum Call { + MockModule, +} + +impl sp_runtime::traits::Dispatchable for Call { + type Origin = (); + type Config = (); + type Info = (); + type PostInfo = (); + + fn dispatch(self, _origin: Self::Origin) -> sp_runtime::DispatchResultWithInfo { + unimplemented!("The Call is not expected to be dispatched.") + } +} + // We use this to get the account on Westend (target) which is derived from Rococo's (source) // account. pub fn derive_account_from_rococo_id(id: bp_runtime::SourceAccount) -> AccountId { diff --git a/bridges/primitives/polkadot-core/Cargo.toml b/bridges/primitives/polkadot-core/Cargo.toml index af5fe84d2a..995f948e5d 100644 --- a/bridges/primitives/polkadot-core/Cargo.toml +++ b/bridges/primitives/polkadot-core/Cargo.toml @@ -24,6 +24,9 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +[dev-dependencies] +hex = "0.4" + [features] default = ["std"] std = [ diff --git a/bridges/primitives/polkadot-core/src/lib.rs b/bridges/primitives/polkadot-core/src/lib.rs index 94e9dda903..8cf62c0030 100644 --- a/bridges/primitives/polkadot-core/src/lib.rs +++ b/bridges/primitives/polkadot-core/src/lib.rs @@ -25,7 +25,7 @@ use frame_support::{ constants::{BlockExecutionWeight, ExtrinsicBaseWeight, WEIGHT_PER_SECOND}, DispatchClass, Weight, }, - RuntimeDebug, + Blake2_128Concat, RuntimeDebug, StorageHasher, Twox128, }; use frame_system::limits; use sp_core::Hasher as HasherT; @@ -34,6 +34,7 @@ use sp_runtime::{ traits::{BlakeTwo256, IdentifyAccount, Verify}, MultiSignature, OpaqueExtrinsic, Perbill, }; +use sp_std::prelude::Vec; // Re-export's to avoid extra substrate dependencies in chain-specific crates. pub use frame_support::Parameter; @@ -290,6 +291,26 @@ impl Convert for AccountIdConverter { } } +/// Return a storage key for account data. +/// +/// This is based on FRAME storage-generation code from Substrate: +/// https://github.com/paritytech/substrate/blob/c939ceba381b6313462d47334f775e128ea4e95d/frame/support/src/storage/generator/map.rs#L74 +/// The equivalent command to invoke in case full `Runtime` is known is this: +/// `let key = frame_system::Account::::storage_map_final_key(&account_id);` +pub fn account_info_storage_key(id: &AccountId) -> Vec { + let module_prefix_hashed = Twox128::hash(b"System"); + let storage_prefix_hashed = Twox128::hash(b"Account"); + let key_hashed = parity_scale_codec::Encode::using_encoded(id, Blake2_128Concat::hash); + + let mut final_key = Vec::with_capacity(module_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.len()); + + final_key.extend_from_slice(&module_prefix_hashed[..]); + final_key.extend_from_slice(&storage_prefix_hashed[..]); + final_key.extend_from_slice(&key_hashed); + + final_key +} + #[cfg(test)] mod tests { use super::*; @@ -305,4 +326,15 @@ mod tests { MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, ); } + + #[test] + fn should_generate_storage_key() { + let acc = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, + ] + .into(); + let key = account_info_storage_key(&acc); + assert_eq!(hex::encode(key), "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da92dccd599abfe1920a1cff8a7358231430102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"); + } } diff --git a/bridges/relays/client-rococo/src/lib.rs b/bridges/relays/client-rococo/src/lib.rs index 3661b31e69..ccf7a89163 100644 --- a/bridges/relays/client-rococo/src/lib.rs +++ b/bridges/relays/client-rococo/src/lib.rs @@ -1,3 +1,6 @@ +// 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 @@ -22,6 +25,9 @@ use std::time::Duration; /// Rococo header id. pub type HeaderId = relay_utils::HeaderId; +/// Rococo header type used in headers sync. +pub type SyncHeader = relay_substrate_client::SyncHeader; + /// Rococo chain definition #[derive(Debug, Clone, Copy)] pub struct Rococo; @@ -111,6 +117,3 @@ impl Default for SigningParams { } } } - -/// Rococo header type used in headers sync. -pub type SyncHeader = relay_substrate_client::SyncHeader; diff --git a/bridges/relays/client-westend/src/lib.rs b/bridges/relays/client-westend/src/lib.rs index c8594a925d..0a6569512a 100644 --- a/bridges/relays/client-westend/src/lib.rs +++ b/bridges/relays/client-westend/src/lib.rs @@ -16,12 +16,18 @@ //! Types used to connect to the Westend chain. -use relay_substrate_client::{Chain, ChainBase}; +use codec::Encode; +use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, TransactionSignScheme}; +use sp_core::{storage::StorageKey, Pair}; +use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; use std::time::Duration; /// Westend header id. pub type HeaderId = relay_utils::HeaderId; +/// Westend header type used in headers sync. +pub type SyncHeader = relay_substrate_client::SyncHeader; + /// Westend chain definition #[derive(Debug, Clone, Copy)] pub struct Westend; @@ -40,8 +46,74 @@ impl Chain for Westend { type AccountId = bp_westend::AccountId; type Index = bp_westend::Nonce; type SignedBlock = bp_westend::SignedBlock; - type Call = (); + type Call = bp_westend::Call; } -/// Westend header type used in headers sync. -pub type SyncHeader = relay_substrate_client::SyncHeader; +impl ChainWithBalances for Westend { + type NativeBalance = bp_westend::Balance; + + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + StorageKey(bp_westend::account_info_storage_key(account_id)) + } +} + +impl TransactionSignScheme for Westend { + type Chain = Westend; + type AccountKeyPair = sp_core::sr25519::Pair; + type SignedTransaction = bp_westend::UncheckedExtrinsic; + + fn sign_transaction( + genesis_hash: ::Hash, + signer: &Self::AccountKeyPair, + signer_nonce: ::Index, + call: ::Call, + ) -> Self::SignedTransaction { + let raw_payload = SignedPayload::new( + call, + bp_westend::SignedExtensions::new( + bp_westend::VERSION, + sp_runtime::generic::Era::Immortal, + genesis_hash, + signer_nonce, + 0, + ), + ) + .expect("SignedExtension never fails."); + + let signature = raw_payload.using_encoded(|payload| signer.sign(payload)); + let signer: sp_runtime::MultiSigner = signer.public().into(); + let (call, extra, _) = raw_payload.deconstruct(); + + bp_westend::UncheckedExtrinsic::new_signed(call, signer.into_account(), signature.into(), extra) + } +} + +/// Westend signing params. +#[derive(Clone)] +pub struct SigningParams { + /// Substrate transactions signer. + pub signer: sp_core::sr25519::Pair, +} + +impl SigningParams { + /// Create signing params from SURI and password. + pub fn from_suri(suri: &str, password: Option<&str>) -> Result { + Ok(SigningParams { + signer: sp_core::sr25519::Pair::from_string(suri, password)?, + }) + } +} + +impl std::fmt::Debug for SigningParams { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.signer.public()) + } +} + +impl Default for SigningParams { + fn default() -> Self { + SigningParams { + signer: sp_keyring::AccountKeyring::Alice.pair(), + } + } +}