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

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

git-subtree-dir: bridges
git-subtree-split: 23dda6248236b27f20d76cbedc30e189cc6f736c
This commit is contained in:
Svyatoslav Nikolsky
2021-06-25 16:45:02 +03:00
committed by GitHub
parent 022e8bc11c
commit feefc34567
167 changed files with 7023 additions and 3239 deletions
@@ -7,6 +7,7 @@ edition = "2018"
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
[dependencies]
bitvec = { version = "0.20", default-features = false, features = ["alloc"] }
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false }
log = { version = "0.4.14", default-features = false }
num-traits = { version = "0.2", default-features = false }
@@ -14,6 +15,7 @@ serde = { version = "1.0.101", optional = true, features = ["derive"] }
# Bridge dependencies
bp-message-dispatch = { path = "../../primitives/message-dispatch", default-features = false }
bp-messages = { path = "../../primitives/messages", default-features = false }
bp-rialto = { path = "../../primitives/chain-rialto", default-features = false }
bp-runtime = { path = "../../primitives/runtime", default-features = false }
@@ -36,6 +38,7 @@ pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "m
[features]
default = ["std"]
std = [
"bp-message-dispatch/std",
"bp-messages/std",
"bp-runtime/std",
"bp-rialto/std",
+27 -3
View File
@@ -101,7 +101,14 @@ the `MessageAccepted` event is emitted in the `send_message()` transaction. The
message lane identifier and nonce that has been assigned to the message. When a message is delivered
to the target chain, the `MessagesDelivered` event is emitted from the
`receive_messages_delivery_proof()` transaction. The `MessagesDelivered` contains the message lane
identifier and inclusive range of delivered message nonces.
identifier, inclusive range of delivered message nonces and their single-bit dispatch results.
Please note that the meaning of the 'dispatch result' is determined by the message dispatcher at
the target chain. For example, in case of immediate call dispatcher it will be the `true` if call
has been successfully dispatched and `false` if it has only been delivered. This simple mechanism
built into the messages module allows building basic bridge applications, which only care whether
their messages have been successfully dispatched or not. More sophisticated applications may use
their own dispatch result delivery mechanism to deliver something larger than single bit.
### How to plug-in Messages Module to Send Messages to the Bridged Chain?
@@ -152,7 +159,7 @@ all required traits and will simply reject all transactions, related to outbound
The `pallet_bridge_messages::Config` trait has 2 main associated types that are used to work with
inbound messages. The `pallet_bridge_messages::Config::SourceHeaderChain` defines how we see the
bridged chain as the source or our inbound messages. When relayer sends us a delivery transaction,
bridged chain as the source or our inbound messages. When relayer sends us a delivery transaction,
this implementation must be able to parse and verify the proof of messages wrapped in this
transaction. Normally, you would reuse the same (configurable) type on all chains that are sending
messages to the same bridged chain.
@@ -194,7 +201,7 @@ message needs to be read. So there's another
When choosing values for these parameters, you must also keep in mind that if proof in your scheme
is based on finality of headers (and it is the most obvious option for Substrate-based chains with
finality notion), then choosing too small values for these parameters may cause significant delays
in message delivery. That's because there too many actors involved in this scheme: 1) authorities
in message delivery. That's because there are too many actors involved in this scheme: 1) authorities
that are finalizing headers of the target chain need to finalize header with non-empty map; 2) the
headers relayer then needs to submit this header and its finality proof to the source chain; 3) the
messages relayer must then send confirmation transaction (storage proof of this map) to the source
@@ -347,6 +354,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
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
#### Related benchmarks
@@ -17,16 +17,25 @@
//! Messages pallet benchmarking.
use crate::weights_ext::EXPECTED_DEFAULT_MESSAGE_LENGTH;
use crate::{inbound_lane::InboundLaneStorage, inbound_lane_storage, outbound_lane, Call, Instance};
use crate::{
inbound_lane::InboundLaneStorage, inbound_lane_storage, outbound_lane, outbound_lane::ReceivalConfirmationResult,
Call, Instance,
};
use bp_messages::{
source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, InboundLaneData, LaneId, MessageData,
MessageNonce, OutboundLaneData, UnrewardedRelayersState,
source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, DeliveredMessages, InboundLaneData, LaneId,
MessageData, MessageNonce, OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState,
};
use bp_runtime::messages::DispatchFeePayment;
use frame_benchmarking::{account, benchmarks_instance};
use frame_support::{traits::Get, weights::Weight};
use frame_system::RawOrigin;
use sp_std::{collections::btree_map::BTreeMap, convert::TryInto, ops::RangeInclusive, prelude::*};
use sp_std::{
collections::{btree_map::BTreeMap, vec_deque::VecDeque},
convert::TryInto,
ops::RangeInclusive,
prelude::*,
};
/// Fee paid by submitter for single message delivery.
pub const MESSAGE_FEE: u64 = 10_000_000_000;
@@ -67,6 +76,8 @@ pub struct MessageProofParams {
pub outbound_lane_data: Option<OutboundLaneData>,
/// Proof size requirements.
pub size: ProofSize,
/// Where the fee for dispatching message is paid?
pub dispatch_fee_payment: DispatchFeePayment,
}
/// Benchmark-specific message delivery proof parameters.
@@ -108,6 +119,8 @@ pub trait Config<I: Instance>: crate::Config<I> {
fn prepare_message_delivery_proof(
params: MessageDeliveryProofParams<Self::AccountId>,
) -> <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! {
@@ -242,7 +255,8 @@ benchmarks_instance! {
// * 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 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.
receive_single_message_proof {
@@ -257,6 +271,7 @@ benchmarks_instance! {
message_nonces: 21..=21,
outbound_lane_data: None,
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)
verify {
@@ -264,13 +279,15 @@ benchmarks_instance! {
crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()),
21,
);
assert!(T::is_message_dispatched(21));
}
// Benchmark `receive_messages_proof` extrinsic with two minimal-weight messages 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 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
// `weight(receive_two_messages_proof) - weight(receive_single_message_proof)`.
@@ -288,6 +305,7 @@ benchmarks_instance! {
message_nonces: 21..=22,
outbound_lane_data: None,
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)
verify {
@@ -295,13 +313,15 @@ benchmarks_instance! {
crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()),
22,
);
assert!(T::is_message_dispatched(22));
}
// Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions:
// * proof includes 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 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
// `weight(receive_single_message_proof_with_outbound_lane_state) - weight(receive_single_message_proof)`.
@@ -323,6 +343,7 @@ benchmarks_instance! {
latest_generated_nonce: 21,
}),
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)
verify {
@@ -334,6 +355,7 @@ benchmarks_instance! {
crate::Pallet::<T, I>::inbound_latest_confirmed_nonce(T::bench_lane_id()),
20,
);
assert!(T::is_message_dispatched(21));
}
// Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions:
@@ -357,6 +379,7 @@ benchmarks_instance! {
message_nonces: 21..=21,
outbound_lane_data: None,
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)
verify {
@@ -364,6 +387,7 @@ benchmarks_instance! {
crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()),
21,
);
assert!(T::is_message_dispatched(21));
}
// Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions:
@@ -389,6 +413,7 @@ benchmarks_instance! {
message_nonces: 21..=21,
outbound_lane_data: None,
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)
verify {
@@ -396,6 +421,40 @@ benchmarks_instance! {
crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()),
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:
@@ -420,7 +479,10 @@ benchmarks_instance! {
let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams {
lane: T::bench_lane_id(),
inbound_lane_data: InboundLaneData {
relayers: vec![(1, 1, relayer_id.clone())].into_iter().collect(),
relayers: vec![UnrewardedRelayer {
relayer: relayer_id.clone(),
messages: DeliveredMessages::new(1, true),
}].into_iter().collect(),
last_confirmed_nonce: 0,
},
size: ProofSize::Minimal(0),
@@ -455,10 +517,15 @@ benchmarks_instance! {
messages_in_oldest_entry: 2,
total_messages: 2,
};
let mut delivered_messages = DeliveredMessages::new(1, true);
delivered_messages.note_dispatched_message(true);
let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams {
lane: T::bench_lane_id(),
inbound_lane_data: InboundLaneData {
relayers: vec![(1, 2, relayer_id.clone())].into_iter().collect(),
relayers: vec![UnrewardedRelayer {
relayer: relayer_id.clone(),
messages: delivered_messages,
}].into_iter().collect(),
last_confirmed_nonce: 0,
},
size: ProofSize::Minimal(0),
@@ -496,8 +563,14 @@ benchmarks_instance! {
lane: T::bench_lane_id(),
inbound_lane_data: InboundLaneData {
relayers: vec![
(1, 1, relayer1_id.clone()),
(2, 2, relayer2_id.clone()),
UnrewardedRelayer {
relayer: relayer1_id.clone(),
messages: DeliveredMessages::new(1, true),
},
UnrewardedRelayer {
relayer: relayer2_id.clone(),
messages: DeliveredMessages::new(2, true),
},
].into_iter().collect(),
last_confirmed_nonce: 0,
},
@@ -569,6 +642,7 @@ benchmarks_instance! {
message_nonces: 21..=(20 + i as MessageNonce),
outbound_lane_data: None,
size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH),
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
});
}: receive_messages_proof(
RawOrigin::Signed(relayer_id_on_target),
@@ -606,6 +680,7 @@ benchmarks_instance! {
message_nonces: 21..=21,
outbound_lane_data: None,
size: ProofSize::HasExtraNodes(i as _),
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
});
}: receive_messages_proof(
RawOrigin::Signed(relayer_id_on_target),
@@ -643,6 +718,7 @@ benchmarks_instance! {
message_nonces: 21..=21,
outbound_lane_data: None,
size: ProofSize::HasLargeLeaf(i as _),
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
});
}: receive_messages_proof(
RawOrigin::Signed(relayer_id_on_target),
@@ -686,6 +762,7 @@ benchmarks_instance! {
latest_generated_nonce: 21,
}),
size: ProofSize::Minimal(0),
dispatch_fee_payment: DispatchFeePayment::AtTargetChain,
});
}: receive_messages_proof(
RawOrigin::Signed(relayer_id_on_target),
@@ -728,10 +805,17 @@ benchmarks_instance! {
messages_in_oldest_entry: 1,
total_messages: i as MessageNonce,
};
let mut delivered_messages = DeliveredMessages::new(1, true);
for nonce in 2..=i {
delivered_messages.note_dispatched_message(true);
}
let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams {
lane: T::bench_lane_id(),
inbound_lane_data: InboundLaneData {
relayers: vec![(1, i as MessageNonce, relayer_id.clone())].into_iter().collect(),
relayers: vec![UnrewardedRelayer {
relayer: relayer_id.clone(),
messages: delivered_messages,
}].into_iter().collect(),
last_confirmed_nonce: 0,
},
size: ProofSize::Minimal(0),
@@ -776,7 +860,10 @@ benchmarks_instance! {
relayers: relayers
.keys()
.enumerate()
.map(|(j, relayer_id)| (j as MessageNonce + 1, j as MessageNonce + 1, relayer_id.clone()))
.map(|(j, relayer)| UnrewardedRelayer {
relayer: relayer.clone(),
messages: DeliveredMessages::new(j as MessageNonce + 1, true),
})
.collect(),
last_confirmed_nonce: 0,
},
@@ -808,13 +895,29 @@ fn send_regular_message_with_payload<T: Config<I>, I: Instance>(payload: Vec<u8>
fn confirm_message_delivery<T: Config<I>, I: Instance>(nonce: MessageNonce) {
let mut outbound_lane = outbound_lane::<T, I>(T::bench_lane_id());
assert!(outbound_lane.confirm_delivery(nonce).is_some());
let latest_received_nonce = outbound_lane.data().latest_received_nonce;
let mut relayers = VecDeque::with_capacity((nonce - latest_received_nonce) as usize);
for nonce in latest_received_nonce + 1..=nonce {
relayers.push_back(UnrewardedRelayer {
relayer: (),
messages: DeliveredMessages::new(nonce, true),
});
}
assert!(matches!(
outbound_lane.confirm_delivery(nonce, &relayers),
ReceivalConfirmationResult::ConfirmedMessages(_),
));
}
fn receive_messages<T: Config<I>, I: Instance>(nonce: MessageNonce) {
let mut inbound_lane_storage = inbound_lane_storage::<T, I>(T::bench_lane_id());
inbound_lane_storage.set_data(InboundLaneData {
relayers: vec![(1, nonce, T::bridged_relayer_id())].into_iter().collect(),
relayers: vec![UnrewardedRelayer {
relayer: T::bridged_relayer_id(),
messages: DeliveredMessages::new(nonce, true),
}]
.into_iter()
.collect(),
last_confirmed_nonce: 0,
});
}
@@ -18,8 +18,10 @@
use bp_messages::{
target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch},
InboundLaneData, LaneId, MessageKey, MessageNonce, OutboundLaneData,
DeliveredMessages, InboundLaneData, LaneId, MessageKey, MessageNonce, OutboundLaneData, UnrewardedRelayer,
};
use bp_runtime::messages::MessageDispatchResult;
use frame_support::RuntimeDebug;
use sp_std::prelude::PartialEq;
/// Inbound lane storage.
@@ -27,7 +29,7 @@ pub trait InboundLaneStorage {
/// Delivery and dispatch fee type on source chain.
type MessageFee;
/// Id of relayer on source chain.
type Relayer: PartialEq;
type Relayer: Clone + PartialEq;
/// Lane id.
fn id(&self) -> LaneId;
@@ -41,6 +43,22 @@ pub trait InboundLaneStorage {
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.
pub struct InboundLane<S> {
storage: S,
@@ -71,7 +89,7 @@ impl<S: InboundLaneStorage> InboundLane<S> {
while data
.relayers
.front()
.map(|(_, nonce_high, _)| *nonce_high <= new_confirmed_nonce)
.map(|entry| entry.messages.end <= new_confirmed_nonce)
.unwrap_or(false)
{
data.relayers.pop_front();
@@ -79,8 +97,12 @@ impl<S: InboundLaneStorage> InboundLane<S> {
// Secondly, update the next record with lower nonce equal to new confirmed nonce if needed.
// Note: There will be max. 1 record to update as we don't allow messages from relayers to overlap.
match data.relayers.front_mut() {
Some((nonce_low, _, _)) if *nonce_low < new_confirmed_nonce => {
*nonce_low = new_confirmed_nonce + 1;
Some(entry) if entry.messages.begin < new_confirmed_nonce => {
entry.messages.dispatch_results = entry
.messages
.dispatch_results
.split_off((new_confirmed_nonce + 1 - entry.messages.begin) as _);
entry.messages.begin = new_confirmed_nonce + 1;
}
_ => {}
}
@@ -90,51 +112,61 @@ impl<S: InboundLaneStorage> InboundLane<S> {
}
/// Receive new message.
pub fn receive_message<P: MessageDispatch<S::MessageFee>>(
pub fn receive_message<P: MessageDispatch<AccountId, S::MessageFee>, AccountId>(
&mut self,
relayer: S::Relayer,
relayer_at_bridged_chain: &S::Relayer,
relayer_at_this_chain: &AccountId,
nonce: MessageNonce,
message_data: DispatchMessageData<P::DispatchPayload, S::MessageFee>,
) -> bool {
) -> ReceivalResult {
let mut data = self.storage.data();
let is_correct_message = nonce == data.last_delivered_nonce() + 1;
if !is_correct_message {
return false;
return ReceivalResult::InvalidNonce;
}
// 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() {
return false;
return ReceivalResult::TooManyUnrewardedRelayers;
}
// if there are more unconfirmed messages than we may accept, reject this message
let unconfirmed_messages_count = nonce.saturating_sub(data.last_confirmed_nonce);
if unconfirmed_messages_count > self.storage.max_unconfirmed_messages() {
return false;
return ReceivalResult::TooManyUnconfirmedMessages;
}
// dispatch message before updating anything in the storage. If dispatch would panic,
// (which should not happen in the runtime) then we simply won't consider message as
// delivered (no changes to the inbound lane storage have been made).
let dispatch_result = P::dispatch(
relayer_at_this_chain,
DispatchMessage {
key: MessageKey {
lane_id: self.storage.id(),
nonce,
},
data: message_data,
},
);
// now let's update inbound lane storage
let push_new = match data.relayers.back_mut() {
Some((_, nonce_high, last_relayer)) if last_relayer == &relayer => {
*nonce_high = nonce;
Some(entry) if entry.relayer == *relayer_at_bridged_chain => {
entry.messages.note_dispatched_message(dispatch_result.dispatch_result);
false
}
_ => true,
};
if push_new {
data.relayers.push_back((nonce, nonce, relayer));
data.relayers.push_back(UnrewardedRelayer {
relayer: (*relayer_at_bridged_chain).clone(),
messages: DeliveredMessages::new(nonce, dispatch_result.dispatch_result),
});
}
self.storage.set_data(data);
P::dispatch(DispatchMessage {
key: MessageKey {
lane_id: self.storage.id(),
nonce,
},
data: message_data,
});
true
ReceivalResult::Dispatched(dispatch_result)
}
}
@@ -144,8 +176,8 @@ mod tests {
use crate::{
inbound_lane,
mock::{
message_data, run_test, TestMessageDispatch, TestRuntime, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_RELAYER_A,
TEST_RELAYER_B, TEST_RELAYER_C,
dispatch_result, message_data, run_test, unrewarded_relayer, TestMessageDispatch, TestRuntime,
REGULAR_PAYLOAD, TEST_LANE_ID, TEST_RELAYER_A, TEST_RELAYER_B, TEST_RELAYER_C,
},
DefaultInstance, RuntimeInboundLaneStorage,
};
@@ -154,11 +186,15 @@ mod tests {
lane: &mut InboundLane<RuntimeInboundLaneStorage<TestRuntime, DefaultInstance>>,
nonce: MessageNonce,
) {
assert!(lane.receive_message::<TestMessageDispatch>(
TEST_RELAYER_A,
nonce,
message_data(REGULAR_PAYLOAD).into()
));
assert_eq!(
lane.receive_message::<TestMessageDispatch, _>(
&TEST_RELAYER_A,
&TEST_RELAYER_A,
nonce,
message_data(REGULAR_PAYLOAD).into()
),
ReceivalResult::Dispatched(dispatch_result(0))
);
}
#[test]
@@ -213,7 +249,10 @@ mod tests {
receive_regular_message(&mut lane, 2);
receive_regular_message(&mut lane, 3);
assert_eq!(lane.storage.data().last_confirmed_nonce, 0);
assert_eq!(lane.storage.data().relayers, vec![(1, 3, TEST_RELAYER_A)]);
assert_eq!(
lane.storage.data().relayers,
vec![unrewarded_relayer(1, 3, TEST_RELAYER_A)]
);
assert_eq!(
lane.receive_state_update(OutboundLaneData {
@@ -223,7 +262,10 @@ mod tests {
Some(2),
);
assert_eq!(lane.storage.data().last_confirmed_nonce, 2);
assert_eq!(lane.storage.data().relayers, vec![(3, 3, TEST_RELAYER_A)]);
assert_eq!(
lane.storage.data().relayers,
vec![unrewarded_relayer(3, 3, TEST_RELAYER_A)]
);
assert_eq!(
lane.receive_state_update(OutboundLaneData {
@@ -244,10 +286,16 @@ mod tests {
let mut seed_storage_data = lane.storage.data();
// Prepare data
seed_storage_data.last_confirmed_nonce = 0;
seed_storage_data.relayers.push_back((1, 1, TEST_RELAYER_A));
seed_storage_data
.relayers
.push_back(unrewarded_relayer(1, 1, TEST_RELAYER_A));
// Simulate messages batch (2, 3, 4) from relayer #2
seed_storage_data.relayers.push_back((2, 4, TEST_RELAYER_B));
seed_storage_data.relayers.push_back((5, 5, TEST_RELAYER_C));
seed_storage_data
.relayers
.push_back(unrewarded_relayer(2, 4, TEST_RELAYER_B));
seed_storage_data
.relayers
.push_back(unrewarded_relayer(5, 5, TEST_RELAYER_C));
lane.storage.set_data(seed_storage_data);
// Check
assert_eq!(
@@ -260,7 +308,10 @@ mod tests {
assert_eq!(lane.storage.data().last_confirmed_nonce, 3);
assert_eq!(
lane.storage.data().relayers,
vec![(4, 4, TEST_RELAYER_B), (5, 5, TEST_RELAYER_C)]
vec![
unrewarded_relayer(4, 4, TEST_RELAYER_B),
unrewarded_relayer(5, 5, TEST_RELAYER_C)
]
);
});
}
@@ -269,11 +320,15 @@ mod tests {
fn fails_to_receive_message_with_incorrect_nonce() {
run_test(|| {
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
assert!(!lane.receive_message::<TestMessageDispatch>(
TEST_RELAYER_A,
10,
message_data(REGULAR_PAYLOAD).into()
));
assert_eq!(
lane.receive_message::<TestMessageDispatch, _>(
&TEST_RELAYER_A,
&TEST_RELAYER_A,
10,
message_data(REGULAR_PAYLOAD).into()
),
ReceivalResult::InvalidNonce
);
assert_eq!(lane.storage.data().last_delivered_nonce(), 0);
});
}
@@ -284,29 +339,35 @@ mod tests {
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
let max_nonce = <TestRuntime as crate::Config>::MaxUnrewardedRelayerEntriesAtInboundLane::get();
for current_nonce in 1..max_nonce + 1 {
assert!(lane.receive_message::<TestMessageDispatch>(
TEST_RELAYER_A + current_nonce,
current_nonce,
message_data(REGULAR_PAYLOAD).into()
));
assert_eq!(
lane.receive_message::<TestMessageDispatch, _>(
&(TEST_RELAYER_A + current_nonce),
&(TEST_RELAYER_A + current_nonce),
current_nonce,
message_data(REGULAR_PAYLOAD).into()
),
ReceivalResult::Dispatched(dispatch_result(0))
);
}
// Fails to dispatch new message from different than latest relayer.
assert_eq!(
false,
lane.receive_message::<TestMessageDispatch>(
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,
message_data(REGULAR_PAYLOAD).into()
)
),
ReceivalResult::TooManyUnrewardedRelayers,
);
// Fails to dispatch new messages from latest relayer. Prevents griefing attacks.
assert_eq!(
false,
lane.receive_message::<TestMessageDispatch>(
TEST_RELAYER_A + max_nonce,
lane.receive_message::<TestMessageDispatch, _>(
&(TEST_RELAYER_A + max_nonce),
&(TEST_RELAYER_A + max_nonce),
max_nonce + 1,
message_data(REGULAR_PAYLOAD).into()
)
),
ReceivalResult::TooManyUnrewardedRelayers,
);
});
}
@@ -317,29 +378,35 @@ mod tests {
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
let max_nonce = <TestRuntime as crate::Config>::MaxUnconfirmedMessagesAtInboundLane::get();
for current_nonce in 1..=max_nonce {
assert!(lane.receive_message::<TestMessageDispatch>(
TEST_RELAYER_A,
current_nonce,
message_data(REGULAR_PAYLOAD).into()
));
assert_eq!(
lane.receive_message::<TestMessageDispatch, _>(
&TEST_RELAYER_A,
&TEST_RELAYER_A,
current_nonce,
message_data(REGULAR_PAYLOAD).into()
),
ReceivalResult::Dispatched(dispatch_result(0))
);
}
// Fails to dispatch new message from different than latest relayer.
assert_eq!(
false,
lane.receive_message::<TestMessageDispatch>(
TEST_RELAYER_B,
lane.receive_message::<TestMessageDispatch, _>(
&TEST_RELAYER_B,
&TEST_RELAYER_B,
max_nonce + 1,
message_data(REGULAR_PAYLOAD).into()
)
),
ReceivalResult::TooManyUnconfirmedMessages,
);
// Fails to dispatch new messages from latest relayer.
assert_eq!(
false,
lane.receive_message::<TestMessageDispatch>(
TEST_RELAYER_A,
lane.receive_message::<TestMessageDispatch, _>(
&TEST_RELAYER_A,
&TEST_RELAYER_A,
max_nonce + 1,
message_data(REGULAR_PAYLOAD).into()
)
),
ReceivalResult::TooManyUnconfirmedMessages,
);
});
}
@@ -348,24 +415,40 @@ mod tests {
fn correctly_receives_following_messages_from_two_relayers_alternately() {
run_test(|| {
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
assert!(lane.receive_message::<TestMessageDispatch>(
TEST_RELAYER_A,
1,
message_data(REGULAR_PAYLOAD).into()
));
assert!(lane.receive_message::<TestMessageDispatch>(
TEST_RELAYER_B,
2,
message_data(REGULAR_PAYLOAD).into()
));
assert!(lane.receive_message::<TestMessageDispatch>(
TEST_RELAYER_A,
3,
message_data(REGULAR_PAYLOAD).into()
));
assert_eq!(
lane.receive_message::<TestMessageDispatch, _>(
&TEST_RELAYER_A,
&TEST_RELAYER_A,
1,
message_data(REGULAR_PAYLOAD).into()
),
ReceivalResult::Dispatched(dispatch_result(0))
);
assert_eq!(
lane.receive_message::<TestMessageDispatch, _>(
&TEST_RELAYER_B,
&TEST_RELAYER_B,
2,
message_data(REGULAR_PAYLOAD).into()
),
ReceivalResult::Dispatched(dispatch_result(0))
);
assert_eq!(
lane.receive_message::<TestMessageDispatch, _>(
&TEST_RELAYER_A,
&TEST_RELAYER_A,
3,
message_data(REGULAR_PAYLOAD).into()
),
ReceivalResult::Dispatched(dispatch_result(0))
);
assert_eq!(
lane.storage.data().relayers,
vec![(1, 1, TEST_RELAYER_A), (2, 2, TEST_RELAYER_B), (3, 3, TEST_RELAYER_A)]
vec![
unrewarded_relayer(1, 1, TEST_RELAYER_A),
unrewarded_relayer(2, 2, TEST_RELAYER_B),
unrewarded_relayer(3, 3, TEST_RELAYER_A)
]
);
});
}
@@ -374,14 +457,23 @@ mod tests {
fn rejects_same_message_from_two_different_relayers() {
run_test(|| {
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
assert!(lane.receive_message::<TestMessageDispatch>(
TEST_RELAYER_A,
1,
message_data(REGULAR_PAYLOAD).into()
));
assert_eq!(
false,
lane.receive_message::<TestMessageDispatch>(TEST_RELAYER_B, 1, message_data(REGULAR_PAYLOAD).into())
lane.receive_message::<TestMessageDispatch, _>(
&TEST_RELAYER_A,
&TEST_RELAYER_A,
1,
message_data(REGULAR_PAYLOAD).into()
),
ReceivalResult::Dispatched(dispatch_result(0))
);
assert_eq!(
lane.receive_message::<TestMessageDispatch, _>(
&TEST_RELAYER_B,
&TEST_RELAYER_B,
1,
message_data(REGULAR_PAYLOAD).into()
),
ReceivalResult::InvalidNonce,
);
});
}
@@ -394,4 +486,22 @@ mod tests {
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))
);
});
}
}
+451 -108
View File
@@ -34,28 +34,34 @@
//! or some benchmarks assumptions are broken for your runtime.
#![cfg_attr(not(feature = "std"), no_std)]
// Generated by `decl_event!`
#![allow(clippy::unused_unit)]
pub use crate::weights_ext::{
ensure_able_to_receive_confirmation, ensure_able_to_receive_message, ensure_weights_are_correct, WeightInfoExt,
EXPECTED_DEFAULT_MESSAGE_LENGTH,
};
use crate::inbound_lane::{InboundLane, InboundLaneStorage};
use crate::outbound_lane::{OutboundLane, OutboundLaneStorage};
use crate::inbound_lane::{InboundLane, InboundLaneStorage, ReceivalResult};
use crate::outbound_lane::{OutboundLane, OutboundLaneStorage, ReceivalConfirmationResult};
use crate::weights::WeightInfo;
use bp_messages::{
source_chain::{LaneMessageVerifier, MessageDeliveryAndDispatchPayment, RelayersRewards, TargetHeaderChain},
source_chain::{
LaneMessageVerifier, MessageDeliveryAndDispatchPayment, OnDeliveryConfirmed, RelayersRewards, TargetHeaderChain,
},
target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, SourceHeaderChain},
total_unrewarded_messages, InboundLaneData, LaneId, MessageData, MessageKey, MessageNonce, MessagePayload,
OutboundLaneData, Parameter as MessagesParameter, UnrewardedRelayersState,
total_unrewarded_messages, DeliveredMessages, InboundLaneData, LaneId, MessageData, MessageKey, MessageNonce,
OperatingMode, OutboundLaneData, Parameter as MessagesParameter, UnrewardedRelayersState,
};
use bp_runtime::Size;
use codec::{Decode, Encode};
use frame_support::{
decl_error, decl_event, decl_module, decl_storage, ensure,
decl_error, decl_event, decl_module, decl_storage,
dispatch::DispatchResultWithPostInfo,
ensure, fail,
traits::Get,
weights::{DispatchClass, Weight},
weights::{DispatchClass, Pays, PostDispatchInfo, Weight},
Parameter, StorageMap,
};
use frame_system::{ensure_signed, RawOrigin};
@@ -142,13 +148,19 @@ pub trait Config<I = DefaultInstance>: frame_system::Config {
type LaneMessageVerifier: LaneMessageVerifier<Self::AccountId, Self::OutboundPayload, Self::OutboundMessageFee>;
/// Message delivery payment.
type MessageDeliveryAndDispatchPayment: MessageDeliveryAndDispatchPayment<Self::AccountId, Self::OutboundMessageFee>;
/// Handler for delivered messages.
type OnDeliveryConfirmed: OnDeliveryConfirmed;
// Types that are used by inbound_lane (on target chain).
/// Source header chain, as it is represented on target chain.
type SourceHeaderChain: SourceHeaderChain<Self::InboundMessageFee>;
/// 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.
@@ -178,6 +190,8 @@ decl_error! {
InvalidMessagesDispatchWeight,
/// Invalid messages delivery proof has been submitted.
InvalidMessagesDeliveryProof,
/// The bridged chain has invalid `UnrewardedRelayers` in its storage (fatal for the lane).
InvalidUnrewardedRelayers,
/// The relayer has declared invalid unrewarded relayers state in the `receive_messages_delivery_proof` call.
InvalidUnrewardedRelayersState,
/// The message someone is trying to work with (i.e. increase fee) is already-delivered.
@@ -196,8 +210,10 @@ decl_storage! {
/// runtime methods may still be used to do that (i.e. democracy::referendum to update halt
/// flag directly or call the `halt_operations`).
pub PalletOwner get(fn module_owner): Option<T::AccountId>;
/// If true, all pallet transactions are failed immediately.
pub IsHalted get(fn is_halted) config(): bool;
/// The current operating mode of the pallet.
///
/// Depending on the mode either all, some, or no transactions will be allowed.
pub PalletOperatingMode get(fn operating_mode) config(): OperatingMode;
/// Map of lane id => inbound lane data.
pub InboundLanes: map hasher(blake2_128_concat) LaneId => InboundLaneData<T::InboundRelayer>;
/// Map of lane id => outbound lane data.
@@ -226,8 +242,8 @@ decl_event!(
ParameterUpdated(Parameter),
/// Message has been accepted and is waiting to be delivered.
MessageAccepted(LaneId, MessageNonce),
/// Messages in the inclusive range have been delivered and processed by the bridged chain.
MessagesDelivered(LaneId, MessageNonce, MessageNonce),
/// Messages in the inclusive range have been delivered to the bridged chain.
MessagesDelivered(LaneId, DeliveredMessages),
/// Phantom member, never used.
Dummy(PhantomData<(AccountId, I)>),
}
@@ -264,19 +280,18 @@ decl_module! {
}
}
/// Halt or resume all pallet operations.
/// Halt or resume all/some pallet operations.
///
/// May only be called either by root, or by `PalletOwner`.
#[weight = (T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational)]
pub fn set_operational(origin, operational: bool) {
pub fn set_operating_mode(origin, operating_mode: OperatingMode) {
ensure_owner_or_root::<T, I>(origin)?;
<IsHalted<I>>::put(operational);
if operational {
log::info!(target: "runtime::bridge-messages", "Resuming pallet operations.");
} else {
log::warn!(target: "runtime::bridge-messages", "Stopping pallet operations.");
}
<PalletOperatingMode<I>>::put(operating_mode);
log::info!(
target: "runtime::bridge-messages",
"Setting messages pallet operating mode to {:?}.",
operating_mode,
);
}
/// Update pallet parameter.
@@ -299,7 +314,7 @@ decl_module! {
payload: T::OutboundPayload,
delivery_and_dispatch_fee: T::OutboundMessageFee,
) -> DispatchResult {
ensure_operational::<T, I>()?;
ensure_normal_operating_mode::<T, I>()?;
let submitter = origin.into().map_err(|_| BadOrigin)?;
// let's first check if message can be delivered to target chain
@@ -382,6 +397,7 @@ decl_module! {
nonce: MessageNonce,
additional_fee: T::OutboundMessageFee,
) -> DispatchResult {
ensure_not_halted::<T, I>()?;
// if someone tries to pay for already-delivered message, we're rejecting this intention
// (otherwise this additional fee will be locked forever in relayers fund)
//
@@ -434,13 +450,13 @@ decl_module! {
#[weight = T::WeightInfo::receive_messages_proof_weight(proof, *messages_count, *dispatch_weight)]
pub fn receive_messages_proof(
origin,
relayer_id: T::InboundRelayer,
relayer_id_at_bridged_chain: T::InboundRelayer,
proof: MessagesProofOf<T, I>,
messages_count: u32,
dispatch_weight: Weight,
) -> DispatchResult {
ensure_operational::<T, I>()?;
let _ = ensure_signed(origin)?;
) -> DispatchResultWithPostInfo {
ensure_not_halted::<T, I>()?;
let relayer_id_at_this_chain = ensure_signed(origin)?;
// reject transactions that are declaring too many messages
ensure!(
@@ -448,6 +464,23 @@ decl_module! {
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
let messages = verify_and_decode_messages_proof::<
T::SourceHeaderChain,
@@ -507,20 +540,57 @@ decl_module! {
debug_assert_eq!(message.key.lane_id, lane_id);
total_messages += 1;
if lane.receive_message::<T::MessageDispatch>(relayer_id.clone(), message.key.nonce, message.data) {
valid_messages += 1;
}
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;
(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!(
target: "runtime::bridge-messages",
"Received messages: total={}, valid={}",
"Received messages: total={}, valid={}. Weight used: {}/{}",
total_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.
@@ -530,7 +600,7 @@ decl_module! {
proof: MessagesDeliveryProofOf<T, I>,
relayers_state: UnrewardedRelayersState,
) -> DispatchResult {
ensure_operational::<T, I>()?;
ensure_not_halted::<T, I>()?;
let confirmation_relayer = ensure_signed(origin)?;
let (lane_id, lane_data) = T::TargetHeaderChain::verify_messages_delivery_proof(proof).map_err(|err| {
@@ -556,19 +626,36 @@ decl_module! {
let mut lane = outbound_lane::<T, I>(lane_id);
let mut relayers_rewards: RelayersRewards<_, T::OutboundMessageFee> = RelayersRewards::new();
let last_delivered_nonce = lane_data.last_delivered_nonce();
let received_range = lane.confirm_delivery(last_delivered_nonce);
if let Some(received_range) = received_range {
Self::deposit_event(RawEvent::MessagesDelivered(lane_id, received_range.0, received_range.1));
let confirmed_messages = match lane.confirm_delivery(last_delivered_nonce, &lane_data.relayers) {
ReceivalConfirmationResult::ConfirmedMessages(confirmed_messages) => Some(confirmed_messages),
ReceivalConfirmationResult::NoNewConfirmations => None,
error => {
log::trace!(
target: "runtime::bridge-messages",
"Messages delivery proof contains invalid unrewarded relayers vec: {:?}",
error,
);
fail!(Error::<T, I>::InvalidUnrewardedRelayers);
},
};
if let Some(confirmed_messages) = confirmed_messages {
// handle messages delivery confirmation
T::OnDeliveryConfirmed::on_messages_delivered(&lane_id, &confirmed_messages);
// emit 'delivered' event
let received_range = confirmed_messages.begin..=confirmed_messages.end;
Self::deposit_event(RawEvent::MessagesDelivered(lane_id, confirmed_messages));
// remember to reward relayers that have delivered messages
// this loop is bounded by `T::MaxUnrewardedRelayerEntriesAtInboundLane` on the bridged chain
for (nonce_low, nonce_high, relayer) in lane_data.relayers {
let nonce_begin = sp_std::cmp::max(nonce_low, received_range.0);
let nonce_end = sp_std::cmp::min(nonce_high, received_range.1);
for entry in lane_data.relayers {
let nonce_begin = sp_std::cmp::max(entry.messages.begin, *received_range.start());
let nonce_end = sp_std::cmp::min(entry.messages.end, *received_range.end());
// loop won't proceed if current entry is ahead of received range (begin > end).
// this loop is bound by `T::MaxUnconfirmedMessagesAtInboundLane` on the bridged chain
let mut relayer_reward = relayers_rewards.entry(relayer).or_default();
let mut relayer_reward = relayers_rewards.entry(entry.relayer).or_default();
for nonce in nonce_begin..nonce_end + 1 {
let message_data = OutboundMessages::<T, I>::get(MessageKey {
lane_id,
@@ -603,9 +690,9 @@ decl_module! {
}
impl<T: Config<I>, I: Instance> Pallet<T, I> {
/// Get payload of given outbound message.
pub fn outbound_message_payload(lane: LaneId, nonce: MessageNonce) -> Option<MessagePayload> {
OutboundMessages::<T, I>::get(MessageKey { lane_id: lane, nonce }).map(|message_data| message_data.payload)
/// Get stored data of the outbound message with given nonce.
pub fn outbound_message_data(lane: LaneId, nonce: MessageNonce) -> Option<MessageData<T::OutboundMessageFee>> {
OutboundMessages::<T, I>::get(MessageKey { lane_id: lane, nonce })
}
/// Get nonce of latest generated message at given outbound lane.
@@ -633,7 +720,10 @@ impl<T: Config<I>, I: Instance> Pallet<T, I> {
let relayers = InboundLanes::<T, I>::get(&lane).relayers;
bp_messages::UnrewardedRelayersState {
unrewarded_relayer_entries: relayers.len() as _,
messages_in_oldest_entry: relayers.front().map(|(begin, end, _)| 1 + end - begin).unwrap_or(0),
messages_in_oldest_entry: relayers
.front()
.map(|entry| 1 + entry.messages.end - entry.messages.begin)
.unwrap_or(0),
total_messages: total_unrewarded_messages(&relayers).unwrap_or(MessageNonce::MAX),
}
}
@@ -665,24 +755,38 @@ impl<T: Config<I>, I: Instance> Pallet<T, I> {
/// trying to avoid here) - by using strings like "Instance2", "OutboundMessages", etc.
pub mod storage_keys {
use super::*;
use frame_support::storage::generator::StorageMap;
use frame_support::{traits::Instance, StorageHasher};
use sp_core::storage::StorageKey;
/// Storage key of the outbound message in the runtime storage.
pub fn message_key<T: Config<I>, I: Instance>(lane: &LaneId, nonce: MessageNonce) -> StorageKey {
let message_key = MessageKey { lane_id: *lane, nonce };
let raw_storage_key = OutboundMessages::<T, I>::storage_map_final_key(message_key);
StorageKey(raw_storage_key)
pub fn message_key<I: Instance>(lane: &LaneId, nonce: MessageNonce) -> StorageKey {
storage_map_final_key::<I>("OutboundMessages", &MessageKey { lane_id: *lane, nonce }.encode())
}
/// Storage key of the outbound message lane state in the runtime storage.
pub fn outbound_lane_data_key<I: Instance>(lane: &LaneId) -> StorageKey {
StorageKey(OutboundLanes::<I>::storage_map_final_key(*lane))
storage_map_final_key::<I>("OutboundLanes", lane)
}
/// Storage key of the inbound message lane state in the runtime storage.
pub fn inbound_lane_data_key<T: Config<I>, I: Instance>(lane: &LaneId) -> StorageKey {
StorageKey(InboundLanes::<T, I>::storage_map_final_key(*lane))
pub fn inbound_lane_data_key<I: Instance>(lane: &LaneId) -> StorageKey {
storage_map_final_key::<I>("InboundLanes", lane)
}
/// This is a copypaste of the `frame_support::storage::generator::StorageMap::storage_map_final_key`.
fn storage_map_final_key<I: Instance>(map_name: &str, key: &[u8]) -> StorageKey {
let module_prefix_hashed = frame_support::Twox128::hash(I::PREFIX.as_bytes());
let storage_prefix_hashed = frame_support::Twox128::hash(map_name.as_bytes());
let key_hashed = frame_support::Blake2_128Concat::hash(key);
let mut final_key =
Vec::with_capacity(module_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.len());
final_key.extend_from_slice(&module_prefix_hashed[..]);
final_key.extend_from_slice(&storage_prefix_hashed[..]);
final_key.extend_from_slice(key_hashed.as_ref());
StorageKey(final_key)
}
}
@@ -695,9 +799,18 @@ fn ensure_owner_or_root<T: Config<I>, I: Instance>(origin: T::Origin) -> Result<
}
}
/// Ensure that the pallet is in operational mode (not halted).
fn ensure_operational<T: Config<I>, I: Instance>() -> Result<(), Error<T, I>> {
if IsHalted::<I>::get() {
/// Ensure that the pallet is in normal operational mode.
fn ensure_normal_operating_mode<T: Config<I>, I: Instance>() -> Result<(), Error<T, I>> {
if PalletOperatingMode::<I>::get() != OperatingMode::Normal {
Err(Error::<T, I>::Halted)
} else {
Ok(())
}
}
/// Ensure that the pallet is not halted.
fn ensure_not_halted<T: Config<I>, I: Instance>() -> Result<(), Error<T, I>> {
if PalletOperatingMode::<I>::get() == OperatingMode::Halted {
Err(Error::<T, I>::Halted)
} else {
Ok(())
@@ -847,12 +960,12 @@ fn verify_and_decode_messages_proof<Chain: SourceHeaderChain<Fee>, Fee, Dispatch
mod tests {
use super::*;
use crate::mock::{
message, run_test, Event as TestEvent, Origin, TestMessageDeliveryAndDispatchPayment,
TestMessagesDeliveryProof, TestMessagesParameter, TestMessagesProof, TestPayload, TestRuntime,
TokenConversionRate, PAYLOAD_REJECTED_BY_TARGET_CHAIN, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_RELAYER_A,
TEST_RELAYER_B,
message, message_payload, run_test, unrewarded_relayer, Event as TestEvent, Origin,
TestMessageDeliveryAndDispatchPayment, TestMessagesDeliveryProof, TestMessagesParameter, TestMessagesProof,
TestRuntime, TokenConversionRate, PAYLOAD_REJECTED_BY_TARGET_CHAIN, REGULAR_PAYLOAD, TEST_LANE_ID,
TEST_RELAYER_A, TEST_RELAYER_B,
};
use bp_messages::UnrewardedRelayersState;
use bp_messages::{UnrewardedRelayer, UnrewardedRelayersState};
use frame_support::{assert_noop, assert_ok};
use frame_system::{EventRecord, Pallet as System, Phase};
use hex_literal::hex;
@@ -866,11 +979,15 @@ mod tests {
fn send_regular_message() {
get_ready_for_events();
let message_nonce = outbound_lane::<TestRuntime, DefaultInstance>(TEST_LANE_ID)
.data()
.latest_generated_nonce
+ 1;
assert_ok!(Pallet::<TestRuntime>::send_message(
Origin::signed(1),
TEST_LANE_ID,
REGULAR_PAYLOAD,
REGULAR_PAYLOAD.1,
REGULAR_PAYLOAD.declared_weight,
));
// check event with assigned nonce
@@ -878,13 +995,16 @@ mod tests {
System::<TestRuntime>::events(),
vec![EventRecord {
phase: Phase::Initialization,
event: TestEvent::Messages(RawEvent::MessageAccepted(TEST_LANE_ID, 1)),
event: TestEvent::Messages(RawEvent::MessageAccepted(TEST_LANE_ID, message_nonce)),
topics: vec![],
}],
);
// 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() {
@@ -897,17 +1017,29 @@ mod tests {
TEST_LANE_ID,
InboundLaneData {
last_confirmed_nonce: 1,
..Default::default()
relayers: vec![UnrewardedRelayer {
relayer: 0,
messages: DeliveredMessages::new(1, true),
}]
.into_iter()
.collect(),
},
))),
Default::default(),
UnrewardedRelayersState {
unrewarded_relayer_entries: 1,
total_messages: 1,
..Default::default()
},
));
assert_eq!(
System::<TestRuntime>::events(),
vec![EventRecord {
phase: Phase::Initialization,
event: TestEvent::Messages(RawEvent::MessagesDelivered(TEST_LANE_ID, 1, 1)),
event: TestEvent::Messages(RawEvent::MessagesDelivered(
TEST_LANE_ID,
DeliveredMessages::new(1, true),
)),
topics: vec![],
}],
);
@@ -920,29 +1052,41 @@ mod tests {
assert_ok!(Pallet::<TestRuntime>::set_owner(Origin::root(), Some(1)));
assert_noop!(
Pallet::<TestRuntime>::set_operational(Origin::signed(2), false),
Pallet::<TestRuntime>::set_operating_mode(Origin::signed(2), OperatingMode::Halted),
DispatchError::BadOrigin,
);
assert_ok!(Pallet::<TestRuntime>::set_operational(Origin::root(), false));
assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
Origin::root(),
OperatingMode::Halted
));
assert_ok!(Pallet::<TestRuntime>::set_owner(Origin::signed(1), None));
assert_noop!(
Pallet::<TestRuntime>::set_operational(Origin::signed(1), true),
Pallet::<TestRuntime>::set_operating_mode(Origin::signed(1), OperatingMode::Normal),
DispatchError::BadOrigin,
);
assert_noop!(
Pallet::<TestRuntime>::set_operational(Origin::signed(2), true),
Pallet::<TestRuntime>::set_operating_mode(Origin::signed(2), OperatingMode::Normal),
DispatchError::BadOrigin,
);
assert_ok!(Pallet::<TestRuntime>::set_operational(Origin::root(), true));
assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
Origin::root(),
OperatingMode::Normal
));
});
}
#[test]
fn pallet_may_be_halted_by_root() {
run_test(|| {
assert_ok!(Pallet::<TestRuntime>::set_operational(Origin::root(), false));
assert_ok!(Pallet::<TestRuntime>::set_operational(Origin::root(), true));
assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
Origin::root(),
OperatingMode::Halted
));
assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
Origin::root(),
OperatingMode::Normal
));
});
}
@@ -951,21 +1095,30 @@ mod tests {
run_test(|| {
PalletOwner::<TestRuntime>::put(2);
assert_ok!(Pallet::<TestRuntime>::set_operational(Origin::signed(2), false));
assert_ok!(Pallet::<TestRuntime>::set_operational(Origin::signed(2), true));
assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
Origin::signed(2),
OperatingMode::Halted
));
assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
Origin::signed(2),
OperatingMode::Normal
));
assert_noop!(
Pallet::<TestRuntime>::set_operational(Origin::signed(1), false),
Pallet::<TestRuntime>::set_operating_mode(Origin::signed(1), OperatingMode::Halted),
DispatchError::BadOrigin,
);
assert_noop!(
Pallet::<TestRuntime>::set_operational(Origin::signed(1), true),
Pallet::<TestRuntime>::set_operating_mode(Origin::signed(1), OperatingMode::Normal),
DispatchError::BadOrigin,
);
assert_ok!(Pallet::<TestRuntime>::set_operational(Origin::signed(2), false));
assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
Origin::signed(2),
OperatingMode::Halted
));
assert_noop!(
Pallet::<TestRuntime>::set_operational(Origin::signed(1), true),
Pallet::<TestRuntime>::set_operating_mode(Origin::signed(1), OperatingMode::Normal),
DispatchError::BadOrigin,
);
});
@@ -1072,25 +1225,30 @@ mod tests {
// send message first to be able to check that delivery_proof fails later
send_regular_message();
IsHalted::<DefaultInstance>::put(true);
PalletOperatingMode::<DefaultInstance>::put(OperatingMode::Halted);
assert_noop!(
Pallet::<TestRuntime>::send_message(
Origin::signed(1),
TEST_LANE_ID,
REGULAR_PAYLOAD,
REGULAR_PAYLOAD.1,
REGULAR_PAYLOAD.declared_weight,
),
Error::<TestRuntime, DefaultInstance>::Halted,
);
assert_noop!(
Pallet::<TestRuntime>::increase_message_fee(Origin::signed(1), TEST_LANE_ID, 1, 1,),
Error::<TestRuntime, DefaultInstance>::Halted,
);
assert_noop!(
Pallet::<TestRuntime>::receive_messages_proof(
Origin::signed(1),
TEST_RELAYER_A,
Ok(vec![message(2, REGULAR_PAYLOAD)]).into(),
1,
REGULAR_PAYLOAD.1,
REGULAR_PAYLOAD.declared_weight,
),
Error::<TestRuntime, DefaultInstance>::Halted,
);
@@ -1112,6 +1270,53 @@ mod tests {
});
}
#[test]
fn pallet_rejects_new_messages_in_rejecting_outbound_messages_operating_mode() {
run_test(|| {
// send message first to be able to check that delivery_proof fails later
send_regular_message();
PalletOperatingMode::<DefaultInstance>::put(OperatingMode::RejectingOutboundMessages);
assert_noop!(
Pallet::<TestRuntime>::send_message(
Origin::signed(1),
TEST_LANE_ID,
REGULAR_PAYLOAD,
REGULAR_PAYLOAD.declared_weight,
),
Error::<TestRuntime, DefaultInstance>::Halted,
);
assert_ok!(Pallet::<TestRuntime>::increase_message_fee(
Origin::signed(1),
TEST_LANE_ID,
1,
1,
));
assert_ok!(Pallet::<TestRuntime>::receive_messages_proof(
Origin::signed(1),
TEST_RELAYER_A,
Ok(vec![message(1, REGULAR_PAYLOAD)]).into(),
1,
REGULAR_PAYLOAD.declared_weight,
),);
assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
Origin::signed(1),
TestMessagesDeliveryProof(Ok((
TEST_LANE_ID,
InboundLaneData {
last_confirmed_nonce: 1,
..Default::default()
},
))),
Default::default(),
));
});
}
#[test]
fn send_message_works() {
run_test(|| {
@@ -1128,7 +1333,7 @@ mod tests {
Origin::signed(1),
TEST_LANE_ID,
PAYLOAD_REJECTED_BY_TARGET_CHAIN,
PAYLOAD_REJECTED_BY_TARGET_CHAIN.1
PAYLOAD_REJECTED_BY_TARGET_CHAIN.declared_weight
),
Error::<TestRuntime, DefaultInstance>::MessageRejectedByChainVerifier,
);
@@ -1155,7 +1360,7 @@ mod tests {
Origin::signed(1),
TEST_LANE_ID,
REGULAR_PAYLOAD,
REGULAR_PAYLOAD.1
REGULAR_PAYLOAD.declared_weight
),
Error::<TestRuntime, DefaultInstance>::FailedToWithdrawMessageFee,
);
@@ -1170,7 +1375,7 @@ mod tests {
TEST_RELAYER_A,
Ok(vec![message(1, REGULAR_PAYLOAD)]).into(),
1,
REGULAR_PAYLOAD.1,
REGULAR_PAYLOAD.declared_weight,
));
assert_eq!(InboundLanes::<TestRuntime>::get(TEST_LANE_ID).last_delivered_nonce(), 1);
@@ -1185,9 +1390,12 @@ mod tests {
TEST_LANE_ID,
InboundLaneData {
last_confirmed_nonce: 8,
relayers: vec![(9, 9, TEST_RELAYER_A), (10, 10, TEST_RELAYER_B)]
.into_iter()
.collect(),
relayers: vec![
unrewarded_relayer(9, 9, TEST_RELAYER_A),
unrewarded_relayer(10, 10, TEST_RELAYER_B),
]
.into_iter()
.collect(),
},
);
assert_eq!(
@@ -1211,16 +1419,19 @@ mod tests {
TEST_RELAYER_A,
message_proof,
1,
REGULAR_PAYLOAD.1,
REGULAR_PAYLOAD.declared_weight,
));
assert_eq!(
InboundLanes::<TestRuntime>::get(TEST_LANE_ID),
InboundLaneData {
last_confirmed_nonce: 9,
relayers: vec![(10, 10, TEST_RELAYER_B), (11, 11, TEST_RELAYER_A)]
.into_iter()
.collect(),
relayers: vec![
unrewarded_relayer(10, 10, TEST_RELAYER_B),
unrewarded_relayer(11, 11, TEST_RELAYER_A)
]
.into_iter()
.collect(),
},
);
assert_eq!(
@@ -1243,7 +1454,7 @@ mod tests {
TEST_RELAYER_A,
Ok(vec![message(1, REGULAR_PAYLOAD)]).into(),
1,
REGULAR_PAYLOAD.1 - 1,
REGULAR_PAYLOAD.declared_weight - 1,
),
Error::<TestRuntime, DefaultInstance>::InvalidMessagesDispatchWeight,
);
@@ -1317,7 +1528,7 @@ mod tests {
TestMessagesDeliveryProof(Ok((
TEST_LANE_ID,
InboundLaneData {
relayers: vec![(1, 1, TEST_RELAYER_A)].into_iter().collect(),
relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into_iter().collect(),
..Default::default()
}
))),
@@ -1342,9 +1553,12 @@ mod tests {
TestMessagesDeliveryProof(Ok((
TEST_LANE_ID,
InboundLaneData {
relayers: vec![(1, 1, TEST_RELAYER_A), (2, 2, TEST_RELAYER_B)]
.into_iter()
.collect(),
relayers: vec![
unrewarded_relayer(1, 1, TEST_RELAYER_A),
unrewarded_relayer(2, 2, TEST_RELAYER_B)
]
.into_iter()
.collect(),
..Default::default()
}
))),
@@ -1389,9 +1603,12 @@ mod tests {
TestMessagesDeliveryProof(Ok((
TEST_LANE_ID,
InboundLaneData {
relayers: vec![(1, 1, TEST_RELAYER_A), (2, 2, TEST_RELAYER_B)]
.into_iter()
.collect(),
relayers: vec![
unrewarded_relayer(1, 1, TEST_RELAYER_A),
unrewarded_relayer(2, 2, TEST_RELAYER_B)
]
.into_iter()
.collect(),
..Default::default()
}
))),
@@ -1411,9 +1628,12 @@ mod tests {
TestMessagesDeliveryProof(Ok((
TEST_LANE_ID,
InboundLaneData {
relayers: vec![(1, 1, TEST_RELAYER_A), (2, 2, TEST_RELAYER_B)]
.into_iter()
.collect(),
relayers: vec![
unrewarded_relayer(1, 1, TEST_RELAYER_A),
unrewarded_relayer(2, 2, TEST_RELAYER_B)
]
.into_iter()
.collect(),
..Default::default()
}
))),
@@ -1465,7 +1685,7 @@ mod tests {
])
.into(),
3,
REGULAR_PAYLOAD.1 + REGULAR_PAYLOAD.1,
REGULAR_PAYLOAD.declared_weight + REGULAR_PAYLOAD.declared_weight,
),);
assert_eq!(
@@ -1479,7 +1699,7 @@ mod tests {
fn storage_message_key_computed_properly() {
// If this test fails, then something has been changed in module storage that is breaking all
// previously crafted messages proofs.
let storage_key = storage_keys::message_key::<TestRuntime, DefaultInstance>(&*b"test", 42).0;
let storage_key = storage_keys::message_key::<DefaultInstance>(&*b"test", 42).0;
assert_eq!(
storage_key,
hex!("dd16c784ebd3390a9bc0357c7511ed018a395e6242c6813b196ca31ed0547ea79446af0e09063bd4a7874aef8a997cec746573742a00000000000000").to_vec(),
@@ -1505,7 +1725,7 @@ mod tests {
fn inbound_lane_data_key_computed_properly() {
// If this test fails, then something has been changed in module storage that is breaking all
// previously crafted inbound lane state proofs.
let storage_key = storage_keys::inbound_lane_data_key::<TestRuntime, DefaultInstance>(&*b"test").0;
let storage_key = storage_keys::inbound_lane_data_key::<DefaultInstance>(&*b"test").0;
assert_eq!(
storage_key,
hex!("dd16c784ebd3390a9bc0357c7511ed01e5f83cf83f2127eb47afdc35d6e43fab44a8995dd50b6657a037a7839304535b74657374").to_vec(),
@@ -1517,9 +1737,9 @@ mod tests {
#[test]
fn actual_dispatch_weight_does_not_overlow() {
run_test(|| {
let message1 = message(1, TestPayload(0, Weight::MAX / 2));
let message2 = message(2, TestPayload(0, Weight::MAX / 2));
let message3 = message(2, TestPayload(0, Weight::MAX / 2));
let message1 = message(1, message_payload(0, Weight::MAX / 2));
let message2 = message(2, message_payload(0, Weight::MAX / 2));
let message3 = message(2, message_payload(0, Weight::MAX / 2));
assert_noop!(
Pallet::<TestRuntime, DefaultInstance>::receive_messages_proof(
@@ -1586,4 +1806,127 @@ mod tests {
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()
);
});
}
#[test]
fn messages_delivered_callbacks_are_called() {
run_test(|| {
send_regular_message();
send_regular_message();
send_regular_message();
// messages 1+2 are confirmed in 1 tx, message 3 in a separate tx
// dispatch of message 2 has failed
let mut delivered_messages_1_and_2 = DeliveredMessages::new(1, true);
delivered_messages_1_and_2.note_dispatched_message(false);
let messages_1_and_2_proof = Ok((
TEST_LANE_ID,
InboundLaneData {
last_confirmed_nonce: 0,
relayers: vec![UnrewardedRelayer {
relayer: 0,
messages: delivered_messages_1_and_2.clone(),
}]
.into_iter()
.collect(),
},
));
let delivered_message_3 = DeliveredMessages::new(3, true);
let messages_3_proof = Ok((
TEST_LANE_ID,
InboundLaneData {
last_confirmed_nonce: 0,
relayers: vec![UnrewardedRelayer {
relayer: 0,
messages: delivered_message_3.clone(),
}]
.into_iter()
.collect(),
},
));
// first tx with messages 1+2
assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
Origin::signed(1),
TestMessagesDeliveryProof(messages_1_and_2_proof),
UnrewardedRelayersState {
unrewarded_relayer_entries: 1,
total_messages: 2,
..Default::default()
},
));
// second tx with message 3
assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
Origin::signed(1),
TestMessagesDeliveryProof(messages_3_proof),
UnrewardedRelayersState {
unrewarded_relayer_entries: 1,
total_messages: 1,
..Default::default()
},
));
// ensure that both callbacks have been called twice: for 1+2, then for 3
crate::mock::TestOnDeliveryConfirmed1::ensure_called(&TEST_LANE_ID, &delivered_messages_1_and_2);
crate::mock::TestOnDeliveryConfirmed1::ensure_called(&TEST_LANE_ID, &delivered_message_3);
crate::mock::TestOnDeliveryConfirmed2::ensure_called(&TEST_LANE_ID, &delivered_messages_1_and_2);
crate::mock::TestOnDeliveryConfirmed2::ensure_called(&TEST_LANE_ID, &delivered_message_3);
});
}
}
+109 -10
View File
@@ -19,15 +19,17 @@
use crate::Config;
use bitvec::prelude::*;
use bp_messages::{
source_chain::{
LaneMessageVerifier, MessageDeliveryAndDispatchPayment, RelayersRewards, Sender, TargetHeaderChain,
LaneMessageVerifier, MessageDeliveryAndDispatchPayment, OnDeliveryConfirmed, RelayersRewards, Sender,
TargetHeaderChain,
},
target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, SourceHeaderChain},
InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce, OutboundLaneData,
Parameter as MessagesParameter,
DeliveredMessages, InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce, OutboundLaneData,
Parameter as MessagesParameter, UnrewardedRelayer,
};
use bp_runtime::Size;
use bp_runtime::{messages::MessageDispatchResult, Size};
use codec::{Decode, Encode};
use frame_support::{parameter_types, weights::Weight};
use sp_core::H256;
@@ -41,7 +43,17 @@ use std::collections::BTreeMap;
pub type AccountId = u64;
pub type Balance = u64;
#[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 TestRelayer = u64;
@@ -115,6 +127,8 @@ impl pallet_balances::Config for TestRuntime {
type ExistentialDeposit = ExistentialDeposit;
type AccountStore = frame_system::Pallet<TestRuntime>;
type WeightInfo = ();
type MaxReserves = ();
type ReserveIdentifier = ();
}
parameter_types! {
@@ -157,6 +171,7 @@ impl Config for TestRuntime {
type TargetHeaderChain = TestTargetHeaderChain;
type LaneMessageVerifier = TestLaneMessageVerifier;
type MessageDeliveryAndDispatchPayment = TestMessageDeliveryAndDispatchPayment;
type OnDeliveryConfirmed = (TestOnDeliveryConfirmed1, TestOnDeliveryConfirmed2);
type SourceHeaderChain = TestSourceHeaderChain;
type MessageDispatch = TestMessageDispatch;
@@ -187,10 +202,10 @@ pub const TEST_ERROR: &str = "Test error";
pub const TEST_LANE_ID: LaneId = [0, 0, 0, 1];
/// 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`.
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.
pub type MessagesByLaneVec = Vec<(LaneId, ProvedLaneMessages<Message<TestMessageFee>>)>;
@@ -333,6 +348,44 @@ impl MessageDeliveryAndDispatchPayment<AccountId, TestMessageFee> for TestMessag
}
}
/// First on-messages-delivered callback.
#[derive(Debug)]
pub struct TestOnDeliveryConfirmed1;
impl TestOnDeliveryConfirmed1 {
/// Verify that the callback has been called with given delivered messages.
pub fn ensure_called(lane: &LaneId, messages: &DeliveredMessages) {
let key = (b"TestOnDeliveryConfirmed1", lane, messages).encode();
assert_eq!(frame_support::storage::unhashed::get(&key), Some(true));
}
}
impl OnDeliveryConfirmed for TestOnDeliveryConfirmed1 {
fn on_messages_delivered(lane: &LaneId, messages: &DeliveredMessages) {
let key = (b"TestOnDeliveryConfirmed1", lane, messages).encode();
frame_support::storage::unhashed::put(&key, &true);
}
}
/// Seconde on-messages-delivered callback.
#[derive(Debug)]
pub struct TestOnDeliveryConfirmed2;
impl TestOnDeliveryConfirmed2 {
/// Verify that the callback has been called with given delivered messages.
pub fn ensure_called(lane: &LaneId, messages: &DeliveredMessages) {
let key = (b"TestOnDeliveryConfirmed2", lane, messages).encode();
assert_eq!(frame_support::storage::unhashed::get(&key), Some(true));
}
}
impl OnDeliveryConfirmed for TestOnDeliveryConfirmed2 {
fn on_messages_delivered(lane: &LaneId, messages: &DeliveredMessages) {
let key = (b"TestOnDeliveryConfirmed2", lane, messages).encode();
frame_support::storage::unhashed::put(&key, &true);
}
}
/// Source header chain that is used in tests.
#[derive(Debug)]
pub struct TestSourceHeaderChain;
@@ -357,17 +410,25 @@ impl SourceHeaderChain<TestMessageFee> for TestSourceHeaderChain {
#[derive(Debug)]
pub struct TestMessageDispatch;
impl MessageDispatch<TestMessageFee> for TestMessageDispatch {
impl MessageDispatch<AccountId, TestMessageFee> for TestMessageDispatch {
type DispatchPayload = TestPayload;
fn dispatch_weight(message: &DispatchMessage<TestPayload, TestMessageFee>) -> Weight {
match message.data.payload.as_ref() {
Ok(payload) => payload.1,
Ok(payload) => payload.declared_weight,
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.
@@ -381,6 +442,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.
pub fn message_data(payload: TestPayload) -> MessageData<TestMessageFee> {
MessageData {
@@ -389,6 +459,35 @@ 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 {
dispatch_result: true,
unspent_weight,
dispatch_fee_paid_during_dispatch: true,
}
}
/// Constructs unrewarded relayer entry from nonces range and relayer id.
pub fn unrewarded_relayer(
begin: MessageNonce,
end: MessageNonce,
relayer: TestRelayer,
) -> UnrewardedRelayer<TestRelayer> {
UnrewardedRelayer {
relayer,
messages: DeliveredMessages {
begin,
end,
dispatch_results: if end >= begin {
bitvec![Msb0, u8; 1; (end - begin + 1) as _]
} else {
Default::default()
},
},
}
}
/// Run pallet test.
pub fn run_test<T>(test: impl FnOnce() -> T) -> T {
let mut t = frame_system::GenesisConfig::default()
@@ -16,7 +16,12 @@
//! Everything about outgoing messages sending.
use bp_messages::{LaneId, MessageData, MessageNonce, OutboundLaneData};
use bitvec::prelude::*;
use bp_messages::{
DeliveredMessages, DispatchResultsBitVec, LaneId, MessageData, MessageNonce, OutboundLaneData, UnrewardedRelayer,
};
use frame_support::RuntimeDebug;
use sp_std::collections::vec_deque::VecDeque;
/// Outbound lane storage.
pub trait OutboundLaneStorage {
@@ -38,6 +43,28 @@ pub trait OutboundLaneStorage {
fn remove_message(&mut self, nonce: &MessageNonce);
}
/// Result of messages receival confirmation.
#[derive(RuntimeDebug, PartialEq, Eq)]
pub enum ReceivalConfirmationResult {
/// New messages have been confirmed by the confirmation transaction.
ConfirmedMessages(DeliveredMessages),
/// Confirmation transaction brings no new confirmation. This may be a result of relayer
/// error or several relayers runnng.
NoNewConfirmations,
/// Bridged chain is trying to confirm more messages than we have generated. May be a result
/// of invalid bridged chain storage.
FailedToConfirmFutureMessages,
/// The unrewarded relayers vec contains an empty entry. May be a result of invalid bridged
/// chain storage.
EmptyUnrewardedRelayerEntry,
/// The unrewarded relayers vec contains non-consecutive entries. May be a result of invalid bridged
/// chain storage.
NonConsecutiveUnrewardedRelayerEntries,
/// The unrewarded relayers vec contains entry with mismatched number of dispatch results. May be
/// a result of invalid bridged chain storage.
InvalidNumberOfDispatchResults,
}
/// Outbound messages lane.
pub struct OutboundLane<S> {
storage: S,
@@ -69,20 +96,34 @@ impl<S: OutboundLaneStorage> OutboundLane<S> {
}
/// Confirm messages delivery.
///
/// Returns `None` if confirmation is wrong/duplicate.
/// Returns `Some` with inclusive ranges of message nonces that have been received.
pub fn confirm_delivery(&mut self, latest_received_nonce: MessageNonce) -> Option<(MessageNonce, MessageNonce)> {
pub fn confirm_delivery<RelayerId>(
&mut self,
latest_received_nonce: MessageNonce,
relayers: &VecDeque<UnrewardedRelayer<RelayerId>>,
) -> ReceivalConfirmationResult {
let mut data = self.storage.data();
if latest_received_nonce <= data.latest_received_nonce || latest_received_nonce > data.latest_generated_nonce {
return None;
if latest_received_nonce <= data.latest_received_nonce {
return ReceivalConfirmationResult::NoNewConfirmations;
}
if latest_received_nonce > data.latest_generated_nonce {
return ReceivalConfirmationResult::FailedToConfirmFutureMessages;
}
let dispatch_results =
match extract_dispatch_results(data.latest_received_nonce, latest_received_nonce, relayers) {
Ok(dispatch_results) => dispatch_results,
Err(extract_error) => return extract_error,
};
let prev_latest_received_nonce = data.latest_received_nonce;
data.latest_received_nonce = latest_received_nonce;
self.storage.set_data(data);
Some((prev_latest_received_nonce + 1, latest_received_nonce))
ReceivalConfirmationResult::ConfirmedMessages(DeliveredMessages {
begin: prev_latest_received_nonce + 1,
end: latest_received_nonce,
dispatch_results,
})
}
/// Prune at most `max_messages_to_prune` already received messages.
@@ -108,13 +149,108 @@ impl<S: OutboundLaneStorage> OutboundLane<S> {
}
}
/// Extract new dispatch results from the unrewarded relayers vec.
///
/// Returns `Err(_)` if unrewarded relayers vec contains invalid data, meaning that the bridged
/// chain has invalid runtime storage.
fn extract_dispatch_results<RelayerId>(
prev_latest_received_nonce: MessageNonce,
latest_received_nonce: MessageNonce,
relayers: &VecDeque<UnrewardedRelayer<RelayerId>>,
) -> Result<DispatchResultsBitVec, ReceivalConfirmationResult> {
// the only caller of this functions checks that the prev_latest_received_nonce..=latest_received_nonce
// is valid, so we're ready to accept messages in this range
// => with_capacity call must succeed here or we'll be unable to receive confirmations at all
let mut received_dispatch_result =
BitVec::with_capacity((latest_received_nonce - prev_latest_received_nonce + 1) as _);
let mut last_entry_end: Option<MessageNonce> = None;
for entry in relayers {
// unrewarded relayer entry must have at least 1 unconfirmed message
// (guaranteed by the `InboundLane::receive_message()`)
if entry.messages.end < entry.messages.begin {
return Err(ReceivalConfirmationResult::EmptyUnrewardedRelayerEntry);
}
// every entry must confirm range of messages that follows previous entry range
// (guaranteed by the `InboundLane::receive_message()`)
if let Some(last_entry_end) = last_entry_end {
let expected_entry_begin = last_entry_end.checked_add(1);
if expected_entry_begin != Some(entry.messages.begin) {
return Err(ReceivalConfirmationResult::NonConsecutiveUnrewardedRelayerEntries);
}
}
last_entry_end = Some(entry.messages.end);
// entry can't confirm messages larger than `inbound_lane_data.latest_received_nonce()`
// (guaranteed by the `InboundLane::receive_message()`)
if entry.messages.end > latest_received_nonce {
// technically this will be detected in the next loop iteration as `InvalidNumberOfDispatchResults`
// but to guarantee safety of loop operations below this is detected now
return Err(ReceivalConfirmationResult::FailedToConfirmFutureMessages);
}
// entry must have single dispatch result for every message
// (guaranteed by the `InboundLane::receive_message()`)
if entry.messages.dispatch_results.len() as MessageNonce != entry.messages.end - entry.messages.begin + 1 {
return Err(ReceivalConfirmationResult::InvalidNumberOfDispatchResults);
}
// now we know that the entry is valid
// => let's check if it brings new confirmations
let new_messages_begin = sp_std::cmp::max(entry.messages.begin, prev_latest_received_nonce + 1);
let new_messages_end = sp_std::cmp::min(entry.messages.end, latest_received_nonce);
let new_messages_range = new_messages_begin..=new_messages_end;
if new_messages_range.is_empty() {
continue;
}
// now we know that entry brings new confirmations
// => let's extract dispatch results
received_dispatch_result.extend_from_bitslice(
&entry.messages.dispatch_results[(new_messages_begin - entry.messages.begin) as usize..],
);
}
Ok(received_dispatch_result)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
mock::{message_data, run_test, TestRuntime, REGULAR_PAYLOAD, TEST_LANE_ID},
mock::{message_data, run_test, unrewarded_relayer, TestRelayer, TestRuntime, REGULAR_PAYLOAD, TEST_LANE_ID},
outbound_lane,
};
use sp_std::ops::RangeInclusive;
fn unrewarded_relayers(nonces: RangeInclusive<MessageNonce>) -> VecDeque<UnrewardedRelayer<TestRelayer>> {
vec![unrewarded_relayer(*nonces.start(), *nonces.end(), 0)]
.into_iter()
.collect()
}
fn delivered_messages(nonces: RangeInclusive<MessageNonce>) -> DeliveredMessages {
DeliveredMessages {
begin: *nonces.start(),
end: *nonces.end(),
dispatch_results: bitvec![Msb0, u8; 1; (nonces.end() - nonces.start() + 1) as _],
}
}
fn assert_3_messages_confirmation_fails(
latest_received_nonce: MessageNonce,
relayers: &VecDeque<UnrewardedRelayer<TestRelayer>>,
) -> ReceivalConfirmationResult {
run_test(|| {
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
lane.send_message(message_data(REGULAR_PAYLOAD));
lane.send_message(message_data(REGULAR_PAYLOAD));
lane.send_message(message_data(REGULAR_PAYLOAD));
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
assert_eq!(lane.storage.data().latest_received_nonce, 0);
let result = lane.confirm_delivery(latest_received_nonce, relayers);
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
assert_eq!(lane.storage.data().latest_received_nonce, 0);
result
})
}
#[test]
fn send_message_works() {
@@ -136,7 +272,10 @@ mod tests {
assert_eq!(lane.send_message(message_data(REGULAR_PAYLOAD)), 3);
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
assert_eq!(lane.storage.data().latest_received_nonce, 0);
assert_eq!(lane.confirm_delivery(3), Some((1, 3)));
assert_eq!(
lane.confirm_delivery(3, &unrewarded_relayers(1..=3)),
ReceivalConfirmationResult::ConfirmedMessages(delivered_messages(1..=3)),
);
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
assert_eq!(lane.storage.data().latest_received_nonce, 3);
});
@@ -151,12 +290,21 @@ mod tests {
lane.send_message(message_data(REGULAR_PAYLOAD));
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
assert_eq!(lane.storage.data().latest_received_nonce, 0);
assert_eq!(lane.confirm_delivery(3), Some((1, 3)));
assert_eq!(lane.confirm_delivery(3), None);
assert_eq!(
lane.confirm_delivery(3, &unrewarded_relayers(1..=3)),
ReceivalConfirmationResult::ConfirmedMessages(delivered_messages(1..=3)),
);
assert_eq!(
lane.confirm_delivery(3, &unrewarded_relayers(1..=3)),
ReceivalConfirmationResult::NoNewConfirmations,
);
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
assert_eq!(lane.storage.data().latest_received_nonce, 3);
assert_eq!(lane.confirm_delivery(2), None);
assert_eq!(
lane.confirm_delivery(2, &unrewarded_relayers(1..=1)),
ReceivalConfirmationResult::NoNewConfirmations,
);
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
assert_eq!(lane.storage.data().latest_received_nonce, 3);
});
@@ -164,17 +312,70 @@ mod tests {
#[test]
fn confirm_delivery_rejects_nonce_larger_than_last_generated() {
run_test(|| {
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
lane.send_message(message_data(REGULAR_PAYLOAD));
lane.send_message(message_data(REGULAR_PAYLOAD));
lane.send_message(message_data(REGULAR_PAYLOAD));
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
assert_eq!(lane.storage.data().latest_received_nonce, 0);
assert_eq!(lane.confirm_delivery(10), None);
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
assert_eq!(lane.storage.data().latest_received_nonce, 0);
});
assert_eq!(
assert_3_messages_confirmation_fails(10, &unrewarded_relayers(1..=10),),
ReceivalConfirmationResult::FailedToConfirmFutureMessages,
);
}
#[test]
fn confirm_delivery_fails_if_entry_confirms_future_messages() {
assert_eq!(
assert_3_messages_confirmation_fails(
3,
&unrewarded_relayers(1..=1)
.into_iter()
.chain(unrewarded_relayers(2..=30).into_iter())
.chain(unrewarded_relayers(3..=3).into_iter())
.collect(),
),
ReceivalConfirmationResult::FailedToConfirmFutureMessages,
);
}
#[test]
#[allow(clippy::reversed_empty_ranges)]
fn confirm_delivery_fails_if_entry_is_empty() {
assert_eq!(
assert_3_messages_confirmation_fails(
3,
&unrewarded_relayers(1..=1)
.into_iter()
.chain(unrewarded_relayers(2..=1).into_iter())
.chain(unrewarded_relayers(2..=3).into_iter())
.collect(),
),
ReceivalConfirmationResult::EmptyUnrewardedRelayerEntry,
);
}
#[test]
fn confirm_delivery_fails_if_entries_are_non_consecutive() {
assert_eq!(
assert_3_messages_confirmation_fails(
3,
&unrewarded_relayers(1..=1)
.into_iter()
.chain(unrewarded_relayers(3..=3).into_iter())
.chain(unrewarded_relayers(2..=2).into_iter())
.collect(),
),
ReceivalConfirmationResult::NonConsecutiveUnrewardedRelayerEntries,
);
}
#[test]
fn confirm_delivery_fails_if_number_of_dispatch_results_in_entry_is_invalid() {
let mut relayers: VecDeque<_> = unrewarded_relayers(1..=1)
.into_iter()
.chain(unrewarded_relayers(2..=2).into_iter())
.chain(unrewarded_relayers(3..=3).into_iter())
.collect();
relayers[0].messages.dispatch_results.clear();
assert_eq!(
assert_3_messages_confirmation_fails(3, &relayers),
ReceivalConfirmationResult::InvalidNumberOfDispatchResults,
);
}
#[test]
@@ -191,11 +392,17 @@ mod tests {
assert_eq!(lane.prune_messages(100), 0);
assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1);
// after confirmation, some messages are received
assert_eq!(lane.confirm_delivery(2), Some((1, 2)));
assert_eq!(
lane.confirm_delivery(2, &unrewarded_relayers(1..=2)),
ReceivalConfirmationResult::ConfirmedMessages(delivered_messages(1..=2)),
);
assert_eq!(lane.prune_messages(100), 2);
assert_eq!(lane.storage.data().oldest_unpruned_nonce, 3);
// after last message is confirmed, everything is pruned
assert_eq!(lane.confirm_delivery(3), Some((3, 3)));
assert_eq!(
lane.confirm_delivery(3, &unrewarded_relayers(3..=3)),
ReceivalConfirmationResult::ConfirmedMessages(delivered_messages(3..=3)),
);
assert_eq!(lane.prune_messages(100), 1);
assert_eq!(lane.storage.data().oldest_unpruned_nonce, 4);
});
@@ -17,7 +17,7 @@
//! Autogenerated weights for pallet_bridge_messages
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0
//! DATE: 2021-04-21, STEPS: [50, ], REPEAT: 20
//! DATE: 2021-06-18, STEPS: [50, ], REPEAT: 20
//! LOW RANGE: [], HIGH RANGE: []
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled
//! 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_1_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_two_messages_by_single_relayer() -> 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>);
impl<T: frame_system::Config> WeightInfo for RialtoWeight<T> {
fn send_minimal_message_worst_case() -> Weight {
(149_643_000 as Weight)
(159_305_000 as Weight)
.saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(12 as Weight))
}
fn send_1_kb_message_worst_case() -> Weight {
(153_329_000 as Weight)
(164_394_000 as Weight)
.saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(12 as Weight))
}
fn send_16_kb_message_worst_case() -> Weight {
(200_113_000 as Weight)
(223_521_000 as Weight)
.saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(12 as Weight))
}
fn increase_message_fee() -> Weight {
(6_407_252_000 as Weight)
.saturating_add(T::DbWeight::get().reads(4 as Weight))
(6_709_925_000 as Weight)
.saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight))
}
fn receive_single_message_proof() -> Weight {
(141_256_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
(206_769_000 as Weight)
.saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight))
}
fn receive_two_messages_proof() -> Weight {
(247_723_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
(343_982_000 as Weight)
.saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight))
}
fn receive_single_message_proof_with_outbound_lane_state() -> Weight {
(159_731_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
(223_738_000 as Weight)
.saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight))
}
fn receive_single_message_proof_1_kb() -> Weight {
(168_546_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
(235_369_000 as Weight)
.saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight))
}
fn receive_single_message_proof_16_kb() -> Weight {
(450_087_000 as Weight)
(510_338_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 {
(141_536_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
}
fn receive_delivery_proof_for_single_message() -> Weight {
(164_519_000 as Weight)
(128_805_000 as Weight)
.saturating_add(T::DbWeight::get().reads(6 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight))
}
fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight {
(173_300_000 as Weight)
(137_143_000 as Weight)
.saturating_add(T::DbWeight::get().reads(7 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight))
}
fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight {
(246_205_000 as Weight)
(193_108_000 as Weight)
.saturating_add(T::DbWeight::get().reads(8 as Weight))
.saturating_add(T::DbWeight::get().writes(4 as Weight))
}
fn send_messages_of_various_lengths(i: u32) -> Weight {
(149_551_000 as Weight)
.saturating_add((3_000 as Weight).saturating_mul(i as Weight))
(133_632_000 as Weight)
.saturating_add((4_000 as Weight).saturating_mul(i as Weight))
.saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(12 as Weight))
}
fn receive_multiple_messages_proof(i: u32) -> Weight {
(0 as Weight)
.saturating_add((114_817_000 as Weight).saturating_mul(i as Weight))
.saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
.saturating_add((145_006_000 as Weight).saturating_mul(i as Weight))
.saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight))
}
fn receive_message_proofs_with_extra_nodes(i: u32) -> Weight {
(437_797_000 as Weight)
(486_301_000 as Weight)
.saturating_add((10_000 as Weight).saturating_mul(i 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().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight))
}
fn receive_message_proofs_with_large_leaf(i: u32) -> Weight {
(137_633_000 as Weight)
(178_139_000 as Weight)
.saturating_add((7_000 as Weight).saturating_mul(i 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().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight))
}
fn receive_multiple_messages_proof_with_outbound_lane_state(i: u32) -> Weight {
(0 as Weight)
.saturating_add((118_482_000 as Weight).saturating_mul(i as Weight))
.saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
.saturating_add((150_844_000 as Weight).saturating_mul(i as Weight))
.saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight))
}
fn receive_delivery_proof_for_multiple_messages_by_single_relayer(i: u32) -> Weight {
(116_036_000 as Weight)
.saturating_add((7_118_000 as Weight).saturating_mul(i as Weight))
(113_140_000 as Weight)
.saturating_add((7_656_000 as Weight).saturating_mul(i 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().writes(3 as Weight))
}
fn receive_delivery_proof_for_multiple_messages_by_multiple_relayers(i: u32) -> Weight {
(172_780_000 as Weight)
.saturating_add((63_718_000 as Weight).saturating_mul(i as Weight))
(97_424_000 as Weight)
.saturating_add((63_128_000 as Weight).saturating_mul(i 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().writes(3 as Weight))
@@ -182,105 +188,110 @@ impl<T: frame_system::Config> WeightInfo for RialtoWeight<T> {
// For backwards compatibility and tests
impl WeightInfo for () {
fn send_minimal_message_worst_case() -> Weight {
(149_643_000 as Weight)
(159_305_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(12 as Weight))
}
fn send_1_kb_message_worst_case() -> Weight {
(153_329_000 as Weight)
(164_394_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(12 as Weight))
}
fn send_16_kb_message_worst_case() -> Weight {
(200_113_000 as Weight)
(223_521_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(12 as Weight))
}
fn increase_message_fee() -> Weight {
(6_407_252_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(4 as Weight))
(6_709_925_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(3 as Weight))
}
fn receive_single_message_proof() -> Weight {
(141_256_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
(206_769_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(3 as Weight))
}
fn receive_two_messages_proof() -> Weight {
(247_723_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
(343_982_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(3 as Weight))
}
fn receive_single_message_proof_with_outbound_lane_state() -> Weight {
(159_731_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
(223_738_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(3 as Weight))
}
fn receive_single_message_proof_1_kb() -> Weight {
(168_546_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
(235_369_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(3 as Weight))
}
fn receive_single_message_proof_16_kb() -> Weight {
(450_087_000 as Weight)
(510_338_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 {
(141_536_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
}
fn receive_delivery_proof_for_single_message() -> Weight {
(164_519_000 as Weight)
(128_805_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(6 as Weight))
.saturating_add(RocksDbWeight::get().writes(3 as Weight))
}
fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight {
(173_300_000 as Weight)
(137_143_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(7 as Weight))
.saturating_add(RocksDbWeight::get().writes(3 as Weight))
}
fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight {
(246_205_000 as Weight)
(193_108_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(8 as Weight))
.saturating_add(RocksDbWeight::get().writes(4 as Weight))
}
fn send_messages_of_various_lengths(i: u32) -> Weight {
(149_551_000 as Weight)
.saturating_add((3_000 as Weight).saturating_mul(i as Weight))
(133_632_000 as Weight)
.saturating_add((4_000 as Weight).saturating_mul(i as Weight))
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(12 as Weight))
}
fn receive_multiple_messages_proof(i: u32) -> Weight {
(0 as Weight)
.saturating_add((114_817_000 as Weight).saturating_mul(i as Weight))
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
.saturating_add((145_006_000 as Weight).saturating_mul(i as Weight))
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(3 as Weight))
}
fn receive_message_proofs_with_extra_nodes(i: u32) -> Weight {
(437_797_000 as Weight)
(486_301_000 as Weight)
.saturating_add((10_000 as Weight).saturating_mul(i as Weight))
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(3 as Weight))
}
fn receive_message_proofs_with_large_leaf(i: u32) -> Weight {
(137_633_000 as Weight)
(178_139_000 as Weight)
.saturating_add((7_000 as Weight).saturating_mul(i as Weight))
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(3 as Weight))
}
fn receive_multiple_messages_proof_with_outbound_lane_state(i: u32) -> Weight {
(0 as Weight)
.saturating_add((118_482_000 as Weight).saturating_mul(i as Weight))
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
.saturating_add((150_844_000 as Weight).saturating_mul(i as Weight))
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(3 as Weight))
}
fn receive_delivery_proof_for_multiple_messages_by_single_relayer(i: u32) -> Weight {
(116_036_000 as Weight)
.saturating_add((7_118_000 as Weight).saturating_mul(i as Weight))
(113_140_000 as Weight)
.saturating_add((7_656_000 as Weight).saturating_mul(i 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().writes(3 as Weight))
}
fn receive_delivery_proof_for_multiple_messages_by_multiple_relayers(i: u32) -> Weight {
(172_780_000 as Weight)
.saturating_add((63_718_000 as Weight).saturating_mul(i as Weight))
(97_424_000 as Weight)
.saturating_add((63_128_000 as Weight).saturating_mul(i 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().writes(3 as Weight))
@@ -34,6 +34,7 @@ pub fn ensure_weights_are_correct<W: WeightInfoExt>(
expected_default_message_delivery_tx_weight: Weight,
expected_additional_byte_delivery_weight: Weight,
expected_messages_delivery_confirmation_tx_weight: Weight,
expected_pay_inbound_dispatch_fee_weight: Weight,
) {
// verify `send_message` weight components
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,
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.
@@ -304,6 +314,13 @@ pub trait WeightInfoExt: WeightInfo {
(Self::receive_single_message_proof_16_kb() - Self::receive_single_message_proof_1_kb()) / (15 * 1024);
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 () {