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,35 +1,38 @@
# hyper-p2p-credit-ledger
Gossip-replicated credit accounts: open, credit, debit, and peer-to-peer transfer.
Gossip-synchronized credit accounts: open, credit, debit, transfer, and balance queries on a shared Hyperswarm topic.
**Category:** Applications (economy)
**Composes with:** `hyper-p2p-bandwidth-broker`, `hyper-p2p-auction-gossip`
**Composes with:** `hyper-p2p-auction-gossip`, `hyper-p2p-marketplace-listing`
**Protocol:** `credit-ledger/v1`
## When to use
Apps that track balances or credits across peers on a shared Hyperswarm topic.
Soft-currency or reputation balances that should converge across peers without a central server.
## When not to use
Authoritative financial ledgers requiring strong consistency (use dedicated consensus).
Strong consistency, double-spend safety, or audit-grade accounting (add consensus or signed operation logs).
## Quick start
```js
const { HyperP2PCreditLedger } = require('hyper-p2p-credit-ledger')
const ledger = new HyperP2PCreditLedger({ topic: 'credits' })
const ledger = new HyperP2PCreditLedger({ topic: 'credits-demo' })
await ledger.ready()
ledger.openAccount('alice', 100)
ledger.transfer('alice', 'bob', 25)
console.log(ledger.balance('bob')) // 25
await ledger.close()
```
## Docs
- [docs/api.md](docs/api.md)
- [docs/architecture.md](docs/architecture.md)
- [examples/basic.js](examples/basic.js)
## Test
@@ -2,25 +2,103 @@
**Protocol:** `credit-ledger/v1`
**Export:** `{ HyperP2PCreditLedger, PROTOCOL }`
## Overview
`HyperP2PCreditLedger` provides gossip-synchronized account balances. Each account holds a numeric `balance`; `credit`, `debit`, and `transfer` update local state and emit `ledger-entry` gossip with the **authoritative balance** after each mutation (not operation logs).
Extends `bare-events` `EventEmitter`.
## Constructor
`new HyperP2PCreditLedger(opts?)``topic`, `keyPair` optional.
```js
const { HyperP2PCreditLedger } = require('hyper-p2p-credit-ledger')
const ledger = new HyperP2PCreditLedger(opts)
```
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `topic` | `string` \| `Buffer` | `null` | Hyperswarm topic |
| `keyPair` | `KeyPair` | random | Signing identity; `peerHex` on entries |
## Lifecycle
### `async ready() → HyperP2PCreditLedger`
Initializes swarm when `topic` set.
### `async close() → void`
Destroys swarm; clears `_accounts`.
### `getStats() → object`
`{ credits, debits, gossipIn, gossipOut, accounts, protocol }`.
## Methods
| Method | Description |
|--------|-------------|
| `openAccount(accountId, initial?)` | Create account |
| `credit(accountId, amount, reason?)` | Add balance |
| `debit(accountId, amount, reason?)` | Subtract balance |
| `transfer(fromId, toId, amount)` | Move credits |
| `balance(accountId)` | number \| null |
| `ready()` / `close()` / `getStats()` | Lifecycle |
### `openAccount(accountId, initial = 0) → account`
Creates account; gossips `ledger-entry` with `kind: 'open'`.
- **Returns:** `{ id, balance, updatedAt }`
- **Throws:** `Error: account exists`
### `credit(accountId, amount, reason = '') → account`
Adds `Math.abs(amount)` to balance; `kind: 'credit'`.
### `debit(accountId, amount, reason = '') → account`
Subtracts `Math.abs(amount)`; `kind: 'debit'`.
### `transfer(fromId, toId, amount) → { from, to, amount, at }`
Atomic pair: debit then credit with linked reasons.
- **Throws:** `unknown account`, `insufficient balance`, `amount must be positive`
### `balance(accountId) → number | null`
Current balance or `null` if unknown.
## Account gossip entry
Remote handler applies **balance snapshot** from gossip (not replay of deltas):
| Field | Type | Description |
|-------|------|-------------|
| `type` | `'ledger-entry'` | Fixed discriminator |
| `accountId` | `string` | Account key |
| `kind` | `'open' \| 'credit' \| 'debit'` | Mutation class |
| `balance` | `number` | Balance after operation |
| `at` | `number` | Timestamp ms |
| `delta` | `number` | Optional; present on credit/debit |
| `reason` | `string` | Optional note |
| `peer` | `string` | Hex pubkey of originator |
## Events
`account`, `credit`, `debit`, `remote-entry`
| Event | When | Payload |
|-------|------|---------|
| `account` | `openAccount` | account |
| `credit` | `credit()` | `{ accountId, balance, delta, reason }` |
| `debit` | `debit()` | same shape |
| `remote-entry` | inbound gossip | full ledger-entry |
## Errors
`account exists`, `unknown account`, `insufficient balance`
`account exists`, `unknown account`, `insufficient balance`, `amount must be positive`, `assertNonEmpty` on ids.
## P2P
Single wire type `ledger-entry`. New accounts created on remote `kind === 'open'`; updates overwrite `balance` when account exists.
## Testing
```bash
npm install && npm test
```
Example: [`../examples/basic.js`](../examples/basic.js).
@@ -1,5 +1,48 @@
# Architecture: hyper-p2p-credit-ledger
Gossip type `ledger-entry`: `accountId`, `kind` (`open`|`credit`|`debit`), `balance`, `delta`, `reason`, `peer`, `at`.
**Category:** `applications-economy` · **Protocol:** `credit-ledger/v1`
Last-writer balance per account on merge. Uses `p2p-bare.js` for swarm wiring.
## Role
Eventually-consistent balance sheet: peers converge on latest gossiped balance per account. Suitable for soft credits and demo economies — not a Byzantine-fault-tolerant ledger without additional consensus.
```mermaid
flowchart LR
App[Wallet / market] --> CL[HyperP2PCreditLedger]
CL --> Acct[_accounts Map]
CL --> Gossip[gossipSend ledger-entry]
```
## Wire messages
| type | direction | fields | behavior |
|------|-----------|--------|----------|
| `ledger-entry` | gossip | `accountId`, `kind`, `balance`, `at`, optional `delta`, `reason`, `peer` | Create on `open`; else overwrite balance |
## Sequence
```mermaid
sequenceDiagram
participant A as Peer A
participant L as CreditLedger
participant B as Peer B
A->>L: transfer(alice, bob, 10)
L->>L: debit + credit locally
L-->>B: ledger-entry (bob balance)
B->>B: apply balance snapshot
```
## State model
| Key | Value |
|-----|-------|
| `_accounts` | `accountId → { id, balance, updatedAt }` |
| `_stats` | credit/debit counters, gossip in/out |
**Merge rule:** last gossip wins per account (by arrival order, not Lamport clock). Transfers are two entries; receivers may briefly see intermediate states.
## Composition
- **`hyper-p2p-auction-gossip`** — pay winner via `debit`/`credit`
- **`hyper-p2p-marketplace-listing`** — price in listing metadata
- **`hyper-p2p-decentralized-oracle`** — FX or reserve rates