@bsv/sdk 1.0.39 → 1.1.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 (38) hide show
  1. package/dist/cjs/package.json +1 -1
  2. package/dist/cjs/src/compat/Utxo.js +50 -0
  3. package/dist/cjs/src/compat/Utxo.js.map +1 -0
  4. package/dist/cjs/src/compat/index.js +3 -1
  5. package/dist/cjs/src/compat/index.js.map +1 -1
  6. package/dist/cjs/src/script/templates/P2PKH.js +0 -1
  7. package/dist/cjs/src/script/templates/P2PKH.js.map +1 -1
  8. package/dist/cjs/src/transaction/Transaction.js +73 -10
  9. package/dist/cjs/src/transaction/Transaction.js.map +1 -1
  10. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  11. package/dist/esm/src/compat/Utxo.js +44 -0
  12. package/dist/esm/src/compat/Utxo.js.map +1 -0
  13. package/dist/esm/src/compat/index.js +1 -0
  14. package/dist/esm/src/compat/index.js.map +1 -1
  15. package/dist/esm/src/script/templates/P2PKH.js +0 -1
  16. package/dist/esm/src/script/templates/P2PKH.js.map +1 -1
  17. package/dist/esm/src/transaction/Transaction.js +73 -10
  18. package/dist/esm/src/transaction/Transaction.js.map +1 -1
  19. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  20. package/dist/types/src/compat/Utxo.d.ts +42 -0
  21. package/dist/types/src/compat/Utxo.d.ts.map +1 -0
  22. package/dist/types/src/compat/index.d.ts +1 -0
  23. package/dist/types/src/compat/index.d.ts.map +1 -1
  24. package/dist/types/src/script/templates/P2PKH.d.ts.map +1 -1
  25. package/dist/types/src/transaction/Transaction.d.ts +14 -0
  26. package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
  27. package/dist/types/src/transaction/TransactionInput.d.ts +0 -4
  28. package/dist/types/src/transaction/TransactionInput.d.ts.map +1 -1
  29. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  30. package/docs/compat.md +52 -10
  31. package/docs/transaction.md +36 -1
  32. package/package.json +1 -1
  33. package/src/compat/Utxo.ts +55 -0
  34. package/src/compat/index.ts +1 -0
  35. package/src/script/templates/P2PKH.ts +0 -1
  36. package/src/transaction/Transaction.ts +74 -10
  37. package/src/transaction/TransactionInput.ts +0 -4
  38. package/src/transaction/__tests/Transaction.test.ts +8 -8
@@ -0,0 +1,55 @@
1
+ import Transaction from '../transaction/Transaction.js'
2
+ import TransactionInput from '../transaction/TransactionInput.js'
3
+ import LockingScript from '../script/LockingScript.js'
4
+ import UnlockingScript from '../script/UnlockingScript.js'
5
+
6
+ type jsonUtxo = {
7
+ txid: string
8
+ vout: number
9
+ satoshis: number
10
+ script: string
11
+ }
12
+ /**
13
+ * @method fromUtxo
14
+ *
15
+ * @description
16
+ * This function creates a transaction input from a utxo json object
17
+ * The idea being old code that uses utxos rather than sourceTranactions can convert using this.
18
+ *
19
+ * @deprecated
20
+ * This approach is made available for compatibility only. It is deprecated in favor of using sourceTransactions
21
+ * directly. It's recommended that wallets general keep transactions which store unspent outputs in their entirety,
22
+ * along with corresonding Merkle paths. The reason you would keep the whole transaction is such that you can prove
23
+ * the txid, and therefore its inclusion within a specific block.
24
+ *
25
+ * @example
26
+ * const i = fromUtxo({
27
+ * txid: '434555433eaca96dff6e71a4d02febd0dd3832e5ca4e5734623ca914522e17d5',
28
+ * vout: 0,
29
+ * script: '51',
30
+ * satoshis: 1234
31
+ * }, new P2PKH().unlock(p))
32
+ *
33
+ * tx.addInput(i)
34
+ *
35
+ * @param utxo: jsonUtxo
36
+ * @param unlockingScriptTemplate: { sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>, estimateLength: (tx: Transaction, inputIndex: number) => Promise<number> }
37
+ * @returns
38
+ */
39
+ export default function fromUtxo(utxo: jsonUtxo, unlockingScriptTemplate: {
40
+ sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>
41
+ estimateLength: (tx: Transaction, inputIndex: number) => Promise<number>
42
+ }): TransactionInput {
43
+ const sourceTransaction = new Transaction(0, [], [], 0)
44
+ sourceTransaction.outputs = Array(utxo.vout + 1).fill(null)
45
+ sourceTransaction.outputs[utxo.vout] = {
46
+ satoshis: utxo.satoshis,
47
+ lockingScript: LockingScript.fromHex(utxo.script)
48
+ }
49
+ return {
50
+ sourceTransaction,
51
+ sourceOutputIndex: utxo.vout,
52
+ unlockingScriptTemplate,
53
+ sequence: 0xFFFFFFFF
54
+ }
55
+ }
@@ -2,3 +2,4 @@ export * as BSM from './BSM.js'
2
2
  export { default as HD } from './HD.js'
3
3
  export { default as Mnemonic } from './Mnemonic.js'
4
4
  export { default as ECIES } from './ECIES.js'
5
+ export { default as fromUtxo } from './Utxo.js'
@@ -91,7 +91,6 @@ export default class P2PKH implements ScriptTemplate {
91
91
  )
92
92
  }
93
93
  sourceSatoshis ||= input.sourceTransaction?.outputs[input.sourceOutputIndex].satoshis
94
- sourceSatoshis ||= input.sourceSatoshis
95
94
  if (!sourceSatoshis) {
96
95
  throw new Error(
97
96
  'The sourceSatoshis or input sourceTransaction is required for transaction signing.'
@@ -122,6 +122,59 @@ export default class Transaction {
122
122
  return transactions[lastTXID].tx
123
123
  }
124
124
 
125
+
126
+ /**
127
+ * Creates a new transaction, linked to its inputs and their associated merkle paths, from a EF (BRC-30) structure.
128
+ * @param ef A binary representation of a transaction in EF format.
129
+ * @returns An extended transaction, linked to its associated inputs by locking script and satoshis amounts only.
130
+ */
131
+ static fromEF (ef: number[]): Transaction {
132
+ const br = new Reader(ef)
133
+ const version = br.readUInt32LE()
134
+ if (toHex(br.read(6)) !== '0000000000ef') throw new Error('Invalid EF marker')
135
+ const inputsLength = br.readVarIntNum()
136
+ const inputs: TransactionInput[] = []
137
+ for (let i = 0; i < inputsLength; i++) {
138
+ const sourceTXID = toHex(br.readReverse(32))
139
+ const sourceOutputIndex = br.readUInt32LE()
140
+ const scriptLength = br.readVarIntNum()
141
+ const scriptBin = br.read(scriptLength)
142
+ const unlockingScript = UnlockingScript.fromBinary(scriptBin)
143
+ const sequence = br.readUInt32LE()
144
+ const satoshis = br.readUInt64LEBn().toNumber()
145
+ const lockingScriptLength = br.readVarIntNum()
146
+ const lockingScriptBin = br.read(lockingScriptLength)
147
+ const lockingScript = LockingScript.fromBinary(lockingScriptBin)
148
+ const sourceTransaction = new Transaction(null, [], [], null)
149
+ sourceTransaction.outputs = Array(sourceOutputIndex + 1).fill(null)
150
+ sourceTransaction.outputs[sourceOutputIndex] = {
151
+ satoshis,
152
+ lockingScript
153
+ }
154
+ inputs.push({
155
+ sourceTransaction,
156
+ sourceTXID,
157
+ sourceOutputIndex,
158
+ unlockingScript,
159
+ sequence
160
+ })
161
+ }
162
+ const outputsLength = br.readVarIntNum()
163
+ const outputs: TransactionOutput[] = []
164
+ for (let i = 0; i < outputsLength; i++) {
165
+ const satoshis = br.readUInt64LEBn().toNumber()
166
+ const scriptLength = br.readVarIntNum()
167
+ const scriptBin = br.read(scriptLength)
168
+ const lockingScript = LockingScript.fromBinary(scriptBin)
169
+ outputs.push({
170
+ satoshis,
171
+ lockingScript
172
+ })
173
+ }
174
+ const lockTime = br.readUInt32LE()
175
+ return new Transaction(version, inputs, outputs, lockTime)
176
+ }
177
+
125
178
  /**
126
179
  * Since the validation of blockchain data is atomically transaction data validation,
127
180
  * any application seeking to validate data in output scripts must store the entire transaction as well.
@@ -220,6 +273,17 @@ export default class Transaction {
220
273
  return Transaction.fromBinary(toArray(hex, 'hex'))
221
274
  }
222
275
 
276
+ /**
277
+ * Creates a Transaction instance from a hexadecimal string encoded EF.
278
+ *
279
+ * @static
280
+ * @param {string} hex - The hexadecimal string representation of the transaction EF.
281
+ * @returns {Transaction} - A new Transaction instance.
282
+ */
283
+ static fromHexEF (hex: string): Transaction {
284
+ return Transaction.fromEF(toArray(hex, 'hex'))
285
+ }
286
+
223
287
  /**
224
288
  * Creates a Transaction instance from a hexadecimal string encoded BEEF.
225
289
  *
@@ -316,12 +380,10 @@ export default class Transaction {
316
380
  // change = inputs - fee - non-change outputs
317
381
  let change = 0
318
382
  for (const input of this.inputs) {
319
- if (typeof input.sourceTransaction !== 'object' && typeof input.sourceSatoshis !== 'number') {
320
- throw new Error('Source transactions or sourceSatoshis are required for all inputs during fee computation')
383
+ if (typeof input.sourceTransaction !== 'object') {
384
+ throw new Error('Source transactions are required for all inputs during fee computation')
321
385
  }
322
- change += input.sourceTransaction
323
- ? input.sourceTransaction.outputs[input.sourceOutputIndex].satoshis
324
- : input.sourceSatoshis
386
+ change += input.sourceTransaction.outputs[input.sourceOutputIndex].satoshis
325
387
  }
326
388
  change -= fee
327
389
  let changeCount = 0
@@ -367,12 +429,10 @@ export default class Transaction {
367
429
  getFee (): number {
368
430
  let totalIn = 0
369
431
  for (const input of this.inputs) {
370
- if (typeof input.sourceTransaction !== 'object' && typeof input.sourceSatoshis !== 'number') {
432
+ if (typeof input.sourceTransaction !== 'object') {
371
433
  throw new Error('Source transactions or sourceSatoshis are required for all inputs to calculate fee')
372
434
  }
373
- totalIn += input.sourceTransaction
374
- ? input.sourceTransaction.outputs[input.sourceOutputIndex].satoshis
375
- : input.sourceSatoshis || 0
435
+ totalIn += input.sourceTransaction.outputs[input.sourceOutputIndex].satoshis
376
436
  }
377
437
  let totalOut = 0
378
438
  for (const output of this.outputs) {
@@ -465,7 +525,11 @@ export default class Transaction {
465
525
  if (typeof i.sourceTransaction === 'undefined') {
466
526
  throw new Error('All inputs must have source transactions when serializing to EF format')
467
527
  }
468
- writer.write(i.sourceTransaction.hash() as number[])
528
+ if (typeof i.sourceTXID === 'undefined') {
529
+ writer.write(i.sourceTransaction.hash() as number[])
530
+ } else {
531
+ writer.write(toArray(i.sourceTXID, 'hex').reverse() as number[])
532
+ }
469
533
  writer.writeUInt32LE(i.sourceOutputIndex)
470
534
  const scriptBin = i.unlockingScript.toBinary()
471
535
  writer.writeVarIntNum(scriptBin.length)
@@ -16,9 +16,6 @@ import Transaction from './Transaction.js'
16
16
  * @property {number} sourceOutputIndex - The index of the output in the source transaction
17
17
  * that this input is spending. It is zero-based, indicating the position of the
18
18
  * output in the array of outputs of the source transaction.
19
- * @property {number} [sourceSatoshis] - The amount of satoshis of the source transaction
20
- * output that this input is spending, used for fee calculation and signing when
21
- * source transaction is not present
22
19
  * @property {UnlockingScript} [unlockingScript] - Optional. The script that 'unlocks' the
23
20
  * source output for spending. This script typically contains signatures and
24
21
  * public keys that evidence the ownership of the output.
@@ -57,7 +54,6 @@ export default interface TransactionInput {
57
54
  sourceTransaction?: Transaction
58
55
  sourceTXID?: string
59
56
  sourceOutputIndex: number
60
- sourceSatoshis?: number
61
57
  unlockingScript?: UnlockingScript
62
58
  unlockingScriptTemplate?: {
63
59
  sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>
@@ -9,6 +9,7 @@ import { hash256, hash160 } from '../../../dist/cjs/src/primitives/Hash'
9
9
  import PrivateKey from '../../../dist/cjs/src/primitives/PrivateKey'
10
10
  import Curve from '../../../dist/cjs/src/primitives/Curve'
11
11
  import P2PKH from '../../../dist/cjs/src/script/templates/P2PKH'
12
+ import fromUtxo from '../../../dist/cjs/src/compat/Utxo'
12
13
 
13
14
  import sighashVectors from '../../primitives/__tests/sighash.vectors'
14
15
  import invalidTransactions from './tx.invalid.vectors'
@@ -396,14 +397,13 @@ describe('Transaction', () => {
396
397
  const priv = PrivateKey.fromRandom()
397
398
  const tx = new Transaction()
398
399
  utxos.forEach(utxo => {
399
- const script = new P2PKH().lock(priv.toPublicKey().toHash())
400
- tx.addInput({
401
- sourceTXID: utxo.tx_hash,
402
- sourceOutputIndex: utxo.tx_pos,
403
- sourceSatoshis: utxo.value,
404
- unlockingScriptTemplate: new P2PKH()
405
- .unlock(priv, 'all', false, utxo.value, script)
406
- })
400
+ const u = {
401
+ txid: utxo.tx_hash,
402
+ vout: utxo.tx_pos,
403
+ script: new P2PKH().lock(priv.toPublicKey().toHash()).toHex(),
404
+ satoshis: utxo.value
405
+ }
406
+ tx.addInput(fromUtxo(u, new P2PKH().unlock(priv)))
407
407
  })
408
408
  tx.addOutput({
409
409
  lockingScript: new P2PKH().lock(priv.toAddress()),