pezbp_messages/lib.rs
1// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
2// This file is part of Parity Bridges Common.
3
4// Parity Bridges Common is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Parity Bridges Common is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
16
17//! Primitives of messages module.
18
19#![warn(missing_docs)]
20#![cfg_attr(not(feature = "std"), no_std)]
21
22use pezbp_header_pez_chain::HeaderChainError;
23use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
24use pezbp_runtime::{
25 messages::MessageDispatchResult, BasicOperatingMode, Chain, OperatingMode, RangeInclusiveExt,
26 StorageProofError, UnderlyingChainOf, UnderlyingChainProvider,
27};
28use pezframe_support::PalletError;
29// Weight is reexported to avoid additional pezframe-support dependencies in related crates.
30pub use pezframe_support::weights::Weight;
31use pezsp_core::RuntimeDebug;
32use pezsp_std::{collections::vec_deque::VecDeque, ops::RangeInclusive, prelude::*};
33use scale_info::TypeInfo;
34use serde::{Deserialize, Serialize};
35use source_chain::RelayersRewards;
36
37pub use call_info::{
38 BaseMessagesProofInfo, BridgeMessagesCall, MessagesCallInfo, ReceiveMessagesDeliveryProofInfo,
39 ReceiveMessagesProofInfo, UnrewardedRelayerOccupation,
40};
41pub use lane::{HashedLaneId, LaneIdType, LaneState, LegacyLaneId};
42
43mod call_info;
44mod lane;
45pub mod source_chain;
46pub mod storage_keys;
47pub mod target_chain;
48
49/// Hard limit on message size that can be sent over the bridge.
50pub const HARD_MESSAGE_SIZE_LIMIT: u32 = 64 * 1024;
51
52/// Bizinikiwi-based chain with messaging support.
53pub trait ChainWithMessages: Chain {
54 /// Name of the bridge messages pezpallet (used in `construct_runtime` macro call) that is
55 /// deployed at some other chain to bridge with this `ChainWithMessages`.
56 ///
57 /// We assume that all chains that are bridging with this `ChainWithMessages` are using
58 /// the same name.
59 const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str;
60
61 /// Maximal number of unrewarded relayers in a single confirmation transaction at this
62 /// `ChainWithMessages`. Unrewarded means that the relayer has delivered messages, but
63 /// either confirmations haven't been delivered back to the source chain, or we haven't
64 /// received reward confirmations yet.
65 ///
66 /// This constant limits maximal number of entries in the `InboundLaneData::relayers`. Keep
67 /// in mind that the same relayer account may take several (non-consecutive) entries in this
68 /// set.
69 const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce;
70 /// Maximal number of unconfirmed messages in a single confirmation transaction at this
71 /// `ChainWithMessages`. Unconfirmed means that the
72 /// message has been delivered, but either confirmations haven't been delivered back to the
73 /// source chain, or we haven't received reward confirmations for these messages yet.
74 ///
75 /// This constant limits difference between last message from last entry of the
76 /// `InboundLaneData::relayers` and first message at the first entry.
77 ///
78 /// There is no point of making this parameter lesser than
79 /// `MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX`, because then maximal number of relayer entries
80 /// will be limited by maximal number of messages.
81 ///
82 /// This value also represents maximal number of messages in single delivery transaction.
83 /// Transaction that is declaring more messages than this value, will be rejected. Even if
84 /// these messages are from different lanes.
85 const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce;
86
87 /// Return maximal dispatch weight of the message we're able to receive.
88 fn maximal_incoming_message_dispatch_weight() -> Weight {
89 // we leave 1/2 of `max_extrinsic_weight` for the delivery transaction itself
90 Self::max_extrinsic_weight() / 2
91 }
92
93 /// Return maximal size of the message we're able to receive.
94 fn maximal_incoming_message_size() -> u32 {
95 maximal_incoming_message_size(Self::max_extrinsic_size())
96 }
97}
98
99/// Return maximal size of the message the chain with `max_extrinsic_size` is able to receive.
100pub fn maximal_incoming_message_size(max_extrinsic_size: u32) -> u32 {
101 // The maximal size of extrinsic at Bizinikiwi-based chain depends on the
102 // `pezframe_system::Config::MaximumBlockLength` and
103 // `pezframe_system::Config::AvailableBlockRatio` constants. This check is here to be sure that
104 // the lane won't stuck because message is too large to fit into delivery transaction.
105 //
106 // **IMPORTANT NOTE**: the delivery transaction contains storage proof of the message, not
107 // the message itself. The proof is always larger than the message. But unless chain state
108 // is enormously large, it should be several dozens/hundreds of bytes. The delivery
109 // transaction also contains signatures and signed extensions. Because of this, we reserve
110 // 1/3 of the the maximal extrinsic size for this data.
111 //
112 // **ANOTHER IMPORTANT NOTE**: large message means not only larger proofs and heavier
113 // proof verification, but also heavier message decoding and dispatch. So we have a hard
114 // limit of `64Kb`, which in practice limits the message size on all chains. Without this
115 // limit the **weight** (not the size) of the message will be higher than the
116 // `Self::maximal_incoming_message_dispatch_weight()`.
117
118 pezsp_std::cmp::min(max_extrinsic_size / 3 * 2, HARD_MESSAGE_SIZE_LIMIT)
119}
120
121impl<T> ChainWithMessages for T
122where
123 T: Chain + UnderlyingChainProvider,
124 UnderlyingChainOf<T>: ChainWithMessages,
125{
126 const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str =
127 UnderlyingChainOf::<T>::WITH_CHAIN_MESSAGES_PALLET_NAME;
128 const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce =
129 UnderlyingChainOf::<T>::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX;
130 const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce =
131 UnderlyingChainOf::<T>::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX;
132}
133
134/// Messages pezpallet operating mode.
135#[derive(
136 Encode,
137 Decode,
138 DecodeWithMemTracking,
139 Clone,
140 Copy,
141 PartialEq,
142 Eq,
143 RuntimeDebug,
144 TypeInfo,
145 MaxEncodedLen,
146 Serialize,
147 Deserialize,
148)]
149pub enum MessagesOperatingMode {
150 /// Basic operating mode (Normal/Halted)
151 Basic(BasicOperatingMode),
152 /// The pezpallet is not accepting outbound messages. Inbound messages and receiving proofs
153 /// are still accepted.
154 ///
155 /// This mode may be used e.g. when bridged chain expects upgrade. Then to avoid dispatch
156 /// failures, the pezpallet owner may stop accepting new messages, while continuing to deliver
157 /// queued messages to the bridged chain. Once upgrade is completed, the mode may be switched
158 /// back to `Normal`.
159 RejectingOutboundMessages,
160}
161
162impl Default for MessagesOperatingMode {
163 fn default() -> Self {
164 MessagesOperatingMode::Basic(BasicOperatingMode::Normal)
165 }
166}
167
168impl OperatingMode for MessagesOperatingMode {
169 fn is_halted(&self) -> bool {
170 match self {
171 Self::Basic(operating_mode) => operating_mode.is_halted(),
172 _ => false,
173 }
174 }
175}
176
177/// Message nonce. Valid messages will never have 0 nonce.
178pub type MessageNonce = u64;
179
180/// Opaque message payload. We only decode this payload when it is dispatched.
181pub type MessagePayload = Vec<u8>;
182
183/// Message key (unique message identifier) as it is stored in the storage.
184#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
185pub struct MessageKey<LaneId: Encode> {
186 /// ID of the message lane.
187 pub lane_id: LaneId,
188 /// Message nonce.
189 pub nonce: MessageNonce,
190}
191
192/// Message as it is stored in the storage.
193#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
194pub struct Message<LaneId: Encode> {
195 /// Message key.
196 pub key: MessageKey<LaneId>,
197 /// Message payload.
198 pub payload: MessagePayload,
199}
200
201/// Inbound lane data.
202#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)]
203pub struct InboundLaneData<RelayerId> {
204 /// Identifiers of relayers and messages that they have delivered to this lane (ordered by
205 /// message nonce).
206 ///
207 /// This serves as a helper storage item, to allow the source chain to easily pay rewards
208 /// to the relayers who successfully delivered messages to the target chain (inbound lane).
209 ///
210 /// It is guaranteed to have at most N entries, where N is configured at the module level.
211 /// If there are N entries in this vec, then:
212 /// 1) all incoming messages are rejected if they're missing corresponding
213 /// `proof-of(outbound-lane.state)`; 2) all incoming messages are rejected if
214 /// `proof-of(outbound-lane.state).last_delivered_nonce` is equal to
215 /// `self.last_confirmed_nonce`. Given what is said above, all nonces in this queue are in
216 /// range: `(self.last_confirmed_nonce; self.last_delivered_nonce()]`.
217 ///
218 /// When a relayer sends a single message, both of MessageNonces are the same.
219 /// When relayer sends messages in a batch, the first arg is the lowest nonce, second arg the
220 /// highest nonce. Multiple dispatches from the same relayer are allowed.
221 pub relayers: VecDeque<UnrewardedRelayer<RelayerId>>,
222
223 /// Nonce of the last message that
224 /// a) has been delivered to the target (this) chain and
225 /// b) the delivery has been confirmed on the source chain
226 ///
227 /// that the target chain knows of.
228 ///
229 /// This value is updated indirectly when an `OutboundLane` state of the source
230 /// chain is received alongside with new messages delivery.
231 pub last_confirmed_nonce: MessageNonce,
232
233 /// Inbound lane state.
234 ///
235 /// If state is `Closed`, then all attempts to deliver messages to this end will fail.
236 pub state: LaneState,
237}
238
239impl<RelayerId> Default for InboundLaneData<RelayerId> {
240 fn default() -> Self {
241 InboundLaneData {
242 state: LaneState::Closed,
243 relayers: VecDeque::new(),
244 last_confirmed_nonce: 0,
245 }
246 }
247}
248
249impl<RelayerId> InboundLaneData<RelayerId> {
250 /// Returns default inbound lane data with opened state.
251 pub fn opened() -> Self {
252 InboundLaneData { state: LaneState::Opened, ..Default::default() }
253 }
254
255 /// Returns approximate size of the struct, given a number of entries in the `relayers` set and
256 /// size of each entry.
257 ///
258 /// Returns `None` if size overflows `usize` limits.
259 pub fn encoded_size_hint(relayers_entries: usize) -> Option<usize>
260 where
261 RelayerId: MaxEncodedLen,
262 {
263 relayers_entries
264 .checked_mul(UnrewardedRelayer::<RelayerId>::max_encoded_len())?
265 .checked_add(MessageNonce::max_encoded_len())
266 }
267
268 /// Returns the approximate size of the struct as u32, given a number of entries in the
269 /// `relayers` set and the size of each entry.
270 ///
271 /// Returns `u32::MAX` if size overflows `u32` limits.
272 pub fn encoded_size_hint_u32(relayers_entries: usize) -> u32
273 where
274 RelayerId: MaxEncodedLen,
275 {
276 Self::encoded_size_hint(relayers_entries)
277 .and_then(|x| u32::try_from(x).ok())
278 .unwrap_or(u32::MAX)
279 }
280
281 /// Nonce of the last message that has been delivered to this (target) chain.
282 pub fn last_delivered_nonce(&self) -> MessageNonce {
283 self.relayers
284 .back()
285 .map(|entry| entry.messages.end)
286 .unwrap_or(self.last_confirmed_nonce)
287 }
288
289 /// Returns the total number of messages in the `relayers` vector,
290 /// saturating in case of underflow or overflow.
291 pub fn total_unrewarded_messages(&self) -> MessageNonce {
292 let relayers = &self.relayers;
293 match (relayers.front(), relayers.back()) {
294 (Some(front), Some(back)) => {
295 (front.messages.begin..=back.messages.end).saturating_len()
296 },
297 _ => 0,
298 }
299 }
300}
301
302/// Outbound message details, returned by runtime APIs.
303#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)]
304pub struct OutboundMessageDetails {
305 /// Nonce assigned to the message.
306 pub nonce: MessageNonce,
307 /// Message dispatch weight.
308 ///
309 /// Depending on messages pezpallet configuration, it may be declared by the message submitter,
310 /// computed automatically or just be zero if dispatch fee is paid at the target chain.
311 pub dispatch_weight: Weight,
312 /// Size of the encoded message.
313 pub size: u32,
314}
315
316/// Inbound message details, returned by runtime APIs.
317#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)]
318pub struct InboundMessageDetails {
319 /// Computed message dispatch weight.
320 ///
321 /// Runtime API guarantees that it will match the value, returned by
322 /// `target_chain::MessageDispatch::dispatch_weight`. This means that if the runtime
323 /// has failed to decode the message, it will be zero - that's because `undecodable`
324 /// message cannot be dispatched.
325 pub dispatch_weight: Weight,
326}
327
328/// Unrewarded relayer entry stored in the inbound lane data.
329///
330/// This struct represents a continuous range of messages that have been delivered by the same
331/// relayer and whose confirmations are still pending.
332#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen)]
333pub struct UnrewardedRelayer<RelayerId> {
334 /// Identifier of the relayer.
335 pub relayer: RelayerId,
336 /// Messages range, delivered by this relayer.
337 pub messages: DeliveredMessages,
338}
339
340/// Received messages with their dispatch result.
341#[derive(Clone, Encode, Decode, DecodeWithMemTracking, RuntimeDebug, PartialEq, Eq, TypeInfo)]
342pub struct ReceivedMessages<DispatchLevelResult, LaneId> {
343 /// Id of the lane which is receiving messages.
344 pub lane: LaneId,
345 /// Result of messages which we tried to dispatch
346 pub receive_results: Vec<(MessageNonce, ReceptionResult<DispatchLevelResult>)>,
347}
348
349impl<DispatchLevelResult, LaneId> ReceivedMessages<DispatchLevelResult, LaneId> {
350 /// Creates new `ReceivedMessages` structure from given results.
351 pub fn new(
352 lane: LaneId,
353 receive_results: Vec<(MessageNonce, ReceptionResult<DispatchLevelResult>)>,
354 ) -> Self {
355 ReceivedMessages { lane: lane.into(), receive_results }
356 }
357
358 /// Push `result` of the `message` delivery onto `receive_results` vector.
359 pub fn push(&mut self, message: MessageNonce, result: ReceptionResult<DispatchLevelResult>) {
360 self.receive_results.push((message, result));
361 }
362}
363
364/// Result of single message receival.
365#[derive(RuntimeDebug, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, Clone, TypeInfo)]
366pub enum ReceptionResult<DispatchLevelResult> {
367 /// Message has been received and dispatched. Note that we don't care whether dispatch has
368 /// been successful or not - in both case message falls into this category.
369 ///
370 /// The message dispatch result is also returned.
371 Dispatched(MessageDispatchResult<DispatchLevelResult>),
372 /// Message has invalid nonce and lane has rejected to accept this message.
373 InvalidNonce,
374 /// There are too many unrewarded relayer entries at the lane.
375 TooManyUnrewardedRelayers,
376 /// There are too many unconfirmed messages at the lane.
377 TooManyUnconfirmedMessages,
378}
379
380/// Delivered messages with their dispatch result.
381#[derive(
382 Clone,
383 Default,
384 Encode,
385 Decode,
386 DecodeWithMemTracking,
387 RuntimeDebug,
388 PartialEq,
389 Eq,
390 TypeInfo,
391 MaxEncodedLen,
392)]
393pub struct DeliveredMessages {
394 /// Nonce of the first message that has been delivered (inclusive).
395 pub begin: MessageNonce,
396 /// Nonce of the last message that has been delivered (inclusive).
397 pub end: MessageNonce,
398}
399
400impl DeliveredMessages {
401 /// Create new `DeliveredMessages` struct that confirms delivery of single nonce with given
402 /// dispatch result.
403 pub fn new(nonce: MessageNonce) -> Self {
404 DeliveredMessages { begin: nonce, end: nonce }
405 }
406
407 /// Return total count of delivered messages.
408 pub fn total_messages(&self) -> MessageNonce {
409 (self.begin..=self.end).saturating_len()
410 }
411
412 /// Note new dispatched message.
413 pub fn note_dispatched_message(&mut self) {
414 self.end += 1;
415 }
416
417 /// Returns true if delivered messages contain message with given nonce.
418 pub fn contains_message(&self, nonce: MessageNonce) -> bool {
419 (self.begin..=self.end).contains(&nonce)
420 }
421}
422
423/// Gist of `InboundLaneData::relayers` field used by runtime APIs.
424#[derive(
425 Clone, Default, Encode, Decode, DecodeWithMemTracking, RuntimeDebug, PartialEq, Eq, TypeInfo,
426)]
427pub struct UnrewardedRelayersState {
428 /// Number of entries in the `InboundLaneData::relayers` set.
429 pub unrewarded_relayer_entries: MessageNonce,
430 /// Number of messages in the oldest entry of `InboundLaneData::relayers`. This is the
431 /// minimal number of reward proofs required to push out this entry from the set.
432 pub messages_in_oldest_entry: MessageNonce,
433 /// Total number of messages in the relayers vector.
434 pub total_messages: MessageNonce,
435 /// Nonce of the latest message that has been delivered to the target chain.
436 ///
437 /// This corresponds to the result of the `InboundLaneData::last_delivered_nonce` call
438 /// at the bridged chain.
439 pub last_delivered_nonce: MessageNonce,
440}
441
442impl UnrewardedRelayersState {
443 /// Verify that the relayers state corresponds with the `InboundLaneData`.
444 pub fn is_valid<RelayerId>(&self, lane_data: &InboundLaneData<RelayerId>) -> bool {
445 self == &lane_data.into()
446 }
447}
448
449impl<RelayerId> From<&InboundLaneData<RelayerId>> for UnrewardedRelayersState {
450 fn from(lane: &InboundLaneData<RelayerId>) -> UnrewardedRelayersState {
451 UnrewardedRelayersState {
452 unrewarded_relayer_entries: lane.relayers.len() as _,
453 messages_in_oldest_entry: lane
454 .relayers
455 .front()
456 .map(|entry| entry.messages.total_messages())
457 .unwrap_or(0),
458 total_messages: lane.total_unrewarded_messages(),
459 last_delivered_nonce: lane.last_delivered_nonce(),
460 }
461 }
462}
463
464/// Outbound lane data.
465#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen)]
466pub struct OutboundLaneData {
467 /// Nonce of the oldest message that we haven't yet pruned. May point to not-yet-generated
468 /// message if all sent messages are already pruned.
469 pub oldest_unpruned_nonce: MessageNonce,
470 /// Nonce of the latest message, received by bridged chain.
471 pub latest_received_nonce: MessageNonce,
472 /// Nonce of the latest message, generated by us.
473 pub latest_generated_nonce: MessageNonce,
474 /// Lane state.
475 ///
476 /// If state is `Closed`, then all attempts to send messages at this end will fail.
477 pub state: LaneState,
478}
479
480impl OutboundLaneData {
481 /// Returns default outbound lane data with opened state.
482 pub fn opened() -> Self {
483 OutboundLaneData { state: LaneState::Opened, ..Default::default() }
484 }
485}
486
487impl Default for OutboundLaneData {
488 fn default() -> Self {
489 OutboundLaneData {
490 state: LaneState::Closed,
491 // it is 1 because we're pruning everything in [oldest_unpruned_nonce;
492 // latest_received_nonce]
493 oldest_unpruned_nonce: 1,
494 latest_received_nonce: 0,
495 latest_generated_nonce: 0,
496 }
497 }
498}
499
500impl OutboundLaneData {
501 /// Return nonces of all currently queued messages (i.e. messages that we believe
502 /// are not delivered yet).
503 pub fn queued_messages(&self) -> RangeInclusive<MessageNonce> {
504 (self.latest_received_nonce + 1)..=self.latest_generated_nonce
505 }
506}
507
508/// Calculate the number of messages that the relayers have delivered.
509pub fn calc_relayers_rewards<AccountId>(
510 pez_messages_relayers: VecDeque<UnrewardedRelayer<AccountId>>,
511 received_range: &RangeInclusive<MessageNonce>,
512) -> RelayersRewards<AccountId>
513where
514 AccountId: pezsp_std::cmp::Ord,
515{
516 // remember to reward relayers that have delivered messages
517 // this loop is bounded by `T::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX` on the bridged chain
518 let mut relayers_rewards = RelayersRewards::new();
519 for entry in pez_messages_relayers {
520 let nonce_begin = pezsp_std::cmp::max(entry.messages.begin, *received_range.start());
521 let nonce_end = pezsp_std::cmp::min(entry.messages.end, *received_range.end());
522 if nonce_end >= nonce_begin {
523 *relayers_rewards.entry(entry.relayer).or_default() += nonce_end - nonce_begin + 1;
524 }
525 }
526 relayers_rewards
527}
528
529/// Error that happens during message verification.
530#[derive(
531 Encode, Decode, DecodeWithMemTracking, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo,
532)]
533pub enum VerificationError {
534 /// The message proof is empty.
535 EmptyMessageProof,
536 /// Error returned by the bridged header chain.
537 HeaderChain(HeaderChainError),
538 /// Error returned while reading/decoding inbound lane data from the storage proof.
539 InboundLaneStorage(StorageProofError),
540 /// The declared message weight is incorrect.
541 InvalidMessageWeight,
542 /// Declared messages count doesn't match actual value.
543 MessagesCountMismatch,
544 /// Error returned while reading/decoding message data from the `VerifiedStorageProof`.
545 MessageStorage(StorageProofError),
546 /// The message is too large.
547 MessageTooLarge,
548 /// Error returned while reading/decoding outbound lane data from the `VerifiedStorageProof`.
549 OutboundLaneStorage(StorageProofError),
550 /// Storage proof related error.
551 StorageProof(StorageProofError),
552 /// Custom error
553 Other(#[codec(skip)] &'static str),
554}
555
556#[cfg(test)]
557mod tests {
558 use super::*;
559
560 #[test]
561 fn lane_is_closed_by_default() {
562 assert_eq!(InboundLaneData::<()>::default().state, LaneState::Closed);
563 assert_eq!(OutboundLaneData::default().state, LaneState::Closed);
564 }
565
566 #[test]
567 fn total_unrewarded_messages_does_not_overflow() {
568 let lane_data = InboundLaneData {
569 state: LaneState::Opened,
570 relayers: vec![
571 UnrewardedRelayer { relayer: 1, messages: DeliveredMessages::new(0) },
572 UnrewardedRelayer {
573 relayer: 2,
574 messages: DeliveredMessages::new(MessageNonce::MAX),
575 },
576 ]
577 .into_iter()
578 .collect(),
579 last_confirmed_nonce: 0,
580 };
581 assert_eq!(lane_data.total_unrewarded_messages(), MessageNonce::MAX);
582 }
583
584 #[test]
585 fn inbound_lane_data_returns_correct_hint() {
586 let test_cases = vec![
587 // single relayer, multiple messages
588 (1, 128u8),
589 // multiple relayers, single message per relayer
590 (128u8, 128u8),
591 // several messages per relayer
592 (13u8, 128u8),
593 ];
594 for (relayer_entries, messages_count) in test_cases {
595 let expected_size = InboundLaneData::<u8>::encoded_size_hint(relayer_entries as _);
596 let actual_size = InboundLaneData {
597 state: LaneState::Opened,
598 relayers: (1u8..=relayer_entries)
599 .map(|i| UnrewardedRelayer {
600 relayer: i,
601 messages: DeliveredMessages::new(i as _),
602 })
603 .collect(),
604 last_confirmed_nonce: messages_count as _,
605 }
606 .encode()
607 .len();
608 let difference = (expected_size.unwrap() as f64 - actual_size as f64).abs();
609 assert!(
610 difference / (std::cmp::min(actual_size, expected_size.unwrap()) as f64) < 0.1,
611 "Too large difference between actual ({actual_size}) and expected ({expected_size:?}) inbound lane data size. Test case: {relayer_entries}+{messages_count}",
612 );
613 }
614 }
615
616 #[test]
617 fn contains_result_works() {
618 let delivered_messages = DeliveredMessages { begin: 100, end: 150 };
619
620 assert!(!delivered_messages.contains_message(99));
621 assert!(delivered_messages.contains_message(100));
622 assert!(delivered_messages.contains_message(150));
623 assert!(!delivered_messages.contains_message(151));
624 }
625}