-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathindex.js
133 lines (108 loc) · 4.11 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import dotenv from "dotenv";
import fs from "fs";
import path from "path";
import { TelegramBot } from "./src/TelegramBot.js";
import { NodeFetcher } from "./src/NodeFetcher.js";
import {
getChunksBlocksStat,
prepareSwitchingEpochInfo,
getPoolId,
countProductivity,
} from "./src/helpers.js";
dotenv.config({ path: "./config.env" });
const __dirname = path.resolve();
const STATE_FILE = __dirname + "/.prev_state.json";
const TRIGGER_UPTIME_NOTIFICATION_RATIO = 0.9;
let prev_state;
try {
prev_state = fs.readFileSync(STATE_FILE, { encoding: "utf8", fd: null });
} catch (error) {
// do nothing, script create the file in end of script
}
const { TG_API_KEY, TG_CHAT_ID, NODE_RPC, POOL_ID } = process.env;
const tgBot = new TelegramBot(TG_API_KEY, TG_CHAT_ID);
const nodeFetcher = new NodeFetcher(NODE_RPC, POOL_ID);
/**callback to find my pool id in different arrays*/
const findMyPoolId = (pool) => pool.account_id === POOL_ID;
const main = async () => {
try {
const node = await nodeFetcher.ping();
const { validator_account_id } = await node.json();
const status = await nodeFetcher.checkValidators();
const { result } = await status.json();
const myKickoutState = result.prev_epoch_kickout.find(findMyPoolId);
const myValidatorState = result.current_validators.find(findMyPoolId);
const myNextValidatorsState = result.next_validators.find(findMyPoolId);
const epochStartHeight = result.epoch_start_height;
const epochHeight = result.epoch_height;
const newState = {
myKickoutState,
myValidatorState,
myNextValidatorsState,
epochStartHeight,
productivity: countProductivity(myValidatorState),
};
const newStateString = JSON.stringify(newState, null, 2);
//if states are equals then do nothing
if (newStateString === prev_state) return;
let oldState;
if (prev_state) oldState = JSON.parse(prev_state);
// rewrite new state
fs.writeFileSync(STATE_FILE, newStateString);
// Notify if epoch has changed
if (newState.epochStartHeight !== oldState?.epochStartHeight) {
const msg = prepareSwitchingEpochInfo(
epochHeight,
oldState,
newState,
POOL_ID
);
await tgBot.sendMessage(msg);
}
if (newState.myValidatorState) {
// if percentage of expected/produced chunks was lower less than 80%
const {
num_expected_chunks: expectedChunks,
num_produced_chunks: producedChunks,
num_expected_blocks: expectedBlocks,
num_produced_blocks: producedBlocks,
num_expected_endorsements: expectedEndorsement,
num_produced_endorsements: producedEndorsement,
} = newState.myValidatorState;
let isProductionRateLow = false
const chunksRatio = producedChunks / expectedChunks;
const blocksRatio = producedBlocks / expectedBlocks;
const endorsementRatio = producedEndorsement / expectedEndorsement;
if(blocksRatio < TRIGGER_UPTIME_NOTIFICATION_RATIO && expectedBlocks >= 4){
isProductionRateLow = true
}
if(chunksRatio < TRIGGER_UPTIME_NOTIFICATION_RATIO && expectedChunks >= 4){
isProductionRateLow = true
}
// endorsement is easier to fail (~95% success rate for now), give a bit more headroom
if(endorsementRatio < TRIGGER_UPTIME_NOTIFICATION_RATIO && expectedEndorsement >= 10){
isProductionRateLow = true
}
if (
isProductionRateLow &&
oldState?.productivity > newState.productivity
) {
const msgRows = [
"⚠ SOMETHIG WRONG!",
getPoolId(POOL_ID),
"Your node has produced lower than expected",
getChunksBlocksStat("Productivity", newState.myValidatorState),
];
await tgBot.sendMessage(msgRows.join("\n"));
}
}
if (validator_account_id !== POOL_ID)
throw Error(`POOL ID PROBLEMS: \n${POOL_ID} !== ${validator_account_id}`);
} catch (error) {
// if there is error then something wrong with node
console.log(error);
const msg = [getPoolId(POOL_ID), error.message];
await tgBot.sendMessage("🚨 ERROR 🚨\n" + msg.join("\n"));
}
};
main();