hivesmartcontracts/find_divergent_block.js

174 lines
5.1 KiB
JavaScript

/* eslint-disable no-console */
/* eslint-disable no-await-in-loop */
/* eslint-disable no-underscore-dangle */
/**
* Used to find which block the hashes diverged. */
require('dotenv').config();
const program = require('commander');
const axios = require('axios');
const conf = require('./config');
const { Database } = require('./libs/Database');
program
.option('-n, --node [url]', 'compare with given node', 'https://api.hive-engine.com/rpc/')
.option('-h, --head-only', 'compare only the head block')
.parse(process.argv);
const { node, headOnly } = program;
let id = 1;
async function getBlock(blockNumber, tries = 1) {
id += 1;
try {
return (await axios({
url: `${node}`,
method: 'POST',
headers: {
'content-type': 'application/json',
},
data: {
jsonrpc: '2.0', id, method: 'blockchain.getBlockInfo', params: { blockNumber },
},
})).data.result;
} catch (error) {
if (tries >= 3) {
console.error(error);
return null;
}
console.log(`Attempt #${tries} failed, retrying...`);
await new Promise(r => setTimeout(() => r(), 500));
return await getBlock(blockNumber, tries + 1);
}
}
const blockData = t => ({
refHiveBlockNumber: t.refHiveBlockNumber,
transactionId: t.transactionId,
sender: t.sender,
contract: t.contract,
payload: t.payload,
executedCodeHash: t.executedCodeHash,
logs: t.logs,
});
function getCompareData(block) {
return block;
// return block.transactions.map(blockData).concat( block.virtualTransactions.map(blockData),);
}
function getCompareString(block) {
return JSON.stringify(getCompareData(block));
}
function compareBlocks(block1, block2) {
return getCompareString(block1) === getCompareString(block2);
}
function printBlockDiff(block, mainBlock) {
// go through transactions, then virtual transactions, then overall hash
if (!block) {
console.log('This node missing block');
} else if (!mainBlock) {
console.log('Comparison node missing block');
} else {
for (let i = 0; i < block.transactions.length; i += 1) {
const txString = JSON.stringify(block.transactions[i]);
const mainTxString = JSON.stringify(mainBlock.transactions[i]);
if (txString === mainTxString) {
console.log(`Transaction ${i} matches`);
} else {
console.log(`Transaction ${i} mismatch: This: ${txString}, Main: ${mainTxString}`);
return;
}
}
for (let i = 0; i < block.virtualTransactions.length; i += 1) {
const txString = JSON.stringify(block.virtualTransactions[i]);
const mainTxString = JSON.stringify(mainBlock.virtualTransactions[i]);
if (txString === mainTxString) {
console.log(`Virtual Transaction ${i} matches`);
} else {
console.log(`Virtual Transaction ${i} mismatch: This: ${txString}, Main: ${mainTxString}`);
return;
}
}
}
}
async function findDivergentBlock() {
let exitCode = 0;
const {
databaseURL,
databaseName,
lightNode,
} = conf;
const database = new Database();
await database.init(databaseURL, databaseName);
const chain = database.database.collection('chain');
let block = (await chain.find().sort({ _id: -1 }).limit(1).toArray())[0];
let mainBlock;
if (headOnly) {
let retries = 0;
while (!mainBlock && retries < 10) {
await new Promise(r => setTimeout(r, 1000)); // sleep 1 second
mainBlock = await getBlock(block._id);
retries += 1;
}
if (!mainBlock) {
console.log(`failed to fetch block ${block._id} from ${node}`);
exitCode = 3;
} else if (mainBlock.hash === block.hash) {
console.log('ok');
} else {
console.log(`head block ${block._id} divergent from ${node}`);
console.log(`hash should be ${mainBlock.hash} is ${block.hash}`);
exitCode = 2;
}
} else {
let low = 0;
if (lightNode.enabled) {
const firstBlock = await chain.findOne({ blockNumber: { $gt: 0 } });
if (firstBlock) {
low = firstBlock.blockNumber;
}
}
let high = block._id;
const headBlock = high;
while (high - low >= 1) {
console.log(`low ${low} high ${high}`);
const check = Math.floor((low + high) / 2);
mainBlock = await getBlock(check);
if (!mainBlock) {
break;
}
block = await chain.findOne({ _id: check });
// Different comparison modes, uncomment desired comparison.
if (mainBlock.hash !== block.hash) {
// if (mainBlock.refHiveBlockNumber !== block.refHiveBlockNumber) {
// if (!compareBlocks(mainBlock, block)) {
high = check;
} else {
low = check + 1;
}
}
mainBlock = await getBlock(high);
block = await chain.findOne({ _id: high });
if (high === headBlock && high - low <= 0) {
console.log('ok');
} else if (high !== low) {
console.log('not caught up or error fetching block');
exitCode = 1;
} else {
console.log('### high block');
printBlockDiff(block, mainBlock);
console.log(`divergent block id at ${high}`);
exitCode = 2;
}
}
database.close();
process.exit(exitCode);
}
findDivergentBlock();