@bsv/sdk 1.1.30 → 1.1.33

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 (36) hide show
  1. package/dist/cjs/package.json +1 -1
  2. package/dist/cjs/src/transaction/Beef.js +195 -50
  3. package/dist/cjs/src/transaction/Beef.js.map +1 -1
  4. package/dist/cjs/src/transaction/BeefParty.js +16 -0
  5. package/dist/cjs/src/transaction/BeefParty.js.map +1 -1
  6. package/dist/cjs/src/transaction/BeefTx.js +6 -1
  7. package/dist/cjs/src/transaction/BeefTx.js.map +1 -1
  8. package/dist/cjs/src/transaction/Transaction.js +25 -10
  9. package/dist/cjs/src/transaction/Transaction.js.map +1 -1
  10. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  11. package/dist/esm/src/transaction/Beef.js +194 -49
  12. package/dist/esm/src/transaction/Beef.js.map +1 -1
  13. package/dist/esm/src/transaction/BeefParty.js +16 -0
  14. package/dist/esm/src/transaction/BeefParty.js.map +1 -1
  15. package/dist/esm/src/transaction/BeefTx.js +6 -1
  16. package/dist/esm/src/transaction/BeefTx.js.map +1 -1
  17. package/dist/esm/src/transaction/Transaction.js +25 -10
  18. package/dist/esm/src/transaction/Transaction.js.map +1 -1
  19. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  20. package/dist/types/src/transaction/Beef.d.ts +60 -4
  21. package/dist/types/src/transaction/Beef.d.ts.map +1 -1
  22. package/dist/types/src/transaction/BeefParty.d.ts +11 -0
  23. package/dist/types/src/transaction/BeefParty.d.ts.map +1 -1
  24. package/dist/types/src/transaction/BeefTx.d.ts +6 -1
  25. package/dist/types/src/transaction/BeefTx.d.ts.map +1 -1
  26. package/dist/types/src/transaction/Transaction.d.ts +8 -2
  27. package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
  28. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  29. package/dist/umd/bundle.js +1 -1
  30. package/docs/transaction.md +166 -9
  31. package/package.json +1 -1
  32. package/src/transaction/Beef.ts +221 -52
  33. package/src/transaction/BeefParty.ts +17 -0
  34. package/src/transaction/BeefTx.ts +6 -1
  35. package/src/transaction/Transaction.ts +25 -10
  36. package/src/transaction/__tests/Beef.test.ts +74 -15
@@ -18,7 +18,12 @@ export default class BeefTx {
18
18
  _rawTx?: number[]
19
19
  _txid?: string
20
20
  inputTxids: string[] = []
21
- degree: number = 0
21
+ /**
22
+ * true if `hasProof` or all inputs chain to `hasProof`.
23
+ *
24
+ * Typically set by sorting transactions by proven dependency chains.
25
+ */
26
+ isValid?: boolean = undefined
22
27
 
23
28
  get bumpIndex (): number | undefined { return this._bumpIndex }
24
29
 
@@ -138,7 +138,18 @@ export default class Transaction {
138
138
 
139
139
  // Ensure that all transactions are part of the dependency graph of the subject transaction
140
140
  const validTxids = new Set<string>()
141
+ // All BUMP level 0 hashes are valid.
142
+ for (const bump of BUMPs) {
143
+ for (const n of bump.path[0])
144
+ if (n.hash)
145
+ validTxids.add(n.hash)
146
+ }
147
+ // To keep track of which transactions were used.
148
+ const unusedTxTxids = new Set<string>()
149
+ for (const txid of Object.keys(transactions)) unusedTxTxids.add(txid)
150
+
141
151
  const traverseDependencies = (txid: string) => {
152
+ unusedTxTxids.delete(txid)
142
153
  if (validTxids.has(txid)) {
143
154
  return
144
155
  }
@@ -156,10 +167,8 @@ export default class Transaction {
156
167
  traverseDependencies(subjectTXID)
157
168
 
158
169
  // Check for any unrelated transactions
159
- for (const txid in transactions) {
160
- if (!validTxids.has(txid)) {
161
- throw new Error(`Unrelated transaction with TXID ${txid} found in Atomic BEEF data.`)
162
- }
170
+ for (const txid of unusedTxTxids) {
171
+ throw new Error(`Unrelated transaction with TXID ${txid} found in Atomic BEEF data.`)
163
172
  }
164
173
 
165
174
  // Build the transaction by linking inputs and merkle paths
@@ -859,10 +868,13 @@ export default class Transaction {
859
868
 
860
869
  /**
861
870
  * Serializes this transaction, together with its inputs and the respective merkle proofs, into the BEEF (BRC-62) format. This enables efficient verification of its compliance with the rules of SPV.
871
+ *
872
+ * @param allowPartial If true, error will not be thrown if there are any missing sourceTransactions.
862
873
  *
863
874
  * @returns The serialized BEEF structure
875
+ * @throws Error if there are any missing sourceTransactions unless `allowPartial` is true.
864
876
  */
865
- toBEEF (): number[] {
877
+ toBEEF (allowPartial?: boolean): number[] {
866
878
  const writer = new Writer()
867
879
  writer.writeUInt32LE(4022206465)
868
880
  const BUMPs: MerklePath[] = []
@@ -907,10 +919,10 @@ export default class Transaction {
907
919
  if (!hasProof) {
908
920
  for (let i = 0; i < tx.inputs.length; i++) {
909
921
  const input = tx.inputs[i]
910
- if (typeof input.sourceTransaction !== 'object') {
922
+ if (typeof input.sourceTransaction === 'object')
923
+ addPathsAndInputs(input.sourceTransaction)
924
+ else if (!allowPartial)
911
925
  throw new Error('A required source transaction is missing!')
912
- }
913
- addPathsAndInputs(input.sourceTransaction)
914
926
  }
915
927
  }
916
928
  }
@@ -940,16 +952,19 @@ export default class Transaction {
940
952
  * and then the BEEF data containing only the subject transaction and its dependencies.
941
953
  * This format ensures that the BEEF structure is atomic and contains no unrelated transactions.
942
954
  *
955
+ * @param allowPartial If true, error will not be thrown if there are any missing sourceTransactions.
956
+ *
943
957
  * @returns {number[]} - The serialized Atomic BEEF structure.
958
+ * @throws Error if there are any missing sourceTransactions unless `allowPartial` is true.
944
959
  */
945
- toAtomicBEEF (): number[] {
960
+ toAtomicBEEF (allowPartial?: boolean): number[] {
946
961
  const writer = new Writer()
947
962
  // Write the Atomic BEEF prefix
948
963
  writer.writeUInt32LE(0x01010101)
949
964
  // Write the subject TXID (big-endian)
950
965
  writer.write(this.id())
951
966
  // Append the BEEF data
952
- const beefData = this.toBEEF()
967
+ const beefData = this.toBEEF(allowPartial)
953
968
  writer.write(beefData)
954
969
  return writer.toArray()
955
970
  }
@@ -1,9 +1,17 @@
1
+ // The following imports allow flag a large number type checking errors by the VsCode editor due to missing type information:
1
2
  import BeefTx from "../../../dist/cjs/src/transaction/BeefTx"
2
3
  import Beef from "../../../dist/cjs/src/transaction/Beef"
3
4
  import BeefParty from "../../../dist/cjs/src/transaction/BeefParty"
4
5
  import { BEEF_MAGIC, BEEF_MAGIC_V2 } from "../../../dist/cjs/src/transaction/Beef"
5
6
  import Transaction from "../../../dist/cjs/src/transaction/Transaction"
6
7
 
8
+ // The following imports allow full type checking by the VsCode editor, but tests will fail to run:
9
+ //import BeefTx from '../BeefTx'
10
+ //import Beef from '../Beef'
11
+ //import BeefParty from "../BeefParty"
12
+ //import { BEEF_MAGIC, BEEF_MAGIC_V2 } from "../Beef"
13
+ //import Transaction from "../Transaction"
14
+
7
15
  describe('Beef tests', () => {
8
16
  jest.setTimeout(99999999)
9
17
 
@@ -56,8 +64,8 @@ describe('Beef tests', () => {
56
64
  expect(btx.rawTx).toBe(undefined)
57
65
  }
58
66
 
59
- const missing = beef.sortTxs()
60
- expect(missing.length).toBe(0)
67
+ const r = beef.sortTxs()
68
+ expect(r.missingInputs.length).toBe(0)
61
69
  expect(beef.toLogString()).toBe(log2)
62
70
 
63
71
  {
@@ -72,8 +80,8 @@ describe('Beef tests', () => {
72
80
  {
73
81
  const beef = new Beef()
74
82
  beef.mergeTransaction(Transaction.fromHex(txs[0]))
75
- const missing = beef.sortTxs()
76
- expect(missing).toEqual(['bd4a39c6dce3bdd982be3c67eb04b83934fd431f8bcb64f9da4413c91c634d07'])
83
+ const { missingInputs } = beef.sortTxs()
84
+ expect(missingInputs).toEqual(['bd4a39c6dce3bdd982be3c67eb04b83934fd431f8bcb64f9da4413c91c634d07'])
77
85
  const beef0 = Beef.fromString(beefs[0])
78
86
  beef.mergeBump(beef0.bumps[0])
79
87
  beef.mergeRawTx(beef0.txs[0].rawTx!, undefined)
@@ -93,7 +101,7 @@ describe('Beef tests', () => {
93
101
  }
94
102
  })
95
103
 
96
- test('4_all merkleRoots equal', async () => {
104
+ test('1_all merkleRoots equal', async () => {
97
105
  {
98
106
  const beef = Beef.fromString(beefs[0])
99
107
  expect(beef.isValid(undefined)).toBe(true)
@@ -107,7 +115,7 @@ describe('Beef tests', () => {
107
115
  }
108
116
  })
109
117
 
110
- test('4_allowTxidOnly', async () => {
118
+ test('2_allowTxidOnly', async () => {
111
119
  {
112
120
  const beef = Beef.fromString(beefs[0])
113
121
  expect(beef.isValid(undefined)).toBe(true)
@@ -117,7 +125,7 @@ describe('Beef tests', () => {
117
125
  }
118
126
  })
119
127
 
120
- test('4_removeExistingTxid', async () => {
128
+ test('3_removeExistingTxid', async () => {
121
129
  {
122
130
  const beef = Beef.fromString(beefs[0])
123
131
  expect(beef.isValid(undefined)).toBe(true)
@@ -157,7 +165,7 @@ describe('Beef tests', () => {
157
165
  }
158
166
  })
159
167
 
160
- test('4_mergeBeef', async () => {
168
+ test('5_mergeBeef', async () => {
161
169
  {
162
170
  const beef = Beef.fromString(beefs[0])
163
171
  const beefB = Beef.fromString(beefs[0])
@@ -193,7 +201,7 @@ describe('Beef tests', () => {
193
201
  }
194
202
  })
195
203
 
196
- test('4_BeefParty', async () => {
204
+ test('6_BeefParty', async () => {
197
205
 
198
206
  const bp = new BeefParty(['b', 'c'])
199
207
  expect(bp.isParty('a')).toBe(false)
@@ -215,10 +223,10 @@ describe('Beef tests', () => {
215
223
  const v = bp.getTrimmedBeefForParty('a').toLogString()
216
224
  expect(v).toBe(`BEEF with 0 BUMPS and 2 Transactions, isValid false
217
225
  TX 0
218
- txid: 4
226
+ txid: 3
219
227
  txidOnly
220
228
  TX 1
221
- txid: 3
229
+ txid: 4
222
230
  txidOnly
223
231
  `)
224
232
  }
@@ -226,10 +234,10 @@ describe('Beef tests', () => {
226
234
  const v = bp.getTrimmedBeefForParty('b').toLogString()
227
235
  expect(v).toBe(`BEEF with 0 BUMPS and 2 Transactions, isValid false
228
236
  TX 0
229
- txid: 2
237
+ txid: 1
230
238
  txidOnly
231
239
  TX 1
232
- txid: 1
240
+ txid: 2
233
241
  txidOnly
234
242
  `)
235
243
  }
@@ -237,15 +245,66 @@ describe('Beef tests', () => {
237
245
  const v = bp.getTrimmedBeefForParty('c').toLogString()
238
246
  expect(v).toBe(`BEEF with 0 BUMPS and 2 Transactions, isValid false
239
247
  TX 0
240
- txid: 4
248
+ txid: 1
241
249
  txidOnly
242
250
  TX 1
243
- txid: 1
251
+ txid: 4
244
252
  txidOnly
245
253
  `)
246
254
  }
247
255
  })
248
256
 
257
+ test('7_AtomicBeef', async () => {
258
+ {
259
+ const beef = Beef.fromString(beefs[0])
260
+ expect(beef.toHex()).toBe(beefs[0])
261
+ const sr = beef.sortTxs()
262
+ const beefHex = beef.toHex()
263
+ const tx = beef.txs[beef.txs.length - 1].tx!
264
+ expect(tx).toBeTruthy()
265
+ const atomic = tx.toAtomicBEEF(true)
266
+ // Verify that atomic BEEF can be deserialized.
267
+ const beef2 = Beef.fromBinary(atomic)
268
+ // The merkle path isn't linked to tx by default.
269
+ expect(beef2.toHex()).not.toBe(beefHex)
270
+ {
271
+ const atx = beef.findAtomicTransaction(tx.id('hex'))
272
+ const atomic = atx.toAtomicBEEF(true)
273
+ // Verify that atomic BEEF can be deserialized.
274
+ const beef2 = Beef.fromBinary(atomic)
275
+ // The merkle path now is linked to tx by default.
276
+ expect(beef2.toHex()).toBe(beefHex)
277
+ }
278
+ }
279
+ {
280
+ const beef = Beef.fromString(beefs[0])
281
+ expect(beef.toHex()).toBe(beefs[0])
282
+ beef.mergeTransaction(Transaction.fromHex(txs[0]))
283
+ const sr = beef.sortTxs()
284
+ const beefHex = beef.toHex()
285
+ const tx = beef.txs[beef.txs.length - 1].tx!
286
+ expect(tx).toBeTruthy()
287
+ const atx = beef.findAtomicTransaction(tx.id('hex'))
288
+ const atomic = atx.toAtomicBEEF()
289
+ // Verify that atomic BEEF can be deserialized.
290
+ const beef2 = Beef.fromBinary(atomic)
291
+ expect(beef2.toHex()).toBe(beefHex)
292
+ }
293
+ })
294
+
295
+ test('8_toBinaryAtomic', async () => {
296
+ {
297
+ const beef = Beef.fromString(beefs[0])
298
+ const tx = Transaction.fromHex(txs[0])
299
+ beef.mergeTransaction(tx)
300
+ const sr = beef.sortTxs()
301
+ const log = beef.toLogString()
302
+ const atomic = beef.toBinaryAtomic(tx.id('hex'))
303
+ const t2 = Transaction.fromAtomicBEEF(atomic)
304
+ const beef2 = t2.toAtomicBEEF()
305
+ expect(atomic).toEqual(beef2)
306
+ }
307
+ })
249
308
  })
250
309
 
251
310
  const txs: string[] = [