@bopen-io/wallet-toolbox 2.14.5 → 2.15.0-bopen.1
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 +47 -2
- package/README.md +73 -7
- package/docs/client.md +3020 -2729
- package/docs/monitor.md +164 -153
- package/docs/services.md +856 -710
- package/docs/setup.md +46 -1
- package/docs/storage.md +1654 -1636
- package/docs/wallet.md +2090 -1842
- package/out/src/Setup.d.ts +10 -0
- package/out/src/Setup.d.ts.map +1 -1
- package/out/src/Setup.js +24 -0
- package/out/src/Setup.js.map +1 -1
- package/out/src/monitor/MonitorDaemon.d.ts +2 -0
- package/out/src/monitor/MonitorDaemon.d.ts.map +1 -1
- package/out/src/monitor/MonitorDaemon.js +9 -1
- package/out/src/monitor/MonitorDaemon.js.map +1 -1
- package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageKnex.d.ts +5 -0
- package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageKnex.d.ts.map +1 -1
- package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageKnex.js +11 -3
- package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageKnex.js.map +1 -1
- package/out/src/storage/StorageKnex.d.ts +3 -0
- package/out/src/storage/StorageKnex.d.ts.map +1 -1
- package/out/src/storage/StorageKnex.js +115 -207
- package/out/src/storage/StorageKnex.js.map +1 -1
- package/out/src/storage/adminServer/adminServer.d.ts.map +1 -1
- package/out/src/storage/adminServer/adminServer.js +3 -41
- package/out/src/storage/adminServer/adminServer.js.map +1 -1
- package/out/src/storage/adminServer/reqReviewQuery.d.ts +41 -0
- package/out/src/storage/adminServer/reqReviewQuery.d.ts.map +1 -0
- package/out/src/storage/adminServer/reqReviewQuery.js +67 -0
- package/out/src/storage/adminServer/reqReviewQuery.js.map +1 -0
- package/out/src/storage/index.all.d.ts +1 -0
- package/out/src/storage/index.all.d.ts.map +1 -1
- package/out/src/storage/index.all.js +1 -0
- package/out/src/storage/index.all.js.map +1 -1
- package/out/src/storage/knexPostgres.d.ts +16 -0
- package/out/src/storage/knexPostgres.d.ts.map +1 -0
- package/out/src/storage/knexPostgres.js +34 -0
- package/out/src/storage/knexPostgres.js.map +1 -0
- package/out/src/storage/methods/listActionsKnex.d.ts.map +1 -1
- package/out/src/storage/methods/listActionsKnex.js +0 -1
- package/out/src/storage/methods/listActionsKnex.js.map +1 -1
- package/out/src/storage/methods/listOutputsKnex.d.ts.map +1 -1
- package/out/src/storage/methods/listOutputsKnex.js +0 -1
- package/out/src/storage/methods/listOutputsKnex.js.map +1 -1
- package/out/src/storage/methods/purgeData.d.ts.map +1 -1
- package/out/src/storage/methods/purgeData.js +0 -3
- package/out/src/storage/methods/purgeData.js.map +1 -1
- package/out/src/storage/methods/reviewStatus.d.ts.map +1 -1
- package/out/src/storage/methods/reviewStatus.js +0 -3
- package/out/src/storage/methods/reviewStatus.js.map +1 -1
- package/out/src/storage/portable/index.js +38 -12
- package/out/src/storage/portable/index.js.map +1 -1
- package/out/src/storage/remoting/StorageServer.d.ts +17 -4
- package/out/src/storage/remoting/StorageServer.d.ts.map +1 -1
- package/out/src/storage/remoting/StorageServer.js +22 -13
- package/out/src/storage/remoting/StorageServer.js.map +1 -1
- package/out/src/storage/schema/KnexMigrations.d.ts +7 -2
- package/out/src/storage/schema/KnexMigrations.d.ts.map +1 -1
- package/out/src/storage/schema/KnexMigrations.js +162 -60
- package/out/src/storage/schema/KnexMigrations.js.map +1 -1
- package/package.json +5 -4
|
@@ -12,6 +12,7 @@ const WERR_errors_1 = require("../sdk/WERR_errors");
|
|
|
12
12
|
const utilityHelpers_1 = require("../utility/utilityHelpers");
|
|
13
13
|
const managedChange_1 = require("./methods/managedChange");
|
|
14
14
|
const preparedBeef_1 = require("./methods/preparedBeef");
|
|
15
|
+
const knexPostgres_1 = require("./knexPostgres");
|
|
15
16
|
// Keep bulk statements below conservative SQLite/MySQL bind-parameter
|
|
16
17
|
// ceilings. The surrounding transaction still makes a complete pack atomic.
|
|
17
18
|
const ACTION_BATCH_BLOB_SQL_CHUNK = 500;
|
|
@@ -47,7 +48,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
|
|
|
47
48
|
if (options.knex == null)
|
|
48
49
|
throw new WERR_errors_1.WERR_INVALID_PARAMETER('options.knex', 'valid');
|
|
49
50
|
this.knex = options.knex;
|
|
50
|
-
|
|
51
|
+
(0, knexPostgres_1.usePostgresInt8Numbers)(this.knex);
|
|
51
52
|
this.preparedBeefPolicy = (0, preparedBeef_1.validatePreparedBeefPolicy)(options.preparedBeef);
|
|
52
53
|
this.preparedBeefCoordinator = new preparedBeef_1.PreparedBeefCoordinator(this);
|
|
53
54
|
if (this.telemetry.enabled) {
|
|
@@ -260,7 +261,6 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
|
|
|
260
261
|
.whereNull('pb.preparedBeefId')
|
|
261
262
|
.select('o.userId', 'o.txid as rootTxid')
|
|
262
263
|
.groupBy('o.userId', 'o.txid')
|
|
263
|
-
// ?? quotes identifiers: Postgres lowercases unquoted names, so camelCase columns must be quoted.
|
|
264
264
|
.orderByRaw('MIN(??)', ['o.outputId'])
|
|
265
265
|
.limit(limit));
|
|
266
266
|
return rows;
|
|
@@ -387,7 +387,6 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
|
|
|
387
387
|
void this.whereExists(k
|
|
388
388
|
.select('*')
|
|
389
389
|
.from('transactions')
|
|
390
|
-
// ?? quotes identifiers: Postgres lowercases unquoted names, so camelCase columns must be quoted.
|
|
391
390
|
.whereRaw('?? = ?? and ?? = ?', [
|
|
392
391
|
'proven_txs.provenTxId',
|
|
393
392
|
'transactions.provenTxId',
|
|
@@ -415,7 +414,6 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
|
|
|
415
414
|
void this.whereExists(k
|
|
416
415
|
.select('*')
|
|
417
416
|
.from('transactions')
|
|
418
|
-
// ?? quotes identifiers: Postgres lowercases unquoted names, so camelCase columns must be quoted.
|
|
419
417
|
.whereRaw('proven_tx_reqs.txid = transactions.txid and ?? = ?', ['transactions.userId', args.userId]));
|
|
420
418
|
});
|
|
421
419
|
if (args.paged != null) {
|
|
@@ -437,7 +435,6 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
|
|
|
437
435
|
let q = k('tx_labels_map').whereExists(k
|
|
438
436
|
.select('*')
|
|
439
437
|
.from('tx_labels')
|
|
440
|
-
// ?? quotes identifiers: Postgres lowercases unquoted names, so camelCase columns must be quoted.
|
|
441
438
|
.whereRaw('?? = ?? and ?? = ?', [
|
|
442
439
|
'tx_labels.txLabelId',
|
|
443
440
|
'tx_labels_map.txLabelId',
|
|
@@ -463,7 +460,6 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
|
|
|
463
460
|
let q = k('output_tags_map').whereExists(k
|
|
464
461
|
.select('*')
|
|
465
462
|
.from('output_tags')
|
|
466
|
-
// ?? quotes identifiers: Postgres lowercases unquoted names, so camelCase columns must be quoted.
|
|
467
463
|
.whereRaw('?? = ?? and ?? = ?', [
|
|
468
464
|
'output_tags.outputTagId',
|
|
469
465
|
'output_tags_map.outputTagId',
|
|
@@ -536,16 +532,20 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
|
|
|
536
532
|
/**
|
|
537
533
|
* Insert one row and return its auto-increment primary key. MySQL and SQLite
|
|
538
534
|
* report the id as the insert result; Postgres requires a RETURNING clause.
|
|
535
|
+
*
|
|
536
|
+
* `recoverable` marks inserts that the findOrInsert helpers retry as a find
|
|
537
|
+
* after a duplicate-key error, in the same transaction; see `inSavepoint`.
|
|
539
538
|
*/
|
|
540
|
-
async insertReturningId(table, idColumn, row, trx) {
|
|
539
|
+
async insertReturningId(table, idColumn, row, trx, recoverable = false) {
|
|
541
540
|
if (this.dbtype !== 'Postgres') {
|
|
542
541
|
const [id] = await this.toDb(trx)(table).insert(row);
|
|
543
542
|
return id;
|
|
544
543
|
}
|
|
545
|
-
|
|
544
|
+
const insert = async (db) => {
|
|
546
545
|
const [r] = await db(table).insert(row).returning(idColumn);
|
|
547
546
|
return r[idColumn];
|
|
548
|
-
}
|
|
547
|
+
};
|
|
548
|
+
return recoverable ? await this.inSavepoint(trx, insert) : await insert(this.toDb(trx));
|
|
549
549
|
}
|
|
550
550
|
/**
|
|
551
551
|
* The findOrInsert helpers catch a failed insert and find the row again in
|
|
@@ -562,7 +562,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
|
|
|
562
562
|
const e = await this.validateEntityForInsert(tx, trx);
|
|
563
563
|
if (e.provenTxId === 0)
|
|
564
564
|
delete e.provenTxId;
|
|
565
|
-
const id = await this.insertReturningId('proven_txs', 'provenTxId', e, trx);
|
|
565
|
+
const id = await this.insertReturningId('proven_txs', 'provenTxId', e, trx, true);
|
|
566
566
|
tx.provenTxId = id;
|
|
567
567
|
return tx.provenTxId;
|
|
568
568
|
}
|
|
@@ -690,7 +690,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
|
|
|
690
690
|
const e = await this.validateEntityForInsert(tx, trx);
|
|
691
691
|
if (e.provenTxReqId === 0)
|
|
692
692
|
delete e.provenTxReqId;
|
|
693
|
-
const id = await this.insertReturningId('proven_tx_reqs', 'provenTxReqId', e, trx);
|
|
693
|
+
const id = await this.insertReturningId('proven_tx_reqs', 'provenTxReqId', e, trx, true);
|
|
694
694
|
tx.provenTxReqId = id;
|
|
695
695
|
return tx.provenTxReqId;
|
|
696
696
|
}
|
|
@@ -737,7 +737,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
|
|
|
737
737
|
const e = await this.validateEntityForInsert(basket, trx, undefined, ['isDeleted']);
|
|
738
738
|
if (e.basketId === 0)
|
|
739
739
|
delete e.basketId;
|
|
740
|
-
const id = await this.insertReturningId('output_baskets', 'basketId', e, trx);
|
|
740
|
+
const id = await this.insertReturningId('output_baskets', 'basketId', e, trx, true);
|
|
741
741
|
basket.basketId = id;
|
|
742
742
|
return basket.basketId;
|
|
743
743
|
}
|
|
@@ -745,7 +745,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
|
|
|
745
745
|
const e = await this.validateEntityForInsert(tx, trx);
|
|
746
746
|
if (e.transactionId === 0)
|
|
747
747
|
delete e.transactionId;
|
|
748
|
-
const id = await this.insertReturningId('transactions', 'transactionId', e, trx);
|
|
748
|
+
const id = await this.insertReturningId('transactions', 'transactionId', e, trx, true);
|
|
749
749
|
tx.transactionId = id;
|
|
750
750
|
return tx.transactionId;
|
|
751
751
|
}
|
|
@@ -782,7 +782,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
|
|
|
782
782
|
const e = await this.validateEntityForInsert(tag, trx, undefined, ['isDeleted']);
|
|
783
783
|
if (e.outputTagId === 0)
|
|
784
784
|
delete e.outputTagId;
|
|
785
|
-
const id = await this.insertReturningId('output_tags', 'outputTagId', e, trx);
|
|
785
|
+
const id = await this.insertReturningId('output_tags', 'outputTagId', e, trx, true);
|
|
786
786
|
tag.outputTagId = id;
|
|
787
787
|
return tag.outputTagId;
|
|
788
788
|
}
|
|
@@ -794,7 +794,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
|
|
|
794
794
|
const e = await this.validateEntityForInsert(label, trx, undefined, ['isDeleted']);
|
|
795
795
|
if (e.txLabelId === 0)
|
|
796
796
|
delete e.txLabelId;
|
|
797
|
-
const id = await this.insertReturningId('tx_labels', 'txLabelId', e, trx);
|
|
797
|
+
const id = await this.insertReturningId('tx_labels', 'txLabelId', e, trx, true);
|
|
798
798
|
label.txLabelId = id;
|
|
799
799
|
return label.txLabelId;
|
|
800
800
|
}
|
|
@@ -1364,13 +1364,27 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
|
|
|
1364
1364
|
if (isSQLite) {
|
|
1365
1365
|
await this.knex.raw('PRAGMA foreign_keys = OFF;');
|
|
1366
1366
|
}
|
|
1367
|
+
const isPostgres = this.knex.client.dialect === 'postgresql';
|
|
1367
1368
|
try {
|
|
1368
1369
|
const config = {
|
|
1369
|
-
migrationSource: new KnexMigrations_1.KnexMigrations(this.chain, storageName, storageIdentityKey, 1024),
|
|
1370
|
+
migrationSource: new KnexMigrations_1.KnexMigrations(this.chain, storageName, storageIdentityKey, 1024, isPostgres ? 'Postgres' : undefined),
|
|
1370
1371
|
// Keep DDL and its migration journal entry in the same transaction.
|
|
1371
1372
|
disableTransactions: false
|
|
1372
1373
|
};
|
|
1373
|
-
|
|
1374
|
+
if (isPostgres) {
|
|
1375
|
+
// Postgres index migrations run outside a transaction (CREATE INDEX
|
|
1376
|
+
// CONCURRENTLY). When any pending migration does, knex's latest()
|
|
1377
|
+
// journals every migration of the batch outside its transaction, so
|
|
1378
|
+
// run one migration per up() to keep the others atomic with their journal row.
|
|
1379
|
+
for (;;) {
|
|
1380
|
+
const [, applied] = (await this.knex.migrate.up(config));
|
|
1381
|
+
if (applied.length === 0)
|
|
1382
|
+
break;
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
else {
|
|
1386
|
+
await this.knex.migrate.latest(config);
|
|
1387
|
+
}
|
|
1374
1388
|
return await this.knex.migrate.currentVersion(config);
|
|
1375
1389
|
}
|
|
1376
1390
|
finally {
|
|
@@ -1605,7 +1619,6 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
|
|
|
1605
1619
|
void this.select(1)
|
|
1606
1620
|
.from('action_batch_outputs as abo')
|
|
1607
1621
|
.join('action_batches as ab', 'abo.actionBatchId', 'ab.actionBatchId')
|
|
1608
|
-
// ?? quotes identifiers: Postgres lowercases unquoted names, so camelCase columns must be quoted.
|
|
1609
1622
|
.whereRaw('?? = ??', ['abo.outputId', 'o.outputId'])
|
|
1610
1623
|
.whereIn('ab.status', ['active', 'prepared'])
|
|
1611
1624
|
.where('ab.expiresAt', '>', now)
|
|
@@ -1640,7 +1653,6 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
|
|
|
1640
1653
|
void this.select(1)
|
|
1641
1654
|
.from('action_batch_outputs as abo')
|
|
1642
1655
|
.join('action_batches as ab', 'abo.actionBatchId', 'ab.actionBatchId')
|
|
1643
|
-
// ?? quotes identifiers: Postgres lowercases unquoted names, so camelCase columns must be quoted.
|
|
1644
1656
|
.whereRaw('?? = ??', ['abo.outputId', 'o.outputId'])
|
|
1645
1657
|
.whereIn('ab.status', ['active', 'prepared'])
|
|
1646
1658
|
.where('ab.expiresAt', '>', now)
|
|
@@ -1675,7 +1687,6 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
|
|
|
1675
1687
|
void this.select(1)
|
|
1676
1688
|
.from('action_batch_outputs as abo')
|
|
1677
1689
|
.join('action_batches as ab', 'abo.actionBatchId', 'ab.actionBatchId')
|
|
1678
|
-
// ?? quotes identifiers: Postgres lowercases unquoted names, so camelCase columns must be quoted.
|
|
1679
1690
|
.whereRaw('?? = ??', ['abo.outputId', 'o.outputId'])
|
|
1680
1691
|
.whereIn('ab.status', ['active', 'prepared'])
|
|
1681
1692
|
.where('ab.expiresAt', '>', now)
|
|
@@ -1865,7 +1876,6 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
|
|
|
1865
1876
|
const status = ['completed', 'unproven'];
|
|
1866
1877
|
if (!excludeSending)
|
|
1867
1878
|
status.push('sending');
|
|
1868
|
-
let reservedConcurrently = false;
|
|
1869
1879
|
const r = await this.knex.transaction(async (trx) => {
|
|
1870
1880
|
const baseQuery = () => trx('outputs as o')
|
|
1871
1881
|
.join('transactions as t', 'o.transactionId', 't.transactionId')
|
|
@@ -1884,36 +1894,42 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
|
|
|
1884
1894
|
.whereNot('o.derivationSuffix', '')
|
|
1885
1895
|
.whereNull('o.spentBy')
|
|
1886
1896
|
.whereNotExists(function () {
|
|
1887
|
-
// ?? quotes identifiers: Postgres lowercases unquoted names, so camelCase columns must be quoted.
|
|
1888
1897
|
void this.select(1).from('action_batch_outputs as abo').whereRaw('?? = ??', ['abo.outputId', 'o.outputId']);
|
|
1889
1898
|
})
|
|
1890
1899
|
.whereIn('t.status', status)
|
|
1891
1900
|
.select('o.*')
|
|
1892
1901
|
.forUpdate();
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1902
|
+
const excluded = [];
|
|
1903
|
+
const candidates = () => excluded.length > 0 ? baseQuery().whereNotIn('o.outputId', excluded) : baseQuery();
|
|
1904
|
+
const selectOutput = async () => {
|
|
1905
|
+
let output;
|
|
1906
|
+
if (exactSatoshis !== undefined) {
|
|
1907
|
+
output = await candidates().where('o.satoshis', exactSatoshis).orderBy('o.outputId', 'asc').first();
|
|
1908
|
+
}
|
|
1909
|
+
output ??= await candidates()
|
|
1910
|
+
.where('o.satoshis', '>=', targetSatoshis)
|
|
1911
|
+
.orderBy('o.satoshis', 'asc')
|
|
1912
|
+
.orderBy('o.outputId', 'asc')
|
|
1913
|
+
.first();
|
|
1914
|
+
output ??= await candidates()
|
|
1915
|
+
.where('o.satoshis', '<', targetSatoshis)
|
|
1916
|
+
.orderBy('o.satoshis', 'desc')
|
|
1917
|
+
.orderBy('o.outputId', 'desc')
|
|
1918
|
+
.first();
|
|
1919
|
+
return output;
|
|
1920
|
+
};
|
|
1921
|
+
let output = await selectOutput();
|
|
1909
1922
|
// Under Postgres READ COMMITTED the NOT EXISTS above uses the statement's
|
|
1910
1923
|
// snapshot, so an action batch reservation committed while the statement
|
|
1911
|
-
// waited for the row lock is not visible to it.
|
|
1912
|
-
|
|
1924
|
+
// waited for the row lock is not visible to it. Re-check with a new statement.
|
|
1925
|
+
while (output != null &&
|
|
1926
|
+
this.dbtype === 'Postgres' &&
|
|
1913
1927
|
(await this.findReservedActionBatchOutputIds([output.outputId], trx)).length > 0) {
|
|
1914
|
-
|
|
1915
|
-
|
|
1928
|
+
excluded.push(output.outputId);
|
|
1929
|
+
output = await selectOutput();
|
|
1916
1930
|
}
|
|
1931
|
+
if (output == null)
|
|
1932
|
+
return undefined;
|
|
1917
1933
|
await this.updateOutput(output.outputId, {
|
|
1918
1934
|
spendable: false,
|
|
1919
1935
|
spentBy: transactionId
|
|
@@ -1925,8 +1941,6 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
|
|
|
1925
1941
|
output.spentBy = transactionId;
|
|
1926
1942
|
return output;
|
|
1927
1943
|
});
|
|
1928
|
-
if (reservedConcurrently)
|
|
1929
|
-
return await this.allocateChangeInput(userId, basketId, targetSatoshis, exactSatoshis, excludeSending, transactionId);
|
|
1930
1944
|
return r;
|
|
1931
1945
|
}
|
|
1932
1946
|
async markChangeInputsSpent(outputIds, transactionId, trx) {
|
|
@@ -1992,8 +2006,8 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
|
|
|
1992
2006
|
return entities;
|
|
1993
2007
|
}
|
|
1994
2008
|
async adminStats(adminIdentityKey) {
|
|
1995
|
-
if (this.dbtype !== 'MySQL')
|
|
1996
|
-
throw new WERR_errors_1.WERR_NOT_IMPLEMENTED('adminStats, only MySQL
|
|
2009
|
+
if (this.dbtype !== 'MySQL' && this.dbtype !== 'Postgres')
|
|
2010
|
+
throw new WERR_errors_1.WERR_NOT_IMPLEMENTED('adminStats, only MySQL and Postgres are supported');
|
|
1997
2011
|
const monitorEvent = (0, utilityHelpers_1.verifyOneOrNone)(await this.findMonitorEvents({
|
|
1998
2012
|
partial: { event: 'MonitorCallHistory' },
|
|
1999
2013
|
orderDescending: true,
|
|
@@ -2001,153 +2015,66 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
|
|
|
2001
2015
|
}));
|
|
2002
2016
|
const monitorStats = monitorEvent != null ? JSON.parse(monitorEvent.details) : undefined;
|
|
2003
2017
|
const servicesStats = this.getServices().getServicesCallHistory(true);
|
|
2004
|
-
const
|
|
2005
|
-
const
|
|
2006
|
-
const
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
(select sum(o.satoshis) from outputs o, transactions t where o.transactionId = t.transactionId and t.status = 'completed' and o.spendable = 1 and o.change = 1 and o.created_at > '${oneWeekAgo}') as satoshisDefaultWeek,
|
|
2059
|
-
(select sum(o.satoshis) from outputs o, transactions t where o.transactionId = t.transactionId and t.status = 'completed' and o.spendable = 1 and o.change = 1 and o.created_at > '${oneMonthAgo}') as satoshisDefaultMonth,
|
|
2060
|
-
(select sum(o.satoshis) from outputs o, transactions t where o.transactionId = t.transactionId and t.status = 'completed' and o.spendable = 1 and o.change = 1) as satoshisDefaultTotal,
|
|
2061
|
-
(select sum(o.satoshis) from outputs o, transactions t where o.transactionId = t.transactionId and t.status = 'completed' and o.spendable = 1 and o.change = 0 and not o.basketId is null and o.created_at > '${oneDayAgo}') as satoshisOtherDay,
|
|
2062
|
-
(select sum(o.satoshis) from outputs o, transactions t where o.transactionId = t.transactionId and t.status = 'completed' and o.spendable = 1 and o.change = 0 and not o.basketId is null and o.created_at > '${oneWeekAgo}') as satoshisOtherWeek,
|
|
2063
|
-
(select sum(o.satoshis) from outputs o, transactions t where o.transactionId = t.transactionId and t.status = 'completed' and o.spendable = 1 and o.change = 0 and not o.basketId is null and o.created_at > '${oneMonthAgo}') as satoshisOtherMonth,
|
|
2064
|
-
(select sum(o.satoshis) from outputs o, transactions t where o.transactionId = t.transactionId and t.status = 'completed' and o.spendable = 1 and o.change = 0 and not o.basketId is null) as satoshisOtherTotal,
|
|
2065
|
-
(select count(*) from output_baskets where created_at > '${oneDayAgo}') as basketsDay,
|
|
2066
|
-
(select count(*) from output_baskets where created_at > '${oneWeekAgo}') as basketsWeek,
|
|
2067
|
-
(select count(*) from output_baskets where created_at > '${oneMonthAgo}') as basketsMonth,
|
|
2068
|
-
(select count(*) from output_baskets) as basketsTotal,
|
|
2069
|
-
(select count(*) from tx_labels where created_at > '${oneDayAgo}') as labelsDay,
|
|
2070
|
-
(select count(*) from tx_labels where created_at > '${oneWeekAgo}') as labelsWeek,
|
|
2071
|
-
(select count(*) from tx_labels where created_at > '${oneMonthAgo}') as labelsMonth,
|
|
2072
|
-
(select count(*) from tx_labels) as labelsTotal,
|
|
2073
|
-
(select count(*) from output_tags where created_at > '${oneDayAgo}') as tagsDay,
|
|
2074
|
-
(select count(*) from output_tags where created_at > '${oneWeekAgo}') as tagsWeek,
|
|
2075
|
-
(select count(*) from output_tags where created_at > '${oneMonthAgo}') as tagsMonth,
|
|
2076
|
-
(select count(*) from output_tags) as tagsTotal
|
|
2077
|
-
`);
|
|
2018
|
+
const day = 24 * 60 * 60 * 1000;
|
|
2019
|
+
const now = Date.now();
|
|
2020
|
+
const periods = [
|
|
2021
|
+
['Day', new Date(now - day)],
|
|
2022
|
+
['Week', new Date(now - 7 * day)],
|
|
2023
|
+
['Month', new Date(now - 30 * day)],
|
|
2024
|
+
['Total', undefined]
|
|
2025
|
+
];
|
|
2026
|
+
const k = this.knex;
|
|
2027
|
+
const count = (table, createdAt = 'created_at') => (since) => {
|
|
2028
|
+
const q = k(table).count('*');
|
|
2029
|
+
if (since != null)
|
|
2030
|
+
void q.where(createdAt, '>', this.validateDateForWhere(since));
|
|
2031
|
+
return q;
|
|
2032
|
+
};
|
|
2033
|
+
const txCount = (status) => (since) => count('transactions')(since).where({ status });
|
|
2034
|
+
const satoshis = (change) => (since) => {
|
|
2035
|
+
const q = k('outputs as o')
|
|
2036
|
+
.join('transactions as t', 'o.transactionId', 't.transactionId')
|
|
2037
|
+
.where({ 't.status': 'completed', 'o.spendable': true, 'o.change': change })
|
|
2038
|
+
.sum('o.satoshis');
|
|
2039
|
+
if (!change)
|
|
2040
|
+
void q.whereNotNull('o.basketId');
|
|
2041
|
+
if (since != null)
|
|
2042
|
+
void q.where('o.created_at', '>', this.validateDateForWhere(since));
|
|
2043
|
+
return q;
|
|
2044
|
+
};
|
|
2045
|
+
const stats = [
|
|
2046
|
+
['users', count('users')],
|
|
2047
|
+
['transactions', count('transactions')],
|
|
2048
|
+
['txCompleted', txCount('completed')],
|
|
2049
|
+
['txFailed', since => txCount('failed')(since).whereNotNull('txid')],
|
|
2050
|
+
['txAbandoned', since => txCount('failed')(since).whereNull('txid')],
|
|
2051
|
+
['txUnprocessed', txCount('unprocessed')],
|
|
2052
|
+
['txSending', txCount('sending')],
|
|
2053
|
+
['txUnproven', txCount('unproven')],
|
|
2054
|
+
['txUnsigned', txCount('unsigned')],
|
|
2055
|
+
['txNosend', txCount('nosend')],
|
|
2056
|
+
['txNonfinal', txCount('nonfinal')],
|
|
2057
|
+
['txUnfail', txCount('unfail')],
|
|
2058
|
+
['satoshisDefault', satoshis(true)],
|
|
2059
|
+
['satoshisOther', satoshis(false)],
|
|
2060
|
+
['baskets', count('output_baskets')],
|
|
2061
|
+
['labels', count('tx_labels')],
|
|
2062
|
+
['tags', count('output_tags')]
|
|
2063
|
+
];
|
|
2064
|
+
// One statement of scalar subqueries; knex quotes the camelCase identifiers per dialect.
|
|
2065
|
+
const columns = {};
|
|
2066
|
+
for (const [name, query] of stats) {
|
|
2067
|
+
for (const [period, since] of periods)
|
|
2068
|
+
columns[`${name}${period}`] = query(since);
|
|
2069
|
+
}
|
|
2070
|
+
const [row] = (await k.select(columns));
|
|
2071
|
+
const values = Object.fromEntries(Object.keys(columns).map(name => [name, Number(row[name] ?? 0)]));
|
|
2078
2072
|
const r = {
|
|
2079
2073
|
monitorStats,
|
|
2080
2074
|
servicesStats,
|
|
2081
2075
|
requestedBy: adminIdentityKey,
|
|
2082
2076
|
when: new Date().toISOString(),
|
|
2083
|
-
|
|
2084
|
-
usersWeek,
|
|
2085
|
-
usersMonth,
|
|
2086
|
-
usersTotal,
|
|
2087
|
-
transactionsDay,
|
|
2088
|
-
transactionsWeek,
|
|
2089
|
-
transactionsMonth,
|
|
2090
|
-
transactionsTotal,
|
|
2091
|
-
txCompletedDay,
|
|
2092
|
-
txCompletedWeek,
|
|
2093
|
-
txCompletedMonth,
|
|
2094
|
-
txCompletedTotal,
|
|
2095
|
-
txFailedDay,
|
|
2096
|
-
txFailedWeek,
|
|
2097
|
-
txFailedMonth,
|
|
2098
|
-
txFailedTotal,
|
|
2099
|
-
txAbandonedDay,
|
|
2100
|
-
txAbandonedWeek,
|
|
2101
|
-
txAbandonedMonth,
|
|
2102
|
-
txAbandonedTotal,
|
|
2103
|
-
txUnprocessedDay,
|
|
2104
|
-
txUnprocessedWeek,
|
|
2105
|
-
txUnprocessedMonth,
|
|
2106
|
-
txUnprocessedTotal,
|
|
2107
|
-
txSendingDay,
|
|
2108
|
-
txSendingWeek,
|
|
2109
|
-
txSendingMonth,
|
|
2110
|
-
txSendingTotal,
|
|
2111
|
-
txUnprovenDay,
|
|
2112
|
-
txUnprovenWeek,
|
|
2113
|
-
txUnprovenMonth,
|
|
2114
|
-
txUnprovenTotal,
|
|
2115
|
-
txUnsignedDay,
|
|
2116
|
-
txUnsignedWeek,
|
|
2117
|
-
txUnsignedMonth,
|
|
2118
|
-
txUnsignedTotal,
|
|
2119
|
-
txNosendDay,
|
|
2120
|
-
txNosendWeek,
|
|
2121
|
-
txNosendMonth,
|
|
2122
|
-
txNosendTotal,
|
|
2123
|
-
txNonfinalDay,
|
|
2124
|
-
txNonfinalWeek,
|
|
2125
|
-
txNonfinalMonth,
|
|
2126
|
-
txNonfinalTotal,
|
|
2127
|
-
txUnfailDay,
|
|
2128
|
-
txUnfailWeek,
|
|
2129
|
-
txUnfailMonth,
|
|
2130
|
-
txUnfailTotal,
|
|
2131
|
-
satoshisDefaultDay: Number(satoshisDefaultDay),
|
|
2132
|
-
satoshisDefaultWeek: Number(satoshisDefaultWeek),
|
|
2133
|
-
satoshisDefaultMonth: Number(satoshisDefaultMonth),
|
|
2134
|
-
satoshisDefaultTotal: Number(satoshisDefaultTotal),
|
|
2135
|
-
satoshisOtherDay: Number(satoshisOtherDay),
|
|
2136
|
-
satoshisOtherWeek: Number(satoshisOtherWeek),
|
|
2137
|
-
satoshisOtherMonth: Number(satoshisOtherMonth),
|
|
2138
|
-
satoshisOtherTotal: Number(satoshisOtherTotal),
|
|
2139
|
-
basketsDay,
|
|
2140
|
-
basketsWeek,
|
|
2141
|
-
basketsMonth,
|
|
2142
|
-
basketsTotal,
|
|
2143
|
-
labelsDay,
|
|
2144
|
-
labelsWeek,
|
|
2145
|
-
labelsMonth,
|
|
2146
|
-
labelsTotal,
|
|
2147
|
-
tagsDay,
|
|
2148
|
-
tagsWeek,
|
|
2149
|
-
tagsMonth,
|
|
2150
|
-
tagsTotal
|
|
2077
|
+
...values
|
|
2151
2078
|
};
|
|
2152
2079
|
return r;
|
|
2153
2080
|
}
|
|
@@ -2197,23 +2124,4 @@ function pagingKey(table) {
|
|
|
2197
2124
|
return [];
|
|
2198
2125
|
}
|
|
2199
2126
|
}
|
|
2200
|
-
const pgInt8Parsed = Symbol('pgInt8Parsed');
|
|
2201
|
-
/**
|
|
2202
|
-
* node-postgres returns int8 values (bigint columns such as satoshis, `count(*)`)
|
|
2203
|
-
* as strings. Set a parser on each connection this knex acquires so they come
|
|
2204
|
-
* back as numbers, as with mysql2 and better-sqlite3. The process-wide `pg.types`
|
|
2205
|
-
* defaults are left unchanged.
|
|
2206
|
-
*/
|
|
2207
|
-
function parsePostgresInt8AsNumber(knex) {
|
|
2208
|
-
const client = knex.client;
|
|
2209
|
-
if (client?.dialect !== 'postgresql' || client[pgInt8Parsed] === true)
|
|
2210
|
-
return;
|
|
2211
|
-
client[pgInt8Parsed] = true;
|
|
2212
|
-
const acquireConnection = client.acquireConnection.bind(client);
|
|
2213
|
-
client.acquireConnection = async () => {
|
|
2214
|
-
const connection = await acquireConnection();
|
|
2215
|
-
connection.setTypeParser(20, (value) => Number.parseInt(value, 10));
|
|
2216
|
-
return connection;
|
|
2217
|
-
};
|
|
2218
|
-
}
|
|
2219
2127
|
//# sourceMappingURL=StorageKnex.js.map
|