Updates
This commit is contained in:
@@ -31,6 +31,7 @@ await auction.close()
|
||||
|
||||
- [docs/api.md](docs/api.md)
|
||||
- [docs/architecture.md](docs/architecture.md)
|
||||
- [examples/basic.js](examples/basic.js)
|
||||
|
||||
## Test
|
||||
|
||||
|
||||
@@ -1,43 +1,115 @@
|
||||
# API: hyper-p2p-auction-gossip
|
||||
|
||||
**Protocol:** `auction-gossip/v1`
|
||||
**Protocol:** `auction-gossip/v1`
|
||||
|
||||
**Export:** `{ HyperP2PAuctionGossip, PROTOCOL }`
|
||||
|
||||
## Overview
|
||||
|
||||
`HyperP2PAuctionGossip` maintains a peer-merged map of auctions. Each auction tracks an ordered bid list (highest first), lifecycle (`open` → `closed`), and optional metadata. Local mutations gossip over Hyperswarm via `../../_shared/p2p-bare.js` when `topic` is set.
|
||||
|
||||
Extends `bare-events` `EventEmitter`.
|
||||
|
||||
## Constructor
|
||||
|
||||
`new HyperP2PAuctionGossip(opts?)`
|
||||
```js
|
||||
const { HyperP2PAuctionGossip } = require('hyper-p2p-auction-gossip')
|
||||
const auction = new HyperP2PAuctionGossip(opts)
|
||||
```
|
||||
|
||||
| Option | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| `topic` | string \| buffer | Hyperswarm topic (optional for local-only) |
|
||||
| `keyPair` | keyPair | Override identity |
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `topic` | `string` \| `Buffer` | `null` | Hyperswarm topic; omit for local-only |
|
||||
| `keyPair` | `KeyPair` | `hypercore-crypto.keyPair()` | Peer identity; `peerHex` derived from public key |
|
||||
|
||||
### Instance fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `peerHex` | `string` | Hex public key of this peer |
|
||||
| `_auctions` | `Map` | `auctionId → auction` record |
|
||||
|
||||
## Lifecycle
|
||||
|
||||
### `async ready() → HyperP2PAuctionGossip`
|
||||
|
||||
Joins Hyperswarm when `topic` is set and swarm not already active. No-op if `topic` is null.
|
||||
|
||||
### `async close() → void`
|
||||
|
||||
Destroys swarm, clears auction map.
|
||||
|
||||
### `getStats() → object`
|
||||
|
||||
Returns `{ opened, bids, closed, gossipIn, gossipOut, auctions, open, protocol }`.
|
||||
|
||||
## Methods
|
||||
|
||||
| Method | Returns | Description |
|
||||
|--------|---------|-------------|
|
||||
| `openAuction(auctionId, meta?)` | auction | Create open auction |
|
||||
| `placeBid(auctionId, amount, meta?)` | bid | Append bid (sorted desc) |
|
||||
| `closeAuction(auctionId)` | auction \| null | Close; sets `winner` to highest bid |
|
||||
| `getAuction(auctionId)` | auction \| null | Lookup |
|
||||
| `listOpen()` | auction[] | Active auctions |
|
||||
| `ready()` | Promise\<this\> | Join swarm when `topic` set |
|
||||
| `close()` | Promise\<void\> | Teardown |
|
||||
| `getStats()` | object | Counters + protocol |
|
||||
### `openAuction(auctionId, meta = {}) → auction`
|
||||
|
||||
Creates an open auction and gossips `auction-open`.
|
||||
|
||||
- **Parameters:** `auctionId` (non-empty string), `meta` (object, stored on auction)
|
||||
- **Returns:** auction object
|
||||
- **Throws:**
|
||||
- `Error: auction already open`
|
||||
- `assertNonEmpty` on `auctionId`
|
||||
|
||||
**Auction shape:** `{ id, meta, status: 'open', bids: [], openedAt, openedBy: peerHex }`
|
||||
|
||||
### `placeBid(auctionId, amount, meta = {}) → bid`
|
||||
|
||||
Appends bid, re-sorts descending by `amount`, gossips `auction-bid`.
|
||||
|
||||
- **Parameters:** `amount` must be positive number
|
||||
- **Returns:** `{ amount, bidder: peerHex, meta, at }`
|
||||
- **Throws:**
|
||||
- `Error: auction not open`
|
||||
- `Error: amount must be a positive number`
|
||||
|
||||
### `closeAuction(auctionId) → auction | null`
|
||||
|
||||
Sets `status: 'closed'`, `winner` to highest bid (or `null`), gossips `auction-close`.
|
||||
|
||||
- **Returns:** closed auction or `null` if unknown id
|
||||
|
||||
### `getAuction(auctionId) → auction | null`
|
||||
|
||||
### `listOpen() → auction[]`
|
||||
|
||||
All auctions with `status === 'open'`.
|
||||
|
||||
## Events
|
||||
|
||||
| Event | Payload |
|
||||
|-------|---------|
|
||||
| `open` | auction |
|
||||
| `bid` | `{ auctionId, bid }` |
|
||||
| `close` | auction |
|
||||
| `remote-open` | auction |
|
||||
| `remote-bid` | gossip payload |
|
||||
| `remote-close` | auction |
|
||||
| Event | When | Payload |
|
||||
|-------|------|---------|
|
||||
| `open` | Local `openAuction` | auction |
|
||||
| `bid` | Local `placeBid` | `{ auctionId, bid }` |
|
||||
| `close` | Local `closeAuction` | auction |
|
||||
| `remote-open` | Gossip `auction-open` | auction |
|
||||
| `remote-bid` | Gossip `auction-bid` | full gossip payload |
|
||||
| `remote-close` | Gossip `auction-close` | auction |
|
||||
|
||||
## P2P wire (gossip JSON)
|
||||
|
||||
| type | fields | receiver behavior |
|
||||
|------|--------|-------------------|
|
||||
| `auction-open` | `auction` | `Map.set`; emit `remote-open` |
|
||||
| `auction-bid` | `auctionId`, `bid` | append + sort if auction open; emit `remote-bid` |
|
||||
| `auction-close` | `auctionId`, `winner`, `closedAt` | set closed; emit `remote-close` |
|
||||
|
||||
Gossip is skipped until `ready()` has initialized `_peerMsgs`.
|
||||
|
||||
## Errors
|
||||
|
||||
- `auction already open` / `auction not open`
|
||||
- `amount must be a positive number`
|
||||
- Validation via `assertNonEmpty` on ids
|
||||
See [`../../_shared/ERROR_CODES.md`](../../_shared/ERROR_CODES.md). Stable strings: `auction already open`, `auction not open`, `amount must be a positive number`.
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
npm install && npm test
|
||||
```
|
||||
|
||||
Example: [`../examples/basic.js`](../examples/basic.js).
|
||||
|
||||
Integration: [`../../../real_tests/integration/`](../../../real_tests/integration/) — auction-gossip two-node when present.
|
||||
|
||||
@@ -4,18 +4,54 @@
|
||||
|
||||
## Role
|
||||
|
||||
Gossip-synchronized auction state: each peer keeps a map of auctions and bid lists; highest bid wins on close.
|
||||
Gossip-synchronized auction room: peers share auction catalog and bid ordering; closing peer publishes winner snapshot.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
App[Market app] --> AG[HyperP2PAuctionGossip]
|
||||
AG --> Map[_auctions Map]
|
||||
AG --> P2P[p2p-bare gossipSend]
|
||||
P2P --> Peers[Remote bidders]
|
||||
```
|
||||
|
||||
## Sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Seller
|
||||
participant AG as AuctionGossip
|
||||
participant SW as Hyperswarm
|
||||
participant Buyer
|
||||
Seller->>AG: openAuction(lot-1)
|
||||
AG->>SW: auction-open
|
||||
SW-->>Buyer: remote-open
|
||||
Buyer->>AG: placeBid(lot-1, 42)
|
||||
AG->>SW: auction-bid
|
||||
Seller->>AG: closeAuction(lot-1)
|
||||
AG->>SW: auction-close + winner
|
||||
```
|
||||
|
||||
## Wire messages
|
||||
|
||||
| type | Fields |
|
||||
|------|--------|
|
||||
| `auction-open` | `auction` (id, meta, status, bids, openedBy, openedAt) |
|
||||
| `auction-bid` | `auctionId`, `bid` (amount, bidder, meta, at) |
|
||||
| `auction-close` | `auctionId`, `winner`, `closedAt` |
|
||||
| type | direction | fields | behavior |
|
||||
|------|-----------|--------|----------|
|
||||
| `auction-open` | gossip | `auction` | Replace/insert auction in `_auctions` |
|
||||
| `auction-bid` | gossip | `auctionId`, `bid` | Append bid if auction open; sort desc |
|
||||
| `auction-close` | gossip | `auctionId`, `winner`, `closedAt` | Set `status: closed`, assign winner |
|
||||
|
||||
Uses `../../_shared/p2p-bare.js` (`initModuleSwarm`, `gossipSend`).
|
||||
Transport: `initModuleSwarm` + `gossipSend` from `../../_shared/p2p-bare.js`. Protomux channel id: `auction-gossip/v1`.
|
||||
|
||||
## State model
|
||||
|
||||
| Structure | Purpose |
|
||||
|-----------|---------|
|
||||
| `_auctions` | `id → { bids[], status, meta, openedBy, winner? }` |
|
||||
| `_stats` | `opened`, `bids`, `closed`, `gossipIn`, `gossipOut` |
|
||||
|
||||
**Consistency:** last-writer on open; bids append-only per peer; close overwrites status. No CRDT merge on conflicting closes — application should designate an authority or use oracle settlement.
|
||||
|
||||
## Composition
|
||||
|
||||
Pairs with `hyper-p2p-credit-ledger` for settlement and `hyper-p2p-marketplace-listing` for item catalog.
|
||||
- **`hyper-p2p-credit-ledger`** — settle winner payment after `close`
|
||||
- **`hyper-p2p-marketplace-listing`** — catalog items before auction
|
||||
- **`hyper-p2p-decentralized-oracle`** — external price/reserve attestation
|
||||
|
||||
Reference in New Issue
Block a user