use alloy::primitives::B256;
use newton_prover_core::state_commit_registry::IStateRootCommittable::StateCommit;
use crate::state_commit::{error::StateCommitError, registry_view::RegistryView};
pub const STATE_COMMIT_V1: u8 = 1;
#[allow(clippy::result_large_err)]
pub fn build_state_commit(
view: &RegistryView,
new_state_root: B256,
da_cert_hash: B256,
pcr0: B256,
now_ts: u64,
) -> Result<StateCommit, StateCommitError> {
if new_state_root == B256::ZERO {
return Err(StateCommitError::InvalidNewStateRoot);
}
if pcr0 == B256::ZERO {
return Err(StateCommitError::InvalidPcr0Commitment);
}
if now_ts <= view.last_commit_timestamp {
return Err(StateCommitError::TimestampRegression {
last: view.last_commit_timestamp,
got: now_ts,
});
}
Ok(StateCommit {
version: STATE_COMMIT_V1,
sequenceNo: view.sequence_no + 1,
prevStateRoot: view.state_root,
newStateRoot: new_state_root,
timestamp: now_ts,
daCertHash: da_cert_hash,
pcr0Commitment: pcr0,
})
}
#[cfg(test)]
mod tests {
use super::*;
fn b32(byte: u8) -> B256 {
B256::repeat_byte(byte)
}
fn baseline_view() -> RegistryView {
RegistryView {
sequence_no: 5,
state_root: b32(0xab),
last_commit_timestamp: 1_000,
}
}
#[test]
fn happy_path_constructs_commit_correctly() {
let view = baseline_view();
let new_root = b32(0xcd);
let da_cert = b32(0xef);
let pcr0 = b32(0x42);
let now_ts = 1_120;
let commit = build_state_commit(&view, new_root, da_cert, pcr0, now_ts).expect("happy path constructs commit");
assert_eq!(commit.version, STATE_COMMIT_V1);
assert_eq!(commit.sequenceNo, 6, "sequenceNo = view.sequence_no + 1");
assert_eq!(commit.prevStateRoot.as_slice(), view.state_root.as_slice());
assert_eq!(commit.newStateRoot.as_slice(), new_root.as_slice());
assert_eq!(commit.timestamp, now_ts);
assert_eq!(commit.daCertHash.as_slice(), da_cert.as_slice());
assert_eq!(commit.pcr0Commitment.as_slice(), pcr0.as_slice());
}
#[test]
fn zero_new_state_root_rejected_off_chain() {
let view = baseline_view();
let result = build_state_commit(&view, B256::ZERO, b32(0xef), b32(0x42), 1_120);
assert!(matches!(result, Err(StateCommitError::InvalidNewStateRoot)));
}
#[test]
fn zero_pcr0_rejected_off_chain() {
let view = baseline_view();
let result = build_state_commit(&view, b32(0xcd), b32(0xef), B256::ZERO, 1_120);
assert!(matches!(result, Err(StateCommitError::InvalidPcr0Commitment)));
}
#[test]
fn timestamp_equal_to_last_rejected_off_chain() {
let view = baseline_view();
let result = build_state_commit(&view, b32(0xcd), b32(0xef), b32(0x42), 1_000);
match result {
Err(StateCommitError::TimestampRegression { last, got }) => {
assert_eq!(last, 1_000);
assert_eq!(got, 1_000);
}
other => panic!("expected TimestampRegression, got {other:?}"),
}
}
#[test]
fn timestamp_in_past_rejected_off_chain() {
let view = baseline_view();
let result = build_state_commit(&view, b32(0xcd), b32(0xef), b32(0x42), 999);
match result {
Err(StateCommitError::TimestampRegression { last, got }) => {
assert_eq!(last, 1_000);
assert_eq!(got, 999);
}
other => panic!("expected TimestampRegression, got {other:?}"),
}
}
#[test]
fn invariant_check_order_zero_new_root_takes_priority_over_zero_pcr0() {
let view = baseline_view();
let result = build_state_commit(&view, B256::ZERO, b32(0xef), B256::ZERO, 1_120);
assert!(matches!(result, Err(StateCommitError::InvalidNewStateRoot)));
}
#[test]
fn invariant_check_order_zero_pcr0_takes_priority_over_timestamp_regression() {
let view = baseline_view();
let result = build_state_commit(&view, b32(0xcd), b32(0xef), B256::ZERO, 999);
assert!(matches!(result, Err(StateCommitError::InvalidPcr0Commitment)));
}
#[test]
fn sequence_no_increments_by_one_from_view() {
let view = RegistryView {
sequence_no: 99,
state_root: b32(0xab),
last_commit_timestamp: 0,
};
let commit = build_state_commit(&view, b32(0x01), b32(0x02), b32(0x03), 1).expect("happy path");
assert_eq!(commit.sequenceNo, 100);
}
#[test]
fn state_commit_v1_constant_is_one() {
assert_eq!(STATE_COMMIT_V1, 1u8);
}
}