@bsv/wallet-toolbox 1.1.46 → 1.1.47
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/docs/client.md +112 -72
- package/docs/services.md +4 -3
- package/docs/storage.md +11 -24
- package/docs/wallet.md +112 -72
- package/out/src/Wallet.d.ts +3 -7
- package/out/src/Wallet.d.ts.map +1 -1
- package/out/src/Wallet.js +6 -5
- package/out/src/Wallet.js.map +1 -1
- package/out/src/monitor/tasks/TaskCheckForProofs.d.ts.map +1 -1
- package/out/src/monitor/tasks/TaskCheckForProofs.js +4 -2
- package/out/src/monitor/tasks/TaskCheckForProofs.js.map +1 -1
- package/out/src/sdk/WalletServices.interfaces.d.ts +3 -0
- package/out/src/sdk/WalletServices.interfaces.d.ts.map +1 -1
- package/out/src/sdk/types.d.ts +12 -0
- package/out/src/sdk/types.d.ts.map +1 -1
- package/out/src/services/Services.d.ts.map +1 -1
- package/out/src/services/Services.js +7 -2
- package/out/src/services/Services.js.map +1 -1
- package/out/src/services/providers/ARC.d.ts.map +1 -1
- package/out/src/services/providers/ARC.js +83 -14
- package/out/src/services/providers/ARC.js.map +1 -1
- package/out/src/services/providers/WhatsOnChain.d.ts +1 -1
- package/out/src/services/providers/WhatsOnChain.d.ts.map +1 -1
- package/out/src/services/providers/WhatsOnChain.js +119 -32
- package/out/src/services/providers/WhatsOnChain.js.map +1 -1
- package/out/src/services/providers/__tests/WhatsOnChain.test.js +4 -4
- package/out/src/services/providers/__tests/WhatsOnChain.test.js.map +1 -1
- package/out/src/signer/methods/buildSignableTransaction.d.ts +10 -0
- package/out/src/signer/methods/buildSignableTransaction.d.ts.map +1 -0
- package/out/src/signer/methods/buildSignableTransaction.js +125 -0
- package/out/src/signer/methods/buildSignableTransaction.js.map +1 -0
- package/out/src/signer/methods/createAction.d.ts +5 -1
- package/out/src/signer/methods/createAction.d.ts.map +1 -1
- package/out/src/signer/methods/createAction.js +8 -121
- package/out/src/signer/methods/createAction.js.map +1 -1
- package/out/src/storage/WalletStorageManager.d.ts +1 -0
- package/out/src/storage/WalletStorageManager.d.ts.map +1 -1
- package/out/src/storage/WalletStorageManager.js +3 -0
- package/out/src/storage/WalletStorageManager.js.map +1 -1
- package/out/src/storage/methods/attemptToPostReqsToNetwork.d.ts.map +1 -1
- package/out/src/storage/methods/attemptToPostReqsToNetwork.js +18 -1
- package/out/src/storage/methods/attemptToPostReqsToNetwork.js.map +1 -1
- package/out/src/storage/schema/entities/ProvenTx.d.ts.map +1 -1
- package/out/src/storage/schema/entities/ProvenTx.js +9 -20
- package/out/src/storage/schema/entities/ProvenTx.js.map +1 -1
- package/out/src/storage/schema/entities/ProvenTxReq.d.ts +4 -9
- package/out/src/storage/schema/entities/ProvenTxReq.d.ts.map +1 -1
- package/out/src/storage/schema/entities/ProvenTxReq.js +8 -5
- package/out/src/storage/schema/entities/ProvenTxReq.js.map +1 -1
- package/out/test/Wallet/local/localWallet.man.test.d.ts +2 -0
- package/out/test/Wallet/local/localWallet.man.test.d.ts.map +1 -0
- package/out/test/Wallet/local/localWallet.man.test.js +171 -0
- package/out/test/Wallet/local/localWallet.man.test.js.map +1 -0
- package/out/test/utils/TestUtilsWalletStorage.d.ts +5 -0
- package/out/test/utils/TestUtilsWalletStorage.d.ts.map +1 -1
- package/out/test/utils/TestUtilsWalletStorage.js +29 -18
- package/out/test/utils/TestUtilsWalletStorage.js.map +1 -1
- package/out/tsconfig.all.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/Wallet.ts +7 -9
- package/src/monitor/tasks/TaskCheckForProofs.ts +4 -2
- package/src/sdk/WalletServices.interfaces.ts +6 -0
- package/src/sdk/types.ts +11 -0
- package/src/services/Services.ts +5 -2
- package/src/services/providers/ARC.ts +84 -15
- package/src/services/providers/WhatsOnChain.ts +129 -41
- package/src/services/providers/__tests/WhatsOnChain.test.ts +4 -5
- package/src/signer/methods/buildSignableTransaction.ts +160 -0
- package/src/signer/methods/createAction.ts +8 -161
- package/src/storage/WalletStorageManager.ts +4 -0
- package/src/storage/methods/attemptToPostReqsToNetwork.ts +17 -1
- package/src/storage/schema/entities/ProvenTx.ts +10 -30
- package/src/storage/schema/entities/ProvenTxReq.ts +15 -17
- package/test/Wallet/local/localWallet.man.test.ts +208 -0
- package/test/utils/TestUtilsWalletStorage.ts +43 -19
- package/out/test/Wallet/local/localWallet.test.d.ts +0 -2
- package/out/test/Wallet/local/localWallet.test.d.ts.map +0 -1
- package/out/test/Wallet/local/localWallet.test.js +0 -14
- package/out/test/Wallet/local/localWallet.test.js.map +0 -1
- package/test/Wallet/local/localWallet.test.ts +0 -15
package/package.json
CHANGED
package/src/Wallet.ts
CHANGED
|
@@ -888,15 +888,13 @@ export class Wallet implements WalletInterface, ProtoWallet {
|
|
|
888
888
|
/**
|
|
889
889
|
* Uses `listOutputs` to iterate through all spendable outputs in the 'default' (change) basket.
|
|
890
890
|
*
|
|
891
|
+
* Outputs in the 'default' basket are managed by the wallet and MUST NOT USED AS UNMANAGED INPUTS.
|
|
892
|
+
*
|
|
891
893
|
* @param {string} basket - Optional. Defaults to 'default', the wallet change basket.
|
|
892
894
|
* @returns { total: number, utxos: { satoshis: number, outpoint: string }[] }
|
|
893
895
|
*/
|
|
894
|
-
async balance(basket: string = 'default'): Promise<{
|
|
895
|
-
total:
|
|
896
|
-
utxos: { satoshis: number; outpoint: string }[]
|
|
897
|
-
}> {
|
|
898
|
-
let total = 0
|
|
899
|
-
const utxos: { satoshis: number; outpoint: string }[] = []
|
|
896
|
+
async balance(basket: string = 'default'): Promise<sdk.WalletBalance> {
|
|
897
|
+
const r: sdk.WalletBalance = { total: 0, utxos: [] }
|
|
900
898
|
let offset = 0
|
|
901
899
|
for (;;) {
|
|
902
900
|
const change = await this.listOutputs({
|
|
@@ -906,12 +904,12 @@ export class Wallet implements WalletInterface, ProtoWallet {
|
|
|
906
904
|
})
|
|
907
905
|
if (change.totalOutputs === 0) break
|
|
908
906
|
for (const o of change.outputs) {
|
|
909
|
-
total += o.satoshis
|
|
910
|
-
utxos.push({ satoshis: o.satoshis, outpoint: o.outpoint })
|
|
907
|
+
r.total += o.satoshis
|
|
908
|
+
r.utxos.push({ satoshis: o.satoshis, outpoint: o.outpoint })
|
|
911
909
|
}
|
|
912
910
|
offset += change.outputs.length
|
|
913
911
|
}
|
|
914
|
-
return
|
|
912
|
+
return r
|
|
915
913
|
}
|
|
916
914
|
}
|
|
917
915
|
|
|
@@ -197,6 +197,8 @@ export class TaskCheckForProofs extends WalletMonitorTask {
|
|
|
197
197
|
|
|
198
198
|
if (ptx) {
|
|
199
199
|
// We have a merklePath proof for the request (and a block header)
|
|
200
|
+
await req.updateStorageDynamicProperties(this.storage)
|
|
201
|
+
await req.refreshFromStorage(this.storage)
|
|
200
202
|
const { provenTxReqId, status, txid, attempts, history } = req.toApi()
|
|
201
203
|
const { index, height, blockHash, merklePath, merkleRoot } = ptx.toApi()
|
|
202
204
|
const r = await this.storage.runAsStorageProvider(async sp => {
|
|
@@ -221,9 +223,9 @@ export class TaskCheckForProofs extends WalletMonitorTask {
|
|
|
221
223
|
if (countsAsAttempt && req.status !== 'nosend') {
|
|
222
224
|
req.attempts++
|
|
223
225
|
}
|
|
224
|
-
await req.updateStorageDynamicProperties(this.storage)
|
|
225
|
-
await req.refreshFromStorage(this.storage)
|
|
226
226
|
}
|
|
227
|
+
await req.updateStorageDynamicProperties(this.storage)
|
|
228
|
+
await req.refreshFromStorage(this.storage)
|
|
227
229
|
|
|
228
230
|
log += req.historyPretty(since, indent + 2) + '\n'
|
|
229
231
|
|
|
@@ -207,6 +207,8 @@ export interface GetMerklePathResult {
|
|
|
207
207
|
* The first exception error that occurred during processing, if any.
|
|
208
208
|
*/
|
|
209
209
|
error?: sdk.WalletError
|
|
210
|
+
|
|
211
|
+
notes?: sdk.ReqHistoryNote[]
|
|
210
212
|
}
|
|
211
213
|
|
|
212
214
|
export interface PostTxResultForTxid {
|
|
@@ -237,6 +239,8 @@ export interface PostTxResultForTxid {
|
|
|
237
239
|
competingTxs?: string[]
|
|
238
240
|
|
|
239
241
|
data?: object | string | PostTxResultForTxidError
|
|
242
|
+
|
|
243
|
+
notes?: sdk.ReqHistoryNote[]
|
|
240
244
|
}
|
|
241
245
|
|
|
242
246
|
export interface PostTxResultForTxidError {
|
|
@@ -269,6 +273,8 @@ export interface PostTxsResult {
|
|
|
269
273
|
* Service response object. Use service name and status to infer type of object.
|
|
270
274
|
*/
|
|
271
275
|
data?: object
|
|
276
|
+
|
|
277
|
+
notes?: sdk.ReqHistoryNote[]
|
|
272
278
|
}
|
|
273
279
|
|
|
274
280
|
export interface GetUtxoStatusDetails {
|
package/src/sdk/types.ts
CHANGED
|
@@ -120,3 +120,14 @@ export interface ScriptTemplateUnlock {
|
|
|
120
120
|
sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>
|
|
121
121
|
estimateLength: (tx: Transaction, inputIndex: number) => Promise<number>
|
|
122
122
|
}
|
|
123
|
+
|
|
124
|
+
export interface WalletBalance {
|
|
125
|
+
total: number
|
|
126
|
+
utxos: { satoshis: number; outpoint: string }[]
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export type ReqHistoryNote = {
|
|
130
|
+
when?: string
|
|
131
|
+
what: string
|
|
132
|
+
[key: string]: string | number | undefined
|
|
133
|
+
}
|
package/src/services/Services.ts
CHANGED
|
@@ -272,11 +272,13 @@ export class Services implements sdk.WalletServices {
|
|
|
272
272
|
): Promise<sdk.GetMerklePathResult> {
|
|
273
273
|
if (useNext) this.getMerklePathServices.next()
|
|
274
274
|
|
|
275
|
-
const r0: sdk.GetMerklePathResult = {}
|
|
275
|
+
const r0: sdk.GetMerklePathResult = { notes: [] }
|
|
276
276
|
|
|
277
277
|
for (let tries = 0; tries < this.getMerklePathServices.count; tries++) {
|
|
278
278
|
const service = this.getMerklePathServices.service
|
|
279
279
|
const r = await service(txid, this)
|
|
280
|
+
if (r.notes) r0.notes!.push(...r.notes)
|
|
281
|
+
if (!r0.name) r0.name = r.name
|
|
280
282
|
if (r.merklePath) {
|
|
281
283
|
// If we have a proof, call it done.
|
|
282
284
|
r0.merklePath = r.merklePath
|
|
@@ -284,9 +286,10 @@ export class Services implements sdk.WalletServices {
|
|
|
284
286
|
r0.name = r.name
|
|
285
287
|
r0.error = undefined
|
|
286
288
|
break
|
|
287
|
-
} else if (r.error && !r0.error)
|
|
289
|
+
} else if (r.error && !r0.error) {
|
|
288
290
|
// If we have an error and didn't before...
|
|
289
291
|
r0.error = r.error
|
|
292
|
+
}
|
|
290
293
|
|
|
291
294
|
this.getMerklePathServices.next()
|
|
292
295
|
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
Utils
|
|
11
11
|
} from '@bsv/sdk'
|
|
12
12
|
import { doubleSha256BE, sdk } from '../../index.client'
|
|
13
|
+
import { ReqHistoryNote } from '../../sdk'
|
|
13
14
|
|
|
14
15
|
/** Configuration options for the ARC broadcaster. */
|
|
15
16
|
export interface ArcConfig {
|
|
@@ -135,6 +136,8 @@ export default class ARC {
|
|
|
135
136
|
let txid = Utils.toHex(doubleSha256BE(Utils.toArray(rawTx, 'hex')))
|
|
136
137
|
if (txids) {
|
|
137
138
|
txid = txids.slice(-1)[0]
|
|
139
|
+
} else {
|
|
140
|
+
txids = [txid]
|
|
138
141
|
}
|
|
139
142
|
|
|
140
143
|
const requestOptions: HttpClientRequestOptions = {
|
|
@@ -145,17 +148,29 @@ export default class ARC {
|
|
|
145
148
|
|
|
146
149
|
const r: sdk.PostTxResultForTxid = {
|
|
147
150
|
txid,
|
|
148
|
-
status: 'success'
|
|
151
|
+
status: 'success',
|
|
152
|
+
notes: []
|
|
149
153
|
}
|
|
150
154
|
|
|
155
|
+
const url = `${this.URL}/v1/tx`
|
|
156
|
+
const nn = () => ({ name: 'ARCv1tx', when: new Date().toISOString() })
|
|
157
|
+
const nne = () => ({ ...nn(), rawTx, txids: txids.join(','), url })
|
|
158
|
+
|
|
151
159
|
try {
|
|
152
160
|
const response = await this.httpClient.request<ArcResponse>(
|
|
153
|
-
|
|
161
|
+
url,
|
|
154
162
|
requestOptions
|
|
155
163
|
)
|
|
156
164
|
|
|
165
|
+
const { txid, extraInfo, txStatus, competingTxs } = response.data
|
|
166
|
+
const nnr = () => ({
|
|
167
|
+
txid,
|
|
168
|
+
extraInfo,
|
|
169
|
+
txStatus,
|
|
170
|
+
competingTxs: competingTxs?.join(',')
|
|
171
|
+
})
|
|
172
|
+
|
|
157
173
|
if (response.ok) {
|
|
158
|
-
const { txid, extraInfo, txStatus, competingTxs } = response.data
|
|
159
174
|
r.data = `${txStatus} ${extraInfo}`
|
|
160
175
|
if (r.txid !== txid) r.data += ` txid altered from ${r.txid} to ${txid}`
|
|
161
176
|
r.txid = txid
|
|
@@ -163,35 +178,59 @@ export default class ARC {
|
|
|
163
178
|
r.status = 'error'
|
|
164
179
|
r.doubleSpend = true
|
|
165
180
|
r.competingTxs = competingTxs
|
|
181
|
+
r.notes!.push({ ...nne(), ...nnr(), what: 'postRawTxDoubleSpend' })
|
|
182
|
+
} else {
|
|
183
|
+
r.notes!.push({ ...nn(), ...nnr(), what: 'postRawTxSuccess' })
|
|
166
184
|
}
|
|
185
|
+
} else if (typeof response === 'string') {
|
|
186
|
+
r.notes!.push({ ...nn(), what: 'postRawTxString', response })
|
|
167
187
|
} else {
|
|
168
188
|
r.status = 'error'
|
|
189
|
+
const n: ReqHistoryNote = {
|
|
190
|
+
...nn(),
|
|
191
|
+
...nne(),
|
|
192
|
+
...nnr(),
|
|
193
|
+
what: 'postRawTxError'
|
|
194
|
+
}
|
|
169
195
|
const ed: sdk.PostTxResultForTxidError = {}
|
|
170
196
|
r.data = ed
|
|
171
197
|
const st = typeof response.status
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
198
|
+
if (st === 'number' || st === 'string') {
|
|
199
|
+
n.status = response.status
|
|
200
|
+
ed.status = response.status.toString()
|
|
201
|
+
} else {
|
|
202
|
+
n.status = st
|
|
203
|
+
ed.status = 'ERR_UNKNOWN'
|
|
204
|
+
}
|
|
176
205
|
|
|
177
206
|
let d = response.data
|
|
178
207
|
if (d && typeof d === 'string') {
|
|
208
|
+
n.data = response.data.slice(0, 128)
|
|
179
209
|
try {
|
|
180
210
|
d = JSON.parse(d)
|
|
181
211
|
} catch {
|
|
182
212
|
// Intentionally left empty
|
|
183
213
|
}
|
|
184
|
-
}
|
|
185
|
-
if (d && typeof d === 'object') {
|
|
214
|
+
} else if (d && typeof d === 'object') {
|
|
186
215
|
ed.more = d
|
|
187
216
|
ed.detail = d['detail']
|
|
188
217
|
if (typeof ed.detail !== 'string') ed.detail = undefined
|
|
218
|
+
if (ed.detail) {
|
|
219
|
+
n.detail = ed.detail
|
|
220
|
+
}
|
|
189
221
|
}
|
|
222
|
+
r.notes!.push(n)
|
|
190
223
|
}
|
|
191
224
|
} catch (eu: unknown) {
|
|
192
225
|
const e = sdk.WalletError.fromUnknown(eu)
|
|
193
226
|
r.status = 'error'
|
|
194
227
|
r.data = `${e.code} ${e.message}`
|
|
228
|
+
r.notes!.push({
|
|
229
|
+
...nne(),
|
|
230
|
+
what: 'postRawTxCatch',
|
|
231
|
+
code: e.code,
|
|
232
|
+
description: e.description
|
|
233
|
+
})
|
|
195
234
|
}
|
|
196
235
|
|
|
197
236
|
return r
|
|
@@ -208,38 +247,68 @@ export default class ARC {
|
|
|
208
247
|
* @returns
|
|
209
248
|
*/
|
|
210
249
|
async postBeef(beef: Beef, txids: string[]): Promise<sdk.PostBeefResult> {
|
|
250
|
+
const r: sdk.PostBeefResult = {
|
|
251
|
+
name: 'ARC',
|
|
252
|
+
status: 'success',
|
|
253
|
+
txidResults: [],
|
|
254
|
+
notes: []
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const nn = () => ({ name: 'ARCpostBeef', when: new Date().toISOString() })
|
|
258
|
+
|
|
211
259
|
if (beef.version === BEEF_V2 && beef.txs.every(btx => !btx.isTxidOnly)) {
|
|
212
260
|
beef.version = BEEF_V1
|
|
261
|
+
r.notes!.push({ ...nn(), what: 'postBeefV2ToV1' })
|
|
213
262
|
}
|
|
214
263
|
|
|
215
264
|
const beefHex = beef.toHex()
|
|
216
265
|
|
|
217
266
|
const prtr = await this.postRawTx(beefHex, txids)
|
|
218
267
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
status: prtr.status,
|
|
222
|
-
txidResults: [prtr]
|
|
223
|
-
}
|
|
268
|
+
r.status = prtr.status
|
|
269
|
+
r.txidResults = [prtr]
|
|
224
270
|
|
|
271
|
+
// Since postRawTx only returns results for a single txid,
|
|
272
|
+
// replicate the basic results any additional txids.
|
|
273
|
+
// TODO: Temporary hack...
|
|
225
274
|
for (const txid of txids) {
|
|
226
275
|
if (prtr.txid === txid) continue
|
|
227
276
|
const tr: sdk.PostTxResultForTxid = {
|
|
228
277
|
txid,
|
|
229
|
-
status: 'success'
|
|
278
|
+
status: 'success',
|
|
279
|
+
notes: []
|
|
230
280
|
}
|
|
281
|
+
// For the extra txids, go back to the service for confirmation...
|
|
231
282
|
const dr = await this.getTxData(txid)
|
|
232
283
|
if (dr.txid !== txid) {
|
|
233
284
|
tr.status = 'error'
|
|
234
285
|
tr.data = 'internal error'
|
|
286
|
+
tr.notes!.push({
|
|
287
|
+
...nn(),
|
|
288
|
+
what: 'postBeefGetTxDataInternal',
|
|
289
|
+
txid,
|
|
290
|
+
returnedTxid: dr.txid
|
|
291
|
+
})
|
|
235
292
|
} else if (
|
|
236
293
|
dr.txStatus === 'SEEN_ON_NETWORK' ||
|
|
237
294
|
dr.txStatus === 'STORED'
|
|
238
295
|
) {
|
|
239
296
|
tr.data = dr.txStatus
|
|
297
|
+
tr.notes!.push({
|
|
298
|
+
...nn(),
|
|
299
|
+
what: 'postBeefGetTxDataSuccess',
|
|
300
|
+
txid,
|
|
301
|
+
txStatus: dr.txStatus
|
|
302
|
+
})
|
|
240
303
|
} else {
|
|
241
304
|
tr.status = 'error'
|
|
242
305
|
tr.data = dr
|
|
306
|
+
tr.notes!.push({
|
|
307
|
+
...nn(),
|
|
308
|
+
what: 'postBeefGetTxDataError',
|
|
309
|
+
txid,
|
|
310
|
+
txStatus: dr.txStatus
|
|
311
|
+
})
|
|
243
312
|
}
|
|
244
313
|
r.txidResults.push(tr)
|
|
245
314
|
if (r.status === 'success' && tr.status === 'error') r.status = 'error'
|
|
@@ -2,12 +2,14 @@ import { Beef, HexString, Utils, WhatsOnChainConfig } from '@bsv/sdk'
|
|
|
2
2
|
import {
|
|
3
3
|
asArray,
|
|
4
4
|
asString,
|
|
5
|
+
doubleSha256BE,
|
|
5
6
|
sdk,
|
|
6
7
|
validateScriptHash,
|
|
7
8
|
wait
|
|
8
9
|
} from '../../index.client'
|
|
9
10
|
import { convertProofToMerklePath } from '../../utility/tscProofToMerklePath'
|
|
10
11
|
import SdkWhatsOnChain from './SdkWhatsOnChain'
|
|
12
|
+
import { ReqHistoryNote } from '../../sdk'
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
*
|
|
@@ -116,17 +118,16 @@ export class WhatsOnChain extends SdkWhatsOnChain {
|
|
|
116
118
|
const r: sdk.PostBeefResult = {
|
|
117
119
|
name: 'WoC',
|
|
118
120
|
status: 'success',
|
|
119
|
-
txidResults: []
|
|
121
|
+
txidResults: [],
|
|
122
|
+
notes: []
|
|
120
123
|
}
|
|
121
124
|
|
|
122
125
|
let delay = false
|
|
123
126
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
txid,
|
|
127
|
-
status: 'success'
|
|
128
|
-
}
|
|
127
|
+
const nn = () => ({ name: 'WoCpostBeef', when: new Date().toISOString() })
|
|
128
|
+
const nne = () => ({ ...nn(), beef: beef.toHex(), txids: txids.join(',') })
|
|
129
129
|
|
|
130
|
+
for (const txid of txids) {
|
|
130
131
|
const rawTx = Utils.toHex(beef.findTxid(txid)!.rawTx!)
|
|
131
132
|
|
|
132
133
|
if (delay) {
|
|
@@ -135,19 +136,21 @@ export class WhatsOnChain extends SdkWhatsOnChain {
|
|
|
135
136
|
}
|
|
136
137
|
delay = true
|
|
137
138
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
tr.
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
} catch (eu: unknown) {
|
|
145
|
-
const e = sdk.WalletError.fromUnknown(eu)
|
|
146
|
-
tr.status = 'error'
|
|
147
|
-
tr.data = `exception ${e.code} ${e.message}`
|
|
139
|
+
const tr = await this.postRawTx(rawTx)
|
|
140
|
+
if (txid !== tr.txid) {
|
|
141
|
+
throw new sdk.WERR_INTERNAL(
|
|
142
|
+
`woc returned txid ${tr.txid}, expected ${txid}`
|
|
143
|
+
)
|
|
148
144
|
}
|
|
149
145
|
|
|
150
146
|
r.txidResults.push(tr)
|
|
147
|
+
if (r.status === 'success' && tr.status !== 'success') r.status = 'error'
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (r.status === 'success') {
|
|
151
|
+
r.notes!.push({ ...nn(), what: 'postBeefSuccess' })
|
|
152
|
+
} else {
|
|
153
|
+
r.notes!.push({ ...nne(), what: 'postBeefError' })
|
|
151
154
|
}
|
|
152
155
|
|
|
153
156
|
return r
|
|
@@ -157,7 +160,15 @@ export class WhatsOnChain extends SdkWhatsOnChain {
|
|
|
157
160
|
* @param rawTx raw transaction to broadcast as hex string
|
|
158
161
|
* @returns txid returned by transaction processor of transaction broadcast
|
|
159
162
|
*/
|
|
160
|
-
async postRawTx(rawTx: HexString): Promise<
|
|
163
|
+
async postRawTx(rawTx: HexString): Promise<sdk.PostTxResultForTxid> {
|
|
164
|
+
let txid = Utils.toHex(doubleSha256BE(Utils.toArray(rawTx, 'hex')))
|
|
165
|
+
|
|
166
|
+
const r: sdk.PostTxResultForTxid = {
|
|
167
|
+
txid,
|
|
168
|
+
status: 'success',
|
|
169
|
+
notes: []
|
|
170
|
+
}
|
|
171
|
+
|
|
161
172
|
const headers = this.getHttpHeaders()
|
|
162
173
|
headers['Content-Type'] = 'application/json'
|
|
163
174
|
headers['Accept'] = 'text/plain'
|
|
@@ -168,41 +179,71 @@ export class WhatsOnChain extends SdkWhatsOnChain {
|
|
|
168
179
|
data: { txhex: rawTx }
|
|
169
180
|
}
|
|
170
181
|
|
|
171
|
-
|
|
182
|
+
const url = `${this.URL}/tx/raw`
|
|
183
|
+
const nn = () => ({ name: 'WoCpostRawTx', when: new Date().toISOString() })
|
|
184
|
+
const nne = () => ({ ...nn(), rawTx, txid, url })
|
|
185
|
+
|
|
186
|
+
const retryLimit = 5
|
|
187
|
+
for (let retry = 0; retry < retryLimit; retry++) {
|
|
172
188
|
try {
|
|
173
189
|
const response = await this.httpClient.request<string>(
|
|
174
|
-
|
|
190
|
+
url,
|
|
175
191
|
requestOptions
|
|
176
192
|
)
|
|
177
193
|
if (response.statusText === 'Too Many Requests' && retry < 2) {
|
|
194
|
+
r.notes!.push({ ...nn(), what: 'postRawTxRateLimit' })
|
|
178
195
|
await wait(2000)
|
|
179
196
|
continue
|
|
180
197
|
}
|
|
181
198
|
if (response.ok) {
|
|
182
199
|
const txid = response.data
|
|
183
|
-
|
|
200
|
+
r.notes!.push({ ...nn(), what: 'postRawTxRateSuccess' })
|
|
201
|
+
return r
|
|
184
202
|
} else {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
203
|
+
r.status = 'error'
|
|
204
|
+
const n: ReqHistoryNote = {
|
|
205
|
+
...nn(),
|
|
206
|
+
...nne(),
|
|
207
|
+
what: 'postRawTxError'
|
|
208
|
+
}
|
|
209
|
+
if (typeof response.data === 'string') {
|
|
210
|
+
n.data = response.data.slice(0, 128)
|
|
211
|
+
r.data = response.data
|
|
212
|
+
}
|
|
213
|
+
if (typeof response.statusText === 'string') {
|
|
214
|
+
n.statusText = response.data.slice(0, 128)
|
|
215
|
+
r.data = `${r.data || ''} ${response.statusText}`
|
|
216
|
+
}
|
|
217
|
+
if (
|
|
218
|
+
typeof response.status === 'string' ||
|
|
219
|
+
typeof response.status === 'number'
|
|
220
|
+
) {
|
|
221
|
+
n.status = response.data.slice(0, 128)
|
|
222
|
+
r.data = `${r.data || ''} ${response.status}`
|
|
223
|
+
}
|
|
224
|
+
r.notes!.push(n)
|
|
195
225
|
}
|
|
196
226
|
} catch (eu: unknown) {
|
|
197
|
-
|
|
227
|
+
r.status = 'error'
|
|
198
228
|
const e = sdk.WalletError.fromUnknown(eu)
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
229
|
+
r.notes!.push({
|
|
230
|
+
...nn(),
|
|
231
|
+
...nne(),
|
|
232
|
+
what: 'postRawTxCatch',
|
|
233
|
+
code: e.code,
|
|
234
|
+
description: e.description
|
|
235
|
+
})
|
|
236
|
+
r.data = `${e.code} ${e.description}`
|
|
203
237
|
}
|
|
204
238
|
}
|
|
205
|
-
|
|
239
|
+
r.status = 'error'
|
|
240
|
+
r.notes!.push({
|
|
241
|
+
...nn(),
|
|
242
|
+
...nne(),
|
|
243
|
+
what: 'postRawTxRetryLimit',
|
|
244
|
+
retryLimit
|
|
245
|
+
})
|
|
246
|
+
return r
|
|
206
247
|
}
|
|
207
248
|
|
|
208
249
|
/**
|
|
@@ -213,7 +254,7 @@ export class WhatsOnChain extends SdkWhatsOnChain {
|
|
|
213
254
|
txid: string,
|
|
214
255
|
services: sdk.WalletServices
|
|
215
256
|
): Promise<sdk.GetMerklePathResult> {
|
|
216
|
-
const r: sdk.GetMerklePathResult = { name: 'WoCTsc' }
|
|
257
|
+
const r: sdk.GetMerklePathResult = { name: 'WoCTsc', notes: [] }
|
|
217
258
|
|
|
218
259
|
const headers = this.getHttpHeaders()
|
|
219
260
|
const requestOptions = {
|
|
@@ -227,25 +268,51 @@ export class WhatsOnChain extends SdkWhatsOnChain {
|
|
|
227
268
|
WhatsOnChainTscProof | WhatsOnChainTscProof[]
|
|
228
269
|
>(`${this.URL}/tx/${txid}/proof/tsc`, requestOptions)
|
|
229
270
|
if (response.statusText === 'Too Many Requests' && retry < 2) {
|
|
271
|
+
r.notes!.push({
|
|
272
|
+
what: 'getMerklePathRetry',
|
|
273
|
+
name: r.name,
|
|
274
|
+
status: response.status,
|
|
275
|
+
statusText: response.statusText
|
|
276
|
+
})
|
|
230
277
|
await wait(2000)
|
|
231
278
|
continue
|
|
232
279
|
}
|
|
233
280
|
|
|
234
|
-
if (response.status === 404 && response.statusText === 'Not Found')
|
|
281
|
+
if (response.status === 404 && response.statusText === 'Not Found') {
|
|
282
|
+
r.notes!.push({
|
|
283
|
+
what: 'getMerklePathNotFound',
|
|
284
|
+
name: r.name,
|
|
285
|
+
status: response.status,
|
|
286
|
+
statusText: response.statusText
|
|
287
|
+
})
|
|
235
288
|
return r
|
|
289
|
+
}
|
|
236
290
|
|
|
237
291
|
if (
|
|
238
292
|
!response.ok ||
|
|
239
293
|
response.status !== 200 ||
|
|
240
294
|
response.statusText !== 'OK'
|
|
241
|
-
)
|
|
295
|
+
) {
|
|
296
|
+
r.notes!.push({
|
|
297
|
+
what: 'getMerklePathBadStatus',
|
|
298
|
+
name: r.name,
|
|
299
|
+
status: response.status,
|
|
300
|
+
statusText: response.statusText
|
|
301
|
+
})
|
|
242
302
|
throw new sdk.WERR_INVALID_PARAMETER(
|
|
243
303
|
'txid',
|
|
244
304
|
`valid transaction. '${txid}' response ${response.statusText}`
|
|
245
305
|
)
|
|
306
|
+
}
|
|
246
307
|
|
|
247
308
|
if (!response.data) {
|
|
248
309
|
// Unmined, proof not yet available.
|
|
310
|
+
r.notes!.push({
|
|
311
|
+
what: 'getMerklePathNoData',
|
|
312
|
+
name: r.name,
|
|
313
|
+
status: response.status,
|
|
314
|
+
statusText: response.statusText
|
|
315
|
+
})
|
|
249
316
|
return r
|
|
250
317
|
}
|
|
251
318
|
|
|
@@ -263,17 +330,38 @@ export class WhatsOnChain extends SdkWhatsOnChain {
|
|
|
263
330
|
}
|
|
264
331
|
r.merklePath = convertProofToMerklePath(txid, proof)
|
|
265
332
|
r.header = header
|
|
333
|
+
r.notes!.push({
|
|
334
|
+
what: 'getMerklePathSuccess',
|
|
335
|
+
name: r.name,
|
|
336
|
+
status: response.status,
|
|
337
|
+
statusText: response.statusText
|
|
338
|
+
})
|
|
266
339
|
} else {
|
|
340
|
+
r.notes!.push({
|
|
341
|
+
what: 'getMerklePathNoHeader',
|
|
342
|
+
target: p.target,
|
|
343
|
+
name: r.name,
|
|
344
|
+
status: response.status,
|
|
345
|
+
statusText: response.statusText
|
|
346
|
+
})
|
|
267
347
|
throw new sdk.WERR_INVALID_PARAMETER(
|
|
268
348
|
'blockhash',
|
|
269
349
|
'a valid on-chain block hash'
|
|
270
350
|
)
|
|
271
351
|
}
|
|
272
|
-
} catch (
|
|
273
|
-
|
|
352
|
+
} catch (eu: unknown) {
|
|
353
|
+
const e = sdk.WalletError.fromUnknown(eu)
|
|
354
|
+
r.notes!.push({
|
|
355
|
+
what: 'getMerklePathError',
|
|
356
|
+
name: r.name,
|
|
357
|
+
code: e.code,
|
|
358
|
+
description: e.description
|
|
359
|
+
})
|
|
360
|
+
r.error = e
|
|
274
361
|
}
|
|
275
362
|
return r
|
|
276
363
|
}
|
|
364
|
+
r.notes!.push({ what: 'getMerklePathInternal', name: r.name })
|
|
277
365
|
throw new sdk.WERR_INTERNAL()
|
|
278
366
|
}
|
|
279
367
|
|
|
@@ -49,14 +49,14 @@ describe('whatsonchain tests', () => {
|
|
|
49
49
|
)
|
|
50
50
|
const s = JSON.stringify(r)
|
|
51
51
|
expect(s).toBe(
|
|
52
|
-
|
|
52
|
+
"{\"name\":\"WoCTsc\",\"notes\":[{\"what\":\"getMerklePathSuccess\",\"name\":\"WoCTsc\",\"status\":200,\"statusText\":\"OK\"}],\"merklePath\":{\"blockHeight\":1661398,\"path\":[[{\"offset\":6,\"hash\":\"7e5b797b86abd31a654bf296900d6cb14d04ef0811568ff4675494af2d92166b\",\"txid\":true},{\"offset\":7,\"hash\":\"97dd9d9080394d52338588732d9f84e1debca93f171f674ac3beac1e75495568\"}],[{\"offset\":2,\"hash\":\"81beedcd219d9e03255bde2ee479db34b9fed04d30373ba8bc264a64af2515b9\"}],[{\"offset\":0,\"hash\":\"9965f9aaeea33f6878335e6f7e6bdb544c3a8550c84e2f0daca54e9cd912111c\"}]]},\"header\":{\"version\":536870912,\"previousHash\":\"000000000688340a14b77e49bb0fca5ac7b624f7f79a5517583d1aae61c4e658\",\"merkleRoot\":\"edbc07082ca0a31d5ec89d1f503a9cd41112c0d8f3221a96acfb8a9d16f8e82b\",\"time\":1739624725,\"bits\":486604799,\"nonce\":1437884974,\"height\":1661398,\"hash\":\"00000000d8a73bf9a37272a71886ea92a25376bed1c1916f2b5cfbec4d6f6a25\"}}"
|
|
53
53
|
)
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
{
|
|
57
57
|
const r = await wocTest.getMerklePath('1'.repeat(64), services)
|
|
58
58
|
const s = JSON.stringify(r)
|
|
59
|
-
expect(s).toBe(
|
|
59
|
+
expect(s).toBe("{\"name\":\"WoCTsc\",\"notes\":[{\"what\":\"getMerklePathNoData\",\"name\":\"WoCTsc\",\"status\":200,\"statusText\":\"OK\"}]}")
|
|
60
60
|
}
|
|
61
61
|
})
|
|
62
62
|
|
|
@@ -69,14 +69,13 @@ describe('whatsonchain tests', () => {
|
|
|
69
69
|
)
|
|
70
70
|
const s = JSON.stringify(r)
|
|
71
71
|
expect(s).toBe(
|
|
72
|
-
|
|
73
|
-
)
|
|
72
|
+
"{\"name\":\"WoCTsc\",\"notes\":[{\"what\":\"getMerklePathSuccess\",\"name\":\"WoCTsc\",\"status\":200,\"statusText\":\"OK\"}],\"merklePath\":{\"blockHeight\":883637,\"path\":[[{\"offset\":46,\"hash\":\"d9978ffc6676523208f7b33bebf1b176388bbeace2c7ef67ce35c2eababa1805\",\"txid\":true},{\"offset\":47,\"hash\":\"066f6fa6fa988f2e3a9d6fe35fa0d3666c652dac35cabaeebff3738a4e67f68f\"}],[{\"offset\":22,\"hash\":\"232089a6f77c566151bc4701fda394b5cc5bf17073140d46a73c4c3ed0a7b911\"}],[{\"offset\":10,\"hash\":\"c639b3a6ce127f67dbd01c7331a6fca62a4b429830387bd68ac6ac05e162116d\"}],[{\"offset\":4,\"hash\":\"730cec44be97881530947d782bb328d25f1122fdae206296937fffb03e936d48\"}],[{\"offset\":3,\"hash\":\"28b681f8ab8db0fa4d5d20cb1532b95184a155346b0b8447bde580b2406d51e6\"}],[{\"offset\":0,\"hash\":\"c49a18028e230dd1439b26794c08c339506f24a450f067c4facd4e0d5a346490\"}],[{\"offset\":1,\"hash\":\"0ba57d1b1fad6874de3640c01088e3dedad3507e5b3a3102b9a8a8055f3df88b\"}],[{\"offset\":1,\"hash\":\"c830edebe5565c19ba584ec73d49129344d17539f322509b7c314ae641c2fcdb\"}],[{\"offset\":1,\"hash\":\"ff62d5ed2a94eb93a2b7d084b8f15b12083573896b6a58cf871507e3352c75f5\"}]]},\"header\":{\"version\":1040187392,\"previousHash\":\"00000000000000000d9f6889dd6743500adee204ea25d8a57225ecd48b111769\",\"merkleRoot\":\"59c1efd79fae0d9c29dd8da63f8eeec0aadde048f4491c6bfa324fcfd537156d\",\"time\":1739329877,\"bits\":403818359,\"nonce\":596827153,\"height\":883637,\"hash\":\"0000000000000000060ac8d63b78d41f58c9aba0b09f81db7d51fa4905a47263\"}}" )
|
|
74
73
|
}
|
|
75
74
|
|
|
76
75
|
{
|
|
77
76
|
const r = await wocMain.getMerklePath('1'.repeat(64), services)
|
|
78
77
|
const s = JSON.stringify(r)
|
|
79
|
-
expect(s).toBe(
|
|
78
|
+
expect(s).toBe("{\"name\":\"WoCTsc\",\"notes\":[{\"what\":\"getMerklePathNoData\",\"name\":\"WoCTsc\",\"status\":200,\"statusText\":\"OK\"}]}")
|
|
80
79
|
}
|
|
81
80
|
})
|
|
82
81
|
|