@psavelis/enterprise-blockchain 0.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -2
- package/dist/aid-settlement/application/reconciler.d.ts +13 -0
- package/dist/aid-settlement/application/reconciler.d.ts.map +1 -0
- package/dist/aid-settlement/application/reconciler.js +77 -0
- package/dist/aid-settlement/domain/entities.d.ts +24 -0
- package/dist/aid-settlement/domain/entities.d.ts.map +1 -0
- package/dist/aid-settlement/domain/entities.js +1 -0
- package/dist/aid-settlement/domain/ports.d.ts +10 -0
- package/dist/aid-settlement/domain/ports.d.ts.map +1 -0
- package/dist/aid-settlement/domain/ports.js +1 -0
- package/dist/aid-settlement/index.d.ts +19 -0
- package/dist/aid-settlement/index.d.ts.map +1 -0
- package/dist/aid-settlement/index.js +23 -0
- package/dist/aid-settlement/infrastructure/in-memory-store.d.ts +12 -0
- package/dist/aid-settlement/infrastructure/in-memory-store.d.ts.map +1 -0
- package/dist/aid-settlement/infrastructure/in-memory-store.js +17 -0
- package/dist/credentialing/application/clearance-evaluator.d.ts +10 -0
- package/dist/credentialing/application/clearance-evaluator.d.ts.map +1 -0
- package/dist/credentialing/application/clearance-evaluator.js +63 -0
- package/dist/credentialing/domain/entities.d.ts +28 -0
- package/dist/credentialing/domain/entities.d.ts.map +1 -0
- package/dist/credentialing/domain/entities.js +1 -0
- package/dist/credentialing/domain/ports.d.ts +9 -0
- package/dist/credentialing/domain/ports.d.ts.map +1 -0
- package/dist/credentialing/domain/ports.js +1 -0
- package/dist/credentialing/index.d.ts +19 -0
- package/dist/credentialing/index.d.ts.map +1 -0
- package/dist/credentialing/index.js +23 -0
- package/dist/credentialing/infrastructure/in-memory-store.d.ts +11 -0
- package/dist/credentialing/infrastructure/in-memory-store.d.ts.map +1 -0
- package/dist/credentialing/infrastructure/in-memory-store.js +14 -0
- package/dist/hsm/application/asymmetric-key-service.d.ts +23 -0
- package/dist/hsm/application/asymmetric-key-service.d.ts.map +1 -0
- package/dist/hsm/application/asymmetric-key-service.js +109 -0
- package/dist/hsm/application/envelope-encryption-service.d.ts +18 -0
- package/dist/hsm/application/envelope-encryption-service.d.ts.map +1 -0
- package/dist/hsm/application/envelope-encryption-service.js +59 -0
- package/dist/hsm/application/symmetric-key-service.d.ts +34 -0
- package/dist/hsm/application/symmetric-key-service.d.ts.map +1 -0
- package/dist/hsm/application/symmetric-key-service.js +107 -0
- package/dist/hsm/domain/entities.d.ts +104 -0
- package/dist/hsm/domain/entities.d.ts.map +1 -0
- package/dist/hsm/domain/entities.js +10 -0
- package/dist/hsm/domain/ports.d.ts +20 -0
- package/dist/hsm/domain/ports.d.ts.map +1 -0
- package/dist/hsm/domain/ports.js +1 -0
- package/dist/hsm/index.d.ts +48 -0
- package/dist/hsm/index.d.ts.map +1 -0
- package/dist/hsm/index.js +97 -0
- package/dist/hsm/infrastructure/audit-log-factory.d.ts +59 -0
- package/dist/hsm/infrastructure/audit-log-factory.d.ts.map +1 -0
- package/dist/hsm/infrastructure/audit-log-factory.js +95 -0
- package/dist/hsm/infrastructure/audit-log.d.ts +8 -0
- package/dist/hsm/infrastructure/audit-log.d.ts.map +1 -0
- package/dist/hsm/infrastructure/audit-log.js +18 -0
- package/dist/hsm/infrastructure/file-audit-log.d.ts +55 -0
- package/dist/hsm/infrastructure/file-audit-log.d.ts.map +1 -0
- package/dist/hsm/infrastructure/file-audit-log.js +128 -0
- package/dist/hsm/infrastructure/key-store.d.ts +9 -0
- package/dist/hsm/infrastructure/key-store.d.ts.map +1 -0
- package/dist/hsm/infrastructure/key-store.js +12 -0
- package/dist/hsm/infrastructure/syslog-audit-log.d.ts +64 -0
- package/dist/hsm/infrastructure/syslog-audit-log.d.ts.map +1 -0
- package/dist/hsm/infrastructure/syslog-audit-log.js +167 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/integrations/besu-client/error-mapper.d.ts +9 -0
- package/dist/integrations/besu-client/error-mapper.d.ts.map +1 -0
- package/dist/integrations/besu-client/error-mapper.js +22 -0
- package/dist/integrations/besu-client/index.d.ts +65 -0
- package/dist/integrations/besu-client/index.d.ts.map +1 -0
- package/dist/integrations/besu-client/index.js +276 -0
- package/dist/integrations/besu-client/ports.d.ts +44 -0
- package/dist/integrations/besu-client/ports.d.ts.map +1 -0
- package/dist/integrations/besu-client/ports.js +1 -0
- package/dist/integrations/corda-gateway/index.d.ts +37 -0
- package/dist/integrations/corda-gateway/index.d.ts.map +1 -0
- package/dist/integrations/corda-gateway/index.js +234 -0
- package/dist/integrations/corda-gateway/ports.d.ts +33 -0
- package/dist/integrations/corda-gateway/ports.d.ts.map +1 -0
- package/dist/integrations/corda-gateway/ports.js +1 -0
- package/dist/integrations/fabric-gateway/index.d.ts +78 -0
- package/dist/integrations/fabric-gateway/index.d.ts.map +1 -0
- package/dist/integrations/fabric-gateway/index.js +214 -0
- package/dist/integrations/fabric-gateway/ports.d.ts +50 -0
- package/dist/integrations/fabric-gateway/ports.d.ts.map +1 -0
- package/dist/integrations/fabric-gateway/ports.js +1 -0
- package/dist/integrations/index.d.ts +19 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +19 -0
- package/dist/integrations/shared/env.d.ts +4 -0
- package/dist/integrations/shared/env.d.ts.map +1 -0
- package/dist/integrations/shared/env.js +24 -0
- package/dist/integrations/shared/retry.d.ts +79 -0
- package/dist/integrations/shared/retry.d.ts.map +1 -0
- package/dist/integrations/shared/retry.js +315 -0
- package/dist/mpc/adapters.d.ts +36 -0
- package/dist/mpc/adapters.d.ts.map +1 -0
- package/dist/mpc/adapters.js +46 -0
- package/dist/mpc/crypto.d.ts +2 -0
- package/dist/mpc/crypto.d.ts.map +1 -0
- package/dist/mpc/crypto.js +2 -0
- package/dist/mpc/dsa.d.ts +134 -0
- package/dist/mpc/dsa.d.ts.map +1 -0
- package/dist/mpc/dsa.js +127 -0
- package/dist/mpc/field.d.ts +127 -0
- package/dist/mpc/field.d.ts.map +1 -0
- package/dist/mpc/field.js +209 -0
- package/dist/mpc/hybrid-kem.d.ts +96 -0
- package/dist/mpc/hybrid-kem.d.ts.map +1 -0
- package/dist/mpc/hybrid-kem.js +136 -0
- package/dist/mpc/index.d.ts +135 -0
- package/dist/mpc/index.d.ts.map +1 -0
- package/dist/mpc/index.js +348 -0
- package/dist/mpc/kyber.d.ts +134 -0
- package/dist/mpc/kyber.d.ts.map +1 -0
- package/dist/mpc/kyber.js +143 -0
- package/dist/mpc/ports.d.ts +67 -0
- package/dist/mpc/ports.d.ts.map +1 -0
- package/dist/mpc/ports.js +9 -0
- package/dist/mpc/quantum.d.ts +80 -0
- package/dist/mpc/quantum.d.ts.map +1 -0
- package/dist/mpc/quantum.js +180 -0
- package/dist/p2mr/adapters.d.ts +31 -0
- package/dist/p2mr/adapters.d.ts.map +1 -0
- package/dist/p2mr/adapters.js +35 -0
- package/dist/p2mr/index.d.ts +63 -0
- package/dist/p2mr/index.d.ts.map +1 -0
- package/dist/p2mr/index.js +59 -0
- package/dist/p2mr/merkle-tree.d.ts +109 -0
- package/dist/p2mr/merkle-tree.d.ts.map +1 -0
- package/dist/p2mr/merkle-tree.js +239 -0
- package/dist/p2mr/p2mr-output.d.ts +142 -0
- package/dist/p2mr/p2mr-output.d.ts.map +1 -0
- package/dist/p2mr/p2mr-output.js +150 -0
- package/dist/p2mr/ports.d.ts +52 -0
- package/dist/p2mr/ports.d.ts.map +1 -0
- package/dist/p2mr/ports.js +9 -0
- package/dist/p2mr/script-interpreter.d.ts +92 -0
- package/dist/p2mr/script-interpreter.d.ts.map +1 -0
- package/dist/p2mr/script-interpreter.js +535 -0
- package/dist/p2mr/script-leaf.d.ts +70 -0
- package/dist/p2mr/script-leaf.d.ts.map +1 -0
- package/dist/p2mr/script-leaf.js +203 -0
- package/dist/p2mr/spend-proof.d.ts +95 -0
- package/dist/p2mr/spend-proof.d.ts.map +1 -0
- package/dist/p2mr/spend-proof.js +358 -0
- package/dist/p2mr/types.d.ts +209 -0
- package/dist/p2mr/types.d.ts.map +1 -0
- package/dist/p2mr/types.js +9 -0
- package/dist/privacy/application/view-projector.d.ts +13 -0
- package/dist/privacy/application/view-projector.d.ts.map +1 -0
- package/dist/privacy/application/view-projector.js +85 -0
- package/dist/privacy/domain/entities.d.ts +26 -0
- package/dist/privacy/domain/entities.d.ts.map +1 -0
- package/dist/privacy/domain/entities.js +1 -0
- package/dist/privacy/domain/ports.d.ts +7 -0
- package/dist/privacy/domain/ports.d.ts.map +1 -0
- package/dist/privacy/domain/ports.js +1 -0
- package/dist/privacy/index.d.ts +21 -0
- package/dist/privacy/index.d.ts.map +1 -0
- package/dist/privacy/index.js +25 -0
- package/dist/privacy/infrastructure/in-memory-store.d.ts +8 -0
- package/dist/privacy/infrastructure/in-memory-store.d.ts.map +1 -0
- package/dist/privacy/infrastructure/in-memory-store.js +7 -0
- package/dist/protocols/besu-port.d.ts +80 -0
- package/dist/protocols/besu-port.d.ts.map +1 -0
- package/dist/protocols/besu-port.js +1 -0
- package/dist/protocols/corda-port.d.ts +103 -0
- package/dist/protocols/corda-port.d.ts.map +1 -0
- package/dist/protocols/corda-port.js +9 -0
- package/dist/protocols/credentialing-port.d.ts +11 -0
- package/dist/protocols/credentialing-port.d.ts.map +1 -0
- package/dist/protocols/credentialing-port.js +1 -0
- package/dist/protocols/fabric-port.d.ts +89 -0
- package/dist/protocols/fabric-port.d.ts.map +1 -0
- package/dist/protocols/fabric-port.js +9 -0
- package/dist/protocols/index.d.ts +14 -0
- package/dist/protocols/index.d.ts.map +1 -0
- package/dist/protocols/index.js +7 -0
- package/dist/protocols/p2mr-port.d.ts +159 -0
- package/dist/protocols/p2mr-port.d.ts.map +1 -0
- package/dist/protocols/p2mr-port.js +12 -0
- package/dist/protocols/privacy-port.d.ts +9 -0
- package/dist/protocols/privacy-port.d.ts.map +1 -0
- package/dist/protocols/privacy-port.js +1 -0
- package/dist/protocols/traceability-port.d.ts +12 -0
- package/dist/protocols/traceability-port.d.ts.map +1 -0
- package/dist/protocols/traceability-port.js +1 -0
- package/dist/shared/collection-store.d.ts +12 -0
- package/dist/shared/collection-store.d.ts.map +1 -0
- package/dist/shared/collection-store.js +26 -0
- package/dist/shared/commit.d.ts +24 -0
- package/dist/shared/commit.d.ts.map +1 -0
- package/dist/shared/commit.js +50 -0
- package/dist/shared/crypto.d.ts +2 -0
- package/dist/shared/crypto.d.ts.map +1 -0
- package/dist/shared/crypto.js +4 -0
- package/dist/shared/date.d.ts +2 -0
- package/dist/shared/date.d.ts.map +1 -0
- package/dist/shared/date.js +3 -0
- package/dist/shared/index.d.ts +9 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +11 -0
- package/dist/shared/logger.d.ts +37 -0
- package/dist/shared/logger.d.ts.map +1 -0
- package/dist/shared/logger.js +45 -0
- package/dist/shared/store.d.ts +25 -0
- package/dist/shared/store.d.ts.map +1 -0
- package/dist/shared/store.js +18 -0
- package/dist/shared/telemetry-sdk.d.ts +26 -0
- package/dist/shared/telemetry-sdk.d.ts.map +1 -0
- package/dist/shared/telemetry-sdk.js +97 -0
- package/dist/shared/telemetry.d.ts +86 -0
- package/dist/shared/telemetry.d.ts.map +1 -0
- package/dist/shared/telemetry.js +137 -0
- package/dist/stark-settlement/application/aggregator-service.d.ts +112 -0
- package/dist/stark-settlement/application/aggregator-service.d.ts.map +1 -0
- package/dist/stark-settlement/application/aggregator-service.js +256 -0
- package/dist/stark-settlement/application/ledger-service.d.ts +114 -0
- package/dist/stark-settlement/application/ledger-service.d.ts.map +1 -0
- package/dist/stark-settlement/application/ledger-service.js +318 -0
- package/dist/stark-settlement/application/settlement-service.d.ts +104 -0
- package/dist/stark-settlement/application/settlement-service.d.ts.map +1 -0
- package/dist/stark-settlement/application/settlement-service.js +251 -0
- package/dist/stark-settlement/domain/entities.d.ts +365 -0
- package/dist/stark-settlement/domain/entities.d.ts.map +1 -0
- package/dist/stark-settlement/domain/entities.js +29 -0
- package/dist/stark-settlement/domain/ports.d.ts +485 -0
- package/dist/stark-settlement/domain/ports.d.ts.map +1 -0
- package/dist/stark-settlement/domain/ports.js +14 -0
- package/dist/stark-settlement/domain/value-objects.d.ts +268 -0
- package/dist/stark-settlement/domain/value-objects.d.ts.map +1 -0
- package/dist/stark-settlement/domain/value-objects.js +500 -0
- package/dist/stark-settlement/index.d.ts +172 -0
- package/dist/stark-settlement/index.d.ts.map +1 -0
- package/dist/stark-settlement/index.js +193 -0
- package/dist/stark-settlement/infrastructure/adapters/audit-adapter.d.ts +52 -0
- package/dist/stark-settlement/infrastructure/adapters/audit-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/audit-adapter.js +154 -0
- package/dist/stark-settlement/infrastructure/adapters/bitcoin-adapter.d.ts +88 -0
- package/dist/stark-settlement/infrastructure/adapters/bitcoin-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/bitcoin-adapter.js +187 -0
- package/dist/stark-settlement/infrastructure/adapters/clock-adapter.d.ts +59 -0
- package/dist/stark-settlement/infrastructure/adapters/clock-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/clock-adapter.js +85 -0
- package/dist/stark-settlement/infrastructure/adapters/dilithium-adapter.d.ts +60 -0
- package/dist/stark-settlement/infrastructure/adapters/dilithium-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/dilithium-adapter.js +104 -0
- package/dist/stark-settlement/infrastructure/adapters/event-emitter-adapter.d.ts +115 -0
- package/dist/stark-settlement/infrastructure/adapters/event-emitter-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/event-emitter-adapter.js +191 -0
- package/dist/stark-settlement/infrastructure/adapters/fiat-adapter.d.ts +65 -0
- package/dist/stark-settlement/infrastructure/adapters/fiat-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/fiat-adapter.js +207 -0
- package/dist/stark-settlement/infrastructure/adapters/mock-stark-adapter.d.ts +73 -0
- package/dist/stark-settlement/infrastructure/adapters/mock-stark-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/mock-stark-adapter.js +287 -0
- package/dist/stark-settlement/infrastructure/adapters/solana-adapter.d.ts +78 -0
- package/dist/stark-settlement/infrastructure/adapters/solana-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/solana-adapter.js +172 -0
- package/dist/stark-settlement/infrastructure/adapters/starknet-proof-adapter.d.ts +56 -0
- package/dist/stark-settlement/infrastructure/adapters/starknet-proof-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/starknet-proof-adapter.js +261 -0
- package/dist/stark-settlement/infrastructure/adapters/stone-proof-adapter.d.ts +125 -0
- package/dist/stark-settlement/infrastructure/adapters/stone-proof-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/stone-proof-adapter.js +416 -0
- package/dist/stark-settlement/infrastructure/persistence/ledger-store.d.ts +68 -0
- package/dist/stark-settlement/infrastructure/persistence/ledger-store.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/persistence/ledger-store.js +238 -0
- package/dist/stark-settlement/infrastructure/persistence/offset-store.d.ts +30 -0
- package/dist/stark-settlement/infrastructure/persistence/offset-store.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/persistence/offset-store.js +57 -0
- package/dist/stark-settlement/infrastructure/persistence/outbox-store.d.ts +45 -0
- package/dist/stark-settlement/infrastructure/persistence/outbox-store.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/persistence/outbox-store.js +171 -0
- package/dist/traceability/application/recall-assessor.d.ts +13 -0
- package/dist/traceability/application/recall-assessor.d.ts.map +1 -0
- package/dist/traceability/application/recall-assessor.js +74 -0
- package/dist/traceability/domain/entities.d.ts +23 -0
- package/dist/traceability/domain/entities.d.ts.map +1 -0
- package/dist/traceability/domain/entities.js +1 -0
- package/dist/traceability/domain/ports.d.ts +23 -0
- package/dist/traceability/domain/ports.d.ts.map +1 -0
- package/dist/traceability/domain/ports.js +1 -0
- package/dist/traceability/domain/recall.d.ts +12 -0
- package/dist/traceability/domain/recall.d.ts.map +1 -0
- package/dist/traceability/domain/recall.js +1 -0
- package/dist/traceability/index.d.ts +22 -0
- package/dist/traceability/index.d.ts.map +1 -0
- package/dist/traceability/index.js +26 -0
- package/dist/traceability/infrastructure/in-memory-store.d.ts +13 -0
- package/dist/traceability/infrastructure/in-memory-store.d.ts.map +1 -0
- package/dist/traceability/infrastructure/in-memory-store.js +24 -0
- package/package.json +12 -9
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Script Leaf Creation and Validation
|
|
3
|
+
*
|
|
4
|
+
* Provides factory functions and validation for P2MR script leaves.
|
|
5
|
+
* Each leaf type has specific requirements for its parameters.
|
|
6
|
+
*/
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Validation
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
/**
|
|
11
|
+
* Validate a public key hash format.
|
|
12
|
+
*
|
|
13
|
+
* @param hash - Hash to validate.
|
|
14
|
+
* @returns true if the hash is 64 hex characters.
|
|
15
|
+
*/
|
|
16
|
+
function isValidPublicKeyHash(hash) {
|
|
17
|
+
return /^[a-f0-9]{64}$/i.test(hash);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Validate a script leaf for correctness.
|
|
21
|
+
*
|
|
22
|
+
* Checks:
|
|
23
|
+
* - Type is a valid ScriptLeafType
|
|
24
|
+
* - publicKeyHashes is non-empty and contains valid hashes
|
|
25
|
+
* - Condition-specific parameters are valid
|
|
26
|
+
*
|
|
27
|
+
* @param leaf - Script leaf to validate.
|
|
28
|
+
* @returns Validation result with error message if invalid.
|
|
29
|
+
*/
|
|
30
|
+
export function validateScriptLeaf(leaf) {
|
|
31
|
+
// Validate type
|
|
32
|
+
const validTypes = [
|
|
33
|
+
"ml-dsa-65-sig",
|
|
34
|
+
"timelock",
|
|
35
|
+
"multisig-ml-dsa",
|
|
36
|
+
"hsm-attested-sig",
|
|
37
|
+
];
|
|
38
|
+
if (!validTypes.includes(leaf.type)) {
|
|
39
|
+
return { valid: false, error: `Invalid script leaf type: ${leaf.type}` };
|
|
40
|
+
}
|
|
41
|
+
// Validate publicKeyHashes
|
|
42
|
+
if (!Array.isArray(leaf.publicKeyHashes) ||
|
|
43
|
+
leaf.publicKeyHashes.length === 0) {
|
|
44
|
+
return { valid: false, error: "publicKeyHashes must be a non-empty array" };
|
|
45
|
+
}
|
|
46
|
+
for (let i = 0; i < leaf.publicKeyHashes.length; i++) {
|
|
47
|
+
if (!isValidPublicKeyHash(leaf.publicKeyHashes[i])) {
|
|
48
|
+
return {
|
|
49
|
+
valid: false,
|
|
50
|
+
error: `Invalid public key hash at index ${i}: must be 64 hex characters`,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Type-specific validation
|
|
55
|
+
switch (leaf.type) {
|
|
56
|
+
case "ml-dsa-65-sig":
|
|
57
|
+
// Single signature requires exactly one public key hash
|
|
58
|
+
if (leaf.publicKeyHashes.length !== 1) {
|
|
59
|
+
return {
|
|
60
|
+
valid: false,
|
|
61
|
+
error: "ml-dsa-65-sig requires exactly one public key hash",
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
break;
|
|
65
|
+
case "timelock":
|
|
66
|
+
// Timelock requires exactly one public key hash and a locktime
|
|
67
|
+
if (leaf.publicKeyHashes.length !== 1) {
|
|
68
|
+
return {
|
|
69
|
+
valid: false,
|
|
70
|
+
error: "timelock requires exactly one public key hash",
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
if (typeof leaf.locktime !== "number" || leaf.locktime < 0) {
|
|
74
|
+
return {
|
|
75
|
+
valid: false,
|
|
76
|
+
error: "timelock requires a non-negative locktime",
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
break;
|
|
80
|
+
case "multisig-ml-dsa": {
|
|
81
|
+
// Multisig requires at least 2 public key hashes and valid threshold
|
|
82
|
+
if (leaf.publicKeyHashes.length < 2) {
|
|
83
|
+
return {
|
|
84
|
+
valid: false,
|
|
85
|
+
error: "multisig-ml-dsa requires at least 2 public key hashes",
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const threshold = leaf.threshold ?? leaf.publicKeyHashes.length;
|
|
89
|
+
if (threshold < 1 || threshold > leaf.publicKeyHashes.length) {
|
|
90
|
+
return {
|
|
91
|
+
valid: false,
|
|
92
|
+
error: `threshold must be between 1 and ${leaf.publicKeyHashes.length}`,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
case "hsm-attested-sig":
|
|
98
|
+
// HSM-attested requires exactly one public key hash and an HSM slot ID
|
|
99
|
+
if (leaf.publicKeyHashes.length !== 1) {
|
|
100
|
+
return {
|
|
101
|
+
valid: false,
|
|
102
|
+
error: "hsm-attested-sig requires exactly one public key hash",
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if (typeof leaf.hsmSlotId !== "string" || leaf.hsmSlotId.length === 0) {
|
|
106
|
+
return {
|
|
107
|
+
valid: false,
|
|
108
|
+
error: "hsm-attested-sig requires a non-empty hsmSlotId",
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
return { valid: true };
|
|
114
|
+
}
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
// Factory Functions
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
/**
|
|
119
|
+
* Create a simple ML-DSA-65 signature leaf.
|
|
120
|
+
*
|
|
121
|
+
* Requires a single valid signature from the specified public key.
|
|
122
|
+
*
|
|
123
|
+
* @param publicKeyHash - SHA-256 hash of the authorized ML-DSA-65 public key.
|
|
124
|
+
* @returns A validated ScriptLeaf.
|
|
125
|
+
* @throws Error if the hash is invalid.
|
|
126
|
+
*/
|
|
127
|
+
export function createSingleSigLeaf(publicKeyHash) {
|
|
128
|
+
const leaf = {
|
|
129
|
+
type: "ml-dsa-65-sig",
|
|
130
|
+
publicKeyHashes: [publicKeyHash],
|
|
131
|
+
};
|
|
132
|
+
const result = validateScriptLeaf(leaf);
|
|
133
|
+
if (!result.valid) {
|
|
134
|
+
throw new Error(result.error);
|
|
135
|
+
}
|
|
136
|
+
return leaf;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Create a time-locked signature leaf.
|
|
140
|
+
*
|
|
141
|
+
* Requires a valid signature from the specified public key, but only after
|
|
142
|
+
* the locktime has passed.
|
|
143
|
+
*
|
|
144
|
+
* @param publicKeyHash - SHA-256 hash of the authorized ML-DSA-65 public key.
|
|
145
|
+
* @param locktime - Unix timestamp (ms) after which spending is allowed.
|
|
146
|
+
* @returns A validated ScriptLeaf.
|
|
147
|
+
* @throws Error if parameters are invalid.
|
|
148
|
+
*/
|
|
149
|
+
export function createTimelockLeaf(publicKeyHash, locktime) {
|
|
150
|
+
const leaf = {
|
|
151
|
+
type: "timelock",
|
|
152
|
+
publicKeyHashes: [publicKeyHash],
|
|
153
|
+
locktime,
|
|
154
|
+
};
|
|
155
|
+
const result = validateScriptLeaf(leaf);
|
|
156
|
+
if (!result.valid) {
|
|
157
|
+
throw new Error(result.error);
|
|
158
|
+
}
|
|
159
|
+
return leaf;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Create a multisig leaf requiring k-of-n signatures.
|
|
163
|
+
*
|
|
164
|
+
* @param publicKeyHashes - SHA-256 hashes of all authorized ML-DSA-65 public keys.
|
|
165
|
+
* @param threshold - Minimum number of valid signatures required.
|
|
166
|
+
* @returns A validated ScriptLeaf.
|
|
167
|
+
* @throws Error if parameters are invalid.
|
|
168
|
+
*/
|
|
169
|
+
export function createMultisigLeaf(publicKeyHashes, threshold) {
|
|
170
|
+
const leaf = {
|
|
171
|
+
type: "multisig-ml-dsa",
|
|
172
|
+
publicKeyHashes: [...publicKeyHashes],
|
|
173
|
+
threshold,
|
|
174
|
+
};
|
|
175
|
+
const result = validateScriptLeaf(leaf);
|
|
176
|
+
if (!result.valid) {
|
|
177
|
+
throw new Error(result.error);
|
|
178
|
+
}
|
|
179
|
+
return leaf;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Create an HSM-attested signature leaf.
|
|
183
|
+
*
|
|
184
|
+
* Requires a valid signature from the specified public key, plus attestation
|
|
185
|
+
* proof that the signature was produced by the specified HSM slot.
|
|
186
|
+
*
|
|
187
|
+
* @param publicKeyHash - SHA-256 hash of the authorized ML-DSA-65 public key.
|
|
188
|
+
* @param hsmSlotId - Identifier of the required HSM slot.
|
|
189
|
+
* @returns A validated ScriptLeaf.
|
|
190
|
+
* @throws Error if parameters are invalid.
|
|
191
|
+
*/
|
|
192
|
+
export function createHsmAttestedLeaf(publicKeyHash, hsmSlotId) {
|
|
193
|
+
const leaf = {
|
|
194
|
+
type: "hsm-attested-sig",
|
|
195
|
+
publicKeyHashes: [publicKeyHash],
|
|
196
|
+
hsmSlotId,
|
|
197
|
+
};
|
|
198
|
+
const result = validateScriptLeaf(leaf);
|
|
199
|
+
if (!result.valid) {
|
|
200
|
+
throw new Error(result.error);
|
|
201
|
+
}
|
|
202
|
+
return leaf;
|
|
203
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spend Proof Construction and Validation
|
|
3
|
+
*
|
|
4
|
+
* Provides functions for building and validating spend proofs.
|
|
5
|
+
* A spend proof demonstrates that:
|
|
6
|
+
* 1. The revealed leaf hashes to the output's Merkle root
|
|
7
|
+
* 2. The witness satisfies the leaf's spending condition
|
|
8
|
+
*/
|
|
9
|
+
import type { P2MROutput, SpendProof, SpendWitness, SpendVerificationResult } from "./types.js";
|
|
10
|
+
import { MerkleTree } from "./merkle-tree.js";
|
|
11
|
+
/**
|
|
12
|
+
* Options for building a spend proof.
|
|
13
|
+
*/
|
|
14
|
+
export interface BuildSpendProofOptions {
|
|
15
|
+
/**
|
|
16
|
+
* ID of the output being spent.
|
|
17
|
+
*/
|
|
18
|
+
outputId: string;
|
|
19
|
+
/**
|
|
20
|
+
* The Merkle tree containing the spending conditions.
|
|
21
|
+
* This must be the same tree used to create the output.
|
|
22
|
+
*/
|
|
23
|
+
tree: MerkleTree;
|
|
24
|
+
/**
|
|
25
|
+
* Index of the leaf (spending condition) to use.
|
|
26
|
+
*/
|
|
27
|
+
leafIndex: number;
|
|
28
|
+
/**
|
|
29
|
+
* Witness data satisfying the spending condition.
|
|
30
|
+
*/
|
|
31
|
+
witness: SpendWitness;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Build a spend proof for a P2MR output.
|
|
35
|
+
*
|
|
36
|
+
* The proof consists of:
|
|
37
|
+
* 1. The output ID being spent
|
|
38
|
+
* 2. The revealed script leaf (spending condition)
|
|
39
|
+
* 3. A Merkle proof from the leaf to the root
|
|
40
|
+
* 4. Witness data (public keys, signatures, etc.)
|
|
41
|
+
*
|
|
42
|
+
* @param options - Spend proof options.
|
|
43
|
+
* @returns A complete spend proof.
|
|
44
|
+
* @throws Error if leaf index is out of bounds.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* const proof = buildSpendProof({
|
|
49
|
+
* outputId: output.outputId,
|
|
50
|
+
* tree: merkleTree,
|
|
51
|
+
* leafIndex: 0,
|
|
52
|
+
* witness: {
|
|
53
|
+
* publicKeys: [myPublicKey],
|
|
54
|
+
* signatures: [mySignature],
|
|
55
|
+
* },
|
|
56
|
+
* });
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export declare function buildSpendProof(options: BuildSpendProofOptions): SpendProof;
|
|
60
|
+
/**
|
|
61
|
+
* Validate the structural correctness of a spend proof.
|
|
62
|
+
*
|
|
63
|
+
* This performs preliminary checks that don't require cryptographic
|
|
64
|
+
* signature verification. Full validation requires the script interpreter.
|
|
65
|
+
*
|
|
66
|
+
* Checks:
|
|
67
|
+
* 1. Output ID is present
|
|
68
|
+
* 2. Revealed leaf passes structural validation
|
|
69
|
+
* 3. Merkle proof is structurally valid
|
|
70
|
+
* 4. Witness has required fields for the leaf type
|
|
71
|
+
*
|
|
72
|
+
* @param proof - The spend proof to validate.
|
|
73
|
+
* @returns Validation result with error if invalid.
|
|
74
|
+
*/
|
|
75
|
+
export declare function validateSpendProofStructure(proof: SpendProof): SpendVerificationResult;
|
|
76
|
+
/**
|
|
77
|
+
* Verify that a spend proof's Merkle path leads to the expected root.
|
|
78
|
+
*
|
|
79
|
+
* @param proof - The spend proof.
|
|
80
|
+
* @param expectedRoot - The Merkle root from the P2MR output.
|
|
81
|
+
* @returns Verification result.
|
|
82
|
+
*/
|
|
83
|
+
export declare function verifyMerkleProof(proof: SpendProof, expectedRoot: string): SpendVerificationResult;
|
|
84
|
+
/**
|
|
85
|
+
* Perform full structural and Merkle verification of a spend proof.
|
|
86
|
+
*
|
|
87
|
+
* This does NOT verify signatures - that requires the script interpreter
|
|
88
|
+
* which is implemented separately.
|
|
89
|
+
*
|
|
90
|
+
* @param proof - The spend proof.
|
|
91
|
+
* @param output - The P2MR output being spent.
|
|
92
|
+
* @returns Verification result.
|
|
93
|
+
*/
|
|
94
|
+
export declare function verifySpendProofStructure(proof: SpendProof, output: P2MROutput): SpendVerificationResult;
|
|
95
|
+
//# sourceMappingURL=spend-proof.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spend-proof.d.ts","sourceRoot":"","sources":["../../src/p2mr/spend-proof.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,UAAU,EAEV,UAAU,EACV,YAAY,EAEZ,uBAAuB,EAExB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAkB,MAAM,kBAAkB,CAAC;AAO9D;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,IAAI,EAAE,UAAU,CAAC;IAEjB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,OAAO,EAAE,YAAY,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,UAAU,CAmB3E;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,UAAU,GAChB,uBAAuB,CA0GzB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,UAAU,EACjB,YAAY,EAAE,MAAM,GACnB,uBAAuB,CA0CzB;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,UAAU,GACjB,uBAAuB,CAiDzB"}
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spend Proof Construction and Validation
|
|
3
|
+
*
|
|
4
|
+
* Provides functions for building and validating spend proofs.
|
|
5
|
+
* A spend proof demonstrates that:
|
|
6
|
+
* 1. The revealed leaf hashes to the output's Merkle root
|
|
7
|
+
* 2. The witness satisfies the leaf's spending condition
|
|
8
|
+
*/
|
|
9
|
+
import { MerkleTree, hashScriptLeaf } from "./merkle-tree.js";
|
|
10
|
+
import { validateScriptLeaf } from "./script-leaf.js";
|
|
11
|
+
/**
|
|
12
|
+
* Build a spend proof for a P2MR output.
|
|
13
|
+
*
|
|
14
|
+
* The proof consists of:
|
|
15
|
+
* 1. The output ID being spent
|
|
16
|
+
* 2. The revealed script leaf (spending condition)
|
|
17
|
+
* 3. A Merkle proof from the leaf to the root
|
|
18
|
+
* 4. Witness data (public keys, signatures, etc.)
|
|
19
|
+
*
|
|
20
|
+
* @param options - Spend proof options.
|
|
21
|
+
* @returns A complete spend proof.
|
|
22
|
+
* @throws Error if leaf index is out of bounds.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const proof = buildSpendProof({
|
|
27
|
+
* outputId: output.outputId,
|
|
28
|
+
* tree: merkleTree,
|
|
29
|
+
* leafIndex: 0,
|
|
30
|
+
* witness: {
|
|
31
|
+
* publicKeys: [myPublicKey],
|
|
32
|
+
* signatures: [mySignature],
|
|
33
|
+
* },
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export function buildSpendProof(options) {
|
|
38
|
+
const { outputId, tree, leafIndex, witness } = options;
|
|
39
|
+
const leaves = tree.leaves;
|
|
40
|
+
if (leafIndex < 0 || leafIndex >= leaves.length) {
|
|
41
|
+
throw new Error(`Leaf index ${leafIndex} out of bounds [0, ${leaves.length - 1}]`);
|
|
42
|
+
}
|
|
43
|
+
const revealedLeaf = leaves[leafIndex];
|
|
44
|
+
const merkleProof = tree.getProof(leafIndex);
|
|
45
|
+
return {
|
|
46
|
+
outputId,
|
|
47
|
+
revealedLeaf,
|
|
48
|
+
merkleProof,
|
|
49
|
+
witness,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Spend Proof Validation (Structural)
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
/**
|
|
56
|
+
* Validate the structural correctness of a spend proof.
|
|
57
|
+
*
|
|
58
|
+
* This performs preliminary checks that don't require cryptographic
|
|
59
|
+
* signature verification. Full validation requires the script interpreter.
|
|
60
|
+
*
|
|
61
|
+
* Checks:
|
|
62
|
+
* 1. Output ID is present
|
|
63
|
+
* 2. Revealed leaf passes structural validation
|
|
64
|
+
* 3. Merkle proof is structurally valid
|
|
65
|
+
* 4. Witness has required fields for the leaf type
|
|
66
|
+
*
|
|
67
|
+
* @param proof - The spend proof to validate.
|
|
68
|
+
* @returns Validation result with error if invalid.
|
|
69
|
+
*/
|
|
70
|
+
export function validateSpendProofStructure(proof) {
|
|
71
|
+
const auditTrail = [];
|
|
72
|
+
// Step 1: Validate output ID
|
|
73
|
+
if (!proof.outputId || proof.outputId.length === 0) {
|
|
74
|
+
auditTrail.push({
|
|
75
|
+
step: "Output ID check",
|
|
76
|
+
passed: false,
|
|
77
|
+
detail: "Output ID is required",
|
|
78
|
+
});
|
|
79
|
+
return {
|
|
80
|
+
valid: false,
|
|
81
|
+
reason: "Missing output ID",
|
|
82
|
+
auditTrail,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
auditTrail.push({
|
|
86
|
+
step: "Output ID check",
|
|
87
|
+
passed: true,
|
|
88
|
+
detail: `Output ID: ${proof.outputId}`,
|
|
89
|
+
});
|
|
90
|
+
// Step 2: Validate revealed leaf structure
|
|
91
|
+
const leafResult = validateScriptLeaf(proof.revealedLeaf);
|
|
92
|
+
if (!leafResult.valid) {
|
|
93
|
+
const errorMsg = leafResult.error ?? "Unknown validation error";
|
|
94
|
+
auditTrail.push({
|
|
95
|
+
step: "Leaf structure check",
|
|
96
|
+
passed: false,
|
|
97
|
+
detail: errorMsg,
|
|
98
|
+
});
|
|
99
|
+
return {
|
|
100
|
+
valid: false,
|
|
101
|
+
reason: `Invalid leaf: ${errorMsg}`,
|
|
102
|
+
auditTrail,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
auditTrail.push({
|
|
106
|
+
step: "Leaf structure check",
|
|
107
|
+
passed: true,
|
|
108
|
+
detail: `Type: ${proof.revealedLeaf.type}`,
|
|
109
|
+
});
|
|
110
|
+
// Step 3: Validate Merkle proof structure
|
|
111
|
+
if (!Array.isArray(proof.merkleProof)) {
|
|
112
|
+
auditTrail.push({
|
|
113
|
+
step: "Merkle proof structure",
|
|
114
|
+
passed: false,
|
|
115
|
+
detail: "Merkle proof must be an array",
|
|
116
|
+
});
|
|
117
|
+
return {
|
|
118
|
+
valid: false,
|
|
119
|
+
reason: "Invalid Merkle proof structure",
|
|
120
|
+
auditTrail,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
for (let i = 0; i < proof.merkleProof.length; i++) {
|
|
124
|
+
const node = proof.merkleProof[i];
|
|
125
|
+
if (!isValidMerkleProofNode(node)) {
|
|
126
|
+
auditTrail.push({
|
|
127
|
+
step: `Merkle proof node ${i}`,
|
|
128
|
+
passed: false,
|
|
129
|
+
detail: "Invalid node structure",
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
valid: false,
|
|
133
|
+
reason: `Invalid Merkle proof node at index ${i}`,
|
|
134
|
+
auditTrail,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
auditTrail.push({
|
|
139
|
+
step: "Merkle proof structure",
|
|
140
|
+
passed: true,
|
|
141
|
+
detail: `Proof length: ${proof.merkleProof.length}`,
|
|
142
|
+
});
|
|
143
|
+
// Step 4: Validate witness structure for leaf type
|
|
144
|
+
const witnessResult = validateWitnessStructure(proof.revealedLeaf, proof.witness);
|
|
145
|
+
if (!witnessResult.valid) {
|
|
146
|
+
auditTrail.push({
|
|
147
|
+
step: "Witness structure check",
|
|
148
|
+
passed: false,
|
|
149
|
+
detail: witnessResult.reason,
|
|
150
|
+
});
|
|
151
|
+
return {
|
|
152
|
+
valid: false,
|
|
153
|
+
reason: witnessResult.reason,
|
|
154
|
+
auditTrail,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
auditTrail.push({
|
|
158
|
+
step: "Witness structure check",
|
|
159
|
+
passed: true,
|
|
160
|
+
detail: `${proof.witness.publicKeys.length} keys, ${proof.witness.signatures.length} signatures`,
|
|
161
|
+
});
|
|
162
|
+
return {
|
|
163
|
+
valid: true,
|
|
164
|
+
reason: "Spend proof structure is valid",
|
|
165
|
+
auditTrail,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Verify that a spend proof's Merkle path leads to the expected root.
|
|
170
|
+
*
|
|
171
|
+
* @param proof - The spend proof.
|
|
172
|
+
* @param expectedRoot - The Merkle root from the P2MR output.
|
|
173
|
+
* @returns Verification result.
|
|
174
|
+
*/
|
|
175
|
+
export function verifyMerkleProof(proof, expectedRoot) {
|
|
176
|
+
const auditTrail = [];
|
|
177
|
+
// Compute leaf hash
|
|
178
|
+
const leafHash = hashScriptLeaf(proof.revealedLeaf);
|
|
179
|
+
auditTrail.push({
|
|
180
|
+
step: "Compute leaf hash",
|
|
181
|
+
passed: true,
|
|
182
|
+
detail: `Hash: ${leafHash.substring(0, 16)}...`,
|
|
183
|
+
});
|
|
184
|
+
// Verify Merkle path
|
|
185
|
+
const verified = MerkleTree.verify(proof.revealedLeaf, proof.merkleProof, expectedRoot);
|
|
186
|
+
if (!verified) {
|
|
187
|
+
auditTrail.push({
|
|
188
|
+
step: "Merkle path verification",
|
|
189
|
+
passed: false,
|
|
190
|
+
detail: `Expected root: ${expectedRoot.substring(0, 16)}...`,
|
|
191
|
+
});
|
|
192
|
+
return {
|
|
193
|
+
valid: false,
|
|
194
|
+
reason: "Merkle proof does not verify to expected root",
|
|
195
|
+
auditTrail,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
auditTrail.push({
|
|
199
|
+
step: "Merkle path verification",
|
|
200
|
+
passed: true,
|
|
201
|
+
detail: `Root: ${expectedRoot.substring(0, 16)}...`,
|
|
202
|
+
});
|
|
203
|
+
return {
|
|
204
|
+
valid: true,
|
|
205
|
+
reason: "Merkle proof verified successfully",
|
|
206
|
+
auditTrail,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Perform full structural and Merkle verification of a spend proof.
|
|
211
|
+
*
|
|
212
|
+
* This does NOT verify signatures - that requires the script interpreter
|
|
213
|
+
* which is implemented separately.
|
|
214
|
+
*
|
|
215
|
+
* @param proof - The spend proof.
|
|
216
|
+
* @param output - The P2MR output being spent.
|
|
217
|
+
* @returns Verification result.
|
|
218
|
+
*/
|
|
219
|
+
export function verifySpendProofStructure(proof, output) {
|
|
220
|
+
const auditTrail = [];
|
|
221
|
+
// Step 1: Verify output ID matches
|
|
222
|
+
if (proof.outputId !== output.outputId) {
|
|
223
|
+
auditTrail.push({
|
|
224
|
+
step: "Output ID match",
|
|
225
|
+
passed: false,
|
|
226
|
+
detail: `Proof: ${proof.outputId}, Output: ${output.outputId}`,
|
|
227
|
+
});
|
|
228
|
+
return {
|
|
229
|
+
valid: false,
|
|
230
|
+
reason: "Spend proof output ID does not match output",
|
|
231
|
+
auditTrail,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
auditTrail.push({
|
|
235
|
+
step: "Output ID match",
|
|
236
|
+
passed: true,
|
|
237
|
+
detail: output.outputId,
|
|
238
|
+
});
|
|
239
|
+
// Step 2: Validate proof structure
|
|
240
|
+
const structureResult = validateSpendProofStructure(proof);
|
|
241
|
+
auditTrail.push(...(structureResult.auditTrail ?? []));
|
|
242
|
+
if (!structureResult.valid) {
|
|
243
|
+
return {
|
|
244
|
+
valid: false,
|
|
245
|
+
reason: structureResult.reason,
|
|
246
|
+
auditTrail,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
// Step 3: Verify Merkle proof
|
|
250
|
+
const merkleResult = verifyMerkleProof(proof, output.merkleRoot);
|
|
251
|
+
auditTrail.push(...(merkleResult.auditTrail ?? []));
|
|
252
|
+
if (!merkleResult.valid) {
|
|
253
|
+
return {
|
|
254
|
+
valid: false,
|
|
255
|
+
reason: merkleResult.reason,
|
|
256
|
+
auditTrail,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
valid: true,
|
|
261
|
+
reason: "Spend proof structure and Merkle path verified",
|
|
262
|
+
auditTrail,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
// Helper Functions
|
|
267
|
+
// ---------------------------------------------------------------------------
|
|
268
|
+
/**
|
|
269
|
+
* Check if a value is a valid MerkleProofNode.
|
|
270
|
+
*/
|
|
271
|
+
function isValidMerkleProofNode(node) {
|
|
272
|
+
if (typeof node !== "object" || node === null) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
const n = node;
|
|
276
|
+
if (typeof n.hash !== "string" || !/^[a-f0-9]{64}$/i.test(n.hash)) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
if (n.position !== "left" && n.position !== "right") {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Validate witness structure for a given leaf type.
|
|
286
|
+
*/
|
|
287
|
+
function validateWitnessStructure(leaf, witness) {
|
|
288
|
+
// Check basic witness requirements
|
|
289
|
+
if (!Array.isArray(witness.publicKeys) || witness.publicKeys.length === 0) {
|
|
290
|
+
return {
|
|
291
|
+
valid: false,
|
|
292
|
+
reason: "Witness must include at least one public key",
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
if (!Array.isArray(witness.signatures) || witness.signatures.length === 0) {
|
|
296
|
+
return {
|
|
297
|
+
valid: false,
|
|
298
|
+
reason: "Witness must include at least one signature",
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
// Type-specific validation
|
|
302
|
+
switch (leaf.type) {
|
|
303
|
+
case "ml-dsa-65-sig":
|
|
304
|
+
if (witness.publicKeys.length !== 1 || witness.signatures.length !== 1) {
|
|
305
|
+
return {
|
|
306
|
+
valid: false,
|
|
307
|
+
reason: "ml-dsa-65-sig requires exactly 1 public key and 1 signature",
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
break;
|
|
311
|
+
case "timelock":
|
|
312
|
+
if (witness.publicKeys.length !== 1 || witness.signatures.length !== 1) {
|
|
313
|
+
return {
|
|
314
|
+
valid: false,
|
|
315
|
+
reason: "timelock requires exactly 1 public key and 1 signature",
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
if (typeof witness.timestamp !== "number") {
|
|
319
|
+
return {
|
|
320
|
+
valid: false,
|
|
321
|
+
reason: "timelock requires a timestamp in the witness",
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
break;
|
|
325
|
+
case "multisig-ml-dsa": {
|
|
326
|
+
const threshold = leaf.threshold ?? leaf.publicKeyHashes.length;
|
|
327
|
+
if (witness.signatures.length < threshold) {
|
|
328
|
+
return {
|
|
329
|
+
valid: false,
|
|
330
|
+
reason: `multisig-ml-dsa requires at least ${threshold} signatures`,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
if (witness.publicKeys.length < threshold) {
|
|
334
|
+
return {
|
|
335
|
+
valid: false,
|
|
336
|
+
reason: `multisig-ml-dsa requires at least ${threshold} public keys`,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
case "hsm-attested-sig":
|
|
342
|
+
if (witness.publicKeys.length !== 1 || witness.signatures.length !== 1) {
|
|
343
|
+
return {
|
|
344
|
+
valid: false,
|
|
345
|
+
reason: "hsm-attested-sig requires exactly 1 public key and 1 signature",
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
if (typeof witness.hsmAttestation !== "string" ||
|
|
349
|
+
witness.hsmAttestation.length === 0) {
|
|
350
|
+
return {
|
|
351
|
+
valid: false,
|
|
352
|
+
reason: "hsm-attested-sig requires HSM attestation in the witness",
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
return { valid: true, reason: "Witness structure valid" };
|
|
358
|
+
}
|