Compare commits

...

26 Commits

Author SHA1 Message Date
Dark Sun d3290d5b1b fix 2023-12-19 22:16:33 +03:00
Dark Sun 576ba19e76 fix 2023-12-19 22:12:26 +03:00
Dark Sun 09fb3c0eba update 2023-12-19 22:08:38 +03:00
Dark Sun 2303fcea9e rollback 2023-12-18 22:22:57 +03:00
Dark Sun e0bf1b534b update 2023-12-18 22:01:35 +03:00
Dark Sun 4481ebcbc0 update 2023-12-18 18:42:44 +03:00
Dark Sun f3ab699f83 update 2023-12-18 17:31:42 +03:00
Dark Sun 7bae572ec9 update 2023-12-18 14:16:00 +03:00
Dark Sun 5f1fb9490c push 2023-12-18 13:50:48 +03:00
Dark Sun 33ec53dc27 update 2023-12-09 20:29:20 +03:00
Dark Sun ad3ca3e988 update 2023-12-08 23:53:35 +03:00
Dark Sun a02917b922 update indexes 2023-12-08 23:25:38 +03:00
Dark Sun c49e6c20e7 update 2023-12-07 23:27:30 +03:00
Dark Sun e557e7d618 update 2023-12-07 17:48:31 +03:00
Dark Sun d84642e8be push 2023-12-07 16:58:28 +03:00
Dark Sun c0c857bc5b update 2023-12-07 16:55:00 +03:00
Dark Sun 20856ecec4 update 2023-12-07 16:48:37 +03:00
Dark Sun c9e821d2f4 update 2023-12-07 16:23:42 +03:00
Dark Sun fe48b146b3 fix typo 2023-12-07 14:39:24 +03:00
Dark Sun 1ef1514b15 update template 2023-12-07 14:02:05 +03:00
Igor Lins e Silva 32145b6e1c Update README.md 2023-10-10 21:21:35 -03:00
Bruno Leal 90d489b600 fix: repair fork index not found 2023-09-27 16:54:35 -03:00
Igor Lins e Silva 69646d9660 cleanup 2023-09-25 21:56:41 -03:00
Bruno Leal fb2fc20d1d Merge branch 'main' of https://github.com/eosrio/hyperion-history-api into main 2023-09-25 15:07:17 -03:00
Bruno Leal b7e17f9dc7 fix: fix progress status by amout of lines not chunks 2023-09-25 15:07:00 -03:00
Igor Lins e Silva b94f99d552 Merge pull request #142 from eosrio/large-tx-fix
Large tx-sig fix
2023-09-25 01:33:31 -03:00
21 changed files with 1047 additions and 173 deletions
+2 -2
View File
@@ -68,8 +68,8 @@ helpers/**/*.js.map
interfaces/**/*.js
interfaces/**/*.js.map
modules/**/*.js
modules/**/*.js.map
#modules/**/*.js
#modules/**/*.js.map
addons/**/*.js
addons/**/*.js.map
+1 -1
View File
@@ -91,7 +91,7 @@ to collect action traces and state deltas. Provides data via websocket to the in
#### 2.7 Hyperion Stream Client (optional)
Web and Node.js client for real-time streaming on enabled hyperion
providers. [Documentation](https://hyperion.docs.eosrio.io/stream_client/)
providers. [Documentation](https://hyperion.docs.eosrio.io/dev/stream_client/)
#### 2.8 Hyperion Plugins (optional)
@@ -12,7 +12,14 @@ export const extendedActions = new Set([
"buyrambytes",
"delegatebw",
"undelegatebw",
"voteproducer"
"voteproducer",
'statement',
'decision',
'batch',
'act',
'exec',
'votefor',
'voteagainst'
]);
export const primaryTerms = [
+68 -1
View File
@@ -183,7 +183,74 @@ export const action = {
"unstake_net_quantity": {"type": "float"},
"amount": {"type": "float"}
}
}
},
// eosio::undelegatebw
// "@exec": {
// "properties": {
// "executer": {"type": "keyword"},
// "coopname": {"type": "keyword"},
// "decision_id": {"type": "long"},
// }
// },
// "@statement": {
// "properties": {
// "coopname": {"type": "keyword"},
// "username": {"type": "keyword"},
// "action": {"type": "keyword"},
// "decision_id": {"type": "long"},
// "batch_id": {"type": "long"},
// "statement": {"enabled": false},
// }
// },
// "@act": {
// "properties": {
// "coopname": {"type": "keyword"},
// "username": {"type": "keyword"},
// "action": {"type": "keyword"},
// "decision_id": {"type": "long"},
// "batch_id": {"type": "long"},
// "act": {"enabled": false},
// }
// },
// "@decision": {
// "properties": {
// "coopname": {"type": "keyword"},
// "username": {"type": "keyword"},
// "action": {"type": "keyword"},
// "decision_id": {"type": "long"},
// "batch_id": {"type": "long"},
// "decision": {"enabled": false},
// }
// },
// "@batch": {
// "properties": {
// "coopname": {"type": "keyword"},
// "action": {"type": "keyword"},
// "batch_id": {"type": "long"},
// }
// },
// // eosio::votefor
// "@votefor": {
// "properties": {
// "coopname": {"type": "keyword"},
// "member": {"type": "keyword"},
// "decision_id": {"type": "long"}
// }
// },
// "@voteagainst": {
// "properties": {
// "coopname": {"type": "keyword"},
// "member": {"type": "keyword"},
// "decision_id": {"type": "long"}
// }
// }
}
}
};
+6 -6
View File
@@ -20,11 +20,11 @@ readdirSync(chainsRoot)
}
});
apps.push({
name: 'hyperion-governor',
namespace: 'hyperion',
script: 'governor/server/index.js',
watch: false,
});
// apps.push({
// name: 'hyperion-governor',
// namespace: 'hyperion',
// script: 'governor/server/index.js',
// watch: false,
// });
module.exports = {apps};
+39
View File
@@ -0,0 +1,39 @@
const hyperionModule = {
chain: "*",
contract: '*',
action: 'act',
parser_version: ['3.2', '2.1','1.8','1.7'],
defineQueryPrefix: 'act',
mappings: {
action: {
"@act": {
"properties": {
"coopname": {"type": "keyword"},
"username": {"type": "keyword"},
"action": {"type": "keyword"},
"decision_id": {"type": "long"},
"batch_id": {"type": "long"},
"act": {"enabled": false}
}
}
}
},
handler: (action) => {
const data = action['act']['data'];
action['@act'] = {
coopname: data.coopname,
username: data.username,
action: data.action,
decision_id: data.decision_id,
batch_id: data.batch_id,
act: data.act
};
}
};
module.exports = {hyperionModule};
+33
View File
@@ -0,0 +1,33 @@
const hyperionModule = {
chain: "*",
contract: '*',
action: 'batch',
parser_version: ['3.2', '2.1','1.8','1.7'],
defineQueryPrefix: 'batch',
mappings: {
action: {
"@batch": {
"properties": {
"coopname": {"type": "keyword"},
"action": {"type": "keyword"},
"batch_id": {"type": "long"}
}
}
}
},
handler: (action) => {
const data = action['act']['data'];
action['@batch'] = {
coopname: data.coopname,
action: data.action,
batch_id: data.batch_id,
};
}
};
module.exports = {hyperionModule};
+39
View File
@@ -0,0 +1,39 @@
const hyperionModule = {
chain: "*",
contract: '*',
action: 'decision',
parser_version: ['3.2', '2.1','1.8','1.7'],
defineQueryPrefix: 'decision',
mappings: {
action: {
"@decision": {
"properties": {
"coopname": {"type": "keyword"},
"username": {"type": "keyword"},
"action": {"type": "keyword"},
"decision_id": {"type": "long"},
"batch_id": {"type": "long"},
"decision": {"enabled": false}
}
}
}
},
handler: (action) => {
const data = action['act']['data'];
action['@decision'] = {
coopname: data.coopname,
username: data.username,
action: data.action,
decision_id: data.decision_id,
batch_id: data.batch_id,
decision: data.decision
};
}
};
module.exports = {hyperionModule};
+33
View File
@@ -0,0 +1,33 @@
const hyperionModule = {
chain: "*",
contract: '*',
action: 'exec',
parser_version: ['3.2', '2.1','1.8','1.7'],
defineQueryPrefix: 'exec',
mappings: {
action: {
"@exec": {
"properties": {
"executer": {"type": "keyword"},
"coopname": {"type": "keyword"},
"decision_id": {"type": "long"}
}
}
}
},
handler: (action) => {
const data = action['act']['data'];
action['@exec'] = {
executer: data.executer,
coopname: data.coopname,
decision_id: data.decision_id,
};
}
};
module.exports = {hyperionModule};
+39
View File
@@ -0,0 +1,39 @@
const hyperionModule = {
chain: "*",
contract: '*',
action: 'statement',
parser_version: ['3.2', '2.1','1.8','1.7'],
defineQueryPrefix: 'statement',
mappings: {
action: {
"@statement": {
"properties": {
"coopname": {"type": "keyword"},
"username": {"type": "keyword"},
"action": {"type": "keyword"},
"decision_id": {"type": "long"},
"batch_id": {"type": "long"},
"statement": {"enabled": false}
}
}
}
},
handler: (action) => {
const data = action['act']['data'];
action['@statement'] = {
coopname: data.coopname,
username: data.username,
action: data.action,
decision_id: data.decision_id,
batch_id: data.batch_id,
statement: data.statement
};
}
};
module.exports = {hyperionModule};
+33
View File
@@ -0,0 +1,33 @@
const hyperionModule = {
chain: "*",
contract: '*',
action: 'voteagainst',
parser_version: ['3.2', '2.1','1.8','1.7'],
defineQueryPrefix: 'voteagainst',
mappings: {
action: {
"@voteagainst": {
"properties": {
"coopname": {"type": "keyword"},
"member": {"type": "keyword"},
"decision_id": {"type": "long"}
}
}
}
},
handler: (action) => {
const data = action['act']['data'];
action['@voteagainst'] = {
coopname: data.coopname,
member: data.member,
decision_id: data.decision_id
};
}
};
module.exports = {hyperionModule};
+33
View File
@@ -0,0 +1,33 @@
const hyperionModule = {
chain: "*",
contract: '*',
action: 'votefor',
parser_version: ['3.2', '2.1','1.8','1.7'],
defineQueryPrefix: 'votefor',
mappings: {
action: {
"@votefor": {
"properties": {
"coopname": {"type": "keyword"},
"member": {"type": "keyword"},
"decision_id": {"type": "long"}
}
}
}
},
handler: (action) => {
const data = action['act']['data'];
action['@votefor'] = {
coopname: data.coopname,
member: data.member,
decision_id: data.decision_id
};
}
};
module.exports = {hyperionModule};
+2 -2
View File
@@ -53,12 +53,12 @@ class HyperionModuleLoader {
const key = `${_module.contract}::${_module.action}`;
if (this.chainMappings.has(key)) {
if (this.chainMappings.get(key) === '*' && _module.chain === chainID) {
// console.log('Overwriting module ' + key + ' for ' + _module.chain);
console.log('Overwriting module ' + key + ' for ' + _module.chain);
this.includeModule(_module);
this.chainMappings.set(key, _module.chain);
}
} else {
// console.log('Including module ' + key + ' for ' + _module.chain);
console.log('Including module ' + key + ' for ' + _module.chain);
this.includeModule(_module);
this.chainMappings.set(key, _module.chain);
}
+462 -160
View File
@@ -1,36 +1,47 @@
import {Command} from 'commander';
import {readFileSync} from "node:fs";
import {Client, estypes} from "@elastic/elasticsearch";
import { Command } from 'commander';
import { readFileSync } from 'node:fs';
import { Client, estypes } from '@elastic/elasticsearch';
// @ts-ignore
import cliProgress from "cli-progress";
import {JsonRpc} from 'eosjs';
import cliProgress from 'cli-progress';
import { JsonRpc } from 'eosjs';
import fetch from 'cross-fetch';
import {existsSync, mkdirSync, writeFileSync} from "fs";
import {HyperionBlock} from "./repair-cli/interfaces.js";
import { existsSync, mkdirSync, writeFileSync } from 'fs';
import { HyperionBlock } from './repair-cli/interfaces.js';
import {
getBlocks,
getFirstIndexedBlock,
getLastIndexedBlock,
initESClient,
readChainConfig,
readConnectionConfig
} from "./repair-cli/functions.js";
import {SearchResponse} from "@elastic/elasticsearch/api/types";
readConnectionConfig,
} from './repair-cli/functions.js';
import { SearchResponse } from '@elastic/elasticsearch/api/types';
import {WebSocket} from 'ws';
import {block} from "../definitions/index-templates";
import { WebSocket } from 'ws';
import { block } from '../definitions/index-templates';
const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);
const progressBar = new cliProgress.SingleBar(
{},
cliProgress.Presets.shades_classic
);
const program = new Command();
const errorRanges: any[] = [];
let pendingBlock: HyperionBlock | null = null;
const missingBlocks: {
start: number,
end: number
start: number;
end: number;
}[] = [];
async function run(client: Client, rpc: JsonRpc, indexName: string, blockInit: number, firstBlock: number, qtdTotal: any, loop: any = 1) {
async function run(
client: Client,
rpc: JsonRpc,
indexName: string,
blockInit: number,
firstBlock: number,
qtdTotal: any,
loop: any = 1
) {
let blockInitial = blockInit;
let blockFinal = blockInitial - qtdTotal;
const tRef = process.hrtime.bigint();
@@ -40,33 +51,44 @@ async function run(client: Client, rpc: JsonRpc, indexName: string, blockInit: n
blockFinal = firstBlock;
}
try {
let {body} = await getBlocks(client, indexName, blockInitial, blockFinal, qtdTotal);
let {hits: {hits}} = body;
let { body } = await getBlocks(
client,
indexName,
blockInitial,
blockFinal,
qtdTotal
);
let {
hits: { hits },
} = body;
const blocks = hits.map((obj: any) => obj._source);
await findForksOnRange(blocks, rpc);
blockInitial = blockFinal;
blockFinal = blockInitial - qtdTotal;
progressBar.update(i);
} catch (e: any) {
console.log("Error: ", e);
console.log('Error: ', e);
process.exit(1);
}
}
progressBar.stop();
console.log("=========== Forked Ranges ===========");
console.log('=========== Forked Ranges ===========');
console.table(errorRanges);
console.log("=========== Missing Ranges ==========");
console.log('=========== Missing Ranges ==========');
console.table(missingBlocks);
const tDiff = Number(process.hrtime.bigint() - tRef) / 1000000;
console.log(`Total time: ${Math.round(tDiff / 1000)}s`);
}
async function findForksOnRange(blocks: HyperionBlock[], rpc: JsonRpc) {
const removals: Set<string> = new Set();
let start = null;
let end = null;
if (blocks.length > 0 && pendingBlock && blocks[0].block_num !== pendingBlock.block_num) {
if (
blocks.length > 0 &&
pendingBlock &&
blocks[0].block_num !== pendingBlock.block_num
) {
blocks.unshift(pendingBlock);
}
@@ -83,10 +105,14 @@ async function findForksOnRange(blocks: HyperionBlock[], rpc: JsonRpc) {
i++;
// console.log('previous -> ', previousBlock.block_num);
if (previousBlock.block_num !== currentBlockNumber - 1) {
console.log(`\nBlock number mismatch, expected: ${currentBlockNumber - 1} got ${previousBlock.block_num}`);
console.log(
`\nBlock number mismatch, expected: ${
currentBlockNumber - 1
} got ${previousBlock.block_num}`
);
missingBlocks.push({
start: previousBlock.block_num + 1,
end: currentBlockNumber - 1
end: currentBlockNumber - 1,
});
continue;
}
@@ -107,7 +133,7 @@ async function findForksOnRange(blocks: HyperionBlock[], rpc: JsonRpc) {
removals.add(currentBlock.block_id);
} else {
end = currentBlockNumber + 1;
const range = {start, end, ids: [...removals]};
const range = { start, end, ids: [...removals] };
errorRanges.push(range);
removals.clear();
// console.log(`\n ⚠️⚠️ Forked at ${range.start} to ${range.end}`);
@@ -129,7 +155,7 @@ async function scanChain(chain: string, args: any) {
const client = initESClient(config);
const jsonRpc = new JsonRpc(config.chains[chain].http, {fetch});
const jsonRpc = new JsonRpc(config.chains[chain].http, { fetch });
const ping = await client.ping();
@@ -165,12 +191,20 @@ async function scanChain(chain: string, args: any) {
const numberOfBatches = Math.ceil(totalBlocks / batchSize);
console.log("Range:", firstBlock, lastBlock);
console.log("Total Blocks:", totalBlocks);
console.log("Batch Size:", batchSize);
console.log("Number of Batches:", numberOfBatches);
console.log('Range:', firstBlock, lastBlock);
console.log('Total Blocks:', totalBlocks);
console.log('Batch Size:', batchSize);
console.log('Number of Batches:', numberOfBatches);
await run(client, jsonRpc, blockIndex, lastBlock, firstBlock, batchSize, numberOfBatches);
await run(
client,
jsonRpc,
blockIndex,
lastBlock,
firstBlock,
batchSize,
numberOfBatches
);
console.log(`Finished checking forked blocks!`);
if (errorRanges.length > 0 || missingBlocks.length > 0) {
@@ -180,7 +214,9 @@ async function scanChain(chain: string, args: any) {
}
if (errorRanges.length > 0) {
let path = `.repair/${chain}-${firstBlock + 2}-${lastBlock}-forked-blocks.json`;
let path = `.repair/${chain}-${
firstBlock + 2
}-${lastBlock}-forked-blocks.json`;
if (args.outFile) {
path = args.outFile + '-forked-blocks.json';
}
@@ -188,7 +224,9 @@ async function scanChain(chain: string, args: any) {
}
if (missingBlocks.length > 0) {
let path = `.repair/${chain}-${firstBlock + 2}-${lastBlock}-missing-blocks.json`;
let path = `.repair/${chain}-${
firstBlock + 2
}-${lastBlock}-missing-blocks.json`;
if (args.outFile) {
path = args.outFile + '-missing-blocks.json';
}
@@ -209,7 +247,6 @@ async function repairMissing(chain: string, file: string, args: any) {
}
async function repairChain(chain: string, file: string, args: any) {
const chainConfig = readChainConfig(chain);
const config = readConnectionConfig();
const client = initESClient(config);
@@ -243,7 +280,6 @@ async function repairChain(chain: string, file: string, args: any) {
let deletePermissions = 0;
for (const range of forkedBlocks) {
// ACTIONS
const searchActions = {
index: `${chain}-action-${chainConfig.settings.index_version}-*`,
@@ -252,22 +288,52 @@ async function repairChain(chain: string, file: string, args: any) {
body: {
query: {
bool: {
must: [{range: {block_num: {lte: range.start, gte: range.end}}}]
}
}
}
must: [
{
range: {
block_num: {
lte: range.start,
gte: range.end,
},
},
},
],
},
},
},
};
if (args.dry) {
const resultActions = await client.search<any>(searchActions);
if ((resultActions.body.hits.total as estypes.SearchTotalHits)?.value > 0) {
deleteActions += (resultActions.body.hits.total as estypes.SearchTotalHits)?.value;
if (
(resultActions.body.hits.total as estypes.SearchTotalHits)
?.value > 0
) {
deleteActions += (
resultActions.body.hits.total as estypes.SearchTotalHits
)?.value;
}
} else {
delete searchActions.size;
const deletedActionsResult = await client.deleteByQuery(searchActions);
if (deletedActionsResult && deletedActionsResult.body.deleted && deletedActionsResult.body.deleted > 0) {
deleteActions += deletedActionsResult.body.deleted;
const indexExists = await client.indices.exists({
index: searchActions.index,
});
if (indexExists.body) {
delete searchActions.size;
const deletedActionsResult = await client.deleteByQuery(
searchActions
);
if (
deletedActionsResult &&
deletedActionsResult.body.deleted &&
deletedActionsResult.body.deleted > 0
) {
deleteActions += deletedActionsResult.body.deleted;
}
} else {
console.log(
`Index ${searchActions.index} doesn't exist. Unable to delete.`
);
}
}
@@ -279,22 +345,51 @@ async function repairChain(chain: string, file: string, args: any) {
body: {
query: {
bool: {
must: [{range: {block_num: {lte: range.start, gte: range.end}}}]
}
}
}
}
must: [
{
range: {
block_num: {
lte: range.start,
gte: range.end,
},
},
},
],
},
},
},
};
if (args.dry) {
const resultDeltas = await client.search<any>(searchDeltas);
if ((resultDeltas.body.hits.total as estypes.SearchTotalHits)?.value > 0) {
deleteDeltas += (resultDeltas.body.hits.total as estypes.SearchTotalHits)?.value;
if (
(resultDeltas.body.hits.total as estypes.SearchTotalHits)
?.value > 0
) {
deleteDeltas += (
resultDeltas.body.hits.total as estypes.SearchTotalHits
)?.value;
}
} else {
delete searchDeltas.size;
const deletedDeltasResult = await client.deleteByQuery(searchDeltas);
if (deletedDeltasResult && deletedDeltasResult.body.deleted && deletedDeltasResult.body.deleted > 0) {
deleteDeltas += deletedDeltasResult.body.deleted;
const indexExists = await client.indices.exists({
index: searchDeltas.index,
});
if (indexExists.body) {
delete searchDeltas.size;
const deletedDeltasResult = await client.deleteByQuery(
searchDeltas
);
if (
deletedDeltasResult &&
deletedDeltasResult.body.deleted &&
deletedDeltasResult.body.deleted > 0
) {
deleteDeltas += deletedDeltasResult.body.deleted;
}
} else {
console.log(
`Index ${searchDeltas.index} doesn't exist. Unable to delete.`
);
}
}
@@ -306,23 +401,49 @@ async function repairChain(chain: string, file: string, args: any) {
body: {
query: {
bool: {
must: [{range: {block: {lte: range.start, gte: range.end}}}]
}
}
}
}
must: [
{
range: {
block: { lte: range.start, gte: range.end },
},
},
],
},
},
},
};
if (args.dry) {
const resultAbis = await client.search<any>(searchAbis);
if ((resultAbis.body.hits.total as estypes.SearchTotalHits)?.value > 0) {
deleteAbis += (resultAbis.body.hits.total as estypes.SearchTotalHits)?.value;
console.log('ABIs', {lte: range.start, gte: range.end});
if (
(resultAbis.body.hits.total as estypes.SearchTotalHits)?.value >
0
) {
deleteAbis += (
resultAbis.body.hits.total as estypes.SearchTotalHits
)?.value;
console.log('ABIs', { lte: range.start, gte: range.end });
}
} else {
delete searchAbis.size;
const deletedAbisResult = await client.deleteByQuery(searchAbis);
if (deletedAbisResult && deletedAbisResult.body.deleted && deletedAbisResult.body.deleted > 0) {
deleteAbis += deletedAbisResult.body.deleted;
const indexExists = await client.indices.exists({
index: searchAbis.index,
});
if (indexExists.body) {
delete searchAbis.size;
const deletedAbisResult = await client.deleteByQuery(
searchAbis
);
if (
deletedAbisResult &&
deletedAbisResult.body.deleted &&
deletedAbisResult.body.deleted > 0
) {
deleteAbis += deletedAbisResult.body.deleted;
}
} else {
console.log(
`Index ${searchAbis.index} doesn't exist. Unable to delete.`
);
}
}
@@ -334,25 +455,61 @@ async function repairChain(chain: string, file: string, args: any) {
body: {
query: {
bool: {
must: [{range: {block_num: {lte: range.start, gte: range.end}}}]
}
}
}
}
must: [
{
range: {
block_num: {
lte: range.start,
gte: range.end,
},
},
},
],
},
},
},
};
if (args.dry) {
const resultAccounts = await client.search<any>(searchAccounts);
if ((resultAccounts.body.hits.total as estypes.SearchTotalHits)?.value > 0) {
console.log('[WARNING]', (resultAccounts.body.hits.total as estypes.SearchTotalHits)?.value, 'accounts needs to be updated');
deleteAccounts += (resultAccounts.body.hits.total as estypes.SearchTotalHits)?.value;
if (
(resultAccounts.body.hits.total as estypes.SearchTotalHits)
?.value > 0
) {
console.log(
'[WARNING]',
(resultAccounts.body.hits.total as estypes.SearchTotalHits)
?.value,
'accounts needs to be updated'
);
deleteAccounts += (
resultAccounts.body.hits.total as estypes.SearchTotalHits
)?.value;
}
} else {
const indexExists = await client.indices.exists({
index: searchAccounts.index,
});
if (indexExists.body) {
delete searchAccounts.size;
const deletedAccountsResult = await client.deleteByQuery(searchAccounts);
if (deletedAccountsResult && deletedAccountsResult.body.deleted && deletedAccountsResult.body.deleted > 0) {
const deletedAccountsResult = await client.deleteByQuery(
searchAccounts
);
if (
deletedAccountsResult &&
deletedAccountsResult.body.deleted &&
deletedAccountsResult.body.deleted > 0
) {
deleteAccounts += deletedAccountsResult.body.deleted;
}
}else {
console.log(
`Index ${searchAccounts.index} doesn't exist. Unable to delete.`
);
}
}
// VOTERS
const searchVoters = {
@@ -362,25 +519,61 @@ async function repairChain(chain: string, file: string, args: any) {
body: {
query: {
bool: {
must: [{range: {block_num: {lte: range.start, gte: range.end}}}]
}
}
}
}
must: [
{
range: {
block_num: {
lte: range.start,
gte: range.end,
},
},
},
],
},
},
},
};
if (args.dry) {
const resultVoters = await client.search<any>(searchVoters);
if ((resultVoters.body.hits.total as estypes.SearchTotalHits)?.value > 0) {
console.log('[WARNING]', (resultVoters.body.hits.total as estypes.SearchTotalHits)?.value, 'voters needs to be updated');
deleteVoters += (resultVoters.body.hits.total as estypes.SearchTotalHits)?.value;
if (
(resultVoters.body.hits.total as estypes.SearchTotalHits)
?.value > 0
) {
console.log(
'[WARNING]',
(resultVoters.body.hits.total as estypes.SearchTotalHits)
?.value,
'voters needs to be updated'
);
deleteVoters += (
resultVoters.body.hits.total as estypes.SearchTotalHits
)?.value;
}
} else {
const indexExists = await client.indices.exists({
index: searchVoters.index,
});
if (indexExists.body) {
delete searchVoters.size;
const deletedVotersResult = await client.deleteByQuery(searchVoters);
if (deletedVotersResult && deletedVotersResult.body.deleted && deletedVotersResult.body.deleted > 0) {
const deletedVotersResult = await client.deleteByQuery(
searchVoters
);
if (
deletedVotersResult &&
deletedVotersResult.body.deleted &&
deletedVotersResult.body.deleted > 0
) {
deleteVoters += deletedVotersResult.body.deleted;
}
}else {
console.log(
`Index ${searchVoters.index} doesn't exist. Unable to delete.`
);
}
}
// PROPOSALS
const searchProposals = {
@@ -390,25 +583,56 @@ async function repairChain(chain: string, file: string, args: any) {
body: {
query: {
bool: {
must: [{range: {block_num: {lte: range.start, gte: range.end}}}]
}
}
}
}
must: [
{
range: {
block_num: {
lte: range.start,
gte: range.end,
},
},
},
],
},
},
},
};
if (args.dry) {
const resultProposals = await client.search<any>(searchProposals);
if ((resultProposals.body.hits.total as estypes.SearchTotalHits)?.value > 0) {
console.log('[WARNING]', (resultProposals.body.hits.total as estypes.SearchTotalHits)?.value, 'proposals needs to be updated');
deleteProposals += (resultProposals.body.hits.total as estypes.SearchTotalHits)?.value;
if (
(resultProposals.body.hits.total as estypes.SearchTotalHits)
?.value > 0
) {
console.log(
'[WARNING]',
(resultProposals.body.hits.total as estypes.SearchTotalHits)
?.value,
'proposals needs to be updated'
);
deleteProposals += (
resultProposals.body.hits.total as estypes.SearchTotalHits
)?.value;
}
} else {
const indexExists = await client.indices.exists({index: searchProposals.index});
if (indexExists.body) {
delete searchProposals.size;
const deletedProposalsResult = await client.deleteByQuery(searchProposals);
if (deletedProposalsResult && deletedProposalsResult.body.deleted && deletedProposalsResult.body.deleted > 0) {
const deletedProposalsResult = await client.deleteByQuery(
searchProposals
);
if (
deletedProposalsResult &&
deletedProposalsResult.body.deleted &&
deletedProposalsResult.body.deleted > 0
) {
deleteProposals += deletedProposalsResult.body.deleted;
}
}
}else { console.log(`Index ${searchProposals.index} doesn't exist. Unable to delete.`);}
}
// LINKS
const searchLinks = {
@@ -418,25 +642,52 @@ async function repairChain(chain: string, file: string, args: any) {
body: {
query: {
bool: {
must: [{range: {block_num: {lte: range.start, gte: range.end}}}]
}
}
}
}
must: [
{
range: {
block_num: {
lte: range.start,
gte: range.end,
},
},
},
],
},
},
},
};
if (args.dry) {
const resultLinks = await client.search<any>(searchLinks);
if ((resultLinks.body.hits.total as estypes.SearchTotalHits)?.value > 0) {
console.log('[WARNING]', (resultLinks.body.hits.total as estypes.SearchTotalHits)?.value, 'links needs to be updated');
deleteLinks += (resultLinks.body.hits.total as estypes.SearchTotalHits)?.value;
if (
(resultLinks.body.hits.total as estypes.SearchTotalHits)
?.value > 0
) {
console.log(
'[WARNING]',
(resultLinks.body.hits.total as estypes.SearchTotalHits)
?.value,
'links needs to be updated'
);
deleteLinks += (
resultLinks.body.hits.total as estypes.SearchTotalHits
)?.value;
}
} else {
const indexExists = await client.indices.exists({index: searchLinks.index});
if (indexExists.body) {
delete searchLinks.size;
const deletedLinksResult = await client.deleteByQuery(searchLinks);
if (deletedLinksResult && deletedLinksResult.body.deleted && deletedLinksResult.body.deleted > 0) {
if (
deletedLinksResult &&
deletedLinksResult.body.deleted &&
deletedLinksResult.body.deleted > 0
) {
deleteLinks += deletedLinksResult.body.deleted;
}
}
}else { console.log(`Index ${searchLinks.index} doesn't exist. Unable to delete.`);}
}
// PERMISSIONS
const searchPermissions = {
@@ -446,38 +697,78 @@ async function repairChain(chain: string, file: string, args: any) {
body: {
query: {
bool: {
must: [{range: {block_num: {lte: range.start, gte: range.end}}}]
}
}
}
}
must: [
{
range: {
block_num: {
lte: range.start,
gte: range.end,
},
},
},
],
},
},
},
};
if (args.dry) {
const resultPermissions = await client.search<any>(searchPermissions);
if ((resultPermissions.body.hits.total as estypes.SearchTotalHits)?.value > 0) {
console.log('[WARNING]', (resultPermissions.body.hits.total as estypes.SearchTotalHits)?.value, 'permissions needs to be updated');
console.log({lte: range.start, gte: range.end});
deletePermissions += (resultPermissions.body.hits.total as estypes.SearchTotalHits)?.value;
const resultPermissions = await client.search<any>(
searchPermissions
);
if (
(resultPermissions.body.hits.total as estypes.SearchTotalHits)
?.value > 0
) {
console.log(
'[WARNING]',
(
resultPermissions.body.hits
.total as estypes.SearchTotalHits
)?.value,
'permissions needs to be updated'
);
console.log({ lte: range.start, gte: range.end });
deletePermissions += (
resultPermissions.body.hits.total as estypes.SearchTotalHits
)?.value;
}
} else {
const indexExists = await client.indices.exists({index: searchPermissions.index});
if (indexExists.body) {
delete searchPermissions.size;
const deletedPermissionsResult = await client.deleteByQuery(searchPermissions);
if (deletedPermissionsResult && deletedPermissionsResult.body.deleted && deletedPermissionsResult.body.deleted > 0) {
const deletedPermissionsResult = await client.deleteByQuery(
searchPermissions
);
if (
deletedPermissionsResult &&
deletedPermissionsResult.body.deleted &&
deletedPermissionsResult.body.deleted > 0
) {
deletePermissions += deletedPermissionsResult.body.deleted;
}
}
}else { console.log(`Index ${searchPermissions.index} doens't exist. Não foi possível realizar a exclusão.`);}
}
for (const id of range.ids) {
const searchBlocks = {
index: blockIndex,
body: {
query: {bool: {must: [{term: {block_id: {value: id}}}]}}
}
query: {
bool: { must: [{ term: { block_id: { value: id } } }] },
},
},
};
if (args.dry) {
console.log(`Deleting block ${id}...`);
const resultBlocks = await client.search<SearchResponse<HyperionBlock>>(searchBlocks);
if (resultBlocks.body.hits.hits.length > 0 && resultBlocks.body.hits.hits[0]._source) {
const resultBlocks = await client.search<
SearchResponse<HyperionBlock>
>(searchBlocks);
if (
resultBlocks.body.hits.hits.length > 0 &&
resultBlocks.body.hits.hits[0]._source
) {
deleteBlocks++;
}
} else {
@@ -490,7 +781,9 @@ async function repairChain(chain: string, file: string, args: any) {
}
if (args.dry) {
console.log(`DRY-RUN: Would have deleted ${deleteBlocks} blocks, ${deleteActions} actions, ${deleteDeltas} deltas and ${deleteAbis} ABIs`);
console.log(
`DRY-RUN: Would have deleted ${deleteBlocks} blocks, ${deleteActions} actions, ${deleteDeltas} deltas and ${deleteAbis} ABIs`
);
console.dir({
blocks: deleteBlocks,
actions: deleteActions,
@@ -500,10 +793,12 @@ async function repairChain(chain: string, file: string, args: any) {
voters: deleteVoters,
proposals: deleteProposals,
links: deleteLinks,
permissions: deletePermissions
permissions: deletePermissions,
});
} else {
console.log(`Deleted ${deleteBlocks} blocks, ${deleteActions} actions, ${deleteDeltas} deltas and ${deleteAbis} ABIs`);
console.log(
`Deleted ${deleteBlocks} blocks, ${deleteActions} actions, ${deleteDeltas} deltas and ${deleteAbis} ABIs`
);
}
await fillMissingBlocksFromFile(args.host, chain, file, args.dry);
@@ -522,11 +817,11 @@ async function fillMissingBlocksFromFile(host, chain, file, dryRun) {
async function sendChunk(chunk): Promise<void> {
return new Promise((resolve, reject) => {
const payload = {
"event": "fill_missing_blocks",
"data": chunk
event: 'fill_missing_blocks',
data: chunk,
};
controller.send(JSON.stringify(payload));
// Wait repair_completed confirmation
controller.once('message', (data) => {
const parsed = JSON.parse(data.toString());
@@ -542,23 +837,28 @@ async function fillMissingBlocksFromFile(host, chain, file, dryRun) {
console.log('Connected to Hyperion Controller');
const parsedFile = JSON.parse(readFileSync(file).toString());
const chunkSize = 20; // Chunk size
if (!dryRun) {
const totalChunks = Math.ceil(parsedFile.length / chunkSize);
let completedChunks = 0;
for (let i = 0; i < parsedFile.length; i += chunkSize) {
const progress = (completedChunks / totalChunks) * 100;
const progressBar = Array(Math.round(progress / 2)).fill('#').join('');
process.stdout.write(`Progress: [${progressBar}] ${progress.toFixed(2)}% \r`);
// console.log(`Progress: [${progressBar}] ${progress.toFixed(2)}%`);
const totalLines = parsedFile.length;
if (!dryRun) {
let completedLines = 0;
for (let i = 0; i < parsedFile.length; i += chunkSize) {
const chunk = parsedFile.slice(i, i + chunkSize);
await sendChunk(chunk);
// Update progess bar
completedChunks++;
// Atualizar o progresso com base no número total de linhas
completedLines += chunk.length;
const progress = (completedLines / totalLines) * 100;
const progressBar = Array(Math.round(progress / 2))
.fill('#')
.join('');
process.stdout.clearLine(0); // Limpar a linha anterior
process.stdout.cursorTo(0); // Mover o cursor para o início da linha
process.stdout.write(
`Progress: [${progressBar}] ${progress.toFixed(2)}%`
);
}
console.log();
console.log(); // Pule para a próxima linha após a conclusão
controller.close();
} else {
console.log('Dry run, skipping repair');
@@ -575,7 +875,6 @@ async function fillMissingBlocksFromFile(host, chain, file, dryRun) {
});
}
function viewFile(file: string) {
const data = readFileSync(file, 'utf8');
const parsed = JSON.parse(data);
@@ -589,8 +888,8 @@ program
.description('CLI to find and repair forked and missing blocks on Hyperion')
.version('0.2.2');
program.command('scan <chain>')
program
.command('scan <chain>')
.description('scan for forked blocks')
.option('-d, --dry', 'dry-run, do not delete or repair blocks')
.option('-o, --out-file <file>', 'forked-blocks.json output file')
@@ -599,24 +898,28 @@ program.command('scan <chain>')
.option('-b, --batch <number>', 'batch size to process')
.action(scanChain);
program.command('repair <chain> <file>')
program
.command('repair <chain> <file>')
.description('repair forked blocks')
.option('-h, --host <host>', 'Hyperion local control api')
.option('-d, --dry', 'dry-run, do not delete or repair blocks')
.option('-t, --check-tasks', 'check for running tasks')
.action(repairChain);
program.command('fill-missing <chain> <file>')
program
.command('fill-missing <chain> <file>')
.description('write missing blocks')
.option('-h, --host <host>', 'Hyperion local control api')
.option('-d, --dry', 'dry-run, do not delete or repair blocks')
.action(repairMissing);
program.command('view <file>')
program
.command('view <file>')
.description('view forked blocks')
.action(viewFile);
program.command('connect')
program
.command('connect')
.option('-h, --host <host>', 'Hyperion local control api')
.action(async (args) => {
let hyperionIndexer = 'ws://localhost:4321';
@@ -632,12 +935,16 @@ program.command('connect')
});
controller.on('close', () => {
if (valid) {
console.log(`✅ Hyperion Indexer Online - ${hyperionIndexer}`);
console.log(
`✅ Hyperion Indexer Online - ${hyperionIndexer}`
);
}
});
controller.on('error', (err) => {
console.log("Error:", err.message);
console.log(`Failed to connect on Hyperion Indexer at ${hyperionIndexer}, please use "--host ws://ADDRESS:PORT" to specify a remote indexer connection`);
console.log('Error:', err.message);
console.log(
`Failed to connect on Hyperion Indexer at ${hyperionIndexer}, please use "--host ws://ADDRESS:PORT" to specify a remote indexer connection`
);
});
} catch (e) {
console.log(e);
@@ -645,8 +952,3 @@ program.command('connect')
});
program.parse();
+39
View File
@@ -0,0 +1,39 @@
const hyperionModule = {
chain: "*",
contract: '*',
action: 'act',
parser_version: ['3.2', '2.1','1.8','1.7'],
defineQueryPrefix: 'act',
mappings: {
action: {
"@act": {
"properties": {
"coopname": {"type": "keyword"},
"username": {"type": "keyword"},
"action": {"type": "keyword"},
"decision_id": {"type": "long"},
"batch_id": {"type": "long"},
"act": {"enabled": false}
}
}
}
},
handler: (action) => {
const data = action['act']['data'];
action['@act'] = {
coopname: data.coopname,
username: data.username,
action: data.action,
decision_id: data.decision_id,
batch_id: data.batch_id,
act: data.act
};
}
};
module.exports = {hyperionModule};
+33
View File
@@ -0,0 +1,33 @@
const hyperionModule = {
chain: "*",
contract: '*',
action: 'batch',
parser_version: ['3.2', '2.1','1.8','1.7'],
defineQueryPrefix: 'batch',
mappings: {
action: {
"@batch": {
"properties": {
"coopname": {"type": "keyword"},
"action": {"type": "keyword"},
"batch_id": {"type": "long"}
}
}
}
},
handler: (action) => {
const data = action['act']['data'];
action['@batch'] = {
coopname: data.coopname,
action: data.action,
batch_id: data.batch_id,
};
}
};
module.exports = {hyperionModule};
+39
View File
@@ -0,0 +1,39 @@
const hyperionModule = {
chain: "*",
contract: '*',
action: 'decision',
parser_version: ['3.2', '2.1','1.8','1.7'],
defineQueryPrefix: 'decision',
mappings: {
action: {
"@decision": {
"properties": {
"coopname": {"type": "keyword"},
"username": {"type": "keyword"},
"action": {"type": "keyword"},
"decision_id": {"type": "long"},
"batch_id": {"type": "long"},
"decision": {"enabled": false}
}
}
}
},
handler: (action) => {
const data = action['act']['data'];
action['@decision'] = {
coopname: data.coopname,
username: data.username,
action: data.action,
decision_id: data.decision_id,
batch_id: data.batch_id,
decision: data.decision
};
}
};
module.exports = {hyperionModule};
+33
View File
@@ -0,0 +1,33 @@
const hyperionModule = {
chain: "*",
contract: '*',
action: 'exec',
parser_version: ['3.2', '2.1','1.8','1.7'],
defineQueryPrefix: 'exec',
mappings: {
action: {
"@exec": {
"properties": {
"executer": {"type": "keyword"},
"coopname": {"type": "keyword"},
"decision_id": {"type": "long"}
}
}
}
},
handler: (action) => {
const data = action['act']['data'];
action['@exec'] = {
executer: data.executer,
coopname: data.coopname,
decision_id: data.decision_id,
};
}
};
module.exports = {hyperionModule};
+39
View File
@@ -0,0 +1,39 @@
const hyperionModule = {
chain: "*",
contract: '*',
action: 'statement',
parser_version: ['3.2', '2.1','1.8','1.7'],
defineQueryPrefix: 'statement',
mappings: {
action: {
"@statement": {
"properties": {
"coopname": {"type": "keyword"},
"username": {"type": "keyword"},
"action": {"type": "keyword"},
"decision_id": {"type": "long"},
"batch_id": {"type": "long"},
"statement": {"enabled": false}
}
}
}
},
handler: (action) => {
const data = action['act']['data'];
action['@statement'] = {
coopname: data.coopname,
username: data.username,
action: data.action,
decision_id: data.decision_id,
batch_id: data.batch_id,
statement: data.statement
};
}
};
module.exports = {hyperionModule};
+33
View File
@@ -0,0 +1,33 @@
const hyperionModule = {
chain: "*",
contract: '*',
action: 'voteagainst',
parser_version: ['3.2', '2.1','1.8','1.7'],
defineQueryPrefix: 'voteagainst',
mappings: {
action: {
"@voteagainst": {
"properties": {
"coopname": {"type": "keyword"},
"member": {"type": "keyword"},
"decision_id": {"type": "long"}
}
}
}
},
handler: (action) => {
const data = action['act']['data'];
action['@voteagainst'] = {
coopname: data.coopname,
member: data.member,
decision_id: decision_id
};
}
};
module.exports = {hyperionModule};
+33
View File
@@ -0,0 +1,33 @@
const hyperionModule = {
chain: "*",
contract: '*',
action: 'votefor',
parser_version: ['3.2', '2.1','1.8','1.7'],
defineQueryPrefix: 'votefor',
mappings: {
action: {
"@votefor": {
"properties": {
"coopname": {"type": "keyword"},
"member": {"type": "keyword"},
"decision_id": {"type": "long"}
}
}
}
},
handler: (action) => {
const data = action['act']['data'];
action['@votefor'] = {
coopname: data.coopname,
member: data.member,
decision_id: decision_id
};
}
};
module.exports = {hyperionModule};