use lex_ast::canonicalize_program;
use lex_store::{Operation, OperationKind, StageTransition, Store, StoreError, DEFAULT_BRANCH};
use lex_syntax::parse_source;
use lex_vcs::{
Attestation, AttestationKind, AttestationResult, ProducerDescriptor,
};
use std::collections::BTreeSet;
fn fresh() -> (Store, tempfile::TempDir) {
let tmp = tempfile::tempdir().unwrap();
let s = Store::open(tmp.path()).unwrap();
(s, tmp)
}
fn parse(src: &str) -> Vec<lex_ast::Stage> {
let prog = parse_source(src).expect("parse");
canonicalize_program(&prog)
}
fn pure_op(sig: &str, stg: &str) -> (Operation, StageTransition) {
(
Operation::new(
OperationKind::AddFunction {
sig_id: sig.into(),
stage_id: stg.into(),
effects: BTreeSet::new(),
budget_cost: None,
},
[],
),
StageTransition::Create {
sig_id: sig.into(),
stage_id: stg.into(),
},
)
}
fn modify_op(sig: &str, parent: &lex_vcs::OpId, from: &str, to: &str)
-> (Operation, StageTransition)
{
(
Operation::new(
OperationKind::ModifyBody {
sig_id: sig.into(),
from_stage_id: from.into(),
to_stage_id: to.into(),
from_budget: None,
to_budget: None,
},
[parent.clone()],
),
StageTransition::Replace {
sig_id: sig.into(),
from: from.into(),
to: to.into(),
},
)
}
fn pure_candidate(name: &str) -> Vec<lex_ast::Stage> {
parse(&format!("fn {name}(n :: Int) -> Int {{ n }}\n"))
}
#[test]
fn walk_back_gate_refuses_advance_when_ancestor_is_contaminated() {
let (s, _tmp) = fresh();
let (op_a, t_a) = pure_op("fac", "stg-1");
s.apply_operation_checked(DEFAULT_BRANCH, op_a, t_a, &pure_candidate("fac"))
.expect("clean publish of A1");
let log = s.attestation_log().unwrap();
let head_after_a = s.get_branch(DEFAULT_BRANCH).unwrap().unwrap().head_op.clone();
let spec_att = Attestation::with_timestamp(
"stg-1".to_string(),
head_after_a.clone(),
None,
AttestationKind::Spec {
spec_id: "fac-spec".into(),
method: lex_vcs::SpecMethod::Random,
trials: Some(100),
},
AttestationResult::Passed,
ProducerDescriptor {
tool: "tool-X".into(),
version: "1.0".into(),
model: None,
},
None,
100,
);
log.put(&spec_att).unwrap();
let head_a = head_after_a.clone().unwrap();
let (op_b, t_b) = modify_op("fac", &head_a, "stg-1", "stg-2");
s.apply_operation_checked(DEFAULT_BRANCH, op_b, t_b, &pure_candidate("fac"))
.expect("clean publish of B");
let block_att = Attestation::with_timestamp(
"tool-X".to_string(),
None,
None,
AttestationKind::ProducerBlock {
tool_id: "tool-X".into(),
reason: "compromised".into(),
blocked_at: 50, },
AttestationResult::Passed,
ProducerDescriptor {
tool: "lex attest retro-block".into(),
version: "0".into(),
model: None,
},
None,
2000,
);
log.put(&block_att).unwrap();
let invalidated = s.invalidate_gate_checkpoints().unwrap();
assert_eq!(invalidated, 1, "main's checkpoint should have been cleared");
let head_b = s.get_branch(DEFAULT_BRANCH).unwrap().unwrap().head_op.clone().unwrap();
let (op_c, t_c) = modify_op("fac", &head_b, "stg-2", "stg-3");
let err = s.apply_operation_checked(DEFAULT_BRANCH, op_c, t_c, &pure_candidate("fac"))
.expect_err("post-retro-block advance must refuse");
let blocked = match err {
StoreError::ProducerBlocked(b) => b,
other => panic!("expected ProducerBlocked, got {other:?}"),
};
assert_eq!(blocked.tool_id, "tool-X");
assert_eq!(blocked.stage_id, "stg-1",
"the contamination is on stg-1 (an ancestor), not the new op's stage");
}
#[test]
fn walk_back_skipped_when_checkpoint_equals_head() {
let (s, _tmp) = fresh();
let (op_a, t_a) = pure_op("fac", "stg-1");
s.apply_operation_checked(DEFAULT_BRANCH, op_a, t_a, &pure_candidate("fac"))
.expect("clean publish");
let b = s.get_branch(DEFAULT_BRANCH).unwrap().unwrap();
assert_eq!(
b.head_op, b.last_gate_checkpoint,
"after successful advance, checkpoint must equal head",
);
}
#[test]
fn unblock_clears_contamination_and_allows_advance() {
let (s, _tmp) = fresh();
let (op_a, t_a) = pure_op("fac", "stg-1");
s.apply_operation_checked(DEFAULT_BRANCH, op_a, t_a, &pure_candidate("fac"))
.expect("clean publish of A1");
let log = s.attestation_log().unwrap();
let head_a = s.get_branch(DEFAULT_BRANCH).unwrap().unwrap().head_op.clone().unwrap();
let spec_att = Attestation::with_timestamp(
"stg-1".to_string(),
Some(head_a.clone()),
None,
AttestationKind::Spec {
spec_id: "fac-spec".into(),
method: lex_vcs::SpecMethod::Random,
trials: Some(100),
},
AttestationResult::Passed,
ProducerDescriptor {
tool: "tool-X".into(),
version: "1.0".into(),
model: None,
},
None,
100,
);
log.put(&spec_att).unwrap();
let block_att = Attestation::with_timestamp(
"tool-X".to_string(),
None,
None,
AttestationKind::ProducerBlock {
tool_id: "tool-X".into(),
reason: "compromised".into(),
blocked_at: 50,
},
AttestationResult::Passed,
ProducerDescriptor {
tool: "lex attest retro-block".into(),
version: "0".into(),
model: None,
},
None,
2000,
);
log.put(&block_att).unwrap();
s.invalidate_gate_checkpoints().unwrap();
let (op_b, t_b) = modify_op("fac", &head_a, "stg-1", "stg-2");
let err = s.apply_operation_checked(DEFAULT_BRANCH, op_b, t_b, &pure_candidate("fac"));
assert!(matches!(err, Err(StoreError::ProducerBlocked(_))),
"expected ProducerBlocked, got {err:?}");
let unblock_att = Attestation::with_timestamp(
"tool-X".to_string(),
None,
None,
AttestationKind::ProducerUnblock {
tool_id: "tool-X".into(),
reason: "vendor patched".into(),
unblocked_at: 3000,
},
AttestationResult::Passed,
ProducerDescriptor {
tool: "lex attest retro-block".into(),
version: "0".into(),
model: None,
},
None,
3000,
);
log.put(&unblock_att).unwrap();
s.invalidate_gate_checkpoints().unwrap();
let (op_b2, t_b2) = modify_op("fac", &head_a, "stg-1", "stg-2");
s.apply_operation_checked(DEFAULT_BRANCH, op_b2, t_b2, &pure_candidate("fac"))
.expect("post-unblock advance should succeed");
}
#[test]
fn pre_256_branch_files_without_checkpoint_walk_from_genesis() {
let (s, _tmp) = fresh();
let (op_a, t_a) = pure_op("fac", "stg-1");
s.apply_operation_checked(DEFAULT_BRANCH, op_a, t_a, &pure_candidate("fac"))
.expect("clean publish of A");
let path = s.root().join("branches/main.json");
let bytes = std::fs::read(&path).unwrap();
let mut value: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
value.as_object_mut().unwrap().remove("last_gate_checkpoint");
std::fs::write(&path, serde_json::to_vec_pretty(&value).unwrap()).unwrap();
let b = s.get_branch(DEFAULT_BRANCH).unwrap().unwrap();
assert!(b.last_gate_checkpoint.is_none(), "checkpoint should default to None");
let head = b.head_op.clone().unwrap();
let (op_b, t_b) = modify_op("fac", &head, "stg-1", "stg-2");
s.apply_operation_checked(DEFAULT_BRANCH, op_b, t_b, &pure_candidate("fac"))
.expect("upgraded-store advance should succeed");
let b2 = s.get_branch(DEFAULT_BRANCH).unwrap().unwrap();
assert!(b2.last_gate_checkpoint.is_some());
assert_eq!(b2.last_gate_checkpoint, b2.head_op);
}