Remove message fee + message send calls (#1642)

* remove message fee

* it is compiling!

* fixes + fmt

* more cleanup

* more cleanup

* restore MessageDeliveryAndDispatchPayment since we'll need relayer rewards

* started rational relayer removal

* more removal

* removed estimate fee subcommand

* remove DispatchFeePayment

* more removals

* removed conversion rates && some metrics

* - unneeded associated type

* - OutboundMessageFee

* fix benchmarks compilation

* fmt

* test + fix benchmarks

* fix send message

* clippy
This commit is contained in:
Svyatoslav Nikolsky
2022-11-18 12:24:45 +03:00
committed by Bastian Köcher
parent 1217b2cf80
commit 8c845602cf
92 changed files with 589 additions and 5796 deletions
+10 -71
View File
@@ -32,18 +32,8 @@ pub mod rialto_messages;
pub mod rialto_parachain_messages;
pub mod xcm_config;
use crate::{
rialto_messages::{ToRialtoMessagePayload, WithRialtoMessageBridge},
rialto_parachain_messages::{
ToRialtoParachainMessagePayload, WithRialtoParachainMessageBridge,
},
};
use beefy_primitives::{crypto::AuthorityId as BeefyId, mmr::MmrLeafVersion, ValidatorSet};
use bp_runtime::{HeaderId, HeaderIdProvider};
use bridge_runtime_common::messages::{
source::estimate_message_dispatch_and_delivery_fee, MessageBridge,
};
use codec::Decode;
use pallet_grandpa::{
fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList,
@@ -57,7 +47,7 @@ use sp_runtime::{
create_runtime_str, generic, impl_opaque_keys,
traits::{Block as BlockT, IdentityLookup, Keccak256, NumberFor, OpaqueKeys},
transaction_validity::{TransactionSource, TransactionValidity},
ApplyExtrinsicResult, FixedPointNumber, FixedU128, Perquintill,
ApplyExtrinsicResult, FixedPointNumber, Perquintill,
};
use sp_std::prelude::*;
#[cfg(feature = "std")]
@@ -476,9 +466,6 @@ parameter_types! {
bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX;
pub const MaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce =
bp_rialto::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX;
// `IdentityFee` is used by Millau => we may use weight directly
pub const GetDeliveryConfirmationTransactionFee: Balance =
bp_millau::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT.ref_time() as _;
pub const RootAccountForPayments: Option<AccountId> = None;
pub const RialtoChainId: bp_runtime::ChainId = bp_runtime::RIALTO_CHAIN_ID;
pub const RialtoParachainChainId: bp_runtime::ChainId = bp_runtime::RIALTO_PARACHAIN_CHAIN_ID;
@@ -490,17 +477,14 @@ pub type WithRialtoMessagesInstance = ();
impl pallet_bridge_messages::Config<WithRialtoMessagesInstance> for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = pallet_bridge_messages::weights::BridgeWeight<Runtime>;
type Parameter = rialto_messages::MillauToRialtoMessagesParameter;
type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce;
type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane;
type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane;
type MaximalOutboundPayloadSize = crate::rialto_messages::ToRialtoMaximalOutboundPayloadSize;
type OutboundPayload = crate::rialto_messages::ToRialtoMessagePayload;
type OutboundMessageFee = Balance;
type InboundPayload = crate::rialto_messages::FromRialtoMessagePayload;
type InboundMessageFee = bp_rialto::Balance;
type InboundRelayer = bp_rialto::AccountId;
type TargetHeaderChain = crate::rialto_messages::Rialto;
@@ -509,7 +493,6 @@ impl pallet_bridge_messages::Config<WithRialtoMessagesInstance> for Runtime {
pallet_bridge_relayers::MessageDeliveryAndDispatchPaymentAdapter<
Runtime,
WithRialtoMessagesInstance,
GetDeliveryConfirmationTransactionFee,
>;
type OnMessageAccepted = ();
type OnDeliveryConfirmed = ();
@@ -525,7 +508,6 @@ pub type WithRialtoParachainMessagesInstance = pallet_bridge_messages::Instance1
impl pallet_bridge_messages::Config<WithRialtoParachainMessagesInstance> for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = pallet_bridge_messages::weights::BridgeWeight<Runtime>;
type Parameter = rialto_parachain_messages::MillauToRialtoParachainMessagesParameter;
type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce;
type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane;
type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane;
@@ -533,10 +515,8 @@ impl pallet_bridge_messages::Config<WithRialtoParachainMessagesInstance> for Run
type MaximalOutboundPayloadSize =
crate::rialto_parachain_messages::ToRialtoParachainMaximalOutboundPayloadSize;
type OutboundPayload = crate::rialto_parachain_messages::ToRialtoParachainMessagePayload;
type OutboundMessageFee = Balance;
type InboundPayload = crate::rialto_parachain_messages::FromRialtoParachainMessagePayload;
type InboundMessageFee = bp_rialto_parachain::Balance;
type InboundRelayer = bp_rialto_parachain::AccountId;
type TargetHeaderChain = crate::rialto_parachain_messages::RialtoParachain;
@@ -545,7 +525,6 @@ impl pallet_bridge_messages::Config<WithRialtoParachainMessagesInstance> for Run
pallet_bridge_relayers::MessageDeliveryAndDispatchPaymentAdapter<
Runtime,
WithRialtoParachainMessagesInstance,
GetDeliveryConfirmationTransactionFee,
>;
type OnMessageAccepted = ();
type OnDeliveryConfirmed = ();
@@ -941,24 +920,12 @@ impl_runtime_apis! {
}
}
impl bp_rialto::ToRialtoOutboundLaneApi<Block, Balance, ToRialtoMessagePayload> for Runtime {
fn estimate_message_delivery_and_dispatch_fee(
_lane_id: bp_messages::LaneId,
payload: ToRialtoMessagePayload,
rialto_to_this_conversion_rate: Option<FixedU128>,
) -> Option<Balance> {
estimate_message_dispatch_and_delivery_fee::<WithRialtoMessageBridge>(
&payload,
WithRialtoMessageBridge::RELAYER_FEE_PERCENT,
rialto_to_this_conversion_rate,
).ok()
}
impl bp_rialto::ToRialtoOutboundLaneApi<Block> for Runtime {
fn message_details(
lane: bp_messages::LaneId,
begin: bp_messages::MessageNonce,
end: bp_messages::MessageNonce,
) -> Vec<bp_messages::OutboundMessageDetails<Balance>> {
) -> Vec<bp_messages::OutboundMessageDetails> {
bridge_runtime_common::messages_api::outbound_message_details::<
Runtime,
WithRialtoMessagesInstance,
@@ -966,10 +933,10 @@ impl_runtime_apis! {
}
}
impl bp_rialto::FromRialtoInboundLaneApi<Block, bp_rialto::Balance> for Runtime {
impl bp_rialto::FromRialtoInboundLaneApi<Block> for Runtime {
fn message_details(
lane: bp_messages::LaneId,
messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails<bp_rialto::Balance>)>,
messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>,
) -> Vec<bp_messages::InboundMessageDetails> {
bridge_runtime_common::messages_api::inbound_message_details::<
Runtime,
@@ -978,24 +945,12 @@ impl_runtime_apis! {
}
}
impl bp_rialto_parachain::ToRialtoParachainOutboundLaneApi<Block, Balance, ToRialtoParachainMessagePayload> for Runtime {
fn estimate_message_delivery_and_dispatch_fee(
_lane_id: bp_messages::LaneId,
payload: ToRialtoParachainMessagePayload,
rialto_parachain_to_this_conversion_rate: Option<FixedU128>,
) -> Option<Balance> {
estimate_message_dispatch_and_delivery_fee::<WithRialtoParachainMessageBridge>(
&payload,
WithRialtoParachainMessageBridge::RELAYER_FEE_PERCENT,
rialto_parachain_to_this_conversion_rate,
).ok()
}
impl bp_rialto_parachain::ToRialtoParachainOutboundLaneApi<Block> for Runtime {
fn message_details(
lane: bp_messages::LaneId,
begin: bp_messages::MessageNonce,
end: bp_messages::MessageNonce,
) -> Vec<bp_messages::OutboundMessageDetails<Balance>> {
) -> Vec<bp_messages::OutboundMessageDetails> {
bridge_runtime_common::messages_api::outbound_message_details::<
Runtime,
WithRialtoParachainMessagesInstance,
@@ -1003,10 +958,10 @@ impl_runtime_apis! {
}
}
impl bp_rialto_parachain::FromRialtoParachainInboundLaneApi<Block, bp_rialto_parachain::Balance> for Runtime {
impl bp_rialto_parachain::FromRialtoParachainInboundLaneApi<Block> for Runtime {
fn message_details(
lane: bp_messages::LaneId,
messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails<bp_rialto_parachain::Balance>)>,
messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>,
) -> Vec<bp_messages::InboundMessageDetails> {
bridge_runtime_common::messages_api::inbound_message_details::<
Runtime,
@@ -1060,13 +1015,11 @@ impl_runtime_apis! {
let mut batches = Vec::<BenchmarkBatch>::new();
let params = (&config, &whitelist);
use bridge_runtime_common::messages_benchmarking::{prepare_message_delivery_proof, prepare_message_proof, prepare_outbound_message};
use bridge_runtime_common::messages;
use bridge_runtime_common::messages_benchmarking::{prepare_message_delivery_proof, prepare_message_proof};
use pallet_bridge_messages::benchmarking::{
Pallet as MessagesBench,
Config as MessagesConfig,
MessageDeliveryProofParams,
MessageParams,
MessageProofParams,
};
use pallet_bridge_parachains::benchmarking::{
@@ -1076,18 +1029,10 @@ impl_runtime_apis! {
use rialto_messages::WithRialtoMessageBridge;
impl MessagesConfig<WithRialtoMessagesInstance> for Runtime {
fn maximal_message_size() -> u32 {
messages::source::maximal_message_size::<WithRialtoMessageBridge>()
}
fn bridged_relayer_id() -> Self::InboundRelayer {
[0u8; 32].into()
}
fn account_balance(account: &Self::AccountId) -> Self::OutboundMessageFee {
pallet_balances::Pallet::<Runtime>::free_balance(account)
}
fn endow_account(account: &Self::AccountId) {
pallet_balances::Pallet::<Runtime>::make_free_balance_be(
account,
@@ -1095,12 +1040,6 @@ impl_runtime_apis! {
);
}
fn prepare_outbound_message(
params: MessageParams<Self::AccountId>,
) -> (rialto_messages::ToRialtoMessagePayload, Balance) {
(prepare_outbound_message::<WithRialtoMessageBridge>(params), Self::message_fee())
}
fn prepare_message_proof(
params: MessageProofParams,
) -> (rialto_messages::FromRialtoMessagesProof, Weight) {
@@ -21,35 +21,20 @@ use crate::{OriginCaller, RialtoGrandpaInstance, Runtime, RuntimeCall, RuntimeOr
use bp_messages::{
source_chain::TargetHeaderChain,
target_chain::{ProvedMessages, SourceHeaderChain},
InboundLaneData, LaneId, Message, MessageNonce, Parameter as MessagesParameter,
InboundLaneData, LaneId, Message, MessageNonce,
};
use bp_runtime::{ChainId, MILLAU_CHAIN_ID, RIALTO_CHAIN_ID};
use bridge_runtime_common::messages::{
self, BasicConfirmationTransactionEstimation, MessageBridge, MessageTransaction,
};
use codec::{Decode, Encode};
use frame_support::{dispatch::DispatchClass, parameter_types, weights::Weight, RuntimeDebug};
use scale_info::TypeInfo;
use sp_runtime::{traits::Saturating, FixedPointNumber, FixedU128};
use sp_std::convert::TryFrom;
use bridge_runtime_common::messages::{self, MessageBridge};
use frame_support::{parameter_types, weights::Weight, RuntimeDebug};
/// Default lane that is used to send messages to Rialto.
pub const DEFAULT_XCM_LANE_TO_RIALTO: LaneId = [0, 0, 0, 0];
/// Initial value of `RialtoToMillauConversionRate` parameter.
pub const INITIAL_RIALTO_TO_MILLAU_CONVERSION_RATE: FixedU128 =
FixedU128::from_inner(FixedU128::DIV);
/// Initial value of `RialtoFeeMultiplier` parameter.
pub const INITIAL_RIALTO_FEE_MULTIPLIER: FixedU128 = FixedU128::from_inner(FixedU128::DIV);
/// Weight of 2 XCM instructions is for simple `Trap(42)` program, coming through bridge
/// (it is prepended with `UniversalOrigin` instruction). It is used just for simplest manual
/// tests, confirming that we don't break encoding somewhere between.
pub const BASE_XCM_WEIGHT_TWICE: u64 = 2 * crate::xcm_config::BASE_XCM_WEIGHT;
parameter_types! {
/// Rialto to Millau conversion rate. Initially we treat both tokens as equal.
pub storage RialtoToMillauConversionRate: FixedU128 = INITIAL_RIALTO_TO_MILLAU_CONVERSION_RATE;
/// Fee multiplier value at Rialto chain.
pub storage RialtoFeeMultiplier: FixedU128 = INITIAL_RIALTO_FEE_MULTIPLIER;
/// Weight credit for our test messages.
///
/// 2 XCM instructions is for simple `Trap(42)` program, coming through bridge
@@ -91,7 +76,6 @@ pub type ToRialtoMaximalOutboundPayloadSize =
pub struct WithRialtoMessageBridge;
impl MessageBridge for WithRialtoMessageBridge {
const RELAYER_FEE_PERCENT: u32 = 10;
const THIS_CHAIN_ID: ChainId = MILLAU_CHAIN_ID;
const BRIDGED_CHAIN_ID: ChainId = RIALTO_CHAIN_ID;
const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME;
@@ -100,16 +84,6 @@ impl MessageBridge for WithRialtoMessageBridge {
type BridgedChain = Rialto;
type BridgedHeaderChain =
pallet_bridge_grandpa::GrandpaChainHeaders<Runtime, RialtoGrandpaInstance>;
fn bridged_balance_to_this_balance(
bridged_balance: bp_rialto::Balance,
bridged_to_this_conversion_rate_override: Option<FixedU128>,
) -> bp_millau::Balance {
let conversion_rate = bridged_to_this_conversion_rate_override
.unwrap_or_else(RialtoToMillauConversionRate::get);
bp_millau::Balance::try_from(conversion_rate.saturating_mul_int(bridged_balance))
.unwrap_or(bp_millau::Balance::MAX)
}
}
/// Millau chain from message lane point of view.
@@ -123,12 +97,6 @@ impl messages::UnderlyingChainProvider for Millau {
impl messages::ThisChainWithMessages for Millau {
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
type ConfirmationTransactionEstimation = BasicConfirmationTransactionEstimation<
bp_millau::AccountId,
{ bp_millau::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT.ref_time() },
{ bp_rialto::EXTRA_STORAGE_PROOF_SIZE },
{ bp_millau::TX_EXTRA_BYTES },
>;
fn is_message_accepted(send_origin: &Self::RuntimeOrigin, lane: &LaneId) -> bool {
let here_location =
@@ -153,21 +121,6 @@ impl messages::ThisChainWithMessages for Millau {
fn maximal_pending_messages_at_outbound_lane() -> MessageNonce {
MessageNonce::MAX
}
fn transaction_payment(transaction: MessageTransaction<Weight>) -> bp_millau::Balance {
// `transaction` may represent transaction from the future, when multiplier value will
// be larger, so let's use slightly increased value
let multiplier = FixedU128::saturating_from_rational(110, 100)
.saturating_mul(pallet_transaction_payment::Pallet::<Runtime>::next_fee_multiplier());
// in our testnets, both per-byte fee and weight-to-fee are 1:1
messages::transaction_payment(
bp_millau::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic,
1,
multiplier,
|weight| weight.ref_time() as _,
transaction,
)
}
}
/// Rialto chain from message lane point of view.
@@ -182,45 +135,6 @@ impl messages::BridgedChainWithMessages for Rialto {
fn verify_dispatch_weight(_message_payload: &[u8]) -> bool {
true
}
fn estimate_delivery_transaction(
message_payload: &[u8],
include_pay_dispatch_fee_cost: bool,
message_dispatch_weight: Weight,
) -> MessageTransaction<Weight> {
let message_payload_len = u32::try_from(message_payload.len()).unwrap_or(u32::MAX);
let extra_bytes_in_payload = message_payload_len
.saturating_sub(pallet_bridge_messages::EXPECTED_DEFAULT_MESSAGE_LENGTH);
MessageTransaction {
dispatch_weight: bp_rialto::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT
.saturating_mul(extra_bytes_in_payload as u64)
.saturating_add(bp_rialto::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT)
.saturating_sub(if include_pay_dispatch_fee_cost {
Weight::from_ref_time(0)
} else {
bp_rialto::PAY_INBOUND_DISPATCH_FEE_WEIGHT
})
.saturating_add(message_dispatch_weight),
size: message_payload_len
.saturating_add(bp_millau::EXTRA_STORAGE_PROOF_SIZE)
.saturating_add(bp_rialto::TX_EXTRA_BYTES),
}
}
fn transaction_payment(transaction: MessageTransaction<Weight>) -> bp_rialto::Balance {
// we don't have a direct access to the value of multiplier at Rialto chain
// => it is a messages module parameter
let multiplier = RialtoFeeMultiplier::get();
// in our testnets, both per-byte fee and weight-to-fee are 1:1
messages::transaction_payment(
bp_rialto::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic,
1,
multiplier,
|weight| weight.ref_time() as _,
transaction,
)
}
}
impl TargetHeaderChain<ToRialtoMessagePayload, bp_millau::AccountId> for Rialto {
@@ -242,7 +156,7 @@ impl TargetHeaderChain<ToRialtoMessagePayload, bp_millau::AccountId> for Rialto
}
}
impl SourceHeaderChain<bp_rialto::Balance> for Rialto {
impl SourceHeaderChain for Rialto {
type Error = &'static str;
// The proof is:
// - hash of the header this proof has been created with;
@@ -254,28 +168,12 @@ impl SourceHeaderChain<bp_rialto::Balance> for Rialto {
fn verify_messages_proof(
proof: Self::MessagesProof,
messages_count: u32,
) -> Result<ProvedMessages<Message<bp_rialto::Balance>>, Self::Error> {
) -> Result<ProvedMessages<Message>, Self::Error> {
messages::target::verify_messages_proof::<WithRialtoMessageBridge>(proof, messages_count)
.map_err(Into::into)
}
}
/// Millau -> Rialto message lane pallet parameters.
#[derive(RuntimeDebug, Clone, Encode, Decode, PartialEq, Eq, TypeInfo)]
pub enum MillauToRialtoMessagesParameter {
/// The conversion formula we use is: `MillauTokens = RialtoTokens * conversion_rate`.
RialtoToMillauConversionRate(FixedU128),
}
impl MessagesParameter for MillauToRialtoMessagesParameter {
fn save(&self) {
match *self {
MillauToRialtoMessagesParameter::RialtoToMillauConversionRate(ref conversion_rate) =>
RialtoToMillauConversionRate::set(conversion_rate),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -295,13 +193,7 @@ mod tests {
fn ensure_millau_message_lane_weights_are_correct() {
type Weights = pallet_bridge_messages::weights::BridgeWeight<Runtime>;
pallet_bridge_messages::ensure_weights_are_correct::<Weights>(
bp_millau::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT,
bp_millau::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT,
bp_millau::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT,
bp_millau::PAY_INBOUND_DISPATCH_FEE_WEIGHT,
DbWeight::get(),
);
pallet_bridge_messages::ensure_weights_are_correct::<Weights>();
let max_incoming_message_proof_size = bp_rialto::EXTRA_STORAGE_PROOF_SIZE.saturating_add(
messages::target::maximal_incoming_message_size(bp_millau::Millau::max_extrinsic_size()),
@@ -366,13 +258,5 @@ mod tests {
bp_rialto::WITH_RIALTO_MESSAGES_PALLET_NAME,
},
});
assert_eq!(
RialtoToMillauConversionRate::key().to_vec(),
bp_runtime::storage_parameter_key(
bp_millau::RIALTO_TO_MILLAU_CONVERSION_RATE_PARAMETER_NAME
)
.0,
);
}
}
@@ -21,17 +21,11 @@ use crate::{Runtime, RuntimeCall, RuntimeOrigin, WithRialtoParachainsInstance};
use bp_messages::{
source_chain::TargetHeaderChain,
target_chain::{ProvedMessages, SourceHeaderChain},
InboundLaneData, LaneId, Message, MessageNonce, Parameter as MessagesParameter,
InboundLaneData, LaneId, Message, MessageNonce,
};
use bp_runtime::{ChainId, MILLAU_CHAIN_ID, RIALTO_PARACHAIN_CHAIN_ID};
use bridge_runtime_common::messages::{
self, BasicConfirmationTransactionEstimation, MessageBridge, MessageTransaction,
};
use codec::{Decode, Encode};
use frame_support::{dispatch::DispatchClass, parameter_types, weights::Weight, RuntimeDebug};
use scale_info::TypeInfo;
use sp_runtime::{traits::Saturating, FixedPointNumber, FixedU128};
use sp_std::convert::TryFrom;
use bridge_runtime_common::messages::{self, MessageBridge};
use frame_support::{parameter_types, weights::Weight, RuntimeDebug};
/// Default lane that is used to send messages to Rialto parachain.
pub const DEFAULT_XCM_LANE_TO_RIALTO_PARACHAIN: LaneId = [0, 0, 0, 0];
@@ -40,18 +34,7 @@ pub const DEFAULT_XCM_LANE_TO_RIALTO_PARACHAIN: LaneId = [0, 0, 0, 0];
/// tests, confirming that we don't break encoding somewhere between.
pub const BASE_XCM_WEIGHT_TWICE: u64 = 2 * crate::xcm_config::BASE_XCM_WEIGHT;
/// Initial value of `RialtoParachainToMillauConversionRate` parameter.
pub const INITIAL_RIALTO_PARACHAIN_TO_MILLAU_CONVERSION_RATE: FixedU128 =
FixedU128::from_inner(FixedU128::DIV);
/// Initial value of `RialtoParachainFeeMultiplier` parameter.
pub const INITIAL_RIALTO_PARACHAIN_FEE_MULTIPLIER: FixedU128 =
FixedU128::from_inner(FixedU128::DIV);
parameter_types! {
/// RialtoParachain to Millau conversion rate. Initially we treat both tokens as equal.
pub storage RialtoParachainToMillauConversionRate: FixedU128 = INITIAL_RIALTO_PARACHAIN_TO_MILLAU_CONVERSION_RATE;
/// Fee multiplier value at RialtoParachain chain.
pub storage RialtoParachainFeeMultiplier: FixedU128 = INITIAL_RIALTO_PARACHAIN_FEE_MULTIPLIER;
/// Weight credit for our test messages.
///
/// 2 XCM instructions is for simple `Trap(42)` program, coming through bridge
@@ -95,7 +78,6 @@ pub type ToRialtoParachainMaximalOutboundPayloadSize =
pub struct WithRialtoParachainMessageBridge;
impl MessageBridge for WithRialtoParachainMessageBridge {
const RELAYER_FEE_PERCENT: u32 = 10;
const THIS_CHAIN_ID: ChainId = MILLAU_CHAIN_ID;
const BRIDGED_CHAIN_ID: ChainId = RIALTO_PARACHAIN_CHAIN_ID;
const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME;
@@ -107,16 +89,6 @@ impl MessageBridge for WithRialtoParachainMessageBridge {
WithRialtoParachainsInstance,
bp_rialto_parachain::RialtoParachain,
>;
fn bridged_balance_to_this_balance(
bridged_balance: bp_rialto_parachain::Balance,
bridged_to_this_conversion_rate_override: Option<FixedU128>,
) -> bp_millau::Balance {
let conversion_rate = bridged_to_this_conversion_rate_override
.unwrap_or_else(RialtoParachainToMillauConversionRate::get);
bp_millau::Balance::try_from(conversion_rate.saturating_mul_int(bridged_balance))
.unwrap_or(bp_millau::Balance::MAX)
}
}
/// Millau chain from message lane point of view.
@@ -130,12 +102,6 @@ impl messages::UnderlyingChainProvider for Millau {
impl messages::ThisChainWithMessages for Millau {
type RuntimeCall = RuntimeCall;
type RuntimeOrigin = RuntimeOrigin;
type ConfirmationTransactionEstimation = BasicConfirmationTransactionEstimation<
bp_rialto_parachain::AccountId,
{ bp_millau::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT.ref_time() },
{ bp_rialto_parachain::EXTRA_STORAGE_PROOF_SIZE },
{ bp_millau::TX_EXTRA_BYTES },
>;
fn is_message_accepted(_send_origin: &Self::RuntimeOrigin, lane: &LaneId) -> bool {
*lane == DEFAULT_XCM_LANE_TO_RIALTO_PARACHAIN || *lane == [0, 0, 0, 1]
@@ -144,21 +110,6 @@ impl messages::ThisChainWithMessages for Millau {
fn maximal_pending_messages_at_outbound_lane() -> MessageNonce {
MessageNonce::MAX
}
fn transaction_payment(transaction: MessageTransaction<Weight>) -> bp_millau::Balance {
// `transaction` may represent transaction from the future, when multiplier value will
// be larger, so let's use slightly increased value
let multiplier = FixedU128::saturating_from_rational(110, 100)
.saturating_mul(pallet_transaction_payment::Pallet::<Runtime>::next_fee_multiplier());
// in our testnets, both per-byte fee and weight-to-fee are 1:1
messages::transaction_payment(
bp_millau::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic,
1,
multiplier,
|weight| weight.ref_time() as _,
transaction,
)
}
}
/// RialtoParachain chain from message lane point of view.
@@ -173,49 +124,6 @@ impl messages::BridgedChainWithMessages for RialtoParachain {
fn verify_dispatch_weight(_message_payload: &[u8]) -> bool {
true
}
fn estimate_delivery_transaction(
message_payload: &[u8],
include_pay_dispatch_fee_cost: bool,
message_dispatch_weight: Weight,
) -> MessageTransaction<Weight> {
let message_payload_len = u32::try_from(message_payload.len()).unwrap_or(u32::MAX);
let extra_bytes_in_payload = message_payload_len
.saturating_sub(pallet_bridge_messages::EXPECTED_DEFAULT_MESSAGE_LENGTH);
MessageTransaction {
dispatch_weight: bp_rialto_parachain::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT
.saturating_mul(extra_bytes_in_payload as _)
.saturating_add(bp_rialto_parachain::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT)
.saturating_sub(if include_pay_dispatch_fee_cost {
Weight::from_ref_time(0)
} else {
bp_rialto_parachain::PAY_INBOUND_DISPATCH_FEE_WEIGHT
})
.saturating_add(message_dispatch_weight),
size: message_payload_len
.saturating_add(bp_millau::EXTRA_STORAGE_PROOF_SIZE)
.saturating_add(bp_rialto_parachain::TX_EXTRA_BYTES),
}
}
fn transaction_payment(
transaction: MessageTransaction<Weight>,
) -> bp_rialto_parachain::Balance {
// we don't have a direct access to the value of multiplier at RialtoParachain chain
// => it is a messages module parameter
let multiplier = RialtoParachainFeeMultiplier::get();
// in our testnets, both per-byte fee and weight-to-fee are 1:1
messages::transaction_payment(
bp_rialto_parachain::BlockWeights::get()
.get(DispatchClass::Normal)
.base_extrinsic,
1,
multiplier,
|weight| weight.ref_time() as _,
transaction,
)
}
}
impl TargetHeaderChain<ToRialtoParachainMessagePayload, bp_millau::AccountId> for RialtoParachain {
@@ -237,7 +145,7 @@ impl TargetHeaderChain<ToRialtoParachainMessagePayload, bp_millau::AccountId> fo
}
}
impl SourceHeaderChain<bp_rialto_parachain::Balance> for RialtoParachain {
impl SourceHeaderChain for RialtoParachain {
type Error = &'static str;
// The proof is:
// - hash of the header this proof has been created with;
@@ -249,7 +157,7 @@ impl SourceHeaderChain<bp_rialto_parachain::Balance> for RialtoParachain {
fn verify_messages_proof(
proof: Self::MessagesProof,
messages_count: u32,
) -> Result<ProvedMessages<Message<bp_rialto_parachain::Balance>>, Self::Error> {
) -> Result<ProvedMessages<Message>, Self::Error> {
messages::target::verify_messages_proof::<WithRialtoParachainMessageBridge>(
proof,
messages_count,
@@ -257,20 +165,3 @@ impl SourceHeaderChain<bp_rialto_parachain::Balance> for RialtoParachain {
.map_err(Into::into)
}
}
/// Millau -> RialtoParachain message lane pallet parameters.
#[derive(RuntimeDebug, Clone, Encode, Decode, PartialEq, Eq, TypeInfo)]
pub enum MillauToRialtoParachainMessagesParameter {
/// The conversion formula we use is: `MillauTokens = RialtoParachainTokens * conversion_rate`.
RialtoParachainToMillauConversionRate(FixedU128),
}
impl MessagesParameter for MillauToRialtoParachainMessagesParameter {
fn save(&self) {
match *self {
MillauToRialtoParachainMessagesParameter::RialtoParachainToMillauConversionRate(
ref conversion_rate,
) => RialtoParachainToMillauConversionRate::set(conversion_rate),
}
}
}
+2 -2
View File
@@ -271,7 +271,7 @@ mod tests {
fn xcm_messages_are_sent_using_bridge_router() {
new_test_ext().execute_with(|| {
let xcm: Xcm<()> = vec![Instruction::Trap(42)].into();
let expected_fee = MultiAssets::from((Here, 4_259_858_152_u64));
let expected_fee = MultiAssets::from((Here, 1_000_000_u128));
let expected_hash =
([0u8, 0u8, 0u8, 0u8], 1u64).using_encoded(sp_io::hashing::blake2_256);
@@ -305,7 +305,7 @@ mod tests {
let mut incoming_message = DispatchMessage {
key: MessageKey { lane_id: [0, 0, 0, 0], nonce: 1 },
data: DispatchMessageData { payload: Ok((location, xcm).into()), fee: 0 },
data: DispatchMessageData { payload: Ok((location, xcm).into()) },
};
let dispatch_weight = MessageDispatcher::dispatch_weight(&mut incoming_message);
@@ -26,14 +26,9 @@
#[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
use crate::millau_messages::{
ToMillauMessagePayload, WithMillauMessageBridge, DEFAULT_XCM_LANE_TO_MILLAU,
};
use crate::millau_messages::{WithMillauMessageBridge, DEFAULT_XCM_LANE_TO_MILLAU};
use bridge_runtime_common::messages::{
source::{estimate_message_dispatch_and_delivery_fee, XcmBridge, XcmBridgeAdapter},
MessageBridge,
};
use bridge_runtime_common::messages::source::{XcmBridge, XcmBridgeAdapter};
use cumulus_pallet_parachain_system::AnyRelayNumber;
use sp_api::impl_runtime_apis;
use sp_core::{crypto::KeyTypeId, OpaqueMetadata};
@@ -41,7 +36,7 @@ use sp_runtime::{
create_runtime_str, generic, impl_opaque_keys,
traits::{AccountIdLookup, Block as BlockT},
transaction_validity::{TransactionSource, TransactionValidity},
ApplyExtrinsicResult, FixedU128,
ApplyExtrinsicResult,
};
use sp_std::prelude::*;
@@ -557,9 +552,6 @@ parameter_types! {
bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX;
pub const MaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce =
bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX;
// `IdentityFee` is used by Rialto => we may use weight directly
pub const GetDeliveryConfirmationTransactionFee: Balance =
bp_rialto_parachain::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT.ref_time() as _;
pub const RootAccountForPayments: Option<AccountId> = None;
pub const BridgedChainId: bp_runtime::ChainId = bp_runtime::MILLAU_CHAIN_ID;
}
@@ -570,17 +562,14 @@ pub type WithMillauMessagesInstance = ();
impl pallet_bridge_messages::Config<WithMillauMessagesInstance> for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = pallet_bridge_messages::weights::BridgeWeight<Runtime>;
type Parameter = millau_messages::RialtoParachainToMillauMessagesParameter;
type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce;
type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane;
type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane;
type MaximalOutboundPayloadSize = crate::millau_messages::ToMillauMaximalOutboundPayloadSize;
type OutboundPayload = crate::millau_messages::ToMillauMessagePayload;
type OutboundMessageFee = Balance;
type InboundPayload = crate::millau_messages::FromMillauMessagePayload;
type InboundMessageFee = bp_millau::Balance;
type InboundRelayer = bp_millau::AccountId;
type TargetHeaderChain = crate::millau_messages::Millau;
@@ -589,7 +578,6 @@ impl pallet_bridge_messages::Config<WithMillauMessagesInstance> for Runtime {
pallet_bridge_relayers::MessageDeliveryAndDispatchPaymentAdapter<
Runtime,
WithMillauMessagesInstance,
GetDeliveryConfirmationTransactionFee,
>;
type OnMessageAccepted = ();
type OnDeliveryConfirmed = ();
@@ -748,24 +736,12 @@ impl_runtime_apis! {
}
}
impl bp_millau::ToMillauOutboundLaneApi<Block, Balance, ToMillauMessagePayload> for Runtime {
fn estimate_message_delivery_and_dispatch_fee(
_lane_id: bp_messages::LaneId,
payload: ToMillauMessagePayload,
millau_to_this_conversion_rate: Option<FixedU128>,
) -> Option<Balance> {
estimate_message_dispatch_and_delivery_fee::<WithMillauMessageBridge>(
&payload,
WithMillauMessageBridge::RELAYER_FEE_PERCENT,
millau_to_this_conversion_rate,
).ok()
}
impl bp_millau::ToMillauOutboundLaneApi<Block> for Runtime {
fn message_details(
lane: bp_messages::LaneId,
begin: bp_messages::MessageNonce,
end: bp_messages::MessageNonce,
) -> Vec<bp_messages::OutboundMessageDetails<Balance>> {
) -> Vec<bp_messages::OutboundMessageDetails> {
bridge_runtime_common::messages_api::outbound_message_details::<
Runtime,
WithMillauMessagesInstance,
@@ -773,10 +749,10 @@ impl_runtime_apis! {
}
}
impl bp_millau::FromMillauInboundLaneApi<Block, bp_millau::Balance> for Runtime {
impl bp_millau::FromMillauInboundLaneApi<Block> for Runtime {
fn message_details(
lane: bp_messages::LaneId,
messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails<bp_millau::Balance>)>,
messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>,
) -> Vec<bp_messages::InboundMessageDetails> {
bridge_runtime_common::messages_api::inbound_message_details::<
Runtime,
@@ -882,7 +858,7 @@ mod tests {
let xcm: Xcm<()> = vec![Instruction::Trap(42)].into();
let send_result = send_xcm::<XcmRouter>(dest.into(), xcm);
let expected_fee = MultiAssets::from((Here, Fungibility::Fungible(4_259_858_152_u128)));
let expected_fee = MultiAssets::from((Here, Fungibility::Fungible(1_000_000_u128)));
let expected_hash =
([0u8, 0u8, 0u8, 0u8], 1u64).using_encoded(sp_io::hashing::blake2_256);
assert_eq!(send_result, Ok((expected_hash, expected_fee)),);
@@ -906,7 +882,7 @@ mod tests {
let mut incoming_message = DispatchMessage {
key: MessageKey { lane_id: [0, 0, 0, 0], nonce: 1 },
data: DispatchMessageData { payload: Ok((location, xcm).into()), fee: 0 },
data: DispatchMessageData { payload: Ok((location, xcm).into()) },
};
let dispatch_weight = MessageDispatcher::dispatch_weight(&mut incoming_message);
@@ -24,35 +24,20 @@ use crate::{MillauGrandpaInstance, OriginCaller, Runtime, RuntimeCall, RuntimeOr
use bp_messages::{
source_chain::TargetHeaderChain,
target_chain::{ProvedMessages, SourceHeaderChain},
InboundLaneData, LaneId, Message, MessageNonce, Parameter as MessagesParameter,
InboundLaneData, LaneId, Message, MessageNonce,
};
use bp_runtime::{ChainId, MILLAU_CHAIN_ID, RIALTO_PARACHAIN_CHAIN_ID};
use bridge_runtime_common::messages::{
self, BasicConfirmationTransactionEstimation, MessageBridge, MessageTransaction,
};
use codec::{Decode, Encode};
use frame_support::{dispatch::DispatchClass, parameter_types, weights::Weight, RuntimeDebug};
use scale_info::TypeInfo;
use sp_runtime::{traits::Saturating, FixedPointNumber, FixedU128};
use sp_std::convert::TryFrom;
use bridge_runtime_common::messages::{self, MessageBridge};
use frame_support::{parameter_types, weights::Weight, RuntimeDebug};
/// Default lane that is used to send messages to Millau.
pub const DEFAULT_XCM_LANE_TO_MILLAU: LaneId = [0, 0, 0, 0];
/// Initial value of `MillauToRialtoParachainConversionRate` parameter.
pub const INITIAL_MILLAU_TO_RIALTO_PARACHAIN_CONVERSION_RATE: FixedU128 =
FixedU128::from_inner(FixedU128::DIV);
/// Initial value of `MillauFeeMultiplier` parameter.
pub const INITIAL_MILLAU_FEE_MULTIPLIER: FixedU128 = FixedU128::from_inner(FixedU128::DIV);
/// Weight of 2 XCM instructions is for simple `Trap(42)` program, coming through bridge
/// (it is prepended with `UniversalOrigin` instruction). It is used just for simplest manual
/// tests, confirming that we don't break encoding somewhere between.
pub const BASE_XCM_WEIGHT_TWICE: u64 = 2 * crate::BASE_XCM_WEIGHT;
parameter_types! {
/// Millau to RialtoParachain conversion rate. Initially we treat both tokens as equal.
pub storage MillauToRialtoParachainConversionRate: FixedU128 = INITIAL_MILLAU_TO_RIALTO_PARACHAIN_CONVERSION_RATE;
/// Fee multiplier value at Millau chain.
pub storage MillauFeeMultiplier: FixedU128 = INITIAL_MILLAU_FEE_MULTIPLIER;
/// Weight credit for our test messages.
///
/// 2 XCM instructions is for simple `Trap(42)` program, coming through bridge
@@ -94,7 +79,6 @@ pub type ToMillauMaximalOutboundPayloadSize =
pub struct WithMillauMessageBridge;
impl MessageBridge for WithMillauMessageBridge {
const RELAYER_FEE_PERCENT: u32 = 10;
const THIS_CHAIN_ID: ChainId = RIALTO_PARACHAIN_CHAIN_ID;
const BRIDGED_CHAIN_ID: ChainId = MILLAU_CHAIN_ID;
const BRIDGED_MESSAGES_PALLET_NAME: &'static str =
@@ -104,16 +88,6 @@ impl MessageBridge for WithMillauMessageBridge {
type BridgedChain = Millau;
type BridgedHeaderChain =
pallet_bridge_grandpa::GrandpaChainHeaders<Runtime, MillauGrandpaInstance>;
fn bridged_balance_to_this_balance(
bridged_balance: bp_millau::Balance,
bridged_to_this_conversion_rate_override: Option<FixedU128>,
) -> bp_rialto_parachain::Balance {
let conversion_rate = bridged_to_this_conversion_rate_override
.unwrap_or_else(MillauToRialtoParachainConversionRate::get);
bp_rialto_parachain::Balance::try_from(conversion_rate.saturating_mul_int(bridged_balance))
.unwrap_or(bp_rialto_parachain::Balance::MAX)
}
}
/// RialtoParachain chain from message lane point of view.
@@ -127,12 +101,6 @@ impl messages::UnderlyingChainProvider for RialtoParachain {
impl messages::ThisChainWithMessages for RialtoParachain {
type RuntimeCall = RuntimeCall;
type RuntimeOrigin = RuntimeOrigin;
type ConfirmationTransactionEstimation = BasicConfirmationTransactionEstimation<
bp_rialto_parachain::AccountId,
{ bp_rialto_parachain::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT.ref_time() },
{ bp_millau::EXTRA_STORAGE_PROOF_SIZE },
{ bp_rialto_parachain::TX_EXTRA_BYTES },
>;
fn is_message_accepted(send_origin: &Self::RuntimeOrigin, lane: &LaneId) -> bool {
let here_location = xcm::v3::MultiLocation::from(crate::UniversalLocation::get());
@@ -156,25 +124,6 @@ impl messages::ThisChainWithMessages for RialtoParachain {
fn maximal_pending_messages_at_outbound_lane() -> MessageNonce {
MessageNonce::MAX
}
fn transaction_payment(
transaction: MessageTransaction<Weight>,
) -> bp_rialto_parachain::Balance {
// `transaction` may represent transaction from the future, when multiplier value will
// be larger, so let's use slightly increased value
let multiplier = FixedU128::saturating_from_rational(110, 100)
.saturating_mul(pallet_transaction_payment::Pallet::<Runtime>::next_fee_multiplier());
// in our testnets, both per-byte fee and weight-to-fee are 1:1
messages::transaction_payment(
bp_rialto_parachain::BlockWeights::get()
.get(DispatchClass::Normal)
.base_extrinsic,
1,
multiplier,
|weight| weight.ref_time() as _,
transaction,
)
}
}
/// Millau chain from message lane point of view.
@@ -189,45 +138,6 @@ impl messages::BridgedChainWithMessages for Millau {
fn verify_dispatch_weight(_message_payload: &[u8]) -> bool {
true
}
fn estimate_delivery_transaction(
message_payload: &[u8],
include_pay_dispatch_fee_cost: bool,
message_dispatch_weight: Weight,
) -> MessageTransaction<Weight> {
let message_payload_len = u32::try_from(message_payload.len()).unwrap_or(u32::MAX);
let extra_bytes_in_payload = message_payload_len
.saturating_sub(pallet_bridge_messages::EXPECTED_DEFAULT_MESSAGE_LENGTH);
MessageTransaction {
dispatch_weight: bp_millau::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT
.saturating_mul(extra_bytes_in_payload as u64)
.saturating_add(bp_millau::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT)
.saturating_sub(if include_pay_dispatch_fee_cost {
Weight::from_ref_time(0)
} else {
bp_millau::PAY_INBOUND_DISPATCH_FEE_WEIGHT
})
.saturating_add(message_dispatch_weight),
size: message_payload_len
.saturating_add(bp_rialto_parachain::EXTRA_STORAGE_PROOF_SIZE)
.saturating_add(bp_millau::TX_EXTRA_BYTES),
}
}
fn transaction_payment(transaction: MessageTransaction<Weight>) -> bp_millau::Balance {
// we don't have a direct access to the value of multiplier at Millau chain
// => it is a messages module parameter
let multiplier = MillauFeeMultiplier::get();
// in our testnets, both per-byte fee and weight-to-fee are 1:1
messages::transaction_payment(
bp_millau::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic,
1,
multiplier,
|weight| weight.ref_time() as _,
transaction,
)
}
}
impl TargetHeaderChain<ToMillauMessagePayload, bp_rialto_parachain::AccountId> for Millau {
@@ -249,7 +159,7 @@ impl TargetHeaderChain<ToMillauMessagePayload, bp_rialto_parachain::AccountId> f
}
}
impl SourceHeaderChain<bp_millau::Balance> for Millau {
impl SourceHeaderChain for Millau {
type Error = &'static str;
// The proof is:
// - hash of the header this proof has been created with;
@@ -261,25 +171,8 @@ impl SourceHeaderChain<bp_millau::Balance> for Millau {
fn verify_messages_proof(
proof: Self::MessagesProof,
messages_count: u32,
) -> Result<ProvedMessages<Message<bp_millau::Balance>>, Self::Error> {
) -> Result<ProvedMessages<Message>, Self::Error> {
messages::target::verify_messages_proof::<WithMillauMessageBridge>(proof, messages_count)
.map_err(Into::into)
}
}
/// RialtoParachain -> Millau message lane pallet parameters.
#[derive(RuntimeDebug, Clone, Encode, Decode, PartialEq, Eq, TypeInfo)]
pub enum RialtoParachainToMillauMessagesParameter {
/// The conversion formula we use is: `RialtoParachainTokens = MillauTokens * conversion_rate`.
MillauToRialtoParachainConversionRate(FixedU128),
}
impl MessagesParameter for RialtoParachainToMillauMessagesParameter {
fn save(&self) {
match *self {
RialtoParachainToMillauMessagesParameter::MillauToRialtoParachainConversionRate(
ref conversion_rate,
) => MillauToRialtoParachainConversionRate::set(conversion_rate),
}
}
}
+5 -29
View File
@@ -32,13 +32,8 @@ pub mod millau_messages;
pub mod parachains;
pub mod xcm_config;
use crate::millau_messages::{ToMillauMessagePayload, WithMillauMessageBridge};
use beefy_primitives::{crypto::AuthorityId as BeefyId, mmr::MmrLeafVersion, ValidatorSet};
use bp_runtime::{HeaderId, HeaderIdProvider};
use bridge_runtime_common::messages::{
source::estimate_message_dispatch_and_delivery_fee, MessageBridge,
};
use pallet_grandpa::{
fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList,
};
@@ -54,7 +49,7 @@ use sp_runtime::{
create_runtime_str, generic, impl_opaque_keys,
traits::{AccountIdLookup, Block as BlockT, Keccak256, NumberFor, OpaqueKeys},
transaction_validity::{TransactionSource, TransactionValidity},
ApplyExtrinsicResult, FixedPointNumber, FixedU128, Perquintill,
ApplyExtrinsicResult, FixedPointNumber, Perquintill,
};
use sp_std::{collections::btree_map::BTreeMap, prelude::*};
#[cfg(feature = "std")]
@@ -445,9 +440,6 @@ parameter_types! {
bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX;
pub const MaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce =
bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX;
// `IdentityFee` is used by Rialto => we may use weight directly
pub const GetDeliveryConfirmationTransactionFee: Balance =
bp_rialto::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT.ref_time() as _;
pub const RootAccountForPayments: Option<AccountId> = None;
pub const BridgedChainId: bp_runtime::ChainId = bp_runtime::MILLAU_CHAIN_ID;
}
@@ -458,17 +450,14 @@ pub type WithMillauMessagesInstance = ();
impl pallet_bridge_messages::Config<WithMillauMessagesInstance> for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = pallet_bridge_messages::weights::BridgeWeight<Runtime>;
type Parameter = millau_messages::RialtoToMillauMessagesParameter;
type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce;
type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane;
type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane;
type MaximalOutboundPayloadSize = crate::millau_messages::ToMillauMaximalOutboundPayloadSize;
type OutboundPayload = crate::millau_messages::ToMillauMessagePayload;
type OutboundMessageFee = Balance;
type InboundPayload = crate::millau_messages::FromMillauMessagePayload;
type InboundMessageFee = bp_millau::Balance;
type InboundRelayer = bp_millau::AccountId;
type TargetHeaderChain = crate::millau_messages::Millau;
@@ -477,7 +466,6 @@ impl pallet_bridge_messages::Config<WithMillauMessagesInstance> for Runtime {
pallet_bridge_relayers::MessageDeliveryAndDispatchPaymentAdapter<
Runtime,
WithMillauMessagesInstance,
GetDeliveryConfirmationTransactionFee,
>;
type OnMessageAccepted = ();
type OnDeliveryConfirmed = ();
@@ -958,24 +946,12 @@ impl_runtime_apis! {
}
}
impl bp_millau::ToMillauOutboundLaneApi<Block, Balance, ToMillauMessagePayload> for Runtime {
fn estimate_message_delivery_and_dispatch_fee(
_lane_id: bp_messages::LaneId,
payload: ToMillauMessagePayload,
millau_to_this_conversion_rate: Option<FixedU128>,
) -> Option<Balance> {
estimate_message_dispatch_and_delivery_fee::<WithMillauMessageBridge>(
&payload,
WithMillauMessageBridge::RELAYER_FEE_PERCENT,
millau_to_this_conversion_rate,
).ok()
}
impl bp_millau::ToMillauOutboundLaneApi<Block> for Runtime {
fn message_details(
lane: bp_messages::LaneId,
begin: bp_messages::MessageNonce,
end: bp_messages::MessageNonce,
) -> Vec<bp_messages::OutboundMessageDetails<Balance>> {
) -> Vec<bp_messages::OutboundMessageDetails> {
bridge_runtime_common::messages_api::outbound_message_details::<
Runtime,
WithMillauMessagesInstance,
@@ -983,10 +959,10 @@ impl_runtime_apis! {
}
}
impl bp_millau::FromMillauInboundLaneApi<Block, bp_millau::Balance> for Runtime {
impl bp_millau::FromMillauInboundLaneApi<Block> for Runtime {
fn message_details(
lane: bp_messages::LaneId,
messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails<bp_millau::Balance>)>,
messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>,
) -> Vec<bp_messages::InboundMessageDetails> {
bridge_runtime_common::messages_api::inbound_message_details::<
Runtime,
@@ -21,33 +21,18 @@ use crate::{MillauGrandpaInstance, OriginCaller, Runtime, RuntimeCall, RuntimeOr
use bp_messages::{
source_chain::TargetHeaderChain,
target_chain::{ProvedMessages, SourceHeaderChain},
InboundLaneData, LaneId, Message, MessageNonce, Parameter as MessagesParameter,
InboundLaneData, LaneId, Message, MessageNonce,
};
use bp_runtime::{ChainId, MILLAU_CHAIN_ID, RIALTO_CHAIN_ID};
use bridge_runtime_common::messages::{
self, BasicConfirmationTransactionEstimation, MessageBridge, MessageTransaction,
};
use codec::{Decode, Encode};
use frame_support::{dispatch::DispatchClass, parameter_types, weights::Weight, RuntimeDebug};
use scale_info::TypeInfo;
use sp_runtime::{traits::Saturating, FixedPointNumber, FixedU128};
use sp_std::convert::TryFrom;
use bridge_runtime_common::messages::{self, MessageBridge};
use frame_support::{parameter_types, weights::Weight, RuntimeDebug};
/// Initial value of `MillauToRialtoConversionRate` parameter.
pub const INITIAL_MILLAU_TO_RIALTO_CONVERSION_RATE: FixedU128 =
FixedU128::from_inner(FixedU128::DIV);
/// Initial value of `MillauFeeMultiplier` parameter.
pub const INITIAL_MILLAU_FEE_MULTIPLIER: FixedU128 = FixedU128::from_inner(FixedU128::DIV);
/// Weight of 2 XCM instructions is for simple `Trap(42)` program, coming through bridge
/// (it is prepended with `UniversalOrigin` instruction). It is used just for simplest manual
/// tests, confirming that we don't break encoding somewhere between.
pub const BASE_XCM_WEIGHT_TWICE: u64 = 2 * crate::xcm_config::BASE_XCM_WEIGHT;
parameter_types! {
/// Millau to Rialto conversion rate. Initially we treat both tokens as equal.
pub storage MillauToRialtoConversionRate: FixedU128 = INITIAL_MILLAU_TO_RIALTO_CONVERSION_RATE;
/// Fee multiplier value at Millau chain.
pub storage MillauFeeMultiplier: FixedU128 = INITIAL_MILLAU_FEE_MULTIPLIER;
/// Weight credit for our test messages.
///
/// 2 XCM instructions is for simple `Trap(42)` program, coming through bridge
@@ -89,7 +74,6 @@ pub type ToMillauMaximalOutboundPayloadSize =
pub struct WithMillauMessageBridge;
impl MessageBridge for WithMillauMessageBridge {
const RELAYER_FEE_PERCENT: u32 = 10;
const THIS_CHAIN_ID: ChainId = RIALTO_CHAIN_ID;
const BRIDGED_CHAIN_ID: ChainId = MILLAU_CHAIN_ID;
const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_rialto::WITH_RIALTO_MESSAGES_PALLET_NAME;
@@ -98,16 +82,6 @@ impl MessageBridge for WithMillauMessageBridge {
type BridgedChain = Millau;
type BridgedHeaderChain =
pallet_bridge_grandpa::GrandpaChainHeaders<Runtime, MillauGrandpaInstance>;
fn bridged_balance_to_this_balance(
bridged_balance: bp_millau::Balance,
bridged_to_this_conversion_rate_override: Option<FixedU128>,
) -> bp_rialto::Balance {
let conversion_rate = bridged_to_this_conversion_rate_override
.unwrap_or_else(MillauToRialtoConversionRate::get);
bp_rialto::Balance::try_from(conversion_rate.saturating_mul_int(bridged_balance))
.unwrap_or(bp_rialto::Balance::MAX)
}
}
/// Rialto chain from message lane point of view.
@@ -121,12 +95,6 @@ impl messages::UnderlyingChainProvider for Rialto {
impl messages::ThisChainWithMessages for Rialto {
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
type ConfirmationTransactionEstimation = BasicConfirmationTransactionEstimation<
bp_rialto::AccountId,
{ bp_rialto::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT.ref_time() },
{ bp_millau::EXTRA_STORAGE_PROOF_SIZE },
{ bp_rialto::TX_EXTRA_BYTES },
>;
fn is_message_accepted(send_origin: &Self::RuntimeOrigin, lane: &LaneId) -> bool {
let here_location =
@@ -151,21 +119,6 @@ impl messages::ThisChainWithMessages for Rialto {
fn maximal_pending_messages_at_outbound_lane() -> MessageNonce {
MessageNonce::MAX
}
fn transaction_payment(transaction: MessageTransaction<Weight>) -> bp_rialto::Balance {
// `transaction` may represent transaction from the future, when multiplier value will
// be larger, so let's use slightly increased value
let multiplier = FixedU128::saturating_from_rational(110, 100)
.saturating_mul(pallet_transaction_payment::Pallet::<Runtime>::next_fee_multiplier());
// in our testnets, both per-byte fee and weight-to-fee are 1:1
messages::transaction_payment(
bp_rialto::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic,
1,
multiplier,
|weight| weight.ref_time() as _,
transaction,
)
}
}
/// Millau chain from message lane point of view.
@@ -180,45 +133,6 @@ impl messages::BridgedChainWithMessages for Millau {
fn verify_dispatch_weight(_message_payload: &[u8]) -> bool {
true
}
fn estimate_delivery_transaction(
message_payload: &[u8],
include_pay_dispatch_fee_cost: bool,
message_dispatch_weight: Weight,
) -> MessageTransaction<Weight> {
let message_payload_len = u32::try_from(message_payload.len()).unwrap_or(u32::MAX);
let extra_bytes_in_payload = message_payload_len
.saturating_sub(pallet_bridge_messages::EXPECTED_DEFAULT_MESSAGE_LENGTH);
MessageTransaction {
dispatch_weight: bp_millau::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT
.saturating_mul(extra_bytes_in_payload as u64)
.saturating_add(bp_millau::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT)
.saturating_sub(if include_pay_dispatch_fee_cost {
Weight::from_ref_time(0)
} else {
bp_millau::PAY_INBOUND_DISPATCH_FEE_WEIGHT
})
.saturating_add(message_dispatch_weight),
size: message_payload_len
.saturating_add(bp_rialto::EXTRA_STORAGE_PROOF_SIZE)
.saturating_add(bp_millau::TX_EXTRA_BYTES),
}
}
fn transaction_payment(transaction: MessageTransaction<Weight>) -> bp_millau::Balance {
// we don't have a direct access to the value of multiplier at Millau chain
// => it is a messages module parameter
let multiplier = MillauFeeMultiplier::get();
// in our testnets, both per-byte fee and weight-to-fee are 1:1
messages::transaction_payment(
bp_millau::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic,
1,
multiplier,
|weight| weight.ref_time() as _,
transaction,
)
}
}
impl TargetHeaderChain<ToMillauMessagePayload, bp_rialto::AccountId> for Millau {
@@ -240,7 +154,7 @@ impl TargetHeaderChain<ToMillauMessagePayload, bp_rialto::AccountId> for Millau
}
}
impl SourceHeaderChain<bp_millau::Balance> for Millau {
impl SourceHeaderChain for Millau {
type Error = &'static str;
// The proof is:
// - hash of the header this proof has been created with;
@@ -252,28 +166,12 @@ impl SourceHeaderChain<bp_millau::Balance> for Millau {
fn verify_messages_proof(
proof: Self::MessagesProof,
messages_count: u32,
) -> Result<ProvedMessages<Message<bp_millau::Balance>>, Self::Error> {
) -> Result<ProvedMessages<Message>, Self::Error> {
messages::target::verify_messages_proof::<WithMillauMessageBridge>(proof, messages_count)
.map_err(Into::into)
}
}
/// Rialto -> Millau message lane pallet parameters.
#[derive(RuntimeDebug, Clone, Encode, Decode, PartialEq, Eq, TypeInfo)]
pub enum RialtoToMillauMessagesParameter {
/// The conversion formula we use is: `RialtoTokens = MillauTokens * conversion_rate`.
MillauToRialtoConversionRate(FixedU128),
}
impl MessagesParameter for RialtoToMillauMessagesParameter {
fn save(&self) {
match *self {
RialtoToMillauMessagesParameter::MillauToRialtoConversionRate(ref conversion_rate) =>
MillauToRialtoConversionRate::set(conversion_rate),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -291,13 +189,7 @@ mod tests {
fn ensure_rialto_message_lane_weights_are_correct() {
type Weights = pallet_bridge_messages::weights::BridgeWeight<Runtime>;
pallet_bridge_messages::ensure_weights_are_correct::<Weights>(
bp_rialto::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT,
bp_rialto::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT,
bp_rialto::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT,
bp_rialto::PAY_INBOUND_DISPATCH_FEE_WEIGHT,
DbWeight::get(),
);
pallet_bridge_messages::ensure_weights_are_correct::<Weights>();
let max_incoming_message_proof_size = bp_millau::EXTRA_STORAGE_PROOF_SIZE.saturating_add(
messages::target::maximal_incoming_message_size(bp_rialto::Rialto::max_extrinsic_size()),
@@ -362,13 +254,5 @@ mod tests {
bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME,
},
});
assert_eq!(
MillauToRialtoConversionRate::key().to_vec(),
bp_runtime::storage_parameter_key(
bp_rialto::MILLAU_TO_RIALTO_CONVERSION_RATE_PARAMETER_NAME
)
.0,
);
}
}
+2 -2
View File
@@ -238,7 +238,7 @@ mod tests {
let xcm: Xcm<()> = vec![Instruction::Trap(42)].into();
let send_result = send_xcm::<XcmRouter>(dest.into(), xcm);
let expected_fee = MultiAssets::from((Here, 4_259_858_152_u128));
let expected_fee = MultiAssets::from((Here, 1_000_000_u128));
let expected_hash =
([0u8, 0u8, 0u8, 0u8], 1u64).using_encoded(sp_io::hashing::blake2_256);
assert_eq!(send_result, Ok((expected_hash, expected_fee)),);
@@ -262,7 +262,7 @@ mod tests {
let mut incoming_message = DispatchMessage {
key: MessageKey { lane_id: [0, 0, 0, 0], nonce: 1 },
data: DispatchMessageData { payload: Ok((location, xcm).into()), fee: 0 },
data: DispatchMessageData { payload: Ok((location, xcm).into()) },
};
let dispatch_weight = MessageDispatcher::dispatch_weight(&mut incoming_message);
@@ -88,10 +88,8 @@ macro_rules! assert_bridge_messages_pallet_types(
use static_assertions::assert_type_eq_all;
assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundPayload, FromThisChainMessagePayload);
assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundMessageFee, BalanceOf<ThisChain<$bridge>>);
assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundPayload, FromBridgedChainMessagePayload<CallOf<ThisChain<$bridge>>>);
assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundMessageFee, BalanceOf<BridgedChain<$bridge>>);
assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundRelayer, AccountIdOf<BridgedChain<$bridge>>);
assert_type_eq_all!(<$r as MessagesConfig<$i>>::TargetHeaderChain, BridgedChain<$bridge>);
+23 -379
View File
@@ -24,26 +24,19 @@ use bp_header_chain::{HeaderChain, HeaderChainError};
use bp_messages::{
source_chain::LaneMessageVerifier,
target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages},
InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce, OutboundLaneData,
InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData,
};
use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, Size, StorageProofChecker};
use codec::{Decode, DecodeLimit, Encode, MaxEncodedLen};
use codec::{Decode, DecodeLimit, Encode};
use frame_support::{traits::Get, weights::Weight, RuntimeDebug};
use hash_db::Hasher;
use scale_info::TypeInfo;
use sp_runtime::{
traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedDiv, CheckedMul},
FixedPointNumber, FixedPointOperand, FixedU128,
};
use sp_std::{convert::TryFrom, fmt::Debug, marker::PhantomData, vec::Vec};
use sp_trie::StorageProof;
use xcm::latest::prelude::*;
/// Bidirectional message bridge.
pub trait MessageBridge {
/// Relayer interest (in percents).
const RELAYER_FEE_PERCENT: u32;
/// Identifier of this chain.
const THIS_CHAIN_ID: ChainId;
/// Identifier of the Bridged chain.
@@ -59,60 +52,6 @@ pub trait MessageBridge {
type BridgedChain: BridgedChainWithMessages;
/// Bridged header chain.
type BridgedHeaderChain: HeaderChain<UnderlyingChainOf<Self::BridgedChain>>;
/// Convert Bridged chain balance into This chain balance.
fn bridged_balance_to_this_balance(
bridged_balance: BalanceOf<BridgedChain<Self>>,
bridged_to_this_conversion_rate_override: Option<FixedU128>,
) -> BalanceOf<ThisChain<Self>>;
}
/// Message related transaction parameters estimation.
#[derive(RuntimeDebug)]
pub struct MessageTransaction<Weight> {
/// The estimated dispatch weight of the transaction.
pub dispatch_weight: Weight,
/// The estimated size of the encoded transaction.
pub size: u32,
}
/// Helper trait for estimating the size and weight of a single message delivery confirmation
/// transaction.
pub trait ConfirmationTransactionEstimation<Weight> {
// Estimate size and weight of single message delivery confirmation transaction.
fn estimate_delivery_confirmation_transaction() -> MessageTransaction<Weight>;
}
/// Default implementation for `ConfirmationTransactionEstimation`.
pub struct BasicConfirmationTransactionEstimation<
AccountId: MaxEncodedLen,
const MAX_CONFIRMATION_TX_WEIGHT: u64,
const EXTRA_STORAGE_PROOF_SIZE: u32,
const TX_EXTRA_BYTES: u32,
>(PhantomData<AccountId>);
impl<
AccountId: MaxEncodedLen,
const MAX_CONFIRMATION_TX_WEIGHT: u64,
const EXTRA_STORAGE_PROOF_SIZE: u32,
const TX_EXTRA_BYTES: u32,
> ConfirmationTransactionEstimation<Weight>
for BasicConfirmationTransactionEstimation<
AccountId,
MAX_CONFIRMATION_TX_WEIGHT,
EXTRA_STORAGE_PROOF_SIZE,
TX_EXTRA_BYTES,
>
{
fn estimate_delivery_confirmation_transaction() -> MessageTransaction<Weight> {
let inbound_data_size = InboundLaneData::<AccountId>::encoded_size_hint_u32(1, 1);
MessageTransaction {
dispatch_weight: Weight::from_ref_time(MAX_CONFIRMATION_TX_WEIGHT),
size: inbound_data_size
.saturating_add(EXTRA_STORAGE_PROOF_SIZE)
.saturating_add(TX_EXTRA_BYTES),
}
}
}
/// A trait that provides the type of the underlying chain.
@@ -127,9 +66,6 @@ pub trait ThisChainWithMessages: UnderlyingChainProvider {
type RuntimeOrigin;
/// Call type on the chain.
type RuntimeCall: Encode + Decode;
/// Helper for estimating the size and weight of a single message delivery confirmation
/// transaction at this chain.
type ConfirmationTransactionEstimation: ConfirmationTransactionEstimation<Weight>;
/// Do we accept message sent by given origin to given lane?
fn is_message_accepted(origin: &Self::RuntimeOrigin, lane: &LaneId) -> bool;
@@ -138,14 +74,6 @@ pub trait ThisChainWithMessages: UnderlyingChainProvider {
///
/// Any messages over this limit, will be rejected.
fn maximal_pending_messages_at_outbound_lane() -> MessageNonce;
/// Estimate size and weight of single message delivery confirmation transaction at This chain.
fn estimate_delivery_confirmation_transaction() -> MessageTransaction<Weight> {
Self::ConfirmationTransactionEstimation::estimate_delivery_confirmation_transaction()
}
/// Returns minimal transaction fee that must be paid for given transaction at This chain.
fn transaction_payment(transaction: MessageTransaction<Weight>) -> BalanceOf<Self>;
}
/// Bridged chain that has `pallet-bridge-messages` module.
@@ -153,17 +81,6 @@ pub trait BridgedChainWithMessages: UnderlyingChainProvider {
/// Returns `true` if message dispatch weight is withing expected limits. `false` means
/// that the message is too heavy to be sent over the bridge and shall be rejected.
fn verify_dispatch_weight(message_payload: &[u8]) -> bool;
/// Estimate size and weight of single message delivery transaction at the Bridged chain.
fn estimate_delivery_transaction(
message_payload: &[u8],
include_pay_dispatch_fee_cost: bool,
message_dispatch_weight: Weight,
) -> MessageTransaction<Weight>;
/// Returns minimal transaction fee that must be paid for given transaction at the Bridged
/// chain.
fn transaction_payment(transaction: MessageTransaction<Weight>) -> BalanceOf<Self>;
}
/// This chain in context of message bridge.
@@ -188,33 +105,6 @@ pub type CallOf<C> = <C as ThisChainWithMessages>::RuntimeCall;
/// Raw storage proof type (just raw trie nodes).
pub type RawStorageProof = Vec<Vec<u8>>;
/// Compute fee of transaction at runtime where regular transaction payment pallet is being used.
///
/// The value of `multiplier` parameter is the expected value of
/// `pallet_transaction_payment::NextFeeMultiplier` at the moment when transaction is submitted. If
/// you're charging this payment in advance (and that's what happens with delivery and confirmation
/// transaction in this crate), then there's a chance that the actual fee will be larger than what
/// is paid in advance. So the value must be chosen carefully.
pub fn transaction_payment<Balance: AtLeast32BitUnsigned + FixedPointOperand>(
base_extrinsic_weight: Weight,
per_byte_fee: Balance,
multiplier: FixedU128,
weight_to_fee: impl Fn(Weight) -> Balance,
transaction: MessageTransaction<Weight>,
) -> Balance {
// base fee is charged for every tx
let base_fee = weight_to_fee(base_extrinsic_weight);
// non-adjustable per-byte fee
let len_fee = per_byte_fee.saturating_mul(Balance::from(transaction.size));
// the adjustable part of the fee
let unadjusted_weight_fee = weight_to_fee(transaction.dispatch_weight);
let adjusted_weight_fee = multiplier.saturating_mul_int(unadjusted_weight_fee);
base_fee.saturating_add(len_fee).saturating_add(adjusted_weight_fee)
}
/// Sub-module that is declaring types required for processing This -> Bridged chain messages.
pub mod source {
use super::*;
@@ -286,15 +176,9 @@ pub mod source {
pub const TOO_MANY_PENDING_MESSAGES: &str = "Too many pending messages at the lane.";
/// The error message returned from LaneMessageVerifier when call origin is mismatch.
pub const BAD_ORIGIN: &str = "Unable to match the source origin to expected target origin.";
/// The error message returned from LaneMessageVerifier when the message fee is too low.
pub const TOO_LOW_FEE: &str = "Provided fee is below minimal threshold required by the lane.";
impl<B>
LaneMessageVerifier<
OriginOf<ThisChain<B>>,
FromThisChainMessagePayload,
BalanceOf<ThisChain<B>>,
> for FromThisChainMessageVerifier<B>
impl<B> LaneMessageVerifier<OriginOf<ThisChain<B>>, FromThisChainMessagePayload>
for FromThisChainMessageVerifier<B>
where
B: MessageBridge,
// matches requirements from the `frame_system::Config::Origin`
@@ -306,10 +190,9 @@ pub mod source {
fn verify_message(
submitter: &OriginOf<ThisChain<B>>,
delivery_and_dispatch_fee: &BalanceOf<ThisChain<B>>,
lane: &LaneId,
lane_outbound_data: &OutboundLaneData,
payload: &FromThisChainMessagePayload,
_payload: &FromThisChainMessagePayload,
) -> Result<(), Self::Error> {
// reject message if lane is blocked
if !ThisChain::<B>::is_message_accepted(submitter, lane) {
@@ -325,17 +208,6 @@ pub mod source {
return Err(TOO_MANY_PENDING_MESSAGES)
}
let minimal_fee_in_this_tokens = estimate_message_dispatch_and_delivery_fee::<B>(
payload,
B::RELAYER_FEE_PERCENT,
None,
)?;
// compare with actual fee paid
if *delivery_and_dispatch_fee < minimal_fee_in_this_tokens {
return Err(TOO_LOW_FEE)
}
Ok(())
}
}
@@ -376,52 +248,6 @@ pub mod source {
Ok(())
}
/// Estimate delivery and dispatch fee that must be paid for delivering a message to the Bridged
/// chain.
///
/// The fee is paid in This chain Balance, but we use Bridged chain balance to avoid additional
/// conversions. Returns `None` if overflow has happened.
pub fn estimate_message_dispatch_and_delivery_fee<B: MessageBridge>(
payload: &FromThisChainMessagePayload,
relayer_fee_percent: u32,
bridged_to_this_conversion_rate: Option<FixedU128>,
) -> Result<BalanceOf<ThisChain<B>>, &'static str> {
// the fee (in Bridged tokens) of all transactions that are made on the Bridged chain
//
// if we're going to pay dispatch fee at the target chain, then we don't include weight
// of the message dispatch in the delivery transaction cost
let delivery_transaction = BridgedChain::<B>::estimate_delivery_transaction(
&payload.encode(),
true,
Weight::zero(),
);
let delivery_transaction_fee = BridgedChain::<B>::transaction_payment(delivery_transaction);
// the fee (in This tokens) of all transactions that are made on This chain
let confirmation_transaction = ThisChain::<B>::estimate_delivery_confirmation_transaction();
let confirmation_transaction_fee =
ThisChain::<B>::transaction_payment(confirmation_transaction);
// minimal fee (in This tokens) is a sum of all required fees
let minimal_fee = B::bridged_balance_to_this_balance(
delivery_transaction_fee,
bridged_to_this_conversion_rate,
)
.checked_add(&confirmation_transaction_fee);
// before returning, add extra fee that is paid to the relayer (relayer interest)
minimal_fee
.and_then(|fee|
// having message with fee that is near the `Balance::MAX_VALUE` of the chain is
// unlikely and should be treated as an error
// => let's do multiplication first
fee
.checked_mul(&relayer_fee_percent.into())
.and_then(|interest| interest.checked_div(&100u32.into()))
.and_then(|interest| fee.checked_add(&interest)))
.ok_or("Overflow when computing minimal required message delivery and dispatch fee")
}
/// Verify proof of This -> Bridged chain messages delivery.
///
/// This function is used when Bridged chain is directly using GRANDPA finality. For Bridged
@@ -462,7 +288,6 @@ pub mod source {
/// Runtime message sender adapter.
type MessageSender: bp_messages::source_chain::MessagesBridge<
OriginOf<ThisChain<Self::MessageBridge>>,
BalanceOf<ThisChain<Self::MessageBridge>>,
FromThisChainMessagePayload,
>;
@@ -484,7 +309,7 @@ pub mod source {
BalanceOf<ThisChain<T::MessageBridge>>: Into<Fungibility>,
OriginOf<ThisChain<T::MessageBridge>>: From<pallet_xcm::Origin>,
{
type Ticket = (BalanceOf<ThisChain<T::MessageBridge>>, FromThisChainMessagePayload);
type Ticket = FromThisChainMessagePayload;
fn validate(
dest: &mut Option<MultiLocation>,
@@ -499,39 +324,22 @@ pub mod source {
let route = T::build_destination();
let msg = (route, msg.take().ok_or(SendError::MissingArgument)?).encode();
let fee = estimate_message_dispatch_and_delivery_fee::<T::MessageBridge>(
&msg,
T::MessageBridge::RELAYER_FEE_PERCENT,
None,
);
let fee = match fee {
Ok(fee) => fee,
Err(e) => {
log::trace!(
target: "runtime::bridge",
"Failed to comupte fee for XCM message to {:?}: {:?}",
T::MessageBridge::BRIDGED_CHAIN_ID,
e,
);
*dest = Some(d);
return Err(SendError::Transport(e))
},
};
let fee_assets = MultiAssets::from((Here, fee));
// let's just take fixed (out of thin air) fee per message in our test bridges
// (this code won't be used in production anyway)
let fee_assets = MultiAssets::from((Here, 1_000_000_u128));
Ok(((fee, msg), fee_assets))
Ok((msg, fee_assets))
}
fn deliver(ticket: Self::Ticket) -> Result<XcmHash, SendError> {
use bp_messages::source_chain::MessagesBridge;
let lane = T::xcm_lane();
let (fee, msg) = ticket;
let msg = ticket;
let result = T::MessageSender::send_message(
pallet_xcm::Origin::from(MultiLocation::from(T::universal_location())).into(),
lane,
msg,
fee,
);
result
.map(|artifacts| {
@@ -633,7 +441,7 @@ pub mod target {
}
impl<B: MessageBridge, XcmExecutor, XcmWeigher, WeightCredit>
MessageDispatch<AccountIdOf<ThisChain<B>>, BalanceOf<BridgedChain<B>>>
MessageDispatch<AccountIdOf<ThisChain<B>>>
for FromBridgedChainMessageDispatch<B, XcmExecutor, XcmWeigher, WeightCredit>
where
XcmExecutor: xcm::v3::ExecuteXcm<CallOf<ThisChain<B>>>,
@@ -643,7 +451,7 @@ pub mod target {
type DispatchPayload = FromBridgedChainMessagePayload<CallOf<ThisChain<B>>>;
fn dispatch_weight(
message: &mut DispatchMessage<Self::DispatchPayload, BalanceOf<BridgedChain<B>>>,
message: &mut DispatchMessage<Self::DispatchPayload>,
) -> frame_support::weights::Weight {
match message.data.payload {
Ok(ref mut payload) => {
@@ -673,7 +481,7 @@ pub mod target {
fn dispatch(
_relayer_account: &AccountIdOf<ThisChain<B>>,
message: DispatchMessage<Self::DispatchPayload, BalanceOf<BridgedChain<B>>>,
message: DispatchMessage<Self::DispatchPayload>,
) -> MessageDispatchResult {
let message_id = (message.key.lane_id, message.key.nonce);
let do_dispatch = move || -> sp_std::result::Result<Outcome, codec::Error> {
@@ -738,7 +546,7 @@ pub mod target {
pub fn verify_messages_proof<B: MessageBridge>(
proof: FromBridgedChainMessagesProof<HashOf<BridgedChain<B>>>,
messages_count: u32,
) -> Result<ProvedMessages<Message<BalanceOf<BridgedChain<B>>>>, MessageProofError> {
) -> Result<ProvedMessages<Message>, MessageProofError> {
let FromBridgedChainMessagesProof {
bridged_header_hash,
storage_proof,
@@ -779,11 +587,9 @@ pub mod target {
let raw_message_data = parser
.read_raw_message(&message_key)
.ok_or(MessageProofError::MissingRequiredMessage)?;
let message_data = MessageData::<BalanceOf<BridgedChain<B>>>::decode(
&mut &raw_message_data[..],
)
.map_err(|_| MessageProofError::FailedToDecodeMessage)?;
messages.push(Message { key: message_key, data: message_data });
let payload = MessagePayload::decode(&mut &raw_message_data[..])
.map_err(|_| MessageProofError::FailedToDecodeMessage)?;
messages.push(Message { key: message_key, payload });
}
// Now let's check if proof contains outbound lane state proof. It is optional, so
@@ -885,11 +691,6 @@ mod tests {
use sp_runtime::traits::{BlakeTwo256, Header as _};
use std::cell::RefCell;
const DELIVERY_TRANSACTION_WEIGHT: Weight = Weight::from_ref_time(100);
const DELIVERY_CONFIRMATION_TRANSACTION_WEIGHT: u64 = 100;
const THIS_CHAIN_WEIGHT_TO_BALANCE_RATE: u32 = 2;
const BRIDGED_CHAIN_WEIGHT_TO_BALANCE_RATE: u32 = 4;
const BRIDGED_CHAIN_TO_THIS_CHAIN_BALANCE_RATE: u32 = 6;
const BRIDGED_CHAIN_MIN_EXTRINSIC_WEIGHT: usize = 5;
const BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT: usize = 2048;
const BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE: u32 = 1024;
@@ -900,7 +701,6 @@ mod tests {
struct OnThisChainBridge;
impl MessageBridge for OnThisChainBridge {
const RELAYER_FEE_PERCENT: u32 = 10;
const THIS_CHAIN_ID: ChainId = *b"this";
const BRIDGED_CHAIN_ID: ChainId = *b"brdg";
const BRIDGED_MESSAGES_PALLET_NAME: &'static str = "";
@@ -908,16 +708,6 @@ mod tests {
type ThisChain = ThisChain;
type BridgedChain = BridgedChain;
type BridgedHeaderChain = BridgedHeaderChain;
fn bridged_balance_to_this_balance(
bridged_balance: BridgedChainBalance,
bridged_to_this_conversion_rate_override: Option<FixedU128>,
) -> ThisChainBalance {
let conversion_rate = bridged_to_this_conversion_rate_override
.map(|r| r.to_float() as u32)
.unwrap_or(BRIDGED_CHAIN_TO_THIS_CHAIN_BALANCE_RATE);
bridged_balance as ThisChainBalance * conversion_rate as ThisChainBalance
}
}
/// Bridge that is deployed on BridgedChain and allows sending/receiving messages to/from
@@ -926,7 +716,6 @@ mod tests {
struct OnBridgedChainBridge;
impl MessageBridge for OnBridgedChainBridge {
const RELAYER_FEE_PERCENT: u32 = 20;
const THIS_CHAIN_ID: ChainId = *b"brdg";
const BRIDGED_CHAIN_ID: ChainId = *b"this";
const BRIDGED_MESSAGES_PALLET_NAME: &'static str = "";
@@ -934,13 +723,6 @@ mod tests {
type ThisChain = BridgedChain;
type BridgedChain = ThisChain;
type BridgedHeaderChain = ThisHeaderChain;
fn bridged_balance_to_this_balance(
_this_balance: ThisChainBalance,
_bridged_to_this_conversion_rate_override: Option<FixedU128>,
) -> BridgedChainBalance {
unreachable!()
}
}
#[derive(Clone, Debug)]
@@ -989,6 +771,7 @@ mod tests {
fn max_extrinsic_size() -> u32 {
BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE
}
fn max_extrinsic_weight() -> Weight {
Weight::zero()
}
@@ -1003,12 +786,6 @@ mod tests {
impl ThisChainWithMessages for ThisChain {
type RuntimeOrigin = ThisChainOrigin;
type RuntimeCall = ThisChainCall;
type ConfirmationTransactionEstimation = BasicConfirmationTransactionEstimation<
ThisChainAccountId,
{ DELIVERY_CONFIRMATION_TRANSACTION_WEIGHT },
0,
0,
>;
fn is_message_accepted(_send_origin: &Self::RuntimeOrigin, lane: &LaneId) -> bool {
lane == TEST_LANE_ID
@@ -1017,31 +794,12 @@ mod tests {
fn maximal_pending_messages_at_outbound_lane() -> MessageNonce {
MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE
}
fn transaction_payment(transaction: MessageTransaction<Weight>) -> BalanceOf<Self> {
transaction
.dispatch_weight
.saturating_mul(THIS_CHAIN_WEIGHT_TO_BALANCE_RATE as u64)
.ref_time() as _
}
}
impl BridgedChainWithMessages for ThisChain {
fn verify_dispatch_weight(_message_payload: &[u8]) -> bool {
unreachable!()
}
fn estimate_delivery_transaction(
_message_payload: &[u8],
_include_pay_dispatch_fee_cost: bool,
_message_dispatch_weight: Weight,
) -> MessageTransaction<Weight> {
unreachable!()
}
fn transaction_payment(_transaction: MessageTransaction<Weight>) -> BalanceOf<Self> {
unreachable!()
}
}
struct BridgedUnderlyingChain;
@@ -1078,8 +836,6 @@ mod tests {
impl ThisChainWithMessages for BridgedChain {
type RuntimeOrigin = BridgedChainOrigin;
type RuntimeCall = BridgedChainCall;
type ConfirmationTransactionEstimation =
BasicConfirmationTransactionEstimation<BridgedChainAccountId, 0, 0, 0>;
fn is_message_accepted(_send_origin: &Self::RuntimeOrigin, _lane: &LaneId) -> bool {
unreachable!()
@@ -1088,10 +844,6 @@ mod tests {
fn maximal_pending_messages_at_outbound_lane() -> MessageNonce {
unreachable!()
}
fn transaction_payment(_transaction: MessageTransaction<Weight>) -> BalanceOf<Self> {
unreachable!()
}
}
impl BridgedChainWithMessages for BridgedChain {
@@ -1099,24 +851,6 @@ mod tests {
message_payload.len() >= BRIDGED_CHAIN_MIN_EXTRINSIC_WEIGHT &&
message_payload.len() <= BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT
}
fn estimate_delivery_transaction(
_message_payload: &[u8],
_include_pay_dispatch_fee_cost: bool,
message_dispatch_weight: Weight,
) -> MessageTransaction<Weight> {
MessageTransaction {
dispatch_weight: DELIVERY_TRANSACTION_WEIGHT + message_dispatch_weight,
size: 0,
}
}
fn transaction_payment(transaction: MessageTransaction<Weight>) -> BalanceOf<Self> {
transaction
.dispatch_weight
.saturating_mul(BRIDGED_CHAIN_WEIGHT_TO_BALANCE_RATE as u64)
.ref_time() as _
}
}
thread_local! {
@@ -1152,50 +886,11 @@ mod tests {
vec![42]
}
#[test]
fn message_fee_is_checked_by_verifier() {
const EXPECTED_MINIMAL_FEE: u32 = 2860;
// payload of the This -> Bridged chain message
let payload = regular_outbound_message_payload();
// let's check if estimation matching hardcoded value
assert_eq!(
source::estimate_message_dispatch_and_delivery_fee::<OnThisChainBridge>(
&payload,
OnThisChainBridge::RELAYER_FEE_PERCENT,
None,
),
Ok(EXPECTED_MINIMAL_FEE),
);
// and now check that the verifier checks the fee
assert_eq!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&ThisChainOrigin(Ok(frame_system::RawOrigin::Root)),
&1,
TEST_LANE_ID,
&test_lane_outbound_data(),
&payload,
),
Err(source::TOO_LOW_FEE)
);
assert!(source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&ThisChainOrigin(Ok(frame_system::RawOrigin::Root)),
&1_000_000,
TEST_LANE_ID,
&test_lane_outbound_data(),
&payload,
)
.is_ok(),);
}
#[test]
fn message_is_rejected_when_sent_using_disabled_lane() {
assert_eq!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&ThisChainOrigin(Ok(frame_system::RawOrigin::Root)),
&1_000_000,
b"dsbl",
&test_lane_outbound_data(),
&regular_outbound_message_payload(),
@@ -1209,7 +904,6 @@ mod tests {
assert_eq!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&ThisChainOrigin(Ok(frame_system::RawOrigin::Root)),
&1_000_000,
TEST_LANE_ID,
&OutboundLaneData {
latest_received_nonce: 100,
@@ -1267,7 +961,7 @@ mod tests {
fn using_messages_proof<R>(
nonces_end: MessageNonce,
outbound_lane_data: Option<OutboundLaneData>,
encode_message: impl Fn(MessageNonce, &MessageData<BridgedChainBalance>) -> Option<Vec<u8>>,
encode_message: impl Fn(MessageNonce, &MessagePayload) -> Option<Vec<u8>>,
encode_outbound_lane_data: impl Fn(&OutboundLaneData) -> Vec<u8>,
test: impl Fn(target::FromBridgedChainMessagesProof<H256>) -> R,
) -> R {
@@ -1366,10 +1060,9 @@ mod tests {
|n, m| {
let mut m = m.encode();
if n == 5 {
m = MessageData { fee: 0, payload: vec![0u8; 42] }.encode();
m.truncate(2);
m = vec![42]
}
Some(m.encode())
Some(m)
},
encode_lane_data,
|proof| target::verify_messages_proof::<OnThisChainBridge>(proof, 10),
@@ -1464,7 +1157,7 @@ mod tests {
}),
messages: vec![Message {
key: MessageKey { lane_id: *TEST_LANE_ID, nonce: 1 },
data: MessageData { payload: vec![42], fee: 0 },
payload: vec![42],
}],
},
)]
@@ -1483,53 +1176,4 @@ mod tests {
Err(target::MessageProofError::MessagesCountMismatch),
);
}
#[test]
fn transaction_payment_works_with_zero_multiplier() {
use sp_runtime::traits::Zero;
assert_eq!(
transaction_payment(
Weight::from_ref_time(100),
10,
FixedU128::zero(),
|weight| weight.ref_time(),
MessageTransaction { size: 50, dispatch_weight: Weight::from_ref_time(777) },
),
100 + 50 * 10,
);
}
#[test]
fn transaction_payment_works_with_non_zero_multiplier() {
use sp_runtime::traits::One;
assert_eq!(
transaction_payment::<u64>(
Weight::from_ref_time(100),
10,
FixedU128::one(),
|weight| weight.ref_time(),
MessageTransaction { size: 50, dispatch_weight: Weight::from_ref_time(777) },
),
100 + 50 * 10 + 777,
);
}
#[test]
fn conversion_rate_override_works() {
let payload = regular_outbound_message_payload();
let regular_fee = source::estimate_message_dispatch_and_delivery_fee::<OnThisChainBridge>(
&payload,
OnThisChainBridge::RELAYER_FEE_PERCENT,
None,
);
let overrided_fee = source::estimate_message_dispatch_and_delivery_fee::<OnThisChainBridge>(
&payload,
OnThisChainBridge::RELAYER_FEE_PERCENT,
Some(FixedU128::from_float((BRIDGED_CHAIN_TO_THIS_CHAIN_BALANCE_RATE * 2) as f64)),
);
assert!(regular_fee < overrided_fee);
}
}
@@ -26,7 +26,7 @@ pub fn outbound_message_details<Runtime, MessagesPalletInstance>(
lane: LaneId,
begin: MessageNonce,
end: MessageNonce,
) -> Vec<OutboundMessageDetails<Runtime::OutboundMessageFee>>
) -> Vec<OutboundMessageDetails>
where
Runtime: pallet_bridge_messages::Config<MessagesPalletInstance>,
MessagesPalletInstance: 'static,
@@ -40,10 +40,7 @@ where
// dispatch message weight is always zero at the source chain, since we're paying for
// dispatch at the target chain
dispatch_weight: frame_support::weights::Weight::zero(),
size: message_data.payload.len() as _,
delivery_and_dispatch_fee: message_data.fee,
// we're delivering XCM messages here, so fee is always paid at the target chain
dispatch_fee_payment: bp_runtime::messages::DispatchFeePayment::AtTargetChain,
size: message_data.len() as _,
})
})
.collect()
@@ -52,7 +49,7 @@ where
/// Implementation of the `To*InboundLaneApi::message_details`.
pub fn inbound_message_details<Runtime, MessagesPalletInstance>(
lane: LaneId,
messages: Vec<(MessagePayload, OutboundMessageDetails<Runtime::InboundMessageFee>)>,
messages: Vec<(MessagePayload, OutboundMessageDetails)>,
) -> Vec<InboundMessageDetails>
where
Runtime: pallet_bridge_messages::Config<MessagesPalletInstance>,
@@ -21,8 +21,7 @@
use crate::{
messages::{
source::{FromBridgedChainMessagesDeliveryProof, FromThisChainMessagePayload},
target::FromBridgedChainMessagesProof,
source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
AccountIdOf, BalanceOf, BridgedChain, CallOf, HashOf, MessageBridge, ThisChain,
},
messages_generation::{
@@ -34,25 +33,12 @@ use bp_messages::storage_keys;
use bp_runtime::{record_all_trie_keys, StorageProofSize};
use codec::Encode;
use frame_support::{dispatch::GetDispatchInfo, weights::Weight};
use pallet_bridge_messages::benchmarking::{
MessageDeliveryProofParams, MessageParams, MessageProofParams,
};
use pallet_bridge_messages::benchmarking::{MessageDeliveryProofParams, MessageProofParams};
use sp_core::Hasher;
use sp_runtime::traits::{Header, MaybeSerializeDeserialize, Zero};
use sp_std::{fmt::Debug, prelude::*};
use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, Recorder, TrieMut};
/// Prepare outbound message for the `send_message` call.
pub fn prepare_outbound_message<B>(
params: MessageParams<AccountIdOf<ThisChain<B>>>,
) -> FromThisChainMessagePayload
where
B: MessageBridge,
BalanceOf<ThisChain<B>>: From<u64>,
{
vec![0; params.size as usize]
}
/// Prepare proof of messages for the `receive_messages_proof` call.
///
/// In addition to returning valid messages proof, environment is prepared to verify this message
@@ -31,7 +31,6 @@ use sp_runtime::transaction_validity::TransactionValidity;
impl<
BridgedHeaderHash,
SourceHeaderChain: bp_messages::target_chain::SourceHeaderChain<
<T as Config<I>>::InboundMessageFee,
MessagesProof = FromBridgedChainMessagesProof<BridgedHeaderHash>,
>,
TargetHeaderChain: bp_messages::source_chain::TargetHeaderChain<
@@ -18,23 +18,19 @@
#![cfg(any(feature = "runtime-benchmarks", test))]
use crate::messages::{BalanceOf, BridgedChain, HashOf, HasherOf, MessageBridge, RawStorageProof};
use crate::messages::{BridgedChain, HashOf, HasherOf, MessageBridge, RawStorageProof};
use bp_messages::{
storage_keys, LaneId, MessageData, MessageKey, MessageNonce, MessagePayload, OutboundLaneData,
storage_keys, LaneId, MessageKey, MessageNonce, MessagePayload, OutboundLaneData,
};
use bp_runtime::{record_all_trie_keys, StorageProofSize};
use codec::Encode;
use sp_core::Hasher;
use sp_runtime::traits::Zero;
use sp_std::{ops::RangeInclusive, prelude::*};
use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, Recorder, TrieMut};
/// Simple and correct message data encode function.
pub(crate) fn encode_all_messages<B: Encode>(
_: MessageNonce,
m: &MessageData<B>,
) -> Option<Vec<u8>> {
pub(crate) fn encode_all_messages(_: MessageNonce, m: &MessagePayload) -> Option<Vec<u8>> {
Some(m.encode())
}
@@ -52,7 +48,7 @@ pub(crate) fn prepare_messages_storage_proof<B>(
outbound_lane_data: Option<OutboundLaneData>,
size: StorageProofSize,
message_payload: MessagePayload,
encode_message: impl Fn(MessageNonce, &MessageData<BalanceOf<BridgedChain<B>>>) -> Option<Vec<u8>>,
encode_message: impl Fn(MessageNonce, &MessagePayload) -> Option<Vec<u8>>,
encode_outbound_lane_data: impl Fn(&OutboundLaneData) -> Vec<u8>,
) -> (HashOf<BridgedChain<B>>, RawStorageProof)
where
@@ -71,12 +67,8 @@ where
// insert messages
for nonce in message_nonces {
let message_key = MessageKey { lane_id: lane, nonce };
let message_data = MessageData {
fee: BalanceOf::<BridgedChain<B>>::zero(),
payload: message_payload.clone(),
};
let message_data = match encode_message(nonce, &message_data) {
Some(message_data) => message_data,
let message_payload = match encode_message(nonce, &message_payload) {
Some(message_payload) => message_payload,
None => continue,
};
let storage_key = storage_keys::message_key(
@@ -85,7 +77,7 @@ where
message_key.nonce,
)
.0;
trie.insert(&storage_key, &message_data)
trie.insert(&storage_key, &message_payload)
.map_err(|_| "TrieMut::insert has failed")
.expect("TrieMut::insert should not fail in benchmarks");
storage_keys.push(storage_key);
+7 -7
View File
@@ -17,7 +17,7 @@
//! Autogenerated weights for `pallet_bridge_grandpa`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2022-10-04, STEPS: 50, REPEAT: 20
//! DATE: 2022-11-17, STEPS: 50, REPEAT: 20
//! LOW RANGE: [], HIGH RANGE: []
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled
//! CHAIN: Some("dev"), DB CACHE: 1024
@@ -59,9 +59,9 @@ pub trait WeightInfo {
pub struct BridgeWeight<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for BridgeWeight<T> {
fn submit_finality_proof(p: u32, v: u32) -> Weight {
Weight::from_ref_time(105_417_000 as u64)
.saturating_add(Weight::from_ref_time(40_923_000 as u64).saturating_mul(p as u64))
.saturating_add(Weight::from_ref_time(1_691_000 as u64).saturating_mul(v as u64))
Weight::from_ref_time(198_274_668 as u64)
.saturating_add(Weight::from_ref_time(39_830_948 as u64).saturating_mul(p as u64))
.saturating_add(Weight::from_ref_time(1_535_681 as u64).saturating_mul(v as u64))
.saturating_add(T::DbWeight::get().reads(7 as u64))
.saturating_add(T::DbWeight::get().writes(6 as u64))
}
@@ -70,9 +70,9 @@ impl<T: frame_system::Config> WeightInfo for BridgeWeight<T> {
// For backwards compatibility and tests
impl WeightInfo for () {
fn submit_finality_proof(p: u32, v: u32) -> Weight {
Weight::from_ref_time(105_417_000 as u64)
.saturating_add(Weight::from_ref_time(40_923_000 as u64).saturating_mul(p as u64))
.saturating_add(Weight::from_ref_time(1_691_000 as u64).saturating_mul(v as u64))
Weight::from_ref_time(198_274_668 as u64)
.saturating_add(Weight::from_ref_time(39_830_948 as u64).saturating_mul(p as u64))
.saturating_add(Weight::from_ref_time(1_535_681 as u64).saturating_mul(v as u64))
.saturating_add(RocksDbWeight::get().reads(7 as u64))
.saturating_add(RocksDbWeight::get().writes(6 as u64))
}
+8 -216
View File
@@ -18,35 +18,25 @@
use crate::{
inbound_lane::InboundLaneStorage, inbound_lane_storage, outbound_lane,
outbound_lane::ReceivalConfirmationResult, weights_ext::EXPECTED_DEFAULT_MESSAGE_LENGTH, Call,
OutboundLanes, OutboundMessages,
weights_ext::EXPECTED_DEFAULT_MESSAGE_LENGTH, Call, OutboundLanes,
};
use bp_messages::{
source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, DeliveredMessages,
InboundLaneData, LaneId, MessageData, MessageKey, MessageNonce, OutboundLaneData,
UnrewardedRelayer, UnrewardedRelayersState,
InboundLaneData, LaneId, MessageNonce, OutboundLaneData, UnrewardedRelayer,
UnrewardedRelayersState,
};
use bp_runtime::{messages::DispatchFeePayment, StorageProofSize};
use bp_runtime::StorageProofSize;
use frame_benchmarking::{account, benchmarks_instance_pallet};
use frame_support::{traits::Get, weights::Weight};
use frame_support::weights::Weight;
use frame_system::RawOrigin;
use sp_std::{collections::vec_deque::VecDeque, convert::TryInto, ops::RangeInclusive, prelude::*};
use sp_std::{ops::RangeInclusive, prelude::*};
const SEED: u32 = 0;
/// Pallet we're benchmarking here.
pub struct Pallet<T: Config<I>, I: 'static>(crate::Pallet<T, I>);
/// Benchmark-specific message parameters.
#[derive(Debug)]
pub struct MessageParams<ThisAccountId> {
/// Size of the message payload.
pub size: u32,
/// Message sender account.
pub sender_account: ThisAccountId,
}
/// Benchmark-specific message proof parameters.
#[derive(Debug)]
pub struct MessageProofParams {
@@ -58,8 +48,6 @@ pub struct MessageProofParams {
pub outbound_lane_data: Option<OutboundLaneData>,
/// Proof size requirements.
pub size: StorageProofSize,
/// Where the fee for dispatching message is paid?
pub dispatch_fee_payment: DispatchFeePayment,
}
/// Benchmark-specific message delivery proof parameters.
@@ -79,29 +67,14 @@ pub trait Config<I: 'static>: crate::Config<I> {
fn bench_lane_id() -> LaneId {
Default::default()
}
/// Get maximal size of the message payload.
fn maximal_message_size() -> u32;
/// Return id of relayer account at the bridged chain.
fn bridged_relayer_id() -> Self::InboundRelayer;
/// Return balance of given account.
fn account_balance(account: &Self::AccountId) -> Self::OutboundMessageFee;
/// Create given account and give it enough balance for test purposes.
fn endow_account(account: &Self::AccountId);
/// Fee paid by submitter for single message delivery.
fn message_fee() -> Self::OutboundMessageFee {
100_000_000_000_000.into()
}
/// Prepare message to send over lane.
fn prepare_outbound_message(
params: MessageParams<Self::AccountId>,
) -> (Self::OutboundPayload, Self::OutboundMessageFee);
/// Prepare messages proof to receive by the module.
fn prepare_message_proof(
params: MessageProofParams,
) -> (
<Self::SourceHeaderChain as SourceHeaderChain<Self::InboundMessageFee>>::MessagesProof,
Weight,
);
) -> (<Self::SourceHeaderChain as SourceHeaderChain>::MessagesProof, Weight);
/// Prepare messages delivery proof to receive by the module.
fn prepare_message_delivery_proof(
params: MessageDeliveryProofParams<Self::AccountId>,
@@ -115,156 +88,6 @@ benchmarks_instance_pallet! {
// Benchmarks that are used directly by the runtime.
//
// Benchmark `send_message` extrinsic with the worst possible conditions:
// * outbound lane already has state, so it needs to be read and decoded;
// * relayers fund account does not exists (in practice it needs to exist in production environment);
// * maximal number of messages is being pruned during the call;
// * message size is minimal for the target chain.
//
// Result of this benchmark is used as a base weight for `send_message` call. Then the 'message weight'
// (estimated using `send_half_maximal_message_worst_case` and `send_maximal_message_worst_case`) is
// added.
send_minimal_message_worst_case {
let lane_id = T::bench_lane_id();
let sender = account("sender", 0, SEED);
T::endow_account(&sender);
// 'send' messages that are to be pruned when our message is sent
for _nonce in 1..=T::MaxMessagesToPruneAtOnce::get() {
send_regular_message::<T, I>();
}
confirm_message_delivery::<T, I>(T::MaxMessagesToPruneAtOnce::get());
let (payload, fee) = T::prepare_outbound_message(MessageParams {
size: 0,
sender_account: sender.clone(),
});
}: send_message(RawOrigin::Signed(sender), lane_id, payload, fee)
verify {
assert_eq!(
crate::OutboundLanes::<T, I>::get(&T::bench_lane_id()).latest_generated_nonce,
T::MaxMessagesToPruneAtOnce::get() + 1,
);
}
// Benchmark `send_message` extrinsic with the worst possible conditions:
// * outbound lane already has state, so it needs to be read and decoded;
// * relayers fund account does not exists (in practice it needs to exist in production environment);
// * maximal number of messages is being pruned during the call;
// * message size is 1KB.
//
// With single KB of message size, the weight of the call is increased (roughly) by
// `(send_16_kb_message_worst_case - send_1_kb_message_worst_case) / 15`.
send_1_kb_message_worst_case {
let lane_id = T::bench_lane_id();
let sender = account("sender", 0, SEED);
T::endow_account(&sender);
// 'send' messages that are to be pruned when our message is sent
for _nonce in 1..=T::MaxMessagesToPruneAtOnce::get() {
send_regular_message::<T, I>();
}
confirm_message_delivery::<T, I>(T::MaxMessagesToPruneAtOnce::get());
let size = 1024;
assert!(
T::maximal_message_size() > size,
"This benchmark can only be used with runtime that accepts 1KB messages",
);
let (payload, fee) = T::prepare_outbound_message(MessageParams {
size,
sender_account: sender.clone(),
});
}: send_message(RawOrigin::Signed(sender), lane_id, payload, fee)
verify {
assert_eq!(
crate::OutboundLanes::<T, I>::get(&T::bench_lane_id()).latest_generated_nonce,
T::MaxMessagesToPruneAtOnce::get() + 1,
);
}
// Benchmark `send_message` extrinsic with the worst possible conditions:
// * outbound lane already has state, so it needs to be read and decoded;
// * relayers fund account does not exists (in practice it needs to exist in production environment);
// * maximal number of messages is being pruned during the call;
// * message size is 16KB.
//
// With single KB of message size, the weight of the call is increased (roughly) by
// `(send_16_kb_message_worst_case - send_1_kb_message_worst_case) / 15`.
send_16_kb_message_worst_case {
let lane_id = T::bench_lane_id();
let sender = account("sender", 0, SEED);
T::endow_account(&sender);
// 'send' messages that are to be pruned when our message is sent
for _nonce in 1..=T::MaxMessagesToPruneAtOnce::get() {
send_regular_message::<T, I>();
}
confirm_message_delivery::<T, I>(T::MaxMessagesToPruneAtOnce::get());
let size = 16 * 1024;
assert!(
T::maximal_message_size() > size,
"This benchmark can only be used with runtime that accepts 16KB messages",
);
let (payload, fee) = T::prepare_outbound_message(MessageParams {
size,
sender_account: sender.clone(),
});
}: send_message(RawOrigin::Signed(sender), lane_id, payload, fee)
verify {
assert_eq!(
crate::OutboundLanes::<T, I>::get(&T::bench_lane_id()).latest_generated_nonce,
T::MaxMessagesToPruneAtOnce::get() + 1,
);
}
// Benchmark `increase_message_fee` with following conditions:
// * message has maximal message;
// * submitter account is killed because its balance is less than ED after payment.
//
// Result of this benchmark is directly used by weight formula of the call.
maximal_increase_message_fee {
let sender = account("sender", 42, SEED);
T::endow_account(&sender);
let additional_fee = T::account_balance(&sender);
let lane_id = T::bench_lane_id();
let nonce = 1;
send_regular_message_with_payload::<T, I>(vec![42u8; T::maximal_message_size() as _]);
}: increase_message_fee(RawOrigin::Signed(sender.clone()), lane_id, nonce, additional_fee)
verify {
assert_eq!(
OutboundMessages::<T, I>::get(MessageKey { lane_id, nonce }).unwrap().fee,
T::message_fee() + additional_fee,
);
}
// Benchmark `increase_message_fee` with following conditions:
// * message size varies from minimal to maximal;
// * submitter account is killed because its balance is less than ED after payment.
increase_message_fee {
let i in 0..T::maximal_message_size().try_into().unwrap_or_default();
let sender = account("sender", 42, SEED);
T::endow_account(&sender);
let additional_fee = T::account_balance(&sender);
let lane_id = T::bench_lane_id();
let nonce = 1;
send_regular_message_with_payload::<T, I>(vec![42u8; i as _]);
}: increase_message_fee(RawOrigin::Signed(sender.clone()), lane_id, nonce, additional_fee)
verify {
assert_eq!(
OutboundMessages::<T, I>::get(MessageKey { lane_id, nonce }).unwrap().fee,
T::message_fee() + additional_fee,
);
}
// Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions:
// * proof does not include outbound lane state proof;
// * inbound lane already has state, so it needs to be read and decoded;
@@ -286,7 +109,6 @@ benchmarks_instance_pallet! {
message_nonces: 21..=21,
outbound_lane_data: None,
size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH),
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
});
}: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight)
verify {
@@ -321,7 +143,6 @@ benchmarks_instance_pallet! {
message_nonces: 21..=22,
outbound_lane_data: None,
size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH),
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
});
}: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 2, dispatch_weight)
verify {
@@ -360,7 +181,6 @@ benchmarks_instance_pallet! {
latest_generated_nonce: 21,
}),
size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH),
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
});
}: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight)
verify {
@@ -392,7 +212,6 @@ benchmarks_instance_pallet! {
message_nonces: 21..=21,
outbound_lane_data: None,
size: StorageProofSize::HasExtraNodes(1024),
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
});
}: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight)
verify {
@@ -427,7 +246,6 @@ benchmarks_instance_pallet! {
message_nonces: 21..=21,
outbound_lane_data: None,
size: StorageProofSize::HasExtraNodes(16 * 1024),
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
});
}: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight)
verify {
@@ -461,7 +279,6 @@ benchmarks_instance_pallet! {
message_nonces: 21..=21,
outbound_lane_data: None,
size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH),
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
});
}: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight)
verify {
@@ -479,7 +296,6 @@ benchmarks_instance_pallet! {
// This is base benchmark for all other confirmations delivery benchmarks.
receive_delivery_proof_for_single_message {
let relayer_id: T::AccountId = account("relayer", 0, SEED);
let relayer_balance = T::account_balance(&relayer_id);
// send message that we're going to confirm
send_regular_message::<T, I>();
@@ -515,7 +331,6 @@ benchmarks_instance_pallet! {
// - weight(receive_delivery_proof_for_single_message)`.
receive_delivery_proof_for_two_messages_by_single_relayer {
let relayer_id: T::AccountId = account("relayer", 0, SEED);
let relayer_balance = T::account_balance(&relayer_id);
// send message that we're going to confirm
send_regular_message::<T, I>();
@@ -554,9 +369,7 @@ benchmarks_instance_pallet! {
// - weight(receive_delivery_proof_for_two_messages_by_single_relayer)`.
receive_delivery_proof_for_two_messages_by_two_relayers {
let relayer1_id: T::AccountId = account("relayer1", 1, SEED);
let relayer1_balance = T::account_balance(&relayer1_id);
let relayer2_id: T::AccountId = account("relayer2", 2, SEED);
let relayer2_balance = T::account_balance(&relayer2_id);
// send message that we're going to confirm
send_regular_message::<T, I>();
@@ -593,28 +406,7 @@ benchmarks_instance_pallet! {
fn send_regular_message<T: Config<I>, I: 'static>() {
let mut outbound_lane = outbound_lane::<T, I>(T::bench_lane_id());
outbound_lane.send_message(MessageData { payload: vec![], fee: T::message_fee() });
}
fn send_regular_message_with_payload<T: Config<I>, I: 'static>(payload: Vec<u8>) {
let mut outbound_lane = outbound_lane::<T, I>(T::bench_lane_id());
outbound_lane.send_message(MessageData { payload, fee: T::message_fee() });
}
fn confirm_message_delivery<T: Config<I>, I: 'static>(nonce: MessageNonce) {
let mut outbound_lane = outbound_lane::<T, I>(T::bench_lane_id());
let latest_received_nonce = outbound_lane.data().latest_received_nonce;
let mut relayers = VecDeque::with_capacity((nonce - latest_received_nonce) as usize);
for nonce in latest_received_nonce + 1..=nonce {
relayers.push_back(UnrewardedRelayer {
relayer: (),
messages: DeliveredMessages::new(nonce, true),
});
}
assert!(matches!(
outbound_lane.confirm_delivery(nonce - latest_received_nonce, nonce, &relayers),
ReceivalConfirmationResult::ConfirmedMessages(_),
));
outbound_lane.send_message(vec![]);
}
fn receive_messages<T: Config<I>, I: 'static>(nonce: MessageNonce) {
+19 -21
View File
@@ -31,8 +31,6 @@ use sp_std::prelude::PartialEq;
/// Inbound lane storage.
pub trait InboundLaneStorage {
/// Delivery and dispatch fee type on source chain.
type MessageFee;
/// Id of relayer on source chain.
type Relayer: Clone + PartialEq;
@@ -183,12 +181,12 @@ impl<S: InboundLaneStorage> InboundLane<S> {
}
/// Receive new message.
pub fn receive_message<P: MessageDispatch<AccountId, S::MessageFee>, AccountId>(
pub fn receive_message<P: MessageDispatch<AccountId>, AccountId>(
&mut self,
relayer_at_bridged_chain: &S::Relayer,
relayer_at_this_chain: &AccountId,
nonce: MessageNonce,
message_data: DispatchMessageData<P::DispatchPayload, S::MessageFee>,
message_data: DispatchMessageData<P::DispatchPayload>,
) -> ReceivalResult {
let mut data = self.storage.data();
let is_correct_message = nonce == data.last_delivered_nonce() + 1;
@@ -242,9 +240,9 @@ mod tests {
use crate::{
inbound_lane,
mock::{
dispatch_result, message_data, run_test, unrewarded_relayer, TestMessageDispatch,
TestRuntime, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_RELAYER_A, TEST_RELAYER_B,
TEST_RELAYER_C,
dispatch_result, inbound_message_data, run_test, unrewarded_relayer,
TestMessageDispatch, TestRuntime, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_RELAYER_A,
TEST_RELAYER_B, TEST_RELAYER_C,
},
RuntimeInboundLaneStorage,
};
@@ -258,7 +256,7 @@ mod tests {
&TEST_RELAYER_A,
&TEST_RELAYER_A,
nonce,
message_data(REGULAR_PAYLOAD).into()
inbound_message_data(REGULAR_PAYLOAD)
),
ReceivalResult::Dispatched(dispatch_result(0))
);
@@ -386,7 +384,7 @@ mod tests {
&TEST_RELAYER_A,
&TEST_RELAYER_A,
10,
message_data(REGULAR_PAYLOAD).into()
inbound_message_data(REGULAR_PAYLOAD)
),
ReceivalResult::InvalidNonce
);
@@ -406,7 +404,7 @@ mod tests {
&(TEST_RELAYER_A + current_nonce),
&(TEST_RELAYER_A + current_nonce),
current_nonce,
message_data(REGULAR_PAYLOAD).into()
inbound_message_data(REGULAR_PAYLOAD)
),
ReceivalResult::Dispatched(dispatch_result(0))
);
@@ -417,7 +415,7 @@ mod tests {
&(TEST_RELAYER_A + max_nonce + 1),
&(TEST_RELAYER_A + max_nonce + 1),
max_nonce + 1,
message_data(REGULAR_PAYLOAD).into()
inbound_message_data(REGULAR_PAYLOAD)
),
ReceivalResult::TooManyUnrewardedRelayers,
);
@@ -427,7 +425,7 @@ mod tests {
&(TEST_RELAYER_A + max_nonce),
&(TEST_RELAYER_A + max_nonce),
max_nonce + 1,
message_data(REGULAR_PAYLOAD).into()
inbound_message_data(REGULAR_PAYLOAD)
),
ReceivalResult::TooManyUnrewardedRelayers,
);
@@ -445,7 +443,7 @@ mod tests {
&TEST_RELAYER_A,
&TEST_RELAYER_A,
current_nonce,
message_data(REGULAR_PAYLOAD).into()
inbound_message_data(REGULAR_PAYLOAD)
),
ReceivalResult::Dispatched(dispatch_result(0))
);
@@ -456,7 +454,7 @@ mod tests {
&TEST_RELAYER_B,
&TEST_RELAYER_B,
max_nonce + 1,
message_data(REGULAR_PAYLOAD).into()
inbound_message_data(REGULAR_PAYLOAD)
),
ReceivalResult::TooManyUnconfirmedMessages,
);
@@ -466,7 +464,7 @@ mod tests {
&TEST_RELAYER_A,
&TEST_RELAYER_A,
max_nonce + 1,
message_data(REGULAR_PAYLOAD).into()
inbound_message_data(REGULAR_PAYLOAD)
),
ReceivalResult::TooManyUnconfirmedMessages,
);
@@ -482,7 +480,7 @@ mod tests {
&TEST_RELAYER_A,
&TEST_RELAYER_A,
1,
message_data(REGULAR_PAYLOAD).into()
inbound_message_data(REGULAR_PAYLOAD)
),
ReceivalResult::Dispatched(dispatch_result(0))
);
@@ -491,7 +489,7 @@ mod tests {
&TEST_RELAYER_B,
&TEST_RELAYER_B,
2,
message_data(REGULAR_PAYLOAD).into()
inbound_message_data(REGULAR_PAYLOAD)
),
ReceivalResult::Dispatched(dispatch_result(0))
);
@@ -500,7 +498,7 @@ mod tests {
&TEST_RELAYER_A,
&TEST_RELAYER_A,
3,
message_data(REGULAR_PAYLOAD).into()
inbound_message_data(REGULAR_PAYLOAD)
),
ReceivalResult::Dispatched(dispatch_result(0))
);
@@ -524,7 +522,7 @@ mod tests {
&TEST_RELAYER_A,
&TEST_RELAYER_A,
1,
message_data(REGULAR_PAYLOAD).into()
inbound_message_data(REGULAR_PAYLOAD)
),
ReceivalResult::Dispatched(dispatch_result(0))
);
@@ -533,7 +531,7 @@ mod tests {
&TEST_RELAYER_B,
&TEST_RELAYER_B,
1,
message_data(REGULAR_PAYLOAD).into()
inbound_message_data(REGULAR_PAYLOAD)
),
ReceivalResult::InvalidNonce,
);
@@ -560,7 +558,7 @@ mod tests {
&TEST_RELAYER_A,
&TEST_RELAYER_A,
1,
message_data(payload).into()
inbound_message_data(payload)
),
ReceivalResult::Dispatched(dispatch_result(1))
);
+78 -573
View File
@@ -38,7 +38,7 @@
#![allow(clippy::unused_unit)]
pub use inbound_lane::StoredInboundLaneData;
pub use outbound_lane::StoredMessageData;
pub use outbound_lane::StoredMessagePayload;
pub use weights::WeightInfo;
pub use weights_ext::{
ensure_able_to_receive_confirmation, ensure_able_to_receive_message,
@@ -59,17 +59,16 @@ use bp_messages::{
DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, SourceHeaderChain,
},
total_unrewarded_messages, DeliveredMessages, InboundLaneData, InboundMessageDetails, LaneId,
MessageData, MessageKey, MessageNonce, MessagePayload, MessagesOperatingMode, OutboundLaneData,
OutboundMessageDetails, Parameter as MessagesParameter, UnrewardedRelayer,
UnrewardedRelayersState,
MessageKey, MessageNonce, MessagePayload, MessagesOperatingMode, OutboundLaneData,
OutboundMessageDetails, UnrewardedRelayer, UnrewardedRelayersState,
};
use bp_runtime::{BasicOperatingMode, ChainId, OwnedBridgeModule, Size};
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::{dispatch::PostDispatchInfo, ensure, fail, traits::Get};
use num_traits::{SaturatingAdd, Zero};
use num_traits::Zero;
use sp_std::{
cell::RefCell, cmp::PartialOrd, collections::vec_deque::VecDeque, marker::PhantomData,
ops::RangeInclusive, prelude::*,
cell::RefCell, collections::vec_deque::VecDeque, marker::PhantomData, ops::RangeInclusive,
prelude::*,
};
mod inbound_lane;
@@ -108,11 +107,6 @@ pub mod pallet {
/// Gets the chain id value from the instance.
#[pallet::constant]
type BridgedChainId: Get<ChainId>;
/// Pallet parameter that is opaque to the pallet itself, but may be used by the runtime
/// for integrating the pallet.
///
/// All pallet parameters may only be updated either by the root, or by the pallet owner.
type Parameter: MessagesParameter;
/// Maximal number of messages that may be pruned during maintenance. Maintenance occurs
/// whenever new message is sent. The reason is that if you want to use lane, you should
@@ -142,25 +136,14 @@ pub mod pallet {
/// these messages are from different lanes.
type MaxUnconfirmedMessagesAtInboundLane: Get<MessageNonce>;
/// Maximal size of the outbound payload.
/// Maximal encoded size of the outbound payload.
#[pallet::constant]
type MaximalOutboundPayloadSize: Get<u32>;
/// Payload type of outbound messages. This payload is dispatched on the bridged chain.
type OutboundPayload: Parameter + Size;
/// Message fee type of outbound messages. This fee is paid on this chain.
type OutboundMessageFee: Default
+ From<u64>
+ PartialOrd
+ Parameter
+ SaturatingAdd
+ Zero
+ Copy
+ MaxEncodedLen;
/// Payload type of inbound messages. This payload is dispatched on this chain.
type InboundPayload: Decode;
/// Message fee type of inbound messages. This fee is paid on the bridged chain.
type InboundMessageFee: Decode + Zero;
/// Identifier of relayer that deliver messages to this chain. Relayer reward is paid on the
/// bridged chain.
type InboundRelayer: Parameter + MaxEncodedLen;
@@ -170,16 +153,11 @@ pub mod pallet {
/// Target header chain.
type TargetHeaderChain: TargetHeaderChain<Self::OutboundPayload, Self::AccountId>;
/// Message payload verifier.
type LaneMessageVerifier: LaneMessageVerifier<
Self::RuntimeOrigin,
Self::OutboundPayload,
Self::OutboundMessageFee,
>;
type LaneMessageVerifier: LaneMessageVerifier<Self::RuntimeOrigin, Self::OutboundPayload>;
/// Message delivery payment.
type MessageDeliveryAndDispatchPayment: MessageDeliveryAndDispatchPayment<
Self::RuntimeOrigin,
Self::AccountId,
Self::OutboundMessageFee,
>;
/// Handler for accepted messages.
type OnMessageAccepted: OnMessageAccepted;
@@ -189,19 +167,17 @@ pub mod pallet {
// Types that are used by inbound_lane (on target chain).
/// Source header chain, as it is represented on target chain.
type SourceHeaderChain: SourceHeaderChain<Self::InboundMessageFee>;
type SourceHeaderChain: SourceHeaderChain;
/// Message dispatch.
type MessageDispatch: MessageDispatch<
Self::AccountId,
Self::InboundMessageFee,
DispatchPayload = Self::InboundPayload,
>;
}
/// Shortcut to messages proof type for Config.
type MessagesProofOf<T, I> = <<T as Config<I>>::SourceHeaderChain as SourceHeaderChain<
<T as Config<I>>::InboundMessageFee,
>>::MessagesProof;
type MessagesProofOf<T, I> =
<<T as Config<I>>::SourceHeaderChain as SourceHeaderChain>::MessagesProof;
/// Shortcut to messages delivery proof type for Config.
type MessagesDeliveryProofOf<T, I> =
<<T as Config<I>>::TargetHeaderChain as TargetHeaderChain<
@@ -241,101 +217,6 @@ pub mod pallet {
<Self as OwnedBridgeModule<_>>::set_operating_mode(origin, operating_mode)
}
/// Update pallet parameter.
///
/// May only be called either by root, or by `PalletOwner`.
///
/// The weight is: single read for permissions check + 2 writes for parameter value and
/// event.
#[pallet::weight((T::DbWeight::get().reads_writes(1, 2), DispatchClass::Operational))]
pub fn update_pallet_parameter(
origin: OriginFor<T>,
parameter: T::Parameter,
) -> DispatchResult {
Self::ensure_owner_or_root(origin)?;
parameter.save();
Self::deposit_event(Event::ParameterUpdated { parameter });
Ok(())
}
/// Send message over lane.
#[pallet::weight(T::WeightInfo::send_message_weight(payload, T::DbWeight::get()))]
pub fn send_message(
origin: OriginFor<T>,
lane_id: LaneId,
payload: T::OutboundPayload,
delivery_and_dispatch_fee: T::OutboundMessageFee,
) -> DispatchResultWithPostInfo {
crate::send_message::<T, I>(origin, lane_id, payload, delivery_and_dispatch_fee).map(
|sent_message| PostDispatchInfo {
actual_weight: Some(sent_message.weight),
pays_fee: Pays::Yes,
},
)
}
/// Pay additional fee for the message.
#[pallet::weight(T::WeightInfo::maximal_increase_message_fee())]
pub fn increase_message_fee(
origin: OriginFor<T>,
lane_id: LaneId,
nonce: MessageNonce,
additional_fee: T::OutboundMessageFee,
) -> DispatchResultWithPostInfo {
Self::ensure_not_halted().map_err(Error::<T, I>::BridgeModule)?;
// if someone tries to pay for already-delivered message, we're rejecting this intention
// (otherwise this additional fee will be locked forever in relayers fund)
//
// if someone tries to pay for not-yet-sent message, we're rejecting this intention, or
// we're risking to have mess in the storage
let lane = outbound_lane::<T, I>(lane_id);
ensure!(
nonce > lane.data().latest_received_nonce,
Error::<T, I>::MessageIsAlreadyDelivered
);
ensure!(
nonce <= lane.data().latest_generated_nonce,
Error::<T, I>::MessageIsNotYetSent
);
// withdraw additional fee from submitter
T::MessageDeliveryAndDispatchPayment::pay_delivery_and_dispatch_fee(
&origin,
&additional_fee,
)
.map_err(|err| {
log::trace!(
target: LOG_TARGET,
"Submitter can't pay additional fee {:?} for the message {:?}/{:?}: {:?}",
additional_fee,
lane_id,
nonce,
err,
);
Error::<T, I>::FailedToWithdrawMessageFee
})?;
// and finally update fee in the storage
let message_key = MessageKey { lane_id, nonce };
let message_size = OutboundMessages::<T, I>::mutate(message_key, |message_data| {
// saturating_add is fine here - overflow here means that someone controls all
// chain funds, which shouldn't ever happen + `pay_delivery_and_dispatch_fee`
// above will fail before we reach here
let message_data = message_data.as_mut().expect(
"the message is sent and not yet delivered; so it is in the storage; qed",
);
message_data.fee = message_data.fee.saturating_add(&additional_fee);
message_data.payload.len()
});
// compute actual dispatch weight that depends on the stored message size
let actual_weight = T::WeightInfo::maximal_increase_message_fee()
.min(T::WeightInfo::increase_message_fee(message_size as _));
Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes })
}
/// Receive messages proof from bridged chain.
///
/// The weight of the call assumes that the transaction always brings outbound lane
@@ -378,7 +259,6 @@ pub mod pallet {
// verify messages proof && convert proof into messages
let messages = verify_and_decode_messages_proof::<
T::SourceHeaderChain,
T::InboundMessageFee,
T::InboundPayload,
>(proof, messages_count)
.map_err(|err| {
@@ -636,8 +516,6 @@ pub mod pallet {
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config<I>, I: 'static = ()> {
/// Pallet parameter has been updated.
ParameterUpdated { parameter: T::Parameter },
/// Message has been accepted and is waiting to be delivered.
MessageAccepted { lane_id: LaneId, nonce: MessageNonce },
/// Messages in the inclusive range have been delivered to the bridged chain.
@@ -709,7 +587,7 @@ pub mod pallet {
/// All queued outbound messages.
#[pallet::storage]
pub type OutboundMessages<T: Config<I>, I: 'static = ()> =
StorageMap<_, Blake2_128Concat, MessageKey, StoredMessageData<T, I>>;
StorageMap<_, Blake2_128Concat, MessageKey, StoredMessagePayload<T, I>>;
#[pallet::genesis_config]
pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
@@ -744,10 +622,7 @@ pub mod pallet {
impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// Get stored data of the outbound message with given nonce.
pub fn outbound_message_data(
lane: LaneId,
nonce: MessageNonce,
) -> Option<MessageData<T::OutboundMessageFee>> {
pub fn outbound_message_data(lane: LaneId, nonce: MessageNonce) -> Option<MessagePayload> {
OutboundMessages::<T, I>::get(MessageKey { lane_id: lane, nonce }).map(Into::into)
}
@@ -755,12 +630,11 @@ pub mod pallet {
pub fn inbound_message_data(
lane: LaneId,
payload: MessagePayload,
outbound_details: OutboundMessageDetails<T::InboundMessageFee>,
outbound_details: OutboundMessageDetails,
) -> InboundMessageDetails {
let mut dispatch_message = DispatchMessage {
key: MessageKey { lane_id: lane, nonce: outbound_details.nonce },
data: MessageData { payload, fee: outbound_details.delivery_and_dispatch_fee }
.into(),
data: payload.into(),
};
InboundMessageDetails {
dispatch_weight: T::MessageDispatch::dispatch_weight(&mut dispatch_message),
@@ -769,12 +643,8 @@ pub mod pallet {
}
}
impl<T, I>
bp_messages::source_chain::MessagesBridge<
T::RuntimeOrigin,
T::OutboundMessageFee,
T::OutboundPayload,
> for Pallet<T, I>
impl<T, I> bp_messages::source_chain::MessagesBridge<T::RuntimeOrigin, T::OutboundPayload>
for Pallet<T, I>
where
T: Config<I>,
I: 'static,
@@ -785,9 +655,8 @@ where
sender: T::RuntimeOrigin,
lane: LaneId,
message: T::OutboundPayload,
delivery_and_dispatch_fee: T::OutboundMessageFee,
) -> Result<SendMessageArtifacts, Self::Error> {
crate::send_message::<T, I>(sender, lane, message, delivery_and_dispatch_fee)
crate::send_message::<T, I>(sender, lane, message)
}
}
@@ -796,21 +665,14 @@ fn send_message<T: Config<I>, I: 'static>(
submitter: T::RuntimeOrigin,
lane_id: LaneId,
payload: T::OutboundPayload,
delivery_and_dispatch_fee: T::OutboundMessageFee,
) -> sp_std::result::Result<
SendMessageArtifacts,
sp_runtime::DispatchErrorWithPostInfo<PostDispatchInfo>,
> {
ensure_normal_operating_mode::<T, I>()?;
// the most lightweigh check is the message size check
ensure!(
payload.size() <= T::MaximalOutboundPayloadSize::get(),
Error::<T, I>::MessageIsTooLarge,
);
// initially, actual (post-dispatch) weight is equal to pre-dispatch weight
let mut actual_weight = T::WeightInfo::send_message_weight(&payload, T::DbWeight::get());
let mut actual_weight = frame_support::weights::Weight::zero(); // TODO (https://github.com/paritytech/parity-bridges-common/issues/1647): remove this
// let's first check if message can be delivered to target chain
T::TargetHeaderChain::verify_message(&payload).map_err(|err| {
@@ -826,46 +688,28 @@ fn send_message<T: Config<I>, I: 'static>(
// now let's enforce any additional lane rules
let mut lane = outbound_lane::<T, I>(lane_id);
T::LaneMessageVerifier::verify_message(
&submitter,
&delivery_and_dispatch_fee,
&lane_id,
&lane.data(),
&payload,
)
.map_err(|err| {
log::trace!(
target: LOG_TARGET,
"Message to lane {:?} is rejected by lane verifier: {:?}",
lane_id,
err,
);
T::LaneMessageVerifier::verify_message(&submitter, &lane_id, &lane.data(), &payload).map_err(
|err| {
log::trace!(
target: LOG_TARGET,
"Message to lane {:?} is rejected by lane verifier: {:?}",
lane_id,
err,
);
Error::<T, I>::MessageRejectedByLaneVerifier
})?;
// let's withdraw delivery and dispatch fee from submitter
T::MessageDeliveryAndDispatchPayment::pay_delivery_and_dispatch_fee(
&submitter,
&delivery_and_dispatch_fee,
)
.map_err(|err| {
log::trace!(
target: LOG_TARGET,
"Message to lane {:?} is rejected because submitter is unable to pay fee {:?}: {:?}",
lane_id,
delivery_and_dispatch_fee,
err,
);
Error::<T, I>::FailedToWithdrawMessageFee
})?;
Error::<T, I>::MessageRejectedByLaneVerifier
},
)?;
// finally, save message in outbound storage and emit event
let encoded_payload = payload.encode();
let encoded_payload_len = encoded_payload.len();
let nonce =
lane.send_message(MessageData { payload: encoded_payload, fee: delivery_and_dispatch_fee });
ensure!(
encoded_payload_len <= T::MaximalOutboundPayloadSize::get() as usize,
Error::<T, I>::MessageIsTooLarge
);
let nonce = lane.send_message(encoded_payload);
// Guaranteed to be called outside only when the message is accepted.
// We assume that the maximum weight call back used is `single_message_callback_overhead`, so do
// not perform complex db operation in callback. If you want to, put these magic logic in
@@ -920,32 +764,26 @@ fn send_message<T: Config<I>, I: 'static>(
Ok(SendMessageArtifacts { nonce, weight: actual_weight })
}
/// Calculate the relayers rewards
/// Calculate the number of messages that the relayers have delivered.
pub fn calc_relayers_rewards<T, I>(
lane_id: LaneId,
messages_relayers: VecDeque<UnrewardedRelayer<T::AccountId>>,
received_range: &RangeInclusive<MessageNonce>,
) -> RelayersRewards<T::AccountId, T::OutboundMessageFee>
) -> RelayersRewards<T::AccountId>
where
T: frame_system::Config + crate::Config<I>,
I: 'static,
{
// remember to reward relayers that have delivered messages
// this loop is bounded by `T::MaxUnrewardedRelayerEntriesAtInboundLane` on the bridged chain
let mut relayers_rewards: RelayersRewards<_, T::OutboundMessageFee> = RelayersRewards::new();
let mut relayers_rewards = RelayersRewards::new();
for entry in messages_relayers {
let nonce_begin = sp_std::cmp::max(entry.messages.begin, *received_range.start());
let nonce_end = sp_std::cmp::min(entry.messages.end, *received_range.end());
// loop won't proceed if current entry is ahead of received range (begin > end).
// this loop is bound by `T::MaxUnconfirmedMessagesAtInboundLane` on the bridged chain
let mut relayer_reward = relayers_rewards.entry(entry.relayer).or_default();
for nonce in nonce_begin..=nonce_end {
let key = MessageKey { lane_id, nonce };
let message_data = OutboundMessages::<T, I>::get(key)
.expect("message was just confirmed; we never prune unconfirmed messages; qed");
relayer_reward.reward = relayer_reward.reward.saturating_add(&message_data.fee);
relayer_reward.messages += 1;
if nonce_end >= nonce_begin {
*relayers_rewards.entry(entry.relayer).or_default() += nonce_end - nonce_begin + 1;
}
}
relayers_rewards
@@ -995,7 +833,6 @@ struct RuntimeInboundLaneStorage<T: Config<I>, I: 'static = ()> {
}
impl<T: Config<I>, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage<T, I> {
type MessageFee = T::InboundMessageFee;
type Relayer = T::InboundRelayer;
fn id(&self) -> LaneId {
@@ -1041,8 +878,6 @@ struct RuntimeOutboundLaneStorage<T, I = ()> {
}
impl<T: Config<I>, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorage<T, I> {
type MessageFee = T::OutboundMessageFee;
fn id(&self) -> LaneId {
self.lane_id
}
@@ -1056,17 +891,20 @@ impl<T: Config<I>, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorag
}
#[cfg(test)]
fn message(&self, nonce: &MessageNonce) -> Option<MessageData<T::OutboundMessageFee>> {
fn message(&self, nonce: &MessageNonce) -> Option<MessagePayload> {
OutboundMessages::<T, I>::get(MessageKey { lane_id: self.lane_id, nonce: *nonce })
.map(Into::into)
}
fn save_message(
&mut self,
nonce: MessageNonce,
mesage_data: MessageData<T::OutboundMessageFee>,
) {
OutboundMessages::<T, I>::insert(MessageKey { lane_id: self.lane_id, nonce }, mesage_data);
fn save_message(&mut self, nonce: MessageNonce, message_payload: MessagePayload) {
OutboundMessages::<T, I>::insert(
MessageKey { lane_id: self.lane_id, nonce },
StoredMessagePayload::<T, I>::try_from(message_payload).expect(
"save_message is called after all checks in send_message; \
send_message checks message size; \
qed",
),
);
}
fn remove_message(&mut self, nonce: &MessageNonce) {
@@ -1075,10 +913,10 @@ impl<T: Config<I>, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorag
}
/// Verify messages proof and return proved messages with decoded payload.
fn verify_and_decode_messages_proof<Chain: SourceHeaderChain<Fee>, Fee, DispatchPayload: Decode>(
fn verify_and_decode_messages_proof<Chain: SourceHeaderChain, DispatchPayload: Decode>(
proof: Chain::MessagesProof,
messages_count: u32,
) -> Result<ProvedMessages<DispatchMessage<DispatchPayload, Fee>>, Chain::Error> {
) -> Result<ProvedMessages<DispatchMessage<DispatchPayload>>, Chain::Error> {
// `receive_messages_proof` weight formula and `MaxUnconfirmedMessagesAtInboundLane` check
// guarantees that the `message_count` is sane and Vec<Message> may be allocated.
// (tx with too many messages will either be rejected from the pool, or will fail earlier)
@@ -1102,12 +940,12 @@ fn verify_and_decode_messages_proof<Chain: SourceHeaderChain<Fee>, Fee, Dispatch
mod tests {
use super::*;
use crate::mock::{
message, message_payload, run_test, unrewarded_relayer, Balance, RuntimeEvent as TestEvent,
message, message_payload, run_test, unrewarded_relayer, RuntimeEvent as TestEvent,
RuntimeOrigin, TestMessageDeliveryAndDispatchPayment, TestMessagesDeliveryProof,
TestMessagesParameter, TestMessagesProof, TestOnDeliveryConfirmed1,
TestOnDeliveryConfirmed2, TestOnMessageAccepted, TestRuntime, TokenConversionRate,
MAX_OUTBOUND_PAYLOAD_SIZE, PAYLOAD_REJECTED_BY_TARGET_CHAIN, REGULAR_PAYLOAD, TEST_LANE_ID,
TEST_RELAYER_A, TEST_RELAYER_B,
TestMessagesProof, TestOnDeliveryConfirmed1, TestOnDeliveryConfirmed2,
TestOnMessageAccepted, TestRuntime, MAX_OUTBOUND_PAYLOAD_SIZE,
PAYLOAD_REJECTED_BY_TARGET_CHAIN, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_RELAYER_A,
TEST_RELAYER_B,
};
use bp_messages::{UnrewardedRelayer, UnrewardedRelayersState};
use bp_test_utils::generate_owned_bridge_module_tests;
@@ -1146,15 +984,13 @@ mod tests {
let message_nonce =
outbound_lane::<TestRuntime, ()>(TEST_LANE_ID).data().latest_generated_nonce + 1;
let weight = Pallet::<TestRuntime>::send_message(
let weight = send_message::<TestRuntime, ()>(
RuntimeOrigin::signed(1),
TEST_LANE_ID,
REGULAR_PAYLOAD,
REGULAR_PAYLOAD.declared_weight.ref_time(),
)
.expect("send_message has failed")
.actual_weight
.expect("send_message always returns Some");
.weight;
// check event with assigned nonce
assert_eq!(
@@ -1169,12 +1005,6 @@ mod tests {
}],
);
// check that fee has been withdrawn from submitter
assert!(TestMessageDeliveryAndDispatchPayment::is_fee_paid(
1,
REGULAR_PAYLOAD.declared_weight.ref_time()
));
weight
}
@@ -1217,102 +1047,6 @@ mod tests {
);
}
#[test]
fn pallet_parameter_may_be_updated_by_root() {
run_test(|| {
get_ready_for_events();
let parameter = TestMessagesParameter::TokenConversionRate(10.into());
assert_ok!(Pallet::<TestRuntime>::update_pallet_parameter(
RuntimeOrigin::root(),
parameter.clone(),
));
assert_eq!(TokenConversionRate::get(), 10.into());
assert_eq!(
System::<TestRuntime>::events(),
vec![EventRecord {
phase: Phase::Initialization,
event: TestEvent::Messages(Event::ParameterUpdated { parameter }),
topics: vec![],
}],
);
});
}
#[test]
fn pallet_parameter_may_be_updated_by_owner() {
run_test(|| {
PalletOwner::<TestRuntime>::put(2);
get_ready_for_events();
let parameter = TestMessagesParameter::TokenConversionRate(10.into());
assert_ok!(Pallet::<TestRuntime>::update_pallet_parameter(
RuntimeOrigin::signed(2),
parameter.clone(),
));
assert_eq!(TokenConversionRate::get(), 10.into());
assert_eq!(
System::<TestRuntime>::events(),
vec![EventRecord {
phase: Phase::Initialization,
event: TestEvent::Messages(Event::ParameterUpdated { parameter }),
topics: vec![],
}],
);
});
}
#[test]
fn pallet_parameter_cant_be_updated_by_arbitrary_submitter() {
run_test(|| {
assert_noop!(
Pallet::<TestRuntime>::update_pallet_parameter(
RuntimeOrigin::signed(2),
TestMessagesParameter::TokenConversionRate(10.into()),
),
DispatchError::BadOrigin,
);
PalletOwner::<TestRuntime>::put(2);
assert_noop!(
Pallet::<TestRuntime>::update_pallet_parameter(
RuntimeOrigin::signed(1),
TestMessagesParameter::TokenConversionRate(10.into()),
),
DispatchError::BadOrigin,
);
});
}
#[test]
fn fixed_u128_works_as_i_think() {
// this test is here just to be sure that conversion rate may be represented with FixedU128
run_test(|| {
use sp_runtime::{FixedPointNumber, FixedU128};
// 1:1 conversion that we use by default for testnets
let rialto_token = 1u64;
let rialto_token_in_millau_tokens =
TokenConversionRate::get().saturating_mul_int(rialto_token);
assert_eq!(rialto_token_in_millau_tokens, 1);
// let's say conversion rate is 1:1.7
let conversion_rate = FixedU128::saturating_from_rational(170, 100);
let rialto_tokens = 100u64;
let rialto_tokens_in_millau_tokens = conversion_rate.saturating_mul_int(rialto_tokens);
assert_eq!(rialto_tokens_in_millau_tokens, 170);
// let's say conversion rate is 1:0.25
let conversion_rate = FixedU128::saturating_from_rational(25, 100);
let rialto_tokens = 100u64;
let rialto_tokens_in_millau_tokens = conversion_rate.saturating_mul_int(rialto_tokens);
assert_eq!(rialto_tokens_in_millau_tokens, 25);
});
}
#[test]
fn pallet_rejects_transactions_if_halted() {
run_test(|| {
@@ -1324,25 +1058,14 @@ mod tests {
));
assert_noop!(
Pallet::<TestRuntime>::send_message(
send_message::<TestRuntime, ()>(
RuntimeOrigin::signed(1),
TEST_LANE_ID,
REGULAR_PAYLOAD,
REGULAR_PAYLOAD.declared_weight.ref_time(),
),
Error::<TestRuntime, ()>::NotOperatingNormally,
);
assert_noop!(
Pallet::<TestRuntime>::increase_message_fee(
RuntimeOrigin::signed(1),
TEST_LANE_ID,
1,
1,
),
Error::<TestRuntime, ()>::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted),
);
assert_noop!(
Pallet::<TestRuntime>::receive_messages_proof(
RuntimeOrigin::signed(1),
@@ -1389,22 +1112,14 @@ mod tests {
);
assert_noop!(
Pallet::<TestRuntime>::send_message(
send_message::<TestRuntime, ()>(
RuntimeOrigin::signed(1),
TEST_LANE_ID,
REGULAR_PAYLOAD,
REGULAR_PAYLOAD.declared_weight.ref_time(),
),
Error::<TestRuntime, ()>::NotOperatingNormally,
);
assert_ok!(Pallet::<TestRuntime>::increase_message_fee(
RuntimeOrigin::signed(1),
TEST_LANE_ID,
1,
1,
));
assert_ok!(Pallet::<TestRuntime>::receive_messages_proof(
RuntimeOrigin::signed(1),
TEST_RELAYER_A,
@@ -1451,25 +1166,23 @@ mod tests {
.extra
.extend_from_slice(&[0u8; MAX_OUTBOUND_PAYLOAD_SIZE as usize]);
assert_noop!(
Pallet::<TestRuntime>::send_message(
send_message::<TestRuntime, ()>(
RuntimeOrigin::signed(1),
TEST_LANE_ID,
message_payload.clone(),
Balance::MAX,
),
Error::<TestRuntime, ()>::MessageIsTooLarge,
);
// let's check that we're able to send `MAX_OUTBOUND_PAYLOAD_SIZE` messages
while message_payload.size() > MAX_OUTBOUND_PAYLOAD_SIZE {
while message_payload.encoded_size() as u32 > MAX_OUTBOUND_PAYLOAD_SIZE {
message_payload.extra.pop();
}
assert_eq!(message_payload.size(), MAX_OUTBOUND_PAYLOAD_SIZE);
assert_ok!(Pallet::<TestRuntime>::send_message(
assert_eq!(message_payload.encoded_size() as u32, MAX_OUTBOUND_PAYLOAD_SIZE);
assert_ok!(send_message::<TestRuntime, ()>(
RuntimeOrigin::signed(1),
TEST_LANE_ID,
message_payload,
Balance::MAX,
),);
})
}
@@ -1479,11 +1192,10 @@ mod tests {
run_test(|| {
// messages with this payload are rejected by target chain verifier
assert_noop!(
Pallet::<TestRuntime>::send_message(
send_message::<TestRuntime, ()>(
RuntimeOrigin::signed(1),
TEST_LANE_ID,
PAYLOAD_REJECTED_BY_TARGET_CHAIN,
PAYLOAD_REJECTED_BY_TARGET_CHAIN.declared_weight.ref_time(),
),
Error::<TestRuntime, ()>::MessageRejectedByChainVerifier,
);
@@ -1494,34 +1206,15 @@ mod tests {
fn lane_verifier_rejects_invalid_message_in_send_message() {
run_test(|| {
// messages with zero fee are rejected by lane verifier
let mut message = REGULAR_PAYLOAD;
message.reject_by_lane_verifier = true;
assert_noop!(
Pallet::<TestRuntime>::send_message(
RuntimeOrigin::signed(1),
TEST_LANE_ID,
REGULAR_PAYLOAD,
0
),
send_message::<TestRuntime, ()>(RuntimeOrigin::signed(1), TEST_LANE_ID, message,),
Error::<TestRuntime, ()>::MessageRejectedByLaneVerifier,
);
});
}
#[test]
fn message_send_fails_if_submitter_cant_pay_message_fee() {
run_test(|| {
TestMessageDeliveryAndDispatchPayment::reject_payments();
assert_noop!(
Pallet::<TestRuntime>::send_message(
RuntimeOrigin::signed(1),
TEST_LANE_ID,
REGULAR_PAYLOAD,
REGULAR_PAYLOAD.declared_weight.ref_time(),
),
Error::<TestRuntime, ()>::FailedToWithdrawMessageFee,
);
});
}
#[test]
fn receive_messages_proof_works() {
run_test(|| {
@@ -1665,17 +1358,15 @@ mod tests {
#[test]
fn receive_messages_delivery_proof_rewards_relayers() {
run_test(|| {
assert_ok!(Pallet::<TestRuntime>::send_message(
assert_ok!(send_message::<TestRuntime, ()>(
RuntimeOrigin::signed(1),
TEST_LANE_ID,
REGULAR_PAYLOAD,
1000,
));
assert_ok!(Pallet::<TestRuntime>::send_message(
assert_ok!(send_message::<TestRuntime, ()>(
RuntimeOrigin::signed(1),
TEST_LANE_ID,
REGULAR_PAYLOAD,
2000,
));
// this reports delivery of message 1 => reward is paid to TEST_RELAYER_A
@@ -1697,8 +1388,8 @@ mod tests {
..Default::default()
},
));
assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_A, 1000));
assert!(!TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_B, 2000));
assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_A, 1));
assert!(!TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_B, 1));
// this reports delivery of both message 1 and message 2 => reward is paid only to
// TEST_RELAYER_B
@@ -1723,8 +1414,8 @@ mod tests {
..Default::default()
},
));
assert!(!TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_A, 1000));
assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_B, 2000));
assert!(!TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_A, 1));
assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_B, 1));
});
}
@@ -1829,7 +1520,7 @@ mod tests {
fn receive_messages_accepts_single_message_with_invalid_payload() {
run_test(|| {
let mut invalid_message = message(1, REGULAR_PAYLOAD);
invalid_message.data.payload = Vec::new();
invalid_message.payload = Vec::new();
assert_ok!(Pallet::<TestRuntime, ()>::receive_messages_proof(
RuntimeOrigin::signed(1),
@@ -1848,7 +1539,7 @@ mod tests {
fn receive_messages_accepts_batch_with_message_with_invalid_payload() {
run_test(|| {
let mut invalid_message = message(2, REGULAR_PAYLOAD);
invalid_message.data.payload = Vec::new();
invalid_message.payload = Vec::new();
assert_ok!(Pallet::<TestRuntime, ()>::receive_messages_proof(
RuntimeOrigin::signed(1),
@@ -1884,73 +1575,6 @@ mod tests {
});
}
#[test]
fn increase_message_fee_fails_if_message_is_already_delivered() {
run_test(|| {
send_regular_message();
receive_messages_delivery_proof();
assert_noop!(
Pallet::<TestRuntime, ()>::increase_message_fee(
RuntimeOrigin::signed(1),
TEST_LANE_ID,
1,
100,
),
Error::<TestRuntime, ()>::MessageIsAlreadyDelivered,
);
});
}
#[test]
fn increase_message_fee_fails_if_message_is_not_yet_sent() {
run_test(|| {
assert_noop!(
Pallet::<TestRuntime, ()>::increase_message_fee(
RuntimeOrigin::signed(1),
TEST_LANE_ID,
1,
100,
),
Error::<TestRuntime, ()>::MessageIsNotYetSent,
);
});
}
#[test]
fn increase_message_fee_fails_if_submitter_cant_pay_additional_fee() {
run_test(|| {
send_regular_message();
TestMessageDeliveryAndDispatchPayment::reject_payments();
assert_noop!(
Pallet::<TestRuntime, ()>::increase_message_fee(
RuntimeOrigin::signed(1),
TEST_LANE_ID,
1,
100,
),
Error::<TestRuntime, ()>::FailedToWithdrawMessageFee,
);
});
}
#[test]
fn increase_message_fee_succeeds() {
run_test(|| {
send_regular_message();
assert_ok!(Pallet::<TestRuntime, ()>::increase_message_fee(
RuntimeOrigin::signed(1),
TEST_LANE_ID,
1,
100,
),);
assert!(TestMessageDeliveryAndDispatchPayment::is_fee_paid(1, 100));
});
}
#[test]
fn weight_refund_from_receive_messages_proof_works() {
run_test(|| {
@@ -2187,101 +1811,6 @@ mod tests {
});
}
#[test]
fn increase_message_fee_weight_depends_on_message_size() {
run_test(|| {
let mut small_payload = message_payload(0, 100);
let mut large_payload = message_payload(1, 100);
small_payload.extra = vec![1; MAX_OUTBOUND_PAYLOAD_SIZE as usize / 10];
large_payload.extra = vec![2; MAX_OUTBOUND_PAYLOAD_SIZE as usize / 5];
assert_ok!(Pallet::<TestRuntime>::send_message(
RuntimeOrigin::signed(1),
TEST_LANE_ID,
small_payload,
100,
));
assert_ok!(Pallet::<TestRuntime>::send_message(
RuntimeOrigin::signed(1),
TEST_LANE_ID,
large_payload,
100,
));
let small_weight = Pallet::<TestRuntime>::increase_message_fee(
RuntimeOrigin::signed(1),
TEST_LANE_ID,
1,
1,
)
.expect("increase_message_fee has failed")
.actual_weight
.expect("increase_message_fee always returns Some");
let large_weight = Pallet::<TestRuntime>::increase_message_fee(
RuntimeOrigin::signed(1),
TEST_LANE_ID,
2,
1,
)
.expect("increase_message_fee has failed")
.actual_weight
.expect("increase_message_fee always returns Some");
assert!(
large_weight.ref_time() > small_weight.ref_time(),
"Actual post-dispatch weigth for larger message {} must be larger than {} for small message",
large_weight,
small_weight,
);
});
}
#[test]
fn weight_is_refunded_for_messages_that_are_not_pruned() {
run_test(|| {
// send first MAX messages - no messages are pruned
let max_messages_to_prune = crate::mock::MaxMessagesToPruneAtOnce::get();
let when_zero_messages_are_pruned = send_regular_message();
let mut delivered_messages = DeliveredMessages::new(1, true);
for _ in 1..max_messages_to_prune {
assert_eq!(send_regular_message(), when_zero_messages_are_pruned);
delivered_messages.note_dispatched_message(true);
}
// confirm delivery of all sent messages
assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
RuntimeOrigin::signed(1),
TestMessagesDeliveryProof(Ok((
TEST_LANE_ID,
InboundLaneData {
last_confirmed_nonce: 1,
relayers: vec![UnrewardedRelayer {
relayer: 0,
messages: delivered_messages,
}]
.into_iter()
.collect(),
},
))),
UnrewardedRelayersState {
unrewarded_relayer_entries: 1,
total_messages: max_messages_to_prune,
last_delivered_nonce: max_messages_to_prune,
..Default::default()
},
));
// when next message is sent, MAX messages are pruned
let weight_when_max_messages_are_pruned = send_regular_message();
assert_eq!(
weight_when_max_messages_are_pruned,
when_zero_messages_are_pruned +
crate::mock::DbWeight::get().writes(max_messages_to_prune),
);
});
}
#[test]
fn message_accepted_callbacks_are_called() {
run_test(|| {
@@ -2302,27 +1831,6 @@ mod tests {
});
}
#[test]
fn message_accepted_refunds_non_zero_weight() {
run_test(|| {
TestOnMessageAccepted::set_consumed_weight_per_message(
crate::mock::DbWeight::get().writes(1),
);
let actual_callback_weight = send_regular_message();
let pre_dispatch_weight = <TestRuntime as Config>::WeightInfo::send_message_weight(
&REGULAR_PAYLOAD,
crate::mock::DbWeight::get(),
);
let prune_weight = crate::mock::DbWeight::get()
.writes(<TestRuntime as Config>::MaxMessagesToPruneAtOnce::get());
assert_eq!(
pre_dispatch_weight.saturating_sub(actual_callback_weight),
crate::mock::DbWeight::get().reads(1).saturating_add(prune_weight)
);
});
}
#[test]
fn storage_keys_computed_properly() {
assert_eq!(
@@ -2360,9 +1868,6 @@ mod tests {
nonce: 0,
dispatch_weight: Weight::from_ref_time(0),
size: 0,
delivery_and_dispatch_fee: 0,
dispatch_fee_payment:
bp_runtime::messages::DispatchFeePayment::AtTargetChain,
},
),
InboundMessageDetails { dispatch_weight: REGULAR_PAYLOAD.declared_weight },
+37 -74
View File
@@ -26,10 +26,11 @@ use bp_messages::{
OnMessageAccepted, TargetHeaderChain,
},
target_chain::{
DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, SourceHeaderChain,
DispatchMessage, DispatchMessageData, MessageDispatch, ProvedLaneMessages, ProvedMessages,
SourceHeaderChain,
},
DeliveredMessages, InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce,
OutboundLaneData, Parameter as MessagesParameter, UnrewardedRelayer,
DeliveredMessages, InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload,
OutboundLaneData, UnrewardedRelayer,
};
use bp_runtime::{messages::MessageDispatchResult, Size};
use codec::{Decode, Encode};
@@ -42,7 +43,7 @@ use sp_core::H256;
use sp_runtime::{
testing::Header as SubstrateHeader,
traits::{BlakeTwo256, IdentityLookup},
FixedU128, Perbill,
Perbill,
};
use std::{
collections::{BTreeMap, VecDeque},
@@ -55,6 +56,8 @@ pub type Balance = u64;
pub struct TestPayload {
/// Field that may be used to identify messages.
pub id: u64,
/// Reject this message by lane verifier?
pub reject_by_lane_verifier: bool,
/// Dispatch weight that is declared by the message sender.
pub declared_weight: Weight,
/// Message dispatch result.
@@ -140,38 +143,20 @@ parameter_types! {
pub const MaxMessagesToPruneAtOnce: u64 = 10;
pub const MaxUnrewardedRelayerEntriesAtInboundLane: u64 = 16;
pub const MaxUnconfirmedMessagesAtInboundLane: u64 = 32;
pub storage TokenConversionRate: FixedU128 = 1.into();
pub const TestBridgedChainId: bp_runtime::ChainId = *b"test";
}
#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq, TypeInfo)]
pub enum TestMessagesParameter {
TokenConversionRate(FixedU128),
}
impl MessagesParameter for TestMessagesParameter {
fn save(&self) {
match *self {
TestMessagesParameter::TokenConversionRate(conversion_rate) =>
TokenConversionRate::set(&conversion_rate),
}
}
}
impl Config for TestRuntime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type Parameter = TestMessagesParameter;
type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce;
type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane;
type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane;
type MaximalOutboundPayloadSize = frame_support::traits::ConstU32<MAX_OUTBOUND_PAYLOAD_SIZE>;
type OutboundPayload = TestPayload;
type OutboundMessageFee = TestMessageFee;
type InboundPayload = TestPayload;
type InboundMessageFee = TestMessageFee;
type InboundRelayer = TestRelayer;
type TargetHeaderChain = TestTargetHeaderChain;
@@ -219,7 +204,7 @@ pub const REGULAR_PAYLOAD: TestPayload = message_payload(0, 50);
pub const PAYLOAD_REJECTED_BY_TARGET_CHAIN: TestPayload = message_payload(1, 50);
/// Vec of proved messages, grouped by lane.
pub type MessagesByLaneVec = Vec<(LaneId, ProvedLaneMessages<Message<TestMessageFee>>)>;
pub type MessagesByLaneVec = Vec<(LaneId, ProvedLaneMessages<Message>)>;
/// Test messages proof.
#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)]
@@ -233,14 +218,12 @@ impl Size for TestMessagesProof {
}
}
impl From<Result<Vec<Message<TestMessageFee>>, ()>> for TestMessagesProof {
fn from(result: Result<Vec<Message<TestMessageFee>>, ()>) -> Self {
impl From<Result<Vec<Message>, ()>> for TestMessagesProof {
fn from(result: Result<Vec<Message>, ()>) -> Self {
Self {
result: result.map(|messages| {
let mut messages_by_lane: BTreeMap<
LaneId,
ProvedLaneMessages<Message<TestMessageFee>>,
> = BTreeMap::new();
let mut messages_by_lane: BTreeMap<LaneId, ProvedLaneMessages<Message>> =
BTreeMap::new();
for message in messages {
messages_by_lane.entry(message.key.lane_id).or_default().messages.push(message);
}
@@ -288,17 +271,16 @@ impl TargetHeaderChain<TestPayload, TestRelayer> for TestTargetHeaderChain {
#[derive(Debug, Default)]
pub struct TestLaneMessageVerifier;
impl LaneMessageVerifier<RuntimeOrigin, TestPayload, TestMessageFee> for TestLaneMessageVerifier {
impl LaneMessageVerifier<RuntimeOrigin, TestPayload> for TestLaneMessageVerifier {
type Error = &'static str;
fn verify_message(
_submitter: &RuntimeOrigin,
delivery_and_dispatch_fee: &TestMessageFee,
_lane: &LaneId,
_lane_outbound_data: &OutboundLaneData,
_payload: &TestPayload,
payload: &TestPayload,
) -> Result<(), Self::Error> {
if *delivery_and_dispatch_fee != 0 {
if !payload.reject_by_lane_verifier {
Ok(())
} else {
Err(TEST_ERROR)
@@ -311,18 +293,6 @@ impl LaneMessageVerifier<RuntimeOrigin, TestPayload, TestMessageFee> for TestLan
pub struct TestMessageDeliveryAndDispatchPayment;
impl TestMessageDeliveryAndDispatchPayment {
/// Reject all payments.
pub fn reject_payments() {
frame_support::storage::unhashed::put(b":reject-message-fee:", &true);
}
/// Returns true if given fee has been paid by given submitter.
pub fn is_fee_paid(submitter: AccountId, fee: TestMessageFee) -> bool {
let raw_origin: Result<frame_system::RawOrigin<_>, _> =
RuntimeOrigin::signed(submitter).into();
frame_support::storage::unhashed::get(b":message-fee:") == Some((raw_origin.unwrap(), fee))
}
/// Returns true if given relayer has been rewarded with given balance. The reward-paid flag is
/// cleared after the call.
pub fn is_reward_paid(relayer: AccountId, fee: TestMessageFee) -> bool {
@@ -331,34 +301,21 @@ impl TestMessageDeliveryAndDispatchPayment {
}
}
impl MessageDeliveryAndDispatchPayment<RuntimeOrigin, AccountId, TestMessageFee>
impl MessageDeliveryAndDispatchPayment<RuntimeOrigin, AccountId>
for TestMessageDeliveryAndDispatchPayment
{
type Error = &'static str;
fn pay_delivery_and_dispatch_fee(
submitter: &RuntimeOrigin,
fee: &TestMessageFee,
) -> Result<(), Self::Error> {
if frame_support::storage::unhashed::get(b":reject-message-fee:") == Some(true) {
return Err(TEST_ERROR)
}
let raw_origin: Result<frame_system::RawOrigin<_>, _> = submitter.clone().into();
frame_support::storage::unhashed::put(b":message-fee:", &(raw_origin.unwrap(), fee));
Ok(())
}
fn pay_relayers_rewards(
lane_id: LaneId,
_lane_id: LaneId,
message_relayers: VecDeque<UnrewardedRelayer<AccountId>>,
_confirmation_relayer: &AccountId,
received_range: &RangeInclusive<MessageNonce>,
) {
let relayers_rewards =
calc_relayers_rewards::<TestRuntime, ()>(lane_id, message_relayers, received_range);
calc_relayers_rewards::<TestRuntime, ()>(message_relayers, received_range);
for (relayer, reward) in &relayers_rewards {
let key = (b":relayer-reward:", relayer, reward.reward).encode();
let key = (b":relayer-reward:", relayer, reward).encode();
frame_support::storage::unhashed::put(&key, &true);
}
}
@@ -450,7 +407,7 @@ impl OnDeliveryConfirmed for TestOnDeliveryConfirmed2 {
#[derive(Debug)]
pub struct TestSourceHeaderChain;
impl SourceHeaderChain<TestMessageFee> for TestSourceHeaderChain {
impl SourceHeaderChain for TestSourceHeaderChain {
type Error = &'static str;
type MessagesProof = TestMessagesProof;
@@ -458,7 +415,7 @@ impl SourceHeaderChain<TestMessageFee> for TestSourceHeaderChain {
fn verify_messages_proof(
proof: Self::MessagesProof,
_messages_count: u32,
) -> Result<ProvedMessages<Message<TestMessageFee>>, Self::Error> {
) -> Result<ProvedMessages<Message>, Self::Error> {
proof.result.map(|proof| proof.into_iter().collect()).map_err(|_| TEST_ERROR)
}
}
@@ -467,10 +424,10 @@ impl SourceHeaderChain<TestMessageFee> for TestSourceHeaderChain {
#[derive(Debug)]
pub struct TestMessageDispatch;
impl MessageDispatch<AccountId, TestMessageFee> for TestMessageDispatch {
impl MessageDispatch<AccountId> for TestMessageDispatch {
type DispatchPayload = TestPayload;
fn dispatch_weight(message: &mut DispatchMessage<TestPayload, TestMessageFee>) -> Weight {
fn dispatch_weight(message: &mut DispatchMessage<TestPayload>) -> Weight {
match message.data.payload.as_ref() {
Ok(payload) => payload.declared_weight,
Err(_) => Weight::from_ref_time(0),
@@ -479,7 +436,7 @@ impl MessageDispatch<AccountId, TestMessageFee> for TestMessageDispatch {
fn dispatch(
_relayer_account: &AccountId,
message: DispatchMessage<TestPayload, TestMessageFee>,
message: DispatchMessage<TestPayload>,
) -> MessageDispatchResult {
match message.data.payload.as_ref() {
Ok(payload) => payload.dispatch_result.clone(),
@@ -489,25 +446,31 @@ impl MessageDispatch<AccountId, TestMessageFee> for TestMessageDispatch {
}
/// Return test lane message with given nonce and payload.
pub fn message(nonce: MessageNonce, payload: TestPayload) -> Message<TestMessageFee> {
Message { key: MessageKey { lane_id: TEST_LANE_ID, nonce }, data: message_data(payload) }
pub fn message(nonce: MessageNonce, payload: TestPayload) -> Message {
Message { key: MessageKey { lane_id: TEST_LANE_ID, nonce }, payload: payload.encode() }
}
/// Return valid outbound message data, constructed from given payload.
pub fn outbound_message_data(payload: TestPayload) -> MessagePayload {
payload.encode()
}
/// Return valid inbound (dispatch) message data, constructed from given payload.
pub fn inbound_message_data(payload: TestPayload) -> DispatchMessageData<TestPayload> {
DispatchMessageData { payload: Ok(payload) }
}
/// Constructs message payload using given arguments and zero unspent weight.
pub const fn message_payload(id: u64, declared_weight: u64) -> TestPayload {
TestPayload {
id,
reject_by_lane_verifier: false,
declared_weight: Weight::from_ref_time(declared_weight),
dispatch_result: dispatch_result(0),
extra: Vec::new(),
}
}
/// Return message data with valid fee for given payload.
pub fn message_data(payload: TestPayload) -> MessageData<TestMessageFee> {
MessageData { payload: payload.encode(), fee: 1 }
}
/// Returns message dispatch result with given unspent weight.
pub const fn dispatch_result(unspent_weight: u64) -> MessageDispatchResult {
MessageDispatchResult {
+26 -80
View File
@@ -20,19 +20,14 @@ use crate::Config;
use bitvec::prelude::*;
use bp_messages::{
DeliveredMessages, DispatchResultsBitVec, LaneId, MessageData, MessageNonce, OutboundLaneData,
UnrewardedRelayer,
DeliveredMessages, DispatchResultsBitVec, LaneId, MessageNonce, MessagePayload,
OutboundLaneData, UnrewardedRelayer,
};
use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};
use frame_support::{traits::Get, RuntimeDebug};
use scale_info::{Type, TypeInfo};
use frame_support::{BoundedVec, RuntimeDebug};
use sp_std::collections::vec_deque::VecDeque;
/// Outbound lane storage.
pub trait OutboundLaneStorage {
/// Delivery and dispatch fee type on source chain.
type MessageFee;
/// Lane id.
fn id(&self) -> LaneId;
/// Get lane data from the storage.
@@ -41,64 +36,15 @@ pub trait OutboundLaneStorage {
fn set_data(&mut self, data: OutboundLaneData);
/// Returns saved outbound message payload.
#[cfg(test)]
fn message(&self, nonce: &MessageNonce) -> Option<MessageData<Self::MessageFee>>;
fn message(&self, nonce: &MessageNonce) -> Option<MessagePayload>;
/// Save outbound message in the storage.
fn save_message(&mut self, nonce: MessageNonce, message_data: MessageData<Self::MessageFee>);
fn save_message(&mut self, nonce: MessageNonce, message_payload: MessagePayload);
/// Remove outbound message from the storage.
fn remove_message(&mut self, nonce: &MessageNonce);
}
/// Outbound message data wrapper that implements `MaxEncodedLen`.
///
/// We have already had `MaxEncodedLen`-like functionality before, but its usage has
/// been localized and we haven't been passing it everywhere. This wrapper allows us
/// to avoid passing these generic bounds all over the code.
///
/// The encoding of this type matches encoding of the corresponding `MessageData`.
#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq)]
pub struct StoredMessageData<T: Config<I>, I: 'static>(pub MessageData<T::OutboundMessageFee>);
impl<T: Config<I>, I: 'static> sp_std::ops::Deref for StoredMessageData<T, I> {
type Target = MessageData<T::OutboundMessageFee>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: Config<I>, I: 'static> sp_std::ops::DerefMut for StoredMessageData<T, I> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T: Config<I>, I: 'static> From<StoredMessageData<T, I>>
for MessageData<T::OutboundMessageFee>
{
fn from(data: StoredMessageData<T, I>) -> Self {
data.0
}
}
impl<T: Config<I>, I: 'static> TypeInfo for StoredMessageData<T, I> {
type Identity = Self;
fn type_info() -> Type {
MessageData::<T::OutboundMessageFee>::type_info()
}
}
impl<T: Config<I>, I: 'static> EncodeLike<StoredMessageData<T, I>>
for MessageData<T::OutboundMessageFee>
{
}
impl<T: Config<I>, I: 'static> MaxEncodedLen for StoredMessageData<T, I> {
fn max_encoded_len() -> usize {
T::OutboundMessageFee::max_encoded_len()
.saturating_add(T::MaximalOutboundPayloadSize::get() as usize)
}
}
pub type StoredMessagePayload<T, I> = BoundedVec<u8, <T as Config<I>>::MaximalOutboundPayloadSize>;
/// Result of messages receival confirmation.
#[derive(RuntimeDebug, PartialEq, Eq)]
@@ -143,12 +89,12 @@ impl<S: OutboundLaneStorage> OutboundLane<S> {
/// Send message over lane.
///
/// Returns new message nonce.
pub fn send_message(&mut self, message_data: MessageData<S::MessageFee>) -> MessageNonce {
pub fn send_message(&mut self, message_payload: MessagePayload) -> MessageNonce {
let mut data = self.storage.data();
let nonce = data.latest_generated_nonce + 1;
data.latest_generated_nonce = nonce;
self.storage.save_message(nonce, message_data);
self.storage.save_message(nonce, message_payload);
self.storage.set_data(data);
nonce
@@ -295,8 +241,8 @@ mod tests {
use super::*;
use crate::{
mock::{
message_data, run_test, unrewarded_relayer, TestRelayer, TestRuntime, REGULAR_PAYLOAD,
TEST_LANE_ID,
outbound_message_data, run_test, unrewarded_relayer, TestRelayer, TestRuntime,
REGULAR_PAYLOAD, TEST_LANE_ID,
},
outbound_lane,
};
@@ -324,9 +270,9 @@ mod tests {
) -> ReceivalConfirmationResult {
run_test(|| {
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
lane.send_message(message_data(REGULAR_PAYLOAD));
lane.send_message(message_data(REGULAR_PAYLOAD));
lane.send_message(message_data(REGULAR_PAYLOAD));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
assert_eq!(lane.storage.data().latest_received_nonce, 0);
let result = lane.confirm_delivery(3, latest_received_nonce, relayers);
@@ -341,7 +287,7 @@ mod tests {
run_test(|| {
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
assert_eq!(lane.storage.data().latest_generated_nonce, 0);
assert_eq!(lane.send_message(message_data(REGULAR_PAYLOAD)), 1);
assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1);
assert!(lane.storage.message(&1).is_some());
assert_eq!(lane.storage.data().latest_generated_nonce, 1);
});
@@ -351,9 +297,9 @@ mod tests {
fn confirm_delivery_works() {
run_test(|| {
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
assert_eq!(lane.send_message(message_data(REGULAR_PAYLOAD)), 1);
assert_eq!(lane.send_message(message_data(REGULAR_PAYLOAD)), 2);
assert_eq!(lane.send_message(message_data(REGULAR_PAYLOAD)), 3);
assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1);
assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 2);
assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 3);
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
assert_eq!(lane.storage.data().latest_received_nonce, 0);
assert_eq!(
@@ -369,9 +315,9 @@ mod tests {
fn confirm_delivery_rejects_nonce_lesser_than_latest_received() {
run_test(|| {
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
lane.send_message(message_data(REGULAR_PAYLOAD));
lane.send_message(message_data(REGULAR_PAYLOAD));
lane.send_message(message_data(REGULAR_PAYLOAD));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
assert_eq!(lane.storage.data().latest_received_nonce, 0);
assert_eq!(
@@ -470,9 +416,9 @@ mod tests {
assert_eq!(lane.prune_messages(100), 0);
assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1);
// when nothing is confirmed, nothing is pruned
lane.send_message(message_data(REGULAR_PAYLOAD));
lane.send_message(message_data(REGULAR_PAYLOAD));
lane.send_message(message_data(REGULAR_PAYLOAD));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
assert_eq!(lane.prune_messages(100), 0);
assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1);
// after confirmation, some messages are received
@@ -496,9 +442,9 @@ mod tests {
fn confirm_delivery_detects_when_more_than_expected_messages_are_confirmed() {
run_test(|| {
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
lane.send_message(message_data(REGULAR_PAYLOAD));
lane.send_message(message_data(REGULAR_PAYLOAD));
lane.send_message(message_data(REGULAR_PAYLOAD));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
assert_eq!(
lane.confirm_delivery(0, 3, &unrewarded_relayers(1..=3)),
ReceivalConfirmationResult::TryingToConfirmMoreMessagesThanExpected(3),
+25 -82
View File
@@ -17,7 +17,7 @@
//! Autogenerated weights for `pallet_bridge_messages`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2022-10-17, STEPS: 50, REPEAT: 20
//! DATE: 2022-11-17, STEPS: 50, REPEAT: 20
//! LOW RANGE: [], HIGH RANGE: []
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled
//! CHAIN: Some("dev"), DB CACHE: 1024
@@ -50,11 +50,6 @@ use sp_std::marker::PhantomData;
/// Weight functions needed for `pallet_bridge_messages`.
pub trait WeightInfo {
fn send_minimal_message_worst_case() -> Weight;
fn send_1_kb_message_worst_case() -> Weight;
fn send_16_kb_message_worst_case() -> Weight;
fn maximal_increase_message_fee() -> Weight;
fn increase_message_fee(i: u32) -> Weight;
fn receive_single_message_proof() -> Weight;
fn receive_two_messages_proof() -> Weight;
fn receive_single_message_proof_with_outbound_lane_state() -> Weight;
@@ -71,150 +66,98 @@ pub trait WeightInfo {
/// Those weights are test only and must never be used in production.
pub struct BridgeWeight<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for BridgeWeight<T> {
fn send_minimal_message_worst_case() -> Weight {
Weight::from_ref_time(61_807_000 as u64)
.saturating_add(T::DbWeight::get().reads(5 as u64))
.saturating_add(T::DbWeight::get().writes(10 as u64))
}
fn send_1_kb_message_worst_case() -> Weight {
Weight::from_ref_time(65_074_000 as u64)
.saturating_add(T::DbWeight::get().reads(5 as u64))
.saturating_add(T::DbWeight::get().writes(10 as u64))
}
fn send_16_kb_message_worst_case() -> Weight {
Weight::from_ref_time(73_584_000 as u64)
.saturating_add(T::DbWeight::get().reads(5 as u64))
.saturating_add(T::DbWeight::get().writes(10 as u64))
}
fn maximal_increase_message_fee() -> Weight {
Weight::from_ref_time(2_522_629_000 as u64)
.saturating_add(T::DbWeight::get().reads(3 as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
}
fn increase_message_fee(i: u32) -> Weight {
Weight::from_ref_time(27_072_000 as u64)
.saturating_add(Weight::from_ref_time(892 as u64).saturating_mul(i as u64))
.saturating_add(T::DbWeight::get().reads(3 as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
}
fn receive_single_message_proof() -> Weight {
Weight::from_ref_time(49_628_000 as u64)
Weight::from_ref_time(50_596_000 as u64)
.saturating_add(T::DbWeight::get().reads(4 as u64))
.saturating_add(T::DbWeight::get().writes(2 as u64))
}
fn receive_two_messages_proof() -> Weight {
Weight::from_ref_time(61_514_000 as u64)
Weight::from_ref_time(77_041_000 as u64)
.saturating_add(T::DbWeight::get().reads(4 as u64))
.saturating_add(T::DbWeight::get().writes(2 as u64))
}
fn receive_single_message_proof_with_outbound_lane_state() -> Weight {
Weight::from_ref_time(65_960_000 as u64)
Weight::from_ref_time(58_331_000 as u64)
.saturating_add(T::DbWeight::get().reads(4 as u64))
.saturating_add(T::DbWeight::get().writes(2 as u64))
}
fn receive_single_message_proof_1_kb() -> Weight {
Weight::from_ref_time(48_009_000 as u64)
Weight::from_ref_time(48_061_000 as u64)
.saturating_add(T::DbWeight::get().reads(3 as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
}
fn receive_single_message_proof_16_kb() -> Weight {
Weight::from_ref_time(100_439_000 as u64)
Weight::from_ref_time(101_601_000 as u64)
.saturating_add(T::DbWeight::get().reads(3 as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
}
fn receive_single_prepaid_message_proof() -> Weight {
Weight::from_ref_time(50_463_000 as u64)
Weight::from_ref_time(49_646_000 as u64)
.saturating_add(T::DbWeight::get().reads(4 as u64))
.saturating_add(T::DbWeight::get().writes(2 as u64))
}
fn receive_delivery_proof_for_single_message() -> Weight {
Weight::from_ref_time(57_383_000 as u64)
.saturating_add(T::DbWeight::get().reads(5 as u64))
Weight::from_ref_time(55_108_000 as u64)
.saturating_add(T::DbWeight::get().reads(4 as u64))
.saturating_add(T::DbWeight::get().writes(2 as u64))
}
fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight {
Weight::from_ref_time(62_003_000 as u64)
.saturating_add(T::DbWeight::get().reads(6 as u64))
Weight::from_ref_time(53_917_000 as u64)
.saturating_add(T::DbWeight::get().reads(4 as u64))
.saturating_add(T::DbWeight::get().writes(2 as u64))
}
fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight {
Weight::from_ref_time(64_401_000 as u64)
.saturating_add(T::DbWeight::get().reads(7 as u64))
Weight::from_ref_time(57_335_000 as u64)
.saturating_add(T::DbWeight::get().reads(5 as u64))
.saturating_add(T::DbWeight::get().writes(3 as u64))
}
}
// For backwards compatibility and tests
impl WeightInfo for () {
fn send_minimal_message_worst_case() -> Weight {
Weight::from_ref_time(61_807_000 as u64)
.saturating_add(RocksDbWeight::get().reads(5 as u64))
.saturating_add(RocksDbWeight::get().writes(10 as u64))
}
fn send_1_kb_message_worst_case() -> Weight {
Weight::from_ref_time(65_074_000 as u64)
.saturating_add(RocksDbWeight::get().reads(5 as u64))
.saturating_add(RocksDbWeight::get().writes(10 as u64))
}
fn send_16_kb_message_worst_case() -> Weight {
Weight::from_ref_time(73_584_000 as u64)
.saturating_add(RocksDbWeight::get().reads(5 as u64))
.saturating_add(RocksDbWeight::get().writes(10 as u64))
}
fn maximal_increase_message_fee() -> Weight {
Weight::from_ref_time(2_522_629_000 as u64)
.saturating_add(RocksDbWeight::get().reads(3 as u64))
.saturating_add(RocksDbWeight::get().writes(1 as u64))
}
fn increase_message_fee(i: u32) -> Weight {
Weight::from_ref_time(27_072_000 as u64)
.saturating_add(Weight::from_ref_time(892 as u64).saturating_mul(i as u64))
.saturating_add(RocksDbWeight::get().reads(3 as u64))
.saturating_add(RocksDbWeight::get().writes(1 as u64))
}
fn receive_single_message_proof() -> Weight {
Weight::from_ref_time(49_628_000 as u64)
Weight::from_ref_time(50_596_000 as u64)
.saturating_add(RocksDbWeight::get().reads(4 as u64))
.saturating_add(RocksDbWeight::get().writes(2 as u64))
}
fn receive_two_messages_proof() -> Weight {
Weight::from_ref_time(61_514_000 as u64)
Weight::from_ref_time(77_041_000 as u64)
.saturating_add(RocksDbWeight::get().reads(4 as u64))
.saturating_add(RocksDbWeight::get().writes(2 as u64))
}
fn receive_single_message_proof_with_outbound_lane_state() -> Weight {
Weight::from_ref_time(65_960_000 as u64)
Weight::from_ref_time(58_331_000 as u64)
.saturating_add(RocksDbWeight::get().reads(4 as u64))
.saturating_add(RocksDbWeight::get().writes(2 as u64))
}
fn receive_single_message_proof_1_kb() -> Weight {
Weight::from_ref_time(48_009_000 as u64)
Weight::from_ref_time(48_061_000 as u64)
.saturating_add(RocksDbWeight::get().reads(3 as u64))
.saturating_add(RocksDbWeight::get().writes(1 as u64))
}
fn receive_single_message_proof_16_kb() -> Weight {
Weight::from_ref_time(100_439_000 as u64)
Weight::from_ref_time(101_601_000 as u64)
.saturating_add(RocksDbWeight::get().reads(3 as u64))
.saturating_add(RocksDbWeight::get().writes(1 as u64))
}
fn receive_single_prepaid_message_proof() -> Weight {
Weight::from_ref_time(50_463_000 as u64)
Weight::from_ref_time(49_646_000 as u64)
.saturating_add(RocksDbWeight::get().reads(4 as u64))
.saturating_add(RocksDbWeight::get().writes(2 as u64))
}
fn receive_delivery_proof_for_single_message() -> Weight {
Weight::from_ref_time(57_383_000 as u64)
.saturating_add(RocksDbWeight::get().reads(5 as u64))
Weight::from_ref_time(55_108_000 as u64)
.saturating_add(RocksDbWeight::get().reads(4 as u64))
.saturating_add(RocksDbWeight::get().writes(2 as u64))
}
fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight {
Weight::from_ref_time(62_003_000 as u64)
.saturating_add(RocksDbWeight::get().reads(6 as u64))
Weight::from_ref_time(53_917_000 as u64)
.saturating_add(RocksDbWeight::get().reads(4 as u64))
.saturating_add(RocksDbWeight::get().writes(2 as u64))
}
fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight {
Weight::from_ref_time(64_401_000 as u64)
.saturating_add(RocksDbWeight::get().reads(7 as u64))
Weight::from_ref_time(57_335_000 as u64)
.saturating_add(RocksDbWeight::get().reads(5 as u64))
.saturating_add(RocksDbWeight::get().writes(3 as u64))
}
}
+1 -92
View File
@@ -35,82 +35,16 @@ const SIGNED_EXTENSIONS_SIZE: u32 = 1024;
pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024;
/// Ensure that weights from `WeightInfoExt` implementation are looking correct.
pub fn ensure_weights_are_correct<W: WeightInfoExt>(
expected_default_message_delivery_tx_weight: Weight,
expected_additional_byte_delivery_weight: Weight,
expected_messages_delivery_confirmation_tx_weight: Weight,
expected_pay_inbound_dispatch_fee_weight: Weight,
db_weight: RuntimeDbWeight,
) {
// verify `send_message` weight components
assert_ne!(W::send_message_overhead(), Weight::zero());
assert_ne!(W::send_message_size_overhead(0), Weight::zero());
pub fn ensure_weights_are_correct<W: WeightInfoExt>() {
// verify `receive_messages_proof` weight components
assert_ne!(W::receive_messages_proof_overhead(), Weight::zero());
assert_ne!(W::receive_messages_proof_messages_overhead(1), Weight::zero());
assert_ne!(W::receive_messages_proof_outbound_lane_state_overhead(), Weight::zero());
assert_ne!(W::storage_proof_size_overhead(1), Weight::zero());
// verify that the hardcoded value covers `receive_messages_proof` weight
let actual_single_regular_message_delivery_tx_weight = W::receive_messages_proof_weight(
&PreComputedSize(
(EXPECTED_DEFAULT_MESSAGE_LENGTH + W::expected_extra_storage_proof_size()) as usize,
),
1,
Weight::zero(),
);
assert!(
actual_single_regular_message_delivery_tx_weight
.all_lte(expected_default_message_delivery_tx_weight),
"Default message delivery transaction weight {} is larger than expected weight {}",
actual_single_regular_message_delivery_tx_weight,
expected_default_message_delivery_tx_weight,
);
// verify that hardcoded value covers additional byte length of `receive_messages_proof` weight
let actual_additional_byte_delivery_weight = W::storage_proof_size_overhead(1);
assert!(
actual_additional_byte_delivery_weight.all_lte(expected_additional_byte_delivery_weight),
"Single additional byte delivery weight {} is larger than expected weight {}",
actual_additional_byte_delivery_weight,
expected_additional_byte_delivery_weight,
);
// verify `receive_messages_delivery_proof` weight components
assert_ne!(W::receive_messages_delivery_proof_overhead(), Weight::zero());
assert_ne!(W::storage_proof_size_overhead(1), Weight::zero());
// `receive_messages_delivery_proof_messages_overhead` and
// `receive_messages_delivery_proof_relayers_overhead` may return zero if rewards are not paid
// during confirmations delivery, so we're not checking it here
// verify that the hardcoded value covers `receive_messages_delivery_proof` weight
let actual_messages_delivery_confirmation_tx_weight = W::receive_messages_delivery_proof_weight(
&PreComputedSize(W::expected_extra_storage_proof_size() as usize),
&UnrewardedRelayersState {
unrewarded_relayer_entries: 1,
total_messages: 1,
..Default::default()
},
db_weight,
);
assert!(
actual_messages_delivery_confirmation_tx_weight
.all_lte(expected_messages_delivery_confirmation_tx_weight),
"Messages delivery confirmation transaction weight {} is larger than expected weight {}",
actual_messages_delivery_confirmation_tx_weight,
expected_messages_delivery_confirmation_tx_weight,
);
// verify pay-dispatch-fee overhead for inbound messages
let actual_pay_inbound_dispatch_fee_weight = W::pay_inbound_dispatch_fee_overhead();
assert!(
actual_pay_inbound_dispatch_fee_weight.all_lte(expected_pay_inbound_dispatch_fee_weight),
"Weight {} of pay-dispatch-fee overhead for inbound messages is larger than expected weight {}",
actual_pay_inbound_dispatch_fee_weight,
expected_pay_inbound_dispatch_fee_weight,
);
}
/// Ensure that we're able to receive maximal (by-size and by-weight) message from other chain.
@@ -198,17 +132,6 @@ pub trait WeightInfoExt: WeightInfo {
// Functions that are directly mapped to extrinsics weights.
/// Weight of message send extrinsic.
fn send_message_weight(message: &impl Size, db_weight: RuntimeDbWeight) -> Weight {
let transaction_overhead = Self::send_message_overhead();
let message_size_overhead = Self::send_message_size_overhead(message.size());
let call_back_overhead = Self::single_message_callback_overhead(db_weight);
transaction_overhead
.saturating_add(message_size_overhead)
.saturating_add(call_back_overhead)
}
/// Weight of message delivery extrinsic.
fn receive_messages_proof_weight(
proof: &impl Size,
@@ -274,20 +197,6 @@ pub trait WeightInfoExt: WeightInfo {
// Functions that are used by extrinsics weights formulas.
/// Returns weight of message send transaction (`send_message`).
fn send_message_overhead() -> Weight {
Self::send_minimal_message_worst_case()
}
/// Returns weight that needs to be accounted when message of given size is sent
/// (`send_message`).
fn send_message_size_overhead(message_size: u32) -> Weight {
let message_size_in_kb = (1024u64 + message_size as u64) / 1024;
let single_kb_weight =
(Self::send_16_kb_message_worst_case() - Self::send_1_kb_message_worst_case()) / 15;
message_size_in_kb * single_kb_weight
}
/// Returns weight overhead of message delivery transaction (`receive_messages_proof`).
fn receive_messages_proof_overhead() -> Weight {
let weight_of_two_messages_and_two_tx_overheads =
+9 -9
View File
@@ -17,7 +17,7 @@
//! Autogenerated weights for `pallet_bridge_parachains`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2022-10-04, STEPS: 50, REPEAT: 20
//! DATE: 2022-11-17, STEPS: 50, REPEAT: 20
//! LOW RANGE: [], HIGH RANGE: []
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled
//! CHAIN: Some("dev"), DB CACHE: 1024
@@ -61,19 +61,19 @@ pub trait WeightInfo {
pub struct BridgeWeight<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for BridgeWeight<T> {
fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight {
Weight::from_ref_time(0 as u64)
.saturating_add(Weight::from_ref_time(24_869_000 as u64).saturating_mul(p as u64))
Weight::from_ref_time(51_173_000 as u64)
.saturating_add(Weight::from_ref_time(24_495_968 as u64).saturating_mul(p as u64))
.saturating_add(T::DbWeight::get().reads(2 as u64))
.saturating_add(T::DbWeight::get().reads((2 as u64).saturating_mul(p as u64)))
.saturating_add(T::DbWeight::get().writes((3 as u64).saturating_mul(p as u64)))
}
fn submit_parachain_heads_with_1kb_proof() -> Weight {
Weight::from_ref_time(56_262_000 as u64)
Weight::from_ref_time(58_175_000 as u64)
.saturating_add(T::DbWeight::get().reads(4 as u64))
.saturating_add(T::DbWeight::get().writes(3 as u64))
}
fn submit_parachain_heads_with_16kb_proof() -> Weight {
Weight::from_ref_time(105_189_000 as u64)
Weight::from_ref_time(101_796_000 as u64)
.saturating_add(T::DbWeight::get().reads(4 as u64))
.saturating_add(T::DbWeight::get().writes(3 as u64))
}
@@ -82,19 +82,19 @@ impl<T: frame_system::Config> WeightInfo for BridgeWeight<T> {
// For backwards compatibility and tests
impl WeightInfo for () {
fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight {
Weight::from_ref_time(0 as u64)
.saturating_add(Weight::from_ref_time(24_869_000 as u64).saturating_mul(p as u64))
Weight::from_ref_time(51_173_000 as u64)
.saturating_add(Weight::from_ref_time(24_495_968 as u64).saturating_mul(p as u64))
.saturating_add(RocksDbWeight::get().reads(2 as u64))
.saturating_add(RocksDbWeight::get().reads((2 as u64).saturating_mul(p as u64)))
.saturating_add(RocksDbWeight::get().writes((3 as u64).saturating_mul(p as u64)))
}
fn submit_parachain_heads_with_1kb_proof() -> Weight {
Weight::from_ref_time(56_262_000 as u64)
Weight::from_ref_time(58_175_000 as u64)
.saturating_add(RocksDbWeight::get().reads(4 as u64))
.saturating_add(RocksDbWeight::get().writes(3 as u64))
}
fn submit_parachain_heads_with_16kb_proof() -> Weight {
Weight::from_ref_time(105_189_000 as u64)
Weight::from_ref_time(101_796_000 as u64)
.saturating_add(RocksDbWeight::get().reads(4 as u64))
.saturating_add(RocksDbWeight::get().writes(3 as u64))
}
-3
View File
@@ -97,17 +97,14 @@ parameter_types! {
impl pallet_bridge_messages::Config for TestRuntime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type Parameter = ();
type MaxMessagesToPruneAtOnce = frame_support::traits::ConstU64<0>;
type MaxUnrewardedRelayerEntriesAtInboundLane = frame_support::traits::ConstU64<8>;
type MaxUnconfirmedMessagesAtInboundLane = frame_support::traits::ConstU64<8>;
type MaximalOutboundPayloadSize = frame_support::traits::ConstU32<1024>;
type OutboundPayload = ();
type OutboundMessageFee = Balance;
type InboundPayload = ();
type InboundMessageFee = Balance;
type InboundRelayer = AccountId;
type TargetHeaderChain = ForbidOutboundMessages;
+28 -39
View File
@@ -20,43 +20,31 @@
use crate::{Config, RelayerRewards};
use bp_messages::source_chain::{MessageDeliveryAndDispatchPayment, RelayersRewards};
use frame_support::{sp_runtime::SaturatedConversion, traits::Get};
use sp_arithmetic::traits::{Saturating, Zero};
use frame_support::sp_runtime::SaturatedConversion;
use sp_arithmetic::traits::{Saturating, UniqueSaturatedFrom, Zero};
use sp_std::{collections::vec_deque::VecDeque, marker::PhantomData, ops::RangeInclusive};
/// Adapter that allows relayers pallet to be used as a delivery+dispatch payment mechanism
/// for the messages pallet.
pub struct MessageDeliveryAndDispatchPaymentAdapter<T, MessagesInstance, GetConfirmationFee>(
PhantomData<(T, MessagesInstance, GetConfirmationFee)>,
pub struct MessageDeliveryAndDispatchPaymentAdapter<T, MessagesInstance>(
PhantomData<(T, MessagesInstance)>,
);
impl<T, MessagesInstance, GetConfirmationFee>
MessageDeliveryAndDispatchPayment<T::RuntimeOrigin, T::AccountId, T::Reward>
for MessageDeliveryAndDispatchPaymentAdapter<T, MessagesInstance, GetConfirmationFee>
impl<T, MessagesInstance> MessageDeliveryAndDispatchPayment<T::RuntimeOrigin, T::AccountId>
for MessageDeliveryAndDispatchPaymentAdapter<T, MessagesInstance>
where
T: Config + pallet_bridge_messages::Config<MessagesInstance, OutboundMessageFee = T::Reward>,
T: Config + pallet_bridge_messages::Config<MessagesInstance>,
MessagesInstance: 'static,
GetConfirmationFee: Get<T::Reward>,
{
type Error = &'static str;
fn pay_delivery_and_dispatch_fee(
_submitter: &T::RuntimeOrigin,
_fee: &T::Reward,
) -> Result<(), Self::Error> {
// nothing shall happen here, because XCM deals with fee payment (planned to be burnt?
// or transferred to the treasury?)
Ok(())
}
fn pay_relayers_rewards(
lane_id: bp_messages::LaneId,
_lane_id: bp_messages::LaneId,
messages_relayers: VecDeque<bp_messages::UnrewardedRelayer<T::AccountId>>,
confirmation_relayer: &T::AccountId,
received_range: &RangeInclusive<bp_messages::MessageNonce>,
) {
let relayers_rewards = pallet_bridge_messages::calc_relayers_rewards::<T, MessagesInstance>(
lane_id,
messages_relayers,
received_range,
);
@@ -64,7 +52,10 @@ where
register_relayers_rewards::<T>(
confirmation_relayer,
relayers_rewards,
GetConfirmationFee::get(),
// TODO (https://github.com/paritytech/parity-bridges-common/issues/1318): this shall be fixed
// in some way. ATM the future of the `register_relayers_rewards` is not yet known
100_000_u32.into(),
10_000_u32.into(),
);
}
}
@@ -72,13 +63,17 @@ where
// Update rewards to given relayers, optionally rewarding confirmation relayer.
fn register_relayers_rewards<T: Config>(
confirmation_relayer: &T::AccountId,
relayers_rewards: RelayersRewards<T::AccountId, T::Reward>,
relayers_rewards: RelayersRewards<T::AccountId>,
delivery_fee: T::Reward,
confirmation_fee: T::Reward,
) {
// reward every relayer except `confirmation_relayer`
let mut confirmation_relayer_reward = T::Reward::zero();
for (relayer, reward) in relayers_rewards {
let mut relayer_reward = reward.reward;
for (relayer, messages) in relayers_rewards {
// sane runtime configurations guarantee that the number of messages will be below
// `u32::MAX`
let mut relayer_reward =
T::Reward::unique_saturated_from(messages).saturating_mul(delivery_fee);
if relayer != *confirmation_relayer {
// If delivery confirmation is submitted by other relayer, let's deduct confirmation fee
@@ -87,12 +82,11 @@ fn register_relayers_rewards<T: Config>(
// If confirmation fee has been increased (or if it was the only component of message
// fee), then messages relayer may receive zero reward.
let mut confirmation_reward =
T::Reward::saturated_from(reward.messages).saturating_mul(confirmation_fee);
T::Reward::saturated_from(messages).saturating_mul(confirmation_fee);
confirmation_reward = sp_std::cmp::min(confirmation_reward, relayer_reward);
relayer_reward = relayer_reward.saturating_sub(confirmation_reward);
confirmation_relayer_reward =
confirmation_relayer_reward.saturating_add(confirmation_reward);
register_relayer_reward::<T>(&relayer, relayer_reward);
} else {
// If delivery confirmation is submitted by this relayer, let's add confirmation fee
@@ -134,32 +128,27 @@ mod tests {
const RELAYER_2: AccountId = 2;
const RELAYER_3: AccountId = 3;
fn relayers_rewards() -> RelayersRewards<AccountId, Balance> {
vec![
(RELAYER_1, bp_messages::source_chain::RelayerRewards { reward: 100, messages: 2 }),
(RELAYER_2, bp_messages::source_chain::RelayerRewards { reward: 100, messages: 3 }),
]
.into_iter()
.collect()
fn relayers_rewards() -> RelayersRewards<AccountId> {
vec![(RELAYER_1, 2), (RELAYER_2, 3)].into_iter().collect()
}
#[test]
fn confirmation_relayer_is_rewarded_if_it_has_also_delivered_messages() {
run_test(|| {
register_relayers_rewards::<TestRuntime>(&RELAYER_2, relayers_rewards(), 10);
register_relayers_rewards::<TestRuntime>(&RELAYER_2, relayers_rewards(), 50, 10);
assert_eq!(RelayerRewards::<TestRuntime>::get(RELAYER_1), Some(80));
assert_eq!(RelayerRewards::<TestRuntime>::get(RELAYER_2), Some(120));
assert_eq!(RelayerRewards::<TestRuntime>::get(RELAYER_2), Some(170));
});
}
#[test]
fn confirmation_relayer_is_rewarded_if_it_has_not_delivered_any_delivered_messages() {
run_test(|| {
register_relayers_rewards::<TestRuntime>(&RELAYER_3, relayers_rewards(), 10);
register_relayers_rewards::<TestRuntime>(&RELAYER_3, relayers_rewards(), 50, 10);
assert_eq!(RelayerRewards::<TestRuntime>::get(RELAYER_1), Some(80));
assert_eq!(RelayerRewards::<TestRuntime>::get(RELAYER_2), Some(70));
assert_eq!(RelayerRewards::<TestRuntime>::get(RELAYER_2), Some(120));
assert_eq!(RelayerRewards::<TestRuntime>::get(RELAYER_3), Some(50));
});
}
@@ -167,11 +156,11 @@ mod tests {
#[test]
fn only_confirmation_relayer_is_rewarded_if_confirmation_fee_has_significantly_increased() {
run_test(|| {
register_relayers_rewards::<TestRuntime>(&RELAYER_3, relayers_rewards(), 1000);
register_relayers_rewards::<TestRuntime>(&RELAYER_3, relayers_rewards(), 50, 1000);
assert_eq!(RelayerRewards::<TestRuntime>::get(RELAYER_1), None);
assert_eq!(RelayerRewards::<TestRuntime>::get(RELAYER_2), None);
assert_eq!(RelayerRewards::<TestRuntime>::get(RELAYER_3), Some(200));
assert_eq!(RelayerRewards::<TestRuntime>::get(RELAYER_3), Some(250));
});
}
}
+3 -3
View File
@@ -17,7 +17,7 @@
//! Autogenerated weights for `pallet_bridge_relayers`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2022-10-04, STEPS: 50, REPEAT: 20
//! DATE: 2022-11-17, STEPS: 50, REPEAT: 20
//! LOW RANGE: [], HIGH RANGE: []
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled
//! CHAIN: Some("dev"), DB CACHE: 1024
@@ -59,7 +59,7 @@ pub trait WeightInfo {
pub struct BridgeWeight<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for BridgeWeight<T> {
fn claim_rewards() -> Weight {
Weight::from_ref_time(55_856_000 as u64)
Weight::from_ref_time(59_334_000 as u64)
.saturating_add(T::DbWeight::get().reads(2 as u64))
.saturating_add(T::DbWeight::get().writes(2 as u64))
}
@@ -68,7 +68,7 @@ impl<T: frame_system::Config> WeightInfo for BridgeWeight<T> {
// For backwards compatibility and tests
impl WeightInfo for () {
fn claim_rewards() -> Weight {
Weight::from_ref_time(55_856_000 as u64)
Weight::from_ref_time(59_334_000 as u64)
.saturating_add(RocksDbWeight::get().reads(2 as u64))
.saturating_add(RocksDbWeight::get().writes(2 as u64))
}
@@ -27,12 +27,12 @@ use bp_runtime::{
use frame_support::{
dispatch::DispatchClass,
parameter_types,
sp_runtime::{FixedU128, MultiAddress, MultiSigner},
sp_runtime::{MultiAddress, MultiSigner},
weights::{
constants::ExtrinsicBaseWeight, WeightToFeeCoefficient, WeightToFeeCoefficients,
WeightToFeePolynomial,
},
Parameter, RuntimeDebug,
RuntimeDebug,
};
use sp_std::prelude::*;
@@ -27,16 +27,14 @@ pub use bp_bridge_hub_rococo::{
account_info_storage_key, AccountId, AccountPublic, AccountSigner, Address, Balance,
BlockLength, BlockNumber, BlockWeights, Hash, Hasher, Hashing, Header, Index, Nonce,
SS58Prefix, Signature, SignedBlock, SignedExtensions, UncheckedExtrinsic, WeightToFee,
ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT, DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT,
EXTRA_STORAGE_PROOF_SIZE, MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT,
MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX,
PAY_INBOUND_DISPATCH_FEE_WEIGHT, TX_EXTRA_BYTES,
TX_EXTRA_BYTES,
};
use bp_messages::*;
use bp_runtime::{
decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, Parachain,
};
use frame_support::{dispatch::DispatchClass, sp_runtime::FixedU128, Parameter, RuntimeDebug};
use frame_support::{dispatch::DispatchClass, RuntimeDebug};
use sp_std::prelude::*;
/// BridgeHubWococo parachain.
+2 -43
View File
@@ -27,14 +27,14 @@ use bp_runtime::{decl_bridge_runtime_apis, Chain};
use frame_support::{
dispatch::DispatchClass,
weights::{constants::WEIGHT_PER_SECOND, IdentityFee, Weight},
Parameter, RuntimeDebug,
RuntimeDebug,
};
use frame_system::limits;
use scale_info::TypeInfo;
use sp_core::{storage::StateVersion, Hasher as HasherT};
use sp_runtime::{
traits::{IdentifyAccount, Verify},
FixedU128, MultiSignature, MultiSigner, Perbill,
MultiSignature, MultiSigner, Perbill,
};
use sp_std::prelude::*;
use sp_trie::{LayoutV0, LayoutV1, TrieConfiguration};
@@ -69,38 +69,6 @@ pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 128;
/// Maximal number of unconfirmed messages in Millau confirmation transaction.
pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 128;
/// Weight of single regular message delivery transaction on Millau chain.
///
/// This value is a result of `pallet_bridge_messages::Pallet::receive_messages_proof_weight()` call
/// for the case when single message of `pallet_bridge_messages::EXPECTED_DEFAULT_MESSAGE_LENGTH`
/// bytes is delivered. The message must have dispatch weight set to zero. The result then must be
/// rounded up to account possible future runtime upgrades.
pub const DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT: Weight = Weight::from_ref_time(1_500_000_000);
/// Increase of delivery transaction weight on Millau chain with every additional message byte.
///
/// This value is a result of
/// `pallet_bridge_messages::WeightInfoExt::storage_proof_size_overhead(1)` call. The result then
/// must be rounded up to account possible future runtime upgrades.
pub const ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT: Weight = Weight::from_ref_time(25_000);
/// Maximal weight of single message delivery confirmation transaction on Millau chain.
///
/// This value is a result of `pallet_bridge_messages::Pallet::receive_messages_delivery_proof`
/// weight formula computation for the case when single message is confirmed. The result then must
/// be rounded up to account possible future runtime upgrades.
pub const MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT: Weight =
Weight::from_ref_time(2_000_000_000);
/// Weight of pay-dispatch-fee operation for inbound messages at Millau chain.
///
/// This value corresponds to the result of
/// `pallet_bridge_messages::WeightInfoExt::pay_inbound_dispatch_fee_overhead()` call for your
/// chain. Don't put too much reserve there, because it is used to **decrease**
/// `DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT` cost. So putting large reserve would make delivery
/// transactions cheaper.
pub const PAY_INBOUND_DISPATCH_FEE_WEIGHT: Weight = Weight::from_ref_time(700_000_000);
/// The target length of a session (how often authorities change) on Millau measured in of number of
/// blocks.
///
@@ -237,13 +205,4 @@ pub const WITH_MILLAU_MESSAGES_PALLET_NAME: &str = "BridgeMillauMessages";
/// Name of the transaction payment pallet at the Millau runtime.
pub const TRANSACTION_PAYMENT_PALLET_NAME: &str = "TransactionPayment";
/// Name of the Rialto->Millau (actually DOT->KSM) conversion rate stored in the Millau runtime.
pub const RIALTO_TO_MILLAU_CONVERSION_RATE_PARAMETER_NAME: &str = "RialtoToMillauConversionRate";
/// Name of the RialtoParachain->Millau (actually DOT->KSM) conversion rate stored in the Millau
/// runtime.
pub const RIALTO_PARACHAIN_TO_MILLAU_CONVERSION_RATE_PARAMETER_NAME: &str =
"RialtoParachainToMillauConversionRate";
/// Name of the RialtoParachain fee multiplier parameter, stored in the Millau runtime.
pub const RIALTO_PARACHAIN_FEE_MULTIPLIER_PARAMETER_NAME: &str = "RialtoParachainFeeMultiplier";
decl_bridge_runtime_apis!(millau);
@@ -25,13 +25,13 @@ use bp_runtime::{decl_bridge_runtime_apis, Chain, Parachain};
use frame_support::{
dispatch::DispatchClass,
weights::{constants::WEIGHT_PER_SECOND, IdentityFee, Weight},
Parameter, RuntimeDebug,
RuntimeDebug,
};
use frame_system::limits;
use sp_core::Hasher as HasherT;
use sp_runtime::{
traits::{BlakeTwo256, IdentifyAccount, Verify},
FixedU128, MultiSignature, MultiSigner, Perbill,
MultiSignature, MultiSigner, Perbill,
};
use sp_std::vec::Vec;
@@ -66,39 +66,6 @@ pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 1024;
/// Maximal number of unconfirmed messages in Rialto confirmation transaction.
pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1024;
/// Weight of single regular message delivery transaction on RialtoParachain chain.
///
/// This value is a result of `pallet_bridge_messages::Pallet::receive_messages_proof_weight()` call
/// for the case when single message of `pallet_bridge_messages::EXPECTED_DEFAULT_MESSAGE_LENGTH`
/// bytes is delivered. The message must have dispatch weight set to zero. The result then must be
/// rounded up to account possible future runtime upgrades.
pub const DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT: Weight = Weight::from_ref_time(1_500_000_000);
/// Increase of delivery transaction weight on RialtoParachain chain with every additional message
/// byte.
///
/// This value is a result of
/// `pallet_bridge_messages::WeightInfoExt::storage_proof_size_overhead(1)` call. The result then
/// must be rounded up to account possible future runtime upgrades.
pub const ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT: Weight = Weight::from_ref_time(25_000);
/// Maximal weight of single message delivery confirmation transaction on RialtoParachain chain.
///
/// This value is a result of `pallet_bridge_messages::Pallet::receive_messages_delivery_proof`
/// weight formula computation for the case when single message is confirmed. The result then must
/// be rounded up to account possible future runtime upgrades.
pub const MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT: Weight =
Weight::from_ref_time(2_000_000_000);
/// Weight of pay-dispatch-fee operation for inbound messages at Rialto chain.
///
/// This value corresponds to the result of
/// `pallet_bridge_messages::WeightInfoExt::pay_inbound_dispatch_fee_overhead()` call for your
/// chain. Don't put too much reserve there, because it is used to **decrease**
/// `DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT` cost. So putting large reserve would make delivery
/// transactions cheaper.
pub const PAY_INBOUND_DISPATCH_FEE_WEIGHT: Weight = Weight::from_ref_time(600_000_000);
/// Block number type used in Rialto.
pub type BlockNumber = u32;
@@ -176,11 +143,4 @@ pub const WITH_RIALTO_PARACHAIN_MESSAGES_PALLET_NAME: &str = "BridgeRialtoParach
/// Name of the transaction payment pallet at the Rialto parachain runtime.
pub const TRANSACTION_PAYMENT_PALLET_NAME: &str = "TransactionPayment";
/// Name of the Millau->RialtoParachain (actually KSM->DOT) conversion rate stored in the Rialto
/// parachain runtime.
pub const MILLAU_TO_RIALTO_PARACHAIN_CONVERSION_RATE_PARAMETER_NAME: &str =
"MillauToRialtoParachainConversionRate";
/// Name of the Millau fee multiplier parameter, stored in the Rialto parachain runtime.
pub const MILLAU_FEE_MULTIPLIER_PARAMETER_NAME: &str = "MillauFeeMultiplier";
decl_bridge_runtime_apis!(rialto_parachain);
+2 -37
View File
@@ -25,13 +25,13 @@ use bp_runtime::{decl_bridge_runtime_apis, Chain};
use frame_support::{
dispatch::DispatchClass,
weights::{constants::WEIGHT_PER_SECOND, IdentityFee, Weight},
Parameter, RuntimeDebug,
RuntimeDebug,
};
use frame_system::limits;
use sp_core::Hasher as HasherT;
use sp_runtime::{
traits::{BlakeTwo256, IdentifyAccount, Verify},
FixedU128, MultiSignature, MultiSigner, Perbill,
MultiSignature, MultiSigner, Perbill,
};
use sp_std::prelude::*;
@@ -60,38 +60,6 @@ pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 1024;
/// Maximal number of unconfirmed messages in Rialto confirmation transaction.
pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1024;
/// Weight of single regular message delivery transaction on Rialto chain.
///
/// This value is a result of `pallet_bridge_messages::Pallet::receive_messages_proof_weight()` call
/// for the case when single message of `pallet_bridge_messages::EXPECTED_DEFAULT_MESSAGE_LENGTH`
/// bytes is delivered. The message must have dispatch weight set to zero. The result then must be
/// rounded up to account possible future runtime upgrades.
pub const DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT: Weight = Weight::from_ref_time(1_500_000_000);
/// Increase of delivery transaction weight on Rialto chain with every additional message byte.
///
/// This value is a result of
/// `pallet_bridge_messages::WeightInfoExt::storage_proof_size_overhead(1)` call. The result then
/// must be rounded up to account possible future runtime upgrades.
pub const ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT: Weight = Weight::from_ref_time(25_000);
/// Maximal weight of single message delivery confirmation transaction on Rialto chain.
///
/// This value is a result of `pallet_bridge_messages::Pallet::receive_messages_delivery_proof`
/// weight formula computation for the case when single message is confirmed. The result then must
/// be rounded up to account possible future runtime upgrades.
pub const MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT: Weight =
Weight::from_ref_time(2_000_000_000);
/// Weight of pay-dispatch-fee operation for inbound messages at Rialto chain.
///
/// This value corresponds to the result of
/// `pallet_bridge_messages::WeightInfoExt::pay_inbound_dispatch_fee_overhead()` call for your
/// chain. Don't put too much reserve there, because it is used to **decrease**
/// `DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT` cost. So putting large reserve would make delivery
/// transactions cheaper.
pub const PAY_INBOUND_DISPATCH_FEE_WEIGHT: Weight = Weight::from_ref_time(700_000_000);
/// The target length of a session (how often authorities change) on Rialto measured in of number of
/// blocks.
///
@@ -203,9 +171,6 @@ pub const WITH_RIALTO_MESSAGES_PALLET_NAME: &str = "BridgeRialtoMessages";
/// Name of the With-Rialto parachains bridge pallet instance that is deployed at bridged chains.
pub const WITH_RIALTO_BRIDGE_PARAS_PALLET_NAME: &str = "BridgeRialtoParachains";
/// Name of the Millau->Rialto (actually KSM->DOT) conversion rate stored in the Rialto runtime.
pub const MILLAU_TO_RIALTO_CONVERSION_RATE_PARAMETER_NAME: &str = "MillauToRialtoConversionRate";
/// Name of the parachain registrar pallet in the Rialto runtime.
pub const PARAS_REGISTRAR_PALLET_NAME: &str = "Registrar";
@@ -1,24 +0,0 @@
[package]
name = "bp-statemine"
description = "Primitives of Statemine runtime."
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2021"
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
[dependencies]
# Bridge Dependencies
bp-polkadot-core = { path = "../polkadot-core", default-features = false }
# Substrate Based Dependencies
sp-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
[features]
default = ["std"]
std = [
"bp-polkadot-core/std",
"sp-version/std",
]
@@ -1,38 +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/>.
#![cfg_attr(not(feature = "std"), no_std)]
// RuntimeApi generated functions
#![allow(clippy::too_many_arguments)]
pub use bp_polkadot_core::*;
use sp_version::RuntimeVersion;
/// Statemine Chain.
pub type Statemine = PolkadotLike;
/// Known Statemine runtime version.
pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: sp_version::create_runtime_str!("statemine"),
impl_name: sp_version::create_runtime_str!("statemine"),
authoring_version: 1,
spec_version: 9300,
impl_version: 0,
apis: sp_version::create_apis_vec![[]],
transaction_version: 8,
state_version: 1,
};
+5 -28
View File
@@ -21,7 +21,7 @@
#![allow(clippy::too_many_arguments)]
use bitvec::prelude::*;
use bp_runtime::{messages::DispatchFeePayment, BasicOperatingMode, OperatingMode};
use bp_runtime::{BasicOperatingMode, OperatingMode};
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::RuntimeDebug;
use scale_info::TypeInfo;
@@ -65,16 +65,6 @@ impl OperatingMode for MessagesOperatingMode {
}
}
/// Messages pallet parameter.
pub trait Parameter: frame_support::Parameter {
/// Save parameter value in the runtime storage.
fn save(&self);
}
impl Parameter for () {
fn save(&self) {}
}
/// Lane identifier.
pub type LaneId = [u8; 4];
@@ -96,22 +86,13 @@ pub struct MessageKey {
pub nonce: MessageNonce,
}
/// Message data as it is stored in the storage.
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub struct MessageData<Fee> {
/// Message payload.
pub payload: MessagePayload,
/// Message delivery and dispatch fee, paid by the submitter.
pub fee: Fee,
}
/// Message as it is stored in the storage.
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub struct Message<Fee> {
pub struct Message {
/// Message key.
pub key: MessageKey,
/// Message data.
pub data: MessageData<Fee>,
/// Message payload.
pub payload: MessagePayload,
}
/// Inbound lane data.
@@ -198,7 +179,7 @@ impl<RelayerId> InboundLaneData<RelayerId> {
/// Outbound message details, returned by runtime APIs.
#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq)]
pub struct OutboundMessageDetails<OutboundMessageFee> {
pub struct OutboundMessageDetails {
/// Nonce assigned to the message.
pub nonce: MessageNonce,
/// Message dispatch weight.
@@ -208,10 +189,6 @@ pub struct OutboundMessageDetails<OutboundMessageFee> {
pub dispatch_weight: Weight,
/// Size of the encoded message.
pub size: u32,
/// Delivery+dispatch fee paid by the message submitter at the source chain.
pub delivery_and_dispatch_fee: OutboundMessageFee,
/// Where the fee for dispatching message is paid?
pub dispatch_fee_payment: DispatchFeePayment,
}
/// Inbound message details, returned by runtime APIs.
+10 -54
View File
@@ -27,17 +27,8 @@ use sp_std::{
ops::RangeInclusive,
};
/// Relayers rewards, grouped by relayer account id.
pub type RelayersRewards<AccountId, Balance> = BTreeMap<AccountId, RelayerRewards<Balance>>;
/// Single relayer rewards.
#[derive(RuntimeDebug, Default)]
pub struct RelayerRewards<Balance> {
/// Total rewards that are to be paid to the relayer.
pub reward: Balance,
/// Total number of messages relayed by this relayer.
pub messages: MessageNonce,
}
/// Number of messages, delivered by relayers.
pub type RelayersRewards<AccountId> = BTreeMap<AccountId, MessageNonce>;
/// Target chain API. Used by source chain to verify target chain proofs.
///
@@ -83,7 +74,7 @@ pub trait TargetHeaderChain<Payload, AccountId> {
/// Lane3 until some block, ...), then it may be built using this verifier.
///
/// Any fee requirements should also be enforced here.
pub trait LaneMessageVerifier<SenderOrigin, Payload, Fee> {
pub trait LaneMessageVerifier<SenderOrigin, Payload> {
/// Error type.
type Error: Debug + Into<&'static str>;
@@ -91,7 +82,6 @@ pub trait LaneMessageVerifier<SenderOrigin, Payload, Fee> {
/// lane.
fn verify_message(
submitter: &SenderOrigin,
delivery_and_dispatch_fee: &Fee,
lane: &LaneId,
outbound_data: &OutboundLaneData,
payload: &Payload,
@@ -107,21 +97,10 @@ pub trait LaneMessageVerifier<SenderOrigin, Payload, Fee> {
/// 3) message-dispatch fee. It is paid by relayer for processing message by target chain;
/// 4) message-receiving-delivery-transaction-fee. It is submitted to the source node
/// by relayer.
///
/// So to be sure that any non-altruist relayer would agree to deliver message, submitter
/// should set `delivery_and_dispatch_fee` to at least (equivalent of): sum of fees from (2)
/// to (4) above, plus some interest for the relayer.
pub trait MessageDeliveryAndDispatchPayment<SenderOrigin, AccountId, Balance> {
pub trait MessageDeliveryAndDispatchPayment<SenderOrigin, AccountId> {
/// Error type.
type Error: Debug + Into<&'static str>;
/// Withhold/write-off delivery_and_dispatch_fee from submitter account to
/// some relayers-fund account.
fn pay_delivery_and_dispatch_fee(
submitter: &SenderOrigin,
fee: &Balance,
) -> Result<(), Self::Error>;
/// Pay rewards for delivering messages to the given relayers.
///
/// The implementation may also choose to pay reward to the `confirmation_relayer`, which is
@@ -134,18 +113,9 @@ pub trait MessageDeliveryAndDispatchPayment<SenderOrigin, AccountId, Balance> {
);
}
impl<SenderOrigin, AccountId, Balance>
MessageDeliveryAndDispatchPayment<SenderOrigin, AccountId, Balance> for ()
{
impl<SenderOrigin, AccountId> MessageDeliveryAndDispatchPayment<SenderOrigin, AccountId> for () {
type Error = &'static str;
fn pay_delivery_and_dispatch_fee(
_submitter: &SenderOrigin,
_fee: &Balance,
) -> Result<(), Self::Error> {
Ok(())
}
fn pay_relayers_rewards(
_lane_id: LaneId,
_messages_relayers: VecDeque<UnrewardedRelayer<AccountId>>,
@@ -165,7 +135,7 @@ pub struct SendMessageArtifacts {
}
/// Messages bridge API to be used from other pallets.
pub trait MessagesBridge<SenderOrigin, Balance, Payload> {
pub trait MessagesBridge<SenderOrigin, Payload> {
/// Error type.
type Error: Debug;
@@ -176,7 +146,6 @@ pub trait MessagesBridge<SenderOrigin, Balance, Payload> {
sender: SenderOrigin,
lane: LaneId,
message: Payload,
delivery_and_dispatch_fee: Balance,
) -> Result<SendMessageArtifacts, Self::Error>;
}
@@ -184,16 +153,13 @@ pub trait MessagesBridge<SenderOrigin, Balance, Payload> {
#[derive(Eq, RuntimeDebug, PartialEq)]
pub struct NoopMessagesBridge;
impl<SenderOrigin, Balance, Payload> MessagesBridge<SenderOrigin, Balance, Payload>
for NoopMessagesBridge
{
impl<SenderOrigin, Payload> MessagesBridge<SenderOrigin, Payload> for NoopMessagesBridge {
type Error = &'static str;
fn send_message(
_sender: SenderOrigin,
_lane: LaneId,
_message: Payload,
_delivery_and_dispatch_fee: Balance,
) -> Result<SendMessageArtifacts, Self::Error> {
Ok(SendMessageArtifacts { nonce: 0, weight: Weight::zero() })
}
@@ -266,14 +232,11 @@ impl<Payload, AccountId> TargetHeaderChain<Payload, AccountId> for ForbidOutboun
}
}
impl<SenderOrigin, Payload, Fee> LaneMessageVerifier<SenderOrigin, Payload, Fee>
for ForbidOutboundMessages
{
impl<SenderOrigin, Payload> LaneMessageVerifier<SenderOrigin, Payload> for ForbidOutboundMessages {
type Error = &'static str;
fn verify_message(
_submitter: &SenderOrigin,
_delivery_and_dispatch_fee: &Fee,
_lane: &LaneId,
_outbound_data: &OutboundLaneData,
_payload: &Payload,
@@ -282,18 +245,11 @@ impl<SenderOrigin, Payload, Fee> LaneMessageVerifier<SenderOrigin, Payload, Fee>
}
}
impl<SenderOrigin, AccountId, Balance>
MessageDeliveryAndDispatchPayment<SenderOrigin, AccountId, Balance> for ForbidOutboundMessages
impl<SenderOrigin, AccountId> MessageDeliveryAndDispatchPayment<SenderOrigin, AccountId>
for ForbidOutboundMessages
{
type Error = &'static str;
fn pay_delivery_and_dispatch_fee(
_submitter: &SenderOrigin,
_fee: &Balance,
) -> Result<(), Self::Error> {
Err(ALL_OUTBOUND_MESSAGES_REJECTED)
}
fn pay_relayers_rewards(
_lane_id: LaneId,
_messages_relayers: VecDeque<UnrewardedRelayer<AccountId>>,
+20 -30
View File
@@ -16,7 +16,7 @@
//! Primitives of messages module, that are used on the target chain.
use crate::{LaneId, Message, MessageData, MessageKey, OutboundLaneData};
use crate::{LaneId, Message, MessageKey, MessagePayload, OutboundLaneData};
use bp_runtime::{messages::MessageDispatchResult, Size};
use codec::{Decode, Encode, Error as CodecError};
@@ -38,20 +38,18 @@ pub struct ProvedLaneMessages<Message> {
/// Message data with decoded dispatch payload.
#[derive(RuntimeDebug)]
pub struct DispatchMessageData<DispatchPayload, Fee> {
pub struct DispatchMessageData<DispatchPayload> {
/// Result of dispatch payload decoding.
pub payload: Result<DispatchPayload, CodecError>,
/// Message delivery and dispatch fee, paid by the submitter.
pub fee: Fee,
}
/// Message with decoded dispatch payload.
#[derive(RuntimeDebug)]
pub struct DispatchMessage<DispatchPayload, Fee> {
pub struct DispatchMessage<DispatchPayload> {
/// Message key.
pub key: MessageKey,
/// Message data with decoded dispatch payload.
pub data: DispatchMessageData<DispatchPayload, Fee>,
pub data: DispatchMessageData<DispatchPayload>,
}
/// Source chain API. Used by target chain, to verify source chain proofs.
@@ -59,7 +57,7 @@ pub struct DispatchMessage<DispatchPayload, Fee> {
/// All implementations of this trait should only work with finalized data that
/// can't change. Wrong implementation may lead to invalid lane states (i.e. lane
/// that's stuck) and/or processing messages without paying fees.
pub trait SourceHeaderChain<Fee> {
pub trait SourceHeaderChain {
/// Error type.
type Error: Debug + Into<&'static str>;
@@ -81,11 +79,11 @@ pub trait SourceHeaderChain<Fee> {
fn verify_messages_proof(
proof: Self::MessagesProof,
messages_count: u32,
) -> Result<ProvedMessages<Message<Fee>>, Self::Error>;
) -> Result<ProvedMessages<Message>, Self::Error>;
}
/// Called when inbound message is received.
pub trait MessageDispatch<AccountId, Fee> {
pub trait MessageDispatch<AccountId> {
/// Decoded message payload type. Valid message may contain invalid payload. In this case
/// message is delivered, but dispatch fails. Therefore, two separate types of payload
/// (opaque `MessagePayload` used in delivery and this `DispatchPayload` used in dispatch).
@@ -96,7 +94,7 @@ pub trait MessageDispatch<AccountId, Fee> {
/// This function must return correct upper bound of dispatch weight. The return value
/// of this function is expected to match return value of the corresponding
/// `From<Chain>InboundLaneApi::message_details().dispatch_weight` call.
fn dispatch_weight(message: &mut DispatchMessage<Self::DispatchPayload, Fee>) -> Weight;
fn dispatch_weight(message: &mut DispatchMessage<Self::DispatchPayload>) -> Weight;
/// Called when inbound message is received.
///
@@ -107,7 +105,7 @@ pub trait MessageDispatch<AccountId, Fee> {
/// it must be paid inside this method to the `relayer_account`.
fn dispatch(
relayer_account: &AccountId,
message: DispatchMessage<Self::DispatchPayload, Fee>,
message: DispatchMessage<Self::DispatchPayload>,
) -> MessageDispatchResult;
}
@@ -117,20 +115,15 @@ impl<Message> Default for ProvedLaneMessages<Message> {
}
}
impl<DispatchPayload: Decode, Fee> From<Message<Fee>> for DispatchMessage<DispatchPayload, Fee> {
fn from(message: Message<Fee>) -> Self {
DispatchMessage { key: message.key, data: message.data.into() }
impl<DispatchPayload: Decode> From<Message> for DispatchMessage<DispatchPayload> {
fn from(message: Message) -> Self {
DispatchMessage { key: message.key, data: message.payload.into() }
}
}
impl<DispatchPayload: Decode, Fee> From<MessageData<Fee>>
for DispatchMessageData<DispatchPayload, Fee>
{
fn from(data: MessageData<Fee>) -> Self {
DispatchMessageData {
payload: DispatchPayload::decode(&mut &data.payload[..]),
fee: data.fee,
}
impl<DispatchPayload: Decode> From<MessagePayload> for DispatchMessageData<DispatchPayload> {
fn from(payload: MessagePayload) -> Self {
DispatchMessageData { payload: DispatchPayload::decode(&mut &payload[..]) }
}
}
@@ -142,29 +135,26 @@ pub struct ForbidInboundMessages;
const ALL_INBOUND_MESSAGES_REJECTED: &str =
"This chain is configured to reject all inbound messages";
impl<Fee> SourceHeaderChain<Fee> for ForbidInboundMessages {
impl SourceHeaderChain for ForbidInboundMessages {
type Error = &'static str;
type MessagesProof = ();
fn verify_messages_proof(
_proof: Self::MessagesProof,
_messages_count: u32,
) -> Result<ProvedMessages<Message<Fee>>, Self::Error> {
) -> Result<ProvedMessages<Message>, Self::Error> {
Err(ALL_INBOUND_MESSAGES_REJECTED)
}
}
impl<AccountId, Fee> MessageDispatch<AccountId, Fee> for ForbidInboundMessages {
impl<AccountId> MessageDispatch<AccountId> for ForbidInboundMessages {
type DispatchPayload = ();
fn dispatch_weight(_message: &mut DispatchMessage<Self::DispatchPayload, Fee>) -> Weight {
fn dispatch_weight(_message: &mut DispatchMessage<Self::DispatchPayload>) -> Weight {
Weight::MAX
}
fn dispatch(
_: &AccountId,
_: DispatchMessage<Self::DispatchPayload, Fee>,
) -> MessageDispatchResult {
fn dispatch(_: &AccountId, _: DispatchMessage<Self::DispatchPayload>) -> MessageDispatchResult {
MessageDispatchResult {
dispatch_result: false,
unspent_weight: Weight::zero(),
@@ -126,49 +126,12 @@ pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 128;
/// Maximal number of unconfirmed messages at inbound lane.
pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 8192;
// One important thing about weight-related constants here is that actually we may have
// different weights on different Polkadot-like chains. But now all deployments are
// almost the same, so we're exporting constants from this crate.
/// Maximal weight of single message delivery confirmation transaction on Polkadot-like chain.
///
/// This value is a result of `pallet_bridge_messages::Pallet::receive_messages_delivery_proof`
/// weight formula computation for the case when single message is confirmed. The result then must
/// be rounded up to account possible future runtime upgrades.
pub const MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT: Weight =
Weight::from_ref_time(2_000_000_000);
/// Increase of delivery transaction weight on Polkadot-like chain with every additional message
/// byte.
///
/// This value is a result of
/// `pallet_bridge_messages::WeightInfoExt::storage_proof_size_overhead(1)` call. The result then
/// must be rounded up to account possible future runtime upgrades.
pub const ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT: Weight = Weight::from_ref_time(25_000);
/// Maximal number of bytes, included in the signed Polkadot-like transaction apart from the encoded
/// call itself.
///
/// Can be computed by subtracting encoded call size from raw transaction size.
pub const TX_EXTRA_BYTES: u32 = 256;
/// Weight of single regular message delivery transaction on Polkadot-like chain.
///
/// This value is a result of `pallet_bridge_messages::Pallet::receive_messages_proof_weight()` call
/// for the case when single message of `pallet_bridge_messages::EXPECTED_DEFAULT_MESSAGE_LENGTH`
/// bytes is delivered. The message must have dispatch weight set to zero. The result then must be
/// rounded up to account possible future runtime upgrades.
pub const DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT: Weight = Weight::from_ref_time(1_500_000_000);
/// Weight of pay-dispatch-fee operation for inbound messages at Polkadot-like chain.
///
/// This value corresponds to the result of
/// `pallet_bridge_messages::WeightInfoExt::pay_inbound_dispatch_fee_overhead()` call for your
/// chain. Don't put too much reserve there, because it is used to **decrease**
/// `DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT` cost. So putting large reserve would make delivery
/// transactions cheaper.
pub const PAY_INBOUND_DISPATCH_FEE_WEIGHT: Weight = Weight::from_ref_time(600_000_000);
/// Re-export `time_units` to make usage easier.
pub use time_units::*;
+5 -19
View File
@@ -300,21 +300,7 @@ macro_rules! decl_bridge_messages_runtime_apis {
///
/// This API is implemented by runtimes that are receiving messages from this chain, not by this
/// chain's runtime itself.
pub trait [<To $chain:camel OutboundLaneApi>]<OutboundMessageFee: Parameter, OutboundPayload: Parameter> {
/// Estimate message delivery and dispatch fee that needs to be paid by the sender on
/// this chain.
///
/// Returns `None` if message is too expensive to be sent to this chain from the bridged chain.
///
/// Please keep in mind that this method returns the lowest message fee required for message
/// to be accepted to the lane. It may be a good idea to pay a bit over this price to account
/// for future exchange rate changes and guarantee that relayer would deliver your message
/// to the target chain.
fn estimate_message_delivery_and_dispatch_fee(
lane_id: LaneId,
payload: OutboundPayload,
[<$chain:lower _to_this_conversion_rate>]: Option<FixedU128>,
) -> Option<OutboundMessageFee>;
pub trait [<To $chain:camel OutboundLaneApi>] {
/// Returns dispatch weight, encoded payload size and delivery+dispatch fee of all
/// messages in given inclusive range.
///
@@ -324,7 +310,7 @@ macro_rules! decl_bridge_messages_runtime_apis {
lane: LaneId,
begin: MessageNonce,
end: MessageNonce,
) -> Vec<OutboundMessageDetails<OutboundMessageFee>>;
) -> Vec<OutboundMessageDetails>;
}
/// Inbound message lane API for messages sent by this chain.
@@ -334,11 +320,11 @@ macro_rules! decl_bridge_messages_runtime_apis {
///
/// Entries of the resulting vector are matching entries of the `messages` vector. Entries of the
/// `messages` vector may (and need to) be read using `To<ThisChain>OutboundLaneApi::message_details`.
pub trait [<From $chain:camel InboundLaneApi>]<InboundMessageFee: Parameter> {
pub trait [<From $chain:camel InboundLaneApi>] {
/// Return details of given inbound messages.
fn message_details(
lane: LaneId,
messages: Vec<(MessagePayload, OutboundMessageDetails<InboundMessageFee>)>,
messages: Vec<(MessagePayload, OutboundMessageDetails)>,
) -> Vec<InboundMessageDetails>;
}
}
@@ -346,7 +332,7 @@ macro_rules! decl_bridge_messages_runtime_apis {
pub use [<$chain _messages_api>]::*;
}
}
};
}
/// Convenience macro that declares bridge finality runtime apis, bridge messages runtime apis
@@ -20,20 +20,6 @@ use codec::{Decode, Encode};
use frame_support::{weights::Weight, RuntimeDebug};
use scale_info::TypeInfo;
/// Where message dispatch fee is paid?
#[derive(Encode, Decode, RuntimeDebug, Clone, Copy, PartialEq, Eq, TypeInfo)]
pub enum DispatchFeePayment {
/// The dispatch fee is paid at the source chain.
AtSourceChain,
/// The dispatch fee is paid at the target chain.
///
/// The fee will be paid right before the message is dispatched. So in case of any other
/// issues (like invalid call encoding, invalid signature, ...) the dispatch module won't
/// do any direct transfers. Instead, it'll return fee related to this message dispatch to the
/// relayer.
AtTargetChain,
}
/// Message dispatch result.
#[derive(Encode, Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)]
pub struct MessageDispatchResult {
-2
View File
@@ -30,7 +30,6 @@ bp-rialto = { path = "../../primitives/chain-rialto" }
bp-rialto-parachain = { path = "../../primitives/chain-rialto-parachain" }
bp-rococo = { path = "../../primitives/chain-rococo" }
bp-runtime = { path = "../../primitives/runtime" }
bp-statemine = { path = "../../primitives/chain-statemine" }
bp-westend = { path = "../../primitives/chain-westend" }
bp-wococo = { path = "../../primitives/chain-wococo" }
bridge-runtime-common = { path = "../../bin/runtime-common" }
@@ -45,7 +44,6 @@ relay-rialto-parachain-client = { path = "../client-rialto-parachain" }
relay-bridge-hub-rococo-client = { path = "../client-bridge-hub-rococo" }
relay-bridge-hub-wococo-client = { path = "../client-bridge-hub-wococo" }
relay-rococo-client = { path = "../client-rococo" }
relay-statemine-client = { path = "../client-statemine" }
relay-substrate-client = { path = "../client-substrate" }
relay-utils = { path = "../utils" }
relay-westend-client = { path = "../client-westend" }
@@ -18,7 +18,6 @@
use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge};
use bp_messages::Weight;
use messages_relay::relay_strategy::MixStrategy;
use relay_bridge_hub_rococo_client::BridgeHubRococo;
use relay_bridge_hub_wococo_client::BridgeHubWococo;
use substrate_relay_helper::messages_lane::SubstrateMessageLane;
@@ -55,15 +54,6 @@ substrate_relay_helper::generate_mocked_receive_message_delivery_proof_call_buil
pub struct BridgeHubRococoMessagesToBridgeHubWococoMessageLane;
impl SubstrateMessageLane for BridgeHubRococoMessagesToBridgeHubWococoMessageLane {
const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = None;
const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = None;
const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None;
const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None;
const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None;
const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None;
type SourceChain = BridgeHubRococo;
type TargetChain = BridgeHubWococo;
@@ -71,8 +61,4 @@ impl SubstrateMessageLane for BridgeHubRococoMessagesToBridgeHubWococoMessageLan
BridgeHubRococoMessagesToBridgeHubWococoMessageLaneReceiveMessagesProofCallBuilder;
type ReceiveMessagesDeliveryProofCallBuilder =
BridgeHubRococoMessagesToBridgeHubWococoMessageLaneReceiveMessagesDeliveryProofCallBuilder;
type TargetToSourceChainConversionRateUpdateBuilder = ();
type RelayStrategy = MixStrategy;
}
@@ -18,7 +18,6 @@
use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge};
use bp_messages::Weight;
use messages_relay::relay_strategy::MixStrategy;
use relay_bridge_hub_rococo_client::BridgeHubRococo;
use relay_bridge_hub_wococo_client::BridgeHubWococo;
use substrate_relay_helper::messages_lane::SubstrateMessageLane;
@@ -55,15 +54,6 @@ substrate_relay_helper::generate_mocked_receive_message_delivery_proof_call_buil
pub struct BridgeHubWococoMessagesToBridgeHubRococoMessageLane;
impl SubstrateMessageLane for BridgeHubWococoMessagesToBridgeHubRococoMessageLane {
const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = None;
const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = None;
const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None;
const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None;
const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None;
const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None;
type SourceChain = BridgeHubWococo;
type TargetChain = BridgeHubRococo;
@@ -71,8 +61,4 @@ impl SubstrateMessageLane for BridgeHubWococoMessagesToBridgeHubRococoMessageLan
BridgeHubWococoMessagesToBridgeHubRococoMessageLaneReceiveMessagesProofCallBuilder;
type ReceiveMessagesDeliveryProofCallBuilder =
BridgeHubWococoMessagesToBridgeHubRococoMessageLaneReceiveMessagesDeliveryProofCallBuilder;
type TargetToSourceChainConversionRateUpdateBuilder = ();
type RelayStrategy = MixStrategy;
}
@@ -16,16 +16,10 @@
//! Millau chain specification for CLI.
use crate::cli::{
bridge,
encode_message::{CliEncodeMessage, RawMessage},
CliChain,
};
use bp_messages::LaneId;
use crate::cli::{bridge, encode_message::CliEncodeMessage, CliChain};
use bp_rialto_parachain::RIALTO_PARACHAIN_ID;
use bp_runtime::EncodedOrDecodedCall;
use relay_millau_client::Millau;
use relay_substrate_client::BalanceOf;
use sp_version::RuntimeVersion;
use xcm::latest::prelude::*;
@@ -56,37 +50,6 @@ impl CliEncodeMessage for Millau {
})
.into())
}
fn encode_send_message_call(
lane: LaneId,
payload: RawMessage,
fee: BalanceOf<Self>,
bridge_instance_index: u8,
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
Ok(match bridge_instance_index {
bridge::MILLAU_TO_RIALTO_INDEX => millau_runtime::RuntimeCall::BridgeRialtoMessages(
millau_runtime::MessagesCall::send_message {
lane_id: lane,
payload,
delivery_and_dispatch_fee: fee,
},
)
.into(),
bridge::MILLAU_TO_RIALTO_PARACHAIN_INDEX =>
millau_runtime::RuntimeCall::BridgeRialtoParachainMessages(
millau_runtime::MessagesCall::send_message {
lane_id: lane,
payload,
delivery_and_dispatch_fee: fee,
},
)
.into(),
_ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}",
bridge_instance_index
),
})
}
}
impl CliChain for Millau {
@@ -16,7 +16,6 @@
//! Millau-to-Rialto messages sync entrypoint.
use messages_relay::relay_strategy::MixStrategy;
use relay_millau_client::Millau;
use relay_rialto_client::Rialto;
use substrate_relay_helper::messages_lane::{
@@ -27,25 +26,8 @@ use substrate_relay_helper::messages_lane::{
/// Description of Millau -> Rialto messages bridge.
#[derive(Clone, Debug)]
pub struct MillauMessagesToRialto;
substrate_relay_helper::generate_direct_update_conversion_rate_call_builder!(
Millau,
MillauMessagesToRialtoUpdateConversionRateCallBuilder,
millau_runtime::Runtime,
millau_runtime::WithRialtoMessagesInstance,
millau_runtime::rialto_messages::MillauToRialtoMessagesParameter::RialtoToMillauConversionRate
);
impl SubstrateMessageLane for MillauMessagesToRialto {
const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> =
Some(bp_rialto::MILLAU_TO_RIALTO_CONVERSION_RATE_PARAMETER_NAME);
const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> =
Some(bp_millau::RIALTO_TO_MILLAU_CONVERSION_RATE_PARAMETER_NAME);
const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None;
const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None;
const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None;
const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None;
type SourceChain = Millau;
type TargetChain = Rialto;
@@ -59,9 +41,4 @@ impl SubstrateMessageLane for MillauMessagesToRialto {
millau_runtime::Runtime,
millau_runtime::WithRialtoMessagesInstance,
>;
type TargetToSourceChainConversionRateUpdateBuilder =
MillauMessagesToRialtoUpdateConversionRateCallBuilder;
type RelayStrategy = MixStrategy;
}
@@ -16,7 +16,6 @@
//! Millau-to-RialtoParachain messages sync entrypoint.
use messages_relay::relay_strategy::MixStrategy;
use relay_millau_client::Millau;
use relay_rialto_parachain_client::RialtoParachain;
use substrate_relay_helper::messages_lane::{
@@ -27,30 +26,8 @@ use substrate_relay_helper::messages_lane::{
/// Description of Millau -> RialtoParachain messages bridge.
#[derive(Clone, Debug)]
pub struct MillauMessagesToRialtoParachain;
substrate_relay_helper::generate_direct_update_conversion_rate_call_builder!(
Millau,
MillauMessagesToRialtoParachainUpdateConversionRateCallBuilder,
millau_runtime::Runtime,
millau_runtime::WithRialtoParachainMessagesInstance,
millau_runtime::rialto_parachain_messages::MillauToRialtoParachainMessagesParameter::RialtoParachainToMillauConversionRate
);
impl SubstrateMessageLane for MillauMessagesToRialtoParachain {
const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> =
Some(bp_rialto_parachain::MILLAU_TO_RIALTO_PARACHAIN_CONVERSION_RATE_PARAMETER_NAME);
const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> =
Some(bp_millau::RIALTO_PARACHAIN_TO_MILLAU_CONVERSION_RATE_PARAMETER_NAME);
const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> =
Some(bp_rialto_parachain::MILLAU_FEE_MULTIPLIER_PARAMETER_NAME);
const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> =
Some(bp_millau::RIALTO_PARACHAIN_FEE_MULTIPLIER_PARAMETER_NAME);
const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> =
Some(bp_millau::TRANSACTION_PAYMENT_PALLET_NAME);
const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> =
Some(bp_rialto_parachain::TRANSACTION_PAYMENT_PALLET_NAME);
type SourceChain = Millau;
type TargetChain = RialtoParachain;
@@ -64,9 +41,4 @@ impl SubstrateMessageLane for MillauMessagesToRialtoParachain {
millau_runtime::Runtime,
millau_runtime::WithRialtoParachainMessagesInstance,
>;
type TargetToSourceChainConversionRateUpdateBuilder =
MillauMessagesToRialtoParachainUpdateConversionRateCallBuilder;
type RelayStrategy = MixStrategy;
}
@@ -37,7 +37,6 @@ mod millau;
mod rialto;
mod rialto_parachain;
mod rococo;
mod statemine;
mod westend;
mod wococo;
@@ -16,15 +16,9 @@
//! Rialto chain specification for CLI.
use crate::cli::{
bridge,
encode_message::{CliEncodeMessage, RawMessage},
CliChain,
};
use bp_messages::LaneId;
use crate::cli::{bridge, encode_message::CliEncodeMessage, CliChain};
use bp_runtime::EncodedOrDecodedCall;
use relay_rialto_client::Rialto;
use relay_substrate_client::BalanceOf;
use sp_version::RuntimeVersion;
use xcm::latest::prelude::*;
@@ -48,28 +42,6 @@ impl CliEncodeMessage for Rialto {
})
.into())
}
fn encode_send_message_call(
lane: LaneId,
payload: RawMessage,
fee: BalanceOf<Self>,
bridge_instance_index: u8,
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
Ok(match bridge_instance_index {
bridge::RIALTO_TO_MILLAU_INDEX => rialto_runtime::RuntimeCall::BridgeMillauMessages(
rialto_runtime::MessagesCall::send_message {
lane_id: lane,
payload,
delivery_and_dispatch_fee: fee,
},
)
.into(),
_ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}",
bridge_instance_index
),
})
}
}
impl CliChain for Rialto {
@@ -16,7 +16,6 @@
//! Rialto-to-Millau messages sync entrypoint.
use messages_relay::relay_strategy::MixStrategy;
use relay_millau_client::Millau;
use relay_rialto_client::Rialto;
use substrate_relay_helper::messages_lane::{
@@ -27,25 +26,8 @@ use substrate_relay_helper::messages_lane::{
/// Description of Rialto -> Millau messages bridge.
#[derive(Clone, Debug)]
pub struct RialtoMessagesToMillau;
substrate_relay_helper::generate_direct_update_conversion_rate_call_builder!(
Rialto,
RialtoMessagesToMillauUpdateConversionRateCallBuilder,
rialto_runtime::Runtime,
rialto_runtime::WithMillauMessagesInstance,
rialto_runtime::millau_messages::RialtoToMillauMessagesParameter::MillauToRialtoConversionRate
);
impl SubstrateMessageLane for RialtoMessagesToMillau {
const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> =
Some(bp_millau::RIALTO_TO_MILLAU_CONVERSION_RATE_PARAMETER_NAME);
const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> =
Some(bp_rialto::MILLAU_TO_RIALTO_CONVERSION_RATE_PARAMETER_NAME);
const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None;
const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None;
const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None;
const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None;
type SourceChain = Rialto;
type TargetChain = Millau;
@@ -59,9 +41,4 @@ impl SubstrateMessageLane for RialtoMessagesToMillau {
rialto_runtime::Runtime,
rialto_runtime::WithMillauMessagesInstance,
>;
type TargetToSourceChainConversionRateUpdateBuilder =
RialtoMessagesToMillauUpdateConversionRateCallBuilder;
type RelayStrategy = MixStrategy;
}
@@ -16,15 +16,9 @@
//! Rialto parachain specification for CLI.
use crate::cli::{
bridge,
encode_message::{CliEncodeMessage, RawMessage},
CliChain,
};
use bp_messages::LaneId;
use crate::cli::{bridge, encode_message::CliEncodeMessage, CliChain};
use bp_runtime::EncodedOrDecodedCall;
use relay_rialto_parachain_client::RialtoParachain;
use relay_substrate_client::BalanceOf;
use sp_version::RuntimeVersion;
use xcm::latest::prelude::*;
@@ -50,29 +44,6 @@ impl CliEncodeMessage for RialtoParachain {
)
.into())
}
fn encode_send_message_call(
lane: LaneId,
payload: RawMessage,
fee: BalanceOf<Self>,
bridge_instance_index: u8,
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> {
Ok(match bridge_instance_index {
bridge::RIALTO_PARACHAIN_TO_MILLAU_INDEX =>
rialto_parachain_runtime::RuntimeCall::BridgeMillauMessages(
rialto_parachain_runtime::MessagesCall::send_message {
lane_id: lane,
payload,
delivery_and_dispatch_fee: fee,
},
)
.into(),
_ => anyhow::bail!(
"Unsupported target bridge pallet with instance index: {}",
bridge_instance_index
),
})
}
}
impl CliChain for RialtoParachain {
@@ -16,7 +16,6 @@
//! RialtoParachain-to-Millau messages sync entrypoint.
use messages_relay::relay_strategy::MixStrategy;
use relay_millau_client::Millau;
use relay_rialto_parachain_client::RialtoParachain;
use substrate_relay_helper::messages_lane::{
@@ -27,30 +26,8 @@ use substrate_relay_helper::messages_lane::{
/// Description of RialtoParachain -> Millau messages bridge.
#[derive(Clone, Debug)]
pub struct RialtoParachainMessagesToMillau;
substrate_relay_helper::generate_direct_update_conversion_rate_call_builder!(
RialtoParachain,
RialtoParachainMessagesToMillauUpdateConversionRateCallBuilder,
rialto_parachain_runtime::Runtime,
rialto_parachain_runtime::WithMillauMessagesInstance,
rialto_parachain_runtime::millau_messages::RialtoParachainToMillauMessagesParameter::MillauToRialtoParachainConversionRate
);
impl SubstrateMessageLane for RialtoParachainMessagesToMillau {
const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> =
Some(bp_millau::RIALTO_PARACHAIN_TO_MILLAU_CONVERSION_RATE_PARAMETER_NAME);
const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> =
Some(bp_rialto_parachain::MILLAU_TO_RIALTO_PARACHAIN_CONVERSION_RATE_PARAMETER_NAME);
const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> =
Some(bp_millau::RIALTO_PARACHAIN_FEE_MULTIPLIER_PARAMETER_NAME);
const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> =
Some(bp_rialto_parachain::MILLAU_FEE_MULTIPLIER_PARAMETER_NAME);
const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> =
Some(bp_rialto_parachain::TRANSACTION_PAYMENT_PALLET_NAME);
const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> =
Some(bp_millau::TRANSACTION_PAYMENT_PALLET_NAME);
type SourceChain = RialtoParachain;
type TargetChain = Millau;
@@ -64,9 +41,4 @@ impl SubstrateMessageLane for RialtoParachainMessagesToMillau {
rialto_parachain_runtime::Runtime,
rialto_parachain_runtime::WithMillauMessagesInstance,
>;
type TargetToSourceChainConversionRateUpdateBuilder =
RialtoParachainMessagesToMillauUpdateConversionRateCallBuilder;
type RelayStrategy = MixStrategy;
}
@@ -1,34 +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/>.
//! Statemine chain specification for CLI.
use crate::cli::CliChain;
use relay_statemine_client::Statemine;
use sp_version::RuntimeVersion;
impl CliChain for Statemine {
const RUNTIME_VERSION: Option<RuntimeVersion> = Some(bp_statemine::VERSION);
type KeyPair = sp_core::sr25519::Pair;
fn ss58_format() -> u16 {
sp_core::crypto::Ss58AddressFormat::from(
sp_core::crypto::Ss58AddressFormatRegistry::KusamaAccount,
)
.into()
}
}
@@ -15,7 +15,6 @@
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
use crate::cli::CliChain;
use messages_relay::relay_strategy::MixStrategy;
use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber};
use parachains_relay::ParachainsPipeline;
use relay_substrate_client::{AccountKeyPairOf, Chain, ChainWithTransactions, RelayChain};
@@ -46,7 +45,7 @@ impl FullBridge {
Self::MillauToRialtoParachain => MILLAU_TO_RIALTO_PARACHAIN_INDEX,
Self::RialtoParachainToMillau => RIALTO_PARACHAIN_TO_MILLAU_INDEX,
Self::BridgeHubRococoToBridgeHubWococo | Self::BridgeHubWococoToBridgeHubRococo =>
unimplemented!("TODO: (bridge_instance_index) do we need it or refactor or remove?"),
unimplemented!("Relay doesn't support send-message subcommand on bridge hubs"),
}
}
}
@@ -102,9 +101,5 @@ pub trait MessagesCliBridge: CliBridgeBase {
/// defined bridge.
const ESTIMATE_MESSAGE_FEE_METHOD: &'static str;
/// The Source -> Destination messages synchronization pipeline.
type MessagesLane: SubstrateMessageLane<
SourceChain = Self::Source,
TargetChain = Self::Target,
RelayStrategy = MixStrategy,
>;
type MessagesLane: SubstrateMessageLane<SourceChain = Self::Source, TargetChain = Self::Target>;
}
@@ -15,8 +15,8 @@
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
use crate::cli::{ExplicitOrMaximal, HexBytes};
use bp_messages::LaneId;
use bp_runtime::EncodedOrDecodedCall;
use codec::Encode;
use relay_substrate_client::Chain;
use structopt::StructOpt;
@@ -47,14 +47,6 @@ pub trait CliEncodeMessage: Chain {
message: xcm::VersionedXcm<()>,
bridge_instance_index: u8,
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>>;
/// Encode a send message call of the bridge-messages pallet.
fn encode_send_message_call(
lane: LaneId,
message: RawMessage,
fee: Self::Balance,
bridge_instance_index: u8,
) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>>;
}
/// Encode message payload passed through CLI flags.
@@ -63,15 +55,36 @@ pub(crate) fn encode_message<Source: Chain, Target: Chain>(
) -> anyhow::Result<RawMessage> {
Ok(match message {
Message::Raw { ref data } => data.0.clone(),
Message::Sized { ref size } => match *size {
ExplicitOrMaximal::Explicit(size) => vec![42; size as usize],
ExplicitOrMaximal::Maximal => {
let maximal_size = compute_maximal_message_size(
Message::Sized { ref size } => {
let expected_xcm_size = match *size {
ExplicitOrMaximal::Explicit(size) => size,
ExplicitOrMaximal::Maximal => compute_maximal_message_size(
Source::max_extrinsic_size(),
Target::max_extrinsic_size(),
),
};
// there's no way to craft XCM of the given size - we'll be using `ExpectPallet`
// instruction, which has byte vector inside
let mut current_vec_size = expected_xcm_size;
let xcm = loop {
let xcm = xcm::VersionedXcm::<()>::V3(
vec![xcm::v3::Instruction::ExpectPallet {
index: 0,
name: vec![42; current_vec_size as usize],
module_name: vec![],
crate_major: 0,
min_crate_minor: 0,
}]
.into(),
);
vec![42; maximal_size as usize]
},
if xcm.encode().len() <= expected_xcm_size as usize {
break xcm
}
current_vec_size -= 1;
};
xcm.encode()
},
})
}
@@ -95,3 +108,38 @@ pub(crate) fn compute_maximal_message_size(
maximal_message_size
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cli::send_message::decode_xcm;
use bp_runtime::Chain;
use relay_millau_client::Millau;
use relay_rialto_client::Rialto;
#[test]
fn encode_explicit_size_message_works() {
let msg = encode_message::<Rialto, Millau>(&Message::Sized {
size: ExplicitOrMaximal::Explicit(100),
})
.unwrap();
assert_eq!(msg.len(), 100);
// check that it decodes to valid xcm
let _ = decode_xcm(msg).unwrap();
}
#[test]
fn encode_maximal_size_message_works() {
let maximal_size = compute_maximal_message_size(
Rialto::max_extrinsic_size(),
Millau::max_extrinsic_size(),
);
let msg =
encode_message::<Rialto, Millau>(&Message::Sized { size: ExplicitOrMaximal::Maximal })
.unwrap();
assert_eq!(msg.len(), maximal_size as usize);
// check that it decodes to valid xcm
let _ = decode_xcm(msg).unwrap();
}
}
@@ -1,292 +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/>.
use crate::{
chains::{
millau_headers_to_rialto::MillauToRialtoCliBridge,
millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge,
rialto_headers_to_millau::RialtoToMillauCliBridge,
rialto_parachains_to_millau::RialtoParachainToMillauCliBridge,
},
cli::{
bridge::{FullBridge, MessagesCliBridge},
chain_schema::*,
relay_headers_and_messages::CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO,
Balance, HexBytes, HexLaneId,
},
};
use async_trait::async_trait;
use bp_runtime::BalanceOf;
use codec::{Decode, Encode};
use relay_substrate_client::{Chain, ChainBase};
use sp_runtime::FixedU128;
use std::fmt::Display;
use structopt::StructOpt;
use strum::VariantNames;
use substrate_relay_helper::helpers::tokens_conversion_rate_from_metrics;
/// Estimate Delivery & Dispatch Fee command.
#[derive(StructOpt, Debug, PartialEq)]
pub struct EstimateFee {
/// A bridge instance to encode call for.
#[structopt(possible_values = FullBridge::VARIANTS, case_insensitive = true)]
bridge: FullBridge,
#[structopt(flatten)]
source: SourceConnectionParams,
/// Hex-encoded id of lane that will be delivering the message.
#[structopt(long, default_value = "00000000")]
lane: HexLaneId,
/// A way to override conversion rate between bridge tokens.
///
/// If not specified, conversion rate from runtime storage is used. It may be obsolete and
/// your message won't be relayed.
#[structopt(long)]
conversion_rate_override: Option<ConversionRateOverride>,
/// Payload to send over the bridge.
#[structopt(flatten)]
payload: crate::cli::encode_message::Message,
}
/// A way to override conversion rate between bridge tokens.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ConversionRateOverride {
/// The actual conversion rate is computed in the same way how rate metric works.
Metric,
/// The actual conversion rate is specified explicitly.
Explicit(f64),
}
impl std::str::FromStr for ConversionRateOverride {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.to_lowercase() == "metric" {
return Ok(ConversionRateOverride::Metric)
}
f64::from_str(s)
.map(ConversionRateOverride::Explicit)
.map_err(|e| format!("Failed to parse '{e:?}'. Expected 'metric' or explicit value"))
}
}
#[async_trait]
trait FeeEstimator: MessagesCliBridge
where
<Self::Source as ChainBase>::Balance: Display + Into<u128>,
{
async fn estimate_fee(data: EstimateFee) -> anyhow::Result<()> {
let source_client = data.source.into_client::<Self::Source>().await?;
let lane = data.lane.into();
let payload =
crate::cli::encode_message::encode_message::<Self::Source, Self::Target>(&data.payload)
.map_err(|e| anyhow::format_err!("{:?}", e))?;
let fee = estimate_message_delivery_and_dispatch_fee::<Self::Source, Self::Target, _>(
&source_client,
data.conversion_rate_override,
Self::ESTIMATE_MESSAGE_FEE_METHOD,
lane,
&payload,
)
.await?;
log::info!(target: "bridge", "Fee: {:?}", Balance(fee.into()));
println!("{fee}");
Ok(())
}
}
impl FeeEstimator for MillauToRialtoCliBridge {}
impl FeeEstimator for RialtoToMillauCliBridge {}
impl FeeEstimator for MillauToRialtoParachainCliBridge {}
impl FeeEstimator for RialtoParachainToMillauCliBridge {}
impl EstimateFee {
/// Run the command.
pub async fn run(self) -> anyhow::Result<()> {
match self.bridge {
FullBridge::MillauToRialto => MillauToRialtoCliBridge::estimate_fee(self),
FullBridge::RialtoToMillau => RialtoToMillauCliBridge::estimate_fee(self),
FullBridge::MillauToRialtoParachain =>
MillauToRialtoParachainCliBridge::estimate_fee(self),
FullBridge::RialtoParachainToMillau =>
RialtoParachainToMillauCliBridge::estimate_fee(self),
FullBridge::BridgeHubRococoToBridgeHubWococo |
FullBridge::BridgeHubWococoToBridgeHubRococo =>
unimplemented!("TODO: (EstimateFee) do we need it or refactor or remove?"),
}
.await
}
}
/// The caller may provide target to source tokens conversion rate override to use in fee
/// computation.
pub(crate) async fn estimate_message_delivery_and_dispatch_fee<
Source: Chain,
Target: Chain,
P: Clone + Encode,
>(
client: &relay_substrate_client::Client<Source>,
conversion_rate_override: Option<ConversionRateOverride>,
estimate_fee_method: &str,
lane: bp_messages::LaneId,
payload: &P,
) -> anyhow::Result<BalanceOf<Source>> {
// actual conversion rate CAN be lesser than the rate stored in the runtime. So we may try to
// pay lesser fee for the message delivery. But in this case, message may be rejected by the
// lane. So we MUST use the larger of two fees - one computed with stored fee and the one
// computed with actual fee.
let conversion_rate_override =
match (conversion_rate_override, Source::TOKEN_ID, Target::TOKEN_ID) {
(Some(ConversionRateOverride::Explicit(v)), _, _) => {
let conversion_rate_override = FixedU128::from_float(v);
log::info!(
target: "bridge",
"{} -> {} conversion rate override: {:?} (explicit)",
Target::NAME,
Source::NAME,
conversion_rate_override.to_float(),
);
Some(conversion_rate_override)
},
(
Some(ConversionRateOverride::Metric),
Some(source_token_id),
Some(target_token_id),
) => {
let conversion_rate_override =
tokens_conversion_rate_from_metrics(target_token_id, source_token_id).await?;
// So we have current actual conversion rate and rate that is stored in the runtime.
// And we may simply choose the maximal of these. But what if right now there's
// rate update transaction on the way, that is updating rate to 10 seconds old
// actual rate, which is bigger than the current rate? Then our message will be
// rejected.
//
// So let's increase the actual rate by the same value that the conversion rate
// updater is using.
let increased_conversion_rate_override = FixedU128::from_float(
conversion_rate_override * (1.0 + CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO),
);
log::info!(
target: "bridge",
"{} -> {} conversion rate override: {} (value from metric - {})",
Target::NAME,
Source::NAME,
increased_conversion_rate_override.to_float(),
conversion_rate_override,
);
Some(increased_conversion_rate_override)
},
_ => None,
};
let without_override = do_estimate_message_delivery_and_dispatch_fee(
client,
estimate_fee_method,
lane,
payload,
None,
)
.await?;
let with_override = do_estimate_message_delivery_and_dispatch_fee(
client,
estimate_fee_method,
lane,
payload,
conversion_rate_override,
)
.await?;
let maximal_fee = std::cmp::max(without_override, with_override);
log::info!(
target: "bridge",
"Estimated message fee: {:?} = max of {:?} (without rate override) and {:?} (with override to {:?})",
maximal_fee,
without_override,
with_override,
conversion_rate_override,
);
Ok(maximal_fee)
}
/// Estimate message delivery and dispatch fee with given conversion rate override.
async fn do_estimate_message_delivery_and_dispatch_fee<Source: Chain, P: Encode>(
client: &relay_substrate_client::Client<Source>,
estimate_fee_method: &str,
lane: bp_messages::LaneId,
payload: &P,
conversion_rate_override: Option<FixedU128>,
) -> anyhow::Result<BalanceOf<Source>> {
let encoded_response = client
.state_call(
estimate_fee_method.into(),
(lane, payload, conversion_rate_override).encode().into(),
None,
)
.await?;
let decoded_response: Option<BalanceOf<Source>> = Decode::decode(&mut &encoded_response.0[..])
.map_err(relay_substrate_client::Error::ResponseParseFailed)?;
let fee = decoded_response.ok_or_else(|| {
anyhow::format_err!("Unable to decode fee from: {:?}", HexBytes(encoded_response.to_vec()))
})?;
Ok(fee)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_parse_cli_options() {
// when
let res = EstimateFee::from_iter(vec![
"estimate_fee",
"rialto-to-millau",
"--source-port",
"1234",
"--conversion-rate-override",
"42.5",
"raw",
"1234",
]);
// then
assert_eq!(
res,
EstimateFee {
bridge: FullBridge::RialtoToMillau,
lane: HexLaneId([0, 0, 0, 0]),
conversion_rate_override: Some(ConversionRateOverride::Explicit(42.5)),
source: SourceConnectionParams {
source_host: "127.0.0.1".into(),
source_port: 1234,
source_secure: false,
source_runtime_version: SourceRuntimeVersionParams {
source_version_mode: RuntimeVersionType::Bundle,
source_spec_version: None,
source_transaction_version: None,
}
},
payload: crate::cli::encode_message::Message::Raw {
data: HexBytes(vec![0x12, 0x34])
}
}
);
}
}
@@ -26,7 +26,6 @@ use bp_messages::LaneId;
pub(crate) mod bridge;
pub(crate) mod encode_message;
pub(crate) mod estimate_fee;
pub(crate) mod send_message;
mod chain_schema;
@@ -74,8 +73,6 @@ pub enum Command {
/// The message is being sent to the source chain, delivered to the target chain and dispatched
/// there.
SendMessage(send_message::SendMessage),
/// Estimate Delivery and Dispatch Fee required for message submission to messages pallet.
EstimateFee(estimate_fee::EstimateFee),
/// Resubmit transactions with increased tip if they are stalled.
ResubmitTransactions(resubmit_transactions::ResubmitTransactions),
/// Register parachain.
@@ -111,7 +108,6 @@ impl Command {
Self::RelayHeadersAndMessages(arg) => arg.run().await?,
Self::InitBridge(arg) => arg.run().await?,
Self::SendMessage(arg) => arg.run().await?,
Self::EstimateFee(arg) => arg.run().await?,
Self::ResubmitTransactions(arg) => arg.run().await?,
Self::RegisterParachain(arg) => arg.run().await?,
Self::RelayParachains(arg) => arg.run().await?,
@@ -31,7 +31,6 @@ mod relay_to_parachain;
use async_trait::async_trait;
use std::{marker::PhantomData, sync::Arc};
use structopt::StructOpt;
use strum::VariantNames;
use futures::{FutureExt, TryFutureExt};
use relay_to_parachain::*;
@@ -50,40 +49,27 @@ use crate::{
RelayToRelayHeadersCliBridge,
},
chain_schema::*,
relay_messages::RelayerMode,
CliChain, HexLaneId, PrometheusParams,
},
declare_chain_cli_schema,
};
use bp_messages::LaneId;
use bp_runtime::{BalanceOf, BlockNumberOf};
use messages_relay::relay_strategy::MixStrategy;
use relay_substrate_client::{
AccountIdOf, AccountKeyPairOf, Chain, ChainWithBalances, ChainWithTransactions, Client,
};
use relay_utils::metrics::MetricsParams;
use sp_core::Pair;
use substrate_relay_helper::{
messages_lane::MessagesRelayParams, messages_metrics::StandaloneMessagesMetrics,
on_demand::OnDemandRelay, TaggedAccount, TransactionParams,
messages_lane::MessagesRelayParams, on_demand::OnDemandRelay, TaggedAccount, TransactionParams,
};
/// Maximal allowed conversion rate error ratio (abs(real - stored) / stored) that we allow.
///
/// If it is zero, then transaction will be submitted every time we see difference between
/// stored and real conversion rates. If it is large enough (e.g. > than 10 percents, which is 0.1),
/// then rational relayers may stop relaying messages because they were submitted using
/// lesser conversion rate.
pub(crate) const CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO: f64 = 0.05;
/// Parameters that have the same names across all bridges.
#[derive(Debug, PartialEq, StructOpt)]
pub struct HeadersAndMessagesSharedParams {
/// Hex-encoded lane identifiers that should be served by the complex relay.
#[structopt(long, default_value = "00000000")]
pub lane: Vec<HexLaneId>,
#[structopt(long, possible_values = RelayerMode::VARIANTS, case_insensitive = true, default_value = "rational")]
pub relayer_mode: RelayerMode,
/// If passed, only mandatory headers (headers that are changing the GRANDPA authorities set)
/// are relayed.
#[structopt(long)]
@@ -101,8 +87,6 @@ pub struct Full2WayBridgeCommonParams<
pub right: BridgeEndCommonParams<Right>,
pub metrics_params: MetricsParams,
pub left_to_right_metrics: StandaloneMessagesMetrics<Left, Right>,
pub right_to_left_metrics: StandaloneMessagesMetrics<Right, Left>,
}
impl<Left: ChainWithTransactions + CliChain, Right: ChainWithTransactions + CliChain>
@@ -116,19 +100,8 @@ impl<Left: ChainWithTransactions + CliChain, Right: ChainWithTransactions + CliC
// Create metrics registry.
let metrics_params = shared.prometheus_params.clone().into();
let metrics_params = relay_utils::relay_metrics(metrics_params).into_params();
let left_to_right_metrics = substrate_relay_helper::messages_metrics::standalone_metrics::<
L2R::MessagesLane,
>(left.client.clone(), right.client.clone())?;
let right_to_left_metrics = left_to_right_metrics.clone().reverse();
Ok(Self {
shared,
left,
right,
metrics_params,
left_to_right_metrics,
right_to_left_metrics,
})
Ok(Self { shared, left, right, metrics_params })
}
}
@@ -146,11 +119,9 @@ struct FullBridge<
Target: ChainWithTransactions + CliChain,
Bridge: MessagesCliBridge<Source = Source, Target = Target>,
> {
shared: &'a HeadersAndMessagesSharedParams,
source: &'a mut BridgeEndCommonParams<Source>,
target: &'a mut BridgeEndCommonParams<Target>,
metrics_params: &'a MetricsParams,
metrics: &'a StandaloneMessagesMetrics<Source, Target>,
_phantom_data: PhantomData<Bridge>,
}
@@ -166,55 +137,11 @@ where
BalanceOf<Source>: TryFrom<BalanceOf<Target>> + Into<u128>,
{
fn new(
shared: &'a HeadersAndMessagesSharedParams,
source: &'a mut BridgeEndCommonParams<Source>,
target: &'a mut BridgeEndCommonParams<Target>,
metrics_params: &'a MetricsParams,
metrics: &'a StandaloneMessagesMetrics<Source, Target>,
) -> Self {
Self { shared, source, target, metrics_params, metrics, _phantom_data: Default::default() }
}
fn start_conversion_rate_update_loop(&mut self) -> anyhow::Result<()> {
if let Some(ref messages_pallet_owner) = self.source.messages_pallet_owner {
let format_err = || {
anyhow::format_err!(
"Cannon run conversion rate updater: {} -> {}",
Target::NAME,
Source::NAME
)
};
substrate_relay_helper::conversion_rate_update::run_conversion_rate_update_loop::<
Bridge::MessagesLane,
>(
self.source.client.clone(),
TransactionParams {
signer: messages_pallet_owner.clone(),
mortality: self.source.transactions_mortality,
},
self.metrics
.target_to_source_conversion_rate
.as_ref()
.ok_or_else(format_err)?
.shared_value_ref(),
self.metrics
.target_to_base_conversion_rate
.as_ref()
.ok_or_else(format_err)?
.shared_value_ref(),
self.metrics
.source_to_base_conversion_rate
.as_ref()
.ok_or_else(format_err)?
.shared_value_ref(),
CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO,
);
self.source.accounts.push(TaggedAccount::MessagesPalletOwner {
id: messages_pallet_owner.public().into(),
bridged_chain: Target::NAME.to_string(),
});
}
Ok(())
Self { source, target, metrics_params, _phantom_data: Default::default() }
}
fn messages_relay_params(
@@ -223,9 +150,6 @@ where
target_to_source_headers_relay: Arc<dyn OnDemandRelay<BlockNumberOf<Target>>>,
lane_id: LaneId,
) -> MessagesRelayParams<Bridge::MessagesLane> {
let relayer_mode = self.shared.relayer_mode.into();
let relay_strategy = MixStrategy::new(relayer_mode);
MessagesRelayParams {
source_client: self.source.client.clone(),
source_transaction_params: TransactionParams {
@@ -241,8 +165,6 @@ where
target_to_source_headers_relay: Some(target_to_source_headers_relay),
lane_id,
metrics_params: self.metrics_params.clone().disable(),
standalone_metrics: Some(self.metrics.clone()),
relay_strategy,
}
}
}
@@ -314,22 +236,18 @@ where
fn left_to_right(&mut self) -> FullBridge<Self::Left, Self::Right, Self::L2R> {
let common = self.mut_base().mut_common();
FullBridge::<_, _, Self::L2R>::new(
&common.shared,
&mut common.left,
&mut common.right,
&common.metrics_params,
&common.left_to_right_metrics,
)
}
fn right_to_left(&mut self) -> FullBridge<Self::Right, Self::Left, Self::R2L> {
let common = self.mut_base().mut_common();
FullBridge::<_, _, Self::R2L>::new(
&common.shared,
&mut common.right,
&mut common.left,
&common.metrics_params,
&common.right_to_left_metrics,
)
}
@@ -347,10 +265,6 @@ where
});
}
// start conversion rate update loops for left/right chains
self.left_to_right().start_conversion_rate_update_loop()?;
self.right_to_left().start_conversion_rate_update_loop()?;
// start on-demand header relays
let (left_to_right_on_demand_headers, right_to_left_on_demand_headers) =
self.mut_base().start_on_demand_headers_relayers().await?;
@@ -528,7 +442,6 @@ mod tests {
HexLaneId([0x00, 0x00, 0x00, 0x00]),
HexLaneId([0x73, 0x77, 0x61, 0x70])
],
relayer_mode: RelayerMode::Rational,
only_mandatory_headers: false,
prometheus_params: PrometheusParams {
no_prometheus: false,
@@ -641,7 +554,6 @@ mod tests {
MillauRialtoParachainHeadersAndMessages {
shared: HeadersAndMessagesSharedParams {
lane: vec![HexLaneId([0x00, 0x00, 0x00, 0x00])],
relayer_mode: RelayerMode::Rational,
only_mandatory_headers: false,
prometheus_params: PrometheusParams {
no_prometheus: false,
@@ -17,7 +17,7 @@
use async_trait::async_trait;
use sp_core::Pair;
use structopt::StructOpt;
use strum::{EnumString, EnumVariantNames, VariantNames};
use strum::VariantNames;
use crate::chains::{
bridge_hub_rococo_messages_to_bridge_hub_wococo::BridgeHubRococoToBridgeHubWococoMessagesCliBridge,
@@ -27,32 +27,11 @@ use crate::chains::{
rialto_headers_to_millau::RialtoToMillauCliBridge,
rialto_parachains_to_millau::RialtoParachainToMillauCliBridge,
};
use messages_relay::relay_strategy::MixStrategy;
use relay_substrate_client::{AccountIdOf, AccountKeyPairOf, BalanceOf, ChainWithTransactions};
use substrate_relay_helper::{messages_lane::MessagesRelayParams, TransactionParams};
use crate::cli::{bridge::*, chain_schema::*, CliChain, HexLaneId, PrometheusParams};
/// Relayer operating mode.
#[derive(Debug, EnumString, EnumVariantNames, Clone, Copy, PartialEq, Eq)]
#[strum(serialize_all = "kebab_case")]
pub enum RelayerMode {
/// The relayer doesn't care about rewards.
Altruistic,
/// The relayer will deliver all messages and confirmations as long as he's not losing any
/// funds.
Rational,
}
impl From<RelayerMode> for messages_relay::message_lane_loop::RelayerMode {
fn from(mode: RelayerMode) -> Self {
match mode {
RelayerMode::Altruistic => Self::Altruistic,
RelayerMode::Rational => Self::Rational,
}
}
}
/// Start messages relayer process.
#[derive(StructOpt)]
pub struct RelayMessages {
@@ -62,8 +41,6 @@ pub struct RelayMessages {
/// Hex-encoded lane id that should be served by the relay. Defaults to `00000000`.
#[structopt(long, default_value = "00000000")]
lane: HexLaneId,
#[structopt(long, possible_values = RelayerMode::VARIANTS, case_insensitive = true, default_value = "rational")]
relayer_mode: RelayerMode,
#[structopt(flatten)]
source: SourceConnectionParams,
#[structopt(flatten)]
@@ -91,8 +68,6 @@ where
let target_client = data.target.into_client::<Self::Target>().await?;
let target_sign = data.target_sign.to_keypair::<Self::Target>()?;
let target_transactions_mortality = data.target_sign.transactions_mortality()?;
let relayer_mode = data.relayer_mode.into();
let relay_strategy = MixStrategy::new(relayer_mode);
substrate_relay_helper::messages_lane::run::<Self::MessagesLane>(MessagesRelayParams {
source_client,
@@ -109,8 +84,6 @@ where
target_to_source_headers_relay: None,
lane_id: data.lane.into(),
metrics_params: data.prometheus_params.into(),
standalone_metrics: None,
relay_strategy,
})
.await
.map_err(|e| anyhow::format_err!("{}", e))
@@ -142,43 +115,3 @@ impl RelayMessages {
.await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_use_rational_relayer_mode_by_default() {
assert_eq!(
RelayMessages::from_iter(vec![
"relay-messages",
"rialto-to-millau",
"--source-port=0",
"--source-signer=//Alice",
"--target-port=0",
"--target-signer=//Alice",
"--lane=00000000",
])
.relayer_mode,
RelayerMode::Rational,
);
}
#[test]
fn should_accept_altruistic_relayer_mode() {
assert_eq!(
RelayMessages::from_iter(vec![
"relay-messages",
"rialto-to-millau",
"--source-port=0",
"--source-signer=//Alice",
"--target-port=0",
"--target-signer=//Alice",
"--lane=00000000",
"--relayer-mode=altruistic",
])
.relayer_mode,
RelayerMode::Altruistic,
);
}
}
@@ -25,8 +25,7 @@ use crate::{
bridge::{FullBridge, MessagesCliBridge},
chain_schema::*,
encode_message::{self, CliEncodeMessage, RawMessage},
estimate_fee::{estimate_message_delivery_and_dispatch_fee, ConversionRateOverride},
Balance, CliChain, HexLaneId,
CliChain,
},
};
use async_trait::async_trait;
@@ -35,30 +34,11 @@ use relay_substrate_client::{
AccountIdOf, AccountKeyPairOf, Chain, ChainBase, ChainWithTransactions, SignParam,
UnsignedTransaction,
};
use sp_core::{Bytes, Pair};
use sp_core::Pair;
use sp_runtime::AccountId32;
use std::fmt::{Debug, Display};
use std::fmt::Display;
use structopt::StructOpt;
use strum::{EnumString, EnumVariantNames, VariantNames};
/// Relayer operating mode.
#[derive(Debug, EnumString, EnumVariantNames, Clone, Copy, PartialEq, Eq)]
#[strum(serialize_all = "kebab_case")]
pub enum DispatchFeePayment {
/// The dispatch fee is paid at the source chain.
AtSourceChain,
/// The dispatch fee is paid at the target chain.
AtTargetChain,
}
impl From<DispatchFeePayment> for bp_runtime::messages::DispatchFeePayment {
fn from(dispatch_fee_payment: DispatchFeePayment) -> Self {
match dispatch_fee_payment {
DispatchFeePayment::AtSourceChain => Self::AtSourceChain,
DispatchFeePayment::AtTargetChain => Self::AtTargetChain,
}
}
}
use strum::VariantNames;
/// Send bridge message.
#[derive(StructOpt)]
@@ -70,23 +50,6 @@ pub struct SendMessage {
source: SourceConnectionParams,
#[structopt(flatten)]
source_sign: SourceSigningParams,
/// Send message using XCM pallet instead. By default message is sent using
/// bridge messages pallet.
#[structopt(long)]
use_xcm_pallet: bool,
/// Hex-encoded lane id. Defaults to `00000000`.
#[structopt(long, default_value = "00000000")]
lane: HexLaneId,
/// A way to override conversion rate between bridge tokens.
///
/// If not specified, conversion rate from runtime storage is used. It may be obsolete and
/// your message won't be relayed.
#[structopt(long)]
conversion_rate_override: Option<ConversionRateOverride>,
/// Delivery and dispatch fee in source chain base currency units. If not passed, determined
/// automatically.
#[structopt(long)]
fee: Option<Balance>,
/// Message type.
#[structopt(subcommand)]
message: crate::cli::encode_message::Message,
@@ -111,53 +74,14 @@ where
let source_client = data.source.into_client::<Self::Source>().await?;
let source_sign = data.source_sign.to_keypair::<Self::Source>()?;
let lane = data.lane.clone().into();
let conversion_rate_override = data.conversion_rate_override;
let fee = match data.fee {
Some(fee) => fee,
None => Balance(
estimate_message_delivery_and_dispatch_fee::<Self::Source, Self::Target, _>(
&source_client,
conversion_rate_override,
Self::ESTIMATE_MESSAGE_FEE_METHOD,
lane,
&payload,
)
.await?
.into(),
),
};
let payload_len = payload.encoded_size();
let send_message_call = if data.use_xcm_pallet {
Self::Source::encode_send_xcm(
decode_xcm(payload)?,
data.bridge.bridge_instance_index(),
)?
} else {
Self::Source::encode_send_message_call(
data.lane.0,
payload,
fee.cast().into(),
data.bridge.bridge_instance_index(),
)?
};
let send_message_call = Self::Source::encode_send_xcm(
decode_xcm(payload)?,
data.bridge.bridge_instance_index(),
)?;
let source_genesis_hash = *source_client.genesis_hash();
let (spec_version, transaction_version) = source_client.simple_runtime_version().await?;
let estimated_transaction_fee = source_client
.estimate_extrinsic_fee(Bytes(
Self::Source::sign_transaction(
SignParam {
spec_version,
transaction_version,
genesis_hash: source_genesis_hash,
signer: source_sign.clone(),
},
UnsignedTransaction::new(send_message_call.clone(), 0),
)?
.encode(),
))
.await?;
source_client
.submit_signed_extrinsic(
source_sign.public().into(),
@@ -171,22 +95,10 @@ where
let unsigned = UnsignedTransaction::new(send_message_call, transaction_nonce);
log::info!(
target: "bridge",
"Sending message to {}. Lane: {:?}. Size: {}. Fee: {}",
"Sending message to {}. Size: {}",
Self::Target::NAME,
lane,
payload_len,
fee,
);
log::info!(
target: "bridge",
"The source account ({:?}) balance will be reduced by (at most) {} (message fee)
+ {} (tx fee ) = {} {} tokens", AccountId32::from(source_sign.public()),
fee.0,
estimated_transaction_fee.inclusion_fee(),
fee.0.saturating_add(estimated_transaction_fee.inclusion_fee().into()),
Self::Source::NAME,
);
Ok(unsigned)
},
)
@@ -223,7 +135,7 @@ impl SendMessage {
}
/// Decode SCALE encoded raw XCM message.
fn decode_xcm(message: RawMessage) -> anyhow::Result<xcm::VersionedXcm<()>> {
pub(crate) fn decode_xcm(message: RawMessage) -> anyhow::Result<xcm::VersionedXcm<()>> {
Decode::decode(&mut &message[..])
.map_err(|e| anyhow::format_err!("Failed to decode XCM program: {:?}", e))
}
@@ -243,8 +155,6 @@ mod tests {
"1234",
"--source-signer",
"//Alice",
"--conversion-rate-override",
"0.75",
"raw",
"dead",
]);
@@ -253,10 +163,6 @@ mod tests {
assert_eq!(send_message.bridge, FullBridge::RialtoToMillau);
assert_eq!(send_message.source.source_port, 1234);
assert_eq!(send_message.source_sign.source_signer, Some("//Alice".into()));
assert_eq!(
send_message.conversion_rate_override,
Some(ConversionRateOverride::Explicit(0.75))
);
assert_eq!(
send_message.message,
crate::cli::encode_message::Message::Raw { data: HexBytes(vec![0xDE, 0xAD]) }
@@ -273,8 +179,6 @@ mod tests {
"1234",
"--source-signer",
"//Alice",
"--conversion-rate-override",
"metric",
"sized",
"max",
]);
@@ -283,7 +187,6 @@ mod tests {
assert_eq!(send_message.bridge, FullBridge::RialtoToMillau);
assert_eq!(send_message.source.source_port, 1234);
assert_eq!(send_message.source_sign.source_signer, Some("//Alice".into()));
assert_eq!(send_message.conversion_rate_override, Some(ConversionRateOverride::Metric));
assert_eq!(
send_message.message,
crate::cli::encode_message::Message::Sized { size: ExplicitOrMaximal::Maximal }
@@ -60,7 +60,6 @@ impl Chain for BridgeHubRococo {
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_bridge_hub_rococo::BEST_FINALIZED_BRIDGE_HUB_ROCOCO_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_bridge_hub_rococo::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = bp_bridge_hub_rococo::SignedBlock;
type Call = runtime::Call;
@@ -126,8 +125,6 @@ impl ChainWithMessages for BridgeHubRococo {
const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str =
bp_bridge_hub_rococo::FROM_BRIDGE_HUB_ROCOCO_MESSAGE_DETAILS_METHOD;
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight =
bp_bridge_hub_rococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce =
bp_bridge_hub_rococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX;
const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce =
@@ -60,7 +60,6 @@ impl Chain for BridgeHubWococo {
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_bridge_hub_wococo::BEST_FINALIZED_BRIDGE_HUB_WOCOCO_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_bridge_hub_wococo::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = bp_bridge_hub_wococo::SignedBlock;
type Call = runtime::Call;
@@ -126,8 +125,6 @@ impl ChainWithMessages for BridgeHubWococo {
const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str =
bp_bridge_hub_wococo::FROM_BRIDGE_HUB_WOCOCO_MESSAGE_DETAILS_METHOD;
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight =
bp_bridge_hub_wococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce =
bp_bridge_hub_wococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX;
const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce =
-1
View File
@@ -54,7 +54,6 @@ impl Chain for Kusama {
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_kusama::BEST_FINALIZED_KUSAMA_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_kusama::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = bp_kusama::SignedBlock;
type Call = ();
-3
View File
@@ -65,8 +65,6 @@ impl ChainWithMessages for Millau {
bp_millau::TO_MILLAU_MESSAGE_DETAILS_METHOD;
const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str =
bp_millau::FROM_MILLAU_MESSAGE_DETAILS_METHOD;
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight =
bp_millau::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce =
bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX;
const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce =
@@ -82,7 +80,6 @@ impl Chain for Millau {
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_millau::BEST_FINALIZED_MILLAU_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5);
const STORAGE_PROOF_OVERHEAD: u32 = bp_millau::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = millau_runtime::SignedBlock;
type Call = millau_runtime::RuntimeCall;
@@ -54,7 +54,6 @@ impl Chain for Polkadot {
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_polkadot::BEST_FINALIZED_POLKADOT_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_polkadot::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = bp_polkadot::SignedBlock;
type Call = ();
@@ -62,7 +62,6 @@ impl Chain for RialtoParachain {
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_rialto_parachain::BEST_FINALIZED_RIALTO_PARACHAIN_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5);
const STORAGE_PROOF_OVERHEAD: u32 = bp_rialto_parachain::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = rialto_parachain_runtime::SignedBlock;
type Call = rialto_parachain_runtime::RuntimeCall;
@@ -86,8 +85,6 @@ impl ChainWithMessages for RialtoParachain {
bp_rialto_parachain::TO_RIALTO_PARACHAIN_MESSAGE_DETAILS_METHOD;
const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str =
bp_rialto_parachain::FROM_RIALTO_PARACHAIN_MESSAGE_DETAILS_METHOD;
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight =
bp_rialto_parachain::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce =
bp_rialto_parachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX;
const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce =
@@ -157,7 +154,7 @@ impl ChainWithTransactions for RialtoParachain {
}
fn parse_transaction(_tx: Self::SignedTransaction) -> Option<UnsignedTransaction<Self>> {
unimplemented!("TODO")
None
}
}
-3
View File
@@ -62,7 +62,6 @@ impl Chain for Rialto {
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_rialto::BEST_FINALIZED_RIALTO_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5);
const STORAGE_PROOF_OVERHEAD: u32 = bp_rialto::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = rialto_runtime::SignedBlock;
type Call = rialto_runtime::RuntimeCall;
@@ -85,8 +84,6 @@ impl ChainWithMessages for Rialto {
bp_rialto::TO_RIALTO_MESSAGE_DETAILS_METHOD;
const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str =
bp_rialto::FROM_RIALTO_MESSAGE_DETAILS_METHOD;
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight =
bp_rialto::PAY_INBOUND_DISPATCH_FEE_WEIGHT;
const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce =
bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX;
const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce =
-1
View File
@@ -57,7 +57,6 @@ impl Chain for Rococo {
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_rococo::BEST_FINALIZED_ROCOCO_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_rococo::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = bp_rococo::SignedBlock;
type Call = ();
@@ -1,24 +0,0 @@
[package]
name = "relay-statemine-client"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2021"
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
[dependencies]
codec = { package = "parity-scale-codec", version = "3.1.5" }
relay-substrate-client = { path = "../client-substrate" }
relay-utils = { path = "../utils" }
scale-info = { version = "2.1.1", features = ["derive"] }
# Bridge dependencies
bp-polkadot-core = { path = "../../primitives/polkadot-core" }
bp-statemine = { path = "../../primitives/chain-statemine" }
# Substrate Dependencies
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
-112
View File
@@ -1,112 +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/>.
//! Types used to connect to the Statemine chain.
use codec::Encode;
use frame_support::weights::Weight;
use relay_substrate_client::{
Chain, ChainBase, ChainWithTransactions, Error as SubstrateError, SignParam,
UnsignedTransaction,
};
use sp_core::Pair;
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
use std::time::Duration;
pub mod runtime;
/// Statemine chain definition
#[derive(Debug, Clone, Copy)]
pub struct Statemine;
impl ChainBase for Statemine {
type BlockNumber = bp_statemine::BlockNumber;
type Hash = bp_statemine::Hash;
type Hasher = bp_statemine::Hasher;
type Header = bp_statemine::Header;
type AccountId = bp_statemine::AccountId;
type Balance = bp_statemine::Balance;
type Index = bp_statemine::Nonce;
type Signature = bp_statemine::Signature;
fn max_extrinsic_size() -> u32 {
bp_statemine::Statemine::max_extrinsic_size()
}
fn max_extrinsic_weight() -> Weight {
bp_statemine::Statemine::max_extrinsic_weight()
}
}
impl Chain for Statemine {
const NAME: &'static str = "Statemine";
const TOKEN_ID: Option<&'static str> = Some("kusama");
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = "<unused>";
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_statemine::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = bp_statemine::SignedBlock;
type Call = runtime::Call;
}
impl ChainWithTransactions for Statemine {
type AccountKeyPair = sp_core::sr25519::Pair;
type SignedTransaction = runtime::UncheckedExtrinsic;
fn sign_transaction(
param: SignParam<Self>,
unsigned: UnsignedTransaction<Self>,
) -> Result<Self::SignedTransaction, SubstrateError> {
let raw_payload = SignedPayload::new(
unsigned.call.clone(),
bp_statemine::SignedExtensions::new(
param.spec_version,
param.transaction_version,
unsigned.era,
param.genesis_hash,
unsigned.nonce,
unsigned.tip,
),
)
.expect("SignedExtension never fails.");
let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload));
let signer: sp_runtime::MultiSigner = param.signer.public().into();
let (call, extra, _) = raw_payload.deconstruct();
Ok(runtime::UncheckedExtrinsic::new_signed(
call,
signer.into_account().into(),
signature.into(),
extra,
))
}
fn is_signed(tx: &Self::SignedTransaction) -> bool {
tx.signature.is_some()
}
fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool {
tx.signature
.as_ref()
.map(|(address, _, _)| *address == bp_statemine::Address::Id(signer.public().into()))
.unwrap_or(false)
}
fn parse_transaction(_tx: Self::SignedTransaction) -> Option<UnsignedTransaction<Self>> {
unimplemented!("not used on Statemine")
}
}
@@ -1,68 +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/>.
//! Types that are specific to the Statemine runtime.
use codec::{Decode, Encode};
use scale_info::TypeInfo;
use sp_runtime::FixedU128;
/// Unchecked Statemine extrinsic.
pub type UncheckedExtrinsic = bp_polkadot_core::UncheckedExtrinsic<Call>;
/// Statemine Runtime `Call` enum.
///
/// The enum represents a subset of possible `Call`s we can send to Statemine chain.
/// Ideally this code would be auto-generated from metadata, because we want to
/// avoid depending directly on the ENTIRE runtime just to get the encoding of `Dispatchable`s.
///
/// All entries here (like pretty much in the entire file) must be kept in sync with Statemine
/// `construct_runtime`, so that we maintain SCALE-compatibility.
///
/// See: [link](https://github.com/paritytech/cumulus/blob/master/parachains/runtimes/assets/statemine/src/lib.rs)
#[allow(clippy::large_enum_variant)]
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)]
pub enum Call {
/// With-Statemint bridge pallet.
// TODO (https://github.com/paritytech/parity-bridges-common/issues/1626):
// must be updated when we'll make appropriate changes in the Statemine runtime
#[codec(index = 42)]
WithStatemintBridgePallet(WithStatemintBridgePalletCall),
}
/// Calls of the with-Statemint bridge pallet.
// TODO (https://github.com/paritytech/parity-bridges-common/issues/1626):
// must be updated when we'll make appropriate changes in the Statemine runtime
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)]
#[allow(non_camel_case_types)]
pub enum WithStatemintBridgePalletCall {
#[codec(index = 42)]
update_dot_to_ksm_conversion_rate(FixedU128),
}
impl sp_runtime::traits::Dispatchable for Call {
type RuntimeOrigin = ();
type Config = ();
type Info = ();
type PostInfo = ();
fn dispatch(
self,
_origin: Self::RuntimeOrigin,
) -> sp_runtime::DispatchResultWithInfo<Self::PostInfo> {
unimplemented!("The Call is not expected to be dispatched.")
}
}
+1 -7
View File
@@ -19,7 +19,7 @@ use bp_runtime::{
Chain as ChainBase, EncodedOrDecodedCall, HashOf, TransactionEra, TransactionEraOf,
};
use codec::{Codec, Encode};
use frame_support::weights::{Weight, WeightToFee};
use frame_support::weights::WeightToFee;
use jsonrpsee::core::{DeserializeOwned, Serialize};
use num_traits::Zero;
use sc_transaction_pool_api::TransactionStatus;
@@ -52,8 +52,6 @@ pub trait Chain: ChainBase + Clone {
/// How often blocks are produced on that chain. It's suggested to set this value
/// to match the block time of the chain.
const AVERAGE_BLOCK_INTERVAL: Duration;
/// Maximal expected storage proof overhead (in bytes).
const STORAGE_PROOF_OVERHEAD: u32;
/// Block type.
type SignedBlock: Member + Serialize + DeserializeOwned + BlockWithJustification<Self::Header>;
@@ -106,10 +104,6 @@ pub trait ChainWithMessages: Chain {
/// The method is provided by the runtime that is bridged with this `ChainWithMessages`.
const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str;
/// Additional weight of the dispatch fee payment if dispatch is paid at the target chain
/// and this `ChainWithMessages` is the target chain.
const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight;
/// Maximal number of unrewarded relayers in a single confirmation transaction at this
/// `ChainWithMessages`.
const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce;
@@ -17,7 +17,5 @@
//! Contains several Substrate-specific metrics that may be exposed by relay.
pub use float_storage_value::{FixedU128OrOne, FloatStorageValue, FloatStorageValueMetric};
pub use storage_proof_overhead::StorageProofOverheadMetric;
mod float_storage_value;
mod storage_proof_overhead;
@@ -1,101 +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/>.
use crate::{chain::Chain, client::Client, error::Error};
use async_trait::async_trait;
use relay_utils::metrics::{
metric_name, register, Gauge, Metric, PrometheusError, Registry, StandaloneMetric, U64,
};
use sp_core::storage::StorageKey;
use sp_runtime::traits::Header as HeaderT;
use sp_storage::well_known_keys::CODE;
use std::time::Duration;
/// Storage proof overhead update interval (in blocks).
const UPDATE_INTERVAL_IN_BLOCKS: u32 = 100;
/// Metric that represents extra size of storage proof as unsigned integer gauge.
///
/// There's one thing to keep in mind when using this metric: the overhead may be slightly
/// different for other values, but this metric gives a good estimation.
#[derive(Debug)]
pub struct StorageProofOverheadMetric<C: Chain> {
client: Client<C>,
metric: Gauge<U64>,
}
impl<C: Chain> Clone for StorageProofOverheadMetric<C> {
fn clone(&self) -> Self {
StorageProofOverheadMetric { client: self.client.clone(), metric: self.metric.clone() }
}
}
impl<C: Chain> StorageProofOverheadMetric<C> {
/// Create new metric instance with given name and help.
pub fn new(client: Client<C>, name: String, help: String) -> Result<Self, PrometheusError> {
Ok(StorageProofOverheadMetric {
client,
metric: Gauge::new(metric_name(None, &name), help)?,
})
}
/// Returns approximate storage proof size overhead.
async fn compute_storage_proof_overhead(&self) -> Result<usize, Error> {
let best_header_hash = self.client.best_finalized_header_hash().await?;
let best_header = self.client.header_by_hash(best_header_hash).await?;
let storage_proof = self
.client
.prove_storage(vec![StorageKey(CODE.to_vec())], best_header_hash)
.await?;
let storage_proof_size: usize = storage_proof.iter_nodes().map(|n| n.len()).sum();
let storage_value_reader = bp_runtime::StorageProofChecker::<C::Hasher>::new(
*best_header.state_root(),
storage_proof,
)
.map_err(Error::StorageProofError)?;
let maybe_encoded_storage_value =
storage_value_reader.read_value(CODE).map_err(Error::StorageProofError)?;
let encoded_storage_value_size =
maybe_encoded_storage_value.ok_or(Error::MissingMandatoryStorageValue)?.len();
Ok(storage_proof_size - encoded_storage_value_size)
}
}
impl<C: Chain> Metric for StorageProofOverheadMetric<C> {
fn register(&self, registry: &Registry) -> Result<(), PrometheusError> {
register(self.metric.clone(), registry).map(drop)
}
}
#[async_trait]
impl<C: Chain> StandaloneMetric for StorageProofOverheadMetric<C> {
fn update_interval(&self) -> Duration {
C::AVERAGE_BLOCK_INTERVAL * UPDATE_INTERVAL_IN_BLOCKS
}
async fn update(&self) {
relay_utils::metrics::set_gauge_value(
&self.metric,
self.compute_storage_proof_overhead()
.await
.map(|overhead| Some(overhead as u64)),
);
}
}
@@ -54,7 +54,6 @@ impl Chain for TestChain {
const TOKEN_ID: Option<&'static str> = None;
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = "TestMethod";
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_millis(0);
const STORAGE_PROOF_OVERHEAD: u32 = 0;
type SignedBlock = sp_runtime::generic::SignedBlock<
sp_runtime::generic::Block<Self::Header, sp_runtime::OpaqueExtrinsic>,
-2
View File
@@ -57,7 +57,6 @@ impl Chain for Westend {
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_westend::BEST_FINALIZED_WESTEND_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_westend::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = bp_westend::SignedBlock;
type Call = ();
@@ -114,7 +113,6 @@ impl Chain for Westmint {
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_westend::BEST_FINALIZED_WESTMINT_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_westend::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = bp_westend::SignedBlock;
type Call = ();
-1
View File
@@ -57,7 +57,6 @@ impl Chain for Wococo {
const BEST_FINALIZED_HEADER_ID_METHOD: &'static str =
bp_wococo::BEST_FINALIZED_WOCOCO_HEADER_METHOD;
const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6);
const STORAGE_PROOF_OVERHEAD: u32 = bp_wococo::EXTRA_STORAGE_PROOF_SIZE;
type SignedBlock = bp_wococo::SignedBlock;
type Call = ();
@@ -1,439 +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/>.
//! Tools for updating conversion rate that is stored in the runtime storage.
use crate::{messages_lane::SubstrateMessageLane, TransactionParams};
use relay_substrate_client::{
transaction_stall_timeout, AccountIdOf, AccountKeyPairOf, CallOf, Chain, ChainWithTransactions,
Client, SignParam, TransactionEra, UnsignedTransaction,
};
use relay_utils::metrics::F64SharedRef;
use sp_core::Pair;
use std::time::{Duration, Instant};
/// Duration between updater iterations.
const SLEEP_DURATION: Duration = Duration::from_secs(60);
/// Duration which will almost never expire. Since changing conversion rate may require manual
/// intervention (e.g. if call is made through `multisig` pallet), we don't want relayer to
/// resubmit transaction often.
const ALMOST_NEVER_DURATION: Duration = Duration::from_secs(60 * 60 * 24 * 30);
/// Update-conversion-rate transaction status.
#[derive(Debug, Clone, Copy, PartialEq)]
enum TransactionStatus {
/// We have not submitted any transaction recently.
Idle,
/// We have recently submitted transaction that should update conversion rate.
Submitted(Instant, f64),
}
/// Different ways of building 'update conversion rate' calls.
pub trait UpdateConversionRateCallBuilder<C: Chain> {
/// Given conversion rate, build call that updates conversion rate in given chain runtime
/// storage.
fn build_update_conversion_rate_call(conversion_rate: f64) -> anyhow::Result<CallOf<C>>;
}
impl<C: Chain> UpdateConversionRateCallBuilder<C> for () {
fn build_update_conversion_rate_call(_conversion_rate: f64) -> anyhow::Result<CallOf<C>> {
Err(anyhow::format_err!("Conversion rate update is not supported at {}", C::NAME))
}
}
/// Macro that generates `UpdateConversionRateCallBuilder` implementation for the case when
/// you have a direct access to the source chain runtime.
#[rustfmt::skip]
#[macro_export]
macro_rules! generate_direct_update_conversion_rate_call_builder {
(
$source_chain:ident,
$mocked_builder:ident,
$runtime:ty,
$instance:ty,
$parameter:path
) => {
pub struct $mocked_builder;
impl $crate::conversion_rate_update::UpdateConversionRateCallBuilder<$source_chain>
for $mocked_builder
{
fn build_update_conversion_rate_call(
conversion_rate: f64,
) -> anyhow::Result<relay_substrate_client::CallOf<$source_chain>> {
Ok(pallet_bridge_messages::Call::update_pallet_parameter::<$runtime, $instance> {
parameter: $parameter(sp_runtime::FixedU128::from_float(conversion_rate)),
}.into())
}
}
};
}
/// Macro that generates `UpdateConversionRateCallBuilder` implementation for the case when
/// you only have an access to the mocked version of source chain runtime. In this case you
/// should provide "name" of the call variant for the bridge messages calls, the "name" of
/// the variant for the `update_pallet_parameter` call within that first option and the name
/// of the conversion rate parameter itself.
#[rustfmt::skip]
#[macro_export]
macro_rules! generate_mocked_update_conversion_rate_call_builder {
(
$source_chain:ident,
$mocked_builder:ident,
$bridge_messages:path,
$update_pallet_parameter:path,
$parameter:path
) => {
pub struct $mocked_builder;
impl $crate::conversion_rate_update::UpdateConversionRateCallBuilder<$source_chain>
for $mocked_builder
{
fn build_update_conversion_rate_call(
conversion_rate: f64,
) -> anyhow::Result<relay_substrate_client::CallOf<$source_chain>> {
Ok($bridge_messages($update_pallet_parameter($parameter(
sp_runtime::FixedU128::from_float(conversion_rate),
))))
}
}
};
}
/// Run infinite conversion rate updater loop.
///
/// The loop is maintaining the Left -> Right conversion rate, used as `RightTokens = LeftTokens *
/// Rate`.
pub fn run_conversion_rate_update_loop<Lane>(
client: Client<Lane::SourceChain>,
transaction_params: TransactionParams<AccountKeyPairOf<Lane::SourceChain>>,
left_to_right_stored_conversion_rate: F64SharedRef,
left_to_base_conversion_rate: F64SharedRef,
right_to_base_conversion_rate: F64SharedRef,
max_difference_ratio: f64,
) where
Lane: SubstrateMessageLane,
Lane::SourceChain: ChainWithTransactions,
AccountIdOf<Lane::SourceChain>: From<<AccountKeyPairOf<Lane::SourceChain> as Pair>::Public>,
{
let stall_timeout = transaction_stall_timeout(
transaction_params.mortality,
Lane::SourceChain::AVERAGE_BLOCK_INTERVAL,
ALMOST_NEVER_DURATION,
);
log::info!(
target: "bridge",
"Starting {} -> {} conversion rate (on {}) update loop. Stall timeout: {}s",
Lane::TargetChain::NAME,
Lane::SourceChain::NAME,
Lane::SourceChain::NAME,
stall_timeout.as_secs(),
);
async_std::task::spawn(async move {
let mut transaction_status = TransactionStatus::Idle;
loop {
async_std::task::sleep(SLEEP_DURATION).await;
let maybe_new_conversion_rate = maybe_select_new_conversion_rate(
stall_timeout,
&mut transaction_status,
&left_to_right_stored_conversion_rate,
&left_to_base_conversion_rate,
&right_to_base_conversion_rate,
max_difference_ratio,
)
.await;
if let Some((prev_conversion_rate, new_conversion_rate)) = maybe_new_conversion_rate {
log::info!(
target: "bridge",
"Going to update {} -> {} (on {}) conversion rate to {}.",
Lane::TargetChain::NAME,
Lane::SourceChain::NAME,
Lane::SourceChain::NAME,
new_conversion_rate,
);
let result = update_target_to_source_conversion_rate::<Lane>(
client.clone(),
transaction_params.clone(),
new_conversion_rate,
)
.await;
match result {
Ok(()) => {
transaction_status =
TransactionStatus::Submitted(Instant::now(), prev_conversion_rate);
},
Err(error) => {
log::error!(
target: "bridge",
"Failed to submit conversion rate update transaction: {:?}",
error,
);
},
}
}
}
});
}
/// Select new conversion rate to submit to the node.
async fn maybe_select_new_conversion_rate(
stall_timeout: Duration,
transaction_status: &mut TransactionStatus,
left_to_right_stored_conversion_rate: &F64SharedRef,
left_to_base_conversion_rate: &F64SharedRef,
right_to_base_conversion_rate: &F64SharedRef,
max_difference_ratio: f64,
) -> Option<(f64, f64)> {
let left_to_right_stored_conversion_rate =
(*left_to_right_stored_conversion_rate.read().await)?;
match *transaction_status {
TransactionStatus::Idle => (),
TransactionStatus::Submitted(submitted_at, _)
if Instant::now() - submitted_at > stall_timeout =>
{
log::error!(
target: "bridge",
"Conversion rate update transaction has been lost and loop stalled. Restarting",
);
// we assume that our transaction has been lost
*transaction_status = TransactionStatus::Idle;
},
TransactionStatus::Submitted(_, previous_left_to_right_stored_conversion_rate) => {
// we can't compare float values from different sources directly, so we only care
// whether the stored rate has been changed or not. If it has been changed, then we
// assume that our proposal has been accepted.
//
// float comparison is ok here, because we compare same-origin (stored in runtime
// storage) values and if they are different, it means that the value has actually been
// updated
#[allow(clippy::float_cmp)]
if previous_left_to_right_stored_conversion_rate == left_to_right_stored_conversion_rate
{
// the rate has not been changed => we won't submit any transactions until it is
// accepted, or the rate is changed by someone else
return None
}
*transaction_status = TransactionStatus::Idle;
},
}
let left_to_base_conversion_rate = (*left_to_base_conversion_rate.read().await)?;
let right_to_base_conversion_rate = (*right_to_base_conversion_rate.read().await)?;
let actual_left_to_right_conversion_rate =
left_to_base_conversion_rate / right_to_base_conversion_rate;
let rate_difference =
(actual_left_to_right_conversion_rate - left_to_right_stored_conversion_rate).abs();
let rate_difference_ratio = rate_difference / left_to_right_stored_conversion_rate;
if rate_difference_ratio < max_difference_ratio {
return None
}
Some((left_to_right_stored_conversion_rate, actual_left_to_right_conversion_rate))
}
/// Update Target -> Source tokens conversion rate, stored in the Source runtime storage.
pub async fn update_target_to_source_conversion_rate<Lane>(
client: Client<Lane::SourceChain>,
transaction_params: TransactionParams<AccountKeyPairOf<Lane::SourceChain>>,
updated_rate: f64,
) -> anyhow::Result<()>
where
Lane: SubstrateMessageLane,
Lane::SourceChain: ChainWithTransactions,
AccountIdOf<Lane::SourceChain>: From<<AccountKeyPairOf<Lane::SourceChain> as Pair>::Public>,
{
let genesis_hash = *client.genesis_hash();
let signer_id = transaction_params.signer.public().into();
let (spec_version, transaction_version) = client.simple_runtime_version().await?;
let call =
Lane::TargetToSourceChainConversionRateUpdateBuilder::build_update_conversion_rate_call(
updated_rate,
)?;
client
.submit_signed_extrinsic(
signer_id,
SignParam::<Lane::SourceChain> {
spec_version,
transaction_version,
genesis_hash,
signer: transaction_params.signer,
},
move |best_block_id, transaction_nonce| {
Ok(UnsignedTransaction::new(call.into(), transaction_nonce)
.era(TransactionEra::new(best_block_id, transaction_params.mortality)))
},
)
.await
.map(drop)
.map_err(|err| anyhow::format_err!("{:?}", err))
}
#[cfg(test)]
mod tests {
use super::*;
use async_std::sync::{Arc, RwLock};
const TEST_STALL_TIMEOUT: Duration = Duration::from_secs(60);
fn test_maybe_select_new_conversion_rate(
mut transaction_status: TransactionStatus,
stored_conversion_rate: Option<f64>,
left_to_base_conversion_rate: Option<f64>,
right_to_base_conversion_rate: Option<f64>,
max_difference_ratio: f64,
) -> (Option<(f64, f64)>, TransactionStatus) {
let stored_conversion_rate = Arc::new(RwLock::new(stored_conversion_rate));
let left_to_base_conversion_rate = Arc::new(RwLock::new(left_to_base_conversion_rate));
let right_to_base_conversion_rate = Arc::new(RwLock::new(right_to_base_conversion_rate));
let result = async_std::task::block_on(maybe_select_new_conversion_rate(
TEST_STALL_TIMEOUT,
&mut transaction_status,
&stored_conversion_rate,
&left_to_base_conversion_rate,
&right_to_base_conversion_rate,
max_difference_ratio,
));
(result, transaction_status)
}
#[test]
fn rate_is_not_updated_when_transaction_is_submitted() {
let status = TransactionStatus::Submitted(Instant::now(), 10.0);
assert_eq!(
test_maybe_select_new_conversion_rate(status, Some(10.0), Some(1.0), Some(1.0), 0.0),
(None, status),
);
}
#[test]
fn transaction_state_is_changed_to_idle_when_stored_rate_shanges() {
assert_eq!(
test_maybe_select_new_conversion_rate(
TransactionStatus::Submitted(Instant::now(), 1.0),
Some(10.0),
Some(1.0),
Some(1.0),
100.0
),
(None, TransactionStatus::Idle),
);
}
#[test]
fn transaction_is_not_submitted_when_left_to_base_rate_is_unknown() {
assert_eq!(
test_maybe_select_new_conversion_rate(
TransactionStatus::Idle,
Some(10.0),
None,
Some(1.0),
0.0
),
(None, TransactionStatus::Idle),
);
}
#[test]
fn transaction_is_not_submitted_when_right_to_base_rate_is_unknown() {
assert_eq!(
test_maybe_select_new_conversion_rate(
TransactionStatus::Idle,
Some(10.0),
Some(1.0),
None,
0.0
),
(None, TransactionStatus::Idle),
);
}
#[test]
fn transaction_is_not_submitted_when_stored_rate_is_unknown() {
assert_eq!(
test_maybe_select_new_conversion_rate(
TransactionStatus::Idle,
None,
Some(1.0),
Some(1.0),
0.0
),
(None, TransactionStatus::Idle),
);
}
#[test]
fn transaction_is_not_submitted_when_difference_is_below_threshold() {
assert_eq!(
test_maybe_select_new_conversion_rate(
TransactionStatus::Idle,
Some(1.0),
Some(1.0),
Some(1.01),
0.02
),
(None, TransactionStatus::Idle),
);
}
#[test]
fn transaction_is_submitted_when_difference_is_above_threshold() {
let left_to_right_stored_conversion_rate = 1.0;
let left_to_base_conversion_rate = 18f64;
let right_to_base_conversion_rate = 180f64;
assert!(left_to_base_conversion_rate < right_to_base_conversion_rate);
assert_eq!(
test_maybe_select_new_conversion_rate(
TransactionStatus::Idle,
Some(left_to_right_stored_conversion_rate),
Some(left_to_base_conversion_rate),
Some(right_to_base_conversion_rate),
0.02
),
(
Some((
left_to_right_stored_conversion_rate,
left_to_base_conversion_rate / right_to_base_conversion_rate,
)),
TransactionStatus::Idle
),
);
}
#[test]
fn transaction_expires() {
let status = TransactionStatus::Submitted(Instant::now() - TEST_STALL_TIMEOUT / 2, 10.0);
assert_eq!(
test_maybe_select_new_conversion_rate(status, Some(10.0), Some(1.0), Some(1.0), 0.0),
(None, status),
);
let status = TransactionStatus::Submitted(Instant::now() - TEST_STALL_TIMEOUT * 2, 10.0);
assert_eq!(
test_maybe_select_new_conversion_rate(status, Some(10.0), Some(1.0), Some(1.0), 0.0),
(Some((10.0, 1.0)), TransactionStatus::Idle),
);
}
}
@@ -1,106 +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/>.
//! Substrate relay helpers
use relay_utils::metrics::{FloatJsonValueMetric, PrometheusError, StandaloneMetric};
/// Creates standalone token price metric.
pub fn token_price_metric(token_id: &str) -> Result<FloatJsonValueMetric, PrometheusError> {
FloatJsonValueMetric::new(
format!("https://api.coingecko.com/api/v3/simple/price?ids={token_id}&vs_currencies=btc"),
format!("$.{token_id}.btc"),
format!("{}_to_base_conversion_rate", token_id.replace('-', "_")),
format!("Rate used to convert from {} to some BASE tokens", token_id.to_uppercase()),
)
}
/// Compute conversion rate between two tokens immediately, without spawning any metrics.
///
/// Returned rate may be used in expression: `from_tokens * rate -> to_tokens`.
pub async fn tokens_conversion_rate_from_metrics(
from_token_id: &str,
to_token_id: &str,
) -> anyhow::Result<f64> {
let from_token_metric = token_price_metric(from_token_id)?;
from_token_metric.update().await;
let to_token_metric = token_price_metric(to_token_id)?;
to_token_metric.update().await;
let from_token_value = *from_token_metric.shared_value_ref().read().await;
let to_token_value = *to_token_metric.shared_value_ref().read().await;
// `FloatJsonValueMetric` guarantees that the value is positive && normal, so no additional
// checks required here
match (from_token_value, to_token_value) {
(Some(from_token_value), Some(to_token_value)) =>
Ok(tokens_conversion_rate(from_token_value, to_token_value)),
_ => Err(anyhow::format_err!(
"Failed to compute conversion rate from {} to {}",
from_token_id,
to_token_id,
)),
}
}
/// Compute conversion rate between two tokens, given token prices.
///
/// Returned rate may be used in expression: `from_tokens * rate -> to_tokens`.
///
/// Both prices are assumed to be normal and non-negative.
pub fn tokens_conversion_rate(from_token_value: f64, to_token_value: f64) -> f64 {
from_token_value / to_token_value
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rialto_to_millau_conversion_rate_is_correct() {
let rialto_price = 18.18;
let millau_price = 136.35;
assert!(rialto_price < millau_price);
let conversion_rate = tokens_conversion_rate(rialto_price, millau_price);
let rialto_amount = 100.0;
let millau_amount = rialto_amount * conversion_rate;
assert!(
rialto_amount > millau_amount,
"{} RLT * {} = {} MLU",
rialto_amount,
conversion_rate,
millau_amount,
);
}
#[test]
fn millau_to_rialto_conversion_rate_is_correct() {
let rialto_price = 18.18;
let millau_price = 136.35;
assert!(rialto_price < millau_price);
let conversion_rate = tokens_conversion_rate(millau_price, rialto_price);
let millau_amount = 100.0;
let rialto_amount = millau_amount * conversion_rate;
assert!(
rialto_amount > millau_amount,
"{} MLU * {} = {} RLT",
millau_amount,
conversion_rate,
rialto_amount,
);
}
}
@@ -18,10 +18,8 @@
#![warn(missing_docs)]
pub mod conversion_rate_update;
pub mod error;
pub mod finality;
pub mod helpers;
pub mod messages_lane;
pub mod messages_metrics;
pub mod messages_source;
@@ -17,8 +17,6 @@
//! Tools for supporting message lanes between two Substrate-based chains.
use crate::{
conversion_rate_update::UpdateConversionRateCallBuilder,
messages_metrics::StandaloneMessagesMetrics,
messages_source::{SubstrateMessagesProof, SubstrateMessagesSource},
messages_target::{SubstrateMessagesDeliveryProof, SubstrateMessagesTarget},
on_demand::OnDemandRelay,
@@ -33,49 +31,21 @@ use bridge_runtime_common::messages::{
};
use codec::Encode;
use frame_support::{dispatch::GetDispatchInfo, weights::Weight};
use messages_relay::{message_lane::MessageLane, relay_strategy::RelayStrategy};
use messages_relay::message_lane::MessageLane;
use pallet_bridge_messages::{Call as BridgeMessagesCall, Config as BridgeMessagesConfig};
use relay_substrate_client::{
transaction_stall_timeout, AccountKeyPairOf, BalanceOf, BlockNumberOf, CallOf, Chain,
ChainWithMessages, ChainWithTransactions, Client, HashOf,
};
use relay_utils::{metrics::MetricsParams, STALL_TIMEOUT};
use relay_utils::{
metrics::{GlobalMetrics, MetricsParams, StandaloneMetric},
STALL_TIMEOUT,
};
use sp_core::Pair;
use std::{convert::TryFrom, fmt::Debug, marker::PhantomData};
/// Substrate -> Substrate messages synchronization pipeline.
pub trait SubstrateMessageLane: 'static + Clone + Debug + Send + Sync {
/// Name of the source -> target tokens conversion rate parameter.
///
/// The parameter is stored at the target chain and the storage key is computed using
/// `bp_runtime::storage_parameter_key` function. If value is unknown, it is assumed
/// to be 1.
const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str>;
/// Name of the target -> source tokens conversion rate parameter.
///
/// The parameter is stored at the source chain and the storage key is computed using
/// `bp_runtime::storage_parameter_key` function. If value is unknown, it is assumed
/// to be 1.
const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str>;
/// Name of the source chain fee multiplier parameter.
///
/// The parameter is stored at the target chain and the storage key is computed using
/// `bp_runtime::storage_parameter_key` function. If value is unknown, it is assumed
/// to be 1.
const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str>;
/// Name of the target chain fee multiplier parameter.
///
/// The parameter is stored at the source chain and the storage key is computed using
/// `bp_runtime::storage_parameter_key` function. If value is unknown, it is assumed
/// to be 1.
const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str>;
/// Name of the transaction payment pallet, deployed at the source chain.
const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str>;
/// Name of the transaction payment pallet, deployed at the target chain.
const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str>;
/// Messages of this chain are relayed to the `TargetChain`.
type SourceChain: ChainWithMessages + ChainWithTransactions;
/// Messages from the `SourceChain` are dispatched on this chain.
@@ -85,16 +55,6 @@ pub trait SubstrateMessageLane: 'static + Clone + Debug + Send + Sync {
type ReceiveMessagesProofCallBuilder: ReceiveMessagesProofCallBuilder<Self>;
/// How receive messages delivery proof call is built?
type ReceiveMessagesDeliveryProofCallBuilder: ReceiveMessagesDeliveryProofCallBuilder<Self>;
/// `TargetChain` tokens to `SourceChain` tokens conversion rate update builder.
///
/// If not applicable to this bridge, you may use `()` here.
type TargetToSourceChainConversionRateUpdateBuilder: UpdateConversionRateCallBuilder<
Self::SourceChain,
>;
/// Message relay strategy.
type RelayStrategy: RelayStrategy;
}
/// Adapter that allows all `SubstrateMessageLane` to act as `MessageLane`.
@@ -138,10 +98,6 @@ pub struct MessagesRelayParams<P: SubstrateMessageLane> {
pub lane_id: LaneId,
/// Metrics parameters.
pub metrics_params: MetricsParams,
/// Pre-registered standalone metrics.
pub standalone_metrics: Option<StandaloneMessagesMetrics<P::SourceChain, P::TargetChain>>,
/// Relay strategy.
pub relay_strategy: P::RelayStrategy,
}
/// Run Substrate-to-Substrate messages sync loop.
@@ -170,13 +126,6 @@ where
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);
let standalone_metrics = params.standalone_metrics.map(Ok).unwrap_or_else(|| {
crate::messages_metrics::standalone_metrics::<P>(
source_client.clone(),
target_client.clone(),
)
})?;
log::info!(
target: "bridge",
"Starting {} -> {} messages relay.\n\t\
@@ -220,7 +169,6 @@ where
max_messages_in_single_batch,
max_messages_weight_in_single_batch,
max_messages_size_in_single_batch,
relay_strategy: params.relay_strategy,
},
},
SubstrateMessagesSource::<P>::new(
@@ -236,10 +184,12 @@ where
params.lane_id,
relayer_id_at_source,
params.target_transaction_params,
standalone_metrics.clone(),
params.source_to_target_headers_relay,
),
standalone_metrics.register_and_spawn(params.metrics_params)?,
{
GlobalMetrics::new()?.register_and_spawn(&params.metrics_params.registry)?;
params.metrics_params
},
futures::future::pending(),
)
.await
@@ -271,7 +221,6 @@ where
R: BridgeMessagesConfig<I, InboundRelayer = AccountIdOf<P::SourceChain>>,
I: 'static,
R::SourceHeaderChain: bp_messages::target_chain::SourceHeaderChain<
R::InboundMessageFee,
MessagesProof = FromBridgedChainMessagesProof<HashOf<P::SourceChain>>,
>,
CallOf<P::TargetChain>: From<BridgeMessagesCall<R, I>> + GetDispatchInfo,
@@ -16,260 +16,20 @@
//! Tools for supporting message lanes between two Substrate-based chains.
use crate::{helpers::tokens_conversion_rate, messages_lane::SubstrateMessageLane, TaggedAccount};
use crate::TaggedAccount;
use codec::Decode;
use frame_system::AccountInfo;
use pallet_balances::AccountData;
use relay_substrate_client::{
metrics::{
FixedU128OrOne, FloatStorageValue, FloatStorageValueMetric, StorageProofOverheadMetric,
},
metrics::{FloatStorageValue, FloatStorageValueMetric},
AccountIdOf, BalanceOf, Chain, ChainWithBalances, Client, Error as SubstrateError, IndexOf,
};
use relay_utils::metrics::{
FloatJsonValueMetric, GlobalMetrics, MetricsParams, PrometheusError, StandaloneMetric,
};
use relay_utils::metrics::{MetricsParams, StandaloneMetric};
use sp_core::storage::StorageData;
use sp_runtime::{FixedPointNumber, FixedU128};
use std::{convert::TryFrom, fmt::Debug, marker::PhantomData};
/// Name of the `NextFeeMultiplier` storage value within the transaction payment pallet.
const NEXT_FEE_MULTIPLIER_VALUE_NAME: &str = "NextFeeMultiplier";
/// Shared references to the standalone metrics of the message lane relay loop.
#[derive(Debug, Clone)]
pub struct StandaloneMessagesMetrics<SC: Chain, TC: Chain> {
/// Global metrics.
pub global: GlobalMetrics,
/// Storage chain proof overhead metric.
pub source_storage_proof_overhead: StorageProofOverheadMetric<SC>,
/// Target chain proof overhead metric.
pub target_storage_proof_overhead: StorageProofOverheadMetric<TC>,
/// Source tokens to base conversion rate metric.
pub source_to_base_conversion_rate: Option<FloatJsonValueMetric>,
/// Target tokens to base conversion rate metric.
pub target_to_base_conversion_rate: Option<FloatJsonValueMetric>,
/// Source tokens to target tokens conversion rate metric. This rate is stored by the target
/// chain.
pub source_to_target_conversion_rate: Option<FloatStorageValueMetric<TC, FixedU128OrOne>>,
/// Target tokens to source tokens conversion rate metric. This rate is stored by the source
/// chain.
pub target_to_source_conversion_rate: Option<FloatStorageValueMetric<SC, FixedU128OrOne>>,
/// Actual source chain fee multiplier.
pub source_fee_multiplier: Option<FloatStorageValueMetric<SC, FixedU128OrOne>>,
/// Source chain fee multiplier, stored at the target chain.
pub source_fee_multiplier_at_target: Option<FloatStorageValueMetric<TC, FixedU128OrOne>>,
/// Actual target chain fee multiplier.
pub target_fee_multiplier: Option<FloatStorageValueMetric<TC, FixedU128OrOne>>,
/// Target chain fee multiplier, stored at the target chain.
pub target_fee_multiplier_at_source: Option<FloatStorageValueMetric<SC, FixedU128OrOne>>,
}
impl<SC: Chain, TC: Chain> StandaloneMessagesMetrics<SC, TC> {
/// Swap source and target sides.
pub fn reverse(self) -> StandaloneMessagesMetrics<TC, SC> {
StandaloneMessagesMetrics {
global: self.global,
source_storage_proof_overhead: self.target_storage_proof_overhead,
target_storage_proof_overhead: self.source_storage_proof_overhead,
source_to_base_conversion_rate: self.target_to_base_conversion_rate,
target_to_base_conversion_rate: self.source_to_base_conversion_rate,
source_to_target_conversion_rate: self.target_to_source_conversion_rate,
target_to_source_conversion_rate: self.source_to_target_conversion_rate,
source_fee_multiplier: self.target_fee_multiplier,
source_fee_multiplier_at_target: self.target_fee_multiplier_at_source,
target_fee_multiplier: self.source_fee_multiplier,
target_fee_multiplier_at_source: self.source_fee_multiplier_at_target,
}
}
/// Register all metrics in the registry.
pub fn register_and_spawn(
self,
metrics: MetricsParams,
) -> Result<MetricsParams, PrometheusError> {
self.global.register_and_spawn(&metrics.registry)?;
self.source_storage_proof_overhead.register_and_spawn(&metrics.registry)?;
self.target_storage_proof_overhead.register_and_spawn(&metrics.registry)?;
if let Some(m) = self.source_to_base_conversion_rate {
m.register_and_spawn(&metrics.registry)?;
}
if let Some(m) = self.target_to_base_conversion_rate {
m.register_and_spawn(&metrics.registry)?;
}
if let Some(m) = self.target_to_source_conversion_rate {
m.register_and_spawn(&metrics.registry)?;
}
if let Some(m) = self.source_fee_multiplier {
m.register_and_spawn(&metrics.registry)?;
}
if let Some(m) = self.source_fee_multiplier_at_target {
m.register_and_spawn(&metrics.registry)?;
}
if let Some(m) = self.target_fee_multiplier {
m.register_and_spawn(&metrics.registry)?;
}
if let Some(m) = self.target_fee_multiplier_at_source {
m.register_and_spawn(&metrics.registry)?;
}
Ok(metrics)
}
/// Return conversion rate from target to source tokens.
pub async fn target_to_source_conversion_rate(&self) -> Option<f64> {
let from_token_value =
(*self.target_to_base_conversion_rate.as_ref()?.shared_value_ref().read().await)?;
let to_token_value =
(*self.source_to_base_conversion_rate.as_ref()?.shared_value_ref().read().await)?;
Some(tokens_conversion_rate(from_token_value, to_token_value))
}
}
/// Create symmetric standalone metrics for the message lane relay loop.
///
/// All metrics returned by this function are exposed by loops that are serving given lane (`P`)
/// and by loops that are serving reverse lane (`P` with swapped `TargetChain` and `SourceChain`).
/// We assume that either conversion rate parameters have values in the storage, or they are
/// initialized with 1:1.
pub fn standalone_metrics<P: SubstrateMessageLane>(
source_client: Client<P::SourceChain>,
target_client: Client<P::TargetChain>,
) -> anyhow::Result<StandaloneMessagesMetrics<P::SourceChain, P::TargetChain>> {
Ok(StandaloneMessagesMetrics {
global: GlobalMetrics::new()?,
source_storage_proof_overhead: StorageProofOverheadMetric::new(
source_client.clone(),
format!("{}_storage_proof_overhead", P::SourceChain::NAME.to_lowercase()),
format!("{} storage proof overhead", P::SourceChain::NAME),
)?,
target_storage_proof_overhead: StorageProofOverheadMetric::new(
target_client.clone(),
format!("{}_storage_proof_overhead", P::TargetChain::NAME.to_lowercase()),
format!("{} storage proof overhead", P::TargetChain::NAME),
)?,
source_to_base_conversion_rate: P::SourceChain::TOKEN_ID
.map(|source_chain_token_id| {
crate::helpers::token_price_metric(source_chain_token_id).map(Some)
})
.unwrap_or(Ok(None))?,
target_to_base_conversion_rate: P::TargetChain::TOKEN_ID
.map(|target_chain_token_id| {
crate::helpers::token_price_metric(target_chain_token_id).map(Some)
})
.unwrap_or(Ok(None))?,
source_to_target_conversion_rate: P::SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME
.map(bp_runtime::storage_parameter_key)
.map(|key| {
FloatStorageValueMetric::new(
FixedU128OrOne::default(),
target_client.clone(),
key,
format!(
"{}_{}_to_{}_conversion_rate",
P::TargetChain::NAME,
P::SourceChain::NAME,
P::TargetChain::NAME
),
format!(
"{} to {} tokens conversion rate (used by {})",
P::SourceChain::NAME,
P::TargetChain::NAME,
P::TargetChain::NAME
),
)
.map(Some)
})
.unwrap_or(Ok(None))?,
target_to_source_conversion_rate: P::TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME
.map(bp_runtime::storage_parameter_key)
.map(|key| {
FloatStorageValueMetric::new(
FixedU128OrOne::default(),
source_client.clone(),
key,
format!(
"{}_{}_to_{}_conversion_rate",
P::SourceChain::NAME,
P::TargetChain::NAME,
P::SourceChain::NAME
),
format!(
"{} to {} tokens conversion rate (used by {})",
P::TargetChain::NAME,
P::SourceChain::NAME,
P::SourceChain::NAME
),
)
.map(Some)
})
.unwrap_or(Ok(None))?,
source_fee_multiplier: P::AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME
.map(|pallet| bp_runtime::storage_value_key(pallet, NEXT_FEE_MULTIPLIER_VALUE_NAME))
.map(|key| {
log::trace!(target: "bridge", "{}_fee_multiplier", P::SourceChain::NAME);
FloatStorageValueMetric::new(
FixedU128OrOne::default(),
source_client.clone(),
key,
format!("{}_fee_multiplier", P::SourceChain::NAME,),
format!("{} fee multiplier", P::SourceChain::NAME,),
)
.map(Some)
})
.unwrap_or(Ok(None))?,
source_fee_multiplier_at_target: P::SOURCE_FEE_MULTIPLIER_PARAMETER_NAME
.map(bp_runtime::storage_parameter_key)
.map(|key| {
FloatStorageValueMetric::new(
FixedU128OrOne::default(),
target_client.clone(),
key,
format!("{}_{}_fee_multiplier", P::TargetChain::NAME, P::SourceChain::NAME,),
format!(
"{} fee multiplier stored at {}",
P::SourceChain::NAME,
P::TargetChain::NAME,
),
)
.map(Some)
})
.unwrap_or(Ok(None))?,
target_fee_multiplier: P::AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME
.map(|pallet| bp_runtime::storage_value_key(pallet, NEXT_FEE_MULTIPLIER_VALUE_NAME))
.map(|key| {
log::trace!(target: "bridge", "{}_fee_multiplier", P::TargetChain::NAME);
FloatStorageValueMetric::new(
FixedU128OrOne::default(),
target_client,
key,
format!("{}_fee_multiplier", P::TargetChain::NAME,),
format!("{} fee multiplier", P::TargetChain::NAME,),
)
.map(Some)
})
.unwrap_or(Ok(None))?,
target_fee_multiplier_at_source: P::TARGET_FEE_MULTIPLIER_PARAMETER_NAME
.map(bp_runtime::storage_parameter_key)
.map(|key| {
FloatStorageValueMetric::new(
FixedU128OrOne::default(),
source_client,
key,
format!("{}_{}_fee_multiplier", P::SourceChain::NAME, P::TargetChain::NAME,),
format!(
"{} fee multiplier stored at {}",
P::TargetChain::NAME,
P::SourceChain::NAME,
),
)
.map(Some)
})
.unwrap_or(Ok(None))?,
})
}
/// Add relay accounts balance metrics.
pub async fn add_relay_balances_metrics<C: ChainWithBalances>(
client: Client<C>,
@@ -359,9 +119,6 @@ fn convert_to_token_balance(balance: u128, token_decimals: u32) -> FixedU128 {
#[cfg(test)]
mod tests {
use super::*;
use frame_support::storage::generator::StorageValue;
use sp_core::storage::StorageKey;
#[test]
fn token_decimals_used_properly() {
let plancks = 425_000_000_000;
@@ -369,12 +126,4 @@ mod tests {
let dots = convert_to_token_balance(plancks, token_decimals);
assert_eq!(dots, FixedU128::saturating_from_rational(425, 10));
}
#[test]
fn next_fee_multiplier_storage_key_is_correct() {
assert_eq!(
bp_runtime::storage_value_key("TransactionPayment", NEXT_FEE_MULTIPLIER_VALUE_NAME),
StorageKey(pallet_transaction_payment::NextFeeMultiplier::<rialto_runtime::Runtime>::storage_value_final_key().to_vec()),
);
}
}
@@ -31,13 +31,11 @@ use async_std::sync::Arc;
use async_trait::async_trait;
use bp_messages::{
storage_keys::{operating_mode_key, outbound_lane_data_key},
InboundMessageDetails, LaneId, MessageData, MessageNonce, MessagePayload,
MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails, UnrewardedRelayersState,
};
use bp_runtime::{messages::DispatchFeePayment, BasicOperatingMode, HeaderIdProvider};
use bridge_runtime_common::messages::{
source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
InboundMessageDetails, LaneId, MessageNonce, MessagePayload, MessagesOperatingMode,
OutboundLaneData, OutboundMessageDetails,
};
use bp_runtime::{BasicOperatingMode, HeaderIdProvider};
use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof;
use codec::{Decode, Encode};
use frame_support::weights::Weight;
use messages_relay::{
@@ -47,11 +45,11 @@ use messages_relay::{
SourceClientState,
},
};
use num_traits::{Bounded, Zero};
use num_traits::Zero;
use relay_substrate_client::{
AccountIdOf, AccountKeyPairOf, BalanceOf, BlockNumberOf, Chain, ChainWithMessages,
ChainWithTransactions, Client, Error as SubstrateError, HashOf, HeaderIdOf, IndexOf, SignParam,
TransactionEra, TransactionTracker, UnsignedTransaction,
AccountIdOf, AccountKeyPairOf, BalanceOf, BlockNumberOf, Chain, ChainWithMessages, Client,
Error as SubstrateError, HashOf, HeaderIdOf, IndexOf, SignParam, TransactionEra,
TransactionTracker, UnsignedTransaction,
};
use relay_utils::{relay_loop::Client as RelayClient, HeaderId};
use sp_core::{Bytes, Pair};
@@ -62,7 +60,7 @@ use std::ops::RangeInclusive;
/// required to submit to the target node: cumulative dispatch weight of bundled messages and
/// the proof itself.
pub type SubstrateMessagesProof<C> = (Weight, FromBridgedChainMessagesProof<HashOf<C>>);
type MessagesToRefine<'a, Balance> = Vec<(MessagePayload, &'a mut OutboundMessageDetails<Balance>)>;
type MessagesToRefine<'a> = Vec<(MessagePayload, &'a mut OutboundMessageDetails)>;
/// Substrate client as Substrate messages source.
pub struct SubstrateMessagesSource<P: SubstrateMessageLane> {
@@ -207,9 +205,7 @@ where
// prepare arguments of the inbound message details call (if we need it)
let mut msgs_to_refine = vec![];
for out_msg_details in out_msgs_details.iter_mut() {
if out_msg_details.dispatch_fee_payment != DispatchFeePayment::AtTargetChain {
continue
}
// in our current strategy all messages are supposed to be paid at the target chain
// for pay-at-target messages we may want to ask target chain for
// refined dispatch weight
@@ -218,7 +214,7 @@ where
&self.lane_id,
out_msg_details.nonce,
);
let msg_data: MessageData<BalanceOf<P::SourceChain>> =
let msg_payload: MessagePayload =
self.source_client.storage_value(msg_key, Some(id.1)).await?.ok_or_else(|| {
SubstrateError::Custom(format!(
"Message to {} {:?}/{} is missing from runtime the storage of {} at {:?}",
@@ -230,7 +226,7 @@ where
))
})?;
msgs_to_refine.push((msg_data.payload, out_msg_details));
msgs_to_refine.push((msg_payload, out_msg_details));
}
for mut msgs_to_refine_batch in
@@ -277,8 +273,7 @@ where
MessageDetails {
dispatch_weight: out_msg_details.dispatch_weight,
size: out_msg_details.size as _,
reward: out_msg_details.delivery_and_dispatch_fee,
dispatch_fee_payment: out_msg_details.dispatch_fee_payment,
reward: Zero::zero(),
},
);
}
@@ -370,39 +365,6 @@ where
target_to_source_headers_relay.require_more_headers(id.0).await;
}
}
async fn estimate_confirmation_transaction(
&self,
) -> <MessageLaneAdapter<P> as MessageLane>::SourceChainBalance {
let runtime_version = match self.source_client.runtime_version().await {
Ok(v) => v,
Err(_) => return BalanceOf::<P::SourceChain>::max_value(),
};
async {
let dummy_tx = P::SourceChain::sign_transaction(
SignParam::<P::SourceChain> {
spec_version: runtime_version.spec_version,
transaction_version: runtime_version.transaction_version,
genesis_hash: *self.source_client.genesis_hash(),
signer: self.transaction_params.signer.clone(),
},
make_messages_delivery_proof_transaction::<P>(
&self.transaction_params,
HeaderId(Default::default(), Default::default()),
Zero::zero(),
prepare_dummy_messages_delivery_proof::<P::SourceChain, P::TargetChain>(),
false,
)?,
)?
.encode();
self.source_client
.estimate_extrinsic_fee(Bytes(dummy_tx))
.await
.map(|fee| fee.inclusion_fee())
}
.await
.unwrap_or_else(|_| BalanceOf::<P::SourceChain>::max_value())
}
}
/// Ensure that the messages pallet at source chain is active.
@@ -441,30 +403,6 @@ fn make_messages_delivery_proof_transaction<P: SubstrateMessageLane>(
.era(TransactionEra::new(source_best_block_id, source_transaction_params.mortality)))
}
/// Prepare 'dummy' messages delivery proof that will compose the delivery confirmation transaction.
///
/// We don't care about proof actually being the valid proof, because its validity doesn't
/// affect the call weight - we only care about its size.
fn prepare_dummy_messages_delivery_proof<SC: Chain, TC: Chain>(
) -> SubstrateMessagesDeliveryProof<TC> {
let single_message_confirmation_size =
bp_messages::InboundLaneData::<()>::encoded_size_hint_u32(1, 1);
let proof_size = TC::STORAGE_PROOF_OVERHEAD.saturating_add(single_message_confirmation_size);
(
UnrewardedRelayersState {
unrewarded_relayer_entries: 1,
messages_in_oldest_entry: 1,
total_messages: 1,
last_delivered_nonce: 1,
},
FromBridgedChainMessagesDeliveryProof {
bridged_header_hash: Default::default(),
storage_proof: vec![vec![0; proof_size as usize]],
lane: Default::default(),
},
)
}
/// Read best blocks from given client.
///
/// This function assumes that the chain that is followed by the `self_client` has
@@ -552,7 +490,7 @@ where
}
fn validate_out_msgs_details<C: Chain>(
out_msgs_details: &[OutboundMessageDetails<C::Balance>],
out_msgs_details: &[OutboundMessageDetails],
nonces: RangeInclusive<MessageNonce>,
) -> Result<(), SubstrateError> {
let make_missing_nonce_error = |expected_nonce| {
@@ -601,8 +539,8 @@ fn validate_out_msgs_details<C: Chain>(
fn split_msgs_to_refine<Source: Chain + ChainWithMessages, Target: Chain>(
lane_id: LaneId,
msgs_to_refine: MessagesToRefine<Source::Balance>,
) -> Result<Vec<MessagesToRefine<Source::Balance>>, SubstrateError> {
msgs_to_refine: MessagesToRefine,
) -> Result<Vec<MessagesToRefine>, SubstrateError> {
let max_batch_size = Target::max_extrinsic_size() as usize;
let mut batches = vec![];
@@ -635,23 +573,20 @@ fn split_msgs_to_refine<Source: Chain + ChainWithMessages, Target: Chain>(
#[cfg(test)]
mod tests {
use super::*;
use bp_runtime::{messages::DispatchFeePayment, Chain as ChainBase};
use codec::MaxEncodedLen;
use bp_runtime::Chain as ChainBase;
use relay_rialto_client::Rialto;
use relay_rococo_client::Rococo;
use relay_wococo_client::Wococo;
fn message_details_from_rpc(
nonces: RangeInclusive<MessageNonce>,
) -> Vec<OutboundMessageDetails<bp_wococo::Balance>> {
) -> Vec<OutboundMessageDetails> {
nonces
.into_iter()
.map(|nonce| bp_messages::OutboundMessageDetails {
nonce,
dispatch_weight: Weight::zero(),
size: 0,
delivery_and_dispatch_fee: 0,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
})
.collect()
}
@@ -704,31 +639,16 @@ mod tests {
));
}
#[test]
fn prepare_dummy_messages_delivery_proof_works() {
let expected_minimal_size =
bp_wococo::AccountId::max_encoded_len() as u32 + Rococo::STORAGE_PROOF_OVERHEAD;
let dummy_proof = prepare_dummy_messages_delivery_proof::<Wococo, Rococo>();
assert!(
dummy_proof.1.encode().len() as u32 > expected_minimal_size,
"Expected proof size at least {}. Got: {}",
expected_minimal_size,
dummy_proof.1.encode().len(),
);
}
fn check_split_msgs_to_refine(
payload_sizes: Vec<usize>,
expected_batches: Result<Vec<usize>, ()>,
) {
let mut out_msgs_details = vec![];
for (idx, _) in payload_sizes.iter().enumerate() {
out_msgs_details.push(OutboundMessageDetails::<BalanceOf<Rialto>> {
out_msgs_details.push(OutboundMessageDetails {
nonce: idx as MessageNonce,
dispatch_weight: Weight::zero(),
size: 0,
delivery_and_dispatch_fee: 0,
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
});
}
@@ -20,7 +20,6 @@
use crate::{
messages_lane::{MessageLaneAdapter, ReceiveMessagesProofCallBuilder, SubstrateMessageLane},
messages_metrics::StandaloneMessagesMetrics,
messages_source::{ensure_messages_pallet_active, read_client_state, SubstrateMessagesProof},
on_demand::OnDemandRelay,
TransactionParams,
@@ -32,24 +31,18 @@ use bp_messages::{
storage_keys::inbound_lane_data_key, total_unrewarded_messages, InboundLaneData, LaneId,
MessageNonce, UnrewardedRelayersState,
};
use bridge_runtime_common::messages::{
source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
};
use codec::Encode;
use frame_support::weights::{Weight, WeightToFee};
use bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof;
use messages_relay::{
message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf},
message_lane_loop::{NoncesSubmitArtifacts, TargetClient, TargetClientState},
};
use num_traits::{Bounded, Zero};
use relay_substrate_client::{
AccountIdOf, AccountKeyPairOf, BalanceOf, BlockNumberOf, Chain, ChainWithMessages,
ChainWithTransactions, Client, Error as SubstrateError, HashOf, HeaderIdOf, IndexOf, SignParam,
TransactionEra, TransactionTracker, UnsignedTransaction, WeightToFeeOf,
AccountIdOf, AccountKeyPairOf, BalanceOf, BlockNumberOf, Chain, ChainWithMessages, Client,
Error as SubstrateError, HashOf, HeaderIdOf, IndexOf, SignParam, TransactionEra,
TransactionTracker, UnsignedTransaction,
};
use relay_utils::{relay_loop::Client as RelayClient, HeaderId};
use sp_core::{Bytes, Pair};
use sp_runtime::{traits::Saturating, FixedPointNumber, FixedU128};
use relay_utils::relay_loop::Client as RelayClient;
use sp_core::Pair;
use std::{collections::VecDeque, convert::TryFrom, ops::RangeInclusive};
/// Message receiving proof returned by the target Substrate node.
@@ -63,7 +56,6 @@ pub struct SubstrateMessagesTarget<P: SubstrateMessageLane> {
lane_id: LaneId,
relayer_id_at_source: AccountIdOf<P::SourceChain>,
transaction_params: TransactionParams<AccountKeyPairOf<P::TargetChain>>,
metric_values: StandaloneMessagesMetrics<P::SourceChain, P::TargetChain>,
source_to_target_headers_relay: Option<Arc<dyn OnDemandRelay<BlockNumberOf<P::SourceChain>>>>,
}
@@ -75,7 +67,6 @@ impl<P: SubstrateMessageLane> SubstrateMessagesTarget<P> {
lane_id: LaneId,
relayer_id_at_source: AccountIdOf<P::SourceChain>,
transaction_params: TransactionParams<AccountKeyPairOf<P::TargetChain>>,
metric_values: StandaloneMessagesMetrics<P::SourceChain, P::TargetChain>,
source_to_target_headers_relay: Option<
Arc<dyn OnDemandRelay<BlockNumberOf<P::SourceChain>>>,
>,
@@ -86,7 +77,6 @@ impl<P: SubstrateMessageLane> SubstrateMessagesTarget<P> {
lane_id,
relayer_id_at_source,
transaction_params,
metric_values,
source_to_target_headers_relay,
}
}
@@ -121,7 +111,6 @@ impl<P: SubstrateMessageLane> Clone for SubstrateMessagesTarget<P> {
lane_id: self.lane_id,
relayer_id_at_source: self.relayer_id_at_source.clone(),
transaction_params: self.transaction_params.clone(),
metric_values: self.metric_values.clone(),
source_to_target_headers_relay: self.source_to_target_headers_relay.clone(),
}
}
@@ -283,142 +272,6 @@ where
source_to_target_headers_relay.require_more_headers(id.0).await;
}
}
async fn estimate_delivery_transaction_in_source_tokens(
&self,
nonces: RangeInclusive<MessageNonce>,
total_prepaid_nonces: MessageNonce,
total_dispatch_weight: Weight,
total_size: u32,
) -> Result<<MessageLaneAdapter<P> as MessageLane>::SourceChainBalance, SubstrateError> {
let conversion_rate =
self.metric_values.target_to_source_conversion_rate().await.ok_or_else(|| {
SubstrateError::Custom(format!(
"Failed to compute conversion rate from {} to {}",
P::TargetChain::NAME,
P::SourceChain::NAME,
))
})?;
let (spec_version, transaction_version) =
self.target_client.simple_runtime_version().await?;
// Prepare 'dummy' delivery transaction - we only care about its length and dispatch weight.
let delivery_tx = P::TargetChain::sign_transaction(
SignParam {
spec_version,
transaction_version,
genesis_hash: Default::default(),
signer: self.transaction_params.signer.clone(),
},
make_messages_delivery_transaction::<P>(
&self.transaction_params,
HeaderId(Default::default(), Default::default()),
Zero::zero(),
self.relayer_id_at_source.clone(),
nonces.clone(),
prepare_dummy_messages_proof::<P::SourceChain>(
nonces.clone(),
total_dispatch_weight,
total_size,
),
false,
)?,
)?
.encode();
let delivery_tx_fee = self.target_client.estimate_extrinsic_fee(Bytes(delivery_tx)).await?;
let inclusion_fee_in_target_tokens = delivery_tx_fee.inclusion_fee();
// The pre-dispatch cost of delivery transaction includes additional fee to cover dispatch
// fee payment (Currency::transfer in regular deployment). But if message dispatch has
// already been paid at the Source chain, the delivery transaction will refund relayer with
// this additional cost. But `estimate_extrinsic_fee` obviously just returns pre-dispatch
// cost of the transaction. So if transaction delivers prepaid message, then it may happen
// that pre-dispatch cost is larger than reward and `Rational` relayer will refuse to
// deliver this message.
//
// The most obvious solution would be to deduct total weight of dispatch fee payments from
// the `total_dispatch_weight` and use regular `estimate_extrinsic_fee` call. But what if
// `total_dispatch_weight` is less than total dispatch fee payments weight? Weight is
// strictly positive, so we can't use this option.
//
// Instead we'll be directly using `WeightToFee` and `NextFeeMultiplier` of the Target
// chain. This requires more knowledge of the Target chain, but seems there's no better way
// to solve this now.
let expected_refund_in_target_tokens = if total_prepaid_nonces != 0 {
const WEIGHT_DIFFERENCE: Weight = Weight::from_ref_time(100);
let (spec_version, transaction_version) =
self.target_client.simple_runtime_version().await?;
let larger_dispatch_weight = total_dispatch_weight.saturating_add(WEIGHT_DIFFERENCE);
let dummy_tx = P::TargetChain::sign_transaction(
SignParam {
spec_version,
transaction_version,
genesis_hash: Default::default(),
signer: self.transaction_params.signer.clone(),
},
make_messages_delivery_transaction::<P>(
&self.transaction_params,
HeaderId(Default::default(), Default::default()),
Zero::zero(),
self.relayer_id_at_source.clone(),
nonces.clone(),
prepare_dummy_messages_proof::<P::SourceChain>(
nonces.clone(),
larger_dispatch_weight,
total_size,
),
false,
)?,
)?
.encode();
let larger_delivery_tx_fee =
self.target_client.estimate_extrinsic_fee(Bytes(dummy_tx)).await?;
compute_prepaid_messages_refund::<P::TargetChain>(
total_prepaid_nonces,
compute_fee_multiplier::<P::TargetChain>(
delivery_tx_fee.adjusted_weight_fee,
total_dispatch_weight,
larger_delivery_tx_fee.adjusted_weight_fee,
larger_dispatch_weight,
),
)
} else {
Zero::zero()
};
let delivery_fee_in_source_tokens =
convert_target_tokens_to_source_tokens::<P::SourceChain, P::TargetChain>(
FixedU128::from_float(conversion_rate),
inclusion_fee_in_target_tokens.saturating_sub(expected_refund_in_target_tokens),
);
log::trace!(
target: "bridge",
"Estimated {} -> {} messages delivery transaction.\n\t\
Total nonces: {:?}\n\t\
Prepaid messages: {}\n\t\
Total messages size: {}\n\t\
Total messages dispatch weight: {}\n\t\
Inclusion fee (in {1} tokens): {:?}\n\t\
Expected refund (in {1} tokens): {:?}\n\t\
{1} -> {0} conversion rate: {:?}\n\t\
Expected delivery tx fee (in {0} tokens): {:?}",
P::SourceChain::NAME,
P::TargetChain::NAME,
nonces,
total_prepaid_nonces,
total_size,
total_dispatch_weight,
inclusion_fee_in_target_tokens,
expected_refund_in_target_tokens,
conversion_rate,
delivery_fee_in_source_tokens,
);
Ok(delivery_fee_in_source_tokens)
}
}
/// Make messages delivery transaction from given proof.
@@ -443,153 +296,3 @@ fn make_messages_delivery_transaction<P: SubstrateMessageLane>(
Ok(UnsignedTransaction::new(call.into(), transaction_nonce)
.era(TransactionEra::new(target_best_block_id, target_transaction_params.mortality)))
}
/// Prepare 'dummy' messages proof that will compose the delivery transaction.
///
/// We don't care about proof actually being the valid proof, because its validity doesn't
/// affect the call weight - we only care about its size.
fn prepare_dummy_messages_proof<SC: Chain>(
nonces: RangeInclusive<MessageNonce>,
total_dispatch_weight: Weight,
total_size: u32,
) -> SubstrateMessagesProof<SC> {
(
total_dispatch_weight,
FromBridgedChainMessagesProof {
bridged_header_hash: Default::default(),
storage_proof: vec![vec![
0;
SC::STORAGE_PROOF_OVERHEAD.saturating_add(total_size) as usize
]],
lane: Default::default(),
nonces_start: *nonces.start(),
nonces_end: *nonces.end(),
},
)
}
/// Given delivery transaction fee in target chain tokens and conversion rate to the source
/// chain tokens, compute transaction cost in source chain tokens.
fn convert_target_tokens_to_source_tokens<SC: Chain, TC: Chain>(
target_to_source_conversion_rate: FixedU128,
target_transaction_fee: TC::Balance,
) -> SC::Balance
where
SC::Balance: TryFrom<TC::Balance>,
{
SC::Balance::try_from(
target_to_source_conversion_rate.saturating_mul_int(target_transaction_fee),
)
.unwrap_or_else(|_| SC::Balance::max_value())
}
/// Compute fee multiplier that is used by the chain, given a couple of fees for transactions
/// that are only differ in dispatch weights.
///
/// This function assumes that standard transaction payment pallet is used by the chain.
/// The only fee component that depends on dispatch weight is the `adjusted_weight_fee`.
///
/// **WARNING**: this functions will only be accurate if weight-to-fee conversion function
/// is linear. For non-linear polynomials the error will grow with `weight_difference` growth.
/// So better to use smaller differences.
fn compute_fee_multiplier<C: ChainWithMessages>(
smaller_adjusted_weight_fee: BalanceOf<C>,
smaller_tx_weight: Weight,
larger_adjusted_weight_fee: BalanceOf<C>,
larger_tx_weight: Weight,
) -> FixedU128 {
let adjusted_weight_fee_difference =
larger_adjusted_weight_fee.saturating_sub(smaller_adjusted_weight_fee);
let smaller_tx_unadjusted_weight_fee = WeightToFeeOf::<C>::weight_to_fee(&smaller_tx_weight);
let larger_tx_unadjusted_weight_fee = WeightToFeeOf::<C>::weight_to_fee(&larger_tx_weight);
FixedU128::saturating_from_rational(
adjusted_weight_fee_difference,
larger_tx_unadjusted_weight_fee.saturating_sub(smaller_tx_unadjusted_weight_fee),
)
}
/// Compute fee that will be refunded to the relayer because dispatch of `total_prepaid_nonces`
/// messages has been paid at the source chain.
fn compute_prepaid_messages_refund<C: ChainWithMessages>(
total_prepaid_nonces: MessageNonce,
fee_multiplier: FixedU128,
) -> BalanceOf<C> {
fee_multiplier.saturating_mul_int(WeightToFeeOf::<C>::weight_to_fee(
&C::PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN.saturating_mul(total_prepaid_nonces),
))
}
#[cfg(test)]
mod tests {
use super::*;
use relay_rialto_client::Rialto;
use relay_rococo_client::Rococo;
use relay_wococo_client::Wococo;
#[test]
fn prepare_dummy_messages_proof_works() {
const DISPATCH_WEIGHT: Weight = Weight::from_ref_time(1_000_000);
const SIZE: u32 = 1_000;
let dummy_proof = prepare_dummy_messages_proof::<Rococo>(1..=10, DISPATCH_WEIGHT, SIZE);
assert_eq!(dummy_proof.0, DISPATCH_WEIGHT);
assert!(
dummy_proof.1.encode().len() as u32 > SIZE,
"Expected proof size at least {}. Got: {}",
SIZE,
dummy_proof.1.encode().len(),
);
}
#[test]
fn convert_target_tokens_to_source_tokens_works() {
assert_eq!(
convert_target_tokens_to_source_tokens::<Rococo, Wococo>((150, 100).into(), 1_000),
1_500
);
assert_eq!(
convert_target_tokens_to_source_tokens::<Rococo, Wococo>((50, 100).into(), 1_000),
500
);
assert_eq!(
convert_target_tokens_to_source_tokens::<Rococo, Wococo>((100, 100).into(), 1_000),
1_000
);
}
#[test]
fn compute_fee_multiplier_returns_sane_results() {
let multiplier: FixedU128 =
bp_rialto::WeightToFee::weight_to_fee(&Weight::from_ref_time(1)).into();
let smaller_weight = 1_000_000;
let smaller_adjusted_weight_fee = multiplier.saturating_mul_int(
WeightToFeeOf::<Rialto>::weight_to_fee(&Weight::from_ref_time(smaller_weight)),
);
let larger_weight = smaller_weight + 200_000;
let larger_adjusted_weight_fee = multiplier.saturating_mul_int(
WeightToFeeOf::<Rialto>::weight_to_fee(&Weight::from_ref_time(larger_weight)),
);
assert_eq!(
compute_fee_multiplier::<Rialto>(
smaller_adjusted_weight_fee,
Weight::from_ref_time(smaller_weight),
larger_adjusted_weight_fee,
Weight::from_ref_time(larger_weight),
),
multiplier,
);
}
#[test]
fn compute_prepaid_messages_refund_returns_sane_results() {
assert!(
compute_prepaid_messages_refund::<Rialto>(
10,
FixedU128::saturating_from_rational(110, 100),
) > Rialto::PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN
.saturating_mul(10u64)
.ref_time() as _
);
}
}
+1 -1
View File
@@ -29,9 +29,9 @@ mod metrics;
pub mod message_lane;
pub mod message_lane_loop;
pub mod relay_strategy;
mod message_race_delivery;
mod message_race_limits;
mod message_race_loop;
mod message_race_receiving;
mod message_race_strategy;
@@ -30,7 +30,6 @@ use async_trait::async_trait;
use futures::{channel::mpsc::unbounded, future::FutureExt, stream::StreamExt};
use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState, Weight};
use bp_runtime::messages::DispatchFeePayment;
use relay_utils::{
interval, metrics::MetricsParams, process_future_result, relay_loop::Client as RelayClient,
retry_backoff, FailedClient, TransactionTracker,
@@ -41,12 +40,11 @@ use crate::{
message_race_delivery::run as run_message_delivery_race,
message_race_receiving::run as run_message_receiving_race,
metrics::MessageLaneLoopMetrics,
relay_strategy::RelayStrategy,
};
/// Message lane loop configuration params.
#[derive(Debug, Clone)]
pub struct Params<Strategy: RelayStrategy> {
pub struct Params {
/// Id of lane this loop is servicing.
pub lane: LaneId,
/// Interval at which we ask target node about its updates.
@@ -56,22 +54,12 @@ pub struct Params<Strategy: RelayStrategy> {
/// Delay between moments when connection error happens and our reconnect attempt.
pub reconnect_delay: Duration,
/// Message delivery race parameters.
pub delivery_params: MessageDeliveryParams<Strategy>,
}
/// Relayer operating mode.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RelayerMode {
/// The relayer doesn't care about rewards.
Altruistic,
/// The relayer will deliver all messages and confirmations as long as he's not losing any
/// funds.
Rational,
pub delivery_params: MessageDeliveryParams,
}
/// Message delivery race parameters.
#[derive(Debug, Clone)]
pub struct MessageDeliveryParams<Strategy: RelayStrategy> {
pub struct MessageDeliveryParams {
/// Maximal number of unconfirmed relayer entries at the inbound lane. If there's that number
/// of entries in the `InboundLaneData::relayers` set, all new messages will be rejected until
/// reward payment will be proved (by including outbound lane state to the message delivery
@@ -87,8 +75,6 @@ pub struct MessageDeliveryParams<Strategy: RelayStrategy> {
pub max_messages_weight_in_single_batch: Weight,
/// Maximal cumulative size of relayed messages in single delivery transaction.
pub max_messages_size_in_single_batch: u32,
/// Relay strategy
pub relay_strategy: Strategy,
}
/// Message details.
@@ -100,8 +86,6 @@ pub struct MessageDetails<SourceChainBalance> {
pub size: u32,
/// The relayer reward paid in the source chain tokens.
pub reward: SourceChainBalance,
/// Where the fee for dispatching message is paid?
pub dispatch_fee_payment: DispatchFeePayment,
}
/// Messages details map.
@@ -173,9 +157,6 @@ pub trait SourceClient<P: MessageLane>: RelayClient {
/// We need given finalized target header on source to continue synchronization.
async fn require_target_header_on_source(&self, id: TargetHeaderIdOf<P>);
/// Estimate cost of single message confirmation transaction in source chain tokens.
async fn estimate_confirmation_transaction(&self) -> P::SourceChainBalance;
}
/// Target client trait.
@@ -221,18 +202,6 @@ pub trait TargetClient<P: MessageLane>: RelayClient {
/// We need given finalized source header on target to continue synchronization.
async fn require_source_header_on_target(&self, id: SourceHeaderIdOf<P>);
/// Estimate cost of messages delivery transaction in source chain tokens.
///
/// Please keep in mind that the returned cost must be converted to the source chain
/// tokens, even though the transaction fee will be paid in the target chain tokens.
async fn estimate_delivery_transaction_in_source_tokens(
&self,
nonces: RangeInclusive<MessageNonce>,
total_prepaid_nonces: MessageNonce,
total_dispatch_weight: Weight,
total_size: u32,
) -> Result<P::SourceChainBalance, Self::Error>;
}
/// State of the client.
@@ -272,8 +241,8 @@ pub fn metrics_prefix<P: MessageLane>(lane: &LaneId) -> String {
}
/// Run message lane service loop.
pub async fn run<P: MessageLane, Strategy: RelayStrategy>(
params: Params<Strategy>,
pub async fn run<P: MessageLane>(
params: Params,
source_client: impl SourceClient<P>,
target_client: impl TargetClient<P>,
metrics_params: MetricsParams,
@@ -283,11 +252,7 @@ pub async fn run<P: MessageLane, Strategy: RelayStrategy>(
relay_utils::relay_loop(source_client, target_client)
.reconnect_delay(params.reconnect_delay)
.with_metrics(metrics_params)
.loop_metric(MessageLaneLoopMetrics::new(
Some(&metrics_prefix::<P>(&params.lane)),
P::SOURCE_NAME,
P::TARGET_NAME,
)?)?
.loop_metric(MessageLaneLoopMetrics::new(Some(&metrics_prefix::<P>(&params.lane)))?)?
.expose()
.await?
.run(metrics_prefix::<P>(&params.lane), move |source_client, target_client, metrics| {
@@ -304,13 +269,8 @@ pub async fn run<P: MessageLane, Strategy: RelayStrategy>(
/// Run one-way message delivery loop until connection with target or source node is lost, or exit
/// signal is received.
async fn run_until_connection_lost<
P: MessageLane,
Strategy: RelayStrategy,
SC: SourceClient<P>,
TC: TargetClient<P>,
>(
params: Params<Strategy>,
async fn run_until_connection_lost<P: MessageLane, SC: SourceClient<P>, TC: TargetClient<P>>(
params: Params,
source_client: SC,
target_client: TC,
metrics_msg: Option<MessageLaneLoopMetrics>,
@@ -477,17 +437,12 @@ pub(crate) mod tests {
use relay_utils::{HeaderId, MaybeConnectionError, TrackedTransactionStatus};
use crate::relay_strategy::AltruisticStrategy;
use super::*;
pub fn header_id(number: TestSourceHeaderNumber) -> TestSourceHeaderId {
HeaderId(number, number)
}
pub const CONFIRMATION_TRANSACTION_COST: TestSourceChainBalance = 1;
pub const BASE_MESSAGE_DELIVERY_TRANSACTION_COST: TestSourceChainBalance = 1;
pub type TestSourceChainBalance = u64;
pub type TestSourceHeaderId = HeaderId<TestSourceHeaderNumber, TestSourceHeaderHash>;
pub type TestTargetHeaderId = HeaderId<TestTargetHeaderNumber, TestTargetHeaderHash>;
@@ -681,7 +636,6 @@ pub(crate) mod tests {
dispatch_weight: Weight::from_ref_time(1),
size: 1,
reward: 1,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
},
)
})
@@ -737,10 +691,6 @@ pub(crate) mod tests {
(self.tick)(&mut data);
(self.post_tick)(&mut data);
}
async fn estimate_confirmation_transaction(&self) -> TestSourceChainBalance {
CONFIRMATION_TRANSACTION_COST
}
}
#[derive(Clone)]
@@ -871,20 +821,6 @@ pub(crate) mod tests {
(self.tick)(&mut data);
(self.post_tick)(&mut data);
}
async fn estimate_delivery_transaction_in_source_tokens(
&self,
nonces: RangeInclusive<MessageNonce>,
_total_prepaid_nonces: MessageNonce,
total_dispatch_weight: Weight,
total_size: u32,
) -> Result<TestSourceChainBalance, TestError> {
Ok((Weight::from_ref_time(BASE_MESSAGE_DELIVERY_TRANSACTION_COST) *
(nonces.end() - nonces.start() + 1) +
total_dispatch_weight +
Weight::from_ref_time(total_size as u64))
.ref_time())
}
}
fn run_loop_test(
@@ -920,7 +856,6 @@ pub(crate) mod tests {
max_messages_in_single_batch: 4,
max_messages_weight_in_single_batch: Weight::from_ref_time(4),
max_messages_size_in_single_batch: 4,
relay_strategy: AltruisticStrategy,
},
},
source_client,
@@ -28,23 +28,23 @@ use crate::{
SourceClient as MessageLaneSourceClient, SourceClientState,
TargetClient as MessageLaneTargetClient, TargetClientState,
},
message_race_limits::{MessageRaceLimits, RelayMessagesBatchReference},
message_race_loop::{
MessageRace, NoncesRange, RaceState, RaceStrategy, SourceClient, SourceClientNonces,
TargetClient, TargetClientNonces,
},
message_race_strategy::BasicStrategy,
metrics::MessageLaneLoopMetrics,
relay_strategy::{EnforcementStrategy, RelayMessagesBatchReference, RelayStrategy},
};
/// Run message delivery race.
pub async fn run<P: MessageLane, Strategy: RelayStrategy>(
pub async fn run<P: MessageLane>(
source_client: impl MessageLaneSourceClient<P>,
source_state_updates: impl FusedStream<Item = SourceClientState<P>>,
target_client: impl MessageLaneTargetClient<P>,
target_state_updates: impl FusedStream<Item = TargetClientState<P>>,
metrics_msg: Option<MessageLaneLoopMetrics>,
params: MessageDeliveryParams<Strategy>,
params: MessageDeliveryParams,
) -> Result<(), FailedClient> {
crate::message_race_loop::run(
MessageDeliveryRaceSource {
@@ -59,7 +59,7 @@ pub async fn run<P: MessageLane, Strategy: RelayStrategy>(
_phantom: Default::default(),
},
target_state_updates,
MessageDeliveryStrategy::<P, Strategy, _, _> {
MessageDeliveryStrategy::<P, _, _> {
lane_source_client: source_client,
lane_target_client: target_client,
max_unrewarded_relayer_entries_at_target: params
@@ -68,7 +68,6 @@ pub async fn run<P: MessageLane, Strategy: RelayStrategy>(
max_messages_in_single_batch: params.max_messages_in_single_batch,
max_messages_weight_in_single_batch: params.max_messages_weight_in_single_batch,
max_messages_size_in_single_batch: params.max_messages_size_in_single_batch,
relay_strategy: params.relay_strategy,
latest_confirmed_nonces_at_source: VecDeque::new(),
target_nonces: None,
strategy: BasicStrategy::new(),
@@ -231,7 +230,7 @@ struct DeliveryRaceTargetNoncesData {
}
/// Messages delivery strategy.
struct MessageDeliveryStrategy<P: MessageLane, Strategy: RelayStrategy, SC, TC> {
struct MessageDeliveryStrategy<P: MessageLane, SC, TC> {
/// The client that is connected to the message lane source node.
lane_source_client: SC,
/// The client that is connected to the message lane target node.
@@ -246,8 +245,6 @@ struct MessageDeliveryStrategy<P: MessageLane, Strategy: RelayStrategy, SC, TC>
max_messages_weight_in_single_batch: Weight,
/// Maximal messages size in the single delivery transaction.
max_messages_size_in_single_batch: u32,
/// Relayer operating mode.
relay_strategy: Strategy,
/// Latest confirmed nonces at the source client + the header id where we have first met this
/// nonce.
latest_confirmed_nonces_at_source: VecDeque<(SourceHeaderIdOf<P>, MessageNonce)>,
@@ -268,9 +265,7 @@ type MessageDeliveryStrategyBase<P> = BasicStrategy<
<P as MessageLane>::MessagesProof,
>;
impl<P: MessageLane, Strategy: RelayStrategy, SC, TC> std::fmt::Debug
for MessageDeliveryStrategy<P, Strategy, SC, TC>
{
impl<P: MessageLane, SC, TC> std::fmt::Debug for MessageDeliveryStrategy<P, SC, TC> {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
fmt.debug_struct("MessageDeliveryStrategy")
.field(
@@ -288,7 +283,7 @@ impl<P: MessageLane, Strategy: RelayStrategy, SC, TC> std::fmt::Debug
}
}
impl<P: MessageLane, Strategy: RelayStrategy, SC, TC> MessageDeliveryStrategy<P, Strategy, SC, TC> {
impl<P: MessageLane, SC, TC> MessageDeliveryStrategy<P, SC, TC> {
/// Returns total weight of all undelivered messages.
fn total_queued_dispatch_weight(&self) -> Weight {
self.strategy
@@ -300,9 +295,8 @@ impl<P: MessageLane, Strategy: RelayStrategy, SC, TC> MessageDeliveryStrategy<P,
}
#[async_trait]
impl<P, Strategy: RelayStrategy, SC, TC>
RaceStrategy<SourceHeaderIdOf<P>, TargetHeaderIdOf<P>, P::MessagesProof>
for MessageDeliveryStrategy<P, Strategy, SC, TC>
impl<P, SC, TC> RaceStrategy<SourceHeaderIdOf<P>, TargetHeaderIdOf<P>, P::MessagesProof>
for MessageDeliveryStrategy<P, SC, TC>
where
P: MessageLane,
SC: MessageLaneSourceClient<P>,
@@ -524,8 +518,7 @@ where
metrics: self.metrics_msg.clone(),
};
let mut strategy = EnforcementStrategy::new(self.relay_strategy.clone());
let range_end = strategy.decide(reference).await?;
let range_end = MessageRaceLimits::decide(reference).await?;
let range_begin = source_queue[0].1.begin();
let selected_nonces = range_begin..=range_end;
@@ -562,38 +555,27 @@ impl<SourceChainBalance: std::fmt::Debug> NoncesRange for MessageDetailsMap<Sour
#[cfg(test)]
mod tests {
use bp_runtime::messages::DispatchFeePayment;
use crate::{
message_lane_loop::{
tests::{
header_id, TestMessageLane, TestMessagesProof, TestSourceChainBalance,
TestSourceClient, TestSourceHeaderId, TestTargetClient, TestTargetHeaderId,
BASE_MESSAGE_DELIVERY_TRANSACTION_COST, CONFIRMATION_TRANSACTION_COST,
},
MessageDetails, RelayerMode,
use crate::message_lane_loop::{
tests::{
header_id, TestMessageLane, TestMessagesProof, TestSourceChainBalance,
TestSourceClient, TestSourceHeaderId, TestTargetClient, TestTargetHeaderId,
},
relay_strategy::MixStrategy,
MessageDetails,
};
use super::*;
const DEFAULT_DISPATCH_WEIGHT: Weight = Weight::from_ref_time(1);
const DEFAULT_SIZE: u32 = 1;
const DEFAULT_REWARD: TestSourceChainBalance = CONFIRMATION_TRANSACTION_COST +
BASE_MESSAGE_DELIVERY_TRANSACTION_COST +
DEFAULT_DISPATCH_WEIGHT.ref_time() +
(DEFAULT_SIZE as TestSourceChainBalance);
type TestRaceState = RaceState<TestSourceHeaderId, TestTargetHeaderId, TestMessagesProof>;
type TestStrategy =
MessageDeliveryStrategy<TestMessageLane, MixStrategy, TestSourceClient, TestTargetClient>;
MessageDeliveryStrategy<TestMessageLane, TestSourceClient, TestTargetClient>;
fn source_nonces(
new_nonces: RangeInclusive<MessageNonce>,
confirmed_nonce: MessageNonce,
reward: TestSourceChainBalance,
dispatch_fee_payment: DispatchFeePayment,
) -> SourceClientNonces<MessageDetailsMap<TestSourceChainBalance>> {
SourceClientNonces {
new_nonces: new_nonces
@@ -605,7 +587,6 @@ mod tests {
dispatch_weight: DEFAULT_DISPATCH_WEIGHT,
size: DEFAULT_SIZE,
reward,
dispatch_fee_payment,
},
)
})
@@ -648,13 +629,11 @@ mod tests {
},
}),
strategy: BasicStrategy::new(),
relay_strategy: MixStrategy::new(RelayerMode::Altruistic),
};
race_strategy.strategy.source_nonces_updated(
header_id(1),
source_nonces(20..=23, 19, DEFAULT_REWARD, DispatchFeePayment::AtSourceChain),
);
race_strategy
.strategy
.source_nonces_updated(header_id(1), source_nonces(20..=23, 19, 0));
let target_nonces = TargetClientNonces { latest_nonce: 19, nonces_data: () };
race_strategy
@@ -687,7 +666,6 @@ mod tests {
dispatch_weight: Weight::from_ref_time(idx),
size: idx as _,
reward: idx as _,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
},
)
})
@@ -978,83 +956,6 @@ mod tests {
assert_eq!(strategy.required_source_header_at_target(&header_id(1)), Some(header_id(2)));
}
#[async_std::test]
async fn rational_relayer_is_delivering_messages_if_cost_is_equal_to_reward() {
let (state, mut strategy) = prepare_strategy();
strategy.relay_strategy = MixStrategy::new(RelayerMode::Rational);
// so now we have:
// - 20..=23 with reward = cost
// => strategy shall select all 20..=23
assert_eq!(
strategy.select_nonces_to_deliver(state).await,
Some(((20..=23), proof_parameters(false, 4)))
);
}
#[async_std::test]
async fn rational_relayer_is_not_delivering_messages_if_cost_is_larger_than_reward() {
let (mut state, mut strategy) = prepare_strategy();
let nonces = source_nonces(
24..=25,
19,
DEFAULT_REWARD - BASE_MESSAGE_DELIVERY_TRANSACTION_COST,
DispatchFeePayment::AtSourceChain,
);
strategy.strategy.source_nonces_updated(header_id(2), nonces);
state.best_finalized_source_header_id_at_best_target = Some(header_id(2));
strategy.relay_strategy = MixStrategy::new(RelayerMode::Rational);
// so now we have:
// - 20..=23 with reward = cost
// - 24..=25 with reward less than cost
// => strategy shall only select 20..=23
assert_eq!(
strategy.select_nonces_to_deliver(state).await,
Some(((20..=23), proof_parameters(false, 4)))
);
}
#[async_std::test]
async fn rational_relayer_is_delivering_unpaid_messages() {
async fn test_with_dispatch_fee_payment(
dispatch_fee_payment: DispatchFeePayment,
) -> Option<(RangeInclusive<MessageNonce>, MessageProofParameters)> {
let (mut state, mut strategy) = prepare_strategy();
let nonces = source_nonces(
24..=24,
19,
DEFAULT_REWARD - DEFAULT_DISPATCH_WEIGHT.ref_time(),
dispatch_fee_payment,
);
strategy.strategy.source_nonces_updated(header_id(2), nonces);
state.best_finalized_source_header_id_at_best_target = Some(header_id(2));
strategy.max_unrewarded_relayer_entries_at_target = 100;
strategy.max_unconfirmed_nonces_at_target = 100;
strategy.max_messages_in_single_batch = 100;
strategy.max_messages_weight_in_single_batch = Weight::from_ref_time(100);
strategy.max_messages_size_in_single_batch = 100;
strategy.relay_strategy = MixStrategy::new(RelayerMode::Rational);
// so now we have:
// - 20..=23 with reward = cost
// - 24..=24 with reward less than cost, but we're deducting `DEFAULT_DISPATCH_WEIGHT`
// from the cost, so it should be fine;
// => when MSG#24 fee is paid at the target chain, strategy shall select all 20..=24
// => when MSG#25 fee is paid at the source chain, strategy shall only select 20..=23
strategy.select_nonces_to_deliver(state).await
}
assert_eq!(
test_with_dispatch_fee_payment(DispatchFeePayment::AtTargetChain).await,
Some(((20..=24), proof_parameters(false, 5)))
);
assert_eq!(
test_with_dispatch_fee_payment(DispatchFeePayment::AtSourceChain).await,
Some(((20..=23), proof_parameters(false, 4)))
);
}
#[async_std::test]
async fn relayer_uses_flattened_view_of_the_source_queue_to_select_nonces() {
// Real scenario that has happened on test deployments:
@@ -1066,7 +967,7 @@ mod tests {
// This was happening because selector (`select_nonces_for_delivery_transaction`) has been
// called for every `source_queue` entry separately without preserving any context.
let (mut state, mut strategy) = prepare_strategy();
let nonces = source_nonces(24..=25, 19, DEFAULT_REWARD, DispatchFeePayment::AtSourceChain);
let nonces = source_nonces(24..=25, 19, 0);
strategy.strategy.source_nonces_updated(header_id(2), nonces);
strategy.max_unrewarded_relayer_entries_at_target = 100;
strategy.max_unconfirmed_nonces_at_target = 100;
@@ -17,43 +17,88 @@
//! enforcement strategy
use num_traits::Zero;
use std::ops::Range;
use bp_messages::{MessageNonce, Weight};
use bp_runtime::messages::DispatchFeePayment;
use crate::{
message_lane::MessageLane,
message_lane_loop::{
MessageDetails, SourceClient as MessageLaneSourceClient,
MessageDetails, MessageDetailsMap, SourceClient as MessageLaneSourceClient,
TargetClient as MessageLaneTargetClient,
},
message_race_loop::NoncesRange,
relay_strategy::{RelayMessagesBatchReference, RelayReference, RelayStrategy},
message_race_strategy::SourceRangesQueue,
metrics::MessageLaneLoopMetrics,
};
/// Do hard check and run soft check strategy
/// Reference data for participating in relay
pub struct RelayReference<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
> {
/// The client that is connected to the message lane source node.
pub lane_source_client: SourceClient,
/// The client that is connected to the message lane target node.
pub lane_target_client: TargetClient,
/// Metrics reference.
pub metrics: Option<MessageLaneLoopMetrics>,
/// Messages size summary
pub selected_size: u32,
/// Hard check begin nonce
pub hard_selected_begin_nonce: MessageNonce,
/// Index by all ready nonces
pub index: usize,
/// Current nonce
pub nonce: MessageNonce,
/// Current nonce details
pub details: MessageDetails<P::SourceChainBalance>,
}
/// Relay reference data
pub struct RelayMessagesBatchReference<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
> {
/// Maximal number of relayed messages in single delivery transaction.
pub max_messages_in_this_batch: MessageNonce,
/// Maximal cumulative dispatch weight of relayed messages in single delivery transaction.
pub max_messages_weight_in_single_batch: Weight,
/// Maximal cumulative size of relayed messages in single delivery transaction.
pub max_messages_size_in_single_batch: u32,
/// The client that is connected to the message lane source node.
pub lane_source_client: SourceClient,
/// The client that is connected to the message lane target node.
pub lane_target_client: TargetClient,
/// Metrics reference.
pub metrics: Option<MessageLaneLoopMetrics>,
/// Source queue.
pub nonces_queue: SourceRangesQueue<
P::SourceHeaderHash,
P::SourceHeaderNumber,
MessageDetailsMap<P::SourceChainBalance>,
>,
/// Source queue range
pub nonces_queue_range: Range<usize>,
}
/// Limits of the message race transactions.
#[derive(Clone)]
pub struct EnforcementStrategy<Strategy: RelayStrategy> {
strategy: Strategy,
}
pub struct MessageRaceLimits;
impl<Strategy: RelayStrategy> EnforcementStrategy<Strategy> {
pub fn new(strategy: Strategy) -> Self {
Self { strategy }
}
}
impl<Strategy: RelayStrategy> EnforcementStrategy<Strategy> {
impl MessageRaceLimits {
pub async fn decide<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
>(
&mut self,
reference: RelayMessagesBatchReference<P, SourceClient, TargetClient>,
) -> Option<MessageNonce> {
let mut hard_selected_count = 0;
let mut soft_selected_count = 0;
let mut selected_weight = Weight::zero();
let mut selected_count: MessageNonce = 0;
@@ -67,17 +112,9 @@ impl<Strategy: RelayStrategy> EnforcementStrategy<Strategy> {
lane_target_client: reference.lane_target_client.clone(),
metrics: reference.metrics.clone(),
selected_reward: P::SourceChainBalance::zero(),
selected_cost: P::SourceChainBalance::zero(),
selected_size: 0,
total_reward: P::SourceChainBalance::zero(),
total_confirmations_cost: P::SourceChainBalance::zero(),
total_cost: P::SourceChainBalance::zero(),
hard_selected_begin_nonce,
selected_prepaid_nonces: 0,
selected_unpaid_weight: Weight::zero(),
index: 0,
nonce: 0,
@@ -85,7 +122,6 @@ impl<Strategy: RelayStrategy> EnforcementStrategy<Strategy> {
dispatch_weight: Weight::zero(),
size: 0,
reward: P::SourceChainBalance::zero(),
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
},
};
@@ -148,74 +184,14 @@ impl<Strategy: RelayStrategy> EnforcementStrategy<Strategy> {
}
relay_reference.selected_size = new_selected_size;
// If dispatch fee has been paid at the source chain, it means that it is **relayer**
// who's paying for dispatch at the target chain AND reward must cover this dispatch
// fee.
//
// If dispatch fee is paid at the target chain, it means that it'll be withdrawn from
// the dispatch origin account AND reward is not covering this fee.
//
// So in the latter case we're not adding the dispatch weight to the delivery
// transaction weight.
let mut new_selected_prepaid_nonces = relay_reference.selected_prepaid_nonces;
let new_selected_unpaid_weight = match details.dispatch_fee_payment {
DispatchFeePayment::AtSourceChain => {
new_selected_prepaid_nonces += 1;
relay_reference.selected_unpaid_weight.saturating_add(details.dispatch_weight)
},
DispatchFeePayment::AtTargetChain => relay_reference.selected_unpaid_weight,
};
relay_reference.selected_prepaid_nonces = new_selected_prepaid_nonces;
relay_reference.selected_unpaid_weight = new_selected_unpaid_weight;
// now the message has passed all 'strong' checks, and we CAN deliver it. But do we WANT
// to deliver it? It depends on the relayer strategy.
if self.strategy.decide(&mut relay_reference).await {
soft_selected_count = index + 1;
}
hard_selected_count = index + 1;
selected_weight = new_selected_weight;
selected_count = new_selected_count;
}
if hard_selected_count != soft_selected_count {
let hard_selected_end_nonce =
hard_selected_begin_nonce + hard_selected_count as MessageNonce - 1;
let soft_selected_begin_nonce = hard_selected_begin_nonce;
let soft_selected_end_nonce =
soft_selected_begin_nonce + soft_selected_count as MessageNonce - 1;
log::warn!(
target: "bridge",
"Relayer may deliver nonces [{:?}; {:?}], but because of its strategy it has selected \
nonces [{:?}; {:?}].",
hard_selected_begin_nonce,
hard_selected_end_nonce,
soft_selected_begin_nonce,
soft_selected_end_nonce,
);
hard_selected_count = soft_selected_count;
}
if hard_selected_count != 0 {
if relay_reference.selected_reward != P::SourceChainBalance::zero() &&
relay_reference.selected_cost != P::SourceChainBalance::zero()
{
log::trace!(
target: "bridge",
"Expected reward from delivering nonces [{:?}; {:?}] is: {:?} - {:?} = {:?}",
hard_selected_begin_nonce,
hard_selected_begin_nonce + hard_selected_count as MessageNonce - 1,
&relay_reference.selected_reward,
&relay_reference.selected_cost,
relay_reference.selected_reward - relay_reference.selected_cost,
);
}
let selected_max_nonce =
hard_selected_begin_nonce + hard_selected_count as MessageNonce - 1;
self.strategy.on_final_decision(&relay_reference);
Some(selected_max_nonce)
} else {
None
+2 -21
View File
@@ -24,7 +24,7 @@ use crate::{
use bp_messages::MessageNonce;
use finality_relay::SyncLoopMetrics;
use relay_utils::metrics::{
metric_name, register, Counter, GaugeVec, Metric, Opts, PrometheusError, Registry, U64,
metric_name, register, GaugeVec, Metric, Opts, PrometheusError, Registry, U64,
};
/// Message lane relay metrics.
@@ -39,17 +39,11 @@ pub struct MessageLaneLoopMetrics {
/// Lane state nonces: "source_latest_generated", "source_latest_confirmed",
/// "target_latest_received", "target_latest_confirmed".
lane_state_nonces: GaugeVec<U64>,
/// Count of unprofitable message delivery transactions that we have submitted so far.
unprofitable_delivery_transactions: Counter<U64>,
}
impl MessageLaneLoopMetrics {
/// Create and register messages loop metrics.
pub fn new(
prefix: Option<&str>,
source_name: &str,
target_name: &str,
) -> Result<Self, PrometheusError> {
pub fn new(prefix: Option<&str>) -> Result<Self, PrometheusError> {
Ok(MessageLaneLoopMetrics {
source_to_target_finality_metrics: SyncLoopMetrics::new(
prefix,
@@ -65,13 +59,6 @@ impl MessageLaneLoopMetrics {
Opts::new(metric_name(prefix, "lane_state_nonces"), "Nonces of the lane state"),
&["type"],
)?,
unprofitable_delivery_transactions: Counter::new(
metric_name(prefix, "unprofitable_delivery_transactions"),
format!(
"Count of unprofitable message delivery transactions from {} to {}",
source_name, target_name
),
)?,
})
}
@@ -140,11 +127,6 @@ impl MessageLaneLoopMetrics {
.with_label_values(&["target_latest_confirmed"])
.set(target_latest_confirmed_nonce);
}
/// Note unprofitable delivery transaction.
pub fn note_unprofitable_delivery_transactions(&self) {
self.unprofitable_delivery_transactions.inc()
}
}
impl Metric for MessageLaneLoopMetrics {
@@ -152,7 +134,6 @@ impl Metric for MessageLaneLoopMetrics {
self.source_to_target_finality_metrics.register(registry)?;
self.target_to_source_finality_metrics.register(registry)?;
register(self.lane_state_nonces.clone(), registry)?;
register(self.unprofitable_delivery_transactions.clone(), registry)?;
Ok(())
}
}
@@ -1,81 +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/>.
//! Altruistic relay strategy
use async_trait::async_trait;
use crate::{
message_lane::MessageLane,
message_lane_loop::{
SourceClient as MessageLaneSourceClient, TargetClient as MessageLaneTargetClient,
},
relay_strategy::{RelayReference, RelayStrategy},
};
/// The relayer doesn't care about rewards.
#[derive(Clone)]
pub struct AltruisticStrategy;
#[async_trait]
impl RelayStrategy for AltruisticStrategy {
async fn decide<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
>(
&mut self,
reference: &mut RelayReference<P, SourceClient, TargetClient>,
) -> bool {
// We don't care about costs and rewards, but we want to report unprofitable transactions.
if let Err(e) = reference.update_cost_and_reward().await {
log::debug!(
target: "bridge",
"Failed to update transaction cost and reward: {:?}. \
The `unprofitable_delivery_transactions` metric will be inaccurate",
e,
);
}
true
}
fn on_final_decision<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
>(
&self,
reference: &RelayReference<P, SourceClient, TargetClient>,
) {
if let Some(ref metrics) = reference.metrics {
if !reference.is_profitable() {
log::debug!(
target: "bridge",
"The relayer has submitted unprofitable {} -> {} message delivery transaction \
with {} messages: total cost = {:?}, total reward = {:?}",
P::SOURCE_NAME,
P::TARGET_NAME,
reference.index + 1,
reference.total_cost,
reference.total_reward,
);
metrics.note_unprofitable_delivery_transactions();
}
}
}
}
@@ -1,72 +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/>.
//! Adapter for using `enum RelayerMode` in a context which requires `RelayStrategy`.
use async_trait::async_trait;
use crate::{
message_lane::MessageLane,
message_lane_loop::{
RelayerMode, SourceClient as MessageLaneSourceClient,
TargetClient as MessageLaneTargetClient,
},
relay_strategy::{AltruisticStrategy, RationalStrategy, RelayReference, RelayStrategy},
};
/// `RelayerMode` adapter.
#[derive(Clone)]
pub struct MixStrategy {
relayer_mode: RelayerMode,
}
impl MixStrategy {
/// Create mix strategy instance
pub fn new(relayer_mode: RelayerMode) -> Self {
Self { relayer_mode }
}
}
#[async_trait]
impl RelayStrategy for MixStrategy {
async fn decide<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
>(
&mut self,
reference: &mut RelayReference<P, SourceClient, TargetClient>,
) -> bool {
match self.relayer_mode {
RelayerMode::Altruistic => AltruisticStrategy.decide(reference).await,
RelayerMode::Rational => RationalStrategy.decide(reference).await,
}
}
fn on_final_decision<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
>(
&self,
reference: &RelayReference<P, SourceClient, TargetClient>,
) {
match self.relayer_mode {
RelayerMode::Altruistic => AltruisticStrategy.on_final_decision(reference),
RelayerMode::Rational => RationalStrategy.on_final_decision(reference),
}
}
}
@@ -1,224 +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/>.
//! Relayer strategy
use async_trait::async_trait;
use bp_messages::{MessageNonce, Weight};
use sp_arithmetic::traits::Saturating;
use std::ops::Range;
use crate::{
message_lane::MessageLane,
message_lane_loop::{
MessageDetails, MessageDetailsMap, SourceClient as MessageLaneSourceClient,
TargetClient as MessageLaneTargetClient,
},
message_race_strategy::SourceRangesQueue,
metrics::MessageLaneLoopMetrics,
};
pub(crate) use self::enforcement_strategy::*;
pub use self::{altruistic_strategy::*, mix_strategy::*, rational_strategy::*};
mod altruistic_strategy;
mod enforcement_strategy;
mod mix_strategy;
mod rational_strategy;
/// Relayer strategy trait
#[async_trait]
pub trait RelayStrategy: 'static + Clone + Send + Sync {
/// The relayer decide how to process nonce by reference.
/// From given set of source nonces, that are ready to be delivered, select nonces
/// to fit into single delivery transaction.
///
/// The function returns last nonce that must be delivered to the target chain.
async fn decide<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
>(
&mut self,
reference: &mut RelayReference<P, SourceClient, TargetClient>,
) -> bool;
/// Notification that the following maximal nonce has been selected for the delivery.
fn on_final_decision<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
>(
&self,
reference: &RelayReference<P, SourceClient, TargetClient>,
);
}
/// Total cost of mesage delivery and confirmation.
struct MessagesDeliveryCost<SourceChainBalance> {
/// Cost of message delivery transaction.
pub delivery_transaction_cost: SourceChainBalance,
/// Cost of confirmation delivery transaction.
pub confirmation_transaction_cost: SourceChainBalance,
}
/// Reference data for participating in relay
pub struct RelayReference<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
> {
/// The client that is connected to the message lane source node.
pub lane_source_client: SourceClient,
/// The client that is connected to the message lane target node.
pub lane_target_client: TargetClient,
/// Metrics reference.
pub metrics: Option<MessageLaneLoopMetrics>,
/// Current block reward summary
pub selected_reward: P::SourceChainBalance,
/// Current block cost summary
pub selected_cost: P::SourceChainBalance,
/// Messages size summary
pub selected_size: u32,
/// Current block reward summary
pub total_reward: P::SourceChainBalance,
/// All confirmations cost
pub total_confirmations_cost: P::SourceChainBalance,
/// Current block cost summary
pub total_cost: P::SourceChainBalance,
/// Hard check begin nonce
pub hard_selected_begin_nonce: MessageNonce,
/// Count prepaid nonces
pub selected_prepaid_nonces: MessageNonce,
/// Unpaid nonces weight summary
pub selected_unpaid_weight: Weight,
/// Index by all ready nonces
pub index: usize,
/// Current nonce
pub nonce: MessageNonce,
/// Current nonce details
pub details: MessageDetails<P::SourceChainBalance>,
}
impl<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
> RelayReference<P, SourceClient, TargetClient>
{
/// Returns whether the current `RelayReference` is profitable.
pub fn is_profitable(&self) -> bool {
self.total_reward >= self.total_cost
}
async fn estimate_messages_delivery_cost(
&self,
) -> Result<MessagesDeliveryCost<P::SourceChainBalance>, TargetClient::Error> {
// technically, multiple confirmations will be delivered in a single transaction,
// meaning less loses for relayer. But here we don't know the final relayer yet, so
// we're adding a separate transaction for every message. Normally, this cost is covered
// by the message sender. Probably reconsider this?
let confirmation_transaction_cost =
self.lane_source_client.estimate_confirmation_transaction().await;
let delivery_transaction_cost = self
.lane_target_client
.estimate_delivery_transaction_in_source_tokens(
self.hard_selected_begin_nonce..=
(self.hard_selected_begin_nonce + self.index as MessageNonce),
self.selected_prepaid_nonces,
self.selected_unpaid_weight,
self.selected_size,
)
.await?;
Ok(MessagesDeliveryCost { confirmation_transaction_cost, delivery_transaction_cost })
}
async fn update_cost_and_reward(&mut self) -> Result<(), TargetClient::Error> {
let prev_is_profitable = self.is_profitable();
let prev_total_cost = self.total_cost;
let prev_total_reward = self.total_reward;
let MessagesDeliveryCost { confirmation_transaction_cost, delivery_transaction_cost } =
self.estimate_messages_delivery_cost().await?;
self.total_confirmations_cost =
self.total_confirmations_cost.saturating_add(confirmation_transaction_cost);
self.total_reward = self.total_reward.saturating_add(self.details.reward);
self.total_cost = self.total_confirmations_cost.saturating_add(delivery_transaction_cost);
if prev_is_profitable && !self.is_profitable() {
// if it is the first message that makes reward less than cost, let's log it
log::debug!(
target: "bridge",
"Message with nonce {} (reward = {:?}) changes total cost {:?}->{:?} and makes it larger than \
total reward {:?}->{:?}",
self.nonce,
self.details.reward,
prev_total_cost,
self.total_cost,
prev_total_reward,
self.total_reward,
);
} else if !prev_is_profitable && self.is_profitable() {
// if this message makes batch profitable again, let's log it
log::debug!(
target: "bridge",
"Message with nonce {} (reward = {:?}) changes total cost {:?}->{:?} and makes it less than or \
equal to the total reward {:?}->{:?} (again)",
self.nonce,
self.details.reward,
prev_total_cost,
self.total_cost,
prev_total_reward,
self.total_reward,
);
}
Ok(())
}
}
/// Relay reference data
pub struct RelayMessagesBatchReference<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
> {
/// Maximal number of relayed messages in single delivery transaction.
pub max_messages_in_this_batch: MessageNonce,
/// Maximal cumulative dispatch weight of relayed messages in single delivery transaction.
pub max_messages_weight_in_single_batch: Weight,
/// Maximal cumulative size of relayed messages in single delivery transaction.
pub max_messages_size_in_single_batch: u32,
/// The client that is connected to the message lane source node.
pub lane_source_client: SourceClient,
/// The client that is connected to the message lane target node.
pub lane_target_client: TargetClient,
/// Metrics reference.
pub metrics: Option<MessageLaneLoopMetrics>,
/// Source queue.
pub nonces_queue: SourceRangesQueue<
P::SourceHeaderHash,
P::SourceHeaderNumber,
MessageDetailsMap<P::SourceChainBalance>,
>,
/// Source queue range
pub nonces_queue_range: Range<usize>,
}
@@ -1,75 +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/>.
//! Rational relay strategy
use async_trait::async_trait;
use crate::{
message_lane::MessageLane,
message_lane_loop::{
SourceClient as MessageLaneSourceClient, TargetClient as MessageLaneTargetClient,
},
relay_strategy::{RelayReference, RelayStrategy},
};
/// The relayer will deliver all messages and confirmations as long as he's not losing any
/// funds.
#[derive(Clone)]
pub struct RationalStrategy;
#[async_trait]
impl RelayStrategy for RationalStrategy {
async fn decide<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
>(
&mut self,
reference: &mut RelayReference<P, SourceClient, TargetClient>,
) -> bool {
if let Err(e) = reference.update_cost_and_reward().await {
log::debug!(
target: "bridge",
"Failed to update transaction cost and reward: {:?}. No nonces selected for delivery",
e,
);
return false
}
// Rational relayer never wants to lose his funds.
if reference.is_profitable() {
reference.selected_reward = reference.total_reward;
reference.selected_cost = reference.total_cost;
return true
}
false
}
fn on_final_decision<
P: MessageLane,
SourceClient: MessageLaneSourceClient<P>,
TargetClient: MessageLaneTargetClient<P>,
>(
&self,
_reference: &RelayReference<P, SourceClient, TargetClient>,
) {
// rational relayer would never submit unprofitable transactions, so we don't need to do
// anything here
}
}