raiv 0.0.2

Bounded reproducible checkpoints for agent behavioral continuity.
Documentation
use super::*;
use crate::command::CommandSpec;
use crate::format::oracle::{encode as bytes, map_bytes};
use crate::limits::Limits;
use alloc::{collections::BTreeMap, vec, vec::Vec};
use ciborium::value::Value as Cbor;
use serde_json::Value;
use sha2::{Digest, Sha256};

fn hex(text: &str) -> Vec<u8> {
    (0..text.len())
        .step_by(2)
        .map(|i| u8::from_str_radix(&text[i..i + 2], 16).unwrap())
        .collect()
}

fn id(value: &Value) -> Id32 {
    hex(value.as_str().unwrap()).try_into().unwrap()
}

fn fixtures() -> Value {
    serde_json::from_str(include_str!("../../../fixtures/format/identity.json")).unwrap()
}

fn command_fixture(index: usize) -> Command {
    let fixtures: Value =
        serde_json::from_str(include_str!("../../../fixtures/format/command.json")).unwrap();
    Command::decode(
        &hex(fixtures[index]["hex"].as_str().unwrap()),
        &Limits::default(),
    )
    .unwrap()
}

fn oracle(fixture: &Value) -> Vec<u8> {
    let domain = Cbor::Bytes(id(&fixture["execution_domain"]).to_vec());
    let tree = match fixture["kind"].as_str().unwrap() {
        "episode" => Cbor::Array(vec![
            Cbor::Text("raiv/episode/1".into()),
            domain,
            Cbor::Bytes(id(&fixture["agent_id"]).to_vec()),
            Cbor::Bytes(id(&fixture["trigger_event_id"]).to_vec()),
        ]),
        "action" => Cbor::Array(vec![
            Cbor::Text("raiv/action/1".into()),
            domain,
            Cbor::Bytes(id(&fixture["agent_id"]).to_vec()),
            Cbor::Bytes(id(&fixture["episode_id"]).to_vec()),
            Cbor::Integer(fixture["action_index"].as_u64().unwrap().into()),
        ]),
        "command" => {
            let command = command_fixture(fixture["command_fixture"].as_u64().unwrap() as usize);
            let tree: Cbor = ciborium::de::from_reader(command.as_bytes()).unwrap();
            Cbor::Array(vec![Cbor::Text("raiv/command/1".into()), domain, tree])
        }
        _ => panic!("unknown identity fixture"),
    };
    bytes(&tree)
}

#[test]
fn all_identity_domains_match_pinned_independent_preimages_and_hashes() {
    let fixtures = fixtures();
    for fixture in fixtures.as_array().unwrap() {
        let preimage = hex(fixture["preimage"].as_str().unwrap());
        let expected = id(&fixture["digest"]);
        assert_eq!(oracle(fixture), preimage);
        assert_eq!(&Sha256::digest(&preimage)[..], expected);
        let domain = id(&fixture["execution_domain"]);
        let actual = match fixture["kind"].as_str().unwrap() {
            "episode" => episode_id(
                domain,
                id(&fixture["agent_id"]),
                id(&fixture["trigger_event_id"]),
            ),
            "action" => action_id(
                domain,
                id(&fixture["agent_id"]),
                id(&fixture["episode_id"]),
                fixture["action_index"].as_u64().unwrap(),
            ),
            "command" => command_digest(
                domain,
                &command_fixture(fixture["command_fixture"].as_u64().unwrap() as usize),
            ),
            _ => unreachable!(),
        };
        assert_eq!(actual, expected);
        if fixture["kind"] == "command" {
            let command = command_fixture(fixture["command_fixture"].as_u64().unwrap() as usize);
            let view = crate::command::CommandView::decode(command.as_bytes(), &Limits::default())
                .unwrap();
            assert_eq!(command_digest_view(domain, view), expected);
        }
    }
}

#[test]
fn episode_and_action_ids_bind_every_scalar_and_full_width_slot_index() {
    let base = episode_id([1; 32], [2; 32], [3; 32]);
    for changed in [
        episode_id([4; 32], [2; 32], [3; 32]),
        episode_id([1; 32], [4; 32], [3; 32]),
        episode_id([1; 32], [2; 32], [4; 32]),
    ] {
        assert_ne!(base, changed);
    }
    let base = action_id([1; 32], [2; 32], [3; 32], 0);
    for changed in [
        action_id([4; 32], [2; 32], [3; 32], 0),
        action_id([1; 32], [4; 32], [3; 32], 0),
        action_id([1; 32], [2; 32], [4; 32], 0),
        action_id([1; 32], [2; 32], [3; 32], 1),
        action_id([1; 32], [2; 32], [3; 32], u64::MAX),
    ] {
        assert_ne!(base, changed);
    }
    assert_ne!(episode_id([1; 32], [2; 32], [3; 32]), base);
    for index in [
        0,
        23,
        24,
        255,
        256,
        65_535,
        65_536,
        4_294_967_295,
        4_294_967_296,
        u64::MAX,
    ] {
        let fixture = serde_json::json!({"kind":"action","execution_domain":"01".repeat(32),"agent_id":"02".repeat(32),"episode_id":"03".repeat(32),"action_index":index});
        let expected: Id32 = Sha256::digest(oracle(&fixture)).into();
        assert_eq!(action_id([1; 32], [2; 32], [3; 32], index), expected);
    }
}

#[test]
fn command_digest_binds_exact_record_and_domain_but_preserves_the_logical_slot() {
    let original = command_fixture(0);
    let slot = original.action_id();
    let base = command_digest([1; 32], &original);
    assert_ne!(base, command_digest([2; 32], &original));
    let arguments = hex("a161616131");
    let mut spec = CommandSpec {
        action_id: slot,
        tool_id: original.tool_id(),
        tool_version: original.tool_version(),
        authority_binding: original.authority_binding(),
        arguments: original.arguments_bytes(),
    };
    for changed in 0..4 {
        let mut input = spec;
        match changed {
            0 => input.tool_id = "different",
            1 => input.tool_version = u64::MAX,
            2 => input.authority_binding = [3; 32],
            _ => input.arguments = &arguments,
        }
        let competing = Command::encode(&input, &Limits::default()).unwrap();
        assert_eq!(competing.action_id(), slot);
        assert_ne!(command_digest([1; 32], &competing), base);
    }
    spec.action_id = [4; 32];
    let another = Command::encode(&spec, &Limits::default()).unwrap();
    assert_ne!(command_digest([1; 32], &another), base);
    assert_eq!(command_digest([1; 32], &original), base);
}

#[test]
fn large_valid_command_tail_is_streamed_without_a_second_encoded_preimage() {
    let mut arguments = BTreeMap::new();
    arguments.insert("note".into(), Cbor::Text("a".repeat(4096)));
    let arguments = map_bytes(arguments);
    let limits = Limits {
        max_command_bytes: 8192,
        ..Limits::default()
    };
    let command = Command::encode(
        &CommandSpec {
            action_id: [1; 32],
            tool_id: "trade",
            tool_version: 1,
            authority_binding: [2; 32],
            arguments: &arguments,
        },
        &limits,
    )
    .unwrap();
    let tree: Cbor = ciborium::de::from_reader(command.as_bytes()).unwrap();
    let preimage = bytes(&Cbor::Array(vec![
        Cbor::Text("raiv/command/1".into()),
        Cbor::Bytes(vec![3; 32]),
        tree,
    ]));
    let expected: Id32 = Sha256::digest(preimage).into();
    assert_eq!(command_digest([3; 32], &command), expected);
}