Skip to content
This repository has been archived by the owner on Oct 4, 2024. It is now read-only.

Commit

Permalink
Move all display logic to utils and add proposals display
Browse files Browse the repository at this point in the history
  • Loading branch information
ilblackdragon committed May 7, 2020
1 parent 52ac90b commit 8648589
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 56 deletions.
1 change: 1 addition & 0 deletions bin/near-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ yargs // eslint-disable-line
.command(require('../commands/repl'))
.command(require('../commands/generate-key'))
.command(require('../commands/validators'))
.command(require('../commands/proposals'))
.config(config)
.alias({
'accountId': ['account_id'],
Expand Down
12 changes: 12 additions & 0 deletions commands/proposals.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const exitOnError = require('../utils/exit-on-error');
const connect = require('../utils/connect');
const validatorsInfo = require('../utils/validators-info');

module.exports = {
command: 'proposals',
desc: 'lookup current proposals',
handler: exitOnError(async (argv) => {
const near = await connect(argv);
await validatorsInfo.showProposalsTable(near);
})
};
75 changes: 19 additions & 56 deletions commands/validators.js
Original file line number Diff line number Diff line change
@@ -1,66 +1,29 @@
const exitOnError = require('../utils/exit-on-error');
const connect = require('../utils/connect');
const { validators, utils } = require('near-api-js');
const BN = require('bn.js');
const AsciiTable = require('ascii-table');
const validatorsInfo = require('../utils/validators-info');

module.exports = {
command: 'EXPERIMENTAL_validators',
desc: 'lookup validators and proposals',
command: 'validators <epoch>',
desc: 'lookup validators for given epoch (or current / next)',
builder: (yargs) => yargs
.option('epoch', {
desc: 'epoch defined by block number or current / next',
type: 'string',
required: true
}),
handler: exitOnError(async (argv) => {
const near = await connect(argv);

const genesisConfig = await near.connection.provider.sendJsonRpc('EXPERIMENTAL_genesis_config', {});
const result = await near.connection.provider.sendJsonRpc('validators', [null]);

// Calculate all required data.
const numSeats = genesisConfig.num_block_producer_seats + genesisConfig.avg_hidden_validator_seats_per_shard.reduce((a, b) => a + b);
const seatPrice = validators.findSeatPrice(result.current_validators, numSeats);
const nextSeatPrice = validators.findSeatPrice(result.next_validators, numSeats);

// Sort validators by their stake.
result.current_validators = result.current_validators.sort((a, b) => -new BN(a.stake).cmp(new BN(b.stake)));
result.next_validators = result.next_validators.sort((a, b) => -new BN(a.stake).cmp(new BN(b.stake)));

var validatorsTable = new AsciiTable();
validatorsTable.setHeading('Validator Id', 'Stake', '# seats', '% online', 'bls produced', 'bls expected');
console.log(`Validators (total: ${result.current_validators.length}, seat price: ${utils.format.formatNearAmount(seatPrice, 0)}):`);
result.current_validators.forEach((validator) => {
validatorsTable.addRow(
validator.account_id,
utils.format.formatNearAmount(validator.stake, 0),
new BN(validator.stake).divRound(seatPrice),
Math.floor(validator.num_produced_blocks / validator.num_expected_blocks * 100),
validator.num_produced_blocks,
validator.num_expected_blocks);
});
console.log(validatorsTable.toString());

if (result.current_fishermen) {
console.log(`\nFishermen (total: ${result.current_fishermen.length}):`);
var fishermenTable = new AsciiTable();
fishermenTable.setHeading('Fisherman Id', 'Stake');
result.current_fishermen.forEach((fisherman) => {
fishermenTable.addRow(fisherman.account_id, utils.format.formatNearAmount(fisherman.stake, 0));
});
console.log(fishermenTable.toString());
switch (argv.epoch) {
case 'current':
await validatorsInfo.showValidatorsTable(near, null);
break;
case 'next':
await validatorsInfo.showNextValidatorsTable(near);
break;
default:
await validatorsInfo.showValidatorsTable(near, argv.epoch);
break;
}

const diff = validators.diffEpochValidators(result.current_validators, result.next_validators);
console.log(`\nNext validators (total: ${result.next_validators.length}, seat price: ${utils.format.formatNearAmount(nextSeatPrice, 0)}):`);
let nextValidatorsTable = new AsciiTable();
nextValidatorsTable.setHeading('Status', 'Validator', 'Stake', '# seats');
diff.newValidators.map((validator) => nextValidatorsTable.addRow(
'New',
validator.account_id,
utils.format.formatNearAmount(validator.stake, 0),
new BN(validator.stake).divRound(nextSeatPrice)));
diff.changedValidators.map((changeValidator) => nextValidatorsTable.addRow(
'Rewarded',
changeValidator.next.account_id,
`${utils.format.formatNearAmount(changeValidator.current.stake, 0)} -> ${utils.format.formatNearAmount(changeValidator.next.stake, 0)}`,
new BN(changeValidator.next.stake).divRound(nextSeatPrice)));
diff.removedValidators.map((validator) => nextValidatorsTable.addRow('Kicked out', validator.account_id, '-', '-'));
console.log(nextValidatorsTable.toString());
})
};
91 changes: 91 additions & 0 deletions utils/validators-info.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
const { validators, utils } = require('near-api-js');
const BN = require('bn.js');
const AsciiTable = require('ascii-table');

async function validatorsInfo(near, epochId) {
const genesisConfig = await near.connection.provider.sendJsonRpc('EXPERIMENTAL_genesis_config', {});
const result = await near.connection.provider.sendJsonRpc('validators', [epochId]);
result.genesisConfig = genesisConfig;
result.numSeats = genesisConfig.num_block_producer_seats + genesisConfig.avg_hidden_validator_seats_per_shard.reduce((a, b) => a + b);
return result;
}

async function showValidatorsTable(near, epochId) {
const result = await validatorsInfo(near, epochId);
const seatPrice = validators.findSeatPrice(result.current_validators, result.numSeats);
result.current_validators = result.current_validators.sort((a, b) => -new BN(a.stake).cmp(new BN(b.stake)));
var validatorsTable = new AsciiTable();
validatorsTable.setHeading('Validator Id', 'Stake', '# seats', '% online', 'bls produced', 'bls expected');
console.log(`Validators (total: ${result.current_validators.length}, seat price: ${utils.format.formatNearAmount(seatPrice, 0)}):`);
result.current_validators.forEach((validator) => {
validatorsTable.addRow(
validator.account_id,
utils.format.formatNearAmount(validator.stake, 0),
new BN(validator.stake).divRound(seatPrice),
Math.floor(validator.num_produced_blocks / validator.num_expected_blocks * 100),
validator.num_produced_blocks,
validator.num_expected_blocks);
});
console.log(validatorsTable.toString());
}

async function showNextValidatorsTable(near) {
const result = await validatorsInfo(near, null);
const nextSeatPrice = validators.findSeatPrice(result.next_validators, result.numSeats);
const diff = validators.diffEpochValidators(result.current_validators, result.next_validators);
console.log(`\nNext validators (total: ${result.next_validators.length}, seat price: ${utils.format.formatNearAmount(nextSeatPrice, 0)}):`);
let nextValidatorsTable = new AsciiTable();
nextValidatorsTable.setHeading('Status', 'Validator', 'Stake', '# seats');
diff.newValidators.forEach((validator) => nextValidatorsTable.addRow(
'New',
validator.account_id,
utils.format.formatNearAmount(validator.stake, 0),
new BN(validator.stake).divRound(nextSeatPrice)));
diff.changedValidators.forEach((changeValidator) => nextValidatorsTable.addRow(
'Rewarded',
changeValidator.next.account_id,
`${utils.format.formatNearAmount(changeValidator.current.stake, 0)} -> ${utils.format.formatNearAmount(changeValidator.next.stake, 0)}`,
new BN(changeValidator.next.stake).divRound(nextSeatPrice)));
diff.removedValidators.forEach((validator) => nextValidatorsTable.addRow('Kicked out', validator.account_id, '-', '-'));
console.log(nextValidatorsTable.toString());
}

function combineValidatorsAndProposals(validators, proposalsMap) {
// TODO: filter out all kicked out validators.
let result = validators.filter((validator) => !proposalsMap.has(validator.account_id));
return result.concat([...proposalsMap.values()]);
}

async function showProposalsTable(near) {
const result = await validatorsInfo(near, null);
let currentValidators = new Map();
result.current_validators.forEach((v) => currentValidators.set(v.account_id, v));
let proposals = new Map();
result.current_proposals.forEach((p) => proposals.set(p.account_id, p));
const combinedProposals = combineValidatorsAndProposals(result.current_validators, proposals);
const expectedSeatPrice = validators.findSeatPrice(combinedProposals, result.numSeats);
console.log(`Proposals (total: ${proposals.size})`);
console.log(`Expected seat price = ${utils.format.formatNearAmount(expectedSeatPrice, 0)}`);
const proposalsTable = new AsciiTable();
combinedProposals.sort((a, b) => -new BN(a.stake).cmp(new BN(b.stake))).forEach((proposal) => {
let kind = '';
if (new BN(proposal.stake).gte(expectedSeatPrice)) {
kind = proposals.has(proposal.account_id) ? 'New' : 'Rollover';
} else {
kind = proposals.has(proposal.account_id) ? 'Declined' : 'Kicked out';
}
let stake_fmt = utils.format.formatNearAmount(proposal.stake, 0);
if (currentValidators.has(proposal.account_id) && proposals.has(proposal.account_id)) {
stake_fmt = `${utils.format.formatNearAmount(currentValidators.get(proposal.account_id).stake, 0)} => ${stake_fmt}`;
}
proposalsTable.addRow(
kind,
proposal.account_id,
stake_fmt
);
});
console.log(proposalsTable.toString());
console.log("Note: this currently doesn't account for offline kickouts and rewards for current epoch");
}

module.exports = { showValidatorsTable, showNextValidatorsTable, showProposalsTable };

0 comments on commit 8648589

Please sign in to comment.