@bsv/wallet-toolbox 1.1.47 → 1.1.49
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/out/src/monitor/tasks/TaskNewHeader.js +1 -2
- package/out/src/monitor/tasks/TaskNewHeader.js.map +1 -1
- package/out/src/services/providers/__tests/WhatsOnChain.test.js +7 -4
- package/out/src/services/providers/__tests/WhatsOnChain.test.js.map +1 -1
- package/out/src/storage/StorageKnex.d.ts.map +1 -1
- package/out/src/storage/StorageKnex.js +4 -2
- package/out/src/storage/StorageKnex.js.map +1 -1
- package/out/src/storage/methods/processAction.d.ts.map +1 -1
- package/out/src/storage/methods/processAction.js +10 -0
- package/out/src/storage/methods/processAction.js.map +1 -1
- package/out/test/Wallet/local/localWallet.man.test.d.ts +4 -1
- package/out/test/Wallet/local/localWallet.man.test.d.ts.map +1 -1
- package/out/test/Wallet/local/localWallet.man.test.js +218 -144
- package/out/test/Wallet/local/localWallet.man.test.js.map +1 -1
- package/out/test/Wallet/support/janitor.man.test.d.ts +2 -0
- package/out/test/Wallet/support/janitor.man.test.d.ts.map +1 -0
- package/out/test/Wallet/support/janitor.man.test.js +35 -0
- package/out/test/Wallet/support/janitor.man.test.js.map +1 -0
- package/out/test/utils/TestUtilsWalletStorage.d.ts +1 -0
- package/out/test/utils/TestUtilsWalletStorage.d.ts.map +1 -1
- package/out/test/utils/TestUtilsWalletStorage.js +5 -1
- package/out/test/utils/TestUtilsWalletStorage.js.map +1 -1
- package/out/tsconfig.all.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/monitor/tasks/TaskNewHeader.ts +1 -1
- package/src/services/providers/__tests/WhatsOnChain.test.ts +12 -4
- package/src/storage/StorageKnex.ts +5 -2
- package/src/storage/methods/processAction.ts +14 -0
- package/test/Wallet/local/localWallet.man.test.ts +312 -159
- package/test/Wallet/support/janitor.man.test.ts +44 -0
- package/test/utils/TestUtilsWalletStorage.ts +7 -1
package/package.json
CHANGED
|
@@ -16,6 +16,9 @@ describe('whatsonchain tests', () => {
|
|
|
16
16
|
apiKey: envMain.taalApiKey
|
|
17
17
|
})
|
|
18
18
|
|
|
19
|
+
test('00', () => {})
|
|
20
|
+
if (_tu.noTestEnv('test')) return
|
|
21
|
+
|
|
19
22
|
test('0 getRawTx testnet', async () => {
|
|
20
23
|
const rawTx = await wocTest.getRawTx(
|
|
21
24
|
'7e5b797b86abd31a654bf296900d6cb14d04ef0811568ff4675494af2d92166b'
|
|
@@ -49,14 +52,16 @@ describe('whatsonchain tests', () => {
|
|
|
49
52
|
)
|
|
50
53
|
const s = JSON.stringify(r)
|
|
51
54
|
expect(s).toBe(
|
|
52
|
-
|
|
55
|
+
'{"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
56
|
)
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
{
|
|
57
60
|
const r = await wocTest.getMerklePath('1'.repeat(64), services)
|
|
58
61
|
const s = JSON.stringify(r)
|
|
59
|
-
expect(s).toBe(
|
|
62
|
+
expect(s).toBe(
|
|
63
|
+
'{"name":"WoCTsc","notes":[{"what":"getMerklePathNoData","name":"WoCTsc","status":200,"statusText":"OK"}]}'
|
|
64
|
+
)
|
|
60
65
|
}
|
|
61
66
|
})
|
|
62
67
|
|
|
@@ -69,13 +74,16 @@ describe('whatsonchain tests', () => {
|
|
|
69
74
|
)
|
|
70
75
|
const s = JSON.stringify(r)
|
|
71
76
|
expect(s).toBe(
|
|
72
|
-
|
|
77
|
+
'{"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"}}'
|
|
78
|
+
)
|
|
73
79
|
}
|
|
74
80
|
|
|
75
81
|
{
|
|
76
82
|
const r = await wocMain.getMerklePath('1'.repeat(64), services)
|
|
77
83
|
const s = JSON.stringify(r)
|
|
78
|
-
expect(s).toBe(
|
|
84
|
+
expect(s).toBe(
|
|
85
|
+
'{"name":"WoCTsc","notes":[{"what":"getMerklePathNoData","name":"WoCTsc","status":200,"statusText":"OK"}]}'
|
|
86
|
+
)
|
|
79
87
|
}
|
|
80
88
|
})
|
|
81
89
|
|
|
@@ -112,6 +112,7 @@ export class StorageKnex
|
|
|
112
112
|
trx?: sdk.TrxToken
|
|
113
113
|
): Promise<number[] | undefined> {
|
|
114
114
|
if (!txid) return undefined
|
|
115
|
+
if (!this.isAvailable()) await this.makeAvailable()
|
|
115
116
|
|
|
116
117
|
let rawTx: number[] | undefined = undefined
|
|
117
118
|
if (Number.isInteger(offset) && Number.isInteger(length)) {
|
|
@@ -160,7 +161,8 @@ export class StorageKnex
|
|
|
160
161
|
q = q.limit(args.paged.limit)
|
|
161
162
|
q = q.offset(args.paged.offset || 0)
|
|
162
163
|
}
|
|
163
|
-
if (args.since)
|
|
164
|
+
if (args.since)
|
|
165
|
+
q = q.where('updated_at', '>=', this.validateDateForWhere(args.since))
|
|
164
166
|
return q
|
|
165
167
|
}
|
|
166
168
|
override async getProvenTxsForUser(
|
|
@@ -189,7 +191,8 @@ export class StorageKnex
|
|
|
189
191
|
q = q.limit(args.paged.limit)
|
|
190
192
|
q = q.offset(args.paged.offset || 0)
|
|
191
193
|
}
|
|
192
|
-
if (args.since)
|
|
194
|
+
if (args.since)
|
|
195
|
+
q = q.where('updated_at', '>=', this.validateDateForWhere(args.since))
|
|
193
196
|
return q
|
|
194
197
|
}
|
|
195
198
|
override async getProvenTxReqsForUser(
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
verifyOneOrNone,
|
|
31
31
|
verifyTruthy
|
|
32
32
|
} from '../../index.client'
|
|
33
|
+
import { ProvenTxReqNonTerminalStatus } from '../../sdk'
|
|
33
34
|
|
|
34
35
|
export async function processAction(
|
|
35
36
|
storage: StorageProvider,
|
|
@@ -169,6 +170,19 @@ async function shareReqsWithWorld(
|
|
|
169
170
|
const d = prtn.details.find(d => d.txid === ar.txid)
|
|
170
171
|
if (d) {
|
|
171
172
|
ar.postBeef = d.pbrft
|
|
173
|
+
if (d.pbrft.status === 'error' && ar.getReq.req) {
|
|
174
|
+
// If the immediate (un-delayed) broadcast attempt APPEARS to fail,
|
|
175
|
+
// fall back to delayed sending and tracking to make sure transaction
|
|
176
|
+
// gets tracked if it is valid and floating around the network...
|
|
177
|
+
const req = await EntityProvenTxReq.fromStorageId(
|
|
178
|
+
storage,
|
|
179
|
+
ar.getReq.req.provenTxReqId
|
|
180
|
+
)
|
|
181
|
+
if (req.status === 'unprocessed') {
|
|
182
|
+
req.status = 'unsent'
|
|
183
|
+
await req.updateStorageDynamicProperties(storage)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
172
186
|
}
|
|
173
187
|
}
|
|
174
188
|
|
|
@@ -1,172 +1,73 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Beef,
|
|
3
3
|
CreateActionArgs,
|
|
4
|
+
CreateActionOptions,
|
|
5
|
+
CreateActionResult,
|
|
4
6
|
P2PKH,
|
|
5
7
|
PrivateKey,
|
|
6
8
|
PublicKey,
|
|
7
9
|
Script,
|
|
8
10
|
SignActionArgs
|
|
9
11
|
} from '@bsv/sdk'
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
+
import {
|
|
13
|
+
asString,
|
|
14
|
+
EntityProvenTxReq,
|
|
15
|
+
EntitySyncState,
|
|
16
|
+
Monitor,
|
|
17
|
+
sdk,
|
|
18
|
+
Services,
|
|
19
|
+
Setup,
|
|
20
|
+
StorageKnex,
|
|
21
|
+
TableOutput,
|
|
22
|
+
TableUser,
|
|
23
|
+
verifyId,
|
|
24
|
+
verifyOne,
|
|
25
|
+
wait
|
|
26
|
+
} from '../../../src'
|
|
27
|
+
import { _tu, TestWalletNoSetup } from '../../utils/TestUtilsWalletStorage'
|
|
28
|
+
import { monitorEventLoopDelay } from 'perf_hooks'
|
|
12
29
|
|
|
13
30
|
describe('localWallet tests', () => {
|
|
14
31
|
jest.setTimeout(99999999)
|
|
15
32
|
|
|
16
|
-
test('
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const env = _tu.getEnv(chain)
|
|
20
|
-
if (!env.testIdentityKey)
|
|
21
|
-
throw new sdk.WERR_INVALID_PARAMETER('env.testIdentityKey', 'valid')
|
|
22
|
-
if (!env.testFilePath)
|
|
23
|
-
throw new sdk.WERR_INVALID_PARAMETER('env.testFilePath', 'valid')
|
|
24
|
-
|
|
25
|
-
const setup = await _tu.createTestWallet({
|
|
26
|
-
chain,
|
|
27
|
-
rootKeyHex: env.devKeys[env.testIdentityKey],
|
|
28
|
-
filePath: env.testFilePath,
|
|
29
|
-
setActiveClient: false,
|
|
30
|
-
addLocalBackup: false
|
|
31
|
-
})
|
|
33
|
+
test('00', () => {})
|
|
34
|
+
if (_tu.noTestEnv('test')) return
|
|
35
|
+
if (_tu.noTestEnv('main')) return
|
|
32
36
|
|
|
33
|
-
|
|
37
|
+
test('0 create 1 sat delayed', async () => {
|
|
38
|
+
const setup = await createSetup('test')
|
|
34
39
|
|
|
35
|
-
const
|
|
36
|
-
for (let i = 0; i < createHowMany; i++) {
|
|
37
|
-
const args: CreateActionArgs = {
|
|
38
|
-
outputs: [
|
|
39
|
-
{
|
|
40
|
-
lockingScript: new P2PKH()
|
|
41
|
-
.lock(PublicKey.fromString(setup.identityKey).toAddress())
|
|
42
|
-
.toHex(),
|
|
43
|
-
satoshis: 1,
|
|
44
|
-
outputDescription: 'test output',
|
|
45
|
-
customInstructions: JSON.stringify({
|
|
46
|
-
type: 'P2PKH',
|
|
47
|
-
key: 'identity'
|
|
48
|
-
}),
|
|
49
|
-
basket: 'test-output'
|
|
50
|
-
}
|
|
51
|
-
],
|
|
52
|
-
description: 'create test output'
|
|
53
|
-
}
|
|
54
|
-
const car = await setup.wallet.createAction(args)
|
|
55
|
-
expect(car.txid)
|
|
56
|
-
|
|
57
|
-
const req = await EntityProvenTxReq.fromStorageTxid(
|
|
58
|
-
setup.activeStorage,
|
|
59
|
-
car.txid!
|
|
60
|
-
)
|
|
61
|
-
expect(req !== undefined && req.history.notes !== undefined)
|
|
62
|
-
if (req && req.history.notes) {
|
|
63
|
-
expect(req.status === 'unsent')
|
|
64
|
-
expect(req.history.notes.length === 1)
|
|
65
|
-
const n = req.history.notes[0]
|
|
66
|
-
expect(n.what === 'status' && n.status_now === 'unsent')
|
|
67
|
-
}
|
|
40
|
+
const car = await createOneSatTestOutput(setup, {}, 1)
|
|
68
41
|
|
|
69
|
-
|
|
70
|
-
if (runOnce) {
|
|
71
|
-
await setup.monitor.runOnce()
|
|
72
|
-
|
|
73
|
-
const req = await EntityProvenTxReq.fromStorageTxid(
|
|
74
|
-
setup.activeStorage,
|
|
75
|
-
car.txid!
|
|
76
|
-
)
|
|
77
|
-
expect(req !== undefined && req.history.notes !== undefined)
|
|
78
|
-
if (req && req.history.notes) {
|
|
79
|
-
expect(req.status === 'unmined')
|
|
80
|
-
expect(req.history.notes.length === 1)
|
|
81
|
-
const n = req.history.notes[0]
|
|
82
|
-
expect(n.what === 'status' && n.status_now === 'unsent')
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
42
|
+
await trackReqByTxid(setup, car.txid!)
|
|
86
43
|
|
|
87
44
|
await setup.wallet.destroy()
|
|
88
45
|
})
|
|
89
46
|
|
|
90
|
-
test('
|
|
91
|
-
const
|
|
92
|
-
if (_tu.noTestEnv(chain)) return
|
|
93
|
-
const env = _tu.getEnv(chain)
|
|
94
|
-
if (!env.testIdentityKey)
|
|
95
|
-
throw new sdk.WERR_INVALID_PARAMETER('env.testIdentityKey', 'valid')
|
|
96
|
-
if (!env.testFilePath)
|
|
97
|
-
throw new sdk.WERR_INVALID_PARAMETER('env.testFilePath', 'valid')
|
|
98
|
-
|
|
99
|
-
const setup = await _tu.createTestWallet({
|
|
100
|
-
chain,
|
|
101
|
-
rootKeyHex: env.devKeys[env.testIdentityKey],
|
|
102
|
-
filePath: env.testFilePath,
|
|
103
|
-
setActiveClient: false,
|
|
104
|
-
addLocalBackup: false
|
|
105
|
-
})
|
|
47
|
+
test('0a create 1 sat immediate', async () => {
|
|
48
|
+
const setup = await createSetup('test')
|
|
106
49
|
|
|
107
|
-
|
|
50
|
+
const car = await createOneSatTestOutput(
|
|
51
|
+
setup,
|
|
52
|
+
{ acceptDelayedBroadcast: false },
|
|
53
|
+
1
|
|
54
|
+
)
|
|
108
55
|
|
|
109
|
-
|
|
110
|
-
basket: 'test-output',
|
|
111
|
-
include: 'entire transactions',
|
|
112
|
-
limit: 1000
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
if (outputs.outputs.length > 8) {
|
|
116
|
-
const args: CreateActionArgs = {
|
|
117
|
-
inputBEEF: outputs.BEEF!,
|
|
118
|
-
inputs: [],
|
|
119
|
-
description: 'recover test output'
|
|
120
|
-
}
|
|
121
|
-
const p2pkh = new P2PKH()
|
|
122
|
-
for (const o of outputs.outputs) {
|
|
123
|
-
args.inputs!.push({
|
|
124
|
-
unlockingScriptLength: 108,
|
|
125
|
-
outpoint: o.outpoint,
|
|
126
|
-
inputDescription: 'recovered test output'
|
|
127
|
-
})
|
|
128
|
-
}
|
|
129
|
-
const car = await setup.wallet.createAction(args)
|
|
130
|
-
expect(car.signableTransaction)
|
|
131
|
-
|
|
132
|
-
const st = car.signableTransaction!
|
|
133
|
-
const beef = Beef.fromBinary(st.tx)
|
|
134
|
-
const tx = beef.findAtomicTransaction(beef.txs.slice(-1)[0].txid)!
|
|
135
|
-
const signArgs: SignActionArgs = {
|
|
136
|
-
reference: st.reference,
|
|
137
|
-
spends: {} // 0: { unlockingScript } },
|
|
138
|
-
}
|
|
139
|
-
for (let i = 0; i < outputs.outputs.length; i++) {
|
|
140
|
-
const o = outputs.outputs[i]
|
|
141
|
-
const unlock = p2pkh.unlock(setup.keyDeriver.rootKey, 'all', false)
|
|
142
|
-
const unlockingScript = (await unlock.sign(tx, i)).toHex()
|
|
143
|
-
signArgs.spends[i] = { unlockingScript }
|
|
144
|
-
}
|
|
145
|
-
const sar = await setup.wallet.signAction(signArgs)
|
|
146
|
-
expect(sar.txid)
|
|
147
|
-
}
|
|
56
|
+
await trackReqByTxid(setup, car.txid!)
|
|
148
57
|
|
|
149
58
|
await setup.wallet.destroy()
|
|
150
59
|
})
|
|
151
60
|
|
|
152
|
-
test('
|
|
153
|
-
const
|
|
154
|
-
if (_tu.noTestEnv(chain)) return
|
|
155
|
-
const env = _tu.getEnv(chain)
|
|
156
|
-
if (!env.testIdentityKey)
|
|
157
|
-
throw new sdk.WERR_INVALID_PARAMETER('env.testIdentityKey', 'valid')
|
|
158
|
-
if (!env.testFilePath)
|
|
159
|
-
throw new sdk.WERR_INVALID_PARAMETER('env.testFilePath', 'valid')
|
|
160
|
-
|
|
161
|
-
const setup = await _tu.createTestWallet({
|
|
162
|
-
chain,
|
|
163
|
-
rootKeyHex: env.devKeys[env.testIdentityKey],
|
|
164
|
-
filePath: env.testFilePath,
|
|
165
|
-
setActiveClient: false,
|
|
166
|
-
addLocalBackup: false
|
|
167
|
-
})
|
|
61
|
+
test('1 recover 1 sat outputs', async () => {
|
|
62
|
+
const setup = await createSetup('test')
|
|
168
63
|
|
|
169
|
-
|
|
64
|
+
await recoverOneSatTestOutputs(setup)
|
|
65
|
+
|
|
66
|
+
await setup.wallet.destroy()
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('2 monitor runOnce', async () => {
|
|
70
|
+
const setup = await createSetup('test')
|
|
170
71
|
|
|
171
72
|
await setup.monitor.runOnce()
|
|
172
73
|
|
|
@@ -174,23 +75,7 @@ describe('localWallet tests', () => {
|
|
|
174
75
|
})
|
|
175
76
|
|
|
176
77
|
test('3 return active to cloud client', async () => {
|
|
177
|
-
const
|
|
178
|
-
if (_tu.noTestEnv(chain)) return
|
|
179
|
-
const env = _tu.getEnv(chain)
|
|
180
|
-
if (!env.testIdentityKey)
|
|
181
|
-
throw new sdk.WERR_INVALID_PARAMETER('env.testIdentityKey', 'valid')
|
|
182
|
-
if (!env.testFilePath)
|
|
183
|
-
throw new sdk.WERR_INVALID_PARAMETER('env.testFilePath', 'valid')
|
|
184
|
-
|
|
185
|
-
const setup = await _tu.createTestWallet({
|
|
186
|
-
chain,
|
|
187
|
-
rootKeyHex: env.devKeys[env.testIdentityKey],
|
|
188
|
-
filePath: env.testFilePath,
|
|
189
|
-
setActiveClient: false,
|
|
190
|
-
addLocalBackup: false
|
|
191
|
-
})
|
|
192
|
-
|
|
193
|
-
console.log(`ACTIVE STORAGE: ${setup.storage.getActiveStoreName()}`)
|
|
78
|
+
const setup = await createSetup('test')
|
|
194
79
|
|
|
195
80
|
const localBalance = await setup.wallet.balance()
|
|
196
81
|
|
|
@@ -205,4 +90,272 @@ describe('localWallet tests', () => {
|
|
|
205
90
|
|
|
206
91
|
await setup.wallet.destroy()
|
|
207
92
|
})
|
|
93
|
+
|
|
94
|
+
test('4 review change utxos', async () => {
|
|
95
|
+
const setup = await createSetup('test')
|
|
96
|
+
|
|
97
|
+
const storage = setup.activeStorage
|
|
98
|
+
const services = setup.services
|
|
99
|
+
|
|
100
|
+
const { invalidSpendableOutputs: notUtxos } = await confirmSpendableOutputs(
|
|
101
|
+
storage,
|
|
102
|
+
services
|
|
103
|
+
)
|
|
104
|
+
const outputsToUpdate = notUtxos.map(o => ({
|
|
105
|
+
id: o.outputId,
|
|
106
|
+
satoshis: o.satoshis
|
|
107
|
+
}))
|
|
108
|
+
|
|
109
|
+
const total: number = outputsToUpdate.reduce((t, o) => t + o.satoshis, 0)
|
|
110
|
+
|
|
111
|
+
debugger
|
|
112
|
+
// *** About set spendable = false for outputs ***/
|
|
113
|
+
for (const o of outputsToUpdate) {
|
|
114
|
+
await storage.updateOutput(o.id, { spendable: false })
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
await setup.wallet.destroy()
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
test('5 review synchunk', async () => {
|
|
121
|
+
const setup = await createSetup('test')
|
|
122
|
+
|
|
123
|
+
const identityKey = setup.identityKey
|
|
124
|
+
const reader = setup.activeStorage
|
|
125
|
+
const readerSettings = reader.getSettings()
|
|
126
|
+
const writer = setup.storage._backups![0].storage
|
|
127
|
+
const writerSettings = writer.getSettings()
|
|
128
|
+
|
|
129
|
+
const ss = await EntitySyncState.fromStorage(
|
|
130
|
+
writer,
|
|
131
|
+
identityKey,
|
|
132
|
+
readerSettings
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
const args = ss.makeRequestSyncChunkArgs(
|
|
136
|
+
identityKey,
|
|
137
|
+
writerSettings.storageIdentityKey
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
const chunk = await reader.getSyncChunk(args)
|
|
141
|
+
|
|
142
|
+
await setup.wallet.destroy()
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
test('6 backup', async () => {
|
|
146
|
+
const setup = await createSetup('test')
|
|
147
|
+
|
|
148
|
+
await setup.storage.updateBackups()
|
|
149
|
+
|
|
150
|
+
await setup.wallet.destroy()
|
|
151
|
+
})
|
|
208
152
|
})
|
|
153
|
+
|
|
154
|
+
async function createSetup(chain: sdk.Chain): Promise<TestWalletNoSetup> {
|
|
155
|
+
const env = _tu.getEnv(chain)
|
|
156
|
+
if (!env.testIdentityKey)
|
|
157
|
+
throw new sdk.WERR_INVALID_PARAMETER('env.testIdentityKey', 'valid')
|
|
158
|
+
if (!env.testFilePath)
|
|
159
|
+
throw new sdk.WERR_INVALID_PARAMETER('env.testFilePath', 'valid')
|
|
160
|
+
|
|
161
|
+
const setup = await _tu.createTestWallet({
|
|
162
|
+
chain,
|
|
163
|
+
rootKeyHex: env.devKeys[env.testIdentityKey],
|
|
164
|
+
filePath: env.testFilePath,
|
|
165
|
+
setActiveClient: false,
|
|
166
|
+
addLocalBackup: false
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
console.log(`ACTIVE STORAGE: ${setup.storage.getActiveStoreName()}`)
|
|
170
|
+
|
|
171
|
+
return setup
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function createOneSatTestOutput(
|
|
175
|
+
setup: TestWalletNoSetup,
|
|
176
|
+
options: CreateActionOptions = {},
|
|
177
|
+
howMany: number = 1
|
|
178
|
+
): Promise<CreateActionResult> {
|
|
179
|
+
let car: CreateActionResult = {}
|
|
180
|
+
|
|
181
|
+
for (let i = 0; i < howMany; i++) {
|
|
182
|
+
const args: CreateActionArgs = {
|
|
183
|
+
outputs: [
|
|
184
|
+
{
|
|
185
|
+
lockingScript: new P2PKH()
|
|
186
|
+
.lock(PublicKey.fromString(setup.identityKey).toAddress())
|
|
187
|
+
.toHex(),
|
|
188
|
+
satoshis: 1,
|
|
189
|
+
outputDescription: 'test output',
|
|
190
|
+
customInstructions: JSON.stringify({
|
|
191
|
+
type: 'P2PKH',
|
|
192
|
+
key: 'identity'
|
|
193
|
+
}),
|
|
194
|
+
basket: 'test-output'
|
|
195
|
+
}
|
|
196
|
+
],
|
|
197
|
+
description: 'create test output',
|
|
198
|
+
options: {
|
|
199
|
+
...options
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
car = await setup.wallet.createAction(args)
|
|
203
|
+
expect(car.txid)
|
|
204
|
+
|
|
205
|
+
const req = await EntityProvenTxReq.fromStorageTxid(
|
|
206
|
+
setup.activeStorage,
|
|
207
|
+
car.txid!
|
|
208
|
+
)
|
|
209
|
+
expect(req !== undefined && req.history.notes !== undefined)
|
|
210
|
+
if (req && req.history.notes) {
|
|
211
|
+
expect(req.status === 'unsent')
|
|
212
|
+
expect(req.history.notes.length === 1)
|
|
213
|
+
const n = req.history.notes[0]
|
|
214
|
+
expect(n.what === 'status' && n.status_now === 'unsent')
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return car
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async function recoverOneSatTestOutputs(
|
|
221
|
+
setup: TestWalletNoSetup
|
|
222
|
+
): Promise<void> {
|
|
223
|
+
const outputs = await setup.wallet.listOutputs({
|
|
224
|
+
basket: 'test-output',
|
|
225
|
+
include: 'entire transactions',
|
|
226
|
+
limit: 1000
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
if (outputs.outputs.length > 8) {
|
|
230
|
+
const args: CreateActionArgs = {
|
|
231
|
+
inputBEEF: outputs.BEEF!,
|
|
232
|
+
inputs: [],
|
|
233
|
+
description: 'recover test output'
|
|
234
|
+
}
|
|
235
|
+
const p2pkh = new P2PKH()
|
|
236
|
+
for (const o of outputs.outputs) {
|
|
237
|
+
args.inputs!.push({
|
|
238
|
+
unlockingScriptLength: 108,
|
|
239
|
+
outpoint: o.outpoint,
|
|
240
|
+
inputDescription: 'recovered test output'
|
|
241
|
+
})
|
|
242
|
+
}
|
|
243
|
+
const car = await setup.wallet.createAction(args)
|
|
244
|
+
expect(car.signableTransaction)
|
|
245
|
+
|
|
246
|
+
const st = car.signableTransaction!
|
|
247
|
+
const beef = Beef.fromBinary(st.tx)
|
|
248
|
+
const tx = beef.findAtomicTransaction(beef.txs.slice(-1)[0].txid)!
|
|
249
|
+
const signArgs: SignActionArgs = {
|
|
250
|
+
reference: st.reference,
|
|
251
|
+
spends: {} // 0: { unlockingScript } },
|
|
252
|
+
}
|
|
253
|
+
for (let i = 0; i < outputs.outputs.length; i++) {
|
|
254
|
+
const o = outputs.outputs[i]
|
|
255
|
+
const unlock = p2pkh.unlock(setup.keyDeriver.rootKey, 'all', false)
|
|
256
|
+
const unlockingScript = (await unlock.sign(tx, i)).toHex()
|
|
257
|
+
signArgs.spends[i] = { unlockingScript }
|
|
258
|
+
}
|
|
259
|
+
const sar = await setup.wallet.signAction(signArgs)
|
|
260
|
+
expect(sar.txid)
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async function trackReqByTxid(
|
|
265
|
+
setup: TestWalletNoSetup,
|
|
266
|
+
txid: string
|
|
267
|
+
): Promise<void> {
|
|
268
|
+
const req = await EntityProvenTxReq.fromStorageTxid(setup.activeStorage, txid)
|
|
269
|
+
|
|
270
|
+
expect(req !== undefined && req.history.notes !== undefined)
|
|
271
|
+
if (!req || !req.history.notes) throw new sdk.WERR_INTERNAL()
|
|
272
|
+
|
|
273
|
+
let newBlocks = 0
|
|
274
|
+
let lastHeight: number | undefined
|
|
275
|
+
for (; req.status !== 'completed'; ) {
|
|
276
|
+
let height = setup.monitor.lastNewHeader?.height
|
|
277
|
+
if (req.status === 'unsent') {
|
|
278
|
+
// send it...
|
|
279
|
+
}
|
|
280
|
+
if (req.status === 'sending') {
|
|
281
|
+
// send it...
|
|
282
|
+
}
|
|
283
|
+
if (req.status === 'unmined') {
|
|
284
|
+
if (height && lastHeight) {
|
|
285
|
+
if (height === lastHeight) {
|
|
286
|
+
await wait(1000 * 60)
|
|
287
|
+
} else {
|
|
288
|
+
newBlocks++
|
|
289
|
+
expect(newBlocks < 5)
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
await setup.monitor.runOnce()
|
|
295
|
+
await req.refreshFromStorage(setup.activeStorage)
|
|
296
|
+
lastHeight = height
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export async function confirmSpendableOutputs(
|
|
301
|
+
storage: StorageKnex,
|
|
302
|
+
services: Services,
|
|
303
|
+
identityKey?: string
|
|
304
|
+
): Promise<{ invalidSpendableOutputs: TableOutput[] }> {
|
|
305
|
+
const invalidSpendableOutputs: TableOutput[] = []
|
|
306
|
+
const partial: Partial<TableUser> = {}
|
|
307
|
+
if (identityKey) partial.identityKey = identityKey
|
|
308
|
+
const users = await storage.findUsers({ partial })
|
|
309
|
+
|
|
310
|
+
for (const { userId } of users) {
|
|
311
|
+
const defaultBasket = verifyOne(
|
|
312
|
+
await storage.findOutputBaskets({ partial: { userId, name: 'default' } })
|
|
313
|
+
)
|
|
314
|
+
const where: Partial<TableOutput> = {
|
|
315
|
+
userId,
|
|
316
|
+
basketId: defaultBasket.basketId,
|
|
317
|
+
spendable: true
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const outputs = await storage.findOutputs({ partial: where })
|
|
321
|
+
|
|
322
|
+
for (let i = outputs.length - 1; i >= 0; i--) {
|
|
323
|
+
const o = outputs[i]
|
|
324
|
+
const oid = verifyId(o.outputId)
|
|
325
|
+
|
|
326
|
+
if (o.spendable) {
|
|
327
|
+
let ok = false
|
|
328
|
+
|
|
329
|
+
if (o.lockingScript && o.lockingScript.length > 0) {
|
|
330
|
+
const r = await services.getUtxoStatus(
|
|
331
|
+
asString(o.lockingScript),
|
|
332
|
+
'script'
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
if (r.status === 'success' && r.isUtxo && r.details?.length > 0) {
|
|
336
|
+
const tx = await storage.findTransactionById(o.transactionId)
|
|
337
|
+
|
|
338
|
+
if (
|
|
339
|
+
tx &&
|
|
340
|
+
tx.txid &&
|
|
341
|
+
r.details.some(
|
|
342
|
+
d =>
|
|
343
|
+
d.txid === tx.txid &&
|
|
344
|
+
d.satoshis === o.satoshis &&
|
|
345
|
+
d.index === o.vout
|
|
346
|
+
)
|
|
347
|
+
) {
|
|
348
|
+
ok = true
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (!ok) {
|
|
354
|
+
invalidSpendableOutputs.push(o)
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return { invalidSpendableOutputs }
|
|
361
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Services, StorageKnex } from '../../../src'
|
|
2
|
+
import { _tu } from '../../utils/TestUtilsWalletStorage'
|
|
3
|
+
import { confirmSpendableOutputs } from '../local/localWallet.man.test'
|
|
4
|
+
|
|
5
|
+
describe('janitor tests', () => {
|
|
6
|
+
jest.setTimeout(99999999)
|
|
7
|
+
|
|
8
|
+
test('0 review utxos by identity key', async () => {
|
|
9
|
+
const env = _tu.getEnv('main')
|
|
10
|
+
if (!env.cloudMySQLConnection) return
|
|
11
|
+
|
|
12
|
+
const connection = JSON.parse(env.cloudMySQLConnection)
|
|
13
|
+
const storage = new StorageKnex({
|
|
14
|
+
...StorageKnex.defaultOptions(),
|
|
15
|
+
knex: _tu.createMySQLFromConnection(connection),
|
|
16
|
+
chain: env.chain
|
|
17
|
+
})
|
|
18
|
+
await storage.makeAvailable()
|
|
19
|
+
|
|
20
|
+
const services = new Services(env.chain)
|
|
21
|
+
|
|
22
|
+
const identityKey =
|
|
23
|
+
'03894bda9b11626c0280ec28f0d0193e9bd34446679ed1b32e5621e94e0e807073'
|
|
24
|
+
const { invalidSpendableOutputs: notUtxos } = await confirmSpendableOutputs(
|
|
25
|
+
storage,
|
|
26
|
+
services,
|
|
27
|
+
identityKey
|
|
28
|
+
)
|
|
29
|
+
const outputsToUpdate = notUtxos.map(o => ({
|
|
30
|
+
id: o.outputId,
|
|
31
|
+
satoshis: o.satoshis
|
|
32
|
+
}))
|
|
33
|
+
|
|
34
|
+
const total: number = outputsToUpdate.reduce((t, o) => t + o.satoshis, 0)
|
|
35
|
+
|
|
36
|
+
debugger
|
|
37
|
+
// *** About set spendable = false for outputs ***/
|
|
38
|
+
for (const o of outputsToUpdate) {
|
|
39
|
+
await storage.updateOutput(o.id, { spendable: false })
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
await storage.destroy()
|
|
43
|
+
})
|
|
44
|
+
})
|