diff --git a/bridges/bin/millau/node/src/chain_spec.rs b/bridges/bin/millau/node/src/chain_spec.rs index ed7ee2440a898..ae5f01bbb5904 100644 --- a/bridges/bin/millau/node/src/chain_spec.rs +++ b/bridges/bin/millau/node/src/chain_spec.rs @@ -18,8 +18,8 @@ use beefy_primitives::crypto::AuthorityId as BeefyId; use bp_millau::derive_account_from_rialto_id; use millau_runtime::{ AccountId, AuraConfig, BalancesConfig, BeefyConfig, BridgeRialtoMessagesConfig, - BridgeWestendGrandpaConfig, GenesisConfig, GrandpaConfig, SessionConfig, SessionKeys, - Signature, SudoConfig, SystemConfig, WASM_BINARY, + BridgeRialtoParachainMessagesConfig, BridgeWestendGrandpaConfig, GenesisConfig, GrandpaConfig, + SessionConfig, SessionKeys, Signature, SudoConfig, SystemConfig, WASM_BINARY, }; use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_core::{sr25519, Pair, Public}; @@ -151,7 +151,7 @@ fn endowed_accounts() -> Vec { get_account_id_from_seed::("George//stash"), get_account_id_from_seed::("Harry//stash"), get_account_id_from_seed::("RialtoMessagesOwner"), - get_account_id_from_seed::("WithRialtoTokenSwap"), + get_account_id_from_seed::("RialtoParachainMessagesOwner"), pallet_bridge_messages::relayer_fund_account_id::< bp_millau::AccountId, bp_millau::AccountIdConverter, @@ -217,6 +217,12 @@ fn testnet_genesis( owner: Some(get_account_id_from_seed::("RialtoMessagesOwner")), ..Default::default() }, + bridge_rialto_parachain_messages: BridgeRialtoParachainMessagesConfig { + owner: Some(get_account_id_from_seed::( + "RialtoParachainMessagesOwner", + )), + ..Default::default() + }, xcm_pallet: Default::default(), } } diff --git a/bridges/bin/millau/runtime/Cargo.toml b/bridges/bin/millau/runtime/Cargo.toml index 97c6878eb8437..9e8f50a38ef7e 100644 --- a/bridges/bin/millau/runtime/Cargo.toml +++ b/bridges/bin/millau/runtime/Cargo.toml @@ -20,7 +20,9 @@ serde = { version = "1.0", optional = true, features = ["derive"] } bp-header-chain = { path = "../../../primitives/header-chain", default-features = false } bp-messages = { path = "../../../primitives/messages", default-features = false } bp-millau = { path = "../../../primitives/chain-millau", default-features = false } +bp-polkadot-core = { path = "../../../primitives/polkadot-core", default-features = false } bp-rialto = { path = "../../../primitives/chain-rialto", default-features = false } +bp-rialto-parachain = { path = "../../../primitives/chain-rialto-parachain", default-features = false } bp-runtime = { path = "../../../primitives/runtime", default-features = false } bp-westend = { path = "../../../primitives/chain-westend", default-features = false } bridge-runtime-common = { path = "../../runtime-common", default-features = false } @@ -87,7 +89,9 @@ std = [ "bp-header-chain/std", "bp-messages/std", "bp-millau/std", + "bp-polkadot-core/std", "bp-rialto/std", + "bp-rialto-parachain/std", "bp-runtime/std", "bp-westend/std", "bridge-runtime-common/std", diff --git a/bridges/bin/millau/runtime/src/lib.rs b/bridges/bin/millau/runtime/src/lib.rs index 6f38f7a54ed54..23cf4d0b05b4e 100644 --- a/bridges/bin/millau/runtime/src/lib.rs +++ b/bridges/bin/millau/runtime/src/lib.rs @@ -29,14 +29,21 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); pub mod rialto_messages; +pub mod rialto_parachain_messages; pub mod xcm_config; -use crate::rialto_messages::{ToRialtoMessagePayload, WithRialtoMessageBridge}; +use crate::{ + rialto_messages::{ToRialtoMessagePayload, WithRialtoMessageBridge}, + rialto_parachain_messages::{ + ToRialtoParachainMessagePayload, WithRialtoParachainMessageBridge, + }, +}; use beefy_primitives::{crypto::AuthorityId as BeefyId, mmr::MmrLeafVersion, ValidatorSet}; use bridge_runtime_common::messages::{ source::estimate_message_dispatch_and_delivery_fee, MessageBridge, }; +use codec::Decode; use pallet_grandpa::{ fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, }; @@ -49,7 +56,9 @@ use sp_mmr_primitives::{ }; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{Block as BlockT, IdentityLookup, Keccak256, NumberFor, OpaqueKeys}, + traits::{ + Block as BlockT, Header as HeaderT, IdentityLookup, Keccak256, NumberFor, OpaqueKeys, + }, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, FixedPointNumber, FixedU128, Perquintill, }; @@ -416,6 +425,15 @@ impl pallet_bridge_grandpa::Config for Runtime { type WeightInfo = pallet_bridge_grandpa::weights::MillauWeight; } +pub type RialtoParachainGrandpaInstance = pallet_bridge_grandpa::Instance2; +impl pallet_bridge_grandpa::Config for Runtime { + type BridgedChain = bp_rialto_parachain::RialtoParachain; + type MaxRequests = MaxRequests; + type HeadersToKeep = HeadersToKeep; + + type WeightInfo = pallet_bridge_grandpa::weights::MillauWeight; +} + impl pallet_shift_session_manager::Config for Runtime {} parameter_types! { @@ -429,6 +447,7 @@ parameter_types! { bp_millau::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT as _; pub const RootAccountForPayments: Option = None; pub const RialtoChainId: bp_runtime::ChainId = bp_runtime::RIALTO_CHAIN_ID; + pub const RialtoParachainChainId: bp_runtime::ChainId = bp_runtime::RIALTO_PARACHAIN_CHAIN_ID; } /// Instance of the messages pallet used to relay messages to/from Rialto chain. @@ -462,6 +481,37 @@ impl pallet_bridge_messages::Config for Runtime { type BridgedChainId = RialtoChainId; } +/// Instance of the messages pallet used to relay messages to/from RialtoParachain chain. +pub type WithRialtoParachainMessagesInstance = pallet_bridge_messages::Instance1; + +impl pallet_bridge_messages::Config for Runtime { + type Event = Event; + type WeightInfo = pallet_bridge_messages::weights::MillauWeight; + type Parameter = rialto_parachain_messages::MillauToRialtoParachainMessagesParameter; + type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce; + type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; + type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; + + type OutboundPayload = crate::rialto_parachain_messages::ToRialtoParachainMessagePayload; + type OutboundMessageFee = Balance; + + type InboundPayload = crate::rialto_parachain_messages::FromRialtoParachainMessagePayload; + type InboundMessageFee = bp_rialto_parachain::Balance; + type InboundRelayer = bp_rialto_parachain::AccountId; + + type AccountIdConverter = bp_millau::AccountIdConverter; + + type TargetHeaderChain = crate::rialto_parachain_messages::RialtoParachain; + type LaneMessageVerifier = crate::rialto_parachain_messages::ToRialtoParachainMessageVerifier; + type MessageDeliveryAndDispatchPayment = (); + type OnMessageAccepted = (); + type OnDeliveryConfirmed = (); + + type SourceHeaderChain = crate::rialto_parachain_messages::RialtoParachain; + type MessageDispatch = crate::rialto_parachain_messages::FromRialtoParachainMessageDispatch; + type BridgedChainId = RialtoParachainChainId; +} + parameter_types! { pub const RialtoParasPalletName: &'static str = bp_rialto::PARAS_PALLET_NAME; } @@ -509,8 +559,9 @@ construct_runtime!( // Westend bridge modules. BridgeWestendGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Config, Storage}, - // Rialto parachains bridge modules. + // RialtoParachain bridge modules. BridgeRialtoParachains: pallet_bridge_parachains::{Pallet, Call, Storage}, + BridgeRialtoParachainMessages: pallet_bridge_messages::::{Pallet, Call, Storage, Event, Config}, // Pallet for sending XCM. XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event, Origin, Config} = 99, @@ -750,6 +801,23 @@ impl_runtime_apis! { } } + impl bp_rialto_parachain::RialtoParachainFinalityApi for Runtime { + fn best_finalized() -> (bp_rialto::BlockNumber, bp_rialto::Hash) { + // the parachains finality pallet is never decoding parachain heads, so it is + // only done in the integration code + use crate::rialto_parachain_messages::RIALTO_PARACHAIN_ID; + let best_rialto_parachain_head = pallet_bridge_parachains::Pallet::< + Runtime, + WitRialtoParachainsInstance, + >::best_parachain_head(RIALTO_PARACHAIN_ID.into()) + .and_then(|encoded_header| bp_rialto_parachain::Header::decode(&mut &encoded_header.0[..]).ok()); + match best_rialto_parachain_head { + Some(head) => (*head.number(), head.hash()), + None => (Default::default(), Default::default()), + } + } + } + impl bp_rialto::ToRialtoOutboundLaneApi for Runtime { fn estimate_message_delivery_and_dispatch_fee( _lane_id: bp_messages::LaneId, @@ -777,6 +845,33 @@ impl_runtime_apis! { } } + impl bp_rialto_parachain::ToRialtoParachainOutboundLaneApi for Runtime { + fn estimate_message_delivery_and_dispatch_fee( + _lane_id: bp_messages::LaneId, + payload: ToRialtoParachainMessagePayload, + rialto_parachain_to_this_conversion_rate: Option, + ) -> Option { + estimate_message_dispatch_and_delivery_fee::( + &payload, + WithRialtoParachainMessageBridge::RELAYER_FEE_PERCENT, + rialto_parachain_to_this_conversion_rate, + ).ok() + } + + fn message_details( + lane: bp_messages::LaneId, + begin: bp_messages::MessageNonce, + end: bp_messages::MessageNonce, + ) -> Vec> { + bridge_runtime_common::messages_api::outbound_message_details::< + Runtime, + WithRialtoParachainMessagesInstance, + WithRialtoParachainMessageBridge, + xcm_config::OutboundXcmWeigher, + >(lane, begin, end) + } + } + #[cfg(feature = "runtime-benchmarks")] impl frame_benchmarking::Benchmark for Runtime { fn benchmark_metadata(extra: bool) -> ( diff --git a/bridges/bin/millau/runtime/src/rialto_parachain_messages.rs b/bridges/bin/millau/runtime/src/rialto_parachain_messages.rs new file mode 100644 index 0000000000000..c4d8f2c10ce56 --- /dev/null +++ b/bridges/bin/millau/runtime/src/rialto_parachain_messages.rs @@ -0,0 +1,304 @@ +// 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 +// (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 . + +//! Everything required to serve Millau <-> RialtoParachain messages. + +use crate::Runtime; + +use bp_messages::{ + source_chain::{SenderOrigin, TargetHeaderChain}, + target_chain::{ProvedMessages, SourceHeaderChain}, + InboundLaneData, LaneId, Message, MessageNonce, Parameter as MessagesParameter, +}; +use bp_polkadot_core::parachains::ParaId; +use bp_runtime::{Chain, ChainId, MILLAU_CHAIN_ID, RIALTO_PARACHAIN_CHAIN_ID}; +use bridge_runtime_common::messages::{self, MessageBridge, MessageTransaction}; +use codec::{Decode, Encode}; +use frame_support::{ + parameter_types, + weights::{DispatchClass, Weight}, + RuntimeDebug, +}; +use scale_info::TypeInfo; +use sp_runtime::{traits::Saturating, FixedPointNumber, FixedU128}; +use sp_std::convert::TryFrom; + +/// Identifier of RialtoParachain in the Rialto relay chain. +/// +/// This identifier is not something that is declared either by Rialto or RialtoParachain. This +/// is an identifier of registration. So in theory it may be changed. But since bridge is going +/// to be deployed after parachain registration AND since parachain de-registration is highly +/// likely impossible, it is fine to declare this constant here. +pub const RIALTO_PARACHAIN_ID: u32 = 2000; +/// Weight of 2 XCM instructions is for simple `Trap(42)` program, coming through bridge +/// (it is prepended with `UniversalOrigin` instruction). It is used just for simplest manual +/// tests, confirming that we don't break encoding somewhere between. +pub const BASE_XCM_WEIGHT_TWICE: Weight = 2 * crate::xcm_config::BASE_XCM_WEIGHT; + +/// Initial value of `RialtoParachainToMillauConversionRate` parameter. +pub const INITIAL_RIALTO_PARACHAIN_TO_MILLAU_CONVERSION_RATE: FixedU128 = + FixedU128::from_inner(FixedU128::DIV); +/// Initial value of `RialtoParachainFeeMultiplier` parameter. +pub const INITIAL_RIALTO_PARACHAIN_FEE_MULTIPLIER: FixedU128 = + FixedU128::from_inner(FixedU128::DIV); + +parameter_types! { + /// RialtoParachain to Millau conversion rate. Initially we treat both tokens as equal. + pub storage RialtoParachainToMillauConversionRate: FixedU128 = INITIAL_RIALTO_PARACHAIN_TO_MILLAU_CONVERSION_RATE; + /// Fee multiplier value at RialtoParachain chain. + pub storage RialtoParachainFeeMultiplier: FixedU128 = INITIAL_RIALTO_PARACHAIN_FEE_MULTIPLIER; +} + +/// Message payload for Millau -> RialtoParachain messages. +pub type ToRialtoParachainMessagePayload = messages::source::FromThisChainMessagePayload; + +/// Message verifier for Millau -> RialtoParachain messages. +pub type ToRialtoParachainMessageVerifier = + messages::source::FromThisChainMessageVerifier; + +/// Message payload for RialtoParachain -> Millau messages. +pub type FromRialtoParachainMessagePayload = + messages::target::FromBridgedChainMessagePayload; + +/// Messages proof for RialtoParachain -> Millau messages. +type FromRialtoParachainMessagesProof = + messages::target::FromBridgedChainMessagesProof; + +/// Messages delivery proof for Millau -> RialtoParachain messages. +type ToRialtoParachainMessagesDeliveryProof = + messages::source::FromBridgedChainMessagesDeliveryProof; + +/// Call-dispatch based message dispatch for RialtoParachain -> Millau messages. +pub type FromRialtoParachainMessageDispatch = messages::target::FromBridgedChainMessageDispatch< + WithRialtoParachainMessageBridge, + xcm_executor::XcmExecutor, + crate::xcm_config::XcmWeigher, + // 2 XCM instructions is for simple `Trap(42)` program, coming through bridge + // (it is prepended with `UniversalOrigin` instruction) + frame_support::traits::ConstU64, +>; + +/// Millau <-> RialtoParachain message bridge. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct WithRialtoParachainMessageBridge; + +impl MessageBridge for WithRialtoParachainMessageBridge { + const RELAYER_FEE_PERCENT: u32 = 10; + const THIS_CHAIN_ID: ChainId = MILLAU_CHAIN_ID; + const BRIDGED_CHAIN_ID: ChainId = RIALTO_PARACHAIN_CHAIN_ID; + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME; + + type ThisChain = Millau; + type BridgedChain = RialtoParachain; + + fn bridged_balance_to_this_balance( + bridged_balance: bp_rialto_parachain::Balance, + bridged_to_this_conversion_rate_override: Option, + ) -> bp_millau::Balance { + let conversion_rate = bridged_to_this_conversion_rate_override + .unwrap_or_else(|| RialtoParachainToMillauConversionRate::get()); + bp_millau::Balance::try_from(conversion_rate.saturating_mul_int(bridged_balance)) + .unwrap_or(bp_millau::Balance::MAX) + } +} + +/// Millau chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct Millau; + +impl messages::ChainWithMessages for Millau { + type Hash = bp_millau::Hash; + type AccountId = bp_millau::AccountId; + type Signer = bp_millau::AccountSigner; + type Signature = bp_millau::Signature; + type Weight = Weight; + type Balance = bp_millau::Balance; +} + +impl messages::ThisChainWithMessages for Millau { + type Call = crate::Call; + type Origin = crate::Origin; + + fn is_message_accepted(send_origin: &Self::Origin, lane: &LaneId) -> bool { + (*lane == [0, 0, 0, 0] || *lane == [0, 0, 0, 1]) && send_origin.linked_account().is_some() + } + + fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { + MessageNonce::MAX + } + + fn estimate_delivery_confirmation_transaction() -> MessageTransaction { + let inbound_data_size = InboundLaneData::::encoded_size_hint( + bp_millau::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, + 1, + 1, + ) + .unwrap_or(u32::MAX); + + MessageTransaction { + dispatch_weight: bp_millau::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT, + size: inbound_data_size + .saturating_add(bp_rialto_parachain::EXTRA_STORAGE_PROOF_SIZE) + .saturating_add(bp_millau::TX_EXTRA_BYTES), + } + } + + fn transaction_payment(transaction: MessageTransaction) -> bp_millau::Balance { + // `transaction` may represent transaction from the future, when multiplier value will + // be larger, so let's use slightly increased value + let multiplier = FixedU128::saturating_from_rational(110, 100) + .saturating_mul(pallet_transaction_payment::Pallet::::next_fee_multiplier()); + // in our testnets, both per-byte fee and weight-to-fee are 1:1 + messages::transaction_payment( + bp_millau::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic, + 1, + multiplier, + |weight| weight as _, + transaction, + ) + } +} + +/// RialtoParachain chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct RialtoParachain; + +impl messages::ChainWithMessages for RialtoParachain { + type Hash = bp_rialto_parachain::Hash; + type AccountId = bp_rialto_parachain::AccountId; + type Signer = bp_rialto_parachain::AccountSigner; + type Signature = bp_rialto_parachain::Signature; + type Weight = Weight; + type Balance = bp_rialto_parachain::Balance; +} + +impl messages::BridgedChainWithMessages for RialtoParachain { + fn maximal_extrinsic_size() -> u32 { + bp_rialto_parachain::RialtoParachain::max_extrinsic_size() + } + + fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { + true + } + + 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(bp_rialto_parachain::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT) + .saturating_add(bp_rialto_parachain::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT) + .saturating_sub(if include_pay_dispatch_fee_cost { + 0 + } else { + bp_rialto_parachain::PAY_INBOUND_DISPATCH_FEE_WEIGHT + }) + .saturating_add(message_dispatch_weight), + size: message_payload_len + .saturating_add(bp_millau::EXTRA_STORAGE_PROOF_SIZE) + .saturating_add(bp_rialto_parachain::TX_EXTRA_BYTES), + } + } + + fn transaction_payment( + transaction: MessageTransaction, + ) -> bp_rialto_parachain::Balance { + // we don't have a direct access to the value of multiplier at RialtoParachain chain + // => it is a messages module parameter + let multiplier = RialtoParachainFeeMultiplier::get(); + // in our testnets, both per-byte fee and weight-to-fee are 1:1 + messages::transaction_payment( + bp_rialto_parachain::BlockWeights::get() + .get(DispatchClass::Normal) + .base_extrinsic, + 1, + multiplier, + |weight| weight as _, + transaction, + ) + } +} + +impl TargetHeaderChain + for RialtoParachain +{ + type Error = &'static str; + // The proof is: + // - hash of the header this proof has been created with; + // - the storage proof or one or several keys; + // - id of the lane we prove state of. + type MessagesDeliveryProof = ToRialtoParachainMessagesDeliveryProof; + + fn verify_message(payload: &ToRialtoParachainMessagePayload) -> 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_from_parachain::< + WithRialtoParachainMessageBridge, + bp_rialto_parachain::Header, + Runtime, + crate::WitRialtoParachainsInstance, + >(ParaId(RIALTO_PARACHAIN_ID), proof) + } +} + +impl SourceHeaderChain for RialtoParachain { + type Error = &'static str; + // The proof is: + // - hash of the header this proof has been created with; + // - the storage proof or one or several keys; + // - id of the lane we prove messages for; + // - inclusive range of messages nonces that are proved. + type MessagesProof = FromRialtoParachainMessagesProof; + + fn verify_messages_proof( + proof: Self::MessagesProof, + messages_count: u32, + ) -> Result>, Self::Error> { + messages::target::verify_messages_proof_from_parachain::< + WithRialtoParachainMessageBridge, + bp_rialto_parachain::Header, + Runtime, + crate::WitRialtoParachainsInstance, + >(ParaId(RIALTO_PARACHAIN_ID), proof, messages_count) + } +} + +/// Millau -> RialtoParachain message lane pallet parameters. +#[derive(RuntimeDebug, Clone, Encode, Decode, PartialEq, Eq, TypeInfo)] +pub enum MillauToRialtoParachainMessagesParameter { + /// The conversion formula we use is: `MillauTokens = RialtoParachainTokens * conversion_rate`. + RialtoParachainToMillauConversionRate(FixedU128), +} + +impl MessagesParameter for MillauToRialtoParachainMessagesParameter { + fn save(&self) { + match *self { + MillauToRialtoParachainMessagesParameter::RialtoParachainToMillauConversionRate( + ref conversion_rate, + ) => RialtoParachainToMillauConversionRate::set(conversion_rate), + } + } +} diff --git a/bridges/bin/rialto-parachain/node/Cargo.toml b/bridges/bin/rialto-parachain/node/Cargo.toml index 7f5e6dedb6b9a..d73cdc6d27638 100644 --- a/bridges/bin/rialto-parachain/node/Cargo.toml +++ b/bridges/bin/rialto-parachain/node/Cargo.toml @@ -25,6 +25,11 @@ codec = { package = 'parity-scale-codec', version = '3.0.0' } serde = { version = '1.0', features = ['derive'] } hex-literal = '0.3.1' +# Bridge dependencies + +bp-rialto-parachain = { path = "../../../primitives/chain-rialto-parachain" } +pallet-bridge-messages = { path = "../../../modules/messages" } + # RPC related Dependencies jsonrpc-core = '18.0' diff --git a/bridges/bin/rialto-parachain/node/src/chain_spec.rs b/bridges/bin/rialto-parachain/node/src/chain_spec.rs index 6a8e751677df7..6f52155817417 100644 --- a/bridges/bin/rialto-parachain/node/src/chain_spec.rs +++ b/bridges/bin/rialto-parachain/node/src/chain_spec.rs @@ -81,6 +81,10 @@ pub fn development_config(id: ParaId) -> ChainSpec { get_account_id_from_seed::("Bob"), get_account_id_from_seed::("Alice//stash"), get_account_id_from_seed::("Bob//stash"), + pallet_bridge_messages::relayer_fund_account_id::< + bp_rialto_parachain::AccountId, + bp_rialto_parachain::AccountIdConverter, + >(), ], id, ) @@ -126,6 +130,10 @@ pub fn local_testnet_config(id: ParaId) -> ChainSpec { get_account_id_from_seed::("Dave//stash"), get_account_id_from_seed::("Eve//stash"), get_account_id_from_seed::("Ferdie//stash"), + pallet_bridge_messages::relayer_fund_account_id::< + bp_rialto_parachain::AccountId, + bp_rialto_parachain::AccountIdConverter, + >(), ], id, ) @@ -161,6 +169,7 @@ fn testnet_genesis( parachain_info: rialto_parachain_runtime::ParachainInfoConfig { parachain_id: id }, aura: rialto_parachain_runtime::AuraConfig { authorities: initial_authorities }, aura_ext: Default::default(), + bridge_millau_messages: Default::default(), // parachain_system: Default::default(), } } diff --git a/bridges/bin/rialto-parachain/runtime/Cargo.toml b/bridges/bin/rialto-parachain/runtime/Cargo.toml index 52a54bd7bb72a..543e2675284bf 100644 --- a/bridges/bin/rialto-parachain/runtime/Cargo.toml +++ b/bridges/bin/rialto-parachain/runtime/Cargo.toml @@ -18,7 +18,13 @@ serde = { version = '1.0', optional = true, features = ['derive'] } # Bridge depedencies +bp-messages = { path = "../../../primitives/messages", default-features = false } +bp-millau = { path = "../../../primitives/chain-millau", default-features = false } +bp-runtime = { path = "../../../primitives/runtime", default-features = false } bp-rialto-parachain = { path = "../../../primitives/chain-rialto-parachain", default-features = false } +bridge-runtime-common = { path = "../../runtime-common", default-features = false } +pallet-bridge-grandpa = { path = "../../../modules/grandpa", default-features = false } +pallet-bridge-messages = { path = "../../../modules/messages", default-features = false } # Substrate Dependencies ## Substrate Primitive Dependencies @@ -82,7 +88,11 @@ runtime-benchmarks = [ 'pallet-timestamp/runtime-benchmarks', ] std = [ + "bp-messages/std", + "bp-millau/std", + "bp-runtime/std", "bp-rialto-parachain/std", + "bridge-runtime-common/std", "codec/std", "log/std", "scale-info/std", @@ -102,6 +112,8 @@ std = [ "frame-executive/std", "frame-system/std", "pallet-balances/std", + "pallet-bridge-grandpa/std", + "pallet-bridge-messages/std", "pallet-randomness-collective-flip/std", "pallet-timestamp/std", "pallet-sudo/std", diff --git a/bridges/bin/rialto-parachain/runtime/src/lib.rs b/bridges/bin/rialto-parachain/runtime/src/lib.rs index 41f159c70d24b..e35e78e9110b1 100644 --- a/bridges/bin/rialto-parachain/runtime/src/lib.rs +++ b/bridges/bin/rialto-parachain/runtime/src/lib.rs @@ -26,13 +26,18 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +use crate::millau_messages::{ToMillauMessagePayload, WithMillauMessageBridge}; + +use bridge_runtime_common::messages::{ + source::estimate_message_dispatch_and_delivery_fee, MessageBridge, +}; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{AccountIdLookup, Block as BlockT}, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, + ApplyExtrinsicResult, FixedU128, }; use sp_std::prelude::*; @@ -52,6 +57,7 @@ pub use frame_support::{ }; pub use frame_system::{Call as SystemCall, EnsureRoot}; pub use pallet_balances::Call as BalancesCall; +pub use pallet_sudo::Call as SudoCall; pub use pallet_timestamp::Call as TimestampCall; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; #[cfg(any(feature = "std", test))] @@ -63,6 +69,9 @@ pub use bp_rialto_parachain::{ Index, Signature, MAXIMUM_BLOCK_WEIGHT, }; +pub use pallet_bridge_grandpa::Call as BridgeGrandpaCall; +pub use pallet_bridge_messages::Call as MessagesCall; + // Polkadot & XCM imports use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; @@ -76,6 +85,8 @@ use xcm_builder::{ }; use xcm_executor::{Config, XcmExecutor}; +mod millau_messages; + /// The address format for describing accounts. pub type Address = MultiAddress; /// Block type as expected by this runtime. @@ -88,6 +99,7 @@ pub type BlockId = generic::BlockId; pub type SignedExtra = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, frame_system::CheckGenesis, frame_system::CheckEra, frame_system::CheckNonce, @@ -342,9 +354,11 @@ pub type XcmOriginToTransactDispatchOrigin = ( XcmPassthrough, ); +pub const BASE_XCM_WEIGHT: Weight = 1_000_000; + parameter_types! { // One XCM operation is 1_000_000 weight - almost certainly a conservative estimate. - pub UnitWeightCost: Weight = 1_000_000; + pub UnitWeightCost: Weight = BASE_XCM_WEIGHT; // One UNIT buys 1 second of weight. pub const WeightPrice: (MultiLocation, u128) = (MultiLocation::parent(), UNIT); pub const MaxInstructions: u32 = 100; @@ -366,6 +380,11 @@ pub type Barrier = ( // ^^^ Parent & its unit plurality gets free execution ); +/// Outbound XCM weigher type. +pub type OutboundXcmWeigher = FixedWeightBounds; +/// XCM weigher type. +pub type XcmWeigher = FixedWeightBounds; + pub struct XcmConfig; impl Config for XcmConfig { type Call = Call; @@ -376,7 +395,7 @@ impl Config for XcmConfig { type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation of UNIT type UniversalLocation = UniversalLocation; type Barrier = Barrier; - type Weigher = FixedWeightBounds; + type Weigher = XcmWeigher; type Trader = UsingComponents, RelayLocation, AccountId, Balances, ()>; type ResponseHandler = PolkadotXcm; type AssetTrap = PolkadotXcm; @@ -412,7 +431,7 @@ impl pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor; type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; + type Weigher = XcmWeigher; type Origin = Origin; type Call = Call; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; @@ -454,10 +473,71 @@ impl pallet_aura::Config for Runtime { type MaxAuthorities = MaxAuthorities; } -// /// Configure the pallet template in pallets/template. -// impl template::Config for Runtime { -// type Event = Event; -// } +parameter_types! { + /// This is a pretty unscientific cap. + /// + /// Note that once this is hit the pallet will essentially throttle incoming requests down to one + /// call per block. + pub const MaxRequests: u32 = 50; + + /// Number of headers to keep. + /// + /// Assuming the worst case of every header being finalized, we will keep headers at least for a + /// week. + pub const HeadersToKeep: u32 = 7 * bp_millau::DAYS as u32; +} + +pub type MillauGrandpaInstance = (); +impl pallet_bridge_grandpa::Config for Runtime { + type BridgedChain = bp_millau::Millau; + type MaxRequests = MaxRequests; + type HeadersToKeep = HeadersToKeep; + type WeightInfo = pallet_bridge_grandpa::weights::MillauWeight; +} + +parameter_types! { + pub const MaxMessagesToPruneAtOnce: bp_messages::MessageNonce = 8; + pub const MaxUnrewardedRelayerEntriesAtInboundLane: bp_messages::MessageNonce = + bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + pub const MaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce = + bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + // `IdentityFee` is used by Rialto => we may use weight directly + pub const GetDeliveryConfirmationTransactionFee: Balance = + bp_rialto_parachain::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT as _; + pub const RootAccountForPayments: Option = None; + pub const BridgedChainId: bp_runtime::ChainId = bp_runtime::MILLAU_CHAIN_ID; +} + +/// Instance of the messages pallet used to relay messages to/from Millau chain. +pub type WithMillauMessagesInstance = (); + +impl pallet_bridge_messages::Config for Runtime { + type Event = Event; + type WeightInfo = pallet_bridge_messages::weights::MillauWeight; + type Parameter = millau_messages::RialtoParachainToMillauMessagesParameter; + type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce; + type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; + type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; + + type OutboundPayload = crate::millau_messages::ToMillauMessagePayload; + type OutboundMessageFee = Balance; + + type InboundPayload = crate::millau_messages::FromMillauMessagePayload; + type InboundMessageFee = bp_millau::Balance; + type InboundRelayer = bp_millau::AccountId; + + type AccountIdConverter = bp_rialto_parachain::AccountIdConverter; + + type TargetHeaderChain = crate::millau_messages::Millau; + type LaneMessageVerifier = crate::millau_messages::ToMillauMessageVerifier; + type MessageDeliveryAndDispatchPayment = (); + type OnMessageAccepted = (); + type OnDeliveryConfirmed = (); + + type SourceHeaderChain = crate::millau_messages::Millau; + type MessageDispatch = crate::millau_messages::FromMillauMessageDispatch; + type BridgedChainId = BridgedChainId; +} // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( @@ -486,8 +566,9 @@ construct_runtime!( CumulusXcm: cumulus_pallet_xcm::{Pallet, Call, Event, Origin} = 52, DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event} = 53, - // //Template - // TemplatePallet: template::{Pallet, Call, Storage, Event}, + // Millau bridge modules. + BridgeMillauGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage}, + BridgeMillauMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, } ); @@ -600,6 +681,40 @@ impl_runtime_apis! { } } + impl bp_millau::MillauFinalityApi for Runtime { + fn best_finalized() -> (bp_millau::BlockNumber, bp_millau::Hash) { + let header = BridgeMillauGrandpa::best_finalized(); + (header.number, header.hash()) + } + } + + impl bp_millau::ToMillauOutboundLaneApi for Runtime { + fn estimate_message_delivery_and_dispatch_fee( + _lane_id: bp_messages::LaneId, + payload: ToMillauMessagePayload, + millau_to_this_conversion_rate: Option, + ) -> Option { + estimate_message_dispatch_and_delivery_fee::( + &payload, + WithMillauMessageBridge::RELAYER_FEE_PERCENT, + millau_to_this_conversion_rate, + ).ok() + } + + fn message_details( + lane: bp_messages::LaneId, + begin: bp_messages::MessageNonce, + end: bp_messages::MessageNonce, + ) -> Vec> { + bridge_runtime_common::messages_api::outbound_message_details::< + Runtime, + WithMillauMessagesInstance, + WithMillauMessageBridge, + OutboundXcmWeigher, + >(lane, begin, end) + } + } + #[cfg(feature = "runtime-benchmarks")] impl frame_benchmarking::Benchmark for Runtime { fn dispatch_benchmark( diff --git a/bridges/bin/rialto-parachain/runtime/src/millau_messages.rs b/bridges/bin/rialto-parachain/runtime/src/millau_messages.rs new file mode 100644 index 0000000000000..4e765000b90d0 --- /dev/null +++ b/bridges/bin/rialto-parachain/runtime/src/millau_messages.rs @@ -0,0 +1,304 @@ +// 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 +// (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 . + +//! Everything required to serve Millau <-> RialtoParachain messages. + +// TODO: this is almost exact copy of `millau_messages.rs` from Rialto runtime. +// Should be extracted to a separate crate and reused here. + +use crate::Runtime; + +use bp_messages::{ + source_chain::{SenderOrigin, TargetHeaderChain}, + target_chain::{ProvedMessages, SourceHeaderChain}, + InboundLaneData, LaneId, Message, MessageNonce, Parameter as MessagesParameter, +}; +use bp_runtime::{Chain, ChainId, MILLAU_CHAIN_ID, RIALTO_PARACHAIN_CHAIN_ID}; +use bridge_runtime_common::messages::{self, MessageBridge, MessageTransaction}; +use codec::{Decode, Encode}; +use frame_support::{ + parameter_types, + weights::{DispatchClass, Weight}, + RuntimeDebug, +}; +use scale_info::TypeInfo; +use sp_runtime::{traits::Saturating, FixedPointNumber, FixedU128}; +use sp_std::convert::TryFrom; + +/// Initial value of `MillauToRialtoParachainConversionRate` parameter. +pub const INITIAL_MILLAU_TO_RIALTO_PARACHAIN_CONVERSION_RATE: FixedU128 = + FixedU128::from_inner(FixedU128::DIV); +/// Initial value of `MillauFeeMultiplier` parameter. +pub const INITIAL_MILLAU_FEE_MULTIPLIER: FixedU128 = FixedU128::from_inner(FixedU128::DIV); +/// Weight of 2 XCM instructions is for simple `Trap(42)` program, coming through bridge +/// (it is prepended with `UniversalOrigin` instruction). It is used just for simplest manual +/// tests, confirming that we don't break encoding somewhere between. +pub const BASE_XCM_WEIGHT_TWICE: Weight = 2 * crate::BASE_XCM_WEIGHT; + +parameter_types! { + /// Millau to RialtoParachain conversion rate. Initially we treat both tokens as equal. + pub storage MillauToRialtoParachainConversionRate: FixedU128 = INITIAL_MILLAU_TO_RIALTO_PARACHAIN_CONVERSION_RATE; + /// Fee multiplier value at Millau chain. + pub storage MillauFeeMultiplier: FixedU128 = INITIAL_MILLAU_FEE_MULTIPLIER; +} + +/// Message payload for RialtoParachain -> Millau messages. +pub type ToMillauMessagePayload = messages::source::FromThisChainMessagePayload; + +/// Message verifier for RialtoParachain -> Millau messages. +pub type ToMillauMessageVerifier = + messages::source::FromThisChainMessageVerifier; + +/// Message payload for Millau -> RialtoParachain messages. +pub type FromMillauMessagePayload = messages::target::FromBridgedChainMessagePayload; + +/// Call-dispatch based message dispatch for Millau -> RialtoParachain messages. +pub type FromMillauMessageDispatch = messages::target::FromBridgedChainMessageDispatch< + WithMillauMessageBridge, + xcm_executor::XcmExecutor, + crate::XcmWeigher, + // 2 XCM instructions is for simple `Trap(42)` program, coming through bridge + // (it is prepended with `UniversalOrigin` instruction) + frame_support::traits::ConstU64, +>; + +/// Messages proof for Millau -> RialtoParachain messages. +pub type FromMillauMessagesProof = messages::target::FromBridgedChainMessagesProof; + +/// Messages delivery proof for RialtoParachain -> Millau messages. +pub type ToMillauMessagesDeliveryProof = + messages::source::FromBridgedChainMessagesDeliveryProof; + +/// Millau <-> RialtoParachain message bridge. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct WithMillauMessageBridge; + +impl MessageBridge for WithMillauMessageBridge { + const RELAYER_FEE_PERCENT: u32 = 10; + const THIS_CHAIN_ID: ChainId = RIALTO_PARACHAIN_CHAIN_ID; + const BRIDGED_CHAIN_ID: ChainId = MILLAU_CHAIN_ID; + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = + bp_rialto_parachain::WITH_RIALTO_PARACHAIN_MESSAGES_PALLET_NAME; + + type ThisChain = RialtoParachain; + type BridgedChain = Millau; + + fn bridged_balance_to_this_balance( + bridged_balance: bp_millau::Balance, + bridged_to_this_conversion_rate_override: Option, + ) -> bp_rialto_parachain::Balance { + let conversion_rate = bridged_to_this_conversion_rate_override + .unwrap_or_else(|| MillauToRialtoParachainConversionRate::get()); + bp_rialto_parachain::Balance::try_from(conversion_rate.saturating_mul_int(bridged_balance)) + .unwrap_or(bp_rialto_parachain::Balance::MAX) + } +} + +/// RialtoParachain chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct RialtoParachain; + +impl messages::ChainWithMessages for RialtoParachain { + type Hash = bp_rialto_parachain::Hash; + type AccountId = bp_rialto_parachain::AccountId; + type Signer = bp_rialto_parachain::AccountSigner; + type Signature = bp_rialto_parachain::Signature; + type Weight = Weight; + type Balance = bp_rialto_parachain::Balance; +} + +impl messages::ThisChainWithMessages for RialtoParachain { + type Call = crate::Call; + type Origin = crate::Origin; + + fn is_message_accepted(send_origin: &Self::Origin, lane: &LaneId) -> bool { + send_origin.linked_account().is_some() && (*lane == [0, 0, 0, 0] || *lane == [0, 0, 0, 1]) + } + + fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { + MessageNonce::MAX + } + + fn estimate_delivery_confirmation_transaction() -> MessageTransaction { + let inbound_data_size = + InboundLaneData::::encoded_size_hint( + bp_rialto_parachain::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, + 1, + 1, + ) + .unwrap_or(u32::MAX); + + MessageTransaction { + dispatch_weight: + bp_rialto_parachain::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT, + size: inbound_data_size + .saturating_add(bp_millau::EXTRA_STORAGE_PROOF_SIZE) + .saturating_add(bp_rialto_parachain::TX_EXTRA_BYTES), + } + } + + fn transaction_payment( + transaction: MessageTransaction, + ) -> bp_rialto_parachain::Balance { + // `transaction` may represent transaction from the future, when multiplier value will + // be larger, so let's use slightly increased value + let multiplier = FixedU128::saturating_from_rational(110, 100) + .saturating_mul(pallet_transaction_payment::Pallet::::next_fee_multiplier()); + // in our testnets, both per-byte fee and weight-to-fee are 1:1 + messages::transaction_payment( + bp_rialto_parachain::BlockWeights::get() + .get(DispatchClass::Normal) + .base_extrinsic, + 1, + multiplier, + |weight| weight as _, + transaction, + ) + } +} + +/// Millau chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct Millau; + +impl messages::ChainWithMessages for Millau { + type Hash = bp_millau::Hash; + type AccountId = bp_millau::AccountId; + type Signer = bp_millau::AccountSigner; + type Signature = bp_millau::Signature; + type Weight = Weight; + type Balance = bp_millau::Balance; +} + +impl messages::BridgedChainWithMessages for Millau { + fn maximal_extrinsic_size() -> u32 { + bp_millau::Millau::max_extrinsic_size() + } + + fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { + true + } + + 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(bp_millau::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT) + .saturating_add(bp_millau::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT) + .saturating_sub(if include_pay_dispatch_fee_cost { + 0 + } else { + bp_millau::PAY_INBOUND_DISPATCH_FEE_WEIGHT + }) + .saturating_add(message_dispatch_weight), + size: message_payload_len + .saturating_add(bp_rialto_parachain::EXTRA_STORAGE_PROOF_SIZE) + .saturating_add(bp_millau::TX_EXTRA_BYTES), + } + } + + fn transaction_payment(transaction: MessageTransaction) -> bp_millau::Balance { + // we don't have a direct access to the value of multiplier at Millau chain + // => it is a messages module parameter + let multiplier = MillauFeeMultiplier::get(); + // in our testnets, both per-byte fee and weight-to-fee are 1:1 + messages::transaction_payment( + bp_millau::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic, + 1, + multiplier, + |weight| weight as _, + transaction, + ) + } +} + +impl TargetHeaderChain for Millau { + type Error = &'static str; + // The proof is: + // - hash of the header this proof has been created with; + // - the storage proof of one or several keys; + // - id of the lane we prove state of. + type MessagesDeliveryProof = ToMillauMessagesDeliveryProof; + + fn verify_message(payload: &ToMillauMessagePayload) -> 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::< + WithMillauMessageBridge, + Runtime, + crate::MillauGrandpaInstance, + >(proof) + } +} + +impl SourceHeaderChain for Millau { + type Error = &'static str; + // The proof is: + // - hash of the header this proof has been created with; + // - the storage proof of one or several keys; + // - id of the lane we prove messages for; + // - inclusive range of messages nonces that are proved. + type MessagesProof = FromMillauMessagesProof; + + fn verify_messages_proof( + proof: Self::MessagesProof, + messages_count: u32, + ) -> Result>, Self::Error> { + messages::target::verify_messages_proof::< + WithMillauMessageBridge, + Runtime, + crate::MillauGrandpaInstance, + >(proof, messages_count) + } +} + +impl SenderOrigin for crate::Origin { + fn linked_account(&self) -> Option { + match self.caller { + crate::OriginCaller::system(frame_system::RawOrigin::Signed(ref submitter)) => + Some(submitter.clone()), + _ => None, + } + } +} + +/// RialtoParachain -> Millau message lane pallet parameters. +#[derive(RuntimeDebug, Clone, Encode, Decode, PartialEq, Eq, TypeInfo)] +pub enum RialtoParachainToMillauMessagesParameter { + /// The conversion formula we use is: `RialtoParachainTokens = MillauTokens * conversion_rate`. + MillauToRialtoParachainConversionRate(FixedU128), +} + +impl MessagesParameter for RialtoParachainToMillauMessagesParameter { + fn save(&self) { + match *self { + RialtoParachainToMillauMessagesParameter::MillauToRialtoParachainConversionRate( + ref conversion_rate, + ) => MillauToRialtoParachainConversionRate::set(conversion_rate), + } + } +} diff --git a/bridges/bin/rialto/runtime/src/lib.rs b/bridges/bin/rialto/runtime/src/lib.rs index 52600c1a173a9..e87639f6cb2a8 100644 --- a/bridges/bin/rialto/runtime/src/lib.rs +++ b/bridges/bin/rialto/runtime/src/lib.rs @@ -425,7 +425,7 @@ parameter_types! { pub const GetDeliveryConfirmationTransactionFee: Balance = bp_rialto::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT as _; pub const RootAccountForPayments: Option = None; - pub const BridgedChainId: bp_runtime::ChainId = bp_runtime::MILLAU_CHAIN_ID; + pub const BridgedChainId: bp_runtime::ChainId = bp_runtime::MILLAU_CHAIN_ID; } /// Instance of the messages pallet used to relay messages to/from Millau chain. diff --git a/bridges/bin/runtime-common/Cargo.toml b/bridges/bin/runtime-common/Cargo.toml index 8177f11512574..61251847bb07e 100644 --- a/bridges/bin/runtime-common/Cargo.toml +++ b/bridges/bin/runtime-common/Cargo.toml @@ -18,9 +18,11 @@ static_assertions = { version = "1.1", optional = true } # Bridge dependencies bp-messages = { path = "../../primitives/messages", default-features = false } +bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } bp-runtime = { path = "../../primitives/runtime", default-features = false } pallet-bridge-grandpa = { path = "../../modules/grandpa", default-features = false } pallet-bridge-messages = { path = "../../modules/messages", default-features = false } +pallet-bridge-parachains = { path = "../../modules/parachains", default-features = false } # Substrate dependencies @@ -47,6 +49,7 @@ xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "gav-x default = ["std"] std = [ "bp-messages/std", + "bp-polkadot-core/std", "bp-runtime/std", "codec/std", "frame-support/std", @@ -56,6 +59,7 @@ std = [ "num-traits/std", "pallet-bridge-grandpa/std", "pallet-bridge-messages/std", + "pallet-bridge-parachains/std", "pallet-transaction-payment/std", "scale-info/std", "sp-api/std", diff --git a/bridges/bin/runtime-common/src/messages.rs b/bridges/bin/runtime-common/src/messages.rs index d3e3ca28ab736..87dd009cff066 100644 --- a/bridges/bin/runtime-common/src/messages.rs +++ b/bridges/bin/runtime-common/src/messages.rs @@ -25,13 +25,14 @@ use bp_messages::{ target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages}, InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce, OutboundLaneData, }; +use bp_polkadot_core::parachains::{ParaHash, ParaHasher, ParaId}; use bp_runtime::{messages::MessageDispatchResult, ChainId, Size, StorageProofChecker}; use codec::{Decode, DecodeLimit, Encode}; use frame_support::{traits::Get, weights::Weight, RuntimeDebug}; use hash_db::Hasher; use scale_info::TypeInfo; use sp_runtime::{ - traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedDiv, CheckedMul}, + traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedDiv, CheckedMul, Header as HeaderT}, FixedPointNumber, FixedPointOperand, FixedU128, }; use sp_std::{cmp::PartialOrd, convert::TryFrom, fmt::Debug, marker::PhantomData, vec::Vec}; @@ -390,6 +391,9 @@ pub mod source { } /// Verify proof of This -> Bridged chain messages delivery. + /// + /// This function is used when Bridged chain is directly using GRANDPA finality. For Bridged + /// parachains, please use the `verify_messages_delivery_proof_from_parachain`. pub fn verify_messages_delivery_proof( proof: FromBridgedChainMessagesDeliveryProof>>, ) -> Result, &'static str> @@ -406,23 +410,70 @@ pub mod source { pallet_bridge_grandpa::Pallet::::parse_finalized_storage_proof( bridged_header_hash.into(), StorageProof::new(storage_proof), - |storage| { - // Messages delivery proof is just proof of single storage key read => any error - // is fatal. - let storage_inbound_lane_data_key = - bp_messages::storage_keys::inbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, &lane); - let raw_inbound_lane_data = storage - .read_value(storage_inbound_lane_data_key.0.as_ref()) - .map_err(|_| "Failed to read inbound lane state from storage proof")? - .ok_or("Inbound lane state is missing from the messages proof")?; - let inbound_lane_data = InboundLaneData::decode(&mut &raw_inbound_lane_data[..]) - .map_err(|_| "Failed to decode inbound lane state from the proof")?; - - Ok((lane, inbound_lane_data)) - }, + |storage| do_verify_messages_delivery_proof::< + B, + bp_runtime::HasherOf< + >::BridgedChain, + >, + >(lane, storage), ) .map_err(<&'static str>::from)? } + + /// Verify proof of This -> Bridged chain messages delivery. + /// + /// This function is used when Bridged chain is using parachain finality. For Bridged + /// chains with direct GRANDPA finality, please use the `verify_messages_delivery_proof`. + /// + /// This function currently only supports parachains, which are using header type that + /// implements `sp_runtime::traits::Header` trait. + pub fn verify_messages_delivery_proof_from_parachain< + B, + BridgedHeader, + ThisRuntime, + ParachainsInstance: 'static, + >( + bridged_parachain: ParaId, + proof: FromBridgedChainMessagesDeliveryProof>>, + ) -> Result, &'static str> + where + B: MessageBridge, + B::BridgedChain: ChainWithMessages, + BridgedHeader: HeaderT>>, + ThisRuntime: pallet_bridge_parachains::Config, + { + let FromBridgedChainMessagesDeliveryProof { bridged_header_hash, storage_proof, lane } = + proof; + pallet_bridge_parachains::Pallet::::parse_finalized_storage_proof( + bridged_parachain, + bridged_header_hash, + StorageProof::new(storage_proof), + |para_head| BridgedHeader::decode(&mut ¶_head.0[..]).ok().map(|h| *h.state_root()), + |storage| do_verify_messages_delivery_proof::(lane, storage), + ) + .map_err(<&'static str>::from)? + } + + /// The essense of This -> Bridged chain messages delivery proof verification. + fn do_verify_messages_delivery_proof( + lane: LaneId, + storage: bp_runtime::StorageProofChecker, + ) -> Result, &'static str> { + // Messages delivery proof is just proof of single storage key read => any error + // is fatal. + let storage_inbound_lane_data_key = bp_messages::storage_keys::inbound_lane_data_key( + B::BRIDGED_MESSAGES_PALLET_NAME, + &lane, + ); + let raw_inbound_lane_data = storage + .read_value(storage_inbound_lane_data_key.0.as_ref()) + .map_err(|_| "Failed to read inbound lane state from storage proof")? + .ok_or("Inbound lane state is missing from the messages proof")?; + let inbound_lane_data = InboundLaneData::decode(&mut &raw_inbound_lane_data[..]) + .map_err(|_| "Failed to decode inbound lane state from the proof")?; + + Ok((lane, inbound_lane_data)) + } } /// Sub-module that is declaring types required for processing Bridged -> This chain messages. @@ -590,6 +641,9 @@ pub mod target { /// Verify proof of Bridged -> This chain messages. /// + /// This function is used when Bridged chain is directly using GRANDPA finality. For Bridged + /// parachains, please use the `verify_messages_proof_from_parachain`. + /// /// The `messages_count` argument verification (sane limits) is supposed to be made /// outside of this function. This function only verifies that the proof declares exactly /// `messages_count` messages. @@ -624,6 +678,54 @@ pub mod target { .map_err(Into::into) } + /// Verify proof of Bridged -> This chain messages. + /// + /// This function is used when Bridged chain is using parachain finality. For Bridged + /// chains with direct GRANDPA finality, please use the `verify_messages_proof`. + /// + /// The `messages_count` argument verification (sane limits) is supposed to be made + /// outside of this function. This function only verifies that the proof declares exactly + /// `messages_count` messages. + /// + /// This function currently only supports parachains, which are using header type that + /// implements `sp_runtime::traits::Header` trait. + pub fn verify_messages_proof_from_parachain< + B, + BridgedHeader, + ThisRuntime, + ParachainsInstance: 'static, + >( + bridged_parachain: ParaId, + proof: FromBridgedChainMessagesProof>>, + messages_count: u32, + ) -> Result>>>, &'static str> + where + B: MessageBridge, + B::BridgedChain: ChainWithMessages, + BridgedHeader: HeaderT>>, + ThisRuntime: pallet_bridge_parachains::Config, + { + verify_messages_proof_with_parser::( + proof, + messages_count, + |bridged_header_hash, bridged_storage_proof| { + pallet_bridge_parachains::Pallet::::parse_finalized_storage_proof( + bridged_parachain, + bridged_header_hash, + StorageProof::new(bridged_storage_proof), + |para_head| BridgedHeader::decode(&mut ¶_head.0[..]).ok().map(|h| *h.state_root()), + |storage_adapter| storage_adapter, + ) + .map(|storage| StorageProofCheckerAdapter::<_, B> { + storage, + _dummy: Default::default(), + }) + .map_err(|err| MessageProofError::Custom(err.into())) + }, + ) + .map_err(Into::into) + } + #[derive(Debug, PartialEq)] pub(crate) enum MessageProofError { Empty, diff --git a/bridges/modules/parachains/src/lib.rs b/bridges/modules/parachains/src/lib.rs index 9bed2b7a18a97..d7a74656c0cfd 100644 --- a/bridges/modules/parachains/src/lib.rs +++ b/bridges/modules/parachains/src/lib.rs @@ -24,7 +24,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use bp_parachains::parachain_head_storage_key_at_source; -use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; +use bp_polkadot_core::parachains::{ParaHash, ParaHasher, ParaHead, ParaHeadsProof, ParaId}; use codec::{Decode, Encode}; use frame_support::RuntimeDebug; use scale_info::TypeInfo; @@ -67,6 +67,12 @@ pub mod pallet { UnknownRelayChainBlock, /// Invalid storage proof has been passed. InvalidStorageProof, + /// Given parachain head is unknown. + UnknownParaHead, + /// The storage proof doesn't contains storage root. So it is invalid for given header. + StorageRootMismatch, + /// Failed to extract state root from given parachain head. + FailedToExtractStateRoot, } #[pallet::config] @@ -190,6 +196,38 @@ pub mod pallet { } impl, I: 'static> Pallet { + /// Get best finalized header of the given parachain. + pub fn best_parachain_head(parachain: ParaId) -> Option { + let best_para_head_hash = BestParaHeads::::get(parachain)?.head_hash; + ImportedParaHeads::::get(parachain, best_para_head_hash) + } + + /// Get parachain head with given hash. + pub fn parachain_head(parachain: ParaId, hash: ParaHash) -> Option { + ImportedParaHeads::::get(parachain, hash) + } + + /// Verify that the passed storage proof is valid, given it is crafted using + /// known finalized header. If the proof is valid, then the `parse` callback + /// is called and the function returns its result. + pub fn parse_finalized_storage_proof( + parachain: ParaId, + hash: ParaHash, + storage_proof: sp_trie::StorageProof, + decode_state_root: impl FnOnce(ParaHead) -> Option, + parse: impl FnOnce(bp_runtime::StorageProofChecker) -> R, + ) -> Result { + let para_head = + Self::parachain_head(parachain, hash).ok_or(Error::::UnknownParaHead)?; + let state_root = + decode_state_root(para_head).ok_or(Error::::FailedToExtractStateRoot)?; + let storage_proof_checker = + bp_runtime::StorageProofChecker::new(state_root, storage_proof) + .map_err(|_| Error::::StorageRootMismatch)?; + + Ok(parse(storage_proof_checker)) + } + /// Read parachain head from storage proof. fn read_parachain_head( storage: &bp_runtime::StorageProofChecker, @@ -329,7 +367,7 @@ mod tests { } fn prepare_parachain_heads_proof( - heads: Vec<(ParaId, ParaHead)>, + heads: Vec<(u32, ParaHead)>, ) -> (RelayBlockHash, ParaHeadsProof) { let mut root = Default::default(); let mut mdb = MemoryDB::default(); @@ -337,7 +375,7 @@ mod tests { let mut trie = TrieDBMutV1::::new(&mut mdb, &mut root); for (parachain, head) in heads { let storage_key = - parachain_head_storage_key_at_source(PARAS_PALLET_NAME, parachain); + parachain_head_storage_key_at_source(PARAS_PALLET_NAME, ParaId(parachain)); trie.insert(&storage_key.0, &head.encode()) .map_err(|_| "TrieMut::insert has failed") .expect("TrieMut::insert should not fail in tests"); @@ -385,10 +423,8 @@ mod tests { #[test] fn imports_initial_parachain_heads() { - let (state_root, proof) = prepare_parachain_heads_proof(vec![ - (ParaId(1), head_data(1, 0)), - (ParaId(3), head_data(3, 10)), - ]); + let (state_root, proof) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 0)), (3, head_data(3, 10))]); run_test(|| { initialize(state_root); @@ -429,10 +465,8 @@ mod tests { #[test] fn imports_parachain_heads_is_able_to_progress() { - let (state_root_5, proof_5) = - prepare_parachain_heads_proof(vec![(ParaId(1), head_data(1, 5))]); - let (state_root_10, proof_10) = - prepare_parachain_heads_proof(vec![(ParaId(1), head_data(1, 10))]); + let (state_root_5, proof_5) = prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + let (state_root_10, proof_10) = prepare_parachain_heads_proof(vec![(1, head_data(1, 10))]); run_test(|| { // start with relay block #0 and import head#5 of parachain#1 initialize(state_root_5); @@ -478,7 +512,7 @@ mod tests { #[test] fn does_nothing_when_already_imported_this_head_at_previous_relay_header() { - let (state_root, proof) = prepare_parachain_heads_proof(vec![(ParaId(1), head_data(1, 0))]); + let (state_root, proof) = prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); run_test(|| { // import head#0 of parachain#1 at relay block#0 initialize(state_root); @@ -495,10 +529,8 @@ mod tests { #[test] fn does_nothing_when_already_imported_head_at_better_relay_header() { - let (state_root_5, proof_5) = - prepare_parachain_heads_proof(vec![(ParaId(1), head_data(1, 5))]); - let (state_root_10, proof_10) = - prepare_parachain_heads_proof(vec![(ParaId(1), head_data(1, 10))]); + let (state_root_5, proof_5) = prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + let (state_root_10, proof_10) = prepare_parachain_heads_proof(vec![(1, head_data(1, 10))]); run_test(|| { // start with relay block #0 initialize(state_root_5); @@ -536,8 +568,7 @@ mod tests { // import exactly `HeadsToKeep` headers for i in 0..heads_to_keep { - let (state_root, proof) = - prepare_parachain_heads_proof(vec![(ParaId(1), head_data(1, i))]); + let (state_root, proof) = prepare_parachain_heads_proof(vec![(1, head_data(1, i))]); if i == 0 { initialize(state_root); } else { @@ -554,7 +585,7 @@ mod tests { // import next relay chain header and next parachain head let (state_root, proof) = - prepare_parachain_heads_proof(vec![(ParaId(1), head_data(1, heads_to_keep))]); + prepare_parachain_heads_proof(vec![(1, head_data(1, heads_to_keep))]); proceed(heads_to_keep, state_root); assert_ok!(import_parachain_1_head(heads_to_keep, state_root, proof)); @@ -571,7 +602,7 @@ mod tests { #[test] fn fails_on_unknown_relay_chain_block() { - let (state_root, proof) = prepare_parachain_heads_proof(vec![(ParaId(1), head_data(1, 5))]); + let (state_root, proof) = prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); run_test(|| { // start with relay block #0 initialize(state_root); @@ -586,8 +617,7 @@ mod tests { #[test] fn fails_on_invalid_storage_proof() { - let (_state_root, proof) = - prepare_parachain_heads_proof(vec![(ParaId(1), head_data(1, 5))]); + let (_state_root, proof) = prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); run_test(|| { // start with relay block #0 initialize(Default::default()); diff --git a/bridges/primitives/chain-millau/src/lib.rs b/bridges/primitives/chain-millau/src/lib.rs index 79c0e63628116..d285283448c85 100644 --- a/bridges/primitives/chain-millau/src/lib.rs +++ b/bridges/primitives/chain-millau/src/lib.rs @@ -241,6 +241,21 @@ pub fn derive_account_from_rialto_id(id: bp_runtime::SourceAccount) - AccountIdConverter::convert(encoded_id) } +/// We use this to get the account on Millau (target) which is derived from RialtoParachain's +/// (source) account. We do this so we can fund the derived account on Millau at Genesis to it can +/// pay transaction fees. +/// +/// The reason we can use the same `AccountId` type for both chains is because they share the same +/// development seed phrase. +/// +/// Note that this should only be used for testing. +pub fn derive_account_from_rialto_parachain_id( + id: bp_runtime::SourceAccount, +) -> AccountId { + let encoded_id = bp_runtime::derive_account_id(bp_runtime::RIALTO_PARACHAIN_CHAIN_ID, id); + AccountIdConverter::convert(encoded_id) +} + frame_support::parameter_types! { pub BlockLength: limits::BlockLength = limits::BlockLength::max_with_normal_ratio(2 * 1024 * 1024, NORMAL_DISPATCH_RATIO); diff --git a/bridges/primitives/chain-rialto-parachain/src/lib.rs b/bridges/primitives/chain-rialto-parachain/src/lib.rs index f3f449c7af3e1..0ea849f6fed44 100644 --- a/bridges/primitives/chain-rialto-parachain/src/lib.rs +++ b/bridges/primitives/chain-rialto-parachain/src/lib.rs @@ -18,19 +18,32 @@ // RuntimeApi generated functions #![allow(clippy::too_many_arguments)] +use bp_messages::{LaneId, MessageDetails, MessageNonce}; use bp_runtime::Chain; use frame_support::{ weights::{constants::WEIGHT_PER_SECOND, DispatchClass, IdentityFee, Weight}, - RuntimeDebug, + Parameter, RuntimeDebug, }; use frame_system::limits; use sp_core::Hasher as HasherT; use sp_runtime::{ - traits::{BlakeTwo256, IdentifyAccount, Verify}, - MultiSignature, MultiSigner, Perbill, + traits::{BlakeTwo256, Convert, IdentifyAccount, Verify}, + FixedU128, MultiSignature, MultiSigner, Perbill, }; +use sp_std::vec::Vec; -/// Maximal weight of single Rialto parachain block. +/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at +/// RialtoParachain chain. This mostly depends on number of entries (and their density) in the +/// storage trie. Some reserve is reserved to account future chain growth. +pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; + +/// Can be computed by subtracting encoded call size from raw transaction size. +pub const TX_EXTRA_BYTES: u32 = 104; + +/// Maximal size (in bytes) of encoded (using `Encode::encode()`) account id. +pub const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = 32; + +/// Maximal weight of single RialtoParachain block. /// /// This represents two seconds of compute assuming a target block time of six seconds. pub const MAXIMUM_BLOCK_WEIGHT: Weight = 2 * WEIGHT_PER_SECOND; @@ -42,6 +55,44 @@ pub const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10); /// Represents the portion of a block that will be used by Normal extrinsics. pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); +/// Maximal number of unrewarded relayer entries in Rialto confirmation transaction. +pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 1024; + +/// Maximal number of unconfirmed messages in Rialto confirmation transaction. +pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1024; + +/// Weight of single regular message delivery transaction on RialtoParachain 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. +pub const DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT: Weight = 1_500_000_000; + +/// Increase of delivery transaction weight on RialtoParachain 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. +pub const ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT: Weight = 25_000; + +/// Maximal weight of single message delivery confirmation transaction on RialtoParachain 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. +pub const MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT: Weight = 2_000_000_000; + +/// Weight of pay-dispatch-fee operation for inbound messages at Rialto 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. +pub const PAY_INBOUND_DISPATCH_FEE_WEIGHT: Weight = 600_000_000; + /// Block number type used in Rialto. pub type BlockNumber = u32; @@ -103,6 +154,15 @@ impl Chain for RialtoParachain { } } +/// Convert a 256-bit hash into an AccountId. +pub struct AccountIdConverter; + +impl Convert for AccountIdConverter { + fn convert(hash: sp_core::H256) -> AccountId { + hash.to_fixed_bytes().into() + } +} + frame_support::parameter_types! { pub BlockLength: limits::BlockLength = limits::BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); @@ -122,3 +182,93 @@ frame_support::parameter_types! { .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) .build_or_panic(); } + +/// Name of the With-Rialto-Parachain messages pallet instance that is deployed at bridged chains. +pub const WITH_RIALTO_PARACHAIN_MESSAGES_PALLET_NAME: &str = "BridgeRialtoParachainMessages"; + +/// Name of the Millau->Rialto (actually KSM->DOT) conversion rate stored in the Rialto parachain +/// runtime. +pub const MILLAU_TO_RIALTO_CONVERSION_RATE_PARAMETER_NAME: &str = "MillauToRialtoConversionRate"; + +/// Name of the `RialtoParachainFinalityApi::best_finalized` runtime method. +pub const BEST_FINALIZED_RIALTO_PARACHAIN_HEADER_METHOD: &str = + "RialtoParachainFinalityApi_best_finalized"; + +/// Name of the `ToRialtoParachainOutboundLaneApi::estimate_message_delivery_and_dispatch_fee` +/// runtime method. +pub const TO_RIALTO_PARACHAIN_ESTIMATE_MESSAGE_FEE_METHOD: &str = + "ToRialtoParachainOutboundLaneApi_estimate_message_delivery_and_dispatch_fee"; +/// Name of the `ToRialtoParachainOutboundLaneApi::message_details` runtime method. +pub const TO_RIALTO_PARACHAIN_MESSAGE_DETAILS_METHOD: &str = + "ToRialtoParachainOutboundLaneApi_message_details"; + +// We use this to get the account on RialtoParachain (target) which is derived from Millau's +// (source) account. We do this so we can fund the derived account on RialtoParachain at Genesis to +// it can pay transaction fees. +// +// The reason we can use the same `AccountId` type for both chains is because they share the same +// development seed phrase. +// +// Note that this should only be used for testing. +pub fn derive_account_from_millau_id(id: bp_runtime::SourceAccount) -> AccountId { + let encoded_id = bp_runtime::derive_account_id(bp_runtime::MILLAU_CHAIN_ID, id); + AccountIdConverter::convert(encoded_id) +} + +sp_api::decl_runtime_apis! { + /// API for querying information about the finalized RialtoParachain headers. + /// + /// This API is implemented by runtimes that are bridging with the RialtoParachain chain, not the + /// RialtoParachain runtime itself. + pub trait RialtoParachainFinalityApi { + /// Returns number and hash of the best finalized header known to the bridge module. + fn best_finalized() -> (BlockNumber, Hash); + } + + /// Outbound message lane API for messages that are sent to RialtoParachain chain. + /// + /// This API is implemented by runtimes that are sending messages to RialtoParachain chain, not the + /// RialtoParachain runtime itself. + pub trait ToRialtoParachainOutboundLaneApi { + /// Estimate message delivery and dispatch fee that needs to be paid by the sender on + /// this chain. + /// + /// Returns `None` if message is too expensive to be sent to RialtoParachain from this chain. + /// + /// Please keep in mind that this method returns the lowest message fee required for message + /// to be accepted to the lane. It may be good idea to pay a bit over this price to account + /// future exchange rate changes and guarantee that relayer would deliver your message + /// to the target chain. + fn estimate_message_delivery_and_dispatch_fee( + lane_id: LaneId, + payload: OutboundPayload, + rialto_parachain_to_this_conversion_rate: Option, + ) -> Option; + /// Returns dispatch weight, encoded payload size and delivery+dispatch fee of all + /// messages in given inclusive range. + /// + /// If some (or all) messages are missing from the storage, they'll also will + /// be missing from the resulting vector. The vector is ordered by the nonce. + fn message_details( + lane: LaneId, + begin: MessageNonce, + end: MessageNonce, + ) -> Vec>; + } +} + +#[cfg(test)] +mod tests { + use super::*; + use sp_runtime::codec::Encode; + + #[test] + fn maximal_account_size_does_not_overflow_constant() { + assert!( + MAXIMAL_ENCODED_ACCOUNT_ID_SIZE as usize >= AccountId::from([0u8; 32]).encode().len(), + "Actual maximal size of encoded AccountId ({}) overflows expected ({})", + AccountId::from([0u8; 32]).encode().len(), + MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, + ); + } +} diff --git a/bridges/primitives/chain-rialto/src/lib.rs b/bridges/primitives/chain-rialto/src/lib.rs index 4bf20489bc851..56ef63f13333f 100644 --- a/bridges/primitives/chain-rialto/src/lib.rs +++ b/bridges/primitives/chain-rialto/src/lib.rs @@ -252,7 +252,7 @@ sp_api::decl_runtime_apis! { /// API for querying information about the finalized Rialto headers. /// /// This API is implemented by runtimes that are bridging with the Rialto chain, not the - /// Millau runtime itself. + /// Rialto runtime itself. pub trait RialtoFinalityApi { /// Returns number and hash of the best finalized header known to the bridge module. fn best_finalized() -> (BlockNumber, Hash); diff --git a/bridges/primitives/polkadot-core/src/parachains.rs b/bridges/primitives/polkadot-core/src/parachains.rs index 7f8b20067758c..8980c46c6ed21 100644 --- a/bridges/primitives/polkadot-core/src/parachains.rs +++ b/bridges/primitives/polkadot-core/src/parachains.rs @@ -55,6 +55,12 @@ use parity_util_mem::MallocSizeOf; )] pub struct ParaId(pub u32); +impl From for ParaId { + fn from(id: u32) -> Self { + ParaId(id) + } +} + /// Parachain head. /// /// This is an equivalent of the `polkadot_parachain::HeadData`. @@ -78,5 +84,8 @@ impl ParaHead { /// Parachain head hash. pub type ParaHash = crate::Hash; +/// Parachain head hasher. +pub type ParaHasher = crate::Hasher; + /// Raw storage proof of parachain heads, stored in polkadot-like chain runtime. pub type ParaHeadsProof = Vec>; diff --git a/bridges/primitives/runtime/src/lib.rs b/bridges/primitives/runtime/src/lib.rs index da6f376ae4f49..cd3ea448e1d55 100644 --- a/bridges/primitives/runtime/src/lib.rs +++ b/bridges/primitives/runtime/src/lib.rs @@ -45,6 +45,9 @@ pub const NO_INSTANCE_ID: ChainId = [0, 0, 0, 0]; /// Bridge-with-Rialto instance id. pub const RIALTO_CHAIN_ID: ChainId = *b"rlto"; +/// Bridge-with-RialtoParachain instance id. +pub const RIALTO_PARACHAIN_CHAIN_ID: ChainId = *b"rlpa"; + /// Bridge-with-Millau instance id. pub const MILLAU_CHAIN_ID: ChainId = *b"mlau"; diff --git a/bridges/relays/bin-substrate/Cargo.toml b/bridges/relays/bin-substrate/Cargo.toml index 6ea4bacb191c9..18cf0cae4a6cc 100644 --- a/bridges/relays/bin-substrate/Cargo.toml +++ b/bridges/relays/bin-substrate/Cargo.toml @@ -26,6 +26,7 @@ bp-header-chain = { path = "../../primitives/header-chain" } bp-kusama = { path = "../../primitives/chain-kusama" } bp-messages = { path = "../../primitives/messages" } bp-millau = { path = "../../primitives/chain-millau" } +bp-parachains = { path = "../../primitives/parachains" } bp-polkadot = { path = "../../primitives/chain-polkadot" } bp-polkadot-core = { path = "../../primitives/polkadot-core" } bp-rialto = { path = "../../primitives/chain-rialto" } diff --git a/bridges/relays/bin-substrate/src/chains/millau_headers_to_rialto_parachain.rs b/bridges/relays/bin-substrate/src/chains/millau_headers_to_rialto_parachain.rs new file mode 100644 index 0000000000000..d44369de01a32 --- /dev/null +++ b/bridges/relays/bin-substrate/src/chains/millau_headers_to_rialto_parachain.rs @@ -0,0 +1,57 @@ +// 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 +// (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 . + +//! Millau-to-RialtoParachain headers sync entrypoint. + +// 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 +// (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 . + +//! Millau-to-RialtoParachain headers sync entrypoint. + +use substrate_relay_helper::finality::{ + engine::Grandpa as GrandpaFinalityEngine, DirectSubmitGrandpaFinalityProofCallBuilder, + SubstrateFinalitySyncPipeline, +}; + +/// Description of Millau -> Rialto finalized headers bridge. +#[derive(Clone, Debug)] +pub struct MillauFinalityToRialtoParachain; + +impl SubstrateFinalitySyncPipeline for MillauFinalityToRialtoParachain { + type SourceChain = relay_millau_client::Millau; + type TargetChain = relay_rialto_parachain_client::RialtoParachain; + + type FinalityEngine = GrandpaFinalityEngine; + type SubmitFinalityProofCallBuilder = DirectSubmitGrandpaFinalityProofCallBuilder< + Self, + rialto_parachain_runtime::Runtime, + rialto_parachain_runtime::MillauGrandpaInstance, + >; + type TransactionSignScheme = relay_rialto_parachain_client::RialtoParachain; +} diff --git a/bridges/relays/bin-substrate/src/chains/millau_messages_to_rialto_parachain.rs b/bridges/relays/bin-substrate/src/chains/millau_messages_to_rialto_parachain.rs new file mode 100644 index 0000000000000..f12fd1de89ec4 --- /dev/null +++ b/bridges/relays/bin-substrate/src/chains/millau_messages_to_rialto_parachain.rs @@ -0,0 +1,62 @@ +// 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 +// (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 . + +//! Millau-to-RialtoParachain messages sync entrypoint. + +use messages_relay::relay_strategy::MixStrategy; +use relay_millau_client::Millau; +use relay_rialto_parachain_client::RialtoParachain; +use substrate_relay_helper::messages_lane::{ + DirectReceiveMessagesDeliveryProofCallBuilder, DirectReceiveMessagesProofCallBuilder, + SubstrateMessageLane, +}; + +/// Description of Millau -> RialtoParachain messages bridge. +#[derive(Clone, Debug)] +pub struct MillauMessagesToRialtoParachain; + +impl SubstrateMessageLane for MillauMessagesToRialtoParachain { + const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = + Some(bp_rialto_parachain::MILLAU_TO_RIALTO_CONVERSION_RATE_PARAMETER_NAME); + const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = + Some(bp_millau::RIALTO_TO_MILLAU_CONVERSION_RATE_PARAMETER_NAME); + + const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None; + const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None; + const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None; + const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None; + + type SourceChain = Millau; + type TargetChain = RialtoParachain; + + type SourceTransactionSignScheme = Millau; + type TargetTransactionSignScheme = RialtoParachain; + + type ReceiveMessagesProofCallBuilder = DirectReceiveMessagesProofCallBuilder< + Self, + rialto_parachain_runtime::Runtime, + rialto_parachain_runtime::WithMillauMessagesInstance, + >; + type ReceiveMessagesDeliveryProofCallBuilder = DirectReceiveMessagesDeliveryProofCallBuilder< + Self, + millau_runtime::Runtime, + millau_runtime::WithRialtoParachainMessagesInstance, + >; + + type TargetToSourceChainConversionRateUpdateBuilder = (); + + type RelayStrategy = MixStrategy; +} diff --git a/bridges/relays/bin-substrate/src/chains/mod.rs b/bridges/relays/bin-substrate/src/chains/mod.rs index 7659fc4897633..ccc1be434f579 100644 --- a/bridges/relays/bin-substrate/src/chains/mod.rs +++ b/bridges/relays/bin-substrate/src/chains/mod.rs @@ -19,11 +19,14 @@ pub mod kusama_headers_to_polkadot; pub mod kusama_messages_to_polkadot; pub mod millau_headers_to_rialto; +pub mod millau_headers_to_rialto_parachain; pub mod millau_messages_to_rialto; +pub mod millau_messages_to_rialto_parachain; pub mod polkadot_headers_to_kusama; pub mod polkadot_messages_to_kusama; pub mod rialto_headers_to_millau; pub mod rialto_messages_to_millau; +pub mod rialto_parachain_messages_to_millau; pub mod rialto_parachains_to_millau; pub mod rococo_headers_to_wococo; pub mod rococo_messages_to_wococo; diff --git a/bridges/relays/bin-substrate/src/chains/rialto_parachain.rs b/bridges/relays/bin-substrate/src/chains/rialto_parachain.rs index 8e87c3ede706f..63322351d6d0e 100644 --- a/bridges/relays/bin-substrate/src/chains/rialto_parachain.rs +++ b/bridges/relays/bin-substrate/src/chains/rialto_parachain.rs @@ -16,10 +16,42 @@ //! Rialto parachain specification for CLI. -use crate::cli::CliChain; +use crate::cli::{ + bridge, + encode_message::{CliEncodeMessage, RawMessage}, + CliChain, +}; +use bp_messages::LaneId; +use bp_runtime::EncodedOrDecodedCall; use relay_rialto_parachain_client::RialtoParachain; +use relay_substrate_client::BalanceOf; use sp_version::RuntimeVersion; +impl CliEncodeMessage for RialtoParachain { + fn encode_send_message_call( + lane: LaneId, + payload: RawMessage, + fee: BalanceOf, + bridge_instance_index: u8, + ) -> anyhow::Result> { + Ok(match bridge_instance_index { + bridge::RIALTO_PARACHAIN_TO_MILLAU_INDEX => + rialto_parachain_runtime::Call::BridgeMillauMessages( + rialto_parachain_runtime::MessagesCall::send_message { + lane_id: lane, + payload, + delivery_and_dispatch_fee: fee, + }, + ) + .into(), + _ => anyhow::bail!( + "Unsupported target bridge pallet with instance index: {}", + bridge_instance_index + ), + }) + } +} + impl CliChain for RialtoParachain { const RUNTIME_VERSION: RuntimeVersion = rialto_parachain_runtime::VERSION; diff --git a/bridges/relays/bin-substrate/src/chains/rialto_parachain_messages_to_millau.rs b/bridges/relays/bin-substrate/src/chains/rialto_parachain_messages_to_millau.rs new file mode 100644 index 0000000000000..e1fef8caf0a36 --- /dev/null +++ b/bridges/relays/bin-substrate/src/chains/rialto_parachain_messages_to_millau.rs @@ -0,0 +1,62 @@ +// 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 +// (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 . + +//! RialtoParachain-to-Millau messages sync entrypoint. + +use messages_relay::relay_strategy::MixStrategy; +use relay_millau_client::Millau; +use relay_rialto_parachain_client::RialtoParachain; +use substrate_relay_helper::messages_lane::{ + DirectReceiveMessagesDeliveryProofCallBuilder, DirectReceiveMessagesProofCallBuilder, + SubstrateMessageLane, +}; + +/// Description of RialtoParachain -> Millau messages bridge. +#[derive(Clone, Debug)] +pub struct RialtoParachainMessagesToMillau; + +impl SubstrateMessageLane for RialtoParachainMessagesToMillau { + const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = + Some(bp_millau::RIALTO_TO_MILLAU_CONVERSION_RATE_PARAMETER_NAME); + const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = + Some(bp_rialto_parachain::MILLAU_TO_RIALTO_CONVERSION_RATE_PARAMETER_NAME); + + const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None; + const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None; + const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None; + const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None; + + type SourceChain = RialtoParachain; + type TargetChain = Millau; + + type SourceTransactionSignScheme = RialtoParachain; + type TargetTransactionSignScheme = Millau; + + type ReceiveMessagesProofCallBuilder = DirectReceiveMessagesProofCallBuilder< + Self, + millau_runtime::Runtime, + millau_runtime::WithRialtoParachainMessagesInstance, + >; + type ReceiveMessagesDeliveryProofCallBuilder = DirectReceiveMessagesDeliveryProofCallBuilder< + Self, + rialto_parachain_runtime::Runtime, + rialto_parachain_runtime::WithMillauMessagesInstance, + >; + + type TargetToSourceChainConversionRateUpdateBuilder = (); + + type RelayStrategy = MixStrategy; +} diff --git a/bridges/relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs b/bridges/relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs index 492f1fdf16d01..dddf73902975a 100644 --- a/bridges/relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs +++ b/bridges/relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs @@ -23,17 +23,17 @@ use substrate_relay_helper::parachains_target::DirectSubmitParachainHeadsCallBui /// Rialto-to-Millau parachains sync description. #[derive(Clone, Debug)] -pub struct RialtoToMillauParachains; +pub struct RialtoParachainsToMillau; -impl ParachainsPipeline for RialtoToMillauParachains { +impl ParachainsPipeline for RialtoParachainsToMillau { type SourceChain = Rialto; type TargetChain = Millau; } /// `submit_parachain_heads` call builder for Rialto-to-Millau parachains sync pipeline. -pub type RialtoToMillauParachainsSubmitParachainHeadsCallBuilder = +pub type RialtoParachainsToMillauSubmitParachainHeadsCallBuilder = DirectSubmitParachainHeadsCallBuilder< - RialtoToMillauParachains, + RialtoParachainsToMillau, millau_runtime::Runtime, millau_runtime::WitRialtoParachainsInstance, >; diff --git a/bridges/relays/bin-substrate/src/cli/bridge.rs b/bridges/relays/bin-substrate/src/cli/bridge.rs index c8476725fff2f..db6c61381f484 100644 --- a/bridges/relays/bin-substrate/src/cli/bridge.rs +++ b/bridges/relays/bin-substrate/src/cli/bridge.rs @@ -26,6 +26,8 @@ pub enum FullBridge { WococoToRococo, KusamaToPolkadot, PolkadotToKusama, + MillauToRialtoParachain, + RialtoParachainToMillau, } impl FullBridge { @@ -38,6 +40,8 @@ impl FullBridge { Self::WococoToRococo => WOCOCO_TO_ROCOCO_INDEX, Self::KusamaToPolkadot => KUSAMA_TO_POLKADOT_INDEX, Self::PolkadotToKusama => POLKADOT_TO_KUSAMA_INDEX, + Self::MillauToRialtoParachain => MILLAU_TO_RIALTO_PARACHAIN_INDEX, + Self::RialtoParachainToMillau => RIALTO_PARACHAIN_TO_MILLAU_INDEX, } } } @@ -48,6 +52,8 @@ pub const ROCOCO_TO_WOCOCO_INDEX: u8 = 0; pub const WOCOCO_TO_ROCOCO_INDEX: u8 = 0; pub const KUSAMA_TO_POLKADOT_INDEX: u8 = 0; pub const POLKADOT_TO_KUSAMA_INDEX: u8 = 0; +pub const MILLAU_TO_RIALTO_PARACHAIN_INDEX: u8 = 1; +pub const RIALTO_PARACHAIN_TO_MILLAU_INDEX: u8 = 0; /// The macro allows executing bridge-specific code without going fully generic. /// @@ -171,6 +177,44 @@ macro_rules! select_full_bridge { $generic }, + FullBridge::MillauToRialtoParachain => { + type Source = relay_millau_client::Millau; + #[allow(dead_code)] + type Target = relay_rialto_parachain_client::RialtoParachain; + + // Derive-account + #[allow(unused_imports)] + use bp_rialto_parachain::derive_account_from_millau_id as derive_account; + + // Relay-messages + #[allow(unused_imports)] + use crate::chains::millau_messages_to_rialto_parachain::MillauMessagesToRialtoParachain as MessagesLane; + + // Send-message / Estimate-fee + #[allow(unused_imports)] + use bp_rialto_parachain::TO_RIALTO_PARACHAIN_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD; + + $generic + } + FullBridge::RialtoParachainToMillau => { + type Source = relay_rialto_parachain_client::RialtoParachain; + #[allow(dead_code)] + type Target = relay_millau_client::Millau; + + // Derive-account + #[allow(unused_imports)] + use bp_millau::derive_account_from_rialto_parachain_id as derive_account; + + // Relay-messages + #[allow(unused_imports)] + use crate::chains::rialto_parachain_messages_to_millau::RialtoParachainMessagesToMillau as MessagesLane; + + // Send-message / Estimate-fee + #[allow(unused_imports)] + use bp_millau::TO_MILLAU_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD; + + $generic + } } }; } diff --git a/bridges/relays/bin-substrate/src/cli/init_bridge.rs b/bridges/relays/bin-substrate/src/cli/init_bridge.rs index add80c58538dd..63252382ec369 100644 --- a/bridges/relays/bin-substrate/src/cli/init_bridge.rs +++ b/bridges/relays/bin-substrate/src/cli/init_bridge.rs @@ -49,6 +49,7 @@ pub enum InitBridgeName { WococoToRococo, KusamaToPolkadot, PolkadotToKusama, + MillauToRialtoParachain, } macro_rules! select_bridge { @@ -181,6 +182,28 @@ macro_rules! select_bridge { ) } + $generic + }, + InitBridgeName::MillauToRialtoParachain => { + type Source = relay_millau_client::Millau; + type Target = relay_rialto_parachain_client::RialtoParachain; + type Engine = GrandpaFinalityEngine; + + fn encode_init_bridge( + init_data: InitializationData<::Header>, + ) -> ::Call { + let initialize_call = rialto_parachain_runtime::BridgeGrandpaCall::< + rialto_parachain_runtime::Runtime, + rialto_parachain_runtime::MillauGrandpaInstance, + >::initialize { + init_data, + }; + rialto_parachain_runtime::SudoCall::sudo { + call: Box::new(initialize_call.into()), + } + .into() + } + $generic }, } diff --git a/bridges/relays/bin-substrate/src/cli/relay_headers.rs b/bridges/relays/bin-substrate/src/cli/relay_headers.rs index 3a353ed4ab03c..59ab3a1a33e28 100644 --- a/bridges/relays/bin-substrate/src/cli/relay_headers.rs +++ b/bridges/relays/bin-substrate/src/cli/relay_headers.rs @@ -55,6 +55,7 @@ pub enum RelayHeadersBridge { WococoToRococo, KusamaToPolkadot, PolkadotToKusama, + MillauToRialtoParachain, } macro_rules! select_bridge { @@ -109,6 +110,14 @@ macro_rules! select_bridge { $generic }, + RelayHeadersBridge::MillauToRialtoParachain => { + type Source = relay_millau_client::Millau; + type Target = relay_rialto_parachain_client::RialtoParachain; + type Finality = crate::chains::millau_headers_to_rialto_parachain::MillauFinalityToRialtoParachain; + + $generic + + }, } }; } diff --git a/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs b/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs index 537ae1e101864..1b6bc41301d00 100644 --- a/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs +++ b/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs @@ -480,7 +480,6 @@ impl RelayHeadersAndMessages { right_to_left_transaction_params, params.shared.only_mandatory_headers, ); - // Need 2x capacity since we consider both directions for each lane let mut message_relays = Vec::with_capacity(lanes.len() * 2); for lane in lanes { diff --git a/bridges/relays/bin-substrate/src/cli/relay_parachains.rs b/bridges/relays/bin-substrate/src/cli/relay_parachains.rs index f1af5b5c95873..23ff1da642086 100644 --- a/bridges/relays/bin-substrate/src/cli/relay_parachains.rs +++ b/bridges/relays/bin-substrate/src/cli/relay_parachains.rs @@ -55,8 +55,8 @@ macro_rules! select_bridge { match $bridge { RelayParachainsBridge::RialtoToMillau => { use crate::chains::rialto_parachains_to_millau::{ - RialtoToMillauParachains as Pipeline, - RialtoToMillauParachainsSubmitParachainHeadsCallBuilder as SubmitParachainHeadsCallBuilder, + RialtoParachainsToMillau as Pipeline, + RialtoParachainsToMillauSubmitParachainHeadsCallBuilder as SubmitParachainHeadsCallBuilder, }; use bp_millau::BRIDGE_PARAS_PALLET_NAME as BRIDGE_PARAS_PALLET_NAME_AT_TARGET; diff --git a/bridges/relays/client-rialto-parachain/Cargo.toml b/bridges/relays/client-rialto-parachain/Cargo.toml index ebc2856064318..190f3b0f7e317 100644 --- a/bridges/relays/client-rialto-parachain/Cargo.toml +++ b/bridges/relays/client-rialto-parachain/Cargo.toml @@ -6,12 +6,14 @@ edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0" } relay-substrate-client = { path = "../client-substrate" } relay-utils = { path = "../utils" } # Bridge dependencies -bp-rialto = { path = "../../primitives/chain-rialto" } +bp-messages = { path = "../../primitives/messages" } +bp-rialto-parachain = { path = "../../primitives/chain-rialto-parachain" } rialto-parachain-runtime = { path = "../../bin/rialto-parachain/runtime" } # Substrate Dependencies @@ -19,3 +21,5 @@ rialto-parachain-runtime = { path = "../../bin/rialto-parachain/runtime" } 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-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/bridges/relays/client-rialto-parachain/src/lib.rs b/bridges/relays/client-rialto-parachain/src/lib.rs index 65bf46f660cc7..f70815c03ef8b 100644 --- a/bridges/relays/client-rialto-parachain/src/lib.rs +++ b/bridges/relays/client-rialto-parachain/src/lib.rs @@ -16,8 +16,15 @@ //! Types used to connect to the Rialto-Substrate chain. +use bp_messages::MessageNonce; +use codec::Encode; use frame_support::weights::Weight; -use relay_substrate_client::{Chain, ChainBase}; +use relay_substrate_client::{ + Chain, ChainBase, ChainWithBalances, ChainWithMessages, Error as SubstrateError, SignParam, + TransactionSignScheme, UnsignedTransaction, +}; +use sp_core::{storage::StorageKey, Pair}; +use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; use std::time::Duration; /// Rialto header id. @@ -40,11 +47,11 @@ impl ChainBase for RialtoParachain { type Signature = rialto_parachain_runtime::Signature; fn max_extrinsic_size() -> u32 { - bp_rialto::Rialto::max_extrinsic_size() + bp_rialto_parachain::RialtoParachain::max_extrinsic_size() } fn max_extrinsic_weight() -> Weight { - bp_rialto::Rialto::max_extrinsic_weight() + bp_rialto_parachain::RialtoParachain::max_extrinsic_weight() } } @@ -52,12 +59,111 @@ impl Chain for RialtoParachain { const NAME: &'static str = "RialtoParachain"; const TOKEN_ID: Option<&'static str> = None; // should be fixed/changed in https://github.com/paritytech/parity-bridges-common/pull/1199 - const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = ""; + // should be removed in https://github.com/paritytech/parity-bridges-common/issues/1246 + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_rialto_parachain::BEST_FINALIZED_RIALTO_PARACHAIN_HEADER_METHOD; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5); - const STORAGE_PROOF_OVERHEAD: u32 = bp_rialto::EXTRA_STORAGE_PROOF_SIZE; - const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_rialto::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE; + const STORAGE_PROOF_OVERHEAD: u32 = bp_rialto_parachain::EXTRA_STORAGE_PROOF_SIZE; + const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = + bp_rialto_parachain::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE; type SignedBlock = rialto_parachain_runtime::SignedBlock; type Call = rialto_parachain_runtime::Call; - type WeightToFee = bp_rialto::WeightToFee; + type WeightToFee = bp_rialto_parachain::WeightToFee; } + +impl ChainWithBalances for RialtoParachain { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + use frame_support::storage::generator::StorageMap; + StorageKey( + frame_system::Account::::storage_map_final_key( + account_id, + ), + ) + } +} + +impl ChainWithMessages for RialtoParachain { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = + bp_rialto_parachain::WITH_RIALTO_PARACHAIN_MESSAGES_PALLET_NAME; + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_rialto_parachain::TO_RIALTO_PARACHAIN_MESSAGE_DETAILS_METHOD; + const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight = + bp_rialto_parachain::PAY_INBOUND_DISPATCH_FEE_WEIGHT; + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = + bp_rialto_parachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = + bp_rialto_parachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + type WeightInfo = (); +} + +impl TransactionSignScheme for RialtoParachain { + type Chain = RialtoParachain; + type AccountKeyPair = sp_core::sr25519::Pair; + type SignedTransaction = rialto_parachain_runtime::UncheckedExtrinsic; + + fn sign_transaction(param: SignParam) -> Result { + let raw_payload = SignedPayload::from_raw( + param.unsigned.call, + ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from( + param.era.frame_era(), + ), + frame_system::CheckNonce::::from( + param.unsigned.nonce, + ), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::< + rialto_parachain_runtime::Runtime, + >::from(param.unsigned.tip), + ), + ( + (), + param.spec_version, + param.transaction_version, + param.genesis_hash, + param.era.signed_payload(param.genesis_hash), + (), + (), + (), + ), + ); + let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); + let signer: sp_runtime::MultiSigner = param.signer.public().into(); + let (call, extra, _) = raw_payload.deconstruct(); + + Ok(rialto_parachain_runtime::UncheckedExtrinsic::new_signed( + call.into_decoded()?, + signer.into_account().into(), + signature.into(), + extra, + )) + } + + fn is_signed(tx: &Self::SignedTransaction) -> bool { + tx.signature.is_some() + } + + fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool { + tx.signature + .as_ref() + .map(|(address, _, _)| { + *address == rialto_parachain_runtime::Address::Id(signer.public().into()) + }) + .unwrap_or(false) + } + + fn parse_transaction(_tx: Self::SignedTransaction) -> Option> { + unimplemented!("TODO") + } +} + +/// RialtoParachain signing params. +pub type SigningParams = sp_core::sr25519::Pair; + +/// RialtoParachain header type used in headers sync. +pub type SyncHeader = relay_substrate_client::SyncHeader; diff --git a/bridges/relays/parachains/src/parachains_loop.rs b/bridges/relays/parachains/src/parachains_loop.rs index 56ceb67c58d20..b3baa217e89ff 100644 --- a/bridges/relays/parachains/src/parachains_loop.rs +++ b/bridges/relays/parachains/src/parachains_loop.rs @@ -298,6 +298,20 @@ fn select_parachains_to_update( where P::SourceChain: Chain, { + log::trace!( + target: "bridge", + "Selecting {} parachains to update at {} (relay block: {:?}):\n\t\ + At {}: {:?}\n\t\ + At {}: {:?}", + P::SourceChain::NAME, + P::TargetChain::NAME, + best_finalized_relay_block, + P::SourceChain::NAME, + heads_at_source, + P::TargetChain::NAME, + heads_at_target, + ); + heads_at_source .into_iter() .zip(heads_at_target.into_iter())