From 96b69aaacbc05f043fe184f22dc97ee3f99677da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kel=C3=A4?= Date: Wed, 22 Jan 2020 11:44:09 +0200 Subject: [PATCH 1/3] added checks for validators and groups --- .../src/commands/validator/register.test.ts | 25 +++++++++++++++++++ .../cli/src/commands/validator/register.ts | 2 ++ .../src/commands/validatorgroup/register.ts | 2 ++ packages/cli/src/utils/checks.ts | 12 +++++++++ 4 files changed, 41 insertions(+) diff --git a/packages/cli/src/commands/validator/register.test.ts b/packages/cli/src/commands/validator/register.test.ts index b1c0a92c9d9..fe0cea5d5f9 100644 --- a/packages/cli/src/commands/validator/register.test.ts +++ b/packages/cli/src/commands/validator/register.test.ts @@ -44,4 +44,29 @@ testWithGanache('validator:register', (web3: Web3) => { 'cdb77255037eb68897cd487fdd85388cbda448f617f874449d4b11588b0b7ad8ddc20d9bb450b513bb35664ea3923900', ]) }) + + test('fails if validator already registered', async () => { + await ValidatorRegister.run([ + '--from', + account, + '--ecdsaKey', + ecdsaPublicKey, + '--blsKey', + '4fa3f67fc913878b068d1fa1cdddc54913d3bf988dbe5a36a20fa888f20d4894c408a6773f3d7bde11154f2a3076b700d345a42fd25a0e5e83f4db5586ac7979ac2053cd95d8f2efd3e959571ceccaa743e02cf4be3f5d7aaddb0b06fc9aff00', + '--blsSignature', + 'cdb77255037eb68897cd487fdd85388cbda448f617f874449d4b11588b0b7ad8ddc20d9bb450b513bb35664ea3923900', + ]) + await expect( + ValidatorRegister.run([ + '--from', + account, + '--ecdsaKey', + ecdsaPublicKey, + '--blsKey', + '4fa3f67fc913878b068d1fa1cdddc54913d3bf988dbe5a36a20fa888f20d4894c408a6773f3d7bde11154f2a3076b700d345a42fd25a0e5e83f4db5586ac7979ac2053cd95d8f2efd3e959571ceccaa743e02cf4be3f5d7aaddb0b06fc9aff00', + '--blsSignature', + 'cdb77255037eb68897cd487fdd85388cbda448f617f874449d4b11588b0b7ad8ddc20d9bb450b513bb35664ea3923900', + ]) + ).rejects.toThrow("Some checks didn't pass!") + }) }) diff --git a/packages/cli/src/commands/validator/register.ts b/packages/cli/src/commands/validator/register.ts index 33303e018a3..b5f644d1f17 100644 --- a/packages/cli/src/commands/validator/register.ts +++ b/packages/cli/src/commands/validator/register.ts @@ -29,6 +29,8 @@ export default class ValidatorRegister extends BaseCommand { await newCheckBuilder(this, res.flags.from) .isSignerOrAccount() .canSignValidatorTxs() + .isNotValidator() + .isNotValidatorGroup() .signerMeetsValidatorBalanceRequirements() .runChecks() diff --git a/packages/cli/src/commands/validatorgroup/register.ts b/packages/cli/src/commands/validatorgroup/register.ts index ef3366b71db..9eefe59bc94 100644 --- a/packages/cli/src/commands/validatorgroup/register.ts +++ b/packages/cli/src/commands/validatorgroup/register.ts @@ -31,6 +31,8 @@ export default class ValidatorGroupRegister extends BaseCommand { .addCheck('Commission is in range [0,1]', () => commission.gte(0) && commission.lte(1)) .isSignerOrAccount() .canSignValidatorTxs() + .isNotValidator() + .isNotValidatorGroup() .signerMeetsValidatorGroupBalanceRequirements() .runChecks() diff --git a/packages/cli/src/utils/checks.ts b/packages/cli/src/utils/checks.ts index 53d335a1979..bff8e08e8da 100644 --- a/packages/cli/src/utils/checks.ts +++ b/packages/cli/src/utils/checks.ts @@ -174,6 +174,18 @@ class CheckBuilder { this.withValidators((v) => v.isValidatorGroup(account)) ) + isNotValidator = () => + this.addCheck( + `${this.signer!} is not a registered Validator`, + this.withValidators((v, _signer, account) => negate(v.isValidator(account))) + ) + + isNotValidatorGroup = () => + this.addCheck( + `${this.signer!} is not a registered ValidatorGroup`, + this.withValidators((v, _signer, account) => negate(v.isValidatorGroup(account))) + ) + signerMeetsValidatorBalanceRequirements = () => this.addCheck( `Signer's account has enough locked gold for registration`, From 319f2860a35beb2f70a538986e477e9a12e1e280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kel=C3=A4?= Date: Wed, 22 Jan 2020 16:30:30 +0200 Subject: [PATCH 2/3] improved check for lockedgold:unlock --- .../cli/src/commands/lockedgold/unlock.ts | 2 +- packages/cli/src/utils/checks.ts | 23 ++++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/commands/lockedgold/unlock.ts b/packages/cli/src/commands/lockedgold/unlock.ts index 71c814a9b0f..9eded2455ec 100644 --- a/packages/cli/src/commands/lockedgold/unlock.ts +++ b/packages/cli/src/commands/lockedgold/unlock.ts @@ -28,7 +28,7 @@ export default class Unlock extends BaseCommand { await newCheckBuilder(this, res.flags.from) .isAccount(res.flags.from) - .hasEnoughNonvotingLockedGold(value) + .hasEnoughLockedGoldToUnlock(value) .runChecks() await displaySendTx('unlock', lockedgold.unlock(value)) diff --git a/packages/cli/src/utils/checks.ts b/packages/cli/src/utils/checks.ts index bff8e08e8da..7e910a46a97 100644 --- a/packages/cli/src/utils/checks.ts +++ b/packages/cli/src/utils/checks.ts @@ -58,16 +58,21 @@ class CheckBuilder { } withLockedGold( - f: (lockedGold: LockedGoldWrapper, signer: Address, account: Address) => A + f: ( + lockedGold: LockedGoldWrapper, + signer: Address, + account: Address, + validators: ValidatorsWrapper + ) => A ): () => Promise> { return async () => { const lockedGold = await this.kit.contracts.getLockedGold() const validators = await this.kit.contracts.getValidators() if (this.signer) { const account = await validators.signerToAccount(this.signer) - return f(lockedGold, this.signer, account) as Resolve + return f(lockedGold, this.signer, account, validators) as Resolve } else { - return f(lockedGold, '', '') as Resolve + return f(lockedGold, '', '', validators) as Resolve } } } @@ -281,6 +286,18 @@ class CheckBuilder { ) } + hasEnoughLockedGoldToUnlock = (value: BigNumber) => { + const valueInEth = this.kit.web3.utils.fromWei(value.toFixed(), 'ether') + return this.addCheck( + `Account has at least ${valueInEth} non-voting Locked Gold over requirement`, + this.withLockedGold(async (l, _signer, account, v) => + value + .plus(await v.getAccountLockedGoldRequirement(account)) + .isLessThanOrEqualTo(await l.getAccountNonvotingLockedGold(account)) + ) + ) + } + async runChecks() { console.log(`Running Checks:`) let allPassed = true From 807e7fc54710e4535dc4ef2a71310524db4f37e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kel=C3=A4?= Date: Wed, 22 Jan 2020 17:00:57 +0200 Subject: [PATCH 3/3] more checks for validator:deregister --- .../cli/src/commands/validator/deregister.ts | 2 ++ packages/cli/src/utils/checks.ts | 24 +++++++++++++++++++ .../contractkit/src/wrappers/Validators.ts | 18 ++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/packages/cli/src/commands/validator/deregister.ts b/packages/cli/src/commands/validator/deregister.ts index 133d3ea2380..a9becaf92a8 100644 --- a/packages/cli/src/commands/validator/deregister.ts +++ b/packages/cli/src/commands/validator/deregister.ts @@ -24,6 +24,8 @@ export default class ValidatorDeregister extends BaseCommand { .isSignerOrAccount() .canSignValidatorTxs() .signerAccountIsValidator() + .isNotValidatorGroupMember() + .validatorDeregisterDurationPassed() .runChecks() const validator = await validators.signerToAccount(res.flags.from) diff --git a/packages/cli/src/utils/checks.ts b/packages/cli/src/utils/checks.ts index 7e910a46a97..38607b1b62a 100644 --- a/packages/cli/src/utils/checks.ts +++ b/packages/cli/src/utils/checks.ts @@ -298,6 +298,30 @@ class CheckBuilder { ) } + isNotValidatorGroupMember = () => { + return this.addCheck( + `Account isn't a member of a validator group`, + this.withValidators(async (v, _signer, account) => { + const { affiliation } = await v.getValidator(account) + const { members } = await v.getValidatorGroup(affiliation!) + return !members.includes(account) + }) + ) + } + + validatorDeregisterDurationPassed = () => { + return this.addCheck( + `Enough time has passed since the account was removed from a validator group`, + this.withValidators(async (v, _signer, account) => { + const { lastRemovedFromGroupTimestamp } = await v.getValidatorMembershipHistoryExtraData( + account + ) + const { duration } = await v.getValidatorLockedGoldRequirements() + return duration.toNumber() + lastRemovedFromGroupTimestamp < Date.now() + }) + ) + } + async runChecks() { console.log(`Running Checks:`) let allPassed = true diff --git a/packages/contractkit/src/wrappers/Validators.ts b/packages/contractkit/src/wrappers/Validators.ts index a7d7dafde7b..8ece4123c7d 100644 --- a/packages/contractkit/src/wrappers/Validators.ts +++ b/packages/contractkit/src/wrappers/Validators.ts @@ -61,6 +61,11 @@ export interface GroupMembership { group: Address } +export interface MembershipHistoryExtraData { + lastRemovedFromGroupTimestamp: number + tail: number +} + /** * Contract for voting for validators and managing validator groups. */ @@ -265,6 +270,19 @@ export class ValidatorsWrapper extends BaseWrapper { zip((epoch, group): GroupMembership => ({ epoch: valueToInt(epoch), group }), res[0], res[1]) ) + /** + * Returns extra data from the Validator's group membership history + * @param validator The validator whose membership history to return. + * @return The group membership history of a validator. + */ + getValidatorMembershipHistoryExtraData: ( + validator: Address + ) => Promise = proxyCall( + this.contract.methods.getMembershipHistory, + undefined, + (res) => ({ lastRemovedFromGroupTimestamp: valueToInt(res[2]), tail: valueToInt(res[3]) }) + ) + /** Get the size (amount of members) of a ValidatorGroup */ getValidatorGroupSize: (group: Address) => Promise = proxyCall( this.contract.methods.getGroupNumMembers,