#[cfg(kani)]
mod verification {
use bytes::Bytes;
use kimberlite_crypto::chain_hash;
use kimberlite_kernel::{Command, State, apply_committed};
use kimberlite_storage::Record;
use kimberlite_types::{DataClass, Offset, Placement, RecordKind, StreamId, StreamName};
#[kani::proof]
#[kani::unwind(7)]
fn verify_kernel_storage_integration() {
let state = State::new();
let stream_id = StreamId::new(1);
let create_cmd = Command::CreateStream {
stream_id,
stream_name: StreamName::new("stream1".to_string()),
data_class: DataClass::Public,
placement: Placement::Global,
};
let result = apply_committed(state, create_cmd);
kani::assume(result.is_ok());
let (state, _) = result.unwrap();
let append_cmd = Command::AppendBatch {
stream_id,
events: vec![Bytes::from("event1")],
expected_offset: Offset::ZERO,
};
let result = apply_committed(state, append_cmd);
kani::assume(result.is_ok());
let record = Record::new(Offset::ZERO, None, Bytes::from("event1"));
let serialized = record.to_bytes();
assert!(!serialized.is_empty());
}
#[kani::proof]
#[kani::unwind(7)]
fn verify_crypto_storage_hash_chain() {
let payload1 = Bytes::from("event1");
let payload2 = Bytes::from("event2");
let record1 = Record::new(Offset::new(0), None, payload1.clone());
let hash1 = record1.compute_hash();
let mut data1 = vec![RecordKind::Data.as_byte()];
data1.extend_from_slice(&payload1);
let expected_hash1 = chain_hash(None, &data1);
assert_eq!(hash1, expected_hash1);
let record2 = Record::new(Offset::new(1), Some(hash1), payload2.clone());
let hash2 = record2.compute_hash();
let mut data2 = vec![RecordKind::Data.as_byte()];
data2.extend_from_slice(&payload2);
let expected_hash2 = chain_hash(Some(&hash1), &data2);
assert_eq!(hash2, expected_hash2);
}
#[kani::proof]
#[kani::unwind(3)]
fn verify_stream_id_tenant_extraction_consistent() {
let tenant_id_raw: u64 = kani::any();
let local_id: u32 = kani::any();
kani::assume(tenant_id_raw < 1000);
let tenant_id = kimberlite_types::TenantId::from(tenant_id_raw);
let stream_id = StreamId::from_tenant_and_local(tenant_id, local_id);
let extracted = kimberlite_types::TenantId::from_stream_id(stream_id);
assert_eq!(extracted, tenant_id);
}
#[kani::proof]
#[kani::unwind(3)]
fn verify_offset_type_consistency() {
let offset_raw: u64 = kani::any();
kani::assume(offset_raw < 1000);
let offset = Offset::new(offset_raw);
assert_eq!(offset.as_u64(), offset_raw);
let offset2 = Offset::new(offset_raw);
assert_eq!(offset, offset2);
}
#[kani::proof]
#[kani::unwind(3)]
fn verify_record_kind_encoding_consistency() {
let kinds = [
RecordKind::Data,
RecordKind::Checkpoint,
RecordKind::Tombstone,
];
for kind in kinds {
let byte = kind.as_byte();
let decoded = RecordKind::from_byte(byte);
assert_eq!(decoded, Some(kind));
}
}
#[kani::proof]
#[kani::unwind(5)]
fn verify_chain_hash_type_consistency() {
let data = b"payload";
let hash1 = chain_hash(None, data);
let record = Record::new(Offset::new(1), Some(hash1), Bytes::from("payload2"));
assert_eq!(record.prev_hash(), Some(hash1));
}
#[kani::proof]
#[kani::unwind(10)]
fn verify_end_to_end_create_append_serialize() {
let state = State::new();
let stream_id = StreamId::new(1);
let create_cmd = Command::CreateStream {
stream_id,
stream_name: StreamName::new("stream1".to_string()),
data_class: DataClass::Public,
placement: Placement::Global,
};
let result = apply_committed(state, create_cmd);
kani::assume(result.is_ok());
let (state, _) = result.unwrap();
assert!(state.stream_exists(&stream_id));
let append_cmd = Command::AppendBatch {
stream_id,
events: vec![Bytes::from("event1")],
expected_offset: Offset::ZERO,
};
let result = apply_committed(state, append_cmd);
kani::assume(result.is_ok());
let (new_state, _) = result.unwrap();
let metadata = new_state.get_stream(&stream_id).unwrap();
assert_eq!(metadata.current_offset, Offset::new(1));
let record = Record::new(Offset::ZERO, None, Bytes::from("event1"));
let hash = record.compute_hash();
assert_ne!(hash.as_bytes(), &[0u8; 32]);
let serialized = record.to_bytes();
let bytes = Bytes::from(serialized);
let (deserialized, _) = Record::from_bytes(&bytes).unwrap();
assert_eq!(deserialized.offset(), record.offset());
assert_eq!(deserialized.payload(), record.payload());
}
#[kani::proof]
#[kani::unwind(10)]
fn verify_multi_record_hash_chain() {
let record1 = Record::new(Offset::new(0), None, Bytes::from("event1"));
let hash1 = record1.compute_hash();
let record2 = Record::new(Offset::new(1), Some(hash1), Bytes::from("event2"));
let hash2 = record2.compute_hash();
let record3 = Record::new(Offset::new(2), Some(hash2), Bytes::from("event3"));
let hash3 = record3.compute_hash();
assert_eq!(record1.prev_hash(), None);
assert_eq!(record2.prev_hash(), Some(hash1));
assert_eq!(record3.prev_hash(), Some(hash2));
assert_ne!(hash1, hash2);
assert_ne!(hash2, hash3);
assert_ne!(hash1, hash3);
}
#[kani::proof]
#[kani::unwind(3)]
fn verify_data_class_consistency() {
let data_classes = [DataClass::PHI, DataClass::Public, DataClass::Deidentified];
for dc in data_classes {
let state = State::new();
let cmd = Command::CreateStream {
stream_id: StreamId::new(1),
stream_name: StreamName::new("stream".to_string()),
data_class: dc,
placement: Placement::Global,
};
let result = apply_committed(state, cmd);
assert!(result.is_ok() || result.is_err());
}
}
#[kani::proof]
#[kani::unwind(5)]
fn verify_placement_consistency() {
use kimberlite_types::Region;
let placements = [
Placement::Global,
Placement::Region(Region::USEast1),
Placement::Region(Region::APSoutheast2),
];
for placement in placements {
let state = State::new();
let cmd = Command::CreateStream {
stream_id: StreamId::new(1),
stream_name: StreamName::new("stream".to_string()),
data_class: DataClass::Public,
placement: placement.clone(),
};
let result = apply_committed(state, cmd);
assert!(result.is_ok() || result.is_err());
}
}
#[kani::proof]
#[kani::unwind(5)]
fn verify_bytes_type_consistency() {
let data = b"test data";
let kernel_bytes = Bytes::from(&data[..]);
let storage_bytes = Bytes::from(&data[..]);
assert_eq!(kernel_bytes, storage_bytes);
let record = Record::new(Offset::ZERO, None, kernel_bytes);
assert_eq!(record.payload(), &storage_bytes);
}
}