use csv_adapter_core::client::{ValidationClient, ValidationResult};
use csv_adapter_core::commitment::Commitment;
use csv_adapter_core::commitment_chain::verify_ordered_commitment_chain;
use csv_adapter_core::consignment::Consignment;
use csv_adapter_core::genesis::Genesis;
use csv_adapter_core::hash::Hash;
use csv_adapter_core::right::{OwnershipProof, Right};
use csv_adapter_core::seal::SealRef;
use csv_adapter_core::seal_registry::{
ChainId, CrossChainSealRegistry, SealConsumption, SealStatus,
};
use csv_adapter_core::state_store::{
ContractHistory, InMemoryStateStore, StateHistoryStore, StateTransitionRecord,
};
use csv_adapter_core::validator::ConsignmentValidator;
fn make_genesis(contract_id: Hash) -> Genesis {
Genesis::new(contract_id, Hash::new([0x01; 32]), vec![], vec![], vec![])
}
fn make_consignment(genesis: Genesis, schema_id: Hash) -> Consignment {
Consignment::new(genesis, vec![], vec![], vec![], schema_id)
}
fn make_test_commitment(previous: Hash, seal_id: u8) -> Commitment {
let domain = [0u8; 32];
let seal = SealRef::new(vec![seal_id], None).unwrap();
Commitment::simple(
Hash::new([0xAB; 32]),
previous,
Hash::new([0u8; 32]),
&seal,
domain,
)
}
#[test]
fn test_validation_client_receives_consignment() {
let mut client = ValidationClient::new();
let contract_id = Hash::new([0xAB; 32]);
let genesis = make_genesis(contract_id);
let consignment = make_consignment(genesis, Hash::new([0x01; 32]));
let result = client.receive_consignment(&consignment, ChainId::Bitcoin);
match result {
ValidationResult::Accepted {
rights_count,
seals_consumed,
..
} => {
assert_eq!(rights_count, 0);
assert_eq!(seals_consumed, 0);
}
ValidationResult::Rejected { reason } => {
let reason_str = format!("{:?}", reason);
assert!(reason_str.contains("EmptyChain") || reason_str.contains("CommitmentChain"));
}
}
}
#[test]
fn test_consignment_validator_report() {
let validator = ConsignmentValidator::new();
let contract_id = Hash::new([0xCD; 32]);
let genesis = make_genesis(contract_id);
let consignment = make_consignment(genesis, Hash::new([0x02; 32]));
let report = validator.validate_consignment(&consignment, ChainId::Bitcoin);
assert!(!report.steps.is_empty());
let structural_step = report
.steps
.iter()
.find(|s| s.name == "Structural Validation");
assert!(structural_step.is_some());
let _ = structural_step.unwrap().passed;
assert!(!report.summary.is_empty());
}
#[test]
fn test_state_history_persistence() {
let mut store = InMemoryStateStore::new();
let contract_id = Hash::new([0xEF; 32]);
let genesis_commitment = make_test_commitment(Hash::new([0u8; 32]), 0x01);
let history = ContractHistory::from_genesis(genesis_commitment);
store.save_contract_history(contract_id, &history).unwrap();
let loaded = store.load_contract_history(contract_id).unwrap();
assert!(loaded.is_some());
let loaded = loaded.unwrap();
assert_eq!(loaded.contract_id, Hash::new([0xAB; 32]));
let contracts = store.list_contracts().unwrap();
assert_eq!(contracts.len(), 1);
assert_eq!(contracts[0], contract_id);
store.delete_contract(contract_id).unwrap();
let contracts = store.list_contracts().unwrap();
assert_eq!(contracts.len(), 0);
}
#[test]
fn test_cross_chain_double_spend_detection() {
let mut registry = CrossChainSealRegistry::new();
let seal_ref = SealRef::new(vec![0x01, 0x02], None).unwrap();
let right_id = csv_adapter_core::right::RightId(Hash::new([0xAA; 32]));
let c1 = SealConsumption {
chain: ChainId::Bitcoin,
seal_ref: seal_ref.clone(),
right_id: right_id.clone(),
block_height: 100,
tx_hash: Hash::new([0xBB; 32]),
recorded_at: 1_000_000,
};
registry.record_consumption(c1).unwrap();
let c2 = SealConsumption {
chain: ChainId::Ethereum,
seal_ref: seal_ref.clone(),
right_id,
block_height: 200,
tx_hash: Hash::new([0xCC; 32]),
recorded_at: 2_000_000,
};
let result = registry.record_consumption(c2);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.is_cross_chain);
match registry.check_seal_status(&seal_ref) {
SealStatus::DoubleSpent { consumptions } => {
assert_eq!(consumptions.len(), 2);
}
_ => panic!("Expected DoubleSpent status"),
}
}
#[test]
fn test_right_lifecycle_with_transfer() {
let right = Right::new(
Hash::new([0xDD; 32]),
OwnershipProof {
proof: vec![0x01, 0x02, 0x03],
owner: vec![0xFF; 32],
scheme: None,
},
&[0x42],
);
assert!(right.verify().is_ok());
assert!(!right.is_consumed());
let new_owner = OwnershipProof {
proof: vec![0xAA, 0xBB],
owner: vec![0xEE; 32],
scheme: None,
};
let transferred = right.transfer(new_owner.clone(), b"transfer-salt");
assert_ne!(transferred.id, right.id);
assert_eq!(transferred.commitment, right.commitment);
assert_eq!(transferred.owner, new_owner);
assert!(right.verify().is_ok());
assert!(transferred.verify().is_ok());
}
#[test]
fn test_commitment_chain_with_state_store() {
let contract_id = Hash::new([0xAB; 32]);
let domain = [0u8; 32];
let seal_genesis = SealRef::new(vec![0x01], None).unwrap();
let genesis_c = Commitment::simple(
contract_id,
Hash::new([0u8; 32]),
Hash::new([0u8; 32]),
&seal_genesis,
domain,
);
let seal_1 = SealRef::new(vec![0x02], None).unwrap();
let c1 = Commitment::simple(
contract_id,
genesis_c.hash(),
Hash::new([0u8; 32]),
&seal_1,
domain,
);
let seal_2 = SealRef::new(vec![0x03], None).unwrap();
let c2 = Commitment::simple(
contract_id,
c1.hash(),
Hash::new([0u8; 32]),
&seal_2,
domain,
);
let result = verify_ordered_commitment_chain(&[genesis_c.clone(), c1.clone(), c2.clone()]);
assert!(result.is_ok());
let chain_result = result.unwrap();
assert_eq!(chain_result.length, 3);
assert_eq!(chain_result.genesis.hash(), genesis_c.hash());
assert_eq!(chain_result.latest.hash(), c2.hash());
assert_eq!(chain_result.contract_id, contract_id);
let mut store = InMemoryStateStore::new();
let mut history = ContractHistory::from_genesis(genesis_c);
let _ = history.add_transition(StateTransitionRecord {
commitment: c1.clone(),
seal_ref: seal_1,
rights: vec![],
block_height: 100,
verified: true,
});
let _ = history.add_transition(StateTransitionRecord {
commitment: c2.clone(),
seal_ref: seal_2,
rights: vec![],
block_height: 200,
verified: true,
});
store.save_contract_history(contract_id, &history).unwrap();
let loaded = store.load_contract_history(contract_id).unwrap().unwrap();
assert_eq!(loaded.transition_count(), 2);
assert_eq!(loaded.latest_commitment_hash, c2.hash());
}
#[test]
fn test_multiple_contracts_in_store() {
let mut store = InMemoryStateStore::new();
let contract_a = Hash::new([0xAA; 32]);
let contract_b = Hash::new([0xBB; 32]);
let _genesis_a = make_genesis(contract_a);
let _genesis_b = make_genesis(contract_b);
let history_a = ContractHistory::from_genesis(make_test_commitment(Hash::new([0u8; 32]), 0x01));
let history_b = ContractHistory::from_genesis(make_test_commitment(Hash::new([0u8; 32]), 0x02));
store.save_contract_history(contract_a, &history_a).unwrap();
store.save_contract_history(contract_b, &history_b).unwrap();
let contracts = store.list_contracts().unwrap();
assert_eq!(contracts.len(), 2);
assert!(contracts.contains(&contract_a));
assert!(contracts.contains(&contract_b));
}
#[test]
fn test_seal_registry_statistics() {
let mut registry = CrossChainSealRegistry::new();
assert_eq!(registry.total_seals(), 0);
assert_eq!(registry.double_spend_count(), 0);
assert_eq!(registry.known_chains().len(), 0);
for (i, chain) in [ChainId::Bitcoin, ChainId::Sui, ChainId::Aptos]
.iter()
.enumerate()
{
let seal_ref = SealRef::new(vec![i as u8 + 1], None).unwrap();
let right_id = csv_adapter_core::right::RightId(Hash::new([i as u8 + 1; 32]));
let consumption = SealConsumption {
chain: chain.clone(),
seal_ref,
right_id,
block_height: (i * 100) as u64,
tx_hash: Hash::new([i as u8 + 1; 32]),
recorded_at: (i + 1) as u64 * 1_000_000,
};
registry.record_consumption(consumption).unwrap();
}
assert_eq!(registry.total_seals(), 3);
assert_eq!(registry.double_spend_count(), 0);
assert_eq!(registry.known_chains().len(), 3);
}
#[test]
fn test_client_tracks_validated_consignments() {
let mut client = ValidationClient::new();
let contract_id = Hash::new([0xCC; 32]);
let genesis = make_genesis(contract_id);
let consignment = make_consignment(genesis, Hash::new([0x03; 32]));
let _ = client.receive_consignment(&consignment, ChainId::Bitcoin);
let _ = client.store();
let _ = client.seal_registry();
}
#[test]
fn test_end_to_end_validation_pipeline() {
let contract_id = Hash::new([0xEE; 32]);
let genesis = make_genesis(contract_id);
let consignment = make_consignment(genesis.clone(), Hash::new([0x04; 32]));
let validator = ConsignmentValidator::new();
let seal_count_before = validator.seal_registry().total_seals();
let report = validator.validate_consignment(&consignment, ChainId::Bitcoin);
assert!(!report.steps.is_empty());
assert!(!report.summary.is_empty());
let step_names: Vec<&str> = report.steps.iter().map(|s| s.name.as_str()).collect();
assert!(step_names.contains(&"Structural Validation"));
assert!(step_names.contains(&"Seal Consumption Validation"));
assert!(step_names.contains(&"State Transition Validation"));
assert_eq!(seal_count_before, 0);
}