#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
use pezbp_header_pez_chain::HeaderChainError;
use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
use pezbp_runtime::{
messages::MessageDispatchResult, BasicOperatingMode, Chain, OperatingMode, RangeInclusiveExt,
StorageProofError, UnderlyingChainOf, UnderlyingChainProvider,
};
use pezframe_support::PalletError;
pub use pezframe_support::weights::Weight;
use pezsp_core::RuntimeDebug;
use pezsp_std::{collections::vec_deque::VecDeque, ops::RangeInclusive, prelude::*};
use scale_info::TypeInfo;
use serde::{Deserialize, Serialize};
use source_chain::RelayersRewards;
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;
pub const HARD_MESSAGE_SIZE_LIMIT: u32 = 64 * 1024;
pub trait ChainWithMessages: Chain {
const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str;
const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce;
const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce;
fn maximal_incoming_message_dispatch_weight() -> Weight {
Self::max_extrinsic_weight() / 2
}
fn maximal_incoming_message_size() -> u32 {
maximal_incoming_message_size(Self::max_extrinsic_size())
}
}
pub fn maximal_incoming_message_size(max_extrinsic_size: u32) -> u32 {
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;
}
#[derive(
Encode,
Decode,
DecodeWithMemTracking,
Clone,
Copy,
PartialEq,
Eq,
RuntimeDebug,
TypeInfo,
MaxEncodedLen,
Serialize,
Deserialize,
)]
pub enum MessagesOperatingMode {
Basic(BasicOperatingMode),
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,
}
}
}
pub type MessageNonce = u64;
pub type MessagePayload = Vec<u8>;
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub struct MessageKey<LaneId: Encode> {
pub lane_id: LaneId,
pub nonce: MessageNonce,
}
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub struct Message<LaneId: Encode> {
pub key: MessageKey<LaneId>,
pub payload: MessagePayload,
}
#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)]
pub struct InboundLaneData<RelayerId> {
pub relayers: VecDeque<UnrewardedRelayer<RelayerId>>,
pub last_confirmed_nonce: MessageNonce,
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> {
pub fn opened() -> Self {
InboundLaneData { state: LaneState::Opened, ..Default::default() }
}
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())
}
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)
}
pub fn last_delivered_nonce(&self) -> MessageNonce {
self.relayers
.back()
.map(|entry| entry.messages.end)
.unwrap_or(self.last_confirmed_nonce)
}
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,
}
}
}
#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)]
pub struct OutboundMessageDetails {
pub nonce: MessageNonce,
pub dispatch_weight: Weight,
pub size: u32,
}
#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)]
pub struct InboundMessageDetails {
pub dispatch_weight: Weight,
}
#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen)]
pub struct UnrewardedRelayer<RelayerId> {
pub relayer: RelayerId,
pub messages: DeliveredMessages,
}
#[derive(Clone, Encode, Decode, DecodeWithMemTracking, RuntimeDebug, PartialEq, Eq, TypeInfo)]
pub struct ReceivedMessages<DispatchLevelResult, LaneId> {
pub lane: LaneId,
pub receive_results: Vec<(MessageNonce, ReceptionResult<DispatchLevelResult>)>,
}
impl<DispatchLevelResult, LaneId> ReceivedMessages<DispatchLevelResult, LaneId> {
pub fn new(
lane: LaneId,
receive_results: Vec<(MessageNonce, ReceptionResult<DispatchLevelResult>)>,
) -> Self {
ReceivedMessages { lane: lane.into(), receive_results }
}
pub fn push(&mut self, message: MessageNonce, result: ReceptionResult<DispatchLevelResult>) {
self.receive_results.push((message, result));
}
}
#[derive(RuntimeDebug, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, Clone, TypeInfo)]
pub enum ReceptionResult<DispatchLevelResult> {
Dispatched(MessageDispatchResult<DispatchLevelResult>),
InvalidNonce,
TooManyUnrewardedRelayers,
TooManyUnconfirmedMessages,
}
#[derive(
Clone,
Default,
Encode,
Decode,
DecodeWithMemTracking,
RuntimeDebug,
PartialEq,
Eq,
TypeInfo,
MaxEncodedLen,
)]
pub struct DeliveredMessages {
pub begin: MessageNonce,
pub end: MessageNonce,
}
impl DeliveredMessages {
pub fn new(nonce: MessageNonce) -> Self {
DeliveredMessages { begin: nonce, end: nonce }
}
pub fn total_messages(&self) -> MessageNonce {
(self.begin..=self.end).saturating_len()
}
pub fn note_dispatched_message(&mut self) {
self.end += 1;
}
pub fn contains_message(&self, nonce: MessageNonce) -> bool {
(self.begin..=self.end).contains(&nonce)
}
}
#[derive(
Clone, Default, Encode, Decode, DecodeWithMemTracking, RuntimeDebug, PartialEq, Eq, TypeInfo,
)]
pub struct UnrewardedRelayersState {
pub unrewarded_relayer_entries: MessageNonce,
pub messages_in_oldest_entry: MessageNonce,
pub total_messages: MessageNonce,
pub last_delivered_nonce: MessageNonce,
}
impl UnrewardedRelayersState {
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(),
}
}
}
#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen)]
pub struct OutboundLaneData {
pub oldest_unpruned_nonce: MessageNonce,
pub latest_received_nonce: MessageNonce,
pub latest_generated_nonce: MessageNonce,
pub state: LaneState,
}
impl OutboundLaneData {
pub fn opened() -> Self {
OutboundLaneData { state: LaneState::Opened, ..Default::default() }
}
}
impl Default for OutboundLaneData {
fn default() -> Self {
OutboundLaneData {
state: LaneState::Closed,
oldest_unpruned_nonce: 1,
latest_received_nonce: 0,
latest_generated_nonce: 0,
}
}
}
impl OutboundLaneData {
pub fn queued_messages(&self) -> RangeInclusive<MessageNonce> {
(self.latest_received_nonce + 1)..=self.latest_generated_nonce
}
}
pub fn calc_relayers_rewards<AccountId>(
pez_messages_relayers: VecDeque<UnrewardedRelayer<AccountId>>,
received_range: &RangeInclusive<MessageNonce>,
) -> RelayersRewards<AccountId>
where
AccountId: pezsp_std::cmp::Ord,
{
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
}
#[derive(
Encode, Decode, DecodeWithMemTracking, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo,
)]
pub enum VerificationError {
EmptyMessageProof,
HeaderChain(HeaderChainError),
InboundLaneStorage(StorageProofError),
InvalidMessageWeight,
MessagesCountMismatch,
MessageStorage(StorageProofError),
MessageTooLarge,
OutboundLaneStorage(StorageProofError),
StorageProof(StorageProofError),
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![
(1, 128u8),
(128u8, 128u8),
(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));
}
}