bitcore-node
Version:
A blockchain indexing node with extended capabilities using bitcore
171 lines (157 loc) • 5.77 kB
text/typescript
import { Request, Response, Router } from 'express';
import logger from '../../logger';
import { ICoin } from '../../models/coin';
import { ITransaction } from '../../models/transaction';
import { ChainStateProvider } from '../../providers/chain-state';
import { StreamTransactionsParams } from '../../types/namespaces/ChainStateProvider';
import { SetCache } from '../middleware';
import { CacheTimes } from '../middleware';
const router = Router({ mergeParams: true });
router.get('/', function(req: Request, res: Response) {
let { chain, network } = req.params;
let { blockHeight, blockHash, limit, since, direction, paging } = req.query as any;
if (!chain || !network) {
return res.status(400).send('Missing required param');
}
if (!blockHash && !blockHeight) {
return res.status(400).send('Must provide blockHash or blockHeight');
}
chain = chain.toUpperCase();
network = network.toLowerCase();
let payload: StreamTransactionsParams = {
chain,
network,
req,
res,
args: { limit, since, direction, paging }
};
if (blockHeight !== undefined) {
payload.args.blockHeight = parseInt(blockHeight);
}
if (blockHash !== undefined) {
payload.args.blockHash = blockHash;
}
return ChainStateProvider.streamTransactions(payload);
});
router.get('/:txId', async (req: Request, res: Response) => {
let { chain, network, txId } = req.params;
if (typeof txId !== 'string' || !chain || !network) {
return res.status(400).send('Missing required param');
}
txId = txId
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
chain = chain.toUpperCase();
network = network.toLowerCase();
try {
const tx = await ChainStateProvider.getTransaction({ chain, network, txId });
if (!tx) {
return res.status(404).send(`The requested txid ${txId} could not be found.`);
} else {
const tip = await ChainStateProvider.getLocalTip({ chain, network });
if (tx && tip && tx.blockHeight > -1 && tip.height - tx.blockHeight > 100) {
SetCache(res, CacheTimes.Month);
}
return res.send(tx);
}
} catch (err: any) {
logger.error('Error getting transaction: %o', err.stack || err.message || err);
return res.status(500).send(err.message || err);
}
});
// Get transaction with input and outputs, assigned to key coins
router.get('/:txId/populated', async (req: Request, res: Response) => {
let { chain, network, txId } = req.params;
let txid = txId;
if (typeof txid !== 'string' || !chain || !network) {
return res.status(400).send('Missing required param');
}
try {
let tx: ITransaction & { blockHeight: number; coins?: Array<ICoin> };
let coins: any;
let tip: any;
[tx, coins, tip] = await Promise.all([
ChainStateProvider.getTransaction({ chain, network, txId }),
ChainStateProvider.getCoinsForTx({ chain, network, txid }),
ChainStateProvider.getLocalTip({ chain, network })
]);
if (!tx) {
return res.status(404).send(`The requested txid ${txid} could not be found.`);
} else {
if (tx && tip && tx.blockHeight > 0 && tip.height - tx.blockHeight > 100) {
SetCache(res, CacheTimes.Month);
}
if (!coins) {
res.status(404).send(`The requested coins for txid ${txid} could not be found.`);
}
tx.coins = coins;
return res.send(tx);
}
} catch (err: any) {
logger.error('Error getting populated transaction: %o', err.stack || err.message || err);
return res.status(500).send(err.message || err);
}
});
router.get('/:txId/authhead', async (req: Request, res: Response) => {
let { chain, network, txId } = req.params;
if (typeof txId !== 'string' || !chain || !network) {
return res.status(400).send('Missing required param');
}
chain = chain.toUpperCase();
network = network.toLowerCase();
try {
const authhead = await ChainStateProvider.getAuthhead({ chain, network, txId });
if (!authhead) {
return res.status(404).send(`Authhead for txid ${txId} could not be found.`);
} else {
return res.send(authhead);
}
} catch (err: any) {
logger.error('Error getting transaction authhead: %o', err.stack || err.message || err);
return res.status(500).send(err.message || err);
}
});
router.get('/:txid/coins', (req: Request, res: Response, next) => {
let { chain, network, txid } = req.params;
if (typeof txid !== 'string' || typeof chain !== 'string' || typeof network !== 'string') {
res.status(400).send('Missing required param');
} else {
chain = chain.toUpperCase();
network = network.toLowerCase();
ChainStateProvider.getCoinsForTx({ chain, network, txid })
.then(coins => {
res.setHeader('Content-Type', 'application/json');
return res.status(200).send(JSON.stringify(coins));
})
.catch(next);
}
});
router.post('/send', async function(req: Request, res: Response) {
let { chain, network } = req.params;
let { rawTx } = req.body;
try {
if (typeof rawTx !== 'string' && !Array.isArray(rawTx)) {
return res.status(400).send('Invalid rawTx');
}
if (Array.isArray(rawTx) && !rawTx.every(tx => typeof tx === 'string')) {
return res.status(400).send('Invalid array of rawTx');
}
chain = chain.toUpperCase();
network = network.toLowerCase();
let txid = await ChainStateProvider.broadcastTransaction({
chain,
network,
rawTx
});
return res.send({ txid });
} catch (err: any) {
logger.error('Broadcast error: %o %o %o %o', chain, network, rawTx, err.stack || err.message || err);
return res.status(500).send(err.message);
}
});
module.exports = {
router,
path: '/tx'
};