@bsv/wallet-toolbox 1.3.5 → 1.3.8

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.
Files changed (40) hide show
  1. package/docs/client.md +137 -60
  2. package/docs/storage.md +243 -20
  3. package/docs/wallet.md +137 -60
  4. package/out/src/Wallet.d.ts +2 -2
  5. package/out/src/Wallet.d.ts.map +1 -1
  6. package/out/src/Wallet.js +15 -8
  7. package/out/src/Wallet.js.map +1 -1
  8. package/out/src/storage/StorageIdb.d.ts +2 -1
  9. package/out/src/storage/StorageIdb.d.ts.map +1 -1
  10. package/out/src/storage/StorageIdb.js +3 -0
  11. package/out/src/storage/StorageIdb.js.map +1 -1
  12. package/out/src/storage/StorageKnex.d.ts +2 -1
  13. package/out/src/storage/StorageKnex.d.ts.map +1 -1
  14. package/out/src/storage/StorageKnex.js +143 -0
  15. package/out/src/storage/StorageKnex.js.map +1 -1
  16. package/out/src/storage/StorageProvider.d.ts +69 -0
  17. package/out/src/storage/StorageProvider.d.ts.map +1 -1
  18. package/out/src/storage/StorageProvider.js.map +1 -1
  19. package/out/src/storage/__test/adminStats.man.test.d.ts +2 -0
  20. package/out/src/storage/__test/adminStats.man.test.d.ts.map +1 -0
  21. package/out/src/storage/__test/adminStats.man.test.js +73 -0
  22. package/out/src/storage/__test/adminStats.man.test.js.map +1 -0
  23. package/out/src/storage/__test/getBeefForTransaction.test.js +3 -0
  24. package/out/src/storage/__test/getBeefForTransaction.test.js.map +1 -1
  25. package/out/src/storage/remoting/StorageServer.d.ts +2 -0
  26. package/out/src/storage/remoting/StorageServer.d.ts.map +1 -1
  27. package/out/src/storage/remoting/StorageServer.js +10 -0
  28. package/out/src/storage/remoting/StorageServer.js.map +1 -1
  29. package/out/test/Wallet/local/localWallet2.man.test.js +3 -2
  30. package/out/test/Wallet/local/localWallet2.man.test.js.map +1 -1
  31. package/out/tsconfig.all.tsbuildinfo +1 -1
  32. package/package.json +1 -1
  33. package/src/Wallet.ts +10 -7
  34. package/src/storage/StorageIdb.ts +5 -1
  35. package/src/storage/StorageKnex.ts +216 -2
  36. package/src/storage/StorageProvider.ts +71 -0
  37. package/src/storage/__test/adminStats.man.test.ts +85 -0
  38. package/src/storage/__test/getBeefForTransaction.test.ts +5 -1
  39. package/src/storage/remoting/StorageServer.ts +12 -2
  40. package/test/Wallet/local/localWallet2.man.test.ts +4 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsv/wallet-toolbox",
3
- "version": "1.3.5",
3
+ "version": "1.3.8",
4
4
  "description": "BRC100 conforming wallet, wallet storage and wallet signer components",
5
5
  "main": "./out/src/index.js",
6
6
  "types": "./out/src/index.d.ts",
package/src/Wallet.ts CHANGED
@@ -635,26 +635,28 @@ export class Wallet implements WalletInterface, ProtoWallet {
635
635
  return transformVerifiableCertificatesWithTrust(trustSettings, results)
636
636
  }
637
637
 
638
- verifyReturnedTxidOnly(beef: Beef): Beef {
638
+ verifyReturnedTxidOnly(beef: Beef, knownTxids?: string[]): Beef {
639
639
  if (this.returnTxidOnly) return beef
640
640
  const onlyTxids = beef.txs.filter(btx => btx.isTxidOnly).map(btx => btx.txid)
641
641
  for (const txid of onlyTxids) {
642
+ if (knownTxids && knownTxids.indexOf(txid) >= 0) continue;
642
643
  const btx = beef.findTxid(txid)
643
644
  const tx = this.beef.findAtomicTransaction(txid)
644
- if (!tx) throw new sdk.WERR_INTERNAL()
645
+ if (!tx) throw new sdk.WERR_INTERNAL(`unable to merge txid ${txid} into beef`)
645
646
  beef.mergeTransaction(tx)
646
647
  }
647
648
  for (const btx of beef.txs) {
648
- if (btx.isTxidOnly) throw new sdk.WERR_INTERNAL()
649
+ if (knownTxids && knownTxids.indexOf(btx.txid) >= 0) continue;
650
+ if (btx.isTxidOnly) throw new sdk.WERR_INTERNAL(`remaining txidOnly ${btx.txid} is not known`)
649
651
  }
650
652
  return beef
651
653
  }
652
654
 
653
- verifyReturnedTxidOnlyAtomicBEEF(beef: AtomicBEEF): AtomicBEEF {
655
+ verifyReturnedTxidOnlyAtomicBEEF(beef: AtomicBEEF, knownTxids?: string[]): AtomicBEEF {
654
656
  if (this.returnTxidOnly) return beef
655
657
  const b = Beef.fromBinary(beef)
656
658
  if (!b.atomicTxid) throw new sdk.WERR_INTERNAL()
657
- return this.verifyReturnedTxidOnly(b).toBinaryAtomic(b.atomicTxid!)
659
+ return this.verifyReturnedTxidOnly(b, knownTxids).toBinaryAtomic(b.atomicTxid!)
658
660
  }
659
661
 
660
662
  verifyReturnedTxidOnlyBEEF(beef: BEEF): BEEF {
@@ -694,7 +696,7 @@ export class Wallet implements WalletInterface, ProtoWallet {
694
696
  this.beef.mergeBeefFromParty(this.storageParty, r.tx)
695
697
  }
696
698
 
697
- if (r.tx) r.tx = this.verifyReturnedTxidOnlyAtomicBEEF(r.tx)
699
+ if (r.tx) r.tx = this.verifyReturnedTxidOnlyAtomicBEEF(r.tx, args.options?.knownTxids)
698
700
 
699
701
  if (!vargs.isDelayed) throwIfAnyUnsuccessfulCreateActions(r)
700
702
 
@@ -713,7 +715,8 @@ export class Wallet implements WalletInterface, ProtoWallet {
713
715
 
714
716
  if (!vargs.isDelayed) throwIfAnyUnsuccessfulSignActions(r)
715
717
 
716
- if (r.tx) r.tx = this.verifyReturnedTxidOnlyAtomicBEEF(r.tx)
718
+ const prior = this.pendingSignActions[args.reference]
719
+ if (r.tx) r.tx = this.verifyReturnedTxidOnlyAtomicBEEF(r.tx, prior.args.options?.knownTxids)
717
720
 
718
721
  return r
719
722
  }
@@ -22,7 +22,7 @@ import {
22
22
  verifyOne,
23
23
  verifyOneOrNone
24
24
  } from '../index.client'
25
- import { StorageProvider, StorageProviderOptions } from './StorageProvider'
25
+ import { StorageAdminStats, StorageProvider, StorageProviderOptions } from './StorageProvider'
26
26
  import { StorageIdbSchema } from './schema/StorageIdbSchema'
27
27
  import { DBType } from './StorageReader'
28
28
  import { TransactionStatus } from '../sdk'
@@ -42,6 +42,10 @@ export class StorageIdb extends StorageProvider implements sdk.WalletStorageProv
42
42
  this.dbName = `wallet-toolbox-${this.chain}net`
43
43
  }
44
44
 
45
+ override adminStats(adminIdentityKey: string): Promise<StorageAdminStats> {
46
+ throw new Error('Method not implemented.')
47
+ }
48
+
45
49
  /**
46
50
  * This method must be called at least once before any other method accesses the database,
47
51
  * and each time the schema may have updated.
@@ -1,4 +1,4 @@
1
- import { ListActionsResult, ListOutputsResult } from '@bsv/sdk'
1
+ import { ListActionsResult, ListOutputsResult, Transaction } from '@bsv/sdk'
2
2
  import { sdk, verifyOne, verifyOneOrNone, verifyTruthy } from '../index.all'
3
3
  import {
4
4
  KnexMigrations,
@@ -25,7 +25,7 @@ import {
25
25
  } from './index.all'
26
26
 
27
27
  import { Knex } from 'knex'
28
- import { StorageProvider, StorageProviderOptions } from './StorageProvider'
28
+ import { StorageAdminStats, StorageProvider, StorageProviderOptions } from './StorageProvider'
29
29
  import { purgeData } from './methods/purgeData'
30
30
  import { listActions } from './methods/listActionsKnex'
31
31
  import { listOutputs } from './methods/listOutputsKnex'
@@ -1126,4 +1126,218 @@ export class StorageKnex extends StorageProvider implements sdk.WalletStoragePro
1126
1126
  }
1127
1127
  return entities
1128
1128
  }
1129
+
1130
+ async adminStats(adminIdentityKey: string): Promise<StorageAdminStats> {
1131
+ if (this.dbtype !== 'MySQL') throw new sdk.WERR_NOT_IMPLEMENTED('adminStats, only MySQL is supported')
1132
+
1133
+ const one_day_ago = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString()
1134
+ const one_week_ago = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString()
1135
+ const one_month_ago = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString()
1136
+
1137
+ const [
1138
+ [
1139
+ {
1140
+ usersDay,
1141
+ usersMonth,
1142
+ usersWeek,
1143
+ usersTotal,
1144
+ transactionsDay,
1145
+ transactionsMonth,
1146
+ transactionsWeek,
1147
+ transactionsTotal,
1148
+ txCompletedDay,
1149
+ txCompletedMonth,
1150
+ txCompletedWeek,
1151
+ txCompletedTotal,
1152
+ txFailedDay,
1153
+ txFailedMonth,
1154
+ txFailedWeek,
1155
+ txFailedTotal,
1156
+ txUnprocessedDay,
1157
+ txUnprocessedMonth,
1158
+ txUnprocessedWeek,
1159
+ txUnprocessedTotal,
1160
+ txSendingDay,
1161
+ txSendingMonth,
1162
+ txSendingWeek,
1163
+ txSendingTotal,
1164
+ txUnprovenDay,
1165
+ txUnprovenMonth,
1166
+ txUnprovenWeek,
1167
+ txUnprovenTotal,
1168
+ txUnsignedDay,
1169
+ txUnsignedMonth,
1170
+ txUnsignedWeek,
1171
+ txUnsignedTotal,
1172
+ txNosendDay,
1173
+ txNosendMonth,
1174
+ txNosendWeek,
1175
+ txNosendTotal,
1176
+ txNonfinalDay,
1177
+ txNonfinalMonth,
1178
+ txNonfinalWeek,
1179
+ txNonfinalTotal,
1180
+ txUnfailDay,
1181
+ txUnfailMonth,
1182
+ txUnfailWeek,
1183
+ txUnfailTotal,
1184
+ satoshisDefaultDay,
1185
+ satoshisDefaultMonth,
1186
+ satoshisDefaultWeek,
1187
+ satoshisDefaultTotal,
1188
+ satoshisOtherDay,
1189
+ satoshisOtherMonth,
1190
+ satoshisOtherWeek,
1191
+ satoshisOtherTotal,
1192
+ basketsDay,
1193
+ basketsMonth,
1194
+ basketsWeek,
1195
+ basketsTotal,
1196
+ labelsDay,
1197
+ labelsMonth,
1198
+ labelsWeek,
1199
+ labelsTotal,
1200
+ tagsDay,
1201
+ tagsMonth,
1202
+ tagsWeek,
1203
+ tagsTotal
1204
+ }
1205
+ ]
1206
+ ] = await this.knex.raw(`
1207
+ select
1208
+ (select count(*) from users where created_at > '${one_day_ago}') as usersDay,
1209
+ (select count(*) from users where created_at > '${one_week_ago}') as usersWeek,
1210
+ (select count(*) from users where created_at > '${one_month_ago}') as usersMonth,
1211
+ (select count(*) from users) as usersTotal,
1212
+ (select count(*) from transactions where created_at > '${one_day_ago}') as transactionsDay,
1213
+ (select count(*) from transactions where created_at > '${one_week_ago}') as transactionsWeek,
1214
+ (select count(*) from transactions where created_at > '${one_month_ago}') as transactionsMonth,
1215
+ (select count(*) from transactions) as transactionsTotal,
1216
+ (select count(*) from transactions where status = 'completed' and created_at > '${one_day_ago}') as txCompletedDay,
1217
+ (select count(*) from transactions where status = 'completed' and created_at > '${one_week_ago}') as txCompletedWeek,
1218
+ (select count(*) from transactions where status = 'completed' and created_at > '${one_month_ago}') as txCompletedMonth,
1219
+ (select count(*) from transactions where status = 'completed') as txCompletedTotal,
1220
+ (select count(*) from transactions where status = 'failed' and created_at > '${one_day_ago}') as txFailedDay,
1221
+ (select count(*) from transactions where status = 'failed' and created_at > '${one_week_ago}') as txFailedWeek,
1222
+ (select count(*) from transactions where status = 'failed' and created_at > '${one_month_ago}') as txFailedMonth,
1223
+ (select count(*) from transactions where status = 'failed') as txFailedTotal,
1224
+ (select count(*) from transactions where status = 'unprocessed' and created_at > '${one_day_ago}') as txUnprocessedDay,
1225
+ (select count(*) from transactions where status = 'unprocessed' and created_at > '${one_week_ago}') as txUnprocessedWeek,
1226
+ (select count(*) from transactions where status = 'unprocessed' and created_at > '${one_month_ago}') as txUnprocessedMonth,
1227
+ (select count(*) from transactions where status = 'unprocessed') as txUnprocessedTotal,
1228
+ (select count(*) from transactions where status = 'sending' and created_at > '${one_day_ago}') as txSendingDay,
1229
+ (select count(*) from transactions where status = 'sending' and created_at > '${one_week_ago}') as txSendingWeek,
1230
+ (select count(*) from transactions where status = 'sending' and created_at > '${one_month_ago}') as txSendingMonth,
1231
+ (select count(*) from transactions where status = 'sending') as txSendingTotal,
1232
+ (select count(*) from transactions where status = 'unproven' and created_at > '${one_day_ago}') as txUnprovenDay,
1233
+ (select count(*) from transactions where status = 'unproven' and created_at > '${one_week_ago}') as txUnprovenWeek,
1234
+ (select count(*) from transactions where status = 'unproven' and created_at > '${one_month_ago}') as txUnprovenMonth,
1235
+ (select count(*) from transactions where status = 'unproven') as txUnprovenTotal,
1236
+ (select count(*) from transactions where status = 'unsigned' and created_at > '${one_day_ago}') as txUnsignedDay,
1237
+ (select count(*) from transactions where status = 'unsigned' and created_at > '${one_week_ago}') as txUnsignedWeek,
1238
+ (select count(*) from transactions where status = 'unsigned' and created_at > '${one_month_ago}') as txUnsignedMonth,
1239
+ (select count(*) from transactions where status = 'unsigned') as txUnsignedTotal,
1240
+ (select count(*) from transactions where status = 'nosend' and created_at > '${one_day_ago}') as txNosendDay,
1241
+ (select count(*) from transactions where status = 'nosend' and created_at > '${one_week_ago}') as txNosendWeek,
1242
+ (select count(*) from transactions where status = 'nosend' and created_at > '${one_month_ago}') as txNosendMonth,
1243
+ (select count(*) from transactions where status = 'nosend') as txNosendTotal,
1244
+ (select count(*) from transactions where status = 'nonfinal' and created_at > '${one_day_ago}') as txNonfinalDay,
1245
+ (select count(*) from transactions where status = 'nonfinal' and created_at > '${one_week_ago}') as txNonfinalWeek,
1246
+ (select count(*) from transactions where status = 'nonfinal' and created_at > '${one_month_ago}') as txNonfinalMonth,
1247
+ (select count(*) from transactions where status = 'nonfinal') as txNonfinalTotal,
1248
+ (select count(*) from transactions where status = 'unfail' and created_at > '${one_day_ago}') as txUnfailDay,
1249
+ (select count(*) from transactions where status = 'unfail' and created_at > '${one_week_ago}') as txUnfailWeek,
1250
+ (select count(*) from transactions where status = 'unfail' and created_at > '${one_month_ago}') as txUnfailMonth,
1251
+ (select count(*) from transactions where status = 'unfail') as txUnfailTotal,
1252
+ (select sum(satoshis) from outputs where spendable = 1 and \`change\` = 1 and created_at > '${one_day_ago}') as satoshisDefaultDay,
1253
+ (select sum(satoshis) from outputs where spendable = 1 and \`change\` = 1 and created_at > '${one_week_ago}') as satoshisDefaultWeek,
1254
+ (select sum(satoshis) from outputs where spendable = 1 and \`change\` = 1 and created_at > '${one_month_ago}') as satoshisDefaultMonth,
1255
+ (select sum(satoshis) from outputs where spendable = 1 and \`change\` = 1) as satoshisDefaultTotal,
1256
+ (select sum(satoshis) from outputs where spendable = 1 and \`change\` = 0 and not basketId is null and created_at > '${one_day_ago}') as satoshisOtherDay,
1257
+ (select sum(satoshis) from outputs where spendable = 1 and \`change\` = 0 and not basketId is null and created_at > '${one_week_ago}') as satoshisOtherWeek,
1258
+ (select sum(satoshis) from outputs where spendable = 1 and \`change\` = 0 and not basketId is null and created_at > '${one_month_ago}') as satoshisOtherMonth,
1259
+ (select sum(satoshis) from outputs where spendable = 1 and \`change\` = 0 and not basketId is null) as satoshisOtherTotal,
1260
+ (select count(*) from output_baskets where created_at > '${one_day_ago}') as basketsDay,
1261
+ (select count(*) from output_baskets where created_at > '${one_week_ago}') as basketsWeek,
1262
+ (select count(*) from output_baskets where created_at > '${one_month_ago}') as basketsMonth,
1263
+ (select count(*) from output_baskets) as basketsTotal,
1264
+ (select count(*) from tx_labels where created_at > '${one_day_ago}') as labelsDay,
1265
+ (select count(*) from tx_labels where created_at > '${one_week_ago}') as labelsWeek,
1266
+ (select count(*) from tx_labels where created_at > '${one_month_ago}') as labelsMonth,
1267
+ (select count(*) from tx_labels) as labelsTotal,
1268
+ (select count(*) from output_tags where created_at > '${one_day_ago}') as tagsDay,
1269
+ (select count(*) from output_tags where created_at > '${one_week_ago}') as tagsWeek,
1270
+ (select count(*) from output_tags where created_at > '${one_month_ago}') as tagsMonth,
1271
+ (select count(*) from output_tags) as tagsTotal
1272
+ `)
1273
+ const r: StorageAdminStats = {
1274
+ requestedBy: adminIdentityKey,
1275
+ when: new Date().toISOString(),
1276
+ usersDay,
1277
+ usersWeek,
1278
+ usersMonth,
1279
+ usersTotal,
1280
+ transactionsDay,
1281
+ transactionsWeek,
1282
+ transactionsMonth,
1283
+ transactionsTotal,
1284
+ txCompletedDay,
1285
+ txCompletedWeek,
1286
+ txCompletedMonth,
1287
+ txCompletedTotal,
1288
+ txFailedDay,
1289
+ txFailedWeek,
1290
+ txFailedMonth,
1291
+ txFailedTotal,
1292
+ txUnprocessedDay,
1293
+ txUnprocessedWeek,
1294
+ txUnprocessedMonth,
1295
+ txUnprocessedTotal,
1296
+ txSendingDay,
1297
+ txSendingWeek,
1298
+ txSendingMonth,
1299
+ txSendingTotal,
1300
+ txUnprovenDay,
1301
+ txUnprovenWeek,
1302
+ txUnprovenMonth,
1303
+ txUnprovenTotal,
1304
+ txUnsignedDay,
1305
+ txUnsignedWeek,
1306
+ txUnsignedMonth,
1307
+ txUnsignedTotal,
1308
+ txNosendDay,
1309
+ txNosendWeek,
1310
+ txNosendMonth,
1311
+ txNosendTotal,
1312
+ txNonfinalDay,
1313
+ txNonfinalWeek,
1314
+ txNonfinalMonth,
1315
+ txNonfinalTotal,
1316
+ txUnfailDay,
1317
+ txUnfailWeek,
1318
+ txUnfailMonth,
1319
+ txUnfailTotal,
1320
+ satoshisDefaultDay: Number(satoshisDefaultDay),
1321
+ satoshisDefaultWeek: Number(satoshisDefaultWeek),
1322
+ satoshisDefaultMonth: Number(satoshisDefaultMonth),
1323
+ satoshisDefaultTotal: Number(satoshisDefaultTotal),
1324
+ satoshisOtherDay: Number(satoshisOtherDay),
1325
+ satoshisOtherWeek: Number(satoshisOtherWeek),
1326
+ satoshisOtherMonth: Number(satoshisOtherMonth),
1327
+ satoshisOtherTotal: Number(satoshisOtherTotal),
1328
+ basketsDay,
1329
+ basketsWeek,
1330
+ basketsMonth,
1331
+ basketsTotal,
1332
+ labelsDay,
1333
+ labelsWeek,
1334
+ labelsMonth,
1335
+ labelsTotal,
1336
+ tagsDay,
1337
+ tagsWeek,
1338
+ tagsMonth,
1339
+ tagsTotal
1340
+ }
1341
+ return r
1342
+ }
1129
1343
  }
@@ -106,6 +106,8 @@ export abstract class StorageProvider extends StorageReaderWriter implements sdk
106
106
  abstract findOutputsAuth(auth: sdk.AuthId, args: sdk.FindOutputsArgs): Promise<TableOutput[]>
107
107
  abstract insertCertificateAuth(auth: sdk.AuthId, certificate: TableCertificateX): Promise<number>
108
108
 
109
+ abstract adminStats(adminIdentityKey: string): Promise<StorageAdminStats>
110
+
109
111
  override isStorageProvider(): boolean {
110
112
  return true
111
113
  }
@@ -694,3 +696,72 @@ export function validateStorageFeeModel(v?: sdk.StorageFeeModel): sdk.StorageFee
694
696
  }
695
697
  return r
696
698
  }
699
+
700
+ export interface StorageAdminStats {
701
+ requestedBy: string
702
+ when: string
703
+ usersDay: number
704
+ usersWeek: number
705
+ usersMonth: number
706
+ usersTotal: number
707
+ transactionsDay: number
708
+ transactionsWeek: number
709
+ transactionsMonth: number
710
+ transactionsTotal: number
711
+ txCompletedDay: number
712
+ txCompletedWeek: number
713
+ txCompletedMonth: number
714
+ txCompletedTotal: number
715
+ txFailedDay: number
716
+ txFailedWeek: number
717
+ txFailedMonth: number
718
+ txFailedTotal: number
719
+ txUnprocessedDay: number
720
+ txUnprocessedWeek: number
721
+ txUnprocessedMonth: number
722
+ txUnprocessedTotal: number
723
+ txSendingDay: number
724
+ txSendingWeek: number
725
+ txSendingMonth: number
726
+ txSendingTotal: number
727
+ txUnprovenDay: number
728
+ txUnprovenWeek: number
729
+ txUnprovenMonth: number
730
+ txUnprovenTotal: number
731
+ txUnsignedDay: number
732
+ txUnsignedWeek: number
733
+ txUnsignedMonth: number
734
+ txUnsignedTotal: number
735
+ txNosendDay: number
736
+ txNosendWeek: number
737
+ txNosendMonth: number
738
+ txNosendTotal: number
739
+ txNonfinalDay: number
740
+ txNonfinalWeek: number
741
+ txNonfinalMonth: number
742
+ txNonfinalTotal: number
743
+ txUnfailDay: number
744
+ txUnfailWeek: number
745
+ txUnfailMonth: number
746
+ txUnfailTotal: number
747
+ satoshisDefaultDay: number
748
+ satoshisDefaultWeek: number
749
+ satoshisDefaultMonth: number
750
+ satoshisDefaultTotal: number
751
+ satoshisOtherDay: number
752
+ satoshisOtherWeek: number
753
+ satoshisOtherMonth: number
754
+ satoshisOtherTotal: number
755
+ basketsDay: number
756
+ basketsWeek: number
757
+ basketsMonth: number
758
+ basketsTotal: number
759
+ labelsDay: number
760
+ labelsWeek: number
761
+ labelsMonth: number
762
+ labelsTotal: number
763
+ tagsDay: number
764
+ tagsWeek: number
765
+ tagsMonth: number
766
+ tagsTotal: number
767
+ }
@@ -0,0 +1,85 @@
1
+ import { before } from 'node:test'
2
+ import { _tu, TestWalletOnly } from '../../../test/utils/TestUtilsWalletStorage'
3
+ import { Setup } from '../../Setup'
4
+ import { StorageKnex } from '../StorageKnex'
5
+ import { AuthFetch, WalletInterface } from '@bsv/sdk'
6
+ import { StorageAdminStats, StorageClient } from '../index.client'
7
+
8
+ describe('storage adminStats tests', () => {
9
+ jest.setTimeout(99999999)
10
+
11
+ const env = _tu.getEnv('main')
12
+ const knex = Setup.createMySQLKnex(process.env.MAIN_CLOUD_MYSQL_CONNECTION!)
13
+ const storage = new StorageKnex({
14
+ chain: env.chain,
15
+ knex: knex,
16
+ commissionSatoshis: 0,
17
+ commissionPubKeyHex: undefined,
18
+ feeModel: { model: 'sat/kb', value: 1 }
19
+ })
20
+
21
+ let setup: TestWalletOnly
22
+ let nextId = 0
23
+
24
+ beforeAll(async () => {
25
+ await storage.makeAvailable()
26
+
27
+ setup = await _tu.createTestWalletWithStorageClient({
28
+ chain: 'main',
29
+ rootKeyHex: env.devKeys[env.identityKey]
30
+ })
31
+ })
32
+ afterAll(async () => {
33
+ await storage.destroy()
34
+ await setup.wallet.destroy()
35
+ })
36
+
37
+ test('0 adminStats StorageKnex', async () => {
38
+ const r = await storage.adminStats(env.identityKey)
39
+ expect(r.requestedBy).toBe(env.identityKey)
40
+ expect(r.usersTotal).toBeGreaterThan(0)
41
+ })
42
+
43
+ test('1 adminStats StorageServer via RPC', async () => {
44
+
45
+ const authFetch = new AuthFetch(setup.wallet)
46
+ const endpointUrl = setup.chain === 'main' ? 'https://storage.babbage.systems' : 'https://staging-storage.babbage.systems'
47
+
48
+ const id = nextId++
49
+ const body = {
50
+ jsonrpc: '2.0',
51
+ method: 'adminStats',
52
+ params: [env.identityKey],
53
+ id
54
+ }
55
+
56
+ let response: Response
57
+ try {
58
+ response = await authFetch.fetch(endpointUrl, {
59
+ method: 'POST',
60
+ headers: { 'Content-Type': 'application/json' },
61
+ body: JSON.stringify(body)
62
+ })
63
+ } catch (eu: unknown) {
64
+ throw eu
65
+ }
66
+
67
+ if (!response.ok) {
68
+ throw new Error(`WalletStorageClient rpcCall: network error ${response.status} ${response.statusText}`)
69
+ }
70
+
71
+ const json = await response.json()
72
+ if (json.error) {
73
+ const { code, message, data } = json.error
74
+ const err = new Error(`RPC Error: ${message}`)
75
+ // You could attach more info here if you like:
76
+ ; (err as any).code = code
77
+ ; (err as any).data = data
78
+ throw err
79
+ }
80
+
81
+ const r = json.result as StorageAdminStats
82
+ expect(r.requestedBy).toBe(env.identityKey)
83
+ expect(r.usersTotal).toBeGreaterThan(0)
84
+ })
85
+ })
@@ -46,7 +46,8 @@ import {
46
46
  TableUser,
47
47
  sdk,
48
48
  Services,
49
- EntityProvenTx
49
+ EntityProvenTx,
50
+ StorageAdminStats
50
51
  } from '../../index.client'
51
52
 
52
53
  describe('getBeefForTransaction tests', () => {
@@ -371,4 +372,7 @@ class ProtoStorage extends StorageProvider {
371
372
  override getOutputTagMapsForUser(args: FindForUserSincePagedArgs): Promise<TableOutputTagMap[]> {
372
373
  throw this.nip
373
374
  }
375
+ override adminStats(adminIdentityKey: string): Promise<StorageAdminStats> {
376
+ throw this.nip
377
+ }
374
378
  }
@@ -11,13 +11,12 @@ import { AuthMiddlewareOptions, createAuthMiddleware } from '@bsv/auth-express-m
11
11
  import { createPaymentMiddleware } from '@bsv/payment-express-middleware'
12
12
  import { sdk, Wallet, StorageProvider } from '../../index.all'
13
13
 
14
- import { StorageKnex } from '../StorageKnex'
15
-
16
14
  export interface WalletStorageServerOptions {
17
15
  port: number
18
16
  wallet: Wallet
19
17
  monetize: boolean
20
18
  calculateRequestPrice?: (req: Request) => number | Promise<number>
19
+ adminIdentityKeys?: string[]
21
20
  }
22
21
 
23
22
  export class StorageServer {
@@ -27,6 +26,7 @@ export class StorageServer {
27
26
  private wallet: Wallet
28
27
  private monetize: boolean
29
28
  private calculateRequestPrice?: (req: Request) => number | Promise<number>
29
+ private adminIdentityKeys?: string[]
30
30
 
31
31
  constructor(storage: StorageProvider, options: WalletStorageServerOptions) {
32
32
  this.storage = storage
@@ -34,6 +34,7 @@ export class StorageServer {
34
34
  this.wallet = options.wallet
35
35
  this.monetize = options.monetize
36
36
  this.calculateRequestPrice = options.calculateRequestPrice
37
+ this.adminIdentityKeys = options.adminIdentityKeys
37
38
 
38
39
  this.setupRoutes()
39
40
  }
@@ -103,6 +104,15 @@ export class StorageServer {
103
104
  throw new sdk.WERR_UNAUTHORIZED('function may only access authenticated user.')
104
105
  }
105
106
  break
107
+ case 'adminStats':
108
+ {
109
+ // TODO: add check for admin user
110
+ if (params[0] !== req.auth.identityKey)
111
+ throw new sdk.WERR_UNAUTHORIZED('function may only access authenticated admin user.')
112
+ if (!this.adminIdentityKeys || !this.adminIdentityKeys.includes(req.auth.identityKey))
113
+ throw new sdk.WERR_UNAUTHORIZED('function may only be accessed by admin user.')
114
+ }
115
+ break
106
116
  case 'processSyncChunk':
107
117
  {
108
118
  await this.validateParam0(params, req)
@@ -203,9 +203,9 @@ describe('localWallet2 tests', () => {
203
203
  await storage.destroy()
204
204
  })
205
205
 
206
- test('8 jackie Beef', async () => {
206
+ test('8 Beef', async () => {
207
207
  const setup = await createSetup(chain, options)
208
- const beef = Beef.fromBinary(beefJackie)
208
+ const beef = Beef.fromBinary(deggen1)
209
209
  console.log(beef.toLogString())
210
210
  const ok = await beef.verify(await setup.services.getChainTracker())
211
211
  await setup.wallet.destroy()
@@ -249,3 +249,5 @@ const beefJackie = [
249
249
 
250
250
  const brayden1 =
251
251
  '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'
252
+
253
+ const deggen1 = [2,0,190,239,1,254,61,164,13,0,9,4,18,0,25,233,123,59,2,93,207,54,99,91,69,64,83,210,227,229,130,77,217,18,14,236,143,122,254,10,250,126,58,110,238,144,19,2,77,46,100,182,154,241,164,76,47,98,205,29,160,92,177,126,92,14,145,35,91,165,0,42,124,199,62,233,121,154,7,24,50,2,33,171,215,183,158,221,38,243,70,211,131,58,170,44,3,231,237,152,13,155,150,30,199,126,229,176,189,102,63,10,111,24,51,0,96,207,90,32,230,218,160,51,4,135,244,191,12,159,31,19,41,249,30,104,83,57,244,44,9,107,188,7,248,1,196,223,2,8,0,12,7,218,35,194,27,189,224,87,232,37,94,137,221,163,111,238,214,218,189,232,184,7,64,230,178,185,212,26,9,164,150,24,0,147,228,31,186,126,234,39,157,58,249,202,105,111,115,131,48,88,19,18,87,119,96,75,56,114,143,230,59,152,76,202,170,2,5,0,202,155,78,246,86,202,171,73,239,29,13,204,22,12,123,194,142,166,210,45,130,52,74,184,201,152,12,255,181,67,215,168,13,0,115,189,166,59,177,77,241,159,94,189,50,110,17,154,28,185,202,97,234,155,78,115,189,69,79,71,188,113,122,109,16,181,2,3,0,194,45,225,166,175,104,161,184,190,254,229,125,27,127,137,161,232,68,193,96,253,87,62,253,3,223,229,24,94,129,214,116,7,0,181,135,252,247,5,39,49,52,191,156,240,250,221,25,129,225,237,152,31,145,108,42,93,6,103,69,76,160,165,23,70,83,2,0,0,209,193,9,113,33,134,63,48,32,212,87,35,34,83,161,246,84,183,72,110,90,1,64,118,21,131,61,196,29,111,119,191,2,0,123,95,31,84,230,82,82,24,53,216,218,40,117,2,63,219,61,238,105,161,46,181,243,167,97,64,228,114,220,71,73,140,0,1,1,0,196,184,184,52,65,192,137,200,207,243,174,77,2,118,237,202,138,161,243,35,91,198,65,64,163,246,38,208,76,180,145,244,1,1,0,18,245,205,109,82,79,229,218,95,101,184,218,130,175,143,244,107,175,141,157,173,206,99,11,227,77,101,98,6,48,136,67,1,1,0,91,191,231,205,39,100,227,219,93,238,93,109,242,165,180,233,198,96,25,89,170,169,130,162,244,10,239,202,107,0,21,60,8,1,0,1,0,0,0,2,174,169,208,19,216,192,152,143,113,93,23,171,156,150,82,10,181,224,21,166,66,108,87,213,157,118,189,68,8,80,85,181,0,0,0,0,73,72,48,69,2,33,0,141,211,248,207,116,130,57,74,242,121,113,29,181,208,168,177,105,53,97,162,117,208,25,83,188,63,236,184,116,248,239,158,2,32,78,38,144,22,189,219,72,21,96,81,150,158,150,251,191,28,30,97,50,202,194,130,117,219,33,208,178,157,115,173,81,199,195,255,255,255,255,174,169,208,19,216,192,152,143,113,93,23,171,156,150,82,10,181,224,21,166,66,108,87,213,157,118,189,68,8,80,85,181,1,0,0,0,107,72,48,69,2,33,0,129,237,226,184,32,219,112,119,111,96,139,17,128,214,18,246,118,49,234,205,130,80,251,91,215,226,131,197,172,55,85,67,2,32,1,147,136,115,154,3,77,227,64,103,97,69,96,201,156,173,48,205,4,12,85,29,64,218,43,8,158,132,54,195,1,28,65,33,3,9,132,53,213,219,57,216,49,210,201,1,84,163,19,164,71,155,46,81,50,172,220,0,61,108,92,81,215,134,128,78,177,255,255,255,255,2,1,0,0,0,0,0,0,0,151,9,71,97,116,104,101,114,105,110,103,32,39,173,197,23,67,75,226,149,105,196,79,37,95,8,142,152,104,192,124,172,221,185,144,166,21,52,13,153,161,102,134,101,70,48,68,2,32,85,71,178,181,183,117,60,128,153,106,239,213,51,77,151,16,94,206,255,149,172,153,197,32,48,100,101,135,10,217,12,43,2,32,47,244,154,103,103,183,30,101,56,44,244,186,9,179,63,97,56,106,79,222,90,197,215,223,107,150,192,240,30,72,246,230,109,117,33,3,128,163,55,102,184,146,119,46,138,201,110,52,156,243,238,211,69,98,164,7,119,132,125,162,236,116,84,135,113,55,14,230,172,16,9,0,0,0,0,0,0,25,118,169,20,42,8,235,102,174,91,200,11,11,113,207,48,100,10,23,125,207,144,193,238,136,172,0,0,0,0,0,1,0,0,0,1,33,171,215,183,158,221,38,243,70,211,131,58,170,44,3,231,237,152,13,155,150,30,199,126,229,176,189,102,63,10,111,24,1,0,0,0,106,71,48,68,2,32,73,193,5,83,88,47,239,215,186,74,226,105,255,62,248,158,77,178,73,5,170,203,54,97,56,76,27,110,211,57,78,123,2,32,61,50,245,195,142,41,61,240,217,58,153,24,18,52,254,128,88,171,135,166,171,126,1,16,251,75,67,79,119,2,105,19,65,33,2,186,190,181,128,51,144,194,251,161,195,193,141,70,118,26,195,177,196,167,18,192,55,102,128,84,171,136,76,70,204,201,94,255,255,255,255,2,1,0,0,0,0,0,0,0,152,9,71,97,116,104,101,114,105,110,103,32,187,218,84,226,225,67,59,112,246,171,151,41,205,105,181,25,239,195,238,172,15,195,250,246,135,86,35,246,162,69,159,208,71,48,69,2,33,0,222,18,181,125,90,189,225,109,77,5,227,48,138,33,126,69,254,55,95,116,184,5,253,91,123,205,9,46,31,130,179,56,2,32,3,44,204,163,37,130,249,162,96,173,145,154,156,205,105,47,222,169,196,224,216,145,151,105,220,42,111,161,92,207,181,86,109,117,33,2,95,158,31,234,171,32,250,80,221,138,66,17,81,194,87,121,50,192,118,86,184,242,176,140,53,225,199,18,152,28,249,21,172,14,9,0,0,0,0,0,0,25,118,169,20,242,208,125,210,1,114,233,153,238,255,190,23,23,171,83,92,138,54,246,76,136,172,0,0,0,0,0,1,0,0,0,2,110,221,142,211,113,133,121,175,242,206,24,221,74,47,164,184,213,46,158,130,180,114,118,22,60,20,19,146,84,64,185,171,0,0,0,0,73,72,48,69,2,33,0,170,226,110,180,64,146,173,123,109,61,183,222,149,151,204,229,156,126,120,0,112,234,21,99,19,239,80,31,122,84,94,243,2,32,100,237,67,136,3,67,211,181,63,84,241,71,44,204,218,51,154,177,145,208,141,114,146,47,174,255,107,103,113,245,105,229,195,255,255,255,255,110,221,142,211,113,133,121,175,242,206,24,221,74,47,164,184,213,46,158,130,180,114,118,22,60,20,19,146,84,64,185,171,1,0,0,0,107,72,48,69,2,33,0,187,50,145,237,80,150,54,194,24,101,222,3,215,233,161,237,163,4,253,195,212,30,10,220,90,139,149,100,214,114,39,56,2,32,108,100,194,156,35,15,198,235,158,234,9,149,98,171,79,121,26,230,231,70,59,78,243,196,233,12,54,191,186,145,121,99,65,33,2,244,42,37,120,18,252,23,187,89,35,215,21,142,253,172,34,235,125,161,252,221,210,135,224,13,178,143,158,187,193,116,46,255,255,255,255,2,1,0,0,0,0,0,0,0,153,10,80,114,111,99,101,115,115,105,110,103,32,122,208,37,32,117,3,217,179,192,88,140,57,68,225,40,140,220,51,201,253,245,146,134,84,135,177,143,9,179,71,102,105,71,48,69,2,33,0,166,15,9,148,241,149,73,41,191,253,141,170,9,55,50,39,154,99,7,156,86,227,152,189,3,229,1,218,58,242,141,204,2,32,34,153,168,7,25,174,186,179,90,139,251,248,92,214,88,21,41,21,94,220,236,206,132,36,189,232,167,223,229,93,210,26,109,117,33,3,122,224,16,236,137,42,108,33,209,28,5,129,16,135,219,155,74,99,240,128,136,27,75,167,114,39,46,154,233,163,53,37,172,13,9,0,0,0,0,0,0,25,118,169,20,117,49,253,23,174,17,143,55,238,78,231,124,223,17,109,176,169,203,63,163,136,172,0,0,0,0,0,1,0,0,0,1,164,69,84,219,142,94,189,212,94,127,216,186,235,68,218,200,29,93,85,143,239,7,20,19,227,37,123,39,34,157,158,121,1,0,0,0,107,72,48,69,2,33,0,207,62,176,67,139,118,234,171,8,5,55,65,133,234,17,106,163,58,185,224,131,57,35,30,212,132,105,57,155,128,139,70,2,32,84,0,232,58,122,76,172,2,74,1,203,100,28,236,116,48,145,193,70,159,19,174,218,108,221,71,5,175,198,152,2,9,65,33,2,212,74,192,116,103,39,76,243,222,56,153,200,126,45,244,247,80,93,151,151,121,45,145,36,239,155,173,73,209,130,130,100,255,255,255,255,2,1,0,0,0,0,0,0,0,153,10,80,114,111,99,101,115,115,105,110,103,32,71,113,135,159,61,244,11,78,150,218,207,111,183,0,67,22,36,225,216,12,209,241,216,121,172,21,190,117,163,112,13,218,71,48,69,2,33,0,239,232,140,151,234,155,250,70,85,231,13,90,208,11,194,157,53,131,225,252,212,116,242,83,210,183,255,140,105,202,61,1,2,32,89,170,64,202,194,152,39,106,12,161,59,142,71,79,223,215,253,63,23,58,252,192,33,226,179,21,80,172,163,220,250,229,109,117,33,3,11,121,157,203,68,133,28,66,255,168,249,201,212,169,247,204,192,94,35,48,82,33,229,207,134,121,152,190,147,140,36,241,172,11,9,0,0,0,0,0,0,25,118,169,20,170,167,118,0,236,22,117,40,249,214,221,71,235,187,141,164,126,11,236,42,136,172,0,0,0,0,0,1,0,0,0,2,108,189,191,127,12,61,166,205,45,250,97,24,178,238,29,79,98,81,145,56,44,21,66,220,187,29,150,82,234,98,115,168,0,0,0,0,73,72,48,69,2,33,0,156,8,26,112,125,153,201,216,48,164,148,57,186,40,133,134,235,176,2,70,0,1,248,133,148,137,224,247,207,72,74,101,2,32,72,56,194,80,82,141,214,12,97,91,118,204,113,146,4,25,127,113,219,143,141,173,135,238,110,155,231,138,136,172,20,10,195,255,255,255,255,108,189,191,127,12,61,166,205,45,250,97,24,178,238,29,79,98,81,145,56,44,21,66,220,187,29,150,82,234,98,115,168,1,0,0,0,106,71,48,68,2,32,82,74,50,73,133,215,96,65,54,65,198,22,113,22,150,24,138,220,20,185,130,18,96,45,90,100,73,6,142,197,134,181,2,32,16,205,220,155,21,253,187,165,51,115,200,57,154,202,11,135,23,31,207,231,78,210,179,254,89,183,213,255,67,29,154,162,65,33,2,36,240,26,152,94,79,15,11,159,5,120,200,241,133,239,215,128,181,119,147,107,235,82,231,104,199,26,63,199,211,44,143,255,255,255,255,2,1,0,0,0,0,0,0,0,155,12,84,114,97,110,115,109,105,115,115,105,111,110,32,39,72,24,103,122,170,132,55,133,166,8,111,191,18,136,196,75,222,47,170,73,187,47,161,88,186,246,16,180,12,18,137,71,48,69,2,33,0,151,119,215,109,118,191,61,231,180,56,47,108,121,81,118,179,31,64,159,167,252,142,14,21,202,89,163,69,143,155,6,63,2,32,27,97,101,78,222,56,60,219,37,85,231,130,241,11,150,35,191,192,105,133,116,153,140,12,73,226,201,208,180,251,157,225,109,117,33,2,202,118,230,138,174,249,137,179,223,58,35,149,140,234,19,114,121,82,238,10,59,96,155,83,99,226,53,178,86,185,95,118,172,10,9,0,0,0,0,0,0,25,118,169,20,62,12,143,167,140,99,113,76,205,84,139,160,111,210,223,94,191,74,103,122,136,172,0,0,0,0,0,1,0,0,0,1,57,30,128,160,9,188,224,146,5,78,39,211,80,114,215,111,14,206,116,121,108,237,186,76,152,44,108,38,65,112,204,178,1,0,0,0,106,71,48,68,2,32,32,93,60,191,3,190,86,247,230,0,224,55,179,43,180,183,172,159,200,142,52,57,193,75,32,103,63,96,3,139,228,31,2,32,24,2,189,98,36,155,73,67,3,10,117,16,31,175,163,253,104,213,193,179,171,49,162,135,251,19,188,146,39,223,1,53,65,33,2,46,239,224,107,21,193,103,21,189,184,117,78,198,91,234,146,183,187,177,21,27,189,243,156,148,107,7,37,77,234,203,128,255,255,255,255,2,1,0,0,0,0,0,0,0,155,12,84,114,97,110,115,109,105,115,115,105,111,110,32,24,4,9,80,213,90,127,116,1,132,27,48,41,89,243,171,57,154,254,98,68,107,226,53,154,44,60,86,182,46,86,175,71,48,69,2,33,0,206,65,237,205,65,19,117,141,149,21,158,113,83,142,137,171,86,113,221,228,59,159,130,114,123,46,145,37,192,36,106,25,2,32,2,174,184,123,134,25,164,12,2,99,64,195,240,26,134,43,12,174,63,84,252,96,164,0,130,66,190,136,93,173,15,71,109,117,33,3,10,249,217,51,79,225,253,0,56,2,251,155,232,113,124,220,53,107,196,5,27,173,169,183,141,117,92,239,14,136,194,64,172,8,9,0,0,0,0,0,0,25,118,169,20,254,31,126,151,156,186,112,154,50,33,68,190,203,53,55,142,125,170,84,163,136,172,0,0,0,0,0,1,0,0,0,2,157,118,108,211,205,92,183,182,223,31,229,107,18,221,194,143,196,232,66,135,32,241,26,154,254,199,71,186,158,133,144,83,0,0,0,0,73,72,48,69,2,33,0,200,255,217,164,8,172,183,217,32,111,201,37,27,5,121,163,216,49,109,168,114,169,18,78,187,149,218,194,242,72,243,96,2,32,74,15,52,137,45,125,73,246,208,90,75,243,12,234,180,150,65,147,251,249,167,158,56,65,147,128,9,42,27,113,146,248,195,255,255,255,255,157,118,108,211,205,92,183,182,223,31,229,107,18,221,194,143,196,232,66,135,32,241,26,154,254,199,71,186,158,133,144,83,1,0,0,0,107,72,48,69,2,33,0,252,253,4,122,108,219,95,93,196,108,235,116,142,36,129,112,36,138,69,45,142,222,153,187,206,14,181,74,136,50,170,180,2,32,122,44,140,93,178,219,210,150,102,203,23,233,34,144,76,92,30,30,240,46,186,163,172,188,254,242,69,139,196,77,43,133,65,33,2,60,108,207,193,164,205,221,248,63,79,70,56,91,243,218,216,175,248,156,22,49,154,69,48,221,185,47,56,20,167,173,197,255,255,255,255,2,1,0,0,0,0,0,0,0,149,7,83,116,111,114,97,103,101,32,18,34,41,1,160,80,87,153,194,150,74,208,60,95,84,74,93,255,230,127,209,72,99,95,15,235,11,21,21,149,206,5,70,48,68,2,32,83,227,201,244,10,34,207,92,43,72,106,52,113,129,117,23,6,170,138,52,245,14,26,105,139,81,3,245,61,75,190,165,2,32,44,120,95,239,1,49,249,68,232,237,141,154,254,105,121,0,99,229,250,227,62,233,85,78,223,57,218,106,244,22,166,138,109,117,33,2,207,49,92,246,195,118,225,143,14,94,216,160,194,76,31,224,2,16,170,185,153,122,95,158,65,127,11,109,43,196,39,92,172,7,9,0,0,0,0,0,0,25,118,169,20,161,15,57,228,131,131,209,129,72,139,4,22,163,167,179,4,48,206,27,236,136,172,0,0,0,0,0,1,0,0,0,1,104,44,157,24,249,145,17,163,126,215,214,223,177,252,247,241,103,133,197,236,225,236,85,84,116,189,252,116,230,109,117,189,1,0,0,0,106,71,48,68,2,32,5,132,53,232,42,60,87,114,248,1,126,65,34,4,171,6,5,234,245,166,95,203,174,138,208,106,212,206,1,105,25,255,2,32,114,99,233,178,238,114,164,217,11,182,136,194,44,83,169,228,54,60,171,18,192,45,251,46,95,137,21,105,92,224,225,35,65,33,2,94,44,59,108,91,20,133,192,202,205,49,115,123,50,190,188,106,77,79,45,245,152,14,40,248,96,123,183,252,122,181,51,255,255,255,255,2,1,0,0,0,0,0,0,0,149,7,83,116,111,114,97,103,101,32,16,168,221,86,196,182,201,99,251,243,115,140,86,184,42,62,169,34,48,168,69,163,187,160,243,112,227,146,92,65,129,118,70,48,68,2,32,70,185,46,41,230,210,20,101,234,177,72,8,96,5,129,100,35,218,148,84,79,119,219,232,217,14,21,202,194,43,181,222,2,32,11,229,172,229,35,174,155,56,111,127,99,165,246,161,128,7,130,125,107,198,190,200,159,196,171,202,215,179,67,154,249,42,109,117,33,3,52,226,220,30,124,57,198,195,217,109,83,95,4,202,207,189,197,156,234,23,39,227,249,138,47,148,9,5,244,137,148,182,172,5,9,0,0,0,0,0,0,25,118,169,20,8,135,188,68,196,118,17,233,131,37,13,43,71,88,16,162,182,176,158,2,136,172,0,0,0,0]