UNPKG

@btc-vision/bitcoin-rpc

Version:

The one and only fully typed Bitcoin RPC client for Node.js

1,105 lines (1,104 loc) 36.4 kB
import { Logger } from '@btc-vision/bsi-common'; import { RPCClient, } from './external/rpc.js'; import { BitcoinVerbosity } from './types/BitcoinVerbosity.js'; import { FeeEstimation } from './types/FeeEstimation.js'; export class BitcoinRPC extends Logger { constructor(cacheClearInterval = 1000, enableDebug = false) { super(); this.cacheClearInterval = cacheClearInterval; this.enableDebug = enableDebug; this.logColor = '#fa9600'; this.rpc = null; this.blockchainInfo = null; this.currentBlockInfo = null; this.purgeInterval = null; this.purgeCachedData(); } destroy() { if (this.purgeInterval) { clearInterval(this.purgeInterval); } this.rpc = null; this.blockchainInfo = null; this.currentBlockInfo = null; } getRpcConfigFromBlockchainConfig(rpcInfo) { return { url: `${rpcInfo.BITCOIND_HTTPS ? 'https' : 'http'}://${rpcInfo.BITCOIND_HOST}`, port: rpcInfo.BITCOIND_PORT, user: rpcInfo.BITCOIND_USERNAME, pass: rpcInfo.BITCOIND_PASSWORD, }; } async getBestBlockHash() { this.debugMessage('getBestBlockHash'); if (!this.rpc) { throw new Error('RPC not initialized'); } const bestBlockHash = (await this.rpc.getbestblockhash().catch((e) => { this.error(`Error getting best block hash: ${e}`); return; })); return bestBlockHash || null; } async getBlockBatch(blockHashes) { this.debugMessage('getBlockBatch'); if (!this.rpc) { throw new Error('RPC not initialized'); } const blockData = (await this.rpc .getblockBatch(blockHashes) .catch((e) => { this.error(`Error getting block batch: ${e}`); return null; })); return blockData || null; } async getBestBlockHeader(verbose = true) { this.debugMessage('getBestBlockHeader'); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { verbose }; const result = await this.rpc.getbestblockheader(params).catch((e) => { this.error(`Error getting best block header: ${e}`); return null; }); return result; } async getDeploymentInfo(blockHash) { this.debugMessage(`getDeploymentInfo ${blockHash || 'tip'}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = blockHash ? { blockhash: blockHash } : {}; const result = await this.rpc.getdeploymentinfo(params).catch((e) => { this.error(`Error getting deployment info: ${e}`); return null; }); return result; } async getMempoolEntryById(wtxid) { this.debugMessage(`getMempoolEntryById ${wtxid}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { wtxid }; const result = await this.rpc.getmempoolentrybyid(params).catch((e) => { this.error(`Error getting mempool entry by id: ${e}`); return null; }); return result; } async generate(nBlocks, maxTries, wallet) { this.debugMessage(`generate ${nBlocks} blocks`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { nblocks: nBlocks, maxtries: maxTries, }; const result = await this.rpc.generate(params, wallet).catch((e) => { this.error(`Error generating blocks: ${e}`); return null; }); return result; } async generateBlock(output, transactions = [], submit = true) { this.debugMessage(`generateBlock to ${output}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { output, transactions, submit, }; const result = await this.rpc.generateblock(params).catch((e) => { this.error(`Error generating block: ${e}`); return null; }); return result; } async addPeerAddress(address, port, tried = false) { this.debugMessage(`addPeerAddress ${address}:${port}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { address, port, tried }; const result = await this.rpc.addpeeraddress(params).catch((e) => { this.error(`Error adding peer address: ${e}`); return null; }); return result; } async getNodeAddresses(count) { this.debugMessage(`getNodeAddresses ${count || 'default'}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = count ? { count } : {}; const result = await this.rpc.getnodeaddresses(params).catch((e) => { this.error(`Error getting node addresses: ${e}`); return null; }); return result; } async sendMsgToPeer(peerId, msgType, data) { this.debugMessage(`sendMsgToPeer to peer ${peerId}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { peer_id: peerId, msg_type: msgType, data, }; await this.rpc.sendmsgtopeer(params).catch((e) => { this.error(`Error sending message to peer: ${e}`); throw e; }); } async submitPackage(packageTxs, maxFeeRate, maxBurnAmount) { this.debugMessage(`submitPackage with ${packageTxs.length} transactions`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { package: packageTxs, maxfeerate: maxFeeRate, maxburnamount: maxBurnAmount, }; const result = await this.rpc.submitpackage(params).catch((e) => { this.error(`Error submitting package: ${e}`); return null; }); return result; } async getIndexInfo(indexName) { this.debugMessage(`getIndexInfo ${indexName || 'all'}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = indexName ? { index_name: indexName } : {}; const result = await this.rpc.getindexinfo(params).catch((e) => { this.error(`Error getting index info: ${e}`); return null; }); return result; } async verifyChainLock(blockHash, signature, height) { this.debugMessage(`verifyChainLock for block ${blockHash}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { blockhash: blockHash, signature, height, }; const result = await this.rpc.verifychainlock(params).catch((e) => { this.error(`Error verifying chainlock: ${e}`); return null; }); return result; } async createWalletDescriptor(type, internal, hdkey, wallet) { this.debugMessage(`createWalletDescriptor type: ${type}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { type, internal, hdkey, }; const result = await this.rpc.createwalletdescriptor(params, wallet).catch((e) => { this.error(`Error creating wallet descriptor: ${e}`); return null; }); return result; } async getHDKeys(activeOnly = false, privateKeys = false, wallet) { this.debugMessage(`getHDKeys`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { active_only: activeOnly, private: privateKeys, }; const result = await this.rpc.gethdkeys(params, wallet).catch((e) => { this.error(`Error getting HD keys: ${e}`); return null; }); return result; } async importDescriptors(requests, wallet) { this.debugMessage(`importDescriptors with ${requests.length} descriptors`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { requests }; const result = await this.rpc.importdescriptors(params, wallet).catch((e) => { this.error(`Error importing descriptors: ${e}`); return null; }); return result; } async listDescriptors(includePrivate = false, wallet) { this.debugMessage(`listDescriptors`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { private: includePrivate }; const result = await this.rpc.listdescriptors(params, wallet).catch((e) => { this.error(`Error listing descriptors: ${e}`); return null; }); return result; } async migrateWallet(walletName, passphrase) { this.debugMessage(`migrateWallet ${walletName || 'default'}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { wallet_name: walletName, passphrase, }; const result = await this.rpc.migratewallet(params).catch((e) => { this.error(`Error migrating wallet: ${e}`); return null; }); return result; } async newKeypool(wallet) { this.debugMessage(`newKeypool`); if (!this.rpc) { throw new Error('RPC not initialized'); } await this.rpc.newkeypool(wallet).catch((e) => { this.error(`Error creating new keypool: ${e}`); throw e; }); } async getBlockTemplate(params) { this.debugMessage('getBlockTemplate'); if (!this.rpc) { throw new Error('RPC not initialized'); } const result = (await this.rpc .getblocktemplate(params) .catch((e) => { this.error(`Error getting block template: ${e}`); return null; })); return result || null; } async submitBlock(blockHex) { this.debugMessage('submitBlock'); if (!this.rpc) { throw new Error('RPC not initialized'); } return (await this.rpc.submitblock({ hexdata: blockHex }).catch((e) => { this.error(`Error submitting block: ${e}`); throw e; })); } async psbtBumpFee(txid, options, wallet) { this.debugMessage(`psbtBumpFee for ${txid}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { txid, options }; const result = await this.rpc.psbtbumpfee(params, wallet).catch((e) => { this.error(`Error bumping fee with PSBT: ${e}`); return null; }); return result; } async send(outputs, options, wallet) { this.debugMessage(`send transaction`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { outputs, conf_target: options?.confTarget, estimate_mode: options?.estimateMode, fee_rate: options?.feeRate, options: options?.advancedOptions, }; const result = await this.rpc.send(params, wallet).catch((e) => { this.error(`Error sending transaction: ${e}`); return null; }); return result; } async sendAll(recipients, options, wallet) { this.debugMessage(`sendAll transaction`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { recipients, conf_target: options?.confTarget, estimate_mode: options?.estimateMode, fee_rate: options?.feeRate, options: options?.advancedOptions, }; const result = await this.rpc.sendall(params, wallet).catch((e) => { this.error(`Error sending all: ${e}`); return null; }); return result; } async simulateRawTransaction(rawTxs = [], includeWatchOnly = false, wallet) { this.debugMessage(`simulateRawTransaction`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { rawtxs: rawTxs, options: { include_watchonly: includeWatchOnly }, }; const result = await this.rpc.simulaterawtransaction(params, wallet).catch((e) => { this.error(`Error simulating transaction: ${e}`); return null; }); return result; } async upgradeWallet(version, wallet) { this.debugMessage(`upgradeWallet to version ${version || 'latest'}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = version ? { version } : {}; const result = await this.rpc.upgradewallet(params, wallet).catch((e) => { this.error(`Error upgrading wallet: ${e}`); return null; }); return result; } async getBlockAsHexString(blockHash) { this.debugMessage('getBlockAsHexString'); if (!this.rpc) { throw new Error('RPC not initialized'); } const param = { blockhash: blockHash, verbosity: BitcoinVerbosity.NONE, }; const blockData = (await this.rpc.getblock(param).catch((e) => { this.error(`Error getting block data: ${e}`); return null; })); return blockData == '' ? null : blockData; } async estimateSmartFee(confTarget, estimateMode = FeeEstimation.CONSERVATIVE) { this.debugMessage('estimateSmartFee'); if (!this.rpc) { throw new Error('RPC not initialized'); } const opts = { conf_target: confTarget, estimate_mode: estimateMode, }; return (await this.rpc.estimatesmartfee(opts)); } async joinPSBTs(psbts) { this.debugMessage('joinPSBTs'); if (!this.rpc) { throw new Error('RPC not initialized'); } const result = (await this.rpc .joinpsbts({ txs: psbts, }) .catch((e) => { this.error(`Error joining PSBTs: ${e}`); return ''; })); return result || null; } async getBlockInfoOnly(blockHash) { this.debugMessage('getBlockInfoOnly'); if (!this.rpc) { throw new Error('RPC not initialized'); } const param = { blockhash: blockHash, verbosity: BitcoinVerbosity.RAW, }; const blockData = (await this.rpc.getblock(param).catch((e) => { this.error(`Error getting block data: ${e}`); return null; })); return blockData || null; } async getBlockInfoWithTransactionData(blockHash) { this.debugMessage(`getBlockInfoWithTransactionData ${blockHash}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const param = { blockhash: blockHash, verbosity: 2, }; const blockData = (await this.rpc .getblock(param) .catch((e) => { this.error(`Error getting block data: ${e}`); return null; })); return blockData || null; } async getBlockHashes(height, count) { this.debugMessage(`getBlockHashes ${height} ${count}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const param = { height, }; const blockHashes = (await this.rpc .getblockhashes(param, count) .catch((e) => { this.error(`Error getting block hashes: ${e}`); return []; })); return blockHashes || null; } async getBlocksInfoWithTransactionData(blockHashes) { if (!this.rpc) { throw new Error('RPC not initialized'); } const blockData = (await this.rpc .getblockBatch(blockHashes, 2) .catch((e) => { this.error(`Error getting block data: ${e}`); return null; })); return blockData || null; } async getBlockCount() { this.debugMessage('getBlockCount'); if (!this.rpc) { throw new Error('RPC not initialized'); } const blockCount = (await this.rpc.getblockcount().catch((e) => { this.error(`Error getting block count: ${e}`); return 0; })); return blockCount ?? null; } async getBlockFilter(blockHash, filterType) { this.debugMessage(`getBlockFilter ${blockHash}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const param = { blockhash: blockHash, filtertype: filterType, }; const result = (await this.rpc .getblockfilter(param) .catch((e) => { this.error(`Error getting block filter: ${e}`); return null; })); return result || null; } async getBlockHash(height) { this.debugMessage(`getBlockHash ${height}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const param = { height: height, }; const result = (await this.rpc.getblockhash(param).catch((e) => { this.error(`Error getting block hash: ${e}`); return ''; })); return result || null; } async getChainInfo() { this.debugMessage('getChainInfo'); if (!this.rpc) { throw new Error('RPC not initialized'); } this.blockchainInfo = (await this.rpc.getblockchaininfo()); if (this.blockchainInfo) { this.currentBlockInfo = { blockHeight: this.blockchainInfo.blocks, blockHash: this.blockchainInfo.bestblockhash, }; } return this.blockchainInfo; } async getWalletInfo(walletName) { this.debugMessage(`getWalletInfo ${walletName}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const walletInfo = (await this.rpc .getwalletinfo(walletName) .catch((e) => { this.error(`Error getting wallet info: ${e}`); return null; })); return walletInfo || null; } async createWallet(params) { this.debugMessage('createWallet'); if (!this.rpc) { throw new Error('RPC not initialized'); } const wallet = (await this.rpc .createwallet(params) .catch((e) => { this.error(`Error creating wallet: ${e}`); return ''; })); return wallet || null; } async loadWallet(filename) { this.debugMessage(`loadWallet ${filename}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { filename, }; const wallet = (await this.rpc .loadwallet(params) .catch((e) => { this.error(`Error loading wallet: ${e}`); return ''; })); return wallet || null; } async listWallets() { this.debugMessage('listWallets'); if (!this.rpc) { throw new Error('RPC not initialized'); } const wallets = (await this.rpc.listwallets().catch((e) => { this.error(`Error listing wallets: ${e}`); return []; })); return wallets || null; } async getNewAddress(label, wallet) { this.debugMessage(`getNewAddress ${label}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { label, }; const address = (await this.rpc .getnewaddress(params, wallet) .catch((e) => { this.error(`Error getting new address: ${e}`); return ''; })); return address || null; } async generateToAddress(nBlock, address, wallet) { this.debugMessage(`generateToAddress ${nBlock} ${address}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { nblocks: nBlock, address, }; const blockHashes = (await this.rpc .generatetoaddress(params, wallet) .catch((e) => { this.error(`Error generating to address: ${e}`); return []; })); return blockHashes || null; } async importPrivateKey(privateKey, label, rescan, wallet) { this.debugMessage(`importPrivateKey ${label}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { privkey: privateKey, label, rescan, }; await this.rpc.importprivkey(params, wallet).catch((e) => { this.error(`Error importing private key: ${e}`); }); } async invalidateBlock(blockHash) { this.debugMessage(`invalidateBlock ${blockHash}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const param = { blockhash: blockHash, }; await this.rpc.invalidateblock(param).catch((e) => { this.error(`Error invalidating block: ${e}`); throw e; }); } async reconsiderBlock(blockHash) { this.debugMessage(`reconsiderBlock ${blockHash}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const param = { blockhash: blockHash, }; await this.rpc.reconsiderblock(param).catch((e) => { this.error(`Error reconsidering block: ${e}`); throw e; }); } async getAddressByLabel(label, wallet) { this.debugMessage(`getAddressByLabel ${label}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { label, }; const address = (await this.rpc .getaddressesbylabel(params, wallet) .catch((e) => { this.error(`Error getting address by label: ${e}`); throw e; })); return address || null; } async sendRawTransaction(params) { this.debugMessage('sendRawTransaction'); if (!this.rpc) { throw new Error('RPC not initialized'); } const txId = (await this.rpc .sendrawtransaction(params) .catch((e) => { throw e; })); return txId || null; } async testMempoolAccept(rawtxs, maxfeerate) { this.debugMessage(`testMempoolAccept with ${rawtxs.length} transactions`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { rawtxs, maxfeerate, }; const result = await this.rpc.testmempoolaccept(params).catch((e) => { this.error(`Error testing mempool accept: ${e}`); throw e; }); return result; } async decodeRawTransaction(hexString, isWitness = false) { this.debugMessage(`decodeRawTransaction ${hexString}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { hexstring: hexString, iswitness: isWitness, }; const rawTx = (await this.rpc .decoderawtransaction(params) .catch((e) => { this.error(`Error decoding raw transaction: ${e}`); return null; })); return rawTx || null; } async dumpPrivateKey(address, wallet) { this.debugMessage(`dumpPrivateKey ${address}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const privateKey = (await this.rpc .dumpprivkey({ address, }, wallet) .catch((e) => { this.error(`Error dumping private key: ${e}`); return ''; })); return privateKey || null; } async getRawTransaction(parameters) { this.debugMessage(`getRawTransaction ${parameters.txId}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const params = { txid: parameters.txId, verbose: parameters.verbose !== BitcoinVerbosity.RAW, }; if (parameters.blockHash) { params.blockhash = parameters.blockHash; } const rawTx = (await this.rpc .getrawtransaction(params) .catch((e) => { this.error(`Error getting raw transaction: ${e}`); return null; })); return rawTx || null; } async getRawTransactions(txs, verbose) { this.debugMessage(`getRawTransactions ${txs}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const txsInfo = (await this.rpc .getrawtransactionBatch(txs, verbose !== BitcoinVerbosity.RAW) .catch((e) => { this.error(`Error getting raw transactions: ${e}`); return null; })); return txsInfo || null; } async getBlockHeight() { this.debugMessage('getBlockHeight'); if (!this.rpc) { throw new Error('RPC not initialized'); } if (!this.currentBlockInfo) { await this.getChainInfo(); } return this.currentBlockInfo; } async getBlockHeader(blockHash, verbose) { this.debugMessage(`getBlockHeader ${blockHash} ${verbose}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const param = { blockhash: blockHash, verbose: verbose, }; const header = (await this.rpc .getblockheader(param) .catch((e) => { this.error(`Error getting block header: ${e}`); return ''; })); return header || null; } async getBlockStatsByHeight(height, stats) { this.debugMessage(`getBlockStatsByHeight ${height} ${stats}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const param = { hash_or_height: height, stats: stats, }; const blockStats = (await this.rpc .getblockstats(param) .catch((e) => { this.error(`Error getting block stats: ${e}`); return null; })); return blockStats || null; } async getBlockStatsByHash(blockHash, stats) { this.debugMessage(`getBlockStatsByHash ${blockHash} ${stats}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const param = { hash_or_height: blockHash, stats: stats, }; const blockStats = (await this.rpc .getblockstats(param) .catch((e) => { this.error(`Error getting block stats: ${e}`); return null; })); return blockStats || null; } async getChainTips() { this.debugMessage('getChainTips'); if (!this.rpc) { throw new Error('RPC not initialized'); } const tips = (await this.rpc.getchaintips().catch((e) => { this.error(`Error getting chain tips: ${e}`); return null; })); return tips || null; } async getChainTxStats(param) { this.debugMessage('getChainTxStats'); if (!this.rpc) { throw new Error('RPC not initialized'); } const chainTxStats = (await this.rpc .getchaintxstats(param) .catch((e) => { this.error(`Error getting chain tx stats: ${e}`); return null; })); return chainTxStats || null; } async getDifficulty() { this.debugMessage('getDifficulty'); if (!this.rpc) { throw new Error('RPC not initialized'); } const difficulty = (await this.rpc.getdifficulty().catch((e) => { this.error(`Error getting difficulty: ${e}`); return 0; })); return difficulty ?? null; } async getMempoolAncestors(txId, verbose) { this.debugMessage(`getMempoolAncestors ${txId}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const param = { txid: txId, verbose: verbose !== BitcoinVerbosity.RAW, }; const transactionInfo = (await this.rpc .getmempoolancestors(param) .catch((e) => { this.error(`Error getting mempool ancestors: ${e}`); return null; })); return transactionInfo || null; } async getMempoolDescendants(txid, verbose) { this.debugMessage(`getMempoolDescendants ${txid}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const param = { txid: txid, verbose: verbose !== BitcoinVerbosity.RAW, }; const transactionInfo = (await this.rpc .getmempooldescendants(param) .catch((e) => { this.error(`Error getting mempool descendants: ${e}`); return null; })); return transactionInfo || null; } async getMempoolEntry(txid) { this.debugMessage(`getMempoolEntry ${txid}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const param = { txid: txid, }; const transactionInfo = (await this.rpc .getmempoolentry(param) .catch((e) => { this.error(`Error getting mempool entry: ${e}`); return null; })); return transactionInfo || null; } async getMempoolInfo() { this.debugMessage('getMempoolInfo'); if (!this.rpc) { throw new Error('RPC not initialized'); } const mempoolInfo = (await this.rpc .getmempoolinfo() .catch((e) => { this.error(`Error getting mempool info: ${e}`); return null; })); return mempoolInfo || null; } async getRawMempool(verbose) { this.debugMessage('getRawMempool'); if (!this.rpc) { throw new Error('RPC not initialized'); } const param = { verbose: verbose !== BitcoinVerbosity.RAW, }; const mempoolInfo = (await this.rpc .getrawmempool(param) .catch((e) => { this.error(`Error getting raw mempool: ${e}`); return null; })); return mempoolInfo || null; } async getTxOut(txid, voutNumber, includeMempool) { this.debugMessage(`getTxOut ${txid} ${voutNumber}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const param = { n: voutNumber, txid: txid, include_mempool: includeMempool, }; const txOuputInfo = (await this.rpc .gettxout(param) .catch((e) => { this.error(`Error getting tx out: ${e}`); return null; })); return txOuputInfo || null; } async getTxOutProof(txids, blockHash) { this.debugMessage(`getTxOutProof ${txids} ${blockHash}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const param = { txids: txids, blockhash: blockHash, }; const txOuputProof = (await this.rpc .gettxoutproof(param) .catch((e) => { this.error(`Error getting tx out proof: ${e}`); return ''; })); return txOuputProof || null; } async getTxOutSetInfo() { this.debugMessage('getTxOutSetInfo'); if (!this.rpc) { throw new Error('RPC not initialized'); } const txOuputSetInfo = (await this.rpc .gettxoutsetinfo() .catch((e) => { this.error(`Error getting tx out set info: ${e}`); return null; })); return txOuputSetInfo || null; } async preciousBlock(blockHash) { this.debugMessage(`preciousBlock ${blockHash}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const param = { blockhash: blockHash, }; await this.rpc.preciousblock(param).catch((e) => { this.error(`Error precious block: ${e}`); }); } async pruneBlockChain(height) { this.debugMessage(`pruneBlockChain ${height}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const param = { height: height, }; const prunedHeight = (await this.rpc .pruneblockchain(param) .catch((e) => { this.error(`Error pruning blockchain: ${e}`); return 0; })); return prunedHeight ?? null; } async saveMempool() { this.debugMessage('saveMempool'); if (!this.rpc) { throw new Error('RPC not initialized'); } await this.rpc.savemempool().catch((e) => { this.error(`Error saving mempool: ${e}`); }); } async verifyChain(checkLevel, nblocks) { this.debugMessage(`verifyChain ${checkLevel} ${nblocks}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const param = { checklevel: checkLevel, nblocks: nblocks, }; const checked = (await this.rpc.verifychain(param).catch((e) => { this.error(`Error verifying chain: ${e}`); return false; })); return checked ?? null; } async verifyTxOutProof(proof) { this.debugMessage(`verifyTxOutProof ${proof}`); if (!this.rpc) { throw new Error('RPC not initialized'); } const param = { proof: proof, }; const proofs = (await this.rpc .verifytxoutproof(param) .catch((e) => { this.error(`Error verifying tx out proof: ${e}`); return []; })); return proofs || null; } async init(rpcInfo) { if (this.rpc) { throw new Error('RPC already initialized'); } const rpcConfig = this.getRpcConfigFromBlockchainConfig(rpcInfo); this.rpc = new RPCClient(rpcConfig); await this.testRPC(); } error(...args) { if (this.enableDebug) { super.error(...args); } } debugMessage(message) { if (this.enableDebug) { this.log(message); } } purgeCachedData() { this.purgeInterval = setInterval(() => { this.blockchainInfo = null; this.currentBlockInfo = null; }, this.cacheClearInterval); } async testRPC() { try { const chainInfo = await this.getChainInfo(); if (!chainInfo) { this.error('RPC errored. Please check your configuration.'); process.exit(1); } } catch (e) { const error = e; this.error(`RPC errored. Please check your configuration. ${error.message}`); } } }