@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.
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/src/compat/Utxo.js +50 -0
- package/dist/cjs/src/compat/Utxo.js.map +1 -0
- package/dist/cjs/src/compat/index.js +3 -1
- package/dist/cjs/src/compat/index.js.map +1 -1
- package/dist/cjs/src/script/templates/P2PKH.js +0 -1
- package/dist/cjs/src/script/templates/P2PKH.js.map +1 -1
- package/dist/cjs/src/transaction/Transaction.js +73 -10
- package/dist/cjs/src/transaction/Transaction.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/compat/Utxo.js +44 -0
- package/dist/esm/src/compat/Utxo.js.map +1 -0
- package/dist/esm/src/compat/index.js +1 -0
- package/dist/esm/src/compat/index.js.map +1 -1
- package/dist/esm/src/script/templates/P2PKH.js +0 -1
- package/dist/esm/src/script/templates/P2PKH.js.map +1 -1
- package/dist/esm/src/transaction/Transaction.js +73 -10
- package/dist/esm/src/transaction/Transaction.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/compat/Utxo.d.ts +42 -0
- package/dist/types/src/compat/Utxo.d.ts.map +1 -0
- package/dist/types/src/compat/index.d.ts +1 -0
- package/dist/types/src/compat/index.d.ts.map +1 -1
- package/dist/types/src/script/templates/P2PKH.d.ts.map +1 -1
- package/dist/types/src/transaction/Transaction.d.ts +14 -0
- package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
- package/dist/types/src/transaction/TransactionInput.d.ts +0 -4
- package/dist/types/src/transaction/TransactionInput.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/docs/compat.md +52 -10
- package/docs/transaction.md +36 -1
- package/package.json +1 -1
- package/src/compat/Utxo.ts +55 -0
- package/src/compat/index.ts +1 -0
- package/src/script/templates/P2PKH.ts +0 -1
- package/src/transaction/Transaction.ts +74 -10
- package/src/transaction/TransactionInput.ts +0 -4
- 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
|
+
}
|
package/src/compat/index.ts
CHANGED
|
@@ -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'
|
|
320
|
-
throw new Error('Source transactions
|
|
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'
|
|
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
|
-
|
|
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
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
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()),
|