use std::borrow::Cow;
use borsh::{BorshDeserialize, BorshSerialize};
use calimero_crypto::Nonce;
use calimero_primitives::blobs::BlobId;
use calimero_primitives::context::ContextId;
use calimero_primitives::hash::Hash;
use calimero_primitives::identity::{PrivateKey, PublicKey};
use super::hash_comparison::TreeNode;
use super::levelwise::LevelNode;
use super::snapshot::SnapshotError;
pub const MAX_TREE_REQUEST_DEPTH: u8 = 16;
#[derive(Debug, BorshSerialize, BorshDeserialize)]
pub enum StreamMessage<'a> {
Init {
context_id: ContextId,
party_id: PublicKey,
payload: InitPayload,
next_nonce: Nonce,
},
Message {
sequence_id: u64,
payload: MessagePayload<'a>,
next_nonce: Nonce,
},
OpaqueError,
}
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)]
pub enum InitPayload {
BlobShare {
blob_id: BlobId,
},
KeyShare,
DeltaRequest {
context_id: ContextId,
delta_id: [u8; 32],
},
DagHeadsRequest {
context_id: ContextId,
},
SnapshotBoundaryRequest {
context_id: ContextId,
requested_cutoff_timestamp: Option<u64>,
},
SnapshotStreamRequest {
context_id: ContextId,
boundary_root_hash: Hash,
page_limit: u16,
byte_limit: u32,
resume_cursor: Option<Vec<u8>>,
},
TreeNodeRequest {
context_id: ContextId,
node_id: [u8; 32],
max_depth: Option<u8>,
},
LevelWiseRequest {
context_id: ContextId,
level: u32,
parent_ids: Option<Vec<[u8; 32]>>,
},
}
#[derive(Debug, BorshSerialize, BorshDeserialize)]
pub enum MessagePayload<'a> {
BlobShare {
chunk: Cow<'a, [u8]>,
},
KeyShare {
sender_key: PrivateKey,
},
DeltaResponse {
delta: Cow<'a, [u8]>,
},
DeltaNotFound,
DagHeadsResponse {
dag_heads: Vec<[u8; 32]>,
root_hash: Hash,
},
Challenge {
challenge: [u8; 32],
},
ChallengeResponse {
signature: [u8; 64],
},
SnapshotBoundaryResponse {
boundary_timestamp: u64,
boundary_root_hash: Hash,
dag_heads: Vec<[u8; 32]>,
},
SnapshotPage {
payload: Cow<'a, [u8]>,
uncompressed_len: u32,
cursor: Option<Vec<u8>>,
page_count: u64,
sent_count: u64,
},
SnapshotError {
error: SnapshotError,
},
TreeNodeResponse {
nodes: Vec<TreeNode>,
not_found: bool,
},
LevelWiseResponse {
level: u32,
nodes: Vec<LevelNode>,
has_more_levels: bool,
},
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_init_payload_tree_node_request() {
let request = InitPayload::TreeNodeRequest {
context_id: ContextId::from([1u8; 32]),
node_id: [2u8; 32],
max_depth: Some(1),
};
let encoded = borsh::to_vec(&request).expect("serialize");
let decoded: InitPayload = borsh::from_slice(&encoded).expect("deserialize");
match decoded {
InitPayload::TreeNodeRequest {
context_id,
node_id,
max_depth,
} => {
assert_eq!(*context_id.as_ref(), [1u8; 32]);
assert_eq!(node_id, [2u8; 32]);
assert_eq!(max_depth, Some(1));
}
_ => panic!("wrong variant"),
}
}
#[test]
fn test_message_payload_tree_node_response() {
use crate::sync::hash_comparison::{LeafMetadata, TreeLeafData, TreeNode};
let leaf_data = TreeLeafData::new(
[10u8; 32],
vec![1, 2, 3],
LeafMetadata::new(
crate::sync::hash_comparison::CrdtType::lww_register("test"),
100,
[0u8; 32],
),
);
let node = TreeNode::leaf([1u8; 32], [2u8; 32], leaf_data);
let response = MessagePayload::TreeNodeResponse {
nodes: vec![node],
not_found: false,
};
let encoded = borsh::to_vec(&response).expect("serialize");
let decoded: MessagePayload = borsh::from_slice(&encoded).expect("deserialize");
match decoded {
MessagePayload::TreeNodeResponse { nodes, not_found } => {
assert_eq!(nodes.len(), 1);
assert!(!not_found);
}
_ => panic!("wrong variant"),
}
}
#[test]
fn test_message_payload_tree_node_response_not_found() {
let response = MessagePayload::TreeNodeResponse {
nodes: vec![],
not_found: true,
};
let encoded = borsh::to_vec(&response).expect("serialize");
let decoded: MessagePayload = borsh::from_slice(&encoded).expect("deserialize");
match decoded {
MessagePayload::TreeNodeResponse { nodes, not_found } => {
assert!(nodes.is_empty());
assert!(not_found);
}
_ => panic!("wrong variant"),
}
}
#[test]
fn test_init_payload_levelwise_request_full_level() {
let request = InitPayload::LevelWiseRequest {
context_id: ContextId::from([1u8; 32]),
level: 0,
parent_ids: None,
};
let encoded = borsh::to_vec(&request).expect("serialize");
let decoded: InitPayload = borsh::from_slice(&encoded).expect("deserialize");
match decoded {
InitPayload::LevelWiseRequest {
context_id,
level,
parent_ids,
} => {
assert_eq!(*context_id.as_ref(), [1u8; 32]);
assert_eq!(level, 0);
assert!(parent_ids.is_none());
}
_ => panic!("wrong variant"),
}
}
#[test]
fn test_init_payload_levelwise_request_with_parents() {
let parents = vec![[10u8; 32], [20u8; 32], [30u8; 32]];
let request = InitPayload::LevelWiseRequest {
context_id: ContextId::from([2u8; 32]),
level: 1,
parent_ids: Some(parents.clone()),
};
let encoded = borsh::to_vec(&request).expect("serialize");
let decoded: InitPayload = borsh::from_slice(&encoded).expect("deserialize");
match decoded {
InitPayload::LevelWiseRequest {
context_id,
level,
parent_ids,
} => {
assert_eq!(*context_id.as_ref(), [2u8; 32]);
assert_eq!(level, 1);
assert_eq!(parent_ids, Some(parents));
}
_ => panic!("wrong variant"),
}
}
#[test]
fn test_message_payload_levelwise_response_internal_nodes() {
use crate::sync::levelwise::LevelNode;
let nodes = vec![
LevelNode::internal([1u8; 32], [10u8; 32], None),
LevelNode::internal([2u8; 32], [20u8; 32], None),
];
let response = MessagePayload::LevelWiseResponse {
level: 0,
nodes: nodes.clone(),
has_more_levels: true,
};
let encoded = borsh::to_vec(&response).expect("serialize");
let decoded: MessagePayload = borsh::from_slice(&encoded).expect("deserialize");
match decoded {
MessagePayload::LevelWiseResponse {
level,
nodes: decoded_nodes,
has_more_levels,
} => {
assert_eq!(level, 0);
assert_eq!(decoded_nodes.len(), 2);
assert!(has_more_levels);
assert!(decoded_nodes[0].is_internal());
assert!(decoded_nodes[1].is_internal());
}
_ => panic!("wrong variant"),
}
}
#[test]
fn test_message_payload_levelwise_response_with_leaves() {
use crate::sync::hash_comparison::{CrdtType, LeafMetadata, TreeLeafData};
use crate::sync::levelwise::LevelNode;
let metadata = LeafMetadata::new(CrdtType::lww_register("test"), 100, [0u8; 32]);
let leaf_data = TreeLeafData::new([5u8; 32], vec![1, 2, 3, 4], metadata);
let nodes = vec![
LevelNode::internal([1u8; 32], [10u8; 32], None),
LevelNode::leaf([2u8; 32], [20u8; 32], Some([1u8; 32]), leaf_data),
];
let response = MessagePayload::LevelWiseResponse {
level: 1,
nodes,
has_more_levels: false,
};
let encoded = borsh::to_vec(&response).expect("serialize");
let decoded: MessagePayload = borsh::from_slice(&encoded).expect("deserialize");
match decoded {
MessagePayload::LevelWiseResponse {
level,
nodes: decoded_nodes,
has_more_levels,
} => {
assert_eq!(level, 1);
assert_eq!(decoded_nodes.len(), 2);
assert!(!has_more_levels);
assert!(decoded_nodes[0].is_internal());
assert!(decoded_nodes[1].is_leaf());
assert_eq!(decoded_nodes[1].parent_id, Some([1u8; 32]));
}
_ => panic!("wrong variant"),
}
}
#[test]
fn test_message_payload_levelwise_response_empty() {
let response = MessagePayload::LevelWiseResponse {
level: 2,
nodes: vec![],
has_more_levels: false,
};
let encoded = borsh::to_vec(&response).expect("serialize");
let decoded: MessagePayload = borsh::from_slice(&encoded).expect("deserialize");
match decoded {
MessagePayload::LevelWiseResponse {
level,
nodes,
has_more_levels,
} => {
assert_eq!(level, 2);
assert!(nodes.is_empty());
assert!(!has_more_levels);
}
_ => panic!("wrong variant"),
}
}
}