pay dispatch fee at target chain (#911)

* pay dispatch fee at target chain

* refund unspent dispatch weight to messages relayer

* test that transfer actually happens

* pay-at-target-cchain benchmarks + fix previous benchmarks (invalid signature)

* include/exclude pay-dispatch-fee weight from delivery_and_dispatch_fee/delivery tx cost

* remvoe some redundant traces

* enum DispatchFeePayment {}

* typo

* update docs

* (revert removal of valid check)

* Update modules/messages/src/benchmarking.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update modules/messages/src/benchmarking.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update modules/messages/src/benchmarking.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update modules/messages/src/benchmarking.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>
Co-authored-by: Tomasz Drwięga <tomasz@parity.io>
This commit is contained in:
Svyatoslav Nikolsky
2021-06-18 12:35:15 +03:00
committed by Bastian Köcher
parent 613803a15d
commit 20b7f341e1
27 changed files with 1052 additions and 278 deletions
+1
View File
@@ -686,6 +686,7 @@ mod tests {
bp_millau::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT, bp_millau::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT,
bp_millau::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT, bp_millau::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT,
bp_millau::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT, bp_millau::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT,
bp_millau::PAY_INBOUND_DISPATCH_FEE_WEIGHT,
); );
let max_incoming_message_proof_size = bp_rialto::EXTRA_STORAGE_PROOF_SIZE.saturating_add( let max_incoming_message_proof_size = bp_rialto::EXTRA_STORAGE_PROOF_SIZE.saturating_add(
@@ -64,6 +64,7 @@ type ToRialtoMessagesDeliveryProof = messages::source::FromBridgedChainMessagesD
pub type FromRialtoMessageDispatch = messages::target::FromBridgedChainMessageDispatch< pub type FromRialtoMessageDispatch = messages::target::FromBridgedChainMessageDispatch<
WithRialtoMessageBridge, WithRialtoMessageBridge,
crate::Runtime, crate::Runtime,
pallet_balances::Pallet<Runtime>,
pallet_bridge_dispatch::DefaultInstance, pallet_bridge_dispatch::DefaultInstance,
>; >;
@@ -172,6 +173,7 @@ impl messages::BridgedChainWithMessages for Rialto {
fn estimate_delivery_transaction( fn estimate_delivery_transaction(
message_payload: &[u8], message_payload: &[u8],
include_pay_dispatch_fee_cost: bool,
message_dispatch_weight: Weight, message_dispatch_weight: Weight,
) -> MessageTransaction<Weight> { ) -> MessageTransaction<Weight> {
let message_payload_len = u32::try_from(message_payload.len()).unwrap_or(u32::MAX); let message_payload_len = u32::try_from(message_payload.len()).unwrap_or(u32::MAX);
@@ -182,6 +184,11 @@ impl messages::BridgedChainWithMessages for Rialto {
dispatch_weight: extra_bytes_in_payload dispatch_weight: extra_bytes_in_payload
.saturating_mul(bp_rialto::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT) .saturating_mul(bp_rialto::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT)
.saturating_add(bp_rialto::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT) .saturating_add(bp_rialto::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT)
.saturating_sub(if include_pay_dispatch_fee_cost {
0
} else {
bp_rialto::PAY_INBOUND_DISPATCH_FEE_WEIGHT
})
.saturating_add(message_dispatch_weight), .saturating_add(message_dispatch_weight),
size: message_payload_len size: message_payload_len
.saturating_add(bp_millau::EXTRA_STORAGE_PROOF_SIZE) .saturating_add(bp_millau::EXTRA_STORAGE_PROOF_SIZE)
+25 -1
View File
@@ -862,6 +862,7 @@ impl_runtime_apis! {
} }
use crate::millau_messages::{ToMillauMessagePayload, WithMillauMessageBridge}; use crate::millau_messages::{ToMillauMessagePayload, WithMillauMessageBridge};
use bp_runtime::messages::DispatchFeePayment;
use bridge_runtime_common::messages; use bridge_runtime_common::messages;
use pallet_bridge_messages::benchmarking::{ use pallet_bridge_messages::benchmarking::{
Pallet as MessagesBench, Pallet as MessagesBench,
@@ -905,6 +906,7 @@ impl_runtime_apis! {
weight: params.size as _, weight: params.size as _,
origin: dispatch_origin, origin: dispatch_origin,
call: message_payload, call: message_payload,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
}; };
(message, pallet_bridge_messages::benchmarking::MESSAGE_FEE.into()) (message, pallet_bridge_messages::benchmarking::MESSAGE_FEE.into())
} }
@@ -921,7 +923,7 @@ impl_runtime_apis! {
use codec::Encode; use codec::Encode;
use frame_support::weights::GetDispatchInfo; use frame_support::weights::GetDispatchInfo;
use pallet_bridge_messages::storage_keys; use pallet_bridge_messages::storage_keys;
use sp_runtime::traits::Header; use sp_runtime::traits::{Header, IdentifyAccount};
let remark = match params.size { let remark = match params.size {
MessagesProofSize::Minimal(ref size) => vec![0u8; *size as _], MessagesProofSize::Minimal(ref size) => vec![0u8; *size as _],
@@ -934,12 +936,19 @@ impl_runtime_apis! {
let (rialto_raw_public, rialto_raw_signature) = ed25519_sign( let (rialto_raw_public, rialto_raw_signature) = ed25519_sign(
&call, &call,
&millau_account_id, &millau_account_id,
VERSION.spec_version,
bp_runtime::MILLAU_CHAIN_ID,
bp_runtime::RIALTO_CHAIN_ID,
); );
let rialto_public = MultiSigner::Ed25519(sp_core::ed25519::Public::from_raw(rialto_raw_public)); let rialto_public = MultiSigner::Ed25519(sp_core::ed25519::Public::from_raw(rialto_raw_public));
let rialto_signature = MultiSignature::Ed25519(sp_core::ed25519::Signature::from_raw( let rialto_signature = MultiSignature::Ed25519(sp_core::ed25519::Signature::from_raw(
rialto_raw_signature, rialto_raw_signature,
)); ));
if params.dispatch_fee_payment == DispatchFeePayment::AtTargetChain {
Self::endow_account(&rialto_public.clone().into_account());
}
let make_millau_message_key = |message_key: MessageKey| storage_keys::message_key::< let make_millau_message_key = |message_key: MessageKey| storage_keys::message_key::<
Runtime, Runtime,
<Millau as ChainWithMessages>::MessagesInstance, <Millau as ChainWithMessages>::MessagesInstance,
@@ -960,6 +969,7 @@ impl_runtime_apis! {
Default::default(), Default::default(),
); );
let dispatch_fee_payment = params.dispatch_fee_payment.clone();
prepare_message_proof::<WithMillauMessageBridge, bp_millau::Hasher, Runtime, (), _, _, _>( prepare_message_proof::<WithMillauMessageBridge, bp_millau::Hasher, Runtime, (), _, _, _>(
params, params,
make_millau_message_key, make_millau_message_key,
@@ -978,6 +988,7 @@ impl_runtime_apis! {
rialto_public, rialto_public,
rialto_signature, rialto_signature,
), ),
dispatch_fee_payment,
call: call.encode(), call: call.encode(),
}.encode(), }.encode(),
) )
@@ -1010,6 +1021,18 @@ impl_runtime_apis! {
), ),
) )
} }
fn is_message_dispatched(nonce: bp_messages::MessageNonce) -> bool {
frame_system::Pallet::<Runtime>::events()
.into_iter()
.map(|event_record| event_record.event)
.any(|event| matches!(
event,
Event::pallet_bridge_dispatch(pallet_bridge_dispatch::Event::<Runtime, _>::MessageDispatched(
_, ([0, 0, 0, 0], nonce_from_event), _,
)) if nonce_from_event == nonce
))
}
} }
add_benchmark!( add_benchmark!(
@@ -1105,6 +1128,7 @@ mod tests {
bp_rialto::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT, bp_rialto::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT,
bp_rialto::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT, bp_rialto::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT,
bp_rialto::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT, bp_rialto::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT,
bp_rialto::PAY_INBOUND_DISPATCH_FEE_WEIGHT,
); );
let max_incoming_message_proof_size = bp_millau::EXTRA_STORAGE_PROOF_SIZE.saturating_add( let max_incoming_message_proof_size = bp_millau::EXTRA_STORAGE_PROOF_SIZE.saturating_add(
@@ -58,6 +58,7 @@ pub type FromMillauEncodedCall = messages::target::FromBridgedChainEncodedMessag
pub type FromMillauMessageDispatch = messages::target::FromBridgedChainMessageDispatch< pub type FromMillauMessageDispatch = messages::target::FromBridgedChainMessageDispatch<
WithMillauMessageBridge, WithMillauMessageBridge,
crate::Runtime, crate::Runtime,
pallet_balances::Pallet<Runtime>,
pallet_bridge_dispatch::DefaultInstance, pallet_bridge_dispatch::DefaultInstance,
>; >;
@@ -172,6 +173,7 @@ impl messages::BridgedChainWithMessages for Millau {
fn estimate_delivery_transaction( fn estimate_delivery_transaction(
message_payload: &[u8], message_payload: &[u8],
include_pay_dispatch_fee_cost: bool,
message_dispatch_weight: Weight, message_dispatch_weight: Weight,
) -> MessageTransaction<Weight> { ) -> MessageTransaction<Weight> {
let message_payload_len = u32::try_from(message_payload.len()).unwrap_or(u32::MAX); let message_payload_len = u32::try_from(message_payload.len()).unwrap_or(u32::MAX);
@@ -182,6 +184,11 @@ impl messages::BridgedChainWithMessages for Millau {
dispatch_weight: extra_bytes_in_payload dispatch_weight: extra_bytes_in_payload
.saturating_mul(bp_millau::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT) .saturating_mul(bp_millau::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT)
.saturating_add(bp_millau::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT) .saturating_add(bp_millau::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT)
.saturating_sub(if include_pay_dispatch_fee_cost {
0
} else {
bp_millau::PAY_INBOUND_DISPATCH_FEE_WEIGHT
})
.saturating_add(message_dispatch_weight), .saturating_add(message_dispatch_weight),
size: message_payload_len size: message_payload_len
.saturating_add(bp_rialto::EXTRA_STORAGE_PROOF_SIZE) .saturating_add(bp_rialto::EXTRA_STORAGE_PROOF_SIZE)
@@ -258,3 +265,87 @@ impl MessagesParameter for RialtoToMillauMessagesParameter {
} }
} }
} }
#[cfg(test)]
mod tests {
use super::*;
use crate::{AccountId, Call, ExistentialDeposit, Runtime, SystemCall, SystemConfig, VERSION};
use bp_message_dispatch::CallOrigin;
use bp_messages::{
target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch},
MessageKey,
};
use bp_runtime::{derive_account_id, messages::DispatchFeePayment, SourceAccount};
use bridge_runtime_common::messages::target::{FromBridgedChainEncodedMessageCall, FromBridgedChainMessagePayload};
use frame_support::{
traits::Currency,
weights::{GetDispatchInfo, WeightToFeePolynomial},
};
use sp_runtime::traits::Convert;
#[test]
fn transfer_happens_when_dispatch_fee_is_paid_at_target_chain() {
// this test actually belongs to the `bridge-runtime-common` crate, but there we have no
// mock runtime. Making another one there just for this test, given that both crates
// live n single repo is an overkill
let mut ext: sp_io::TestExternalities = SystemConfig::default().build_storage::<Runtime>().unwrap().into();
ext.execute_with(|| {
let bridge = MILLAU_CHAIN_ID;
let call: Call = SystemCall::remark(vec![]).into();
let dispatch_weight = call.get_dispatch_info().weight;
let dispatch_fee = <Runtime as pallet_transaction_payment::Config>::WeightToFee::calc(&dispatch_weight);
assert!(dispatch_fee > 0);
// create relayer account with minimal balance
let relayer_account: AccountId = [1u8; 32].into();
let initial_amount = ExistentialDeposit::get();
let _ = <pallet_balances::Pallet<Runtime> as Currency<AccountId>>::deposit_creating(
&relayer_account,
initial_amount,
);
// create dispatch account with minimal balance + dispatch fee
let dispatch_account = derive_account_id::<<Runtime as pallet_bridge_dispatch::Config>::SourceChainAccountId>(
bridge,
SourceAccount::Root,
);
let dispatch_account =
<Runtime as pallet_bridge_dispatch::Config>::AccountIdConverter::convert(dispatch_account);
let _ = <pallet_balances::Pallet<Runtime> as Currency<AccountId>>::deposit_creating(
&dispatch_account,
initial_amount + dispatch_fee,
);
// dispatch message with intention to pay dispatch fee at the target chain
FromMillauMessageDispatch::dispatch(
&relayer_account,
DispatchMessage {
key: MessageKey {
lane_id: Default::default(),
nonce: 0,
},
data: DispatchMessageData {
payload: Ok(FromBridgedChainMessagePayload::<WithMillauMessageBridge> {
spec_version: VERSION.spec_version,
weight: dispatch_weight,
origin: CallOrigin::SourceRoot,
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
call: FromBridgedChainEncodedMessageCall::new(call.encode()),
}),
fee: 1,
},
},
);
// ensure that fee has been transferred from dispatch to relayer account
assert_eq!(
<pallet_balances::Pallet<Runtime> as Currency<AccountId>>::free_balance(&relayer_account),
initial_amount + dispatch_fee,
);
assert_eq!(
<pallet_balances::Pallet<Runtime> as Currency<AccountId>>::free_balance(&dispatch_account),
initial_amount,
);
});
}
}
+2
View File
@@ -24,6 +24,7 @@ pallet-bridge-messages = { path = "../../modules/messages", default-features = f
# Substrate dependencies # Substrate dependencies
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false }
pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false }
sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false, optional = true } sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false, optional = true }
@@ -42,6 +43,7 @@ std = [
"pallet-bridge-dispatch/std", "pallet-bridge-dispatch/std",
"pallet-bridge-grandpa/std", "pallet-bridge-grandpa/std",
"pallet-bridge-messages/std", "pallet-bridge-messages/std",
"pallet-transaction-payment/std",
"sp-core/std", "sp-core/std",
"sp-runtime/std", "sp-runtime/std",
"sp-state-machine/std", "sp-state-machine/std",
+3 -1
View File
@@ -102,7 +102,9 @@ This trait represents this chain from bridge point of view. Let's review every m
have declared dispatch weight larger than 50% of the maximal bridged extrinsic weight. have declared dispatch weight larger than 50% of the maximal bridged extrinsic weight.
- `MessageBridge::estimate_delivery_transaction`: you will need to return estimated dispatch weight and - `MessageBridge::estimate_delivery_transaction`: you will need to return estimated dispatch weight and
size of the delivery transaction that delivers a given message to the target chain. size of the delivery transaction that delivers a given message to the target chain. The transaction
weight must or must not include the weight of pay-dispatch-fee operation, depending on the value
of `include_pay_dispatch_fee_cost` argument.
- `MessageBridge::transaction_payment`: you'll need to return fee that the submitter - `MessageBridge::transaction_payment`: you'll need to return fee that the submitter
must pay for given transaction on bridged chain. The best case is when you have the same conversion must pay for given transaction on bridged chain. The best case is when you have the same conversion
+96 -22
View File
@@ -26,9 +26,16 @@ use bp_messages::{
target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages}, target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages},
InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce, OutboundLaneData, InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce, OutboundLaneData,
}; };
use bp_runtime::{ChainId, Size, StorageProofChecker}; use bp_runtime::{
messages::{DispatchFeePayment, MessageDispatchResult},
ChainId, Size, StorageProofChecker,
};
use codec::{Decode, Encode}; use codec::{Decode, Encode};
use frame_support::{traits::Instance, weights::Weight, RuntimeDebug}; use frame_support::{
traits::{Currency, ExistenceRequirement, Instance},
weights::{Weight, WeightToFeePolynomial},
RuntimeDebug,
};
use hash_db::Hasher; use hash_db::Hasher;
use sp_runtime::{ use sp_runtime::{
traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedDiv, CheckedMul}, traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedDiv, CheckedMul},
@@ -124,6 +131,7 @@ pub trait BridgedChainWithMessages: ChainWithMessages {
/// Estimate size and weight of single message delivery transaction at the Bridged chain. /// Estimate size and weight of single message delivery transaction at the Bridged chain.
fn estimate_delivery_transaction( fn estimate_delivery_transaction(
message_payload: &[u8], message_payload: &[u8],
include_pay_dispatch_fee_cost: bool,
message_dispatch_weight: WeightOf<Self>, message_dispatch_weight: WeightOf<Self>,
) -> MessageTransaction<WeightOf<Self>>; ) -> MessageTransaction<WeightOf<Self>>;
@@ -326,8 +334,19 @@ pub mod source {
relayer_fee_percent: u32, relayer_fee_percent: u32,
) -> Result<BalanceOf<ThisChain<B>>, &'static str> { ) -> Result<BalanceOf<ThisChain<B>>, &'static str> {
// the fee (in Bridged tokens) of all transactions that are made on the Bridged chain // the fee (in Bridged tokens) of all transactions that are made on the Bridged chain
let delivery_transaction = //
BridgedChain::<B>::estimate_delivery_transaction(&payload.call, payload.weight.into()); // 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 pay_dispatch_fee_at_target_chain = payload.dispatch_fee_payment == DispatchFeePayment::AtTargetChain;
let delivery_transaction = BridgedChain::<B>::estimate_delivery_transaction(
&payload.call,
pay_dispatch_fee_at_target_chain,
if pay_dispatch_fee_at_target_chain {
0.into()
} else {
payload.weight.into()
},
);
let delivery_transaction_fee = BridgedChain::<B>::transaction_payment(delivery_transaction); let delivery_transaction_fee = BridgedChain::<B>::transaction_payment(delivery_transaction);
// the fee (in This tokens) of all transactions that are made on This chain // the fee (in This tokens) of all transactions that are made on This chain
@@ -445,8 +464,18 @@ pub mod target {
/// vector length. Custom decode implementation here is exactly to deal with this. /// vector length. Custom decode implementation here is exactly to deal with this.
#[derive(Decode, Encode, RuntimeDebug, PartialEq)] #[derive(Decode, Encode, RuntimeDebug, PartialEq)]
pub struct FromBridgedChainEncodedMessageCall<B> { pub struct FromBridgedChainEncodedMessageCall<B> {
pub(crate) encoded_call: Vec<u8>, encoded_call: Vec<u8>,
pub(crate) _marker: PhantomData<B>, _marker: PhantomData<B>,
}
impl<B: MessageBridge> FromBridgedChainEncodedMessageCall<B> {
/// Create encoded call.
pub fn new(encoded_call: Vec<u8>) -> Self {
FromBridgedChainEncodedMessageCall {
encoded_call,
_marker: PhantomData::default(),
}
}
} }
impl<B: MessageBridge> From<FromBridgedChainEncodedMessageCall<B>> for Result<CallOf<ThisChain<B>>, ()> { impl<B: MessageBridge> From<FromBridgedChainEncodedMessageCall<B>> for Result<CallOf<ThisChain<B>>, ()> {
@@ -457,20 +486,28 @@ pub mod target {
/// Dispatching Bridged -> This chain messages. /// Dispatching Bridged -> This chain messages.
#[derive(RuntimeDebug, Clone, Copy)] #[derive(RuntimeDebug, Clone, Copy)]
pub struct FromBridgedChainMessageDispatch<B, ThisRuntime, ThisDispatchInstance> { pub struct FromBridgedChainMessageDispatch<B, ThisRuntime, ThisCurrency, ThisDispatchInstance> {
_marker: PhantomData<(B, ThisRuntime, ThisDispatchInstance)>, _marker: PhantomData<(B, ThisRuntime, ThisCurrency, ThisDispatchInstance)>,
} }
impl<B: MessageBridge, ThisRuntime, ThisDispatchInstance> impl<B: MessageBridge, ThisRuntime, ThisCurrency, ThisDispatchInstance>
MessageDispatch<<BridgedChain<B> as ChainWithMessages>::Balance> MessageDispatch<AccountIdOf<ThisChain<B>>, BalanceOf<BridgedChain<B>>>
for FromBridgedChainMessageDispatch<B, ThisRuntime, ThisDispatchInstance> for FromBridgedChainMessageDispatch<B, ThisRuntime, ThisCurrency, ThisDispatchInstance>
where where
ThisDispatchInstance: frame_support::traits::Instance, ThisDispatchInstance: frame_support::traits::Instance,
ThisRuntime: pallet_bridge_dispatch::Config<ThisDispatchInstance, MessageId = (LaneId, MessageNonce)>, ThisRuntime: pallet_bridge_dispatch::Config<ThisDispatchInstance, MessageId = (LaneId, MessageNonce)>
<ThisRuntime as pallet_bridge_dispatch::Config<ThisDispatchInstance>>::Event: + pallet_transaction_payment::Config,
From<pallet_bridge_dispatch::RawEvent<(LaneId, MessageNonce), ThisDispatchInstance>>, <ThisRuntime as pallet_transaction_payment::Config>::OnChargeTransaction:
pallet_bridge_dispatch::Pallet<ThisRuntime, ThisDispatchInstance>: pallet_transaction_payment::OnChargeTransaction<ThisRuntime, Balance = BalanceOf<ThisChain<B>>>,
bp_message_dispatch::MessageDispatch<(LaneId, MessageNonce), Message = FromBridgedChainMessagePayload<B>>, ThisCurrency: Currency<AccountIdOf<ThisChain<B>>, Balance = BalanceOf<ThisChain<B>>>,
<ThisRuntime as pallet_bridge_dispatch::Config<ThisDispatchInstance>>::Event: From<
pallet_bridge_dispatch::RawEvent<(LaneId, MessageNonce), AccountIdOf<ThisChain<B>>, ThisDispatchInstance>,
>,
pallet_bridge_dispatch::Pallet<ThisRuntime, ThisDispatchInstance>: bp_message_dispatch::MessageDispatch<
AccountIdOf<ThisChain<B>>,
(LaneId, MessageNonce),
Message = FromBridgedChainMessagePayload<B>,
>,
{ {
type DispatchPayload = FromBridgedChainMessagePayload<B>; type DispatchPayload = FromBridgedChainMessagePayload<B>;
@@ -480,14 +517,26 @@ pub mod target {
message.data.payload.as_ref().map(|payload| payload.weight).unwrap_or(0) message.data.payload.as_ref().map(|payload| payload.weight).unwrap_or(0)
} }
fn dispatch(message: DispatchMessage<Self::DispatchPayload, BalanceOf<BridgedChain<B>>>) { fn dispatch(
relayer_account: &AccountIdOf<ThisChain<B>>,
message: DispatchMessage<Self::DispatchPayload, BalanceOf<BridgedChain<B>>>,
) -> MessageDispatchResult {
let message_id = (message.key.lane_id, message.key.nonce); let message_id = (message.key.lane_id, message.key.nonce);
pallet_bridge_dispatch::Pallet::<ThisRuntime, ThisDispatchInstance>::dispatch( pallet_bridge_dispatch::Pallet::<ThisRuntime, ThisDispatchInstance>::dispatch(
B::BridgedChain::ID, B::BridgedChain::ID,
B::ThisChain::ID, B::ThisChain::ID,
message_id, message_id,
message.data.payload.map_err(drop), message.data.payload.map_err(drop),
); |dispatch_origin, dispatch_weight| {
ThisCurrency::transfer(
dispatch_origin,
relayer_account,
ThisRuntime::WeightToFee::calc(&dispatch_weight),
ExistenceRequirement::AllowDeath,
)
.map_err(drop)
},
)
} }
} }
@@ -854,6 +903,7 @@ mod tests {
fn estimate_delivery_transaction( fn estimate_delivery_transaction(
_message_payload: &[u8], _message_payload: &[u8],
_include_pay_dispatch_fee_cost: bool,
_message_dispatch_weight: WeightOf<Self>, _message_dispatch_weight: WeightOf<Self>,
) -> MessageTransaction<WeightOf<Self>> { ) -> MessageTransaction<WeightOf<Self>> {
unreachable!() unreachable!()
@@ -911,6 +961,7 @@ mod tests {
fn estimate_delivery_transaction( fn estimate_delivery_transaction(
_message_payload: &[u8], _message_payload: &[u8],
_include_pay_dispatch_fee_cost: bool,
message_dispatch_weight: WeightOf<Self>, message_dispatch_weight: WeightOf<Self>,
) -> MessageTransaction<WeightOf<Self>> { ) -> MessageTransaction<WeightOf<Self>> {
MessageTransaction { MessageTransaction {
@@ -935,6 +986,7 @@ mod tests {
spec_version: 1, spec_version: 1,
weight: 100, weight: 100,
origin: bp_message_dispatch::CallOrigin::SourceRoot, origin: bp_message_dispatch::CallOrigin::SourceRoot,
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
call: ThisChainCall::Transfer.encode(), call: ThisChainCall::Transfer.encode(),
} }
.encode(); .encode();
@@ -949,10 +1001,10 @@ mod tests {
spec_version: 1, spec_version: 1,
weight: 100, weight: 100,
origin: bp_message_dispatch::CallOrigin::SourceRoot, origin: bp_message_dispatch::CallOrigin::SourceRoot,
call: target::FromBridgedChainEncodedMessageCall::<OnThisChainBridge> { dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
encoded_call: ThisChainCall::Transfer.encode(), call: target::FromBridgedChainEncodedMessageCall::<OnThisChainBridge>::new(
_marker: PhantomData::default(), ThisChainCall::Transfer.encode(),
}, ),
} }
); );
assert_eq!(Ok(ThisChainCall::Transfer), message_on_this_chain.call.into()); assert_eq!(Ok(ThisChainCall::Transfer), message_on_this_chain.call.into());
@@ -966,6 +1018,7 @@ mod tests {
spec_version: 1, spec_version: 1,
weight: 100, weight: 100,
origin: bp_message_dispatch::CallOrigin::SourceRoot, origin: bp_message_dispatch::CallOrigin::SourceRoot,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
call: vec![42], call: vec![42],
} }
} }
@@ -986,6 +1039,21 @@ mod tests {
Ok(ThisChainBalance(EXPECTED_MINIMAL_FEE)), Ok(ThisChainBalance(EXPECTED_MINIMAL_FEE)),
); );
// let's check if estimation is less than hardcoded, if dispatch is paid at target chain
let mut payload_with_pay_on_target = regular_outbound_message_payload();
payload_with_pay_on_target.dispatch_fee_payment = DispatchFeePayment::AtTargetChain;
let fee_at_source = source::estimate_message_dispatch_and_delivery_fee::<OnThisChainBridge>(
&payload_with_pay_on_target,
OnThisChainBridge::RELAYER_FEE_PERCENT,
)
.expect("estimate_message_dispatch_and_delivery_fee failed for pay-at-target-chain message");
assert!(
fee_at_source < EXPECTED_MINIMAL_FEE.into(),
"Computed fee {:?} without prepaid dispatch must be less than the fee with prepaid dispatch {}",
fee_at_source,
EXPECTED_MINIMAL_FEE,
);
// and now check that the verifier checks the fee // and now check that the verifier checks the fee
assert_eq!( assert_eq!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message( source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
@@ -1016,6 +1084,7 @@ mod tests {
spec_version: 1, spec_version: 1,
weight: 100, weight: 100,
origin: bp_message_dispatch::CallOrigin::SourceRoot, origin: bp_message_dispatch::CallOrigin::SourceRoot,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
call: vec![42], call: vec![42],
}; };
@@ -1059,6 +1128,7 @@ mod tests {
spec_version: 1, spec_version: 1,
weight: 100, weight: 100,
origin: bp_message_dispatch::CallOrigin::SourceAccount(ThisChainAccountId(1)), origin: bp_message_dispatch::CallOrigin::SourceAccount(ThisChainAccountId(1)),
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
call: vec![42], call: vec![42],
}; };
@@ -1126,6 +1196,7 @@ mod tests {
spec_version: 1, spec_version: 1,
weight: 5, weight: 5,
origin: bp_message_dispatch::CallOrigin::SourceRoot, origin: bp_message_dispatch::CallOrigin::SourceRoot,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
call: vec![1, 2, 3, 4, 5, 6], call: vec![1, 2, 3, 4, 5, 6],
},) },)
.is_err() .is_err()
@@ -1141,6 +1212,7 @@ mod tests {
spec_version: 1, spec_version: 1,
weight: BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT + 1, weight: BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT + 1,
origin: bp_message_dispatch::CallOrigin::SourceRoot, origin: bp_message_dispatch::CallOrigin::SourceRoot,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
call: vec![1, 2, 3, 4, 5, 6], call: vec![1, 2, 3, 4, 5, 6],
},) },)
.is_err() .is_err()
@@ -1156,6 +1228,7 @@ mod tests {
spec_version: 1, spec_version: 1,
weight: BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT, weight: BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT,
origin: bp_message_dispatch::CallOrigin::SourceRoot, origin: bp_message_dispatch::CallOrigin::SourceRoot,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
call: vec![0; source::maximal_message_size::<OnThisChainBridge>() as usize + 1], call: vec![0; source::maximal_message_size::<OnThisChainBridge>() as usize + 1],
},) },)
.is_err() .is_err()
@@ -1171,6 +1244,7 @@ mod tests {
spec_version: 1, spec_version: 1,
weight: BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT, weight: BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT,
origin: bp_message_dispatch::CallOrigin::SourceRoot, origin: bp_message_dispatch::CallOrigin::SourceRoot,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
call: vec![0; source::maximal_message_size::<OnThisChainBridge>() as _], call: vec![0; source::maximal_message_size::<OnThisChainBridge>() as _],
},), },),
Ok(()), Ok(()),
@@ -25,6 +25,7 @@ use crate::messages::{
}; };
use bp_messages::{LaneId, MessageData, MessageKey, MessagePayload}; use bp_messages::{LaneId, MessageData, MessageKey, MessagePayload};
use bp_runtime::ChainId;
use codec::Encode; use codec::Encode;
use ed25519_dalek::{PublicKey, SecretKey, Signer, KEYPAIR_LENGTH, SECRET_KEY_LENGTH}; use ed25519_dalek::{PublicKey, SecretKey, Signer, KEYPAIR_LENGTH, SECRET_KEY_LENGTH};
use frame_support::weights::Weight; use frame_support::weights::Weight;
@@ -37,7 +38,13 @@ use sp_trie::{record_all_keys, trie_types::TrieDBMut, Layout, MemoryDB, Recorder
/// Generate ed25519 signature to be used in `pallet_brdige_call_dispatch::CallOrigin::TargetAccount`. /// Generate ed25519 signature to be used in `pallet_brdige_call_dispatch::CallOrigin::TargetAccount`.
/// ///
/// Returns public key of the signer and the signature itself. /// Returns public key of the signer and the signature itself.
pub fn ed25519_sign(target_call: &impl Encode, source_account_id: &impl Encode) -> ([u8; 32], [u8; 64]) { pub fn ed25519_sign(
target_call: &impl Encode,
source_account_id: &impl Encode,
target_spec_version: u32,
source_chain_id: ChainId,
target_chain_id: ChainId,
) -> ([u8; 32], [u8; 64]) {
// key from the repo example (https://docs.rs/ed25519-dalek/1.0.1/ed25519_dalek/struct.SecretKey.html) // key from the repo example (https://docs.rs/ed25519-dalek/1.0.1/ed25519_dalek/struct.SecretKey.html)
let target_secret = SecretKey::from_bytes(&[ let target_secret = SecretKey::from_bytes(&[
157, 097, 177, 157, 239, 253, 090, 096, 186, 132, 074, 244, 146, 236, 044, 196, 068, 073, 197, 105, 123, 050, 157, 097, 177, 157, 239, 253, 090, 096, 186, 132, 074, 244, 146, 236, 044, 196, 068, 073, 197, 105, 123, 050,
@@ -51,9 +58,13 @@ pub fn ed25519_sign(target_call: &impl Encode, source_account_id: &impl Encode)
target_pair_bytes[SECRET_KEY_LENGTH..].copy_from_slice(&target_public.to_bytes()); target_pair_bytes[SECRET_KEY_LENGTH..].copy_from_slice(&target_public.to_bytes());
let target_pair = ed25519_dalek::Keypair::from_bytes(&target_pair_bytes).expect("hardcoded pair is valid"); let target_pair = ed25519_dalek::Keypair::from_bytes(&target_pair_bytes).expect("hardcoded pair is valid");
let mut signature_message = Vec::new(); let signature_message = pallet_bridge_dispatch::account_ownership_digest(
target_call.encode_to(&mut signature_message); target_call,
source_account_id.encode_to(&mut signature_message); source_account_id,
target_spec_version,
source_chain_id,
target_chain_id,
);
let target_origin_signature = target_pair let target_origin_signature = target_pair
.try_sign(&signature_message) .try_sign(&signature_message)
.expect("Ed25519 try_sign should not fail in benchmarks"); .expect("Ed25519 try_sign should not fail in benchmarks");
+2
View File
@@ -44,6 +44,8 @@ module events set:
weight, the dispatch is rejected. Keep in mind, that even if post-dispatch weight will be less weight, the dispatch is rejected. Keep in mind, that even if post-dispatch weight will be less
than specified, the submitter still have to declare (and pay for) the maximal possible weight than specified, the submitter still have to declare (and pay for) the maximal possible weight
(that is the pre-dispatch weight); (that is the pre-dispatch weight);
- `MessageDispatchPaymentFailed` event is emitted if the message submitter has selected to pay
dispatch fee at the target chain, but has failed to do that;
- `MessageDispatched` event is emitted if the message has passed all checks and we have actually - `MessageDispatched` event is emitted if the message has passed all checks and we have actually
dispatched it. The dispatch may still fail, though - that's why we are including the dispatch dispatched it. The dispatch may still fail, though - that's why we are including the dispatch
result in the event payload. result in the event payload.
+148 -31
View File
@@ -27,7 +27,11 @@
#![allow(clippy::unused_unit)] #![allow(clippy::unused_unit)]
use bp_message_dispatch::{CallOrigin, MessageDispatch, MessagePayload, SpecVersion, Weight}; use bp_message_dispatch::{CallOrigin, MessageDispatch, MessagePayload, SpecVersion, Weight};
use bp_runtime::{derive_account_id, ChainId, SourceAccount}; use bp_runtime::{
derive_account_id,
messages::{DispatchFeePayment, MessageDispatchResult},
ChainId, SourceAccount,
};
use codec::{Decode, Encode}; use codec::{Decode, Encode};
use frame_support::{ use frame_support::{
decl_event, decl_module, decl_storage, decl_event, decl_module, decl_storage,
@@ -89,7 +93,8 @@ decl_storage! {
decl_event!( decl_event!(
pub enum Event<T, I = DefaultInstance> where pub enum Event<T, I = DefaultInstance> where
<T as Config<I>>::MessageId <T as Config<I>>::MessageId,
AccountId = <T as frame_system::Config>::AccountId,
{ {
/// Message has been rejected before reaching dispatch. /// Message has been rejected before reaching dispatch.
MessageRejected(ChainId, MessageId), MessageRejected(ChainId, MessageId),
@@ -101,12 +106,14 @@ decl_event!(
MessageWeightMismatch(ChainId, MessageId, Weight, Weight), MessageWeightMismatch(ChainId, MessageId, Weight, Weight),
/// Message signature mismatch. /// Message signature mismatch.
MessageSignatureMismatch(ChainId, MessageId), MessageSignatureMismatch(ChainId, MessageId),
/// Message has been dispatched with given result.
MessageDispatched(ChainId, MessageId, DispatchResult),
/// We have failed to decode Call from the message. /// We have failed to decode Call from the message.
MessageCallDecodeFailed(ChainId, MessageId), MessageCallDecodeFailed(ChainId, MessageId),
/// The call from the message has been rejected by the call filter. /// The call from the message has been rejected by the call filter.
MessageCallRejected(ChainId, MessageId), MessageCallRejected(ChainId, MessageId),
/// The origin account has failed to pay fee for dispatching the message.
MessageDispatchPaymentFailed(ChainId, MessageId, AccountId, Weight),
/// Message has been dispatched with given result.
MessageDispatched(ChainId, MessageId, DispatchResult),
/// Phantom member, never used. Needed to handle multiple pallet instances. /// Phantom member, never used. Needed to handle multiple pallet instances.
_Dummy(PhantomData<I>), _Dummy(PhantomData<I>),
} }
@@ -120,7 +127,7 @@ decl_module! {
} }
} }
impl<T: Config<I>, I: Instance> MessageDispatch<T::MessageId> for Pallet<T, I> { impl<T: Config<I>, I: Instance> MessageDispatch<T::AccountId, T::MessageId> for Pallet<T, I> {
type Message = type Message =
MessagePayload<T::SourceChainAccountId, T::TargetChainAccountPublic, T::TargetChainSignature, T::EncodedCall>; MessagePayload<T::SourceChainAccountId, T::TargetChainAccountPublic, T::TargetChainSignature, T::EncodedCall>;
@@ -128,7 +135,13 @@ impl<T: Config<I>, I: Instance> MessageDispatch<T::MessageId> for Pallet<T, I> {
message.weight message.weight
} }
fn dispatch(source_chain: ChainId, target_chain: ChainId, id: T::MessageId, message: Result<Self::Message, ()>) { fn dispatch<P: FnOnce(&T::AccountId, Weight) -> Result<(), ()>>(
source_chain: ChainId,
target_chain: ChainId,
id: T::MessageId,
message: Result<Self::Message, ()>,
pay_dispatch_fee: P,
) -> MessageDispatchResult {
// emit special even if message has been rejected by external component // emit special even if message has been rejected by external component
let message = match message { let message = match message {
Ok(message) => message, Ok(message) => message,
@@ -140,12 +153,19 @@ impl<T: Config<I>, I: Instance> MessageDispatch<T::MessageId> for Pallet<T, I> {
id, id,
); );
Self::deposit_event(RawEvent::MessageRejected(source_chain, id)); Self::deposit_event(RawEvent::MessageRejected(source_chain, id));
return; return MessageDispatchResult {
unspent_weight: 0,
dispatch_fee_paid_during_dispatch: false,
};
} }
}; };
// verify spec version // verify spec version
// (we want it to be the same, because otherwise we may decode Call improperly) // (we want it to be the same, because otherwise we may decode Call improperly)
let mut dispatch_result = MessageDispatchResult {
unspent_weight: message.weight,
dispatch_fee_paid_during_dispatch: false,
};
let expected_version = <T as frame_system::Config>::Version::get().spec_version; let expected_version = <T as frame_system::Config>::Version::get().spec_version;
if message.spec_version != expected_version { if message.spec_version != expected_version {
log::trace!( log::trace!(
@@ -161,7 +181,7 @@ impl<T: Config<I>, I: Instance> MessageDispatch<T::MessageId> for Pallet<T, I> {
expected_version, expected_version,
message.spec_version, message.spec_version,
)); ));
return; return dispatch_result;
} }
// now that we have spec version checked, let's decode the call // now that we have spec version checked, let's decode the call
@@ -175,7 +195,7 @@ impl<T: Config<I>, I: Instance> MessageDispatch<T::MessageId> for Pallet<T, I> {
id, id,
); );
Self::deposit_event(RawEvent::MessageCallDecodeFailed(source_chain, id)); Self::deposit_event(RawEvent::MessageCallDecodeFailed(source_chain, id));
return; return dispatch_result;
} }
}; };
@@ -207,7 +227,7 @@ impl<T: Config<I>, I: Instance> MessageDispatch<T::MessageId> for Pallet<T, I> {
target_signature, target_signature,
); );
Self::deposit_event(RawEvent::MessageSignatureMismatch(source_chain, id)); Self::deposit_event(RawEvent::MessageSignatureMismatch(source_chain, id));
return; return dispatch_result;
} }
log::trace!(target: "runtime::bridge-dispatch", "Target Account: {:?}", &target_account); log::trace!(target: "runtime::bridge-dispatch", "Target Account: {:?}", &target_account);
@@ -231,7 +251,7 @@ impl<T: Config<I>, I: Instance> MessageDispatch<T::MessageId> for Pallet<T, I> {
call, call,
); );
Self::deposit_event(RawEvent::MessageCallRejected(source_chain, id)); Self::deposit_event(RawEvent::MessageCallRejected(source_chain, id));
return; return dispatch_result;
} }
// verify weight // verify weight
@@ -254,21 +274,43 @@ impl<T: Config<I>, I: Instance> MessageDispatch<T::MessageId> for Pallet<T, I> {
expected_weight, expected_weight,
message.weight, message.weight,
)); ));
return; return dispatch_result;
} }
// pay dispatch fee right before dispatch
let pay_dispatch_fee_at_target_chain = message.dispatch_fee_payment == DispatchFeePayment::AtTargetChain;
if pay_dispatch_fee_at_target_chain && pay_dispatch_fee(&origin_account, message.weight).is_err() {
log::trace!(
target: "runtime::bridge-dispatch",
"Failed to pay dispatch fee for dispatching message {:?}/{:?} with weight {}",
source_chain,
id,
message.weight,
);
Self::deposit_event(RawEvent::MessageDispatchPaymentFailed(
source_chain,
id,
origin_account,
message.weight,
));
return dispatch_result;
}
dispatch_result.dispatch_fee_paid_during_dispatch = pay_dispatch_fee_at_target_chain;
// finally dispatch message // finally dispatch message
let origin = RawOrigin::Signed(origin_account).into(); let origin = RawOrigin::Signed(origin_account).into();
log::trace!(target: "runtime::bridge-dispatch", "Message being dispatched is: {:.4096?}", &call); log::trace!(target: "runtime::bridge-dispatch", "Message being dispatched is: {:.4096?}", &call);
let dispatch_result = call.dispatch(origin); let result = call.dispatch(origin);
let actual_call_weight = extract_actual_weight(&dispatch_result, &dispatch_info); let actual_call_weight = extract_actual_weight(&result, &dispatch_info);
dispatch_result.unspent_weight = message.weight.saturating_sub(actual_call_weight);
log::trace!( log::trace!(
target: "runtime::bridge-dispatch", target: "runtime::bridge-dispatch",
"Message {:?}/{:?} has been dispatched. Weight: {} of {}. Result: {:?}", "Message {:?}/{:?} has been dispatched. Weight: {} of {}. Result: {:?}",
source_chain, source_chain,
id, id,
actual_call_weight, dispatch_result.unspent_weight,
message.weight, message.weight,
dispatch_result, dispatch_result,
); );
@@ -276,8 +318,10 @@ impl<T: Config<I>, I: Instance> MessageDispatch<T::MessageId> for Pallet<T, I> {
Self::deposit_event(RawEvent::MessageDispatched( Self::deposit_event(RawEvent::MessageDispatched(
source_chain, source_chain,
id, id,
dispatch_result.map(drop).map_err(|e| e.error), result.map(drop).map_err(|e| e.error),
)); ));
dispatch_result
} }
} }
@@ -485,31 +529,32 @@ mod tests {
fn prepare_message( fn prepare_message(
origin: CallOrigin<AccountId, TestAccountPublic, TestSignature>, origin: CallOrigin<AccountId, TestAccountPublic, TestSignature>,
call: Call, call: Call,
) -> <Pallet<TestRuntime> as MessageDispatch<<TestRuntime as Config>::MessageId>>::Message { ) -> <Pallet<TestRuntime> as MessageDispatch<AccountId, <TestRuntime as Config>::MessageId>>::Message {
MessagePayload { MessagePayload {
spec_version: TEST_SPEC_VERSION, spec_version: TEST_SPEC_VERSION,
weight: TEST_WEIGHT, weight: TEST_WEIGHT,
origin, origin,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
call: EncodedCall(call.encode()), call: EncodedCall(call.encode()),
} }
} }
fn prepare_root_message( fn prepare_root_message(
call: Call, call: Call,
) -> <Pallet<TestRuntime> as MessageDispatch<<TestRuntime as Config>::MessageId>>::Message { ) -> <Pallet<TestRuntime> as MessageDispatch<AccountId, <TestRuntime as Config>::MessageId>>::Message {
prepare_message(CallOrigin::SourceRoot, call) prepare_message(CallOrigin::SourceRoot, call)
} }
fn prepare_target_message( fn prepare_target_message(
call: Call, call: Call,
) -> <Pallet<TestRuntime> as MessageDispatch<<TestRuntime as Config>::MessageId>>::Message { ) -> <Pallet<TestRuntime> as MessageDispatch<AccountId, <TestRuntime as Config>::MessageId>>::Message {
let origin = CallOrigin::TargetAccount(1, TestAccountPublic(1), TestSignature(1)); let origin = CallOrigin::TargetAccount(1, TestAccountPublic(1), TestSignature(1));
prepare_message(origin, call) prepare_message(origin, call)
} }
fn prepare_source_message( fn prepare_source_message(
call: Call, call: Call,
) -> <Pallet<TestRuntime> as MessageDispatch<<TestRuntime as Config>::MessageId>>::Message { ) -> <Pallet<TestRuntime> as MessageDispatch<AccountId, <TestRuntime as Config>::MessageId>>::Message {
let origin = CallOrigin::SourceAccount(1); let origin = CallOrigin::SourceAccount(1);
prepare_message(origin, call) prepare_message(origin, call)
} }
@@ -522,10 +567,12 @@ mod tests {
const BAD_SPEC_VERSION: SpecVersion = 99; const BAD_SPEC_VERSION: SpecVersion = 99;
let mut message = let mut message =
prepare_root_message(Call::System(<frame_system::Call<TestRuntime>>::remark(vec![1, 2, 3]))); prepare_root_message(Call::System(<frame_system::Call<TestRuntime>>::remark(vec![1, 2, 3])));
let weight = message.weight;
message.spec_version = BAD_SPEC_VERSION; message.spec_version = BAD_SPEC_VERSION;
System::set_block_number(1); System::set_block_number(1);
Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message)); let result = Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message), |_, _| unreachable!());
assert_eq!(result.unspent_weight, weight);
assert_eq!( assert_eq!(
System::events(), System::events(),
@@ -549,10 +596,11 @@ mod tests {
let id = [0; 4]; let id = [0; 4];
let mut message = let mut message =
prepare_root_message(Call::System(<frame_system::Call<TestRuntime>>::remark(vec![1, 2, 3]))); prepare_root_message(Call::System(<frame_system::Call<TestRuntime>>::remark(vec![1, 2, 3])));
message.weight = 0; message.weight = 7;
System::set_block_number(1); System::set_block_number(1);
Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message)); let result = Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message), |_, _| unreachable!());
assert_eq!(result.unspent_weight, 7);
assert_eq!( assert_eq!(
System::events(), System::events(),
@@ -562,7 +610,7 @@ mod tests {
SOURCE_CHAIN_ID, SOURCE_CHAIN_ID,
id, id,
1345000, 1345000,
0, 7,
)), )),
topics: vec![], topics: vec![],
}], }],
@@ -580,9 +628,11 @@ mod tests {
call_origin, call_origin,
Call::System(<frame_system::Call<TestRuntime>>::remark(vec![1, 2, 3])), Call::System(<frame_system::Call<TestRuntime>>::remark(vec![1, 2, 3])),
); );
let weight = message.weight;
System::set_block_number(1); System::set_block_number(1);
Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message)); let result = Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message), |_, _| unreachable!());
assert_eq!(result.unspent_weight, weight);
assert_eq!( assert_eq!(
System::events(), System::events(),
@@ -604,7 +654,7 @@ mod tests {
let id = [0; 4]; let id = [0; 4];
System::set_block_number(1); System::set_block_number(1);
Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Err(())); Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Err(()), |_, _| unreachable!());
assert_eq!( assert_eq!(
System::events(), System::events(),
@@ -627,10 +677,12 @@ mod tests {
let mut message = let mut message =
prepare_root_message(Call::System(<frame_system::Call<TestRuntime>>::remark(vec![1, 2, 3]))); prepare_root_message(Call::System(<frame_system::Call<TestRuntime>>::remark(vec![1, 2, 3])));
let weight = message.weight;
message.call.0 = vec![]; message.call.0 = vec![];
System::set_block_number(1); System::set_block_number(1);
Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message)); let result = Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message), |_, _| unreachable!());
assert_eq!(result.unspent_weight, weight);
assert_eq!( assert_eq!(
System::events(), System::events(),
@@ -657,7 +709,8 @@ mod tests {
message.weight = weight; message.weight = weight;
System::set_block_number(1); System::set_block_number(1);
Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message)); let result = Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message), |_, _| unreachable!());
assert_eq!(result.unspent_weight, weight);
assert_eq!( assert_eq!(
System::events(), System::events(),
@@ -673,6 +726,67 @@ mod tests {
}); });
} }
#[test]
fn should_emit_event_for_unpaid_calls() {
new_test_ext().execute_with(|| {
let id = [0; 4];
let mut message =
prepare_root_message(Call::System(<frame_system::Call<TestRuntime>>::remark(vec![1, 2, 3])));
let weight = message.weight;
message.dispatch_fee_payment = DispatchFeePayment::AtTargetChain;
System::set_block_number(1);
let result = Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message), |_, _| Err(()));
assert_eq!(result.unspent_weight, weight);
assert_eq!(
System::events(),
vec![EventRecord {
phase: Phase::Initialization,
event: Event::call_dispatch(call_dispatch::Event::<TestRuntime>::MessageDispatchPaymentFailed(
SOURCE_CHAIN_ID,
id,
AccountIdConverter::convert(derive_account_id::<AccountId>(
SOURCE_CHAIN_ID,
SourceAccount::Root
)),
TEST_WEIGHT,
)),
topics: vec![],
}],
);
});
}
#[test]
fn should_dispatch_calls_paid_at_target_chain() {
new_test_ext().execute_with(|| {
let id = [0; 4];
let mut message =
prepare_root_message(Call::System(<frame_system::Call<TestRuntime>>::remark(vec![1, 2, 3])));
message.dispatch_fee_payment = DispatchFeePayment::AtTargetChain;
System::set_block_number(1);
let result = Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message), |_, _| Ok(()));
assert!(result.dispatch_fee_paid_during_dispatch);
assert_eq!(
System::events(),
vec![EventRecord {
phase: Phase::Initialization,
event: Event::call_dispatch(call_dispatch::Event::<TestRuntime>::MessageDispatched(
SOURCE_CHAIN_ID,
id,
Ok(())
)),
topics: vec![],
}],
);
});
}
#[test] #[test]
fn should_dispatch_bridge_message_from_root_origin() { fn should_dispatch_bridge_message_from_root_origin() {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
@@ -680,7 +794,8 @@ mod tests {
let message = prepare_root_message(Call::System(<frame_system::Call<TestRuntime>>::remark(vec![1, 2, 3]))); let message = prepare_root_message(Call::System(<frame_system::Call<TestRuntime>>::remark(vec![1, 2, 3])));
System::set_block_number(1); System::set_block_number(1);
Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message)); let result = Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message), |_, _| unreachable!());
assert!(!result.dispatch_fee_paid_during_dispatch);
assert_eq!( assert_eq!(
System::events(), System::events(),
@@ -706,7 +821,8 @@ mod tests {
let message = prepare_target_message(call); let message = prepare_target_message(call);
System::set_block_number(1); System::set_block_number(1);
Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message)); let result = Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message), |_, _| unreachable!());
assert!(!result.dispatch_fee_paid_during_dispatch);
assert_eq!( assert_eq!(
System::events(), System::events(),
@@ -732,7 +848,8 @@ mod tests {
let message = prepare_source_message(call); let message = prepare_source_message(call);
System::set_block_number(1); System::set_block_number(1);
Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message)); let result = Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message), |_, _| unreachable!());
assert!(!result.dispatch_fee_paid_during_dispatch);
assert_eq!( assert_eq!(
System::events(), System::events(),
+2
View File
@@ -14,6 +14,7 @@ serde = { version = "1.0.101", optional = true, features = ["derive"] }
# Bridge dependencies # Bridge dependencies
bp-message-dispatch = { path = "../../primitives/message-dispatch", default-features = false }
bp-messages = { path = "../../primitives/messages", default-features = false } bp-messages = { path = "../../primitives/messages", default-features = false }
bp-rialto = { path = "../../primitives/chain-rialto", default-features = false } bp-rialto = { path = "../../primitives/chain-rialto", default-features = false }
bp-runtime = { path = "../../primitives/runtime", default-features = false } bp-runtime = { path = "../../primitives/runtime", default-features = false }
@@ -36,6 +37,7 @@ pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "m
[features] [features]
default = ["std"] default = ["std"]
std = [ std = [
"bp-message-dispatch/std",
"bp-messages/std", "bp-messages/std",
"bp-runtime/std", "bp-runtime/std",
"bp-rialto/std", "bp-rialto/std",
+17
View File
@@ -347,6 +347,23 @@ Both conditions are verified by `pallet_bridge_messages::ensure_weights_are_corr
`pallet_bridge_messages::ensure_able_to_receive_messages` functions, which must be called from every `pallet_bridge_messages::ensure_able_to_receive_messages` functions, which must be called from every
runtime's tests. runtime's tests.
### Post-dispatch weight refunds of the `receive_messages_proof` call
Weight formula of the `receive_messages_proof` call assumes that the dispatch fee of every message is
paid at the target chain (where call is executed), that every message will be dispatched and that
dispatch weight of the message will be exactly the weight that is returned from the
`MessageDispatch::dispatch_weight` method call. This isn't true for all messages, so the call returns
actual weight used to dispatch messages.
This actual weight is the weight, returned by the weight formula, minus:
- the weight of undispatched messages, if we have failed to dispatch because of different issues;
- the unspent dispatch weight if the declared weight of some messages is less than their actual post-dispatch weight;
- the pay-dispatch-fee weight for every message that had dispatch fee paid at the source chain.
The last component is computed as a difference between two benchmarks results - the `receive_single_message_proof`
benchmark (that assumes that the fee is paid during dispatch) and the `receive_single_prepaid_message_proof`
(that assumes that the dispatch fee is already paid).
### Weight of `receive_messages_delivery_proof` call ### Weight of `receive_messages_delivery_proof` call
#### Related benchmarks #### Related benchmarks
+58 -3
View File
@@ -23,6 +23,7 @@ use bp_messages::{
source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, InboundLaneData, LaneId, MessageData, source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, InboundLaneData, LaneId, MessageData,
MessageNonce, OutboundLaneData, UnrewardedRelayersState, MessageNonce, OutboundLaneData, UnrewardedRelayersState,
}; };
use bp_runtime::messages::DispatchFeePayment;
use frame_benchmarking::{account, benchmarks_instance}; use frame_benchmarking::{account, benchmarks_instance};
use frame_support::{traits::Get, weights::Weight}; use frame_support::{traits::Get, weights::Weight};
use frame_system::RawOrigin; use frame_system::RawOrigin;
@@ -67,6 +68,8 @@ pub struct MessageProofParams {
pub outbound_lane_data: Option<OutboundLaneData>, pub outbound_lane_data: Option<OutboundLaneData>,
/// Proof size requirements. /// Proof size requirements.
pub size: ProofSize, pub size: ProofSize,
/// If true, dispatch fee is paid at the target chain (if supported by configuration).
pub dispatch_fee_payment: DispatchFeePayment,
} }
/// Benchmark-specific message delivery proof parameters. /// Benchmark-specific message delivery proof parameters.
@@ -108,6 +111,8 @@ pub trait Config<I: Instance>: crate::Config<I> {
fn prepare_message_delivery_proof( fn prepare_message_delivery_proof(
params: MessageDeliveryProofParams<Self::AccountId>, params: MessageDeliveryProofParams<Self::AccountId>,
) -> <Self::TargetHeaderChain as TargetHeaderChain<Self::OutboundPayload, Self::AccountId>>::MessagesDeliveryProof; ) -> <Self::TargetHeaderChain as TargetHeaderChain<Self::OutboundPayload, Self::AccountId>>::MessagesDeliveryProof;
/// Returns true if message has been dispatched (either successfully or not).
fn is_message_dispatched(nonce: MessageNonce) -> bool;
} }
benchmarks_instance! { benchmarks_instance! {
@@ -242,7 +247,8 @@ benchmarks_instance! {
// * proof does not include outbound lane state proof; // * proof does not include outbound lane state proof;
// * inbound lane already has state, so it needs to be read and decoded; // * inbound lane already has state, so it needs to be read and decoded;
// * message is successfully dispatched; // * message is successfully dispatched;
// * message requires all heavy checks done by dispatcher. // * message requires all heavy checks done by dispatcher;
// * message dispatch fee is paid at target (this) chain.
// //
// This is base benchmark for all other message delivery benchmarks. // This is base benchmark for all other message delivery benchmarks.
receive_single_message_proof { receive_single_message_proof {
@@ -257,6 +263,7 @@ benchmarks_instance! {
message_nonces: 21..=21, message_nonces: 21..=21,
outbound_lane_data: None, outbound_lane_data: None,
size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), size: ProofSize::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) }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight)
verify { verify {
@@ -264,13 +271,15 @@ benchmarks_instance! {
crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()), crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()),
21, 21,
); );
assert!(T::is_message_dispatched(21));
} }
// Benchmark `receive_messages_proof` extrinsic with two minimal-weight messages and following conditions: // Benchmark `receive_messages_proof` extrinsic with two minimal-weight messages and following conditions:
// * proof does not include outbound lane state proof; // * proof does not include outbound lane state proof;
// * inbound lane already has state, so it needs to be read and decoded; // * inbound lane already has state, so it needs to be read and decoded;
// * message is successfully dispatched; // * message is successfully dispatched;
// * message requires all heavy checks done by dispatcher. // * message requires all heavy checks done by dispatcher;
// * message dispatch fee is paid at target (this) chain.
// //
// The weight of single message delivery could be approximated as // The weight of single message delivery could be approximated as
// `weight(receive_two_messages_proof) - weight(receive_single_message_proof)`. // `weight(receive_two_messages_proof) - weight(receive_single_message_proof)`.
@@ -288,6 +297,7 @@ benchmarks_instance! {
message_nonces: 21..=22, message_nonces: 21..=22,
outbound_lane_data: None, outbound_lane_data: None,
size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), size: ProofSize::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) }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 2, dispatch_weight)
verify { verify {
@@ -295,13 +305,15 @@ benchmarks_instance! {
crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()), crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()),
22, 22,
); );
assert!(T::is_message_dispatched(22));
} }
// Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions:
// * proof includes outbound lane state proof; // * proof includes outbound lane state proof;
// * inbound lane already has state, so it needs to be read and decoded; // * inbound lane already has state, so it needs to be read and decoded;
// * message is successfully dispatched; // * message is successfully dispatched;
// * message requires all heavy checks done by dispatcher. // * message requires all heavy checks done by dispatcher;
// * message dispatch fee is paid at target (this) chain.
// //
// The weight of outbound lane state delivery would be // The weight of outbound lane state delivery would be
// `weight(receive_single_message_proof_with_outbound_lane_state) - weight(receive_single_message_proof)`. // `weight(receive_single_message_proof_with_outbound_lane_state) - weight(receive_single_message_proof)`.
@@ -323,6 +335,7 @@ benchmarks_instance! {
latest_generated_nonce: 21, latest_generated_nonce: 21,
}), }),
size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), size: ProofSize::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) }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight)
verify { verify {
@@ -334,6 +347,7 @@ benchmarks_instance! {
crate::Pallet::<T, I>::inbound_latest_confirmed_nonce(T::bench_lane_id()), crate::Pallet::<T, I>::inbound_latest_confirmed_nonce(T::bench_lane_id()),
20, 20,
); );
assert!(T::is_message_dispatched(21));
} }
// Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions:
@@ -357,6 +371,7 @@ benchmarks_instance! {
message_nonces: 21..=21, message_nonces: 21..=21,
outbound_lane_data: None, outbound_lane_data: None,
size: ProofSize::HasExtraNodes(1024), size: ProofSize::HasExtraNodes(1024),
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
}); });
}: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight)
verify { verify {
@@ -364,6 +379,7 @@ benchmarks_instance! {
crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()), crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()),
21, 21,
); );
assert!(T::is_message_dispatched(21));
} }
// Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions:
@@ -389,6 +405,7 @@ benchmarks_instance! {
message_nonces: 21..=21, message_nonces: 21..=21,
outbound_lane_data: None, outbound_lane_data: None,
size: ProofSize::HasExtraNodes(16 * 1024), size: ProofSize::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) }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight)
verify { verify {
@@ -396,6 +413,40 @@ benchmarks_instance! {
crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()), crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()),
21, 21,
); );
assert!(T::is_message_dispatched(21));
}
// 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;
// * message is successfully dispatched;
// * message requires all heavy checks done by dispatcher;
// * message dispatch fee is paid at source (bridged) chain.
//
// This benchmark is used to compute extra weight spent at target chain when fee is paid there. Then we use
// this information in two places: (1) to reduce weight of delivery tx if sender pays fee at the source chain
// and (2) to refund relayer with this weight if fee has been paid at the source chain.
receive_single_prepaid_message_proof {
let relayer_id_on_source = T::bridged_relayer_id();
let relayer_id_on_target = account("relayer", 0, SEED);
// mark messages 1..=20 as delivered
receive_messages::<T, I>(20);
let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams {
lane: T::bench_lane_id(),
message_nonces: 21..=21,
outbound_lane_data: None,
size: ProofSize::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 {
assert_eq!(
crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()),
21,
);
assert!(T::is_message_dispatched(21));
} }
// Benchmark `receive_messages_delivery_proof` extrinsic with following conditions: // Benchmark `receive_messages_delivery_proof` extrinsic with following conditions:
@@ -569,6 +620,7 @@ benchmarks_instance! {
message_nonces: 21..=(20 + i as MessageNonce), message_nonces: 21..=(20 + i as MessageNonce),
outbound_lane_data: None, outbound_lane_data: None,
size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH),
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
}); });
}: receive_messages_proof( }: receive_messages_proof(
RawOrigin::Signed(relayer_id_on_target), RawOrigin::Signed(relayer_id_on_target),
@@ -606,6 +658,7 @@ benchmarks_instance! {
message_nonces: 21..=21, message_nonces: 21..=21,
outbound_lane_data: None, outbound_lane_data: None,
size: ProofSize::HasExtraNodes(i as _), size: ProofSize::HasExtraNodes(i as _),
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
}); });
}: receive_messages_proof( }: receive_messages_proof(
RawOrigin::Signed(relayer_id_on_target), RawOrigin::Signed(relayer_id_on_target),
@@ -643,6 +696,7 @@ benchmarks_instance! {
message_nonces: 21..=21, message_nonces: 21..=21,
outbound_lane_data: None, outbound_lane_data: None,
size: ProofSize::HasLargeLeaf(i as _), size: ProofSize::HasLargeLeaf(i as _),
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
}); });
}: receive_messages_proof( }: receive_messages_proof(
RawOrigin::Signed(relayer_id_on_target), RawOrigin::Signed(relayer_id_on_target),
@@ -686,6 +740,7 @@ benchmarks_instance! {
latest_generated_nonce: 21, latest_generated_nonce: 21,
}), }),
size: ProofSize::Minimal(0), size: ProofSize::Minimal(0),
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
}); });
}: receive_messages_proof( }: receive_messages_proof(
RawOrigin::Signed(relayer_id_on_target), RawOrigin::Signed(relayer_id_on_target),
+145 -54
View File
@@ -20,6 +20,8 @@ use bp_messages::{
target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch},
InboundLaneData, LaneId, MessageKey, MessageNonce, OutboundLaneData, InboundLaneData, LaneId, MessageKey, MessageNonce, OutboundLaneData,
}; };
use bp_runtime::messages::MessageDispatchResult;
use frame_support::RuntimeDebug;
use sp_std::prelude::PartialEq; use sp_std::prelude::PartialEq;
/// Inbound lane storage. /// Inbound lane storage.
@@ -27,7 +29,7 @@ pub trait InboundLaneStorage {
/// Delivery and dispatch fee type on source chain. /// Delivery and dispatch fee type on source chain.
type MessageFee; type MessageFee;
/// Id of relayer on source chain. /// Id of relayer on source chain.
type Relayer: PartialEq; type Relayer: Clone + PartialEq;
/// Lane id. /// Lane id.
fn id(&self) -> LaneId; fn id(&self) -> LaneId;
@@ -41,6 +43,22 @@ pub trait InboundLaneStorage {
fn set_data(&mut self, data: InboundLaneData<Self::Relayer>); fn set_data(&mut self, data: InboundLaneData<Self::Relayer>);
} }
/// Result of single message receival.
#[derive(RuntimeDebug, PartialEq, Eq)]
pub enum ReceivalResult {
/// Message has been received and dispatched. Note that we don't care whether dispatch has
/// been successful or not - in both case message falls into this category.
///
/// The message dispatch result is also returned.
Dispatched(MessageDispatchResult),
/// Message has invalid nonce and lane has rejected to accept this message.
InvalidNonce,
/// There are too many unrewarded relayer entires at the lane.
TooManyUnrewardedRelayers,
/// There are too many unconfirmed messages at the lane.
TooManyUnconfirmedMessages,
}
/// Inbound messages lane. /// Inbound messages lane.
pub struct InboundLane<S> { pub struct InboundLane<S> {
storage: S, storage: S,
@@ -90,51 +108,54 @@ impl<S: InboundLaneStorage> InboundLane<S> {
} }
/// Receive new message. /// Receive new message.
pub fn receive_message<P: MessageDispatch<S::MessageFee>>( pub fn receive_message<P: MessageDispatch<AccountId, S::MessageFee>, AccountId>(
&mut self, &mut self,
relayer: S::Relayer, relayer_at_bridged_chain: &S::Relayer,
relayer_at_this_chain: &AccountId,
nonce: MessageNonce, nonce: MessageNonce,
message_data: DispatchMessageData<P::DispatchPayload, S::MessageFee>, message_data: DispatchMessageData<P::DispatchPayload, S::MessageFee>,
) -> bool { ) -> ReceivalResult {
let mut data = self.storage.data(); let mut data = self.storage.data();
let is_correct_message = nonce == data.last_delivered_nonce() + 1; let is_correct_message = nonce == data.last_delivered_nonce() + 1;
if !is_correct_message { if !is_correct_message {
return false; return ReceivalResult::InvalidNonce;
} }
// if there are more unrewarded relayer entries than we may accept, reject this message // if there are more unrewarded relayer entries than we may accept, reject this message
if data.relayers.len() as MessageNonce >= self.storage.max_unrewarded_relayer_entries() { if data.relayers.len() as MessageNonce >= self.storage.max_unrewarded_relayer_entries() {
return false; return ReceivalResult::TooManyUnrewardedRelayers;
} }
// if there are more unconfirmed messages than we may accept, reject this message // if there are more unconfirmed messages than we may accept, reject this message
let unconfirmed_messages_count = nonce.saturating_sub(data.last_confirmed_nonce); let unconfirmed_messages_count = nonce.saturating_sub(data.last_confirmed_nonce);
if unconfirmed_messages_count > self.storage.max_unconfirmed_messages() { if unconfirmed_messages_count > self.storage.max_unconfirmed_messages() {
return false; return ReceivalResult::TooManyUnconfirmedMessages;
} }
let push_new = match data.relayers.back_mut() { let push_new = match data.relayers.back_mut() {
Some((_, nonce_high, last_relayer)) if last_relayer == &relayer => { Some((_, nonce_high, last_relayer)) if last_relayer == relayer_at_bridged_chain => {
*nonce_high = nonce; *nonce_high = nonce;
false false
} }
_ => true, _ => true,
}; };
if push_new { if push_new {
data.relayers.push_back((nonce, nonce, relayer)); data.relayers
.push_back((nonce, nonce, (*relayer_at_bridged_chain).clone()));
} }
self.storage.set_data(data); self.storage.set_data(data);
P::dispatch(DispatchMessage { ReceivalResult::Dispatched(P::dispatch(
relayer_at_this_chain,
DispatchMessage {
key: MessageKey { key: MessageKey {
lane_id: self.storage.id(), lane_id: self.storage.id(),
nonce, nonce,
}, },
data: message_data, data: message_data,
}); },
))
true
} }
} }
@@ -144,8 +165,8 @@ mod tests {
use crate::{ use crate::{
inbound_lane, inbound_lane,
mock::{ mock::{
message_data, run_test, TestMessageDispatch, TestRuntime, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_RELAYER_A, dispatch_result, message_data, run_test, TestMessageDispatch, TestRuntime, REGULAR_PAYLOAD, TEST_LANE_ID,
TEST_RELAYER_B, TEST_RELAYER_C, TEST_RELAYER_A, TEST_RELAYER_B, TEST_RELAYER_C,
}, },
DefaultInstance, RuntimeInboundLaneStorage, DefaultInstance, RuntimeInboundLaneStorage,
}; };
@@ -154,11 +175,15 @@ mod tests {
lane: &mut InboundLane<RuntimeInboundLaneStorage<TestRuntime, DefaultInstance>>, lane: &mut InboundLane<RuntimeInboundLaneStorage<TestRuntime, DefaultInstance>>,
nonce: MessageNonce, nonce: MessageNonce,
) { ) {
assert!(lane.receive_message::<TestMessageDispatch>( assert_eq!(
TEST_RELAYER_A, lane.receive_message::<TestMessageDispatch, _>(
&TEST_RELAYER_A,
&TEST_RELAYER_A,
nonce, nonce,
message_data(REGULAR_PAYLOAD).into() message_data(REGULAR_PAYLOAD).into()
)); ),
ReceivalResult::Dispatched(dispatch_result(0))
);
} }
#[test] #[test]
@@ -269,11 +294,15 @@ mod tests {
fn fails_to_receive_message_with_incorrect_nonce() { fn fails_to_receive_message_with_incorrect_nonce() {
run_test(|| { run_test(|| {
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID); let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
assert!(!lane.receive_message::<TestMessageDispatch>( assert_eq!(
TEST_RELAYER_A, lane.receive_message::<TestMessageDispatch, _>(
&TEST_RELAYER_A,
&TEST_RELAYER_A,
10, 10,
message_data(REGULAR_PAYLOAD).into() message_data(REGULAR_PAYLOAD).into()
)); ),
ReceivalResult::InvalidNonce
);
assert_eq!(lane.storage.data().last_delivered_nonce(), 0); assert_eq!(lane.storage.data().last_delivered_nonce(), 0);
}); });
} }
@@ -284,24 +313,36 @@ mod tests {
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID); let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
let max_nonce = <TestRuntime as crate::Config>::MaxUnrewardedRelayerEntriesAtInboundLane::get(); let max_nonce = <TestRuntime as crate::Config>::MaxUnrewardedRelayerEntriesAtInboundLane::get();
for current_nonce in 1..max_nonce + 1 { for current_nonce in 1..max_nonce + 1 {
assert!(lane.receive_message::<TestMessageDispatch>( assert_eq!(
TEST_RELAYER_A + current_nonce, lane.receive_message::<TestMessageDispatch, _>(
&(TEST_RELAYER_A + current_nonce),
&(TEST_RELAYER_A + current_nonce),
current_nonce, current_nonce,
message_data(REGULAR_PAYLOAD).into() message_data(REGULAR_PAYLOAD).into()
)); ),
ReceivalResult::Dispatched(dispatch_result(0))
);
} }
// Fails to dispatch new message from different than latest relayer. // Fails to dispatch new message from different than latest relayer.
assert!(!lane.receive_message::<TestMessageDispatch>( assert_eq!(
TEST_RELAYER_A + max_nonce + 1, lane.receive_message::<TestMessageDispatch, _>(
&(TEST_RELAYER_A + max_nonce + 1),
&(TEST_RELAYER_A + max_nonce + 1),
max_nonce + 1, max_nonce + 1,
message_data(REGULAR_PAYLOAD).into() message_data(REGULAR_PAYLOAD).into()
)); ),
ReceivalResult::TooManyUnrewardedRelayers,
);
// Fails to dispatch new messages from latest relayer. Prevents griefing attacks. // Fails to dispatch new messages from latest relayer. Prevents griefing attacks.
assert!(!lane.receive_message::<TestMessageDispatch>( assert_eq!(
TEST_RELAYER_A + max_nonce, lane.receive_message::<TestMessageDispatch, _>(
&(TEST_RELAYER_A + max_nonce),
&(TEST_RELAYER_A + max_nonce),
max_nonce + 1, max_nonce + 1,
message_data(REGULAR_PAYLOAD).into() message_data(REGULAR_PAYLOAD).into()
)); ),
ReceivalResult::TooManyUnrewardedRelayers,
);
}); });
} }
@@ -311,24 +352,36 @@ mod tests {
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID); let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
let max_nonce = <TestRuntime as crate::Config>::MaxUnconfirmedMessagesAtInboundLane::get(); let max_nonce = <TestRuntime as crate::Config>::MaxUnconfirmedMessagesAtInboundLane::get();
for current_nonce in 1..=max_nonce { for current_nonce in 1..=max_nonce {
assert!(lane.receive_message::<TestMessageDispatch>( assert_eq!(
TEST_RELAYER_A, lane.receive_message::<TestMessageDispatch, _>(
&TEST_RELAYER_A,
&TEST_RELAYER_A,
current_nonce, current_nonce,
message_data(REGULAR_PAYLOAD).into() message_data(REGULAR_PAYLOAD).into()
)); ),
ReceivalResult::Dispatched(dispatch_result(0))
);
} }
// Fails to dispatch new message from different than latest relayer. // Fails to dispatch new message from different than latest relayer.
assert!(!lane.receive_message::<TestMessageDispatch>( assert_eq!(
TEST_RELAYER_B, lane.receive_message::<TestMessageDispatch, _>(
&TEST_RELAYER_B,
&TEST_RELAYER_B,
max_nonce + 1, max_nonce + 1,
message_data(REGULAR_PAYLOAD).into() message_data(REGULAR_PAYLOAD).into()
)); ),
ReceivalResult::TooManyUnconfirmedMessages,
);
// Fails to dispatch new messages from latest relayer. // Fails to dispatch new messages from latest relayer.
assert!(!lane.receive_message::<TestMessageDispatch>( assert_eq!(
TEST_RELAYER_A, lane.receive_message::<TestMessageDispatch, _>(
&TEST_RELAYER_A,
&TEST_RELAYER_A,
max_nonce + 1, max_nonce + 1,
message_data(REGULAR_PAYLOAD).into() message_data(REGULAR_PAYLOAD).into()
)); ),
ReceivalResult::TooManyUnconfirmedMessages,
);
}); });
} }
@@ -336,21 +389,33 @@ mod tests {
fn correctly_receives_following_messages_from_two_relayers_alternately() { fn correctly_receives_following_messages_from_two_relayers_alternately() {
run_test(|| { run_test(|| {
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID); let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
assert!(lane.receive_message::<TestMessageDispatch>( assert_eq!(
TEST_RELAYER_A, lane.receive_message::<TestMessageDispatch, _>(
&TEST_RELAYER_A,
&TEST_RELAYER_A,
1, 1,
message_data(REGULAR_PAYLOAD).into() message_data(REGULAR_PAYLOAD).into()
)); ),
assert!(lane.receive_message::<TestMessageDispatch>( ReceivalResult::Dispatched(dispatch_result(0))
TEST_RELAYER_B, );
assert_eq!(
lane.receive_message::<TestMessageDispatch, _>(
&TEST_RELAYER_B,
&TEST_RELAYER_B,
2, 2,
message_data(REGULAR_PAYLOAD).into() message_data(REGULAR_PAYLOAD).into()
)); ),
assert!(lane.receive_message::<TestMessageDispatch>( ReceivalResult::Dispatched(dispatch_result(0))
TEST_RELAYER_A, );
assert_eq!(
lane.receive_message::<TestMessageDispatch, _>(
&TEST_RELAYER_A,
&TEST_RELAYER_A,
3, 3,
message_data(REGULAR_PAYLOAD).into() message_data(REGULAR_PAYLOAD).into()
)); ),
ReceivalResult::Dispatched(dispatch_result(0))
);
assert_eq!( assert_eq!(
lane.storage.data().relayers, lane.storage.data().relayers,
vec![(1, 1, TEST_RELAYER_A), (2, 2, TEST_RELAYER_B), (3, 3, TEST_RELAYER_A)] vec![(1, 1, TEST_RELAYER_A), (2, 2, TEST_RELAYER_B), (3, 3, TEST_RELAYER_A)]
@@ -362,16 +427,24 @@ mod tests {
fn rejects_same_message_from_two_different_relayers() { fn rejects_same_message_from_two_different_relayers() {
run_test(|| { run_test(|| {
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID); let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
assert!(lane.receive_message::<TestMessageDispatch>( assert_eq!(
TEST_RELAYER_A, lane.receive_message::<TestMessageDispatch, _>(
&TEST_RELAYER_A,
&TEST_RELAYER_A,
1, 1,
message_data(REGULAR_PAYLOAD).into() message_data(REGULAR_PAYLOAD).into()
)); ),
assert!(!lane.receive_message::<TestMessageDispatch>( ReceivalResult::Dispatched(dispatch_result(0))
TEST_RELAYER_B, );
assert_eq!(
lane.receive_message::<TestMessageDispatch, _>(
&TEST_RELAYER_B,
&TEST_RELAYER_B,
1, 1,
message_data(REGULAR_PAYLOAD).into() message_data(REGULAR_PAYLOAD).into()
)); ),
ReceivalResult::InvalidNonce,
);
}); });
} }
@@ -383,4 +456,22 @@ mod tests {
assert_eq!(lane.storage.data().last_delivered_nonce(), 1); assert_eq!(lane.storage.data().last_delivered_nonce(), 1);
}); });
} }
#[test]
fn unspent_weight_is_returned_by_receive_message() {
run_test(|| {
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
let mut payload = REGULAR_PAYLOAD;
payload.dispatch_result.unspent_weight = 1;
assert_eq!(
lane.receive_message::<TestMessageDispatch, _>(
&TEST_RELAYER_A,
&TEST_RELAYER_A,
1,
message_data(payload).into()
),
ReceivalResult::Dispatched(dispatch_result(1))
);
});
}
} }
+148 -29
View File
@@ -42,7 +42,7 @@ pub use crate::weights_ext::{
EXPECTED_DEFAULT_MESSAGE_LENGTH, EXPECTED_DEFAULT_MESSAGE_LENGTH,
}; };
use crate::inbound_lane::{InboundLane, InboundLaneStorage}; use crate::inbound_lane::{InboundLane, InboundLaneStorage, ReceivalResult};
use crate::outbound_lane::{OutboundLane, OutboundLaneStorage}; use crate::outbound_lane::{OutboundLane, OutboundLaneStorage};
use crate::weights::WeightInfo; use crate::weights::WeightInfo;
@@ -55,9 +55,11 @@ use bp_messages::{
use bp_runtime::Size; use bp_runtime::Size;
use codec::{Decode, Encode}; use codec::{Decode, Encode};
use frame_support::{ use frame_support::{
decl_error, decl_event, decl_module, decl_storage, ensure, decl_error, decl_event, decl_module, decl_storage,
dispatch::DispatchResultWithPostInfo,
ensure,
traits::Get, traits::Get,
weights::{DispatchClass, Weight}, weights::{DispatchClass, Pays, PostDispatchInfo, Weight},
Parameter, StorageMap, Parameter, StorageMap,
}; };
use frame_system::{ensure_signed, RawOrigin}; use frame_system::{ensure_signed, RawOrigin};
@@ -150,7 +152,11 @@ pub trait Config<I = DefaultInstance>: frame_system::Config {
/// Source header chain, as it is represented on target chain. /// Source header chain, as it is represented on target chain.
type SourceHeaderChain: SourceHeaderChain<Self::InboundMessageFee>; type SourceHeaderChain: SourceHeaderChain<Self::InboundMessageFee>;
/// Message dispatch. /// Message dispatch.
type MessageDispatch: MessageDispatch<Self::InboundMessageFee, DispatchPayload = Self::InboundPayload>; type MessageDispatch: MessageDispatch<
Self::AccountId,
Self::InboundMessageFee,
DispatchPayload = Self::InboundPayload,
>;
} }
/// Shortcut to messages proof type for Config. /// Shortcut to messages proof type for Config.
@@ -438,13 +444,13 @@ decl_module! {
#[weight = T::WeightInfo::receive_messages_proof_weight(proof, *messages_count, *dispatch_weight)] #[weight = T::WeightInfo::receive_messages_proof_weight(proof, *messages_count, *dispatch_weight)]
pub fn receive_messages_proof( pub fn receive_messages_proof(
origin, origin,
relayer_id: T::InboundRelayer, relayer_id_at_bridged_chain: T::InboundRelayer,
proof: MessagesProofOf<T, I>, proof: MessagesProofOf<T, I>,
messages_count: u32, messages_count: u32,
dispatch_weight: Weight, dispatch_weight: Weight,
) -> DispatchResult { ) -> DispatchResultWithPostInfo {
ensure_not_halted::<T, I>()?; ensure_not_halted::<T, I>()?;
let _ = ensure_signed(origin)?; let relayer_id_at_this_chain = ensure_signed(origin)?;
// reject transactions that are declaring too many messages // reject transactions that are declaring too many messages
ensure!( ensure!(
@@ -452,6 +458,23 @@ decl_module! {
Error::<T, I>::TooManyMessagesInTheProof Error::<T, I>::TooManyMessagesInTheProof
); );
// why do we need to know the weight of this (`receive_messages_proof`) call? Because
// we may want to return some funds for not-dispatching (or partially dispatching) some
// messages to the call origin (relayer). And this is done by returning actual weight
// from the call. But we only know dispatch weight of every messages. So to refund relayer
// because we have not dispatched Message, we need to:
//
// ActualWeight = DeclaredWeight - Message.DispatchWeight
//
// The DeclaredWeight is exactly what's computed here. Unfortunately it is impossible
// to get pre-computed value (and it has been already computed by the executive).
let declared_weight = T::WeightInfo::receive_messages_proof_weight(
&proof,
messages_count,
dispatch_weight,
);
let mut actual_weight = declared_weight;
// verify messages proof && convert proof into messages // verify messages proof && convert proof into messages
let messages = verify_and_decode_messages_proof::< let messages = verify_and_decode_messages_proof::<
T::SourceHeaderChain, T::SourceHeaderChain,
@@ -511,20 +534,57 @@ decl_module! {
debug_assert_eq!(message.key.lane_id, lane_id); debug_assert_eq!(message.key.lane_id, lane_id);
total_messages += 1; total_messages += 1;
if lane.receive_message::<T::MessageDispatch>(relayer_id.clone(), message.key.nonce, message.data) { let dispatch_weight = T::MessageDispatch::dispatch_weight(&message);
let receival_result = lane.receive_message::<T::MessageDispatch, T::AccountId>(
&relayer_id_at_bridged_chain,
&relayer_id_at_this_chain,
message.key.nonce,
message.data,
);
// note that we're returning unspent weight to relayer even if message has been
// rejected by the lane. This allows relayers to submit spam transactions with
// e.g. the same set of already delivered messages over and over again, without
// losing funds for messages dispatch. But keep in mind that relayer pays base
// delivery transaction cost anyway. And base cost covers everything except
// dispatch, so we have a balance here.
let (unspent_weight, refund_pay_dispatch_fee) = match receival_result {
ReceivalResult::Dispatched(dispatch_result) => {
valid_messages += 1; valid_messages += 1;
(dispatch_result.unspent_weight, !dispatch_result.dispatch_fee_paid_during_dispatch)
},
ReceivalResult::InvalidNonce
| ReceivalResult::TooManyUnrewardedRelayers
| ReceivalResult::TooManyUnconfirmedMessages => (dispatch_weight, true),
};
actual_weight = actual_weight
.saturating_sub(sp_std::cmp::min(unspent_weight, dispatch_weight))
.saturating_sub(
// delivery call weight formula assumes that the fee is paid at
// this (target) chain. If the message is prepaid at the source
// chain, let's refund relayer with this extra cost.
if refund_pay_dispatch_fee {
T::WeightInfo::pay_inbound_dispatch_fee_overhead()
} else {
0
} }
);
} }
} }
log::trace!( log::trace!(
target: "runtime::bridge-messages", target: "runtime::bridge-messages",
"Received messages: total={}, valid={}", "Received messages: total={}, valid={}. Weight used: {}/{}",
total_messages, total_messages,
valid_messages, valid_messages,
actual_weight,
declared_weight,
); );
Ok(()) Ok(PostDispatchInfo {
actual_weight: Some(actual_weight),
pays_fee: Pays::Yes,
})
} }
/// Receive messages delivery proof from bridged chain. /// Receive messages delivery proof from bridged chain.
@@ -860,10 +920,9 @@ fn verify_and_decode_messages_proof<Chain: SourceHeaderChain<Fee>, Fee, Dispatch
mod tests { mod tests {
use super::*; use super::*;
use crate::mock::{ use crate::mock::{
message, run_test, Event as TestEvent, Origin, TestMessageDeliveryAndDispatchPayment, message, message_payload, run_test, Event as TestEvent, Origin, TestMessageDeliveryAndDispatchPayment,
TestMessagesDeliveryProof, TestMessagesParameter, TestMessagesProof, TestPayload, TestRuntime, TestMessagesDeliveryProof, TestMessagesParameter, TestMessagesProof, TestRuntime, TokenConversionRate,
TokenConversionRate, PAYLOAD_REJECTED_BY_TARGET_CHAIN, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_RELAYER_A, PAYLOAD_REJECTED_BY_TARGET_CHAIN, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_RELAYER_A, TEST_RELAYER_B,
TEST_RELAYER_B,
}; };
use bp_messages::UnrewardedRelayersState; use bp_messages::UnrewardedRelayersState;
use frame_support::{assert_noop, assert_ok}; use frame_support::{assert_noop, assert_ok};
@@ -883,7 +942,7 @@ mod tests {
Origin::signed(1), Origin::signed(1),
TEST_LANE_ID, TEST_LANE_ID,
REGULAR_PAYLOAD, REGULAR_PAYLOAD,
REGULAR_PAYLOAD.1, REGULAR_PAYLOAD.declared_weight,
)); ));
// check event with assigned nonce // check event with assigned nonce
@@ -897,7 +956,10 @@ mod tests {
); );
// check that fee has been withdrawn from submitter // check that fee has been withdrawn from submitter
assert!(TestMessageDeliveryAndDispatchPayment::is_fee_paid(1, REGULAR_PAYLOAD.1)); assert!(TestMessageDeliveryAndDispatchPayment::is_fee_paid(
1,
REGULAR_PAYLOAD.declared_weight
));
} }
fn receive_messages_delivery_proof() { fn receive_messages_delivery_proof() {
@@ -1113,7 +1175,7 @@ mod tests {
Origin::signed(1), Origin::signed(1),
TEST_LANE_ID, TEST_LANE_ID,
REGULAR_PAYLOAD, REGULAR_PAYLOAD,
REGULAR_PAYLOAD.1, REGULAR_PAYLOAD.declared_weight,
), ),
Error::<TestRuntime, DefaultInstance>::Halted, Error::<TestRuntime, DefaultInstance>::Halted,
); );
@@ -1129,7 +1191,7 @@ mod tests {
TEST_RELAYER_A, TEST_RELAYER_A,
Ok(vec![message(2, REGULAR_PAYLOAD)]).into(), Ok(vec![message(2, REGULAR_PAYLOAD)]).into(),
1, 1,
REGULAR_PAYLOAD.1, REGULAR_PAYLOAD.declared_weight,
), ),
Error::<TestRuntime, DefaultInstance>::Halted, Error::<TestRuntime, DefaultInstance>::Halted,
); );
@@ -1164,7 +1226,7 @@ mod tests {
Origin::signed(1), Origin::signed(1),
TEST_LANE_ID, TEST_LANE_ID,
REGULAR_PAYLOAD, REGULAR_PAYLOAD,
REGULAR_PAYLOAD.1, REGULAR_PAYLOAD.declared_weight,
), ),
Error::<TestRuntime, DefaultInstance>::Halted, Error::<TestRuntime, DefaultInstance>::Halted,
); );
@@ -1181,7 +1243,7 @@ mod tests {
TEST_RELAYER_A, TEST_RELAYER_A,
Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), Ok(vec![message(1, REGULAR_PAYLOAD)]).into(),
1, 1,
REGULAR_PAYLOAD.1, REGULAR_PAYLOAD.declared_weight,
),); ),);
assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof( assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
@@ -1214,7 +1276,7 @@ mod tests {
Origin::signed(1), Origin::signed(1),
TEST_LANE_ID, TEST_LANE_ID,
PAYLOAD_REJECTED_BY_TARGET_CHAIN, PAYLOAD_REJECTED_BY_TARGET_CHAIN,
PAYLOAD_REJECTED_BY_TARGET_CHAIN.1 PAYLOAD_REJECTED_BY_TARGET_CHAIN.declared_weight
), ),
Error::<TestRuntime, DefaultInstance>::MessageRejectedByChainVerifier, Error::<TestRuntime, DefaultInstance>::MessageRejectedByChainVerifier,
); );
@@ -1241,7 +1303,7 @@ mod tests {
Origin::signed(1), Origin::signed(1),
TEST_LANE_ID, TEST_LANE_ID,
REGULAR_PAYLOAD, REGULAR_PAYLOAD,
REGULAR_PAYLOAD.1 REGULAR_PAYLOAD.declared_weight
), ),
Error::<TestRuntime, DefaultInstance>::FailedToWithdrawMessageFee, Error::<TestRuntime, DefaultInstance>::FailedToWithdrawMessageFee,
); );
@@ -1256,7 +1318,7 @@ mod tests {
TEST_RELAYER_A, TEST_RELAYER_A,
Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), Ok(vec![message(1, REGULAR_PAYLOAD)]).into(),
1, 1,
REGULAR_PAYLOAD.1, REGULAR_PAYLOAD.declared_weight,
)); ));
assert_eq!(InboundLanes::<TestRuntime>::get(TEST_LANE_ID).last_delivered_nonce(), 1); assert_eq!(InboundLanes::<TestRuntime>::get(TEST_LANE_ID).last_delivered_nonce(), 1);
@@ -1297,7 +1359,7 @@ mod tests {
TEST_RELAYER_A, TEST_RELAYER_A,
message_proof, message_proof,
1, 1,
REGULAR_PAYLOAD.1, REGULAR_PAYLOAD.declared_weight,
)); ));
assert_eq!( assert_eq!(
@@ -1329,7 +1391,7 @@ mod tests {
TEST_RELAYER_A, TEST_RELAYER_A,
Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), Ok(vec![message(1, REGULAR_PAYLOAD)]).into(),
1, 1,
REGULAR_PAYLOAD.1 - 1, REGULAR_PAYLOAD.declared_weight - 1,
), ),
Error::<TestRuntime, DefaultInstance>::InvalidMessagesDispatchWeight, Error::<TestRuntime, DefaultInstance>::InvalidMessagesDispatchWeight,
); );
@@ -1551,7 +1613,7 @@ mod tests {
]) ])
.into(), .into(),
3, 3,
REGULAR_PAYLOAD.1 + REGULAR_PAYLOAD.1, REGULAR_PAYLOAD.declared_weight + REGULAR_PAYLOAD.declared_weight,
),); ),);
assert_eq!( assert_eq!(
@@ -1603,9 +1665,9 @@ mod tests {
#[test] #[test]
fn actual_dispatch_weight_does_not_overlow() { fn actual_dispatch_weight_does_not_overlow() {
run_test(|| { run_test(|| {
let message1 = message(1, TestPayload(0, Weight::MAX / 2)); let message1 = message(1, message_payload(0, Weight::MAX / 2));
let message2 = message(2, TestPayload(0, Weight::MAX / 2)); let message2 = message(2, message_payload(0, Weight::MAX / 2));
let message3 = message(2, TestPayload(0, Weight::MAX / 2)); let message3 = message(2, message_payload(0, Weight::MAX / 2));
assert_noop!( assert_noop!(
Pallet::<TestRuntime, DefaultInstance>::receive_messages_proof( Pallet::<TestRuntime, DefaultInstance>::receive_messages_proof(
@@ -1672,4 +1734,61 @@ mod tests {
assert!(TestMessageDeliveryAndDispatchPayment::is_fee_paid(1, 100)); assert!(TestMessageDeliveryAndDispatchPayment::is_fee_paid(1, 100));
}); });
} }
#[test]
fn weight_refund_from_receive_messages_proof_works() {
run_test(|| {
fn submit_with_unspent_weight(
nonce: MessageNonce,
unspent_weight: Weight,
is_prepaid: bool,
) -> (Weight, Weight) {
let mut payload = REGULAR_PAYLOAD;
payload.dispatch_result.unspent_weight = unspent_weight;
payload.dispatch_result.dispatch_fee_paid_during_dispatch = !is_prepaid;
let proof = Ok(vec![message(nonce, payload)]).into();
let messages_count = 1;
let pre_dispatch_weight = <TestRuntime as Config>::WeightInfo::receive_messages_proof_weight(
&proof,
messages_count,
REGULAR_PAYLOAD.declared_weight,
);
let post_dispatch_weight = Pallet::<TestRuntime>::receive_messages_proof(
Origin::signed(1),
TEST_RELAYER_A,
proof,
messages_count,
REGULAR_PAYLOAD.declared_weight,
)
.expect("delivery has failed")
.actual_weight
.expect("receive_messages_proof always returns Some");
(pre_dispatch_weight, post_dispatch_weight)
}
// when dispatch is returning `unspent_weight < declared_weight`
let (pre, post) = submit_with_unspent_weight(1, 1, false);
assert_eq!(post, pre - 1);
// when dispatch is returning `unspent_weight = declared_weight`
let (pre, post) = submit_with_unspent_weight(2, REGULAR_PAYLOAD.declared_weight, false);
assert_eq!(post, pre - REGULAR_PAYLOAD.declared_weight);
// when dispatch is returning `unspent_weight > declared_weight`
let (pre, post) = submit_with_unspent_weight(3, REGULAR_PAYLOAD.declared_weight + 1, false);
assert_eq!(post, pre - REGULAR_PAYLOAD.declared_weight);
// when there's no unspent weight
let (pre, post) = submit_with_unspent_weight(4, 0, false);
assert_eq!(post, pre);
// when dispatch is returning `unspent_weight < declared_weight` AND message is prepaid
let (pre, post) = submit_with_unspent_weight(5, 1, true);
assert_eq!(
post,
pre - 1 - <TestRuntime as Config>::WeightInfo::pay_inbound_dispatch_fee_overhead()
);
});
}
} }
+42 -7
View File
@@ -27,7 +27,7 @@ use bp_messages::{
InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce, OutboundLaneData, InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce, OutboundLaneData,
Parameter as MessagesParameter, Parameter as MessagesParameter,
}; };
use bp_runtime::Size; use bp_runtime::{messages::MessageDispatchResult, Size};
use codec::{Decode, Encode}; use codec::{Decode, Encode};
use frame_support::{parameter_types, weights::Weight}; use frame_support::{parameter_types, weights::Weight};
use sp_core::H256; use sp_core::H256;
@@ -41,7 +41,17 @@ use std::collections::BTreeMap;
pub type AccountId = u64; pub type AccountId = u64;
pub type Balance = u64; pub type Balance = u64;
#[derive(Decode, Encode, Clone, Debug, PartialEq, Eq)] #[derive(Decode, Encode, Clone, Debug, PartialEq, Eq)]
pub struct TestPayload(pub u64, pub Weight); pub struct TestPayload {
/// Field that may be used to identify messages.
pub id: u64,
/// Dispatch weight that is declared by the message sender.
pub declared_weight: Weight,
/// Message dispatch result.
///
/// Note: in correct code `dispatch_result.unspent_weight` will always be <= `declared_weight`, but for test
/// purposes we'll be making it larger than `declared_weight` sometimes.
pub dispatch_result: MessageDispatchResult,
}
pub type TestMessageFee = u64; pub type TestMessageFee = u64;
pub type TestRelayer = u64; pub type TestRelayer = u64;
@@ -189,10 +199,10 @@ pub const TEST_ERROR: &str = "Test error";
pub const TEST_LANE_ID: LaneId = [0, 0, 0, 1]; pub const TEST_LANE_ID: LaneId = [0, 0, 0, 1];
/// Regular message payload. /// Regular message payload.
pub const REGULAR_PAYLOAD: TestPayload = TestPayload(0, 50); pub const REGULAR_PAYLOAD: TestPayload = message_payload(0, 50);
/// Payload that is rejected by `TestTargetHeaderChain`. /// Payload that is rejected by `TestTargetHeaderChain`.
pub const PAYLOAD_REJECTED_BY_TARGET_CHAIN: TestPayload = TestPayload(1, 50); pub const PAYLOAD_REJECTED_BY_TARGET_CHAIN: TestPayload = message_payload(1, 50);
/// Vec of proved messages, grouped by lane. /// Vec of proved messages, grouped by lane.
pub type MessagesByLaneVec = Vec<(LaneId, ProvedLaneMessages<Message<TestMessageFee>>)>; pub type MessagesByLaneVec = Vec<(LaneId, ProvedLaneMessages<Message<TestMessageFee>>)>;
@@ -359,17 +369,25 @@ impl SourceHeaderChain<TestMessageFee> for TestSourceHeaderChain {
#[derive(Debug)] #[derive(Debug)]
pub struct TestMessageDispatch; pub struct TestMessageDispatch;
impl MessageDispatch<TestMessageFee> for TestMessageDispatch { impl MessageDispatch<AccountId, TestMessageFee> for TestMessageDispatch {
type DispatchPayload = TestPayload; type DispatchPayload = TestPayload;
fn dispatch_weight(message: &DispatchMessage<TestPayload, TestMessageFee>) -> Weight { fn dispatch_weight(message: &DispatchMessage<TestPayload, TestMessageFee>) -> Weight {
match message.data.payload.as_ref() { match message.data.payload.as_ref() {
Ok(payload) => payload.1, Ok(payload) => payload.declared_weight,
Err(_) => 0, Err(_) => 0,
} }
} }
fn dispatch(_message: DispatchMessage<TestPayload, TestMessageFee>) {} fn dispatch(
_relayer_account: &AccountId,
message: DispatchMessage<TestPayload, TestMessageFee>,
) -> MessageDispatchResult {
match message.data.payload.as_ref() {
Ok(payload) => payload.dispatch_result.clone(),
Err(_) => dispatch_result(0),
}
}
} }
/// Return test lane message with given nonce and payload. /// Return test lane message with given nonce and payload.
@@ -383,6 +401,15 @@ pub fn message(nonce: MessageNonce, payload: TestPayload) -> Message<TestMessage
} }
} }
/// Constructs message payload using given arguments and zero unspent weight.
pub const fn message_payload(id: u64, declared_weight: Weight) -> TestPayload {
TestPayload {
id,
declared_weight,
dispatch_result: dispatch_result(0),
}
}
/// Return message data with valid fee for given payload. /// Return message data with valid fee for given payload.
pub fn message_data(payload: TestPayload) -> MessageData<TestMessageFee> { pub fn message_data(payload: TestPayload) -> MessageData<TestMessageFee> {
MessageData { MessageData {
@@ -391,6 +418,14 @@ pub fn message_data(payload: TestPayload) -> MessageData<TestMessageFee> {
} }
} }
/// Returns message dispatch result with given unspent weight.
pub const fn dispatch_result(unspent_weight: Weight) -> MessageDispatchResult {
MessageDispatchResult {
unspent_weight,
dispatch_fee_paid_during_dispatch: true,
}
}
/// Run pallet test. /// Run pallet test.
pub fn run_test<T>(test: impl FnOnce() -> T) -> T { pub fn run_test<T>(test: impl FnOnce() -> T) -> T {
let mut t = frame_system::GenesisConfig::default() let mut t = frame_system::GenesisConfig::default()
+92 -81
View File
@@ -17,7 +17,7 @@
//! Autogenerated weights for pallet_bridge_messages //! Autogenerated weights for pallet_bridge_messages
//! //!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0
//! DATE: 2021-06-03, STEPS: [50, ], REPEAT: 20 //! DATE: 2021-06-15, STEPS: [50, ], REPEAT: 20
//! LOW RANGE: [], HIGH RANGE: [] //! LOW RANGE: [], HIGH RANGE: []
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled
//! CHAIN: Some("dev"), DB CACHE: 128 //! CHAIN: Some("dev"), DB CACHE: 128
@@ -57,6 +57,7 @@ pub trait WeightInfo {
fn receive_single_message_proof_with_outbound_lane_state() -> Weight; fn receive_single_message_proof_with_outbound_lane_state() -> Weight;
fn receive_single_message_proof_1_kb() -> Weight; fn receive_single_message_proof_1_kb() -> Weight;
fn receive_single_message_proof_16_kb() -> Weight; fn receive_single_message_proof_16_kb() -> Weight;
fn receive_single_prepaid_message_proof() -> Weight;
fn receive_delivery_proof_for_single_message() -> Weight; fn receive_delivery_proof_for_single_message() -> Weight;
fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight; fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight;
fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight; fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight;
@@ -73,105 +74,110 @@ pub trait WeightInfo {
pub struct RialtoWeight<T>(PhantomData<T>); pub struct RialtoWeight<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for RialtoWeight<T> { impl<T: frame_system::Config> WeightInfo for RialtoWeight<T> {
fn send_minimal_message_worst_case() -> Weight { fn send_minimal_message_worst_case() -> Weight {
(140_457_000 as Weight) (154_371_000 as Weight)
.saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(12 as Weight)) .saturating_add(T::DbWeight::get().writes(12 as Weight))
} }
fn send_1_kb_message_worst_case() -> Weight { fn send_1_kb_message_worst_case() -> Weight {
(138_097_000 as Weight) (157_479_000 as Weight)
.saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(12 as Weight)) .saturating_add(T::DbWeight::get().writes(12 as Weight))
} }
fn send_16_kb_message_worst_case() -> Weight { fn send_16_kb_message_worst_case() -> Weight {
(196_192_000 as Weight) (186_840_000 as Weight)
.saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(12 as Weight)) .saturating_add(T::DbWeight::get().writes(12 as Weight))
} }
fn increase_message_fee() -> Weight { fn increase_message_fee() -> Weight {
(6_244_063_000 as Weight) (4_377_567_000 as Weight)
.saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight))
} }
fn receive_single_message_proof() -> Weight { fn receive_single_message_proof() -> Weight {
(135_633_000 as Weight) (205_350_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight))
} }
fn receive_two_messages_proof() -> Weight { fn receive_two_messages_proof() -> Weight {
(229_415_000 as Weight) (337_102_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight))
} }
fn receive_single_message_proof_with_outbound_lane_state() -> Weight { fn receive_single_message_proof_with_outbound_lane_state() -> Weight {
(147_408_000 as Weight) (218_825_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight))
} }
fn receive_single_message_proof_1_kb() -> Weight { fn receive_single_message_proof_1_kb() -> Weight {
(160_092_000 as Weight) (230_759_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight))
} }
fn receive_single_message_proof_16_kb() -> Weight { fn receive_single_message_proof_16_kb() -> Weight {
(452_140_000 as Weight) (490_522_000 as Weight)
.saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight))
}
fn receive_single_prepaid_message_proof() -> Weight {
(136_550_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight))
} }
fn receive_delivery_proof_for_single_message() -> Weight { fn receive_delivery_proof_for_single_message() -> Weight {
(123_704_000 as Weight) (131_397_000 as Weight)
.saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().reads(6 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight))
} }
fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight {
(127_844_000 as Weight) (137_946_000 as Weight)
.saturating_add(T::DbWeight::get().reads(7 as Weight)) .saturating_add(T::DbWeight::get().reads(7 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight))
} }
fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight {
(183_119_000 as Weight) (194_448_000 as Weight)
.saturating_add(T::DbWeight::get().reads(8 as Weight)) .saturating_add(T::DbWeight::get().reads(8 as Weight))
.saturating_add(T::DbWeight::get().writes(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight))
} }
fn send_messages_of_various_lengths(i: u32) -> Weight { fn send_messages_of_various_lengths(i: u32) -> Weight {
(162_249_000 as Weight) (142_576_000 as Weight)
.saturating_add((3_000 as Weight).saturating_mul(i as Weight)) .saturating_add((2_000 as Weight).saturating_mul(i as Weight))
.saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(12 as Weight)) .saturating_add(T::DbWeight::get().writes(12 as Weight))
} }
fn receive_multiple_messages_proof(i: u32) -> Weight { fn receive_multiple_messages_proof(i: u32) -> Weight {
(0 as Weight) (0 as Weight)
.saturating_add((107_235_000 as Weight).saturating_mul(i as Weight)) .saturating_add((138_341_000 as Weight).saturating_mul(i as Weight))
.saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight))
} }
fn receive_message_proofs_with_extra_nodes(i: u32) -> Weight { fn receive_message_proofs_with_extra_nodes(i: u32) -> Weight {
(450_232_000 as Weight) (472_752_000 as Weight)
.saturating_add((9_000 as Weight).saturating_mul(i as Weight)) .saturating_add((9_000 as Weight).saturating_mul(i as Weight))
.saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight))
} }
fn receive_message_proofs_with_large_leaf(i: u32) -> Weight { fn receive_message_proofs_with_large_leaf(i: u32) -> Weight {
(181_851_000 as Weight) (175_300_000 as Weight)
.saturating_add((7_000 as Weight).saturating_mul(i as Weight)) .saturating_add((6_000 as Weight).saturating_mul(i as Weight))
.saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight))
} }
fn receive_multiple_messages_proof_with_outbound_lane_state(i: u32) -> Weight { fn receive_multiple_messages_proof_with_outbound_lane_state(i: u32) -> Weight {
(0 as Weight) (0 as Weight)
.saturating_add((114_622_000 as Weight).saturating_mul(i as Weight)) .saturating_add((142_176_000 as Weight).saturating_mul(i as Weight))
.saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight))
} }
fn receive_delivery_proof_for_multiple_messages_by_single_relayer(i: u32) -> Weight { fn receive_delivery_proof_for_multiple_messages_by_single_relayer(i: u32) -> Weight {
(103_133_000 as Weight) (127_372_000 as Weight)
.saturating_add((6_676_000 as Weight).saturating_mul(i as Weight)) .saturating_add((7_927_000 as Weight).saturating_mul(i as Weight))
.saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(i as Weight))) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(i as Weight)))
.saturating_add(T::DbWeight::get().writes(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight))
} }
fn receive_delivery_proof_for_multiple_messages_by_multiple_relayers(i: u32) -> Weight { fn receive_delivery_proof_for_multiple_messages_by_multiple_relayers(i: u32) -> Weight {
(100_321_000 as Weight) (99_781_000 as Weight)
.saturating_add((59_736_000 as Weight).saturating_mul(i as Weight)) .saturating_add((64_001_000 as Weight).saturating_mul(i as Weight))
.saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(i as Weight))) .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(i as Weight)))
.saturating_add(T::DbWeight::get().writes(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight))
@@ -182,105 +188,110 @@ impl<T: frame_system::Config> WeightInfo for RialtoWeight<T> {
// For backwards compatibility and tests // For backwards compatibility and tests
impl WeightInfo for () { impl WeightInfo for () {
fn send_minimal_message_worst_case() -> Weight { fn send_minimal_message_worst_case() -> Weight {
(140_457_000 as Weight) (154_371_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(12 as Weight)) .saturating_add(RocksDbWeight::get().writes(12 as Weight))
} }
fn send_1_kb_message_worst_case() -> Weight { fn send_1_kb_message_worst_case() -> Weight {
(138_097_000 as Weight) (157_479_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(12 as Weight)) .saturating_add(RocksDbWeight::get().writes(12 as Weight))
} }
fn send_16_kb_message_worst_case() -> Weight { fn send_16_kb_message_worst_case() -> Weight {
(196_192_000 as Weight) (186_840_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(12 as Weight)) .saturating_add(RocksDbWeight::get().writes(12 as Weight))
} }
fn increase_message_fee() -> Weight { fn increase_message_fee() -> Weight {
(6_244_063_000 as Weight) (4_377_567_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight))
} }
fn receive_single_message_proof() -> Weight { fn receive_single_message_proof() -> Weight {
(135_633_000 as Weight) (205_350_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight))
} }
fn receive_two_messages_proof() -> Weight { fn receive_two_messages_proof() -> Weight {
(229_415_000 as Weight) (337_102_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight))
} }
fn receive_single_message_proof_with_outbound_lane_state() -> Weight { fn receive_single_message_proof_with_outbound_lane_state() -> Weight {
(147_408_000 as Weight) (218_825_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight))
} }
fn receive_single_message_proof_1_kb() -> Weight { fn receive_single_message_proof_1_kb() -> Weight {
(160_092_000 as Weight) (230_759_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight))
} }
fn receive_single_message_proof_16_kb() -> Weight { fn receive_single_message_proof_16_kb() -> Weight {
(452_140_000 as Weight) (490_522_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(3 as Weight))
}
fn receive_single_prepaid_message_proof() -> Weight {
(136_550_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight))
} }
fn receive_delivery_proof_for_single_message() -> Weight { fn receive_delivery_proof_for_single_message() -> Weight {
(123_704_000 as Weight) (131_397_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().reads(6 as Weight))
.saturating_add(RocksDbWeight::get().writes(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight))
} }
fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight {
(127_844_000 as Weight) (137_946_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(7 as Weight)) .saturating_add(RocksDbWeight::get().reads(7 as Weight))
.saturating_add(RocksDbWeight::get().writes(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight))
} }
fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight {
(183_119_000 as Weight) (194_448_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(8 as Weight)) .saturating_add(RocksDbWeight::get().reads(8 as Weight))
.saturating_add(RocksDbWeight::get().writes(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight))
} }
fn send_messages_of_various_lengths(i: u32) -> Weight { fn send_messages_of_various_lengths(i: u32) -> Weight {
(162_249_000 as Weight) (142_576_000 as Weight)
.saturating_add((3_000 as Weight).saturating_mul(i as Weight)) .saturating_add((2_000 as Weight).saturating_mul(i as Weight))
.saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(12 as Weight)) .saturating_add(RocksDbWeight::get().writes(12 as Weight))
} }
fn receive_multiple_messages_proof(i: u32) -> Weight { fn receive_multiple_messages_proof(i: u32) -> Weight {
(0 as Weight) (0 as Weight)
.saturating_add((107_235_000 as Weight).saturating_mul(i as Weight)) .saturating_add((138_341_000 as Weight).saturating_mul(i as Weight))
.saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight))
} }
fn receive_message_proofs_with_extra_nodes(i: u32) -> Weight { fn receive_message_proofs_with_extra_nodes(i: u32) -> Weight {
(450_232_000 as Weight) (472_752_000 as Weight)
.saturating_add((9_000 as Weight).saturating_mul(i as Weight)) .saturating_add((9_000 as Weight).saturating_mul(i as Weight))
.saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight))
} }
fn receive_message_proofs_with_large_leaf(i: u32) -> Weight { fn receive_message_proofs_with_large_leaf(i: u32) -> Weight {
(181_851_000 as Weight) (175_300_000 as Weight)
.saturating_add((7_000 as Weight).saturating_mul(i as Weight)) .saturating_add((6_000 as Weight).saturating_mul(i as Weight))
.saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight))
} }
fn receive_multiple_messages_proof_with_outbound_lane_state(i: u32) -> Weight { fn receive_multiple_messages_proof_with_outbound_lane_state(i: u32) -> Weight {
(0 as Weight) (0 as Weight)
.saturating_add((114_622_000 as Weight).saturating_mul(i as Weight)) .saturating_add((142_176_000 as Weight).saturating_mul(i as Weight))
.saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight))
} }
fn receive_delivery_proof_for_multiple_messages_by_single_relayer(i: u32) -> Weight { fn receive_delivery_proof_for_multiple_messages_by_single_relayer(i: u32) -> Weight {
(103_133_000 as Weight) (127_372_000 as Weight)
.saturating_add((6_676_000 as Weight).saturating_mul(i as Weight)) .saturating_add((7_927_000 as Weight).saturating_mul(i as Weight))
.saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(i as Weight))) .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(i as Weight)))
.saturating_add(RocksDbWeight::get().writes(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight))
} }
fn receive_delivery_proof_for_multiple_messages_by_multiple_relayers(i: u32) -> Weight { fn receive_delivery_proof_for_multiple_messages_by_multiple_relayers(i: u32) -> Weight {
(100_321_000 as Weight) (99_781_000 as Weight)
.saturating_add((59_736_000 as Weight).saturating_mul(i as Weight)) .saturating_add((64_001_000 as Weight).saturating_mul(i as Weight))
.saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(i as Weight))) .saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(i as Weight)))
.saturating_add(RocksDbWeight::get().writes(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight))
@@ -34,6 +34,7 @@ pub fn ensure_weights_are_correct<W: WeightInfoExt>(
expected_default_message_delivery_tx_weight: Weight, expected_default_message_delivery_tx_weight: Weight,
expected_additional_byte_delivery_weight: Weight, expected_additional_byte_delivery_weight: Weight,
expected_messages_delivery_confirmation_tx_weight: Weight, expected_messages_delivery_confirmation_tx_weight: Weight,
expected_pay_inbound_dispatch_fee_weight: Weight,
) { ) {
// verify `send_message` weight components // verify `send_message` weight components
assert_ne!(W::send_message_overhead(), 0); assert_ne!(W::send_message_overhead(), 0);
@@ -88,6 +89,15 @@ pub fn ensure_weights_are_correct<W: WeightInfoExt>(
actual_messages_delivery_confirmation_tx_weight, actual_messages_delivery_confirmation_tx_weight,
expected_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 <= 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. /// Ensure that we're able to receive maximal (by-size and by-weight) message from other chain.
@@ -304,6 +314,13 @@ pub trait WeightInfoExt: WeightInfo {
(Self::receive_single_message_proof_16_kb() - Self::receive_single_message_proof_1_kb()) / (15 * 1024); (Self::receive_single_message_proof_16_kb() - Self::receive_single_message_proof_1_kb()) / (15 * 1024);
proof_size_in_bytes * byte_weight proof_size_in_bytes * byte_weight
} }
/// Returns weight of the pay-dispatch-fee operation for inbound messages.
///
/// This function may return zero if runtime doesn't support pay-dispatch-fee-at-target-chain option.
fn pay_inbound_dispatch_fee_overhead() -> Weight {
Self::receive_single_message_proof().saturating_sub(Self::receive_single_prepaid_message_proof())
}
} }
impl WeightInfoExt for () { impl WeightInfoExt for () {
+8 -1
View File
@@ -80,7 +80,7 @@ pub const MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE: MessageNonce = 1024;
/// for the case when single message of `pallet_bridge_messages::EXPECTED_DEFAULT_MESSAGE_LENGTH` bytes is delivered. /// 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 /// The message must have dispatch weight set to zero. The result then must be rounded up to account
/// possible future runtime upgrades. /// possible future runtime upgrades.
pub const DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT: Weight = 1_000_000_000; pub const DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT: Weight = 1_500_000_000;
/// Increase of delivery transaction weight on Millau chain with every additional message byte. /// Increase of delivery transaction weight on Millau chain with every additional message byte.
/// ///
@@ -95,6 +95,13 @@ pub const ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT: Weight = 25_000;
/// runtime upgrades. /// runtime upgrades.
pub const MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT: Weight = 2_000_000_000; pub const MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT: Weight = 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 = 600_000_000;
/// The target length of a session (how often authorities change) on Millau measured in of number of /// The target length of a session (how often authorities change) on Millau measured in of number of
/// blocks. /// blocks.
/// ///
+8 -1
View File
@@ -71,7 +71,7 @@ pub const MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE: MessageNonce = 128;
/// for the case when single message of `pallet_bridge_messages::EXPECTED_DEFAULT_MESSAGE_LENGTH` bytes is delivered. /// 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 /// The message must have dispatch weight set to zero. The result then must be rounded up to account
/// possible future runtime upgrades. /// possible future runtime upgrades.
pub const DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT: Weight = 1_000_000_000; pub const DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT: Weight = 1_500_000_000;
/// Increase of delivery transaction weight on Rialto chain with every additional message byte. /// Increase of delivery transaction weight on Rialto chain with every additional message byte.
/// ///
@@ -86,6 +86,13 @@ pub const ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT: Weight = 25_000;
/// runtime upgrades. /// runtime upgrades.
pub const MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT: Weight = 2_000_000_000; pub const MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT: Weight = 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 = 600_000_000;
/// The target length of a session (how often authorities change) on Rialto measured in of number of /// The target length of a session (how often authorities change) on Rialto measured in of number of
/// blocks. /// blocks.
/// ///
+16 -3
View File
@@ -19,7 +19,10 @@
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs)] #![warn(missing_docs)]
use bp_runtime::{ChainId, Size}; use bp_runtime::{
messages::{DispatchFeePayment, MessageDispatchResult},
ChainId, Size,
};
use codec::{Decode, Encode}; use codec::{Decode, Encode};
use frame_support::RuntimeDebug; use frame_support::RuntimeDebug;
use sp_std::prelude::*; use sp_std::prelude::*;
@@ -31,7 +34,7 @@ pub type Weight = u64;
pub type SpecVersion = u32; pub type SpecVersion = u32;
/// A generic trait to dispatch arbitrary messages delivered over the bridge. /// A generic trait to dispatch arbitrary messages delivered over the bridge.
pub trait MessageDispatch<MessageId> { pub trait MessageDispatch<AccountId, MessageId> {
/// A type of the message to be dispatched. /// A type of the message to be dispatched.
type Message: codec::Decode; type Message: codec::Decode;
@@ -52,7 +55,15 @@ pub trait MessageDispatch<MessageId> {
/// a sign that some other component has rejected the message even before it has /// a sign that some other component has rejected the message even before it has
/// reached `dispatch` method (right now this may only be caused if we fail to decode /// reached `dispatch` method (right now this may only be caused if we fail to decode
/// the whole message). /// the whole message).
fn dispatch(source_chain: ChainId, target_chain: ChainId, id: MessageId, message: Result<Self::Message, ()>); ///
/// Returns unspent dispatch weight.
fn dispatch<P: FnOnce(&AccountId, Weight) -> Result<(), ()>>(
source_chain: ChainId,
target_chain: ChainId,
id: MessageId,
message: Result<Self::Message, ()>,
pay_dispatch_fee: P,
) -> MessageDispatchResult;
} }
/// Origin of a Call when it is dispatched on the target chain. /// Origin of a Call when it is dispatched on the target chain.
@@ -110,6 +121,8 @@ pub struct MessagePayload<SourceChainAccountId, TargetChainAccountPublic, Target
pub weight: Weight, pub weight: Weight,
/// Call origin to be used during dispatch. /// Call origin to be used during dispatch.
pub origin: CallOrigin<SourceChainAccountId, TargetChainAccountPublic, TargetChainSignature>, pub origin: CallOrigin<SourceChainAccountId, TargetChainAccountPublic, TargetChainSignature>,
/// Where message dispatch fee is paid?
pub dispatch_fee_payment: DispatchFeePayment,
/// The call itself. /// The call itself.
pub call: Call, pub call: Call,
} }
@@ -18,7 +18,7 @@
use crate::{LaneId, Message, MessageData, MessageKey, OutboundLaneData}; use crate::{LaneId, Message, MessageData, MessageKey, OutboundLaneData};
use bp_runtime::Size; use bp_runtime::{messages::MessageDispatchResult, Size};
use codec::{Decode, Encode, Error as CodecError}; use codec::{Decode, Encode, Error as CodecError};
use frame_support::{weights::Weight, Parameter, RuntimeDebug}; use frame_support::{weights::Weight, Parameter, RuntimeDebug};
use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, prelude::*}; use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, prelude::*};
@@ -84,7 +84,7 @@ pub trait SourceHeaderChain<Fee> {
} }
/// Called when inbound message is received. /// Called when inbound message is received.
pub trait MessageDispatch<Fee> { pub trait MessageDispatch<AccountId, Fee> {
/// Decoded message payload type. Valid message may contain invalid payload. In this case /// 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 /// message is delivered, but dispatch fails. Therefore, two separate types of payload
/// (opaque `MessagePayload` used in delivery and this `DispatchPayload` used in dispatch). /// (opaque `MessagePayload` used in delivery and this `DispatchPayload` used in dispatch).
@@ -100,7 +100,13 @@ pub trait MessageDispatch<Fee> {
/// ///
/// It is up to the implementers of this trait to determine whether the message /// It is up to the implementers of this trait to determine whether the message
/// is invalid (i.e. improperly encoded, has too large weight, ...) or not. /// is invalid (i.e. improperly encoded, has too large weight, ...) or not.
fn dispatch(message: DispatchMessage<Self::DispatchPayload, Fee>); ///
/// If your configuration allows paying dispatch fee at the target chain, then
/// it must be paid inside this method to the `relayer_account`.
fn dispatch(
relayer_account: &AccountId,
message: DispatchMessage<Self::DispatchPayload, Fee>,
) -> MessageDispatchResult;
} }
impl<Message> Default for ProvedLaneMessages<Message> { impl<Message> Default for ProvedLaneMessages<Message> {
@@ -149,12 +155,17 @@ impl<Fee> SourceHeaderChain<Fee> for ForbidInboundMessages {
} }
} }
impl<Fee> MessageDispatch<Fee> for ForbidInboundMessages { impl<AccountId, Fee> MessageDispatch<AccountId, Fee> for ForbidInboundMessages {
type DispatchPayload = (); type DispatchPayload = ();
fn dispatch_weight(_message: &DispatchMessage<Self::DispatchPayload, Fee>) -> Weight { fn dispatch_weight(_message: &DispatchMessage<Self::DispatchPayload, Fee>) -> Weight {
Weight::MAX Weight::MAX
} }
fn dispatch(_message: DispatchMessage<Self::DispatchPayload, Fee>) {} fn dispatch(_: &AccountId, _: DispatchMessage<Self::DispatchPayload, Fee>) -> MessageDispatchResult {
MessageDispatchResult {
unspent_weight: 0,
dispatch_fee_paid_during_dispatch: false,
}
}
} }
+2
View File
@@ -29,6 +29,8 @@ pub use storage_proof::{Error as StorageProofError, StorageProofChecker};
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub use storage_proof::craft_valid_storage_proof; pub use storage_proof::craft_valid_storage_proof;
pub mod messages;
mod chain; mod chain;
mod storage_proof; mod storage_proof;
@@ -0,0 +1,50 @@
// 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/>.
//! Primitives that may be used by different message delivery and dispatch mechanisms.
use codec::{Decode, Encode};
use frame_support::{weights::Weight, RuntimeDebug};
/// Where message dispatch fee is paid?
#[derive(Encode, Decode, RuntimeDebug, Clone, PartialEq, Eq)]
pub enum DispatchFeePayment {
/// The dispacth 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)]
pub struct MessageDispatchResult {
/// Unspent dispatch weight. This weight that will be deducted from total delivery transaction
/// weight, thus reducing the transaction cost. This shall not be zero in (at least) two cases:
///
/// 1) if message has been dispatched successfully, but post-dispatch weight is less than
/// the weight, declared by the message sender;
/// 2) if message has not been dispatched at all.
pub unspent_weight: Weight,
/// Whether the message dispatch fee has been paid during dispatch. This will be true if your
/// configuration supports pay-dispatch-fee-at-target-chain option and message sender has enabled
/// this option.
pub dispatch_fee_paid_during_dispatch: bool,
}
@@ -72,7 +72,7 @@ mod tests {
#[test] #[test]
fn should_encode_raw_message() { fn should_encode_raw_message() {
// given // given
let msg = "01000000e88514000000000002d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d3c040130000000000000000000000000"; let msg = "01000000e88514000000000002d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d003c040130000000000000000000000000";
let encode_message = EncodeMessage::from_iter(vec!["encode-message", "MillauToRialto", "raw", msg]); let encode_message = EncodeMessage::from_iter(vec!["encode-message", "MillauToRialto", "raw", msg]);
// when // when
@@ -101,6 +101,6 @@ mod tests {
let hex = encode_message.encode().unwrap(); let hex = encode_message.encode().unwrap();
// then // then
assert_eq!(format!("{:?}", hex), "0x01000000e88514000000000002d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d3c040130000000000000000000000000"); assert_eq!(format!("{:?}", hex), "0x01000000e88514000000000002d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d003c040130000000000000000000000000");
} }
} }
@@ -22,6 +22,7 @@ use crate::cli::{
TargetSigningParams, TargetSigningParams,
}; };
use bp_message_dispatch::{CallOrigin, MessagePayload}; use bp_message_dispatch::{CallOrigin, MessagePayload};
use bp_runtime::messages::DispatchFeePayment;
use codec::Encode; use codec::Encode;
use frame_support::{dispatch::GetDispatchInfo, weights::Weight}; use frame_support::{dispatch::GetDispatchInfo, weights::Weight};
use relay_substrate_client::{Chain, TransactionSignScheme}; use relay_substrate_client::{Chain, TransactionSignScheme};
@@ -211,6 +212,7 @@ where
spec_version, spec_version,
weight, weight,
origin, origin,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
call: HexBytes::encode(call), call: HexBytes::encode(call),
}; };
@@ -222,12 +224,14 @@ where
spec_version, spec_version,
weight, weight,
origin, origin,
dispatch_fee_payment,
call, call,
} = payload; } = payload;
MessagePayload { MessagePayload {
spec_version, spec_version,
weight, weight,
origin, origin,
dispatch_fee_payment,
call: call.0, call: call.0,
} }
} }
@@ -268,6 +272,7 @@ mod tests {
spec_version: relay_millau_client::Millau::RUNTIME_VERSION.spec_version, spec_version: relay_millau_client::Millau::RUNTIME_VERSION.spec_version,
weight: 1345000, weight: 1345000,
origin: CallOrigin::SourceAccount(sp_keyring::AccountKeyring::Alice.to_account_id()), origin: CallOrigin::SourceAccount(sp_keyring::AccountKeyring::Alice.to_account_id()),
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
call: hex!("0401081234").to_vec(), call: hex!("0401081234").to_vec(),
} }
); );
@@ -311,6 +316,7 @@ mod tests {
sp_keyring::AccountKeyring::Bob.into(), sp_keyring::AccountKeyring::Bob.into(),
signature, signature,
), ),
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
call: hex!("0701081234").to_vec(), call: hex!("0701081234").to_vec(),
} }
); );
@@ -204,7 +204,7 @@ mod tests {
// reserved for messages dispatch allows dispatch of non-trivial messages. // reserved for messages dispatch allows dispatch of non-trivial messages.
// //
// Any significant change in this values should attract additional attention. // Any significant change in this values should attract additional attention.
(1024, 216_583_333_334), (814, 216_583_333_334),
); );
} }
} }