@bsv/sdk 1.1.29 → 1.1.30

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 (56) hide show
  1. package/dist/cjs/package.json +1 -1
  2. package/dist/cjs/src/primitives/Schnorr.js +92 -0
  3. package/dist/cjs/src/primitives/Schnorr.js.map +1 -0
  4. package/dist/cjs/src/primitives/index.js +3 -1
  5. package/dist/cjs/src/primitives/index.js.map +1 -1
  6. package/dist/cjs/src/totp/totp.js +1 -1
  7. package/dist/cjs/src/totp/totp.js.map +1 -1
  8. package/dist/cjs/src/transaction/Beef.js +139 -118
  9. package/dist/cjs/src/transaction/Beef.js.map +1 -1
  10. package/dist/cjs/src/transaction/BeefParty.js +30 -26
  11. package/dist/cjs/src/transaction/BeefParty.js.map +1 -1
  12. package/dist/cjs/src/transaction/BeefTx.js +25 -15
  13. package/dist/cjs/src/transaction/BeefTx.js.map +1 -1
  14. package/dist/cjs/src/transaction/MerklePath.js.map +1 -1
  15. package/dist/cjs/src/transaction/Transaction.js.map +1 -1
  16. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  17. package/dist/esm/src/primitives/Schnorr.js +87 -0
  18. package/dist/esm/src/primitives/Schnorr.js.map +1 -0
  19. package/dist/esm/src/primitives/index.js +1 -0
  20. package/dist/esm/src/primitives/index.js.map +1 -1
  21. package/dist/esm/src/totp/totp.js +1 -1
  22. package/dist/esm/src/totp/totp.js.map +1 -1
  23. package/dist/esm/src/transaction/Beef.js +142 -121
  24. package/dist/esm/src/transaction/Beef.js.map +1 -1
  25. package/dist/esm/src/transaction/BeefParty.js +31 -27
  26. package/dist/esm/src/transaction/BeefParty.js.map +1 -1
  27. package/dist/esm/src/transaction/BeefTx.js +29 -19
  28. package/dist/esm/src/transaction/BeefTx.js.map +1 -1
  29. package/dist/esm/src/transaction/MerklePath.js.map +1 -1
  30. package/dist/esm/src/transaction/Transaction.js.map +1 -1
  31. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  32. package/dist/types/src/primitives/Schnorr.d.ts +65 -0
  33. package/dist/types/src/primitives/Schnorr.d.ts.map +1 -0
  34. package/dist/types/src/primitives/index.d.ts +1 -0
  35. package/dist/types/src/primitives/index.d.ts.map +1 -1
  36. package/dist/types/src/transaction/Beef.d.ts +94 -94
  37. package/dist/types/src/transaction/Beef.d.ts.map +1 -1
  38. package/dist/types/src/transaction/BeefParty.d.ts +23 -23
  39. package/dist/types/src/transaction/BeefParty.d.ts.map +1 -1
  40. package/dist/types/src/transaction/BeefTx.d.ts +5 -5
  41. package/dist/types/src/transaction/BeefTx.d.ts.map +1 -1
  42. package/dist/types/src/transaction/MerklePath.d.ts.map +1 -1
  43. package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
  44. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  45. package/dist/umd/bundle.js +1 -1
  46. package/docs/primitives.md +120 -9
  47. package/package.json +1 -1
  48. package/src/primitives/Schnorr.ts +95 -0
  49. package/src/primitives/__tests/Schnorr.test.ts +272 -0
  50. package/src/primitives/index.ts +1 -0
  51. package/src/totp/totp.ts +1 -1
  52. package/src/transaction/Beef.ts +351 -375
  53. package/src/transaction/BeefParty.ts +54 -58
  54. package/src/transaction/BeefTx.ts +128 -143
  55. package/src/transaction/MerklePath.ts +11 -11
  56. package/src/transaction/Transaction.ts +32 -32
@@ -1,10 +1,10 @@
1
- import MerklePath from "./MerklePath.js";
2
- import Transaction from "./Transaction.js";
3
- import ChainTracker from "./ChainTracker.js";
4
- import BeefTx from "./BeefTx.js";
5
- import { Reader, Writer, toHex, toArray } from "../primitives/utils.js"
1
+ import MerklePath from './MerklePath.js'
2
+ import Transaction from './Transaction.js'
3
+ import ChainTracker from './ChainTracker.js'
4
+ import BeefTx from './BeefTx.js'
5
+ import { Reader, Writer, toHex, toArray } from '../primitives/utils.js'
6
6
 
7
- export const BEEF_MAGIC = 4022206465 // 0100BEEF in LE order
7
+ 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
 
@@ -13,225 +13,211 @@ export type BeefVersion = undefined | 'V1' | 'V2'
13
13
  /*
14
14
  * BEEF standard: BRC-62: Background Evaluation Extended Format (BEEF) Transactions
15
15
  * https://github.com/bitcoin-sv/BRCs/blob/master/transactions/0062.md
16
- *
16
+ *
17
17
  * BUMP standard: BRC-74: BSV Unified Merkle Path (BUMP) Format
18
18
  * https://github.com/bitcoin-sv/BRCs/blob/master/transactions/0074.md
19
- *
19
+ *
20
20
  * A valid serialized BEEF is the cornerstone of Simplified Payment Validation (SPV)
21
21
  * where they are exchanged between two non-trusting parties to establish the
22
22
  * validity of a newly constructed bitcoin transaction and its inputs from prior
23
23
  * transactions.
24
- *
24
+ *
25
25
  * A `Beef` is fundamentally an list of `BUMP`s and a list of transactions.
26
- *
26
+ *
27
27
  * A `BUMP` is a partial merkle tree for a 'mined' bitcoin block.
28
28
  * It can therefore be used to prove the validity of transaction data
29
29
  * for each transaction txid whose merkle path is included in the tree.
30
- *
30
+ *
31
31
  * To be valid, the list of transactions must be sorted in dependency order:
32
32
  * oldest transaction first;
33
33
  * and each transaction must either
34
34
  * have a merkle path in one of the BUMPs, or
35
35
  * have all of its input transactions included in the list of transactions.
36
- *
36
+ *
37
37
  * The `Beef` class supports the construction of valid BEEFs by allowing BUMPs
38
38
  * (merkle paths) and transactions to be merged sequentially.
39
- *
39
+ *
40
40
  * The `Beef` class also extends the standard by supporting 'known' transactions.
41
41
  * A 'known' transaction is represented solely by its txid.
42
42
  * To become valid, all the 'known' transactions in a `Beef` must be replaced by full
43
43
  * transactions and merkle paths, if they are mined.
44
- *
44
+ *
45
45
  * The purpose of supporting 'known' transactions is that one or both parties
46
46
  * generating and exchanging BEEFs often possess partial knowledge of valid transactions
47
47
  * due to their history.
48
- *
48
+ *
49
49
  * A valid `Beef` is only required when sent to a party with no shared history,
50
50
  * such as a transaction processor.
51
51
  */
52
52
  export class Beef {
53
- bumps: MerklePath[] = []
54
- txs: BeefTx[] = []
55
- version: BeefVersion = undefined
53
+ bumps: MerklePath[] = []
54
+ txs: BeefTx[] = []
55
+ version: BeefVersion = undefined
56
56
 
57
- constructor(version?: BeefVersion) {
58
- this.version = version
59
- }
57
+ constructor (version?: BeefVersion) {
58
+ this.version = version
59
+ }
60
60
 
61
- /**
61
+ /**
62
62
  * BEEF_MAGIC is the original V1 version.
63
63
  * BEEF_MAGIC_V2 includes support for txidOnly transactions in serialized beefs.
64
64
  * @returns version magic value based on current contents and constructor version parameter.
65
65
  */
66
- get magic(): number {
67
- if (this.version === 'V1')
68
- return BEEF_MAGIC
66
+ get magic (): number {
67
+ if (this.version === 'V1') { return BEEF_MAGIC }
69
68
 
70
- if (this.version === 'V2')
71
- return BEEF_MAGIC_V2
69
+ if (this.version === 'V2') { return BEEF_MAGIC_V2 }
72
70
 
73
- const hasTxidOnly = -1 < this.txs.findIndex(tx => tx.isTxidOnly)
74
- if (hasTxidOnly)
75
- return BEEF_MAGIC_V2
71
+ const hasTxidOnly = this.txs.findIndex(tx => tx.isTxidOnly) > -1
72
+ if (hasTxidOnly) { return BEEF_MAGIC_V2 }
76
73
 
77
- return BEEF_MAGIC
78
- }
74
+ return BEEF_MAGIC
75
+ }
79
76
 
80
- /**
77
+ /**
81
78
  * @param txid of `beefTx` to find
82
79
  * @returns `BeefTx` in `txs` with `txid`.
83
80
  */
84
- findTxid(txid: string): BeefTx | undefined {
85
- return this.txs.find(tx => tx.txid === txid)
86
- }
81
+ findTxid (txid: string): BeefTx | undefined {
82
+ return this.txs.find(tx => tx.txid === txid)
83
+ }
87
84
 
88
- /**
85
+ /**
89
86
  * Merge a MerklePath that is assumed to be fully valid.
90
- * @param bump
87
+ * @param bump
91
88
  * @returns index of merged bump
92
89
  */
93
- mergeBump(bump: MerklePath): number {
94
- let bumpIndex: number | undefined = undefined
95
- // If this proof is identical to another one previously added, we use that first. Otherwise, we try to merge it with proofs from the same block.
96
- for (let i = 0; i < this.bumps.length; i++) {
97
- const b = this.bumps[i]
98
- if (b === bump) { // Literally the same
99
- return i
100
- }
101
- if (b.blockHeight === bump.blockHeight) {
102
- // Probably the same...
103
- const rootA = b.computeRoot()
104
- const rootB = bump.computeRoot()
105
- if (rootA === rootB) {
106
- // Definitely the same... combine them to save space
107
- b.combine(bump)
108
- bumpIndex = i
109
- break
110
- }
111
- }
90
+ mergeBump (bump: MerklePath): number {
91
+ let bumpIndex: number | undefined
92
+ // If this proof is identical to another one previously added, we use that first. Otherwise, we try to merge it with proofs from the same block.
93
+ for (let i = 0; i < this.bumps.length; i++) {
94
+ const b = this.bumps[i]
95
+ if (b === bump) { // Literally the same
96
+ return i
97
+ }
98
+ if (b.blockHeight === bump.blockHeight) {
99
+ // Probably the same...
100
+ const rootA = b.computeRoot()
101
+ const rootB = bump.computeRoot()
102
+ if (rootA === rootB) {
103
+ // Definitely the same... combine them to save space
104
+ b.combine(bump)
105
+ bumpIndex = i
106
+ break
112
107
  }
108
+ }
109
+ }
113
110
 
114
- // if the proof is not yet added, add a new path.
115
- if (bumpIndex === undefined) {
116
- bumpIndex = this.bumps.length
117
- this.bumps.push(bump)
118
- }
111
+ // if the proof is not yet added, add a new path.
112
+ if (bumpIndex === undefined) {
113
+ bumpIndex = this.bumps.length
114
+ this.bumps.push(bump)
115
+ }
119
116
 
120
- // review if any transactions are proven by this bump
121
- const b = this.bumps[bumpIndex]
122
- for (const tx of this.txs) {
123
- const txid = tx.txid
124
- if (!tx.bumpIndex) {
125
- for (const n of b.path[0]) {
126
- if (n.hash === txid) {
127
- tx.bumpIndex = bumpIndex
128
- n.txid = true
129
- break
130
- }
131
- }
132
- }
117
+ // review if any transactions are proven by this bump
118
+ const b = this.bumps[bumpIndex]
119
+ for (const tx of this.txs) {
120
+ const txid = tx.txid
121
+ if (!tx.bumpIndex) {
122
+ for (const n of b.path[0]) {
123
+ if (n.hash === txid) {
124
+ tx.bumpIndex = bumpIndex
125
+ n.txid = true
126
+ break
127
+ }
133
128
  }
134
-
135
- return bumpIndex
129
+ }
136
130
  }
137
131
 
138
- /**
132
+ return bumpIndex
133
+ }
134
+
135
+ /**
139
136
  * Merge a serialized transaction.
140
- *
137
+ *
141
138
  * Checks that a transaction with the same txid hasn't already been merged.
142
- *
139
+ *
143
140
  * Replaces existing transaction with same txid.
144
- *
145
- * @param rawTx
141
+ *
142
+ * @param rawTx
146
143
  * @param bumpIndex Optional. If a number, must be valid index into bumps array.
147
144
  * @returns txid of rawTx
148
145
  */
149
- mergeRawTx(rawTx: number[], bumpIndex?: number): BeefTx {
150
- const newTx: BeefTx = new BeefTx(rawTx, bumpIndex)
151
- this.removeExistingTxid(newTx.txid)
152
- this.txs.push(newTx)
153
- this.tryToValidateBumpIndex(newTx)
154
- return newTx
155
- }
156
-
157
- /**
146
+ mergeRawTx (rawTx: number[], bumpIndex?: number): BeefTx {
147
+ const newTx: BeefTx = new BeefTx(rawTx, bumpIndex)
148
+ this.removeExistingTxid(newTx.txid)
149
+ this.txs.push(newTx)
150
+ this.tryToValidateBumpIndex(newTx)
151
+ return newTx
152
+ }
153
+
154
+ /**
158
155
  * Merge a `Transaction` and any referenced `merklePath` and `sourceTransaction`, recursifely.
159
- *
156
+ *
160
157
  * Replaces existing transaction with same txid.
161
- *
158
+ *
162
159
  * Attempts to match an existing bump to the new transaction.
163
- *
164
- * @param tx
160
+ *
161
+ * @param tx
165
162
  * @returns txid of tx
166
163
  */
167
- mergeTransaction(tx: Transaction): BeefTx {
168
- const txid = tx.id('hex')
169
- this.removeExistingTxid(txid)
170
- let bumpIndex: number | undefined = undefined
171
- if (tx.merklePath)
172
- bumpIndex = this.mergeBump(tx.merklePath)
173
- const newTx = new BeefTx(tx, bumpIndex)
174
- this.txs.push(newTx)
175
- this.tryToValidateBumpIndex(newTx)
176
- bumpIndex = newTx.bumpIndex
177
- if (bumpIndex === undefined) {
178
- for (const input of tx.inputs) {
179
- if (input.sourceTransaction)
180
- this.mergeTransaction(input.sourceTransaction)
181
- }
182
- }
183
- return newTx
164
+ mergeTransaction (tx: Transaction): BeefTx {
165
+ const txid = tx.id('hex')
166
+ this.removeExistingTxid(txid)
167
+ let bumpIndex: number | undefined
168
+ if (tx.merklePath) { bumpIndex = this.mergeBump(tx.merklePath) }
169
+ const newTx = new BeefTx(tx, bumpIndex)
170
+ this.txs.push(newTx)
171
+ this.tryToValidateBumpIndex(newTx)
172
+ bumpIndex = newTx.bumpIndex
173
+ if (bumpIndex === undefined) {
174
+ for (const input of tx.inputs) {
175
+ if (input.sourceTransaction) { this.mergeTransaction(input.sourceTransaction) }
176
+ }
184
177
  }
178
+ return newTx
179
+ }
185
180
 
186
- /**
181
+ /**
187
182
  * Removes an existing transaction from the BEEF, given its TXID
188
183
  * @param txid TXID of the transaction to remove
189
184
  */
190
- removeExistingTxid(txid: string) {
191
- const existingTxIndex = this.txs.findIndex(t => t.txid === txid)
192
- if (existingTxIndex >= 0)
193
- this.txs.splice(existingTxIndex, 1)
185
+ removeExistingTxid (txid: string) {
186
+ const existingTxIndex = this.txs.findIndex(t => t.txid === txid)
187
+ if (existingTxIndex >= 0) { this.txs.splice(existingTxIndex, 1) }
188
+ }
189
+
190
+ mergeTxidOnly (txid: string): BeefTx {
191
+ if (this.version === 'V1') { throw new Error('BEEF V1 format does not support txid only transactions.') }
192
+
193
+ let tx = this.txs.find(t => t.txid === txid)
194
+ if (!tx) {
195
+ tx = new BeefTx(txid)
196
+ this.txs.push(tx)
197
+ this.tryToValidateBumpIndex(tx)
194
198
  }
199
+ return tx
200
+ }
195
201
 
196
- mergeTxidOnly(txid: string): BeefTx {
197
- if (this.version === 'V1')
198
- throw new Error(`BEEF V1 format does not support txid only transactions.`)
199
-
200
- let tx = this.txs.find(t => t.txid === txid)
201
- if (!tx) {
202
- tx = new BeefTx(txid)
203
- this.txs.push(tx)
204
- this.tryToValidateBumpIndex(tx)
205
- }
206
- return tx
202
+ mergeBeefTx (btx: BeefTx): BeefTx {
203
+ let beefTx = this.findTxid(btx.txid)
204
+ if (!beefTx && btx.isTxidOnly) { beefTx = this.mergeTxidOnly(btx.txid) } else if (!beefTx || beefTx.isTxidOnly) {
205
+ if (btx._tx) { beefTx = this.mergeTransaction(btx._tx) } else { beefTx = this.mergeRawTx(btx._rawTx) }
207
206
  }
207
+ return beefTx
208
+ }
208
209
 
209
- mergeBeefTx(btx: BeefTx): BeefTx {
210
- let beefTx = this.findTxid(btx.txid)
211
- if (!beefTx && btx.isTxidOnly)
212
- beefTx = this.mergeTxidOnly(btx.txid)
213
- else if (!beefTx || beefTx.isTxidOnly) {
214
- if (btx._tx)
215
- beefTx = this.mergeTransaction(btx._tx)
216
- else
217
- beefTx = this.mergeRawTx(btx._rawTx!)
218
- }
219
- return beefTx
220
- }
210
+ mergeBeef (beef: number[] | Beef) {
211
+ const b: Beef = Array.isArray(beef) ? Beef.fromBinary(beef) : beef
221
212
 
222
- mergeBeef(beef: number[] | Beef) {
223
- const b: Beef = Array.isArray(beef) ? Beef.fromBinary(beef) : beef
213
+ for (const bump of b.bumps) { this.mergeBump(bump) }
224
214
 
225
- for (const bump of b.bumps)
226
- this.mergeBump(bump)
215
+ for (const tx of b.txs) { this.mergeBeefTx(tx) }
216
+ }
227
217
 
228
- for (const tx of b.txs)
229
- this.mergeBeefTx(tx)
230
- }
231
-
232
- /**
218
+ /**
233
219
  * Sorts `txs` and checks structural validity of beef.
234
- *
220
+ *
235
221
  * Does NOT verify merkle roots.
236
222
  *
237
223
  * Validity requirements:
@@ -239,14 +225,14 @@ export class Beef {
239
225
  * 2. All transactions have bumps or their inputs chain back to bumps (or are known).
240
226
  * 3. Order of transactions satisfies dependencies before dependents.
241
227
  * 4. No transactions with duplicate txids.
242
- *
228
+ *
243
229
  * @param allowTxidOnly optional. If true, transaction txid only is assumed valid
244
230
  */
245
- isValid(allowTxidOnly?: boolean): boolean {
246
- return this.verifyValid(allowTxidOnly).valid
247
- }
231
+ isValid (allowTxidOnly?: boolean): boolean {
232
+ return this.verifyValid(allowTxidOnly).valid
233
+ }
248
234
 
249
- /**
235
+ /**
250
236
  * Sorts `txs` and confirms validity of transaction data contained in beef
251
237
  * by validating structure of this beef and confirming computed merkle roots
252
238
  * using `chainTracker`.
@@ -256,291 +242,281 @@ export class Beef {
256
242
  * 2. All transactions have bumps or their inputs chain back to bumps (or are known).
257
243
  * 3. Order of transactions satisfies dependencies before dependents.
258
244
  * 4. No transactions with duplicate txids.
259
- *
245
+ *
260
246
  * @param chainTracker Used to verify computed merkle path roots for all bump txids.
261
247
  * @param allowTxidOnly optional. If true, transaction txid is assumed valid
262
248
  */
263
- async verify(chainTracker: ChainTracker, allowTxidOnly?: boolean): Promise<boolean> {
264
- const r = this.verifyValid(allowTxidOnly)
265
- if (!r.valid) return false
266
-
267
- for (const height of Object.keys(r.roots)) {
268
- const isValid = await chainTracker.isValidRootForHeight(r.roots[height], Number(height))
269
- if (!isValid)
270
- return false
271
- }
249
+ async verify (chainTracker: ChainTracker, allowTxidOnly?: boolean): Promise<boolean> {
250
+ const r = this.verifyValid(allowTxidOnly)
251
+ if (!r.valid) return false
272
252
 
273
- return true
253
+ for (const height of Object.keys(r.roots)) {
254
+ const isValid = await chainTracker.isValidRootForHeight(r.roots[height], Number(height))
255
+ if (!isValid) { return false }
274
256
  }
275
257
 
276
- private verifyValid(allowTxidOnly?: boolean)
277
- : { valid: boolean, roots: Record<number, string> } {
278
-
279
- const r: { valid: boolean, roots: Record<number, string> } = { valid: false, roots: {} }
258
+ return true
259
+ }
280
260
 
281
- this.sortTxs()
261
+ private verifyValid (allowTxidOnly?: boolean): { valid: boolean, roots: Record<number, string> } {
262
+ const r: { valid: boolean, roots: Record<number, string> } = { valid: false, roots: {} }
282
263
 
283
- // valid txids: only txids if allowed, bump txids, then txids with input's in txids
284
- const txids: Record<string, boolean> = {}
264
+ this.sortTxs()
285
265
 
286
- for (const tx of this.txs) {
287
- if (tx.isTxidOnly) {
288
- if (!allowTxidOnly) return r
289
- txids[tx.txid] = true
290
- }
291
- }
266
+ // valid txids: only txids if allowed, bump txids, then txids with input's in txids
267
+ const txids: Record<string, boolean> = {}
292
268
 
293
- const confirmComputedRoot = (b: MerklePath, txid: string): boolean => {
294
- const root = b.computeRoot(txid)
295
- if (!r.roots[b.blockHeight]) {
296
- // accept the root as valid for this block and reuse for subsequent txids
297
- r.roots[b.blockHeight] = root
298
- }
299
- if (r.roots[b.blockHeight] !== root)
300
- return false
301
- return true
302
- }
269
+ for (const tx of this.txs) {
270
+ if (tx.isTxidOnly) {
271
+ if (!allowTxidOnly) return r
272
+ txids[tx.txid] = true
273
+ }
274
+ }
303
275
 
304
- for (const b of this.bumps) {
305
- for (const n of b.path[0]) {
306
- if (n.txid && n.hash) {
307
- txids[n.hash] = true
308
- // all txid hashes in all bumps must have agree on computed merkle path roots
309
- if (!confirmComputedRoot(b, n.hash))
310
- return r
311
- }
312
- }
313
- }
276
+ const confirmComputedRoot = (b: MerklePath, txid: string): boolean => {
277
+ const root = b.computeRoot(txid)
278
+ if (!r.roots[b.blockHeight]) {
279
+ // accept the root as valid for this block and reuse for subsequent txids
280
+ r.roots[b.blockHeight] = root
281
+ }
282
+ if (r.roots[b.blockHeight] !== root) { return false }
283
+ return true
284
+ }
314
285
 
315
- for (const t of this.txs) {
316
- for (const i of t.inputTxids)
317
- // all input txids must be included before they are referenced
318
- if (!txids[i]) return r
319
- txids[t.txid] = true
286
+ for (const b of this.bumps) {
287
+ for (const n of b.path[0]) {
288
+ if (n.txid && n.hash) {
289
+ txids[n.hash] = true
290
+ // all txid hashes in all bumps must have agree on computed merkle path roots
291
+ if (!confirmComputedRoot(b, n.hash)) { return r }
320
292
  }
293
+ }
294
+ }
321
295
 
322
- r.valid = true
323
- return r
296
+ for (const t of this.txs) {
297
+ for (const i of t.inputTxids)
298
+ // all input txids must be included before they are referenced
299
+ { if (!txids[i]) return r }
300
+ txids[t.txid] = true
324
301
  }
325
302
 
326
- /**
303
+ r.valid = true
304
+ return r
305
+ }
306
+
307
+ /**
327
308
  * Returns a binary array representing the serialized BEEF
328
309
  * @returns A binary array representing the BEEF
329
310
  */
330
- toBinary(): number[] {
311
+ toBinary (): number[] {
312
+ const writer = new Writer()
313
+ writer.writeUInt32LE(this.magic)
331
314
 
332
- const writer = new Writer()
333
- writer.writeUInt32LE(this.magic)
334
-
335
- writer.writeVarIntNum(this.bumps.length)
336
- for (const b of this.bumps) {
337
- writer.write(b.toBinary())
338
- }
339
-
340
- writer.writeVarIntNum(this.txs.length)
341
- for (const tx of this.txs) {
342
- tx.toWriter(writer, this.magic)
343
- }
315
+ writer.writeVarIntNum(this.bumps.length)
316
+ for (const b of this.bumps) {
317
+ writer.write(b.toBinary())
318
+ }
344
319
 
345
- return writer.toArray()
320
+ writer.writeVarIntNum(this.txs.length)
321
+ for (const tx of this.txs) {
322
+ tx.toWriter(writer, this.magic)
346
323
  }
347
324
 
348
- /**
325
+ return writer.toArray()
326
+ }
327
+
328
+ /**
349
329
  * Returns a hex string representing the serialized BEEF
350
330
  * @returns A hex string representing the BEEF
351
331
  */
352
- toHex(): string {
353
- return toHex(this.toBinary())
332
+ toHex (): string {
333
+ return toHex(this.toBinary())
334
+ }
335
+
336
+ static fromReader (br: Reader): Beef {
337
+ const version = br.readUInt32LE()
338
+ 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}`) }
339
+ const beef = new Beef(version === BEEF_MAGIC_V2 ? 'V2' : undefined)
340
+ const bumpsLength = br.readVarIntNum()
341
+ for (let i = 0; i < bumpsLength; i++) {
342
+ const bump = MerklePath.fromReader(br)
343
+ beef.bumps.push(bump)
354
344
  }
355
-
356
- static fromReader(br: Reader): Beef {
357
- const version = br.readUInt32LE()
358
- if (version !== BEEF_MAGIC && version !== BEEF_MAGIC_V2)
359
- throw new Error(`Serialized BEEF must start with ${BEEF_MAGIC} or ${BEEF_MAGIC_V2} but starts with ${version}`)
360
- const beef = new Beef(version === BEEF_MAGIC_V2 ? 'V2' : undefined)
361
- const bumpsLength = br.readVarIntNum()
362
- for (let i = 0; i < bumpsLength; i++) {
363
- const bump = MerklePath.fromReader(br)
364
- beef.bumps.push(bump)
365
- }
366
- const txsLength = br.readVarIntNum()
367
- for (let i = 0; i < txsLength; i++) {
368
- const beefTx = BeefTx.fromReader(br, version)
369
- beef.txs.push(beefTx)
370
- }
371
- return beef
345
+ const txsLength = br.readVarIntNum()
346
+ for (let i = 0; i < txsLength; i++) {
347
+ const beefTx = BeefTx.fromReader(br, version)
348
+ beef.txs.push(beefTx)
372
349
  }
350
+ return beef
351
+ }
373
352
 
374
- /**
353
+ /**
375
354
  * Constructs an instance of the Beef class based on the provided binary array
376
355
  * @param bin The binary array from which to construct BEEF
377
356
  * @returns An instance of the Beef class constructed from the binary data
378
357
  */
379
- static fromBinary(bin: number[]): Beef {
380
- const br = new Reader(bin)
381
- return Beef.fromReader(br)
382
- }
358
+ static fromBinary (bin: number[]): Beef {
359
+ const br = new Reader(bin)
360
+ return Beef.fromReader(br)
361
+ }
383
362
 
384
- /**
363
+ /**
385
364
  * Constructs an instance of the Beef class based on the provided string
386
365
  * @param s The string value from which to construct BEEF
387
366
  * @param enc The encoding of the string value from which BEEF should be constructed
388
367
  * @returns An instance of the Beef class constructed from the string
389
368
  */
390
- static fromString(s: string, enc?: 'hex' | 'utf8' | 'base64'): Beef {
391
- enc ||= 'hex'
392
- const bin = toArray(s, enc)
393
- const br = new Reader(bin)
394
- return Beef.fromReader(br)
395
- }
396
-
397
- /**
369
+ static fromString (s: string, enc?: 'hex' | 'utf8' | 'base64'): Beef {
370
+ enc ||= 'hex'
371
+ const bin = toArray(s, enc)
372
+ const br = new Reader(bin)
373
+ return Beef.fromReader(br)
374
+ }
375
+
376
+ /**
398
377
  * Try to validate newTx.bumpIndex by looking for an existing bump
399
378
  * that proves newTx.txid
400
- *
379
+ *
401
380
  * @param newTx A new `BeefTx` that has been added to this.txs
402
381
  * @returns true if a bump was found, false otherwise
403
382
  */
404
- private tryToValidateBumpIndex(newTx: BeefTx): boolean {
405
- if (newTx.bumpIndex !== undefined)
406
- return true
407
- const txid = newTx.txid
408
- for (let i = 0; i < this.bumps.length; i++) {
409
- const j = this.bumps[i].path[0].findIndex(b => b.hash === txid)
410
- if (j >= 0) {
411
- newTx.bumpIndex = i
412
- this.bumps[i].path[0][j].txid = true
413
- return true
414
- }
415
- }
416
- return false
383
+ private tryToValidateBumpIndex (newTx: BeefTx): boolean {
384
+ if (newTx.bumpIndex !== undefined) { return true }
385
+ const txid = newTx.txid
386
+ for (let i = 0; i < this.bumps.length; i++) {
387
+ const j = this.bumps[i].path[0].findIndex(b => b.hash === txid)
388
+ if (j >= 0) {
389
+ newTx.bumpIndex = i
390
+ this.bumps[i].path[0][j].txid = true
391
+ return true
392
+ }
417
393
  }
394
+ return false
395
+ }
418
396
 
419
- /**
397
+ /**
420
398
  * Sort the `txs` by input txid dependency order.
421
399
  * @returns array of input txids of unproven transactions that aren't included in txs.
422
400
  */
423
- sortTxs(): string[] {
424
- const missingInputs: Record<string, boolean> = {}
401
+ sortTxs (): string[] {
402
+ const missingInputs: Record<string, boolean> = {}
425
403
 
426
- const txidToTx: Record<string, BeefTx> = {}
404
+ const txidToTx: Record<string, BeefTx> = {}
427
405
 
428
- for (const tx of this.txs) {
429
- txidToTx[tx.txid] = tx
430
- // All transactions in this beef start at degree zero.
431
- tx.degree = 0
432
- }
406
+ for (const tx of this.txs) {
407
+ txidToTx[tx.txid] = tx
408
+ // All transactions in this beef start at degree zero.
409
+ tx.degree = 0
410
+ }
433
411
 
434
- for (const tx of this.txs) {
435
- if (tx.bumpIndex === undefined) {
436
- // For all the unproven transactions,
437
- // link their inputs that exist in this beef,
438
- // make a note of missing inputs.
439
- for (const inputTxid of tx.inputTxids) {
440
- if (!txidToTx[inputTxid])
441
- missingInputs[inputTxid] = true
442
- }
443
- }
412
+ for (const tx of this.txs) {
413
+ if (tx.bumpIndex === undefined) {
414
+ // For all the unproven transactions,
415
+ // link their inputs that exist in this beef,
416
+ // make a note of missing inputs.
417
+ for (const inputTxid of tx.inputTxids) {
418
+ if (!txidToTx[inputTxid]) { missingInputs[inputTxid] = true }
444
419
  }
420
+ }
421
+ }
445
422
 
446
- // queue of transactions that no unsorted transactions depend upon...
447
- const queue: BeefTx[] = []
448
- // sorted transactions
449
- const result: BeefTx[] = []
450
-
451
- // Increment each txid's degree for every input reference to it by another txid
452
- for (const tx of this.txs) {
453
- for (const inputTxid of tx.inputTxids) {
454
- const tx = txidToTx[inputTxid]
455
- if (tx)
456
- tx.degree++
457
- }
458
- }
459
- // Since circular dependencies aren't possible, start with the txids no one depends on.
460
- // These are the transactions that should be sent last...
461
- for (const tx of this.txs) {
462
- if (tx.degree === 0) {
463
- queue.push(tx)
464
- }
465
- }
466
- // As long as we have transactions to send...
467
- while (queue.length) {
468
- let tx = queue.shift()!
469
- // Add it as new first to send
470
- result.unshift(tx)
471
- // And remove its impact on degree
472
- // noting that any tx achieving a
473
- // value of zero can be sent...
474
- for (const inputTxid of tx.inputTxids) {
475
- const inputTx = txidToTx[inputTxid]
476
- if (inputTx) {
477
- inputTx.degree--
478
- if (inputTx.degree === 0) {
479
- queue.push(inputTx)
480
- }
481
- }
482
- }
423
+ // queue of transactions that no unsorted transactions depend upon...
424
+ const queue: BeefTx[] = []
425
+ // sorted transactions
426
+ const result: BeefTx[] = []
427
+
428
+ // Increment each txid's degree for every input reference to it by another txid
429
+ for (const tx of this.txs) {
430
+ for (const inputTxid of tx.inputTxids) {
431
+ const tx = txidToTx[inputTxid]
432
+ if (tx) { tx.degree++ }
433
+ }
434
+ }
435
+ // Since circular dependencies aren't possible, start with the txids no one depends on.
436
+ // These are the transactions that should be sent last...
437
+ for (const tx of this.txs) {
438
+ if (tx.degree === 0) {
439
+ queue.push(tx)
440
+ }
441
+ }
442
+ // As long as we have transactions to send...
443
+ while (queue.length > 0) {
444
+ const tx = queue.shift()!
445
+ // Add it as new first to send
446
+ result.unshift(tx)
447
+ // And remove its impact on degree
448
+ // noting that any tx achieving a
449
+ // value of zero can be sent...
450
+ for (const inputTxid of tx.inputTxids) {
451
+ const inputTx = txidToTx[inputTxid]
452
+ if (inputTx) {
453
+ inputTx.degree--
454
+ if (inputTx.degree === 0) {
455
+ queue.push(inputTx)
456
+ }
483
457
  }
484
- this.txs = result
485
-
486
- return Object.keys(missingInputs)
458
+ }
487
459
  }
460
+ this.txs = result
461
+
462
+ return Object.keys(missingInputs)
463
+ }
488
464
 
489
- /**
465
+ /**
490
466
  * @returns a shallow copy of this beef
491
467
  */
492
- clone(): Beef {
493
- const c = new Beef()
494
- c.bumps = Array.from(this.bumps)
495
- c.txs = Array.from(this.txs)
496
- return c
497
- }
498
-
499
- /**
468
+ clone (): Beef {
469
+ const c = new Beef()
470
+ c.bumps = Array.from(this.bumps)
471
+ c.txs = Array.from(this.txs)
472
+ return c
473
+ }
474
+
475
+ /**
500
476
  * Ensure that all the txids in `knownTxids` are txidOnly
501
- * @param knownTxids
477
+ * @param knownTxids
502
478
  */
503
- trimKnownTxids(knownTxids: string[]) {
504
- for (let i = 0; i < this.txs.length;) {
505
- const tx = this.txs[i]
506
- if (tx.isTxidOnly && -1 < knownTxids.indexOf(tx.txid)) {
507
- this.txs.splice(i, 1)
508
- } else {
509
- i++
510
- }
511
- }
512
- // TODO: bumps could be trimmed to eliminate unreferenced proofs.
479
+ trimKnownTxids (knownTxids: string[]) {
480
+ for (let i = 0; i < this.txs.length;) {
481
+ const tx = this.txs[i]
482
+ if (tx.isTxidOnly && knownTxids.includes(tx.txid)) {
483
+ this.txs.splice(i, 1)
484
+ } else {
485
+ i++
486
+ }
513
487
  }
488
+ // TODO: bumps could be trimmed to eliminate unreferenced proofs.
489
+ }
514
490
 
515
- /**
491
+ /**
516
492
  * @returns Summary of `Beef` contents as multi-line string.
517
493
  */
518
- toLogString(): string {
519
- let log = ''
520
- log += `BEEF with ${this.bumps.length} BUMPS and ${this.txs.length} Transactions, isValid ${this.isValid()}\n`
521
- let i = -1
522
- for (const b of this.bumps) {
523
- i++
524
- log += ` BUMP ${i}\n block: ${b.blockHeight}\n txids: [\n${b.path[0].filter(n => !!n.txid).map(n => ` '${n.hash}'`).join(',\n')}\n ]\n`
525
- }
526
- i = -1
527
- for (const t of this.txs) {
528
- i++
529
- log += ` TX ${i}\n txid: ${t.txid}\n`
530
- if (t.bumpIndex !== undefined) {
531
- log += ` bumpIndex: ${t.bumpIndex}\n`
532
- }
533
- if (t.isTxidOnly) {
534
- log += ` txidOnly\n`
535
- } else {
536
- log += ` rawTx length=${t.rawTx!.length}\n`
537
- }
538
- if (t.inputTxids.length > 0) {
539
- log += ` inputs: [\n${t.inputTxids.map(it => ` '${it}'`).join(',\n')}\n ]\n`
540
- }
541
- }
542
- return log
494
+ toLogString (): string {
495
+ let log = ''
496
+ log += `BEEF with ${this.bumps.length} BUMPS and ${this.txs.length} Transactions, isValid ${this.isValid()}\n`
497
+ let i = -1
498
+ for (const b of this.bumps) {
499
+ i++
500
+ log += ` BUMP ${i}\n block: ${b.blockHeight}\n txids: [\n${b.path[0].filter(n => !!n.txid).map(n => ` '${n.hash}'`).join(',\n')}\n ]\n`
501
+ }
502
+ i = -1
503
+ for (const t of this.txs) {
504
+ i++
505
+ log += ` TX ${i}\n txid: ${t.txid}\n`
506
+ if (t.bumpIndex !== undefined) {
507
+ log += ` bumpIndex: ${t.bumpIndex}\n`
508
+ }
509
+ if (t.isTxidOnly) {
510
+ log += ' txidOnly\n'
511
+ } else {
512
+ log += ` rawTx length=${t.rawTx.length}\n`
513
+ }
514
+ if (t.inputTxids.length > 0) {
515
+ log += ` inputs: [\n${t.inputTxids.map(it => ` '${it}'`).join(',\n')}\n ]\n`
516
+ }
543
517
  }
518
+ return log
519
+ }
544
520
  }
545
521
 
546
- export default Beef
522
+ export default Beef