1use crate::{
22 finality_base::best_synced_header_id,
23 messages::{
24 BatchProofTransaction, MessageLaneAdapter, ReceiveMessagesDeliveryProofCallBuilder,
25 SubstrateMessageLane,
26 },
27 on_demand::OnDemandRelay,
28 proofs::to_raw_storage_proof,
29 TransactionParams,
30};
31
32use async_std::sync::Arc;
33use async_trait::async_trait;
34use bp_messages::{
35 storage_keys::{operating_mode_key, outbound_lane_data_key},
36 target_chain::FromBridgedChainMessagesProof,
37 ChainWithMessages as _, InboundMessageDetails, MessageNonce, MessagePayload,
38 MessagesOperatingMode, OutboundMessageDetails,
39};
40use bp_runtime::{BasicOperatingMode, HeaderIdProvider, RangeInclusiveExt};
41use codec::{Decode, Encode};
42use frame_support::weights::Weight;
43use messages_relay::{
44 message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf},
45 message_lane_loop::{
46 ClientState, MessageDetails, MessageDetailsMap, MessageProofParameters, SourceClient,
47 SourceClientState,
48 },
49};
50use num_traits::Zero;
51use relay_substrate_client::{
52 AccountIdOf, AccountKeyPairOf, BalanceOf, Chain, ChainWithMessages, Client,
53 Error as SubstrateError, HashOf, HeaderIdOf, TransactionEra, TransactionTracker,
54 UnsignedTransaction,
55};
56use relay_utils::relay_loop::Client as RelayClient;
57use sp_core::Pair;
58use std::ops::RangeInclusive;
59
60pub type SubstrateMessagesProof<C, L> = (Weight, FromBridgedChainMessagesProof<HashOf<C>, L>);
64type MessagesToRefine<'a> = Vec<(MessagePayload, &'a mut OutboundMessageDetails)>;
65
66#[derive(Decode)]
71struct LegacyOutboundLaneData {
72 #[allow(unused)]
73 oldest_unpruned_nonce: MessageNonce,
74 latest_received_nonce: MessageNonce,
75 latest_generated_nonce: MessageNonce,
76}
77
78pub struct SubstrateMessagesSource<P: SubstrateMessageLane, SourceClnt, TargetClnt> {
80 source_client: SourceClnt,
81 target_client: TargetClnt,
82 lane_id: P::LaneId,
83 transaction_params: TransactionParams<AccountKeyPairOf<P::SourceChain>>,
84 target_to_source_headers_relay: Option<Arc<dyn OnDemandRelay<P::TargetChain, P::SourceChain>>>,
85}
86
87impl<P: SubstrateMessageLane, SourceClnt: Client<P::SourceChain>, TargetClnt>
88 SubstrateMessagesSource<P, SourceClnt, TargetClnt>
89{
90 pub fn new(
92 source_client: SourceClnt,
93 target_client: TargetClnt,
94 lane_id: P::LaneId,
95 transaction_params: TransactionParams<AccountKeyPairOf<P::SourceChain>>,
96 target_to_source_headers_relay: Option<
97 Arc<dyn OnDemandRelay<P::TargetChain, P::SourceChain>>,
98 >,
99 ) -> Self {
100 SubstrateMessagesSource {
101 source_client,
102 target_client,
103 lane_id,
104 transaction_params,
105 target_to_source_headers_relay,
106 }
107 }
108
109 async fn outbound_lane_data(
111 &self,
112 id: SourceHeaderIdOf<MessageLaneAdapter<P>>,
113 ) -> Result<Option<LegacyOutboundLaneData>, SubstrateError> {
114 self.source_client
115 .storage_value(
116 id.hash(),
117 outbound_lane_data_key(
118 P::TargetChain::WITH_CHAIN_MESSAGES_PALLET_NAME,
119 &self.lane_id,
120 ),
121 )
122 .await
123 }
124
125 async fn ensure_pallet_active(&self) -> Result<(), SubstrateError> {
127 ensure_messages_pallet_active::<P::SourceChain, P::TargetChain, _>(&self.source_client)
128 .await
129 }
130}
131
132impl<P: SubstrateMessageLane, SourceClnt: Clone, TargetClnt: Clone> Clone
133 for SubstrateMessagesSource<P, SourceClnt, TargetClnt>
134{
135 fn clone(&self) -> Self {
136 Self {
137 source_client: self.source_client.clone(),
138 target_client: self.target_client.clone(),
139 lane_id: self.lane_id,
140 transaction_params: self.transaction_params.clone(),
141 target_to_source_headers_relay: self.target_to_source_headers_relay.clone(),
142 }
143 }
144}
145
146#[async_trait]
147impl<
148 P: SubstrateMessageLane,
149 SourceClnt: Client<P::SourceChain>,
150 TargetClnt: Client<P::TargetChain>,
151 > RelayClient for SubstrateMessagesSource<P, SourceClnt, TargetClnt>
152{
153 type Error = SubstrateError;
154
155 async fn reconnect(&mut self) -> Result<(), SubstrateError> {
156 self.source_client.reconnect().await?;
158 self.target_client.reconnect().await?;
159
160 if let Some(ref mut target_to_source_headers_relay) = self.target_to_source_headers_relay {
168 target_to_source_headers_relay.reconnect().await?;
169 }
170
171 Ok(())
172 }
173}
174
175#[async_trait]
176impl<
177 P: SubstrateMessageLane,
178 SourceClnt: Client<P::SourceChain>,
179 TargetClnt: Client<P::TargetChain>,
180 > SourceClient<MessageLaneAdapter<P>> for SubstrateMessagesSource<P, SourceClnt, TargetClnt>
181where
182 AccountIdOf<P::SourceChain>: From<<AccountKeyPairOf<P::SourceChain> as Pair>::Public>,
183{
184 type BatchTransaction =
185 BatchProofTransaction<P::SourceChain, P::TargetChain, P::SourceBatchCallBuilder>;
186 type TransactionTracker = TransactionTracker<P::SourceChain, SourceClnt>;
187
188 async fn state(&self) -> Result<SourceClientState<MessageLaneAdapter<P>>, SubstrateError> {
189 self.source_client.ensure_synced().await?;
195 self.target_client.ensure_synced().await?;
196 self.ensure_pallet_active().await?;
198
199 read_client_state_from_both_chains(&self.source_client, &self.target_client).await
200 }
201
202 async fn latest_generated_nonce(
203 &self,
204 id: SourceHeaderIdOf<MessageLaneAdapter<P>>,
205 ) -> Result<(SourceHeaderIdOf<MessageLaneAdapter<P>>, MessageNonce), SubstrateError> {
206 let latest_generated_nonce = self
208 .outbound_lane_data(id)
209 .await?
210 .map(|data| data.latest_generated_nonce)
211 .unwrap_or(0);
212 Ok((id, latest_generated_nonce))
213 }
214
215 async fn latest_confirmed_received_nonce(
216 &self,
217 id: SourceHeaderIdOf<MessageLaneAdapter<P>>,
218 ) -> Result<(SourceHeaderIdOf<MessageLaneAdapter<P>>, MessageNonce), SubstrateError> {
219 let latest_received_nonce = self
221 .outbound_lane_data(id)
222 .await?
223 .map(|data| data.latest_received_nonce)
224 .unwrap_or(0);
225 Ok((id, latest_received_nonce))
226 }
227
228 async fn generated_message_details(
229 &self,
230 id: SourceHeaderIdOf<MessageLaneAdapter<P>>,
231 nonces: RangeInclusive<MessageNonce>,
232 ) -> Result<MessageDetailsMap<BalanceOf<P::SourceChain>>, SubstrateError> {
233 let mut out_msgs_details: Vec<_> = self
234 .source_client
235 .state_call::<_, Vec<_>>(
236 id.hash(),
237 P::TargetChain::TO_CHAIN_MESSAGE_DETAILS_METHOD.into(),
238 (self.lane_id, *nonces.start(), *nonces.end()),
239 )
240 .await?;
241 validate_out_msgs_details::<P::SourceChain>(&out_msgs_details, nonces)?;
242
243 let mut msgs_to_refine = vec![];
245 for out_msg_details in out_msgs_details.iter_mut() {
246 let msg_key = bp_messages::storage_keys::message_key(
251 P::TargetChain::WITH_CHAIN_MESSAGES_PALLET_NAME,
252 &self.lane_id,
253 out_msg_details.nonce,
254 );
255 let msg_payload: MessagePayload =
256 self.source_client.storage_value(id.hash(), msg_key).await?.ok_or_else(|| {
257 SubstrateError::Custom(format!(
258 "Message to {} {:?}/{} is missing from runtime the storage of {} at {:?}",
259 P::TargetChain::NAME,
260 self.lane_id,
261 out_msg_details.nonce,
262 P::SourceChain::NAME,
263 id,
264 ))
265 })?;
266
267 msgs_to_refine.push((msg_payload, out_msg_details));
268 }
269
270 let best_target_header_hash = self.target_client.best_header_hash().await?;
271 for mut msgs_to_refine_batch in split_msgs_to_refine::<
272 P::SourceChain,
273 P::TargetChain,
274 P::LaneId,
275 >(self.lane_id, msgs_to_refine)?
276 {
277 let in_msgs_details = self
278 .target_client
279 .state_call::<_, Vec<InboundMessageDetails>>(
280 best_target_header_hash,
281 P::SourceChain::FROM_CHAIN_MESSAGE_DETAILS_METHOD.into(),
282 (self.lane_id, &msgs_to_refine_batch),
283 )
284 .await?;
285 if in_msgs_details.len() != msgs_to_refine_batch.len() {
286 return Err(SubstrateError::Custom(format!(
287 "Call of {} at {} has returned {} entries instead of expected {}",
288 P::SourceChain::FROM_CHAIN_MESSAGE_DETAILS_METHOD,
289 P::TargetChain::NAME,
290 in_msgs_details.len(),
291 msgs_to_refine_batch.len(),
292 )))
293 }
294 for ((_, out_msg_details), in_msg_details) in
295 msgs_to_refine_batch.iter_mut().zip(in_msgs_details)
296 {
297 log::trace!(
298 target: "bridge",
299 "Refined weight of {}->{} message {:?}/{}: at-source: {}, at-target: {}",
300 P::SourceChain::NAME,
301 P::TargetChain::NAME,
302 self.lane_id,
303 out_msg_details.nonce,
304 out_msg_details.dispatch_weight,
305 in_msg_details.dispatch_weight,
306 );
307 out_msg_details.dispatch_weight = in_msg_details.dispatch_weight;
308 }
309 }
310
311 let mut msgs_details_map = MessageDetailsMap::new();
312 for out_msg_details in out_msgs_details {
313 msgs_details_map.insert(
314 out_msg_details.nonce,
315 MessageDetails {
316 dispatch_weight: out_msg_details.dispatch_weight,
317 size: out_msg_details.size as _,
318 reward: Zero::zero(),
319 },
320 );
321 }
322
323 Ok(msgs_details_map)
324 }
325
326 async fn prove_messages(
327 &self,
328 id: SourceHeaderIdOf<MessageLaneAdapter<P>>,
329 nonces: RangeInclusive<MessageNonce>,
330 proof_parameters: MessageProofParameters,
331 ) -> Result<
332 (
333 SourceHeaderIdOf<MessageLaneAdapter<P>>,
334 RangeInclusive<MessageNonce>,
335 <MessageLaneAdapter<P> as MessageLane>::MessagesProof,
336 ),
337 SubstrateError,
338 > {
339 let mut storage_keys = Vec::with_capacity(nonces.saturating_len() as usize);
340 for message_nonce in nonces.clone() {
341 let message_key = bp_messages::storage_keys::message_key(
342 P::TargetChain::WITH_CHAIN_MESSAGES_PALLET_NAME,
343 &self.lane_id,
344 message_nonce,
345 );
346 storage_keys.push(message_key);
347 }
348 if proof_parameters.outbound_state_proof_required {
349 storage_keys.push(outbound_lane_data_key(
350 P::TargetChain::WITH_CHAIN_MESSAGES_PALLET_NAME,
351 &self.lane_id,
352 ));
353 }
354
355 let storage_proof =
356 self.source_client.prove_storage(id.hash(), storage_keys.clone()).await?;
357 let proof = FromBridgedChainMessagesProof {
358 bridged_header_hash: id.1,
359 storage_proof: to_raw_storage_proof::<P::SourceChain>(storage_proof),
360 lane: self.lane_id,
361 nonces_start: *nonces.start(),
362 nonces_end: *nonces.end(),
363 };
364 Ok((id, nonces, (proof_parameters.dispatch_weight, proof)))
365 }
366
367 async fn submit_messages_receiving_proof(
368 &self,
369 maybe_batch_tx: Option<Self::BatchTransaction>,
370 _generated_at_block: TargetHeaderIdOf<MessageLaneAdapter<P>>,
371 proof: <MessageLaneAdapter<P> as MessageLane>::MessagesReceivingProof,
372 ) -> Result<Self::TransactionTracker, SubstrateError> {
373 let messages_proof_call =
374 P::ReceiveMessagesDeliveryProofCallBuilder::build_receive_messages_delivery_proof_call(
375 proof,
376 maybe_batch_tx.is_none(),
377 );
378 let final_call = match maybe_batch_tx {
379 Some(batch_tx) => batch_tx.append_call_and_build(messages_proof_call),
380 None => messages_proof_call,
381 };
382
383 let transaction_params = self.transaction_params.clone();
384 self.source_client
385 .submit_and_watch_signed_extrinsic(
386 &self.transaction_params.signer,
387 move |best_block_id, transaction_nonce| {
388 Ok(UnsignedTransaction::new(final_call.into(), transaction_nonce)
389 .era(TransactionEra::new(best_block_id, transaction_params.mortality)))
390 },
391 )
392 .await
393 }
394
395 async fn require_target_header_on_source(
396 &self,
397 id: TargetHeaderIdOf<MessageLaneAdapter<P>>,
398 ) -> Result<Option<Self::BatchTransaction>, SubstrateError> {
399 if let Some(ref target_to_source_headers_relay) = self.target_to_source_headers_relay {
400 if let Some(batch_tx) =
401 BatchProofTransaction::new(target_to_source_headers_relay.clone(), id.0).await?
402 {
403 return Ok(Some(batch_tx))
404 }
405
406 target_to_source_headers_relay.require_more_headers(id.0).await;
407 }
408
409 Ok(None)
410 }
411}
412
413pub(crate) async fn ensure_messages_pallet_active<AtChain, WithChain, AtChainClient>(
415 client: &AtChainClient,
416) -> Result<(), SubstrateError>
417where
418 AtChain: ChainWithMessages,
419 WithChain: ChainWithMessages,
420 AtChainClient: Client<AtChain>,
421{
422 let operating_mode = client
423 .storage_value(
424 client.best_header_hash().await?,
425 operating_mode_key(WithChain::WITH_CHAIN_MESSAGES_PALLET_NAME),
426 )
427 .await?;
428 let is_halted =
429 operating_mode == Some(MessagesOperatingMode::Basic(BasicOperatingMode::Halted));
430 if is_halted {
431 Err(SubstrateError::BridgePalletIsHalted)
432 } else {
433 Ok(())
434 }
435}
436
437pub async fn read_client_state<SelfChain, PeerChain>(
446 self_client: &impl Client<SelfChain>,
447) -> Result<ClientState<HeaderIdOf<SelfChain>, HeaderIdOf<PeerChain>>, SubstrateError>
448where
449 SelfChain: Chain,
450 PeerChain: Chain,
451{
452 let self_best_finalized_id = self_client.best_finalized_header().await?.id();
454 let self_best_id = self_client.best_header().await?.id();
456
457 let peer_on_self_best_finalized_id =
459 best_synced_header_id::<PeerChain, SelfChain>(self_client, self_best_id.hash()).await?;
460
461 Ok(ClientState {
462 best_self: self_best_id,
463 best_finalized_self: self_best_finalized_id,
464 best_finalized_peer_at_best_self: peer_on_self_best_finalized_id,
465 actual_best_finalized_peer_at_best_self: peer_on_self_best_finalized_id,
466 })
467}
468
469pub async fn read_client_state_from_both_chains<SelfChain, PeerChain>(
472 self_client: &impl Client<SelfChain>,
473 peer_client: &impl Client<PeerChain>,
474) -> Result<ClientState<HeaderIdOf<SelfChain>, HeaderIdOf<PeerChain>>, SubstrateError>
475where
476 SelfChain: Chain,
477 PeerChain: Chain,
478{
479 let mut client_state = read_client_state::<SelfChain, PeerChain>(self_client).await?;
480 client_state.actual_best_finalized_peer_at_best_self =
481 match client_state.best_finalized_peer_at_best_self.as_ref() {
482 Some(peer_on_self_best_finalized_id) => {
483 let actual_peer_on_self_best_finalized =
484 peer_client.header_by_number(peer_on_self_best_finalized_id.number()).await?;
485 Some(actual_peer_on_self_best_finalized.id())
486 },
487 _ => client_state.best_finalized_peer_at_best_self,
488 };
489 Ok(client_state)
490}
491
492pub async fn best_finalized_peer_header_at_self<SelfChain, PeerChain>(
496 self_client: &impl Client<SelfChain>,
497 at_self_hash: HashOf<SelfChain>,
498) -> Result<Option<HeaderIdOf<PeerChain>>, SubstrateError>
499where
500 SelfChain: Chain,
501 PeerChain: Chain,
502{
503 self_client
505 .state_call::<_, Option<_>>(
506 at_self_hash,
507 PeerChain::BEST_FINALIZED_HEADER_ID_METHOD.into(),
508 (),
509 )
510 .await
511}
512
513fn validate_out_msgs_details<C: Chain>(
514 out_msgs_details: &[OutboundMessageDetails],
515 nonces: RangeInclusive<MessageNonce>,
516) -> Result<(), SubstrateError> {
517 let make_missing_nonce_error = |expected_nonce| {
518 Err(SubstrateError::Custom(format!(
519 "Missing nonce {expected_nonce} in message_details call result. Expected all nonces from {nonces:?}",
520 )))
521 };
522
523 if out_msgs_details.len() > nonces.clone().count() {
524 return Err(SubstrateError::Custom(
525 "More messages than requested returned by the message_details call.".into(),
526 ))
527 }
528
529 if out_msgs_details.is_empty() && !nonces.is_empty() {
531 return make_missing_nonce_error(*nonces.end())
532 }
533
534 let mut nonces_iter = nonces.clone().rev().peekable();
535 let mut out_msgs_details_iter = out_msgs_details.iter().rev();
536 while let Some((out_msg_details, &nonce)) = out_msgs_details_iter.next().zip(nonces_iter.peek())
537 {
538 nonces_iter.next();
539 if out_msg_details.nonce != nonce {
540 return make_missing_nonce_error(nonce)
542 }
543 }
544
545 if nonces_iter.peek().is_some() {
549 log::info!(
550 target: "bridge",
551 "Some messages are missing from the {} node: {:?}. Target node may be out of sync?",
552 C::NAME,
553 nonces_iter.rev().collect::<Vec<_>>(),
554 );
555 }
556
557 Ok(())
558}
559
560fn split_msgs_to_refine<Source: Chain + ChainWithMessages, Target: Chain, LaneId: Encode + Copy>(
561 lane_id: LaneId,
562 msgs_to_refine: MessagesToRefine,
563) -> Result<Vec<MessagesToRefine>, SubstrateError> {
564 let max_batch_size = Target::max_extrinsic_size() as usize;
565 let mut batches = vec![];
566
567 let mut current_msgs_batch = msgs_to_refine;
568 while !current_msgs_batch.is_empty() {
569 let mut next_msgs_batch = vec![];
570 while (lane_id, ¤t_msgs_batch).encoded_size() > max_batch_size {
571 if current_msgs_batch.len() <= 1 {
572 return Err(SubstrateError::Custom(format!(
573 "Call of {} at {} can't be executed even if only one message is supplied. \
574 max_extrinsic_size(): {}",
575 Source::FROM_CHAIN_MESSAGE_DETAILS_METHOD,
576 Target::NAME,
577 Target::max_extrinsic_size(),
578 )))
579 }
580
581 if let Some(msg) = current_msgs_batch.pop() {
582 next_msgs_batch.insert(0, msg);
583 }
584 }
585
586 batches.push(current_msgs_batch);
587 current_msgs_batch = next_msgs_batch;
588 }
589
590 Ok(batches)
591}
592
593#[cfg(test)]
594mod tests {
595 use super::*;
596 use bp_messages::{HashedLaneId, LaneIdType};
597 use relay_substrate_client::test_chain::TestChain;
598
599 type TestLaneIdType = HashedLaneId;
601
602 fn message_details_from_rpc(
603 nonces: RangeInclusive<MessageNonce>,
604 ) -> Vec<OutboundMessageDetails> {
605 nonces
606 .into_iter()
607 .map(|nonce| bp_messages::OutboundMessageDetails {
608 nonce,
609 dispatch_weight: Weight::zero(),
610 size: 0,
611 })
612 .collect()
613 }
614
615 #[test]
616 fn validate_out_msgs_details_succeeds_if_no_messages_are_missing() {
617 assert!(validate_out_msgs_details::<TestChain>(&message_details_from_rpc(1..=3), 1..=3,)
618 .is_ok());
619 }
620
621 #[test]
622 fn validate_out_msgs_details_succeeds_if_head_messages_are_missing() {
623 assert!(validate_out_msgs_details::<TestChain>(&message_details_from_rpc(2..=3), 1..=3,)
624 .is_ok())
625 }
626
627 #[test]
628 fn validate_out_msgs_details_fails_if_mid_messages_are_missing() {
629 let mut message_details_from_rpc = message_details_from_rpc(1..=3);
630 message_details_from_rpc.remove(1);
631 assert!(matches!(
632 validate_out_msgs_details::<TestChain>(&message_details_from_rpc, 1..=3,),
633 Err(SubstrateError::Custom(_))
634 ));
635 }
636
637 #[test]
638 fn validate_out_msgs_details_map_fails_if_tail_messages_are_missing() {
639 assert!(matches!(
640 validate_out_msgs_details::<TestChain>(&message_details_from_rpc(1..=2), 1..=3,),
641 Err(SubstrateError::Custom(_))
642 ));
643 }
644
645 #[test]
646 fn validate_out_msgs_details_fails_if_all_messages_are_missing() {
647 assert!(matches!(
648 validate_out_msgs_details::<TestChain>(&[], 1..=3),
649 Err(SubstrateError::Custom(_))
650 ));
651 }
652
653 #[test]
654 fn validate_out_msgs_details_fails_if_more_messages_than_nonces() {
655 assert!(matches!(
656 validate_out_msgs_details::<TestChain>(&message_details_from_rpc(1..=5), 2..=5,),
657 Err(SubstrateError::Custom(_))
658 ));
659 }
660
661 fn check_split_msgs_to_refine(
662 payload_sizes: Vec<usize>,
663 expected_batches: Result<Vec<usize>, ()>,
664 ) {
665 let mut out_msgs_details = vec![];
666 for (idx, _) in payload_sizes.iter().enumerate() {
667 out_msgs_details.push(OutboundMessageDetails {
668 nonce: idx as MessageNonce,
669 dispatch_weight: Weight::zero(),
670 size: 0,
671 });
672 }
673
674 let mut msgs_to_refine = vec![];
675 for (&payload_size, out_msg_details) in
676 payload_sizes.iter().zip(out_msgs_details.iter_mut())
677 {
678 let payload = vec![1u8; payload_size];
679 msgs_to_refine.push((payload, out_msg_details));
680 }
681
682 let maybe_batches = split_msgs_to_refine::<TestChain, TestChain, TestLaneIdType>(
683 TestLaneIdType::try_new(1, 2).unwrap(),
684 msgs_to_refine,
685 );
686 match expected_batches {
687 Ok(expected_batches) => {
688 let batches = maybe_batches.unwrap();
689 let mut idx = 0;
690 assert_eq!(batches.len(), expected_batches.len());
691 for (batch, &expected_batch_size) in batches.iter().zip(expected_batches.iter()) {
692 assert_eq!(batch.len(), expected_batch_size);
693 for msg_to_refine in batch {
694 assert_eq!(msg_to_refine.0.len(), payload_sizes[idx]);
695 idx += 1;
696 }
697 }
698 },
699 Err(_) => {
700 matches!(maybe_batches, Err(SubstrateError::Custom(_)));
701 },
702 }
703 }
704
705 #[test]
706 fn test_split_msgs_to_refine() {
707 let max_extrinsic_size = 100000;
708
709 check_split_msgs_to_refine(vec![max_extrinsic_size], Err(()));
711 check_split_msgs_to_refine(vec![50, 100, max_extrinsic_size, 200], Err(()));
712
713 check_split_msgs_to_refine(vec![100, 200, 300, 400], Ok(vec![4]));
715 check_split_msgs_to_refine(
716 vec![
717 50,
718 100,
719 max_extrinsic_size - 500,
720 500,
721 1000,
722 1500,
723 max_extrinsic_size - 3500,
724 5000,
725 10000,
726 ],
727 Ok(vec![3, 4, 2]),
728 );
729 check_split_msgs_to_refine(
730 vec![
731 50,
732 100,
733 max_extrinsic_size - 150,
734 500,
735 1000,
736 1500,
737 max_extrinsic_size - 3000,
738 5000,
739 10000,
740 ],
741 Ok(vec![2, 1, 3, 1, 2]),
742 );
743 check_split_msgs_to_refine(
744 vec![
745 5000,
746 10000,
747 max_extrinsic_size - 3500,
748 500,
749 1000,
750 1500,
751 max_extrinsic_size - 500,
752 50,
753 100,
754 ],
755 Ok(vec![2, 4, 3]),
756 );
757 }
758
759 #[test]
760 fn outbound_lane_data_wrapper_is_compatible() {
761 let bytes_without_state =
762 vec![1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0];
763 let bytes_with_state = {
764 let mut b = bytes_without_state.clone();
766 b.push(0);
767 b
768 };
769
770 let full = bp_messages::OutboundLaneData {
771 oldest_unpruned_nonce: 1,
772 latest_received_nonce: 2,
773 latest_generated_nonce: 3,
774 state: bp_messages::LaneState::Opened,
775 };
776 assert_eq!(full.encode(), bytes_with_state);
777 assert_ne!(full.encode(), bytes_without_state);
778
779 let decoded: LegacyOutboundLaneData = Decode::decode(&mut &bytes_with_state[..]).unwrap();
781 assert_eq!(full.oldest_unpruned_nonce, decoded.oldest_unpruned_nonce);
782 assert_eq!(full.latest_received_nonce, decoded.latest_received_nonce);
783 assert_eq!(full.latest_generated_nonce, decoded.latest_generated_nonce);
784
785 let decoded: LegacyOutboundLaneData =
787 Decode::decode(&mut &bytes_without_state[..]).unwrap();
788 assert_eq!(full.oldest_unpruned_nonce, decoded.oldest_unpruned_nonce);
789 assert_eq!(full.latest_received_nonce, decoded.latest_received_nonce);
790 assert_eq!(full.latest_generated_nonce, decoded.latest_generated_nonce);
791 }
792}