@dvmkit/sdk 0.4.0-rc.1 → 0.6.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -0
- package/dist/{chunk-TXX4GOUB.js → chunk-7ZUKL4IV.js} +1 -1
- package/dist/chunk-AFV77S4C.js +258 -0
- package/dist/{chunk-2VGWYG4P.js → chunk-B2IROPQF.js} +446 -81
- package/dist/chunk-BQJIFRAZ.js +36 -0
- package/dist/{chunk-PB4FYZET.js → chunk-EQLA4FFY.js} +54 -4
- package/dist/{chunk-74PUEZJ4.js → chunk-GFY5FRX3.js} +1 -1
- package/dist/{chunk-O3QDFPKM.js → chunk-HCLVYZUZ.js} +2 -2
- package/dist/{chunk-K2YX2VRA.js → chunk-HHBDBMEO.js} +170 -43
- package/dist/{chunk-O5AT6S5T.js → chunk-IV6Q7NRW.js} +25 -195
- package/dist/{chunk-W2EQGDF5.js → chunk-LE74UEV7.js} +49 -8
- package/dist/{chunk-CE7XEHYC.js → chunk-N5RZ5HVB.js} +1 -1
- package/dist/chunk-OFVV37XC.js +57 -0
- package/dist/{chunk-5LR6P7WN.js → chunk-OQA7IHI4.js} +133 -213
- package/dist/{chunk-3MHE725Z.js → chunk-PAIM66YA.js} +90 -1
- package/dist/{chunk-6DOGTAHA.js → chunk-QR2EF2NI.js} +9 -43
- package/dist/{chunk-KXZUCCEY.js → chunk-RCM3P2HE.js} +5 -56
- package/dist/{chunk-R4DQZFAL.js → chunk-UOCZXO2Z.js} +3 -3
- package/dist/content-retention-BTGSQRfP.d.ts +78 -0
- package/dist/{credit-menu-B8_p_vUz.d.ts → credit-menu-BAsrRu3z.d.ts} +98 -5
- package/dist/{fx-DuaYHvVn.d.ts → fx-BgDmyTdd.d.ts} +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +4 -3
- package/dist/internal/caller.d.ts +90 -22
- package/dist/internal/caller.js +40 -40
- package/dist/internal/index.js +1 -1
- package/dist/internal/server.d.ts +8 -99
- package/dist/internal/server.js +15 -11
- package/dist/{job-store-D6Rjj5Oi.d.ts → job-store-BVeF5_Jq.d.ts} +1 -1
- package/dist/{lightning-backend-BaeCPmx7.d.ts → lightning-backend-40fIzz1W.d.ts} +1 -1
- package/dist/{postgres-consumed-credential-store-CUQJKRSL.js → postgres-consumed-credential-store-BUVCAPLS.js} +13 -0
- package/dist/{postgres-job-store-VLXLWXUQ.js → postgres-job-store-VHLQBJZC.js} +3 -1
- package/dist/server/index.d.ts +28 -10
- package/dist/server/index.js +24 -10
- package/dist/{step-cache-Bi43A0JN.d.ts → step-cache-CPdH1hei.d.ts} +3 -3
- package/dist/{tempo-lifecycle-OTHV757Y.js → tempo-lifecycle-ZBBLFBMH.js} +5 -4
- package/dist/{tempo-wallet-32JWQZKQ.js → tempo-wallet-B5XXV3YH.js} +1 -1
- package/dist/testing/index.d.ts +9 -2
- package/dist/testing/index.js +6 -3
- package/dist/{usd-AZPVq4eU.d.ts → usd-AWlrwlM5.d.ts} +1 -1
- package/dist/{x402-CEDHGZDI.js → x402-R6HPBY5D.js} +5 -4
- package/package.json +3 -2
- package/recipes/postgres/financial-export-v1.sql +137 -0
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
|
+
computeResultHash,
|
|
2
3
|
costAnchor,
|
|
3
4
|
formatUsd,
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
isDrainReceipt,
|
|
6
|
+
isFundingReceipt,
|
|
7
|
+
msatsToUsd,
|
|
8
|
+
verifyDrainReceipt,
|
|
9
|
+
verifyFundingReceipt,
|
|
10
|
+
verifyReceipt
|
|
11
|
+
} from "./chunk-PAIM66YA.js";
|
|
6
12
|
import {
|
|
7
13
|
AGENT_MNEMONIC_FILE,
|
|
8
14
|
BUILDER_CONFIG_DIR,
|
|
@@ -13,7 +19,7 @@ import {
|
|
|
13
19
|
} from "./chunk-CZULM42S.js";
|
|
14
20
|
import {
|
|
15
21
|
canonicaliseForSigning
|
|
16
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-OFVV37XC.js";
|
|
17
23
|
import {
|
|
18
24
|
callerLoggers
|
|
19
25
|
} from "./chunk-66HGCPBU.js";
|
|
@@ -947,6 +953,112 @@ function generateEphemeralReceiptKey() {
|
|
|
947
953
|
return { secret: bytesToHex(secretKey), pubkey: bytesToHex(publicKey) };
|
|
948
954
|
}
|
|
949
955
|
|
|
956
|
+
// src/lib/wallet-agent/keypair.ts
|
|
957
|
+
import { secp256k1 as secp256k12 } from "@noble/curves/secp256k1.js";
|
|
958
|
+
var PRIVKEY_HEX_RE = /^[0-9a-f]{64}$/i;
|
|
959
|
+
function derivePubkeyFromPrivkey(privkey) {
|
|
960
|
+
if (!PRIVKEY_HEX_RE.test(privkey)) {
|
|
961
|
+
throw new DvmError(
|
|
962
|
+
"invalid_privkey",
|
|
963
|
+
"Privkey must be 64 hex characters (32 bytes).",
|
|
964
|
+
"Provide a valid secp256k1 private key as a 64-char hex string."
|
|
965
|
+
);
|
|
966
|
+
}
|
|
967
|
+
const bytes = hexToBytes2(privkey);
|
|
968
|
+
if (!secp256k12.utils.isValidSecretKey(bytes)) {
|
|
969
|
+
throw new DvmError(
|
|
970
|
+
"invalid_privkey",
|
|
971
|
+
"Privkey is not a valid secp256k1 scalar (zero or out of curve order).",
|
|
972
|
+
"Generate a fresh key with: dvm wallet init"
|
|
973
|
+
);
|
|
974
|
+
}
|
|
975
|
+
const pub = secp256k12.getPublicKey(bytes);
|
|
976
|
+
return bytesToHex2(pub);
|
|
977
|
+
}
|
|
978
|
+
function bytesToHex2(bytes) {
|
|
979
|
+
return Buffer.from(bytes).toString("hex");
|
|
980
|
+
}
|
|
981
|
+
function hexToBytes2(hex) {
|
|
982
|
+
return Uint8Array.from(Buffer.from(hex, "hex"));
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
// src/lib/wallet-agent/sign.ts
|
|
986
|
+
import { schnorr as schnorr2 } from "@noble/curves/secp256k1.js";
|
|
987
|
+
var PUBKEY_COMPRESSED_HEX_RE = /^0[23][0-9a-f]{64}$/i;
|
|
988
|
+
var PUBKEY_XONLY_HEX_RE = /^[0-9a-f]{64}$/i;
|
|
989
|
+
var SIG_HEX_RE = /^[0-9a-f]{128}$/i;
|
|
990
|
+
function signChallenge(privkeyHex, challenge) {
|
|
991
|
+
if (!PRIVKEY_HEX_RE.test(privkeyHex)) {
|
|
992
|
+
throw new DvmError(
|
|
993
|
+
"invalid_privkey",
|
|
994
|
+
"Privkey must be 64 hex characters (32 bytes).",
|
|
995
|
+
"Provide a valid secp256k1 private key as a 64-char hex string."
|
|
996
|
+
);
|
|
997
|
+
}
|
|
998
|
+
if (challenge.length !== 32) {
|
|
999
|
+
throw new DvmError(
|
|
1000
|
+
"invalid_challenge",
|
|
1001
|
+
`Challenge must be exactly 32 bytes; got ${challenge.length}.`,
|
|
1002
|
+
"Pass the SHA-256 digest output (32 bytes)."
|
|
1003
|
+
);
|
|
1004
|
+
}
|
|
1005
|
+
const sig = schnorr2.sign(challenge, hexToBytes3(privkeyHex));
|
|
1006
|
+
return bytesToHex3(sig);
|
|
1007
|
+
}
|
|
1008
|
+
function verifyChallenge(pubkeyHex, challenge, sigHex) {
|
|
1009
|
+
if (!SIG_HEX_RE.test(sigHex)) return false;
|
|
1010
|
+
if (challenge.length !== 32) return false;
|
|
1011
|
+
let xonly;
|
|
1012
|
+
if (PUBKEY_COMPRESSED_HEX_RE.test(pubkeyHex)) {
|
|
1013
|
+
xonly = hexToBytes3(pubkeyHex.slice(2));
|
|
1014
|
+
} else if (PUBKEY_XONLY_HEX_RE.test(pubkeyHex)) {
|
|
1015
|
+
xonly = hexToBytes3(pubkeyHex);
|
|
1016
|
+
} else {
|
|
1017
|
+
return false;
|
|
1018
|
+
}
|
|
1019
|
+
try {
|
|
1020
|
+
return schnorr2.verify(hexToBytes3(sigHex), challenge, xonly);
|
|
1021
|
+
} catch {
|
|
1022
|
+
return false;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
function hexToBytes3(hex) {
|
|
1026
|
+
return Uint8Array.from(Buffer.from(hex, "hex"));
|
|
1027
|
+
}
|
|
1028
|
+
function bytesToHex3(bytes) {
|
|
1029
|
+
return Buffer.from(bytes).toString("hex");
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
// src/lib/cashu/admin-auth.ts
|
|
1033
|
+
import { randomBytes } from "crypto";
|
|
1034
|
+
import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
|
|
1035
|
+
var ADMIN_AUTH_NONCE_TTL_SECONDS = 300;
|
|
1036
|
+
var ADMIN_AUTH_CLOCK_SKEW_SECONDS = 30;
|
|
1037
|
+
var ADMIN_STATE_UNAVAILABLE = "admin_state_unavailable";
|
|
1038
|
+
function buildAdminChallenge(args) {
|
|
1039
|
+
const msg = `dvmkit-admin:${args.method}:${args.path}:${args.dvmId}:${args.nonceHex}:${args.unixSeconds}`;
|
|
1040
|
+
return sha2562(new TextEncoder().encode(msg));
|
|
1041
|
+
}
|
|
1042
|
+
function buildAdminAuthHeader(args) {
|
|
1043
|
+
const nowMs = args.now ? args.now() : Date.now();
|
|
1044
|
+
const unixSeconds = Math.floor(nowMs / 1e3);
|
|
1045
|
+
const nonceBytes = args.randomNonce ? args.randomNonce() : Uint8Array.from(randomBytes(16));
|
|
1046
|
+
const nonceHex = Buffer.from(nonceBytes).toString("hex");
|
|
1047
|
+
const challenge = buildAdminChallenge({
|
|
1048
|
+
method: args.method,
|
|
1049
|
+
path: args.path,
|
|
1050
|
+
dvmId: args.dvmId,
|
|
1051
|
+
nonceHex,
|
|
1052
|
+
unixSeconds
|
|
1053
|
+
});
|
|
1054
|
+
const sigHex = signChallenge(args.privkeyHex, challenge);
|
|
1055
|
+
return {
|
|
1056
|
+
header: `P2PK ${nonceHex}.${unixSeconds}.${sigHex}`,
|
|
1057
|
+
nonceHex,
|
|
1058
|
+
unixSeconds
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
|
|
950
1062
|
// src/lib/builder-identity.ts
|
|
951
1063
|
import {
|
|
952
1064
|
chmodSync as chmodSync3,
|
|
@@ -958,8 +1070,8 @@ import {
|
|
|
958
1070
|
unlinkSync as unlinkSync4,
|
|
959
1071
|
writeFileSync as writeFileSync5
|
|
960
1072
|
} from "fs";
|
|
961
|
-
import { schnorr as
|
|
962
|
-
import { sha256 as
|
|
1073
|
+
import { schnorr as schnorr3 } from "@noble/curves/secp256k1.js";
|
|
1074
|
+
import { sha256 as sha2563 } from "@noble/hashes/sha2.js";
|
|
963
1075
|
|
|
964
1076
|
// src/lib/builder-store.ts
|
|
965
1077
|
import {
|
|
@@ -1064,8 +1176,8 @@ function migrateV1Store(parsed) {
|
|
|
1064
1176
|
|
|
1065
1177
|
// src/lib/builder-identity.ts
|
|
1066
1178
|
function generateIdentity() {
|
|
1067
|
-
const { secretKey, publicKey } =
|
|
1068
|
-
return { pubkey:
|
|
1179
|
+
const { secretKey, publicKey } = schnorr3.keygen();
|
|
1180
|
+
return { pubkey: bytesToHex4(publicKey), secret: bytesToHex4(secretKey) };
|
|
1069
1181
|
}
|
|
1070
1182
|
function loadIdentitySecret() {
|
|
1071
1183
|
if (!existsSync5(BUILDER_KEY_FILE)) return null;
|
|
@@ -1108,7 +1220,7 @@ function writeIdentity(keypair, opts = {}) {
|
|
|
1108
1220
|
setIdentityPubkey(keypair.pubkey);
|
|
1109
1221
|
}
|
|
1110
1222
|
function hashCapabilities(capabilities) {
|
|
1111
|
-
return
|
|
1223
|
+
return bytesToHex4(sha2563(canonicaliseForSigning(capabilities)));
|
|
1112
1224
|
}
|
|
1113
1225
|
function buildAttestation(args) {
|
|
1114
1226
|
return {
|
|
@@ -1121,14 +1233,14 @@ function buildAttestation(args) {
|
|
|
1121
1233
|
};
|
|
1122
1234
|
}
|
|
1123
1235
|
function signAttestation(payload, secretHex) {
|
|
1124
|
-
const secretBytes =
|
|
1236
|
+
const secretBytes = hexToBytes4(secretHex);
|
|
1125
1237
|
const canonicalBytes = canonicaliseForSigning(payload);
|
|
1126
|
-
return
|
|
1238
|
+
return bytesToHex4(schnorr3.sign(canonicalBytes, secretBytes));
|
|
1127
1239
|
}
|
|
1128
1240
|
function verifyAttestation(payload, signatureHex, pubkeyHex) {
|
|
1129
1241
|
try {
|
|
1130
1242
|
const canonicalBytes = canonicaliseForSigning(payload);
|
|
1131
|
-
return
|
|
1243
|
+
return schnorr3.verify(hexToBytes4(signatureHex), canonicalBytes, hexToBytes4(pubkeyHex));
|
|
1132
1244
|
} catch {
|
|
1133
1245
|
return false;
|
|
1134
1246
|
}
|
|
@@ -1151,12 +1263,12 @@ function warnAndTightenLooseKeyMode() {
|
|
|
1151
1263
|
} catch {
|
|
1152
1264
|
}
|
|
1153
1265
|
}
|
|
1154
|
-
function
|
|
1266
|
+
function bytesToHex4(bytes) {
|
|
1155
1267
|
let s = "";
|
|
1156
1268
|
for (const b of bytes) s += b.toString(16).padStart(2, "0");
|
|
1157
1269
|
return s;
|
|
1158
1270
|
}
|
|
1159
|
-
function
|
|
1271
|
+
function hexToBytes4(hex) {
|
|
1160
1272
|
if (hex.length % 2 !== 0) {
|
|
1161
1273
|
throw new Error(`hex string has odd length: ${String(hex.length)}`);
|
|
1162
1274
|
}
|
|
@@ -1167,82 +1279,6 @@ function hexToBytes2(hex) {
|
|
|
1167
1279
|
return out;
|
|
1168
1280
|
}
|
|
1169
1281
|
|
|
1170
|
-
// src/lib/receipt.ts
|
|
1171
|
-
import { schnorr as schnorr3 } from "@noble/curves/secp256k1.js";
|
|
1172
|
-
import { sha256 as sha2563 } from "@noble/hashes/sha2.js";
|
|
1173
|
-
import { bytesToHex as bytesToHex3, hexToBytes as hexToBytes3 } from "@noble/hashes/utils.js";
|
|
1174
|
-
function computeResultHash(summary, artifactContents) {
|
|
1175
|
-
const artifactHashes = artifactContents.map(
|
|
1176
|
-
(content) => bytesToHex3(sha2563(canonicaliseForSigning(content)))
|
|
1177
|
-
);
|
|
1178
|
-
return bytesToHex3(
|
|
1179
|
-
sha2563(
|
|
1180
|
-
canonicaliseForSigning({
|
|
1181
|
-
summary: summary ?? null,
|
|
1182
|
-
artifact_hashes: artifactHashes
|
|
1183
|
-
})
|
|
1184
|
-
)
|
|
1185
|
-
);
|
|
1186
|
-
}
|
|
1187
|
-
function signReceipt(unsigned, secretHex) {
|
|
1188
|
-
const bytes = canonicaliseForSigning(unsigned);
|
|
1189
|
-
const signature = bytesToHex3(schnorr3.sign(bytes, hexToBytes3(secretHex)));
|
|
1190
|
-
return { ...unsigned, signature };
|
|
1191
|
-
}
|
|
1192
|
-
function verifyReceipt(receipt) {
|
|
1193
|
-
try {
|
|
1194
|
-
const { signature, ...unsigned } = receipt;
|
|
1195
|
-
const bytes = canonicaliseForSigning(unsigned);
|
|
1196
|
-
return schnorr3.verify(hexToBytes3(signature), bytes, hexToBytes3(receipt.receipt_pubkey));
|
|
1197
|
-
} catch {
|
|
1198
|
-
return false;
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
function isSignedJobReceipt(value) {
|
|
1202
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1203
|
-
const r = value;
|
|
1204
|
-
const paid = r.paid;
|
|
1205
|
-
return r.v === 1 && typeof r.job_id === "string" && typeof r.dvm_id === "string" && typeof r.dvm === "string" && typeof r.capability === "string" && (r.outcome === "completed" || r.outcome === "failed" || r.outcome === "cancelled") && typeof r.seq === "number" && typeof r.issued_at === "number" && typeof r.receipt_pubkey === "string" && typeof r.signature === "string" && !!paid && typeof paid === "object" && !Array.isArray(paid) && typeof paid.msats === "number";
|
|
1206
|
-
}
|
|
1207
|
-
function signDrainReceipt(unsigned, secretHex) {
|
|
1208
|
-
const bytes = canonicaliseForSigning(unsigned);
|
|
1209
|
-
const signature = bytesToHex3(schnorr3.sign(bytes, hexToBytes3(secretHex)));
|
|
1210
|
-
return { ...unsigned, signature };
|
|
1211
|
-
}
|
|
1212
|
-
function signFundingReceipt(unsigned, secretHex) {
|
|
1213
|
-
const bytes = canonicaliseForSigning(unsigned);
|
|
1214
|
-
const signature = bytesToHex3(schnorr3.sign(bytes, hexToBytes3(secretHex)));
|
|
1215
|
-
return { ...unsigned, signature };
|
|
1216
|
-
}
|
|
1217
|
-
function isFundingReceipt(value) {
|
|
1218
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1219
|
-
const r = value;
|
|
1220
|
-
return r.v === 1 && r.kind === "credit_funding" && typeof r.credit_id === "string" && typeof r.fund_id === "string" && typeof r.funded_micro === "number" && typeof r.balance_after_micro === "number" && typeof r.ledger_seq === "number" && typeof r.issued_at === "number" && typeof r.caller_pubkey === "string" && typeof r.dvm_id === "string" && typeof r.dvm === "string" && typeof r.receipt_pubkey === "string" && typeof r.signature === "string";
|
|
1221
|
-
}
|
|
1222
|
-
function verifyFundingReceipt(receipt) {
|
|
1223
|
-
try {
|
|
1224
|
-
const { signature, ...unsigned } = receipt;
|
|
1225
|
-
const bytes = canonicaliseForSigning(unsigned);
|
|
1226
|
-
return schnorr3.verify(hexToBytes3(signature), bytes, hexToBytes3(receipt.receipt_pubkey));
|
|
1227
|
-
} catch {
|
|
1228
|
-
return false;
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
function isDrainReceipt(value) {
|
|
1232
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1233
|
-
const r = value;
|
|
1234
|
-
return r.v === 1 && r.kind === "credit_drain" && typeof r.credit_id === "string" && typeof r.drain_id === "string" && (r.event === "requested" || r.event === "parked" || r.event === "picked_up" || r.event === "sent" || r.event === "released") && typeof r.method === "string" && typeof r.amount_micro === "number" && typeof r.issued_at === "number" && typeof r.caller_pubkey === "string" && typeof r.dvm_id === "string" && typeof r.dvm === "string" && typeof r.receipt_pubkey === "string" && typeof r.signature === "string";
|
|
1235
|
-
}
|
|
1236
|
-
function verifyDrainReceipt(receipt) {
|
|
1237
|
-
try {
|
|
1238
|
-
const { signature, ...unsigned } = receipt;
|
|
1239
|
-
const bytes = canonicaliseForSigning(unsigned);
|
|
1240
|
-
return schnorr3.verify(hexToBytes3(signature), bytes, hexToBytes3(receipt.receipt_pubkey));
|
|
1241
|
-
} catch {
|
|
1242
|
-
return false;
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
1282
|
// src/lib/receipt-verify.ts
|
|
1247
1283
|
function attestedDvmIdentity(builder) {
|
|
1248
1284
|
const builderPubkey = authenticatedBuilderPubkey(builder);
|
|
@@ -1532,112 +1568,6 @@ function parseTraceparent(header) {
|
|
|
1532
1568
|
return { traceId, parentSpanId, flags: parseInt(flagsHex, 16) };
|
|
1533
1569
|
}
|
|
1534
1570
|
|
|
1535
|
-
// src/lib/wallet-agent/keypair.ts
|
|
1536
|
-
import { secp256k1 as secp256k12 } from "@noble/curves/secp256k1.js";
|
|
1537
|
-
var PRIVKEY_HEX_RE = /^[0-9a-f]{64}$/i;
|
|
1538
|
-
function derivePubkeyFromPrivkey(privkey) {
|
|
1539
|
-
if (!PRIVKEY_HEX_RE.test(privkey)) {
|
|
1540
|
-
throw new DvmError(
|
|
1541
|
-
"invalid_privkey",
|
|
1542
|
-
"Privkey must be 64 hex characters (32 bytes).",
|
|
1543
|
-
"Provide a valid secp256k1 private key as a 64-char hex string."
|
|
1544
|
-
);
|
|
1545
|
-
}
|
|
1546
|
-
const bytes = hexToBytes4(privkey);
|
|
1547
|
-
if (!secp256k12.utils.isValidSecretKey(bytes)) {
|
|
1548
|
-
throw new DvmError(
|
|
1549
|
-
"invalid_privkey",
|
|
1550
|
-
"Privkey is not a valid secp256k1 scalar (zero or out of curve order).",
|
|
1551
|
-
"Generate a fresh key with: dvm wallet init"
|
|
1552
|
-
);
|
|
1553
|
-
}
|
|
1554
|
-
const pub = secp256k12.getPublicKey(bytes);
|
|
1555
|
-
return bytesToHex4(pub);
|
|
1556
|
-
}
|
|
1557
|
-
function bytesToHex4(bytes) {
|
|
1558
|
-
return Buffer.from(bytes).toString("hex");
|
|
1559
|
-
}
|
|
1560
|
-
function hexToBytes4(hex) {
|
|
1561
|
-
return Uint8Array.from(Buffer.from(hex, "hex"));
|
|
1562
|
-
}
|
|
1563
|
-
|
|
1564
|
-
// src/lib/wallet-agent/sign.ts
|
|
1565
|
-
import { schnorr as schnorr4 } from "@noble/curves/secp256k1.js";
|
|
1566
|
-
var PUBKEY_COMPRESSED_HEX_RE = /^0[23][0-9a-f]{64}$/i;
|
|
1567
|
-
var PUBKEY_XONLY_HEX_RE = /^[0-9a-f]{64}$/i;
|
|
1568
|
-
var SIG_HEX_RE = /^[0-9a-f]{128}$/i;
|
|
1569
|
-
function signChallenge(privkeyHex, challenge) {
|
|
1570
|
-
if (!PRIVKEY_HEX_RE.test(privkeyHex)) {
|
|
1571
|
-
throw new DvmError(
|
|
1572
|
-
"invalid_privkey",
|
|
1573
|
-
"Privkey must be 64 hex characters (32 bytes).",
|
|
1574
|
-
"Provide a valid secp256k1 private key as a 64-char hex string."
|
|
1575
|
-
);
|
|
1576
|
-
}
|
|
1577
|
-
if (challenge.length !== 32) {
|
|
1578
|
-
throw new DvmError(
|
|
1579
|
-
"invalid_challenge",
|
|
1580
|
-
`Challenge must be exactly 32 bytes; got ${challenge.length}.`,
|
|
1581
|
-
"Pass the SHA-256 digest output (32 bytes)."
|
|
1582
|
-
);
|
|
1583
|
-
}
|
|
1584
|
-
const sig = schnorr4.sign(challenge, hexToBytes5(privkeyHex));
|
|
1585
|
-
return bytesToHex5(sig);
|
|
1586
|
-
}
|
|
1587
|
-
function verifyChallenge(pubkeyHex, challenge, sigHex) {
|
|
1588
|
-
if (!SIG_HEX_RE.test(sigHex)) return false;
|
|
1589
|
-
if (challenge.length !== 32) return false;
|
|
1590
|
-
let xonly;
|
|
1591
|
-
if (PUBKEY_COMPRESSED_HEX_RE.test(pubkeyHex)) {
|
|
1592
|
-
xonly = hexToBytes5(pubkeyHex.slice(2));
|
|
1593
|
-
} else if (PUBKEY_XONLY_HEX_RE.test(pubkeyHex)) {
|
|
1594
|
-
xonly = hexToBytes5(pubkeyHex);
|
|
1595
|
-
} else {
|
|
1596
|
-
return false;
|
|
1597
|
-
}
|
|
1598
|
-
try {
|
|
1599
|
-
return schnorr4.verify(hexToBytes5(sigHex), challenge, xonly);
|
|
1600
|
-
} catch {
|
|
1601
|
-
return false;
|
|
1602
|
-
}
|
|
1603
|
-
}
|
|
1604
|
-
function hexToBytes5(hex) {
|
|
1605
|
-
return Uint8Array.from(Buffer.from(hex, "hex"));
|
|
1606
|
-
}
|
|
1607
|
-
function bytesToHex5(bytes) {
|
|
1608
|
-
return Buffer.from(bytes).toString("hex");
|
|
1609
|
-
}
|
|
1610
|
-
|
|
1611
|
-
// src/lib/cashu/admin-auth.ts
|
|
1612
|
-
import { randomBytes } from "crypto";
|
|
1613
|
-
import { sha256 as sha2564 } from "@noble/hashes/sha2.js";
|
|
1614
|
-
var ADMIN_AUTH_NONCE_TTL_SECONDS = 300;
|
|
1615
|
-
var ADMIN_AUTH_CLOCK_SKEW_SECONDS = 30;
|
|
1616
|
-
var ADMIN_STATE_UNAVAILABLE = "admin_state_unavailable";
|
|
1617
|
-
function buildAdminChallenge(args) {
|
|
1618
|
-
const msg = `dvmkit-admin:${args.method}:${args.path}:${args.dvmId}:${args.nonceHex}:${args.unixSeconds}`;
|
|
1619
|
-
return sha2564(new TextEncoder().encode(msg));
|
|
1620
|
-
}
|
|
1621
|
-
function buildAdminAuthHeader(args) {
|
|
1622
|
-
const nowMs = args.now ? args.now() : Date.now();
|
|
1623
|
-
const unixSeconds = Math.floor(nowMs / 1e3);
|
|
1624
|
-
const nonceBytes = args.randomNonce ? args.randomNonce() : Uint8Array.from(randomBytes(16));
|
|
1625
|
-
const nonceHex = Buffer.from(nonceBytes).toString("hex");
|
|
1626
|
-
const challenge = buildAdminChallenge({
|
|
1627
|
-
method: args.method,
|
|
1628
|
-
path: args.path,
|
|
1629
|
-
dvmId: args.dvmId,
|
|
1630
|
-
nonceHex,
|
|
1631
|
-
unixSeconds
|
|
1632
|
-
});
|
|
1633
|
-
const sigHex = signChallenge(args.privkeyHex, challenge);
|
|
1634
|
-
return {
|
|
1635
|
-
header: `P2PK ${nonceHex}.${unixSeconds}.${sigHex}`,
|
|
1636
|
-
nonceHex,
|
|
1637
|
-
unixSeconds
|
|
1638
|
-
};
|
|
1639
|
-
}
|
|
1640
|
-
|
|
1641
1571
|
// src/lib/monotonic-clock.ts
|
|
1642
1572
|
function createMonotonicClock() {
|
|
1643
1573
|
let anchorWallMs = Date.now();
|
|
@@ -1732,6 +1662,14 @@ export {
|
|
|
1732
1662
|
receiptPubkeyFromSecret,
|
|
1733
1663
|
deriveReceiptKeypair,
|
|
1734
1664
|
generateEphemeralReceiptKey,
|
|
1665
|
+
PRIVKEY_HEX_RE,
|
|
1666
|
+
derivePubkeyFromPrivkey,
|
|
1667
|
+
verifyChallenge,
|
|
1668
|
+
ADMIN_AUTH_NONCE_TTL_SECONDS,
|
|
1669
|
+
ADMIN_AUTH_CLOCK_SKEW_SECONDS,
|
|
1670
|
+
ADMIN_STATE_UNAVAILABLE,
|
|
1671
|
+
buildAdminChallenge,
|
|
1672
|
+
buildAdminAuthHeader,
|
|
1735
1673
|
generateIdentity,
|
|
1736
1674
|
loadIdentitySecret,
|
|
1737
1675
|
writeIdentity,
|
|
@@ -1739,16 +1677,6 @@ export {
|
|
|
1739
1677
|
buildAttestation,
|
|
1740
1678
|
signAttestation,
|
|
1741
1679
|
verifyAttestation,
|
|
1742
|
-
computeResultHash,
|
|
1743
|
-
signReceipt,
|
|
1744
|
-
verifyReceipt,
|
|
1745
|
-
isSignedJobReceipt,
|
|
1746
|
-
signDrainReceipt,
|
|
1747
|
-
signFundingReceipt,
|
|
1748
|
-
isFundingReceipt,
|
|
1749
|
-
verifyFundingReceipt,
|
|
1750
|
-
isDrainReceipt,
|
|
1751
|
-
verifyDrainReceipt,
|
|
1752
1680
|
attestedDvmIdentity,
|
|
1753
1681
|
authenticatedBuilderPubkey,
|
|
1754
1682
|
verifyJobReceipt,
|
|
@@ -1765,13 +1693,5 @@ export {
|
|
|
1765
1693
|
receiptFailureDetail,
|
|
1766
1694
|
isPublicRequestId,
|
|
1767
1695
|
parseTraceparent,
|
|
1768
|
-
PRIVKEY_HEX_RE,
|
|
1769
|
-
derivePubkeyFromPrivkey,
|
|
1770
|
-
verifyChallenge,
|
|
1771
|
-
ADMIN_AUTH_NONCE_TTL_SECONDS,
|
|
1772
|
-
ADMIN_AUTH_CLOCK_SKEW_SECONDS,
|
|
1773
|
-
ADMIN_STATE_UNAVAILABLE,
|
|
1774
|
-
buildAdminChallenge,
|
|
1775
|
-
buildAdminAuthHeader,
|
|
1776
1696
|
createMonotonicClock
|
|
1777
1697
|
};
|
|
@@ -2,6 +2,9 @@ import {
|
|
|
2
2
|
BTC_RATE_FILE,
|
|
3
3
|
ensureConfigSubdir
|
|
4
4
|
} from "./chunk-CZULM42S.js";
|
|
5
|
+
import {
|
|
6
|
+
canonicaliseForSigning
|
|
7
|
+
} from "./chunk-OFVV37XC.js";
|
|
5
8
|
import {
|
|
6
9
|
formatFiat
|
|
7
10
|
} from "./chunk-AT6V3SY7.js";
|
|
@@ -232,6 +235,82 @@ async function fetchFromApi() {
|
|
|
232
235
|
return rate;
|
|
233
236
|
}
|
|
234
237
|
|
|
238
|
+
// src/lib/receipt.ts
|
|
239
|
+
import { schnorr } from "@noble/curves/secp256k1.js";
|
|
240
|
+
import { sha256 } from "@noble/hashes/sha2.js";
|
|
241
|
+
import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
|
|
242
|
+
function computeResultHash(summary, artifactContents) {
|
|
243
|
+
const artifactHashes = artifactContents.map(
|
|
244
|
+
(content) => bytesToHex(sha256(canonicaliseForSigning(content)))
|
|
245
|
+
);
|
|
246
|
+
return bytesToHex(
|
|
247
|
+
sha256(
|
|
248
|
+
canonicaliseForSigning({
|
|
249
|
+
summary: summary ?? null,
|
|
250
|
+
artifact_hashes: artifactHashes
|
|
251
|
+
})
|
|
252
|
+
)
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
function signReceipt(unsigned, secretHex) {
|
|
256
|
+
const bytes = canonicaliseForSigning(unsigned);
|
|
257
|
+
const signature = bytesToHex(schnorr.sign(bytes, hexToBytes(secretHex)));
|
|
258
|
+
return { ...unsigned, signature };
|
|
259
|
+
}
|
|
260
|
+
function verifyReceipt(receipt) {
|
|
261
|
+
try {
|
|
262
|
+
const { signature, ...unsigned } = receipt;
|
|
263
|
+
const bytes = canonicaliseForSigning(unsigned);
|
|
264
|
+
return schnorr.verify(hexToBytes(signature), bytes, hexToBytes(receipt.receipt_pubkey));
|
|
265
|
+
} catch {
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
function isSignedJobReceipt(value) {
|
|
270
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
271
|
+
const r = value;
|
|
272
|
+
const paid = r.paid;
|
|
273
|
+
return r.v === 1 && typeof r.job_id === "string" && typeof r.dvm_id === "string" && typeof r.dvm === "string" && typeof r.capability === "string" && (r.outcome === "completed" || r.outcome === "failed" || r.outcome === "cancelled") && typeof r.seq === "number" && typeof r.issued_at === "number" && typeof r.receipt_pubkey === "string" && typeof r.signature === "string" && !!paid && typeof paid === "object" && !Array.isArray(paid) && typeof paid.msats === "number";
|
|
274
|
+
}
|
|
275
|
+
function signDrainReceipt(unsigned, secretHex) {
|
|
276
|
+
const bytes = canonicaliseForSigning(unsigned);
|
|
277
|
+
const signature = bytesToHex(schnorr.sign(bytes, hexToBytes(secretHex)));
|
|
278
|
+
return { ...unsigned, signature };
|
|
279
|
+
}
|
|
280
|
+
function signFundingReceipt(unsigned, secretHex) {
|
|
281
|
+
const bytes = canonicaliseForSigning(unsigned);
|
|
282
|
+
const signature = bytesToHex(schnorr.sign(bytes, hexToBytes(secretHex)));
|
|
283
|
+
return { ...unsigned, signature };
|
|
284
|
+
}
|
|
285
|
+
function isFundingReceipt(value) {
|
|
286
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
287
|
+
const r = value;
|
|
288
|
+
return r.v === 1 && r.kind === "credit_funding" && typeof r.credit_id === "string" && typeof r.fund_id === "string" && typeof r.funded_micro === "number" && typeof r.balance_after_micro === "number" && typeof r.ledger_seq === "number" && typeof r.issued_at === "number" && typeof r.caller_pubkey === "string" && typeof r.dvm_id === "string" && typeof r.dvm === "string" && typeof r.receipt_pubkey === "string" && typeof r.signature === "string";
|
|
289
|
+
}
|
|
290
|
+
function verifyFundingReceipt(receipt) {
|
|
291
|
+
try {
|
|
292
|
+
const { signature, ...unsigned } = receipt;
|
|
293
|
+
const bytes = canonicaliseForSigning(unsigned);
|
|
294
|
+
return schnorr.verify(hexToBytes(signature), bytes, hexToBytes(receipt.receipt_pubkey));
|
|
295
|
+
} catch {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
function isDrainReceipt(value) {
|
|
300
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
301
|
+
const r = value;
|
|
302
|
+
return r.v === 1 && r.kind === "credit_drain" && typeof r.credit_id === "string" && typeof r.drain_id === "string" && (r.event === "requested" || r.event === "parked" || r.event === "picked_up" || r.event === "sent" || r.event === "released") && typeof r.method === "string" && typeof r.amount_micro === "number" && typeof r.issued_at === "number" && typeof r.caller_pubkey === "string" && typeof r.dvm_id === "string" && typeof r.dvm === "string" && typeof r.receipt_pubkey === "string" && typeof r.signature === "string";
|
|
303
|
+
}
|
|
304
|
+
function verifyDrainReceipt(receipt) {
|
|
305
|
+
try {
|
|
306
|
+
const { signature, ...unsigned } = receipt;
|
|
307
|
+
const bytes = canonicaliseForSigning(unsigned);
|
|
308
|
+
return schnorr.verify(hexToBytes(signature), bytes, hexToBytes(receipt.receipt_pubkey));
|
|
309
|
+
} catch {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
235
314
|
export {
|
|
236
315
|
IMPLICIT_CREDIT_TTL_MS,
|
|
237
316
|
COINGECKO_SIMPLE_PRICE_URL,
|
|
@@ -254,5 +333,15 @@ export {
|
|
|
254
333
|
fetchBtcUsdRate,
|
|
255
334
|
getLastKnownBtcRate,
|
|
256
335
|
formatStaleness,
|
|
257
|
-
formatAmountBestEffort
|
|
336
|
+
formatAmountBestEffort,
|
|
337
|
+
computeResultHash,
|
|
338
|
+
signReceipt,
|
|
339
|
+
verifyReceipt,
|
|
340
|
+
isSignedJobReceipt,
|
|
341
|
+
signDrainReceipt,
|
|
342
|
+
signFundingReceipt,
|
|
343
|
+
isFundingReceipt,
|
|
344
|
+
verifyFundingReceipt,
|
|
345
|
+
isDrainReceipt,
|
|
346
|
+
verifyDrainReceipt
|
|
258
347
|
};
|
|
@@ -10,39 +10,6 @@ import {
|
|
|
10
10
|
projectSpanRow
|
|
11
11
|
} from "./chunk-ETXDTUJO.js";
|
|
12
12
|
|
|
13
|
-
// src/sdk/step-cache.ts
|
|
14
|
-
var StepCache = class _StepCache {
|
|
15
|
-
cache = /* @__PURE__ */ new Map();
|
|
16
|
-
has(id) {
|
|
17
|
-
return this.cache.has(id);
|
|
18
|
-
}
|
|
19
|
-
get(id) {
|
|
20
|
-
return this.getRecord(id).value;
|
|
21
|
-
}
|
|
22
|
-
getRecord(id) {
|
|
23
|
-
const record = this.cache.get(id);
|
|
24
|
-
if (record === void 0) throw new Error(`StepCache: no cached result for step "${id}"`);
|
|
25
|
-
return record;
|
|
26
|
-
}
|
|
27
|
-
set(id, value, costs) {
|
|
28
|
-
this.cache.set(id, {
|
|
29
|
-
id,
|
|
30
|
-
value,
|
|
31
|
-
...costs !== void 0 && costs.length > 0 ? { costs: costs.map(({ amount, currency }) => ({ amount, currency })) } : {}
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
serialize() {
|
|
35
|
-
return [...this.cache.values()];
|
|
36
|
-
}
|
|
37
|
-
static deserialize(records) {
|
|
38
|
-
const cache = new _StepCache();
|
|
39
|
-
for (const { id, value, costs } of records) {
|
|
40
|
-
cache.set(id, value, costs);
|
|
41
|
-
}
|
|
42
|
-
return cache;
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
|
|
46
13
|
// src/sdk/logger.ts
|
|
47
14
|
function createConsoleLogger(_jobId) {
|
|
48
15
|
return {
|
|
@@ -373,15 +340,15 @@ async function withSpan(opts, fn) {
|
|
|
373
340
|
|
|
374
341
|
// src/observability/datasets.ts
|
|
375
342
|
var SPANS_DATASETS = [
|
|
376
|
-
{ name: "platform", retentionDays:
|
|
377
|
-
{ name: "cashu", retentionDays:
|
|
378
|
-
{ name: "lightning", retentionDays:
|
|
379
|
-
{ name: "stripe", retentionDays:
|
|
380
|
-
{ name: "x402", retentionDays:
|
|
381
|
-
{ name: "tempo", retentionDays:
|
|
382
|
-
{ name: "isolate", retentionDays:
|
|
383
|
-
{ name: "audit", retentionDays:
|
|
384
|
-
{ name: "cron", retentionDays:
|
|
343
|
+
{ name: "platform", retentionDays: 730 },
|
|
344
|
+
{ name: "cashu", retentionDays: 730 },
|
|
345
|
+
{ name: "lightning", retentionDays: 730 },
|
|
346
|
+
{ name: "stripe", retentionDays: 730 },
|
|
347
|
+
{ name: "x402", retentionDays: 730 },
|
|
348
|
+
{ name: "tempo", retentionDays: 730 },
|
|
349
|
+
{ name: "isolate", retentionDays: 730 },
|
|
350
|
+
{ name: "audit", retentionDays: 730 },
|
|
351
|
+
{ name: "cron", retentionDays: 730 }
|
|
385
352
|
];
|
|
386
353
|
var DATASET_NAMES = new Set(SPANS_DATASETS.map((d) => d.name));
|
|
387
354
|
|
|
@@ -485,7 +452,6 @@ function buildNoopLoggers() {
|
|
|
485
452
|
}
|
|
486
453
|
|
|
487
454
|
export {
|
|
488
|
-
StepCache,
|
|
489
455
|
createConsoleLogger,
|
|
490
456
|
createStdoutLogger,
|
|
491
457
|
createNoopLogger,
|