mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 23:21:02 +00:00
Update bridges subtree (#5165)
* Squashed 'bridges/' changes from 1602249f0a..f220d2fcca f220d2fcca Polkadot staging update (#1356) 02fd3d497c fix parse_transaction on Rialto+Millau (#1360) bc191fd9a2 update parity-scale-codec to 3.1.2 (#1359) a37226e79c update chain versions (#1358) ff5d539fcb Update Substrate/Polkadot/Cumulus references (#1353) 1581f60cd5 Support dedicated lanes for pallets (#962) 0a7ccf5c57 ignore more "increase" alerts that are sometimes signalling NoData at startup (#1351) 31165127cc added no_stack_overflow_when_decoding_nested_call_during_dispatch test (#1349) 7000619eb8 replace From<>InboundLaneApi with direct storage reads (#1348) 515df10ccc added alerts for relay balances (#1347) b56f6a87de Mortal conversion rate updater transactions (#1257) 20f2f331ec edition = "2021" (#1346) 99147d4f75 update regex to 1.5.5 (#1345) 686191f379 use DecodeLimit when decoding incoming calls (#1344) a70c276006 get rid of '[No Data] Messages from Millau to Rialto are not being delivered' warnings (#1342) 01f29b8ac1 fix conversion rate metric in dashboards (#1341) 51c3bf351f Increase rate from metric when estimating fee (#1340) 3bb9c4f68f fix generator scripts to be consistent with updatedrelay output (#1339) 0475a1667b fixed mess with conversion rates (#1338) d8fdd7d716 synchronize relay cli changes and token swap generator script (#1337) 6e928137a5 fix conversion rate override in token swap (#1336) 62d4a4811d override conversion rate in tokens swap generator (#1335) ed9e1c839c fi typo in generator script (#1334) 3254b5af7a Override conversion rate when computing message fee (#1261) 66df68b5b8 Revert "Revert "override conversion rate in estimate-message-fee RPC (#1189)" (#1275)" (#1333) 0ca6fc6ef8 fix clippy issues (#1332) 5414b2fffb Reinitialize bridge relay subcommand (#1331) a63d95ba7d removed extra *_RUNTIME_VERSION consts from relay code (#1330) 59fb18a310 fix typo in alert expression (#1329) a6267a47ee Using-same-fork metric for finality and complex relay (#1327) 88d684d37e use mortal transactions in transaction resubmitter (#1326) 8ff88b6844 impl Decode for SignedExtensions (otherwise transaction resubmitter panicks) (#1325) 1ed09854f0 Encode and estimate Rococo/Wococo/Kusama/Polkadot messages (#1322) ddb4517e13 Add some tests to check integrity of chain constants + bridge configuration (#1316) bdeedb7ab9 Fix issues from cargo deny (#1311) d3d79d01e0 expose fee multiplier metrics in messages relay (#1312) c8b3f0ea16 Endow relayer account at target chain in message benchmarks (#1310) f51ecd92b6 fix benchmarks before using it in Polkadot/Kusama/Rococo runtimes (#1309) 6935c619ad increase relay balance guard limits for Polkadot<>Kusama bridge (#1308) 7e31834c66 Fix mandatory headers scanning in on-demand relay (#1306) 92ddc3ea7a Polkadot-staging update (#1305) 3787193a31 fix session length of Rococo and Wococo (#1304) eb468d29c0 Revert nightly docker pin (#1301) e2d4c073e1 Use raw balance value if tokenDecimals property is missing (#1299) 108f4b29d1 Fix ss58 prefixes of Polkadot, Kusama and Westend used by relay (#1298) 64fbd2705e bump chain spec versions (#1297) 5707777b86 Bump Substrate/Polkadot/Cumulus refs (#1295) 29eecdf1fa Merge pull request #1294 from paritytech/polkadot-staging-update 1f0c05368e Relay balance metrics (#1291) 6356bb90b3 when messages pallet is halted, relay shall not submit messages delivery/confirmation transactions (#1289) 800dc2df8d when GRANDPA pallet is halted, relay shall not submit finality transactions (#1288) 3dd8e4f936 disable BEEFY allerts for Rialto (#1285) f58fed7380 support version mode cli options in send-message subcommand (#1284) 3aac448da3 reuse polkadot-service code (#1273) 2bdbb651e1 replace latest_confirmed_nonce runtime APIs with direct storage reads (#1282) 5f9c6d241f move "common" code of messages pallet benchmarks helpers to the common library (#1281) 173d2d8229 Merge pull request #1280 from paritytech/polkadot-staging-update 8b9c4ec16d do not start spec_version guard when version mode is set to auto (#1278) e98d682de2 removed extra messages benchmarks (#1279) c730e25b61 Move benchmarks from Rialto to Millau (#1277) 54146416e7 Merge pull request #1276 from paritytech/polkadot-staging-update df70118174 Merge branch 'master' into polkadot-staging-update ed7def64c4 Revert "override conversion rate in estimate-message-fee RPC (#1189)" (#1275) 38c6c3a49f Use "production" floating tag when uilding docker image from version git tags (#1272) ded9ff6dbb Replace InboundLaneApi::latest_received_nonce with direct storage read (#1269) f704a741ee Polkadot staging update (#1270) 8c65f0d7ab verify that GRANDPA pallet is not initialized before submitting initialization transaction (#1267) e7e83d8944 remove OutboundLaneApi::latest_received_nonce (#1262) 9f4b34acf1 bump rococo version (#1263) 82c08c5a87 read latest_generated_nonce directly from storage (#1260) 50ffb5dd08 override conversion rate in estimate-message-fee RPC (#1189) 467ca5ef59 move storage keys computation to primitivs (#1254) 4f9884066b remporary use pinned bridges-ci image in Dockerfile (#1258) edfcb74e00 Change submit transaction spec_version and transaction_version query from chain (#1248) 4009d970d0 pin bridges-ci image (#1256) 65e51b5e1c decrease startup sleep to 5s for relays and to 120s for generators + remove curl (#1251) 3bc74355d9 Add missing RPC APIs to rialto parachain node (#1250) 80c9429284 Bump relay version to 1.0.0 (#1249) 9ead06af2a runtimes: fix call_size() test (#1245) 4fc8a29357 Use same endowed accounts set on dev/local chains (#1244) fed54371c2 Refactor message relay helpers (#1234) a15b4faae7 post-merge build fix (#1243) 52232d8d54 Fix transactions mortality (#1196) c07bba931f Expose prometheus BEEFY metrics and add them to grafana dashboard (#1242) f927775bd5 Refactor finality relay helpers (#1220) 7bf76f14a8 Update Rococo/Wococo version + prepare relay for Rococo<>Wococo bridge (#1241) e860fecd04 Enable offchain indexing for Rialto/Millau nodes (#1239) 04d4d1c6b4 Enable Beefy debug logs in test deployment (#1237) cd771f1089 Fix storage parameter name computation (#1238) 816ddd2dd2 Integrate BEEFY with Rialto & Millau runtimes (#1227) d94b62b1ac update dependencies (#1229) 98eb9ee13d Add mut support (#1232) ffef6f89f9 fixed set_operational in GRANDPA pallet (#1226) bd2f8bfbd7 Add CODEOWNERS file (#1219) 6b5cf2b591 Unify metric names (#1209) d1541e797e remove abandoned exchange relay (#1217) 39140d0b34 Remove unused `relays/headers` (#1216) 9bc071d42b Remove unused PoA<>Substrate bridge (#1210) 877e8d01e3 Fix UI deployment. (#1211) 6cd5775ebe Add `AtLeast32BitUnsigned` for MessageLance::SourceChainBalance (#1207) git-subtree-dir: bridges git-subtree-split: f220d2fccabbf141101d19456ecb4e3576a1d797 * fix compilation warnings
This commit is contained in:
committed by
GitHub
parent
20da356434
commit
8e01ba9c03
@@ -2,25 +2,27 @@
|
||||
name = "relay-substrate-client"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||
|
||||
[dependencies]
|
||||
async-std = { version = "1.6.5", features = ["attributes"] }
|
||||
async-trait = "0.1.40"
|
||||
codec = { package = "parity-scale-codec", version = "2.2.0" }
|
||||
jsonrpsee-proc-macros = "0.3.1"
|
||||
jsonrpsee-ws-client = "0.3.1"
|
||||
codec = { package = "parity-scale-codec", version = "3.0.0" }
|
||||
jsonrpsee = { version = "0.8", features = ["macros", "ws-client"] }
|
||||
log = "0.4.11"
|
||||
num-traits = "0.2"
|
||||
rand = "0.7"
|
||||
tokio = "1.8"
|
||||
serde = { version = "1.0" }
|
||||
tokio = { version = "1.8", features = ["rt-multi-thread"] }
|
||||
thiserror = "1.0.26"
|
||||
|
||||
# Bridge dependencies
|
||||
|
||||
bp-header-chain = { path = "../../primitives/header-chain" }
|
||||
bp-messages = { path = "../../primitives/messages" }
|
||||
bp-runtime = { path = "../../primitives/runtime" }
|
||||
pallet-bridge-messages = { path = "../../modules/messages" }
|
||||
finality-relay = { path = "../finality" }
|
||||
relay-utils = { path = "../utils" }
|
||||
|
||||
@@ -31,9 +33,10 @@ frame-system = { git = "https://github.com/paritytech/substrate", branch = "mast
|
||||
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||
pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||
pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||
sc-chain-spec = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||
sc-rpc-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||
sc-transaction-pool-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||
sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||
sp-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||
|
||||
@@ -14,10 +14,11 @@
|
||||
// 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 bp_runtime::{Chain as ChainBase, HashOf, TransactionEraOf};
|
||||
use bp_messages::MessageNonce;
|
||||
use bp_runtime::{Chain as ChainBase, EncodedOrDecodedCall, HashOf, TransactionEraOf};
|
||||
use codec::{Codec, Encode};
|
||||
use frame_support::weights::WeightToFeePolynomial;
|
||||
use jsonrpsee_ws_client::types::{DeserializeOwned, Serialize};
|
||||
use frame_support::weights::{Weight, WeightToFeePolynomial};
|
||||
use jsonrpsee::core::{DeserializeOwned, Serialize};
|
||||
use num_traits::Zero;
|
||||
use sc_transaction_pool_api::TransactionStatus;
|
||||
use sp_core::{storage::StorageKey, Pair};
|
||||
@@ -32,6 +33,18 @@ use std::{fmt::Debug, time::Duration};
|
||||
pub trait Chain: ChainBase + Clone {
|
||||
/// Chain name.
|
||||
const NAME: &'static str;
|
||||
/// Identifier of the basic token of the chain (if applicable).
|
||||
///
|
||||
/// This identifier is used to fetch token price. In case of testnets, you may either
|
||||
/// set it to `None`, or associate testnet with one of the existing tokens.
|
||||
const TOKEN_ID: Option<&'static str>;
|
||||
/// Name of the runtime API method that is returning best known finalized header number
|
||||
/// and hash (as tuple).
|
||||
///
|
||||
/// Keep in mind that this method is normally provided by the other chain, which is
|
||||
/// bridged with this chain.
|
||||
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str;
|
||||
|
||||
/// Average block interval.
|
||||
///
|
||||
/// How often blocks are produced on that chain. It's suggested to set this value
|
||||
@@ -45,12 +58,54 @@ pub trait Chain: ChainBase + Clone {
|
||||
/// Block type.
|
||||
type SignedBlock: Member + Serialize + DeserializeOwned + BlockWithJustification<Self::Header>;
|
||||
/// The aggregated `Call` type.
|
||||
type Call: Clone + Dispatchable + Debug;
|
||||
type Call: Clone + Codec + Dispatchable + Debug + Send;
|
||||
|
||||
/// Type that is used by the chain, to convert from weight to fee.
|
||||
type WeightToFee: WeightToFeePolynomial<Balance = Self::Balance>;
|
||||
}
|
||||
|
||||
/// Substrate-based chain that is using direct GRANDPA finality from minimal relay-client point of
|
||||
/// view.
|
||||
///
|
||||
/// Keep in mind that parachains are relying on relay chain GRANDPA, so they should not implement
|
||||
/// this trait.
|
||||
pub trait ChainWithGrandpa: Chain {
|
||||
/// Name of the bridge GRANDPA pallet (used in `construct_runtime` macro call) that is deployed
|
||||
/// at some other chain to bridge with this `ChainWithGrandpa`.
|
||||
///
|
||||
/// We assume that all chains that are bridging with this `ChainWithGrandpa` are using
|
||||
/// the same name.
|
||||
const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str;
|
||||
}
|
||||
|
||||
/// Substrate-based chain with messaging support from minimal relay-client point of view.
|
||||
pub trait ChainWithMessages: Chain {
|
||||
/// Name of the bridge messages pallet (used in `construct_runtime` macro call) that is deployed
|
||||
/// at some other chain to bridge with this `ChainWithMessages`.
|
||||
///
|
||||
/// We assume that all chains that are bridging with this `ChainWithMessages` are using
|
||||
/// the same name.
|
||||
const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str;
|
||||
|
||||
/// Name of the `To<ChainWithMessages>OutboundLaneApi::message_details` runtime API method.
|
||||
/// The method is provided by the runtime that is bridged with this `ChainWithMessages`.
|
||||
const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str;
|
||||
|
||||
/// Additional weight of the dispatch fee payment if dispatch is paid at the target chain
|
||||
/// and this `ChainWithMessages` is the target chain.
|
||||
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight;
|
||||
|
||||
/// Maximal number of unrewarded relayers in a single confirmation transaction at this
|
||||
/// `ChainWithMessages`.
|
||||
const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce;
|
||||
/// Maximal number of unconfirmed messages in a single confirmation transaction at this
|
||||
/// `ChainWithMessages`.
|
||||
const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce;
|
||||
|
||||
/// Weights of message pallet calls.
|
||||
type WeightInfo: pallet_bridge_messages::WeightInfoExt;
|
||||
}
|
||||
|
||||
/// Call type used by the chain.
|
||||
pub type CallOf<C> = <C as Chain>::Call;
|
||||
/// Weight-to-Fee type used by the chain.
|
||||
@@ -58,7 +113,7 @@ pub type WeightToFeeOf<C> = <C as Chain>::WeightToFee;
|
||||
/// Transaction status of the chain.
|
||||
pub type TransactionStatusOf<C> = TransactionStatus<HashOf<C>, HashOf<C>>;
|
||||
|
||||
/// Substrate-based chain with `frame_system::Config::AccountData` set to
|
||||
/// Substrate-based chain with `AccountData` generic argument of `frame_system::AccountInfo` set to
|
||||
/// the `pallet_balances::AccountData<Balance>`.
|
||||
pub trait ChainWithBalances: Chain {
|
||||
/// Return runtime storage key for getting `frame_system::AccountInfo` of given account.
|
||||
@@ -79,10 +134,10 @@ pub trait BlockWithJustification<Header> {
|
||||
}
|
||||
|
||||
/// Transaction before it is signed.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct UnsignedTransaction<C: Chain> {
|
||||
/// Runtime call of this transaction.
|
||||
pub call: C::Call,
|
||||
pub call: EncodedOrDecodedCall<C::Call>,
|
||||
/// Transaction nonce.
|
||||
pub nonce: C::Index,
|
||||
/// Tip included into transaction.
|
||||
@@ -91,7 +146,7 @@ pub struct UnsignedTransaction<C: Chain> {
|
||||
|
||||
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 {
|
||||
pub fn new(call: EncodedOrDecodedCall<C::Call>, nonce: C::Index) -> Self {
|
||||
Self { call, nonce, tip: Zero::zero() }
|
||||
}
|
||||
|
||||
@@ -102,6 +157,9 @@ impl<C: Chain> UnsignedTransaction<C> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Account key pair used by transactions signing scheme.
|
||||
pub type AccountKeyPairOf<S> = <S as TransactionSignScheme>::AccountKeyPair;
|
||||
|
||||
/// Substrate-based chain transactions signing scheme.
|
||||
pub trait TransactionSignScheme {
|
||||
/// Chain that this scheme is to be used.
|
||||
@@ -112,12 +170,9 @@ pub trait TransactionSignScheme {
|
||||
type SignedTransaction: Clone + Debug + Codec + Send + 'static;
|
||||
|
||||
/// Create transaction for given runtime call, signed by given account.
|
||||
fn sign_transaction(
|
||||
genesis_hash: <Self::Chain as ChainBase>::Hash,
|
||||
signer: &Self::AccountKeyPair,
|
||||
era: TransactionEraOf<Self::Chain>,
|
||||
unsigned: UnsignedTransaction<Self::Chain>,
|
||||
) -> Self::SignedTransaction;
|
||||
fn sign_transaction(param: SignParam<Self>) -> Result<Self::SignedTransaction, crate::Error>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Returns true if transaction is signed.
|
||||
fn is_signed(tx: &Self::SignedTransaction) -> bool;
|
||||
@@ -131,6 +186,22 @@ pub trait TransactionSignScheme {
|
||||
fn parse_transaction(tx: Self::SignedTransaction) -> Option<UnsignedTransaction<Self::Chain>>;
|
||||
}
|
||||
|
||||
/// Sign transaction parameters
|
||||
pub struct SignParam<T: TransactionSignScheme> {
|
||||
/// Version of the runtime specification.
|
||||
pub spec_version: u32,
|
||||
/// Transaction version
|
||||
pub transaction_version: u32,
|
||||
/// Hash of the genesis block.
|
||||
pub genesis_hash: <T::Chain as ChainBase>::Hash,
|
||||
/// Signer account
|
||||
pub signer: T::AccountKeyPair,
|
||||
/// Transaction era used by the chain.
|
||||
pub era: TransactionEraOf<T::Chain>,
|
||||
/// Transaction before it is signed.
|
||||
pub unsigned: UnsignedTransaction<T::Chain>,
|
||||
}
|
||||
|
||||
impl<Block: BlockT> BlockWithJustification<Block::Header> for SignedBlock<Block> {
|
||||
fn header(&self) -> Block::Header {
|
||||
self.block.header().clone()
|
||||
|
||||
@@ -18,8 +18,9 @@
|
||||
|
||||
use crate::{
|
||||
chain::{Chain, ChainWithBalances, TransactionStatusOf},
|
||||
rpc::Substrate,
|
||||
ConnectionParams, Error, HashOf, HeaderIdOf, Result,
|
||||
rpc::SubstrateClient,
|
||||
AccountIdOf, BlockNumberOf, ConnectionParams, Error, HashOf, HeaderIdOf, HeaderOf, IndexOf,
|
||||
Result,
|
||||
};
|
||||
|
||||
use async_std::sync::{Arc, Mutex};
|
||||
@@ -27,14 +28,12 @@ use async_trait::async_trait;
|
||||
use codec::{Decode, Encode};
|
||||
use frame_system::AccountInfo;
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use jsonrpsee_ws_client::{
|
||||
types::{
|
||||
self as jsonrpsee_types, traits::SubscriptionClient, v2::params::JsonRpcParams,
|
||||
DeserializeOwned,
|
||||
},
|
||||
WsClient as RpcClient, WsClientBuilder as RpcClientBuilder,
|
||||
use jsonrpsee::{
|
||||
core::{client::SubscriptionClientT, DeserializeOwned},
|
||||
types::params::ParamsSer,
|
||||
ws_client::{WsClient as RpcClient, WsClientBuilder as RpcClientBuilder},
|
||||
};
|
||||
use num_traits::{Bounded, Zero};
|
||||
use num_traits::{Bounded, CheckedSub, One, Zero};
|
||||
use pallet_balances::AccountData;
|
||||
use pallet_transaction_payment::InclusionFee;
|
||||
use relay_utils::{relay_loop::RECONNECT_DELAY, HeaderId};
|
||||
@@ -60,6 +59,17 @@ pub struct Subscription<T>(Mutex<futures::channel::mpsc::Receiver<Option<T>>>);
|
||||
/// Opaque GRANDPA authorities set.
|
||||
pub type OpaqueGrandpaAuthoritiesSet = Vec<u8>;
|
||||
|
||||
/// Chain runtime version in client
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ChainRuntimeVersion {
|
||||
/// Auto query from chain.
|
||||
Auto,
|
||||
/// Custom runtime version, defined by user.
|
||||
/// the first is `spec_version`
|
||||
/// the second is `transaction_version`
|
||||
Custom(u32, u32),
|
||||
}
|
||||
|
||||
/// Substrate client type.
|
||||
///
|
||||
/// Cloning `Client` is a cheap operation.
|
||||
@@ -77,6 +87,8 @@ pub struct Client<C: Chain> {
|
||||
/// transactions will be rejected from the pool. This lock is here to prevent situations like
|
||||
/// that.
|
||||
submit_signed_extrinsic_lock: Arc<Mutex<()>>,
|
||||
/// Saved chain runtime version
|
||||
chain_runtime_version: ChainRuntimeVersion,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -99,6 +111,7 @@ impl<C: Chain> Clone for Client<C> {
|
||||
client: self.client.clone(),
|
||||
genesis_hash: self.genesis_hash,
|
||||
submit_signed_extrinsic_lock: self.submit_signed_extrinsic_lock.clone(),
|
||||
chain_runtime_version: self.chain_runtime_version.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,16 +153,26 @@ impl<C: Chain> Client<C> {
|
||||
let genesis_hash_client = client.clone();
|
||||
let genesis_hash = tokio
|
||||
.spawn(async move {
|
||||
Substrate::<C>::chain_get_block_hash(&*genesis_hash_client, number).await
|
||||
SubstrateClient::<
|
||||
AccountIdOf<C>,
|
||||
BlockNumberOf<C>,
|
||||
HashOf<C>,
|
||||
HeaderOf<C>,
|
||||
IndexOf<C>,
|
||||
C::SignedBlock,
|
||||
>::chain_get_block_hash(&*genesis_hash_client, Some(number))
|
||||
.await
|
||||
})
|
||||
.await??;
|
||||
|
||||
let chain_runtime_version = params.chain_runtime_version.clone();
|
||||
Ok(Self {
|
||||
tokio,
|
||||
params,
|
||||
client,
|
||||
genesis_hash,
|
||||
submit_signed_extrinsic_lock: Arc::new(Mutex::new(())),
|
||||
chain_runtime_version,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -178,10 +201,31 @@ impl<C: Chain> Client<C> {
|
||||
}
|
||||
|
||||
impl<C: Chain> Client<C> {
|
||||
/// Return simple runtime version, only include `spec_version` and `transaction_version`.
|
||||
pub async fn simple_runtime_version(&self) -> Result<(u32, u32)> {
|
||||
let (spec_version, transaction_version) = match self.chain_runtime_version {
|
||||
ChainRuntimeVersion::Auto => {
|
||||
let runtime_version = self.runtime_version().await?;
|
||||
(runtime_version.spec_version, runtime_version.transaction_version)
|
||||
},
|
||||
ChainRuntimeVersion::Custom(spec_version, transaction_version) =>
|
||||
(spec_version, transaction_version),
|
||||
};
|
||||
Ok((spec_version, transaction_version))
|
||||
}
|
||||
|
||||
/// Returns true if client is connected to at least one peer and is in synced state.
|
||||
pub async fn ensure_synced(&self) -> Result<()> {
|
||||
self.jsonrpsee_execute(|client| async move {
|
||||
let health = Substrate::<C>::system_health(&*client).await?;
|
||||
let health = SubstrateClient::<
|
||||
AccountIdOf<C>,
|
||||
BlockNumberOf<C>,
|
||||
HashOf<C>,
|
||||
HeaderOf<C>,
|
||||
IndexOf<C>,
|
||||
C::SignedBlock,
|
||||
>::system_health(&*client)
|
||||
.await?;
|
||||
let is_synced = !health.is_syncing && (!health.should_have_peers || health.peers > 0);
|
||||
if is_synced {
|
||||
Ok(())
|
||||
@@ -200,7 +244,15 @@ impl<C: Chain> Client<C> {
|
||||
/// Return hash of the best finalized block.
|
||||
pub async fn best_finalized_header_hash(&self) -> Result<C::Hash> {
|
||||
self.jsonrpsee_execute(|client| async move {
|
||||
Ok(Substrate::<C>::chain_get_finalized_head(&*client).await?)
|
||||
Ok(SubstrateClient::<
|
||||
AccountIdOf<C>,
|
||||
BlockNumberOf<C>,
|
||||
HashOf<C>,
|
||||
HeaderOf<C>,
|
||||
IndexOf<C>,
|
||||
C::SignedBlock,
|
||||
>::chain_get_finalized_head(&*client)
|
||||
.await?)
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -216,7 +268,15 @@ impl<C: Chain> Client<C> {
|
||||
C::Header: DeserializeOwned,
|
||||
{
|
||||
self.jsonrpsee_execute(|client| async move {
|
||||
Ok(Substrate::<C>::chain_get_header(&*client, None).await?)
|
||||
Ok(SubstrateClient::<
|
||||
AccountIdOf<C>,
|
||||
BlockNumberOf<C>,
|
||||
HashOf<C>,
|
||||
HeaderOf<C>,
|
||||
IndexOf<C>,
|
||||
C::SignedBlock,
|
||||
>::chain_get_header(&*client, None)
|
||||
.await?)
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -224,7 +284,15 @@ impl<C: Chain> Client<C> {
|
||||
/// Get a Substrate block from its hash.
|
||||
pub async fn get_block(&self, block_hash: Option<C::Hash>) -> Result<C::SignedBlock> {
|
||||
self.jsonrpsee_execute(move |client| async move {
|
||||
Ok(Substrate::<C>::chain_get_block(&*client, block_hash).await?)
|
||||
Ok(SubstrateClient::<
|
||||
AccountIdOf<C>,
|
||||
BlockNumberOf<C>,
|
||||
HashOf<C>,
|
||||
HeaderOf<C>,
|
||||
IndexOf<C>,
|
||||
C::SignedBlock,
|
||||
>::chain_get_block(&*client, block_hash)
|
||||
.await?)
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -235,7 +303,15 @@ impl<C: Chain> Client<C> {
|
||||
C::Header: DeserializeOwned,
|
||||
{
|
||||
self.jsonrpsee_execute(move |client| async move {
|
||||
Ok(Substrate::<C>::chain_get_header(&*client, block_hash).await?)
|
||||
Ok(SubstrateClient::<
|
||||
AccountIdOf<C>,
|
||||
BlockNumberOf<C>,
|
||||
HashOf<C>,
|
||||
HeaderOf<C>,
|
||||
IndexOf<C>,
|
||||
C::SignedBlock,
|
||||
>::chain_get_header(&*client, Some(block_hash))
|
||||
.await?)
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -243,7 +319,15 @@ impl<C: Chain> Client<C> {
|
||||
/// Get a Substrate block hash by its number.
|
||||
pub async fn block_hash_by_number(&self, number: C::BlockNumber) -> Result<C::Hash> {
|
||||
self.jsonrpsee_execute(move |client| async move {
|
||||
Ok(Substrate::<C>::chain_get_block_hash(&*client, number).await?)
|
||||
Ok(SubstrateClient::<
|
||||
AccountIdOf<C>,
|
||||
BlockNumberOf<C>,
|
||||
HashOf<C>,
|
||||
HeaderOf<C>,
|
||||
IndexOf<C>,
|
||||
C::SignedBlock,
|
||||
>::chain_get_block_hash(&*client, Some(number))
|
||||
.await?)
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -261,7 +345,15 @@ impl<C: Chain> Client<C> {
|
||||
/// Return runtime version.
|
||||
pub async fn runtime_version(&self) -> Result<RuntimeVersion> {
|
||||
self.jsonrpsee_execute(move |client| async move {
|
||||
Ok(Substrate::<C>::state_runtime_version(&*client).await?)
|
||||
Ok(SubstrateClient::<
|
||||
AccountIdOf<C>,
|
||||
BlockNumberOf<C>,
|
||||
HashOf<C>,
|
||||
HeaderOf<C>,
|
||||
IndexOf<C>,
|
||||
C::SignedBlock,
|
||||
>::state_runtime_version(&*client)
|
||||
.await?)
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -287,7 +379,15 @@ impl<C: Chain> Client<C> {
|
||||
block_hash: Option<C::Hash>,
|
||||
) -> Result<Option<StorageData>> {
|
||||
self.jsonrpsee_execute(move |client| async move {
|
||||
Ok(Substrate::<C>::state_get_storage(&*client, storage_key, block_hash).await?)
|
||||
Ok(SubstrateClient::<
|
||||
AccountIdOf<C>,
|
||||
BlockNumberOf<C>,
|
||||
HashOf<C>,
|
||||
HeaderOf<C>,
|
||||
IndexOf<C>,
|
||||
C::SignedBlock,
|
||||
>::state_get_storage(&*client, storage_key, block_hash)
|
||||
.await?)
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -299,10 +399,16 @@ impl<C: Chain> Client<C> {
|
||||
{
|
||||
self.jsonrpsee_execute(move |client| async move {
|
||||
let storage_key = C::account_info_storage_key(&account);
|
||||
let encoded_account_data =
|
||||
Substrate::<C>::state_get_storage(&*client, storage_key, None)
|
||||
.await?
|
||||
.ok_or(Error::AccountDoesNotExist)?;
|
||||
let encoded_account_data = SubstrateClient::<
|
||||
AccountIdOf<C>,
|
||||
BlockNumberOf<C>,
|
||||
HashOf<C>,
|
||||
HeaderOf<C>,
|
||||
IndexOf<C>,
|
||||
C::SignedBlock,
|
||||
>::state_get_storage(&*client, storage_key, None)
|
||||
.await?
|
||||
.ok_or(Error::AccountDoesNotExist)?;
|
||||
let decoded_account_data = AccountInfo::<C::Index, AccountData<C::Balance>>::decode(
|
||||
&mut &encoded_account_data.0[..],
|
||||
)
|
||||
@@ -317,7 +423,15 @@ impl<C: Chain> Client<C> {
|
||||
/// Note: It's the caller's responsibility to make sure `account` is a valid SS58 address.
|
||||
pub async fn next_account_index(&self, account: C::AccountId) -> Result<C::Index> {
|
||||
self.jsonrpsee_execute(move |client| async move {
|
||||
Ok(Substrate::<C>::system_account_next_index(&*client, account).await?)
|
||||
Ok(SubstrateClient::<
|
||||
AccountIdOf<C>,
|
||||
BlockNumberOf<C>,
|
||||
HashOf<C>,
|
||||
HeaderOf<C>,
|
||||
IndexOf<C>,
|
||||
C::SignedBlock,
|
||||
>::system_account_next_index(&*client, account)
|
||||
.await?)
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -327,7 +441,15 @@ impl<C: Chain> Client<C> {
|
||||
/// Note: The given transaction needs to be SCALE encoded beforehand.
|
||||
pub async fn submit_unsigned_extrinsic(&self, transaction: Bytes) -> Result<C::Hash> {
|
||||
self.jsonrpsee_execute(move |client| async move {
|
||||
let tx_hash = Substrate::<C>::author_submit_extrinsic(&*client, transaction).await?;
|
||||
let tx_hash = SubstrateClient::<
|
||||
AccountIdOf<C>,
|
||||
BlockNumberOf<C>,
|
||||
HashOf<C>,
|
||||
HeaderOf<C>,
|
||||
IndexOf<C>,
|
||||
C::SignedBlock,
|
||||
>::author_submit_extrinsic(&*client, transaction)
|
||||
.await?;
|
||||
log::trace!(target: "bridge", "Sent transaction to Substrate node: {:?}", tx_hash);
|
||||
Ok(tx_hash)
|
||||
})
|
||||
@@ -344,15 +466,33 @@ impl<C: Chain> Client<C> {
|
||||
pub async fn submit_signed_extrinsic(
|
||||
&self,
|
||||
extrinsic_signer: C::AccountId,
|
||||
prepare_extrinsic: impl FnOnce(HeaderIdOf<C>, C::Index) -> Bytes + Send + 'static,
|
||||
prepare_extrinsic: impl FnOnce(HeaderIdOf<C>, C::Index) -> Result<Bytes> + Send + 'static,
|
||||
) -> Result<C::Hash> {
|
||||
let _guard = self.submit_signed_extrinsic_lock.lock().await;
|
||||
let transaction_nonce = self.next_account_index(extrinsic_signer).await?;
|
||||
let best_header = self.best_header().await?;
|
||||
let best_header_id = HeaderId(*best_header.number(), best_header.hash());
|
||||
|
||||
// By using parent of best block here, we are protecing again best-block reorganizations.
|
||||
// E.g. transaction my have been submitted when the best block was `A[num=100]`. Then it has
|
||||
// been changed to `B[num=100]`. Hash of `A` has been included into transaction signature
|
||||
// payload. So when signature will be checked, the check will fail and transaction will be
|
||||
// dropped from the pool.
|
||||
let best_header_id = match best_header.number().checked_sub(&One::one()) {
|
||||
Some(parent_block_number) => HeaderId(parent_block_number, *best_header.parent_hash()),
|
||||
None => HeaderId(*best_header.number(), best_header.hash()),
|
||||
};
|
||||
|
||||
self.jsonrpsee_execute(move |client| async move {
|
||||
let extrinsic = prepare_extrinsic(best_header_id, transaction_nonce);
|
||||
let tx_hash = Substrate::<C>::author_submit_extrinsic(&*client, extrinsic).await?;
|
||||
let extrinsic = prepare_extrinsic(best_header_id, transaction_nonce)?;
|
||||
let tx_hash = SubstrateClient::<
|
||||
AccountIdOf<C>,
|
||||
BlockNumberOf<C>,
|
||||
HashOf<C>,
|
||||
HeaderOf<C>,
|
||||
IndexOf<C>,
|
||||
C::SignedBlock,
|
||||
>::author_submit_extrinsic(&*client, extrinsic)
|
||||
.await?;
|
||||
log::trace!(target: "bridge", "Sent transaction to {} node: {:?}", C::NAME, tx_hash);
|
||||
Ok(tx_hash)
|
||||
})
|
||||
@@ -364,7 +504,7 @@ impl<C: Chain> Client<C> {
|
||||
pub async fn submit_and_watch_signed_extrinsic(
|
||||
&self,
|
||||
extrinsic_signer: C::AccountId,
|
||||
prepare_extrinsic: impl FnOnce(HeaderIdOf<C>, C::Index) -> Bytes + Send + 'static,
|
||||
prepare_extrinsic: impl FnOnce(HeaderIdOf<C>, C::Index) -> Result<Bytes> + Send + 'static,
|
||||
) -> Result<Subscription<TransactionStatusOf<C>>> {
|
||||
let _guard = self.submit_signed_extrinsic_lock.lock().await;
|
||||
let transaction_nonce = self.next_account_index(extrinsic_signer).await?;
|
||||
@@ -372,13 +512,13 @@ impl<C: Chain> Client<C> {
|
||||
let best_header_id = HeaderId(*best_header.number(), best_header.hash());
|
||||
let subscription = self
|
||||
.jsonrpsee_execute(move |client| async move {
|
||||
let extrinsic = prepare_extrinsic(best_header_id, transaction_nonce);
|
||||
let extrinsic = prepare_extrinsic(best_header_id, transaction_nonce)?;
|
||||
let tx_hash = C::Hasher::hash(&extrinsic.0);
|
||||
let subscription = client
|
||||
.subscribe(
|
||||
"author_submitAndWatchExtrinsic",
|
||||
JsonRpcParams::Array(vec![jsonrpsee_types::to_json_value(extrinsic)
|
||||
.map_err(|e| Error::RpcError(e.into()))?]),
|
||||
Some(ParamsSer::Array(vec![jsonrpsee::core::to_json_value(extrinsic)
|
||||
.map_err(|e| Error::RpcError(e.into()))?])),
|
||||
"author_unwatchExtrinsic",
|
||||
)
|
||||
.await?;
|
||||
@@ -399,7 +539,15 @@ impl<C: Chain> Client<C> {
|
||||
/// 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?)
|
||||
Ok(SubstrateClient::<
|
||||
AccountIdOf<C>,
|
||||
BlockNumberOf<C>,
|
||||
HashOf<C>,
|
||||
HeaderOf<C>,
|
||||
IndexOf<C>,
|
||||
C::SignedBlock,
|
||||
>::author_pending_extrinsics(&*client)
|
||||
.await?)
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -414,8 +562,15 @@ impl<C: Chain> Client<C> {
|
||||
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 encoded_response = SubstrateClient::<
|
||||
AccountIdOf<C>,
|
||||
BlockNumberOf<C>,
|
||||
HashOf<C>,
|
||||
HeaderOf<C>,
|
||||
IndexOf<C>,
|
||||
C::SignedBlock,
|
||||
>::state_call(&*client, call, data, Some(at_block))
|
||||
.await?;
|
||||
let validity = TransactionValidity::decode(&mut &encoded_response.0[..])
|
||||
.map_err(Error::ResponseParseFailed)?;
|
||||
|
||||
@@ -430,8 +585,15 @@ impl<C: Chain> Client<C> {
|
||||
transaction: Bytes,
|
||||
) -> Result<InclusionFee<C::Balance>> {
|
||||
self.jsonrpsee_execute(move |client| async move {
|
||||
let fee_details =
|
||||
Substrate::<C>::payment_query_fee_details(&*client, transaction, None).await?;
|
||||
let fee_details = SubstrateClient::<
|
||||
AccountIdOf<C>,
|
||||
BlockNumberOf<C>,
|
||||
HashOf<C>,
|
||||
HeaderOf<C>,
|
||||
IndexOf<C>,
|
||||
C::SignedBlock,
|
||||
>::payment_query_fee_details(&*client, transaction, None)
|
||||
.await?;
|
||||
let inclusion_fee = fee_details
|
||||
.inclusion_fee
|
||||
.map(|inclusion_fee| InclusionFee {
|
||||
@@ -463,8 +625,15 @@ impl<C: Chain> Client<C> {
|
||||
let call = SUB_API_GRANDPA_AUTHORITIES.to_string();
|
||||
let data = Bytes(Vec::new());
|
||||
|
||||
let encoded_response =
|
||||
Substrate::<C>::state_call(&*client, call, data, Some(block)).await?;
|
||||
let encoded_response = SubstrateClient::<
|
||||
AccountIdOf<C>,
|
||||
BlockNumberOf<C>,
|
||||
HashOf<C>,
|
||||
HeaderOf<C>,
|
||||
IndexOf<C>,
|
||||
C::SignedBlock,
|
||||
>::state_call(&*client, call, data, Some(block))
|
||||
.await?;
|
||||
let authority_list = encoded_response.0;
|
||||
|
||||
Ok(authority_list)
|
||||
@@ -480,9 +649,16 @@ impl<C: Chain> Client<C> {
|
||||
at_block: Option<C::Hash>,
|
||||
) -> Result<Bytes> {
|
||||
self.jsonrpsee_execute(move |client| async move {
|
||||
Substrate::<C>::state_call(&*client, method, data, at_block)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
SubstrateClient::<
|
||||
AccountIdOf<C>,
|
||||
BlockNumberOf<C>,
|
||||
HashOf<C>,
|
||||
HeaderOf<C>,
|
||||
IndexOf<C>,
|
||||
C::SignedBlock,
|
||||
>::state_call(&*client, method, data, at_block)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -494,10 +670,36 @@ impl<C: Chain> Client<C> {
|
||||
at_block: C::Hash,
|
||||
) -> Result<StorageProof> {
|
||||
self.jsonrpsee_execute(move |client| async move {
|
||||
Substrate::<C>::state_prove_storage(&*client, keys, Some(at_block))
|
||||
.await
|
||||
.map(|proof| StorageProof::new(proof.proof.into_iter().map(|b| b.0)))
|
||||
.map_err(Into::into)
|
||||
SubstrateClient::<
|
||||
AccountIdOf<C>,
|
||||
BlockNumberOf<C>,
|
||||
HashOf<C>,
|
||||
HeaderOf<C>,
|
||||
IndexOf<C>,
|
||||
C::SignedBlock,
|
||||
>::state_prove_storage(&*client, keys, Some(at_block))
|
||||
.await
|
||||
.map(|proof| {
|
||||
StorageProof::new(proof.proof.into_iter().map(|b| b.0).collect::<Vec<_>>())
|
||||
})
|
||||
.map_err(Into::into)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Return `tokenDecimals` property from the set of chain properties.
|
||||
pub async fn token_decimals(&self) -> Result<Option<u64>> {
|
||||
self.jsonrpsee_execute(move |client| async move {
|
||||
let system_properties = SubstrateClient::<
|
||||
AccountIdOf<C>,
|
||||
BlockNumberOf<C>,
|
||||
HashOf<C>,
|
||||
HeaderOf<C>,
|
||||
IndexOf<C>,
|
||||
C::SignedBlock,
|
||||
>::system_properties(&*client)
|
||||
.await?;
|
||||
Ok(system_properties.get("tokenDecimals").and_then(|v| v.as_u64()))
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -509,7 +711,7 @@ impl<C: Chain> Client<C> {
|
||||
Ok(client
|
||||
.subscribe(
|
||||
"grandpa_subscribeJustifications",
|
||||
JsonRpcParams::NoParams,
|
||||
None,
|
||||
"grandpa_unsubscribeJustifications",
|
||||
)
|
||||
.await?)
|
||||
@@ -549,26 +751,16 @@ impl<T: DeserializeOwned> Subscription<T> {
|
||||
async fn background_worker(
|
||||
chain_name: String,
|
||||
item_type: String,
|
||||
mut subscription: jsonrpsee_types::Subscription<T>,
|
||||
mut subscription: jsonrpsee::core::client::Subscription<T>,
|
||||
mut sender: futures::channel::mpsc::Sender<Option<T>>,
|
||||
) {
|
||||
loop {
|
||||
match subscription.next().await {
|
||||
Ok(Some(item)) =>
|
||||
Some(Ok(item)) =>
|
||||
if sender.send(Some(item)).await.is_err() {
|
||||
break
|
||||
},
|
||||
Ok(None) => {
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
"{} {} subscription stream has returned None. Stream needs to be restarted.",
|
||||
chain_name,
|
||||
item_type,
|
||||
);
|
||||
let _ = sender.send(None).await;
|
||||
break
|
||||
},
|
||||
Err(e) => {
|
||||
Some(Err(e)) => {
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
"{} {} subscription stream has returned '{:?}'. Stream needs to be restarted.",
|
||||
@@ -579,6 +771,16 @@ impl<T: DeserializeOwned> Subscription<T> {
|
||||
let _ = sender.send(None).await;
|
||||
break
|
||||
},
|
||||
None => {
|
||||
log::trace!(
|
||||
target: "bridge",
|
||||
"{} {} subscription stream has returned None. Stream needs to be restarted.",
|
||||
chain_name,
|
||||
item_type,
|
||||
);
|
||||
let _ = sender.send(None).await;
|
||||
break
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
//! Substrate node RPC errors.
|
||||
|
||||
use jsonrpsee_ws_client::types::Error as RpcError;
|
||||
use jsonrpsee::core::Error as RpcError;
|
||||
use relay_utils::MaybeConnectionError;
|
||||
use sc_rpc_api::system::Health;
|
||||
use sp_runtime::transaction_validity::TransactionValidityError;
|
||||
@@ -51,6 +51,9 @@ pub enum Error {
|
||||
/// The client we're connected to is not synced, so we can't rely on its state.
|
||||
#[error("Substrate client is not synced {0}.")]
|
||||
ClientNotSynced(Health),
|
||||
/// The bridge pallet is halted and all transactions will be rejected.
|
||||
#[error("Bridge pallet is halted.")]
|
||||
BridgePalletIsHalted,
|
||||
/// An error has happened when we have tried to parse storage proof.
|
||||
#[error("Error when parsing storage proof: {0:?}.")]
|
||||
StorageProofError(bp_runtime::StorageProofError),
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
// 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/>.
|
||||
|
||||
//! Default generic implementation of finality source for basic Substrate client.
|
||||
|
||||
use crate::{
|
||||
chain::{BlockWithJustification, Chain},
|
||||
client::Client,
|
||||
error::Error,
|
||||
sync_header::SyncHeader,
|
||||
};
|
||||
|
||||
use async_std::sync::{Arc, Mutex};
|
||||
use async_trait::async_trait;
|
||||
use bp_header_chain::justification::GrandpaJustification;
|
||||
use codec::Decode;
|
||||
use finality_relay::{FinalitySyncPipeline, SourceClient, SourceHeader};
|
||||
use futures::stream::{unfold, Stream, StreamExt};
|
||||
use relay_utils::relay_loop::Client as RelayClient;
|
||||
use sp_runtime::traits::Header as HeaderT;
|
||||
use std::{marker::PhantomData, pin::Pin};
|
||||
|
||||
/// Shared updatable reference to the maximal header number that we want to sync from the source.
|
||||
pub type RequiredHeaderNumberRef<C> = Arc<Mutex<<C as bp_runtime::Chain>::BlockNumber>>;
|
||||
|
||||
/// Substrate node as finality source.
|
||||
pub struct FinalitySource<C: Chain, P> {
|
||||
client: Client<C>,
|
||||
maximal_header_number: Option<RequiredHeaderNumberRef<C>>,
|
||||
_phantom: PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<C: Chain, P> FinalitySource<C, P> {
|
||||
/// Create new headers source using given client.
|
||||
pub fn new(
|
||||
client: Client<C>,
|
||||
maximal_header_number: Option<RequiredHeaderNumberRef<C>>,
|
||||
) -> Self {
|
||||
FinalitySource { client, maximal_header_number, _phantom: Default::default() }
|
||||
}
|
||||
|
||||
/// Returns reference to the underlying RPC client.
|
||||
pub fn client(&self) -> &Client<C> {
|
||||
&self.client
|
||||
}
|
||||
|
||||
/// Returns best finalized block number.
|
||||
pub async fn on_chain_best_finalized_block_number(&self) -> Result<C::BlockNumber, Error> {
|
||||
// we **CAN** continue to relay finality proofs if source node is out of sync, because
|
||||
// target node may be missing proofs that are already available at the source
|
||||
let finalized_header_hash = self.client.best_finalized_header_hash().await?;
|
||||
let finalized_header = self.client.header_by_hash(finalized_header_hash).await?;
|
||||
Ok(*finalized_header.number())
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Chain, P> Clone for FinalitySource<C, P> {
|
||||
fn clone(&self) -> Self {
|
||||
FinalitySource {
|
||||
client: self.client.clone(),
|
||||
maximal_header_number: self.maximal_header_number.clone(),
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<C: Chain, P: FinalitySyncPipeline> RelayClient for FinalitySource<C, P> {
|
||||
type Error = Error;
|
||||
|
||||
async fn reconnect(&mut self) -> Result<(), Error> {
|
||||
self.client.reconnect().await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<C, P> SourceClient<P> for FinalitySource<C, P>
|
||||
where
|
||||
C: Chain,
|
||||
C::BlockNumber: relay_utils::BlockNumberBase,
|
||||
P: FinalitySyncPipeline<
|
||||
Hash = C::Hash,
|
||||
Number = C::BlockNumber,
|
||||
Header = SyncHeader<C::Header>,
|
||||
FinalityProof = GrandpaJustification<C::Header>,
|
||||
>,
|
||||
P::Header: SourceHeader<C::BlockNumber>,
|
||||
{
|
||||
type FinalityProofsStream = Pin<Box<dyn Stream<Item = GrandpaJustification<C::Header>> + Send>>;
|
||||
|
||||
async fn best_finalized_block_number(&self) -> Result<P::Number, Error> {
|
||||
let mut finalized_header_number = self.on_chain_best_finalized_block_number().await?;
|
||||
// never return block number larger than requested. This way we'll never sync headers
|
||||
// past `maximal_header_number`
|
||||
if let Some(ref maximal_header_number) = self.maximal_header_number {
|
||||
let maximal_header_number = *maximal_header_number.lock().await;
|
||||
if finalized_header_number > maximal_header_number {
|
||||
finalized_header_number = maximal_header_number;
|
||||
}
|
||||
}
|
||||
Ok(finalized_header_number)
|
||||
}
|
||||
|
||||
async fn header_and_finality_proof(
|
||||
&self,
|
||||
number: P::Number,
|
||||
) -> Result<(P::Header, Option<P::FinalityProof>), Error> {
|
||||
let header_hash = self.client.block_hash_by_number(number).await?;
|
||||
let signed_block = self.client.get_block(Some(header_hash)).await?;
|
||||
|
||||
let justification = signed_block
|
||||
.justification()
|
||||
.map(|raw_justification| {
|
||||
GrandpaJustification::<C::Header>::decode(&mut raw_justification.as_slice())
|
||||
})
|
||||
.transpose()
|
||||
.map_err(Error::ResponseParseFailed)?;
|
||||
|
||||
Ok((signed_block.header().into(), justification))
|
||||
}
|
||||
|
||||
async fn finality_proofs(&self) -> Result<Self::FinalityProofsStream, Error> {
|
||||
Ok(unfold(
|
||||
self.client.clone().subscribe_justifications().await?,
|
||||
move |subscription| async move {
|
||||
loop {
|
||||
let log_error = |err| {
|
||||
log::error!(
|
||||
target: "bridge",
|
||||
"Failed to read justification target from the {} justifications stream: {:?}",
|
||||
P::SOURCE_NAME,
|
||||
err,
|
||||
);
|
||||
};
|
||||
|
||||
let next_justification = subscription
|
||||
.next()
|
||||
.await
|
||||
.map_err(|err| log_error(err.to_string()))
|
||||
.ok()??;
|
||||
|
||||
let decoded_justification =
|
||||
GrandpaJustification::<C::Header>::decode(&mut &next_justification[..]);
|
||||
|
||||
let justification = match decoded_justification {
|
||||
Ok(j) => j,
|
||||
Err(err) => {
|
||||
log_error(format!("decode failed with error {:?}", err));
|
||||
continue
|
||||
},
|
||||
};
|
||||
|
||||
return Some((justification, subscription))
|
||||
}
|
||||
},
|
||||
)
|
||||
.boxed())
|
||||
}
|
||||
}
|
||||
@@ -64,6 +64,13 @@ pub fn abort_on_spec_version_change<C: ChainWithBalances>(
|
||||
expected_spec_version: u32,
|
||||
) {
|
||||
async_std::task::spawn(async move {
|
||||
log::info!(
|
||||
target: "bridge-guard",
|
||||
"Starting spec_version guard for {}. Expected spec_version: {}",
|
||||
C::NAME,
|
||||
expected_spec_version,
|
||||
);
|
||||
|
||||
loop {
|
||||
let actual_spec_version = env.runtime_version().await;
|
||||
match actual_spec_version {
|
||||
@@ -103,6 +110,14 @@ pub fn abort_when_account_balance_decreased<C: ChainWithBalances>(
|
||||
const DAY: Duration = Duration::from_secs(60 * 60 * 24);
|
||||
|
||||
async_std::task::spawn(async move {
|
||||
log::info!(
|
||||
target: "bridge-guard",
|
||||
"Starting balance guard for {}/{:?}. Maximal decrease: {:?}",
|
||||
C::NAME,
|
||||
account_id,
|
||||
maximal_decrease,
|
||||
);
|
||||
|
||||
let mut balances = VecDeque::new();
|
||||
|
||||
loop {
|
||||
@@ -181,7 +196,7 @@ impl<C: ChainWithBalances> Environment<C> for Client<C> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use frame_support::weights::IdentityFee;
|
||||
use frame_support::weights::{IdentityFee, Weight};
|
||||
use futures::{
|
||||
channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender},
|
||||
future::FutureExt,
|
||||
@@ -202,10 +217,19 @@ mod tests {
|
||||
type Balance = u32;
|
||||
type Index = u32;
|
||||
type Signature = sp_runtime::testing::TestSignature;
|
||||
|
||||
fn max_extrinsic_size() -> u32 {
|
||||
unreachable!()
|
||||
}
|
||||
fn max_extrinsic_weight() -> Weight {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Chain for TestChain {
|
||||
const NAME: &'static str = "Test";
|
||||
const TOKEN_ID: Option<&'static str> = None;
|
||||
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = "BestTestHeader";
|
||||
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_millis(1);
|
||||
const STORAGE_PROOF_OVERHEAD: u32 = 0;
|
||||
const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = 0;
|
||||
|
||||
@@ -24,7 +24,6 @@ mod error;
|
||||
mod rpc;
|
||||
mod sync_header;
|
||||
|
||||
pub mod finality_source;
|
||||
pub mod guard;
|
||||
pub mod metrics;
|
||||
|
||||
@@ -32,10 +31,11 @@ use std::time::Duration;
|
||||
|
||||
pub use crate::{
|
||||
chain::{
|
||||
BlockWithJustification, CallOf, Chain, ChainWithBalances, TransactionSignScheme,
|
||||
TransactionStatusOf, UnsignedTransaction, WeightToFeeOf,
|
||||
AccountKeyPairOf, BlockWithJustification, CallOf, Chain, ChainWithBalances,
|
||||
ChainWithGrandpa, ChainWithMessages, SignParam, TransactionSignScheme, TransactionStatusOf,
|
||||
UnsignedTransaction, WeightToFeeOf,
|
||||
},
|
||||
client::{Client, OpaqueGrandpaAuthoritiesSet, Subscription},
|
||||
client::{ChainRuntimeVersion, Client, OpaqueGrandpaAuthoritiesSet, Subscription},
|
||||
error::{Error, Result},
|
||||
sync_header::SyncHeader,
|
||||
};
|
||||
@@ -56,11 +56,18 @@ pub struct ConnectionParams {
|
||||
pub port: u16,
|
||||
/// Use secure websocket connection.
|
||||
pub secure: bool,
|
||||
/// Defined chain runtime version
|
||||
pub chain_runtime_version: ChainRuntimeVersion,
|
||||
}
|
||||
|
||||
impl Default for ConnectionParams {
|
||||
fn default() -> Self {
|
||||
ConnectionParams { host: "localhost".into(), port: 9944, secure: false }
|
||||
ConnectionParams {
|
||||
host: "localhost".into(),
|
||||
port: 9944,
|
||||
secure: false,
|
||||
chain_runtime_version: ChainRuntimeVersion::Auto,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,48 +14,84 @@
|
||||
// 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::{chain::Chain, client::Client};
|
||||
use crate::{chain::Chain, client::Client, Error as SubstrateError};
|
||||
|
||||
use async_std::sync::{Arc, RwLock};
|
||||
use async_trait::async_trait;
|
||||
use codec::Decode;
|
||||
use num_traits::One;
|
||||
use relay_utils::metrics::{
|
||||
metric_name, register, F64SharedRef, Gauge, Metric, PrometheusError, Registry,
|
||||
StandaloneMetric, F64,
|
||||
};
|
||||
use sp_core::storage::StorageKey;
|
||||
use sp_runtime::{traits::UniqueSaturatedInto, FixedPointNumber};
|
||||
use std::time::Duration;
|
||||
use sp_core::storage::{StorageData, StorageKey};
|
||||
use sp_runtime::{traits::UniqueSaturatedInto, FixedPointNumber, FixedU128};
|
||||
use std::{marker::PhantomData, time::Duration};
|
||||
|
||||
/// Storage value update interval (in blocks).
|
||||
const UPDATE_INTERVAL_IN_BLOCKS: u32 = 5;
|
||||
|
||||
/// Metric that represents fixed-point runtime storage value as float gauge.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FloatStorageValueMetric<C: Chain, T: Clone> {
|
||||
client: Client<C>,
|
||||
storage_key: StorageKey,
|
||||
maybe_default_value: Option<T>,
|
||||
metric: Gauge<F64>,
|
||||
shared_value_ref: F64SharedRef,
|
||||
/// Fied-point storage value and the way it is decoded from the raw storage value.
|
||||
pub trait FloatStorageValue: 'static + Clone + Send + Sync {
|
||||
/// Type of the value.
|
||||
type Value: FixedPointNumber;
|
||||
/// Try to decode value from the raw storage value.
|
||||
fn decode(
|
||||
&self,
|
||||
maybe_raw_value: Option<StorageData>,
|
||||
) -> Result<Option<Self::Value>, SubstrateError>;
|
||||
}
|
||||
|
||||
impl<C: Chain, T: Decode + FixedPointNumber> FloatStorageValueMetric<C, T> {
|
||||
/// Implementation of `FloatStorageValue` that expects encoded `FixedU128` value and returns `1` if
|
||||
/// value is missing from the storage.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct FixedU128OrOne;
|
||||
|
||||
impl FloatStorageValue for FixedU128OrOne {
|
||||
type Value = FixedU128;
|
||||
|
||||
fn decode(
|
||||
&self,
|
||||
maybe_raw_value: Option<StorageData>,
|
||||
) -> Result<Option<Self::Value>, SubstrateError> {
|
||||
maybe_raw_value
|
||||
.map(|raw_value| {
|
||||
FixedU128::decode(&mut &raw_value.0[..])
|
||||
.map_err(SubstrateError::ResponseParseFailed)
|
||||
.map(Some)
|
||||
})
|
||||
.unwrap_or_else(|| Ok(Some(FixedU128::one())))
|
||||
}
|
||||
}
|
||||
|
||||
/// Metric that represents fixed-point runtime storage value as float gauge.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FloatStorageValueMetric<C: Chain, V: FloatStorageValue> {
|
||||
value_converter: V,
|
||||
client: Client<C>,
|
||||
storage_key: StorageKey,
|
||||
metric: Gauge<F64>,
|
||||
shared_value_ref: F64SharedRef,
|
||||
_phantom: PhantomData<V>,
|
||||
}
|
||||
|
||||
impl<C: Chain, V: FloatStorageValue> FloatStorageValueMetric<C, V> {
|
||||
/// Create new metric.
|
||||
pub fn new(
|
||||
value_converter: V,
|
||||
client: Client<C>,
|
||||
storage_key: StorageKey,
|
||||
maybe_default_value: Option<T>,
|
||||
name: String,
|
||||
help: String,
|
||||
) -> Result<Self, PrometheusError> {
|
||||
let shared_value_ref = Arc::new(RwLock::new(None));
|
||||
Ok(FloatStorageValueMetric {
|
||||
value_converter,
|
||||
client,
|
||||
storage_key,
|
||||
maybe_default_value,
|
||||
metric: Gauge::new(metric_name(None, &name), help)?,
|
||||
shared_value_ref,
|
||||
_phantom: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -65,20 +101,14 @@ impl<C: Chain, T: Decode + FixedPointNumber> FloatStorageValueMetric<C, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Chain, T> Metric for FloatStorageValueMetric<C, T>
|
||||
where
|
||||
T: 'static + Decode + Send + Sync + FixedPointNumber,
|
||||
{
|
||||
impl<C: Chain, V: FloatStorageValue> Metric for FloatStorageValueMetric<C, V> {
|
||||
fn register(&self, registry: &Registry) -> Result<(), PrometheusError> {
|
||||
register(self.metric.clone(), registry).map(drop)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<C: Chain, T> StandaloneMetric for FloatStorageValueMetric<C, T>
|
||||
where
|
||||
T: 'static + Decode + Send + Sync + FixedPointNumber,
|
||||
{
|
||||
impl<C: Chain, V: FloatStorageValue> StandaloneMetric for FloatStorageValueMetric<C, V> {
|
||||
fn update_interval(&self) -> Duration {
|
||||
C::AVERAGE_BLOCK_INTERVAL * UPDATE_INTERVAL_IN_BLOCKS
|
||||
}
|
||||
@@ -86,16 +116,18 @@ where
|
||||
async fn update(&self) {
|
||||
let value = self
|
||||
.client
|
||||
.storage_value::<T>(self.storage_key.clone(), None)
|
||||
.raw_storage_value(self.storage_key.clone(), None)
|
||||
.await
|
||||
.map(|maybe_storage_value| {
|
||||
maybe_storage_value.or(self.maybe_default_value).map(|storage_value| {
|
||||
storage_value.into_inner().unique_saturated_into() as f64 /
|
||||
T::DIV.unique_saturated_into() as f64
|
||||
.and_then(|maybe_storage_value| {
|
||||
self.value_converter.decode(maybe_storage_value).map(|maybe_fixed_point_value| {
|
||||
maybe_fixed_point_value.map(|fixed_point_value| {
|
||||
fixed_point_value.into_inner().unique_saturated_into() as f64 /
|
||||
V::Value::DIV.unique_saturated_into() as f64
|
||||
})
|
||||
})
|
||||
})
|
||||
.map_err(drop);
|
||||
relay_utils::metrics::set_gauge_value(&self.metric, value);
|
||||
.map_err(|e| e.to_string());
|
||||
relay_utils::metrics::set_gauge_value(&self.metric, value.clone());
|
||||
*self.shared_value_ref.write().await = value.ok().and_then(|x| x);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
//! Contains several Substrate-specific metrics that may be exposed by relay.
|
||||
|
||||
pub use float_storage_value::FloatStorageValueMetric;
|
||||
pub use float_storage_value::{FixedU128OrOne, FloatStorageValue, FloatStorageValueMetric};
|
||||
pub use storage_proof_overhead::StorageProofOverheadMetric;
|
||||
|
||||
mod float_storage_value;
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
|
||||
//! The most generic Substrate node RPC interface.
|
||||
|
||||
use crate::chain::Chain;
|
||||
|
||||
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
|
||||
use pallet_transaction_payment_rpc_runtime_api::FeeDetails;
|
||||
use sc_rpc_api::{state::ReadProof, system::Health};
|
||||
use sp_core::{
|
||||
@@ -27,33 +26,51 @@ use sp_core::{
|
||||
use sp_rpc::number::NumberOrHex;
|
||||
use sp_version::RuntimeVersion;
|
||||
|
||||
jsonrpsee_proc_macros::rpc_client_api! {
|
||||
pub(crate) Substrate<C: Chain> {
|
||||
#[rpc(method = "system_health", positional_params)]
|
||||
fn system_health() -> Health;
|
||||
#[rpc(method = "chain_getHeader", positional_params)]
|
||||
fn chain_get_header(block_hash: Option<C::Hash>) -> C::Header;
|
||||
#[rpc(method = "chain_getFinalizedHead", positional_params)]
|
||||
fn chain_get_finalized_head() -> C::Hash;
|
||||
#[rpc(method = "chain_getBlock", positional_params)]
|
||||
fn chain_get_block(block_hash: Option<C::Hash>) -> C::SignedBlock;
|
||||
#[rpc(method = "chain_getBlockHash", positional_params)]
|
||||
fn chain_get_block_hash(block_number: Option<C::BlockNumber>) -> C::Hash;
|
||||
#[rpc(method = "system_accountNextIndex", positional_params)]
|
||||
fn system_account_next_index(account_id: C::AccountId) -> C::Index;
|
||||
#[rpc(method = "author_submitExtrinsic", positional_params)]
|
||||
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)]
|
||||
fn state_call(method: String, data: Bytes, at_block: Option<C::Hash>) -> Bytes;
|
||||
#[rpc(method = "state_getStorage", positional_params)]
|
||||
fn state_get_storage(key: StorageKey, at_block: Option<C::Hash>) -> Option<StorageData>;
|
||||
#[rpc(method = "state_getReadProof", positional_params)]
|
||||
fn state_prove_storage(keys: Vec<StorageKey>, hash: Option<C::Hash>) -> ReadProof<C::Hash>;
|
||||
#[rpc(method = "state_getRuntimeVersion", positional_params)]
|
||||
fn state_runtime_version() -> RuntimeVersion;
|
||||
#[rpc(method = "payment_queryFeeDetails", positional_params)]
|
||||
fn payment_query_fee_details(extrinsic: Bytes, at_block: Option<C::Hash>) -> FeeDetails<NumberOrHex>;
|
||||
}
|
||||
#[rpc(client)]
|
||||
pub(crate) trait Substrate<AccountId, BlockNumber, Hash, Header, Index, SignedBlock> {
|
||||
#[method(name = "system_health", param_kind = array)]
|
||||
async fn system_health(&self) -> RpcResult<Health>;
|
||||
#[method(name = "system_properties", param_kind = array)]
|
||||
async fn system_properties(&self) -> RpcResult<sc_chain_spec::Properties>;
|
||||
#[method(name = "chain_getHeader", param_kind = array)]
|
||||
async fn chain_get_header(&self, block_hash: Option<Hash>) -> RpcResult<Header>;
|
||||
#[method(name = "chain_getFinalizedHead", param_kind = array)]
|
||||
async fn chain_get_finalized_head(&self) -> RpcResult<Hash>;
|
||||
#[method(name = "chain_getBlock", param_kind = array)]
|
||||
async fn chain_get_block(&self, block_hash: Option<Hash>) -> RpcResult<SignedBlock>;
|
||||
#[method(name = "chain_getBlockHash", param_kind = array)]
|
||||
async fn chain_get_block_hash(&self, block_number: Option<BlockNumber>) -> RpcResult<Hash>;
|
||||
#[method(name = "system_accountNextIndex", param_kind = array)]
|
||||
async fn system_account_next_index(&self, account_id: AccountId) -> RpcResult<Index>;
|
||||
#[method(name = "author_submitExtrinsic", param_kind = array)]
|
||||
async fn author_submit_extrinsic(&self, extrinsic: Bytes) -> RpcResult<Hash>;
|
||||
#[method(name = "author_pendingExtrinsics", param_kind = array)]
|
||||
async fn author_pending_extrinsics(&self) -> RpcResult<Vec<Bytes>>;
|
||||
#[method(name = "state_call", param_kind = array)]
|
||||
async fn state_call(
|
||||
&self,
|
||||
method: String,
|
||||
data: Bytes,
|
||||
at_block: Option<Hash>,
|
||||
) -> RpcResult<Bytes>;
|
||||
#[method(name = "state_getStorage", param_kind = array)]
|
||||
async fn state_get_storage(
|
||||
&self,
|
||||
key: StorageKey,
|
||||
at_block: Option<Hash>,
|
||||
) -> RpcResult<Option<StorageData>>;
|
||||
#[method(name = "state_getReadProof", param_kind = array)]
|
||||
async fn state_prove_storage(
|
||||
&self,
|
||||
keys: Vec<StorageKey>,
|
||||
hash: Option<Hash>,
|
||||
) -> RpcResult<ReadProof<Hash>>;
|
||||
#[method(name = "state_getRuntimeVersion", param_kind = array)]
|
||||
async fn state_runtime_version(&self) -> RpcResult<RuntimeVersion>;
|
||||
#[method(name = "payment_queryFeeDetails", param_kind = array)]
|
||||
async fn payment_query_fee_details(
|
||||
&self,
|
||||
extrinsic: Bytes,
|
||||
at_block: Option<Hash>,
|
||||
) -> RpcResult<FeeDetails<NumberOrHex>>;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,11 @@ impl<Header> From<Header> for SyncHeader<Header> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<Header: HeaderT> FinalitySourceHeader<Header::Number> for SyncHeader<Header> {
|
||||
impl<Header: HeaderT> FinalitySourceHeader<Header::Hash, Header::Number> for SyncHeader<Header> {
|
||||
fn hash(&self) -> Header::Hash {
|
||||
self.0.hash()
|
||||
}
|
||||
|
||||
fn number(&self) -> Header::Number {
|
||||
*self.0.number()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user