mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-17 11:21:07 +00:00
Polkadot <> Kusama relayers (#1122)
* relay headers between Kusama and Polkadot * relay messages between Kusama and Polkadot * complex Kusama <> Polkadot relayer * expose relayer_fund_account_id from messages pallet * create relayers fund accounts on Kusama/Polkadot + some more fixes * fmt * fix compilation * compilation + clippy * compilation * MAXIMAL_BALANCE_DECREASE_PER_DAY for K<>P header relays * fmt * deduplicate tests * Update modules/messages/src/lib.rs Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * extract storage_parameter_key function * other grumbles * fix * fmt Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>
This commit is contained in:
committed by
Bastian Köcher
parent
417903f9e7
commit
2db84b74cc
@@ -135,10 +135,10 @@ impl Alternative {
|
|||||||
get_account_id_from_seed::<sr25519::Public>("George//stash"),
|
get_account_id_from_seed::<sr25519::Public>("George//stash"),
|
||||||
get_account_id_from_seed::<sr25519::Public>("Harry//stash"),
|
get_account_id_from_seed::<sr25519::Public>("Harry//stash"),
|
||||||
get_account_id_from_seed::<sr25519::Public>("RialtoMessagesOwner"),
|
get_account_id_from_seed::<sr25519::Public>("RialtoMessagesOwner"),
|
||||||
pallet_bridge_messages::Pallet::<
|
pallet_bridge_messages::relayer_fund_account_id::<
|
||||||
millau_runtime::Runtime,
|
bp_millau::AccountId,
|
||||||
millau_runtime::WithRialtoMessagesInstance,
|
bp_millau::AccountIdConverter,
|
||||||
>::relayer_fund_account_id(),
|
>(),
|
||||||
derive_account_from_rialto_id(bp_runtime::SourceAccount::Account(
|
derive_account_from_rialto_id(bp_runtime::SourceAccount::Account(
|
||||||
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
||||||
)),
|
)),
|
||||||
|
|||||||
@@ -151,10 +151,10 @@ impl Alternative {
|
|||||||
get_account_id_from_seed::<sr25519::Public>("George//stash"),
|
get_account_id_from_seed::<sr25519::Public>("George//stash"),
|
||||||
get_account_id_from_seed::<sr25519::Public>("Harry//stash"),
|
get_account_id_from_seed::<sr25519::Public>("Harry//stash"),
|
||||||
get_account_id_from_seed::<sr25519::Public>("MillauMessagesOwner"),
|
get_account_id_from_seed::<sr25519::Public>("MillauMessagesOwner"),
|
||||||
pallet_bridge_messages::Pallet::<
|
pallet_bridge_messages::relayer_fund_account_id::<
|
||||||
rialto_runtime::Runtime,
|
bp_rialto::AccountId,
|
||||||
rialto_runtime::WithMillauMessagesInstance,
|
bp_rialto::AccountIdConverter,
|
||||||
>::relayer_fund_account_id(),
|
>(),
|
||||||
derive_account_from_millau_id(bp_runtime::SourceAccount::Account(
|
derive_account_from_millau_id(bp_runtime::SourceAccount::Account(
|
||||||
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
||||||
)),
|
)),
|
||||||
|
|||||||
@@ -484,7 +484,7 @@ benchmarks_instance_pallet! {
|
|||||||
//
|
//
|
||||||
// This is base benchmark for all other confirmations delivery benchmarks.
|
// This is base benchmark for all other confirmations delivery benchmarks.
|
||||||
receive_delivery_proof_for_single_message {
|
receive_delivery_proof_for_single_message {
|
||||||
let relayers_fund_id = crate::Pallet::<T, I>::relayer_fund_account_id();
|
let relayers_fund_id = crate::relayer_fund_account_id::<T::AccountId, T::AccountIdConverter>();
|
||||||
let relayer_id: T::AccountId = account("relayer", 0, SEED);
|
let relayer_id: T::AccountId = account("relayer", 0, SEED);
|
||||||
let relayer_balance = T::account_balance(&relayer_id);
|
let relayer_balance = T::account_balance(&relayer_id);
|
||||||
T::endow_account(&relayers_fund_id);
|
T::endow_account(&relayers_fund_id);
|
||||||
@@ -524,7 +524,7 @@ benchmarks_instance_pallet! {
|
|||||||
// as `weight(receive_delivery_proof_for_two_messages_by_single_relayer)
|
// as `weight(receive_delivery_proof_for_two_messages_by_single_relayer)
|
||||||
// - weight(receive_delivery_proof_for_single_message)`.
|
// - weight(receive_delivery_proof_for_single_message)`.
|
||||||
receive_delivery_proof_for_two_messages_by_single_relayer {
|
receive_delivery_proof_for_two_messages_by_single_relayer {
|
||||||
let relayers_fund_id = crate::Pallet::<T, I>::relayer_fund_account_id();
|
let relayers_fund_id = crate::relayer_fund_account_id::<T::AccountId, T::AccountIdConverter>();
|
||||||
let relayer_id: T::AccountId = account("relayer", 0, SEED);
|
let relayer_id: T::AccountId = account("relayer", 0, SEED);
|
||||||
let relayer_balance = T::account_balance(&relayer_id);
|
let relayer_balance = T::account_balance(&relayer_id);
|
||||||
T::endow_account(&relayers_fund_id);
|
T::endow_account(&relayers_fund_id);
|
||||||
@@ -564,7 +564,7 @@ benchmarks_instance_pallet! {
|
|||||||
// as `weight(receive_delivery_proof_for_two_messages_by_two_relayers)
|
// as `weight(receive_delivery_proof_for_two_messages_by_two_relayers)
|
||||||
// - weight(receive_delivery_proof_for_two_messages_by_single_relayer)`.
|
// - weight(receive_delivery_proof_for_two_messages_by_single_relayer)`.
|
||||||
receive_delivery_proof_for_two_messages_by_two_relayers {
|
receive_delivery_proof_for_two_messages_by_two_relayers {
|
||||||
let relayers_fund_id = crate::Pallet::<T, I>::relayer_fund_account_id();
|
let relayers_fund_id = crate::relayer_fund_account_id::<T::AccountId, T::AccountIdConverter>();
|
||||||
let relayer1_id: T::AccountId = account("relayer1", 1, SEED);
|
let relayer1_id: T::AccountId = account("relayer1", 1, SEED);
|
||||||
let relayer1_balance = T::account_balance(&relayer1_id);
|
let relayer1_balance = T::account_balance(&relayer1_id);
|
||||||
let relayer2_id: T::AccountId = account("relayer2", 2, SEED);
|
let relayer2_id: T::AccountId = account("relayer2", 2, SEED);
|
||||||
@@ -811,7 +811,7 @@ benchmarks_instance_pallet! {
|
|||||||
.try_into()
|
.try_into()
|
||||||
.expect("Value of MaxUnrewardedRelayerEntriesAtInboundLane is too large");
|
.expect("Value of MaxUnrewardedRelayerEntriesAtInboundLane is too large");
|
||||||
|
|
||||||
let relayers_fund_id = crate::Pallet::<T, I>::relayer_fund_account_id();
|
let relayers_fund_id = crate::relayer_fund_account_id::<T::AccountId, T::AccountIdConverter>();
|
||||||
let relayer_id: T::AccountId = account("relayer", 0, SEED);
|
let relayer_id: T::AccountId = account("relayer", 0, SEED);
|
||||||
let relayer_balance = T::account_balance(&relayer_id);
|
let relayer_balance = T::account_balance(&relayer_id);
|
||||||
T::endow_account(&relayers_fund_id);
|
T::endow_account(&relayers_fund_id);
|
||||||
@@ -854,7 +854,7 @@ benchmarks_instance_pallet! {
|
|||||||
.try_into()
|
.try_into()
|
||||||
.expect("Value of MaxUnconfirmedMessagesAtInboundLane is too large ");
|
.expect("Value of MaxUnconfirmedMessagesAtInboundLane is too large ");
|
||||||
|
|
||||||
let relayers_fund_id = crate::Pallet::<T, I>::relayer_fund_account_id();
|
let relayers_fund_id = crate::relayer_fund_account_id::<T::AccountId, T::AccountIdConverter>();
|
||||||
let confirmation_relayer_id = account("relayer", 0, SEED);
|
let confirmation_relayer_id = account("relayer", 0, SEED);
|
||||||
let relayers: BTreeMap<T::AccountId, T::OutboundMessageFee> = (1..=i)
|
let relayers: BTreeMap<T::AccountId, T::OutboundMessageFee> = (1..=i)
|
||||||
.map(|j| {
|
.map(|j| {
|
||||||
|
|||||||
@@ -64,7 +64,8 @@ use frame_support::{
|
|||||||
};
|
};
|
||||||
use frame_system::RawOrigin;
|
use frame_system::RawOrigin;
|
||||||
use num_traits::{SaturatingAdd, Zero};
|
use num_traits::{SaturatingAdd, Zero};
|
||||||
use sp_runtime::traits::BadOrigin;
|
use sp_core::H256;
|
||||||
|
use sp_runtime::traits::{BadOrigin, Convert};
|
||||||
use sp_std::{cell::RefCell, cmp::PartialOrd, marker::PhantomData, prelude::*};
|
use sp_std::{cell::RefCell, cmp::PartialOrd, marker::PhantomData, prelude::*};
|
||||||
|
|
||||||
mod inbound_lane;
|
mod inbound_lane;
|
||||||
@@ -286,16 +287,17 @@ pub mod pallet {
|
|||||||
T::MessageDeliveryAndDispatchPayment::pay_delivery_and_dispatch_fee(
|
T::MessageDeliveryAndDispatchPayment::pay_delivery_and_dispatch_fee(
|
||||||
&submitter,
|
&submitter,
|
||||||
&additional_fee,
|
&additional_fee,
|
||||||
&Self::relayer_fund_account_id(),
|
&relayer_fund_account_id::<T::AccountId, T::AccountIdConverter>(),
|
||||||
)
|
)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
log::trace!(
|
log::trace!(
|
||||||
target: "runtime::bridge-messages",
|
target: "runtime::bridge-messages",
|
||||||
"Submitter {:?} can't pay additional fee {:?} for the message {:?}/{:?}: {:?}",
|
"Submitter {:?} can't pay additional fee {:?} for the message {:?}/{:?} to {:?}: {:?}",
|
||||||
submitter,
|
submitter,
|
||||||
additional_fee,
|
additional_fee,
|
||||||
lane_id,
|
lane_id,
|
||||||
nonce,
|
nonce,
|
||||||
|
relayer_fund_account_id::<T::AccountId, T::AccountIdConverter>(),
|
||||||
err,
|
err,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -604,7 +606,7 @@ pub mod pallet {
|
|||||||
|
|
||||||
// if some new messages have been confirmed, reward relayers
|
// if some new messages have been confirmed, reward relayers
|
||||||
if !relayers_rewards.is_empty() {
|
if !relayers_rewards.is_empty() {
|
||||||
let relayer_fund_account = Self::relayer_fund_account_id();
|
let relayer_fund_account = relayer_fund_account_id::<T::AccountId, T::AccountIdConverter>();
|
||||||
<T as Config<I>>::MessageDeliveryAndDispatchPayment::pay_relayers_rewards(
|
<T as Config<I>>::MessageDeliveryAndDispatchPayment::pay_relayers_rewards(
|
||||||
&confirmation_relayer,
|
&confirmation_relayer,
|
||||||
relayers_rewards,
|
relayers_rewards,
|
||||||
@@ -768,17 +770,6 @@ pub mod pallet {
|
|||||||
total_messages: total_unrewarded_messages(&relayers).unwrap_or(MessageNonce::MAX),
|
total_messages: total_unrewarded_messages(&relayers).unwrap_or(MessageNonce::MAX),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// AccountId of the shared relayer fund account.
|
|
||||||
///
|
|
||||||
/// This account is passed to `MessageDeliveryAndDispatchPayment` trait, and depending
|
|
||||||
/// on the implementation it can be used to store relayers rewards.
|
|
||||||
/// See [InstantCurrencyPayments] for a concrete implementation.
|
|
||||||
pub fn relayer_fund_account_id() -> T::AccountId {
|
|
||||||
use sp_runtime::traits::Convert;
|
|
||||||
let encoded_id = bp_runtime::derive_relayer_fund_account_id(bp_runtime::NO_INSTANCE_ID);
|
|
||||||
T::AccountIdConverter::convert(encoded_id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -825,6 +816,16 @@ pub mod storage_keys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// AccountId of the shared relayer fund account.
|
||||||
|
///
|
||||||
|
/// This account is passed to `MessageDeliveryAndDispatchPayment` trait, and depending
|
||||||
|
/// on the implementation it can be used to store relayers rewards.
|
||||||
|
/// See [`InstantCurrencyPayments`] for a concrete implementation.
|
||||||
|
pub fn relayer_fund_account_id<AccountId, AccountIdConverter: Convert<H256, AccountId>>() -> AccountId {
|
||||||
|
let encoded_id = bp_runtime::derive_relayer_fund_account_id(bp_runtime::NO_INSTANCE_ID);
|
||||||
|
AccountIdConverter::convert(encoded_id)
|
||||||
|
}
|
||||||
|
|
||||||
impl<T, I> bp_messages::source_chain::MessagesBridge<T::AccountId, T::OutboundMessageFee, T::OutboundPayload>
|
impl<T, I> bp_messages::source_chain::MessagesBridge<T::AccountId, T::OutboundMessageFee, T::OutboundPayload>
|
||||||
for Pallet<T, I>
|
for Pallet<T, I>
|
||||||
where
|
where
|
||||||
@@ -894,7 +895,7 @@ fn send_message<T: Config<I>, I: 'static>(
|
|||||||
T::MessageDeliveryAndDispatchPayment::pay_delivery_and_dispatch_fee(
|
T::MessageDeliveryAndDispatchPayment::pay_delivery_and_dispatch_fee(
|
||||||
&submitter,
|
&submitter,
|
||||||
&delivery_and_dispatch_fee,
|
&delivery_and_dispatch_fee,
|
||||||
&Pallet::<T, I>::relayer_fund_account_id(),
|
&relayer_fund_account_id::<T::AccountId, T::AccountIdConverter>(),
|
||||||
)
|
)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
log::trace!(
|
log::trace!(
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ bp-runtime = { path = "../runtime", default-features = false }
|
|||||||
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||||
sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||||
sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||||
|
sp-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
@@ -30,4 +31,5 @@ std = [
|
|||||||
"frame-support/std",
|
"frame-support/std",
|
||||||
"sp-api/std",
|
"sp-api/std",
|
||||||
"sp-std/std",
|
"sp-std/std",
|
||||||
|
"sp-version/std",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -23,12 +23,24 @@
|
|||||||
use bp_messages::{LaneId, MessageDetails, MessageNonce, UnrewardedRelayersState};
|
use bp_messages::{LaneId, MessageDetails, MessageNonce, UnrewardedRelayersState};
|
||||||
use frame_support::weights::{WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial};
|
use frame_support::weights::{WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial};
|
||||||
use sp_std::prelude::*;
|
use sp_std::prelude::*;
|
||||||
|
use sp_version::RuntimeVersion;
|
||||||
|
|
||||||
pub use bp_polkadot_core::*;
|
pub use bp_polkadot_core::*;
|
||||||
|
|
||||||
/// Kusama Chain
|
/// Kusama Chain
|
||||||
pub type Kusama = PolkadotLike;
|
pub type Kusama = PolkadotLike;
|
||||||
|
|
||||||
|
// NOTE: This needs to be kept up to date with the Kusama runtime found in the Polkadot repo.
|
||||||
|
pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||||
|
spec_name: sp_version::create_runtime_str!("kusama"),
|
||||||
|
impl_name: sp_version::create_runtime_str!("parity-kusama"),
|
||||||
|
authoring_version: 2,
|
||||||
|
spec_version: 9100,
|
||||||
|
impl_version: 0,
|
||||||
|
apis: sp_version::create_apis_vec![[]],
|
||||||
|
transaction_version: 5,
|
||||||
|
};
|
||||||
|
|
||||||
// NOTE: This needs to be kept up to date with the Kusama runtime found in the Polkadot repo.
|
// NOTE: This needs to be kept up to date with the Kusama runtime found in the Polkadot repo.
|
||||||
pub struct WeightToFee;
|
pub struct WeightToFee;
|
||||||
impl WeightToFeePolynomial for WeightToFee {
|
impl WeightToFeePolynomial for WeightToFee {
|
||||||
@@ -57,9 +69,22 @@ pub fn derive_account_from_polkadot_id(id: bp_runtime::SourceAccount<AccountId>)
|
|||||||
/// Per-byte fee for Kusama transactions.
|
/// Per-byte fee for Kusama transactions.
|
||||||
pub const TRANSACTION_BYTE_FEE: Balance = 10 * 1_000_000_000_000 / 30_000 / 1_000;
|
pub const TRANSACTION_BYTE_FEE: Balance = 10 * 1_000_000_000_000 / 30_000 / 1_000;
|
||||||
|
|
||||||
|
/// Existential deposit on Kusama.
|
||||||
|
pub const EXISTENTIAL_DEPOSIT: Balance = 1_000_000_000_000 / 30_000;
|
||||||
|
|
||||||
|
/// The target length of a session (how often authorities change) on Kusama measured in of number of
|
||||||
|
/// blocks.
|
||||||
|
///
|
||||||
|
/// Note that since this is a target sessions may change before/after this time depending on network
|
||||||
|
/// conditions.
|
||||||
|
pub const SESSION_LENGTH: BlockNumber = 1 * time_units::HOURS;
|
||||||
|
|
||||||
/// Name of the With-Polkadot messages pallet instance in the Kusama runtime.
|
/// Name of the With-Polkadot messages pallet instance in the Kusama runtime.
|
||||||
pub const WITH_POLKADOT_MESSAGES_PALLET_NAME: &str = "BridgePolkadotMessages";
|
pub const WITH_POLKADOT_MESSAGES_PALLET_NAME: &str = "BridgePolkadotMessages";
|
||||||
|
|
||||||
|
/// Name of the DOT->KSM conversion rate stored in the Kusama runtime.
|
||||||
|
pub const POLKADOT_TO_KUSAMA_CONVERSION_RATE_PARAMETER_NAME: &str = "PolkadotToKusamaConversionRate";
|
||||||
|
|
||||||
/// Name of the `KusamaFinalityApi::best_finalized` runtime method.
|
/// Name of the `KusamaFinalityApi::best_finalized` runtime method.
|
||||||
pub const BEST_FINALIZED_KUSAMA_HEADER_METHOD: &str = "KusamaFinalityApi_best_finalized";
|
pub const BEST_FINALIZED_KUSAMA_HEADER_METHOD: &str = "KusamaFinalityApi_best_finalized";
|
||||||
/// Name of the `KusamaFinalityApi::is_known_header` runtime method.
|
/// Name of the `KusamaFinalityApi::is_known_header` runtime method.
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ bp-runtime = { path = "../runtime", default-features = false }
|
|||||||
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||||
sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||||
sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||||
|
sp-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
@@ -30,4 +31,5 @@ std = [
|
|||||||
"frame-support/std",
|
"frame-support/std",
|
||||||
"sp-api/std",
|
"sp-api/std",
|
||||||
"sp-std/std",
|
"sp-std/std",
|
||||||
|
"sp-version/std",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -23,12 +23,24 @@
|
|||||||
use bp_messages::{LaneId, MessageDetails, MessageNonce, UnrewardedRelayersState};
|
use bp_messages::{LaneId, MessageDetails, MessageNonce, UnrewardedRelayersState};
|
||||||
use frame_support::weights::{WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial};
|
use frame_support::weights::{WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial};
|
||||||
use sp_std::prelude::*;
|
use sp_std::prelude::*;
|
||||||
|
use sp_version::RuntimeVersion;
|
||||||
|
|
||||||
pub use bp_polkadot_core::*;
|
pub use bp_polkadot_core::*;
|
||||||
|
|
||||||
/// Polkadot Chain
|
/// Polkadot Chain
|
||||||
pub type Polkadot = PolkadotLike;
|
pub type Polkadot = PolkadotLike;
|
||||||
|
|
||||||
|
// NOTE: This needs to be kept up to date with the Polkadot runtime found in the Polkadot repo.
|
||||||
|
pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||||
|
spec_name: sp_version::create_runtime_str!("polkadot"),
|
||||||
|
impl_name: sp_version::create_runtime_str!("parity-polkadot"),
|
||||||
|
authoring_version: 0,
|
||||||
|
spec_version: 9100,
|
||||||
|
impl_version: 0,
|
||||||
|
apis: sp_version::create_apis_vec![[]],
|
||||||
|
transaction_version: 7,
|
||||||
|
};
|
||||||
|
|
||||||
// NOTE: This needs to be kept up to date with the Polkadot runtime found in the Polkadot repo.
|
// NOTE: This needs to be kept up to date with the Polkadot runtime found in the Polkadot repo.
|
||||||
pub struct WeightToFee;
|
pub struct WeightToFee;
|
||||||
impl WeightToFeePolynomial for WeightToFee {
|
impl WeightToFeePolynomial for WeightToFee {
|
||||||
@@ -57,9 +69,22 @@ pub fn derive_account_from_kusama_id(id: bp_runtime::SourceAccount<AccountId>) -
|
|||||||
/// Per-byte fee for Polkadot transactions.
|
/// Per-byte fee for Polkadot transactions.
|
||||||
pub const TRANSACTION_BYTE_FEE: Balance = 10 * 10_000_000_000 / 100 / 1_000;
|
pub const TRANSACTION_BYTE_FEE: Balance = 10 * 10_000_000_000 / 100 / 1_000;
|
||||||
|
|
||||||
|
/// Existential deposit on Polkadot.
|
||||||
|
pub const EXISTENTIAL_DEPOSIT: Balance = 10_000_000_000;
|
||||||
|
|
||||||
|
/// The target length of a session (how often authorities change) on Polkadot measured in of number of
|
||||||
|
/// blocks.
|
||||||
|
///
|
||||||
|
/// Note that since this is a target sessions may change before/after this time depending on network
|
||||||
|
/// conditions.
|
||||||
|
pub const SESSION_LENGTH: BlockNumber = 4 * time_units::HOURS;
|
||||||
|
|
||||||
/// Name of the With-Kusama messages pallet instance in the Polkadot runtime.
|
/// Name of the With-Kusama messages pallet instance in the Polkadot runtime.
|
||||||
pub const WITH_KUSAMA_MESSAGES_PALLET_NAME: &str = "BridgeKusamaMessages";
|
pub const WITH_KUSAMA_MESSAGES_PALLET_NAME: &str = "BridgeKusamaMessages";
|
||||||
|
|
||||||
|
/// Name of the KSM->DOT conversion rate stored in the Polkadot runtime.
|
||||||
|
pub const KUSAMA_TO_POLKADOT_CONVERSION_RATE_PARAMETER_NAME: &str = "KusamaToPolkadotConversionRate";
|
||||||
|
|
||||||
/// Name of the `PolkadotFinalityApi::best_finalized` runtime method.
|
/// Name of the `PolkadotFinalityApi::best_finalized` runtime method.
|
||||||
pub const BEST_FINALIZED_POLKADOT_HEADER_METHOD: &str = "PolkadotFinalityApi_best_finalized";
|
pub const BEST_FINALIZED_POLKADOT_HEADER_METHOD: &str = "PolkadotFinalityApi_best_finalized";
|
||||||
/// Name of the `PolkadotFinalityApi::is_known_header` runtime method.
|
/// Name of the `PolkadotFinalityApi::is_known_header` runtime method.
|
||||||
|
|||||||
@@ -217,6 +217,9 @@ pub type AccountPublic = <Signature as Verify>::Signer;
|
|||||||
/// Id of account on Polkadot-like chains.
|
/// Id of account on Polkadot-like chains.
|
||||||
pub type AccountId = <AccountPublic as IdentifyAccount>::AccountId;
|
pub type AccountId = <AccountPublic as IdentifyAccount>::AccountId;
|
||||||
|
|
||||||
|
/// Address of account on Polkadot-like chains.
|
||||||
|
pub type AccountAddress = MultiAddress<AccountId, ()>;
|
||||||
|
|
||||||
/// Index of a transaction on the Polkadot-like chains.
|
/// Index of a transaction on the Polkadot-like chains.
|
||||||
pub type Nonce = u32;
|
pub type Nonce = u32;
|
||||||
|
|
||||||
@@ -231,7 +234,7 @@ pub type Balance = u128;
|
|||||||
|
|
||||||
/// Unchecked Extrinsic type.
|
/// Unchecked Extrinsic type.
|
||||||
pub type UncheckedExtrinsic<Call> =
|
pub type UncheckedExtrinsic<Call> =
|
||||||
generic::UncheckedExtrinsic<MultiAddress<AccountId, ()>, Call, Signature, SignedExtensions<Call>>;
|
generic::UncheckedExtrinsic<AccountAddress, Call, Signature, SignedExtensions<Call>>;
|
||||||
|
|
||||||
/// Account address, used by the Polkadot-like chain.
|
/// Account address, used by the Polkadot-like chain.
|
||||||
pub type Address = MultiAddress<AccountId, ()>;
|
pub type Address = MultiAddress<AccountId, ()>;
|
||||||
|
|||||||
@@ -20,9 +20,9 @@
|
|||||||
|
|
||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
use frame_support::RuntimeDebug;
|
use frame_support::RuntimeDebug;
|
||||||
use sp_core::hash::H256;
|
use sp_core::{hash::H256, storage::StorageKey};
|
||||||
use sp_io::hashing::blake2_256;
|
use sp_io::hashing::blake2_256;
|
||||||
use sp_std::convert::TryFrom;
|
use sp_std::{convert::TryFrom, vec::Vec};
|
||||||
|
|
||||||
pub use chain::{
|
pub use chain::{
|
||||||
AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain, HashOf, HasherOf, HeaderOf, IndexOf, SignatureOf,
|
AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain, HashOf, HasherOf, HeaderOf, IndexOf, SignatureOf,
|
||||||
@@ -183,3 +183,15 @@ impl<BlockNumber: Copy + Into<u64>, BlockHash: Copy> TransactionEra<BlockNumber,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This is how a storage key of storage parameter (`parameter_types! { storage Param: bool = false; }`) is computed.
|
||||||
|
///
|
||||||
|
/// Copypaste from `frame_support::parameter_types` macro
|
||||||
|
pub fn storage_parameter_key(parameter_name: &str) -> StorageKey {
|
||||||
|
let mut buffer = Vec::with_capacity(1 + parameter_name.len() + 1 + 1);
|
||||||
|
buffer.push(':' as u8);
|
||||||
|
buffer.extend_from_slice(parameter_name.as_bytes());
|
||||||
|
buffer.push(':' as u8);
|
||||||
|
buffer.push(0);
|
||||||
|
StorageKey(sp_io::hashing::twox_128(&buffer).to_vec())
|
||||||
|
}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ substrate-relay-helper = { path = "../lib-substrate-relay" }
|
|||||||
|
|
||||||
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||||
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||||
|
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||||
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||||
sp-version = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
sp-version = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
// 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 codec::Decode;
|
||||||
|
use frame_support::weights::{DispatchClass, DispatchInfo, Pays, Weight};
|
||||||
|
use relay_kusama_client::Kusama;
|
||||||
|
use sp_version::RuntimeVersion;
|
||||||
|
|
||||||
|
use crate::cli::{
|
||||||
|
bridge,
|
||||||
|
encode_call::{Call, CliEncodeCall},
|
||||||
|
encode_message, CliChain,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Weight of the `system::remark` call at Kusama.
|
||||||
|
///
|
||||||
|
/// This weight is larger (x2) than actual weight at current Kusama runtime to avoid unsuccessful
|
||||||
|
/// calls in the future. But since it is used only in tests (and on test chains), this is ok.
|
||||||
|
pub(crate) const SYSTEM_REMARK_CALL_WEIGHT: Weight = 2 * 1_345_000;
|
||||||
|
|
||||||
|
/// Id of Kusama token that is used to fetch token price.
|
||||||
|
pub(crate) const TOKEN_ID: &str = "kusama";
|
||||||
|
|
||||||
|
impl CliEncodeCall for Kusama {
|
||||||
|
fn max_extrinsic_size() -> u32 {
|
||||||
|
bp_kusama::max_extrinsic_size()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_call(call: &Call) -> anyhow::Result<Self::Call> {
|
||||||
|
Ok(match call {
|
||||||
|
Call::Remark { remark_payload, .. } => {
|
||||||
|
relay_kusama_client::runtime::Call::System(relay_kusama_client::runtime::SystemCall::remark(
|
||||||
|
remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Call::BridgeSendMessage {
|
||||||
|
lane,
|
||||||
|
payload,
|
||||||
|
fee,
|
||||||
|
bridge_instance_index,
|
||||||
|
} => match *bridge_instance_index {
|
||||||
|
bridge::KUSAMA_TO_POLKADOT_INDEX => {
|
||||||
|
let payload = Decode::decode(&mut &*payload.0)?;
|
||||||
|
relay_kusama_client::runtime::Call::BridgePolkadotMessages(
|
||||||
|
relay_kusama_client::runtime::BridgePolkadotMessagesCall::send_message(lane.0, payload, fee.0),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => anyhow::bail!(
|
||||||
|
"Unsupported target bridge pallet with instance index: {}",
|
||||||
|
bridge_instance_index
|
||||||
|
),
|
||||||
|
},
|
||||||
|
_ => anyhow::bail!("Unsupported Kusama call: {:?}", call),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_dispatch_info(call: &relay_kusama_client::runtime::Call) -> anyhow::Result<DispatchInfo> {
|
||||||
|
match *call {
|
||||||
|
relay_kusama_client::runtime::Call::System(relay_kusama_client::runtime::SystemCall::remark(_)) => {
|
||||||
|
Ok(DispatchInfo {
|
||||||
|
weight: crate::chains::kusama::SYSTEM_REMARK_CALL_WEIGHT,
|
||||||
|
class: DispatchClass::Normal,
|
||||||
|
pays_fee: Pays::Yes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => anyhow::bail!("Unsupported Kusama call: {:?}", call),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CliChain for Kusama {
|
||||||
|
const RUNTIME_VERSION: RuntimeVersion = bp_kusama::VERSION;
|
||||||
|
|
||||||
|
type KeyPair = sp_core::sr25519::Pair;
|
||||||
|
type MessagePayload = ();
|
||||||
|
|
||||||
|
fn ss58_format() -> u16 {
|
||||||
|
42
|
||||||
|
}
|
||||||
|
|
||||||
|
fn max_extrinsic_weight() -> Weight {
|
||||||
|
bp_kusama::max_extrinsic_weight()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_message(_message: encode_message::MessagePayload) -> Result<Self::MessagePayload, String> {
|
||||||
|
Err("Sending messages from Kusama is not yet supported.".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Kusama-to-Polkadot headers sync entrypoint.
|
||||||
|
|
||||||
|
use codec::Encode;
|
||||||
|
use sp_core::{Bytes, Pair};
|
||||||
|
|
||||||
|
use bp_header_chain::justification::GrandpaJustification;
|
||||||
|
use relay_kusama_client::{Kusama, SyncHeader as KusamaSyncHeader};
|
||||||
|
use relay_polkadot_client::{Polkadot, SigningParams as PolkadotSigningParams};
|
||||||
|
use relay_substrate_client::{Client, TransactionSignScheme, UnsignedTransaction};
|
||||||
|
use relay_utils::metrics::MetricsParams;
|
||||||
|
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
|
||||||
|
|
||||||
|
/// Maximal saturating difference between `balance(now)` and `balance(now-24h)` to treat
|
||||||
|
/// relay as gone wild.
|
||||||
|
///
|
||||||
|
/// Actual value, returned by `maximal_balance_decrease_per_day_is_sane` test is approximately 21 DOT,
|
||||||
|
/// but let's round up to 30 DOT here.
|
||||||
|
pub(crate) const MAXIMAL_BALANCE_DECREASE_PER_DAY: bp_polkadot::Balance = 30_000_000_000;
|
||||||
|
|
||||||
|
/// Kusama-to-Polkadot finality sync pipeline.
|
||||||
|
pub(crate) type FinalityPipelineKusamaFinalityToPolkadot =
|
||||||
|
SubstrateFinalityToSubstrate<Kusama, Polkadot, PolkadotSigningParams>;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(crate) struct KusamaFinalityToPolkadot {
|
||||||
|
finality_pipeline: FinalityPipelineKusamaFinalityToPolkadot,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KusamaFinalityToPolkadot {
|
||||||
|
pub fn new(target_client: Client<Polkadot>, target_sign: PolkadotSigningParams) -> Self {
|
||||||
|
Self {
|
||||||
|
finality_pipeline: FinalityPipelineKusamaFinalityToPolkadot::new(target_client, target_sign),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubstrateFinalitySyncPipeline for KusamaFinalityToPolkadot {
|
||||||
|
type FinalitySyncPipeline = FinalityPipelineKusamaFinalityToPolkadot;
|
||||||
|
|
||||||
|
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_kusama::BEST_FINALIZED_KUSAMA_HEADER_METHOD;
|
||||||
|
|
||||||
|
type TargetChain = Polkadot;
|
||||||
|
|
||||||
|
fn customize_metrics(params: MetricsParams) -> anyhow::Result<MetricsParams> {
|
||||||
|
crate::chains::add_polkadot_kusama_price_metrics::<Self::FinalitySyncPipeline>(
|
||||||
|
Some(finality_relay::metrics_prefix::<Self::FinalitySyncPipeline>()),
|
||||||
|
params,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_relay_guards(&self) {
|
||||||
|
relay_substrate_client::guard::abort_on_spec_version_change(
|
||||||
|
self.finality_pipeline.target_client.clone(),
|
||||||
|
bp_polkadot::VERSION.spec_version,
|
||||||
|
);
|
||||||
|
relay_substrate_client::guard::abort_when_account_balance_decreased(
|
||||||
|
self.finality_pipeline.target_client.clone(),
|
||||||
|
self.transactions_author(),
|
||||||
|
MAXIMAL_BALANCE_DECREASE_PER_DAY,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transactions_author(&self) -> bp_polkadot::AccountId {
|
||||||
|
(*self.finality_pipeline.target_sign.public().as_array_ref()).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_submit_finality_proof_transaction(
|
||||||
|
&self,
|
||||||
|
era: bp_runtime::TransactionEraOf<Polkadot>,
|
||||||
|
transaction_nonce: bp_runtime::IndexOf<Polkadot>,
|
||||||
|
header: KusamaSyncHeader,
|
||||||
|
proof: GrandpaJustification<bp_kusama::Header>,
|
||||||
|
) -> Bytes {
|
||||||
|
let call = relay_polkadot_client::runtime::Call::BridgeKusamaGrandpa(
|
||||||
|
relay_polkadot_client::runtime::BridgeKusamaGrandpaCall::submit_finality_proof(header.into_inner(), proof),
|
||||||
|
);
|
||||||
|
let genesis_hash = *self.finality_pipeline.target_client.genesis_hash();
|
||||||
|
let transaction = Polkadot::sign_transaction(
|
||||||
|
genesis_hash,
|
||||||
|
&self.finality_pipeline.target_sign,
|
||||||
|
era,
|
||||||
|
UnsignedTransaction::new(call, transaction_nonce),
|
||||||
|
);
|
||||||
|
|
||||||
|
Bytes(transaction.encode())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) mod tests {
|
||||||
|
use super::*;
|
||||||
|
use frame_support::weights::WeightToFeePolynomial;
|
||||||
|
use pallet_bridge_grandpa::weights::WeightInfo;
|
||||||
|
|
||||||
|
pub fn compute_maximal_balance_decrease_per_day<B, W>(expected_source_headers_per_day: u32) -> B
|
||||||
|
where
|
||||||
|
B: From<u32> + std::ops::Mul<Output = B>,
|
||||||
|
W: WeightToFeePolynomial<Balance = B>,
|
||||||
|
{
|
||||||
|
// we assume that the GRANDPA is not lagging here => ancestry length will be near to 0 (let's round up to 2)
|
||||||
|
const AVG_VOTES_ANCESTRIES_LEN: u32 = 2;
|
||||||
|
// let's assume number of validators is 1024 (more than on any existing well-known chain atm)
|
||||||
|
// => number of precommits is *2/3 + 1
|
||||||
|
const AVG_PRECOMMITS_LEN: u32 = 1024 * 2 / 3 + 1;
|
||||||
|
|
||||||
|
// GRANDPA pallet weights. We're now using Rialto weights everywhere.
|
||||||
|
//
|
||||||
|
// Using Rialto runtime is slightly incorrect, because `DbWeight` of other runtimes may differ
|
||||||
|
// from the `DbWeight` of Rialto runtime. But now (and most probably forever) it is the same.
|
||||||
|
type GrandpaPalletWeights = pallet_bridge_grandpa::weights::RialtoWeight<rialto_runtime::Runtime>;
|
||||||
|
|
||||||
|
// The following formula shall not be treated as super-accurate - guard is to protect from mad relays,
|
||||||
|
// not to protect from over-average loses.
|
||||||
|
|
||||||
|
// increase number of headers a bit
|
||||||
|
let expected_source_headers_per_day = expected_source_headers_per_day * 110 / 100;
|
||||||
|
let single_source_header_submit_call_weight =
|
||||||
|
GrandpaPalletWeights::submit_finality_proof(AVG_VOTES_ANCESTRIES_LEN, AVG_PRECOMMITS_LEN);
|
||||||
|
// for simplicity - add extra weight for base tx fee + fee that is paid for the tx size + adjusted fee
|
||||||
|
let single_source_header_submit_tx_weight = single_source_header_submit_call_weight * 3 / 2;
|
||||||
|
let single_source_header_tx_cost = W::calc(&single_source_header_submit_tx_weight);
|
||||||
|
let maximal_expected_decrease = single_source_header_tx_cost * B::from(expected_source_headers_per_day);
|
||||||
|
|
||||||
|
maximal_expected_decrease
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn maximal_balance_decrease_per_day_is_sane() {
|
||||||
|
// we expect Kusama -> Polkadot relay to be running in mandatory-headers-only mode
|
||||||
|
// => we expect single header for every Kusama session
|
||||||
|
let maximal_balance_decrease = compute_maximal_balance_decrease_per_day::<
|
||||||
|
bp_polkadot::Balance,
|
||||||
|
bp_polkadot::WeightToFee,
|
||||||
|
>(bp_kusama::DAYS / bp_kusama::SESSION_LENGTH + 1);
|
||||||
|
assert!(
|
||||||
|
MAXIMAL_BALANCE_DECREASE_PER_DAY >= maximal_balance_decrease,
|
||||||
|
"Maximal expected loss per day {} is larger than hardcoded {}",
|
||||||
|
maximal_balance_decrease,
|
||||||
|
MAXIMAL_BALANCE_DECREASE_PER_DAY,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,304 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Kusama-to-Polkadot messages sync entrypoint.
|
||||||
|
|
||||||
|
use std::{ops::RangeInclusive, time::Duration};
|
||||||
|
|
||||||
|
use codec::Encode;
|
||||||
|
use sp_core::{Bytes, Pair};
|
||||||
|
|
||||||
|
use bp_messages::MessageNonce;
|
||||||
|
use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof;
|
||||||
|
use frame_support::weights::Weight;
|
||||||
|
use messages_relay::message_lane::MessageLane;
|
||||||
|
use relay_kusama_client::{HeaderId as KusamaHeaderId, Kusama, SigningParams as KusamaSigningParams};
|
||||||
|
use relay_polkadot_client::{HeaderId as PolkadotHeaderId, Polkadot, SigningParams as PolkadotSigningParams};
|
||||||
|
use relay_substrate_client::{Chain, Client, TransactionSignScheme, UnsignedTransaction};
|
||||||
|
use relay_utils::metrics::MetricsParams;
|
||||||
|
use sp_runtime::{FixedPointNumber, FixedU128};
|
||||||
|
use substrate_relay_helper::messages_lane::{
|
||||||
|
select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics, SubstrateMessageLane,
|
||||||
|
SubstrateMessageLaneToSubstrate,
|
||||||
|
};
|
||||||
|
use substrate_relay_helper::messages_source::SubstrateMessagesSource;
|
||||||
|
use substrate_relay_helper::messages_target::SubstrateMessagesTarget;
|
||||||
|
|
||||||
|
/// Kusama-to-Polkadot message lane.
|
||||||
|
pub type MessageLaneKusamaMessagesToPolkadot =
|
||||||
|
SubstrateMessageLaneToSubstrate<Kusama, KusamaSigningParams, Polkadot, PolkadotSigningParams>;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct KusamaMessagesToPolkadot {
|
||||||
|
message_lane: MessageLaneKusamaMessagesToPolkadot,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubstrateMessageLane for KusamaMessagesToPolkadot {
|
||||||
|
type MessageLane = MessageLaneKusamaMessagesToPolkadot;
|
||||||
|
|
||||||
|
const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str = bp_polkadot::TO_POLKADOT_MESSAGE_DETAILS_METHOD;
|
||||||
|
const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str =
|
||||||
|
bp_polkadot::TO_POLKADOT_LATEST_GENERATED_NONCE_METHOD;
|
||||||
|
const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str =
|
||||||
|
bp_polkadot::TO_POLKADOT_LATEST_RECEIVED_NONCE_METHOD;
|
||||||
|
|
||||||
|
const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = bp_kusama::FROM_KUSAMA_LATEST_RECEIVED_NONCE_METHOD;
|
||||||
|
const INBOUND_LANE_LATEST_CONFIRMED_NONCE_METHOD: &'static str =
|
||||||
|
bp_kusama::FROM_KUSAMA_LATEST_CONFIRMED_NONCE_METHOD;
|
||||||
|
const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str = bp_kusama::FROM_KUSAMA_UNREWARDED_RELAYERS_STATE;
|
||||||
|
|
||||||
|
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_kusama::BEST_FINALIZED_KUSAMA_HEADER_METHOD;
|
||||||
|
const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = bp_polkadot::BEST_FINALIZED_POLKADOT_HEADER_METHOD;
|
||||||
|
|
||||||
|
const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = bp_kusama::WITH_POLKADOT_MESSAGES_PALLET_NAME;
|
||||||
|
const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = bp_polkadot::WITH_KUSAMA_MESSAGES_PALLET_NAME;
|
||||||
|
|
||||||
|
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight = bp_polkadot::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
|
||||||
|
|
||||||
|
type SourceChain = Kusama;
|
||||||
|
type TargetChain = Polkadot;
|
||||||
|
|
||||||
|
fn source_transactions_author(&self) -> bp_kusama::AccountId {
|
||||||
|
(*self.message_lane.source_sign.public().as_array_ref()).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_messages_receiving_proof_transaction(
|
||||||
|
&self,
|
||||||
|
transaction_nonce: bp_runtime::IndexOf<Kusama>,
|
||||||
|
_generated_at_block: PolkadotHeaderId,
|
||||||
|
proof: <Self::MessageLane as MessageLane>::MessagesReceivingProof,
|
||||||
|
) -> Bytes {
|
||||||
|
let (relayers_state, proof) = proof;
|
||||||
|
let call = relay_kusama_client::runtime::Call::BridgePolkadotMessages(
|
||||||
|
relay_kusama_client::runtime::BridgePolkadotMessagesCall::receive_messages_delivery_proof(
|
||||||
|
proof,
|
||||||
|
relayers_state,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
let genesis_hash = *self.message_lane.source_client.genesis_hash();
|
||||||
|
let transaction = Kusama::sign_transaction(
|
||||||
|
genesis_hash,
|
||||||
|
&self.message_lane.source_sign,
|
||||||
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
|
UnsignedTransaction::new(call, transaction_nonce),
|
||||||
|
);
|
||||||
|
log::trace!(
|
||||||
|
target: "bridge",
|
||||||
|
"Prepared Polkadot -> Kusama confirmation transaction. Weight: <unknown>/{}, size: {}/{}",
|
||||||
|
bp_kusama::max_extrinsic_weight(),
|
||||||
|
transaction.encode().len(),
|
||||||
|
bp_kusama::max_extrinsic_size(),
|
||||||
|
);
|
||||||
|
Bytes(transaction.encode())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn target_transactions_author(&self) -> bp_polkadot::AccountId {
|
||||||
|
(*self.message_lane.target_sign.public().as_array_ref()).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_messages_delivery_transaction(
|
||||||
|
&self,
|
||||||
|
transaction_nonce: bp_runtime::IndexOf<Polkadot>,
|
||||||
|
_generated_at_header: KusamaHeaderId,
|
||||||
|
_nonces: RangeInclusive<MessageNonce>,
|
||||||
|
proof: <Self::MessageLane as MessageLane>::MessagesProof,
|
||||||
|
) -> Bytes {
|
||||||
|
let (dispatch_weight, proof) = proof;
|
||||||
|
let FromBridgedChainMessagesProof {
|
||||||
|
ref nonces_start,
|
||||||
|
ref nonces_end,
|
||||||
|
..
|
||||||
|
} = proof;
|
||||||
|
let messages_count = nonces_end - nonces_start + 1;
|
||||||
|
|
||||||
|
let call = relay_polkadot_client::runtime::Call::BridgeKusamaMessages(
|
||||||
|
relay_polkadot_client::runtime::BridgeKusamaMessagesCall::receive_messages_proof(
|
||||||
|
self.message_lane.relayer_id_at_source.clone(),
|
||||||
|
proof,
|
||||||
|
messages_count as _,
|
||||||
|
dispatch_weight,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
let genesis_hash = *self.message_lane.target_client.genesis_hash();
|
||||||
|
let transaction = Polkadot::sign_transaction(
|
||||||
|
genesis_hash,
|
||||||
|
&self.message_lane.target_sign,
|
||||||
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
|
UnsignedTransaction::new(call, transaction_nonce),
|
||||||
|
);
|
||||||
|
log::trace!(
|
||||||
|
target: "bridge",
|
||||||
|
"Prepared Kusama -> Polkadot delivery transaction. Weight: <unknown>/{}, size: {}/{}",
|
||||||
|
bp_polkadot::max_extrinsic_weight(),
|
||||||
|
transaction.encode().len(),
|
||||||
|
bp_polkadot::max_extrinsic_size(),
|
||||||
|
);
|
||||||
|
Bytes(transaction.encode())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Kusama node as messages source.
|
||||||
|
type KusamaSourceClient = SubstrateMessagesSource<KusamaMessagesToPolkadot>;
|
||||||
|
|
||||||
|
/// Polkadot node as messages target.
|
||||||
|
type PolkadotTargetClient = SubstrateMessagesTarget<KusamaMessagesToPolkadot>;
|
||||||
|
|
||||||
|
/// Run Kusama-to-Polkadot messages sync.
|
||||||
|
pub async fn run(
|
||||||
|
params: MessagesRelayParams<Kusama, KusamaSigningParams, Polkadot, PolkadotSigningParams>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let stall_timeout = Duration::from_secs(5 * 60);
|
||||||
|
let relayer_id_at_kusama = (*params.source_sign.public().as_array_ref()).into();
|
||||||
|
|
||||||
|
let lane_id = params.lane_id;
|
||||||
|
let source_client = params.source_client;
|
||||||
|
let lane = KusamaMessagesToPolkadot {
|
||||||
|
message_lane: SubstrateMessageLaneToSubstrate {
|
||||||
|
source_client: source_client.clone(),
|
||||||
|
source_sign: params.source_sign,
|
||||||
|
target_client: params.target_client.clone(),
|
||||||
|
target_sign: params.target_sign,
|
||||||
|
relayer_id_at_source: relayer_id_at_kusama,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2/3 is reserved for proofs and tx overhead
|
||||||
|
let max_messages_size_in_single_batch = bp_polkadot::max_extrinsic_size() / 3;
|
||||||
|
// we don't know exact weights of the Polkadot runtime. So to guess weights we'll be using
|
||||||
|
// weights from Rialto and then simply dividing it by x2.
|
||||||
|
let (max_messages_in_single_batch, max_messages_weight_in_single_batch) =
|
||||||
|
select_delivery_transaction_limits::<pallet_bridge_messages::weights::RialtoWeight<rialto_runtime::Runtime>>(
|
||||||
|
bp_polkadot::max_extrinsic_weight(),
|
||||||
|
bp_polkadot::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
|
||||||
|
);
|
||||||
|
let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = (
|
||||||
|
max_messages_in_single_batch / 2,
|
||||||
|
max_messages_weight_in_single_batch / 2,
|
||||||
|
);
|
||||||
|
|
||||||
|
log::info!(
|
||||||
|
target: "bridge",
|
||||||
|
"Starting Kusama -> Polkadot messages relay.\n\t\
|
||||||
|
Kusama relayer account id: {:?}\n\t\
|
||||||
|
Max messages in single transaction: {}\n\t\
|
||||||
|
Max messages size in single transaction: {}\n\t\
|
||||||
|
Max messages weight in single transaction: {}\n\t\
|
||||||
|
Relayer mode: {:?}",
|
||||||
|
lane.message_lane.relayer_id_at_source,
|
||||||
|
max_messages_in_single_batch,
|
||||||
|
max_messages_size_in_single_batch,
|
||||||
|
max_messages_weight_in_single_batch,
|
||||||
|
params.relayer_mode,
|
||||||
|
);
|
||||||
|
|
||||||
|
let (metrics_params, metrics_values) = add_standalone_metrics(
|
||||||
|
Some(messages_relay::message_lane_loop::metrics_prefix::<
|
||||||
|
<KusamaMessagesToPolkadot as SubstrateMessageLane>::MessageLane,
|
||||||
|
>(&lane_id)),
|
||||||
|
params.metrics_params,
|
||||||
|
source_client.clone(),
|
||||||
|
)?;
|
||||||
|
messages_relay::message_lane_loop::run(
|
||||||
|
messages_relay::message_lane_loop::Params {
|
||||||
|
lane: lane_id,
|
||||||
|
source_tick: Kusama::AVERAGE_BLOCK_INTERVAL,
|
||||||
|
target_tick: Polkadot::AVERAGE_BLOCK_INTERVAL,
|
||||||
|
reconnect_delay: relay_utils::relay_loop::RECONNECT_DELAY,
|
||||||
|
stall_timeout,
|
||||||
|
delivery_params: messages_relay::message_lane_loop::MessageDeliveryParams {
|
||||||
|
max_unrewarded_relayer_entries_at_target: bp_polkadot::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
|
||||||
|
max_unconfirmed_nonces_at_target: bp_polkadot::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE,
|
||||||
|
max_messages_in_single_batch,
|
||||||
|
max_messages_weight_in_single_batch,
|
||||||
|
max_messages_size_in_single_batch,
|
||||||
|
relayer_mode: params.relayer_mode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
KusamaSourceClient::new(
|
||||||
|
source_client.clone(),
|
||||||
|
lane.clone(),
|
||||||
|
lane_id,
|
||||||
|
params.target_to_source_headers_relay,
|
||||||
|
),
|
||||||
|
PolkadotTargetClient::new(
|
||||||
|
params.target_client,
|
||||||
|
lane,
|
||||||
|
lane_id,
|
||||||
|
metrics_values,
|
||||||
|
params.source_to_target_headers_relay,
|
||||||
|
),
|
||||||
|
metrics_params,
|
||||||
|
futures::future::pending(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add standalone metrics for the Kusama -> Polkadot messages loop.
|
||||||
|
pub(crate) fn add_standalone_metrics(
|
||||||
|
metrics_prefix: Option<String>,
|
||||||
|
metrics_params: MetricsParams,
|
||||||
|
source_client: Client<Kusama>,
|
||||||
|
) -> anyhow::Result<(MetricsParams, StandaloneMessagesMetrics)> {
|
||||||
|
let polkadot_to_kusama_conversion_rate_key =
|
||||||
|
bp_runtime::storage_parameter_key(bp_kusama::POLKADOT_TO_KUSAMA_CONVERSION_RATE_PARAMETER_NAME).0;
|
||||||
|
|
||||||
|
substrate_relay_helper::messages_lane::add_standalone_metrics::<KusamaMessagesToPolkadot>(
|
||||||
|
metrics_prefix,
|
||||||
|
metrics_params,
|
||||||
|
source_client,
|
||||||
|
Some(crate::chains::polkadot::TOKEN_ID),
|
||||||
|
Some(crate::chains::kusama::TOKEN_ID),
|
||||||
|
Some((
|
||||||
|
sp_core::storage::StorageKey(polkadot_to_kusama_conversion_rate_key),
|
||||||
|
// starting relay before this parameter will be set to some value may cause troubles
|
||||||
|
FixedU128::from_inner(FixedU128::DIV),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update Polkadot -> Kusama conversion rate, stored in Kusama runtime storage.
|
||||||
|
pub(crate) async fn update_polkadot_to_kusama_conversion_rate(
|
||||||
|
client: Client<Kusama>,
|
||||||
|
signer: <Kusama as TransactionSignScheme>::AccountKeyPair,
|
||||||
|
updated_rate: f64,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let genesis_hash = *client.genesis_hash();
|
||||||
|
let signer_id = (*signer.public().as_array_ref()).into();
|
||||||
|
client
|
||||||
|
.submit_signed_extrinsic(signer_id, move |_, transaction_nonce| {
|
||||||
|
Bytes(
|
||||||
|
Kusama::sign_transaction(
|
||||||
|
genesis_hash,
|
||||||
|
&signer,
|
||||||
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
|
UnsignedTransaction::new(
|
||||||
|
relay_kusama_client::runtime::Call::BridgePolkadotMessages(
|
||||||
|
relay_kusama_client::runtime::BridgePolkadotMessagesCall::update_pallet_parameter(
|
||||||
|
relay_kusama_client::runtime::BridgePolkadotMessagesParameter::PolkadotToKusamaConversionRate(
|
||||||
|
sp_runtime::FixedU128::from_float(updated_rate),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
transaction_nonce,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.encode(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map(drop)
|
||||||
|
.map_err(|err| anyhow::format_err!("{:?}", err))
|
||||||
|
}
|
||||||
@@ -16,8 +16,12 @@
|
|||||||
|
|
||||||
//! Chain-specific relayer configuration.
|
//! Chain-specific relayer configuration.
|
||||||
|
|
||||||
|
pub mod kusama_headers_to_polkadot;
|
||||||
|
pub mod kusama_messages_to_polkadot;
|
||||||
pub mod millau_headers_to_rialto;
|
pub mod millau_headers_to_rialto;
|
||||||
pub mod millau_messages_to_rialto;
|
pub mod millau_messages_to_rialto;
|
||||||
|
pub mod polkadot_headers_to_kusama;
|
||||||
|
pub mod polkadot_messages_to_kusama;
|
||||||
pub mod rialto_headers_to_millau;
|
pub mod rialto_headers_to_millau;
|
||||||
pub mod rialto_messages_to_millau;
|
pub mod rialto_messages_to_millau;
|
||||||
pub mod rococo_headers_to_wococo;
|
pub mod rococo_headers_to_wococo;
|
||||||
@@ -26,7 +30,9 @@ pub mod westend_headers_to_millau;
|
|||||||
pub mod wococo_headers_to_rococo;
|
pub mod wococo_headers_to_rococo;
|
||||||
pub mod wococo_messages_to_rococo;
|
pub mod wococo_messages_to_rococo;
|
||||||
|
|
||||||
|
mod kusama;
|
||||||
mod millau;
|
mod millau;
|
||||||
|
mod polkadot;
|
||||||
mod rialto;
|
mod rialto;
|
||||||
mod rococo;
|
mod rococo;
|
||||||
mod westend;
|
mod westend;
|
||||||
@@ -37,9 +43,9 @@ mod wococo;
|
|||||||
// Rialto as BTC and Millau as wBTC (only in relayer).
|
// Rialto as BTC and Millau as wBTC (only in relayer).
|
||||||
|
|
||||||
/// The identifier of token, which value is associated with Rialto token value by relayer.
|
/// The identifier of token, which value is associated with Rialto token value by relayer.
|
||||||
pub(crate) const RIALTO_ASSOCIATED_TOKEN_ID: &str = "polkadot";
|
pub(crate) const RIALTO_ASSOCIATED_TOKEN_ID: &str = polkadot::TOKEN_ID;
|
||||||
/// The identifier of token, which value is associated with Millau token value by relayer.
|
/// The identifier of token, which value is associated with Millau token value by relayer.
|
||||||
pub(crate) const MILLAU_ASSOCIATED_TOKEN_ID: &str = "kusama";
|
pub(crate) const MILLAU_ASSOCIATED_TOKEN_ID: &str = kusama::TOKEN_ID;
|
||||||
|
|
||||||
use relay_utils::metrics::MetricsParams;
|
use relay_utils::metrics::MetricsParams;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
// 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 codec::Decode;
|
||||||
|
use frame_support::weights::{DispatchClass, DispatchInfo, Pays, Weight};
|
||||||
|
use relay_polkadot_client::Polkadot;
|
||||||
|
use sp_version::RuntimeVersion;
|
||||||
|
|
||||||
|
use crate::cli::{
|
||||||
|
bridge,
|
||||||
|
encode_call::{Call, CliEncodeCall},
|
||||||
|
encode_message, CliChain,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Weight of the `system::remark` call at Polkadot.
|
||||||
|
///
|
||||||
|
/// This weight is larger (x2) than actual weight at current Polkadot runtime to avoid unsuccessful
|
||||||
|
/// calls in the future. But since it is used only in tests (and on test chains), this is ok.
|
||||||
|
pub(crate) const SYSTEM_REMARK_CALL_WEIGHT: Weight = 2 * 1_345_000;
|
||||||
|
|
||||||
|
/// Id of Polkadot token that is used to fetch token price.
|
||||||
|
pub(crate) const TOKEN_ID: &str = "polkadot";
|
||||||
|
|
||||||
|
impl CliEncodeCall for Polkadot {
|
||||||
|
fn max_extrinsic_size() -> u32 {
|
||||||
|
bp_polkadot::max_extrinsic_size()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_call(call: &Call) -> anyhow::Result<Self::Call> {
|
||||||
|
Ok(match call {
|
||||||
|
Call::Remark { remark_payload, .. } => {
|
||||||
|
relay_polkadot_client::runtime::Call::System(relay_polkadot_client::runtime::SystemCall::remark(
|
||||||
|
remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Call::BridgeSendMessage {
|
||||||
|
lane,
|
||||||
|
payload,
|
||||||
|
fee,
|
||||||
|
bridge_instance_index,
|
||||||
|
} => match *bridge_instance_index {
|
||||||
|
bridge::POLKADOT_TO_KUSAMA_INDEX => {
|
||||||
|
let payload = Decode::decode(&mut &*payload.0)?;
|
||||||
|
relay_polkadot_client::runtime::Call::BridgeKusamaMessages(
|
||||||
|
relay_polkadot_client::runtime::BridgeKusamaMessagesCall::send_message(lane.0, payload, fee.0),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => anyhow::bail!(
|
||||||
|
"Unsupported target bridge pallet with instance index: {}",
|
||||||
|
bridge_instance_index
|
||||||
|
),
|
||||||
|
},
|
||||||
|
_ => anyhow::bail!("Unsupported Polkadot call: {:?}", call),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_dispatch_info(call: &relay_polkadot_client::runtime::Call) -> anyhow::Result<DispatchInfo> {
|
||||||
|
match *call {
|
||||||
|
relay_polkadot_client::runtime::Call::System(relay_polkadot_client::runtime::SystemCall::remark(_)) => {
|
||||||
|
Ok(DispatchInfo {
|
||||||
|
weight: crate::chains::polkadot::SYSTEM_REMARK_CALL_WEIGHT,
|
||||||
|
class: DispatchClass::Normal,
|
||||||
|
pays_fee: Pays::Yes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => anyhow::bail!("Unsupported Polkadot call: {:?}", call),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CliChain for Polkadot {
|
||||||
|
const RUNTIME_VERSION: RuntimeVersion = bp_polkadot::VERSION;
|
||||||
|
|
||||||
|
type KeyPair = sp_core::sr25519::Pair;
|
||||||
|
type MessagePayload = ();
|
||||||
|
|
||||||
|
fn ss58_format() -> u16 {
|
||||||
|
42
|
||||||
|
}
|
||||||
|
|
||||||
|
fn max_extrinsic_weight() -> Weight {
|
||||||
|
bp_polkadot::max_extrinsic_weight()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_message(_message: encode_message::MessagePayload) -> Result<Self::MessagePayload, String> {
|
||||||
|
Err("Sending messages from Polkadot is not yet supported.".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Polkadot-to-Kusama headers sync entrypoint.
|
||||||
|
|
||||||
|
use codec::Encode;
|
||||||
|
use sp_core::{Bytes, Pair};
|
||||||
|
|
||||||
|
use bp_header_chain::justification::GrandpaJustification;
|
||||||
|
use relay_kusama_client::{Kusama, SigningParams as KusamaSigningParams};
|
||||||
|
use relay_polkadot_client::{Polkadot, SyncHeader as PolkadotSyncHeader};
|
||||||
|
use relay_substrate_client::{Client, TransactionSignScheme, UnsignedTransaction};
|
||||||
|
use relay_utils::metrics::MetricsParams;
|
||||||
|
use substrate_relay_helper::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
|
||||||
|
|
||||||
|
/// Maximal saturating difference between `balance(now)` and `balance(now-24h)` to treat
|
||||||
|
/// relay as gone wild.
|
||||||
|
///
|
||||||
|
/// Actual value, returned by `maximal_balance_decrease_per_day_is_sane` test is approximately 0.001 KSM,
|
||||||
|
/// but let's round up to 0.1 KSM here.
|
||||||
|
pub(crate) const MAXIMAL_BALANCE_DECREASE_PER_DAY: bp_polkadot::Balance = 100_000_000_000;
|
||||||
|
|
||||||
|
/// Polkadot-to-Kusama finality sync pipeline.
|
||||||
|
pub(crate) type FinalityPipelinePolkadotFinalityToKusama =
|
||||||
|
SubstrateFinalityToSubstrate<Polkadot, Kusama, KusamaSigningParams>;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(crate) struct PolkadotFinalityToKusama {
|
||||||
|
finality_pipeline: FinalityPipelinePolkadotFinalityToKusama,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PolkadotFinalityToKusama {
|
||||||
|
pub fn new(target_client: Client<Kusama>, target_sign: KusamaSigningParams) -> Self {
|
||||||
|
Self {
|
||||||
|
finality_pipeline: FinalityPipelinePolkadotFinalityToKusama::new(target_client, target_sign),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubstrateFinalitySyncPipeline for PolkadotFinalityToKusama {
|
||||||
|
type FinalitySyncPipeline = FinalityPipelinePolkadotFinalityToKusama;
|
||||||
|
|
||||||
|
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_polkadot::BEST_FINALIZED_POLKADOT_HEADER_METHOD;
|
||||||
|
|
||||||
|
type TargetChain = Kusama;
|
||||||
|
|
||||||
|
fn customize_metrics(params: MetricsParams) -> anyhow::Result<MetricsParams> {
|
||||||
|
crate::chains::add_polkadot_kusama_price_metrics::<Self::FinalitySyncPipeline>(
|
||||||
|
Some(finality_relay::metrics_prefix::<Self::FinalitySyncPipeline>()),
|
||||||
|
params,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_relay_guards(&self) {
|
||||||
|
relay_substrate_client::guard::abort_on_spec_version_change(
|
||||||
|
self.finality_pipeline.target_client.clone(),
|
||||||
|
bp_kusama::VERSION.spec_version,
|
||||||
|
);
|
||||||
|
relay_substrate_client::guard::abort_when_account_balance_decreased(
|
||||||
|
self.finality_pipeline.target_client.clone(),
|
||||||
|
self.transactions_author(),
|
||||||
|
MAXIMAL_BALANCE_DECREASE_PER_DAY,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transactions_author(&self) -> bp_kusama::AccountId {
|
||||||
|
(*self.finality_pipeline.target_sign.public().as_array_ref()).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_submit_finality_proof_transaction(
|
||||||
|
&self,
|
||||||
|
era: bp_runtime::TransactionEraOf<Kusama>,
|
||||||
|
transaction_nonce: bp_runtime::IndexOf<Kusama>,
|
||||||
|
header: PolkadotSyncHeader,
|
||||||
|
proof: GrandpaJustification<bp_polkadot::Header>,
|
||||||
|
) -> Bytes {
|
||||||
|
let call = relay_kusama_client::runtime::Call::BridgePolkadotGrandpa(
|
||||||
|
relay_kusama_client::runtime::BridgePolkadotGrandpaCall::submit_finality_proof(header.into_inner(), proof),
|
||||||
|
);
|
||||||
|
let genesis_hash = *self.finality_pipeline.target_client.genesis_hash();
|
||||||
|
let transaction = Kusama::sign_transaction(
|
||||||
|
genesis_hash,
|
||||||
|
&self.finality_pipeline.target_sign,
|
||||||
|
era,
|
||||||
|
UnsignedTransaction::new(call, transaction_nonce),
|
||||||
|
);
|
||||||
|
|
||||||
|
Bytes(transaction.encode())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::chains::kusama_headers_to_polkadot::tests::compute_maximal_balance_decrease_per_day;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn maximal_balance_decrease_per_day_is_sane() {
|
||||||
|
// we expect Polkadot -> Kusama relay to be running in mandatory-headers-only mode
|
||||||
|
// => we expect single header for every Polkadot session
|
||||||
|
let maximal_balance_decrease = compute_maximal_balance_decrease_per_day::<
|
||||||
|
bp_kusama::Balance,
|
||||||
|
bp_kusama::WeightToFee,
|
||||||
|
>(bp_polkadot::DAYS / bp_polkadot::SESSION_LENGTH + 1);
|
||||||
|
assert!(
|
||||||
|
MAXIMAL_BALANCE_DECREASE_PER_DAY >= maximal_balance_decrease,
|
||||||
|
"Maximal expected loss per day {} is larger than hardcoded {}",
|
||||||
|
maximal_balance_decrease,
|
||||||
|
MAXIMAL_BALANCE_DECREASE_PER_DAY,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,303 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Polkadot-to-Kusama messages sync entrypoint.
|
||||||
|
|
||||||
|
use std::{ops::RangeInclusive, time::Duration};
|
||||||
|
|
||||||
|
use codec::Encode;
|
||||||
|
use sp_core::{Bytes, Pair};
|
||||||
|
|
||||||
|
use bp_messages::MessageNonce;
|
||||||
|
use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof;
|
||||||
|
use frame_support::weights::Weight;
|
||||||
|
use messages_relay::message_lane::MessageLane;
|
||||||
|
use relay_kusama_client::{HeaderId as KusamaHeaderId, Kusama, SigningParams as KusamaSigningParams};
|
||||||
|
use relay_polkadot_client::{HeaderId as PolkadotHeaderId, Polkadot, SigningParams as PolkadotSigningParams};
|
||||||
|
use relay_substrate_client::{Chain, Client, TransactionSignScheme, UnsignedTransaction};
|
||||||
|
use relay_utils::metrics::MetricsParams;
|
||||||
|
use sp_runtime::{FixedPointNumber, FixedU128};
|
||||||
|
use substrate_relay_helper::messages_lane::{
|
||||||
|
select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics, SubstrateMessageLane,
|
||||||
|
SubstrateMessageLaneToSubstrate,
|
||||||
|
};
|
||||||
|
use substrate_relay_helper::messages_source::SubstrateMessagesSource;
|
||||||
|
use substrate_relay_helper::messages_target::SubstrateMessagesTarget;
|
||||||
|
|
||||||
|
/// Polkadot-to-Kusama message lane.
|
||||||
|
pub type MessageLanePolkadotMessagesToKusama =
|
||||||
|
SubstrateMessageLaneToSubstrate<Polkadot, PolkadotSigningParams, Kusama, KusamaSigningParams>;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PolkadotMessagesToKusama {
|
||||||
|
message_lane: MessageLanePolkadotMessagesToKusama,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubstrateMessageLane for PolkadotMessagesToKusama {
|
||||||
|
type MessageLane = MessageLanePolkadotMessagesToKusama;
|
||||||
|
const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str = bp_kusama::TO_KUSAMA_MESSAGE_DETAILS_METHOD;
|
||||||
|
const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str =
|
||||||
|
bp_kusama::TO_KUSAMA_LATEST_GENERATED_NONCE_METHOD;
|
||||||
|
const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = bp_kusama::TO_KUSAMA_LATEST_RECEIVED_NONCE_METHOD;
|
||||||
|
|
||||||
|
const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str =
|
||||||
|
bp_polkadot::FROM_POLKADOT_LATEST_RECEIVED_NONCE_METHOD;
|
||||||
|
const INBOUND_LANE_LATEST_CONFIRMED_NONCE_METHOD: &'static str =
|
||||||
|
bp_polkadot::FROM_POLKADOT_LATEST_CONFIRMED_NONCE_METHOD;
|
||||||
|
const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str = bp_polkadot::FROM_POLKADOT_UNREWARDED_RELAYERS_STATE;
|
||||||
|
|
||||||
|
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_polkadot::BEST_FINALIZED_POLKADOT_HEADER_METHOD;
|
||||||
|
const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = bp_kusama::BEST_FINALIZED_KUSAMA_HEADER_METHOD;
|
||||||
|
|
||||||
|
const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = bp_polkadot::WITH_KUSAMA_MESSAGES_PALLET_NAME;
|
||||||
|
const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = bp_kusama::WITH_POLKADOT_MESSAGES_PALLET_NAME;
|
||||||
|
|
||||||
|
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight = bp_kusama::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
|
||||||
|
|
||||||
|
type SourceChain = Polkadot;
|
||||||
|
type TargetChain = Kusama;
|
||||||
|
|
||||||
|
fn source_transactions_author(&self) -> bp_polkadot::AccountId {
|
||||||
|
(*self.message_lane.source_sign.public().as_array_ref()).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_messages_receiving_proof_transaction(
|
||||||
|
&self,
|
||||||
|
transaction_nonce: bp_runtime::IndexOf<Polkadot>,
|
||||||
|
_generated_at_block: KusamaHeaderId,
|
||||||
|
proof: <Self::MessageLane as MessageLane>::MessagesReceivingProof,
|
||||||
|
) -> Bytes {
|
||||||
|
let (relayers_state, proof) = proof;
|
||||||
|
let call = relay_polkadot_client::runtime::Call::BridgeKusamaMessages(
|
||||||
|
relay_polkadot_client::runtime::BridgeKusamaMessagesCall::receive_messages_delivery_proof(
|
||||||
|
proof,
|
||||||
|
relayers_state,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
let genesis_hash = *self.message_lane.source_client.genesis_hash();
|
||||||
|
let transaction = Polkadot::sign_transaction(
|
||||||
|
genesis_hash,
|
||||||
|
&self.message_lane.source_sign,
|
||||||
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
|
UnsignedTransaction::new(call, transaction_nonce),
|
||||||
|
);
|
||||||
|
log::trace!(
|
||||||
|
target: "bridge",
|
||||||
|
"Prepared Kusama -> Polkadot confirmation transaction. Weight: <unknown>/{}, size: {}/{}",
|
||||||
|
bp_polkadot::max_extrinsic_weight(),
|
||||||
|
transaction.encode().len(),
|
||||||
|
bp_polkadot::max_extrinsic_size(),
|
||||||
|
);
|
||||||
|
Bytes(transaction.encode())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn target_transactions_author(&self) -> bp_kusama::AccountId {
|
||||||
|
(*self.message_lane.target_sign.public().as_array_ref()).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_messages_delivery_transaction(
|
||||||
|
&self,
|
||||||
|
transaction_nonce: bp_runtime::IndexOf<Kusama>,
|
||||||
|
_generated_at_header: PolkadotHeaderId,
|
||||||
|
_nonces: RangeInclusive<MessageNonce>,
|
||||||
|
proof: <Self::MessageLane as MessageLane>::MessagesProof,
|
||||||
|
) -> Bytes {
|
||||||
|
let (dispatch_weight, proof) = proof;
|
||||||
|
let FromBridgedChainMessagesProof {
|
||||||
|
ref nonces_start,
|
||||||
|
ref nonces_end,
|
||||||
|
..
|
||||||
|
} = proof;
|
||||||
|
let messages_count = nonces_end - nonces_start + 1;
|
||||||
|
|
||||||
|
let call = relay_kusama_client::runtime::Call::BridgePolkadotMessages(
|
||||||
|
relay_kusama_client::runtime::BridgePolkadotMessagesCall::receive_messages_proof(
|
||||||
|
self.message_lane.relayer_id_at_source.clone(),
|
||||||
|
proof,
|
||||||
|
messages_count as _,
|
||||||
|
dispatch_weight,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
let genesis_hash = *self.message_lane.target_client.genesis_hash();
|
||||||
|
let transaction = Kusama::sign_transaction(
|
||||||
|
genesis_hash,
|
||||||
|
&self.message_lane.target_sign,
|
||||||
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
|
UnsignedTransaction::new(call, transaction_nonce),
|
||||||
|
);
|
||||||
|
log::trace!(
|
||||||
|
target: "bridge",
|
||||||
|
"Prepared Polkadot -> Kusama delivery transaction. Weight: <unknown>/{}, size: {}/{}",
|
||||||
|
bp_kusama::max_extrinsic_weight(),
|
||||||
|
transaction.encode().len(),
|
||||||
|
bp_kusama::max_extrinsic_size(),
|
||||||
|
);
|
||||||
|
Bytes(transaction.encode())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Polkadot node as messages source.
|
||||||
|
type PolkadotSourceClient = SubstrateMessagesSource<PolkadotMessagesToKusama>;
|
||||||
|
|
||||||
|
/// Kusama node as messages target.
|
||||||
|
type KusamaTargetClient = SubstrateMessagesTarget<PolkadotMessagesToKusama>;
|
||||||
|
|
||||||
|
/// Run Polkadot-to-Kusama messages sync.
|
||||||
|
pub async fn run(
|
||||||
|
params: MessagesRelayParams<Polkadot, PolkadotSigningParams, Kusama, KusamaSigningParams>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let stall_timeout = Duration::from_secs(5 * 60);
|
||||||
|
let relayer_id_at_polkadot = (*params.source_sign.public().as_array_ref()).into();
|
||||||
|
|
||||||
|
let lane_id = params.lane_id;
|
||||||
|
let source_client = params.source_client;
|
||||||
|
let lane = PolkadotMessagesToKusama {
|
||||||
|
message_lane: SubstrateMessageLaneToSubstrate {
|
||||||
|
source_client: source_client.clone(),
|
||||||
|
source_sign: params.source_sign,
|
||||||
|
target_client: params.target_client.clone(),
|
||||||
|
target_sign: params.target_sign,
|
||||||
|
relayer_id_at_source: relayer_id_at_polkadot,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2/3 is reserved for proofs and tx overhead
|
||||||
|
let max_messages_size_in_single_batch = bp_kusama::max_extrinsic_size() / 3;
|
||||||
|
// we don't know exact weights of the Kusama runtime. So to guess weights we'll be using
|
||||||
|
// weights from Rialto and then simply dividing it by x2.
|
||||||
|
let (max_messages_in_single_batch, max_messages_weight_in_single_batch) =
|
||||||
|
select_delivery_transaction_limits::<pallet_bridge_messages::weights::RialtoWeight<rialto_runtime::Runtime>>(
|
||||||
|
bp_kusama::max_extrinsic_weight(),
|
||||||
|
bp_kusama::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
|
||||||
|
);
|
||||||
|
let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = (
|
||||||
|
max_messages_in_single_batch / 2,
|
||||||
|
max_messages_weight_in_single_batch / 2,
|
||||||
|
);
|
||||||
|
|
||||||
|
log::info!(
|
||||||
|
target: "bridge",
|
||||||
|
"Starting Polkadot -> Kusama messages relay.\n\t\
|
||||||
|
Polkadot relayer account id: {:?}\n\t\
|
||||||
|
Max messages in single transaction: {}\n\t\
|
||||||
|
Max messages size in single transaction: {}\n\t\
|
||||||
|
Max messages weight in single transaction: {}\n\t\
|
||||||
|
Relayer mode: {:?}",
|
||||||
|
lane.message_lane.relayer_id_at_source,
|
||||||
|
max_messages_in_single_batch,
|
||||||
|
max_messages_size_in_single_batch,
|
||||||
|
max_messages_weight_in_single_batch,
|
||||||
|
params.relayer_mode,
|
||||||
|
);
|
||||||
|
|
||||||
|
let (metrics_params, metrics_values) = add_standalone_metrics(
|
||||||
|
Some(messages_relay::message_lane_loop::metrics_prefix::<
|
||||||
|
<PolkadotMessagesToKusama as SubstrateMessageLane>::MessageLane,
|
||||||
|
>(&lane_id)),
|
||||||
|
params.metrics_params,
|
||||||
|
source_client.clone(),
|
||||||
|
)?;
|
||||||
|
messages_relay::message_lane_loop::run(
|
||||||
|
messages_relay::message_lane_loop::Params {
|
||||||
|
lane: lane_id,
|
||||||
|
source_tick: Polkadot::AVERAGE_BLOCK_INTERVAL,
|
||||||
|
target_tick: Kusama::AVERAGE_BLOCK_INTERVAL,
|
||||||
|
reconnect_delay: relay_utils::relay_loop::RECONNECT_DELAY,
|
||||||
|
stall_timeout,
|
||||||
|
delivery_params: messages_relay::message_lane_loop::MessageDeliveryParams {
|
||||||
|
max_unrewarded_relayer_entries_at_target: bp_kusama::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
|
||||||
|
max_unconfirmed_nonces_at_target: bp_kusama::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE,
|
||||||
|
max_messages_in_single_batch,
|
||||||
|
max_messages_weight_in_single_batch,
|
||||||
|
max_messages_size_in_single_batch,
|
||||||
|
relayer_mode: params.relayer_mode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PolkadotSourceClient::new(
|
||||||
|
source_client.clone(),
|
||||||
|
lane.clone(),
|
||||||
|
lane_id,
|
||||||
|
params.target_to_source_headers_relay,
|
||||||
|
),
|
||||||
|
KusamaTargetClient::new(
|
||||||
|
params.target_client,
|
||||||
|
lane,
|
||||||
|
lane_id,
|
||||||
|
metrics_values,
|
||||||
|
params.source_to_target_headers_relay,
|
||||||
|
),
|
||||||
|
metrics_params,
|
||||||
|
futures::future::pending(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add standalone metrics for the Polkadot -> Kusama messages loop.
|
||||||
|
pub(crate) fn add_standalone_metrics(
|
||||||
|
metrics_prefix: Option<String>,
|
||||||
|
metrics_params: MetricsParams,
|
||||||
|
source_client: Client<Polkadot>,
|
||||||
|
) -> anyhow::Result<(MetricsParams, StandaloneMessagesMetrics)> {
|
||||||
|
let kusama_to_polkadot_conversion_rate_key =
|
||||||
|
bp_runtime::storage_parameter_key(bp_polkadot::KUSAMA_TO_POLKADOT_CONVERSION_RATE_PARAMETER_NAME).0;
|
||||||
|
|
||||||
|
substrate_relay_helper::messages_lane::add_standalone_metrics::<PolkadotMessagesToKusama>(
|
||||||
|
metrics_prefix,
|
||||||
|
metrics_params,
|
||||||
|
source_client,
|
||||||
|
Some(crate::chains::kusama::TOKEN_ID),
|
||||||
|
Some(crate::chains::polkadot::TOKEN_ID),
|
||||||
|
Some((
|
||||||
|
sp_core::storage::StorageKey(kusama_to_polkadot_conversion_rate_key),
|
||||||
|
// starting relay before this parameter will be set to some value may cause troubles
|
||||||
|
FixedU128::from_inner(FixedU128::DIV),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update Kusama -> Polkadot conversion rate, stored in Polkadot runtime storage.
|
||||||
|
pub(crate) async fn update_kusama_to_polkadot_conversion_rate(
|
||||||
|
client: Client<Polkadot>,
|
||||||
|
signer: <Polkadot as TransactionSignScheme>::AccountKeyPair,
|
||||||
|
updated_rate: f64,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let genesis_hash = *client.genesis_hash();
|
||||||
|
let signer_id = (*signer.public().as_array_ref()).into();
|
||||||
|
client
|
||||||
|
.submit_signed_extrinsic(signer_id, move |_, transaction_nonce| {
|
||||||
|
Bytes(
|
||||||
|
Polkadot::sign_transaction(
|
||||||
|
genesis_hash,
|
||||||
|
&signer,
|
||||||
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
|
UnsignedTransaction::new(
|
||||||
|
relay_polkadot_client::runtime::Call::BridgeKusamaMessages(
|
||||||
|
relay_polkadot_client::runtime::BridgeKusamaMessagesCall::update_pallet_parameter(
|
||||||
|
relay_polkadot_client::runtime::BridgeKusamaMessagesParameter::KusamaToPolkadotConversionRate(
|
||||||
|
sp_runtime::FixedU128::from_float(updated_rate),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
transaction_nonce,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.encode(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map(drop)
|
||||||
|
.map_err(|err| anyhow::format_err!("{:?}", err))
|
||||||
|
}
|
||||||
@@ -104,39 +104,18 @@ impl SubstrateFinalitySyncPipeline for WococoFinalityToRococo {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use frame_support::weights::WeightToFeePolynomial;
|
|
||||||
|
|
||||||
use pallet_bridge_grandpa::weights::WeightInfo;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::chains::kusama_headers_to_polkadot::tests::compute_maximal_balance_decrease_per_day;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn maximal_balance_decrease_per_day_is_sane() {
|
fn maximal_balance_decrease_per_day_is_sane() {
|
||||||
// Rococo/Wococo GRANDPA pallet weights. They're now using Rialto weights => using `RialtoWeight` is justified.
|
// we expect Wococo -> Rococo relay to be running in all-headers mode
|
||||||
//
|
let maximal_balance_decrease =
|
||||||
// Using Rialto runtime this is slightly incorrect, because `DbWeight` of Rococo/Wococo runtime may differ
|
compute_maximal_balance_decrease_per_day::<bp_kusama::Balance, bp_kusama::WeightToFee>(bp_wococo::DAYS);
|
||||||
// from the `DbWeight` of Rialto runtime. But now (and most probably forever) it is the same.
|
|
||||||
type RococoGrandpaPalletWeights = pallet_bridge_grandpa::weights::RialtoWeight<rialto_runtime::Runtime>;
|
|
||||||
|
|
||||||
// The following formula shall not be treated as super-accurate - guard is to protect from mad relays,
|
|
||||||
// not to protect from over-average loses.
|
|
||||||
//
|
|
||||||
// Worst case: we're submitting proof for every source header. Since we submit every header, the number of
|
|
||||||
// headers in ancestry proof is near to 0 (let's round up to 2). And the number of authorities is 1024,
|
|
||||||
// which is (now) larger than on any existing chain => normally there'll be ~1024*2/3+1 commits.
|
|
||||||
const AVG_VOTES_ANCESTRIES_LEN: u32 = 2;
|
|
||||||
const AVG_PRECOMMITS_LEN: u32 = 1024 * 2 / 3 + 1;
|
|
||||||
let number_of_source_headers_per_day: bp_wococo::Balance = bp_wococo::DAYS as _;
|
|
||||||
let single_source_header_submit_call_weight =
|
|
||||||
RococoGrandpaPalletWeights::submit_finality_proof(AVG_VOTES_ANCESTRIES_LEN, AVG_PRECOMMITS_LEN);
|
|
||||||
// for simplicity - add extra weight for base tx fee + fee that is paid for the tx size + adjusted fee
|
|
||||||
let single_source_header_submit_tx_weight = single_source_header_submit_call_weight * 3 / 2;
|
|
||||||
let single_source_header_tx_cost = bp_rococo::WeightToFee::calc(&single_source_header_submit_tx_weight);
|
|
||||||
let maximal_expected_decrease = single_source_header_tx_cost * number_of_source_headers_per_day;
|
|
||||||
assert!(
|
assert!(
|
||||||
MAXIMAL_BALANCE_DECREASE_PER_DAY >= maximal_expected_decrease,
|
MAXIMAL_BALANCE_DECREASE_PER_DAY >= maximal_balance_decrease,
|
||||||
"Maximal expected loss per day {} is larger than hardcoded {}",
|
"Maximal expected loss per day {} is larger than hardcoded {}",
|
||||||
maximal_expected_decrease,
|
maximal_balance_decrease,
|
||||||
MAXIMAL_BALANCE_DECREASE_PER_DAY,
|
MAXIMAL_BALANCE_DECREASE_PER_DAY,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ pub enum FullBridge {
|
|||||||
RialtoToMillau,
|
RialtoToMillau,
|
||||||
RococoToWococo,
|
RococoToWococo,
|
||||||
WococoToRococo,
|
WococoToRococo,
|
||||||
|
KusamaToPolkadot,
|
||||||
|
PolkadotToKusama,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FullBridge {
|
impl FullBridge {
|
||||||
@@ -34,6 +36,8 @@ impl FullBridge {
|
|||||||
Self::RialtoToMillau => RIALTO_TO_MILLAU_INDEX,
|
Self::RialtoToMillau => RIALTO_TO_MILLAU_INDEX,
|
||||||
Self::RococoToWococo => ROCOCO_TO_WOCOCO_INDEX,
|
Self::RococoToWococo => ROCOCO_TO_WOCOCO_INDEX,
|
||||||
Self::WococoToRococo => WOCOCO_TO_ROCOCO_INDEX,
|
Self::WococoToRococo => WOCOCO_TO_ROCOCO_INDEX,
|
||||||
|
Self::KusamaToPolkadot => KUSAMA_TO_POLKADOT_INDEX,
|
||||||
|
Self::PolkadotToKusama => POLKADOT_TO_KUSAMA_INDEX,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,6 +46,8 @@ pub const RIALTO_TO_MILLAU_INDEX: u8 = 0;
|
|||||||
pub const MILLAU_TO_RIALTO_INDEX: u8 = 0;
|
pub const MILLAU_TO_RIALTO_INDEX: u8 = 0;
|
||||||
pub const ROCOCO_TO_WOCOCO_INDEX: u8 = 0;
|
pub const ROCOCO_TO_WOCOCO_INDEX: u8 = 0;
|
||||||
pub const WOCOCO_TO_ROCOCO_INDEX: u8 = 0;
|
pub const WOCOCO_TO_ROCOCO_INDEX: u8 = 0;
|
||||||
|
pub const KUSAMA_TO_POLKADOT_INDEX: u8 = 0;
|
||||||
|
pub const POLKADOT_TO_KUSAMA_INDEX: u8 = 0;
|
||||||
|
|
||||||
/// The macro allows executing bridge-specific code without going fully generic.
|
/// The macro allows executing bridge-specific code without going fully generic.
|
||||||
///
|
///
|
||||||
@@ -138,6 +144,50 @@ macro_rules! select_full_bridge {
|
|||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use relay_wococo_client::runtime::wococo_to_rococo_account_ownership_digest as account_ownership_digest;
|
use relay_wococo_client::runtime::wococo_to_rococo_account_ownership_digest as account_ownership_digest;
|
||||||
|
|
||||||
|
$generic
|
||||||
|
}
|
||||||
|
FullBridge::KusamaToPolkadot => {
|
||||||
|
type Source = relay_kusama_client::Kusama;
|
||||||
|
#[allow(dead_code)]
|
||||||
|
type Target = relay_polkadot_client::Polkadot;
|
||||||
|
|
||||||
|
// Derive-account
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use bp_polkadot::derive_account_from_kusama_id as derive_account;
|
||||||
|
|
||||||
|
// Relay-messages
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use crate::chains::kusama_messages_to_polkadot::run as relay_messages;
|
||||||
|
|
||||||
|
// Send-message / Estimate-fee
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use bp_polkadot::TO_POLKADOT_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD;
|
||||||
|
// Send-message
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use relay_kusama_client::runtime::kusama_to_polkadot_account_ownership_digest as account_ownership_digest;
|
||||||
|
|
||||||
|
$generic
|
||||||
|
}
|
||||||
|
FullBridge::PolkadotToKusama => {
|
||||||
|
type Source = relay_polkadot_client::Polkadot;
|
||||||
|
#[allow(dead_code)]
|
||||||
|
type Target = relay_kusama_client::Kusama;
|
||||||
|
|
||||||
|
// Derive-account
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use bp_kusama::derive_account_from_polkadot_id as derive_account;
|
||||||
|
|
||||||
|
// Relay-messages
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use crate::chains::polkadot_messages_to_kusama::run as relay_messages;
|
||||||
|
|
||||||
|
// Send-message / Estimate-fee
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use bp_kusama::TO_KUSAMA_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD;
|
||||||
|
// Send-message
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use relay_polkadot_client::runtime::polkadot_to_kusama_account_ownership_digest as account_ownership_digest;
|
||||||
|
|
||||||
$generic
|
$generic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ pub enum InitBridgeName {
|
|||||||
WestendToMillau,
|
WestendToMillau,
|
||||||
RococoToWococo,
|
RococoToWococo,
|
||||||
WococoToRococo,
|
WococoToRococo,
|
||||||
|
KusamaToPolkadot,
|
||||||
|
PolkadotToKusama,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! select_bridge {
|
macro_rules! select_bridge {
|
||||||
@@ -127,6 +129,34 @@ macro_rules! select_bridge {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$generic
|
||||||
|
}
|
||||||
|
InitBridgeName::KusamaToPolkadot => {
|
||||||
|
type Source = relay_kusama_client::Kusama;
|
||||||
|
type Target = relay_polkadot_client::Polkadot;
|
||||||
|
|
||||||
|
fn encode_init_bridge(
|
||||||
|
init_data: InitializationData<<Source as ChainBase>::Header>,
|
||||||
|
) -> <Target as Chain>::Call {
|
||||||
|
relay_polkadot_client::runtime::Call::BridgeKusamaGrandpa(
|
||||||
|
relay_polkadot_client::runtime::BridgeKusamaGrandpaCall::initialize(init_data),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
$generic
|
||||||
|
}
|
||||||
|
InitBridgeName::PolkadotToKusama => {
|
||||||
|
type Source = relay_polkadot_client::Polkadot;
|
||||||
|
type Target = relay_kusama_client::Kusama;
|
||||||
|
|
||||||
|
fn encode_init_bridge(
|
||||||
|
init_data: InitializationData<<Source as ChainBase>::Header>,
|
||||||
|
) -> <Target as Chain>::Call {
|
||||||
|
relay_kusama_client::runtime::Call::BridgePolkadotGrandpa(
|
||||||
|
relay_kusama_client::runtime::BridgePolkadotGrandpaCall::initialize(init_data),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
$generic
|
$generic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ pub enum RelayHeadersBridge {
|
|||||||
WestendToMillau,
|
WestendToMillau,
|
||||||
RococoToWococo,
|
RococoToWococo,
|
||||||
WococoToRococo,
|
WococoToRococo,
|
||||||
|
KusamaToPolkadot,
|
||||||
|
PolkadotToKusama,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! select_bridge {
|
macro_rules! select_bridge {
|
||||||
@@ -87,6 +89,20 @@ macro_rules! select_bridge {
|
|||||||
type Target = relay_rococo_client::Rococo;
|
type Target = relay_rococo_client::Rococo;
|
||||||
type Finality = crate::chains::wococo_headers_to_rococo::WococoFinalityToRococo;
|
type Finality = crate::chains::wococo_headers_to_rococo::WococoFinalityToRococo;
|
||||||
|
|
||||||
|
$generic
|
||||||
|
}
|
||||||
|
RelayHeadersBridge::KusamaToPolkadot => {
|
||||||
|
type Source = relay_kusama_client::Kusama;
|
||||||
|
type Target = relay_polkadot_client::Polkadot;
|
||||||
|
type Finality = crate::chains::kusama_headers_to_polkadot::KusamaFinalityToPolkadot;
|
||||||
|
|
||||||
|
$generic
|
||||||
|
}
|
||||||
|
RelayHeadersBridge::PolkadotToKusama => {
|
||||||
|
type Source = relay_polkadot_client::Polkadot;
|
||||||
|
type Target = relay_kusama_client::Kusama;
|
||||||
|
type Finality = crate::chains::polkadot_headers_to_kusama::PolkadotFinalityToKusama;
|
||||||
|
|
||||||
$generic
|
$generic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,10 @@ use futures::{FutureExt, TryFutureExt};
|
|||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
use strum::VariantNames;
|
use strum::VariantNames;
|
||||||
|
|
||||||
use relay_substrate_client::{Chain, Client, TransactionSignScheme};
|
use codec::Encode;
|
||||||
|
use relay_substrate_client::{AccountIdOf, Chain, Client, TransactionSignScheme, UnsignedTransaction};
|
||||||
use relay_utils::metrics::MetricsParams;
|
use relay_utils::metrics::MetricsParams;
|
||||||
|
use sp_core::{Bytes, Pair};
|
||||||
use substrate_relay_helper::messages_lane::{MessagesRelayParams, SubstrateMessageLane};
|
use substrate_relay_helper::messages_lane::{MessagesRelayParams, SubstrateMessageLane};
|
||||||
use substrate_relay_helper::on_demand_headers::OnDemandHeadersRelay;
|
use substrate_relay_helper::on_demand_headers::OnDemandHeadersRelay;
|
||||||
|
|
||||||
@@ -47,6 +49,7 @@ const CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO: f64 = 0.05;
|
|||||||
pub enum RelayHeadersAndMessages {
|
pub enum RelayHeadersAndMessages {
|
||||||
MillauRialto(MillauRialtoHeadersAndMessages),
|
MillauRialto(MillauRialtoHeadersAndMessages),
|
||||||
RococoWococo(RococoWococoHeadersAndMessages),
|
RococoWococo(RococoWococoHeadersAndMessages),
|
||||||
|
KusamaPolkadot(KusamaPolkadotHeadersAndMessages),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parameters that have the same names across all bridges.
|
/// Parameters that have the same names across all bridges.
|
||||||
@@ -57,6 +60,9 @@ pub struct HeadersAndMessagesSharedParams {
|
|||||||
lane: Vec<HexLaneId>,
|
lane: Vec<HexLaneId>,
|
||||||
#[structopt(long, possible_values = RelayerMode::VARIANTS, case_insensitive = true, default_value = "rational")]
|
#[structopt(long, possible_values = RelayerMode::VARIANTS, case_insensitive = true, default_value = "rational")]
|
||||||
relayer_mode: RelayerMode,
|
relayer_mode: RelayerMode,
|
||||||
|
/// Create relayers fund accounts on both chains, if it does not exists yet.
|
||||||
|
#[structopt(long)]
|
||||||
|
create_relayers_fund_accounts: bool,
|
||||||
/// If passed, only mandatory headers (headers that are changing the GRANDPA authorities set) are relayed.
|
/// If passed, only mandatory headers (headers that are changing the GRANDPA authorities set) are relayed.
|
||||||
#[structopt(long)]
|
#[structopt(long)]
|
||||||
only_mandatory_headers: bool,
|
only_mandatory_headers: bool,
|
||||||
@@ -89,7 +95,6 @@ macro_rules! declare_bridge_options {
|
|||||||
right_messages_pallet_owner: [<$chain2 MessagesPalletOwnerSigningParams>],
|
right_messages_pallet_owner: [<$chain2 MessagesPalletOwnerSigningParams>],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unreachable_patterns)]
|
|
||||||
impl From<RelayHeadersAndMessages> for [<$chain1 $chain2 HeadersAndMessages>] {
|
impl From<RelayHeadersAndMessages> for [<$chain1 $chain2 HeadersAndMessages>] {
|
||||||
fn from(relay_params: RelayHeadersAndMessages) -> [<$chain1 $chain2 HeadersAndMessages>] {
|
fn from(relay_params: RelayHeadersAndMessages) -> [<$chain1 $chain2 HeadersAndMessages>] {
|
||||||
match relay_params {
|
match relay_params {
|
||||||
@@ -117,6 +122,9 @@ macro_rules! select_bridge {
|
|||||||
type LeftToRightMessages = crate::chains::millau_messages_to_rialto::MillauMessagesToRialto;
|
type LeftToRightMessages = crate::chains::millau_messages_to_rialto::MillauMessagesToRialto;
|
||||||
type RightToLeftMessages = crate::chains::rialto_messages_to_millau::RialtoMessagesToMillau;
|
type RightToLeftMessages = crate::chains::rialto_messages_to_millau::RialtoMessagesToMillau;
|
||||||
|
|
||||||
|
type LeftAccountIdConverter = bp_millau::AccountIdConverter;
|
||||||
|
type RightAccountIdConverter = bp_rialto::AccountIdConverter;
|
||||||
|
|
||||||
const MAX_MISSING_LEFT_HEADERS_AT_RIGHT: bp_millau::BlockNumber = bp_millau::SESSION_LENGTH;
|
const MAX_MISSING_LEFT_HEADERS_AT_RIGHT: bp_millau::BlockNumber = bp_millau::SESSION_LENGTH;
|
||||||
const MAX_MISSING_RIGHT_HEADERS_AT_LEFT: bp_rialto::BlockNumber = bp_rialto::SESSION_LENGTH;
|
const MAX_MISSING_RIGHT_HEADERS_AT_LEFT: bp_rialto::BlockNumber = bp_rialto::SESSION_LENGTH;
|
||||||
|
|
||||||
@@ -129,6 +137,22 @@ macro_rules! select_bridge {
|
|||||||
update_millau_to_rialto_conversion_rate as update_left_to_right_conversion_rate,
|
update_millau_to_rialto_conversion_rate as update_left_to_right_conversion_rate,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async fn left_create_account(
|
||||||
|
_left_client: Client<Left>,
|
||||||
|
_left_sign: <Left as TransactionSignScheme>::AccountKeyPair,
|
||||||
|
_account_id: AccountIdOf<Left>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
Err(anyhow::format_err!("Account creation is not supported by this bridge"))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn right_create_account(
|
||||||
|
_right_client: Client<Right>,
|
||||||
|
_right_sign: <Right as TransactionSignScheme>::AccountKeyPair,
|
||||||
|
_account_id: AccountIdOf<Right>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
Err(anyhow::format_err!("Account creation is not supported by this bridge"))
|
||||||
|
}
|
||||||
|
|
||||||
$generic
|
$generic
|
||||||
}
|
}
|
||||||
RelayHeadersAndMessages::RococoWococo(_) => {
|
RelayHeadersAndMessages::RococoWococo(_) => {
|
||||||
@@ -143,6 +167,9 @@ macro_rules! select_bridge {
|
|||||||
type LeftToRightMessages = crate::chains::rococo_messages_to_wococo::RococoMessagesToWococo;
|
type LeftToRightMessages = crate::chains::rococo_messages_to_wococo::RococoMessagesToWococo;
|
||||||
type RightToLeftMessages = crate::chains::wococo_messages_to_rococo::WococoMessagesToRococo;
|
type RightToLeftMessages = crate::chains::wococo_messages_to_rococo::WococoMessagesToRococo;
|
||||||
|
|
||||||
|
type LeftAccountIdConverter = bp_rococo::AccountIdConverter;
|
||||||
|
type RightAccountIdConverter = bp_wococo::AccountIdConverter;
|
||||||
|
|
||||||
const MAX_MISSING_LEFT_HEADERS_AT_RIGHT: bp_rococo::BlockNumber = bp_rococo::SESSION_LENGTH;
|
const MAX_MISSING_LEFT_HEADERS_AT_RIGHT: bp_rococo::BlockNumber = bp_rococo::SESSION_LENGTH;
|
||||||
const MAX_MISSING_RIGHT_HEADERS_AT_LEFT: bp_wococo::BlockNumber = bp_wococo::SESSION_LENGTH;
|
const MAX_MISSING_RIGHT_HEADERS_AT_LEFT: bp_wococo::BlockNumber = bp_wococo::SESSION_LENGTH;
|
||||||
|
|
||||||
@@ -169,6 +196,113 @@ macro_rules! select_bridge {
|
|||||||
Err(anyhow::format_err!("Conversion rate is not supported by this bridge"))
|
Err(anyhow::format_err!("Conversion rate is not supported by this bridge"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn left_create_account(
|
||||||
|
_left_client: Client<Left>,
|
||||||
|
_left_sign: <Left as TransactionSignScheme>::AccountKeyPair,
|
||||||
|
_account_id: AccountIdOf<Left>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
Err(anyhow::format_err!("Account creation is not supported by this bridge"))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn right_create_account(
|
||||||
|
_right_client: Client<Right>,
|
||||||
|
_right_sign: <Right as TransactionSignScheme>::AccountKeyPair,
|
||||||
|
_account_id: AccountIdOf<Right>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
Err(anyhow::format_err!("Account creation is not supported by this bridge"))
|
||||||
|
}
|
||||||
|
|
||||||
|
$generic
|
||||||
|
}
|
||||||
|
RelayHeadersAndMessages::KusamaPolkadot(_) => {
|
||||||
|
type Params = KusamaPolkadotHeadersAndMessages;
|
||||||
|
|
||||||
|
type Left = relay_kusama_client::Kusama;
|
||||||
|
type Right = relay_polkadot_client::Polkadot;
|
||||||
|
|
||||||
|
type LeftToRightFinality = crate::chains::kusama_headers_to_polkadot::KusamaFinalityToPolkadot;
|
||||||
|
type RightToLeftFinality = crate::chains::polkadot_headers_to_kusama::PolkadotFinalityToKusama;
|
||||||
|
|
||||||
|
type LeftToRightMessages = crate::chains::kusama_messages_to_polkadot::KusamaMessagesToPolkadot;
|
||||||
|
type RightToLeftMessages = crate::chains::polkadot_messages_to_kusama::PolkadotMessagesToKusama;
|
||||||
|
|
||||||
|
type LeftAccountIdConverter = bp_kusama::AccountIdConverter;
|
||||||
|
type RightAccountIdConverter = bp_polkadot::AccountIdConverter;
|
||||||
|
|
||||||
|
const MAX_MISSING_LEFT_HEADERS_AT_RIGHT: bp_kusama::BlockNumber = bp_kusama::SESSION_LENGTH;
|
||||||
|
const MAX_MISSING_RIGHT_HEADERS_AT_LEFT: bp_polkadot::BlockNumber = bp_polkadot::SESSION_LENGTH;
|
||||||
|
|
||||||
|
use crate::chains::kusama_messages_to_polkadot::{
|
||||||
|
add_standalone_metrics as add_left_to_right_standalone_metrics, run as left_to_right_messages,
|
||||||
|
update_polkadot_to_kusama_conversion_rate as update_right_to_left_conversion_rate,
|
||||||
|
};
|
||||||
|
use crate::chains::polkadot_messages_to_kusama::{
|
||||||
|
add_standalone_metrics as add_right_to_left_standalone_metrics, run as right_to_left_messages,
|
||||||
|
update_kusama_to_polkadot_conversion_rate as update_left_to_right_conversion_rate,
|
||||||
|
};
|
||||||
|
|
||||||
|
async fn left_create_account(
|
||||||
|
left_client: Client<Left>,
|
||||||
|
left_sign: <Left as TransactionSignScheme>::AccountKeyPair,
|
||||||
|
account_id: AccountIdOf<Left>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let left_genesis_hash = *left_client.genesis_hash();
|
||||||
|
left_client
|
||||||
|
.submit_signed_extrinsic(left_sign.public().into(), move |_, transaction_nonce| {
|
||||||
|
Bytes(
|
||||||
|
Left::sign_transaction(
|
||||||
|
left_genesis_hash,
|
||||||
|
&left_sign,
|
||||||
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
|
UnsignedTransaction::new(
|
||||||
|
relay_kusama_client::runtime::Call::Balances(
|
||||||
|
relay_kusama_client::runtime::BalancesCall::transfer(
|
||||||
|
bp_kusama::AccountAddress::Id(account_id),
|
||||||
|
bp_kusama::EXISTENTIAL_DEPOSIT.into(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
transaction_nonce,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.encode(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map(drop)
|
||||||
|
.map_err(|e| anyhow::format_err!("{}", e))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn right_create_account(
|
||||||
|
right_client: Client<Right>,
|
||||||
|
right_sign: <Right as TransactionSignScheme>::AccountKeyPair,
|
||||||
|
account_id: AccountIdOf<Right>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let right_genesis_hash = *right_client.genesis_hash();
|
||||||
|
right_client
|
||||||
|
.submit_signed_extrinsic(right_sign.public().into(), move |_, transaction_nonce| {
|
||||||
|
Bytes(
|
||||||
|
Right::sign_transaction(
|
||||||
|
right_genesis_hash,
|
||||||
|
&right_sign,
|
||||||
|
relay_substrate_client::TransactionEra::immortal(),
|
||||||
|
UnsignedTransaction::new(
|
||||||
|
relay_polkadot_client::runtime::Call::Balances(
|
||||||
|
relay_polkadot_client::runtime::BalancesCall::transfer(
|
||||||
|
bp_polkadot::AccountAddress::Id(account_id),
|
||||||
|
bp_polkadot::EXISTENTIAL_DEPOSIT.into(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
transaction_nonce,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.encode(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map(drop)
|
||||||
|
.map_err(|e| anyhow::format_err!("{}", e))
|
||||||
|
}
|
||||||
|
|
||||||
$generic
|
$generic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,9 +314,12 @@ declare_chain_options!(Millau, millau);
|
|||||||
declare_chain_options!(Rialto, rialto);
|
declare_chain_options!(Rialto, rialto);
|
||||||
declare_chain_options!(Rococo, rococo);
|
declare_chain_options!(Rococo, rococo);
|
||||||
declare_chain_options!(Wococo, wococo);
|
declare_chain_options!(Wococo, wococo);
|
||||||
|
declare_chain_options!(Kusama, kusama);
|
||||||
|
declare_chain_options!(Polkadot, polkadot);
|
||||||
// All supported bridges.
|
// All supported bridges.
|
||||||
declare_bridge_options!(Millau, Rialto);
|
declare_bridge_options!(Millau, Rialto);
|
||||||
declare_bridge_options!(Rococo, Wococo);
|
declare_bridge_options!(Rococo, Wococo);
|
||||||
|
declare_bridge_options!(Kusama, Polkadot);
|
||||||
|
|
||||||
impl RelayHeadersAndMessages {
|
impl RelayHeadersAndMessages {
|
||||||
/// Run the command.
|
/// Run the command.
|
||||||
@@ -275,6 +412,26 @@ impl RelayHeadersAndMessages {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if params.shared.create_relayers_fund_accounts {
|
||||||
|
let relayer_fund_acount_id =
|
||||||
|
pallet_bridge_messages::relayer_fund_account_id::<AccountIdOf<Left>, LeftAccountIdConverter>();
|
||||||
|
let relayers_fund_account_balance =
|
||||||
|
left_client.free_native_balance(relayer_fund_acount_id.clone()).await;
|
||||||
|
if let Err(relay_substrate_client::Error::AccountDoesNotExist) = relayers_fund_account_balance {
|
||||||
|
log::info!(target: "bridge", "Going to create relayers fund account at {}.", Left::NAME);
|
||||||
|
left_create_account(left_client.clone(), left_sign.clone(), relayer_fund_acount_id).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let relayer_fund_acount_id =
|
||||||
|
pallet_bridge_messages::relayer_fund_account_id::<AccountIdOf<Right>, RightAccountIdConverter>();
|
||||||
|
let relayers_fund_account_balance =
|
||||||
|
right_client.free_native_balance(relayer_fund_acount_id.clone()).await;
|
||||||
|
if let Err(relay_substrate_client::Error::AccountDoesNotExist) = relayers_fund_account_balance {
|
||||||
|
log::info!(target: "bridge", "Going to create relayers fund account at {}.", Right::NAME);
|
||||||
|
right_create_account(right_client.clone(), right_sign.clone(), relayer_fund_acount_id).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let left_to_right_on_demand_headers = OnDemandHeadersRelay::new(
|
let left_to_right_on_demand_headers = OnDemandHeadersRelay::new(
|
||||||
left_client.clone(),
|
left_client.clone(),
|
||||||
right_client.clone(),
|
right_client.clone(),
|
||||||
|
|||||||
@@ -6,9 +6,24 @@ edition = "2018"
|
|||||||
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
codec = { package = "parity-scale-codec", version = "2.2.0" }
|
||||||
relay-substrate-client = { path = "../client-substrate" }
|
relay-substrate-client = { path = "../client-substrate" }
|
||||||
relay-utils = { path = "../utils" }
|
relay-utils = { path = "../utils" }
|
||||||
|
|
||||||
# Bridge dependencies
|
# Bridge dependencies
|
||||||
|
|
||||||
|
bp-header-chain = { path = "../../primitives/header-chain" }
|
||||||
bp-kusama = { path = "../../primitives/chain-kusama" }
|
bp-kusama = { path = "../../primitives/chain-kusama" }
|
||||||
|
bp-message-dispatch = { path = "../../primitives/message-dispatch" }
|
||||||
|
bp-messages = { path = "../../primitives/messages" }
|
||||||
|
bp-polkadot = { path = "../../primitives/chain-polkadot" }
|
||||||
|
bp-polkadot-core = { path = "../../primitives/polkadot-core" }
|
||||||
|
bp-runtime = { path = "../../primitives/runtime" }
|
||||||
|
bridge-runtime-common = { path = "../../bin/runtime-common" }
|
||||||
|
pallet-bridge-dispatch = { path = "../../modules/dispatch" }
|
||||||
|
|
||||||
|
# Substrate Dependencies
|
||||||
|
|
||||||
|
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||||
|
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||||
|
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||||
|
|||||||
@@ -16,9 +16,16 @@
|
|||||||
|
|
||||||
//! Types used to connect to the Kusama chain.
|
//! Types used to connect to the Kusama chain.
|
||||||
|
|
||||||
use relay_substrate_client::{Chain, ChainBase};
|
use codec::Encode;
|
||||||
|
use relay_substrate_client::{
|
||||||
|
Chain, ChainBase, ChainWithBalances, TransactionEraOf, TransactionSignScheme, UnsignedTransaction,
|
||||||
|
};
|
||||||
|
use sp_core::{storage::StorageKey, Pair};
|
||||||
|
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
pub mod runtime;
|
||||||
|
|
||||||
/// Kusama header id.
|
/// Kusama header id.
|
||||||
pub type HeaderId = relay_utils::HeaderId<bp_kusama::Hash, bp_kusama::BlockNumber>;
|
pub type HeaderId = relay_utils::HeaderId<bp_kusama::Hash, bp_kusama::BlockNumber>;
|
||||||
|
|
||||||
@@ -45,9 +52,68 @@ impl Chain for Kusama {
|
|||||||
const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_kusama::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE;
|
const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_kusama::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE;
|
||||||
|
|
||||||
type SignedBlock = bp_kusama::SignedBlock;
|
type SignedBlock = bp_kusama::SignedBlock;
|
||||||
type Call = ();
|
type Call = crate::runtime::Call;
|
||||||
type WeightToFee = bp_kusama::WeightToFee;
|
type WeightToFee = bp_kusama::WeightToFee;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ChainWithBalances for Kusama {
|
||||||
|
fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey {
|
||||||
|
StorageKey(bp_kusama::account_info_storage_key(account_id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransactionSignScheme for Kusama {
|
||||||
|
type Chain = Kusama;
|
||||||
|
type AccountKeyPair = sp_core::sr25519::Pair;
|
||||||
|
type SignedTransaction = crate::runtime::UncheckedExtrinsic;
|
||||||
|
|
||||||
|
fn sign_transaction(
|
||||||
|
genesis_hash: <Self::Chain as ChainBase>::Hash,
|
||||||
|
signer: &Self::AccountKeyPair,
|
||||||
|
era: TransactionEraOf<Self::Chain>,
|
||||||
|
unsigned: UnsignedTransaction<Self::Chain>,
|
||||||
|
) -> Self::SignedTransaction {
|
||||||
|
let raw_payload = SignedPayload::new(
|
||||||
|
unsigned.call,
|
||||||
|
bp_kusama::SignedExtensions::new(bp_kusama::VERSION, era, genesis_hash, unsigned.nonce, unsigned.tip),
|
||||||
|
)
|
||||||
|
.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_kusama::UncheckedExtrinsic::new_signed(
|
||||||
|
call,
|
||||||
|
sp_runtime::MultiAddress::Id(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 == bp_kusama::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(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Kusama header type used in headers sync.
|
/// Kusama header type used in headers sync.
|
||||||
pub type SyncHeader = relay_substrate_client::SyncHeader<bp_kusama::Header>;
|
pub type SyncHeader = relay_substrate_client::SyncHeader<bp_kusama::Header>;
|
||||||
|
|
||||||
|
/// Kusama signing params.
|
||||||
|
pub type SigningParams = sp_core::sr25519::Pair;
|
||||||
|
|||||||
@@ -0,0 +1,151 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Types that are specific to the Kusama runtime.
|
||||||
|
|
||||||
|
use bp_messages::{LaneId, UnrewardedRelayersState};
|
||||||
|
use bp_polkadot_core::{AccountAddress, Balance, PolkadotLike};
|
||||||
|
use bp_runtime::Chain;
|
||||||
|
use codec::{Compact, Decode, Encode};
|
||||||
|
use frame_support::weights::Weight;
|
||||||
|
use sp_runtime::FixedU128;
|
||||||
|
|
||||||
|
/// Unchecked Kusama extrinsic.
|
||||||
|
pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic<Call>;
|
||||||
|
|
||||||
|
/// Polkadot account ownership digest from Kusama.
|
||||||
|
///
|
||||||
|
/// The byte vector returned by this function should be signed with a Polkadot account private key.
|
||||||
|
/// This way, the owner of `kusama_account_id` on Kusama proves that the Polkadot account private key
|
||||||
|
/// is also under his control.
|
||||||
|
pub fn kusama_to_polkadot_account_ownership_digest<Call, AccountId, SpecVersion>(
|
||||||
|
polkadot_call: &Call,
|
||||||
|
kusama_account_id: AccountId,
|
||||||
|
polkadot_spec_version: SpecVersion,
|
||||||
|
) -> Vec<u8>
|
||||||
|
where
|
||||||
|
Call: codec::Encode,
|
||||||
|
AccountId: codec::Encode,
|
||||||
|
SpecVersion: codec::Encode,
|
||||||
|
{
|
||||||
|
pallet_bridge_dispatch::account_ownership_digest(
|
||||||
|
polkadot_call,
|
||||||
|
kusama_account_id,
|
||||||
|
polkadot_spec_version,
|
||||||
|
bp_runtime::KUSAMA_CHAIN_ID,
|
||||||
|
bp_runtime::POLKADOT_CHAIN_ID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Kusama Runtime `Call` enum.
|
||||||
|
///
|
||||||
|
/// The enum represents a subset of possible `Call`s we can send to Kusama chain.
|
||||||
|
/// Ideally this code would be auto-generated from metadata, because we want to
|
||||||
|
/// avoid depending directly on the ENTIRE runtime just to get the encoding of `Dispatchable`s.
|
||||||
|
///
|
||||||
|
/// All entries here (like pretty much in the entire file) must be kept in sync with Kusama
|
||||||
|
/// `construct_runtime`, so that we maintain SCALE-compatibility.
|
||||||
|
///
|
||||||
|
/// See: [link](https://github.com/paritytech/polkadot/blob/master/runtime/kusama/src/lib.rs)
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
|
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)]
|
||||||
|
pub enum Call {
|
||||||
|
/// System pallet.
|
||||||
|
#[codec(index = 0)]
|
||||||
|
System(SystemCall),
|
||||||
|
/// Balances pallet.
|
||||||
|
#[codec(index = 4)]
|
||||||
|
Balances(BalancesCall),
|
||||||
|
/// Polkadot bridge pallet.
|
||||||
|
#[codec(index = 110)]
|
||||||
|
BridgePolkadotGrandpa(BridgePolkadotGrandpaCall),
|
||||||
|
/// Polkadot messages pallet.
|
||||||
|
#[codec(index = 111)]
|
||||||
|
BridgePolkadotMessages(BridgePolkadotMessagesCall),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub enum SystemCall {
|
||||||
|
#[codec(index = 1)]
|
||||||
|
remark(Vec<u8>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub enum BalancesCall {
|
||||||
|
#[codec(index = 0)]
|
||||||
|
transfer(AccountAddress, Compact<Balance>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub enum BridgePolkadotGrandpaCall {
|
||||||
|
#[codec(index = 0)]
|
||||||
|
submit_finality_proof(
|
||||||
|
<PolkadotLike as Chain>::Header,
|
||||||
|
bp_header_chain::justification::GrandpaJustification<<PolkadotLike as Chain>::Header>,
|
||||||
|
),
|
||||||
|
#[codec(index = 1)]
|
||||||
|
initialize(bp_header_chain::InitializationData<<PolkadotLike as Chain>::Header>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub enum BridgePolkadotMessagesCall {
|
||||||
|
#[codec(index = 2)]
|
||||||
|
update_pallet_parameter(BridgePolkadotMessagesParameter),
|
||||||
|
#[codec(index = 3)]
|
||||||
|
send_message(
|
||||||
|
LaneId,
|
||||||
|
bp_message_dispatch::MessagePayload<
|
||||||
|
bp_kusama::AccountId,
|
||||||
|
bp_polkadot::AccountId,
|
||||||
|
bp_polkadot::AccountPublic,
|
||||||
|
Vec<u8>,
|
||||||
|
>,
|
||||||
|
bp_kusama::Balance,
|
||||||
|
),
|
||||||
|
#[codec(index = 5)]
|
||||||
|
receive_messages_proof(
|
||||||
|
bp_polkadot::AccountId,
|
||||||
|
bridge_runtime_common::messages::target::FromBridgedChainMessagesProof<bp_polkadot::Hash>,
|
||||||
|
u32,
|
||||||
|
Weight,
|
||||||
|
),
|
||||||
|
#[codec(index = 6)]
|
||||||
|
receive_messages_delivery_proof(
|
||||||
|
bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof<bp_polkadot::Hash>,
|
||||||
|
UnrewardedRelayersState,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)]
|
||||||
|
pub enum BridgePolkadotMessagesParameter {
|
||||||
|
#[codec(index = 0)]
|
||||||
|
PolkadotToKusamaConversionRate(FixedU128),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl sp_runtime::traits::Dispatchable for Call {
|
||||||
|
type Origin = ();
|
||||||
|
type Config = ();
|
||||||
|
type Info = ();
|
||||||
|
type PostInfo = ();
|
||||||
|
|
||||||
|
fn dispatch(self, _origin: Self::Origin) -> sp_runtime::DispatchResultWithInfo<Self::PostInfo> {
|
||||||
|
unimplemented!("The Call is not expected to be dispatched.")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,9 +6,24 @@ edition = "2018"
|
|||||||
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
codec = { package = "parity-scale-codec", version = "2.2.0" }
|
||||||
relay-substrate-client = { path = "../client-substrate" }
|
relay-substrate-client = { path = "../client-substrate" }
|
||||||
relay-utils = { path = "../utils" }
|
relay-utils = { path = "../utils" }
|
||||||
|
|
||||||
# Bridge dependencies
|
# Bridge dependencies
|
||||||
|
|
||||||
|
bp-header-chain = { path = "../../primitives/header-chain" }
|
||||||
|
bp-kusama = { path = "../../primitives/chain-kusama" }
|
||||||
|
bp-message-dispatch = { path = "../../primitives/message-dispatch" }
|
||||||
|
bp-messages = { path = "../../primitives/messages" }
|
||||||
bp-polkadot = { path = "../../primitives/chain-polkadot" }
|
bp-polkadot = { path = "../../primitives/chain-polkadot" }
|
||||||
|
bp-polkadot-core = { path = "../../primitives/polkadot-core" }
|
||||||
|
bp-runtime = { path = "../../primitives/runtime" }
|
||||||
|
bridge-runtime-common = { path = "../../bin/runtime-common" }
|
||||||
|
pallet-bridge-dispatch = { path = "../../modules/dispatch" }
|
||||||
|
|
||||||
|
# Substrate Dependencies
|
||||||
|
|
||||||
|
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||||
|
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||||
|
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||||
|
|||||||
@@ -16,9 +16,16 @@
|
|||||||
|
|
||||||
//! Types used to connect to the Polkadot chain.
|
//! Types used to connect to the Polkadot chain.
|
||||||
|
|
||||||
use relay_substrate_client::{Chain, ChainBase};
|
use codec::Encode;
|
||||||
|
use relay_substrate_client::{
|
||||||
|
Chain, ChainBase, ChainWithBalances, TransactionEraOf, TransactionSignScheme, UnsignedTransaction,
|
||||||
|
};
|
||||||
|
use sp_core::{storage::StorageKey, Pair};
|
||||||
|
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
pub mod runtime;
|
||||||
|
|
||||||
/// Polkadot header id.
|
/// Polkadot header id.
|
||||||
pub type HeaderId = relay_utils::HeaderId<bp_polkadot::Hash, bp_polkadot::BlockNumber>;
|
pub type HeaderId = relay_utils::HeaderId<bp_polkadot::Hash, bp_polkadot::BlockNumber>;
|
||||||
|
|
||||||
@@ -45,9 +52,68 @@ impl Chain for Polkadot {
|
|||||||
const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_polkadot::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE;
|
const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_polkadot::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE;
|
||||||
|
|
||||||
type SignedBlock = bp_polkadot::SignedBlock;
|
type SignedBlock = bp_polkadot::SignedBlock;
|
||||||
type Call = ();
|
type Call = crate::runtime::Call;
|
||||||
type WeightToFee = bp_polkadot::WeightToFee;
|
type WeightToFee = bp_polkadot::WeightToFee;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ChainWithBalances for Polkadot {
|
||||||
|
fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey {
|
||||||
|
StorageKey(bp_polkadot::account_info_storage_key(account_id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransactionSignScheme for Polkadot {
|
||||||
|
type Chain = Polkadot;
|
||||||
|
type AccountKeyPair = sp_core::sr25519::Pair;
|
||||||
|
type SignedTransaction = crate::runtime::UncheckedExtrinsic;
|
||||||
|
|
||||||
|
fn sign_transaction(
|
||||||
|
genesis_hash: <Self::Chain as ChainBase>::Hash,
|
||||||
|
signer: &Self::AccountKeyPair,
|
||||||
|
era: TransactionEraOf<Self::Chain>,
|
||||||
|
unsigned: UnsignedTransaction<Self::Chain>,
|
||||||
|
) -> Self::SignedTransaction {
|
||||||
|
let raw_payload = SignedPayload::new(
|
||||||
|
unsigned.call,
|
||||||
|
bp_polkadot::SignedExtensions::new(bp_polkadot::VERSION, era, genesis_hash, unsigned.nonce, unsigned.tip),
|
||||||
|
)
|
||||||
|
.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_polkadot::UncheckedExtrinsic::new_signed(
|
||||||
|
call,
|
||||||
|
sp_runtime::MultiAddress::Id(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 == bp_polkadot::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(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Polkadot header type used in headers sync.
|
/// Polkadot header type used in headers sync.
|
||||||
pub type SyncHeader = relay_substrate_client::SyncHeader<bp_polkadot::Header>;
|
pub type SyncHeader = relay_substrate_client::SyncHeader<bp_polkadot::Header>;
|
||||||
|
|
||||||
|
/// Polkadot signing params.
|
||||||
|
pub type SigningParams = sp_core::sr25519::Pair;
|
||||||
|
|||||||
@@ -0,0 +1,151 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Types that are specific to the Polkadot runtime.
|
||||||
|
|
||||||
|
use bp_messages::{LaneId, UnrewardedRelayersState};
|
||||||
|
use bp_polkadot_core::{AccountAddress, Balance, PolkadotLike};
|
||||||
|
use bp_runtime::Chain;
|
||||||
|
use codec::{Compact, Decode, Encode};
|
||||||
|
use frame_support::weights::Weight;
|
||||||
|
use sp_runtime::FixedU128;
|
||||||
|
|
||||||
|
/// Unchecked Polkadot extrinsic.
|
||||||
|
pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic<Call>;
|
||||||
|
|
||||||
|
/// Kusama account ownership digest from Polkadot.
|
||||||
|
///
|
||||||
|
/// The byte vector returned by this function should be signed with a Kusama account private key.
|
||||||
|
/// This way, the owner of `kusam_account_id` on Polkadot proves that the Kusama account private key
|
||||||
|
/// is also under his control.
|
||||||
|
pub fn polkadot_to_kusama_account_ownership_digest<Call, AccountId, SpecVersion>(
|
||||||
|
kusama_call: &Call,
|
||||||
|
kusam_account_id: AccountId,
|
||||||
|
kusama_spec_version: SpecVersion,
|
||||||
|
) -> Vec<u8>
|
||||||
|
where
|
||||||
|
Call: codec::Encode,
|
||||||
|
AccountId: codec::Encode,
|
||||||
|
SpecVersion: codec::Encode,
|
||||||
|
{
|
||||||
|
pallet_bridge_dispatch::account_ownership_digest(
|
||||||
|
kusama_call,
|
||||||
|
kusam_account_id,
|
||||||
|
kusama_spec_version,
|
||||||
|
bp_runtime::POLKADOT_CHAIN_ID,
|
||||||
|
bp_runtime::KUSAMA_CHAIN_ID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Polkadot Runtime `Call` enum.
|
||||||
|
///
|
||||||
|
/// The enum represents a subset of possible `Call`s we can send to Polkadot chain.
|
||||||
|
/// Ideally this code would be auto-generated from metadata, because we want to
|
||||||
|
/// avoid depending directly on the ENTIRE runtime just to get the encoding of `Dispatchable`s.
|
||||||
|
///
|
||||||
|
/// All entries here (like pretty much in the entire file) must be kept in sync with Polkadot
|
||||||
|
/// `construct_runtime`, so that we maintain SCALE-compatibility.
|
||||||
|
///
|
||||||
|
/// See: [link](https://github.com/paritytech/kusama/blob/master/runtime/kusam/src/lib.rs)
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
|
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)]
|
||||||
|
pub enum Call {
|
||||||
|
/// System pallet.
|
||||||
|
#[codec(index = 0)]
|
||||||
|
System(SystemCall),
|
||||||
|
/// Balances pallet.
|
||||||
|
#[codec(index = 5)]
|
||||||
|
Balances(BalancesCall),
|
||||||
|
/// Kusama bridge pallet.
|
||||||
|
#[codec(index = 110)]
|
||||||
|
BridgeKusamaGrandpa(BridgeKusamaGrandpaCall),
|
||||||
|
/// Kusama messages pallet.
|
||||||
|
#[codec(index = 111)]
|
||||||
|
BridgeKusamaMessages(BridgeKusamaMessagesCall),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub enum SystemCall {
|
||||||
|
#[codec(index = 1)]
|
||||||
|
remark(Vec<u8>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub enum BalancesCall {
|
||||||
|
#[codec(index = 0)]
|
||||||
|
transfer(AccountAddress, Compact<Balance>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub enum BridgeKusamaGrandpaCall {
|
||||||
|
#[codec(index = 0)]
|
||||||
|
submit_finality_proof(
|
||||||
|
<PolkadotLike as Chain>::Header,
|
||||||
|
bp_header_chain::justification::GrandpaJustification<<PolkadotLike as Chain>::Header>,
|
||||||
|
),
|
||||||
|
#[codec(index = 1)]
|
||||||
|
initialize(bp_header_chain::InitializationData<<PolkadotLike as Chain>::Header>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub enum BridgeKusamaMessagesCall {
|
||||||
|
#[codec(index = 2)]
|
||||||
|
update_pallet_parameter(BridgeKusamaMessagesParameter),
|
||||||
|
#[codec(index = 3)]
|
||||||
|
send_message(
|
||||||
|
LaneId,
|
||||||
|
bp_message_dispatch::MessagePayload<
|
||||||
|
bp_polkadot::AccountId,
|
||||||
|
bp_kusama::AccountId,
|
||||||
|
bp_kusama::AccountPublic,
|
||||||
|
Vec<u8>,
|
||||||
|
>,
|
||||||
|
bp_polkadot::Balance,
|
||||||
|
),
|
||||||
|
#[codec(index = 5)]
|
||||||
|
receive_messages_proof(
|
||||||
|
bp_kusama::AccountId,
|
||||||
|
bridge_runtime_common::messages::target::FromBridgedChainMessagesProof<bp_kusama::Hash>,
|
||||||
|
u32,
|
||||||
|
Weight,
|
||||||
|
),
|
||||||
|
#[codec(index = 6)]
|
||||||
|
receive_messages_delivery_proof(
|
||||||
|
bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof<bp_kusama::Hash>,
|
||||||
|
UnrewardedRelayersState,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)]
|
||||||
|
pub enum BridgeKusamaMessagesParameter {
|
||||||
|
#[codec(index = 0)]
|
||||||
|
KusamaToPolkadotConversionRate(FixedU128),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl sp_runtime::traits::Dispatchable for Call {
|
||||||
|
type Origin = ();
|
||||||
|
type Config = ();
|
||||||
|
type Info = ();
|
||||||
|
type PostInfo = ();
|
||||||
|
|
||||||
|
fn dispatch(self, _origin: Self::Origin) -> sp_runtime::DispatchResultWithInfo<Self::PostInfo> {
|
||||||
|
unimplemented!("The Call is not expected to be dispatched.")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ relay-substrate-client = { path = "../client-substrate" }
|
|||||||
relay-utils = { path = "../utils" }
|
relay-utils = { path = "../utils" }
|
||||||
|
|
||||||
# Bridge dependencies
|
# Bridge dependencies
|
||||||
|
|
||||||
bridge-runtime-common = { path = "../../bin/runtime-common" }
|
bridge-runtime-common = { path = "../../bin/runtime-common" }
|
||||||
bp-header-chain = { path = "../../primitives/header-chain" }
|
bp-header-chain = { path = "../../primitives/header-chain" }
|
||||||
bp-message-dispatch = { path = "../../primitives/message-dispatch" }
|
bp-message-dispatch = { path = "../../primitives/message-dispatch" }
|
||||||
@@ -23,6 +24,7 @@ pallet-bridge-dispatch = { path = "../../modules/dispatch" }
|
|||||||
pallet-bridge-messages = { path = "../../modules/messages" }
|
pallet-bridge-messages = { path = "../../modules/messages" }
|
||||||
|
|
||||||
# Substrate Dependencies
|
# Substrate Dependencies
|
||||||
|
|
||||||
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||||
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||||
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ pub use crate::client::{Client, JustificationsSubscription, OpaqueGrandpaAuthori
|
|||||||
pub use crate::error::{Error, Result};
|
pub use crate::error::{Error, Result};
|
||||||
pub use crate::sync_header::SyncHeader;
|
pub use crate::sync_header::SyncHeader;
|
||||||
pub use bp_runtime::{
|
pub use bp_runtime::{
|
||||||
BalanceOf, BlockNumberOf, Chain as ChainBase, HashOf, HeaderOf, IndexOf, TransactionEra, TransactionEraOf,
|
AccountIdOf, BalanceOf, BlockNumberOf, Chain as ChainBase, HashOf, HeaderOf, IndexOf, TransactionEra,
|
||||||
|
TransactionEraOf,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Header id used by the chain.
|
/// Header id used by the chain.
|
||||||
|
|||||||
Reference in New Issue
Block a user