@bsv/sdk 1.2.14 → 1.2.17
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 +2 -2
- package/dist/cjs/src/script/templates/P2PKH.js +2 -0
- package/dist/cjs/src/script/templates/P2PKH.js.map +1 -1
- package/dist/cjs/src/transaction/Beef.js +41 -15
- package/dist/cjs/src/transaction/Beef.js.map +1 -1
- package/dist/cjs/src/transaction/MerklePath.js +4 -4
- package/dist/cjs/src/transaction/MerklePath.js.map +1 -1
- package/dist/cjs/src/transaction/Transaction.js +26 -0
- package/dist/cjs/src/transaction/Transaction.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/script/templates/P2PKH.js +2 -0
- package/dist/esm/src/script/templates/P2PKH.js.map +1 -1
- package/dist/esm/src/transaction/Beef.js +41 -15
- package/dist/esm/src/transaction/Beef.js.map +1 -1
- package/dist/esm/src/transaction/MerklePath.js +4 -4
- package/dist/esm/src/transaction/MerklePath.js.map +1 -1
- package/dist/esm/src/transaction/Transaction.js +26 -0
- package/dist/esm/src/transaction/Transaction.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/script/templates/P2PKH.d.ts.map +1 -1
- package/dist/types/src/transaction/Beef.d.ts +8 -2
- package/dist/types/src/transaction/Beef.d.ts.map +1 -1
- package/dist/types/src/transaction/MerklePath.d.ts +2 -2
- package/dist/types/src/transaction/MerklePath.d.ts.map +1 -1
- package/dist/types/src/transaction/Transaction.d.ts +8 -0
- package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +1 -1
- package/docs/transaction.md +31 -4
- package/docs/wallet-substrates.md +1410 -1
- package/package.json +2 -2
- package/src/script/templates/P2PKH.ts +1 -0
- package/src/transaction/Beef.ts +49 -8
- package/src/transaction/MerklePath.ts +4 -4
- package/src/transaction/Transaction.ts +22 -0
- package/src/transaction/__tests/Beef.test.ts +24 -5
- package/src/transaction/__tests/Transaction.benchmarks.test.ts +2 -2
- package/src/transaction/__tests/Transaction.test.ts +44 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bsv/sdk",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.17",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "BSV Blockchain Software Development Kit",
|
|
6
6
|
"main": "dist/cjs/mod.js",
|
|
@@ -188,7 +188,7 @@
|
|
|
188
188
|
"build:umd": "webpack --config webpack.config.js",
|
|
189
189
|
"dev": "tsc -b -w",
|
|
190
190
|
"prepublish": "npm run build",
|
|
191
|
-
"doc": "ts2md --inputFilename=src/script/index.ts --outputFilename=docs/script.md --filenameSubString=script --firstHeadingLevel=1 && ts2md --inputFilename=src/primitives/index.ts --outputFilename=docs/primitives.md --filenameSubString=primitives --firstHeadingLevel=1 && ts2md --inputFilename=src/transaction/index.ts --outputFilename=docs/transaction.md --filenameSubString=transaction --firstHeadingLevel=1 && ts2md --inputFilename=src/messages/index.ts --outputFilename=docs/messages.md --filenameSubString=messages --firstHeadingLevel=1 && ts2md --inputFilename=src/compat/index.ts --outputFilename=docs/compat.md --filenameSubString=compat --firstHeadingLevel=1 && ts2md --inputFilename=src/wallet/index.ts --outputFilename=docs/wallet.md --filenameSubString=wallet --firstHeadingLevel=1 && ts2md --inputFilename=src/totp/index.ts --outputFilename=docs/totp.md --filenameSubString=totp --firstHeadingLevel=1 && ts2md --inputFilename=src/overlay-tools/index.ts --outputFilename=docs/overlay-tools.md --filenameSubString=overlay-tools --firstHeadingLevel=1"
|
|
191
|
+
"doc": "ts2md --inputFilename=src/script/index.ts --outputFilename=docs/script.md --filenameSubString=script --firstHeadingLevel=1 && ts2md --inputFilename=src/primitives/index.ts --outputFilename=docs/primitives.md --filenameSubString=primitives --firstHeadingLevel=1 && ts2md --inputFilename=src/transaction/index.ts --outputFilename=docs/transaction.md --filenameSubString=transaction --firstHeadingLevel=1 && ts2md --inputFilename=src/messages/index.ts --outputFilename=docs/messages.md --filenameSubString=messages --firstHeadingLevel=1 && ts2md --inputFilename=src/compat/index.ts --outputFilename=docs/compat.md --filenameSubString=compat --firstHeadingLevel=1 && ts2md --inputFilename=src/wallet/index.ts --outputFilename=docs/wallet.md --filenameSubString=wallet --firstHeadingLevel=1 && ts2md --inputFilename=src/wallet/substrates/index.ts --outputFilename=docs/wallet-substrates.md --filenameSubString=wallet/substrates --firstHeadingLevel=1 && ts2md --inputFilename=src/totp/index.ts --outputFilename=docs/totp.md --filenameSubString=totp --firstHeadingLevel=1 && ts2md --inputFilename=src/overlay-tools/index.ts --outputFilename=docs/overlay-tools.md --filenameSubString=overlay-tools --firstHeadingLevel=1"
|
|
192
192
|
},
|
|
193
193
|
"repository": {
|
|
194
194
|
"type": "git",
|
package/src/transaction/Beef.ts
CHANGED
|
@@ -3,6 +3,7 @@ import Transaction from './Transaction.js'
|
|
|
3
3
|
import ChainTracker from './ChainTracker.js'
|
|
4
4
|
import BeefTx from './BeefTx.js'
|
|
5
5
|
import { Reader, Writer, toHex, toArray } from '../primitives/utils.js'
|
|
6
|
+
import { hash256 } from '../primitives/Hash.js'
|
|
6
7
|
|
|
7
8
|
export const BEEF_MAGIC = 4022206465 // 0100BEEF in LE order
|
|
8
9
|
export const BEEF_MAGIC_V2 = 4022206466 // 0200BEEF in LE order
|
|
@@ -308,9 +309,12 @@ export class Beef {
|
|
|
308
309
|
|
|
309
310
|
mergeBeefTx (btx: BeefTx): BeefTx {
|
|
310
311
|
let beefTx = this.findTxid(btx.txid)
|
|
311
|
-
if (
|
|
312
|
-
|
|
313
|
-
|
|
312
|
+
if (btx.isTxidOnly && !beefTx)
|
|
313
|
+
beefTx = this.mergeTxidOnly(btx.txid)
|
|
314
|
+
else if (btx._tx && (!beefTx || beefTx.isTxidOnly))
|
|
315
|
+
beefTx = this.mergeTransaction(btx._tx)
|
|
316
|
+
else if (btx._rawTx && (!beefTx || beefTx.isTxidOnly))
|
|
317
|
+
beefTx = this.mergeRawTx(btx._rawTx)
|
|
314
318
|
return beefTx
|
|
315
319
|
}
|
|
316
320
|
|
|
@@ -442,8 +446,9 @@ export class Beef {
|
|
|
442
446
|
/**
|
|
443
447
|
* Serialize this Beef as AtomicBEEF.
|
|
444
448
|
*
|
|
445
|
-
* `txid` must exist
|
|
446
|
-
*
|
|
449
|
+
* `txid` must exist
|
|
450
|
+
*
|
|
451
|
+
* after sorting, if txid is not last txid, creates a clone and removes newer txs
|
|
447
452
|
*
|
|
448
453
|
* @param txid
|
|
449
454
|
* @returns serialized contents of this Beef with AtomicBEEF prefix.
|
|
@@ -452,11 +457,16 @@ export class Beef {
|
|
|
452
457
|
this.sortTxs()
|
|
453
458
|
const tx = this.findTxid(txid)
|
|
454
459
|
if (!tx) { throw new Error(`${txid} does not exist in this Beef`) }
|
|
455
|
-
|
|
460
|
+
let beef: Beef = this
|
|
461
|
+
if (this.txs[this.txs.length - 1] !== tx) {
|
|
462
|
+
beef = this.clone()
|
|
463
|
+
const i = this.txs.findIndex(t => t.txid === txid)
|
|
464
|
+
beef.txs.splice(i + 1)
|
|
465
|
+
}
|
|
456
466
|
const writer = new Writer()
|
|
457
467
|
writer.writeUInt32LE(ATOMIC_BEEF)
|
|
458
468
|
writer.write(toArray(txid, 'hex'))
|
|
459
|
-
|
|
469
|
+
beef.toWriter(writer)
|
|
460
470
|
return writer.toArray()
|
|
461
471
|
}
|
|
462
472
|
|
|
@@ -480,7 +490,7 @@ export class Beef {
|
|
|
480
490
|
const beef = new Beef(version === BEEF_MAGIC_V2 ? 'V2' : undefined)
|
|
481
491
|
const bumpsLength = br.readVarIntNum()
|
|
482
492
|
for (let i = 0; i < bumpsLength; i++) {
|
|
483
|
-
const bump = MerklePath.fromReader(br)
|
|
493
|
+
const bump = MerklePath.fromReader(br, false)
|
|
484
494
|
beef.bumps.push(bump)
|
|
485
495
|
}
|
|
486
496
|
const txsLength = br.readVarIntNum()
|
|
@@ -697,6 +707,37 @@ export class Beef {
|
|
|
697
707
|
}
|
|
698
708
|
return log
|
|
699
709
|
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* In some circumstances it may be helpful for the BUMP MerkePaths to include
|
|
713
|
+
* leaves that can be computed from row zero.
|
|
714
|
+
*/
|
|
715
|
+
addComputedLeaves() {
|
|
716
|
+
const beef = this
|
|
717
|
+
const hash = (m: string): string => toHex((
|
|
718
|
+
hash256(toArray(m, 'hex').reverse())
|
|
719
|
+
).reverse())
|
|
720
|
+
|
|
721
|
+
for (const bump of beef.bumps) {
|
|
722
|
+
for (let row = 1; row < bump.path.length; row++) {
|
|
723
|
+
for (const leafL of bump.path[row - 1]) {
|
|
724
|
+
if (leafL.hash && (leafL.offset & 1) === 0) {
|
|
725
|
+
const leafR = bump.path[row - 1].find(l => l.offset === leafL.offset + 1)
|
|
726
|
+
const offsetOnRow = leafL.offset >> 1
|
|
727
|
+
if (leafR && leafR.hash && -1 === bump.path[row].findIndex(l => l.offset === offsetOnRow)) {
|
|
728
|
+
// computable leaf is missing... add it.
|
|
729
|
+
bump.path[row].push({
|
|
730
|
+
offset: offsetOnRow,
|
|
731
|
+
// string concatenation puts the right leaf on the left of the left leaf hash :-)
|
|
732
|
+
hash: hash(leafR.hash + leafL.hash)
|
|
733
|
+
})
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
700
741
|
}
|
|
701
742
|
|
|
702
743
|
export default Beef
|
|
@@ -50,7 +50,7 @@ export default class MerklePath {
|
|
|
50
50
|
return MerklePath.fromBinary(toArray(hex, 'hex'))
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
static fromReader (reader: Reader): MerklePath {
|
|
53
|
+
static fromReader (reader: Reader, legalOffsetsOnly: boolean = true): MerklePath {
|
|
54
54
|
const blockHeight = reader.readVarIntNum()
|
|
55
55
|
const treeHeight = reader.readUInt8()
|
|
56
56
|
const path = Array(treeHeight).fill(0).map(() => ([]))
|
|
@@ -79,7 +79,7 @@ export default class MerklePath {
|
|
|
79
79
|
}
|
|
80
80
|
path[level].sort((a, b) => a.offset - b.offset)
|
|
81
81
|
}
|
|
82
|
-
return new MerklePath(blockHeight, path)
|
|
82
|
+
return new MerklePath(blockHeight, path, legalOffsetsOnly)
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
/**
|
|
@@ -114,7 +114,7 @@ export default class MerklePath {
|
|
|
114
114
|
hash?: string
|
|
115
115
|
txid?: boolean
|
|
116
116
|
duplicate?: boolean
|
|
117
|
-
}
|
|
117
|
+
}>>, legalOffsetsOnly: boolean = true) {
|
|
118
118
|
this.blockHeight = blockHeight
|
|
119
119
|
this.path = path
|
|
120
120
|
|
|
@@ -135,7 +135,7 @@ export default class MerklePath {
|
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
} else {
|
|
138
|
-
if (!legalOffsets[height].has(leaf.offset)) {
|
|
138
|
+
if (legalOffsetsOnly && !legalOffsets[height].has(leaf.offset)) {
|
|
139
139
|
throw new Error(`Invalid offset: ${leaf.offset}, at height: ${height}, with legal offsets: ${Array.from(legalOffsets[height]).join(', ')}`)
|
|
140
140
|
}
|
|
141
141
|
}
|
|
@@ -13,6 +13,7 @@ import ChainTracker from './ChainTracker.js'
|
|
|
13
13
|
import { defaultBroadcaster } from './broadcasters/DefaultBroadcaster.js'
|
|
14
14
|
import { defaultChainTracker } from './chaintrackers/DefaultChainTracker.js'
|
|
15
15
|
import { ATOMIC_BEEF, BEEF_MAGIC } from './Beef.js'
|
|
16
|
+
import P2PKH from '../script/templates/P2PKH.js'
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Represents a complete Bitcoin transaction. This class encapsulates all the details
|
|
@@ -455,9 +456,30 @@ export default class Transaction {
|
|
|
455
456
|
*/
|
|
456
457
|
addOutput (output: TransactionOutput): void {
|
|
457
458
|
this.cachedHash = undefined
|
|
459
|
+
if (!output.change) {
|
|
460
|
+
if (typeof output.satoshis === 'undefined') throw new Error('either satoshis must be defined or change must be set to true')
|
|
461
|
+
if (output.satoshis <= 0) throw new Error('satoshis must be a positive integer or zero')
|
|
462
|
+
}
|
|
463
|
+
if (!output.lockingScript) throw new Error('lockingScript must be defined')
|
|
458
464
|
this.outputs.push(output)
|
|
459
465
|
}
|
|
460
466
|
|
|
467
|
+
/**
|
|
468
|
+
* Adds a new P2PKH output to the transaction.
|
|
469
|
+
*
|
|
470
|
+
* @param {number[] | string} address - The P2PKH address of the output.
|
|
471
|
+
* @param {number} [satoshis] - The number of satoshis to send to the address - if not provided, the output is considered a change output.
|
|
472
|
+
*
|
|
473
|
+
*/
|
|
474
|
+
addP2PKHOutput (address: number[] | string, satoshis?: number): void {
|
|
475
|
+
const lockingScript = new P2PKH().lock(address)
|
|
476
|
+
if (typeof satoshis === 'undefined') { return this.addOutput({ lockingScript, change: true }) }
|
|
477
|
+
this.addOutput({
|
|
478
|
+
lockingScript,
|
|
479
|
+
satoshis
|
|
480
|
+
})
|
|
481
|
+
}
|
|
482
|
+
|
|
461
483
|
/**
|
|
462
484
|
* Updates the transaction's metadata.
|
|
463
485
|
*
|
|
@@ -4,13 +4,17 @@ import Beef from "../../../dist/cjs/src/transaction/Beef"
|
|
|
4
4
|
import BeefParty from "../../../dist/cjs/src/transaction/BeefParty"
|
|
5
5
|
import { BEEF_MAGIC, BEEF_MAGIC_V2 } from "../../../dist/cjs/src/transaction/Beef"
|
|
6
6
|
import Transaction from "../../../dist/cjs/src/transaction/Transaction"
|
|
7
|
+
import { fromBase58 } from '../../../dist/cjs/src/primitives/utils'
|
|
7
8
|
|
|
8
9
|
// The following imports allow full type checking by the VsCode editor, but tests will fail to run:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
/*
|
|
11
|
+
import BeefTx from '../BeefTx'
|
|
12
|
+
import Beef from '../Beef'
|
|
13
|
+
import BeefParty from "../BeefParty"
|
|
14
|
+
import { BEEF_MAGIC, BEEF_MAGIC_V2 } from "../Beef"
|
|
15
|
+
import Transaction from "../Transaction"
|
|
16
|
+
import { fromBase58 } from "../../primitives/utils"
|
|
17
|
+
*/
|
|
14
18
|
|
|
15
19
|
describe('Beef tests', () => {
|
|
16
20
|
jest.setTimeout(99999999)
|
|
@@ -323,8 +327,23 @@ describe('Beef tests', () => {
|
|
|
323
327
|
expect(beef.txs[1].txid).toBe('a')
|
|
324
328
|
}
|
|
325
329
|
})
|
|
330
|
+
|
|
331
|
+
test('10_deserialize beef with extra leaves', async () => {
|
|
332
|
+
const b58Beef = b58Beef_10
|
|
333
|
+
|
|
334
|
+
const beef = Beef.fromBinary(fromBase58(b58Beef))
|
|
335
|
+
expect(beef.verifyValid().valid).toBe(true)
|
|
336
|
+
const abeef = beef.toBinaryAtomic(beef.txs[beef.txs.length -1].txid)
|
|
337
|
+
const pbeef = Beef.fromBinary(abeef)
|
|
338
|
+
pbeef.addComputedLeaves();
|
|
339
|
+
const pbeefBinary = pbeef.toBinary();
|
|
340
|
+
expect(pbeefBinary).toBeTruthy()
|
|
341
|
+
expect(pbeef.verifyValid().valid).toBe(true)
|
|
342
|
+
})
|
|
326
343
|
})
|
|
327
344
|
|
|
345
|
+
const b58Beef_10 = 'gno9MC7VXii1KoCkc2nsVyYJpqzN3dhBzYATETJcys62emMKfpBof4R7GozwYEaSapUtnNvqQ57aaYYjm3U2dv9eUJ1sV46boHkQgppYmAz9YH8FdZduV8aJayPViaKcyPmbDhEw6UW8TM5iFZLXNs7HBnJHUKCeTdNK4FUEL7vAugxAV9WUUZ43BZjJk2SmSeps9TCXjt1Ci9fKWp3d9QSoYvTpxwzyUFHjRKtbUgwq55ZfkBp5bV2Bpz9qSuKywKewW7Hh4S1nCUScwwzpKDozb3zic1V9p2k8rQxoPsRxjUJ8bjhNDdsN8d7KukFuc3n47fXzdWttvnxwsujLJRGnQbgJuknQqx3KLf5kJXHzwjG6TzigZk2t24qeB6d3hbYiaDr2fFkUJBL3tukTHhfNkQYRXuz3kucVDzvejHyqJaF51mXG8BjMN5aQj91ZJXCaPVqkMWCzmvyaqmXMdRiJdSAynhXbQK91xf6RwdNhz1tg5f9B6oJJMhsi9UYSVymmax8VLKD9AKzBCBDcfyD83m3jyS1VgKGZn3SkQmr6bsoWq88L3GsMnnmYUGogvdAYarTqg3pzkjCMxHzmJBMN6ofnUk8c1sRTXQue7BbyUaN5uZu3KW6CmFsEfpuqVvnqFW93TU1jrPP2S8yz8AexAnARPCKE8Yz7RfVaT6RCavwQKL3u5iookwRWEZXW1QWmM37yJWHD87SjVynyg327a1CLwcBxmE2CB48QeNVGyQki4CTQMqw2o8TMhDPJej1g68oniAjBcxBLSCs7KGvK3k7AfrHbCMULX9CTibYhCjdFjbsbBoocqJpxxcvkMo1fEEiAzZuiBVZQDYktDdTVbhKHvYkW25HcYX75NJrpNAhm7AjFeKLzEVxqAQkMfvTufpESNRZF4kQqg2Rg8h2ajcKTd5cpEPwXCrZLHm4EaZEmZVbg3QNfGhn7BJu1bHMtLqPD4y8eJxm2uGrW6saf6qKYmmu64F8A667NbD4yskPRQ1S863VzwGpxxmgLc1Ta3R46jEqsAoRDoZVUaCgBBZG3Yg1CTgi1EVBMXU7qvY4n3h8o2FLCEMWY4KadnV3iD4FbcdCmg4yxBosNAZgbPjhgGjCimjh4YsLd9zymGLmivmz2ZBg5m3xaiXT9NN81X9C1JUujd'
|
|
346
|
+
|
|
328
347
|
const txs: string[] = [
|
|
329
348
|
// 0
|
|
330
349
|
"0100000001074d631cc91344daf964cb8b1f43fd3439b804eb673cbe82d9bde3dcc6394abd020000006b48304502210096e24b07db278344a32c12667bac0d38ea87b28e9c52f3630ff7c11760d4002e02201b9dc879465c82152920f650828bfabba0d0bc8f7e15b568b196f1fa7e2cb4644121034e6dcc60f278b83e3e7b2b6570c13a184f4cf27a279199625c981adfe42de997ffffffff03b706000000000000fdc20421029b09fdddfae493e309d3d97b919f6ab2902a789158f6f78489ad903b7a14baeaac2131546f446f44744b7265457a6248594b466a6d6f42756475466d53585855475a474d3004a1edf497c9db470e7862d8c8319b4312f197a18db529a5839ba7c7e4f4dcee4ff4b4651a2a30040ef0aab1c2a4386620b96cb068c2ad2bc06ed90cebed65643710497f785f48c91482ac86893dd675c3de5d7db2e305fb30a588c75282f062367dee1dd4d930502f64f043c45cd1f220f875ff749f5449c99b29a200a86287a842a4f1df21dafda06a1b39b590b33b15109ee8a700728f1a80016ed370f639b35c9d079dea938d7fe451a52c744605a64a89ac7ebfcf692ef345a1e41fba055c197f7e3f608196001ed150bd56883970f3b8954a63aa70e4fa1c33799d43caaacf7ca30f8e873a6a20a04b1508f851633fcb4f30de75954747e702775b143c668c3b80cef96001980f05a8322726a567a379a2901db030057400bf0484c0852b1ec42cdaae6bf9f258ba0c914d83a96522d63b46a71571087fc4ad4ea0be0f6f354e7f638a7c316ceca3be69698104c1662ce0d5f4334b3327377497f9bb8605388c86a5a3928b22dbafd20a488442862c0ea4cd72778ae3b1bfd1d517299969f553b21173f75b530d1c19e23835dbc423c0ab5d3a894560a064951e237d2956db5d007d2132721a949849df49c5c5ab21f9eacb9ea5aa704c37cd101c17bee37954eae01472096f0e3e52b9f98d46426f35437c76c643c6f5d4d0a5ceb89d03a31c451d7882a527c9e78abf01f6178446cd74a7ad4190f364cc79fccc91583f3521e706bd548dc19633234fe96e40e19ef6424b252691e2183484cbf088e965066d4b93b80de927f3426e71f265152d898537c6112ed4500b8b7f23d033c18bad6c3ae5a2e069b03af5b619a334713f367082d41fdff8d5b17ab90a8aff224fbec3a8ca93828fe18c24b54fdcffdd9ee53c40808a4d233a56ed6f21bda0ebf1f4375785f2427da484ba9cdcd45bd28f8072e885bcb687c575ed00bea520508e795b32b7c882ffdd36062e9f748fddbb99a71e7afaed26c24e631ccce19ceb92f1920fb45ae30b58dde09389062eccf1292c55aca96e7eae53a3b6721a568b1bc8f1bad201c0e3b3fe7caec178e491dc12e97a0d0448136f7fb9970c7b053a85a553927a2273682eb2ae4c41c12f1d893d427f659048bbfa6c4be0682d8226d21e4b522e0a2d575655dea64d36d1e0f5719d4f6d06b47f4b4e32731652f0c12e28a9642e90d049b0fbc2c2c94079106c017ad85a81882344ddb71e3c5de31f5b42df9c269e1f64e36b3179ef5e9581fc57c1ed919962ba11686ef0e0a40694f8733bd1e2232196ca4f35c33df1b27872bd3aa0e258601192e50ec5bb64c6ae1a60f580cb6434048c7a419a76028f95442ce5c628f1324281ca3e8050c7ea8a90c3782a6df2cfee5d735e43f518bc215017cd036f70011312204bf885c615b1d1601626de7a1ff9ff21a44818fa19b7a8ed241b6f16380c9e376c6c7a2f48414269e4a3f130ac5c01a7ca8ad4af044ed4288657f59b675e89287c3811c8e81ba1c75f68b8ec85a35d15b5f1cee0fa25874730450221008d6e0b7d98442477d5637fc12fa871f224fd82194d2c3d8e524a12e389ddc35602206286a3b292ef3bac9f047c842a3e9b418102617546e9dc6f9dcc6653c82e645c6d75c8000000000000001976a9142e1ba3aade0562035e69630cb40024f403c0599e88ac77420000000000001976a914eeea6c76530f46d0f2a20ad522c4a6886bad686d88ac00000000"
|
|
@@ -42,7 +42,7 @@ describe('Transaction Verification Benchmark', () => {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// Build the chain
|
|
45
|
-
for (let i =
|
|
45
|
+
for (let i = 1; i < depth + 1; i++) {
|
|
46
46
|
const newTx = new Transaction()
|
|
47
47
|
newTx.addInput({
|
|
48
48
|
sourceTransaction: tx,
|
|
@@ -52,7 +52,7 @@ describe('Transaction Verification Benchmark', () => {
|
|
|
52
52
|
})
|
|
53
53
|
newTx.addOutput({
|
|
54
54
|
lockingScript: p2pkh.lock(publicKeyHash),
|
|
55
|
-
satoshis: 100000 -
|
|
55
|
+
satoshis: 100000 - (i * 10)
|
|
56
56
|
})
|
|
57
57
|
await newTx.sign()
|
|
58
58
|
tx = newTx
|
|
@@ -973,4 +973,48 @@ describe('Transaction', () => {
|
|
|
973
973
|
}).toThrowError('Transaction with TXID 0000000000000000000000000000000000000000000000000000000000000000 not found in BEEF data.')
|
|
974
974
|
})
|
|
975
975
|
})
|
|
976
|
+
|
|
977
|
+
describe('addP2PKHOutput', () => {
|
|
978
|
+
it('should create an output on the current transaction using an address hash or string', async () => {
|
|
979
|
+
const privateKey = PrivateKey.fromRandom()
|
|
980
|
+
const lockingScript = new P2PKH().lock(privateKey.toAddress())
|
|
981
|
+
const tx = new Transaction()
|
|
982
|
+
tx.addInput({
|
|
983
|
+
sourceTXID: '00'.repeat(32),
|
|
984
|
+
sourceOutputIndex: 0,
|
|
985
|
+
unlockingScriptTemplate: new P2PKH().unlock(privateKey),
|
|
986
|
+
})
|
|
987
|
+
tx.addP2PKHOutput(privateKey.toAddress(), 10000)
|
|
988
|
+
expect(tx.outputs.length).toEqual(1)
|
|
989
|
+
expect(tx.outputs[0].satoshis).toEqual(10000)
|
|
990
|
+
expect(tx.outputs[0].lockingScript.toHex() === lockingScript.toHex()).toBeTruthy()
|
|
991
|
+
})
|
|
992
|
+
it('should error is the address is non base58', async () => {
|
|
993
|
+
const tx = new Transaction()
|
|
994
|
+
expect(() => tx.addP2PKHOutput('A small chicken', 10000)).toThrow("Invalid base58 character ")
|
|
995
|
+
})
|
|
996
|
+
it('should error if the address is incorrectly copied', async () => {
|
|
997
|
+
const tx = new Transaction()
|
|
998
|
+
expect(() => tx.addP2PKHOutput('14afWk1jLH9Uwi2mC9C5ehrsvcFxTLYDp', 10000)).toThrow("Invalid checksum")
|
|
999
|
+
})
|
|
1000
|
+
it('should error if the address is a hash of wrong length', async () => {
|
|
1001
|
+
const tx = new Transaction()
|
|
1002
|
+
const address = [1,2,3,4,5]
|
|
1003
|
+
expect(() => tx.addP2PKHOutput(address, 10000)).toThrow("P2PKH hash length must be 20 bytes")
|
|
1004
|
+
})
|
|
1005
|
+
it('should set the output to a change output if the satoshi value is not given', async () => {
|
|
1006
|
+
const tx = new Transaction()
|
|
1007
|
+
tx.addP2PKHOutput('18E63MgXH43KBb288ETM8u91J2cqdGNEmg')
|
|
1008
|
+
expect(tx.outputs[0].change).toBeTruthy()
|
|
1009
|
+
expect(tx.outputs[0].satoshis).toBeUndefined()
|
|
1010
|
+
})
|
|
1011
|
+
it('throw error when satoshi value is negative', async () => {
|
|
1012
|
+
const tx = new Transaction()
|
|
1013
|
+
expect(() => tx.addP2PKHOutput('18E63MgXH43KBb288ETM8u91J2cqdGNEmg', -1000)).toThrow('satoshis must be a positive integer or zero')
|
|
1014
|
+
})
|
|
1015
|
+
it('throw error when output does not set satoshi value', async () => {
|
|
1016
|
+
const tx = new Transaction()
|
|
1017
|
+
expect(() => tx.addOutput({ lockingScript: Script.fromASM('OP_TRUE') })).toThrow('either satoshis must be defined or change must be set to true')
|
|
1018
|
+
})
|
|
1019
|
+
})
|
|
976
1020
|
})
|