From fca5fde06a6405eb66ede8757e82d7a1707a2f91 Mon Sep 17 00:00:00 2001 From: Victor Graf Date: Tue, 26 Nov 2019 09:55:13 -0600 Subject: [PATCH 01/23] create validator:online command to check if a validator is alive --- packages/cli/src/commands/validator/online.ts | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 packages/cli/src/commands/validator/online.ts diff --git a/packages/cli/src/commands/validator/online.ts b/packages/cli/src/commands/validator/online.ts new file mode 100644 index 00000000000..9ec434e85f0 --- /dev/null +++ b/packages/cli/src/commands/validator/online.ts @@ -0,0 +1,84 @@ +import { cli } from 'cli-ux' +import { range } from 'lodash' +import * as rlp from 'rlp' +import { Block } from 'web3/eth/types' +import { BaseCommand } from '../../base' +import { newCheckBuilder } from '../../utils/checks' +import { Flags } from '../../utils/command' + +export default class ValidatorOnline extends BaseCommand { + static description = 'Show information about whether the given signer is online and validating' + + // How many blocks to look back for proposals of this signer. + static readonly lookback = 50 + + static flags = { + ...BaseCommand.flags, + signer: Flags.address({ + required: true, + description: 'address of the signer to check if online and validating', + }), + } + + static examples = ['online --signer 0x5409ED021D9299bf6814279A6A1411A7e866A631'] + + requireSynced = true + + async run() { + const { flags } = this.parse(ValidatorOnline) + + await newCheckBuilder(this, flags.signer) + .signerAccountIsValidator() + .runChecks() + + cli.action.start('Querying current validator set') + const election = await this.kit.contracts.getElection() + const size = await election.numberValidatorsInCurrentSet() + const set = await Promise.all(range(size).map(election.validatorAddressFromCurrentSet)) + cli.action.stop() + + const signerIndex = set.map((a) => a.toLowerCase()).indexOf(flags.signer.toLowerCase()) + if (signerIndex < 0) { + this.error(`Signer ${flags.signer} is not in the validator set for this epoch`) + } + + cli.action.start('Searching for proposed blocks') + const latest = await this.web3.eth.getBlock('latest') + const blocks = await Promise.all( + range(1, ValidatorOnline.lookback).map((i) => this.web3.eth.getBlock(latest.number - i)) + ) + blocks.splice(0, 0, latest) + cli.action.stop() + + for (const [i, block] of blocks.entries()) { + if (this.validatorIndexSignedParentHeader(block, signerIndex)) { + const parent = + i + 1 < blocks.length ? blocks[i + 1] : await this.web3.getBlock(block.number - 1) + console.info( + `Signer is online and signed block ${parent.number} ${Math.ceil( + Date.now() / 1000 - parent.timestamp + )} seconds ago` + ) + return + } + } + this.error( + `Signer is part of the current validator set, but has not signed any of the last ${ + ValidatorOnline.lookback + } blocks` + ) + } + + // Check the parent seal in the given block header for a bit indicating at the given index. + private validatorIndexSignedParentHeader(block: Block, index: number) { + // See https://github.com/celo-org/celo-blockchain/blob/master/core/types/istanbul.go + // tslint:disable:no-bitwise + // @ts-ignore + const parentSealBitmap = rlp.decode('0x' + block.extraData.slice(66))[5][0] + // @ts-ignore + return ( + (parentSealBitmap[parentSealBitmap.length - 1 - Math.floor(index / 8)] & (1 << index % 8)) > 0 + ) + // tslint:enable:no-bitwise + } +} From 33674b705c7e326784b8e53d1413ad4d14d9fd4d Mon Sep 17 00:00:00 2001 From: Victor Graf Date: Tue, 26 Nov 2019 09:58:04 -0600 Subject: [PATCH 02/23] add a couple more checks --- packages/cli/src/commands/validator/online.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cli/src/commands/validator/online.ts b/packages/cli/src/commands/validator/online.ts index 9ec434e85f0..8daff1db799 100644 --- a/packages/cli/src/commands/validator/online.ts +++ b/packages/cli/src/commands/validator/online.ts @@ -28,6 +28,7 @@ export default class ValidatorOnline extends BaseCommand { const { flags } = this.parse(ValidatorOnline) await newCheckBuilder(this, flags.signer) + .signerMeetsValidatorBalanceRequirements() .signerAccountIsValidator() .runChecks() From 9ac6868538f4cd3f35c6189a02c727b1c4130a9b Mon Sep 17 00:00:00 2001 From: Victor Graf Date: Tue, 26 Nov 2019 10:12:33 -0600 Subject: [PATCH 03/23] add a check --- packages/cli/src/commands/validator/online.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/validator/online.ts b/packages/cli/src/commands/validator/online.ts index 8daff1db799..b93d49e5cce 100644 --- a/packages/cli/src/commands/validator/online.ts +++ b/packages/cli/src/commands/validator/online.ts @@ -27,7 +27,9 @@ export default class ValidatorOnline extends BaseCommand { async run() { const { flags } = this.parse(ValidatorOnline) + // Use redundant checks to help the user diagnose issues. await newCheckBuilder(this, flags.signer) + .canSignValidatorTxs() .signerMeetsValidatorBalanceRequirements() .signerAccountIsValidator() .runChecks() @@ -69,7 +71,7 @@ export default class ValidatorOnline extends BaseCommand { } blocks` ) } - + k // Check the parent seal in the given block header for a bit indicating at the given index. private validatorIndexSignedParentHeader(block: Block, index: number) { // See https://github.com/celo-org/celo-blockchain/blob/master/core/types/istanbul.go From ebfa798c3dcf862bc4de5ec6722190d8942fb81b Mon Sep 17 00:00:00 2001 From: Victor Graf Date: Tue, 26 Nov 2019 10:18:22 -0600 Subject: [PATCH 04/23] rename online to status --- .../src/commands/validator/{online.ts => status.ts} | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) rename packages/cli/src/commands/validator/{online.ts => status.ts} (87%) diff --git a/packages/cli/src/commands/validator/online.ts b/packages/cli/src/commands/validator/status.ts similarity index 87% rename from packages/cli/src/commands/validator/online.ts rename to packages/cli/src/commands/validator/status.ts index b93d49e5cce..53524ab4d53 100644 --- a/packages/cli/src/commands/validator/online.ts +++ b/packages/cli/src/commands/validator/status.ts @@ -7,7 +7,7 @@ import { newCheckBuilder } from '../../utils/checks' import { Flags } from '../../utils/command' export default class ValidatorOnline extends BaseCommand { - static description = 'Show information about whether the given signer is online and validating' + static description = 'Show information about whether the signer is elected and validating' // How many blocks to look back for proposals of this signer. static readonly lookback = 50 @@ -16,11 +16,11 @@ export default class ValidatorOnline extends BaseCommand { ...BaseCommand.flags, signer: Flags.address({ required: true, - description: 'address of the signer to check if online and validating', + description: 'address of the signer to check if elected and validating', }), } - static examples = ['online --signer 0x5409ED021D9299bf6814279A6A1411A7e866A631'] + static examples = ['status --signer 0x5409ED021D9299bf6814279A6A1411A7e866A631'] requireSynced = true @@ -57,10 +57,9 @@ export default class ValidatorOnline extends BaseCommand { if (this.validatorIndexSignedParentHeader(block, signerIndex)) { const parent = i + 1 < blocks.length ? blocks[i + 1] : await this.web3.getBlock(block.number - 1) + const timedelta = Date.now() / 1000 - parent.timestamp console.info( - `Signer is online and signed block ${parent.number} ${Math.ceil( - Date.now() / 1000 - parent.timestamp - )} seconds ago` + `Signer most recently signed block ${parent.number} ${Math.ceil(timedelta)} seconds ago` ) return } From 0ecea6b3f8cc5f5ab0bc0d41ee8aed926c05841d Mon Sep 17 00:00:00 2001 From: Victor Graf Date: Tue, 26 Nov 2019 10:29:27 -0600 Subject: [PATCH 05/23] remove k --- packages/cli/src/commands/validator/status.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/commands/validator/status.ts b/packages/cli/src/commands/validator/status.ts index 53524ab4d53..5b4d42680da 100644 --- a/packages/cli/src/commands/validator/status.ts +++ b/packages/cli/src/commands/validator/status.ts @@ -70,7 +70,7 @@ export default class ValidatorOnline extends BaseCommand { } blocks` ) } - k + // Check the parent seal in the given block header for a bit indicating at the given index. private validatorIndexSignedParentHeader(block: Block, index: number) { // See https://github.com/celo-org/celo-blockchain/blob/master/core/types/istanbul.go From 94c8248483e7cdf301fcb6d976f10e75362b1ce2 Mon Sep 17 00:00:00 2001 From: Victor Graf Date: Tue, 26 Nov 2019 10:37:27 -0600 Subject: [PATCH 06/23] remove extra ts-ignore --- packages/cli/src/commands/validator/status.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cli/src/commands/validator/status.ts b/packages/cli/src/commands/validator/status.ts index 5b4d42680da..282045ffd80 100644 --- a/packages/cli/src/commands/validator/status.ts +++ b/packages/cli/src/commands/validator/status.ts @@ -77,7 +77,6 @@ export default class ValidatorOnline extends BaseCommand { // tslint:disable:no-bitwise // @ts-ignore const parentSealBitmap = rlp.decode('0x' + block.extraData.slice(66))[5][0] - // @ts-ignore return ( (parentSealBitmap[parentSealBitmap.length - 1 - Math.floor(index / 8)] & (1 << index % 8)) > 0 ) From af8e695b9a7c9dacfb0d5faf1fb6ebaff63b074c Mon Sep 17 00:00:00 2001 From: Victor Graf Date: Tue, 26 Nov 2019 12:36:07 -0600 Subject: [PATCH 07/23] fix web3.eth.getBlock --- packages/cli/src/commands/validator/status.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/commands/validator/status.ts b/packages/cli/src/commands/validator/status.ts index 282045ffd80..67c5f96c8be 100644 --- a/packages/cli/src/commands/validator/status.ts +++ b/packages/cli/src/commands/validator/status.ts @@ -56,7 +56,7 @@ export default class ValidatorOnline extends BaseCommand { for (const [i, block] of blocks.entries()) { if (this.validatorIndexSignedParentHeader(block, signerIndex)) { const parent = - i + 1 < blocks.length ? blocks[i + 1] : await this.web3.getBlock(block.number - 1) + i + 1 < blocks.length ? blocks[i + 1] : await this.web3.eth.getBlock(block.number - 1) const timedelta = Date.now() / 1000 - parent.timestamp console.info( `Signer most recently signed block ${parent.number} ${Math.ceil(timedelta)} seconds ago` From 1afa4df52f8fdc1505498957373713d477b1fb4f Mon Sep 17 00:00:00 2001 From: Victor Graf Date: Tue, 26 Nov 2019 15:15:10 -0600 Subject: [PATCH 08/23] update docs --- packages/cli/src/commands/validator/status.ts | 3 ++- .../docs/command-line-interface/validator.md | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/validator/status.ts b/packages/cli/src/commands/validator/status.ts index 67c5f96c8be..4d1fc5d092d 100644 --- a/packages/cli/src/commands/validator/status.ts +++ b/packages/cli/src/commands/validator/status.ts @@ -7,7 +7,8 @@ import { newCheckBuilder } from '../../utils/checks' import { Flags } from '../../utils/command' export default class ValidatorOnline extends BaseCommand { - static description = 'Show information about whether the signer is elected and validating' + static description = + 'Show information about whether the signer is elected and validating. This command will check that the signer, which may be a validator or its authorized signer, meets the registration requirements, is currently elected, and is actively signing blocks.' // How many blocks to look back for proposals of this signer. static readonly lookback = 50 diff --git a/packages/docs/command-line-interface/validator.md b/packages/docs/command-line-interface/validator.md index fc7e1f015c2..ee5e37904f5 100644 --- a/packages/docs/command-line-interface/validator.md +++ b/packages/docs/command-line-interface/validator.md @@ -125,6 +125,24 @@ EXAMPLE _See code: [packages/cli/src/commands/validator/show.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/validator/show.ts)_ +### Status + +Show information about whether the signer is elected and validating. This command will check that the signer, which may be a validator or its authorized signer, meets the registration requirements, is currently elected, and is actively signing blocks. + +``` +USAGE + $ celocli validator:status + +OPTIONS + --signer=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d (required) address of the signer to check if elected and + validating + +EXAMPLE + status --signer 0x5409ED021D9299bf6814279A6A1411A7e866A631 +``` + +_See code: [packages/cli/src/commands/validator/status.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/validator/status.ts)_ + ### Update-bls-public-key Update the BLS public key for a Validator to be used in consensus. Regular (ECDSA and BLS) key rotation is recommended for Validator operational security. From b1bd6f8e92515cc6f1e89aa5955509492a306030 Mon Sep 17 00:00:00 2001 From: Victor Graf Date: Tue, 26 Nov 2019 16:17:12 -0600 Subject: [PATCH 09/23] provide more helpful message about election status --- packages/cli/src/commands/validator/status.ts | 26 +++++++++++++------ packages/cli/src/utils/checks.ts | 2 +- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/packages/cli/src/commands/validator/status.ts b/packages/cli/src/commands/validator/status.ts index 4d1fc5d092d..51a3dba95cf 100644 --- a/packages/cli/src/commands/validator/status.ts +++ b/packages/cli/src/commands/validator/status.ts @@ -8,7 +8,7 @@ import { Flags } from '../../utils/command' export default class ValidatorOnline extends BaseCommand { static description = - 'Show information about whether the signer is elected and validating. This command will check that the signer, which may be a validator or its authorized signer, meets the registration requirements, is currently elected, and is actively signing blocks.' + 'Show information about whether the signer is elected and validating. This command will check that the signer, which may be the validator itself, meets the registration requirements, is currently elected, and is actively signing blocks.' // How many blocks to look back for proposals of this signer. static readonly lookback = 50 @@ -35,15 +35,25 @@ export default class ValidatorOnline extends BaseCommand { .signerAccountIsValidator() .runChecks() - cli.action.start('Querying current validator set') const election = await this.kit.contracts.getElection() - const size = await election.numberValidatorsInCurrentSet() - const set = await Promise.all(range(size).map(election.validatorAddressFromCurrentSet)) - cli.action.stop() - - const signerIndex = set.map((a) => a.toLowerCase()).indexOf(flags.signer.toLowerCase()) + const signers = await election.getCurrentValidatorSigners() + const signerIndex = signers.map((a) => a.toLowerCase()).indexOf(flags.signer.toLowerCase()) if (signerIndex < 0) { - this.error(`Signer ${flags.signer} is not in the validator set for this epoch`) + // Determine whether the signer will be elected at the next epoch to provide a helpful error. + const frontrunners = await election.electValidatorSigners() + if (frontrunners.map((a) => a.toLowerCase()).indexOf(flags.signer.toLowerCase()) >= 0) { + this.error( + `Signer ${ + flags.signer + } is not elected for this epoch, but is currently winning in the upcoming election. Wait for the next epoch.` + ) + } else { + this.error( + `Signer ${ + flags.signer + } is not elected for this epoch, and is not currently winning the upcoming election.` + ) + } } cli.action.start('Searching for proposed blocks') diff --git a/packages/cli/src/utils/checks.ts b/packages/cli/src/utils/checks.ts index d51d3b21df4..ac3944e7983 100644 --- a/packages/cli/src/utils/checks.ts +++ b/packages/cli/src/utils/checks.ts @@ -56,7 +56,7 @@ class CheckBuilder { } } - withAccounts(f: (lockedGold: AccountsWrapper) => A): () => Promise> { + withAccounts(f: (accounts: AccountsWrapper) => A): () => Promise> { return async () => { const accounts = await this.kit.contracts.getAccounts() return f(accounts) as Resolve From 3a677016657f64d484021fac2a7ad5624a733c5b Mon Sep 17 00:00:00 2001 From: Victor Graf Date: Tue, 26 Nov 2019 16:54:00 -0600 Subject: [PATCH 10/23] allow specifying validator and lookback --- packages/cli/src/commands/validator/status.ts | 84 ++++++++++++------- .../docs/command-line-interface/validator.md | 15 ++-- 2 files changed, 61 insertions(+), 38 deletions(-) diff --git a/packages/cli/src/commands/validator/status.ts b/packages/cli/src/commands/validator/status.ts index 51a3dba95cf..1a174f77ce2 100644 --- a/packages/cli/src/commands/validator/status.ts +++ b/packages/cli/src/commands/validator/status.ts @@ -1,3 +1,4 @@ +import { flags } from '@oclif/command' import { cli } from 'cli-ux' import { range } from 'lodash' import * as rlp from 'rlp' @@ -8,78 +9,97 @@ import { Flags } from '../../utils/command' export default class ValidatorOnline extends BaseCommand { static description = - 'Show information about whether the signer is elected and validating. This command will check that the signer, which may be the validator itself, meets the registration requirements, is currently elected, and is actively signing blocks.' - - // How many blocks to look back for proposals of this signer. - static readonly lookback = 50 + 'Show information about whether the validator signer is elected and validating. This command will check that the validator meets the registration requirements, and its signer is currently elected and actively signing blocks.' static flags = { ...BaseCommand.flags, signer: Flags.address({ - required: true, + description: 'address of the validator to check if elected and validating', + }), + validator: Flags.address({ description: 'address of the signer to check if elected and validating', }), + lookback: flags.integer({ + description: 'how many blocks to look back for signer activity', + default: 50, + }), } - static examples = ['status --signer 0x5409ED021D9299bf6814279A6A1411A7e866A631'] + static examples = [ + 'status --validator 0x5409ED021D9299bf6814279A6A1411A7e866A631', + 'status --signer 0x738337030fAeb1E805253228881d844b5332fB4c', + 'status --signer 0x738337030fAeb1E805253228881d844b5332fB4c --lookback 100', + ] requireSynced = true async run() { const { flags } = this.parse(ValidatorOnline) + if (!flags.validator && !flags.signer) { + this.error('Either validator or signer must be specified') + } + + // Determine the validator signer to check. + let signer: Address + if (flags.validator) { + const accounts = await this.kit.contracts.getAccounts() + signer = await accounts.getValidatorSigner(flags.validator) + console.info(`Identified ${signer} as the authorized validator signer`) + + // If the user specified both, verify that it matches what is stored on chain. + if (flags.signer && signer.toLowerCase() !== flags.signer.toLowerCase()) { + this.error(`${flags.signer} is not the authorized signer for ${flags.validator}`) + } + } else { + signer = flags.signer! + } + // Use redundant checks to help the user diagnose issues. - await newCheckBuilder(this, flags.signer) + await newCheckBuilder(this, signer) .canSignValidatorTxs() .signerMeetsValidatorBalanceRequirements() .signerAccountIsValidator() .runChecks() + // Determine if the signer is elected, and get their index in the validator set. const election = await this.kit.contracts.getElection() const signers = await election.getCurrentValidatorSigners() - const signerIndex = signers.map((a) => a.toLowerCase()).indexOf(flags.signer.toLowerCase()) + const signerIndex = signers.map((a) => a.toLowerCase()).indexOf(signer.toLowerCase()) if (signerIndex < 0) { // Determine whether the signer will be elected at the next epoch to provide a helpful error. const frontrunners = await election.electValidatorSigners() - if (frontrunners.map((a) => a.toLowerCase()).indexOf(flags.signer.toLowerCase()) >= 0) { + if (frontrunners.map((a) => a.toLowerCase()).indexOf(signer.toLowerCase()) >= 0) { this.error( - `Signer ${ - flags.signer - } is not elected for this epoch, but is currently winning in the upcoming election. Wait for the next epoch.` + `Signer ${signer} is not elected for this epoch, but is currently winning in the upcoming election. Wait for the next epoch.` ) } else { this.error( - `Signer ${ - flags.signer - } is not elected for this epoch, and is not currently winning the upcoming election.` + `Signer ${signer} is not elected for this epoch, and is not currently winning the upcoming election.` ) } } + console.info('Signer has been elected for this epoch') + + if (flags.lookback <= 0) { + return + } - cli.action.start('Searching for proposed blocks') + // Retrieve blocks to examine for the singers signature. + cli.action.start(`Retreiving the last ${flags.lookback} blocks`) const latest = await this.web3.eth.getBlock('latest') const blocks = await Promise.all( - range(1, ValidatorOnline.lookback).map((i) => this.web3.eth.getBlock(latest.number - i)) + range(1, flags.lookback).map((i) => this.web3.eth.getBlock(latest.number - i)) ) blocks.splice(0, 0, latest) cli.action.stop() - for (const [i, block] of blocks.entries()) { - if (this.validatorIndexSignedParentHeader(block, signerIndex)) { - const parent = - i + 1 < blocks.length ? blocks[i + 1] : await this.web3.eth.getBlock(block.number - 1) - const timedelta = Date.now() / 1000 - parent.timestamp - console.info( - `Signer most recently signed block ${parent.number} ${Math.ceil(timedelta)} seconds ago` - ) - return - } + const signedCount = blocks.filter((b) => this.validatorIndexSignedParentHeader(b, signerIndex)) + .length + if (signedCount === 0) { + this.error(`Signer has not signed any of the last ${flags.lookback} blocks`) } - this.error( - `Signer is part of the current validator set, but has not signed any of the last ${ - ValidatorOnline.lookback - } blocks` - ) + console.info(`Signer has signed ${signedCount} of the last ${flags.lookback} blocks`) } // Check the parent seal in the given block header for a bit indicating at the given index. diff --git a/packages/docs/command-line-interface/validator.md b/packages/docs/command-line-interface/validator.md index ee5e37904f5..9e60d0e5b74 100644 --- a/packages/docs/command-line-interface/validator.md +++ b/packages/docs/command-line-interface/validator.md @@ -127,18 +127,21 @@ _See code: [packages/cli/src/commands/validator/show.ts](https://github.com/celo ### Status -Show information about whether the signer is elected and validating. This command will check that the signer, which may be a validator or its authorized signer, meets the registration requirements, is currently elected, and is actively signing blocks. +Show information about whether the validator signer is elected and validating. This command will check that the validator meets the registration requirements, and its signer is currently elected and actively signing blocks. ``` USAGE $ celocli validator:status OPTIONS - --signer=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d (required) address of the signer to check if elected and - validating - -EXAMPLE - status --signer 0x5409ED021D9299bf6814279A6A1411A7e866A631 + --lookback=lookback [default: 50] how many blocks to look back for signer activity + --signer=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d address of the validator to check if elected and validating + --validator=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d address of the signer to check if elected and validating + +EXAMPLES + status --validator 0x5409ED021D9299bf6814279A6A1411A7e866A631 + status --signer 0x738337030fAeb1E805253228881d844b5332fB4c + status --signer 0x738337030fAeb1E805253228881d844b5332fB4c --lookback 100 ``` _See code: [packages/cli/src/commands/validator/status.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/validator/status.ts)_ From 1a61d52cd970ddec6d75ddfd6acb406896bf1d47 Mon Sep 17 00:00:00 2001 From: Victor Graf Date: Tue, 26 Nov 2019 16:57:42 -0600 Subject: [PATCH 11/23] add note to running-a-validator doc --- packages/docs/getting-started/running-a-validator.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/docs/getting-started/running-a-validator.md b/packages/docs/getting-started/running-a-validator.md index d81cbc90971..8f03597c343 100644 --- a/packages/docs/getting-started/running-a-validator.md +++ b/packages/docs/getting-started/running-a-validator.md @@ -329,10 +329,10 @@ celocli election:list If you find your Validator still not getting elected you may need to faucet yourself more funds and lock more gold in order to be able to cast more votes for your Validator Group! -At any moment you can check the currently elected validators by running the following command: +You can check the status of your validator, including whether it is elected and signing blocks, by running: ```bash -celocli election:current +celocli validator:status --validator $CELO_VALIDATOR_ADDRESS ``` ### Stop Validating From 06b06addc0d269f4ae56f1d0d62d2d917386cbcc Mon Sep 17 00:00:00 2001 From: Victor Graf Date: Tue, 26 Nov 2019 17:28:11 -0600 Subject: [PATCH 12/23] add address import --- packages/cli/src/commands/validator/status.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cli/src/commands/validator/status.ts b/packages/cli/src/commands/validator/status.ts index 1a174f77ce2..4f0b1a91d54 100644 --- a/packages/cli/src/commands/validator/status.ts +++ b/packages/cli/src/commands/validator/status.ts @@ -1,3 +1,4 @@ +import { Address } from '@celo/contractkit' import { flags } from '@oclif/command' import { cli } from 'cli-ux' import { range } from 'lodash' From a6a5fa942c2bdfed1583c8be65f8474f83d6fbab Mon Sep 17 00:00:00 2001 From: Victor Graf Date: Mon, 2 Dec 2019 21:50:15 -0800 Subject: [PATCH 13/23] checkpoint --- packages/cli/src/commands/validator/status.ts | 77 +++++++++++-------- packages/cli/src/utils/checks.ts | 14 +++- 2 files changed, 59 insertions(+), 32 deletions(-) diff --git a/packages/cli/src/commands/validator/status.ts b/packages/cli/src/commands/validator/status.ts index 4f0b1a91d54..90c387807f9 100644 --- a/packages/cli/src/commands/validator/status.ts +++ b/packages/cli/src/commands/validator/status.ts @@ -1,14 +1,15 @@ import { Address } from '@celo/contractkit' +import { eqAddress } from '@celo/utils/lib/address' +import { concurrentMap } from '@celo/utils/lib/async' import { flags } from '@oclif/command' import { cli } from 'cli-ux' -import { range } from 'lodash' import * as rlp from 'rlp' import { Block } from 'web3/eth/types' import { BaseCommand } from '../../base' -import { newCheckBuilder } from '../../utils/checks' +import { CheckBuilder, newCheckBuilder } from '../../utils/checks' import { Flags } from '../../utils/command' -export default class ValidatorOnline extends BaseCommand { +export default class ValidatorStatus extends BaseCommand { static description = 'Show information about whether the validator signer is elected and validating. This command will check that the validator meets the registration requirements, and its signer is currently elected and actively signing blocks.' @@ -22,7 +23,7 @@ export default class ValidatorOnline extends BaseCommand { }), lookback: flags.integer({ description: 'how many blocks to look back for signer activity', - default: 50, + default: 100, }), } @@ -35,42 +36,53 @@ export default class ValidatorOnline extends BaseCommand { requireSynced = true async run() { - const { flags } = this.parse(ValidatorOnline) + const res = this.parse(ValidatorStatus) - if (!flags.validator && !flags.signer) { + // Determine the validator signer to check. + let signer: Address + if (res.flags.signer) { + signer = res.flags.signer + } else { + signer = await accounts.getValidatorSigner(res.flags.validator) + } + + // Check that the specified validator or signer meets the validator requirements. + const checker = newCheckBuilder(this, res.flags.signer) + if (res.flags.validator) { + const account = res.flags.validator + checker.isValidator(account).meetsValidatorBalanceRequirements(account) + } else if (res.flags.signer) { + cheker.signerMeetsValidatorBalanceRequirements().signerAccountIsValidator() + } else { this.error('Either validator or signer must be specified') } + await checker.runChecks() - // Determine the validator signer to check. + // Assign and verify the signer. let signer: Address - if (flags.validator) { - const accounts = await this.kit.contracts.getAccounts() - signer = await accounts.getValidatorSigner(flags.validator) - console.info(`Identified ${signer} as the authorized validator signer`) - - // If the user specified both, verify that it matches what is stored on chain. - if (flags.signer && signer.toLowerCase() !== flags.signer.toLowerCase()) { - this.error(`${flags.signer} is not the authorized signer for ${flags.validator}`) + if (res.flags.signer) { + signer = res.flags.signer + if (res.flags.validator) { + const accounts = await this.kit.contracts.getAccounts() + if ((await accounts.signerToAccount(signer)) !== res.flags.validator) { + this.error( + `Signer ${signer} has never been authorized for account ${res.flags.validator}` + ) + } } } else { - signer = flags.signer! + signer = await accounts.getValidatorSigner(res.flags.validator!) + console.info(`Identified ${signer} as the authorized validator signer`) } - // Use redundant checks to help the user diagnose issues. - await newCheckBuilder(this, signer) - .canSignValidatorTxs() - .signerMeetsValidatorBalanceRequirements() - .signerAccountIsValidator() - .runChecks() - // Determine if the signer is elected, and get their index in the validator set. const election = await this.kit.contracts.getElection() const signers = await election.getCurrentValidatorSigners() - const signerIndex = signers.map((a) => a.toLowerCase()).indexOf(signer.toLowerCase()) + const signerIndex = signers.map((a) => eqAddress(a, signer)).indexOf(true) if (signerIndex < 0) { // Determine whether the signer will be elected at the next epoch to provide a helpful error. const frontrunners = await election.electValidatorSigners() - if (frontrunners.map((a) => a.toLowerCase()).indexOf(signer.toLowerCase()) >= 0) { + if (frontrunners.some((a) => eqAddress(a, signer))) { this.error( `Signer ${signer} is not elected for this epoch, but is currently winning in the upcoming election. Wait for the next epoch.` ) @@ -82,15 +94,15 @@ export default class ValidatorOnline extends BaseCommand { } console.info('Signer has been elected for this epoch') - if (flags.lookback <= 0) { + if (res.flags.lookback <= 0) { return } // Retrieve blocks to examine for the singers signature. - cli.action.start(`Retreiving the last ${flags.lookback} blocks`) + cli.action.start(`Retreiving the last ${res.flags.lookback} blocks`) const latest = await this.web3.eth.getBlock('latest') - const blocks = await Promise.all( - range(1, flags.lookback).map((i) => this.web3.eth.getBlock(latest.number - i)) + const blocks = await concurrentMap(10, [...Array(res.flags.lookback).keys()].slice(1), (i) => + this.web3.eth.getBlock(latest.number - i) ) blocks.splice(0, 0, latest) cli.action.stop() @@ -98,9 +110,12 @@ export default class ValidatorOnline extends BaseCommand { const signedCount = blocks.filter((b) => this.validatorIndexSignedParentHeader(b, signerIndex)) .length if (signedCount === 0) { - this.error(`Signer has not signed any of the last ${flags.lookback} blocks`) + this.error(`Signer has not signed any of the last ${res.flags.lookback} blocks`) } - console.info(`Signer has signed ${signedCount} of the last ${flags.lookback} blocks`) + console.info(`Signer has signed ${signedCount} of the last ${res.flags.lookback} blocks`) + + const proposedCount = blocks.filter((b) => b.miner === signer).length + console.info(`Signer has proposed ${proposedCount} of the last ${res.flags.lookback} blocks`) } // Check the parent seal in the given block header for a bit indicating at the given index. diff --git a/packages/cli/src/utils/checks.ts b/packages/cli/src/utils/checks.ts index ac3944e7983..645171ee4aa 100644 --- a/packages/cli/src/utils/checks.ts +++ b/packages/cli/src/utils/checks.ts @@ -108,12 +108,24 @@ class CheckBuilder { signerMeetsValidatorGroupBalanceRequirements = () => this.addCheck( - `Signer's account has enough locked gold for registration`, + `Signer's account has enough locked gold for group registration`, this.withValidators((v, _signer, account) => v.meetsValidatorGroupBalanceRequirements(account) ) ) + meetsValidatorBalanceRequirements = (account: Address) => + this.addCheck( + `${account} has enough locked gold for registration`, + this.withValidators((v) => v.meetsValidatorBalanceRequirements(account)) + ) + + meetsValidatorGroupBalanceRequirements = (account: Address) => + this.addCheck( + `${account} has enough locked gold for group registration`, + this.withValidators((v) => v.meetsValidatorGroupBalanceRequirements(account)) + ) + isNotAccount = (address: Address) => this.addCheck( `${address} is not an Account`, From 2962cd98c04df01f0b5ed807d087c4deb8f952a1 Mon Sep 17 00:00:00 2001 From: Victor Graf Date: Tue, 3 Dec 2019 15:09:57 -0800 Subject: [PATCH 14/23] add fuinctions to parse Istanbul extra data to utils --- packages/cli/src/commands/account/unlock.ts | 3 +- packages/cli/src/commands/validator/status.ts | 35 +++------- packages/utils/src/istanbul.test.ts | 64 +++++++++++++++++++ packages/utils/src/istanbul.ts | 62 ++++++++++++++++++ 4 files changed, 136 insertions(+), 28 deletions(-) create mode 100644 packages/utils/src/istanbul.test.ts create mode 100644 packages/utils/src/istanbul.ts diff --git a/packages/cli/src/commands/account/unlock.ts b/packages/cli/src/commands/account/unlock.ts index 5036b501811..645e185e4dc 100644 --- a/packages/cli/src/commands/account/unlock.ts +++ b/packages/cli/src/commands/account/unlock.ts @@ -19,7 +19,8 @@ export default class Unlock extends BaseCommand { // Unlock till geth exits // Source: https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_unlockaccount const unlockDurationInMs = 0 - const password = res.flags.password || (await cli.prompt('Password', { type: 'hide' })) + const password = + res.flags.password || (await cli.prompt('Password', { type: 'hide', required: false })) this.web3.eth.personal.unlockAccount(res.flags.account, password, unlockDurationInMs) } diff --git a/packages/cli/src/commands/validator/status.ts b/packages/cli/src/commands/validator/status.ts index 90c387807f9..3087bca5f06 100644 --- a/packages/cli/src/commands/validator/status.ts +++ b/packages/cli/src/commands/validator/status.ts @@ -1,12 +1,11 @@ import { Address } from '@celo/contractkit' import { eqAddress } from '@celo/utils/lib/address' import { concurrentMap } from '@celo/utils/lib/async' +import { bitIsSet, parseBlockExtraData } from '@celo/utils/lib/istanbul' import { flags } from '@oclif/command' import { cli } from 'cli-ux' -import * as rlp from 'rlp' -import { Block } from 'web3/eth/types' import { BaseCommand } from '../../base' -import { CheckBuilder, newCheckBuilder } from '../../utils/checks' +import { newCheckBuilder } from '../../utils/checks' import { Flags } from '../../utils/command' export default class ValidatorStatus extends BaseCommand { @@ -38,21 +37,13 @@ export default class ValidatorStatus extends BaseCommand { async run() { const res = this.parse(ValidatorStatus) - // Determine the validator signer to check. - let signer: Address - if (res.flags.signer) { - signer = res.flags.signer - } else { - signer = await accounts.getValidatorSigner(res.flags.validator) - } - // Check that the specified validator or signer meets the validator requirements. const checker = newCheckBuilder(this, res.flags.signer) if (res.flags.validator) { const account = res.flags.validator checker.isValidator(account).meetsValidatorBalanceRequirements(account) } else if (res.flags.signer) { - cheker.signerMeetsValidatorBalanceRequirements().signerAccountIsValidator() + checker.signerMeetsValidatorBalanceRequirements().signerAccountIsValidator() } else { this.error('Either validator or signer must be specified') } @@ -71,6 +62,7 @@ export default class ValidatorStatus extends BaseCommand { } } } else { + const accounts = await this.kit.contracts.getAccounts() signer = await accounts.getValidatorSigner(res.flags.validator!) console.info(`Identified ${signer} as the authorized validator signer`) } @@ -94,7 +86,7 @@ export default class ValidatorStatus extends BaseCommand { } console.info('Signer has been elected for this epoch') - if (res.flags.lookback <= 0) { + if (((res.flags && res.flags.lookback) || 0) <= 0) { return } @@ -107,8 +99,9 @@ export default class ValidatorStatus extends BaseCommand { blocks.splice(0, 0, latest) cli.action.stop() - const signedCount = blocks.filter((b) => this.validatorIndexSignedParentHeader(b, signerIndex)) - .length + const signedCount = blocks.filter((b) => + bitIsSet(parseBlockExtraData(b.extraData).parentAggregatedSeal.bitmap, signerIndex) + ).length if (signedCount === 0) { this.error(`Signer has not signed any of the last ${res.flags.lookback} blocks`) } @@ -117,16 +110,4 @@ export default class ValidatorStatus extends BaseCommand { const proposedCount = blocks.filter((b) => b.miner === signer).length console.info(`Signer has proposed ${proposedCount} of the last ${res.flags.lookback} blocks`) } - - // Check the parent seal in the given block header for a bit indicating at the given index. - private validatorIndexSignedParentHeader(block: Block, index: number) { - // See https://github.com/celo-org/celo-blockchain/blob/master/core/types/istanbul.go - // tslint:disable:no-bitwise - // @ts-ignore - const parentSealBitmap = rlp.decode('0x' + block.extraData.slice(66))[5][0] - return ( - (parentSealBitmap[parentSealBitmap.length - 1 - Math.floor(index / 8)] & (1 << index % 8)) > 0 - ) - // tslint:enable:no-bitwise - } } diff --git a/packages/utils/src/istanbul.test.ts b/packages/utils/src/istanbul.test.ts new file mode 100644 index 00000000000..9b8a89cd1f7 --- /dev/null +++ b/packages/utils/src/istanbul.test.ts @@ -0,0 +1,64 @@ +import BigNumber from 'bignumber.js' +import { bitIsSet, Bitmap, IstanbulExtra, parseBlockExtraData } from './istanbul' + +describe('Istanbul utilities', () => { + describe('parseBlockExtraData', () => { + const testExtraData = + '0xd983010817846765746888676f312e31312e358664617277696e000000000000f90127d594fd0893e334' + + 'c6401188ae77072546979b94d91813f862b8604fa3f67fc913878b068d1fa1cdddc54913d3bf988dbe5a36' + + 'a20fa888f20d4894c408a6773f3d7bde11154f2a3076b700d345a42fd25a0e5e83f4db5586ac7979ac2053' + + 'cd95d8f2efd3e959571ceccaa743e02cf4be3f5d7aaddb0b06fc9aff0001b84188022a71c12a801a4318e2' + + '7eeb5c82aa923160632c63b0eae4457ed120356ddb549fb7c4e4865728478aa61c19b9abe10ec7db34c866' + + '2b003b139188e99edcd400f30db040c083f6b6e29a6a2cab4498f50d37d458a2458b5438c9faeae8598cd4' + + '7f4ed6e17ca10e1f87c6faa14d5e3e393f0e0080f30db06107252c187052f8212ef5cfc9052fe59c7af040' + + 'e77a09b762fd51060220511e93d1c681be8883043f8a93ea637492818080' + + const expected: IstanbulExtra = { + addedValidators: ['0xFd0893E334C6401188Ae77072546979B94d91813'], + addedValidatorsPublicKeys: [ + '0x4fa3f67fc913878b068d1fa1cdddc54913d3bf988dbe5a36a20fa888f20d4894c408a6773f3d7bde11154f2a3076b700d345a42fd25a0e5e83f4db5586ac7979ac2053cd95d8f2efd3e959571ceccaa743e02cf4be3f5d7aaddb0b06fc9aff00', + ], + removedValidators: new BigNumber(1), + seal: + '0x88022a71c12a801a4318e27eeb5c82aa923160632c63b0eae4457ed120356ddb549fb7c4e4865728478aa61c19b9abe10ec7db34c8662b003b139188e99edcd400', + aggregatedSeal: { + bitmap: new BigNumber(13), + signature: + '0x40c083f6b6e29a6a2cab4498f50d37d458a2458b5438c9faeae8598cd47f4ed6e17ca10e1f87c6faa14d5e3e393f0e00', + round: new BigNumber(0), + }, + parentAggregatedSeal: { + bitmap: new BigNumber(13), + signature: + '0x6107252c187052f8212ef5cfc9052fe59c7af040e77a09b762fd51060220511e93d1c681be8883043f8a93ea63749281', + round: new BigNumber(0), + }, + } + + it('should decode the Istanbul extra data correctly', () => { + expect(parseBlockExtraData(testExtraData)).toEqual(expected) + }) + }) + + describe('bitIsSet', () => { + const testBitmap: Bitmap = new BigNumber('0x40d1', 16) + const testBitmapAsBinary = ('0100' + '0000' + '1101' + '0001') + .split('') + .map((b) => b === '1') + .reverse() + + it('should correctly identify set bits within expected index', () => { + for (let i = 0; i < testBitmapAsBinary.length; i++) { + expect(bitIsSet(testBitmap, i)).toBe(testBitmapAsBinary[i]) + } + }) + + it('should return false when the index is too large', () => { + expect(bitIsSet(testBitmap, 1000)).toBe(false) + }) + + it('should throw an error when the index is negative', () => { + expect(() => bitIsSet(testBitmap, -1)).toThrow() + }) + }) +}) diff --git a/packages/utils/src/istanbul.ts b/packages/utils/src/istanbul.ts new file mode 100644 index 00000000000..e1c2165811c --- /dev/null +++ b/packages/utils/src/istanbul.ts @@ -0,0 +1,62 @@ +import { Address } from '@celo/contractkit' +import BigNumber from 'bignumber.js' +import { toChecksumAddress } from 'ethereumjs-util' +import * as rlp from 'rlp' + +// See https://github.com/celo-org/celo-blockchain/blob/master/core/types/istanbul.go +export const ISTANBUL_EXTRA_VANITY_BYTES = 32 + +export type Bitmap = BigNumber + +// Aggregated BLS signatures for a block. +export interface Seal { + bitmap: Bitmap + signature: string + round: BigNumber +} + +// Extra data in the block header to support Istanbul BFT. +export interface IstanbulExtra { + addedValidators: Address[] + addedValidatorsPublicKeys: string[] + removedValidators: Bitmap + seal: string + aggregatedSeal: Seal + parentAggregatedSeal: Seal +} + +function bigNumberFromBuffer(data: Buffer): BigNumber { + return new BigNumber('0x' + (data.toString('hex') || '0'), 16) +} + +function sealFromBuffers(data: Buffer[]): Seal { + return { + bitmap: bigNumberFromBuffer(data[0]), + signature: '0x' + data[1].toString('hex'), + round: bigNumberFromBuffer(data[2]), + } +} + +// Parse RLP encoded block extra data into an IstanbulExtra object. +export function parseBlockExtraData(data: string): IstanbulExtra { + const buffer = Buffer.from(data.replace(/^0x/, ''), 'hex') + const decode: any = rlp.decode('0x' + buffer.slice(ISTANBUL_EXTRA_VANITY_BYTES).toString('hex')) + return { + addedValidators: decode[0].map((addr: Buffer) => toChecksumAddress(addr.toString('hex'))), + addedValidatorsPublicKeys: decode[1].map((key: Buffer) => '0x' + key.toString('hex')), + removedValidators: bigNumberFromBuffer(decode[2]), + seal: '0x' + decode[3].toString('hex'), + aggregatedSeal: sealFromBuffers(decode[4]), + parentAggregatedSeal: sealFromBuffers(decode[5]), + } +} + +export function bitIsSet(bitmap: Bitmap, index: number): boolean { + if (index < 0) { + throw new Error(`bit index must be greate than zero: got ${index}`) + } + return bitmap + .idiv('1' + '0'.repeat(index), 2) + .mod(2) + .gt(0) +} From ace681e12517e1eae475f5a30e04b5cf54f4de2f Mon Sep 17 00:00:00 2001 From: Victor Graf Date: Tue, 3 Dec 2019 15:10:38 -0800 Subject: [PATCH 15/23] remove obviated isvalidator command --- .../cli/src/commands/account/isvalidator.ts | 33 ------------------- 1 file changed, 33 deletions(-) delete mode 100644 packages/cli/src/commands/account/isvalidator.ts diff --git a/packages/cli/src/commands/account/isvalidator.ts b/packages/cli/src/commands/account/isvalidator.ts deleted file mode 100644 index 316514dfa8e..00000000000 --- a/packages/cli/src/commands/account/isvalidator.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { eqAddress } from '@celo/utils/lib/address' -import { BaseCommand } from '../../base' -import { Args } from '../../utils/command' - -export default class IsValidator extends BaseCommand { - static description = - 'Check whether a given address is elected to be validating in the current epoch' - - static flags = { - ...BaseCommand.flags, - } - - static args = [Args.address('address')] - - static examples = ['isvalidator 0x5409ed021d9299bf6814279a6a1411a7e866a631'] - - async run() { - const { args } = this.parse(IsValidator) - - const election = await this.kit.contracts.getElection() - const numberValidators = await election.numberValidatorsInCurrentSet() - - for (let i = 0; i < numberValidators; i++) { - const validatorAddress = await election.validatorAddressFromCurrentSet(i) - if (eqAddress(validatorAddress, args.address)) { - console.log(`${args.address} is in the current validator set`) - return - } - } - - console.log(`${args.address} is not currently in the validator set`) - } -} From 2bdf52f7bbce74ec288a5ee8db8cb0f22657a03c Mon Sep 17 00:00:00 2001 From: Victor Graf Date: Tue, 3 Dec 2019 15:28:08 -0800 Subject: [PATCH 16/23] fix current and run commands --- packages/cli/src/commands/election/current.ts | 4 +--- packages/cli/src/commands/election/run.ts | 6 ++---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/cli/src/commands/election/current.ts b/packages/cli/src/commands/election/current.ts index 9586d512cf2..9ab5de4abba 100644 --- a/packages/cli/src/commands/election/current.ts +++ b/packages/cli/src/commands/election/current.ts @@ -3,14 +3,12 @@ import { BaseCommand } from '../../base' export default class ElectionCurrent extends BaseCommand { static description = - 'Outputs the set of validators currently participating in BFT to create blocks. The validator set is re-elected at the end of every epoch.' + 'Outputs the set of validators currently participating in BFT to create blocks. An election is run to select the validator set at the end of every epoch.' static flags = { ...BaseCommand.flags, } - static examples = ['current'] - async run() { cli.action.start('Fetching currently elected Validators') const election = await this.kit.contracts.getElection() diff --git a/packages/cli/src/commands/election/run.ts b/packages/cli/src/commands/election/run.ts index 3026f7b3e27..66432d35c8d 100644 --- a/packages/cli/src/commands/election/run.ts +++ b/packages/cli/src/commands/election/run.ts @@ -9,13 +9,11 @@ export default class ElectionRun extends BaseCommand { ...BaseCommand.flags, } - static examples = ['run'] - async run() { cli.action.start('Running mock election') const election = await this.kit.contracts.getElection() const validators = await this.kit.contracts.getValidators() - const signers = await election.getCurrentValidatorSigners() + const signers = await election.electValidatorSigners() const validatorList = await Promise.all( signers.map((addr) => validators.getValidatorFromSigner(addr)) ) @@ -24,7 +22,7 @@ export default class ElectionRun extends BaseCommand { address: {}, name: {}, affiliation: {}, - score: {}, + score: { get: (v) => v.score.toFixed() }, ecdsaPublicKey: {}, blsPublicKey: {}, }) From a09192c4564ecff4e29e3a042eb8b3bbce5a0909 Mon Sep 17 00:00:00 2001 From: Victor Graf Date: Tue, 3 Dec 2019 15:30:33 -0800 Subject: [PATCH 17/23] update docs --- packages/docs/command-line-interface/account.md | 14 -------------- packages/docs/command-line-interface/election.md | 8 +------- packages/docs/command-line-interface/validator.md | 5 ++++- 3 files changed, 5 insertions(+), 22 deletions(-) diff --git a/packages/docs/command-line-interface/account.md b/packages/docs/command-line-interface/account.md index 09eaedcaf0c..6dbce3678fb 100644 --- a/packages/docs/command-line-interface/account.md +++ b/packages/docs/command-line-interface/account.md @@ -189,20 +189,6 @@ EXAMPLE _See code: [packages/cli/src/commands/account/get-metadata.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/account/get-metadata.ts)_ -### Isvalidator - -Check whether a given address is elected to be validating in the current epoch - -``` -USAGE - $ celocli account:isvalidator ADDRESS - -EXAMPLE - isvalidator 0x5409ed021d9299bf6814279a6a1411a7e866a631 -``` - -_See code: [packages/cli/src/commands/account/isvalidator.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/account/isvalidator.ts)_ - ### New Creates a new account diff --git a/packages/docs/command-line-interface/election.md b/packages/docs/command-line-interface/election.md index 0c2cfc870f0..6c295bcbfcb 100644 --- a/packages/docs/command-line-interface/election.md +++ b/packages/docs/command-line-interface/election.md @@ -25,14 +25,11 @@ _See code: [packages/cli/src/commands/election/activate.ts](https://github.com/c ### Current -Outputs the set of validators currently participating in BFT to create blocks. The validator set is re-elected at the end of every epoch. +Outputs the set of validators currently participating in BFT to create blocks. An election is run to select the validator set at the end of every epoch. ``` USAGE $ celocli election:current - -EXAMPLE - current ``` _See code: [packages/cli/src/commands/election/current.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/election/current.ts)_ @@ -78,9 +75,6 @@ Runs a "mock" election and prints out the validators that would be elected if th ``` USAGE $ celocli election:run - -EXAMPLE - run ``` _See code: [packages/cli/src/commands/election/run.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/election/run.ts)_ diff --git a/packages/docs/command-line-interface/validator.md b/packages/docs/command-line-interface/validator.md index a7e9e1a2025..f46ddfb7d88 100644 --- a/packages/docs/command-line-interface/validator.md +++ b/packages/docs/command-line-interface/validator.md @@ -140,8 +140,11 @@ USAGE $ celocli validator:status OPTIONS - --lookback=lookback [default: 50] how many blocks to look back for signer activity + --lookback=lookback [default: 100] how many blocks to look back for signer + activity + --signer=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d address of the validator to check if elected and validating + --validator=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d address of the signer to check if elected and validating EXAMPLES From d5fc096a0646f14d8121c9546ab97abe37743409 Mon Sep 17 00:00:00 2001 From: Victor Graf Date: Tue, 3 Dec 2019 15:50:26 -0800 Subject: [PATCH 18/23] fix relative path --- packages/utils/src/istanbul.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/utils/src/istanbul.ts b/packages/utils/src/istanbul.ts index e1c2165811c..d7f52bec3d8 100644 --- a/packages/utils/src/istanbul.ts +++ b/packages/utils/src/istanbul.ts @@ -1,7 +1,7 @@ -import { Address } from '@celo/contractkit' import BigNumber from 'bignumber.js' import { toChecksumAddress } from 'ethereumjs-util' import * as rlp from 'rlp' +import { Address } from './address' // See https://github.com/celo-org/celo-blockchain/blob/master/core/types/istanbul.go export const ISTANBUL_EXTRA_VANITY_BYTES = 32 From 41ba66bad0ebea05bd6f5813e73beffa1eadbb6e Mon Sep 17 00:00:00 2001 From: Victor Graf Date: Wed, 4 Dec 2019 07:00:47 -0800 Subject: [PATCH 19/23] export IstanbulUtils --- packages/utils/src/index.ts | 1 + packages/utils/src/istanbul.ts | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 6809c109f4d..657c7c661cd 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -7,3 +7,4 @@ export * from './dappkit' export { ECIES } from './ecies' export { PhoneNumberUtils } from './phoneNumbers' export { SignatureUtils } from './signatureUtils' +export { IstanbulUtils } from './istanbul' diff --git a/packages/utils/src/istanbul.ts b/packages/utils/src/istanbul.ts index d7f52bec3d8..27e46b4ee19 100644 --- a/packages/utils/src/istanbul.ts +++ b/packages/utils/src/istanbul.ts @@ -3,8 +3,10 @@ import { toChecksumAddress } from 'ethereumjs-util' import * as rlp from 'rlp' import { Address } from './address' +// This file contains utilities that help with istanbul-specific block information. // See https://github.com/celo-org/celo-blockchain/blob/master/core/types/istanbul.go -export const ISTANBUL_EXTRA_VANITY_BYTES = 32 + +const ISTANBUL_EXTRA_VANITY_BYTES = 32 export type Bitmap = BigNumber @@ -60,3 +62,8 @@ export function bitIsSet(bitmap: Bitmap, index: number): boolean { .mod(2) .gt(0) } + +export const IstanbulUtils = { + parseBlockExtraData, + bitIsSet, +} From eec6e8fbd4f03d077f6a7f5a0c2e56f031befa37 Mon Sep 17 00:00:00 2001 From: Victor Graf Date: Thu, 5 Dec 2019 10:08:37 -0800 Subject: [PATCH 20/23] address review comments --- packages/cli/src/commands/validator/status.ts | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/packages/cli/src/commands/validator/status.ts b/packages/cli/src/commands/validator/status.ts index 3087bca5f06..65331acbeb1 100644 --- a/packages/cli/src/commands/validator/status.ts +++ b/packages/cli/src/commands/validator/status.ts @@ -15,10 +15,12 @@ export default class ValidatorStatus extends BaseCommand { static flags = { ...BaseCommand.flags, signer: Flags.address({ - description: 'address of the validator to check if elected and validating', + description: 'address of the signer to check if elected and validating', + exclusive: ['validator'], }), validator: Flags.address({ - description: 'address of the signer to check if elected and validating', + description: 'address of the validator to check if elected and validating', + exclusive: ['signer'], }), lookback: flags.integer({ description: 'how many blocks to look back for signer activity', @@ -41,27 +43,23 @@ export default class ValidatorStatus extends BaseCommand { const checker = newCheckBuilder(this, res.flags.signer) if (res.flags.validator) { const account = res.flags.validator - checker.isValidator(account).meetsValidatorBalanceRequirements(account) + checker + .isAccount(account) + .isValidator(account) + .meetsValidatorBalanceRequirements(account) } else if (res.flags.signer) { - checker.signerMeetsValidatorBalanceRequirements().signerAccountIsValidator() + checker + .isSignerOrAccount() + .signerMeetsValidatorBalanceRequirements() + .signerAccountIsValidator() } else { this.error('Either validator or signer must be specified') } await checker.runChecks() - // Assign and verify the signer. - let signer: Address - if (res.flags.signer) { - signer = res.flags.signer - if (res.flags.validator) { - const accounts = await this.kit.contracts.getAccounts() - if ((await accounts.signerToAccount(signer)) !== res.flags.validator) { - this.error( - `Signer ${signer} has never been authorized for account ${res.flags.validator}` - ) - } - } - } else { + // Get the signer from the validator account if not provided. + let signer = res.flags.signer + if (!signer) { const accounts = await this.kit.contracts.getAccounts() signer = await accounts.getValidatorSigner(res.flags.validator!) console.info(`Identified ${signer} as the authorized validator signer`) @@ -76,11 +74,11 @@ export default class ValidatorStatus extends BaseCommand { const frontrunners = await election.electValidatorSigners() if (frontrunners.some((a) => eqAddress(a, signer))) { this.error( - `Signer ${signer} is not elected for this epoch, but is currently winning in the upcoming election. Wait for the next epoch.` + `Signer ${signer} is not elected for this epoch, but would be elected if an election were to be held now. Please wait until the next epoch.` ) } else { this.error( - `Signer ${signer} is not elected for this epoch, and is not currently winning the upcoming election.` + `Signer ${signer} is not elected for this epoch, and would not be elected if an election were to be held now.` ) } } From d64020bfe7cdb358bed503d6f72df2ddcb10c3e7 Mon Sep 17 00:00:00 2001 From: Victor Graf Date: Thu, 5 Dec 2019 10:25:43 -0800 Subject: [PATCH 21/23] fix type error --- packages/cli/src/commands/validator/status.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/commands/validator/status.ts b/packages/cli/src/commands/validator/status.ts index 65331acbeb1..bcf81dc417b 100644 --- a/packages/cli/src/commands/validator/status.ts +++ b/packages/cli/src/commands/validator/status.ts @@ -58,7 +58,7 @@ export default class ValidatorStatus extends BaseCommand { await checker.runChecks() // Get the signer from the validator account if not provided. - let signer = res.flags.signer + let signer: Address = res.flags.signer || '' if (!signer) { const accounts = await this.kit.contracts.getAccounts() signer = await accounts.getValidatorSigner(res.flags.validator!) From a9220dc4237a9ea792063eb33fb14f1c7ea3cfe6 Mon Sep 17 00:00:00 2001 From: "Victor \"Nate\" Graf" Date: Thu, 5 Dec 2019 10:30:58 -0800 Subject: [PATCH 22/23] fix typo --- packages/utils/src/istanbul.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/utils/src/istanbul.ts b/packages/utils/src/istanbul.ts index 27e46b4ee19..36f2be2ae6d 100644 --- a/packages/utils/src/istanbul.ts +++ b/packages/utils/src/istanbul.ts @@ -55,7 +55,7 @@ export function parseBlockExtraData(data: string): IstanbulExtra { export function bitIsSet(bitmap: Bitmap, index: number): boolean { if (index < 0) { - throw new Error(`bit index must be greate than zero: got ${index}`) + throw new Error(`bit index must be greater than zero: got ${index}`) } return bitmap .idiv('1' + '0'.repeat(index), 2) From 80eb7025befe03d6b8aa8acca1ca10f290b2de06 Mon Sep 17 00:00:00 2001 From: Victor Graf Date: Thu, 5 Dec 2019 12:44:17 -0800 Subject: [PATCH 23/23] update docs --- packages/docs/command-line-interface/validator.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/docs/command-line-interface/validator.md b/packages/docs/command-line-interface/validator.md index f46ddfb7d88..f1d45a1e68c 100644 --- a/packages/docs/command-line-interface/validator.md +++ b/packages/docs/command-line-interface/validator.md @@ -143,9 +143,9 @@ OPTIONS --lookback=lookback [default: 100] how many blocks to look back for signer activity - --signer=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d address of the validator to check if elected and validating + --signer=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d address of the signer to check if elected and validating - --validator=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d address of the signer to check if elected and validating + --validator=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d address of the validator to check if elected and validating EXAMPLES status --validator 0x5409ED021D9299bf6814279A6A1411A7e866A631