crypto-aio 0.1.0 → 0.2.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.
Files changed (163) hide show
  1. package/CHANGELOG.md +151 -1
  2. package/README.md +32 -12
  3. package/dist/adapters/evm/builder.js +64 -4
  4. package/dist/adapters/evm/builder.js.map +1 -1
  5. package/dist/adapters/evm/decode.js +6 -1
  6. package/dist/adapters/evm/decode.js.map +1 -1
  7. package/dist/adapters/evm/ethers-client.js +4 -0
  8. package/dist/adapters/evm/ethers-client.js.map +1 -1
  9. package/dist/adapters/evm/fees.d.ts +24 -0
  10. package/dist/adapters/evm/fees.js +47 -4
  11. package/dist/adapters/evm/fees.js.map +1 -1
  12. package/dist/adapters/evm/index.d.ts +1 -1
  13. package/dist/adapters/evm/index.js +2 -1
  14. package/dist/adapters/evm/index.js.map +1 -1
  15. package/dist/adapters/evm/network.d.ts +5 -0
  16. package/dist/adapters/evm/network.js +21 -2
  17. package/dist/adapters/evm/network.js.map +1 -1
  18. package/dist/adapters/evm/plugin.d.ts +5 -6
  19. package/dist/adapters/evm/plugin.js +50 -2
  20. package/dist/adapters/evm/plugin.js.map +1 -1
  21. package/dist/adapters/evm/proofs.js +17 -5
  22. package/dist/adapters/evm/proofs.js.map +1 -1
  23. package/dist/adapters/evm/reader.d.ts +18 -0
  24. package/dist/adapters/evm/reader.js +34 -0
  25. package/dist/adapters/evm/reader.js.map +1 -1
  26. package/dist/adapters/evm/tokens.d.ts +3 -0
  27. package/dist/adapters/evm/tokens.js.map +1 -1
  28. package/dist/adapters/evm/types.d.ts +10 -0
  29. package/dist/adapters/evm/types.js.map +1 -1
  30. package/dist/adapters/evm/web3-client.d.ts +6 -3
  31. package/dist/adapters/evm/web3-client.js +19 -2
  32. package/dist/adapters/evm/web3-client.js.map +1 -1
  33. package/dist/adapters/solana/builder.js +29 -18
  34. package/dist/adapters/solana/builder.js.map +1 -1
  35. package/dist/adapters/solana/fees.d.ts +12 -3
  36. package/dist/adapters/solana/fees.js +13 -4
  37. package/dist/adapters/solana/fees.js.map +1 -1
  38. package/dist/adapters/solana/message.d.ts +15 -0
  39. package/dist/adapters/solana/message.js +120 -0
  40. package/dist/adapters/solana/message.js.map +1 -0
  41. package/dist/adapters/solana/plugin.js +4 -1
  42. package/dist/adapters/solana/plugin.js.map +1 -1
  43. package/dist/adapters/solana/types.d.ts +8 -5
  44. package/dist/adapters/solana/types.js.map +1 -1
  45. package/dist/adapters/solana/web3.js +10 -32
  46. package/dist/adapters/solana/web3.js.map +1 -1
  47. package/dist/adapters/ton/proofs.js +18 -76
  48. package/dist/adapters/ton/proofs.js.map +1 -1
  49. package/dist/adapters/ton/reader.d.ts +45 -4
  50. package/dist/adapters/ton/reader.js +200 -5
  51. package/dist/adapters/ton/reader.js.map +1 -1
  52. package/dist/adapters/ton/trace.d.ts +26 -0
  53. package/dist/adapters/ton/trace.js +35 -9
  54. package/dist/adapters/ton/trace.js.map +1 -1
  55. package/dist/adapters/tron/abi.d.ts +2 -0
  56. package/dist/adapters/tron/abi.js +2 -0
  57. package/dist/adapters/tron/abi.js.map +1 -1
  58. package/dist/adapters/tron/builder.js +20 -4
  59. package/dist/adapters/tron/builder.js.map +1 -1
  60. package/dist/adapters/tron/decode.d.ts +2 -0
  61. package/dist/adapters/tron/decode.js +1 -1
  62. package/dist/adapters/tron/decode.js.map +1 -1
  63. package/dist/adapters/tron/proofs.js +1 -0
  64. package/dist/adapters/tron/proofs.js.map +1 -1
  65. package/dist/adapters/tron/reader.d.ts +10 -0
  66. package/dist/adapters/tron/reader.js +17 -0
  67. package/dist/adapters/tron/reader.js.map +1 -1
  68. package/dist/adapters/tron/tokens.d.ts +2 -0
  69. package/dist/adapters/tron/tokens.js.map +1 -1
  70. package/dist/adapters/utxo/builder.js +1 -1
  71. package/dist/adapters/utxo/builder.js.map +1 -1
  72. package/dist/adapters/utxo/chains.js +2 -1
  73. package/dist/adapters/utxo/chains.js.map +1 -1
  74. package/dist/adapters/utxo/context.d.ts +6 -0
  75. package/dist/adapters/utxo/context.js +8 -1
  76. package/dist/adapters/utxo/context.js.map +1 -1
  77. package/dist/adapters/utxo/decode.d.ts +8 -0
  78. package/dist/adapters/utxo/decode.js +36 -0
  79. package/dist/adapters/utxo/decode.js.map +1 -1
  80. package/dist/adapters/utxo/driver.js +5 -0
  81. package/dist/adapters/utxo/driver.js.map +1 -1
  82. package/dist/adapters/utxo/esplora.d.ts +23 -2
  83. package/dist/adapters/utxo/esplora.js +49 -2
  84. package/dist/adapters/utxo/esplora.js.map +1 -1
  85. package/dist/adapters/utxo/proofs.d.ts +15 -6
  86. package/dist/adapters/utxo/proofs.js +185 -47
  87. package/dist/adapters/utxo/proofs.js.map +1 -1
  88. package/dist/adapters/utxo/rawblock.d.ts +46 -0
  89. package/dist/adapters/utxo/rawblock.js +159 -0
  90. package/dist/adapters/utxo/rawblock.js.map +1 -0
  91. package/dist/adapters/utxo/rawtx.d.ts +14 -0
  92. package/dist/adapters/utxo/rawtx.js +44 -14
  93. package/dist/adapters/utxo/rawtx.js.map +1 -1
  94. package/dist/core/blockchain/handle.d.ts +6 -27
  95. package/dist/core/blockchain/handle.js +112 -112
  96. package/dist/core/blockchain/handle.js.map +1 -1
  97. package/dist/core/config/types.d.ts +15 -0
  98. package/dist/core/config/types.js.map +1 -1
  99. package/dist/core/container/container.d.ts +1 -8
  100. package/dist/core/container/container.js +42 -8
  101. package/dist/core/container/container.js.map +1 -1
  102. package/dist/core/driver/types.d.ts +34 -1
  103. package/dist/core/driver/types.js.map +1 -1
  104. package/dist/core/errors/codes.d.ts +10 -0
  105. package/dist/core/errors/codes.js +4 -0
  106. package/dist/core/errors/codes.js.map +1 -1
  107. package/dist/core/lifecycle/engine-rules.d.ts +3 -4
  108. package/dist/core/lifecycle/engine-rules.js +16 -6
  109. package/dist/core/lifecycle/engine-rules.js.map +1 -1
  110. package/dist/core/lifecycle/engine.d.ts +2 -0
  111. package/dist/core/lifecycle/engine.js +19 -1
  112. package/dist/core/lifecycle/engine.js.map +1 -1
  113. package/dist/core/lifecycle/evaluate.js +9 -4
  114. package/dist/core/lifecycle/evaluate.js.map +1 -1
  115. package/dist/core/lifecycle/monitor.js +16 -6
  116. package/dist/core/lifecycle/monitor.js.map +1 -1
  117. package/dist/core/lifecycle/reconcile.d.ts +1 -1
  118. package/dist/core/lifecycle/reconcile.js +1 -1
  119. package/dist/core/lifecycle/reconcile.js.map +1 -1
  120. package/dist/core/lifecycle/workers.d.ts +9 -3
  121. package/dist/core/lifecycle/workers.js +32 -14
  122. package/dist/core/lifecycle/workers.js.map +1 -1
  123. package/dist/core/model/ids.d.ts +1 -0
  124. package/dist/core/model/ids.js +1 -0
  125. package/dist/core/model/ids.js.map +1 -1
  126. package/dist/core/model/ordering.d.ts +8 -0
  127. package/dist/core/model/ordering.js.map +1 -1
  128. package/dist/core/model/transaction.d.ts +7 -0
  129. package/dist/core/model/transaction.js.map +1 -1
  130. package/dist/core/observe/scanner.js +3 -0
  131. package/dist/core/observe/scanner.js.map +1 -1
  132. package/dist/core/signing/wallet.js +8 -0
  133. package/dist/core/signing/wallet.js.map +1 -1
  134. package/dist/core/store/format.d.ts +20 -0
  135. package/dist/core/store/format.js +86 -0
  136. package/dist/core/store/format.js.map +1 -0
  137. package/dist/core/store/memory.js +2 -2
  138. package/dist/core/store/memory.js.map +1 -1
  139. package/dist/core/store/types.d.ts +15 -3
  140. package/dist/core/store/types.js +2 -0
  141. package/dist/core/store/types.js.map +1 -1
  142. package/dist/core/transport/http-transport.js +21 -13
  143. package/dist/core/transport/http-transport.js.map +1 -1
  144. package/dist/core/transport/types.d.ts +16 -1
  145. package/dist/core/transport/types.js.map +1 -1
  146. package/dist/core/util/json.d.ts +7 -1
  147. package/dist/core/util/json.js +13 -4
  148. package/dist/core/util/json.js.map +1 -1
  149. package/dist/core/util/names.d.ts +6 -0
  150. package/dist/core/util/names.js +10 -0
  151. package/dist/core/util/names.js.map +1 -1
  152. package/dist/index.d.ts +2 -1
  153. package/dist/index.js +4 -2
  154. package/dist/index.js.map +1 -1
  155. package/dist/testing/contracts/operations.js +20 -1
  156. package/dist/testing/contracts/operations.js.map +1 -1
  157. package/dist/testing/env.d.ts +5 -0
  158. package/dist/testing/env.js +1 -1
  159. package/dist/testing/env.js.map +1 -1
  160. package/dist/testing/faulty-store.d.ts +2 -0
  161. package/dist/testing/faulty-store.js +5 -0
  162. package/dist/testing/faulty-store.js.map +1 -1
  163. package/package.json +17 -7
@@ -11,13 +11,17 @@ const address_1 = require("./address");
11
11
  const context_1 = require("./context");
12
12
  const decode_1 = require("./decode");
13
13
  const esplora_1 = require("./esplora");
14
+ const rawblock_1 = require("./rawblock");
14
15
  const reader_1 = require("./reader");
15
16
  /** Blocks `finalizedHead` trails one endpoint's head by, so peers can attest it. */
16
17
  exports.PEER_SKEW = 2n;
17
18
  /** Esplora's page of block transactions (electrs' `CHAIN_TXS_PER_PAGE`). */
18
19
  const PAGE = 25;
19
- /** Consensus: a block's transaction count × 4 is at most `MAX_BLOCK_WEIGHT` (4,000,000). */
20
- const MAX_BLOCK_TXS = 1_000_000;
20
+ /**
21
+ * How many previous transactions a block scan reads at once: a full block's delivered
22
+ * transactions spend outputs of thousands. The transport's rate limit still paces them.
23
+ */
24
+ const PREVIOUS_READS = 8;
21
25
  /** No height is below 0 or beyond what a server parses: such a height is never sent. */
22
26
  const MAX_HEIGHT = BigInt(Number.MAX_SAFE_INTEGER);
23
27
  const outOfRange = (height) => height < 0n || height > MAX_HEIGHT;
@@ -25,6 +29,46 @@ const undecided = (reason) => new error_1.ProviderError('PROVIDER_UNAVAILABLE',
25
29
  /** Two quorum reads that cannot both hold decide nothing (retryable). */
26
30
  const contradiction = (reason) => new error_1.ProviderError('PROVIDER_INCONSISTENT', reason, { retryable: true });
27
31
  const stale = () => contradiction('the including block is no longer the block at its height');
32
+ /** The block a scan reads is gone from the endpoint's chain, or was never on it. */
33
+ const changed = (block) => contradiction(`block ${block.height} changed while scanning`);
34
+ /**
35
+ * An output of a block's or a previous transaction's bytes as an Esplora page shows it.
36
+ * Its value is in range (consensus), so out of range the bytes are not chain data.
37
+ */
38
+ function outputView(output) {
39
+ if (output.value < 0n || output.value > esplora_1.MAX_MONEY)
40
+ throw (0, esplora_1.malformed)('output value');
41
+ return {
42
+ script: (0, bytes_1.toHex)(output.script),
43
+ type: (0, decode_1.scriptType)(output.script),
44
+ value: output.value,
45
+ };
46
+ }
47
+ /**
48
+ * Whether a page's transaction is `own`, the one the block's bytes hold at its place: the
49
+ * same txid, and the same inputs (outpoints, sequences, coinbase) and outputs.
50
+ */
51
+ function samePageTx(page, own) {
52
+ return (page.txid === own.txid &&
53
+ page.version === own.version &&
54
+ page.locktime === own.locktime &&
55
+ page.vin.length === own.inputs.length &&
56
+ page.vout.length === own.outputs.length &&
57
+ page.vin.every((input, n) => {
58
+ const mine = own.inputs[n];
59
+ return (mine !== undefined &&
60
+ input.txid === mine.txid &&
61
+ input.vout === mine.vout &&
62
+ input.sequence === mine.sequence &&
63
+ input.coinbase === (0, rawblock_1.isNullOutpoint)(mine));
64
+ }) &&
65
+ page.vout.every((output, n) => {
66
+ const mine = own.outputs[n];
67
+ return (mine !== undefined &&
68
+ output.value === mine.value &&
69
+ output.script === (0, bytes_1.toHex)(mine.script));
70
+ }));
71
+ }
28
72
  /**
29
73
  * A block hash read under the proof quorum is compared as `parseHash` reads it, so two
30
74
  * honest endpoints that differ only by whitespace agree, and the key is exactly the value
@@ -253,6 +297,115 @@ function blockSource(ctx) {
253
297
  }
254
298
  return scripts;
255
299
  };
300
+ /**
301
+ * The places of `raw`'s transactions that touch `wanted`, with each one's page: an output
302
+ * to it, read from the block's own bytes, or an input that, by the index's pages, spends
303
+ * from it. Every page is bound to the bytes: exactly the transactions they hold at its
304
+ * places, with the same inputs and outputs, confirmed in this block. So a page only claims
305
+ * which outputs the block's inputs spend, and `viewOf` checks the claims of every
306
+ * transaction delivered against the previous transactions' own bytes: all a lying index
307
+ * can do is hide a spend from a watched address that pays no watched address.
308
+ */
309
+ const touching = async (block, raw, wanted) => {
310
+ const picked = new Map();
311
+ for (let start = 0; start < raw.txs.length; start += PAGE) {
312
+ let page;
313
+ try {
314
+ page = await esplora.blockTxs(block.hash, start, context_1.MONITOR);
315
+ }
316
+ catch (error) {
317
+ // A backend that does not have the block (lagging, load-balanced) decides nothing.
318
+ if ((0, esplora_1.isNotFound)(error))
319
+ throw changed(block);
320
+ throw error;
321
+ }
322
+ // Each page holds exactly what the block leaves for it.
323
+ if (page.length !== Math.min(PAGE, raw.txs.length - start)) {
324
+ throw (0, esplora_1.malformed)('block txs page');
325
+ }
326
+ page.forEach((tx, offset) => {
327
+ const own = raw.txs[start + offset];
328
+ if (!samePageTx(tx, own)) {
329
+ throw contradiction("a block page differs from the block's own bytes");
330
+ }
331
+ const { status } = tx;
332
+ if (!status.confirmed ||
333
+ status.blockHash !== block.hash ||
334
+ status.blockHeight !== block.height) {
335
+ throw changed(block);
336
+ }
337
+ if (own.outputs.some((output) => wanted.has((0, bytes_1.toHex)(output.script))) ||
338
+ tx.vin.some((input) => input.prevout && wanted.has(input.prevout.script))) {
339
+ picked.set(start + offset, tx);
340
+ }
341
+ });
342
+ }
343
+ return picked;
344
+ };
345
+ /**
346
+ * The outputs the inputs of `txs` spend, by previous txid: a transaction of the block
347
+ * itself, or the previous transaction's own bytes, bound to its txid (`outputsOf`). One
348
+ * endpoint answers, so one that does not have a previous transaction decides nothing.
349
+ */
350
+ const spentOutputs = async (raw, txs) => {
351
+ const known = new Map(raw.txs.map((tx) => [tx.txid, tx.outputs]));
352
+ const missing = new Set();
353
+ for (const tx of txs) {
354
+ for (const input of tx.inputs) {
355
+ if (!(0, rawblock_1.isNullOutpoint)(input) && !known.has(input.txid))
356
+ missing.add(input.txid);
357
+ }
358
+ }
359
+ await (0, context_1.forEachBounded)([...missing], PREVIOUS_READS, async (txid) => {
360
+ const outputs = await esplora.outputsOf(txid, context_1.MONITOR);
361
+ if (!outputs)
362
+ throw undecided("an input's previous transaction is not available");
363
+ known.set(txid, outputs);
364
+ });
365
+ return known;
366
+ };
367
+ /**
368
+ * A transaction of the block as Esplora shows it, from its own bytes and the outputs its
369
+ * inputs spend, so it decodes as a page of it would. A page's claims about those outputs
370
+ * (`page`) must be what the previous transactions hold.
371
+ */
372
+ const viewOf = (tx, spent, status, page) => {
373
+ const vout = tx.outputs.map(outputView);
374
+ let coinbase = false;
375
+ let fee = -vout.reduce((sum, output) => sum + output.value, 0n);
376
+ const vin = tx.inputs.map((input, n) => {
377
+ const outpoint = { txid: input.txid, vout: input.vout, sequence: input.sequence };
378
+ if ((0, rawblock_1.isNullOutpoint)(input)) {
379
+ coinbase = true;
380
+ return { ...outpoint, coinbase: true };
381
+ }
382
+ const output = spent.get(input.txid)?.[input.vout];
383
+ if (!output)
384
+ throw contradiction('a block input spends an output that does not exist');
385
+ const prevout = outputView(output);
386
+ const claim = page?.vin[n]?.prevout;
387
+ if (claim && (claim.script !== prevout.script || claim.value !== prevout.value)) {
388
+ throw contradiction('the index names another output than the one an input spends');
389
+ }
390
+ fee += prevout.value;
391
+ return { ...outpoint, coinbase: false, prevout };
392
+ });
393
+ // Esplora's convention: a coinbase's fee is 0.
394
+ if (coinbase)
395
+ fee = 0n;
396
+ if (fee < 0n || fee > esplora_1.MAX_MONEY)
397
+ throw (0, esplora_1.malformed)('tx fee');
398
+ return {
399
+ txid: tx.txid,
400
+ version: tx.version,
401
+ locktime: tx.locktime,
402
+ weight: tx.strippedSize * 3 + tx.bytes.length,
403
+ fee,
404
+ vin,
405
+ vout,
406
+ status,
407
+ };
408
+ };
256
409
  return {
257
410
  async header(height) {
258
411
  if (outOfRange(height))
@@ -275,56 +428,41 @@ function blockSource(ctx) {
275
428
  };
276
429
  },
277
430
  async transactions(block, filter) {
278
- const inconsistent = () => contradiction(`block ${block.height} changed while scanning`);
279
- const meta = await esplora.block(block.hash, context_1.MONITOR);
280
- if (!meta || meta.height !== block.height)
281
- throw inconsistent();
282
- // Every block has a coinbase; none holds more than consensus allows. Bounded work.
283
- if (meta.txCount < 1 || meta.txCount > MAX_BLOCK_TXS) {
284
- throw (0, esplora_1.malformed)('block.tx_count');
285
- }
286
431
  // `filter.assets` is a hint: a list without the native asset wants tokens only, and a
287
- // Bitcoin block has none, so nothing is paged.
432
+ // Bitcoin block has none, so nothing is read.
288
433
  if (filter?.assets?.length && !filter.assets.includes('native'))
289
434
  return [];
290
- const txs = [];
291
- const seen = new Set();
292
- for (let start = 0; start < meta.txCount; start += PAGE) {
293
- let page;
294
- try {
295
- page = await esplora.blockTxs(block.hash, start, context_1.MONITOR);
296
- }
297
- catch (error) {
298
- // A backend that does not have the block (lagging, load-balanced) decides nothing.
299
- if ((0, esplora_1.isNotFound)(error))
300
- throw inconsistent();
301
- throw error;
302
- }
303
- // Each page holds exactly what the count leaves for it, and no txid twice.
304
- if (page.length !== Math.min(PAGE, meta.txCount - start)) {
305
- throw (0, esplora_1.malformed)('block txs page');
306
- }
307
- for (const tx of page) {
308
- if (seen.has(tx.txid))
309
- throw (0, esplora_1.malformed)('block txs page');
310
- seen.add(tx.txid);
311
- const { status } = tx;
312
- if (!status.confirmed ||
313
- status.blockHash !== block.hash ||
314
- status.blockHeight !== block.height) {
315
- throw inconsistent();
316
- }
317
- txs.push(tx);
318
- }
435
+ // The block's own bytes, bound to its hash (`rawBlock`): every transaction delivered
436
+ // is the block's, byte for byte, whatever the index's pages say.
437
+ const raw = await esplora.rawBlock(block.hash, context_1.MONITOR);
438
+ // A backend that does not have the block (lagging, load-balanced, or a full-mode
439
+ // electrs, which serves its best chain's only) decides nothing.
440
+ if (!raw)
441
+ throw changed(block);
442
+ // The header read at this height must be the bytes' own, so the block delivered (its
443
+ // parent link and time) is the one its hash names.
444
+ if (raw.parentHash !== block.parentHash || raw.time !== block.timestamp) {
445
+ throw contradiction(`the header of block ${block.height} is not its own`);
319
446
  }
320
- // Checked after the pages, so a reorg while paging is caught.
321
- if ((await esplora.blockHashAt(block.height, context_1.MONITOR)) !== block.hash)
322
- throw inconsistent();
323
447
  const wanted = scriptsOf(filter?.addresses);
324
- const touches = (tx) => wanted === undefined ||
325
- tx.vout.some((output) => wanted.has(output.script)) ||
326
- tx.vin.some((input) => input.prevout && wanted.has(input.prevout.script));
327
- return txs.filter(touches).map((tx) => (0, decode_1.decodeTransaction)(tx, config.address));
448
+ const picked = wanted && (await touching(block, raw, wanted));
449
+ const txs = picked ? raw.txs.filter((_, i) => picked.has(i)) : raw.txs;
450
+ const spent = await spentOutputs(raw, txs);
451
+ // Checked after the reads, so a reorg while reading is caught.
452
+ if ((await esplora.blockHashAt(block.height, context_1.MONITOR)) !== block.hash) {
453
+ throw changed(block);
454
+ }
455
+ const status = {
456
+ confirmed: true,
457
+ blockHeight: block.height,
458
+ blockHash: block.hash,
459
+ blockTime: raw.time,
460
+ };
461
+ return raw.txs.flatMap((tx, i) => picked && !picked.has(i)
462
+ ? []
463
+ : [
464
+ (0, decode_1.decodeTransaction)(viewOf(tx, spent, status, picked?.get(i)), config.address),
465
+ ]);
328
466
  },
329
467
  };
330
468
  }
@@ -1 +1 @@
1
- {"version":3,"file":"proofs.js","sourceRoot":"","sources":["../../../src/adapters/utxo/proofs.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Proofs (finalized-state checks behind `proven` verdicts) and the block source.\n *\n * Finality is N confirmations. Every fact is attested at its own height with a monotone\n * predicate quorum key, and no endpoint proposes a height. \"Block h is final\" is \"every\n * proof endpoint holds a block at h + N − 1\": the quorum reads the head with the key\n * `head >= h + N − 1`, so endpoints further ahead agree, and one that trails decides\n * nothing. Only `finalizedHead` (no anchor of its own) takes one endpoint's head, trails\n * it by `PEER_SKEW`, then attests it. One endpoint's head never decides finality.\n *\n * `includedFinal` answers \"not included\" only when an input of our Attempt has a\n * quorum-attested final spender that is neither our txid nor a malleated copy of it.\n * Every other case decides nothing (a retryable `ProviderError`): our transaction in a\n * mempool or in a block not yet final, an unknown spender, a lagging or load-balanced\n * backend. A stale inclusion (its block is no longer the block at its height) is a\n * retryable `PROVIDER_INCONSISTENT`, and so is a transaction view that puts ours in a\n * block while another transaction's spend of its input is final: both cannot hold. So\n * `includedFinal` never answers \"not included\" without its own attestation of a different\n * final spend, and an endpoint-set change between the monitor's `slotConsumed` and\n * `includedFinal` calls cannot produce a false proven `replaced`.\n *\n * The block source reads chain data leniently (the Esplora client's parsers) and binds\n * every page to its block: each page has exactly the transactions the block's count\n * leaves for it, no txid twice, each confirmed in that block. A reorg while paging, or a\n * page the endpoint does not have, decides nothing (retryable). Address filters match\n * output and prevout scripts, so a watched address's spend is kept even when no output of\n * it has an address.\n */\nimport type {\n BlockSource,\n DriverBlock,\n FinalityLevel,\n ProofSource,\n} from '../../core/driver/types';\nimport { ProviderError } from '../../core/errors/error';\nimport type { OrderingData } from '../../core/model/ordering';\nimport type { AttemptRef } from '../../core/model/transaction';\nimport { toHex } from '../../core/util/bytes';\nimport { decodeAddress } from './address';\nimport {\n MONITOR,\n PROOF,\n forEachBounded,\n parseOutpoint,\n proofRead,\n withSignal,\n type UtxoContext,\n} from './context';\nimport { decodeTransaction } from './decode';\nimport {\n isHash,\n isNotFound,\n type EsploraClient,\n malformed,\n parseHash,\n parseHeight,\n parseOutspend,\n parseTx,\n} from './esplora';\nimport { isOwnCopy } from './reader';\nimport type { EsploraTx } from './types';\n\n/** Blocks `finalizedHead` trails one endpoint's head by, so peers can attest it. */\nexport const PEER_SKEW = 2n;\n\n/** Esplora's page of block transactions (electrs' `CHAIN_TXS_PER_PAGE`). */\nconst PAGE = 25;\n/** Consensus: a block's transaction count × 4 is at most `MAX_BLOCK_WEIGHT` (4,000,000). */\nconst MAX_BLOCK_TXS = 1_000_000;\n/** No height is below 0 or beyond what a server parses: such a height is never sent. */\nconst MAX_HEIGHT = BigInt(Number.MAX_SAFE_INTEGER);\nconst outOfRange = (height: bigint): boolean => height < 0n || height > MAX_HEIGHT;\n\nconst undecided = (reason: string): ProviderError =>\n new ProviderError('PROVIDER_UNAVAILABLE', reason);\n\n/** Two quorum reads that cannot both hold decide nothing (retryable). */\nconst contradiction = (reason: string): ProviderError =>\n new ProviderError('PROVIDER_INCONSISTENT', reason, { retryable: true });\n\nconst stale = (): ProviderError =>\n contradiction('the including block is no longer the block at its height');\n\n/**\n * A block hash read under the proof quorum is compared as `parseHash` reads it, so two\n * honest endpoints that differ only by whitespace agree, and the key is exactly the value\n * the verdict uses. Compared as raw text, one endpoint's trailing newline would split the\n * quorum forever.\n */\nconst HASH_PROOF = { ...PROOF, quorumKey: parseHash };\n\n/** A transaction view as the proof quorum compares it: its txid, and its block and height. */\nconst txViewKey = (answer: unknown): string => {\n const t = parseTx(answer);\n return t.status.confirmed\n ? `${t.txid}@${t.status.blockHash}:${t.status.blockHeight}`\n : `${t.txid}:unconfirmed`;\n};\n\n/**\n * The reads a proof attests under the proof quorum, each fact at its own height. The\n * proof source and the builder share them: the builder's proof-tagged reads are this\n * module's, never a copy.\n */\nexport function attestedReads(ctx: UtxoContext, signal?: AbortSignal) {\n const { esplora } = ctx;\n /** \"I hold a block at `height`\" (monotone in the head, so endpoints further ahead agree). */\n const holds = async (height: bigint): Promise<boolean> => {\n const head = await esplora.tipHeight({\n ...withSignal(PROOF, signal),\n quorumKey: (answer) => parseHeight(answer) >= height,\n });\n return head >= height;\n };\n /** The hash of the block at `height` on the proof endpoints' chain. */\n const hashAt = (height: bigint): Promise<string | null> =>\n esplora.blockHashAt(height, withSignal(HASH_PROOF, signal));\n /** The quorum's hash at `height` must be `hash`; otherwise the answer was stale. */\n const assertCanonical = async (height: bigint, hash: string): Promise<void> => {\n if ((await hashAt(height)) !== hash) throw stale();\n };\n /** A transaction and the block it is in, as the proof endpoints agree on it. */\n const txView = (txid: string): Promise<EsploraTx | null> =>\n esplora.tx(txid, { ...withSignal(PROOF, signal), quorumKey: txViewKey });\n return { holds, hashAt, assertCanonical, txView };\n}\n\n/** How many parents `assertConfirmed` reads at once, and keeps once final. */\nconst PARENT_READS = 4;\nconst FINAL_PARENTS = 10_000;\nconst finalParents = new WeakMap<EsploraClient, Map<string, true>>();\n\nconst notConfirmed = (): ProviderError =>\n new ProviderError(\n 'PROVIDER_UNAVAILABLE',\n \"an input's previous transaction is not in a block the proof endpoints hold yet\",\n );\n\n/**\n * Each of `txids` (the previous transactions of the inputs a build adds) is in a\n * block, attested under the proof quorum at its own height: the proof endpoints agree on the\n * transaction and its block, all hold a block `confirmations − 1` above it, and have that\n * block at that height. Its bytes authenticate themselves, but only this proves a\n * chain holds it: an indexer's invented transaction, or one a reorg took out, would stall an\n * Attempt for good, since nothing would ever spend its outpoint. Anything short of that is a\n * retryable `PROVIDER_UNAVAILABLE` (a stale view `PROVIDER_INCONSISTENT`), and every read\n * decides nothing on failure. A parent found final is not read again.\n */\nexport async function assertConfirmed(\n ctx: UtxoContext,\n txids: readonly string[],\n confirmations: number,\n signal?: AbortSignal,\n): Promise<void> {\n let final = finalParents.get(ctx.esplora);\n if (!final) {\n final = new Map();\n finalParents.set(ctx.esplora, final);\n }\n const known = final;\n const pending = [...new Set(txids)].filter((txid) => !known.has(txid));\n if (pending.length === 0) return;\n await proofRead(async () => {\n const reads = attestedReads(ctx, signal);\n const blocks = new Map<string, bigint>();\n let top = 0n;\n await forEachBounded(pending, PARENT_READS, async (txid) => {\n const tx = await reads.txView(txid);\n if (!tx?.status.confirmed) throw notConfirmed();\n const height = tx.status.blockHeight as bigint;\n blocks.set(tx.status.blockHash as string, height);\n if (height > top) top = height;\n });\n // Monotone: the proof endpoints hold `top + confirmations − 1` only if they hold every\n // lower height, so one read attests every parent's depth.\n if (!(await reads.holds(top + BigInt(Math.max(1, confirmations)) - 1n))) {\n throw notConfirmed();\n }\n for (const [hash, height] of blocks) await reads.assertCanonical(height, hash);\n if (await reads.holds(top + BigInt(ctx.config.confirmations) - 1n)) {\n for (const txid of pending) {\n known.set(txid, true);\n if (known.size > FINAL_PARENTS) known.delete(known.keys().next().value as string);\n }\n }\n });\n}\n\nexport function proofSource(ctx: UtxoContext): ProofSource {\n const { esplora, config } = ctx;\n const depth = BigInt(config.confirmations) - 1n;\n const { holds, hashAt, assertCanonical, txView } = attestedReads(ctx);\n\n /** The transaction that spent `input` in a final, canonical block (quorum-attested). */\n const finalSpender = async (input: {\n txid: string;\n vout: number;\n }): Promise<{ txid: string; height: bigint; hash: string } | undefined> => {\n const spend = await esplora.outspend(input.txid, input.vout, {\n ...PROOF,\n quorumKey: (answer) => {\n const s = parseOutspend(answer);\n return s.spent && s.status?.confirmed\n ? `${s.txid}@${s.status.blockHash}:${s.status.blockHeight}`\n : 'not-in-a-block';\n },\n });\n const status = spend.status;\n if (!spend.spent || spend.txid === undefined || !status?.confirmed) return undefined;\n const height = status.blockHeight as bigint;\n const hash = status.blockHash as string;\n if (!(await holds(height + depth))) return undefined;\n await assertCanonical(height, hash);\n return { txid: spend.txid, height, hash };\n };\n\n const inputsOf = (ordering: OrderingData): { txid: string; vout: number }[] =>\n ordering.kind === 'inputs' ? ordering.inputs.map(parseOutpoint) : [];\n\n const source: ProofSource = {\n async finalizedHead() {\n const anchor = (await esplora.tipHeight(MONITOR)) - PEER_SKEW;\n if (anchor < 0n || !(await holds(anchor))) {\n throw undecided('the proof endpoints have not reached the head');\n }\n const final = anchor - depth;\n const height = final < 0n ? 0n : final;\n const hash = await hashAt(height);\n if (hash === null)\n throw undecided('the final block is not visible to the proof endpoints');\n return { height, hash };\n },\n\n async includedFinal(ref: AttemptRef, ordering: OrderingData, from: string) {\n if (!isHash(ref.id)) throw undecided('malformed transaction id');\n const tx = await txView(ref.id);\n if (tx?.status.confirmed) {\n const height = tx.status.blockHeight as bigint;\n const hash = tx.status.blockHash as string;\n if (await holds(height + depth)) {\n await assertCanonical(height, hash);\n // A transaction in a block has executed; its txid commits to its outputs.\n return {\n included: true,\n success: true,\n blockHeight: height,\n blockHash: hash,\n txHash: tx.txid,\n };\n }\n }\n // Unknown, in a mempool, or not yet final: only a final spender of one of our inputs\n // decides. Our own txid there means the views are inconsistent (decide nothing); a\n // malleated copy of ours is our payment; any other transaction proves ours dead,\n // unless the quorum also puts ours in a block (the views contradict each other).\n for (const input of inputsOf(ordering)) {\n const spender = await finalSpender(input);\n if (spender === undefined) continue;\n if (spender.txid === ref.id) {\n throw undecided('the spend and the transaction views disagree');\n }\n if (await isOwnCopy(ctx, ref.id, from, spender.txid, PROOF)) {\n return {\n included: true,\n success: true,\n blockHeight: spender.height,\n blockHash: spender.hash,\n txHash: spender.txid,\n };\n }\n if (tx?.status.confirmed) {\n throw contradiction(\n 'the transaction is in a block while another spend of its input is final',\n );\n }\n return { included: false };\n }\n throw undecided(\n tx\n ? 'the transaction is not final yet'\n : 'the indexer does not know this transaction yet',\n );\n },\n\n async slotConsumed(ordering, _from, level: FinalityLevel) {\n for (const input of inputsOf(ordering)) {\n if (level === 'latest') {\n if ((await esplora.outspend(input.txid, input.vout, MONITOR)).spent)\n return true;\n } else if ((await finalSpender(input)) !== undefined) {\n return true;\n }\n }\n return false;\n },\n\n expired: async () => false,\n\n async blockHash(height, level) {\n if (outOfRange(height)) return null;\n if (level === 'finalized' && !(await holds(height + depth))) return null;\n return hashAt(height);\n },\n };\n\n // Every read of every proof decides nothing on \"not available here\".\n return {\n finalizedHead: () => proofRead(() => source.finalizedHead()),\n includedFinal: (ref, ordering, from) =>\n proofRead(() => source.includedFinal(ref, ordering, from)),\n slotConsumed: (ordering, from, level) =>\n proofRead(() => source.slotConsumed(ordering, from, level)),\n expired: (ordering) => source.expired(ordering),\n blockHash: (height, level) => proofRead(() => source.blockHash(height, level)),\n };\n}\n\nexport function blockSource(ctx: UtxoContext): BlockSource {\n const { esplora, config } = ctx;\n\n /** The output scripts of the watched addresses; `undefined` means no filter. */\n const scriptsOf = (addresses: readonly string[] | undefined) => {\n if (addresses === undefined || addresses.length === 0) return undefined;\n const scripts = new Set<string>();\n for (const address of addresses) {\n try {\n scripts.add(toHex(decodeAddress(address, config.address).script));\n } catch {\n // Not an address of this network: it matches nothing.\n }\n }\n return scripts;\n };\n\n return {\n async header(height): Promise<DriverBlock | null> {\n if (outOfRange(height)) return null;\n const hash = await esplora.blockHashAt(height, MONITOR);\n if (!hash) return null;\n const block = await esplora.block(hash, MONITOR);\n if (!block) return null;\n // The block at `height` must say it is at `height`.\n if (block.height !== height) {\n throw contradiction(`block ${height} changed while reading`);\n }\n return {\n height: block.height,\n hash: block.hash,\n parentHash: block.parentHash,\n timestamp: block.timestamp,\n };\n },\n\n async transactions(block, filter) {\n const inconsistent = () =>\n contradiction(`block ${block.height} changed while scanning`);\n const meta = await esplora.block(block.hash, MONITOR);\n if (!meta || meta.height !== block.height) throw inconsistent();\n // Every block has a coinbase; none holds more than consensus allows. Bounded work.\n if (meta.txCount < 1 || meta.txCount > MAX_BLOCK_TXS) {\n throw malformed('block.tx_count');\n }\n // `filter.assets` is a hint: a list without the native asset wants tokens only, and a\n // Bitcoin block has none, so nothing is paged.\n if (filter?.assets?.length && !filter.assets.includes('native')) return [];\n const txs: EsploraTx[] = [];\n const seen = new Set<string>();\n for (let start = 0; start < meta.txCount; start += PAGE) {\n let page: EsploraTx[];\n try {\n page = await esplora.blockTxs(block.hash, start, MONITOR);\n } catch (error) {\n // A backend that does not have the block (lagging, load-balanced) decides nothing.\n if (isNotFound(error)) throw inconsistent();\n throw error;\n }\n // Each page holds exactly what the count leaves for it, and no txid twice.\n if (page.length !== Math.min(PAGE, meta.txCount - start)) {\n throw malformed('block txs page');\n }\n for (const tx of page) {\n if (seen.has(tx.txid)) throw malformed('block txs page');\n seen.add(tx.txid);\n const { status } = tx;\n if (\n !status.confirmed ||\n status.blockHash !== block.hash ||\n status.blockHeight !== block.height\n ) {\n throw inconsistent();\n }\n txs.push(tx);\n }\n }\n // Checked after the pages, so a reorg while paging is caught.\n if ((await esplora.blockHashAt(block.height, MONITOR)) !== block.hash)\n throw inconsistent();\n const wanted = scriptsOf(filter?.addresses);\n const touches = (tx: EsploraTx) =>\n wanted === undefined ||\n tx.vout.some((output) => wanted.has(output.script)) ||\n tx.vin.some((input) => input.prevout && wanted.has(input.prevout.script));\n return txs.filter(touches).map((tx) => decodeTransaction(tx, config.address));\n },\n };\n}\n"]}
1
+ {"version":3,"file":"proofs.js","sourceRoot":"","sources":["../../../src/adapters/utxo/proofs.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Proofs (finalized-state checks behind `proven` verdicts) and the block source.\n *\n * Finality is N confirmations. Every fact is attested at its own height with a monotone\n * predicate quorum key, and no endpoint proposes a height. \"Block h is final\" is \"every\n * proof endpoint holds a block at h + N − 1\": the quorum reads the head with the key\n * `head >= h + N − 1`, so endpoints further ahead agree, and one that trails decides\n * nothing. Only `finalizedHead` (no anchor of its own) takes one endpoint's head, trails\n * it by `PEER_SKEW`, then attests it. One endpoint's head never decides finality.\n *\n * `includedFinal` answers \"not included\" only when an input of our Attempt has a\n * quorum-attested final spender that is neither our txid nor a malleated copy of it.\n * Every other case decides nothing (a retryable `ProviderError`): our transaction in a\n * mempool or in a block not yet final, an unknown spender, a lagging or load-balanced\n * backend. A stale inclusion (its block is no longer the block at its height) is a\n * retryable `PROVIDER_INCONSISTENT`, and so is a transaction view that puts ours in a\n * block while another transaction's spend of its input is final: both cannot hold. So\n * `includedFinal` never answers \"not included\" without its own attestation of a different\n * final spend, and an endpoint-set change between the monitor's `slotConsumed` and\n * `includedFinal` calls cannot produce a false proven `replaced`.\n *\n * The block source delivers only what authenticated bytes say. A block's transactions are\n * decoded from its own bytes (`rawBlock`), which must hash to the block the scan asked for\n * and commit to every transaction and witness, and the header read at its height must be\n * those bytes' own (parent and time). The outputs a delivered transaction's inputs spend,\n * which give its `from`, input values and fee, are read from the block itself or from each\n * previous transaction's bytes, bound to the txid the input names. So no endpoint can add,\n * drop or alter a transaction, an output or a sender of what is delivered. A filtered scan\n * picks transactions by their outputs (the bytes) and by the inputs the index's pages say\n * spend from a watched address; each page must be the bytes' own, confirmed in that block,\n * and its claims must hold for every transaction delivered, so a lying index can at most\n * hide a watched address's spend that pays no watched address. Which block is at a height\n * stays the endpoint's word, as in every family (the proof quorum confirms rollbacks). A\n * reorg while reading, or a block, page or previous transaction the endpoint does not have,\n * decides nothing (retryable). Address filters match output and prevout scripts, so a\n * watched address's spend is kept even when no output of it has an address.\n */\nimport type {\n BlockSource,\n DriverBlock,\n FinalityLevel,\n ProofSource,\n} from '../../core/driver/types';\nimport { ProviderError } from '../../core/errors/error';\nimport type { OrderingData } from '../../core/model/ordering';\nimport type { AttemptRef } from '../../core/model/transaction';\nimport { toHex } from '../../core/util/bytes';\nimport { decodeAddress } from './address';\nimport {\n MONITOR,\n PROOF,\n forEachBounded,\n parseOutpoint,\n proofRead,\n withSignal,\n type UtxoContext,\n} from './context';\nimport { decodeTransaction, scriptType } from './decode';\nimport {\n MAX_MONEY,\n isHash,\n isNotFound,\n type EsploraClient,\n malformed,\n parseHash,\n parseHeight,\n parseOutspend,\n parseTx,\n type TxOutput,\n} from './esplora';\nimport { isNullOutpoint, type RawBlock } from './rawblock';\nimport type { ParsedTx } from './rawtx';\nimport { isOwnCopy } from './reader';\nimport type { EsploraInput, EsploraOutput, EsploraStatus, EsploraTx } from './types';\n\n/** Blocks `finalizedHead` trails one endpoint's head by, so peers can attest it. */\nexport const PEER_SKEW = 2n;\n\n/** Esplora's page of block transactions (electrs' `CHAIN_TXS_PER_PAGE`). */\nconst PAGE = 25;\n/**\n * How many previous transactions a block scan reads at once: a full block's delivered\n * transactions spend outputs of thousands. The transport's rate limit still paces them.\n */\nconst PREVIOUS_READS = 8;\n/** No height is below 0 or beyond what a server parses: such a height is never sent. */\nconst MAX_HEIGHT = BigInt(Number.MAX_SAFE_INTEGER);\nconst outOfRange = (height: bigint): boolean => height < 0n || height > MAX_HEIGHT;\n\nconst undecided = (reason: string): ProviderError =>\n new ProviderError('PROVIDER_UNAVAILABLE', reason);\n\n/** Two quorum reads that cannot both hold decide nothing (retryable). */\nconst contradiction = (reason: string): ProviderError =>\n new ProviderError('PROVIDER_INCONSISTENT', reason, { retryable: true });\n\nconst stale = (): ProviderError =>\n contradiction('the including block is no longer the block at its height');\n\n/** The block a scan reads is gone from the endpoint's chain, or was never on it. */\nconst changed = (block: DriverBlock): ProviderError =>\n contradiction(`block ${block.height} changed while scanning`);\n\n/**\n * An output of a block's or a previous transaction's bytes as an Esplora page shows it.\n * Its value is in range (consensus), so out of range the bytes are not chain data.\n */\nfunction outputView(output: TxOutput): EsploraOutput {\n if (output.value < 0n || output.value > MAX_MONEY) throw malformed('output value');\n return {\n script: toHex(output.script),\n type: scriptType(output.script),\n value: output.value,\n };\n}\n\n/**\n * Whether a page's transaction is `own`, the one the block's bytes hold at its place: the\n * same txid, and the same inputs (outpoints, sequences, coinbase) and outputs.\n */\nfunction samePageTx(page: EsploraTx, own: ParsedTx): boolean {\n return (\n page.txid === own.txid &&\n page.version === own.version &&\n page.locktime === own.locktime &&\n page.vin.length === own.inputs.length &&\n page.vout.length === own.outputs.length &&\n page.vin.every((input, n) => {\n const mine = own.inputs[n];\n return (\n mine !== undefined &&\n input.txid === mine.txid &&\n input.vout === mine.vout &&\n input.sequence === mine.sequence &&\n input.coinbase === isNullOutpoint(mine)\n );\n }) &&\n page.vout.every((output, n) => {\n const mine = own.outputs[n];\n return (\n mine !== undefined &&\n output.value === mine.value &&\n output.script === toHex(mine.script)\n );\n })\n );\n}\n\n/**\n * A block hash read under the proof quorum is compared as `parseHash` reads it, so two\n * honest endpoints that differ only by whitespace agree, and the key is exactly the value\n * the verdict uses. Compared as raw text, one endpoint's trailing newline would split the\n * quorum forever.\n */\nconst HASH_PROOF = { ...PROOF, quorumKey: parseHash };\n\n/** A transaction view as the proof quorum compares it: its txid, and its block and height. */\nconst txViewKey = (answer: unknown): string => {\n const t = parseTx(answer);\n return t.status.confirmed\n ? `${t.txid}@${t.status.blockHash}:${t.status.blockHeight}`\n : `${t.txid}:unconfirmed`;\n};\n\n/**\n * The reads a proof attests under the proof quorum, each fact at its own height. The\n * proof source and the builder share them: the builder's proof-tagged reads are this\n * module's, never a copy.\n */\nexport function attestedReads(ctx: UtxoContext, signal?: AbortSignal) {\n const { esplora } = ctx;\n /** \"I hold a block at `height`\" (monotone in the head, so endpoints further ahead agree). */\n const holds = async (height: bigint): Promise<boolean> => {\n const head = await esplora.tipHeight({\n ...withSignal(PROOF, signal),\n quorumKey: (answer) => parseHeight(answer) >= height,\n });\n return head >= height;\n };\n /** The hash of the block at `height` on the proof endpoints' chain. */\n const hashAt = (height: bigint): Promise<string | null> =>\n esplora.blockHashAt(height, withSignal(HASH_PROOF, signal));\n /** The quorum's hash at `height` must be `hash`; otherwise the answer was stale. */\n const assertCanonical = async (height: bigint, hash: string): Promise<void> => {\n if ((await hashAt(height)) !== hash) throw stale();\n };\n /** A transaction and the block it is in, as the proof endpoints agree on it. */\n const txView = (txid: string): Promise<EsploraTx | null> =>\n esplora.tx(txid, { ...withSignal(PROOF, signal), quorumKey: txViewKey });\n return { holds, hashAt, assertCanonical, txView };\n}\n\n/** How many parents `assertConfirmed` reads at once, and keeps once final. */\nconst PARENT_READS = 4;\nconst FINAL_PARENTS = 10_000;\nconst finalParents = new WeakMap<EsploraClient, Map<string, true>>();\n\nconst notConfirmed = (): ProviderError =>\n new ProviderError(\n 'PROVIDER_UNAVAILABLE',\n \"an input's previous transaction is not in a block the proof endpoints hold yet\",\n );\n\n/**\n * Each of `txids` (the previous transactions of the inputs a build adds) is in a\n * block, attested under the proof quorum at its own height: the proof endpoints agree on the\n * transaction and its block, all hold a block `confirmations − 1` above it, and have that\n * block at that height. Its bytes authenticate themselves, but only this proves a\n * chain holds it: an indexer's invented transaction, or one a reorg took out, would stall an\n * Attempt for good, since nothing would ever spend its outpoint. Anything short of that is a\n * retryable `PROVIDER_UNAVAILABLE` (a stale view `PROVIDER_INCONSISTENT`), and every read\n * decides nothing on failure. A parent found final is not read again.\n */\nexport async function assertConfirmed(\n ctx: UtxoContext,\n txids: readonly string[],\n confirmations: number,\n signal?: AbortSignal,\n): Promise<void> {\n let final = finalParents.get(ctx.esplora);\n if (!final) {\n final = new Map();\n finalParents.set(ctx.esplora, final);\n }\n const known = final;\n const pending = [...new Set(txids)].filter((txid) => !known.has(txid));\n if (pending.length === 0) return;\n await proofRead(async () => {\n const reads = attestedReads(ctx, signal);\n const blocks = new Map<string, bigint>();\n let top = 0n;\n await forEachBounded(pending, PARENT_READS, async (txid) => {\n const tx = await reads.txView(txid);\n if (!tx?.status.confirmed) throw notConfirmed();\n const height = tx.status.blockHeight as bigint;\n blocks.set(tx.status.blockHash as string, height);\n if (height > top) top = height;\n });\n // Monotone: the proof endpoints hold `top + confirmations − 1` only if they hold every\n // lower height, so one read attests every parent's depth.\n if (!(await reads.holds(top + BigInt(Math.max(1, confirmations)) - 1n))) {\n throw notConfirmed();\n }\n for (const [hash, height] of blocks) await reads.assertCanonical(height, hash);\n if (await reads.holds(top + BigInt(ctx.config.confirmations) - 1n)) {\n for (const txid of pending) {\n known.set(txid, true);\n if (known.size > FINAL_PARENTS) known.delete(known.keys().next().value as string);\n }\n }\n });\n}\n\nexport function proofSource(ctx: UtxoContext): ProofSource {\n const { esplora, config } = ctx;\n const depth = BigInt(config.confirmations) - 1n;\n const { holds, hashAt, assertCanonical, txView } = attestedReads(ctx);\n\n /** The transaction that spent `input` in a final, canonical block (quorum-attested). */\n const finalSpender = async (input: {\n txid: string;\n vout: number;\n }): Promise<{ txid: string; height: bigint; hash: string } | undefined> => {\n const spend = await esplora.outspend(input.txid, input.vout, {\n ...PROOF,\n quorumKey: (answer) => {\n const s = parseOutspend(answer);\n return s.spent && s.status?.confirmed\n ? `${s.txid}@${s.status.blockHash}:${s.status.blockHeight}`\n : 'not-in-a-block';\n },\n });\n const status = spend.status;\n if (!spend.spent || spend.txid === undefined || !status?.confirmed) return undefined;\n const height = status.blockHeight as bigint;\n const hash = status.blockHash as string;\n if (!(await holds(height + depth))) return undefined;\n await assertCanonical(height, hash);\n return { txid: spend.txid, height, hash };\n };\n\n const inputsOf = (ordering: OrderingData): { txid: string; vout: number }[] =>\n ordering.kind === 'inputs' ? ordering.inputs.map(parseOutpoint) : [];\n\n const source: ProofSource = {\n async finalizedHead() {\n const anchor = (await esplora.tipHeight(MONITOR)) - PEER_SKEW;\n if (anchor < 0n || !(await holds(anchor))) {\n throw undecided('the proof endpoints have not reached the head');\n }\n const final = anchor - depth;\n const height = final < 0n ? 0n : final;\n const hash = await hashAt(height);\n if (hash === null)\n throw undecided('the final block is not visible to the proof endpoints');\n return { height, hash };\n },\n\n async includedFinal(ref: AttemptRef, ordering: OrderingData, from: string) {\n if (!isHash(ref.id)) throw undecided('malformed transaction id');\n const tx = await txView(ref.id);\n if (tx?.status.confirmed) {\n const height = tx.status.blockHeight as bigint;\n const hash = tx.status.blockHash as string;\n if (await holds(height + depth)) {\n await assertCanonical(height, hash);\n // A transaction in a block has executed; its txid commits to its outputs.\n return {\n included: true,\n success: true,\n blockHeight: height,\n blockHash: hash,\n txHash: tx.txid,\n };\n }\n }\n // Unknown, in a mempool, or not yet final: only a final spender of one of our inputs\n // decides. Our own txid there means the views are inconsistent (decide nothing); a\n // malleated copy of ours is our payment; any other transaction proves ours dead,\n // unless the quorum also puts ours in a block (the views contradict each other).\n for (const input of inputsOf(ordering)) {\n const spender = await finalSpender(input);\n if (spender === undefined) continue;\n if (spender.txid === ref.id) {\n throw undecided('the spend and the transaction views disagree');\n }\n if (await isOwnCopy(ctx, ref.id, from, spender.txid, PROOF)) {\n return {\n included: true,\n success: true,\n blockHeight: spender.height,\n blockHash: spender.hash,\n txHash: spender.txid,\n };\n }\n if (tx?.status.confirmed) {\n throw contradiction(\n 'the transaction is in a block while another spend of its input is final',\n );\n }\n return { included: false };\n }\n throw undecided(\n tx\n ? 'the transaction is not final yet'\n : 'the indexer does not know this transaction yet',\n );\n },\n\n async slotConsumed(ordering, _from, level: FinalityLevel) {\n for (const input of inputsOf(ordering)) {\n if (level === 'latest') {\n if ((await esplora.outspend(input.txid, input.vout, MONITOR)).spent)\n return true;\n } else if ((await finalSpender(input)) !== undefined) {\n return true;\n }\n }\n return false;\n },\n\n expired: async () => false,\n\n async blockHash(height, level) {\n if (outOfRange(height)) return null;\n if (level === 'finalized' && !(await holds(height + depth))) return null;\n return hashAt(height);\n },\n };\n\n // Every read of every proof decides nothing on \"not available here\".\n return {\n finalizedHead: () => proofRead(() => source.finalizedHead()),\n includedFinal: (ref, ordering, from) =>\n proofRead(() => source.includedFinal(ref, ordering, from)),\n slotConsumed: (ordering, from, level) =>\n proofRead(() => source.slotConsumed(ordering, from, level)),\n expired: (ordering) => source.expired(ordering),\n blockHash: (height, level) => proofRead(() => source.blockHash(height, level)),\n };\n}\n\nexport function blockSource(ctx: UtxoContext): BlockSource {\n const { esplora, config } = ctx;\n\n /** The output scripts of the watched addresses; `undefined` means no filter. */\n const scriptsOf = (addresses: readonly string[] | undefined) => {\n if (addresses === undefined || addresses.length === 0) return undefined;\n const scripts = new Set<string>();\n for (const address of addresses) {\n try {\n scripts.add(toHex(decodeAddress(address, config.address).script));\n } catch {\n // Not an address of this network: it matches nothing.\n }\n }\n return scripts;\n };\n\n /**\n * The places of `raw`'s transactions that touch `wanted`, with each one's page: an output\n * to it, read from the block's own bytes, or an input that, by the index's pages, spends\n * from it. Every page is bound to the bytes: exactly the transactions they hold at its\n * places, with the same inputs and outputs, confirmed in this block. So a page only claims\n * which outputs the block's inputs spend, and `viewOf` checks the claims of every\n * transaction delivered against the previous transactions' own bytes: all a lying index\n * can do is hide a spend from a watched address that pays no watched address.\n */\n const touching = async (\n block: DriverBlock,\n raw: RawBlock,\n wanted: ReadonlySet<string>,\n ): Promise<Map<number, EsploraTx>> => {\n const picked = new Map<number, EsploraTx>();\n for (let start = 0; start < raw.txs.length; start += PAGE) {\n let page: EsploraTx[];\n try {\n page = await esplora.blockTxs(block.hash, start, MONITOR);\n } catch (error) {\n // A backend that does not have the block (lagging, load-balanced) decides nothing.\n if (isNotFound(error)) throw changed(block);\n throw error;\n }\n // Each page holds exactly what the block leaves for it.\n if (page.length !== Math.min(PAGE, raw.txs.length - start)) {\n throw malformed('block txs page');\n }\n page.forEach((tx, offset) => {\n const own = raw.txs[start + offset] as ParsedTx;\n if (!samePageTx(tx, own)) {\n throw contradiction(\"a block page differs from the block's own bytes\");\n }\n const { status } = tx;\n if (\n !status.confirmed ||\n status.blockHash !== block.hash ||\n status.blockHeight !== block.height\n ) {\n throw changed(block);\n }\n if (\n own.outputs.some((output) => wanted.has(toHex(output.script))) ||\n tx.vin.some((input) => input.prevout && wanted.has(input.prevout.script))\n ) {\n picked.set(start + offset, tx);\n }\n });\n }\n return picked;\n };\n\n /**\n * The outputs the inputs of `txs` spend, by previous txid: a transaction of the block\n * itself, or the previous transaction's own bytes, bound to its txid (`outputsOf`). One\n * endpoint answers, so one that does not have a previous transaction decides nothing.\n */\n const spentOutputs = async (\n raw: RawBlock,\n txs: readonly ParsedTx[],\n ): Promise<ReadonlyMap<string, readonly TxOutput[]>> => {\n const known = new Map<string, readonly TxOutput[]>(\n raw.txs.map((tx) => [tx.txid, tx.outputs]),\n );\n const missing = new Set<string>();\n for (const tx of txs) {\n for (const input of tx.inputs) {\n if (!isNullOutpoint(input) && !known.has(input.txid)) missing.add(input.txid);\n }\n }\n await forEachBounded([...missing], PREVIOUS_READS, async (txid) => {\n const outputs = await esplora.outputsOf(txid, MONITOR);\n if (!outputs) throw undecided(\"an input's previous transaction is not available\");\n known.set(txid, outputs);\n });\n return known;\n };\n\n /**\n * A transaction of the block as Esplora shows it, from its own bytes and the outputs its\n * inputs spend, so it decodes as a page of it would. A page's claims about those outputs\n * (`page`) must be what the previous transactions hold.\n */\n const viewOf = (\n tx: ParsedTx,\n spent: ReadonlyMap<string, readonly TxOutput[]>,\n status: EsploraStatus,\n page: EsploraTx | undefined,\n ): EsploraTx => {\n const vout = tx.outputs.map(outputView);\n let coinbase = false;\n let fee = -vout.reduce((sum, output) => sum + output.value, 0n);\n const vin = tx.inputs.map((input, n): EsploraInput => {\n const outpoint = { txid: input.txid, vout: input.vout, sequence: input.sequence };\n if (isNullOutpoint(input)) {\n coinbase = true;\n return { ...outpoint, coinbase: true };\n }\n const output = spent.get(input.txid)?.[input.vout];\n if (!output)\n throw contradiction('a block input spends an output that does not exist');\n const prevout = outputView(output);\n const claim = page?.vin[n]?.prevout;\n if (claim && (claim.script !== prevout.script || claim.value !== prevout.value)) {\n throw contradiction(\n 'the index names another output than the one an input spends',\n );\n }\n fee += prevout.value;\n return { ...outpoint, coinbase: false, prevout };\n });\n // Esplora's convention: a coinbase's fee is 0.\n if (coinbase) fee = 0n;\n if (fee < 0n || fee > MAX_MONEY) throw malformed('tx fee');\n return {\n txid: tx.txid,\n version: tx.version,\n locktime: tx.locktime,\n weight: tx.strippedSize * 3 + tx.bytes.length,\n fee,\n vin,\n vout,\n status,\n };\n };\n\n return {\n async header(height): Promise<DriverBlock | null> {\n if (outOfRange(height)) return null;\n const hash = await esplora.blockHashAt(height, MONITOR);\n if (!hash) return null;\n const block = await esplora.block(hash, MONITOR);\n if (!block) return null;\n // The block at `height` must say it is at `height`.\n if (block.height !== height) {\n throw contradiction(`block ${height} changed while reading`);\n }\n return {\n height: block.height,\n hash: block.hash,\n parentHash: block.parentHash,\n timestamp: block.timestamp,\n };\n },\n\n async transactions(block, filter) {\n // `filter.assets` is a hint: a list without the native asset wants tokens only, and a\n // Bitcoin block has none, so nothing is read.\n if (filter?.assets?.length && !filter.assets.includes('native')) return [];\n // The block's own bytes, bound to its hash (`rawBlock`): every transaction delivered\n // is the block's, byte for byte, whatever the index's pages say.\n const raw = await esplora.rawBlock(block.hash, MONITOR);\n // A backend that does not have the block (lagging, load-balanced, or a full-mode\n // electrs, which serves its best chain's only) decides nothing.\n if (!raw) throw changed(block);\n // The header read at this height must be the bytes' own, so the block delivered (its\n // parent link and time) is the one its hash names.\n if (raw.parentHash !== block.parentHash || raw.time !== block.timestamp) {\n throw contradiction(`the header of block ${block.height} is not its own`);\n }\n const wanted = scriptsOf(filter?.addresses);\n const picked = wanted && (await touching(block, raw, wanted));\n const txs = picked ? raw.txs.filter((_, i) => picked.has(i)) : raw.txs;\n const spent = await spentOutputs(raw, txs);\n // Checked after the reads, so a reorg while reading is caught.\n if ((await esplora.blockHashAt(block.height, MONITOR)) !== block.hash) {\n throw changed(block);\n }\n const status: EsploraStatus = {\n confirmed: true,\n blockHeight: block.height,\n blockHash: block.hash,\n blockTime: raw.time,\n };\n return raw.txs.flatMap((tx, i) =>\n picked && !picked.has(i)\n ? []\n : [\n decodeTransaction(\n viewOf(tx, spent, status, picked?.get(i)),\n config.address,\n ),\n ],\n );\n },\n };\n}\n"]}
@@ -0,0 +1,46 @@
1
+ import { type ParsedInput, type ParsedTx } from './rawtx';
2
+ /** Bitcoin Core's `MAX_BLOCK_SERIALIZED_SIZE`: no block's bytes are more. */
3
+ export declare const MAX_BLOCK_BYTES = 4000000;
4
+ export interface RawBlock {
5
+ /** The block hash in display order: the double SHA-256 of the header. */
6
+ readonly hash: string;
7
+ /** The previous block's hash in display order (zeros for a genesis block). */
8
+ readonly parentHash: string;
9
+ /** The header's time, in seconds. */
10
+ readonly time: number;
11
+ /** The header's merkle root of the txids, in internal byte order. */
12
+ readonly merkleRoot: Uint8Array;
13
+ /** Every transaction, the coinbase first, as the bytes hold them. */
14
+ readonly txs: readonly ParsedTx[];
15
+ }
16
+ /** An input that spends no output (`COutPoint::IsNull`): a coinbase's. */
17
+ export declare const isNullOutpoint: (input: ParsedInput) => boolean;
18
+ /** Block bytes, read in one pass; `undefined` when bitcoind would not decode them. */
19
+ export declare function readBlock(bytes: Uint8Array): RawBlock | undefined;
20
+ /**
21
+ * Bitcoin Core's `ComputeMerkleRoot`: the root of `leaves`, each level's odd last node
22
+ * paired with itself, and whether a level holds two equal nodes at a pair's places. That
23
+ * flags CVE-2012-2459: a list whose last transactions are repeated has the root of the list
24
+ * without them, so equal pairs are refused rather than read as a different block.
25
+ */
26
+ export declare function merkleRootOf(leaves: readonly Uint8Array[]): {
27
+ readonly root: Uint8Array;
28
+ readonly mutated: boolean;
29
+ };
30
+ /**
31
+ * Whether `block`'s transactions are the ones its header commits to, as bitcoind's
32
+ * `CheckMerkleRoot`, `bad-cb-missing` and `CheckWitnessMalleation` check them:
33
+ * - the merkle root of the txids is the header's, with no repeated pair (CVE-2012-2459) and
34
+ * no txid twice: no valid block holds one twice;
35
+ * - the first transaction is a coinbase (one input, spending nothing). A 64-byte
36
+ * transaction's txid is the hash of two merkle nodes, so a list with such "transactions"
37
+ * in place of the nodes' subtrees has the same root. Its leftmost one stands for the
38
+ * subtree that holds the coinbase, so it would have to be a coinbase made of hash output,
39
+ * with a null outpoint's 36 fixed bytes in it, which no one can find;
40
+ * - when any transaction carries a witness, the coinbase commits to every witness (BIP141):
41
+ * its last output that starts `OP_RETURN aa21a9ed` holds the double SHA-256 of the
42
+ * wtxids' merkle root (the coinbase's taken as zero) and the coinbase's one 32-byte
43
+ * witness item. A block with no witness at all is checked by its txids alone: an
44
+ * earlier block's coinbase may carry a commitment that segwit's rules did not cover yet.
45
+ */
46
+ export declare function commitsToTransactions(block: RawBlock): boolean;
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isNullOutpoint = exports.MAX_BLOCK_BYTES = void 0;
4
+ exports.readBlock = readBlock;
5
+ exports.merkleRootOf = merkleRootOf;
6
+ exports.commitsToTransactions = commitsToTransactions;
7
+ /**
8
+ * A linear, SDK-free reader of untrusted block bytes (Esplora's `/block/:hash/raw`), and the
9
+ * checks that bind a block's transactions to its header.
10
+ *
11
+ * A block hash is the double SHA-256 of the 80-byte header, and the header commits to the
12
+ * transactions through the merkle root of their txids, and (BIP141) to their witnesses
13
+ * through the coinbase's witness commitment. So bytes that hash to the block asked for and
14
+ * pass `commitsToTransactions` are that block's transactions, byte for byte, whoever served
15
+ * them: an indexer can neither add, drop nor alter one. bitcoinjs-lib's `Block.fromBuffer`
16
+ * copies the rest of the block for every transaction it reads (quadratic in the block's
17
+ * size), so each transaction is read here by `rawtx.ts`, in one pass over views of the bytes.
18
+ *
19
+ * The format is bitcoind's: the header, a canonical CompactSize count of at most `MAX_SIZE`,
20
+ * then each transaction with witness support, and nothing after the last one.
21
+ */
22
+ const bytes_1 = require("../../core/util/bytes");
23
+ const rawtx_1 = require("./rawtx");
24
+ /** Bitcoin Core's `MAX_BLOCK_SERIALIZED_SIZE`: no block's bytes are more. */
25
+ exports.MAX_BLOCK_BYTES = 4_000_000;
26
+ const HEADER = 80;
27
+ /** bitcoind's `MAX_SIZE`: the largest CompactSize a deserializer accepts. */
28
+ const MAX_SIZE = 0x02000000;
29
+ /** The fewest bytes `rawtx.ts` decodes as a transaction (version, two zero bytes, lock time). */
30
+ const MIN_TX = 10;
31
+ /** BIP141: an output script of at least 38 bytes that starts `OP_RETURN 0x24 aa21a9ed`. */
32
+ const COMMITMENT_HEADER = Uint8Array.of(0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed);
33
+ const COMMITMENT_SIZE = 38;
34
+ const ZERO_HASH = new Uint8Array(32);
35
+ /** An input that spends no output (`COutPoint::IsNull`): a coinbase's. */
36
+ const isNullOutpoint = (input) => input.vout === 0xffffffff && (0, bytes_1.equalBytes)(input.hash, ZERO_HASH);
37
+ exports.isNullOutpoint = isNullOutpoint;
38
+ /** Block bytes, read in one pass; `undefined` when bitcoind would not decode them. */
39
+ function readBlock(bytes) {
40
+ if (!(bytes instanceof Uint8Array))
41
+ return undefined;
42
+ if (bytes.length < HEADER + 1 || bytes.length > exports.MAX_BLOCK_BYTES)
43
+ return undefined;
44
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
45
+ let pos = HEADER;
46
+ // A canonical CompactSize of at most `MAX_SIZE` (bitcoind's `ReadCompactSize`).
47
+ const first = bytes[pos++];
48
+ let count = first;
49
+ if (first === 0xfd) {
50
+ if (pos + 2 > bytes.length)
51
+ return undefined;
52
+ count = view.getUint16(pos, true);
53
+ pos += 2;
54
+ if (count < 0xfd)
55
+ return undefined;
56
+ }
57
+ else if (first === 0xfe) {
58
+ if (pos + 4 > bytes.length)
59
+ return undefined;
60
+ count = view.getUint32(pos, true);
61
+ pos += 4;
62
+ if (count < 0x1_0000)
63
+ return undefined;
64
+ }
65
+ else if (first === 0xff) {
66
+ return undefined; // 8 bytes: at least 2^32, beyond MAX_SIZE
67
+ }
68
+ // Every block has a coinbase, and the count is bounded by the bytes left before
69
+ // anything is kept for it.
70
+ if (count < 1 || count > MAX_SIZE || count * MIN_TX > bytes.length - pos) {
71
+ return undefined;
72
+ }
73
+ const txs = [];
74
+ for (let i = 0; i < count; i++) {
75
+ const tx = (0, rawtx_1.readTxAt)(bytes, pos);
76
+ if (!tx)
77
+ return undefined;
78
+ txs.push(tx);
79
+ pos += tx.bytes.length;
80
+ }
81
+ if (pos !== bytes.length)
82
+ return undefined;
83
+ const header = bytes.subarray(0, HEADER);
84
+ return {
85
+ hash: (0, rawtx_1.displayHex)((0, rawtx_1.hash256)([header])),
86
+ parentHash: (0, rawtx_1.displayHex)(header.subarray(4, 36)),
87
+ time: view.getUint32(68, true),
88
+ merkleRoot: header.subarray(36, 68),
89
+ txs,
90
+ };
91
+ }
92
+ /**
93
+ * Bitcoin Core's `ComputeMerkleRoot`: the root of `leaves`, each level's odd last node
94
+ * paired with itself, and whether a level holds two equal nodes at a pair's places. That
95
+ * flags CVE-2012-2459: a list whose last transactions are repeated has the root of the list
96
+ * without them, so equal pairs are refused rather than read as a different block.
97
+ */
98
+ function merkleRootOf(leaves) {
99
+ let level = [...leaves];
100
+ let mutated = false;
101
+ while (level.length > 1) {
102
+ for (let i = 0; i + 1 < level.length; i += 2) {
103
+ if ((0, bytes_1.equalBytes)(level[i], level[i + 1]))
104
+ mutated = true;
105
+ }
106
+ const next = [];
107
+ for (let i = 0; i < level.length; i += 2) {
108
+ const left = level[i];
109
+ next.push((0, rawtx_1.hash256)([left, level[i + 1] ?? left]));
110
+ }
111
+ level = next;
112
+ }
113
+ return { root: level[0] ?? ZERO_HASH, mutated };
114
+ }
115
+ /**
116
+ * Whether `block`'s transactions are the ones its header commits to, as bitcoind's
117
+ * `CheckMerkleRoot`, `bad-cb-missing` and `CheckWitnessMalleation` check them:
118
+ * - the merkle root of the txids is the header's, with no repeated pair (CVE-2012-2459) and
119
+ * no txid twice: no valid block holds one twice;
120
+ * - the first transaction is a coinbase (one input, spending nothing). A 64-byte
121
+ * transaction's txid is the hash of two merkle nodes, so a list with such "transactions"
122
+ * in place of the nodes' subtrees has the same root. Its leftmost one stands for the
123
+ * subtree that holds the coinbase, so it would have to be a coinbase made of hash output,
124
+ * with a null outpoint's 36 fixed bytes in it, which no one can find;
125
+ * - when any transaction carries a witness, the coinbase commits to every witness (BIP141):
126
+ * its last output that starts `OP_RETURN aa21a9ed` holds the double SHA-256 of the
127
+ * wtxids' merkle root (the coinbase's taken as zero) and the coinbase's one 32-byte
128
+ * witness item. A block with no witness at all is checked by its txids alone: an
129
+ * earlier block's coinbase may carry a commitment that segwit's rules did not cover yet.
130
+ */
131
+ function commitsToTransactions(block) {
132
+ const { txs } = block;
133
+ const coinbase = txs[0];
134
+ const input = coinbase?.inputs[0];
135
+ if (!coinbase || coinbase.inputs.length !== 1 || !input || !(0, exports.isNullOutpoint)(input)) {
136
+ return false;
137
+ }
138
+ if (new Set(txs.map((tx) => tx.txid)).size !== txs.length)
139
+ return false;
140
+ const ids = merkleRootOf(txs.map((tx) => tx.hash));
141
+ if (ids.mutated || !(0, bytes_1.equalBytes)(ids.root, block.merkleRoot))
142
+ return false;
143
+ if (!txs.some((tx) => tx.hasWitness))
144
+ return true;
145
+ let commitment;
146
+ for (const output of coinbase.outputs) {
147
+ const script = output.script;
148
+ if (script.length >= COMMITMENT_SIZE &&
149
+ (0, bytes_1.equalBytes)(script.subarray(0, COMMITMENT_HEADER.length), COMMITMENT_HEADER)) {
150
+ commitment = script.subarray(COMMITMENT_HEADER.length, COMMITMENT_SIZE);
151
+ }
152
+ }
153
+ const reserved = input.witness;
154
+ if (!commitment || reserved.length !== 1 || reserved[0]?.length !== 32)
155
+ return false;
156
+ const witnesses = merkleRootOf(txs.map((tx, i) => i === 0 ? ZERO_HASH : tx.hasWitness ? (0, rawtx_1.hash256)([tx.bytes]) : tx.hash));
157
+ return (0, bytes_1.equalBytes)((0, rawtx_1.hash256)([witnesses.root, reserved[0]]), commitment);
158
+ }
159
+ //# sourceMappingURL=rawblock.js.map