From b4981f81fc71f04310ab68bc6e5dd376b26065ea Mon Sep 17 00:00:00 2001 From: chedieck Date: Wed, 31 Jul 2024 20:37:22 -0300 Subject: [PATCH 01/16] feat: config for urls in a single config option --- README.md | 26 +++++++---------------- config/example-config.json | 7 ++++--- config/index.ts | 23 +++++++++++++++++---- constants/index.ts | 1 + services/blockchainService.ts | 4 ++-- services/chronikService.ts | 39 ++++++++++++++++++----------------- 6 files changed, 53 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 8328245a5..dd8788dfd 100644 --- a/README.md +++ b/README.md @@ -71,28 +71,16 @@ default: false, > If the connection of test networks for eCash and Bitcoin Cash should appear in the Networks tab. -#### grpcBCHNodeURL -``` -type: string -default: "bchd.greyh.at:8335" -``` -> GRPC URL to connect to for BCH (unsupported at the moment). - - -#### grpcXECNodeURL -``` -type: string -default: "grpc.fabien.cash:8335" +default: "https://chronik.fabien.cash" +#### networkBlockchainURLs ``` -> GRPC URL to connect to for XEC (unsupported at the moment). - +type: { + "ecash": string + "bitcoincash": string +} -#### chronikClientURL -``` -type: string -default: "https://chronik.fabien.cash" ``` -> URL for the Chronik client to connect to. Providing an array of URLs is supported. +> What URLs to connect each network chosen client to (from networkBlockchainClients) #### priceAPIURL diff --git a/config/example-config.json b/config/example-config.json index 59afed951..f637ef1c0 100644 --- a/config/example-config.json +++ b/config/example-config.json @@ -5,15 +5,16 @@ "websiteDomain": "http://localhost:3000", "wsBaseURL": "http://localhost:5000", "showTestNetworks": false, - "grpcBCHNodeURL": "bchd.greyh.at:8335", - "grpcXECNodeURL": "grpc.fabien.cash:8335", - "chronikClientURL": "https://chronik.fabien.cash", "priceAPIURL": "https://coin.dance/api/", "redisURL": "redis://paybutton-cache:6379", "networkBlockchainClients": { "ecash": "chronik", "bitcoincash": "grpc" }, + "networkBlockchainURLs": { + "ecash": "https://chronik.fabien.cash", + "bitcoincash": "https://chronik.pay2stay.com/bch" + }, "networksUnderMaintenance": { "bitcoincash": true }, diff --git a/config/index.ts b/config/index.ts index 1ec67e6fb..30998051e 100644 --- a/config/index.ts +++ b/config/index.ts @@ -1,4 +1,4 @@ -import { KeyValueT } from 'constants/index' +import { KeyValueT, RESPONSE_MESSAGES } from 'constants/index' import localConfig from '../paybutton-config.json' export type BlockchainClientOptions = 'grpc' | 'chronik' @@ -12,12 +12,10 @@ interface Config { wsBaseURL: string showTestNetworks: false - grpcBCHNodeURL: string - grpcXECNodeURL: string - chronikClientURL: string priceAPIURL: string redisURL: string networkBlockchainClients: KeyValueT + networkBlockchainURLs: KeyValueT networksUnderMaintenance: KeyValueT triggerPOSTTimeout: number } @@ -28,6 +26,23 @@ const readConfig = (): Config => { if (config.networksUnderMaintenance === undefined) { config.networksUnderMaintenance = {} } + if ( + ( + config.networkBlockchainURLs.ecash === undefined || + config.networkBlockchainURLs.ecash === '' + ) && + !config.networksUnderMaintenance.ecash + ) { + throw new Error(RESPONSE_MESSAGES.MISSING_BLOCKCHAIN_CLIENT_URL_400('ecash').message) + } else if ( + ( + config.networkBlockchainURLs.bitcoincash === undefined || + config.networkBlockchainURLs.bitcoincash === '' + ) && + !config.networksUnderMaintenance.bitcoincash + ) { + throw new Error(RESPONSE_MESSAGES.MISSING_BLOCKCHAIN_CLIENT_URL_400('bitcoincash').message) + } const wsURLSplit = config.wsBaseURL.split('//') const noProtocolWsURL = wsURLSplit[wsURLSplit.length - 1] if (process.env.NODE_ENV === 'production') { diff --git a/constants/index.ts b/constants/index.ts index 382235636..0f25fc114 100644 --- a/constants/index.ts +++ b/constants/index.ts @@ -54,6 +54,7 @@ export const RESPONSE_MESSAGES = { INVALID_QUOTE_SLUG_400: { statusCode: 400, message: 'Invalid quote slug.' }, INVALID_TICKER_400: { statusCode: 400, message: 'Invalid ticker.' }, MISSING_BLOCKCHAIN_CLIENT_400: { statusCode: 400, message: 'There is no blockchain client chosen for this network.' }, + MISSING_BLOCKCHAIN_CLIENT_URL_400: (networkSlug: string) => { return { statusCode: 400, message: `Missing client URL for network ${networkSlug}` } }, NO_BLOCKCHAIN_CLIENT_INSTANTIATED_400: { statusCode: 400, message: 'Blockchain client was not instantiated.' }, DEFAULT_WALLET_CANNOT_BE_DELETED_400: { statusCode: 400, message: 'A default wallet cannot be deleted.' }, NO_USER_PROFILE_FOUND_404: { statusCode: 404, message: 'User profile not found.' }, diff --git a/services/blockchainService.ts b/services/blockchainService.ts index ffc2f115f..044965fc5 100644 --- a/services/blockchainService.ts +++ b/services/blockchainService.ts @@ -64,9 +64,9 @@ function getBlockchainClient (networkSlug: string): BlockchainClient { case 'grpc' as BlockchainClientOptions: return new GrpcBlockchainClient() case 'chronik' as BlockchainClientOptions: - if (global.chronik === undefined && ChronikBlockchainClient !== undefined) { + if (global.chronik === undefined) { console.log('creating chronik instance...') - global.chronik = new ChronikBlockchainClient() + global.chronik = new ChronikBlockchainClient(networkSlug) // Subscribe addresses & Sync lost transactions on DB upon client initialization if ( process.env.NEXT_PHASE !== PHASE_PRODUCTION_BUILD && diff --git a/services/chronikService.ts b/services/chronikService.ts index ec6b8412c..958872b13 100644 --- a/services/chronikService.ts +++ b/services/chronikService.ts @@ -115,12 +115,13 @@ export class ChronikBlockchainClient implements BlockchainClient { wsEndpoint: Socket lastProcessedMessages: ProcessedMessages - constructor () { + constructor (networkSlug: string) { if (process.env.WS_AUTH_KEY === '' || process.env.WS_AUTH_KEY === undefined) { throw new Error(RESPONSE_MESSAGES.MISSING_WS_AUTH_KEY_400.message) } - this.chronik = new ChronikClientNode([config.chronikClientURL]) - this.availableNetworks = [NETWORK_SLUGS.ecash] + + this.chronik = new ChronikClientNode([config.networkBlockchainURLs[networkSlug]]) + this.availableNetworks = [NETWORK_SLUGS.ecash, NETWORK_SLUGS.bitcoincash] this.subscribedAddresses = {} this.chronikWSEndpoint = this.chronik.ws(this.getWsConfig()) void this.chronikWSEndpoint.waitForOpen() @@ -276,7 +277,7 @@ export class ChronikBlockchainClient implements BlockchainClient { const persistedTransactions = await createManyTransactions(transactionsToPersist) const simplifiedTransactions = getSimplifiedTransactions(persistedTransactions) - console.log(`added ${simplifiedTransactions.length} txs to ${addressString}`) + console.log(`[CHRONIK]: added ${simplifiedTransactions.length} txs to ${addressString}`) const broadcastTxData: BroadcastTxData = {} as BroadcastTxData broadcastTxData.messageType = 'OldTx' @@ -336,10 +337,10 @@ export class ChronikBlockchainClient implements BlockchainClient { private getWsConfig (): WsConfig_InNode { return { onMessage: (msg: WsMsgClient) => { void this.processWsMessage(msg) }, - onError: (e: ws.ErrorEvent) => { console.log(`Chronik webSocket error, type: ${e.type} | message: ${e.message} | error: ${e.error as string}`) }, - onReconnect: (_: ws.Event) => { console.log('Chronik webSocket unexpectedly closed.') }, - onConnect: (_: ws.Event) => { console.log('Chronik webSocket connection (re)established.') }, - onEnd: (e: ws.Event) => { console.log(`Chronik WebSocket ended, type: ${e.type}.`) }, + onError: (e: ws.ErrorEvent) => { console.log(`[CHRONIK]: Chronik webSocket error, type: ${e.type} | message: ${e.message} | error: ${e.error as string}`) }, + onReconnect: (_: ws.Event) => { console.log('[CHRONIK]: Chronik webSocket unexpectedly closed.') }, + onConnect: (_: ws.Event) => { console.log('[CHRONIK]: Chronik webSocket connection (re)established.') }, + onEnd: (e: ws.Event) => { console.log(`[CHRONIK]: Chronik WebSocket ended, type: ${e.type}.`) }, autoReconnect: true } } @@ -349,7 +350,7 @@ export class ChronikBlockchainClient implements BlockchainClient { // if they were cancelled and not confirmed if (msg.type === 'Tx') { if (msg.msgType === 'TX_REMOVED_FROM_MEMPOOL') { - console.log(`[${msg.type}] ${msg.txid}`) + console.log(`[CHRONIK]: [${msg.type}] ${msg.txid}`) const transactionsToDelete = await fetchUnconfirmedTransactions(msg.txid) try { await deleteTransactions(transactionsToDelete) @@ -364,7 +365,7 @@ export class ChronikBlockchainClient implements BlockchainClient { if (this.isAlreadyBeingProcessed(msg.txid, msg.msgType === 'TX_CONFIRMED')) { return } - console.log(`[${msg.type}] ${msg.txid}`) + console.log(`[CHRONIK]: [${msg.type}] ${msg.txid}`) const transaction = await this.chronik.tx(msg.txid) const addressesWithTransactions = await this.getAddressesForTransaction(transaction) for (const addressWithTransaction of addressesWithTransactions) { @@ -391,9 +392,9 @@ export class ChronikBlockchainClient implements BlockchainClient { } } } else if (msg.type === 'Block') { - console.log(`[${msg.type.toString()}] ${msg.msgType} Height: ${msg.blockHeight} Hash: ${msg.blockHash}`) + console.log(`[CHRONIK]: [${msg.type}] ${msg.msgType} Height: ${msg.blockHeight} Hash: ${msg.blockHash}`) } else if (msg.type === 'Error') { - console.log(`[${msg.type}] ${JSON.stringify(msg.msg)}`) + console.log(`[CHRONIK]: [${msg.type}] ${JSON.stringify(msg.msg)}`) } } @@ -417,7 +418,7 @@ export class ChronikBlockchainClient implements BlockchainClient { const addressesAlreadySubscribed = addresses.filter(address => Object.keys(this.subscribedAddresses).includes(address.address)) addressesAlreadySubscribed.forEach(address => { - console.log(`This address was already subscribed: ${address.address}`) + console.warn(`[CHRONIK]: address already subscribed: ${address.address}`) }) if (addressesAlreadySubscribed.length === addresses.length) return addresses = addresses.filter(address => !addressesAlreadySubscribed.includes(address)) @@ -426,7 +427,7 @@ export class ChronikBlockchainClient implements BlockchainClient { for (const [, networkAddresses] of Object.entries(addressesByNetwork)) { networkAddresses.forEach(address => { - console.log('subbing', address.address) + console.log('[CHRONIK]: subscribing ', address.address) this.chronikWSEndpoint.subscribeToAddress(address.address) this.subscribedAddresses[address.address] = address }) @@ -438,13 +439,13 @@ export class ChronikBlockchainClient implements BlockchainClient { try { const { failedAddressesWithErrors, successfulAddressesWithCount } = await syncAddresses(addresses) Object.keys(failedAddressesWithErrors).forEach((addr) => { - console.error(`When syncing missing addresses for address ${addr} encountered error: ${failedAddressesWithErrors[addr]}`) + console.error(`[CHRONIK]: When syncing missing addresses for address ${addr} encountered error: ${failedAddressesWithErrors[addr]}`) }) Object.keys(successfulAddressesWithCount).forEach((addr) => { - console.log(`Successful synced ${successfulAddressesWithCount[addr]} txs for ${addr}`) + console.log(`[CHRONIK]: Successful synced ${successfulAddressesWithCount[addr]} txs for ${addr}`) }) } catch (err: any) { - console.error(`ERROR: (skipping anyway) initial missing transactions sync failed: ${err.message as string} ${err.stack as string}`) + console.error(`[CHRONIK]: ERROR: (skipping anyway) initial missing transactions sync failed: ${err.message as string} ${err.stack as string}`) } } @@ -453,7 +454,7 @@ export class ChronikBlockchainClient implements BlockchainClient { try { await this.subscribeAddresses(addresses) } catch (err: any) { - console.error(`ERROR: (skipping anyway) initial chronik subscription failed: ${err.message as string} ${err.stack as string}`) + console.error(`[CHRONIK]: ERROR: (skipping anyway) initial chronik subscription failed: ${err.message as string} ${err.stack as string}`) } } } @@ -484,7 +485,7 @@ export function toHash160 (address: string): {type: ScriptType_InNode, hash160: ) return { type: type.toLowerCase() as ScriptType_InNode, hash160: addrHash160 } } catch (err) { - console.log('Error converting address to hash160') + console.log('[CHRONIK]: Error converting address to hash160') throw err } } From a3187aa8d78d5764a7181e9bd19a8dee142e5c3b Mon Sep 17 00:00:00 2001 From: chedieck Date: Thu, 1 Aug 2024 12:02:38 -0300 Subject: [PATCH 02/16] feat: remove grpc --- Makefile | 2 +- README.md | 14 +- constants/index.ts | 8 +- package.json | 1 - services/blockchainService.ts | 3 - services/grpcService.ts | 310 --------------------------- tests/mockGrpcClient.ts | 70 ------ tests/mockedObjects.ts | 154 +------------- tests/setupMocks.ts | 7 - yarn.lock | 386 +--------------------------------- 10 files changed, 17 insertions(+), 938 deletions(-) delete mode 100644 services/grpcService.ts delete mode 100644 tests/mockGrpcClient.ts delete mode 100644 tests/setupMocks.ts diff --git a/Makefile b/Makefile index b5dd03811..becfaca83 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ git_hook_setup = cp .githooks/pre-commit .git/hooks/pre-commit git_diff_to_master = git diff --name-only --diff-filter=ACMRTUXB origin/master > DIFF -create_test_paybutton_json = echo { \"priceAPIURL\": \"foo\", \"networkBlockchainClients\": { \"ecash\": \"chronik\", \"bitcoincash\": \"grpc\" }, \"chronikClientURL\": \"https://xec.paybutton.io\", \"wsBaseURL\": \"localhost:5000\" } > paybutton-config.json +create_test_paybutton_json = echo { \"priceAPIURL\": \"foo\", \"networkBlockchainClients\": { \"ecash\": \"chronik\", \"bitcoincash\": \"chronik\" }, \"networkBlockchainURLs\": { \"ecash\": \"https://xec.paybutton.io\", \"bitcoincash\": \"https://chronik.pay2stay.com/bch\" }, \"wsBaseURL\": \"localhost:5000\" } > paybutton-config.json touch_local_env = touch .env.local prod: diff --git a/README.md b/README.md index dd8788dfd..31807d26e 100644 --- a/README.md +++ b/README.md @@ -71,12 +71,11 @@ default: false, > If the connection of test networks for eCash and Bitcoin Cash should appear in the Networks tab. -default: "https://chronik.fabien.cash" #### networkBlockchainURLs ``` type: { - "ecash": string - "bitcoincash": string + "ecash": "https://chronik.fabien.cash", + "bitcoincash": "https://chronik.pay2stay.com/bch" } ``` @@ -101,16 +100,15 @@ default: "redis://paybutton-cache:6379" #### networkBlockchainClients ``` type: { - "ecash": "chronik" | "grpc" - "bitcoincash": "grpc" + "ecash": "chronik", + "bitcoincash": "chronik" } default: { "ecash": "chronik", - "bitcoincash": "grpc" + "bitcoincash": "chronik" } ``` -> Which client to use to get the blockchain information for each network. Currently, only "chronik" is supported for eCash -and Bitcoin Cash is not supported. +> Which client to use to get the blockchain information for each network. Currently, only "chronik" is supported for eCash and Bitcoin Cash. #### networksUnderMaintenance diff --git a/constants/index.ts b/constants/index.ts index 0f25fc114..3a33d6e75 100644 --- a/constants/index.ts +++ b/constants/index.ts @@ -152,13 +152,7 @@ export const NETWORK_TICKERS_FROM_ID: KeyValueT = { export const NETWORK_IDS: KeyValueT = { XEC: 1, BCH: 2 } export const QUOTE_IDS: KeyValueT = { USD: 1, CAD: 2 } -export type BLOCKCHAIN_CLIENT_OPTIONS = 'grpc' | 'chronik' -export const NETWORK_BLOCKCHAIN_CLIENTS: KeyValueT = { - ecash: 'chronik', - bitcoincash: 'grpc' -} - -export const CHRONIK_CLIENT_URL = 'https://chronik.be.cash/xec' +export type BLOCKCHAIN_CLIENT_OPTIONS = 'chronik' export const UPSERT_TRANSACTION_PRICES_ON_DB_TIMEOUT = 45000 export const DEFAULT_TX_PAGE_SIZE = 100 diff --git a/package.json b/package.json index 2a297e849..f7e40af5a 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,6 @@ "ecashaddrjs": "^1.0.7", "express": "^4.17.1", "graphql": "^16.3.0", - "grpc-bchrpc-node": "^0.15.2", "helmet": "^4.4.1", "lodash": "^4.17.21", "micro": "^9.3.4", diff --git a/services/blockchainService.ts b/services/blockchainService.ts index 044965fc5..c823002a9 100644 --- a/services/blockchainService.ts +++ b/services/blockchainService.ts @@ -1,4 +1,3 @@ -import { GrpcBlockchainClient } from './grpcService' import { ChronikBlockchainClient } from './chronikService' import { getObjectValueForAddress, getObjectValueForNetworkSlug } from '../utils/index' import { RESPONSE_MESSAGES, KeyValueT, NETWORK_IDS, NETWORK_TICKERS } from '../constants/index' @@ -61,8 +60,6 @@ function getBlockchainClient (networkSlug: string): BlockchainClient { if (!Object.keys(config.networkBlockchainClients).includes(networkSlug)) { throw new Error(RESPONSE_MESSAGES.MISSING_BLOCKCHAIN_CLIENT_400.message) } switch (config.networkBlockchainClients[networkSlug]) { - case 'grpc' as BlockchainClientOptions: - return new GrpcBlockchainClient() case 'chronik' as BlockchainClientOptions: if (global.chronik === undefined) { console.log('creating chronik instance...') diff --git a/services/grpcService.ts b/services/grpcService.ts deleted file mode 100644 index 3b5df8543..000000000 --- a/services/grpcService.ts +++ /dev/null @@ -1,310 +0,0 @@ -import { - GrpcClient, - TransactionNotification, - Transaction as GrpcTransaction, - GetAddressUnspentOutputsResponse, - MempoolTransaction, - Transaction -} from 'grpc-bchrpc-node' - -import { AddressWithTransaction, BlockchainClient, BlockchainInfo, BlockInfo, TransactionDetails } from './blockchainService' -import { getObjectValueForNetworkSlug, getObjectValueForAddress, satoshisToUnit, pubkeyToAddress, removeAddressPrefix, groupAddressesByNetwork } from '../utils/index' -import { BCH_NETWORK_ID, BCH_TIMESTAMP_THRESHOLD, FETCH_DELAY, FETCH_N, KeyValueT, NETWORK_SLUGS, RESPONSE_MESSAGES, XEC_NETWORK_ID, XEC_TIMESTAMP_THRESHOLD } from '../constants/index' -import { Address, Prisma } from '@prisma/client' -import xecaddr from 'xecaddrjs' -import { fetchAddressBySubstring, setSyncing, updateLastSynced } from './addressService' -import { TransactionWithAddressAndPrices, createTransaction, createManyTransactions, base64HashToHex, getSimplifiedTrasaction } from './transactionService' -import { BroadcastTxData } from 'ws-service/types' -import config from 'config' -import io, { Socket } from 'socket.io-client' - -export interface OutputsList { - outpoint: object - pubkeyScript: string - value: number - isCoinbase: boolean - blockHeight: number - slpToken: string | undefined -} - -const grpcBCH = new GrpcClient({ url: config.grpcBCHNodeURL }) - -export const getGrpcClients = (): KeyValueT => { - return { - bitcoincash: grpcBCH, - ecash: new GrpcClient({ url: config.grpcXECNodeURL }) - } -} - -export class GrpcBlockchainClient implements BlockchainClient { - availableNetworks: string[] - subscribedAddresses: KeyValueT
- wsEndpoint: Socket - - constructor () { - this.availableNetworks = [NETWORK_SLUGS.bitcoincash, NETWORK_SLUGS.ecash] - this.subscribedAddresses = {} - this.wsEndpoint = io(`${config.wsBaseURL}/broadcast`, { - query: { - key: process.env.WS_AUTH_KEY - } - }) - } - - private getClientForAddress (addressString: string): GrpcClient { - return getObjectValueForAddress(addressString, getGrpcClients()) - } - - private getClientForNetworkSlug (networkSlug: string): GrpcClient { - return getObjectValueForNetworkSlug(networkSlug, getGrpcClients()) - } - - public async getBlockchainInfo (networkSlug: string): Promise { - const client = this.getClientForNetworkSlug(networkSlug) - const blockchainInfo = (await client.getBlockchainInfo()).toObject() - return { height: blockchainInfo.bestHeight, hash: blockchainInfo.bestBlockHash } - }; - - public async getBlockInfo (networkSlug: string, height: number): Promise { - const client = this.getClientForNetworkSlug(networkSlug) - const blockInfo = (await client.getBlockInfo({ index: height })).toObject()?.info - if (blockInfo === undefined) { throw new Error(RESPONSE_MESSAGES.COULD_NOT_GET_BLOCK_INFO_500.message) } - return { hash: blockInfo.hash, height: blockInfo.height, timestamp: blockInfo.timestamp } - }; - - private txThesholdFilter (address: Address) { - return (t: GrpcTransaction.AsObject, index: number, array: GrpcTransaction.AsObject[]): boolean => { - return ( - (t.timestamp >= XEC_TIMESTAMP_THRESHOLD && address.networkId === XEC_NETWORK_ID) || - (t.timestamp >= BCH_TIMESTAMP_THRESHOLD && address.networkId === BCH_NETWORK_ID) - ) - } - } - - private parseMempoolTx (mempoolTx: MempoolTransaction.AsObject): GrpcTransaction.AsObject { - const tx = mempoolTx.transaction! - tx.timestamp = mempoolTx.addedTime - return tx - } - - private async getTransactionAmount (transaction: GrpcTransaction.AsObject, addressString: string): Promise { - let totalOutput = 0 - let totalInput = 0 - const addressFormat = xecaddr.detectAddressFormat(addressString) - const unprefixedAddress = removeAddressPrefix(addressString) - - for (const output of transaction.outputsList) { - let outAddress: string | undefined = removeAddressPrefix(output.address) - if (output.scriptClass === 'pubkey') { - outAddress = await pubkeyToAddress(outAddress, addressFormat) - if (outAddress !== undefined) outAddress = removeAddressPrefix(outAddress) - } - if (unprefixedAddress === outAddress) { - totalOutput += output.value - } - } - for (const input of transaction.inputsList) { - let addressFromPkey = await pubkeyToAddress(input.address, addressFormat) - if (addressFromPkey !== undefined) addressFromPkey = removeAddressPrefix(addressFromPkey) - if (unprefixedAddress === removeAddressPrefix(input.address) || unprefixedAddress === addressFromPkey) { - totalInput += input.value - } - } - const satoshis = new Prisma.Decimal(totalOutput).minus(totalInput) - return await satoshisToUnit( - satoshis, - addressFormat - ) - } - - // WIP: this should be private in the future (after 411-6) - public async getTransactionFromGrpcTransaction (transaction: GrpcTransaction.AsObject, address: Address, confirmed: boolean): Promise { - return { - hash: await base64HashToHex(transaction.hash as string), - amount: await this.getTransactionAmount(transaction, address.address), - timestamp: transaction.timestamp, - addressId: address.id, - confirmed - } - } - - public async * syncTransactionsForAddress (addressString: string): AsyncGenerator { - const address = await fetchAddressBySubstring(addressString) - const pageSize = FETCH_N - let page = 0 - - while (true) { - const client = this.getClientForAddress(address.address) - const transactions = (await client.getAddressTransactions({ - address: address.address, - nbSkip: page * pageSize, - nbFetch: pageSize - })).toObject() - - if (transactions.confirmedTransactionsList.length === 0 && transactions.unconfirmedTransactionsList.length === 0) break - - // filter out transactions that happened before a certain date set in constants/index, - // this date is understood as the beginning and we don't look past it - const confirmedTransactions = transactions.confirmedTransactionsList.filter(this.txThesholdFilter(address)) - const unconfirmedTransactions = transactions.unconfirmedTransactionsList.map(mempoolTx => this.parseMempoolTx(mempoolTx)) - - page += 1 - - const transactionsToPersist = [ - ...await Promise.all( - confirmedTransactions.map(async tx => await this.getTransactionFromGrpcTransaction(tx, address, true)) - ), - ...await Promise.all( - unconfirmedTransactions.map(async tx => await this.getTransactionFromGrpcTransaction(tx, address, false)) - ) - ] - const persistedTransactions = await createManyTransactions(transactionsToPersist) - yield persistedTransactions - - await new Promise(resolve => setTimeout(resolve, FETCH_DELAY)) - } - await setSyncing(addressString, false) - await updateLastSynced(addressString) - }; - - private async getUtxos (address: string): Promise { - const client = this.getClientForAddress(address) - const res = (await client.getAddressUtxos({ address })).toObject() - return res - }; - - public async getBalance (address: string): Promise { - const outputsList = (await this.getUtxos(address)).outputsList - return outputsList.reduce((acc, output) => acc + output.value, 0) - }; - - public async getTransactionDetails (hash: string, networkSlug: string): Promise { - const client = this.getClientForNetworkSlug(networkSlug) - const tx = ( - await client.getTransaction({ hash, reversedHashOrder: true }) - ).toObject().transaction as GrpcTransaction.AsObject - - const details: TransactionDetails = { - hash: tx.hash as string, - version: tx.version, - block: { - hash: tx.blockHash as string, - height: tx.blockHeight, - timestamp: `${tx.timestamp}` - }, - inputs: [], - outputs: [] - } - for (const input of tx.inputsList) { - details.inputs.push({ - value: new Prisma.Decimal(input.value), - address: input.address - }) - } - for (const output of tx.outputsList) { - details.outputs.push({ - value: new Prisma.Decimal(output.value), - address: output.address - }) - } - return details - }; - - public async subscribeAddresses (addresses: Address[]): Promise { - if (addresses.length === 0) return - - const addressesAlreadySubscribed = addresses.filter(address => Object.keys(this.subscribedAddresses).includes(address.address)) - if (addressesAlreadySubscribed.length === addresses.length) return - addressesAlreadySubscribed.forEach(address => { - console.log(`This address was already subscribed: ${address.address}`) - }) - addresses = addresses.filter(address => !addressesAlreadySubscribed.includes(address)) - - const addressesByNetwork: KeyValueT = groupAddressesByNetwork(this.availableNetworks, addresses) - - for (const [network, networkAddresses] of Object.entries(addressesByNetwork)) { - const client = getGrpcClients()[network] - const addressesToSubscribe = networkAddresses.map(address => address.address) - const stream = await client.subscribeTransactions({ - includeMempoolAcceptance: true, - includeBlockAcceptance: true, - addresses: addressesToSubscribe - }) - const nowDateString = (new Date()).toISOString() - - // output for end, error or close of stream - void stream.on('end', () => { - console.log(`${nowDateString}: addresses ${addressesToSubscribe.join(', ')} stream ended`) - }) - void stream.on('close', () => { - console.log(`${nowDateString}: addresses ${addressesToSubscribe.join(', ')} stream closed`) - }) - void stream.on('error', (error: any) => { - console.log(`${nowDateString}: addresses ${addressesToSubscribe.join(', ')} stream error`, error) - }) - - // output for data stream - void stream.on('data', (data: TransactionNotification) => { - void this.processSubscribedNotification(data) - }) - - // subscribed addresses - networkAddresses.forEach(address => { - this.subscribedAddresses[address.address] = address - }) - } - } - - private async getAddressesForTransaction (transaction: Transaction.AsObject, confirmed: boolean): Promise { - const addressWithTransactions: AddressWithTransaction[] = await Promise.all(Object.values(this.subscribedAddresses).map( - async address => { - return { - address, - transaction: await this.getTransactionFromGrpcTransaction(transaction, address, confirmed) - } - } - )) - return addressWithTransactions.filter( - addressWithTransaction => addressWithTransaction.transaction.amount > new Prisma.Decimal(0) - ) - } - - private async processSubscribedNotification (data: TransactionNotification): Promise { - let addressWithConfirmedTransactions: AddressWithTransaction[] = [] - let addressWithUnconfirmedTransactions: AddressWithTransaction[] = [] - - // get new confirmed transactions - const confirmedTransaction = data.getConfirmedTransaction()?.toObject() - if (confirmedTransaction != null) { - addressWithConfirmedTransactions = await this.getAddressesForTransaction(confirmedTransaction, true) - } - - // get new unconfirmed transactions - const unconfirmedTransaction = data.getUnconfirmedTransaction()?.toObject() - if (unconfirmedTransaction != null) { - const parsedUnconfirmedTransaction = this.parseMempoolTx(unconfirmedTransaction) - addressWithUnconfirmedTransactions = await this.getAddressesForTransaction(parsedUnconfirmedTransaction, false) - } - - const broadcastTxData: BroadcastTxData = {} as BroadcastTxData - await Promise.all( - [...addressWithUnconfirmedTransactions, ...addressWithConfirmedTransactions].map(async addressWithTransaction => { - const { tx } = await createTransaction(addressWithTransaction.transaction) - - if (tx !== undefined) { - const simplifiedTransaction = getSimplifiedTrasaction(tx) - broadcastTxData.address = addressWithTransaction.address.address - broadcastTxData.messageType = 'NewTx' - broadcastTxData.txs = [simplifiedTransaction] - // TODO: implement triggers - } - return tx - }) - ) - try { - this.wsEndpoint.emit('txs-broadcast', broadcastTxData) - } catch (err: any) { - console.error(RESPONSE_MESSAGES.COULD_NOT_BROADCAST_TX_TO_WS_SERVER_500.message, err.stack) - } - } -} diff --git a/tests/mockGrpcClient.ts b/tests/mockGrpcClient.ts deleted file mode 100644 index 9585231a5..000000000 --- a/tests/mockGrpcClient.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { mockedGrpc, unspentOutputFromObject, transactionFromObject } from './mockedObjects' -import { - GetTransactionResponse, - GetAddressTransactionsResponse, - GetAddressUnspentOutputsResponse -} from 'grpc-bchrpc-node' - -export default class MockGrpcClient { - private _checkNetworkIntegrity = false - - public get checkNetworkIntegrity (): boolean { - return this._checkNetworkIntegrity - } - - public set checkNetworkIntegrity (value) { - this._checkNetworkIntegrity = value - } - - getAddressTransactions (_: any): GetAddressTransactionsResponse { - const res = new GetAddressTransactionsResponse() - if (_.nbSkip > 0) { - res.setConfirmedTransactionsList([]) - } else { - res.setConfirmedTransactionsList([mockedGrpc.transaction1, mockedGrpc.transaction2]) - } - return res - } - - getAddressUtxos (_: object): GetAddressUnspentOutputsResponse { - const res = new GetAddressUnspentOutputsResponse() - res.setOutputsList([ - unspentOutputFromObject({ - pubkeyScript: 'dqkUYF1GSq6KqSQSKPbQtcOJWRBPEFaIrA==', - value: 547, - isCoinbase: false, - blockHeight: 684161 - }), - unspentOutputFromObject({ - pubkeyScript: 'dqkUYF1GSq6KqSQSKPbQtcOJWRBPEFaIrA==', - value: 122, - isCoinbase: false, - blockHeight: 657711 - }), - unspentOutputFromObject({ - pubkeyScript: 'dqkUYF1GSq6KqSQSKPbQtcOJWRBPEFaIrA==', - value: 1111, - isCoinbase: false, - blockHeight: 596627 - }) - ]) - return res - } - - getTransaction (_: object): GetTransactionResponse { - const res = new GetTransactionResponse() - res.setTransaction(transactionFromObject({ - hash: 'hu9m3BZg/zlxis7ehc0x/+9qELXC8dkbimOtc5v598s=', - version: 2, - lockTime: 0, - size: 518, - timestamp: 1653653100, - confirmations: 0, - blockHeight: 0, - blockHash: '', - inputsList: [], - outputsList: [] - })) - return res - } -} diff --git a/tests/mockedObjects.ts b/tests/mockedObjects.ts index 03fb21e6f..6340fdb41 100644 --- a/tests/mockedObjects.ts +++ b/tests/mockedObjects.ts @@ -1,9 +1,5 @@ // Paybutton // GRPC-BCHRPC -import { - Transaction, - UnspentOutput -} from 'grpc-bchrpc-node' import { Prisma, Price, UserProfile, AddressesOnButtons } from '@prisma/client' import { PaybuttonWithAddresses } from 'services/paybuttonService' @@ -461,152 +457,6 @@ export const mockedTransactionList = [ } ] -// BCH GRPC -export const unspentOutputFromObject = (obj: UnspentOutput.AsObject): UnspentOutput => { - const uo = new UnspentOutput() - uo.setPubkeyScript(obj.pubkeyScript) - uo.setValue(obj.value) - uo.setIsCoinbase(obj.isCoinbase) - uo.setBlockHeight(obj.blockHeight) - return uo -} - -const outputFromObject = (obj: Transaction.Output.AsObject): Transaction.Output => { - const out = new Transaction.Output() - out.setIndex(obj.index) - out.setValue(obj.value) - out.setPubkeyScript(obj.pubkeyScript) - out.setAddress(obj.address) - out.setScriptClass(obj.scriptClass) - out.setDisassembledScript(obj.disassembledScript) - return out -} - -const inputFromObject = (obj: Transaction.Input.AsObject): Transaction.Input => { - const inp = new Transaction.Input() - inp.setIndex(obj.index) - inp.setSignatureScript(obj.signatureScript) - inp.setSequence(obj.sequence) - inp.setValue(obj.value) - inp.setPreviousScript(obj.previousScript) - inp.setAddress(obj.address) - return inp -} - -export const transactionFromObject = (obj: Transaction.AsObject): Transaction => { - const t = new Transaction() - t.setHash(obj.hash) - t.setVersion(obj.version) - t.setLockTime(obj.lockTime) - t.setSize(obj.size) - t.setTimestamp(obj.timestamp) - t.setConfirmations(obj.confirmations) - t.setBlockHeight(obj.blockHeight) - t.setBlockHash(obj.blockHash) - t.setOutputsList(obj.outputsList.map((out) => outputFromObject(out))) - t.setInputsList(obj.inputsList.map((inp) => inputFromObject(inp))) - return t -} - -export const mockedGrpc = { - transaction1: transactionFromObject({ - hash: 'LUZSpMOab+ZYlyQNxF0XasKpArgQAX633LoA5CBPGgE=', - version: 1, - lockTime: 0, - size: 219, - timestamp: 1653460454, - confirmations: 60, - blockHeight: 741620, - blockHash: 'jzSPV4kkI3x5Fdoow/ei3f7Zit+oGMYCAAAAAAAAAAA=', - inputsList: [ - { - index: 0, - outpoint: { hash: 'NTkmyHCk82XbV43ew+Ev8mSN6hSF1SNPv2q+9MEVQNw=', index: 2 }, - signatureScript: 'RzBEAiATvPHhB2XZSDBh4qPKGbpZnthiP7b6F5nNq0jl9e9segIgQb/p7YKnBcAkXrn1ePKRQOCb3CS2TqjLl55Q46xLA7FBIQI+0YnB1MWonyW6U8ydXQbD46v80yMEO61nvbuLGgeMlA==', - sequence: 4294967295, - value: 5179951, - previousScript: 'dqkUsDxLgAuwV19sDxHhVVQcwGYaj5qIrA==', - address: 'qzcrcjuqpwc9whmvpug7z425rnqxvx50ngl60rrjst', - slpToken: undefined - }, - { - index: 1, - outpoint: { hash: 'cnPvSV4O12UwKBSyPX9RoD+xem14XBlcUFhklAowLjE=', index: 1 }, - signatureScript: 'RzBEAiBVcKX1SZ08kwIvKt+CJCFcq2AMPuJh9xNoZPbFCBtO8AIgDcTg6dy/l8+Se0hD5Fb6zXhaVLZi9JyShl3qlChlCF5BIQNVw6/pYXrkj4YXjrHuzGDWysHPUCvUVptjbOmlkVmamA==', - sequence: 4294967295, - value: 546, - previousScript: 'dqkUi4A+rsJZAKFsCtIAF8coYnYGLEqIrA==', - address: 'qz9cq04wcfvspgtvptfqq9789p38vp3vfgt3y66gue', - slpToken: { - tokenId: 'MS4wCpRkWFBcGVx4bXqxP6BRfz2yFCgwZdcOXknvc3I=', - amount: '1', - isMintBaton: false, - address: 'qz9cq04wcfvspgtvptfqq9789p38vp3vfg820p0gz8', - decimals: 0, - slpAction: 10, - tokenType: 65 - } - } - ], - outputsList: [{ - index: 0, - value: 431247724, - pubkeyScript: 'dqkUeCxnbWKveaKpCSRhJC/poE+saL+IrA==', - address: mockedBCHAddress.address, - scriptClass: 'pubkeyhash', - disassembledScript: 'OP_DUP OP_HASH160 782c676d62af79a2a9092461242fe9a04fac68bf OP_EQUALVERIFY OP_CHECKSIG' - }, { - index: 1, - value: 227413293, - pubkeyScript: 'dqkUokKnAjaab8AVlPxzrrxk1aRq4BOIrA==', - address: 'qz3y9fczx6dxlsq4jn788t4uvn26g6hqzvrczjuzz2', - scriptClass: 'pubkeyhash', - disassembledScript: 'OP_DUP OP_HASH160 a242a702369a6fc01594fc73aebc64d5a46ae013 OP_EQUALVERIFY OP_CHECKSIG' - }] - }), - transaction2: transactionFromObject({ - hash: 'jiZHfE+AohEJglMO29nQ5aTR6F/n4Om2whzEZUiXcHk=', - version: 2, - lockTime: 0, - size: 225, - timestamp: 1653459437, - confirmations: 61, - blockHeight: 741619, - blockHash: 'A6kjJsl4gaVrY0Z15k0SoRzfKv0Fis8EAAAAAAAAAAA=', - inputsList: [ - { - index: 0, - outpoint: { hash: 'NTkmyHCk82XbV43ew+Ev8mSN6hSF1SNPv2q+9MEVQNw=', index: 2 }, - signatureScript: 'RzBEAiATvPHhB2XZSDBh4qPKGbpZnthiP7b6F5nNq0jl9e9segIgQb/p7YKnBcAkXrn1ePKRQOCb3CS2TqjLl55Q46xLA7FBIQI+0YnB1MWonyW6U8ydXQbD46v80yMEO61nvbuLGgeMlA==', - sequence: 4294967295, - value: 5179951, - previousScript: 'dqkUsDxLgAuwV19sDxHhVVQcwGYaj5qIrA==', - address: 'qzcrcjuqpwc9whmvpug7z425rnqxvx50ngl60rrjst', - slpToken: undefined - }, - { - index: 1, - outpoint: { hash: 'cnPvSV4O12UwKBSyPX9RoD+xem14XBlcUFhklAowLjE=', index: 1 }, - signatureScript: 'RzBEAiBVcKX1SZ08kwIvKt+CJCFcq2AMPuJh9xNoZPbFCBtO8AIgDcTg6dy/l8+Se0hD5Fb6zXhaVLZi9JyShl3qlChlCF5BIQNVw6/pYXrkj4YXjrHuzGDWysHPUCvUVptjbOmlkVmamA==', - sequence: 4294967295, - value: 546, - previousScript: 'dqkUi4A+rsJZAKFsCtIAF8coYnYGLEqIrA==', - address: mockedBCHAddress.address, - slpToken: { - tokenId: 'MS4wCpRkWFBcGVx4bXqxP6BRfz2yFCgwZdcOXknvc3I=', - amount: '1', - isMintBaton: false, - address: 'qz9cq04wcfvspgtvptfqq9789p38vp3vfg820p0gz8', - decimals: 0, - slpAction: 10, - tokenType: 65 - } - } - ], - outputsList: [] - }) -} - export const mockedUSDPrice = { id: 1, value: new Prisma.Decimal(10), @@ -703,5 +553,5 @@ export const mockedAddressIdList = [ '0a03a880-86fe-4d82-9aa2-8df270cf032d', 'a37b9a8c-d262-468b-b1dd-571434a16308', '1ca6b7f5-6930-42a7-8ea4-8de57de03251', - '4f68e74f-de19-467a-b195-139d98217ada', -] \ No newline at end of file + '4f68e74f-de19-467a-b195-139d98217ada' +] diff --git a/tests/setupMocks.ts b/tests/setupMocks.ts deleted file mode 100644 index 9b1523f6c..000000000 --- a/tests/setupMocks.ts +++ /dev/null @@ -1,7 +0,0 @@ -import MockGrpcClient from './mockGrpcClient' - -jest.mock('grpc-bchrpc-node', () => ({ - ...jest.requireActual('grpc-bchrpc-node'), - _HAS_NETWORK_INTEGRITY: false, - GrpcClient: jest.fn(() => new MockGrpcClient()) -})) diff --git a/yarn.lock b/yarn.lock index 9db2a07a9..21e0431f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1061,21 +1061,6 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@mapbox/node-pre-gyp@^1.0.4": - version "1.0.10" - resolved "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz" - integrity sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA== - dependencies: - detect-libc "^2.0.0" - https-proxy-agent "^5.0.0" - make-dir "^3.1.0" - node-fetch "^2.6.7" - nopt "^5.0.0" - npmlog "^5.0.1" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.11" - "@material-ui/core@4.12.4": version "4.12.4" resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-4.12.4.tgz#4ac17488e8fcaf55eb6a7f5efb2a131e10138a73" @@ -1471,14 +1456,6 @@ "@types/connect" "*" "@types/node" "*" -"@types/bytebuffer@^5.0.40": - version "5.0.44" - resolved "https://registry.npmjs.org/@types/bytebuffer/-/bytebuffer-5.0.44.tgz" - integrity sha512-k1qonHga/SfQT02NF633i+7tIfKd+cfC/8pjnedcfuXJNMWooss/FkCgRMSnLf2WorLjbuH4bfgAZEbtyHBDoQ== - dependencies: - "@types/long" "^3.0.0" - "@types/node" "*" - "@types/connect@*": version "3.4.35" resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" @@ -1525,11 +1502,6 @@ "@types/qs" "*" "@types/serve-static" "*" -"@types/google-protobuf@^3.7.4": - version "3.15.6" - resolved "https://registry.npmjs.org/@types/google-protobuf/-/google-protobuf-3.15.6.tgz" - integrity sha512-pYVNNJ+winC4aek+lZp93sIKxnXt5qMkuKmaqS3WGuTq0Bw1ZDYNBgzG5kkdtwcv+GmYJGo3yEg6z2cKKAiEdw== - "@types/graceful-fs@^4.1.3": version "4.1.6" resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz" @@ -1591,11 +1563,6 @@ resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz" integrity sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ== -"@types/long@^3.0.0": - version "3.0.32" - resolved "https://registry.npmjs.org/@types/long/-/long-3.0.32.tgz" - integrity sha512-ZXyOOm83p7X8p3s0IYM3VeueNmHpkk/yMlP8CLeOnEcu6hIwPH7YjZBvhQkR0ZFS2DqZAxKtJ/M5fcuv3OU5BA== - "@types/long@^4.0.0", "@types/long@^4.0.1": version "4.0.2" resolved "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz" @@ -1899,11 +1866,6 @@ ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: dependencies: type-fest "^0.21.3" -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" - integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== - ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" @@ -2029,19 +1991,6 @@ apollo-server-types@^3.8.0: apollo-reporting-protobuf "^3.4.0" apollo-server-env "^4.2.1" -"aproba@^1.0.3 || ^2.0.0": - version "2.0.0" - resolved "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz" - integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== - -are-we-there-yet@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz" - integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== - dependencies: - delegates "^1.0.0" - readable-stream "^3.6.0" - arg@4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/arg/-/arg-4.1.0.tgz" @@ -2105,14 +2054,6 @@ array.prototype.flatmap@^1.3.1: es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" -ascli@~1: - version "1.0.1" - resolved "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz" - integrity sha512-JGQaNxpaCJz9Bd1JvVaFIHuWn9S+l3xhN17R0V/vmUDiGE0QngNMXhjlqpwqV+91plWz9Fg+Lt28Lj7p5vjs8A== - dependencies: - colour "~0.7.1" - optjs "~3.2.2" - astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz" @@ -2405,14 +2346,6 @@ buffer-from@^1.0.0: resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer@^5.6.0: - version "5.7.1" - resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - buffer@^6.0.3: version "6.0.3" resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz" @@ -2442,13 +2375,6 @@ bullmq@^3.3.5: tslib "^2.0.0" uuid "^9.0.0" -bytebuffer@~5: - version "5.0.1" - resolved "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz" - integrity sha512-IuzSdmADppkZ6DlpycMkm8l9zeEq16fWtLvunEwFiYciR/BHo4E8/xs5piFquG+Za8OWmMqHF8zuRviz2LHvRQ== - dependencies: - long "~3" - bytes@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" @@ -2472,11 +2398,6 @@ callsites@^3.0.0: resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -camelcase@^2.0.1: - version "2.1.1" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz" - integrity sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw== - camelcase@^5.3.1: version "5.3.1" resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" @@ -2534,11 +2455,6 @@ chokidar@^3.5.2: optionalDependencies: fsevents "~2.3.2" -chownr@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz" - integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== - chronik-client@^0.28.0: version "0.28.0" resolved "https://registry.yarnpkg.com/chronik-client/-/chronik-client-0.28.0.tgz#4a194a91ec9b26da19435b2ca8aca6f929fb8ac1" @@ -2602,15 +2518,6 @@ client-only@0.0.1: resolved "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz" integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== -cliui@^3.0.3: - version "3.2.0" - resolved "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz" - integrity sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w== - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi "^2.0.0" - cliui@^8.0.1: version "8.0.1" resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" @@ -2635,11 +2542,6 @@ co@^4.6.0: resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz" integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz" - integrity sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA== - collect-v8-coverage@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz" @@ -2669,21 +2571,11 @@ color-name@~1.1.4: resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-support@^1.1.2: - version "1.1.3" - resolved "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== - colorette@^2.0.16: version "2.0.19" resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz" integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== -colour@~0.7.1: - version "0.7.1" - resolved "https://registry.npmjs.org/colour/-/colour-0.7.1.tgz" - integrity sha512-Rel466v0EnmKPcsxHo91L4kgPs/6XF7Pu2LJNszq9lXYwi5CFWEeIiRaTX5ym7PPMdj4udDHkLSVC1//JVkZQg== - combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -2706,11 +2598,6 @@ concat-map@0.0.1: resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -console-control-strings@^1.0.0, console-control-strings@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" - integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== - content-disposition@0.5.4, content-disposition@^0.5.3: version "0.5.4" resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" @@ -2883,11 +2770,6 @@ debug@^3.2.7: dependencies: ms "^2.1.1" -decamelize@^1.1.1: - version "1.2.0" - resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" - integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== - dedent@^0.7.0: version "0.7.0" resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz" @@ -2916,11 +2798,6 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" - integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== - denque@^2.0.1, denque@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz" @@ -2941,11 +2818,6 @@ destroy@1.2.0: resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== -detect-libc@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz" - integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== - detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz" @@ -3659,13 +3531,6 @@ fresh@0.5.2, fresh@^0.5.2: resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== -fs-minipass@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz" - integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== - dependencies: - minipass "^3.0.0" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" @@ -3696,21 +3561,6 @@ functions-have-names@^1.2.2: resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== -gauge@^3.0.0: - version "3.0.2" - resolved "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz" - integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== - dependencies: - aproba "^1.0.3 || ^2.0.0" - color-support "^1.1.2" - console-control-strings "^1.0.0" - has-unicode "^2.0.1" - object-assign "^4.1.1" - signal-exit "^3.0.0" - string-width "^4.2.3" - strip-ansi "^6.0.1" - wide-align "^1.1.2" - generate-function@^2.3.1: version "2.3.1" resolved "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz" @@ -3789,7 +3639,7 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@^7.0.5, glob@^7.1.3, glob@^7.1.4: +glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -3843,11 +3693,6 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -google-protobuf@^3.15.5: - version "3.21.2" - resolved "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.2.tgz" - integrity sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA== - gopd@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz" @@ -3877,39 +3722,6 @@ graphql@^16.3.0: resolved "https://registry.npmjs.org/graphql/-/graphql-16.6.0.tgz" integrity sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw== -grpc-bchrpc-node@^0.15.2: - version "0.15.2" - resolved "https://registry.npmjs.org/grpc-bchrpc-node/-/grpc-bchrpc-node-0.15.2.tgz" - integrity sha512-GgQH0d3eUNAC1HAFt9ia+jyIIJJYZFN3NedSiF6dsx6ixl414fWrfkyMc9MweOUbT89ppazjh+9dVnTD1/zqfw== - dependencies: - "@types/google-protobuf" "^3.7.4" - google-protobuf "^3.15.5" - grpc "^1.24.5" - grpc-bchrpc "0.1.0" - protobufjs "^6.10.2" - -grpc-bchrpc@0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/grpc-bchrpc/-/grpc-bchrpc-0.1.0.tgz" - integrity sha512-XI7qhn0/uq9IA8TZg5ADdG9UmllLMsCJ2XdYVBu2WzpSr3S/o1eFbMm1j/SL1dVD/Z6s4H4g4k5JuFO4GXwO7w== - dependencies: - "@types/google-protobuf" "^3.7.4" - buffer "^5.6.0" - google-protobuf "^3.15.5" - typescript "^4.2.3" - -grpc@^1.24.5: - version "1.24.11" - resolved "https://registry.npmjs.org/grpc/-/grpc-1.24.11.tgz" - integrity sha512-8/AQdFCzCeCDWW3SoaMNp6ccbRvTQEH1O1u1uFtt29eWsg5gSZCJ3m6fbkduEIh3smY7WAPP+LgVJ5n3nZRxcA== - dependencies: - "@mapbox/node-pre-gyp" "^1.0.4" - "@types/bytebuffer" "^5.0.40" - lodash.camelcase "^4.3.0" - lodash.clone "^4.5.0" - nan "^2.13.2" - protobufjs "^5.0.3" - has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz" @@ -3949,11 +3761,6 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" -has-unicode@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" - integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== - has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -4046,7 +3853,7 @@ iconv-lite@^0.6.3: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -ieee754@^1.1.13, ieee754@^1.2.1: +ieee754@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -4114,11 +3921,6 @@ internal-slot@^1.0.4: has "^1.0.3" side-channel "^1.0.4" -invert-kv@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz" - integrity sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ== - ioredis@^5.2.4, ioredis@^5.3.0: version "5.3.1" resolved "https://registry.npmjs.org/ioredis/-/ioredis-5.3.1.tgz" @@ -4199,13 +4001,6 @@ is-extglob@^2.1.1: resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz" - integrity sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw== - dependencies: - number-is-nan "^1.0.0" - is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" @@ -5052,13 +4847,6 @@ kleur@^3.0.3: resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -lcid@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz" - integrity sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw== - dependencies: - invert-kv "^1.0.0" - leven@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" @@ -5140,16 +4928,6 @@ lodash-es@^4.17.15: resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== -lodash.camelcase@^4.3.0: - version "4.3.0" - resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz" - integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== - -lodash.clone@^4.5.0: - version "4.5.0" - resolved "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz" - integrity sha512-GhrVeweiTD6uTmmn5hV/lzgCQhccwReIVRLHp7LT4SopOjqEZ5BbX8b5WWEtAKasjmy8hR7ZPwsYlxRCku5odg== - lodash.defaults@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz" @@ -5200,11 +4978,6 @@ long@^4.0.0: resolved "https://registry.npmjs.org/long/-/long-4.0.0.tgz" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== -long@~3: - version "3.2.0" - resolved "https://registry.npmjs.org/long/-/long-3.2.0.tgz" - integrity sha512-ZYvPPOMqUwPoDsbJaR10iQJYnMuZhRTvHYl62ErLIEX7RgFlziSBUUvrt3OVfc47QlHHpzPZYP17g3Fv7oeJkg== - loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" @@ -5241,7 +5014,7 @@ luxon@^3.2.1: resolved "https://registry.npmjs.org/luxon/-/luxon-3.2.1.tgz" integrity sha512-QrwPArQCNLAKGO/C+ZIilgIuDnEnKx5QYODdDtbFaxzsbZcc/a7WFq7MhsVYgRlwawLtvOUESTlfJ+hc/USqPg== -make-dir@^3.0.0, make-dir@^3.1.0: +make-dir@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -5362,31 +5135,6 @@ minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -minipass@^3.0.0: - version "3.3.6" - resolved "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz" - integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== - dependencies: - yallist "^4.0.0" - -minipass@^4.0.0: - version "4.2.4" - resolved "https://registry.npmjs.org/minipass/-/minipass-4.2.4.tgz" - integrity sha512-lwycX3cBMTvcejsHITUgYj6Gy6A7Nh4Q6h9NP4sTHY1ccJlC7yKzDmiShEHsJ16Jf1nKGDEaiHxiltsJEvk0nQ== - -minizlib@^2.1.1: - version "2.1.2" - resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz" - integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== - dependencies: - minipass "^3.0.0" - yallist "^4.0.0" - -mkdirp@^1.0.3: - version "1.0.4" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - moment@^2.29.4: version "2.29.4" resolved "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz" @@ -5449,11 +5197,6 @@ named-placeholders@^1.1.2: dependencies: lru-cache "^7.14.1" -nan@^2.13.2: - version "2.17.0" - resolved "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz" - integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== - nanoid@^3.3.4: version "3.3.4" resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz" @@ -5570,13 +5313,6 @@ nodemon@^2.0.4: touch "^3.1.0" undefsafe "^2.0.5" -nopt@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz" - integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== - dependencies: - abbrev "1" - nopt@~1.0.10: version "1.0.10" resolved "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz" @@ -5604,21 +5340,6 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" -npmlog@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz" - integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== - dependencies: - are-we-there-yet "^2.0.0" - console-control-strings "^1.1.0" - gauge "^3.0.0" - set-blocking "^2.0.0" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" - integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ== - object-assign@^4, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" @@ -5686,18 +5407,6 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" -optjs@~3.2.2: - version "3.2.2" - resolved "https://registry.npmjs.org/optjs/-/optjs-3.2.2.tgz" - integrity sha512-f8lTJm4LKirX+45xsFhuRNjA4f46QVLQKfGoNH7e2AEWS+24eM4XNH4pQ8Tw2LISCIvbST/wNcLdtgvgcqVaxA== - -os-locale@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz" - integrity sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g== - dependencies: - lcid "^1.0.0" - p-limit@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" @@ -5891,17 +5600,7 @@ prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2, object-assign "^4.1.1" react-is "^16.13.1" -protobufjs@^5.0.3: - version "5.0.3" - resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-5.0.3.tgz" - integrity sha512-55Kcx1MhPZX0zTbVosMQEO5R6/rikNXd9b6RQK4KSPcrSIIwoXTtebIczUrXlwaSrbz4x8XUVThGPob1n8I4QA== - dependencies: - ascli "~1" - bytebuffer "~5" - glob "^7.0.5" - yargs "^3.10.0" - -protobufjs@^6.10.2, protobufjs@^6.8.8: +protobufjs@^6.8.8: version "6.11.3" resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz" integrity sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg== @@ -6375,11 +6074,6 @@ serve-static@1.15.0: parseurl "~1.3.3" send "0.18.0" -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" - integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== - setprototypeof@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" @@ -6424,7 +6118,7 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: +signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -6606,16 +6300,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz" - integrity sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw== - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -6658,13 +6343,6 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" - integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== - dependencies: - ansi-regex "^2.0.0" - strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" @@ -6795,18 +6473,6 @@ symbol-observable@^1.2.0: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== -tar@^6.1.11: - version "6.1.13" - resolved "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz" - integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw== - dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^4.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" - terminal-link@^2.0.0: version "2.1.1" resolved "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz" @@ -7024,7 +6690,7 @@ typeforce@^1.11.3: resolved "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz" integrity sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g== -typescript@^4.2.3, typescript@^4.6.4: +typescript@^4.6.4: version "4.9.5" resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== @@ -7173,13 +6839,6 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -wide-align@^1.1.2: - version "1.1.5" - resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz" - integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== - dependencies: - string-width "^1.0.2 || 2 || 3 || 4" - wif@^2.0.1: version "2.0.6" resolved "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz" @@ -7187,24 +6846,11 @@ wif@^2.0.1: dependencies: bs58check "<3.0.0" -window-size@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz" - integrity sha512-2thx4pB0cV3h+Bw7QmMXcEbdmOzv9t0HFplJH/Lz6yu60hXYy5RT8rUu+wlIreVxWsGN20mo+MHeCSfUpQBwPw== - word-wrap@^1.2.3: version "1.2.3" resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz" - integrity sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw== - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz" @@ -7274,11 +6920,6 @@ xss@^1.0.8: commander "^2.20.3" cssfilter "0.0.10" -y18n@^3.2.0: - version "3.2.2" - resolved "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz" - integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ== - y18n@^5.0.5: version "5.0.8" resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" @@ -7317,19 +6958,6 @@ yargs@^17.3.1: y18n "^5.0.5" yargs-parser "^21.1.1" -yargs@^3.10.0: - version "3.32.0" - resolved "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz" - integrity sha512-ONJZiimStfZzhKamYvR/xvmgW3uEkAUFSP91y2caTEPhzF6uP2JfPiVZcq66b/YR0C3uitxSV7+T1x8p5bkmMg== - dependencies: - camelcase "^2.0.1" - cliui "^3.0.3" - decamelize "^1.1.1" - os-locale "^1.4.0" - string-width "^1.0.1" - window-size "^0.1.4" - y18n "^3.2.0" - yn@3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" From 1dcd62ed8195ed834b972338adbfa1d8234ca8e6 Mon Sep 17 00:00:00 2001 From: chedieck Date: Fri, 2 Aug 2024 19:18:43 -0300 Subject: [PATCH 03/16] fix: multinetwork chronik client & admin view fix: multinetwork chronik client & admin view --- constants/index.ts | 6 +++ pages/admin/index.tsx | 25 +++++------ pages/api/chronikStatus/index.ts | 4 +- services/blockchainService.ts | 30 +++++++++---- services/chronikService.ts | 66 +++++++++++++++-------------- tests/unittests/grpcService.test.ts | 32 -------------- 6 files changed, 75 insertions(+), 88 deletions(-) delete mode 100644 tests/unittests/grpcService.test.ts diff --git a/constants/index.ts b/constants/index.ts index 3a33d6e75..6978423fa 100644 --- a/constants/index.ts +++ b/constants/index.ts @@ -93,11 +93,17 @@ export const NETWORK_SLUGS: KeyValueT = { bchtest: 'bchtest', bchreg: 'bchreg' } + export const NETWORK_IDS_FROM_SLUGS: KeyValueT = { ecash: 1, bitcoincash: 2 } +export const NETWORK_SLUGS_FROM_IDS: Record = { + 1: 'ecash', + 2: 'bitcoincash' +} + // When fetching some address transactions, number of transactions to fetch at a time. // On chronik, the max allowed is 200 export const FETCH_N = 200 diff --git a/pages/admin/index.tsx b/pages/admin/index.tsx index b0ee7cdda..d9f288f3b 100644 --- a/pages/admin/index.tsx +++ b/pages/admin/index.tsx @@ -49,9 +49,8 @@ interface IProps { export default function Admin ({ user, isAdmin }: IProps): JSX.Element { const router = useRouter() - const [subbedAddresses, setSubbedAddresses] = useState([]) - const [currentAddresses, setCurrentAddresses] = useState([]) - const [different, setDifferent] = useState(false) + const [ecashSubscribedAddresses, setEcashSubscribedAddresses] = useState([]) + const [bitcoincashSubscribedAddresses, setBitcoincashSubscribedAddresses] = useState([]) const [users, setUsers] = useState([]) useEffect(() => { @@ -63,11 +62,11 @@ export default function Admin ({ user, isAdmin }: IProps): JSX.Element { useEffect(() => { void (async () => { const ok = await (await fetch('chronikStatus')).json() - const subbedAddressesTableData = ok.registeredSubscriptions.map((value) => ({ address: value })) - const currentAddressesTableData = ok.currentSubscriptions.map((value) => ({ address: value })) - setSubbedAddresses(subbedAddressesTableData) - setCurrentAddresses(currentAddressesTableData) - setDifferent(ok?.different) + console.log('ok', ok) // WIP + const subscribedEcashAddresses = ok.ecash.map((value: string) => ({ address: value })) + const subscribedBitcoincashAddresses = ok.bitcoincash.map((value: string) => ({ address: value })) + setEcashSubscribedAddresses(subscribedEcashAddresses) + setBitcoincashSubscribedAddresses(subscribedBitcoincashAddresses) const ok2 = await (await fetch('/api/users')).json() setUsers(ok2) })() @@ -101,12 +100,10 @@ export default function Admin ({ user, isAdmin }: IProps): JSX.Element { return <>

Admin Dashboard

- - { different && <> -

Warning!
The subscribed addresses registered since the beginning of the last deploy (list above) is different than the addresses being read by the chronik object instance (list below).

- - - } +

Ecash

+ +

Bitcoin Cash

+ => { if (user.isAdmin !== true) { throw new Error('unauthorised') } - res.status(200).json(getSubbedAddresses()) + res.status(200).json(getAllSubscribedAddresses()) } catch (err: any) { switch (err.message) { default: diff --git a/services/blockchainService.ts b/services/blockchainService.ts index c823002a9..37a34945e 100644 --- a/services/blockchainService.ts +++ b/services/blockchainService.ts @@ -51,19 +51,33 @@ export interface BlockchainClient { subscribeAddresses: (addresses: Address[]) => Promise } +interface NetworkClients{ + ecash?: ChronikBlockchainClient + bitcoincash?: ChronikBlockchainClient +} + +export type Networks = 'ecash' | 'bitcoincash' + export interface NodeJsGlobalChronik extends NodeJS.Global { - chronik: ChronikBlockchainClient + chronik?: NetworkClients } declare const global: NodeJsGlobalChronik -function getBlockchainClient (networkSlug: string): BlockchainClient { +function getBlockchainClient (networkSlug: Networks): BlockchainClient { if (!Object.keys(config.networkBlockchainClients).includes(networkSlug)) { throw new Error(RESPONSE_MESSAGES.MISSING_BLOCKCHAIN_CLIENT_400.message) } switch (config.networkBlockchainClients[networkSlug]) { case 'chronik' as BlockchainClientOptions: - if (global.chronik === undefined) { - console.log('creating chronik instance...') - global.chronik = new ChronikBlockchainClient(networkSlug) + if (global.chronik === undefined || global.chronik[networkSlug] === undefined) { + console.log('creating chronik client for ', networkSlug) + const newClient = new ChronikBlockchainClient(networkSlug) + if (global.chronik === undefined) { + global.chronik = { + [networkSlug]: newClient + } + } else { + global.chronik[networkSlug] = newClient + } // Subscribe addresses & Sync lost transactions on DB upon client initialization if ( process.env.NEXT_PHASE !== PHASE_PRODUCTION_BUILD && @@ -71,12 +85,12 @@ function getBlockchainClient (networkSlug: string): BlockchainClient { process.env.JOBS_ENV === undefined ) { console.log('subscribing existent addresses...') - void global.chronik.subscribeInitialAddresses() + void newClient.subscribeInitialAddresses() console.log('syncing missed transactions...') - void global.chronik.syncMissedTransactions() + void newClient.syncMissedTransactions() } } - return global.chronik + return global.chronik[networkSlug] as BlockchainClient default: throw new Error(RESPONSE_MESSAGES.NO_BLOCKCHAIN_CLIENT_INSTANTIATED_400.message) } diff --git a/services/chronikService.ts b/services/chronikService.ts index 958872b13..02d044fd3 100644 --- a/services/chronikService.ts +++ b/services/chronikService.ts @@ -1,8 +1,8 @@ import { BlockInfo_InNode, ChronikClientNode, ScriptType_InNode, ScriptUtxo_InNode, Tx_InNode, WsConfig_InNode, WsEndpoint_InNode, WsMsgClient, WsSubScriptClient } from 'chronik-client' import { encode, decode } from 'ecashaddrjs' import bs58 from 'bs58' -import { AddressWithTransaction, BlockchainClient, BlockchainInfo, BlockInfo, NodeJsGlobalChronik, TransactionDetails } from './blockchainService' -import { NETWORK_SLUGS, CHRONIK_MESSAGE_CACHE_DELAY, RESPONSE_MESSAGES, XEC_TIMESTAMP_THRESHOLD, XEC_NETWORK_ID, BCH_NETWORK_ID, BCH_TIMESTAMP_THRESHOLD, FETCH_DELAY, FETCH_N, KeyValueT } from 'constants/index' +import { AddressWithTransaction, BlockchainClient, BlockchainInfo, BlockInfo, NodeJsGlobalChronik, TransactionDetails, Networks } from './blockchainService' +import { CHRONIK_MESSAGE_CACHE_DELAY, RESPONSE_MESSAGES, XEC_TIMESTAMP_THRESHOLD, XEC_NETWORK_ID, BCH_NETWORK_ID, BCH_TIMESTAMP_THRESHOLD, FETCH_DELAY, FETCH_N, KeyValueT, NETWORK_IDS_FROM_SLUGS, NETWORK_SLUGS_FROM_IDS } from 'constants/index' import { TransactionWithAddressAndPrices, createManyTransactions, @@ -109,8 +109,7 @@ export function getNullDataScriptData (outputScript: string): OpReturnData | nul export class ChronikBlockchainClient implements BlockchainClient { chronik: ChronikClientNode - availableNetworks: string[] - subscribedAddresses: KeyValueT
+ networkId: number chronikWSEndpoint: WsEndpoint_InNode wsEndpoint: Socket lastProcessedMessages: ProcessedMessages @@ -120,9 +119,8 @@ export class ChronikBlockchainClient implements BlockchainClient { throw new Error(RESPONSE_MESSAGES.MISSING_WS_AUTH_KEY_400.message) } + this.networkId = NETWORK_IDS_FROM_SLUGS[networkSlug] this.chronik = new ChronikClientNode([config.networkBlockchainURLs[networkSlug]]) - this.availableNetworks = [NETWORK_SLUGS.ecash, NETWORK_SLUGS.bitcoincash] - this.subscribedAddresses = {} this.chronikWSEndpoint = this.chronik.ws(this.getWsConfig()) void this.chronikWSEndpoint.waitForOpen() this.chronikWSEndpoint.subscribeToBlocks() @@ -152,6 +150,12 @@ export class ChronikBlockchainClient implements BlockchainClient { } } + public getSubscribedAddresses (): string[] { + const ret = this.chronikWSEndpoint.subs.scripts.map((script: WsSubScriptClient) => fromHash160(script.scriptType, script.payload)) + console.log('IMP reting', ret, 'for', this.networkId) + return ret + } + private isAlreadyBeingProcessed (txid: string, confirmed: boolean): boolean { this.clearOldMessages() if (confirmed) { @@ -172,7 +176,7 @@ export class ChronikBlockchainClient implements BlockchainClient { } private validateNetwork (networkSlug: string): void { - if (!this.availableNetworks.includes(networkSlug)) { throw new Error(RESPONSE_MESSAGES.INVALID_NETWORK_SLUG_400.message) } + if (NETWORK_IDS_FROM_SLUGS[networkSlug] !== this.networkId) { throw new Error(RESPONSE_MESSAGES.INVALID_NETWORK_SLUG_400.message) } } async getBlockchainInfo (networkSlug: string): Promise { @@ -277,7 +281,7 @@ export class ChronikBlockchainClient implements BlockchainClient { const persistedTransactions = await createManyTransactions(transactionsToPersist) const simplifiedTransactions = getSimplifiedTransactions(persistedTransactions) - console.log(`[CHRONIK]: added ${simplifiedTransactions.length} txs to ${addressString}`) + console.log(`[CHRONIK ${this.networkId}]: added ${simplifiedTransactions.length} txs to ${addressString}`) const broadcastTxData: BroadcastTxData = {} as BroadcastTxData broadcastTxData.messageType = 'OldTx' @@ -399,7 +403,7 @@ export class ChronikBlockchainClient implements BlockchainClient { } private async getAddressesForTransaction (transaction: Tx_InNode): Promise { - const addressesWithTransactions: AddressWithTransaction[] = await Promise.all(Object.values(this.subscribedAddresses).map( + const addressesWithTransactions: AddressWithTransaction[] = await Promise.all(Object.values(this.getSubscribedAddresses()).map( async address => { return { address, @@ -416,26 +420,31 @@ export class ChronikBlockchainClient implements BlockchainClient { public async subscribeAddresses (addresses: Address[]): Promise { if (addresses.length === 0) return - const addressesAlreadySubscribed = addresses.filter(address => Object.keys(this.subscribedAddresses).includes(address.address)) + const addressesAlreadySubscribed = addresses.filter(address => Object.keys(this.getSubscribedAddresses()).includes(address.address)) addressesAlreadySubscribed.forEach(address => { console.warn(`[CHRONIK]: address already subscribed: ${address.address}`) }) if (addressesAlreadySubscribed.length === addresses.length) return addresses = addresses.filter(address => !addressesAlreadySubscribed.includes(address)) - const addressesByNetwork: KeyValueT = groupAddressesByNetwork(this.availableNetworks, addresses) + const addressesByNetwork: KeyValueT = groupAddressesByNetwork([NETWORK_SLUGS_FROM_IDS[this.networkId]], addresses) + if (this.networkId === 2) { // WIP + console.log('ANTES', this.getSubscribedAddresses()) + } for (const [, networkAddresses] of Object.entries(addressesByNetwork)) { networkAddresses.forEach(address => { console.log('[CHRONIK]: subscribing ', address.address) this.chronikWSEndpoint.subscribeToAddress(address.address) - this.subscribedAddresses[address.address] = address }) } + if (this.networkId === 2) { // WIP + console.log('DPS', this.getSubscribedAddresses()) + } } public async syncMissedTransactions (): Promise { - const addresses = await fetchAllAddressesForNetworkId(XEC_NETWORK_ID) + const addresses = await fetchAllAddressesForNetworkId(this.networkId) try { const { failedAddressesWithErrors, successfulAddressesWithCount } = await syncAddresses(addresses) Object.keys(failedAddressesWithErrors).forEach((addr) => { @@ -450,7 +459,7 @@ export class ChronikBlockchainClient implements BlockchainClient { } public async subscribeInitialAddresses (): Promise { - const addresses = await fetchAllAddressesForNetworkId(XEC_NETWORK_ID) + const addresses = await fetchAllAddressesForNetworkId(this.networkId) try { await this.subscribeAddresses(addresses) } catch (err: any) { @@ -522,26 +531,19 @@ export function outputScriptToAddress (outputScript: string | undefined): string } export interface SubbedAddressesLog { - registeredSubscriptions: string[] - currentSubscriptions: string[] - different: boolean + [k: string]: string[] } -export function getSubbedAddresses (): SubbedAddressesLog { - const chronik = (global as unknown as NodeJsGlobalChronik).chronik +export function getAllSubscribedAddresses (): SubbedAddressesLog { + const chronikClients = (global as unknown as NodeJsGlobalChronik).chronik + if (chronikClients === undefined) { + throw new Error('No chronik client instantiated to see subscribed addresses') + } const ret = {} as any - ret.registeredSubscriptions = Object.keys(chronik.subscribedAddresses) - ret.currentSubscriptions = chronik.chronikWSEndpoint.subs.scripts.map((script: WsSubScriptClient) => fromHash160(script.scriptType, script.payload)) - ret.different = currentSubscriptionsDifferentThanRegistered(ret.registeredSubscriptions, ret.currentSubscriptions) - return ret -} - -function currentSubscriptionsDifferentThanRegistered (registeredSubscriptions: string[], currentSubscriptions: string[]): boolean { - if (registeredSubscriptions.length !== currentSubscriptions.length) return true - - for (let i = 0; i < registeredSubscriptions.length; i++) { - if (registeredSubscriptions[i] !== currentSubscriptions[i]) return true + // chronik?.ecash?.chronikWSEndpoint.subs + for (const key of Object.keys(chronikClients)) { + ret[key] = chronikClients[key as Networks]?.getSubscribedAddresses() + console.log('jsut set key', key, 'to', ret[key]) } - - return false + return ret } diff --git a/tests/unittests/grpcService.test.ts b/tests/unittests/grpcService.test.ts deleted file mode 100644 index 36b73bbdc..000000000 --- a/tests/unittests/grpcService.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { mockedBCHAddress } from '../mockedObjects' -import { getBalance, getTransactionDetails } from '../../services/blockchainService' -import { NETWORK_SLUGS } from 'constants/index' - -describe('Test service returned objects consistency', () => { - // WIP - /* - it('test getAddress for real address', async () => { - const res = await getAddressTransactions({ address: mockedBCHAddress.address }) - expect(res).toEqual(expect.objectContaining({ - confirmedTransactionsList: [ - mockedGrpc.transaction1.toObject(), - mockedGrpc.transaction2.toObject() - ] - })) - }) - */ - it('test getBalance', async () => { - const res = await getBalance(mockedBCHAddress.address) - expect(res).toBe(1780) - }) - it('test getTransactionDetails', async () => { - const res = await getTransactionDetails(mockedBCHAddress.address, NETWORK_SLUGS.bitcoincash) - expect(res).toEqual({ - hash: 'hu9m3BZg/zlxis7ehc0x/+9qELXC8dkbimOtc5v598s=', - version: 2, - block: { hash: '', height: 0, timestamp: '1653653100' }, - inputs: [], - outputs: [] - }) - }) -}) From e4a19303c216e1ece3e8e5d08f099c7d041fb3b6 Mon Sep 17 00:00:00 2001 From: chedieck Date: Mon, 5 Aug 2024 10:45:23 -0300 Subject: [PATCH 04/16] chore: update chronik --- package.json | 7 +++++-- yarn.lock | 18 +++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index f7e40af5a..387ec0b3e 100644 --- a/package.json +++ b/package.json @@ -31,11 +31,11 @@ "bitcoinjs-lib": "^6.0.2", "bs58": "^5.0.0", "chart.js": "^3.8.0", - "chronik-client": "^0.28.0", + "chronik-client": "^0.28.1", "cors": "^2.8.5", "cross-env": "^7.0.2", "dotenv-cli": "^5.1.0", - "ecashaddrjs": "^1.0.7", + "ecashaddrjs": "^1.5.8", "express": "^4.17.1", "graphql": "^16.3.0", "helmet": "^4.4.1", @@ -85,6 +85,9 @@ "tsx": "^3.12.1", "typescript": "^4.6.4" }, + "resolutions": { + "chronik-client/ecashaddrjs": "^1.5.8" + }, "lint-staged": { "*.ts?(x)": [ "yarn eslint --fix" diff --git a/yarn.lock b/yarn.lock index 21e0431f7..dd8499b17 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2455,14 +2455,14 @@ chokidar@^3.5.2: optionalDependencies: fsevents "~2.3.2" -chronik-client@^0.28.0: - version "0.28.0" - resolved "https://registry.yarnpkg.com/chronik-client/-/chronik-client-0.28.0.tgz#4a194a91ec9b26da19435b2ca8aca6f929fb8ac1" - integrity sha512-Wx3cz6+dKXQs5zvP2Qcsrwas/pK0gs+1L0eyXuXpr3JHSd24+nRyBNkY0Zi9LdMsQkvhGSTifTDyCN5/qLN25w== +chronik-client@^0.28.1: + version "0.28.1" + resolved "https://registry.yarnpkg.com/chronik-client/-/chronik-client-0.28.1.tgz#475c36f61db0458e88fbd509a0ff940eb3df2f2e" + integrity sha512-mOuThrTH6JfZBlXs0B9tD9IvNTa5h4K4ehx8xi7Xl0rAvtEdbVEmfIFLeER02DvXKkMnfuEA/v4M/jxQLWnptw== dependencies: "@types/ws" "^8.2.1" axios "^1.6.3" - ecashaddrjs "^1.5.6" + ecashaddrjs "file:../.cache/yarn/v6/npm-chronik-client-0.28.1-475c36f61db0458e88fbd509a0ff940eb3df2f2e-integrity/node_modules/ecashaddrjs" isomorphic-ws "^4.0.1" protobufjs "^6.8.8" ws "^8.3.0" @@ -2904,10 +2904,10 @@ ecashaddrjs@^1.0.7: dependencies: big-integer "1.6.36" -ecashaddrjs@^1.5.6: - version "1.5.7" - resolved "https://registry.yarnpkg.com/ecashaddrjs/-/ecashaddrjs-1.5.7.tgz#67e7c0c6a474727f48280522ab0fd066f1f58452" - integrity sha512-KEsh2bbKS+7PEngrbcMVT4YeYRXR12LFFdmXv/ZSAgJCjoHBs8f80Akl8JgAYK27+hpuMXBESz5IurUbCdflDw== +ecashaddrjs@^1.5.8, "ecashaddrjs@file:../../../.cache/yarn/v6/npm-chronik-client-0.28.1-475c36f61db0458e88fbd509a0ff940eb3df2f2e-integrity/node_modules/ecashaddrjs": + version "1.5.8" + resolved "https://registry.yarnpkg.com/ecashaddrjs/-/ecashaddrjs-1.5.8.tgz#fff684d14a944cfd4bca56dd8dc3adbfce8016f9" + integrity sha512-EBL052a1Hd1+wwaBQr3Q+jXY4XfH0jXR1tK360P8GQ9YJUZ5zLG+0I7yee8/QgHY04zdoCzDOY0fRlwqtqDhRg== dependencies: big-integer "1.6.36" bs58check "^3.0.1" From bc5a15778e30b1f33f7b5b1d4fa6431c8bfc569a Mon Sep 17 00:00:00 2001 From: chedieck Date: Wed, 7 Aug 2024 19:58:39 -0300 Subject: [PATCH 05/16] fix: prefix & log msgs --- services/chronikService.ts | 42 ++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/services/chronikService.ts b/services/chronikService.ts index 02d044fd3..57d2c019f 100644 --- a/services/chronikService.ts +++ b/services/chronikService.ts @@ -112,6 +112,7 @@ export class ChronikBlockchainClient implements BlockchainClient { networkId: number chronikWSEndpoint: WsEndpoint_InNode wsEndpoint: Socket + CHRONIK_MSG_PREFIX: string lastProcessedMessages: ProcessedMessages constructor (networkSlug: string) { @@ -125,6 +126,7 @@ export class ChronikBlockchainClient implements BlockchainClient { void this.chronikWSEndpoint.waitForOpen() this.chronikWSEndpoint.subscribeToBlocks() this.lastProcessedMessages = { confirmed: {}, unconfirmed: {} } + this.CHRONIK_MSG_PREFIX = `[CHRONIK ${networkSlug}]` this.wsEndpoint = io(`${config.wsBaseURL}/broadcast`, { query: { key: process.env.WS_AUTH_KEY @@ -152,8 +154,7 @@ export class ChronikBlockchainClient implements BlockchainClient { public getSubscribedAddresses (): string[] { const ret = this.chronikWSEndpoint.subs.scripts.map((script: WsSubScriptClient) => fromHash160(script.scriptType, script.payload)) - console.log('IMP reting', ret, 'for', this.networkId) - return ret + return [...new Set(ret)] } private isAlreadyBeingProcessed (txid: string, confirmed: boolean): boolean { @@ -281,7 +282,7 @@ export class ChronikBlockchainClient implements BlockchainClient { const persistedTransactions = await createManyTransactions(transactionsToPersist) const simplifiedTransactions = getSimplifiedTransactions(persistedTransactions) - console.log(`[CHRONIK ${this.networkId}]: added ${simplifiedTransactions.length} txs to ${addressString}`) + console.log(`${this.CHRONIK_MSG_PREFIX}: added ${simplifiedTransactions.length} txs to ${addressString}`) const broadcastTxData: BroadcastTxData = {} as BroadcastTxData broadcastTxData.messageType = 'OldTx' @@ -341,10 +342,10 @@ export class ChronikBlockchainClient implements BlockchainClient { private getWsConfig (): WsConfig_InNode { return { onMessage: (msg: WsMsgClient) => { void this.processWsMessage(msg) }, - onError: (e: ws.ErrorEvent) => { console.log(`[CHRONIK]: Chronik webSocket error, type: ${e.type} | message: ${e.message} | error: ${e.error as string}`) }, - onReconnect: (_: ws.Event) => { console.log('[CHRONIK]: Chronik webSocket unexpectedly closed.') }, - onConnect: (_: ws.Event) => { console.log('[CHRONIK]: Chronik webSocket connection (re)established.') }, - onEnd: (e: ws.Event) => { console.log(`[CHRONIK]: Chronik WebSocket ended, type: ${e.type}.`) }, + onError: (e: ws.ErrorEvent) => { console.log(`${this.CHRONIK_MSG_PREFIX}: Chronik webSocket error, type: ${e.type} | message: ${e.message} | error: ${e.error as string}`) }, + onReconnect: (_: ws.Event) => { console.log(`${this.CHRONIK_MSG_PREFIX}: Chronik webSocket unexpectedly closed.`) }, + onConnect: (_: ws.Event) => { console.log(`${this.CHRONIK_MSG_PREFIX}: Chronik webSocket connection (re)established.`) }, + onEnd: (e: ws.Event) => { console.log(`${this.CHRONIK_MSG_PREFIX}: Chronik WebSocket ended, type: ${e.type}.`) }, autoReconnect: true } } @@ -354,7 +355,7 @@ export class ChronikBlockchainClient implements BlockchainClient { // if they were cancelled and not confirmed if (msg.type === 'Tx') { if (msg.msgType === 'TX_REMOVED_FROM_MEMPOOL') { - console.log(`[CHRONIK]: [${msg.type}] ${msg.txid}`) + console.log(`${this.CHRONIK_MSG_PREFIX}: [${msg.type}] ${msg.txid}`) const transactionsToDelete = await fetchUnconfirmedTransactions(msg.txid) try { await deleteTransactions(transactionsToDelete) @@ -369,7 +370,7 @@ export class ChronikBlockchainClient implements BlockchainClient { if (this.isAlreadyBeingProcessed(msg.txid, msg.msgType === 'TX_CONFIRMED')) { return } - console.log(`[CHRONIK]: [${msg.type}] ${msg.txid}`) + console.log(`${this.CHRONIK_MSG_PREFIX}: [${msg.type}] ${msg.txid}`) const transaction = await this.chronik.tx(msg.txid) const addressesWithTransactions = await this.getAddressesForTransaction(transaction) for (const addressWithTransaction of addressesWithTransactions) { @@ -396,9 +397,9 @@ export class ChronikBlockchainClient implements BlockchainClient { } } } else if (msg.type === 'Block') { - console.log(`[CHRONIK]: [${msg.type}] ${msg.msgType} Height: ${msg.blockHeight} Hash: ${msg.blockHash}`) + console.log(`${this.CHRONIK_MSG_PREFIX}: [${msg.type}] ${msg.msgType} Height: ${msg.blockHeight} Hash: ${msg.blockHash}`) } else if (msg.type === 'Error') { - console.log(`[CHRONIK]: [${msg.type}] ${JSON.stringify(msg.msg)}`) + console.log(`${this.CHRONIK_MSG_PREFIX}: [${msg.type}] ${JSON.stringify(msg.msg)}`) } } @@ -422,25 +423,19 @@ export class ChronikBlockchainClient implements BlockchainClient { const addressesAlreadySubscribed = addresses.filter(address => Object.keys(this.getSubscribedAddresses()).includes(address.address)) addressesAlreadySubscribed.forEach(address => { - console.warn(`[CHRONIK]: address already subscribed: ${address.address}`) + console.warn(`${this.CHRONIK_MSG_PREFIX}: address already subscribed: ${address.address}`) }) if (addressesAlreadySubscribed.length === addresses.length) return addresses = addresses.filter(address => !addressesAlreadySubscribed.includes(address)) const addressesByNetwork: KeyValueT = groupAddressesByNetwork([NETWORK_SLUGS_FROM_IDS[this.networkId]], addresses) - if (this.networkId === 2) { // WIP - console.log('ANTES', this.getSubscribedAddresses()) - } for (const [, networkAddresses] of Object.entries(addressesByNetwork)) { networkAddresses.forEach(address => { - console.log('[CHRONIK]: subscribing ', address.address) + console.log(`${this.CHRONIK_MSG_PREFIX}: subscribing `, address.address) this.chronikWSEndpoint.subscribeToAddress(address.address) }) } - if (this.networkId === 2) { // WIP - console.log('DPS', this.getSubscribedAddresses()) - } } public async syncMissedTransactions (): Promise { @@ -448,13 +443,13 @@ export class ChronikBlockchainClient implements BlockchainClient { try { const { failedAddressesWithErrors, successfulAddressesWithCount } = await syncAddresses(addresses) Object.keys(failedAddressesWithErrors).forEach((addr) => { - console.error(`[CHRONIK]: When syncing missing addresses for address ${addr} encountered error: ${failedAddressesWithErrors[addr]}`) + console.error(`${this.CHRONIK_MSG_PREFIX}: When syncing missing addresses for address ${addr} encountered error: ${failedAddressesWithErrors[addr]}`) }) Object.keys(successfulAddressesWithCount).forEach((addr) => { - console.log(`[CHRONIK]: Successful synced ${successfulAddressesWithCount[addr]} txs for ${addr}`) + console.log(`${this.CHRONIK_MSG_PREFIX}: Successful synced ${successfulAddressesWithCount[addr]} txs for ${addr}`) }) } catch (err: any) { - console.error(`[CHRONIK]: ERROR: (skipping anyway) initial missing transactions sync failed: ${err.message as string} ${err.stack as string}`) + console.error(`${this.CHRONIK_MSG_PREFIX}: ERROR: (skipping anyway) initial missing transactions sync failed: ${err.message as string} ${err.stack as string}`) } } @@ -463,7 +458,7 @@ export class ChronikBlockchainClient implements BlockchainClient { try { await this.subscribeAddresses(addresses) } catch (err: any) { - console.error(`[CHRONIK]: ERROR: (skipping anyway) initial chronik subscription failed: ${err.message as string} ${err.stack as string}`) + console.error(`${this.CHRONIK_MSG_PREFIX}: ERROR: (skipping anyway) initial chronik subscription failed: ${err.message as string} ${err.stack as string}`) } } } @@ -543,7 +538,6 @@ export function getAllSubscribedAddresses (): SubbedAddressesLog { // chronik?.ecash?.chronikWSEndpoint.subs for (const key of Object.keys(chronikClients)) { ret[key] = chronikClients[key as Networks]?.getSubscribedAddresses() - console.log('jsut set key', key, 'to', ret[key]) } return ret } From ad7bbd68aa0a268fd9593a531506effe1625af6f Mon Sep 17 00:00:00 2001 From: chedieck Date: Thu, 8 Aug 2024 08:02:08 -0300 Subject: [PATCH 06/16] fix: ecash hardcoded, better log messages --- services/chronikService.ts | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/services/chronikService.ts b/services/chronikService.ts index 57d2c019f..351807279 100644 --- a/services/chronikService.ts +++ b/services/chronikService.ts @@ -110,6 +110,7 @@ export function getNullDataScriptData (outputScript: string): OpReturnData | nul export class ChronikBlockchainClient implements BlockchainClient { chronik: ChronikClientNode networkId: number + networkSlug: string chronikWSEndpoint: WsEndpoint_InNode wsEndpoint: Socket CHRONIK_MSG_PREFIX: string @@ -120,6 +121,7 @@ export class ChronikBlockchainClient implements BlockchainClient { throw new Error(RESPONSE_MESSAGES.MISSING_WS_AUTH_KEY_400.message) } + this.networkSlug = networkSlug this.networkId = NETWORK_IDS_FROM_SLUGS[networkSlug] this.chronik = new ChronikClientNode([config.networkBlockchainURLs[networkSlug]]) this.chronikWSEndpoint = this.chronik.ws(this.getWsConfig()) @@ -153,7 +155,7 @@ export class ChronikBlockchainClient implements BlockchainClient { } public getSubscribedAddresses (): string[] { - const ret = this.chronikWSEndpoint.subs.scripts.map((script: WsSubScriptClient) => fromHash160(script.scriptType, script.payload)) + const ret = this.chronikWSEndpoint.subs.scripts.map((script: WsSubScriptClient) => fromHash160(this.networkSlug, script.scriptType, script.payload)) return [...new Set(ret)] } @@ -327,13 +329,13 @@ export class ChronikBlockchainClient implements BlockchainClient { for (const input of tx.inputs) { details.inputs.push({ value: new Prisma.Decimal(input.value), - address: outputScriptToAddress(input.outputScript) + address: outputScriptToAddress(this.networkSlug, input.outputScript) }) } for (const output of tx.outputs) { details.outputs.push({ value: new Prisma.Decimal(output.value), - address: outputScriptToAddress(output.outputScript) + address: outputScriptToAddress(this.networkSlug, output.outputScript) }) } return details @@ -355,7 +357,7 @@ export class ChronikBlockchainClient implements BlockchainClient { // if they were cancelled and not confirmed if (msg.type === 'Tx') { if (msg.msgType === 'TX_REMOVED_FROM_MEMPOOL') { - console.log(`${this.CHRONIK_MSG_PREFIX}: [${msg.type}] ${msg.txid}`) + console.log(`${this.CHRONIK_MSG_PREFIX}: [${msg.msgType}] ${msg.txid}`) const transactionsToDelete = await fetchUnconfirmedTransactions(msg.txid) try { await deleteTransactions(transactionsToDelete) @@ -370,7 +372,7 @@ export class ChronikBlockchainClient implements BlockchainClient { if (this.isAlreadyBeingProcessed(msg.txid, msg.msgType === 'TX_CONFIRMED')) { return } - console.log(`${this.CHRONIK_MSG_PREFIX}: [${msg.type}] ${msg.txid}`) + console.log(`${this.CHRONIK_MSG_PREFIX}: [${msg.msgType}] ${msg.txid}`) const transaction = await this.chronik.tx(msg.txid) const addressesWithTransactions = await this.getAddressesForTransaction(transaction) for (const addressWithTransaction of addressesWithTransactions) { @@ -397,7 +399,7 @@ export class ChronikBlockchainClient implements BlockchainClient { } } } else if (msg.type === 'Block') { - console.log(`${this.CHRONIK_MSG_PREFIX}: [${msg.type}] ${msg.msgType} Height: ${msg.blockHeight} Hash: ${msg.blockHash}`) + console.log(`${this.CHRONIK_MSG_PREFIX}: [${msg.msgType}] ${msg.msgType} Height: ${msg.blockHeight} Hash: ${msg.blockHash}`) } else if (msg.type === 'Error') { console.log(`${this.CHRONIK_MSG_PREFIX}: [${msg.type}] ${JSON.stringify(msg.msg)}`) } @@ -463,7 +465,7 @@ export class ChronikBlockchainClient implements BlockchainClient { } } -export function fromHash160 (type: string, hash160: string): string { +export function fromHash160 (networkSlug: string, type: string, hash160: string): string { const buffer = Buffer.from(hash160, 'hex') // Because ecashaddrjs only accepts Uint8Array as input type, convert @@ -474,7 +476,7 @@ export function fromHash160 (type: string, hash160: string): string { } return encode( - 'ecash', + networkSlug, type.toUpperCase(), hash160Uint8Array ) @@ -495,7 +497,7 @@ export function toHash160 (address: string): {type: ScriptType_InNode, hash160: } // returns P2SH (type 76a914...88ac) or P2PKH (type a914...87) address -export function outputScriptToAddress (outputScript: string | undefined): string | undefined { +export function outputScriptToAddress (networkSlug: string, outputScript: string | undefined): string | undefined { if (outputScript === undefined) return undefined const typeTestSlice = outputScript.slice(0, 4) @@ -522,7 +524,7 @@ export function outputScriptToAddress (outputScript: string | undefined): string if (hash160.length !== 40) return undefined - return fromHash160(addressType, hash160) + return fromHash160(networkSlug, addressType, hash160) } export interface SubbedAddressesLog { From da11b2557d4ef52441004b5fd7fbf2346c9dd915 Mon Sep 17 00:00:00 2001 From: chedieck Date: Thu, 8 Aug 2024 08:25:24 -0300 Subject: [PATCH 07/16] fix: address obj instead of string --- services/addressService.ts | 4 ++-- services/chronikService.ts | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/services/addressService.ts b/services/addressService.ts index ea91fea81..e17d9a65e 100644 --- a/services/addressService.ts +++ b/services/addressService.ts @@ -184,7 +184,7 @@ export async function fetchAllAddressesForNetworkId (networkId: number): Promise }) } -export async function fetchAddressesInList (prefixedAddressList: string[]): Promise { +export async function fetchAddressesArray (prefixedAddressList: string[]): Promise { return await prisma.address.findMany({ where: { address: { @@ -321,4 +321,4 @@ export async function fetchAddressesByPaybuttonId(paybuttonId: string): Promise< } return addressesIds -} \ No newline at end of file +} diff --git a/services/chronikService.ts b/services/chronikService.ts index 351807279..697b9d148 100644 --- a/services/chronikService.ts +++ b/services/chronikService.ts @@ -16,7 +16,7 @@ import { import { Address, Prisma } from '@prisma/client' import xecaddr from 'xecaddrjs' import { groupAddressesByNetwork, satoshisToUnit } from 'utils' -import { fetchAddressBySubstring, fetchAllAddressesForNetworkId, getLatestTxTimestampForAddress, setSyncing, updateLastSynced } from './addressService' +import { fetchAddressBySubstring, fetchAddressesArray, fetchAllAddressesForNetworkId, getLatestTxTimestampForAddress, setSyncing, updateLastSynced } from './addressService' import * as ws from 'ws' import { BroadcastTxData } from 'ws-service/types' import config from 'config' @@ -406,7 +406,8 @@ export class ChronikBlockchainClient implements BlockchainClient { } private async getAddressesForTransaction (transaction: Tx_InNode): Promise { - const addressesWithTransactions: AddressWithTransaction[] = await Promise.all(Object.values(this.getSubscribedAddresses()).map( + const addressesFromStringArray = await fetchAddressesArray(this.getSubscribedAddresses()) + const addressesWithTransactions: AddressWithTransaction[] = await Promise.all(addressesFromStringArray.map( async address => { return { address, From 14d7f8b72c42c2a9c0cd7f67d1692f7eaf935167 Mon Sep 17 00:00:00 2001 From: chedieck Date: Thu, 8 Aug 2024 08:25:45 -0300 Subject: [PATCH 08/16] fix: sync concurrence in address syncing --- services/chronikService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/services/chronikService.ts b/services/chronikService.ts index 697b9d148..523142691 100644 --- a/services/chronikService.ts +++ b/services/chronikService.ts @@ -259,6 +259,7 @@ export class ChronikBlockchainClient implements BlockchainClient { let page = 0 const latestTimestamp = await getLatestTxTimestampForAddress(address.id) ?? 0 + if (address.syncing) { return } await setSyncing(addressString, true) while (true) { let transactions = await this.getPaginatedTxs(addressString, page, pageSize) From b465ea43b6a20c87800837c4284284ec81aa4d57 Mon Sep 17 00:00:00 2001 From: chedieck Date: Thu, 8 Aug 2024 10:30:50 -0300 Subject: [PATCH 09/16] chore: example uses chronik --- config/example-config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/example-config.json b/config/example-config.json index f71afc051..9923e63a8 100644 --- a/config/example-config.json +++ b/config/example-config.json @@ -9,7 +9,7 @@ "redisURL": "redis://paybutton-cache:6379", "networkBlockchainClients": { "ecash": "chronik", - "bitcoincash": "grpc" + "bitcoincash": "chronik" }, "networkBlockchainURLs": { "ecash": "https://chronik.fabien.cash", From 5c44adb23c968a733bb6c48ef2b3cc1035b3a841 Mon Sep 17 00:00:00 2001 From: chedieck Date: Sun, 11 Aug 2024 09:22:49 -0300 Subject: [PATCH 10/16] fix: remove reference for deleted file --- jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index a24dc451d..0ee3fec4e 100644 --- a/jest.config.js +++ b/jest.config.js @@ -3,5 +3,5 @@ module.exports = { preset: 'ts-jest', testEnvironment: 'node', modulePaths: ['./'], - setupFilesAfterEnv: ["./tests/setup.js", "./tests/setupMocks.ts"] + setupFilesAfterEnv: ["./tests/setup.js"] }; From 9057901a69594cdbda11660b05365e3f2cb8717d Mon Sep 17 00:00:00 2001 From: chedieck Date: Mon, 12 Aug 2024 08:49:33 -0300 Subject: [PATCH 11/16] feat: import socket messages --- services/chronikService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/chronikService.ts b/services/chronikService.ts index 7c648b137..757497263 100644 --- a/services/chronikService.ts +++ b/services/chronikService.ts @@ -2,7 +2,7 @@ import { BlockInfo_InNode, ChronikClientNode, ScriptType_InNode, ScriptUtxo_InNo import { encode, decode } from 'ecashaddrjs' import bs58 from 'bs58' import { AddressWithTransaction, BlockchainClient, BlockchainInfo, BlockInfo, NodeJsGlobalChronik, TransactionDetails, Networks } from './blockchainService' -import { CHRONIK_MESSAGE_CACHE_DELAY, RESPONSE_MESSAGES, XEC_TIMESTAMP_THRESHOLD, XEC_NETWORK_ID, BCH_NETWORK_ID, BCH_TIMESTAMP_THRESHOLD, FETCH_DELAY, FETCH_N, KeyValueT, NETWORK_IDS_FROM_SLUGS, NETWORK_SLUGS_FROM_IDS } from 'constants/index' +import { CHRONIK_MESSAGE_CACHE_DELAY, RESPONSE_MESSAGES, XEC_TIMESTAMP_THRESHOLD, XEC_NETWORK_ID, BCH_NETWORK_ID, BCH_TIMESTAMP_THRESHOLD, FETCH_DELAY, FETCH_N, KeyValueT, NETWORK_IDS_FROM_SLUGS, NETWORK_SLUGS_FROM_IDS, SOCKET_MESSAGES } from 'constants/index' import { TransactionWithAddressAndPrices, createManyTransactions, From e29941ba5072fc8a0c9f0847a46517758ddfb1be Mon Sep 17 00:00:00 2001 From: chedieck Date: Mon, 12 Aug 2024 09:36:46 -0300 Subject: [PATCH 12/16] chore: fix config initing --- config/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/index.ts b/config/index.ts index ca104a0ba..d6f325d7a 100644 --- a/config/index.ts +++ b/config/index.ts @@ -27,6 +27,9 @@ const readConfig = (): Config => { if (config.networksUnderMaintenance === undefined) { config.networksUnderMaintenance = {} } + if (config.networkBlockchainURLs === undefined) { + config.networkBlockchainURLs = {} + } if ( ( config.networkBlockchainURLs.ecash === undefined || From e89864c23d5c5110935d7cc07e8c72190b3390e9 Mon Sep 17 00:00:00 2001 From: chedieck Date: Tue, 13 Aug 2024 15:08:34 -0300 Subject: [PATCH 13/16] chore: remove wip log --- pages/admin/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/pages/admin/index.tsx b/pages/admin/index.tsx index d9f288f3b..cd5258d2c 100644 --- a/pages/admin/index.tsx +++ b/pages/admin/index.tsx @@ -62,7 +62,6 @@ export default function Admin ({ user, isAdmin }: IProps): JSX.Element { useEffect(() => { void (async () => { const ok = await (await fetch('chronikStatus')).json() - console.log('ok', ok) // WIP const subscribedEcashAddresses = ok.ecash.map((value: string) => ({ address: value })) const subscribedBitcoincashAddresses = ok.bitcoincash.map((value: string) => ({ address: value })) setEcashSubscribedAddresses(subscribedEcashAddresses) From 1af0f048540a0e5a14efa87a4480035ec93dedac Mon Sep 17 00:00:00 2001 From: chedieck Date: Tue, 13 Aug 2024 20:51:09 -0300 Subject: [PATCH 14/16] chore: update yarn lock --- yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index dd8499b17..85685964a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2904,7 +2904,7 @@ ecashaddrjs@^1.0.7: dependencies: big-integer "1.6.36" -ecashaddrjs@^1.5.8, "ecashaddrjs@file:../../../.cache/yarn/v6/npm-chronik-client-0.28.1-475c36f61db0458e88fbd509a0ff940eb3df2f2e-integrity/node_modules/ecashaddrjs": +ecashaddrjs@^1.5.8, "ecashaddrjs@file:../.cache/yarn/v6/npm-chronik-client-0.28.1-475c36f61db0458e88fbd509a0ff940eb3df2f2e-integrity/node_modules/ecashaddrjs": version "1.5.8" resolved "https://registry.yarnpkg.com/ecashaddrjs/-/ecashaddrjs-1.5.8.tgz#fff684d14a944cfd4bca56dd8dc3adbfce8016f9" integrity sha512-EBL052a1Hd1+wwaBQr3Q+jXY4XfH0jXR1tK360P8GQ9YJUZ5zLG+0I7yee8/QgHY04zdoCzDOY0fRlwqtqDhRg== From 360823f3d8222ef5a40d3478676c2d2720146964 Mon Sep 17 00:00:00 2001 From: chedieck Date: Wed, 14 Aug 2024 08:07:55 -0300 Subject: [PATCH 15/16] test: integration tests for blockchain --- tests/integration-tests/blockchainClients.test.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/integration-tests/blockchainClients.test.ts diff --git a/tests/integration-tests/blockchainClients.test.ts b/tests/integration-tests/blockchainClients.test.ts new file mode 100644 index 000000000..b7cb61170 --- /dev/null +++ b/tests/integration-tests/blockchainClients.test.ts @@ -0,0 +1,12 @@ +import { getLastBlockTimestamp } from 'services/blockchainService' + +describe('blockchain clients connect', () => { + it('bitcoincash chronik connects', async () => { + const x = await getLastBlockTimestamp('bitcoincash') + expect(x).toBeGreaterThan(1723630000) + }) + it('eCash chronik connects', async () => { + const x = await getLastBlockTimestamp('ecash') + expect(x).toBeGreaterThan(1723630000) + }) +}) From c81fea7d6de43e75ac00b788fd9b26836f4380a6 Mon Sep 17 00:00:00 2001 From: chedieck Date: Wed, 14 Aug 2024 08:59:40 -0300 Subject: [PATCH 16/16] test: integration tests for tx detail --- .../blockchainClients.test.ts | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/tests/integration-tests/blockchainClients.test.ts b/tests/integration-tests/blockchainClients.test.ts index b7cb61170..e06a1e102 100644 --- a/tests/integration-tests/blockchainClients.test.ts +++ b/tests/integration-tests/blockchainClients.test.ts @@ -1,12 +1,23 @@ -import { getLastBlockTimestamp } from 'services/blockchainService' +import { getLastBlockTimestamp, getTransactionDetails } from 'services/blockchainService' describe('blockchain clients connect', () => { it('bitcoincash chronik connects', async () => { - const x = await getLastBlockTimestamp('bitcoincash') - expect(x).toBeGreaterThan(1723630000) + const t = await getLastBlockTimestamp('bitcoincash') + expect(t).toBeGreaterThan(1723630000) }) it('eCash chronik connects', async () => { - const x = await getLastBlockTimestamp('ecash') - expect(x).toBeGreaterThan(1723630000) + const t = await getLastBlockTimestamp('ecash') + expect(t).toBeGreaterThan(1723630000) + }) +}) + +describe('client fetches tx', () => { + it('bitcoincash chronik connects', async () => { + const tx = await getTransactionDetails('003688bbfe007a83dcbb59b329002306d94b7d1029f027399f2a30d6b3b7e5bb', 'bitcoincash') + expect(tx.block.height).toBe(858963) + }) + it('eCash chronik connects', async () => { + const tx = await getTransactionDetails('ad5fd39867d70037251fb92a76b153a593a80e77457c16dc46aa12bd4aa70343', 'ecash') + expect(tx.block.height).toBe(857742) }) })