mtok-verify 0.1.1 → 0.1.2

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.
Files changed (2) hide show
  1. package/onchain.js +18 -10
  2. package/package.json +1 -1
package/onchain.js CHANGED
@@ -272,20 +272,28 @@ export function createOnchainVerifier({ rpcUrl, rpcUrls, usdcAddress, expectedCh
272
272
 
273
273
  // #580 draw-age guard: refuse a payment older than maxPaidAgeMs so a stale replay
274
274
  // can't buy a fresh serve after the relay's local redemption record lapsed or was
275
- // lost. Fail CLOSED on an unreadable block (the receipt just read fine on this same
276
- // RPC pool, so this is a money guard, not a liveness knob). Opt-in: unset => no
277
- // extra RPC, exact current behavior.
275
+ // lost. This is DEFENSE-IN-DEPTH on top of the relay's served.has() one-serve guard,
276
+ // so it only REJECTS when it actually reads an age past the bound. If the paid block
277
+ // can't be read (missing blockNumber, or a transient RPC failure even after retries),
278
+ // it SKIPS the freshness check rather than reject: the payment was already fully
279
+ // verified above, and rejecting would make the SDK auto-DISPUTE a good draw over an RPC
280
+ // blip, burning an honest buyer's USDC and busting an innocent seller's reputation.
281
+ // Opt-in: unset => no extra RPC, exact current behavior.
278
282
  const maxAge = Number(maxPaidAgeMs);
279
283
  if (Number.isFinite(maxAge) && maxAge > 0) {
280
284
  const bn = matchedLog?.blockNumber;
281
- if (bn == null) return { ok: false, reason: 'paid_block_unknown' };
282
- let paidAtMs;
283
- try {
285
+ let paidAtMs = null;
286
+ if (bn != null) {
284
287
  const blockTag = (typeof bn === 'string' && bn.startsWith('0x')) ? bn : '0x' + BigInt(bn).toString(16);
285
- const block = await rpc('eth_getBlockByNumber', [blockTag, false]);
286
- paidAtMs = Number(BigInt(block.timestamp)) * 1000;
287
- } catch { return { ok: false, reason: 'paid_block_unreadable' }; }
288
- if (nowMs() - paidAtMs > maxAge) return { ok: false, reason: 'payment_too_old' };
288
+ for (let i = 0; i <= receiptRetries; i++) {
289
+ try {
290
+ const block = await rpc('eth_getBlockByNumber', [blockTag, false]);
291
+ if (block?.timestamp != null) { paidAtMs = Number(BigInt(block.timestamp)) * 1000; break; }
292
+ } catch { /* transient: fall through to retry */ }
293
+ if (i < receiptRetries) await sleepImpl(receiptRetryMs);
294
+ }
295
+ }
296
+ if (paidAtMs != null && nowMs() - paidAtMs > maxAge) return { ok: false, reason: 'payment_too_old' };
289
297
  }
290
298
 
291
299
  // #(fable review): bind each leg's `from` to the pay-time buyer (event.buyer,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mtok-verify",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "The on-chain verify core for mtok.market sellers: decode + verify a MtokDripLedger DrawPaid receipt (and its USDC transfer legs) against a paid draw, with the canonical DrawPaid topic set. Dependency-free, runs anywhere fetch runs (node, a CF Worker). The SSOT the platform + reference relay + house seller all share.",
5
5
  "type": "module",
6
6
  "exports": {