Upgrades Alert System
1. Scan for new Upgrade proposals once every 30 minutes
Code is in PHP
This code scans for new upgrade proposals once every 30 minutes and outputs the data to a .json file (example - proposals.json)
<?php
$jsonFilePath = '/var/www/html/proposals.json';
$jsonContents = file_get_contents($jsonFilePath);
$existingProposals = json_decode($jsonContents, true);
$final = $existingProposals;
$chains = [
"cosmoshub",
"archway",
"axelar",
"canto",
"evmos",
"kava",
"kujira",
"mars",
"meme",
"migaloo",
"oraichain",
"planq",
"provenance",
"stride",
"tenet",
"terra2"
];
foreach ($chains as $chain) {
$api = "https://rest.cosmos.directory/". $chain . "/cosmos/gov/v1beta1/proposals";
$rpc = "https://rpc.cosmos.directory/". $chain;
$ch = curl_init($api . '?proposal_status=2');
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$resp = curl_exec($ch);
curl_close($ch);
$result = json_decode($resp, true);
foreach ($result['proposals'] as $proposal) {
$pid = $proposal['proposal_id'];
$type = $proposal['content']['@type'];
if (strpos($type, 'SoftwareUpgradeProposal') !== false) {
$newProposal = [
"chain" => ucfirst($chain),
"pid" => $pid,
"rpc" => $rpc,
"api" => $api . '/' . $pid,
];
$exists = false;
foreach ($existingProposals as $existingProposal) {
if ($existingProposal['chain'] === $newProposal['chain'] && $existingProposal['pid'] === $newProposal['pid']) {
$exists = true;
break;
}
}
if (!$exists) {
$final[] = $newProposal;
}
}
}
}
echo json_encode($final, JSON_UNESCAPED_SLASHES);
?>
Create a .sh file called ``proposals.sh``
#!/bin/bash
/usr/bin/php /var/www/html/proposals.php > /var/www/html/proposals2.json
/usr/bin/cat /var/www/html/proposals2.json > /var/www/html/proposals.json
Setup a cronjob to run the above code once every 30 mins.
*/30 * * * * /usr/bin/sh /var/www/html/proposals.sh
Now, proposals.json will contain the following:
[{"chain":"Cosmoshub","pid":"804","rpc":"https://rpc.cosmos.directory/cosmoshub","api":"https://rest.cosmos.directory/cosmoshub/cosmos/gov/v1beta1/proposals/804"},{"chain":"Archway","pid":"10","rpc":"https://rpc.cosmos.directory/archway","api":"https://rest.cosmos.directory/archway/cosmos/gov/v1beta1/proposals/10"}]
2. Update Status & Current Block number once every 5 Minutes
Read the proposals.json
file once every 5 minutes, update the status of the proposal, and update current block number of the blockchain.
Code is in PHP.
<?php
$jsonFilePath = '/var/www/html/proposals.json';
$jsonContents = file_get_contents($jsonFilePath);
$proposals = json_decode($jsonContents, true);
$final = [];
foreach ($proposals as $proposal) {
$apiUrl = $proposal['api'];
$rpcUrl = $proposal['rpc'];
$proposalDetails = fetchData($apiUrl);
$version = $proposalDetails['proposal']['content']['plan']['name'];
$status = getStatus($proposalDetails['proposal']['status']);
$height = $proposalDetails['proposal']['content']['plan']['height'];
$current = fetchData($rpcUrl . "/abci_info")['result']['response']['last_block_height'];
$chain_id = fetchData($rpcUrl . "/status")['result']['node_info']['network'];
$final[] = [
"chain" => $proposal['chain'],
"chain_id" => $chain_id,
"pid" => $proposal['pid'],
"status" => $status,
"height" => $height,
"current" => $current,
"version" => $version
];
}
echo json_encode($final, JSON_UNESCAPED_SLASHES);
// Function to fetch data from a URL and return decoded JSON
function fetchData($url) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
// Function to get status description
function getStatus($status) {
if ($status == 'PROPOSAL_STATUS_VOTING_PERIOD') return "Voting period";
if ($status == 'PROPOSAL_STATUS_PASSED') return "Passed";
return "Unknown status";
}
?>
Setup a cronjob to run the above code once every 30 mins.
*/5 * * * * /usr/bin/php /var/www/html/upgrades.php > /var/www/html/upgrades.json
Now, upgrades.json
will contain the following:
[{"chain":"Cosmoshub","chain_id":"cosmoshub-4","pid":"804","status":"Voting period","height":16596000,"current":16381476,"version":"v11"},{"chain":"Archway","chain_id":"archway-1","pid":"10","status":"Voting period","height":525000,"current":420328,"version":"v2.0.0"}]
3. NodeJs file to run every 5 Minutes for PagerDuty alerts
The following NodeJs code is executed every 5 minutes to send an alert using PagerDuty when the remaining blocks to upgrade reaches below 1000 blocks, then, again when the blocks remaining reaches below 500, and one more when the blocks remaining reaches below 100.
In this example, we do this for Cosmos Hub blockchain.
import fs from 'fs/promises';
import pdClient from 'node-pagerduty';
const PD_API_TOKEN = '<api-token>';
const pd = new pdClient(PD_API_TOKEN);
const FILE_PATH = '/var/www/html/upgrades.json';
// Thresholds for notifications
let thresholds = {
thousand: 0,
fivehundred: 0,
hundred: 0,
};
// Read JSON file and parse content
async function readJsonFile(filePath) {
try {
const data = await fs.readFile(filePath, 'utf8');
return JSON.parse(data);
} catch (err) {
console.error('Error reading or parsing file:', err);
throw err;
}
}
// Formulate message based on chain height and current height
function getMessage(item) {
const { height, current } = item;
const remaining = height - current;
let message = '';
if (remaining <= 1000 && thresholds.thousand === 0) {
message = 'Cosmoshub Upgrade Less than 1000 Blocks to go.';
thresholds.thousand = 1000;
} else if (remaining <= 500 && thresholds.fivehundred === 0) {
message = 'Cosmoshub Upgrade Less than 500 Blocks to go.';
thresholds.fivehundred = 500;
} else if (remaining <= 100 && thresholds.hundred === 0) {
message = 'Cosmoshub Upgrade Less than 100 Blocks to go.';
thresholds.hundred = 100;
}
return message;
}
// Main function to process data and send notifications
async function processUpgrades() {
try {
const data = await readJsonFile(FILE_PATH);
if (!data.some(item => item.chain === 'Cosmoshub')) {
console.log('String not found in data');
thresholds = {
thousand: 0,
fivehundred: 0,
hundred: 0,
};
return;
}
data.forEach((item, index) => {
console.log('Item', index + 1, 'Chain:', item.chain, 'Pid:', item.pid);
if (item.chain === 'Cosmoshub') {
const message = getMessage(item);
console.log("Message = ", message);
const payloadCosmoshub = {
payload: {
summary: message,
source: 'Cosmoshub Upgrade',
severity: 'info',
},
routing_key: '<api-token>',
event_action: 'trigger',
client: 'Cosmoshub Upgrade Monitoring Service',
};
if (message) pd.events.sendEvent(payloadCosmoshub);
}
});
} catch (error) {
console.error('Error processing upgrades:', error);
}
}
// Run the processing function periodically
processUpgrades();
setInterval(processUpgrades, 300000);
Last updated