@t2000/sdk 5.16.0 → 5.18.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/dist/index.cjs +212 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -1
- package/dist/index.d.ts +50 -1
- package/dist/index.js +212 -3
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/dist/index.cjs
CHANGED
|
@@ -13,6 +13,9 @@ var cryptography = require('@mysten/sui/cryptography');
|
|
|
13
13
|
var promises = require('fs/promises');
|
|
14
14
|
var path = require('path');
|
|
15
15
|
var os = require('os');
|
|
16
|
+
var secp256k1 = require('@noble/curves/secp256k1');
|
|
17
|
+
var sha256 = require('@noble/hashes/sha256');
|
|
18
|
+
var utils$1 = require('@noble/hashes/utils');
|
|
16
19
|
var fs = require('fs');
|
|
17
20
|
var suins = require('@mysten/suins');
|
|
18
21
|
|
|
@@ -1226,10 +1229,10 @@ async function finalize(response, opts) {
|
|
|
1226
1229
|
return { status: response.status, body: body2, paid: opts.paid };
|
|
1227
1230
|
}
|
|
1228
1231
|
async function makeGrpcBuildClient(client) {
|
|
1229
|
-
const { SuiGrpcClient:
|
|
1232
|
+
const { SuiGrpcClient: SuiGrpcClient3 } = await import('@mysten/sui/grpc');
|
|
1230
1233
|
const network = client.network === "testnet" ? "testnet" : "mainnet";
|
|
1231
1234
|
const baseUrl = network === "testnet" ? "https://fullnode.testnet.sui.io" : "https://fullnode.mainnet.sui.io";
|
|
1232
|
-
return new
|
|
1235
|
+
return new SuiGrpcClient3({ baseUrl, network });
|
|
1233
1236
|
}
|
|
1234
1237
|
function atomicToHuman(raw, decimals) {
|
|
1235
1238
|
return Number(raw) / 10 ** decimals;
|
|
@@ -2030,6 +2033,207 @@ async function listModels(opts) {
|
|
|
2030
2033
|
privacy: m.privacy ?? m.privacy_tier
|
|
2031
2034
|
}));
|
|
2032
2035
|
}
|
|
2036
|
+
var RECEIPT_ANCHORED_SUFFIX = "::anchor::ReceiptAnchored";
|
|
2037
|
+
function fullnodeUrl(network) {
|
|
2038
|
+
return network === "testnet" ? "https://fullnode.testnet.sui.io" : "https://fullnode.mainnet.sui.io";
|
|
2039
|
+
}
|
|
2040
|
+
function jcs(value) {
|
|
2041
|
+
if (value === null) {
|
|
2042
|
+
return "null";
|
|
2043
|
+
}
|
|
2044
|
+
if (typeof value === "boolean") {
|
|
2045
|
+
return value ? "true" : "false";
|
|
2046
|
+
}
|
|
2047
|
+
if (typeof value === "number") {
|
|
2048
|
+
if (!Number.isInteger(value)) {
|
|
2049
|
+
throw new Error("JCS: non-integer number");
|
|
2050
|
+
}
|
|
2051
|
+
return String(value);
|
|
2052
|
+
}
|
|
2053
|
+
if (typeof value === "string") {
|
|
2054
|
+
return JSON.stringify(value);
|
|
2055
|
+
}
|
|
2056
|
+
if (Array.isArray(value)) {
|
|
2057
|
+
return `[${value.map(jcs).join(",")}]`;
|
|
2058
|
+
}
|
|
2059
|
+
const keys = Object.keys(value).sort();
|
|
2060
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${jcs(value[k])}`).join(",")}}`;
|
|
2061
|
+
}
|
|
2062
|
+
function verifyReceiptSignature(receipt, signingKeyHex) {
|
|
2063
|
+
try {
|
|
2064
|
+
const sig = receipt.signature;
|
|
2065
|
+
if (sig?.algo !== "ecdsa-secp256k1" || !sig.value) {
|
|
2066
|
+
return false;
|
|
2067
|
+
}
|
|
2068
|
+
const canonical = {
|
|
2069
|
+
api_version: receipt.api_version ?? "",
|
|
2070
|
+
receipt_id: receipt.receipt_id ?? "",
|
|
2071
|
+
chat_id: receipt.chat_id ?? null,
|
|
2072
|
+
workload_id: receipt.workload_id ?? "",
|
|
2073
|
+
workload_keyset_digest: receipt.workload_keyset_digest ?? "",
|
|
2074
|
+
endpoint: receipt.endpoint ?? "",
|
|
2075
|
+
method: receipt.method ?? "",
|
|
2076
|
+
served_at: receipt.served_at ?? 0,
|
|
2077
|
+
event_log: receipt.event_log ?? [],
|
|
2078
|
+
signature: { algo: sig.algo, key_id: sig.key_id ?? "" }
|
|
2079
|
+
};
|
|
2080
|
+
const prehash = sha256.sha256(new TextEncoder().encode(jcs(canonical)));
|
|
2081
|
+
const sigBytes = utils$1.hexToBytes(sig.value);
|
|
2082
|
+
if (sigBytes.length !== 65) {
|
|
2083
|
+
return false;
|
|
2084
|
+
}
|
|
2085
|
+
let v = sigBytes[64];
|
|
2086
|
+
if (v >= 27 && v <= 30) {
|
|
2087
|
+
v -= 27;
|
|
2088
|
+
}
|
|
2089
|
+
if (v > 3) {
|
|
2090
|
+
return false;
|
|
2091
|
+
}
|
|
2092
|
+
const recovered = secp256k1.secp256k1.Signature.fromCompact(sigBytes.slice(0, 64)).addRecoveryBit(v).recoverPublicKey(prehash).toHex(false);
|
|
2093
|
+
const endorsed = utils$1.bytesToHex(utils$1.hexToBytes(signingKeyHex.replace(/^0x/, "")));
|
|
2094
|
+
return recovered.toLowerCase() === endorsed.toLowerCase();
|
|
2095
|
+
} catch {
|
|
2096
|
+
return false;
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
async function verifyReceipt(receiptId, opts = {}) {
|
|
2100
|
+
const base = opts.apiBase ?? DEFAULT_API_BASE;
|
|
2101
|
+
const network = opts.network ?? "mainnet";
|
|
2102
|
+
const checks = [];
|
|
2103
|
+
let receipt = null;
|
|
2104
|
+
try {
|
|
2105
|
+
const res = await fetch(`${base}/aci/receipts/${encodeURIComponent(receiptId)}`);
|
|
2106
|
+
if (res.ok) {
|
|
2107
|
+
receipt = await res.json();
|
|
2108
|
+
}
|
|
2109
|
+
} catch {
|
|
2110
|
+
}
|
|
2111
|
+
if (!receipt?.event_log) {
|
|
2112
|
+
checks.push({
|
|
2113
|
+
name: "Receipt",
|
|
2114
|
+
status: "fail",
|
|
2115
|
+
detail: "receipt not found or malformed",
|
|
2116
|
+
trust: "receipt-asserted"
|
|
2117
|
+
});
|
|
2118
|
+
return { receiptId, verified: false, anchorVerified: false, checks };
|
|
2119
|
+
}
|
|
2120
|
+
const wireHash = receipt.event_log.find((e) => e.type === "response.returned")?.wire_hash;
|
|
2121
|
+
const workloadId = receipt.workload_id;
|
|
2122
|
+
checks.push({
|
|
2123
|
+
name: "Receipt",
|
|
2124
|
+
status: wireHash && workloadId ? "pass" : "fail",
|
|
2125
|
+
detail: wireHash ? `well-formed (${receipt.event_log.length} log entries, workload ${workloadId})` : "missing response wire_hash / workload_id",
|
|
2126
|
+
trust: "receipt-asserted"
|
|
2127
|
+
});
|
|
2128
|
+
const upstreamEv = receipt.event_log.find((e) => e.type === "upstream.verified");
|
|
2129
|
+
const upstreamOk = upstreamEv?.result === "verified";
|
|
2130
|
+
checks.push({
|
|
2131
|
+
name: "Confidential upstream",
|
|
2132
|
+
status: upstreamEv ? upstreamOk ? "pass" : "fail" : "skip",
|
|
2133
|
+
detail: upstreamEv ? `${upstreamEv.provider ?? upstreamEv.upstream_name ?? "upstream"}: ${upstreamEv.result ?? "unknown"}${upstreamEv.tcb_status ? ` (TCB ${upstreamEv.tcb_status})` : ""}` : "no upstream.verified event (routed/non-confidential?)",
|
|
2134
|
+
trust: "receipt-asserted"
|
|
2135
|
+
});
|
|
2136
|
+
let anchorVerified = false;
|
|
2137
|
+
let anchor;
|
|
2138
|
+
let digest;
|
|
2139
|
+
try {
|
|
2140
|
+
const res = await fetch(`${base}/aci/anchor/${encodeURIComponent(receiptId)}`);
|
|
2141
|
+
if (res.ok) {
|
|
2142
|
+
const j = await res.json();
|
|
2143
|
+
digest = j.txDigest;
|
|
2144
|
+
}
|
|
2145
|
+
} catch {
|
|
2146
|
+
}
|
|
2147
|
+
if (!digest) {
|
|
2148
|
+
checks.push({
|
|
2149
|
+
name: "Sui anchor",
|
|
2150
|
+
status: "fail",
|
|
2151
|
+
detail: `no anchor on record \u2014 POST ${base}/aci/anchor/${receiptId} to create one`,
|
|
2152
|
+
trust: "trustless"
|
|
2153
|
+
});
|
|
2154
|
+
} else {
|
|
2155
|
+
try {
|
|
2156
|
+
const client = new grpc.SuiGrpcClient({ baseUrl: fullnodeUrl(network), network });
|
|
2157
|
+
const tx = await client.core.getTransaction({
|
|
2158
|
+
digest,
|
|
2159
|
+
include: { events: true }
|
|
2160
|
+
});
|
|
2161
|
+
const txn = tx.$kind === "Transaction" ? tx.Transaction : tx.FailedTransaction;
|
|
2162
|
+
const ev = (txn.events ?? []).find(
|
|
2163
|
+
(e) => e.eventType.endsWith(RECEIPT_ANCHORED_SUFFIX)
|
|
2164
|
+
);
|
|
2165
|
+
const data = ev?.json ?? {};
|
|
2166
|
+
const onChainReceipt = String(data.receipt_id ?? "");
|
|
2167
|
+
const onChainWire = String(data.wire_hash ?? "");
|
|
2168
|
+
const onChainWorkload = String(data.workload_id ?? "");
|
|
2169
|
+
const matches = onChainReceipt === receiptId && onChainWire === wireHash && onChainWorkload === workloadId;
|
|
2170
|
+
anchorVerified = matches;
|
|
2171
|
+
anchor = {
|
|
2172
|
+
txDigest: digest,
|
|
2173
|
+
anchoredAtMs: data.anchored_at_ms ? String(data.anchored_at_ms) : void 0,
|
|
2174
|
+
anchoredBy: data.anchored_by ? String(data.anchored_by) : void 0,
|
|
2175
|
+
explorer: `https://suiscan.xyz/${network}/tx/${digest}`
|
|
2176
|
+
};
|
|
2177
|
+
checks.push({
|
|
2178
|
+
name: "Sui anchor",
|
|
2179
|
+
status: matches ? "pass" : "fail",
|
|
2180
|
+
detail: matches ? `on-chain ReceiptAnchored matches (wire_hash + workload_id), tx ${digest}` : `on-chain event does NOT match the receipt (wire ${onChainWire || "absent"})`,
|
|
2181
|
+
trust: "trustless"
|
|
2182
|
+
});
|
|
2183
|
+
} catch (e) {
|
|
2184
|
+
checks.push({
|
|
2185
|
+
name: "Sui anchor",
|
|
2186
|
+
status: "fail",
|
|
2187
|
+
detail: `could not read anchor tx ${digest}: ${e instanceof Error ? e.message : "error"}`,
|
|
2188
|
+
trust: "trustless"
|
|
2189
|
+
});
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
let signatureForged = false;
|
|
2193
|
+
let sigStatus = "skip";
|
|
2194
|
+
let sigDetail = "no signature on receipt";
|
|
2195
|
+
if (receipt.signature?.value) {
|
|
2196
|
+
try {
|
|
2197
|
+
const model = opts.model ?? "phala/glm-5.2";
|
|
2198
|
+
const res = await fetch(
|
|
2199
|
+
`${base}/aci/attestation?model=${encodeURIComponent(model)}`
|
|
2200
|
+
);
|
|
2201
|
+
const att = res.ok ? await res.json() : null;
|
|
2202
|
+
if (!att?.signingKey) {
|
|
2203
|
+
sigDetail = "could not fetch the attested keyset to check the signature";
|
|
2204
|
+
} else if (att.workloadId && att.workloadId !== workloadId) {
|
|
2205
|
+
sigDetail = `attested keyset is for a different workload \u2014 pass --model for ${workloadId}`;
|
|
2206
|
+
} else {
|
|
2207
|
+
const ok = verifyReceiptSignature(receipt, att.signingKey);
|
|
2208
|
+
sigStatus = ok ? "pass" : "fail";
|
|
2209
|
+
signatureForged = !ok;
|
|
2210
|
+
sigDetail = ok ? `signed by the attested receipt key (${receipt.signature.key_id ?? "key"})` : "signature does NOT recover the attested receipt key \u2014 forged/altered";
|
|
2211
|
+
}
|
|
2212
|
+
} catch {
|
|
2213
|
+
sigDetail = "signature check errored";
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
checks.push({
|
|
2217
|
+
name: "Receipt signature",
|
|
2218
|
+
status: sigStatus,
|
|
2219
|
+
detail: sigDetail,
|
|
2220
|
+
trust: sigStatus === "skip" ? "roadmap" : "trustless"
|
|
2221
|
+
});
|
|
2222
|
+
return {
|
|
2223
|
+
receiptId,
|
|
2224
|
+
verified: Boolean(wireHash && workloadId) && anchorVerified && !signatureForged,
|
|
2225
|
+
anchorVerified,
|
|
2226
|
+
checks,
|
|
2227
|
+
wireHash,
|
|
2228
|
+
workloadId,
|
|
2229
|
+
upstream: upstreamEv ? {
|
|
2230
|
+
provider: upstreamEv.provider ?? upstreamEv.upstream_name,
|
|
2231
|
+
result: upstreamEv.result,
|
|
2232
|
+
tcbStatus: upstreamEv.tcb_status
|
|
2233
|
+
} : void 0,
|
|
2234
|
+
anchor
|
|
2235
|
+
};
|
|
2236
|
+
}
|
|
2033
2237
|
var DEFAULT_CONFIG_DIR = path.join(os.homedir(), ".t2000");
|
|
2034
2238
|
function resolveConfigPath(configDir) {
|
|
2035
2239
|
return path.join(configDir ?? DEFAULT_CONFIG_DIR, "config.json");
|
|
@@ -2315,6 +2519,11 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
2315
2519
|
async models(opts) {
|
|
2316
2520
|
return listModels(opts);
|
|
2317
2521
|
}
|
|
2522
|
+
/** Verify a confidential response by receipt id — checks the signed receipt
|
|
2523
|
+
* + its trustless on-chain Sui anchor. Fails closed on any mismatch. */
|
|
2524
|
+
async verify(receiptId, opts) {
|
|
2525
|
+
return verifyReceipt(receiptId, opts);
|
|
2526
|
+
}
|
|
2318
2527
|
// -- Swap --
|
|
2319
2528
|
async swap(params) {
|
|
2320
2529
|
this.limits.assert({
|
|
@@ -3202,6 +3411,7 @@ exports.usdcToRaw = usdcToRaw;
|
|
|
3202
3411
|
exports.validateAddress = validateAddress;
|
|
3203
3412
|
exports.validateLabel = validateLabel;
|
|
3204
3413
|
exports.verifyCetusRouteCoinMatch = verifyCetusRouteCoinMatch;
|
|
3414
|
+
exports.verifyReceipt = verifyReceipt;
|
|
3205
3415
|
exports.walletExists = walletExists;
|
|
3206
3416
|
exports.writeLimitsFile = writeLimitsFile;
|
|
3207
3417
|
//# sourceMappingURL=index.cjs.map
|