@bitsocial/pubsub-voting 0.7.5 → 0.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/client/voter.d.ts +10 -0
- package/dist/client/voter.js +19 -1
- package/package.json +14 -3
package/README.md
CHANGED
|
@@ -34,6 +34,18 @@ See [DESIGN.md](./DESIGN.md) for the full rationale, including how this resists
|
|
|
34
34
|
|
|
35
35
|
## Usage
|
|
36
36
|
|
|
37
|
+
Hosts with a dedicated votes blockstore can use `voter.retainsBlock(cid)` during garbage
|
|
38
|
+
collection. This synchronous, network-free check protects every joined contest's admitted
|
|
39
|
+
bundles (including provisional votes and verified fallback winners) and its last encoded
|
|
40
|
+
checkpoint root/chunks. It compares multihashes, so filesystem stores that enumerate CIDs
|
|
41
|
+
with another codec work too. Recheck immediately before deletion under your blockstore's
|
|
42
|
+
operation lock; also protect concurrent reads/writes and retain superseded blocks for a
|
|
43
|
+
grace period so peers can finish pulling older checkpoints. For a shared store, retain
|
|
44
|
+
other applications' blocks too. The method does not run GC, refresh checkpoints, or impose
|
|
45
|
+
a storage quota. Persisted checkpoint snapshots are self-contained and must be retained
|
|
46
|
+
independently of the blockstore.
|
|
47
|
+
|
|
48
|
+
|
|
37
49
|
The library never starts a node and never takes a host SDK (there is no `pkc` argument). A host passes its own running Helia node in directly and injects its seams into a single `PubsubVoter`. **Identity is not one of the seams**: the voting wallet (`VoteSigner`) belongs to each ballot, passed to `createContestVote`, so one voter on the host's shared node publishes for as many wallets as the host holds keys for — and a client that only renders tallies never touches key material.
|
|
38
50
|
|
|
39
51
|
| Seam | Type | Required | Purpose |
|
package/dist/client/voter.d.ts
CHANGED
|
@@ -348,6 +348,15 @@ export interface VoteClient {
|
|
|
348
348
|
* `createContest` / `update()` after.
|
|
349
349
|
*/
|
|
350
350
|
stop(): Promise<void>;
|
|
351
|
+
/**
|
|
352
|
+
* Synchronous GC retention check for joined contests: admitted bundles (including
|
|
353
|
+
* pending verification and fallback winners) and the last encoded checkpoint.
|
|
354
|
+
* Compares multihashes, since filesystem stores may enumerate another CID codec.
|
|
355
|
+
* Recheck immediately before deletion under the host's blockstore lock; protect
|
|
356
|
+
* in-flight reads/writes, other users of a shared store, and a grace period for
|
|
357
|
+
* superseded checkpoints. No I/O and no permission to delete a shared store's data.
|
|
358
|
+
*/
|
|
359
|
+
retainsBlock(cid: CID): boolean;
|
|
351
360
|
/**
|
|
352
361
|
* Terminal teardown (mirrors pkc-js `destroy`): leave every topic, unregister the fetch
|
|
353
362
|
* responder, and mark the voter and all its contests destroyed. Unlike `stop`, this is NOT
|
|
@@ -468,6 +477,7 @@ export declare class PubsubVoter implements VoteClient {
|
|
|
468
477
|
votes: Vote[];
|
|
469
478
|
signer: VoteSigner;
|
|
470
479
|
}): Promise<ContestVote>;
|
|
480
|
+
retainsBlock(cid: CID): boolean;
|
|
471
481
|
stop(): Promise<void>;
|
|
472
482
|
destroy(): Promise<void>;
|
|
473
483
|
}
|
package/dist/client/voter.js
CHANGED
|
@@ -20,7 +20,7 @@ import { makeVerdictCache } from "../verify/cache.js";
|
|
|
20
20
|
import { makeNameResolutionCache } from "../verify/name-resolution-cache.js";
|
|
21
21
|
import { makeStorage } from "../storage/node.js";
|
|
22
22
|
import { makeAnnouncer } from "../transport/announce/node.js";
|
|
23
|
-
import { encode as encodeDagCbor } from "@ipld/dag-cbor";
|
|
23
|
+
import { encode as encodeDagCbor, code as dagCborCode } from "@ipld/dag-cbor";
|
|
24
24
|
import { sha256 } from "viem";
|
|
25
25
|
import { makeBackgroundVerifier } from "../verify/background.js";
|
|
26
26
|
import { preflightCommunityNames } from "../verify/name-preflight.js";
|
|
@@ -1110,6 +1110,10 @@ class ContestEngine {
|
|
|
1110
1110
|
throw new VoterDestroyedError();
|
|
1111
1111
|
if (this.#joined)
|
|
1112
1112
|
return;
|
|
1113
|
+
// A host may have collected this stopped contest's old checkpoint blocks.
|
|
1114
|
+
// Re-encode/re-store them before serving the cached root on a re-join.
|
|
1115
|
+
this.#checkpointDirty = true;
|
|
1116
|
+
this.#rootRecordCache = undefined;
|
|
1113
1117
|
const limit = pLimit(GATE_CONCURRENCY);
|
|
1114
1118
|
const gate = makeGossipGate({
|
|
1115
1119
|
decodeMessage: decodeVoteMessage,
|
|
@@ -1768,6 +1772,13 @@ class ContestEngine {
|
|
|
1768
1772
|
latestCheckpointRoot() {
|
|
1769
1773
|
return this.#rootRecordCache?.record.root;
|
|
1770
1774
|
}
|
|
1775
|
+
/** Keep all admitted bundles, including pending checks and verified fallback winners. */
|
|
1776
|
+
retainsBlock(cid) {
|
|
1777
|
+
if (!this.#joined)
|
|
1778
|
+
return false;
|
|
1779
|
+
const key = CID.createV1(dagCborCode, cid.multihash).toString();
|
|
1780
|
+
return this.#checks.has(key) || (this.#rootRecordCache?.blocks.some((block) => block.cid.toString() === key) ?? false);
|
|
1781
|
+
}
|
|
1771
1782
|
/**
|
|
1772
1783
|
* The current root record plus its checkpoint chunk blocks, for the bulk responder's inline
|
|
1773
1784
|
* path (see `BulkFetchRootRecord.chunkBlocks`): the blocks are already in hand from the same
|
|
@@ -2528,6 +2539,13 @@ export class PubsubVoter {
|
|
|
2528
2539
|
this.#unregisterResponder();
|
|
2529
2540
|
}
|
|
2530
2541
|
};
|
|
2542
|
+
retainsBlock(cid) {
|
|
2543
|
+
for (const engine of this.#engines.values()) {
|
|
2544
|
+
if (engine.retainsBlock(cid))
|
|
2545
|
+
return true;
|
|
2546
|
+
}
|
|
2547
|
+
return false;
|
|
2548
|
+
}
|
|
2531
2549
|
async stop() {
|
|
2532
2550
|
// Reset each read view (detach its engine listeners, clear `#subscribed`) so it can
|
|
2533
2551
|
// `update()` again — this is what keeps `stop()` reusable. Then leave any engine with no
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitsocial/pubsub-voting",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.7",
|
|
4
4
|
"description": "Trustless pubsub voting over a shared libp2p/Helia node.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "GPL-3.0-or-later",
|
|
@@ -74,11 +74,11 @@
|
|
|
74
74
|
"@libp2p/identify": "4.1.13",
|
|
75
75
|
"@libp2p/tcp": "11.0.27",
|
|
76
76
|
"@multiformats/multiaddr": "13.0.3",
|
|
77
|
-
"@pkcprotocol/pkc-js": "0.0.
|
|
77
|
+
"@pkcprotocol/pkc-js": "0.0.96",
|
|
78
78
|
"@release-it/conventional-changelog": "10.0.6",
|
|
79
79
|
"@types/better-sqlite3": "7.6.13",
|
|
80
80
|
"@vitest/coverage-v8": "4.1.11",
|
|
81
|
-
"commitizen": "4.3.
|
|
81
|
+
"commitizen": "4.3.2",
|
|
82
82
|
"cz-conventional-changelog": "3.3.0",
|
|
83
83
|
"husky": "9.1.7",
|
|
84
84
|
"libp2p": "3.3.9",
|
|
@@ -86,5 +86,16 @@
|
|
|
86
86
|
"strip-json-comments": "5.0.3",
|
|
87
87
|
"typescript": "5.9.3",
|
|
88
88
|
"vitest": "4.1.11"
|
|
89
|
+
},
|
|
90
|
+
"overrides": {
|
|
91
|
+
"@pkcprotocol/pkc-js": {
|
|
92
|
+
"undici": "7.29.0",
|
|
93
|
+
"uuid": "13.0.1",
|
|
94
|
+
"ws": "8.21.3"
|
|
95
|
+
},
|
|
96
|
+
"protobufjs": "7.6.6",
|
|
97
|
+
"release-it": {
|
|
98
|
+
"undici": "6.28.0"
|
|
99
|
+
}
|
|
89
100
|
}
|
|
90
101
|
}
|