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
@@ -37,6 +37,12 @@ export declare function forEachBounded<T>(items: readonly T[], limit: number, wo
37
37
  */
38
38
  export declare function proofRead<T>(read: () => Promise<T>): Promise<T>;
39
39
  export declare const ADDRESS_TYPES: readonly UtxoAddressType[];
40
+ /**
41
+ * The scheme a wallet of `type` signs every input with: Schnorr with the BIP341 key-path
42
+ * tweak for `p2tr`, ECDSA otherwise. Its signing requests carry it, and the driver's
43
+ * `signingSchemes` names it, so the core refuses a signer without it before any build.
44
+ */
45
+ export declare const signingScheme: (type: UtxoAddressType) => string;
40
46
  export interface ParsedWalletOptions {
41
47
  readonly addressType: UtxoAddressType;
42
48
  readonly changeAddress?: DecodedAddress;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ADDRESS_TYPES = exports.withSignal = exports.PROOF = exports.MONITOR = exports.READ = void 0;
3
+ exports.signingScheme = exports.ADDRESS_TYPES = exports.withSignal = exports.PROOF = exports.MONITOR = exports.READ = void 0;
4
4
  exports.forEachBounded = forEachBounded;
5
5
  exports.proofRead = proofRead;
6
6
  exports.parseWalletOptions = parseWalletOptions;
@@ -61,6 +61,13 @@ exports.ADDRESS_TYPES = [
61
61
  'p2pkh',
62
62
  'p2tr',
63
63
  ];
64
+ /**
65
+ * The scheme a wallet of `type` signs every input with: Schnorr with the BIP341 key-path
66
+ * tweak for `p2tr`, ECDSA otherwise. Its signing requests carry it, and the driver's
67
+ * `signingSchemes` names it, so the core refuses a signer without it before any build.
68
+ */
69
+ const signingScheme = (type) => type === 'p2tr' ? 'secp256k1-schnorr' : 'secp256k1-ecdsa';
70
+ exports.signingScheme = signingScheme;
64
71
  const WALLET_OPTION_KEYS = ['addressType', 'changeAddress', 'allowExternalChangeAddress'];
65
72
  /**
66
73
  * `WalletConfig.utxo`, validated (`CONFIG_INVALID`): the address type, the change address
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/adapters/utxo/context.ts"],"names":[],"mappings":";;;AAuCA,wCAmBC;AAWD,8BAaC;AAuBD,gDAgDC;AAED,oCAOC;AAGD,sCAMC;AAtKD,mDAKiC;AAEjC,iDAAoD;AAGpD,uCAA+D;AAalD,QAAA,IAAI,GAAiB,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AACxD,QAAA,OAAO,GAAiB,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAC9D,QAAA,KAAK,GAAiB,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;AAEjF,MAAM,UAAU,GAAG,CAAC,IAAkB,EAAE,MAAoB,EAAgB,EAAE,CACnF,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AADzB,QAAA,UAAU,cACe;AAEtC;;;GAGG;AACI,KAAK,UAAU,cAAc,CAClC,KAAmB,EACnB,KAAa,EACb,IAAgC;IAEhC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,MAAM,MAAM,GAAG,KAAK,IAAmB,EAAE;QACvC,OAAO,CAAC,MAAM,IAAI,IAAI,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAM,CAAC;YAChC,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,GAAG,IAAI,CAAC;gBACd,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;AACnF,CAAC;AAED;;;;;;;;GAQG;AACI,KAAK,UAAU,SAAS,CAAI,IAAsB;IACvD,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,EAAE,CAAC;IACtB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,IAAA,wBAAgB,EAAC,KAAK,CAAC,IAAI,KAAK,CAAC,QAAQ,KAAK,UAAU,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;YACjF,MAAM,IAAI,qBAAa,CACrB,sBAAsB,EACtB,4DAA4D,EAC5D,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAEY,QAAA,aAAa,GAA+B;IACvD,QAAQ;IACR,aAAa;IACb,OAAO;IACP,MAAM;CACP,CAAC;AASF,MAAM,kBAAkB,GAAG,CAAC,aAAa,EAAE,eAAe,EAAE,4BAA4B,CAAC,CAAC;AAE1F;;;;GAIG;AACH,SAAgB,kBAAkB,CAChC,MAAiC,EACjC,MAAyB;IAEzB,MAAM,GAAG,GAAG,MAAM,EAAE,IAAI,CAAC;IACzB,IAAI,GAAG,KAAK,SAAS;QACnB,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,0BAA0B,EAAE,KAAK,EAAE,CAAC;IACtE,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAClE,MAAM,IAAI,mBAAW,CAAC,gBAAgB,EAAE,+BAA+B,CAAC,CAAC;IAC3E,CAAC;IACD,MAAM,OAAO,GAAG,GAAwC,CAAC;IACzD,0EAA0E;IAC1E,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,kBAAkB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAC1E,MAAM,IAAI,mBAAW,CACnB,gBAAgB,EAChB,sBAAsB,IAAA,mBAAW,EAAC,QAAQ,EAAE,kBAAkB,CAAC,EAAE,CAClE,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,IAAI,QAAQ,CAAC;IAC7C,IAAI,CAAC,qBAAa,CAAC,QAAQ,CAAC,IAAuB,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,mBAAW,CACnB,gBAAgB,EAChB,0CAA0C,qBAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACrE,CAAC;IACJ,CAAC;IACD,IAAI,aAAyC,CAAC;IAC9C,IAAI,OAAO,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;QACxC,IAAI,CAAC;YACH,aAAa,GAAG,IAAA,uBAAa,EAAC,OAAO,CAAC,aAAuB,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QACjF,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,mBAAW,CACnB,gBAAgB,EAChB,kEAAkE,CACnE,CAAC;QACJ,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAG,OAAO,CAAC,0BAA0B,IAAI,KAAK,CAAC;IAC7D,IAAI,OAAO,QAAQ,KAAK,SAAS,EAAE,CAAC;QAClC,MAAM,IAAI,mBAAW,CACnB,gBAAgB,EAChB,0DAA0D,CAC3D,CAAC;IACJ,CAAC;IACD,OAAO;QACL,WAAW,EAAE,IAAuB;QACpC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3C,0BAA0B,EAAE,QAAQ;KACrC,CAAC;AACJ,CAAC;AAED,SAAgB,YAAY,CAAC,KAAe;IAC1C,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvB,MAAM,IAAI,kCAA0B,CAClC,wBAAwB,EACxB,0DAA0D,CAC3D,CAAC;IACJ,CAAC;AACH,CAAC;AAED,uFAAuF;AACvF,SAAgB,aAAa,CAAC,QAAgB;IAC5C,MAAM,KAAK,GAAG,sCAAsC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpE,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,EAAE,CAAC;QAC5C,MAAM,IAAI,qBAAa,CAAC,sBAAsB,EAAE,oCAAoC,CAAC,CAAC;IACxF,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAW,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9D,CAAC","sourcesContent":["/**\n * What every UTXO port is built from, the transport tags of the `ChainDriver` contract\n * table, and the wallet options every builder call parses.\n */\nimport type { WalletOptions } from '../../core/driver/types';\nimport {\n ConfigError,\n ProviderError,\n UnsupportedCapabilityError,\n isCryptoAioError,\n} from '../../core/errors/error';\nimport type { AssetRef } from '../../core/model/asset';\nimport { unknownName } from '../../core/util/names';\nimport type { Logger } from '../../core/events/logger';\nimport type { ChainInfo, NetworkInfo } from '../../core/model/chain';\nimport { decodeAddress, type DecodedAddress } from './address';\nimport type { EsploraClient } from './esplora';\nimport type { UtxoNetworkConfig } from './network';\nimport type { UtxoAddressType, UtxoCallTags } from './types';\n\nexport interface UtxoContext {\n readonly esplora: EsploraClient;\n readonly chain: ChainInfo;\n readonly network: NetworkInfo;\n readonly config: UtxoNetworkConfig;\n readonly log: Logger;\n}\n\nexport const READ: UtxoCallTags = { purpose: 'read', retry: 'safe' };\nexport const MONITOR: UtxoCallTags = { purpose: 'monitor', retry: 'safe' };\nexport const PROOF: UtxoCallTags = { purpose: 'proof', retry: 'safe', quorum: 'proof' };\n\nexport const withSignal = (tags: UtxoCallTags, signal?: AbortSignal): UtxoCallTags =>\n signal ? { ...tags, signal } : tags;\n\n/**\n * Runs `work` on each item, at most `limit` at a time. After a failure the\n * other runners stop at their next step, and the first failure is thrown.\n */\nexport async function forEachBounded<T>(\n items: readonly T[],\n limit: number,\n work: (item: T) => Promise<void>,\n): Promise<void> {\n let next = 0;\n let failed = false;\n const runner = async (): Promise<void> => {\n while (!failed && next < items.length) {\n const item = items[next++] as T;\n try {\n await work(item);\n } catch (error) {\n failed = true;\n throw error;\n }\n }\n };\n await Promise.all(Array.from({ length: Math.min(limit, items.length) }, runner));\n}\n\n/**\n * On a proof path only a definitive negative proof may answer \"no\"\n * (for Bitcoin: a quorum-attested final spend by another transaction). Every other RPC or\n * HTTP error decides nothing: a non-retryable provider error, such as a CDN or proxy 400,\n * 410 or 422 (the transport's `RPC_ERROR`, ambiguous or not) or a 401/403\n * (`PROVIDER_MISCONFIGURED`), becomes a retryable `PROVIDER_UNAVAILABLE` here, so the monitor\n * never rethrows it. Retryable provider errors already decide nothing and pass unchanged, as\n * do Esplora's own 404s, which the client maps to `null` first. The cost is liveness only.\n */\nexport async function proofRead<T>(read: () => Promise<T>): Promise<T> {\n try {\n return await read();\n } catch (error) {\n if (isCryptoAioError(error) && error.category === 'provider' && !error.retryable) {\n throw new ProviderError(\n 'PROVIDER_UNAVAILABLE',\n 'the proof read failed on this endpoint; nothing is decided',\n { cause: error },\n );\n }\n throw error;\n }\n}\n\nexport const ADDRESS_TYPES: readonly UtxoAddressType[] = [\n 'p2wpkh',\n 'p2sh-p2wpkh',\n 'p2pkh',\n 'p2tr',\n];\n\nexport interface ParsedWalletOptions {\n readonly addressType: UtxoAddressType;\n readonly changeAddress?: DecodedAddress;\n /** The explicit opt-out for a change address the wallet's key does not derive. */\n readonly allowExternalChangeAddress: boolean;\n}\n\nconst WALLET_OPTION_KEYS = ['addressType', 'changeAddress', 'allowExternalChangeAddress'];\n\n/**\n * `WalletConfig.utxo`, validated (`CONFIG_INVALID`): the address type, the change address\n * and its opt-out. Whether the wallet derives the change address is checked where the\n * wallet's keys are known (`changeAddressOf`, `spend.ts`).\n */\nexport function parseWalletOptions(\n wallet: WalletOptions | undefined,\n config: UtxoNetworkConfig,\n): ParsedWalletOptions {\n const raw = wallet?.utxo;\n if (raw === undefined)\n return { addressType: 'p2wpkh', allowExternalChangeAddress: false };\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new ConfigError('CONFIG_INVALID', 'wallet.utxo must be an object');\n }\n const options = raw as Readonly<Record<string, unknown>>;\n // The accepted names, never the caller's key (it may be a pasted secret).\n if (Object.keys(options).some((key) => !WALLET_OPTION_KEYS.includes(key))) {\n throw new ConfigError(\n 'CONFIG_INVALID',\n `wallet.utxo has an ${unknownName('option', WALLET_OPTION_KEYS)}`,\n );\n }\n const type = options.addressType ?? 'p2wpkh';\n if (!ADDRESS_TYPES.includes(type as UtxoAddressType)) {\n throw new ConfigError(\n 'CONFIG_INVALID',\n `wallet.utxo.addressType must be one of ${ADDRESS_TYPES.join(', ')}`,\n );\n }\n let changeAddress: DecodedAddress | undefined;\n if (options.changeAddress !== undefined) {\n try {\n changeAddress = decodeAddress(options.changeAddress as string, config.address);\n } catch {\n throw new ConfigError(\n 'CONFIG_INVALID',\n 'wallet.utxo.changeAddress is not a valid address on this network',\n );\n }\n }\n const external = options.allowExternalChangeAddress ?? false;\n if (typeof external !== 'boolean') {\n throw new ConfigError(\n 'CONFIG_INVALID',\n 'wallet.utxo.allowExternalChangeAddress must be a boolean',\n );\n }\n return {\n addressType: type as UtxoAddressType,\n ...(changeAddress ? { changeAddress } : {}),\n allowExternalChangeAddress: external,\n };\n}\n\nexport function assertNative(asset: AssetRef): void {\n if (asset !== 'native') {\n throw new UnsupportedCapabilityError(\n 'UNSUPPORTED_CAPABILITY',\n 'Bitcoin has no tokens; only the native asset can be sent',\n );\n }\n}\n\n/** Splits an `inputs` ordering entry (`txid:vout`); malformed data decides nothing. */\nexport function parseOutpoint(outpoint: string): { txid: string; vout: number } {\n const match = /^([0-9a-f]{64}):(0|[1-9][0-9]{0,9})$/.exec(outpoint);\n if (!match || Number(match[2]) > 0xffffffff) {\n throw new ProviderError('PROVIDER_UNAVAILABLE', 'malformed outpoint in the ordering');\n }\n return { txid: match[1] as string, vout: Number(match[2]) };\n}\n"]}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/adapters/utxo/context.ts"],"names":[],"mappings":";;;AAuCA,wCAmBC;AAWD,8BAaC;AA+BD,gDAgDC;AAED,oCAOC;AAGD,sCAMC;AA9KD,mDAKiC;AAEjC,iDAAoD;AAGpD,uCAA+D;AAalD,QAAA,IAAI,GAAiB,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AACxD,QAAA,OAAO,GAAiB,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAC9D,QAAA,KAAK,GAAiB,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;AAEjF,MAAM,UAAU,GAAG,CAAC,IAAkB,EAAE,MAAoB,EAAgB,EAAE,CACnF,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AADzB,QAAA,UAAU,cACe;AAEtC;;;GAGG;AACI,KAAK,UAAU,cAAc,CAClC,KAAmB,EACnB,KAAa,EACb,IAAgC;IAEhC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,MAAM,MAAM,GAAG,KAAK,IAAmB,EAAE;QACvC,OAAO,CAAC,MAAM,IAAI,IAAI,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAM,CAAC;YAChC,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,GAAG,IAAI,CAAC;gBACd,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;AACnF,CAAC;AAED;;;;;;;;GAQG;AACI,KAAK,UAAU,SAAS,CAAI,IAAsB;IACvD,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,EAAE,CAAC;IACtB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,IAAA,wBAAgB,EAAC,KAAK,CAAC,IAAI,KAAK,CAAC,QAAQ,KAAK,UAAU,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;YACjF,MAAM,IAAI,qBAAa,CACrB,sBAAsB,EACtB,4DAA4D,EAC5D,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAEY,QAAA,aAAa,GAA+B;IACvD,QAAQ;IACR,aAAa;IACb,OAAO;IACP,MAAM;CACP,CAAC;AAEF;;;;GAIG;AACI,MAAM,aAAa,GAAG,CAAC,IAAqB,EAAU,EAAE,CAC7D,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,iBAAiB,CAAC;AAD/C,QAAA,aAAa,iBACkC;AAS5D,MAAM,kBAAkB,GAAG,CAAC,aAAa,EAAE,eAAe,EAAE,4BAA4B,CAAC,CAAC;AAE1F;;;;GAIG;AACH,SAAgB,kBAAkB,CAChC,MAAiC,EACjC,MAAyB;IAEzB,MAAM,GAAG,GAAG,MAAM,EAAE,IAAI,CAAC;IACzB,IAAI,GAAG,KAAK,SAAS;QACnB,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,0BAA0B,EAAE,KAAK,EAAE,CAAC;IACtE,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAClE,MAAM,IAAI,mBAAW,CAAC,gBAAgB,EAAE,+BAA+B,CAAC,CAAC;IAC3E,CAAC;IACD,MAAM,OAAO,GAAG,GAAwC,CAAC;IACzD,0EAA0E;IAC1E,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,kBAAkB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAC1E,MAAM,IAAI,mBAAW,CACnB,gBAAgB,EAChB,sBAAsB,IAAA,mBAAW,EAAC,QAAQ,EAAE,kBAAkB,CAAC,EAAE,CAClE,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,IAAI,QAAQ,CAAC;IAC7C,IAAI,CAAC,qBAAa,CAAC,QAAQ,CAAC,IAAuB,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,mBAAW,CACnB,gBAAgB,EAChB,0CAA0C,qBAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACrE,CAAC;IACJ,CAAC;IACD,IAAI,aAAyC,CAAC;IAC9C,IAAI,OAAO,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;QACxC,IAAI,CAAC;YACH,aAAa,GAAG,IAAA,uBAAa,EAAC,OAAO,CAAC,aAAuB,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QACjF,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,mBAAW,CACnB,gBAAgB,EAChB,kEAAkE,CACnE,CAAC;QACJ,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAG,OAAO,CAAC,0BAA0B,IAAI,KAAK,CAAC;IAC7D,IAAI,OAAO,QAAQ,KAAK,SAAS,EAAE,CAAC;QAClC,MAAM,IAAI,mBAAW,CACnB,gBAAgB,EAChB,0DAA0D,CAC3D,CAAC;IACJ,CAAC;IACD,OAAO;QACL,WAAW,EAAE,IAAuB;QACpC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3C,0BAA0B,EAAE,QAAQ;KACrC,CAAC;AACJ,CAAC;AAED,SAAgB,YAAY,CAAC,KAAe;IAC1C,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvB,MAAM,IAAI,kCAA0B,CAClC,wBAAwB,EACxB,0DAA0D,CAC3D,CAAC;IACJ,CAAC;AACH,CAAC;AAED,uFAAuF;AACvF,SAAgB,aAAa,CAAC,QAAgB;IAC5C,MAAM,KAAK,GAAG,sCAAsC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpE,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,EAAE,CAAC;QAC5C,MAAM,IAAI,qBAAa,CAAC,sBAAsB,EAAE,oCAAoC,CAAC,CAAC;IACxF,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAW,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9D,CAAC","sourcesContent":["/**\n * What every UTXO port is built from, the transport tags of the `ChainDriver` contract\n * table, and the wallet options every builder call parses.\n */\nimport type { WalletOptions } from '../../core/driver/types';\nimport {\n ConfigError,\n ProviderError,\n UnsupportedCapabilityError,\n isCryptoAioError,\n} from '../../core/errors/error';\nimport type { AssetRef } from '../../core/model/asset';\nimport { unknownName } from '../../core/util/names';\nimport type { Logger } from '../../core/events/logger';\nimport type { ChainInfo, NetworkInfo } from '../../core/model/chain';\nimport { decodeAddress, type DecodedAddress } from './address';\nimport type { EsploraClient } from './esplora';\nimport type { UtxoNetworkConfig } from './network';\nimport type { UtxoAddressType, UtxoCallTags } from './types';\n\nexport interface UtxoContext {\n readonly esplora: EsploraClient;\n readonly chain: ChainInfo;\n readonly network: NetworkInfo;\n readonly config: UtxoNetworkConfig;\n readonly log: Logger;\n}\n\nexport const READ: UtxoCallTags = { purpose: 'read', retry: 'safe' };\nexport const MONITOR: UtxoCallTags = { purpose: 'monitor', retry: 'safe' };\nexport const PROOF: UtxoCallTags = { purpose: 'proof', retry: 'safe', quorum: 'proof' };\n\nexport const withSignal = (tags: UtxoCallTags, signal?: AbortSignal): UtxoCallTags =>\n signal ? { ...tags, signal } : tags;\n\n/**\n * Runs `work` on each item, at most `limit` at a time. After a failure the\n * other runners stop at their next step, and the first failure is thrown.\n */\nexport async function forEachBounded<T>(\n items: readonly T[],\n limit: number,\n work: (item: T) => Promise<void>,\n): Promise<void> {\n let next = 0;\n let failed = false;\n const runner = async (): Promise<void> => {\n while (!failed && next < items.length) {\n const item = items[next++] as T;\n try {\n await work(item);\n } catch (error) {\n failed = true;\n throw error;\n }\n }\n };\n await Promise.all(Array.from({ length: Math.min(limit, items.length) }, runner));\n}\n\n/**\n * On a proof path only a definitive negative proof may answer \"no\"\n * (for Bitcoin: a quorum-attested final spend by another transaction). Every other RPC or\n * HTTP error decides nothing: a non-retryable provider error, such as a CDN or proxy 400,\n * 410 or 422 (the transport's `RPC_ERROR`, ambiguous or not) or a 401/403\n * (`PROVIDER_MISCONFIGURED`), becomes a retryable `PROVIDER_UNAVAILABLE` here, so the monitor\n * never rethrows it. Retryable provider errors already decide nothing and pass unchanged, as\n * do Esplora's own 404s, which the client maps to `null` first. The cost is liveness only.\n */\nexport async function proofRead<T>(read: () => Promise<T>): Promise<T> {\n try {\n return await read();\n } catch (error) {\n if (isCryptoAioError(error) && error.category === 'provider' && !error.retryable) {\n throw new ProviderError(\n 'PROVIDER_UNAVAILABLE',\n 'the proof read failed on this endpoint; nothing is decided',\n { cause: error },\n );\n }\n throw error;\n }\n}\n\nexport const ADDRESS_TYPES: readonly UtxoAddressType[] = [\n 'p2wpkh',\n 'p2sh-p2wpkh',\n 'p2pkh',\n 'p2tr',\n];\n\n/**\n * The scheme a wallet of `type` signs every input with: Schnorr with the BIP341 key-path\n * tweak for `p2tr`, ECDSA otherwise. Its signing requests carry it, and the driver's\n * `signingSchemes` names it, so the core refuses a signer without it before any build.\n */\nexport const signingScheme = (type: UtxoAddressType): string =>\n type === 'p2tr' ? 'secp256k1-schnorr' : 'secp256k1-ecdsa';\n\nexport interface ParsedWalletOptions {\n readonly addressType: UtxoAddressType;\n readonly changeAddress?: DecodedAddress;\n /** The explicit opt-out for a change address the wallet's key does not derive. */\n readonly allowExternalChangeAddress: boolean;\n}\n\nconst WALLET_OPTION_KEYS = ['addressType', 'changeAddress', 'allowExternalChangeAddress'];\n\n/**\n * `WalletConfig.utxo`, validated (`CONFIG_INVALID`): the address type, the change address\n * and its opt-out. Whether the wallet derives the change address is checked where the\n * wallet's keys are known (`changeAddressOf`, `spend.ts`).\n */\nexport function parseWalletOptions(\n wallet: WalletOptions | undefined,\n config: UtxoNetworkConfig,\n): ParsedWalletOptions {\n const raw = wallet?.utxo;\n if (raw === undefined)\n return { addressType: 'p2wpkh', allowExternalChangeAddress: false };\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new ConfigError('CONFIG_INVALID', 'wallet.utxo must be an object');\n }\n const options = raw as Readonly<Record<string, unknown>>;\n // The accepted names, never the caller's key (it may be a pasted secret).\n if (Object.keys(options).some((key) => !WALLET_OPTION_KEYS.includes(key))) {\n throw new ConfigError(\n 'CONFIG_INVALID',\n `wallet.utxo has an ${unknownName('option', WALLET_OPTION_KEYS)}`,\n );\n }\n const type = options.addressType ?? 'p2wpkh';\n if (!ADDRESS_TYPES.includes(type as UtxoAddressType)) {\n throw new ConfigError(\n 'CONFIG_INVALID',\n `wallet.utxo.addressType must be one of ${ADDRESS_TYPES.join(', ')}`,\n );\n }\n let changeAddress: DecodedAddress | undefined;\n if (options.changeAddress !== undefined) {\n try {\n changeAddress = decodeAddress(options.changeAddress as string, config.address);\n } catch {\n throw new ConfigError(\n 'CONFIG_INVALID',\n 'wallet.utxo.changeAddress is not a valid address on this network',\n );\n }\n }\n const external = options.allowExternalChangeAddress ?? false;\n if (typeof external !== 'boolean') {\n throw new ConfigError(\n 'CONFIG_INVALID',\n 'wallet.utxo.allowExternalChangeAddress must be a boolean',\n );\n }\n return {\n addressType: type as UtxoAddressType,\n ...(changeAddress ? { changeAddress } : {}),\n allowExternalChangeAddress: external,\n };\n}\n\nexport function assertNative(asset: AssetRef): void {\n if (asset !== 'native') {\n throw new UnsupportedCapabilityError(\n 'UNSUPPORTED_CAPABILITY',\n 'Bitcoin has no tokens; only the native asset can be sent',\n );\n }\n}\n\n/** Splits an `inputs` ordering entry (`txid:vout`); malformed data decides nothing. */\nexport function parseOutpoint(outpoint: string): { txid: string; vout: number } {\n const match = /^([0-9a-f]{64}):(0|[1-9][0-9]{0,9})$/.exec(outpoint);\n if (!match || Number(match[2]) > 0xffffffff) {\n throw new ProviderError('PROVIDER_UNAVAILABLE', 'malformed outpoint in the ordering');\n }\n return { txid: match[1] as string, vout: Number(match[2]) };\n}\n"]}
@@ -10,5 +10,13 @@
10
10
  import type { DriverTransaction, DriverTxObservation } from '../../core/driver/types';
11
11
  import { type AddressParams } from './address';
12
12
  import type { EsploraStatus, EsploraTx } from './types';
13
+ /**
14
+ * Esplora's name for an output script's type, from the script itself: rust-bitcoin's
15
+ * predicates in Blockstream electrs' order (`TxOutValue::new`), so a transaction read from a
16
+ * block's own bytes decodes as Blockstream's page of it would. mempool/electrs names a few
17
+ * more (`anchor`, `multisig`, and `op_return` for any provably unspendable script); a scan
18
+ * names them all the one way, whatever server it reads.
19
+ */
20
+ export declare function scriptType(script: Uint8Array): string;
13
21
  export declare function observationOf(txid: string, status: EsploraStatus): DriverTxObservation;
14
22
  export declare function decodeTransaction(tx: EsploraTx, params: AddressParams): DriverTransaction;
@@ -1,9 +1,45 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.scriptType = scriptType;
3
4
  exports.observationOf = observationOf;
4
5
  exports.decodeTransaction = decodeTransaction;
5
6
  const bytes_1 = require("../../core/util/bytes");
6
7
  const address_1 = require("./address");
8
+ /**
9
+ * Esplora's name for an output script's type, from the script itself: rust-bitcoin's
10
+ * predicates in Blockstream electrs' order (`TxOutValue::new`), so a transaction read from a
11
+ * block's own bytes decodes as Blockstream's page of it would. mempool/electrs names a few
12
+ * more (`anchor`, `multisig`, and `op_return` for any provably unspendable script); a scan
13
+ * names them all the one way, whatever server it reads.
14
+ */
15
+ function scriptType(script) {
16
+ const n = script.length;
17
+ const at = (i) => script[i];
18
+ if (n === 0)
19
+ return 'empty';
20
+ if (at(0) === 0x6a)
21
+ return 'op_return';
22
+ if (((n === 35 && at(0) === 33) || (n === 67 && at(0) === 65)) && at(n - 1) === 0xac) {
23
+ return 'p2pk';
24
+ }
25
+ if (n === 25 &&
26
+ at(0) === 0x76 &&
27
+ at(1) === 0xa9 &&
28
+ at(2) === 0x14 &&
29
+ at(23) === 0x88 &&
30
+ at(24) === 0xac) {
31
+ return 'p2pkh';
32
+ }
33
+ if (n === 23 && at(0) === 0xa9 && at(1) === 0x14 && at(22) === 0x87)
34
+ return 'p2sh';
35
+ if (n === 22 && at(0) === 0x00 && at(1) === 0x14)
36
+ return 'v0_p2wpkh';
37
+ if (n === 34 && at(0) === 0x00 && at(1) === 0x20)
38
+ return 'v0_p2wsh';
39
+ if (n === 34 && at(0) === 0x51 && at(1) === 0x20)
40
+ return 'v1_p2tr';
41
+ return 'unknown';
42
+ }
7
43
  function observationOf(txid, status) {
8
44
  return status.confirmed
9
45
  ? {
@@ -1 +1 @@
1
- {"version":3,"file":"decode.js","sourceRoot":"","sources":["../../../src/adapters/utxo/decode.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Esplora transactions as `DriverTransaction`s (SDK-free). Every output whose script has an\n * address is a `vout:<n>` transfer from the input addresses; an output this\n * library cannot name that carries value (a future witness version, a bare script) makes the\n * decoding `partial`. Addresses are derived from the scripts, never taken from the server's\n * `scriptpubkey_address`, which a server could leave out (making a deposit `partial`) or\n * change (crediting another address). A Bitcoin transaction that is in a block has executed:\n * `success` is `true` (general decoding reports the chain as it is).\n */\nimport type {\n DriverTransaction,\n DriverTransfer,\n DriverTxObservation,\n} from '../../core/driver/types';\nimport { fromHex } from '../../core/util/bytes';\nimport { addressFromScript, type AddressParams } from './address';\nimport type { EsploraOutput, EsploraStatus, EsploraTx } from './types';\n\nexport function observationOf(txid: string, status: EsploraStatus): DriverTxObservation {\n return status.confirmed\n ? {\n seen: 'block',\n txHash: txid,\n blockHeight: status.blockHeight as bigint,\n blockHash: status.blockHash as string,\n success: true,\n }\n : { seen: 'mempool', txHash: txid };\n}\n\n/** The canonical address an output's script pays on this network, or `undefined`. */\nfunction addressOf(output: EsploraOutput, params: AddressParams): string | undefined {\n return addressFromScript(fromHex(output.script), params)?.canonical;\n}\n\nexport function decodeTransaction(\n tx: EsploraTx,\n params: AddressParams,\n): DriverTransaction {\n const coinbase = tx.vin.some((input) => input.coinbase);\n const senders = tx.vin.map((input) =>\n input.prevout ? addressOf(input.prevout, params) : undefined,\n );\n const recipients = tx.vout.map((output) => addressOf(output, params));\n const from = [\n ...new Set(senders.filter((address): address is string => address !== undefined)),\n ];\n const transfers: DriverTransfer[] = [];\n let partial = false;\n tx.vout.forEach((output, n) => {\n const to = recipients[n];\n if (to === undefined) {\n if (output.value > 0n) partial = true;\n return;\n }\n transfers.push({\n locator: `vout:${n}`,\n from,\n to,\n asset: 'native',\n amount: output.value,\n source: 'native',\n });\n });\n return {\n id: tx.txid,\n observation: observationOf(tx.txid, tx.status),\n ...(coinbase ? {} : { fee: [{ asset: 'native' as const, amount: tx.fee }] }),\n transfers,\n decoding: partial ? 'partial' : 'complete',\n ...(tx.status.blockTime !== undefined ? { timestamp: tx.status.blockTime } : {}),\n details: {\n version: tx.version,\n locktime: tx.locktime,\n weight: tx.weight,\n vin: tx.vin.map((input, n) => {\n const address = senders[n];\n return {\n txid: input.txid,\n vout: input.vout,\n coinbase: input.coinbase,\n sequence: input.sequence,\n ...(input.prevout\n ? {\n value: input.prevout.value,\n ...(address !== undefined ? { address } : {}),\n }\n : {}),\n };\n }),\n vout: tx.vout.map((output, n) => {\n const address = recipients[n];\n return {\n n,\n value: output.value,\n type: output.type,\n ...(address !== undefined ? { address } : {}),\n };\n }),\n },\n };\n}\n"]}
1
+ {"version":3,"file":"decode.js","sourceRoot":"","sources":["../../../src/adapters/utxo/decode.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Esplora transactions as `DriverTransaction`s (SDK-free). Every output whose script has an\n * address is a `vout:<n>` transfer from the input addresses; an output this\n * library cannot name that carries value (a future witness version, a bare script) makes the\n * decoding `partial`. Addresses are derived from the scripts, never taken from the server's\n * `scriptpubkey_address`, which a server could leave out (making a deposit `partial`) or\n * change (crediting another address). A Bitcoin transaction that is in a block has executed:\n * `success` is `true` (general decoding reports the chain as it is).\n */\nimport type {\n DriverTransaction,\n DriverTransfer,\n DriverTxObservation,\n} from '../../core/driver/types';\nimport { fromHex } from '../../core/util/bytes';\nimport { addressFromScript, type AddressParams } from './address';\nimport type { EsploraOutput, EsploraStatus, EsploraTx } from './types';\n\n/**\n * Esplora's name for an output script's type, from the script itself: rust-bitcoin's\n * predicates in Blockstream electrs' order (`TxOutValue::new`), so a transaction read from a\n * block's own bytes decodes as Blockstream's page of it would. mempool/electrs names a few\n * more (`anchor`, `multisig`, and `op_return` for any provably unspendable script); a scan\n * names them all the one way, whatever server it reads.\n */\nexport function scriptType(script: Uint8Array): string {\n const n = script.length;\n const at = (i: number): number | undefined => script[i];\n if (n === 0) return 'empty';\n if (at(0) === 0x6a) return 'op_return';\n if (((n === 35 && at(0) === 33) || (n === 67 && at(0) === 65)) && at(n - 1) === 0xac) {\n return 'p2pk';\n }\n if (\n n === 25 &&\n at(0) === 0x76 &&\n at(1) === 0xa9 &&\n at(2) === 0x14 &&\n at(23) === 0x88 &&\n at(24) === 0xac\n ) {\n return 'p2pkh';\n }\n if (n === 23 && at(0) === 0xa9 && at(1) === 0x14 && at(22) === 0x87) return 'p2sh';\n if (n === 22 && at(0) === 0x00 && at(1) === 0x14) return 'v0_p2wpkh';\n if (n === 34 && at(0) === 0x00 && at(1) === 0x20) return 'v0_p2wsh';\n if (n === 34 && at(0) === 0x51 && at(1) === 0x20) return 'v1_p2tr';\n return 'unknown';\n}\n\nexport function observationOf(txid: string, status: EsploraStatus): DriverTxObservation {\n return status.confirmed\n ? {\n seen: 'block',\n txHash: txid,\n blockHeight: status.blockHeight as bigint,\n blockHash: status.blockHash as string,\n success: true,\n }\n : { seen: 'mempool', txHash: txid };\n}\n\n/** The canonical address an output's script pays on this network, or `undefined`. */\nfunction addressOf(output: EsploraOutput, params: AddressParams): string | undefined {\n return addressFromScript(fromHex(output.script), params)?.canonical;\n}\n\nexport function decodeTransaction(\n tx: EsploraTx,\n params: AddressParams,\n): DriverTransaction {\n const coinbase = tx.vin.some((input) => input.coinbase);\n const senders = tx.vin.map((input) =>\n input.prevout ? addressOf(input.prevout, params) : undefined,\n );\n const recipients = tx.vout.map((output) => addressOf(output, params));\n const from = [\n ...new Set(senders.filter((address): address is string => address !== undefined)),\n ];\n const transfers: DriverTransfer[] = [];\n let partial = false;\n tx.vout.forEach((output, n) => {\n const to = recipients[n];\n if (to === undefined) {\n if (output.value > 0n) partial = true;\n return;\n }\n transfers.push({\n locator: `vout:${n}`,\n from,\n to,\n asset: 'native',\n amount: output.value,\n source: 'native',\n });\n });\n return {\n id: tx.txid,\n observation: observationOf(tx.txid, tx.status),\n ...(coinbase ? {} : { fee: [{ asset: 'native' as const, amount: tx.fee }] }),\n transfers,\n decoding: partial ? 'partial' : 'complete',\n ...(tx.status.blockTime !== undefined ? { timestamp: tx.status.blockTime } : {}),\n details: {\n version: tx.version,\n locktime: tx.locktime,\n weight: tx.weight,\n vin: tx.vin.map((input, n) => {\n const address = senders[n];\n return {\n txid: input.txid,\n vout: input.vout,\n coinbase: input.coinbase,\n sequence: input.sequence,\n ...(input.prevout\n ? {\n value: input.prevout.value,\n ...(address !== undefined ? { address } : {}),\n }\n : {}),\n };\n }),\n vout: tx.vout.map((output, n) => {\n const address = recipients[n];\n return {\n n,\n value: output.value,\n type: output.type,\n ...(address !== undefined ? { address } : {}),\n };\n }),\n },\n };\n}\n"]}
@@ -58,6 +58,11 @@ exports.utxoDriverFactory = {
58
58
  },
59
59
  },
60
60
  limits: () => ({ maxOutputs: spend_1.MAX_OUTPUTS }),
61
+ // One scheme per address type, so a p2tr wallet's ECDSA-only signer (a KMS key) is
62
+ // refused when the wallet resolves, not at signing.
63
+ signingSchemes: (wallet) => [
64
+ (0, context_1.signingScheme)((0, context_1.parseWalletOptions)(wallet, config).addressType),
65
+ ],
61
66
  createNativeClient: () => {
62
67
  // The doc's promise: taproot works on this module (and a swapped backend is undone).
63
68
  (0, sdk_1.useNobleEcc)();
@@ -1 +1 @@
1
- {"version":3,"file":"driver.js","sourceRoot":"","sources":["../../../src/adapters/utxo/driver.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The UTXO driver factory: validated network config, endpoint probes on both transports,\n * and the ports. Loaded only by the manifest's `load()`, with bitcoinjs-lib.\n */\nimport type { ChainDriver, DriverContext, DriverFactory } from '../../core/driver/types';\nimport { ConfigError } from '../../core/errors/error';\nimport type { EndpointCall, HealthProbes } from '../../core/transport/types';\nimport { utxoBroadcaster, utxoBuilder, utxoReplacement } from './builder';\nimport { networkOf } from './codec';\nimport { READ, type UtxoContext } from './context';\nimport { EsploraClient, parseHash, parseHeight } from './esplora';\nimport { utxoNetworkConfig } from './network';\nimport { blockSource, proofSource } from './proofs';\nimport { addressCodec, addressHistory, chainReader, listUnspent } from './reader';\nimport { bitcoin, useNobleEcc } from './sdk';\nimport { coinSelectionPreview } from './preview';\nimport { MAX_OUTPUTS } from './spend';\nimport type { UtxoExt } from './types';\n\nexport interface UtxoNativeClient {\n /** The bitcoinjs-lib module, with this library's `@noble/curves` ECC backend installed. */\n readonly bitcoin: typeof bitcoin;\n /** A fresh copy of this network's bitcoinjs parameters. */\n readonly network: ReturnType<typeof networkOf>;\n /** A GET to the handle's Esplora (`rpc`) endpoints, through the policy-wrapped transport. */\n esplora<T = unknown>(path: string, responseType?: 'json' | 'text'): Promise<T>;\n}\n\nfunction probes(expectedIdentity: string): HealthProbes {\n const text = (call: EndpointCall, path: string, route: string) =>\n call.http<string>({ method: 'GET', path, route, responseType: 'text' });\n return {\n identity: async (call) =>\n parseHash(await text(call, '/block-height/0', '/block-height/:height')),\n expectedIdentity,\n height: async (call) =>\n parseHeight(await text(call, '/blocks/tip/height', '/blocks/tip/height')),\n };\n}\n\nexport const utxoDriverFactory: DriverFactory = {\n async create(ctx: DriverContext): Promise<ChainDriver> {\n const config = utxoNetworkConfig(ctx.chain, ctx.network, ctx.options);\n if (!ctx.indexer) {\n throw new ConfigError(\n 'CONFIG_INVALID',\n 'the UTXO driver requires an indexer provider',\n );\n }\n // Both transports, before any traffic: the genesis-hash probe keeps an endpoint of\n // another network out of the `rpc` reads and the `indexer` reads alike.\n ctx.transport.setProbes(probes(config.genesisHash));\n ctx.indexer.setProbes(probes(config.genesisHash));\n const esplora = new EsploraClient(ctx.transport, ctx.indexer);\n const utxo: UtxoContext = {\n esplora,\n chain: ctx.chain,\n network: ctx.network,\n config,\n log: ctx.log,\n };\n const network = networkOf(config.address);\n const builder = utxoBuilder(utxo, network);\n return {\n ordering: 'inputs',\n capabilities: config.capabilities,\n address: addressCodec(utxo),\n reader: chainReader(utxo),\n builder,\n broadcaster: utxoBroadcaster(utxo),\n proofs: proofSource(utxo),\n replacement: utxoReplacement(utxo, network),\n blocks: blockSource(utxo),\n history: addressHistory(utxo),\n ext: {\n utxo: {\n listUnspent: (address: string) => listUnspent(utxo, address),\n coinSelection: (request: Parameters<UtxoExt['utxo']['coinSelection']>[0]) =>\n coinSelectionPreview(utxo, request),\n },\n } satisfies UtxoExt,\n limits: () => ({ maxOutputs: MAX_OUTPUTS }),\n createNativeClient: () => {\n // The doc's promise: taproot works on this module (and a swapped backend is undone).\n useNobleEcc();\n const client: UtxoNativeClient = {\n bitcoin,\n network: { ...network, bip32: { ...network.bip32 } },\n esplora: <T>(path: string, responseType: 'json' | 'text' = 'json') =>\n ctx.transport.http<T>(\n { method: 'GET', path, route: '/native', responseType },\n READ,\n ),\n };\n return { client };\n },\n };\n },\n};\n"]}
1
+ {"version":3,"file":"driver.js","sourceRoot":"","sources":["../../../src/adapters/utxo/driver.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The UTXO driver factory: validated network config, endpoint probes on both transports,\n * and the ports. Loaded only by the manifest's `load()`, with bitcoinjs-lib.\n */\nimport type { ChainDriver, DriverContext, DriverFactory } from '../../core/driver/types';\nimport { ConfigError } from '../../core/errors/error';\nimport type { EndpointCall, HealthProbes } from '../../core/transport/types';\nimport { utxoBroadcaster, utxoBuilder, utxoReplacement } from './builder';\nimport { networkOf } from './codec';\nimport { READ, parseWalletOptions, signingScheme, type UtxoContext } from './context';\nimport { EsploraClient, parseHash, parseHeight } from './esplora';\nimport { utxoNetworkConfig } from './network';\nimport { blockSource, proofSource } from './proofs';\nimport { addressCodec, addressHistory, chainReader, listUnspent } from './reader';\nimport { bitcoin, useNobleEcc } from './sdk';\nimport { coinSelectionPreview } from './preview';\nimport { MAX_OUTPUTS } from './spend';\nimport type { UtxoExt } from './types';\n\nexport interface UtxoNativeClient {\n /** The bitcoinjs-lib module, with this library's `@noble/curves` ECC backend installed. */\n readonly bitcoin: typeof bitcoin;\n /** A fresh copy of this network's bitcoinjs parameters. */\n readonly network: ReturnType<typeof networkOf>;\n /** A GET to the handle's Esplora (`rpc`) endpoints, through the policy-wrapped transport. */\n esplora<T = unknown>(path: string, responseType?: 'json' | 'text'): Promise<T>;\n}\n\nfunction probes(expectedIdentity: string): HealthProbes {\n const text = (call: EndpointCall, path: string, route: string) =>\n call.http<string>({ method: 'GET', path, route, responseType: 'text' });\n return {\n identity: async (call) =>\n parseHash(await text(call, '/block-height/0', '/block-height/:height')),\n expectedIdentity,\n height: async (call) =>\n parseHeight(await text(call, '/blocks/tip/height', '/blocks/tip/height')),\n };\n}\n\nexport const utxoDriverFactory: DriverFactory = {\n async create(ctx: DriverContext): Promise<ChainDriver> {\n const config = utxoNetworkConfig(ctx.chain, ctx.network, ctx.options);\n if (!ctx.indexer) {\n throw new ConfigError(\n 'CONFIG_INVALID',\n 'the UTXO driver requires an indexer provider',\n );\n }\n // Both transports, before any traffic: the genesis-hash probe keeps an endpoint of\n // another network out of the `rpc` reads and the `indexer` reads alike.\n ctx.transport.setProbes(probes(config.genesisHash));\n ctx.indexer.setProbes(probes(config.genesisHash));\n const esplora = new EsploraClient(ctx.transport, ctx.indexer);\n const utxo: UtxoContext = {\n esplora,\n chain: ctx.chain,\n network: ctx.network,\n config,\n log: ctx.log,\n };\n const network = networkOf(config.address);\n const builder = utxoBuilder(utxo, network);\n return {\n ordering: 'inputs',\n capabilities: config.capabilities,\n address: addressCodec(utxo),\n reader: chainReader(utxo),\n builder,\n broadcaster: utxoBroadcaster(utxo),\n proofs: proofSource(utxo),\n replacement: utxoReplacement(utxo, network),\n blocks: blockSource(utxo),\n history: addressHistory(utxo),\n ext: {\n utxo: {\n listUnspent: (address: string) => listUnspent(utxo, address),\n coinSelection: (request: Parameters<UtxoExt['utxo']['coinSelection']>[0]) =>\n coinSelectionPreview(utxo, request),\n },\n } satisfies UtxoExt,\n limits: () => ({ maxOutputs: MAX_OUTPUTS }),\n // One scheme per address type, so a p2tr wallet's ECDSA-only signer (a KMS key) is\n // refused when the wallet resolves, not at signing.\n signingSchemes: (wallet) => [\n signingScheme(parseWalletOptions(wallet, config).addressType),\n ],\n createNativeClient: () => {\n // The doc's promise: taproot works on this module (and a swapped backend is undone).\n useNobleEcc();\n const client: UtxoNativeClient = {\n bitcoin,\n network: { ...network, bip32: { ...network.bip32 } },\n esplora: <T>(path: string, responseType: 'json' | 'text' = 'json') =>\n ctx.transport.http<T>(\n { method: 'GET', path, route: '/native', responseType },\n READ,\n ),\n };\n return { client };\n },\n };\n },\n};\n"]}
@@ -6,12 +6,13 @@
6
6
  * field is malformed and never a default, an answer for another id than the one asked for is
7
7
  * refused, and a 404 is `null`. Chain data is read as the chain allows it, never bounded by
8
8
  * what this library builds (lenient readers). Chain reads use the `rpc` transport; address
9
- * reads use the `indexer`. Only raw transaction hex is decoded, by the strict linear
10
- * reader (`rawtx.ts`), to bind it to its txid.
9
+ * reads use the `indexer`. Only raw transactions and blocks are decoded, by the strict
10
+ * linear readers (`rawtx.ts`, `rawblock.ts`), to bind them to their txid or block hash.
11
11
  */
12
12
  import { ProviderError } from '../../core/errors/error';
13
13
  import type { Transport } from '../../core/transport/types';
14
14
  import { type PreviousTx } from './codec';
15
+ import { type RawBlock } from './rawblock';
15
16
  import type { EsploraAddressStats, EsploraBlock, EsploraOutspend, EsploraStatus, EsploraTx, EsploraUtxo, UtxoCallTags } from './types';
16
17
  /**
17
18
  * The previous transactions one client keeps, by count and by bytes without the
@@ -35,6 +36,11 @@ export declare function parseOutspend(value: unknown): EsploraOutspend;
35
36
  export declare function parseFeeEstimates(value: unknown): ReadonlyMap<number, number>;
36
37
  /** Whether a caller's id is a txid or block hash (64 lowercase hex characters). */
37
38
  export declare const isHash: (value: unknown) => value is string;
39
+ /** An output as a transaction's bytes hold it: its value, signed as bitcoind reads it. */
40
+ export interface TxOutput {
41
+ readonly value: bigint;
42
+ readonly script: Uint8Array;
43
+ }
38
44
  /** One Esplora endpoint family (the `rpc` transport) and its address index (`indexer`). */
39
45
  export declare class EsploraClient {
40
46
  #private;
@@ -49,6 +55,14 @@ export declare class EsploraClient {
49
55
  /** The hash of the block at `height` on this endpoint's best chain, or `null` above its tip. */
50
56
  blockHashAt(height: bigint, tags: UtxoCallTags): Promise<string | null>;
51
57
  block(blockHash: string, tags: UtxoCallTags): Promise<EsploraBlock | null>;
58
+ /**
59
+ * `/block/:hash/raw`, the block's own bytes, read strictly once (`rawblock.ts`) and bound to
60
+ * `blockHash`: bytes that do not decode are malformed, and bytes whose header hashes to
61
+ * another block, or whose transactions are not the ones that header commits to, are a
62
+ * retryable `PROVIDER_INCONSISTENT`. So no endpoint can add, drop or alter a transaction of
63
+ * a block whose hash is right. A 404 (a backend that does not have the block) is `null`.
64
+ */
65
+ rawBlock(blockHash: string, tags: UtxoCallTags): Promise<RawBlock | null>;
52
66
  blockTxids(blockHash: string, tags: UtxoCallTags): Promise<string[]>;
53
67
  /** Up to 25 transactions of a block from `start` (a multiple of 25). */
54
68
  blockTxs(blockHash: string, start: number, tags: UtxoCallTags): Promise<EsploraTx[]>;
@@ -61,6 +75,13 @@ export declare class EsploraClient {
61
75
  * served it, in a bounded cache: a replacement's inputs are read once per client.
62
76
  */
63
77
  previousTx(txid: string, tags: UtxoCallTags): Promise<PreviousTx | null>;
78
+ /**
79
+ * The outputs of transaction `txid`, from its bytes, bound to `txid` (see `#rawTx`), or
80
+ * `null` when the endpoint does not have it: what an input naming `txid:vout` spends. A
81
+ * previous transaction this client keeps answers without a read; one read here is not kept,
82
+ * since a block scan reads thousands, which would evict a build's.
83
+ */
84
+ outputsOf(txid: string, tags: UtxoCallTags): Promise<readonly TxOutput[] | null>;
64
85
  /**
65
86
  * A transaction this client sent for an Attempt and a node took. Its bytes are
66
87
  * known here, so its outputs are not an indexer's word even before a block holds it, and
@@ -17,11 +17,13 @@ exports.parseFeeEstimates = parseFeeEstimates;
17
17
  * field is malformed and never a default, an answer for another id than the one asked for is
18
18
  * refused, and a 404 is `null`. Chain data is read as the chain allows it, never bounded by
19
19
  * what this library builds (lenient readers). Chain reads use the `rpc` transport; address
20
- * reads use the `indexer`. Only raw transaction hex is decoded, by the strict linear
21
- * reader (`rawtx.ts`), to bind it to its txid.
20
+ * reads use the `indexer`. Only raw transactions and blocks are decoded, by the strict
21
+ * linear readers (`rawtx.ts`, `rawblock.ts`), to bind them to their txid or block hash.
22
22
  */
23
23
  const error_1 = require("../../core/errors/error");
24
24
  const codec_1 = require("./codec");
25
+ const rawblock_1 = require("./rawblock");
26
+ const rawtx_1 = require("./rawtx");
25
27
  const HEX64 = /^[0-9a-f]{64}$/;
26
28
  /**
27
29
  * The previous transactions one client keeps, by count and by bytes without the
@@ -238,6 +240,17 @@ function txidOf(hex) {
238
240
  return undefined;
239
241
  }
240
242
  }
243
+ /** The txid and outputs (copied) of strictly decoded transaction hex, or `undefined`. */
244
+ function outputsOfHex(hex) {
245
+ const tx = (0, rawtx_1.readTxHex)(hex);
246
+ return (tx && {
247
+ txid: tx.txid,
248
+ outputs: tx.outputs.map(({ value, script }) => ({
249
+ value,
250
+ script: Uint8Array.from(script),
251
+ })),
252
+ });
253
+ }
241
254
  /** Transactions kept per txid, oldest out first, by count and by bytes without witness. */
242
255
  class TxCache {
243
256
  bounds;
@@ -321,6 +334,28 @@ class EsploraClient {
321
334
  return block;
322
335
  });
323
336
  }
337
+ /**
338
+ * `/block/:hash/raw`, the block's own bytes, read strictly once (`rawblock.ts`) and bound to
339
+ * `blockHash`: bytes that do not decode are malformed, and bytes whose header hashes to
340
+ * another block, or whose transactions are not the ones that header commits to, are a
341
+ * retryable `PROVIDER_INCONSISTENT`. So no endpoint can add, drop or alter a transaction of
342
+ * a block whose hash is right. A 404 (a backend that does not have the block) is `null`.
343
+ */
344
+ rawBlock(blockHash, tags) {
345
+ return this.#orNull(async () => {
346
+ const answer = await this.#get(this.chain, `/block/${segment(blockHash, 'id')}/raw`, '/block/:hash/raw', tags, 'bytes');
347
+ const block = answer instanceof Uint8Array ? (0, rawblock_1.readBlock)(answer) : undefined;
348
+ if (!block)
349
+ throw malformed('raw block');
350
+ if (block.hash !== blockHash) {
351
+ throw new error_1.ProviderError('PROVIDER_INCONSISTENT', 'the block bytes do not hash to the block asked for', { retryable: true });
352
+ }
353
+ if (!(0, rawblock_1.commitsToTransactions)(block)) {
354
+ throw new error_1.ProviderError('PROVIDER_INCONSISTENT', "the block's transactions are not the ones its header commits to", { retryable: true });
355
+ }
356
+ return block;
357
+ });
358
+ }
324
359
  async blockTxids(blockHash, tags) {
325
360
  const ids = await this.#get(this.chain, `/block/${segment(blockHash, 'id')}/txids`, '/block/:hash/txids', tags);
326
361
  return list(ids, 'txids', (id) => hash(id, 'txid'));
@@ -395,6 +430,18 @@ class EsploraClient {
395
430
  this.#previous.set(found);
396
431
  return found;
397
432
  }
433
+ /**
434
+ * The outputs of transaction `txid`, from its bytes, bound to `txid` (see `#rawTx`), or
435
+ * `null` when the endpoint does not have it: what an input naming `txid:vout` spends. A
436
+ * previous transaction this client keeps answers without a read; one read here is not kept,
437
+ * since a block scan reads thousands, which would evict a build's.
438
+ */
439
+ async outputsOf(txid, tags) {
440
+ const known = this.#own.get(txid) ?? this.#previous.get(txid);
441
+ if (known)
442
+ return known.outputs;
443
+ return this.#orNull(async () => (await this.#rawTx(txid, tags, outputsOfHex)).tx.outputs);
444
+ }
398
445
  /**
399
446
  * A transaction this client sent for an Attempt and a node took. Its bytes are
400
447
  * known here, so its outputs are not an indexer's word even before a block holds it, and
@@ -1 +1 @@
1
- {"version":3,"file":"esplora.js","sourceRoot":"","sources":["../../../src/adapters/utxo/esplora.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The Esplora REST API, sent directly over the core transport. Every call carries the\n * calling driver method's tags and a `route` template with no identifiers in it, which\n * events and logs show instead of the path. Answers are validated strictly: a malformed\n * one is a retryable `ProviderError('PROVIDER_UNAVAILABLE')`, a missing or ill-typed\n * field is malformed and never a default, an answer for another id than the one asked for is\n * refused, and a 404 is `null`. Chain data is read as the chain allows it, never bounded by\n * what this library builds (lenient readers). Chain reads use the `rpc` transport; address\n * reads use the `indexer`. Only raw transaction hex is decoded, by the strict linear\n * reader (`rawtx.ts`), to bind it to its txid.\n */\nimport {\n ProviderError,\n ValidationError,\n isCryptoAioError,\n} from '../../core/errors/error';\nimport type { HttpRequest, Transport } from '../../core/transport/types';\nimport { previousTxOf, txidOfHex, type PreviousTx } from './codec';\nimport type {\n EsploraAddressStats,\n EsploraBlock,\n EsploraInput,\n EsploraOutput,\n EsploraOutspend,\n EsploraStatus,\n EsploraTx,\n EsploraUtxo,\n UtxoCallTags,\n} from './types';\n\ntype Json = Readonly<Record<string, unknown>>;\n\nconst HEX64 = /^[0-9a-f]{64}$/;\n/**\n * The previous transactions one client keeps, by count and by bytes without the\n * witness (a larger one is not kept). A typical one is a few hundred bytes.\n */\nexport const PREVIOUS_TX_CACHE = Object.freeze({ entries: 1_000, bytes: 8_000_000 });\n/** Bitcoin Core's `MAX_MONEY`: no output, and no balance, is larger. */\nexport const MAX_MONEY = 2_100_000_000_000_000n;\n\n/**\n * Lowercase hex of whole bytes. A flat character class, not `([0-9a-f]{2})+`: that pattern\n * overflows V8's regexp stack on the hex of a transaction of about 3.5 MB, which is valid.\n */\nconst isHex = (value: string): boolean =>\n value.length % 2 === 0 && /^[0-9a-f]*$/.test(value);\n\nexport function malformed(what: string): ProviderError {\n return new ProviderError('PROVIDER_UNAVAILABLE', `malformed Esplora answer: ${what}`);\n}\n\n/** A definitive 404 (not an ambiguous or retried failure): the resource is not there. */\nexport function isNotFound(error: unknown): boolean {\n return (\n isCryptoAioError(error, 'RPC_ERROR') &&\n !error.ambiguous &&\n error.details?.status === 404\n );\n}\n\nconst record = (value: unknown, what: string): Json => {\n if (value === null || typeof value !== 'object' || Array.isArray(value))\n throw malformed(what);\n return value as Json;\n};\nconst hash = (value: unknown, what: string): string => {\n if (typeof value !== 'string' || !HEX64.test(value)) throw malformed(what);\n return value;\n};\nconst integer = (value: unknown, what: string, max = Number.MAX_SAFE_INTEGER): number => {\n if (!Number.isSafeInteger(value) || (value as number) < 0 || (value as number) > max) {\n throw malformed(what);\n }\n return value as number;\n};\nconst sats = (value: unknown, what: string): bigint =>\n BigInt(integer(value, what, Number(MAX_MONEY)));\nconst u32 = (value: unknown, what: string): number => integer(value, what, 0xffffffff);\n/**\n * A transaction's 32-bit version, which Esplora servers print signed or unsigned, as one\n * signed value (`v | 0`), so both printings agree. Chain data: any version a block holds is\n * read, whatever this library builds (version 2).\n */\nconst txVersion = (value: unknown): number => {\n if (\n !Number.isSafeInteger(value) ||\n (value as number) < -0x80000000 ||\n (value as number) > 0xffffffff\n ) {\n throw malformed('tx.version');\n }\n return (value as number) | 0;\n};\n/**\n * A cumulative total of an address (every output it ever received or spent), read exactly\n * (`exactIntegers`): a busy address's totals exceed 21 million bitcoin, and may exceed 2^53.\n */\nconst total = (value: unknown, what: string): bigint => {\n if (typeof value === 'bigint') {\n if (value < 0n) throw malformed(what);\n return value;\n }\n return BigInt(integer(value, what));\n};\n\nexport function parseHeight(text: unknown): bigint {\n if (typeof text !== 'string' || !/^(0|[1-9][0-9]{0,9})$/.test(text.trim())) {\n throw malformed('block height');\n }\n return BigInt(text.trim());\n}\n\nexport function parseHash(text: unknown): string {\n return hash(typeof text === 'string' ? text.trim() : text, 'block hash');\n}\n\nexport function parseStatus(value: unknown): EsploraStatus {\n const status = record(value, 'status');\n if (typeof status.confirmed !== 'boolean') throw malformed('status.confirmed');\n if (!status.confirmed) return { confirmed: false };\n return {\n confirmed: true,\n blockHeight: BigInt(integer(status.block_height, 'status.block_height')),\n blockHash: hash(status.block_hash, 'status.block_hash'),\n ...(status.block_time !== undefined && status.block_time !== null\n ? { blockTime: integer(status.block_time, 'status.block_time') }\n : {}),\n };\n}\n\nfunction parseOutput(value: unknown): EsploraOutput {\n const output = record(value, 'vout');\n if (typeof output.scriptpubkey !== 'string' || !isHex(output.scriptpubkey)) {\n throw malformed('vout.scriptpubkey');\n }\n if (typeof output.scriptpubkey_type !== 'string')\n throw malformed('vout.scriptpubkey_type');\n const address = output.scriptpubkey_address;\n if (address !== undefined && address !== null && typeof address !== 'string') {\n throw malformed('vout.scriptpubkey_address');\n }\n return {\n script: output.scriptpubkey,\n type: output.scriptpubkey_type,\n ...(typeof address === 'string' ? { address } : {}),\n value: sats(output.value, 'vout.value'),\n };\n}\n\nfunction parseInput(value: unknown): EsploraInput {\n const input = record(value, 'vin');\n // It decides whether the input has a previous output and the transaction a fee.\n if (typeof input.is_coinbase !== 'boolean') throw malformed('vin.is_coinbase');\n const coinbase = input.is_coinbase;\n return {\n txid: hash(input.txid, 'vin.txid'),\n vout: u32(input.vout, 'vin.vout'),\n coinbase,\n sequence: u32(input.sequence, 'vin.sequence'),\n ...(coinbase ? {} : { prevout: parseOutput(input.prevout) }),\n };\n}\n\nexport function parseTx(value: unknown): EsploraTx {\n const tx = record(value, 'tx');\n if (!Array.isArray(tx.vin) || !Array.isArray(tx.vout)) throw malformed('tx.vin/vout');\n return {\n txid: hash(tx.txid, 'tx.txid'),\n version: txVersion(tx.version),\n locktime: u32(tx.locktime, 'tx.locktime'),\n weight: integer(tx.weight, 'tx.weight'),\n // Esplora answers `fee` for every transaction (0 for a coinbase): missing is malformed.\n fee: sats(tx.fee, 'tx.fee'),\n vin: tx.vin.map(parseInput),\n vout: tx.vout.map(parseOutput),\n status: parseStatus(tx.status),\n };\n}\n\nfunction parseBlock(value: unknown): EsploraBlock {\n const block = record(value, 'block');\n const height = BigInt(integer(block.height, 'block.height'));\n return {\n hash: hash(block.id, 'block.id'),\n height,\n parentHash:\n height === 0n && block.previousblockhash === null\n ? '0'.repeat(64)\n : hash(block.previousblockhash, 'block.previousblockhash'),\n timestamp: integer(block.timestamp, 'block.timestamp'),\n txCount: integer(block.tx_count, 'block.tx_count'),\n };\n}\n\nexport function parseOutspend(value: unknown): EsploraOutspend {\n const spend = record(value, 'outspend');\n if (typeof spend.spent !== 'boolean') throw malformed('outspend.spent');\n if (!spend.spent) return { spent: false };\n return {\n spent: true,\n txid: hash(spend.txid, 'outspend.txid'),\n vin: u32(spend.vin, 'outspend.vin'),\n status: parseStatus(spend.status),\n };\n}\n\nfunction parseUtxo(value: unknown): EsploraUtxo {\n const utxo = record(value, 'utxo');\n return {\n txid: hash(utxo.txid, 'utxo.txid'),\n vout: u32(utxo.vout, 'utxo.vout'),\n value: sats(utxo.value, 'utxo.value'),\n status: parseStatus(utxo.status),\n };\n}\n\nfunction list<T>(value: unknown, what: string, parse: (item: unknown) => T): T[] {\n if (!Array.isArray(value)) throw malformed(what);\n return value.map(parse);\n}\n\n/** Esplora's `/fee-estimates`: confirmation target (blocks) → sat/vB (a float). */\nexport function parseFeeEstimates(value: unknown): ReadonlyMap<number, number> {\n const estimates = record(value, 'fee-estimates');\n const out = new Map<number, number>();\n for (const [target, rate] of Object.entries(estimates)) {\n if (!/^[1-9][0-9]{0,3}$/.test(target)) throw malformed('fee-estimates target');\n if (typeof rate !== 'number' || !Number.isFinite(rate) || rate < 0 || rate > 1e7) {\n throw malformed('fee-estimates rate');\n }\n out.set(Number(target), rate);\n }\n return out;\n}\n\n/** Whether a caller's id is a txid or block hash (64 lowercase hex characters). */\nexport const isHash = (value: unknown): value is string =>\n typeof value === 'string' && HEX64.test(value);\n\n/**\n * A URL path segment from a caller must be exactly an id or an address, so no `..`, `/`\n * or query can reach another route. The reader maps a malformed id to \"not found\" first.\n */\nfunction segment(value: string, kind: 'id' | 'address'): string {\n if (kind === 'id' && !isHash(value)) {\n throw new ValidationError(\n 'INVALID_INTENT',\n 'a transaction or block id must be 64 lowercase hex digits',\n );\n }\n if (kind === 'address' && !/^[0-9A-Za-z]{1,90}$/.test(value)) {\n throw new ValidationError('INVALID_ADDRESS', 'invalid Bitcoin address');\n }\n return value;\n}\n\n/** The txid of strictly decoded transaction hex, or `undefined`. */\nfunction txidOf(hex: string): { readonly txid: string } | undefined {\n try {\n return { txid: txidOfHex(hex) };\n } catch {\n return undefined;\n }\n}\n\n/** Transactions kept per txid, oldest out first, by count and by bytes without witness. */\nclass TxCache {\n readonly #txs = new Map<string, PreviousTx>();\n #bytes = 0;\n\n constructor(\n private readonly bounds: { readonly entries: number; readonly bytes: number },\n ) {}\n\n has(txid: string): boolean {\n return this.#txs.has(txid);\n }\n\n /** The transaction, now the most recently used, or `undefined`. */\n get(txid: string): PreviousTx | undefined {\n const tx = this.#txs.get(txid);\n if (tx) {\n this.#txs.delete(txid);\n this.#txs.set(txid, tx);\n }\n return tx;\n }\n\n set(tx: PreviousTx): void {\n if (tx.bytes.length > this.bounds.bytes) return;\n const old = this.#txs.get(tx.txid);\n if (old) {\n this.#txs.delete(tx.txid);\n this.#bytes -= old.bytes.length;\n }\n this.#txs.set(tx.txid, tx);\n this.#bytes += tx.bytes.length;\n // The oldest go first (a Map iterates in insertion order).\n for (const [id, entry] of this.#txs) {\n if (this.#txs.size <= this.bounds.entries && this.#bytes <= this.bounds.bytes)\n break;\n this.#txs.delete(id);\n this.#bytes -= entry.bytes.length;\n }\n }\n}\n\n/** One Esplora endpoint family (the `rpc` transport) and its address index (`indexer`). */\nexport class EsploraClient {\n readonly #previous: TxCache;\n readonly #own: TxCache;\n\n constructor(\n private readonly chain: Transport,\n private readonly indexer: Transport,\n private readonly cache: {\n readonly entries: number;\n readonly bytes: number;\n } = PREVIOUS_TX_CACHE,\n ) {\n this.#previous = new TxCache(cache);\n this.#own = new TxCache(cache);\n }\n\n #get<T>(\n transport: Transport,\n path: string,\n route: string,\n tags: UtxoCallTags,\n responseType: 'json' | 'text' = 'json',\n exactIntegers = false,\n ): Promise<T> {\n const request: HttpRequest = { method: 'GET', path, route, responseType };\n return transport.http<T>(request, exactIntegers ? { ...tags, exactIntegers } : tags);\n }\n\n async #orNull<T>(work: () => Promise<T>): Promise<T | null> {\n try {\n return await work();\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n }\n\n async tipHeight(tags: UtxoCallTags): Promise<bigint> {\n return parseHeight(\n await this.#get(\n this.chain,\n '/blocks/tip/height',\n '/blocks/tip/height',\n tags,\n 'text',\n ),\n );\n }\n\n /** The hash of the block at `height` on this endpoint's best chain, or `null` above its tip. */\n blockHashAt(height: bigint, tags: UtxoCallTags): Promise<string | null> {\n return this.#orNull(async () =>\n parseHash(\n await this.#get(\n this.chain,\n `/block-height/${height}`,\n '/block-height/:height',\n tags,\n 'text',\n ),\n ),\n );\n }\n\n block(blockHash: string, tags: UtxoCallTags): Promise<EsploraBlock | null> {\n return this.#orNull(async () => {\n const block = parseBlock(\n await this.#get(\n this.chain,\n `/block/${segment(blockHash, 'id')}`,\n '/block/:hash',\n tags,\n ),\n );\n // The answer must be the block asked for.\n if (block.hash !== blockHash) throw malformed('block.id');\n return block;\n });\n }\n\n async blockTxids(blockHash: string, tags: UtxoCallTags): Promise<string[]> {\n const ids = await this.#get(\n this.chain,\n `/block/${segment(blockHash, 'id')}/txids`,\n '/block/:hash/txids',\n tags,\n );\n return list(ids, 'txids', (id) => hash(id, 'txid'));\n }\n\n /** Up to 25 transactions of a block from `start` (a multiple of 25). */\n async blockTxs(\n blockHash: string,\n start: number,\n tags: UtxoCallTags,\n ): Promise<EsploraTx[]> {\n const txs = await this.#get(\n this.chain,\n `/block/${segment(blockHash, 'id')}/txs/${start}`,\n '/block/:hash/txs/:start',\n tags,\n );\n return list(txs, 'block txs', parseTx);\n }\n\n tx(txid: string, tags: UtxoCallTags): Promise<EsploraTx | null> {\n return this.#orNull(async () => {\n const tx = parseTx(\n await this.#get(this.chain, `/tx/${segment(txid, 'id')}`, '/tx/:txid', tags),\n );\n // The answer must be the transaction asked for.\n if (tx.txid !== txid) throw malformed('tx.txid');\n return tx;\n });\n }\n\n /**\n * `/tx/:txid/hex`, decoded strictly once and bound to `txid`: bytes that do not decode are\n * malformed, and the bytes of another transaction are a retryable `PROVIDER_INCONSISTENT`,\n * so a garbled answer is never taken for the caller's error. The decoder's cap is the\n * largest transaction a block can hold, never what this library builds. Under\n * a quorum, each endpoint's answer is keyed on the txid its bytes hash to, and the answer\n * the quorum returns is not decoded again.\n */\n async #rawTx<T extends { readonly txid: string }>(\n txid: string,\n tags: UtxoCallTags,\n decode: (hex: string) => T | undefined,\n ): Promise<{ readonly hex: string; readonly tx: T }> {\n const decoded = new Map<string, T | undefined>();\n const read = (answer: unknown): { hex: string; tx: T | undefined } => {\n const hex = typeof answer === 'string' ? answer.trim() : '';\n if (!decoded.has(hex)) {\n decoded.set(hex, hex.length > 0 && isHex(hex) ? decode(hex) : undefined);\n }\n return { hex, tx: decoded.get(hex) };\n };\n const keyed: UtxoCallTags =\n tags.quorum === undefined\n ? tags\n : {\n ...tags,\n // A throwing key counts as a disagreement.\n quorumKey: (answer) => {\n const { tx } = read(answer);\n if (!tx) throw malformed('tx hex');\n return tx.txid;\n },\n };\n const answer = await this.#get(\n this.chain,\n `/tx/${segment(txid, 'id')}/hex`,\n '/tx/:txid/hex',\n keyed,\n 'text',\n );\n const { hex, tx } = read(answer);\n if (!tx) throw malformed('tx hex');\n if (tx.txid !== txid) {\n throw new ProviderError(\n 'PROVIDER_INCONSISTENT',\n 'the transaction bytes do not hash to the id asked for',\n { retryable: true },\n );\n }\n return { hex, tx };\n }\n\n /** The raw transaction, as hex, bound to `txid` (see `#rawTx`). */\n txHex(txid: string, tags: UtxoCallTags): Promise<string | null> {\n return this.#orNull(async () => (await this.#rawTx(txid, tags, txidOf)).hex);\n }\n\n /**\n * An input's previous transaction, decoded once and bound to `txid` (see\n * `#rawTx`). Its bytes authenticate themselves, so it is kept per txid, whatever endpoint\n * served it, in a bounded cache: a replacement's inputs are read once per client.\n */\n async previousTx(txid: string, tags: UtxoCallTags): Promise<PreviousTx | null> {\n const known = this.#own.get(txid) ?? this.#previous.get(txid);\n if (known) return known;\n const found = await this.#orNull(\n async () => (await this.#rawTx(txid, tags, previousTxOf)).tx,\n );\n if (found) this.#previous.set(found);\n return found;\n }\n\n /**\n * A transaction this client sent for an Attempt and a node took. Its bytes are\n * known here, so its outputs are not an indexer's word even before a block holds it, and\n * under `minInputConfirmations: 0` they may be spent unconfirmed. Kept like the cache.\n */\n rememberOwn(tx: PreviousTx): void {\n this.#own.set(tx);\n }\n\n isOwn(txid: string): boolean {\n return this.#own.has(txid);\n }\n\n async outspend(\n txid: string,\n vout: number,\n tags: UtxoCallTags,\n ): Promise<EsploraOutspend> {\n return parseOutspend(\n await this.#get(\n this.chain,\n `/tx/${segment(txid, 'id')}/outspend/${vout}`,\n '/tx/:txid/outspend/:vout',\n tags,\n ),\n );\n }\n\n async feeEstimates(tags: UtxoCallTags): Promise<ReadonlyMap<number, number>> {\n return parseFeeEstimates(\n await this.#get(this.chain, '/fee-estimates', '/fee-estimates', tags),\n );\n }\n\n /** `POST /tx`: the node's txid on success; a node refusal is the transport's `RPC_ERROR`. */\n async broadcast(hex: string, tags: UtxoCallTags): Promise<string> {\n const answer = await this.chain.http<string>(\n { method: 'POST', path: '/tx', route: '/tx', body: hex, responseType: 'text' },\n tags,\n );\n if (typeof answer !== 'string' || !HEX64.test(answer.trim())) {\n // The node answered 2xx, so the bytes may have been accepted.\n throw new ProviderError(\n 'PROVIDER_UNAVAILABLE',\n 'malformed Esplora answer: broadcast txid',\n {\n ambiguous: true,\n },\n );\n }\n return answer.trim();\n }\n\n async addressUtxos(address: string, tags: UtxoCallTags): Promise<EsploraUtxo[]> {\n const utxos = await this.#get(\n this.indexer,\n `/address/${segment(address, 'address')}/utxo`,\n '/address/:address/utxo',\n tags,\n );\n return list(utxos, 'utxo list', parseUtxo);\n }\n\n async addressStats(address: string, tags: UtxoCallTags): Promise<EsploraAddressStats> {\n const info = record(\n await this.#get(\n this.indexer,\n `/address/${segment(address, 'address')}`,\n '/address/:address',\n tags,\n 'json',\n true,\n ),\n 'address',\n );\n const chain = record(info.chain_stats, 'chain_stats');\n const funded = total(chain.funded_txo_sum, 'funded_txo_sum');\n const spent = total(chain.spent_txo_sum, 'spent_txo_sum');\n // The totals are unbounded; the balance is the chain's: 0 to 21 million bitcoin.\n if (spent > funded || funded - spent > MAX_MONEY) throw malformed('address balance');\n return { funded, spent };\n }\n\n /** Confirmed history, newest first, 25 per page, after `lastSeen` when given. */\n async addressTxs(\n address: string,\n lastSeen: string | undefined,\n tags: UtxoCallTags,\n ): Promise<EsploraTx[]> {\n const base = `/address/${segment(address, 'address')}/txs/chain`;\n const path = lastSeen ? `${base}/${segment(lastSeen, 'id')}` : base;\n const route = lastSeen\n ? '/address/:address/txs/chain/:last_seen_txid'\n : '/address/:address/txs/chain';\n return list(await this.#get(this.indexer, path, route, tags), 'address txs', parseTx);\n }\n}\n"]}
1
+ {"version":3,"file":"esplora.js","sourceRoot":"","sources":["../../../src/adapters/utxo/esplora.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The Esplora REST API, sent directly over the core transport. Every call carries the\n * calling driver method's tags and a `route` template with no identifiers in it, which\n * events and logs show instead of the path. Answers are validated strictly: a malformed\n * one is a retryable `ProviderError('PROVIDER_UNAVAILABLE')`, a missing or ill-typed\n * field is malformed and never a default, an answer for another id than the one asked for is\n * refused, and a 404 is `null`. Chain data is read as the chain allows it, never bounded by\n * what this library builds (lenient readers). Chain reads use the `rpc` transport; address\n * reads use the `indexer`. Only raw transactions and blocks are decoded, by the strict\n * linear readers (`rawtx.ts`, `rawblock.ts`), to bind them to their txid or block hash.\n */\nimport {\n ProviderError,\n ValidationError,\n isCryptoAioError,\n} from '../../core/errors/error';\nimport type { HttpRequest, Transport } from '../../core/transport/types';\nimport { previousTxOf, txidOfHex, type PreviousTx } from './codec';\nimport { commitsToTransactions, readBlock, type RawBlock } from './rawblock';\nimport { readTxHex } from './rawtx';\nimport type {\n EsploraAddressStats,\n EsploraBlock,\n EsploraInput,\n EsploraOutput,\n EsploraOutspend,\n EsploraStatus,\n EsploraTx,\n EsploraUtxo,\n UtxoCallTags,\n} from './types';\n\ntype Json = Readonly<Record<string, unknown>>;\n\nconst HEX64 = /^[0-9a-f]{64}$/;\n/**\n * The previous transactions one client keeps, by count and by bytes without the\n * witness (a larger one is not kept). A typical one is a few hundred bytes.\n */\nexport const PREVIOUS_TX_CACHE = Object.freeze({ entries: 1_000, bytes: 8_000_000 });\n/** Bitcoin Core's `MAX_MONEY`: no output, and no balance, is larger. */\nexport const MAX_MONEY = 2_100_000_000_000_000n;\n\n/**\n * Lowercase hex of whole bytes. A flat character class, not `([0-9a-f]{2})+`: that pattern\n * overflows V8's regexp stack on the hex of a transaction of about 3.5 MB, which is valid.\n */\nconst isHex = (value: string): boolean =>\n value.length % 2 === 0 && /^[0-9a-f]*$/.test(value);\n\nexport function malformed(what: string): ProviderError {\n return new ProviderError('PROVIDER_UNAVAILABLE', `malformed Esplora answer: ${what}`);\n}\n\n/** A definitive 404 (not an ambiguous or retried failure): the resource is not there. */\nexport function isNotFound(error: unknown): boolean {\n return (\n isCryptoAioError(error, 'RPC_ERROR') &&\n !error.ambiguous &&\n error.details?.status === 404\n );\n}\n\nconst record = (value: unknown, what: string): Json => {\n if (value === null || typeof value !== 'object' || Array.isArray(value))\n throw malformed(what);\n return value as Json;\n};\nconst hash = (value: unknown, what: string): string => {\n if (typeof value !== 'string' || !HEX64.test(value)) throw malformed(what);\n return value;\n};\nconst integer = (value: unknown, what: string, max = Number.MAX_SAFE_INTEGER): number => {\n if (!Number.isSafeInteger(value) || (value as number) < 0 || (value as number) > max) {\n throw malformed(what);\n }\n return value as number;\n};\nconst sats = (value: unknown, what: string): bigint =>\n BigInt(integer(value, what, Number(MAX_MONEY)));\nconst u32 = (value: unknown, what: string): number => integer(value, what, 0xffffffff);\n/**\n * A transaction's 32-bit version, which Esplora servers print signed or unsigned, as one\n * signed value (`v | 0`), so both printings agree. Chain data: any version a block holds is\n * read, whatever this library builds (version 2).\n */\nconst txVersion = (value: unknown): number => {\n if (\n !Number.isSafeInteger(value) ||\n (value as number) < -0x80000000 ||\n (value as number) > 0xffffffff\n ) {\n throw malformed('tx.version');\n }\n return (value as number) | 0;\n};\n/**\n * A cumulative total of an address (every output it ever received or spent), read exactly\n * (`exactIntegers`): a busy address's totals exceed 21 million bitcoin, and may exceed 2^53.\n */\nconst total = (value: unknown, what: string): bigint => {\n if (typeof value === 'bigint') {\n if (value < 0n) throw malformed(what);\n return value;\n }\n return BigInt(integer(value, what));\n};\n\nexport function parseHeight(text: unknown): bigint {\n if (typeof text !== 'string' || !/^(0|[1-9][0-9]{0,9})$/.test(text.trim())) {\n throw malformed('block height');\n }\n return BigInt(text.trim());\n}\n\nexport function parseHash(text: unknown): string {\n return hash(typeof text === 'string' ? text.trim() : text, 'block hash');\n}\n\nexport function parseStatus(value: unknown): EsploraStatus {\n const status = record(value, 'status');\n if (typeof status.confirmed !== 'boolean') throw malformed('status.confirmed');\n if (!status.confirmed) return { confirmed: false };\n return {\n confirmed: true,\n blockHeight: BigInt(integer(status.block_height, 'status.block_height')),\n blockHash: hash(status.block_hash, 'status.block_hash'),\n ...(status.block_time !== undefined && status.block_time !== null\n ? { blockTime: integer(status.block_time, 'status.block_time') }\n : {}),\n };\n}\n\nfunction parseOutput(value: unknown): EsploraOutput {\n const output = record(value, 'vout');\n if (typeof output.scriptpubkey !== 'string' || !isHex(output.scriptpubkey)) {\n throw malformed('vout.scriptpubkey');\n }\n if (typeof output.scriptpubkey_type !== 'string')\n throw malformed('vout.scriptpubkey_type');\n const address = output.scriptpubkey_address;\n if (address !== undefined && address !== null && typeof address !== 'string') {\n throw malformed('vout.scriptpubkey_address');\n }\n return {\n script: output.scriptpubkey,\n type: output.scriptpubkey_type,\n ...(typeof address === 'string' ? { address } : {}),\n value: sats(output.value, 'vout.value'),\n };\n}\n\nfunction parseInput(value: unknown): EsploraInput {\n const input = record(value, 'vin');\n // It decides whether the input has a previous output and the transaction a fee.\n if (typeof input.is_coinbase !== 'boolean') throw malformed('vin.is_coinbase');\n const coinbase = input.is_coinbase;\n return {\n txid: hash(input.txid, 'vin.txid'),\n vout: u32(input.vout, 'vin.vout'),\n coinbase,\n sequence: u32(input.sequence, 'vin.sequence'),\n ...(coinbase ? {} : { prevout: parseOutput(input.prevout) }),\n };\n}\n\nexport function parseTx(value: unknown): EsploraTx {\n const tx = record(value, 'tx');\n if (!Array.isArray(tx.vin) || !Array.isArray(tx.vout)) throw malformed('tx.vin/vout');\n return {\n txid: hash(tx.txid, 'tx.txid'),\n version: txVersion(tx.version),\n locktime: u32(tx.locktime, 'tx.locktime'),\n weight: integer(tx.weight, 'tx.weight'),\n // Esplora answers `fee` for every transaction (0 for a coinbase): missing is malformed.\n fee: sats(tx.fee, 'tx.fee'),\n vin: tx.vin.map(parseInput),\n vout: tx.vout.map(parseOutput),\n status: parseStatus(tx.status),\n };\n}\n\nfunction parseBlock(value: unknown): EsploraBlock {\n const block = record(value, 'block');\n const height = BigInt(integer(block.height, 'block.height'));\n return {\n hash: hash(block.id, 'block.id'),\n height,\n parentHash:\n height === 0n && block.previousblockhash === null\n ? '0'.repeat(64)\n : hash(block.previousblockhash, 'block.previousblockhash'),\n timestamp: integer(block.timestamp, 'block.timestamp'),\n txCount: integer(block.tx_count, 'block.tx_count'),\n };\n}\n\nexport function parseOutspend(value: unknown): EsploraOutspend {\n const spend = record(value, 'outspend');\n if (typeof spend.spent !== 'boolean') throw malformed('outspend.spent');\n if (!spend.spent) return { spent: false };\n return {\n spent: true,\n txid: hash(spend.txid, 'outspend.txid'),\n vin: u32(spend.vin, 'outspend.vin'),\n status: parseStatus(spend.status),\n };\n}\n\nfunction parseUtxo(value: unknown): EsploraUtxo {\n const utxo = record(value, 'utxo');\n return {\n txid: hash(utxo.txid, 'utxo.txid'),\n vout: u32(utxo.vout, 'utxo.vout'),\n value: sats(utxo.value, 'utxo.value'),\n status: parseStatus(utxo.status),\n };\n}\n\nfunction list<T>(value: unknown, what: string, parse: (item: unknown) => T): T[] {\n if (!Array.isArray(value)) throw malformed(what);\n return value.map(parse);\n}\n\n/** Esplora's `/fee-estimates`: confirmation target (blocks) → sat/vB (a float). */\nexport function parseFeeEstimates(value: unknown): ReadonlyMap<number, number> {\n const estimates = record(value, 'fee-estimates');\n const out = new Map<number, number>();\n for (const [target, rate] of Object.entries(estimates)) {\n if (!/^[1-9][0-9]{0,3}$/.test(target)) throw malformed('fee-estimates target');\n if (typeof rate !== 'number' || !Number.isFinite(rate) || rate < 0 || rate > 1e7) {\n throw malformed('fee-estimates rate');\n }\n out.set(Number(target), rate);\n }\n return out;\n}\n\n/** Whether a caller's id is a txid or block hash (64 lowercase hex characters). */\nexport const isHash = (value: unknown): value is string =>\n typeof value === 'string' && HEX64.test(value);\n\n/**\n * A URL path segment from a caller must be exactly an id or an address, so no `..`, `/`\n * or query can reach another route. The reader maps a malformed id to \"not found\" first.\n */\nfunction segment(value: string, kind: 'id' | 'address'): string {\n if (kind === 'id' && !isHash(value)) {\n throw new ValidationError(\n 'INVALID_INTENT',\n 'a transaction or block id must be 64 lowercase hex digits',\n );\n }\n if (kind === 'address' && !/^[0-9A-Za-z]{1,90}$/.test(value)) {\n throw new ValidationError('INVALID_ADDRESS', 'invalid Bitcoin address');\n }\n return value;\n}\n\n/** The txid of strictly decoded transaction hex, or `undefined`. */\nfunction txidOf(hex: string): { readonly txid: string } | undefined {\n try {\n return { txid: txidOfHex(hex) };\n } catch {\n return undefined;\n }\n}\n\n/** An output as a transaction's bytes hold it: its value, signed as bitcoind reads it. */\nexport interface TxOutput {\n readonly value: bigint;\n readonly script: Uint8Array;\n}\n\n/** The txid and outputs (copied) of strictly decoded transaction hex, or `undefined`. */\nfunction outputsOfHex(\n hex: string,\n): { readonly txid: string; readonly outputs: readonly TxOutput[] } | undefined {\n const tx = readTxHex(hex);\n return (\n tx && {\n txid: tx.txid,\n outputs: tx.outputs.map(({ value, script }) => ({\n value,\n script: Uint8Array.from(script),\n })),\n }\n );\n}\n\n/** Transactions kept per txid, oldest out first, by count and by bytes without witness. */\nclass TxCache {\n readonly #txs = new Map<string, PreviousTx>();\n #bytes = 0;\n\n constructor(\n private readonly bounds: { readonly entries: number; readonly bytes: number },\n ) {}\n\n has(txid: string): boolean {\n return this.#txs.has(txid);\n }\n\n /** The transaction, now the most recently used, or `undefined`. */\n get(txid: string): PreviousTx | undefined {\n const tx = this.#txs.get(txid);\n if (tx) {\n this.#txs.delete(txid);\n this.#txs.set(txid, tx);\n }\n return tx;\n }\n\n set(tx: PreviousTx): void {\n if (tx.bytes.length > this.bounds.bytes) return;\n const old = this.#txs.get(tx.txid);\n if (old) {\n this.#txs.delete(tx.txid);\n this.#bytes -= old.bytes.length;\n }\n this.#txs.set(tx.txid, tx);\n this.#bytes += tx.bytes.length;\n // The oldest go first (a Map iterates in insertion order).\n for (const [id, entry] of this.#txs) {\n if (this.#txs.size <= this.bounds.entries && this.#bytes <= this.bounds.bytes)\n break;\n this.#txs.delete(id);\n this.#bytes -= entry.bytes.length;\n }\n }\n}\n\n/** One Esplora endpoint family (the `rpc` transport) and its address index (`indexer`). */\nexport class EsploraClient {\n readonly #previous: TxCache;\n readonly #own: TxCache;\n\n constructor(\n private readonly chain: Transport,\n private readonly indexer: Transport,\n private readonly cache: {\n readonly entries: number;\n readonly bytes: number;\n } = PREVIOUS_TX_CACHE,\n ) {\n this.#previous = new TxCache(cache);\n this.#own = new TxCache(cache);\n }\n\n #get<T>(\n transport: Transport,\n path: string,\n route: string,\n tags: UtxoCallTags,\n responseType: 'json' | 'text' | 'bytes' = 'json',\n exactIntegers = false,\n ): Promise<T> {\n const request: HttpRequest = { method: 'GET', path, route, responseType };\n return transport.http<T>(request, exactIntegers ? { ...tags, exactIntegers } : tags);\n }\n\n async #orNull<T>(work: () => Promise<T>): Promise<T | null> {\n try {\n return await work();\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n }\n\n async tipHeight(tags: UtxoCallTags): Promise<bigint> {\n return parseHeight(\n await this.#get(\n this.chain,\n '/blocks/tip/height',\n '/blocks/tip/height',\n tags,\n 'text',\n ),\n );\n }\n\n /** The hash of the block at `height` on this endpoint's best chain, or `null` above its tip. */\n blockHashAt(height: bigint, tags: UtxoCallTags): Promise<string | null> {\n return this.#orNull(async () =>\n parseHash(\n await this.#get(\n this.chain,\n `/block-height/${height}`,\n '/block-height/:height',\n tags,\n 'text',\n ),\n ),\n );\n }\n\n block(blockHash: string, tags: UtxoCallTags): Promise<EsploraBlock | null> {\n return this.#orNull(async () => {\n const block = parseBlock(\n await this.#get(\n this.chain,\n `/block/${segment(blockHash, 'id')}`,\n '/block/:hash',\n tags,\n ),\n );\n // The answer must be the block asked for.\n if (block.hash !== blockHash) throw malformed('block.id');\n return block;\n });\n }\n\n /**\n * `/block/:hash/raw`, the block's own bytes, read strictly once (`rawblock.ts`) and bound to\n * `blockHash`: bytes that do not decode are malformed, and bytes whose header hashes to\n * another block, or whose transactions are not the ones that header commits to, are a\n * retryable `PROVIDER_INCONSISTENT`. So no endpoint can add, drop or alter a transaction of\n * a block whose hash is right. A 404 (a backend that does not have the block) is `null`.\n */\n rawBlock(blockHash: string, tags: UtxoCallTags): Promise<RawBlock | null> {\n return this.#orNull(async () => {\n const answer = await this.#get<unknown>(\n this.chain,\n `/block/${segment(blockHash, 'id')}/raw`,\n '/block/:hash/raw',\n tags,\n 'bytes',\n );\n const block = answer instanceof Uint8Array ? readBlock(answer) : undefined;\n if (!block) throw malformed('raw block');\n if (block.hash !== blockHash) {\n throw new ProviderError(\n 'PROVIDER_INCONSISTENT',\n 'the block bytes do not hash to the block asked for',\n { retryable: true },\n );\n }\n if (!commitsToTransactions(block)) {\n throw new ProviderError(\n 'PROVIDER_INCONSISTENT',\n \"the block's transactions are not the ones its header commits to\",\n { retryable: true },\n );\n }\n return block;\n });\n }\n\n async blockTxids(blockHash: string, tags: UtxoCallTags): Promise<string[]> {\n const ids = await this.#get(\n this.chain,\n `/block/${segment(blockHash, 'id')}/txids`,\n '/block/:hash/txids',\n tags,\n );\n return list(ids, 'txids', (id) => hash(id, 'txid'));\n }\n\n /** Up to 25 transactions of a block from `start` (a multiple of 25). */\n async blockTxs(\n blockHash: string,\n start: number,\n tags: UtxoCallTags,\n ): Promise<EsploraTx[]> {\n const txs = await this.#get(\n this.chain,\n `/block/${segment(blockHash, 'id')}/txs/${start}`,\n '/block/:hash/txs/:start',\n tags,\n );\n return list(txs, 'block txs', parseTx);\n }\n\n tx(txid: string, tags: UtxoCallTags): Promise<EsploraTx | null> {\n return this.#orNull(async () => {\n const tx = parseTx(\n await this.#get(this.chain, `/tx/${segment(txid, 'id')}`, '/tx/:txid', tags),\n );\n // The answer must be the transaction asked for.\n if (tx.txid !== txid) throw malformed('tx.txid');\n return tx;\n });\n }\n\n /**\n * `/tx/:txid/hex`, decoded strictly once and bound to `txid`: bytes that do not decode are\n * malformed, and the bytes of another transaction are a retryable `PROVIDER_INCONSISTENT`,\n * so a garbled answer is never taken for the caller's error. The decoder's cap is the\n * largest transaction a block can hold, never what this library builds. Under\n * a quorum, each endpoint's answer is keyed on the txid its bytes hash to, and the answer\n * the quorum returns is not decoded again.\n */\n async #rawTx<T extends { readonly txid: string }>(\n txid: string,\n tags: UtxoCallTags,\n decode: (hex: string) => T | undefined,\n ): Promise<{ readonly hex: string; readonly tx: T }> {\n const decoded = new Map<string, T | undefined>();\n const read = (answer: unknown): { hex: string; tx: T | undefined } => {\n const hex = typeof answer === 'string' ? answer.trim() : '';\n if (!decoded.has(hex)) {\n decoded.set(hex, hex.length > 0 && isHex(hex) ? decode(hex) : undefined);\n }\n return { hex, tx: decoded.get(hex) };\n };\n const keyed: UtxoCallTags =\n tags.quorum === undefined\n ? tags\n : {\n ...tags,\n // A throwing key counts as a disagreement.\n quorumKey: (answer) => {\n const { tx } = read(answer);\n if (!tx) throw malformed('tx hex');\n return tx.txid;\n },\n };\n const answer = await this.#get(\n this.chain,\n `/tx/${segment(txid, 'id')}/hex`,\n '/tx/:txid/hex',\n keyed,\n 'text',\n );\n const { hex, tx } = read(answer);\n if (!tx) throw malformed('tx hex');\n if (tx.txid !== txid) {\n throw new ProviderError(\n 'PROVIDER_INCONSISTENT',\n 'the transaction bytes do not hash to the id asked for',\n { retryable: true },\n );\n }\n return { hex, tx };\n }\n\n /** The raw transaction, as hex, bound to `txid` (see `#rawTx`). */\n txHex(txid: string, tags: UtxoCallTags): Promise<string | null> {\n return this.#orNull(async () => (await this.#rawTx(txid, tags, txidOf)).hex);\n }\n\n /**\n * An input's previous transaction, decoded once and bound to `txid` (see\n * `#rawTx`). Its bytes authenticate themselves, so it is kept per txid, whatever endpoint\n * served it, in a bounded cache: a replacement's inputs are read once per client.\n */\n async previousTx(txid: string, tags: UtxoCallTags): Promise<PreviousTx | null> {\n const known = this.#own.get(txid) ?? this.#previous.get(txid);\n if (known) return known;\n const found = await this.#orNull(\n async () => (await this.#rawTx(txid, tags, previousTxOf)).tx,\n );\n if (found) this.#previous.set(found);\n return found;\n }\n\n /**\n * The outputs of transaction `txid`, from its bytes, bound to `txid` (see `#rawTx`), or\n * `null` when the endpoint does not have it: what an input naming `txid:vout` spends. A\n * previous transaction this client keeps answers without a read; one read here is not kept,\n * since a block scan reads thousands, which would evict a build's.\n */\n async outputsOf(txid: string, tags: UtxoCallTags): Promise<readonly TxOutput[] | null> {\n const known = this.#own.get(txid) ?? this.#previous.get(txid);\n if (known) return known.outputs;\n return this.#orNull(\n async () => (await this.#rawTx(txid, tags, outputsOfHex)).tx.outputs,\n );\n }\n\n /**\n * A transaction this client sent for an Attempt and a node took. Its bytes are\n * known here, so its outputs are not an indexer's word even before a block holds it, and\n * under `minInputConfirmations: 0` they may be spent unconfirmed. Kept like the cache.\n */\n rememberOwn(tx: PreviousTx): void {\n this.#own.set(tx);\n }\n\n isOwn(txid: string): boolean {\n return this.#own.has(txid);\n }\n\n async outspend(\n txid: string,\n vout: number,\n tags: UtxoCallTags,\n ): Promise<EsploraOutspend> {\n return parseOutspend(\n await this.#get(\n this.chain,\n `/tx/${segment(txid, 'id')}/outspend/${vout}`,\n '/tx/:txid/outspend/:vout',\n tags,\n ),\n );\n }\n\n async feeEstimates(tags: UtxoCallTags): Promise<ReadonlyMap<number, number>> {\n return parseFeeEstimates(\n await this.#get(this.chain, '/fee-estimates', '/fee-estimates', tags),\n );\n }\n\n /** `POST /tx`: the node's txid on success; a node refusal is the transport's `RPC_ERROR`. */\n async broadcast(hex: string, tags: UtxoCallTags): Promise<string> {\n const answer = await this.chain.http<string>(\n { method: 'POST', path: '/tx', route: '/tx', body: hex, responseType: 'text' },\n tags,\n );\n if (typeof answer !== 'string' || !HEX64.test(answer.trim())) {\n // The node answered 2xx, so the bytes may have been accepted.\n throw new ProviderError(\n 'PROVIDER_UNAVAILABLE',\n 'malformed Esplora answer: broadcast txid',\n {\n ambiguous: true,\n },\n );\n }\n return answer.trim();\n }\n\n async addressUtxos(address: string, tags: UtxoCallTags): Promise<EsploraUtxo[]> {\n const utxos = await this.#get(\n this.indexer,\n `/address/${segment(address, 'address')}/utxo`,\n '/address/:address/utxo',\n tags,\n );\n return list(utxos, 'utxo list', parseUtxo);\n }\n\n async addressStats(address: string, tags: UtxoCallTags): Promise<EsploraAddressStats> {\n const info = record(\n await this.#get(\n this.indexer,\n `/address/${segment(address, 'address')}`,\n '/address/:address',\n tags,\n 'json',\n true,\n ),\n 'address',\n );\n const chain = record(info.chain_stats, 'chain_stats');\n const funded = total(chain.funded_txo_sum, 'funded_txo_sum');\n const spent = total(chain.spent_txo_sum, 'spent_txo_sum');\n // The totals are unbounded; the balance is the chain's: 0 to 21 million bitcoin.\n if (spent > funded || funded - spent > MAX_MONEY) throw malformed('address balance');\n return { funded, spent };\n }\n\n /** Confirmed history, newest first, 25 per page, after `lastSeen` when given. */\n async addressTxs(\n address: string,\n lastSeen: string | undefined,\n tags: UtxoCallTags,\n ): Promise<EsploraTx[]> {\n const base = `/address/${segment(address, 'address')}/txs/chain`;\n const path = lastSeen ? `${base}/${segment(lastSeen, 'id')}` : base;\n const route = lastSeen\n ? '/address/:address/txs/chain/:last_seen_txid'\n : '/address/:address/txs/chain';\n return list(await this.#get(this.indexer, path, route, tags), 'address txs', parseTx);\n }\n}\n"]}
@@ -19,12 +19,21 @@
19
19
  * final spend, and an endpoint-set change between the monitor's `slotConsumed` and
20
20
  * `includedFinal` calls cannot produce a false proven `replaced`.
21
21
  *
22
- * The block source reads chain data leniently (the Esplora client's parsers) and binds
23
- * every page to its block: each page has exactly the transactions the block's count
24
- * leaves for it, no txid twice, each confirmed in that block. A reorg while paging, or a
25
- * page the endpoint does not have, decides nothing (retryable). Address filters match
26
- * output and prevout scripts, so a watched address's spend is kept even when no output of
27
- * it has an address.
22
+ * The block source delivers only what authenticated bytes say. A block's transactions are
23
+ * decoded from its own bytes (`rawBlock`), which must hash to the block the scan asked for
24
+ * and commit to every transaction and witness, and the header read at its height must be
25
+ * those bytes' own (parent and time). The outputs a delivered transaction's inputs spend,
26
+ * which give its `from`, input values and fee, are read from the block itself or from each
27
+ * previous transaction's bytes, bound to the txid the input names. So no endpoint can add,
28
+ * drop or alter a transaction, an output or a sender of what is delivered. A filtered scan
29
+ * picks transactions by their outputs (the bytes) and by the inputs the index's pages say
30
+ * spend from a watched address; each page must be the bytes' own, confirmed in that block,
31
+ * and its claims must hold for every transaction delivered, so a lying index can at most
32
+ * hide a watched address's spend that pays no watched address. Which block is at a height
33
+ * stays the endpoint's word, as in every family (the proof quorum confirms rollbacks). A
34
+ * reorg while reading, or a block, page or previous transaction the endpoint does not have,
35
+ * decides nothing (retryable). Address filters match output and prevout scripts, so a
36
+ * watched address's spend is kept even when no output of it has an address.
28
37
  */
29
38
  import type { BlockSource, ProofSource } from '../../core/driver/types';
30
39
  import { type UtxoContext } from './context';