@bsv/wallet-toolbox 1.3.27 → 1.3.29
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 +19 -0
- package/docs/client.md +550 -172
- package/docs/monitor.md +25 -7
- package/docs/services.md +134 -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 +2 -2
- package/mobile/out/src/services/ServiceCollection.d.ts.map +1 -1
- package/mobile/out/src/services/ServiceCollection.js.map +1 -1
- package/mobile/out/src/services/Services.d.ts.map +1 -1
- 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 +2 -2
- package/out/src/services/ServiceCollection.d.ts.map +1 -1
- package/out/src/services/ServiceCollection.js.map +1 -1
- package/out/src/services/Services.d.ts.map +1 -1
- 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 +9 -6
- package/src/services/Services.ts +13 -25
- 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()
|
|
@@ -92,7 +95,7 @@ export class ServiceCollection<T> {
|
|
|
92
95
|
this._historyByProvider[providerName] = h
|
|
93
96
|
}
|
|
94
97
|
h.calls.unshift(call)
|
|
95
|
-
h.calls = h.calls.slice(0,MAX_CALL_HISTORY)
|
|
98
|
+
h.calls = h.calls.slice(0, MAX_CALL_HISTORY)
|
|
96
99
|
h.totalCounts.until = now
|
|
97
100
|
h.resetCounts[0].until = now
|
|
98
101
|
return h
|
|
@@ -220,7 +223,7 @@ export interface ServiceCall {
|
|
|
220
223
|
/**
|
|
221
224
|
* Error code and message iff success is false and a exception was thrown.
|
|
222
225
|
*/
|
|
223
|
-
error?: { message: string
|
|
226
|
+
error?: { message: string; code: string }
|
|
224
227
|
}
|
|
225
228
|
|
|
226
229
|
export interface ServiceToCall<T> {
|
|
@@ -228,4 +231,4 @@ export interface ServiceToCall<T> {
|
|
|
228
231
|
serviceName: string
|
|
229
232
|
service: T
|
|
230
233
|
call: ServiceCall
|
|
231
|
-
}
|
|
234
|
+
}
|
package/src/services/Services.ts
CHANGED
|
@@ -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)
|
|
@@ -319,17 +313,13 @@ export class Services implements sdk.WalletServices {
|
|
|
319
313
|
r.rawTx = undefined
|
|
320
314
|
}
|
|
321
315
|
|
|
322
|
-
if (r.error)
|
|
323
|
-
|
|
324
|
-
else
|
|
325
|
-
services.addServiceCallSuccess(stc, `not found`)
|
|
326
|
-
else
|
|
327
|
-
services.addServiceCallFailure(stc)
|
|
316
|
+
if (r.error) services.addServiceCallError(stc, r.error)
|
|
317
|
+
else if (!r.rawTx) services.addServiceCallSuccess(stc, `not found`)
|
|
318
|
+
else services.addServiceCallFailure(stc)
|
|
328
319
|
|
|
329
320
|
if (r.error && !r0.error && !r0.rawTx)
|
|
330
321
|
// If we have an error and didn't before...
|
|
331
322
|
r0.error = r.error
|
|
332
|
-
|
|
333
323
|
} catch (eu: unknown) {
|
|
334
324
|
const e = sdk.WalletError.fromUnknown(eu)
|
|
335
325
|
services.addServiceCallError(stc, e)
|
|
@@ -401,11 +391,9 @@ export class Services implements sdk.WalletServices {
|
|
|
401
391
|
services.addServiceCallSuccess(stc)
|
|
402
392
|
break
|
|
403
393
|
}
|
|
404
|
-
|
|
405
|
-
if (r.error)
|
|
406
|
-
|
|
407
|
-
else
|
|
408
|
-
services.addServiceCallFailure(stc)
|
|
394
|
+
|
|
395
|
+
if (r.error) services.addServiceCallError(stc, r.error)
|
|
396
|
+
else services.addServiceCallFailure(stc)
|
|
409
397
|
|
|
410
398
|
if (r.error && !r0.error) {
|
|
411
399
|
// 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)
|
|
@@ -492,19 +492,44 @@ export class StorageKnex extends StorageProvider implements sdk.WalletStoragePro
|
|
|
492
492
|
if (args.orderDescending) {
|
|
493
493
|
let sortColumn = ''
|
|
494
494
|
switch (table) {
|
|
495
|
-
case 'certificates':
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
case '
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
case '
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
case '
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
495
|
+
case 'certificates':
|
|
496
|
+
sortColumn = 'certificateId'
|
|
497
|
+
break
|
|
498
|
+
case 'commissions':
|
|
499
|
+
sortColumn = 'commissionId'
|
|
500
|
+
break
|
|
501
|
+
case 'output_baskets':
|
|
502
|
+
sortColumn = 'basketId'
|
|
503
|
+
break
|
|
504
|
+
case 'outputs':
|
|
505
|
+
sortColumn = 'outputId'
|
|
506
|
+
break
|
|
507
|
+
case 'output_tags':
|
|
508
|
+
sortColumn = 'outputTagId'
|
|
509
|
+
break
|
|
510
|
+
case 'proven_tx_reqs':
|
|
511
|
+
sortColumn = 'provenTxReqId'
|
|
512
|
+
break
|
|
513
|
+
case 'proven_txs':
|
|
514
|
+
sortColumn = 'provenTxId'
|
|
515
|
+
break
|
|
516
|
+
case 'sync_states':
|
|
517
|
+
sortColumn = 'syncStateId'
|
|
518
|
+
break
|
|
519
|
+
case 'transactions':
|
|
520
|
+
sortColumn = 'transactionId'
|
|
521
|
+
break
|
|
522
|
+
case 'tx_labels':
|
|
523
|
+
sortColumn = 'txLabelId'
|
|
524
|
+
break
|
|
525
|
+
case 'users':
|
|
526
|
+
sortColumn = 'userId'
|
|
527
|
+
break
|
|
528
|
+
case 'monitor_events':
|
|
529
|
+
sortColumn = 'id'
|
|
530
|
+
break
|
|
531
|
+
default:
|
|
532
|
+
break
|
|
508
533
|
}
|
|
509
534
|
if (sortColumn !== '') {
|
|
510
535
|
q.orderBy(sortColumn, 'desc')
|
|
@@ -1156,7 +1181,13 @@ export class StorageKnex extends StorageProvider implements sdk.WalletStoragePro
|
|
|
1156
1181
|
async adminStats(adminIdentityKey: string): Promise<AdminStatsResult> {
|
|
1157
1182
|
if (this.dbtype !== 'MySQL') throw new sdk.WERR_NOT_IMPLEMENTED('adminStats, only MySQL is supported')
|
|
1158
1183
|
|
|
1159
|
-
const monitorEvent = verifyOneOrNone(
|
|
1184
|
+
const monitorEvent = verifyOneOrNone(
|
|
1185
|
+
await this.findMonitorEvents({
|
|
1186
|
+
partial: { event: 'MonitorCallHistory' },
|
|
1187
|
+
orderDescending: true,
|
|
1188
|
+
paged: { limit: 1 }
|
|
1189
|
+
})
|
|
1190
|
+
)
|
|
1160
1191
|
const monitorStats: ServicesCallHistory | undefined = monitorEvent ? JSON.parse(monitorEvent.details!) : undefined
|
|
1161
1192
|
const servicesStats = this.getServices().getServicesCallHistory(true)
|
|
1162
1193
|
await this.insertMonitorEvent({
|
|
@@ -603,7 +603,10 @@ async function validateRequiredInputs(
|
|
|
603
603
|
const output = verifyOneOrNone(await storage.findOutputs({ partial: { userId, txid, vout } }))
|
|
604
604
|
if (output) {
|
|
605
605
|
if (output.change) {
|
|
606
|
-
throw new sdk.WERR_INVALID_PARAMETER(
|
|
606
|
+
throw new sdk.WERR_INVALID_PARAMETER(
|
|
607
|
+
`inputs[${input.vin}]`,
|
|
608
|
+
'an unmanaged input. Change outputs are managed by your wallet.'
|
|
609
|
+
)
|
|
607
610
|
}
|
|
608
611
|
input.output = output
|
|
609
612
|
if (!Array.isArray(output.lockingScript) || !Number.isInteger(output.satoshis))
|