hivesmartcontracts/sync_hashes.js

111 lines
4.2 KiB
JavaScript

/* eslint-disable no-console */
/* eslint-disable no-await-in-loop */
/**
* Used to sync local hashes to one that is running, which relies on having a window
* in which we can nab all hashes before any of them change on a live node. This will sync
* individual table hashes which are part of the block hashes, and can be corrupted if
* the node is interrupted and processes a block partially. Adding transactions will mitigate
* this issue, but the possibility is still there.
*
* Note that after this, you will need to also update the last block's hashes using mongo to
* match.
*
* The process is
* 1) Have local node up to date with main node, with hash verification off.
* 2) run hash script
* 3) turn on hash verification, which will cause the local node to stall
* 4) update the last block's hashes to match the main node
* 5) resume, and let it catch up, hashes will now be the same.
*/
require('dotenv').config();
const axios = require('axios');
const conf = require('./config');
const { Database } = require('./libs/Database');
let id = 1;
async function latestBlock() {
id += 1;
return (await axios({
url: 'https://api.hive-engine.com/rpc/blockchain',
method: 'POST',
headers: {
'content-type': 'application/json',
},
data: { jsonrpc: '2.0', id, method: 'getLatestBlockInfo' },
})).data.result;
}
const contractNames = ['tokens', 'claimdrops', 'distribution', 'nftmarket', 'mining', 'packmanager', 'nft', 'airdrops', 'inflation', 'marketmaker', 'botcontroller', 'market', 'crittermanager', 'hivepegged'];
async function fetchContractHashes() {
const tables = await Promise.all(contractNames.map(contractName => (async () => {
id += 1;
const contract = (await axios({
url: 'https://api.hive-engine.com/rpc/contracts',
method: 'POST',
headers: {
'content-type': 'application/json',
},
data: {
jsonrpc: '2.0', id, method: 'getContract', params: { name: contractName },
},
})).data.result;
return contract.tables;
})()));
return tables;
}
async function getHashes() {
const block = await latestBlock();
console.log(block.blockNumber);
const hashes1 = await fetchContractHashes();
const hashes2 = await fetchContractHashes();
const blockAfterFetch = await latestBlock();
console.log(`Block before hash fetch: ${block.blockNumber}. Block after: ${blockAfterFetch.blockNumber}`);
console.log(JSON.stringify(hashes1) === JSON.stringify(hashes2));
if (block.blockNumber === blockAfterFetch.blockNumber
&& JSON.stringify(hashes1) === JSON.stringify(hashes2)) {
// hash match, put into database
const {
databaseURL,
databaseName,
} = conf;
const database = new Database();
await database.init(databaseURL, databaseName);
const contracts = database.database.collection('contracts');
const chain = database.database.collection('chain');
// eslint-disable-next-line no-underscore-dangle
const localBlock = (await chain.find().sort({ _id: -1 }).limit(1).toArray())[0]._id;
for (let i = 0; i < contractNames.length; i += 1) {
const contract = contractNames[i];
// eslint-disable-next-line no-underscore-dangle
const contractInDb = await contracts.findOne({ _id: contract });
console.log(`Checking contract ${contract}`);
const tables = hashes1[i];
const tableKeys = Object.keys(tables);
for (let j = 0; j < tableKeys.length; j += 1) {
const tableName = tableKeys[j];
if (contractInDb.tables[tableName].hash !== tables[tableName].hash) {
console.log(`Would replace table hash for ${tableName} from ${contractInDb.tables[tableName].hash} to ${tables[tableName].hash}`);
contractInDb.tables[tableName].hash = tables[tableName].hash;
// uncomment to actually update the hashes of the contracts
// await contracts.updateOne({ _id: contract }, { $set: contractInDb });
}
}
}
// eslint-disable-next-line no-underscore-dangle
const localBlockAfterFetch = (await chain.find().sort({ _id: -1 }).limit(1).toArray())[0]._id;
console.log(`Local block before hash fetch: ${localBlock}. Block after: ${localBlockAfterFetch}`);
database.close();
}
}
getHashes();