Squashed 'bridges/' changes from b2099c5..23dda62 (#3369)

23dda62 Rococo <> Wococo messages relay (#1030)
bcde21d Update the wasm builder to substrate master (#1029)
a8318ce Make target signer optional when sending message. (#1018)
f8602e1 Fix insufficient balance when send message. (#1020)
d95c0a7 greedy relayer don't need message dispatch to be prepaid if dispatch is supposed to be paid at the target chain (#1016)
ad5876f Update types. (#1027)
116cbbc CI: fix starting the pipeline (#1022)
7e0fadd Add temporary `canary` job (#1019)
6787091 Update types to contain dispatch_fee_payment (#1017)
03f79ad Allow Root to assume SourceAccount. (#1011)
372d019 Return dispatch_fee_payment from message details RPC (#1014)
604eb1c Relay basic single-bit message dispatch results back to the source chain (#935)
bf52fff Use plain source_queue view when selecting nonces for delivery (#1010)
fc5cf7d pay dispatch fee at target chain (#911)
1e35477 Bump Substrate to `286d7ce` (#1006)
7ad07b3 Add --only-mandatory-headers mode (#1004)
5351dc9 Messages relayer operating mode (#995)
9bc29a7 Rococo <> Wococo relayer balance guard (#998)
bc17341 rename messages_dispatch_weight -> message_details (#996)
95be244 Bump Rococo and Wococo spec versions (#999)
c35567b Move ChainWithBalances::NativeBalance -> Chain::Balance (#990)
1bfece1 Fix some nits (#988)
334ea0f Increase pause before starting relays again (#989)
7fb8248 Fix clippy in test code (#993)
d60ae50 fix clippy issues (#991)
75ca813 Make sure GRANDPA shares state with RPC. (#987)
da2a38a Bump Substrate (#986)
5a9862f Update submit finality proof weight formula (#981)
69df513 Flag for rejecting all outbound messages (#982)
14d0506 Add script to setup bench machine. (#984)
e74e8ab Move CI from GitHub Actions to GitLab (#814)
c5ca5dd Custom justification verification (#979)
643f10d Always run on-demand headers relay in complex relay (#975)
a35b0ef Add JSON type definitions for Rococo<>Wococo bridge (#977)
0eb83f2 Update cargo.deny (#980)
e1d1f4c Bump Rococo/Wococo spec_version (#976)
deac90d increase pause before starting relays (#974)
68d6d79 Revert to use InspectCmd, bump substrate `6bef4f4` (#966)
66e1508 Avoid hashing headers twice in verify_justification (#973)
a31844f Bump `environmental` dependency (#972)
2a4c29a in auto-relays keep trying to connect to nodes until connection is established (#971)
0e767b3 removed stray file (#969)
b9545dc Serve multiple lanes with single complex relay instance (#964)
73419f4 Correct type error (#968)
bac256f Start finality relay spec-version guards for Rococo <> Wococo finality relays (#965)
bfd7037 pass source and target chain ids to account_ownership_proof (#963)
8436073 Upstream changes from Polkadot repo (#961)
e58d851 Increase account endowment amount (#960)

git-subtree-dir: bridges
git-subtree-split: 23dda6248236b27f20d76cbedc30e189cc6f736c
This commit is contained in:
Svyatoslav Nikolsky
2021-06-25 16:45:02 +03:00
committed by GitHub
parent 022e8bc11c
commit feefc34567
167 changed files with 7023 additions and 3239 deletions
@@ -23,7 +23,7 @@ use crate::cli::{
};
use bp_message_dispatch::{CallOrigin, MessagePayload};
use codec::Decode;
use frame_support::weights::{GetDispatchInfo, Weight};
use frame_support::weights::{DispatchInfo, GetDispatchInfo, Weight};
use relay_millau_client::Millau;
use sp_version::RuntimeVersion;
@@ -62,6 +62,10 @@ impl CliEncodeCall for Millau {
},
})
}
fn get_dispatch_info(call: &millau_runtime::Call) -> anyhow::Result<DispatchInfo> {
Ok(call.get_dispatch_info())
}
}
impl CliChain for Millau {
@@ -23,7 +23,7 @@ use crate::messages_source::SubstrateMessagesSource;
use crate::messages_target::SubstrateMessagesTarget;
use bp_messages::MessageNonce;
use bp_runtime::{MILLAU_BRIDGE_INSTANCE, RIALTO_BRIDGE_INSTANCE};
use bp_runtime::{MILLAU_CHAIN_ID, RIALTO_CHAIN_ID};
use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof;
use codec::Encode;
use frame_support::dispatch::GetDispatchInfo;
@@ -42,8 +42,7 @@ pub type MillauMessagesToRialto =
SubstrateMessageLaneToSubstrate<Millau, MillauSigningParams, Rialto, RialtoSigningParams>;
impl SubstrateMessageLane for MillauMessagesToRialto {
const OUTBOUND_LANE_MESSAGES_DISPATCH_WEIGHT_METHOD: &'static str =
bp_rialto::TO_RIALTO_MESSAGES_DISPATCH_WEIGHT_METHOD;
const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str = bp_rialto::TO_RIALTO_MESSAGE_DETAILS_METHOD;
const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str =
bp_rialto::TO_RIALTO_LATEST_GENERATED_NONCE_METHOD;
const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = bp_rialto::TO_RIALTO_LATEST_RECEIVED_NONCE_METHOD;
@@ -59,7 +58,7 @@ impl SubstrateMessageLane for MillauMessagesToRialto {
type SourceChain = Millau;
type TargetChain = Rialto;
fn source_transactions_author(&self) -> bp_rialto::AccountId {
fn source_transactions_author(&self) -> bp_millau::AccountId {
(*self.source_sign.public().as_array_ref()).into()
}
@@ -127,20 +126,12 @@ impl SubstrateMessageLane for MillauMessagesToRialto {
}
/// Millau node as messages source.
type MillauSourceClient = SubstrateMessagesSource<
Millau,
MillauMessagesToRialto,
millau_runtime::Runtime,
millau_runtime::WithRialtoMessagesInstance,
>;
type MillauSourceClient =
SubstrateMessagesSource<Millau, MillauMessagesToRialto, millau_runtime::WithRialtoMessagesInstance>;
/// Rialto node as messages target.
type RialtoTargetClient = SubstrateMessagesTarget<
Rialto,
MillauMessagesToRialto,
rialto_runtime::Runtime,
rialto_runtime::WithMillauMessagesInstance,
>;
type RialtoTargetClient =
SubstrateMessagesTarget<Rialto, MillauMessagesToRialto, rialto_runtime::WithMillauMessagesInstance>;
/// Run Millau-to-Rialto messages sync.
pub async fn run(
@@ -160,7 +151,7 @@ pub async fn run(
};
// 2/3 is reserved for proofs and tx overhead
let max_messages_size_in_single_batch = bp_rialto::max_extrinsic_size() as usize / 3;
let max_messages_size_in_single_batch = bp_rialto::max_extrinsic_size() / 3;
// TODO: use Millau weights after https://github.com/paritytech/parity-bridges-common/issues/390
let (max_messages_in_single_batch, max_messages_weight_in_single_batch) =
select_delivery_transaction_limits::<pallet_bridge_messages::weights::RialtoWeight<millau_runtime::Runtime>>(
@@ -194,20 +185,21 @@ pub async fn run(
max_messages_in_single_batch,
max_messages_weight_in_single_batch,
max_messages_size_in_single_batch,
relayer_mode: messages_relay::message_lane_loop::RelayerMode::Altruistic,
},
},
MillauSourceClient::new(
source_client.clone(),
lane.clone(),
lane_id,
RIALTO_BRIDGE_INSTANCE,
RIALTO_CHAIN_ID,
params.target_to_source_headers_relay,
),
RialtoTargetClient::new(
params.target_client,
lane,
lane_id,
MILLAU_BRIDGE_INSTANCE,
MILLAU_CHAIN_ID,
params.source_to_target_headers_relay,
),
relay_utils::relay_metrics(
@@ -21,8 +21,10 @@ pub mod millau_messages_to_rialto;
pub mod rialto_headers_to_millau;
pub mod rialto_messages_to_millau;
pub mod rococo_headers_to_wococo;
pub mod rococo_messages_to_wococo;
pub mod westend_headers_to_millau;
pub mod wococo_headers_to_rococo;
pub mod wococo_messages_to_rococo;
mod millau;
mod rialto;
@@ -86,7 +88,7 @@ mod tests {
let millau_public: bp_millau::AccountSigner = millau_sign.public().into();
let millau_account_id: bp_millau::AccountId = millau_public.into_account();
let digest = millau_runtime::rialto_account_ownership_digest(
let digest = millau_runtime::millau_to_rialto_account_ownership_digest(
&call,
millau_account_id,
rialto_runtime::VERSION.spec_version,
@@ -107,7 +109,7 @@ mod tests {
let rialto_public: bp_rialto::AccountSigner = rialto_sign.public().into();
let rialto_account_id: bp_rialto::AccountId = rialto_public.into_account();
let digest = rialto_runtime::millau_account_ownership_digest(
let digest = rialto_runtime::rialto_to_millau_account_ownership_digest(
&call,
rialto_account_id,
millau_runtime::VERSION.spec_version,
@@ -271,7 +273,10 @@ mod rococo_tests {
votes_ancestries: vec![],
};
let actual = bp_rococo::BridgeGrandpaWococoCall::submit_finality_proof(header.clone(), justification.clone());
let actual = relay_rococo_client::runtime::BridgeGrandpaWococoCall::submit_finality_proof(
header.clone(),
justification.clone(),
);
let expected = millau_runtime::BridgeGrandpaRialtoCall::<millau_runtime::Runtime>::submit_finality_proof(
header,
justification,
@@ -23,7 +23,7 @@ use crate::cli::{
};
use bp_message_dispatch::{CallOrigin, MessagePayload};
use codec::Decode;
use frame_support::weights::{GetDispatchInfo, Weight};
use frame_support::weights::{DispatchInfo, GetDispatchInfo, Weight};
use relay_rialto_client::Rialto;
use sp_version::RuntimeVersion;
@@ -60,6 +60,10 @@ impl CliEncodeCall for Rialto {
},
})
}
fn get_dispatch_info(call: &rialto_runtime::Call) -> anyhow::Result<DispatchInfo> {
Ok(call.get_dispatch_info())
}
}
impl CliChain for Rialto {
@@ -23,7 +23,7 @@ use crate::messages_source::SubstrateMessagesSource;
use crate::messages_target::SubstrateMessagesTarget;
use bp_messages::MessageNonce;
use bp_runtime::{MILLAU_BRIDGE_INSTANCE, RIALTO_BRIDGE_INSTANCE};
use bp_runtime::{MILLAU_CHAIN_ID, RIALTO_CHAIN_ID};
use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof;
use codec::Encode;
use frame_support::dispatch::GetDispatchInfo;
@@ -42,8 +42,7 @@ pub type RialtoMessagesToMillau =
SubstrateMessageLaneToSubstrate<Rialto, RialtoSigningParams, Millau, MillauSigningParams>;
impl SubstrateMessageLane for RialtoMessagesToMillau {
const OUTBOUND_LANE_MESSAGES_DISPATCH_WEIGHT_METHOD: &'static str =
bp_millau::TO_MILLAU_MESSAGES_DISPATCH_WEIGHT_METHOD;
const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str = bp_millau::TO_MILLAU_MESSAGE_DETAILS_METHOD;
const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str =
bp_millau::TO_MILLAU_LATEST_GENERATED_NONCE_METHOD;
const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = bp_millau::TO_MILLAU_LATEST_RECEIVED_NONCE_METHOD;
@@ -86,7 +85,7 @@ impl SubstrateMessageLane for RialtoMessagesToMillau {
Bytes(transaction.encode())
}
fn target_transactions_author(&self) -> bp_rialto::AccountId {
fn target_transactions_author(&self) -> bp_millau::AccountId {
(*self.target_sign.public().as_array_ref()).into()
}
@@ -127,20 +126,12 @@ impl SubstrateMessageLane for RialtoMessagesToMillau {
}
/// Rialto node as messages source.
type RialtoSourceClient = SubstrateMessagesSource<
Rialto,
RialtoMessagesToMillau,
rialto_runtime::Runtime,
rialto_runtime::WithMillauMessagesInstance,
>;
type RialtoSourceClient =
SubstrateMessagesSource<Rialto, RialtoMessagesToMillau, rialto_runtime::WithMillauMessagesInstance>;
/// Millau node as messages target.
type MillauTargetClient = SubstrateMessagesTarget<
Millau,
RialtoMessagesToMillau,
millau_runtime::Runtime,
millau_runtime::WithRialtoMessagesInstance,
>;
type MillauTargetClient =
SubstrateMessagesTarget<Millau, RialtoMessagesToMillau, millau_runtime::WithRialtoMessagesInstance>;
/// Run Rialto-to-Millau messages sync.
pub async fn run(
@@ -160,7 +151,7 @@ pub async fn run(
};
// 2/3 is reserved for proofs and tx overhead
let max_messages_size_in_single_batch = bp_millau::max_extrinsic_size() as usize / 3;
let max_messages_size_in_single_batch = bp_millau::max_extrinsic_size() / 3;
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_millau::max_extrinsic_weight(),
@@ -193,20 +184,21 @@ pub async fn run(
max_messages_in_single_batch,
max_messages_weight_in_single_batch,
max_messages_size_in_single_batch,
relayer_mode: messages_relay::message_lane_loop::RelayerMode::Altruistic,
},
},
RialtoSourceClient::new(
source_client.clone(),
lane.clone(),
lane_id,
MILLAU_BRIDGE_INSTANCE,
MILLAU_CHAIN_ID,
params.target_to_source_headers_relay,
),
MillauTargetClient::new(
params.target_client,
lane,
lane_id,
RIALTO_BRIDGE_INSTANCE,
RIALTO_CHAIN_ID,
params.source_to_target_headers_relay,
),
relay_utils::relay_metrics(
@@ -14,11 +14,70 @@
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
use crate::cli::{encode_message, CliChain};
use frame_support::weights::Weight;
use codec::Decode;
use frame_support::weights::{DispatchClass, DispatchInfo, Pays, Weight};
use relay_rococo_client::Rococo;
use sp_version::RuntimeVersion;
use crate::cli::{
bridge,
encode_call::{Call, CliEncodeCall},
encode_message, CliChain,
};
/// Weight of the `system::remark` call at Rococo.
///
/// This weight is larger (x2) than actual weight at current Rooco 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;
impl CliEncodeCall for Rococo {
fn max_extrinsic_size() -> u32 {
bp_rococo::max_extrinsic_size()
}
fn encode_call(call: &Call) -> anyhow::Result<Self::Call> {
Ok(match call {
Call::Remark { remark_payload, .. } => {
relay_rococo_client::runtime::Call::System(relay_rococo_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::ROCOCO_TO_WOCOCO_INDEX => {
let payload = Decode::decode(&mut &*payload.0)?;
relay_rococo_client::runtime::Call::BridgeMessagesWococo(
relay_rococo_client::runtime::BridgeMessagesWococoCall::send_message(lane.0, payload, fee.0),
)
}
_ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}",
bridge_instance_index
),
},
_ => anyhow::bail!("The call is not supported"),
})
}
fn get_dispatch_info(call: &relay_rococo_client::runtime::Call) -> anyhow::Result<DispatchInfo> {
match *call {
relay_rococo_client::runtime::Call::System(relay_rococo_client::runtime::SystemCall::remark(_)) => {
Ok(DispatchInfo {
weight: SYSTEM_REMARK_CALL_WEIGHT,
class: DispatchClass::Normal,
pays_fee: Pays::Yes,
})
}
_ => anyhow::bail!("Unsupported Rococo call: {:?}", call),
}
}
}
impl CliChain for Rococo {
const RUNTIME_VERSION: RuntimeVersion = bp_rococo::VERSION;
@@ -30,7 +89,7 @@ impl CliChain for Rococo {
}
fn max_extrinsic_weight() -> Weight {
0
bp_wococo::max_extrinsic_weight()
}
fn encode_message(_message: encode_message::MessagePayload) -> Result<Self::MessagePayload, String> {
@@ -16,6 +16,7 @@
//! Rococo-to-Wococo headers sync entrypoint.
use crate::chains::wococo_headers_to_rococo::MAXIMAL_BALANCE_DECREASE_PER_DAY;
use crate::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
use bp_header_chain::justification::GrandpaJustification;
@@ -38,6 +39,18 @@ impl SubstrateFinalitySyncPipeline for RococoFinalityToWococo {
crate::chains::add_polkadot_kusama_price_metrics::<Self>(params)
}
fn start_relay_guards(&self) {
relay_substrate_client::guard::abort_on_spec_version_change(
self.target_client.clone(),
bp_wococo::VERSION.spec_version,
);
relay_substrate_client::guard::abort_when_account_balance_decreased(
self.target_client.clone(),
self.transactions_author(),
MAXIMAL_BALANCE_DECREASE_PER_DAY,
);
}
fn transactions_author(&self) -> bp_wococo::AccountId {
(*self.target_sign.public().as_array_ref()).into()
}
@@ -48,10 +61,9 @@ impl SubstrateFinalitySyncPipeline for RococoFinalityToWococo {
header: RococoSyncHeader,
proof: GrandpaJustification<bp_rococo::Header>,
) -> Bytes {
let call = bp_wococo::Call::BridgeGrandpaRococo(bp_wococo::BridgeGrandpaRococoCall::submit_finality_proof(
header.into_inner(),
proof,
));
let call = relay_wococo_client::runtime::Call::BridgeGrandpaRococo(
relay_wococo_client::runtime::BridgeGrandpaRococoCall::submit_finality_proof(header.into_inner(), proof),
);
let genesis_hash = *self.target_client.genesis_hash();
let transaction = Wococo::sign_transaction(genesis_hash, &self.target_sign, transaction_nonce, call);
@@ -0,0 +1,227 @@
// 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/>.
//! Rococo-to-Wococo messages sync entrypoint.
use crate::messages_lane::{
select_delivery_transaction_limits, MessagesRelayParams, SubstrateMessageLane, SubstrateMessageLaneToSubstrate,
};
use crate::messages_source::SubstrateMessagesSource;
use crate::messages_target::SubstrateMessagesTarget;
use bp_messages::MessageNonce;
use bp_runtime::{ROCOCO_CHAIN_ID, WOCOCO_CHAIN_ID};
use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof;
use codec::Encode;
use messages_relay::message_lane::MessageLane;
use relay_rococo_client::{HeaderId as RococoHeaderId, Rococo, SigningParams as RococoSigningParams};
use relay_substrate_client::{metrics::StorageProofOverheadMetric, Chain, TransactionSignScheme};
use relay_wococo_client::{HeaderId as WococoHeaderId, SigningParams as WococoSigningParams, Wococo};
use sp_core::{Bytes, Pair};
use std::{ops::RangeInclusive, time::Duration};
/// Rococo-to-Wococo message lane.
pub type RococoMessagesToWococo =
SubstrateMessageLaneToSubstrate<Rococo, RococoSigningParams, Wococo, WococoSigningParams>;
impl SubstrateMessageLane for RococoMessagesToWococo {
const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str = bp_wococo::TO_WOCOCO_MESSAGE_DETAILS_METHOD;
const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str =
bp_wococo::TO_WOCOCO_LATEST_GENERATED_NONCE_METHOD;
const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = bp_wococo::TO_WOCOCO_LATEST_RECEIVED_NONCE_METHOD;
const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = bp_rococo::FROM_ROCOCO_LATEST_RECEIVED_NONCE_METHOD;
const INBOUND_LANE_LATEST_CONFIRMED_NONCE_METHOD: &'static str =
bp_rococo::FROM_ROCOCO_LATEST_CONFIRMED_NONCE_METHOD;
const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str = bp_rococo::FROM_ROCOCO_UNREWARDED_RELAYERS_STATE;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_rococo::BEST_FINALIZED_ROCOCO_HEADER_METHOD;
const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = bp_wococo::BEST_FINALIZED_WOCOCO_HEADER_METHOD;
type SourceChain = Rococo;
type TargetChain = Wococo;
fn source_transactions_author(&self) -> bp_rococo::AccountId {
(*self.source_sign.public().as_array_ref()).into()
}
fn make_messages_receiving_proof_transaction(
&self,
transaction_nonce: <Rococo as Chain>::Index,
_generated_at_block: WococoHeaderId,
proof: <Self as MessageLane>::MessagesReceivingProof,
) -> Bytes {
let (relayers_state, proof) = proof;
let call = relay_rococo_client::runtime::Call::BridgeMessagesWococo(
relay_rococo_client::runtime::BridgeMessagesWococoCall::receive_messages_delivery_proof(
proof,
relayers_state,
),
);
let genesis_hash = *self.source_client.genesis_hash();
let transaction = Rococo::sign_transaction(genesis_hash, &self.source_sign, transaction_nonce, call);
log::trace!(
target: "bridge",
"Prepared Wococo -> Rococo confirmation transaction. Weight: <unknown>/{}, size: {}/{}",
bp_rococo::max_extrinsic_weight(),
transaction.encode().len(),
bp_rococo::max_extrinsic_size(),
);
Bytes(transaction.encode())
}
fn target_transactions_author(&self) -> bp_wococo::AccountId {
(*self.target_sign.public().as_array_ref()).into()
}
fn make_messages_delivery_transaction(
&self,
transaction_nonce: <Wococo as Chain>::Index,
_generated_at_header: RococoHeaderId,
_nonces: RangeInclusive<MessageNonce>,
proof: <Self 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_wococo_client::runtime::Call::BridgeMessagesRococo(
relay_wococo_client::runtime::BridgeMessagesRococoCall::receive_messages_proof(
self.relayer_id_at_source.clone(),
proof,
messages_count as _,
dispatch_weight,
),
);
let genesis_hash = *self.target_client.genesis_hash();
let transaction = Wococo::sign_transaction(genesis_hash, &self.target_sign, transaction_nonce, call);
log::trace!(
target: "bridge",
"Prepared Rococo -> Wococo delivery transaction. Weight: <unknown>/{}, size: {}/{}",
bp_wococo::max_extrinsic_weight(),
transaction.encode().len(),
bp_wococo::max_extrinsic_size(),
);
Bytes(transaction.encode())
}
}
/// Rococo node as messages source.
type RococoSourceClient =
SubstrateMessagesSource<Rococo, RococoMessagesToWococo, relay_rococo_client::runtime::WithWococoMessagesInstance>;
/// Wococo node as messages target.
type WococoTargetClient =
SubstrateMessagesTarget<Wococo, RococoMessagesToWococo, relay_wococo_client::runtime::WithRococoMessagesInstance>;
/// Run Rococo-to-Wococo messages sync.
pub async fn run(
params: MessagesRelayParams<Rococo, RococoSigningParams, Wococo, WococoSigningParams>,
) -> Result<(), String> {
let stall_timeout = Duration::from_secs(5 * 60);
let relayer_id_at_rococo = (*params.source_sign.public().as_array_ref()).into();
let lane_id = params.lane_id;
let source_client = params.source_client;
let lane = RococoMessagesToWococo {
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_rococo,
};
// 2/3 is reserved for proofs and tx overhead
let max_messages_size_in_single_batch = bp_wococo::max_extrinsic_size() / 3;
// we don't know exact weights of the Wococo 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_wococo::max_extrinsic_weight(),
bp_wococo::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 Rococo -> Wococo messages relay.\n\t\
Rococo 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: {}",
lane.relayer_id_at_source,
max_messages_in_single_batch,
max_messages_size_in_single_batch,
max_messages_weight_in_single_batch,
);
messages_relay::message_lane_loop::run(
messages_relay::message_lane_loop::Params {
lane: lane_id,
source_tick: Rococo::AVERAGE_BLOCK_INTERVAL,
target_tick: Wococo::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_wococo::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
max_unconfirmed_nonces_at_target: bp_wococo::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: messages_relay::message_lane_loop::RelayerMode::Altruistic,
},
},
RococoSourceClient::new(
source_client.clone(),
lane.clone(),
lane_id,
WOCOCO_CHAIN_ID,
params.target_to_source_headers_relay,
),
WococoTargetClient::new(
params.target_client,
lane,
lane_id,
ROCOCO_CHAIN_ID,
params.source_to_target_headers_relay,
),
relay_utils::relay_metrics(
Some(messages_relay::message_lane_loop::metrics_prefix::<
RococoMessagesToWococo,
>(&lane_id)),
params.metrics_params,
)
.standalone_metric(|registry, prefix| {
StorageProofOverheadMetric::new(
registry,
prefix,
source_client.clone(),
"rococo_storage_proof_overhead".into(),
"Rococo storage proof overhead".into(),
)
})?
.into_params(),
futures::future::pending(),
)
.await
}
@@ -1,60 +0,0 @@
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! Westend-to-Rococo headers sync entrypoint.
use crate::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
use bp_header_chain::justification::GrandpaJustification;
use codec::Encode;
use relay_rococo_client::{Rococo, SigningParams as RococoSigningParams};
use relay_substrate_client::{Chain, TransactionSignScheme};
use relay_utils::metrics::MetricsParams;
use relay_westend_client::{SyncHeader as WestendSyncHeader, Westend};
use sp_core::{Bytes, Pair};
/// Westend-to-Rococo finality sync pipeline.
pub(crate) type WestendFinalityToRococo = SubstrateFinalityToSubstrate<Westend, Rococo, RococoSigningParams>;
impl SubstrateFinalitySyncPipeline for WestendFinalityToRococo {
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_westend::BEST_FINALIZED_WESTEND_HEADER_METHOD;
type TargetChain = Rococo;
fn customize_metrics(params: MetricsParams) -> anyhow::Result<MetricsParams> {
crate::chains::add_polkadot_kusama_price_metrics::<Self>(params)
}
fn transactions_author(&self) -> bp_rococo::AccountId {
(*self.target_sign.public().as_array_ref()).into()
}
fn make_submit_finality_proof_transaction(
&self,
transaction_nonce: <Rococo as Chain>::Index,
header: WestendSyncHeader,
proof: GrandpaJustification<bp_westend::Header>,
) -> Bytes {
let call = bp_rococo::Call::BridgeGrandpaWestend(bp_rococo::BridgeGrandpaCall::submit_finality_proof(
header.into_inner(),
proof,
));
let genesis_hash = *self.target_client.genesis_hash();
let transaction = Rococo::sign_transaction(genesis_hash, &self.target_sign, transaction_nonce, call);
Bytes(transaction.encode())
}
}
@@ -14,11 +14,64 @@
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
use crate::cli::{encode_message, CliChain};
use frame_support::weights::Weight;
use codec::Decode;
use frame_support::weights::{DispatchClass, DispatchInfo, Pays, Weight};
use relay_wococo_client::Wococo;
use sp_version::RuntimeVersion;
use crate::cli::{
bridge,
encode_call::{Call, CliEncodeCall},
encode_message, CliChain,
};
impl CliEncodeCall for Wococo {
fn max_extrinsic_size() -> u32 {
bp_wococo::max_extrinsic_size()
}
fn encode_call(call: &Call) -> anyhow::Result<Self::Call> {
Ok(match call {
Call::Remark { remark_payload, .. } => {
relay_wococo_client::runtime::Call::System(relay_wococo_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::WOCOCO_TO_ROCOCO_INDEX => {
let payload = Decode::decode(&mut &*payload.0)?;
relay_wococo_client::runtime::Call::BridgeMessagesRococo(
relay_wococo_client::runtime::BridgeMessagesRococoCall::send_message(lane.0, payload, fee.0),
)
}
_ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}",
bridge_instance_index
),
},
_ => anyhow::bail!("The call is not supported"),
})
}
fn get_dispatch_info(call: &relay_wococo_client::runtime::Call) -> anyhow::Result<DispatchInfo> {
match *call {
relay_wococo_client::runtime::Call::System(relay_wococo_client::runtime::SystemCall::remark(_)) => {
Ok(DispatchInfo {
weight: crate::chains::rococo::SYSTEM_REMARK_CALL_WEIGHT,
class: DispatchClass::Normal,
pays_fee: Pays::Yes,
})
}
_ => anyhow::bail!("Unsupported Rococo call: {:?}", call),
}
}
}
impl CliChain for Wococo {
const RUNTIME_VERSION: RuntimeVersion = bp_wococo::VERSION;
@@ -30,7 +83,7 @@ impl CliChain for Wococo {
}
fn max_extrinsic_weight() -> Weight {
0
bp_wococo::max_extrinsic_weight()
}
fn encode_message(_message: encode_message::MessagePayload) -> Result<Self::MessagePayload, String> {
@@ -26,6 +26,13 @@ use relay_utils::metrics::MetricsParams;
use relay_wococo_client::{SyncHeader as WococoSyncHeader, Wococo};
use sp_core::{Bytes, Pair};
/// Maximal saturating difference between `balance(now)` and `balance(now-24h)` to treat
/// relay as gone wild.
///
/// See `maximal_balance_decrease_per_day_is_sane` test for details.
/// Note that this is in plancks, so this corresponds to `1500 UNITS`.
pub(crate) const MAXIMAL_BALANCE_DECREASE_PER_DAY: bp_rococo::Balance = 1_500_000_000_000_000;
/// Wococo-to-Rococo finality sync pipeline.
pub(crate) type WococoFinalityToRococo = SubstrateFinalityToSubstrate<Wococo, Rococo, RococoSigningParams>;
@@ -38,6 +45,18 @@ impl SubstrateFinalitySyncPipeline for WococoFinalityToRococo {
crate::chains::add_polkadot_kusama_price_metrics::<Self>(params)
}
fn start_relay_guards(&self) {
relay_substrate_client::guard::abort_on_spec_version_change(
self.target_client.clone(),
bp_rococo::VERSION.spec_version,
);
relay_substrate_client::guard::abort_when_account_balance_decreased(
self.target_client.clone(),
self.transactions_author(),
MAXIMAL_BALANCE_DECREASE_PER_DAY,
);
}
fn transactions_author(&self) -> bp_rococo::AccountId {
(*self.target_sign.public().as_array_ref()).into()
}
@@ -48,13 +67,50 @@ impl SubstrateFinalitySyncPipeline for WococoFinalityToRococo {
header: WococoSyncHeader,
proof: GrandpaJustification<bp_wococo::Header>,
) -> Bytes {
let call = bp_rococo::Call::BridgeGrandpaWococo(bp_rococo::BridgeGrandpaWococoCall::submit_finality_proof(
header.into_inner(),
proof,
));
let call = relay_rococo_client::runtime::Call::BridgeGrandpaWococo(
relay_rococo_client::runtime::BridgeGrandpaWococoCall::submit_finality_proof(header.into_inner(), proof),
);
let genesis_hash = *self.target_client.genesis_hash();
let transaction = Rococo::sign_transaction(genesis_hash, &self.target_sign, transaction_nonce, call);
Bytes(transaction.encode())
}
}
#[cfg(test)]
mod tests {
use super::*;
use frame_support::weights::WeightToFeePolynomial;
use pallet_bridge_grandpa::weights::WeightInfo;
#[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;
assert!(
MAXIMAL_BALANCE_DECREASE_PER_DAY >= maximal_expected_decrease,
"Maximal expected loss per day {} is larger than hardcoded {}",
maximal_expected_decrease,
MAXIMAL_BALANCE_DECREASE_PER_DAY,
);
}
}
@@ -0,0 +1,227 @@
// 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/>.
//! Wococo-to-Rococo messages sync entrypoint.
use crate::messages_lane::{
select_delivery_transaction_limits, MessagesRelayParams, SubstrateMessageLane, SubstrateMessageLaneToSubstrate,
};
use crate::messages_source::SubstrateMessagesSource;
use crate::messages_target::SubstrateMessagesTarget;
use bp_messages::MessageNonce;
use bp_runtime::{ROCOCO_CHAIN_ID, WOCOCO_CHAIN_ID};
use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof;
use codec::Encode;
use messages_relay::message_lane::MessageLane;
use relay_rococo_client::{HeaderId as RococoHeaderId, Rococo, SigningParams as RococoSigningParams};
use relay_substrate_client::{metrics::StorageProofOverheadMetric, Chain, TransactionSignScheme};
use relay_wococo_client::{HeaderId as WococoHeaderId, SigningParams as WococoSigningParams, Wococo};
use sp_core::{Bytes, Pair};
use std::{ops::RangeInclusive, time::Duration};
/// Wococo-to-Rococo message lane.
pub type WococoMessagesToRococo =
SubstrateMessageLaneToSubstrate<Wococo, WococoSigningParams, Rococo, RococoSigningParams>;
impl SubstrateMessageLane for WococoMessagesToRococo {
const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str = bp_rococo::TO_ROCOCO_MESSAGE_DETAILS_METHOD;
const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str =
bp_rococo::TO_ROCOCO_LATEST_GENERATED_NONCE_METHOD;
const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = bp_rococo::TO_ROCOCO_LATEST_RECEIVED_NONCE_METHOD;
const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = bp_wococo::FROM_WOCOCO_LATEST_RECEIVED_NONCE_METHOD;
const INBOUND_LANE_LATEST_CONFIRMED_NONCE_METHOD: &'static str =
bp_wococo::FROM_WOCOCO_LATEST_CONFIRMED_NONCE_METHOD;
const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str = bp_wococo::FROM_WOCOCO_UNREWARDED_RELAYERS_STATE;
const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = bp_wococo::BEST_FINALIZED_WOCOCO_HEADER_METHOD;
const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = bp_rococo::BEST_FINALIZED_ROCOCO_HEADER_METHOD;
type SourceChain = Wococo;
type TargetChain = Rococo;
fn source_transactions_author(&self) -> bp_wococo::AccountId {
(*self.source_sign.public().as_array_ref()).into()
}
fn make_messages_receiving_proof_transaction(
&self,
transaction_nonce: <Wococo as Chain>::Index,
_generated_at_block: RococoHeaderId,
proof: <Self as MessageLane>::MessagesReceivingProof,
) -> Bytes {
let (relayers_state, proof) = proof;
let call = relay_wococo_client::runtime::Call::BridgeMessagesRococo(
relay_wococo_client::runtime::BridgeMessagesRococoCall::receive_messages_delivery_proof(
proof,
relayers_state,
),
);
let genesis_hash = *self.source_client.genesis_hash();
let transaction = Wococo::sign_transaction(genesis_hash, &self.source_sign, transaction_nonce, call);
log::trace!(
target: "bridge",
"Prepared Rococo -> Wococo confirmation transaction. Weight: <unknown>/{}, size: {}/{}",
bp_wococo::max_extrinsic_weight(),
transaction.encode().len(),
bp_wococo::max_extrinsic_size(),
);
Bytes(transaction.encode())
}
fn target_transactions_author(&self) -> bp_rococo::AccountId {
(*self.target_sign.public().as_array_ref()).into()
}
fn make_messages_delivery_transaction(
&self,
transaction_nonce: <Rococo as Chain>::Index,
_generated_at_header: WococoHeaderId,
_nonces: RangeInclusive<MessageNonce>,
proof: <Self 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_rococo_client::runtime::Call::BridgeMessagesWococo(
relay_rococo_client::runtime::BridgeMessagesWococoCall::receive_messages_proof(
self.relayer_id_at_source.clone(),
proof,
messages_count as _,
dispatch_weight,
),
);
let genesis_hash = *self.target_client.genesis_hash();
let transaction = Rococo::sign_transaction(genesis_hash, &self.target_sign, transaction_nonce, call);
log::trace!(
target: "bridge",
"Prepared Wococo -> Rococo delivery transaction. Weight: <unknown>/{}, size: {}/{}",
bp_rococo::max_extrinsic_weight(),
transaction.encode().len(),
bp_rococo::max_extrinsic_size(),
);
Bytes(transaction.encode())
}
}
/// Wococo node as messages source.
type WococoSourceClient =
SubstrateMessagesSource<Wococo, WococoMessagesToRococo, relay_wococo_client::runtime::WithRococoMessagesInstance>;
/// Rococo node as messages target.
type RococoTargetClient =
SubstrateMessagesTarget<Rococo, WococoMessagesToRococo, relay_rococo_client::runtime::WithWococoMessagesInstance>;
/// Run Wococo-to-Rococo messages sync.
pub async fn run(
params: MessagesRelayParams<Wococo, WococoSigningParams, Rococo, RococoSigningParams>,
) -> Result<(), String> {
let stall_timeout = Duration::from_secs(5 * 60);
let relayer_id_at_wococo = (*params.source_sign.public().as_array_ref()).into();
let lane_id = params.lane_id;
let source_client = params.source_client;
let lane = WococoMessagesToRococo {
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_wococo,
};
// 2/3 is reserved for proofs and tx overhead
let max_messages_size_in_single_batch = bp_rococo::max_extrinsic_size() / 3;
// we don't know exact weights of the Rococo 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_rococo::max_extrinsic_weight(),
bp_rococo::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 Wococo -> Rococo messages relay.\n\t\
Wococo 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: {}",
lane.relayer_id_at_source,
max_messages_in_single_batch,
max_messages_size_in_single_batch,
max_messages_weight_in_single_batch,
);
messages_relay::message_lane_loop::run(
messages_relay::message_lane_loop::Params {
lane: lane_id,
source_tick: Wococo::AVERAGE_BLOCK_INTERVAL,
target_tick: Rococo::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_rococo::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
max_unconfirmed_nonces_at_target: bp_rococo::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: messages_relay::message_lane_loop::RelayerMode::Altruistic,
},
},
WococoSourceClient::new(
source_client.clone(),
lane.clone(),
lane_id,
ROCOCO_CHAIN_ID,
params.target_to_source_headers_relay,
),
RococoTargetClient::new(
params.target_client,
lane,
lane_id,
WOCOCO_CHAIN_ID,
params.source_to_target_headers_relay,
),
relay_utils::relay_metrics(
Some(messages_relay::message_lane_loop::metrics_prefix::<
WococoMessagesToRococo,
>(&lane_id)),
params.metrics_params,
)
.standalone_metric(|registry, prefix| {
StorageProofOverheadMetric::new(
registry,
prefix,
source_client.clone(),
"wococo_storage_proof_overhead".into(),
"Wococo storage proof overhead".into(),
)
})?
.into_params(),
futures::future::pending(),
)
.await
}
@@ -22,6 +22,8 @@ arg_enum! {
pub enum FullBridge {
MillauToRialto,
RialtoToMillau,
RococoToWococo,
WococoToRococo,
}
}
@@ -31,12 +33,16 @@ impl FullBridge {
match self {
Self::MillauToRialto => MILLAU_TO_RIALTO_INDEX,
Self::RialtoToMillau => RIALTO_TO_MILLAU_INDEX,
Self::RococoToWococo => ROCOCO_TO_WOCOCO_INDEX,
Self::WococoToRococo => WOCOCO_TO_ROCOCO_INDEX,
}
}
}
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;
/// The macro allows executing bridge-specific code without going fully generic.
///
@@ -64,7 +70,7 @@ macro_rules! select_full_bridge {
use bp_rialto::TO_RIALTO_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD;
// Send-message
#[allow(unused_imports)]
use millau_runtime::rialto_account_ownership_digest as account_ownership_digest;
use millau_runtime::millau_to_rialto_account_ownership_digest as account_ownership_digest;
$generic
}
@@ -87,7 +93,51 @@ macro_rules! select_full_bridge {
// Send-message
#[allow(unused_imports)]
use rialto_runtime::millau_account_ownership_digest as account_ownership_digest;
use rialto_runtime::rialto_to_millau_account_ownership_digest as account_ownership_digest;
$generic
}
FullBridge::RococoToWococo => {
type Source = relay_rococo_client::Rococo;
#[allow(dead_code)]
type Target = relay_wococo_client::Wococo;
// Derive-account
#[allow(unused_imports)]
use bp_wococo::derive_account_from_rococo_id as derive_account;
// Relay-messages
#[allow(unused_imports)]
use crate::chains::rococo_messages_to_wococo::run as relay_messages;
// Send-message / Estimate-fee
#[allow(unused_imports)]
use bp_wococo::TO_WOCOCO_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD;
// Send-message
#[allow(unused_imports)]
use relay_rococo_client::runtime::rococo_to_wococo_account_ownership_digest as account_ownership_digest;
$generic
}
FullBridge::WococoToRococo => {
type Source = relay_wococo_client::Wococo;
#[allow(dead_code)]
type Target = relay_rococo_client::Rococo;
// Derive-account
#[allow(unused_imports)]
use bp_rococo::derive_account_from_wococo_id as derive_account;
// Relay-messages
#[allow(unused_imports)]
use crate::chains::wococo_messages_to_rococo::run as relay_messages;
// Send-message / Estimate-fee
#[allow(unused_imports)]
use bp_rococo::TO_ROCOCO_ESTIMATE_MESSAGE_FEE_METHOD as ESTIMATE_MESSAGE_FEE_METHOD;
// Send-message
#[allow(unused_imports)]
use relay_wococo_client::runtime::wococo_to_rococo_account_ownership_digest as account_ownership_digest;
$generic
}
@@ -17,7 +17,7 @@
use crate::cli::bridge::FullBridge;
use crate::cli::{AccountId, Balance, CliChain, ExplicitOrMaximal, HexBytes, HexLaneId};
use crate::select_full_bridge;
use frame_support::dispatch::GetDispatchInfo;
use frame_support::weights::DispatchInfo;
use relay_substrate_client::Chain;
use structopt::StructOpt;
@@ -85,6 +85,9 @@ pub trait CliEncodeCall: Chain {
/// Encode a CLI call.
fn encode_call(call: &Call) -> anyhow::Result<Self::Call>;
/// Get dispatch info for the call.
fn get_dispatch_info(call: &Self::Call) -> anyhow::Result<DispatchInfo>;
}
impl EncodeCall {
@@ -96,7 +99,7 @@ impl EncodeCall {
let encoded = HexBytes::encode(&call);
log::info!(target: "bridge", "Generated {} call: {:#?}", Source::NAME, call);
log::info!(target: "bridge", "Weight of {} call: {}", Source::NAME, call.get_dispatch_info().weight);
log::info!(target: "bridge", "Weight of {} call: {}", Source::NAME, Source::get_dispatch_info(&call)?.weight);
log::info!(target: "bridge", "Encoded {} call: {:?}", Source::NAME, encoded);
Ok(encoded)
@@ -129,7 +132,7 @@ pub(crate) fn preprocess_call<Source: CliEncodeCall + CliChain, Target: CliEncod
} => {
if remark_payload.is_none() {
*remark_payload = Some(HexBytes(generate_remark_payload(
&remark_size,
remark_size,
compute_maximal_message_arguments_size(Source::max_extrinsic_size(), Target::max_extrinsic_size()),
)));
}
@@ -72,7 +72,7 @@ mod tests {
#[test]
fn should_encode_raw_message() {
// given
let msg = "01000000e88514000000000002d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d3c040130000000000000000000000000";
let msg = "01000000e88514000000000002d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d003c040130000000000000000000000000";
let encode_message = EncodeMessage::from_iter(vec!["encode-message", "MillauToRialto", "raw", msg]);
// when
@@ -101,6 +101,6 @@ mod tests {
let hex = encode_message.encode().unwrap();
// then
assert_eq!(format!("{:?}", hex), "0x01000000e88514000000000002d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d3c040130000000000000000000000000");
assert_eq!(format!("{:?}", hex), "0x01000000b0d60f000000000002d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d003c040130000000000000000000000000");
}
}
@@ -18,7 +18,7 @@ use crate::cli::bridge::FullBridge;
use crate::cli::{Balance, CliChain, HexBytes, HexLaneId, SourceConnectionParams};
use crate::select_full_bridge;
use codec::{Decode, Encode};
use relay_substrate_client::{Chain, ChainWithBalances};
use relay_substrate_client::Chain;
use structopt::StructOpt;
/// Estimate Delivery & Dispatch Fee command.
@@ -52,7 +52,7 @@ impl EstimateFee {
let lane = lane.into();
let payload = Source::encode_message(payload).map_err(|e| anyhow::format_err!("{:?}", e))?;
let fee: <Source as ChainWithBalances>::NativeBalance =
let fee: <Source as Chain>::Balance =
estimate_message_delivery_and_dispatch_fee(&source_client, ESTIMATE_MESSAGE_FEE_METHOD, lane, payload)
.await?;
@@ -109,7 +109,9 @@ macro_rules! select_bridge {
fn encode_init_bridge(
init_data: InitializationData<<Source as ChainBase>::Header>,
) -> <Target as Chain>::Call {
bp_wococo::Call::BridgeGrandpaRococo(bp_wococo::BridgeGrandpaRococoCall::initialize(init_data))
relay_wococo_client::runtime::Call::BridgeGrandpaRococo(
relay_wococo_client::runtime::BridgeGrandpaRococoCall::initialize(init_data),
)
}
$generic
@@ -121,7 +123,9 @@ macro_rules! select_bridge {
fn encode_init_bridge(
init_data: InitializationData<<Source as ChainBase>::Header>,
) -> <Target as Chain>::Call {
bp_rococo::Call::BridgeGrandpaWococo(bp_rococo::BridgeGrandpaWococoCall::initialize(init_data))
relay_rococo_client::runtime::Call::BridgeGrandpaWococo(
relay_rococo_client::runtime::BridgeGrandpaWococoCall::initialize(init_data),
)
}
$generic
@@ -406,7 +406,7 @@ macro_rules! declare_chain_options {
port: self.[<$chain_prefix _port>],
secure: self.[<$chain_prefix _secure>],
})
.await?
.await
)
}
}
@@ -24,6 +24,9 @@ pub struct RelayHeaders {
/// A bridge instance to relay headers for.
#[structopt(possible_values = &RelayHeadersBridge::variants(), case_insensitive = true)]
bridge: RelayHeadersBridge,
/// If passed, only mandatory headers (headers that are changing the GRANDPA authorities set) are relayed.
#[structopt(long)]
only_mandatory_headers: bool,
#[structopt(flatten)]
source: SourceConnectionParams,
#[structopt(flatten)]
@@ -97,12 +100,14 @@ impl RelayHeaders {
let target_client = self.target.to_client::<Target>().await?;
let target_sign = self.target_sign.to_keypair::<Target>()?;
let metrics_params = Finality::customize_metrics(self.prometheus_params.into())?;
let finality = Finality::new(target_client.clone(), target_sign);
finality.start_relay_guards();
crate::finality_pipeline::run(
Finality::new(target_client.clone(), target_sign),
finality,
source_client,
target_client,
false,
self.only_mandatory_headers,
metrics_params,
)
.await
@@ -35,14 +35,15 @@ use structopt::StructOpt;
#[derive(StructOpt)]
pub enum RelayHeadersAndMessages {
MillauRialto(MillauRialtoHeadersAndMessages),
RococoWococo(RococoWococoHeadersAndMessages),
}
/// Parameters that have the same names across all bridges.
#[derive(StructOpt)]
pub struct HeadersAndMessagesSharedParams {
/// Hex-encoded lane id that should be served by the relay. Defaults to `00000000`.
/// Hex-encoded lane identifiers that should be served by the complex relay.
#[structopt(long, default_value = "00000000")]
lane: HexLaneId,
lane: Vec<HexLaneId>,
#[structopt(flatten)]
prometheus_params: PrometheusParams,
}
@@ -102,6 +103,26 @@ macro_rules! select_bridge {
use crate::chains::millau_messages_to_rialto::run as left_to_right_messages;
use crate::chains::rialto_messages_to_millau::run as right_to_left_messages;
$generic
}
RelayHeadersAndMessages::RococoWococo(_) => {
type Params = RococoWococoHeadersAndMessages;
type Left = relay_rococo_client::Rococo;
type Right = relay_wococo_client::Wococo;
type LeftToRightFinality = crate::chains::rococo_headers_to_wococo::RococoFinalityToWococo;
type RightToLeftFinality = crate::chains::wococo_headers_to_rococo::WococoFinalityToRococo;
type LeftToRightMessages = crate::chains::rococo_messages_to_wococo::RococoMessagesToWococo;
type RightToLeftMessages = crate::chains::wococo_messages_to_rococo::WococoMessagesToRococo;
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;
use crate::chains::rococo_messages_to_wococo::run as left_to_right_messages;
use crate::chains::wococo_messages_to_rococo::run as right_to_left_messages;
$generic
}
}
@@ -111,8 +132,11 @@ macro_rules! select_bridge {
// All supported chains.
declare_chain_options!(Millau, millau);
declare_chain_options!(Rialto, rialto);
declare_chain_options!(Rococo, rococo);
declare_chain_options!(Wococo, wococo);
// All supported bridges.
declare_bridge_options!(Millau, Rialto);
declare_bridge_options!(Rococo, Wococo);
impl RelayHeadersAndMessages {
/// Run the command.
@@ -125,7 +149,7 @@ impl RelayHeadersAndMessages {
let right_client = params.right.to_client::<Right>().await?;
let right_sign = params.right_sign.to_keypair::<Right>()?;
let lane = params.shared.lane.into();
let lanes = params.shared.lane;
let metrics_params: MetricsParams = params.shared.prometheus_params.into();
let metrics_params = relay_utils::relay_metrics(None, metrics_params).into_params();
@@ -143,46 +167,49 @@ impl RelayHeadersAndMessages {
MAX_MISSING_RIGHT_HEADERS_AT_LEFT,
);
let left_to_right_messages = left_to_right_messages(MessagesRelayParams {
source_client: left_client.clone(),
source_sign: left_sign.clone(),
target_client: right_client.clone(),
target_sign: right_sign.clone(),
source_to_target_headers_relay: Some(left_to_right_on_demand_headers.clone()),
target_to_source_headers_relay: Some(right_to_left_on_demand_headers.clone()),
lane_id: lane,
metrics_params: metrics_params
.clone()
.disable()
.metrics_prefix(messages_relay::message_lane_loop::metrics_prefix::<LeftToRightMessages>(&lane)),
})
.map_err(|e| anyhow::format_err!("{}", e))
.boxed();
let right_to_left_messages = right_to_left_messages(MessagesRelayParams {
source_client: right_client,
source_sign: right_sign,
target_client: left_client.clone(),
target_sign: left_sign.clone(),
source_to_target_headers_relay: Some(right_to_left_on_demand_headers),
target_to_source_headers_relay: Some(left_to_right_on_demand_headers),
lane_id: lane,
metrics_params: metrics_params
.clone()
.disable()
.metrics_prefix(messages_relay::message_lane_loop::metrics_prefix::<RightToLeftMessages>(&lane)),
})
.map_err(|e| anyhow::format_err!("{}", e))
.boxed();
// Need 2x capacity since we consider both directions for each lane
let mut message_relays = Vec::with_capacity(lanes.len() * 2);
for lane in lanes {
let lane = lane.into();
let left_to_right_messages = left_to_right_messages(MessagesRelayParams {
source_client: left_client.clone(),
source_sign: left_sign.clone(),
target_client: right_client.clone(),
target_sign: right_sign.clone(),
source_to_target_headers_relay: Some(left_to_right_on_demand_headers.clone()),
target_to_source_headers_relay: Some(right_to_left_on_demand_headers.clone()),
lane_id: lane,
metrics_params: metrics_params.clone().disable().metrics_prefix(
messages_relay::message_lane_loop::metrics_prefix::<LeftToRightMessages>(&lane),
),
})
.map_err(|e| anyhow::format_err!("{}", e))
.boxed();
let right_to_left_messages = right_to_left_messages(MessagesRelayParams {
source_client: right_client.clone(),
source_sign: right_sign.clone(),
target_client: left_client.clone(),
target_sign: left_sign.clone(),
source_to_target_headers_relay: Some(right_to_left_on_demand_headers.clone()),
target_to_source_headers_relay: Some(left_to_right_on_demand_headers.clone()),
lane_id: lane,
metrics_params: metrics_params.clone().disable().metrics_prefix(
messages_relay::message_lane_loop::metrics_prefix::<RightToLeftMessages>(&lane),
),
})
.map_err(|e| anyhow::format_err!("{}", e))
.boxed();
message_relays.push(left_to_right_messages);
message_relays.push(right_to_left_messages);
}
relay_utils::relay_metrics(None, metrics_params)
.expose()
.await
.map_err(|e| anyhow::format_err!("{}", e))?;
futures::future::select(left_to_right_messages, right_to_left_messages)
.await
.factor_first()
.0
futures::future::select_all(message_relays).await.0
})
}
}
@@ -22,8 +22,9 @@ use crate::cli::{
TargetSigningParams,
};
use bp_message_dispatch::{CallOrigin, MessagePayload};
use bp_runtime::messages::DispatchFeePayment;
use codec::Encode;
use frame_support::{dispatch::GetDispatchInfo, weights::Weight};
use frame_support::weights::Weight;
use relay_substrate_client::{Chain, TransactionSignScheme};
use sp_core::{Bytes, Pair};
use sp_runtime::{traits::IdentifyAccount, AccountId32, MultiSignature, MultiSigner};
@@ -40,9 +41,12 @@ pub struct SendMessage {
source: SourceConnectionParams,
#[structopt(flatten)]
source_sign: SourceSigningParams,
// TODO [#885] Move TargetSign to origins
#[structopt(flatten)]
target_sign: TargetSigningParams,
/// The SURI of secret key to use when transactions are submitted to the Target node.
#[structopt(long, required_if("origin", "Target"))]
target_signer: Option<String>,
/// The password for the SURI of secret key to use when transactions are submitted to the Target node.
#[structopt(long)]
target_signer_password: Option<String>,
/// Hex-encoded lane id. Defaults to `00000000`.
#[structopt(long, default_value = "00000000")]
lane: HexLaneId,
@@ -68,7 +72,8 @@ impl SendMessage {
crate::select_full_bridge!(self.bridge, {
let SendMessage {
source_sign,
target_sign,
target_signer,
target_signer_password,
ref mut message,
dispatch_weight,
origin,
@@ -77,15 +82,14 @@ impl SendMessage {
} = self;
let source_sign = source_sign.to_keypair::<Source>()?;
let target_sign = target_sign.to_keypair::<Target>()?;
encode_call::preprocess_call::<Source, Target>(message, bridge.bridge_instance_index());
let target_call = Target::encode_call(&message)?;
let target_call = Target::encode_call(message)?;
let payload = {
let target_call_weight = prepare_call_dispatch_weight(
dispatch_weight,
ExplicitOrMaximal::Explicit(target_call.get_dispatch_info().weight),
ExplicitOrMaximal::Explicit(Target::get_dispatch_info(&target_call)?.weight),
compute_maximal_message_dispatch_weight(Target::max_extrinsic_weight()),
);
let source_sender_public: MultiSigner = source_sign.public().into();
@@ -97,6 +101,13 @@ impl SendMessage {
match origin {
Origins::Source => CallOrigin::SourceAccount(source_account_id),
Origins::Target => {
let target_sign = TargetSigningParams {
target_signer: target_signer.clone().ok_or_else(|| {
anyhow::format_err!("The argument target_signer is not available")
})?,
target_signer_password: target_signer_password.clone(),
};
let target_sign = target_sign.to_keypair::<Target>()?;
let digest = account_ownership_digest(
&target_call,
source_account_id.clone(),
@@ -130,11 +141,12 @@ impl SendMessage {
let fee = match self.fee {
Some(fee) => fee,
None => Balance(
estimate_message_delivery_and_dispatch_fee::<
<Source as relay_substrate_client::ChainWithBalances>::NativeBalance,
_,
_,
>(&source_client, ESTIMATE_MESSAGE_FEE_METHOD, lane, payload.clone())
estimate_message_delivery_and_dispatch_fee::<<Source as Chain>::Balance, _, _>(
&source_client,
ESTIMATE_MESSAGE_FEE_METHOD,
lane,
payload.clone(),
)
.await? as _,
),
};
@@ -210,6 +222,7 @@ where
spec_version,
weight,
origin,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
call: HexBytes::encode(call),
};
@@ -221,12 +234,14 @@ where
spec_version,
weight,
origin,
dispatch_fee_payment,
call,
} = payload;
MessagePayload {
spec_version,
weight,
origin,
dispatch_fee_payment,
call: call.0,
}
}
@@ -250,8 +265,6 @@ mod tests {
"1234",
"--source-signer",
"//Alice",
"--target-signer",
"//Bob",
"remark",
"--remark-payload",
"1234",
@@ -265,8 +278,9 @@ mod tests {
payload,
MessagePayload {
spec_version: relay_millau_client::Millau::RUNTIME_VERSION.spec_version,
weight: 1345000,
weight: 1038000,
origin: CallOrigin::SourceAccount(sp_keyring::AccountKeyring::Alice.to_account_id()),
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
call: hex!("0401081234").to_vec(),
}
);
@@ -304,14 +318,35 @@ mod tests {
payload,
MessagePayload {
spec_version: relay_millau_client::Millau::RUNTIME_VERSION.spec_version,
weight: 1345000,
weight: 1038000,
origin: CallOrigin::TargetAccount(
sp_keyring::AccountKeyring::Alice.to_account_id(),
sp_keyring::AccountKeyring::Bob.into(),
signature,
),
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
call: hex!("0701081234").to_vec(),
}
);
}
#[test]
fn target_signer_must_exist_if_origin_is_target() {
// given
let send_message = SendMessage::from_iter_safe(vec![
"send-message",
"MillauToRialto",
"--source-port",
"1234",
"--source-signer",
"//Alice",
"--origin",
"Target",
"remark",
"--remark-payload",
"1234",
]);
assert!(send_message.is_err());
}
}
@@ -26,12 +26,12 @@ use sp_core::Bytes;
use std::{fmt::Debug, marker::PhantomData, time::Duration};
/// Default synchronization loop timeout.
const STALL_TIMEOUT: Duration = Duration::from_secs(120);
pub(crate) const STALL_TIMEOUT: Duration = Duration::from_secs(120);
/// Default limit of recent finality proofs.
///
/// Finality delay of 4096 blocks is unlikely to happen in practice in
/// Substrate+GRANDPA based chains (good to know).
const RECENT_FINALITY_PROOFS_LIMIT: usize = 4096;
pub(crate) const RECENT_FINALITY_PROOFS_LIMIT: usize = 4096;
/// Headers sync pipeline for Substrate <-> Substrate relays.
pub trait SubstrateFinalitySyncPipeline: FinalitySyncPipeline {
@@ -46,6 +46,13 @@ pub trait SubstrateFinalitySyncPipeline: FinalitySyncPipeline {
Ok(params)
}
/// Start finality relay guards.
///
/// Different finality bridges may have different set of guards - e.g. on ephemeral chains we
/// don't need version guards, on test chains we don't care that much about relayer account
/// balance, ... So the implementation is left to the specific bridges.
fn start_relay_guards(&self) {}
/// Returns id of account that we're using to sign transactions at target chain.
fn transactions_author(&self) -> <Self::TargetChain as Chain>::AccountId;
@@ -112,7 +119,7 @@ pub async fn run<SourceChain, TargetChain, P>(
pipeline: P,
source_client: Client<SourceChain>,
target_client: Client<TargetChain>,
is_on_demand_task: bool,
only_mandatory_headers: bool,
metrics_params: MetricsParams,
) -> anyhow::Result<()>
where
@@ -135,13 +142,13 @@ where
);
finality_relay::run(
FinalitySource::new(source_client),
FinalitySource::new(source_client, None),
SubstrateFinalityTarget::new(target_client, pipeline),
FinalitySyncParams {
is_on_demand_task,
tick: std::cmp::max(SourceChain::AVERAGE_BLOCK_INTERVAL, TargetChain::AVERAGE_BLOCK_INTERVAL),
recent_finality_proofs_limit: RECENT_FINALITY_PROOFS_LIMIT,
stall_timeout: STALL_TIMEOUT,
only_mandatory_headers,
},
metrics_params,
futures::future::pending(),
@@ -49,7 +49,7 @@ pub struct MessagesRelayParams<SC: Chain, SS, TC: Chain, TS> {
/// Message sync pipeline for Substrate <-> Substrate relays.
pub trait SubstrateMessageLane: MessageLane {
/// Name of the runtime method that returns dispatch weight of outbound messages at the source chain.
const OUTBOUND_LANE_MESSAGES_DISPATCH_WEIGHT_METHOD: &'static str;
const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str;
/// Name of the runtime method that returns latest generated nonce at the source chain.
const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str;
/// Name of the runtime method that returns latest received (confirmed) nonce at the the source chain.
@@ -139,6 +139,7 @@ where
type MessagesProof = SubstrateMessagesProof<Source>;
type MessagesReceivingProof = SubstrateMessagesReceivingProof<Target>;
type SourceChainBalance = Source::Balance;
type SourceHeaderNumber = BlockNumberOf<Source>;
type SourceHeaderHash = HashOf<Source>;
@@ -203,7 +204,7 @@ mod tests {
// reserved for messages dispatch allows dispatch of non-trivial messages.
//
// Any significant change in this values should attract additional attention.
(1013, 216_583_333_334),
(782, 216_583_333_334),
);
}
}
@@ -23,17 +23,16 @@ use crate::on_demand_headers::OnDemandHeadersRelay;
use async_trait::async_trait;
use bp_messages::{LaneId, MessageNonce};
use bp_runtime::InstanceId;
use bp_runtime::{messages::DispatchFeePayment, ChainId};
use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof;
use codec::{Decode, Encode};
use frame_support::{traits::Instance, weights::Weight};
use messages_relay::{
message_lane::{SourceHeaderIdOf, TargetHeaderIdOf},
message_lane_loop::{
ClientState, MessageProofParameters, MessageWeights, MessageWeightsMap, SourceClient, SourceClientState,
ClientState, MessageDetails, MessageDetailsMap, MessageProofParameters, SourceClient, SourceClientState,
},
};
use pallet_bridge_messages::Config as MessagesConfig;
use relay_substrate_client::{Chain, Client, Error as SubstrateError, HashOf, HeaderIdOf};
use relay_utils::{relay_loop::Client as RelayClient, BlockNumberBase, HeaderId};
use sp_core::Bytes;
@@ -46,22 +45,22 @@ use std::{marker::PhantomData, ops::RangeInclusive};
pub type SubstrateMessagesProof<C> = (Weight, FromBridgedChainMessagesProof<HashOf<C>>);
/// Substrate client as Substrate messages source.
pub struct SubstrateMessagesSource<C: Chain, P: SubstrateMessageLane, R, I> {
pub struct SubstrateMessagesSource<C: Chain, P: SubstrateMessageLane, I> {
client: Client<C>,
lane: P,
lane_id: LaneId,
instance: InstanceId,
instance: ChainId,
target_to_source_headers_relay: Option<OnDemandHeadersRelay<P::TargetChain>>,
_phantom: PhantomData<(R, I)>,
_phantom: PhantomData<I>,
}
impl<C: Chain, P: SubstrateMessageLane, R, I> SubstrateMessagesSource<C, P, R, I> {
impl<C: Chain, P: SubstrateMessageLane, I> SubstrateMessagesSource<C, P, I> {
/// Create new Substrate headers source.
pub fn new(
client: Client<C>,
lane: P,
lane_id: LaneId,
instance: InstanceId,
instance: ChainId,
target_to_source_headers_relay: Option<OnDemandHeadersRelay<P::TargetChain>>,
) -> Self {
SubstrateMessagesSource {
@@ -75,7 +74,7 @@ impl<C: Chain, P: SubstrateMessageLane, R, I> SubstrateMessagesSource<C, P, R, I
}
}
impl<C: Chain, P: SubstrateMessageLane, R, I> Clone for SubstrateMessagesSource<C, P, R, I> {
impl<C: Chain, P: SubstrateMessageLane, I> Clone for SubstrateMessagesSource<C, P, I> {
fn clone(&self) -> Self {
Self {
client: self.client.clone(),
@@ -89,11 +88,10 @@ impl<C: Chain, P: SubstrateMessageLane, R, I> Clone for SubstrateMessagesSource<
}
#[async_trait]
impl<C, P, R, I> RelayClient for SubstrateMessagesSource<C, P, R, I>
impl<C, P, I> RelayClient for SubstrateMessagesSource<C, P, I>
where
C: Chain,
P: SubstrateMessageLane,
R: 'static + Send + Sync,
I: Send + Sync + Instance,
{
type Error = SubstrateError;
@@ -104,7 +102,7 @@ where
}
#[async_trait]
impl<C, P, R, I> SourceClient<P> for SubstrateMessagesSource<C, P, R, I>
impl<C, P, I> SourceClient<P> for SubstrateMessagesSource<C, P, I>
where
C: Chain,
C::Header: DeserializeOwned,
@@ -112,6 +110,7 @@ where
C::BlockNumber: BlockNumberBase,
P: SubstrateMessageLane<
MessagesProof = SubstrateMessagesProof<C>,
SourceChainBalance = C::Balance,
SourceHeaderNumber = <C::Header as HeaderT>::Number,
SourceHeaderHash = <C::Header as HeaderT>::Hash,
SourceChain = C,
@@ -119,7 +118,6 @@ where
P::TargetChain: Chain<Hash = P::TargetHeaderHash, BlockNumber = P::TargetHeaderNumber>,
P::TargetHeaderNumber: Decode,
P::TargetHeaderHash: Decode,
R: Send + Sync + MessagesConfig<I>,
I: Send + Sync + Instance,
{
async fn state(&self) -> Result<SourceClientState<P>, SubstrateError> {
@@ -168,21 +166,21 @@ where
Ok((id, latest_received_nonce))
}
async fn generated_messages_weights(
async fn generated_message_details(
&self,
id: SourceHeaderIdOf<P>,
nonces: RangeInclusive<MessageNonce>,
) -> Result<MessageWeightsMap, SubstrateError> {
) -> Result<MessageDetailsMap<P::SourceChainBalance>, SubstrateError> {
let encoded_response = self
.client
.state_call(
P::OUTBOUND_LANE_MESSAGES_DISPATCH_WEIGHT_METHOD.into(),
P::OUTBOUND_LANE_MESSAGE_DETAILS_METHOD.into(),
Bytes((self.lane_id, nonces.start(), nonces.end()).encode()),
Some(id.1),
)
.await?;
make_message_weights_map::<C>(
make_message_details_map::<C>(
Decode::decode(&mut &encoded_response.0[..]).map_err(SubstrateError::ResponseParseFailed)?,
nonces,
)
@@ -197,7 +195,7 @@ where
let mut storage_keys = Vec::with_capacity(nonces.end().saturating_sub(*nonces.start()) as usize + 1);
let mut message_nonce = *nonces.start();
while message_nonce <= *nonces.end() {
let message_key = pallet_bridge_messages::storage_keys::message_key::<R, I>(&self.lane_id, message_nonce);
let message_key = pallet_bridge_messages::storage_keys::message_key::<I>(&self.lane_id, message_nonce);
storage_keys.push(message_key);
message_nonce += 1;
}
@@ -239,9 +237,13 @@ where
async fn require_target_header_on_source(&self, id: TargetHeaderIdOf<P>) {
if let Some(ref target_to_source_headers_relay) = self.target_to_source_headers_relay {
target_to_source_headers_relay.require_finalized_header(id);
target_to_source_headers_relay.require_finalized_header(id).await;
}
}
async fn estimate_confirmation_transaction(&self) -> P::SourceChainBalance {
num_traits::Zero::zero() // TODO: https://github.com/paritytech/parity-bridges-common/issues/997
}
}
pub async fn read_client_state<SelfChain, BridgedHeaderHash, BridgedHeaderNumber>(
@@ -287,10 +289,10 @@ where
})
}
fn make_message_weights_map<C: Chain>(
weights: Vec<(MessageNonce, Weight, u32)>,
fn make_message_details_map<C: Chain>(
weights: Vec<bp_messages::MessageDetails<C::Balance>>,
nonces: RangeInclusive<MessageNonce>,
) -> Result<MessageWeightsMap, SubstrateError> {
) -> Result<MessageDetailsMap<C::Balance>, SubstrateError> {
let make_missing_nonce_error = |expected_nonce| {
Err(SubstrateError::Custom(format!(
"Missing nonce {} in messages_dispatch_weight call result. Expected all nonces from {:?}",
@@ -298,7 +300,7 @@ fn make_message_weights_map<C: Chain>(
)))
};
let mut weights_map = MessageWeightsMap::new();
let mut weights_map = MessageDetailsMap::new();
// this is actually prevented by external logic
if nonces.is_empty() {
@@ -308,7 +310,7 @@ fn make_message_weights_map<C: Chain>(
// check if last nonce is missing - loop below is not checking this
let last_nonce_is_missing = weights
.last()
.map(|(last_nonce, _, _)| last_nonce != nonces.end())
.map(|details| details.nonce != *nonces.end())
.unwrap_or(true);
if last_nonce_is_missing {
return make_missing_nonce_error(*nonces.end());
@@ -317,8 +319,8 @@ fn make_message_weights_map<C: Chain>(
let mut expected_nonce = *nonces.start();
let mut is_at_head = true;
for (nonce, weight, size) in weights {
match (nonce == expected_nonce, is_at_head) {
for details in weights {
match (details.nonce == expected_nonce, is_at_head) {
(true, _) => (),
(false, true) => {
// this may happen if some messages were already pruned from the source node
@@ -328,7 +330,7 @@ fn make_message_weights_map<C: Chain>(
target: "bridge",
"Some messages are missing from the {} node: {:?}. Target node may be out of sync?",
C::NAME,
expected_nonce..nonce,
expected_nonce..details.nonce,
);
}
(false, false) => {
@@ -340,13 +342,16 @@ fn make_message_weights_map<C: Chain>(
}
weights_map.insert(
nonce,
MessageWeights {
weight,
size: size as _,
details.nonce,
MessageDetails {
dispatch_weight: details.dispatch_weight,
size: details.size as _,
// TODO: https://github.com/paritytech/parity-bridges-common/issues/997
reward: num_traits::Zero::zero(),
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
},
);
expected_nonce = nonce + 1;
expected_nonce = details.nonce + 1;
is_at_head = false;
}
@@ -357,15 +362,53 @@ fn make_message_weights_map<C: Chain>(
mod tests {
use super::*;
fn message_details_from_rpc(
nonces: RangeInclusive<MessageNonce>,
) -> Vec<bp_messages::MessageDetails<bp_rialto::Balance>> {
nonces
.into_iter()
.map(|nonce| bp_messages::MessageDetails {
nonce,
dispatch_weight: 0,
size: 0,
delivery_and_dispatch_fee: 0,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
})
.collect()
}
#[test]
fn make_message_weights_map_succeeds_if_no_messages_are_missing() {
fn make_message_details_map_succeeds_if_no_messages_are_missing() {
assert_eq!(
make_message_weights_map::<relay_rialto_client::Rialto>(vec![(1, 0, 0), (2, 0, 0), (3, 0, 0)], 1..=3,)
.unwrap(),
make_message_details_map::<relay_rialto_client::Rialto>(message_details_from_rpc(1..=3), 1..=3,).unwrap(),
vec![
(1, MessageWeights { weight: 0, size: 0 }),
(2, MessageWeights { weight: 0, size: 0 }),
(3, MessageWeights { weight: 0, size: 0 }),
(
1,
MessageDetails {
dispatch_weight: 0,
size: 0,
reward: 0,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
}
),
(
2,
MessageDetails {
dispatch_weight: 0,
size: 0,
reward: 0,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
}
),
(
3,
MessageDetails {
dispatch_weight: 0,
size: 0,
reward: 0,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
}
),
]
.into_iter()
.collect(),
@@ -373,12 +416,28 @@ mod tests {
}
#[test]
fn make_message_weights_map_succeeds_if_head_messages_are_missing() {
fn make_message_details_map_succeeds_if_head_messages_are_missing() {
assert_eq!(
make_message_weights_map::<relay_rialto_client::Rialto>(vec![(2, 0, 0), (3, 0, 0)], 1..=3,).unwrap(),
make_message_details_map::<relay_rialto_client::Rialto>(message_details_from_rpc(2..=3), 1..=3,).unwrap(),
vec![
(2, MessageWeights { weight: 0, size: 0 }),
(3, MessageWeights { weight: 0, size: 0 }),
(
2,
MessageDetails {
dispatch_weight: 0,
size: 0,
reward: 0,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
}
),
(
3,
MessageDetails {
dispatch_weight: 0,
size: 0,
reward: 0,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
}
),
]
.into_iter()
.collect(),
@@ -386,25 +445,27 @@ mod tests {
}
#[test]
fn make_message_weights_map_fails_if_mid_messages_are_missing() {
fn make_message_details_map_fails_if_mid_messages_are_missing() {
let mut message_details_from_rpc = message_details_from_rpc(1..=3);
message_details_from_rpc.remove(1);
assert!(matches!(
make_message_weights_map::<relay_rialto_client::Rialto>(vec![(1, 0, 0), (3, 0, 0)], 1..=3,),
make_message_details_map::<relay_rialto_client::Rialto>(message_details_from_rpc, 1..=3,),
Err(SubstrateError::Custom(_))
));
}
#[test]
fn make_message_weights_map_fails_if_tail_messages_are_missing() {
fn make_message_details_map_fails_if_tail_messages_are_missing() {
assert!(matches!(
make_message_weights_map::<relay_rialto_client::Rialto>(vec![(1, 0, 0), (2, 0, 0)], 1..=3,),
make_message_details_map::<relay_rialto_client::Rialto>(message_details_from_rpc(1..=2), 1..=3,),
Err(SubstrateError::Custom(_))
));
}
#[test]
fn make_message_weights_map_fails_if_all_messages_are_missing() {
fn make_message_details_map_fails_if_all_messages_are_missing() {
assert!(matches!(
make_message_weights_map::<relay_rialto_client::Rialto>(vec![], 1..=3),
make_message_details_map::<relay_rialto_client::Rialto>(vec![], 1..=3),
Err(SubstrateError::Custom(_))
));
}
@@ -24,15 +24,14 @@ use crate::on_demand_headers::OnDemandHeadersRelay;
use async_trait::async_trait;
use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState};
use bp_runtime::InstanceId;
use bp_runtime::ChainId;
use bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof;
use codec::{Decode, Encode};
use frame_support::traits::Instance;
use frame_support::{traits::Instance, weights::Weight};
use messages_relay::{
message_lane::{SourceHeaderIdOf, TargetHeaderIdOf},
message_lane_loop::{TargetClient, TargetClientState},
};
use pallet_bridge_messages::Config as MessagesConfig;
use relay_substrate_client::{Chain, Client, Error as SubstrateError, HashOf};
use relay_utils::{relay_loop::Client as RelayClient, BlockNumberBase};
use sp_core::Bytes;
@@ -46,22 +45,22 @@ pub type SubstrateMessagesReceivingProof<C> = (
);
/// Substrate client as Substrate messages target.
pub struct SubstrateMessagesTarget<C: Chain, P: SubstrateMessageLane, R, I> {
pub struct SubstrateMessagesTarget<C: Chain, P: SubstrateMessageLane, I> {
client: Client<C>,
lane: P,
lane_id: LaneId,
instance: InstanceId,
instance: ChainId,
source_to_target_headers_relay: Option<OnDemandHeadersRelay<P::SourceChain>>,
_phantom: PhantomData<(R, I)>,
_phantom: PhantomData<I>,
}
impl<C: Chain, P: SubstrateMessageLane, R, I> SubstrateMessagesTarget<C, P, R, I> {
impl<C: Chain, P: SubstrateMessageLane, I> SubstrateMessagesTarget<C, P, I> {
/// Create new Substrate headers target.
pub fn new(
client: Client<C>,
lane: P,
lane_id: LaneId,
instance: InstanceId,
instance: ChainId,
source_to_target_headers_relay: Option<OnDemandHeadersRelay<P::SourceChain>>,
) -> Self {
SubstrateMessagesTarget {
@@ -75,7 +74,7 @@ impl<C: Chain, P: SubstrateMessageLane, R, I> SubstrateMessagesTarget<C, P, R, I
}
}
impl<C: Chain, P: SubstrateMessageLane, R, I> Clone for SubstrateMessagesTarget<C, P, R, I> {
impl<C: Chain, P: SubstrateMessageLane, I> Clone for SubstrateMessagesTarget<C, P, I> {
fn clone(&self) -> Self {
Self {
client: self.client.clone(),
@@ -89,11 +88,10 @@ impl<C: Chain, P: SubstrateMessageLane, R, I> Clone for SubstrateMessagesTarget<
}
#[async_trait]
impl<C, P, R, I> RelayClient for SubstrateMessagesTarget<C, P, R, I>
impl<C, P, I> RelayClient for SubstrateMessagesTarget<C, P, I>
where
C: Chain,
P: SubstrateMessageLane,
R: 'static + Send + Sync,
I: Send + Sync + Instance,
{
type Error = SubstrateError;
@@ -104,7 +102,7 @@ where
}
#[async_trait]
impl<C, P, R, I> TargetClient<P> for SubstrateMessagesTarget<C, P, R, I>
impl<C, P, I> TargetClient<P> for SubstrateMessagesTarget<C, P, I>
where
C: Chain,
C::Header: DeserializeOwned,
@@ -119,7 +117,6 @@ where
P::SourceChain: Chain<Hash = P::SourceHeaderHash, BlockNumber = P::SourceHeaderNumber>,
P::SourceHeaderNumber: Decode,
P::SourceHeaderHash: Decode,
R: Send + Sync + MessagesConfig<I>,
I: Send + Sync + Instance,
{
async fn state(&self) -> Result<TargetClientState<P>, SubstrateError> {
@@ -190,7 +187,7 @@ where
id: TargetHeaderIdOf<P>,
) -> Result<(TargetHeaderIdOf<P>, P::MessagesReceivingProof), SubstrateError> {
let (id, relayers_state) = self.unrewarded_relayers_state(id).await?;
let inbound_data_key = pallet_bridge_messages::storage_keys::inbound_lane_data_key::<R, I>(&self.lane_id);
let inbound_data_key = pallet_bridge_messages::storage_keys::inbound_lane_data_key::<I>(&self.lane_id);
let proof = self
.client
.prove_storage(vec![inbound_data_key], id.1)
@@ -226,7 +223,16 @@ where
async fn require_source_header_on_target(&self, id: SourceHeaderIdOf<P>) {
if let Some(ref source_to_target_headers_relay) = self.source_to_target_headers_relay {
source_to_target_headers_relay.require_finalized_header(id);
source_to_target_headers_relay.require_finalized_header(id).await;
}
}
async fn estimate_delivery_transaction_in_source_tokens(
&self,
_nonces: RangeInclusive<MessageNonce>,
_total_dispatch_weight: Weight,
_total_size: u32,
) -> P::SourceChainBalance {
num_traits::Zero::zero() // TODO: https://github.com/paritytech/parity-bridges-common/issues/997
}
}
@@ -16,39 +16,38 @@
//! On-demand Substrate -> Substrate headers relay.
use crate::finality_pipeline::{SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate};
use crate::finality_pipeline::{
SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate, RECENT_FINALITY_PROOFS_LIMIT, STALL_TIMEOUT,
};
use crate::finality_target::SubstrateFinalityTarget;
use async_std::sync::{Arc, Mutex};
use bp_header_chain::justification::GrandpaJustification;
use finality_relay::{
FinalitySyncPipeline, SourceClient as FinalitySourceClient, TargetClient as FinalityTargetClient,
FinalitySyncParams, FinalitySyncPipeline, SourceClient as FinalitySourceClient, SourceHeader,
TargetClient as FinalityTargetClient,
};
use futures::{
channel::{mpsc, oneshot},
select, FutureExt, StreamExt,
};
use num_traits::{CheckedSub, Zero};
use futures::{select, FutureExt};
use num_traits::{CheckedSub, One, Zero};
use relay_substrate_client::{
finality_source::FinalitySource as SubstrateFinalitySource, BlockNumberOf, Chain, Client, HashOf, HeaderIdOf,
SyncHeader,
finality_source::{FinalitySource as SubstrateFinalitySource, RequiredHeaderNumberRef},
BlockNumberOf, Chain, Client, HashOf, HeaderIdOf, SyncHeader,
};
use relay_utils::{
metrics::MetricsParams, relay_loop::Client as RelayClient, BlockNumberBase, FailedClient, HeaderId,
MaybeConnectionError,
metrics::MetricsParams, relay_loop::Client as RelayClient, BlockNumberBase, FailedClient, MaybeConnectionError,
};
use std::fmt::Debug;
/// On-demand Substrate <-> Substrate headers relay.
///
/// This relay may be started by messages whenever some other relay (e.g. messages relay) needs more
/// headers to be relayed to continue its regular work. When enough headers are relayed, on-demand
/// relay may be deactivated.
/// This relay may be requested to sync more headers, whenever some other relay (e.g. messages relay) needs
/// it to continue its regular work. When enough headers are relayed, on-demand stops syncing headers.
#[derive(Clone)]
pub struct OnDemandHeadersRelay<SourceChain: Chain> {
/// Background task name.
background_task_name: String,
/// Required headers to background sender.
required_header_tx: mpsc::Sender<HeaderId<SourceChain::Hash, SourceChain::BlockNumber>>,
/// Relay task name.
relay_task_name: String,
/// Shared reference to maximal required finalized header number.
required_header_number: RequiredHeaderNumberRef<SourceChain>,
}
impl<SourceChain: Chain> OnDemandHeadersRelay<SourceChain> {
@@ -75,49 +74,49 @@ impl<SourceChain: Chain> OnDemandHeadersRelay<SourceChain> {
SubstrateFinalityTarget<TargetChain, SubstrateFinalityToSubstrate<SourceChain, TargetChain, TargetSign>>:
FinalityTargetClient<SubstrateFinalityToSubstrate<SourceChain, TargetChain, TargetSign>>,
{
let (required_header_tx, required_header_rx) = mpsc::channel(1);
let required_header_number = Arc::new(Mutex::new(Zero::zero()));
let this = OnDemandHeadersRelay {
relay_task_name: on_demand_headers_relay_name::<SourceChain, TargetChain>(),
required_header_number: required_header_number.clone(),
};
async_std::task::spawn(async move {
background_task(
source_client,
target_client,
pipeline,
maximal_headers_difference,
required_header_rx,
required_header_number,
)
.await;
});
let background_task_name = format!(
"{}-background",
on_demand_headers_relay_name::<SourceChain, TargetChain>()
);
OnDemandHeadersRelay {
background_task_name,
required_header_tx,
}
this
}
/// Someone is asking us to relay given finalized header.
pub fn require_finalized_header(&self, header_id: HeaderIdOf<SourceChain>) {
if let Err(error) = self.required_header_tx.clone().try_send(header_id) {
log::error!(
pub async fn require_finalized_header(&self, header_id: HeaderIdOf<SourceChain>) {
let mut required_header_number = self.required_header_number.lock().await;
if header_id.0 > *required_header_number {
log::trace!(
target: "bridge",
"Failed to send require header id {:?} to {:?}: {:?}",
header_id,
self.background_task_name,
error,
"More {} headers required in {} relay. Going to sync up to the {}",
SourceChain::NAME,
self.relay_task_name,
header_id.0,
);
*required_header_number = header_id.0;
}
}
}
/// Background task that is responsible for starting and stopping headers relay when required.
/// Background task that is responsible for starting headers relay.
async fn background_task<SourceChain, TargetChain, TargetSign>(
source_client: Client<SourceChain>,
target_client: Client<TargetChain>,
pipeline: SubstrateFinalityToSubstrate<SourceChain, TargetChain, TargetSign>,
maximal_headers_difference: SourceChain::BlockNumber,
mut required_header_rx: mpsc::Receiver<HeaderIdOf<SourceChain>>,
required_header_number: RequiredHeaderNumberRef<SourceChain>,
) where
SourceChain: Chain + Debug,
SourceChain::BlockNumber: BlockNumberBase,
@@ -138,36 +137,20 @@ async fn background_task<SourceChain, TargetChain, TargetSign>(
let mut finality_source = SubstrateFinalitySource::<
_,
SubstrateFinalityToSubstrate<SourceChain, TargetChain, TargetSign>,
>::new(source_client.clone());
>::new(source_client.clone(), Some(required_header_number.clone()));
let mut finality_target = SubstrateFinalityTarget::new(target_client.clone(), pipeline.clone());
let mut latest_non_mandatory_at_source = Zero::zero();
let mut active_headers_relay = None;
let mut required_header_number = Zero::zero();
let mut relay_exited_rx = futures::future::pending().left_future();
let mut restart_relay = true;
let finality_relay_task = futures::future::Fuse::terminated();
futures::pin_mut!(finality_relay_task);
loop {
// wait for next target block or for new required header
select! {
_ = async_std::task::sleep(TargetChain::AVERAGE_BLOCK_INTERVAL).fuse() => {},
required_header_id = required_header_rx.next() => {
match required_header_id {
Some(required_header_id) => {
if required_header_id.0 > required_header_number {
required_header_number = required_header_id.0;
}
},
None => {
// that's the only way to exit background task - to drop `required_header_tx`
break
},
}
},
_ = relay_exited_rx => {
// there could be a situation when we're receiving exit signals after we
// have already stopped relay or when we have already started new relay.
// but it isn't critical, because even if we'll accidentally stop new relay
// we'll restart it almost immediately
stop_on_demand_headers_relay(active_headers_relay.take()).await;
_ = finality_relay_task => {
// this should never happen in practice given the current code
restart_relay = true;
},
}
@@ -199,58 +182,187 @@ async fn background_task<SourceChain, TargetChain, TargetSign>(
continue;
}
// start or stop headers relay if required
let action = select_on_demand_relay_action::<SourceChain>(
// submit mandatory header if some headers are missing
let best_finalized_source_header_at_target_fmt = format!("{:?}", best_finalized_source_header_at_target);
let mandatory_scan_range = mandatory_headers_scan_range::<SourceChain>(
best_finalized_source_header_at_source.ok(),
best_finalized_source_header_at_target.ok(),
required_header_number,
maximal_headers_difference,
&relay_task_name,
active_headers_relay.is_some(),
);
match action {
OnDemandRelayAction::Start => {
let (relay_exited_tx, new_relay_exited_rx) = oneshot::channel();
active_headers_relay = start_on_demand_headers_relay(
relay_task_name.clone(),
relay_exited_tx,
source_client.clone(),
target_client.clone(),
pipeline.clone(),
);
if active_headers_relay.is_some() {
relay_exited_rx = new_relay_exited_rx.right_future();
&required_header_number,
)
.await;
if let Some(mandatory_scan_range) = mandatory_scan_range {
let relay_mandatory_header_result = relay_mandatory_header_from_range(
&finality_source,
&required_header_number,
best_finalized_source_header_at_target_fmt,
(
std::cmp::max(mandatory_scan_range.0, latest_non_mandatory_at_source),
mandatory_scan_range.1,
),
&relay_task_name,
)
.await;
match relay_mandatory_header_result {
Ok(true) => (),
Ok(false) => {
// there are no (or we don't need to relay them) mandatory headers in the range
// => to avoid scanning the same headers over and over again, remember that
latest_non_mandatory_at_source = mandatory_scan_range.1;
}
Err(e) => {
if e.is_connection_error() {
relay_utils::relay_loop::reconnect_failed_client(
FailedClient::Source,
relay_utils::relay_loop::RECONNECT_DELAY,
&mut finality_source,
&mut finality_target,
)
.await;
continue;
}
}
}
OnDemandRelayAction::Stop => {
stop_on_demand_headers_relay(active_headers_relay.take()).await;
}
OnDemandRelayAction::None => (),
}
// start/restart relay
if restart_relay {
finality_relay_task.set(
finality_relay::run(
finality_source.clone(),
finality_target.clone(),
FinalitySyncParams {
tick: std::cmp::max(SourceChain::AVERAGE_BLOCK_INTERVAL, TargetChain::AVERAGE_BLOCK_INTERVAL),
recent_finality_proofs_limit: RECENT_FINALITY_PROOFS_LIMIT,
stall_timeout: STALL_TIMEOUT,
only_mandatory_headers: false,
},
MetricsParams::disabled(),
futures::future::pending(),
)
.fuse(),
);
restart_relay = false;
}
}
}
/// Returns `Some()` with inclusive range of headers which must be scanned for manadatory headers
/// and the first of such headers must be submitted to the target node.
async fn mandatory_headers_scan_range<C: Chain>(
best_finalized_source_header_at_source: Option<C::BlockNumber>,
best_finalized_source_header_at_target: Option<C::BlockNumber>,
maximal_headers_difference: C::BlockNumber,
required_header_number: &RequiredHeaderNumberRef<C>,
) -> Option<(C::BlockNumber, C::BlockNumber)> {
let required_header_number = *required_header_number.lock().await;
// if we have been unable to read header number from the target, then let's assume
// that it is the same as required header number. Otherwise we risk submitting
// unneeded transactions
let best_finalized_source_header_at_target =
best_finalized_source_header_at_target.unwrap_or(required_header_number);
// if we have been unable to read header number from the source, then let's assume
// that it is the same as at the target
let best_finalized_source_header_at_source =
best_finalized_source_header_at_source.unwrap_or(best_finalized_source_header_at_target);
// if there are too many source headers missing from the target node, sync mandatory
// headers to target
//
// why do we need that? When complex headers+messages relay is used, it'll normally only relay
// headers when there are undelivered messages/confirmations. But security model of the
// `pallet-bridge-grandpa` module relies on the fact that headers are synced in real-time and
// that it'll see authorities-change header before unbonding period will end for previous
// authorities set.
let current_headers_difference = best_finalized_source_header_at_source
.checked_sub(&best_finalized_source_header_at_target)
.unwrap_or_else(Zero::zero);
if current_headers_difference <= maximal_headers_difference {
return None;
}
// if relay is already asked to sync headers, don't do anything yet
if required_header_number > best_finalized_source_header_at_target {
return None;
}
Some((
best_finalized_source_header_at_target + One::one(),
best_finalized_source_header_at_source,
))
}
/// Try to find mandatory header in the inclusive headers range and, if one is found, ask to relay it.
///
/// Returns `true` if header was found and (asked to be) relayed and `false` otherwise.
async fn relay_mandatory_header_from_range<SourceChain: Chain, P>(
finality_source: &SubstrateFinalitySource<SourceChain, P>,
required_header_number: &RequiredHeaderNumberRef<SourceChain>,
best_finalized_source_header_at_target: String,
range: (SourceChain::BlockNumber, SourceChain::BlockNumber),
relay_task_name: &str,
) -> Result<bool, relay_substrate_client::Error>
where
SubstrateFinalitySource<SourceChain, P>: FinalitySourceClient<P>,
P: FinalitySyncPipeline<Number = SourceChain::BlockNumber>,
{
// search for mandatory header first
let mandatory_source_header_number = find_mandatory_header_in_range(finality_source, range).await?;
// if there are no mandatory headers - we have nothing to do
let mandatory_source_header_number = match mandatory_source_header_number {
Some(mandatory_source_header_number) => mandatory_source_header_number,
None => return Ok(false),
};
// `find_mandatory_header` call may take a while => check if `required_header_number` is still
// less than our `mandatory_source_header_number` before logging anything
let mut required_header_number = required_header_number.lock().await;
if *required_header_number >= mandatory_source_header_number {
return Ok(false);
}
log::trace!(
target: "bridge",
"Too many {} headers missing at target in {} relay ({} vs {}). Going to sync up to the mandatory {}",
SourceChain::NAME,
relay_task_name,
best_finalized_source_header_at_target,
range.1,
mandatory_source_header_number,
);
*required_header_number = mandatory_source_header_number;
Ok(true)
}
/// Read best finalized source block number from source client.
///
/// Returns `None` if we have failed to read the number.
async fn best_finalized_source_header_at_source<SourceChain: Chain, P>(
finality_source: &SubstrateFinalitySource<SourceChain, P>,
relay_task_name: &str,
) -> Result<SourceChain::BlockNumber, <SubstrateFinalitySource<SourceChain, P> as RelayClient>::Error>
) -> Result<SourceChain::BlockNumber, relay_substrate_client::Error>
where
SubstrateFinalitySource<SourceChain, P>: FinalitySourceClient<P>,
P: FinalitySyncPipeline<Number = SourceChain::BlockNumber>,
{
finality_source.best_finalized_block_number().await.map_err(|error| {
log::error!(
target: "bridge",
"Failed to read best finalized source header from source in {} relay: {:?}",
relay_task_name,
error,
);
finality_source
.on_chain_best_finalized_block_number()
.await
.map_err(|error| {
log::error!(
target: "bridge",
"Failed to read best finalized source header from source in {} relay: {:?}",
relay_task_name,
error,
);
error
})
error
})
}
/// Read best finalized source block number from target client.
@@ -279,68 +391,28 @@ where
})
}
/// What to do with the on-demand relay task?
#[derive(Debug, PartialEq)]
enum OnDemandRelayAction {
Start,
Stop,
None,
}
fn select_on_demand_relay_action<C: Chain>(
best_finalized_source_header_at_source: Option<C::BlockNumber>,
best_finalized_source_header_at_target: Option<C::BlockNumber>,
mut required_source_header_at_target: C::BlockNumber,
maximal_headers_difference: C::BlockNumber,
relay_task_name: &str,
is_active: bool,
) -> OnDemandRelayAction {
// if we have been unable to read header number from the target, then let's assume
// that it is the same as required header number. Otherwise we risk submitting
// unneeded transactions
let best_finalized_source_header_at_target =
best_finalized_source_header_at_target.unwrap_or(required_source_header_at_target);
// if we have been unable to read header number from the source, then let's assume
// that it is the same as at the target
let best_finalized_source_header_at_source =
best_finalized_source_header_at_source.unwrap_or(best_finalized_source_header_at_target);
// if there are too many source headers missing from the target node, require some
// new headers at target
//
// why do we need that? When complex headers+messages relay is used, it'll normally only relay
// headers when there are undelivered messages/confirmations. But security model of the
// `pallet-bridge-grandpa` module relies on the fact that headers are synced in real-time and
// that it'll see authorities-change header before unbonding period will end for previous
// authorities set.
let current_headers_difference = best_finalized_source_header_at_source
.checked_sub(&best_finalized_source_header_at_target)
.unwrap_or_else(Zero::zero);
if current_headers_difference > maximal_headers_difference {
required_source_header_at_target = best_finalized_source_header_at_source;
// don't log if relay is already running
if !is_active {
log::trace!(
target: "bridge",
"Too many {} headers missing at target in {} relay ({} vs {}). Going to sync up to the {}",
C::NAME,
relay_task_name,
best_finalized_source_header_at_source,
best_finalized_source_header_at_target,
best_finalized_source_header_at_source,
);
/// Read first mandatory header in given inclusive range.
///
/// Returns `Ok(None)` if there were no mandatory headers in the range.
async fn find_mandatory_header_in_range<SourceChain: Chain, P>(
finality_source: &SubstrateFinalitySource<SourceChain, P>,
range: (SourceChain::BlockNumber, SourceChain::BlockNumber),
) -> Result<Option<SourceChain::BlockNumber>, relay_substrate_client::Error>
where
SubstrateFinalitySource<SourceChain, P>: FinalitySourceClient<P>,
P: FinalitySyncPipeline<Number = SourceChain::BlockNumber>,
{
let mut current = range.0;
while current <= range.1 {
let header: SyncHeader<SourceChain::Header> = finality_source.client().header_by_number(current).await?.into();
if header.is_mandatory() {
return Ok(Some(current));
}
current += One::one();
}
// now let's select what to do with relay
let needs_to_be_active = required_source_header_at_target > best_finalized_source_header_at_target;
match (needs_to_be_active, is_active) {
(true, false) => OnDemandRelayAction::Start,
(false, true) => OnDemandRelayAction::Stop,
_ => OnDemandRelayAction::None,
}
Ok(None)
}
/// On-demand headers relay task name.
@@ -348,61 +420,6 @@ fn on_demand_headers_relay_name<SourceChain: Chain, TargetChain: Chain>() -> Str
format!("on-demand-{}-to-{}", SourceChain::NAME, TargetChain::NAME)
}
/// Start on-demand headers relay task.
fn start_on_demand_headers_relay<SourceChain: Chain, TargetChain: Chain, TargetSign>(
task_name: String,
relay_exited_tx: oneshot::Sender<()>,
source_client: Client<SourceChain>,
target_client: Client<TargetChain>,
pipeline: SubstrateFinalityToSubstrate<SourceChain, TargetChain, TargetSign>,
) -> Option<async_std::task::JoinHandle<()>>
where
SourceChain::BlockNumber: BlockNumberBase,
SubstrateFinalityToSubstrate<SourceChain, TargetChain, TargetSign>: SubstrateFinalitySyncPipeline<
Hash = HashOf<SourceChain>,
Number = BlockNumberOf<SourceChain>,
Header = SyncHeader<SourceChain::Header>,
FinalityProof = GrandpaJustification<SourceChain::Header>,
TargetChain = TargetChain,
>,
TargetSign: 'static,
{
let headers_relay_future =
crate::finality_pipeline::run(pipeline, source_client, target_client, true, MetricsParams::disabled());
let closure_task_name = task_name.clone();
async_std::task::Builder::new()
.name(task_name.clone())
.spawn(async move {
log::info!(target: "bridge", "Starting {} headers relay", closure_task_name);
let result = headers_relay_future.await;
log::trace!(target: "bridge", "{} headers relay has exited. Result: {:?}", closure_task_name, result);
let _ = relay_exited_tx.send(());
})
.map_err(|error| {
log::error!(
target: "bridge",
"Failed to start {} relay: {:?}",
task_name,
error,
);
})
.ok()
}
/// Stop on-demand headers relay task.
async fn stop_on_demand_headers_relay(task: Option<async_std::task::JoinHandle<()>>) {
if let Some(task) = task {
let task_name = task
.task()
.name()
.expect("on-demand tasks are always started with name; qed")
.to_string();
log::trace!(target: "bridge", "Cancelling {} headers relay", task_name);
task.cancel().await;
log::info!(target: "bridge", "Cancelled {} headers relay", task_name);
}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -412,42 +429,19 @@ mod tests {
const AT_SOURCE: Option<bp_millau::BlockNumber> = Some(10);
const AT_TARGET: Option<bp_millau::BlockNumber> = Some(1);
#[test]
fn starts_relay_when_headers_are_required() {
#[async_std::test]
async fn mandatory_headers_scan_range_selects_range_if_too_many_headers_are_missing() {
assert_eq!(
select_on_demand_relay_action::<TestChain>(AT_SOURCE, AT_TARGET, 5, 100, "test", false),
OnDemandRelayAction::Start,
);
assert_eq!(
select_on_demand_relay_action::<TestChain>(AT_SOURCE, AT_TARGET, 5, 100, "test", true),
OnDemandRelayAction::None,
mandatory_headers_scan_range::<TestChain>(AT_SOURCE, AT_TARGET, 5, &Arc::new(Mutex::new(0))).await,
Some((AT_TARGET.unwrap() + 1, AT_SOURCE.unwrap())),
);
}
#[test]
fn starts_relay_when_too_many_headers_missing() {
#[async_std::test]
async fn mandatory_headers_scan_range_selects_nothing_if_enough_headers_are_relayed() {
assert_eq!(
select_on_demand_relay_action::<TestChain>(AT_SOURCE, AT_TARGET, 0, 5, "test", false),
OnDemandRelayAction::Start,
);
assert_eq!(
select_on_demand_relay_action::<TestChain>(AT_SOURCE, AT_TARGET, 0, 5, "test", true),
OnDemandRelayAction::None,
);
}
#[test]
fn stops_relay_if_required_header_is_synced() {
assert_eq!(
select_on_demand_relay_action::<TestChain>(AT_SOURCE, AT_TARGET, AT_TARGET.unwrap(), 100, "test", true),
OnDemandRelayAction::Stop,
);
assert_eq!(
select_on_demand_relay_action::<TestChain>(AT_SOURCE, AT_TARGET, AT_TARGET.unwrap(), 100, "test", false),
OnDemandRelayAction::None,
mandatory_headers_scan_range::<TestChain>(AT_SOURCE, AT_TARGET, 10, &Arc::new(Mutex::new(0))).await,
None,
);
}
}