This commit is contained in:
Raven Scott
2026-05-20 23:36:32 -04:00
parent a020270cb1
commit be94546cd3
218 changed files with 9189 additions and 3078 deletions
@@ -1,37 +1,31 @@
# hyper-p2p-crdt-version-vector
Per-peer logical clocks for causality and conflict detection.
**Version vector** clock for causal comparison and merge over gossip.
**Protocol:** `crdt-version-vector/v1`
**Category:** State CRDTs · **Protocol:** `crdt-version-vector/v1`
## When to use
- Detecting concurrent edits before merging application state.
- Tracking causality across replicated peers.
Detect concurrent vs ordered events; merge peer clocks from snapshots or wire.
## When not to use
- Storing application payload (use LWW register or OR-map).
- Global numeric counters (use PN-counter).
When you only need LWW timestamps on keys (use LWW register). Full BFT ordering (use `hyper-p2p-causal-consensus`).
## Quick start
```js
const { HyperP2PCrdtVersionVector } = require('hyper-p2p-crdt-version-vector')
const vv = new HyperP2PCrdtVersionVector({ topic: 'vv-demo' })
const { HyperP2PCrdtVersionVector, compareVectors } = require('hyper-p2p-crdt-version-vector')
const vv = new HyperP2PCrdtVersionVector({ topic: process.argv[2] })
await vv.ready()
vv.increment('peer-a')
const order = vv.compare(vv.toJSON(), { 'peer-b': 2 })
await vv.close()
console.log(compareVectors(vv.toJSON(), { 'peer-b': 1 }))
```
## Docs
- [docs/api.md](docs/api.md)
- [docs/architecture.md](docs/architecture.md)
[docs/api.md](docs/api.md) · [docs/architecture.md](docs/architecture.md)
## Test
```bash
npm install && npm test
```
`npm install && npm test` · `bare examples/basic.js [topic]`
@@ -1,21 +1,93 @@
# API: hyper-p2p-crdt-version-vector
**Protocol:** `crdt-version-vector/v1` · **Export:** `HyperP2PCrdtVersionVector`, `compareVectors`
**Protocol:** `crdt-version-vector/v1`
**Export:** `{ HyperP2PCrdtVersionVector, PROTOCOL, compareVectors }`
## Overview
`HyperP2PCrdtVersionVector` tracks per-peer logical clocks. `increment(peer)` bumps one slot and gossips. `merge(other)` max-merges remote vector. `compare(a, b)` (also exported as `compareVectors`) returns causal relation: `equal`, `before`, `after`, or `concurrent`.
## Constructor
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `topic` | `string` \| `Buffer` \| `null` | `null` | Hyperswarm topic |
| `keyPair` | `KeyPair` | `hypercore-crypto.keyPair()` | Swarm identity |
## Methods
### `increment(peer)`
Increments this peer's counter and gossips.
- **Returns:** `number` — new counter for `peer`
- **Throws:** `ValidationError: peer is required`
- **Gossip:** `crdt-version-vector-sync`
### `merge(other)`
Pointwise max merge: `other` is `{ peerId: count }`.
- **Parameters:** `other` — plain object `peer → number`
- **Returns:** `number` — slots updated
- **Throws:** —
### `compare(a, b)`
Returns `'equal' | 'before' | 'after' | 'concurrent'`.
Delegates to `compareVectors(a, b)`.
### `toJSON()` / `getStats()` / `ready()` / `close()`
- **Returns:** `'equal' | 'before' | 'after' | 'concurrent'`
Standard lifecycle; gossip message `crdt-version-vector-sync` with `{ peer, value }`.
### `toJSON()`
- **Returns:** `object` — serializable clock map
### `async ready()` / `async close()`
Standard gossip module lifecycle.
### `getStats()`
| Field | Type | Description |
|-------|------|-------------|
| `ops` | `number` | `increment` count |
| `gossipIn` / `gossipOut` | `number` | Wire counters |
| `peers` | `number` | Distinct peer keys in clock |
| `protocol` | `string` | `crdt-version-vector/v1` |
## compareVectors(a, b)
Standalone export for tests and `hyper-p2p-causal-consensus` integration.
| Result | Meaning |
|--------|---------|
| `equal` | All slots equal |
| `after` | `a` strictly dominates `b` |
| `before` | `b` strictly dominates `a` |
| `concurrent` | Neither dominates |
## Events
None emitted.
## getStats()
Shallow copy with `peers` count.
## Wire
| type | fields | direction | behavior |
|------|--------|-----------|----------|
| `crdt-version-vector-sync` | `peer`, `value` | bidirectional | `merge({ [peer]: value })` |
## Errors
| Message | Source |
|---------|--------|
| `peer is required` | `increment` |
## Testing
```bash
cd modules/state-crdts/hyper-p2p-crdt-version-vector
npm install && npm test
```
Unit-test `compareVectors` without network; integration uses shared `topic`.
@@ -1,13 +1,24 @@
# Architecture: hyper-p2p-crdt-version-vector
## Model
**Protocol:** `crdt-version-vector/v1`
Map of peer → monotonic integer. `increment(peer)` only advances the local peer entry on this replica; remote peers advance via gossip merge (max).
```mermaid
flowchart LR
App --> VV[HyperP2PCrdtVersionVector]
VV --> Clock["_clock Map"]
VV --> Cmp[compareVectors]
```
## Compare
## Wire messages
`compareVectors` implements standard dominates relation: `after` if all components ≥ and at least one >; `before` if dominated; `concurrent` if incomparable; `equal` if identical.
| type | fields | direction | behavior |
|------|--------|-----------|----------|
| `crdt-version-vector-sync` | `peer`, `value` | bidirectional | Per-peer max merge |
## Gossip
## State model
`initModuleSwarm` when `topic` set; inbound sync applies max per peer without re-broadcast.
`Map<peerId, number>` — only increases per peer via merge or local increment.
## Composition
Used by `hyper-p2p-causal-consensus` (`integrateVectorClock`) and collab stacks for conflict UI.
@@ -2,10 +2,14 @@ require('bare-process/global')
const { HyperP2PCrdtVersionVector, compareVectors } = require('../index.js')
async function main () {
const vv = new HyperP2PCrdtVersionVector()
vv.increment('local')
vv.merge({ remote: 3 })
console.log(vv.toJSON(), compareVectors(vv.toJSON(), { local: 1 }), vv.getStats())
const vv = new HyperP2PCrdtVersionVector({ topic: process.argv[2] || null })
await vv.ready()
vv.increment('peer-a')
vv.increment('peer-a')
vv.merge({ 'peer-b': 3 })
console.log('clock:', vv.toJSON())
console.log('compare:', compareVectors(vv.toJSON(), { 'peer-b': 3 }))
console.log('stats:', vv.getStats())
await vv.close()
}