mirror of
https://github.com/Retropex/umbrel-bitcoin.git
synced 2025-05-12 19:20:49 +02:00
303 lines
7.8 KiB
JavaScript
303 lines
7.8 KiB
JavaScript
const bitcoindService = require('services/bitcoind.js');
|
|
const BitcoindError = require('models/errors.js').BitcoindError;
|
|
|
|
async function getBlockCount() {
|
|
const blockCount = await bitcoindService.getBlockCount();
|
|
|
|
return {blockCount: blockCount.result};
|
|
}
|
|
|
|
async function getConnectionsCount() {
|
|
const peerInfo = await bitcoindService.getPeerInfo();
|
|
|
|
var outBoundConnections = 0;
|
|
var inBoundConnections = 0;
|
|
var clearnetConnections = 0;
|
|
var torConnections = 0;
|
|
var i2pConnections = 0;
|
|
|
|
peerInfo.result.forEach(function(peer) {
|
|
if (peer.inbound === false) {
|
|
outBoundConnections++;
|
|
} else {
|
|
inBoundConnections++;
|
|
}
|
|
|
|
if (peer.network === "onion") {
|
|
torConnections++;
|
|
} else if (peer.network === "i2p") {
|
|
i2pConnections++;
|
|
} else {
|
|
// ipv4 and ipv6 are clearnet
|
|
clearnetConnections++;
|
|
}
|
|
});
|
|
|
|
const connections = {
|
|
total: inBoundConnections + outBoundConnections,
|
|
inbound: inBoundConnections,
|
|
outbound: outBoundConnections,
|
|
clearnet: clearnetConnections,
|
|
tor: torConnections,
|
|
i2p: i2pConnections
|
|
};
|
|
|
|
return connections;
|
|
}
|
|
|
|
async function getStatus() {
|
|
try {
|
|
await bitcoindService.help();
|
|
|
|
return {operational: true};
|
|
} catch (error) {
|
|
if (error instanceof BitcoindError) {
|
|
return {operational: false};
|
|
}
|
|
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// Return the max synced header for all connected peers or -1 if no data is available.
|
|
async function getMaxSyncHeader() {
|
|
const peerInfo = (await bitcoindService.getPeerInfo()).result;
|
|
|
|
if (peerInfo.length === 0) {
|
|
return -1;
|
|
}
|
|
|
|
const maxPeer = peerInfo.reduce(function(prev, current) {
|
|
return prev.syncedHeaders > current.syncedHeaders ? prev : current;
|
|
});
|
|
|
|
return maxPeer.syncedHeaders;
|
|
}
|
|
|
|
async function getMempoolInfo() {
|
|
return await bitcoindService.getMempoolInfo();
|
|
}
|
|
|
|
async function getLocalSyncInfo() {
|
|
const info = await bitcoindService.getBlockChainInfo();
|
|
|
|
var blockChainInfo = info.result;
|
|
var chain = blockChainInfo.chain;
|
|
var blockCount = blockChainInfo.blocks;
|
|
var headerCount = blockChainInfo.headers;
|
|
var percent = blockChainInfo.verificationprogress;
|
|
var pruned = blockChainInfo.pruned;
|
|
var pruneTargetSize = blockChainInfo.pruneTargetSize;
|
|
|
|
return {
|
|
chain,
|
|
percent,
|
|
currentBlock: blockCount,
|
|
headerCount: headerCount, // eslint-disable-line object-shorthand,
|
|
pruned,
|
|
pruneTargetSize
|
|
};
|
|
}
|
|
|
|
async function getSyncStatus() {
|
|
const maxPeerHeader = await getMaxSyncHeader();
|
|
const localSyncInfo = await getLocalSyncInfo();
|
|
|
|
if (maxPeerHeader > localSyncInfo.headerCount) {
|
|
localSyncInfo.headerCount = maxPeerHeader;
|
|
}
|
|
|
|
return localSyncInfo;
|
|
}
|
|
|
|
// TODO - consider using getNetworkInfo for info on proxy for ipv4 and ipv6
|
|
async function getVersion() {
|
|
const networkInfo = await bitcoindService.getNetworkInfo();
|
|
const unformattedVersion = networkInfo.result.subversion;
|
|
|
|
// Remove all non-digits or decimals.
|
|
const version = unformattedVersion.replace(/[^\d.]/g, '');
|
|
|
|
return {version: version}; // eslint-disable-line object-shorthand
|
|
}
|
|
|
|
async function getTransaction(txid) {
|
|
const transactionObj = await bitcoindService.getTransaction(txid);
|
|
|
|
return {
|
|
txid,
|
|
timestamp: transactionObj.result.time,
|
|
confirmations: transactionObj.result.confirmations,
|
|
blockhash: transactionObj.result.blockhash,
|
|
size: transactionObj.result.size,
|
|
input: transactionObj.result.vin.txid,
|
|
utxo: transactionObj.result.vout,
|
|
rawtx: transactionObj.result.hex
|
|
};
|
|
}
|
|
|
|
async function getNetworkInfo() {
|
|
const networkInfo = await bitcoindService.getNetworkInfo();
|
|
|
|
return networkInfo.result; // eslint-disable-line object-shorthand
|
|
}
|
|
|
|
async function getBlock(hash) {
|
|
const blockObj = await bitcoindService.getBlock(hash);
|
|
|
|
return {
|
|
block: hash,
|
|
confirmations: blockObj.result.confirmations,
|
|
size: blockObj.result.size,
|
|
height: blockObj.result.height,
|
|
blocktime: blockObj.result.time,
|
|
prevblock: blockObj.result.previousblockhash,
|
|
nextblock: blockObj.result.nextblockhash,
|
|
transactions: blockObj.result.tx
|
|
};
|
|
}
|
|
|
|
const memoizedGetFormattedBlock = () => {
|
|
const cache = {};
|
|
|
|
return async blockHeight => {
|
|
// cache cleanup
|
|
// 6 blocks/hr * 24 hrs/day * 7 days = 1008 blocks over 7 days
|
|
// plus some wiggle room in case weird difficulty adjustment or period of faster blocks
|
|
const CACHE_LIMIT = 1100;
|
|
while(Object.keys(cache).length > CACHE_LIMIT) {
|
|
const cacheItemToDelete = Object.keys(cache)[0];
|
|
delete cache[cacheItemToDelete];
|
|
}
|
|
|
|
if (blockHeight in cache) {
|
|
return cache[blockHeight];
|
|
} else {
|
|
let blockHash;
|
|
try {
|
|
({result: blockHash} = await bitcoindService.getBlockHash(blockHeight));
|
|
} catch (error) {
|
|
if (error instanceof BitcoindError) {
|
|
return error;
|
|
}
|
|
throw error;
|
|
}
|
|
|
|
const {result: block} = await bitcoindService.getBlock(blockHash);
|
|
|
|
cache[blockHeight] = {
|
|
hash: block.hash,
|
|
height: block.height,
|
|
numTransactions: block.tx.length,
|
|
confirmations: block.confirmations,
|
|
time: block.time,
|
|
size: block.size,
|
|
previousblockhash: block.previousblockhash
|
|
};
|
|
|
|
return cache[blockHeight];
|
|
}
|
|
};
|
|
};
|
|
|
|
const initializedMemoizedGetFormattedBlock = memoizedGetFormattedBlock();
|
|
|
|
|
|
async function getBlockRangeTransactionChunks(fromHeight, toHeight, blocksPerChunk) {
|
|
const {blocks} = await getBlocks(fromHeight, toHeight);
|
|
const chunks = [];
|
|
blocks.forEach((block, index) => {
|
|
const chunkIndex = Math.floor(index / blocksPerChunk);
|
|
if (!chunks[chunkIndex]) {
|
|
chunks[chunkIndex] = {
|
|
time: block.time,
|
|
numTransactions: 0,
|
|
};
|
|
}
|
|
chunks[chunkIndex].numTransactions += block.numTransactions;
|
|
});
|
|
|
|
return chunks;
|
|
}
|
|
|
|
async function getBlocks(fromHeight, toHeight) {
|
|
const blocks = [];
|
|
|
|
// loop from 'to height' till 'from Height'
|
|
for (let currentHeight = toHeight; currentHeight >= fromHeight; currentHeight--) {
|
|
// terminate loop if we reach the genesis block
|
|
if (currentHeight === 0) {
|
|
break;
|
|
}
|
|
|
|
try {
|
|
const formattedBlock = await initializedMemoizedGetFormattedBlock(currentHeight);
|
|
blocks.push(formattedBlock);
|
|
} catch(e) {
|
|
console.error('Error memoizing formatted blocks')
|
|
}
|
|
}
|
|
|
|
return {blocks};
|
|
}
|
|
|
|
async function getBlockHash(height) {
|
|
const getBlockHashObj = await bitcoindService.getBlockHash(height);
|
|
|
|
return {
|
|
hash: getBlockHashObj.result
|
|
};
|
|
}
|
|
|
|
async function nodeStatusDump() {
|
|
const blockchainInfo = await bitcoindService.getBlockChainInfo();
|
|
const networkInfo = await bitcoindService.getNetworkInfo();
|
|
const mempoolInfo = await bitcoindService.getMempoolInfo();
|
|
const miningInfo = await bitcoindService.getMiningInfo();
|
|
|
|
return {
|
|
blockchain_info: blockchainInfo.result,
|
|
network_info: networkInfo.result,
|
|
mempool: mempoolInfo.result,
|
|
mining_info: miningInfo.result
|
|
};
|
|
}
|
|
|
|
async function nodeStatusSummary() {
|
|
const blockchainInfo = await bitcoindService.getBlockChainInfo();
|
|
const networkInfo = await bitcoindService.getNetworkInfo();
|
|
const mempoolInfo = await bitcoindService.getMempoolInfo();
|
|
const miningInfo = await bitcoindService.getMiningInfo();
|
|
|
|
return {
|
|
difficulty: blockchainInfo.result.difficulty,
|
|
size: blockchainInfo.result.sizeOnDisk,
|
|
mempool: mempoolInfo.result.usage,
|
|
connections: networkInfo.result.connections,
|
|
networkhashps: miningInfo.result.networkhashps
|
|
};
|
|
}
|
|
|
|
async function stop() {
|
|
const stopResponse = await bitcoindService.stop();
|
|
return {stopResponse};
|
|
}
|
|
|
|
module.exports = {
|
|
getBlockHash,
|
|
getTransaction,
|
|
getBlock,
|
|
getBlockCount,
|
|
getBlocks,
|
|
getBlockRangeTransactionChunks,
|
|
getConnectionsCount,
|
|
getNetworkInfo,
|
|
getMempoolInfo,
|
|
getStatus,
|
|
getSyncStatus,
|
|
getVersion,
|
|
nodeStatusDump,
|
|
nodeStatusSummary,
|
|
stop
|
|
};
|