@dvmkit/sdk 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,13 @@
1
1
  import {
2
2
  computeResultHash,
3
3
  isDrainReceipt,
4
+ isDrawFundingReceipt,
4
5
  isFundingReceipt,
5
6
  verifyDrainReceipt,
7
+ verifyDrawFundingReceipt,
6
8
  verifyFundingReceipt,
7
9
  verifyReceipt
8
- } from "./chunk-5H7P3ORL.js";
10
+ } from "./chunk-5UC7TOQC.js";
9
11
  import {
10
12
  DvmError
11
13
  } from "./chunk-BPYMEMQX.js";
@@ -1699,6 +1701,53 @@ function verifyFundingReceiptChain(args) {
1699
1701
  receipt: named
1700
1702
  };
1701
1703
  }
1704
+ function verifyDrawFundingReceiptChain(args) {
1705
+ const named = isDrawFundingReceipt(args.receipt) ? args.receipt : void 0;
1706
+ const check = (actual, expected) => expected === void 0 ? "unchecked" : actual === expected ? "ok" : "mismatch";
1707
+ const checks = {
1708
+ credit_id: check(named?.credit_id, args.expectedCreditId),
1709
+ job_id: check(named?.job_id, args.expectedJobId),
1710
+ draw_id: check(named?.draw_id, args.expectedDrawId),
1711
+ fund_id: check(named?.fund_id, args.expectedFundId),
1712
+ currency: check(named?.currency, args.expectedCurrency),
1713
+ funded_micro: check(named?.funded_micro, args.expectedFundedMicro),
1714
+ ledger_seq: check(named?.ledger_seq, args.expectedLedgerSeq),
1715
+ caller_pubkey: check(named?.caller_pubkey, args.expectedCallerPubkey),
1716
+ signature: named !== void 0 && verifyDrawFundingReceipt(named),
1717
+ attestation: "absent"
1718
+ };
1719
+ const mismatch = [
1720
+ ["credit_id", "credit_id_mismatch"],
1721
+ ["job_id", "job_id_mismatch"],
1722
+ ["draw_id", "draw_id_mismatch"],
1723
+ ["fund_id", "fund_id_mismatch"],
1724
+ ["currency", "funding_currency_mismatch"],
1725
+ ["funded_micro", "funding_amount_mismatch"],
1726
+ ["ledger_seq", "funding_ledger_seq_mismatch"],
1727
+ ["caller_pubkey", "caller_pubkey_mismatch"]
1728
+ ];
1729
+ for (const [field, reason] of mismatch) {
1730
+ if (checks[field] === "mismatch") return { status: "invalid", checks, reason, receipt: named };
1731
+ }
1732
+ if (!named || !checks.signature) {
1733
+ return { status: "invalid", checks, reason: "signature_invalid", receipt: named };
1734
+ }
1735
+ checks.attestation = checkReceiptAttestation(named, args.builder);
1736
+ if (checks.attestation === "invalid") {
1737
+ return { status: "invalid", checks, reason: "attestation_invalid", receipt: named };
1738
+ }
1739
+ if (checks.attestation === "identity_mismatch") {
1740
+ return { status: "invalid", checks, reason: "dvm_identity_mismatch", receipt: named };
1741
+ }
1742
+ if (checks.attestation === "key_mismatch") {
1743
+ return { status: "invalid", checks, reason: "receipt_key_mismatch", receipt: named };
1744
+ }
1745
+ return {
1746
+ status: checks.attestation === "ok" ? "verified" : "verified_unattested",
1747
+ checks,
1748
+ receipt: named
1749
+ };
1750
+ }
1702
1751
  function weakestReceiptVerdict(statuses) {
1703
1752
  if (statuses.length === 0) return "absent";
1704
1753
  if (statuses.includes("invalid")) return "invalid";
@@ -1862,6 +1911,7 @@ export {
1862
1911
  checkReceiptAttestation,
1863
1912
  verifyDrainReceiptChain,
1864
1913
  verifyFundingReceiptChain,
1914
+ verifyDrawFundingReceiptChain,
1865
1915
  weakestReceiptVerdict,
1866
1916
  isPublicRequestId,
1867
1917
  parseTraceparent,
@@ -63,6 +63,25 @@ function verifyFundingReceipt(receipt) {
63
63
  return false;
64
64
  }
65
65
  }
66
+ function signDrawFundingReceipt(unsigned, secretHex) {
67
+ const bytes = canonicaliseForSigning(unsigned);
68
+ const signature = bytesToHex(schnorr.sign(bytes, hexToBytes(secretHex)));
69
+ return { ...unsigned, signature };
70
+ }
71
+ function isDrawFundingReceipt(value) {
72
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
73
+ const r = value;
74
+ return r.v === 1 && r.kind === "credit_draw_funding" && typeof r.credit_id === "string" && typeof r.job_id === "string" && typeof r.draw_id === "string" && typeof r.fund_id === "string" && (r.payment_seq === void 0 || typeof r.payment_seq === "number") && typeof r.currency === "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";
75
+ }
76
+ function verifyDrawFundingReceipt(receipt) {
77
+ try {
78
+ const { signature, ...unsigned } = receipt;
79
+ const bytes = canonicaliseForSigning(unsigned);
80
+ return schnorr.verify(hexToBytes(signature), bytes, hexToBytes(receipt.receipt_pubkey));
81
+ } catch {
82
+ return false;
83
+ }
84
+ }
66
85
  function isDrainReceipt(value) {
67
86
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
68
87
  const r = value;
@@ -87,6 +106,9 @@ export {
87
106
  signFundingReceipt,
88
107
  isFundingReceipt,
89
108
  verifyFundingReceipt,
109
+ signDrawFundingReceipt,
110
+ isDrawFundingReceipt,
111
+ verifyDrawFundingReceipt,
90
112
  isDrainReceipt,
91
113
  verifyDrainReceipt
92
114
  };
@@ -7,7 +7,7 @@ import {
7
7
  import {
8
8
  isSignedJobReceipt,
9
9
  verifyReceipt
10
- } from "./chunk-5H7P3ORL.js";
10
+ } from "./chunk-5UC7TOQC.js";
11
11
  import {
12
12
  applyRetryToPool
13
13
  } from "./chunk-EP7OVWI5.js";
@@ -1345,7 +1345,11 @@ var CreditLedger = class {
1345
1345
  "caller_pubkey TEXT",
1346
1346
  "balance_after_micro BIGINT",
1347
1347
  "ledger_seq BIGINT",
1348
- "receipt JSONB"
1348
+ "receipt JSONB",
1349
+ // Mid-job draw funding bindings; null on opening and `/v1/credit` fundings.
1350
+ "job_id TEXT",
1351
+ "draw_id TEXT",
1352
+ "payment_seq BIGINT"
1349
1353
  ]) {
1350
1354
  await this.pool.query(`ALTER TABLE credit_fundings ADD COLUMN IF NOT EXISTS ${column}`);
1351
1355
  }
@@ -2467,11 +2471,21 @@ var CreditLedger = class {
2467
2471
  });
2468
2472
  const nowMs = args.nowMs ?? Date.now();
2469
2473
  const { rows } = await q.query(
2470
- `INSERT INTO credit_fundings (credit_id, fund_id, amount_micro, rail, created_at)
2471
- VALUES ($1, $2, $3, $4, $5)
2474
+ `INSERT INTO credit_fundings
2475
+ (credit_id, fund_id, amount_micro, rail, created_at, job_id, draw_id, payment_seq)
2476
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
2472
2477
  ON CONFLICT (credit_id, fund_id) DO NOTHING
2473
2478
  RETURNING *`,
2474
- [args.creditId, args.fundId, args.amountMicro, args.rail ?? null, nowMs]
2479
+ [
2480
+ args.creditId,
2481
+ args.fundId,
2482
+ args.amountMicro,
2483
+ args.rail ?? null,
2484
+ nowMs,
2485
+ args.jobId ?? null,
2486
+ args.drawId ?? null,
2487
+ args.paymentSeq ?? null
2488
+ ]
2475
2489
  );
2476
2490
  if (rows.length === 0) {
2477
2491
  throw new CreditLedgerError(
@@ -2494,6 +2508,19 @@ var CreditLedger = class {
2494
2508
  );
2495
2509
  return rows.length > 0 ? fundingRecordFromRow(rows[0]) : void 0;
2496
2510
  }
2511
+ /**
2512
+ * Every funding accepted against one job's draw, oldest first. The order is
2513
+ * stable across reads so a job read lists the same evidence each time.
2514
+ */
2515
+ async listDrawFundings(args) {
2516
+ const { rows } = await this.pool.query(
2517
+ `SELECT * FROM credit_fundings
2518
+ WHERE credit_id = $1 AND draw_id = $2
2519
+ ORDER BY created_at ASC, fund_id ASC`,
2520
+ [args.creditId, args.drawId]
2521
+ );
2522
+ return rows.map(fundingRecordFromRow);
2523
+ }
2497
2524
  /** Fix the funding-time receipt tuple inside the rail's open transaction. */
2498
2525
  async completeFunding(args) {
2499
2526
  const q = args.tx ?? this.pool;
@@ -4336,7 +4363,10 @@ function fundingRecordFromRow(row) {
4336
4363
  callerPubkey: row.caller_pubkey,
4337
4364
  balanceAfterMicro: row.balance_after_micro === null ? null : toSafeInt(row.balance_after_micro),
4338
4365
  ledgerSeq: row.ledger_seq === null ? null : toSafeInt(row.ledger_seq),
4339
- receipt: row.receipt
4366
+ receipt: row.receipt,
4367
+ jobId: row.job_id ?? null,
4368
+ drawId: row.draw_id ?? null,
4369
+ paymentSeq: row.payment_seq === null || row.payment_seq === void 0 ? null : toSafeInt(row.payment_seq)
4340
4370
  };
4341
4371
  }
4342
4372
  function invoiceRecordFromRow(row) {