minerva 0.2.0

Causal ordering for distributed systems
use proptest::prelude::*;

use crate::metis::{DotSet, HaveSetDecodeBudget, HaveSetDecodeError};

const HEADER_PREFIX_LEN: usize = 41;
const HEADER_LEN: usize = HEADER_PREFIX_LEN + size_of::<u32>();
const SIGNATURE_LEN: usize = 64;

fn decode_possession_have_set(
    bytes: &[u8],
    budget: HaveSetDecodeBudget,
) -> Option<Result<DotSet, HaveSetDecodeError>> {
    let encoded_len = bytes.get(HEADER_PREFIX_LEN..HEADER_LEN)?;
    let have_len = usize::try_from(u32::from_be_bytes(encoded_len.try_into().ok()?)).ok()?;
    let expected = HEADER_LEN
        .checked_add(have_len)?
        .checked_add(SIGNATURE_LEN)?;
    if expected != bytes.len() {
        return None;
    }
    Some(DotSet::from_bytes_with_budget(
        &bytes[HEADER_LEN..HEADER_LEN + have_len],
        budget,
    ))
}

proptest! {
    /// The Minerva seam inside a Polis possession envelope is total under the
    /// receiver's exception ceiling and preserves canonical byte identity.
    #[test]
    fn prop_polis_possession_have_set_never_panics(
        header in prop::collection::vec(any::<u8>(), HEADER_PREFIX_LEN..=HEADER_PREFIX_LEN),
        have_bytes in prop::collection::vec(any::<u8>(), 0..192),
        signature in prop::collection::vec(any::<u8>(), SIGNATURE_LEN..=SIGNATURE_LEN),
        max_exceptions in 0usize..=256,
    ) {
        let mut envelope = header;
        envelope.extend_from_slice(
            &u32::try_from(have_bytes.len()).expect("generated frame length fits").to_be_bytes(),
        );
        envelope.extend_from_slice(&have_bytes);
        envelope.extend_from_slice(&signature);

        if let Ok(set) = decode_possession_have_set(
            &envelope,
            HaveSetDecodeBudget::new(max_exceptions),
        ).expect("generated outer length is exact") {
            let reencoded = set.to_bytes();
            prop_assert_eq!(reencoded.as_slice(), have_bytes.as_slice());
        }
    }
}