minerva 0.2.0

Causal ordering for distributed systems
//! The endorsement frame's golden layout and typed refusals (S346;
//! PRD 0028 R7: the arrival round's first wire spelling).

use super::*;
use crate::metis::{Admission, Endorsement, EndorsementDecodeBudget, EndorsementDecodeError};

/// The fixture endorsement: two joiners over a founded base, an opaque
/// caller commitment.
fn endorsement() -> Endorsement {
    let admission = Admission::new(address(1, (2, 3)), [5, 9]).expect("two joiners are a boundary");
    Endorsement::from_parts(admission, [0xA5; 32])
}

/// The golden endorsement layout: every frame-owned byte pinned ---
/// version, base address, joiner count, joiners ascending, commitment ---
/// with the canonical bijection and the allocation-free preflight.
#[test]
fn test_endorsement_golden_layout() {
    let record = endorsement();
    let frame = record.to_bytes();

    let mut expected = vec![0x01];
    expected.extend_from_slice(&1u64.to_be_bytes()); // base generation
    expected.extend_from_slice(&2u32.to_be_bytes()); // base station
    expected.extend_from_slice(&3u64.to_be_bytes()); // base counter
    expected.extend_from_slice(&2u32.to_be_bytes()); // joiner count
    expected.extend_from_slice(&5u32.to_be_bytes());
    expected.extend_from_slice(&9u32.to_be_bytes());
    expected.extend_from_slice(&[0xA5; 32]);
    assert_eq!(frame, expected);
    assert_eq!(record.encoded_len(), frame.len());

    let decoded = Endorsement::from_bytes(&frame).expect("the golden frame decodes");
    assert_eq!(decoded, record);
    assert_eq!(decoded.to_bytes(), frame);
}

/// An unknown version byte refuses before any other work.
#[test]
fn test_endorsement_unknown_version_refuses() {
    let mut frame = endorsement().to_bytes();
    frame[0] = 0x7F;
    assert_eq!(
        Endorsement::from_bytes(&frame),
        Err(EndorsementDecodeError::UnknownVersion(0x7F))
    );
}

/// Trailing bytes refuse: `from_bytes` is the exact-frame door.
#[test]
fn test_endorsement_trailing_bytes_refuse() {
    let mut frame = endorsement().to_bytes();
    let exact = frame.len();
    frame.push(0);
    assert_eq!(
        Endorsement::from_bytes(&frame),
        Err(EndorsementDecodeError::UnexpectedLength {
            expected: exact,
            found: exact + 1
        })
    );
}

/// The base identity refusals ride the nominal type: a zero generation
/// and a zero declaration counter each refuse with the evidence
/// [`EpochAddress::try_from_parts`] states once.
#[test]
fn test_endorsement_zero_base_parts_refuse() {
    let frame = endorsement().to_bytes();
    let mut zeroed = frame.clone();
    for byte in &mut zeroed[1..9] {
        *byte = 0;
    }
    assert_eq!(
        Endorsement::from_bytes(&zeroed),
        Err(EndorsementDecodeError::Address(
            InvalidEpochAddress::ZeroGeneration
        ))
    );
    let mut zeroed = frame;
    for byte in &mut zeroed[13..21] {
        *byte = 0;
    }
    assert_eq!(
        Endorsement::from_bytes(&zeroed),
        Err(EndorsementDecodeError::Address(
            InvalidEpochAddress::ZeroDeclarationCounter { station: 2 }
        ))
    );
}

/// A declared joiner count of zero refuses as the empty admission: the
/// same set [`Admission::new`] refuses at the minting door.
#[test]
fn test_endorsement_empty_admission_refuses() {
    let populated = endorsement().to_bytes();
    let mut frame = populated[..25].to_vec();
    frame[21..25].copy_from_slice(&0u32.to_be_bytes());
    frame.extend_from_slice(&populated[33..]);
    assert_eq!(frame.len(), 57);
    assert_eq!(
        Endorsement::from_bytes(&frame),
        Err(EndorsementDecodeError::EmptyAdmission)
    );
}

/// The canonical joiner order refuses descent and duplication, each with
/// both joiners in evidence.
#[test]
fn test_endorsement_non_ascending_joiners_refuse() {
    let frame = endorsement().to_bytes();
    let mut swapped = frame.clone();
    swapped[25..29].copy_from_slice(&9u32.to_be_bytes());
    swapped[29..33].copy_from_slice(&5u32.to_be_bytes());
    assert_eq!(
        Endorsement::from_bytes(&swapped),
        Err(EndorsementDecodeError::NonAscendingJoiners {
            previous: 9,
            found: 5
        })
    );
    let mut doubled = frame;
    doubled[29..33].copy_from_slice(&5u32.to_be_bytes());
    assert_eq!(
        Endorsement::from_bytes(&doubled),
        Err(EndorsementDecodeError::NonAscendingJoiners {
            previous: 5,
            found: 5
        })
    );
}

/// The joiner ceiling refuses past the caller's budget and admits at it;
/// the default ceiling admits any plausible boundary.
#[test]
fn test_endorsement_budget_refusals() {
    let record = endorsement();
    let frame = record.to_bytes();
    assert_eq!(
        Endorsement::from_bytes_with_budget(&frame, EndorsementDecodeBudget::new(2)),
        Ok(record.clone())
    );
    assert_eq!(
        Endorsement::from_bytes_with_budget(&frame, EndorsementDecodeBudget::default_ceiling()),
        Ok(record)
    );
    assert_eq!(
        Endorsement::from_bytes_with_budget(&frame, EndorsementDecodeBudget::new(1)),
        Err(EndorsementDecodeError::TooManyJoiners {
            count: 2,
            budget: 1
        })
    );
}

/// Count preflight is `O(1)`: an impossible count refuses on arithmetic
/// over the input length, before any allocation, and ahead of the budget
/// (a lying count is a length lie, not a wide boundary).
#[test]
fn test_endorsement_impossible_count_refuses_in_o1() {
    let mut frame = endorsement().to_bytes();
    frame[21..25].copy_from_slice(&u32::MAX.to_be_bytes());
    let floor = u64::from(u32::MAX) * 4 + 32;
    let expected = usize::try_from(25u64.saturating_add(floor)).unwrap_or(usize::MAX);
    assert_eq!(
        Endorsement::from_bytes(&frame),
        Err(EndorsementDecodeError::UnexpectedLength {
            expected,
            found: frame.len()
        })
    );
    // A merely short frame states the exact bytes the count owes.
    let mut short = endorsement().to_bytes();
    let _ = short.pop();
    assert_eq!(
        Endorsement::from_bytes(&short),
        Err(EndorsementDecodeError::UnexpectedLength {
            expected: 25 + 2 * 4 + 32,
            found: short.len()
        })
    );
}

/// Input below the fixed fields refuses with the structural floor.
#[test]
fn test_endorsement_short_input_refuses() {
    let frame = endorsement().to_bytes();
    assert_eq!(
        Endorsement::from_bytes(&frame[..40]),
        Err(EndorsementDecodeError::UnexpectedLength {
            expected: 57,
            found: 40
        })
    );
}

/// Station zero is accepted at every seat of the frame --- base and
/// joiners --- and re-encodes identically. The R-91 division, pinned
/// from Minerva's side: this codec carries the crate's full station
/// domain, and the nonzero-station rule stays the consumer's
/// authenticated-boundary law. A future Minerva that refuses here has
/// moved that law without a joint act, and this pin says so.
#[test]
fn test_endorsement_station_zero_is_the_consumers_law() {
    let admission = Admission::new(address(1, (0, 3)), [0, 7]).expect("a boundary");
    let record = Endorsement::from_parts(admission, [0x11; 32]);
    let frame = record.to_bytes();
    let decoded = Endorsement::from_bytes(&frame).expect("station zero decodes");
    assert_eq!(decoded, record);
    assert_eq!(decoded.to_bytes(), frame);
}