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.
- package/LICENSE +674 -0
- package/README.md +92 -0
- package/README.zh-CN.md +90 -0
- package/SECURITY.md +7 -0
- package/bin/pactium.mjs +121 -0
- package/docs/LICOLITE-ASPECT.md +57 -0
- package/docs/README.md +13 -0
- package/docs/TERM.md +289 -0
- package/docs/architecture/ARCHITECTURE.md +62 -0
- package/docs/protocols/PROFILE.md +124 -0
- package/docs/protocols/PROTOCOLS.md +62 -0
- package/examples/record-operation.mjs +26 -0
- package/package.json +69 -0
- package/src/README.md +13 -0
- package/src/aspects/licolite/aspect.js +278 -0
- package/src/aspects/licolite/constants.js +13 -0
- package/src/aspects/licolite/evidence.js +47 -0
- package/src/aspects/licolite/index.d.ts +51 -0
- package/src/aspects/licolite/index.js +19 -0
- package/src/aspects/licolite/signing.js +78 -0
- package/src/canonical/value.js +40 -0
- package/src/core/append-condition.js +102 -0
- package/src/core/pactium-core.js +1073 -0
- package/src/core/tracking-cursor.js +68 -0
- package/src/http.js +99 -0
- package/src/index-engine/snapshot-merkle-index.js +994 -0
- package/src/index.d.ts +244 -0
- package/src/index.js +73 -0
- package/src/ledger/signed-head.js +204 -0
- package/src/ledger/transparency-log.js +702 -0
- package/src/maintenance/task-engine.js +36 -0
- package/src/proof/bundle-format.js +265 -0
- package/src/proof/bundle.js +77 -0
- package/src/proof/envelope.js +548 -0
- package/src/proof/registry.js +18 -0
- package/src/protocol/constants.js +69 -0
- package/src/protocol/hashing.js +47 -0
- package/src/quality/profile-runner.js +291 -0
- package/src/repair/planner.js +62 -0
- package/src/shared/records.js +32 -0
- package/src/storage/local-json-storage-port.js +360 -0
- package/src/verification/failure.js +31 -0
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
export type PactiumCanonicalValue =
|
|
2
|
+
| null
|
|
3
|
+
| boolean
|
|
4
|
+
| number
|
|
5
|
+
| string
|
|
6
|
+
| PactiumCanonicalValue[]
|
|
7
|
+
| { [key: string]: PactiumCanonicalValue };
|
|
8
|
+
|
|
9
|
+
export type PactiumRecord = Record<string, unknown>;
|
|
10
|
+
|
|
11
|
+
export interface PactiumDataDirOptions {
|
|
12
|
+
dataDir?: string;
|
|
13
|
+
userDataPath?: string;
|
|
14
|
+
inMemory?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface PactiumStoragePort {
|
|
18
|
+
protocol: string;
|
|
19
|
+
schema: string;
|
|
20
|
+
dataDir: string;
|
|
21
|
+
inMemory: boolean;
|
|
22
|
+
initialize(): Promise<void>;
|
|
23
|
+
putBlock(value: unknown, options?: PactiumRecord): Promise<PactiumRecord>;
|
|
24
|
+
getBlock(cid: string): Promise<(PactiumRecord & { bytes?: Uint8Array }) | null>;
|
|
25
|
+
hasBlock(cid: string): Promise<boolean>;
|
|
26
|
+
walk(rootCid: string): Promise<PactiumRecord>;
|
|
27
|
+
putProtocolObject(scope: string, key: string, value: unknown): Promise<PactiumCanonicalValue>;
|
|
28
|
+
getProtocolObject(scope: string, key: string, fallback?: unknown): Promise<unknown>;
|
|
29
|
+
clearCache?(): void;
|
|
30
|
+
withWriteLock?<T>(
|
|
31
|
+
task: () => T | Promise<T>,
|
|
32
|
+
options?: { name?: string; timeoutMs?: number; retryMs?: number; staleMs?: number }
|
|
33
|
+
): Promise<T>;
|
|
34
|
+
pruneBlocks?(predicate?: (record: PactiumRecord) => boolean): number;
|
|
35
|
+
pruneProtocolObjects?(predicate?: (record: PactiumRecord & { scope: string; key: string; value: unknown }) => boolean): number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface PactiumLedgerHead {
|
|
39
|
+
protocol: string;
|
|
40
|
+
schema: string;
|
|
41
|
+
size: number;
|
|
42
|
+
rootHash: string;
|
|
43
|
+
root: string;
|
|
44
|
+
headId?: string;
|
|
45
|
+
previousHeadId?: string;
|
|
46
|
+
createdAt?: string;
|
|
47
|
+
signatureRef?: string;
|
|
48
|
+
signatureHash?: string;
|
|
49
|
+
verifierManifest?: PactiumRecord;
|
|
50
|
+
verifierManifestRef?: string;
|
|
51
|
+
signatures?: PactiumRecord[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface PactiumProofEnvelope {
|
|
55
|
+
protocol: string;
|
|
56
|
+
schema: string;
|
|
57
|
+
envelopeType: "pactium.proof-envelope";
|
|
58
|
+
envelopeKind: string;
|
|
59
|
+
envelopeId: string;
|
|
60
|
+
factType: string;
|
|
61
|
+
factId: string;
|
|
62
|
+
factRef: PactiumRecord;
|
|
63
|
+
ledgerHead: PactiumLedgerHead;
|
|
64
|
+
proofRefs: PactiumRecord[];
|
|
65
|
+
extensions: PactiumRecord[];
|
|
66
|
+
criticalExtensions: string[];
|
|
67
|
+
relatedEnvelopeIds: string[];
|
|
68
|
+
replayed: boolean;
|
|
69
|
+
createdAt: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface PactiumVerificationFailure {
|
|
73
|
+
protocol: string;
|
|
74
|
+
layer: string;
|
|
75
|
+
code: string;
|
|
76
|
+
severity: string;
|
|
77
|
+
message?: string;
|
|
78
|
+
evidenceRef?: string;
|
|
79
|
+
repairable?: boolean;
|
|
80
|
+
details?: PactiumRecord;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface PactiumVerificationResult {
|
|
84
|
+
protocol: string;
|
|
85
|
+
ok: boolean;
|
|
86
|
+
envelopeId?: string;
|
|
87
|
+
failures: PactiumVerificationFailure[];
|
|
88
|
+
checked?: string[];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface PactiumProofBundle {
|
|
92
|
+
protocol: string;
|
|
93
|
+
schema: string;
|
|
94
|
+
bundleType: "pactium.proof-bundle.indexed";
|
|
95
|
+
manifest: PactiumRecord;
|
|
96
|
+
envelope: PactiumProofEnvelope;
|
|
97
|
+
index?: PactiumRecord[];
|
|
98
|
+
blocksEncoding?: string;
|
|
99
|
+
binaryBase64?: string;
|
|
100
|
+
byteLength?: number;
|
|
101
|
+
bundleHash: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface PactiumIndexScanOptions extends PactiumRecord {
|
|
105
|
+
min?: string;
|
|
106
|
+
max?: string;
|
|
107
|
+
limit?: number;
|
|
108
|
+
after?: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface PactiumIndexEngine {
|
|
112
|
+
protocol: string;
|
|
113
|
+
engine: string;
|
|
114
|
+
domain: string;
|
|
115
|
+
createIndex(entries?: PactiumRecord[], options?: PactiumRecord): Promise<PactiumRecord>;
|
|
116
|
+
put(root: string, key: string, value: unknown, options?: PactiumRecord): Promise<PactiumRecord>;
|
|
117
|
+
delete(root: string, key: string, options?: PactiumRecord): Promise<PactiumRecord>;
|
|
118
|
+
get(root: string, key: string): Promise<PactiumRecord | null>;
|
|
119
|
+
prove(root: string, key: string): Promise<PactiumRecord>;
|
|
120
|
+
verifyProof(proof: PactiumRecord): boolean;
|
|
121
|
+
scan(root: string, options?: PactiumIndexScanOptions): Promise<PactiumRecord[]>;
|
|
122
|
+
prefix(root: string, keyPrefix?: string, options?: PactiumIndexScanOptions): Promise<PactiumRecord[]>;
|
|
123
|
+
diff(leftRoot: string, rightRoot: string): Promise<PactiumRecord[]>;
|
|
124
|
+
readSnapshot(root: string): Promise<PactiumRecord>;
|
|
125
|
+
readIndexRoot(root: string): Promise<PactiumRecord>;
|
|
126
|
+
readNode(root: string): Promise<PactiumRecord>;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface PactiumLedgerPageOptions extends PactiumRecord {
|
|
130
|
+
start?: number;
|
|
131
|
+
limit?: number;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface PactiumLedgerPage extends PactiumRecord {
|
|
135
|
+
protocol: string;
|
|
136
|
+
start: number;
|
|
137
|
+
limit: number;
|
|
138
|
+
entries: PactiumRecord[];
|
|
139
|
+
nextPosition: number;
|
|
140
|
+
head: PactiumLedgerHead;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export interface PactiumLedger extends PactiumRecord {
|
|
144
|
+
append(entry?: PactiumRecord): Promise<PactiumRecord>;
|
|
145
|
+
head(): Promise<PactiumLedgerHead>;
|
|
146
|
+
entries(): Promise<PactiumRecord[]>;
|
|
147
|
+
pageEntries(options?: PactiumLedgerPageOptions): Promise<PactiumLedgerPage>;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export interface PactiumProofBundleVerificationOptions extends PactiumRecord {
|
|
151
|
+
verifyAllBlocks?: boolean;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface PactiumProofBundleExportOptions extends PactiumRecord {
|
|
155
|
+
format?: "indexed";
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface PactiumCore {
|
|
159
|
+
protocol: string;
|
|
160
|
+
schema: string;
|
|
161
|
+
dataDir: string;
|
|
162
|
+
storage: PactiumStoragePort;
|
|
163
|
+
ledger: PactiumLedger;
|
|
164
|
+
indexEngine: PactiumIndexEngine;
|
|
165
|
+
beginOperationIntent(input?: PactiumRecord): Promise<PactiumProofEnvelope>;
|
|
166
|
+
appendOperationOutcome(input?: PactiumRecord): Promise<PactiumProofEnvelope>;
|
|
167
|
+
recordOperation(input?: PactiumRecord): Promise<PactiumProofEnvelope>;
|
|
168
|
+
lookupOpenIntent(intentId: string): Promise<PactiumRecord>;
|
|
169
|
+
lookupOutcome(intentId: string): Promise<PactiumRecord>;
|
|
170
|
+
createAppendCondition(input?: PactiumRecord): PactiumRecord;
|
|
171
|
+
getLedgerCursor(input?: PactiumRecord): Promise<PactiumRecord>;
|
|
172
|
+
getWorkspaceCursor(input?: PactiumRecord): Promise<PactiumRecord>;
|
|
173
|
+
verifyCursor(cursor: PactiumRecord, context?: PactiumRecord): boolean;
|
|
174
|
+
advanceTrustedHead(input?: PactiumRecord): PactiumRecord;
|
|
175
|
+
planRecovery(input?: PactiumRecord): PactiumRecord;
|
|
176
|
+
getWorkspaceProjection(workspaceId?: string): Promise<PactiumRecord>;
|
|
177
|
+
proveWorkspaceMembership(input?: PactiumRecord): Promise<PactiumRecord>;
|
|
178
|
+
verifyEnvelope(envelope: PactiumProofEnvelope, options?: PactiumRecord): Promise<PactiumVerificationResult>;
|
|
179
|
+
exportProofBundle(envelopeOrId: PactiumProofEnvelope | string, options?: PactiumProofBundleExportOptions): Promise<PactiumProofBundle>;
|
|
180
|
+
createExtension(extension: PactiumRecord): Promise<PactiumRecord>;
|
|
181
|
+
storeEnvelope(envelope: PactiumProofEnvelope): Promise<PactiumProofEnvelope>;
|
|
182
|
+
protocolCatalog(): Promise<PactiumRecord>;
|
|
183
|
+
doctor(): Promise<PactiumRecord & { ok: boolean; dataDir: string }>;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export const PACTIUM_PROTOCOL: "pactium.v0.2";
|
|
187
|
+
export const PACTIUM_SCHEMA_VERSION: "pactium.v0.2.schema.latest";
|
|
188
|
+
export const PACTIUM_PACKAGE_VERSION: "0.2.0";
|
|
189
|
+
export const PACTIUM_INDEX_ENGINE: "pactium.verifiable-index-engine";
|
|
190
|
+
export const PACTIUM_INDEX_SPLITTER: "pactium-cdc-boundary";
|
|
191
|
+
export const PACTIUM_PROOF_BUNDLE_TYPE: "pactium.proof-bundle.indexed";
|
|
192
|
+
export const PACTIUM_BUNDLE_ENCODING: "pactium.bundle.indexed-record-stream";
|
|
193
|
+
export const PACTIUM_PROOF_TYPES: Readonly<{
|
|
194
|
+
ledgerInclusion: "ledger.inclusion.audit-path";
|
|
195
|
+
ledgerConsistency: "ledger.consistency.audit-path";
|
|
196
|
+
indexMembership: "index.membership.prolly-path";
|
|
197
|
+
indexNonMembership: "index.non-membership.prolly-path";
|
|
198
|
+
}>;
|
|
199
|
+
export const PACTIUM_PROTOCOL_PROFILE: PactiumRecord;
|
|
200
|
+
export const HASH_DOMAINS: Record<string, string>;
|
|
201
|
+
|
|
202
|
+
export function defaultPactiumDataDir(): string;
|
|
203
|
+
export function resolveDataDir(dataDir?: string): string;
|
|
204
|
+
export function resolveWithin(root: string, ...segments: string[]): string;
|
|
205
|
+
export function normalizeCanonicalValue(value: unknown): PactiumCanonicalValue;
|
|
206
|
+
export function canonicalString(value: unknown): string;
|
|
207
|
+
export function canonicalEncode(value: unknown): Uint8Array;
|
|
208
|
+
export function canonicalDecode(bytes: Uint8Array | ArrayBuffer | string): PactiumCanonicalValue;
|
|
209
|
+
export function protocolHashHex(domain: string, value: unknown): string;
|
|
210
|
+
export function protocolHash(domain: string, value: unknown): string;
|
|
211
|
+
export function cidForBytes(bytes: Uint8Array | ArrayBuffer | string): string;
|
|
212
|
+
export function cidForCanonical(value: unknown): string;
|
|
213
|
+
export function createVerificationFailure(input?: PactiumRecord): PactiumVerificationFailure;
|
|
214
|
+
export function createStoragePort(options?: PactiumDataDirOptions): PactiumStoragePort;
|
|
215
|
+
export function ledgerLeafHash(leaf: unknown): string;
|
|
216
|
+
export function ledgerNodeHash(leftHash: string, rightHash: string): string;
|
|
217
|
+
export function emptyTreeHash(): string;
|
|
218
|
+
export function createCompactRange(input?: PactiumRecord): PactiumRecord;
|
|
219
|
+
export function createLedgerInclusionProof(input?: PactiumRecord): PactiumRecord;
|
|
220
|
+
export function verifyLedgerInclusionProof(input?: PactiumRecord): boolean;
|
|
221
|
+
export function createLedgerConsistencyProof(input?: PactiumRecord): PactiumRecord;
|
|
222
|
+
export function verifyLedgerConsistencyProof(input?: PactiumRecord): boolean;
|
|
223
|
+
export function createLedgerTransparencyLog(options?: PactiumRecord): PactiumLedger;
|
|
224
|
+
export function createVerifierManifest(input?: PactiumRecord): PactiumRecord;
|
|
225
|
+
export function ledgerHeadSigningPayload(head?: PactiumRecord): PactiumRecord;
|
|
226
|
+
export function signLedgerHead(head?: PactiumRecord, options?: PactiumRecord): PactiumRecord;
|
|
227
|
+
export function verifyLedgerHeadSignature(head?: PactiumRecord, manifest?: PactiumRecord, options?: PactiumRecord): PactiumVerificationResult & { accepted?: number };
|
|
228
|
+
export function advanceTrustedHead(input?: PactiumRecord): PactiumRecord;
|
|
229
|
+
export function createVerifiableIndexEngine(options?: PactiumRecord): PactiumIndexEngine;
|
|
230
|
+
export function verifyIndexProof(proof: PactiumRecord): boolean;
|
|
231
|
+
export function createAppendCondition(input?: PactiumRecord): PactiumRecord;
|
|
232
|
+
export function createTrackingCursor(input?: PactiumRecord): PactiumRecord;
|
|
233
|
+
export function covers(cursor: PactiumRecord, position: number): boolean;
|
|
234
|
+
export function advanceTo(cursor: PactiumRecord, position: number, options?: PactiumRecord): PactiumRecord;
|
|
235
|
+
export function samePositionAs(left: PactiumRecord, right: PactiumRecord): boolean;
|
|
236
|
+
export function verifyTrackingCursor(cursor: PactiumRecord, context?: PactiumRecord): boolean;
|
|
237
|
+
export function createPactium(options?: PactiumDataDirOptions & { storage?: PactiumStoragePort | null }): PactiumCore;
|
|
238
|
+
export function verifyProofEnvelope(envelope: PactiumProofEnvelope, options?: PactiumRecord): Promise<PactiumVerificationResult>;
|
|
239
|
+
export function verifyProofBundle(bundle: PactiumProofBundle, options?: PactiumProofBundleVerificationOptions): Promise<PactiumVerificationResult & { bundleHash?: string }>;
|
|
240
|
+
export function createDefaultProofVerifierRegistry(extraVerifiers?: PactiumRecord): Map<string, (...args: unknown[]) => unknown>;
|
|
241
|
+
export function createRepairPlanner(): PactiumRecord;
|
|
242
|
+
export function createMaintenanceTaskEngine(options?: PactiumRecord): PactiumRecord;
|
|
243
|
+
export function envelopeSigningHash(envelope: PactiumProofEnvelope): string;
|
|
244
|
+
export function runPactiumQualityGateProfile(options?: PactiumRecord): Promise<PactiumRecord>;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export {
|
|
2
|
+
HASH_DOMAINS,
|
|
3
|
+
PACTIUM_BUNDLE_ENCODING,
|
|
4
|
+
PACTIUM_INDEX_ENGINE,
|
|
5
|
+
PACTIUM_INDEX_SPLITTER,
|
|
6
|
+
PACTIUM_PACKAGE_VERSION,
|
|
7
|
+
PACTIUM_PROOF_BUNDLE_TYPE,
|
|
8
|
+
PACTIUM_PROOF_TYPES,
|
|
9
|
+
PACTIUM_PROTOCOL,
|
|
10
|
+
PACTIUM_PROTOCOL_PROFILE,
|
|
11
|
+
PACTIUM_SCHEMA_VERSION
|
|
12
|
+
} from "./protocol/constants.js";
|
|
13
|
+
export {
|
|
14
|
+
canonicalDecode,
|
|
15
|
+
canonicalEncode,
|
|
16
|
+
canonicalString,
|
|
17
|
+
normalizeCanonicalValue
|
|
18
|
+
} from "./canonical/value.js";
|
|
19
|
+
export {
|
|
20
|
+
cidForBytes,
|
|
21
|
+
cidForCanonical,
|
|
22
|
+
protocolHash,
|
|
23
|
+
protocolHashHex
|
|
24
|
+
} from "./protocol/hashing.js";
|
|
25
|
+
export {
|
|
26
|
+
createStoragePort,
|
|
27
|
+
defaultPactiumDataDir,
|
|
28
|
+
resolveDataDir,
|
|
29
|
+
resolveWithin
|
|
30
|
+
} from "./storage/local-json-storage-port.js";
|
|
31
|
+
export { createVerificationFailure } from "./verification/failure.js";
|
|
32
|
+
export {
|
|
33
|
+
createLedgerConsistencyProof,
|
|
34
|
+
createLedgerInclusionProof,
|
|
35
|
+
createLedgerTransparencyLog,
|
|
36
|
+
createCompactRange,
|
|
37
|
+
emptyTreeHash,
|
|
38
|
+
ledgerLeafHash,
|
|
39
|
+
ledgerNodeHash,
|
|
40
|
+
verifyLedgerConsistencyProof,
|
|
41
|
+
verifyLedgerInclusionProof
|
|
42
|
+
} from "./ledger/transparency-log.js";
|
|
43
|
+
export {
|
|
44
|
+
advanceTrustedHead,
|
|
45
|
+
createVerifierManifest,
|
|
46
|
+
ledgerHeadSigningPayload,
|
|
47
|
+
signLedgerHead,
|
|
48
|
+
verifyLedgerHeadSignature
|
|
49
|
+
} from "./ledger/signed-head.js";
|
|
50
|
+
export {
|
|
51
|
+
createVerifiableIndexEngine,
|
|
52
|
+
verifyIndexProof
|
|
53
|
+
} from "./index-engine/snapshot-merkle-index.js";
|
|
54
|
+
export {
|
|
55
|
+
createAppendCondition
|
|
56
|
+
} from "./core/append-condition.js";
|
|
57
|
+
export {
|
|
58
|
+
advanceTo,
|
|
59
|
+
covers,
|
|
60
|
+
createTrackingCursor,
|
|
61
|
+
samePositionAs,
|
|
62
|
+
verifyTrackingCursor
|
|
63
|
+
} from "./core/tracking-cursor.js";
|
|
64
|
+
export { createPactium } from "./core/pactium-core.js";
|
|
65
|
+
export {
|
|
66
|
+
envelopeSigningHash,
|
|
67
|
+
verifyProofEnvelope
|
|
68
|
+
} from "./proof/envelope.js";
|
|
69
|
+
export { createDefaultProofVerifierRegistry } from "./proof/registry.js";
|
|
70
|
+
export { verifyProofBundle } from "./proof/bundle.js";
|
|
71
|
+
export { createRepairPlanner } from "./repair/planner.js";
|
|
72
|
+
export { createMaintenanceTaskEngine } from "./maintenance/task-engine.js";
|
|
73
|
+
export { runPactiumQualityGateProfile } from "./quality/profile-runner.js";
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
|
|
3
|
+
import { PACTIUM_PROTOCOL, PACTIUM_SCHEMA_VERSION } from "../protocol/constants.js";
|
|
4
|
+
import { canonicalEncode, normalizeCanonicalValue } from "../canonical/value.js";
|
|
5
|
+
import { createId, protocolHash } from "../protocol/hashing.js";
|
|
6
|
+
import { asArray, asRecord, nowIso, safeText } from "../shared/records.js";
|
|
7
|
+
import { createVerificationFailure } from "../verification/failure.js";
|
|
8
|
+
import { verifyLedgerConsistencyProof } from "./transparency-log.js";
|
|
9
|
+
|
|
10
|
+
export function createVerifierManifest(input = {}) {
|
|
11
|
+
const payload = {
|
|
12
|
+
protocol: PACTIUM_PROTOCOL,
|
|
13
|
+
schema: PACTIUM_SCHEMA_VERSION,
|
|
14
|
+
manifestType: "pactium.verifier-manifest",
|
|
15
|
+
signers: asArray(input.signers).map((signer) => ({
|
|
16
|
+
signerId: safeText(signer.signerId),
|
|
17
|
+
algorithm: safeText(signer.algorithm, "ed25519"),
|
|
18
|
+
publicKey: safeText(signer.publicKey),
|
|
19
|
+
validFrom: safeText(signer.validFrom),
|
|
20
|
+
validTo: safeText(signer.validTo),
|
|
21
|
+
roles: asArray(signer.roles).map(String)
|
|
22
|
+
})).filter((signer) => signer.signerId && signer.publicKey),
|
|
23
|
+
quorum: Math.max(1, Number(input.quorum || 1))
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
...payload,
|
|
27
|
+
manifestId: input.manifestId || createId("verifier_manifest", payload),
|
|
28
|
+
manifestHash: protocolHash("verifier.manifest", payload)
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function ledgerHeadSigningPayload(head = {}) {
|
|
33
|
+
return normalizeCanonicalValue({
|
|
34
|
+
protocol: PACTIUM_PROTOCOL,
|
|
35
|
+
schema: PACTIUM_SCHEMA_VERSION,
|
|
36
|
+
ledgerId: head.ledgerId || "pactium-operation-ledger",
|
|
37
|
+
size: Number(head.size || 0),
|
|
38
|
+
rootHash: safeText(head.rootHash),
|
|
39
|
+
root: safeText(head.root),
|
|
40
|
+
headId: safeText(head.headId),
|
|
41
|
+
previousHeadId: safeText(head.previousHeadId),
|
|
42
|
+
createdAt: safeText(head.createdAt)
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function signLedgerHead(head = {}, {
|
|
47
|
+
privateKey = "",
|
|
48
|
+
signerId = "",
|
|
49
|
+
manifest = null,
|
|
50
|
+
createdAt = nowIso()
|
|
51
|
+
} = {}) {
|
|
52
|
+
const resolvedManifest = manifest ? createVerifierManifest(manifest) : null;
|
|
53
|
+
const payload = ledgerHeadSigningPayload(head);
|
|
54
|
+
const signature = crypto.sign(null, Buffer.from(canonicalEncode(payload)), privateKey).toString("base64");
|
|
55
|
+
return {
|
|
56
|
+
protocol: PACTIUM_PROTOCOL,
|
|
57
|
+
schema: PACTIUM_SCHEMA_VERSION,
|
|
58
|
+
signatureType: "pactium.ledger-head-signature",
|
|
59
|
+
signerId: safeText(signerId),
|
|
60
|
+
algorithm: "ed25519",
|
|
61
|
+
manifestId: resolvedManifest?.manifestId || safeText(manifest?.manifestId),
|
|
62
|
+
manifestHash: resolvedManifest?.manifestHash || safeText(manifest?.manifestHash),
|
|
63
|
+
headId: safeText(head.headId),
|
|
64
|
+
signedPayloadHash: protocolHash("ledger.head.signing", payload),
|
|
65
|
+
signature,
|
|
66
|
+
createdAt
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function verifyLedgerHeadSignature(head = {}, manifest = {}, options = {}) {
|
|
71
|
+
const verifierManifest = manifest?.manifestType === "pactium.verifier-manifest"
|
|
72
|
+
? manifest
|
|
73
|
+
: createVerifierManifest(manifest);
|
|
74
|
+
const signatures = asArray(options.signatures || head.signatures || (head.signature ? [head.signature] : []));
|
|
75
|
+
const failures = [];
|
|
76
|
+
let accepted = 0;
|
|
77
|
+
const acceptedSigners = new Set();
|
|
78
|
+
const payload = ledgerHeadSigningPayload(head);
|
|
79
|
+
const payloadBytes = Buffer.from(canonicalEncode(payload));
|
|
80
|
+
for (const signature of signatures) {
|
|
81
|
+
const record = asRecord(signature);
|
|
82
|
+
const signer = asArray(verifierManifest.signers).find((candidate) => candidate.signerId === record.signerId);
|
|
83
|
+
if (!signer) {
|
|
84
|
+
failures.push(createVerificationFailure({
|
|
85
|
+
layer: "ledger-head-signature",
|
|
86
|
+
code: "unknown_signer",
|
|
87
|
+
evidenceRef: record.signerId || ""
|
|
88
|
+
}));
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (record.manifestId && record.manifestId !== verifierManifest.manifestId) {
|
|
92
|
+
failures.push(createVerificationFailure({
|
|
93
|
+
layer: "ledger-head-signature",
|
|
94
|
+
code: "signature_manifest_mismatch",
|
|
95
|
+
evidenceRef: record.signerId || ""
|
|
96
|
+
}));
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (record.manifestHash && record.manifestHash !== verifierManifest.manifestHash) {
|
|
100
|
+
failures.push(createVerificationFailure({
|
|
101
|
+
layer: "ledger-head-signature",
|
|
102
|
+
code: "signature_manifest_mismatch",
|
|
103
|
+
evidenceRef: record.signerId || ""
|
|
104
|
+
}));
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (acceptedSigners.has(record.signerId)) {
|
|
108
|
+
failures.push(createVerificationFailure({
|
|
109
|
+
layer: "ledger-head-signature",
|
|
110
|
+
code: "duplicate_signature_signer",
|
|
111
|
+
evidenceRef: record.signerId || ""
|
|
112
|
+
}));
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (signer.algorithm !== "ed25519" || record.algorithm !== "ed25519") {
|
|
116
|
+
failures.push(createVerificationFailure({
|
|
117
|
+
layer: "ledger-head-signature",
|
|
118
|
+
code: "unsupported_signature_algorithm",
|
|
119
|
+
evidenceRef: record.signerId || ""
|
|
120
|
+
}));
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (!asArray(signer.roles).includes("ledger-head")) {
|
|
124
|
+
failures.push(createVerificationFailure({
|
|
125
|
+
layer: "ledger-head-signature",
|
|
126
|
+
code: "signer_role_missing",
|
|
127
|
+
evidenceRef: record.signerId || ""
|
|
128
|
+
}));
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (record.signedPayloadHash !== protocolHash("ledger.head.signing", payload)) {
|
|
132
|
+
failures.push(createVerificationFailure({
|
|
133
|
+
layer: "ledger-head-signature",
|
|
134
|
+
code: "bad_signed_head_payload",
|
|
135
|
+
evidenceRef: record.signerId || ""
|
|
136
|
+
}));
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const ok = crypto.verify(
|
|
140
|
+
null,
|
|
141
|
+
payloadBytes,
|
|
142
|
+
signer.publicKey,
|
|
143
|
+
Buffer.from(String(record.signature || ""), "base64")
|
|
144
|
+
);
|
|
145
|
+
if (ok) {
|
|
146
|
+
accepted += 1;
|
|
147
|
+
acceptedSigners.add(record.signerId);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
failures.push(createVerificationFailure({
|
|
151
|
+
layer: "ledger-head-signature",
|
|
152
|
+
code: "bad_head_signature",
|
|
153
|
+
evidenceRef: record.signerId || ""
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (accepted < Number(verifierManifest.quorum || 1)) {
|
|
158
|
+
failures.push(createVerificationFailure({
|
|
159
|
+
layer: "ledger-head-signature",
|
|
160
|
+
code: "manifest_quorum_not_met",
|
|
161
|
+
details: { accepted, quorum: verifierManifest.quorum || 1 }
|
|
162
|
+
}));
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
protocol: PACTIUM_PROTOCOL,
|
|
166
|
+
ok: failures.length === 0,
|
|
167
|
+
accepted,
|
|
168
|
+
failures
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function advanceTrustedHead({
|
|
173
|
+
oldHead = {},
|
|
174
|
+
newHead = {},
|
|
175
|
+
proof = {},
|
|
176
|
+
manifest = null,
|
|
177
|
+
signatures = []
|
|
178
|
+
} = {}) {
|
|
179
|
+
const failures = [];
|
|
180
|
+
if (!verifyLedgerConsistencyProof({ oldHead, newHead, proof })) {
|
|
181
|
+
failures.push(createVerificationFailure({
|
|
182
|
+
layer: "trusted-head",
|
|
183
|
+
code: "bad_trusted_head_consistency",
|
|
184
|
+
message: "New Ledger head does not extend the old trusted head."
|
|
185
|
+
}));
|
|
186
|
+
}
|
|
187
|
+
if (manifest) {
|
|
188
|
+
const signatureResult = verifyLedgerHeadSignature(newHead, manifest, { signatures });
|
|
189
|
+
failures.push(...signatureResult.failures);
|
|
190
|
+
}
|
|
191
|
+
if (failures.length > 0) {
|
|
192
|
+
return { protocol: PACTIUM_PROTOCOL, ok: false, failures };
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
protocol: PACTIUM_PROTOCOL,
|
|
196
|
+
ok: true,
|
|
197
|
+
trustStoreType: "pactium.trusted-head-store",
|
|
198
|
+
ledgerId: newHead.ledgerId || oldHead.ledgerId || "pactium-operation-ledger",
|
|
199
|
+
lastTrustedHead: newHead,
|
|
200
|
+
verifierManifestRef: manifest?.manifestId || "",
|
|
201
|
+
updatedAt: nowIso(),
|
|
202
|
+
failures: []
|
|
203
|
+
};
|
|
204
|
+
}
|