-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathzk-reasoning.ts
More file actions
181 lines (165 loc) · 5.47 KB
/
Copy pathzk-reasoning.ts
File metadata and controls
181 lines (165 loc) · 5.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
import "server-only";
import { createHash, randomUUID } from "node:crypto";
import type {
StoredDecisionPayload,
StoredPortfolioSnapshot,
StoredZkReasoningProof,
ZkReasoningProofStatus,
} from "@/lib/backend-data";
import { type WalletNetworkKey } from "@/lib/wallet";
import { recordZkReasoningProof } from "@/lib/server/runtime-store";
import { recordProofRegistryAnchor } from "@/lib/server/backend-signer";
import { uploadJsonToZeroGStorage } from "@/lib/server/zero-g-storage";
export interface CreateZkReasoningProofInput {
networkKey: WalletNetworkKey;
walletAddress?: string;
agentId?: string;
prompt?: string;
reasoning?: string;
decision?: Partial<StoredDecisionPayload>;
portfolioSnapshot?: StoredPortfolioSnapshot;
verifier?: string;
proofType?: string;
summary?: string;
publicSignals?: Record<string, unknown>;
}
function canonicalize(value: unknown): unknown {
if (Array.isArray(value)) {
return value.map((item) => canonicalize(item));
}
if (value && typeof value === "object") {
return Object.fromEntries(
Object.entries(value as Record<string, unknown>)
.filter(([, item]) => item !== undefined)
.sort(([left], [right]) => left.localeCompare(right))
.map(([key, item]) => [key, canonicalize(item)]),
);
}
return value;
}
function stableStringify(value: unknown) {
return JSON.stringify(canonicalize(value));
}
function sha256Hex(value: unknown) {
return createHash("sha256")
.update(typeof value === "string" ? value : stableStringify(value))
.digest("hex");
}
function joinNotes(...notes: Array<string | undefined>) {
const values = notes.filter(Boolean);
return values.length ? values.join(",") : undefined;
}
function resolveStatus(
networkKey: WalletNetworkKey,
storageMode: StoredZkReasoningProof["storageMode"],
): ZkReasoningProofStatus {
if (storageMode === "0g" && networkKey === "testnet") {
return "testnet-verified";
}
if (storageMode === "0g") {
return "tee-envelope-recorded";
}
return "zk-ready";
}
async function anchorReasoningEnvelope(input: {
cid: string;
rootHash?: string;
storageTxHash?: string;
networkKey: WalletNetworkKey;
}) {
return recordProofRegistryAnchor({
networkKey: input.networkKey,
cid: input.cid,
rootHash: input.rootHash,
storageTxHash: input.storageTxHash,
currentApyBps: 0,
optimizedApyBps: 0,
});
}
export function buildZkReasoningProofPayload(input: CreateZkReasoningProofInput) {
const createdAt = new Date().toISOString();
const proofId = `zkr-${randomUUID()}`;
const verifier = input.verifier ?? "0G Storage + YieldBoost deterministic verifier";
const proofType = input.proofType ?? "TEE/ZK reasoning proof envelope";
const privateInputCommitment = sha256Hex({
prompt: input.prompt,
reasoning: input.reasoning,
decision: input.decision,
portfolioSnapshot: input.portfolioSnapshot,
});
const publicSignals = {
appId: "yieldboost-ai",
networkKey: input.networkKey,
walletAddress: input.walletAddress,
agentId: input.agentId,
recommended: input.decision?.recommended,
currentApy: input.decision?.current_apy,
optimizedApy: input.decision?.optimized_apy,
...input.publicSignals,
};
const envelope = {
appId: "yieldboost-ai",
artifactType: "zk-reasoning-proof-envelope",
proofId,
createdAt,
networkKey: input.networkKey,
walletAddress: input.walletAddress,
agentId: input.agentId,
verifier,
proofType,
envelopeMode: "tee-zk-ready",
cryptographyClaim:
"This records a TEE/ZK reasoning commitment envelope; it does not claim a full ZK circuit proof.",
publicSignals,
privateInputCommitment,
reasoningTraceCommitment: sha256Hex(input.reasoning ?? input.decision?.reasoning ?? ""),
decisionCommitment: sha256Hex(input.decision ?? {}),
portfolioCommitment: sha256Hex(input.portfolioSnapshot ?? {}),
summary:
input.summary ??
`Reasoning envelope recorded for ${input.decision?.recommended ?? "YieldBoost agent decision"}.`,
};
return {
...envelope,
artifactHash: sha256Hex(envelope),
};
}
export async function createZkReasoningProof(input: CreateZkReasoningProofInput) {
const payload = buildZkReasoningProofPayload(input);
const upload = await uploadJsonToZeroGStorage({
networkKey: input.networkKey,
payload,
filenamePrefix: "yieldboost-zk-reasoning",
allowLocalFallback: true,
});
const anchor = await anchorReasoningEnvelope({
cid: upload.cid,
rootHash: upload.rootHash,
storageTxHash: upload.txHash,
networkKey: input.networkKey,
});
const record: StoredZkReasoningProof = {
proofId: payload.proofId,
proofCid: upload.cid,
txHash: upload.txHash,
blockNumber: upload.blockNumber,
explorerUrl: upload.explorerUrl,
networkKey: input.networkKey,
verifier: payload.verifier,
proofType: payload.proofType,
createdAt: payload.createdAt,
status: resolveStatus(input.networkKey, upload.storageMode),
summary: payload.summary,
storageMode: upload.storageMode,
artifactHash: payload.artifactHash,
rootHash: upload.rootHash,
walletAddress: input.walletAddress,
agentId: input.agentId,
proofRegistryAddress: anchor.proofRegistryAddress,
proofRegistryTxHash: anchor.proofRegistryTxHash,
proofRegistryProofId: anchor.proofRegistryProofId,
proofRegistryExplorerUrl: anchor.proofRegistryExplorerUrl,
note: joinNotes(upload.note, anchor.note),
};
return recordZkReasoningProof(record);
}