@dvmkit/sdk 0.16.2 → 0.17.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/dist/{chunk-XSF5YTEY.js → chunk-2PN4DKZW.js} +1 -1
- package/dist/{chunk-F2LNWZCV.js → chunk-3KRFFLAT.js} +1 -1
- package/dist/{chunk-V73WRF46.js → chunk-5CCQEAQV.js} +1 -1
- package/dist/{chunk-4IFZHJQK.js → chunk-5RLNRLLB.js} +1 -1
- package/dist/{chunk-7QGS27UT.js → chunk-72A4PHLD.js} +1 -1
- package/dist/{chunk-XBQTFOIT.js → chunk-7RWE6ELV.js} +1 -1
- package/dist/{chunk-N4SBDUVD.js → chunk-A6EY4K46.js} +1 -1
- package/dist/{chunk-5U2RG3ZW.js → chunk-ALTE7I6C.js} +5 -0
- package/dist/{chunk-K4ZQE7IB.js → chunk-B5MPLDRC.js} +2 -2
- package/dist/{chunk-PP5WHB6T.js → chunk-C5XNZLZ7.js} +1 -1
- package/dist/{chunk-2UZOJRRS.js → chunk-CVN2YNMG.js} +1 -1
- package/dist/{chunk-MD34LE6M.js → chunk-CXRI4N4Y.js} +1 -1
- package/dist/{chunk-ELNWQS24.js → chunk-EIDRCGTY.js} +1 -1
- package/dist/{chunk-ICHC4VDZ.js → chunk-KRX7VNBG.js} +1 -1
- package/dist/{chunk-TN6MWTZI.js → chunk-LY2JYSJS.js} +1 -1
- package/dist/{chunk-7N5VDCG3.js → chunk-MWDUGMN2.js} +2 -2
- package/dist/{chunk-FRGKANTZ.js → chunk-ORBWKYDE.js} +1 -1
- package/dist/{chunk-VQHBJXBL.js → chunk-QTKGFG3I.js} +1 -1
- package/dist/{chunk-PBT3H2LZ.js → chunk-RX6FT7XA.js} +1 -1
- package/dist/{chunk-OYBSDJR6.js → chunk-VKFR5PCW.js} +2 -2
- package/dist/{chunk-V4CBI5HB.js → chunk-Y6JJDOGO.js} +1 -1
- package/dist/{chunk-KERFCBT7.js → chunk-YRQMDRI4.js} +502 -129
- package/dist/{content-retention-BASsjgKw.d.ts → content-retention-B_hOQS36.d.ts} +1 -1
- package/dist/{credit-ledger-VWZ5NP7V.js → credit-ledger-5HMJ6VUX.js} +3 -3
- package/dist/{credit-menu-BfXaeHgR.d.ts → credit-menu-D-h0zJCZ.d.ts} +42 -10
- package/dist/{fx-BmvWGISH.d.ts → fx-CpqQaIfg.d.ts} +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +4 -4
- package/dist/internal/caller.d.ts +14 -8
- package/dist/internal/caller.js +7 -7
- package/dist/internal/server.d.ts +7 -7
- package/dist/internal/server.js +20 -20
- package/dist/{job-store-CA7u1nHV.d.ts → job-store-CgXT79tS.d.ts} +1 -1
- package/dist/{lightning-backend-BOcopzXk.d.ts → lightning-backend-B1hRUIOb.d.ts} +1 -1
- package/dist/{memory-credit-ledger-DZWRQGSF.js → memory-credit-ledger-ME6FH462.js} +4 -4
- package/dist/{mpp-setup-3NJY354I.js → mpp-setup-BLIYBORK.js} +3 -3
- package/dist/{payout-reporter-P3652TLG.js → payout-reporter-5QAHCYIO.js} +2 -2
- package/dist/{postgres-consumed-credential-store-S2CBQ4NO.js → postgres-consumed-credential-store-4PCJEZBD.js} +1 -1
- package/dist/{postgres-job-store-JRSG54VJ.js → postgres-job-store-E5GXLRB4.js} +2 -2
- package/dist/{postgres-replay-store-ZPILIJIW.js → postgres-replay-store-GH5AINQR.js} +2 -2
- package/dist/{pricing-3P7KAONY.js → pricing-JJYLTTWK.js} +3 -3
- package/dist/{revenue-reporter-2P3PWJJM.js → revenue-reporter-6XARUEG6.js} +2 -2
- package/dist/server/index.d.ts +10 -10
- package/dist/server/index.js +32 -31
- package/dist/{step-cache-BCRHimiY.d.ts → step-cache-Bezn-_ZL.d.ts} +41 -3
- package/dist/{tempo-charge-store-2LK6IDLS.js → tempo-charge-store-A3CDF7D5.js} +2 -2
- package/dist/{tempo-session-store-5MHCH3YW.js → tempo-session-store-EWBE5HJM.js} +3 -3
- package/dist/testing/index.d.ts +3 -3
- package/dist/testing/index.js +6 -6
- package/dist/{usd-B8_WMfHT.d.ts → usd-CJwU3-A4.d.ts} +1 -1
- package/dist/{x402-6T73AZQ6.js → x402-HVCUEWWU.js} +3 -3
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from "./chunk-XRLKGCAW.js";
|
|
6
6
|
import {
|
|
7
7
|
x402EarnedCeiling
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-KRX7VNBG.js";
|
|
9
9
|
import {
|
|
10
10
|
createX402FacilitatorAuthHeaders,
|
|
11
11
|
createX402FacilitatorClient,
|
|
@@ -14,13 +14,13 @@ import {
|
|
|
14
14
|
verifyWithFacilitator,
|
|
15
15
|
withPaymentDiagnostics,
|
|
16
16
|
withPaymentStage
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-3KRFFLAT.js";
|
|
18
18
|
import {
|
|
19
19
|
MemoryCreditLedger
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-C5XNZLZ7.js";
|
|
21
21
|
import {
|
|
22
22
|
PostgresJobStore
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-A6EY4K46.js";
|
|
24
24
|
import {
|
|
25
25
|
SIGNED_ENVELOPE_FIELDS,
|
|
26
26
|
SignedRequestError,
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
humanizeZodIssues,
|
|
29
29
|
signedRequestInput,
|
|
30
30
|
zodIssuesOf
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-5CCQEAQV.js";
|
|
32
32
|
import {
|
|
33
33
|
MemoryJobStore,
|
|
34
34
|
UNCAPPABLE_ASK_TOTAL,
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
isJobRetentionStore,
|
|
38
38
|
isReceiptIssuingStore,
|
|
39
39
|
isStreamableJobStore
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-Y6JJDOGO.js";
|
|
41
41
|
import {
|
|
42
42
|
ContentHoldError,
|
|
43
43
|
JobCancelledError,
|
|
@@ -78,7 +78,7 @@ import {
|
|
|
78
78
|
receiptPubkeyFromSecret,
|
|
79
79
|
toLockPubkey,
|
|
80
80
|
verifyChallenge
|
|
81
|
-
} from "./chunk-
|
|
81
|
+
} from "./chunk-7RWE6ELV.js";
|
|
82
82
|
import {
|
|
83
83
|
X402_BATCH_SETTLEMENT_SCHEME,
|
|
84
84
|
X402_DEFAULT_FACILITATOR,
|
|
@@ -105,7 +105,7 @@ import {
|
|
|
105
105
|
} from "./chunk-SXL44PL5.js";
|
|
106
106
|
import {
|
|
107
107
|
createConsoleLogger
|
|
108
|
-
} from "./chunk-
|
|
108
|
+
} from "./chunk-QTKGFG3I.js";
|
|
109
109
|
import {
|
|
110
110
|
StepCache
|
|
111
111
|
} from "./chunk-BQJIFRAZ.js";
|
|
@@ -122,7 +122,7 @@ import {
|
|
|
122
122
|
roundUsd,
|
|
123
123
|
satsToFiat,
|
|
124
124
|
validateCurrency
|
|
125
|
-
} from "./chunk-
|
|
125
|
+
} from "./chunk-72A4PHLD.js";
|
|
126
126
|
import {
|
|
127
127
|
CreditLedger,
|
|
128
128
|
CreditLedgerError,
|
|
@@ -138,7 +138,7 @@ import {
|
|
|
138
138
|
isNonChannelBitcoinRail,
|
|
139
139
|
netOwedSats,
|
|
140
140
|
x402SettlementPending
|
|
141
|
-
} from "./chunk-
|
|
141
|
+
} from "./chunk-B5MPLDRC.js";
|
|
142
142
|
import {
|
|
143
143
|
parseStoreValue
|
|
144
144
|
} from "./chunk-YG7G4DPZ.js";
|
|
@@ -156,7 +156,7 @@ import {
|
|
|
156
156
|
} from "./chunk-5UC7TOQC.js";
|
|
157
157
|
import {
|
|
158
158
|
withPostgresRecoveryContext
|
|
159
|
-
} from "./chunk-
|
|
159
|
+
} from "./chunk-EIDRCGTY.js";
|
|
160
160
|
import {
|
|
161
161
|
DvmError,
|
|
162
162
|
x402NetworkByCaip2
|
|
@@ -174,17 +174,17 @@ import {
|
|
|
174
174
|
installHostedTempoCloseGuard,
|
|
175
175
|
isTempoInsufficientFundsError,
|
|
176
176
|
resolveTempoNetwork
|
|
177
|
-
} from "./chunk-
|
|
177
|
+
} from "./chunk-MWDUGMN2.js";
|
|
178
178
|
import {
|
|
179
179
|
loggers,
|
|
180
180
|
pinDimension,
|
|
181
181
|
withSpan,
|
|
182
182
|
withTraceContext
|
|
183
|
-
} from "./chunk-
|
|
183
|
+
} from "./chunk-LY2JYSJS.js";
|
|
184
184
|
import {
|
|
185
185
|
createDiagnosticCapabilityAuthority,
|
|
186
186
|
diagnosticConsole
|
|
187
|
-
} from "./chunk-
|
|
187
|
+
} from "./chunk-ALTE7I6C.js";
|
|
188
188
|
|
|
189
189
|
// src/sdk/server/lock-pubkey-store.ts
|
|
190
190
|
async function initLockPubkeyTable(db) {
|
|
@@ -428,13 +428,26 @@ async function insertAccumulatorRows(db, args) {
|
|
|
428
428
|
client.release();
|
|
429
429
|
}
|
|
430
430
|
}
|
|
431
|
-
async function
|
|
432
|
-
|
|
431
|
+
async function claimAccumulatorRequestId(tx, dvmId, requestId) {
|
|
432
|
+
await tx.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [
|
|
433
|
+
JSON.stringify(["wallet_accumulator_request", dvmId, requestId])
|
|
434
|
+
]);
|
|
435
|
+
const { rows: rows2 } = await tx.query(
|
|
436
|
+
`SELECT 1 FROM wallet_accumulator WHERE dvm_id = $1 AND request_id = $2 LIMIT 1`,
|
|
437
|
+
[dvmId, requestId]
|
|
438
|
+
);
|
|
439
|
+
if (rows2.length > 0) throw new AccumulatorReplayError(dvmId, requestId);
|
|
440
|
+
}
|
|
441
|
+
async function acceptedRequestIds(db, dvmId, secrets) {
|
|
442
|
+
if (secrets.length === 0) return [];
|
|
443
|
+
const hashes = secrets.map(hashProofSecret);
|
|
433
444
|
const { rows: rows2 } = await db.query(
|
|
434
|
-
`SELECT
|
|
435
|
-
|
|
445
|
+
`SELECT proof_secret_hash, request_id FROM wallet_accumulator
|
|
446
|
+
WHERE dvm_id = $1 AND proof_secret_hash = ANY($2::text[])`,
|
|
447
|
+
[dvmId, hashes]
|
|
436
448
|
);
|
|
437
|
-
|
|
449
|
+
const byHash = new Map(rows2.map((row) => [row.proof_secret_hash, row.request_id]));
|
|
450
|
+
return hashes.map((hash3) => byHash.get(hash3));
|
|
438
451
|
}
|
|
439
452
|
async function listPendingBatches(db, dvmId) {
|
|
440
453
|
const { rows: rows2 } = await db.query(
|
|
@@ -1161,6 +1174,9 @@ function pinAskFiat(args) {
|
|
|
1161
1174
|
return { currency: args.currency, amountMicro: Math.max(1, rounded) };
|
|
1162
1175
|
}
|
|
1163
1176
|
function fundedMicroFor(priceFiat, paidMsats, requiredMsats) {
|
|
1177
|
+
if (!Number.isFinite(requiredMsats) || requiredMsats <= 0) {
|
|
1178
|
+
throw new Error(`fundedMicroFor needs a positive reference price in msats (got ${requiredMsats})`);
|
|
1179
|
+
}
|
|
1164
1180
|
if (paidMsats === requiredMsats) return priceFiat.amountMicro;
|
|
1165
1181
|
return Math.round(priceFiat.amountMicro * paidMsats / requiredMsats);
|
|
1166
1182
|
}
|
|
@@ -3234,7 +3250,7 @@ function paymentFailureFields(error) {
|
|
|
3234
3250
|
}
|
|
3235
3251
|
|
|
3236
3252
|
// src/sdk/server/payment.ts
|
|
3237
|
-
import { createHash as
|
|
3253
|
+
import { createHash as createHash5, randomBytes, randomUUID as randomUUID2 } from "crypto";
|
|
3238
3254
|
import { Challenge } from "mppx";
|
|
3239
3255
|
|
|
3240
3256
|
// src/lib/cashu/verify.ts
|
|
@@ -3328,6 +3344,63 @@ function methodNativeAmountToDecimal(amount, methodName) {
|
|
|
3328
3344
|
return `${nativeAmount2 / scale}.${(nativeAmount2 % scale).toString().padStart(decimals, "0")}`;
|
|
3329
3345
|
}
|
|
3330
3346
|
|
|
3347
|
+
// src/sdk/server/cashu-payment-request.ts
|
|
3348
|
+
import { createHash as createHash4 } from "crypto";
|
|
3349
|
+
import { PaymentRequest } from "@cashu/cashu-ts";
|
|
3350
|
+
function cashuPaymentRequest(args) {
|
|
3351
|
+
return new PaymentRequest(
|
|
3352
|
+
void 0,
|
|
3353
|
+
void 0,
|
|
3354
|
+
Math.ceil(args.requiredMsats / 1e3),
|
|
3355
|
+
"sat",
|
|
3356
|
+
args.mints,
|
|
3357
|
+
void 0,
|
|
3358
|
+
false,
|
|
3359
|
+
{ kind: "P2PK", data: args.lockPubkey, tags: [] }
|
|
3360
|
+
).toEncodedCreqA();
|
|
3361
|
+
}
|
|
3362
|
+
function proofSetRequestId(secrets) {
|
|
3363
|
+
const canonical = JSON.stringify([...secrets].sort());
|
|
3364
|
+
return `${DERIVED_REQUEST_ID_PREFIX}${createHash4("sha256").update(canonical).digest("hex")}`;
|
|
3365
|
+
}
|
|
3366
|
+
var DERIVED_REQUEST_ID_PREFIX = "nut24-";
|
|
3367
|
+
function withFxHistory(inner, opts = {}) {
|
|
3368
|
+
const holdWindowMs = opts.holdWindowMs ?? FX_HOLD_WINDOW_MS;
|
|
3369
|
+
const now = opts.now ?? Date.now;
|
|
3370
|
+
const history = [];
|
|
3371
|
+
return {
|
|
3372
|
+
async fetch() {
|
|
3373
|
+
const snapshot = await inner.fetch();
|
|
3374
|
+
const latest = history.at(-1);
|
|
3375
|
+
if (latest && sameRates(latest.snapshot, snapshot)) {
|
|
3376
|
+
latest.lastPricedAt = now();
|
|
3377
|
+
} else {
|
|
3378
|
+
history.push({ snapshot, lastPricedAt: now() });
|
|
3379
|
+
if (history.length > 4) history.shift();
|
|
3380
|
+
}
|
|
3381
|
+
return snapshot;
|
|
3382
|
+
},
|
|
3383
|
+
supportedCurrencies: () => inner.supportedCurrencies(),
|
|
3384
|
+
...inner.lastKnown && { lastKnown: inner.lastKnown.bind(inner) },
|
|
3385
|
+
previousFor(pricedAt) {
|
|
3386
|
+
for (let i = history.length - 1; i > 0; i--) {
|
|
3387
|
+
if (!sameRates(history[i].snapshot, pricedAt)) continue;
|
|
3388
|
+
const replaced = history[i - 1];
|
|
3389
|
+
return now() - replaced.lastPricedAt <= holdWindowMs ? replaced.snapshot : void 0;
|
|
3390
|
+
}
|
|
3391
|
+
return void 0;
|
|
3392
|
+
}
|
|
3393
|
+
};
|
|
3394
|
+
}
|
|
3395
|
+
function sameRates(a, b) {
|
|
3396
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(a.rates), ...Object.keys(b.rates)]);
|
|
3397
|
+
for (const key of keys) {
|
|
3398
|
+
if (a.rates[key] !== b.rates[key]) return false;
|
|
3399
|
+
}
|
|
3400
|
+
return true;
|
|
3401
|
+
}
|
|
3402
|
+
var FX_HOLD_WINDOW_MS = 10 * 6e4;
|
|
3403
|
+
|
|
3331
3404
|
// src/lib/mints.ts
|
|
3332
3405
|
function isTestMintUrl(url) {
|
|
3333
3406
|
let host;
|
|
@@ -3459,13 +3532,20 @@ async function acceptAccumulatorPayment(opts) {
|
|
|
3459
3532
|
if (opts.disposable && !opts.mints.every(isTestMintUrl)) {
|
|
3460
3533
|
throw new Error("Disposable Cashu accepts only known test or loopback mints.");
|
|
3461
3534
|
}
|
|
3462
|
-
if (!opts.requestId) {
|
|
3535
|
+
if (!opts.requestId && !opts.deriveRequestId) {
|
|
3463
3536
|
return {
|
|
3464
3537
|
kind: "rejected",
|
|
3465
3538
|
code: "missing_request_id",
|
|
3466
3539
|
message: "X-Cashu-Request-Id header required for the agent-wallet path."
|
|
3467
3540
|
};
|
|
3468
3541
|
}
|
|
3542
|
+
if (opts.requestId?.startsWith(DERIVED_REQUEST_ID_PREFIX)) {
|
|
3543
|
+
return {
|
|
3544
|
+
kind: "rejected",
|
|
3545
|
+
code: "reserved_request_id",
|
|
3546
|
+
message: `A Cashu request id may not start with "${DERIVED_REQUEST_ID_PREFIX}".`
|
|
3547
|
+
};
|
|
3548
|
+
}
|
|
3469
3549
|
if (opts.lockPubkeys.length === 0) {
|
|
3470
3550
|
return {
|
|
3471
3551
|
kind: "rejected",
|
|
@@ -3486,6 +3566,9 @@ async function acceptAccumulatorPayment(opts) {
|
|
|
3486
3566
|
if (meta.unit !== "sat" || proofs.length === 0) {
|
|
3487
3567
|
throw new Error("Cashu payment requires nonempty sat proofs.");
|
|
3488
3568
|
}
|
|
3569
|
+
if (new Set(proofs.map((proof) => proof.secret)).size !== proofs.length) {
|
|
3570
|
+
throw new Error("Cashu payment carries the same proof more than once.");
|
|
3571
|
+
}
|
|
3489
3572
|
for (const proof of proofs) {
|
|
3490
3573
|
if (!proof.dleq || !verifyDleqIfPresent(proof, wallet.getKeyset(proof.id))) {
|
|
3491
3574
|
throw new Error(
|
|
@@ -3501,10 +3584,12 @@ async function acceptAccumulatorPayment(opts) {
|
|
|
3501
3584
|
message: err instanceof Error ? err.message : "Cashu token decode failed"
|
|
3502
3585
|
};
|
|
3503
3586
|
}
|
|
3587
|
+
const requestId = opts.requestId || proofSetRequestId(proofs.map((proof) => proof.secret));
|
|
3504
3588
|
const acceptedValue = {
|
|
3505
3589
|
...valuation,
|
|
3506
3590
|
mintUrl,
|
|
3507
|
-
paidMsats: valuation.netMsats
|
|
3591
|
+
paidMsats: valuation.netMsats,
|
|
3592
|
+
requestId
|
|
3508
3593
|
};
|
|
3509
3594
|
const nowSec = Math.floor(Date.now() / 1e3);
|
|
3510
3595
|
const tExpireMsByIndex = [];
|
|
@@ -3521,9 +3606,8 @@ async function acceptAccumulatorPayment(opts) {
|
|
|
3521
3606
|
tExpireMsByIndex.push(parseNut11LocktimeMs(p.secret));
|
|
3522
3607
|
}
|
|
3523
3608
|
if (belowFloorMessage) {
|
|
3524
|
-
const
|
|
3525
|
-
|
|
3526
|
-
if (banked) return replayRejected();
|
|
3609
|
+
const banked = await acceptedUnder(opts, proofs);
|
|
3610
|
+
if (banked.some((id) => id !== void 0)) return replayRejected(banked, requestId);
|
|
3527
3611
|
return { kind: "rejected", code: "lock_not_exclusive", message: belowFloorMessage };
|
|
3528
3612
|
}
|
|
3529
3613
|
try {
|
|
@@ -3567,23 +3651,24 @@ async function acceptAccumulatorPayment(opts) {
|
|
|
3567
3651
|
lockPubkeyByIndex: matchedLockPubkeyByIndex,
|
|
3568
3652
|
mintUrl,
|
|
3569
3653
|
proofs,
|
|
3570
|
-
requestId
|
|
3654
|
+
requestId,
|
|
3571
3655
|
tExpireMsByIndex
|
|
3572
3656
|
};
|
|
3573
3657
|
try {
|
|
3574
3658
|
if (opts.disposable) {
|
|
3575
3659
|
const account = opts.withDisposableLedger;
|
|
3576
3660
|
if (!account) throw new Error("Disposable Cashu requires atomic credit accounting.");
|
|
3577
|
-
await opts.disposable.commit(
|
|
3661
|
+
await opts.disposable.commit(requestId, proofs, (ledger) => account(ledger, acceptedValue));
|
|
3578
3662
|
} else if (!opts.db) {
|
|
3579
3663
|
throw new Error("Cashu accumulator requires a storage backend.");
|
|
3580
|
-
} else
|
|
3581
|
-
const withCommitTx = opts.withCommitTx;
|
|
3664
|
+
} else {
|
|
3582
3665
|
const client = await opts.db.connect();
|
|
3583
3666
|
try {
|
|
3584
3667
|
await client.query("BEGIN");
|
|
3668
|
+
await client.query("SET TRANSACTION ISOLATION LEVEL READ COMMITTED");
|
|
3669
|
+
await claimAccumulatorRequestId(client, opts.dvmId, requestId);
|
|
3585
3670
|
await insertAccumulatorRows(opts.db, { ...insertArgs, tx: client });
|
|
3586
|
-
await withCommitTx(client, acceptedValue);
|
|
3671
|
+
await opts.withCommitTx?.(client, acceptedValue);
|
|
3587
3672
|
await client.query("COMMIT");
|
|
3588
3673
|
} catch (err) {
|
|
3589
3674
|
try {
|
|
@@ -3594,11 +3679,13 @@ async function acceptAccumulatorPayment(opts) {
|
|
|
3594
3679
|
} finally {
|
|
3595
3680
|
client.release();
|
|
3596
3681
|
}
|
|
3597
|
-
} else {
|
|
3598
|
-
await insertAccumulatorRows(opts.db, insertArgs);
|
|
3599
3682
|
}
|
|
3600
3683
|
} catch (err) {
|
|
3601
|
-
if (err instanceof AccumulatorReplayError)
|
|
3684
|
+
if (err instanceof AccumulatorReplayError) {
|
|
3685
|
+
const banked = await acceptedUnder(opts, proofs);
|
|
3686
|
+
if (banked.every((id) => id === void 0)) return requestIdReused(requestId);
|
|
3687
|
+
return replayRejected(banked, requestId);
|
|
3688
|
+
}
|
|
3602
3689
|
throw err;
|
|
3603
3690
|
}
|
|
3604
3691
|
return {
|
|
@@ -3607,14 +3694,33 @@ async function acceptAccumulatorPayment(opts) {
|
|
|
3607
3694
|
grossMsats: valuation.grossMsats,
|
|
3608
3695
|
inputFeeMsats: valuation.inputFeeMsats,
|
|
3609
3696
|
netMsats: valuation.netMsats,
|
|
3610
|
-
mintUrl
|
|
3697
|
+
mintUrl,
|
|
3698
|
+
requestId
|
|
3611
3699
|
};
|
|
3612
3700
|
}
|
|
3613
|
-
function
|
|
3701
|
+
async function acceptedUnder(opts, proofs) {
|
|
3702
|
+
const secrets = proofs.map((p) => p.secret);
|
|
3703
|
+
if (opts.disposable) return await opts.disposable.acceptedRequestIds(secrets);
|
|
3704
|
+
if (opts.db) return await acceptedRequestIds(opts.db, opts.dvmId, secrets);
|
|
3705
|
+
return secrets.map(() => void 0);
|
|
3706
|
+
}
|
|
3707
|
+
function requestIdReused(requestId) {
|
|
3708
|
+
return {
|
|
3709
|
+
kind: "rejected",
|
|
3710
|
+
code: "request_id_reused",
|
|
3711
|
+
requestId,
|
|
3712
|
+
message: "This request id already carries a different payment at this DVM; these proofs were not accepted."
|
|
3713
|
+
};
|
|
3714
|
+
}
|
|
3715
|
+
function replayRejected(accepted, requestId) {
|
|
3716
|
+
const [first] = accepted;
|
|
3717
|
+
const bound = first !== void 0 && accepted.every((id) => id === first);
|
|
3614
3718
|
return {
|
|
3615
3719
|
kind: "rejected",
|
|
3616
3720
|
code: "replay_rejected",
|
|
3617
|
-
message: "Proofs or request id already accepted at this DVM."
|
|
3721
|
+
message: "Proofs or request id already accepted at this DVM.",
|
|
3722
|
+
requestId,
|
|
3723
|
+
...bound && { acceptedRequestId: first }
|
|
3618
3724
|
};
|
|
3619
3725
|
}
|
|
3620
3726
|
function parseNut11LocktimeMs(secret) {
|
|
@@ -3850,7 +3956,7 @@ async function verifyUpfrontPaymentInScope(opts) {
|
|
|
3850
3956
|
}
|
|
3851
3957
|
};
|
|
3852
3958
|
}
|
|
3853
|
-
if (
|
|
3959
|
+
if (cashuAccumulatorReady(opts)) {
|
|
3854
3960
|
return verifyAccumulatorReceipt(opts, mints, requiredMsats, ledgerCtx);
|
|
3855
3961
|
}
|
|
3856
3962
|
return {
|
|
@@ -4014,7 +4120,7 @@ async function verifyUpfrontPaymentInScope(opts) {
|
|
|
4014
4120
|
x402Requirements
|
|
4015
4121
|
});
|
|
4016
4122
|
}
|
|
4017
|
-
const { verifyX402Payment } = await import("./x402-
|
|
4123
|
+
const { verifyX402Payment } = await import("./x402-HVCUEWWU.js");
|
|
4018
4124
|
const receipt = await withPaymentStage("verifier_broadcast", () => verifyX402Payment(
|
|
4019
4125
|
x402Payment,
|
|
4020
4126
|
x402Config,
|
|
@@ -4469,6 +4575,8 @@ async function verifyUpfrontPaymentInScope(opts) {
|
|
|
4469
4575
|
opts.fundOnly?.allowOneShotStablecoin,
|
|
4470
4576
|
opts.priceFiat
|
|
4471
4577
|
) : void 0;
|
|
4578
|
+
const lockPubkey = opts.lockPubkeys?.[0];
|
|
4579
|
+
const cashuRequest = opts.nut24 && opts.nut24.mints.length > 0 && !opts.fundOnly && cashuAccumulatorReady(opts) && lockPubkey ? cashuPaymentRequest({ requiredMsats, mints: opts.nut24.mints, lockPubkey }) : void 0;
|
|
4472
4580
|
return {
|
|
4473
4581
|
paidMsats: 0,
|
|
4474
4582
|
receivedProofs: [],
|
|
@@ -4479,6 +4587,7 @@ async function verifyUpfrontPaymentInScope(opts) {
|
|
|
4479
4587
|
...mints && { mints }
|
|
4480
4588
|
},
|
|
4481
4589
|
status: 402,
|
|
4590
|
+
...cashuRequest && { cashuPaymentRequest: cashuRequest },
|
|
4482
4591
|
...mppChallenges && { mppChallenges },
|
|
4483
4592
|
...x402Requirements && { x402Requirements },
|
|
4484
4593
|
...x402BatchRequirements && { x402V2Requirements: x402BatchRequirements }
|
|
@@ -4612,6 +4721,11 @@ var PAYMENT_ERROR_COPY = {
|
|
|
4612
4721
|
hint: "Request a fresh quote and pay again \u2014 the previous payment can't be reused.",
|
|
4613
4722
|
retryable: false
|
|
4614
4723
|
},
|
|
4724
|
+
payment_method_requires_signature: {
|
|
4725
|
+
display: "This service accepts unsigned requests only when they are paid with Cashu, so this payment was not applied to the job.",
|
|
4726
|
+
hint: "Pay with Cashu to call without signing, or sign the request (secp256k1 Schnorr) to use another payment method.",
|
|
4727
|
+
retryable: false
|
|
4728
|
+
},
|
|
4615
4729
|
x402_not_offered: {
|
|
4616
4730
|
display: "This service does not offer x402 payment for prices in its configured currency. Nothing was charged.",
|
|
4617
4731
|
hint: "Use one of the payment methods advertised by this service and submit the request again.",
|
|
@@ -4837,6 +4951,11 @@ var PAYMENT_ERROR_COPY = {
|
|
|
4837
4951
|
hint: "Include the X-Cashu-Request-Id header and pay again.",
|
|
4838
4952
|
retryable: false
|
|
4839
4953
|
},
|
|
4954
|
+
reserved_request_id: {
|
|
4955
|
+
display: "The payment's request identifier isn't allowed, so the payment wasn't processed. Nothing was charged.",
|
|
4956
|
+
hint: 'Send a fresh UUIDv4 as X-Cashu-Request-Id. Ids starting with "nut24-" are reserved for payments sent without one.',
|
|
4957
|
+
retryable: false
|
|
4958
|
+
},
|
|
4840
4959
|
missing_lock_pubkey: {
|
|
4841
4960
|
display: "This service can't accept the payment right now. Nothing was charged.",
|
|
4842
4961
|
hint: "The service is missing payment configuration; retrying won't help. Contact the service operator.",
|
|
@@ -4903,6 +5022,11 @@ var PAYMENT_ERROR_COPY = {
|
|
|
4903
5022
|
// agent could follow straight into paying twice for the same charge.
|
|
4904
5023
|
hint: "Don't re-send this payment \u2014 the proofs already landed under this request id. Only a genuinely new charge needs a fresh token and request id.",
|
|
4905
5024
|
retryable: false
|
|
5025
|
+
},
|
|
5026
|
+
request_id_reused: {
|
|
5027
|
+
display: "This payment was not accepted and nothing was charged.",
|
|
5028
|
+
hint: "Each X-Cashu-Request-Id belongs to the first payment made under it. Send these proofs again under a new request id.",
|
|
5029
|
+
retryable: false
|
|
4906
5030
|
}
|
|
4907
5031
|
};
|
|
4908
5032
|
function paymentErrorBody(code, opts) {
|
|
@@ -5044,7 +5168,7 @@ async function verifyIncomingPaymentInScope(body, opts, snapshot) {
|
|
|
5044
5168
|
snapshot
|
|
5045
5169
|
});
|
|
5046
5170
|
}
|
|
5047
|
-
const { verifyX402Payment } = await import("./x402-
|
|
5171
|
+
const { verifyX402Payment } = await import("./x402-HVCUEWWU.js");
|
|
5048
5172
|
const receipt = await withPaymentStage("verifier_broadcast", () => verifyX402Payment(
|
|
5049
5173
|
body.content.x402_payment,
|
|
5050
5174
|
opts.x402Config,
|
|
@@ -5495,7 +5619,7 @@ async function processIncomingPaymentInScope(job, body, opts) {
|
|
|
5495
5619
|
try {
|
|
5496
5620
|
const requiredMsats = job.pendingPaymentMsats ?? 0;
|
|
5497
5621
|
const requiredUsdcMicro = job.pendingX402AmountUsdcMicro !== void 0 ? BigInt(job.pendingX402AmountUsdcMicro) : BigInt(msatsToUsdc(requiredMsats, rate));
|
|
5498
|
-
const { verifyX402Payment } = await import("./x402-
|
|
5622
|
+
const { verifyX402Payment } = await import("./x402-HVCUEWWU.js");
|
|
5499
5623
|
const receipt = await withPaymentStage("verifier_broadcast", () => verifyX402Payment(
|
|
5500
5624
|
body.content.x402_payment,
|
|
5501
5625
|
opts.x402Config,
|
|
@@ -5620,6 +5744,16 @@ function creditPendingPayment(job) {
|
|
|
5620
5744
|
job.pendingPaymentMsats = void 0;
|
|
5621
5745
|
}
|
|
5622
5746
|
}
|
|
5747
|
+
function cashuAccumulatorReady(opts) {
|
|
5748
|
+
return opts.cashuMode === "p2pk-accumulator" && Boolean(opts.accumulatorDb || opts.disposableCashu) && Boolean(opts.dvmId) && (opts.lockPubkeys?.length ?? 0) > 0;
|
|
5749
|
+
}
|
|
5750
|
+
var NUT24_PAYER_FIXABLE = /* @__PURE__ */ new Set([
|
|
5751
|
+
"invalid_token",
|
|
5752
|
+
"not_p2pk_locked",
|
|
5753
|
+
"wrong_lock_pubkey",
|
|
5754
|
+
"lock_not_exclusive",
|
|
5755
|
+
"payment_insufficient"
|
|
5756
|
+
]);
|
|
5623
5757
|
async function verifyAccumulatorReceipt(opts, mints, requiredMsats, ledgerCtx) {
|
|
5624
5758
|
const { cashuToken, accumulatorDb, dvmId, lockPubkeys, mintHealthTracker } = opts;
|
|
5625
5759
|
if (!cashuToken || !accumulatorDb && !opts.disposableCashu || !dvmId || !lockPubkeys || lockPubkeys.length === 0) {
|
|
@@ -5668,7 +5802,14 @@ async function verifyAccumulatorReceipt(opts, mints, requiredMsats, ledgerCtx) {
|
|
|
5668
5802
|
};
|
|
5669
5803
|
}
|
|
5670
5804
|
let credited;
|
|
5671
|
-
const
|
|
5805
|
+
const headerless = !opts.requestId && opts.nut24 !== void 0;
|
|
5806
|
+
const previousRequired = headerless ? opts.nut24?.previousRequiredMsats : void 0;
|
|
5807
|
+
const heldAt = (paidMsats) => requiredMsats !== void 0 && paidMsats < requiredMsats && previousRequired !== void 0 && paidMsats >= previousRequired ? previousRequired : void 0;
|
|
5808
|
+
const requiredFor = (paidMsats) => heldAt(paidMsats) ?? requiredMsats;
|
|
5809
|
+
const held = (ctx, paidMsats) => {
|
|
5810
|
+
const previous = heldAt(paidMsats);
|
|
5811
|
+
return previous === void 0 ? ctx : { ...ctx, requiredMsats: previous };
|
|
5812
|
+
};
|
|
5672
5813
|
const signedFund = ledgerCtx?.creditEnvelope?.fund;
|
|
5673
5814
|
const beforeCommit = ledgerCtx && signedFund ? (accepted) => {
|
|
5674
5815
|
const expected = fundedMicroFor(
|
|
@@ -5680,12 +5821,12 @@ async function verifyAccumulatorReceipt(opts, mints, requiredMsats, ledgerCtx) {
|
|
|
5680
5821
|
throw new CashuFundingAmountMismatchError(signedFund.amountMicro, expected);
|
|
5681
5822
|
}
|
|
5682
5823
|
} : void 0;
|
|
5683
|
-
const withCommitTx = ledgerCtx
|
|
5824
|
+
const withCommitTx = ledgerCtx ? async (tx, accepted) => {
|
|
5684
5825
|
credited = await fundAndDraw(
|
|
5685
|
-
ledgerCtx,
|
|
5826
|
+
held(ledgerCtx, accepted.paidMsats),
|
|
5686
5827
|
{
|
|
5687
5828
|
rail: "cashu",
|
|
5688
|
-
paymentId: requestId,
|
|
5829
|
+
paymentId: accepted.requestId,
|
|
5689
5830
|
paidMsats: accepted.paidMsats,
|
|
5690
5831
|
grossPaidMsats: accepted.grossMsats,
|
|
5691
5832
|
cashuInputFeeMsats: accepted.inputFeeMsats,
|
|
@@ -5699,13 +5840,14 @@ async function verifyAccumulatorReceipt(opts, mints, requiredMsats, ledgerCtx) {
|
|
|
5699
5840
|
result = await acceptAccumulatorPayment({
|
|
5700
5841
|
cashuToken,
|
|
5701
5842
|
requestId: opts.requestId,
|
|
5843
|
+
deriveRequestId: headerless,
|
|
5702
5844
|
mints,
|
|
5703
5845
|
db: accumulatorDb,
|
|
5704
5846
|
disposable: opts.disposableCashu,
|
|
5705
|
-
withDisposableLedger: ledgerCtx
|
|
5706
|
-
credited = await fundAndDraw({ ...ledgerCtx, ledger }, {
|
|
5847
|
+
withDisposableLedger: ledgerCtx ? async (ledger, accepted) => {
|
|
5848
|
+
credited = await fundAndDraw({ ...held(ledgerCtx, accepted.paidMsats), ledger }, {
|
|
5707
5849
|
rail: "cashu",
|
|
5708
|
-
paymentId: requestId,
|
|
5850
|
+
paymentId: accepted.requestId,
|
|
5709
5851
|
paidMsats: accepted.paidMsats,
|
|
5710
5852
|
grossPaidMsats: accepted.grossMsats,
|
|
5711
5853
|
cashuInputFeeMsats: accepted.inputFeeMsats,
|
|
@@ -5729,24 +5871,35 @@ async function verifyAccumulatorReceipt(opts, mints, requiredMsats, ledgerCtx) {
|
|
|
5729
5871
|
throw err;
|
|
5730
5872
|
}
|
|
5731
5873
|
if (result.kind === "rejected") {
|
|
5732
|
-
const status = result.code === "replay_rejected" ? 409 : 402;
|
|
5874
|
+
const status = result.code === "replay_rejected" || result.code === "request_id_reused" ? 409 : result.code === "reserved_request_id" || headerless && NUT24_PAYER_FIXABLE.has(result.code) ? 400 : 402;
|
|
5875
|
+
const requestId = result.requestId;
|
|
5733
5876
|
return {
|
|
5734
5877
|
paidMsats: 0,
|
|
5735
5878
|
receivedProofs: [],
|
|
5736
5879
|
error: {
|
|
5737
|
-
body: paymentErrorBody(result.code, {
|
|
5880
|
+
body: paymentErrorBody(result.code, {
|
|
5881
|
+
message: result.message,
|
|
5882
|
+
// Without a request id the proof set is the payment's identity, so
|
|
5883
|
+
// only these exact proofs with the original body recover its job.
|
|
5884
|
+
...headerless && result.code === "replay_rejected" && {
|
|
5885
|
+
hint: "These proofs already paid for an earlier request. Resend them with that request's exact body to fetch its job; a new request needs new proofs."
|
|
5886
|
+
},
|
|
5887
|
+
extra: { mints }
|
|
5888
|
+
}),
|
|
5738
5889
|
status,
|
|
5739
|
-
// The
|
|
5740
|
-
//
|
|
5741
|
-
//
|
|
5742
|
-
|
|
5890
|
+
// The request id is also the funding id a mid-job top-up committed
|
|
5891
|
+
// under and the job's payment reference, so the route can return the
|
|
5892
|
+
// original evidence beside the 409. It is passed on only when every
|
|
5893
|
+
// presented proof was accepted under this same request id.
|
|
5894
|
+
...requestId && result.acceptedRequestId === requestId && { replayPaymentId: requestId }
|
|
5743
5895
|
}
|
|
5744
5896
|
};
|
|
5745
5897
|
}
|
|
5746
5898
|
if (credited?.drawRefusal) {
|
|
5747
5899
|
return creditLedgerErrorResult(credited.drawRefusal, void 0, credited.deposit);
|
|
5748
5900
|
}
|
|
5749
|
-
|
|
5901
|
+
const heldRequired = requiredFor(result.paidMsats);
|
|
5902
|
+
if (heldRequired && result.paidMsats < heldRequired && !ledgerCtx?.fundOnly && !ledgerCtx?.creditEnvelope) {
|
|
5750
5903
|
let forfeit;
|
|
5751
5904
|
if (credited?.draw && ledgerCtx && !ledgerCtx.midJob) {
|
|
5752
5905
|
const drawId = credited.draw.drawId;
|
|
@@ -5768,36 +5921,39 @@ async function verifyAccumulatorReceipt(opts, mints, requiredMsats, ledgerCtx) {
|
|
|
5768
5921
|
});
|
|
5769
5922
|
}
|
|
5770
5923
|
}
|
|
5771
|
-
const
|
|
5924
|
+
const held2 = ledgerCtx?.midJob ? credited : void 0;
|
|
5772
5925
|
return {
|
|
5773
5926
|
paidMsats: 0,
|
|
5774
5927
|
receivedProofs: [],
|
|
5775
5928
|
error: {
|
|
5776
5929
|
body: paymentErrorBody("payment_insufficient", {
|
|
5777
5930
|
message: `Payment of ${result.paidMsats} msats is short of the ${requiredMsats} msats required.`,
|
|
5778
|
-
...
|
|
5931
|
+
...held2 && {
|
|
5779
5932
|
// Per-code copy can't say where the money went, and this is the one
|
|
5780
5933
|
// site where that is the caller's next action.
|
|
5781
|
-
hint: `The ${result.paidMsats} msats already sent are held on credit ${
|
|
5934
|
+
hint: `The ${result.paidMsats} msats already sent are held on credit ${held2.creditId} and do not reduce what is owed \u2014 retry with a single payment of required_msats, or reclaim the held balance with op "drain" at POST /v1/credit.`
|
|
5935
|
+
},
|
|
5936
|
+
...!held2 && headerless && {
|
|
5937
|
+
hint: "The proofs already sent are kept and do not count toward this request. Pay again with new proofs worth required_msats after the mint's input fee."
|
|
5782
5938
|
},
|
|
5783
5939
|
extra: {
|
|
5784
5940
|
required_msats: requiredMsats,
|
|
5785
5941
|
received_msats: result.paidMsats,
|
|
5786
5942
|
mints,
|
|
5787
|
-
...
|
|
5788
|
-
credit_id:
|
|
5789
|
-
credited_micro:
|
|
5790
|
-
credit_currency:
|
|
5943
|
+
...held2 && {
|
|
5944
|
+
credit_id: held2.creditId,
|
|
5945
|
+
credited_micro: held2.deposit.amountMicro,
|
|
5946
|
+
credit_currency: held2.deposit.currency
|
|
5791
5947
|
}
|
|
5792
5948
|
}
|
|
5793
5949
|
}),
|
|
5794
|
-
status: 402,
|
|
5950
|
+
status: headerless ? 400 : 402,
|
|
5795
5951
|
forfeit,
|
|
5796
5952
|
// Report the deposit on the failure path too, or the platform never
|
|
5797
5953
|
// learns about a liability it is carrying. Mutually exclusive with
|
|
5798
5954
|
// `forfeit` by construction: one is money still owed, the other is
|
|
5799
5955
|
// money nothing is owed against.
|
|
5800
|
-
...
|
|
5956
|
+
...held2 && { funding: held2.deposit }
|
|
5801
5957
|
}
|
|
5802
5958
|
};
|
|
5803
5959
|
}
|
|
@@ -5807,13 +5963,11 @@ async function verifyAccumulatorReceipt(opts, mints, requiredMsats, ledgerCtx) {
|
|
|
5807
5963
|
paymentMint: result.mintUrl,
|
|
5808
5964
|
paymentRail: "cashu",
|
|
5809
5965
|
receivedProofs: [],
|
|
5810
|
-
//
|
|
5811
|
-
//
|
|
5812
|
-
//
|
|
5813
|
-
//
|
|
5814
|
-
|
|
5815
|
-
// it serves as a stable id even though it isn't an on-chain hash.
|
|
5816
|
-
paymentTxHash: opts.requestId,
|
|
5966
|
+
// The request id the proofs were banked under, supplied or derived, is
|
|
5967
|
+
// the per-call settlement reference for revenue_events.tx_hash. Unique
|
|
5968
|
+
// per (dvm_id, request_id) by the wallet_accumulator UNIQUE constraint,
|
|
5969
|
+
// so it serves as a stable id even though it isn't an on-chain hash.
|
|
5970
|
+
paymentTxHash: result.requestId,
|
|
5817
5971
|
cashuFlow: "p2pk_accumulator"
|
|
5818
5972
|
},
|
|
5819
5973
|
ledgerCtx,
|
|
@@ -5889,7 +6043,7 @@ async function resolveTopUpLedgerContext(opts, snapshot, binding) {
|
|
|
5889
6043
|
draw_id: snapshot.drawId,
|
|
5890
6044
|
...extra,
|
|
5891
6045
|
...typeof opts.jobId === "string" && {
|
|
5892
|
-
job_id_hash:
|
|
6046
|
+
job_id_hash: createHash5("sha256").update(opts.jobId, "utf8").digest("hex")
|
|
5893
6047
|
}
|
|
5894
6048
|
}
|
|
5895
6049
|
);
|
|
@@ -9592,7 +9746,7 @@ async function handle(c, opts, action) {
|
|
|
9592
9746
|
}
|
|
9593
9747
|
|
|
9594
9748
|
// src/sdk/server/x402-exact-settlement.ts
|
|
9595
|
-
import { createHash as
|
|
9749
|
+
import { createHash as createHash6 } from "crypto";
|
|
9596
9750
|
var X402ExactSettlementServer = class {
|
|
9597
9751
|
constructor(opts) {
|
|
9598
9752
|
this.opts = opts;
|
|
@@ -9918,7 +10072,7 @@ function settlementMayHaveMoved(response) {
|
|
|
9918
10072
|
return reason.includes("authorization_already_used") || reason.includes("authorization already used") || /facilitator_status_5\d\d/.test(reason);
|
|
9919
10073
|
}
|
|
9920
10074
|
function hash(parts) {
|
|
9921
|
-
return
|
|
10075
|
+
return createHash6("sha256").update(JSON.stringify(parts)).digest("hex");
|
|
9922
10076
|
}
|
|
9923
10077
|
function safeError(err) {
|
|
9924
10078
|
if (err instanceof Error) return err.name;
|
|
@@ -9926,7 +10080,7 @@ function safeError(err) {
|
|
|
9926
10080
|
}
|
|
9927
10081
|
|
|
9928
10082
|
// src/sdk/server/hosting-settlement-report-obligations.ts
|
|
9929
|
-
import { createHash as
|
|
10083
|
+
import { createHash as createHash7, randomUUID as randomUUID3 } from "crypto";
|
|
9930
10084
|
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
9931
10085
|
import { BATCH_SETTLEMENT_ADDRESS as BATCH_SETTLEMENT_ADDRESS3 } from "@x402/evm";
|
|
9932
10086
|
import { computeChannelId as computeChannelId2 } from "@x402/evm/batch-settlement/client";
|
|
@@ -9999,7 +10153,7 @@ var TEMPO_CLOSED_EVENT = TempoSession.Precompile.escrowAbi.find(
|
|
|
9999
10153
|
(item) => item.type === "event" && item.name === "ChannelClosed"
|
|
10000
10154
|
);
|
|
10001
10155
|
function hash2(value) {
|
|
10002
|
-
return
|
|
10156
|
+
return createHash7("sha256").update(JSON.stringify(value)).digest("hex");
|
|
10003
10157
|
}
|
|
10004
10158
|
function hex(value, bytes) {
|
|
10005
10159
|
return typeof value === "string" && new RegExp(`^0x[0-9a-fA-F]{${bytes * 2}}$`).test(value);
|
|
@@ -11387,7 +11541,7 @@ var HostingCleanupFence = class {
|
|
|
11387
11541
|
};
|
|
11388
11542
|
|
|
11389
11543
|
// src/sdk/server/hosting-channel-evidence.ts
|
|
11390
|
-
import { createHash as
|
|
11544
|
+
import { createHash as createHash8 } from "crypto";
|
|
11391
11545
|
import { Session as TempoSession2 } from "mppx/tempo";
|
|
11392
11546
|
import { createPublicClient as createPublicClient5, http as http5 } from "viem";
|
|
11393
11547
|
import { BATCH_SETTLEMENT_ADDRESS as BATCH_SETTLEMENT_ADDRESS4 } from "@x402/evm";
|
|
@@ -11438,7 +11592,7 @@ async function readHostedChannelSnapshot(query) {
|
|
|
11438
11592
|
};
|
|
11439
11593
|
const x402HistoryComplete = await history(names[6]);
|
|
11440
11594
|
const tempoHistoryComplete = await history(names[7]);
|
|
11441
|
-
const digest =
|
|
11595
|
+
const digest = createHash8("sha256").update(JSON.stringify({
|
|
11442
11596
|
present: [...exists].sort(),
|
|
11443
11597
|
x402,
|
|
11444
11598
|
x402Tombstones,
|
|
@@ -12588,9 +12742,9 @@ import { AsyncLocalStorage } from "async_hooks";
|
|
|
12588
12742
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
12589
12743
|
|
|
12590
12744
|
// src/sdk/server/instrumented-kv.ts
|
|
12591
|
-
import { createHash as
|
|
12745
|
+
import { createHash as createHash9 } from "crypto";
|
|
12592
12746
|
function hashKey(key) {
|
|
12593
|
-
return
|
|
12747
|
+
return createHash9("sha256").update(key).digest("hex").slice(0, 16);
|
|
12594
12748
|
}
|
|
12595
12749
|
function instrumentKVStore(inner) {
|
|
12596
12750
|
return {
|
|
@@ -12904,7 +13058,7 @@ function buildContext(job, opts) {
|
|
|
12904
13058
|
let x402Nonce;
|
|
12905
13059
|
let x402AmountUsdcMicro;
|
|
12906
13060
|
const hasExplicitX402 = Boolean(payOpts?.x402);
|
|
12907
|
-
if ((opts.pricingCurrency ?? "usd") === "usd") {
|
|
13061
|
+
if (!opts.cashuOnly && (opts.pricingCurrency ?? "usd") === "usd") {
|
|
12908
13062
|
if (payOpts?.x402) {
|
|
12909
13063
|
paymentRequestContent.x402 = payOpts.x402;
|
|
12910
13064
|
} else if (opts.paymentMethods?.includes("x402") && opts.x402 && (opts.x402FacilitatorHealth?.support().available ?? true)) {
|
|
@@ -12943,7 +13097,8 @@ function buildContext(job, opts) {
|
|
|
12943
13097
|
job.pendingX402Nonce = x402Nonce;
|
|
12944
13098
|
job.pendingX402AmountUsdcMicro = x402AmountUsdcMicro;
|
|
12945
13099
|
let mppChallengeIds;
|
|
12946
|
-
if (
|
|
13100
|
+
if (opts.cashuOnly) {
|
|
13101
|
+
} else if (payOpts?.mpp) {
|
|
12947
13102
|
const challenges = payOpts.mpp.challenges.filter((c) => c.intent !== "session");
|
|
12948
13103
|
if (challenges.length > 0) {
|
|
12949
13104
|
paymentRequestContent.tempo = { challenges };
|
|
@@ -13351,6 +13506,39 @@ function resultHashFor(record) {
|
|
|
13351
13506
|
return computeResultHash(summary, artifacts);
|
|
13352
13507
|
}
|
|
13353
13508
|
|
|
13509
|
+
// src/sdk/server/unsigned-request.ts
|
|
13510
|
+
function takesUnsignedPath(auth, body) {
|
|
13511
|
+
if (!auth?.allowUnsignedPaid) return false;
|
|
13512
|
+
const raw = signedRequestInput(body);
|
|
13513
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return true;
|
|
13514
|
+
return !SIGNED_ENVELOPE_FIELDS.some((field) => field in raw);
|
|
13515
|
+
}
|
|
13516
|
+
function carriesCreditFields(raw) {
|
|
13517
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return false;
|
|
13518
|
+
return CREDIT_ENVELOPE_KEYS.some((field) => field in raw);
|
|
13519
|
+
}
|
|
13520
|
+
function unsignedInputSchema(schema) {
|
|
13521
|
+
if (narrowed.has(schema)) return narrowed.get(schema);
|
|
13522
|
+
const result = narrow(schema);
|
|
13523
|
+
narrowed.set(schema, result);
|
|
13524
|
+
return result;
|
|
13525
|
+
}
|
|
13526
|
+
var narrowed = /* @__PURE__ */ new WeakMap();
|
|
13527
|
+
function narrow(schema) {
|
|
13528
|
+
const candidate = schema;
|
|
13529
|
+
const def = candidate._zod?.def;
|
|
13530
|
+
if (def?.type !== "object" || typeof candidate.clone !== "function" || candidate.shape === null || typeof candidate.shape !== "object") {
|
|
13531
|
+
return void 0;
|
|
13532
|
+
}
|
|
13533
|
+
const shape = { ...candidate.shape };
|
|
13534
|
+
for (const field of SIGNED_ENVELOPE_FIELDS) delete shape[field];
|
|
13535
|
+
try {
|
|
13536
|
+
return candidate.clone({ ...def, shape });
|
|
13537
|
+
} catch {
|
|
13538
|
+
return void 0;
|
|
13539
|
+
}
|
|
13540
|
+
}
|
|
13541
|
+
|
|
13354
13542
|
// src/lib/cashu/mint-info.ts
|
|
13355
13543
|
async function getKeysetFinalExpiry(mintUrl, keysetId) {
|
|
13356
13544
|
try {
|
|
@@ -13814,11 +14002,12 @@ var JobManager = class {
|
|
|
13814
14002
|
* `"$X.XX"` is parsed as USD and converted via the SDK's shared fx fetcher.
|
|
13815
14003
|
* Returns `undefined` for dynamic-priced capabilities (those that declare
|
|
13816
14004
|
* `onQuote`) — the caller falls back to `computeDynamicPrice` in that path.
|
|
14005
|
+
* A caller that already holds the snapshot it prices at passes it as `fx`.
|
|
13817
14006
|
*/
|
|
13818
|
-
async currentPriceMsats(capability) {
|
|
14007
|
+
async currentPriceMsats(capability, fx) {
|
|
13819
14008
|
const cap = this.requireCapability(capability);
|
|
13820
14009
|
if (cap.price === void 0) return void 0;
|
|
13821
|
-
return resolveStaticPriceMsats(cap.price, this.opts.fxFetcher);
|
|
14010
|
+
return resolveStaticPriceMsats(cap.price, fx ?? await this.opts.fxFetcher.fetch());
|
|
13822
14011
|
}
|
|
13823
14012
|
/**
|
|
13824
14013
|
* Translate a capability's static `price` into the fiat envelope used by
|
|
@@ -13851,12 +14040,16 @@ var JobManager = class {
|
|
|
13851
14040
|
* explicit draw as an unknown key, before the envelope was even extracted.
|
|
13852
14041
|
* The persisted `record.input` the reactivation path re-reads is the
|
|
13853
14042
|
* pre-Zod wire form, so it comes through the second branch carrying them too.
|
|
14043
|
+
*
|
|
14044
|
+
* `unsigned` validates against the schema without its envelope fields; pass
|
|
14045
|
+
* it only when {@link unsignedPaidInput} accepted the same body.
|
|
13854
14046
|
*/
|
|
13855
|
-
parseInput(body, capability) {
|
|
14047
|
+
parseInput(body, capability, unsigned2 = false) {
|
|
13856
14048
|
const cap = this.requireCapability(capability);
|
|
13857
14049
|
const rawSchema = cap.input;
|
|
13858
14050
|
if (!rawSchema || !isZodSchema(rawSchema)) return body.input ?? "";
|
|
13859
|
-
const schema = rawSchema;
|
|
14051
|
+
const schema = unsigned2 ? unsignedInputSchema(rawSchema) : rawSchema;
|
|
14052
|
+
if (!schema) throw new Error(`capability "${capability}" cannot validate unsigned input`);
|
|
13860
14053
|
if (body.data !== void 0) {
|
|
13861
14054
|
return schema.parse(stripProtocolEnvelope(schema, body.data));
|
|
13862
14055
|
}
|
|
@@ -13866,6 +14059,16 @@ var JobManager = class {
|
|
|
13866
14059
|
}
|
|
13867
14060
|
throw new MissingStructuredInputError();
|
|
13868
14061
|
}
|
|
14062
|
+
/**
|
|
14063
|
+
* Whether this body takes the unsigned paid path: the auth scheme allows it,
|
|
14064
|
+
* the raw input carries no envelope field, and the capability schema can be
|
|
14065
|
+
* narrowed to exclude the envelope. Otherwise the signed path applies.
|
|
14066
|
+
*/
|
|
14067
|
+
unsignedPaidInput(body, capability) {
|
|
14068
|
+
if (!takesUnsignedPath(this.descriptor.auth, body)) return false;
|
|
14069
|
+
const rawSchema = this.requireCapability(capability).input;
|
|
14070
|
+
return isZodSchema(rawSchema) && unsignedInputSchema(rawSchema) !== void 0;
|
|
14071
|
+
}
|
|
13869
14072
|
/**
|
|
13870
14073
|
* Look up the per-capability descriptor by name. Throws when the
|
|
13871
14074
|
* name doesn't exist — the route layer validates body.capability first, so
|
|
@@ -15352,8 +15555,9 @@ var JobManager = class {
|
|
|
15352
15555
|
* it re-verified clean.
|
|
15353
15556
|
*/
|
|
15354
15557
|
reactivateJob(record) {
|
|
15355
|
-
const
|
|
15356
|
-
|
|
15558
|
+
const unsigned2 = record.authRequestPath === void 0 && record.requesterPubkey === void 0 && this.unsignedPaidInput({ input: record.input }, record.capability);
|
|
15559
|
+
const parsedInput = this.parseInput({ input: record.input }, record.capability, unsigned2);
|
|
15560
|
+
if (this.descriptor.auth && !unsigned2) {
|
|
15357
15561
|
const cap = this.requireCapability(record.capability);
|
|
15358
15562
|
if (!cap.input || !isZodSchema(cap.input)) {
|
|
15359
15563
|
throw new ReactivationAuthSchemaError(record.id, record.capability);
|
|
@@ -15897,6 +16101,7 @@ var JobManager = class {
|
|
|
15897
16101
|
store: this.opts.store,
|
|
15898
16102
|
env: this.opts.env,
|
|
15899
16103
|
paymentMethods: this.opts.paymentMethods,
|
|
16104
|
+
cashuOnly: Boolean(this.descriptor.auth?.allowUnsignedPaid) && job.requesterPubkey === void 0,
|
|
15900
16105
|
x402: this.opts.x402,
|
|
15901
16106
|
x402FacilitatorHealth: this.opts.x402FacilitatorHealth,
|
|
15902
16107
|
mpp: this.opts.mpp,
|
|
@@ -16701,9 +16906,8 @@ function unappliedCopy(reason, creditId) {
|
|
|
16701
16906
|
}
|
|
16702
16907
|
}[reason];
|
|
16703
16908
|
}
|
|
16704
|
-
|
|
16909
|
+
function resolveStaticPriceMsats(price, fx) {
|
|
16705
16910
|
const usd = parseUsdPrice(price, "price");
|
|
16706
|
-
const fx = await fxFetcher.fetch();
|
|
16707
16911
|
return fiatToSatsCeil(usd, fxRateFor(fx, "usd")) * 1e3;
|
|
16708
16912
|
}
|
|
16709
16913
|
function deriveAuthFromParsedInput(descriptor, parsedInput) {
|
|
@@ -17769,7 +17973,7 @@ function withWallClockDeadline(promise, timeoutMs) {
|
|
|
17769
17973
|
}
|
|
17770
17974
|
|
|
17771
17975
|
// src/sdk/server/balance-payment.ts
|
|
17772
|
-
import { createHash as
|
|
17976
|
+
import { createHash as createHash10 } from "crypto";
|
|
17773
17977
|
import { z as z2 } from "zod";
|
|
17774
17978
|
var BALANCE_PAYMENT_SCHEMA = z2.object({
|
|
17775
17979
|
credit_id: z2.string().min(1).max(256),
|
|
@@ -17799,7 +18003,7 @@ async function commitBalancePayment(args) {
|
|
|
17799
18003
|
args.auth.recheckSignature(BALANCE_PAYMENT_SCHEMA, args.raw, args.domain);
|
|
17800
18004
|
const auth = BALANCE_PAYMENT_SCHEMA.parse(args.raw);
|
|
17801
18005
|
const { pubkey, credit_id, draw_id, payment_id, request_seq, amount_micro, currency } = auth;
|
|
17802
|
-
const fingerprint =
|
|
18006
|
+
const fingerprint = createHash10("sha256").update(JSON.stringify([
|
|
17803
18007
|
args.domain,
|
|
17804
18008
|
args.jobId,
|
|
17805
18009
|
pubkey,
|
|
@@ -17923,7 +18127,7 @@ async function commitBalancePayment(args) {
|
|
|
17923
18127
|
}
|
|
17924
18128
|
|
|
17925
18129
|
// src/sdk/server/app.ts
|
|
17926
|
-
import { createHash as
|
|
18130
|
+
import { createHash as createHash12, randomBytes as randomBytes5, randomUUID as randomUUID6 } from "crypto";
|
|
17927
18131
|
import { Hono } from "hono";
|
|
17928
18132
|
import { getCookie } from "hono/cookie";
|
|
17929
18133
|
import { cors } from "hono/cors";
|
|
@@ -20974,7 +21178,7 @@ initSchemaForm();
|
|
|
20974
21178
|
}
|
|
20975
21179
|
|
|
20976
21180
|
// src/sdk/server/request-fingerprint.ts
|
|
20977
|
-
import { createHash as
|
|
21181
|
+
import { createHash as createHash11 } from "crypto";
|
|
20978
21182
|
function requestFingerprint(req, opts) {
|
|
20979
21183
|
const canonical = canonicalize({
|
|
20980
21184
|
capability: req.capability,
|
|
@@ -20982,7 +21186,7 @@ function requestFingerprint(req, opts) {
|
|
|
20982
21186
|
params: req.params ?? {},
|
|
20983
21187
|
data: opts.stripAuthEnvelope ? stripEnvelope(req.data) : req.data
|
|
20984
21188
|
});
|
|
20985
|
-
return
|
|
21189
|
+
return createHash11("sha256").update(canonical).digest("hex");
|
|
20986
21190
|
}
|
|
20987
21191
|
var ENVELOPE_KEYS = /* @__PURE__ */ new Set(["pubkey", "signature", "timestamp", "nonce", "auth_statement"]);
|
|
20988
21192
|
function stripEnvelope(data) {
|
|
@@ -21105,7 +21309,7 @@ async function createDVMServer(descriptor, opts) {
|
|
|
21105
21309
|
}
|
|
21106
21310
|
}
|
|
21107
21311
|
if (!opts.onJobCompleted && platformToken && platformUrl && dvmId && opts.db) {
|
|
21108
|
-
const { RevenueReporter } = await import("./revenue-reporter-
|
|
21312
|
+
const { RevenueReporter } = await import("./revenue-reporter-6XARUEG6.js");
|
|
21109
21313
|
const reporter = new RevenueReporter(
|
|
21110
21314
|
opts.db,
|
|
21111
21315
|
platformUrl,
|
|
@@ -21115,7 +21319,7 @@ async function createDVMServer(descriptor, opts) {
|
|
|
21115
21319
|
revenueReporter = reporter;
|
|
21116
21320
|
await reporter.init();
|
|
21117
21321
|
reporter.startRetryLoop();
|
|
21118
|
-
const { MemoryX402BatchStore, PayoutReporter } = await import("./payout-reporter-
|
|
21322
|
+
const { MemoryX402BatchStore, PayoutReporter } = await import("./payout-reporter-5QAHCYIO.js");
|
|
21119
21323
|
payoutReporter = new PayoutReporter({
|
|
21120
21324
|
dvmId,
|
|
21121
21325
|
db: opts.db,
|
|
@@ -21396,7 +21600,9 @@ var DVMServer = class {
|
|
|
21396
21600
|
this.consumedCredentialStore = opts.consumedCredentialStore ?? new MemoryConsumedCredentialStore();
|
|
21397
21601
|
this.adminCashuNonceStore = opts.adminCashuNonceStore ?? createAdminCashuNonceStore();
|
|
21398
21602
|
const fxEnv = opts.env;
|
|
21399
|
-
this.fxFetcher =
|
|
21603
|
+
this.fxFetcher = withFxHistory(
|
|
21604
|
+
opts.fxFetcher ?? createFxFetcher({ source: resolveFxSourceFromEnv(fxEnv) })
|
|
21605
|
+
);
|
|
21400
21606
|
const fxCurrencies = this.fxFetcher.supportedCurrencies();
|
|
21401
21607
|
if (!fxCurrencies.includes(this.pricingCurrency)) {
|
|
21402
21608
|
throw new Error(
|
|
@@ -21505,6 +21711,15 @@ var DVMServer = class {
|
|
|
21505
21711
|
this.app = new Hono();
|
|
21506
21712
|
try {
|
|
21507
21713
|
this.registerRoutes();
|
|
21714
|
+
if (opts.mints?.length && !this.resolvePaymentMethods().includes("cashu")) {
|
|
21715
|
+
diagnosticConsole.warn({
|
|
21716
|
+
event: "cashu_not_advertised",
|
|
21717
|
+
cashu_explicitly_excluded: opts.paymentMethods !== void 0 && !opts.paymentMethods.includes("cashu"),
|
|
21718
|
+
cashu_mode_missing: opts.cashuMode !== "p2pk-accumulator",
|
|
21719
|
+
cashu_lock_pubkey_missing: !opts.lockPubkey,
|
|
21720
|
+
cashu_store_missing: !opts.db && !opts.disposableCashu
|
|
21721
|
+
});
|
|
21722
|
+
}
|
|
21508
21723
|
if (opts.hostingControlInstance) {
|
|
21509
21724
|
this.hostingSweepTimer = setInterval(() => {
|
|
21510
21725
|
void this.sweepHostingDeadline().catch((error) => {
|
|
@@ -21714,7 +21929,7 @@ var DVMServer = class {
|
|
|
21714
21929
|
})
|
|
21715
21930
|
);
|
|
21716
21931
|
if (this.opts.devMode) {
|
|
21717
|
-
this.app.use("*", cors());
|
|
21932
|
+
this.app.use("*", cors({ exposeHeaders: ["X-Cashu"] }));
|
|
21718
21933
|
this.app.get("/_dev", (c) => c.html(devPageHtml(this.descriptor.name)));
|
|
21719
21934
|
this.app.all("/_proxy/*", (c) => this.handleProxy(c));
|
|
21720
21935
|
}
|
|
@@ -22028,7 +22243,7 @@ var DVMServer = class {
|
|
|
22028
22243
|
const dvmId = this.opts.dvmId ?? `dev:${this.descriptor.name}`;
|
|
22029
22244
|
return {
|
|
22030
22245
|
dvm_id: dvmId,
|
|
22031
|
-
builder_pubkey:
|
|
22246
|
+
builder_pubkey: createHash12("sha256").update(`dvmkit-dev:${dvmId}`).digest("hex")
|
|
22032
22247
|
};
|
|
22033
22248
|
}
|
|
22034
22249
|
throw new AuthAudienceError();
|
|
@@ -22126,7 +22341,7 @@ var DVMServer = class {
|
|
|
22126
22341
|
const methods = this.resolvePaymentMethods().filter(
|
|
22127
22342
|
(method) => method !== "tempo" || creditMpp
|
|
22128
22343
|
);
|
|
22129
|
-
if (this.x402CurrencySupported() && this.x402BatchAvailable() && !methods.includes("x402")) {
|
|
22344
|
+
if ((this.opts.paymentMethods === void 0 || this.opts.paymentMethods.includes("x402")) && this.x402CurrencySupported() && this.x402BatchAvailable() && !methods.includes("x402")) {
|
|
22130
22345
|
methods.push("x402");
|
|
22131
22346
|
}
|
|
22132
22347
|
if (this.opts.lightningReceive?.available()) methods.push("lightning");
|
|
@@ -22149,7 +22364,9 @@ var DVMServer = class {
|
|
|
22149
22364
|
* distinction, and a reason it can relay.
|
|
22150
22365
|
*/
|
|
22151
22366
|
creditMppSplit() {
|
|
22152
|
-
if (!this.opts.mpp)
|
|
22367
|
+
if (!this.opts.mpp || !this.resolvePaymentMethods().includes("tempo")) {
|
|
22368
|
+
return { offered: [], withheld: [] };
|
|
22369
|
+
}
|
|
22153
22370
|
const configured = advertisedMethods(this.opts.mpp).filter(
|
|
22154
22371
|
(method) => method.intent === "charge" || method.intent === "session"
|
|
22155
22372
|
).sort(
|
|
@@ -22381,7 +22598,7 @@ var DVMServer = class {
|
|
|
22381
22598
|
const paymentAuthorization = ctx.req.header("Authorization");
|
|
22382
22599
|
const mppArtifact = paymentAuthorization ? (await import("mppx")).Credential.extractPaymentScheme(paymentAuthorization) : void 0;
|
|
22383
22600
|
const attachedPayment = ctx.req.header("X-Cashu") ?? ctx.req.header("PAYMENT-SIGNATURE") ?? ctx.req.header("X-PAYMENT") ?? mppArtifact;
|
|
22384
|
-
const paymentArtifactHash = attachedPayment ?
|
|
22601
|
+
const paymentArtifactHash = attachedPayment ? createHash12("sha256").update(attachedPayment).digest("hex") : void 0;
|
|
22385
22602
|
hostingFundingArtifactHash = paymentArtifactHash;
|
|
22386
22603
|
if (!await control.admit(admissionId, workId, {
|
|
22387
22604
|
creditId,
|
|
@@ -22481,7 +22698,7 @@ var DVMServer = class {
|
|
|
22481
22698
|
name: this.descriptor.name,
|
|
22482
22699
|
description: this.descriptor.description || void 0,
|
|
22483
22700
|
tags: this.descriptor.tags,
|
|
22484
|
-
auth: this.descriptor.auth ? [this.descriptor.auth.
|
|
22701
|
+
auth: !this.descriptor.auth ? ["none"] : this.descriptor.auth.allowUnsignedPaid ? [this.descriptor.auth.id, "none"] : [this.descriptor.auth.id],
|
|
22485
22702
|
...this.authAudience ? { auth_audience: this.authAudience } : {},
|
|
22486
22703
|
protocol_version: 1,
|
|
22487
22704
|
sdk_sha: this.opts.env.GIT_SHA ?? "unknown",
|
|
@@ -22500,7 +22717,7 @@ var DVMServer = class {
|
|
|
22500
22717
|
if (this.opts.mints) info.mints = this.advertisedMints();
|
|
22501
22718
|
info.capabilities = this.renderCapabilities();
|
|
22502
22719
|
info.payment = { methods: this.resolvePaymentMethods() };
|
|
22503
|
-
if (
|
|
22720
|
+
if (info.payment.methods.includes("x402") && this.opts.x402 && this.x402Support()?.available) {
|
|
22504
22721
|
info.x402 = {
|
|
22505
22722
|
pay_to: this.opts.x402.payTo,
|
|
22506
22723
|
network: this.opts.x402.network,
|
|
@@ -22510,7 +22727,7 @@ var DVMServer = class {
|
|
|
22510
22727
|
dual_serve: this.opts.x402.dualServe ?? true
|
|
22511
22728
|
};
|
|
22512
22729
|
}
|
|
22513
|
-
if (this.opts.mpp) {
|
|
22730
|
+
if (info.payment.methods.includes("tempo") && this.opts.mpp) {
|
|
22514
22731
|
const configured = advertisedMethods(this.opts.mpp);
|
|
22515
22732
|
const withheld = configured.flatMap((method) => {
|
|
22516
22733
|
const reason = this.tempoSessionWithheldReason(method);
|
|
@@ -22885,6 +23102,48 @@ var DVMServer = class {
|
|
|
22885
23102
|
400
|
|
22886
23103
|
);
|
|
22887
23104
|
}
|
|
23105
|
+
/**
|
|
23106
|
+
* Refuse an x402 or Tempo mid-job payment on an unsigned paid job before it
|
|
23107
|
+
* is recorded or verified: those rails settle into credit owned by a caller
|
|
23108
|
+
* pubkey, which the job lacks. Signed jobs always store their pubkey.
|
|
23109
|
+
*/
|
|
23110
|
+
unsignedJobRailRefusal(job, body) {
|
|
23111
|
+
if (!this.descriptor.auth?.allowUnsignedPaid || job.requesterPubkey !== void 0) return void 0;
|
|
23112
|
+
if (body.type !== "payment" || !body.content.x402_payment && !body.content.tempo_credential) {
|
|
23113
|
+
return void 0;
|
|
23114
|
+
}
|
|
23115
|
+
return buildPaymentErrorResponse(
|
|
23116
|
+
paymentErrorBody("payment_method_requires_signature", {
|
|
23117
|
+
message: "Unsigned jobs may be paid only with Cashu; an x402 or Tempo payment was sent."
|
|
23118
|
+
}),
|
|
23119
|
+
void 0,
|
|
23120
|
+
void 0,
|
|
23121
|
+
402
|
|
23122
|
+
);
|
|
23123
|
+
}
|
|
23124
|
+
/** Whether this DVM takes Cashu, the only rail an unsigned payer may use. */
|
|
23125
|
+
unsignedCashuOffered() {
|
|
23126
|
+
return (this.opts.mints?.length ?? 0) > 0 && this.resolvePaymentMethods().includes("cashu");
|
|
23127
|
+
}
|
|
23128
|
+
/**
|
|
23129
|
+
* The error the signed path raises for this envelope-free body, so a refused
|
|
23130
|
+
* unsigned request reads exactly as it does on a DVM without the option.
|
|
23131
|
+
*/
|
|
23132
|
+
unsignedRequestRefusal(c, schema, body, capabilityName) {
|
|
23133
|
+
try {
|
|
23134
|
+
if (this.descriptor.auth && isZodSchema(schema)) {
|
|
23135
|
+
this.descriptor.auth.checkAuth(
|
|
23136
|
+
schema,
|
|
23137
|
+
signedRequestInput(body),
|
|
23138
|
+
this.signedRequestDomain(c, capabilityName)
|
|
23139
|
+
);
|
|
23140
|
+
}
|
|
23141
|
+
} catch (err) {
|
|
23142
|
+
if (err instanceof SignedRequestError) return err;
|
|
23143
|
+
throw err;
|
|
23144
|
+
}
|
|
23145
|
+
return new SignedRequestError("signed request required", "signature_invalid");
|
|
23146
|
+
}
|
|
22888
23147
|
/**
|
|
22889
23148
|
* On authenticated DVMs, envelope-only parse failures defer to auth
|
|
22890
23149
|
* (401 signature_invalid). Builder input issues remain 400 invalid_input.
|
|
@@ -22914,6 +23173,25 @@ var DVMServer = class {
|
|
|
22914
23173
|
if (healthy.length > 0) return healthy;
|
|
22915
23174
|
return all.filter((m) => !tracker.isNutIncompatible(m));
|
|
22916
23175
|
}
|
|
23176
|
+
/**
|
|
23177
|
+
* This request's price at the rates `pricedAt` replaced, or `undefined` when
|
|
23178
|
+
* there are none or they were replaced longer ago than the hold window.
|
|
23179
|
+
* Computed the way the current price is, so the figure matches the one an
|
|
23180
|
+
* earlier 402 quoted.
|
|
23181
|
+
*/
|
|
23182
|
+
previousPriceMsats(cap, dynamicUpfront, pricedAt) {
|
|
23183
|
+
const previous = pricedAt && this.fxFetcher.previousFor(pricedAt);
|
|
23184
|
+
if (!previous) return void 0;
|
|
23185
|
+
try {
|
|
23186
|
+
if (dynamicUpfront) {
|
|
23187
|
+
return fiatToSatsCeil(dynamicUpfront.amount, fxRateFor(previous, dynamicUpfront.currency)) * 1e3;
|
|
23188
|
+
}
|
|
23189
|
+
if (cap.price === void 0) return void 0;
|
|
23190
|
+
return fiatToSatsCeil(parseUsdPrice(cap.price, "price"), fxRateFor(previous, "usd")) * 1e3;
|
|
23191
|
+
} catch {
|
|
23192
|
+
return void 0;
|
|
23193
|
+
}
|
|
23194
|
+
}
|
|
22917
23195
|
/** Authenticated, read-only recovery of a previously accepted public request id. */
|
|
22918
23196
|
async handleRequestResult(c) {
|
|
22919
23197
|
const requestId = c.req.param("requestId");
|
|
@@ -22984,6 +23262,14 @@ var DVMServer = class {
|
|
|
22984
23262
|
400
|
|
22985
23263
|
);
|
|
22986
23264
|
}
|
|
23265
|
+
if (c.req.header("X-Cashu-Request-Id")?.startsWith(DERIVED_REQUEST_ID_PREFIX)) {
|
|
23266
|
+
return c.json(
|
|
23267
|
+
paymentErrorBody("reserved_request_id", {
|
|
23268
|
+
message: `X-Cashu-Request-Id may not start with "${DERIVED_REQUEST_ID_PREFIX}".`
|
|
23269
|
+
}),
|
|
23270
|
+
400
|
|
23271
|
+
);
|
|
23272
|
+
}
|
|
22987
23273
|
const publicRequestId = requestIdFromEnvelope(body.data);
|
|
22988
23274
|
const recoveryStore = requestRecoveryStore(this.jobManager.store);
|
|
22989
23275
|
const resumedRequestId = resumeOnly ? c.req.param("requestId") : void 0;
|
|
@@ -23002,6 +23288,17 @@ var DVMServer = class {
|
|
|
23002
23288
|
const capabilityResult = this.requireCapabilityFromBody(c, body.capability);
|
|
23003
23289
|
if ("error" in capabilityResult) return capabilityResult.error;
|
|
23004
23290
|
const capabilityName = capabilityResult.name;
|
|
23291
|
+
const unsignedPaid = this.jobManager.unsignedPaidInput(body, capabilityName);
|
|
23292
|
+
if (unsignedPaid && publicRequestId) {
|
|
23293
|
+
return c.json(
|
|
23294
|
+
{
|
|
23295
|
+
error: "request_recovery_unsupported",
|
|
23296
|
+
message: "Public request ids require a signed request.",
|
|
23297
|
+
hint: "Sign the request to use request_id recovery, or omit request_id."
|
|
23298
|
+
},
|
|
23299
|
+
400
|
|
23300
|
+
);
|
|
23301
|
+
}
|
|
23005
23302
|
pinDimension("capability", capabilityName);
|
|
23006
23303
|
const cap = this.descriptor.capabilities[capabilityName];
|
|
23007
23304
|
let parsedInput;
|
|
@@ -23010,13 +23307,23 @@ var DVMServer = class {
|
|
|
23010
23307
|
parsedInput = await loggers.isolate.span(
|
|
23011
23308
|
"dvm.input.validate",
|
|
23012
23309
|
{ capability: capabilityName, stage: "input" },
|
|
23013
|
-
() => this.jobManager.parseInput(body, capabilityName),
|
|
23310
|
+
() => this.jobManager.parseInput(body, capabilityName, unsignedPaid),
|
|
23014
23311
|
{ onError: () => ({ "error.code": "invalid_input" }) }
|
|
23015
23312
|
);
|
|
23016
23313
|
} catch (err) {
|
|
23017
|
-
if (!this.deferToAuthGate(err)) return this.invalidInputResponse(c, err);
|
|
23314
|
+
if (unsignedPaid || !this.deferToAuthGate(err)) return this.invalidInputResponse(c, err);
|
|
23018
23315
|
deferredInputError = err;
|
|
23019
23316
|
}
|
|
23317
|
+
if (unsignedPaid && carriesCreditFields(signedRequestInput(body))) {
|
|
23318
|
+
return c.json(
|
|
23319
|
+
{
|
|
23320
|
+
error: "invalid_credit_fields",
|
|
23321
|
+
message: "credit_id, draw_id and fund require a signed request.",
|
|
23322
|
+
hint: "Sign the request to draw on or fund prepaid credit, or remove the credit fields and pay per call."
|
|
23323
|
+
},
|
|
23324
|
+
400
|
|
23325
|
+
);
|
|
23326
|
+
}
|
|
23020
23327
|
let creditEnvelope;
|
|
23021
23328
|
try {
|
|
23022
23329
|
creditEnvelope = extractCreditEnvelope(body.data);
|
|
@@ -23033,9 +23340,26 @@ var DVMServer = class {
|
|
|
23033
23340
|
}
|
|
23034
23341
|
throw err;
|
|
23035
23342
|
}
|
|
23343
|
+
if (unsignedPaid && (cap.price === void 0 && !cap.onQuote || devModeSkipsPaymentVerification(this.opts) || !this.unsignedCashuOffered())) {
|
|
23344
|
+
return buildAuthErrorResponse(
|
|
23345
|
+
c,
|
|
23346
|
+
this.unsignedRequestRefusal(c, cap.input, body, capabilityName),
|
|
23347
|
+
this.publicOrigin
|
|
23348
|
+
);
|
|
23349
|
+
}
|
|
23350
|
+
if (unsignedPaid && await carriesNonCashuPayment(c)) {
|
|
23351
|
+
return buildPaymentErrorResponse(
|
|
23352
|
+
paymentErrorBody("payment_method_requires_signature", {
|
|
23353
|
+
message: "Unsigned requests may pay only with Cashu; an x402 or Tempo payment was attached."
|
|
23354
|
+
}),
|
|
23355
|
+
void 0,
|
|
23356
|
+
void 0,
|
|
23357
|
+
402
|
|
23358
|
+
);
|
|
23359
|
+
}
|
|
23036
23360
|
let authEnvelope;
|
|
23037
23361
|
let authPubkey;
|
|
23038
|
-
if (this.descriptor.auth) {
|
|
23362
|
+
if (this.descriptor.auth && !unsignedPaid) {
|
|
23039
23363
|
const auth = this.descriptor.auth;
|
|
23040
23364
|
try {
|
|
23041
23365
|
const result = await loggers.isolate.span(
|
|
@@ -23083,14 +23407,14 @@ var DVMServer = class {
|
|
|
23083
23407
|
{ capability: capabilityName, input: body.input, params: body.params, data: body.data },
|
|
23084
23408
|
{ stripAuthEnvelope: this.descriptor.auth !== void 0 }
|
|
23085
23409
|
);
|
|
23086
|
-
const authFingerprint =
|
|
23410
|
+
const authFingerprint = authEnvelope ? requestFingerprint(
|
|
23087
23411
|
{ capability: capabilityName, input: body.input, params: body.params, data: body.data },
|
|
23088
23412
|
{ stripAuthEnvelope: false }
|
|
23089
23413
|
) : void 0;
|
|
23090
23414
|
const authorization = c.req.header("Authorization");
|
|
23091
23415
|
const mppArtifact = authorization ? (await import("mppx")).Credential.extractPaymentScheme(authorization) : void 0;
|
|
23092
23416
|
const attachedPayment = c.req.header("X-Cashu") ?? c.req.header("PAYMENT-SIGNATURE") ?? c.req.header("X-PAYMENT") ?? mppArtifact;
|
|
23093
|
-
const hostingPaymentArtifactHash = attachedPayment ?
|
|
23417
|
+
const hostingPaymentArtifactHash = attachedPayment ? createHash12("sha256").update(attachedPayment).digest("hex") : void 0;
|
|
23094
23418
|
if (!resumeOnly && this.opts.hostingControlInstance && hostingPaymentArtifactHash) {
|
|
23095
23419
|
const priorId = await this.opts.hostingControlInstance.acceptedJobByPaymentArtifactHash(
|
|
23096
23420
|
hostingPaymentArtifactHash
|
|
@@ -23172,9 +23496,11 @@ var DVMServer = class {
|
|
|
23172
23496
|
}
|
|
23173
23497
|
const skipPayment = devModeSkipsPaymentVerification(this.opts);
|
|
23174
23498
|
let priceMsats = resumeOnly ? payment.paidMsats || void 0 : void 0;
|
|
23499
|
+
let pricedAt;
|
|
23175
23500
|
if (!resumeOnly && !skipPayment) {
|
|
23176
23501
|
try {
|
|
23177
|
-
|
|
23502
|
+
if (cap.price !== void 0) pricedAt = await this.fxFetcher.fetch();
|
|
23503
|
+
priceMsats = await this.jobManager.currentPriceMsats(capabilityName, pricedAt);
|
|
23178
23504
|
} catch (err) {
|
|
23179
23505
|
const fxErr = pickFxRateUnavailable(err);
|
|
23180
23506
|
if (fxErr) return c.json({ error: fxErr }, 503);
|
|
@@ -23188,17 +23514,26 @@ var DVMServer = class {
|
|
|
23188
23514
|
parsedInput,
|
|
23189
23515
|
normalisedBody,
|
|
23190
23516
|
authPubkey,
|
|
23191
|
-
skipPayment
|
|
23517
|
+
skipPayment,
|
|
23518
|
+
unsignedPaid
|
|
23192
23519
|
);
|
|
23193
23520
|
if (dyn.error) return c.json(dyn.error.body, dyn.error.status);
|
|
23194
23521
|
priceMsats = dyn.priceMsats;
|
|
23195
23522
|
dynamicUpfront = dyn.upfront;
|
|
23523
|
+
pricedAt = dyn.pricedAt;
|
|
23196
23524
|
}
|
|
23197
23525
|
const priceFiat = resumeOnly ? void 0 : resolvePriceFiat({
|
|
23198
23526
|
price: typeof cap.price === "string" ? cap.price : void 0,
|
|
23199
23527
|
priceMsats,
|
|
23200
23528
|
dynamicUpfront
|
|
23201
23529
|
});
|
|
23530
|
+
if (unsignedPaid && !priceMsats) {
|
|
23531
|
+
return buildAuthErrorResponse(
|
|
23532
|
+
c,
|
|
23533
|
+
this.unsignedRequestRefusal(c, cap.input, body, capabilityName),
|
|
23534
|
+
this.publicOrigin
|
|
23535
|
+
);
|
|
23536
|
+
}
|
|
23202
23537
|
if (!resumeOnly && creditEnvelope?.creditId) {
|
|
23203
23538
|
await this.settleLightningFunding(authPubkey, creditEnvelope.creditId);
|
|
23204
23539
|
}
|
|
@@ -23221,7 +23556,13 @@ var DVMServer = class {
|
|
|
23221
23556
|
}
|
|
23222
23557
|
}
|
|
23223
23558
|
if (!skipPayment && !resumedAcceptedPayment) {
|
|
23224
|
-
const railOpts = await this.railVerifyOpts(c);
|
|
23559
|
+
const railOpts = unsignedPaid ? cashuOnlyRailOpts(await this.railVerifyOpts(c)) : await this.railVerifyOpts(c);
|
|
23560
|
+
if (!this.descriptor.auth || unsignedPaid) {
|
|
23561
|
+
railOpts.nut24 = {
|
|
23562
|
+
mints: this.advertisedMints(),
|
|
23563
|
+
previousRequiredMsats: this.previousPriceMsats(cap, dynamicUpfront, pricedAt)
|
|
23564
|
+
};
|
|
23565
|
+
}
|
|
23225
23566
|
hostingPaymentMayHaveLanded = Boolean(
|
|
23226
23567
|
creditEnvelope || railOpts.cashuToken || railOpts.x402Payment || railOpts.mppCredential
|
|
23227
23568
|
);
|
|
@@ -23259,7 +23600,7 @@ var DVMServer = class {
|
|
|
23259
23600
|
this.reportShortPayForfeit(err.detail.forfeit);
|
|
23260
23601
|
if (err.detail.body.error === "replay_rejected") {
|
|
23261
23602
|
const recovered = await this.recoverReplayedJob(c, {
|
|
23262
|
-
requestId: err.detail.replayPaymentId
|
|
23603
|
+
requestId: err.detail.replayPaymentId,
|
|
23263
23604
|
fingerprint,
|
|
23264
23605
|
callerPubkey: authPubkey
|
|
23265
23606
|
});
|
|
@@ -23287,15 +23628,17 @@ var DVMServer = class {
|
|
|
23287
23628
|
}
|
|
23288
23629
|
}
|
|
23289
23630
|
if (!resumedAcceptedPayment) {
|
|
23290
|
-
|
|
23291
|
-
|
|
23292
|
-
|
|
23293
|
-
|
|
23631
|
+
if (!unsignedPaid) {
|
|
23632
|
+
attachCreditMenu(
|
|
23633
|
+
err.detail.body,
|
|
23634
|
+
await this.creditMenu(this.creditCurrency(), authPubkey)
|
|
23635
|
+
);
|
|
23636
|
+
}
|
|
23294
23637
|
if (priceFiat) {
|
|
23295
23638
|
err.detail.body.price_micro = priceFiat.amountMicro;
|
|
23296
23639
|
err.detail.body.price_currency = priceFiat.currency;
|
|
23297
23640
|
}
|
|
23298
|
-
|
|
23641
|
+
const response2 = buildPaymentErrorResponse(
|
|
23299
23642
|
err.detail.body,
|
|
23300
23643
|
err.detail.mppChallenges,
|
|
23301
23644
|
err.detail.x402Requirements,
|
|
@@ -23305,6 +23648,11 @@ var DVMServer = class {
|
|
|
23305
23648
|
this.x402Support()?.exact,
|
|
23306
23649
|
err.detail.x402ErrorCode
|
|
23307
23650
|
);
|
|
23651
|
+
if (err.detail.cashuPaymentRequest) {
|
|
23652
|
+
response2.headers.set("X-Cashu", err.detail.cashuPaymentRequest);
|
|
23653
|
+
response2.headers.set("Cache-Control", "no-store");
|
|
23654
|
+
}
|
|
23655
|
+
return response2;
|
|
23308
23656
|
}
|
|
23309
23657
|
}
|
|
23310
23658
|
throw err;
|
|
@@ -23388,7 +23736,7 @@ var DVMServer = class {
|
|
|
23388
23736
|
}
|
|
23389
23737
|
const isAnonymous = requesterId === "anonymous" || requesterId === "dev-anonymous";
|
|
23390
23738
|
const jobToken = isAnonymous ? randomBytes5(32).toString("hex") : void 0;
|
|
23391
|
-
const requesterTokenHash = jobToken ?
|
|
23739
|
+
const requesterTokenHash = jobToken ? createHash12("sha256").update(jobToken).digest("hex") : void 0;
|
|
23392
23740
|
const job = jm.createJob(
|
|
23393
23741
|
normalisedBody,
|
|
23394
23742
|
requesterId,
|
|
@@ -23401,7 +23749,7 @@ var DVMServer = class {
|
|
|
23401
23749
|
requestFingerprint: fingerprint,
|
|
23402
23750
|
requesterPubkey: authPubkey,
|
|
23403
23751
|
requestId: publicRequestId,
|
|
23404
|
-
authRequestPath:
|
|
23752
|
+
authRequestPath: authEnvelope ? canonicalRequestPath(c.req.url) : void 0,
|
|
23405
23753
|
jobId
|
|
23406
23754
|
}
|
|
23407
23755
|
);
|
|
@@ -23497,15 +23845,16 @@ var DVMServer = class {
|
|
|
23497
23845
|
}
|
|
23498
23846
|
/**
|
|
23499
23847
|
* Recover a lost paid response only for matching request fingerprint,
|
|
23500
|
-
* authenticated pubkey and requester identity.
|
|
23501
|
-
*
|
|
23848
|
+
* authenticated pubkey and requester identity. `requestId` must be the
|
|
23849
|
+
* payment reference the rail bound the presented payment to; a spent bearer
|
|
23850
|
+
* token alone is not proof of request ownership.
|
|
23502
23851
|
*/
|
|
23503
23852
|
async recoverReplayedJob(c, opts) {
|
|
23504
23853
|
if (!opts.requestId) return void 0;
|
|
23505
23854
|
const prior = await this.jobManager.store.findJobByPaymentTxHash(opts.requestId);
|
|
23506
23855
|
if (!prior) return void 0;
|
|
23507
23856
|
const bodyMatches = prior.requestFingerprint !== void 0 && prior.requestFingerprint === opts.fingerprint;
|
|
23508
|
-
const pubkeyMatches = this.descriptor.auth === void 0 || prior.requesterPubkey
|
|
23857
|
+
const pubkeyMatches = this.descriptor.auth === void 0 || prior.requesterPubkey === opts.callerPubkey;
|
|
23509
23858
|
const requesterMatches = prior.requesterId === c.get("requesterId");
|
|
23510
23859
|
if (!bodyMatches || !pubkeyMatches || !requesterMatches) {
|
|
23511
23860
|
diagnosticConsole.warn(
|
|
@@ -23638,16 +23987,17 @@ var DVMServer = class {
|
|
|
23638
23987
|
* schema separately. Refuse invalid input/results, currency mismatch or missing
|
|
23639
23988
|
* FX when payment needs a price (400/500/503 as classified below).
|
|
23640
23989
|
*/
|
|
23641
|
-
async computeDynamicPrice(onQuote, parsedInput, body, callerPubkey, skipRateConversion = false) {
|
|
23990
|
+
async computeDynamicPrice(onQuote, parsedInput, body, callerPubkey, skipRateConversion = false, unsigned2 = false) {
|
|
23642
23991
|
let data = parsedInput;
|
|
23643
23992
|
if (isZodSchema(onQuote.schema)) {
|
|
23644
|
-
const
|
|
23993
|
+
const schema = unsigned2 ? unsignedInputSchema(onQuote.schema) ?? onQuote.schema : onQuote.schema;
|
|
23994
|
+
const parsed = schema.safeParse(parsedInput);
|
|
23645
23995
|
if (!parsed.success) {
|
|
23646
23996
|
return {
|
|
23647
23997
|
error: {
|
|
23648
23998
|
body: {
|
|
23649
23999
|
error: "quote_unavailable",
|
|
23650
|
-
message: "DVM uses dynamic pricing but the job input does not satisfy onQuote.schema. Submit the same payload to /v1/quote to confirm pricing."
|
|
24000
|
+
message: unsigned2 ? "DVM uses dynamic pricing but the unsigned job input does not satisfy onQuote.schema. Sign the request, or send input that matches the published schema." : "DVM uses dynamic pricing but the job input does not satisfy onQuote.schema. Submit the same payload to /v1/quote to confirm pricing."
|
|
23651
24001
|
},
|
|
23652
24002
|
status: 400
|
|
23653
24003
|
}
|
|
@@ -23677,7 +24027,8 @@ var DVMServer = class {
|
|
|
23677
24027
|
const priceMsats = fiatToSatsCeil(validated.upfront.amount, fxRateFor(fx, upfrontCurrency)) * 1e3;
|
|
23678
24028
|
return {
|
|
23679
24029
|
priceMsats,
|
|
23680
|
-
upfront: { amount: validated.upfront.amount, currency: upfrontCurrency }
|
|
24030
|
+
upfront: { amount: validated.upfront.amount, currency: upfrontCurrency },
|
|
24031
|
+
pricedAt: fx
|
|
23681
24032
|
};
|
|
23682
24033
|
} catch (err) {
|
|
23683
24034
|
if (err instanceof InvalidCurrencyError || err instanceof UnsupportedCurrencyError) {
|
|
@@ -23959,6 +24310,8 @@ var DVMServer = class {
|
|
|
23959
24310
|
this.opts.devMode === true
|
|
23960
24311
|
);
|
|
23961
24312
|
if (contentErr2) return c.json({ error: "invalid_message", message: contentErr2 }, 400);
|
|
24313
|
+
const railRefusal2 = this.unsignedJobRailRefusal(activeJob, body2);
|
|
24314
|
+
if (railRefusal2) return railRefusal2;
|
|
23962
24315
|
const inboundSeq2 = await jm.recordInboundPending(activeJob, body2.type, body2.content);
|
|
23963
24316
|
const lockPubkeys2 = await this.resolveLockPubkeysForReceive();
|
|
23964
24317
|
let unappliedCredit2;
|
|
@@ -24019,6 +24372,8 @@ var DVMServer = class {
|
|
|
24019
24372
|
}
|
|
24020
24373
|
const contentErr = validateMessageContent(body.type, body.content, this.opts.devMode === true);
|
|
24021
24374
|
if (contentErr) return c.json({ error: "invalid_message", message: contentErr }, 400);
|
|
24375
|
+
const railRefusal = this.unsignedJobRailRefusal(record, body);
|
|
24376
|
+
if (railRefusal) return railRefusal;
|
|
24022
24377
|
if (isStreamableJobStore(jm.store)) {
|
|
24023
24378
|
record.messages = await jm.store.getMessages(id, 0);
|
|
24024
24379
|
record.seq = Math.max(record.seq, ...record.messages.map((message) => message.seq));
|
|
@@ -24218,7 +24573,7 @@ function isJobOwner(c, job) {
|
|
|
24218
24573
|
if (job.requesterTokenHash) {
|
|
24219
24574
|
const token = c.req.header("X-Job-Token");
|
|
24220
24575
|
if (!token) return false;
|
|
24221
|
-
const provided =
|
|
24576
|
+
const provided = createHash12("sha256").update(token).digest("hex");
|
|
24222
24577
|
return timingSafeEqualHex(provided, job.requesterTokenHash);
|
|
24223
24578
|
}
|
|
24224
24579
|
return c.get("requesterId") === job.requesterId;
|
|
@@ -24341,6 +24696,24 @@ function removeUnavailableContent(fields) {
|
|
|
24341
24696
|
delete fields.payload_in;
|
|
24342
24697
|
}
|
|
24343
24698
|
}
|
|
24699
|
+
async function carriesNonCashuPayment(c) {
|
|
24700
|
+
if (c.req.header("PAYMENT-SIGNATURE") || c.req.header("X-PAYMENT")) return true;
|
|
24701
|
+
const authorization = c.req.header("Authorization");
|
|
24702
|
+
if (!authorization) return false;
|
|
24703
|
+
return Boolean((await import("mppx")).Credential.extractPaymentScheme(authorization));
|
|
24704
|
+
}
|
|
24705
|
+
function cashuOnlyRailOpts(opts) {
|
|
24706
|
+
return {
|
|
24707
|
+
...opts,
|
|
24708
|
+
x402Payment: void 0,
|
|
24709
|
+
x402Config: void 0,
|
|
24710
|
+
x402ExactSettlement: void 0,
|
|
24711
|
+
x402ExactVersions: void 0,
|
|
24712
|
+
x402BatchSettlement: void 0,
|
|
24713
|
+
mppCredential: void 0,
|
|
24714
|
+
mpp: void 0
|
|
24715
|
+
};
|
|
24716
|
+
}
|
|
24344
24717
|
function terminalResponseFields(job) {
|
|
24345
24718
|
const fields = {};
|
|
24346
24719
|
if (job.upfrontPayment !== void 0) fields.upfront_payment = job.upfrontPayment;
|