Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
690a31f
feat(storage): implement Merkle tree synchronization protocols
xilosada Jan 30, 2026
b2cd632
feat: readme patching (#1745)
frdomovic Jan 23, 2026
9bc8823
feat(sync): implement Phase 1 snapshot sync for bootstrap (#1744)
sh3ll3x3c Jan 26, 2026
cf9237c
chore: release 0.10.0-rc.37 (#1750)
sh3ll3x3c Jan 26, 2026
6cb8698
WIP: sync protocol POC - CIP, network sync tests, hybrid merge archit…
xilosada Jan 30, 2026
d117259
feat(storage): Add crdt_type to Metadata for CRDT-aware merge dispatch
xilosada Jan 30, 2026
e1b7a4b
feat(sdk): enforce CRDT types in #[app::state] macro
xilosada Jan 30, 2026
b2816f8
feat(storage): implement WasmMergeCallback for custom type sync
xilosada Jan 30, 2026
9b81d64
refactor: remove ResolutionStrategy, use CrdtType for all merge dispatch
xilosada Jan 30, 2026
38083d6
docs: update CIP with Phase 2 completion status
xilosada Jan 30, 2026
1042383
docs: move Runtime Integration from Phase 2.3 to Phase 3.1
xilosada Jan 30, 2026
24f953b
feat(sync): implement Phase 3 - Network Layer & Runtime Integration
xilosada Jan 30, 2026
37cf468
docs: update CIP with Phase 3 completion status
xilosada Jan 30, 2026
797a2cd
refactor(sync): make sync_hints required (not optional)
xilosada Jan 30, 2026
0807633
refactor(storage): inject MergeRegistry for test isolation
xilosada Jan 30, 2026
3ace693
feat(sync): process sync_hints for proactive divergence detection
xilosada Jan 30, 2026
bf21769
feat(sync): implement delta buffering during snapshot sync
xilosada Jan 30, 2026
a609198
feat(sync): wire RuntimeMergeCallback and add post-snapshot delta replay
xilosada Jan 30, 2026
49489c5
docs(CIP): mark Phase 4 as complete
xilosada Jan 30, 2026
0b5e800
chore: allow dead_code for get_merge_callback (ready for Phase 5)
xilosada Jan 30, 2026
7db1392
test(sync): add integration tests for sync flow
xilosada Jan 30, 2026
326725a
docs(CIP): update Phase 4 with integration tests
xilosada Jan 30, 2026
369f5bc
docs(CIP): update Phase 4 complete, Phase 5 in progress
xilosada Jan 30, 2026
bd9fd8b
feat(sync): implement adaptive protocol selection
xilosada Jan 30, 2026
7401494
feat(sync): add DeltaIdBloomFilter for efficient membership testing
xilosada Jan 30, 2026
772cf11
feat(sync): add GossipMode for delta broadcast control
xilosada Jan 30, 2026
0868d90
docs(CIP): mark Phase 5 as complete
xilosada Jan 30, 2026
b13903f
test(e2e): add comprehensive sync workflow tests
xilosada Jan 30, 2026
5358c78
docs(CIP): add E2E workflow tests section
xilosada Jan 30, 2026
280975a
fix(e2e): update workflow tests for merobox 0.3.x compatibility
xilosada Jan 30, 2026
74de349
fix(sync): return RootHashMismatch error instead of force-overwriting…
xilosada Jan 30, 2026
335e325
feat(sync): implement smart concurrent branch merge handling
xilosada Jan 30, 2026
4d227d8
docs(CIP): update sync protocol spec with concurrent branch handling
xilosada Jan 30, 2026
1f14b91
fix(sync): remove force_root_hash for multiple DAG heads
xilosada Jan 30, 2026
563e063
fix: CRDT sync - deterministic collection IDs and trust merge semantics
xilosada Jan 30, 2026
e7031ba
fix(sync): dedicated channel for network events - fixes 3-node sync
xilosada Jan 31, 2026
672f785
docs: document dedicated network event channel (Appendix J)
xilosada Jan 31, 2026
35dc615
test(e2e): add comprehensive sync protocol tests
xilosada Jan 31, 2026
f5ebbb3
test: increase wait times in E2E sync tests
xilosada Jan 31, 2026
7e70415
feat(sync): Add configurable fresh node sync strategy for benchmarking
xilosada Jan 31, 2026
c3a8032
fix(sync): Add snapshot boundary stubs for DAG parent resolution
xilosada Jan 31, 2026
e1791a8
docs: Document fresh node sync strategy and snapshot boundary stubs
xilosada Jan 31, 2026
96857ba
feat(sync): Add configurable StateSyncStrategy for Merkle tree protocols
xilosada Jan 31, 2026
f407524
feat(sync): Wire StateSyncStrategy into SyncManager with logging
xilosada Jan 31, 2026
0a11a32
fix(sync): CRITICAL - Prevent Snapshot from overwriting local data
xilosada Jan 31, 2026
cb51836
docs: Update CIP Appendix M with Snapshot safety protection
xilosada Jan 31, 2026
7ed3a66
feat(sync): implement entity-based bloom filter sync and per-phase in…
xilosada Jan 31, 2026
62c9ec4
feat(sync): Add strategy benchmarking with instrumentation
xilosada Jan 31, 2026
8e674fc
feat(bench): Add edge case benchmarks and analysis
xilosada Jan 31, 2026
85b7af7
docs: Add edge case benchmark references to CIP and analysis docs
xilosada Jan 31, 2026
32dda8f
docs: Add peer finding analysis plan
xilosada Jan 31, 2026
2dcbf39
feat(sync): Add peer finding instrumentation (PEER_FIND_BREAKDOWN)
xilosada Jan 31, 2026
2803000
feat(sync): Update peer finding docs and metrics extraction
xilosada Jan 31, 2026
4edd604
feat(sync): Implement peer finding strategies A0-A5
xilosada Jan 31, 2026
6266d2f
docs: Update peer finding analysis with benchmark results
xilosada Jan 31, 2026
8a42c33
feat(sync): Separate peer finding from dialing with proper phase trac…
xilosada Jan 31, 2026
889a997
feat(sync): Phase 2 - Dial latency instrumentation and optimization
xilosada Jan 31, 2026
7853303
docs: Add comprehensive sync performance investigation document
xilosada Jan 31, 2026
761301b
feat(sync): Complete Phase 2 missing items
xilosada Jan 31, 2026
ab3274b
docs: Update documentation, benchmarks, and decision log
xilosada Jan 31, 2026
8426e53
docs: Update all sync documentation to reflect completion status
xilosada Jan 31, 2026
8f75ef8
feat(sync): Wire Phase 4 - Protocol negotiation and merge callback in…
xilosada Jan 31, 2026
044ee9e
docs: Add Phase 4 benchmark results to peer finding analysis
xilosada Jan 31, 2026
c1acd92
docs: Add comprehensive branch checkpoint before delta pruning
xilosada Jan 31, 2026
80cb600
docs: Update documentation index with hierarchy and checkpoint
xilosada Jan 31, 2026
4770c4e
docs: Add RFC for Actix network architecture discussion
xilosada Jan 31, 2026
9624cac
feat(sync): Integrate ParallelDialTracker + document tech debt
xilosada Jan 31, 2026
912c205
fix(sync): Wire merge callback into tree sync entity application
xilosada Jan 31, 2026
6de2bc8
docs: Clarify tree sync CRDT merge architectural gap
xilosada Jan 31, 2026
da0155a
fix: Properly integrate CRDT merge in tree sync
xilosada Jan 31, 2026
c13c737
docs: CRITICAL AUDIT - CRDT merge is broken
xilosada Jan 31, 2026
279458e
docs: Update documentation post critical audit
xilosada Jan 31, 2026
a0a1bde
test: Add unit tests for CRDT merge and TreeLeafData propagation
xilosada Jan 31, 2026
6f3fb31
fix: Fix flaky test_peer_backoff test
xilosada Jan 31, 2026
f821543
audit: Comprehensive code verification (2026-01-31)
xilosada Jan 31, 2026
aa70ee4
fix: Include metadata in BloomFilterResponse wire format
xilosada Jan 31, 2026
8762a16
docs: clarify WASM merge callback is NOT missing - registry already w…
xilosada Jan 31, 2026
d7e051f
feat: implement TRUE parallel dialing with FuturesUnordered
xilosada Feb 1, 2026
7600733
docs: mark CIP as CODE COMPLETE
xilosada Feb 1, 2026
52a451e
docs: consolidate - remove 13 redundant analysis docs
xilosada Feb 1, 2026
1870b93
feat: add DeltaKind::Checkpoint for proper snapshot boundaries
xilosada Feb 1, 2026
41c4de6
feat(apps): add comprehensive sync-test application
xilosada Feb 1, 2026
36e0545
docs: add payload compression to future optimizations backlog
xilosada Feb 1, 2026
53f9474
docs: polish sync protocol documentation for review
xilosada Feb 1, 2026
59b25f9
fix: address critical review findings from bugbot and agents
xilosada Feb 1, 2026
761a897
docs: update documentation with review findings and fixes
xilosada Feb 1, 2026
51bca56
docs: restructure documentation by purpose (spec vs decisions vs POC)
xilosada Feb 1, 2026
180ea15
docs: improve sync state machine diagram with clearer explanations
xilosada Feb 1, 2026
8891531
fix(sync): address Bugbot findings
xilosada Feb 1, 2026
9d3992f
docs: add Bugbot findings to POC implementation notes
xilosada Feb 1, 2026
6d3f500
docs(cip): correct entity transfer status - all strategies fully impl…
xilosada Feb 1, 2026
e6e8c8a
docs(cip): add explicit invariants, acceptance criteria, and clarific…
xilosada Feb 1, 2026
070dbf4
docs(cip): convert to pure protocol specification
xilosada Feb 1, 2026
3cceffe
docs(cip): convert to pure normative spec (remove impl details)
xilosada Feb 1, 2026
e11b6b7
docs: move implementation details from CIP to POC notes
xilosada Feb 1, 2026
135d20b
docs(cip): final consistency fixes and polish
xilosada Feb 1, 2026
710fc7f
docs(cip): fix flowchart inconsistency and malicious delta wording
xilosada Feb 1, 2026
009233f
docs(cip): final consistency pass - use cases table and terminology
xilosada Feb 1, 2026
ff38ba9
docs: create implementation issues from CIP
xilosada Feb 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .cursor/worktrees.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"setup-worktree": [
"npm install"
]
}
34 changes: 31 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ members = [
"./apps/abi_conformance",
"./apps/state-schema-conformance",
"./apps/xcall-example",
"./apps/sync-test",

"./tools/calimero-abi",
"./tools/merodb",
Expand Down Expand Up @@ -143,6 +144,7 @@ jsonschema = "0.17"
jsonwebtoken = "9.3.0"
lazy_static = "1.4"
libp2p = "0.56.0"
lz4_flex = "0.11"
libp2p-identity = "0.2.12"
libp2p-metrics = "0.17.0"
libp2p-stream = "0.4.0-alpha"
Expand Down
13 changes: 7 additions & 6 deletions apps/abi_conformance/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::collections::BTreeMap;
use calimero_sdk::app;
use calimero_sdk::borsh::{BorshDeserialize, BorshSerialize};
use calimero_sdk::serde::{Deserialize, Serialize};
use calimero_storage::collections::{LwwRegister, UnorderedMap, Vector};
use thiserror::Error;

// Test multi-file ABI generation
Expand Down Expand Up @@ -106,22 +107,22 @@ pub enum Event {

// State
#[app::state(emits = Event)]
#[derive(Debug, PartialEq, Eq, PartialOrd, BorshSerialize, BorshDeserialize)]
#[derive(Debug, BorshSerialize, BorshDeserialize)]
#[borsh(crate = "calimero_sdk::borsh")]
pub struct AbiState {
counters: BTreeMap<String, u32>, // map<string,u32>
users: Vec<UserId32>, // list<UserId32>
counters: UnorderedMap<String, LwwRegister<u32>>, // map<string,u32> - CRDT with LWW values
users: Vector<LwwRegister<UserId32>>, // list<UserId32> - CRDT with LWW values
}

// Implementation
#[app::logic]
impl AbiState {
#[app::init]
#[must_use]
pub const fn init() -> Self {
pub fn init() -> Self {
Self {
counters: BTreeMap::new(),
users: Vec::new(),
counters: UnorderedMap::new(),
users: Vector::new(),
}
}

Expand Down
28 changes: 19 additions & 9 deletions apps/blobs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use calimero_sdk::borsh::{BorshDeserialize, BorshSerialize};
use calimero_sdk::serde::Serialize;
use calimero_sdk::{app, env};
use calimero_storage::collections::UnorderedMap;
use calimero_storage::collections::{Counter, LwwRegister, UnorderedMap};

// === CONSTANTS ===

Expand Down Expand Up @@ -146,15 +146,15 @@ impl calimero_storage::collections::Mergeable for FileRecord {
pub struct FileShareState {
/// Context owner's identity as base58-encoded public key
/// Set during initialization from `env::executor_id()`
pub owner: String,
pub owner: LwwRegister<String>,

/// Map of file ID to file metadata records
/// Key: file ID (e.g., "file_0"), Value: FileRecord
pub files: UnorderedMap<String, FileRecord>,

/// Counter for generating unique file IDs
/// Incremented on each file upload
pub file_counter: u64,
/// Incremented on each file upload (CRDT G-Counter for distributed safety)
pub file_counter: Counter,
}

/// Events emitted by the application
Expand Down Expand Up @@ -195,9 +195,9 @@ impl FileShareState {
app::log!("Initializing file sharing app for owner: {}", owner);

FileShareState {
owner,
owner: LwwRegister::new(owner),
files: UnorderedMap::new(),
file_counter: 0,
file_counter: Counter::new(),
}
}

Expand Down Expand Up @@ -225,8 +225,15 @@ impl FileShareState {
) -> Result<String, String> {
let blob_id = parse_blob_id_base58(&blob_id_str)?;

let file_id = format!("file_{}", self.file_counter);
self.file_counter += 1;
// Get current counter value for file ID, then increment
let counter_value = self
.file_counter
.value()
.map_err(|e| format!("Failed to get counter: {e:?}"))?;
let file_id = format!("file_{}", counter_value);
self.file_counter
.increment()
.map_err(|e| format!("Failed to increment counter: {e:?}"))?;

let uploader_id = env::executor_id();
let uploader = encode_blob_id_base58(&uploader_id);
Expand Down Expand Up @@ -436,7 +443,10 @@ impl FileShareState {
- Total files: {}\n\
- Total storage: {:.2} MB ({} bytes)\n\
- Owner: {}",
file_count, total_mb, total_size, self.owner
file_count,
total_mb,
total_size,
self.owner.get()
))
}
}
3 changes: 2 additions & 1 deletion apps/kv-store/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ impl KvStore {
#[app::init]
pub fn init() -> KvStore {
KvStore {
items: UnorderedMap::new(),
// Use deterministic ID based on field name for sync compatibility
items: UnorderedMap::new_with_field_name("items"),
}
}

Expand Down
24 changes: 24 additions & 0 deletions apps/sync-test/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "sync-test"
version = "0.1.0"
edition = "2021"
publish = false

[lib]
crate-type = ["cdylib"]

[dependencies]
calimero-sdk = { path = "../../crates/sdk" }
calimero-storage = { path = "../../crates/storage" }
calimero-storage-macros = { path = "../../crates/storage-macros" }
hex = "0.4"
thiserror = "2.0"

[profile.app-release]
inherits = "release"
codegen-units = 1
opt-level = "z"
lto = true
debug = false
panic = "abort"
overflow-checks = true
96 changes: 96 additions & 0 deletions apps/sync-test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Sync Test Application

Comprehensive test application for validating Calimero's synchronization protocol.

## Purpose

This application exercises ALL storage spaces and CRDT types to ensure proper synchronization:

- **Public Storage**: Shared state across all nodes
- **User Storage**: Per-user isolated state
- **Frozen Storage**: Content-addressed immutable data

## CRDT Types Tested

| Type | Description | Merge Semantics |
|------|-------------|-----------------|
| `LwwRegister<T>` | Last-Write-Wins register | Latest timestamp wins |
| `Counter` | PN-Counter | Positive/negative increments merge |
| `UnorderedMap<K, V>` | Key-value map | Merge by key, delegate to value CRDT |
| `UserStorage<T>` | Per-user data | Isolated by user public key |
| `FrozenStorage<T>` | Immutable blobs | Content-addressed (no merge needed) |

## Operations

### Public Key-Value
- `set(key, value)` - Set a key-value pair
- `get(key)` - Get a value
- `delete(key)` - Delete a key (creates tombstone)
- `batch_set(pairs)` - Batch set multiple pairs
- `entries()` - Get all entries
- `len()` - Get count of entries

### Public Counters
- `counter_inc(name)` - Increment a named counter
- `counter_dec(name)` - Decrement a named counter
- `counter_get(name)` - Get counter value

### Public Stats (Nested CRDT)
- `stats_inc(entity)` - Record increment
- `stats_dec(entity)` - Record decrement
- `stats_get(entity)` - Get (increments, decrements)

### User Storage
- `user_set_simple(value)` - Set current user's value
- `user_get_simple()` - Get current user's value
- `user_set_kv(key, value)` - Set in user's private store
- `user_get_kv(key)` - Get from user's private store
- `user_delete_kv(key)` - Delete from user's private store
- `user_counter_inc()` - Increment user's counter
- `user_counter_get()` - Get user's counter
- `user_get_simple_for(user_key)` - Read another user's value

### Frozen Storage
- `frozen_add(data)` - Add immutable data, returns hash
- `frozen_get(hash_hex)` - Get by hash

### Verification
- `snapshot()` - Get deterministic state snapshot
- `verify(expected)` - Verify state matches expected
- `get_operation_count()` - Total operations performed
- `get_deleted_count()` - Count of deleted keys
- `was_deleted(key)` - Check if key was deleted

### Bulk Operations (Benchmarking)
- `bulk_write(prefix, count, value_size)` - Write N keys
- `bulk_delete(prefix, count)` - Delete N keys
- `bulk_counter_inc(name, count)` - Increment N times

## Building

```bash
./build.sh
```

Output: `res/sync_test.wasm`

## Testing with merobox

See `workflows/` for example test workflows.

## Deterministic Verification

The `snapshot()` method returns a deterministic representation of state that can be compared across nodes:

```json
{
"public_kv_count": 10,
"public_kv_entries": {"key1": "value1", ...},
"public_counter_values": {"counter1": 5, ...},
"deleted_keys_count": 2,
"frozen_count": 1,
"operation_count": 15
}
```

After sync convergence, all nodes should return identical snapshots.
20 changes: 20 additions & 0 deletions apps/sync-test/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/bash

set -e

cd "$(dirname $0)"

TARGET="${CARGO_TARGET_DIR:-../../target}"

cargo build --target wasm32-unknown-unknown --profile app-release

mkdir -p res

cp "$TARGET/wasm32-unknown-unknown/app-release/sync_test.wasm" res/sync_test.wasm

# Skip wasm-opt for now - it requires --enable-bulk-memory-opt
# if command -v wasm-opt >/dev/null 2>&1; then
# wasm-opt -Oz --enable-bulk-memory res/sync_test.wasm -o res/sync_test.wasm
# fi

ls -la res/sync_test.wasm
Binary file added apps/sync-test/res/sync_test.wasm
Binary file not shown.
Loading