diff --git a/bridges/primitives/runtime/src/chain.rs b/bridges/primitives/runtime/src/chain.rs index 4f88e701fb..14035566e6 100644 --- a/bridges/primitives/runtime/src/chain.rs +++ b/bridges/primitives/runtime/src/chain.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . +use crate::HeaderIdProvider; use codec::{Decode, Encode}; use frame_support::{weights::Weight, Parameter}; use num_traits::{AsPrimitive, Bounded, CheckedSub, Saturating, SaturatingAdd, Zero}; @@ -143,6 +144,7 @@ pub trait Chain: Send + Sync + 'static { // https://crates.parity.io/sp_runtime/traits/trait.Header.html type Header: Parameter + HeaderT + + HeaderIdProvider + MaybeSerializeDeserialize; /// The user account identifier type for the runtime. diff --git a/bridges/primitives/runtime/src/lib.rs b/bridges/primitives/runtime/src/lib.rs index ca8a22ee16..982b8f9a0e 100644 --- a/bridges/primitives/runtime/src/lib.rs +++ b/bridges/primitives/runtime/src/lib.rs @@ -26,7 +26,7 @@ use frame_system::RawOrigin; use scale_info::TypeInfo; use sp_core::{hash::H256, storage::StorageKey}; use sp_io::hashing::blake2_256; -use sp_runtime::traits::BadOrigin; +use sp_runtime::traits::{BadOrigin, Header as HeaderT}; use sp_std::{convert::TryFrom, fmt::Debug, vec, vec::Vec}; pub use chain::{ @@ -34,6 +34,7 @@ pub use chain::{ HasherOf, HeaderOf, IndexOf, SignatureOf, TransactionEraOf, }; pub use frame_support::storage::storage_prefix as storage_value_final_key; +use num_traits::{CheckedSub, One}; pub use storage_proof::{ Error as StorageProofError, ProofSize as StorageProofSize, StorageProofChecker, }; @@ -83,6 +84,27 @@ pub const ROOT_ACCOUNT_DERIVATION_PREFIX: &[u8] = b"pallet-bridge/account-deriva #[derive(RuntimeDebug, Default, Clone, Copy, Eq, Hash, PartialEq)] pub struct HeaderId(pub Number, pub Hash); +/// Generic header id provider. +pub trait HeaderIdProvider { + // Get the header id. + fn id(&self) -> HeaderId; + + // Get the header id for the parent block. + fn parent_id(&self) -> Option>; +} + +impl HeaderIdProvider
for Header { + fn id(&self) -> HeaderId { + HeaderId(*self.number(), self.hash()) + } + + fn parent_id(&self) -> Option> { + self.number() + .checked_sub(&One::one()) + .map(|parent_number| HeaderId(parent_number, *self.parent_hash())) + } +} + /// Unique identifier of the chain. /// /// In addition to its main function (identifying the chain), this type may also be used to diff --git a/bridges/relays/bin-substrate/src/cli/resubmit_transactions.rs b/bridges/relays/bin-substrate/src/cli/resubmit_transactions.rs index a4cec79655..dad1afbf14 100644 --- a/bridges/relays/bin-substrate/src/cli/resubmit_transactions.rs +++ b/bridges/relays/bin-substrate/src/cli/resubmit_transactions.rs @@ -16,13 +16,14 @@ use crate::cli::{chain_schema::*, Balance}; +use bp_runtime::HeaderIdProvider; use codec::{Decode, Encode}; use num_traits::{One, Zero}; use relay_substrate_client::{ BlockWithJustification, Chain, Client, Error as SubstrateError, HeaderIdOf, HeaderOf, SignParam, TransactionSignScheme, }; -use relay_utils::{FailedClient, HeaderId}; +use relay_utils::FailedClient; use sp_core::Bytes; use sp_runtime::{ traits::{Hash, Header as HeaderT}, @@ -274,7 +275,7 @@ async fn run_loop_iteration>( let (is_updated, updated_transaction) = update_transaction_tip::( &client, &transaction_params, - HeaderId(*context.best_header.number(), context.best_header.hash()), + context.best_header.id(), original_transaction, context.tip_step, context.tip_limit, diff --git a/bridges/relays/client-substrate/src/client.rs b/bridges/relays/client-substrate/src/client.rs index 5619645d0a..e5309bb54e 100644 --- a/bridges/relays/client-substrate/src/client.rs +++ b/bridges/relays/client-substrate/src/client.rs @@ -25,6 +25,7 @@ use crate::{ use async_std::sync::{Arc, Mutex}; use async_trait::async_trait; +use bp_runtime::HeaderIdProvider; use codec::{Decode, Encode}; use frame_system::AccountInfo; use futures::{SinkExt, StreamExt}; @@ -33,10 +34,10 @@ use jsonrpsee::{ types::params::ParamsSer, ws_client::{WsClient as RpcClient, WsClientBuilder as RpcClientBuilder}, }; -use num_traits::{Bounded, CheckedSub, One, Zero}; +use num_traits::{Bounded, Zero}; use pallet_balances::AccountData; use pallet_transaction_payment::InclusionFee; -use relay_utils::{relay_loop::RECONNECT_DELAY, HeaderId}; +use relay_utils::relay_loop::RECONNECT_DELAY; use sp_core::{ storage::{StorageData, StorageKey}, Bytes, Hasher, @@ -484,14 +485,11 @@ impl Client { let best_header = self.best_header().await?; // By using parent of best block here, we are protecing again best-block reorganizations. - // E.g. transaction my have been submitted when the best block was `A[num=100]`. Then it has - // been changed to `B[num=100]`. Hash of `A` has been included into transaction signature - // payload. So when signature will be checked, the check will fail and transaction will be - // dropped from the pool. - let best_header_id = match best_header.number().checked_sub(&One::one()) { - Some(parent_block_number) => HeaderId(parent_block_number, *best_header.parent_hash()), - None => HeaderId(*best_header.number(), best_header.hash()), - }; + // E.g. transaction may have been submitted when the best block was `A[num=100]`. Then it + // has been changed to `B[num=100]`. Hash of `A` has been included into transaction + // signature payload. So when signature will be checked, the check will fail and transaction + // will be dropped from the pool. + let best_header_id = best_header.parent_id().unwrap_or_else(|| best_header.id()); self.jsonrpsee_execute(move |client| async move { let extrinsic = prepare_extrinsic(best_header_id, transaction_nonce)?; @@ -524,7 +522,7 @@ impl Client { 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()); + let best_header_id = best_header.id(); let subscription = self .jsonrpsee_execute(move |client| async move { let extrinsic = prepare_extrinsic(best_header_id, transaction_nonce)?; diff --git a/bridges/relays/lib-substrate-relay/src/messages_source.rs b/bridges/relays/lib-substrate-relay/src/messages_source.rs index 8ed8367d35..2daa41fc28 100644 --- a/bridges/relays/lib-substrate-relay/src/messages_source.rs +++ b/bridges/relays/lib-substrate-relay/src/messages_source.rs @@ -34,7 +34,7 @@ use bp_messages::{ InboundMessageDetails, LaneId, MessageData, MessageNonce, MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails, UnrewardedRelayersState, }; -use bp_runtime::{messages::DispatchFeePayment, BasicOperatingMode}; +use bp_runtime::{messages::DispatchFeePayment, BasicOperatingMode, HeaderIdProvider}; use bridge_runtime_common::messages::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, }; @@ -510,13 +510,12 @@ where let self_best_finalized_header_hash = self_client.best_finalized_header_hash().await?; let self_best_finalized_header = self_client.header_by_hash(self_best_finalized_header_hash).await?; - let self_best_finalized_id = - HeaderId(*self_best_finalized_header.number(), self_best_finalized_header_hash); + let self_best_finalized_id = self_best_finalized_header.id(); // now let's read our best header on **this** chain let self_best_header = self_client.best_header().await?; let self_best_hash = self_best_header.hash(); - let self_best_id = HeaderId(*self_best_header.number(), self_best_hash); + let self_best_id = self_best_header.id(); // now let's read id of best finalized peer header at our best finalized block let peer_on_self_best_finalized_id = @@ -532,7 +531,7 @@ where Some(peer_client) => { let actual_peer_on_self_best_finalized = peer_client.header_by_number(peer_on_self_best_finalized_id.0).await?; - HeaderId(peer_on_self_best_finalized_id.0, actual_peer_on_self_best_finalized.hash()) + actual_peer_on_self_best_finalized.id() }, None => peer_on_self_best_finalized_id, }; diff --git a/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs b/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs index f25d3b7694..77d80bc79b 100644 --- a/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs +++ b/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs @@ -32,6 +32,7 @@ use async_std::{ }; use async_trait::async_trait; use bp_polkadot_core::parachains::ParaHash; +use bp_runtime::HeaderIdProvider; use futures::{select, FutureExt}; use num_traits::Zero; use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; @@ -43,7 +44,6 @@ use relay_substrate_client::{ use relay_utils::{ metrics::MetricsParams, relay_loop::Client as RelayClient, FailedClient, HeaderId, }; -use sp_runtime::traits::Header as HeaderT; use std::fmt::Debug; /// On-demand Substrate <-> Substrate parachain finality relay. @@ -387,8 +387,7 @@ where let best_finalized_relay_header = source.client().best_finalized_header().await.map_err(map_source_err)?; - let best_finalized_relay_block_id = - HeaderId(*best_finalized_relay_header.number(), best_finalized_relay_header.hash()); + let best_finalized_relay_block_id = best_finalized_relay_header.id(); let para_header_at_source = source .on_chain_parachain_header( best_finalized_relay_block_id, @@ -396,7 +395,7 @@ where ) .await .map_err(map_source_err)? - .map(|h| HeaderId(*h.number(), h.hash())); + .map(|h| h.id()); let relay_header_at_source = best_finalized_relay_block_id.0; let relay_header_at_target = @@ -412,7 +411,7 @@ where .on_chain_parachain_header(relay_header_at_target, P::SOURCE_PARACHAIN_PARA_ID.into()) .await .map_err(map_source_err)? - .map(|h| HeaderId(*h.number(), h.hash())); + .map(|h| h.id()); Ok(RelayData { required_para_header: required_header_number, diff --git a/bridges/relays/lib-substrate-relay/src/parachains/target.rs b/bridges/relays/lib-substrate-relay/src/parachains/target.rs index d26894b9bd..43168f2c85 100644 --- a/bridges/relays/lib-substrate-relay/src/parachains/target.rs +++ b/bridges/relays/lib-substrate-relay/src/parachains/target.rs @@ -29,6 +29,7 @@ use bp_parachains::{ BestParaHeadHash, }; use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; +use bp_runtime::HeaderIdProvider; use codec::{Decode, Encode}; use parachains_relay::{ parachains_loop::TargetClient, parachains_loop_metrics::ParachainsLoopMetrics, @@ -90,8 +91,7 @@ where { async fn best_block(&self) -> Result, Self::Error> { let best_header = self.client.best_header().await?; - let best_hash = best_header.hash(); - let best_id = HeaderId(*best_header.number(), best_hash); + let best_id = best_header.id(); Ok(best_id) }