mirror of
https://github.com/mempool/mempool.git
synced 2025-03-15 12:20:28 +01:00
[bitcoin core] add internal routes to bitcoin core rpc
This commit is contained in:
parent
91fad7ff65
commit
f095913538
3 changed files with 224 additions and 0 deletions
221
backend/src/api/bitcoin/bitcoin-core.routes.ts
Normal file
221
backend/src/api/bitcoin/bitcoin-core.routes.ts
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
import { Application, NextFunction, Request, Response } from 'express';
|
||||||
|
import logger from '../../logger';
|
||||||
|
import bitcoinClient from './bitcoin-client';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define a set of routes used by the accelerator server
|
||||||
|
* Those routes are not designed to be public
|
||||||
|
*/
|
||||||
|
class BitcoinBackendRoutes {
|
||||||
|
private static tag = 'BitcoinBackendRoutes';
|
||||||
|
|
||||||
|
public initRoutes(app: Application) {
|
||||||
|
app
|
||||||
|
.get('/api/internal/bitcoinCore/' + 'getMempoolEntry', this.disableCache, this.$getMempoolEntry)
|
||||||
|
.post('/api/internal/bitcoinCore/' + 'decodeRawTransaction', this.disableCache, this.$decodeRawTransaction)
|
||||||
|
.get('/api/internal/bitcoinCore/' + 'getRawTransaction', this.disableCache, this.$getRawTransaction)
|
||||||
|
.post('/api/internal/bitcoinCore/' + 'sendRawTransaction', this.disableCache, this.$sendRawTransaction)
|
||||||
|
.post('/api/internal/bitcoinCore/' + 'testMempoolAccept', this.disableCache, this.$testMempoolAccept)
|
||||||
|
.get('/api/internal/bitcoinCore/' + 'getMempoolAncestors', this.disableCache, this.$getMempoolAncestors)
|
||||||
|
.get('/api/internal/bitcoinCore/' + 'getBlock', this.disableCache, this.$getBlock)
|
||||||
|
.get('/api/internal/bitcoinCore/' + 'getBlockHash', this.disableCache, this.$getBlockHash)
|
||||||
|
.get('/api/internal/bitcoinCore/' + 'getBlockCount', this.disableCache, this.$getBlockCount)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable caching for bitcoin core routes
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
* @param next
|
||||||
|
*/
|
||||||
|
private disableCache(req: Request, res: Response, next: NextFunction): void {
|
||||||
|
res.setHeader('Pragma', 'no-cache');
|
||||||
|
res.setHeader('Cache-control', 'private, no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0');
|
||||||
|
res.setHeader('expires', -1);
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exeption handler to return proper details to the accelerator server
|
||||||
|
*
|
||||||
|
* @param e
|
||||||
|
* @param fnName
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
|
private static handleException(e: any, fnName: string, res: Response): void {
|
||||||
|
if (typeof(e.code) === 'number') {
|
||||||
|
res.status(400).send(JSON.stringify(e, ['code', 'message']));
|
||||||
|
} else {
|
||||||
|
const err = `exception in ${fnName}. ${e}. Details: ${JSON.stringify(e, ['code', 'message'])}`;
|
||||||
|
logger.err(err, BitcoinBackendRoutes.tag);
|
||||||
|
res.status(500).send(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async $getMempoolEntry(req: Request, res: Response): Promise<void> {
|
||||||
|
const txid = req.query.txid;
|
||||||
|
try {
|
||||||
|
if (typeof(txid) !== 'string' || txid.length !== 64) {
|
||||||
|
res.status(400).send(`invalid param txid ${txid}. must be a string of 64 char`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const mempoolEntry = await bitcoinClient.getMempoolEntry(txid);
|
||||||
|
if (!mempoolEntry) {
|
||||||
|
res.status(404).send(`no mempool entry found for txid ${txid}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.status(200).send(mempoolEntry);
|
||||||
|
} catch (e: any) {
|
||||||
|
BitcoinBackendRoutes.handleException(e, 'getMempoolEntry', res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async $decodeRawTransaction(req: Request, res: Response): Promise<void> {
|
||||||
|
const rawTx = req.body.rawTx;
|
||||||
|
try {
|
||||||
|
if (typeof(rawTx) !== 'string') {
|
||||||
|
res.status(400).send(`invalid param rawTx ${rawTx}. must be a string`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const decodedTx = await bitcoinClient.decodeRawTransaction(rawTx);
|
||||||
|
if (!decodedTx) {
|
||||||
|
res.status(400).send(`unable to decode rawTx ${rawTx}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.status(200).send(decodedTx);
|
||||||
|
} catch (e: any) {
|
||||||
|
BitcoinBackendRoutes.handleException(e, 'decodeRawTransaction', res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async $getRawTransaction(req: Request, res: Response): Promise<void> {
|
||||||
|
const txid = req.query.txid;
|
||||||
|
try {
|
||||||
|
if (typeof(txid) !== 'string' || txid.length !== 64) {
|
||||||
|
res.status(400).send(`invalid param txid ${txid}. must be a string of 64 char`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const decodedTx = await bitcoinClient.getRawTransaction(txid);
|
||||||
|
if (!decodedTx) {
|
||||||
|
res.status(400).send(`unable to get raw transaction for txid ${txid}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.status(200).send(decodedTx);
|
||||||
|
} catch (e: any) {
|
||||||
|
BitcoinBackendRoutes.handleException(e, 'decodeRawTransaction', res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async $sendRawTransaction(req: Request, res: Response): Promise<void> {
|
||||||
|
const rawTx = req.body.rawTx;
|
||||||
|
try {
|
||||||
|
if (typeof(rawTx) !== 'string') {
|
||||||
|
res.status(400).send(`invalid param rawTx ${rawTx}. must be a string`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const txHex = await bitcoinClient.sendRawTransaction(rawTx);
|
||||||
|
if (!txHex) {
|
||||||
|
res.status(400).send(`unable to send rawTx ${rawTx}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.status(200).send(txHex);
|
||||||
|
} catch (e: any) {
|
||||||
|
BitcoinBackendRoutes.handleException(e, 'sendRawTransaction', res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async $testMempoolAccept(req: Request, res: Response): Promise<void> {
|
||||||
|
const rawTx = req.body.rawTx;
|
||||||
|
try {
|
||||||
|
if (typeof(rawTx) !== 'string') {
|
||||||
|
res.status(400).send(`invalid param rawTx ${rawTx}. must be a string`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const txHex = await bitcoinClient.testMempoolAccept([rawTx]);
|
||||||
|
if (typeof(txHex) !== 'object' || txHex.length === 0) {
|
||||||
|
res.status(400).send(`testmempoolaccept failed for raw tx ${rawTx}, got an empty result`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.status(200).send(txHex);
|
||||||
|
} catch (e: any) {
|
||||||
|
BitcoinBackendRoutes.handleException(e, 'testMempoolAccept', res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async $getMempoolAncestors(req: Request, res: Response): Promise<void> {
|
||||||
|
const txid = req.query.txid;
|
||||||
|
try {
|
||||||
|
if (typeof(txid) !== 'string' || txid.length !== 64) {
|
||||||
|
res.status(400).send(`invalid param txid ${txid}. must be a string of 64 char`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const decodedTx = await bitcoinClient.getMempoolAncestors(txid);
|
||||||
|
if (!decodedTx) {
|
||||||
|
res.status(400).send(`unable to get mempool ancestors for txid ${txid}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.status(200).send(decodedTx);
|
||||||
|
} catch (e: any) {
|
||||||
|
BitcoinBackendRoutes.handleException(e, 'getMempoolAncestors', res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async $getBlock(req: Request, res: Response): Promise<void> {
|
||||||
|
const blockHash = req.query.hash;
|
||||||
|
try {
|
||||||
|
if (typeof(blockHash) !== 'string' || blockHash.length !== 64) {
|
||||||
|
res.status(400).send(`invalid param blockHash ${blockHash}. must be a string of 64 char`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const block = await bitcoinClient.getBlock(blockHash);
|
||||||
|
if (!block) {
|
||||||
|
res.status(400).send(`unable to get block for block hash ${blockHash}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.status(200).send(block);
|
||||||
|
} catch (e: any) {
|
||||||
|
BitcoinBackendRoutes.handleException(e, 'getBlock', res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async $getBlockHash(req: Request, res: Response): Promise<void> {
|
||||||
|
const blockHeight = req.query.height;
|
||||||
|
try {
|
||||||
|
if (typeof(blockHeight) !== 'string') {
|
||||||
|
res.status(400).send(`invalid param blockHeight ${blockHeight}, must be a string representing an integer`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const blockHeightNumber = parseInt(blockHeight, 10);
|
||||||
|
if (!blockHeightNumber) {
|
||||||
|
res.status(400).send(`invalid param blockHeight ${blockHeight}. must be a valid integer`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const block = await bitcoinClient.getBlockHash(blockHeightNumber);
|
||||||
|
if (!block) {
|
||||||
|
res.status(400).send(`unable to get block hash for block height ${blockHeightNumber}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.status(200).send(block);
|
||||||
|
} catch (e: any) {
|
||||||
|
BitcoinBackendRoutes.handleException(e, 'getBlockHash', res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async $getBlockCount(req: Request, res: Response): Promise<void> {
|
||||||
|
try {
|
||||||
|
const count = await bitcoinClient.getBlockCount();
|
||||||
|
if (!count) {
|
||||||
|
res.status(400).send(`unable to get block count`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.status(200).send(`${count}`);
|
||||||
|
} catch (e: any) {
|
||||||
|
BitcoinBackendRoutes.handleException(e, 'getBlockCount', res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new BitcoinBackendRoutes
|
|
@ -44,6 +44,7 @@ import v8 from 'v8';
|
||||||
import { formatBytes, getBytesUnit } from './utils/format';
|
import { formatBytes, getBytesUnit } from './utils/format';
|
||||||
import redisCache from './api/redis-cache';
|
import redisCache from './api/redis-cache';
|
||||||
import accelerationApi from './api/services/acceleration';
|
import accelerationApi from './api/services/acceleration';
|
||||||
|
import bitcoinCoreRoutes from './api/bitcoin/bitcoin-core.routes';
|
||||||
|
|
||||||
class Server {
|
class Server {
|
||||||
private wss: WebSocket.Server | undefined;
|
private wss: WebSocket.Server | undefined;
|
||||||
|
@ -282,6 +283,7 @@ class Server {
|
||||||
|
|
||||||
setUpHttpApiRoutes(): void {
|
setUpHttpApiRoutes(): void {
|
||||||
bitcoinRoutes.initRoutes(this.app);
|
bitcoinRoutes.initRoutes(this.app);
|
||||||
|
bitcoinCoreRoutes.initRoutes(this.app);
|
||||||
pricesRoutes.initRoutes(this.app);
|
pricesRoutes.initRoutes(this.app);
|
||||||
if (config.STATISTICS.ENABLED && config.DATABASE.ENABLED && config.MEMPOOL.ENABLED) {
|
if (config.STATISTICS.ENABLED && config.DATABASE.ENABLED && config.MEMPOOL.ENABLED) {
|
||||||
statisticsRoutes.initRoutes(this.app);
|
statisticsRoutes.initRoutes(this.app);
|
||||||
|
|
|
@ -91,4 +91,5 @@ module.exports = {
|
||||||
walletPassphraseChange: 'walletpassphrasechange',
|
walletPassphraseChange: 'walletpassphrasechange',
|
||||||
getTxoutSetinfo: 'gettxoutsetinfo',
|
getTxoutSetinfo: 'gettxoutsetinfo',
|
||||||
getIndexInfo: 'getindexinfo',
|
getIndexInfo: 'getindexinfo',
|
||||||
|
testMempoolAccept: 'testmempoolaccept',
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue