use calimero_dag::CausalDelta;
use calimero_storage::action::Action;
use calimero_storage::address::Id;
use calimero_storage::entities::Metadata;
#[tokio::test]
async fn test_concurrent_branches_track_expected_root_hash() {
use calimero_dag::{ApplyError, DagStore, DeltaApplier};
use std::sync::Arc;
use tokio::sync::Mutex;
struct TestApplier {
applied: Arc<Mutex<Vec<([u8; 32], [u8; 32])>>>, }
#[async_trait::async_trait]
impl DeltaApplier<Vec<Action>> for TestApplier {
async fn apply(&self, delta: &CausalDelta<Vec<Action>>) -> Result<(), ApplyError> {
self.applied
.lock()
.await
.push((delta.id, delta.expected_root_hash));
Ok(())
}
}
let applier = TestApplier {
applied: Arc::new(Mutex::new(Vec::new())),
};
let mut dag = DagStore::new([0; 32]);
let delta_a = create_delta_with_root([0x01; 32], vec![[0; 32]], [0xAA; 32]);
let delta_b = create_delta_with_root([0x02; 32], vec![[0; 32]], [0xBB; 32]);
let _ = dag.add_delta(delta_a.clone(), &applier).await.unwrap();
let _ = dag.add_delta(delta_b.clone(), &applier).await.unwrap();
let mut heads = dag.get_heads();
heads.sort();
assert_eq!(heads.len(), 2);
assert_eq!(heads, vec![[0x01; 32], [0x02; 32]]);
let applied = applier.applied.lock().await;
assert_eq!(applied.len(), 2);
let delta_a_applied = applied.iter().find(|(id, _)| *id == [0x01; 32]).unwrap();
assert_eq!(
delta_a_applied.1, [0xAA; 32],
"Delta A should have expected_root_hash [0xAA; 32]"
);
let delta_b_applied = applied.iter().find(|(id, _)| *id == [0x02; 32]).unwrap();
assert_eq!(
delta_b_applied.1, [0xBB; 32],
"Delta B should have expected_root_hash [0xBB; 32]"
);
}
#[tokio::test]
async fn test_merge_delta_expected_root_hash() {
use calimero_dag::{ApplyError, DagStore, DeltaApplier};
use std::sync::Arc;
use tokio::sync::Mutex;
struct TestApplier {
applied: Arc<Mutex<Vec<([u8; 32], [u8; 32])>>>,
}
#[async_trait::async_trait]
impl DeltaApplier<Vec<Action>> for TestApplier {
async fn apply(&self, delta: &CausalDelta<Vec<Action>>) -> Result<(), ApplyError> {
self.applied
.lock()
.await
.push((delta.id, delta.expected_root_hash));
Ok(())
}
}
let applier = TestApplier {
applied: Arc::new(Mutex::new(Vec::new())),
};
let mut dag = DagStore::new([0; 32]);
let delta_a = create_delta_with_root([0x01; 32], vec![[0; 32]], [0xAA; 32]);
let delta_b = create_delta_with_root([0x02; 32], vec![[0; 32]], [0xBB; 32]);
let _ = dag.add_delta(delta_a, &applier).await.unwrap();
let _ = dag.add_delta(delta_b, &applier).await.unwrap();
let delta_merge = create_delta_with_root(
[0x03; 32],
vec![[0x01; 32], [0x02; 32]],
[0xCC; 32], );
let _ = dag.add_delta(delta_merge, &applier).await.unwrap();
assert_eq!(dag.get_heads(), vec![[0x03; 32]]);
let applied = applier.applied.lock().await;
let merge_applied = applied.iter().find(|(id, _)| *id == [0x03; 32]).unwrap();
assert_eq!(
merge_applied.1, [0xCC; 32],
"Merge delta should have its own expected_root_hash"
);
}
fn create_delta_with_root(
id: [u8; 32],
parents: Vec<[u8; 32]>,
expected_root_hash: [u8; 32],
) -> CausalDelta<Vec<Action>> {
let action = Action::Add {
id: Id::from([id[0]; 32]),
data: vec![1, 2, 3],
ancestors: vec![],
metadata: Metadata::default(),
};
CausalDelta {
id,
parents,
payload: vec![action],
hlc: calimero_storage::logical_clock::HybridTimestamp::default(),
expected_root_hash,
kind: calimero_dag::DeltaKind::Regular,
}
}