Update bridges subtree (#2996)

Update bridges subtree
This commit is contained in:
Serban Iorga
2024-01-19 17:50:24 +01:00
committed by GitHub
parent 2e9b4405ed
commit 320b52892e
53 changed files with 565 additions and 533 deletions
+3 -2
View File
@@ -32,9 +32,8 @@
//! Shall the fork occur on the bridged chain governance intervention will be required to
//! re-initialize the bridge and track the right fork.
#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
// Runtime-generated enums
#![allow(clippy::large_enum_variant)]
pub use storage_types::StoredAuthoritySet;
@@ -408,7 +407,9 @@ pub mod pallet {
pub enum Event<T: Config<I>, I: 'static = ()> {
/// Best finalized chain header has been updated to the header with given number and hash.
UpdatedBestFinalizedHeader {
/// Number of the new best finalized header.
number: BridgedBlockNumber<T, I>,
/// Hash of the new best finalized header.
hash: BridgedBlockHash<T, I>,
/// The Grandpa info associated to the new best finalized header.
grandpa_info: StoredHeaderGrandpaInfo<BridgedHeader<T, I>>,
+4 -2
View File
@@ -18,7 +18,7 @@
#![allow(clippy::from_over_into)]
use bp_header_chain::ChainWithGrandpa;
use bp_runtime::Chain;
use bp_runtime::{Chain, ChainId};
use frame_support::{
construct_runtime, derive_impl, parameter_types, traits::Hooks, weights::Weight,
};
@@ -64,7 +64,9 @@ impl grandpa::Config for TestRuntime {
pub struct TestBridgedChain;
impl Chain for TestBridgedChain {
type BlockNumber = TestNumber;
const ID: ChainId = *b"tbch";
type BlockNumber = frame_system::pallet_prelude::BlockNumberFor<TestRuntime>;
type Hash = <TestRuntime as frame_system::Config>::Hash;
type Hasher = <TestRuntime as frame_system::Config>::Hashing;
type Header = TestHeader;
+6 -20
View File
@@ -116,26 +116,12 @@ maximal possible transaction size of the chain and so on. And when the relayer s
implementation must be able to parse and verify the proof of messages delivery. Normally, you would reuse the same
(configurable) type on all chains that are sending messages to the same bridged chain.
The `pallet_bridge_messages::Config::LaneMessageVerifier` defines a single callback to verify outbound messages. The
simplest callback may just accept all messages. But in this case you'll need to answer many questions first. Who will
pay for the delivery and confirmation transaction? Are we sure that someone will ever deliver this message to the
bridged chain? Are we sure that we don't bloat our runtime storage by accepting this message? What if the message is
improperly encoded or has some fields set to invalid values? Answering all those (and similar) questions would lead to
correct implementation.
There's another thing to consider when implementing type for use in
`pallet_bridge_messages::Config::LaneMessageVerifier`. It is whether we treat all message lanes identically, or they'll
have different sets of verification rules? For example, you may reserve lane#1 for messages coming from some
'wrapped-token' pallet - then you may verify in your implementation that the origin is associated with this pallet.
Lane#2 may be reserved for 'system' messages and you may charge zero fee for such messages. You may have some rate
limiting for messages sent over the lane#3. Or you may just verify the same rules set for all outbound messages - it is
all up to the `pallet_bridge_messages::Config::LaneMessageVerifier` implementation.
The last type is the `pallet_bridge_messages::Config::DeliveryConfirmationPayments`. When confirmation transaction is
received, we call the `pay_reward()` method, passing the range of delivered messages. You may use the
[`pallet-bridge-relayers`](../relayers/) pallet and its
[`DeliveryConfirmationPaymentsAdapter`](../relayers/src/payment_adapter.rs) adapter as a possible implementation. It
allows you to pay fixed reward for relaying the message and some of its portion for confirming delivery.
The last type is the `pallet_bridge_messages::Config::DeliveryConfirmationPayments`. When confirmation
transaction is received, we call the `pay_reward()` method, passing the range of delivered messages.
You may use the [`pallet-bridge-relayers`](../relayers/) pallet and its
[`DeliveryConfirmationPaymentsAdapter`](../relayers/src/payment_adapter.rs) adapter as a possible
implementation. It allows you to pay fixed reward for relaying the message and some of its portion
for confirming delivery.
### I have a Messages Module in my Runtime, but I Want to Reject all Outbound Messages. What shall I do?
+2 -2
View File
@@ -31,7 +31,7 @@ use codec::Decode;
use frame_benchmarking::{account, benchmarks_instance_pallet};
use frame_support::weights::Weight;
use frame_system::RawOrigin;
use sp_runtime::traits::TrailingZeroInput;
use sp_runtime::{traits::TrailingZeroInput, BoundedVec};
use sp_std::{ops::RangeInclusive, prelude::*};
const SEED: u32 = 0;
@@ -443,7 +443,7 @@ benchmarks_instance_pallet! {
fn send_regular_message<T: Config<I>, I: 'static>() {
let mut outbound_lane = outbound_lane::<T, I>(T::bench_lane_id());
outbound_lane.send_message(vec![]).expect("We craft valid messages");
outbound_lane.send_message(BoundedVec::try_from(vec![]).expect("We craft valid messages"));
}
fn receive_messages<T: Config<I>, I: 'static>(nonce: MessageNonce) {
+110 -121
View File
@@ -33,9 +33,8 @@
//! If this test fails with your weights, then either weights are computed incorrectly,
//! or some benchmarks assumptions are broken for your runtime.
#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
// Generated by `decl_event!`
#![allow(clippy::unused_unit)]
pub use inbound_lane::StoredInboundLaneData;
pub use outbound_lane::StoredMessagePayload;
@@ -53,8 +52,7 @@ use crate::{
use bp_messages::{
source_chain::{
DeliveryConfirmationPayments, LaneMessageVerifier, OnMessagesDelivered,
SendMessageArtifacts, TargetHeaderChain,
DeliveryConfirmationPayments, OnMessagesDelivered, SendMessageArtifacts, TargetHeaderChain,
},
target_chain::{
DeliveryPayments, DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages,
@@ -155,8 +153,6 @@ pub mod pallet {
/// Target header chain.
type TargetHeaderChain: TargetHeaderChain<Self::OutboundPayload, Self::AccountId>;
/// Message payload verifier.
type LaneMessageVerifier: LaneMessageVerifier<Self::OutboundPayload>;
/// Delivery confirmation payments.
type DeliveryConfirmationPayments: DeliveryConfirmationPayments<Self::AccountId>;
/// Delivery confirmation callback.
@@ -517,16 +513,28 @@ pub mod pallet {
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config<I>, I: 'static = ()> {
/// Message has been accepted and is waiting to be delivered.
MessageAccepted { lane_id: LaneId, nonce: MessageNonce },
MessageAccepted {
/// Lane, which has accepted the message.
lane_id: LaneId,
/// Nonce of accepted message.
nonce: MessageNonce,
},
/// Messages have been received from the bridged chain.
MessagesReceived(
/// Result of received messages dispatch.
Vec<ReceivedMessages<<T::MessageDispatch as MessageDispatch>::DispatchLevelResult>>,
),
/// Messages in the inclusive range have been delivered to the bridged chain.
MessagesDelivered { lane_id: LaneId, messages: DeliveredMessages },
MessagesDelivered {
/// Lane for which the delivery has been confirmed.
lane_id: LaneId,
/// Delivered messages.
messages: DeliveredMessages,
},
}
#[pallet::error]
#[derive(PartialEq, Eq)]
pub enum Error<T, I = ()> {
/// Pallet is not in Normal operating mode.
NotOperatingNormally,
@@ -536,8 +544,6 @@ pub mod pallet {
MessageDispatchInactive,
/// Message has been treated as invalid by chain verifier.
MessageRejectedByChainVerifier(VerificationError),
/// Message has been treated as invalid by lane verifier.
MessageRejectedByLaneVerifier(VerificationError),
/// Message has been treated as invalid by the pallet logic.
MessageRejectedByPallet(VerificationError),
/// Submitter has failed to pay fee for delivering and dispatching messages.
@@ -683,80 +689,72 @@ pub mod pallet {
}
}
/// Structure, containing a validated message payload and all the info required
/// to send it on the bridge.
#[derive(Debug, PartialEq, Eq)]
pub struct SendMessageArgs<T: Config<I>, I: 'static> {
lane_id: LaneId,
payload: StoredMessagePayload<T, I>,
}
impl<T, I> bp_messages::source_chain::MessagesBridge<T::OutboundPayload> for Pallet<T, I>
where
T: Config<I>,
I: 'static,
{
type Error = sp_runtime::DispatchErrorWithPostInfo<PostDispatchInfo>;
type Error = Error<T, I>;
type SendMessageArgs = SendMessageArgs<T, I>;
fn send_message(
fn validate_message(
lane: LaneId,
message: T::OutboundPayload,
) -> Result<SendMessageArtifacts, Self::Error> {
crate::send_message::<T, I>(lane, message)
message: &T::OutboundPayload,
) -> Result<SendMessageArgs<T, I>, Self::Error> {
ensure_normal_operating_mode::<T, I>()?;
// let's check if outbound lane is active
ensure!(T::ActiveOutboundLanes::get().contains(&lane), Error::<T, I>::InactiveOutboundLane);
// let's first check if message can be delivered to target chain
T::TargetHeaderChain::verify_message(message).map_err(|err| {
log::trace!(
target: LOG_TARGET,
"Message to lane {:?} is rejected by target chain: {:?}",
lane,
err,
);
Error::<T, I>::MessageRejectedByChainVerifier(err)
})?;
Ok(SendMessageArgs {
lane_id: lane,
payload: StoredMessagePayload::<T, I>::try_from(message.encode()).map_err(|_| {
Error::<T, I>::MessageRejectedByPallet(VerificationError::MessageTooLarge)
})?,
})
}
}
/// Function that actually sends message.
fn send_message<T: Config<I>, I: 'static>(
lane_id: LaneId,
payload: T::OutboundPayload,
) -> sp_std::result::Result<
SendMessageArtifacts,
sp_runtime::DispatchErrorWithPostInfo<PostDispatchInfo>,
> {
ensure_normal_operating_mode::<T, I>()?;
fn send_message(args: SendMessageArgs<T, I>) -> SendMessageArtifacts {
// save message in outbound storage and emit event
let mut lane = outbound_lane::<T, I>(args.lane_id);
let message_len = args.payload.len();
let nonce = lane.send_message(args.payload);
// let's check if outbound lane is active
ensure!(T::ActiveOutboundLanes::get().contains(&lane_id), Error::<T, I>::InactiveOutboundLane,);
// return number of messages in the queue to let sender know about its state
let enqueued_messages = lane.data().queued_messages().saturating_len();
// let's first check if message can be delivered to target chain
T::TargetHeaderChain::verify_message(&payload).map_err(|err| {
log::trace!(
target: LOG_TARGET,
"Message to lane {:?} is rejected by target chain: {:?}",
lane_id,
err,
"Accepted message {} to lane {:?}. Message size: {:?}",
nonce,
args.lane_id,
message_len,
);
Error::<T, I>::MessageRejectedByChainVerifier(err)
})?;
Pallet::<T, I>::deposit_event(Event::MessageAccepted { lane_id: args.lane_id, nonce });
// now let's enforce any additional lane rules
let mut lane = outbound_lane::<T, I>(lane_id);
T::LaneMessageVerifier::verify_message(&lane_id, &lane.data(), &payload).map_err(|err| {
log::trace!(
target: LOG_TARGET,
"Message to lane {:?} is rejected by lane verifier: {:?}",
lane_id,
err,
);
Error::<T, I>::MessageRejectedByLaneVerifier(err)
})?;
// finally, save message in outbound storage and emit event
let encoded_payload = payload.encode();
let encoded_payload_len = encoded_payload.len();
let nonce = lane
.send_message(encoded_payload)
.map_err(Error::<T, I>::MessageRejectedByPallet)?;
// return number of messages in the queue to let sender know about its state
let enqueued_messages = lane.data().queued_messages().saturating_len();
log::trace!(
target: LOG_TARGET,
"Accepted message {} to lane {:?}. Message size: {:?}",
nonce,
lane_id,
encoded_payload_len,
);
Pallet::<T, I>::deposit_event(Event::MessageAccepted { lane_id, nonce });
Ok(SendMessageArtifacts { nonce, enqueued_messages })
SendMessageArtifacts { nonce, enqueued_messages }
}
}
/// Ensure that the pallet is in normal operational mode.
@@ -857,6 +855,8 @@ struct RuntimeOutboundLaneStorage<T, I = ()> {
}
impl<T: Config<I>, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorage<T, I> {
type StoredMessagePayload = StoredMessagePayload<T, I>;
fn id(&self) -> LaneId {
self.lane_id
}
@@ -870,22 +870,15 @@ impl<T: Config<I>, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorag
}
#[cfg(test)]
fn message(&self, nonce: &MessageNonce) -> Option<MessagePayload> {
fn message(&self, nonce: &MessageNonce) -> Option<Self::StoredMessagePayload> {
OutboundMessages::<T, I>::get(MessageKey { lane_id: self.lane_id, nonce: *nonce })
.map(Into::into)
}
fn save_message(
&mut self,
nonce: MessageNonce,
message_payload: MessagePayload,
) -> Result<(), VerificationError> {
fn save_message(&mut self, nonce: MessageNonce, message_payload: Self::StoredMessagePayload) {
OutboundMessages::<T, I>::insert(
MessageKey { lane_id: self.lane_id, nonce },
StoredMessagePayload::<T, I>::try_from(message_payload)
.map_err(|_| VerificationError::MessageTooLarge)?,
message_payload,
);
Ok(())
}
fn remove_message(&mut self, nonce: &MessageNonce) {
@@ -932,7 +925,10 @@ mod tests {
},
outbound_lane::ReceivalConfirmationError,
};
use bp_messages::{BridgeMessagesCall, UnrewardedRelayer, UnrewardedRelayersState};
use bp_messages::{
source_chain::MessagesBridge, BridgeMessagesCall, UnrewardedRelayer,
UnrewardedRelayersState,
};
use bp_test_utils::generate_owned_bridge_module_tests;
use frame_support::{
assert_noop, assert_ok,
@@ -949,14 +945,15 @@ mod tests {
System::<TestRuntime>::reset_events();
}
fn send_regular_message() {
fn send_regular_message(lane_id: LaneId) {
get_ready_for_events();
let outbound_lane = outbound_lane::<TestRuntime, ()>(TEST_LANE_ID);
let outbound_lane = outbound_lane::<TestRuntime, ()>(lane_id);
let message_nonce = outbound_lane.data().latest_generated_nonce + 1;
let prev_enqueud_messages = outbound_lane.data().queued_messages().saturating_len();
let artifacts = send_message::<TestRuntime, ()>(TEST_LANE_ID, REGULAR_PAYLOAD)
.expect("send_message has failed");
let valid_message = Pallet::<TestRuntime, ()>::validate_message(lane_id, &REGULAR_PAYLOAD)
.expect("validate_message has failed");
let artifacts = Pallet::<TestRuntime, ()>::send_message(valid_message);
assert_eq!(artifacts.enqueued_messages, prev_enqueud_messages + 1);
// check event with assigned nonce
@@ -965,7 +962,7 @@ mod tests {
vec![EventRecord {
phase: Phase::Initialization,
event: TestEvent::Messages(Event::MessageAccepted {
lane_id: TEST_LANE_ID,
lane_id,
nonce: message_nonce
}),
topics: vec![],
@@ -1016,14 +1013,14 @@ mod tests {
fn pallet_rejects_transactions_if_halted() {
run_test(|| {
// send message first to be able to check that delivery_proof fails later
send_regular_message();
send_regular_message(TEST_LANE_ID);
PalletOperatingMode::<TestRuntime, ()>::put(MessagesOperatingMode::Basic(
BasicOperatingMode::Halted,
));
assert_noop!(
send_message::<TestRuntime, ()>(TEST_LANE_ID, REGULAR_PAYLOAD,),
Pallet::<TestRuntime, ()>::validate_message(TEST_LANE_ID, &REGULAR_PAYLOAD),
Error::<TestRuntime, ()>::NotOperatingNormally,
);
@@ -1066,14 +1063,14 @@ mod tests {
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();
send_regular_message(TEST_LANE_ID);
PalletOperatingMode::<TestRuntime, ()>::put(
MessagesOperatingMode::RejectingOutboundMessages,
);
assert_noop!(
send_message::<TestRuntime, ()>(TEST_LANE_ID, REGULAR_PAYLOAD,),
Pallet::<TestRuntime, ()>::validate_message(TEST_LANE_ID, &REGULAR_PAYLOAD),
Error::<TestRuntime, ()>::NotOperatingNormally,
);
@@ -1109,7 +1106,7 @@ mod tests {
#[test]
fn send_message_works() {
run_test(|| {
send_regular_message();
send_regular_message(TEST_LANE_ID);
});
}
@@ -1123,7 +1120,7 @@ mod tests {
.extra
.extend_from_slice(&[0u8; MAX_OUTBOUND_PAYLOAD_SIZE as usize]);
assert_noop!(
send_message::<TestRuntime, ()>(TEST_LANE_ID, message_payload.clone(),),
Pallet::<TestRuntime, ()>::validate_message(TEST_LANE_ID, &message_payload.clone(),),
Error::<TestRuntime, ()>::MessageRejectedByPallet(
VerificationError::MessageTooLarge
),
@@ -1134,7 +1131,11 @@ mod tests {
message_payload.extra.pop();
}
assert_eq!(message_payload.encoded_size() as u32, MAX_OUTBOUND_PAYLOAD_SIZE);
assert_ok!(send_message::<TestRuntime, ()>(TEST_LANE_ID, message_payload,),);
let valid_message =
Pallet::<TestRuntime, ()>::validate_message(TEST_LANE_ID, &message_payload)
.expect("validate_message has failed");
Pallet::<TestRuntime, ()>::send_message(valid_message);
})
}
@@ -1143,7 +1144,10 @@ mod tests {
run_test(|| {
// messages with this payload are rejected by target chain verifier
assert_noop!(
send_message::<TestRuntime, ()>(TEST_LANE_ID, PAYLOAD_REJECTED_BY_TARGET_CHAIN,),
Pallet::<TestRuntime, ()>::validate_message(
TEST_LANE_ID,
&PAYLOAD_REJECTED_BY_TARGET_CHAIN,
),
Error::<TestRuntime, ()>::MessageRejectedByChainVerifier(VerificationError::Other(
mock::TEST_ERROR
)),
@@ -1151,21 +1155,6 @@ mod tests {
});
}
#[test]
fn lane_verifier_rejects_invalid_message_in_send_message() {
run_test(|| {
// messages with zero fee are rejected by lane verifier
let mut message = REGULAR_PAYLOAD;
message.reject_by_lane_verifier = true;
assert_noop!(
send_message::<TestRuntime, ()>(TEST_LANE_ID, message,),
Error::<TestRuntime, ()>::MessageRejectedByLaneVerifier(VerificationError::Other(
mock::TEST_ERROR
)),
);
});
}
#[test]
fn receive_messages_proof_works() {
run_test(|| {
@@ -1318,7 +1307,7 @@ mod tests {
#[test]
fn receive_messages_delivery_proof_works() {
run_test(|| {
send_regular_message();
send_regular_message(TEST_LANE_ID);
receive_messages_delivery_proof();
assert_eq!(
@@ -1331,8 +1320,8 @@ mod tests {
#[test]
fn receive_messages_delivery_proof_rewards_relayers() {
run_test(|| {
assert_ok!(send_message::<TestRuntime, ()>(TEST_LANE_ID, REGULAR_PAYLOAD,));
assert_ok!(send_message::<TestRuntime, ()>(TEST_LANE_ID, REGULAR_PAYLOAD,));
send_regular_message(TEST_LANE_ID);
send_regular_message(TEST_LANE_ID);
// this reports delivery of message 1 => reward is paid to TEST_RELAYER_A
let single_message_delivery_proof = TestMessagesDeliveryProof(Ok((
@@ -1718,9 +1707,9 @@ mod tests {
#[test]
fn messages_delivered_callbacks_are_called() {
run_test(|| {
send_regular_message();
send_regular_message();
send_regular_message();
send_regular_message(TEST_LANE_ID);
send_regular_message(TEST_LANE_ID);
send_regular_message(TEST_LANE_ID);
// messages 1+2 are confirmed in 1 tx, message 3 in a separate tx
// dispatch of message 2 has failed
@@ -1779,7 +1768,7 @@ mod tests {
) {
run_test(|| {
// send message first to be able to check that delivery_proof fails later
send_regular_message();
send_regular_message(TEST_LANE_ID);
// 1) InboundLaneData declares that the `last_confirmed_nonce` is 1;
// 2) InboundLaneData has no entries => `InboundLaneData::last_delivered_nonce()`
@@ -1846,10 +1835,10 @@ mod tests {
#[test]
fn on_idle_callback_respects_remaining_weight() {
run_test(|| {
send_regular_message();
send_regular_message();
send_regular_message();
send_regular_message();
send_regular_message(TEST_LANE_ID);
send_regular_message(TEST_LANE_ID);
send_regular_message(TEST_LANE_ID);
send_regular_message(TEST_LANE_ID);
assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
RuntimeOrigin::signed(1),
@@ -1928,10 +1917,10 @@ mod tests {
fn on_idle_callback_is_rotating_lanes_to_prune() {
run_test(|| {
// send + receive confirmation for lane 1
send_regular_message();
send_regular_message(TEST_LANE_ID);
receive_messages_delivery_proof();
// send + receive confirmation for lane 2
assert_ok!(send_message::<TestRuntime, ()>(TEST_LANE_ID_2, REGULAR_PAYLOAD,));
send_regular_message(TEST_LANE_ID_2);
assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
RuntimeOrigin::signed(1),
TestMessagesDeliveryProof(Ok((
@@ -2007,7 +1996,7 @@ mod tests {
fn outbound_message_from_unconfigured_lane_is_rejected() {
run_test(|| {
assert_noop!(
send_message::<TestRuntime, ()>(TEST_LANE_ID_3, REGULAR_PAYLOAD,),
Pallet::<TestRuntime, ()>::validate_message(TEST_LANE_ID_3, &REGULAR_PAYLOAD,),
Error::<TestRuntime, ()>::InactiveOutboundLane,
);
});
+6 -30
View File
@@ -17,19 +17,17 @@
// From construct_runtime macro
#![allow(clippy::from_over_into)]
use crate::Config;
use crate::{Config, StoredMessagePayload};
use bp_messages::{
calc_relayers_rewards,
source_chain::{
DeliveryConfirmationPayments, LaneMessageVerifier, OnMessagesDelivered, TargetHeaderChain,
},
source_chain::{DeliveryConfirmationPayments, OnMessagesDelivered, TargetHeaderChain},
target_chain::{
DeliveryPayments, DispatchMessage, DispatchMessageData, MessageDispatch,
ProvedLaneMessages, ProvedMessages, SourceHeaderChain,
},
DeliveredMessages, InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload,
OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState, VerificationError,
DeliveredMessages, InboundLaneData, LaneId, Message, MessageKey, MessageNonce,
UnrewardedRelayer, UnrewardedRelayersState, VerificationError,
};
use bp_runtime::{messages::MessageDispatchResult, Size};
use codec::{Decode, Encode};
@@ -50,8 +48,6 @@ pub type Balance = u64;
pub struct TestPayload {
/// Field that may be used to identify messages.
pub id: u64,
/// Reject this message by lane verifier?
pub reject_by_lane_verifier: bool,
/// Dispatch weight that is declared by the message sender.
pub declared_weight: Weight,
/// Message dispatch result.
@@ -120,7 +116,6 @@ impl Config for TestRuntime {
type DeliveryPayments = TestDeliveryPayments;
type TargetHeaderChain = TestTargetHeaderChain;
type LaneMessageVerifier = TestLaneMessageVerifier;
type DeliveryConfirmationPayments = TestDeliveryConfirmationPayments;
type OnMessagesDelivered = TestOnMessagesDelivered;
@@ -268,24 +263,6 @@ impl TargetHeaderChain<TestPayload, TestRelayer> for TestTargetHeaderChain {
}
}
/// Lane message verifier that is used in tests.
#[derive(Debug, Default)]
pub struct TestLaneMessageVerifier;
impl LaneMessageVerifier<TestPayload> for TestLaneMessageVerifier {
fn verify_message(
_lane: &LaneId,
_lane_outbound_data: &OutboundLaneData,
payload: &TestPayload,
) -> Result<(), VerificationError> {
if !payload.reject_by_lane_verifier {
Ok(())
} else {
Err(VerificationError::Other(TEST_ERROR))
}
}
}
/// Reward payments at the target chain during delivery transaction.
#[derive(Debug, Default)]
pub struct TestDeliveryPayments;
@@ -425,8 +402,8 @@ pub fn message(nonce: MessageNonce, payload: TestPayload) -> Message {
}
/// Return valid outbound message data, constructed from given payload.
pub fn outbound_message_data(payload: TestPayload) -> MessagePayload {
payload.encode()
pub fn outbound_message_data(payload: TestPayload) -> StoredMessagePayload<TestRuntime, ()> {
StoredMessagePayload::<TestRuntime, ()>::try_from(payload.encode()).expect("payload too large")
}
/// Return valid inbound (dispatch) message data, constructed from given payload.
@@ -438,7 +415,6 @@ pub fn inbound_message_data(payload: TestPayload) -> DispatchMessageData<TestPay
pub const fn message_payload(id: u64, declared_weight: u64) -> TestPayload {
TestPayload {
id,
reject_by_lane_verifier: false,
declared_weight: Weight::from_parts(declared_weight, 0),
dispatch_result: dispatch_result(0),
extra: Vec::new(),
+25 -33
View File
@@ -18,10 +18,7 @@
use crate::{Config, LOG_TARGET};
use bp_messages::{
DeliveredMessages, LaneId, MessageNonce, MessagePayload, OutboundLaneData, UnrewardedRelayer,
VerificationError,
};
use bp_messages::{DeliveredMessages, LaneId, MessageNonce, OutboundLaneData, UnrewardedRelayer};
use codec::{Decode, Encode};
use frame_support::{
weights::{RuntimeDbWeight, Weight},
@@ -34,6 +31,8 @@ use sp_std::collections::vec_deque::VecDeque;
/// Outbound lane storage.
pub trait OutboundLaneStorage {
type StoredMessagePayload;
/// Lane id.
fn id(&self) -> LaneId;
/// Get lane data from the storage.
@@ -42,13 +41,9 @@ pub trait OutboundLaneStorage {
fn set_data(&mut self, data: OutboundLaneData);
/// Returns saved outbound message payload.
#[cfg(test)]
fn message(&self, nonce: &MessageNonce) -> Option<MessagePayload>;
fn message(&self, nonce: &MessageNonce) -> Option<Self::StoredMessagePayload>;
/// Save outbound message in the storage.
fn save_message(
&mut self,
nonce: MessageNonce,
message_payload: MessagePayload,
) -> Result<(), VerificationError>;
fn save_message(&mut self, nonce: MessageNonce, message_payload: Self::StoredMessagePayload);
/// Remove outbound message from the storage.
fn remove_message(&mut self, nonce: &MessageNonce);
}
@@ -91,18 +86,15 @@ impl<S: OutboundLaneStorage> OutboundLane<S> {
/// Send message over lane.
///
/// Returns new message nonce.
pub fn send_message(
&mut self,
message_payload: MessagePayload,
) -> Result<MessageNonce, VerificationError> {
pub fn send_message(&mut self, message_payload: S::StoredMessagePayload) -> MessageNonce {
let mut data = self.storage.data();
let nonce = data.latest_generated_nonce + 1;
data.latest_generated_nonce = nonce;
self.storage.save_message(nonce, message_payload)?;
self.storage.save_message(nonce, message_payload);
self.storage.set_data(data);
Ok(nonce)
nonce
}
/// Confirm messages delivery.
@@ -218,7 +210,7 @@ mod tests {
},
outbound_lane,
};
use frame_support::{assert_ok, weights::constants::RocksDbWeight};
use frame_support::weights::constants::RocksDbWeight;
use sp_std::ops::RangeInclusive;
fn unrewarded_relayers(
@@ -239,9 +231,9 @@ mod tests {
) -> Result<Option<DeliveredMessages>, ReceivalConfirmationError> {
run_test(|| {
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
assert_ok!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)));
assert_ok!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)));
assert_ok!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
assert_eq!(lane.storage.data().latest_received_nonce, 0);
let result = lane.confirm_delivery(3, latest_received_nonce, relayers);
@@ -256,7 +248,7 @@ mod tests {
run_test(|| {
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
assert_eq!(lane.storage.data().latest_generated_nonce, 0);
assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), Ok(1));
assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1);
assert!(lane.storage.message(&1).is_some());
assert_eq!(lane.storage.data().latest_generated_nonce, 1);
});
@@ -266,9 +258,9 @@ mod tests {
fn confirm_delivery_works() {
run_test(|| {
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), Ok(1));
assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), Ok(2));
assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), Ok(3));
assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1);
assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 2);
assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 3);
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
assert_eq!(lane.storage.data().latest_received_nonce, 0);
assert_eq!(
@@ -284,9 +276,9 @@ mod tests {
fn confirm_delivery_rejects_nonce_lesser_than_latest_received() {
run_test(|| {
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
assert_ok!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)));
assert_ok!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)));
assert_ok!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
assert_eq!(lane.storage.data().latest_received_nonce, 0);
assert_eq!(
@@ -368,9 +360,9 @@ mod tests {
);
assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1);
// when nothing is confirmed, nothing is pruned
assert_ok!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)));
assert_ok!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)));
assert_ok!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
assert!(lane.storage.message(&1).is_some());
assert!(lane.storage.message(&2).is_some());
assert!(lane.storage.message(&3).is_some());
@@ -412,9 +404,9 @@ mod tests {
fn confirm_delivery_detects_when_more_than_expected_messages_are_confirmed() {
run_test(|| {
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
assert_ok!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)));
assert_ok!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)));
assert_ok!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
lane.send_message(outbound_message_data(REGULAR_PAYLOAD));
assert_eq!(
lane.confirm_delivery(0, 3, &unrewarded_relayers(1..=3)),
Err(ReceivalConfirmationError::TryingToConfirmMoreMessagesThanExpected),
+1 -1
View File
@@ -178,7 +178,7 @@ mod tests {
RuntimeCall::Parachains(crate::Call::<TestRuntime, ()>::submit_parachain_heads {
at_relay_block: (num, Default::default()),
parachains,
parachain_heads_proof: ParaHeadsProof(Vec::new()),
parachain_heads_proof: ParaHeadsProof { storage_proof: Vec::new() },
})
.check_obsolete_submit_parachain_heads()
.is_ok()
+29 -5
View File
@@ -21,6 +21,7 @@
//! accepts storage proof of some parachain `Heads` entries from bridged relay chain.
//! It requires corresponding relay headers to be already synced.
#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
pub use weights::WeightInfo;
@@ -98,27 +99,49 @@ pub mod pallet {
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config<I>, I: 'static = ()> {
/// The caller has provided head of parachain that the pallet is not configured to track.
UntrackedParachainRejected { parachain: ParaId },
UntrackedParachainRejected {
/// Identifier of the parachain that is not tracked by the pallet.
parachain: ParaId,
},
/// The caller has declared that he has provided given parachain head, but it is missing
/// from the storage proof.
MissingParachainHead { parachain: ParaId },
MissingParachainHead {
/// Identifier of the parachain with missing head.
parachain: ParaId,
},
/// The caller has provided parachain head hash that is not matching the hash read from the
/// storage proof.
IncorrectParachainHeadHash {
/// Identifier of the parachain with incorrect head hast.
parachain: ParaId,
/// Specified parachain head hash.
parachain_head_hash: ParaHash,
/// Actual parachain head hash.
actual_parachain_head_hash: ParaHash,
},
/// The caller has provided obsolete parachain head, which is already known to the pallet.
RejectedObsoleteParachainHead { parachain: ParaId, parachain_head_hash: ParaHash },
RejectedObsoleteParachainHead {
/// Identifier of the parachain with obsolete head.
parachain: ParaId,
/// Obsolete parachain head hash.
parachain_head_hash: ParaHash,
},
/// The caller has provided parachain head that exceeds the maximal configured head size.
RejectedLargeParachainHead {
/// Identifier of the parachain with rejected head.
parachain: ParaId,
/// Parachain head hash.
parachain_head_hash: ParaHash,
/// Parachain head size.
parachain_head_size: u32,
},
/// Parachain head has been updated.
UpdatedParachainHead { parachain: ParaId, parachain_head_hash: ParaHash },
UpdatedParachainHead {
/// Identifier of the parachain that has been updated.
parachain: ParaId,
/// Parachain head hash.
parachain_head_hash: ParaHash,
},
}
#[pallet::error]
@@ -137,6 +160,7 @@ pub mod pallet {
pub trait BoundedBridgeGrandpaConfig<I: 'static>:
pallet_bridge_grandpa::Config<I, BridgedChain = Self::BridgedRelayChain>
{
/// Type of the bridged relay chain.
type BridgedRelayChain: Chain<
BlockNumber = RelayBlockNumber,
Hash = RelayBlockHash,
@@ -336,7 +360,7 @@ pub mod pallet {
let mut storage = GrandpaPalletOf::<T, I>::storage_proof_checker(
relay_block_hash,
parachain_heads_proof.0,
parachain_heads_proof.storage_proof,
)
.map_err(Error::<T, I>::HeaderChainStorageProof)?;
+13 -1
View File
@@ -16,7 +16,7 @@
use bp_header_chain::ChainWithGrandpa;
use bp_polkadot_core::parachains::ParaId;
use bp_runtime::{Chain, Parachain};
use bp_runtime::{Chain, ChainId, Parachain};
use frame_support::{
construct_runtime, derive_impl, parameter_types, traits::ConstU32, weights::Weight,
};
@@ -49,6 +49,8 @@ pub type BigParachainHeader = sp_runtime::generic::Header<u128, BlakeTwo256>;
pub struct Parachain1;
impl Chain for Parachain1 {
const ID: ChainId = *b"pch1";
type BlockNumber = u64;
type Hash = H256;
type Hasher = RegularParachainHasher;
@@ -73,6 +75,8 @@ impl Parachain for Parachain1 {
pub struct Parachain2;
impl Chain for Parachain2 {
const ID: ChainId = *b"pch2";
type BlockNumber = u64;
type Hash = H256;
type Hasher = RegularParachainHasher;
@@ -97,6 +101,8 @@ impl Parachain for Parachain2 {
pub struct Parachain3;
impl Chain for Parachain3 {
const ID: ChainId = *b"pch3";
type BlockNumber = u64;
type Hash = H256;
type Hasher = RegularParachainHasher;
@@ -122,6 +128,8 @@ impl Parachain for Parachain3 {
pub struct BigParachain;
impl Chain for BigParachain {
const ID: ChainId = *b"bpch";
type BlockNumber = u128;
type Hash = H256;
type Hasher = RegularParachainHasher;
@@ -229,6 +237,8 @@ impl pallet_bridge_parachains::benchmarking::Config<()> for TestRuntime {
pub struct TestBridgedChain;
impl Chain for TestBridgedChain {
const ID: ChainId = *b"tbch";
type BlockNumber = crate::RelayBlockNumber;
type Hash = crate::RelayBlockHash;
type Hasher = crate::RelayBlockHasher;
@@ -260,6 +270,8 @@ impl ChainWithGrandpa for TestBridgedChain {
pub struct OtherBridgedChain;
impl Chain for OtherBridgedChain {
const ID: ChainId = *b"obch";
type BlockNumber = u64;
type Hash = crate::RelayBlockHash;
type Hasher = crate::RelayBlockHasher;
+34 -37
View File
@@ -42,12 +42,13 @@ type MessagesPallet<T, I> = BridgeMessagesPallet<T, <T as Config<I>>::BridgeMess
impl<T: Config<I>, I: 'static> ExportXcm for Pallet<T, I>
where
T: BridgeMessagesConfig<
<T as Config<I>>::BridgeMessagesPalletInstance,
OutboundPayload = XcmAsPlainPayload,
>,
T: BridgeMessagesConfig<T::BridgeMessagesPalletInstance, OutboundPayload = XcmAsPlainPayload>,
{
type Ticket = (SenderAndLane, XcmAsPlainPayload, XcmHash);
type Ticket = (
SenderAndLane,
<MessagesPallet<T, I> as MessagesBridge<T::OutboundPayload>>::SendMessageArgs,
XcmHash,
);
fn validate(
network: NetworkId,
@@ -74,42 +75,38 @@ where
message,
)?;
Ok(((sender_and_lane, blob, id), price))
}
fn deliver(
(sender_and_lane, blob, id): (SenderAndLane, XcmAsPlainPayload, XcmHash),
) -> Result<XcmHash, SendError> {
let lane_id = sender_and_lane.lane;
let send_result = MessagesPallet::<T, I>::send_message(lane_id, blob);
match send_result {
Ok(artifacts) => {
log::info!(
target: LOG_TARGET,
"XCM message {:?} has been enqueued at bridge {:?} with nonce {}",
id,
lane_id,
artifacts.nonce,
);
// notify XCM queue manager about updated lane state
LocalXcmQueueManager::<T::LanesSupport>::on_bridge_message_enqueued(
&sender_and_lane,
artifacts.enqueued_messages,
);
},
Err(error) => {
let bridge_message = MessagesPallet::<T, I>::validate_message(sender_and_lane.lane, &blob)
.map_err(|e| {
log::debug!(
target: LOG_TARGET,
"XCM message {:?} has been dropped because of bridge error {:?} on bridge {:?}",
"XCM message {:?} cannot be exported because of bridge error {:?} on bridge {:?}",
id,
error,
lane_id,
e,
sender_and_lane.lane,
);
return Err(SendError::Transport("BridgeSendError"))
},
}
SendError::Transport("BridgeValidateError")
})?;
Ok(((sender_and_lane, bridge_message, id), price))
}
fn deliver((sender_and_lane, bridge_message, id): Self::Ticket) -> Result<XcmHash, SendError> {
let lane_id = sender_and_lane.lane;
let artifacts = MessagesPallet::<T, I>::send_message(bridge_message);
log::info!(
target: LOG_TARGET,
"XCM message {:?} has been enqueued at bridge {:?} with nonce {}",
id,
lane_id,
artifacts.nonce,
);
// notify XCM queue manager about updated lane state
LocalXcmQueueManager::<T::LanesSupport>::on_bridge_message_enqueued(
&sender_and_lane,
artifacts.enqueued_messages,
);
Ok(id)
}
+4 -18
View File
@@ -19,11 +19,10 @@
use crate as pallet_xcm_bridge_hub;
use bp_messages::{
source_chain::LaneMessageVerifier,
target_chain::{DispatchMessage, MessageDispatch},
LaneId, OutboundLaneData, VerificationError,
LaneId,
};
use bp_runtime::{messages::MessageDispatchResult, Chain, UnderlyingChainProvider};
use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, UnderlyingChainProvider};
use bridge_runtime_common::{
messages::{
source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter,
@@ -78,20 +77,6 @@ impl pallet_balances::Config for TestRuntime {
type AccountStore = System;
}
/// Lane message verifier that is used in tests.
#[derive(Debug, Default)]
pub struct TestLaneMessageVerifier;
impl LaneMessageVerifier<Vec<u8>> for TestLaneMessageVerifier {
fn verify_message(
_lane: &LaneId,
_lane_outbound_data: &OutboundLaneData,
_payload: &Vec<u8>,
) -> Result<(), VerificationError> {
Ok(())
}
}
parameter_types! {
pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID];
}
@@ -110,7 +95,6 @@ impl pallet_bridge_messages::Config for TestRuntime {
type InboundRelayer = ();
type DeliveryPayments = ();
type TargetHeaderChain = TargetHeaderChainAdapter<OnThisChainBridge>;
type LaneMessageVerifier = TestLaneMessageVerifier;
type DeliveryConfirmationPayments = ();
type OnMessagesDelivered = ();
type SourceHeaderChain = SourceHeaderChainAdapter<OnThisChainBridge>;
@@ -217,6 +201,7 @@ impl XcmBlobHauler for TestXcmBlobHauler {
pub struct ThisChain;
impl Chain for ThisChain {
const ID: ChainId = *b"tuch";
type BlockNumber = u64;
type Hash = H256;
type Hasher = BlakeTwo256;
@@ -240,6 +225,7 @@ pub type BridgedHeaderHash = H256;
pub type BridgedChainHeader = SubstrateHeader;
impl Chain for BridgedChain {
const ID: ChainId = *b"tuch";
type BlockNumber = u64;
type Hash = BridgedHeaderHash;
type Hasher = BlakeTwo256;