diff --git a/bridges/primitives/polkadot-core/src/lib.rs b/bridges/primitives/polkadot-core/src/lib.rs index 4744e29492..ab4e0f5947 100644 --- a/bridges/primitives/polkadot-core/src/lib.rs +++ b/bridges/primitives/polkadot-core/src/lib.rs @@ -235,26 +235,26 @@ impl parity_scale_codec::Decode for SignedExtensions { impl SignedExtensions { pub fn new( version: sp_version::RuntimeVersion, - era: sp_runtime::generic::Era, + era: bp_runtime::TransactionEraOf, genesis_hash: Hash, nonce: Nonce, tip: Balance, ) -> Self { Self { encode_payload: ( - (), // spec version - (), // tx version - (), // genesis - era, // era - nonce.into(), // nonce (compact encoding) - (), // Check weight - tip.into(), // transaction payment / tip (compact encoding) + (), // spec version + (), // tx version + (), // genesis + era.frame_era(), // era + nonce.into(), // nonce (compact encoding) + (), // Check weight + tip.into(), // transaction payment / tip (compact encoding) ), additional_signed: ( version.spec_version, version.transaction_version, genesis_hash, - genesis_hash, + era.signed_payload(genesis_hash), (), (), (), diff --git a/bridges/primitives/runtime/src/chain.rs b/bridges/primitives/runtime/src/chain.rs index cb19c6e726..270e2d7b2f 100644 --- a/bridges/primitives/runtime/src/chain.rs +++ b/bridges/primitives/runtime/src/chain.rs @@ -42,7 +42,10 @@ pub trait Chain: Send + Sync + 'static { + FromStr + MaybeMallocSizeOf + AsPrimitive - + Default; + + Default + // original `sp_runtime::traits::Header::BlockNumber` doesn't have this trait, but + // `sp_runtime::generic::Era` requires block number -> `u64` conversion. + + Into; /// A type that fulfills the abstract idea of what a Substrate hash is. // Constraits come from the associated Hash type of `sp_runtime::traits::Header` @@ -85,3 +88,6 @@ pub type HasherOf = ::Hasher; /// Header type used by the chain. pub type HeaderOf = ::Header; + +/// Transaction era used by the chain. +pub type TransactionEraOf = crate::TransactionEra, HashOf>; diff --git a/bridges/primitives/runtime/src/lib.rs b/bridges/primitives/runtime/src/lib.rs index ed915b7288..fa1586c792 100644 --- a/bridges/primitives/runtime/src/lib.rs +++ b/bridges/primitives/runtime/src/lib.rs @@ -19,11 +19,12 @@ #![cfg_attr(not(feature = "std"), no_std)] use codec::Encode; +use frame_support::RuntimeDebug; use sp_core::hash::H256; use sp_io::hashing::blake2_256; use sp_std::convert::TryFrom; -pub use chain::{BlockNumberOf, Chain, HashOf, HasherOf, HeaderOf}; +pub use chain::{BlockNumberOf, Chain, HashOf, HasherOf, HeaderOf, TransactionEraOf}; pub use storage_proof::{Error as StorageProofError, StorageProofChecker}; #[cfg(feature = "std")] @@ -138,3 +139,44 @@ impl Size for PreComputedSize { u32::try_from(self.0).unwrap_or(u32::MAX) } } + +/// Era of specific transaction. +#[derive(RuntimeDebug, Clone, Copy)] +pub enum TransactionEra { + /// Transaction is immortal. + Immortal, + /// Transaction is valid for given number of blocks, starting from given block. + Mortal(BlockNumber, BlockHash, u32), +} + +impl, BlockHash: Copy> TransactionEra { + /// Prepare transaction era, based on mortality period and current best block number. + pub fn new(best_block_number: BlockNumber, best_block_hash: BlockHash, mortality_period: Option) -> Self { + mortality_period + .map(|mortality_period| TransactionEra::Mortal(best_block_number, best_block_hash, mortality_period)) + .unwrap_or(TransactionEra::Immortal) + } + + /// Create new immortal transaction era. + pub fn immortal() -> Self { + TransactionEra::Immortal + } + + /// Returns era that is used by FRAME-based runtimes. + pub fn frame_era(&self) -> sp_runtime::generic::Era { + match *self { + TransactionEra::Immortal => sp_runtime::generic::Era::immortal(), + TransactionEra::Mortal(header_number, _, period) => { + sp_runtime::generic::Era::mortal(period as _, header_number.into()) + } + } + } + + /// Returns header hash that needs to be included in the signature payload. + pub fn signed_payload(&self, genesis_hash: BlockHash) -> BlockHash { + match *self { + TransactionEra::Immortal => genesis_hash, + TransactionEra::Mortal(_, header_hash, _) => header_hash, + } + } +} diff --git a/bridges/relays/bin-ethereum/src/rialto_client.rs b/bridges/relays/bin-ethereum/src/rialto_client.rs index 311bc9edbc..b340c4d82a 100644 --- a/bridges/relays/bin-ethereum/src/rialto_client.rs +++ b/bridges/relays/bin-ethereum/src/rialto_client.rs @@ -157,11 +157,12 @@ impl SubmitEthereumHeaders for SubstrateClient { let ids = headers.iter().map(|header| header.id()).collect(); let genesis_hash = *self.genesis_hash(); let submission_result = async { - self.submit_signed_extrinsic((*params.public().as_array_ref()).into(), move |transaction_nonce| { + self.submit_signed_extrinsic((*params.public().as_array_ref()).into(), move |_, transaction_nonce| { Bytes( Rialto::sign_transaction( genesis_hash, ¶ms, + relay_substrate_client::TransactionEra::immortal(), transaction_nonce, instance.build_signed_header_call(headers), ) @@ -259,11 +260,12 @@ impl SubmitEthereumExchangeTransactionProof for SubstrateClient { proof: rialto_runtime::exchange::EthereumTransactionInclusionProof, ) -> RpcResult<()> { let genesis_hash = *self.genesis_hash(); - self.submit_signed_extrinsic((*params.public().as_array_ref()).into(), move |transaction_nonce| { + self.submit_signed_extrinsic((*params.public().as_array_ref()).into(), move |_, transaction_nonce| { Bytes( Rialto::sign_transaction( genesis_hash, ¶ms, + relay_substrate_client::TransactionEra::immortal(), transaction_nonce, instance.build_currency_exchange_call(proof), ) diff --git a/bridges/relays/bin-substrate/src/chains/millau_headers_to_rialto.rs b/bridges/relays/bin-substrate/src/chains/millau_headers_to_rialto.rs index 5be5083f5b..75d7b49c40 100644 --- a/bridges/relays/bin-substrate/src/chains/millau_headers_to_rialto.rs +++ b/bridges/relays/bin-substrate/src/chains/millau_headers_to_rialto.rs @@ -54,6 +54,7 @@ impl SubstrateFinalitySyncPipeline for MillauFinalityToRialto { fn make_submit_finality_proof_transaction( &self, + era: bp_runtime::TransactionEraOf, transaction_nonce: ::Index, header: MillauSyncHeader, proof: GrandpaJustification, @@ -64,6 +65,7 @@ impl SubstrateFinalitySyncPipeline for MillauFinalityToRialto { let transaction = Rialto::sign_transaction( genesis_hash, &self.finality_pipeline.target_sign, + era, transaction_nonce, call, ); diff --git a/bridges/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs b/bridges/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs index b63d57aeb7..6fbce3d314 100644 --- a/bridges/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs +++ b/bridges/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs @@ -82,8 +82,13 @@ impl SubstrateMessageLane for MillauMessagesToRialto { millau_runtime::MessagesCall::receive_messages_delivery_proof(proof, relayers_state).into(); let call_weight = call.get_dispatch_info().weight; let genesis_hash = *self.message_lane.source_client.genesis_hash(); - let transaction = - Millau::sign_transaction(genesis_hash, &self.message_lane.source_sign, transaction_nonce, call); + let transaction = Millau::sign_transaction( + genesis_hash, + &self.message_lane.source_sign, + relay_substrate_client::TransactionEra::immortal(), + transaction_nonce, + call, + ); log::trace!( target: "bridge", "Prepared Rialto -> Millau confirmation transaction. Weight: {}/{}, size: {}/{}", @@ -122,8 +127,13 @@ impl SubstrateMessageLane for MillauMessagesToRialto { .into(); let call_weight = call.get_dispatch_info().weight; let genesis_hash = *self.message_lane.target_client.genesis_hash(); - let transaction = - Rialto::sign_transaction(genesis_hash, &self.message_lane.target_sign, transaction_nonce, call); + let transaction = Rialto::sign_transaction( + genesis_hash, + &self.message_lane.target_sign, + relay_substrate_client::TransactionEra::immortal(), + transaction_nonce, + call, + ); log::trace!( target: "bridge", "Prepared Millau -> Rialto delivery transaction. Weight: {}/{}, size: {}/{}", diff --git a/bridges/relays/bin-substrate/src/chains/mod.rs b/bridges/relays/bin-substrate/src/chains/mod.rs index 8a9cb780b4..0d655698c5 100644 --- a/bridges/relays/bin-substrate/src/chains/mod.rs +++ b/bridges/relays/bin-substrate/src/chains/mod.rs @@ -214,6 +214,7 @@ mod tests { let rialto_tx = Rialto::sign_transaction( Default::default(), &sp_keyring::AccountKeyring::Alice.pair(), + relay_substrate_client::TransactionEra::immortal(), 0, rialto_call.clone(), ); @@ -232,6 +233,7 @@ mod tests { let millau_tx = Millau::sign_transaction( Default::default(), &sp_keyring::AccountKeyring::Alice.pair(), + relay_substrate_client::TransactionEra::immortal(), 0, millau_call.clone(), ); diff --git a/bridges/relays/bin-substrate/src/chains/rialto_headers_to_millau.rs b/bridges/relays/bin-substrate/src/chains/rialto_headers_to_millau.rs index 2604e79983..69b750c542 100644 --- a/bridges/relays/bin-substrate/src/chains/rialto_headers_to_millau.rs +++ b/bridges/relays/bin-substrate/src/chains/rialto_headers_to_millau.rs @@ -55,6 +55,7 @@ impl SubstrateFinalitySyncPipeline for RialtoFinalityToMillau { fn make_submit_finality_proof_transaction( &self, + era: bp_runtime::TransactionEraOf, transaction_nonce: ::Index, header: RialtoSyncHeader, proof: GrandpaJustification, @@ -69,6 +70,7 @@ impl SubstrateFinalitySyncPipeline for RialtoFinalityToMillau { let transaction = Millau::sign_transaction( genesis_hash, &self.finality_pipeline.target_sign, + era, transaction_nonce, call, ); diff --git a/bridges/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs b/bridges/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs index 1f861d1bb5..986b7519e1 100644 --- a/bridges/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs +++ b/bridges/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs @@ -82,8 +82,13 @@ impl SubstrateMessageLane for RialtoMessagesToMillau { rialto_runtime::MessagesCall::receive_messages_delivery_proof(proof, relayers_state).into(); let call_weight = call.get_dispatch_info().weight; let genesis_hash = *self.message_lane.source_client.genesis_hash(); - let transaction = - Rialto::sign_transaction(genesis_hash, &self.message_lane.source_sign, transaction_nonce, call); + let transaction = Rialto::sign_transaction( + genesis_hash, + &self.message_lane.source_sign, + relay_substrate_client::TransactionEra::immortal(), + transaction_nonce, + call, + ); log::trace!( target: "bridge", "Prepared Millau -> Rialto confirmation transaction. Weight: {}/{}, size: {}/{}", @@ -122,8 +127,13 @@ impl SubstrateMessageLane for RialtoMessagesToMillau { .into(); let call_weight = call.get_dispatch_info().weight; let genesis_hash = *self.message_lane.target_client.genesis_hash(); - let transaction = - Millau::sign_transaction(genesis_hash, &self.message_lane.target_sign, transaction_nonce, call); + let transaction = Millau::sign_transaction( + genesis_hash, + &self.message_lane.target_sign, + relay_substrate_client::TransactionEra::immortal(), + transaction_nonce, + call, + ); log::trace!( target: "bridge", "Prepared Rialto -> Millau delivery transaction. Weight: {}/{}, size: {}/{}", diff --git a/bridges/relays/bin-substrate/src/chains/rococo_headers_to_wococo.rs b/bridges/relays/bin-substrate/src/chains/rococo_headers_to_wococo.rs index 38bb54ddbc..3cacce9a48 100644 --- a/bridges/relays/bin-substrate/src/chains/rococo_headers_to_wococo.rs +++ b/bridges/relays/bin-substrate/src/chains/rococo_headers_to_wococo.rs @@ -77,6 +77,7 @@ impl SubstrateFinalitySyncPipeline for RococoFinalityToWococo { fn make_submit_finality_proof_transaction( &self, + era: bp_runtime::TransactionEraOf, transaction_nonce: ::Index, header: RococoSyncHeader, proof: GrandpaJustification, @@ -88,6 +89,7 @@ impl SubstrateFinalitySyncPipeline for RococoFinalityToWococo { let transaction = Wococo::sign_transaction( genesis_hash, &self.finality_pipeline.target_sign, + era, transaction_nonce, call, ); diff --git a/bridges/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs b/bridges/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs index 3abedc0ca3..7cc4d0f725 100644 --- a/bridges/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs +++ b/bridges/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs @@ -84,8 +84,13 @@ impl SubstrateMessageLane for RococoMessagesToWococo { ), ); let genesis_hash = *self.message_lane.source_client.genesis_hash(); - let transaction = - Rococo::sign_transaction(genesis_hash, &self.message_lane.source_sign, transaction_nonce, call); + let transaction = Rococo::sign_transaction( + genesis_hash, + &self.message_lane.source_sign, + relay_substrate_client::TransactionEra::immortal(), + transaction_nonce, + call, + ); log::trace!( target: "bridge", "Prepared Wococo -> Rococo confirmation transaction. Weight: /{}, size: {}/{}", @@ -124,8 +129,13 @@ impl SubstrateMessageLane for RococoMessagesToWococo { ), ); let genesis_hash = *self.message_lane.target_client.genesis_hash(); - let transaction = - Wococo::sign_transaction(genesis_hash, &self.message_lane.target_sign, transaction_nonce, call); + let transaction = Wococo::sign_transaction( + genesis_hash, + &self.message_lane.target_sign, + relay_substrate_client::TransactionEra::immortal(), + transaction_nonce, + call, + ); log::trace!( target: "bridge", "Prepared Rococo -> Wococo delivery transaction. Weight: /{}, size: {}/{}", diff --git a/bridges/relays/bin-substrate/src/chains/westend_headers_to_millau.rs b/bridges/relays/bin-substrate/src/chains/westend_headers_to_millau.rs index 9fdf8b1a81..20464b99bf 100644 --- a/bridges/relays/bin-substrate/src/chains/westend_headers_to_millau.rs +++ b/bridges/relays/bin-substrate/src/chains/westend_headers_to_millau.rs @@ -63,6 +63,7 @@ impl SubstrateFinalitySyncPipeline for WestendFinalityToMillau { fn make_submit_finality_proof_transaction( &self, + era: bp_runtime::TransactionEraOf, transaction_nonce: ::Index, header: WestendSyncHeader, proof: GrandpaJustification, @@ -77,6 +78,7 @@ impl SubstrateFinalitySyncPipeline for WestendFinalityToMillau { let transaction = Millau::sign_transaction( genesis_hash, &self.finality_pipeline.target_sign, + era, transaction_nonce, call, ); diff --git a/bridges/relays/bin-substrate/src/chains/wococo_headers_to_rococo.rs b/bridges/relays/bin-substrate/src/chains/wococo_headers_to_rococo.rs index a65bfd7624..4bb5b15fe0 100644 --- a/bridges/relays/bin-substrate/src/chains/wococo_headers_to_rococo.rs +++ b/bridges/relays/bin-substrate/src/chains/wococo_headers_to_rococo.rs @@ -82,6 +82,7 @@ impl SubstrateFinalitySyncPipeline for WococoFinalityToRococo { fn make_submit_finality_proof_transaction( &self, + era: bp_runtime::TransactionEraOf, transaction_nonce: ::Index, header: WococoSyncHeader, proof: GrandpaJustification, @@ -93,6 +94,7 @@ impl SubstrateFinalitySyncPipeline for WococoFinalityToRococo { let transaction = Rococo::sign_transaction( genesis_hash, &self.finality_pipeline.target_sign, + era, transaction_nonce, call, ); diff --git a/bridges/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs b/bridges/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs index d4cff289e2..16a8858ac9 100644 --- a/bridges/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs +++ b/bridges/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs @@ -83,8 +83,13 @@ impl SubstrateMessageLane for WococoMessagesToRococo { ), ); let genesis_hash = *self.message_lane.source_client.genesis_hash(); - let transaction = - Wococo::sign_transaction(genesis_hash, &self.message_lane.source_sign, transaction_nonce, call); + let transaction = Wococo::sign_transaction( + genesis_hash, + &self.message_lane.source_sign, + relay_substrate_client::TransactionEra::immortal(), + transaction_nonce, + call, + ); log::trace!( target: "bridge", "Prepared Rococo -> Wococo confirmation transaction. Weight: /{}, size: {}/{}", @@ -123,8 +128,13 @@ impl SubstrateMessageLane for WococoMessagesToRococo { ), ); let genesis_hash = *self.message_lane.target_client.genesis_hash(); - let transaction = - Rococo::sign_transaction(genesis_hash, &self.message_lane.target_sign, transaction_nonce, call); + let transaction = Rococo::sign_transaction( + genesis_hash, + &self.message_lane.target_sign, + relay_substrate_client::TransactionEra::immortal(), + transaction_nonce, + call, + ); log::trace!( target: "bridge", "Prepared Wococo -> Rococo delivery transaction. Weight: /{}, size: {}/{}", diff --git a/bridges/relays/bin-substrate/src/cli/init_bridge.rs b/bridges/relays/bin-substrate/src/cli/init_bridge.rs index 846d3a575e..76bc5aa273 100644 --- a/bridges/relays/bin-substrate/src/cli/init_bridge.rs +++ b/bridges/relays/bin-substrate/src/cli/init_bridge.rs @@ -150,6 +150,7 @@ impl InitBridge { Target::sign_transaction( *target_client.genesis_hash(), &target_sign, + relay_substrate_client::TransactionEra::immortal(), transaction_nonce, encode_init_bridge(initialization_data), ) diff --git a/bridges/relays/bin-substrate/src/cli/mod.rs b/bridges/relays/bin-substrate/src/cli/mod.rs index 832b530877..c02f6b5130 100644 --- a/bridges/relays/bin-substrate/src/cli/mod.rs +++ b/bridges/relays/bin-substrate/src/cli/mod.rs @@ -389,6 +389,10 @@ macro_rules! declare_chain_options { #[doc = "Path to the file, that password for the SURI of secret key to use when transactions are submitted to the " $chain " node. Can be overridden with " $chain_prefix "_signer_password option."] #[structopt(long)] pub [<$chain_prefix _signer_password_file>]: Option, + + #[doc = "Transactions mortality period, in blocks. MUST be a power of two in [4; 65536] range. MAY NOT be larger than `BlockHashCount` parameter of the chain system module."] + #[structopt(long)] + pub [<$chain_prefix _transactions_mortality>]: Option, } #[doc = "Parameters required to sign transaction on behalf of owner of the messages pallet at " $chain "."] @@ -403,6 +407,25 @@ macro_rules! declare_chain_options { } impl [<$chain SigningParams>] { + /// Return transactions mortality. + #[allow(dead_code)] + pub fn transactions_mortality(&self) -> anyhow::Result> { + self.[<$chain_prefix _transactions_mortality>] + .map(|transactions_mortality| { + if !(4..=65536).contains(&transactions_mortality) + || !transactions_mortality.is_power_of_two() + { + Err(anyhow::format_err!( + "Transactions mortality {} is not a power of two in a [4; 65536] range", + transactions_mortality, + )) + } else { + Ok(transactions_mortality) + } + }) + .transpose() + } + /// Parse signing params into chain-specific KeyPair. pub fn to_keypair(&self) -> anyhow::Result { let suri = match (self.[<$chain_prefix _signer>].as_ref(), self.[<$chain_prefix _signer_file>].as_ref()) { @@ -550,6 +573,8 @@ mod tests { target_signer_file: None, target_signer_password_file: None, + + target_transactions_mortality: None, } .to_keypair::() .map(|p| p.public()) @@ -565,6 +590,8 @@ mod tests { target_signer_file: Some(suri_file_path.clone()), target_signer_password_file: Some(password_file_path.clone()), + + target_transactions_mortality: None, } .to_keypair::() .map(|p| p.public()) @@ -580,6 +607,8 @@ mod tests { target_signer_file: Some(suri_file_path.clone()), target_signer_password_file: Some(password_file_path.clone()), + + target_transactions_mortality: None, } .to_keypair::() .map(|p| p.public()) @@ -595,6 +624,8 @@ mod tests { target_signer_file: Some(suri_file_path), target_signer_password_file: Some(password_file_path), + + target_transactions_mortality: None, } .to_keypair::() .map(|p| p.public()) diff --git a/bridges/relays/bin-substrate/src/cli/relay_headers.rs b/bridges/relays/bin-substrate/src/cli/relay_headers.rs index 404e571c2c..0e527eb5b9 100644 --- a/bridges/relays/bin-substrate/src/cli/relay_headers.rs +++ b/bridges/relays/bin-substrate/src/cli/relay_headers.rs @@ -99,6 +99,7 @@ impl RelayHeaders { select_bridge!(self.bridge, { let source_client = self.source.to_client::().await?; let target_client = self.target.to_client::().await?; + let target_transactions_mortality = self.target_sign.target_transactions_mortality; let target_sign = self.target_sign.to_keypair::()?; let metrics_params = Finality::customize_metrics(self.prometheus_params.into())?; let finality = Finality::new(target_client.clone(), target_sign); @@ -109,6 +110,7 @@ impl RelayHeaders { source_client, target_client, self.only_mandatory_headers, + target_transactions_mortality, metrics_params, ) .await diff --git a/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs b/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs index a2f0d236a9..236b2b964b 100644 --- a/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs +++ b/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs @@ -188,9 +188,11 @@ impl RelayHeadersAndMessages { let params: Params = self.into(); let left_client = params.left.to_client::().await?; + let left_transactions_mortality = params.left_sign.transactions_mortality()?; 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_transactions_mortality = params.right_sign.transactions_mortality()?; let right_sign = params.right_sign.to_keypair::()?; let right_messages_pallet_owner = params.right_messages_pallet_owner.to_keypair::()?; @@ -273,12 +275,14 @@ impl RelayHeadersAndMessages { let left_to_right_on_demand_headers = OnDemandHeadersRelay::new( left_client.clone(), right_client.clone(), + right_transactions_mortality, LeftToRightFinality::new(right_client.clone(), right_sign.clone()), MAX_MISSING_LEFT_HEADERS_AT_RIGHT, ); let right_to_left_on_demand_headers = OnDemandHeadersRelay::new( right_client.clone(), left_client.clone(), + left_transactions_mortality, RightToLeftFinality::new(left_client.clone(), left_sign.clone()), MAX_MISSING_RIGHT_HEADERS_AT_LEFT, ); diff --git a/bridges/relays/bin-substrate/src/cli/send_message.rs b/bridges/relays/bin-substrate/src/cli/send_message.rs index 6259c85b50..75c3118eb4 100644 --- a/bridges/relays/bin-substrate/src/cli/send_message.rs +++ b/bridges/relays/bin-substrate/src/cli/send_message.rs @@ -178,10 +178,11 @@ impl SendMessage { let source_genesis_hash = *source_client.genesis_hash(); source_client - .submit_signed_extrinsic(source_sign.public().into(), move |transaction_nonce| { + .submit_signed_extrinsic(source_sign.public().into(), move |_, transaction_nonce| { let signed_source_call = Source::sign_transaction( source_genesis_hash, &source_sign, + relay_substrate_client::TransactionEra::immortal(), transaction_nonce, send_message_call, ) diff --git a/bridges/relays/client-millau/src/lib.rs b/bridges/relays/client-millau/src/lib.rs index ac4368392c..ce1ab87014 100644 --- a/bridges/relays/client-millau/src/lib.rs +++ b/bridges/relays/client-millau/src/lib.rs @@ -66,6 +66,7 @@ impl TransactionSignScheme for Millau { fn sign_transaction( genesis_hash: ::Hash, signer: &Self::AccountKeyPair, + era: relay_substrate_client::TransactionEraOf, signer_nonce: ::Index, call: ::Call, ) -> Self::SignedTransaction { @@ -75,7 +76,7 @@ impl TransactionSignScheme for Millau { frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), frame_system::CheckGenesis::::new(), - frame_system::CheckEra::::from(sp_runtime::generic::Era::Immortal), + frame_system::CheckEra::::from(era.frame_era()), frame_system::CheckNonce::::from(signer_nonce), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(0), @@ -84,7 +85,7 @@ impl TransactionSignScheme for Millau { millau_runtime::VERSION.spec_version, millau_runtime::VERSION.transaction_version, genesis_hash, - genesis_hash, + era.signed_payload(genesis_hash), (), (), (), diff --git a/bridges/relays/client-rialto/src/lib.rs b/bridges/relays/client-rialto/src/lib.rs index 646d762923..aaa62eea0e 100644 --- a/bridges/relays/client-rialto/src/lib.rs +++ b/bridges/relays/client-rialto/src/lib.rs @@ -66,6 +66,7 @@ impl TransactionSignScheme for Rialto { fn sign_transaction( genesis_hash: ::Hash, signer: &Self::AccountKeyPair, + era: relay_substrate_client::TransactionEraOf, signer_nonce: ::Index, call: ::Call, ) -> Self::SignedTransaction { @@ -75,7 +76,7 @@ impl TransactionSignScheme for Rialto { frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), frame_system::CheckGenesis::::new(), - frame_system::CheckEra::::from(sp_runtime::generic::Era::Immortal), + frame_system::CheckEra::::from(era.frame_era()), frame_system::CheckNonce::::from(signer_nonce), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(0), @@ -84,7 +85,7 @@ impl TransactionSignScheme for Rialto { rialto_runtime::VERSION.spec_version, rialto_runtime::VERSION.transaction_version, genesis_hash, - genesis_hash, + era.signed_payload(genesis_hash), (), (), (), diff --git a/bridges/relays/client-rococo/src/lib.rs b/bridges/relays/client-rococo/src/lib.rs index 4d4812e459..25c10999c6 100644 --- a/bridges/relays/client-rococo/src/lib.rs +++ b/bridges/relays/client-rococo/src/lib.rs @@ -68,18 +68,13 @@ impl TransactionSignScheme for Rococo { fn sign_transaction( genesis_hash: ::Hash, signer: &Self::AccountKeyPair, + era: relay_substrate_client::TransactionEraOf, signer_nonce: ::Index, call: ::Call, ) -> Self::SignedTransaction { let raw_payload = SignedPayload::new( call, - bp_rococo::SignedExtensions::new( - bp_rococo::VERSION, - sp_runtime::generic::Era::Immortal, - genesis_hash, - signer_nonce, - 0, - ), + bp_rococo::SignedExtensions::new(bp_rococo::VERSION, era, genesis_hash, signer_nonce, 0), ) .expect("SignedExtension never fails."); diff --git a/bridges/relays/client-substrate/src/chain.rs b/bridges/relays/client-substrate/src/chain.rs index 03cbaef2d8..39d122a72b 100644 --- a/bridges/relays/client-substrate/src/chain.rs +++ b/bridges/relays/client-substrate/src/chain.rs @@ -107,6 +107,7 @@ pub trait TransactionSignScheme { fn sign_transaction( genesis_hash: ::Hash, signer: &Self::AccountKeyPair, + era: bp_runtime::TransactionEraOf, signer_nonce: ::Index, call: ::Call, ) -> Self::SignedTransaction; diff --git a/bridges/relays/client-substrate/src/client.rs b/bridges/relays/client-substrate/src/client.rs index 8164501348..b63e7f30a6 100644 --- a/bridges/relays/client-substrate/src/client.rs +++ b/bridges/relays/client-substrate/src/client.rs @@ -18,7 +18,7 @@ use crate::chain::{Chain, ChainWithBalances}; use crate::rpc::Substrate; -use crate::{ConnectionParams, Error, Result}; +use crate::{ConnectionParams, Error, HeaderIdOf, Result}; use async_std::sync::{Arc, Mutex}; use codec::Decode; @@ -29,8 +29,9 @@ use jsonrpsee_ws_client::{WsClient as RpcClient, WsClientBuilder as RpcClientBui use num_traits::{Bounded, Zero}; use pallet_balances::AccountData; use pallet_transaction_payment::InclusionFee; -use relay_utils::relay_loop::RECONNECT_DELAY; +use relay_utils::{relay_loop::RECONNECT_DELAY, HeaderId}; use sp_core::{storage::StorageKey, Bytes}; +use sp_runtime::traits::Header as HeaderT; use sp_trie::StorageProof; use sp_version::RuntimeVersion; use std::{convert::TryFrom, future::Future}; @@ -293,12 +294,14 @@ impl Client { pub async fn submit_signed_extrinsic( &self, extrinsic_signer: C::AccountId, - prepare_extrinsic: impl FnOnce(C::Index) -> Bytes + Send + 'static, + prepare_extrinsic: impl FnOnce(HeaderIdOf, C::Index) -> Bytes + Send + 'static, ) -> Result { let _guard = self.submit_signed_extrinsic_lock.lock().await; let transaction_nonce = self.next_account_index(extrinsic_signer).await?; + let best_header = self.best_header().await?; + let best_header_id = HeaderId(*best_header.number(), best_header.hash()); self.jsonrpsee_execute(move |client| async move { - let extrinsic = prepare_extrinsic(transaction_nonce); + let extrinsic = prepare_extrinsic(best_header_id, transaction_nonce); let tx_hash = Substrate::::author_submit_extrinsic(&*client, extrinsic).await?; log::trace!(target: "bridge", "Sent transaction to {} node: {:?}", C::NAME, tx_hash); Ok(tx_hash) diff --git a/bridges/relays/client-substrate/src/lib.rs b/bridges/relays/client-substrate/src/lib.rs index 44895dcdc6..2648fc4ff2 100644 --- a/bridges/relays/client-substrate/src/lib.rs +++ b/bridges/relays/client-substrate/src/lib.rs @@ -29,11 +29,13 @@ pub mod guard; pub mod headers_source; pub mod metrics; +use std::time::Duration; + pub use crate::chain::{BlockWithJustification, Chain, ChainWithBalances, TransactionSignScheme}; pub use crate::client::{Client, JustificationsSubscription, OpaqueGrandpaAuthoritiesSet}; pub use crate::error::{Error, Result}; pub use crate::sync_header::SyncHeader; -pub use bp_runtime::{BlockNumberOf, Chain as ChainBase, HashOf, HeaderOf}; +pub use bp_runtime::{BlockNumberOf, Chain as ChainBase, HashOf, HeaderOf, TransactionEra, TransactionEraOf}; /// Header id used by the chain. pub type HeaderIdOf = relay_utils::HeaderId, BlockNumberOf>; @@ -58,3 +60,14 @@ impl Default for ConnectionParams { } } } + +/// Returns stall timeout for relay loop. +/// +/// Relay considers himself stalled if he has submitted transaction to the node, but it has not +/// been mined for this period. +/// +/// Returns `None` if mortality period is `None` +pub fn transaction_stall_timeout(mortality_period: Option, average_block_interval: Duration) -> Option { + // 1 extra block for transaction to reach the pool && 1 for relayer to awake after it is mined + mortality_period.map(|mortality_period| average_block_interval.saturating_mul(mortality_period + 1 + 1)) +} diff --git a/bridges/relays/client-westend/src/lib.rs b/bridges/relays/client-westend/src/lib.rs index b2347da502..fefab00c56 100644 --- a/bridges/relays/client-westend/src/lib.rs +++ b/bridges/relays/client-westend/src/lib.rs @@ -66,18 +66,13 @@ impl TransactionSignScheme for Westend { fn sign_transaction( genesis_hash: ::Hash, signer: &Self::AccountKeyPair, + era: relay_substrate_client::TransactionEraOf, signer_nonce: ::Index, call: ::Call, ) -> Self::SignedTransaction { let raw_payload = SignedPayload::new( call, - bp_westend::SignedExtensions::new( - bp_westend::VERSION, - sp_runtime::generic::Era::Immortal, - genesis_hash, - signer_nonce, - 0, - ), + bp_westend::SignedExtensions::new(bp_westend::VERSION, era, genesis_hash, signer_nonce, 0), ) .expect("SignedExtension never fails."); diff --git a/bridges/relays/client-wococo/src/lib.rs b/bridges/relays/client-wococo/src/lib.rs index 3ef55223b5..4b3bdd7d84 100644 --- a/bridges/relays/client-wococo/src/lib.rs +++ b/bridges/relays/client-wococo/src/lib.rs @@ -68,18 +68,13 @@ impl TransactionSignScheme for Wococo { fn sign_transaction( genesis_hash: ::Hash, signer: &Self::AccountKeyPair, + era: bp_runtime::TransactionEraOf, signer_nonce: ::Index, call: ::Call, ) -> Self::SignedTransaction { let raw_payload = SignedPayload::new( call, - bp_wococo::SignedExtensions::new( - bp_wococo::VERSION, - sp_runtime::generic::Era::Immortal, - genesis_hash, - signer_nonce, - 0, - ), + bp_wococo::SignedExtensions::new(bp_wococo::VERSION, era, genesis_hash, signer_nonce, 0), ) .expect("SignedExtension never fails."); diff --git a/bridges/relays/lib-substrate-relay/src/finality_pipeline.rs b/bridges/relays/lib-substrate-relay/src/finality_pipeline.rs index 2dc9a63947..73a346f217 100644 --- a/bridges/relays/lib-substrate-relay/src/finality_pipeline.rs +++ b/bridges/relays/lib-substrate-relay/src/finality_pipeline.rs @@ -25,8 +25,14 @@ use relay_utils::{metrics::MetricsParams, BlockNumberBase}; use sp_core::Bytes; use std::{fmt::Debug, marker::PhantomData, time::Duration}; -/// Default synchronization loop timeout. -pub(crate) const STALL_TIMEOUT: Duration = Duration::from_secs(120); +/// Default synchronization loop timeout. If transactions generated by relay are immortal, then +/// this timeout is used. +/// +/// There are no any strict requirements on block time in Substrate. But we assume here that all +/// Substrate-based chains will be designed to produce relatively fast (compared to slowest +/// blockchains) blocks. So 1 hour seems to be a good guess for (even congested) chains to mine +/// transaction, or remove it from the pool. +pub(crate) const STALL_TIMEOUT: Duration = Duration::from_secs(60 * 60); /// Default limit of recent finality proofs. /// /// Finality delay of 4096 blocks is unlikely to happen in practice in @@ -62,6 +68,7 @@ pub trait SubstrateFinalitySyncPipeline: 'static + Clone + Debug + Send + Sync { /// Make submit header transaction. fn make_submit_finality_proof_transaction( &self, + era: bp_runtime::TransactionEraOf, transaction_nonce: ::Index, header: ::Header, proof: ::FinalityProof, @@ -123,6 +130,7 @@ pub async fn run( source_client: Client, target_client: Client, only_mandatory_headers: bool, + transactions_mortality: Option, metrics_params: MetricsParams, ) -> anyhow::Result<()> where @@ -146,11 +154,15 @@ where finality_relay::run( FinalitySource::new(source_client, None), - SubstrateFinalityTarget::new(target_client, pipeline), + SubstrateFinalityTarget::new(target_client, pipeline, transactions_mortality), FinalitySyncParams { tick: std::cmp::max(SourceChain::AVERAGE_BLOCK_INTERVAL, TargetChain::AVERAGE_BLOCK_INTERVAL), recent_finality_proofs_limit: RECENT_FINALITY_PROOFS_LIMIT, - stall_timeout: STALL_TIMEOUT, + stall_timeout: relay_substrate_client::transaction_stall_timeout( + transactions_mortality, + TargetChain::AVERAGE_BLOCK_INTERVAL, + ) + .unwrap_or(STALL_TIMEOUT), only_mandatory_headers, }, metrics_params, diff --git a/bridges/relays/lib-substrate-relay/src/finality_target.rs b/bridges/relays/lib-substrate-relay/src/finality_target.rs index bcf09b1a6d..5db7d080ab 100644 --- a/bridges/relays/lib-substrate-relay/src/finality_target.rs +++ b/bridges/relays/lib-substrate-relay/src/finality_target.rs @@ -30,12 +30,17 @@ use relay_utils::relay_loop::Client as RelayClient; pub struct SubstrateFinalityTarget { client: Client, pipeline: P, + transactions_mortality: Option, } impl SubstrateFinalityTarget { /// Create new Substrate headers target. - pub fn new(client: Client, pipeline: P) -> Self { - SubstrateFinalityTarget { client, pipeline } + pub fn new(client: Client, pipeline: P, transactions_mortality: Option) -> Self { + SubstrateFinalityTarget { + client, + pipeline, + transactions_mortality, + } } } @@ -44,6 +49,7 @@ impl Clone for SubstrateFinalityTarg SubstrateFinalityTarget { client: self.client.clone(), pipeline: self.pipeline.clone(), + transactions_mortality: self.transactions_mortality, } } } @@ -89,9 +95,19 @@ where ) -> Result<(), SubstrateError> { let transactions_author = self.pipeline.transactions_author(); let pipeline = self.pipeline.clone(); + let transactions_mortality = self.transactions_mortality; self.client - .submit_signed_extrinsic(transactions_author, move |transaction_nonce| { - pipeline.make_submit_finality_proof_transaction(transaction_nonce, header, proof) + .submit_signed_extrinsic(transactions_author, move |best_block_id, transaction_nonce| { + pipeline.make_submit_finality_proof_transaction( + relay_substrate_client::TransactionEra::new( + best_block_id.0, + best_block_id.1, + transactions_mortality, + ), + transaction_nonce, + header, + proof, + ) }) .await .map(drop) diff --git a/bridges/relays/lib-substrate-relay/src/headers_initialize.rs b/bridges/relays/lib-substrate-relay/src/headers_initialize.rs index d3a78028e0..4a7a0798bb 100644 --- a/bridges/relays/lib-substrate-relay/src/headers_initialize.rs +++ b/bridges/relays/lib-substrate-relay/src/headers_initialize.rs @@ -88,7 +88,7 @@ async fn do_initialize( ); let initialization_tx_hash = target_client - .submit_signed_extrinsic(target_transactions_signer, move |transaction_nonce| { + .submit_signed_extrinsic(target_transactions_signer, move |_, transaction_nonce| { prepare_initialize_transaction(transaction_nonce, initialization_data) }) .await diff --git a/bridges/relays/lib-substrate-relay/src/messages_source.rs b/bridges/relays/lib-substrate-relay/src/messages_source.rs index a442ebfb44..3924c6d3a5 100644 --- a/bridges/relays/lib-substrate-relay/src/messages_source.rs +++ b/bridges/relays/lib-substrate-relay/src/messages_source.rs @@ -246,7 +246,7 @@ where ) -> Result<(), SubstrateError> { let lane = self.lane.clone(); self.client - .submit_signed_extrinsic(self.lane.source_transactions_author(), move |transaction_nonce| { + .submit_signed_extrinsic(self.lane.source_transactions_author(), move |_, transaction_nonce| { lane.make_messages_receiving_proof_transaction(transaction_nonce, generated_at_block, proof) }) .await?; diff --git a/bridges/relays/lib-substrate-relay/src/messages_target.rs b/bridges/relays/lib-substrate-relay/src/messages_target.rs index 1db8345931..3b8e749a60 100644 --- a/bridges/relays/lib-substrate-relay/src/messages_target.rs +++ b/bridges/relays/lib-substrate-relay/src/messages_target.rs @@ -229,7 +229,7 @@ where let lane = self.lane.clone(); let nonces_clone = nonces.clone(); self.client - .submit_signed_extrinsic(self.lane.target_transactions_author(), move |transaction_nonce| { + .submit_signed_extrinsic(self.lane.target_transactions_author(), move |_, transaction_nonce| { lane.make_messages_delivery_transaction(transaction_nonce, generated_at_header, nonces_clone, proof) }) .await?; diff --git a/bridges/relays/lib-substrate-relay/src/on_demand_headers.rs b/bridges/relays/lib-substrate-relay/src/on_demand_headers.rs index 74166b89e7..2f8441dbfc 100644 --- a/bridges/relays/lib-substrate-relay/src/on_demand_headers.rs +++ b/bridges/relays/lib-substrate-relay/src/on_demand_headers.rs @@ -56,6 +56,7 @@ impl OnDemandHeadersRelay { pub fn new( source_client: Client, target_client: Client, + target_transactions_mortality: Option, pipeline: P, maximal_headers_difference: SourceChain::BlockNumber, ) -> Self @@ -79,6 +80,7 @@ impl OnDemandHeadersRelay { background_task( source_client, target_client, + target_transactions_mortality, pipeline, maximal_headers_difference, required_header_number, @@ -110,7 +112,7 @@ impl OnDemandHeadersRelay { async fn background_task( source_client: Client, target_client: Client, - // pipeline: SubstrateFinalityToSubstrate, + target_transactions_mortality: Option, pipeline: P, maximal_headers_difference: SourceChain::BlockNumber, required_header_number: RequiredHeaderNumberRef, @@ -130,7 +132,8 @@ async fn background_task( _, SubstrateFinalityToSubstrate, >::new(source_client.clone(), Some(required_header_number.clone())); - let mut finality_target = SubstrateFinalityTarget::new(target_client.clone(), pipeline.clone()); + let mut finality_target = + SubstrateFinalityTarget::new(target_client.clone(), pipeline.clone(), target_transactions_mortality); let mut latest_non_mandatory_at_source = Zero::zero(); let mut restart_relay = true;