Compare commits
2 Commits
7b567b3f78
...
40c45bbae7
| Author | SHA1 | Date |
|---|---|---|
|
|
40c45bbae7 | |
|
|
febf348bd6 |
|
|
@ -314,6 +314,15 @@ version = "1.8.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
|
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bincode"
|
||||||
|
version = "1.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.13.1"
|
version = "2.13.1"
|
||||||
|
|
@ -484,10 +493,15 @@ dependencies = [
|
||||||
name = "chronogrid_net"
|
name = "chronogrid_net"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"bincode",
|
||||||
|
"chacha20poly1305",
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
"futures",
|
"futures",
|
||||||
|
"generic-array",
|
||||||
"hex",
|
"hex",
|
||||||
"libp2p",
|
"libp2p",
|
||||||
|
"lz4_flex",
|
||||||
"rand 0.8.7",
|
"rand 0.8.7",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
@ -1680,6 +1694,7 @@ dependencies = [
|
||||||
"libp2p-noise",
|
"libp2p-noise",
|
||||||
"libp2p-quic",
|
"libp2p-quic",
|
||||||
"libp2p-relay",
|
"libp2p-relay",
|
||||||
|
"libp2p-request-response",
|
||||||
"libp2p-swarm",
|
"libp2p-swarm",
|
||||||
"libp2p-tcp",
|
"libp2p-tcp",
|
||||||
"libp2p-upnp",
|
"libp2p-upnp",
|
||||||
|
|
@ -1974,6 +1989,26 @@ dependencies = [
|
||||||
"web-time",
|
"web-time",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libp2p-request-response"
|
||||||
|
version = "0.27.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1356c9e376a94a75ae830c42cdaea3d4fe1290ba409a22c809033d1b7dcab0a6"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"futures",
|
||||||
|
"futures-bounded",
|
||||||
|
"futures-timer",
|
||||||
|
"libp2p-core",
|
||||||
|
"libp2p-identity",
|
||||||
|
"libp2p-swarm",
|
||||||
|
"rand 0.8.7",
|
||||||
|
"smallvec",
|
||||||
|
"tracing",
|
||||||
|
"void",
|
||||||
|
"web-time",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libp2p-swarm"
|
name = "libp2p-swarm"
|
||||||
version = "0.45.1"
|
version = "0.45.1"
|
||||||
|
|
@ -2146,6 +2181,15 @@ version = "0.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lz4_flex"
|
||||||
|
version = "0.11.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "373f5eceeeab7925e0c1098212f2fbc4d416adec9d35051a6ab251e824c1854a"
|
||||||
|
dependencies = [
|
||||||
|
"twox-hash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "match-lookup"
|
name = "match-lookup"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
|
@ -3638,6 +3682,12 @@ version = "0.2.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "twox-hash"
|
||||||
|
version = "2.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8464ec13c3691491391d9fce00f6416c9a48e46972f72d7865688be2080192c9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.20.1"
|
version = "1.20.1"
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,21 @@
|
||||||
- `KadEvent::RoutingUpdated` uses private libp2p-internal types; routing-table tracking tested via `#[cfg(test)] insert_peer_for_test()` helper; full coverage deferred to Task 2.5 integration test.
|
- `KadEvent::RoutingUpdated` uses private libp2p-internal types; routing-table tracking tested via `#[cfg(test)] insert_peer_for_test()` helper; full coverage deferred to Task 2.5 integration test.
|
||||||
- Full ADR at `docs/ARCHITECTURE/ADR/0009-net-transport-discovery-layer.md`.
|
- Full ADR at `docs/ARCHITECTURE/ADR/0009-net-transport-discovery-layer.md`.
|
||||||
|
|
||||||
|
## [2026-08-22] - ADL-010: chronogrid_net Crate — CRDT Delta Sync Protocol (Task 2.4)
|
||||||
|
|
||||||
|
- **Status**: Accepted
|
||||||
|
- **Context**: Task 2.4 required implementing the CRDT delta exchange protocol over libp2p in `chronogrid_net`, building on the Ed25519 `NodeIdentity`, `SwarmKey`, and transport/discovery layers from Tasks 2.2 and 2.3.
|
||||||
|
- **Decision**:
|
||||||
|
- Added new `sync` module with 7 public types: `VectorClock`, `DeltaPacket`, `SyncCodec`, `SyncProtocol`, `GossipEvent`, `GossipsubBroadcast`, `NetEvent`.
|
||||||
|
- `VectorClock` uses `HashMap<entity_id, hlc_timestamp>` with lexicographic HLC comparison — no numeric parsing required since the `<ms>-<logical>-<node_prefix>` format preserves causal ordering under string comparison.
|
||||||
|
- `DeltaPacket` wire format: `bincode` → `lz4_flex` compress → `ChaCha20-Poly1305` encrypt with 12-byte random nonce prepended. AAD = `b"chronogrid-delta"` binds ciphertext to protocol context.
|
||||||
|
- `SyncProtocol` uses `#[derive(NetworkBehaviour)]` wrapping `libp2p::request_response::Behaviour<SyncCodec>`. `SyncCodec` implements the `Codec` trait via `#[async_trait]` (required by libp2p-request-response 0.27.0) using `futures::AsyncRead`/`AsyncWrite` (not `tokio::io`).
|
||||||
|
- `GossipsubBroadcast` subscribes to topic `chronogrid/state-changes/1` with SHA-256 content-addressed message deduplication and `ValidationMode::Strict`.
|
||||||
|
- `NetEvent` enum: `DeltasReceived { from_peer, deltas }`, `SyncComplete { peer_id }`, `SyncError { peer_id, reason }`, `GossipReceived { entity_type, entity_id, hlc_timestamp, origin_peer }`.
|
||||||
|
- Added dependencies: `bincode = "1"`, `lz4_flex = "0.11"`, `chacha20poly1305 = "0.10"`, `generic-array = "0.14"`, `async-trait = "0.1"`, libp2p `"request-response"` feature.
|
||||||
|
- 100 unit tests pass + 5 doc-tests pass (35 new sync tests + 65 from Tasks 2.2/2.3).
|
||||||
|
- Full ADR at `docs/ARCHITECTURE/ADR/0010-net-crdt-delta-sync-protocol.md`.
|
||||||
|
|
||||||
## [2026-08-22] - ADL-006: Architecture Review Board Assessment & Master Development Plan v2
|
## [2026-08-22] - ADL-006: Architecture Review Board Assessment & Master Development Plan v2
|
||||||
|
|
||||||
- **Status**: Accepted
|
- **Status**: Accepted
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ libp2p = { version = "0.54", features = [
|
||||||
"kad",
|
"kad",
|
||||||
"identify",
|
"identify",
|
||||||
"relay",
|
"relay",
|
||||||
|
"request-response",
|
||||||
"tokio",
|
"tokio",
|
||||||
"macros",
|
"macros",
|
||||||
] }
|
] }
|
||||||
|
|
@ -44,6 +45,16 @@ tokio = { version = "1", features = ["full"] }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
# Logging / tracing
|
# Logging / tracing
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
|
# Serialization for DeltaPacket (bincode binary format)
|
||||||
|
bincode = "1"
|
||||||
|
# LZ4 compression for delta payloads
|
||||||
|
lz4_flex = "0.11"
|
||||||
|
# ChaCha20-Poly1305 AEAD encryption for delta payloads
|
||||||
|
chacha20poly1305 = "0.10"
|
||||||
|
# Generic array for AEAD key/nonce types
|
||||||
|
generic-array = "0.14"
|
||||||
|
# async-trait for implementing libp2p request_response Codec
|
||||||
|
async-trait = "0.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
//! | [`peer_event`] | `PeerEvent` enum — unified peer lifecycle events |
|
//! | [`peer_event`] | `PeerEvent` enum — unified peer lifecycle events |
|
||||||
//! | [`transport`] | TCP + Noise XK + Yamux transport builder; keypair conversion |
|
//! | [`transport`] | TCP + Noise XK + Yamux transport builder; keypair conversion |
|
||||||
//! | [`discovery`] | `MdnsDiscovery` (LAN) and `KademliaDht` (WAN) peer discovery |
|
//! | [`discovery`] | `MdnsDiscovery` (LAN) and `KademliaDht` (WAN) peer discovery |
|
||||||
|
//! | [`sync`] | `VectorClock`, `DeltaPacket`, `SyncProtocol`, `GossipsubBroadcast`, `NetEvent` |
|
||||||
//!
|
//!
|
||||||
//! ## Quick Start
|
//! ## Quick Start
|
||||||
//!
|
//!
|
||||||
|
|
@ -23,6 +24,7 @@
|
||||||
//! use chronogrid_net::identity::load_or_create_identity;
|
//! use chronogrid_net::identity::load_or_create_identity;
|
||||||
//! use chronogrid_net::swarm_key::SwarmKey;
|
//! use chronogrid_net::swarm_key::SwarmKey;
|
||||||
//! use chronogrid_net::transport::{build_transport, TransportConfig};
|
//! use chronogrid_net::transport::{build_transport, TransportConfig};
|
||||||
|
//! use chronogrid_net::sync::{VectorClock, DeltaPacket, SyncProtocol};
|
||||||
//!
|
//!
|
||||||
//! // Load or generate a persistent node identity.
|
//! // Load or generate a persistent node identity.
|
||||||
//! let identity = load_or_create_identity(Path::new("/var/lib/chronogrid/identity.json"))
|
//! let identity = load_or_create_identity(Path::new("/var/lib/chronogrid/identity.json"))
|
||||||
|
|
@ -40,6 +42,10 @@
|
||||||
//! // Build the transport config (used by SwarmManager in Task 2.5).
|
//! // Build the transport config (used by SwarmManager in Task 2.5).
|
||||||
//! let config = TransportConfig::from_swarm_key(&swarm_key);
|
//! let config = TransportConfig::from_swarm_key(&swarm_key);
|
||||||
//! println!("User-agent: {}", config.user_agent());
|
//! println!("User-agent: {}", config.user_agent());
|
||||||
|
//!
|
||||||
|
//! // Build a vector clock for delta sync.
|
||||||
|
//! let mut clock = VectorClock::new();
|
||||||
|
//! clock.update("entity-uuid-1", "1724000000000-0001-ab12");
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
pub mod discovery;
|
pub mod discovery;
|
||||||
|
|
@ -47,6 +53,7 @@ pub mod error;
|
||||||
pub mod identity;
|
pub mod identity;
|
||||||
pub mod peer_event;
|
pub mod peer_event;
|
||||||
pub mod swarm_key;
|
pub mod swarm_key;
|
||||||
|
pub mod sync;
|
||||||
pub mod transport;
|
pub mod transport;
|
||||||
|
|
||||||
// Re-export the most commonly used types at the crate root for convenience.
|
// Re-export the most commonly used types at the crate root for convenience.
|
||||||
|
|
@ -54,4 +61,5 @@ pub use error::{NetError, NetResult};
|
||||||
pub use identity::{NodeId, NodeIdentity};
|
pub use identity::{NodeId, NodeIdentity};
|
||||||
pub use peer_event::PeerEvent;
|
pub use peer_event::PeerEvent;
|
||||||
pub use swarm_key::SwarmKey;
|
pub use swarm_key::SwarmKey;
|
||||||
|
pub use sync::{DeltaPacket, GossipEvent, NetEvent, SyncProtocol, VectorClock};
|
||||||
pub use transport::{TransportConfig, build_transport, peer_id_from_identity};
|
pub use transport::{TransportConfig, build_transport, peer_id_from_identity};
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,154 @@
|
||||||
|
# ADR-0010: chronogrid_net — CRDT Delta Sync Protocol (Task 2.4)
|
||||||
|
|
||||||
|
**Status:** Accepted
|
||||||
|
**Date:** 2026-08-22
|
||||||
|
**Task:** 2.4 — Implement chronogrid_net: CRDT Delta Sync Protocol
|
||||||
|
**Author:** Autonomous Agent
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Task 2.4 required implementing the CRDT delta exchange protocol over libp2p in
|
||||||
|
`crates/chronogrid_net/`, building on the transport and discovery layers from
|
||||||
|
Tasks 2.2 and 2.3.
|
||||||
|
|
||||||
|
The implementation must satisfy:
|
||||||
|
- `VectorClock` — per-entity HLC head-state tracking for delta computation.
|
||||||
|
- `DeltaPacket` — serializable, compressed, encrypted CRDT changeset container.
|
||||||
|
- `SyncProtocol` — libp2p `NetworkBehaviour` for request/response vector-clock exchange.
|
||||||
|
- `GossipsubBroadcast` — real-time state-change notifications via Gossipsub.
|
||||||
|
- `NetEvent` — unified event enum for `SwarmManager` (Task 2.5) consumption.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
### New module: `crates/chronogrid_net/src/sync.rs`
|
||||||
|
|
||||||
|
| Type | Role |
|
||||||
|
|-----------------------|-------------------------------------------------------------------------------|
|
||||||
|
| `VectorClock` | `HashMap<entity_id, hlc_timestamp>` — lexicographic HLC comparison for delta computation |
|
||||||
|
| `DeltaPacket` | Serializable CRDT changeset with entity routing metadata |
|
||||||
|
| `SyncCodec` | libp2p `request_response::Codec` — length-prefixed bincode framing |
|
||||||
|
| `SyncProtocol` | `#[derive(NetworkBehaviour)]` wrapping `ReqResBehaviour<SyncCodec>` |
|
||||||
|
| `GossipEvent` | Application-level gossip message (entity_type, entity_id, hlc_timestamp, origin_peer) |
|
||||||
|
| `GossipsubBroadcast` | Wraps `libp2p::gossipsub::Behaviour` with topic subscription and event parsing |
|
||||||
|
| `NetEvent` | Unified event enum: `DeltasReceived`, `SyncComplete`, `SyncError`, `GossipReceived` |
|
||||||
|
|
||||||
|
### VectorClock Design
|
||||||
|
|
||||||
|
- `HashMap<String, String>` mapping `entity_id → hlc_timestamp`.
|
||||||
|
- HLC timestamps use lexicographic ordering (format: `<ms>-<logical>-<node_prefix>`),
|
||||||
|
which is equivalent to causal ordering — no numeric parsing required.
|
||||||
|
- `update()` returns `bool` indicating whether the entry was actually advanced.
|
||||||
|
- `missing_from(other)` computes the set of entity IDs where `other` is ahead of `self`.
|
||||||
|
- `merge(other)` takes the maximum timestamp per entity (LWW semantics).
|
||||||
|
- Fully serializable via `bincode` for wire transmission in `SyncRequest`/`SyncResponse`.
|
||||||
|
|
||||||
|
### DeltaPacket Wire Format
|
||||||
|
|
||||||
|
```text
|
||||||
|
[ 12-byte random nonce ]
|
||||||
|
[ ChaCha20-Poly1305 ciphertext of:
|
||||||
|
LZ4(bincode(DeltaPacket { entity_type, entity_id, hlc_timestamp, changeset_bytes, sequence }))
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Serialization:** `bincode` (compact binary, no schema overhead).
|
||||||
|
- **Compression:** `lz4_flex` with size-prepended format — Automerge changesets
|
||||||
|
compress well (typically 60–80% reduction for structured JSON-like data).
|
||||||
|
- **Encryption:** ChaCha20-Poly1305 AEAD with the 32-byte `SwarmKey` as the
|
||||||
|
symmetric key. A fresh 12-byte random nonce per packet ensures ciphertext
|
||||||
|
uniqueness even for identical plaintexts.
|
||||||
|
- **Authentication:** The AEAD tag authenticates the ciphertext; tampering
|
||||||
|
causes decryption to fail with an error (not silent corruption).
|
||||||
|
- **AAD:** `b"chronogrid-delta"` as additional authenticated data to bind the
|
||||||
|
ciphertext to the ChronoGrid protocol context.
|
||||||
|
|
||||||
|
### SyncProtocol (NetworkBehaviour)
|
||||||
|
|
||||||
|
- Wraps `libp2p::request_response::Behaviour<SyncCodec>` via `#[derive(NetworkBehaviour)]`.
|
||||||
|
- `SyncCodec` implements `libp2p::request_response::Codec` using `#[async_trait]`
|
||||||
|
(required by libp2p-request-response 0.27.0 which uses `async_trait` internally).
|
||||||
|
- Frame format: 4-byte big-endian u32 length prefix + bincode payload.
|
||||||
|
- Maximum frame size: 64 MiB (generous upper bound for large delta batches).
|
||||||
|
- `send_sync_request()` / `send_sync_response()` delegate to the inner behaviour.
|
||||||
|
- `encode_deltas()` / `decode_deltas()` are static helpers for batch processing.
|
||||||
|
|
||||||
|
### GossipsubBroadcast Design
|
||||||
|
|
||||||
|
- Subscribes to topic `chronogrid/state-changes/1` on construction.
|
||||||
|
- Message deduplication: SHA-256 of message data as `MessageId` (content-addressed).
|
||||||
|
- `ValidationMode::Strict` — only signed messages from known peers are accepted.
|
||||||
|
- `handle_behaviour_event()` maps `GossipBehaviourEvent::Message` → `NetEvent::GossipReceived`.
|
||||||
|
- All other Gossipsub events (Subscribed, Unsubscribed, GossipsubNotSupported) are
|
||||||
|
logged at DEBUG level and produce no `NetEvent` output.
|
||||||
|
|
||||||
|
### NetEvent enum
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub enum NetEvent {
|
||||||
|
DeltasReceived { from_peer: PeerId, deltas: Vec<DeltaPacket> },
|
||||||
|
SyncComplete { peer_id: PeerId },
|
||||||
|
SyncError { peer_id: PeerId, reason: String },
|
||||||
|
GossipReceived { entity_type, entity_id, hlc_timestamp, origin_peer: String },
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`SwarmManager` (Task 2.5) broadcasts these events via a
|
||||||
|
`tokio::sync::broadcast::Sender<NetEvent>` channel to all application subscribers.
|
||||||
|
|
||||||
|
### New dependencies added
|
||||||
|
|
||||||
|
| Crate | Version | Purpose |
|
||||||
|
|--------------------|---------|--------------------------------------------------|
|
||||||
|
| `bincode` | 1 | Compact binary serialization for DeltaPacket |
|
||||||
|
| `lz4_flex` | 0.11 | LZ4 compression for delta payloads |
|
||||||
|
| `chacha20poly1305` | 0.10 | ChaCha20-Poly1305 AEAD encryption |
|
||||||
|
| `generic-array` | 0.14 | GenericArray for AEAD key/nonce types |
|
||||||
|
| `async-trait` | 0.1 | Required by libp2p request_response Codec trait |
|
||||||
|
| `libp2p` feature | — | Added `"request-response"` feature flag |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Test Coverage
|
||||||
|
|
||||||
|
**100 unit tests pass + 5 doc-tests pass** across all modules:
|
||||||
|
|
||||||
|
| Module | Tests | Coverage highlights |
|
||||||
|
|---------------|-------|----------------------------------------------------------------------------|
|
||||||
|
| `sync` | 35 | VectorClock (11), DeltaPacket (8), SyncProtocol helpers (3), GossipEvent (2), NetEvent (11) |
|
||||||
|
| `peer_event` | 10 | (from Task 2.3, unchanged) |
|
||||||
|
| `transport` | 12 | (from Task 2.3, unchanged) |
|
||||||
|
| `discovery` | 13 | (from Task 2.3, unchanged) |
|
||||||
|
| `identity` | 9 | (from Task 2.2, unchanged) |
|
||||||
|
| `swarm_key` | 16 | (from Task 2.2, unchanged) |
|
||||||
|
| `sync` (integration) | 1 | `test_vector_clock_drives_delta_selection` — full encode/decode/merge cycle |
|
||||||
|
|
||||||
|
### Key test scenarios
|
||||||
|
|
||||||
|
- **VectorClock:** insert, update (newer/older/equal), `missing_from` with empty/partial/superset,
|
||||||
|
merge, iter, bincode round-trip.
|
||||||
|
- **DeltaPacket:** encode/decode round-trip, random nonce uniqueness, wrong-key rejection,
|
||||||
|
truncated payload rejection, tampered ciphertext rejection (AEAD authentication),
|
||||||
|
1 MiB large changeset, bincode round-trip.
|
||||||
|
- **SyncProtocol:** batch encode/decode, empty batch, construction.
|
||||||
|
- **GossipEvent:** bincode round-trip, invalid bytes rejection.
|
||||||
|
- **NetEvent:** all 4 variants — predicates, Display, Clone.
|
||||||
|
- **Integration:** two-peer delta selection driven by VectorClock comparison,
|
||||||
|
encode/decode pipeline, clock update after merge.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- `SwarmManager` (Task 2.5) can now import `SyncProtocol`, `GossipsubBroadcast`,
|
||||||
|
`VectorClock`, `DeltaPacket`, and `NetEvent` directly from `chronogrid_net::sync`.
|
||||||
|
- The `SyncProtocol` behaviour must be polled by the libp2p `Swarm` event loop in
|
||||||
|
Task 2.5; `send_sync_request()` should be called on `SwarmEvent::ConnectionEstablished`.
|
||||||
|
- `GossipsubBroadcast.behaviour` must be included in the composite `NetworkBehaviour`
|
||||||
|
derive struct in Task 2.5.
|
||||||
|
- `chronogrid_core::sync::SyncEngine` (Task 2.6) receives `NetEvent::DeltasReceived`
|
||||||
|
and calls `Database::merge_crdt_doc()` for each `DeltaPacket`.
|
||||||
|
- No breaking changes to existing modules (`identity`, `swarm_key`, `transport`, `discovery`).
|
||||||
|
|
@ -57,7 +57,7 @@
|
||||||
|
|
||||||
## Task 2.4 — Implement chronogrid_net: CRDT Delta Sync Protocol
|
## Task 2.4 — Implement chronogrid_net: CRDT Delta Sync Protocol
|
||||||
|
|
||||||
- [ ] **Task 2.4**: Implement the CRDT delta exchange protocol over libp2p in `crates/chronogrid_net/`:
|
- [x] **Task 2.4**: Implement the CRDT delta exchange protocol over libp2p in `crates/chronogrid_net/`:
|
||||||
- Implement `sync::VectorClock` — tracks per-entity HLC head state for delta computation
|
- Implement `sync::VectorClock` — tracks per-entity HLC head state for delta computation
|
||||||
- Implement `sync::DeltaPacket` — serializable struct containing compressed CRDT changesets
|
- Implement `sync::DeltaPacket` — serializable struct containing compressed CRDT changesets
|
||||||
- Implement `sync::SyncProtocol` — custom libp2p `NetworkBehaviour`:
|
- Implement `sync::SyncProtocol` — custom libp2p `NetworkBehaviour`:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue