@bsv/sdk 1.2.12 → 1.2.14

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.
@@ -135,7 +135,7 @@ This interface defines a standard method for broadcasting transactions.
135
135
  ```ts
136
136
  export interface Broadcaster {
137
137
  broadcast: (transaction: Transaction) => Promise<BroadcastResponse | BroadcastFailure>;
138
- broadcastMany?: (txs: Transaction[]) => Promise<Array<object>>;
138
+ broadcastMany?: (txs: Transaction[]) => Promise<object[]>;
139
139
  }
140
140
  ```
141
141
 
@@ -1219,6 +1219,7 @@ export default class MerklePath {
1219
1219
  static fromHex(hex: string): MerklePath
1220
1220
  static fromReader(reader: Reader): MerklePath
1221
1221
  static fromBinary(bump: number[]): MerklePath
1222
+ static fromCoinbaseTxidAndHeight(txid: string, height: number): MerklePath
1222
1223
  constructor(blockHeight: number, path: Array<Array<{
1223
1224
  offset: number;
1224
1225
  hash?: string;
@@ -1309,6 +1310,24 @@ Argument Details
1309
1310
  + **bump**
1310
1311
  + The binary array representation of the Merkle Path.
1311
1312
 
1313
+ #### Method fromCoinbaseTxidAndHeight
1314
+
1315
+ ```ts
1316
+ static fromCoinbaseTxidAndHeight(txid: string, height: number): MerklePath
1317
+ ```
1318
+ See also: [MerklePath](#class-merklepath)
1319
+
1320
+ Returns
1321
+
1322
+ - A new MerklePath instance which assumes the tx is in a block with no other transactions.
1323
+
1324
+ Argument Details
1325
+
1326
+ + **txid**
1327
+ + The coinbase txid.
1328
+ + **height**
1329
+ + The height of the block.
1330
+
1312
1331
  #### Method fromHex
1313
1332
 
1314
1333
  Creates a MerklePath instance from a hexadecimal string.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsv/sdk",
3
- "version": "1.2.12",
3
+ "version": "1.2.14",
4
4
  "type": "module",
5
5
  "description": "BSV Blockchain Software Development Kit",
6
6
  "main": "dist/cjs/mod.js",
@@ -11,9 +11,9 @@ describe('PrivateKey', () => {
11
11
  BRC42Private.forEach((vector, index) => {
12
12
  it(`Passes BRC42 private vector #${index + 1}`, () => {
13
13
  const publicKey = PublicKey.fromString(vector.senderPublicKey)
14
- const privateKey = PrivateKey.fromString(vector.recipientPrivateKey, 16)
14
+ const privateKey = PrivateKey.fromString(vector.recipientPrivateKey)
15
15
  const derived = privateKey.deriveChild(publicKey, vector.invoiceNumber)
16
- expect(derived.toHex(32)).toEqual(vector.privateKey)
16
+ expect(derived.toHex()).toEqual(vector.privateKey)
17
17
  })
18
18
  })
19
19
  })
@@ -94,6 +94,21 @@ export default class MerklePath {
94
94
  return MerklePath.fromReader(reader)
95
95
  }
96
96
 
97
+ /**
98
+ *
99
+ * @static fromCoinbaseTxid
100
+ *
101
+ * Creates a MerklePath instance for a coinbase transaction in an empty block.
102
+ * This edge case is difficult to retrieve from standard APIs.
103
+ *
104
+ * @param {string} txid - The coinbase txid.
105
+ * @param {number} height - The height of the block.
106
+ * @returns {MerklePath} - A new MerklePath instance which assumes the tx is in a block with no other transactions.
107
+ */
108
+ static fromCoinbaseTxidAndHeight (txid: string, height: number): MerklePath {
109
+ return new MerklePath(height, [[{ offset: 0, hash: txid, txid: true }]])
110
+ }
111
+
97
112
  constructor (blockHeight: number, path: Array<Array<{
98
113
  offset: number
99
114
  hash?: string
@@ -127,8 +142,8 @@ export default class MerklePath {
127
142
  })
128
143
  })
129
144
 
130
- let root: string
131
145
  // every txid must calculate to the same root.
146
+ let root: string
132
147
  this.path[0].map((leaf, idx) => {
133
148
  if (idx === 0) root = this.computeRoot(leaf.hash)
134
149
  if (root !== this.computeRoot(leaf.hash)) {
@@ -198,6 +213,10 @@ export default class MerklePath {
198
213
  hash256(toArray(m, 'hex').reverse())
199
214
  ).reverse())
200
215
  let workingHash = txid
216
+
217
+ // special case for blocks with only one transaction
218
+ if (this.path.length === 1 && this.path[0].length === 1) return workingHash
219
+
201
220
  for (let height = 0; height < this.path.length; height++) {
202
221
  const leaves = this.path[height]
203
222
  const offset = index >> height ^ 1
@@ -12,7 +12,7 @@ import Spend from '../script/Spend.js'
12
12
  import ChainTracker from './ChainTracker.js'
13
13
  import { defaultBroadcaster } from './broadcasters/DefaultBroadcaster.js'
14
14
  import { defaultChainTracker } from './chaintrackers/DefaultChainTracker.js'
15
- import { BEEF_MAGIC } from './Beef.js'
15
+ import { ATOMIC_BEEF, BEEF_MAGIC } from './Beef.js'
16
16
 
17
17
  /**
18
18
  * Represents a complete Bitcoin transaction. This class encapsulates all the details
@@ -119,8 +119,8 @@ export default class Transaction {
119
119
  const reader = new Reader(beef)
120
120
  // Read the Atomic BEEF prefix
121
121
  const prefix = reader.readUInt32LE()
122
- if (prefix !== 0x01010101) {
123
- throw new Error(`Invalid Atomic BEEF prefix. Expected 0x01010101, received ${prefix.toString(16)}.`)
122
+ if (prefix !== ATOMIC_BEEF) {
123
+ throw new Error(`Invalid Atomic BEEF prefix. Expected 0x01010101, received 0x${prefix.toString(16)}.`)
124
124
  }
125
125
 
126
126
  // Read the subject TXID
@@ -185,4 +185,11 @@ describe('MerklePath', () => {
185
185
  expect(() => MerklePath.fromHex(valid.bump)).not.toThrowError()
186
186
  }
187
187
  })
188
+ it('Validates a MerklePath for a block which only has 1 tx', () => {
189
+ const path = MerklePath.fromHex('fdd2040101000202ef57aa9f29c8141ae17935c88434457b2117890f23efba0d0e0cba7a7a37d5')
190
+ expect(path.computeRoot('d5377a7aba0c0e0dbaef230f8917217b453484c83579e11a14c8299faa57ef02')).toEqual('d5377a7aba0c0e0dbaef230f8917217b453484c83579e11a14c8299faa57ef02')
191
+ })
192
+ it('Creates a valid MerklePath from a txid', () => {
193
+ expect(() => MerklePath.fromCoinbaseTxidAndHeight('d5377a7aba0c0e0dbaef230f8917217b453484c83579e11a14c8299faa57ef02', 1)).not.toThrowError()
194
+ })
188
195
  })
@@ -661,6 +661,15 @@ describe('Transaction', () => {
661
661
  const verified = await tx.verify("scripts only")
662
662
  expect(verified).toBe(true)
663
663
  })
664
+
665
+ it('Verifies tx scripts only when our input has no MerklePath.', async () => {
666
+ const sourceTransaction = Transaction.fromHex('01000000013834506e4af751dc3a0d73e9bdd067709ade5248860967fb3eb72854be6011dc020000006b4830450221008abf2ce1afbcc3c4895265b0966ac69e2275fb35a738f9873b5416cd3bb68cc102202c906f9771d2d6868f81a34d6a3af4cd7de070969286db7097c5e0f3a009b638412102f6a81c442dcd903c7808a75abde9217268bb70417ad538c18fb597b4c553c354ffffffff0301000000000000007c21039bf52152665699e24c1018bf5b48e575de316f844962a79db32b93b7e328bf71ac1062656e63686d61726b546f6b656e5f36463044022034ca3cc49163e4d5bc24f41c5ea4631efd4ae7c243f904c90e1db6e6a55e8f0e02206bb031baca2fc6d64da5ad722ce2e07137718e5e58092e5ecf54e52fcdd664ab6dc8000000000000001976a91470763a553d615a8144fa0889a0e1f93d1d8da8b088acaf430000000000001976a9145aa12f9be0121eee54e7331d8f995fe4dded72f588ac00000000')
667
+ const tx = Transaction.fromHex('01000000015c22e8d34a95a761a0685b8b5a99a0845e9f03d87d5716a7351d08939db5cd92020000006a47304402205101f2bdc40098cbde1a270071a5bfdd5a6fbefc8a38a8b4a1adaca0a6ff55e702200ec5c0a57cb65dfc6aa22e6856bb4c62b8987a7b9fa32cb601c94b579ba67d83412103c8be6b217fc55939851e28239fbbf731f997e026ad344aa9d4b739181fe96743ffffffff0301000000000000007c21039bf52152665699e24c1018bf5b48e575de316f844962a79db32b93b7e328bf71ac1062656e63686d61726b546f6b656e5f374630440220026808e9453f75ca1fc3c7bb0f7be1efaca5cea6b1eda96ab7db56c519b6793202207d1fa5e7d54254d2142256947f93a061781280a6ed2464052470633a7c888d1f6dc8000000000000001976a91428bb6f207ac8f4b7f6c1c5914e143a6165586b2488ace5420000000000001976a914197cbde77abbcb985ca60a85c2988b2ac32c541a88ac00000000')
668
+ sourceTransaction.merklePath = { assumeValid: true } // can be any object.
669
+ tx.inputs[0].sourceTransaction = sourceTransaction
670
+ const verified = await tx.verify('scripts only', undefined)
671
+ expect(verified).toBe(true)
672
+ })
664
673
  })
665
674
 
666
675
  describe('vectors: a 1mb transaction', () => {