Skip to main content

bp_messages/
lib.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
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 bp_header_chain::HeaderChainError;
23use bp_runtime::{
24	messages::MessageDispatchResult, BasicOperatingMode, Chain, OperatingMode, RangeInclusiveExt,
25	StorageProofError, UnderlyingChainOf, UnderlyingChainProvider,
26};
27use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
28use frame_support::PalletError;
29// Weight is reexported to avoid additional frame-support dependencies in related crates.
30pub use frame_support::weights::Weight;
31use scale_info::TypeInfo;
32use serde::{Deserialize, Serialize};
33use source_chain::RelayersRewards;
34use sp_std::{collections::vec_deque::VecDeque, ops::RangeInclusive, prelude::*};
35use Debug;
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/// Substrate-based chain with messaging support.
53pub trait ChainWithMessages: Chain {
54	/// Name of the bridge messages pallet (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 Substrate-based chain depends on the
102	// `frame_system::Config::MaximumBlockLength` and
103	// `frame_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	sp_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 pallet operating mode.
135#[derive(
136	Encode,
137	Decode,
138	DecodeWithMemTracking,
139	Clone,
140	Copy,
141	PartialEq,
142	Eq,
143	Debug,
144	TypeInfo,
145	MaxEncodedLen,
146	Serialize,
147	Deserialize,
148)]
149pub enum MessagesOperatingMode {
150	/// Basic operating mode (Normal/Halted)
151	Basic(BasicOperatingMode),
152	/// The pallet 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 pallet 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, Debug, 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, Debug, 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, Debug, 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, Debug, 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 pallet 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, Debug, 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, Debug, 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, Debug, 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(Debug, 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	Debug,
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(Clone, Default, Encode, Decode, DecodeWithMemTracking, Debug, PartialEq, Eq, TypeInfo)]
425pub struct UnrewardedRelayersState {
426	/// Number of entries in the `InboundLaneData::relayers` set.
427	pub unrewarded_relayer_entries: MessageNonce,
428	/// Number of messages in the oldest entry of `InboundLaneData::relayers`. This is the
429	/// minimal number of reward proofs required to push out this entry from the set.
430	pub messages_in_oldest_entry: MessageNonce,
431	/// Total number of messages in the relayers vector.
432	pub total_messages: MessageNonce,
433	/// Nonce of the latest message that has been delivered to the target chain.
434	///
435	/// This corresponds to the result of the `InboundLaneData::last_delivered_nonce` call
436	/// at the bridged chain.
437	pub last_delivered_nonce: MessageNonce,
438}
439
440impl UnrewardedRelayersState {
441	/// Verify that the relayers state corresponds with the `InboundLaneData`.
442	pub fn is_valid<RelayerId>(&self, lane_data: &InboundLaneData<RelayerId>) -> bool {
443		self == &lane_data.into()
444	}
445}
446
447impl<RelayerId> From<&InboundLaneData<RelayerId>> for UnrewardedRelayersState {
448	fn from(lane: &InboundLaneData<RelayerId>) -> UnrewardedRelayersState {
449		UnrewardedRelayersState {
450			unrewarded_relayer_entries: lane.relayers.len() as _,
451			messages_in_oldest_entry: lane
452				.relayers
453				.front()
454				.map(|entry| entry.messages.total_messages())
455				.unwrap_or(0),
456			total_messages: lane.total_unrewarded_messages(),
457			last_delivered_nonce: lane.last_delivered_nonce(),
458		}
459	}
460}
461
462/// Outbound lane data.
463#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen)]
464pub struct OutboundLaneData {
465	/// Nonce of the oldest message that we haven't yet pruned. May point to not-yet-generated
466	/// message if all sent messages are already pruned.
467	pub oldest_unpruned_nonce: MessageNonce,
468	/// Nonce of the latest message, received by bridged chain.
469	pub latest_received_nonce: MessageNonce,
470	/// Nonce of the latest message, generated by us.
471	pub latest_generated_nonce: MessageNonce,
472	/// Lane state.
473	///
474	/// If state is `Closed`, then all attempts to send messages at this end will fail.
475	pub state: LaneState,
476}
477
478impl OutboundLaneData {
479	/// Returns default outbound lane data with opened state.
480	pub fn opened() -> Self {
481		OutboundLaneData { state: LaneState::Opened, ..Default::default() }
482	}
483}
484
485impl Default for OutboundLaneData {
486	fn default() -> Self {
487		OutboundLaneData {
488			state: LaneState::Closed,
489			// it is 1 because we're pruning everything in [oldest_unpruned_nonce;
490			// latest_received_nonce]
491			oldest_unpruned_nonce: 1,
492			latest_received_nonce: 0,
493			latest_generated_nonce: 0,
494		}
495	}
496}
497
498impl OutboundLaneData {
499	/// Return nonces of all currently queued messages (i.e. messages that we believe
500	/// are not delivered yet).
501	pub fn queued_messages(&self) -> RangeInclusive<MessageNonce> {
502		(self.latest_received_nonce + 1)..=self.latest_generated_nonce
503	}
504}
505
506/// Calculate the number of messages that the relayers have delivered.
507pub fn calc_relayers_rewards<AccountId>(
508	messages_relayers: VecDeque<UnrewardedRelayer<AccountId>>,
509	received_range: &RangeInclusive<MessageNonce>,
510) -> RelayersRewards<AccountId>
511where
512	AccountId: sp_std::cmp::Ord,
513{
514	// remember to reward relayers that have delivered messages
515	// this loop is bounded by `T::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX` on the bridged chain
516	let mut relayers_rewards = RelayersRewards::new();
517	for entry in messages_relayers {
518		let nonce_begin = sp_std::cmp::max(entry.messages.begin, *received_range.start());
519		let nonce_end = sp_std::cmp::min(entry.messages.end, *received_range.end());
520		if nonce_end >= nonce_begin {
521			*relayers_rewards.entry(entry.relayer).or_default() += nonce_end - nonce_begin + 1;
522		}
523	}
524	relayers_rewards
525}
526
527/// Error that happens during message verification.
528#[derive(Encode, Decode, DecodeWithMemTracking, Debug, PartialEq, Eq, PalletError, TypeInfo)]
529pub enum VerificationError {
530	/// The message proof is empty.
531	EmptyMessageProof,
532	/// Error returned by the bridged header chain.
533	HeaderChain(HeaderChainError),
534	/// Error returned while reading/decoding inbound lane data from the storage proof.
535	InboundLaneStorage(StorageProofError),
536	/// The declared message weight is incorrect.
537	InvalidMessageWeight,
538	/// Declared messages count doesn't match actual value.
539	MessagesCountMismatch,
540	/// Error returned while reading/decoding message data from the `VerifiedStorageProof`.
541	MessageStorage(StorageProofError),
542	/// The message is too large.
543	MessageTooLarge,
544	/// Error returned while reading/decoding outbound lane data from the `VerifiedStorageProof`.
545	OutboundLaneStorage(StorageProofError),
546	/// Storage proof related error.
547	StorageProof(StorageProofError),
548	/// Custom error
549	Other(#[codec(skip)] &'static str),
550}
551
552#[cfg(test)]
553mod tests {
554	use super::*;
555
556	#[test]
557	fn lane_is_closed_by_default() {
558		assert_eq!(InboundLaneData::<()>::default().state, LaneState::Closed);
559		assert_eq!(OutboundLaneData::default().state, LaneState::Closed);
560	}
561
562	#[test]
563	fn total_unrewarded_messages_does_not_overflow() {
564		let lane_data = InboundLaneData {
565			state: LaneState::Opened,
566			relayers: vec![
567				UnrewardedRelayer { relayer: 1, messages: DeliveredMessages::new(0) },
568				UnrewardedRelayer {
569					relayer: 2,
570					messages: DeliveredMessages::new(MessageNonce::MAX),
571				},
572			]
573			.into_iter()
574			.collect(),
575			last_confirmed_nonce: 0,
576		};
577		assert_eq!(lane_data.total_unrewarded_messages(), MessageNonce::MAX);
578	}
579
580	#[test]
581	fn inbound_lane_data_returns_correct_hint() {
582		let test_cases = vec![
583			// single relayer, multiple messages
584			(1, 128u8),
585			// multiple relayers, single message per relayer
586			(128u8, 128u8),
587			// several messages per relayer
588			(13u8, 128u8),
589		];
590		for (relayer_entries, messages_count) in test_cases {
591			let expected_size = InboundLaneData::<u8>::encoded_size_hint(relayer_entries as _);
592			let actual_size = InboundLaneData {
593				state: LaneState::Opened,
594				relayers: (1u8..=relayer_entries)
595					.map(|i| UnrewardedRelayer {
596						relayer: i,
597						messages: DeliveredMessages::new(i as _),
598					})
599					.collect(),
600				last_confirmed_nonce: messages_count as _,
601			}
602			.encode()
603			.len();
604			let difference = (expected_size.unwrap() as f64 - actual_size as f64).abs();
605			assert!(
606				difference / (std::cmp::min(actual_size, expected_size.unwrap()) as f64) < 0.1,
607				"Too large difference between actual ({actual_size}) and expected ({expected_size:?}) inbound lane data size. Test case: {relayer_entries}+{messages_count}",
608			);
609		}
610	}
611
612	#[test]
613	fn contains_result_works() {
614		let delivered_messages = DeliveredMessages { begin: 100, end: 150 };
615
616		assert!(!delivered_messages.contains_message(99));
617		assert!(delivered_messages.contains_message(100));
618		assert!(delivered_messages.contains_message(150));
619		assert!(!delivered_messages.contains_message(151));
620	}
621}