pactium 0.2.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 (42) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +92 -0
  3. package/README.zh-CN.md +90 -0
  4. package/SECURITY.md +7 -0
  5. package/bin/pactium.mjs +121 -0
  6. package/docs/LICOLITE-ASPECT.md +57 -0
  7. package/docs/README.md +13 -0
  8. package/docs/TERM.md +289 -0
  9. package/docs/architecture/ARCHITECTURE.md +62 -0
  10. package/docs/protocols/PROFILE.md +124 -0
  11. package/docs/protocols/PROTOCOLS.md +62 -0
  12. package/examples/record-operation.mjs +26 -0
  13. package/package.json +69 -0
  14. package/src/README.md +13 -0
  15. package/src/aspects/licolite/aspect.js +278 -0
  16. package/src/aspects/licolite/constants.js +13 -0
  17. package/src/aspects/licolite/evidence.js +47 -0
  18. package/src/aspects/licolite/index.d.ts +51 -0
  19. package/src/aspects/licolite/index.js +19 -0
  20. package/src/aspects/licolite/signing.js +78 -0
  21. package/src/canonical/value.js +40 -0
  22. package/src/core/append-condition.js +102 -0
  23. package/src/core/pactium-core.js +1073 -0
  24. package/src/core/tracking-cursor.js +68 -0
  25. package/src/http.js +99 -0
  26. package/src/index-engine/snapshot-merkle-index.js +994 -0
  27. package/src/index.d.ts +244 -0
  28. package/src/index.js +73 -0
  29. package/src/ledger/signed-head.js +204 -0
  30. package/src/ledger/transparency-log.js +702 -0
  31. package/src/maintenance/task-engine.js +36 -0
  32. package/src/proof/bundle-format.js +265 -0
  33. package/src/proof/bundle.js +77 -0
  34. package/src/proof/envelope.js +548 -0
  35. package/src/proof/registry.js +18 -0
  36. package/src/protocol/constants.js +69 -0
  37. package/src/protocol/hashing.js +47 -0
  38. package/src/quality/profile-runner.js +291 -0
  39. package/src/repair/planner.js +62 -0
  40. package/src/shared/records.js +32 -0
  41. package/src/storage/local-json-storage-port.js +360 -0
  42. package/src/verification/failure.js +31 -0
@@ -0,0 +1,548 @@
1
+ import { PACTIUM_PROTOCOL } from "../protocol/constants.js";
2
+ import { canonicalDecode, normalizeCanonicalValue } from "../canonical/value.js";
3
+ import { cidForBytes, createId, hashBytes, protocolHash } from "../protocol/hashing.js";
4
+ import { verifyLedgerConsistencyProof, verifyLedgerInclusionProof } from "../ledger/transparency-log.js";
5
+ import { verifyLedgerHeadSignature } from "../ledger/signed-head.js";
6
+ import { asArray, asRecord } from "../shared/records.js";
7
+ import { createVerificationFailure } from "../verification/failure.js";
8
+ import { createIndexedBundleResolver } from "./bundle-format.js";
9
+ import { createDefaultProofVerifierRegistry } from "./registry.js";
10
+
11
+ const CORE_CRITICAL_EXTENSIONS = new Set([]);
12
+
13
+ export async function createProofRef(storage, name, value, refs = []) {
14
+ const block = await storage.putBlock(value, { kind: `proof-material:${name}`, refs });
15
+ return {
16
+ name,
17
+ cid: block.cid,
18
+ payloadHash: block.payloadHash,
19
+ byteLength: block.byteLength
20
+ };
21
+ }
22
+
23
+ function extensionSigningPayload(envelope) {
24
+ return normalizeCanonicalValue({
25
+ ...envelope,
26
+ envelopeId: undefined,
27
+ replayed: false,
28
+ extensions: asArray(envelope.extensions).filter((extension) => extension.name !== "licolite.signature")
29
+ });
30
+ }
31
+
32
+ function envelopeIdentityPayload(envelope) {
33
+ return normalizeCanonicalValue({
34
+ ...envelope,
35
+ replayed: false,
36
+ envelopeId: undefined
37
+ });
38
+ }
39
+
40
+ export function envelopeSigningHash(envelope) {
41
+ return protocolHash("proof.envelope.signing", extensionSigningPayload(envelope));
42
+ }
43
+
44
+ export function finalizeEnvelope(envelope) {
45
+ const identity = envelopeIdentityPayload(envelope);
46
+ return {
47
+ ...envelope,
48
+ envelopeId: createId("proof_envelope", identity)
49
+ };
50
+ }
51
+
52
+ export async function materializeExtension(storage, extension) {
53
+ if (!extension) return null;
54
+ if (extension.valueRef && extension.valueHash) {
55
+ return {
56
+ protocol: PACTIUM_PROTOCOL,
57
+ name: String(extension.name || ""),
58
+ critical: extension.critical === true,
59
+ valueRef: String(extension.valueRef),
60
+ valueHash: String(extension.valueHash),
61
+ metadata: normalizeCanonicalValue(asRecord(extension.metadata))
62
+ };
63
+ }
64
+ const block = await storage.putBlock(extension.value ?? {}, {
65
+ kind: `proof-extension:${extension.name || "extension"}`,
66
+ refs: [
67
+ ...asArray(extension.refs),
68
+ extension.value?.evidenceRef || ""
69
+ ].filter(Boolean)
70
+ });
71
+ return {
72
+ protocol: PACTIUM_PROTOCOL,
73
+ name: String(extension.name || ""),
74
+ critical: extension.critical === true,
75
+ valueRef: block.cid,
76
+ valueHash: block.payloadHash,
77
+ metadata: normalizeCanonicalValue(asRecord(extension.metadata))
78
+ };
79
+ }
80
+
81
+ async function resolveBlock({ cid, storage, bundleMap }) {
82
+ if (bundleMap?.has(cid)) {
83
+ const record = await bundleMap.get(cid);
84
+ if (!record) return null;
85
+ const bytes = Buffer.from(String(record.payloadBase64 || ""), "base64");
86
+ const payloadHash = `sha256:${hashBytes(bytes)}`;
87
+ if (payloadHash !== record.payloadHash || cidForBytes(bytes) !== record.cid) {
88
+ throw new Error(`Proof bundle block integrity failure for ${cid}`);
89
+ }
90
+ return { ...record, bytes };
91
+ }
92
+ return storage ? storage.getBlock(cid) : null;
93
+ }
94
+
95
+ async function decodeBlockValue(block) {
96
+ if (!block) return null;
97
+ if (block.codec === "raw") return block.bytes;
98
+ return canonicalDecode(block.bytes);
99
+ }
100
+
101
+ function bundleBlockMap(bundle) {
102
+ if (!bundle) return null;
103
+ return createIndexedBundleResolver(bundle);
104
+ }
105
+
106
+ function proofIsCritical(proof) {
107
+ return asRecord(proof).critical !== false;
108
+ }
109
+
110
+ async function verifyEmbeddedProofs({
111
+ proofMaterial,
112
+ registry,
113
+ requireAllProofs,
114
+ failures,
115
+ checked
116
+ }) {
117
+ async function visit(value, path) {
118
+ if (Array.isArray(value)) {
119
+ for (const [index, item] of value.entries()) await visit(item, `${path}[${index}]`);
120
+ return;
121
+ }
122
+ if (!value || typeof value !== "object") return;
123
+ if (value.proofType) {
124
+ const proofType = String(value.proofType);
125
+ const verifier = registry.get(proofType);
126
+ if (!verifier) {
127
+ if (requireAllProofs || proofIsCritical(value)) {
128
+ failures.push(createVerificationFailure({
129
+ layer: "proof-registry",
130
+ code: "missing_proof_verifier",
131
+ message: `No verifier is registered for proof type ${proofType}.`,
132
+ evidenceRef: path,
133
+ repairable: true,
134
+ details: { proofType, path }
135
+ }));
136
+ }
137
+ } else {
138
+ try {
139
+ const result = await verifier(value, {
140
+ proofMaterial,
141
+ head: proofMaterial?.ledger?.head,
142
+ oldHead: proofMaterial?.ledger?.previousHead,
143
+ newHead: proofMaterial?.ledger?.head
144
+ });
145
+ const ok = typeof result === "boolean" ? result : result?.ok === true;
146
+ if (!ok) {
147
+ failures.push(createVerificationFailure({
148
+ layer: "proof-registry",
149
+ code: "bad_embedded_proof",
150
+ message: `Embedded proof ${proofType} does not verify.`,
151
+ evidenceRef: path,
152
+ details: { proofType, path }
153
+ }));
154
+ } else {
155
+ checked.push(path);
156
+ }
157
+ } catch (error) {
158
+ failures.push(createVerificationFailure({
159
+ layer: "proof-registry",
160
+ code: "proof_verifier_threw",
161
+ message: error instanceof Error ? error.message : `Verifier for ${proofType} threw.`,
162
+ evidenceRef: path,
163
+ details: { proofType, path }
164
+ }));
165
+ }
166
+ }
167
+ }
168
+ for (const [key, nested] of Object.entries(value)) {
169
+ await visit(nested, path ? `${path}.${key}` : key);
170
+ }
171
+ }
172
+ await visit(proofMaterial?.proofs || {}, "proofs");
173
+ }
174
+
175
+ function factRefBindings(envelope, proofMaterial) {
176
+ const proof = proofMaterial?.ledger?.inclusionProof || {};
177
+ const leaf = proof.leaf || {};
178
+ const leafIndex = Number(leaf.index ?? proof.index);
179
+ const leafHash = String(proof.leafHash || "");
180
+ const expectedEventId = Number.isFinite(leafIndex) && leafHash
181
+ ? createId("ledger_event", { index: leafIndex, leafHash })
182
+ : "";
183
+ return {
184
+ ok: Boolean(expectedEventId) &&
185
+ envelope.factRef?.ledgerEventId === expectedEventId &&
186
+ Number(envelope.factRef?.ledgerIndex) === leafIndex &&
187
+ envelope.factRef?.factCid === leaf.factCid &&
188
+ envelope.factRef?.factHash === leaf.factHash &&
189
+ envelope.factType === leaf.factType,
190
+ expectedEventId,
191
+ leafIndex
192
+ };
193
+ }
194
+
195
+ function verifySemanticBindings({ envelope, proofMaterial, failures }) {
196
+ const proofHead = proofMaterial?.ledger?.head || {};
197
+ if (envelope.ledgerHead && (
198
+ envelope.ledgerHead.rootHash !== proofHead.rootHash ||
199
+ Number(envelope.ledgerHead.size || 0) !== Number(proofHead.size || 0)
200
+ )) {
201
+ failures.push(createVerificationFailure({
202
+ layer: "proof-envelope",
203
+ code: "bad_ledger_head_binding",
204
+ message: "Envelope ledger head does not match the verified proof material head.",
205
+ evidenceRef: envelope.envelopeId
206
+ }));
207
+ }
208
+
209
+ const factBinding = factRefBindings(envelope, proofMaterial);
210
+ if (!factBinding.ok) {
211
+ failures.push(createVerificationFailure({
212
+ layer: "proof-envelope",
213
+ code: "bad_fact_ref_binding",
214
+ message: "Envelope factRef does not match the verified Ledger inclusion leaf.",
215
+ evidenceRef: envelope.envelopeId,
216
+ details: {
217
+ expectedLedgerEventId: factBinding.expectedEventId,
218
+ expectedLedgerIndex: factBinding.leafIndex
219
+ }
220
+ }));
221
+ }
222
+
223
+ const proofs = proofMaterial?.proofs || {};
224
+ function badBinding(code, message, details = {}) {
225
+ failures.push(createVerificationFailure({
226
+ layer: "proof-semantics",
227
+ code,
228
+ message,
229
+ evidenceRef: envelope.envelopeId,
230
+ details
231
+ }));
232
+ }
233
+ function expectIndexProof(proof, { root = "", key = "", proofType = "", label = "" } = {}) {
234
+ if (!proof) return;
235
+ if (proofType && proof.proofType !== proofType) {
236
+ badBinding("bad_index_proof_binding", `${label || "Index proof"} has the wrong proof type.`, {
237
+ label,
238
+ expectedProofType: proofType,
239
+ actualProofType: proof.proofType
240
+ });
241
+ }
242
+ if (root && proof.indexRoot !== root) {
243
+ badBinding("bad_index_proof_binding", `${label || "Index proof"} does not bind to the declared root.`, {
244
+ label,
245
+ expectedRoot: root,
246
+ actualRoot: proof.indexRoot
247
+ });
248
+ }
249
+ if (key && proof.key !== key) {
250
+ badBinding("bad_index_proof_binding", `${label || "Index proof"} does not bind to the declared key.`, {
251
+ label,
252
+ expectedKey: key,
253
+ actualKey: proof.key
254
+ });
255
+ }
256
+ }
257
+ if (proofs.workspaceProjection) {
258
+ expectIndexProof(proofs.workspaceProjection.orderProof, {
259
+ root: proofs.workspaceProjection.orderRoot,
260
+ key: proofs.workspaceProjection.orderKey,
261
+ proofType: "index.membership.prolly-path",
262
+ label: "workspace order proof"
263
+ });
264
+ expectIndexProof(proofs.workspaceProjection.membershipProof, {
265
+ root: proofs.workspaceProjection.membershipRoot,
266
+ key: envelope.factRef?.ledgerEventId || "",
267
+ proofType: "index.membership.prolly-path",
268
+ label: "workspace membership proof"
269
+ });
270
+ }
271
+ const stateCommit = proofs.stateCommit;
272
+ if (stateCommit) {
273
+ const touchedKeyProofs = asArray(proofs.state?.touchedKeyProofs);
274
+ const mutationKeys = asArray(stateCommit.mutationKeys).map(String).filter(Boolean);
275
+ const mutationActions = asArray(stateCommit.mutationActions).map(String);
276
+ const invalidStateCommit = stateCommit.factType !== "state.commit" ||
277
+ stateCommit.intentId !== envelope.factId ||
278
+ stateCommit.stateRoot !== proofs.state?.root ||
279
+ Number(stateCommit.mutationCount || 0) !== mutationKeys.length ||
280
+ mutationActions.length !== mutationKeys.length ||
281
+ Number(stateCommit.touchedKeyCount || 0) !== touchedKeyProofs.length;
282
+ if (invalidStateCommit) {
283
+ failures.push(createVerificationFailure({
284
+ layer: "proof-semantics",
285
+ code: "bad_state_commit_binding",
286
+ message: "State Commit material does not bind to the envelope outcome and state proof root.",
287
+ evidenceRef: envelope.envelopeId
288
+ }));
289
+ }
290
+ }
291
+ for (const [index, proof] of asArray(proofs.state?.touchedKeyProofs).entries()) {
292
+ const mutationKey = asArray(stateCommit?.mutationKeys)[index] || "";
293
+ const mutationAction = asArray(stateCommit?.mutationActions)[index] || "";
294
+ expectIndexProof(proof, {
295
+ root: proofs.state?.root || "",
296
+ key: String(mutationKey || proof?.key || ""),
297
+ proofType: mutationAction === "delete" ? "index.non-membership.prolly-path" : "index.membership.prolly-path",
298
+ label: `state touched key proof ${index}`
299
+ });
300
+ }
301
+
302
+ const checkpointProof = proofs.checkpoint?.proof;
303
+ if (checkpointProof) {
304
+ expectIndexProof(checkpointProof, {
305
+ root: proofs.checkpoint?.root || "",
306
+ proofType: "index.membership.prolly-path",
307
+ label: "checkpoint proof"
308
+ });
309
+ const metadata = checkpointProof.entry?.metadata || {};
310
+ const isOutcome = envelope.envelopeKind === "operation-outcome";
311
+ const invalidCheckpoint = metadata.ledgerEventId !== envelope.factRef?.ledgerEventId ||
312
+ metadata.checkpointKind !== (isOutcome ? "outcome" : "intent") ||
313
+ (isOutcome && (
314
+ metadata.intentId !== envelope.factId ||
315
+ metadata.outcomeId !== stateCommit?.outcomeId ||
316
+ metadata.stateCommitId !== stateCommit?.stateCommitId
317
+ )) ||
318
+ (!isOutcome && metadata.intentId !== envelope.factId);
319
+ if (invalidCheckpoint) {
320
+ failures.push(createVerificationFailure({
321
+ layer: "proof-semantics",
322
+ code: "bad_checkpoint_binding",
323
+ message: "Checkpoint proof metadata does not bind to the envelope fact.",
324
+ evidenceRef: envelope.envelopeId
325
+ }));
326
+ }
327
+ }
328
+ const ledgerLeaf = proofMaterial?.ledger?.inclusionProof?.leaf || {};
329
+ const causalityRefs = asArray(ledgerLeaf.causalityRefs).map(String);
330
+ const causalityOutcomeId = String(stateCommit?.outcomeId || ledgerLeaf.outcomeId || "");
331
+ for (const [index, proof] of asArray(proofs.causality?.proofs).entries()) {
332
+ const expectedCausalityKey = causalityRefs[index] && causalityOutcomeId
333
+ ? `${causalityRefs[index]}\u0000${causalityOutcomeId}`
334
+ : "";
335
+ expectIndexProof(proof, {
336
+ root: proofs.causality?.root || "",
337
+ key: expectedCausalityKey,
338
+ proofType: "index.membership.prolly-path",
339
+ label: `causality proof ${index}`
340
+ });
341
+ }
342
+ if (proofs.openIntent) {
343
+ expectIndexProof(proofs.openIntent, {
344
+ key: envelope.factId,
345
+ proofType: "index.membership.prolly-path",
346
+ label: "open intent proof"
347
+ });
348
+ }
349
+ if (proofs.outcome) {
350
+ expectIndexProof(proofs.outcome, {
351
+ key: stateCommit?.intentId || proofs.checkpoint?.proof?.entry?.metadata?.intentId || "",
352
+ proofType: "index.membership.prolly-path",
353
+ label: "outcome proof"
354
+ });
355
+ }
356
+ if (proofs.openIntentRemoved) {
357
+ expectIndexProof(proofs.openIntentRemoved, {
358
+ key: stateCommit?.intentId || proofs.checkpoint?.proof?.entry?.metadata?.intentId || "",
359
+ proofType: "index.non-membership.prolly-path",
360
+ label: "open intent removal proof"
361
+ });
362
+ }
363
+ }
364
+
365
+ export async function verifyProofEnvelope(envelope, {
366
+ storage = null,
367
+ bundle = null,
368
+ supportedCriticalExtensions = [],
369
+ proofVerifiers = {},
370
+ requireAllProofs = true,
371
+ verifierManifest = null,
372
+ ledgerHeadSignatures = [],
373
+ bundleResolver = null,
374
+ includeBundleResolverFailures = true
375
+ } = {}) {
376
+ const failures = [];
377
+ const checkedProofPaths = [];
378
+ const supported = new Set([...CORE_CRITICAL_EXTENSIONS, ...supportedCriticalExtensions]);
379
+ const bundleMap = bundleResolver || bundleBlockMap(bundle);
380
+ if (!envelope || envelope.protocol !== PACTIUM_PROTOCOL || envelope.envelopeType !== "pactium.proof-envelope") {
381
+ return {
382
+ protocol: PACTIUM_PROTOCOL,
383
+ ok: false,
384
+ failures: [createVerificationFailure({
385
+ layer: "proof-envelope",
386
+ code: "malformed_envelope",
387
+ message: "Proof Envelope is missing or has the wrong protocol."
388
+ })]
389
+ };
390
+ }
391
+ if (finalizeEnvelope(envelope).envelopeId !== envelope.envelopeId) {
392
+ failures.push(createVerificationFailure({
393
+ layer: "proof-envelope",
394
+ code: "bad_envelope_id",
395
+ message: "Proof Envelope id does not match its hash-bound body.",
396
+ evidenceRef: envelope.envelopeId
397
+ }));
398
+ }
399
+ for (const critical of asArray(envelope.criticalExtensions)) {
400
+ if (!supported.has(critical)) {
401
+ failures.push(createVerificationFailure({
402
+ layer: "proof-extension",
403
+ code: "unsupported_critical_extension",
404
+ message: `Unsupported critical Proof Extension: ${critical}`,
405
+ evidenceRef: critical,
406
+ repairable: true
407
+ }));
408
+ }
409
+ }
410
+ let proofMaterial = null;
411
+ for (const proofRef of asArray(envelope.proofRefs)) {
412
+ let block = null;
413
+ try {
414
+ block = await resolveBlock({ cid: proofRef.cid, storage, bundleMap });
415
+ } catch (error) {
416
+ failures.push(createVerificationFailure({
417
+ layer: "proof-material",
418
+ code: "replaced_proof_material",
419
+ message: error instanceof Error ? error.message : "Proof material ref was replaced or corrupted.",
420
+ evidenceRef: proofRef.cid
421
+ }));
422
+ continue;
423
+ }
424
+ if (!block) {
425
+ failures.push(createVerificationFailure({
426
+ layer: "proof-material",
427
+ code: "missing_proof_material",
428
+ message: "Proof material ref is missing.",
429
+ evidenceRef: proofRef.cid,
430
+ repairable: true
431
+ }));
432
+ continue;
433
+ }
434
+ if (block.payloadHash !== proofRef.payloadHash || block.byteLength !== proofRef.byteLength) {
435
+ failures.push(createVerificationFailure({
436
+ layer: "proof-material",
437
+ code: "replaced_proof_material",
438
+ message: "Proof material ref was replaced or corrupted.",
439
+ evidenceRef: proofRef.cid
440
+ }));
441
+ continue;
442
+ }
443
+ const value = await decodeBlockValue(block);
444
+ if (value?.materialType === "pactium.proof-material") proofMaterial = value;
445
+ }
446
+ for (const extension of asArray(envelope.extensions)) {
447
+ let block = null;
448
+ try {
449
+ block = await resolveBlock({ cid: extension.valueRef, storage, bundleMap });
450
+ } catch (error) {
451
+ failures.push(createVerificationFailure({
452
+ layer: "proof-extension",
453
+ code: "bad_extension_hash",
454
+ message: error instanceof Error ? error.message : "Proof Extension material was replaced or corrupted.",
455
+ evidenceRef: extension.valueRef
456
+ }));
457
+ continue;
458
+ }
459
+ if (!block) {
460
+ failures.push(createVerificationFailure({
461
+ layer: "proof-extension",
462
+ code: "missing_extension_material",
463
+ message: "Proof Extension material is missing.",
464
+ evidenceRef: extension.valueRef,
465
+ repairable: true
466
+ }));
467
+ continue;
468
+ }
469
+ if (block.payloadHash !== extension.valueHash) {
470
+ failures.push(createVerificationFailure({
471
+ layer: "proof-extension",
472
+ code: "bad_extension_hash",
473
+ message: "Proof Extension material hash does not match the envelope binding.",
474
+ evidenceRef: extension.valueRef
475
+ }));
476
+ }
477
+ }
478
+ if (!proofMaterial?.ledger) {
479
+ failures.push(createVerificationFailure({
480
+ layer: "ledger",
481
+ code: "missing_ledger_proof",
482
+ message: "Proof Envelope has no Ledger inclusion material.",
483
+ repairable: true
484
+ }));
485
+ } else {
486
+ if (!verifyLedgerInclusionProof({
487
+ head: proofMaterial.ledger.head,
488
+ proof: proofMaterial.ledger.inclusionProof
489
+ })) {
490
+ failures.push(createVerificationFailure({
491
+ layer: "ledger",
492
+ code: "bad_ledger_inclusion",
493
+ message: "Ledger inclusion proof does not verify.",
494
+ evidenceRef: envelope.factRef?.ledgerEventId || ""
495
+ }));
496
+ }
497
+ if (!verifyLedgerConsistencyProof({
498
+ oldHead: proofMaterial.ledger.previousHead,
499
+ newHead: proofMaterial.ledger.head,
500
+ proof: proofMaterial.ledger.consistencyProof
501
+ })) {
502
+ failures.push(createVerificationFailure({
503
+ layer: "ledger",
504
+ code: "bad_ledger_consistency",
505
+ message: "Ledger consistency proof does not verify.",
506
+ repairable: true
507
+ }));
508
+ }
509
+ const manifestForHead = verifierManifest || proofMaterial.ledger.verifierManifest || proofMaterial.ledger.head?.verifierManifest || null;
510
+ const signaturesForHead = asArray(ledgerHeadSignatures).length > 0
511
+ ? ledgerHeadSignatures
512
+ : asArray(proofMaterial.ledger.ledgerHeadSignatures || proofMaterial.ledger.head?.signatures);
513
+ if (manifestForHead) {
514
+ const signatureResult = verifyLedgerHeadSignature(proofMaterial.ledger.head, manifestForHead, {
515
+ signatures: signaturesForHead
516
+ });
517
+ failures.push(...signatureResult.failures);
518
+ if (signatureResult.ok) checkedProofPaths.push("ledger-head-signature");
519
+ }
520
+ }
521
+ if (proofMaterial) {
522
+ verifySemanticBindings({ envelope, proofMaterial, failures });
523
+ await verifyEmbeddedProofs({
524
+ proofMaterial,
525
+ registry: createDefaultProofVerifierRegistry(proofVerifiers),
526
+ requireAllProofs,
527
+ failures,
528
+ checked: checkedProofPaths
529
+ });
530
+ }
531
+ if (includeBundleResolverFailures && bundleMap?.failures) {
532
+ failures.push(...bundleMap.failures);
533
+ }
534
+ return {
535
+ protocol: PACTIUM_PROTOCOL,
536
+ envelopeId: envelope.envelopeId,
537
+ ok: failures.length === 0,
538
+ failures,
539
+ checked: [
540
+ "envelope-id",
541
+ "proof-material-refs",
542
+ "critical-extensions",
543
+ "ledger-inclusion",
544
+ "ledger-consistency",
545
+ ...checkedProofPaths
546
+ ]
547
+ };
548
+ }
@@ -0,0 +1,18 @@
1
+ import { verifyIndexProof } from "../index-engine/snapshot-merkle-index.js";
2
+ import { verifyLedgerConsistencyProof, verifyLedgerInclusionProof } from "../ledger/transparency-log.js";
3
+ import { PACTIUM_PROOF_TYPES } from "../protocol/constants.js";
4
+ import { asRecord } from "../shared/records.js";
5
+
6
+ export function createDefaultProofVerifierRegistry(extraVerifiers = {}) {
7
+ return new Map([
8
+ [PACTIUM_PROOF_TYPES.ledgerInclusion, (proof, context = {}) => verifyLedgerInclusionProof({ head: context.head || {}, proof })],
9
+ [PACTIUM_PROOF_TYPES.ledgerConsistency, (proof, context = {}) => verifyLedgerConsistencyProof({
10
+ oldHead: context.oldHead || {},
11
+ newHead: context.newHead || {},
12
+ proof
13
+ })],
14
+ [PACTIUM_PROOF_TYPES.indexMembership, verifyIndexProof],
15
+ [PACTIUM_PROOF_TYPES.indexNonMembership, verifyIndexProof],
16
+ ...Object.entries(asRecord(extraVerifiers))
17
+ ]);
18
+ }
@@ -0,0 +1,69 @@
1
+ export const PACTIUM_PROTOCOL = "pactium.v0.2";
2
+ export const PACTIUM_SCHEMA_VERSION = "pactium.v0.2.schema.latest";
3
+ export const PACTIUM_PACKAGE_VERSION = "0.2.0";
4
+ export const PACTIUM_INDEX_ENGINE = "pactium.verifiable-index-engine";
5
+ export const PACTIUM_INDEX_SPLITTER = "pactium-cdc-boundary";
6
+ export const PACTIUM_PROOF_BUNDLE_TYPE = "pactium.proof-bundle.indexed";
7
+ export const PACTIUM_BUNDLE_ENCODING = "pactium.bundle.indexed-record-stream";
8
+ export const PACTIUM_PROOF_TYPES = Object.freeze({
9
+ ledgerInclusion: "ledger.inclusion.audit-path",
10
+ ledgerConsistency: "ledger.consistency.audit-path",
11
+ indexMembership: "index.membership.prolly-path",
12
+ indexNonMembership: "index.non-membership.prolly-path"
13
+ });
14
+
15
+ export const PACTIUM_PROTOCOL_PROFILE = Object.freeze({
16
+ protocol: PACTIUM_PROTOCOL,
17
+ schema: PACTIUM_SCHEMA_VERSION,
18
+ hash: "sha256",
19
+ cid: "cid:sha256:<hex>",
20
+ canonicalValue: "restricted-ipld-dag-cbor-style",
21
+ orderingAuthority: "operation-ledger",
22
+ ledger: {
23
+ model: "rfc6962-transparency-log",
24
+ leafHash: "H(0x00 || canonical(leaf))",
25
+ nodeHash: "H(0x01 || leftHash || rightHash)",
26
+ emptyTreeHash: "H(\"\")",
27
+ appendLane: "single-ledger-append-lane"
28
+ },
29
+ indexEngine: {
30
+ structure: "canonical-prolly-tree",
31
+ chunking: {
32
+ minEntries: 32,
33
+ targetEntries: 64,
34
+ maxEntries: 128,
35
+ boundaryMask: 63
36
+ }
37
+ },
38
+ licoLite: {
39
+ exportPath: "pactium/licolite",
40
+ workspaceProjection: "enabled-by-default",
41
+ signing: "enabled-by-default",
42
+ criticalExtensions: [
43
+ "licolite.policy",
44
+ "licolite.workspaceEffect"
45
+ ],
46
+ dataSupport: "latest-schema-only"
47
+ }
48
+ });
49
+
50
+ export const HASH_DOMAINS = Object.freeze({
51
+ block: "pactium.v0.2.block",
52
+ "append.condition": "pactium.v0.2.append.condition",
53
+ "checkpoint.node": "pactium.v0.2.checkpoint.node",
54
+ "index.boundary": "pactium.v0.2.index.boundary",
55
+ "index.leaf": "pactium.v0.2.index.leaf",
56
+ "index.node": "pactium.v0.2.index.node",
57
+ "ledger.consistency": "pactium.v0.2.ledger.consistency",
58
+ "ledger.event-id": "pactium.v0.2.ledger.event-id",
59
+ "ledger.head.signing": "pactium.v0.2.ledger.head.signing",
60
+ "operation.intent": "pactium.v0.2.operation.intent",
61
+ "operation.outcome": "pactium.v0.2.operation.outcome",
62
+ "proof.bundle": "pactium.v0.2.proof.bundle",
63
+ "proof.envelope": "pactium.v0.2.proof.envelope",
64
+ "proof.envelope.signing": "pactium.v0.2.proof.envelope.signing",
65
+ "proof.extension": "pactium.v0.2.proof.extension",
66
+ "state.commit": "pactium.v0.2.state.commit",
67
+ "verifier.manifest": "pactium.v0.2.verifier.manifest",
68
+ "workspace.projection": "pactium.v0.2.workspace.projection"
69
+ });
@@ -0,0 +1,47 @@
1
+ import crypto from "node:crypto";
2
+ import { HASH_DOMAINS, PACTIUM_PROTOCOL } from "./constants.js";
3
+ import { canonicalEncode } from "../canonical/value.js";
4
+
5
+ export function hashBytes(bytes) {
6
+ return crypto.createHash("sha256").update(bytes).digest("hex");
7
+ }
8
+
9
+ export function hexToBytes(hex) {
10
+ return Buffer.from(String(hex || ""), "hex");
11
+ }
12
+
13
+ export function cidFromHex(hex) {
14
+ return `cid:sha256:${hex}`;
15
+ }
16
+
17
+ export function hexFromCid(cid) {
18
+ const text = String(cid || "");
19
+ return text.startsWith("cid:sha256:") ? text.slice("cid:sha256:".length) : "";
20
+ }
21
+
22
+ export function createId(prefix, value) {
23
+ return `${prefix}_${protocolHashHex(prefix, value).slice(0, 32)}`;
24
+ }
25
+
26
+ export function protocolHashHex(domain, value) {
27
+ const separator = HASH_DOMAINS[domain] || String(domain || "pactium.v0.2.generic");
28
+ const bytes = Buffer.isBuffer(value) || value instanceof Uint8Array
29
+ ? Buffer.from(value)
30
+ : Buffer.from(canonicalEncode(value));
31
+ return hashBytes(Buffer.concat([
32
+ Buffer.from(`${PACTIUM_PROTOCOL}:${separator}\0`, "utf8"),
33
+ bytes
34
+ ]));
35
+ }
36
+
37
+ export function protocolHash(domain, value) {
38
+ return `sha256:${protocolHashHex(domain, value)}`;
39
+ }
40
+
41
+ export function cidForBytes(bytes) {
42
+ return cidFromHex(hashBytes(Buffer.from(bytes || "")));
43
+ }
44
+
45
+ export function cidForCanonical(value) {
46
+ return cidForBytes(canonicalEncode(value));
47
+ }