fix: Complete snowbridge pezpallet rebrand and critical bug fixes

- snowbridge-pezpallet-* → pezsnowbridge-pezpallet-* (201 refs)
- pallet/ directories → pezpallet/ (4 locations)
- Fixed pezpallet.rs self-include recursion bug
- Fixed sc-chain-spec hardcoded crate name in derive macro
- Reverted .pezpallet_by_name() to .pallet_by_name() (subxt API)
- Added BizinikiwiConfig type alias for zombienet tests
- Deleted obsolete session state files

Verified: pezsnowbridge-pezpallet-*, pezpallet-staking,
pezpallet-staking-async, pezframe-benchmarking-cli all pass cargo check
This commit is contained in:
2025-12-16 09:57:23 +03:00
parent eea003e14d
commit 3139ffa25e
3022 changed files with 42157 additions and 23579 deletions
@@ -0,0 +1,164 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! Defines structures related to calls of the `pezpallet-bridge-messages` pezpallet.
use crate::{MessageNonce, UnrewardedRelayersState};
use codec::{Decode, Encode};
use pezframe_support::weights::Weight;
use scale_info::TypeInfo;
use pezsp_core::RuntimeDebug;
use pezsp_std::ops::RangeInclusive;
/// A minimized version of `pezpallet-bridge-messages::Call` that can be used without a runtime.
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)]
#[allow(non_camel_case_types)]
pub enum BridgeMessagesCall<AccountId, MessagesProof, MessagesDeliveryProof> {
/// `pezpallet-bridge-messages::Call::receive_messages_proof`
#[codec(index = 2)]
receive_messages_proof {
/// Account id of relayer at the **bridged** chain.
relayer_id_at_bridged_chain: AccountId,
/// Messages proof.
proof: MessagesProof,
/// A number of messages in the proof.
messages_count: u32,
/// Total dispatch weight of messages in the proof.
dispatch_weight: Weight,
},
/// `pezpallet-bridge-messages::Call::receive_messages_delivery_proof`
#[codec(index = 3)]
receive_messages_delivery_proof {
/// Messages delivery proof.
proof: MessagesDeliveryProof,
/// "Digest" of unrewarded relayers state at the bridged chain.
relayers_state: UnrewardedRelayersState,
},
}
/// Generic info about a messages delivery/confirmation proof.
#[derive(PartialEq, RuntimeDebug)]
pub struct BaseMessagesProofInfo<LaneId> {
/// Message lane, used by the call.
pub lane_id: LaneId,
/// Nonces of messages, included in the call.
///
/// For delivery transaction, it is nonces of bundled messages. For confirmation
/// transaction, it is nonces that are to be confirmed during the call.
pub bundled_range: RangeInclusive<MessageNonce>,
/// Nonce of the best message, stored by this chain before the call is dispatched.
///
/// For delivery transaction, it is the nonce of best delivered message before the call.
/// For confirmation transaction, it is the nonce of best confirmed message before the call.
pub best_stored_nonce: MessageNonce,
}
impl<LaneId> BaseMessagesProofInfo<LaneId> {
/// Returns true if `bundled_range` continues the `0..=best_stored_nonce` range.
pub fn appends_to_stored_nonce(&self) -> bool {
Some(*self.bundled_range.start()) == self.best_stored_nonce.checked_add(1)
}
}
/// Occupation state of the unrewarded relayers vector.
#[derive(PartialEq, RuntimeDebug)]
#[cfg_attr(test, derive(Default))]
pub struct UnrewardedRelayerOccupation {
/// The number of remaining unoccupied entries for new relayers.
pub free_relayer_slots: MessageNonce,
/// The number of messages that we are ready to accept.
pub free_message_slots: MessageNonce,
}
/// Info about a `ReceiveMessagesProof` call which tries to update a single lane.
#[derive(PartialEq, RuntimeDebug)]
pub struct ReceiveMessagesProofInfo<LaneId> {
/// Base messages proof info
pub base: BaseMessagesProofInfo<LaneId>,
/// State of unrewarded relayers vector.
pub unrewarded_relayers: UnrewardedRelayerOccupation,
}
impl<LaneId> ReceiveMessagesProofInfo<LaneId> {
/// Returns true if:
///
/// - either inbound lane is ready to accept bundled messages;
///
/// - or there are no bundled messages, but the inbound lane is blocked by too many unconfirmed
/// messages and/or unrewarded relayers.
pub fn is_obsolete(&self, is_dispatcher_active: bool) -> bool {
// if dispatcher is inactive, we don't accept any delivery transactions
if !is_dispatcher_active {
return true;
}
// transactions with zero bundled nonces are not allowed, unless they're message
// delivery transactions, which brings reward confirmations required to unblock
// the lane
if self.base.bundled_range.is_empty() {
let empty_transactions_allowed =
// we allow empty transactions when we can't accept delivery from new relayers
self.unrewarded_relayers.free_relayer_slots == 0 ||
// or if we can't accept new messages at all
self.unrewarded_relayers.free_message_slots == 0;
return !empty_transactions_allowed;
}
// otherwise we require bundled messages to continue stored range
!self.base.appends_to_stored_nonce()
}
}
/// Info about a `ReceiveMessagesDeliveryProof` call which tries to update a single lane.
#[derive(PartialEq, RuntimeDebug)]
pub struct ReceiveMessagesDeliveryProofInfo<LaneId>(pub BaseMessagesProofInfo<LaneId>);
impl<LaneId> ReceiveMessagesDeliveryProofInfo<LaneId> {
/// Returns true if outbound lane is ready to accept confirmations of bundled messages.
pub fn is_obsolete(&self) -> bool {
self.0.bundled_range.is_empty() || !self.0.appends_to_stored_nonce()
}
}
/// Info about a `ReceiveMessagesProof` or a `ReceiveMessagesDeliveryProof` call
/// which tries to update a single lane.
#[derive(PartialEq, RuntimeDebug)]
pub enum MessagesCallInfo<LaneId: Clone + Copy> {
/// Messages delivery call info.
ReceiveMessagesProof(ReceiveMessagesProofInfo<LaneId>),
/// Messages delivery confirmation call info.
ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo<LaneId>),
}
impl<LaneId: Clone + Copy> MessagesCallInfo<LaneId> {
/// Returns lane, used by the call.
pub fn lane_id(&self) -> LaneId {
match *self {
Self::ReceiveMessagesProof(ref info) => info.base.lane_id,
Self::ReceiveMessagesDeliveryProof(ref info) => info.0.lane_id,
}
}
/// Returns range of messages, bundled with the call.
pub fn bundled_messages(&self) -> RangeInclusive<MessageNonce> {
match *self {
Self::ReceiveMessagesProof(ref info) => info.base.bundled_range.clone(),
Self::ReceiveMessagesDeliveryProof(ref info) => info.0.bundled_range.clone(),
}
}
}
+336
View File
@@ -0,0 +1,336 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! Primitives of messages module, that represents lane id.
use codec::{Codec, Decode, DecodeWithMemTracking, Encode, EncodeLike, MaxEncodedLen};
use scale_info::TypeInfo;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use pezsp_core::{RuntimeDebug, TypeId, H256};
use pezsp_io::hashing::blake2_256;
use pezsp_std::fmt::Debug;
/// Trait representing a generic `LaneId` type.
pub trait LaneIdType:
Clone
+ Copy
+ Codec
+ EncodeLike
+ Debug
+ Default
+ PartialEq
+ Eq
+ Ord
+ TypeInfo
+ MaxEncodedLen
+ Serialize
+ DeserializeOwned
{
/// Creates a new `LaneId` type (if supported).
fn try_new<E: Ord + Encode>(endpoint1: E, endpoint2: E) -> Result<Self, ()>;
}
/// Bridge lane identifier (legacy).
///
/// Note: For backwards compatibility reasons, we also handle the older format `[u8; 4]`.
#[derive(
Clone,
Copy,
Decode,
DecodeWithMemTracking,
Default,
Encode,
Eq,
Ord,
PartialOrd,
PartialEq,
TypeInfo,
MaxEncodedLen,
Serialize,
Deserialize,
)]
pub struct LegacyLaneId(pub [u8; 4]);
impl LaneIdType for LegacyLaneId {
/// Create lane identifier from two locations.
fn try_new<T: Ord + Encode>(_endpoint1: T, _endpoint2: T) -> Result<Self, ()> {
// we don't support this for `LegacyLaneId`, because it was hard-coded before
Err(())
}
}
#[cfg(feature = "std")]
impl TryFrom<Vec<u8>> for LegacyLaneId {
type Error = ();
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
if value.len() == 4 {
return <[u8; 4]>::try_from(value).map(Self).map_err(|_| ());
}
Err(())
}
}
impl core::fmt::Debug for LegacyLaneId {
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
self.0.fmt(fmt)
}
}
impl AsRef<[u8]> for LegacyLaneId {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl TypeId for LegacyLaneId {
const TYPE_ID: [u8; 4] = *b"blan";
}
/// Bridge lane identifier.
///
/// Lane connects two endpoints at both sides of the bridge. We assume that every endpoint
/// has its own unique identifier. We want lane identifiers to be **the same on the both sides
/// of the bridge** (and naturally unique across global consensus if endpoints have unique
/// identifiers). So lane id is the hash (`blake2_256`) of **ordered** encoded locations
/// concatenation (separated by some binary data). I.e.:
///
/// ```nocompile
/// let endpoint1 = X2(GlobalConsensus(NetworkId::Pezkuwi), Teyrchain(42));
/// let endpoint2 = X2(GlobalConsensus(NetworkId::Kusama), Teyrchain(777));
///
/// let final_lane_key = if endpoint1 < endpoint2 {
/// (endpoint1, VALUES_SEPARATOR, endpoint2)
/// } else {
/// (endpoint2, VALUES_SEPARATOR, endpoint1)
/// }.using_encoded(blake2_256);
/// ```
#[derive(
Clone,
Copy,
Decode,
DecodeWithMemTracking,
Default,
Encode,
Eq,
Ord,
PartialOrd,
PartialEq,
TypeInfo,
MaxEncodedLen,
Serialize,
Deserialize,
)]
pub struct HashedLaneId(H256);
impl HashedLaneId {
/// Create lane identifier from given hash.
///
/// There's no `From<H256>` implementation for the `LaneId`, because using this conversion
/// in a wrong way (i.e. computing hash of endpoints manually) may lead to issues. So we
/// want the call to be explicit.
#[cfg(feature = "std")]
pub const fn from_inner(inner: H256) -> Self {
Self(inner)
}
/// Access the inner lane representation.
pub fn inner(&self) -> &H256 {
&self.0
}
}
impl core::fmt::Display for HashedLaneId {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
core::fmt::Display::fmt(&self.0, f)
}
}
impl core::fmt::Debug for HashedLaneId {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
core::fmt::Debug::fmt(&self.0, f)
}
}
impl TypeId for HashedLaneId {
const TYPE_ID: [u8; 4] = *b"hlan";
}
impl LaneIdType for HashedLaneId {
/// Create lane identifier from two locations.
fn try_new<T: Ord + Encode>(endpoint1: T, endpoint2: T) -> Result<Self, ()> {
const VALUES_SEPARATOR: [u8; 31] = *b"bridges-lane-id-value-separator";
Ok(Self(
if endpoint1 < endpoint2 {
(endpoint1, VALUES_SEPARATOR, endpoint2)
} else {
(endpoint2, VALUES_SEPARATOR, endpoint1)
}
.using_encoded(blake2_256)
.into(),
))
}
}
#[cfg(feature = "std")]
impl TryFrom<Vec<u8>> for HashedLaneId {
type Error = ();
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
if value.len() == 32 {
return <[u8; 32]>::try_from(value).map(|v| Self(H256::from(v))).map_err(|_| ());
}
Err(())
}
}
/// Lane state.
#[derive(Clone, Copy, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen, RuntimeDebug)]
pub enum LaneState {
/// Lane is opened and messages may be sent/received over it.
Opened,
/// Lane is closed and all attempts to send/receive messages to/from this lane
/// will fail.
///
/// Keep in mind that the lane has two ends and the state of the same lane at
/// its ends may be different. Those who are controlling/serving the lane
/// and/or sending messages over the lane, have to coordinate their actions on
/// both ends to make sure that lane is operating smoothly on both ends.
Closed,
}
impl LaneState {
/// Returns true if lane state allows sending/receiving messages.
pub fn is_active(&self) -> bool {
matches!(*self, LaneState::Opened)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::MessageNonce;
#[test]
fn lane_id_debug_format_matches_inner_hash_format() {
assert_eq!(
format!("{:?}", HashedLaneId(H256::from([1u8; 32]))),
format!("{:?}", H256::from([1u8; 32])),
);
assert_eq!(format!("{:?}", LegacyLaneId([0, 0, 0, 1])), format!("{:?}", [0, 0, 0, 1]),);
}
#[test]
fn hashed_encode_decode_works() {
// simple encode/decode - new format
let lane_id = HashedLaneId(H256::from([1u8; 32]));
let encoded_lane_id = lane_id.encode();
let decoded_lane_id = HashedLaneId::decode(&mut &encoded_lane_id[..]).expect("decodable");
assert_eq!(lane_id, decoded_lane_id);
assert_eq!(
"0101010101010101010101010101010101010101010101010101010101010101",
hex::encode(encoded_lane_id)
);
}
#[test]
fn legacy_encode_decode_works() {
// simple encode/decode - old format
let lane_id = LegacyLaneId([0, 0, 0, 1]);
let encoded_lane_id = lane_id.encode();
let decoded_lane_id = LegacyLaneId::decode(&mut &encoded_lane_id[..]).expect("decodable");
assert_eq!(lane_id, decoded_lane_id);
assert_eq!("00000001", hex::encode(encoded_lane_id));
// decode sample
let bytes = vec![0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0];
let (lane, nonce_start, nonce_end): (LegacyLaneId, MessageNonce, MessageNonce) =
Decode::decode(&mut &bytes[..]).unwrap();
assert_eq!(lane, LegacyLaneId([0, 0, 0, 2]));
assert_eq!(nonce_start, 1);
assert_eq!(nonce_end, 1);
// run encode/decode for `LaneId` with different positions
let expected_lane = LegacyLaneId([0, 0, 0, 1]);
let expected_nonce_start = 1088_u64;
let expected_nonce_end = 9185_u64;
// decode: LaneId,Nonce,Nonce
let bytes = (expected_lane, expected_nonce_start, expected_nonce_end).encode();
let (lane, nonce_start, nonce_end): (LegacyLaneId, MessageNonce, MessageNonce) =
Decode::decode(&mut &bytes[..]).unwrap();
assert_eq!(lane, expected_lane);
assert_eq!(nonce_start, expected_nonce_start);
assert_eq!(nonce_end, expected_nonce_end);
// decode: Nonce,LaneId,Nonce
let bytes = (expected_nonce_start, expected_lane, expected_nonce_end).encode();
let (nonce_start, lane, nonce_end): (MessageNonce, LegacyLaneId, MessageNonce) =
Decode::decode(&mut &bytes[..]).unwrap();
assert_eq!(lane, expected_lane);
assert_eq!(nonce_start, expected_nonce_start);
assert_eq!(nonce_end, expected_nonce_end);
// decode: Nonce,Nonce,LaneId
let bytes = (expected_nonce_start, expected_nonce_end, expected_lane).encode();
let (nonce_start, nonce_end, lane): (MessageNonce, MessageNonce, LegacyLaneId) =
Decode::decode(&mut &bytes[..]).unwrap();
assert_eq!(lane, expected_lane);
assert_eq!(nonce_start, expected_nonce_start);
assert_eq!(nonce_end, expected_nonce_end);
}
#[test]
fn hashed_lane_id_is_generated_using_ordered_endpoints() {
assert_eq!(HashedLaneId::try_new(1, 2).unwrap(), HashedLaneId::try_new(2, 1).unwrap());
}
#[test]
fn hashed_lane_id_is_different_for_different_endpoints() {
assert_ne!(HashedLaneId::try_new(1, 2).unwrap(), HashedLaneId::try_new(1, 3).unwrap());
}
#[test]
fn hashed_lane_id_is_different_even_if_arguments_has_partial_matching_encoding() {
/// Some artificial type that generates the same encoding for different values
/// concatenations. I.e. the encoding for `(Either::Two(1, 2), Either::Two(3, 4))`
/// is the same as encoding of `(Either::Three(1, 2, 3), Either::One(4))`.
/// In practice, this type is not useful, because you can't do a proper decoding.
/// But still there may be some collisions even in proper types.
#[derive(Eq, Ord, PartialEq, PartialOrd)]
enum Either {
Three(u64, u64, u64),
Two(u64, u64),
One(u64),
}
impl codec::Encode for Either {
fn encode(&self) -> Vec<u8> {
match *self {
Self::One(a) => a.encode(),
Self::Two(a, b) => (a, b).encode(),
Self::Three(a, b, c) => (a, b, c).encode(),
}
}
}
assert_ne!(
HashedLaneId::try_new(Either::Two(1, 2), Either::Two(3, 4)).unwrap(),
HashedLaneId::try_new(Either::Three(1, 2, 3), Either::One(4)).unwrap(),
);
}
}
+624
View File
@@ -0,0 +1,624 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! Primitives of messages module.
#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
use bp_header_pez_chain::HeaderChainError;
use pezbp_runtime::{
messages::MessageDispatchResult, BasicOperatingMode, Chain, OperatingMode, RangeInclusiveExt,
StorageProofError, UnderlyingChainOf, UnderlyingChainProvider,
};
use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
use pezframe_support::PalletError;
// Weight is reexported to avoid additional pezframe-support dependencies in related crates.
pub use pezframe_support::weights::Weight;
use scale_info::TypeInfo;
use serde::{Deserialize, Serialize};
use source_chain::RelayersRewards;
use pezsp_core::RuntimeDebug;
use pezsp_std::{collections::vec_deque::VecDeque, ops::RangeInclusive, prelude::*};
pub use call_info::{
BaseMessagesProofInfo, BridgeMessagesCall, MessagesCallInfo, ReceiveMessagesDeliveryProofInfo,
ReceiveMessagesProofInfo, UnrewardedRelayerOccupation,
};
pub use lane::{HashedLaneId, LaneIdType, LaneState, LegacyLaneId};
mod call_info;
mod lane;
pub mod source_chain;
pub mod storage_keys;
pub mod target_chain;
/// Hard limit on message size that can be sent over the bridge.
pub const HARD_MESSAGE_SIZE_LIMIT: u32 = 64 * 1024;
/// Bizinikiwi-based chain with messaging support.
pub trait ChainWithMessages: Chain {
/// Name of the bridge messages pezpallet (used in `construct_runtime` macro call) that is
/// deployed at some other chain to bridge with this `ChainWithMessages`.
///
/// We assume that all chains that are bridging with this `ChainWithMessages` are using
/// the same name.
const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str;
/// Maximal number of unrewarded relayers in a single confirmation transaction at this
/// `ChainWithMessages`. Unrewarded means that the relayer has delivered messages, but
/// either confirmations haven't been delivered back to the source chain, or we haven't
/// received reward confirmations yet.
///
/// This constant limits maximal number of entries in the `InboundLaneData::relayers`. Keep
/// in mind that the same relayer account may take several (non-consecutive) entries in this
/// set.
const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce;
/// Maximal number of unconfirmed messages in a single confirmation transaction at this
/// `ChainWithMessages`. Unconfirmed means that the
/// message has been delivered, but either confirmations haven't been delivered back to the
/// source chain, or we haven't received reward confirmations for these messages yet.
///
/// This constant limits difference between last message from last entry of the
/// `InboundLaneData::relayers` and first message at the first entry.
///
/// There is no point of making this parameter lesser than
/// `MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX`, because then maximal number of relayer entries
/// will be limited by maximal number of messages.
///
/// This value also represents maximal number of messages in single delivery transaction.
/// Transaction that is declaring more messages than this value, will be rejected. Even if
/// these messages are from different lanes.
const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce;
/// Return maximal dispatch weight of the message we're able to receive.
fn maximal_incoming_message_dispatch_weight() -> Weight {
// we leave 1/2 of `max_extrinsic_weight` for the delivery transaction itself
Self::max_extrinsic_weight() / 2
}
/// Return maximal size of the message we're able to receive.
fn maximal_incoming_message_size() -> u32 {
maximal_incoming_message_size(Self::max_extrinsic_size())
}
}
/// Return maximal size of the message the chain with `max_extrinsic_size` is able to receive.
pub fn maximal_incoming_message_size(max_extrinsic_size: u32) -> u32 {
// The maximal size of extrinsic at Bizinikiwi-based chain depends on the
// `pezframe_system::Config::MaximumBlockLength` and
// `pezframe_system::Config::AvailableBlockRatio` constants. This check is here to be sure that
// the lane won't stuck because message is too large to fit into delivery transaction.
//
// **IMPORTANT NOTE**: the delivery transaction contains storage proof of the message, not
// the message itself. The proof is always larger than the message. But unless chain state
// is enormously large, it should be several dozens/hundreds of bytes. The delivery
// transaction also contains signatures and signed extensions. Because of this, we reserve
// 1/3 of the the maximal extrinsic size for this data.
//
// **ANOTHER IMPORTANT NOTE**: large message means not only larger proofs and heavier
// proof verification, but also heavier message decoding and dispatch. So we have a hard
// limit of `64Kb`, which in practice limits the message size on all chains. Without this
// limit the **weight** (not the size) of the message will be higher than the
// `Self::maximal_incoming_message_dispatch_weight()`.
pezsp_std::cmp::min(max_extrinsic_size / 3 * 2, HARD_MESSAGE_SIZE_LIMIT)
}
impl<T> ChainWithMessages for T
where
T: Chain + UnderlyingChainProvider,
UnderlyingChainOf<T>: ChainWithMessages,
{
const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str =
UnderlyingChainOf::<T>::WITH_CHAIN_MESSAGES_PALLET_NAME;
const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce =
UnderlyingChainOf::<T>::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX;
const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce =
UnderlyingChainOf::<T>::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX;
}
/// Messages pezpallet operating mode.
#[derive(
Encode,
Decode,
DecodeWithMemTracking,
Clone,
Copy,
PartialEq,
Eq,
RuntimeDebug,
TypeInfo,
MaxEncodedLen,
Serialize,
Deserialize,
)]
pub enum MessagesOperatingMode {
/// Basic operating mode (Normal/Halted)
Basic(BasicOperatingMode),
/// The pezpallet is not accepting outbound messages. Inbound messages and receiving proofs
/// are still accepted.
///
/// This mode may be used e.g. when bridged chain expects upgrade. Then to avoid dispatch
/// failures, the pezpallet owner may stop accepting new messages, while continuing to deliver
/// queued messages to the bridged chain. Once upgrade is completed, the mode may be switched
/// back to `Normal`.
RejectingOutboundMessages,
}
impl Default for MessagesOperatingMode {
fn default() -> Self {
MessagesOperatingMode::Basic(BasicOperatingMode::Normal)
}
}
impl OperatingMode for MessagesOperatingMode {
fn is_halted(&self) -> bool {
match self {
Self::Basic(operating_mode) => operating_mode.is_halted(),
_ => false,
}
}
}
/// Message nonce. Valid messages will never have 0 nonce.
pub type MessageNonce = u64;
/// Opaque message payload. We only decode this payload when it is dispatched.
pub type MessagePayload = Vec<u8>;
/// Message key (unique message identifier) as it is stored in the storage.
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub struct MessageKey<LaneId: Encode> {
/// ID of the message lane.
pub lane_id: LaneId,
/// Message nonce.
pub nonce: MessageNonce,
}
/// Message as it is stored in the storage.
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub struct Message<LaneId: Encode> {
/// Message key.
pub key: MessageKey<LaneId>,
/// Message payload.
pub payload: MessagePayload,
}
/// Inbound lane data.
#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)]
pub struct InboundLaneData<RelayerId> {
/// Identifiers of relayers and messages that they have delivered to this lane (ordered by
/// message nonce).
///
/// This serves as a helper storage item, to allow the source chain to easily pay rewards
/// to the relayers who successfully delivered messages to the target chain (inbound lane).
///
/// It is guaranteed to have at most N entries, where N is configured at the module level.
/// If there are N entries in this vec, then:
/// 1) all incoming messages are rejected if they're missing corresponding
/// `proof-of(outbound-lane.state)`; 2) all incoming messages are rejected if
/// `proof-of(outbound-lane.state).last_delivered_nonce` is equal to
/// `self.last_confirmed_nonce`. Given what is said above, all nonces in this queue are in
/// range: `(self.last_confirmed_nonce; self.last_delivered_nonce()]`.
///
/// When a relayer sends a single message, both of MessageNonces are the same.
/// When relayer sends messages in a batch, the first arg is the lowest nonce, second arg the
/// highest nonce. Multiple dispatches from the same relayer are allowed.
pub relayers: VecDeque<UnrewardedRelayer<RelayerId>>,
/// Nonce of the last message that
/// a) has been delivered to the target (this) chain and
/// b) the delivery has been confirmed on the source chain
///
/// that the target chain knows of.
///
/// This value is updated indirectly when an `OutboundLane` state of the source
/// chain is received alongside with new messages delivery.
pub last_confirmed_nonce: MessageNonce,
/// Inbound lane state.
///
/// If state is `Closed`, then all attempts to deliver messages to this end will fail.
pub state: LaneState,
}
impl<RelayerId> Default for InboundLaneData<RelayerId> {
fn default() -> Self {
InboundLaneData {
state: LaneState::Closed,
relayers: VecDeque::new(),
last_confirmed_nonce: 0,
}
}
}
impl<RelayerId> InboundLaneData<RelayerId> {
/// Returns default inbound lane data with opened state.
pub fn opened() -> Self {
InboundLaneData { state: LaneState::Opened, ..Default::default() }
}
/// Returns approximate size of the struct, given a number of entries in the `relayers` set and
/// size of each entry.
///
/// Returns `None` if size overflows `usize` limits.
pub fn encoded_size_hint(relayers_entries: usize) -> Option<usize>
where
RelayerId: MaxEncodedLen,
{
relayers_entries
.checked_mul(UnrewardedRelayer::<RelayerId>::max_encoded_len())?
.checked_add(MessageNonce::max_encoded_len())
}
/// Returns the approximate size of the struct as u32, given a number of entries in the
/// `relayers` set and the size of each entry.
///
/// Returns `u32::MAX` if size overflows `u32` limits.
pub fn encoded_size_hint_u32(relayers_entries: usize) -> u32
where
RelayerId: MaxEncodedLen,
{
Self::encoded_size_hint(relayers_entries)
.and_then(|x| u32::try_from(x).ok())
.unwrap_or(u32::MAX)
}
/// Nonce of the last message that has been delivered to this (target) chain.
pub fn last_delivered_nonce(&self) -> MessageNonce {
self.relayers
.back()
.map(|entry| entry.messages.end)
.unwrap_or(self.last_confirmed_nonce)
}
/// Returns the total number of messages in the `relayers` vector,
/// saturating in case of underflow or overflow.
pub fn total_unrewarded_messages(&self) -> MessageNonce {
let relayers = &self.relayers;
match (relayers.front(), relayers.back()) {
(Some(front), Some(back)) =>
(front.messages.begin..=back.messages.end).saturating_len(),
_ => 0,
}
}
}
/// Outbound message details, returned by runtime APIs.
#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)]
pub struct OutboundMessageDetails {
/// Nonce assigned to the message.
pub nonce: MessageNonce,
/// Message dispatch weight.
///
/// Depending on messages pezpallet configuration, it may be declared by the message submitter,
/// computed automatically or just be zero if dispatch fee is paid at the target chain.
pub dispatch_weight: Weight,
/// Size of the encoded message.
pub size: u32,
}
/// Inbound message details, returned by runtime APIs.
#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)]
pub struct InboundMessageDetails {
/// Computed message dispatch weight.
///
/// Runtime API guarantees that it will match the value, returned by
/// `target_chain::MessageDispatch::dispatch_weight`. This means that if the runtime
/// has failed to decode the message, it will be zero - that's because `undecodable`
/// message cannot be dispatched.
pub dispatch_weight: Weight,
}
/// Unrewarded relayer entry stored in the inbound lane data.
///
/// This struct represents a continuous range of messages that have been delivered by the same
/// relayer and whose confirmations are still pending.
#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen)]
pub struct UnrewardedRelayer<RelayerId> {
/// Identifier of the relayer.
pub relayer: RelayerId,
/// Messages range, delivered by this relayer.
pub messages: DeliveredMessages,
}
/// Received messages with their dispatch result.
#[derive(Clone, Encode, Decode, DecodeWithMemTracking, RuntimeDebug, PartialEq, Eq, TypeInfo)]
pub struct ReceivedMessages<DispatchLevelResult, LaneId> {
/// Id of the lane which is receiving messages.
pub lane: LaneId,
/// Result of messages which we tried to dispatch
pub receive_results: Vec<(MessageNonce, ReceptionResult<DispatchLevelResult>)>,
}
impl<DispatchLevelResult, LaneId> ReceivedMessages<DispatchLevelResult, LaneId> {
/// Creates new `ReceivedMessages` structure from given results.
pub fn new(
lane: LaneId,
receive_results: Vec<(MessageNonce, ReceptionResult<DispatchLevelResult>)>,
) -> Self {
ReceivedMessages { lane: lane.into(), receive_results }
}
/// Push `result` of the `message` delivery onto `receive_results` vector.
pub fn push(&mut self, message: MessageNonce, result: ReceptionResult<DispatchLevelResult>) {
self.receive_results.push((message, result));
}
}
/// Result of single message receival.
#[derive(RuntimeDebug, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, Clone, TypeInfo)]
pub enum ReceptionResult<DispatchLevelResult> {
/// 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<DispatchLevelResult>),
/// Message has invalid nonce and lane has rejected to accept this message.
InvalidNonce,
/// There are too many unrewarded relayer entries at the lane.
TooManyUnrewardedRelayers,
/// There are too many unconfirmed messages at the lane.
TooManyUnconfirmedMessages,
}
/// Delivered messages with their dispatch result.
#[derive(
Clone,
Default,
Encode,
Decode,
DecodeWithMemTracking,
RuntimeDebug,
PartialEq,
Eq,
TypeInfo,
MaxEncodedLen,
)]
pub struct DeliveredMessages {
/// Nonce of the first message that has been delivered (inclusive).
pub begin: MessageNonce,
/// Nonce of the last message that has been delivered (inclusive).
pub end: MessageNonce,
}
impl DeliveredMessages {
/// Create new `DeliveredMessages` struct that confirms delivery of single nonce with given
/// dispatch result.
pub fn new(nonce: MessageNonce) -> Self {
DeliveredMessages { begin: nonce, end: nonce }
}
/// Return total count of delivered messages.
pub fn total_messages(&self) -> MessageNonce {
(self.begin..=self.end).saturating_len()
}
/// Note new dispatched message.
pub fn note_dispatched_message(&mut self) {
self.end += 1;
}
/// Returns true if delivered messages contain message with given nonce.
pub fn contains_message(&self, nonce: MessageNonce) -> bool {
(self.begin..=self.end).contains(&nonce)
}
}
/// Gist of `InboundLaneData::relayers` field used by runtime APIs.
#[derive(
Clone, Default, Encode, Decode, DecodeWithMemTracking, RuntimeDebug, PartialEq, Eq, TypeInfo,
)]
pub struct UnrewardedRelayersState {
/// Number of entries in the `InboundLaneData::relayers` set.
pub unrewarded_relayer_entries: MessageNonce,
/// Number of messages in the oldest entry of `InboundLaneData::relayers`. This is the
/// minimal number of reward proofs required to push out this entry from the set.
pub messages_in_oldest_entry: MessageNonce,
/// Total number of messages in the relayers vector.
pub total_messages: MessageNonce,
/// Nonce of the latest message that has been delivered to the target chain.
///
/// This corresponds to the result of the `InboundLaneData::last_delivered_nonce` call
/// at the bridged chain.
pub last_delivered_nonce: MessageNonce,
}
impl UnrewardedRelayersState {
/// Verify that the relayers state corresponds with the `InboundLaneData`.
pub fn is_valid<RelayerId>(&self, lane_data: &InboundLaneData<RelayerId>) -> bool {
self == &lane_data.into()
}
}
impl<RelayerId> From<&InboundLaneData<RelayerId>> for UnrewardedRelayersState {
fn from(lane: &InboundLaneData<RelayerId>) -> UnrewardedRelayersState {
UnrewardedRelayersState {
unrewarded_relayer_entries: lane.relayers.len() as _,
messages_in_oldest_entry: lane
.relayers
.front()
.map(|entry| entry.messages.total_messages())
.unwrap_or(0),
total_messages: lane.total_unrewarded_messages(),
last_delivered_nonce: lane.last_delivered_nonce(),
}
}
}
/// Outbound lane data.
#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen)]
pub struct OutboundLaneData {
/// Nonce of the oldest message that we haven't yet pruned. May point to not-yet-generated
/// message if all sent messages are already pruned.
pub oldest_unpruned_nonce: MessageNonce,
/// Nonce of the latest message, received by bridged chain.
pub latest_received_nonce: MessageNonce,
/// Nonce of the latest message, generated by us.
pub latest_generated_nonce: MessageNonce,
/// Lane state.
///
/// If state is `Closed`, then all attempts to send messages at this end will fail.
pub state: LaneState,
}
impl OutboundLaneData {
/// Returns default outbound lane data with opened state.
pub fn opened() -> Self {
OutboundLaneData { state: LaneState::Opened, ..Default::default() }
}
}
impl Default for OutboundLaneData {
fn default() -> Self {
OutboundLaneData {
state: LaneState::Closed,
// it is 1 because we're pruning everything in [oldest_unpruned_nonce;
// latest_received_nonce]
oldest_unpruned_nonce: 1,
latest_received_nonce: 0,
latest_generated_nonce: 0,
}
}
}
impl OutboundLaneData {
/// Return nonces of all currently queued messages (i.e. messages that we believe
/// are not delivered yet).
pub fn queued_messages(&self) -> RangeInclusive<MessageNonce> {
(self.latest_received_nonce + 1)..=self.latest_generated_nonce
}
}
/// Calculate the number of messages that the relayers have delivered.
pub fn calc_relayers_rewards<AccountId>(
pez_messages_relayers: VecDeque<UnrewardedRelayer<AccountId>>,
received_range: &RangeInclusive<MessageNonce>,
) -> RelayersRewards<AccountId>
where
AccountId: pezsp_std::cmp::Ord,
{
// remember to reward relayers that have delivered messages
// this loop is bounded by `T::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX` on the bridged chain
let mut relayers_rewards = RelayersRewards::new();
for entry in pez_messages_relayers {
let nonce_begin = pezsp_std::cmp::max(entry.messages.begin, *received_range.start());
let nonce_end = pezsp_std::cmp::min(entry.messages.end, *received_range.end());
if nonce_end >= nonce_begin {
*relayers_rewards.entry(entry.relayer).or_default() += nonce_end - nonce_begin + 1;
}
}
relayers_rewards
}
/// Error that happens during message verification.
#[derive(
Encode, Decode, DecodeWithMemTracking, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo,
)]
pub enum VerificationError {
/// The message proof is empty.
EmptyMessageProof,
/// Error returned by the bridged header chain.
HeaderChain(HeaderChainError),
/// Error returned while reading/decoding inbound lane data from the storage proof.
InboundLaneStorage(StorageProofError),
/// The declared message weight is incorrect.
InvalidMessageWeight,
/// Declared messages count doesn't match actual value.
MessagesCountMismatch,
/// Error returned while reading/decoding message data from the `VerifiedStorageProof`.
MessageStorage(StorageProofError),
/// The message is too large.
MessageTooLarge,
/// Error returned while reading/decoding outbound lane data from the `VerifiedStorageProof`.
OutboundLaneStorage(StorageProofError),
/// Storage proof related error.
StorageProof(StorageProofError),
/// Custom error
Other(#[codec(skip)] &'static str),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lane_is_closed_by_default() {
assert_eq!(InboundLaneData::<()>::default().state, LaneState::Closed);
assert_eq!(OutboundLaneData::default().state, LaneState::Closed);
}
#[test]
fn total_unrewarded_messages_does_not_overflow() {
let lane_data = InboundLaneData {
state: LaneState::Opened,
relayers: vec![
UnrewardedRelayer { relayer: 1, messages: DeliveredMessages::new(0) },
UnrewardedRelayer {
relayer: 2,
messages: DeliveredMessages::new(MessageNonce::MAX),
},
]
.into_iter()
.collect(),
last_confirmed_nonce: 0,
};
assert_eq!(lane_data.total_unrewarded_messages(), MessageNonce::MAX);
}
#[test]
fn inbound_lane_data_returns_correct_hint() {
let test_cases = vec![
// single relayer, multiple messages
(1, 128u8),
// multiple relayers, single message per relayer
(128u8, 128u8),
// several messages per relayer
(13u8, 128u8),
];
for (relayer_entries, messages_count) in test_cases {
let expected_size = InboundLaneData::<u8>::encoded_size_hint(relayer_entries as _);
let actual_size = InboundLaneData {
state: LaneState::Opened,
relayers: (1u8..=relayer_entries)
.map(|i| UnrewardedRelayer {
relayer: i,
messages: DeliveredMessages::new(i as _),
})
.collect(),
last_confirmed_nonce: messages_count as _,
}
.encode()
.len();
let difference = (expected_size.unwrap() as f64 - actual_size as f64).abs();
assert!(
difference / (std::cmp::min(actual_size, expected_size.unwrap()) as f64) < 0.1,
"Too large difference between actual ({actual_size}) and expected ({expected_size:?}) inbound lane data size. Test case: {relayer_entries}+{messages_count}",
);
}
}
#[test]
fn contains_result_works() {
let delivered_messages = DeliveredMessages { begin: 100, end: 150 };
assert!(!delivered_messages.contains_message(99));
assert!(delivered_messages.contains_message(100));
assert!(delivered_messages.contains_message(150));
assert!(!delivered_messages.contains_message(151));
}
}
@@ -0,0 +1,157 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! Primitives of messages module, that are used on the source chain.
use crate::{MessageNonce, UnrewardedRelayer};
use pezbp_runtime::{raw_storage_proof_size, RawStorageProof, Size};
use codec::{Decode, DecodeWithMemTracking, Encode};
use scale_info::TypeInfo;
use pezsp_core::RuntimeDebug;
use pezsp_std::{
collections::{btree_map::BTreeMap, vec_deque::VecDeque},
fmt::Debug,
ops::RangeInclusive,
};
/// Messages delivery proof from the bridged chain.
///
/// It contains everything required to prove that our (this chain) messages have been
/// delivered to the bridged (target) chain:
///
/// - hash of finalized header;
///
/// - storage proof of the inbound lane state;
///
/// - lane id.
#[derive(Clone, Decode, DecodeWithMemTracking, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)]
pub struct FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash, LaneId> {
/// Hash of the bridge header the proof is for.
pub bridged_header_hash: BridgedHeaderHash,
/// Storage trie proof generated for [`Self::bridged_header_hash`].
pub storage_proof: RawStorageProof,
/// Lane id of which messages were delivered and the proof is for.
pub lane: LaneId,
}
impl<BridgedHeaderHash, LaneId> Size
for FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash, LaneId>
{
fn size(&self) -> u32 {
use pezframe_support::pezsp_runtime::SaturatedConversion;
raw_storage_proof_size(&self.storage_proof).saturated_into()
}
}
/// Number of messages, delivered by relayers.
pub type RelayersRewards<AccountId> = BTreeMap<AccountId, MessageNonce>;
/// Manages payments that are happening at the source chain during delivery confirmation
/// transaction.
pub trait DeliveryConfirmationPayments<AccountId, LaneId> {
/// Error type.
type Error: Debug + Into<&'static str>;
/// Pay rewards for delivering messages to the given relayers.
///
/// The implementation may also choose to pay reward to the `confirmation_relayer`, which is
/// a relayer that has submitted delivery confirmation transaction.
///
/// Returns number of actually rewarded relayers.
fn pay_reward(
lane_id: LaneId,
pez_messages_relayers: VecDeque<UnrewardedRelayer<AccountId>>,
confirmation_relayer: &AccountId,
received_range: &RangeInclusive<MessageNonce>,
) -> MessageNonce;
}
impl<AccountId, LaneId> DeliveryConfirmationPayments<AccountId, LaneId> for () {
type Error = &'static str;
fn pay_reward(
_lane_id: LaneId,
_pez_messages_relayers: VecDeque<UnrewardedRelayer<AccountId>>,
_confirmation_relayer: &AccountId,
_received_range: &RangeInclusive<MessageNonce>,
) -> MessageNonce {
// this implementation is not rewarding relayers at all
0
}
}
/// Callback that is called at the source chain (bridge hub) when we get delivery confirmation
/// for new messages.
pub trait OnMessagesDelivered<LaneId> {
/// New messages delivery has been confirmed.
///
/// The only argument of the function is the number of yet undelivered messages
fn on_messages_delivered(lane: LaneId, enqueued_messages: MessageNonce);
}
impl<LaneId> OnMessagesDelivered<LaneId> for () {
fn on_messages_delivered(_lane: LaneId, _enqueued_messages: MessageNonce) {}
}
/// Send message artifacts.
#[derive(Eq, RuntimeDebug, PartialEq)]
pub struct SendMessageArtifacts {
/// Nonce of the message.
pub nonce: MessageNonce,
/// Number of enqueued messages at the lane, after the message is sent.
pub enqueued_messages: MessageNonce,
}
/// Messages bridge API to be used from other pallets.
pub trait MessagesBridge<Payload, LaneId> {
/// Error type.
type Error: Debug;
/// Intermediary structure returned by `validate_message()`.
///
/// It can than be passed to `send_message()` in order to actually send the message
/// on the bridge.
type SendMessageArgs;
/// Check if the message can be sent over the bridge.
fn validate_message(
lane: LaneId,
message: &Payload,
) -> Result<Self::SendMessageArgs, Self::Error>;
/// Send message over the bridge.
///
/// Returns unique message nonce or error if send has failed.
fn send_message(message: Self::SendMessageArgs) -> SendMessageArtifacts;
}
/// Structure that may be used in place `MessageDeliveryAndDispatchPayment` on chains,
/// where outbound messages are forbidden.
pub struct ForbidOutboundMessages;
impl<AccountId, LaneId> DeliveryConfirmationPayments<AccountId, LaneId> for ForbidOutboundMessages {
type Error = &'static str;
fn pay_reward(
_lane_id: LaneId,
_pez_messages_relayers: VecDeque<UnrewardedRelayer<AccountId>>,
_confirmation_relayer: &AccountId,
_received_range: &RangeInclusive<MessageNonce>,
) -> MessageNonce {
0
}
}
@@ -0,0 +1,166 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! Storage keys of bridge messages pezpallet.
/// Name of the `OPERATING_MODE_VALUE_NAME` storage value.
pub const OPERATING_MODE_VALUE_NAME: &str = "PalletOperatingMode";
/// Name of the `OutboundMessages` storage map.
pub const OUTBOUND_MESSAGES_MAP_NAME: &str = "OutboundMessages";
/// Name of the `OutboundLanes` storage map.
pub const OUTBOUND_LANES_MAP_NAME: &str = "OutboundLanes";
/// Name of the `InboundLanes` storage map.
pub const INBOUND_LANES_MAP_NAME: &str = "InboundLanes";
use crate::{MessageKey, MessageNonce};
use codec::Encode;
use pezframe_support::Blake2_128Concat;
use pezsp_core::storage::StorageKey;
/// Storage key of the `PalletOperatingMode` value in the runtime storage.
pub fn operating_mode_key(pezpallet_prefix: &str) -> StorageKey {
StorageKey(
pezbp_runtime::storage_value_final_key(
pezpallet_prefix.as_bytes(),
OPERATING_MODE_VALUE_NAME.as_bytes(),
)
.to_vec(),
)
}
/// Storage key of the outbound message in the runtime storage.
pub fn message_key<LaneId: Encode>(
pezpallet_prefix: &str,
lane: LaneId,
nonce: MessageNonce,
) -> StorageKey {
pezbp_runtime::storage_map_final_key::<Blake2_128Concat>(
pezpallet_prefix,
OUTBOUND_MESSAGES_MAP_NAME,
&MessageKey { lane_id: lane, nonce }.encode(),
)
}
/// Storage key of the outbound message lane state in the runtime storage.
pub fn outbound_lane_data_key<LaneId: Encode>(pezpallet_prefix: &str, lane: &LaneId) -> StorageKey {
pezbp_runtime::storage_map_final_key::<Blake2_128Concat>(
pezpallet_prefix,
OUTBOUND_LANES_MAP_NAME,
&lane.encode(),
)
}
/// Storage key of the inbound message lane state in the runtime storage.
pub fn inbound_lane_data_key<LaneId: Encode>(pezpallet_prefix: &str, lane: &LaneId) -> StorageKey {
pezbp_runtime::storage_map_final_key::<Blake2_128Concat>(
pezpallet_prefix,
INBOUND_LANES_MAP_NAME,
&lane.encode(),
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
lane::{HashedLaneId, LegacyLaneId},
LaneIdType,
};
use hex_literal::hex;
#[test]
fn operating_mode_key_computed_properly() {
// If this test fails, then something has been changed in module storage that is possibly
// breaking all existing message relays.
let storage_key = operating_mode_key("BridgeMessages").0;
assert_eq!(
storage_key,
hex!("dd16c784ebd3390a9bc0357c7511ed010f4cf0917788d791142ff6c1f216e7b3").to_vec(),
"Unexpected storage key: {}",
hex::encode(&storage_key),
);
}
#[test]
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 =
message_key("BridgeMessages", &HashedLaneId::try_new(1, 2).unwrap(), 42).0;
assert_eq!(
storage_key,
hex!("dd16c784ebd3390a9bc0357c7511ed018a395e6242c6813b196ca31ed0547ea70e9bdb8f50c68d12f06eabb57759ee5eb1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dcf87f9793be208e5ea02a00000000000000").to_vec(),
"Unexpected storage key: {}",
hex::encode(&storage_key),
);
// check backwards compatibility
let storage_key = message_key("BridgeMessages", &LegacyLaneId(*b"test"), 42).0;
assert_eq!(
storage_key,
hex!("dd16c784ebd3390a9bc0357c7511ed018a395e6242c6813b196ca31ed0547ea79446af0e09063bd4a7874aef8a997cec746573742a00000000000000").to_vec(),
"Unexpected storage key: {}",
hex::encode(&storage_key),
);
}
#[test]
fn outbound_lane_data_key_computed_properly() {
// If this test fails, then something has been changed in module storage that is breaking
// all previously crafted outbound lane state proofs.
let storage_key =
outbound_lane_data_key("BridgeMessages", &HashedLaneId::try_new(1, 2).unwrap()).0;
assert_eq!(
storage_key,
hex!("dd16c784ebd3390a9bc0357c7511ed0196c246acb9b55077390e3ca723a0ca1fd3bef8b00df8ca7b01813b5e2741950db1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dcf87f9793be208e5ea0").to_vec(),
"Unexpected storage key: {}",
hex::encode(&storage_key),
);
// check backwards compatibility
let storage_key = outbound_lane_data_key("BridgeMessages", &LegacyLaneId(*b"test")).0;
assert_eq!(
storage_key,
hex!("dd16c784ebd3390a9bc0357c7511ed0196c246acb9b55077390e3ca723a0ca1f44a8995dd50b6657a037a7839304535b74657374").to_vec(),
"Unexpected storage key: {}",
hex::encode(&storage_key),
);
}
#[test]
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 =
inbound_lane_data_key("BridgeMessages", &HashedLaneId::try_new(1, 2).unwrap()).0;
assert_eq!(
storage_key,
hex!("dd16c784ebd3390a9bc0357c7511ed01e5f83cf83f2127eb47afdc35d6e43fabd3bef8b00df8ca7b01813b5e2741950db1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dcf87f9793be208e5ea0").to_vec(),
"Unexpected storage key: {}",
hex::encode(&storage_key),
);
// check backwards compatibility
let storage_key = inbound_lane_data_key("BridgeMessages", &LegacyLaneId(*b"test")).0;
assert_eq!(
storage_key,
hex!("dd16c784ebd3390a9bc0357c7511ed01e5f83cf83f2127eb47afdc35d6e43fab44a8995dd50b6657a037a7839304535b74657374").to_vec(),
"Unexpected storage key: {}",
hex::encode(&storage_key),
);
}
}
@@ -0,0 +1,207 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! Primitives of messages module, that are used on the target chain.
use crate::{Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData};
use pezbp_runtime::{messages::MessageDispatchResult, raw_storage_proof_size, RawStorageProof, Size};
use codec::{Decode, DecodeWithMemTracking, Encode, Error as CodecError};
use pezframe_support::weights::Weight;
use scale_info::TypeInfo;
use pezsp_core::RuntimeDebug;
use pezsp_std::{fmt::Debug, marker::PhantomData, prelude::*};
/// Messages proof from bridged chain.
///
/// It contains everything required to prove that bridged (source) chain has
/// sent us some messages:
///
/// - hash of finalized header;
///
/// - storage proof of messages and (optionally) outbound lane state;
///
/// - lane id;
///
/// - nonces (inclusive range) of messages which are included in this proof.
#[derive(Clone, Decode, DecodeWithMemTracking, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)]
pub struct FromBridgedChainMessagesProof<BridgedHeaderHash, Lane> {
/// Hash of the finalized bridged header the proof is for.
pub bridged_header_hash: BridgedHeaderHash,
/// A storage trie proof of messages being delivered.
pub storage_proof: RawStorageProof,
/// Messages in this proof are sent over this lane.
pub lane: Lane,
/// Nonce of the first message being delivered.
pub nonces_start: MessageNonce,
/// Nonce of the last message being delivered.
pub nonces_end: MessageNonce,
}
impl<BridgedHeaderHash, Lane> Size for FromBridgedChainMessagesProof<BridgedHeaderHash, Lane> {
fn size(&self) -> u32 {
use pezframe_support::pezsp_runtime::SaturatedConversion;
raw_storage_proof_size(&self.storage_proof).saturated_into()
}
}
/// Proved messages from the source chain.
pub type ProvedMessages<LaneId, Message> = (LaneId, ProvedLaneMessages<Message>);
/// Proved messages from single lane of the source chain.
#[derive(RuntimeDebug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)]
pub struct ProvedLaneMessages<Message> {
/// Optional outbound lane state.
pub lane_state: Option<OutboundLaneData>,
/// Messages sent through this lane.
pub messages: Vec<Message>,
}
/// Message data with decoded dispatch payload.
#[derive(RuntimeDebug)]
pub struct DispatchMessageData<DispatchPayload> {
/// Result of dispatch payload decoding.
pub payload: Result<DispatchPayload, CodecError>,
}
/// Message with decoded dispatch payload.
#[derive(RuntimeDebug)]
pub struct DispatchMessage<DispatchPayload, LaneId: Encode> {
/// Message key.
pub key: MessageKey<LaneId>,
/// Message data with decoded dispatch payload.
pub data: DispatchMessageData<DispatchPayload>,
}
/// Called when inbound message is received.
pub trait MessageDispatch {
/// Decoded message payload type. Valid message may contain invalid payload. In this case
/// message is delivered, but dispatch fails. Therefore, two separate types of payload
/// (opaque `MessagePayload` used in delivery and this `DispatchPayload` used in dispatch).
type DispatchPayload: Decode;
/// Fine-grained result of single message dispatch (for better diagnostic purposes)
type DispatchLevelResult: Clone + pezsp_std::fmt::Debug + Eq;
/// Lane identifier type.
type LaneId: Encode;
/// Returns `true` if dispatcher is ready to accept additional messages. The `false` should
/// be treated as a hint by both dispatcher and its consumers - i.e. dispatcher shall not
/// simply drop messages if it returns `false`. The consumer may still call the `dispatch`
/// if dispatcher has returned `false`.
///
/// We check it in the messages delivery transaction prologue. So if it becomes `false`
/// after some portion of messages is already dispatched, it doesn't fail the whole transaction.
fn is_active(lane: Self::LaneId) -> bool;
/// Estimate dispatch weight.
///
/// This function must return correct upper bound of dispatch weight. The return value
/// of this function is expected to match return value of the corresponding
/// `From<Chain>InboundLaneApi::message_details().dispatch_weight` call.
fn dispatch_weight(
message: &mut DispatchMessage<Self::DispatchPayload, Self::LaneId>,
) -> Weight;
/// Called when inbound message is received.
///
/// It is up to the implementers of this trait to determine whether the message
/// is invalid (i.e. improperly encoded, has too large weight, ...) or not.
fn dispatch(
message: DispatchMessage<Self::DispatchPayload, Self::LaneId>,
) -> MessageDispatchResult<Self::DispatchLevelResult>;
}
/// Manages payments that are happening at the target chain during message delivery transaction.
pub trait DeliveryPayments<AccountId> {
/// Error type.
type Error: Debug + Into<&'static str>;
/// Pay rewards for delivering messages to the given relayer.
///
/// This method is called during message delivery transaction which has been submitted
/// by the `relayer`. The transaction brings `total_messages` messages but only
/// `valid_messages` have been accepted. The post-dispatch transaction weight is the
/// `actual_weight`.
fn pay_reward(
relayer: AccountId,
total_messages: MessageNonce,
valid_messages: MessageNonce,
actual_weight: Weight,
);
}
impl<Message> Default for ProvedLaneMessages<Message> {
fn default() -> Self {
ProvedLaneMessages { lane_state: None, messages: Vec::new() }
}
}
impl<DispatchPayload: Decode, LaneId: Encode> From<Message<LaneId>>
for DispatchMessage<DispatchPayload, LaneId>
{
fn from(message: Message<LaneId>) -> Self {
DispatchMessage { key: message.key, data: message.payload.into() }
}
}
impl<DispatchPayload: Decode> From<MessagePayload> for DispatchMessageData<DispatchPayload> {
fn from(payload: MessagePayload) -> Self {
DispatchMessageData { payload: DispatchPayload::decode(&mut &payload[..]) }
}
}
impl<AccountId> DeliveryPayments<AccountId> for () {
type Error = &'static str;
fn pay_reward(
_relayer: AccountId,
_total_messages: MessageNonce,
_valid_messages: MessageNonce,
_actual_weight: Weight,
) {
// this implementation is not rewarding relayer at all
}
}
/// Structure that may be used in place of `MessageDispatch` on chains,
/// where inbound messages are forbidden.
pub struct ForbidInboundMessages<DispatchPayload, LaneId>(PhantomData<(DispatchPayload, LaneId)>);
impl<DispatchPayload: Decode, LaneId: Encode> MessageDispatch
for ForbidInboundMessages<DispatchPayload, LaneId>
{
type DispatchPayload = DispatchPayload;
type DispatchLevelResult = ();
type LaneId = LaneId;
fn is_active(_: LaneId) -> bool {
false
}
fn dispatch_weight(
_message: &mut DispatchMessage<Self::DispatchPayload, Self::LaneId>,
) -> Weight {
Weight::MAX
}
fn dispatch(
_: DispatchMessage<Self::DispatchPayload, Self::LaneId>,
) -> MessageDispatchResult<Self::DispatchLevelResult> {
MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () }
}
}