@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.
- package/CHANGELOG.md +24 -0
- package/docs/client.md +550 -172
- package/docs/monitor.md +25 -7
- package/docs/services.md +136 -23
- package/docs/storage.md +53 -22
- package/docs/wallet.md +550 -172
- package/mobile/out/src/monitor/Monitor.js +1 -1
- package/mobile/out/src/monitor/Monitor.js.map +1 -1
- package/mobile/out/src/sdk/WalletServices.interfaces.d.ts.map +1 -1
- package/mobile/out/src/services/ServiceCollection.d.ts +3 -2
- package/mobile/out/src/services/ServiceCollection.d.ts.map +1 -1
- package/mobile/out/src/services/ServiceCollection.js +5 -4
- package/mobile/out/src/services/ServiceCollection.js.map +1 -1
- package/mobile/out/src/services/Services.d.ts +1 -0
- package/mobile/out/src/services/Services.d.ts.map +1 -1
- package/mobile/out/src/services/Services.js +29 -7
- package/mobile/out/src/services/Services.js.map +1 -1
- package/mobile/out/src/services/createDefaultWalletServicesOptions.d.ts.map +1 -1
- package/mobile/out/src/services/createDefaultWalletServicesOptions.js +1 -3
- package/mobile/out/src/services/createDefaultWalletServicesOptions.js.map +1 -1
- package/mobile/out/src/signer/methods/completeSignedTransaction.d.ts +10 -0
- package/mobile/out/src/signer/methods/completeSignedTransaction.d.ts.map +1 -0
- package/mobile/out/src/signer/methods/completeSignedTransaction.js +104 -0
- package/mobile/out/src/signer/methods/completeSignedTransaction.js.map +1 -0
- package/mobile/out/src/signer/methods/createAction.d.ts +2 -3
- package/mobile/out/src/signer/methods/createAction.d.ts.map +1 -1
- package/mobile/out/src/signer/methods/createAction.js +8 -46
- package/mobile/out/src/signer/methods/createAction.js.map +1 -1
- package/mobile/out/src/signer/methods/signAction.d.ts +2 -4
- package/mobile/out/src/signer/methods/signAction.d.ts.map +1 -1
- package/mobile/out/src/signer/methods/signAction.js +7 -54
- package/mobile/out/src/signer/methods/signAction.js.map +1 -1
- package/mobile/out/src/storage/methods/createAction.d.ts.map +1 -1
- package/mobile/out/src/storage/methods/createAction.js.map +1 -1
- package/mobile/package-lock.json +2 -2
- package/mobile/package.json +1 -1
- package/out/src/monitor/Monitor.js +1 -1
- package/out/src/monitor/Monitor.js.map +1 -1
- package/out/src/sdk/WalletServices.interfaces.d.ts.map +1 -1
- package/out/src/services/ServiceCollection.d.ts +3 -2
- package/out/src/services/ServiceCollection.d.ts.map +1 -1
- package/out/src/services/ServiceCollection.js +5 -4
- package/out/src/services/ServiceCollection.js.map +1 -1
- package/out/src/services/Services.d.ts +1 -0
- package/out/src/services/Services.d.ts.map +1 -1
- package/out/src/services/Services.js +29 -7
- package/out/src/services/Services.js.map +1 -1
- package/out/src/services/createDefaultWalletServicesOptions.d.ts.map +1 -1
- package/out/src/services/createDefaultWalletServicesOptions.js +1 -3
- package/out/src/services/createDefaultWalletServicesOptions.js.map +1 -1
- package/out/src/signer/methods/completeSignedTransaction.d.ts +10 -0
- package/out/src/signer/methods/completeSignedTransaction.d.ts.map +1 -0
- package/out/src/signer/methods/completeSignedTransaction.js +104 -0
- package/out/src/signer/methods/completeSignedTransaction.js.map +1 -0
- package/out/src/signer/methods/createAction.d.ts +2 -3
- package/out/src/signer/methods/createAction.d.ts.map +1 -1
- package/out/src/signer/methods/createAction.js +8 -46
- package/out/src/signer/methods/createAction.js.map +1 -1
- package/out/src/signer/methods/signAction.d.ts +2 -4
- package/out/src/signer/methods/signAction.d.ts.map +1 -1
- package/out/src/signer/methods/signAction.js +7 -54
- package/out/src/signer/methods/signAction.js.map +1 -1
- package/out/src/storage/StorageKnex.d.ts.map +1 -1
- package/out/src/storage/StorageKnex.js +7 -2
- package/out/src/storage/StorageKnex.js.map +1 -1
- package/out/src/storage/methods/createAction.d.ts.map +1 -1
- package/out/src/storage/methods/createAction.js.map +1 -1
- package/out/test/Wallet/support/operations.man.test.js +23 -5
- package/out/test/Wallet/support/operations.man.test.js.map +1 -1
- package/out/test/wallet/action/createAction2.test.d.ts.map +1 -1
- package/out/test/wallet/action/createAction2.test.js +1 -0
- package/out/test/wallet/action/createAction2.test.js.map +1 -1
- package/out/tsconfig.all.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/monitor/Monitor.ts +1 -1
- package/src/sdk/WalletServices.interfaces.ts +2 -2
- package/src/services/ServiceCollection.ts +15 -10
- package/src/services/Services.ts +53 -42
- package/src/services/createDefaultWalletServicesOptions.ts +1 -4
- package/src/signer/methods/completeSignedTransaction.ts +115 -0
- package/src/signer/methods/createAction.ts +9 -68
- package/src/signer/methods/signAction.ts +11 -78
- package/src/storage/StorageKnex.ts +45 -14
- package/src/storage/StorageProvider.ts +1 -1
- package/src/storage/methods/createAction.ts +4 -1
- package/test/Wallet/support/operations.man.test.ts +29 -10
- package/test/wallet/action/createAction2.test.ts +1 -0
package/package.json
CHANGED
package/src/monitor/Monitor.ts
CHANGED
|
@@ -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)
|
|
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
|
|
542
|
+
error?: { message: string; code: string }
|
|
543
543
|
}
|
|
544
544
|
|
|
545
545
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { WalletError } from
|
|
2
|
-
import { ProviderCallHistory, ServiceCallHistory } from
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
+
}
|
package/src/services/Services.ts
CHANGED
|
@@ -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
|
-
|
|
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: '
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
-
|
|
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
|
-
|
|
324
|
-
else
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 :
|
|
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)
|