From ad6e755b842aee67543c9af66db1801b27cc7d87 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 6 Jul 2021 13:03:12 +0300 Subject: [PATCH] Enable over-bridge-messaging in Rococo/Wococo runtime (#3377) * bridges in W<>R * fix node compilation * Update runtime/rococo/src/bridge_messages.rs Co-authored-by: Hernando Castano * Update runtime/rococo/src/bridge_messages.rs Co-authored-by: Hernando Castano * Update runtime/rococo/src/bridge_messages.rs Co-authored-by: Hernando Castano * Update runtime/rococo/src/bridge_messages.rs Co-authored-by: Hernando Castano * long line fix * comment/remove -> comment/#[ignore] * explicit instances Co-authored-by: Hernando Castano --- Cargo.lock | 93 ++++++ node/service/src/chain_spec.rs | 16 + runtime/rococo/Cargo.toml | 10 + runtime/rococo/src/bridge_messages.rs | 410 ++++++++++++++++++++++++++ runtime/rococo/src/lib.rs | 225 ++++++++++++++ 5 files changed, 754 insertions(+) create mode 100644 runtime/rococo/src/bridge_messages.rs diff --git a/Cargo.lock b/Cargo.lock index 1914b9877866..3496afc6e7e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -734,6 +734,16 @@ dependencies = [ "sp-std", ] +[[package]] +name = "bp-message-dispatch" +version = "0.1.0" +dependencies = [ + "bp-runtime", + "frame-support", + "parity-scale-codec", + "sp-std", +] + [[package]] name = "bp-messages" version = "0.1.0" @@ -765,6 +775,20 @@ dependencies = [ "sp-version", ] +[[package]] +name = "bp-rialto" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-runtime", + "frame-support", + "frame-system", + "sp-api", + "sp-core", + "sp-runtime", + "sp-std", +] + [[package]] name = "bp-rococo" version = "0.1.0" @@ -825,6 +849,28 @@ dependencies = [ "sp-std", ] +[[package]] +name = "bridge-runtime-common" +version = "0.1.0" +dependencies = [ + "bp-message-dispatch", + "bp-messages", + "bp-runtime", + "ed25519-dalek", + "frame-support", + "hash-db", + "pallet-bridge-dispatch", + "pallet-bridge-grandpa", + "pallet-bridge-messages", + "pallet-transaction-payment", + "parity-scale-codec", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", +] + [[package]] name = "bs58" version = "0.4.0" @@ -4661,6 +4707,23 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-bridge-dispatch" +version = "0.1.0" +dependencies = [ + "bp-message-dispatch", + "bp-runtime", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-bridge-grandpa" version = "0.1.0" @@ -4683,6 +4746,31 @@ dependencies = [ "sp-trie", ] +[[package]] +name = "pallet-bridge-messages" +version = "0.1.0" +dependencies = [ + "bitvec", + "bp-message-dispatch", + "bp-messages", + "bp-rialto", + "bp-runtime", + "frame-benchmarking", + "frame-support", + "frame-system", + "hex", + "hex-literal", + "log", + "num-traits", + "pallet-balances", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-collective" version = "3.0.0" @@ -7628,8 +7716,11 @@ name = "rococo-runtime" version = "0.9.8" dependencies = [ "beefy-primitives", + "bp-messages", "bp-rococo", + "bp-runtime", "bp-wococo", + "bridge-runtime-common", "frame-executive", "frame-support", "frame-system", @@ -7641,7 +7732,9 @@ dependencies = [ "pallet-babe", "pallet-balances", "pallet-beefy", + "pallet-bridge-dispatch", "pallet-bridge-grandpa", + "pallet-bridge-messages", "pallet-collective", "pallet-grandpa", "pallet-im-online", diff --git a/node/service/src/chain_spec.rs b/node/service/src/chain_spec.rs index 5bb0af0ad9ad..57e07b2bf0f7 100644 --- a/node/service/src/chain_spec.rs +++ b/node/service/src/chain_spec.rs @@ -988,6 +988,14 @@ fn rococo_staging_testnet_config_genesis(wasm_binary: &[u8]) -> rococo_runtime:: owner: Some(endowed_accounts[0].clone()), ..Default::default() }, + bridge_rococo_messages: rococo_runtime::BridgeRococoMessagesConfig { + owner: Some(endowed_accounts[0].clone()), + ..Default::default() + }, + bridge_wococo_messages: rococo_runtime::BridgeWococoMessagesConfig { + owner: Some(endowed_accounts[0].clone()), + ..Default::default() + }, } } @@ -1524,6 +1532,14 @@ pub fn rococo_testnet_genesis( owner: Some(root_key.clone()), ..Default::default() }, + bridge_rococo_messages: rococo_runtime::BridgeRococoMessagesConfig { + owner: Some(root_key.clone()), + ..Default::default() + }, + bridge_wococo_messages: rococo_runtime::BridgeWococoMessagesConfig { + owner: Some(root_key.clone()), + ..Default::default() + }, } } diff --git a/runtime/rococo/Cargo.toml b/runtime/rococo/Cargo.toml index f9cd04c7decf..d59aac3a0834 100644 --- a/runtime/rococo/Cargo.toml +++ b/runtime/rococo/Cargo.toml @@ -69,9 +69,14 @@ xcm-builder = { package = "xcm-builder", path = "../../xcm/xcm-builder", default pallet-xcm = { path = "../../xcm/pallet-xcm", default-features = false } # Bridge Dependencies +bp-messages = { path = "../../bridges/primitives/messages", default-features = false } bp-rococo = { path = "../../bridges/primitives/chain-rococo", default-features = false } +bp-runtime = { path = "../../bridges/primitives/runtime", default-features = false } bp-wococo = { path = "../../bridges/primitives/chain-wococo", default-features = false } +bridge-runtime-common = { path = "../../bridges/bin/runtime-common", default-features = false } +pallet-bridge-dispatch = { path = "../../bridges/modules/dispatch", default-features = false } pallet-bridge-grandpa = { path = "../../bridges/modules/grandpa", default-features = false } +pallet-bridge-messages = { path = "../../bridges/modules/messages", default-features = false } [build-dependencies] substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "master" } @@ -82,8 +87,11 @@ no_std = [] std = [ "authority-discovery-primitives/std", "babe-primitives/std", + "bp-messages/std", "bp-rococo/std", + "bp-runtime/std", "bp-wococo/std", + "bridge-runtime-common/std", "parity-scale-codec/std", "frame-executive/std", "pallet-authority-discovery/std", @@ -91,7 +99,9 @@ std = [ "pallet-babe/std", "beefy-primitives/std", "pallet-balances/std", + "pallet-bridge-dispatch/std", "pallet-bridge-grandpa/std", + "pallet-bridge-messages/std", "pallet-collective/std", "pallet-beefy/std", "pallet-grandpa/std", diff --git a/runtime/rococo/src/bridge_messages.rs b/runtime/rococo/src/bridge_messages.rs new file mode 100644 index 000000000000..93e7314c737d --- /dev/null +++ b/runtime/rococo/src/bridge_messages.rs @@ -0,0 +1,410 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Over-bridge messaging support for Rococo <> Wococo bridge. + +pub use self::{at_rococo::*, at_wococo::*}; + +use bp_messages::{ + source_chain::TargetHeaderChain, + target_chain::{ProvedMessages, SourceHeaderChain}, + InboundLaneData, LaneId, Message, MessageNonce, +}; +use bp_rococo::{EXTRA_STORAGE_PROOF_SIZE, MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, max_extrinsic_size, max_extrinsic_weight}; +use bp_runtime::{ROCOCO_CHAIN_ID, WOCOCO_CHAIN_ID, ChainId}; +use bridge_runtime_common::messages::{ + BridgedChainWithMessages, ChainWithMessages, MessageBridge, MessageTransaction, ThisChainWithMessages, + source as messages_source, target as messages_target, +}; +use frame_support::{traits::Get, weights::{Weight, WeightToFeePolynomial}, RuntimeDebug}; +use sp_std::{convert::TryFrom, marker::PhantomData, ops::RangeInclusive}; + +/// Maximal number of pending outbound messages. +const MAXIMAL_PENDING_MESSAGES_AT_OUTBOUND_LANE: MessageNonce = bp_rococo::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE; +/// Maximal weight of single message delivery confirmation transaction on Rococo/Wococo chain. +/// +/// This value is a result of `pallet_bridge_messages::Pallet::receive_messages_delivery_proof` weight formula +/// computation for the case when single message is confirmed. The result then must be rounded up to account +/// possible future runtime upgrades. +const MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT: Weight = 2_000_000_000; +/// Increase of delivery transaction weight on Rococo/Wococo chain with every additional message byte. +/// +/// This value is a result of `pallet_bridge_messages::WeightInfoExt::storage_proof_size_overhead(1)` call. The +/// result then must be rounded up to account possible future runtime upgrades. +const ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT: Weight = 25_000; +/// Weight of single regular message delivery transaction on Rococo/Wococo chain. +/// +/// This value is a result of `pallet_bridge_messages::Pallet::receive_messages_proof_weight()` call +/// for the case when single message of `pallet_bridge_messages::EXPECTED_DEFAULT_MESSAGE_LENGTH` bytes is delivered. +/// The message must have dispatch weight set to zero. The result then must be rounded up to account +/// possible future runtime upgrades. +const DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT: Weight = 1_500_000_000; +/// Weight of pay-dispatch-fee operation for inbound messages at Rococo/Wococo chain. +/// +/// This value corresponds to the result of `pallet_bridge_messages::WeightInfoExt::pay_inbound_dispatch_fee_overhead()` +/// call for your chain. Don't put too much reserve there, because it is used to **decrease** +/// `DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT` cost. So putting large reserve would make delivery transactions cheaper. +const PAY_INBOUND_DISPATCH_FEE_WEIGHT: Weight = 600_000_000; +/// Number of bytes, included in the signed Rococo/Wococo transaction apart from the encoded call itself. +/// +/// Can be computed by subtracting encoded call size from raw transaction size. +const TX_EXTRA_BYTES: u32 = 130; + +/// Rococo chain as it is seen at Rococo. +pub type RococoAtRococo = RococoLikeChain; + +/// Rococo chain as it is seen at Wococo. +pub type RococoAtWococo = RococoLikeChain; + +/// Wococo chain as it is seen at Wococo. +pub type WococoAtWococo = RococoLikeChain; + +/// Wococo chain as it is seen at Rococo. +pub type WococoAtRococo = RococoLikeChain; + +/// Rococo/Wococo chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct RococoLikeChain { + _bridge_definition: PhantomData, + _at_this_chain_grandpa_pallet_instance: PhantomData, +} + +impl ChainWithMessages for RococoLikeChain { + type Hash = crate::Hash; + type AccountId = crate::AccountId; + type Signer = primitives::v1::AccountPublic; + type Signature = crate::Signature; + type Weight = Weight; + type Balance = crate::Balance; +} + +impl ThisChainWithMessages for RococoLikeChain { + type Call = crate::Call; + + fn is_outbound_lane_enabled(lane: &LaneId) -> bool { + *lane == [0, 0, 0, 0] + } + + fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { + MAXIMAL_PENDING_MESSAGES_AT_OUTBOUND_LANE + } + + fn estimate_delivery_confirmation_transaction() -> MessageTransaction { + let inbound_data_size = InboundLaneData::::encoded_size_hint( + MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, + 1, + 1, + ) + .unwrap_or(u32::MAX); + + MessageTransaction { + dispatch_weight: MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT, + size: inbound_data_size + .saturating_add(EXTRA_STORAGE_PROOF_SIZE) + .saturating_add(TX_EXTRA_BYTES), + } + } + + fn transaction_payment(transaction: MessageTransaction) -> crate::Balance { + // current fee multiplier is used here + bridge_runtime_common::messages::transaction_payment( + crate::BlockWeights::get().get(frame_support::weights::DispatchClass::Normal).base_extrinsic, + crate::TransactionByteFee::get(), + pallet_transaction_payment::Pallet::::next_fee_multiplier(), + |weight| crate::constants::fee::WeightToFee::calc(&weight), + transaction, + ) + } +} + +impl BridgedChainWithMessages for RococoLikeChain { + fn maximal_extrinsic_size() -> u32 { + 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(max_extrinsic_weight()); + + // we're charging for payload bytes in `With(Wococo | Rococo)MessageBridge::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 estimate_delivery_transaction( + message_payload: &[u8], + include_pay_dispatch_fee_cost: bool, + message_dispatch_weight: Weight, + ) -> MessageTransaction { + let message_payload_len = u32::try_from(message_payload.len()).unwrap_or(u32::MAX); + let extra_bytes_in_payload = Weight::from(message_payload_len) + .saturating_sub(pallet_bridge_messages::EXPECTED_DEFAULT_MESSAGE_LENGTH.into()); + + MessageTransaction { + dispatch_weight: extra_bytes_in_payload + .saturating_mul(ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT) + .saturating_add(DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT) + .saturating_sub(if include_pay_dispatch_fee_cost { + 0 + } else { + PAY_INBOUND_DISPATCH_FEE_WEIGHT + }) + .saturating_add(message_dispatch_weight), + size: message_payload_len + .saturating_add(EXTRA_STORAGE_PROOF_SIZE) + .saturating_add(TX_EXTRA_BYTES), + } + } + + fn transaction_payment(transaction: MessageTransaction) -> crate::Balance { + // current fee multiplier is used here + bridge_runtime_common::messages::transaction_payment( + crate::BlockWeights::get().get(frame_support::weights::DispatchClass::Normal).base_extrinsic, + crate::TransactionByteFee::get(), + pallet_transaction_payment::Pallet::::next_fee_multiplier(), + |weight| crate::constants::fee::WeightToFee::calc(&weight), + transaction, + ) + } +} + +impl TargetHeaderChain, crate::AccountId> + for RococoLikeChain +where + B: MessageBridge, + B::ThisChain: ChainWithMessages, + B::BridgedChain: ChainWithMessages, + GI: 'static, + crate::Runtime: pallet_bridge_grandpa::Config + pallet_bridge_messages::Config, + <>::BridgedChain as bp_runtime::Chain>::Hash: From, +{ + type Error = &'static str; + type MessagesDeliveryProof = messages_source::FromBridgedChainMessagesDeliveryProof; + + fn verify_message(payload: &messages_source::FromThisChainMessagePayload) -> Result<(), Self::Error> { + messages_source::verify_chain_message::(payload) + } + + fn verify_messages_delivery_proof( + proof: Self::MessagesDeliveryProof, + ) -> Result<(LaneId, InboundLaneData), Self::Error> { + messages_source::verify_messages_delivery_proof::(proof) + } +} + +impl SourceHeaderChain for RococoLikeChain +where + B: MessageBridge, + B::BridgedChain: ChainWithMessages, + GI: 'static, + crate::Runtime: pallet_bridge_grandpa::Config + pallet_bridge_messages::Config, + <>::BridgedChain as bp_runtime::Chain>::Hash: From, +{ + type Error = &'static str; + type MessagesProof = messages_target::FromBridgedChainMessagesProof; + + fn verify_messages_proof( + proof: Self::MessagesProof, + messages_count: u32, + ) -> Result>, Self::Error> { + messages_target::verify_messages_proof::(proof, messages_count) + } +} + +/// The cost of delivery confirmation transaction. +pub struct GetDeliveryConfirmationTransactionFee; + +impl Get for GetDeliveryConfirmationTransactionFee { + fn get() -> crate::Balance { + ::transaction_payment( + RococoAtRococo::estimate_delivery_confirmation_transaction() + ) + } +} + +/// This module contains definitions that are used by the messages pallet instance, 'deployed' at Rococo. +mod at_rococo { + use super::*; + + /// Message bridge that is 'deployed' at Rococo chain and connecting it to Wococo chain. + #[derive(RuntimeDebug, Clone, Copy)] + pub struct AtRococoWithWococoMessageBridge; + + impl MessageBridge for AtRococoWithWococoMessageBridge { + const THIS_CHAIN_ID: ChainId = ROCOCO_CHAIN_ID; + const BRIDGED_CHAIN_ID: ChainId = WOCOCO_CHAIN_ID; + const RELAYER_FEE_PERCENT: u32 = 10; + + type ThisChain = RococoAtRococo; + type BridgedChain = WococoAtRococo; + type BridgedMessagesInstance = crate::AtWococoWithRococoMessagesInstance; + + fn bridged_balance_to_this_balance(bridged_balance: bp_wococo::Balance) -> bp_rococo::Balance { + bridged_balance + } + } + + /// Message payload for Rococo -> Wococo messages as it is seen at the Rococo. + pub type ToWococoMessagePayload = messages_source::FromThisChainMessagePayload; + + /// Message verifier for Rococo -> Wococo messages at Rococo. + pub type ToWococoMessageVerifier = messages_source::FromThisChainMessageVerifier; + + /// Message payload for Wococo -> Rococo messages as it is seen at Rococo. + pub type FromWococoMessagePayload = messages_target::FromBridgedChainMessagePayload< + AtRococoWithWococoMessageBridge + >; + + /// Encoded Rococo Call as it comes from Wococo. + pub type FromWococoEncodedCall = messages_target::FromBridgedChainEncodedMessageCall; + + /// Call-dispatch based message dispatch for Wococo -> Rococo messages. + pub type FromWococoMessageDispatch = messages_target::FromBridgedChainMessageDispatch< + AtRococoWithWococoMessageBridge, + crate::Runtime, + pallet_balances::Pallet, + crate::AtRococoFromWococoMessagesDispatch, + >; +} + +/// This module contains definitions that are used by the messages pallet instance, 'deployed' at Wococo. +mod at_wococo { + use super::*; + + /// Message bridge that is 'deployed' at Wococo chain and connecting it to Rococo chain. + #[derive(RuntimeDebug, Clone, Copy)] + pub struct AtWococoWithRococoMessageBridge; + + impl MessageBridge for AtWococoWithRococoMessageBridge { + const THIS_CHAIN_ID: ChainId = WOCOCO_CHAIN_ID; + const BRIDGED_CHAIN_ID: ChainId = ROCOCO_CHAIN_ID; + const RELAYER_FEE_PERCENT: u32 = 10; + + type ThisChain = WococoAtWococo; + type BridgedChain = RococoAtWococo; + type BridgedMessagesInstance = crate::AtRococoWithWococoMessagesInstance; + + fn bridged_balance_to_this_balance(bridged_balance: bp_rococo::Balance) -> bp_wococo::Balance { + bridged_balance + } + } + + /// Message payload for Wococo -> Rococo messages as it is seen at the Wococo. + pub type ToRococoMessagePayload = messages_source::FromThisChainMessagePayload; + + /// Message verifier for Wococo -> Rococo messages at Wococo. + pub type ToRococoMessageVerifier = messages_source::FromThisChainMessageVerifier; + + /// Message payload for Rococo -> Wococo messages as it is seen at Wococo. + pub type FromRococoMessagePayload = messages_target::FromBridgedChainMessagePayload< + AtWococoWithRococoMessageBridge, + >; + + /// Encoded Wococo Call as it comes from Rococo. + pub type FromRococoEncodedCall = messages_target::FromBridgedChainEncodedMessageCall; + + /// Call-dispatch based message dispatch for Rococo -> Wococo messages. + pub type FromRococoMessageDispatch = messages_target::FromBridgedChainMessageDispatch< + AtWococoWithRococoMessageBridge, + crate::Runtime, + pallet_balances::Pallet, + crate::AtWococoFromRococoMessagesDispatch, + >; +} + +#[cfg(test)] +mod tests { + use bridge_runtime_common::messages; + use parity_scale_codec::Encode; + use super::*; + + #[test] + fn ensure_rococo_messages_weights_are_correct() { + // **NOTE**: the main purpose of this test is to be sure that any message that is sumbitted + // to (any) inbound lane in Rococo<>Wococo bridge can be delivered to the bridged chain. + // Since we deal with testnets here, in case of failure + urgency: + // + // 1) ping bridges team about this failure (see the CODEOWNERS file if you're unsure who to ping); + // 2) comment/#[ignore] the test. + + // we don't have any knowledge of messages-at-Rococo weights, so we'll be using + // weights of one of our testnets, which should be accurate enough + type Weights = pallet_bridge_messages::weights::RialtoWeight; + + pallet_bridge_messages::ensure_weights_are_correct::( + DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT, + ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT, + MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT, + PAY_INBOUND_DISPATCH_FEE_WEIGHT, + ); + + let max_incoming_message_proof_size = bp_rococo::EXTRA_STORAGE_PROOF_SIZE.saturating_add( + messages::target::maximal_incoming_message_size(bp_rococo::max_extrinsic_size()), + ); + pallet_bridge_messages::ensure_able_to_receive_message::( + bp_rococo::max_extrinsic_size(), + bp_rococo::max_extrinsic_weight(), + max_incoming_message_proof_size, + messages::target::maximal_incoming_message_dispatch_weight(bp_rococo::max_extrinsic_weight()), + ); + + let max_incoming_inbound_lane_data_proof_size = bp_messages::InboundLaneData::<()>::encoded_size_hint( + bp_rococo::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, + bp_rococo::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE as _, + bp_rococo::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE as _, + ) + .unwrap_or(u32::MAX); + pallet_bridge_messages::ensure_able_to_receive_confirmation::( + bp_rococo::max_extrinsic_size(), + bp_rococo::max_extrinsic_weight(), + max_incoming_inbound_lane_data_proof_size, + bp_rococo::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, + bp_rococo::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE, + ); + } + + #[test] + fn ensure_rococo_tx_extra_bytes_constant_is_correct() { + // **NOTE**: this test checks that we're computing transaction fee (for bridged chain, which, in + // case of Rococo<>Wococo, means any chain) on-chain properly. If this assert fails: + // + // 1) just fix the `TX_EXTRA_BYTES` constant to actual (or sightly rounded up) value; + // 2) (only if it has changed significantly (> x2 times)) ping the bridges team (see the CODEOWNERS + // file if you're unsure who to ping) + + let signed_extra: crate::SignedExtra = ( + frame_system::CheckSpecVersion::new(), + frame_system::CheckTxVersion::new(), + frame_system::CheckGenesis::new(), + frame_system::CheckMortality::from(sp_runtime::generic::Era::mortal(u64::MAX, u64::MAX)), + frame_system::CheckNonce::from(primitives::v1::Nonce::MAX), + frame_system::CheckWeight::new(), + pallet_transaction_payment::ChargeTransactionPayment::from(primitives::v1::Balance::MAX), + ); + let extra_bytes_in_transaction = crate::Address::default().encoded_size() + + crate::Signature::default().encoded_size() + + signed_extra.encoded_size(); + assert!( + TX_EXTRA_BYTES as usize >= extra_bytes_in_transaction, + "Hardcoded number of extra bytes in Rococo transaction {} is lower than actual value: {}", + TX_EXTRA_BYTES, + extra_bytes_in_transaction, + ); + } +} diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index 9bb541ed9f07..a68123efea23 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -82,6 +82,8 @@ use runtime_parachains::ump as parachains_ump; use runtime_parachains::hrmp as parachains_hrmp; use runtime_parachains::scheduler as parachains_scheduler; +use bridge_runtime_common::messages::{MessageBridge, source::estimate_message_dispatch_and_delivery_fee}; + pub use pallet_balances::Call as BalancesCall; use polkadot_parachain::primitives::Id as ParaId; @@ -99,6 +101,7 @@ use frame_support::traits::InstanceFilter; /// Constant values used within the runtime. pub mod constants; +mod bridge_messages; mod validator_manager; // Make the WASM binary available. @@ -245,6 +248,13 @@ construct_runtime! { // Validator Manager pallet. ValidatorManager: validator_manager::{Pallet, Call, Storage, Event}, + // Bridge messages support. The same story as with the bridge grandpa pallet above ^^^ - when we're + // running as Rococo we only use `BridgeWococoMessages`/`BridgeWococoMessagesDispatch`, and vice versa. + BridgeRococoMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config} = 43, + BridgeWococoMessages: pallet_bridge_messages::::{Pallet, Call, Storage, Event, Config} = 44, + BridgeRococoMessagesDispatch: pallet_bridge_dispatch::{Pallet, Event} = 45, + BridgeWococoMessagesDispatch: pallet_bridge_dispatch::::{Pallet, Event} = 46, + // A "council" Collective: pallet_collective::{Pallet, Call, Storage, Origin, Event, Config} = 80, Membership: pallet_membership::{Pallet, Call, Storage, Event, Config} = 81, @@ -857,6 +867,111 @@ impl pallet_bridge_grandpa::Config for Runtime { type WeightInfo = pallet_bridge_grandpa::weights::RialtoWeight; } +// Instance that is 'deployed' at Wococo chain. Responsible for dispatching Rococo -> Wococo messages. +pub type AtWococoFromRococoMessagesDispatch = pallet_bridge_dispatch::DefaultInstance; +impl pallet_bridge_dispatch::Config for Runtime { + type Event = Event; + type MessageId = (bp_messages::LaneId, bp_messages::MessageNonce); + type Call = Call; + type CallFilter = (); + type EncodedCall = bridge_messages::FromRococoEncodedCall; + type SourceChainAccountId = bp_wococo::AccountId; + type TargetChainAccountPublic = sp_runtime::MultiSigner; + type TargetChainSignature = sp_runtime::MultiSignature; + type AccountIdConverter = bp_rococo::AccountIdConverter; +} + +// Instance that is 'deployed' at Rococo chain. Responsible for dispatching Wococo -> Rococo messages. +pub type AtRococoFromWococoMessagesDispatch = pallet_bridge_dispatch::Instance1; +impl pallet_bridge_dispatch::Config for Runtime { + type Event = Event; + type MessageId = (bp_messages::LaneId, bp_messages::MessageNonce); + type Call = Call; + type CallFilter = (); + type EncodedCall = bridge_messages::FromWococoEncodedCall; + type SourceChainAccountId = bp_rococo::AccountId; + type TargetChainAccountPublic = sp_runtime::MultiSigner; + type TargetChainSignature = sp_runtime::MultiSignature; + type AccountIdConverter = bp_wococo::AccountIdConverter; +} + +parameter_types! { + pub const MaxMessagesToPruneAtOnce: bp_messages::MessageNonce = 8; + pub const MaxUnrewardedRelayerEntriesAtInboundLane: bp_messages::MessageNonce = + bp_rococo::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE; + pub const MaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce = + bp_rococo::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE; + pub const RootAccountForPayments: Option = None; +} + +// Instance that is 'deployed' at Wococo chain. Responsible for sending Wococo -> Rococo messages +// and receiving Rococo -> Wococo messages. +pub type AtWococoWithRococoMessagesInstance = pallet_bridge_messages::DefaultInstance; +impl pallet_bridge_messages::Config for Runtime { + type Event = Event; + type WeightInfo = pallet_bridge_messages::weights::RialtoWeight; + type Parameter = (); + type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce; + type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; + type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; + + type OutboundPayload = crate::bridge_messages::ToRococoMessagePayload; + type OutboundMessageFee = bp_wococo::Balance; + + type InboundPayload = crate::bridge_messages::FromRococoMessagePayload; + type InboundMessageFee = bp_rococo::Balance; + type InboundRelayer = bp_rococo::AccountId; + + type AccountIdConverter = bp_wococo::AccountIdConverter; + + type TargetHeaderChain = crate::bridge_messages::RococoAtWococo; + type LaneMessageVerifier = crate::bridge_messages::ToRococoMessageVerifier; + type MessageDeliveryAndDispatchPayment = pallet_bridge_messages::instant_payments::InstantCurrencyPayments< + Runtime, + pallet_balances::Pallet, + crate::bridge_messages::GetDeliveryConfirmationTransactionFee, + RootAccountForPayments, + >; + type OnDeliveryConfirmed = (); + + type SourceHeaderChain = crate::bridge_messages::RococoAtWococo; + type MessageDispatch = crate::bridge_messages::FromRococoMessageDispatch; +} + +// Instance that is 'deployed' at Rococo chain. Responsible for sending Rococo -> Wococo messages +// and receiving Wococo -> Rococo messages. +pub type AtRococoWithWococoMessagesInstance = pallet_bridge_messages::Instance1; +impl pallet_bridge_messages::Config for Runtime { + type Event = Event; + type WeightInfo = pallet_bridge_messages::weights::RialtoWeight; + type Parameter = (); + type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce; + type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; + type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; + + type OutboundPayload = crate::bridge_messages::ToWococoMessagePayload; + type OutboundMessageFee = bp_rococo::Balance; + + type InboundPayload = crate::bridge_messages::FromWococoMessagePayload; + type InboundMessageFee = bp_wococo::Balance; + type InboundRelayer = bp_wococo::AccountId; + + type AccountIdConverter = bp_rococo::AccountIdConverter; + + type TargetHeaderChain = crate::bridge_messages::WococoAtRococo; + type LaneMessageVerifier = crate::bridge_messages::ToWococoMessageVerifier; + type MessageDeliveryAndDispatchPayment = pallet_bridge_messages::instant_payments::InstantCurrencyPayments< + Runtime, + pallet_balances::Pallet, + crate::bridge_messages::GetDeliveryConfirmationTransactionFee, + RootAccountForPayments, + >; + type OnDeliveryConfirmed = (); + + type SourceHeaderChain = crate::bridge_messages::WococoAtRococo; + type MessageDispatch = crate::bridge_messages::FromWococoMessageDispatch; +} + impl Randomness for ParentHashRandomness { fn random(subject: &[u8]) -> (Hash, BlockNumber) { ( @@ -1302,6 +1417,116 @@ sp_api::impl_runtime_apis! { } } + impl bp_rococo::ToRococoOutboundLaneApi for Runtime { + fn estimate_message_delivery_and_dispatch_fee( + _lane_id: bp_messages::LaneId, + payload: bridge_messages::ToWococoMessagePayload, + ) -> Option { + estimate_message_dispatch_and_delivery_fee::( + &payload, + bridge_messages::AtWococoWithRococoMessageBridge::RELAYER_FEE_PERCENT, + ).ok() + } + + fn message_details( + lane: bp_messages::LaneId, + begin: bp_messages::MessageNonce, + end: bp_messages::MessageNonce, + ) -> Vec> { + (begin..=end).filter_map(|nonce| { + let message_data = BridgeRococoMessages::outbound_message_data(lane, nonce)?; + let decoded_payload = bridge_messages::ToRococoMessagePayload::decode( + &mut &message_data.payload[..] + ).ok()?; + Some(bp_messages::MessageDetails { + nonce, + dispatch_weight: decoded_payload.weight, + size: message_data.payload.len() as _, + delivery_and_dispatch_fee: message_data.fee, + dispatch_fee_payment: decoded_payload.dispatch_fee_payment, + }) + }) + .collect() + } + + fn latest_received_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { + BridgeRococoMessages::outbound_latest_received_nonce(lane) + } + + fn latest_generated_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { + BridgeRococoMessages::outbound_latest_generated_nonce(lane) + } + } + + impl bp_rococo::FromRococoInboundLaneApi for Runtime { + fn latest_received_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { + BridgeRococoMessages::inbound_latest_received_nonce(lane) + } + + fn latest_confirmed_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { + BridgeRococoMessages::inbound_latest_confirmed_nonce(lane) + } + + fn unrewarded_relayers_state(lane: bp_messages::LaneId) -> bp_messages::UnrewardedRelayersState { + BridgeRococoMessages::inbound_unrewarded_relayers_state(lane) + } + } + + impl bp_wococo::ToWococoOutboundLaneApi for Runtime { + fn estimate_message_delivery_and_dispatch_fee( + _lane_id: bp_messages::LaneId, + payload: bridge_messages::ToWococoMessagePayload, + ) -> Option { + estimate_message_dispatch_and_delivery_fee::( + &payload, + bridge_messages::AtRococoWithWococoMessageBridge::RELAYER_FEE_PERCENT, + ).ok() + } + + fn message_details( + lane: bp_messages::LaneId, + begin: bp_messages::MessageNonce, + end: bp_messages::MessageNonce, + ) -> Vec> { + (begin..=end).filter_map(|nonce| { + let message_data = BridgeWococoMessages::outbound_message_data(lane, nonce)?; + let decoded_payload = bridge_messages::ToWococoMessagePayload::decode( + &mut &message_data.payload[..] + ).ok()?; + Some(bp_messages::MessageDetails { + nonce, + dispatch_weight: decoded_payload.weight, + size: message_data.payload.len() as _, + delivery_and_dispatch_fee: message_data.fee, + dispatch_fee_payment: decoded_payload.dispatch_fee_payment, + }) + }) + .collect() + } + + fn latest_received_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { + BridgeWococoMessages::outbound_latest_received_nonce(lane) + } + + fn latest_generated_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { + BridgeWococoMessages::outbound_latest_generated_nonce(lane) + } + } + + impl bp_wococo::FromWococoInboundLaneApi for Runtime { + fn latest_received_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { + BridgeWococoMessages::inbound_latest_received_nonce(lane) + } + + fn latest_confirmed_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { + BridgeWococoMessages::inbound_latest_confirmed_nonce(lane) + } + + fn unrewarded_relayers_state(lane: bp_messages::LaneId) -> bp_messages::UnrewardedRelayersState { + BridgeWococoMessages::inbound_unrewarded_relayers_state(lane) + } + } + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { fn account_nonce(account: AccountId) -> Nonce { System::account_nonce(account)