edge-currency-plugins 3.15.0 → 3.16.0
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/android/src/main/assets/edge-currency-plugins/edge-currency-plugins.js +48 -48
- package/lib/common/plugin/EngineEmitter.js +10 -0
- package/lib/common/plugin/PluginState.js +87 -1
- package/lib/common/utxobased/engine/ServerStates.js +240 -8
- package/lib/common/utxobased/engine/UtxoEngineProcessor.js +112 -22
- package/lib/common/utxobased/network/Blockbook.js +6 -1
- package/lib/common/utxobased/network/Socket.js +8 -2
- package/lib/src/common/plugin/EngineEmitter.d.ts +7 -2
- package/lib/src/common/plugin/PluginState.d.ts +30 -0
- package/lib/src/common/utxobased/engine/ServerStates.d.ts +31 -0
- package/lib/src/common/utxobased/engine/UtxoEngineProcessor.d.ts +3 -1
- package/lib/src/common/utxobased/network/Blockbook.d.ts +3 -0
- package/lib/src/common/utxobased/network/Socket.d.ts +4 -0
- package/lib/test/common/plugin/PluginState.spec.d.ts +1 -0
- package/lib/test/common/utxobased/engine/AddressRepair.spec.d.ts +1 -0
- package/package.json +1 -1
|
@@ -156,9 +156,25 @@ var _utils3 = require('./utils');
|
|
|
156
156
|
* 2. Address UTXO processing
|
|
157
157
|
**/
|
|
158
158
|
const processesPerAddress = 2
|
|
159
|
-
|
|
159
|
+
// Which of the two processes have completed, per address (by scriptPubkey).
|
|
160
|
+
// Flags rather than a running count, so an address re-queued after a server
|
|
161
|
+
// drop is not counted twice and the ratio cannot pass 1 with work pending.
|
|
162
|
+
const progressByScriptPubkey = new Map
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
()
|
|
160
166
|
let processedPercent = 0 // last sync ratio emitted
|
|
161
|
-
const updateProgressRatio = async (
|
|
167
|
+
const updateProgressRatio = async (
|
|
168
|
+
scriptPubkey,
|
|
169
|
+
process
|
|
170
|
+
) => {
|
|
171
|
+
const progress = _nullishCoalesce(progressByScriptPubkey.get(scriptPubkey), () => ( {
|
|
172
|
+
txs: false,
|
|
173
|
+
utxos: false
|
|
174
|
+
}))
|
|
175
|
+
progress[process] = true
|
|
176
|
+
progressByScriptPubkey.set(scriptPubkey, progress)
|
|
177
|
+
|
|
162
178
|
// Avoid re-sending sync ratios / sending ratios larger than 1
|
|
163
179
|
if (processedPercent >= 1) return
|
|
164
180
|
|
|
@@ -167,8 +183,11 @@ var _utils3 = require('./utils');
|
|
|
167
183
|
const expectedProcessCount =
|
|
168
184
|
Object.keys(taskCache.addressSubscribeCache).length * processesPerAddress
|
|
169
185
|
|
|
170
|
-
|
|
171
|
-
|
|
186
|
+
let processedCount = 0
|
|
187
|
+
for (const { txs, utxos } of progressByScriptPubkey.values()) {
|
|
188
|
+
if (txs) processedCount++
|
|
189
|
+
if (utxos) processedCount++
|
|
190
|
+
}
|
|
172
191
|
|
|
173
192
|
// If we have no addresses, we should not have not yet began processing.
|
|
174
193
|
if (expectedProcessCount === 0) throw new Error('No addresses to process')
|
|
@@ -244,8 +263,14 @@ var _utils3 = require('./utils');
|
|
|
244
263
|
if (running) return
|
|
245
264
|
running = true
|
|
246
265
|
|
|
247
|
-
|
|
248
|
-
|
|
266
|
+
try {
|
|
267
|
+
await initializeAddressSubscriptions()
|
|
268
|
+
await setLookAhead(common)
|
|
269
|
+
} catch (error) {
|
|
270
|
+
// Let the next call try again instead of skipping the initialization:
|
|
271
|
+
running = false
|
|
272
|
+
throw error
|
|
273
|
+
}
|
|
249
274
|
}
|
|
250
275
|
|
|
251
276
|
emitter.on(
|
|
@@ -265,6 +290,30 @@ var _utils3 = require('./utils');
|
|
|
265
290
|
}
|
|
266
291
|
)
|
|
267
292
|
|
|
293
|
+
emitter.on(_EngineEmitter.EngineEvent.SERVER_DROPPED, (uri) => {
|
|
294
|
+
// Everything this server told us about may stop short of the chain tip.
|
|
295
|
+
// Re-open every address so the surviving connection re-queries it. Each
|
|
296
|
+
// query starts from the address's stored lastQueriedBlockHeight, which is
|
|
297
|
+
// never above the height of the server that answered it, so the range
|
|
298
|
+
// the dropped server missed is fetched again.
|
|
299
|
+
let requeued = 0
|
|
300
|
+
for (const [address, cacheItem] of Object.entries(
|
|
301
|
+
taskCache.addressSubscribeCache
|
|
302
|
+
)) {
|
|
303
|
+
if (!cacheItem.processing) continue
|
|
304
|
+
cacheItem.processing = false
|
|
305
|
+
taskCache.addressForUtxosCache[address] = {
|
|
306
|
+
processing: false,
|
|
307
|
+
path: cacheItem.path
|
|
308
|
+
}
|
|
309
|
+
// The re-query has to complete again before the address counts as
|
|
310
|
+
// processed:
|
|
311
|
+
progressByScriptPubkey.delete(walletTools.addressToScriptPubkey(address))
|
|
312
|
+
requeued++
|
|
313
|
+
}
|
|
314
|
+
log(`${uri} dropped, re-queued ${requeued} addresses`)
|
|
315
|
+
})
|
|
316
|
+
|
|
268
317
|
emitter.on(
|
|
269
318
|
_EngineEmitter.EngineEvent.NEW_ADDRESS_TRANSACTION,
|
|
270
319
|
async (_uri, response) => {
|
|
@@ -290,6 +339,30 @@ var _utils3 = require('./utils');
|
|
|
290
339
|
}
|
|
291
340
|
)
|
|
292
341
|
|
|
342
|
+
// The DataLayer can count an address that it can no longer read, such as
|
|
343
|
+
// when a write to disk was cut short. Every address derives from the wallet
|
|
344
|
+
// keys and its path, so rebuild the missing row instead of failing the
|
|
345
|
+
// engine. The rebuilt row has no sync state, so the address syncs again.
|
|
346
|
+
const repairAddress = async (path) => {
|
|
347
|
+
const { format, changeIndex, addressIndex } = path
|
|
348
|
+
const pathName = `${format}/${changeIndex}/${addressIndex}`
|
|
349
|
+
log.warn(`Repairing missing data-layer address with '${pathName}' path`)
|
|
350
|
+
|
|
351
|
+
const { scriptPubkey, redeemScript } = walletTools.getScriptPubkey(path)
|
|
352
|
+
await dataLayer.saveAddress(
|
|
353
|
+
_types5.makeAddressData.call(void 0, { scriptPubkey, redeemScript, path })
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
// Read the row back, since the save keeps any state that survived:
|
|
357
|
+
const addressData = await dataLayer.fetchAddress(path)
|
|
358
|
+
if (addressData == null) {
|
|
359
|
+
throw new Error(
|
|
360
|
+
`Missing data-layer address with '${pathName}' path during initialization`
|
|
361
|
+
)
|
|
362
|
+
}
|
|
363
|
+
return addressData
|
|
364
|
+
}
|
|
365
|
+
|
|
293
366
|
// Initialize the addressSubscribeCache with the existing addresses already
|
|
294
367
|
// processed by the DataLayer. This happens only once before any call to
|
|
295
368
|
// setLookAhead.
|
|
@@ -318,16 +391,13 @@ var _utils3 = require('./utils');
|
|
|
318
391
|
addressIndex < branchAddressCount;
|
|
319
392
|
addressIndex++
|
|
320
393
|
) {
|
|
321
|
-
const
|
|
394
|
+
const path = {
|
|
322
395
|
format,
|
|
323
396
|
changeIndex: branch,
|
|
324
397
|
addressIndex
|
|
325
|
-
})
|
|
326
|
-
if (addressData == null) {
|
|
327
|
-
throw new Error(
|
|
328
|
-
`Missing data-layer address with '${format}/${branch}/${addressIndex}' path during initialization`
|
|
329
|
-
)
|
|
330
398
|
}
|
|
399
|
+
const addressData =
|
|
400
|
+
await _asyncNullishCoalesce((await dataLayer.fetchAddress(path)), async () => ( (await repairAddress(path))))
|
|
331
401
|
const { address } = walletTools.scriptPubkeyToAddress({
|
|
332
402
|
changePath,
|
|
333
403
|
scriptPubkey: addressData.scriptPubkey
|
|
@@ -360,7 +430,7 @@ var _utils3 = require('./utils');
|
|
|
360
430
|
return {
|
|
361
431
|
processedPercent,
|
|
362
432
|
async start() {
|
|
363
|
-
|
|
433
|
+
progressByScriptPubkey.clear()
|
|
364
434
|
processedPercent = 0
|
|
365
435
|
|
|
366
436
|
await run()
|
|
@@ -694,6 +764,12 @@ const internalGetFreshAddress = async (
|
|
|
694
764
|
|
|
695
765
|
|
|
696
766
|
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
|
|
772
|
+
|
|
697
773
|
|
|
698
774
|
|
|
699
775
|
|
|
@@ -1208,6 +1284,17 @@ async function* processAddressForTransactions(
|
|
|
1208
1284
|
throw new Error(`could not find address with script pubkey ${scriptPubkey}`)
|
|
1209
1285
|
}
|
|
1210
1286
|
|
|
1287
|
+
// The address was queued with the height of whichever server subscribed
|
|
1288
|
+
// it, but any server can answer the query. Never record more than the
|
|
1289
|
+
// answering server has indexed, so a lagging server cannot move
|
|
1290
|
+
// lastQueriedBlockHeight past blocks it never saw. A height of 0 means this
|
|
1291
|
+
// server has not reported one yet, so nothing it answers can be trusted to
|
|
1292
|
+
// move the cursor; the next query covers the range again.
|
|
1293
|
+
const queriedHeight =
|
|
1294
|
+
serverState.blockHeight > 0
|
|
1295
|
+
? Math.min(blockHeight, serverState.blockHeight)
|
|
1296
|
+
: addressData.lastQueriedBlockHeight
|
|
1297
|
+
|
|
1211
1298
|
try {
|
|
1212
1299
|
const addressResponse = yield* common.serverStates.addressQueryTask(
|
|
1213
1300
|
serverUri,
|
|
@@ -1287,23 +1374,25 @@ async function* processAddressForTransactions(
|
|
|
1287
1374
|
addressForTransactionsCache[address] = {
|
|
1288
1375
|
...cacheItem,
|
|
1289
1376
|
processing: false,
|
|
1290
|
-
page: page + 1
|
|
1377
|
+
page: page + 1,
|
|
1378
|
+
blockHeight: queriedHeight
|
|
1291
1379
|
}
|
|
1292
1380
|
return true
|
|
1293
1381
|
}
|
|
1294
1382
|
|
|
1295
1383
|
// Update the lastQueriedBlockHeight for the address
|
|
1296
|
-
addressData.lastQueriedBlockHeight =
|
|
1384
|
+
addressData.lastQueriedBlockHeight = queriedHeight
|
|
1297
1385
|
|
|
1298
1386
|
// Save/update the fully-processed address
|
|
1299
1387
|
await common.dataLayer.saveAddress(addressData)
|
|
1300
1388
|
|
|
1301
|
-
//
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
// Call setLookAhead to update the lookahead
|
|
1389
|
+
// Extend the lookahead before counting progress, so any addresses it adds
|
|
1390
|
+
// are in the total before this address's flag can bring the ratio to 1:
|
|
1305
1391
|
await setLookAhead(common)
|
|
1306
1392
|
|
|
1393
|
+
// Update the progress now that the transactions for an address have processed
|
|
1394
|
+
await common.updateProgressRatio(scriptPubkey, 'txs')
|
|
1395
|
+
|
|
1307
1396
|
// Add the address to the server cache
|
|
1308
1397
|
serverState.addresses.add(address)
|
|
1309
1398
|
|
|
@@ -1573,13 +1662,14 @@ const processDataLayerUtxos = async (
|
|
|
1573
1662
|
})
|
|
1574
1663
|
}
|
|
1575
1664
|
|
|
1576
|
-
//
|
|
1577
|
-
await common.updateProgressRatio()
|
|
1578
|
-
|
|
1665
|
+
// Extend the lookahead first, as in processAddressForTransactions:
|
|
1579
1666
|
await setLookAhead(common).catch(err => {
|
|
1580
1667
|
common.log.error(err)
|
|
1581
1668
|
throw err
|
|
1582
1669
|
})
|
|
1670
|
+
|
|
1671
|
+
// Update the progress now that the UTXOs for an address have been processed
|
|
1672
|
+
await common.updateProgressRatio(scriptPubkey, 'utxos')
|
|
1583
1673
|
}
|
|
1584
1674
|
|
|
1585
1675
|
/**
|
|
@@ -103,6 +103,9 @@ var _Socket = require('./Socket');
|
|
|
103
103
|
|
|
104
104
|
|
|
105
105
|
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
106
109
|
|
|
107
110
|
|
|
108
111
|
|
|
@@ -277,7 +280,9 @@ var _Socket = require('./Socket');
|
|
|
277
280
|
taskGeneratorFn,
|
|
278
281
|
log,
|
|
279
282
|
emitter: socketEmitter,
|
|
280
|
-
walletId
|
|
283
|
+
walletId,
|
|
284
|
+
keepAliveMs: config.keepAliveMs,
|
|
285
|
+
wakeUpMs: config.wakeUpMs
|
|
281
286
|
})
|
|
282
287
|
|
|
283
288
|
async function ping() {
|
|
@@ -87,6 +87,10 @@ const WAKE_UP_MS = 5000 // interval at which we wakeUp and potentially onQueueSp
|
|
|
87
87
|
|
|
88
88
|
|
|
89
89
|
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
90
94
|
|
|
91
95
|
|
|
92
96
|
|
|
@@ -134,6 +138,8 @@ const asResponseDefault = (raw) => [
|
|
|
134
138
|
let connected = false
|
|
135
139
|
let cancelConnect = false
|
|
136
140
|
const timeout = 1000 * (_nullishCoalesce(config.timeout, () => ( 30)))
|
|
141
|
+
const keepAliveMs = _nullishCoalesce(config.keepAliveMs, () => ( KEEP_ALIVE_MS))
|
|
142
|
+
const wakeUpMs = _nullishCoalesce(config.wakeUpMs, () => ( WAKE_UP_MS))
|
|
137
143
|
let trackedError
|
|
138
144
|
let timer
|
|
139
145
|
|
|
@@ -264,7 +270,7 @@ const asResponseDefault = (raw) => [
|
|
|
264
270
|
const onTimer = () => {
|
|
265
271
|
log(`socket timer with server ${uri} expired, check if healthCheck needed`)
|
|
266
272
|
const now = Date.now() - TIMER_SLACK
|
|
267
|
-
if (lastKeepAlive +
|
|
273
|
+
if (lastKeepAlive + keepAliveMs < now) {
|
|
268
274
|
log(`submitting healthCheck to server ${uri}`)
|
|
269
275
|
lastKeepAlive = now
|
|
270
276
|
config
|
|
@@ -291,7 +297,7 @@ const asResponseDefault = (raw) => [
|
|
|
291
297
|
|
|
292
298
|
const setupTimer = () => {
|
|
293
299
|
log(`setupTimer with server ${uri}`)
|
|
294
|
-
let nextWakeUp = lastWakeUp +
|
|
300
|
+
let nextWakeUp = lastWakeUp + wakeUpMs
|
|
295
301
|
for (const request of Object.values(pendingRequests)) {
|
|
296
302
|
const to = request.startTime + timeout
|
|
297
303
|
if (to < nextWakeUp) nextWakeUp = to
|
|
@@ -6,11 +6,11 @@ export declare interface EngineEmitter {
|
|
|
6
6
|
emit: ((event: EngineEvent.SEEN_TX_CHECKPOINT, checkpoint: string) => boolean) & ((event: EngineEvent.TRANSACTIONS, transactionEvents: EdgeTransactionEvent[]) => boolean) & ((event: EngineEvent.TRANSACTIONS_CHANGED, transactions: EdgeTransaction[]) => boolean) & ((event: EngineEvent.ADDRESS_BALANCE_CHANGED, currencyCode: string, addressBalanceChanges: Array<{
|
|
7
7
|
scriptPubkey: string;
|
|
8
8
|
balance: string;
|
|
9
|
-
}>) => boolean) & ((event: EngineEvent.WALLET_BALANCE_CHANGED, currencyCode: string, nativeBalance: string) => boolean) & ((event: EngineEvent.BLOCK_HEIGHT_CHANGED, uri: string, blockHeight: number) => boolean) & ((event: EngineEvent.NEW_ADDRESS_TRANSACTION, uri: string, newTx: SubscribeAddressResponse) => boolean) & ((event: EngineEvent.ADDRESSES_CHECKED, progressRatio: number) => boolean) & ((event: EngineEvent.TXIDS_CHANGED, txids: EdgeTxidMap) => boolean);
|
|
9
|
+
}>) => boolean) & ((event: EngineEvent.WALLET_BALANCE_CHANGED, currencyCode: string, nativeBalance: string) => boolean) & ((event: EngineEvent.BLOCK_HEIGHT_CHANGED, uri: string, blockHeight: number) => boolean) & ((event: EngineEvent.NEW_ADDRESS_TRANSACTION, uri: string, newTx: SubscribeAddressResponse) => boolean) & ((event: EngineEvent.ADDRESSES_CHECKED, progressRatio: number) => boolean) & ((event: EngineEvent.TXIDS_CHANGED, txids: EdgeTxidMap) => boolean) & ((event: EngineEvent.SERVER_DROPPED, uri: string) => boolean);
|
|
10
10
|
on: ((event: EngineEvent.SEEN_TX_CHECKPOINT, listener: (checkpoint: string) => Promise<void> | void) => this) & ((event: EngineEvent.TRANSACTIONS, listener: (transactionEvents: EdgeTransactionEvent[]) => Promise<void> | void) => boolean) & ((event: EngineEvent.TRANSACTIONS_CHANGED, listener: (transactions: EdgeTransaction[]) => Promise<void> | void) => this) & ((event: EngineEvent.ADDRESS_BALANCE_CHANGED, listener: (currencyCode: string, addressBalanceChanges: Array<{
|
|
11
11
|
scriptPubkey: string;
|
|
12
12
|
balance: string;
|
|
13
|
-
}>) => Promise<void> | void) => this) & ((event: EngineEvent.WALLET_BALANCE_CHANGED, listener: (currencyCode: string, nativeBalance: string) => Promise<void> | void) => this) & ((event: EngineEvent.BLOCK_HEIGHT_CHANGED, listener: (uri: string, blockHeight: number) => Promise<void> | void) => this) & ((event: EngineEvent.NEW_ADDRESS_TRANSACTION, listener: (uri: string, newTx: SubscribeAddressResponse) => Promise<void> | void) => this) & ((event: EngineEvent.ADDRESSES_CHECKED, listener: (progressRatio: number) => Promise<void> | void) => this) & ((event: EngineEvent.TXIDS_CHANGED, listener: (txids: EdgeTxidMap) => Promise<void> | void) => this);
|
|
13
|
+
}>) => Promise<void> | void) => this) & ((event: EngineEvent.WALLET_BALANCE_CHANGED, listener: (currencyCode: string, nativeBalance: string) => Promise<void> | void) => this) & ((event: EngineEvent.BLOCK_HEIGHT_CHANGED, listener: (uri: string, blockHeight: number) => Promise<void> | void) => this) & ((event: EngineEvent.NEW_ADDRESS_TRANSACTION, listener: (uri: string, newTx: SubscribeAddressResponse) => Promise<void> | void) => this) & ((event: EngineEvent.ADDRESSES_CHECKED, listener: (progressRatio: number) => Promise<void> | void) => this) & ((event: EngineEvent.TXIDS_CHANGED, listener: (txids: EdgeTxidMap) => Promise<void> | void) => this) & ((event: EngineEvent.SERVER_DROPPED, listener: (uri: string) => Promise<void> | void) => this);
|
|
14
14
|
}
|
|
15
15
|
export declare class EngineEmitter extends EventEmitter {
|
|
16
16
|
}
|
|
@@ -25,6 +25,11 @@ export declare enum EngineEvent {
|
|
|
25
25
|
NEW_ADDRESS_TRANSACTION = "address:transaction:changed",
|
|
26
26
|
ADDRESSES_CHECKED = "addresses:checked",
|
|
27
27
|
TXIDS_CHANGED = "txids:changed",
|
|
28
|
+
/**
|
|
29
|
+
* The engine disconnected a server on purpose because it was found to be
|
|
30
|
+
* out of sync. Data fetched through it may be behind the chain.
|
|
31
|
+
*/
|
|
32
|
+
SERVER_DROPPED = "server:dropped",
|
|
28
33
|
CONNECTION_OPEN = "connection:open",
|
|
29
34
|
CONNECTION_CLOSE = "connection:close",
|
|
30
35
|
CONNECTION_TIMER = "connection:timer"
|
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
import { Disklet } from 'disklet';
|
|
2
2
|
import { EdgeIo, EdgeLog } from 'edge-core-js/types';
|
|
3
|
+
import { ServerHealth } from '../utxobased/engine/ServerStates';
|
|
3
4
|
import { UtxoUserSettings } from '../utxobased/engine/types';
|
|
4
5
|
import { UtxoEngineProcessor } from '../utxobased/engine/UtxoEngineProcessor';
|
|
5
6
|
import { InfoPayload } from './types';
|
|
7
|
+
/**
|
|
8
|
+
* How long a server dropped for being out of sync stays out of the pick
|
|
9
|
+
* order. Long enough for a lagging indexer to catch up, short enough that a
|
|
10
|
+
* recovered server is tried again without an app restart. After expiry the
|
|
11
|
+
* server is probed again on connect and dropped again if still behind.
|
|
12
|
+
*/
|
|
13
|
+
export declare const SERVER_QUARANTINE_MS: number;
|
|
14
|
+
/**
|
|
15
|
+
* Runs a server's health probe, or hands back the one started for the same
|
|
16
|
+
* URI less than `minIntervalMs` ago, whether it is still in flight or has
|
|
17
|
+
* already answered.
|
|
18
|
+
*/
|
|
19
|
+
export declare type ShareServerProbe = (uri: string, minIntervalMs: number, probe: () => Promise<ServerHealth>) => Promise<ServerHealth>;
|
|
20
|
+
export declare function makeShareServerProbe(): ShareServerProbe;
|
|
6
21
|
/** A JSON object (as opposed to an array or primitive). */
|
|
7
22
|
interface JsonObject {
|
|
8
23
|
[name: string]: unknown;
|
|
@@ -28,6 +43,21 @@ export interface PluginState {
|
|
|
28
43
|
load: () => Promise<PluginState>;
|
|
29
44
|
serverScoreDown: (uri: string) => void;
|
|
30
45
|
serverScoreUp: (uri: string, score: number) => void;
|
|
46
|
+
/**
|
|
47
|
+
* Keeps a server out of getLocalServers for a while. Shared by every
|
|
48
|
+
* engine of this plugin, so one wallet's finding spares the others the
|
|
49
|
+
* same connect-probe-drop cycle. In memory only: it does not outlive the
|
|
50
|
+
* plugin, and a restart gives the server a fresh chance.
|
|
51
|
+
*/
|
|
52
|
+
quarantineServer: (uri: string, durationMs?: number) => void;
|
|
53
|
+
isServerQuarantined: (uri: string) => boolean;
|
|
54
|
+
hasQuarantinedServers: () => boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Shares health probes between every engine of this plugin, so N wallets
|
|
57
|
+
* connected to the same server cost one REST request per interval rather
|
|
58
|
+
* than N. Each engine still applies the answer to its own connection.
|
|
59
|
+
*/
|
|
60
|
+
shareServerProbe: ShareServerProbe;
|
|
31
61
|
clearCache: () => Promise<void>;
|
|
32
62
|
getLocalServers: (numServersWanted: number, includePatterns?: Array<string | RegExp>) => string[];
|
|
33
63
|
refreshServers: (updatedCustomServers?: string[]) => Promise<void>;
|
|
@@ -8,10 +8,20 @@ import { AddressResponse, AddressUtxosResponse, BlockbookTransaction } from '../
|
|
|
8
8
|
import Deferred from '../network/Deferred';
|
|
9
9
|
import { WsTask, WsTaskGenerator } from '../network/Socket';
|
|
10
10
|
import { UtxoInitOptions } from './types';
|
|
11
|
+
/**
|
|
12
|
+
* What the last REST health probe said about a connected server:
|
|
13
|
+
* - `pending`: the probe has not answered yet.
|
|
14
|
+
* - `healthy`: Blockbook reported `inSync: true`.
|
|
15
|
+
* - `unhealthy`: Blockbook reported `inSync: false`.
|
|
16
|
+
* - `unknown`: the server has no HTTP twin to probe, or the probe failed
|
|
17
|
+
* (network error, non-200, unparseable body). Never treated as unhealthy.
|
|
18
|
+
*/
|
|
19
|
+
export declare type ServerHealth = 'pending' | 'healthy' | 'unhealthy' | 'unknown';
|
|
11
20
|
export interface ServerState {
|
|
12
21
|
blockbook: Blockbook;
|
|
13
22
|
blockSubscriptionStatus: 'unsubscribed' | 'subscribing' | 'subscribed';
|
|
14
23
|
blockHeight: number;
|
|
24
|
+
health: ServerHealth;
|
|
15
25
|
txids: Set<string>;
|
|
16
26
|
addresses: Set<string>;
|
|
17
27
|
}
|
|
@@ -28,6 +38,14 @@ interface ServerStateConfig {
|
|
|
28
38
|
* seconds the socket layer uses for a request. Exposed for tests.
|
|
29
39
|
*/
|
|
30
40
|
broadcastTimeoutMs?: number;
|
|
41
|
+
/**
|
|
42
|
+
* Timing overrides so tests can drive the keepalive probe cycle in
|
|
43
|
+
* milliseconds rather than minutes. Production uses the defaults.
|
|
44
|
+
*/
|
|
45
|
+
healthProbeMinIntervalMs?: number;
|
|
46
|
+
healthProbeTimeoutMs?: number;
|
|
47
|
+
keepAliveMs?: number;
|
|
48
|
+
wakeUpMs?: number;
|
|
31
49
|
}
|
|
32
50
|
export interface ServerStates {
|
|
33
51
|
setPickNextTaskCB: (callback: (uri: string) => AsyncGenerator<WsTask<unknown> | boolean>) => void;
|
|
@@ -43,6 +61,7 @@ export interface ServerStates {
|
|
|
43
61
|
watchAddresses: (uri: string, addresses: string[], deferredAddressSub?: Deferred<unknown>) => void;
|
|
44
62
|
watchBlocks: (uri: string) => void;
|
|
45
63
|
getBlockHeight: (uri: string) => number;
|
|
64
|
+
getServerHealth: (uri: string) => ServerHealth | undefined;
|
|
46
65
|
addressQueryTask: (serverUri: string, address: string, params: {
|
|
47
66
|
lastQueriedBlockHeight: number;
|
|
48
67
|
page: number;
|
|
@@ -67,5 +86,17 @@ export declare const NOWNODES_BROADCAST_DELAY_MS = 2000;
|
|
|
67
86
|
* open. This mirrors the socket figure.
|
|
68
87
|
*/
|
|
69
88
|
export declare const BROADCAST_ATTEMPT_TIMEOUT_MS = 30000;
|
|
89
|
+
/**
|
|
90
|
+
* Upper bound on a REST health probe. The probe runs off the socket keepalive
|
|
91
|
+
* and must never hold the connection's timer for long.
|
|
92
|
+
*/
|
|
93
|
+
export declare const HEALTH_PROBE_TIMEOUT_MS = 10000;
|
|
94
|
+
/**
|
|
95
|
+
* Probes of one server closer together than this share a single request,
|
|
96
|
+
* across every wallet of the plugin. The connect path and the first
|
|
97
|
+
* keepalive can land within milliseconds of each other, and N wallets each
|
|
98
|
+
* run their own keepalive; one answer serves them all.
|
|
99
|
+
*/
|
|
100
|
+
export declare const HEALTH_PROBE_MIN_INTERVAL_MS = 10000;
|
|
70
101
|
export declare function makeServerStates(config: ServerStateConfig): ServerStates;
|
|
71
102
|
export {};
|
|
@@ -42,7 +42,7 @@ interface CommonParams {
|
|
|
42
42
|
emitter: EngineEmitter;
|
|
43
43
|
taskCache: TaskCache;
|
|
44
44
|
pendingTimeouts: Set<NodeJS.Timeout>;
|
|
45
|
-
updateProgressRatio: () => void
|
|
45
|
+
updateProgressRatio: (scriptPubkey: string, process: AddressProcess) => Promise<void>;
|
|
46
46
|
updateSeenTxCheckpoint: () => void;
|
|
47
47
|
io: EdgeIo;
|
|
48
48
|
log: EdgeLog;
|
|
@@ -75,6 +75,8 @@ interface AddressForUtxosCache {
|
|
|
75
75
|
path: ChangePath;
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
|
+
/** The two per-address jobs that make up the sync progress ratio. */
|
|
79
|
+
declare type AddressProcess = 'txs' | 'utxos';
|
|
78
80
|
interface AddressSubscribeCache {
|
|
79
81
|
[key: string]: {
|
|
80
82
|
processing: boolean;
|
|
@@ -42,6 +42,9 @@ interface BlockbookConfig {
|
|
|
42
42
|
ping?: () => Promise<void>;
|
|
43
43
|
socketEmitter: SocketEmitter;
|
|
44
44
|
walletId: string;
|
|
45
|
+
/** Socket timing overrides, for tests. */
|
|
46
|
+
keepAliveMs?: number;
|
|
47
|
+
wakeUpMs?: number;
|
|
45
48
|
}
|
|
46
49
|
export declare function makeBlockbook(config: BlockbookConfig): Blockbook;
|
|
47
50
|
export {};
|
|
@@ -39,6 +39,10 @@ interface SocketConfig {
|
|
|
39
39
|
taskGeneratorFn: TaskGeneratorFn;
|
|
40
40
|
timeout?: number;
|
|
41
41
|
walletId: string;
|
|
42
|
+
/** Keepalive interval override, for tests. Defaults to KEEP_ALIVE_MS. */
|
|
43
|
+
keepAliveMs?: number;
|
|
44
|
+
/** Timer wake-up interval override, for tests. Defaults to WAKE_UP_MS. */
|
|
45
|
+
wakeUpMs?: number;
|
|
42
46
|
}
|
|
43
47
|
export declare type WsResponse = WsResponseMessage[];
|
|
44
48
|
export interface WsResponseMessage {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "edge-currency-plugins",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.16.0",
|
|
4
4
|
"description": "Edge currency plugins to fetch and process wallet accounts, balances and transactions.",
|
|
5
5
|
"homepage": "https://edge.app/",
|
|
6
6
|
"repository": "https://github.com/EdgeApp/edge-currency-plugins",
|