@optimystic/db-p2p 0.24.2 → 0.25.0

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.
Files changed (249) hide show
  1. package/{readme.md → README.md} +688 -668
  2. package/dist/src/cluster/block-transfer-service.d.ts +133 -10
  3. package/dist/src/cluster/block-transfer-service.d.ts.map +1 -1
  4. package/dist/src/cluster/block-transfer-service.js +155 -9
  5. package/dist/src/cluster/block-transfer-service.js.map +1 -1
  6. package/dist/src/cluster/block-transfer.d.ts +67 -7
  7. package/dist/src/cluster/block-transfer.d.ts.map +1 -1
  8. package/dist/src/cluster/block-transfer.js +127 -24
  9. package/dist/src/cluster/block-transfer.js.map +1 -1
  10. package/dist/src/cluster/certified-claims.d.ts +170 -0
  11. package/dist/src/cluster/certified-claims.d.ts.map +1 -0
  12. package/dist/src/cluster/certified-claims.js +200 -0
  13. package/dist/src/cluster/certified-claims.js.map +1 -0
  14. package/dist/src/cluster/client-signature-verifier.d.ts +23 -0
  15. package/dist/src/cluster/client-signature-verifier.d.ts.map +1 -0
  16. package/dist/src/cluster/client-signature-verifier.js +33 -0
  17. package/dist/src/cluster/client-signature-verifier.js.map +1 -0
  18. package/dist/src/cluster/cluster-policy.d.ts +9 -3
  19. package/dist/src/cluster/cluster-policy.d.ts.map +1 -1
  20. package/dist/src/cluster/cluster-policy.js +111 -27
  21. package/dist/src/cluster/cluster-policy.js.map +1 -1
  22. package/dist/src/cluster/cluster-repo.d.ts +140 -11
  23. package/dist/src/cluster/cluster-repo.d.ts.map +1 -1
  24. package/dist/src/cluster/cluster-repo.js +485 -29
  25. package/dist/src/cluster/cluster-repo.js.map +1 -1
  26. package/dist/src/cluster/commit-proof.d.ts +134 -0
  27. package/dist/src/cluster/commit-proof.d.ts.map +1 -0
  28. package/dist/src/cluster/commit-proof.js +263 -0
  29. package/dist/src/cluster/commit-proof.js.map +1 -0
  30. package/dist/src/cluster/quorum-restore.d.ts +95 -8
  31. package/dist/src/cluster/quorum-restore.d.ts.map +1 -1
  32. package/dist/src/cluster/quorum-restore.js +113 -14
  33. package/dist/src/cluster/quorum-restore.js.map +1 -1
  34. package/dist/src/cluster/rebalance-monitor.d.ts +84 -0
  35. package/dist/src/cluster/rebalance-monitor.d.ts.map +1 -1
  36. package/dist/src/cluster/rebalance-monitor.js +207 -5
  37. package/dist/src/cluster/rebalance-monitor.js.map +1 -1
  38. package/dist/src/cluster/reconcile-block.d.ts +27 -7
  39. package/dist/src/cluster/reconcile-block.d.ts.map +1 -1
  40. package/dist/src/cluster/reconcile-block.js +179 -38
  41. package/dist/src/cluster/reconcile-block.js.map +1 -1
  42. package/dist/src/cluster/service.d.ts +13 -4
  43. package/dist/src/cluster/service.d.ts.map +1 -1
  44. package/dist/src/cluster/service.js +17 -18
  45. package/dist/src/cluster/service.js.map +1 -1
  46. package/dist/src/cluster/spread-on-churn.d.ts +8 -2
  47. package/dist/src/cluster/spread-on-churn.d.ts.map +1 -1
  48. package/dist/src/cluster/spread-on-churn.js +7 -7
  49. package/dist/src/cluster/spread-on-churn.js.map +1 -1
  50. package/dist/src/cohort-topic/host.d.ts.map +1 -1
  51. package/dist/src/cohort-topic/host.js +8 -7
  52. package/dist/src/cohort-topic/host.js.map +1 -1
  53. package/dist/src/cohort-topic/stream-util.d.ts +7 -6
  54. package/dist/src/cohort-topic/stream-util.d.ts.map +1 -1
  55. package/dist/src/cohort-topic/stream-util.js +14 -41
  56. package/dist/src/cohort-topic/stream-util.js.map +1 -1
  57. package/dist/src/dispute/cascade.d.ts +7 -8
  58. package/dist/src/dispute/cascade.d.ts.map +1 -1
  59. package/dist/src/dispute/cascade.js +24 -7
  60. package/dist/src/dispute/cascade.js.map +1 -1
  61. package/dist/src/dispute/dispute-service.d.ts +5 -0
  62. package/dist/src/dispute/dispute-service.d.ts.map +1 -1
  63. package/dist/src/dispute/dispute-service.js +13 -3
  64. package/dist/src/dispute/dispute-service.js.map +1 -1
  65. package/dist/src/dispute/invalidation.d.ts +33 -15
  66. package/dist/src/dispute/invalidation.d.ts.map +1 -1
  67. package/dist/src/dispute/invalidation.js +127 -34
  68. package/dist/src/dispute/invalidation.js.map +1 -1
  69. package/dist/src/dispute/service.d.ts.map +1 -1
  70. package/dist/src/dispute/service.js +2 -1
  71. package/dist/src/dispute/service.js.map +1 -1
  72. package/dist/src/index.d.ts +7 -0
  73. package/dist/src/index.d.ts.map +1 -1
  74. package/dist/src/index.js +7 -0
  75. package/dist/src/index.js.map +1 -1
  76. package/dist/src/libp2p-key-network.d.ts +14 -10
  77. package/dist/src/libp2p-key-network.d.ts.map +1 -1
  78. package/dist/src/libp2p-key-network.js +28 -58
  79. package/dist/src/libp2p-key-network.js.map +1 -1
  80. package/dist/src/libp2p-node-base.d.ts +38 -7
  81. package/dist/src/libp2p-node-base.d.ts.map +1 -1
  82. package/dist/src/libp2p-node-base.js +148 -63
  83. package/dist/src/libp2p-node-base.js.map +1 -1
  84. package/dist/src/network/open-protocol-stream.d.ts +56 -0
  85. package/dist/src/network/open-protocol-stream.d.ts.map +1 -0
  86. package/dist/src/network/open-protocol-stream.js +60 -0
  87. package/dist/src/network/open-protocol-stream.js.map +1 -0
  88. package/dist/src/network/register-protocol-handler.d.ts +66 -0
  89. package/dist/src/network/register-protocol-handler.d.ts.map +1 -0
  90. package/dist/src/network/register-protocol-handler.js +51 -0
  91. package/dist/src/network/register-protocol-handler.js.map +1 -0
  92. package/dist/src/peer-address-book.d.ts +60 -2
  93. package/dist/src/peer-address-book.d.ts.map +1 -1
  94. package/dist/src/peer-address-book.js +89 -1
  95. package/dist/src/peer-address-book.js.map +1 -1
  96. package/dist/src/pend-validation.d.ts +65 -0
  97. package/dist/src/pend-validation.d.ts.map +1 -0
  98. package/dist/src/pend-validation.js +69 -0
  99. package/dist/src/pend-validation.js.map +1 -0
  100. package/dist/src/reactivity/notify-transport.d.ts.map +1 -1
  101. package/dist/src/reactivity/notify-transport.js +2 -1
  102. package/dist/src/reactivity/notify-transport.js.map +1 -1
  103. package/dist/src/reactivity/push-state-gossip.d.ts.map +1 -1
  104. package/dist/src/reactivity/push-state-gossip.js +2 -1
  105. package/dist/src/reactivity/push-state-gossip.js.map +1 -1
  106. package/dist/src/repo/cluster-coordinator.d.ts +32 -6
  107. package/dist/src/repo/cluster-coordinator.d.ts.map +1 -1
  108. package/dist/src/repo/cluster-coordinator.js +44 -10
  109. package/dist/src/repo/cluster-coordinator.js.map +1 -1
  110. package/dist/src/repo/coordinator-repo.d.ts +181 -25
  111. package/dist/src/repo/coordinator-repo.d.ts.map +1 -1
  112. package/dist/src/repo/coordinator-repo.js +650 -62
  113. package/dist/src/repo/coordinator-repo.js.map +1 -1
  114. package/dist/src/repo/served-repo-proxy.d.ts +30 -0
  115. package/dist/src/repo/served-repo-proxy.d.ts.map +1 -0
  116. package/dist/src/repo/served-repo-proxy.js +50 -0
  117. package/dist/src/repo/served-repo-proxy.js.map +1 -0
  118. package/dist/src/repo/service.d.ts +16 -1
  119. package/dist/src/repo/service.d.ts.map +1 -1
  120. package/dist/src/repo/service.js +18 -16
  121. package/dist/src/repo/service.js.map +1 -1
  122. package/dist/src/reputation/types.d.ts +3 -1
  123. package/dist/src/reputation/types.d.ts.map +1 -1
  124. package/dist/src/reputation/types.js +3 -1
  125. package/dist/src/reputation/types.js.map +1 -1
  126. package/dist/src/rn.d.ts +24 -2
  127. package/dist/src/rn.d.ts.map +1 -1
  128. package/dist/src/rn.js +33 -4
  129. package/dist/src/rn.js.map +1 -1
  130. package/dist/src/storage/block-archive.d.ts +153 -0
  131. package/dist/src/storage/block-archive.d.ts.map +1 -0
  132. package/dist/src/storage/block-archive.js +194 -0
  133. package/dist/src/storage/block-archive.js.map +1 -0
  134. package/dist/src/storage/block-latch.d.ts +92 -0
  135. package/dist/src/storage/block-latch.d.ts.map +1 -0
  136. package/dist/src/storage/block-latch.js +132 -0
  137. package/dist/src/storage/block-latch.js.map +1 -0
  138. package/dist/src/storage/block-storage.d.ts +169 -12
  139. package/dist/src/storage/block-storage.d.ts.map +1 -1
  140. package/dist/src/storage/block-storage.js +415 -152
  141. package/dist/src/storage/block-storage.js.map +1 -1
  142. package/dist/src/storage/cached-raw-storage.d.ts +5 -1
  143. package/dist/src/storage/cached-raw-storage.d.ts.map +1 -1
  144. package/dist/src/storage/cached-raw-storage.js +16 -1
  145. package/dist/src/storage/cached-raw-storage.js.map +1 -1
  146. package/dist/src/storage/cached-store-driver.d.ts +14 -4
  147. package/dist/src/storage/cached-store-driver.d.ts.map +1 -1
  148. package/dist/src/storage/cached-store-driver.js +39 -5
  149. package/dist/src/storage/cached-store-driver.js.map +1 -1
  150. package/dist/src/storage/i-block-storage.d.ts +70 -19
  151. package/dist/src/storage/i-block-storage.d.ts.map +1 -1
  152. package/dist/src/storage/i-block-storage.js +21 -1
  153. package/dist/src/storage/i-block-storage.js.map +1 -1
  154. package/dist/src/storage/i-raw-storage.d.ts +38 -0
  155. package/dist/src/storage/i-raw-storage.d.ts.map +1 -1
  156. package/dist/src/storage/kv-raw-storage.d.ts +14 -8
  157. package/dist/src/storage/kv-raw-storage.d.ts.map +1 -1
  158. package/dist/src/storage/kv-raw-storage.js +29 -9
  159. package/dist/src/storage/kv-raw-storage.js.map +1 -1
  160. package/dist/src/storage/memory-store-driver.d.ts +4 -1
  161. package/dist/src/storage/memory-store-driver.d.ts.map +1 -1
  162. package/dist/src/storage/memory-store-driver.js +22 -2
  163. package/dist/src/storage/memory-store-driver.js.map +1 -1
  164. package/dist/src/storage/raw-store-codec.js +2 -2
  165. package/dist/src/storage/raw-store-driver.d.ts +44 -3
  166. package/dist/src/storage/raw-store-driver.d.ts.map +1 -1
  167. package/dist/src/storage/shared-cache-pool.d.ts +60 -3
  168. package/dist/src/storage/shared-cache-pool.d.ts.map +1 -1
  169. package/dist/src/storage/shared-cache-pool.js +68 -4
  170. package/dist/src/storage/shared-cache-pool.js.map +1 -1
  171. package/dist/src/storage/storage-repo.d.ts +204 -26
  172. package/dist/src/storage/storage-repo.d.ts.map +1 -1
  173. package/dist/src/storage/storage-repo.js +422 -119
  174. package/dist/src/storage/storage-repo.js.map +1 -1
  175. package/dist/src/storage/store-identity.d.ts +21 -0
  176. package/dist/src/storage/store-identity.d.ts.map +1 -0
  177. package/dist/src/storage/store-identity.js +33 -0
  178. package/dist/src/storage/store-identity.js.map +1 -0
  179. package/dist/src/storage/struct.d.ts +15 -0
  180. package/dist/src/storage/struct.d.ts.map +1 -1
  181. package/dist/src/storage/with-read-cache.d.ts +92 -0
  182. package/dist/src/storage/with-read-cache.d.ts.map +1 -0
  183. package/dist/src/storage/with-read-cache.js +132 -0
  184. package/dist/src/storage/with-read-cache.js.map +1 -0
  185. package/dist/src/sync/service.d.ts +13 -3
  186. package/dist/src/sync/service.d.ts.map +1 -1
  187. package/dist/src/sync/service.js +7 -30
  188. package/dist/src/sync/service.js.map +1 -1
  189. package/dist/src/testing/mesh-harness.d.ts +75 -2
  190. package/dist/src/testing/mesh-harness.d.ts.map +1 -1
  191. package/dist/src/testing/mesh-harness.js +193 -81
  192. package/dist/src/testing/mesh-harness.js.map +1 -1
  193. package/dist/src/testing/raw-storage-conformance.d.ts +8 -0
  194. package/dist/src/testing/raw-storage-conformance.d.ts.map +1 -1
  195. package/dist/src/testing/raw-storage-conformance.js +97 -2
  196. package/dist/src/testing/raw-storage-conformance.js.map +1 -1
  197. package/package.json +5 -6
  198. package/src/cluster/block-transfer-service.ts +528 -312
  199. package/src/cluster/block-transfer.ts +547 -424
  200. package/src/cluster/certified-claims.ts +319 -0
  201. package/src/cluster/client-signature-verifier.ts +34 -0
  202. package/src/cluster/cluster-policy.ts +294 -203
  203. package/src/cluster/cluster-repo.ts +2501 -2034
  204. package/src/cluster/commit-proof.ts +344 -0
  205. package/src/cluster/quorum-restore.ts +348 -223
  206. package/src/cluster/rebalance-monitor.ts +539 -254
  207. package/src/cluster/reconcile-block.ts +404 -203
  208. package/src/cluster/service.ts +23 -20
  209. package/src/cluster/spread-on-churn.ts +352 -346
  210. package/src/cohort-topic/host.ts +8 -7
  211. package/src/cohort-topic/stream-util.ts +14 -43
  212. package/src/dispute/cascade.ts +31 -15
  213. package/src/dispute/dispute-service.ts +706 -696
  214. package/src/dispute/invalidation.ts +162 -52
  215. package/src/dispute/service.ts +126 -125
  216. package/src/index.ts +7 -0
  217. package/src/libp2p-key-network.ts +1204 -1235
  218. package/src/libp2p-node-base.ts +185 -70
  219. package/src/network/open-protocol-stream.ts +87 -0
  220. package/src/network/register-protocol-handler.ts +85 -0
  221. package/src/peer-address-book.ts +96 -2
  222. package/src/pend-validation.ts +89 -0
  223. package/src/reactivity/notify-transport.ts +2 -1
  224. package/src/reactivity/push-state-gossip.ts +2 -1
  225. package/src/repo/cluster-coordinator.ts +65 -11
  226. package/src/repo/coordinator-repo.ts +729 -71
  227. package/src/repo/served-repo-proxy.ts +55 -0
  228. package/src/repo/service.ts +25 -16
  229. package/src/reputation/types.ts +3 -1
  230. package/src/rn.ts +34 -4
  231. package/src/storage/block-archive.ts +242 -0
  232. package/src/storage/block-latch.ts +144 -0
  233. package/src/storage/block-storage.ts +787 -498
  234. package/src/storage/cached-raw-storage.ts +21 -1
  235. package/src/storage/cached-store-driver.ts +898 -859
  236. package/src/storage/i-block-storage.ts +172 -116
  237. package/src/storage/i-raw-storage.ts +40 -0
  238. package/src/storage/kv-raw-storage.ts +34 -9
  239. package/src/storage/memory-store-driver.ts +26 -2
  240. package/src/storage/raw-store-codec.ts +42 -42
  241. package/src/storage/raw-store-driver.ts +45 -3
  242. package/src/storage/shared-cache-pool.ts +516 -452
  243. package/src/storage/storage-repo.ts +1421 -1014
  244. package/src/storage/store-identity.ts +41 -0
  245. package/src/storage/struct.ts +52 -34
  246. package/src/storage/with-read-cache.ts +182 -0
  247. package/src/sync/service.ts +178 -198
  248. package/src/testing/mesh-harness.ts +524 -344
  249. package/src/testing/raw-storage-conformance.ts +114 -2
@@ -0,0 +1,263 @@
1
+ import { canonicalBlockHash, clusterVoteVerificationPayload, computeClusterCommitHash, computeClusterMessageHash, computeClusterPromiseHash, membershipDigestFromIds } from "@optimystic/db-core";
2
+ import { peerIdFromString } from "@libp2p/peer-id";
3
+ import { publicKeyFromRaw } from "@libp2p/crypto/keys";
4
+ import { fromString as uint8ArrayFromString } from "uint8arrays/from-string";
5
+ /**
6
+ * Project a consensus-committed {@link ClusterRecord} into a {@link BlockCommitProof}. A cheap
7
+ * projection — no hashing, no signature work; the record's maps are carried by reference (the
8
+ * storage layer JSON-snapshots on save). Returns `undefined` for a v1 / unversioned record: its
9
+ * hashes bind no peer set, so its signer list is unbound and it can never be certified (the caller
10
+ * logs the skip).
11
+ */
12
+ export function buildBlockCommitProof(record) {
13
+ if (record.membershipVersion !== 2 || typeof record.membershipDigest !== 'string') {
14
+ return undefined;
15
+ }
16
+ return {
17
+ v: 1,
18
+ messageHash: record.messageHash,
19
+ message: record.message,
20
+ promises: record.promises,
21
+ commits: record.commits,
22
+ membershipVersion: 2,
23
+ membershipDigest: record.membershipDigest,
24
+ peerIds: Object.keys(record.peers).sort()
25
+ };
26
+ }
27
+ /**
28
+ * Verify that `proof` certifies the CLAIM — `claim.blockId` at `claim.rev` under `claim.actionId` —
29
+ * without needing the block bytes. Pure and total on hostile input: never throws; every failure is a
30
+ * distinguishable {@link ProofFailure}.
31
+ *
32
+ * Mirrors the outcome discipline of `ClusterMember.verifySignature`: a malformed or unbound signer is
33
+ * a verification failure that must NEVER be turned into a reputation penalty, because the identity
34
+ * behind it was not proven. Callers deciding attributability must key off the reason values, and
35
+ * treat `unknown-signer` / `non-ed25519-signer` / `malformed-signature` / `malformed-proof` as
36
+ * non-attributable.
37
+ *
38
+ * **Why both thresholds.** The commit-round vote is cast blind — a member signs the commit whenever
39
+ * the approve-promises reach super-majority, regardless of its own promise vote. The promise-round
40
+ * approvals are the votes that carry "I validated this message, including its content digest".
41
+ * Requiring only the commit approvals would count signatures that attest to nothing; requiring only
42
+ * the promises would accept a record the cohort never actually committed. Require both.
43
+ *
44
+ * The claim step (a `{ commit }` op whose `blockIds`/`actionId`/`rev` all match) is what stops
45
+ * replay: a genuine proof for rev 5 presented for rev 9, or for a different block id, dies there.
46
+ *
47
+ * ## What a passing verdict does NOT prove — two caller obligations
48
+ *
49
+ * 1. **The signers are not bound to the block.** A verdict says "the cohort in `proof.peerIds`
50
+ * agreed", never "that is the cohort responsible for `claim.blockId`". Any attacker who controls
51
+ * N keys can stand up their own N-peer cohort, sign a commit for any block id at any revision,
52
+ * and produce a proof that verifies here. Nothing offline can close this: a block's cohort is
53
+ * chosen by live placement and rotates over history, so `peerIds` cannot be checked against a
54
+ * fixed expected set. A caller accepting proofs from untrusted peers MUST corroborate the cohort
55
+ * separately (overlap with the block's currently-derived cohort, or a membership anchor — see
56
+ * `feat-cluster-membership-threshold-cert-anchoring`). Both repair paths do this via
57
+ * `cluster/certified-claims.ts`, whose layer 2 logs the overlap and surfaces the unanchored
58
+ * residual; the anchor itself is still open.
59
+ * 2. **Cost is attacker-chosen.** This performs one Ed25519 verify per approve vote and hashes the
60
+ * whole message; nothing here caps `peerIds`, the vote maps, or the message. A caller reading a
61
+ * proof off the wire must bound its size and cohort count BEFORE calling.
62
+ */
63
+ export async function verifyBlockCommitProofClaim(proof, claim, thresholds) {
64
+ try {
65
+ if (proof === null || typeof proof !== 'object') {
66
+ return { ok: false, reason: 'malformed-proof' };
67
+ }
68
+ // A version this verifier does not implement is never half-verified. `membershipVersion !== 2`
69
+ // means the signer list is unbound (v1 / unversioned history) — never certifiable.
70
+ if (proof.v !== 1 || proof.membershipVersion !== 2) {
71
+ return { ok: false, reason: 'legacy-record' };
72
+ }
73
+ if (typeof proof.messageHash !== 'string'
74
+ || typeof proof.membershipDigest !== 'string'
75
+ || proof.message === null || typeof proof.message !== 'object'
76
+ || !Array.isArray(proof.message.operations)
77
+ || !isVoteMap(proof.promises) || !isVoteMap(proof.commits)
78
+ || !Array.isArray(proof.peerIds) || !proof.peerIds.every(id => typeof id === 'string')) {
79
+ return { ok: false, reason: 'malformed-proof' };
80
+ }
81
+ // A duplicated id in the denominator list would let one signer count twice against the
82
+ // threshold. (A JSON-parsed vote map cannot carry duplicate keys, so the array is the one
83
+ // reachable site for duplication.)
84
+ const peerSet = new Set(proof.peerIds);
85
+ if (peerSet.size !== proof.peerIds.length) {
86
+ return { ok: false, reason: 'duplicate-signer' };
87
+ }
88
+ // The peer list must be the one every signature covers: recompute the membership digest from
89
+ // it, then the message hash over (message + digest). Tampering with peerIds dies here;
90
+ // tampering with the message dies on the next check.
91
+ if (await membershipDigestFromIds(proof.peerIds) !== proof.membershipDigest) {
92
+ return { ok: false, reason: 'membership-mismatch' };
93
+ }
94
+ if (await computeClusterMessageHash(proof.message, proof.membershipDigest) !== proof.messageHash) {
95
+ return { ok: false, reason: 'message-hash-mismatch' };
96
+ }
97
+ const promiseHash = await computeClusterPromiseHash(proof.messageHash, proof.message, proof.membershipDigest);
98
+ const commitHash = await computeClusterCommitHash(proof.messageHash, proof.message, proof.promises, proof.membershipDigest);
99
+ const promiseRound = await countApprovals(proof.promises, promiseHash, peerSet);
100
+ if ('reason' in promiseRound) {
101
+ return { ok: false, reason: promiseRound.reason };
102
+ }
103
+ const commitRound = await countApprovals(proof.commits, commitHash, peerSet);
104
+ if ('reason' in commitRound) {
105
+ return { ok: false, reason: commitRound.reason };
106
+ }
107
+ // Threshold gates. When a threshold fails AND that round skipped a signer outside `peerIds`,
108
+ // report `unknown-signer` rather than the bare threshold reason — the skipped signature is the
109
+ // record of why the count came up short.
110
+ const superMajority = Math.ceil(thresholds.superMajorityThreshold * proof.peerIds.length);
111
+ if (promiseRound.approves < superMajority) {
112
+ return { ok: false, reason: promiseRound.sawUnknownSigner ? 'unknown-signer' : 'promise-threshold' };
113
+ }
114
+ if (!(commitRound.approves > proof.peerIds.length * thresholds.simpleMajorityThreshold)) {
115
+ return { ok: false, reason: commitRound.sawUnknownSigner ? 'unknown-signer' : 'commit-threshold' };
116
+ }
117
+ const op = findClaimedCommitOp(proof.message, claim);
118
+ if (!op) {
119
+ return { ok: false, reason: 'claim-not-in-message' };
120
+ }
121
+ return { ok: true, declaredDigest: op.blockDigests?.[claim.blockId]?.digest };
122
+ }
123
+ catch {
124
+ return { ok: false, reason: 'malformed-proof' };
125
+ }
126
+ }
127
+ /**
128
+ * Claim verification ({@link verifyBlockCommitProofClaim}) PLUS: the digest the commit op declared
129
+ * for `claim.blockId` must be present (`no-digest-declared`) and equal `canonicalBlockHash(block)`
130
+ * (`digest-mismatch`). Total like the claim half; a block shape that `canonicalBlockHash` cannot
131
+ * digest reads as `digest-mismatch` — the received bytes provably are not the declared content.
132
+ */
133
+ export async function verifyBlockCommitProofContent(proof, claim, block, thresholds) {
134
+ const verdict = await verifyBlockCommitProofClaim(proof, claim, thresholds);
135
+ if (!verdict.ok) {
136
+ return verdict;
137
+ }
138
+ if (typeof verdict.declaredDigest !== 'string') {
139
+ return { ok: false, reason: 'no-digest-declared' };
140
+ }
141
+ try {
142
+ if (await canonicalBlockHash(block) !== verdict.declaredDigest) {
143
+ return { ok: false, reason: 'digest-mismatch' };
144
+ }
145
+ }
146
+ catch {
147
+ return { ok: false, reason: 'digest-mismatch' };
148
+ }
149
+ return verdict;
150
+ }
151
+ /**
152
+ * The digest `proof.message`'s commit operation declares for the claimed block, or `undefined`
153
+ * when the claim resolves no commit op or the op carries no digest for that block id. This is the
154
+ * SAME op resolution {@link verifyBlockCommitProofClaim}'s claim step uses, so the digest the
155
+ * storage layer's retention rule compares against (persist only when the local materialization
156
+ * matches) can never drift from the digest a later verifier extracts.
157
+ */
158
+ export function proofDeclaredDigest(proof, claim) {
159
+ return findClaimedCommitOp(proof.message, claim)?.blockDigests?.[claim.blockId]?.digest;
160
+ }
161
+ /**
162
+ * Does `proof`'s message actually carry a commit operation for this exact
163
+ * `(blockId, rev, actionId)`? The claim step of {@link verifyBlockCommitProofClaim} with NO
164
+ * cryptography — the same {@link findClaimedCommitOp} resolution, so the cheap structural check and
165
+ * the full verification can never disagree about which op a claim names.
166
+ *
167
+ * This is the pairing guard a SERVER uses: a proof is looked up by revision, so attaching one whose
168
+ * message names a different revision (or block, or action) would be publishing a mis-paired
169
+ * artifact. It says nothing about signatures or thresholds — a receiver must still verify. Total on
170
+ * hostile input like its verifying sibling: never throws, `false` on any malformed shape.
171
+ */
172
+ export function proofClaimsCommit(proof, claim) {
173
+ try {
174
+ return findClaimedCommitOp(proof.message, claim) !== undefined;
175
+ }
176
+ catch {
177
+ return false;
178
+ }
179
+ }
180
+ /** A plain-object (non-array) map of votes — the shape `promises` / `commits` must have. */
181
+ function isVoteMap(value) {
182
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
183
+ }
184
+ /**
185
+ * Count distinct `approve` votes over `hash` from signers inside `peerSet`, verifying each signature
186
+ * against the Ed25519 key its peer id names. Per-entry rules (the spec's step 5/6):
187
+ *
188
+ * - a non-`approve` vote is ignored silently (rejects/conflicts are part of the signed record but
189
+ * never count toward approval);
190
+ * - a signer outside `peerSet` is skipped and noted (`sawUnknownSigner`) — the caller downgrades a
191
+ * failed threshold to `unknown-signer`;
192
+ * - a signer inside `peerSet` whose id is not a valid Ed25519 peer id → immediate
193
+ * `non-ed25519-signer` (its key cannot be recovered, so the proof as a whole is unverifiable);
194
+ * - a signature that is not a string, fails base64url decode, or fails cryptographic verify →
195
+ * immediate `malformed-signature`;
196
+ * - each signer id is counted at most once.
197
+ */
198
+ async function countApprovals(votes, hash, peerSet) {
199
+ let approves = 0;
200
+ let sawUnknownSigner = false;
201
+ const counted = new Set();
202
+ for (const [signerId, vote] of Object.entries(votes)) {
203
+ if (vote === null || typeof vote !== 'object' || vote.type !== 'approve') {
204
+ continue;
205
+ }
206
+ if (!peerSet.has(signerId)) {
207
+ sawUnknownSigner = true;
208
+ continue;
209
+ }
210
+ if (counted.has(signerId)) {
211
+ continue;
212
+ }
213
+ let rawKey;
214
+ try {
215
+ const peerId = peerIdFromString(signerId);
216
+ if (peerId.type !== 'Ed25519' || peerId.publicKey === undefined) {
217
+ return { reason: 'non-ed25519-signer' };
218
+ }
219
+ rawKey = peerId.publicKey.raw;
220
+ }
221
+ catch {
222
+ // An id that does not even parse as a peer id certainly names no Ed25519 key.
223
+ return { reason: 'non-ed25519-signer' };
224
+ }
225
+ try {
226
+ if (typeof vote.signature !== 'string') {
227
+ return { reason: 'malformed-signature' };
228
+ }
229
+ const sigBytes = uint8ArrayFromString(vote.signature, 'base64url');
230
+ const payload = clusterVoteVerificationPayload(hash, vote);
231
+ if (!await publicKeyFromRaw(rawKey).verify(payload, sigBytes)) {
232
+ return { reason: 'malformed-signature' };
233
+ }
234
+ }
235
+ catch {
236
+ return { reason: 'malformed-signature' };
237
+ }
238
+ counted.add(signerId);
239
+ approves++;
240
+ }
241
+ return { approves, sawUnknownSigner };
242
+ }
243
+ /**
244
+ * The `{ commit }` operation the claim points at: `blockIds` contains the claimed block and the
245
+ * op's `actionId` / `rev` equal the claim's. Absent → `claim-not-in-message` (the replay stop).
246
+ */
247
+ function findClaimedCommitOp(message, claim) {
248
+ for (const operation of message.operations) {
249
+ if (operation === null || typeof operation !== 'object' || !('commit' in operation)) {
250
+ continue;
251
+ }
252
+ const commit = operation.commit;
253
+ if (commit !== null && typeof commit === 'object'
254
+ && Array.isArray(commit.blockIds)
255
+ && commit.blockIds.includes(claim.blockId)
256
+ && commit.actionId === claim.actionId
257
+ && commit.rev === claim.rev) {
258
+ return commit;
259
+ }
260
+ }
261
+ return undefined;
262
+ }
263
+ //# sourceMappingURL=commit-proof.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commit-proof.js","sourceRoot":"","sources":["../../../src/cluster/commit-proof.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import type { IBlock } from "@optimystic/db-core";
2
+ import type { BlockCommitProof } from "./commit-proof.js";
2
3
  /**
3
4
  * Quorum-corroboration helpers shared by the two block-restoration paths:
4
5
  * - read-repair (`CoordinatorRepo.queryClusterForLatest`)
@@ -9,9 +10,13 @@ import type { IBlock } from "@optimystic/db-core";
9
10
  * "max wins" with "highest value corroborated by a quorum of distinct peers".
10
11
  *
11
12
  * NOTE: the quorum here is corroboration-of-a-claim, NOT Sybil-resistant cohort
12
- * membership. A peer minting fresh keypairs can still cast a vote proving a
13
- * voter is a legitimate cohort member requires commit-cert + membership
14
- * anchoring, deferred to backlog `debt-read-repair-commit-cert-verification`.
13
+ * membership. A peer minting fresh keypairs can still cast a vote. Selection now
14
+ * additionally weighs *certified* claims those whose cohort commit proof a
15
+ * caller has already verified (`cluster/certified-claims.ts`) and marked via the
16
+ * injected `certified` flag — so a lone honest holder with a valid proof is
17
+ * sufficient where uncertified claims still need distinct-peer corroboration.
18
+ * Verification itself never happens here: both selectors stay pure and
19
+ * synchronous; verdicts arrive as booleans.
15
20
  */
16
21
  /** A single peer's self-reported (rev, actionId) for a block. */
17
22
  export interface RevClaim {
@@ -19,13 +24,34 @@ export interface RevClaim {
19
24
  peerId: string;
20
25
  rev: number;
21
26
  actionId: string;
27
+ /**
28
+ * The cohort commit proof the claiming peer attached, when it had one. Selection never reads
29
+ * this field: presence proves nothing on its own — the peer chose what to attach. A caller
30
+ * that verifies it (`certifyClaim` / `certifyContent` in `cluster/certified-claims.ts`, built
31
+ * on `verifyBlockCommitProofClaim`) records the verdict in {@link certified}, which is what
32
+ * {@link selectQuorumRev} weighs.
33
+ */
34
+ proof?: BlockCommitProof;
35
+ /**
36
+ * Injected verdict: the caller verified this claim's cohort commit proof and it certifies this
37
+ * exact `(rev, actionId)`. A certified claim carries the cohort's signature set as its
38
+ * corroboration, so {@link selectQuorumRev} can select it without a second peer vouching.
39
+ * Never set this from the mere presence of {@link proof}.
40
+ */
41
+ certified?: boolean;
22
42
  }
23
43
  /** The (rev, actionId) pair a quorum agreed on, plus the peers that corroborated it. */
24
44
  export interface QuorumRev {
25
45
  rev: number;
26
46
  actionId: string;
27
- /** Distinct peer-ids that voted for this exact (rev, actionId). */
47
+ /**
48
+ * Distinct peer-ids that voted for this exact (rev, actionId). When {@link certified} is set,
49
+ * these are the certified claimants at that pair instead — possibly a single peer, whose
50
+ * corroboration is the proof's signature set rather than other voters.
51
+ */
28
52
  supporters: string[];
53
+ /** Set when the certified path selected this pair, so callers can log which rule won. */
54
+ certified?: true;
29
55
  }
30
56
  /**
31
57
  * Votes a claim needs when the cohort is big enough to supply them: a claim must be
@@ -91,15 +117,55 @@ export declare function corroboratorCapacity(cohortPeerCount: number, repairCorr
91
117
  *
92
118
  * Returns `undefined` when nothing is corroborated — an uncorroborated claim
93
119
  * must never drive restoration.
120
+ *
121
+ * **Certified claims** (`certified === true`, injected by a caller that verified the claim's
122
+ * cohort commit proof — see `cluster/certified-claims.ts`) short-circuit the distinct-peer rule,
123
+ * because the proof's signature set IS the corroboration:
124
+ *
125
+ * - No certified claims → today's corroboration result, unchanged.
126
+ * - A corroborated pair at a HIGHER rev than every certified claim wins — corroboration stays a
127
+ * legitimate weaker path, so a legacy uncertified tail written after the last proven rev
128
+ * remains readable.
129
+ * - Otherwise the highest certified rev wins: this covers a certified rev beaten in raw rev only
130
+ * by an *uncorroborated* claim (which failed quorum and is no evidence), and the equal-rev
131
+ * tie, where the proof outweighs votes.
132
+ * - Except: two distinct `actionId`s certified at that top rev is equivocation — the cohort
133
+ * provably signed two different actions into one revision — and the whole selection declines
134
+ * (`undefined`). Callers distinguish this decline from a plain no-quorum via
135
+ * {@link certifiedEquivocation}.
94
136
  */
95
137
  export declare function selectQuorumRev(claims: RevClaim[], simpleMajorityThreshold: number, corroboratorCapacity?: number): QuorumRev | undefined;
96
- /** Canonical sha256 of a block's content, base64url. Same pattern as `ClusterMember.computeCommitHash`. */
97
- export declare function canonicalBlockHash(block: IBlock): Promise<string>;
98
- /** One block candidate paired with its serving peer and canonical hash. */
138
+ /**
139
+ * The conflicting certified set at the top certified rev, when there is one: two-plus distinct
140
+ * `actionId`s each carrying a verified cohort commit proof for the SAME revision. It deserves a
141
+ * distinct log line from a plain no-quorum — the cohort (or whoever holds its keys) provably
142
+ * signed both sides. Selection stays pure, so callers do the logging with what this reports.
143
+ *
144
+ * `undefined` when no certified claim exists or the top certified rev names a single action —
145
+ * conflicts at LOWER certified revs are history already superseded, not equivocation worth
146
+ * declining over.
147
+ *
148
+ * **Ask this only on a decline.** A non-`undefined` result does NOT imply {@link selectQuorumRev}
149
+ * returned `undefined`: a corroborated pair STRICTLY above the top certified rev still wins, and
150
+ * the equivocation below it is reported here while selection succeeded. Call it when selection
151
+ * declined, to say WHY it declined.
152
+ */
153
+ export declare function certifiedEquivocation(claims: RevClaim[]): {
154
+ rev: number;
155
+ actionIds: string[];
156
+ } | undefined;
157
+ /** One block candidate paired with its serving peer and canonical hash
158
+ * (`canonicalBlockHash` from `@optimystic/db-core`). */
99
159
  export interface BlockHashCandidate {
100
160
  peerId: string;
101
161
  hash: string;
102
162
  block: IBlock;
163
+ /**
164
+ * Injected verdict: the caller verified a cohort commit proof binding this candidate's CONTENT
165
+ * (`certifyContent` in `cluster/certified-claims.ts` — the declared digest matched these
166
+ * bytes), so the cohort's signatures stand in for other peers serving the same hash.
167
+ */
168
+ certified?: boolean;
103
169
  }
104
170
  /**
105
171
  * Among candidates already known to corroborate the target `(rev, actionId)`,
@@ -113,16 +179,37 @@ export interface BlockHashCandidate {
113
179
  * Omit it to keep the floor at two.
114
180
  *
115
181
  * **What relaxing this does and does not cost.** Block ids are random 256-bit strings, not
116
- * content-addressed, so {@link canonicalBlockHash} is a cross-peer *agreement* hash and never a
182
+ * content-addressed, so `canonicalBlockHash` (db-core) is a cross-peer *agreement* hash and never a
117
183
  * check against the requested id — nothing here re-derives an id from received bytes. At capacity
118
184
  * one the sole peer's content is therefore taken on its word. That extends no trust the cohort had
119
185
  * not already extended: the same peer's `(rev, actionId)` claim is equally uncorroborable at that
120
186
  * size, and a two-member cohort has no honest majority to appeal to. Pass a capacity a shrunken
121
187
  * view of the network cannot talk down (see {@link corroboratorCapacity}), so only a cohort that is
122
188
  * *genuinely* that small reaches this branch.
189
+ *
190
+ * **Certified candidates** (`certified === true` — a caller verified a cohort commit proof whose
191
+ * declared digest matches these exact bytes) short-circuit the hash quorum: exactly one distinct
192
+ * certified hash → that block wins outright, however many peers served it. Two-plus distinct
193
+ * certified hashes is certified content equivocation → decline (`undefined`), mirroring the
194
+ * existing unique-hash-tie decline; callers name that decline via
195
+ * {@link certifiedContentEquivocation}. No certified candidate → the hash quorum below, unchanged.
123
196
  */
124
197
  export declare function selectQuorumBlock(candidates: BlockHashCandidate[], simpleMajorityThreshold: number, corroboratorCapacity?: number): {
125
198
  block: IBlock;
126
199
  hash: string;
127
200
  } | undefined;
201
+ /**
202
+ * The conflicting certified hashes when there are two or more — the content-side sibling of
203
+ * {@link certifiedEquivocation}, and the reason {@link selectQuorumBlock} declines outright.
204
+ * Candidates reaching that selector all carry the SAME `(rev, actionId)`, so two certified hashes
205
+ * mean the cohort's keys signed two different digests into one revision: a provable compromise an
206
+ * operator must be able to tell apart from the routine "not enough carriers agreed" decline. Both
207
+ * declines return `undefined`, so without this they log identically.
208
+ *
209
+ * `undefined` when fewer than two distinct certified hashes exist — including the ordinary
210
+ * no-certified-candidate case, where a decline really is a plain content-quorum shortfall.
211
+ */
212
+ export declare function certifiedContentEquivocation(candidates: BlockHashCandidate[]): {
213
+ hashes: string[];
214
+ } | undefined;
128
215
  //# sourceMappingURL=quorum-restore.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"quorum-restore.d.ts","sourceRoot":"","sources":["../../../src/cluster/quorum-restore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAIlD;;;;;;;;;;;;;GAaG;AAEH,iEAAiE;AACjE,MAAM,WAAW,QAAQ;IACxB,wFAAwF;IACxF,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,wFAAwF;AACxF,MAAM,WAAW,SAAS;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,UAAU,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CACzB,cAAc,EAAE,MAAM,EACtB,uBAAuB,EAAE,MAAM,EAC/B,oBAAoB,GAAE,MAAiC,GACrD,MAAM,CAGR;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAAC,eAAe,EAAE,MAAM,EAAE,8BAA8B,EAAE,MAAM,GAAG,MAAM,CAE5G;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,eAAe,CAC9B,MAAM,EAAE,QAAQ,EAAE,EAClB,uBAAuB,EAAE,MAAM,EAC/B,oBAAoB,CAAC,EAAE,MAAM,GAC3B,SAAS,GAAG,SAAS,CAkCvB;AAWD,2GAA2G;AAC3G,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAIvE;AAED,2EAA2E;AAC3E,MAAM,WAAW,kBAAkB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAChC,UAAU,EAAE,kBAAkB,EAAE,EAChC,uBAAuB,EAAE,MAAM,EAC/B,oBAAoB,CAAC,EAAE,MAAM,GAC3B;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAuB7C"}
1
+ {"version":3,"file":"quorum-restore.d.ts","sourceRoot":"","sources":["../../../src/cluster/quorum-restore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;;;;;;;;;;;;;;;;GAiBG;AAEH,iEAAiE;AACjE,MAAM,WAAW,QAAQ;IACxB,wFAAwF;IACxF,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,wFAAwF;AACxF,MAAM,WAAW,SAAS;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,yFAAyF;IACzF,SAAS,CAAC,EAAE,IAAI,CAAC;CACjB;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CACzB,cAAc,EAAE,MAAM,EACtB,uBAAuB,EAAE,MAAM,EAC/B,oBAAoB,GAAE,MAAiC,GACrD,MAAM,CAGR;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAAC,eAAe,EAAE,MAAM,EAAE,8BAA8B,EAAE,MAAM,GAAG,MAAM,CAE5G;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,eAAe,CAC9B,MAAM,EAAE,QAAQ,EAAE,EAClB,uBAAuB,EAAE,MAAM,EAC/B,oBAAoB,CAAC,EAAE,MAAM,GAC3B,SAAS,GAAG,SAAS,CA+CvB;AAsBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,SAAS,CAI1G;AAED;wDACwD;AACxD,MAAM,WAAW,kBAAkB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,iBAAiB,CAChC,UAAU,EAAE,kBAAkB,EAAE,EAChC,uBAAuB,EAAE,MAAM,EAC/B,oBAAoB,CAAC,EAAE,MAAM,GAC3B;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CA+B7C;AAWD;;;;;;;;;;GAUG;AACH,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,kBAAkB,EAAE,GAAG;IAAE,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,SAAS,CAG/G"}
@@ -1,5 +1,3 @@
1
- import { sha256 } from "multiformats/hashes/sha2";
2
- import { toString as uint8ArrayToString } from 'uint8arrays';
3
1
  /**
4
2
  * Votes a claim needs when the cohort is big enough to supply them: a claim must be
5
3
  * seconded by a second, independent peer. See {@link quorumSize} for the cap that
@@ -69,6 +67,22 @@ export function corroboratorCapacity(cohortPeerCount, repairCorroborationCluster
69
67
  *
70
68
  * Returns `undefined` when nothing is corroborated — an uncorroborated claim
71
69
  * must never drive restoration.
70
+ *
71
+ * **Certified claims** (`certified === true`, injected by a caller that verified the claim's
72
+ * cohort commit proof — see `cluster/certified-claims.ts`) short-circuit the distinct-peer rule,
73
+ * because the proof's signature set IS the corroboration:
74
+ *
75
+ * - No certified claims → today's corroboration result, unchanged.
76
+ * - A corroborated pair at a HIGHER rev than every certified claim wins — corroboration stays a
77
+ * legitimate weaker path, so a legacy uncertified tail written after the last proven rev
78
+ * remains readable.
79
+ * - Otherwise the highest certified rev wins: this covers a certified rev beaten in raw rev only
80
+ * by an *uncorroborated* claim (which failed quorum and is no evidence), and the equal-rev
81
+ * tie, where the proof outweighs votes.
82
+ * - Except: two distinct `actionId`s certified at that top rev is equivocation — the cohort
83
+ * provably signed two different actions into one revision — and the whole selection declines
84
+ * (`undefined`). Callers distinguish this decline from a plain no-quorum via
85
+ * {@link certifiedEquivocation}.
72
86
  */
73
87
  export function selectQuorumRev(claims, simpleMajorityThreshold, corroboratorCapacity) {
74
88
  if (claims.length === 0)
@@ -99,21 +113,67 @@ export function selectQuorumRev(claims, simpleMajorityThreshold, corroboratorCap
99
113
  best = g;
100
114
  }
101
115
  }
102
- return best
116
+ const corroborated = best
103
117
  ? { rev: best.rev, actionId: best.actionId, supporters: [...best.supporters] }
104
118
  : undefined;
119
+ const certified = certifiedGroups(claims);
120
+ if (!certified)
121
+ return corroborated;
122
+ // A corroborated pair strictly above every certified rev wins — a legacy uncertified tail
123
+ // must stay readable. A merely UNcorroborated higher rev never reaches here (it is not in
124
+ // `corroborated`), so it cannot outrank a proof.
125
+ if (corroborated && corroborated.rev > certified.rev)
126
+ return corroborated;
127
+ // Two actions provably signed into the same top revision: decline the whole selection rather
128
+ // than pick a side. Callers log via certifiedEquivocation.
129
+ if (certified.byAction.size !== 1)
130
+ return undefined;
131
+ const [entry] = certified.byAction;
132
+ const [actionId, supporters] = entry; // size === 1 checked above
133
+ return { rev: certified.rev, actionId, supporters: [...supporters], certified: true };
105
134
  }
106
- /** Deterministic JSON: sorts object keys so the hash is order-independent. Mirrors `ClusterMember.canonicalJson`. */
107
- function canonicalJson(value) {
108
- return JSON.stringify(value, (_, v) => v && typeof v === 'object' && !Array.isArray(v)
109
- ? Object.keys(v).sort().reduce((o, k) => { o[k] = v[k]; return o; }, {})
110
- : v);
135
+ /** Certified claims at the top certified rev, keyed by actionId distinct certified claimants. */
136
+ function certifiedGroups(claims) {
137
+ let top;
138
+ for (const c of claims) {
139
+ if (c.certified === true && (top === undefined || c.rev > top))
140
+ top = c.rev;
141
+ }
142
+ if (top === undefined)
143
+ return undefined;
144
+ const byAction = new Map();
145
+ for (const c of claims) {
146
+ if (c.certified !== true || c.rev !== top)
147
+ continue;
148
+ let s = byAction.get(c.actionId);
149
+ if (!s) {
150
+ s = new Set();
151
+ byAction.set(c.actionId, s);
152
+ }
153
+ s.add(c.peerId);
154
+ }
155
+ return { rev: top, byAction };
111
156
  }
112
- /** Canonical sha256 of a block's content, base64url. Same pattern as `ClusterMember.computeCommitHash`. */
113
- export async function canonicalBlockHash(block) {
114
- const bytes = new TextEncoder().encode(canonicalJson(block));
115
- const digest = await sha256.digest(bytes);
116
- return uint8ArrayToString(digest.digest, 'base64url');
157
+ /**
158
+ * The conflicting certified set at the top certified rev, when there is one: two-plus distinct
159
+ * `actionId`s each carrying a verified cohort commit proof for the SAME revision. It deserves a
160
+ * distinct log line from a plain no-quorum — the cohort (or whoever holds its keys) provably
161
+ * signed both sides. Selection stays pure, so callers do the logging with what this reports.
162
+ *
163
+ * `undefined` when no certified claim exists or the top certified rev names a single action —
164
+ * conflicts at LOWER certified revs are history already superseded, not equivocation worth
165
+ * declining over.
166
+ *
167
+ * **Ask this only on a decline.** A non-`undefined` result does NOT imply {@link selectQuorumRev}
168
+ * returned `undefined`: a corroborated pair STRICTLY above the top certified rev still wins, and
169
+ * the equivocation below it is reported here while selection succeeded. Call it when selection
170
+ * declined, to say WHY it declined.
171
+ */
172
+ export function certifiedEquivocation(claims) {
173
+ const groups = certifiedGroups(claims);
174
+ if (!groups || groups.byAction.size < 2)
175
+ return undefined;
176
+ return { rev: groups.rev, actionIds: [...groups.byAction.keys()] };
117
177
  }
118
178
  /**
119
179
  * Among candidates already known to corroborate the target `(rev, actionId)`,
@@ -127,17 +187,32 @@ export async function canonicalBlockHash(block) {
127
187
  * Omit it to keep the floor at two.
128
188
  *
129
189
  * **What relaxing this does and does not cost.** Block ids are random 256-bit strings, not
130
- * content-addressed, so {@link canonicalBlockHash} is a cross-peer *agreement* hash and never a
190
+ * content-addressed, so `canonicalBlockHash` (db-core) is a cross-peer *agreement* hash and never a
131
191
  * check against the requested id — nothing here re-derives an id from received bytes. At capacity
132
192
  * one the sole peer's content is therefore taken on its word. That extends no trust the cohort had
133
193
  * not already extended: the same peer's `(rev, actionId)` claim is equally uncorroborable at that
134
194
  * size, and a two-member cohort has no honest majority to appeal to. Pass a capacity a shrunken
135
195
  * view of the network cannot talk down (see {@link corroboratorCapacity}), so only a cohort that is
136
196
  * *genuinely* that small reaches this branch.
197
+ *
198
+ * **Certified candidates** (`certified === true` — a caller verified a cohort commit proof whose
199
+ * declared digest matches these exact bytes) short-circuit the hash quorum: exactly one distinct
200
+ * certified hash → that block wins outright, however many peers served it. Two-plus distinct
201
+ * certified hashes is certified content equivocation → decline (`undefined`), mirroring the
202
+ * existing unique-hash-tie decline; callers name that decline via
203
+ * {@link certifiedContentEquivocation}. No certified candidate → the hash quorum below, unchanged.
137
204
  */
138
205
  export function selectQuorumBlock(candidates, simpleMajorityThreshold, corroboratorCapacity) {
139
206
  if (candidates.length === 0)
140
207
  return undefined;
208
+ const certifiedByHash = certifiedHashes(candidates);
209
+ if (certifiedByHash.size === 1) {
210
+ const [entry] = certifiedByHash;
211
+ const [hash, block] = entry;
212
+ return { block, hash };
213
+ }
214
+ if (certifiedByHash.size > 1)
215
+ return undefined; // certified content equivocation — decline
141
216
  // One vote per distinct peer per hash group, matching selectQuorumRev — a peer appearing twice
142
217
  // must not be able to second itself into a content quorum.
143
218
  const groups = new Map();
@@ -159,4 +234,28 @@ export function selectQuorumBlock(candidates, simpleMajorityThreshold, corrobora
159
234
  const [hash, group] = meeting[0];
160
235
  return { block: group.block, hash };
161
236
  }
237
+ /** Distinct hashes carried by certified candidates → the first block instance serving each. */
238
+ function certifiedHashes(candidates) {
239
+ const byHash = new Map();
240
+ for (const c of candidates) {
241
+ if (c.certified === true && !byHash.has(c.hash))
242
+ byHash.set(c.hash, c.block);
243
+ }
244
+ return byHash;
245
+ }
246
+ /**
247
+ * The conflicting certified hashes when there are two or more — the content-side sibling of
248
+ * {@link certifiedEquivocation}, and the reason {@link selectQuorumBlock} declines outright.
249
+ * Candidates reaching that selector all carry the SAME `(rev, actionId)`, so two certified hashes
250
+ * mean the cohort's keys signed two different digests into one revision: a provable compromise an
251
+ * operator must be able to tell apart from the routine "not enough carriers agreed" decline. Both
252
+ * declines return `undefined`, so without this they log identically.
253
+ *
254
+ * `undefined` when fewer than two distinct certified hashes exist — including the ordinary
255
+ * no-certified-candidate case, where a decline really is a plain content-quorum shortfall.
256
+ */
257
+ export function certifiedContentEquivocation(candidates) {
258
+ const byHash = certifiedHashes(candidates);
259
+ return byHash.size < 2 ? undefined : { hashes: [...byHash.keys()] };
260
+ }
162
261
  //# sourceMappingURL=quorum-restore.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"quorum-restore.js","sourceRoot":"","sources":["../../../src/cluster/quorum-restore.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"quorum-restore.js","sourceRoot":"","sources":["../../../src/cluster/quorum-restore.ts"],"names":[],"mappings":"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"}