@bsv/sdk 1.2.15 → 1.2.18

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 (76) hide show
  1. package/dist/cjs/package.json +2 -2
  2. package/dist/cjs/src/primitives/BigNumber.js +85 -89
  3. package/dist/cjs/src/primitives/BigNumber.js.map +1 -1
  4. package/dist/cjs/src/primitives/PublicKey.js +5 -2
  5. package/dist/cjs/src/primitives/PublicKey.js.map +1 -1
  6. package/dist/cjs/src/primitives/Random.js +3 -2
  7. package/dist/cjs/src/primitives/Random.js.map +1 -1
  8. package/dist/cjs/src/primitives/utils.js +71 -62
  9. package/dist/cjs/src/primitives/utils.js.map +1 -1
  10. package/dist/cjs/src/totp/totp.js +0 -1
  11. package/dist/cjs/src/totp/totp.js.map +1 -1
  12. package/dist/cjs/src/transaction/Beef.js +61 -49
  13. package/dist/cjs/src/transaction/Beef.js.map +1 -1
  14. package/dist/cjs/src/transaction/BeefParty.js +1 -1
  15. package/dist/cjs/src/transaction/BeefTx.js +75 -73
  16. package/dist/cjs/src/transaction/BeefTx.js.map +1 -1
  17. package/dist/cjs/src/transaction/MerklePath.js +4 -4
  18. package/dist/cjs/src/transaction/MerklePath.js.map +1 -1
  19. package/dist/cjs/src/transaction/Transaction.js +70 -96
  20. package/dist/cjs/src/transaction/Transaction.js.map +1 -1
  21. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  22. package/dist/esm/src/primitives/BigNumber.js +85 -89
  23. package/dist/esm/src/primitives/BigNumber.js.map +1 -1
  24. package/dist/esm/src/primitives/PublicKey.js +5 -2
  25. package/dist/esm/src/primitives/PublicKey.js.map +1 -1
  26. package/dist/esm/src/primitives/Random.js +2 -2
  27. package/dist/esm/src/primitives/Random.js.map +1 -1
  28. package/dist/esm/src/primitives/utils.js +70 -61
  29. package/dist/esm/src/primitives/utils.js.map +1 -1
  30. package/dist/esm/src/totp/totp.js +0 -1
  31. package/dist/esm/src/totp/totp.js.map +1 -1
  32. package/dist/esm/src/transaction/Beef.js +60 -48
  33. package/dist/esm/src/transaction/Beef.js.map +1 -1
  34. package/dist/esm/src/transaction/BeefParty.js +1 -1
  35. package/dist/esm/src/transaction/BeefTx.js +76 -74
  36. package/dist/esm/src/transaction/BeefTx.js.map +1 -1
  37. package/dist/esm/src/transaction/MerklePath.js +4 -4
  38. package/dist/esm/src/transaction/MerklePath.js.map +1 -1
  39. package/dist/esm/src/transaction/Transaction.js +71 -97
  40. package/dist/esm/src/transaction/Transaction.js.map +1 -1
  41. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  42. package/dist/types/src/primitives/BigNumber.d.ts +24 -22
  43. package/dist/types/src/primitives/BigNumber.d.ts.map +1 -1
  44. package/dist/types/src/primitives/PublicKey.d.ts.map +1 -1
  45. package/dist/types/src/primitives/utils.d.ts +17 -17
  46. package/dist/types/src/primitives/utils.d.ts.map +1 -1
  47. package/dist/types/src/transaction/Beef.d.ts +17 -14
  48. package/dist/types/src/transaction/Beef.d.ts.map +1 -1
  49. package/dist/types/src/transaction/BeefParty.d.ts +1 -1
  50. package/dist/types/src/transaction/BeefTx.d.ts +5 -2
  51. package/dist/types/src/transaction/BeefTx.d.ts.map +1 -1
  52. package/dist/types/src/transaction/MerklePath.d.ts +2 -2
  53. package/dist/types/src/transaction/MerklePath.d.ts.map +1 -1
  54. package/dist/types/src/transaction/Transaction.d.ts +6 -0
  55. package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
  56. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  57. package/dist/umd/bundle.js +1 -1
  58. package/docs/compat.md +13 -11
  59. package/docs/primitives.md +152 -188
  60. package/docs/transaction.md +87 -79
  61. package/docs/wallet-substrates.md +1410 -1
  62. package/package.json +2 -2
  63. package/src/primitives/BigNumber.ts +111 -111
  64. package/src/primitives/PublicKey.ts +5 -2
  65. package/src/primitives/Random.ts +2 -2
  66. package/src/primitives/utils.ts +92 -77
  67. package/src/totp/totp.ts +0 -1
  68. package/src/transaction/Beef.ts +60 -42
  69. package/src/transaction/BeefParty.ts +1 -1
  70. package/src/transaction/BeefTx.ts +89 -57
  71. package/src/transaction/MerklePath.ts +4 -4
  72. package/src/transaction/Transaction.ts +77 -100
  73. package/src/transaction/__tests/Beef.test.ts +32 -13
  74. package/src/transaction/__tests/Transaction.benchmarks.test.ts +1 -1
  75. package/src/transaction/__tests/Transaction.test.ts +3 -3
  76. package/src/transaction/broadcasters/__tests/WhatsOnChainBroadcaster.test.ts +2 -2
@@ -21,8 +21,8 @@ export const zero2 = (word: string): string => {
21
21
  */
22
22
  export const toHex = (msg: number[]): string => {
23
23
  let res = ''
24
- for (let i = 0; i < msg.length; i++) {
25
- res += zero2(msg[i].toString(16))
24
+ for (const num of msg) {
25
+ res += zero2(num.toString(16))
26
26
  }
27
27
  return res
28
28
  }
@@ -36,59 +36,63 @@ export const toHex = (msg: number[]): string => {
36
36
  * @returns {any[]} - Array representation of the input.
37
37
  */
38
38
  export const toArray = (msg: any, enc?: 'hex' | 'utf8' | 'base64'): any[] => {
39
- // Return a copy if already an array
40
- if (Array.isArray(msg)) { return msg.slice() }
41
-
42
- // Return empty array for falsy values
43
- if (!(msg as boolean)) { return [] }
44
- const res: any[] = []
39
+ if (Array.isArray(msg)) return msg.slice()
40
+ if (!msg) return []
45
41
 
46
- // Convert non-string messages to numbers
47
42
  if (typeof msg !== 'string') {
48
- for (let i = 0; i < msg.length; i++) { res[i] = msg[i] | 0 }
49
- return res
43
+ return Array.from(msg, (item: any) => item | 0)
50
44
  }
51
45
 
52
- // Handle hexadecimal encoding
53
- if (enc === 'hex') {
54
- msg = msg.replace(/[^a-z0-9]+/ig, '')
55
- if (msg.length % 2 !== 0) { msg = '0' + (msg as string) }
56
- for (let i = 0; i < msg.length; i += 2) {
57
- res.push(
58
- parseInt((msg[i] as string) + (msg[i + 1] as string), 16)
59
- )
60
- }
46
+ switch (enc) {
47
+ case 'hex':
48
+ return hexToArray(msg)
49
+ case 'base64':
50
+ return base64ToArray(msg)
51
+ default:
52
+ return utf8ToArray(msg)
53
+ }
54
+ }
61
55
 
62
- // Handle base64
63
- } else if (enc === 'base64') {
64
- const base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
65
- const result: number[] = []
66
- let currentBit: number = 0
67
- let currentByte: number = 0
68
-
69
- for (const char of msg.replace(/=+$/, '')) {
70
- currentBit = (currentBit << 6) | base64Chars.indexOf(char)
71
- currentByte += 6
72
-
73
- if (currentByte >= 8) {
74
- currentByte -= 8
75
- result.push((currentBit >> currentByte) & 0xFF)
76
- currentBit &= (1 << currentByte) - 1
77
- }
56
+ const hexToArray = (msg: string): number[] => {
57
+ msg = msg.replace(/[^a-z0-9]+/ig, '')
58
+ if (msg.length % 2 !== 0) msg = '0' + msg
59
+ const res: number[] = []
60
+ for (let i = 0; i < msg.length; i += 2) {
61
+ res.push(parseInt(msg[i] + msg[i + 1], 16))
62
+ }
63
+ return res
64
+ }
65
+
66
+ const base64ToArray = (msg: string): number[] => {
67
+ const base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
68
+ const result: number[] = []
69
+ let currentBit = 0
70
+ let currentByte = 0
71
+
72
+ for (const char of msg.replace(/=+$/, '')) {
73
+ currentBit = (currentBit << 6) | base64Chars.indexOf(char)
74
+ currentByte += 6
75
+
76
+ if (currentByte >= 8) {
77
+ currentByte -= 8
78
+ result.push((currentBit >> currentByte) & 0xFF)
79
+ currentBit &= (1 << currentByte) - 1
78
80
  }
81
+ }
79
82
 
80
- return result
81
- } else {
82
- // Handle UTF-8 encoding
83
- for (let i = 0; i < msg.length; i++) {
84
- const c = msg.charCodeAt(i)
85
- const hi = c >> 8
86
- const lo = c & 0xff
87
- if (hi as unknown as boolean) {
88
- res.push(hi, lo)
89
- } else {
90
- res.push(lo)
91
- }
83
+ return result
84
+ }
85
+
86
+ const utf8ToArray = (msg: string): number[] => {
87
+ const res: number[] = []
88
+ for (let i = 0; i < msg.length; i++) {
89
+ const c = msg.charCodeAt(i)
90
+ const hi = c >> 8
91
+ const lo = c & 0xff
92
+ if (hi) {
93
+ res.push(hi, lo)
94
+ } else {
95
+ res.push(lo)
92
96
  }
93
97
  }
94
98
  return res
@@ -101,32 +105,43 @@ export const toArray = (msg: any, enc?: 'hex' | 'utf8' | 'base64'): any[] => {
101
105
  */
102
106
  export const toUTF8 = (arr: number[]): string => {
103
107
  let result = ''
108
+ let skip = 0
104
109
 
105
110
  for (let i = 0; i < arr.length; i++) {
106
111
  const byte = arr[i]
107
112
 
113
+ // this byte is part of a multi-byte sequence, skip it
114
+ // added to avoid modifying i within the loop which is considered unsafe.
115
+ if (skip > 0) {
116
+ skip--
117
+ continue
118
+ }
119
+
108
120
  // 1-byte sequence (0xxxxxxx)
109
121
  if (byte <= 0x7F) {
110
122
  result += String.fromCharCode(byte)
111
123
  }
112
124
  // 2-byte sequence (110xxxxx 10xxxxxx)
113
125
  else if (byte >= 0xC0 && byte <= 0xDF) {
114
- const byte2 = arr[++i]
126
+ const byte2 = arr[i + 1]
127
+ skip = 1
115
128
  const codePoint = ((byte & 0x1F) << 6) | (byte2 & 0x3F)
116
129
  result += String.fromCharCode(codePoint)
117
130
  }
118
131
  // 3-byte sequence (1110xxxx 10xxxxxx 10xxxxxx)
119
132
  else if (byte >= 0xE0 && byte <= 0xEF) {
120
- const byte2 = arr[++i]
121
- const byte3 = arr[++i]
133
+ const byte2 = arr[i + 1]
134
+ const byte3 = arr[i + 2]
135
+ skip = 2
122
136
  const codePoint = ((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | (byte3 & 0x3F)
123
137
  result += String.fromCharCode(codePoint)
124
138
  }
125
139
  // 4-byte sequence (11110xxx 10xxxxxx 10xxxxxx 10xxxxxx)
126
140
  else if (byte >= 0xF0 && byte <= 0xF7) {
127
- const byte2 = arr[++i]
128
- const byte3 = arr[++i]
129
- const byte4 = arr[++i]
141
+ const byte2 = arr[i + 1]
142
+ const byte3 = arr[i + 2]
143
+ const byte4 = arr[i + 3]
144
+ skip = 3
130
145
  const codePoint = ((byte & 0x07) << 18) | ((byte2 & 0x3F) << 12) | ((byte3 & 0x3F) << 6) | (byte4 & 0x3F)
131
146
 
132
147
  // Convert to UTF-16 surrogate pair
@@ -212,7 +227,7 @@ export const fromBase58 = (str: string): number[] => {
212
227
  const uint8 = new Uint8Array([
213
228
  ...new Uint8Array(psz),
214
229
  ...str
215
- .match(/.{1}/gmu)
230
+ .match(/./gmu)
216
231
  .map((i) => base58chars.indexOf(i))
217
232
  .reduce((acc, i) => {
218
233
  acc = acc.map((j) => {
@@ -324,19 +339,19 @@ export class Writer {
324
339
  const ret = new Array(totalLength)
325
340
  let offset = 0
326
341
  for (const buf of this.bufs) {
327
- for (let i = 0; i < buf.length; i++) {
328
- ret[offset++] = buf[i]
342
+ for (const value of buf) {
343
+ ret[offset++] = value
329
344
  }
330
345
  }
331
346
  return ret
332
347
  }
333
348
 
334
- write (buf: number[]): Writer {
349
+ write (buf: number[]): this {
335
350
  this.bufs.push(buf)
336
351
  return this
337
352
  }
338
353
 
339
- writeReverse (buf: number[]): Writer {
354
+ writeReverse (buf: number[]): this {
340
355
  const buf2: number[] = new Array(buf.length)
341
356
  for (let i = 0; i < buf2.length; i++) {
342
357
  buf2[i] = buf[buf.length - 1 - i]
@@ -345,21 +360,21 @@ export class Writer {
345
360
  return this
346
361
  }
347
362
 
348
- writeUInt8 (n: number): Writer {
363
+ writeUInt8 (n: number): this {
349
364
  const buf = new Array(1)
350
365
  buf[0] = n
351
366
  this.write(buf)
352
367
  return this
353
368
  }
354
369
 
355
- writeInt8 (n: number): Writer {
370
+ writeInt8 (n: number): this {
356
371
  const buf = new Array(1)
357
372
  buf[0] = n & 0xFF
358
373
  this.write(buf)
359
374
  return this
360
375
  }
361
376
 
362
- writeUInt16BE (n: number): Writer {
377
+ writeUInt16BE (n: number): this {
363
378
  this.bufs.push([
364
379
  (n >> 8) & 0xFF, // shift right 8 bits to get the high byte
365
380
  n & 0xFF // low byte is just the last 8 bits
@@ -367,11 +382,11 @@ export class Writer {
367
382
  return this
368
383
  }
369
384
 
370
- writeInt16BE (n: number): Writer {
385
+ writeInt16BE (n: number): this {
371
386
  return this.writeUInt16BE(n & 0xFFFF) // Mask with 0xFFFF to get the lower 16 bits
372
387
  }
373
388
 
374
- writeUInt16LE (n: number): Writer {
389
+ writeUInt16LE (n: number): this {
375
390
  this.bufs.push([
376
391
  n & 0xFF, // low byte is just the last 8 bits
377
392
  (n >> 8) & 0xFF // shift right 8 bits to get the high byte
@@ -379,11 +394,11 @@ export class Writer {
379
394
  return this
380
395
  }
381
396
 
382
- writeInt16LE (n: number): Writer {
397
+ writeInt16LE (n: number): this {
383
398
  return this.writeUInt16LE(n & 0xFFFF) // Mask with 0xFFFF to get the lower 16 bits
384
399
  }
385
400
 
386
- writeUInt32BE (n: number): Writer {
401
+ writeUInt32BE (n: number): this {
387
402
  this.bufs.push([
388
403
  (n >> 24) & 0xFF, // highest byte
389
404
  (n >> 16) & 0xFF,
@@ -393,11 +408,11 @@ export class Writer {
393
408
  return this
394
409
  }
395
410
 
396
- writeInt32BE (n: number): Writer {
411
+ writeInt32BE (n: number): this {
397
412
  return this.writeUInt32BE(n >>> 0) // Using unsigned right shift to handle negative numbers
398
413
  }
399
414
 
400
- writeUInt32LE (n: number): Writer {
415
+ writeUInt32LE (n: number): this {
401
416
  this.bufs.push([
402
417
  n & 0xFF, // lowest byte
403
418
  (n >> 8) & 0xFF,
@@ -407,35 +422,35 @@ export class Writer {
407
422
  return this
408
423
  }
409
424
 
410
- writeInt32LE (n: number): Writer {
425
+ writeInt32LE (n: number): this {
411
426
  return this.writeUInt32LE(n >>> 0) // Using unsigned right shift to handle negative numbers
412
427
  }
413
428
 
414
- writeUInt64BEBn (bn: BigNumber): Writer {
429
+ writeUInt64BEBn (bn: BigNumber): this {
415
430
  const buf = bn.toArray('be', 8)
416
431
  this.write(buf)
417
432
  return this
418
433
  }
419
434
 
420
- writeUInt64LEBn (bn: BigNumber): Writer {
435
+ writeUInt64LEBn (bn: BigNumber): this {
421
436
  const buf = bn.toArray('be', 8)
422
437
  this.writeReverse(buf)
423
438
  return this
424
439
  }
425
440
 
426
- writeUInt64LE (n: number): Writer {
441
+ writeUInt64LE (n: number): this {
427
442
  const buf = new BigNumber(n).toArray('be', 8)
428
443
  this.writeReverse(buf)
429
444
  return this
430
445
  }
431
446
 
432
- writeVarIntNum (n: number): Writer {
447
+ writeVarIntNum (n: number): this {
433
448
  const buf = Writer.varIntNum(n)
434
449
  this.write(buf)
435
450
  return this
436
451
  }
437
452
 
438
- writeVarIntBn (bn: BigNumber): Writer {
453
+ writeVarIntBn (bn: BigNumber): this {
439
454
  const buf = Writer.varIntBn(bn)
440
455
  this.write(buf)
441
456
  return this
@@ -696,13 +711,13 @@ export const minimallyEncode = (buf: number[]): number[] => {
696
711
  if ((buf[i - 1] & 0x80) !== 0) {
697
712
  // We found a byte with it sign bit set so we need one more
698
713
  // byte.
699
- buf[i++] = last
714
+ buf[i] = last
715
+ return buf.slice(0, i + 1)
700
716
  } else {
701
717
  // the sign bit is clear, we can use it.
702
718
  buf[i - 1] |= last
719
+ return buf.slice(0, i)
703
720
  }
704
-
705
- return buf.slice(0, i)
706
721
  }
707
722
  }
708
723
 
package/src/totp/totp.ts CHANGED
@@ -106,7 +106,6 @@ function generateHOTP (
106
106
  options: Required<TOTPOptions>
107
107
  ): string {
108
108
  const timePad = new BigNumber(counter).toArray('be', 8)
109
- // console.log({ timePad })
110
109
  const hmac = calcHMAC(secret, timePad, options.algorithm)
111
110
  const signature = hmac.digest()
112
111
 
@@ -3,13 +3,16 @@ 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
-
7
- export const BEEF_MAGIC = 4022206465 // 0100BEEF in LE order
8
- export const BEEF_MAGIC_V2 = 4022206466 // 0200BEEF in LE order
9
- export const BEEF_MAGIC_TXID_ONLY_EXTENSION = 4022206465 // 0100BEEF in LE order
10
- export const ATOMIC_BEEF = 0x01010101 // Atomic Beef serialization prefix
11
-
12
- export type BeefVersion = undefined | 'V1' | 'V2'
6
+ import { hash256 } from '../primitives/Hash.js'
7
+
8
+ export const BEEF_V1 = 4022206465 // 0100BEEF in LE order
9
+ export const BEEF_V2 = 4022206466 // 0200BEEF in LE order
10
+ export const ATOMIC_BEEF = 0x01010101 // 01010101
11
+ export enum TX_DATA_FORMAT {
12
+ RAWTX = 0, // rawtx without BUMP
13
+ RAWTX_AND_BUMP_INDEX = 1, // rawtx with bump index
14
+ TXID_ONLY = 2, // txid only
15
+ }
13
16
 
14
17
  /*
15
18
  * BEEF standard: BRC-62: Background Evaluation Extended Format (BEEF) Transactions
@@ -67,29 +70,13 @@ export type BeefVersion = undefined | 'V1' | 'V2'
67
70
  export class Beef {
68
71
  bumps: MerklePath[] = []
69
72
  txs: BeefTx[] = []
70
- version: BeefVersion = undefined
73
+ version: number = BEEF_V2
71
74
  atomicTxid: string | undefined = undefined
72
75
 
73
- constructor (version?: BeefVersion) {
76
+ constructor (version: number = BEEF_V2) {
74
77
  this.version = version
75
78
  }
76
79
 
77
- /**
78
- * BEEF_MAGIC is the original V1 version.
79
- * BEEF_MAGIC_V2 includes support for txidOnly transactions in serialized beefs.
80
- * @returns version magic value based on current contents and constructor version parameter.
81
- */
82
- get magic (): number {
83
- if (this.version === 'V1') { return BEEF_MAGIC }
84
-
85
- if (this.version === 'V2') { return BEEF_MAGIC_V2 }
86
-
87
- const hasTxidOnly = this.txs.findIndex(tx => tx.isTxidOnly) > -1
88
- if (hasTxidOnly) { return BEEF_MAGIC_V2 }
89
-
90
- return BEEF_MAGIC
91
- }
92
-
93
80
  /**
94
81
  * @param txid of `beefTx` to find
95
82
  * @returns `BeefTx` in `txs` with `txid`.
@@ -113,7 +100,7 @@ export class Beef {
113
100
  if (i === -1) return undefined
114
101
  let btx = this.txs[i]
115
102
  if (btx.isTxidOnly) { return btx }
116
- this.txs.slice(i, i + 1)
103
+ this.txs.splice(i, 1)
117
104
  btx = this.mergeTxidOnly(txid)
118
105
  return btx
119
106
  }
@@ -295,8 +282,6 @@ export class Beef {
295
282
  }
296
283
 
297
284
  mergeTxidOnly (txid: string): BeefTx {
298
- if (this.version === 'V1') { throw new Error('BEEF V1 format does not support txid only transactions.') }
299
-
300
285
  let tx = this.txs.find(t => t.txid === txid)
301
286
  if (!tx) {
302
287
  tx = new BeefTx(txid)
@@ -308,9 +293,7 @@ export class Beef {
308
293
 
309
294
  mergeBeefTx (btx: BeefTx): BeefTx {
310
295
  let beefTx = this.findTxid(btx.txid)
311
- if (!beefTx && btx.isTxidOnly) { beefTx = this.mergeTxidOnly(btx.txid) } else if (!beefTx || beefTx.isTxidOnly) {
312
- if (btx._tx) { beefTx = this.mergeTransaction(btx._tx) } else { beefTx = this.mergeRawTx(btx._rawTx) }
313
- }
296
+ if (btx.isTxidOnly && !beefTx) { beefTx = this.mergeTxidOnly(btx.txid) } else if (btx._tx && (!beefTx || beefTx.isTxidOnly)) { beefTx = this.mergeTransaction(btx._tx) } else if (btx._rawTx && (!beefTx || beefTx.isTxidOnly)) { beefTx = this.mergeRawTx(btx._rawTx) }
314
297
  return beefTx
315
298
  }
316
299
 
@@ -416,7 +399,7 @@ export class Beef {
416
399
  * @param writer
417
400
  */
418
401
  toWriter (writer: Writer) {
419
- writer.writeUInt32LE(this.magic)
402
+ writer.writeUInt32LE(this.version)
420
403
 
421
404
  writer.writeVarIntNum(this.bumps.length)
422
405
  for (const b of this.bumps) {
@@ -425,7 +408,7 @@ export class Beef {
425
408
 
426
409
  writer.writeVarIntNum(this.txs.length)
427
410
  for (const tx of this.txs) {
428
- tx.toWriter(writer, this.magic)
411
+ tx.toWriter(writer, this.version)
429
412
  }
430
413
  }
431
414
 
@@ -442,8 +425,9 @@ export class Beef {
442
425
  /**
443
426
  * Serialize this Beef as AtomicBEEF.
444
427
  *
445
- * `txid` must exist and be the last transaction
446
- * in sorted (dependency) order.
428
+ * `txid` must exist
429
+ *
430
+ * after sorting, if txid is not last txid, creates a clone and removes newer txs
447
431
  *
448
432
  * @param txid
449
433
  * @returns serialized contents of this Beef with AtomicBEEF prefix.
@@ -452,11 +436,16 @@ export class Beef {
452
436
  this.sortTxs()
453
437
  const tx = this.findTxid(txid)
454
438
  if (!tx) { throw new Error(`${txid} does not exist in this Beef`) }
455
- if (this.txs[this.txs.length - 1] !== tx) { throw new Error(`${txid} is not the last transaction in this Beef`) }
439
+ let beef: Beef = this
440
+ if (this.txs[this.txs.length - 1] !== tx) {
441
+ beef = this.clone()
442
+ const i = this.txs.findIndex(t => t.txid === txid)
443
+ beef.txs.splice(i + 1)
444
+ }
456
445
  const writer = new Writer()
457
446
  writer.writeUInt32LE(ATOMIC_BEEF)
458
447
  writer.write(toArray(txid, 'hex'))
459
- this.toWriter(writer)
448
+ beef.toWriter(writer)
460
449
  return writer.toArray()
461
450
  }
462
451
 
@@ -476,11 +465,11 @@ export class Beef {
476
465
  atomicTxid = toHex(br.read(32))
477
466
  version = br.readUInt32LE()
478
467
  }
479
- if (version !== BEEF_MAGIC && version !== BEEF_MAGIC_V2) { throw new Error(`Serialized BEEF must start with ${BEEF_MAGIC} or ${BEEF_MAGIC_V2} but starts with ${version}`) }
480
- const beef = new Beef(version === BEEF_MAGIC_V2 ? 'V2' : undefined)
468
+ if (version !== BEEF_V1 && version !== BEEF_V2) { throw new Error(`Serialized BEEF must start with ${BEEF_V1} or ${BEEF_V2} but starts with ${version}`) }
469
+ const beef = new Beef(version)
481
470
  const bumpsLength = br.readVarIntNum()
482
471
  for (let i = 0; i < bumpsLength; i++) {
483
- const bump = MerklePath.fromReader(br)
472
+ const bump = MerklePath.fromReader(br, false)
484
473
  beef.bumps.push(bump)
485
474
  }
486
475
  const txsLength = br.readVarIntNum()
@@ -508,8 +497,7 @@ export class Beef {
508
497
  * @param enc The encoding of the string value from which BEEF should be constructed
509
498
  * @returns An instance of the Beef class constructed from the string
510
499
  */
511
- static fromString (s: string, enc?: 'hex' | 'utf8' | 'base64'): Beef {
512
- enc ||= 'hex'
500
+ static fromString (s: string, enc: 'hex' | 'utf8' | 'base64' = 'hex'): Beef {
513
501
  const bin = toArray(s, enc)
514
502
  const br = new Reader(bin)
515
503
  return Beef.fromReader(br)
@@ -697,6 +685,36 @@ export class Beef {
697
685
  }
698
686
  return log
699
687
  }
688
+
689
+ /**
690
+ * In some circumstances it may be helpful for the BUMP MerkePaths to include
691
+ * leaves that can be computed from row zero.
692
+ */
693
+ addComputedLeaves () {
694
+ const beef = this
695
+ const hash = (m: string): string => toHex((
696
+ hash256(toArray(m, 'hex').reverse())
697
+ ).reverse())
698
+
699
+ for (const bump of beef.bumps) {
700
+ for (let row = 1; row < bump.path.length; row++) {
701
+ for (const leafL of bump.path[row - 1]) {
702
+ if (leafL.hash && (leafL.offset & 1) === 0) {
703
+ const leafR = bump.path[row - 1].find(l => l.offset === leafL.offset + 1)
704
+ const offsetOnRow = leafL.offset >> 1
705
+ if (leafR && leafR.hash && bump.path[row].findIndex(l => l.offset === offsetOnRow) === -1) {
706
+ // computable leaf is missing... add it.
707
+ bump.path[row].push({
708
+ offset: offsetOnRow,
709
+ // string concatenation puts the right leaf on the left of the left leaf hash :-)
710
+ hash: hash(leafR.hash + leafL.hash)
711
+ })
712
+ }
713
+ }
714
+ }
715
+ }
716
+ }
717
+ }
700
718
  }
701
719
 
702
720
  export default Beef
@@ -41,7 +41,7 @@ export class BeefParty extends Beef {
41
41
 
42
42
  /**
43
43
  * @param party
44
- * @returns `true` if `party` has already beed added to this `BeefParty`.
44
+ * @returns `true` if `party` has already been added to this `BeefParty`.
45
45
  */
46
46
  isParty (party: string) {
47
47
  const r = Object.keys(this.knownTo).includes(party)