@escro/sdk 0.1.4 → 0.3.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/README.md +1 -1
- package/dist/index.cjs +2018 -84
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1594 -14
- package/dist/index.d.ts +1594 -14
- package/dist/index.js +1928 -85
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/dist/index.cjs
CHANGED
|
@@ -32,27 +32,243 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
32
32
|
// src/index.ts
|
|
33
33
|
var index_exports = {};
|
|
34
34
|
__export(index_exports, {
|
|
35
|
+
AKASH_ADAPTER_ID: () => AKASH_ADAPTER_ID,
|
|
36
|
+
AKASH_CAPABILITIES: () => AKASH_CAPABILITIES,
|
|
37
|
+
AKASH_DEMO_ACCEPT_FLOOR: () => AKASH_DEMO_ACCEPT_FLOOR,
|
|
38
|
+
AKASH_DEMO_MARKER: () => AKASH_DEMO_MARKER,
|
|
39
|
+
AdapterContractError: () => AdapterContractError,
|
|
40
|
+
ApiClient: () => ApiClient,
|
|
41
|
+
ENABLED_RAILS: () => ENABLED_RAILS,
|
|
35
42
|
Escro: () => Escro,
|
|
43
|
+
EscroConfirmError: () => EscroConfirmError,
|
|
36
44
|
EscroError: () => EscroError,
|
|
45
|
+
EscroRailNotEnabledError: () => EscroRailNotEnabledError,
|
|
37
46
|
EscrowNotFoundError: () => EscrowNotFoundError,
|
|
47
|
+
EscrowPaymentRouter: () => EscrowPaymentRouter,
|
|
48
|
+
EscrowState: () => EscrowState,
|
|
38
49
|
EscrowStateError: () => EscrowStateError,
|
|
39
50
|
EscrowTimeoutError: () => EscrowTimeoutError,
|
|
40
|
-
|
|
51
|
+
GEN404_ADAPTER_ID: () => GEN404_ADAPTER_ID,
|
|
52
|
+
GEN404_CAPABILITIES: () => GEN404_CAPABILITIES,
|
|
53
|
+
GEN404_DEMO_EVAL_FLOOR: () => GEN404_DEMO_EVAL_FLOOR,
|
|
54
|
+
GEN404_DEMO_MARKER: () => GEN404_DEMO_MARKER,
|
|
55
|
+
GENSYN_ADAPTER_ID: () => GENSYN_ADAPTER_ID,
|
|
56
|
+
GENSYN_CAPABILITIES: () => GENSYN_CAPABILITIES,
|
|
57
|
+
GENSYN_DEMO_MARKER: () => GENSYN_DEMO_MARKER,
|
|
58
|
+
GENSYN_REE_SCHEME: () => GENSYN_REE_SCHEME,
|
|
59
|
+
GRADIENTS_ADAPTER_ID: () => GRADIENTS_ADAPTER_ID,
|
|
60
|
+
GRADIENTS_CAPABILITIES: () => GRADIENTS_CAPABILITIES,
|
|
61
|
+
GRADIENTS_DEMO_EVAL_FLOOR: () => GRADIENTS_DEMO_EVAL_FLOOR,
|
|
62
|
+
GRADIENTS_DEMO_MARKER: () => GRADIENTS_DEMO_MARKER,
|
|
63
|
+
LIUM_ADAPTER_ID: () => LIUM_ADAPTER_ID,
|
|
64
|
+
LIUM_CAPABILITIES: () => LIUM_CAPABILITIES,
|
|
65
|
+
LIUM_DEMO_ACCEPT_FLOOR: () => LIUM_DEMO_ACCEPT_FLOOR,
|
|
66
|
+
LIUM_DEMO_MARKER: () => LIUM_DEMO_MARKER,
|
|
67
|
+
RAILS: () => RAILS,
|
|
68
|
+
RAILS_DOCS_URL: () => RAILS_DOCS_URL,
|
|
69
|
+
REFERENCE_MICRO_USDC_PER_AKT: () => REFERENCE_MICRO_USDC_PER_AKT,
|
|
70
|
+
REFERENCE_MICRO_USDC_PER_RENDER: () => REFERENCE_MICRO_USDC_PER_RENDER,
|
|
71
|
+
RENDER_ADAPTER_ID: () => RENDER_ADAPTER_ID,
|
|
72
|
+
RENDER_CAPABILITIES: () => RENDER_CAPABILITIES,
|
|
73
|
+
RENDER_DEMO_ACCEPT_FLOOR: () => RENDER_DEMO_ACCEPT_FLOOR,
|
|
74
|
+
RENDER_DEMO_MARKER: () => RENDER_DEMO_MARKER,
|
|
75
|
+
RIDGES_ADAPTER_ID: () => RIDGES_ADAPTER_ID,
|
|
76
|
+
RIDGES_CAPABILITIES: () => RIDGES_CAPABILITIES,
|
|
77
|
+
RIDGES_DEMO_COVERAGE_TARGET: () => RIDGES_DEMO_COVERAGE_TARGET,
|
|
78
|
+
RIDGES_DEMO_MARKER: () => RIDGES_DEMO_MARKER,
|
|
79
|
+
ReputationUnavailableError: () => ReputationUnavailableError,
|
|
80
|
+
UnauthorizedError: () => UnauthorizedError,
|
|
81
|
+
ZKML_ADAPTER_ID: () => ZKML_ADAPTER_ID,
|
|
82
|
+
ZKML_CAPABILITIES: () => ZKML_CAPABILITIES,
|
|
83
|
+
ZKML_DEMO_MARKER: () => ZKML_DEMO_MARKER,
|
|
84
|
+
ZKML_PROOF_SCHEME: () => ZKML_PROOF_SCHEME,
|
|
85
|
+
ZKML_PROOF_URI_PREFIX: () => ZKML_PROOF_URI_PREFIX,
|
|
86
|
+
akashDemoJobSpec: () => akashDemoJobSpec,
|
|
87
|
+
akt: () => akt,
|
|
88
|
+
assertServiceAdapterContract: () => assertServiceAdapterContract,
|
|
89
|
+
buildReeProof: () => buildReeProof,
|
|
90
|
+
buildZkmlProof: () => buildZkmlProof,
|
|
91
|
+
computeAkashDemoArtifact: () => computeAkashDemoArtifact,
|
|
92
|
+
computeLiumDemoArtifact: () => computeLiumDemoArtifact,
|
|
93
|
+
computeRenderDemoArtifact: () => computeRenderDemoArtifact,
|
|
94
|
+
create404genAdapter: () => create404genAdapter,
|
|
95
|
+
createAkashAdapter: () => createAkashAdapter,
|
|
96
|
+
createGensynAdapter: () => createGensynAdapter,
|
|
97
|
+
createGradientsAdapter: () => createGradientsAdapter,
|
|
98
|
+
createLiumAdapter: () => createLiumAdapter,
|
|
99
|
+
createRenderAdapter: () => createRenderAdapter,
|
|
100
|
+
createRidgesAdapter: () => createRidgesAdapter,
|
|
101
|
+
createZkmlAdapter: () => createZkmlAdapter,
|
|
102
|
+
decodeZkmlProofUri: () => decodeZkmlProofUri,
|
|
103
|
+
encodeZkmlProofUri: () => encodeZkmlProofUri,
|
|
104
|
+
gen404DemoJobSpec: () => gen404DemoJobSpec,
|
|
105
|
+
gensynAutoRelease: () => gensynAutoRelease,
|
|
106
|
+
gensynDemoJobSpec: () => gensynDemoJobSpec,
|
|
107
|
+
gradientsDemoJobSpec: () => gradientsDemoJobSpec,
|
|
108
|
+
liumDemoJobSpec: () => liumDemoJobSpec,
|
|
109
|
+
renderAmount: () => renderAmount,
|
|
110
|
+
renderDemoJobSpec: () => renderDemoJobSpec,
|
|
111
|
+
reproduceGensynCompute: () => reproduceGensynCompute,
|
|
112
|
+
ridgesDemoJobSpec: () => ridgesDemoJobSpec,
|
|
113
|
+
score404genDemoDeliverable: () => score404genDemoDeliverable,
|
|
114
|
+
scoreAkashDemoDeliverable: () => scoreAkashDemoDeliverable,
|
|
115
|
+
scoreGradientsDemoDeliverable: () => scoreGradientsDemoDeliverable,
|
|
116
|
+
scoreLiumDemoDeliverable: () => scoreLiumDemoDeliverable,
|
|
117
|
+
scoreRenderDemoDeliverable: () => scoreRenderDemoDeliverable,
|
|
118
|
+
scoreRidgesDemoDeliverable: () => scoreRidgesDemoDeliverable,
|
|
119
|
+
serviceResultToSubmitParams: () => serviceResultToSubmitParams,
|
|
120
|
+
solveAkashDemo: () => solveAkashDemo,
|
|
121
|
+
solveGen404Demo: () => solveGen404Demo,
|
|
122
|
+
solveGradientsDemo: () => solveGradientsDemo,
|
|
123
|
+
solveLiumDemo: () => solveLiumDemo,
|
|
124
|
+
solveRenderDemo: () => solveRenderDemo,
|
|
125
|
+
solveRidgesDemo: () => solveRidgesDemo,
|
|
126
|
+
solveZkmlDemo: () => solveZkmlDemo,
|
|
127
|
+
tao: () => tao,
|
|
128
|
+
verifyReeProof: () => verifyReeProof,
|
|
129
|
+
verifyZkmlProof: () => verifyZkmlProof,
|
|
130
|
+
verifyZkmlProofUri: () => verifyZkmlProofUri,
|
|
131
|
+
zkmlDemoJobSpec: () => zkmlDemoJobSpec,
|
|
132
|
+
zkmlProofAutoRelease: () => zkmlProofAutoRelease
|
|
41
133
|
});
|
|
42
134
|
module.exports = __toCommonJS(index_exports);
|
|
43
135
|
|
|
44
136
|
// src/client.ts
|
|
45
137
|
var import_web32 = require("@solana/web3.js");
|
|
46
138
|
|
|
47
|
-
//
|
|
139
|
+
// ../shared/dist/index.js
|
|
140
|
+
var import_crypto = require("crypto");
|
|
48
141
|
var import_tweetnacl = __toESM(require("tweetnacl"), 1);
|
|
49
142
|
var import_bs58 = __toESM(require("bs58"), 1);
|
|
143
|
+
var import_tweetnacl2 = __toESM(require("tweetnacl"), 1);
|
|
144
|
+
var import_bs582 = __toESM(require("bs58"), 1);
|
|
145
|
+
var EscrowState = /* @__PURE__ */ ((EscrowState2) => {
|
|
146
|
+
EscrowState2["CREATED"] = "CREATED";
|
|
147
|
+
EscrowState2["FUNDED"] = "FUNDED";
|
|
148
|
+
EscrowState2["IN_PROGRESS"] = "IN_PROGRESS";
|
|
149
|
+
EscrowState2["SUBMITTED"] = "SUBMITTED";
|
|
150
|
+
EscrowState2["COMPLETED"] = "COMPLETED";
|
|
151
|
+
EscrowState2["CANCELLED"] = "CANCELLED";
|
|
152
|
+
EscrowState2["DISPUTED"] = "DISPUTED";
|
|
153
|
+
EscrowState2["REFUNDED"] = "REFUNDED";
|
|
154
|
+
return EscrowState2;
|
|
155
|
+
})(EscrowState || {});
|
|
156
|
+
function canonicalize(value) {
|
|
157
|
+
if (value === null) return "null";
|
|
158
|
+
if (Array.isArray(value)) {
|
|
159
|
+
return "[" + value.map((v) => v === void 0 ? "null" : canonicalize(v)).join(",") + "]";
|
|
160
|
+
}
|
|
161
|
+
if (typeof value === "object") {
|
|
162
|
+
const obj = value;
|
|
163
|
+
const keys = Object.keys(obj).filter((k) => obj[k] !== void 0).sort();
|
|
164
|
+
return "{" + keys.map((k) => JSON.stringify(k) + ":" + canonicalize(obj[k])).join(",") + "}";
|
|
165
|
+
}
|
|
166
|
+
return JSON.stringify(value);
|
|
167
|
+
}
|
|
168
|
+
var PROGRAM_ID_DEVNET = "EjLpJ3obUYk1f2KDensU7UdGBZTxSdxufm7CSnMA3H5C";
|
|
169
|
+
var PROGRAM_ID_MAINNET = "4CwLNRF4mFXutiBGjtMHQBxh8whW8T8EyvJXdRhY1rvr";
|
|
170
|
+
var ONCHAIN_REFUND_GRACE_SECONDS = 900;
|
|
171
|
+
var ONCHAIN_TIMEOUT_SECONDS = 86400;
|
|
172
|
+
var CLOCK_SKEW_SECONDS = 60;
|
|
173
|
+
var SCANNER_REFUND_GRACE_SECONDS = ONCHAIN_REFUND_GRACE_SECONDS + CLOCK_SKEW_SECONDS;
|
|
174
|
+
var SCANNER_TIMEOUT_SECONDS = ONCHAIN_TIMEOUT_SECONDS + CLOCK_SKEW_SECONDS;
|
|
175
|
+
var MAINNET_PROGRAM_ID_PLACEHOLDER = "11111111111111111111111111111111";
|
|
176
|
+
function getProgramId(cluster) {
|
|
177
|
+
return cluster === "mainnet" ? PROGRAM_ID_MAINNET : PROGRAM_ID_DEVNET;
|
|
178
|
+
}
|
|
179
|
+
function isPlaceholderProgramId(id) {
|
|
180
|
+
return id === MAINNET_PROGRAM_ID_PLACEHOLDER;
|
|
181
|
+
}
|
|
182
|
+
function assertProgramIdNotPlaceholder(id, cluster) {
|
|
183
|
+
if (cluster === "mainnet" && isPlaceholderProgramId(id)) {
|
|
184
|
+
throw new Error(
|
|
185
|
+
`PROGRAM_ID_MAINNET is still the System Program placeholder (${MAINNET_PROGRAM_ID_PLACEHOLDER}). Run scripts/deploy-mainnet.sh to bootstrap mainnet, then update the placeholder in packages/shared/src/config.ts, packages/program/programs/escro/src/lib.rs, packages/program/Anchor.toml, and infra/bin/escro.ts.`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function hashBody(rawBody) {
|
|
190
|
+
return (0, import_crypto.createHash)("sha256").update(rawBody, "utf8").digest("hex");
|
|
191
|
+
}
|
|
192
|
+
function buildSignedMessage(timestamp, method, fullPath, bodyHash) {
|
|
193
|
+
return `escro:${String(timestamp)}:${method}:${fullPath}:${bodyHash}`;
|
|
194
|
+
}
|
|
195
|
+
function signRequestRaw(secretKey, walletAddress, method, fullPath, rawBody) {
|
|
196
|
+
const timestamp = Date.now();
|
|
197
|
+
const bodyHash = hashBody(rawBody);
|
|
198
|
+
const message = buildSignedMessage(timestamp, method, fullPath, bodyHash);
|
|
199
|
+
const sigBytes = import_tweetnacl.default.sign.detached(new TextEncoder().encode(message), secretKey);
|
|
200
|
+
return {
|
|
201
|
+
"x-wallet-address": walletAddress,
|
|
202
|
+
"x-signature": import_bs58.default.encode(sigBytes),
|
|
203
|
+
"x-timestamp": String(timestamp),
|
|
204
|
+
"x-content-sha256": bodyHash
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function signRequest(wallet, method, fullPath, rawBody) {
|
|
208
|
+
return signRequestRaw(wallet.secretKey, wallet.publicKey.toBase58(), method, fullPath, rawBody);
|
|
209
|
+
}
|
|
210
|
+
function canonicalBotDescriptorMessage(descriptor) {
|
|
211
|
+
return canonicalize({
|
|
212
|
+
wallet: descriptor.wallet,
|
|
213
|
+
role: descriptor.role,
|
|
214
|
+
capabilities: descriptor.capabilities,
|
|
215
|
+
serviceRef: descriptor.serviceRef,
|
|
216
|
+
pricingHint: descriptor.pricingHint,
|
|
217
|
+
endpoint: descriptor.endpoint,
|
|
218
|
+
signedAt: descriptor.signedAt
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
function verifyBotDescriptorSignature(descriptor) {
|
|
222
|
+
try {
|
|
223
|
+
const { signature, ...unsigned } = descriptor;
|
|
224
|
+
const message = new TextEncoder().encode(canonicalBotDescriptorMessage(unsigned));
|
|
225
|
+
const signatureBytes = import_bs582.default.decode(signature);
|
|
226
|
+
if (signatureBytes.length !== 64) return false;
|
|
227
|
+
const pubkeyBytes = import_bs582.default.decode(descriptor.wallet);
|
|
228
|
+
if (pubkeyBytes.length !== 32) return false;
|
|
229
|
+
return import_tweetnacl2.default.sign.detached.verify(message, signatureBytes, pubkeyBytes);
|
|
230
|
+
} catch {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function spec(discriminator, escrowAccountIndex, cause) {
|
|
235
|
+
return {
|
|
236
|
+
discriminator: Uint8Array.from(discriminator),
|
|
237
|
+
escrowAccountIndex,
|
|
238
|
+
signerAccountIndex: 0,
|
|
239
|
+
cause
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
var ESCROW_INSTRUCTIONS = {
|
|
243
|
+
create_escrow: spec([253, 215, 165, 116, 36, 108, 68, 80], 4, null),
|
|
244
|
+
claim_task: spec([49, 222, 219, 238, 155, 68, 221, 136], 1, null),
|
|
245
|
+
submit_deliverable: spec([38, 137, 64, 44, 237, 11, 125, 101], 1, null),
|
|
246
|
+
raise_dispute: spec([41, 243, 1, 51, 150, 95, 246, 73], 1, null),
|
|
247
|
+
cancel_escrow: spec([156, 203, 54, 179, 38, 72, 33, 21], 3, "CANCELLED"),
|
|
248
|
+
release_payment: spec([24, 34, 191, 86, 145, 160, 183, 233], 3, "EXPLICIT_RELEASE"),
|
|
249
|
+
timeout_release: spec([98, 176, 26, 12, 83, 105, 154, 68], 3, "TIMEOUT_RELEASE"),
|
|
250
|
+
refund_buyer: spec([199, 139, 203, 146, 192, 150, 53, 218], 3, "DEADLINE_REFUND"),
|
|
251
|
+
arbitration_release: spec([118, 157, 141, 26, 104, 117, 94, 229], 4, "ARBITRATION_RELEASE"),
|
|
252
|
+
arbitration_refund: spec([255, 194, 84, 248, 185, 249, 120, 122], 4, "ARBITRATION_REFUND")
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// src/payments.ts
|
|
256
|
+
var RAILS = ["escrow", "x402"];
|
|
257
|
+
var ENABLED_RAILS = ["escrow"];
|
|
258
|
+
var RAILS_DOCS_URL = "https://docs.escro.ai/rails";
|
|
259
|
+
var EscrowPaymentRouter = class {
|
|
260
|
+
selectRail(tx) {
|
|
261
|
+
const preferred = tx.mode === "sync" ? "x402" : "escrow";
|
|
262
|
+
return ENABLED_RAILS.includes(preferred) ? preferred : "escrow";
|
|
263
|
+
}
|
|
264
|
+
};
|
|
50
265
|
|
|
51
266
|
// src/errors.ts
|
|
52
267
|
var EscroError = class extends Error {
|
|
53
|
-
constructor(message, code = "ESCRO_ERROR") {
|
|
268
|
+
constructor(message, code = "ESCRO_ERROR", retryAfterMs2) {
|
|
54
269
|
super(message);
|
|
55
|
-
this
|
|
270
|
+
__publicField(this, "code", code);
|
|
271
|
+
__publicField(this, "retryAfterMs", retryAfterMs2);
|
|
56
272
|
this.name = "EscroError";
|
|
57
273
|
}
|
|
58
274
|
};
|
|
@@ -62,6 +278,15 @@ var EscrowNotFoundError = class extends EscroError {
|
|
|
62
278
|
this.name = "EscrowNotFoundError";
|
|
63
279
|
}
|
|
64
280
|
};
|
|
281
|
+
var ReputationUnavailableError = class extends EscroError {
|
|
282
|
+
constructor() {
|
|
283
|
+
super(
|
|
284
|
+
"Reputation endpoint is unavailable (HTTP 404): this deployment has no reputation source mounted.",
|
|
285
|
+
"REPUTATION_UNAVAILABLE"
|
|
286
|
+
);
|
|
287
|
+
this.name = "ReputationUnavailableError";
|
|
288
|
+
}
|
|
289
|
+
};
|
|
65
290
|
var EscrowStateError = class extends EscroError {
|
|
66
291
|
constructor(message) {
|
|
67
292
|
super(message, "ESCROW_INVALID_STATE");
|
|
@@ -74,40 +299,58 @@ var EscrowTimeoutError = class extends EscroError {
|
|
|
74
299
|
this.name = "EscrowTimeoutError";
|
|
75
300
|
}
|
|
76
301
|
};
|
|
302
|
+
var EscroConfirmError = class extends EscroError {
|
|
303
|
+
constructor(address, signature, code, message) {
|
|
304
|
+
super(`Confirm failed for escrow ${address} (signature ${signature}): ${message}`, code);
|
|
305
|
+
__publicField(this, "address", address);
|
|
306
|
+
__publicField(this, "signature", signature);
|
|
307
|
+
this.name = "EscroConfirmError";
|
|
308
|
+
}
|
|
309
|
+
};
|
|
77
310
|
var UnauthorizedError = class extends EscroError {
|
|
78
311
|
constructor(message = "Unauthorized") {
|
|
79
312
|
super(message, "UNAUTHORIZED");
|
|
80
313
|
this.name = "UnauthorizedError";
|
|
81
314
|
}
|
|
82
315
|
};
|
|
316
|
+
var EscroRailNotEnabledError = class extends EscroError {
|
|
317
|
+
constructor(rail) {
|
|
318
|
+
super(
|
|
319
|
+
`Payment rail "${rail}" is not enabled. Only the escrow rail is available today; the x402 rail is reserved and lands in Phase 9. See ${RAILS_DOCS_URL}`,
|
|
320
|
+
"ESCRO_RAIL_NOT_ENABLED"
|
|
321
|
+
);
|
|
322
|
+
__publicField(this, "rail", rail);
|
|
323
|
+
this.name = "EscroRailNotEnabledError";
|
|
324
|
+
}
|
|
325
|
+
};
|
|
83
326
|
|
|
84
327
|
// src/api.ts
|
|
85
|
-
function authHeaders(wallet, method, path) {
|
|
86
|
-
|
|
87
|
-
const message = `escro:${timestamp}:${method}:${path}`;
|
|
88
|
-
const msgBytes = new TextEncoder().encode(message);
|
|
89
|
-
const sigBytes = import_tweetnacl.default.sign.detached(msgBytes, wallet.secretKey);
|
|
90
|
-
return {
|
|
91
|
-
"x-wallet-address": wallet.publicKey.toBase58(),
|
|
92
|
-
"x-signature": import_bs58.default.encode(sigBytes),
|
|
93
|
-
"x-timestamp": String(timestamp)
|
|
94
|
-
};
|
|
328
|
+
function authHeaders(wallet, method, path, rawBody = "") {
|
|
329
|
+
return signRequest(wallet, method, path, rawBody);
|
|
95
330
|
}
|
|
96
331
|
function deserialize(raw) {
|
|
97
332
|
return { ...raw, amount: BigInt(raw.amount) };
|
|
98
333
|
}
|
|
99
334
|
async function handleError(res, address) {
|
|
100
335
|
const body = await res.json().catch(() => ({}));
|
|
101
|
-
const msg = body.error ?? `HTTP ${res.status}`;
|
|
336
|
+
const msg = body.error ?? `HTTP ${String(res.status)}`;
|
|
102
337
|
if (res.status === 404) throw new EscrowNotFoundError(address ?? msg);
|
|
103
338
|
if (res.status === 409) throw new EscrowStateError(msg);
|
|
104
339
|
if (res.status === 401 || res.status === 403) throw new UnauthorizedError(msg);
|
|
105
|
-
throw new EscroError(msg, body.code ?? "UNKNOWN");
|
|
340
|
+
throw new EscroError(msg, body.code ?? "UNKNOWN", retryAfterMs(res.headers.get("retry-after")));
|
|
341
|
+
}
|
|
342
|
+
function retryAfterMs(header) {
|
|
343
|
+
return header !== null && /^\d+$/.test(header.trim()) ? Number(header.trim()) * 1e3 : void 0;
|
|
106
344
|
}
|
|
107
345
|
var ApiClient = class {
|
|
108
346
|
constructor(baseUrl, wallet) {
|
|
109
|
-
this
|
|
110
|
-
this
|
|
347
|
+
__publicField(this, "baseUrl", baseUrl);
|
|
348
|
+
__publicField(this, "wallet", wallet);
|
|
349
|
+
const url = new URL(baseUrl);
|
|
350
|
+
const isLocalhost = url.hostname === "localhost" || url.hostname === "127.0.0.1";
|
|
351
|
+
if (url.protocol !== "https:" && !isLocalhost) {
|
|
352
|
+
throw new Error("apiUrl must use HTTPS in production");
|
|
353
|
+
}
|
|
111
354
|
}
|
|
112
355
|
url(path) {
|
|
113
356
|
return `${this.baseUrl}${path}`;
|
|
@@ -119,10 +362,11 @@ var ApiClient = class {
|
|
|
119
362
|
if (!res.ok) await handleError(res, address);
|
|
120
363
|
return deserialize(await res.json());
|
|
121
364
|
}
|
|
122
|
-
/** GET /v1/escrows?assignedTo=...&state=... */
|
|
365
|
+
/** GET /v1/escrows?assignedTo=...&maker=...&state=... */
|
|
123
366
|
async listEscrows(params) {
|
|
124
367
|
const qs = new URLSearchParams();
|
|
125
368
|
if (params.assignedTo) qs.set("assignedTo", params.assignedTo);
|
|
369
|
+
if (params.maker) qs.set("maker", params.maker);
|
|
126
370
|
if (params.state) qs.set("state", params.state);
|
|
127
371
|
if (params.limit != null) qs.set("limit", String(params.limit));
|
|
128
372
|
if (params.offset != null) qs.set("offset", String(params.offset));
|
|
@@ -137,21 +381,77 @@ var ApiClient = class {
|
|
|
137
381
|
offset: body.offset
|
|
138
382
|
};
|
|
139
383
|
}
|
|
384
|
+
/**
|
|
385
|
+
* GET /v1/bots?capability=...&role=...&limit=...&offset= : EscroBots registry
|
|
386
|
+
* read. Unauthenticated public read (no auth headers). Returns descriptors in
|
|
387
|
+
* registry order (newest first); descriptors are self-reported claims.
|
|
388
|
+
*/
|
|
389
|
+
async listBots(params) {
|
|
390
|
+
const qs = new URLSearchParams();
|
|
391
|
+
if (params.capability != null) qs.set("capability", params.capability);
|
|
392
|
+
if (params.role != null) qs.set("role", params.role);
|
|
393
|
+
if (params.limit != null) qs.set("limit", String(params.limit));
|
|
394
|
+
if (params.offset != null) qs.set("offset", String(params.offset));
|
|
395
|
+
const path = `/v1/bots?${qs.toString()}`;
|
|
396
|
+
const res = await fetch(this.url(path));
|
|
397
|
+
if (!res.ok) await handleError(res);
|
|
398
|
+
const body = await res.json();
|
|
399
|
+
return { items: body.items, total: body.total, limit: body.limit, offset: body.offset };
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* GET /rep : EscroRep bulk export (ARCHITECTURE §9). Unauthenticated public
|
|
403
|
+
* read (no auth headers). Returns every indexed wallet's RAW components exactly
|
|
404
|
+
* once (no composite score). Ranking layers join against this ONE bulk fetch:
|
|
405
|
+
* the per-wallet endpoint shares a 10/min public-metrics budget, so a
|
|
406
|
+
* per-candidate fan-out would rate-limit itself. A 404 means the deployment
|
|
407
|
+
* mounted no reputation source (unknown wallets return zeroed rep, never 404),
|
|
408
|
+
* so it surfaces as {@link ReputationUnavailableError}, not EscrowNotFoundError.
|
|
409
|
+
*/
|
|
410
|
+
async listReputations() {
|
|
411
|
+
const res = await fetch(this.url("/rep"));
|
|
412
|
+
if (!res.ok) {
|
|
413
|
+
if (res.status === 404) throw new ReputationUnavailableError();
|
|
414
|
+
await handleError(res);
|
|
415
|
+
}
|
|
416
|
+
const body = await res.json();
|
|
417
|
+
return body.wallets;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* GET /rep/:wallet : one wallet's EscroRep RAW components (ARCHITECTURE §9).
|
|
421
|
+
* Unauthenticated public read (no auth headers). Unknown wallets return zeroed
|
|
422
|
+
* components, never 404; a 404 means the deployment mounted no reputation
|
|
423
|
+
* source and surfaces as {@link ReputationUnavailableError}. Shares the public
|
|
424
|
+
* per-wallet rate budget with every other caller, so fan-outs should use
|
|
425
|
+
* {@link listReputations} instead.
|
|
426
|
+
*/
|
|
427
|
+
async getReputation(wallet) {
|
|
428
|
+
const res = await fetch(this.url(`/rep/${encodeURIComponent(wallet)}`));
|
|
429
|
+
if (!res.ok) {
|
|
430
|
+
if (res.status === 404) throw new ReputationUnavailableError();
|
|
431
|
+
await handleError(res);
|
|
432
|
+
}
|
|
433
|
+
return await res.json();
|
|
434
|
+
}
|
|
140
435
|
/**
|
|
141
436
|
* POST /v1/escrows — create escrow, returns unsigned tx + ids.
|
|
142
437
|
* Requires auth.
|
|
143
438
|
*/
|
|
144
439
|
async createEscrow(params) {
|
|
145
440
|
const path = "/v1/escrows";
|
|
441
|
+
const bodyObj = {
|
|
442
|
+
taskSpec: params.taskSpec,
|
|
443
|
+
amountUsdc: params.amountUsdc,
|
|
444
|
+
deadlineUnix: params.deadlineUnix,
|
|
445
|
+
assignedWorker: params.assignedWorker
|
|
446
|
+
};
|
|
447
|
+
const rawBody = JSON.stringify(bodyObj);
|
|
146
448
|
const res = await fetch(this.url(path), {
|
|
147
449
|
method: "POST",
|
|
148
|
-
headers: {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
assignedWorker: params.assignedWorker
|
|
154
|
-
})
|
|
450
|
+
headers: {
|
|
451
|
+
...authHeaders(this.wallet, "POST", path, rawBody),
|
|
452
|
+
"content-type": "application/json"
|
|
453
|
+
},
|
|
454
|
+
body: rawBody
|
|
155
455
|
});
|
|
156
456
|
if (!res.ok) await handleError(res);
|
|
157
457
|
return res.json();
|
|
@@ -175,13 +475,17 @@ var ApiClient = class {
|
|
|
175
475
|
*/
|
|
176
476
|
async submitDeliverable(address, params) {
|
|
177
477
|
const path = `/v1/escrows/${address}/submit`;
|
|
478
|
+
const rawBody = JSON.stringify({
|
|
479
|
+
contentHash: params.contentHash,
|
|
480
|
+
proofUri: params.proofUri
|
|
481
|
+
});
|
|
178
482
|
const res = await fetch(this.url(path), {
|
|
179
483
|
method: "POST",
|
|
180
|
-
headers: {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
484
|
+
headers: {
|
|
485
|
+
...authHeaders(this.wallet, "POST", path, rawBody),
|
|
486
|
+
"content-type": "application/json"
|
|
487
|
+
},
|
|
488
|
+
body: rawBody
|
|
185
489
|
});
|
|
186
490
|
if (!res.ok) await handleError(res, address);
|
|
187
491
|
return res.json();
|
|
@@ -217,14 +521,58 @@ var ApiClient = class {
|
|
|
217
521
|
*/
|
|
218
522
|
async raiseDispute(address, params) {
|
|
219
523
|
const path = `/v1/escrows/${address}/dispute`;
|
|
524
|
+
const rawBody = JSON.stringify({ reason: params.reason });
|
|
220
525
|
const res = await fetch(this.url(path), {
|
|
221
526
|
method: "POST",
|
|
222
|
-
headers: {
|
|
223
|
-
|
|
527
|
+
headers: {
|
|
528
|
+
...authHeaders(this.wallet, "POST", path, rawBody),
|
|
529
|
+
"content-type": "application/json"
|
|
530
|
+
},
|
|
531
|
+
body: rawBody
|
|
224
532
|
});
|
|
225
533
|
if (!res.ok) await handleError(res, address);
|
|
226
534
|
return res.json();
|
|
227
535
|
}
|
|
536
|
+
/**
|
|
537
|
+
* POST /v1/escrows/:address/confirm: the API records the create, claim, submit,
|
|
538
|
+
* cancel or dispute the landed `signature` carries, after verifying it on chain.
|
|
539
|
+
* A non-2xx answer throws an {@link EscroError} with the server's code, or
|
|
540
|
+
* `HTTP_<status>` when the answer carries none; a network failure rejects with
|
|
541
|
+
* fetch's own error. {@link Escro.confirm} decides which to retry.
|
|
542
|
+
*/
|
|
543
|
+
async confirm(address, signature, evidence) {
|
|
544
|
+
const path = `/v1/escrows/${address}/confirm`;
|
|
545
|
+
const rawBody = JSON.stringify({ signature, evidence });
|
|
546
|
+
const res = await fetch(this.url(path), {
|
|
547
|
+
method: "POST",
|
|
548
|
+
headers: {
|
|
549
|
+
...authHeaders(this.wallet, "POST", path, rawBody),
|
|
550
|
+
"content-type": "application/json"
|
|
551
|
+
},
|
|
552
|
+
body: rawBody
|
|
553
|
+
});
|
|
554
|
+
if (!res.ok) {
|
|
555
|
+
const body2 = await res.json().catch(() => ({}));
|
|
556
|
+
throw new EscroError(
|
|
557
|
+
body2.error ?? `HTTP ${String(res.status)}`,
|
|
558
|
+
body2.code ?? `HTTP_${String(res.status)}`,
|
|
559
|
+
retryAfterMs(res.headers.get("retry-after"))
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
const body = await res.json();
|
|
563
|
+
return deserialize(body.escrow);
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* GET /v1/stats — public protocol-wide stats.
|
|
567
|
+
* No auth required. Upstream typing in {@link Escro.getStats} narrows the
|
|
568
|
+
* return shape.
|
|
569
|
+
*/
|
|
570
|
+
async getStats() {
|
|
571
|
+
const path = "/v1/stats";
|
|
572
|
+
const res = await fetch(this.url(path));
|
|
573
|
+
if (!res.ok) await handleError(res);
|
|
574
|
+
return res.json();
|
|
575
|
+
}
|
|
228
576
|
};
|
|
229
577
|
|
|
230
578
|
// src/poll.ts
|
|
@@ -241,7 +589,7 @@ function sleep(ms) {
|
|
|
241
589
|
}
|
|
242
590
|
async function pollUntil(address, fetcher, predicate, timeoutMs = MS_24_H) {
|
|
243
591
|
const deadline = Date.now() + timeoutMs;
|
|
244
|
-
|
|
592
|
+
for (; ; ) {
|
|
245
593
|
const result = await fetcher();
|
|
246
594
|
if (predicate(result)) return result;
|
|
247
595
|
const elapsed = Date.now() - (deadline - timeoutMs);
|
|
@@ -262,38 +610,108 @@ var import_node_crypto = require("crypto");
|
|
|
262
610
|
var import_web3 = require("@solana/web3.js");
|
|
263
611
|
var import_spl_token = require("@solana/spl-token");
|
|
264
612
|
function disc(name) {
|
|
265
|
-
return Buffer.from(
|
|
266
|
-
(0, import_node_crypto.createHash)("sha256").update(`global:${name}`).digest()
|
|
267
|
-
).subarray(0, 8);
|
|
613
|
+
return Buffer.from((0, import_node_crypto.createHash)("sha256").update(`global:${name}`).digest()).subarray(0, 8);
|
|
268
614
|
}
|
|
269
615
|
var DISC_RELEASE_PAYMENT = disc("release_payment");
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
616
|
+
function resolveProgramId(cluster) {
|
|
617
|
+
const id = getProgramId(cluster);
|
|
618
|
+
assertProgramIdNotPlaceholder(id, cluster);
|
|
619
|
+
return new import_web3.PublicKey(id);
|
|
620
|
+
}
|
|
621
|
+
function allowedProgramIds(programId) {
|
|
622
|
+
return /* @__PURE__ */ new Set([
|
|
623
|
+
programId.toBase58(),
|
|
624
|
+
import_spl_token.TOKEN_PROGRAM_ID.toBase58(),
|
|
625
|
+
import_spl_token.ASSOCIATED_TOKEN_PROGRAM_ID.toBase58(),
|
|
626
|
+
import_web3.ComputeBudgetProgram.programId.toBase58()
|
|
627
|
+
]);
|
|
628
|
+
}
|
|
629
|
+
var MAX_INSTRUCTIONS = 6;
|
|
630
|
+
var FORBIDDEN_TOKEN_IX_TAGS = /* @__PURE__ */ new Set([
|
|
631
|
+
3,
|
|
632
|
+
// Transfer
|
|
633
|
+
4,
|
|
634
|
+
// Approve
|
|
635
|
+
6,
|
|
636
|
+
// SetAuthority
|
|
637
|
+
7,
|
|
638
|
+
// MintTo
|
|
639
|
+
8,
|
|
640
|
+
// Burn
|
|
641
|
+
9,
|
|
642
|
+
// CloseAccount
|
|
643
|
+
12,
|
|
644
|
+
// TransferChecked
|
|
645
|
+
13,
|
|
646
|
+
// ApproveChecked
|
|
647
|
+
14,
|
|
648
|
+
// MintToChecked
|
|
649
|
+
15
|
|
650
|
+
// BurnChecked
|
|
651
|
+
]);
|
|
652
|
+
var EXPECTED_PLATFORM_CONFIG_VERSION = 1;
|
|
653
|
+
var VERSION_OFFSET = 8;
|
|
654
|
+
var MINT_OFFSET = 43;
|
|
655
|
+
var TREASURY_OFFSET = 75;
|
|
656
|
+
async function fetchPlatformConfig(connection, cluster) {
|
|
657
|
+
const programId = resolveProgramId(cluster);
|
|
658
|
+
const [pda] = import_web3.PublicKey.findProgramAddressSync([Buffer.from("config")], programId);
|
|
273
659
|
const info = await connection.getAccountInfo(pda);
|
|
274
660
|
if (!info) throw new Error("PlatformConfig account not found on-chain");
|
|
661
|
+
const version = info.data[VERSION_OFFSET];
|
|
662
|
+
if (version !== EXPECTED_PLATFORM_CONFIG_VERSION) {
|
|
663
|
+
throw new Error(
|
|
664
|
+
`PlatformConfig version mismatch: expected ${String(EXPECTED_PLATFORM_CONFIG_VERSION)}, got ${String(version)}`
|
|
665
|
+
);
|
|
666
|
+
}
|
|
275
667
|
return {
|
|
276
|
-
mint: new import_web3.PublicKey(info.data.
|
|
277
|
-
treasury: new import_web3.PublicKey(info.data.
|
|
668
|
+
mint: new import_web3.PublicKey(info.data.subarray(MINT_OFFSET, MINT_OFFSET + 32)),
|
|
669
|
+
treasury: new import_web3.PublicKey(info.data.subarray(TREASURY_OFFSET, TREASURY_OFFSET + 32))
|
|
278
670
|
};
|
|
279
671
|
}
|
|
280
|
-
|
|
672
|
+
function validateTransaction(tx, cluster) {
|
|
673
|
+
const programId = resolveProgramId(cluster);
|
|
674
|
+
const allowed = allowedProgramIds(programId);
|
|
675
|
+
const tokenProgram = import_spl_token.TOKEN_PROGRAM_ID.toBase58();
|
|
676
|
+
for (const ix of tx.instructions) {
|
|
677
|
+
const id = ix.programId.toBase58();
|
|
678
|
+
if (!allowed.has(id)) {
|
|
679
|
+
throw new Error(
|
|
680
|
+
`Unexpected program in transaction: ${id}. Allowed: ${[...allowed].join(", ")}`
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
if (id === tokenProgram) {
|
|
684
|
+
const tag = ix.data.length > 0 ? ix.data[0] : void 0;
|
|
685
|
+
if (tag !== void 0 && FORBIDDEN_TOKEN_IX_TAGS.has(tag)) {
|
|
686
|
+
throw new Error(
|
|
687
|
+
`Refusing to sign a fund-moving SPL Token instruction (tag ${String(tag)}) in a server-built transaction. Escro never requires the wallet to directly sign a token transfer/approve/burn/close; all token movement is a CPI inside the escro program.`
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
if (tx.instructions.length > MAX_INSTRUCTIONS) {
|
|
693
|
+
throw new Error(
|
|
694
|
+
`Too many instructions (${String(tx.instructions.length)}). Max allowed: ${String(MAX_INSTRUCTIONS)}`
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
async function signAndSend(base64Tx, wallet, connection, cluster) {
|
|
281
699
|
const buf = Buffer.from(base64Tx, "base64");
|
|
282
700
|
const tx = import_web3.Transaction.from(buf);
|
|
701
|
+
validateTransaction(tx, cluster);
|
|
283
702
|
tx.partialSign(wallet);
|
|
284
703
|
const raw = tx.serialize();
|
|
704
|
+
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash("confirmed");
|
|
285
705
|
const signature = await connection.sendRawTransaction(raw, {
|
|
286
706
|
skipPreflight: false
|
|
287
707
|
});
|
|
288
|
-
await connection.confirmTransaction(signature, "confirmed");
|
|
708
|
+
await connection.confirmTransaction({ signature, blockhash, lastValidBlockHeight }, "confirmed");
|
|
289
709
|
return signature;
|
|
290
710
|
}
|
|
291
711
|
async function buildReleasePaymentTx(params) {
|
|
292
|
-
const { buyer, escrowPda, worker, mint, treasury, connection } = params;
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
PROGRAM_ID
|
|
296
|
-
);
|
|
712
|
+
const { buyer, escrowPda, worker, mint, treasury, connection, cluster } = params;
|
|
713
|
+
const programId = resolveProgramId(cluster);
|
|
714
|
+
const [platformConfig] = import_web3.PublicKey.findProgramAddressSync([Buffer.from("config")], programId);
|
|
297
715
|
const vault = (0, import_spl_token.getAssociatedTokenAddressSync)(
|
|
298
716
|
mint,
|
|
299
717
|
escrowPda,
|
|
@@ -311,16 +729,12 @@ async function buildReleasePaymentTx(params) {
|
|
|
311
729
|
const treasuryAta = (0, import_spl_token.getAssociatedTokenAddressSync)(
|
|
312
730
|
mint,
|
|
313
731
|
treasury,
|
|
314
|
-
|
|
732
|
+
true,
|
|
315
733
|
import_spl_token.TOKEN_PROGRAM_ID,
|
|
316
734
|
import_spl_token.ASSOCIATED_TOKEN_PROGRAM_ID
|
|
317
735
|
);
|
|
318
|
-
const [workerAtaInfo, treasuryAtaInfo] = await connection.getMultipleAccountsInfo([
|
|
319
|
-
workerAta,
|
|
320
|
-
treasuryAta
|
|
321
|
-
]);
|
|
322
736
|
const ix = new import_web3.TransactionInstruction({
|
|
323
|
-
programId
|
|
737
|
+
programId,
|
|
324
738
|
keys: [
|
|
325
739
|
{ pubkey: buyer, isSigner: true, isWritable: true },
|
|
326
740
|
{ pubkey: platformConfig, isSigner: false, isWritable: false },
|
|
@@ -340,45 +754,67 @@ async function buildReleasePaymentTx(params) {
|
|
|
340
754
|
const tx = new import_web3.Transaction();
|
|
341
755
|
tx.recentBlockhash = blockhash;
|
|
342
756
|
tx.feePayer = buyer;
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
(0, import_spl_token.createAssociatedTokenAccountInstruction)(buyer, workerAta, worker, mint)
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
|
-
if (!treasuryAtaInfo) {
|
|
349
|
-
tx.add(
|
|
350
|
-
(0, import_spl_token.createAssociatedTokenAccountInstruction)(buyer, treasuryAta, treasury, mint)
|
|
351
|
-
);
|
|
352
|
-
}
|
|
757
|
+
tx.add((0, import_spl_token.createAssociatedTokenAccountIdempotentInstruction)(buyer, workerAta, worker, mint));
|
|
758
|
+
tx.add((0, import_spl_token.createAssociatedTokenAccountIdempotentInstruction)(buyer, treasuryAta, treasury, mint));
|
|
353
759
|
tx.add(ix);
|
|
354
760
|
return tx.serialize({ requireAllSignatures: false }).toString("base64");
|
|
355
761
|
}
|
|
356
762
|
|
|
357
763
|
// src/client.ts
|
|
764
|
+
var CONFIRM_WINDOW_MS = 3e4;
|
|
765
|
+
var CONFIRM_FIRST_DELAY_MS = 500;
|
|
766
|
+
var CONFIRM_MAX_DELAY_MS = 4e3;
|
|
767
|
+
var RETRYABLE_CONFIRM_CODES = [
|
|
768
|
+
"TX_NOT_FOUND",
|
|
769
|
+
"RPC_UNAVAILABLE",
|
|
770
|
+
"HTTP_502",
|
|
771
|
+
"HTTP_503",
|
|
772
|
+
"HTTP_504",
|
|
773
|
+
"RATE_LIMITED",
|
|
774
|
+
"HTTP_429"
|
|
775
|
+
];
|
|
776
|
+
function isRetryableConfirmCode(code) {
|
|
777
|
+
return RETRYABLE_CONFIRM_CODES.includes(code);
|
|
778
|
+
}
|
|
779
|
+
function isUrl(value) {
|
|
780
|
+
try {
|
|
781
|
+
new URL(value);
|
|
782
|
+
return true;
|
|
783
|
+
} catch {
|
|
784
|
+
return false;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
358
788
|
var Escro = class {
|
|
359
789
|
constructor(options) {
|
|
360
790
|
__publicField(this, "connection");
|
|
361
791
|
__publicField(this, "api");
|
|
362
792
|
__publicField(this, "options");
|
|
793
|
+
__publicField(this, "cluster");
|
|
794
|
+
__publicField(this, "router", new EscrowPaymentRouter());
|
|
363
795
|
this.options = options;
|
|
796
|
+
this.cluster = options.cluster;
|
|
364
797
|
this.connection = new import_web32.Connection(options.rpcUrl, "confirmed");
|
|
365
798
|
this.api = new ApiClient(options.apiUrl, options.wallet);
|
|
366
799
|
}
|
|
367
800
|
// ── Buyer Bot ────────────────────────────────────────────────────────────────
|
|
368
801
|
/**
|
|
369
802
|
* Create a new escrow: registers with the API, signs and submits the
|
|
370
|
-
* on-chain `create_escrow` transaction.
|
|
803
|
+
* on-chain `create_escrow` transaction, then confirms it with the API.
|
|
371
804
|
*
|
|
372
805
|
* @returns `{ escrowId, escrowPda, signature }` — use `escrowPda` for all subsequent calls.
|
|
373
806
|
*/
|
|
374
807
|
async createEscrow(params) {
|
|
375
808
|
const { escrowId, escrowPda, unsignedTx } = await this.api.createEscrow(params);
|
|
376
|
-
const signature = await
|
|
809
|
+
const signature = await this.send(unsignedTx);
|
|
810
|
+
await this.confirm(escrowPda, signature);
|
|
377
811
|
return { escrowId, escrowPda, signature };
|
|
378
812
|
}
|
|
379
813
|
/**
|
|
380
814
|
* Release payment to the worker. Buyer must be the wallet in the constructor.
|
|
381
|
-
*
|
|
815
|
+
* Builds and submits the `release_payment` on-chain tx FIRST (the chain is the
|
|
816
|
+
* source of truth), then confirms DB state. The DB confirmation is best-effort:
|
|
817
|
+
* the oracle sweep reconciles it if it fails.
|
|
382
818
|
*
|
|
383
819
|
* @returns Solana transaction signature.
|
|
384
820
|
*/
|
|
@@ -387,59 +823,96 @@ var Escro = class {
|
|
|
387
823
|
if (!escrow.taker) {
|
|
388
824
|
throw new EscrowNotFoundError(`Escrow ${address} has no assigned worker`);
|
|
389
825
|
}
|
|
390
|
-
const { treasury, mint } = await fetchPlatformConfig(this.connection);
|
|
391
|
-
await this.api.releasePaymentDb(address);
|
|
826
|
+
const { treasury, mint } = await fetchPlatformConfig(this.connection, this.cluster);
|
|
392
827
|
const unsignedTx = await buildReleasePaymentTx({
|
|
393
828
|
buyer: this.options.wallet.publicKey,
|
|
394
829
|
escrowPda: new import_web32.PublicKey(address),
|
|
395
830
|
worker: new import_web32.PublicKey(escrow.taker),
|
|
396
831
|
mint,
|
|
397
832
|
treasury,
|
|
398
|
-
connection: this.connection
|
|
833
|
+
connection: this.connection,
|
|
834
|
+
cluster: this.cluster
|
|
399
835
|
});
|
|
400
|
-
|
|
836
|
+
const signature = await signAndSend(
|
|
837
|
+
unsignedTx,
|
|
838
|
+
this.options.wallet,
|
|
839
|
+
this.connection,
|
|
840
|
+
this.cluster
|
|
841
|
+
);
|
|
842
|
+
try {
|
|
843
|
+
await this.api.releasePaymentDb(address);
|
|
844
|
+
} catch {
|
|
845
|
+
}
|
|
846
|
+
return signature;
|
|
401
847
|
}
|
|
402
848
|
/**
|
|
403
849
|
* Raise a dispute for an escrow. Either the buyer or the assigned worker
|
|
404
|
-
* may call this. Signs and submits the `raise_dispute` on-chain tx.
|
|
850
|
+
* may call this. Signs and submits the `raise_dispute` on-chain tx, then confirms it with the API.
|
|
405
851
|
*
|
|
406
852
|
* @returns Solana transaction signature.
|
|
407
853
|
*/
|
|
408
854
|
async raiseDispute(address, params) {
|
|
409
|
-
|
|
410
|
-
|
|
855
|
+
if (params.evidence !== void 0 && !isUrl(params.evidence)) {
|
|
856
|
+
throw new EscroError(`evidence must be a URL: ${params.evidence}`, "VALIDATION_ERROR");
|
|
857
|
+
}
|
|
858
|
+
const { unsignedTx } = await this.api.raiseDispute(address, { reason: params.reason });
|
|
859
|
+
const signature = await this.send(unsignedTx);
|
|
860
|
+
await this.confirm(address, signature, {
|
|
861
|
+
...params.evidence !== void 0 && { evidence: params.evidence }
|
|
862
|
+
});
|
|
863
|
+
return signature;
|
|
411
864
|
}
|
|
412
865
|
/**
|
|
413
866
|
* Cancel a funded escrow before any worker has claimed it.
|
|
414
867
|
* Buyer must be the wallet in the constructor. Only valid when state is `FUNDED`.
|
|
415
|
-
* Signs and submits the `cancel_escrow` on-chain tx; full USDC refund to buyer.
|
|
868
|
+
* Signs and submits the `cancel_escrow` on-chain tx, then confirms it with the API; full USDC refund to buyer.
|
|
416
869
|
*
|
|
417
870
|
* @returns Solana transaction signature.
|
|
418
871
|
*/
|
|
419
872
|
async cancelEscrow(address) {
|
|
420
873
|
const { unsignedTx } = await this.api.cancelEscrow(address);
|
|
421
|
-
return
|
|
874
|
+
return this.sendAndConfirm(address, unsignedTx);
|
|
875
|
+
}
|
|
876
|
+
// ── Payments (dual-path; x402 reserved) ───────────────────────────────────────
|
|
877
|
+
/**
|
|
878
|
+
* Reserved synchronous-payment entrypoint (ARCHITECTURE §7a). `pay()` settles
|
|
879
|
+
* over the x402 rail, which is not implemented until Phase 9; today it rejects
|
|
880
|
+
* with {@link EscroRailNotEnabledError}. The signature is FROZEN so enabling
|
|
881
|
+
* x402 later is not a breaking change for kit-generated bots. For asynchronous,
|
|
882
|
+
* verification-needed work use {@link createEscrow}, the core product.
|
|
883
|
+
*
|
|
884
|
+
* Not `async`: it returns a rejected promise (never awaits) so callers can
|
|
885
|
+
* `await`/`.catch()` it uniformly, and it exercises the same {@link PaymentRouter}
|
|
886
|
+
* routing seam Phase 9 will implement.
|
|
887
|
+
*/
|
|
888
|
+
pay(params) {
|
|
889
|
+
const rail = this.router.selectRail({
|
|
890
|
+
to: params.to,
|
|
891
|
+
amountMicroUsdc: params.amountMicroUsdc,
|
|
892
|
+
mode: "sync"
|
|
893
|
+
});
|
|
894
|
+
return Promise.reject(new EscroRailNotEnabledError(rail === "escrow" ? "x402" : rail));
|
|
422
895
|
}
|
|
423
896
|
// ── Worker Bot ───────────────────────────────────────────────────────────────
|
|
424
897
|
/**
|
|
425
898
|
* Claim an assigned task. Worker must be the wallet in the constructor.
|
|
426
|
-
* Signs and submits the `claim_task` on-chain tx.
|
|
899
|
+
* Signs and submits the `claim_task` on-chain tx, then confirms it with the API.
|
|
427
900
|
*
|
|
428
901
|
* @returns Solana transaction signature.
|
|
429
902
|
*/
|
|
430
903
|
async claimTask(address) {
|
|
431
904
|
const { unsignedTx } = await this.api.claimTask(address);
|
|
432
|
-
return
|
|
905
|
+
return this.sendAndConfirm(address, unsignedTx);
|
|
433
906
|
}
|
|
434
907
|
/**
|
|
435
|
-
* Submit a deliverable for
|
|
436
|
-
* Signs and submits the `submit_deliverable` on-chain tx.
|
|
908
|
+
* Submit a deliverable for the buyer to review. Worker must be the wallet.
|
|
909
|
+
* Signs and submits the `submit_deliverable` on-chain tx, then confirms it with the API.
|
|
437
910
|
*
|
|
438
911
|
* @returns Solana transaction signature.
|
|
439
912
|
*/
|
|
440
913
|
async submitDeliverable(address, params) {
|
|
441
914
|
const { unsignedTx } = await this.api.submitDeliverable(address, params);
|
|
442
|
-
return
|
|
915
|
+
return this.sendAndConfirm(address, unsignedTx);
|
|
443
916
|
}
|
|
444
917
|
/**
|
|
445
918
|
* List tasks assigned to the wallet's public key.
|
|
@@ -470,7 +943,130 @@ var Escro = class {
|
|
|
470
943
|
options.timeoutMs
|
|
471
944
|
);
|
|
472
945
|
}
|
|
946
|
+
/**
|
|
947
|
+
* List escrows CREATED by the wallet's public key (the maker / buyer), the
|
|
948
|
+
* mirror of {@link getMyTasks} for the buyer side. Optionally filter by state
|
|
949
|
+
* and paginate.
|
|
950
|
+
*
|
|
951
|
+
* A Buyer Bot uses this to sum its own outstanding (still-locked) escrows for
|
|
952
|
+
* the client-side spend cap: the program is the source of truth, so the total
|
|
953
|
+
* is read from chain state (via the API), never a local tally a crash could lose.
|
|
954
|
+
*
|
|
955
|
+
* @param state - Filter by one or more states (first state used as API filter).
|
|
956
|
+
* @param options - Polling, pagination (limit/offset).
|
|
957
|
+
*/
|
|
958
|
+
async getMyEscrows(state, options) {
|
|
959
|
+
const maker = this.options.wallet.publicKey.toBase58();
|
|
960
|
+
const params = {
|
|
961
|
+
maker,
|
|
962
|
+
...state?.[0] != null && { state: state[0] },
|
|
963
|
+
...options?.limit != null && { limit: options.limit },
|
|
964
|
+
...options?.offset != null && { offset: options.offset }
|
|
965
|
+
};
|
|
966
|
+
if (!options?.poll) {
|
|
967
|
+
return this.api.listEscrows(params);
|
|
968
|
+
}
|
|
969
|
+
const fetcher = () => this.api.listEscrows(params).then((result) => result.items.length > 0 ? result : null);
|
|
970
|
+
return pollUntil(
|
|
971
|
+
maker,
|
|
972
|
+
fetcher,
|
|
973
|
+
(r) => r !== null,
|
|
974
|
+
options.timeoutMs
|
|
975
|
+
);
|
|
976
|
+
}
|
|
977
|
+
// ── EscroBots discovery ──────────────────────────────────────────────────────
|
|
978
|
+
/**
|
|
979
|
+
* Find worker bots in the EscroBots registry, optionally filtered by
|
|
980
|
+
* capability. Queries `GET /v1/bots?role=worker` (the API includes `both`
|
|
981
|
+
* bots) and returns candidates in REGISTRY ORDER: this method does no
|
|
982
|
+
* ranking, scoring, or reordering (ARCHITECTURE §8: no curation; §13: not a
|
|
983
|
+
* marketplace). Escro-shipped code never ranks; the caller's own bot decides.
|
|
984
|
+
*
|
|
985
|
+
* Each candidate keeps the CLAIM (`descriptor`) separate from the AUTHORSHIP
|
|
986
|
+
* check (`signatureValid`, verified locally so a third party need not trust the
|
|
987
|
+
* API). Capabilities remain unverified claims; on-chain history (EscroRep,
|
|
988
|
+
* Phase 7) is their verification, deliberately not merged in here.
|
|
989
|
+
*/
|
|
990
|
+
async findWorkers(params = {}) {
|
|
991
|
+
const { items } = await this.api.listBots({
|
|
992
|
+
role: "worker",
|
|
993
|
+
...params.capability != null && { capability: params.capability },
|
|
994
|
+
...params.limit != null && { limit: params.limit },
|
|
995
|
+
...params.offset != null && { offset: params.offset }
|
|
996
|
+
});
|
|
997
|
+
return items.map((descriptor) => ({
|
|
998
|
+
descriptor,
|
|
999
|
+
signatureValid: verifyBotDescriptorSignature(descriptor)
|
|
1000
|
+
}));
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* Fetch the EscroRep bulk export: every indexed wallet's RAW reputation
|
|
1004
|
+
* components (ARCHITECTURE §9), each wallet exactly once, in one fetch. Returns
|
|
1005
|
+
* the raw components verbatim - this method does NO ranking, scoring, or default
|
|
1006
|
+
* weighting (Escro-shipped code never ranks; the caller's own bot weights and
|
|
1007
|
+
* decides). Selection layers join these components to {@link findWorkers}
|
|
1008
|
+
* candidates by wallet. Throws {@link ReputationUnavailableError} when the
|
|
1009
|
+
* deployment mounts no reputation source (HTTP 404).
|
|
1010
|
+
*/
|
|
1011
|
+
async listReputations() {
|
|
1012
|
+
return this.api.listReputations();
|
|
1013
|
+
}
|
|
1014
|
+
// ── Stats ────────────────────────────────────────────────────────────────────
|
|
1015
|
+
/**
|
|
1016
|
+
* Fetch protocol-wide stats from the public `GET /v1/stats` endpoint.
|
|
1017
|
+
*
|
|
1018
|
+
* @returns protocol-wide {@link ProtocolStats}.
|
|
1019
|
+
*/
|
|
1020
|
+
async getStats() {
|
|
1021
|
+
const data = await this.api.getStats();
|
|
1022
|
+
return data;
|
|
1023
|
+
}
|
|
473
1024
|
// ── Shared ───────────────────────────────────────────────────────────────────
|
|
1025
|
+
/**
|
|
1026
|
+
* Ask the API to record the create, claim, submit, cancel or dispute that the
|
|
1027
|
+
* landed `signature` carries; the API verifies it on chain first. Retries a
|
|
1028
|
+
* not-yet-visible transaction, an RPC failure, a gateway error or a rate limit
|
|
1029
|
+
* for about 30 s (waiting at least a sent Retry-After), never a rejection. Throws
|
|
1030
|
+
* {@link EscroConfirmError}; calling again is safe (a replay of a recorded
|
|
1031
|
+
* transition succeeds).
|
|
1032
|
+
*/
|
|
1033
|
+
async confirm(address, signature, opts = {}) {
|
|
1034
|
+
const deadline = Date.now() + CONFIRM_WINDOW_MS;
|
|
1035
|
+
let delay = CONFIRM_FIRST_DELAY_MS;
|
|
1036
|
+
for (; ; ) {
|
|
1037
|
+
let code;
|
|
1038
|
+
let message;
|
|
1039
|
+
let wait = delay;
|
|
1040
|
+
try {
|
|
1041
|
+
return await this.api.confirm(address, signature, opts.evidence);
|
|
1042
|
+
} catch (err) {
|
|
1043
|
+
if (err instanceof EscroError) {
|
|
1044
|
+
if (!isRetryableConfirmCode(err.code)) {
|
|
1045
|
+
throw new EscroConfirmError(address, signature, err.code, err.message);
|
|
1046
|
+
}
|
|
1047
|
+
code = err.code;
|
|
1048
|
+
message = err.message;
|
|
1049
|
+
wait = Math.max(delay, err.retryAfterMs ?? 0);
|
|
1050
|
+
} else {
|
|
1051
|
+
code = "NETWORK_ERROR";
|
|
1052
|
+
message = err instanceof Error ? err.message : String(err);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
if (Date.now() + wait > deadline) {
|
|
1056
|
+
throw new EscroConfirmError(address, signature, code, message);
|
|
1057
|
+
}
|
|
1058
|
+
await sleep2(wait);
|
|
1059
|
+
delay = Math.min(delay * 2, CONFIRM_MAX_DELAY_MS);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
send(unsignedTx) {
|
|
1063
|
+
return signAndSend(unsignedTx, this.options.wallet, this.connection, this.cluster);
|
|
1064
|
+
}
|
|
1065
|
+
async sendAndConfirm(address, unsignedTx) {
|
|
1066
|
+
const signature = await this.send(unsignedTx);
|
|
1067
|
+
await this.confirm(address, signature);
|
|
1068
|
+
return signature;
|
|
1069
|
+
}
|
|
474
1070
|
/**
|
|
475
1071
|
* Fetch an escrow by its PDA address.
|
|
476
1072
|
* If `options.poll` is true, polls with exponential backoff until the escrow
|
|
@@ -482,19 +1078,1357 @@ var Escro = class {
|
|
|
482
1078
|
}
|
|
483
1079
|
return pollUntil(
|
|
484
1080
|
address,
|
|
485
|
-
() => this.api.getEscrow(address).catch((e) =>
|
|
1081
|
+
() => this.api.getEscrow(address).catch((e) => {
|
|
1082
|
+
if (e instanceof EscrowNotFoundError) return null;
|
|
1083
|
+
throw e instanceof Error ? e : new Error(String(e));
|
|
1084
|
+
}),
|
|
486
1085
|
(r) => r !== null,
|
|
487
1086
|
options.timeoutMs
|
|
488
1087
|
);
|
|
489
1088
|
}
|
|
490
1089
|
};
|
|
1090
|
+
|
|
1091
|
+
// src/adapter.ts
|
|
1092
|
+
function serviceResultToSubmitParams(result) {
|
|
1093
|
+
return {
|
|
1094
|
+
contentHash: result.contentHash,
|
|
1095
|
+
...result.proofUri !== void 0 && { proofUri: result.proofUri }
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
// src/adapter-contract.ts
|
|
1100
|
+
var import_crypto2 = require("crypto");
|
|
1101
|
+
var AdapterContractError = class extends EscroError {
|
|
1102
|
+
constructor(adapterId, violations) {
|
|
1103
|
+
super(
|
|
1104
|
+
`ServiceAdapter "${adapterId}" violates the adapter contract:
|
|
1105
|
+
- ${violations.join("\n- ")}`,
|
|
1106
|
+
"ADAPTER_CONTRACT_VIOLATION"
|
|
1107
|
+
);
|
|
1108
|
+
__publicField(this, "violations", violations);
|
|
1109
|
+
this.name = "AdapterContractError";
|
|
1110
|
+
}
|
|
1111
|
+
};
|
|
1112
|
+
function errMessage(err) {
|
|
1113
|
+
return err instanceof Error ? err.message : String(err);
|
|
1114
|
+
}
|
|
1115
|
+
function sha256Hex(content) {
|
|
1116
|
+
return (0, import_crypto2.createHash)("sha256").update(content).digest("hex");
|
|
1117
|
+
}
|
|
1118
|
+
function validateServiceResult(result) {
|
|
1119
|
+
if (typeof result !== "object" || result === null) {
|
|
1120
|
+
return ["`execute()` must resolve to a ServiceResult object"];
|
|
1121
|
+
}
|
|
1122
|
+
const violations = [];
|
|
1123
|
+
const r = result;
|
|
1124
|
+
const contentIsValid = typeof r.content === "string" || Buffer.isBuffer(r.content);
|
|
1125
|
+
if (!contentIsValid) {
|
|
1126
|
+
violations.push("`content` must be a string or Buffer");
|
|
1127
|
+
}
|
|
1128
|
+
if (typeof r.contentHash !== "string" || r.contentHash.trim() === "") {
|
|
1129
|
+
violations.push("`contentHash` must be a non-empty string");
|
|
1130
|
+
} else if (contentIsValid && r.contentHash !== sha256Hex(r.content)) {
|
|
1131
|
+
violations.push("`contentHash` must equal the SHA-256 hex digest of `content`");
|
|
1132
|
+
}
|
|
1133
|
+
if (r.proofUri !== void 0 && typeof r.proofUri !== "string") {
|
|
1134
|
+
violations.push("`proofUri`, when present, must be a string");
|
|
1135
|
+
}
|
|
1136
|
+
if (typeof r.metadata !== "object" || r.metadata === null || Array.isArray(r.metadata)) {
|
|
1137
|
+
violations.push("`metadata` must be a plain object");
|
|
1138
|
+
}
|
|
1139
|
+
return violations;
|
|
1140
|
+
}
|
|
1141
|
+
async function assertServiceAdapterContract(adapter, options) {
|
|
1142
|
+
const a = adapter;
|
|
1143
|
+
const violations = [];
|
|
1144
|
+
const id = typeof a.id === "string" && a.id.trim() !== "" ? a.id : "(unknown)";
|
|
1145
|
+
if (typeof a.id !== "string" || a.id.trim() === "") {
|
|
1146
|
+
violations.push("`id` must be a non-empty string");
|
|
1147
|
+
}
|
|
1148
|
+
if (!Array.isArray(a.capabilities) || a.capabilities.length === 0 || a.capabilities.some((c) => typeof c !== "string" || c.trim() === "")) {
|
|
1149
|
+
violations.push("`capabilities` must be a non-empty array of non-empty strings");
|
|
1150
|
+
}
|
|
1151
|
+
if (typeof a.healthCheck !== "function") {
|
|
1152
|
+
violations.push("`healthCheck` must be a function");
|
|
1153
|
+
}
|
|
1154
|
+
if (typeof a.execute !== "function") {
|
|
1155
|
+
violations.push("`execute` must be a function");
|
|
1156
|
+
}
|
|
1157
|
+
if (typeof a.healthCheck === "function") {
|
|
1158
|
+
try {
|
|
1159
|
+
const healthy = await a.healthCheck();
|
|
1160
|
+
if (typeof healthy !== "boolean") {
|
|
1161
|
+
violations.push("`healthCheck()` must resolve to a boolean");
|
|
1162
|
+
}
|
|
1163
|
+
} catch (err) {
|
|
1164
|
+
violations.push(`\`healthCheck()\` threw: ${errMessage(err)}`);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
let result;
|
|
1168
|
+
if (typeof a.execute === "function") {
|
|
1169
|
+
try {
|
|
1170
|
+
result = await a.execute(options.sampleSpec);
|
|
1171
|
+
violations.push(...validateServiceResult(result));
|
|
1172
|
+
} catch (err) {
|
|
1173
|
+
violations.push(`\`execute()\` threw: ${errMessage(err)}`);
|
|
1174
|
+
result = void 0;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
if (violations.length > 0 || result === void 0) {
|
|
1178
|
+
throw new AdapterContractError(
|
|
1179
|
+
id,
|
|
1180
|
+
violations.length > 0 ? violations : ["`execute()` produced no result"]
|
|
1181
|
+
);
|
|
1182
|
+
}
|
|
1183
|
+
return result;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
// src/adapters/bittensor/index.ts
|
|
1187
|
+
var import_crypto3 = require("crypto");
|
|
1188
|
+
function tao(amount) {
|
|
1189
|
+
return amount;
|
|
1190
|
+
}
|
|
1191
|
+
function sha256Hex2(content) {
|
|
1192
|
+
return (0, import_crypto3.createHash)("sha256").update(content).digest("hex");
|
|
1193
|
+
}
|
|
1194
|
+
var REFERENCE_MICRO_USDC_PER_TAO = 3e8;
|
|
1195
|
+
var defaultSwap = (microUsdc) => Promise.resolve(tao(microUsdc / REFERENCE_MICRO_USDC_PER_TAO));
|
|
1196
|
+
var defaultPin = (content) => Promise.resolve(`ipfs://placeholder-${sha256Hex2(content).slice(0, 16)}`);
|
|
1197
|
+
function createBittensorSubnetAdapter(config) {
|
|
1198
|
+
const id = `bittensor:${config.subnet}`;
|
|
1199
|
+
return {
|
|
1200
|
+
id,
|
|
1201
|
+
capabilities: config.capabilities,
|
|
1202
|
+
healthCheck: config.checkHealth,
|
|
1203
|
+
async execute(spec2) {
|
|
1204
|
+
const budgetTao = await config.swap(config.taskBudgetMicroUsdc);
|
|
1205
|
+
const content = await config.callSubnet(spec2, budgetTao);
|
|
1206
|
+
const contentHash = sha256Hex2(content);
|
|
1207
|
+
const proofUri = await config.pin(content);
|
|
1208
|
+
return {
|
|
1209
|
+
content,
|
|
1210
|
+
contentHash,
|
|
1211
|
+
proofUri,
|
|
1212
|
+
metadata: { adapter: id, subnet: config.subnet }
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
// src/adapters/gradients/index.ts
|
|
1219
|
+
var GRADIENTS_ADAPTER_ID = "bittensor:sn56";
|
|
1220
|
+
var GRADIENTS_CAPABILITIES = ["fine-tune"];
|
|
1221
|
+
var DEFAULT_SUBNET = "sn56";
|
|
1222
|
+
var DEFAULT_TASK_BUDGET_MICRO_USDC = 5e6;
|
|
1223
|
+
var GRADIENTS_DEMO_MARKER = "gradients-sn56-demo-v1";
|
|
1224
|
+
var GRADIENTS_DEMO_EVAL_FLOOR = 0.8;
|
|
1225
|
+
var DEMO_DATASET = [3, 7, 11, 15, 19];
|
|
1226
|
+
function demoExpectedOutputs() {
|
|
1227
|
+
return DEMO_DATASET.map((x) => 2 * x + 1);
|
|
1228
|
+
}
|
|
1229
|
+
var gradientsDemoJobSpec = {
|
|
1230
|
+
version: "1.0.0",
|
|
1231
|
+
// TaskType has no fine-tune category; "custom" is the canonical fallback.
|
|
1232
|
+
taskType: "custom",
|
|
1233
|
+
description: "Fine-tune the reference model on the canned SN56 demo dataset and return its predictions on the same inputs. The target map is y = 2x + 1. Acceptance is deterministic and recomputed by the buyer: prediction accuracy must be >= 0.8; the worker's self-reported eval score is not trusted.",
|
|
1234
|
+
acceptanceCriteria: [
|
|
1235
|
+
{
|
|
1236
|
+
id: "ac-eval-floor",
|
|
1237
|
+
description: "Prediction accuracy on the demo dataset is >= 0.8, recomputed by the buyer from the deliverable.",
|
|
1238
|
+
required: true
|
|
1239
|
+
}
|
|
1240
|
+
],
|
|
1241
|
+
deliverableFormat: { type: "json" },
|
|
1242
|
+
metadata: {
|
|
1243
|
+
demo: GRADIENTS_DEMO_MARKER,
|
|
1244
|
+
dataset: [...DEMO_DATASET],
|
|
1245
|
+
target: "y = 2x + 1",
|
|
1246
|
+
evalFloor: GRADIENTS_DEMO_EVAL_FLOOR
|
|
1247
|
+
}
|
|
1248
|
+
};
|
|
1249
|
+
function solveGradientsDemo() {
|
|
1250
|
+
const predictions = demoExpectedOutputs();
|
|
1251
|
+
return canonicalize({
|
|
1252
|
+
checkpoint: "reference-sn56-demo-checkpoint",
|
|
1253
|
+
eval: { metric: "accuracy", score: 1 },
|
|
1254
|
+
predictions
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1257
|
+
function parseDemoDeliverable(content) {
|
|
1258
|
+
const text = Buffer.isBuffer(content) ? content.toString("utf8") : content;
|
|
1259
|
+
let parsed;
|
|
1260
|
+
try {
|
|
1261
|
+
parsed = JSON.parse(text);
|
|
1262
|
+
} catch {
|
|
1263
|
+
return null;
|
|
1264
|
+
}
|
|
1265
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
1266
|
+
return null;
|
|
1267
|
+
}
|
|
1268
|
+
const predictions = parsed.predictions;
|
|
1269
|
+
if (!Array.isArray(predictions) || predictions.some((p) => typeof p !== "number")) {
|
|
1270
|
+
return null;
|
|
1271
|
+
}
|
|
1272
|
+
return { predictions };
|
|
1273
|
+
}
|
|
1274
|
+
function scoreGradientsDemoDeliverable(content) {
|
|
1275
|
+
const parsed = parseDemoDeliverable(content);
|
|
1276
|
+
if (parsed === null) {
|
|
1277
|
+
return 0;
|
|
1278
|
+
}
|
|
1279
|
+
const expected = demoExpectedOutputs();
|
|
1280
|
+
let matches = 0;
|
|
1281
|
+
for (let i = 0; i < expected.length; i++) {
|
|
1282
|
+
if (parsed.predictions[i] === expected[i]) {
|
|
1283
|
+
matches++;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
return matches / expected.length;
|
|
1287
|
+
}
|
|
1288
|
+
function referencePlaceholder(spec2) {
|
|
1289
|
+
return canonicalize({
|
|
1290
|
+
checkpoint: "reference-sn56-checkpoint",
|
|
1291
|
+
eval: { metric: "accuracy", score: 0 },
|
|
1292
|
+
note: "reference stub - replace the callSubnet port with a real SN56 miner call.",
|
|
1293
|
+
taskType: spec2.taskType
|
|
1294
|
+
});
|
|
1295
|
+
}
|
|
1296
|
+
var defaultCallSubnet = (spec2) => Promise.resolve(
|
|
1297
|
+
spec2.metadata.demo === GRADIENTS_DEMO_MARKER ? solveGradientsDemo() : referencePlaceholder(spec2)
|
|
1298
|
+
);
|
|
1299
|
+
function createGradientsAdapter(config = {}) {
|
|
1300
|
+
return createBittensorSubnetAdapter({
|
|
1301
|
+
subnet: config.subnet ?? DEFAULT_SUBNET,
|
|
1302
|
+
capabilities: config.capabilities ?? [...GRADIENTS_CAPABILITIES],
|
|
1303
|
+
taskBudgetMicroUsdc: config.taskBudgetMicroUsdc ?? DEFAULT_TASK_BUDGET_MICRO_USDC,
|
|
1304
|
+
swap: config.swap ?? defaultSwap,
|
|
1305
|
+
callSubnet: config.callSubnet ?? defaultCallSubnet,
|
|
1306
|
+
pin: config.pin ?? defaultPin,
|
|
1307
|
+
checkHealth: config.checkHealth ?? (() => Promise.resolve(true))
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
// src/adapters/ridges/index.ts
|
|
1312
|
+
var import_crypto4 = require("crypto");
|
|
1313
|
+
var RIDGES_ADAPTER_ID = "ridges";
|
|
1314
|
+
var RIDGES_CAPABILITIES = ["code-generation"];
|
|
1315
|
+
var DEFAULT_SERVICE = "ridges";
|
|
1316
|
+
function sha256Hex3(content) {
|
|
1317
|
+
return (0, import_crypto4.createHash)("sha256").update(content).digest("hex");
|
|
1318
|
+
}
|
|
1319
|
+
var RIDGES_DEMO_MARKER = "ridges-demo-v1";
|
|
1320
|
+
var RIDGES_DEMO_COVERAGE_TARGET = 0.8;
|
|
1321
|
+
var DEMO_INPUTS = [1, 2, 3, 5, 15];
|
|
1322
|
+
function fizzbuzz(n) {
|
|
1323
|
+
if (n % 15 === 0) return "FizzBuzz";
|
|
1324
|
+
if (n % 3 === 0) return "Fizz";
|
|
1325
|
+
if (n % 5 === 0) return "Buzz";
|
|
1326
|
+
return String(n);
|
|
1327
|
+
}
|
|
1328
|
+
var ridgesDemoJobSpec = {
|
|
1329
|
+
version: "1.0.0",
|
|
1330
|
+
taskType: "code_generation",
|
|
1331
|
+
description: "Implement FizzBuzz for the fixed demo battery and return a test report: for each battery input, the output your implementation produced. Acceptance is deterministic and recomputed by the buyer: every delivered test must pass (exit 0) and coverage of the battery must be >= 0.8; the worker's self-reported pass/coverage is not trusted.",
|
|
1332
|
+
acceptanceCriteria: [
|
|
1333
|
+
{
|
|
1334
|
+
id: "ac-tests-pass",
|
|
1335
|
+
description: "Every delivered test passes (exit 0), recomputed by the buyer from the test report.",
|
|
1336
|
+
required: true
|
|
1337
|
+
},
|
|
1338
|
+
{
|
|
1339
|
+
id: "ac-coverage",
|
|
1340
|
+
description: "Coverage of the demo battery is >= 0.8, recomputed by the buyer.",
|
|
1341
|
+
required: true
|
|
1342
|
+
}
|
|
1343
|
+
],
|
|
1344
|
+
deliverableFormat: { type: "json" },
|
|
1345
|
+
metadata: {
|
|
1346
|
+
demo: RIDGES_DEMO_MARKER,
|
|
1347
|
+
kata: "fizzbuzz",
|
|
1348
|
+
inputs: [...DEMO_INPUTS],
|
|
1349
|
+
coverageTarget: RIDGES_DEMO_COVERAGE_TARGET
|
|
1350
|
+
}
|
|
1351
|
+
};
|
|
1352
|
+
var REFERENCE_SOLUTION = 'export function fizzbuzz(n: number): string {\n if (n % 15 === 0) return "FizzBuzz";\n if (n % 3 === 0) return "Fizz";\n if (n % 5 === 0) return "Buzz";\n return String(n);\n}\n';
|
|
1353
|
+
function solveRidgesDemo() {
|
|
1354
|
+
const tests = DEMO_INPUTS.map((input) => ({ input, output: fizzbuzz(input) }));
|
|
1355
|
+
return canonicalize({
|
|
1356
|
+
language: "typescript",
|
|
1357
|
+
solution: REFERENCE_SOLUTION,
|
|
1358
|
+
coverage: 1,
|
|
1359
|
+
passed: true,
|
|
1360
|
+
tests
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
function parseTestReport(content) {
|
|
1364
|
+
const text = Buffer.isBuffer(content) ? content.toString("utf8") : content;
|
|
1365
|
+
let parsed;
|
|
1366
|
+
try {
|
|
1367
|
+
parsed = JSON.parse(text);
|
|
1368
|
+
} catch {
|
|
1369
|
+
return null;
|
|
1370
|
+
}
|
|
1371
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
1372
|
+
return null;
|
|
1373
|
+
}
|
|
1374
|
+
const tests = parsed.tests;
|
|
1375
|
+
if (!Array.isArray(tests)) {
|
|
1376
|
+
return null;
|
|
1377
|
+
}
|
|
1378
|
+
const results = [];
|
|
1379
|
+
for (const t of tests) {
|
|
1380
|
+
if (typeof t !== "object" || t === null) {
|
|
1381
|
+
return null;
|
|
1382
|
+
}
|
|
1383
|
+
const { input, output } = t;
|
|
1384
|
+
if (typeof input !== "number" || typeof output !== "string") {
|
|
1385
|
+
return null;
|
|
1386
|
+
}
|
|
1387
|
+
results.push({ input, output });
|
|
1388
|
+
}
|
|
1389
|
+
return results;
|
|
1390
|
+
}
|
|
1391
|
+
function scoreRidgesDemoDeliverable(content) {
|
|
1392
|
+
const results = parseTestReport(content);
|
|
1393
|
+
if (results === null) {
|
|
1394
|
+
return 0;
|
|
1395
|
+
}
|
|
1396
|
+
const battery = new Set(DEMO_INPUTS);
|
|
1397
|
+
const coveredInputs = /* @__PURE__ */ new Set();
|
|
1398
|
+
for (const { input, output } of results) {
|
|
1399
|
+
if (!battery.has(input)) {
|
|
1400
|
+
continue;
|
|
1401
|
+
}
|
|
1402
|
+
if (output !== fizzbuzz(input)) {
|
|
1403
|
+
return 0;
|
|
1404
|
+
}
|
|
1405
|
+
coveredInputs.add(input);
|
|
1406
|
+
}
|
|
1407
|
+
return coveredInputs.size / DEMO_INPUTS.length;
|
|
1408
|
+
}
|
|
1409
|
+
function referencePlaceholder2(spec2) {
|
|
1410
|
+
return canonicalize({
|
|
1411
|
+
language: "typescript",
|
|
1412
|
+
solution: "// reference stub - replace the runAgent port with a real Ridges agent call.",
|
|
1413
|
+
passed: false,
|
|
1414
|
+
tests: [],
|
|
1415
|
+
taskType: spec2.taskType
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
var defaultRunAgent = (spec2) => Promise.resolve(
|
|
1419
|
+
spec2.metadata.demo === RIDGES_DEMO_MARKER ? solveRidgesDemo() : referencePlaceholder2(spec2)
|
|
1420
|
+
);
|
|
1421
|
+
var defaultPin2 = (content) => Promise.resolve(`ipfs://placeholder-${sha256Hex3(content).slice(0, 16)}`);
|
|
1422
|
+
function createRidgesAdapter(config = {}) {
|
|
1423
|
+
const service = config.service ?? DEFAULT_SERVICE;
|
|
1424
|
+
const capabilities = config.capabilities ?? [...RIDGES_CAPABILITIES];
|
|
1425
|
+
const runAgent = config.runAgent ?? defaultRunAgent;
|
|
1426
|
+
const pin = config.pin ?? defaultPin2;
|
|
1427
|
+
const checkHealth = config.checkHealth ?? (() => Promise.resolve(true));
|
|
1428
|
+
return {
|
|
1429
|
+
id: service,
|
|
1430
|
+
capabilities,
|
|
1431
|
+
healthCheck: checkHealth,
|
|
1432
|
+
async execute(spec2) {
|
|
1433
|
+
const content = await runAgent(spec2);
|
|
1434
|
+
const contentHash = sha256Hex3(content);
|
|
1435
|
+
const proofUri = await pin(content);
|
|
1436
|
+
return {
|
|
1437
|
+
content,
|
|
1438
|
+
contentHash,
|
|
1439
|
+
proofUri,
|
|
1440
|
+
metadata: { adapter: service }
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
};
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
// src/adapters/404gen/index.ts
|
|
1447
|
+
var GEN404_ADAPTER_ID = "bittensor:sn17";
|
|
1448
|
+
var GEN404_CAPABILITIES = ["3d-generation"];
|
|
1449
|
+
var DEFAULT_SUBNET2 = "sn17";
|
|
1450
|
+
var DEFAULT_TASK_BUDGET_MICRO_USDC2 = 5e6;
|
|
1451
|
+
var GEN404_DEMO_MARKER = "404gen-sn17-demo-v1";
|
|
1452
|
+
var GEN404_DEMO_EVAL_FLOOR = 1;
|
|
1453
|
+
var DEMO_EXPECTATION = {
|
|
1454
|
+
requestedAssets: 1,
|
|
1455
|
+
allowedFormats: ["GLB", "PLY"],
|
|
1456
|
+
minPolygons: 1e3,
|
|
1457
|
+
maxPolygons: 1e6
|
|
1458
|
+
};
|
|
1459
|
+
var gen404DemoJobSpec = {
|
|
1460
|
+
version: "1.0.0",
|
|
1461
|
+
// TaskType has no 3D category; "custom" is the canonical fallback.
|
|
1462
|
+
taskType: "custom",
|
|
1463
|
+
description: "Generate the canned SN17 demo batch: one 3D asset in a GLB or PLY container with a polygon count between 1000 and 1000000. Acceptance is deterministic and recomputed by the buyer: every requested asset must be present, in an allowed format, and in polygon range; the worker's self-reported validity is not trusted.",
|
|
1464
|
+
acceptanceCriteria: [
|
|
1465
|
+
{
|
|
1466
|
+
id: "ac-format-valid",
|
|
1467
|
+
description: "Every asset is in an allowed container format (GLB or PLY), recomputed by the buyer.",
|
|
1468
|
+
required: true
|
|
1469
|
+
},
|
|
1470
|
+
{
|
|
1471
|
+
id: "ac-polygon-range",
|
|
1472
|
+
description: "Every asset's polygon count is within [1000, 1000000], recomputed by the buyer.",
|
|
1473
|
+
required: true
|
|
1474
|
+
},
|
|
1475
|
+
{
|
|
1476
|
+
id: "ac-batch-complete",
|
|
1477
|
+
description: "The batch contains all requested assets (completeness), recomputed by the buyer.",
|
|
1478
|
+
required: true
|
|
1479
|
+
}
|
|
1480
|
+
],
|
|
1481
|
+
deliverableFormat: { type: "json" },
|
|
1482
|
+
metadata: {
|
|
1483
|
+
demo: GEN404_DEMO_MARKER,
|
|
1484
|
+
requestedAssets: DEMO_EXPECTATION.requestedAssets,
|
|
1485
|
+
allowedFormats: [...DEMO_EXPECTATION.allowedFormats],
|
|
1486
|
+
minPolygons: DEMO_EXPECTATION.minPolygons,
|
|
1487
|
+
maxPolygons: DEMO_EXPECTATION.maxPolygons
|
|
1488
|
+
}
|
|
1489
|
+
};
|
|
1490
|
+
function solveGen404Demo() {
|
|
1491
|
+
return canonicalize({
|
|
1492
|
+
assets: [{ format: "GLB", polygonCount: 5e3, uri: "ipfs://404gen-sn17-demo-asset-0" }],
|
|
1493
|
+
eval: { metric: "validity", score: 1 }
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1496
|
+
function parseBatch(content) {
|
|
1497
|
+
const text = Buffer.isBuffer(content) ? content.toString("utf8") : content;
|
|
1498
|
+
let parsed;
|
|
1499
|
+
try {
|
|
1500
|
+
parsed = JSON.parse(text);
|
|
1501
|
+
} catch {
|
|
1502
|
+
return null;
|
|
1503
|
+
}
|
|
1504
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
1505
|
+
return null;
|
|
1506
|
+
}
|
|
1507
|
+
const assets = parsed.assets;
|
|
1508
|
+
if (!Array.isArray(assets)) {
|
|
1509
|
+
return null;
|
|
1510
|
+
}
|
|
1511
|
+
const cleaned = [];
|
|
1512
|
+
for (const asset of assets) {
|
|
1513
|
+
if (typeof asset !== "object" || asset === null) {
|
|
1514
|
+
return null;
|
|
1515
|
+
}
|
|
1516
|
+
const format = asset.format;
|
|
1517
|
+
const polygonCount = asset.polygonCount;
|
|
1518
|
+
if (typeof format !== "string" || typeof polygonCount !== "number") {
|
|
1519
|
+
return null;
|
|
1520
|
+
}
|
|
1521
|
+
cleaned.push({ format, polygonCount });
|
|
1522
|
+
}
|
|
1523
|
+
return { assets: cleaned };
|
|
1524
|
+
}
|
|
1525
|
+
function isAssetValid(asset) {
|
|
1526
|
+
const formatOk = DEMO_EXPECTATION.allowedFormats.some(
|
|
1527
|
+
(f) => f.toLowerCase() === asset.format.toLowerCase()
|
|
1528
|
+
);
|
|
1529
|
+
const rangeOk = asset.polygonCount >= DEMO_EXPECTATION.minPolygons && asset.polygonCount <= DEMO_EXPECTATION.maxPolygons;
|
|
1530
|
+
return formatOk && rangeOk;
|
|
1531
|
+
}
|
|
1532
|
+
function score404genDemoDeliverable(content) {
|
|
1533
|
+
const batch = parseBatch(content);
|
|
1534
|
+
if (batch === null) {
|
|
1535
|
+
return 0;
|
|
1536
|
+
}
|
|
1537
|
+
if (batch.assets.some((asset) => !isAssetValid(asset))) {
|
|
1538
|
+
return 0;
|
|
1539
|
+
}
|
|
1540
|
+
return Math.min(batch.assets.length / DEMO_EXPECTATION.requestedAssets, 1);
|
|
1541
|
+
}
|
|
1542
|
+
function referencePlaceholder3(spec2) {
|
|
1543
|
+
return canonicalize({
|
|
1544
|
+
assets: [{ format: "GLB", polygonCount: 0, uri: "ipfs://placeholder" }],
|
|
1545
|
+
note: "reference stub - replace the callSubnet port with a real SN17 miner call.",
|
|
1546
|
+
taskType: spec2.taskType
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
var defaultCallSubnet2 = (spec2) => Promise.resolve(
|
|
1550
|
+
spec2.metadata.demo === GEN404_DEMO_MARKER ? solveGen404Demo() : referencePlaceholder3(spec2)
|
|
1551
|
+
);
|
|
1552
|
+
function create404genAdapter(config = {}) {
|
|
1553
|
+
return createBittensorSubnetAdapter({
|
|
1554
|
+
subnet: config.subnet ?? DEFAULT_SUBNET2,
|
|
1555
|
+
capabilities: config.capabilities ?? [...GEN404_CAPABILITIES],
|
|
1556
|
+
taskBudgetMicroUsdc: config.taskBudgetMicroUsdc ?? DEFAULT_TASK_BUDGET_MICRO_USDC2,
|
|
1557
|
+
swap: config.swap ?? defaultSwap,
|
|
1558
|
+
callSubnet: config.callSubnet ?? defaultCallSubnet2,
|
|
1559
|
+
pin: config.pin ?? defaultPin,
|
|
1560
|
+
checkHealth: config.checkHealth ?? (() => Promise.resolve(true))
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
// src/adapters/lium/index.ts
|
|
1565
|
+
var LIUM_ADAPTER_ID = "bittensor:sn51";
|
|
1566
|
+
var LIUM_CAPABILITIES = ["gpu-compute"];
|
|
1567
|
+
var DEFAULT_SUBNET3 = "sn51";
|
|
1568
|
+
var DEFAULT_TASK_BUDGET_MICRO_USDC3 = 2e7;
|
|
1569
|
+
var LIUM_DEMO_MARKER = "lium-sn51-demo-v1";
|
|
1570
|
+
var LIUM_DEMO_ACCEPT_FLOOR = 1;
|
|
1571
|
+
var DEMO_SEED = "lium-sn51-demo-seed";
|
|
1572
|
+
var DEMO_ROUNDS = 1e3;
|
|
1573
|
+
function computeLiumDemoArtifact() {
|
|
1574
|
+
let h = DEMO_SEED;
|
|
1575
|
+
for (let i = 0; i < DEMO_ROUNDS; i++) {
|
|
1576
|
+
h = sha256Hex2(h);
|
|
1577
|
+
}
|
|
1578
|
+
return h;
|
|
1579
|
+
}
|
|
1580
|
+
var liumDemoJobSpec = {
|
|
1581
|
+
version: "1.0.0",
|
|
1582
|
+
// TaskType has no GPU-compute category; "custom" is the canonical fallback.
|
|
1583
|
+
taskType: "custom",
|
|
1584
|
+
description: "Run the canned SN51 scripted GPU job (a fixed hash-chain compute over a known seed) and return its output artifact. Acceptance is deterministic and recomputed by the buyer: the job must complete, an output artifact must be present, and the artifact must match the buyer's independent recompute; the worker's self-reported artifact hash is not trusted.",
|
|
1585
|
+
acceptanceCriteria: [
|
|
1586
|
+
{
|
|
1587
|
+
id: "ac-completion",
|
|
1588
|
+
description: "The scripted job ran to completion (status=completed).",
|
|
1589
|
+
required: true
|
|
1590
|
+
},
|
|
1591
|
+
{
|
|
1592
|
+
id: "ac-artifact-present",
|
|
1593
|
+
description: "An output artifact is present in the deliverable.",
|
|
1594
|
+
required: true
|
|
1595
|
+
},
|
|
1596
|
+
{
|
|
1597
|
+
id: "ac-hash-match",
|
|
1598
|
+
description: "The output artifact matches the buyer's independent recompute of the scripted job.",
|
|
1599
|
+
required: true
|
|
1600
|
+
}
|
|
1601
|
+
],
|
|
1602
|
+
deliverableFormat: { type: "json" },
|
|
1603
|
+
metadata: {
|
|
1604
|
+
demo: LIUM_DEMO_MARKER,
|
|
1605
|
+
seed: DEMO_SEED,
|
|
1606
|
+
rounds: DEMO_ROUNDS,
|
|
1607
|
+
acceptFloor: LIUM_DEMO_ACCEPT_FLOOR
|
|
1608
|
+
}
|
|
1609
|
+
};
|
|
1610
|
+
function solveLiumDemo() {
|
|
1611
|
+
const artifact = computeLiumDemoArtifact();
|
|
1612
|
+
return canonicalize({
|
|
1613
|
+
job: LIUM_DEMO_MARKER,
|
|
1614
|
+
status: "completed",
|
|
1615
|
+
rounds: DEMO_ROUNDS,
|
|
1616
|
+
artifact,
|
|
1617
|
+
// Self-reported and deliberately ignored by the buyer's recompute.
|
|
1618
|
+
artifactHash: sha256Hex2(artifact)
|
|
1619
|
+
});
|
|
1620
|
+
}
|
|
1621
|
+
function parseDemoDeliverable2(content) {
|
|
1622
|
+
const text = Buffer.isBuffer(content) ? content.toString("utf8") : content;
|
|
1623
|
+
let parsed;
|
|
1624
|
+
try {
|
|
1625
|
+
parsed = JSON.parse(text);
|
|
1626
|
+
} catch {
|
|
1627
|
+
return null;
|
|
1628
|
+
}
|
|
1629
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
1630
|
+
return null;
|
|
1631
|
+
}
|
|
1632
|
+
const obj = parsed;
|
|
1633
|
+
const status = typeof obj.status === "string" ? obj.status : "";
|
|
1634
|
+
const artifact = typeof obj.artifact === "string" ? obj.artifact : null;
|
|
1635
|
+
return { status, artifact };
|
|
1636
|
+
}
|
|
1637
|
+
function scoreLiumDemoDeliverable(content) {
|
|
1638
|
+
const parsed = parseDemoDeliverable2(content);
|
|
1639
|
+
if (parsed === null) {
|
|
1640
|
+
return 0;
|
|
1641
|
+
}
|
|
1642
|
+
if (parsed.status !== "completed") {
|
|
1643
|
+
return 0;
|
|
1644
|
+
}
|
|
1645
|
+
if (parsed.artifact === null) {
|
|
1646
|
+
return 0;
|
|
1647
|
+
}
|
|
1648
|
+
return parsed.artifact === computeLiumDemoArtifact() ? 1 : 0;
|
|
1649
|
+
}
|
|
1650
|
+
function referencePlaceholder4(spec2) {
|
|
1651
|
+
return canonicalize({
|
|
1652
|
+
job: "lium-sn51-generic",
|
|
1653
|
+
status: "completed",
|
|
1654
|
+
artifact: null,
|
|
1655
|
+
note: "reference stub - replace the runJob port with a real SN51 GPU-rental call.",
|
|
1656
|
+
taskType: spec2.taskType
|
|
1657
|
+
});
|
|
1658
|
+
}
|
|
1659
|
+
var defaultRunJob = (spec2) => Promise.resolve(
|
|
1660
|
+
spec2.metadata.demo === LIUM_DEMO_MARKER ? solveLiumDemo() : referencePlaceholder4(spec2)
|
|
1661
|
+
);
|
|
1662
|
+
function createLiumAdapter(config = {}) {
|
|
1663
|
+
return createBittensorSubnetAdapter({
|
|
1664
|
+
subnet: config.subnet ?? DEFAULT_SUBNET3,
|
|
1665
|
+
capabilities: config.capabilities ?? [...LIUM_CAPABILITIES],
|
|
1666
|
+
taskBudgetMicroUsdc: config.taskBudgetMicroUsdc ?? DEFAULT_TASK_BUDGET_MICRO_USDC3,
|
|
1667
|
+
swap: config.swap ?? defaultSwap,
|
|
1668
|
+
callSubnet: config.runJob ?? defaultRunJob,
|
|
1669
|
+
pin: config.pin ?? defaultPin,
|
|
1670
|
+
checkHealth: config.checkHealth ?? (() => Promise.resolve(true))
|
|
1671
|
+
});
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
// src/adapters/akash/index.ts
|
|
1675
|
+
var import_crypto5 = require("crypto");
|
|
1676
|
+
function akt(amount) {
|
|
1677
|
+
return amount;
|
|
1678
|
+
}
|
|
1679
|
+
function sha256Hex4(content) {
|
|
1680
|
+
return (0, import_crypto5.createHash)("sha256").update(content).digest("hex");
|
|
1681
|
+
}
|
|
1682
|
+
var AKASH_ADAPTER_ID = "akash";
|
|
1683
|
+
var AKASH_CAPABILITIES = ["cloud-compute"];
|
|
1684
|
+
var DEFAULT_TASK_BUDGET_MICRO_USDC4 = 1e7;
|
|
1685
|
+
var REFERENCE_MICRO_USDC_PER_AKT = 3e6;
|
|
1686
|
+
var defaultSwap2 = (microUsdc) => Promise.resolve(akt(microUsdc / REFERENCE_MICRO_USDC_PER_AKT));
|
|
1687
|
+
var defaultPin3 = (content) => Promise.resolve(`ipfs://placeholder-${sha256Hex4(content).slice(0, 16)}`);
|
|
1688
|
+
var AKASH_DEMO_MARKER = "akash-demo-v1";
|
|
1689
|
+
var AKASH_DEMO_ACCEPT_FLOOR = 1;
|
|
1690
|
+
var DEMO_SEED2 = "akash-demo-seed";
|
|
1691
|
+
var DEMO_ROUNDS2 = 1e3;
|
|
1692
|
+
function computeAkashDemoArtifact() {
|
|
1693
|
+
let h = DEMO_SEED2;
|
|
1694
|
+
for (let i = 0; i < DEMO_ROUNDS2; i++) {
|
|
1695
|
+
h = sha256Hex4(h);
|
|
1696
|
+
}
|
|
1697
|
+
return h;
|
|
1698
|
+
}
|
|
1699
|
+
var akashDemoJobSpec = {
|
|
1700
|
+
version: "1.0.0",
|
|
1701
|
+
// TaskType has no cloud-compute category; "custom" is the canonical fallback.
|
|
1702
|
+
taskType: "custom",
|
|
1703
|
+
description: "Run the canned Akash scripted batch job (a fixed hash-chain compute over a known seed) on a deployment and return its output artifact. Acceptance is deterministic and recomputed by the buyer: the job must complete, an output artifact must be present, and the artifact must match the buyer's independent recompute; the worker's self-reported artifact hash is not trusted.",
|
|
1704
|
+
acceptanceCriteria: [
|
|
1705
|
+
{
|
|
1706
|
+
id: "ac-completion",
|
|
1707
|
+
description: "The scripted job ran to completion (status=completed).",
|
|
1708
|
+
required: true
|
|
1709
|
+
},
|
|
1710
|
+
{
|
|
1711
|
+
id: "ac-artifact-present",
|
|
1712
|
+
description: "An output artifact is present in the deliverable.",
|
|
1713
|
+
required: true
|
|
1714
|
+
},
|
|
1715
|
+
{
|
|
1716
|
+
id: "ac-hash-match",
|
|
1717
|
+
description: "The output artifact matches the buyer's independent recompute of the scripted job.",
|
|
1718
|
+
required: true
|
|
1719
|
+
}
|
|
1720
|
+
],
|
|
1721
|
+
deliverableFormat: { type: "json" },
|
|
1722
|
+
metadata: {
|
|
1723
|
+
demo: AKASH_DEMO_MARKER,
|
|
1724
|
+
seed: DEMO_SEED2,
|
|
1725
|
+
rounds: DEMO_ROUNDS2,
|
|
1726
|
+
acceptFloor: AKASH_DEMO_ACCEPT_FLOOR
|
|
1727
|
+
}
|
|
1728
|
+
};
|
|
1729
|
+
function solveAkashDemo() {
|
|
1730
|
+
const artifact = computeAkashDemoArtifact();
|
|
1731
|
+
return canonicalize({
|
|
1732
|
+
job: AKASH_DEMO_MARKER,
|
|
1733
|
+
status: "completed",
|
|
1734
|
+
rounds: DEMO_ROUNDS2,
|
|
1735
|
+
artifact,
|
|
1736
|
+
// Self-reported and deliberately ignored by the buyer's recompute.
|
|
1737
|
+
artifactHash: sha256Hex4(artifact)
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1740
|
+
function parseDemoDeliverable3(content) {
|
|
1741
|
+
const text = Buffer.isBuffer(content) ? content.toString("utf8") : content;
|
|
1742
|
+
let parsed;
|
|
1743
|
+
try {
|
|
1744
|
+
parsed = JSON.parse(text);
|
|
1745
|
+
} catch {
|
|
1746
|
+
return null;
|
|
1747
|
+
}
|
|
1748
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
1749
|
+
return null;
|
|
1750
|
+
}
|
|
1751
|
+
const obj = parsed;
|
|
1752
|
+
const status = typeof obj.status === "string" ? obj.status : "";
|
|
1753
|
+
const artifact = typeof obj.artifact === "string" ? obj.artifact : null;
|
|
1754
|
+
return { status, artifact };
|
|
1755
|
+
}
|
|
1756
|
+
function scoreAkashDemoDeliverable(content) {
|
|
1757
|
+
const parsed = parseDemoDeliverable3(content);
|
|
1758
|
+
if (parsed === null) {
|
|
1759
|
+
return 0;
|
|
1760
|
+
}
|
|
1761
|
+
if (parsed.status !== "completed") {
|
|
1762
|
+
return 0;
|
|
1763
|
+
}
|
|
1764
|
+
if (parsed.artifact === null) {
|
|
1765
|
+
return 0;
|
|
1766
|
+
}
|
|
1767
|
+
return parsed.artifact === computeAkashDemoArtifact() ? 1 : 0;
|
|
1768
|
+
}
|
|
1769
|
+
function referencePlaceholder5(spec2) {
|
|
1770
|
+
return canonicalize({
|
|
1771
|
+
job: "akash-generic",
|
|
1772
|
+
status: "completed",
|
|
1773
|
+
artifact: null,
|
|
1774
|
+
note: "reference stub - replace the runDeployment port with a real Akash deployment call.",
|
|
1775
|
+
taskType: spec2.taskType
|
|
1776
|
+
});
|
|
1777
|
+
}
|
|
1778
|
+
var defaultRunDeployment = (spec2) => Promise.resolve(
|
|
1779
|
+
spec2.metadata.demo === AKASH_DEMO_MARKER ? solveAkashDemo() : referencePlaceholder5(spec2)
|
|
1780
|
+
);
|
|
1781
|
+
function createAkashAdapter(config = {}) {
|
|
1782
|
+
const capabilities = config.capabilities ?? [...AKASH_CAPABILITIES];
|
|
1783
|
+
const taskBudgetMicroUsdc = config.taskBudgetMicroUsdc ?? DEFAULT_TASK_BUDGET_MICRO_USDC4;
|
|
1784
|
+
const swap = config.swap ?? defaultSwap2;
|
|
1785
|
+
const runDeployment = config.runDeployment ?? defaultRunDeployment;
|
|
1786
|
+
const pin = config.pin ?? defaultPin3;
|
|
1787
|
+
const checkHealth = config.checkHealth ?? (() => Promise.resolve(true));
|
|
1788
|
+
return {
|
|
1789
|
+
id: AKASH_ADAPTER_ID,
|
|
1790
|
+
capabilities,
|
|
1791
|
+
healthCheck: checkHealth,
|
|
1792
|
+
async execute(spec2) {
|
|
1793
|
+
const budgetAkt = await swap(taskBudgetMicroUsdc);
|
|
1794
|
+
const content = await runDeployment(spec2, budgetAkt);
|
|
1795
|
+
const contentHash = sha256Hex4(content);
|
|
1796
|
+
const proofUri = await pin(content);
|
|
1797
|
+
return {
|
|
1798
|
+
content,
|
|
1799
|
+
contentHash,
|
|
1800
|
+
proofUri,
|
|
1801
|
+
metadata: { adapter: AKASH_ADAPTER_ID }
|
|
1802
|
+
};
|
|
1803
|
+
}
|
|
1804
|
+
};
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
// src/adapters/render/index.ts
|
|
1808
|
+
var import_crypto6 = require("crypto");
|
|
1809
|
+
function renderAmount(amount) {
|
|
1810
|
+
return amount;
|
|
1811
|
+
}
|
|
1812
|
+
var RENDER_ADAPTER_ID = "render";
|
|
1813
|
+
var RENDER_CAPABILITIES = ["3d-rendering", "video-rendering", "gpu-inference"];
|
|
1814
|
+
var DEFAULT_SERVICE2 = "render";
|
|
1815
|
+
var DEFAULT_TASK_BUDGET_MICRO_USDC5 = 15e6;
|
|
1816
|
+
function sha256Hex5(content) {
|
|
1817
|
+
return (0, import_crypto6.createHash)("sha256").update(content).digest("hex");
|
|
1818
|
+
}
|
|
1819
|
+
var REFERENCE_MICRO_USDC_PER_RENDER = 2e6;
|
|
1820
|
+
var defaultSwap3 = (microUsdc) => Promise.resolve(renderAmount(microUsdc / REFERENCE_MICRO_USDC_PER_RENDER));
|
|
1821
|
+
var defaultPin4 = (content) => Promise.resolve(`ipfs://placeholder-${sha256Hex5(content).slice(0, 16)}`);
|
|
1822
|
+
var RENDER_DEMO_MARKER = "render-demo-v1";
|
|
1823
|
+
var RENDER_DEMO_ACCEPT_FLOOR = 1;
|
|
1824
|
+
var DEMO_SCENE = "render-demo-scene-v1";
|
|
1825
|
+
var DEMO_WIDTH = 16;
|
|
1826
|
+
var DEMO_HEIGHT = 16;
|
|
1827
|
+
function shade(x, y) {
|
|
1828
|
+
return x * 53 + y * 97 + (x ^ y) * 29 & 255;
|
|
1829
|
+
}
|
|
1830
|
+
function computeRenderDemoArtifact() {
|
|
1831
|
+
const raster = Buffer.alloc(DEMO_WIDTH * DEMO_HEIGHT);
|
|
1832
|
+
let i = 0;
|
|
1833
|
+
for (let y = 0; y < DEMO_HEIGHT; y++) {
|
|
1834
|
+
for (let x = 0; x < DEMO_WIDTH; x++) {
|
|
1835
|
+
raster[i++] = shade(x, y);
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
return raster.toString("hex");
|
|
1839
|
+
}
|
|
1840
|
+
var renderDemoJobSpec = {
|
|
1841
|
+
version: "1.0.0",
|
|
1842
|
+
taskType: "image_generation",
|
|
1843
|
+
description: "Render the canned demo scene (a fixed procedural frame) and return the rendered frame. Acceptance is deterministic and recomputed by the buyer: the job must complete, a rendered frame must be present, and the frame must match the buyer's independent re-render; the worker's self-reported frame hash is not trusted.",
|
|
1844
|
+
acceptanceCriteria: [
|
|
1845
|
+
{
|
|
1846
|
+
id: "ac-completion",
|
|
1847
|
+
description: "The render job ran to completion (status=completed).",
|
|
1848
|
+
required: true
|
|
1849
|
+
},
|
|
1850
|
+
{
|
|
1851
|
+
id: "ac-frame-present",
|
|
1852
|
+
description: "A rendered frame is present in the deliverable.",
|
|
1853
|
+
required: true
|
|
1854
|
+
},
|
|
1855
|
+
{
|
|
1856
|
+
id: "ac-frame-match",
|
|
1857
|
+
description: "The rendered frame matches the buyer's independent re-render of the scene.",
|
|
1858
|
+
required: true
|
|
1859
|
+
}
|
|
1860
|
+
],
|
|
1861
|
+
deliverableFormat: { type: "json" },
|
|
1862
|
+
metadata: {
|
|
1863
|
+
demo: RENDER_DEMO_MARKER,
|
|
1864
|
+
scene: DEMO_SCENE,
|
|
1865
|
+
width: DEMO_WIDTH,
|
|
1866
|
+
height: DEMO_HEIGHT,
|
|
1867
|
+
acceptFloor: RENDER_DEMO_ACCEPT_FLOOR
|
|
1868
|
+
}
|
|
1869
|
+
};
|
|
1870
|
+
function solveRenderDemo() {
|
|
1871
|
+
const frame = computeRenderDemoArtifact();
|
|
1872
|
+
return canonicalize({
|
|
1873
|
+
job: RENDER_DEMO_MARKER,
|
|
1874
|
+
status: "completed",
|
|
1875
|
+
width: DEMO_WIDTH,
|
|
1876
|
+
height: DEMO_HEIGHT,
|
|
1877
|
+
frame,
|
|
1878
|
+
// Self-reported and deliberately ignored by the buyer's recompute.
|
|
1879
|
+
frameHash: sha256Hex5(frame)
|
|
1880
|
+
});
|
|
1881
|
+
}
|
|
1882
|
+
function parseDemoDeliverable4(content) {
|
|
1883
|
+
const text = Buffer.isBuffer(content) ? content.toString("utf8") : content;
|
|
1884
|
+
let parsed;
|
|
1885
|
+
try {
|
|
1886
|
+
parsed = JSON.parse(text);
|
|
1887
|
+
} catch {
|
|
1888
|
+
return null;
|
|
1889
|
+
}
|
|
1890
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
1891
|
+
return null;
|
|
1892
|
+
}
|
|
1893
|
+
const obj = parsed;
|
|
1894
|
+
const status = typeof obj.status === "string" ? obj.status : "";
|
|
1895
|
+
const frame = typeof obj.frame === "string" ? obj.frame : null;
|
|
1896
|
+
return { status, frame };
|
|
1897
|
+
}
|
|
1898
|
+
function scoreRenderDemoDeliverable(content) {
|
|
1899
|
+
const parsed = parseDemoDeliverable4(content);
|
|
1900
|
+
if (parsed === null) {
|
|
1901
|
+
return 0;
|
|
1902
|
+
}
|
|
1903
|
+
if (parsed.status !== "completed") {
|
|
1904
|
+
return 0;
|
|
1905
|
+
}
|
|
1906
|
+
if (parsed.frame === null) {
|
|
1907
|
+
return 0;
|
|
1908
|
+
}
|
|
1909
|
+
return parsed.frame === computeRenderDemoArtifact() ? 1 : 0;
|
|
1910
|
+
}
|
|
1911
|
+
function referencePlaceholder6(spec2) {
|
|
1912
|
+
return canonicalize({
|
|
1913
|
+
job: "render-generic",
|
|
1914
|
+
status: "completed",
|
|
1915
|
+
frame: null,
|
|
1916
|
+
note: "reference stub - replace the runJob port with a real Render Network job submission.",
|
|
1917
|
+
taskType: spec2.taskType
|
|
1918
|
+
});
|
|
1919
|
+
}
|
|
1920
|
+
var defaultRunJob2 = (spec2) => Promise.resolve(
|
|
1921
|
+
spec2.metadata.demo === RENDER_DEMO_MARKER ? solveRenderDemo() : referencePlaceholder6(spec2)
|
|
1922
|
+
);
|
|
1923
|
+
function createRenderAdapter(config = {}) {
|
|
1924
|
+
const service = config.service ?? DEFAULT_SERVICE2;
|
|
1925
|
+
const capabilities = config.capabilities ?? [...RENDER_CAPABILITIES];
|
|
1926
|
+
const taskBudgetMicroUsdc = config.taskBudgetMicroUsdc ?? DEFAULT_TASK_BUDGET_MICRO_USDC5;
|
|
1927
|
+
const swap = config.swap ?? defaultSwap3;
|
|
1928
|
+
const runJob = config.runJob ?? defaultRunJob2;
|
|
1929
|
+
const pin = config.pin ?? defaultPin4;
|
|
1930
|
+
const checkHealth = config.checkHealth ?? (() => Promise.resolve(true));
|
|
1931
|
+
return {
|
|
1932
|
+
id: service,
|
|
1933
|
+
capabilities,
|
|
1934
|
+
healthCheck: checkHealth,
|
|
1935
|
+
async execute(spec2) {
|
|
1936
|
+
const budgetRender = await swap(taskBudgetMicroUsdc);
|
|
1937
|
+
const content = await runJob(spec2, budgetRender);
|
|
1938
|
+
const contentHash = sha256Hex5(content);
|
|
1939
|
+
const proofUri = await pin(content);
|
|
1940
|
+
return {
|
|
1941
|
+
content,
|
|
1942
|
+
contentHash,
|
|
1943
|
+
proofUri,
|
|
1944
|
+
metadata: { adapter: service, network: "render" }
|
|
1945
|
+
};
|
|
1946
|
+
}
|
|
1947
|
+
};
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
// src/adapters/gensyn/index.ts
|
|
1951
|
+
var import_crypto7 = require("crypto");
|
|
1952
|
+
var GENSYN_ADAPTER_ID = "gensyn";
|
|
1953
|
+
var GENSYN_CAPABILITIES = ["verifiable-compute"];
|
|
1954
|
+
var GENSYN_REE_SCHEME = "gensyn-ree-v1";
|
|
1955
|
+
var GENSYN_DEMO_MARKER = "gensyn-ree-demo-v1";
|
|
1956
|
+
var GENSYN_DEMO_SEED = 12345;
|
|
1957
|
+
var GENSYN_DEMO_STEPS = 16;
|
|
1958
|
+
function sha256Hex6(content) {
|
|
1959
|
+
return (0, import_crypto7.createHash)("sha256").update(content).digest("hex");
|
|
1960
|
+
}
|
|
1961
|
+
var LCG_MULT = 1103515245n;
|
|
1962
|
+
var LCG_INC = 12345n;
|
|
1963
|
+
var LCG_MOD = 1n << 31n;
|
|
1964
|
+
function readComputeParams(spec2) {
|
|
1965
|
+
const rawSeed = spec2.metadata.seed;
|
|
1966
|
+
const rawSteps = spec2.metadata.steps;
|
|
1967
|
+
const seed = typeof rawSeed === "number" && Number.isInteger(rawSeed) ? rawSeed : GENSYN_DEMO_SEED;
|
|
1968
|
+
const steps = typeof rawSteps === "number" && Number.isInteger(rawSteps) && rawSteps > 0 ? rawSteps : GENSYN_DEMO_STEPS;
|
|
1969
|
+
return { seed, steps };
|
|
1970
|
+
}
|
|
1971
|
+
var reproduceGensynCompute = (spec2) => {
|
|
1972
|
+
const { seed, steps } = readComputeParams(spec2);
|
|
1973
|
+
const states = [];
|
|
1974
|
+
let x = BigInt(seed) % LCG_MOD;
|
|
1975
|
+
if (x < 0n) {
|
|
1976
|
+
x += LCG_MOD;
|
|
1977
|
+
}
|
|
1978
|
+
for (let i = 0; i < steps; i++) {
|
|
1979
|
+
x = (LCG_MULT * x + LCG_INC) % LCG_MOD;
|
|
1980
|
+
states.push(Number(x));
|
|
1981
|
+
}
|
|
1982
|
+
return { seed, steps, states, result: states[states.length - 1] ?? seed };
|
|
1983
|
+
};
|
|
1984
|
+
function gensynDeliverable(exec) {
|
|
1985
|
+
return canonicalize({ result: exec.result, steps: exec.steps });
|
|
1986
|
+
}
|
|
1987
|
+
function commitmentOf(base) {
|
|
1988
|
+
return sha256Hex6(
|
|
1989
|
+
[base.scheme, base.specHash, base.outputHash, String(base.steps), base.transcriptHash].join(
|
|
1990
|
+
"\n"
|
|
1991
|
+
)
|
|
1992
|
+
);
|
|
1993
|
+
}
|
|
1994
|
+
function buildReeProof(spec2, exec, content) {
|
|
1995
|
+
const base = {
|
|
1996
|
+
scheme: GENSYN_REE_SCHEME,
|
|
1997
|
+
specHash: sha256Hex6(canonicalize(spec2)),
|
|
1998
|
+
outputHash: sha256Hex6(content),
|
|
1999
|
+
steps: exec.steps,
|
|
2000
|
+
transcriptHash: sha256Hex6(canonicalize(exec.states))
|
|
2001
|
+
};
|
|
2002
|
+
return { ...base, commitment: commitmentOf(base) };
|
|
2003
|
+
}
|
|
2004
|
+
var HEX_64 = /^[0-9a-f]{64}$/;
|
|
2005
|
+
function isHex64(v) {
|
|
2006
|
+
return typeof v === "string" && HEX_64.test(v);
|
|
2007
|
+
}
|
|
2008
|
+
function malformed(reason) {
|
|
2009
|
+
return { valid: false, reason: `malformed: ${reason}` };
|
|
2010
|
+
}
|
|
2011
|
+
function invalid(reason) {
|
|
2012
|
+
return { valid: false, reason: `invalid: ${reason}` };
|
|
2013
|
+
}
|
|
2014
|
+
function verifyReeProof(proof, ctx) {
|
|
2015
|
+
if (typeof proof !== "object" || proof === null) {
|
|
2016
|
+
return malformed("proof is not an object");
|
|
2017
|
+
}
|
|
2018
|
+
const p = proof;
|
|
2019
|
+
if (p.scheme !== GENSYN_REE_SCHEME) {
|
|
2020
|
+
return malformed(`scheme must be "${GENSYN_REE_SCHEME}"`);
|
|
2021
|
+
}
|
|
2022
|
+
if (!isHex64(p.specHash)) {
|
|
2023
|
+
return malformed("specHash must be a 64-char lowercase hex string");
|
|
2024
|
+
}
|
|
2025
|
+
if (!isHex64(p.outputHash)) {
|
|
2026
|
+
return malformed("outputHash must be a 64-char lowercase hex string");
|
|
2027
|
+
}
|
|
2028
|
+
if (!isHex64(p.transcriptHash)) {
|
|
2029
|
+
return malformed("transcriptHash must be a 64-char lowercase hex string");
|
|
2030
|
+
}
|
|
2031
|
+
if (!isHex64(p.commitment)) {
|
|
2032
|
+
return malformed("commitment must be a 64-char lowercase hex string");
|
|
2033
|
+
}
|
|
2034
|
+
if (typeof p.steps !== "number" || !Number.isInteger(p.steps) || p.steps <= 0) {
|
|
2035
|
+
return malformed("steps must be a positive integer");
|
|
2036
|
+
}
|
|
2037
|
+
const base = {
|
|
2038
|
+
scheme: GENSYN_REE_SCHEME,
|
|
2039
|
+
specHash: p.specHash,
|
|
2040
|
+
outputHash: p.outputHash,
|
|
2041
|
+
steps: p.steps,
|
|
2042
|
+
transcriptHash: p.transcriptHash
|
|
2043
|
+
};
|
|
2044
|
+
if (commitmentOf(base) !== p.commitment) {
|
|
2045
|
+
return invalid("commitment does not bind the proof fields (tampered)");
|
|
2046
|
+
}
|
|
2047
|
+
if (sha256Hex6(ctx.content) !== p.outputHash) {
|
|
2048
|
+
return invalid("outputHash does not match the delivered content");
|
|
2049
|
+
}
|
|
2050
|
+
if (sha256Hex6(canonicalize(ctx.spec)) !== p.specHash) {
|
|
2051
|
+
return invalid("specHash does not match the commissioned task spec");
|
|
2052
|
+
}
|
|
2053
|
+
const exec = (ctx.reproduce ?? reproduceGensynCompute)(ctx.spec);
|
|
2054
|
+
if (p.steps !== exec.steps) {
|
|
2055
|
+
return invalid("steps does not match the reproduced execution");
|
|
2056
|
+
}
|
|
2057
|
+
if (sha256Hex6(canonicalize(exec.states)) !== p.transcriptHash) {
|
|
2058
|
+
return invalid("transcriptHash is not reproducible from the task spec");
|
|
2059
|
+
}
|
|
2060
|
+
if (sha256Hex6(gensynDeliverable(exec)) !== p.outputHash) {
|
|
2061
|
+
return invalid("outputHash does not match the reproduced deliverable");
|
|
2062
|
+
}
|
|
2063
|
+
return {
|
|
2064
|
+
valid: true,
|
|
2065
|
+
reason: "REE proof verified: commitment, spec, transcript, and the reproduced deliverable all bind to the delivered content."
|
|
2066
|
+
};
|
|
2067
|
+
}
|
|
2068
|
+
function gensynAutoRelease(bundle, ctx) {
|
|
2069
|
+
const verdict = verifyReeProof(bundle.proof, {
|
|
2070
|
+
spec: ctx.spec,
|
|
2071
|
+
content: bundle.content,
|
|
2072
|
+
...ctx.reproduce !== void 0 && { reproduce: ctx.reproduce }
|
|
2073
|
+
});
|
|
2074
|
+
return { release: verdict.valid, reason: verdict.reason };
|
|
2075
|
+
}
|
|
2076
|
+
var gensynDemoJobSpec = {
|
|
2077
|
+
version: "1.0.0",
|
|
2078
|
+
taskType: "custom",
|
|
2079
|
+
description: "Run the reference reproducible compute (a fixed LCG walk from the seed in metadata) and return its final result. Acceptance is a valid gensyn-ree-v1 REE proof: the buyer recomputes the transcript and confirms the proof binds the delivered content, this spec, and the reproducible execution. The worker's self-reported result is never trusted on its own.",
|
|
2080
|
+
acceptanceCriteria: [
|
|
2081
|
+
{
|
|
2082
|
+
id: "ac-ree-proof",
|
|
2083
|
+
description: "A valid gensyn-ree-v1 proof binding the delivered content, this spec, and a reproducible transcript.",
|
|
2084
|
+
required: true
|
|
2085
|
+
}
|
|
2086
|
+
],
|
|
2087
|
+
deliverableFormat: { type: "json" },
|
|
2088
|
+
metadata: {
|
|
2089
|
+
demo: GENSYN_DEMO_MARKER,
|
|
2090
|
+
seed: GENSYN_DEMO_SEED,
|
|
2091
|
+
steps: GENSYN_DEMO_STEPS
|
|
2092
|
+
}
|
|
2093
|
+
};
|
|
2094
|
+
var defaultPin5 = (content) => Promise.resolve(`ipfs://gensyn-${sha256Hex6(content).slice(0, 16)}`);
|
|
2095
|
+
function createGensynAdapter(config = {}) {
|
|
2096
|
+
const runCompute = config.runCompute ?? reproduceGensynCompute;
|
|
2097
|
+
const pin = config.pin ?? defaultPin5;
|
|
2098
|
+
const checkHealth = config.checkHealth ?? (() => Promise.resolve(true));
|
|
2099
|
+
const capabilities = config.capabilities ?? [...GENSYN_CAPABILITIES];
|
|
2100
|
+
return {
|
|
2101
|
+
id: GENSYN_ADAPTER_ID,
|
|
2102
|
+
capabilities,
|
|
2103
|
+
healthCheck: checkHealth,
|
|
2104
|
+
async execute(spec2) {
|
|
2105
|
+
const exec = runCompute(spec2);
|
|
2106
|
+
const content = gensynDeliverable(exec);
|
|
2107
|
+
const contentHash = sha256Hex6(content);
|
|
2108
|
+
const proof = buildReeProof(spec2, exec, content);
|
|
2109
|
+
const proofUri = await pin(content);
|
|
2110
|
+
return {
|
|
2111
|
+
content,
|
|
2112
|
+
contentHash,
|
|
2113
|
+
proofUri,
|
|
2114
|
+
metadata: { adapter: GENSYN_ADAPTER_ID, reeProof: proof }
|
|
2115
|
+
};
|
|
2116
|
+
}
|
|
2117
|
+
};
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
// src/adapters/zkml/index.ts
|
|
2121
|
+
var import_crypto8 = require("crypto");
|
|
2122
|
+
var ZKML_ADAPTER_ID = "zkml";
|
|
2123
|
+
var ZKML_CAPABILITIES = ["zk-verified-inference"];
|
|
2124
|
+
var ZKML_DEMO_MARKER = "zkml-demo-v1";
|
|
2125
|
+
var ZKML_PROOF_SCHEME = "escro-zkml-ref-v1";
|
|
2126
|
+
var ZKML_PROOF_URI_PREFIX = "zkml-proof:";
|
|
2127
|
+
function sha256Hex7(content) {
|
|
2128
|
+
return (0, import_crypto8.createHash)("sha256").update(content).digest("hex");
|
|
2129
|
+
}
|
|
2130
|
+
function computeZkmlVerificationTag(publicInputs) {
|
|
2131
|
+
return sha256Hex7(
|
|
2132
|
+
canonicalize({
|
|
2133
|
+
scheme: publicInputs.scheme,
|
|
2134
|
+
modelCommitment: publicInputs.modelCommitment,
|
|
2135
|
+
inputCommitment: publicInputs.inputCommitment,
|
|
2136
|
+
outputHash: publicInputs.outputHash
|
|
2137
|
+
})
|
|
2138
|
+
);
|
|
2139
|
+
}
|
|
2140
|
+
function buildZkmlProof(args) {
|
|
2141
|
+
const publicInputs = {
|
|
2142
|
+
scheme: args.scheme ?? ZKML_PROOF_SCHEME,
|
|
2143
|
+
modelCommitment: args.modelCommitment,
|
|
2144
|
+
inputCommitment: args.inputCommitment,
|
|
2145
|
+
outputHash: args.outputHash
|
|
2146
|
+
};
|
|
2147
|
+
return { ...publicInputs, verificationTag: computeZkmlVerificationTag(publicInputs) };
|
|
2148
|
+
}
|
|
2149
|
+
function encodeZkmlProofUri(proof) {
|
|
2150
|
+
return ZKML_PROOF_URI_PREFIX + Buffer.from(canonicalize(proof), "utf8").toString("base64url");
|
|
2151
|
+
}
|
|
2152
|
+
function isProofShaped(value) {
|
|
2153
|
+
if (typeof value !== "object" || value === null) {
|
|
2154
|
+
return false;
|
|
2155
|
+
}
|
|
2156
|
+
const p = value;
|
|
2157
|
+
return typeof p.scheme === "string" && typeof p.modelCommitment === "string" && typeof p.inputCommitment === "string" && typeof p.outputHash === "string" && typeof p.verificationTag === "string";
|
|
2158
|
+
}
|
|
2159
|
+
function decodeZkmlProofUri(uri) {
|
|
2160
|
+
if (!uri.startsWith(ZKML_PROOF_URI_PREFIX)) {
|
|
2161
|
+
return null;
|
|
2162
|
+
}
|
|
2163
|
+
const encoded = uri.slice(ZKML_PROOF_URI_PREFIX.length);
|
|
2164
|
+
let parsed;
|
|
2165
|
+
try {
|
|
2166
|
+
parsed = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
|
|
2167
|
+
} catch {
|
|
2168
|
+
return null;
|
|
2169
|
+
}
|
|
2170
|
+
return isProofShaped(parsed) ? parsed : null;
|
|
2171
|
+
}
|
|
2172
|
+
function verifyZkmlProof(proof, expected) {
|
|
2173
|
+
if (!isProofShaped(proof)) {
|
|
2174
|
+
return { valid: false, category: "malformed", reason: "proof is missing required fields" };
|
|
2175
|
+
}
|
|
2176
|
+
if (proof.scheme !== ZKML_PROOF_SCHEME) {
|
|
2177
|
+
return {
|
|
2178
|
+
valid: false,
|
|
2179
|
+
category: "malformed",
|
|
2180
|
+
reason: `unsupported proof scheme "${proof.scheme}"`
|
|
2181
|
+
};
|
|
2182
|
+
}
|
|
2183
|
+
if (proof.verificationTag !== computeZkmlVerificationTag(proof)) {
|
|
2184
|
+
return {
|
|
2185
|
+
valid: false,
|
|
2186
|
+
category: "invalid",
|
|
2187
|
+
reason: "verification tag does not recompute (public inputs altered after the tag was computed)"
|
|
2188
|
+
};
|
|
2189
|
+
}
|
|
2190
|
+
if (proof.outputHash !== expected.contentHash) {
|
|
2191
|
+
return {
|
|
2192
|
+
valid: false,
|
|
2193
|
+
category: "invalid",
|
|
2194
|
+
reason: "proof attests a different output than the submitted deliverable"
|
|
2195
|
+
};
|
|
2196
|
+
}
|
|
2197
|
+
if (expected.modelCommitment !== void 0 && proof.modelCommitment !== expected.modelCommitment) {
|
|
2198
|
+
return { valid: false, category: "invalid", reason: "proof attests a different model" };
|
|
2199
|
+
}
|
|
2200
|
+
return { valid: true };
|
|
2201
|
+
}
|
|
2202
|
+
function verifyZkmlProofUri(uri, expected) {
|
|
2203
|
+
if (typeof uri !== "string" || uri === "") {
|
|
2204
|
+
return { valid: false, category: "malformed", reason: "no proof URI attached" };
|
|
2205
|
+
}
|
|
2206
|
+
const proof = decodeZkmlProofUri(uri);
|
|
2207
|
+
if (proof === null) {
|
|
2208
|
+
return {
|
|
2209
|
+
valid: false,
|
|
2210
|
+
category: "malformed",
|
|
2211
|
+
reason: "proof URI is not a decodable inline zkml proof"
|
|
2212
|
+
};
|
|
2213
|
+
}
|
|
2214
|
+
return verifyZkmlProof(proof, expected);
|
|
2215
|
+
}
|
|
2216
|
+
async function resolveZkmlProof(uri, fetchProof) {
|
|
2217
|
+
if (typeof uri !== "string" || uri === "") {
|
|
2218
|
+
return null;
|
|
2219
|
+
}
|
|
2220
|
+
if (uri.startsWith(ZKML_PROOF_URI_PREFIX)) {
|
|
2221
|
+
return decodeZkmlProofUri(uri);
|
|
2222
|
+
}
|
|
2223
|
+
return fetchProof ? await fetchProof(uri) : null;
|
|
2224
|
+
}
|
|
2225
|
+
function zkmlProofAutoRelease(opts) {
|
|
2226
|
+
const verify = opts.verify ?? verifyZkmlProof;
|
|
2227
|
+
return {
|
|
2228
|
+
name: "zkml-proof-auto-release",
|
|
2229
|
+
async evaluate(d) {
|
|
2230
|
+
const recomputed = sha256Hex7(await opts.fetchContent(d));
|
|
2231
|
+
if (recomputed !== d.contentHash) {
|
|
2232
|
+
return {
|
|
2233
|
+
accept: false,
|
|
2234
|
+
dispute: true,
|
|
2235
|
+
reason: "delivered artifact does not hash to the submitted contentHash (tampered or wrong)"
|
|
2236
|
+
};
|
|
2237
|
+
}
|
|
2238
|
+
const proof = await resolveZkmlProof(d.proofUri, opts.fetchProof);
|
|
2239
|
+
if (proof === null) {
|
|
2240
|
+
return { accept: false, dispute: true, reason: "no decodable zkml proof attached" };
|
|
2241
|
+
}
|
|
2242
|
+
const verdict = verify(proof, {
|
|
2243
|
+
contentHash: recomputed,
|
|
2244
|
+
...opts.modelCommitment !== void 0 && { modelCommitment: opts.modelCommitment }
|
|
2245
|
+
});
|
|
2246
|
+
if (verdict.valid) {
|
|
2247
|
+
return {
|
|
2248
|
+
accept: true,
|
|
2249
|
+
dispute: false,
|
|
2250
|
+
reason: "zkml proof verified against the re-hashed deliverable"
|
|
2251
|
+
};
|
|
2252
|
+
}
|
|
2253
|
+
return {
|
|
2254
|
+
accept: false,
|
|
2255
|
+
dispute: true,
|
|
2256
|
+
reason: `zkml proof ${verdict.category}: ${verdict.reason}`
|
|
2257
|
+
};
|
|
2258
|
+
}
|
|
2259
|
+
};
|
|
2260
|
+
}
|
|
2261
|
+
var zkmlDemoJobSpec = {
|
|
2262
|
+
version: "1.0.0",
|
|
2263
|
+
taskType: "custom",
|
|
2264
|
+
description: "Run the canned zk-verified inference demo: classify the fixed demo input and attach a proof that the output is the correct result of the committed model on the committed input. Acceptance is deterministic and recomputed by the buyer: the attached proof must verify against the submitted deliverable's contentHash.",
|
|
2265
|
+
acceptanceCriteria: [
|
|
2266
|
+
{
|
|
2267
|
+
id: "ac-proof-verifies",
|
|
2268
|
+
description: "The attached proof verifies against the submitted deliverable's contentHash, recomputed by the buyer.",
|
|
2269
|
+
required: true
|
|
2270
|
+
}
|
|
2271
|
+
],
|
|
2272
|
+
deliverableFormat: { type: "json" },
|
|
2273
|
+
metadata: { demo: ZKML_DEMO_MARKER }
|
|
2274
|
+
};
|
|
2275
|
+
var DEMO_MODEL_COMMITMENT = sha256Hex7("zkml-demo-classifier-v1");
|
|
2276
|
+
var DEMO_INPUT_COMMITMENT = sha256Hex7("zkml-demo-input-v1");
|
|
2277
|
+
function solveZkmlDemo() {
|
|
2278
|
+
return canonicalize({
|
|
2279
|
+
model: "zkml-demo-classifier-v1",
|
|
2280
|
+
output: { label: "cat", confidence: 0.98 }
|
|
2281
|
+
});
|
|
2282
|
+
}
|
|
2283
|
+
function referencePlaceholderOutput(spec2) {
|
|
2284
|
+
return {
|
|
2285
|
+
content: canonicalize({
|
|
2286
|
+
note: "reference stub - replace the runInference port with a real zk-verified inference call.",
|
|
2287
|
+
taskType: spec2.taskType
|
|
2288
|
+
}),
|
|
2289
|
+
modelCommitment: sha256Hex7("zkml-reference-stub-model"),
|
|
2290
|
+
inputCommitment: sha256Hex7(canonicalize(spec2))
|
|
2291
|
+
};
|
|
2292
|
+
}
|
|
2293
|
+
var defaultRunInference = (spec2) => spec2.metadata.demo === ZKML_DEMO_MARKER ? {
|
|
2294
|
+
content: solveZkmlDemo(),
|
|
2295
|
+
modelCommitment: DEMO_MODEL_COMMITMENT,
|
|
2296
|
+
inputCommitment: DEMO_INPUT_COMMITMENT
|
|
2297
|
+
} : referencePlaceholderOutput(spec2);
|
|
2298
|
+
function createZkmlAdapter(config = {}) {
|
|
2299
|
+
const id = config.id ?? ZKML_ADAPTER_ID;
|
|
2300
|
+
const capabilities = config.capabilities ?? [...ZKML_CAPABILITIES];
|
|
2301
|
+
const runInference = config.runInference ?? defaultRunInference;
|
|
2302
|
+
const prove = config.prove ?? ((args) => buildZkmlProof({
|
|
2303
|
+
modelCommitment: args.output.modelCommitment,
|
|
2304
|
+
inputCommitment: args.output.inputCommitment,
|
|
2305
|
+
outputHash: args.outputHash
|
|
2306
|
+
}));
|
|
2307
|
+
const pin = config.pin ?? ((proof) => encodeZkmlProofUri(proof));
|
|
2308
|
+
const checkHealth = config.checkHealth ?? (() => Promise.resolve(true));
|
|
2309
|
+
return {
|
|
2310
|
+
id,
|
|
2311
|
+
capabilities,
|
|
2312
|
+
async execute(spec2) {
|
|
2313
|
+
const output = await runInference(spec2);
|
|
2314
|
+
const contentHash = sha256Hex7(output.content);
|
|
2315
|
+
const proof = await prove({ spec: spec2, output, outputHash: contentHash });
|
|
2316
|
+
const proofUri = await pin(proof);
|
|
2317
|
+
return {
|
|
2318
|
+
content: output.content,
|
|
2319
|
+
contentHash,
|
|
2320
|
+
proofUri,
|
|
2321
|
+
metadata: {
|
|
2322
|
+
adapterId: id,
|
|
2323
|
+
scheme: proof.scheme,
|
|
2324
|
+
modelCommitment: proof.modelCommitment
|
|
2325
|
+
}
|
|
2326
|
+
};
|
|
2327
|
+
},
|
|
2328
|
+
healthCheck() {
|
|
2329
|
+
return checkHealth();
|
|
2330
|
+
}
|
|
2331
|
+
};
|
|
2332
|
+
}
|
|
491
2333
|
// Annotate the CommonJS export names for ESM import in node:
|
|
492
2334
|
0 && (module.exports = {
|
|
2335
|
+
AKASH_ADAPTER_ID,
|
|
2336
|
+
AKASH_CAPABILITIES,
|
|
2337
|
+
AKASH_DEMO_ACCEPT_FLOOR,
|
|
2338
|
+
AKASH_DEMO_MARKER,
|
|
2339
|
+
AdapterContractError,
|
|
2340
|
+
ApiClient,
|
|
2341
|
+
ENABLED_RAILS,
|
|
493
2342
|
Escro,
|
|
2343
|
+
EscroConfirmError,
|
|
494
2344
|
EscroError,
|
|
2345
|
+
EscroRailNotEnabledError,
|
|
495
2346
|
EscrowNotFoundError,
|
|
2347
|
+
EscrowPaymentRouter,
|
|
2348
|
+
EscrowState,
|
|
496
2349
|
EscrowStateError,
|
|
497
2350
|
EscrowTimeoutError,
|
|
498
|
-
|
|
2351
|
+
GEN404_ADAPTER_ID,
|
|
2352
|
+
GEN404_CAPABILITIES,
|
|
2353
|
+
GEN404_DEMO_EVAL_FLOOR,
|
|
2354
|
+
GEN404_DEMO_MARKER,
|
|
2355
|
+
GENSYN_ADAPTER_ID,
|
|
2356
|
+
GENSYN_CAPABILITIES,
|
|
2357
|
+
GENSYN_DEMO_MARKER,
|
|
2358
|
+
GENSYN_REE_SCHEME,
|
|
2359
|
+
GRADIENTS_ADAPTER_ID,
|
|
2360
|
+
GRADIENTS_CAPABILITIES,
|
|
2361
|
+
GRADIENTS_DEMO_EVAL_FLOOR,
|
|
2362
|
+
GRADIENTS_DEMO_MARKER,
|
|
2363
|
+
LIUM_ADAPTER_ID,
|
|
2364
|
+
LIUM_CAPABILITIES,
|
|
2365
|
+
LIUM_DEMO_ACCEPT_FLOOR,
|
|
2366
|
+
LIUM_DEMO_MARKER,
|
|
2367
|
+
RAILS,
|
|
2368
|
+
RAILS_DOCS_URL,
|
|
2369
|
+
REFERENCE_MICRO_USDC_PER_AKT,
|
|
2370
|
+
REFERENCE_MICRO_USDC_PER_RENDER,
|
|
2371
|
+
RENDER_ADAPTER_ID,
|
|
2372
|
+
RENDER_CAPABILITIES,
|
|
2373
|
+
RENDER_DEMO_ACCEPT_FLOOR,
|
|
2374
|
+
RENDER_DEMO_MARKER,
|
|
2375
|
+
RIDGES_ADAPTER_ID,
|
|
2376
|
+
RIDGES_CAPABILITIES,
|
|
2377
|
+
RIDGES_DEMO_COVERAGE_TARGET,
|
|
2378
|
+
RIDGES_DEMO_MARKER,
|
|
2379
|
+
ReputationUnavailableError,
|
|
2380
|
+
UnauthorizedError,
|
|
2381
|
+
ZKML_ADAPTER_ID,
|
|
2382
|
+
ZKML_CAPABILITIES,
|
|
2383
|
+
ZKML_DEMO_MARKER,
|
|
2384
|
+
ZKML_PROOF_SCHEME,
|
|
2385
|
+
ZKML_PROOF_URI_PREFIX,
|
|
2386
|
+
akashDemoJobSpec,
|
|
2387
|
+
akt,
|
|
2388
|
+
assertServiceAdapterContract,
|
|
2389
|
+
buildReeProof,
|
|
2390
|
+
buildZkmlProof,
|
|
2391
|
+
computeAkashDemoArtifact,
|
|
2392
|
+
computeLiumDemoArtifact,
|
|
2393
|
+
computeRenderDemoArtifact,
|
|
2394
|
+
create404genAdapter,
|
|
2395
|
+
createAkashAdapter,
|
|
2396
|
+
createGensynAdapter,
|
|
2397
|
+
createGradientsAdapter,
|
|
2398
|
+
createLiumAdapter,
|
|
2399
|
+
createRenderAdapter,
|
|
2400
|
+
createRidgesAdapter,
|
|
2401
|
+
createZkmlAdapter,
|
|
2402
|
+
decodeZkmlProofUri,
|
|
2403
|
+
encodeZkmlProofUri,
|
|
2404
|
+
gen404DemoJobSpec,
|
|
2405
|
+
gensynAutoRelease,
|
|
2406
|
+
gensynDemoJobSpec,
|
|
2407
|
+
gradientsDemoJobSpec,
|
|
2408
|
+
liumDemoJobSpec,
|
|
2409
|
+
renderAmount,
|
|
2410
|
+
renderDemoJobSpec,
|
|
2411
|
+
reproduceGensynCompute,
|
|
2412
|
+
ridgesDemoJobSpec,
|
|
2413
|
+
score404genDemoDeliverable,
|
|
2414
|
+
scoreAkashDemoDeliverable,
|
|
2415
|
+
scoreGradientsDemoDeliverable,
|
|
2416
|
+
scoreLiumDemoDeliverable,
|
|
2417
|
+
scoreRenderDemoDeliverable,
|
|
2418
|
+
scoreRidgesDemoDeliverable,
|
|
2419
|
+
serviceResultToSubmitParams,
|
|
2420
|
+
solveAkashDemo,
|
|
2421
|
+
solveGen404Demo,
|
|
2422
|
+
solveGradientsDemo,
|
|
2423
|
+
solveLiumDemo,
|
|
2424
|
+
solveRenderDemo,
|
|
2425
|
+
solveRidgesDemo,
|
|
2426
|
+
solveZkmlDemo,
|
|
2427
|
+
tao,
|
|
2428
|
+
verifyReeProof,
|
|
2429
|
+
verifyZkmlProof,
|
|
2430
|
+
verifyZkmlProofUri,
|
|
2431
|
+
zkmlDemoJobSpec,
|
|
2432
|
+
zkmlProofAutoRelease
|
|
499
2433
|
});
|
|
500
2434
|
//# sourceMappingURL=index.cjs.map
|