ccf-core 1.0.0

Canonical CCF core v1 trust-update and runtime-certificate primitives for filed-Prov6 contract enforcement
Documentation
use ccf_core::context::{
    apply_active_context_update, reject_cross_context_normalization_leakage,
    reject_global_shared_timestamp, ContextAccumulator, ContextUpdateTick,
    PerContextAccumulatorStore, CONTEXT_ISOLATION_JOURNEY_ID, CONTEXT_ISOLATION_STORY_ID,
    CROSS_CONTEXT_NORMALIZATION_OLD_WRONG_PATH, GLOBAL_SHARED_TIMESTAMP_OLD_WRONG_PATH,
};
use ccf_core::qac::Matrix3;

const TEST_SOURCE: &str = include_str!("per_context_isolation.rs");
const CONTRACT_SOURCE: &str =
    include_str!("../../../docs2/contracts/ccf-core-v1/journey_context_isolation.yml");
const ACTIVE_CONTEXT: &str = "K1";
const INACTIVE_CONTEXT: &str = "K2";
const FNV_OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
const EXPECTED_K1_UPDATE_COUNT_BEFORE: u64 = 3;
const EXPECTED_K1_UPDATE_COUNT_AFTER: u64 = 4;
const EXPECTED_K2_UPDATE_COUNT: u64 = 11;
const EXPECTED_K1_TIMESTAMP_BEFORE: u64 = 1_781_755_200_000;
const EXPECTED_K1_TIMESTAMP_AFTER: u64 = 1_781_758_800_000;
const EXPECTED_K2_TIMESTAMP: u64 = 1_781_751_600_000;
const EXPECTED_K1_BEFORE_HASH: u64 = 0x1d6a_07df_fdce_bb25;
const EXPECTED_K1_AFTER_HASH: u64 = 0x8e39_d71b_4685_80d4;
const EXPECTED_K2_HASH: u64 = 0xe6ad_e95f_9142_b7b5;

fn k1_state() -> Matrix3 {
    Matrix3::new([[0.82, 1.15, 0.64], [1.04, 0.91, 1.33], [0.77, 1.28, 1.09]])
}

fn k1_reference() -> Matrix3 {
    Matrix3::new([[1.21, 0.73, 1.08], [0.88, 1.42, 0.96], [1.31, 0.69, 1.17]])
}

fn k2_state() -> Matrix3 {
    Matrix3::new([[1.32, 0.86, 1.11], [0.93, 1.27, 0.72], [1.08, 0.99, 1.44]])
}

fn fixture_store() -> PerContextAccumulatorStore {
    PerContextAccumulatorStore {
        first: ContextAccumulator {
            context_id: ACTIVE_CONTEXT,
            state: k1_state(),
            update_count: EXPECTED_K1_UPDATE_COUNT_BEFORE,
            last_update_unix_ms: EXPECTED_K1_TIMESTAMP_BEFORE,
        },
        second: ContextAccumulator {
            context_id: INACTIVE_CONTEXT,
            state: k2_state(),
            update_count: EXPECTED_K2_UPDATE_COUNT,
            last_update_unix_ms: EXPECTED_K2_TIMESTAMP,
        },
    }
}

fn active_tick() -> ContextUpdateTick {
    ContextUpdateTick {
        context_id: ACTIVE_CONTEXT,
        reference_state: k1_reference(),
        left_normalization: [1.10, 0.90, 1.05],
        right_normalization: [0.95, 1.20, 0.85],
        alpha_t: 0.35,
        wall_clock_unix_ms: EXPECTED_K1_TIMESTAMP_AFTER,
    }
}

fn matrix_hash(matrix: Matrix3) -> u64 {
    let mut hash = FNV_OFFSET;
    for row in matrix.entries {
        for value in row {
            for byte in value.to_bits().to_le_bytes() {
                hash ^= byte as u64;
                hash = hash.wrapping_mul(FNV_PRIME);
            }
        }
    }
    hash
}

#[test]
fn active_context_update_changes_only_k1() {
    eprintln!("journey={CONTEXT_ISOLATION_JOURNEY_ID} story={CONTEXT_ISOLATION_STORY_ID}");

    let mut store = fixture_store();
    let inactive_baseline = store.second;
    let active_hash_before = matrix_hash(store.first.state);
    let inactive_hash_before = matrix_hash(inactive_baseline.state);

    assert_eq!(active_hash_before, EXPECTED_K1_BEFORE_HASH);
    assert_eq!(inactive_hash_before, EXPECTED_K2_HASH);
    assert_eq!(store.first.update_count, EXPECTED_K1_UPDATE_COUNT_BEFORE);
    assert_eq!(
        store.first.last_update_unix_ms,
        EXPECTED_K1_TIMESTAMP_BEFORE
    );
    assert_eq!(store.second.update_count, EXPECTED_K2_UPDATE_COUNT);
    assert_eq!(store.second.last_update_unix_ms, EXPECTED_K2_TIMESTAMP);

    let report = apply_active_context_update(&mut store, &active_tick())
        .expect("per-context isolation implementation is required");

    assert_eq!(report.active_context, ACTIVE_CONTEXT);
    assert_eq!(report.inactive_context, INACTIVE_CONTEXT);
    assert_eq!(report.active_before.state_hash, EXPECTED_K1_BEFORE_HASH);
    assert_eq!(
        report.active_before.update_count,
        EXPECTED_K1_UPDATE_COUNT_BEFORE
    );
    assert_eq!(
        report.active_before.last_update_unix_ms,
        EXPECTED_K1_TIMESTAMP_BEFORE
    );
    assert_eq!(report.active_after.state_hash, EXPECTED_K1_AFTER_HASH);
    assert_eq!(
        report.active_after.update_count,
        EXPECTED_K1_UPDATE_COUNT_AFTER
    );
    assert_eq!(
        report.active_after.last_update_unix_ms,
        EXPECTED_K1_TIMESTAMP_AFTER
    );
    assert_eq!(report.inactive_before.state_hash, EXPECTED_K2_HASH);
    assert_eq!(report.inactive_after.state_hash, EXPECTED_K2_HASH);
    assert_eq!(report.inactive_after.state, inactive_baseline.state);
    assert_eq!(report.inactive_after.update_count, EXPECTED_K2_UPDATE_COUNT);
    assert_eq!(
        report.inactive_after.last_update_unix_ms,
        EXPECTED_K2_TIMESTAMP
    );
    assert_eq!(store.second, inactive_baseline);
    eprintln!(
        "active_context={} inactive_context={} k1_state_hash_before=0x{:016x} k1_state_hash_after=0x{:016x} k2_state_hash_before=0x{:016x} k2_state_hash_after=0x{:016x} k1_update_count_changed=PASS k1_timestamp_changed=PASS k1_state_hash_changed=PASS k2_state_byte_equal=PASS k2_timestamp_unchanged=PASS k2_update_count_unchanged=PASS active_context_only=PASS no_global_normalization=PASS",
        report.active_context,
        report.inactive_context,
        report.active_before.state_hash,
        report.active_after.state_hash,
        report.inactive_before.state_hash,
        report.inactive_after.state_hash
    );
}

#[test]
fn global_shared_timestamp_old_wrong_path_is_rejected() {
    eprintln!("journey={CONTEXT_ISOLATION_JOURNEY_ID} story={CONTEXT_ISOLATION_STORY_ID}");

    let report = reject_global_shared_timestamp()
        .expect("global/shared timestamp negative guard is required");

    assert_eq!(
        report.old_wrong_path,
        GLOBAL_SHARED_TIMESTAMP_OLD_WRONG_PATH
    );
    assert!(report.rejected);
    eprintln!(
        "global_shared_timestamp_negative=PASS old_wrong_path={}",
        report.old_wrong_path
    );
}

#[test]
fn cross_context_normalization_old_wrong_path_is_rejected() {
    eprintln!("journey={CONTEXT_ISOLATION_JOURNEY_ID} story={CONTEXT_ISOLATION_STORY_ID}");

    let report = reject_cross_context_normalization_leakage()
        .expect("cross-context normalization negative guard is required");

    assert_eq!(
        report.old_wrong_path,
        CROSS_CONTEXT_NORMALIZATION_OLD_WRONG_PATH
    );
    assert!(report.rejected);
    eprintln!(
        "cross_context_normalization_negative=PASS old_wrong_path={}",
        report.old_wrong_path
    );
}

#[test]
fn context_isolation_gate_has_no_silent_skip_marker() {
    eprintln!("journey={CONTEXT_ISOLATION_JOURNEY_ID} story={CONTEXT_ISOLATION_STORY_ID}");

    for required_token in [
        "cargo test -p ccf-core --test per_context_isolation -- --nocapture",
        "active_context=K1",
        "inactive_context=K2",
        "k1_update_count_changed=PASS",
        "k2_state_byte_equal=PASS",
        "global_shared_timestamp_negative=PASS",
        "cross_context_normalization_negative=PASS",
    ] {
        assert!(
            CONTRACT_SOURCE.contains(required_token),
            "context isolation contract missing token {required_token}"
        );
    }

    let skip_needles = [
        concat!("test", ".skip"),
        concat!("describe", ".skip"),
        concat!("it", ".skip"),
        concat!("#[", "ignore]"),
        concat!("status:", " skipped"),
    ];

    for source in [TEST_SOURCE, CONTRACT_SOURCE] {
        for needle in skip_needles {
            assert!(
                !source.contains(needle),
                "context isolation gate must not contain silent skip marker {needle}"
            );
        }
    }
    eprintln!("context_isolation_skip_scan=PASS");
}