@fluxpointstudios/orynq-sdk-midnight-prover 0.1.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 +21 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +89 -0
- package/dist/index.js.map +1 -0
- package/dist/linking/cardano-anchor-link.d.ts +250 -0
- package/dist/linking/cardano-anchor-link.d.ts.map +1 -0
- package/dist/linking/cardano-anchor-link.js +447 -0
- package/dist/linking/cardano-anchor-link.js.map +1 -0
- package/dist/linking/index.d.ts +33 -0
- package/dist/linking/index.d.ts.map +1 -0
- package/dist/linking/index.js +31 -0
- package/dist/linking/index.js.map +1 -0
- package/dist/linking/proof-publication.d.ts +217 -0
- package/dist/linking/proof-publication.d.ts.map +1 -0
- package/dist/linking/proof-publication.js +385 -0
- package/dist/linking/proof-publication.js.map +1 -0
- package/dist/midnight/index.d.ts +30 -0
- package/dist/midnight/index.d.ts.map +1 -0
- package/dist/midnight/index.js +27 -0
- package/dist/midnight/index.js.map +1 -0
- package/dist/midnight/proof-server-client.d.ts +236 -0
- package/dist/midnight/proof-server-client.d.ts.map +1 -0
- package/dist/midnight/proof-server-client.js +422 -0
- package/dist/midnight/proof-server-client.js.map +1 -0
- package/dist/midnight/public-inputs.d.ts +134 -0
- package/dist/midnight/public-inputs.d.ts.map +1 -0
- package/dist/midnight/public-inputs.js +338 -0
- package/dist/midnight/public-inputs.js.map +1 -0
- package/dist/midnight/witness-builder.d.ts +119 -0
- package/dist/midnight/witness-builder.d.ts.map +1 -0
- package/dist/midnight/witness-builder.js +238 -0
- package/dist/midnight/witness-builder.js.map +1 -0
- package/dist/proofs/hash-chain-proof.d.ts +171 -0
- package/dist/proofs/hash-chain-proof.d.ts.map +1 -0
- package/dist/proofs/hash-chain-proof.js +437 -0
- package/dist/proofs/hash-chain-proof.js.map +1 -0
- package/dist/proofs/index.d.ts +35 -0
- package/dist/proofs/index.d.ts.map +1 -0
- package/dist/proofs/index.js +34 -0
- package/dist/proofs/index.js.map +1 -0
- package/dist/proofs/policy-compliance-proof.d.ts +165 -0
- package/dist/proofs/policy-compliance-proof.d.ts.map +1 -0
- package/dist/proofs/policy-compliance-proof.js +514 -0
- package/dist/proofs/policy-compliance-proof.js.map +1 -0
- package/dist/proofs/selective-disclosure.d.ts +213 -0
- package/dist/proofs/selective-disclosure.d.ts.map +1 -0
- package/dist/proofs/selective-disclosure.js +629 -0
- package/dist/proofs/selective-disclosure.js.map +1 -0
- package/dist/prover-interface.d.ts +288 -0
- package/dist/prover-interface.d.ts.map +1 -0
- package/dist/prover-interface.js +114 -0
- package/dist/prover-interface.js.map +1 -0
- package/dist/prover.d.ts +163 -0
- package/dist/prover.d.ts.map +1 -0
- package/dist/prover.js +417 -0
- package/dist/prover.js.map +1 -0
- package/dist/types.d.ts +410 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +128 -0
- package/dist/types.js.map +1 -0
- package/package.json +59 -0
- package/src/__tests__/hash-chain-proof.test.ts +709 -0
- package/src/__tests__/midnight-prover.test.ts +716 -0
- package/src/__tests__/policy-compliance.test.ts +567 -0
- package/src/__tests__/proof-publication.test.ts +644 -0
- package/src/__tests__/selective-disclosure.test.ts +921 -0
- package/src/index.ts +260 -0
- package/src/linking/cardano-anchor-link.ts +682 -0
- package/src/linking/index.ts +58 -0
- package/src/linking/proof-publication.ts +557 -0
- package/src/midnight/index.ts +73 -0
- package/src/midnight/proof-server-client.ts +595 -0
- package/src/midnight/public-inputs.ts +590 -0
- package/src/midnight/witness-builder.ts +341 -0
- package/src/proofs/hash-chain-proof.ts +610 -0
- package/src/proofs/index.ts +77 -0
- package/src/proofs/policy-compliance-proof.ts +717 -0
- package/src/proofs/selective-disclosure.ts +839 -0
- package/src/prover-interface.ts +410 -0
- package/src/prover.ts +537 -0
- package/src/types.ts +551 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Policy compliance proof generation and verification.
|
|
3
|
+
*
|
|
4
|
+
* Location: packages/midnight-prover/src/proofs/policy-compliance-proof.ts
|
|
5
|
+
*
|
|
6
|
+
* Summary:
|
|
7
|
+
* This module implements the PolicyComplianceProver class which generates ZK proofs
|
|
8
|
+
* demonstrating that content complies with a specified policy without revealing the
|
|
9
|
+
* actual content. The prover evaluates policy rules (blocklist, allowlist, regex,
|
|
10
|
+
* classifier) against content hashes and produces a proof of compliance.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* - Used by the MidnightProver to generate policy compliance proofs
|
|
14
|
+
* - Integrates with the types defined in ../types.ts
|
|
15
|
+
* - Binds proofs to Cardano anchor transactions for cross-chain verification
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { PolicyComplianceProver } from './policy-compliance-proof.js';
|
|
20
|
+
*
|
|
21
|
+
* const prover = new PolicyComplianceProver();
|
|
22
|
+
*
|
|
23
|
+
* const proof = await prover.generateProof({
|
|
24
|
+
* promptHash: 'abc123...',
|
|
25
|
+
* outputHash: 'def456...',
|
|
26
|
+
* policy: { id: 'policy-1', version: '1.0.0', rules: [...] },
|
|
27
|
+
* cardanoAnchorTxHash: 'txhash...',
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* const isValid = await prover.verifyProof(proof);
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
import type { PolicyInput, PolicyProof, ContentPolicy, PolicyRule } from "../types.js";
|
|
34
|
+
/**
|
|
35
|
+
* Result of evaluating a single policy rule.
|
|
36
|
+
*/
|
|
37
|
+
export interface RuleEvaluationResult {
|
|
38
|
+
rule: PolicyRule;
|
|
39
|
+
passed: boolean;
|
|
40
|
+
message: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Result of evaluating all policy rules.
|
|
44
|
+
*/
|
|
45
|
+
export interface PolicyEvaluationResult {
|
|
46
|
+
compliant: boolean;
|
|
47
|
+
ruleResults: RuleEvaluationResult[];
|
|
48
|
+
evaluationTimeMs: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Options for the PolicyComplianceProver.
|
|
52
|
+
*/
|
|
53
|
+
export interface PolicyComplianceProverOptions {
|
|
54
|
+
/**
|
|
55
|
+
* Enable debug logging.
|
|
56
|
+
*/
|
|
57
|
+
debug?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Custom rule evaluators for extending policy rule types.
|
|
60
|
+
*/
|
|
61
|
+
customEvaluators?: Map<string, RuleEvaluator>;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Function type for evaluating a policy rule.
|
|
65
|
+
*/
|
|
66
|
+
export type RuleEvaluator = (rule: PolicyRule, promptHash: string, outputHash: string) => Promise<RuleEvaluationResult>;
|
|
67
|
+
/**
|
|
68
|
+
* PolicyComplianceProver generates and verifies ZK proofs of policy compliance.
|
|
69
|
+
*
|
|
70
|
+
* This prover evaluates content against policy rules and produces a proof
|
|
71
|
+
* that the content complies (or not) without revealing the actual content.
|
|
72
|
+
* Only content hashes and policy identifiers are exposed in public inputs.
|
|
73
|
+
*
|
|
74
|
+
* Supported rule types:
|
|
75
|
+
* - blocklist: Content must not match blocked patterns
|
|
76
|
+
* - allowlist: Content must match at least one allowed pattern
|
|
77
|
+
* - regex: Content must (not) match a regex pattern
|
|
78
|
+
* - classifier: Content must pass ML classification threshold
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* const prover = new PolicyComplianceProver();
|
|
83
|
+
* const proof = await prover.generateProof(input);
|
|
84
|
+
* console.log(proof.publicInputs.compliant); // true or false
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export declare class PolicyComplianceProver {
|
|
88
|
+
private readonly debug;
|
|
89
|
+
private readonly evaluators;
|
|
90
|
+
/**
|
|
91
|
+
* Create a new PolicyComplianceProver instance.
|
|
92
|
+
*
|
|
93
|
+
* @param options - Configuration options
|
|
94
|
+
*/
|
|
95
|
+
constructor(options?: PolicyComplianceProverOptions);
|
|
96
|
+
/**
|
|
97
|
+
* Generate a policy compliance proof.
|
|
98
|
+
*
|
|
99
|
+
* This method evaluates the content against all policy rules and generates
|
|
100
|
+
* a ZK proof of the compliance result. The proof binds to the Cardano
|
|
101
|
+
* anchor transaction for cross-chain verification.
|
|
102
|
+
*
|
|
103
|
+
* @param input - Policy compliance input
|
|
104
|
+
* @returns Promise resolving to the policy proof
|
|
105
|
+
* @throws MidnightProverException on validation or generation failure
|
|
106
|
+
*/
|
|
107
|
+
generateProof(input: PolicyInput): Promise<PolicyProof>;
|
|
108
|
+
/**
|
|
109
|
+
* Verify a policy compliance proof.
|
|
110
|
+
*
|
|
111
|
+
* This method verifies the cryptographic validity of the proof and
|
|
112
|
+
* checks that public inputs are consistent.
|
|
113
|
+
*
|
|
114
|
+
* @param proof - The policy proof to verify
|
|
115
|
+
* @returns Promise resolving to true if valid
|
|
116
|
+
*/
|
|
117
|
+
verifyProof(proof: PolicyProof): Promise<boolean>;
|
|
118
|
+
/**
|
|
119
|
+
* Evaluate all policy rules against content.
|
|
120
|
+
*
|
|
121
|
+
* @param policy - The policy to evaluate
|
|
122
|
+
* @param promptHash - Hash of prompt content
|
|
123
|
+
* @param outputHash - Hash of output content
|
|
124
|
+
* @returns Evaluation result with rule-by-rule breakdown
|
|
125
|
+
*/
|
|
126
|
+
evaluatePolicy(policy: ContentPolicy, promptHash: string, outputHash: string): Promise<PolicyEvaluationResult>;
|
|
127
|
+
/**
|
|
128
|
+
* Register a custom rule evaluator.
|
|
129
|
+
*
|
|
130
|
+
* @param type - Rule type identifier
|
|
131
|
+
* @param evaluator - Evaluation function
|
|
132
|
+
*/
|
|
133
|
+
registerEvaluator(type: string, evaluator: RuleEvaluator): void;
|
|
134
|
+
/**
|
|
135
|
+
* Validate policy input.
|
|
136
|
+
*/
|
|
137
|
+
private validateInput;
|
|
138
|
+
/**
|
|
139
|
+
* Generate mock proof bytes.
|
|
140
|
+
* In a real implementation, this would call the Midnight proof server.
|
|
141
|
+
*/
|
|
142
|
+
private generateProofBytes;
|
|
143
|
+
/**
|
|
144
|
+
* Verify mock proof bytes.
|
|
145
|
+
* In a real implementation, this would cryptographically verify the proof
|
|
146
|
+
* against the public inputs using the ZK verifier.
|
|
147
|
+
*/
|
|
148
|
+
private verifyProofBytes;
|
|
149
|
+
/**
|
|
150
|
+
* Generate proof ID from public inputs.
|
|
151
|
+
*/
|
|
152
|
+
private generateProofId;
|
|
153
|
+
/**
|
|
154
|
+
* Convert hex string to bytes.
|
|
155
|
+
*/
|
|
156
|
+
private hexToBytes;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Create a new PolicyComplianceProver instance.
|
|
160
|
+
*
|
|
161
|
+
* @param options - Configuration options
|
|
162
|
+
* @returns New prover instance
|
|
163
|
+
*/
|
|
164
|
+
export declare function createPolicyComplianceProver(options?: PolicyComplianceProverOptions): PolicyComplianceProver;
|
|
165
|
+
//# sourceMappingURL=policy-compliance-proof.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-compliance-proof.d.ts","sourceRoot":"","sources":["../../src/proofs/policy-compliance-proof.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAOH,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EAEX,aAAa,EACb,UAAU,EACX,MAAM,aAAa,CAAC;AAWrB;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,oBAAoB,EAAE,CAAC;IACpC,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,6BAA6B;IAC5C;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CAC/C;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,CAC1B,IAAI,EAAE,UAAU,EAChB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,KACf,OAAO,CAAC,oBAAoB,CAAC,CAAC;AA0NnC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAU;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6B;IAExD;;;;OAIG;gBACS,OAAO,GAAE,6BAAkC;IAmBvD;;;;;;;;;;OAUG;IACG,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IAyD7D;;;;;;;;OAQG;IACG,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IAgDvD;;;;;;;OAOG;IACG,cAAc,CAClB,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,sBAAsB,CAAC;IAiClC;;;;;OAKG;IACH,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,GAAG,IAAI;IAQ/D;;OAEG;IACH,OAAO,CAAC,aAAa;IA4CrB;;;OAGG;YACW,kBAAkB;IAkDhC;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IA2BxB;;OAEG;YACW,eAAe;IAS7B;;OAEG;IACH,OAAO,CAAC,UAAU;CAOnB;AAMD;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,CAAC,EAAE,6BAA6B,GACtC,sBAAsB,CAExB"}
|
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Policy compliance proof generation and verification.
|
|
3
|
+
*
|
|
4
|
+
* Location: packages/midnight-prover/src/proofs/policy-compliance-proof.ts
|
|
5
|
+
*
|
|
6
|
+
* Summary:
|
|
7
|
+
* This module implements the PolicyComplianceProver class which generates ZK proofs
|
|
8
|
+
* demonstrating that content complies with a specified policy without revealing the
|
|
9
|
+
* actual content. The prover evaluates policy rules (blocklist, allowlist, regex,
|
|
10
|
+
* classifier) against content hashes and produces a proof of compliance.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* - Used by the MidnightProver to generate policy compliance proofs
|
|
14
|
+
* - Integrates with the types defined in ../types.ts
|
|
15
|
+
* - Binds proofs to Cardano anchor transactions for cross-chain verification
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { PolicyComplianceProver } from './policy-compliance-proof.js';
|
|
20
|
+
*
|
|
21
|
+
* const prover = new PolicyComplianceProver();
|
|
22
|
+
*
|
|
23
|
+
* const proof = await prover.generateProof({
|
|
24
|
+
* promptHash: 'abc123...',
|
|
25
|
+
* outputHash: 'def456...',
|
|
26
|
+
* policy: { id: 'policy-1', version: '1.0.0', rules: [...] },
|
|
27
|
+
* cardanoAnchorTxHash: 'txhash...',
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* const isValid = await prover.verifyProof(proof);
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
import { sha256StringHex, canonicalize, } from "@fluxpointstudios/orynq-sdk-core/utils";
|
|
34
|
+
import { MidnightProverError, MidnightProverException, } from "../types.js";
|
|
35
|
+
// =============================================================================
|
|
36
|
+
// DOMAIN PREFIXES
|
|
37
|
+
// =============================================================================
|
|
38
|
+
/**
|
|
39
|
+
* Domain separation prefixes for policy compliance proofs.
|
|
40
|
+
*/
|
|
41
|
+
const POLICY_DOMAIN_PREFIXES = {
|
|
42
|
+
proof: "poi-prover:policy:v1|",
|
|
43
|
+
witness: "poi-prover:policy-witness:v1|",
|
|
44
|
+
publicInput: "poi-prover:policy-input:v1|",
|
|
45
|
+
};
|
|
46
|
+
// =============================================================================
|
|
47
|
+
// DEFAULT RULE EVALUATORS
|
|
48
|
+
// =============================================================================
|
|
49
|
+
/**
|
|
50
|
+
* Default evaluator for blocklist rules.
|
|
51
|
+
* In ZK context, this simulates checking content hashes against a blocklist.
|
|
52
|
+
*
|
|
53
|
+
* @param rule - The blocklist rule to evaluate
|
|
54
|
+
* @param promptHash - Hash of the prompt content
|
|
55
|
+
* @param outputHash - Hash of the output content
|
|
56
|
+
* @returns Evaluation result
|
|
57
|
+
*/
|
|
58
|
+
async function evaluateBlocklistRule(rule, promptHash, outputHash) {
|
|
59
|
+
// In a real ZK implementation, this would use a Bloom filter or
|
|
60
|
+
// commitment scheme to check against the blocklist without revealing items.
|
|
61
|
+
// For mock purposes, we simulate by checking if hashes match blocklist patterns.
|
|
62
|
+
const blockedHashes = rule.params.blockedHashes ?? [];
|
|
63
|
+
const target = rule.target;
|
|
64
|
+
let passed = true;
|
|
65
|
+
let message = "Content does not match any blocked patterns";
|
|
66
|
+
if (target === "prompt" || target === "both") {
|
|
67
|
+
if (blockedHashes.includes(promptHash)) {
|
|
68
|
+
passed = false;
|
|
69
|
+
message = "Prompt matches blocked pattern";
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (passed && (target === "output" || target === "both")) {
|
|
73
|
+
if (blockedHashes.includes(outputHash)) {
|
|
74
|
+
passed = false;
|
|
75
|
+
message = "Output matches blocked pattern";
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return { rule, passed, message };
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Default evaluator for allowlist rules.
|
|
82
|
+
* Content must match at least one allowed pattern.
|
|
83
|
+
*
|
|
84
|
+
* @param rule - The allowlist rule to evaluate
|
|
85
|
+
* @param promptHash - Hash of the prompt content
|
|
86
|
+
* @param outputHash - Hash of the output content
|
|
87
|
+
* @returns Evaluation result
|
|
88
|
+
*/
|
|
89
|
+
async function evaluateAllowlistRule(rule, promptHash, outputHash) {
|
|
90
|
+
// In a real ZK implementation, this would verify membership in an
|
|
91
|
+
// allowed set using accumulator proofs or similar techniques.
|
|
92
|
+
const allowedHashes = rule.params.allowedHashes ?? [];
|
|
93
|
+
const requireAllMatch = rule.params.requireAllMatch ?? false;
|
|
94
|
+
const target = rule.target;
|
|
95
|
+
// If allowedHashes is empty, everything is allowed (permissive default)
|
|
96
|
+
if (allowedHashes.length === 0) {
|
|
97
|
+
return { rule, passed: true, message: "No allowlist constraints (permissive)" };
|
|
98
|
+
}
|
|
99
|
+
let promptAllowed = true;
|
|
100
|
+
let outputAllowed = true;
|
|
101
|
+
if (target === "prompt" || target === "both") {
|
|
102
|
+
promptAllowed = allowedHashes.some((hash) => promptHash.startsWith(hash));
|
|
103
|
+
}
|
|
104
|
+
if (target === "output" || target === "both") {
|
|
105
|
+
outputAllowed = allowedHashes.some((hash) => outputHash.startsWith(hash));
|
|
106
|
+
}
|
|
107
|
+
const passed = requireAllMatch
|
|
108
|
+
? promptAllowed && outputAllowed
|
|
109
|
+
: promptAllowed || outputAllowed;
|
|
110
|
+
const message = passed
|
|
111
|
+
? "Content matches allowlist criteria"
|
|
112
|
+
: "Content does not match any allowed patterns";
|
|
113
|
+
return { rule, passed, message };
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Default evaluator for regex rules.
|
|
117
|
+
* Simulates regex pattern matching in ZK context.
|
|
118
|
+
*
|
|
119
|
+
* @param rule - The regex rule to evaluate
|
|
120
|
+
* @param promptHash - Hash of the prompt content
|
|
121
|
+
* @param outputHash - Hash of the output content
|
|
122
|
+
* @returns Evaluation result
|
|
123
|
+
*/
|
|
124
|
+
async function evaluateRegexRule(rule, promptHash, outputHash) {
|
|
125
|
+
// In a real ZK implementation, regex matching would be done on
|
|
126
|
+
// the actual content as a private witness. The circuit would
|
|
127
|
+
// verify the regex match and output a boolean commitment.
|
|
128
|
+
// Here we simulate by checking pattern hash signatures.
|
|
129
|
+
const patternHash = rule.params.patternHash ?? "";
|
|
130
|
+
const isBlockPattern = rule.params.isBlockPattern ?? true;
|
|
131
|
+
const target = rule.target;
|
|
132
|
+
// Simulate pattern matching by checking if content hash contains pattern signature
|
|
133
|
+
let matchFound = false;
|
|
134
|
+
const hashesToCheck = [];
|
|
135
|
+
if (target === "prompt" || target === "both") {
|
|
136
|
+
hashesToCheck.push(promptHash);
|
|
137
|
+
}
|
|
138
|
+
if (target === "output" || target === "both") {
|
|
139
|
+
hashesToCheck.push(outputHash);
|
|
140
|
+
}
|
|
141
|
+
// Simulate: pattern matches if any hash shares prefix with pattern hash
|
|
142
|
+
for (const hash of hashesToCheck) {
|
|
143
|
+
if (patternHash && hash.slice(0, 8) === patternHash.slice(0, 8)) {
|
|
144
|
+
matchFound = true;
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// For block patterns, passing means no match; for allow patterns, passing means match
|
|
149
|
+
const passed = isBlockPattern ? !matchFound : matchFound;
|
|
150
|
+
const message = isBlockPattern
|
|
151
|
+
? passed
|
|
152
|
+
? "Content does not match blocked regex pattern"
|
|
153
|
+
: "Content matches blocked regex pattern"
|
|
154
|
+
: passed
|
|
155
|
+
? "Content matches required regex pattern"
|
|
156
|
+
: "Content does not match required regex pattern";
|
|
157
|
+
return { rule, passed, message };
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Default evaluator for classifier rules.
|
|
161
|
+
* Simulates ML classifier evaluation in ZK context.
|
|
162
|
+
*
|
|
163
|
+
* @param rule - The classifier rule to evaluate
|
|
164
|
+
* @param promptHash - Hash of the prompt content
|
|
165
|
+
* @param outputHash - Hash of the output content
|
|
166
|
+
* @returns Evaluation result
|
|
167
|
+
*/
|
|
168
|
+
async function evaluateClassifierRule(rule, promptHash, outputHash) {
|
|
169
|
+
// In a real ZK implementation (zkML), this would run a neural network
|
|
170
|
+
// classifier circuit on the content and produce a proof of the
|
|
171
|
+
// classification result. This is extremely expensive and limited
|
|
172
|
+
// to small models.
|
|
173
|
+
// Here we simulate with deterministic hash-based classification.
|
|
174
|
+
const classifierId = rule.params.classifierId ?? "default";
|
|
175
|
+
const threshold = rule.params.threshold ?? 0.5;
|
|
176
|
+
const target = rule.target;
|
|
177
|
+
// Simulate classification score based on hash
|
|
178
|
+
const hashesToClassify = [];
|
|
179
|
+
if (target === "prompt" || target === "both") {
|
|
180
|
+
hashesToClassify.push(promptHash);
|
|
181
|
+
}
|
|
182
|
+
if (target === "output" || target === "both") {
|
|
183
|
+
hashesToClassify.push(outputHash);
|
|
184
|
+
}
|
|
185
|
+
// Deterministic "classification" based on hash bytes
|
|
186
|
+
let simulatedScore = 0;
|
|
187
|
+
for (const hash of hashesToClassify) {
|
|
188
|
+
// Use first 4 bytes of hash as a pseudo-random score
|
|
189
|
+
const hashBytes = hash.slice(0, 8);
|
|
190
|
+
const numericValue = parseInt(hashBytes, 16);
|
|
191
|
+
simulatedScore += (numericValue % 100) / 100;
|
|
192
|
+
}
|
|
193
|
+
simulatedScore = simulatedScore / hashesToClassify.length;
|
|
194
|
+
const passed = simulatedScore >= threshold;
|
|
195
|
+
const message = passed
|
|
196
|
+
? `Classifier '${classifierId}' score ${simulatedScore.toFixed(2)} meets threshold ${threshold}`
|
|
197
|
+
: `Classifier '${classifierId}' score ${simulatedScore.toFixed(2)} below threshold ${threshold}`;
|
|
198
|
+
return { rule, passed, message };
|
|
199
|
+
}
|
|
200
|
+
// =============================================================================
|
|
201
|
+
// POLICY COMPLIANCE PROVER
|
|
202
|
+
// =============================================================================
|
|
203
|
+
/**
|
|
204
|
+
* PolicyComplianceProver generates and verifies ZK proofs of policy compliance.
|
|
205
|
+
*
|
|
206
|
+
* This prover evaluates content against policy rules and produces a proof
|
|
207
|
+
* that the content complies (or not) without revealing the actual content.
|
|
208
|
+
* Only content hashes and policy identifiers are exposed in public inputs.
|
|
209
|
+
*
|
|
210
|
+
* Supported rule types:
|
|
211
|
+
* - blocklist: Content must not match blocked patterns
|
|
212
|
+
* - allowlist: Content must match at least one allowed pattern
|
|
213
|
+
* - regex: Content must (not) match a regex pattern
|
|
214
|
+
* - classifier: Content must pass ML classification threshold
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```typescript
|
|
218
|
+
* const prover = new PolicyComplianceProver();
|
|
219
|
+
* const proof = await prover.generateProof(input);
|
|
220
|
+
* console.log(proof.publicInputs.compliant); // true or false
|
|
221
|
+
* ```
|
|
222
|
+
*/
|
|
223
|
+
export class PolicyComplianceProver {
|
|
224
|
+
debug;
|
|
225
|
+
evaluators;
|
|
226
|
+
/**
|
|
227
|
+
* Create a new PolicyComplianceProver instance.
|
|
228
|
+
*
|
|
229
|
+
* @param options - Configuration options
|
|
230
|
+
*/
|
|
231
|
+
constructor(options = {}) {
|
|
232
|
+
this.debug = options.debug ?? false;
|
|
233
|
+
// Initialize with default evaluators
|
|
234
|
+
this.evaluators = new Map([
|
|
235
|
+
["blocklist", evaluateBlocklistRule],
|
|
236
|
+
["allowlist", evaluateAllowlistRule],
|
|
237
|
+
["regex", evaluateRegexRule],
|
|
238
|
+
["classifier", evaluateClassifierRule],
|
|
239
|
+
]);
|
|
240
|
+
// Add custom evaluators if provided
|
|
241
|
+
if (options.customEvaluators) {
|
|
242
|
+
for (const [type, evaluator] of options.customEvaluators) {
|
|
243
|
+
this.evaluators.set(type, evaluator);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Generate a policy compliance proof.
|
|
249
|
+
*
|
|
250
|
+
* This method evaluates the content against all policy rules and generates
|
|
251
|
+
* a ZK proof of the compliance result. The proof binds to the Cardano
|
|
252
|
+
* anchor transaction for cross-chain verification.
|
|
253
|
+
*
|
|
254
|
+
* @param input - Policy compliance input
|
|
255
|
+
* @returns Promise resolving to the policy proof
|
|
256
|
+
* @throws MidnightProverException on validation or generation failure
|
|
257
|
+
*/
|
|
258
|
+
async generateProof(input) {
|
|
259
|
+
const startTime = performance.now();
|
|
260
|
+
// Validate input
|
|
261
|
+
this.validateInput(input);
|
|
262
|
+
if (this.debug) {
|
|
263
|
+
console.log("[PolicyComplianceProver] Generating proof for policy:", input.policy.id);
|
|
264
|
+
}
|
|
265
|
+
// Evaluate policy rules
|
|
266
|
+
const evaluationResult = await this.evaluatePolicy(input.policy, input.promptHash, input.outputHash);
|
|
267
|
+
// Generate proof bytes (mock implementation)
|
|
268
|
+
const proofBytes = await this.generateProofBytes(input, evaluationResult);
|
|
269
|
+
const endTime = performance.now();
|
|
270
|
+
const provingTimeMs = Math.round(endTime - startTime);
|
|
271
|
+
// Construct public inputs
|
|
272
|
+
const publicInputs = {
|
|
273
|
+
promptHash: input.promptHash,
|
|
274
|
+
policyId: input.policy.id,
|
|
275
|
+
policyVersion: input.policy.version,
|
|
276
|
+
compliant: evaluationResult.compliant,
|
|
277
|
+
cardanoAnchorTxHash: input.cardanoAnchorTxHash,
|
|
278
|
+
};
|
|
279
|
+
// Generate proof ID
|
|
280
|
+
const proofId = await this.generateProofId(publicInputs);
|
|
281
|
+
const proof = {
|
|
282
|
+
proofType: "policy-compliance",
|
|
283
|
+
proofId,
|
|
284
|
+
proof: proofBytes,
|
|
285
|
+
createdAt: new Date().toISOString(),
|
|
286
|
+
provingTimeMs,
|
|
287
|
+
proofSizeBytes: proofBytes.length,
|
|
288
|
+
publicInputs,
|
|
289
|
+
};
|
|
290
|
+
if (this.debug) {
|
|
291
|
+
console.log("[PolicyComplianceProver] Proof generated:", {
|
|
292
|
+
proofId,
|
|
293
|
+
compliant: evaluationResult.compliant,
|
|
294
|
+
provingTimeMs,
|
|
295
|
+
ruleCount: input.policy.rules.length,
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
return proof;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Verify a policy compliance proof.
|
|
302
|
+
*
|
|
303
|
+
* This method verifies the cryptographic validity of the proof and
|
|
304
|
+
* checks that public inputs are consistent.
|
|
305
|
+
*
|
|
306
|
+
* @param proof - The policy proof to verify
|
|
307
|
+
* @returns Promise resolving to true if valid
|
|
308
|
+
*/
|
|
309
|
+
async verifyProof(proof) {
|
|
310
|
+
try {
|
|
311
|
+
// Validate proof structure
|
|
312
|
+
if (proof.proofType !== "policy-compliance") {
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
if (!proof.proof || proof.proof.length === 0) {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
// Validate public inputs
|
|
319
|
+
const { publicInputs } = proof;
|
|
320
|
+
if (!publicInputs.promptHash || publicInputs.promptHash.length !== 64) {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
if (!publicInputs.policyId || publicInputs.policyId.length === 0) {
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
if (!publicInputs.policyVersion || publicInputs.policyVersion.length === 0) {
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
if (!publicInputs.cardanoAnchorTxHash || publicInputs.cardanoAnchorTxHash.length === 0) {
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
if (typeof publicInputs.compliant !== "boolean") {
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
// Verify proof ID matches public inputs
|
|
336
|
+
const expectedProofId = await this.generateProofId(publicInputs);
|
|
337
|
+
if (proof.proofId !== expectedProofId) {
|
|
338
|
+
return false;
|
|
339
|
+
}
|
|
340
|
+
// Verify proof bytes contain expected structure
|
|
341
|
+
// In a real implementation, this would verify the ZK proof cryptographically
|
|
342
|
+
const proofValid = this.verifyProofBytes(proof.proof, publicInputs);
|
|
343
|
+
return proofValid;
|
|
344
|
+
}
|
|
345
|
+
catch (error) {
|
|
346
|
+
if (this.debug) {
|
|
347
|
+
console.error("[PolicyComplianceProver] Verification error:", error);
|
|
348
|
+
}
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Evaluate all policy rules against content.
|
|
354
|
+
*
|
|
355
|
+
* @param policy - The policy to evaluate
|
|
356
|
+
* @param promptHash - Hash of prompt content
|
|
357
|
+
* @param outputHash - Hash of output content
|
|
358
|
+
* @returns Evaluation result with rule-by-rule breakdown
|
|
359
|
+
*/
|
|
360
|
+
async evaluatePolicy(policy, promptHash, outputHash) {
|
|
361
|
+
const startTime = performance.now();
|
|
362
|
+
const ruleResults = [];
|
|
363
|
+
for (const rule of policy.rules) {
|
|
364
|
+
const evaluator = this.evaluators.get(rule.type);
|
|
365
|
+
if (!evaluator) {
|
|
366
|
+
throw new MidnightProverException(MidnightProverError.INVALID_INPUT, `Unknown rule type: ${rule.type}`);
|
|
367
|
+
}
|
|
368
|
+
const result = await evaluator(rule, promptHash, outputHash);
|
|
369
|
+
ruleResults.push(result);
|
|
370
|
+
if (this.debug) {
|
|
371
|
+
console.log(`[PolicyComplianceProver] Rule ${rule.type}:`, result.message);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
const endTime = performance.now();
|
|
375
|
+
// Policy is compliant only if all rules pass
|
|
376
|
+
const compliant = ruleResults.every((r) => r.passed);
|
|
377
|
+
return {
|
|
378
|
+
compliant,
|
|
379
|
+
ruleResults,
|
|
380
|
+
evaluationTimeMs: Math.round(endTime - startTime),
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Register a custom rule evaluator.
|
|
385
|
+
*
|
|
386
|
+
* @param type - Rule type identifier
|
|
387
|
+
* @param evaluator - Evaluation function
|
|
388
|
+
*/
|
|
389
|
+
registerEvaluator(type, evaluator) {
|
|
390
|
+
this.evaluators.set(type, evaluator);
|
|
391
|
+
}
|
|
392
|
+
// ===========================================================================
|
|
393
|
+
// PRIVATE METHODS
|
|
394
|
+
// ===========================================================================
|
|
395
|
+
/**
|
|
396
|
+
* Validate policy input.
|
|
397
|
+
*/
|
|
398
|
+
validateInput(input) {
|
|
399
|
+
if (!input.promptHash || input.promptHash.length !== 64) {
|
|
400
|
+
throw new MidnightProverException(MidnightProverError.INVALID_INPUT, "Invalid prompt hash: must be 64-character hex string");
|
|
401
|
+
}
|
|
402
|
+
if (!input.outputHash || input.outputHash.length !== 64) {
|
|
403
|
+
throw new MidnightProverException(MidnightProverError.INVALID_INPUT, "Invalid output hash: must be 64-character hex string");
|
|
404
|
+
}
|
|
405
|
+
if (!input.policy) {
|
|
406
|
+
throw new MidnightProverException(MidnightProverError.MISSING_REQUIRED_FIELD, "Policy is required");
|
|
407
|
+
}
|
|
408
|
+
if (!input.policy.id || input.policy.id.length === 0) {
|
|
409
|
+
throw new MidnightProverException(MidnightProverError.INVALID_INPUT, "Policy ID is required");
|
|
410
|
+
}
|
|
411
|
+
if (!input.policy.version || input.policy.version.length === 0) {
|
|
412
|
+
throw new MidnightProverException(MidnightProverError.INVALID_INPUT, "Policy version is required");
|
|
413
|
+
}
|
|
414
|
+
if (!input.cardanoAnchorTxHash || input.cardanoAnchorTxHash.length === 0) {
|
|
415
|
+
throw new MidnightProverException(MidnightProverError.MISSING_REQUIRED_FIELD, "Cardano anchor transaction hash is required");
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Generate mock proof bytes.
|
|
420
|
+
* In a real implementation, this would call the Midnight proof server.
|
|
421
|
+
*/
|
|
422
|
+
async generateProofBytes(input, evaluation) {
|
|
423
|
+
// Construct witness data (private inputs)
|
|
424
|
+
const witnessData = canonicalize({
|
|
425
|
+
promptHash: input.promptHash,
|
|
426
|
+
outputHash: input.outputHash,
|
|
427
|
+
policyId: input.policy.id,
|
|
428
|
+
policyVersion: input.policy.version,
|
|
429
|
+
rules: input.policy.rules,
|
|
430
|
+
compliant: evaluation.compliant,
|
|
431
|
+
cardanoAnchorTxHash: input.cardanoAnchorTxHash,
|
|
432
|
+
});
|
|
433
|
+
// Generate witness hash
|
|
434
|
+
const witnessHash = await sha256StringHex(POLICY_DOMAIN_PREFIXES.witness + witnessData);
|
|
435
|
+
// Construct public input commitment
|
|
436
|
+
const publicInputData = canonicalize({
|
|
437
|
+
promptHash: input.promptHash,
|
|
438
|
+
policyId: input.policy.id,
|
|
439
|
+
policyVersion: input.policy.version,
|
|
440
|
+
compliant: evaluation.compliant,
|
|
441
|
+
cardanoAnchorTxHash: input.cardanoAnchorTxHash,
|
|
442
|
+
});
|
|
443
|
+
const publicInputHash = await sha256StringHex(POLICY_DOMAIN_PREFIXES.publicInput + publicInputData);
|
|
444
|
+
// Generate mock proof: commitment to witness + public inputs
|
|
445
|
+
const proofCommitment = await sha256StringHex(POLICY_DOMAIN_PREFIXES.proof + witnessHash + "|" + publicInputHash);
|
|
446
|
+
// Construct proof bytes: version (1 byte) + commitment (32 bytes) + witness hash (32 bytes)
|
|
447
|
+
const proofBytes = new Uint8Array(65);
|
|
448
|
+
proofBytes[0] = 0x01; // Version 1
|
|
449
|
+
// Copy commitment hash
|
|
450
|
+
const commitmentBytes = this.hexToBytes(proofCommitment);
|
|
451
|
+
proofBytes.set(commitmentBytes, 1);
|
|
452
|
+
// Copy witness hash
|
|
453
|
+
const witnessBytes = this.hexToBytes(witnessHash);
|
|
454
|
+
proofBytes.set(witnessBytes, 33);
|
|
455
|
+
return proofBytes;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Verify mock proof bytes.
|
|
459
|
+
* In a real implementation, this would cryptographically verify the proof
|
|
460
|
+
* against the public inputs using the ZK verifier.
|
|
461
|
+
*/
|
|
462
|
+
verifyProofBytes(proofBytes, _publicInputs) {
|
|
463
|
+
// Check minimum proof size
|
|
464
|
+
if (proofBytes.length < 65) {
|
|
465
|
+
return false;
|
|
466
|
+
}
|
|
467
|
+
// Check version byte
|
|
468
|
+
if (proofBytes[0] !== 0x01) {
|
|
469
|
+
return false;
|
|
470
|
+
}
|
|
471
|
+
// Note: _publicInputs would be used in real ZK verification
|
|
472
|
+
// Extract commitment from proof and verify structure is valid
|
|
473
|
+
const commitmentBytes = proofBytes.slice(1, 33);
|
|
474
|
+
const witnessHashBytes = proofBytes.slice(33, 65);
|
|
475
|
+
// Verify both are non-zero (basic sanity check)
|
|
476
|
+
const commitmentNonZero = commitmentBytes.some((b) => b !== 0);
|
|
477
|
+
const witnessNonZero = witnessHashBytes.some((b) => b !== 0);
|
|
478
|
+
return commitmentNonZero && witnessNonZero;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Generate proof ID from public inputs.
|
|
482
|
+
*/
|
|
483
|
+
async generateProofId(publicInputs) {
|
|
484
|
+
const data = canonicalize({
|
|
485
|
+
type: "policy-compliance",
|
|
486
|
+
...publicInputs,
|
|
487
|
+
});
|
|
488
|
+
const hash = await sha256StringHex(data);
|
|
489
|
+
return `policy-proof-${hash.slice(0, 16)}`;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Convert hex string to bytes.
|
|
493
|
+
*/
|
|
494
|
+
hexToBytes(hex) {
|
|
495
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
496
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
497
|
+
bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);
|
|
498
|
+
}
|
|
499
|
+
return bytes;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
// =============================================================================
|
|
503
|
+
// FACTORY FUNCTION
|
|
504
|
+
// =============================================================================
|
|
505
|
+
/**
|
|
506
|
+
* Create a new PolicyComplianceProver instance.
|
|
507
|
+
*
|
|
508
|
+
* @param options - Configuration options
|
|
509
|
+
* @returns New prover instance
|
|
510
|
+
*/
|
|
511
|
+
export function createPolicyComplianceProver(options) {
|
|
512
|
+
return new PolicyComplianceProver(options);
|
|
513
|
+
}
|
|
514
|
+
//# sourceMappingURL=policy-compliance-proof.js.map
|