use borsh::{BorshDeserialize, BorshSerialize};
pub const DEFAULT_DELTA_SYNC_THRESHOLD: usize = 128;
#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
pub struct DeltaSyncRequest {
pub missing_delta_ids: Vec<[u8; 32]>,
}
impl DeltaSyncRequest {
#[must_use]
pub fn new(missing_delta_ids: Vec<[u8; 32]>) -> Self {
Self { missing_delta_ids }
}
#[must_use]
pub fn is_within_threshold(&self) -> bool {
self.missing_delta_ids.len() <= DEFAULT_DELTA_SYNC_THRESHOLD
}
#[must_use]
pub fn count(&self) -> usize {
self.missing_delta_ids.len()
}
}
#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
pub struct DeltaSyncResponse {
pub deltas: Vec<DeltaPayload>,
pub not_found: Vec<[u8; 32]>,
}
impl DeltaSyncResponse {
#[must_use]
pub fn new(deltas: Vec<DeltaPayload>, not_found: Vec<[u8; 32]>) -> Self {
Self { deltas, not_found }
}
#[must_use]
pub fn empty(not_found: Vec<[u8; 32]>) -> Self {
Self {
deltas: vec![],
not_found,
}
}
#[must_use]
pub fn is_complete(&self) -> bool {
self.not_found.is_empty()
}
#[must_use]
pub fn count(&self) -> usize {
self.deltas.len()
}
}
#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
pub struct DeltaPayload {
pub id: [u8; 32],
pub parents: Vec<[u8; 32]>,
pub payload: Vec<u8>,
pub hlc_timestamp: u64,
pub expected_root_hash: [u8; 32],
}
impl DeltaPayload {
#[must_use]
pub fn is_genesis(&self) -> bool {
self.parents.is_empty()
}
}
#[derive(Clone, Debug)]
pub enum DeltaApplyResult {
Success {
applied_count: usize,
new_root_hash: [u8; 32],
},
MissingParents {
missing_parent_deltas: Vec<[u8; 32]>,
applied_before_failure: usize,
},
Failed {
reason: String,
},
}
impl DeltaApplyResult {
#[must_use]
pub fn is_success(&self) -> bool {
matches!(self, Self::Success { .. })
}
#[must_use]
pub fn needs_state_sync(&self) -> bool {
matches!(self, Self::MissingParents { .. })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_delta_sync_request_roundtrip() {
let request = DeltaSyncRequest::new(vec![[1; 32], [2; 32], [3; 32]]);
let encoded = borsh::to_vec(&request).expect("serialize");
let decoded: DeltaSyncRequest = borsh::from_slice(&encoded).expect("deserialize");
assert_eq!(request, decoded);
assert_eq!(decoded.count(), 3);
}
#[test]
fn test_delta_sync_request_threshold() {
let small_request = DeltaSyncRequest::new(vec![[1; 32]; 10]);
assert!(small_request.is_within_threshold());
let at_threshold = DeltaSyncRequest::new(vec![[1; 32]; DEFAULT_DELTA_SYNC_THRESHOLD]);
assert!(at_threshold.is_within_threshold());
let large_request = DeltaSyncRequest::new(vec![[1; 32]; DEFAULT_DELTA_SYNC_THRESHOLD + 1]);
assert!(!large_request.is_within_threshold());
}
#[test]
fn test_delta_payload_roundtrip() {
let payload = DeltaPayload {
id: [1; 32],
parents: vec![[2; 32], [3; 32]],
payload: vec![4, 5, 6, 7],
hlc_timestamp: 12345678,
expected_root_hash: [8; 32],
};
let encoded = borsh::to_vec(&payload).expect("serialize");
let decoded: DeltaPayload = borsh::from_slice(&encoded).expect("deserialize");
assert_eq!(payload, decoded);
assert!(!decoded.is_genesis());
}
#[test]
fn test_delta_payload_genesis() {
let genesis = DeltaPayload {
id: [1; 32],
parents: vec![], payload: vec![1, 2, 3],
hlc_timestamp: 0,
expected_root_hash: [2; 32],
};
assert!(genesis.is_genesis());
let non_genesis = DeltaPayload {
id: [2; 32],
parents: vec![[1; 32]], payload: vec![4, 5, 6],
hlc_timestamp: 1,
expected_root_hash: [3; 32],
};
assert!(!non_genesis.is_genesis());
}
#[test]
fn test_delta_sync_response_roundtrip() {
let delta1 = DeltaPayload {
id: [1; 32],
parents: vec![],
payload: vec![1, 2, 3],
hlc_timestamp: 100,
expected_root_hash: [10; 32],
};
let delta2 = DeltaPayload {
id: [2; 32],
parents: vec![[1; 32]],
payload: vec![4, 5, 6],
hlc_timestamp: 200,
expected_root_hash: [20; 32],
};
let response = DeltaSyncResponse::new(vec![delta1, delta2], vec![[99; 32]]);
let encoded = borsh::to_vec(&response).expect("serialize");
let decoded: DeltaSyncResponse = borsh::from_slice(&encoded).expect("deserialize");
assert_eq!(response, decoded);
assert_eq!(decoded.count(), 2);
assert!(!decoded.is_complete()); }
#[test]
fn test_delta_sync_response_complete() {
let delta = DeltaPayload {
id: [1; 32],
parents: vec![],
payload: vec![1, 2, 3],
hlc_timestamp: 100,
expected_root_hash: [10; 32],
};
let complete_response = DeltaSyncResponse::new(vec![delta], vec![]); assert!(complete_response.is_complete());
let incomplete_response = DeltaSyncResponse::empty(vec![[1; 32]]);
assert!(!incomplete_response.is_complete());
assert_eq!(incomplete_response.count(), 0);
}
#[test]
fn test_delta_apply_result_success() {
let success = DeltaApplyResult::Success {
applied_count: 5,
new_root_hash: [1; 32],
};
assert!(success.is_success());
assert!(!success.needs_state_sync());
}
#[test]
fn test_delta_apply_result_missing_parents() {
let missing = DeltaApplyResult::MissingParents {
missing_parent_deltas: vec![[1; 32]],
applied_before_failure: 3,
};
assert!(!missing.is_success());
assert!(missing.needs_state_sync());
}
#[test]
fn test_delta_apply_result_failed() {
let failed = DeltaApplyResult::Failed {
reason: "hash mismatch".to_string(),
};
assert!(!failed.is_success());
assert!(!failed.needs_state_sync());
}
}