use std::ops::RangeInclusive;
use miden_protobuf::{BuildUnchecked, ConversionResultExt, Verify, VerifyWith};
use miden_protocol::block::{BlockHeader, BlockNumber, SignedBlock};
use miden_protocol::protocol_config::ProtocolConfig;
use thiserror::Error;
use super::protocol_config::verify_protocol_config_commitment;
use crate::errors::ConversionError;
use crate::generated as proto;
impl BuildUnchecked for proto::miden::node::v1::DecodedBlockSubscriptionResponse {
type Output = (SignedBlock, BlockNumber, Option<ProtocolConfig>);
type Error = ConversionError;
fn build_unchecked(self) -> Result<Self::Output, Self::Error> {
let block = self.block.build_unchecked().context("block")?;
let protocol_config = self.protocol_config.try_map(|config| {
verify_protocol_config_commitment(
config.verify().map_err(ConversionError::new)?,
block.header(),
)
})?;
Ok((block, self.committed_chain_tip.into(), protocol_config))
}
}
impl VerifyWith<&BlockHeader> for proto::miden::node::v1::DecodedBlockSubscriptionResponse {
type Verified = (SignedBlock, BlockNumber, Option<ProtocolConfig>);
type Error = ConversionError;
fn verify_with(self, parent: &BlockHeader) -> Result<Self::Verified, Self::Error> {
let block = self.block.verify_with(parent).context("block")?;
let protocol_config = self.protocol_config.try_map(|config| {
verify_protocol_config_commitment(
config.verify().map_err(ConversionError::new)?,
block.header(),
)
})?;
Ok((block, self.committed_chain_tip.into(), protocol_config))
}
}
#[derive(Debug, Clone, Error, PartialEq, Eq)]
pub enum InvalidBlockRange {
#[error("start ({start}) greater than end ({end})")]
StartGreaterThanEnd { start: BlockNumber, end: BlockNumber },
}
impl Verify for proto::miden::node::v1::DecodedBlockRange {
type Verified = RangeInclusive<BlockNumber>;
type Error = InvalidBlockRange;
fn verify(self) -> Result<Self::Verified, Self::Error> {
let block_range = RangeInclusive::new(self.block_from.into(), self.block_to.into());
if block_range.start() > block_range.end() {
return Err(InvalidBlockRange::StartGreaterThanEnd {
start: *block_range.start(),
end: *block_range.end(),
});
}
Ok(block_range)
}
}
impl From<RangeInclusive<BlockNumber>> for proto::miden::node::v1::BlockRange {
fn from(range: RangeInclusive<BlockNumber>) -> Self {
Self {
block_from: range.start().as_u32(),
block_to: range.end().as_u32(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn range(from: u32, to: u32) -> proto::miden::node::v1::DecodedBlockRange {
use crate::DecodeMessage;
proto::miden::node::v1::BlockRange { block_from: from, block_to: to }
.decode_fields()
.unwrap()
}
#[test]
fn verify_rejects_start_greater_than_end() {
let err = range(5, 4).verify().expect_err("inverted range must be rejected");
assert_eq!(
err,
InvalidBlockRange::StartGreaterThanEnd {
start: BlockNumber::from(5u32),
end: BlockNumber::from(4u32),
}
);
}
#[test]
fn verify_accepts_single_block() {
let got = range(7, 7).verify().expect("start == end is a valid inclusive range");
assert_eq!(*got.start(), BlockNumber::from(7u32));
assert_eq!(*got.end(), BlockNumber::from(7u32));
}
#[test]
fn verify_accepts_ascending_span() {
let got = range(1, 3).verify().expect("ascending range must be accepted");
assert_eq!(*got.start(), BlockNumber::from(1u32));
assert_eq!(*got.end(), BlockNumber::from(3u32));
}
}