use alloc::string::ToString;
use super::ProtocolConfigError;
use crate::block::BlockNumber;
use crate::utils::serde::{
ByteReader,
ByteWriter,
Deserializable,
DeserializationError,
Serializable,
};
use crate::{Hasher, Word, ZERO};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NextProtocolConfig {
effective_from: BlockNumber,
protocol_config: Word,
}
impl NextProtocolConfig {
pub fn new(
effective_from: BlockNumber,
protocol_config: Word,
) -> Result<Self, ProtocolConfigError> {
if effective_from == BlockNumber::GENESIS {
return Err(ProtocolConfigError::NextConfigEffectiveAtGenesis);
}
Ok(Self { effective_from, protocol_config })
}
pub fn effective_from(&self) -> BlockNumber {
self.effective_from
}
pub fn protocol_config(&self) -> Word {
self.protocol_config
}
pub fn to_commitment(&self) -> Word {
let effective_from = Word::new([self.effective_from.into(), ZERO, ZERO, ZERO]);
Hasher::merge(&[effective_from, self.protocol_config])
}
}
impl Serializable for NextProtocolConfig {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
let Self { effective_from, protocol_config } = self;
effective_from.write_into(target);
protocol_config.write_into(target);
}
}
impl Deserializable for NextProtocolConfig {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let effective_from = source.read()?;
let protocol_config = source.read()?;
Self::new(effective_from, protocol_config)
.map_err(|err| DeserializationError::InvalidValue(err.to_string()))
}
}
#[cfg(test)]
mod tests {
use assert_matches::assert_matches;
use miden_crypto::rand::test_utils::rand_value;
use super::*;
#[test]
fn commitment_binds_both_fields() {
let config = rand_value::<Word>();
let next = NextProtocolConfig::new(BlockNumber::from(10u32), config).unwrap();
let other_block = NextProtocolConfig::new(BlockNumber::from(11u32), config).unwrap();
let other_config =
NextProtocolConfig::new(BlockNumber::from(10u32), rand_value::<Word>()).unwrap();
assert_ne!(next.to_commitment(), other_block.to_commitment());
assert_ne!(next.to_commitment(), other_config.to_commitment());
}
#[test]
fn new_rejects_genesis() {
let error = NextProtocolConfig::new(BlockNumber::GENESIS, Word::empty()).unwrap_err();
assert_matches!(error, ProtocolConfigError::NextConfigEffectiveAtGenesis);
}
#[test]
fn serde_round_trip() -> anyhow::Result<()> {
let next = NextProtocolConfig::new(BlockNumber::from(42u32), rand_value::<Word>())?;
let deserialized = NextProtocolConfig::read_from_bytes(&next.to_bytes())
.map_err(|err| anyhow::anyhow!("{err}"))?;
assert_eq!(next, deserialized);
Ok(())
}
}