@bsv/sdk 1.6.8 → 1.6.10

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 (92) hide show
  1. package/README.md +9 -4
  2. package/dist/cjs/package.json +7 -5
  3. package/dist/cjs/src/wallet/substrates/HTTPWalletJSON.js +11 -3
  4. package/dist/cjs/src/wallet/substrates/HTTPWalletJSON.js.map +1 -1
  5. package/dist/cjs/src/wallet/substrates/WalletWireProcessor.js +1 -1
  6. package/dist/cjs/src/wallet/substrates/WalletWireProcessor.js.map +1 -1
  7. package/dist/cjs/src/wallet/substrates/utils/toOriginHeader.js +21 -0
  8. package/dist/cjs/src/wallet/substrates/utils/toOriginHeader.js.map +1 -0
  9. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  10. package/dist/esm/src/wallet/substrates/HTTPWalletJSON.js +9 -1
  11. package/dist/esm/src/wallet/substrates/HTTPWalletJSON.js.map +1 -1
  12. package/dist/esm/src/wallet/substrates/WalletWireProcessor.js +1 -1
  13. package/dist/esm/src/wallet/substrates/WalletWireProcessor.js.map +1 -1
  14. package/dist/esm/src/wallet/substrates/utils/toOriginHeader.js +17 -0
  15. package/dist/esm/src/wallet/substrates/utils/toOriginHeader.js.map +1 -0
  16. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  17. package/dist/types/src/wallet/substrates/HTTPWalletJSON.d.ts.map +1 -1
  18. package/dist/types/src/wallet/substrates/utils/toOriginHeader.d.ts +2 -0
  19. package/dist/types/src/wallet/substrates/utils/toOriginHeader.d.ts.map +1 -0
  20. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  21. package/dist/umd/bundle.js +1 -1
  22. package/docs/concepts/beef.md +84 -0
  23. package/docs/concepts/chain-tracking.md +122 -0
  24. package/docs/concepts/decentralized-identity.md +184 -0
  25. package/docs/concepts/fees.md +217 -0
  26. package/docs/concepts/identity-certificates.md +255 -0
  27. package/docs/concepts/index.md +62 -0
  28. package/docs/concepts/key-management.md +176 -0
  29. package/docs/concepts/script-templates.md +163 -0
  30. package/docs/concepts/sdk-philosophy.md +72 -0
  31. package/docs/concepts/signatures.md +179 -0
  32. package/docs/concepts/spv-verification.md +106 -0
  33. package/docs/concepts/transaction-encoding.md +148 -0
  34. package/docs/concepts/transaction-structure.md +63 -0
  35. package/docs/concepts/trust-model.md +123 -0
  36. package/docs/concepts/verification.md +219 -0
  37. package/docs/concepts/wallet-integration.md +95 -0
  38. package/docs/guides/direct-transaction-creation.md +137 -0
  39. package/docs/guides/http-client-configuration.md +414 -0
  40. package/docs/guides/index.md +30 -0
  41. package/docs/guides/transaction-signing-methods.md +268 -0
  42. package/docs/index.md +74 -0
  43. package/docs/reference/arc-config.md +698 -0
  44. package/docs/reference/brc-100.md +33 -0
  45. package/docs/reference/configuration.md +829 -0
  46. package/docs/reference/debugging.md +700 -0
  47. package/docs/reference/errors.md +547 -0
  48. package/docs/reference/index.md +98 -0
  49. package/docs/reference/network-config.md +914 -0
  50. package/docs/reference/op-codes.md +306 -0
  51. package/docs/reference/transaction-signatures.md +94 -0
  52. package/docs/{wallet.md → reference/wallet.md} +9 -0
  53. package/docs/requirements.txt +3 -0
  54. package/docs/tutorials/advanced-transaction.md +575 -0
  55. package/docs/tutorials/aes-encryption.md +947 -0
  56. package/docs/tutorials/authfetch-tutorial.md +957 -0
  57. package/docs/tutorials/ecdh-key-exchange.md +547 -0
  58. package/docs/tutorials/elliptic-curve-fundamentals.md +603 -0
  59. package/docs/tutorials/error-handling.md +1215 -0
  60. package/docs/tutorials/first-transaction-low-level.md +204 -0
  61. package/docs/tutorials/first-transaction.md +278 -0
  62. package/docs/tutorials/hashes-and-hmacs.md +814 -0
  63. package/docs/tutorials/identity-management.md +702 -0
  64. package/docs/tutorials/index.md +182 -0
  65. package/docs/tutorials/key-management.md +536 -0
  66. package/docs/tutorials/protowallet-development.md +716 -0
  67. package/docs/tutorials/script-construction.md +690 -0
  68. package/docs/tutorials/spv-merkle-proofs.md +682 -0
  69. package/docs/tutorials/testnet-transactions-low-level.md +352 -0
  70. package/docs/tutorials/transaction-broadcasting.md +535 -0
  71. package/docs/tutorials/transaction-types.md +419 -0
  72. package/docs/tutorials/type-42.md +582 -0
  73. package/docs/tutorials/uhrp-storage.md +579 -0
  74. package/package.json +7 -5
  75. package/src/transaction/__tests/Transaction.test.ts +1 -1
  76. package/src/wallet/substrates/HTTPWalletJSON.ts +11 -1
  77. package/src/wallet/substrates/WalletWireProcessor.ts +1 -1
  78. package/src/wallet/substrates/__tests/toOriginHeader.test.ts +34 -0
  79. package/src/wallet/substrates/utils/toOriginHeader.ts +15 -0
  80. package/docs/README.md +0 -21
  81. /package/docs/{auth.md → reference/auth.md} +0 -0
  82. /package/docs/{compat.md → reference/compat.md} +0 -0
  83. /package/docs/{identity.md → reference/identity.md} +0 -0
  84. /package/docs/{kvstore.md → reference/kvstore.md} +0 -0
  85. /package/docs/{messages.md → reference/messages.md} +0 -0
  86. /package/docs/{overlay-tools.md → reference/overlay-tools.md} +0 -0
  87. /package/docs/{primitives.md → reference/primitives.md} +0 -0
  88. /package/docs/{registry.md → reference/registry.md} +0 -0
  89. /package/docs/{script.md → reference/script.md} +0 -0
  90. /package/docs/{storage.md → reference/storage.md} +0 -0
  91. /package/docs/{totp.md → reference/totp.md} +0 -0
  92. /package/docs/{transaction.md → reference/transaction.md} +0 -0
@@ -0,0 +1,682 @@
1
+ # SPV and Merkle Proof Verification
2
+
3
+ ## Introduction
4
+
5
+ Simplified Payment Verification (SPV) is a method for verifying Bitcoin transactions without downloading the entire blockchain. Instead of storing all transaction data, SPV clients only need block headers and merkle proofs to verify that specific transactions are included in the blockchain.
6
+
7
+ This tutorial covers:
8
+ - Understanding SPV principles and merkle trees
9
+ - Working with merkle proofs using the `MerklePath` class
10
+ - Verifying transactions with the `Transaction.verify()` method
11
+ - Implementing custom chain trackers for block header verification
12
+ - Working with BEEF (BRC-62) structures for efficient SPV
13
+
14
+ > **📚 Related Concepts**: Review [SPV Verification](../concepts/spv-verification.md), [Transaction Verification](../concepts/verification.md), and [BEEF Format](../concepts/beef.md) for foundational understanding.
15
+
16
+ ## Prerequisites
17
+
18
+ - Completed "Your First BSV Transaction" tutorial
19
+ - Basic understanding of Bitcoin transaction structure
20
+ - Familiarity with cryptographic hash functions
21
+
22
+ ## Understanding SPV and Merkle Trees
23
+
24
+ ### What is SPV?
25
+
26
+ SPV allows lightweight clients to verify transactions without storing the full blockchain by:
27
+
28
+ 1. **Block Headers Only**: Store only block headers (80 bytes each) instead of full blocks
29
+ 2. **Merkle Proofs**: Use cryptographic proofs to verify transaction inclusion
30
+ 3. **Chain Validation**: Verify the proof-of-work chain of block headers
31
+ 4. **Script Validation**: Validate that transaction scripts are properly formed
32
+
33
+ ### Merkle Trees in Bitcoin
34
+
35
+ Bitcoin blocks organize transactions in a binary merkle tree structure:
36
+
37
+ ```
38
+ Merkle Root
39
+ / \
40
+ Hash AB Hash CD
41
+ / \ / \
42
+ Hash A Hash B Hash C Hash D
43
+ | | | |
44
+ Tx A Tx B Tx C Tx D
45
+ ```
46
+
47
+ A merkle proof provides the minimum hashes needed to compute the merkle root from a specific transaction.
48
+
49
+ ## Working with MerklePath
50
+
51
+ The `MerklePath` class represents a merkle proof for a specific transaction:
52
+
53
+ ```typescript
54
+ import { MerklePath, WhatsOnChain } from '@bsv/sdk'
55
+
56
+ async function runMerkleExample() {
57
+ // Create the merkle path first (our demonstration example)
58
+ const blockHeight = 850000
59
+ const merklePath = new MerklePath(blockHeight, [
60
+ [
61
+ { offset: 0, hash: 'ffeff11c25cde7c06d407490d81ef4d0db64aad6ab3d14393530701561a465ef', txid: true },
62
+ { offset: 1, hash: 'b9ef07a62553ef8b0898a79c291b92c60f7932260888bde0dab2dd2610d8668e' }
63
+ ]
64
+ ])
65
+
66
+ // Example tx
67
+ const txid = 'ffeff11c25cde7c06d407490d81ef4d0db64aad6ab3d14393530701561a465ef'
68
+
69
+ // Create a chain tracker for mainnet
70
+ const chainTracker = new WhatsOnChain('main')
71
+
72
+ // Verify the merkle proof
73
+ const isValid = await merklePath.verify(txid, chainTracker)
74
+ console.log('Merkle proof valid:', isValid)
75
+
76
+ // Note: This will return false because our example merkle path
77
+ // doesn't correspond to a real block on the BSV mainnet
78
+ }
79
+
80
+ runMerkleExample().catch(console.error)
81
+ ```
82
+
83
+ ## Working with Real Blockchain Data
84
+
85
+ The example above demonstrates the fundamental concepts using a simplified 2-transaction block. In real BSV blockchain scenarios, blocks contain hundreds or thousands of transactions, creating much deeper merkle trees.
86
+
87
+ **Our Working Example:**
88
+ - **Block Height**: 850000 (arbitrary example height)
89
+ - **Transaction ID**: `ffeff11c25cde7c06d407490d81ef4d0db64aad6ab3d14393530701561a465ef` (from BSV Technical Standards)
90
+ - **Sibling Hash**: `b9ef07a62553ef8b0898a79c291b92c60f7932260888bde0dab2dd2610d8668e` (from BSV Technical Standards)
91
+ - **Computed Merkle Root**: `6f0a2a566d54512576b3b32eb3a8ca5273d8f35d8bfba02123bb7aad59be1e61`
92
+
93
+ **Real-World Complexity:**
94
+ In actual BSV blocks, a transaction at index 12 (like in the BSV Technical Standards example) would require a merkle path with multiple levels:
95
+ - **5 proof levels** for a block with ~32 transactions
96
+ - **10 proof levels** for a block with ~1024 transactions
97
+ - **20 proof levels** for a block with ~1 million transactions
98
+
99
+ The logarithmic nature of merkle trees means even massive blocks require relatively few proof hashes for verification.
100
+
101
+ ## Real Blockchain Merkle Paths
102
+
103
+ In practice, merkle paths from real BSV blocks are more complex, with multiple levels representing the tree structure. The example above shows the simplest case - a block with only 2 transactions.
104
+
105
+ **Real BSV Blockchain Example:**
106
+ Based on data from the [BSV Technical Standards](https://tsc.bsvblockchain.org/standards/merkle-proof-standardised-format/), a transaction at index 12 in a larger block would have a merkle path with these hash values:
107
+
108
+ - **Transaction**: `ffeff11c25cde7c06d407490d81ef4d0db64aad6ab3d14393530701561a465ef`
109
+ - **Proof nodes**: 5 hash values that form the path to the merkle root
110
+ - **Merkle root**: `75edb0a69eb195cdd81e310553aa4d25e18450e08f168532a2c2e9cf447bf169`
111
+
112
+ The SDK handles the complex offset calculations automatically when parsing from binary formats or BEEF structures.
113
+
114
+ ## Serialization Formats
115
+
116
+ The BSV TypeScript SDK uses an internal object format for MerklePath construction, while the [BSV Technical Standards](https://tsc.bsvblockchain.org/standards/merkle-proof-standardised-format/) define a binary serialization format. The SDK handles the conversion between these formats internally.
117
+
118
+ **Internal Format (used above):**
119
+ - Array of levels, each containing leaf objects with `offset`, `hash`, `txid`, and `duplicate` properties
120
+ - Direct construction allows for clear understanding of the merkle tree structure
121
+
122
+ **Binary Format (from standards):**
123
+ - Compact binary representation for network transmission and storage
124
+ - Can be parsed using `MerklePath.fromHex()` when properly formatted
125
+
126
+ ## Computing Merkle Roots
127
+
128
+ You can compute the merkle root for a given transaction ID:
129
+
130
+ ```typescript
131
+ // Compute merkle root for a specific transaction
132
+ const txid = 'ffeff11c25cde7c06d407490d81ef4d0db64aad6ab3d14393530701561a465ef'
133
+ const merkleRoot = merklePath.computeRoot(txid)
134
+
135
+ console.log('Computed merkle root:', merkleRoot)
136
+
137
+ // How the merkle root is computed for our 2-transaction block:
138
+ // 1. Take our transaction: ffeff11c25cde7c06d407490d81ef4d0db64aad6ab3d14393530701561a465ef
139
+ // 2. Take sibling transaction: b9ef07a62553ef8b0898a79c291b92c60f7932260888bde0dab2dd2610d8668e
140
+ // 3. Hash them together: SHA256(SHA256(txid + sibling))
141
+ // 4. Result is the merkle root: 6f0a2a566d54512576b3b32eb3a8ca5273d8f35d8bfba02123bb7aad59be1e61
142
+
143
+ console.log('Expected result: 6f0a2a566d54512576b3b32eb3a8ca5273d8f35d8bfba02123bb7aad59be1e61')
144
+ console.log('Merkle root matches expected:', merkleRoot === '6f0a2a566d54512576b3b32eb3a8ca5273d8f35d8bfba02123bb7aad59be1e61')
145
+ ```
146
+
147
+ ## Understanding Merkle Tree Computation
148
+
149
+ The merkle root computation follows a specific mathematical process:
150
+
151
+ **For a 2-transaction block (our example):**
152
+ ```
153
+ Level 0 (Leaves): [Transaction A] [Transaction B]
154
+ | |
155
+ Level 1 (Root): [Hash(A + B)]
156
+ ```
157
+
158
+ **Step-by-step process:**
159
+ 1. **Start with transaction IDs** (already hashed)
160
+ 2. **Concatenate them**: `ffeff11c...465ef` + `b9ef07a6...8668e`
161
+ 3. **Double SHA256**: `SHA256(SHA256(concatenated_data))`
162
+ 4. **Result**: The merkle root that represents the entire block
163
+
164
+ **For larger blocks (e.g., 4 transactions):**
165
+ ```
166
+ Level 0: [Tx A] [Tx B] [Tx C] [Tx D]
167
+ | | | |
168
+ Level 1: [Hash(A+B)] [Hash(C+D)]
169
+ | |
170
+ Level 2: [Hash(AB + CD)]
171
+ ```
172
+
173
+ This tree structure allows you to prove any transaction's inclusion with only `log₂(n)` hashes, making verification extremely efficient even for blocks with millions of transactions.
174
+
175
+ ## Verifying Merkle Proofs
176
+
177
+ Verify that a transaction is included in a block using a chain tracker:
178
+
179
+ ```typescript
180
+ import { MerklePath, WhatsOnChain } from '@bsv/sdk'
181
+
182
+ async function runMerkleVerificationExample() {
183
+ // Create a simple merkle path for demonstration
184
+ // This is a 2-transaction block example from BSV Technical Standards
185
+ const blockHeight = 850000
186
+ const merklePath = new MerklePath(blockHeight, [
187
+ [
188
+ { offset: 0, hash: 'ffeff11c25cde7c06d407490d81ef4d0db64aad6ab3d14393530701561a465ef', txid: true },
189
+ { offset: 1, hash: 'b9ef07a62553ef8b0898a79c291b92c60f7932260888bde0dab2dd2610d8668e' }
190
+ ]
191
+ ])
192
+
193
+ // Example transaction ID
194
+ const txid = 'ffeff11c25cde7c06d407490d81ef4d0db64aad6ab3d14393530701561a465ef'
195
+
196
+ // Create a chain tracker for mainnet
197
+ const chainTracker = new WhatsOnChain('main')
198
+
199
+ try {
200
+ // Compute the merkle root (this works with our demonstration data)
201
+ const merkleRoot = merklePath.computeRoot(txid)
202
+ console.log('Computed merkle root:', merkleRoot)
203
+
204
+ // Verify the merkle proof (this will return false for our demo data)
205
+ const isValid = await merklePath.verify(txid, chainTracker)
206
+ console.log('Merkle proof valid:', isValid)
207
+
208
+ // Note: This returns false because our example uses demonstration data
209
+ // rather than real blockchain merkle proof data
210
+ if (!isValid) {
211
+ console.log('ℹ️ This is expected - our example uses synthetic data for learning purposes')
212
+ console.log(' Real applications receive merkle paths from BEEF structures or blockchain services')
213
+ }
214
+ } catch (error) {
215
+ console.error('Error verifying merkle proof:', error)
216
+ }
217
+ }
218
+
219
+ // Run the example
220
+ runMerkleVerificationExample().catch(console.error)
221
+ ```
222
+
223
+ ## Chain Trackers
224
+
225
+ Chain trackers verify that merkle roots are valid for specific block heights. The SDK provides the `WhatsOnChain` implementation:
226
+
227
+ ### Using WhatsOnChain Chain Tracker
228
+
229
+ ```typescript
230
+ import { WhatsOnChain } from '@bsv/sdk'
231
+
232
+ async function runChainTrackerExample() {
233
+ // Mainnet chain tracker
234
+ const mainnetTracker = new WhatsOnChain('main')
235
+
236
+ // Testnet chain tracker
237
+ const testnetTracker = new WhatsOnChain('test', {
238
+ apiKey: 'your-api-key' // Optional for higher rate limits
239
+ })
240
+
241
+ try {
242
+ // Check current blockchain height
243
+ const currentHeight = await mainnetTracker.currentHeight()
244
+ console.log('Current block height:', currentHeight)
245
+
246
+ // Verify a merkle root for a specific height
247
+ const isValidRoot = await mainnetTracker.isValidRootForHeight(
248
+ 'merkle-root-hex',
249
+ 850000
250
+ )
251
+ console.log('Valid merkle root:', isValidRoot)
252
+ console.log('NOTE - this is expected to be false, as our example uses demonstration data')
253
+ } catch (error) {
254
+ console.error('Chain tracker error:', error)
255
+ }
256
+ }
257
+
258
+ // Run the example
259
+ runChainTrackerExample().catch(console.error)
260
+ ```
261
+
262
+ ## Transaction Verification with SPV
263
+
264
+ The `Transaction.verify()` method performs complete SPV verification:
265
+
266
+ ### Basic Transaction Verification
267
+
268
+ ```typescript
269
+ import { Transaction, WhatsOnChain, SatoshisPerKilobyte } from '@bsv/sdk'
270
+
271
+ async function runTransactionVerificationExample() {
272
+ // Create transaction from BEEF data
273
+ const beefHex = 'your-beef-hex-data'
274
+ const transaction = Transaction.fromHexBEEF(beefHex)
275
+
276
+ // Set up chain tracker and fee model
277
+ const chainTracker = new WhatsOnChain('main')
278
+ const feeModel = new SatoshisPerKilobyte(1)
279
+
280
+ // Verify the transaction
281
+ try {
282
+ const isValid = await transaction.verify(chainTracker, feeModel)
283
+ console.log('Transaction valid:', isValid)
284
+ console.log('NOTE - this is expected to be false, as our example uses demonstration data')
285
+ } catch (error) {
286
+ console.error('Verification failed:', error.message)
287
+ }
288
+ }
289
+
290
+ // Run the example
291
+ runTransactionVerificationExample().catch(console.error)
292
+ ```
293
+
294
+ ### Scripts-Only Verification
295
+
296
+ ```typescript
297
+ async function runScriptsOnlyVerificationExample() {
298
+ // Assuming you have a transaction from previous example
299
+ const beefHex = 'your-beef-hex-data'
300
+ const transaction = Transaction.fromHexBEEF(beefHex)
301
+
302
+ try {
303
+ // Verify only scripts without checking block headers
304
+ const isScriptValid = await transaction.verify('scripts only')
305
+ console.log('Scripts valid:', isScriptValid)
306
+ } catch (error) {
307
+ console.error('Script verification failed:', error.message)
308
+ }
309
+ }
310
+
311
+ // Run the example
312
+ runScriptsOnlyVerificationExample().catch(console.error)
313
+ ```
314
+
315
+ ### Working with BEEF Structures
316
+
317
+ BEEF (BRC-62) provides an efficient format for SPV data:
318
+
319
+ ### Creating BEEF from Transaction
320
+
321
+ ```typescript
322
+ // Create a transaction with inputs and merkle proofs
323
+ const tx = new Transaction()
324
+ // ... add inputs and outputs ...
325
+
326
+ // Convert to BEEF format
327
+ const beefData = tx.toBEEF()
328
+ const beefHex = Buffer.from(beefData).toString('hex')
329
+
330
+ console.log('BEEF hex:', beefHex)
331
+ ```
332
+
333
+ ### Parsing BEEF Data
334
+
335
+ ```typescript
336
+ // Parse BEEF structure
337
+ const transaction = Transaction.fromHexBEEF(beefHex)
338
+
339
+ console.log('Transaction ID:', Buffer.from(transaction.id()).toString('hex'))
340
+ console.log('Input count:', transaction.inputs.length)
341
+ console.log('Output count:', transaction.outputs.length)
342
+
343
+ // Check if merkle paths are included
344
+ transaction.inputs.forEach((input, index) => {
345
+ if (input.sourceTransaction?.merklePath) {
346
+ console.log(`Input ${index} has merkle proof at height:`,
347
+ input.sourceTransaction.merklePath.blockHeight)
348
+ }
349
+ })
350
+ ```
351
+
352
+ ## Practical Example: Payment Verification
353
+
354
+ Let's create a complete example that verifies a payment transaction:
355
+
356
+ ```typescript
357
+ import {
358
+ Transaction,
359
+ WhatsOnChain,
360
+ SatoshisPerKilobyte,
361
+ PrivateKey,
362
+ P2PKH
363
+ } from '@bsv/sdk'
364
+
365
+ async function verifyPayment(beefHex: string): Promise<boolean> {
366
+ try {
367
+ // Parse the BEEF transaction
368
+ const transaction = Transaction.fromHexBEEF(beefHex)
369
+
370
+ // Set up verification components
371
+ const chainTracker = new WhatsOnChain('main')
372
+
373
+ // Perform SPV verification
374
+ const isValid = await transaction.verify(chainTracker)
375
+
376
+ if (isValid) {
377
+ console.log('✅ Payment verified successfully!')
378
+
379
+ // Extract payment details
380
+ const txid = Buffer.from(transaction.id()).toString('hex')
381
+ console.log('Transaction ID:', txid)
382
+
383
+ // Check outputs for payment amounts
384
+ transaction.outputs.forEach((output, index) => {
385
+ console.log(`Output ${index}: ${output.satoshis} satoshis`)
386
+
387
+ // Check if it's a P2PKH output (OP_DUP OP_HASH160 <20-byte-hash> OP_EQUALVERIFY OP_CHECKSIG)
388
+ try {
389
+ const script = output.lockingScript
390
+ const chunks = script.chunks
391
+ if (chunks.length === 5 &&
392
+ chunks[0].op === 118 && // OP_DUP
393
+ chunks[1].op === 169 && // OP_HASH160
394
+ chunks[2].data && chunks[2].data.length === 20 &&
395
+ chunks[3].op === 136 && // OP_EQUALVERIFY
396
+ chunks[4].op === 172) { // OP_CHECKSIG
397
+ const pubKeyHash = Buffer.from(chunks[2].data).toString('hex')
398
+ console.log(` → P2PKH address hash: ${pubKeyHash}`)
399
+ } else {
400
+ console.log(` → Custom script output`)
401
+ }
402
+ } catch {
403
+ console.log(` → Custom script output`)
404
+ }
405
+ })
406
+
407
+ return true
408
+ } else {
409
+ console.log('❌ Payment verification failed')
410
+ return false
411
+ }
412
+ } catch (error) {
413
+ console.error('Verification error:', (error as Error).message)
414
+ return false
415
+ }
416
+ }
417
+
418
+ // Example usage
419
+ const exampleBEEF = '0100beef01fe636d0c0007021400fe507c0c7aa754cef1f7889d5fd395cf1f785dd7de98eed895dbedfe4e5bc70d1502ac4e164f5bc16746bb0868404292ac8318bbac3800e4aad13a014da427adce3e010b00bc4ff395efd11719b277694cface5aa50d085a0bb81f613f70313acd28cf4557010400574b2d9142b8d28b61d88e3b2c3f44d858411356b49a28a4643b6d1a6a092a5201030051a05fc84d531b5d250c23f4f886f6812f9fe3f402d61607f977b4ecd2701c19010000fd781529d58fc2523cf396a7f25440b409857e7e221766c57214b1d38c7b481f01010062f542f45ea3660f86c013ced80534cb5fd4c19d66c56e7e8c5d4bf2d40acc5e010100b121e91836fd7cd5102b654e9f72f3cf6fdbfd0b161c53a9c54b12c841126331020100000001cd4e4cac3c7b56920d1e7655e7e260d31f29d9a388d04910f1bbd72304a79029010000006b483045022100e75279a205a547c445719420aa3138bf14743e3f42618e5f86a19bde14bb95f7022064777d34776b05d816daf1699493fcdf2ef5a5ab1ad710d9c97bfb5b8f7cef3641210263e2dee22b1ddc5e11f6fab8bcd2378bdd19580d640501ea956ec0e786f93e76ffffffff013e660000000000001976a9146bfd5c7fbe21529d45803dbcf0c87dd3c71efbc288ac0000000001000100000001ac4e164f5bc16746bb0868404292ac8318bbac3800e4aad13a014da427adce3e000000006a47304402203a61a2e931612b4bda08d541cfb980885173b8dcf64a3471238ae7abcd368d6402204cbf24f04b9aa2256d8901f0ed97866603d2be8324c2bfb7a37bf8fc90edd5b441210263e2dee22b1ddc5e11f6fab8bcd2378bdd19580d640501ea956ec0e786f93e76ffffffff013c660000000000001976a9146bfd5c7fbe21529d45803dbcf0c87dd3c71efbc288ac0000000000'
420
+
421
+ async function runPaymentVerificationExample() {
422
+ console.log('=== Payment Verification Example ===\n')
423
+
424
+ // Verify the payment
425
+ const isValid = await verifyPayment(exampleBEEF)
426
+
427
+ if (isValid) {
428
+ console.log('\n✅ Payment successfully verified using SPV!')
429
+ console.log('This transaction can be trusted without downloading the full blockchain.')
430
+ } else {
431
+ console.log('\n❌ Payment verification failed!')
432
+ console.log('This transaction should not be trusted.')
433
+ }
434
+
435
+ console.log('\n=== Payment Processing Workflow ===')
436
+ console.log('1. Customer sends BEEF-encoded transaction')
437
+ console.log('2. Merchant verifies transaction using SPV')
438
+ console.log('3. If valid, merchant can safely accept payment')
439
+ console.log('4. No need to wait for confirmations or run full node')
440
+ }
441
+
442
+ runPaymentVerificationExample().catch(console.error)
443
+ ```
444
+
445
+ ## Advanced SPV Patterns
446
+
447
+ ### Batch Verification
448
+
449
+ Verify multiple transactions efficiently:
450
+
451
+ ```typescript
452
+ async function verifyMultipleTransactions(beefHexArray: string[]): Promise<boolean[]> {
453
+ const chainTracker = new WhatsOnChain('main')
454
+ const feeModel = new SatoshisPerKilobyte(1)
455
+
456
+ const results = await Promise.all(
457
+ beefHexArray.map(async (beefHex) => {
458
+ try {
459
+ const tx = Transaction.fromHexBEEF(beefHex)
460
+ return await tx.verify(chainTracker, feeModel)
461
+ } catch (error) {
462
+ console.error('Verification failed:', error.message)
463
+ return false
464
+ }
465
+ })
466
+ )
467
+
468
+ return results
469
+ }
470
+ ```
471
+
472
+ ### Merkle Proof Validation
473
+
474
+ Manually validate merkle proofs:
475
+
476
+ ```typescript
477
+ function validateMerkleProof(
478
+ txid: string,
479
+ merklePath: MerklePath,
480
+ expectedRoot: string
481
+ ): boolean {
482
+ try {
483
+ const computedRoot = merklePath.computeRoot(txid)
484
+ return computedRoot === expectedRoot
485
+ } catch (error) {
486
+ console.error('Error computing merkle root:', error.message)
487
+ return false
488
+ }
489
+ }
490
+
491
+ // Example usage
492
+ const isValidProof = validateMerkleProof(
493
+ 'transaction-id',
494
+ merklePath,
495
+ 'expected-merkle-root'
496
+ )
497
+ console.log('Merkle proof valid:', isValidProof)
498
+ ```
499
+
500
+ ### Custom Memory Limits
501
+
502
+ Control script execution memory usage:
503
+
504
+ ```typescript
505
+ // Verify with custom memory limit (in bytes)
506
+ const isValid = await transaction.verify(
507
+ chainTracker,
508
+ feeModel,
509
+ 1024 * 1024 // 1MB memory limit
510
+ )
511
+ ```
512
+
513
+ ## Error Handling and Debugging
514
+
515
+ ### Common Verification Errors
516
+
517
+ ```typescript
518
+ async function robustVerification(beefHex: string): Promise<void> {
519
+ try {
520
+ const transaction = Transaction.fromHexBEEF(beefHex)
521
+
522
+ // Set up verification components
523
+ const chainTracker = new WhatsOnChain('main')
524
+
525
+ console.log('🔍 Starting transaction verification...')
526
+ console.log('Transaction ID:', Buffer.from(transaction.id()).toString('hex'))
527
+
528
+ // Perform SPV verification
529
+ const isValid = await transaction.verify(chainTracker)
530
+
531
+ if (!isValid) {
532
+ console.log('❌ Transaction verification failed. Checking components...')
533
+
534
+ // Check individual merkle proofs
535
+ for (let i = 0; i < transaction.inputs.length; i++) {
536
+ const input = transaction.inputs[i]
537
+ if (input.sourceTransaction?.merklePath) {
538
+ try {
539
+ const sourceTxid = Buffer.from(input.sourceTransaction.id()).toString('hex')
540
+ const proofValid = await input.sourceTransaction.merklePath.verify(
541
+ sourceTxid,
542
+ chainTracker
543
+ )
544
+ console.log(` Input ${i} merkle proof: ${proofValid ? '✅' : '❌'}`)
545
+ } catch (err) {
546
+ console.log(` Input ${i} merkle proof: ❌ (${(err as Error).message})`)
547
+ }
548
+ } else {
549
+ console.log(` Input ${i}: No merkle path provided`)
550
+ }
551
+ }
552
+
553
+ // Try scripts-only verification
554
+ try {
555
+ const scriptsValid = await transaction.verify('scripts only')
556
+ console.log(' Scripts validation:', scriptsValid ? '✅' : '❌')
557
+ } catch (err) {
558
+ console.log(' Scripts validation: ❌ (', (err as Error).message, ')')
559
+ }
560
+ } else {
561
+ console.log('✅ Transaction verification successful!')
562
+ }
563
+
564
+ } catch (error) {
565
+ const errorMessage = (error as Error).message
566
+
567
+ console.log('❌ Verification failed with error:')
568
+
569
+ if (errorMessage.includes('Missing source transaction')) {
570
+ console.error(' → BEEF structure incomplete - missing input transactions')
571
+ console.error(' → Solution: Ensure all input transactions are included in BEEF')
572
+ } else if (errorMessage.includes('Merkle root')) {
573
+ console.error(' → Merkle proof verification failed')
574
+ console.error(' → Solution: Check merkle path data and chain tracker connectivity')
575
+ } else if (errorMessage.includes('script')) {
576
+ console.error(' → Script validation failed')
577
+ console.error(' → Solution: Check unlocking scripts and signature validity')
578
+ } else if (errorMessage.includes('BEEF')) {
579
+ console.error(' → BEEF parsing error')
580
+ console.error(' → Solution: Verify BEEF format and encoding')
581
+ } else {
582
+ console.error(' → Unexpected error:', errorMessage)
583
+ }
584
+ }
585
+ }
586
+ ```
587
+
588
+ ### Network-Specific Verification
589
+
590
+ ```typescript
591
+ async function verifyOnNetwork(beefHex: string, network: 'main' | 'test'): Promise<boolean> {
592
+ const chainTracker = new WhatsOnChain(network)
593
+ const transaction = Transaction.fromHexBEEF(beefHex)
594
+
595
+ console.log(`Verifying on ${network}net...`)
596
+ return await transaction.verify(chainTracker)
597
+ }
598
+ ```
599
+
600
+ ## Best Practices
601
+
602
+ ### 1. Chain Tracker Selection
603
+
604
+ ```typescript
605
+ // Production: Use WhatsOnChain with API key
606
+ const productionTracker = new WhatsOnChain('main', {
607
+ apiKey: process.env.WHATSONCHAIN_API_KEY
608
+ })
609
+
610
+ // Development: Use testnet
611
+ const devTracker = new WhatsOnChain('test')
612
+
613
+ // Testing: Use mock tracker
614
+ const testTracker: ChainTracker = {
615
+ async isValidRootForHeight() { return true },
616
+ async currentHeight() { return 850000 }
617
+ }
618
+ ```
619
+
620
+ ### 2. Error Recovery
621
+
622
+ ```typescript
623
+ async function verifyWithRetry(beefHex: string, maxRetries = 3): Promise<boolean> {
624
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
625
+ try {
626
+ const transaction = Transaction.fromHexBEEF(beefHex)
627
+ const chainTracker = new WhatsOnChain('main')
628
+
629
+ return await transaction.verify(chainTracker)
630
+ } catch (error) {
631
+ console.log(`Attempt ${attempt} failed:`, (error as Error).message)
632
+
633
+ if (attempt === maxRetries) {
634
+ throw error
635
+ }
636
+
637
+ // Wait before retry
638
+ await new Promise(resolve => setTimeout(resolve, 1000 * attempt))
639
+ }
640
+ }
641
+
642
+ return false
643
+ }
644
+ ```
645
+
646
+ ## Summary
647
+
648
+ SPV and merkle proof verification enable lightweight Bitcoin clients to verify transactions without storing the full blockchain. The BSV TypeScript SDK provides comprehensive tools for:
649
+
650
+ - **MerklePath**: Computing and verifying merkle proofs
651
+ - **ChainTracker**: Validating merkle roots against block headers
652
+ - **Transaction.verify()**: Complete SPV verification
653
+ - **BEEF**: Efficient SPV data structures
654
+
655
+ Key takeaways:
656
+ - SPV trades storage for bandwidth and computation
657
+ - Merkle proofs provide cryptographic inclusion proofs
658
+ - Chain trackers ensure merkle roots are valid
659
+ - BEEF structures optimize SPV data transmission
660
+ - Always use trusted chain trackers in production
661
+ - Implement proper error handling and retry logic
662
+
663
+ This foundation enables building lightweight Bitcoin applications that can verify payments and transactions without running a full node.
664
+
665
+ Understanding of `WalletClient` usage is also important for building robust applications. `WalletClient` provides high-level transaction verification, but understanding SPV verification gives you the ability to build lightweight applications that can verify transactions without downloading the entire blockchain.
666
+
667
+ ## SPV vs Full Node vs `WalletClient` Verification
668
+
669
+ | Method | `WalletClient` | SPV | Full Node |
670
+ | --- | --- | --- | --- |
671
+ | **Storage** | High | Low | High |
672
+ | **Bandwidth** | Low | High | Low |
673
+ | **Verification** | High-level | Low-level | High-level |
674
+ | **Security** | High | High | High |
675
+
676
+ The `WalletClient` approach is recommended for most applications, while SPV verification is valuable for specialized lightweight applications.
677
+
678
+ ```typescript
679
+ // Example usage
680
+ const walletClient = new WalletClient('main')
681
+ const isValid = await walletClient.verifyTransaction(beefHex)
682
+ console.log('Transaction valid:', isValid)