diff --git a/bridges/primitives/chain-rococo/Cargo.toml b/bridges/primitives/chain-rococo/Cargo.toml index 863203ad8d..667b11275f 100644 --- a/bridges/primitives/chain-rococo/Cargo.toml +++ b/bridges/primitives/chain-rococo/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 } @@ -14,8 +15,14 @@ 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"] @@ -23,6 +30,10 @@ std = [ "bp-messages/std", "bp-polkadot-core/std", "bp-runtime/std", + "frame-support/std", + "parity-scale-codec/std", "sp-api/std", + "sp-runtime/std", "sp-std/std", + "sp-version/std", ] diff --git a/bridges/primitives/chain-rococo/src/lib.rs b/bridges/primitives/chain-rococo/src/lib.rs index 13a9934780..3d1ce94c04 100644 --- a/bridges/primitives/chain-rococo/src/lib.rs +++ b/bridges/primitives/chain-rococo/src/lib.rs @@ -21,10 +21,60 @@ #![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::*; +pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic; + +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: sp_version::create_runtime_str!("rococo"), + impl_name: sp_version::create_runtime_str!("parity-rococo-v1-1"), + authoring_version: 0, + spec_version: 30, + impl_version: 0, + apis: sp_version::create_apis_vec![[]], + transaction_version: 6, +}; + +#[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.") + } +} + +/// 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; @@ -115,3 +165,19 @@ 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/polkadot-core/Cargo.toml b/bridges/primitives/polkadot-core/Cargo.toml index 8df209ca67..af5fe84d2a 100644 --- a/bridges/primitives/polkadot-core/Cargo.toml +++ b/bridges/primitives/polkadot-core/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 @@ -21,6 +22,7 @@ sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" , sp-core = { 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 } [features] default = ["std"] @@ -29,8 +31,10 @@ std = [ "bp-runtime/std", "frame-support/std", "frame-system/std", + "parity-scale-codec/std", "sp-api/std", "sp-core/std", "sp-runtime/std", "sp-std/std", + "sp-version/std", ] diff --git a/bridges/primitives/polkadot-core/src/lib.rs b/bridges/primitives/polkadot-core/src/lib.rs index f3f9563774..eaeddfa2af 100644 --- a/bridges/primitives/polkadot-core/src/lib.rs +++ b/bridges/primitives/polkadot-core/src/lib.rs @@ -19,6 +19,7 @@ use bp_messages::MessageNonce; use bp_runtime::Chain; use frame_support::{ + dispatch::Dispatchable, parameter_types, weights::{ constants::{BlockExecutionWeight, ExtrinsicBaseWeight, WEIGHT_PER_SECOND}, @@ -31,7 +32,7 @@ use sp_core::Hasher as HasherT; use sp_runtime::{ generic, traits::{BlakeTwo256, IdentifyAccount, Verify}, - MultiSignature, OpaqueExtrinsic as UncheckedExtrinsic, Perbill, + MultiSignature, OpaqueExtrinsic, Perbill, }; // Re-export's to avoid extra substrate dependencies in chain-specific crates. @@ -141,6 +142,12 @@ pub type BlockNumber = u32; /// Hash type used in Polkadot-like chains. pub type Hash = ::Out; +/// Account Index (a.k.a. nonce). +pub type Index = u32; + +/// Hashing type. +pub type Hashing = BlakeTwo256; + /// The type of an object that can produce hashes on Polkadot-like chains. pub type Hasher = BlakeTwo256; @@ -160,7 +167,7 @@ pub type AccountId = ::AccountId; pub type Nonce = u32; /// Block type of Polkadot-like chains. -pub type Block = generic::Block; +pub type Block = generic::Block; /// Polkadot-like block signed with a Justification. pub type SignedBlock = generic::SignedBlock; @@ -168,6 +175,86 @@ pub type SignedBlock = generic::SignedBlock; /// The balance of an account on Polkadot-like chain. pub type Balance = u128; +/// Unchecked Extrinsic type. +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic>; + +/// A type of the data encoded as part of the transaction. +pub type SignedExtra = ((), (), (), sp_runtime::generic::Era, Nonce, (), Balance); + +/// Parameters which are part of the payload used to produce transaction signature, +/// but don't end up in the transaction itself (i.e. inherent part of the runtime). +pub type AdditionalSigned = (u32, u32, Hash, Hash, (), (), ()); + +/// A simplified version of signed extensions meant for producing signed transactions +/// and signed payload in the client code. +#[derive(PartialEq, Eq, Clone, RuntimeDebug)] +pub struct SignedExtensions { + encode_payload: SignedExtra, + additional_signed: AdditionalSigned, + _data: sp_std::marker::PhantomData, +} + +impl parity_scale_codec::Encode for SignedExtensions { + fn using_encoded R>(&self, f: F) -> R { + self.encode_payload.using_encoded(f) + } +} + +impl parity_scale_codec::Decode for SignedExtensions { + fn decode(_input: &mut I) -> Result { + unimplemented!("SignedExtensions are never meant to be decoded, they are only used to create transaction"); + } +} + +impl SignedExtensions { + pub fn new( + version: sp_version::RuntimeVersion, + era: sp_runtime::generic::Era, + genesis_hash: Hash, + nonce: Nonce, + tip: Balance, + ) -> Self { + Self { + encode_payload: ( + (), // spec version + (), // tx version + (), // genesis + era, // era + nonce, // nonce (compact encoding) + (), // Check weight + tip, // transaction payment / tip (compact encoding) + ), + additional_signed: ( + version.spec_version, + version.transaction_version, + genesis_hash, + genesis_hash, + (), + (), + (), + ), + _data: Default::default(), + } + } +} + +impl sp_runtime::traits::SignedExtension for SignedExtensions +where + Call: parity_scale_codec::Codec + sp_std::fmt::Debug + Sync + Send + Clone + Eq + PartialEq, + Call: Dispatchable, +{ + const IDENTIFIER: &'static str = "Not needed."; + + type AccountId = AccountId; + type Call = Call; + type AdditionalSigned = AdditionalSigned; + type Pre = (); + + fn additional_signed(&self) -> Result { + Ok(self.additional_signed) + } +} + /// Polkadot-like chain. #[derive(RuntimeDebug)] pub struct PolkadotLike; diff --git a/bridges/relays/client-rococo/Cargo.toml b/bridges/relays/client-rococo/Cargo.toml new file mode 100644 index 0000000000..095f365374 --- /dev/null +++ b/bridges/relays/client-rococo/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "relay-rococo-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "2.0.0" } +headers-relay = { path = "../headers" } +relay-substrate-client = { path = "../client-substrate" } +relay-utils = { path = "../utils" } + +# Bridge dependencies +bp-rococo = { path = "../../primitives/chain-rococo" } + +# Substrate Dependencies +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/bridges/relays/client-rococo/src/lib.rs b/bridges/relays/client-rococo/src/lib.rs new file mode 100644 index 0000000000..3661b31e69 --- /dev/null +++ b/bridges/relays/client-rococo/src/lib.rs @@ -0,0 +1,116 @@ +// 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 . + +//! Types used to connect to the Rococo-Substrate chain. + +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; + +/// Rococo header id. +pub type HeaderId = relay_utils::HeaderId; + +/// Rococo chain definition +#[derive(Debug, Clone, Copy)] +pub struct Rococo; + +impl ChainBase for Rococo { + type BlockNumber = bp_rococo::BlockNumber; + type Hash = bp_rococo::Hash; + type Hasher = bp_rococo::Hashing; + type Header = bp_rococo::Header; +} + +impl Chain for Rococo { + const NAME: &'static str = "Rococo"; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); + + type AccountId = bp_rococo::AccountId; + type Index = bp_rococo::Index; + type SignedBlock = bp_rococo::SignedBlock; + type Call = bp_rococo::Call; +} + +impl ChainWithBalances for Rococo { + type NativeBalance = bp_rococo::Balance; + + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + StorageKey(bp_rococo::account_info_storage_key(account_id)) + } +} + +impl TransactionSignScheme for Rococo { + type Chain = Rococo; + type AccountKeyPair = sp_core::sr25519::Pair; + type SignedTransaction = bp_rococo::UncheckedExtrinsic; + + fn sign_transaction( + genesis_hash: ::Hash, + signer: &Self::AccountKeyPair, + signer_nonce: ::Index, + call: ::Call, + ) -> Self::SignedTransaction { + let raw_payload = SignedPayload::new( + call, + bp_rococo::SignedExtensions::new( + bp_rococo::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_rococo::UncheckedExtrinsic::new_signed(call, signer.into_account(), signature.into(), extra) + } +} + +/// Rococo 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(), + } + } +} + +/// Rococo header type used in headers sync. +pub type SyncHeader = relay_substrate_client::SyncHeader;