@bsv/sdk 1.0.0 → 1.0.1

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 (123) hide show
  1. package/README.md +25 -3
  2. package/package.json +9 -5
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
  4. package/.github/ISSUE_TEMPLATE/discussion.md +0 -24
  5. package/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +0 -23
  6. package/CHANGELOG.md +0 -72
  7. package/CONTRIBUTING.md +0 -85
  8. package/ROADMAP.md +0 -3
  9. package/docs/getting-started/COMMONJS.md +0 -94
  10. package/docs/getting-started/REACT-TS.md +0 -131
  11. package/docs/getting-started/TS-NODE.md +0 -106
  12. package/docs/getting-started/VUE.md +0 -103
  13. package/jest.config.js +0 -6
  14. package/mod.ts +0 -8
  15. package/src/compat/BSM.ts +0 -51
  16. package/src/compat/ECIES.ts +0 -557
  17. package/src/compat/HD.ts +0 -348
  18. package/src/compat/Mnemonic.ts +0 -295
  19. package/src/compat/__tests/BSM.test.ts +0 -38
  20. package/src/compat/__tests/ECIES.test.ts +0 -90
  21. package/src/compat/__tests/HD.test.ts +0 -405
  22. package/src/compat/__tests/Mnemonic.test.ts +0 -177
  23. package/src/compat/__tests/Mnemonic.vectors.ts +0 -172
  24. package/src/compat/bip-39-wordlist-en.ts +0 -2053
  25. package/src/compat/index.ts +0 -4
  26. package/src/messages/EncryptedMessage.ts +0 -70
  27. package/src/messages/SignedMessage.ts +0 -87
  28. package/src/messages/__tests/EncryptedMessage.test.ts +0 -36
  29. package/src/messages/__tests/SignedMessage.test.ts +0 -53
  30. package/src/messages/index.ts +0 -2
  31. package/src/primitives/AESGCM.ts +0 -479
  32. package/src/primitives/BasePoint.ts +0 -21
  33. package/src/primitives/BigNumber.ts +0 -4619
  34. package/src/primitives/Curve.ts +0 -1163
  35. package/src/primitives/DRBG.ts +0 -102
  36. package/src/primitives/ECDSA.ts +0 -164
  37. package/src/primitives/Hash.ts +0 -1420
  38. package/src/primitives/JacobianPoint.ts +0 -410
  39. package/src/primitives/K256.ts +0 -116
  40. package/src/primitives/Mersenne.ts +0 -123
  41. package/src/primitives/MontgomoryMethod.ts +0 -160
  42. package/src/primitives/Point.ts +0 -852
  43. package/src/primitives/PrivateKey.ts +0 -195
  44. package/src/primitives/PublicKey.ts +0 -154
  45. package/src/primitives/Random.ts +0 -55
  46. package/src/primitives/ReductionContext.ts +0 -528
  47. package/src/primitives/Signature.ts +0 -235
  48. package/src/primitives/SymmetricKey.ts +0 -75
  49. package/src/primitives/TransactionSignature.ts +0 -189
  50. package/src/primitives/__tests/AESGCM.test.ts +0 -338
  51. package/src/primitives/__tests/BRC42.private.vectors.ts +0 -33
  52. package/src/primitives/__tests/BRC42.public.vectors.ts +0 -33
  53. package/src/primitives/__tests/BigNumber.arithmatic.test.ts +0 -572
  54. package/src/primitives/__tests/BigNumber.binary.test.ts +0 -203
  55. package/src/primitives/__tests/BigNumber.constructor.test.ts +0 -176
  56. package/src/primitives/__tests/BigNumber.dhGroup.test.ts +0 -18
  57. package/src/primitives/__tests/BigNumber.fixtures.ts +0 -264
  58. package/src/primitives/__tests/BigNumber.serializers.test.ts +0 -157
  59. package/src/primitives/__tests/BigNumber.utils.test.ts +0 -347
  60. package/src/primitives/__tests/Curve.unit.test.ts +0 -192
  61. package/src/primitives/__tests/DRBG.test.ts +0 -18
  62. package/src/primitives/__tests/DRBG.vectors.ts +0 -167
  63. package/src/primitives/__tests/ECDH.test.ts +0 -31
  64. package/src/primitives/__tests/ECDSA.test.ts +0 -58
  65. package/src/primitives/__tests/HMAC.test.ts +0 -59
  66. package/src/primitives/__tests/Hash.test.ts +0 -121
  67. package/src/primitives/__tests/PBKDF2.vectors.ts +0 -119
  68. package/src/primitives/__tests/PrivateKey.test.ts +0 -17
  69. package/src/primitives/__tests/PublicKey.test.ts +0 -66
  70. package/src/primitives/__tests/Random.test.ts +0 -14
  71. package/src/primitives/__tests/Reader.test.ts +0 -296
  72. package/src/primitives/__tests/ReductionContext.test.ts +0 -279
  73. package/src/primitives/__tests/SymmetricKey.test.ts +0 -58
  74. package/src/primitives/__tests/SymmetricKey.vectors.ts +0 -40
  75. package/src/primitives/__tests/Writer.test.ts +0 -198
  76. package/src/primitives/__tests/sighash.vectors.ts +0 -3503
  77. package/src/primitives/__tests/utils.test.ts +0 -108
  78. package/src/primitives/index.ts +0 -8
  79. package/src/primitives/utils.ts +0 -665
  80. package/src/script/LockingScript.ts +0 -30
  81. package/src/script/OP.ts +0 -219
  82. package/src/script/Script.ts +0 -426
  83. package/src/script/ScriptChunk.ts +0 -7
  84. package/src/script/ScriptTemplate.ts +0 -36
  85. package/src/script/Spend.ts +0 -1379
  86. package/src/script/UnlockingScript.ts +0 -30
  87. package/src/script/__tests/Script.test.ts +0 -369
  88. package/src/script/__tests/Spend.test.ts +0 -248
  89. package/src/script/__tests/script.invalid.vectors.ts +0 -925
  90. package/src/script/__tests/script.valid.vectors.ts +0 -1120
  91. package/src/script/__tests/scriptFromVector.ts +0 -42
  92. package/src/script/__tests/spend.valid.vectors.ts +0 -2288
  93. package/src/script/index.ts +0 -7
  94. package/src/script/templates/P2PKH.ts +0 -109
  95. package/src/script/templates/RPuzzle.ts +0 -140
  96. package/src/script/templates/index.ts +0 -2
  97. package/src/transaction/Broadcaster.ts +0 -42
  98. package/src/transaction/ChainTracker.ts +0 -22
  99. package/src/transaction/FeeModel.ts +0 -13
  100. package/src/transaction/MerklePath.ts +0 -259
  101. package/src/transaction/Transaction.ts +0 -602
  102. package/src/transaction/TransactionInput.ts +0 -63
  103. package/src/transaction/TransactionOutput.ts +0 -37
  104. package/src/transaction/__tests/MerklePath.test.ts +0 -181
  105. package/src/transaction/__tests/Transaction.test.ts +0 -413
  106. package/src/transaction/__tests/bigtx.vectors.ts +0 -4
  107. package/src/transaction/__tests/bump.invalid.vectors.ts +0 -8
  108. package/src/transaction/__tests/bump.valid.vectors.ts +0 -4
  109. package/src/transaction/__tests/tx.invalid.vectors.ts +0 -281
  110. package/src/transaction/__tests/tx.valid.vectors.ts +0 -364
  111. package/src/transaction/broadcasters/ARC.ts +0 -106
  112. package/src/transaction/broadcasters/__tests/ARC.test.ts +0 -115
  113. package/src/transaction/broadcasters/index.ts +0 -1
  114. package/src/transaction/fee-models/SatoshisPerKilobyte.ts +0 -71
  115. package/src/transaction/fee-models/index.ts +0 -1
  116. package/src/transaction/index.ts +0 -6
  117. package/ts2md.json +0 -5
  118. package/tsconfig.base.json +0 -26
  119. package/tsconfig.cjs.json +0 -11
  120. package/tsconfig.eslint.json +0 -12
  121. package/tsconfig.esm.json +0 -9
  122. package/tsconfig.json +0 -17
  123. package/tsconfig.types.json +0 -11
@@ -1,602 +0,0 @@
1
- import TransactionInput from './TransactionInput.js'
2
- import TransactionOutput from './TransactionOutput.js'
3
- import UnlockingScript from '../script/UnlockingScript.js'
4
- import LockingScript from '../script/LockingScript.js'
5
- import { Reader, Writer, toHex, toArray } from '../primitives/utils.js'
6
- import { hash256 } from '../primitives/Hash.js'
7
- import BigNumber from '../primitives/BigNumber.js'
8
- import FeeModel from './FeeModel.js'
9
- import SatoshisPerKilobyte from './fee-models/SatoshisPerKilobyte.js'
10
- import { Broadcaster, BroadcastResponse, BroadcastFailure } from './Broadcaster.js'
11
- import MerklePath from './MerklePath.js'
12
- import Spend from '../script/Spend.js'
13
- import ChainTracker from './ChainTracker.js'
14
-
15
- /**
16
- * Represents a complete Bitcoin transaction. This class encapsulates all the details
17
- * required for creating, signing, and processing a Bitcoin transaction, including
18
- * inputs, outputs, and various transaction-related methods.
19
- *
20
- * @class Transaction
21
- * @property {number} version - The version number of the transaction. Used to specify
22
- * which set of rules this transaction follows.
23
- * @property {TransactionInput[]} inputs - An array of TransactionInput objects, representing
24
- * the inputs for the transaction. Each input references a previous transaction's output.
25
- * @property {TransactionOutput[]} outputs - An array of TransactionOutput objects, representing
26
- * the outputs for the transaction. Each output specifies the amount of satoshis to be
27
- * transferred and the conditions under which they can be spent.
28
- * @property {number} lockTime - The lock time of the transaction. If non-zero, it specifies the
29
- * earliest time or block height at which the transaction can be added to the block chain.
30
- * @property {Record<string, any>} metadata - A key-value store for attaching additional data to
31
- * the transaction object, not included in the transaction itself. Useful for adding descriptions, internal reference numbers, or other information.
32
- * @property {MerkleProof} [merkleProof] - Optional. A merkle proof demonstrating the transaction's
33
- * inclusion in a block. Useful for transaction verification using SPV.
34
- *
35
- * @example
36
- * // Creating a new transaction
37
- * let tx = new Transaction();
38
- * tx.addInput(...);
39
- * tx.addOutput(...);
40
- * await tx.fee();
41
- * await tx.sign();
42
- * await tx.broadcast();
43
- *
44
- * @description
45
- * The Transaction class provides comprehensive
46
- * functionality to handle various aspects of transaction creation, including
47
- * adding inputs and outputs, computing fees, signing the transaction, and
48
- * generating its binary or hexadecimal representation.
49
- */
50
- export default class Transaction {
51
- version: number
52
- inputs: TransactionInput[]
53
- outputs: TransactionOutput[]
54
- lockTime: number
55
- metadata: Record<string, any>
56
- merklePath?: MerklePath
57
-
58
- /**
59
- * Creates a new transaction, linked to its inputs and their associated merkle paths, from a BEEF (BRC-62) structure.
60
- * @param beef A binary representation of a transaction in BEEF format.
61
- * @returns An anchored transaction, linked to its associated inputs populated with merkle paths.
62
- */
63
- static fromBEEF(beef: number[]): Transaction {
64
- const reader = new Reader(beef)
65
- // Read the version
66
- const version = reader.readUInt32LE()
67
- if (version !== 4022206465) {
68
- throw new Error(`Invalid BEEF version. Expected 4022206465, received ${version}.`)
69
- }
70
-
71
- // Read the BUMPs
72
- const numberOfBUMPs = reader.readVarIntNum()
73
- const BUMPs = []
74
- for (let i = 0; i < numberOfBUMPs; i++) {
75
- BUMPs.push(MerklePath.fromReader(reader))
76
- }
77
-
78
- // Read all transactions into an object
79
- // The object has keys of TXIDs and values of objects with transactions and BUMP indexes
80
- const numberOfTransactions = reader.readVarIntNum()
81
- const transactions: Record<string, { pathIndex?: number, tx: Transaction }> = {}
82
- let lastTXID: string
83
- for (let i = 0; i < numberOfTransactions; i++) {
84
- const tx = Transaction.fromReader(reader)
85
- const obj: { pathIndex?: number, tx: Transaction } = { tx }
86
- const txid = tx.id('hex') as string
87
- if (i + 1 === numberOfTransactions) { // The last tXID is stored for later
88
- lastTXID = txid
89
- }
90
- const hasBump = Boolean(reader.readUInt8())
91
- if (hasBump) {
92
- obj.pathIndex = reader.readVarIntNum()
93
- }
94
- transactions[txid] = obj
95
- }
96
-
97
- // Recursive function for adding merkle proofs or input transactions
98
- const addPathOrInputs = (obj: { pathIndex?: number, tx: Transaction }): void => {
99
- if (typeof obj.pathIndex === 'number') {
100
- const path = BUMPs[obj.pathIndex]
101
- if (typeof path !== 'object') {
102
- throw new Error('Invalid merkle path index found in BEEF!')
103
- }
104
- obj.tx.merklePath = path
105
- } else {
106
- for (let i = 0; i < obj.tx.inputs.length; i++) {
107
- const input = obj.tx.inputs[i]
108
- const sourceObj = transactions[input.sourceTXID]
109
- if (typeof sourceObj !== 'object') {
110
- throw new Error(`Reference to unknown TXID in BUMP: ${input.sourceTXID}`)
111
- }
112
- input.sourceTransaction = sourceObj.tx
113
- addPathOrInputs(sourceObj)
114
- }
115
- }
116
- }
117
-
118
- // Read the final transaction and Add inputs and merkle proofs to the final transaction, returning it
119
- addPathOrInputs(transactions[lastTXID])
120
- return transactions[lastTXID].tx
121
- }
122
-
123
- private static fromReader(br: Reader): Transaction {
124
- const version = br.readUInt32LE()
125
- const inputsLength = br.readVarIntNum()
126
- const inputs: TransactionInput[] = []
127
- for (let i = 0; i < inputsLength; i++) {
128
- const sourceTXID = toHex(br.readReverse(32))
129
- const sourceOutputIndex = br.readUInt32LE()
130
- const scriptLength = br.readVarIntNum()
131
- const scriptBin = br.read(scriptLength)
132
- const unlockingScript = UnlockingScript.fromBinary(scriptBin)
133
- const sequence = br.readUInt32LE()
134
- inputs.push({
135
- sourceTXID,
136
- sourceOutputIndex,
137
- unlockingScript,
138
- sequence
139
- })
140
- }
141
- const outputsLength = br.readVarIntNum()
142
- const outputs: TransactionOutput[] = []
143
- for (let i = 0; i < outputsLength; i++) {
144
- const satoshis = br.readUInt64LEBn().toNumber()
145
- const scriptLength = br.readVarIntNum()
146
- const scriptBin = br.read(scriptLength)
147
- const lockingScript = LockingScript.fromBinary(scriptBin)
148
- outputs.push({
149
- satoshis,
150
- lockingScript
151
- })
152
- }
153
- const lockTime = br.readUInt32LE()
154
- return new Transaction(version, inputs, outputs, lockTime)
155
- }
156
-
157
- /**
158
- * Creates a Transaction instance from a binary array.
159
- *
160
- * @static
161
- * @param {number[]} bin - The binary array representation of the transaction.
162
- * @returns {Transaction} - A new Transaction instance.
163
- */
164
- static fromBinary(bin: number[]): Transaction {
165
- const br = new Reader(bin)
166
- return Transaction.fromReader(br)
167
- }
168
-
169
- /**
170
- * Creates a Transaction instance from a hexadecimal string.
171
- *
172
- * @static
173
- * @param {string} hex - The hexadecimal string representation of the transaction.
174
- * @returns {Transaction} - A new Transaction instance.
175
- */
176
- static fromHex(hex: string): Transaction {
177
- return Transaction.fromBinary(toArray(hex, 'hex'))
178
- }
179
-
180
- /**
181
- * Creates a Transaction instance from a hexadecimal string encoded BEEF.
182
- *
183
- * @static
184
- * @param {string} hex - The hexadecimal string representation of the transaction BEEF.
185
- * @returns {Transaction} - A new Transaction instance.
186
- */
187
- static fromHexBEEF(hex: string): Transaction {
188
- return Transaction.fromBEEF(toArray(hex, 'hex'))
189
- }
190
-
191
- constructor(
192
- version: number = 1,
193
- inputs: TransactionInput[] = [],
194
- outputs: TransactionOutput[] = [],
195
- lockTime: number = 0,
196
- metadata: Record<string, any> = {},
197
- merklePath?: MerklePath
198
- ) {
199
- this.version = version
200
- this.inputs = inputs
201
- this.outputs = outputs
202
- this.lockTime = lockTime
203
- this.metadata = metadata
204
- this.merklePath = merklePath
205
- }
206
-
207
- /**
208
- * Adds a new input to the transaction.
209
- *
210
- * @param {TransactionInput} input - The TransactionInput object to add to the transaction.
211
- * @throws {Error} - If the input does not have a sourceTXID or sourceTransaction defined.
212
- */
213
- addInput(input: TransactionInput): void {
214
- if (
215
- typeof input.sourceTXID === 'undefined' &&
216
- typeof input.sourceTransaction === 'undefined'
217
- ) {
218
- throw new Error('A reference to an an input transaction is required. If the input transaction itself cannot be referenced, its TXID must still be provided.')
219
- }
220
- // If the input sequence number hasn't been set, the expectation is that it is final.
221
- if (typeof input.sequence === 'undefined') {
222
- input.sequence = 0xFFFFFFFF
223
- }
224
- this.inputs.push(input)
225
- }
226
-
227
- /**
228
- * Adds a new output to the transaction.
229
- *
230
- * @param {TransactionOutput} output - The TransactionOutput object to add to the transaction.
231
- */
232
- addOutput(output: TransactionOutput): void {
233
- this.outputs.push(output)
234
- }
235
-
236
- /**
237
- * Updates the transaction's metadata.
238
- *
239
- * @param {Record<string, any>} metadata - The metadata object to merge into the existing metadata.
240
- */
241
- updateMetadata(metadata: Record<string, any>): void {
242
- this.metadata = {
243
- ...this.metadata,
244
- ...metadata
245
- }
246
- }
247
-
248
- /**
249
- * Computes fees prior to signing.
250
- * If no fee model is provided, uses a SatoshisPerKilobyte fee model that pays 10 sat/kb.
251
- *
252
- * @param model - The initialized fee model to use
253
- * @param changeDistribution - Specifies how the change should be distributed
254
- * amongst the change outputs
255
- *
256
- * TODO: Benford's law change distribution.
257
- */
258
- async fee(model?: FeeModel, changeDistribution: 'equal' | 'random' = 'equal'): Promise<void> {
259
- if (typeof model === 'undefined') {
260
- model = new SatoshisPerKilobyte(10)
261
- }
262
- const fee = await model.computeFee(this)
263
- // change = inputs - fee - non-change outputs
264
- let change = 0
265
- for (const input of this.inputs) {
266
- if (typeof input.sourceTransaction !== 'object') {
267
- throw new Error('Source transactions are required for all inputs during fee computation')
268
- }
269
- change += input.sourceTransaction.outputs[input.sourceOutputIndex].satoshis
270
- }
271
- change -= fee
272
- let changeCount = 0
273
- for (const out of this.outputs) {
274
- if (!out.change) {
275
- change -= out.satoshis
276
- } else {
277
- changeCount++
278
- }
279
- }
280
-
281
- if (change <= changeCount) {
282
- // There is not enough change to distribute among the change outputs.
283
- // We'll remove all change outputs and leave the extra for the miners.
284
- for (let i = 0; i < this.outputs.length; i++) {
285
- if (this.outputs[i].change) {
286
- this.outputs.splice(i, 1)
287
- i--
288
- }
289
- }
290
- return
291
- }
292
-
293
- // Distribute change among change outputs
294
- if (changeDistribution === 'random') {
295
- // TODO
296
- throw new Error('Not yet implemented')
297
- } else if (changeDistribution === 'equal') {
298
- const perOutput = Math.floor(change / changeCount)
299
- for (const out of this.outputs) {
300
- if (out.change) {
301
- out.satoshis = perOutput
302
- }
303
- }
304
- }
305
- }
306
-
307
- /**
308
- * Signs a transaction, hydrating all its unlocking scripts based on the provided script templates where they are available.
309
- */
310
- async sign(): Promise<void> {
311
- for (const out of this.outputs) {
312
- if (typeof out.satoshis === 'undefined') {
313
- if (out.change) {
314
- throw new Error('There are still change outputs with uncomputed amounts. Use the fee() method to compute the change amounts and transaction fees prior to signing.')
315
- } else {
316
- throw new Error('One or more transaction outputs is missing an amount. Ensure all output amounts are provided before signing.')
317
- }
318
- }
319
- }
320
- for (let i = 0, l = this.inputs.length; i < l; i++) {
321
- if (typeof this.inputs[i].unlockingScriptTemplate === 'object') {
322
- this.inputs[i].unlockingScript = await this.inputs[i]
323
- .unlockingScriptTemplate
324
- .sign(this, i)
325
- }
326
- }
327
- }
328
-
329
- /**
330
- * Broadcasts a transaction.
331
- *
332
- * @param broadcaster The Broadcaster instance wwhere the transaction will be sent
333
- * @returns A BroadcastResponse or BroadcastFailure from the Broadcaster
334
- */
335
- async broadcast(broadcaster: Broadcaster): Promise<BroadcastResponse | BroadcastFailure> {
336
- return await broadcaster.broadcast(this)
337
- }
338
-
339
- /**
340
- * Converts the transaction to a binary array format.
341
- *
342
- * @returns {number[]} - The binary array representation of the transaction.
343
- */
344
- toBinary(): number[] {
345
- const writer = new Writer()
346
- writer.writeUInt32LE(this.version)
347
- writer.writeVarIntNum(this.inputs.length)
348
- for (const i of this.inputs) {
349
- if (typeof i.sourceTransaction !== 'undefined') {
350
- writer.write(i.sourceTransaction.hash() as number[])
351
- } else {
352
- writer.writeReverse(toArray(i.sourceTXID, 'hex'))
353
- }
354
- writer.writeUInt32LE(i.sourceOutputIndex)
355
- const scriptBin = i.unlockingScript.toBinary()
356
- writer.writeVarIntNum(scriptBin.length)
357
- writer.write(scriptBin)
358
- writer.writeUInt32LE(i.sequence)
359
- }
360
- writer.writeVarIntNum(this.outputs.length)
361
- for (const o of this.outputs) {
362
- writer.writeUInt64LE(o.satoshis)
363
- const scriptBin = o.lockingScript.toBinary()
364
- writer.writeVarIntNum(scriptBin.length)
365
- writer.write(scriptBin)
366
- }
367
- writer.writeUInt32LE(this.lockTime)
368
- return writer.toArray()
369
- }
370
-
371
- /**
372
- * Converts the transaction to a BRC-30 EF format.
373
- *
374
- * @returns {number[]} - The BRC-30 EF representation of the transaction.
375
- */
376
- toEF(): number[] {
377
- const writer = new Writer()
378
- writer.writeUInt32LE(this.version)
379
- writer.write([0, 0, 0, 0, 0, 0xef])
380
- writer.writeVarIntNum(this.inputs.length)
381
- for (const i of this.inputs) {
382
- if (typeof i.sourceTransaction === 'undefined') {
383
- throw new Error('All inputs must have source transactions when serializing to EF format')
384
- }
385
- writer.write(i.sourceTransaction.hash() as number[])
386
- writer.writeUInt32LE(i.sourceOutputIndex)
387
- const scriptBin = i.unlockingScript.toBinary()
388
- writer.writeVarIntNum(scriptBin.length)
389
- writer.write(scriptBin)
390
- writer.writeUInt32LE(i.sequence)
391
- writer.writeUInt64LE(i.sourceTransaction.outputs[i.sourceOutputIndex].satoshis)
392
- const lockingScriptBin = i.sourceTransaction.outputs[i.sourceOutputIndex].lockingScript.toBinary()
393
- writer.writeVarIntNum(lockingScriptBin.length)
394
- writer.write(lockingScriptBin)
395
- }
396
- writer.writeVarIntNum(this.outputs.length)
397
- for (const o of this.outputs) {
398
- writer.writeUInt64LE(o.satoshis)
399
- const scriptBin = o.lockingScript.toBinary()
400
- writer.writeVarIntNum(scriptBin.length)
401
- writer.write(scriptBin)
402
- }
403
- writer.writeUInt32LE(this.lockTime)
404
- return writer.toArray()
405
- }
406
-
407
- /**
408
- * Converts the transaction to a hexadecimal string EF.
409
- *
410
- * @returns {string} - The hexadecimal string representation of the transaction EF.
411
- */
412
- toHexEF(): string {
413
- return toHex(this.toEF())
414
- }
415
-
416
- /**
417
- * Converts the transaction to a hexadecimal string format.
418
- *
419
- * @returns {string} - The hexadecimal string representation of the transaction.
420
- */
421
- toHex(): string {
422
- return toHex(this.toBinary())
423
- }
424
-
425
- /**
426
- * Converts the transaction to a hexadecimal string BEEF.
427
- *
428
- * @returns {string} - The hexadecimal string representation of the transaction BEEF.
429
- */
430
- toHexBEEF(): string {
431
- return toHex(this.toBEEF())
432
- }
433
-
434
- /**
435
- * Calculates the transaction's hash.
436
- *
437
- * @param {'hex' | undefined} enc - The encoding to use for the hash. If 'hex', returns a hexadecimal string; otherwise returns a binary array.
438
- * @returns {string | number[]} - The hash of the transaction in the specified format.
439
- */
440
- hash(enc?: 'hex'): number[] | string {
441
- return hash256(this.toBinary(), enc)
442
- }
443
-
444
- /**
445
- * Calculates the transaction's ID.
446
- *
447
- * @param {'hex' | undefined} enc - The encoding to use for the ID. If 'hex', returns a hexadecimal string; otherwise returns a binary array.
448
- * @returns {string | number[]} - The ID of the transaction in the specified format.
449
- */
450
- id(enc?: 'hex'): number[] | string {
451
- const id = hash256(this.toBinary()) as number[]
452
- id.reverse()
453
- if (enc === 'hex') {
454
- return toHex(id)
455
- }
456
- return id
457
- }
458
-
459
- /**
460
- * Verifies the legitimacy of the Bitcoin transaction according to the rules of SPV by ensuring all the input transactions link back to valid block headers, the chain of spends for all inputs are valid, and the sum of inputs is not less than the sum of outputs.
461
- *
462
- * @param chainTracker - An instance of ChainTracker, a Bitcoin block header tracker. If the value is set to 'scripts only', headers will not be verified.
463
- *
464
- * @returns Whether the transaction is valid according to the rules of SPV.
465
- */
466
- async verify(chainTracker: ChainTracker | 'scripts only'): Promise<boolean> {
467
- // If the transaction has a valid merkle path, verification is complete.
468
- if (typeof this.merklePath === 'object' && chainTracker !== 'scripts only') {
469
- const proofValid = await this.merklePath.verify(
470
- this.id('hex') as string,
471
- chainTracker
472
- )
473
- // Note that if the proof is provided but not valid, the transaction could still be verified by proving all inputs (if they are available) and checking the associated spends.
474
- if (proofValid) {
475
- return true
476
- }
477
- }
478
-
479
- // Verify each input transaction and evaluate the spend events.
480
- // Also, keep a total of the input amounts for later.
481
- let inputTotal = 0
482
- for (let i = 0; i < this.inputs.length; i++) {
483
- const input = this.inputs[i]
484
- if (typeof input.sourceTransaction !== 'object') {
485
- throw new Error(`Verification failed because the input at index ${i} of transaction ${this.id('hex') as string} is missing an associated source transaction. This source transaction is required for transaction verification because there is no merkle proof for the transaction spending a UTXO it contains.`)
486
- }
487
- if (typeof input.unlockingScript !== 'object') {
488
- throw new Error(`Verification failed because the input at index ${i} of transaction ${this.id('hex') as string} is missing an associated unlocking script. This script is required for transaction verification because there is no merkle proof for the transaction spending the UTXO.`)
489
- }
490
- const sourceOutput = input.sourceTransaction.outputs[input.sourceOutputIndex]
491
- inputTotal += sourceOutput.satoshis
492
- const inputVerified = await input.sourceTransaction.verify(chainTracker)
493
- if (!inputVerified) {
494
- return false
495
- }
496
- const otherInputs = [...this.inputs]
497
- otherInputs.splice(i, 1)
498
- const spend = new Spend({
499
- sourceTXID: input.sourceTransaction.id('hex') as string,
500
- sourceOutputIndex: input.sourceOutputIndex,
501
- lockingScript: sourceOutput.lockingScript,
502
- sourceSatoshis: sourceOutput.satoshis,
503
- transactionVersion: this.version,
504
- otherInputs,
505
- unlockingScript: input.unlockingScript,
506
- inputSequence: input.sequence,
507
- inputIndex: i,
508
- outputs: this.outputs,
509
- lockTime: this.lockTime
510
- })
511
- const spendValid = spend.validate()
512
- if (!spendValid) {
513
- return false
514
- }
515
- }
516
-
517
- // Total the outputs to ensure they don't amount to more than the inputs
518
- let outputTotal = 0
519
- for (const out of this.outputs) {
520
- if (typeof out.satoshis !== 'number') {
521
- throw new Error('Every output must have a defined amount during transaction verification.')
522
- }
523
- outputTotal += out.satoshis
524
- }
525
-
526
- return outputTotal <= inputTotal
527
- }
528
-
529
- /**
530
- * Serializes this transaction, together with its inputs and the respective merkle proofs, into the BEEF (BRC-62) format. This enables efficient verification of its compliance with the rules of SPV.
531
- *
532
- * @returns The serialized BEEF structure
533
- */
534
- toBEEF(): number[] {
535
- const writer = new Writer()
536
- writer.writeUInt32LE(4022206465)
537
- const BUMPs: MerklePath[] = []
538
- const txs: Array<{ tx: Transaction, pathIndex?: number }> = []
539
-
540
- // Recursive function to add paths and input transactions for a TX
541
- const addPathsAndInputs = (tx: Transaction): void => {
542
- const obj: { tx: Transaction, pathIndex?: number } = { tx }
543
- const hasProof = typeof tx.merklePath === 'object'
544
- if (hasProof) {
545
- let added = false
546
- // If this proof is identical to another one previously added, we use that first. Otherwise, we try to merge it with proofs from the same block.
547
- for (let i = 0; i < BUMPs.length; i++) {
548
- if (BUMPs[i] === tx.merklePath) { // Literally the same
549
- obj.pathIndex = i
550
- added = true
551
- break
552
- }
553
- if (BUMPs[i].blockHeight === tx.merklePath.blockHeight) {
554
- // Probably the same...
555
- const rootA = BUMPs[i].computeRoot()
556
- const rootB = tx.merklePath.computeRoot()
557
- if (rootA === rootB) {
558
- // Definitely the same... combine them to save space
559
- BUMPs[i].combine(tx.merklePath)
560
- obj.pathIndex = i
561
- added = true
562
- break
563
- }
564
- }
565
- }
566
- // Finally, if the proof is not yet added, add a new path.
567
- if (!added) {
568
- obj.pathIndex = BUMPs.length
569
- BUMPs.push(tx.merklePath)
570
- }
571
- }
572
- txs.unshift(obj)
573
- if (!hasProof) {
574
- for (let i = 0; i < tx.inputs.length; i++) {
575
- const input = tx.inputs[i]
576
- if (typeof input.sourceTransaction !== 'object') {
577
- throw new Error('A required source transaction is missing!')
578
- }
579
- addPathsAndInputs(input.sourceTransaction)
580
- }
581
- }
582
- }
583
-
584
- addPathsAndInputs(this)
585
-
586
- writer.writeVarIntNum(BUMPs.length)
587
- for (const b of BUMPs) {
588
- writer.write(b.toBinary())
589
- }
590
- writer.writeVarIntNum(txs.length)
591
- for (const t of txs) {
592
- writer.write(t.tx.toBinary())
593
- if (typeof t.pathIndex === 'number') {
594
- writer.writeUInt8(1)
595
- writer.writeVarIntNum(t.pathIndex)
596
- } else {
597
- writer.writeUInt8(0)
598
- }
599
- }
600
- return writer.toArray()
601
- }
602
- }
@@ -1,63 +0,0 @@
1
- import UnlockingScript from '../script/UnlockingScript.js'
2
- import Transaction from './Transaction.js'
3
-
4
- /**
5
- * Represents an input to a Bitcoin transaction.
6
- * This interface defines the structure and components required to construct
7
- * a transaction input in the Bitcoin blockchain.
8
- *
9
- * @interface TransactionInput
10
- * @property {Transaction} [sourceTransaction] - Optional. The source transaction
11
- * from which this input is derived. This is the transaction whose output
12
- * is being spent by this input. Preferably provided if available.
13
- * @property {string} [sourceTXID] - Optional. The transaction ID (TXID) of the source
14
- * transaction. Required if the source transaction itself is not provided.
15
- * This uniquely identifies the transaction within the blockchain.
16
- * @property {number} sourceOutputIndex - The index of the output in the source transaction
17
- * that this input is spending. It is zero-based, indicating the position of the
18
- * output in the array of outputs of the source transaction.
19
- * @property {UnlockingScript} [unlockingScript] - Optional. The script that 'unlocks' the
20
- * source output for spending. This script typically contains signatures and
21
- * public keys that evidence the ownership of the output.
22
- * @property {Object} [unlockingScriptTemplate] - Optional. A template for generating the
23
- * unlocking script. Useful when the unlocking script needs to be generated
24
- * dynamically.
25
- * @property {Function} unlockingScriptTemplate.sign - A function that, when given the
26
- * current transaction and the index of this input, returns a Promise that
27
- * resolves to the UnlockingScript.
28
- * @property {Function} unlockingScriptTemplate.estimateLength - A function that estimates
29
- * the length of the unlocking script, given the transaction and the input index.
30
- * @property {number} sequence - A sequence number for the input. Used to enable
31
- * updates to this input. If set to a non-final value (less than 0xFFFFFFFF),
32
- * it indicates that the input may be replaced in the future.
33
- *
34
- * @example
35
- * // Creating a simple transaction input
36
- * let txInput = {
37
- * sourceTXID: '123abc...',
38
- * sourceOutputIndex: 0,
39
- * sequence: 0xFFFFFFFF
40
- * };
41
- *
42
- * // Using an unlocking script template
43
- * txInput.unlockingScriptTemplate = {
44
- * sign: async (tx, index) => { ... },
45
- * estimateLength: async (tx, index) => { ... }
46
- * };
47
- *
48
- * @description
49
- * This interface links an input to a
50
- * previous output and provides mechanisms (through unlocking scripts) to authorize the
51
- * spending of that output.
52
- */
53
- export default interface TransactionInput {
54
- sourceTransaction?: Transaction
55
- sourceTXID?: string
56
- sourceOutputIndex: number
57
- unlockingScript?: UnlockingScript
58
- unlockingScriptTemplate?: {
59
- sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>
60
- estimateLength: (tx: Transaction, inputIndex: number) => Promise<number>
61
- }
62
- sequence: number
63
- }
@@ -1,37 +0,0 @@
1
- import BigNumber from '../primitives/BigNumber.js'
2
- import LockingScript from '../script/LockingScript.js'
3
-
4
- /**
5
- * Represents an output in a Bitcoin transaction.
6
- * This interface defines the structure and components necessary to construct
7
- * a transaction output, which secures owned Bitcoins to be unlocked later.
8
- *
9
- * @interface TransactionOutput
10
- * @property {number} [satoshis] - Optional. The amount of satoshis (the smallest unit of Bitcoin) to be transferred by this output.
11
- * @property {LockingScript} lockingScript - The script that 'locks' the satoshis,
12
- * specifying the conditions under which they can be spent. This script is
13
- * essential for securing the funds and typically contains cryptographic
14
- * puzzles that need to be solved to spend the output.
15
- * @property {boolean} [change] - Optional. A flag that indicates whether this output
16
- * is a change output. If true, it means this output is returning funds back
17
- * to the sender, usually as the 'change' from the transaction inputs.
18
- *
19
- * @example
20
- * // Creating a simple transaction output
21
- * let txOutput = {
22
- * satoshis: 1000,
23
- * lockingScript: LockingScript.fromASM('OP_DUP OP_HASH160 ... OP_EQUALVERIFY OP_CHECKSIG'),
24
- * change: false
25
- * };
26
- *
27
- * @description
28
- * The TransactionOutput interface defines how bitcoins are to be distributed in a transaction, either to a recipient or
29
- * back to the sender as change. The lockingScript is critical as it determines the conditions
30
- * under which the output can be spent, typically requiring a digital signature matching the
31
- * intended recipient's public key.
32
- */
33
- export default interface TransactionOutput {
34
- satoshis?: number
35
- lockingScript: LockingScript
36
- change?: boolean
37
- }