miden_node_proto/domain/
block.rs1use std::ops::RangeInclusive;
2
3use miden_protobuf::{BuildUnchecked, ConversionResultExt, Verify, VerifyWith};
4use miden_protocol::block::{BlockHeader, BlockNumber, SignedBlock};
5use miden_protocol::protocol_config::ProtocolConfig;
6use thiserror::Error;
7
8use super::protocol_config::verify_protocol_config_commitment;
9use crate::errors::ConversionError;
10use crate::generated as proto;
11
12impl BuildUnchecked for proto::miden::node::v1::DecodedBlockSubscriptionResponse {
13 type Output = (SignedBlock, BlockNumber, Option<ProtocolConfig>);
14 type Error = ConversionError;
15
16 fn build_unchecked(self) -> Result<Self::Output, Self::Error> {
20 let block = self.block.build_unchecked().context("block")?;
23 let protocol_config = self.protocol_config.try_map(|config| {
24 verify_protocol_config_commitment(
25 config.verify().map_err(ConversionError::new)?,
26 block.header(),
27 )
28 })?;
29 Ok((block, self.committed_chain_tip.into(), protocol_config))
30 }
31}
32
33impl VerifyWith<&BlockHeader> for proto::miden::node::v1::DecodedBlockSubscriptionResponse {
34 type Verified = (SignedBlock, BlockNumber, Option<ProtocolConfig>);
35 type Error = ConversionError;
36
37 fn verify_with(self, parent: &BlockHeader) -> Result<Self::Verified, Self::Error> {
41 let block = self.block.verify_with(parent).context("block")?;
42 let protocol_config = self.protocol_config.try_map(|config| {
43 verify_protocol_config_commitment(
44 config.verify().map_err(ConversionError::new)?,
45 block.header(),
46 )
47 })?;
48 Ok((block, self.committed_chain_tip.into(), protocol_config))
49 }
50}
51
52#[derive(Debug, Clone, Error, PartialEq, Eq)]
53pub enum InvalidBlockRange {
54 #[error("start ({start}) greater than end ({end})")]
55 StartGreaterThanEnd { start: BlockNumber, end: BlockNumber },
56}
57
58impl Verify for proto::miden::node::v1::DecodedBlockRange {
59 type Verified = RangeInclusive<BlockNumber>;
60 type Error = InvalidBlockRange;
61
62 fn verify(self) -> Result<Self::Verified, Self::Error> {
68 let block_range = RangeInclusive::new(self.block_from.into(), self.block_to.into());
69
70 if block_range.start() > block_range.end() {
71 return Err(InvalidBlockRange::StartGreaterThanEnd {
72 start: *block_range.start(),
73 end: *block_range.end(),
74 });
75 }
76
77 Ok(block_range)
78 }
79}
80
81impl From<RangeInclusive<BlockNumber>> for proto::miden::node::v1::BlockRange {
82 fn from(range: RangeInclusive<BlockNumber>) -> Self {
83 Self {
84 block_from: range.start().as_u32(),
85 block_to: range.end().as_u32(),
86 }
87 }
88}
89
90#[cfg(test)]
91mod tests {
92
93 use super::*;
94
95 fn range(from: u32, to: u32) -> proto::miden::node::v1::DecodedBlockRange {
96 use crate::DecodeMessage;
97 proto::miden::node::v1::BlockRange { block_from: from, block_to: to }
98 .decode_fields()
99 .unwrap()
100 }
101
102 #[test]
103 fn verify_rejects_start_greater_than_end() {
104 let err = range(5, 4).verify().expect_err("inverted range must be rejected");
105 assert_eq!(
106 err,
107 InvalidBlockRange::StartGreaterThanEnd {
108 start: BlockNumber::from(5u32),
109 end: BlockNumber::from(4u32),
110 }
111 );
112 }
113
114 #[test]
115 fn verify_accepts_single_block() {
116 let got = range(7, 7).verify().expect("start == end is a valid inclusive range");
117 assert_eq!(*got.start(), BlockNumber::from(7u32));
118 assert_eq!(*got.end(), BlockNumber::from(7u32));
119 }
120
121 #[test]
122 fn verify_accepts_ascending_span() {
123 let got = range(1, 3).verify().expect("ascending range must be accepted");
124 assert_eq!(*got.start(), BlockNumber::from(1u32));
125 assert_eq!(*got.end(), BlockNumber::from(3u32));
126 }
127}