Updates
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user