diff --git a/bridges/bin/millau/node/Cargo.toml b/bridges/bin/millau/node/Cargo.toml index b650bd478a62..c4438d0cef3e 100644 --- a/bridges/bin/millau/node/Cargo.toml +++ b/bridges/bin/millau/node/Cargo.toml @@ -23,9 +23,13 @@ pallet-bridge-messages = { path = "../../../modules/messages" } # Substrate Dependencies +beefy-gadget = { git = "https://github.com/paritytech/substrate", branch = "master" } +beefy-gadget-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } +beefy-primitives = { git = "https://github.com/paritytech/substrate", branch = "master" } frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master" } frame-benchmarking-cli = { git = "https://github.com/paritytech/substrate", branch = "master" } node-inspect = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-mmr-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-transaction-payment-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-basic-authorship = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-cli = { git = "https://github.com/paritytech/substrate", branch = "master", features = ["wasmtime"] } diff --git a/bridges/bin/millau/node/src/chain_spec.rs b/bridges/bin/millau/node/src/chain_spec.rs index 05496bb64f63..fbfca8692fcb 100644 --- a/bridges/bin/millau/node/src/chain_spec.rs +++ b/bridges/bin/millau/node/src/chain_spec.rs @@ -14,11 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . +use beefy_primitives::crypto::AuthorityId as BeefyId; use bp_millau::derive_account_from_rialto_id; use millau_runtime::{ - AccountId, AuraConfig, BalancesConfig, BridgeRialtoMessagesConfig, BridgeWestendGrandpaConfig, - GenesisConfig, GrandpaConfig, SessionConfig, SessionKeys, Signature, SudoConfig, SystemConfig, - WASM_BINARY, + AccountId, AuraConfig, BalancesConfig, BeefyConfig, BridgeRialtoMessagesConfig, + BridgeWestendGrandpaConfig, GenesisConfig, GrandpaConfig, SessionConfig, SessionKeys, + Signature, SudoConfig, SystemConfig, WASM_BINARY, }; use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_core::{sr25519, Pair, Public}; @@ -57,10 +58,11 @@ where } /// Helper function to generate an authority key for Aura -pub fn get_authority_keys_from_seed(s: &str) -> (AccountId, AuraId, GrandpaId) { +pub fn get_authority_keys_from_seed(s: &str) -> (AccountId, AuraId, BeefyId, GrandpaId) { ( get_account_id_from_seed::(s), get_from_seed::(s), + get_from_seed::(s), get_from_seed::(s), ) } @@ -173,12 +175,12 @@ impl Alternative { } } -fn session_keys(aura: AuraId, grandpa: GrandpaId) -> SessionKeys { - SessionKeys { aura, grandpa } +fn session_keys(aura: AuraId, beefy: BeefyId, grandpa: GrandpaId) -> SessionKeys { + SessionKeys { aura, beefy, grandpa } } fn testnet_genesis( - initial_authorities: Vec<(AccountId, AuraId, GrandpaId)>, + initial_authorities: Vec<(AccountId, AuraId, BeefyId, GrandpaId)>, root_key: AccountId, endowed_accounts: Vec, _enable_println: bool, @@ -191,12 +193,15 @@ fn testnet_genesis( balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 50)).collect(), }, aura: AuraConfig { authorities: Vec::new() }, + beefy: BeefyConfig { authorities: Vec::new() }, grandpa: GrandpaConfig { authorities: Vec::new() }, sudo: SudoConfig { key: root_key }, session: SessionConfig { keys: initial_authorities .iter() - .map(|x| (x.0.clone(), x.0.clone(), session_keys(x.1.clone(), x.2.clone()))) + .map(|x| { + (x.0.clone(), x.0.clone(), session_keys(x.1.clone(), x.2.clone(), x.3.clone())) + }) .collect::>(), }, bridge_westend_grandpa: BridgeWestendGrandpaConfig { diff --git a/bridges/bin/millau/node/src/service.rs b/bridges/bin/millau/node/src/service.rs index 4085982494b8..b01c0bfca906 100644 --- a/bridges/bin/millau/node/src/service.rs +++ b/bridges/bin/millau/node/src/service.rs @@ -21,9 +21,10 @@ // ===================================================================================== // UPDATE GUIDE: // 1) replace everything with node-template/src/service.rs contents (found in main Substrate repo); -// 2) the only thing to keep from old code, is `rpc_extensions_builder` - we use our own custom -// RPCs; 3) fix compilation errors; -// 4) test :) +// 2) from old code keep `rpc_extensions_builder` - we use our own custom RPCs; +// 3) from old code keep the Beefy gadget; +// 4) fix compilation errors; +// 5) test :) // ===================================================================================== // ===================================================================================== // ===================================================================================== @@ -209,6 +210,7 @@ pub fn new_full(mut config: Configuration) -> Result } config.network.extra_sets.push(sc_finality_grandpa::grandpa_peers_set_config()); + config.network.extra_sets.push(beefy_gadget::beefy_peers_set_config()); let warp_sync = Arc::new(sc_finality_grandpa::warp_proof::NetworkProvider::new( backend.clone(), grandpa_link.shared_authority_set().clone(), @@ -242,6 +244,8 @@ pub fn new_full(mut config: Configuration) -> Result let enable_grandpa = !config.disable_grandpa; let prometheus_registry = config.prometheus_registry().cloned(); let shared_voter_state = SharedVoterState::empty(); + let (signed_commitment_sender, signed_commitment_stream) = + beefy_gadget::notification::BeefySignedCommitmentStream::channel(); let rpc_extensions_builder = { use sc_finality_grandpa::FinalityProofProvider as GrandpaFinalityProofProvider; @@ -264,7 +268,7 @@ pub fn new_full(mut config: Configuration) -> Result Some(shared_authority_set.clone()), ); - Box::new(move |_, subscription_executor| { + Box::new(move |_, subscription_executor: sc_rpc::SubscriptionTaskExecutor| { let mut io = jsonrpc_core::IoHandler::default(); io.extend_with(SystemApi::to_delegate(FullSystem::new( client.clone(), @@ -278,9 +282,18 @@ pub fn new_full(mut config: Configuration) -> Result shared_authority_set.clone(), shared_voter_state.clone(), justification_stream.clone(), - subscription_executor, + subscription_executor.clone(), finality_proof_provider.clone(), ))); + io.extend_with(beefy_gadget_rpc::BeefyApi::to_delegate( + beefy_gadget_rpc::BeefyRpcHandler::new( + signed_commitment_stream.clone(), + subscription_executor, + ), + )); + io.extend_with(pallet_mmr_rpc::MmrApi::to_delegate(pallet_mmr_rpc::Mmr::new( + client.clone(), + ))); Ok(io) }) }; @@ -292,7 +305,7 @@ pub fn new_full(mut config: Configuration) -> Result task_manager: &mut task_manager, transaction_pool: transaction_pool.clone(), rpc_extensions_builder, - backend, + backend: backend.clone(), system_rpc_tx, config, telemetry: telemetry.as_mut(), @@ -355,6 +368,23 @@ pub fn new_full(mut config: Configuration) -> Result let keystore = if role.is_authority() { Some(keystore_container.sync_keystore()) } else { None }; + let beefy_params = beefy_gadget::BeefyParams { + client, + backend, + key_store: keystore.clone(), + network: network.clone(), + signed_commitment_sender, + min_block_delta: 4, + prometheus_registry: prometheus_registry.clone(), + }; + + // Start the BEEFY bridge gadget. + task_manager.spawn_essential_handle().spawn_blocking( + "beefy-gadget", + None, + beefy_gadget::start_beefy_gadget::<_, _, _, _>(beefy_params), + ); + let grandpa_config = sc_finality_grandpa::Config { // FIXME #1578 make this available through chainspec gossip_duration: Duration::from_millis(333), diff --git a/bridges/bin/millau/runtime/Cargo.toml b/bridges/bin/millau/runtime/Cargo.toml index c8d7f0a15958..13195b95194b 100644 --- a/bridges/bin/millau/runtime/Cargo.toml +++ b/bridges/bin/millau/runtime/Cargo.toml @@ -30,6 +30,7 @@ pallet-shift-session-manager = { path = "../../../modules/shift-session-manager" # Substrate Dependencies +beefy-primitives = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } frame-executive = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -37,7 +38,11 @@ frame-system = { git = "https://github.com/paritytech/substrate", branch = "mast frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-aura = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-beefy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-beefy-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-mmr-primitives = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-randomness-collective-flip = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-sudo = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -64,6 +69,7 @@ substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", bran [features] default = ["std"] std = [ + "beefy-primitives/std", "bp-header-chain/std", "bp-messages/std", "bp-millau/std", @@ -78,11 +84,14 @@ std = [ "frame-system/std", "pallet-aura/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", "pallet-session/std", "pallet-shift-session-manager/std", diff --git a/bridges/bin/millau/runtime/src/lib.rs b/bridges/bin/millau/runtime/src/lib.rs index b713211b1e48..288ff9a47d60 100644 --- a/bridges/bin/millau/runtime/src/lib.rs +++ b/bridges/bin/millau/runtime/src/lib.rs @@ -34,19 +34,23 @@ pub mod rialto_messages; use crate::rialto_messages::{ToRialtoMessagePayload, WithRialtoMessageBridge}; +use beefy_primitives::{crypto::AuthorityId as BeefyId, mmr::MmrLeafVersion, ValidatorSet}; use bridge_runtime_common::messages::{ source::estimate_message_dispatch_and_delivery_fee, MessageBridge, }; use pallet_grandpa::{ fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, }; +use pallet_mmr_primitives::{ + DataOrHash, EncodableOpaqueLeaf, Error as MmrError, LeafDataProvider, Proof as MmrProof, +}; use pallet_transaction_payment::{FeeDetails, Multiplier, RuntimeDispatchInfo}; use sp_api::impl_runtime_apis; use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{Block as BlockT, IdentityLookup, NumberFor, OpaqueKeys}, + traits::{Block as BlockT, IdentityLookup, Keccak256, NumberFor, OpaqueKeys}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, FixedPointNumber, MultiSignature, MultiSigner, Perquintill, }; @@ -120,6 +124,7 @@ pub mod opaque { impl_opaque_keys! { pub struct SessionKeys { pub aura: Aura, + pub beefy: Beefy, pub grandpa: Grandpa, } } @@ -212,6 +217,11 @@ impl pallet_aura::Config for Runtime { type MaxAuthorities = MaxAuthorities; type DisabledValidators = (); } + +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); @@ -240,6 +250,40 @@ impl pallet_grandpa::Config for Runtime { type MaxAuthorities = MaxAuthorities; } +type MmrHash = ::Output; + +impl pallet_mmr::Config for Runtime { + const INDEXING_PREFIX: &'static [u8] = b"mmr"; + type Hashing = Keccak256; + type Hash = MmrHash; + type OnNewRoot = pallet_beefy_mmr::DepositBeefyDigest; + type WeightInfo = (); + type LeafData = pallet_beefy_mmr::Pallet; +} + +parameter_types! { + /// Version of the produced MMR leaf. + /// + /// The version consists of two parts; + /// - `major` (3 bits) + /// - `minor` (5 bits) + /// + /// `major` should be updated only if decoding the previous MMR Leaf format from the payload + /// is not possible (i.e. backward incompatible change). + /// `minor` should be updated if fields are added to the previous MMR Leaf, which given SCALE + /// encoding does not prevent old leafs from being decoded. + /// + /// Hence we expect `major` to be changed really rarely (think never). + /// See [`MmrLeafVersion`] type documentation for more details. + pub LeafVersion: MmrLeafVersion = MmrLeafVersion::new(0, 0); +} + +impl pallet_beefy_mmr::Config for Runtime { + type LeafVersion = LeafVersion; + type BeefyAuthorityToMerkleLeaf = pallet_beefy_mmr::BeefyEcdsaToEthereum; + type ParachainHeads = (); +} + parameter_types! { pub const MinimumPeriod: u64 = bp_millau::SLOT_DURATION / 2; } @@ -459,6 +503,11 @@ construct_runtime!( ShiftSessionManager: pallet_shift_session_manager::{Pallet}, RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Pallet, Storage}, + // BEEFY Bridges support. + Beefy: pallet_beefy::{Pallet, Storage, Config}, + Mmr: pallet_mmr::{Pallet, Storage}, + MmrLeaf: pallet_beefy_mmr::{Pallet, Storage}, + // Rialto bridge modules. BridgeRialtoGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage}, BridgeDispatch: pallet_bridge_dispatch::{Pallet, Event}, @@ -603,6 +652,45 @@ impl_runtime_apis! { } } + impl beefy_primitives::BeefyApi for Runtime { + fn validator_set() -> ValidatorSet { + Beefy::validator_set() + } + } + + impl pallet_mmr_primitives::MmrApi for Runtime { + fn generate_proof(leaf_index: u64) + -> Result<(EncodableOpaqueLeaf, MmrProof), MmrError> + { + Mmr::generate_proof(leaf_index) + .map(|(leaf, proof)| (EncodableOpaqueLeaf::from_leaf(&leaf), proof)) + } + + fn verify_proof(leaf: EncodableOpaqueLeaf, proof: MmrProof) + -> Result<(), MmrError> + { + pub type Leaf = < + ::LeafData as LeafDataProvider + >::LeafData; + + let leaf: Leaf = leaf + .into_opaque_leaf() + .try_decode() + .ok_or(MmrError::Verify)?; + Mmr::verify_leaf(leaf, proof) + } + + fn verify_proof_stateless( + root: MmrHash, + leaf: EncodableOpaqueLeaf, + proof: MmrProof + ) -> Result<(), MmrError> { + type MmrHashing = ::Hashing; + let node = DataOrHash::Data(leaf.into_opaque_leaf()); + pallet_mmr::verify_leaf_proof::(root, node, proof) + } + } + impl fg_primitives::GrandpaApi for Runtime { fn current_set_id() -> fg_primitives::SetId { Grandpa::current_set_id() diff --git a/bridges/bin/rialto/node/Cargo.toml b/bridges/bin/rialto/node/Cargo.toml index fd76fbf9a617..2795f2eecaec 100644 --- a/bridges/bin/rialto/node/Cargo.toml +++ b/bridges/bin/rialto/node/Cargo.toml @@ -28,10 +28,15 @@ rialto-runtime = { path = "../runtime" } # Substrate Dependencies +beefy-gadget = { git = "https://github.com/paritytech/substrate", branch = "master" } +beefy-gadget-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } +beefy-primitives = { git = "https://github.com/paritytech/substrate", branch = "master" } frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master" } frame-benchmarking-cli = { git = "https://github.com/paritytech/substrate", branch = "master" } frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master" } node-inspect = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-mmr-primitives = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-mmr-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-transaction-payment-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-authority-discovery = { git = "https://github.com/paritytech/substrate", branch = "master" } @@ -70,6 +75,10 @@ sp-timestamp = { git = "https://github.com/paritytech/substrate", branch = "mast substrate-frame-rpc-system = { git = "https://github.com/paritytech/substrate", branch = "master" } substrate-prometheus-endpoint = { git = "https://github.com/paritytech/substrate", branch = "master" } +# Polkadot Dependencies + +polkadot-client = { git = "https://github.com/paritytech/polkadot", branch = "master" } + # Polkadot (parachain) Dependencies polkadot-approval-distribution = { git = "https://github.com/paritytech/polkadot", branch = "master" } diff --git a/bridges/bin/rialto/node/src/chain_spec.rs b/bridges/bin/rialto/node/src/chain_spec.rs index cadacad72da9..fb18a35a6af0 100644 --- a/bridges/bin/rialto/node/src/chain_spec.rs +++ b/bridges/bin/rialto/node/src/chain_spec.rs @@ -14,12 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . +use beefy_primitives::crypto::AuthorityId as BeefyId; use bp_rialto::derive_account_from_millau_id; use polkadot_primitives::v1::{AssignmentId, ValidatorId}; use rialto_runtime::{ - AccountId, BabeConfig, BalancesConfig, BridgeMillauMessagesConfig, ConfigurationConfig, - GenesisConfig, GrandpaConfig, SessionConfig, SessionKeys, Signature, SudoConfig, SystemConfig, - WASM_BINARY, + AccountId, BabeConfig, BalancesConfig, BeefyConfig, BridgeMillauMessagesConfig, + ConfigurationConfig, GenesisConfig, GrandpaConfig, SessionConfig, SessionKeys, Signature, + SudoConfig, SystemConfig, WASM_BINARY, }; use serde_json::json; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; @@ -62,10 +63,11 @@ where /// Helper function to generate authority keys. pub fn get_authority_keys_from_seed( s: &str, -) -> (AccountId, BabeId, GrandpaId, ValidatorId, AssignmentId, AuthorityDiscoveryId) { +) -> (AccountId, BabeId, BeefyId, GrandpaId, ValidatorId, AssignmentId, AuthorityDiscoveryId) { ( get_account_id_from_seed::(s), get_from_seed::(s), + get_from_seed::(s), get_from_seed::(s), get_from_seed::(s), get_from_seed::(s), @@ -183,18 +185,20 @@ impl Alternative { fn session_keys( babe: BabeId, + beefy: BeefyId, grandpa: GrandpaId, para_validator: ValidatorId, para_assignment: AssignmentId, authority_discovery: AuthorityDiscoveryId, ) -> SessionKeys { - SessionKeys { babe, grandpa, para_validator, para_assignment, authority_discovery } + SessionKeys { babe, beefy, grandpa, para_validator, para_assignment, authority_discovery } } fn testnet_genesis( initial_authorities: Vec<( AccountId, BabeId, + BeefyId, GrandpaId, ValidatorId, AssignmentId, @@ -215,6 +219,7 @@ fn testnet_genesis( authorities: Vec::new(), epoch_config: Some(rialto_runtime::BABE_GENESIS_EPOCH_CONFIG), }, + beefy: BeefyConfig { authorities: Vec::new() }, grandpa: GrandpaConfig { authorities: Vec::new() }, sudo: SudoConfig { key: root_key }, session: SessionConfig { @@ -230,6 +235,7 @@ fn testnet_genesis( x.3.clone(), x.4.clone(), x.5.clone(), + x.6.clone(), ), ) }) diff --git a/bridges/bin/rialto/node/src/service.rs b/bridges/bin/rialto/node/src/service.rs index fb774d86cca9..3349b09edb9f 100644 --- a/bridges/bin/rialto/node/src/service.rs +++ b/bridges/bin/rialto/node/src/service.rs @@ -17,16 +17,11 @@ //! Rialto chain node service. //! //! The code is mostly copy of `service/src/lib.rs` file from Polkadot repository -//! without optional functions. - -// this warning comes from Error enum (sc_cli::Error in particular) && it isn't easy to use box -// there -#![allow(clippy::large_enum_variant)] -// this warning comes from `sc_service::PartialComponents` type -#![allow(clippy::type_complexity)] +//! without optional functions, and with BEEFY added on top. use crate::overseer::{OverseerGen, OverseerGenArgs}; +use polkadot_client::RuntimeApiCollection; use polkadot_node_core_approval_voting::Config as ApprovalVotingConfig; use polkadot_node_core_av_store::Config as AvailabilityConfig; use polkadot_node_core_candidate_validation::Config as CandidateValidationConfig; @@ -43,7 +38,7 @@ use sc_service::{config::PrometheusConfig, Configuration, TaskManager}; use sc_telemetry::{Telemetry, TelemetryWorker}; use sp_api::{ConstructRuntimeApi, HeaderT}; use sp_consensus::SelectChain; -use sp_runtime::traits::{BlakeTwo256, Block as BlockT}; +use sp_runtime::traits::Block as BlockT; use std::{sync::Arc, time::Duration}; use substrate_prometheus_endpoint::Registry; @@ -115,52 +110,6 @@ type FullBabeBlockImport = type FullBabeLink = sc_consensus_babe::BabeLink; type FullGrandpaLink = sc_finality_grandpa::LinkHalf; -/// A set of APIs that polkadot-like runtimes must implement. -/// -/// This is the copy of `polkadot_service::RuntimeApiCollection` with some APIs removed -/// (right now - MMR and BEEFY). -pub trait RequiredApiCollection: - sp_transaction_pool::runtime_api::TaggedTransactionQueue - + sp_api::ApiExt - + sp_consensus_babe::BabeApi - + sp_finality_grandpa::GrandpaApi - + polkadot_primitives::v1::ParachainHost - + sp_block_builder::BlockBuilder - + frame_system_rpc_runtime_api::AccountNonceApi< - Block, - bp_rialto::AccountId, - rialto_runtime::Index, - > + pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi - + sp_api::Metadata - + sp_offchain::OffchainWorkerApi - + sp_session::SessionKeys - + sp_authority_discovery::AuthorityDiscoveryApi -where - >::StateBackend: sp_api::StateBackend, -{ -} - -impl RequiredApiCollection for Api -where - Api: sp_transaction_pool::runtime_api::TaggedTransactionQueue - + sp_api::ApiExt - + sp_consensus_babe::BabeApi - + sp_finality_grandpa::GrandpaApi - + polkadot_primitives::v1::ParachainHost - + sp_block_builder::BlockBuilder - + frame_system_rpc_runtime_api::AccountNonceApi< - Block, - bp_rialto::AccountId, - rialto_runtime::Index, - > + pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi - + sp_api::Metadata - + sp_offchain::OffchainWorkerApi - + sp_session::SessionKeys - + sp_authority_discovery::AuthorityDiscoveryApi, - >::StateBackend: sp_api::StateBackend, -{ -} - // If we're using prometheus, use a registry with a prefix of `polkadot`. fn set_prometheus_registry(config: &mut Configuration) -> Result<(), Error> { if let Some(PrometheusConfig { registry, .. }) = config.prometheus_config.as_mut() { @@ -170,6 +119,8 @@ fn set_prometheus_registry(config: &mut Configuration) -> Result<(), Error> { Ok(()) } +// Needed here for complex return type while `impl Trait` in type aliases is unstable. +#[allow(clippy::type_complexity)] pub fn new_partial( config: &mut Configuration, ) -> Result< @@ -184,7 +135,12 @@ pub fn new_partial( sc_rpc::DenyUnsafe, sc_rpc::SubscriptionTaskExecutor, ) -> Result, sc_service::Error>, - (FullBabeBlockImport, FullGrandpaLink, FullBabeLink), + ( + FullBabeBlockImport, + FullGrandpaLink, + FullBabeLink, + beefy_gadget::notification::BeefySignedCommitmentSender, + ), sc_finality_grandpa::SharedVoterState, std::time::Duration, Option, @@ -195,7 +151,7 @@ pub fn new_partial( where RuntimeApi: ConstructRuntimeApi + Send + Sync + 'static, >::RuntimeApi: - RequiredApiCollection>, + RuntimeApiCollection>, ExecutorDispatch: NativeExecutionDispatch + 'static, { set_prometheus_registry(config)?; @@ -282,7 +238,10 @@ where let shared_authority_set = grandpa_link.shared_authority_set().clone(); let shared_voter_state = sc_finality_grandpa::SharedVoterState::empty(); - let import_setup = (block_import, grandpa_link, babe_link); + let (signed_commitment_sender, signed_commitment_stream) = + beefy_gadget::notification::BeefySignedCommitmentStream::channel(); + + let import_setup = (block_import, grandpa_link, babe_link, signed_commitment_sender); let rpc_setup = shared_voter_state.clone(); let slot_duration = babe_config.slot_duration(); @@ -316,14 +275,23 @@ where pool, deny_unsafe, ))); - io.extend_with(TransactionPaymentApi::to_delegate(TransactionPayment::new(client))); + io.extend_with(TransactionPaymentApi::to_delegate(TransactionPayment::new( + client.clone(), + ))); io.extend_with(GrandpaApi::to_delegate(GrandpaRpcHandler::new( shared_authority_set.clone(), shared_voter_state, justification_stream.clone(), - subscription_executor, + subscription_executor.clone(), finality_proof_provider, ))); + io.extend_with(beefy_gadget_rpc::BeefyApi::to_delegate( + beefy_gadget_rpc::BeefyRpcHandler::new( + signed_commitment_stream.clone(), + subscription_executor, + ), + )); + io.extend_with(pallet_mmr_rpc::MmrApi::to_delegate(pallet_mmr_rpc::Mmr::new(client))); Ok(io) } @@ -361,7 +329,7 @@ async fn active_leaves( where RuntimeApi: ConstructRuntimeApi + Send + Sync + 'static, >::RuntimeApi: - RequiredApiCollection>, + RuntimeApiCollection>, ExecutorDispatch: NativeExecutionDispatch + 'static, { let best_block = select_chain.best_chain().await?; @@ -406,7 +374,7 @@ pub fn new_full( where RuntimeApi: ConstructRuntimeApi + Send + Sync + 'static, >::RuntimeApi: - RequiredApiCollection>, + RuntimeApiCollection>, ExecutorDispatch: NativeExecutionDispatch + 'static, { let is_collator = false; @@ -442,6 +410,8 @@ where // Substrate nodes. config.network.extra_sets.push(sc_finality_grandpa::grandpa_peers_set_config()); + config.network.extra_sets.push(beefy_gadget::beefy_peers_set_config()); + { use polkadot_network_bridge::{peer_sets_info, IsAuthority}; let is_authority = if role.is_authority() { IsAuthority::Yes } else { IsAuthority::No }; @@ -536,7 +506,7 @@ where telemetry: telemetry.as_mut(), })?; - let (block_import, link_half, babe_link) = import_setup; + let (block_import, link_half, babe_link, signed_commitment_sender) = import_setup; let overseer_client = client.clone(); let spawner = task_manager.spawn_handle(); @@ -713,6 +683,23 @@ where let keystore_opt = if role.is_authority() { Some(keystore_container.sync_keystore()) } else { None }; + let beefy_params = beefy_gadget::BeefyParams { + client: client.clone(), + backend: backend.clone(), + key_store: keystore_opt.clone(), + network: network.clone(), + signed_commitment_sender, + min_block_delta: 2, + prometheus_registry: prometheus_registry.clone(), + }; + + // Start the BEEFY bridge gadget. + task_manager.spawn_essential_handle().spawn_blocking( + "beefy-gadget", + None, + beefy_gadget::start_beefy_gadget::<_, _, _, _>(beefy_params), + ); + let config = sc_finality_grandpa::Config { // FIXME substrate#1578 make this available through chainspec gossip_duration: Duration::from_millis(1000), diff --git a/bridges/bin/rialto/runtime/Cargo.toml b/bridges/bin/rialto/runtime/Cargo.toml index 36dc436ddca6..3c4ec1ebce1c 100644 --- a/bridges/bin/rialto/runtime/Cargo.toml +++ b/bridges/bin/rialto/runtime/Cargo.toml @@ -31,6 +31,7 @@ pallet-shift-session-manager = { path = "../../../modules/shift-session-manager" # Substrate Dependencies +beefy-primitives = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } frame-executive = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -39,7 +40,11 @@ frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/substrate" pallet-authority-discovery = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-babe = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-beefy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-beefy-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-mmr-primitives = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-sudo = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -76,6 +81,7 @@ substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", bran [features] default = ["std"] std = [ + "beefy-primitives/std", "bp-header-chain/std", "bp-message-dispatch/std", "bp-messages/std", @@ -93,10 +99,14 @@ std = [ "pallet-authority-discovery/std", "pallet-babe/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", + "pallet-mmr/std", + "pallet-mmr-primitives/std", "pallet-shift-session-manager/std", "pallet-sudo/std", "pallet-timestamp/std", diff --git a/bridges/bin/rialto/runtime/src/lib.rs b/bridges/bin/rialto/runtime/src/lib.rs index 5e7e47490d58..0987184c73aa 100644 --- a/bridges/bin/rialto/runtime/src/lib.rs +++ b/bridges/bin/rialto/runtime/src/lib.rs @@ -35,19 +35,23 @@ pub mod parachains; use crate::millau_messages::{ToMillauMessagePayload, WithMillauMessageBridge}; +use beefy_primitives::{crypto::AuthorityId as BeefyId, mmr::MmrLeafVersion, ValidatorSet}; use bridge_runtime_common::messages::{ source::estimate_message_dispatch_and_delivery_fee, MessageBridge, }; use pallet_grandpa::{ fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, }; +use pallet_mmr_primitives::{ + DataOrHash, EncodableOpaqueLeaf, Error as MmrError, LeafDataProvider, Proof as MmrProof, +}; use pallet_transaction_payment::{FeeDetails, Multiplier, RuntimeDispatchInfo}; use sp_api::impl_runtime_apis; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{AccountIdLookup, Block as BlockT, NumberFor, OpaqueKeys}, + traits::{AccountIdLookup, Block as BlockT, Keccak256, NumberFor, OpaqueKeys}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, FixedPointNumber, MultiSignature, MultiSigner, Perquintill, }; @@ -122,6 +126,7 @@ impl_opaque_keys! { pub struct SessionKeys { pub babe: Babe, pub grandpa: Grandpa, + pub beefy: Beefy, pub para_validator: Initializer, pub para_assignment: SessionInfo, pub authority_discovery: AuthorityDiscovery, @@ -242,6 +247,10 @@ impl pallet_babe::Config for Runtime { type WeightInfo = (); } +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); @@ -270,6 +279,38 @@ impl pallet_grandpa::Config for Runtime { type WeightInfo = (); } +impl pallet_mmr::Config for Runtime { + const INDEXING_PREFIX: &'static [u8] = b"mmr"; + type Hashing = Keccak256; + type Hash = ::Output; + type OnNewRoot = pallet_beefy_mmr::DepositBeefyDigest; + type WeightInfo = (); + type LeafData = pallet_beefy_mmr::Pallet; +} + +parameter_types! { + /// Version of the produced MMR leaf. + /// + /// The version consists of two parts; + /// - `major` (3 bits) + /// - `minor` (5 bits) + /// + /// `major` should be updated only if decoding the previous MMR Leaf format from the payload + /// is not possible (i.e. backward incompatible change). + /// `minor` should be updated if fields are added to the previous MMR Leaf, which given SCALE + /// encoding does not prevent old leafs from being decoded. + /// + /// Hence we expect `major` to be changed really rarely (think never). + /// See [`MmrLeafVersion`] type documentation for more details. + pub LeafVersion: MmrLeafVersion = MmrLeafVersion::new(0, 0); +} + +impl pallet_beefy_mmr::Config for Runtime { + type LeafVersion = LeafVersion; + type BeefyAuthorityToMerkleLeaf = pallet_beefy_mmr::BeefyEcdsaToEthereum; + type ParachainHeads = (); +} + parameter_types! { pub const MinimumPeriod: u64 = bp_rialto::SLOT_DURATION / 2; } @@ -463,6 +504,11 @@ construct_runtime!( Grandpa: pallet_grandpa::{Pallet, Call, Storage, Config, Event}, ShiftSessionManager: pallet_shift_session_manager::{Pallet}, + // BEEFY Bridges support. + Beefy: pallet_beefy::{Pallet, Storage, Config}, + Mmr: pallet_mmr::{Pallet, Storage}, + MmrLeaf: pallet_beefy_mmr::{Pallet, Storage}, + // Millau bridge modules. BridgeMillauGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage}, BridgeDispatch: pallet_bridge_dispatch::{Pallet, Event}, @@ -572,6 +618,45 @@ impl_runtime_apis! { } } + impl beefy_primitives::BeefyApi for Runtime { + fn validator_set() -> ValidatorSet { + Beefy::validator_set() + } + } + + impl pallet_mmr_primitives::MmrApi for Runtime { + fn generate_proof(leaf_index: u64) + -> Result<(EncodableOpaqueLeaf, MmrProof), MmrError> + { + Mmr::generate_proof(leaf_index) + .map(|(leaf, proof)| (EncodableOpaqueLeaf::from_leaf(&leaf), proof)) + } + + fn verify_proof(leaf: EncodableOpaqueLeaf, proof: MmrProof) + -> Result<(), MmrError> + { + pub type Leaf = < + ::LeafData as LeafDataProvider + >::LeafData; + + let leaf: Leaf = leaf + .into_opaque_leaf() + .try_decode() + .ok_or(MmrError::Verify)?; + Mmr::verify_leaf(leaf, proof) + } + + fn verify_proof_stateless( + root: Hash, + leaf: EncodableOpaqueLeaf, + proof: MmrProof + ) -> Result<(), MmrError> { + type MmrHashing = ::Hashing; + let node = DataOrHash::Data(leaf.into_opaque_leaf()); + pallet_mmr::verify_leaf_proof::(root, node, proof) + } + } + impl bp_millau::MillauFinalityApi for Runtime { fn best_finalized() -> (bp_millau::BlockNumber, bp_millau::Hash) { let header = BridgeMillauGrandpa::best_finalized(); @@ -1147,7 +1232,10 @@ mod tests { #[test] fn call_size() { - const MAX_CALL_SIZE: usize = 230; // value from polkadot-runtime tests - assert!(core::mem::size_of::() <= MAX_CALL_SIZE); + const DOT_MAX_CALL_SZ: usize = 230; + assert!(core::mem::size_of::>() <= DOT_MAX_CALL_SZ); + // FIXME: get this down to 230. https://github.com/paritytech/grandpa-bridge-gadget/issues/359 + const BEEFY_MAX_CALL_SZ: usize = 232; + assert!(core::mem::size_of::>() <= BEEFY_MAX_CALL_SZ); } } diff --git a/bridges/relays/bin-substrate/src/cli/encode_call.rs b/bridges/relays/bin-substrate/src/cli/encode_call.rs index ca0e6dd8abff..e17854662e5c 100644 --- a/bridges/relays/bin-substrate/src/cli/encode_call.rs +++ b/bridges/relays/bin-substrate/src/cli/encode_call.rs @@ -345,7 +345,7 @@ mod tests { // then assert!(format!("{:?}", call_hex).starts_with( - "0x0c030000000001000000381409000000000001d43593c715fdd31c61141abd04a99fd6822c8558854cc\ + "0x0f030000000001000000381409000000000001d43593c715fdd31c61141abd04a99fd6822c8558854cc\ de39a5684e7a56da27d01d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01" )) }