1use alloc::vec::Vec;
35use codec::{Decode, DecodeWithMemTracking, Encode};
36use polkadot_core_primitives::Hash;
37use scale_info::TypeInfo;
38use sp_core::ConstU32;
39use sp_runtime::{traits::Hash as HashT, BoundedVec};
40
41use crate::{
42 lift::{MMRExtensionProof, MmrInclusionProof, ProofError},
43 mmr::{MessagePosition, MmrFrontier, MmrRoot, MAX_MMR_HASHES, MAX_MMR_LEAF_COUNT},
44 stream::StreamId,
45 streams_root::{streams_root_from_proof, StreamProof, StreamsRoot},
46 SpecHasher, LEAF_TAG,
47};
48
49#[derive(Clone, Copy, PartialEq, Eq, Debug)]
52pub enum VerifyError {
53 Proof(ProofError),
55 TreeProof,
57 RootMismatch,
60 UnexpectedBase,
62 ExceedsBudget,
65 VariantMismatch,
68 PayloadTooLarge,
70 MalformedResponse,
73}
74
75impl From<ProofError> for VerifyError {
76 fn from(e: ProofError) -> Self {
77 VerifyError::Proof(e)
78 }
79}
80
81pub const MAX_SPECULATIVE_MESSAGE_LEN: u32 = 102_400;
83
84pub const MAX_MESSAGES_PER_RESPONSE: u32 = 1024;
89
90pub fn leaf_hash(leaf_version: u8, payload: &[u8]) -> Hash {
94 let mut preimage = Vec::with_capacity(2 + payload.len());
95 preimage.push(LEAF_TAG);
96 preimage.push(leaf_version);
97 preimage.extend_from_slice(payload);
98 <SpecHasher as HashT>::hash(&preimage)
99}
100
101#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, Debug, TypeInfo)]
103pub struct MessagesRequest {
104 pub stream: StreamId,
105 pub start: MessagePosition,
107 pub under: StreamsRoot,
111 pub max_bytes: u32,
114}
115
116#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, Debug, TypeInfo)]
120pub struct MessagesResponse {
121 pub base: MessagePosition,
123 pub leaf_version: u8,
126 pub payloads: BoundedVec<Vec<u8>, ConstU32<MAX_MESSAGES_PER_RESPONSE>>,
129 pub start_peaks: BoundedVec<Hash, ConstU32<MAX_MMR_HASHES>>,
132 pub extension: MMRExtensionProof,
135 pub tree_proof: StreamProof,
137}
138
139pub fn verify_messages(req: &MessagesRequest, resp: &MessagesResponse) -> Result<(), VerifyError> {
144 if resp.base != req.start {
145 return Err(VerifyError::UnexpectedBase);
146 }
147 let total = resp.payloads.iter().map(|p| p.len() as u64).fold(0u64, u64::saturating_add);
148 if total > req.max_bytes as u64 || (req.max_bytes == 0 && !resp.payloads.is_empty()) {
149 return Err(VerifyError::ExceedsBudget);
150 }
151 verify_messages_response(req.stream, req.under, resp)
152}
153
154pub(crate) fn verify_messages_response(
158 stream: StreamId,
159 under: StreamsRoot,
160 resp: &MessagesResponse,
161) -> Result<(), VerifyError> {
162 let mut frontier = MmrFrontier::from_parts(resp.start_peaks.to_vec(), resp.base.0)
165 .ok_or(VerifyError::MalformedResponse)?;
166 resp.base
169 .0
170 .checked_add(resp.payloads.len() as u64)
171 .filter(|end| *end <= MAX_MMR_LEAF_COUNT)
172 .ok_or(VerifyError::MalformedResponse)?;
173 resp.payloads.iter().try_for_each(|p| check_payload_len(p))?;
176
177 for payload in &resp.payloads {
178 frontier
179 .try_append(leaf_hash(resp.leaf_version, payload))
180 .map_err(|_| VerifyError::MalformedResponse)?;
181 }
182
183 let current = resp.extension.calculate_root(&frontier)?;
184 bind_under(stream, under, current, &resp.tree_proof)
185}
186
187fn check_payload_len(payload: &[u8]) -> Result<(), VerifyError> {
188 if payload.len() as u64 > MAX_SPECULATIVE_MESSAGE_LEN as u64 {
189 return Err(VerifyError::PayloadTooLarge);
190 }
191 Ok(())
192}
193
194fn bind_under(
198 stream: StreamId,
199 under: StreamsRoot,
200 stream_root: MmrRoot,
201 tree_proof: &StreamProof,
202) -> Result<(), VerifyError> {
203 let root =
204 streams_root_from_proof(stream, stream_root.0, tree_proof).ok_or(VerifyError::TreeProof)?;
205 (root == under).then_some(()).ok_or(VerifyError::RootMismatch)
206}
207
208fn event_leaf(resp: &EventResponse) -> Result<Hash, VerifyError> {
209 check_payload_len(&resp.payload)?;
210 Ok(leaf_hash(resp.leaf_version, &resp.payload))
211}
212
213#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, Debug, TypeInfo)]
216pub struct EventRequest {
217 pub stream: StreamId,
218 pub under: StreamsRoot,
220 pub at: Option<MessagePosition>,
222}
223
224#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, Debug, TypeInfo)]
228pub struct EventResponse {
229 pub payload: Vec<u8>,
231 pub leaf_version: u8,
234 pub inclusion: MmrInclusionProof,
236 pub tree_proof: StreamProof,
239}
240
241#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, Debug, TypeInfo)]
244pub enum ExchangeRequest {
245 #[codec(index = 0)]
247 Messages(MessagesRequest),
248 #[codec(index = 1)]
250 Event(EventRequest),
251}
252
253#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, Debug, TypeInfo)]
256pub enum ExchangeResponse {
257 #[codec(index = 0)]
259 Messages(MessagesResponse),
260 #[codec(index = 1)]
262 Event(EventResponse),
263}
264
265#[derive(Clone, PartialEq, Eq, Debug)]
269pub enum VerifiedEvent {
270 Head { position: MessagePosition, frontier: MmrFrontier },
273 Positional { position: MessagePosition },
275}
276
277pub fn verify_event(
281 req: &EventRequest,
282 resp: &EventResponse,
283) -> Result<VerifiedEvent, VerifyError> {
284 match req.at {
285 None => {
286 let (position, frontier) = verify_event_response(req.stream, req.under, resp)?;
287 Ok(VerifiedEvent::Head { position, frontier })
288 },
289 Some(position) => {
290 verify_positional_event_response(req.stream, req.under, position, resp)?;
291 Ok(VerifiedEvent::Positional { position })
292 },
293 }
294}
295
296pub(crate) fn verify_event_response(
301 stream: StreamId,
302 under: StreamsRoot,
303 resp: &EventResponse,
304) -> Result<(MessagePosition, MmrFrontier), VerifyError> {
305 let (position, frontier) = resp.inclusion.verify_head(event_leaf(resp)?)?;
306 bind_under(stream, under, frontier.root(), &resp.tree_proof)?;
307 Ok((position, frontier))
308}
309
310pub(crate) fn verify_positional_event_response(
314 stream: StreamId,
315 under: StreamsRoot,
316 position: MessagePosition,
317 resp: &EventResponse,
318) -> Result<(), VerifyError> {
319 let root = resp.inclusion.verify_leaf(position, event_leaf(resp)?)?;
320 bind_under(stream, under, root, &resp.tree_proof)
321}
322
323#[derive(Clone, PartialEq, Eq, Debug)]
325pub enum ExchangeVerified {
326 Messages,
328 Event(VerifiedEvent),
330}
331
332pub fn verify_exchange(
336 req: &ExchangeRequest,
337 resp: &ExchangeResponse,
338) -> Result<ExchangeVerified, VerifyError> {
339 match (req, resp) {
340 (ExchangeRequest::Messages(mreq), ExchangeResponse::Messages(mresp)) => {
341 verify_messages(mreq, mresp).map(|()| ExchangeVerified::Messages)
342 },
343 (ExchangeRequest::Event(ereq), ExchangeResponse::Event(eresp)) => {
344 verify_event(ereq, eresp).map(ExchangeVerified::Event)
345 },
346 _ => Err(VerifyError::VariantMismatch),
347 }
348}
349
350#[cfg(test)]
351mod tests {
352 use super::*;
353 use crate::{
354 lift::{
355 build_requires, ConsumptionRecord, Interval, LiftsBySource, MmrInclusionProof,
356 RequiresLift,
357 },
358 mmr::{MmrRoot, SpecMerge},
359 streams_root::{gen_stream_proof, streams_root, TreeStep},
360 test_utils::{ch, frontier_at, mmr_size},
361 };
362 use alloc::collections::BTreeMap;
363 use codec::Encode;
364 use mmr_lib::{
365 helper::get_peak_map,
366 leaf_index_to_mmr_size,
367 util::{MemMMR, MemStore},
368 };
369 use polkadot_parachain_primitives::primitives::Id as ParaId;
370 use sp_core::H256;
371
372 fn bound(mmr_size: u64, bag: Hash) -> Hash {
374 MmrRoot::from_bag(get_peak_map(mmr_size), bag).0
375 }
376
377 fn current_root(leaves: &[Hash], n: usize) -> Hash {
379 let store = MemStore::<Hash>::default();
380 let mut mmr = MemMMR::<Hash, SpecMerge>::new(0, &store);
381 for l in &leaves[..n] {
382 mmr.push(*l).unwrap();
383 }
384 bound(mmr.mmr_size(), mmr.get_root().unwrap())
385 }
386
387 fn ancestry(leaves: &[Hash], k: usize, n: usize) -> MMRExtensionProof {
389 let store = MemStore::<Hash>::default();
390 let mut mmr = MemMMR::<Hash, SpecMerge>::new(0, &store);
391 for l in &leaves[..n] {
392 mmr.push(*l).unwrap();
393 }
394 let ap = mmr.gen_ancestry_proof(mmr_size(k)).unwrap();
395 MMRExtensionProof {
396 leaf_count: n as u64,
397 connecting_nodes: BoundedVec::truncate_from(
398 ap.prev_peaks_proof.proof_items().iter().map(|(_, h)| *h).collect::<Vec<_>>(),
399 ),
400 }
401 }
402
403 #[test]
407 fn composition_fetch_verify_then_build_requires() {
408 let source = ParaId::from(1000);
409 let stream = ch(2000);
410
411 let payloads: Vec<Vec<u8>> = (0..6u8).map(|i| vec![i, i.wrapping_add(10)]).collect();
412 let leaves: Vec<Hash> = payloads.iter().map(|p| leaf_hash(0, p)).collect();
413
414 let r_current = current_root(&leaves, 6);
416 let under = streams_root(&BTreeMap::from([(stream, r_current)])).unwrap();
417 let (_r, tree_proof) =
418 gen_stream_proof(&BTreeMap::from([(stream, r_current)]), stream).unwrap();
419
420 let resp = MessagesResponse {
422 base: MessagePosition(2),
423 leaf_version: 0,
424 payloads: vec![payloads[2].clone(), payloads[3].clone()].try_into().unwrap(),
425 start_peaks: BoundedVec::truncate_from(frontier_at(&leaves, 2).peaks().to_vec()),
426 extension: ancestry(&leaves, 4, 6),
427 tree_proof: tree_proof.clone(),
428 };
429 assert_eq!(verify_messages_response(stream, under, &resp), Ok(()));
430
431 let record = ConsumptionRecord {
433 entries: BTreeMap::from([(
434 source,
435 BTreeMap::from([(
436 stream,
437 Interval {
438 start: frontier_at(&leaves, 2).root(),
439 end: frontier_at(&leaves, 4),
440 },
441 )]),
442 )]),
443 };
444 let mut lifts = BTreeMap::new();
445 lifts.insert(
446 source,
447 vec![RequiresLift {
448 advances: Vec::new(),
449 extension: ancestry(&leaves, 4, 6),
450 tree_proof,
451 }],
452 );
453
454 let lifts = LiftsBySource::try_from(lifts).unwrap();
455 let requires = build_requires(&[record], &lifts).unwrap().expect("consumed, so a set");
456 assert_eq!(requires.get(source), Some(&under));
458 }
459
460 #[test]
461 fn leaf_hash_is_payload_only_and_version_disjoint() {
462 let payload = b"hello";
463 let mut pre = Vec::new();
465 pre.push(LEAF_TAG);
466 pre.push(0u8);
467 pre.extend_from_slice(payload);
468 assert_eq!(leaf_hash(0, payload), <SpecHasher as HashT>::hash(&pre));
469 assert_ne!(leaf_hash(0, payload), leaf_hash(1, payload));
471 }
472
473 #[test]
474 fn leaf_hash_frozen_vector() {
475 assert_eq!(
478 leaf_hash(crate::LEAF_VERSION, b"hello"),
479 Hash::from(hex_literal::hex!(
480 "cd31917fb8992dae762dbaaf276d8eb65aa89cdfb87daf69e05f8c08b490e78b"
481 ))
482 );
483 }
484
485 #[test]
487 fn messages_response_round_trips_under_streams_root() {
488 let payloads: Vec<Vec<u8>> = (0..4u8).map(|i| vec![i, i + 1, i + 2]).collect();
489
490 let store = MemStore::<Hash>::default();
492 let mut src = MemMMR::<Hash, SpecMerge>::new(0, &store);
493 for p in &payloads {
494 src.push(leaf_hash(0, p)).unwrap();
495 }
496 let stream_root = bound(src.mmr_size(), src.get_root().unwrap());
497
498 let stream = ch(2000);
499 let entries = BTreeMap::from([(stream, stream_root)]);
500 let under = streams_root(&entries).unwrap();
501 let (_r, tree_proof) = gen_stream_proof(&entries, stream).unwrap();
502
503 let resp = MessagesResponse {
505 base: MessagePosition(0),
506 leaf_version: 0,
507 payloads: payloads.clone().try_into().unwrap(),
508 start_peaks: BoundedVec::new(),
509 extension: MMRExtensionProof::identity(),
510 tree_proof,
511 };
512
513 assert_eq!(verify_messages_response(stream, under, &resp), Ok(()));
514 let mut mmr = MmrFrontier::new();
516 for p in &resp.payloads {
517 mmr.append(leaf_hash(0, p));
518 }
519 assert_eq!(mmr.root().0, stream_root);
520 }
521
522 #[test]
523 fn verify_messages_binds_base_to_request_start() {
524 let payloads: Vec<Vec<u8>> = (0..4u8).map(|i| vec![i, i + 1]).collect();
525 let store = MemStore::<Hash>::default();
526 let mut src = MemMMR::<Hash, SpecMerge>::new(0, &store);
527 for p in &payloads {
528 src.push(leaf_hash(0, p)).unwrap();
529 }
530 let stream = ch(2000);
531 let entries = BTreeMap::from([(stream, bound(src.mmr_size(), src.get_root().unwrap()))]);
532 let under = streams_root(&entries).unwrap();
533 let (_r, tree_proof) = gen_stream_proof(&entries, stream).unwrap();
534
535 let resp = MessagesResponse {
537 base: MessagePosition(0),
538 leaf_version: 0,
539 payloads: payloads.clone().try_into().unwrap(),
540 start_peaks: BoundedVec::new(),
541 extension: MMRExtensionProof::identity(),
542 tree_proof,
543 };
544
545 let total_bytes = payloads.iter().map(|p| p.len() as u32).sum::<u32>();
547
548 let req =
550 MessagesRequest { stream, start: MessagePosition(0), under, max_bytes: total_bytes };
551 assert_eq!(verify_messages(&req, &resp), Ok(()));
552
553 let req_wrong =
556 MessagesRequest { stream, start: MessagePosition(2), under, max_bytes: total_bytes };
557 assert_eq!(verify_messages(&req_wrong, &resp), Err(VerifyError::UnexpectedBase));
558
559 let req_tight = MessagesRequest {
561 stream,
562 start: MessagePosition(0),
563 under,
564 max_bytes: total_bytes - 1,
565 };
566 assert_eq!(verify_messages(&req_tight, &resp), Err(VerifyError::ExceedsBudget));
567
568 let req_liftonly =
570 MessagesRequest { stream, start: MessagePosition(0), under, max_bytes: 0 };
571 assert_eq!(verify_messages(&req_liftonly, &resp), Err(VerifyError::ExceedsBudget));
572 }
573
574 #[test]
575 fn zero_budget_refuses_even_empty_payloads() {
576 let payloads: Vec<Vec<u8>> = vec![Vec::new(), Vec::new()];
579 let stream = ch(2000);
580 let entries = BTreeMap::from([(
581 stream,
582 frontier_at(&payloads.iter().map(|p| leaf_hash(0, p)).collect::<Vec<_>>(), 2)
583 .root()
584 .0,
585 )]);
586 let under = streams_root(&entries).unwrap();
587 let (_r, tree_proof) = gen_stream_proof(&entries, stream).unwrap();
588 let resp = MessagesResponse {
589 base: MessagePosition(0),
590 leaf_version: 0,
591 payloads: payloads.clone().try_into().unwrap(),
592 start_peaks: BoundedVec::new(),
593 extension: MMRExtensionProof::identity(),
594 tree_proof,
595 };
596 let req =
597 |max_bytes| MessagesRequest { stream, start: MessagePosition(0), under, max_bytes };
598 assert_eq!(verify_messages(&req(1), &resp), Ok(()));
599 assert_eq!(verify_messages(&req(0), &resp), Err(VerifyError::ExceedsBudget));
600 }
601
602 #[test]
603 fn a_response_past_the_payload_cap_fails_to_decode() {
604 let cap = MAX_MESSAGES_PER_RESPONSE as usize;
607 let encoded = |count: usize| {
608 (
609 MessagePosition(0),
610 0u8,
611 vec![Vec::<u8>::new(); count],
612 Vec::<Hash>::new(),
613 MMRExtensionProof::identity(),
614 StreamProof { steps: Default::default() },
615 )
616 .encode()
617 };
618 assert!(MessagesResponse::decode(&mut &encoded(cap)[..]).is_ok());
619 assert!(MessagesResponse::decode(&mut &encoded(cap + 1)[..]).is_err());
620 }
621
622 #[test]
623 fn messages_response_rejects_wrong_root_or_tampered_payload() {
624 let payloads: Vec<Vec<u8>> = (0..3u8).map(|i| vec![i]).collect();
625 let store = MemStore::<Hash>::default();
626 let mut src = MemMMR::<Hash, SpecMerge>::new(0, &store);
627 for p in &payloads {
628 src.push(leaf_hash(0, p)).unwrap();
629 }
630 let stream = ch(2000);
631 let entries = BTreeMap::from([(stream, bound(src.mmr_size(), src.get_root().unwrap()))]);
632 let under = streams_root(&entries).unwrap();
633 let (_r, tree_proof) = gen_stream_proof(&entries, stream).unwrap();
634
635 let resp = MessagesResponse {
636 base: MessagePosition(0),
637 leaf_version: 0,
638 payloads: payloads.try_into().unwrap(),
639 start_peaks: BoundedVec::new(),
640 extension: MMRExtensionProof::identity(),
641 tree_proof,
642 };
643
644 assert_eq!(
646 verify_messages_response(stream, StreamsRoot(H256::repeat_byte(0xff)), &resp),
647 Err(VerifyError::RootMismatch),
648 );
649 let mut bad = resp.clone();
651 bad.payloads[0] = vec![0xAA];
652 assert_eq!(verify_messages_response(stream, under, &bad), Err(VerifyError::RootMismatch));
653 }
654
655 #[test]
656 fn malformed_frontier_rejects_without_panicking() {
657 let stream = ch(2000);
658 let under = StreamsRoot(H256::repeat_byte(0xff));
659
660 let crafted = MessagesResponse {
663 base: MessagePosition(1),
664 leaf_version: 0,
665 payloads: vec![vec![0x01]].try_into().unwrap(),
666 start_peaks: BoundedVec::new(),
667 extension: MMRExtensionProof::identity(),
668 tree_proof: StreamProof { steps: Default::default() },
669 };
670 assert_eq!(
671 verify_messages_response(stream, under, &crafted),
672 Err(VerifyError::MalformedResponse),
673 );
674
675 let too_large = MessagesResponse {
678 base: MessagePosition(1 << 63),
679 leaf_version: 0,
680 payloads: vec![vec![0x01]].try_into().unwrap(),
681 start_peaks: BoundedVec::truncate_from(vec![H256::zero(); 1]),
682 extension: MMRExtensionProof::identity(),
683 tree_proof: StreamProof { steps: Default::default() },
684 };
685 assert_eq!(
686 verify_messages_response(stream, under, &too_large),
687 Err(VerifyError::MalformedResponse),
688 );
689
690 let at_ceiling = MessagesResponse {
693 base: MessagePosition(MAX_MMR_LEAF_COUNT),
694 leaf_version: 0,
695 payloads: vec![vec![0x01]].try_into().unwrap(),
696 start_peaks: BoundedVec::truncate_from(vec![H256::zero(); 1]),
697 extension: MMRExtensionProof::identity(),
698 tree_proof: StreamProof { steps: Default::default() },
699 };
700 assert_eq!(
701 verify_messages_response(stream, under, &at_ceiling),
702 Err(VerifyError::MalformedResponse),
703 );
704
705 let overflow = MessagesResponse {
708 base: MessagePosition(u64::MAX),
709 leaf_version: 0,
710 payloads: vec![vec![0x01]].try_into().unwrap(),
711 start_peaks: BoundedVec::truncate_from(vec![H256::zero(); MAX_MMR_HASHES as usize]),
712 extension: MMRExtensionProof::identity(),
713 tree_proof: StreamProof { steps: Default::default() },
714 };
715 assert_eq!(
716 verify_messages_response(stream, under, &overflow),
717 Err(VerifyError::MalformedResponse),
718 );
719 }
720
721 #[test]
724 fn exchange_envelope_indices_are_frozen() {
725 let mreq = MessagesRequest {
726 stream: ch(1),
727 start: MessagePosition(0),
728 under: StreamsRoot(H256::zero()),
729 max_bytes: 0,
730 };
731 let ereq = EventRequest { stream: ch(1), under: StreamsRoot(H256::zero()), at: None };
732 assert_eq!(ExchangeRequest::Messages(mreq).encode()[0], 0x00);
733 assert_eq!(ExchangeRequest::Event(ereq).encode()[0], 0x01);
734
735 let mresp = MessagesResponse {
736 base: MessagePosition(0),
737 leaf_version: 0,
738 payloads: Vec::new().try_into().unwrap(),
739 start_peaks: BoundedVec::new(),
740 extension: MMRExtensionProof::identity(),
741 tree_proof: StreamProof { steps: Default::default() },
742 };
743 let eresp = EventResponse {
744 payload: Vec::new(),
745 leaf_version: 0,
746 inclusion: MmrInclusionProof { mmr_size: 0, items: BoundedVec::new() },
747 tree_proof: StreamProof { steps: Default::default() },
748 };
749 assert_eq!(ExchangeResponse::Messages(mresp).encode()[0], 0x00);
750 assert_eq!(ExchangeResponse::Event(eresp).encode()[0], 0x01);
751 }
752
753 #[test]
755 fn event_response_round_trips_under_streams_root() {
756 let stream = ch(2000);
757 let payloads: Vec<Vec<u8>> = (0..6u8).map(|i| vec![i, i.wrapping_add(7)]).collect();
760
761 let store = MemStore::<Hash>::default();
762 let mut src = MemMMR::<Hash, SpecMerge>::new(0, &store);
763 let positions: Vec<u64> =
764 payloads.iter().map(|p| src.push(leaf_hash(0, p)).unwrap()).collect();
765 let stream_root = bound(src.mmr_size(), src.get_root().unwrap());
766
767 let entries = BTreeMap::from([(stream, stream_root)]);
768 let under = streams_root(&entries).unwrap();
769 let (_r, tree_proof) = gen_stream_proof(&entries, stream).unwrap();
770
771 let head = payloads.len() - 1;
773 let mproof = src.gen_proof(vec![positions[head]]).unwrap();
774 let inclusion = MmrInclusionProof {
775 mmr_size: mmr_size(payloads.len()),
776 items: BoundedVec::truncate_from(mproof.proof_items().to_vec()),
777 };
778
779 let resp = EventResponse {
780 payload: payloads[head].clone(),
781 leaf_version: 0,
782 inclusion,
783 tree_proof,
784 };
785
786 let (pos, frontier) = verify_event_response(stream, under, &resp).expect("head verifies");
788 assert_eq!(pos, MessagePosition(head as u64));
789 assert_eq!(frontier.leaf_count(), 6);
790 assert_eq!(frontier.root().0, stream_root);
791
792 let req = EventRequest { stream, under, at: None };
794 assert_eq!(
795 verify_event(&req, &resp),
796 Ok(VerifiedEvent::Head { position: MessagePosition(head as u64), frontier }),
797 );
798
799 assert!(verify_positional_event_response(stream, under, MessagePosition(0), &resp).is_err());
802 assert_eq!(
804 verify_event_response(stream, StreamsRoot(H256::repeat_byte(0xff)), &resp),
805 Err(VerifyError::RootMismatch),
806 );
807 let mut bad = resp.clone();
809 bad.payload = vec![0xAA];
810 assert_eq!(verify_event_response(stream, under, &bad), Err(VerifyError::RootMismatch));
811 let mut big = resp.clone();
813 big.payload = vec![0u8; MAX_SPECULATIVE_MESSAGE_LEN as usize + 1];
814 assert_eq!(verify_event_response(stream, under, &big), Err(VerifyError::PayloadTooLarge));
815 big.payload.pop();
817 assert_eq!(verify_event_response(stream, under, &big), Err(VerifyError::RootMismatch));
818
819 let mut bogus = resp.clone();
821 bogus.inclusion.mmr_size = 2;
822 assert_eq!(
823 verify_event_response(stream, under, &bogus),
824 Err(VerifyError::Proof(ProofError::InvalidProof)),
825 );
826 }
827
828 #[test]
829 fn event_response_rejects_a_forged_head_position() {
830 let stream = ch(2000);
833 let payloads: Vec<Vec<u8>> = (0..3u8).map(|i| vec![i]).collect();
834 let leaves: Vec<Hash> = payloads.iter().map(|p| leaf_hash(0, p)).collect();
835 let entries = BTreeMap::from([(stream, frontier_at(&leaves, 3).root().0)]);
836 let under = streams_root(&entries).unwrap();
837 let (_r, tree_proof) = gen_stream_proof(&entries, stream).unwrap();
838
839 let forged_count = (1u64 << 47) + 1;
840 let resp = EventResponse {
841 payload: payloads[2].clone(),
842 leaf_version: 0,
843 inclusion: MmrInclusionProof {
844 mmr_size: leaf_index_to_mmr_size(forged_count - 1),
845 items: BoundedVec::truncate_from(frontier_at(&leaves, 2).peaks().to_vec()),
846 },
847 tree_proof,
848 };
849 assert_eq!(verify_event_response(stream, under, &resp), Err(VerifyError::RootMismatch));
850 }
851
852 #[test]
853 fn positional_event_response_round_trips_under_streams_root() {
854 let stream = ch(2000);
855 let payloads: Vec<Vec<u8>> = (0..6u8).map(|i| vec![i, i.wrapping_add(7)]).collect();
856
857 let store = MemStore::<Hash>::default();
858 let mut src = MemMMR::<Hash, SpecMerge>::new(0, &store);
859 let positions: Vec<u64> =
860 payloads.iter().map(|p| src.push(leaf_hash(0, p)).unwrap()).collect();
861 let stream_root = bound(src.mmr_size(), src.get_root().unwrap());
862
863 let entries = BTreeMap::from([(stream, stream_root)]);
864 let under = streams_root(&entries).unwrap();
865 let (_r, tree_proof) = gen_stream_proof(&entries, stream).unwrap();
866
867 let at = 2usize;
869 let mproof = src.gen_proof(vec![positions[at]]).unwrap();
870 let inclusion = MmrInclusionProof {
871 mmr_size: mmr_size(payloads.len()),
872 items: BoundedVec::truncate_from(mproof.proof_items().to_vec()),
873 };
874
875 let resp =
876 EventResponse { payload: payloads[at].clone(), leaf_version: 0, inclusion, tree_proof };
877
878 assert_eq!(
880 verify_positional_event_response(stream, under, MessagePosition(at as u64), &resp),
881 Ok(()),
882 );
883 assert!(verify_positional_event_response(stream, under, MessagePosition(3), &resp).is_err());
885 assert_eq!(
887 verify_positional_event_response(
888 stream,
889 StreamsRoot(H256::repeat_byte(0xff)),
890 MessagePosition(at as u64),
891 &resp,
892 ),
893 Err(VerifyError::RootMismatch),
894 );
895 let mut bad = resp.clone();
897 bad.payload = vec![0xAA];
898 assert_eq!(
899 verify_positional_event_response(stream, under, MessagePosition(at as u64), &bad),
900 Err(VerifyError::RootMismatch),
901 );
902
903 let req = EventRequest { stream, under, at: Some(MessagePosition(at as u64)) };
905 assert_eq!(
906 verify_event(&req, &resp),
907 Ok(VerifiedEvent::Positional { position: MessagePosition(at as u64) }),
908 );
909 }
910
911 #[test]
912 fn event_response_tree_proof_failure_surfaces_as_treeproof() {
913 let stream = ch(2000);
915 let payloads: Vec<Vec<u8>> = (0..6u8).map(|i| vec![i]).collect();
916 let store = MemStore::<Hash>::default();
917 let mut src = MemMMR::<Hash, SpecMerge>::new(0, &store);
918 let positions: Vec<u64> =
919 payloads.iter().map(|p| src.push(leaf_hash(0, p)).unwrap()).collect();
920 let head = payloads.len() - 1;
921 let mproof = src.gen_proof(vec![positions[head]]).unwrap();
922 let resp = EventResponse {
923 payload: payloads[head].clone(),
924 leaf_version: 0,
925 inclusion: MmrInclusionProof {
926 mmr_size: mmr_size(payloads.len()),
927 items: BoundedVec::truncate_from(mproof.proof_items().to_vec()),
928 },
929 tree_proof: StreamProof {
931 steps: vec![TreeStep { split_bit: 255, sibling: H256::zero() }].try_into().unwrap(),
932 },
933 };
934 assert_eq!(
935 verify_event_response(stream, StreamsRoot(H256::repeat_byte(0xff)), &resp),
936 Err(VerifyError::TreeProof),
937 );
938 }
939
940 #[test]
941 fn verify_exchange_dispatches_and_binds_variant() {
942 let mpayloads: Vec<Vec<u8>> = (0..3u8).map(|i| vec![i]).collect();
944 let mstore = MemStore::<Hash>::default();
945 let mut msrc = MemMMR::<Hash, SpecMerge>::new(0, &mstore);
946 for p in &mpayloads {
947 msrc.push(leaf_hash(0, p)).unwrap();
948 }
949 let mstream = ch(2000);
950 let mentries =
951 BTreeMap::from([(mstream, bound(msrc.mmr_size(), msrc.get_root().unwrap()))]);
952 let munder = streams_root(&mentries).unwrap();
953 let (_r, mtree) = gen_stream_proof(&mentries, mstream).unwrap();
954 let mresp = MessagesResponse {
955 base: MessagePosition(0),
956 leaf_version: 0,
957 payloads: mpayloads.clone().try_into().unwrap(),
958 start_peaks: BoundedVec::new(),
959 extension: MMRExtensionProof::identity(),
960 tree_proof: mtree,
961 };
962 let mbytes = mpayloads.iter().map(|p| p.len() as u32).sum::<u32>();
963 let mreq = MessagesRequest {
964 stream: mstream,
965 start: MessagePosition(0),
966 under: munder,
967 max_bytes: mbytes,
968 };
969
970 let epayloads: Vec<Vec<u8>> = (0..6u8).map(|i| vec![i, i + 1]).collect();
972 let estore = MemStore::<Hash>::default();
973 let mut esrc = MemMMR::<Hash, SpecMerge>::new(0, &estore);
974 let epos: Vec<u64> =
975 epayloads.iter().map(|p| esrc.push(leaf_hash(0, p)).unwrap()).collect();
976 let estream = ch(3000);
977 let eentries =
978 BTreeMap::from([(estream, bound(esrc.mmr_size(), esrc.get_root().unwrap()))]);
979 let eunder = streams_root(&eentries).unwrap();
980 let (_r2, etree) = gen_stream_proof(&eentries, estream).unwrap();
981 let ehead = epayloads.len() - 1;
982 let eproof = esrc.gen_proof(vec![epos[ehead]]).unwrap();
983 let eresp = EventResponse {
984 payload: epayloads[ehead].clone(),
985 leaf_version: 0,
986 inclusion: MmrInclusionProof {
987 mmr_size: mmr_size(epayloads.len()),
988 items: BoundedVec::truncate_from(eproof.proof_items().to_vec()),
989 },
990 tree_proof: etree,
991 };
992 let ereq = EventRequest { stream: estream, under: eunder, at: None };
993
994 assert_eq!(
996 verify_exchange(
997 &ExchangeRequest::Messages(mreq.clone()),
998 &ExchangeResponse::Messages(mresp.clone()),
999 ),
1000 Ok(ExchangeVerified::Messages),
1001 );
1002 assert!(matches!(
1003 verify_exchange(
1004 &ExchangeRequest::Event(ereq.clone()),
1005 &ExchangeResponse::Event(eresp.clone()),
1006 ),
1007 Ok(ExchangeVerified::Event(VerifiedEvent::Head { .. })),
1008 ));
1009
1010 assert_eq!(
1012 verify_exchange(&ExchangeRequest::Messages(mreq), &ExchangeResponse::Event(eresp)),
1013 Err(VerifyError::VariantMismatch),
1014 );
1015 assert_eq!(
1016 verify_exchange(&ExchangeRequest::Event(ereq), &ExchangeResponse::Messages(mresp)),
1017 Err(VerifyError::VariantMismatch),
1018 );
1019 }
1020
1021 struct TwoStreams {
1023 under: StreamsRoot,
1024 streams: [(StreamId, Vec<Vec<u8>>, Vec<Hash>, StreamProof); 2],
1026 }
1027
1028 fn two_streams() -> TwoStreams {
1029 let ids = [ch(2000), ch(3000)];
1030 let payloads: [Vec<Vec<u8>>; 2] =
1031 core::array::from_fn(|s| (0..6u8).map(|i| vec![s as u8, i]).collect());
1032 let leaves: [Vec<Hash>; 2] =
1033 core::array::from_fn(|s| payloads[s].iter().map(|p| leaf_hash(0, p)).collect());
1034 let entries: BTreeMap<_, _> =
1035 (0..2).map(|s| (ids[s], frontier_at(&leaves[s], 6).root().0)).collect();
1036 let under = streams_root(&entries).unwrap();
1037 let streams = core::array::from_fn(|s| {
1038 let (_, proof) = gen_stream_proof(&entries, ids[s]).unwrap();
1039 (ids[s], payloads[s].clone(), leaves[s].clone(), proof)
1040 });
1041 TwoStreams { under, streams }
1042 }
1043
1044 fn fetch(
1046 (_, payloads, leaves, tree_proof): &(StreamId, Vec<Vec<u8>>, Vec<Hash>, StreamProof),
1047 base: usize,
1048 end: usize,
1049 ) -> MessagesResponse {
1050 MessagesResponse {
1051 base: MessagePosition(base as u64),
1052 leaf_version: 0,
1053 payloads: payloads[base..end].to_vec().try_into().unwrap(),
1054 start_peaks: BoundedVec::truncate_from(frontier_at(leaves, base).peaks().to_vec()),
1055 extension: ancestry(leaves, end, 6),
1056 tree_proof: tree_proof.clone(),
1057 }
1058 }
1059
1060 fn head(
1062 (_, payloads, leaves, tree_proof): &(StreamId, Vec<Vec<u8>>, Vec<Hash>, StreamProof),
1063 ) -> EventResponse {
1064 let store = MemStore::<Hash>::default();
1065 let mut mmr = MemMMR::<Hash, SpecMerge>::new(0, &store);
1066 let positions: Vec<u64> = leaves.iter().map(|l| mmr.push(*l).unwrap()).collect();
1067 let proof = mmr.gen_proof(vec![positions[5]]).unwrap();
1068 EventResponse {
1069 payload: payloads[5].clone(),
1070 leaf_version: 0,
1071 inclusion: MmrInclusionProof {
1072 mmr_size: mmr_size(6),
1073 items: BoundedVec::truncate_from(proof.proof_items().to_vec()),
1074 },
1075 tree_proof: tree_proof.clone(),
1076 }
1077 }
1078
1079 #[test]
1080 fn a_forged_tail_after_a_genuine_prefix_does_not_verify() {
1081 let t = two_streams();
1082 let (stream, ..) = t.streams[0];
1083 let honest = fetch(&t.streams[0], 2, 4);
1084 assert_eq!(verify_messages_response(stream, t.under, &honest), Ok(()));
1085
1086 let mut forged = honest.clone();
1088 forged.payloads[1] = vec![0xAA];
1089 assert_eq!(
1090 verify_messages_response(stream, t.under, &forged),
1091 Err(VerifyError::RootMismatch)
1092 );
1093
1094 let mut appended = fetch(&t.streams[0], 2, 4);
1096 appended.payloads.try_push(vec![0xAA]).unwrap();
1097 appended.extension = ancestry(&t.streams[0].2, 5, 6);
1098 assert_eq!(
1099 verify_messages_response(stream, t.under, &appended),
1100 Err(VerifyError::RootMismatch)
1101 );
1102 }
1103
1104 #[test]
1105 fn a_messages_payload_past_the_bound_is_too_large() {
1106 let t = two_streams();
1107 let (stream, ..) = t.streams[0];
1108 let mut resp = fetch(&t.streams[0], 2, 4);
1109 resp.payloads[1] = vec![0; MAX_SPECULATIVE_MESSAGE_LEN as usize + 1];
1110 let req = MessagesRequest {
1111 stream,
1112 start: MessagePosition(2),
1113 under: t.under,
1114 max_bytes: u32::MAX,
1115 };
1116 assert_eq!(verify_messages(&req, &resp), Err(VerifyError::PayloadTooLarge));
1117
1118 resp.payloads[1].pop();
1120 assert_eq!(verify_messages(&req, &resp), Err(VerifyError::RootMismatch));
1121 }
1122
1123 #[test]
1124 fn a_wrong_leaf_version_is_a_root_mismatch() {
1125 let t = two_streams();
1126 let (stream, ..) = t.streams[0];
1127 let mut resp = fetch(&t.streams[0], 2, 4);
1128 resp.leaf_version = 1;
1129 assert_eq!(
1130 verify_messages_response(stream, t.under, &resp),
1131 Err(VerifyError::RootMismatch)
1132 );
1133
1134 let mut event = head(&t.streams[0]);
1135 event.leaf_version = 1;
1136 assert_eq!(verify_event_response(stream, t.under, &event), Err(VerifyError::RootMismatch));
1137 }
1138
1139 #[test]
1140 fn material_of_another_stream_is_a_root_mismatch() {
1141 let t = two_streams();
1142 let (a, ..) = t.streams[0];
1143 let (b, ..) = t.streams[1];
1144
1145 let mut resp = fetch(&t.streams[0], 2, 4);
1147 resp.tree_proof = t.streams[1].3.clone();
1148 assert_eq!(verify_messages_response(a, t.under, &resp), Err(VerifyError::RootMismatch));
1149
1150 let resp = fetch(&t.streams[1], 2, 4);
1152 assert_eq!(verify_messages_response(b, t.under, &resp), Ok(()));
1153 assert_eq!(verify_messages_response(a, t.under, &resp), Err(VerifyError::RootMismatch));
1154
1155 let event = head(&t.streams[1]);
1156 assert!(verify_event_response(b, t.under, &event).is_ok());
1157 assert_eq!(verify_event_response(a, t.under, &event), Err(VerifyError::RootMismatch));
1158 }
1159
1160 #[test]
1161 fn a_response_without_payloads_verifies_and_advances_nothing() {
1162 let t = two_streams();
1165 let (stream, ..) = t.streams[0];
1166 let resp = fetch(&t.streams[0], 2, 2);
1167 assert!(resp.payloads.is_empty());
1168 for max_bytes in [0, u32::MAX] {
1169 let req =
1170 MessagesRequest { stream, start: MessagePosition(2), under: t.under, max_bytes };
1171 assert_eq!(verify_messages(&req, &resp), Ok(()));
1172 }
1173 }
1174}