diff --git a/.gitignore b/.gitignore index 0ab08578432..5d10cfa41a4 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ hfuzz_workspace .DS_Store +.cargo .idea .vscode *.iml diff --git a/Cargo.lock b/Cargo.lock index b07b6976fba..1a6ee84f98e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4965,7 +4965,6 @@ dependencies = [ "bitvec", "bp-message-dispatch", "bp-messages", - "bp-rialto", "bp-runtime", "frame-benchmarking", "frame-support", diff --git a/README.md b/README.md index e71563705df..fe568dd2204 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,7 @@ Finally, we can run the message relayers. ```bash ./deployments/local-scripts/relay-messages-millau-to-rialto.sh -./deployments/local-scripts/relay-messages-millau-to-rialto.sh +./deployments/local-scripts/relay-messages-rialto-to-millau.sh ``` You will also see the message lane relayers listening for new messages. diff --git a/bin/millau/node/src/chain_spec.rs b/bin/millau/node/src/chain_spec.rs index 846dfa69302..66ce0e23118 100644 --- a/bin/millau/node/src/chain_spec.rs +++ b/bin/millau/node/src/chain_spec.rs @@ -16,8 +16,8 @@ use bp_millau::derive_account_from_rialto_id; use millau_runtime::{ - AccountId, AuraConfig, BalancesConfig, BridgeWestendGrandpaConfig, GenesisConfig, GrandpaConfig, SessionConfig, - SessionKeys, Signature, SudoConfig, SystemConfig, WASM_BINARY, + AccountId, AuraConfig, BalancesConfig, BridgeRialtoMessagesConfig, BridgeWestendGrandpaConfig, GenesisConfig, + GrandpaConfig, SessionConfig, SessionKeys, Signature, SudoConfig, SystemConfig, WASM_BINARY, }; use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_core::{sr25519, Pair, Public}; @@ -134,9 +134,10 @@ impl Alternative { get_account_id_from_seed::("Ferdie//stash"), get_account_id_from_seed::("George//stash"), get_account_id_from_seed::("Harry//stash"), + get_account_id_from_seed::("RialtoMessagesOwner"), pallet_bridge_messages::Pallet::< millau_runtime::Runtime, - pallet_bridge_messages::DefaultInstance, + millau_runtime::WithRialtoMessagesInstance, >::relayer_fund_account_id(), derive_account_from_rialto_id(bp_runtime::SourceAccount::Account( get_account_id_from_seed::("Alice"), @@ -208,6 +209,10 @@ fn testnet_genesis( owner: Some(get_account_id_from_seed::("George")), ..Default::default() }, + bridge_rialto_messages: BridgeRialtoMessagesConfig { + owner: Some(get_account_id_from_seed::("RialtoMessagesOwner")), + ..Default::default() + }, } } diff --git a/bin/millau/runtime/src/lib.rs b/bin/millau/runtime/src/lib.rs index cc004b86297..9bbdf3a9372 100644 --- a/bin/millau/runtime/src/lib.rs +++ b/bin/millau/runtime/src/lib.rs @@ -366,7 +366,7 @@ parameter_types! { } /// Instance of the messages pallet used to relay messages to/from Rialto chain. -pub type WithRialtoMessagesInstance = pallet_bridge_messages::DefaultInstance; +pub type WithRialtoMessagesInstance = (); impl pallet_bridge_messages::Config for Runtime { type Event = Event; @@ -407,7 +407,7 @@ construct_runtime!( NodeBlock = opaque::Block, UncheckedExtrinsic = UncheckedExtrinsic { - BridgeRialtoMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event}, + BridgeRialtoMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, BridgeDispatch: pallet_bridge_dispatch::{Pallet, Event}, BridgeRialtoGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage}, BridgeWestendGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Config, Storage}, diff --git a/bin/millau/runtime/src/rialto_messages.rs b/bin/millau/runtime/src/rialto_messages.rs index 5b3d08688c8..34b3b231c81 100644 --- a/bin/millau/runtime/src/rialto_messages.rs +++ b/bin/millau/runtime/src/rialto_messages.rs @@ -80,10 +80,10 @@ impl MessageBridge for WithRialtoMessageBridge { const RELAYER_FEE_PERCENT: u32 = 10; const THIS_CHAIN_ID: ChainId = MILLAU_CHAIN_ID; const BRIDGED_CHAIN_ID: ChainId = RIALTO_CHAIN_ID; + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_rialto::WITH_MILLAU_MESSAGES_PALLET_NAME; type ThisChain = Millau; type BridgedChain = Rialto; - type BridgedMessagesInstance = crate::WithRialtoMessagesInstance; fn bridged_balance_to_this_balance(bridged_balance: bp_rialto::Balance) -> bp_millau::Balance { bp_millau::Balance::try_from(RialtoToMillauConversionRate::get().saturating_mul_int(bridged_balance)) diff --git a/bin/rialto/node/src/chain_spec.rs b/bin/rialto/node/src/chain_spec.rs index 99645e14cba..977ee179461 100644 --- a/bin/rialto/node/src/chain_spec.rs +++ b/bin/rialto/node/src/chain_spec.rs @@ -16,8 +16,8 @@ use bp_rialto::derive_account_from_millau_id; use rialto_runtime::{ - AccountId, BabeConfig, BalancesConfig, BridgeKovanConfig, BridgeRialtoPoaConfig, GenesisConfig, GrandpaConfig, - SessionConfig, SessionKeys, Signature, SudoConfig, SystemConfig, WASM_BINARY, + AccountId, BabeConfig, BalancesConfig, BridgeKovanConfig, BridgeMillauMessagesConfig, BridgeRialtoPoaConfig, + GenesisConfig, GrandpaConfig, SessionConfig, SessionKeys, Signature, SudoConfig, SystemConfig, WASM_BINARY, }; use serde_json::json; use sp_consensus_babe::AuthorityId as BabeId; @@ -135,9 +135,10 @@ impl Alternative { get_account_id_from_seed::("Ferdie//stash"), get_account_id_from_seed::("George//stash"), get_account_id_from_seed::("Harry//stash"), + get_account_id_from_seed::("MillauMessagesOwner"), pallet_bridge_messages::Pallet::< rialto_runtime::Runtime, - pallet_bridge_messages::DefaultInstance, + rialto_runtime::WithMillauMessagesInstance, >::relayer_fund_account_id(), derive_account_from_millau_id(bp_runtime::SourceAccount::Account( get_account_id_from_seed::("Alice"), @@ -205,6 +206,10 @@ fn testnet_genesis( .map(|x| (x.0.clone(), x.0.clone(), session_keys(x.1.clone(), x.2.clone()))) .collect::>(), }, + bridge_millau_messages: BridgeMillauMessagesConfig { + owner: Some(get_account_id_from_seed::("MillauMessagesOwner")), + ..Default::default() + }, } } diff --git a/bin/rialto/runtime/src/lib.rs b/bin/rialto/runtime/src/lib.rs index 1a34061592e..2899d18d140 100644 --- a/bin/rialto/runtime/src/lib.rs +++ b/bin/rialto/runtime/src/lib.rs @@ -494,7 +494,7 @@ parameter_types! { } /// Instance of the messages pallet used to relay messages to/from Millau chain. -pub type WithMillauMessagesInstance = pallet_bridge_messages::DefaultInstance; +pub type WithMillauMessagesInstance = (); impl pallet_bridge_messages::Config for Runtime { type Event = Event; @@ -559,7 +559,7 @@ construct_runtime!( // Millau bridge modules. BridgeMillauGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage}, BridgeDispatch: pallet_bridge_dispatch::{Pallet, Event}, - BridgeMillauMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event}, + BridgeMillauMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, } ); @@ -1021,14 +1021,12 @@ impl_runtime_apis! { Self::endow_account(&rialto_public.clone().into_account()); } - let make_millau_message_key = |message_key: MessageKey| storage_keys::message_key::< - ::BridgedMessagesInstance, - >( + let make_millau_message_key = |message_key: MessageKey| storage_keys::message_key( + ::BRIDGED_MESSAGES_PALLET_NAME, &message_key.lane_id, message_key.nonce, ).0; - let make_millau_outbound_lane_data_key = |lane_id| storage_keys::outbound_lane_data_key::< - ::BridgedMessagesInstance, - >( + let make_millau_outbound_lane_data_key = |lane_id| storage_keys::outbound_lane_data_key( + ::BRIDGED_MESSAGES_PALLET_NAME, &lane_id, ).0; @@ -1074,9 +1072,8 @@ impl_runtime_apis! { prepare_message_delivery_proof::( params, - |lane_id| pallet_bridge_messages::storage_keys::inbound_lane_data_key::< - ::BridgedMessagesInstance, - >( + |lane_id| pallet_bridge_messages::storage_keys::inbound_lane_data_key( + ::BRIDGED_MESSAGES_PALLET_NAME, &lane_id, ).0, |state_root| bp_millau::Header::new( diff --git a/bin/rialto/runtime/src/millau_messages.rs b/bin/rialto/runtime/src/millau_messages.rs index ac79d8eca6b..bb24a95f9cc 100644 --- a/bin/rialto/runtime/src/millau_messages.rs +++ b/bin/rialto/runtime/src/millau_messages.rs @@ -80,10 +80,10 @@ impl MessageBridge for WithMillauMessageBridge { const RELAYER_FEE_PERCENT: u32 = 10; const THIS_CHAIN_ID: ChainId = RIALTO_CHAIN_ID; const BRIDGED_CHAIN_ID: ChainId = MILLAU_CHAIN_ID; + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_millau::WITH_RIALTO_MESSAGES_PALLET_NAME; type ThisChain = Rialto; type BridgedChain = Millau; - type BridgedMessagesInstance = crate::WithMillauMessagesInstance; fn bridged_balance_to_this_balance(bridged_balance: bp_millau::Balance) -> bp_rialto::Balance { bp_rialto::Balance::try_from(MillauToRialtoConversionRate::get().saturating_mul_int(bridged_balance)) diff --git a/bin/runtime-common/src/messages.rs b/bin/runtime-common/src/messages.rs index 974920c528c..a7591b2e57e 100644 --- a/bin/runtime-common/src/messages.rs +++ b/bin/runtime-common/src/messages.rs @@ -32,7 +32,7 @@ use bp_runtime::{ }; use codec::{Decode, Encode}; use frame_support::{ - traits::{Currency, ExistenceRequirement, Instance}, + traits::{Currency, ExistenceRequirement}, weights::{Weight, WeightToFeePolynomial}, RuntimeDebug, }; @@ -53,13 +53,15 @@ pub trait MessageBridge { const THIS_CHAIN_ID: ChainId; /// Identifier of the Bridged chain. const BRIDGED_CHAIN_ID: ChainId; + /// Name of the paired messages pallet instance at the Bridged chain. + /// + /// Should be the name that is used in the `construct_runtime!()` macro. + const BRIDGED_MESSAGES_PALLET_NAME: &'static str; /// This chain in context of message bridge. type ThisChain: ThisChainWithMessages; /// Bridged chain in context of message bridge. type BridgedChain: BridgedChainWithMessages; - /// Instance of the `pallet-bridge-messages` pallet at the Bridged chain. - type BridgedMessagesInstance: Instance; /// Convert Bridged chain balance into This chain balance. fn bridged_balance_to_this_balance(bridged_balance: BalanceOf>) -> BalanceOf>; @@ -391,7 +393,7 @@ pub mod source { // Messages delivery proof is just proof of single storage key read => any error // is fatal. let storage_inbound_lane_data_key = - pallet_bridge_messages::storage_keys::inbound_lane_data_key::(&lane); + pallet_bridge_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")? @@ -563,7 +565,6 @@ pub mod target { ) -> Result>>>, &'static str> where ThisRuntime: pallet_bridge_grandpa::Config, - ThisRuntime: pallet_bridge_messages::Config, HashOf>: Into>::BridgedChain>>, { @@ -628,14 +629,15 @@ pub mod target { { fn read_raw_outbound_lane_data(&self, lane_id: &LaneId) -> Option> { let storage_outbound_lane_data_key = - pallet_bridge_messages::storage_keys::outbound_lane_data_key::(lane_id); + pallet_bridge_messages::storage_keys::outbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, lane_id); self.storage .read_value(storage_outbound_lane_data_key.0.as_ref()) .ok()? } fn read_raw_message(&self, message_key: &MessageKey) -> Option> { - let storage_message_key = pallet_bridge_messages::storage_keys::message_key::( + let storage_message_key = pallet_bridge_messages::storage_keys::message_key( + B::BRIDGED_MESSAGES_PALLET_NAME, &message_key.lane_id, message_key.nonce, ); @@ -745,10 +747,10 @@ mod tests { const RELAYER_FEE_PERCENT: u32 = 10; const THIS_CHAIN_ID: ChainId = *b"this"; const BRIDGED_CHAIN_ID: ChainId = *b"brdg"; + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = ""; type ThisChain = ThisChain; type BridgedChain = BridgedChain; - type BridgedMessagesInstance = pallet_bridge_messages::DefaultInstance; fn bridged_balance_to_this_balance(bridged_balance: BridgedChainBalance) -> ThisChainBalance { ThisChainBalance(bridged_balance.0 * BRIDGED_CHAIN_TO_THIS_CHAIN_BALANCE_RATE as u32) @@ -763,10 +765,10 @@ mod tests { const RELAYER_FEE_PERCENT: u32 = 20; const THIS_CHAIN_ID: ChainId = *b"brdg"; const BRIDGED_CHAIN_ID: ChainId = *b"this"; + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = ""; type ThisChain = BridgedChain; type BridgedChain = ThisChain; - type BridgedMessagesInstance = pallet_bridge_messages::DefaultInstance; fn bridged_balance_to_this_balance(_this_balance: ThisChainBalance) -> BridgedChainBalance { unreachable!() diff --git a/bin/runtime-common/src/messages_api.rs b/bin/runtime-common/src/messages_api.rs index d0f4180b5c1..b09a88e6279 100644 --- a/bin/runtime-common/src/messages_api.rs +++ b/bin/runtime-common/src/messages_api.rs @@ -20,7 +20,6 @@ use crate::messages::{source::FromThisChainMessagePayload, MessageBridge}; use bp_messages::{LaneId, MessageDetails, MessageNonce}; use codec::Decode; -use frame_support::traits::Instance; use sp_std::vec::Vec; /// Implementation of the `To*OutboundLaneApi::message_details`. @@ -31,7 +30,7 @@ pub fn outbound_message_details( ) -> Vec> where Runtime: pallet_bridge_messages::Config, - MessagesPalletInstance: Instance, + MessagesPalletInstance: 'static, BridgeConfig: MessageBridge, { (begin..=end) diff --git a/deployments/README.md b/deployments/README.md index bea0873f1b4..f9207e6d683 100644 --- a/deployments/README.md +++ b/deployments/README.md @@ -121,7 +121,9 @@ Following accounts are used when `rialto-millau` bridge is running: - Millau's `Eve` signs relay transactions with message delivery confirmations (lane 00000001) from Rialto to Millau; - Rialto's `Eve` signs relay transactions with messages (lane 00000001) from Millau to Rialto; - Millau's `Ferdie` signs relay transactions with messages (lane 00000001) from Rialto to Millau; -- Rialto's `Ferdie` signs relay transactions with message delivery confirmations (lane 00000001) from Millau to Rialto. +- Rialto's `Ferdie` signs relay transactions with message delivery confirmations (lane 00000001) from Millau to Rialto; +- Millau's `RialtoMessagesOwner` signs relay transactions with updated Rialto -> Millau conversion rate; +- Rialto's `MillauMessagesOwner` signs relay transactions with updated Millau -> Rialto conversion rate. Following accounts are used when `westend-millau` bridge is running: diff --git a/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh b/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh index 067ce76b471..688547535e3 100755 --- a/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh +++ b/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh @@ -34,6 +34,10 @@ LARGE_MESSAGES_TIME=0 # start sending message packs in a hour BUNCH_OF_MESSAGES_TIME=3600 +# give conversion rate updater some time to update Millau->Rialto conversion rate in Rialto +# (initially rate=1 and rational relayer won't deliver any messages if it'll be changed to larger value) +sleep 60 + while true do rand_sleep diff --git a/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-generator-entrypoint.sh b/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-generator-entrypoint.sh index efc0925f2ae..dec79f8e362 100755 --- a/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-generator-entrypoint.sh +++ b/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-generator-entrypoint.sh @@ -34,6 +34,10 @@ LARGE_MESSAGES_TIME=0 # start sending message packs in a hour BUNCH_OF_MESSAGES_TIME=3600 +# give conversion rate updater some time to update Rialto->Millau conversion rate in Millau +# (initially rate=1 and rational relayer won't deliver any messages if it'll be changed to larger value) +sleep 60 + while true do rand_sleep diff --git a/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh b/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh index a5982454770..401e6d4aae9 100755 --- a/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh +++ b/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh @@ -26,8 +26,10 @@ sleep 6 --millau-host millau-node-alice \ --millau-port 9944 \ --millau-signer //Charlie \ + --millau-messages-pallet-owner=//RialtoMessagesOwner\ --rialto-host rialto-node-alice \ --rialto-port 9944 \ --rialto-signer //Charlie \ + --rialto-messages-pallet-owner=//MillauMessagesOwner\ --lane=00000000 \ --prometheus-host=0.0.0.0 diff --git a/modules/currency-exchange/src/benchmarking.rs b/modules/currency-exchange/src/benchmarking.rs index 574ae93f6ee..db8b2256c80 100644 --- a/modules/currency-exchange/src/benchmarking.rs +++ b/modules/currency-exchange/src/benchmarking.rs @@ -18,12 +18,10 @@ //! So we are giving runtime opportunity to prepare environment and construct proof //! before invoking module calls. -use super::{ - Call, Config as CurrencyExchangeConfig, InclusionProofVerifier, Instance, Pallet as CurrencyExchangePallet, -}; +use super::{Call, Config as CurrencyExchangeConfig, InclusionProofVerifier, Pallet as CurrencyExchangePallet}; use sp_std::prelude::*; -use frame_benchmarking::{account, benchmarks_instance}; +use frame_benchmarking::{account, benchmarks_instance_pallet}; use frame_system::RawOrigin; const SEED: u32 = 0; @@ -31,7 +29,7 @@ const WORST_TX_SIZE_FACTOR: u32 = 1000; const WORST_PROOF_SIZE_FACTOR: u32 = 1000; /// Pallet we're benchmarking here. -pub struct Pallet, I: Instance>(CurrencyExchangePallet); +pub struct Pallet, I: 'static>(CurrencyExchangePallet); /// Proof benchmarking parameters. pub struct ProofParams { @@ -48,14 +46,14 @@ pub struct ProofParams { } /// Config that must be implemented by runtime. -pub trait Config: CurrencyExchangeConfig { +pub trait Config: CurrencyExchangeConfig { /// Prepare proof for importing exchange transaction. fn make_proof( proof_params: ProofParams, ) -> <>::PeerBlockchain as InclusionProofVerifier>::TransactionInclusionProof; } -benchmarks_instance! { +benchmarks_instance_pallet! { // Benchmark `import_peer_transaction` extrinsic with the best possible conditions: // * Proof is the transaction itself. // * Transaction has minimal size. diff --git a/modules/currency-exchange/src/lib.rs b/modules/currency-exchange/src/lib.rs index 9a8af5ba501..290e1a9bc95 100644 --- a/modules/currency-exchange/src/lib.rs +++ b/modules/currency-exchange/src/lib.rs @@ -22,8 +22,7 @@ use bp_currency_exchange::{ CurrencyConverter, DepositInto, Error as ExchangeError, MaybeLockFundsTransaction, RecipientsMap, }; use bp_header_chain::InclusionProofVerifier; -use frame_support::{decl_error, decl_module, decl_storage, ensure}; -use sp_runtime::DispatchResult; +use frame_support::ensure; #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; @@ -34,61 +33,53 @@ pub trait OnTransactionSubmitted { fn on_valid_transaction_submitted(submitter: AccountId); } -/// The module configuration trait -pub trait Config: frame_system::Config { - /// Handler for transaction submission result. - type OnTransactionSubmitted: OnTransactionSubmitted; - /// Represents the blockchain that we'll be exchanging currency with. - type PeerBlockchain: InclusionProofVerifier; - /// Peer blockchain transaction parser. - type PeerMaybeLockFundsTransaction: MaybeLockFundsTransaction< - Transaction = ::Transaction, - >; - /// Map between blockchains recipients. - type RecipientsMap: RecipientsMap< - PeerRecipient = ::Recipient, - Recipient = Self::AccountId, - >; - /// This blockchain currency amount type. - type Amount; - /// Converter from peer blockchain currency type into current blockchain currency type. - type CurrencyConverter: CurrencyConverter< - SourceAmount = ::Amount, - TargetAmount = Self::Amount, - >; - /// Something that could grant money. - type DepositInto: DepositInto; -} +pub use pallet::*; -decl_error! { - pub enum Error for Pallet, I: Instance> { - /// Invalid peer blockchain transaction provided. - InvalidTransaction, - /// Peer transaction has invalid amount. - InvalidAmount, - /// Peer transaction has invalid recipient. - InvalidRecipient, - /// Cannot map from peer recipient to this blockchain recipient. - FailedToMapRecipients, - /// Failed to convert from peer blockchain currency to this blockchain currency. - FailedToConvertCurrency, - /// Deposit has failed. - DepositFailed, - /// Deposit has partially failed (changes to recipient account were made). - DepositPartiallyFailed, - /// Transaction is not finalized. - UnfinalizedTransaction, - /// Transaction funds are already claimed. - AlreadyClaimed, +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + /// Handler for transaction submission result. + type OnTransactionSubmitted: OnTransactionSubmitted; + /// Represents the blockchain that we'll be exchanging currency with. + type PeerBlockchain: InclusionProofVerifier; + /// Peer blockchain transaction parser. + type PeerMaybeLockFundsTransaction: MaybeLockFundsTransaction< + Transaction = ::Transaction, + >; + /// Map between blockchains recipients. + type RecipientsMap: RecipientsMap< + PeerRecipient = ::Recipient, + Recipient = Self::AccountId, + >; + /// This blockchain currency amount type. + type Amount; + /// Converter from peer blockchain currency type into current blockchain currency type. + type CurrencyConverter: CurrencyConverter< + SourceAmount = ::Amount, + TargetAmount = Self::Amount, + >; + /// Something that could grant money. + type DepositInto: DepositInto; } -} -decl_module! { - pub struct Module, I: Instance = DefaultInstance> for enum Call where origin: T::Origin { + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(PhantomData<(T, I)>); + + #[pallet::hooks] + impl, I: 'static> Hooks> for Pallet {} + + #[pallet::call] + impl, I: 'static> Pallet { /// Imports lock fund transaction of the peer blockchain. - #[weight = 0] // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) + #[pallet::weight(0)] // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) pub fn import_peer_transaction( - origin, + origin: OriginFor, proof: <>::PeerBlockchain as InclusionProofVerifier>::TransactionInclusionProof, ) -> DispatchResult { let submitter = frame_system::ensure_signed(origin)?; @@ -122,16 +113,41 @@ decl_module! { Ok(()) } } -} -decl_storage! { - trait Store for Pallet, I: Instance = DefaultInstance> as Bridge { - /// All transfers that have already been claimed. - Transfers: map hasher(blake2_128_concat) ::Id => (); + #[pallet::error] + pub enum Error { + /// Invalid peer blockchain transaction provided. + InvalidTransaction, + /// Peer transaction has invalid amount. + InvalidAmount, + /// Peer transaction has invalid recipient. + InvalidRecipient, + /// Cannot map from peer recipient to this blockchain recipient. + FailedToMapRecipients, + /// Failed to convert from peer blockchain currency to this blockchain currency. + FailedToConvertCurrency, + /// Deposit has failed. + DepositFailed, + /// Deposit has partially failed (changes to recipient account were made). + DepositPartiallyFailed, + /// Transaction is not finalized. + UnfinalizedTransaction, + /// Transaction funds are already claimed. + AlreadyClaimed, } + + /// All transfers that have already been claimed. + #[pallet::storage] + pub(super) type Transfers, I: 'static = ()> = StorageMap< + _, + Blake2_128Concat, + ::Id, + (), + ValueQuery, + >; } -impl, I: Instance> Pallet { +impl, I: 'static> Pallet { /// Returns true if currency exchange module is able to import given transaction proof in /// its current state. pub fn filter_transaction_proof( @@ -151,7 +167,7 @@ impl, I: Instance> Pallet { } } -impl, I: Instance> From for Error { +impl, I: 'static> From for Error { fn from(error: ExchangeError) -> Self { match error { ExchangeError::InvalidTransaction => Error::InvalidTransaction, @@ -170,7 +186,7 @@ impl OnTransactionSubmitted for () { } /// Exchange deposit details. -struct DepositDetails, I: Instance> { +struct DepositDetails, I: 'static> { /// Transfer id. pub transfer_id: ::Id, /// Transfer recipient. @@ -181,7 +197,7 @@ struct DepositDetails, I: Instance> { /// Verify and parse transaction proof, preparing everything required for importing /// this transaction proof. -fn prepare_deposit_details, I: Instance>( +fn prepare_deposit_details, I: 'static>( proof: &<>::PeerBlockchain as InclusionProofVerifier>::TransactionInclusionProof, ) -> Result, Error> { // ensure that transaction is included in finalized block that we know of @@ -238,7 +254,7 @@ mod tests { impl OnTransactionSubmitted for DummyTransactionSubmissionHandler { fn on_valid_transaction_submitted(submitter: AccountId) { - Transfers::::insert(submitter, ()); + Transfers::::insert(submitter, ()); } } @@ -394,7 +410,7 @@ mod tests { new_test_ext().execute_with(|| { assert_noop!( Exchange::import_peer_transaction(Origin::signed(SUBMITTER), (false, transaction(0))), - Error::::UnfinalizedTransaction, + Error::::UnfinalizedTransaction, ); }); } @@ -407,7 +423,7 @@ mod tests { Origin::signed(SUBMITTER), (true, transaction(INVALID_TRANSACTION_ID)), ), - Error::::InvalidTransaction, + Error::::InvalidTransaction, ); }); } @@ -421,7 +437,7 @@ mod tests { Origin::signed(SUBMITTER), (true, transaction(ALREADY_CLAIMED_TRANSACTION_ID)), ), - Error::::AlreadyClaimed, + Error::::AlreadyClaimed, ); }); } @@ -433,7 +449,7 @@ mod tests { transaction.recipient = UNKNOWN_RECIPIENT_ID; assert_noop!( Exchange::import_peer_transaction(Origin::signed(SUBMITTER), (true, transaction)), - Error::::FailedToMapRecipients, + Error::::FailedToMapRecipients, ); }); } @@ -445,7 +461,7 @@ mod tests { transaction.amount = INVALID_AMOUNT; assert_noop!( Exchange::import_peer_transaction(Origin::signed(SUBMITTER), (true, transaction)), - Error::::FailedToConvertCurrency, + Error::::FailedToConvertCurrency, ); }); } @@ -457,7 +473,7 @@ mod tests { transaction.amount = MAX_DEPOSIT_AMOUNT + 1; assert_noop!( Exchange::import_peer_transaction(Origin::signed(SUBMITTER), (true, transaction)), - Error::::DepositFailed, + Error::::DepositFailed, ); }); } diff --git a/modules/messages/Cargo.toml b/modules/messages/Cargo.toml index a2febf50af3..0cb14764f3b 100644 --- a/modules/messages/Cargo.toml +++ b/modules/messages/Cargo.toml @@ -17,7 +17,6 @@ serde = { version = "1.0.101", optional = true, features = ["derive"] } bp-message-dispatch = { path = "../../primitives/message-dispatch", default-features = false } bp-messages = { path = "../../primitives/messages", default-features = false } -bp-rialto = { path = "../../primitives/chain-rialto", default-features = false } bp-runtime = { path = "../../primitives/runtime", default-features = false } # Substrate Dependencies @@ -41,7 +40,6 @@ std = [ "bp-message-dispatch/std", "bp-messages/std", "bp-runtime/std", - "bp-rialto/std", "codec/std", "frame-support/std", "frame-system/std", diff --git a/modules/messages/src/benchmarking.rs b/modules/messages/src/benchmarking.rs index e2a3237eb06..4adf073191b 100644 --- a/modules/messages/src/benchmarking.rs +++ b/modules/messages/src/benchmarking.rs @@ -19,7 +19,7 @@ use crate::weights_ext::EXPECTED_DEFAULT_MESSAGE_LENGTH; use crate::{ inbound_lane::InboundLaneStorage, inbound_lane_storage, outbound_lane, outbound_lane::ReceivalConfirmationResult, - Call, Instance, + Call, }; use bp_messages::{ @@ -27,7 +27,7 @@ use bp_messages::{ MessageData, MessageNonce, OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState, }; use bp_runtime::messages::DispatchFeePayment; -use frame_benchmarking::{account, benchmarks_instance}; +use frame_benchmarking::{account, benchmarks_instance_pallet}; use frame_support::{traits::Get, weights::Weight}; use frame_system::RawOrigin; use sp_std::{ @@ -43,7 +43,7 @@ pub const MESSAGE_FEE: u64 = 100_000_000_000; const SEED: u32 = 0; /// Pallet we're benchmarking here. -pub struct Pallet, I: crate::Instance>(crate::Pallet); +pub struct Pallet, I: 'static>(crate::Pallet); /// Proof size requirements. pub enum ProofSize { @@ -91,7 +91,7 @@ pub struct MessageDeliveryProofParams { } /// Trait that must be implemented by runtime. -pub trait Config: crate::Config { +pub trait Config: crate::Config { /// Lane id to use in benchmarks. fn bench_lane_id() -> LaneId { Default::default() @@ -123,7 +123,7 @@ pub trait Config: crate::Config { fn is_message_dispatched(nonce: MessageNonce) -> bool; } -benchmarks_instance! { +benchmarks_instance_pallet! { // // Benchmarks that are used directly by the runtime. // @@ -898,7 +898,7 @@ benchmarks_instance! { } } -fn send_regular_message, I: Instance>() { +fn send_regular_message, I: 'static>() { let mut outbound_lane = outbound_lane::(T::bench_lane_id()); outbound_lane.send_message(MessageData { payload: vec![], @@ -906,7 +906,7 @@ fn send_regular_message, I: Instance>() { }); } -fn send_regular_message_with_payload, I: Instance>(payload: Vec) { +fn send_regular_message_with_payload, I: 'static>(payload: Vec) { let mut outbound_lane = outbound_lane::(T::bench_lane_id()); outbound_lane.send_message(MessageData { payload, @@ -914,7 +914,7 @@ fn send_regular_message_with_payload, I: Instance>(payload: Vec }); } -fn confirm_message_delivery, I: Instance>(nonce: MessageNonce) { +fn confirm_message_delivery, I: 'static>(nonce: MessageNonce) { let mut outbound_lane = outbound_lane::(T::bench_lane_id()); let latest_received_nonce = outbound_lane.data().latest_received_nonce; let mut relayers = VecDeque::with_capacity((nonce - latest_received_nonce) as usize); @@ -930,7 +930,7 @@ fn confirm_message_delivery, I: Instance>(nonce: MessageNonce) { )); } -fn receive_messages, I: Instance>(nonce: MessageNonce) { +fn receive_messages, I: 'static>(nonce: MessageNonce) { let mut inbound_lane_storage = inbound_lane_storage::(T::bench_lane_id()); inbound_lane_storage.set_data(InboundLaneData { relayers: vec![UnrewardedRelayer { @@ -943,7 +943,7 @@ fn receive_messages, I: Instance>(nonce: MessageNonce) { }); } -fn ensure_relayer_rewarded, I: Instance>(relayer_id: &T::AccountId, old_balance: &T::OutboundMessageFee) { +fn ensure_relayer_rewarded, I: 'static>(relayer_id: &T::AccountId, old_balance: &T::OutboundMessageFee) { let new_balance = T::account_balance(relayer_id); assert!( new_balance > *old_balance, diff --git a/modules/messages/src/inbound_lane.rs b/modules/messages/src/inbound_lane.rs index 83d17dc3c06..ae5de1c1521 100644 --- a/modules/messages/src/inbound_lane.rs +++ b/modules/messages/src/inbound_lane.rs @@ -179,11 +179,11 @@ mod tests { dispatch_result, message_data, run_test, unrewarded_relayer, TestMessageDispatch, TestRuntime, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_RELAYER_A, TEST_RELAYER_B, TEST_RELAYER_C, }, - DefaultInstance, RuntimeInboundLaneStorage, + RuntimeInboundLaneStorage, }; fn receive_regular_message( - lane: &mut InboundLane>, + lane: &mut InboundLane>, nonce: MessageNonce, ) { assert_eq!( diff --git a/modules/messages/src/lib.rs b/modules/messages/src/lib.rs index 8f7a6c8b712..7c6bacfb66d 100644 --- a/modules/messages/src/lib.rs +++ b/modules/messages/src/lib.rs @@ -56,15 +56,8 @@ use bp_messages::{ }; use bp_runtime::{ChainId, Size}; use codec::{Decode, Encode}; -use frame_support::{ - decl_error, decl_event, decl_module, decl_storage, - dispatch::DispatchResultWithPostInfo, - ensure, fail, - traits::Get, - weights::{DispatchClass, Pays, PostDispatchInfo, Weight}, - Parameter, StorageMap, -}; -use frame_system::{ensure_signed, RawOrigin}; +use frame_support::{fail, traits::Get, weights::PostDispatchInfo}; +use frame_system::RawOrigin; use num_traits::{SaturatingAdd, Zero}; use sp_runtime::traits::BadOrigin; use sp_std::{cell::RefCell, cmp::PartialOrd, marker::PhantomData, prelude::*}; @@ -82,223 +75,158 @@ pub mod benchmarking; #[cfg(test)] mod mock; -/// The module configuration trait -pub trait Config: frame_system::Config { - // General types - - /// They are overarching event type. - type Event: From> + Into<::Event>; - /// Benchmarks results from runtime we're plugged into. - type WeightInfo: WeightInfoExt; - /// Pallet parameter that is opaque to the pallet itself, but may be used by the runtime - /// for integrating the pallet. - /// - /// All pallet parameters may only be updated either by the root, or by the pallet owner. - type Parameter: MessagesParameter; - - /// Maximal number of messages that may be pruned during maintenance. Maintenance occurs - /// whenever new message is sent. The reason is that if you want to use lane, you should - /// be ready to pay for its maintenance. - type MaxMessagesToPruneAtOnce: Get; - /// Maximal number of unrewarded relayer entries at inbound lane. Unrewarded means that the - /// relayer has delivered messages, but either confirmations haven't been delivered back to the - /// source chain, or we haven't received reward confirmations yet. - /// - /// This constant limits maximal number of entries in the `InboundLaneData::relayers`. Keep - /// in mind that the same relayer account may take several (non-consecutive) entries in this - /// set. - type MaxUnrewardedRelayerEntriesAtInboundLane: Get; - /// Maximal number of unconfirmed messages at inbound lane. Unconfirmed means that the - /// message has been delivered, but either confirmations haven't been delivered back to the - /// source chain, or we haven't received reward confirmations for these messages yet. - /// - /// This constant limits difference between last message from last entry of the - /// `InboundLaneData::relayers` and first message at the first entry. - /// - /// There is no point of making this parameter lesser than MaxUnrewardedRelayerEntriesAtInboundLane, - /// because then maximal number of relayer entries will be limited by maximal number of messages. - /// - /// This value also represents maximal number of messages in single delivery transaction. Transaction - /// that is declaring more messages than this value, will be rejected. Even if these messages are - /// from different lanes. - type MaxUnconfirmedMessagesAtInboundLane: Get; - - /// Payload type of outbound messages. This payload is dispatched on the bridged chain. - type OutboundPayload: Parameter + Size; - /// Message fee type of outbound messages. This fee is paid on this chain. - type OutboundMessageFee: Default + From + PartialOrd + Parameter + SaturatingAdd + Zero; - - /// Payload type of inbound messages. This payload is dispatched on this chain. - type InboundPayload: Decode; - /// Message fee type of inbound messages. This fee is paid on the bridged chain. - type InboundMessageFee: Decode; - /// Identifier of relayer that deliver messages to this chain. Relayer reward is paid on the bridged chain. - type InboundRelayer: Parameter; - - /// A type which can be turned into an AccountId from a 256-bit hash. - /// - /// Used when deriving the shared relayer fund account. - type AccountIdConverter: sp_runtime::traits::Convert; - - // Types that are used by outbound_lane (on source chain). - - /// Target header chain. - type TargetHeaderChain: TargetHeaderChain; - /// Message payload verifier. - type LaneMessageVerifier: LaneMessageVerifier; - /// Message delivery payment. - type MessageDeliveryAndDispatchPayment: MessageDeliveryAndDispatchPayment; - /// Handler for delivered messages. - type OnDeliveryConfirmed: OnDeliveryConfirmed; - - // Types that are used by inbound_lane (on target chain). - - /// Source header chain, as it is represented on target chain. - type SourceHeaderChain: SourceHeaderChain; - /// Message dispatch. - type MessageDispatch: MessageDispatch< - Self::AccountId, - Self::InboundMessageFee, - DispatchPayload = Self::InboundPayload, - >; - - /// Chain Id for the bridged chain. - type BridgedChainId: Get; -} - -/// Shortcut to messages proof type for Config. -type MessagesProofOf = - <>::SourceHeaderChain as SourceHeaderChain<>::InboundMessageFee>>::MessagesProof; -/// Shortcut to messages delivery proof type for Config. -type MessagesDeliveryProofOf = <>::TargetHeaderChain as TargetHeaderChain< - >::OutboundPayload, - ::AccountId, ->>::MessagesDeliveryProof; - -decl_error! { - pub enum Error for Pallet, I: Instance> { - /// All pallet operations are halted. - Halted, - /// Message has been treated as invalid by chain verifier. - MessageRejectedByChainVerifier, - /// Message has been treated as invalid by lane verifier. - MessageRejectedByLaneVerifier, - /// Submitter has failed to pay fee for delivering and dispatching messages. - FailedToWithdrawMessageFee, - /// The transaction brings too many messages. - TooManyMessagesInTheProof, - /// Invalid messages has been submitted. - InvalidMessagesProof, - /// Invalid messages delivery proof has been submitted. - InvalidMessagesDeliveryProof, - /// The bridged chain has invalid `UnrewardedRelayers` in its storage (fatal for the lane). - InvalidUnrewardedRelayers, - /// The relayer has declared invalid unrewarded relayers state in the `receive_messages_delivery_proof` call. - InvalidUnrewardedRelayersState, - /// The message someone is trying to work with (i.e. increase fee) is already-delivered. - MessageIsAlreadyDelivered, - /// The message someone is trying to work with (i.e. increase fee) is not yet sent. - MessageIsNotYetSent, - /// The number of actually confirmed messages is going to be larger than the number of messages in the proof. - /// This may mean that this or bridged chain storage is corrupted. - TryingToConfirmMoreMessagesThanExpected, - } -} +pub use pallet::*; -decl_storage! { - trait Store for Pallet, I: Instance = DefaultInstance> as BridgeMessages { - /// Optional pallet owner. - /// - /// Pallet owner has a right to halt all pallet operations and then resume it. If it is - /// `None`, then there are no direct ways to halt/resume pallet operations, but other - /// runtime methods may still be used to do that (i.e. democracy::referendum to update halt - /// flag directly or call the `halt_operations`). - pub PalletOwner get(fn module_owner): Option; - /// The current operating mode of the pallet. - /// - /// Depending on the mode either all, some, or no transactions will be allowed. - pub PalletOperatingMode get(fn operating_mode) config(): OperatingMode; - /// Map of lane id => inbound lane data. - pub InboundLanes: map hasher(blake2_128_concat) LaneId => InboundLaneData; - /// Map of lane id => outbound lane data. - pub OutboundLanes: map hasher(blake2_128_concat) LaneId => OutboundLaneData; - /// All queued outbound messages. - pub OutboundMessages: map hasher(blake2_128_concat) MessageKey => Option>; - } - add_extra_genesis { - config(phantom): sp_std::marker::PhantomData; - config(owner): Option; - build(|config| { - if let Some(ref owner) = config.owner { - >::put(owner); - } - }) - } -} +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; -decl_event!( - pub enum Event - where - AccountId = ::AccountId, - Parameter = >::Parameter, - { - /// Pallet parameter has been updated. - ParameterUpdated(Parameter), - /// Message has been accepted and is waiting to be delivered. - MessageAccepted(LaneId, MessageNonce), - /// Messages in the inclusive range have been delivered to the bridged chain. - MessagesDelivered(LaneId, DeliveredMessages), - /// Phantom member, never used. - Dummy(PhantomData<(AccountId, I)>), - } -); + #[pallet::config] + pub trait Config: frame_system::Config { + // General types -decl_module! { - pub struct Module, I: Instance = DefaultInstance> for enum Call where origin: T::Origin { - /// Deposit one of this module's events by using the default implementation. - fn deposit_event() = default; + /// The overarching event type. + type Event: From> + IsType<::Event>; + /// Benchmarks results from runtime we're plugged into. + type WeightInfo: WeightInfoExt; /// Gets the chain id value from the instance. - const BridgedChainId: ChainId = T::BridgedChainId::get(); - + #[pallet::constant] + type BridgedChainId: Get; + /// Pallet parameter that is opaque to the pallet itself, but may be used by the runtime + /// for integrating the pallet. + /// + /// All pallet parameters may only be updated either by the root, or by the pallet owner. + type Parameter: MessagesParameter; + + /// Maximal number of messages that may be pruned during maintenance. Maintenance occurs + /// whenever new message is sent. The reason is that if you want to use lane, you should + /// be ready to pay for its maintenance. + type MaxMessagesToPruneAtOnce: Get; + /// Maximal number of unrewarded relayer entries at inbound lane. Unrewarded means that the + /// relayer has delivered messages, but either confirmations haven't been delivered back to the + /// source chain, or we haven't received reward confirmations yet. + /// + /// This constant limits maximal number of entries in the `InboundLaneData::relayers`. Keep + /// in mind that the same relayer account may take several (non-consecutive) entries in this + /// set. + type MaxUnrewardedRelayerEntriesAtInboundLane: Get; + /// Maximal number of unconfirmed messages at inbound lane. Unconfirmed means that the + /// message has been delivered, but either confirmations haven't been delivered back to the + /// source chain, or we haven't received reward confirmations for these messages yet. + /// + /// This constant limits difference between last message from last entry of the + /// `InboundLaneData::relayers` and first message at the first entry. + /// + /// There is no point of making this parameter lesser than MaxUnrewardedRelayerEntriesAtInboundLane, + /// because then maximal number of relayer entries will be limited by maximal number of messages. + /// + /// This value also represents maximal number of messages in single delivery transaction. Transaction + /// that is declaring more messages than this value, will be rejected. Even if these messages are + /// from different lanes. + type MaxUnconfirmedMessagesAtInboundLane: Get; + + /// Payload type of outbound messages. This payload is dispatched on the bridged chain. + type OutboundPayload: Parameter + Size; + /// Message fee type of outbound messages. This fee is paid on this chain. + type OutboundMessageFee: Default + From + PartialOrd + Parameter + SaturatingAdd + Zero; + + /// Payload type of inbound messages. This payload is dispatched on this chain. + type InboundPayload: Decode; + /// Message fee type of inbound messages. This fee is paid on the bridged chain. + type InboundMessageFee: Decode; + /// Identifier of relayer that deliver messages to this chain. Relayer reward is paid on the bridged chain. + type InboundRelayer: Parameter; + + /// A type which can be turned into an AccountId from a 256-bit hash. + /// + /// Used when deriving the shared relayer fund account. + type AccountIdConverter: sp_runtime::traits::Convert; + + // Types that are used by outbound_lane (on source chain). + + /// Target header chain. + type TargetHeaderChain: TargetHeaderChain; + /// Message payload verifier. + type LaneMessageVerifier: LaneMessageVerifier; + /// Message delivery payment. + type MessageDeliveryAndDispatchPayment: MessageDeliveryAndDispatchPayment< + Self::AccountId, + Self::OutboundMessageFee, + >; + /// Handler for delivered messages. + type OnDeliveryConfirmed: OnDeliveryConfirmed; + + // Types that are used by inbound_lane (on target chain). + + /// Source header chain, as it is represented on target chain. + type SourceHeaderChain: SourceHeaderChain; + /// Message dispatch. + type MessageDispatch: MessageDispatch< + Self::AccountId, + Self::InboundMessageFee, + DispatchPayload = Self::InboundPayload, + >; + } + + /// Shortcut to messages proof type for Config. + type MessagesProofOf = + <>::SourceHeaderChain as SourceHeaderChain<>::InboundMessageFee>>::MessagesProof; + /// Shortcut to messages delivery proof type for Config. + type MessagesDeliveryProofOf = <>::TargetHeaderChain as TargetHeaderChain< + >::OutboundPayload, + ::AccountId, + >>::MessagesDeliveryProof; + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(PhantomData<(T, I)>); + + #[pallet::hooks] + impl, I: 'static> Hooks> for Pallet { /// Ensure runtime invariants. fn on_runtime_upgrade() -> Weight { - let reads = T::MessageDeliveryAndDispatchPayment::initialize( - &Self::relayer_fund_account_id() - ); + let reads = T::MessageDeliveryAndDispatchPayment::initialize(&Self::relayer_fund_account_id()); T::DbWeight::get().reads(reads as u64) } + } + #[pallet::call] + impl, I: 'static> Pallet { /// Change `PalletOwner`. /// /// May only be called either by root, or by `PalletOwner`. - #[weight = (T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational)] - pub fn set_owner(origin, new_owner: Option) { + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { ensure_owner_or_root::(origin)?; match new_owner { Some(new_owner) => { PalletOwner::::put(&new_owner); log::info!(target: "runtime::bridge-messages", "Setting pallet Owner to: {:?}", new_owner); - }, + } None => { PalletOwner::::kill(); log::info!(target: "runtime::bridge-messages", "Removed Owner of pallet."); - }, + } } + Ok(()) } /// Halt or resume all/some pallet operations. /// /// May only be called either by root, or by `PalletOwner`. - #[weight = (T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational)] - pub fn set_operating_mode(origin, operating_mode: OperatingMode) { + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_operating_mode(origin: OriginFor, operating_mode: OperatingMode) -> DispatchResult { ensure_owner_or_root::(origin)?; - >::put(operating_mode); + PalletOperatingMode::::put(operating_mode); log::info!( target: "runtime::bridge-messages", "Setting messages pallet operating mode to {:?}.", operating_mode, ); + Ok(()) } /// Update pallet parameter. @@ -306,17 +234,18 @@ decl_module! { /// May only be called either by root, or by `PalletOwner`. /// /// The weight is: single read for permissions check + 2 writes for parameter value and event. - #[weight = (T::DbWeight::get().reads_writes(1, 2), DispatchClass::Operational)] - pub fn update_pallet_parameter(origin, parameter: T::Parameter) { + #[pallet::weight((T::DbWeight::get().reads_writes(1, 2), DispatchClass::Operational))] + pub fn update_pallet_parameter(origin: OriginFor, parameter: T::Parameter) -> DispatchResult { ensure_owner_or_root::(origin)?; parameter.save(); - Self::deposit_event(RawEvent::ParameterUpdated(parameter)); + Self::deposit_event(Event::ParameterUpdated(parameter)); + Ok(()) } /// Send message over lane. - #[weight = T::WeightInfo::send_message_weight(payload)] + #[pallet::weight(T::WeightInfo::send_message_weight(payload))] pub fn send_message( - origin, + origin: OriginFor, lane_id: LaneId, payload: T::OutboundPayload, delivery_and_dispatch_fee: T::OutboundMessageFee, @@ -328,17 +257,16 @@ decl_module! { let mut actual_weight = T::WeightInfo::send_message_weight(&payload); // let's first check if message can be delivered to target chain - T::TargetHeaderChain::verify_message(&payload) - .map_err(|err| { - log::trace!( - target: "runtime::bridge-messages", - "Message to lane {:?} is rejected by target chain: {:?}", - lane_id, - err, - ); + T::TargetHeaderChain::verify_message(&payload).map_err(|err| { + log::trace!( + target: "runtime::bridge-messages", + "Message to lane {:?} is rejected by target chain: {:?}", + lane_id, + err, + ); - Error::::MessageRejectedByChainVerifier - })?; + Error::::MessageRejectedByChainVerifier + })?; // now let's enforce any additional lane rules let mut lane = outbound_lane::(lane_id); @@ -348,7 +276,8 @@ decl_module! { &lane_id, &lane.data(), &payload, - ).map_err(|err| { + ) + .map_err(|err| { log::trace!( target: "runtime::bridge-messages", "Message to lane {:?} is rejected by lane verifier: {:?}", @@ -364,7 +293,8 @@ decl_module! { &submitter, &delivery_and_dispatch_fee, &Self::relayer_fund_account_id(), - ).map_err(|err| { + ) + .map_err(|err| { log::trace!( target: "runtime::bridge-messages", "Message to lane {:?} is rejected because submitter {:?} is unable to pay fee {:?}: {:?}", @@ -402,7 +332,7 @@ decl_module! { encoded_payload_len, ); - Self::deposit_event(RawEvent::MessageAccepted(lane_id, nonce)); + Self::deposit_event(Event::MessageAccepted(lane_id, nonce)); Ok(PostDispatchInfo { actual_weight: Some(actual_weight), @@ -411,9 +341,9 @@ decl_module! { } /// Pay additional fee for the message. - #[weight = T::WeightInfo::maximal_increase_message_fee()] + #[pallet::weight(T::WeightInfo::maximal_increase_message_fee())] pub fn increase_message_fee( - origin, + origin: OriginFor, lane_id: LaneId, nonce: MessageNonce, additional_fee: T::OutboundMessageFee, @@ -425,8 +355,14 @@ decl_module! { // if someone tries to pay for not-yet-sent message, we're rejeting this intention, or // we're risking to have mess in the storage let lane = outbound_lane::(lane_id); - ensure!(nonce > lane.data().latest_received_nonce, Error::::MessageIsAlreadyDelivered); - ensure!(nonce <= lane.data().latest_generated_nonce, Error::::MessageIsNotYetSent); + ensure!( + nonce > lane.data().latest_received_nonce, + Error::::MessageIsAlreadyDelivered + ); + ensure!( + nonce <= lane.data().latest_generated_nonce, + Error::::MessageIsNotYetSent + ); // withdraw additional fee from submitter let submitter = origin.into().map_err(|_| BadOrigin)?; @@ -434,7 +370,8 @@ decl_module! { &submitter, &additional_fee, &Self::relayer_fund_account_id(), - ).map_err(|err| { + ) + .map_err(|err| { log::trace!( target: "runtime::bridge-messages", "Submitter {:?} can't pay additional fee {:?} for the message {:?}/{:?}: {:?}", @@ -478,9 +415,9 @@ decl_module! { /// The weight of the call assumes that the transaction always brings outbound lane /// state update. Because of that, the submitter (relayer) has no benefit of not including /// this data in the transaction, so reward confirmations lags should be minimal. - #[weight = T::WeightInfo::receive_messages_proof_weight(proof, *messages_count, *dispatch_weight)] + #[pallet::weight(T::WeightInfo::receive_messages_proof_weight(proof, *messages_count, *dispatch_weight))] pub fn receive_messages_proof( - origin, + origin: OriginFor, relayer_id_at_bridged_chain: T::InboundRelayer, proof: MessagesProofOf, messages_count: u32, @@ -505,11 +442,7 @@ decl_module! { // // The DeclaredWeight is exactly what's computed here. Unfortunately it is impossible // to get pre-computed value (and it has been already computed by the executive). - let declared_weight = T::WeightInfo::receive_messages_proof_weight( - &proof, - messages_count, - dispatch_weight, - ); + let declared_weight = T::WeightInfo::receive_messages_proof_weight(&proof, messages_count, dispatch_weight); let mut actual_weight = declared_weight; // verify messages proof && convert proof into messages @@ -518,15 +451,15 @@ decl_module! { T::InboundMessageFee, T::InboundPayload, >(proof, messages_count) - .map_err(|err| { - log::trace!( - target: "runtime::bridge-messages", - "Rejecting invalid messages proof: {:?}", - err, - ); + .map_err(|err| { + log::trace!( + target: "runtime::bridge-messages", + "Rejecting invalid messages proof: {:?}", + err, + ); - Error::::InvalidMessagesProof - })?; + Error::::InvalidMessagesProof + })?; // dispatch messages and (optionally) update lane(s) state(s) let mut total_messages = 0; @@ -582,27 +515,28 @@ decl_module! { let (unspent_weight, refund_pay_dispatch_fee) = match receival_result { ReceivalResult::Dispatched(dispatch_result) => { valid_messages += 1; - (dispatch_result.unspent_weight, !dispatch_result.dispatch_fee_paid_during_dispatch) - }, + ( + dispatch_result.unspent_weight, + !dispatch_result.dispatch_fee_paid_during_dispatch, + ) + } ReceivalResult::InvalidNonce - | ReceivalResult::TooManyUnrewardedRelayers - | ReceivalResult::TooManyUnconfirmedMessages => (dispatch_weight, true), + | ReceivalResult::TooManyUnrewardedRelayers + | ReceivalResult::TooManyUnconfirmedMessages => (dispatch_weight, true), }; let unspent_weight = sp_std::cmp::min(unspent_weight, dispatch_weight); dispatch_weight_left -= dispatch_weight - unspent_weight; - actual_weight = actual_weight - .saturating_sub(unspent_weight) - .saturating_sub( - // delivery call weight formula assumes that the fee is paid at - // this (target) chain. If the message is prepaid at the source - // chain, let's refund relayer with this extra cost. - if refund_pay_dispatch_fee { - T::WeightInfo::pay_inbound_dispatch_fee_overhead() - } else { - 0 - } - ); + actual_weight = actual_weight.saturating_sub(unspent_weight).saturating_sub( + // delivery call weight formula assumes that the fee is paid at + // this (target) chain. If the message is prepaid at the source + // chain, let's refund relayer with this extra cost. + if refund_pay_dispatch_fee { + T::WeightInfo::pay_inbound_dispatch_fee_overhead() + } else { + 0 + }, + ); } } @@ -622,13 +556,13 @@ decl_module! { } /// Receive messages delivery proof from bridged chain. - #[weight = T::WeightInfo::receive_messages_delivery_proof_weight( + #[pallet::weight(T::WeightInfo::receive_messages_delivery_proof_weight( proof, relayers_state, T::DbWeight::get(), - )] + ))] pub fn receive_messages_delivery_proof( - origin, + origin: OriginFor, proof: MessagesDeliveryProofOf, relayers_state: UnrewardedRelayersState, ) -> DispatchResultWithPostInfo { @@ -644,11 +578,8 @@ decl_module! { // The DeclaredWeight is exactly what's computed here. Unfortunately it is impossible // to get pre-computed value (and it has been already computed by the executive). let single_message_callback_overhead = T::WeightInfo::single_message_callback_overhead(T::DbWeight::get()); - let declared_weight = T::WeightInfo::receive_messages_delivery_proof_weight( - &proof, - &relayers_state, - T::DbWeight::get(), - ); + let declared_weight = + T::WeightInfo::receive_messages_delivery_proof_weight(&proof, &relayers_state, T::DbWeight::get()); let mut actual_weight = declared_weight; let confirmation_relayer = ensure_signed(origin)?; @@ -665,8 +596,8 @@ decl_module! { // verify that the relayer has declared correct `lane_data::relayers` state // (we only care about total number of entries and messages, because this affects call weight) ensure!( - total_unrewarded_messages(&lane_data.relayers) - .unwrap_or(MessageNonce::MAX) == relayers_state.total_messages + total_unrewarded_messages(&lane_data.relayers).unwrap_or(MessageNonce::MAX) + == relayers_state.total_messages && lane_data.relayers.len() as MessageNonce == relayers_state.unrewarded_relayer_entries, Error::::InvalidUnrewardedRelayersState ); @@ -675,43 +606,38 @@ decl_module! { let mut lane = outbound_lane::(lane_id); let mut relayers_rewards: RelayersRewards<_, T::OutboundMessageFee> = RelayersRewards::new(); let last_delivered_nonce = lane_data.last_delivered_nonce(); - let confirmed_messages = match lane.confirm_delivery( - relayers_state.total_messages, - last_delivered_nonce, - &lane_data.relayers, - ) { - ReceivalConfirmationResult::ConfirmedMessages(confirmed_messages) => Some(confirmed_messages), - ReceivalConfirmationResult::NoNewConfirmations => None, - ReceivalConfirmationResult::TryingToConfirmMoreMessagesThanExpected(to_confirm_messages_count) => { - log::trace!( - target: "runtime::bridge-messages", - "Messages delivery proof contains too many messages to confirm: {} vs declared {}", - to_confirm_messages_count, - relayers_state.total_messages, - ); + let confirmed_messages = + match lane.confirm_delivery(relayers_state.total_messages, last_delivered_nonce, &lane_data.relayers) { + ReceivalConfirmationResult::ConfirmedMessages(confirmed_messages) => Some(confirmed_messages), + ReceivalConfirmationResult::NoNewConfirmations => None, + ReceivalConfirmationResult::TryingToConfirmMoreMessagesThanExpected(to_confirm_messages_count) => { + log::trace!( + target: "runtime::bridge-messages", + "Messages delivery proof contains too many messages to confirm: {} vs declared {}", + to_confirm_messages_count, + relayers_state.total_messages, + ); - fail!(Error::::TryingToConfirmMoreMessagesThanExpected); - }, - error => { - log::trace!( - target: "runtime::bridge-messages", - "Messages delivery proof contains invalid unrewarded relayers vec: {:?}", - error, - ); + fail!(Error::::TryingToConfirmMoreMessagesThanExpected); + } + error => { + log::trace!( + target: "runtime::bridge-messages", + "Messages delivery proof contains invalid unrewarded relayers vec: {:?}", + error, + ); - fail!(Error::::InvalidUnrewardedRelayers); - }, - }; + fail!(Error::::InvalidUnrewardedRelayers); + } + }; if let Some(confirmed_messages) = confirmed_messages { // handle messages delivery confirmation - let preliminary_callback_overhead = relayers_state.total_messages.saturating_mul( - single_message_callback_overhead - ); - let actual_callback_weight = T::OnDeliveryConfirmed::on_messages_delivered( - &lane_id, - &confirmed_messages, - ); + let preliminary_callback_overhead = relayers_state + .total_messages + .saturating_mul(single_message_callback_overhead); + let actual_callback_weight = + T::OnDeliveryConfirmed::on_messages_delivered(&lane_id, &confirmed_messages); match preliminary_callback_overhead.checked_sub(actual_callback_weight) { Some(difference) if difference == 0 => (), Some(difference) => { @@ -724,7 +650,7 @@ decl_module! { difference, ); actual_weight -= difference; - }, + } None => { debug_assert!(false, "The delivery confirmation callback is wrong"); log::trace!( @@ -739,7 +665,7 @@ decl_module! { // emit 'delivered' event let received_range = confirmed_messages.begin..=confirmed_messages.end; - Self::deposit_event(RawEvent::MessagesDelivered(lane_id, confirmed_messages)); + Self::deposit_event(Event::MessagesDelivered(lane_id, confirmed_messages)); // remember to reward relayers that have delivered messages // this loop is bounded by `T::MaxUnrewardedRelayerEntriesAtInboundLane` on the bridged chain @@ -751,10 +677,8 @@ decl_module! { // this loop is bound by `T::MaxUnconfirmedMessagesAtInboundLane` on the bridged chain let mut relayer_reward = relayers_rewards.entry(entry.relayer).or_default(); for nonce in nonce_begin..nonce_end + 1 { - let message_data = OutboundMessages::::get(MessageKey { - lane_id, - nonce, - }).expect("message was just confirmed; we never prune unconfirmed messages; qed"); + let message_data = OutboundMessages::::get(MessageKey { lane_id, nonce }) + .expect("message was just confirmed; we never prune unconfirmed messages; qed"); relayer_reward.reward = relayer_reward.reward.saturating_add(&message_data.fee); relayer_reward.messages += 1; } @@ -784,102 +708,199 @@ decl_module! { }) } } -} -impl, I: Instance> Pallet { - /// Get stored data of the outbound message with given nonce. - pub fn outbound_message_data(lane: LaneId, nonce: MessageNonce) -> Option> { - OutboundMessages::::get(MessageKey { lane_id: lane, nonce }) + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + #[pallet::metadata(T::Parameter = "Parameter")] + pub enum Event, I: 'static = ()> { + /// Pallet parameter has been updated. + ParameterUpdated(T::Parameter), + /// Message has been accepted and is waiting to be delivered. + MessageAccepted(LaneId, MessageNonce), + /// Messages in the inclusive range have been delivered to the bridged chain. + MessagesDelivered(LaneId, DeliveredMessages), } - /// Get nonce of the latest generated message at given outbound lane. - pub fn outbound_latest_generated_nonce(lane: LaneId) -> MessageNonce { - OutboundLanes::::get(&lane).latest_generated_nonce + #[pallet::error] + pub enum Error { + /// All pallet operations are halted. + Halted, + /// Message has been treated as invalid by chain verifier. + MessageRejectedByChainVerifier, + /// Message has been treated as invalid by lane verifier. + MessageRejectedByLaneVerifier, + /// Submitter has failed to pay fee for delivering and dispatching messages. + FailedToWithdrawMessageFee, + /// The transaction brings too many messages. + TooManyMessagesInTheProof, + /// Invalid messages has been submitted. + InvalidMessagesProof, + /// Invalid messages delivery proof has been submitted. + InvalidMessagesDeliveryProof, + /// The bridged chain has invalid `UnrewardedRelayers` in its storage (fatal for the lane). + InvalidUnrewardedRelayers, + /// The relayer has declared invalid unrewarded relayers state in the `receive_messages_delivery_proof` call. + InvalidUnrewardedRelayersState, + /// The message someone is trying to work with (i.e. increase fee) is already-delivered. + MessageIsAlreadyDelivered, + /// The message someone is trying to work with (i.e. increase fee) is not yet sent. + MessageIsNotYetSent, + /// The number of actually confirmed messages is going to be larger than the number of messages in the proof. + /// This may mean that this or bridged chain storage is corrupted. + TryingToConfirmMoreMessagesThanExpected, } - /// Get nonce of the latest confirmed message at given outbound lane. - pub fn outbound_latest_received_nonce(lane: LaneId) -> MessageNonce { - OutboundLanes::::get(&lane).latest_received_nonce + /// Optional pallet owner. + /// + /// Pallet owner has a right to halt all pallet operations and then resume it. If it is + /// `None`, then there are no direct ways to halt/resume pallet operations, but other + /// runtime methods may still be used to do that (i.e. democracy::referendum to update halt + /// flag directly or call the `halt_operations`). + #[pallet::storage] + #[pallet::getter(fn module_owner)] + pub type PalletOwner, I: 'static = ()> = StorageValue<_, T::AccountId>; + + /// The current operating mode of the pallet. + /// + /// Depending on the mode either all, some, or no transactions will be allowed. + #[pallet::storage] + #[pallet::getter(fn operating_mode)] + pub type PalletOperatingMode, I: 'static = ()> = StorageValue<_, OperatingMode, ValueQuery>; + + /// Map of lane id => inbound lane data. + #[pallet::storage] + pub type InboundLanes, I: 'static = ()> = + StorageMap<_, Blake2_128Concat, LaneId, InboundLaneData, ValueQuery>; + + /// Map of lane id => outbound lane data. + #[pallet::storage] + pub type OutboundLanes, I: 'static = ()> = + StorageMap<_, Blake2_128Concat, LaneId, OutboundLaneData, ValueQuery>; + + /// All queued outbound messages. + #[pallet::storage] + pub type OutboundMessages, I: 'static = ()> = + StorageMap<_, Blake2_128Concat, MessageKey, MessageData>; + + #[pallet::genesis_config] + pub struct GenesisConfig, I: 'static = ()> { + /// Initial pallet operating mode. + pub operating_mode: OperatingMode, + /// Initial pallet owner. + pub owner: Option, + /// Dummy marker. + pub phantom: sp_std::marker::PhantomData, + } + + #[cfg(feature = "std")] + impl, I: 'static> Default for GenesisConfig { + fn default() -> Self { + Self { + operating_mode: Default::default(), + owner: Default::default(), + phantom: Default::default(), + } + } } - /// Get nonce of the latest received message at given inbound lane. - pub fn inbound_latest_received_nonce(lane: LaneId) -> MessageNonce { - InboundLanes::::get(&lane).last_delivered_nonce() + #[pallet::genesis_build] + impl, I: 'static> GenesisBuild for GenesisConfig { + fn build(&self) { + PalletOperatingMode::::put(&self.operating_mode); + if let Some(ref owner) = self.owner { + PalletOwner::::put(owner); + } + } } - /// Get nonce of the latest confirmed message at given inbound lane. - pub fn inbound_latest_confirmed_nonce(lane: LaneId) -> MessageNonce { - InboundLanes::::get(&lane).last_confirmed_nonce - } + impl, I: 'static> Pallet { + /// Get stored data of the outbound message with given nonce. + pub fn outbound_message_data(lane: LaneId, nonce: MessageNonce) -> Option> { + OutboundMessages::::get(MessageKey { lane_id: lane, nonce }) + } - /// Get state of unrewarded relayers set. - pub fn inbound_unrewarded_relayers_state(lane: bp_messages::LaneId) -> bp_messages::UnrewardedRelayersState { - let relayers = InboundLanes::::get(&lane).relayers; - bp_messages::UnrewardedRelayersState { - unrewarded_relayer_entries: relayers.len() as _, - messages_in_oldest_entry: relayers - .front() - .map(|entry| 1 + entry.messages.end - entry.messages.begin) - .unwrap_or(0), - total_messages: total_unrewarded_messages(&relayers).unwrap_or(MessageNonce::MAX), + /// Get nonce of the latest generated message at given outbound lane. + pub fn outbound_latest_generated_nonce(lane: LaneId) -> MessageNonce { + OutboundLanes::::get(&lane).latest_generated_nonce } - } - /// AccountId of the shared relayer fund account. - /// - /// This account is passed to `MessageDeliveryAndDispatchPayment` trait, and depending - /// on the implementation it can be used to store relayers rewards. - /// See [InstantCurrencyPayments] for a concrete implementation. - pub fn relayer_fund_account_id() -> T::AccountId { - use sp_runtime::traits::Convert; - let encoded_id = bp_runtime::derive_relayer_fund_account_id(bp_runtime::NO_INSTANCE_ID); - T::AccountIdConverter::convert(encoded_id) + /// Get nonce of the latest confirmed message at given outbound lane. + pub fn outbound_latest_received_nonce(lane: LaneId) -> MessageNonce { + OutboundLanes::::get(&lane).latest_received_nonce + } + + /// Get nonce of the latest received message at given inbound lane. + pub fn inbound_latest_received_nonce(lane: LaneId) -> MessageNonce { + InboundLanes::::get(&lane).last_delivered_nonce() + } + + /// Get nonce of the latest confirmed message at given inbound lane. + pub fn inbound_latest_confirmed_nonce(lane: LaneId) -> MessageNonce { + InboundLanes::::get(&lane).last_confirmed_nonce + } + + /// Get state of unrewarded relayers set. + pub fn inbound_unrewarded_relayers_state(lane: bp_messages::LaneId) -> bp_messages::UnrewardedRelayersState { + let relayers = InboundLanes::::get(&lane).relayers; + bp_messages::UnrewardedRelayersState { + unrewarded_relayer_entries: relayers.len() as _, + messages_in_oldest_entry: relayers + .front() + .map(|entry| 1 + entry.messages.end - entry.messages.begin) + .unwrap_or(0), + total_messages: total_unrewarded_messages(&relayers).unwrap_or(MessageNonce::MAX), + } + } + + /// AccountId of the shared relayer fund account. + /// + /// This account is passed to `MessageDeliveryAndDispatchPayment` trait, and depending + /// on the implementation it can be used to store relayers rewards. + /// See [InstantCurrencyPayments] for a concrete implementation. + pub fn relayer_fund_account_id() -> T::AccountId { + use sp_runtime::traits::Convert; + let encoded_id = bp_runtime::derive_relayer_fund_account_id(bp_runtime::NO_INSTANCE_ID); + T::AccountIdConverter::convert(encoded_id) + } } } /// Getting storage keys for messages and lanes states. These keys are normally used when building /// messages and lanes states proofs. -/// -/// Keep in mind that all functions in this module are **NOT** using passed `T` argument, so any -/// runtime can be passed. E.g. if you're verifying proof from Runtime1 in Runtime2, you only have -/// access to Runtime2 and you may pass it to the functions, where required. This is because our -/// maps are not using any Runtime-specific data in the keys. -/// -/// On the other side, passing correct instance is required. So if proof has been crafted by the -/// Instance1, you should verify it using Instance1. This is inconvenient if you're using different -/// instances on different sides of the bridge. I.e. in Runtime1 it is Instance2, but on Runtime2 -/// it is Instance42. But there's no other way, but to craft this key manually (which is what I'm -/// trying to avoid here) - by using strings like "Instance2", "OutboundMessages", etc. pub mod storage_keys { use super::*; - use frame_support::{traits::Instance, StorageHasher}; + use frame_support::StorageHasher; use sp_core::storage::StorageKey; /// Storage key of the outbound message in the runtime storage. - pub fn message_key(lane: &LaneId, nonce: MessageNonce) -> StorageKey { - storage_map_final_key::("OutboundMessages", &MessageKey { lane_id: *lane, nonce }.encode()) + pub fn message_key(pallet_prefix: &str, lane: &LaneId, nonce: MessageNonce) -> StorageKey { + storage_map_final_key( + pallet_prefix, + "OutboundMessages", + &MessageKey { lane_id: *lane, nonce }.encode(), + ) } /// Storage key of the outbound message lane state in the runtime storage. - pub fn outbound_lane_data_key(lane: &LaneId) -> StorageKey { - storage_map_final_key::("OutboundLanes", lane) + pub fn outbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey { + storage_map_final_key(pallet_prefix, "OutboundLanes", lane) } /// Storage key of the inbound message lane state in the runtime storage. - pub fn inbound_lane_data_key(lane: &LaneId) -> StorageKey { - storage_map_final_key::("InboundLanes", lane) + pub fn inbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey { + storage_map_final_key(pallet_prefix, "InboundLanes", lane) } /// This is a copypaste of the `frame_support::storage::generator::StorageMap::storage_map_final_key`. - fn storage_map_final_key(map_name: &str, key: &[u8]) -> StorageKey { - let module_prefix_hashed = frame_support::Twox128::hash(I::PREFIX.as_bytes()); + fn storage_map_final_key(pallet_prefix: &str, map_name: &str, key: &[u8]) -> StorageKey { + let pallet_prefix_hashed = frame_support::Twox128::hash(pallet_prefix.as_bytes()); let storage_prefix_hashed = frame_support::Twox128::hash(map_name.as_bytes()); let key_hashed = frame_support::Blake2_128Concat::hash(key); let mut final_key = - Vec::with_capacity(module_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.len()); + Vec::with_capacity(pallet_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.len()); - final_key.extend_from_slice(&module_prefix_hashed[..]); + final_key.extend_from_slice(&pallet_prefix_hashed[..]); final_key.extend_from_slice(&storage_prefix_hashed[..]); final_key.extend_from_slice(key_hashed.as_ref()); @@ -888,7 +909,7 @@ pub mod storage_keys { } /// Ensure that the origin is either root, or `PalletOwner`. -fn ensure_owner_or_root, I: Instance>(origin: T::Origin) -> Result<(), BadOrigin> { +fn ensure_owner_or_root, I: 'static>(origin: T::Origin) -> Result<(), BadOrigin> { match origin.into() { Ok(RawOrigin::Root) => Ok(()), Ok(RawOrigin::Signed(ref signer)) if Some(signer) == Pallet::::module_owner().as_ref() => Ok(()), @@ -897,8 +918,8 @@ fn ensure_owner_or_root, I: Instance>(origin: T::Origin) -> Result< } /// Ensure that the pallet is in normal operational mode. -fn ensure_normal_operating_mode, I: Instance>() -> Result<(), Error> { - if PalletOperatingMode::::get() != OperatingMode::Normal { +fn ensure_normal_operating_mode, I: 'static>() -> Result<(), Error> { + if PalletOperatingMode::::get() != OperatingMode::Normal { Err(Error::::Halted) } else { Ok(()) @@ -906,8 +927,8 @@ fn ensure_normal_operating_mode, I: Instance>() -> Result<(), Error } /// Ensure that the pallet is not halted. -fn ensure_not_halted, I: Instance>() -> Result<(), Error> { - if PalletOperatingMode::::get() == OperatingMode::Halted { +fn ensure_not_halted, I: 'static>() -> Result<(), Error> { + if PalletOperatingMode::::get() == OperatingMode::Halted { Err(Error::::Halted) } else { Ok(()) @@ -915,12 +936,12 @@ fn ensure_not_halted, I: Instance>() -> Result<(), Error> { } /// Creates new inbound lane object, backed by runtime storage. -fn inbound_lane, I: Instance>(lane_id: LaneId) -> InboundLane> { +fn inbound_lane, I: 'static>(lane_id: LaneId) -> InboundLane> { InboundLane::new(inbound_lane_storage::(lane_id)) } /// Creates new runtime inbound lane storage. -fn inbound_lane_storage, I: Instance>(lane_id: LaneId) -> RuntimeInboundLaneStorage { +fn inbound_lane_storage, I: 'static>(lane_id: LaneId) -> RuntimeInboundLaneStorage { RuntimeInboundLaneStorage { lane_id, cached_data: RefCell::new(None), @@ -929,7 +950,7 @@ fn inbound_lane_storage, I: Instance>(lane_id: LaneId) -> RuntimeIn } /// Creates new outbound lane object, backed by runtime storage. -fn outbound_lane, I: Instance>(lane_id: LaneId) -> OutboundLane> { +fn outbound_lane, I: 'static>(lane_id: LaneId) -> OutboundLane> { OutboundLane::new(RuntimeOutboundLaneStorage { lane_id, _phantom: Default::default(), @@ -937,13 +958,13 @@ fn outbound_lane, I: Instance>(lane_id: LaneId) -> OutboundLane, I = DefaultInstance> { +struct RuntimeInboundLaneStorage, I: 'static = ()> { lane_id: LaneId, cached_data: RefCell>>, _phantom: PhantomData, } -impl, I: Instance> InboundLaneStorage for RuntimeInboundLaneStorage { +impl, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage { type MessageFee = T::InboundMessageFee; type Relayer = T::InboundRelayer; @@ -983,12 +1004,12 @@ impl, I: Instance> InboundLaneStorage for RuntimeInboundLaneStorage } /// Runtime outbound lane storage. -struct RuntimeOutboundLaneStorage { +struct RuntimeOutboundLaneStorage { lane_id: LaneId, _phantom: PhantomData<(T, I)>, } -impl, I: Instance> OutboundLaneStorage for RuntimeOutboundLaneStorage { +impl, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorage { type MessageFee = T::OutboundMessageFee; fn id(&self) -> LaneId { @@ -996,11 +1017,11 @@ impl, I: Instance> OutboundLaneStorage for RuntimeOutboundLaneStora } fn data(&self) -> OutboundLaneData { - OutboundLanes::::get(&self.lane_id) + OutboundLanes::::get(&self.lane_id) } fn set_data(&mut self, data: OutboundLaneData) { - OutboundLanes::::insert(&self.lane_id, data) + OutboundLanes::::insert(&self.lane_id, data) } #[cfg(test)] @@ -1063,7 +1084,7 @@ mod tests { PAYLOAD_REJECTED_BY_TARGET_CHAIN, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_RELAYER_A, TEST_RELAYER_B, }; use bp_messages::{UnrewardedRelayer, UnrewardedRelayersState}; - use frame_support::{assert_noop, assert_ok}; + use frame_support::{assert_noop, assert_ok, weights::Weight}; use frame_system::{EventRecord, Pallet as System, Phase}; use hex_literal::hex; use sp_runtime::DispatchError; @@ -1076,7 +1097,7 @@ mod tests { fn send_regular_message() -> Weight { get_ready_for_events(); - let message_nonce = outbound_lane::(TEST_LANE_ID) + let message_nonce = outbound_lane::(TEST_LANE_ID) .data() .latest_generated_nonce + 1; @@ -1095,7 +1116,7 @@ mod tests { System::::events(), vec![EventRecord { phase: Phase::Initialization, - event: TestEvent::Messages(RawEvent::MessageAccepted(TEST_LANE_ID, message_nonce)), + event: TestEvent::Messages(Event::MessageAccepted(TEST_LANE_ID, message_nonce)), topics: vec![], }], ); @@ -1138,10 +1159,7 @@ mod tests { System::::events(), vec![EventRecord { phase: Phase::Initialization, - event: TestEvent::Messages(RawEvent::MessagesDelivered( - TEST_LANE_ID, - DeliveredMessages::new(1, true), - )), + event: TestEvent::Messages(Event::MessagesDelivered(TEST_LANE_ID, DeliveredMessages::new(1, true),)), topics: vec![], }], ); @@ -1242,7 +1260,7 @@ mod tests { System::::events(), vec![EventRecord { phase: Phase::Initialization, - event: TestEvent::Messages(RawEvent::ParameterUpdated(parameter)), + event: TestEvent::Messages(Event::ParameterUpdated(parameter)), topics: vec![], }], ); @@ -1266,7 +1284,7 @@ mod tests { System::::events(), vec![EventRecord { phase: Phase::Initialization, - event: TestEvent::Messages(RawEvent::ParameterUpdated(parameter)), + event: TestEvent::Messages(Event::ParameterUpdated(parameter)), topics: vec![], }], ); @@ -1327,7 +1345,7 @@ mod tests { // send message first to be able to check that delivery_proof fails later send_regular_message(); - PalletOperatingMode::::put(OperatingMode::Halted); + PalletOperatingMode::::put(OperatingMode::Halted); assert_noop!( Pallet::::send_message( @@ -1336,12 +1354,12 @@ mod tests { REGULAR_PAYLOAD, REGULAR_PAYLOAD.declared_weight, ), - Error::::Halted, + Error::::Halted, ); assert_noop!( Pallet::::increase_message_fee(Origin::signed(1), TEST_LANE_ID, 1, 1,), - Error::::Halted, + Error::::Halted, ); assert_noop!( @@ -1352,7 +1370,7 @@ mod tests { 1, REGULAR_PAYLOAD.declared_weight, ), - Error::::Halted, + Error::::Halted, ); assert_noop!( @@ -1371,7 +1389,7 @@ mod tests { total_messages: 1, }, ), - Error::::Halted, + Error::::Halted, ); }); } @@ -1382,7 +1400,7 @@ mod tests { // send message first to be able to check that delivery_proof fails later send_regular_message(); - PalletOperatingMode::::put(OperatingMode::RejectingOutboundMessages); + PalletOperatingMode::::put(OperatingMode::RejectingOutboundMessages); assert_noop!( Pallet::::send_message( @@ -1391,7 +1409,7 @@ mod tests { REGULAR_PAYLOAD, REGULAR_PAYLOAD.declared_weight, ), - Error::::Halted, + Error::::Halted, ); assert_ok!(Pallet::::increase_message_fee( @@ -1445,7 +1463,7 @@ mod tests { PAYLOAD_REJECTED_BY_TARGET_CHAIN, PAYLOAD_REJECTED_BY_TARGET_CHAIN.declared_weight ), - Error::::MessageRejectedByChainVerifier, + Error::::MessageRejectedByChainVerifier, ); }); } @@ -1456,7 +1474,7 @@ mod tests { // messages with zero fee are rejected by lane verifier assert_noop!( Pallet::::send_message(Origin::signed(1), TEST_LANE_ID, REGULAR_PAYLOAD, 0), - Error::::MessageRejectedByLaneVerifier, + Error::::MessageRejectedByLaneVerifier, ); }); } @@ -1472,7 +1490,7 @@ mod tests { REGULAR_PAYLOAD, REGULAR_PAYLOAD.declared_weight ), - Error::::FailedToWithdrawMessageFee, + Error::::FailedToWithdrawMessageFee, ); }); } @@ -1496,7 +1514,7 @@ mod tests { fn receive_messages_proof_updates_confirmed_message_nonce() { run_test(|| { // say we have received 10 messages && last confirmed message is 8 - InboundLanes::::insert( + InboundLanes::::insert( TEST_LANE_ID, InboundLaneData { last_confirmed_nonce: 8, @@ -1573,14 +1591,14 @@ mod tests { fn receive_messages_proof_rejects_invalid_proof() { run_test(|| { assert_noop!( - Pallet::::receive_messages_proof( + Pallet::::receive_messages_proof( Origin::signed(1), TEST_RELAYER_A, Err(()).into(), 1, 0, ), - Error::::InvalidMessagesProof, + Error::::InvalidMessagesProof, ); }); } @@ -1589,14 +1607,14 @@ mod tests { fn receive_messages_proof_rejects_proof_with_too_many_messages() { run_test(|| { assert_noop!( - Pallet::::receive_messages_proof( + Pallet::::receive_messages_proof( Origin::signed(1), TEST_RELAYER_A, Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), u32::MAX, 0, ), - Error::::TooManyMessagesInTheProof, + Error::::TooManyMessagesInTheProof, ); }); } @@ -1608,7 +1626,7 @@ mod tests { receive_messages_delivery_proof(); assert_eq!( - OutboundLanes::::get(&TEST_LANE_ID).latest_received_nonce, + OutboundLanes::::get(&TEST_LANE_ID).latest_received_nonce, 1, ); }); @@ -1696,7 +1714,7 @@ mod tests { TestMessagesDeliveryProof(Err(())), Default::default(), ), - Error::::InvalidMessagesDeliveryProof, + Error::::InvalidMessagesDeliveryProof, ); }); } @@ -1726,7 +1744,7 @@ mod tests { ..Default::default() }, ), - Error::::InvalidUnrewardedRelayersState, + Error::::InvalidUnrewardedRelayersState, ); // when number of messages is invalid @@ -1751,7 +1769,7 @@ mod tests { ..Default::default() }, ), - Error::::InvalidUnrewardedRelayersState, + Error::::InvalidUnrewardedRelayersState, ); }); } @@ -1762,7 +1780,7 @@ mod tests { let mut invalid_message = message(1, REGULAR_PAYLOAD); invalid_message.data.payload = Vec::new(); - assert_ok!(Pallet::::receive_messages_proof( + assert_ok!(Pallet::::receive_messages_proof( Origin::signed(1), TEST_RELAYER_A, Ok(vec![invalid_message]).into(), @@ -1783,7 +1801,7 @@ mod tests { let mut invalid_message = message(2, REGULAR_PAYLOAD); invalid_message.data.payload = Vec::new(); - assert_ok!(Pallet::::receive_messages_proof( + assert_ok!(Pallet::::receive_messages_proof( Origin::signed(1), TEST_RELAYER_A, Ok(vec![ @@ -1807,7 +1825,7 @@ mod tests { fn storage_message_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking all // previously crafted messages proofs. - let storage_key = storage_keys::message_key::(&*b"test", 42).0; + let storage_key = storage_keys::message_key("BridgeMessages", &*b"test", 42).0; assert_eq!( storage_key, hex!("dd16c784ebd3390a9bc0357c7511ed018a395e6242c6813b196ca31ed0547ea79446af0e09063bd4a7874aef8a997cec746573742a00000000000000").to_vec(), @@ -1820,7 +1838,7 @@ mod tests { fn outbound_lane_data_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking all // previously crafted outbound lane state proofs. - let storage_key = storage_keys::outbound_lane_data_key::(&*b"test").0; + let storage_key = storage_keys::outbound_lane_data_key("BridgeMessages", &*b"test").0; assert_eq!( storage_key, hex!("dd16c784ebd3390a9bc0357c7511ed0196c246acb9b55077390e3ca723a0ca1f44a8995dd50b6657a037a7839304535b74657374").to_vec(), @@ -1833,7 +1851,7 @@ mod tests { fn inbound_lane_data_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking all // previously crafted inbound lane state proofs. - let storage_key = storage_keys::inbound_lane_data_key::(&*b"test").0; + let storage_key = storage_keys::inbound_lane_data_key("BridgeMessages", &*b"test").0; assert_eq!( storage_key, hex!("dd16c784ebd3390a9bc0357c7511ed01e5f83cf83f2127eb47afdc35d6e43fab44a8995dd50b6657a037a7839304535b74657374").to_vec(), @@ -1849,7 +1867,7 @@ mod tests { let message2 = message(2, message_payload(0, Weight::MAX / 2)); let message3 = message(3, message_payload(0, Weight::MAX / 2)); - assert_ok!(Pallet::::receive_messages_proof( + assert_ok!(Pallet::::receive_messages_proof( Origin::signed(1), TEST_RELAYER_A, // this may cause overflow if source chain storage is invalid @@ -1868,8 +1886,8 @@ mod tests { receive_messages_delivery_proof(); assert_noop!( - Pallet::::increase_message_fee(Origin::signed(1), TEST_LANE_ID, 1, 100,), - Error::::MessageIsAlreadyDelivered, + Pallet::::increase_message_fee(Origin::signed(1), TEST_LANE_ID, 1, 100,), + Error::::MessageIsAlreadyDelivered, ); }); } @@ -1878,8 +1896,8 @@ mod tests { fn increase_message_fee_fails_if_message_is_not_yet_sent() { run_test(|| { assert_noop!( - Pallet::::increase_message_fee(Origin::signed(1), TEST_LANE_ID, 1, 100,), - Error::::MessageIsNotYetSent, + Pallet::::increase_message_fee(Origin::signed(1), TEST_LANE_ID, 1, 100,), + Error::::MessageIsNotYetSent, ); }); } @@ -1892,8 +1910,8 @@ mod tests { TestMessageDeliveryAndDispatchPayment::reject_payments(); assert_noop!( - Pallet::::increase_message_fee(Origin::signed(1), TEST_LANE_ID, 1, 100,), - Error::::FailedToWithdrawMessageFee, + Pallet::::increase_message_fee(Origin::signed(1), TEST_LANE_ID, 1, 100,), + Error::::FailedToWithdrawMessageFee, ); }); } @@ -1903,7 +1921,7 @@ mod tests { run_test(|| { send_regular_message(); - assert_ok!(Pallet::::increase_message_fee( + assert_ok!(Pallet::::increase_message_fee( Origin::signed(1), TEST_LANE_ID, 1, @@ -2120,7 +2138,7 @@ mod tests { ))), UnrewardedRelayersState::default(), ), - Error::::TryingToConfirmMoreMessagesThanExpected, + Error::::TryingToConfirmMoreMessagesThanExpected, ); }); } diff --git a/modules/messages/src/weights_ext.rs b/modules/messages/src/weights_ext.rs index b17be922f5d..c88ec47d72b 100644 --- a/modules/messages/src/weights_ext.rs +++ b/modules/messages/src/weights_ext.rs @@ -29,6 +29,11 @@ pub const EXPECTED_DEFAULT_MESSAGE_LENGTH: u32 = 128; /// we're checking here would fit 1KB. const SIGNED_EXTENSIONS_SIZE: u32 = 1024; +/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at +/// Rialto 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; + /// Ensure that weights from `WeightInfoExt` implementation are looking correct. pub fn ensure_weights_are_correct( expected_default_message_delivery_tx_weight: Weight, @@ -344,12 +349,12 @@ pub trait WeightInfoExt: WeightInfo { impl WeightInfoExt for () { fn expected_extra_storage_proof_size() -> u32 { - bp_rialto::EXTRA_STORAGE_PROOF_SIZE + EXTRA_STORAGE_PROOF_SIZE } } impl WeightInfoExt for crate::weights::RialtoWeight { fn expected_extra_storage_proof_size() -> u32 { - bp_rialto::EXTRA_STORAGE_PROOF_SIZE + EXTRA_STORAGE_PROOF_SIZE } } diff --git a/primitives/chain-millau/src/lib.rs b/primitives/chain-millau/src/lib.rs index d9f4fd51f96..34d59ce2ef9 100644 --- a/primitives/chain-millau/src/lib.rs +++ b/primitives/chain-millau/src/lib.rs @@ -245,6 +245,9 @@ pub fn max_extrinsic_size() -> u32 { *BlockLength::get().max.get(DispatchClass::Normal) } +/// Name of the With-Rialto messages pallet instance in the Millau runtime. +pub const WITH_RIALTO_MESSAGES_PALLET_NAME: &str = "BridgeRialtoMessages"; + /// Name of the `MillauFinalityApi::best_finalized` runtime method. pub const BEST_FINALIZED_MILLAU_HEADER_METHOD: &str = "MillauFinalityApi_best_finalized"; diff --git a/primitives/chain-rialto/src/lib.rs b/primitives/chain-rialto/src/lib.rs index 10eac7deb8f..57fe5d4bfa5 100644 --- a/primitives/chain-rialto/src/lib.rs +++ b/primitives/chain-rialto/src/lib.rs @@ -214,6 +214,9 @@ pub fn max_extrinsic_size() -> u32 { *BlockLength::get().max.get(DispatchClass::Normal) } +/// Name of the With-Millau messages pallet instance in the Millau runtime. +pub const WITH_MILLAU_MESSAGES_PALLET_NAME: &str = "BridgeMillauMessages"; + /// Name of the `RialtoFinalityApi::best_finalized` runtime method. pub const BEST_FINALIZED_RIALTO_HEADER_METHOD: &str = "RialtoFinalityApi_best_finalized"; diff --git a/primitives/chain-rococo/src/lib.rs b/primitives/chain-rococo/src/lib.rs index d76ec8e679d..2d5769a39c2 100644 --- a/primitives/chain-rococo/src/lib.rs +++ b/primitives/chain-rococo/src/lib.rs @@ -72,6 +72,9 @@ pub fn derive_account_from_wococo_id(id: bp_runtime::SourceAccount) - AccountIdConverter::convert(encoded_id) } +/// Name of the With-Wococo messages pallet instance in the Rococo runtime. +pub const WITH_WOCOCO_MESSAGES_PALLET_NAME: &str = "BridgeWococoMessages"; + /// Name of the `RococoFinalityApi::best_finalized` runtime method. pub const BEST_FINALIZED_ROCOCO_HEADER_METHOD: &str = "RococoFinalityApi_best_finalized"; /// Name of the `RococoFinalityApi::is_known_header` runtime method. diff --git a/primitives/chain-wococo/src/lib.rs b/primitives/chain-wococo/src/lib.rs index e0d49ffe855..b846e00321f 100644 --- a/primitives/chain-wococo/src/lib.rs +++ b/primitives/chain-wococo/src/lib.rs @@ -37,6 +37,9 @@ pub fn derive_account_from_rococo_id(id: bp_runtime::SourceAccount) - AccountIdConverter::convert(encoded_id) } +/// Name of the With-Rococo messages pallet instance in the Wococo runtime. +pub const WITH_ROCOCO_MESSAGES_PALLET_NAME: &str = "BridgeRococoMessages"; + /// Name of the `WococoFinalityApi::best_finalized` runtime method. pub const BEST_FINALIZED_WOCOCO_HEADER_METHOD: &str = "WococoFinalityApi_best_finalized"; /// Name of the `WococoFinalityApi::is_known_header` runtime method. diff --git a/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs b/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs index 93662a9d6fa..b63d57aeb78 100644 --- a/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs +++ b/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs @@ -23,7 +23,6 @@ use frame_support::dispatch::GetDispatchInfo; use sp_core::{Bytes, Pair}; use bp_messages::MessageNonce; -use bp_runtime::{MILLAU_CHAIN_ID, RIALTO_CHAIN_ID}; use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; use messages_relay::message_lane::MessageLane; use relay_millau_client::{HeaderId as MillauHeaderId, Millau, SigningParams as MillauSigningParams}; @@ -62,6 +61,9 @@ impl SubstrateMessageLane for MillauMessagesToRialto { const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_millau::BEST_FINALIZED_MILLAU_HEADER_METHOD; const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = bp_rialto::BEST_FINALIZED_RIALTO_HEADER_METHOD; + const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = bp_millau::WITH_RIALTO_MESSAGES_PALLET_NAME; + const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = bp_rialto::WITH_MILLAU_MESSAGES_PALLET_NAME; + type SourceChain = Millau; type TargetChain = Rialto; @@ -135,12 +137,10 @@ impl SubstrateMessageLane for MillauMessagesToRialto { } /// Millau node as messages source. -type MillauSourceClient = - SubstrateMessagesSource; +type MillauSourceClient = SubstrateMessagesSource; /// Rialto node as messages target. -type RialtoTargetClient = - SubstrateMessagesTarget; +type RialtoTargetClient = SubstrateMessagesTarget; /// Run Millau-to-Rialto messages sync. pub async fn run( @@ -212,14 +212,12 @@ pub async fn run( source_client.clone(), lane.clone(), lane_id, - RIALTO_CHAIN_ID, params.target_to_source_headers_relay, ), RialtoTargetClient::new( params.target_client, lane, lane_id, - MILLAU_CHAIN_ID, metrics_values, params.source_to_target_headers_relay, ), @@ -247,3 +245,33 @@ pub(crate) fn add_standalone_metrics( )), ) } + +/// Update Rialto -> Millau conversion rate, stored in Millau runtime storage. +pub(crate) async fn update_rialto_to_millau_conversion_rate( + client: Client, + signer: ::AccountKeyPair, + updated_rate: f64, +) -> anyhow::Result<()> { + let genesis_hash = *client.genesis_hash(); + let signer_id = (*signer.public().as_array_ref()).into(); + client + .submit_signed_extrinsic(signer_id, move |transaction_nonce| { + Bytes( + Millau::sign_transaction( + genesis_hash, + &signer, + transaction_nonce, + millau_runtime::MessagesCall::update_pallet_parameter( + millau_runtime::rialto_messages::MillauToRialtoMessagesParameter::RialtoToMillauConversionRate( + sp_runtime::FixedU128::from_float(updated_rate), + ), + ) + .into(), + ) + .encode(), + ) + }) + .await + .map(drop) + .map_err(|err| anyhow::format_err!("{:?}", err)) +} diff --git a/relays/bin-substrate/src/chains/mod.rs b/relays/bin-substrate/src/chains/mod.rs index d2b72d44483..8a9cb780b4a 100644 --- a/relays/bin-substrate/src/chains/mod.rs +++ b/relays/bin-substrate/src/chains/mod.rs @@ -37,9 +37,9 @@ mod wococo; // Rialto as BTC and Millau as wBTC (only in relayer). /// The identifier of token, which value is associated with Rialto token value by relayer. -pub(crate) const RIALTO_ASSOCIATED_TOKEN_ID: &str = "bitcoin"; +pub(crate) const RIALTO_ASSOCIATED_TOKEN_ID: &str = "polkadot"; /// The identifier of token, which value is associated with Millau token value by relayer. -pub(crate) const MILLAU_ASSOCIATED_TOKEN_ID: &str = "wrapped-bitcoin"; +pub(crate) const MILLAU_ASSOCIATED_TOKEN_ID: &str = "kusama"; use relay_utils::metrics::MetricsParams; diff --git a/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs b/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs index 2b7f32f2d43..1f861d1bb50 100644 --- a/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs +++ b/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs @@ -23,7 +23,6 @@ use frame_support::dispatch::GetDispatchInfo; use sp_core::{Bytes, Pair}; use bp_messages::MessageNonce; -use bp_runtime::{MILLAU_CHAIN_ID, RIALTO_CHAIN_ID}; use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; use messages_relay::message_lane::MessageLane; use relay_millau_client::{HeaderId as MillauHeaderId, Millau, SigningParams as MillauSigningParams}; @@ -62,6 +61,9 @@ impl SubstrateMessageLane for RialtoMessagesToMillau { const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_rialto::BEST_FINALIZED_RIALTO_HEADER_METHOD; const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = bp_millau::BEST_FINALIZED_MILLAU_HEADER_METHOD; + const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = bp_rialto::WITH_MILLAU_MESSAGES_PALLET_NAME; + const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = bp_millau::WITH_RIALTO_MESSAGES_PALLET_NAME; + type SourceChain = Rialto; type TargetChain = Millau; @@ -135,12 +137,10 @@ impl SubstrateMessageLane for RialtoMessagesToMillau { } /// Rialto node as messages source. -type RialtoSourceClient = - SubstrateMessagesSource; +type RialtoSourceClient = SubstrateMessagesSource; /// Millau node as messages target. -type MillauTargetClient = - SubstrateMessagesTarget; +type MillauTargetClient = SubstrateMessagesTarget; /// Run Rialto-to-Millau messages sync. pub async fn run( @@ -211,14 +211,12 @@ pub async fn run( source_client.clone(), lane.clone(), lane_id, - MILLAU_CHAIN_ID, params.target_to_source_headers_relay, ), MillauTargetClient::new( params.target_client, lane, lane_id, - RIALTO_CHAIN_ID, metrics_values, params.source_to_target_headers_relay, ), @@ -246,3 +244,33 @@ pub(crate) fn add_standalone_metrics( )), ) } + +/// Update Millau -> Rialto conversion rate, stored in Rialto runtime storage. +pub(crate) async fn update_millau_to_rialto_conversion_rate( + client: Client, + signer: ::AccountKeyPair, + updated_rate: f64, +) -> anyhow::Result<()> { + let genesis_hash = *client.genesis_hash(); + let signer_id = (*signer.public().as_array_ref()).into(); + client + .submit_signed_extrinsic(signer_id, move |transaction_nonce| { + Bytes( + Rialto::sign_transaction( + genesis_hash, + &signer, + transaction_nonce, + rialto_runtime::MessagesCall::update_pallet_parameter( + rialto_runtime::millau_messages::RialtoToMillauMessagesParameter::MillauToRialtoConversionRate( + sp_runtime::FixedU128::from_float(updated_rate), + ), + ) + .into(), + ) + .encode(), + ) + }) + .await + .map(drop) + .map_err(|err| anyhow::format_err!("{:?}", err)) +} diff --git a/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs b/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs index 043e6c89e44..3abedc0ca38 100644 --- a/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs +++ b/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs @@ -22,7 +22,6 @@ use codec::Encode; use sp_core::{Bytes, Pair}; use bp_messages::MessageNonce; -use bp_runtime::{ROCOCO_CHAIN_ID, WOCOCO_CHAIN_ID}; use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; use messages_relay::message_lane::MessageLane; use relay_rococo_client::{HeaderId as RococoHeaderId, Rococo, SigningParams as RococoSigningParams}; @@ -61,6 +60,9 @@ impl SubstrateMessageLane for RococoMessagesToWococo { const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_rococo::BEST_FINALIZED_ROCOCO_HEADER_METHOD; const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = bp_wococo::BEST_FINALIZED_WOCOCO_HEADER_METHOD; + const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = bp_rococo::WITH_WOCOCO_MESSAGES_PALLET_NAME; + const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = bp_wococo::WITH_ROCOCO_MESSAGES_PALLET_NAME; + type SourceChain = Rococo; type TargetChain = Wococo; @@ -136,20 +138,10 @@ impl SubstrateMessageLane for RococoMessagesToWococo { } /// Rococo node as messages source. -type RococoSourceClient = SubstrateMessagesSource< - Rococo, - Wococo, - RococoMessagesToWococo, - relay_rococo_client::runtime::WithWococoMessagesInstance, ->; +type RococoSourceClient = SubstrateMessagesSource; /// Wococo node as messages target. -type WococoTargetClient = SubstrateMessagesTarget< - Rococo, - Wococo, - RococoMessagesToWococo, - relay_wococo_client::runtime::WithRococoMessagesInstance, ->; +type WococoTargetClient = SubstrateMessagesTarget; /// Run Rococo-to-Wococo messages sync. pub async fn run( @@ -226,14 +218,12 @@ pub async fn run( source_client.clone(), lane.clone(), lane_id, - WOCOCO_CHAIN_ID, params.target_to_source_headers_relay, ), WococoTargetClient::new( params.target_client, lane, lane_id, - ROCOCO_CHAIN_ID, metrics_values, params.source_to_target_headers_relay, ), diff --git a/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs b/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs index d068dc2f11b..d4cff289e22 100644 --- a/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs +++ b/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs @@ -22,7 +22,6 @@ use codec::Encode; use sp_core::{Bytes, Pair}; use bp_messages::MessageNonce; -use bp_runtime::{ROCOCO_CHAIN_ID, WOCOCO_CHAIN_ID}; use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; use messages_relay::message_lane::MessageLane; use relay_rococo_client::{HeaderId as RococoHeaderId, Rococo, SigningParams as RococoSigningParams}; @@ -60,6 +59,9 @@ impl SubstrateMessageLane for WococoMessagesToRococo { const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_wococo::BEST_FINALIZED_WOCOCO_HEADER_METHOD; const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = bp_rococo::BEST_FINALIZED_ROCOCO_HEADER_METHOD; + const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = bp_wococo::WITH_ROCOCO_MESSAGES_PALLET_NAME; + const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = bp_rococo::WITH_WOCOCO_MESSAGES_PALLET_NAME; + type SourceChain = Wococo; type TargetChain = Rococo; @@ -135,20 +137,10 @@ impl SubstrateMessageLane for WococoMessagesToRococo { } /// Wococo node as messages source. -type WococoSourceClient = SubstrateMessagesSource< - Wococo, - Rococo, - WococoMessagesToRococo, - relay_wococo_client::runtime::WithRococoMessagesInstance, ->; +type WococoSourceClient = SubstrateMessagesSource; /// Rococo node as messages target. -type RococoTargetClient = SubstrateMessagesTarget< - Wococo, - Rococo, - WococoMessagesToRococo, - relay_rococo_client::runtime::WithWococoMessagesInstance, ->; +type RococoTargetClient = SubstrateMessagesTarget; /// Run Wococo-to-Rococo messages sync. pub async fn run( @@ -225,14 +217,12 @@ pub async fn run( source_client.clone(), lane.clone(), lane_id, - ROCOCO_CHAIN_ID, params.target_to_source_headers_relay, ), RococoTargetClient::new( params.target_client, lane, lane_id, - WOCOCO_CHAIN_ID, metrics_values, params.source_to_target_headers_relay, ), diff --git a/relays/bin-substrate/src/cli/mod.rs b/relays/bin-substrate/src/cli/mod.rs index d108c71f7b4..832b5308774 100644 --- a/relays/bin-substrate/src/cli/mod.rs +++ b/relays/bin-substrate/src/cli/mod.rs @@ -391,6 +391,17 @@ macro_rules! declare_chain_options { pub [<$chain_prefix _signer_password_file>]: Option, } + #[doc = "Parameters required to sign transaction on behalf of owner of the messages pallet at " $chain "."] + #[derive(StructOpt, Debug, PartialEq, Eq)] + pub struct [<$chain MessagesPalletOwnerSigningParams>] { + #[doc = "The SURI of secret key to use when transactions are submitted to the " $chain " node."] + #[structopt(long)] + pub [<$chain_prefix _messages_pallet_owner>]: Option, + #[doc = "The password for the SURI of secret key to use when transactions are submitted to the " $chain " node."] + #[structopt(long)] + pub [<$chain_prefix _messages_pallet_owner_password>]: Option, + } + impl [<$chain SigningParams>] { /// Parse signing params into chain-specific KeyPair. pub fn to_keypair(&self) -> anyhow::Result { @@ -433,6 +444,23 @@ macro_rules! declare_chain_options { } } + #[allow(dead_code)] + impl [<$chain MessagesPalletOwnerSigningParams>] { + /// Parse signing params into chain-specific KeyPair. + pub fn to_keypair(&self) -> anyhow::Result> { + use sp_core::crypto::Pair; + + let [<$chain_prefix _messages_pallet_owner>] = match self.[<$chain_prefix _messages_pallet_owner>] { + Some(ref messages_pallet_owner) => messages_pallet_owner, + None => return Ok(None), + }; + Chain::KeyPair::from_string( + [<$chain_prefix _messages_pallet_owner>], + self.[<$chain_prefix _messages_pallet_owner_password>].as_deref() + ).map_err(|e| anyhow::format_err!("{:?}", e)).map(Some) + } + } + impl [<$chain ConnectionParams>] { /// Convert connection params into Substrate client. pub async fn to_client( diff --git a/relays/bin-substrate/src/cli/relay_headers_and_messages.rs b/relays/bin-substrate/src/cli/relay_headers_and_messages.rs index 47479390859..a2f0d236a96 100644 --- a/relays/bin-substrate/src/cli/relay_headers_and_messages.rs +++ b/relays/bin-substrate/src/cli/relay_headers_and_messages.rs @@ -26,6 +26,7 @@ use futures::{FutureExt, TryFutureExt}; use structopt::StructOpt; use strum::VariantNames; +use relay_substrate_client::{Chain, Client, TransactionSignScheme}; use relay_utils::metrics::MetricsParams; use substrate_relay_helper::messages_lane::{MessagesRelayParams, SubstrateMessageLane}; use substrate_relay_helper::on_demand_headers::OnDemandHeadersRelay; @@ -33,6 +34,14 @@ use substrate_relay_helper::on_demand_headers::OnDemandHeadersRelay; use crate::cli::{relay_messages::RelayerMode, CliChain, HexLaneId, PrometheusParams}; use crate::declare_chain_options; +/// Maximal allowed conversion rate error ratio (abs(real - stored) / stored) that we allow. +/// +/// If it is zero, then transaction will be submitted every time we see difference between +/// stored and real conversion rates. If it is large enough (e.g. > than 10 percents, which is 0.1), +/// then rational relayers may stop relaying messages because they were submitted using +/// lesser conversion rate. +const CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO: f64 = 0.05; + /// Start headers+messages relayer process. #[derive(StructOpt)] pub enum RelayHeadersAndMessages { @@ -68,9 +77,13 @@ macro_rules! declare_bridge_options { #[structopt(flatten)] left_sign: [<$chain1 SigningParams>], #[structopt(flatten)] + left_messages_pallet_owner: [<$chain1 MessagesPalletOwnerSigningParams>], + #[structopt(flatten)] right: [<$chain2 ConnectionParams>], #[structopt(flatten)] right_sign: [<$chain2 SigningParams>], + #[structopt(flatten)] + right_messages_pallet_owner: [<$chain2 MessagesPalletOwnerSigningParams>], } #[allow(unreachable_patterns)] @@ -106,9 +119,11 @@ macro_rules! select_bridge { use crate::chains::millau_messages_to_rialto::{ add_standalone_metrics as add_left_to_right_standalone_metrics, run as left_to_right_messages, + update_rialto_to_millau_conversion_rate as update_right_to_left_conversion_rate, }; use crate::chains::rialto_messages_to_millau::{ add_standalone_metrics as add_right_to_left_standalone_metrics, run as right_to_left_messages, + update_millau_to_rialto_conversion_rate as update_left_to_right_conversion_rate, }; $generic @@ -135,6 +150,22 @@ macro_rules! select_bridge { add_standalone_metrics as add_right_to_left_standalone_metrics, run as right_to_left_messages, }; + async fn update_right_to_left_conversion_rate( + _client: Client, + _signer: ::AccountKeyPair, + _updated_rate: f64, + ) -> anyhow::Result<()> { + Err(anyhow::format_err!("Conversion rate is not supported by this bridge")) + } + + async fn update_left_to_right_conversion_rate( + _client: Client, + _signer: ::AccountKeyPair, + _updated_rate: f64, + ) -> anyhow::Result<()> { + Err(anyhow::format_err!("Conversion rate is not supported by this bridge")) + } + $generic } } @@ -158,16 +189,86 @@ impl RelayHeadersAndMessages { let left_client = params.left.to_client::().await?; let left_sign = params.left_sign.to_keypair::()?; + let left_messages_pallet_owner = params.left_messages_pallet_owner.to_keypair::()?; let right_client = params.right.to_client::().await?; let right_sign = params.right_sign.to_keypair::()?; + let right_messages_pallet_owner = params.right_messages_pallet_owner.to_keypair::()?; let lanes = params.shared.lane; let relayer_mode = params.shared.relayer_mode.into(); + const METRIC_IS_SOME_PROOF: &str = "it is `None` when metric has been already registered; \ + this is the command entrypoint, so nothing has been registered yet; \ + qed"; + let metrics_params: MetricsParams = params.shared.prometheus_params.into(); let metrics_params = relay_utils::relay_metrics(None, metrics_params).into_params(); - let (metrics_params, _) = add_left_to_right_standalone_metrics(None, metrics_params, left_client.clone())?; - let (metrics_params, _) = add_right_to_left_standalone_metrics(None, metrics_params, right_client.clone())?; + let (metrics_params, left_to_right_metrics) = + add_left_to_right_standalone_metrics(None, metrics_params, left_client.clone())?; + let (metrics_params, right_to_left_metrics) = + add_right_to_left_standalone_metrics(None, metrics_params, right_client.clone())?; + if let Some(left_messages_pallet_owner) = left_messages_pallet_owner { + let left_client = left_client.clone(); + substrate_relay_helper::conversion_rate_update::run_conversion_rate_update_loop( + left_to_right_metrics + .target_to_source_conversion_rate + .expect(METRIC_IS_SOME_PROOF), + left_to_right_metrics + .target_to_base_conversion_rate + .clone() + .expect(METRIC_IS_SOME_PROOF), + left_to_right_metrics + .source_to_base_conversion_rate + .clone() + .expect(METRIC_IS_SOME_PROOF), + CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO, + move |new_rate| { + log::info!( + target: "bridge", + "Going to update {} -> {} (on {}) conversion rate to {}.", + Right::NAME, + Left::NAME, + Left::NAME, + new_rate, + ); + update_right_to_left_conversion_rate( + left_client.clone(), + left_messages_pallet_owner.clone(), + new_rate, + ) + }, + ); + } + if let Some(right_messages_pallet_owner) = right_messages_pallet_owner { + let right_client = right_client.clone(); + substrate_relay_helper::conversion_rate_update::run_conversion_rate_update_loop( + right_to_left_metrics + .target_to_source_conversion_rate + .expect(METRIC_IS_SOME_PROOF), + left_to_right_metrics + .source_to_base_conversion_rate + .expect(METRIC_IS_SOME_PROOF), + left_to_right_metrics + .target_to_base_conversion_rate + .expect(METRIC_IS_SOME_PROOF), + CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO, + move |new_rate| { + log::info!( + target: "bridge", + "Going to update {} -> {} (on {}) conversion rate to {}.", + Left::NAME, + Right::NAME, + Right::NAME, + new_rate, + ); + update_left_to_right_conversion_rate( + right_client.clone(), + right_messages_pallet_owner.clone(), + new_rate, + ) + }, + ); + } let left_to_right_on_demand_headers = OnDemandHeadersRelay::new( left_client.clone(), diff --git a/relays/client-rococo/src/runtime.rs b/relays/client-rococo/src/runtime.rs index 77d10529bf2..0a8187b1f05 100644 --- a/relays/client-rococo/src/runtime.rs +++ b/relays/client-rococo/src/runtime.rs @@ -22,9 +22,6 @@ use bp_runtime::Chain; use codec::{Decode, Encode}; use frame_support::weights::Weight; -/// Instance of messages pallet that is used to bridge with Wococo chain. -pub type WithWococoMessagesInstance = pallet_bridge_messages::Instance1; - /// Unchecked Rococo extrinsic. pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic; diff --git a/relays/client-substrate/src/metrics/float_storage_value.rs b/relays/client-substrate/src/metrics/float_storage_value.rs index f3ba8988eea..e5988852278 100644 --- a/relays/client-substrate/src/metrics/float_storage_value.rs +++ b/relays/client-substrate/src/metrics/float_storage_value.rs @@ -17,9 +17,12 @@ use crate::chain::Chain; use crate::client::Client; +use async_std::sync::{Arc, RwLock}; use async_trait::async_trait; use codec::Decode; -use relay_utils::metrics::{metric_name, register, Gauge, PrometheusError, Registry, StandaloneMetrics, F64}; +use relay_utils::metrics::{ + metric_name, register, F64SharedRef, Gauge, PrometheusError, Registry, StandaloneMetrics, F64, +}; use sp_core::storage::StorageKey; use sp_runtime::{traits::UniqueSaturatedInto, FixedPointNumber}; use std::time::Duration; @@ -34,6 +37,7 @@ pub struct FloatStorageValueMetric { storage_key: StorageKey, maybe_default_value: Option, metric: Gauge, + shared_value_ref: F64SharedRef, } impl FloatStorageValueMetric { @@ -47,13 +51,20 @@ impl FloatStorageValueMetric { name: String, help: String, ) -> Result { + let shared_value_ref = Arc::new(RwLock::new(None)); Ok(FloatStorageValueMetric { client, storage_key, maybe_default_value, metric: register(Gauge::new(metric_name(prefix, &name), help)?, registry)?, + shared_value_ref, }) } + + /// Get shared reference to metric value. + pub fn shared_value_ref(&self) -> F64SharedRef { + self.shared_value_ref.clone() + } } #[async_trait] @@ -66,17 +77,17 @@ where } async fn update(&self) { - relay_utils::metrics::set_gauge_value( - &self.metric, - self.client - .storage_value::(self.storage_key.clone()) - .await - .map(|maybe_storage_value| { - maybe_storage_value.or(self.maybe_default_value).map(|storage_value| { - storage_value.into_inner().unique_saturated_into() as f64 - / T::DIV.unique_saturated_into() as f64 - }) - }), - ); + let value = self + .client + .storage_value::(self.storage_key.clone()) + .await + .map(|maybe_storage_value| { + maybe_storage_value.or(self.maybe_default_value).map(|storage_value| { + storage_value.into_inner().unique_saturated_into() as f64 / T::DIV.unique_saturated_into() as f64 + }) + }) + .map_err(drop); + relay_utils::metrics::set_gauge_value(&self.metric, value); + *self.shared_value_ref.write().await = value.ok().and_then(|x| x); } } diff --git a/relays/client-wococo/src/runtime.rs b/relays/client-wococo/src/runtime.rs index e3e7ce7b31d..6a2f7e401ac 100644 --- a/relays/client-wococo/src/runtime.rs +++ b/relays/client-wococo/src/runtime.rs @@ -22,9 +22,6 @@ use bp_runtime::Chain; use codec::{Decode, Encode}; use frame_support::weights::Weight; -/// Instance of messages pallet that is used to bridge with Rococo chain. -pub type WithRococoMessagesInstance = pallet_bridge_messages::DefaultInstance; - /// Unchecked Wococo extrinsic. pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic; diff --git a/relays/lib-substrate-relay/src/conversion_rate_update.rs b/relays/lib-substrate-relay/src/conversion_rate_update.rs new file mode 100644 index 00000000000..ee02672b483 --- /dev/null +++ b/relays/lib-substrate-relay/src/conversion_rate_update.rs @@ -0,0 +1,210 @@ +// 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 . + +//! Tools for updating conversion rate that is stored in the runtime storage. + +use relay_utils::metrics::F64SharedRef; +use std::{future::Future, time::Duration}; + +/// Duration between updater wakeups. +const SLEEP_DURATION: Duration = Duration::from_secs(60); + +/// Update-conversion-rate transaction status. +#[derive(Debug, Clone, Copy, PartialEq)] +enum TransactionStatus { + /// We have not submitted any transaction recently. + Idle, + /// We have recently submitted transaction that should update conversion rate. + Submitted(f64), +} + +/// Run infinite conversion rate updater loop. +/// +/// The loop is maintaining the Left -> Right conversion rate, used as `RightTokens = LeftTokens * Rate`. +pub fn run_conversion_rate_update_loop< + SubmitConversionRateFuture: Future> + Send + 'static, +>( + left_to_right_stored_conversion_rate: F64SharedRef, + left_to_base_conversion_rate: F64SharedRef, + right_to_base_conversion_rate: F64SharedRef, + max_difference_ratio: f64, + submit_conversion_rate: impl Fn(f64) -> SubmitConversionRateFuture + Send + 'static, +) { + async_std::task::spawn(async move { + let mut transaction_status = TransactionStatus::Idle; + loop { + async_std::task::sleep(SLEEP_DURATION).await; + let maybe_new_conversion_rate = maybe_select_new_conversion_rate( + &mut transaction_status, + &left_to_right_stored_conversion_rate, + &left_to_base_conversion_rate, + &right_to_base_conversion_rate, + max_difference_ratio, + ) + .await; + if let Some((prev_conversion_rate, new_conversion_rate)) = maybe_new_conversion_rate { + let submit_conversion_rate_future = submit_conversion_rate(new_conversion_rate); + match submit_conversion_rate_future.await { + Ok(()) => { + transaction_status = TransactionStatus::Submitted(prev_conversion_rate); + } + Err(error) => { + log::trace!(target: "bridge", "Failed to submit conversion rate update transaction: {:?}", error); + } + } + } + } + }); +} + +/// Select new conversion rate to submit to the node. +async fn maybe_select_new_conversion_rate( + transaction_status: &mut TransactionStatus, + left_to_right_stored_conversion_rate: &F64SharedRef, + left_to_base_conversion_rate: &F64SharedRef, + right_to_base_conversion_rate: &F64SharedRef, + max_difference_ratio: f64, +) -> Option<(f64, f64)> { + let left_to_right_stored_conversion_rate = (*left_to_right_stored_conversion_rate.read().await)?; + match *transaction_status { + TransactionStatus::Idle => (), + TransactionStatus::Submitted(previous_left_to_right_stored_conversion_rate) => { + // we can't compare float values from different sources directly, so we only care whether the + // stored rate has been changed or not. If it has been changed, then we assume that our proposal + // has been accepted. + // + // float comparison is ok here, because we compare same-origin (stored in runtime storage) values + // and if they are different, it means that the value has actually been updated + #[allow(clippy::float_cmp)] + if previous_left_to_right_stored_conversion_rate == left_to_right_stored_conversion_rate { + // the rate has not been changed => we won't submit any transactions until it is accepted, + // or the rate is changed by someone else + return None; + } + + *transaction_status = TransactionStatus::Idle; + } + } + + let left_to_base_conversion_rate = (*left_to_base_conversion_rate.read().await)?; + let right_to_base_conversion_rate = (*right_to_base_conversion_rate.read().await)?; + let actual_left_to_right_conversion_rate = right_to_base_conversion_rate / left_to_base_conversion_rate; + + let rate_difference = (actual_left_to_right_conversion_rate - left_to_right_stored_conversion_rate).abs(); + let rate_difference_ratio = rate_difference / left_to_right_stored_conversion_rate; + if rate_difference_ratio < max_difference_ratio { + return None; + } + + Some(( + left_to_right_stored_conversion_rate, + actual_left_to_right_conversion_rate, + )) +} + +#[cfg(test)] +mod tests { + use super::*; + use async_std::sync::{Arc, RwLock}; + + fn test_maybe_select_new_conversion_rate( + mut transaction_status: TransactionStatus, + stored_conversion_rate: Option, + left_to_base_conversion_rate: Option, + right_to_base_conversion_rate: Option, + max_difference_ratio: f64, + ) -> (Option<(f64, f64)>, TransactionStatus) { + let stored_conversion_rate = Arc::new(RwLock::new(stored_conversion_rate)); + let left_to_base_conversion_rate = Arc::new(RwLock::new(left_to_base_conversion_rate)); + let right_to_base_conversion_rate = Arc::new(RwLock::new(right_to_base_conversion_rate)); + let result = async_std::task::block_on(maybe_select_new_conversion_rate( + &mut transaction_status, + &stored_conversion_rate, + &left_to_base_conversion_rate, + &right_to_base_conversion_rate, + max_difference_ratio, + )); + (result, transaction_status) + } + + #[test] + fn rate_is_not_updated_when_transaction_is_submitted() { + assert_eq!( + test_maybe_select_new_conversion_rate( + TransactionStatus::Submitted(10.0), + Some(10.0), + Some(1.0), + Some(1.0), + 0.0 + ), + (None, TransactionStatus::Submitted(10.0)), + ); + } + + #[test] + fn transaction_state_is_changed_to_idle_when_stored_rate_shanges() { + assert_eq!( + test_maybe_select_new_conversion_rate( + TransactionStatus::Submitted(1.0), + Some(10.0), + Some(1.0), + Some(1.0), + 100.0 + ), + (None, TransactionStatus::Idle), + ); + } + + #[test] + fn transaction_is_not_submitted_when_left_to_base_rate_is_unknown() { + assert_eq!( + test_maybe_select_new_conversion_rate(TransactionStatus::Idle, Some(10.0), None, Some(1.0), 0.0), + (None, TransactionStatus::Idle), + ); + } + + #[test] + fn transaction_is_not_submitted_when_right_to_base_rate_is_unknown() { + assert_eq!( + test_maybe_select_new_conversion_rate(TransactionStatus::Idle, Some(10.0), Some(1.0), None, 0.0), + (None, TransactionStatus::Idle), + ); + } + + #[test] + fn transaction_is_not_submitted_when_stored_rate_is_unknown() { + assert_eq!( + test_maybe_select_new_conversion_rate(TransactionStatus::Idle, None, Some(1.0), Some(1.0), 0.0), + (None, TransactionStatus::Idle), + ); + } + + #[test] + fn transaction_is_not_submitted_when_difference_is_below_threshold() { + assert_eq!( + test_maybe_select_new_conversion_rate(TransactionStatus::Idle, Some(1.0), Some(1.0), Some(1.01), 0.02), + (None, TransactionStatus::Idle), + ); + } + + #[test] + fn transaction_is_submitted_when_difference_is_above_threshold() { + assert_eq!( + test_maybe_select_new_conversion_rate(TransactionStatus::Idle, Some(1.0), Some(1.0), Some(1.03), 0.02), + (Some((1.0, 1.03)), TransactionStatus::Idle), + ); + } +} diff --git a/relays/lib-substrate-relay/src/finality_pipeline.rs b/relays/lib-substrate-relay/src/finality_pipeline.rs index deb83d4ea9b..2dc9a639476 100644 --- a/relays/lib-substrate-relay/src/finality_pipeline.rs +++ b/relays/lib-substrate-relay/src/finality_pipeline.rs @@ -35,6 +35,7 @@ pub(crate) const RECENT_FINALITY_PROOFS_LIMIT: usize = 4096; /// Headers sync pipeline for Substrate <-> Substrate relays. pub trait SubstrateFinalitySyncPipeline: 'static + Clone + Debug + Send + Sync { + /// Pipeline for syncing finalized Source chain headers to Target chain. type FinalitySyncPipeline: FinalitySyncPipeline; /// Name of the runtime method that returns id of best finalized source header at target chain. diff --git a/relays/lib-substrate-relay/src/lib.rs b/relays/lib-substrate-relay/src/lib.rs index 32eaa2276e1..380bcfef2db 100644 --- a/relays/lib-substrate-relay/src/lib.rs +++ b/relays/lib-substrate-relay/src/lib.rs @@ -18,6 +18,7 @@ #![warn(missing_docs)] +pub mod conversion_rate_update; pub mod finality_pipeline; pub mod finality_target; pub mod headers_initialize; diff --git a/relays/lib-substrate-relay/src/messages_lane.rs b/relays/lib-substrate-relay/src/messages_lane.rs index fae07c33b3c..0b648e8cc83 100644 --- a/relays/lib-substrate-relay/src/messages_lane.rs +++ b/relays/lib-substrate-relay/src/messages_lane.rs @@ -14,10 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . +//! Tools for supporting message lanes between two Substrate-based chains. + use crate::messages_source::SubstrateMessagesProof; use crate::messages_target::SubstrateMessagesReceivingProof; use crate::on_demand_headers::OnDemandHeadersRelay; +use async_trait::async_trait; use bp_messages::{LaneId, MessageNonce}; use frame_support::weights::Weight; use messages_relay::message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}; @@ -56,7 +59,9 @@ pub struct MessagesRelayParams { } /// Message sync pipeline for Substrate <-> Substrate relays. +#[async_trait] pub trait SubstrateMessageLane: 'static + Clone + Send + Sync { + /// Underlying generic message lane. type MessageLane: MessageLane; /// Name of the runtime method that returns dispatch weight of outbound messages at the source chain. @@ -78,6 +83,11 @@ pub trait SubstrateMessageLane: 'static + Clone + Send + Sync { /// Name of the runtime method that returns id of best finalized target header at source chain. const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str; + /// Name of the messages pallet as it is declared in the `construct_runtime!()` at source chain. + const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str; + /// Name of the messages pallet as it is declared in the `construct_runtime!()` at target chain. + const MESSAGE_PALLET_NAME_AT_TARGET: &'static str; + /// Source chain. type SourceChain: Chain; /// Target chain. @@ -203,6 +213,8 @@ pub struct StandaloneMessagesMetrics { pub target_to_base_conversion_rate: Option, /// Shared reference to the actual source -> chain token conversion rate. pub source_to_base_conversion_rate: Option, + /// Shared reference to the stored (in the source chain runtime storage) target -> source chain conversion rate. + pub target_to_source_conversion_rate: Option, } impl StandaloneMessagesMetrics { @@ -210,7 +222,7 @@ impl StandaloneMessagesMetrics { pub async fn target_to_source_conversion_rate(&self) -> Option { let target_to_base_conversion_rate = (*self.target_to_base_conversion_rate.as_ref()?.read().await)?; let source_to_base_conversion_rate = (*self.source_to_base_conversion_rate.as_ref()?.read().await)?; - Some(target_to_base_conversion_rate / source_to_base_conversion_rate) + Some(source_to_base_conversion_rate / target_to_base_conversion_rate) } } @@ -223,6 +235,7 @@ pub fn add_standalone_metrics( target_chain_token_id: Option<&str>, target_to_source_conversion_rate_params: Option<(StorageKey, FixedU128)>, ) -> anyhow::Result<(MetricsParams, StandaloneMessagesMetrics)> { + let mut target_to_source_conversion_rate = None; let mut source_to_base_conversion_rate = None; let mut target_to_base_conversion_rate = None; let mut metrics_params = @@ -258,6 +271,7 @@ pub fn add_standalone_metrics( P::SourceChain::NAME ), )?; + target_to_source_conversion_rate = Some(metric.shared_value_ref()); Ok(metric) })?; } @@ -280,6 +294,7 @@ pub fn add_standalone_metrics( StandaloneMessagesMetrics { target_to_base_conversion_rate, source_to_base_conversion_rate, + target_to_source_conversion_rate, }, )) } @@ -287,6 +302,7 @@ pub fn add_standalone_metrics( #[cfg(test)] mod tests { use super::*; + use async_std::sync::{Arc, RwLock}; type RialtoToMillauMessagesWeights = pallet_bridge_messages::weights::RialtoWeight; @@ -306,4 +322,15 @@ mod tests { (782, 216_583_333_334), ); } + + #[async_std::test] + async fn target_to_source_conversion_rate_works() { + let metrics = StandaloneMessagesMetrics { + target_to_base_conversion_rate: Some(Arc::new(RwLock::new(Some(183.15)))), + source_to_base_conversion_rate: Some(Arc::new(RwLock::new(Some(12.32)))), + target_to_source_conversion_rate: None, // we don't care + }; + + assert_eq!(metrics.target_to_source_conversion_rate().await, Some(12.32 / 183.15),); + } } diff --git a/relays/lib-substrate-relay/src/messages_source.rs b/relays/lib-substrate-relay/src/messages_source.rs index 279b2c45e0f..a442ebfb444 100644 --- a/relays/lib-substrate-relay/src/messages_source.rs +++ b/relays/lib-substrate-relay/src/messages_source.rs @@ -24,12 +24,12 @@ use crate::on_demand_headers::OnDemandHeadersRelay; use async_trait::async_trait; use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState}; -use bp_runtime::{messages::DispatchFeePayment, ChainId}; +use bp_runtime::messages::DispatchFeePayment; use bridge_runtime_common::messages::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, }; use codec::{Decode, Encode}; -use frame_support::{traits::Instance, weights::Weight}; +use frame_support::weights::Weight; use messages_relay::message_lane::MessageLane; use messages_relay::{ message_lane::{SourceHeaderIdOf, TargetHeaderIdOf}, @@ -42,7 +42,7 @@ use relay_substrate_client::{Chain, Client, Error as SubstrateError, HashOf, Hea use relay_utils::{relay_loop::Client as RelayClient, BlockNumberBase, HeaderId}; use sp_core::Bytes; use sp_runtime::{traits::Header as HeaderT, DeserializeOwned}; -use std::{marker::PhantomData, ops::RangeInclusive}; +use std::ops::RangeInclusive; /// Intermediate message proof returned by the source Substrate node. Includes everything /// required to submit to the target node: cumulative dispatch weight of bundled messages and @@ -50,55 +50,47 @@ use std::{marker::PhantomData, ops::RangeInclusive}; pub type SubstrateMessagesProof = (Weight, FromBridgedChainMessagesProof>); /// Substrate client as Substrate messages source. -pub struct SubstrateMessagesSource { +pub struct SubstrateMessagesSource { client: Client, lane: P, lane_id: LaneId, - instance: ChainId, target_to_source_headers_relay: Option>, - _phantom: PhantomData, } -impl SubstrateMessagesSource { +impl SubstrateMessagesSource { /// Create new Substrate headers source. pub fn new( client: Client, lane: P, lane_id: LaneId, - instance: ChainId, target_to_source_headers_relay: Option>, ) -> Self { SubstrateMessagesSource { client, lane, lane_id, - instance, target_to_source_headers_relay, - _phantom: Default::default(), } } } -impl Clone for SubstrateMessagesSource { +impl Clone for SubstrateMessagesSource { fn clone(&self) -> Self { Self { client: self.client.clone(), lane: self.lane.clone(), lane_id: self.lane_id, - instance: self.instance, target_to_source_headers_relay: self.target_to_source_headers_relay.clone(), - _phantom: Default::default(), } } } #[async_trait] -impl RelayClient for SubstrateMessagesSource +impl RelayClient for SubstrateMessagesSource where SC: Chain, TC: Chain, P: SubstrateMessageLane, - I: Send + Sync + Instance, { type Error = SubstrateError; @@ -108,7 +100,7 @@ where } #[async_trait] -impl SourceClient for SubstrateMessagesSource +impl SourceClient for SubstrateMessagesSource where SC: Chain< Hash = ::SourceHeaderHash, @@ -132,7 +124,6 @@ where >, ::TargetHeaderNumber: Decode, ::TargetHeaderHash: Decode, - I: Send + Sync + Instance, { async fn state(&self) -> Result, SubstrateError> { // we can't continue to deliver confirmations if source node is out of sync, because @@ -217,12 +208,17 @@ where let mut storage_keys = Vec::with_capacity(nonces.end().saturating_sub(*nonces.start()) as usize + 1); let mut message_nonce = *nonces.start(); while message_nonce <= *nonces.end() { - let message_key = pallet_bridge_messages::storage_keys::message_key::(&self.lane_id, message_nonce); + let message_key = pallet_bridge_messages::storage_keys::message_key( + P::MESSAGE_PALLET_NAME_AT_SOURCE, + &self.lane_id, + message_nonce, + ); storage_keys.push(message_key); message_nonce += 1; } if proof_parameters.outbound_state_proof_required { - storage_keys.push(pallet_bridge_messages::storage_keys::outbound_lane_data_key::( + storage_keys.push(pallet_bridge_messages::storage_keys::outbound_lane_data_key( + P::MESSAGE_PALLET_NAME_AT_SOURCE, &self.lane_id, )); } @@ -298,6 +294,11 @@ fn prepare_dummy_messages_delivery_proof() -> SubstrateMes ) } +/// Read best blocks from given client. +/// +/// This function assumes that the chain that is followed by the `self_client` has +/// bridge GRANDPA pallet deployed and it provides `best_finalized_header_id_method_name` +/// runtime API to read best finalized Bridged chain header. pub async fn read_client_state( self_client: &Client, best_finalized_header_id_method_name: &str, diff --git a/relays/lib-substrate-relay/src/messages_target.rs b/relays/lib-substrate-relay/src/messages_target.rs index 4cafcc4866f..1db83459319 100644 --- a/relays/lib-substrate-relay/src/messages_target.rs +++ b/relays/lib-substrate-relay/src/messages_target.rs @@ -24,12 +24,11 @@ use crate::on_demand_headers::OnDemandHeadersRelay; use async_trait::async_trait; use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState}; -use bp_runtime::ChainId; use bridge_runtime_common::messages::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, }; use codec::{Decode, Encode}; -use frame_support::{traits::Instance, weights::Weight}; +use frame_support::weights::Weight; use messages_relay::message_lane::MessageLane; use messages_relay::{ message_lane::{SourceHeaderIdOf, TargetHeaderIdOf}, @@ -40,7 +39,7 @@ use relay_substrate_client::{Chain, Client, Error as SubstrateError, HashOf}; use relay_utils::{relay_loop::Client as RelayClient, BlockNumberBase, HeaderId}; use sp_core::Bytes; use sp_runtime::{traits::Header as HeaderT, DeserializeOwned, FixedPointNumber, FixedU128}; -use std::{convert::TryFrom, marker::PhantomData, ops::RangeInclusive}; +use std::{convert::TryFrom, ops::RangeInclusive}; /// Message receiving proof returned by the target Substrate node. pub type SubstrateMessagesReceivingProof = ( @@ -49,23 +48,20 @@ pub type SubstrateMessagesReceivingProof = ( ); /// Substrate client as Substrate messages target. -pub struct SubstrateMessagesTarget { +pub struct SubstrateMessagesTarget { client: Client, lane: P, lane_id: LaneId, - instance: ChainId, metric_values: StandaloneMessagesMetrics, source_to_target_headers_relay: Option>, - _phantom: PhantomData, } -impl SubstrateMessagesTarget { +impl SubstrateMessagesTarget { /// Create new Substrate headers target. pub fn new( client: Client, lane: P, lane_id: LaneId, - instance: ChainId, metric_values: StandaloneMessagesMetrics, source_to_target_headers_relay: Option>, ) -> Self { @@ -73,35 +69,30 @@ impl SubstrateMessagesTarget Clone for SubstrateMessagesTarget { +impl Clone for SubstrateMessagesTarget { fn clone(&self) -> Self { Self { client: self.client.clone(), lane: self.lane.clone(), lane_id: self.lane_id, - instance: self.instance, metric_values: self.metric_values.clone(), source_to_target_headers_relay: self.source_to_target_headers_relay.clone(), - _phantom: Default::default(), } } } #[async_trait] -impl RelayClient for SubstrateMessagesTarget +impl RelayClient for SubstrateMessagesTarget where SC: Chain, TC: Chain, P: SubstrateMessageLane, - I: Send + Sync + Instance, { type Error = SubstrateError; @@ -111,7 +102,7 @@ where } #[async_trait] -impl TargetClient for SubstrateMessagesTarget +impl TargetClient for SubstrateMessagesTarget where SC: Chain< Hash = ::SourceHeaderHash, @@ -135,7 +126,6 @@ where >, ::SourceHeaderNumber: Decode, ::SourceHeaderHash: Decode, - I: Send + Sync + Instance, { async fn state(&self) -> Result, SubstrateError> { // we can't continue to deliver messages if target node is out of sync, because @@ -212,7 +202,10 @@ where SubstrateError, > { let (id, relayers_state) = self.unrewarded_relayers_state(id).await?; - let inbound_data_key = pallet_bridge_messages::storage_keys::inbound_lane_data_key::(&self.lane_id); + let inbound_data_key = pallet_bridge_messages::storage_keys::inbound_lane_data_key( + P::MESSAGE_PALLET_NAME_AT_TARGET, + &self.lane_id, + ); let proof = self .client .prove_storage(vec![inbound_data_key], id.1)