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:
Svyatoslav Nikolsky
2021-09-21 16:53:37 +03:00
committed by Bastian Köcher
parent 417903f9e7
commit 2db84b74cc
31 changed files with 1936 additions and 68 deletions
+4 -4
View File
@@ -135,10 +135,10 @@ impl Alternative {
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>("RialtoMessagesOwner"),
pallet_bridge_messages::Pallet::<
millau_runtime::Runtime,
millau_runtime::WithRialtoMessagesInstance,
>::relayer_fund_account_id(),
pallet_bridge_messages::relayer_fund_account_id::<
bp_millau::AccountId,
bp_millau::AccountIdConverter,
>(),
derive_account_from_rialto_id(bp_runtime::SourceAccount::Account(
get_account_id_from_seed::<sr25519::Public>("Alice"),
)),
+4 -4
View File
@@ -151,10 +151,10 @@ impl Alternative {
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>("MillauMessagesOwner"),
pallet_bridge_messages::Pallet::<
rialto_runtime::Runtime,
rialto_runtime::WithMillauMessagesInstance,
>::relayer_fund_account_id(),
pallet_bridge_messages::relayer_fund_account_id::<
bp_rialto::AccountId,
bp_rialto::AccountIdConverter,
>(),
derive_account_from_millau_id(bp_runtime::SourceAccount::Account(
get_account_id_from_seed::<sr25519::Public>("Alice"),
)),
+5 -5
View File
@@ -484,7 +484,7 @@ benchmarks_instance_pallet! {
//
// This is base benchmark for all other confirmations delivery benchmarks.
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_balance = T::account_balance(&relayer_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)
// - weight(receive_delivery_proof_for_single_message)`.
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_balance = T::account_balance(&relayer_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)
// - weight(receive_delivery_proof_for_two_messages_by_single_relayer)`.
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_balance = T::account_balance(&relayer1_id);
let relayer2_id: T::AccountId = account("relayer2", 2, SEED);
@@ -811,7 +811,7 @@ benchmarks_instance_pallet! {
.try_into()
.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_balance = T::account_balance(&relayer_id);
T::endow_account(&relayers_fund_id);
@@ -854,7 +854,7 @@ benchmarks_instance_pallet! {
.try_into()
.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 relayers: BTreeMap<T::AccountId, T::OutboundMessageFee> = (1..=i)
.map(|j| {
+17 -16
View File
@@ -64,7 +64,8 @@ use frame_support::{
};
use frame_system::RawOrigin;
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::*};
mod inbound_lane;
@@ -286,16 +287,17 @@ pub mod pallet {
T::MessageDeliveryAndDispatchPayment::pay_delivery_and_dispatch_fee(
&submitter,
&additional_fee,
&Self::relayer_fund_account_id(),
&relayer_fund_account_id::<T::AccountId, T::AccountIdConverter>(),
)
.map_err(|err| {
log::trace!(
target: "runtime::bridge-messages",
"Submitter {:?} can't pay additional fee {:?} for the message {:?}/{:?}: {:?}",
"Submitter {:?} can't pay additional fee {:?} for the message {:?}/{:?} to {:?}: {:?}",
submitter,
additional_fee,
lane_id,
nonce,
relayer_fund_account_id::<T::AccountId, T::AccountIdConverter>(),
err,
);
@@ -604,7 +606,7 @@ pub mod pallet {
// if some new messages have been confirmed, reward relayers
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(
&confirmation_relayer,
relayers_rewards,
@@ -768,17 +770,6 @@ pub mod pallet {
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>
for Pallet<T, I>
where
@@ -894,7 +895,7 @@ fn send_message<T: Config<I>, I: 'static>(
T::MessageDeliveryAndDispatchPayment::pay_delivery_and_dispatch_fee(
&submitter,
&delivery_and_dispatch_fee,
&Pallet::<T, I>::relayer_fund_account_id(),
&relayer_fund_account_id::<T::AccountId, T::AccountIdConverter>(),
)
.map_err(|err| {
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 }
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-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
[features]
default = ["std"]
@@ -30,4 +31,5 @@ std = [
"frame-support/std",
"sp-api/std",
"sp-std/std",
"sp-version/std",
]
@@ -23,12 +23,24 @@
use bp_messages::{LaneId, MessageDetails, MessageNonce, UnrewardedRelayersState};
use frame_support::weights::{WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial};
use sp_std::prelude::*;
use sp_version::RuntimeVersion;
pub use bp_polkadot_core::*;
/// Kusama Chain
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.
pub struct 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.
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.
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.
pub const BEST_FINALIZED_KUSAMA_HEADER_METHOD: &str = "KusamaFinalityApi_best_finalized";
/// 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 }
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-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
[features]
default = ["std"]
@@ -30,4 +31,5 @@ std = [
"frame-support/std",
"sp-api/std",
"sp-std/std",
"sp-version/std",
]
@@ -23,12 +23,24 @@
use bp_messages::{LaneId, MessageDetails, MessageNonce, UnrewardedRelayersState};
use frame_support::weights::{WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial};
use sp_std::prelude::*;
use sp_version::RuntimeVersion;
pub use bp_polkadot_core::*;
/// Polkadot Chain
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.
pub struct 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.
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.
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.
pub const BEST_FINALIZED_POLKADOT_HEADER_METHOD: &str = "PolkadotFinalityApi_best_finalized";
/// Name of the `PolkadotFinalityApi::is_known_header` runtime method.
+4 -1
View File
@@ -217,6 +217,9 @@ pub type AccountPublic = <Signature as Verify>::Signer;
/// Id of account on Polkadot-like chains.
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.
pub type Nonce = u32;
@@ -231,7 +234,7 @@ pub type Balance = u128;
/// Unchecked Extrinsic type.
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.
pub type Address = MultiAddress<AccountId, ()>;
+14 -2
View File
@@ -20,9 +20,9 @@
use codec::Encode;
use frame_support::RuntimeDebug;
use sp_core::hash::H256;
use sp_core::{hash::H256, storage::StorageKey};
use sp_io::hashing::blake2_256;
use sp_std::convert::TryFrom;
use sp_std::{convert::TryFrom, vec::Vec};
pub use chain::{
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())
}
+1
View File
@@ -52,6 +52,7 @@ substrate-relay-helper = { path = "../lib-substrate-relay" }
frame-support = { 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-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.
pub mod kusama_headers_to_polkadot;
pub mod kusama_messages_to_polkadot;
pub mod millau_headers_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_messages_to_millau;
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_messages_to_rococo;
mod kusama;
mod millau;
mod polkadot;
mod rialto;
mod rococo;
mod westend;
@@ -37,9 +43,9 @@ mod wococo;
// Rialto as BTC and Millau as wBTC (only in 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.
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;
@@ -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)]
mod tests {
use frame_support::weights::WeightToFeePolynomial;
use pallet_bridge_grandpa::weights::WeightInfo;
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() {
// Rococo/Wococo GRANDPA pallet weights. They're now using Rialto weights => using `RialtoWeight` is justified.
//
// Using Rialto runtime this is slightly incorrect, because `DbWeight` of Rococo/Wococo runtime may differ
// 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;
// we expect Wococo -> Rococo relay to be running in all-headers mode
let maximal_balance_decrease =
compute_maximal_balance_decrease_per_day::<bp_kusama::Balance, bp_kusama::WeightToFee>(bp_wococo::DAYS);
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_decrease,
maximal_balance_decrease,
MAXIMAL_BALANCE_DECREASE_PER_DAY,
);
}
@@ -24,6 +24,8 @@ pub enum FullBridge {
RialtoToMillau,
RococoToWococo,
WococoToRococo,
KusamaToPolkadot,
PolkadotToKusama,
}
impl FullBridge {
@@ -34,6 +36,8 @@ impl FullBridge {
Self::RialtoToMillau => RIALTO_TO_MILLAU_INDEX,
Self::RococoToWococo => ROCOCO_TO_WOCOCO_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 ROCOCO_TO_WOCOCO_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.
///
@@ -138,6 +144,50 @@ macro_rules! select_full_bridge {
#[allow(unused_imports)]
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
}
}
@@ -46,6 +46,8 @@ pub enum InitBridgeName {
WestendToMillau,
RococoToWococo,
WococoToRococo,
KusamaToPolkadot,
PolkadotToKusama,
}
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
}
}
@@ -49,6 +49,8 @@ pub enum RelayHeadersBridge {
WestendToMillau,
RococoToWococo,
WococoToRococo,
KusamaToPolkadot,
PolkadotToKusama,
}
macro_rules! select_bridge {
@@ -87,6 +89,20 @@ macro_rules! select_bridge {
type Target = relay_rococo_client::Rococo;
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
}
}
@@ -26,8 +26,10 @@ use futures::{FutureExt, TryFutureExt};
use structopt::StructOpt;
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 sp_core::{Bytes, Pair};
use substrate_relay_helper::messages_lane::{MessagesRelayParams, SubstrateMessageLane};
use substrate_relay_helper::on_demand_headers::OnDemandHeadersRelay;
@@ -47,6 +49,7 @@ const CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO: f64 = 0.05;
pub enum RelayHeadersAndMessages {
MillauRialto(MillauRialtoHeadersAndMessages),
RococoWococo(RococoWococoHeadersAndMessages),
KusamaPolkadot(KusamaPolkadotHeadersAndMessages),
}
/// Parameters that have the same names across all bridges.
@@ -57,6 +60,9 @@ pub struct HeadersAndMessagesSharedParams {
lane: Vec<HexLaneId>,
#[structopt(long, possible_values = RelayerMode::VARIANTS, case_insensitive = true, default_value = "rational")]
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.
#[structopt(long)]
only_mandatory_headers: bool,
@@ -89,7 +95,6 @@ macro_rules! declare_bridge_options {
right_messages_pallet_owner: [<$chain2 MessagesPalletOwnerSigningParams>],
}
#[allow(unreachable_patterns)]
impl From<RelayHeadersAndMessages> for [<$chain1 $chain2 HeadersAndMessages>] {
fn from(relay_params: RelayHeadersAndMessages) -> [<$chain1 $chain2 HeadersAndMessages>] {
match relay_params {
@@ -117,6 +122,9 @@ macro_rules! select_bridge {
type LeftToRightMessages = crate::chains::millau_messages_to_rialto::MillauMessagesToRialto;
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_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,
};
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::RococoWococo(_) => {
@@ -143,6 +167,9 @@ macro_rules! select_bridge {
type LeftToRightMessages = crate::chains::rococo_messages_to_wococo::RococoMessagesToWococo;
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_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"))
}
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
}
}
@@ -180,9 +314,12 @@ declare_chain_options!(Millau, millau);
declare_chain_options!(Rialto, rialto);
declare_chain_options!(Rococo, rococo);
declare_chain_options!(Wococo, wococo);
declare_chain_options!(Kusama, kusama);
declare_chain_options!(Polkadot, polkadot);
// All supported bridges.
declare_bridge_options!(Millau, Rialto);
declare_bridge_options!(Rococo, Wococo);
declare_bridge_options!(Kusama, Polkadot);
impl RelayHeadersAndMessages {
/// 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(
left_client.clone(),
right_client.clone(),
+15
View File
@@ -6,9 +6,24 @@ edition = "2018"
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
[dependencies]
codec = { package = "parity-scale-codec", version = "2.2.0" }
relay-substrate-client = { path = "../client-substrate" }
relay-utils = { path = "../utils" }
# 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-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" }
+68 -2
View File
@@ -16,9 +16,16 @@
//! 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;
pub mod runtime;
/// Kusama header id.
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;
type SignedBlock = bp_kusama::SignedBlock;
type Call = ();
type Call = crate::runtime::Call;
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.
pub type SyncHeader = relay_substrate_client::SyncHeader<bp_kusama::Header>;
/// Kusama signing params.
pub type SigningParams = sp_core::sr25519::Pair;
+151
View File
@@ -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.")
}
}
+15
View File
@@ -6,9 +6,24 @@ edition = "2018"
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
[dependencies]
codec = { package = "parity-scale-codec", version = "2.2.0" }
relay-substrate-client = { path = "../client-substrate" }
relay-utils = { path = "../utils" }
# 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-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" }
+68 -2
View File
@@ -16,9 +16,16 @@
//! 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;
pub mod runtime;
/// Polkadot header id.
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;
type SignedBlock = bp_polkadot::SignedBlock;
type Call = ();
type Call = crate::runtime::Call;
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.
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.")
}
}
+2
View File
@@ -11,6 +11,7 @@ relay-substrate-client = { path = "../client-substrate" }
relay-utils = { path = "../utils" }
# Bridge dependencies
bridge-runtime-common = { path = "../../bin/runtime-common" }
bp-header-chain = { path = "../../primitives/header-chain" }
bp-message-dispatch = { path = "../../primitives/message-dispatch" }
@@ -23,6 +24,7 @@ pallet-bridge-dispatch = { path = "../../modules/dispatch" }
pallet-bridge-messages = { path = "../../modules/messages" }
# 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" }
+2 -1
View File
@@ -38,7 +38,8 @@ pub use crate::client::{Client, JustificationsSubscription, OpaqueGrandpaAuthori
pub use crate::error::{Error, Result};
pub use crate::sync_header::SyncHeader;
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.