mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 05:11:09 +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
@@ -233,6 +233,9 @@ pub type Balance = u128;
|
|||||||
pub type UncheckedExtrinsic<Call> =
|
pub type UncheckedExtrinsic<Call> =
|
||||||
generic::UncheckedExtrinsic<MultiAddress<AccountId, ()>, Call, Signature, SignedExtensions<Call>>;
|
generic::UncheckedExtrinsic<MultiAddress<AccountId, ()>, Call, Signature, SignedExtensions<Call>>;
|
||||||
|
|
||||||
|
/// Account address, used by the Polkadot-like chain.
|
||||||
|
pub type Address = MultiAddress<AccountId, ()>;
|
||||||
|
|
||||||
/// A type of the data encoded as part of the transaction.
|
/// A type of the data encoded as part of the transaction.
|
||||||
pub type SignedExtra = (
|
pub type SignedExtra = (
|
||||||
(),
|
(),
|
||||||
@@ -301,6 +304,18 @@ impl<Call> SignedExtensions<Call> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<Call> SignedExtensions<Call> {
|
||||||
|
/// Return signer nonce, used to craft transaction.
|
||||||
|
pub fn nonce(&self) -> Nonce {
|
||||||
|
self.encode_payload.4.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return transaction tip.
|
||||||
|
pub fn tip(&self) -> Balance {
|
||||||
|
self.encode_payload.6.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<Call> sp_runtime::traits::SignedExtension for SignedExtensions<Call>
|
impl<Call> sp_runtime::traits::SignedExtension for SignedExtensions<Call>
|
||||||
where
|
where
|
||||||
Call: parity_scale_codec::Codec + sp_std::fmt::Debug + Sync + Send + Clone + Eq + PartialEq,
|
Call: parity_scale_codec::Codec + sp_std::fmt::Debug + Sync + Send + Clone + Eq + PartialEq,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ use codec::{Decode, Encode};
|
|||||||
use headers_relay::sync_types::SubmittedHeaders;
|
use headers_relay::sync_types::SubmittedHeaders;
|
||||||
use relay_ethereum_client::types::HeaderId as EthereumHeaderId;
|
use relay_ethereum_client::types::HeaderId as EthereumHeaderId;
|
||||||
use relay_rialto_client::{Rialto, SigningParams as RialtoSigningParams};
|
use relay_rialto_client::{Rialto, SigningParams as RialtoSigningParams};
|
||||||
use relay_substrate_client::{Client as SubstrateClient, TransactionSignScheme};
|
use relay_substrate_client::{Client as SubstrateClient, TransactionSignScheme, UnsignedTransaction};
|
||||||
use relay_utils::HeaderId;
|
use relay_utils::HeaderId;
|
||||||
use sp_core::{crypto::Pair, Bytes};
|
use sp_core::{crypto::Pair, Bytes};
|
||||||
use std::{collections::VecDeque, sync::Arc};
|
use std::{collections::VecDeque, sync::Arc};
|
||||||
@@ -163,8 +163,7 @@ impl SubmitEthereumHeaders for SubstrateClient<Rialto> {
|
|||||||
genesis_hash,
|
genesis_hash,
|
||||||
¶ms,
|
¶ms,
|
||||||
relay_substrate_client::TransactionEra::immortal(),
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
transaction_nonce,
|
UnsignedTransaction::new(instance.build_signed_header_call(headers), transaction_nonce),
|
||||||
instance.build_signed_header_call(headers),
|
|
||||||
)
|
)
|
||||||
.encode(),
|
.encode(),
|
||||||
)
|
)
|
||||||
@@ -266,8 +265,7 @@ impl SubmitEthereumExchangeTransactionProof for SubstrateClient<Rialto> {
|
|||||||
genesis_hash,
|
genesis_hash,
|
||||||
¶ms,
|
¶ms,
|
||||||
relay_substrate_client::TransactionEra::immortal(),
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
transaction_nonce,
|
UnsignedTransaction::new(instance.build_currency_exchange_call(proof), transaction_nonce),
|
||||||
instance.build_currency_exchange_call(proof),
|
|
||||||
)
|
)
|
||||||
.encode(),
|
.encode(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ futures = "0.3.12"
|
|||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
num-format = "0.4"
|
num-format = "0.4"
|
||||||
|
num-traits = "0.2"
|
||||||
paste = "1.0"
|
paste = "1.0"
|
||||||
structopt = "0.3"
|
structopt = "0.3"
|
||||||
strum = { version = "0.21.0", features = ["derive"] }
|
strum = { version = "0.21.0", features = ["derive"] }
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ use sp_core::{Bytes, Pair};
|
|||||||
use bp_header_chain::justification::GrandpaJustification;
|
use bp_header_chain::justification::GrandpaJustification;
|
||||||
use relay_millau_client::{Millau, SyncHeader as MillauSyncHeader};
|
use relay_millau_client::{Millau, SyncHeader as MillauSyncHeader};
|
||||||
use relay_rialto_client::{Rialto, SigningParams as RialtoSigningParams};
|
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};
|
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
|
||||||
|
|
||||||
/// Millau-to-Rialto finality sync pipeline.
|
/// Millau-to-Rialto finality sync pipeline.
|
||||||
@@ -66,8 +66,7 @@ impl SubstrateFinalitySyncPipeline for MillauFinalityToRialto {
|
|||||||
genesis_hash,
|
genesis_hash,
|
||||||
&self.finality_pipeline.target_sign,
|
&self.finality_pipeline.target_sign,
|
||||||
era,
|
era,
|
||||||
transaction_nonce,
|
UnsignedTransaction::new(call, transaction_nonce),
|
||||||
call,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Bytes(transaction.encode())
|
Bytes(transaction.encode())
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ use frame_support::weights::Weight;
|
|||||||
use messages_relay::message_lane::MessageLane;
|
use messages_relay::message_lane::MessageLane;
|
||||||
use relay_millau_client::{HeaderId as MillauHeaderId, Millau, SigningParams as MillauSigningParams};
|
use relay_millau_client::{HeaderId as MillauHeaderId, Millau, SigningParams as MillauSigningParams};
|
||||||
use relay_rialto_client::{HeaderId as RialtoHeaderId, Rialto, SigningParams as RialtoSigningParams};
|
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 relay_utils::metrics::MetricsParams;
|
||||||
use substrate_relay_helper::messages_lane::{
|
use substrate_relay_helper::messages_lane::{
|
||||||
select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics, SubstrateMessageLane,
|
select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics, SubstrateMessageLane,
|
||||||
@@ -89,8 +89,7 @@ impl SubstrateMessageLane for MillauMessagesToRialto {
|
|||||||
genesis_hash,
|
genesis_hash,
|
||||||
&self.message_lane.source_sign,
|
&self.message_lane.source_sign,
|
||||||
relay_substrate_client::TransactionEra::immortal(),
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
transaction_nonce,
|
UnsignedTransaction::new(call, transaction_nonce),
|
||||||
call,
|
|
||||||
);
|
);
|
||||||
log::trace!(
|
log::trace!(
|
||||||
target: "bridge",
|
target: "bridge",
|
||||||
@@ -134,8 +133,7 @@ impl SubstrateMessageLane for MillauMessagesToRialto {
|
|||||||
genesis_hash,
|
genesis_hash,
|
||||||
&self.message_lane.target_sign,
|
&self.message_lane.target_sign,
|
||||||
relay_substrate_client::TransactionEra::immortal(),
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
transaction_nonce,
|
UnsignedTransaction::new(call, transaction_nonce),
|
||||||
call,
|
|
||||||
);
|
);
|
||||||
log::trace!(
|
log::trace!(
|
||||||
target: "bridge",
|
target: "bridge",
|
||||||
@@ -274,13 +272,15 @@ pub(crate) async fn update_rialto_to_millau_conversion_rate(
|
|||||||
genesis_hash,
|
genesis_hash,
|
||||||
&signer,
|
&signer,
|
||||||
relay_substrate_client::TransactionEra::immortal(),
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
transaction_nonce,
|
UnsignedTransaction::new(
|
||||||
millau_runtime::MessagesCall::update_pallet_parameter(
|
millau_runtime::MessagesCall::update_pallet_parameter(
|
||||||
millau_runtime::rialto_messages::MillauToRialtoMessagesParameter::RialtoToMillauConversionRate(
|
millau_runtime::rialto_messages::MillauToRialtoMessagesParameter::RialtoToMillauConversionRate(
|
||||||
sp_runtime::FixedU128::from_float(updated_rate),
|
sp_runtime::FixedU128::from_float(updated_rate),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
|
transaction_nonce,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.encode(),
|
.encode(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ mod tests {
|
|||||||
use frame_support::dispatch::GetDispatchInfo;
|
use frame_support::dispatch::GetDispatchInfo;
|
||||||
use relay_millau_client::Millau;
|
use relay_millau_client::Millau;
|
||||||
use relay_rialto_client::Rialto;
|
use relay_rialto_client::Rialto;
|
||||||
use relay_substrate_client::TransactionSignScheme;
|
use relay_substrate_client::{TransactionSignScheme, UnsignedTransaction};
|
||||||
use sp_core::Pair;
|
use sp_core::Pair;
|
||||||
use sp_runtime::traits::{IdentifyAccount, Verify};
|
use sp_runtime::traits::{IdentifyAccount, Verify};
|
||||||
|
|
||||||
@@ -215,8 +215,7 @@ mod tests {
|
|||||||
Default::default(),
|
Default::default(),
|
||||||
&sp_keyring::AccountKeyring::Alice.pair(),
|
&sp_keyring::AccountKeyring::Alice.pair(),
|
||||||
relay_substrate_client::TransactionEra::immortal(),
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
0,
|
UnsignedTransaction::new(rialto_call.clone(), 0),
|
||||||
rialto_call.clone(),
|
|
||||||
);
|
);
|
||||||
let extra_bytes_in_transaction = rialto_tx.encode().len() - rialto_call.encode().len();
|
let extra_bytes_in_transaction = rialto_tx.encode().len() - rialto_call.encode().len();
|
||||||
assert!(
|
assert!(
|
||||||
@@ -234,8 +233,7 @@ mod tests {
|
|||||||
Default::default(),
|
Default::default(),
|
||||||
&sp_keyring::AccountKeyring::Alice.pair(),
|
&sp_keyring::AccountKeyring::Alice.pair(),
|
||||||
relay_substrate_client::TransactionEra::immortal(),
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
0,
|
UnsignedTransaction::new(millau_call.clone(), 0),
|
||||||
millau_call.clone(),
|
|
||||||
);
|
);
|
||||||
let extra_bytes_in_transaction = millau_tx.encode().len() - millau_call.encode().len();
|
let extra_bytes_in_transaction = millau_tx.encode().len() - millau_call.encode().len();
|
||||||
assert!(
|
assert!(
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ use sp_core::{Bytes, Pair};
|
|||||||
use bp_header_chain::justification::GrandpaJustification;
|
use bp_header_chain::justification::GrandpaJustification;
|
||||||
use relay_millau_client::{Millau, SigningParams as MillauSigningParams};
|
use relay_millau_client::{Millau, SigningParams as MillauSigningParams};
|
||||||
use relay_rialto_client::{Rialto, SyncHeader as RialtoSyncHeader};
|
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};
|
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
|
||||||
|
|
||||||
/// Rialto-to-Millau finality sync pipeline.
|
/// Rialto-to-Millau finality sync pipeline.
|
||||||
@@ -71,8 +71,7 @@ impl SubstrateFinalitySyncPipeline for RialtoFinalityToMillau {
|
|||||||
genesis_hash,
|
genesis_hash,
|
||||||
&self.finality_pipeline.target_sign,
|
&self.finality_pipeline.target_sign,
|
||||||
era,
|
era,
|
||||||
transaction_nonce,
|
UnsignedTransaction::new(call, transaction_nonce),
|
||||||
call,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Bytes(transaction.encode())
|
Bytes(transaction.encode())
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ use frame_support::weights::Weight;
|
|||||||
use messages_relay::message_lane::MessageLane;
|
use messages_relay::message_lane::MessageLane;
|
||||||
use relay_millau_client::{HeaderId as MillauHeaderId, Millau, SigningParams as MillauSigningParams};
|
use relay_millau_client::{HeaderId as MillauHeaderId, Millau, SigningParams as MillauSigningParams};
|
||||||
use relay_rialto_client::{HeaderId as RialtoHeaderId, Rialto, SigningParams as RialtoSigningParams};
|
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 relay_utils::metrics::MetricsParams;
|
||||||
use substrate_relay_helper::messages_lane::{
|
use substrate_relay_helper::messages_lane::{
|
||||||
select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics, SubstrateMessageLane,
|
select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics, SubstrateMessageLane,
|
||||||
@@ -89,8 +89,7 @@ impl SubstrateMessageLane for RialtoMessagesToMillau {
|
|||||||
genesis_hash,
|
genesis_hash,
|
||||||
&self.message_lane.source_sign,
|
&self.message_lane.source_sign,
|
||||||
relay_substrate_client::TransactionEra::immortal(),
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
transaction_nonce,
|
UnsignedTransaction::new(call, transaction_nonce),
|
||||||
call,
|
|
||||||
);
|
);
|
||||||
log::trace!(
|
log::trace!(
|
||||||
target: "bridge",
|
target: "bridge",
|
||||||
@@ -134,8 +133,7 @@ impl SubstrateMessageLane for RialtoMessagesToMillau {
|
|||||||
genesis_hash,
|
genesis_hash,
|
||||||
&self.message_lane.target_sign,
|
&self.message_lane.target_sign,
|
||||||
relay_substrate_client::TransactionEra::immortal(),
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
transaction_nonce,
|
UnsignedTransaction::new(call, transaction_nonce),
|
||||||
call,
|
|
||||||
);
|
);
|
||||||
log::trace!(
|
log::trace!(
|
||||||
target: "bridge",
|
target: "bridge",
|
||||||
@@ -273,13 +271,15 @@ pub(crate) async fn update_millau_to_rialto_conversion_rate(
|
|||||||
genesis_hash,
|
genesis_hash,
|
||||||
&signer,
|
&signer,
|
||||||
relay_substrate_client::TransactionEra::immortal(),
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
transaction_nonce,
|
UnsignedTransaction::new(
|
||||||
rialto_runtime::MessagesCall::update_pallet_parameter(
|
rialto_runtime::MessagesCall::update_pallet_parameter(
|
||||||
rialto_runtime::millau_messages::RialtoToMillauMessagesParameter::MillauToRialtoConversionRate(
|
rialto_runtime::millau_messages::RialtoToMillauMessagesParameter::MillauToRialtoConversionRate(
|
||||||
sp_runtime::FixedU128::from_float(updated_rate),
|
sp_runtime::FixedU128::from_float(updated_rate),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
|
transaction_nonce,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.encode(),
|
.encode(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ use sp_core::{Bytes, Pair};
|
|||||||
|
|
||||||
use bp_header_chain::justification::GrandpaJustification;
|
use bp_header_chain::justification::GrandpaJustification;
|
||||||
use relay_rococo_client::{Rococo, SyncHeader as RococoSyncHeader};
|
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_utils::metrics::MetricsParams;
|
||||||
use relay_wococo_client::{SigningParams as WococoSigningParams, Wococo};
|
use relay_wococo_client::{SigningParams as WococoSigningParams, Wococo};
|
||||||
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
|
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
|
||||||
@@ -90,8 +90,7 @@ impl SubstrateFinalitySyncPipeline for RococoFinalityToWococo {
|
|||||||
genesis_hash,
|
genesis_hash,
|
||||||
&self.finality_pipeline.target_sign,
|
&self.finality_pipeline.target_sign,
|
||||||
era,
|
era,
|
||||||
transaction_nonce,
|
UnsignedTransaction::new(call, transaction_nonce),
|
||||||
call,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Bytes(transaction.encode())
|
Bytes(transaction.encode())
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof;
|
|||||||
use frame_support::weights::Weight;
|
use frame_support::weights::Weight;
|
||||||
use messages_relay::message_lane::MessageLane;
|
use messages_relay::message_lane::MessageLane;
|
||||||
use relay_rococo_client::{HeaderId as RococoHeaderId, Rococo, SigningParams as RococoSigningParams};
|
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_utils::metrics::MetricsParams;
|
||||||
use relay_wococo_client::{HeaderId as WococoHeaderId, SigningParams as WococoSigningParams, Wococo};
|
use relay_wococo_client::{HeaderId as WococoHeaderId, SigningParams as WococoSigningParams, Wococo};
|
||||||
use substrate_relay_helper::messages_lane::{
|
use substrate_relay_helper::messages_lane::{
|
||||||
@@ -91,8 +91,7 @@ impl SubstrateMessageLane for RococoMessagesToWococo {
|
|||||||
genesis_hash,
|
genesis_hash,
|
||||||
&self.message_lane.source_sign,
|
&self.message_lane.source_sign,
|
||||||
relay_substrate_client::TransactionEra::immortal(),
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
transaction_nonce,
|
UnsignedTransaction::new(call, transaction_nonce),
|
||||||
call,
|
|
||||||
);
|
);
|
||||||
log::trace!(
|
log::trace!(
|
||||||
target: "bridge",
|
target: "bridge",
|
||||||
@@ -136,8 +135,7 @@ impl SubstrateMessageLane for RococoMessagesToWococo {
|
|||||||
genesis_hash,
|
genesis_hash,
|
||||||
&self.message_lane.target_sign,
|
&self.message_lane.target_sign,
|
||||||
relay_substrate_client::TransactionEra::immortal(),
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
transaction_nonce,
|
UnsignedTransaction::new(call, transaction_nonce),
|
||||||
call,
|
|
||||||
);
|
);
|
||||||
log::trace!(
|
log::trace!(
|
||||||
target: "bridge",
|
target: "bridge",
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ use sp_core::{Bytes, Pair};
|
|||||||
|
|
||||||
use bp_header_chain::justification::GrandpaJustification;
|
use bp_header_chain::justification::GrandpaJustification;
|
||||||
use relay_millau_client::{Millau, SigningParams as MillauSigningParams};
|
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_utils::metrics::MetricsParams;
|
||||||
use relay_westend_client::{SyncHeader as WestendSyncHeader, Westend};
|
use relay_westend_client::{SyncHeader as WestendSyncHeader, Westend};
|
||||||
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
|
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
|
||||||
@@ -79,8 +79,7 @@ impl SubstrateFinalitySyncPipeline for WestendFinalityToMillau {
|
|||||||
genesis_hash,
|
genesis_hash,
|
||||||
&self.finality_pipeline.target_sign,
|
&self.finality_pipeline.target_sign,
|
||||||
era,
|
era,
|
||||||
transaction_nonce,
|
UnsignedTransaction::new(call, transaction_nonce),
|
||||||
call,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Bytes(transaction.encode())
|
Bytes(transaction.encode())
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ use sp_core::{Bytes, Pair};
|
|||||||
|
|
||||||
use bp_header_chain::justification::GrandpaJustification;
|
use bp_header_chain::justification::GrandpaJustification;
|
||||||
use relay_rococo_client::{Rococo, SigningParams as RococoSigningParams};
|
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_utils::metrics::MetricsParams;
|
||||||
use relay_wococo_client::{SyncHeader as WococoSyncHeader, Wococo};
|
use relay_wococo_client::{SyncHeader as WococoSyncHeader, Wococo};
|
||||||
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
|
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
|
||||||
@@ -95,8 +95,7 @@ impl SubstrateFinalitySyncPipeline for WococoFinalityToRococo {
|
|||||||
genesis_hash,
|
genesis_hash,
|
||||||
&self.finality_pipeline.target_sign,
|
&self.finality_pipeline.target_sign,
|
||||||
era,
|
era,
|
||||||
transaction_nonce,
|
UnsignedTransaction::new(call, transaction_nonce),
|
||||||
call,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Bytes(transaction.encode())
|
Bytes(transaction.encode())
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof;
|
|||||||
use frame_support::weights::Weight;
|
use frame_support::weights::Weight;
|
||||||
use messages_relay::message_lane::MessageLane;
|
use messages_relay::message_lane::MessageLane;
|
||||||
use relay_rococo_client::{HeaderId as RococoHeaderId, Rococo, SigningParams as RococoSigningParams};
|
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_utils::metrics::MetricsParams;
|
||||||
use relay_wococo_client::{HeaderId as WococoHeaderId, SigningParams as WococoSigningParams, Wococo};
|
use relay_wococo_client::{HeaderId as WococoHeaderId, SigningParams as WococoSigningParams, Wococo};
|
||||||
use substrate_relay_helper::messages_lane::{
|
use substrate_relay_helper::messages_lane::{
|
||||||
@@ -90,8 +90,7 @@ impl SubstrateMessageLane for WococoMessagesToRococo {
|
|||||||
genesis_hash,
|
genesis_hash,
|
||||||
&self.message_lane.source_sign,
|
&self.message_lane.source_sign,
|
||||||
relay_substrate_client::TransactionEra::immortal(),
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
transaction_nonce,
|
UnsignedTransaction::new(call, transaction_nonce),
|
||||||
call,
|
|
||||||
);
|
);
|
||||||
log::trace!(
|
log::trace!(
|
||||||
target: "bridge",
|
target: "bridge",
|
||||||
@@ -135,8 +134,7 @@ impl SubstrateMessageLane for WococoMessagesToRococo {
|
|||||||
genesis_hash,
|
genesis_hash,
|
||||||
&self.message_lane.target_sign,
|
&self.message_lane.target_sign,
|
||||||
relay_substrate_client::TransactionEra::immortal(),
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
transaction_nonce,
|
UnsignedTransaction::new(call, transaction_nonce),
|
||||||
call,
|
|
||||||
);
|
);
|
||||||
log::trace!(
|
log::trace!(
|
||||||
target: "bridge",
|
target: "bridge",
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use crate::cli::{SourceConnectionParams, TargetConnectionParams, TargetSigningPa
|
|||||||
use bp_header_chain::InitializationData;
|
use bp_header_chain::InitializationData;
|
||||||
use bp_runtime::Chain as ChainBase;
|
use bp_runtime::Chain as ChainBase;
|
||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
use relay_substrate_client::{Chain, TransactionSignScheme};
|
use relay_substrate_client::{Chain, TransactionSignScheme, UnsignedTransaction};
|
||||||
use sp_core::{Bytes, Pair};
|
use sp_core::{Bytes, Pair};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
use strum::{EnumString, EnumVariantNames, VariantNames};
|
use strum::{EnumString, EnumVariantNames, VariantNames};
|
||||||
@@ -151,8 +151,7 @@ impl InitBridge {
|
|||||||
*target_client.genesis_hash(),
|
*target_client.genesis_hash(),
|
||||||
&target_sign,
|
&target_sign,
|
||||||
relay_substrate_client::TransactionEra::immortal(),
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
transaction_nonce,
|
UnsignedTransaction::new(encode_init_bridge(initialization_data), transaction_nonce),
|
||||||
encode_init_bridge(initialization_data),
|
|
||||||
)
|
)
|
||||||
.encode(),
|
.encode(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ mod init_bridge;
|
|||||||
mod relay_headers;
|
mod relay_headers;
|
||||||
mod relay_headers_and_messages;
|
mod relay_headers_and_messages;
|
||||||
mod relay_messages;
|
mod relay_messages;
|
||||||
|
mod resubmit_transactions;
|
||||||
|
|
||||||
/// Parse relay CLI args.
|
/// Parse relay CLI args.
|
||||||
pub fn parse_args() -> Command {
|
pub fn parse_args() -> Command {
|
||||||
@@ -86,6 +87,8 @@ pub enum Command {
|
|||||||
EstimateFee(estimate_fee::EstimateFee),
|
EstimateFee(estimate_fee::EstimateFee),
|
||||||
/// Given a source chain `AccountId`, derive the corresponding `AccountId` for the target chain.
|
/// Given a source chain `AccountId`, derive the corresponding `AccountId` for the target chain.
|
||||||
DeriveAccount(derive_account::DeriveAccount),
|
DeriveAccount(derive_account::DeriveAccount),
|
||||||
|
/// Resubmit transactions with increased tip if they are stalled.
|
||||||
|
ResubmitTransactions(resubmit_transactions::ResubmitTransactions),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl Command {
|
||||||
@@ -116,6 +119,7 @@ impl Command {
|
|||||||
Self::EncodeMessage(arg) => arg.run().await?,
|
Self::EncodeMessage(arg) => arg.run().await?,
|
||||||
Self::EstimateFee(arg) => arg.run().await?,
|
Self::EstimateFee(arg) => arg.run().await?,
|
||||||
Self::DeriveAccount(arg) => arg.run().await?,
|
Self::DeriveAccount(arg) => arg.run().await?,
|
||||||
|
Self::ResubmitTransactions(arg) => arg.run().await?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -360,7 +364,7 @@ macro_rules! declare_chain_options {
|
|||||||
($chain:ident, $chain_prefix:ident) => {
|
($chain:ident, $chain_prefix:ident) => {
|
||||||
paste::item! {
|
paste::item! {
|
||||||
#[doc = $chain " connection params."]
|
#[doc = $chain " connection params."]
|
||||||
#[derive(StructOpt, Debug, PartialEq, Eq)]
|
#[derive(StructOpt, Debug, PartialEq, Eq, Clone)]
|
||||||
pub struct [<$chain ConnectionParams>] {
|
pub struct [<$chain ConnectionParams>] {
|
||||||
#[doc = "Connect to " $chain " node at given host."]
|
#[doc = "Connect to " $chain " node at given host."]
|
||||||
#[structopt(long, default_value = "127.0.0.1")]
|
#[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 bp_message_dispatch::{CallOrigin, MessagePayload};
|
||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
use frame_support::weights::Weight;
|
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_core::{Bytes, Pair};
|
||||||
use sp_runtime::{traits::IdentifyAccount, AccountId32, MultiSignature, MultiSigner};
|
use sp_runtime::{traits::IdentifyAccount, AccountId32, MultiSignature, MultiSigner};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
@@ -183,8 +183,7 @@ impl SendMessage {
|
|||||||
source_genesis_hash,
|
source_genesis_hash,
|
||||||
&source_sign,
|
&source_sign,
|
||||||
relay_substrate_client::TransactionEra::immortal(),
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
0,
|
UnsignedTransaction::new(send_message_call.clone(), 0),
|
||||||
send_message_call.clone(),
|
|
||||||
)
|
)
|
||||||
.encode(),
|
.encode(),
|
||||||
))
|
))
|
||||||
@@ -195,8 +194,7 @@ impl SendMessage {
|
|||||||
source_genesis_hash,
|
source_genesis_hash,
|
||||||
&source_sign,
|
&source_sign,
|
||||||
relay_substrate_client::TransactionEra::immortal(),
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
transaction_nonce,
|
UnsignedTransaction::new(send_message_call, transaction_nonce),
|
||||||
send_message_call,
|
|
||||||
)
|
)
|
||||||
.encode();
|
.encode();
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,10 @@
|
|||||||
|
|
||||||
//! Types used to connect to the Millau-Substrate chain.
|
//! Types used to connect to the Millau-Substrate chain.
|
||||||
|
|
||||||
use codec::Encode;
|
use codec::{Compact, Decode, Encode};
|
||||||
use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, TransactionSignScheme};
|
use relay_substrate_client::{
|
||||||
|
Chain, ChainBase, ChainWithBalances, TransactionEraOf, TransactionSignScheme, UnsignedTransaction,
|
||||||
|
};
|
||||||
use sp_core::{storage::StorageKey, Pair};
|
use sp_core::{storage::StorageKey, Pair};
|
||||||
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
|
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -67,20 +69,19 @@ impl TransactionSignScheme for Millau {
|
|||||||
fn sign_transaction(
|
fn sign_transaction(
|
||||||
genesis_hash: <Self::Chain as ChainBase>::Hash,
|
genesis_hash: <Self::Chain as ChainBase>::Hash,
|
||||||
signer: &Self::AccountKeyPair,
|
signer: &Self::AccountKeyPair,
|
||||||
era: relay_substrate_client::TransactionEraOf<Self::Chain>,
|
era: TransactionEraOf<Self::Chain>,
|
||||||
signer_nonce: <Self::Chain as Chain>::Index,
|
unsigned: UnsignedTransaction<Self::Chain>,
|
||||||
call: <Self::Chain as Chain>::Call,
|
|
||||||
) -> Self::SignedTransaction {
|
) -> Self::SignedTransaction {
|
||||||
let raw_payload = SignedPayload::from_raw(
|
let raw_payload = SignedPayload::from_raw(
|
||||||
call,
|
unsigned.call,
|
||||||
(
|
(
|
||||||
frame_system::CheckSpecVersion::<millau_runtime::Runtime>::new(),
|
frame_system::CheckSpecVersion::<millau_runtime::Runtime>::new(),
|
||||||
frame_system::CheckTxVersion::<millau_runtime::Runtime>::new(),
|
frame_system::CheckTxVersion::<millau_runtime::Runtime>::new(),
|
||||||
frame_system::CheckGenesis::<millau_runtime::Runtime>::new(),
|
frame_system::CheckGenesis::<millau_runtime::Runtime>::new(),
|
||||||
frame_system::CheckEra::<millau_runtime::Runtime>::from(era.frame_era()),
|
frame_system::CheckEra::<millau_runtime::Runtime>::from(era.frame_era()),
|
||||||
frame_system::CheckNonce::<millau_runtime::Runtime>::from(signer_nonce),
|
frame_system::CheckNonce::<millau_runtime::Runtime>::from(unsigned.nonce),
|
||||||
frame_system::CheckWeight::<millau_runtime::Runtime>::new(),
|
frame_system::CheckWeight::<millau_runtime::Runtime>::new(),
|
||||||
pallet_transaction_payment::ChargeTransactionPayment::<millau_runtime::Runtime>::from(0),
|
pallet_transaction_payment::ChargeTransactionPayment::<millau_runtime::Runtime>::from(unsigned.tip),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
millau_runtime::VERSION.spec_version,
|
millau_runtime::VERSION.spec_version,
|
||||||
@@ -98,6 +99,30 @@ impl TransactionSignScheme for Millau {
|
|||||||
|
|
||||||
millau_runtime::UncheckedExtrinsic::new_signed(call, signer.into_account(), signature.into(), extra)
|
millau_runtime::UncheckedExtrinsic::new_signed(call, signer.into_account(), signature.into(), extra)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_signed(tx: &Self::SignedTransaction) -> bool {
|
||||||
|
tx.signature.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool {
|
||||||
|
tx.signature
|
||||||
|
.as_ref()
|
||||||
|
.map(|(address, _, _)| *address == millau_runtime::Address::from(*signer.public().as_array_ref()))
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_transaction(tx: Self::SignedTransaction) -> Option<UnsignedTransaction<Self::Chain>> {
|
||||||
|
let extra = &tx.signature.as_ref()?.2;
|
||||||
|
Some(UnsignedTransaction {
|
||||||
|
call: tx.function,
|
||||||
|
nonce: Compact::<<Self::Chain as Chain>::Index>::decode(&mut &extra.4.encode()[..])
|
||||||
|
.ok()?
|
||||||
|
.into(),
|
||||||
|
tip: Compact::<<Self::Chain as Chain>::Balance>::decode(&mut &extra.6.encode()[..])
|
||||||
|
.ok()?
|
||||||
|
.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Millau signing params.
|
/// Millau signing params.
|
||||||
|
|||||||
@@ -16,8 +16,10 @@
|
|||||||
|
|
||||||
//! Types used to connect to the Rialto-Substrate chain.
|
//! Types used to connect to the Rialto-Substrate chain.
|
||||||
|
|
||||||
use codec::Encode;
|
use codec::{Compact, Decode, Encode};
|
||||||
use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, TransactionSignScheme};
|
use relay_substrate_client::{
|
||||||
|
Chain, ChainBase, ChainWithBalances, TransactionEraOf, TransactionSignScheme, UnsignedTransaction,
|
||||||
|
};
|
||||||
use sp_core::{storage::StorageKey, Pair};
|
use sp_core::{storage::StorageKey, Pair};
|
||||||
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
|
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -67,20 +69,19 @@ impl TransactionSignScheme for Rialto {
|
|||||||
fn sign_transaction(
|
fn sign_transaction(
|
||||||
genesis_hash: <Self::Chain as ChainBase>::Hash,
|
genesis_hash: <Self::Chain as ChainBase>::Hash,
|
||||||
signer: &Self::AccountKeyPair,
|
signer: &Self::AccountKeyPair,
|
||||||
era: relay_substrate_client::TransactionEraOf<Self::Chain>,
|
era: TransactionEraOf<Self::Chain>,
|
||||||
signer_nonce: <Self::Chain as Chain>::Index,
|
unsigned: UnsignedTransaction<Self::Chain>,
|
||||||
call: <Self::Chain as Chain>::Call,
|
|
||||||
) -> Self::SignedTransaction {
|
) -> Self::SignedTransaction {
|
||||||
let raw_payload = SignedPayload::from_raw(
|
let raw_payload = SignedPayload::from_raw(
|
||||||
call,
|
unsigned.call,
|
||||||
(
|
(
|
||||||
frame_system::CheckSpecVersion::<rialto_runtime::Runtime>::new(),
|
frame_system::CheckSpecVersion::<rialto_runtime::Runtime>::new(),
|
||||||
frame_system::CheckTxVersion::<rialto_runtime::Runtime>::new(),
|
frame_system::CheckTxVersion::<rialto_runtime::Runtime>::new(),
|
||||||
frame_system::CheckGenesis::<rialto_runtime::Runtime>::new(),
|
frame_system::CheckGenesis::<rialto_runtime::Runtime>::new(),
|
||||||
frame_system::CheckEra::<rialto_runtime::Runtime>::from(era.frame_era()),
|
frame_system::CheckEra::<rialto_runtime::Runtime>::from(era.frame_era()),
|
||||||
frame_system::CheckNonce::<rialto_runtime::Runtime>::from(signer_nonce),
|
frame_system::CheckNonce::<rialto_runtime::Runtime>::from(unsigned.nonce),
|
||||||
frame_system::CheckWeight::<rialto_runtime::Runtime>::new(),
|
frame_system::CheckWeight::<rialto_runtime::Runtime>::new(),
|
||||||
pallet_transaction_payment::ChargeTransactionPayment::<rialto_runtime::Runtime>::from(0),
|
pallet_transaction_payment::ChargeTransactionPayment::<rialto_runtime::Runtime>::from(unsigned.tip),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
rialto_runtime::VERSION.spec_version,
|
rialto_runtime::VERSION.spec_version,
|
||||||
@@ -98,6 +99,30 @@ impl TransactionSignScheme for Rialto {
|
|||||||
|
|
||||||
rialto_runtime::UncheckedExtrinsic::new_signed(call, signer.into_account(), signature.into(), extra)
|
rialto_runtime::UncheckedExtrinsic::new_signed(call, signer.into_account(), signature.into(), extra)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_signed(tx: &Self::SignedTransaction) -> bool {
|
||||||
|
tx.signature.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool {
|
||||||
|
tx.signature
|
||||||
|
.as_ref()
|
||||||
|
.map(|(address, _, _)| *address == rialto_runtime::Address::from(*signer.public().as_array_ref()))
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_transaction(tx: Self::SignedTransaction) -> Option<UnsignedTransaction<Self::Chain>> {
|
||||||
|
let extra = &tx.signature.as_ref()?.2;
|
||||||
|
Some(UnsignedTransaction {
|
||||||
|
call: tx.function,
|
||||||
|
nonce: Compact::<<Self::Chain as Chain>::Index>::decode(&mut &extra.4.encode()[..])
|
||||||
|
.ok()?
|
||||||
|
.into(),
|
||||||
|
tip: Compact::<<Self::Chain as Chain>::Balance>::decode(&mut &extra.6.encode()[..])
|
||||||
|
.ok()?
|
||||||
|
.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rialto signing params.
|
/// Rialto signing params.
|
||||||
|
|||||||
@@ -17,7 +17,9 @@
|
|||||||
//! Types used to connect to the Rococo-Substrate chain.
|
//! Types used to connect to the Rococo-Substrate chain.
|
||||||
|
|
||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, TransactionSignScheme};
|
use relay_substrate_client::{
|
||||||
|
Chain, ChainBase, ChainWithBalances, TransactionEraOf, TransactionSignScheme, UnsignedTransaction,
|
||||||
|
};
|
||||||
use sp_core::{storage::StorageKey, Pair};
|
use sp_core::{storage::StorageKey, Pair};
|
||||||
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
|
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -69,13 +71,12 @@ impl TransactionSignScheme for Rococo {
|
|||||||
fn sign_transaction(
|
fn sign_transaction(
|
||||||
genesis_hash: <Self::Chain as ChainBase>::Hash,
|
genesis_hash: <Self::Chain as ChainBase>::Hash,
|
||||||
signer: &Self::AccountKeyPair,
|
signer: &Self::AccountKeyPair,
|
||||||
era: relay_substrate_client::TransactionEraOf<Self::Chain>,
|
era: TransactionEraOf<Self::Chain>,
|
||||||
signer_nonce: <Self::Chain as Chain>::Index,
|
unsigned: UnsignedTransaction<Self::Chain>,
|
||||||
call: <Self::Chain as Chain>::Call,
|
|
||||||
) -> Self::SignedTransaction {
|
) -> Self::SignedTransaction {
|
||||||
let raw_payload = SignedPayload::new(
|
let raw_payload = SignedPayload::new(
|
||||||
call,
|
unsigned.call,
|
||||||
bp_rococo::SignedExtensions::new(bp_rococo::VERSION, era, genesis_hash, signer_nonce, 0),
|
bp_rococo::SignedExtensions::new(bp_rococo::VERSION, era, genesis_hash, unsigned.nonce, unsigned.tip),
|
||||||
)
|
)
|
||||||
.expect("SignedExtension never fails.");
|
.expect("SignedExtension never fails.");
|
||||||
|
|
||||||
@@ -90,6 +91,26 @@ impl TransactionSignScheme for Rococo {
|
|||||||
extra,
|
extra,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_signed(tx: &Self::SignedTransaction) -> bool {
|
||||||
|
tx.signature.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool {
|
||||||
|
tx.signature
|
||||||
|
.as_ref()
|
||||||
|
.map(|(address, _, _)| *address == bp_rococo::AccountId::from(*signer.public().as_array_ref()).into())
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_transaction(tx: Self::SignedTransaction) -> Option<UnsignedTransaction<Self::Chain>> {
|
||||||
|
let extra = &tx.signature.as_ref()?.2;
|
||||||
|
Some(UnsignedTransaction {
|
||||||
|
call: tx.function,
|
||||||
|
nonce: extra.nonce(),
|
||||||
|
tip: extra.tip(),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rococo signing params.
|
/// Rococo signing params.
|
||||||
|
|||||||
@@ -14,7 +14,8 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use bp_runtime::Chain as ChainBase;
|
use bp_runtime::{Chain as ChainBase, TransactionEraOf};
|
||||||
|
use codec::{Codec, Encode};
|
||||||
use frame_support::{weights::WeightToFeePolynomial, Parameter};
|
use frame_support::{weights::WeightToFeePolynomial, Parameter};
|
||||||
use jsonrpsee_ws_client::{DeserializeOwned, Serialize};
|
use jsonrpsee_ws_client::{DeserializeOwned, Serialize};
|
||||||
use num_traits::{Bounded, CheckedSub, SaturatingAdd, Zero};
|
use num_traits::{Bounded, CheckedSub, SaturatingAdd, Zero};
|
||||||
@@ -58,7 +59,7 @@ pub trait Chain: ChainBase + Clone {
|
|||||||
/// Block type.
|
/// Block type.
|
||||||
type SignedBlock: Member + Serialize + DeserializeOwned + BlockWithJustification<Self::Header>;
|
type SignedBlock: Member + Serialize + DeserializeOwned + BlockWithJustification<Self::Header>;
|
||||||
/// The aggregated `Call` type.
|
/// The aggregated `Call` type.
|
||||||
type Call: Dispatchable + Debug;
|
type Call: Clone + Dispatchable + Debug;
|
||||||
/// Balance of an account in native tokens.
|
/// Balance of an account in native tokens.
|
||||||
///
|
///
|
||||||
/// The chain may support multiple tokens, but this particular type is for token that is used
|
/// The chain may support multiple tokens, but this particular type is for token that is used
|
||||||
@@ -96,14 +97,47 @@ pub trait ChainWithBalances: Chain {
|
|||||||
fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey;
|
fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// SCALE-encoded extrinsic.
|
||||||
|
pub type EncodedExtrinsic = Vec<u8>;
|
||||||
|
|
||||||
/// Block with justification.
|
/// Block with justification.
|
||||||
pub trait BlockWithJustification<Header> {
|
pub trait BlockWithJustification<Header> {
|
||||||
/// Return block header.
|
/// Return block header.
|
||||||
fn header(&self) -> Header;
|
fn header(&self) -> Header;
|
||||||
|
/// Return encoded block extrinsics.
|
||||||
|
fn extrinsics(&self) -> Vec<EncodedExtrinsic>;
|
||||||
/// Return block justification, if known.
|
/// Return block justification, if known.
|
||||||
fn justification(&self) -> Option<&EncodedJustification>;
|
fn justification(&self) -> Option<&EncodedJustification>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Transaction before it is signed.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct UnsignedTransaction<C: Chain> {
|
||||||
|
/// Runtime call of this transaction.
|
||||||
|
pub call: C::Call,
|
||||||
|
/// Transaction nonce.
|
||||||
|
pub nonce: C::Index,
|
||||||
|
/// Tip included into transaction.
|
||||||
|
pub tip: C::Balance,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: Chain> UnsignedTransaction<C> {
|
||||||
|
/// Create new unsigned transaction with given call, nonce and zero tip.
|
||||||
|
pub fn new(call: C::Call, nonce: C::Index) -> Self {
|
||||||
|
Self {
|
||||||
|
call,
|
||||||
|
nonce,
|
||||||
|
tip: Zero::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set transaction tip.
|
||||||
|
pub fn tip(mut self, tip: C::Balance) -> Self {
|
||||||
|
self.tip = tip;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Substrate-based chain transactions signing scheme.
|
/// Substrate-based chain transactions signing scheme.
|
||||||
pub trait TransactionSignScheme {
|
pub trait TransactionSignScheme {
|
||||||
/// Chain that this scheme is to be used.
|
/// Chain that this scheme is to be used.
|
||||||
@@ -111,16 +145,26 @@ pub trait TransactionSignScheme {
|
|||||||
/// Type of key pairs used to sign transactions.
|
/// Type of key pairs used to sign transactions.
|
||||||
type AccountKeyPair: Pair;
|
type AccountKeyPair: Pair;
|
||||||
/// Signed transaction.
|
/// Signed transaction.
|
||||||
type SignedTransaction;
|
type SignedTransaction: Clone + Debug + Codec + Send + 'static;
|
||||||
|
|
||||||
/// Create transaction for given runtime call, signed by given account.
|
/// Create transaction for given runtime call, signed by given account.
|
||||||
fn sign_transaction(
|
fn sign_transaction(
|
||||||
genesis_hash: <Self::Chain as ChainBase>::Hash,
|
genesis_hash: <Self::Chain as ChainBase>::Hash,
|
||||||
signer: &Self::AccountKeyPair,
|
signer: &Self::AccountKeyPair,
|
||||||
era: bp_runtime::TransactionEraOf<Self::Chain>,
|
era: TransactionEraOf<Self::Chain>,
|
||||||
signer_nonce: <Self::Chain as Chain>::Index,
|
unsigned: UnsignedTransaction<Self::Chain>,
|
||||||
call: <Self::Chain as Chain>::Call,
|
|
||||||
) -> Self::SignedTransaction;
|
) -> Self::SignedTransaction;
|
||||||
|
|
||||||
|
/// Returns true if transaction is signed.
|
||||||
|
fn is_signed(tx: &Self::SignedTransaction) -> bool;
|
||||||
|
|
||||||
|
/// Returns true if transaction is signed by given signer.
|
||||||
|
fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool;
|
||||||
|
|
||||||
|
/// Parse signed transaction into its unsigned part.
|
||||||
|
///
|
||||||
|
/// Returns `None` if signed transaction has unsupported format.
|
||||||
|
fn parse_transaction(tx: Self::SignedTransaction) -> Option<UnsignedTransaction<Self::Chain>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Block: BlockT> BlockWithJustification<Block::Header> for SignedBlock<Block> {
|
impl<Block: BlockT> BlockWithJustification<Block::Header> for SignedBlock<Block> {
|
||||||
@@ -128,6 +172,10 @@ impl<Block: BlockT> BlockWithJustification<Block::Header> for SignedBlock<Block>
|
|||||||
self.block.header().clone()
|
self.block.header().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extrinsics(&self) -> Vec<EncodedExtrinsic> {
|
||||||
|
self.block.extrinsics().iter().map(Encode::encode).collect()
|
||||||
|
}
|
||||||
|
|
||||||
fn justification(&self) -> Option<&EncodedJustification> {
|
fn justification(&self) -> Option<&EncodedJustification> {
|
||||||
self.justifications
|
self.justifications
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ use crate::rpc::Substrate;
|
|||||||
use crate::{ConnectionParams, Error, HeaderIdOf, Result};
|
use crate::{ConnectionParams, Error, HeaderIdOf, Result};
|
||||||
|
|
||||||
use async_std::sync::{Arc, Mutex};
|
use async_std::sync::{Arc, Mutex};
|
||||||
use codec::Decode;
|
use async_trait::async_trait;
|
||||||
|
use codec::{Decode, Encode};
|
||||||
use frame_system::AccountInfo;
|
use frame_system::AccountInfo;
|
||||||
use futures::{SinkExt, StreamExt};
|
use futures::{SinkExt, StreamExt};
|
||||||
use jsonrpsee_ws_client::{traits::SubscriptionClient, v2::params::JsonRpcParams, DeserializeOwned};
|
use jsonrpsee_ws_client::{traits::SubscriptionClient, v2::params::JsonRpcParams, DeserializeOwned};
|
||||||
@@ -31,12 +32,16 @@ use pallet_balances::AccountData;
|
|||||||
use pallet_transaction_payment::InclusionFee;
|
use pallet_transaction_payment::InclusionFee;
|
||||||
use relay_utils::{relay_loop::RECONNECT_DELAY, HeaderId};
|
use relay_utils::{relay_loop::RECONNECT_DELAY, HeaderId};
|
||||||
use sp_core::{storage::StorageKey, Bytes};
|
use sp_core::{storage::StorageKey, Bytes};
|
||||||
use sp_runtime::traits::Header as HeaderT;
|
use sp_runtime::{
|
||||||
|
traits::Header as HeaderT,
|
||||||
|
transaction_validity::{TransactionSource, TransactionValidity},
|
||||||
|
};
|
||||||
use sp_trie::StorageProof;
|
use sp_trie::StorageProof;
|
||||||
use sp_version::RuntimeVersion;
|
use sp_version::RuntimeVersion;
|
||||||
use std::{convert::TryFrom, future::Future};
|
use std::{convert::TryFrom, future::Future};
|
||||||
|
|
||||||
const SUB_API_GRANDPA_AUTHORITIES: &str = "GrandpaApi_grandpa_authorities";
|
const SUB_API_GRANDPA_AUTHORITIES: &str = "GrandpaApi_grandpa_authorities";
|
||||||
|
const SUB_API_TXPOOL_VALIDATE_TRANSACTION: &str = "TaggedTransactionQueue_validate_transaction";
|
||||||
const MAX_SUBSCRIPTION_CAPACITY: usize = 4096;
|
const MAX_SUBSCRIPTION_CAPACITY: usize = 4096;
|
||||||
|
|
||||||
/// Opaque justifications subscription type.
|
/// Opaque justifications subscription type.
|
||||||
@@ -63,6 +68,18 @@ pub struct Client<C: Chain> {
|
|||||||
submit_signed_extrinsic_lock: Arc<Mutex<()>>,
|
submit_signed_extrinsic_lock: Arc<Mutex<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<C: Chain> relay_utils::relay_loop::Client for Client<C> {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
async fn reconnect(&mut self) -> Result<()> {
|
||||||
|
let (tokio, client) = Self::build_client(self.params.clone()).await?;
|
||||||
|
self.tokio = tokio;
|
||||||
|
self.client = client;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<C: Chain> Clone for Client<C> {
|
impl<C: Chain> Clone for Client<C> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Client {
|
Client {
|
||||||
@@ -125,14 +142,6 @@ impl<C: Chain> Client<C> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reopen client connection.
|
|
||||||
pub async fn reconnect(&mut self) -> Result<()> {
|
|
||||||
let (tokio, client) = Self::build_client(self.params.clone()).await?;
|
|
||||||
self.tokio = tokio;
|
|
||||||
self.client = client;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build client to use in connection.
|
/// Build client to use in connection.
|
||||||
async fn build_client(params: ConnectionParams) -> Result<(Arc<tokio::runtime::Runtime>, Arc<RpcClient>)> {
|
async fn build_client(params: ConnectionParams) -> Result<(Arc<tokio::runtime::Runtime>, Arc<RpcClient>)> {
|
||||||
let tokio = tokio::runtime::Runtime::new()?;
|
let tokio = tokio::runtime::Runtime::new()?;
|
||||||
@@ -309,6 +318,33 @@ impl<C: Chain> Client<C> {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns pending extrinsics from transaction pool.
|
||||||
|
pub async fn pending_extrinsics(&self) -> Result<Vec<Bytes>> {
|
||||||
|
self.jsonrpsee_execute(
|
||||||
|
move |client| async move { Ok(Substrate::<C>::author_pending_extrinsics(&*client).await?) },
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validate transaction at given block state.
|
||||||
|
pub async fn validate_transaction<SignedTransaction: Encode + Send + 'static>(
|
||||||
|
&self,
|
||||||
|
at_block: C::Hash,
|
||||||
|
transaction: SignedTransaction,
|
||||||
|
) -> Result<TransactionValidity> {
|
||||||
|
self.jsonrpsee_execute(move |client| async move {
|
||||||
|
let call = SUB_API_TXPOOL_VALIDATE_TRANSACTION.to_string();
|
||||||
|
let data = Bytes((TransactionSource::External, transaction, at_block).encode());
|
||||||
|
|
||||||
|
let encoded_response = Substrate::<C>::state_call(&*client, call, data, Some(at_block)).await?;
|
||||||
|
let validity =
|
||||||
|
TransactionValidity::decode(&mut &encoded_response.0[..]).map_err(Error::ResponseParseFailed)?;
|
||||||
|
|
||||||
|
Ok(validity)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
/// Estimate fee that will be spent on given extrinsic.
|
/// Estimate fee that will be spent on given extrinsic.
|
||||||
pub async fn estimate_extrinsic_fee(&self, transaction: Bytes) -> Result<InclusionFee<C::Balance>> {
|
pub async fn estimate_extrinsic_fee(&self, transaction: Bytes) -> Result<InclusionFee<C::Balance>> {
|
||||||
self.jsonrpsee_execute(move |client| async move {
|
self.jsonrpsee_execute(move |client| async move {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
use jsonrpsee_ws_client::Error as RpcError;
|
use jsonrpsee_ws_client::Error as RpcError;
|
||||||
use relay_utils::MaybeConnectionError;
|
use relay_utils::MaybeConnectionError;
|
||||||
use sc_rpc_api::system::Health;
|
use sc_rpc_api::system::Health;
|
||||||
|
use sp_runtime::transaction_validity::TransactionValidityError;
|
||||||
|
|
||||||
/// Result type used by Substrate client.
|
/// Result type used by Substrate client.
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
@@ -44,6 +45,8 @@ pub enum Error {
|
|||||||
ClientNotSynced(Health),
|
ClientNotSynced(Health),
|
||||||
/// An error has happened when we have tried to parse storage proof.
|
/// An error has happened when we have tried to parse storage proof.
|
||||||
StorageProofError(bp_runtime::StorageProofError),
|
StorageProofError(bp_runtime::StorageProofError),
|
||||||
|
/// The Substrate transaction is invalid.
|
||||||
|
TransactionInvalid(TransactionValidityError),
|
||||||
/// Custom logic error.
|
/// Custom logic error.
|
||||||
Custom(String),
|
Custom(String),
|
||||||
}
|
}
|
||||||
@@ -59,6 +62,7 @@ impl std::error::Error for Error {
|
|||||||
Self::MissingMandatoryCodeEntry => None,
|
Self::MissingMandatoryCodeEntry => None,
|
||||||
Self::ClientNotSynced(_) => None,
|
Self::ClientNotSynced(_) => None,
|
||||||
Self::StorageProofError(_) => None,
|
Self::StorageProofError(_) => None,
|
||||||
|
Self::TransactionInvalid(_) => None,
|
||||||
Self::Custom(_) => None,
|
Self::Custom(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,6 +86,12 @@ impl From<tokio::task::JoinError> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<TransactionValidityError> for Error {
|
||||||
|
fn from(error: TransactionValidityError) -> Self {
|
||||||
|
Error::TransactionInvalid(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl MaybeConnectionError for Error {
|
impl MaybeConnectionError for Error {
|
||||||
fn is_connection_error(&self) -> bool {
|
fn is_connection_error(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
@@ -105,6 +115,7 @@ impl std::fmt::Display for Error {
|
|||||||
Self::MissingMandatoryCodeEntry => "Mandatory :code: entry is missing from runtime storage".into(),
|
Self::MissingMandatoryCodeEntry => "Mandatory :code: entry is missing from runtime storage".into(),
|
||||||
Self::StorageProofError(e) => format!("Error when parsing storage proof: {:?}", e),
|
Self::StorageProofError(e) => format!("Error when parsing storage proof: {:?}", e),
|
||||||
Self::ClientNotSynced(health) => format!("Substrate client is not synced: {}", health),
|
Self::ClientNotSynced(health) => format!("Substrate client is not synced: {}", health),
|
||||||
|
Self::TransactionInvalid(e) => format!("Substrate transaction is invalid: {:?}", e),
|
||||||
Self::Custom(e) => e.clone(),
|
Self::Custom(e) => e.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ pub mod metrics;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
pub use crate::chain::{
|
pub use crate::chain::{
|
||||||
BalanceOf, BlockWithJustification, Chain, ChainWithBalances, IndexOf, TransactionSignScheme, WeightToFeeOf,
|
BalanceOf, BlockWithJustification, Chain, ChainWithBalances, IndexOf, TransactionSignScheme, UnsignedTransaction,
|
||||||
|
WeightToFeeOf,
|
||||||
};
|
};
|
||||||
pub use crate::client::{Client, JustificationsSubscription, OpaqueGrandpaAuthoritiesSet};
|
pub use crate::client::{Client, JustificationsSubscription, OpaqueGrandpaAuthoritiesSet};
|
||||||
pub use crate::error::{Error, Result};
|
pub use crate::error::{Error, Result};
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ jsonrpsee_proc_macros::rpc_client_api! {
|
|||||||
fn system_account_next_index(account_id: C::AccountId) -> C::Index;
|
fn system_account_next_index(account_id: C::AccountId) -> C::Index;
|
||||||
#[rpc(method = "author_submitExtrinsic", positional_params)]
|
#[rpc(method = "author_submitExtrinsic", positional_params)]
|
||||||
fn author_submit_extrinsic(extrinsic: Bytes) -> C::Hash;
|
fn author_submit_extrinsic(extrinsic: Bytes) -> C::Hash;
|
||||||
|
#[rpc(method = "author_pendingExtrinsics", positional_params)]
|
||||||
|
fn author_pending_extrinsics() -> Vec<Bytes>;
|
||||||
#[rpc(method = "state_call", positional_params)]
|
#[rpc(method = "state_call", positional_params)]
|
||||||
fn state_call(method: String, data: Bytes, at_block: Option<C::Hash>) -> Bytes;
|
fn state_call(method: String, data: Bytes, at_block: Option<C::Hash>) -> Bytes;
|
||||||
#[rpc(method = "state_getStorage", positional_params)]
|
#[rpc(method = "state_getStorage", positional_params)]
|
||||||
|
|||||||
@@ -16,10 +16,8 @@
|
|||||||
|
|
||||||
//! Types used to connect to the Westend chain.
|
//! Types used to connect to the Westend chain.
|
||||||
|
|
||||||
use codec::Encode;
|
use relay_substrate_client::{Chain, ChainBase, ChainWithBalances};
|
||||||
use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, TransactionSignScheme};
|
use sp_core::storage::StorageKey;
|
||||||
use sp_core::{storage::StorageKey, Pair};
|
|
||||||
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
/// Westend header id.
|
/// Westend header id.
|
||||||
@@ -58,37 +56,3 @@ impl ChainWithBalances for Westend {
|
|||||||
StorageKey(bp_westend::account_info_storage_key(account_id))
|
StorageKey(bp_westend::account_info_storage_key(account_id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransactionSignScheme for Westend {
|
|
||||||
type Chain = Westend;
|
|
||||||
type AccountKeyPair = sp_core::sr25519::Pair;
|
|
||||||
type SignedTransaction = bp_westend::UncheckedExtrinsic;
|
|
||||||
|
|
||||||
fn sign_transaction(
|
|
||||||
genesis_hash: <Self::Chain as ChainBase>::Hash,
|
|
||||||
signer: &Self::AccountKeyPair,
|
|
||||||
era: relay_substrate_client::TransactionEraOf<Self::Chain>,
|
|
||||||
signer_nonce: <Self::Chain as Chain>::Index,
|
|
||||||
call: <Self::Chain as Chain>::Call,
|
|
||||||
) -> Self::SignedTransaction {
|
|
||||||
let raw_payload = SignedPayload::new(
|
|
||||||
call,
|
|
||||||
bp_westend::SignedExtensions::new(bp_westend::VERSION, era, genesis_hash, signer_nonce, 0),
|
|
||||||
)
|
|
||||||
.expect("SignedExtension never fails.");
|
|
||||||
|
|
||||||
let signature = raw_payload.using_encoded(|payload| signer.sign(payload));
|
|
||||||
let signer: sp_runtime::MultiSigner = signer.public().into();
|
|
||||||
let (call, extra, _) = raw_payload.deconstruct();
|
|
||||||
|
|
||||||
bp_westend::UncheckedExtrinsic::new_signed(
|
|
||||||
call,
|
|
||||||
sp_runtime::MultiAddress::Id(signer.into_account()),
|
|
||||||
signature.into(),
|
|
||||||
extra,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Westend signing params.
|
|
||||||
pub type SigningParams = sp_core::sr25519::Pair;
|
|
||||||
|
|||||||
@@ -17,7 +17,9 @@
|
|||||||
//! Types used to connect to the Wococo-Substrate chain.
|
//! Types used to connect to the Wococo-Substrate chain.
|
||||||
|
|
||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, TransactionSignScheme};
|
use relay_substrate_client::{
|
||||||
|
Chain, ChainBase, ChainWithBalances, TransactionEraOf, TransactionSignScheme, UnsignedTransaction,
|
||||||
|
};
|
||||||
use sp_core::{storage::StorageKey, Pair};
|
use sp_core::{storage::StorageKey, Pair};
|
||||||
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
|
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -69,13 +71,12 @@ impl TransactionSignScheme for Wococo {
|
|||||||
fn sign_transaction(
|
fn sign_transaction(
|
||||||
genesis_hash: <Self::Chain as ChainBase>::Hash,
|
genesis_hash: <Self::Chain as ChainBase>::Hash,
|
||||||
signer: &Self::AccountKeyPair,
|
signer: &Self::AccountKeyPair,
|
||||||
era: bp_runtime::TransactionEraOf<Self::Chain>,
|
era: TransactionEraOf<Self::Chain>,
|
||||||
signer_nonce: <Self::Chain as Chain>::Index,
|
unsigned: UnsignedTransaction<Self::Chain>,
|
||||||
call: <Self::Chain as Chain>::Call,
|
|
||||||
) -> Self::SignedTransaction {
|
) -> Self::SignedTransaction {
|
||||||
let raw_payload = SignedPayload::new(
|
let raw_payload = SignedPayload::new(
|
||||||
call,
|
unsigned.call,
|
||||||
bp_wococo::SignedExtensions::new(bp_wococo::VERSION, era, genesis_hash, signer_nonce, 0),
|
bp_wococo::SignedExtensions::new(bp_wococo::VERSION, era, genesis_hash, unsigned.nonce, unsigned.tip),
|
||||||
)
|
)
|
||||||
.expect("SignedExtension never fails.");
|
.expect("SignedExtension never fails.");
|
||||||
|
|
||||||
@@ -90,6 +91,26 @@ impl TransactionSignScheme for Wococo {
|
|||||||
extra,
|
extra,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_signed(tx: &Self::SignedTransaction) -> bool {
|
||||||
|
tx.signature.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool {
|
||||||
|
tx.signature
|
||||||
|
.as_ref()
|
||||||
|
.map(|(address, _, _)| *address == bp_wococo::AccountId::from(*signer.public().as_array_ref()).into())
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_transaction(tx: Self::SignedTransaction) -> Option<UnsignedTransaction<Self::Chain>> {
|
||||||
|
let extra = &tx.signature.as_ref()?.2;
|
||||||
|
Some(UnsignedTransaction {
|
||||||
|
call: tx.function,
|
||||||
|
nonce: extra.nonce(),
|
||||||
|
tip: extra.tip(),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wococo signing params.
|
/// Wococo signing params.
|
||||||
|
|||||||
@@ -34,6 +34,15 @@ pub trait Client: 'static + Clone + Send + Sync {
|
|||||||
async fn reconnect(&mut self) -> Result<(), Self::Error>;
|
async fn reconnect(&mut self) -> Result<(), Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Client for () {
|
||||||
|
type Error = crate::StringifiedMaybeConnectionError;
|
||||||
|
|
||||||
|
async fn reconnect(&mut self) -> Result<(), Self::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns generic loop that may be customized and started.
|
/// Returns generic loop that may be customized and started.
|
||||||
pub fn relay_loop<SC, TC>(source_client: SC, target_client: TC) -> Loop<SC, TC, ()> {
|
pub fn relay_loop<SC, TC>(source_client: SC, target_client: TC) -> Loop<SC, TC, ()> {
|
||||||
Loop {
|
Loop {
|
||||||
|
|||||||
Reference in New Issue
Block a user