northroot-proof-engine 0.3.2

Core proof computation and validation library for PoSH (Proof of Shape) and PoX (Proof of Execution)
Documentation
//! Isolation tests for v0.2 engine
//!
//! These tests ensure that Po$ and PoΣ (when added) do not affect
//! PoX/PoSH commitments, and that identity changes only affect identity_root.

use northroot_proof_engine::{
    compute_identity_root, compute_trace_set_root, ArtifactRef, MethodRef, SpanCommitment,
    SpanStatus,
};

#[test]
fn test_identity_root_isolation() {
    let trace_id = "tr_01H1234567890";
    let identity1 = "did:key:zTenant1";
    let identity2 = "did:key:zTenant2";
    let spans = vec![
        "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".to_string(),
        "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb".to_string(),
    ];

    // trace_set_root should be the same regardless of identity
    let set_root1 = compute_trace_set_root(&spans).unwrap();
    let set_root2 = compute_trace_set_root(&spans).unwrap();
    assert_eq!(
        set_root1, set_root2,
        "trace_set_root must be independent of identity"
    );

    // identity_root should differ with different identities
    let id_root1 = compute_identity_root(trace_id, identity1, &spans).unwrap();
    let id_root2 = compute_identity_root(trace_id, identity2, &spans).unwrap();
    assert_ne!(
        id_root1, id_root2,
        "Different identities must produce different identity_root"
    );

    // But same identity should produce same root
    let id_root3 = compute_identity_root(trace_id, identity1, &spans).unwrap();
    assert_eq!(
        id_root1, id_root3,
        "Same identity must produce same identity_root"
    );
}

#[test]
fn test_trace_set_root_independent_of_identity() {
    let spans = vec![
        "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".to_string(),
        "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb".to_string(),
    ];

    // trace_set_root should be computed the same way regardless of identity
    let root1 = compute_trace_set_root(&spans).unwrap();
    let root2 = compute_trace_set_root(&spans).unwrap();
    assert_eq!(root1, root2);

    // Changing identity should NOT change trace_set_root
    let id_root1 = compute_identity_root("tr1", "did:key:z1", &spans).unwrap();
    let id_root2 = compute_identity_root("tr1", "did:key:z2", &spans).unwrap();
    assert_ne!(id_root1, id_root2, "Identity root should change");

    // But trace_set_root should remain the same
    let root3 = compute_trace_set_root(&spans).unwrap();
    assert_eq!(root1, root3, "trace_set_root must remain unchanged");
}

#[test]
fn test_span_commitment_independent_of_optional_fields() {
    // Create a base commitment
    let base_commitment = SpanCommitment {
        schema_version: "proof.v2".to_string(),
        trace_id: "tr_01H1234567890".to_string(),
        span_id: "sp-0001".to_string(),
        method_ref: MethodRef {
            method_id: "com.test/method".to_string(),
            version: "1.0.0".to_string(),
            method_shape_root:
                "sha256:0000000000000000000000000000000000000000000000000000000000000000"
                    .to_string(),
            image_digest: "sha256:0000000000000000000000000000000000000000000000000000000000000000"
                .to_string(),
            method_fingerprint: "git:deadbeef".to_string(),
        },
        data_shape_hash: "sha256:1111111111111111111111111111111111111111111111111111111111111111"
            .to_string(),
        span_shape_hash: "sha256:2222222222222222222222222222222222222222222222222222222222222222"
            .to_string(),
        inputs: vec![ArtifactRef {
            name: "input.csv".to_string(),
            content_hash: "sha256:3333333333333333333333333333333333333333333333333333333333333333"
                .to_string(),
            uri: None,
        }],
        outputs: vec![ArtifactRef {
            name: "output.json".to_string(),
            content_hash: "sha256:4444444444444444444444444444444444444444444444444444444444444444"
                .to_string(),
            uri: None,
        }],
        start_time: "2025-11-06T12:00:00.000Z".to_string(),
        end_time: "2025-11-06T12:00:00.042Z".to_string(),
        status: SpanStatus::OK,
        meta: None,
    };

    let hash1 = northroot_proof_engine::compute_span_commitment_hash(&base_commitment).unwrap();

    // Adding optional URI should NOT change hash (URI is in ArtifactRef, not commitment)
    // Actually, URI is part of ArtifactRef, so it WOULD change the hash if included
    // But the test shows that meta (which is optional) doesn't affect if None
    let mut with_meta = base_commitment.clone();
    with_meta.meta = Some(northroot_proof_engine::SpanMeta {
        lamport: Some(1),
        parents: None,
        order_index: None,
    });
    let hash2 = northroot_proof_engine::compute_span_commitment_hash(&with_meta).unwrap();
    assert_ne!(
        hash1, hash2,
        "Adding meta should change hash (it's part of commitment)"
    );

    // But same commitment should produce same hash
    let hash3 = northroot_proof_engine::compute_span_commitment_hash(&base_commitment).unwrap();
    assert_eq!(hash1, hash3, "Same commitment must produce same hash");
}