feat(examples): P2PCalc : decentralised EtherCalc collaboration via GossipSub with HLC + CRDT#36
Open
DZDasherKTB wants to merge 2 commits intoseetadev:mainfrom
Conversation
Adds examples/p2pcalc/ — a working integration layer that replaces EtherCalc's centralised Redis pub-sub sync with py-libp2p GossipSub. Architecture: - operation.py: SocialCalc command encoding with Hybrid Logical Clocks and MessagePack serialisation - crdt.py: Multi-Value Register (cell conflicts) + RGA (structural ops) - adapter.py: Redis <-> GossipSub bridge (zero EtherCalc source changes) - p2p_node.py: libp2p host with GossipSub v2.0, topic-per-sheet design - state_sync.py: two-phase late-joiner snapshot + op-log replay Innovations beyond baseline: - HLC timestamps instead of wall clocks (causal ordering across peers) - MVR conflict detection surfaces concurrent edits rather than silently dropping them (unlike LWW) - RGA for row/col structural ops handles concurrent inserts correctly - Causal buffering ensures out-of-order ops are applied correctly - Echo loop prevention at Redis inject layer - Local op-log WAL for crash recovery without full network resync Tests: 42 unit tests, no network or Redis required. Closes seetadev#34
22 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
This PR adds
examples/p2pcalc/, a working integration layer thatreplaces EtherCalc's centralised Redis pub-sub synchronisation with
py-libp2p GossipSub. Multiple peers collaboratively edit the same
spreadsheet in real time with no central server, no single point of
failure, and no changes to EtherCalc's source code or browser UI.
Closes #34
The problem with EtherCalc's current architecture
EtherCalc relays SocialCalc commands through a central Node.js server
over Redis pub-sub. Every edit flows:
Browser -> WebSocket -> Node.js -> Redis -> Node.js -> WebSocket -> Browser
This introduces a single point of failure, makes offline-first workflows
impossible, and creates a centralised trust requirement that is
unacceptable for civic-tech and low-connectivity rural deployments.
The key insight from studying EtherCalc's internals: the server does
not compute, it only relays. Every SocialCalc command is executed
client-side in the browser's JavaScript engine. The server is a message
bus. If we replace that bus with a decentralised one, we get peer-to-peer
collaboration for free.
How the integration works
EtherCalc uses Redis pub-sub channels internally, one per room
(
sc:<room_name>). P2PCalc subscribes to this channel, wraps eachcommand in a typed operation envelope, and broadcasts it via GossipSub
to the topic
p2pcalc/<room_name>. Incoming operations from remotepeers are decoded and injected back into the same Redis channel.
EtherCalc's existing Socket.io layer delivers them to browsers unchanged.
Browser -> WS -> Node.js -> Redis ──> P2PCalc Adapter
│
GossipSub mesh
p2pcalc/
│
Remote P2PCalc Adapter <── Redis <- Node.js <- WS <- Browser
Zero changes to EtherCalc. Zero changes to the browser UI.
Innovations
Hybrid Logical Clocks (HLC) for causal ordering
Wall-clock timestamps break under clock drift and network partitions.
Pure Lamport clocks lose physical time meaning. P2PCalc uses Hybrid
Logical Clocks (Kulkarni et al., 2014), combining physical time with
a logical counter, to achieve causally-consistent operation ordering
across peers without any clock synchronisation protocol.
Multi-Value Register for conflict resolution
Last-Write-Wins silently loses data. If Alice and Bob edit cell A1
concurrently, one edit disappears with no warning. P2PCalc implements
a Multi-Value Register (MVR) that preserves all concurrent versions of
a cell as candidates. Conflicts are surfaced as cell markers directly
in the spreadsheet UI so users can explicitly resolve them, no silent
data loss. A write that causally follows all candidates (higher HLC)
automatically dominates and resolves the conflict.
Replicated Growable Array for structural operations
Row and column insertions/deletions are harder than cell values.
Operational Transformation handles this with transformation functions
that have known correctness issues for spreadsheet structures. P2PCalc
uses an RGA (Roh et al., 2011) that tracks operation intent rather than
absolute position. Concurrent insertions at the same index are
deterministically ordered by peer_id. Deletions use tombstones so
concurrent edits targeting a deleted row can still be applied correctly.
Causal buffering for out-of-order delivery
GossipSub provides best-effort, eventually-consistent delivery.
Operations may arrive out of causal order after reconnection or late
join. P2PCalc attaches causal dependency (op_id of last observed
operation) to each message. The adapter holds operations in a causal
buffer until their declared dependencies have arrived, then flushes in
dependency order. This gives causal consistency on top of GossipSub's
eventual delivery without vector clocks.
Two-phase late-joiner state sync
On joining a sheet, the peer broadcasts a
SNAPSHOT_REQUESTviaGossipSub. Responding peers send
SNAPSHOT_CHUNKoperations. Ifmultiple peers respond simultaneously, P2PCalc selects the peer with
the highest operation count (most history) as authoritative, no
coordination required. After the snapshot applies, operations that
arrived after the snapshot are replayed using the embedded
op_log_fromfield.Local WAL for crash recovery
Every operation is appended to a length-prefixed MessagePack write-ahead
log. On restart, the peer replays its local WAL before requesting a
network snapshot, recovery works without other peers being online, and
the snapshot request covers only the delta.
MessagePack over JSON
Operations are serialised with MessagePack: 30-40% smaller and 2-3x
faster than JSON. The GossipSub heartbeat interval is set to 1 second
(vs the default 2 seconds) to reduce propagation latency without
saturating the mesh.
Operation integrity verification
Every operation carries a SHA-256 content hash. The GossipSub message
validator rejects operations that fail integrity checks at the pub-sub
layer, before forwarding to other mesh peers.
Files added
examples/p2pcalc/
├── README.md problem, approach, all 8 innovations documented
├── requirements.txt
├── p2pcalc/
│ ├── operation.py SocialCalc command parsing, HLC, MessagePack
│ ├── crdt.py MVR (cell conflicts) + RGA (structural ops)
│ ├── adapter.py Redis <-> GossipSub bridge, causal buffer
│ ├── p2p_node.py libp2p host, GossipSub v2.0, topic-per-sheet
│ ├── state_sync.py snapshot assembly, race resolution, WAL
│ └── main.py CLI: --room, --port, --peer, --conflict-policy
└── tests/
└── test_p2pcalc.py 42 unit tests
How to test (no network or Redis required)
Test coverage: HLC monotonicity and causal ordering, SocialCalc command
parsing for all operation types, MessagePack roundtrip, integrity
verification, MVR conflict detection and resolution across all three
policies, RGA concurrent structural merge, SheetCRDT deduplication and
op-log, snapshot race resolution, multi-peer convergence simulation.
How to run end-to-end
References
Author: Dashpreet Singh, dashpreetsinghhanda@gmail.com
IIT Jammu, B.Tech CSE 2024-2028