@bsv/wallet-toolbox 1.3.28 → 1.3.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 (87) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/docs/client.md +550 -172
  3. package/docs/monitor.md +25 -7
  4. package/docs/services.md +136 -23
  5. package/docs/storage.md +53 -22
  6. package/docs/wallet.md +550 -172
  7. package/mobile/out/src/monitor/Monitor.js +1 -1
  8. package/mobile/out/src/monitor/Monitor.js.map +1 -1
  9. package/mobile/out/src/sdk/WalletServices.interfaces.d.ts.map +1 -1
  10. package/mobile/out/src/services/ServiceCollection.d.ts +3 -2
  11. package/mobile/out/src/services/ServiceCollection.d.ts.map +1 -1
  12. package/mobile/out/src/services/ServiceCollection.js +5 -4
  13. package/mobile/out/src/services/ServiceCollection.js.map +1 -1
  14. package/mobile/out/src/services/Services.d.ts +1 -0
  15. package/mobile/out/src/services/Services.d.ts.map +1 -1
  16. package/mobile/out/src/services/Services.js +29 -7
  17. package/mobile/out/src/services/Services.js.map +1 -1
  18. package/mobile/out/src/services/createDefaultWalletServicesOptions.d.ts.map +1 -1
  19. package/mobile/out/src/services/createDefaultWalletServicesOptions.js +1 -3
  20. package/mobile/out/src/services/createDefaultWalletServicesOptions.js.map +1 -1
  21. package/mobile/out/src/signer/methods/completeSignedTransaction.d.ts +10 -0
  22. package/mobile/out/src/signer/methods/completeSignedTransaction.d.ts.map +1 -0
  23. package/mobile/out/src/signer/methods/completeSignedTransaction.js +104 -0
  24. package/mobile/out/src/signer/methods/completeSignedTransaction.js.map +1 -0
  25. package/mobile/out/src/signer/methods/createAction.d.ts +2 -3
  26. package/mobile/out/src/signer/methods/createAction.d.ts.map +1 -1
  27. package/mobile/out/src/signer/methods/createAction.js +8 -46
  28. package/mobile/out/src/signer/methods/createAction.js.map +1 -1
  29. package/mobile/out/src/signer/methods/signAction.d.ts +2 -4
  30. package/mobile/out/src/signer/methods/signAction.d.ts.map +1 -1
  31. package/mobile/out/src/signer/methods/signAction.js +7 -54
  32. package/mobile/out/src/signer/methods/signAction.js.map +1 -1
  33. package/mobile/out/src/storage/methods/createAction.d.ts.map +1 -1
  34. package/mobile/out/src/storage/methods/createAction.js.map +1 -1
  35. package/mobile/package-lock.json +2 -2
  36. package/mobile/package.json +1 -1
  37. package/out/src/monitor/Monitor.js +1 -1
  38. package/out/src/monitor/Monitor.js.map +1 -1
  39. package/out/src/sdk/WalletServices.interfaces.d.ts.map +1 -1
  40. package/out/src/services/ServiceCollection.d.ts +3 -2
  41. package/out/src/services/ServiceCollection.d.ts.map +1 -1
  42. package/out/src/services/ServiceCollection.js +5 -4
  43. package/out/src/services/ServiceCollection.js.map +1 -1
  44. package/out/src/services/Services.d.ts +1 -0
  45. package/out/src/services/Services.d.ts.map +1 -1
  46. package/out/src/services/Services.js +29 -7
  47. package/out/src/services/Services.js.map +1 -1
  48. package/out/src/services/createDefaultWalletServicesOptions.d.ts.map +1 -1
  49. package/out/src/services/createDefaultWalletServicesOptions.js +1 -3
  50. package/out/src/services/createDefaultWalletServicesOptions.js.map +1 -1
  51. package/out/src/signer/methods/completeSignedTransaction.d.ts +10 -0
  52. package/out/src/signer/methods/completeSignedTransaction.d.ts.map +1 -0
  53. package/out/src/signer/methods/completeSignedTransaction.js +104 -0
  54. package/out/src/signer/methods/completeSignedTransaction.js.map +1 -0
  55. package/out/src/signer/methods/createAction.d.ts +2 -3
  56. package/out/src/signer/methods/createAction.d.ts.map +1 -1
  57. package/out/src/signer/methods/createAction.js +8 -46
  58. package/out/src/signer/methods/createAction.js.map +1 -1
  59. package/out/src/signer/methods/signAction.d.ts +2 -4
  60. package/out/src/signer/methods/signAction.d.ts.map +1 -1
  61. package/out/src/signer/methods/signAction.js +7 -54
  62. package/out/src/signer/methods/signAction.js.map +1 -1
  63. package/out/src/storage/StorageKnex.d.ts.map +1 -1
  64. package/out/src/storage/StorageKnex.js +7 -2
  65. package/out/src/storage/StorageKnex.js.map +1 -1
  66. package/out/src/storage/methods/createAction.d.ts.map +1 -1
  67. package/out/src/storage/methods/createAction.js.map +1 -1
  68. package/out/test/Wallet/support/operations.man.test.js +23 -5
  69. package/out/test/Wallet/support/operations.man.test.js.map +1 -1
  70. package/out/test/wallet/action/createAction2.test.d.ts.map +1 -1
  71. package/out/test/wallet/action/createAction2.test.js +1 -0
  72. package/out/test/wallet/action/createAction2.test.js.map +1 -1
  73. package/out/tsconfig.all.tsbuildinfo +1 -1
  74. package/package.json +1 -1
  75. package/src/monitor/Monitor.ts +1 -1
  76. package/src/sdk/WalletServices.interfaces.ts +2 -2
  77. package/src/services/ServiceCollection.ts +15 -10
  78. package/src/services/Services.ts +53 -42
  79. package/src/services/createDefaultWalletServicesOptions.ts +1 -4
  80. package/src/signer/methods/completeSignedTransaction.ts +115 -0
  81. package/src/signer/methods/createAction.ts +9 -68
  82. package/src/signer/methods/signAction.ts +11 -78
  83. package/src/storage/StorageKnex.ts +45 -14
  84. package/src/storage/StorageProvider.ts +1 -1
  85. package/src/storage/methods/createAction.ts +4 -1
  86. package/test/Wallet/support/operations.man.test.ts +29 -10
  87. package/test/wallet/action/createAction2.test.ts +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsv/wallet-toolbox",
3
- "version": "1.3.28",
3
+ "version": "1.3.30",
4
4
  "description": "BRC100 conforming wallet, wallet storage and wallet signer components",
5
5
  "main": "./out/src/index.js",
6
6
  "types": "./out/src/index.d.ts",
@@ -221,7 +221,7 @@ export class Monitor {
221
221
  if (this.storage.getActive().isStorageProvider()) {
222
222
  const log = await ttr.runTask()
223
223
  if (log && log.length > 0) {
224
- console.log(`Task${ttr.name} ${log}`)
224
+ console.log(`Task${ttr.name} ${log.slice(0, 80)}`)
225
225
  await this.logEvent(ttr.name, log)
226
226
  }
227
227
  }
@@ -173,7 +173,7 @@ export interface WalletServices {
173
173
  * @param reset if true, ends current interval and starts a new one.
174
174
  * @returns a history of service calls made to the configured services.
175
175
  */
176
- getServicesCallHistory(reset?: boolean) : ServicesCallHistory
176
+ getServicesCallHistory(reset?: boolean): ServicesCallHistory
177
177
  }
178
178
 
179
179
  export type ScriptHashFormat = 'hashLE' | 'hashBE' | 'script'
@@ -539,7 +539,7 @@ export interface ServiceCall {
539
539
  /**
540
540
  * Error code and message iff success is false and a exception was thrown.
541
541
  */
542
- error?: { message: string, code: string }
542
+ error?: { message: string; code: string }
543
543
  }
544
544
 
545
545
  /**
@@ -1,5 +1,5 @@
1
- import { WalletError } from "../sdk/WalletError";
2
- import { ProviderCallHistory, ServiceCallHistory } from "../sdk/WalletServices.interfaces";
1
+ import { WalletError } from '../sdk/WalletError'
2
+ import { ProviderCallHistory, ServiceCallHistory } from '../sdk/WalletServices.interfaces'
3
3
 
4
4
  const MAX_RESET_COUNTS = 32
5
5
  const MAX_CALL_HISTORY = 32
@@ -14,7 +14,10 @@ export class ServiceCollection<T> {
14
14
  readonly since: Date
15
15
  _historyByProvider: Record<string, ProviderCallHistory> = {}
16
16
 
17
- constructor(public serviceName: string, services?: { name: string; service: T }[]) {
17
+ constructor(
18
+ public serviceName: string,
19
+ services?: { name: string; service: T }[]
20
+ ) {
18
21
  this.services = services || []
19
22
  this._index = 0
20
23
  this.since = new Date()
@@ -37,19 +40,21 @@ export class ServiceCollection<T> {
37
40
  return this.services[this._index].service
38
41
  }
39
42
 
40
- get serviceToCall(): ServiceToCall<T> {
41
- const i = this._index
43
+ getServiceToCall(i: number): ServiceToCall<T> {
42
44
  const name = this.services[i].name
43
45
  const service = this.services[i].service
44
46
  const call = { name, when: new Date(), msecs: 0, success: false, result: undefined, error: undefined }
45
47
  return { serviceName: this.serviceName, providerName: name, service, call }
46
48
  }
47
49
 
50
+ get serviceToCall(): ServiceToCall<T> {
51
+ return this.getServiceToCall(this._index)
52
+ }
53
+
48
54
  get allServicesToCall(): ServiceToCall<T>[] {
49
55
  const all: ServiceToCall<T>[] = []
50
56
  for (let i = 0; i < this.services.length; i++) {
51
- all.push(this.serviceToCall)
52
- this.next()
57
+ all.push(this.getServiceToCall(i))
53
58
  }
54
59
  return all
55
60
  }
@@ -92,7 +97,7 @@ export class ServiceCollection<T> {
92
97
  this._historyByProvider[providerName] = h
93
98
  }
94
99
  h.calls.unshift(call)
95
- h.calls = h.calls.slice(0,MAX_CALL_HISTORY)
100
+ h.calls = h.calls.slice(0, MAX_CALL_HISTORY)
96
101
  h.totalCounts.until = now
97
102
  h.resetCounts[0].until = now
98
103
  return h
@@ -220,7 +225,7 @@ export interface ServiceCall {
220
225
  /**
221
226
  * Error code and message iff success is false and a exception was thrown.
222
227
  */
223
- error?: { message: string, code: string }
228
+ error?: { message: string; code: string }
224
229
  }
225
230
 
226
231
  export interface ServiceToCall<T> {
@@ -228,4 +233,4 @@ export interface ServiceToCall<T> {
228
233
  serviceName: string
229
234
  service: T
230
235
  call: ServiceCall
231
- }
236
+ }
@@ -1,6 +1,6 @@
1
1
  import { Transaction as BsvTransaction, Beef, ChainTracker, Utils } from '@bsv/sdk'
2
2
  import { asArray, asString, doubleSha256BE, sdk, sha256Hash, TableOutput, wait } from '../index.client'
3
- import { ServiceCollection } from './ServiceCollection'
3
+ import { ServiceCollection, ServiceToCall } from './ServiceCollection'
4
4
  import { createDefaultWalletServicesOptions } from './createDefaultWalletServicesOptions'
5
5
  import { ChaintracksChainTracker } from './chaintracker'
6
6
  import { WhatsOnChain } from './providers/WhatsOnChain'
@@ -40,7 +40,7 @@ export class Services implements sdk.WalletServices {
40
40
 
41
41
  this.arcTaal = new ARC(this.options.arcUrl, this.options.arcConfig, 'arcTaal')
42
42
  if (this.options.arcGorillaPoolUrl) {
43
- //this.arcGorillaPool = new ARC(this.options.arcGorillaPoolUrl, this.options.arcGorillaPoolConfig, 'arcGorillaPool')
43
+ this.arcGorillaPool = new ARC(this.options.arcGorillaPoolUrl, this.options.arcGorillaPoolConfig, 'arcGorillaPool')
44
44
  }
45
45
 
46
46
  this.bitails = new Bitails(this.chain)
@@ -57,13 +57,13 @@ export class Services implements sdk.WalletServices {
57
57
  this.postBeefServices = new ServiceCollection<sdk.PostBeefService>('postBeef')
58
58
  if (this.arcGorillaPool) {
59
59
  //prettier-ignore
60
- this.postBeefServices.add({ name: 'GorillaPool', service: this.arcGorillaPool.postBeef.bind(this.arcGorillaPool) })
60
+ this.postBeefServices.add({ name: 'GorillaPoolArcBeef', service: this.arcGorillaPool.postBeef.bind(this.arcGorillaPool) })
61
61
  }
62
62
  //prettier-ignore
63
63
  this.postBeefServices
64
64
  .add({ name: 'TaalArcBeef', service: this.arcTaal.postBeef.bind(this.arcTaal) })
65
- .add({ name: 'WhatsOnChain', service: this.whatsonchain.postBeef.bind(this.whatsonchain) })
66
65
  .add({ name: 'Bitails', service: this.bitails.postBeef.bind(this.bitails) })
66
+ .add({ name: 'WhatsOnChain', service: this.whatsonchain.postBeef.bind(this.whatsonchain) })
67
67
  ;
68
68
 
69
69
  //prettier-ignore
@@ -84,7 +84,7 @@ export class Services implements sdk.WalletServices {
84
84
  .add({ name: 'exchangeratesapi', service: updateExchangeratesapi })
85
85
  }
86
86
 
87
- getServicesCallHistory(reset?: boolean) : ServicesCallHistory {
87
+ getServicesCallHistory(reset?: boolean): ServicesCallHistory {
88
88
  return {
89
89
  version: 2,
90
90
  getMerklePath: this.getMerklePathServices.getServiceCallHistory(reset),
@@ -155,10 +155,8 @@ export class Services implements sdk.WalletServices {
155
155
  r0 = r
156
156
  break
157
157
  } else {
158
- if (r.error)
159
- services.addServiceCallError(stc, r.error)
160
- else
161
- services.addServiceCallFailure(stc)
158
+ if (r.error) services.addServiceCallError(stc, r.error)
159
+ else services.addServiceCallFailure(stc)
162
160
  }
163
161
  } catch (eu: unknown) {
164
162
  const e = sdk.WalletError.fromUnknown(eu)
@@ -217,10 +215,8 @@ export class Services implements sdk.WalletServices {
217
215
  r0 = r
218
216
  break
219
217
  } else {
220
- if (r.error)
221
- services.addServiceCallError(stc, r.error)
222
- else
223
- services.addServiceCallFailure(stc)
218
+ if (r.error) services.addServiceCallError(stc, r.error)
219
+ else services.addServiceCallFailure(stc)
224
220
  }
225
221
  } catch (eu: unknown) {
226
222
  const e = sdk.WalletError.fromUnknown(eu)
@@ -253,10 +249,8 @@ export class Services implements sdk.WalletServices {
253
249
  r0 = r
254
250
  break
255
251
  } else {
256
- if (r.error)
257
- services.addServiceCallError(stc, r.error)
258
- else
259
- services.addServiceCallFailure(stc)
252
+ if (r.error) services.addServiceCallError(stc, r.error)
253
+ else services.addServiceCallFailure(stc)
260
254
  }
261
255
  } catch (eu: unknown) {
262
256
  const e = sdk.WalletError.fromUnknown(eu)
@@ -267,6 +261,8 @@ export class Services implements sdk.WalletServices {
267
261
  return r0
268
262
  }
269
263
 
264
+ postBeefMode: 'PromiseAll' | 'UntilSuccess' = 'UntilSuccess'
265
+
270
266
  /**
271
267
  *
272
268
  * @param beef
@@ -274,24 +270,45 @@ export class Services implements sdk.WalletServices {
274
270
  * @returns
275
271
  */
276
272
  async postBeef(beef: Beef, txids: string[]): Promise<sdk.PostBeefResult[]> {
273
+ let rs: sdk.PostBeefResult[] = []
277
274
  const services = this.postBeefServices
278
275
  const stcs = services.allServicesToCall
279
- let rs = await Promise.all(
280
- stcs.map(async stc => {
281
- const r = await stc.service(beef, txids)
282
- if (r.status === 'success') {
283
- services.addServiceCallSuccess(stc)
284
- } else {
285
- if (r.error) {
286
- services.addServiceCallError(stc, r.error)
287
- } else {
288
- services.addServiceCallFailure(stc)
276
+ switch (this.postBeefMode) {
277
+ case 'UntilSuccess':
278
+ {
279
+ for (const stc of stcs) {
280
+ const r = await callService(stc)
281
+ rs.push(r)
282
+ if (r.status === 'success') break
289
283
  }
290
284
  }
291
- return r
292
- })
293
- )
285
+ break
286
+ case 'PromiseAll':
287
+ {
288
+ rs = await Promise.all(
289
+ stcs.map(async stc => {
290
+ const r = await callService(stc)
291
+ return r
292
+ })
293
+ )
294
+ }
295
+ break
296
+ }
294
297
  return rs
298
+
299
+ async function callService(stc: ServiceToCall<sdk.PostBeefService>) {
300
+ const r = await stc.service(beef, txids)
301
+ if (r.status === 'success') {
302
+ services.addServiceCallSuccess(stc)
303
+ } else {
304
+ if (r.error) {
305
+ services.addServiceCallError(stc, r.error)
306
+ } else {
307
+ services.addServiceCallFailure(stc)
308
+ }
309
+ }
310
+ return r
311
+ }
295
312
  }
296
313
 
297
314
  async getRawTx(txid: string, useNext?: boolean): Promise<sdk.GetRawTxResult> {
@@ -319,17 +336,13 @@ export class Services implements sdk.WalletServices {
319
336
  r.rawTx = undefined
320
337
  }
321
338
 
322
- if (r.error)
323
- services.addServiceCallError(stc, r.error)
324
- else if (!r.rawTx)
325
- services.addServiceCallSuccess(stc, `not found`)
326
- else
327
- services.addServiceCallFailure(stc)
339
+ if (r.error) services.addServiceCallError(stc, r.error)
340
+ else if (!r.rawTx) services.addServiceCallSuccess(stc, `not found`)
341
+ else services.addServiceCallFailure(stc)
328
342
 
329
343
  if (r.error && !r0.error && !r0.rawTx)
330
344
  // If we have an error and didn't before...
331
345
  r0.error = r.error
332
-
333
346
  } catch (eu: unknown) {
334
347
  const e = sdk.WalletError.fromUnknown(eu)
335
348
  services.addServiceCallError(stc, e)
@@ -401,11 +414,9 @@ export class Services implements sdk.WalletServices {
401
414
  services.addServiceCallSuccess(stc)
402
415
  break
403
416
  }
404
-
405
- if (r.error)
406
- services.addServiceCallError(stc, r.error)
407
- else
408
- services.addServiceCallFailure(stc)
417
+
418
+ if (r.error) services.addServiceCallError(stc, r.error)
419
+ else services.addServiceCallFailure(stc)
409
420
 
410
421
  if (r.error && !r0.error) {
411
422
  // If we have an error and didn't before...
@@ -7,10 +7,7 @@ export function createDefaultWalletServicesOptions(chain: sdk.Chain): sdk.Wallet
7
7
  chain === 'main'
8
8
  ? 'mainnet_9596de07e92300c6287e4393594ae39c' // no plan
9
9
  : 'testnet_0e6cf72133b43ea2d7861da2a38684e3' // personal "starter" key
10
- const gorillaPoolApiKey =
11
- chain === 'main'
12
- ? ''
13
- : ''
10
+ const gorillaPoolApiKey = chain === 'main' ? '' : ''
14
11
 
15
12
  const o: sdk.WalletServicesOptions = {
16
13
  chain,
@@ -0,0 +1,115 @@
1
+ import { Beef, SignActionResult, SignActionSpend, Spend, Transaction } from '@bsv/sdk'
2
+ import { PendingSignAction, Wallet } from '../../Wallet'
3
+ import { asBsvSdkScript, ScriptTemplateBRC29, sdk } from '../../index.client'
4
+ import { WalletError, WERR_INTERNAL, WERR_INVALID_PARAMETER } from '../../sdk'
5
+
6
+ export async function completeSignedTransaction(
7
+ prior: PendingSignAction,
8
+ spends: Record<number, SignActionSpend>,
9
+ wallet: Wallet
10
+ ): Promise<Transaction> {
11
+ /////////////////////
12
+ // Insert the user provided unlocking scripts from "spends" arg
13
+ /////////////////////
14
+ for (const [key, spend] of Object.entries(spends)) {
15
+ const vin = Number(key)
16
+ const createInput = prior.args.inputs[vin]
17
+ const input = prior.tx.inputs[vin]
18
+ if (!createInput || !input || createInput.unlockingScript || !Number.isInteger(createInput.unlockingScriptLength))
19
+ throw new sdk.WERR_INVALID_PARAMETER(
20
+ 'args',
21
+ `spend does not correspond to prior input with valid unlockingScriptLength.`
22
+ )
23
+ if (spend.unlockingScript.length / 2 > createInput.unlockingScriptLength!)
24
+ throw new sdk.WERR_INVALID_PARAMETER(
25
+ 'args',
26
+ `spend unlockingScript length ${spend.unlockingScript.length} exceeds expected length ${createInput.unlockingScriptLength}`
27
+ )
28
+ input.unlockingScript = asBsvSdkScript(spend.unlockingScript)
29
+ if (spend.sequenceNumber !== undefined) input.sequence = spend.sequenceNumber
30
+ }
31
+
32
+ const results = {
33
+ sdk: <SignActionResult>{}
34
+ }
35
+
36
+ /////////////////////
37
+ // Insert SABPPP unlock templates for wallet signed inputs
38
+ /////////////////////
39
+ for (const pdi of prior.pdi) {
40
+ const sabppp = new ScriptTemplateBRC29({
41
+ derivationPrefix: pdi.derivationPrefix,
42
+ derivationSuffix: pdi.derivationSuffix,
43
+ keyDeriver: wallet.keyDeriver
44
+ })
45
+ const keys = wallet.getClientChangeKeyPair()
46
+ const lockerPrivKey = keys.privateKey
47
+ const unlockerPubKey = pdi.unlockerPubKey || keys.publicKey
48
+ const sourceSatoshis = pdi.sourceSatoshis
49
+ const lockingScript = asBsvSdkScript(pdi.lockingScript)
50
+ const unlockTemplate = sabppp.unlock(lockerPrivKey, unlockerPubKey, sourceSatoshis, lockingScript)
51
+ const input = prior.tx.inputs[pdi.vin]
52
+ input.unlockingScriptTemplate = unlockTemplate
53
+ }
54
+
55
+ /////////////////////
56
+ // Sign wallet signed inputs making transaction fully valid.
57
+ /////////////////////
58
+ await prior.tx.sign()
59
+
60
+ return prior.tx
61
+ }
62
+
63
+ /**
64
+ * @param txid The TXID of a transaction in the beef for which all unlocking scripts must be valid.
65
+ * @param beef Must contain transactions for txid and all its inputs.
66
+ * @throws WERR_INVALID_PARAMETER if any unlocking script is invalid, if sourceTXID is invalid, if beef doesn't contain required transactions.
67
+ */
68
+ export function verifyUnlockScripts(txid: string, beef: Beef): void {
69
+ const tx = beef.findTxid(txid)?.tx
70
+ if (!tx) throw new WERR_INVALID_PARAMETER(`txid`, `contained in beef, txid ${txid}`)
71
+
72
+ for (let i = 0; i < tx.inputs.length; i++) {
73
+ const input = tx.inputs[i]
74
+ if (!input.sourceTXID) throw new WERR_INVALID_PARAMETER(`inputs[${i}].sourceTXID`, `valid`)
75
+ if (!input.unlockingScript) throw new WERR_INVALID_PARAMETER(`inputs[${i}].unlockingScript`, `valid`)
76
+ input.sourceTransaction = beef.findTxid(input.sourceTXID)?.tx
77
+ if (!input.sourceTransaction) {
78
+ // The beef doesn't contain all the source transactions only if advanced features
79
+ // such as knownTxids are used.
80
+ // Skip unlock script checks.
81
+ return
82
+ // throw new WERR_INVALID_PARAMETER(`inputs[${i}].sourceTXID`, `contained in beef`)
83
+ }
84
+ }
85
+
86
+ for (let i = 0; i < tx.inputs.length; i++) {
87
+ const input = tx.inputs[i]
88
+ const sourceOutput = input.sourceTransaction!.outputs[input.sourceOutputIndex]
89
+
90
+ const otherInputs = tx.inputs.filter((_, idx) => idx !== i)
91
+
92
+ const spend = new Spend({
93
+ sourceTXID: input.sourceTXID!,
94
+ sourceOutputIndex: input.sourceOutputIndex,
95
+ lockingScript: sourceOutput.lockingScript,
96
+ sourceSatoshis: sourceOutput.satoshis ?? 0,
97
+ transactionVersion: tx.version,
98
+ otherInputs,
99
+ unlockingScript: input.unlockingScript!,
100
+ inputSequence: input.sequence ?? 0,
101
+ inputIndex: i,
102
+ outputs: tx.outputs,
103
+ lockTime: tx.lockTime
104
+ })
105
+
106
+ try {
107
+ const spendValid = spend.validate()
108
+
109
+ if (!spendValid) throw new WERR_INVALID_PARAMETER(`inputs[${i}].unlockScript`, `valid`)
110
+ } catch (eu: unknown) {
111
+ const e = WalletError.fromUnknown(eu)
112
+ throw new WERR_INVALID_PARAMETER(`inputs[${i}].unlockScript`, `valid. ${e.message}`)
113
+ }
114
+ }
115
+ }
@@ -5,22 +5,13 @@ import {
5
5
  OutpointString,
6
6
  SendWithResult,
7
7
  SignableTransaction,
8
- SignActionResult,
9
- SignActionSpend,
10
8
  TXIDHexString
11
9
  } from '@bsv/sdk'
12
10
  import { Script, Transaction } from '@bsv/sdk'
13
- import {
14
- asBsvSdkScript,
15
- makeAtomicBeef,
16
- PendingSignAction,
17
- ScriptTemplateBRC29,
18
- sdk,
19
- verifyTruthy,
20
- Wallet
21
- } from '../../index.client'
11
+ import { makeAtomicBeef, PendingSignAction, ScriptTemplateBRC29, sdk, verifyTruthy, Wallet } from '../../index.client'
22
12
  import { buildSignableTransaction } from './buildSignableTransaction'
23
13
  import { ReviewActionResult } from '../../sdk/WalletStorage.interfaces'
14
+ import { completeSignedTransaction, verifyUnlockScripts } from './completeSignedTransaction'
24
15
 
25
16
  export interface CreateActionResultX extends CreateActionResult {
26
17
  txid?: TXIDHexString
@@ -50,8 +41,14 @@ export async function createAction(
50
41
  prior.tx = await completeSignedTransaction(prior, {}, wallet)
51
42
 
52
43
  r.txid = prior.tx.id('hex')
44
+ const beef = new Beef()
45
+ if (prior.dcr.inputBeef) beef.mergeBeef(prior.dcr.inputBeef)
46
+ beef.mergeTransaction(prior.tx)
47
+
48
+ verifyUnlockScripts(r.txid, beef)
49
+
53
50
  r.noSendChange = prior.dcr.noSendChangeOutputVouts?.map(vout => `${r.txid}.${vout}`)
54
- if (!vargs.options.returnTXIDOnly) r.tx = makeAtomicBeef(prior.tx, prior.dcr.inputBeef!)
51
+ if (!vargs.options.returnTXIDOnly) r.tx = beef.toBinaryAtomic(r.txid)
55
52
  }
56
53
 
57
54
  const { sendWithResults, notDelayedResults } = await processAction(prior, wallet, auth, vargs)
@@ -131,62 +128,6 @@ export function makeChangeLock(
131
128
  return lockingScript
132
129
  }
133
130
 
134
- export async function completeSignedTransaction(
135
- prior: PendingSignAction,
136
- spends: Record<number, SignActionSpend>,
137
- wallet: Wallet
138
- ): Promise<Transaction> {
139
- /////////////////////
140
- // Insert the user provided unlocking scripts from "spends" arg
141
- /////////////////////
142
- for (const [key, spend] of Object.entries(spends)) {
143
- const vin = Number(key)
144
- const createInput = prior.args.inputs[vin]
145
- const input = prior.tx.inputs[vin]
146
- if (!createInput || !input || createInput.unlockingScript || !Number.isInteger(createInput.unlockingScriptLength))
147
- throw new sdk.WERR_INVALID_PARAMETER(
148
- 'args',
149
- `spend does not correspond to prior input with valid unlockingScriptLength.`
150
- )
151
- if (spend.unlockingScript.length / 2 > createInput.unlockingScriptLength!)
152
- throw new sdk.WERR_INVALID_PARAMETER(
153
- 'args',
154
- `spend unlockingScript length ${spend.unlockingScript.length} exceeds expected length ${createInput.unlockingScriptLength}`
155
- )
156
- input.unlockingScript = asBsvSdkScript(spend.unlockingScript)
157
- if (spend.sequenceNumber !== undefined) input.sequence = spend.sequenceNumber
158
- }
159
-
160
- const results = {
161
- sdk: <SignActionResult>{}
162
- }
163
-
164
- /////////////////////
165
- // Insert SABPPP unlock templates for storage signed inputs
166
- /////////////////////
167
- for (const pdi of prior.pdi) {
168
- const sabppp = new ScriptTemplateBRC29({
169
- derivationPrefix: pdi.derivationPrefix,
170
- derivationSuffix: pdi.derivationSuffix,
171
- keyDeriver: wallet.keyDeriver
172
- })
173
- const keys = wallet.getClientChangeKeyPair()
174
- const lockerPrivKey = keys.privateKey
175
- const unlockerPubKey = pdi.unlockerPubKey || keys.publicKey
176
- const sourceSatoshis = pdi.sourceSatoshis
177
- const lockingScript = asBsvSdkScript(pdi.lockingScript)
178
- const unlockTemplate = sabppp.unlock(lockerPrivKey, unlockerPubKey, sourceSatoshis, lockingScript)
179
- const input = prior.tx.inputs[pdi.vin]
180
- input.unlockingScriptTemplate = unlockTemplate
181
- }
182
-
183
- /////////////////////
184
- // Sign storage signed inputs making transaction fully valid.
185
- /////////////////////
186
- await prior.tx.sign()
187
-
188
- return prior.tx
189
- }
190
131
  function removeUnlockScripts(args: sdk.ValidCreateActionArgs) {
191
132
  let storageArgs = args
192
133
  if (!storageArgs.inputs.every(i => i.unlockingScript === undefined)) {
@@ -1,20 +1,10 @@
1
- /* eslint-disable @typescript-eslint/no-unused-vars */
2
-
3
- import {
4
- AtomicBEEF,
5
- Beef,
6
- Transaction as BsvTransaction,
7
- SendWithResult,
8
- SignActionArgs,
9
- SignActionOptions,
10
- SignActionResult,
11
- SignActionSpend,
12
- TXIDHexString
13
- } from '@bsv/sdk'
14
- import { asBsvSdkScript, PendingSignAction, ScriptTemplateBRC29, sdk, Wallet } from '../../index.client'
1
+ import { AtomicBEEF, Beef, SendWithResult, SignActionArgs, SignActionResult, TXIDHexString } from '@bsv/sdk'
2
+ import { sdk, Wallet } from '../../index.client'
15
3
  import { processAction } from './createAction'
16
4
  import { ReviewActionResult } from '../../sdk/WalletStorage.interfaces'
17
5
  import { validateSignActionArgs } from '../../sdk'
6
+ import { completeSignedTransaction, verifyUnlockScripts } from './completeSignedTransaction'
7
+ import { makeAtomicBeef } from '../../utility/utilityHelpers'
18
8
 
19
9
  export interface SignActionResultX extends SignActionResult {
20
10
  txid?: TXIDHexString
@@ -35,9 +25,15 @@ export async function signAction(wallet: Wallet, auth: sdk.AuthId, args: SignAct
35
25
 
36
26
  const { sendWithResults, notDelayedResults } = await processAction(prior, wallet, auth, vargs)
37
27
 
28
+ const txid = prior.tx.id('hex')
29
+ const beef = Beef.fromBinary(prior.dcr.inputBeef)
30
+ beef.mergeTransaction(prior.tx)
31
+
32
+ verifyUnlockScripts(txid, beef)
33
+
38
34
  const r: SignActionResultX = {
39
35
  txid: prior.tx.id('hex'),
40
- tx: vargs.options.returnTXIDOnly ? undefined : makeAtomicBeef(prior.tx, prior.dcr.inputBeef),
36
+ tx: vargs.options.returnTXIDOnly ? undefined : beef.toBinaryAtomic(txid),
41
37
  sendWithResults,
42
38
  notDelayedResults
43
39
  }
@@ -45,69 +41,6 @@ export async function signAction(wallet: Wallet, auth: sdk.AuthId, args: SignAct
45
41
  return r
46
42
  }
47
43
 
48
- export function makeAtomicBeef(tx: BsvTransaction, beef: number[] | Beef): number[] {
49
- if (Array.isArray(beef)) beef = Beef.fromBinary(beef)
50
- beef.mergeTransaction(tx)
51
- return beef.toBinaryAtomic(tx.id('hex'))
52
- }
53
-
54
- export async function completeSignedTransaction(
55
- prior: PendingSignAction,
56
- spends: Record<number, SignActionSpend>,
57
- wallet: Wallet
58
- ): Promise<BsvTransaction> {
59
- /////////////////////
60
- // Insert the user provided unlocking scripts from "spends" arg
61
- /////////////////////
62
- for (const [key, spend] of Object.entries(spends)) {
63
- const vin = Number(key)
64
- const createInput = prior.args.inputs[vin]
65
- const input = prior.tx.inputs[vin]
66
- if (!createInput || !input || createInput.unlockingScript || !Number.isInteger(createInput.unlockingScriptLength))
67
- throw new sdk.WERR_INVALID_PARAMETER(
68
- 'args',
69
- `spend does not correspond to prior input with valid unlockingScriptLength.`
70
- )
71
- if (spend.unlockingScript.length / 2 > createInput.unlockingScriptLength!)
72
- throw new sdk.WERR_INVALID_PARAMETER(
73
- 'args',
74
- `spend unlockingScript length ${spend.unlockingScript.length} exceeds expected length ${createInput.unlockingScriptLength}`
75
- )
76
- input.unlockingScript = asBsvSdkScript(spend.unlockingScript)
77
- if (spend.sequenceNumber !== undefined) input.sequence = spend.sequenceNumber
78
- }
79
-
80
- const results = {
81
- sdk: <SignActionResult>{}
82
- }
83
-
84
- /////////////////////
85
- // Insert SABPPP unlock templates for wallet signed inputs
86
- /////////////////////
87
- for (const pdi of prior.pdi) {
88
- const sabppp = new ScriptTemplateBRC29({
89
- derivationPrefix: pdi.derivationPrefix,
90
- derivationSuffix: pdi.derivationSuffix,
91
- keyDeriver: wallet.keyDeriver
92
- })
93
- const keys = wallet.getClientChangeKeyPair()
94
- const lockerPrivKey = keys.privateKey
95
- const unlockerPubKey = pdi.unlockerPubKey || keys.publicKey
96
- const sourceSatoshis = pdi.sourceSatoshis
97
- const lockingScript = asBsvSdkScript(pdi.lockingScript)
98
- const unlockTemplate = sabppp.unlock(lockerPrivKey, unlockerPubKey, sourceSatoshis, lockingScript)
99
- const input = prior.tx.inputs[pdi.vin]
100
- input.unlockingScriptTemplate = unlockTemplate
101
- }
102
-
103
- /////////////////////
104
- // Sign wallet signed inputs making transaction fully valid.
105
- /////////////////////
106
- await prior.tx.sign()
107
-
108
- return prior.tx
109
- }
110
-
111
44
  function mergePriorOptions(caVargs: sdk.ValidCreateActionArgs, saArgs: SignActionArgs): sdk.ValidSignActionArgs {
112
45
  const saOptions = (saArgs.options ||= {})
113
46
  if (saOptions.acceptDelayedBroadcast === undefined)