@bsv/sdk 1.1.25 → 1.1.27

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 (32) hide show
  1. package/dist/cjs/package.json +1 -1
  2. package/dist/cjs/src/transaction/Beef.js +17 -10
  3. package/dist/cjs/src/transaction/Beef.js.map +1 -1
  4. package/dist/cjs/src/transaction/BeefTx.js +67 -24
  5. package/dist/cjs/src/transaction/BeefTx.js.map +1 -1
  6. package/dist/cjs/src/transaction/MerklePath.js.map +1 -1
  7. package/dist/cjs/src/transaction/Transaction.js +154 -34
  8. package/dist/cjs/src/transaction/Transaction.js.map +1 -1
  9. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  10. package/dist/esm/src/transaction/Beef.js +17 -10
  11. package/dist/esm/src/transaction/Beef.js.map +1 -1
  12. package/dist/esm/src/transaction/BeefTx.js +68 -25
  13. package/dist/esm/src/transaction/BeefTx.js.map +1 -1
  14. package/dist/esm/src/transaction/MerklePath.js.map +1 -1
  15. package/dist/esm/src/transaction/Transaction.js +154 -34
  16. package/dist/esm/src/transaction/Transaction.js.map +1 -1
  17. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  18. package/dist/types/src/transaction/Beef.d.ts +5 -3
  19. package/dist/types/src/transaction/Beef.d.ts.map +1 -1
  20. package/dist/types/src/transaction/BeefTx.d.ts +2 -2
  21. package/dist/types/src/transaction/BeefTx.d.ts.map +1 -1
  22. package/dist/types/src/transaction/MerklePath.d.ts.map +1 -1
  23. package/dist/types/src/transaction/Transaction.d.ts +44 -4
  24. package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
  25. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  26. package/docs/transaction.md +80 -9
  27. package/package.json +1 -1
  28. package/src/transaction/Beef.ts +23 -10
  29. package/src/transaction/BeefTx.ts +65 -24
  30. package/src/transaction/MerklePath.ts +11 -11
  31. package/src/transaction/Transaction.ts +195 -59
  32. package/src/transaction/__tests/Transaction.test.ts +222 -3
@@ -8,6 +8,8 @@ export const BEEF_MAGIC = 4022206465 // 0100BEEF in LE order
8
8
  export const BEEF_MAGIC_V2 = 4022206466 // 0200BEEF in LE order
9
9
  export const BEEF_MAGIC_TXID_ONLY_EXTENSION = 4022206465 // 0100BEEF in LE order
10
10
 
11
+ export type BeefVersion = undefined | 'V1' | 'V2'
12
+
11
13
  /*
12
14
  * BEEF standard: BRC-62: Background Evaluation Extended Format (BEEF) Transactions
13
15
  * https://github.com/bitcoin-sv/BRCs/blob/master/transactions/0062.md
@@ -50,21 +52,29 @@ export const BEEF_MAGIC_TXID_ONLY_EXTENSION = 4022206465 // 0100BEEF in LE order
50
52
  export class Beef {
51
53
  bumps: MerklePath[] = []
52
54
  txs: BeefTx[] = []
55
+ version: BeefVersion = undefined
53
56
 
54
- constructor() {
57
+ constructor(version?: BeefVersion) {
58
+ this.version = version
55
59
  }
56
60
 
57
61
  /**
58
62
  * BEEF_MAGIC is the original V1 version.
59
63
  * BEEF_MAGIC_V2 includes support for txidOnly transactions in serialized beefs.
60
- * @returns version based on current contents.
64
+ * @returns version magic value based on current contents and constructor version parameter.
61
65
  */
62
- get version(): number {
63
- const hasTxidOnly = !this.txs.every(tx => !tx.isTxidOnly)
66
+ get magic(): number {
67
+ if (this.version === 'V1')
68
+ return BEEF_MAGIC
69
+
70
+ if (this.version === 'V2')
71
+ return BEEF_MAGIC_V2
72
+
73
+ const hasTxidOnly = -1 < this.txs.findIndex(tx => tx.isTxidOnly)
64
74
  if (hasTxidOnly)
65
75
  return BEEF_MAGIC_V2
66
- else
67
- return BEEF_MAGIC
76
+
77
+ return BEEF_MAGIC
68
78
  }
69
79
 
70
80
  /**
@@ -184,6 +194,9 @@ export class Beef {
184
194
  }
185
195
 
186
196
  mergeTxidOnly(txid: string): BeefTx {
197
+ if (this.version === 'V1')
198
+ throw new Error(`BEEF V1 format does not support txid only transactions.`)
199
+
187
200
  let tx = this.txs.find(t => t.txid === txid)
188
201
  if (!tx) {
189
202
  tx = new BeefTx(txid)
@@ -317,7 +330,7 @@ export class Beef {
317
330
  toBinary(): number[] {
318
331
 
319
332
  const writer = new Writer()
320
- writer.writeUInt32LE(this.version)
333
+ writer.writeUInt32LE(this.magic)
321
334
 
322
335
  writer.writeVarIntNum(this.bumps.length)
323
336
  for (const b of this.bumps) {
@@ -326,7 +339,7 @@ export class Beef {
326
339
 
327
340
  writer.writeVarIntNum(this.txs.length)
328
341
  for (const tx of this.txs) {
329
- tx.toWriter(writer)
342
+ tx.toWriter(writer, this.magic)
330
343
  }
331
344
 
332
345
  return writer.toArray()
@@ -344,7 +357,7 @@ export class Beef {
344
357
  const version = br.readUInt32LE()
345
358
  if (version !== BEEF_MAGIC && version !== BEEF_MAGIC_V2)
346
359
  throw new Error(`Serialized BEEF must start with ${BEEF_MAGIC} or ${BEEF_MAGIC_V2} but starts with ${version}`)
347
- const beef = new Beef()
360
+ const beef = new Beef(version === BEEF_MAGIC_V2 ? 'V2' : undefined)
348
361
  const bumpsLength = br.readVarIntNum()
349
362
  for (let i = 0; i < bumpsLength; i++) {
350
363
  const bump = MerklePath.fromReader(br)
@@ -352,7 +365,7 @@ export class Beef {
352
365
  }
353
366
  const txsLength = br.readVarIntNum()
354
367
  for (let i = 0; i < txsLength; i++) {
355
- const beefTx = BeefTx.fromReader(br)
368
+ const beefTx = BeefTx.fromReader(br, version)
356
369
  beef.txs.push(beefTx)
357
370
  }
358
371
  return beef
@@ -1,7 +1,7 @@
1
1
  import { hash256 } from "../primitives/Hash.js"
2
2
  import { Reader, Writer, toHex, toArray } from "../primitives/utils.js"
3
3
  import Transaction from "./Transaction.js"
4
- import { BEEF_MAGIC_TXID_ONLY_EXTENSION } from "./Beef.js"
4
+ import { BEEF_MAGIC, BEEF_MAGIC_TXID_ONLY_EXTENSION } from "./Beef.js"
5
5
 
6
6
  /**
7
7
  * A single bitcoin transaction associated with a `Beef` validity proof set.
@@ -84,36 +84,77 @@ export default class BeefTx {
84
84
  }
85
85
  }
86
86
 
87
- toWriter(writer: Writer) : void {
88
- if (this.isTxidOnly) {
89
- // Encode just the txid of a known transaction using the txid
90
- writer.writeUInt32LE(BEEF_MAGIC_TXID_ONLY_EXTENSION)
91
- writer.writeReverse(toArray(this._txid, 'hex'))
92
- } else if (this._rawTx)
93
- writer.write(this._rawTx)
94
- else if (this._tx)
95
- writer.write(this._tx.toBinary())
96
- else
97
- throw new Error('a valid serialized Transaction is expected')
98
- if (this.bumpIndex === undefined) {
99
- writer.writeUInt8(0)
87
+ toWriter(writer: Writer, magic: number) : void {
88
+ if (magic === BEEF_MAGIC) {
89
+ // V1
90
+ if (this.isTxidOnly) {
91
+ // Encode just the txid of a known transaction using the txid
92
+ writer.writeUInt32LE(BEEF_MAGIC_TXID_ONLY_EXTENSION)
93
+ writer.writeReverse(toArray(this._txid, 'hex'))
94
+ } else if (this._rawTx)
95
+ writer.write(this._rawTx)
96
+ else if (this._tx)
97
+ writer.write(this._tx.toBinary())
98
+ else
99
+ throw new Error('a valid serialized Transaction is expected')
100
+ if (this.bumpIndex === undefined) {
101
+ writer.writeUInt8(0)
102
+ } else {
103
+ writer.writeUInt8(1)
104
+ writer.writeVarIntNum(this.bumpIndex)
105
+ }
100
106
  } else {
101
- writer.writeUInt8(1)
102
- writer.writeVarIntNum(this.bumpIndex)
107
+ // V2
108
+ if (this.isTxidOnly) {
109
+ // Encode just the txid of a known transaction using the txid
110
+ writer.writeUInt8(2)
111
+ writer.writeReverse(toArray(this._txid, 'hex'))
112
+ } else {
113
+ if (this.bumpIndex === undefined) {
114
+ writer.writeUInt8(0)
115
+ } else {
116
+ writer.writeUInt8(1)
117
+ writer.writeVarIntNum(this.bumpIndex)
118
+ }
119
+ if (this._rawTx)
120
+ writer.write(this._rawTx)
121
+ else if (this._tx)
122
+ writer.write(this._tx.toBinary())
123
+ else
124
+ throw new Error('a valid serialized Transaction is expected')
125
+ }
103
126
  }
104
127
  }
105
128
 
106
- static fromReader (br: Reader): BeefTx {
129
+ static fromReader (br: Reader, magic: number): BeefTx {
130
+
107
131
  let tx: Transaction | number[] | string | undefined = undefined
108
- const version = br.readUInt32LE()
109
- if (version === BEEF_MAGIC_TXID_ONLY_EXTENSION) {
110
- // This is the extension to support known transactions
111
- tx = toHex(br.readReverse(32))
132
+ let bumpIndex: number | undefined = undefined
133
+
134
+ if (magic === BEEF_MAGIC) {
135
+ // V1
136
+ const version = br.readUInt32LE()
137
+ if (version === BEEF_MAGIC_TXID_ONLY_EXTENSION) {
138
+ // This is the extension to support known transactions
139
+ tx = toHex(br.readReverse(32))
140
+ } else {
141
+ br.pos -= 4 // Unread the version...
142
+ tx = Transaction.fromReader(br)
143
+ }
144
+ bumpIndex = br.readUInt8() ? br.readVarIntNum() : undefined
112
145
  } else {
113
- br.pos -= 4 // Unread the version...
114
- tx = Transaction.fromReader(br)
146
+ // V2
147
+ const format = br.readUInt8()
148
+ if (format === 2) {
149
+ // txid only
150
+ tx = toHex(br.readReverse(32))
151
+ } else {
152
+ if (format === 1)
153
+ bumpIndex = br.readVarIntNum()
154
+ tx = Transaction.fromReader(br)
155
+ }
115
156
  }
116
- const bumpIndex = br.readUInt8() ? br.readVarIntNum() : undefined
157
+
117
158
  const beefTx = new BeefTx(tx, bumpIndex)
118
159
  return beefTx
119
160
  }
@@ -46,11 +46,11 @@ export default class MerklePath {
46
46
  * @param {string} hex - The hexadecimal string representation of the Merkle Path.
47
47
  * @returns {MerklePath} - A new MerklePath instance.
48
48
  */
49
- static fromHex (hex: string): MerklePath {
49
+ static fromHex(hex: string): MerklePath {
50
50
  return MerklePath.fromBinary(toArray(hex, 'hex'))
51
51
  }
52
52
 
53
- static fromReader (reader: Reader): MerklePath {
53
+ static fromReader(reader: Reader): MerklePath {
54
54
  const blockHeight = reader.readVarIntNum()
55
55
  const treeHeight = reader.readUInt8()
56
56
  const path = Array(treeHeight).fill(0).map(() => ([]))
@@ -89,12 +89,12 @@ export default class MerklePath {
89
89
  * @param {number[]} bump - The binary array representation of the Merkle Path.
90
90
  * @returns {MerklePath} - A new MerklePath instance.
91
91
  */
92
- static fromBinary (bump: number[]): MerklePath {
92
+ static fromBinary(bump: number[]): MerklePath {
93
93
  const reader = new Reader(bump)
94
94
  return MerklePath.fromReader(reader)
95
95
  }
96
96
 
97
- constructor (blockHeight: number, path: Array<Array<{
97
+ constructor(blockHeight: number, path: Array<Array<{
98
98
  offset: number
99
99
  hash?: string
100
100
  txid?: boolean
@@ -142,7 +142,7 @@ export default class MerklePath {
142
142
  *
143
143
  * @returns {number[]} - The binary array representation of the Merkle Path.
144
144
  */
145
- toBinary (): number[] {
145
+ toBinary(): number[] {
146
146
  const writer = new Writer()
147
147
  writer.writeVarIntNum(this.blockHeight)
148
148
  const treeHeight = this.path.length
@@ -173,7 +173,7 @@ export default class MerklePath {
173
173
  *
174
174
  * @returns {string} - The hexadecimal string representation of the Merkle Path.
175
175
  */
176
- toHex (): string {
176
+ toHex(): string {
177
177
  return toHex(this.toBinary())
178
178
  }
179
179
 
@@ -184,7 +184,7 @@ export default class MerklePath {
184
184
  * @returns {string} - The computed Merkle root as a hexadecimal string.
185
185
  * @throws {Error} - If the transaction ID is not part of the Merkle Path.
186
186
  */
187
- computeRoot (txid?: string): string {
187
+ computeRoot(txid?: string): string {
188
188
  if (typeof txid !== 'string') {
189
189
  txid = this.path[0].find(leaf => Boolean(leaf?.hash)).hash
190
190
  }
@@ -224,7 +224,7 @@ export default class MerklePath {
224
224
  * @param height
225
225
  * @param offset
226
226
  */
227
- findOrComputeLeaf (height: number, offset: number): MerklePathLeaf | undefined {
227
+ findOrComputeLeaf(height: number, offset: number): MerklePathLeaf | undefined {
228
228
  const hash = (m: string): string => toHex((
229
229
  hash256(toArray(m, 'hex').reverse())
230
230
  ).reverse())
@@ -261,7 +261,7 @@ export default class MerklePath {
261
261
  * @param {ChainTracker} chainTracker - The ChainTracker instance used to verify the Merkle root.
262
262
  * @returns {boolean} - True if the transaction ID is valid within the Merkle Path at the specified block height.
263
263
  */
264
- async verify (txid: string, chainTracker: ChainTracker): Promise<boolean> {
264
+ async verify(txid: string, chainTracker: ChainTracker): Promise<boolean> {
265
265
  const root = this.computeRoot(txid)
266
266
  // Use the chain tracker to determine whether this is a valid merkle root at the given block height
267
267
  return await chainTracker.isValidRootForHeight(root, this.blockHeight)
@@ -273,7 +273,7 @@ export default class MerklePath {
273
273
  * @param {MerklePath} other - Another MerklePath to combine with this path.
274
274
  * @throws {Error} - If the paths have different block heights or roots.
275
275
  */
276
- combine (other: MerklePath): void {
276
+ combine(other: MerklePath): void {
277
277
  if (this.blockHeight !== other.blockHeight) {
278
278
  throw new Error('You cannot combine paths which do not have the same block height.')
279
279
  }
@@ -309,7 +309,7 @@ export default class MerklePath {
309
309
  * Assumes that at least all required nodes are present.
310
310
  * Leaves all levels sorted by increasing offset.
311
311
  */
312
- trim () {
312
+ trim() {
313
313
  const pushIfNew = (v: number, a: number[]) => {
314
314
  if (a.length === 0 || a.slice(-1)[0] !== v) { a.push(v) }
315
315
  }