mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 19:51:02 +00:00
Transactions resubmitter (#1083)
* resubmit transactions: start * resubmit transactions: continue * enable resubmitter in deployments * clippy * spellcheck * Update relays/client-substrate/src/chain.rs Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * fix compilation * fix compilation Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>
This commit is contained in:
committed by
Bastian Köcher
parent
d59d442e93
commit
1df7076c4f
@@ -13,6 +13,7 @@ futures = "0.3.12"
|
||||
hex = "0.4"
|
||||
log = "0.4.14"
|
||||
num-format = "0.4"
|
||||
num-traits = "0.2"
|
||||
paste = "1.0"
|
||||
structopt = "0.3"
|
||||
strum = { version = "0.21.0", features = ["derive"] }
|
||||
|
||||
@@ -22,7 +22,7 @@ use sp_core::{Bytes, Pair};
|
||||
use bp_header_chain::justification::GrandpaJustification;
|
||||
use relay_millau_client::{Millau, SyncHeader as MillauSyncHeader};
|
||||
use relay_rialto_client::{Rialto, SigningParams as RialtoSigningParams};
|
||||
use relay_substrate_client::{Chain, Client, TransactionSignScheme};
|
||||
use relay_substrate_client::{Chain, Client, TransactionSignScheme, UnsignedTransaction};
|
||||
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
|
||||
|
||||
/// Millau-to-Rialto finality sync pipeline.
|
||||
@@ -66,8 +66,7 @@ impl SubstrateFinalitySyncPipeline for MillauFinalityToRialto {
|
||||
genesis_hash,
|
||||
&self.finality_pipeline.target_sign,
|
||||
era,
|
||||
transaction_nonce,
|
||||
call,
|
||||
UnsignedTransaction::new(call, transaction_nonce),
|
||||
);
|
||||
|
||||
Bytes(transaction.encode())
|
||||
|
||||
@@ -28,7 +28,7 @@ use frame_support::weights::Weight;
|
||||
use messages_relay::message_lane::MessageLane;
|
||||
use relay_millau_client::{HeaderId as MillauHeaderId, Millau, SigningParams as MillauSigningParams};
|
||||
use relay_rialto_client::{HeaderId as RialtoHeaderId, Rialto, SigningParams as RialtoSigningParams};
|
||||
use relay_substrate_client::{Chain, Client, TransactionSignScheme};
|
||||
use relay_substrate_client::{Chain, Client, TransactionSignScheme, UnsignedTransaction};
|
||||
use relay_utils::metrics::MetricsParams;
|
||||
use substrate_relay_helper::messages_lane::{
|
||||
select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics, SubstrateMessageLane,
|
||||
@@ -89,8 +89,7 @@ impl SubstrateMessageLane for MillauMessagesToRialto {
|
||||
genesis_hash,
|
||||
&self.message_lane.source_sign,
|
||||
relay_substrate_client::TransactionEra::immortal(),
|
||||
transaction_nonce,
|
||||
call,
|
||||
UnsignedTransaction::new(call, transaction_nonce),
|
||||
);
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
@@ -134,8 +133,7 @@ impl SubstrateMessageLane for MillauMessagesToRialto {
|
||||
genesis_hash,
|
||||
&self.message_lane.target_sign,
|
||||
relay_substrate_client::TransactionEra::immortal(),
|
||||
transaction_nonce,
|
||||
call,
|
||||
UnsignedTransaction::new(call, transaction_nonce),
|
||||
);
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
@@ -274,13 +272,15 @@ pub(crate) async fn update_rialto_to_millau_conversion_rate(
|
||||
genesis_hash,
|
||||
&signer,
|
||||
relay_substrate_client::TransactionEra::immortal(),
|
||||
transaction_nonce,
|
||||
millau_runtime::MessagesCall::update_pallet_parameter(
|
||||
millau_runtime::rialto_messages::MillauToRialtoMessagesParameter::RialtoToMillauConversionRate(
|
||||
sp_runtime::FixedU128::from_float(updated_rate),
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
UnsignedTransaction::new(
|
||||
millau_runtime::MessagesCall::update_pallet_parameter(
|
||||
millau_runtime::rialto_messages::MillauToRialtoMessagesParameter::RialtoToMillauConversionRate(
|
||||
sp_runtime::FixedU128::from_float(updated_rate),
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
transaction_nonce,
|
||||
),
|
||||
)
|
||||
.encode(),
|
||||
)
|
||||
|
||||
@@ -67,7 +67,7 @@ mod tests {
|
||||
use frame_support::dispatch::GetDispatchInfo;
|
||||
use relay_millau_client::Millau;
|
||||
use relay_rialto_client::Rialto;
|
||||
use relay_substrate_client::TransactionSignScheme;
|
||||
use relay_substrate_client::{TransactionSignScheme, UnsignedTransaction};
|
||||
use sp_core::Pair;
|
||||
use sp_runtime::traits::{IdentifyAccount, Verify};
|
||||
|
||||
@@ -215,8 +215,7 @@ mod tests {
|
||||
Default::default(),
|
||||
&sp_keyring::AccountKeyring::Alice.pair(),
|
||||
relay_substrate_client::TransactionEra::immortal(),
|
||||
0,
|
||||
rialto_call.clone(),
|
||||
UnsignedTransaction::new(rialto_call.clone(), 0),
|
||||
);
|
||||
let extra_bytes_in_transaction = rialto_tx.encode().len() - rialto_call.encode().len();
|
||||
assert!(
|
||||
@@ -234,8 +233,7 @@ mod tests {
|
||||
Default::default(),
|
||||
&sp_keyring::AccountKeyring::Alice.pair(),
|
||||
relay_substrate_client::TransactionEra::immortal(),
|
||||
0,
|
||||
millau_call.clone(),
|
||||
UnsignedTransaction::new(millau_call.clone(), 0),
|
||||
);
|
||||
let extra_bytes_in_transaction = millau_tx.encode().len() - millau_call.encode().len();
|
||||
assert!(
|
||||
|
||||
@@ -22,7 +22,7 @@ use sp_core::{Bytes, Pair};
|
||||
use bp_header_chain::justification::GrandpaJustification;
|
||||
use relay_millau_client::{Millau, SigningParams as MillauSigningParams};
|
||||
use relay_rialto_client::{Rialto, SyncHeader as RialtoSyncHeader};
|
||||
use relay_substrate_client::{Chain, Client, TransactionSignScheme};
|
||||
use relay_substrate_client::{Chain, Client, TransactionSignScheme, UnsignedTransaction};
|
||||
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
|
||||
|
||||
/// Rialto-to-Millau finality sync pipeline.
|
||||
@@ -71,8 +71,7 @@ impl SubstrateFinalitySyncPipeline for RialtoFinalityToMillau {
|
||||
genesis_hash,
|
||||
&self.finality_pipeline.target_sign,
|
||||
era,
|
||||
transaction_nonce,
|
||||
call,
|
||||
UnsignedTransaction::new(call, transaction_nonce),
|
||||
);
|
||||
|
||||
Bytes(transaction.encode())
|
||||
|
||||
@@ -28,7 +28,7 @@ use frame_support::weights::Weight;
|
||||
use messages_relay::message_lane::MessageLane;
|
||||
use relay_millau_client::{HeaderId as MillauHeaderId, Millau, SigningParams as MillauSigningParams};
|
||||
use relay_rialto_client::{HeaderId as RialtoHeaderId, Rialto, SigningParams as RialtoSigningParams};
|
||||
use relay_substrate_client::{Chain, Client, TransactionSignScheme};
|
||||
use relay_substrate_client::{Chain, Client, TransactionSignScheme, UnsignedTransaction};
|
||||
use relay_utils::metrics::MetricsParams;
|
||||
use substrate_relay_helper::messages_lane::{
|
||||
select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics, SubstrateMessageLane,
|
||||
@@ -89,8 +89,7 @@ impl SubstrateMessageLane for RialtoMessagesToMillau {
|
||||
genesis_hash,
|
||||
&self.message_lane.source_sign,
|
||||
relay_substrate_client::TransactionEra::immortal(),
|
||||
transaction_nonce,
|
||||
call,
|
||||
UnsignedTransaction::new(call, transaction_nonce),
|
||||
);
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
@@ -134,8 +133,7 @@ impl SubstrateMessageLane for RialtoMessagesToMillau {
|
||||
genesis_hash,
|
||||
&self.message_lane.target_sign,
|
||||
relay_substrate_client::TransactionEra::immortal(),
|
||||
transaction_nonce,
|
||||
call,
|
||||
UnsignedTransaction::new(call, transaction_nonce),
|
||||
);
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
@@ -273,13 +271,15 @@ pub(crate) async fn update_millau_to_rialto_conversion_rate(
|
||||
genesis_hash,
|
||||
&signer,
|
||||
relay_substrate_client::TransactionEra::immortal(),
|
||||
transaction_nonce,
|
||||
rialto_runtime::MessagesCall::update_pallet_parameter(
|
||||
rialto_runtime::millau_messages::RialtoToMillauMessagesParameter::MillauToRialtoConversionRate(
|
||||
sp_runtime::FixedU128::from_float(updated_rate),
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
UnsignedTransaction::new(
|
||||
rialto_runtime::MessagesCall::update_pallet_parameter(
|
||||
rialto_runtime::millau_messages::RialtoToMillauMessagesParameter::MillauToRialtoConversionRate(
|
||||
sp_runtime::FixedU128::from_float(updated_rate),
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
transaction_nonce,
|
||||
),
|
||||
)
|
||||
.encode(),
|
||||
)
|
||||
|
||||
@@ -21,7 +21,7 @@ use sp_core::{Bytes, Pair};
|
||||
|
||||
use bp_header_chain::justification::GrandpaJustification;
|
||||
use relay_rococo_client::{Rococo, SyncHeader as RococoSyncHeader};
|
||||
use relay_substrate_client::{Chain, Client, TransactionSignScheme};
|
||||
use relay_substrate_client::{Chain, Client, TransactionSignScheme, UnsignedTransaction};
|
||||
use relay_utils::metrics::MetricsParams;
|
||||
use relay_wococo_client::{SigningParams as WococoSigningParams, Wococo};
|
||||
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
|
||||
@@ -90,8 +90,7 @@ impl SubstrateFinalitySyncPipeline for RococoFinalityToWococo {
|
||||
genesis_hash,
|
||||
&self.finality_pipeline.target_sign,
|
||||
era,
|
||||
transaction_nonce,
|
||||
call,
|
||||
UnsignedTransaction::new(call, transaction_nonce),
|
||||
);
|
||||
|
||||
Bytes(transaction.encode())
|
||||
|
||||
@@ -26,7 +26,7 @@ use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof;
|
||||
use frame_support::weights::Weight;
|
||||
use messages_relay::message_lane::MessageLane;
|
||||
use relay_rococo_client::{HeaderId as RococoHeaderId, Rococo, SigningParams as RococoSigningParams};
|
||||
use relay_substrate_client::{Chain, Client, TransactionSignScheme};
|
||||
use relay_substrate_client::{Chain, Client, TransactionSignScheme, UnsignedTransaction};
|
||||
use relay_utils::metrics::MetricsParams;
|
||||
use relay_wococo_client::{HeaderId as WococoHeaderId, SigningParams as WococoSigningParams, Wococo};
|
||||
use substrate_relay_helper::messages_lane::{
|
||||
@@ -91,8 +91,7 @@ impl SubstrateMessageLane for RococoMessagesToWococo {
|
||||
genesis_hash,
|
||||
&self.message_lane.source_sign,
|
||||
relay_substrate_client::TransactionEra::immortal(),
|
||||
transaction_nonce,
|
||||
call,
|
||||
UnsignedTransaction::new(call, transaction_nonce),
|
||||
);
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
@@ -136,8 +135,7 @@ impl SubstrateMessageLane for RococoMessagesToWococo {
|
||||
genesis_hash,
|
||||
&self.message_lane.target_sign,
|
||||
relay_substrate_client::TransactionEra::immortal(),
|
||||
transaction_nonce,
|
||||
call,
|
||||
UnsignedTransaction::new(call, transaction_nonce),
|
||||
);
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
|
||||
@@ -21,7 +21,7 @@ use sp_core::{Bytes, Pair};
|
||||
|
||||
use bp_header_chain::justification::GrandpaJustification;
|
||||
use relay_millau_client::{Millau, SigningParams as MillauSigningParams};
|
||||
use relay_substrate_client::{Chain, Client, TransactionSignScheme};
|
||||
use relay_substrate_client::{Chain, Client, TransactionSignScheme, UnsignedTransaction};
|
||||
use relay_utils::metrics::MetricsParams;
|
||||
use relay_westend_client::{SyncHeader as WestendSyncHeader, Westend};
|
||||
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
|
||||
@@ -79,8 +79,7 @@ impl SubstrateFinalitySyncPipeline for WestendFinalityToMillau {
|
||||
genesis_hash,
|
||||
&self.finality_pipeline.target_sign,
|
||||
era,
|
||||
transaction_nonce,
|
||||
call,
|
||||
UnsignedTransaction::new(call, transaction_nonce),
|
||||
);
|
||||
|
||||
Bytes(transaction.encode())
|
||||
|
||||
@@ -21,7 +21,7 @@ use sp_core::{Bytes, Pair};
|
||||
|
||||
use bp_header_chain::justification::GrandpaJustification;
|
||||
use relay_rococo_client::{Rococo, SigningParams as RococoSigningParams};
|
||||
use relay_substrate_client::{Chain, Client, TransactionSignScheme};
|
||||
use relay_substrate_client::{Chain, Client, TransactionSignScheme, UnsignedTransaction};
|
||||
use relay_utils::metrics::MetricsParams;
|
||||
use relay_wococo_client::{SyncHeader as WococoSyncHeader, Wococo};
|
||||
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
|
||||
@@ -95,8 +95,7 @@ impl SubstrateFinalitySyncPipeline for WococoFinalityToRococo {
|
||||
genesis_hash,
|
||||
&self.finality_pipeline.target_sign,
|
||||
era,
|
||||
transaction_nonce,
|
||||
call,
|
||||
UnsignedTransaction::new(call, transaction_nonce),
|
||||
);
|
||||
|
||||
Bytes(transaction.encode())
|
||||
|
||||
@@ -26,7 +26,7 @@ use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof;
|
||||
use frame_support::weights::Weight;
|
||||
use messages_relay::message_lane::MessageLane;
|
||||
use relay_rococo_client::{HeaderId as RococoHeaderId, Rococo, SigningParams as RococoSigningParams};
|
||||
use relay_substrate_client::{Chain, Client, TransactionSignScheme};
|
||||
use relay_substrate_client::{Chain, Client, TransactionSignScheme, UnsignedTransaction};
|
||||
use relay_utils::metrics::MetricsParams;
|
||||
use relay_wococo_client::{HeaderId as WococoHeaderId, SigningParams as WococoSigningParams, Wococo};
|
||||
use substrate_relay_helper::messages_lane::{
|
||||
@@ -90,8 +90,7 @@ impl SubstrateMessageLane for WococoMessagesToRococo {
|
||||
genesis_hash,
|
||||
&self.message_lane.source_sign,
|
||||
relay_substrate_client::TransactionEra::immortal(),
|
||||
transaction_nonce,
|
||||
call,
|
||||
UnsignedTransaction::new(call, transaction_nonce),
|
||||
);
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
@@ -135,8 +134,7 @@ impl SubstrateMessageLane for WococoMessagesToRococo {
|
||||
genesis_hash,
|
||||
&self.message_lane.target_sign,
|
||||
relay_substrate_client::TransactionEra::immortal(),
|
||||
transaction_nonce,
|
||||
call,
|
||||
UnsignedTransaction::new(call, transaction_nonce),
|
||||
);
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
|
||||
@@ -18,7 +18,7 @@ use crate::cli::{SourceConnectionParams, TargetConnectionParams, TargetSigningPa
|
||||
use bp_header_chain::InitializationData;
|
||||
use bp_runtime::Chain as ChainBase;
|
||||
use codec::Encode;
|
||||
use relay_substrate_client::{Chain, TransactionSignScheme};
|
||||
use relay_substrate_client::{Chain, TransactionSignScheme, UnsignedTransaction};
|
||||
use sp_core::{Bytes, Pair};
|
||||
use structopt::StructOpt;
|
||||
use strum::{EnumString, EnumVariantNames, VariantNames};
|
||||
@@ -151,8 +151,7 @@ impl InitBridge {
|
||||
*target_client.genesis_hash(),
|
||||
&target_sign,
|
||||
relay_substrate_client::TransactionEra::immortal(),
|
||||
transaction_nonce,
|
||||
encode_init_bridge(initialization_data),
|
||||
UnsignedTransaction::new(encode_init_bridge(initialization_data), transaction_nonce),
|
||||
)
|
||||
.encode(),
|
||||
)
|
||||
|
||||
@@ -35,6 +35,7 @@ mod init_bridge;
|
||||
mod relay_headers;
|
||||
mod relay_headers_and_messages;
|
||||
mod relay_messages;
|
||||
mod resubmit_transactions;
|
||||
|
||||
/// Parse relay CLI args.
|
||||
pub fn parse_args() -> Command {
|
||||
@@ -86,6 +87,8 @@ pub enum Command {
|
||||
EstimateFee(estimate_fee::EstimateFee),
|
||||
/// Given a source chain `AccountId`, derive the corresponding `AccountId` for the target chain.
|
||||
DeriveAccount(derive_account::DeriveAccount),
|
||||
/// Resubmit transactions with increased tip if they are stalled.
|
||||
ResubmitTransactions(resubmit_transactions::ResubmitTransactions),
|
||||
}
|
||||
|
||||
impl Command {
|
||||
@@ -116,6 +119,7 @@ impl Command {
|
||||
Self::EncodeMessage(arg) => arg.run().await?,
|
||||
Self::EstimateFee(arg) => arg.run().await?,
|
||||
Self::DeriveAccount(arg) => arg.run().await?,
|
||||
Self::ResubmitTransactions(arg) => arg.run().await?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -360,7 +364,7 @@ macro_rules! declare_chain_options {
|
||||
($chain:ident, $chain_prefix:ident) => {
|
||||
paste::item! {
|
||||
#[doc = $chain " connection params."]
|
||||
#[derive(StructOpt, Debug, PartialEq, Eq)]
|
||||
#[derive(StructOpt, Debug, PartialEq, Eq, Clone)]
|
||||
pub struct [<$chain ConnectionParams>] {
|
||||
#[doc = "Connect to " $chain " node at given host."]
|
||||
#[structopt(long, default_value = "127.0.0.1")]
|
||||
|
||||
@@ -0,0 +1,364 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::cli::{TargetConnectionParams, TargetSigningParams};
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use num_traits::{One, Zero};
|
||||
use relay_substrate_client::{BlockWithJustification, Chain, Client, Error as SubstrateError, TransactionSignScheme};
|
||||
use relay_utils::FailedClient;
|
||||
use sp_core::Bytes;
|
||||
use sp_runtime::{
|
||||
traits::{Hash, Header as HeaderT},
|
||||
transaction_validity::TransactionPriority,
|
||||
};
|
||||
use structopt::StructOpt;
|
||||
use strum::{EnumString, EnumVariantNames, VariantNames};
|
||||
|
||||
/// Start resubmit transactions process.
|
||||
#[derive(StructOpt)]
|
||||
pub struct ResubmitTransactions {
|
||||
/// A bridge instance to relay headers for.
|
||||
#[structopt(possible_values = RelayChain::VARIANTS, case_insensitive = true)]
|
||||
chain: RelayChain,
|
||||
#[structopt(flatten)]
|
||||
target: TargetConnectionParams,
|
||||
#[structopt(flatten)]
|
||||
target_sign: TargetSigningParams,
|
||||
}
|
||||
|
||||
/// Chain, which transactions we're going to track && resubmit.
|
||||
#[derive(Debug, EnumString, EnumVariantNames)]
|
||||
#[strum(serialize_all = "kebab_case")]
|
||||
pub enum RelayChain {
|
||||
Millau,
|
||||
}
|
||||
|
||||
macro_rules! select_bridge {
|
||||
($bridge: expr, $generic: tt) => {
|
||||
match $bridge {
|
||||
RelayChain::Millau => {
|
||||
type Target = relay_millau_client::Millau;
|
||||
type TargetSign = relay_millau_client::Millau;
|
||||
|
||||
const TIP_STEP: bp_millau::Balance = 1_000_000;
|
||||
const TIP_LIMIT: bp_millau::Balance = 1_000_000_000;
|
||||
|
||||
const STALLED_BLOCKS: bp_millau::BlockNumber = 5;
|
||||
|
||||
$generic
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl ResubmitTransactions {
|
||||
/// Run the command.
|
||||
pub async fn run(self) -> anyhow::Result<()> {
|
||||
select_bridge!(self.chain, {
|
||||
let relay_loop_name = format!("ResubmitTransactions{}", Target::NAME);
|
||||
let client = self.target.to_client::<Target>().await?;
|
||||
let key_pair = self.target_sign.to_keypair::<Target>()?;
|
||||
|
||||
relay_utils::relay_loop((), client)
|
||||
.run(relay_loop_name, move |_, client, _| {
|
||||
run_until_connection_lost::<Target, TargetSign>(
|
||||
client,
|
||||
key_pair.clone(),
|
||||
Context {
|
||||
transaction: None,
|
||||
stalled_for: Zero::zero(),
|
||||
stalled_for_limit: STALLED_BLOCKS,
|
||||
tip_step: TIP_STEP,
|
||||
tip_limit: TIP_LIMIT,
|
||||
},
|
||||
)
|
||||
})
|
||||
.await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct Context<C: Chain> {
|
||||
/// Hash of the (potentially) stalled transaction.
|
||||
transaction: Option<C::Hash>,
|
||||
/// This transaction is in pool for `stalled_for` wakeup intervals.
|
||||
stalled_for: C::BlockNumber,
|
||||
/// When `stalled_for` reaching this limit, transaction is considered stalled.
|
||||
stalled_for_limit: C::BlockNumber,
|
||||
/// Tip step interval.
|
||||
tip_step: C::Balance,
|
||||
/// Maximal tip.
|
||||
tip_limit: C::Balance,
|
||||
}
|
||||
|
||||
impl<C: Chain> Context<C> {
|
||||
/// Return true if transaction has stalled.
|
||||
fn is_stalled(&self) -> bool {
|
||||
self.stalled_for >= self.stalled_for_limit
|
||||
}
|
||||
|
||||
/// Forget stalled transaction.
|
||||
fn clear(mut self) -> Self {
|
||||
self.transaction = None;
|
||||
self.stalled_for = Zero::zero();
|
||||
self
|
||||
}
|
||||
|
||||
/// Notice transaction from the transaction pool.
|
||||
fn notice_transaction(mut self, transaction: C::Hash) -> Self {
|
||||
if self.transaction == Some(transaction) {
|
||||
self.stalled_for += One::one();
|
||||
} else {
|
||||
self.transaction = Some(transaction);
|
||||
self.stalled_for = One::one();
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Run resubmit transactions loop.
|
||||
async fn run_until_connection_lost<C: Chain, S: TransactionSignScheme<Chain = C>>(
|
||||
client: Client<C>,
|
||||
key_pair: S::AccountKeyPair,
|
||||
mut context: Context<C>,
|
||||
) -> Result<(), FailedClient> {
|
||||
loop {
|
||||
async_std::task::sleep(C::AVERAGE_BLOCK_INTERVAL).await;
|
||||
|
||||
let result = run_loop_iteration::<C, S>(client.clone(), key_pair.clone(), context).await;
|
||||
context = match result {
|
||||
Ok(context) => context,
|
||||
Err(error) => {
|
||||
log::error!(
|
||||
target: "bridge",
|
||||
"Resubmit {} transactions loop has failed with error: {:?}",
|
||||
C::NAME,
|
||||
error,
|
||||
);
|
||||
return Err(FailedClient::Target);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Run single loop iteration.
|
||||
async fn run_loop_iteration<C: Chain, S: TransactionSignScheme<Chain = C>>(
|
||||
client: Client<C>,
|
||||
key_pair: S::AccountKeyPair,
|
||||
context: Context<C>,
|
||||
) -> Result<Context<C>, SubstrateError> {
|
||||
let original_transaction = match lookup_signer_transaction::<C, S>(&client, &key_pair).await? {
|
||||
Some(original_transaction) => original_transaction,
|
||||
None => {
|
||||
log::trace!(target: "bridge", "No {} transactions from required signer in the txpool", C::NAME);
|
||||
return Ok(context);
|
||||
}
|
||||
};
|
||||
let original_transaction_hash = C::Hasher::hash(&original_transaction.encode());
|
||||
let context = context.notice_transaction(original_transaction_hash);
|
||||
|
||||
if !context.is_stalled() {
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
"{} transaction {:?} is not yet stalled ({:?}/{:?})",
|
||||
C::NAME,
|
||||
context.transaction,
|
||||
context.stalled_for,
|
||||
context.stalled_for_limit,
|
||||
);
|
||||
return Ok(context);
|
||||
}
|
||||
|
||||
let (best_block, target_priority) = match read_previous_best_priority::<C, S>(&client).await? {
|
||||
Some((best_block, target_priority)) => (best_block, target_priority),
|
||||
None => {
|
||||
log::trace!(target: "bridge", "Failed to read priority of best {} transaction in its best block", C::NAME);
|
||||
return Ok(context);
|
||||
}
|
||||
};
|
||||
|
||||
let (is_updated, updated_transaction) = select_transaction_tip::<C, S>(
|
||||
&client,
|
||||
&key_pair,
|
||||
best_block,
|
||||
original_transaction,
|
||||
context.tip_step,
|
||||
context.tip_limit,
|
||||
target_priority,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if !is_updated {
|
||||
log::trace!(target: "bridge", "{} transaction tip can not be updated. Reached limit?", C::NAME);
|
||||
return Ok(context);
|
||||
}
|
||||
|
||||
let updated_transaction = updated_transaction.encode();
|
||||
let updated_transaction_hash = C::Hasher::hash(&updated_transaction);
|
||||
client.submit_unsigned_extrinsic(Bytes(updated_transaction)).await?;
|
||||
|
||||
log::info!(
|
||||
target: "bridge",
|
||||
"Replaced {} transaction {} with {} in txpool",
|
||||
C::NAME,
|
||||
original_transaction_hash,
|
||||
updated_transaction_hash,
|
||||
);
|
||||
|
||||
Ok(context.clear())
|
||||
}
|
||||
|
||||
/// Search transaction pool for transaction, signed by given key pair.
|
||||
async fn lookup_signer_transaction<C: Chain, S: TransactionSignScheme<Chain = C>>(
|
||||
client: &Client<C>,
|
||||
key_pair: &S::AccountKeyPair,
|
||||
) -> Result<Option<S::SignedTransaction>, SubstrateError> {
|
||||
let pending_transactions = client.pending_extrinsics().await?;
|
||||
for pending_transaction in pending_transactions {
|
||||
let pending_transaction = S::SignedTransaction::decode(&mut &pending_transaction.0[..])
|
||||
.map_err(SubstrateError::ResponseParseFailed)?;
|
||||
if !S::is_signed_by(key_pair, &pending_transaction) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return Ok(Some(pending_transaction));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Read priority of best signed transaction of previous block.
|
||||
async fn read_previous_best_priority<C: Chain, S: TransactionSignScheme<Chain = C>>(
|
||||
client: &Client<C>,
|
||||
) -> Result<Option<(C::Hash, TransactionPriority)>, SubstrateError> {
|
||||
let best_header = client.best_header().await?;
|
||||
let best_header_hash = best_header.hash();
|
||||
let best_block = client.get_block(Some(best_header_hash)).await?;
|
||||
let best_transaction = best_block
|
||||
.extrinsics()
|
||||
.iter()
|
||||
.filter_map(|xt| S::SignedTransaction::decode(&mut &xt[..]).ok())
|
||||
.find(|xt| S::is_signed(xt));
|
||||
match best_transaction {
|
||||
Some(best_transaction) => Ok(Some((
|
||||
best_header_hash,
|
||||
client
|
||||
.validate_transaction(*best_header.parent_hash(), best_transaction)
|
||||
.await??
|
||||
.priority,
|
||||
))),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to find appropriate tip for transaction so that its priority is larger than given.
|
||||
async fn select_transaction_tip<C: Chain, S: TransactionSignScheme<Chain = C>>(
|
||||
client: &Client<C>,
|
||||
key_pair: &S::AccountKeyPair,
|
||||
at_block: C::Hash,
|
||||
tx: S::SignedTransaction,
|
||||
tip_step: C::Balance,
|
||||
tip_limit: C::Balance,
|
||||
target_priority: TransactionPriority,
|
||||
) -> Result<(bool, S::SignedTransaction), SubstrateError> {
|
||||
let stx = format!("{:?}", tx);
|
||||
let mut current_priority = client.validate_transaction(at_block, tx.clone()).await??.priority;
|
||||
let mut unsigned_tx = S::parse_transaction(tx)
|
||||
.ok_or_else(|| SubstrateError::Custom(format!("Failed to parse {} transaction {}", C::NAME, stx,)))?;
|
||||
let old_tip = unsigned_tx.tip;
|
||||
|
||||
while current_priority < target_priority {
|
||||
let next_tip = unsigned_tx.tip + tip_step;
|
||||
if next_tip > tip_limit {
|
||||
break;
|
||||
}
|
||||
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
"{} transaction priority with tip={:?}: {}. Target priority: {}",
|
||||
C::NAME,
|
||||
unsigned_tx.tip,
|
||||
current_priority,
|
||||
target_priority,
|
||||
);
|
||||
|
||||
unsigned_tx.tip = next_tip;
|
||||
current_priority = client
|
||||
.validate_transaction(
|
||||
at_block,
|
||||
S::sign_transaction(
|
||||
*client.genesis_hash(),
|
||||
key_pair,
|
||||
relay_substrate_client::TransactionEra::immortal(),
|
||||
unsigned_tx.clone(),
|
||||
),
|
||||
)
|
||||
.await??
|
||||
.priority;
|
||||
}
|
||||
|
||||
log::debug!(
|
||||
target: "bridge",
|
||||
"{} transaction tip has changed from {:?} to {:?}",
|
||||
C::NAME,
|
||||
old_tip,
|
||||
unsigned_tx.tip,
|
||||
);
|
||||
|
||||
Ok((
|
||||
old_tip != unsigned_tx.tip,
|
||||
S::sign_transaction(
|
||||
*client.genesis_hash(),
|
||||
key_pair,
|
||||
relay_substrate_client::TransactionEra::immortal(),
|
||||
unsigned_tx,
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use relay_rialto_client::Rialto;
|
||||
|
||||
#[test]
|
||||
fn context_works() {
|
||||
let mut context: Context<Rialto> = Context {
|
||||
transaction: None,
|
||||
stalled_for: Zero::zero(),
|
||||
stalled_for_limit: 3,
|
||||
tip_step: 100,
|
||||
tip_limit: 1000,
|
||||
};
|
||||
|
||||
// when transaction is noticed 2/3 times, it isn't stalled
|
||||
context = context.notice_transaction(Default::default());
|
||||
assert!(!context.is_stalled());
|
||||
context = context.notice_transaction(Default::default());
|
||||
assert!(!context.is_stalled());
|
||||
|
||||
// when transaction is noticed for 3rd time in a row, it is considered stalled
|
||||
context = context.notice_transaction(Default::default());
|
||||
assert!(context.is_stalled());
|
||||
|
||||
// and after we resubmit it, we forget previous transaction
|
||||
context = context.clear();
|
||||
assert_eq!(context.transaction, None);
|
||||
assert_eq!(context.stalled_for, 0);
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ use crate::cli::{
|
||||
use bp_message_dispatch::{CallOrigin, MessagePayload};
|
||||
use codec::Encode;
|
||||
use frame_support::weights::Weight;
|
||||
use relay_substrate_client::{Chain, TransactionSignScheme};
|
||||
use relay_substrate_client::{Chain, TransactionSignScheme, UnsignedTransaction};
|
||||
use sp_core::{Bytes, Pair};
|
||||
use sp_runtime::{traits::IdentifyAccount, AccountId32, MultiSignature, MultiSigner};
|
||||
use std::fmt::Debug;
|
||||
@@ -183,8 +183,7 @@ impl SendMessage {
|
||||
source_genesis_hash,
|
||||
&source_sign,
|
||||
relay_substrate_client::TransactionEra::immortal(),
|
||||
0,
|
||||
send_message_call.clone(),
|
||||
UnsignedTransaction::new(send_message_call.clone(), 0),
|
||||
)
|
||||
.encode(),
|
||||
))
|
||||
@@ -195,8 +194,7 @@ impl SendMessage {
|
||||
source_genesis_hash,
|
||||
&source_sign,
|
||||
relay_substrate_client::TransactionEra::immortal(),
|
||||
transaction_nonce,
|
||||
send_message_call,
|
||||
UnsignedTransaction::new(send_message_call, transaction_nonce),
|
||||
)
|
||||
.encode();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user