Skip to content

Commit

Permalink
Add CLI command to update validator public key (#1626)
Browse files Browse the repository at this point in the history
  • Loading branch information
Asa Oines authored and celo-ci-bot-user committed Nov 9, 2019
1 parent f1b7e0d commit cda5aaa
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 7 deletions.
43 changes: 43 additions & 0 deletions packages/cli/src/commands/validator/publicKey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { BaseCommand } from '../../base'
import { newCheckBuilder } from '../../utils/checks'
import { displaySendTx } from '../../utils/cli'
import { Flags } from '../../utils/command'
import { getPubKeyFromAddrAndWeb3 } from '../../utils/helpers'

export default class ValidatorPublicKey extends BaseCommand {
static description = 'Manage BLS public key data for a validator'

static flags = {
...BaseCommand.flags,
from: Flags.address({ required: true, description: "Validator's address" }),
publicKey: Flags.publicKey({ required: true }),
}

static examples = [
'publickey --from 0x47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95 --publicKey 0xc52f3fab06e22a54915a8765c4f6826090cfac5e40282b43844bf1c0df83aaa632e55b67869758f2291d1aabe0ebecc7cbf4236aaa45e3e0cfbf997eda082ae19d3e1d8f49f6b0d8e9a03d80ca07b1d24cf1cc0557bdcc04f5e17a46e35d02d0d411d956dbd5d2d2464eebd7b74ae30005d223780d785d2abc5644fac7ac29fb0e302bdc80c81a5d45018b68b1045068a4b3a4861c93037685fd0d252d7405011220a66a6257562d0c26dabf64485a1d96bad27bb1c0fd6080a75b0ec9f75b50298a2a8e04b02b2688c8104fca61fb00',
]
async run() {
const res = this.parse(ValidatorPublicKey)
this.kit.defaultAccount = res.flags.from
const validators = await this.kit.contracts.getValidators()
const accounts = await this.kit.contracts.getAccounts()

await newCheckBuilder(this, res.flags.from)
.isSignerOrAccount()
.canSignValidatorTxs()
.signerAccountIsValidator()
.runChecks()

await displaySendTx(
'updatePublicKeysData',
validators.updatePublicKeysData(res.flags.publicKey as any)
)

// register encryption key on accounts contract
// TODO: Use a different key data encryption
const pubKey = await getPubKeyFromAddrAndWeb3(res.flags.from, this.web3)
// TODO fix typing
const setKeyTx = accounts.setAccountDataEncryptionKey(pubKey as any)
await displaySendTx('Set encryption key', setKeyTx)
}
}
1 change: 1 addition & 0 deletions packages/contractkit/src/wrappers/Validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export interface GroupMembership {
*/
// TODO(asa): Support authorized validators
export class ValidatorsWrapper extends BaseWrapper<Validators> {
updatePublicKeysData = proxySend(this.kit, this.contract.methods.updatePublicKeysData)
/**
* Returns the Locked Gold requirements for validators.
* @returns The Locked Gold requirements for validators.
Expand Down
22 changes: 22 additions & 0 deletions packages/docs/command-line-interface/validator.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,28 @@ EXAMPLE

_See code: [packages/cli/src/commands/validator/list.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/validator/list.ts)_

### PublicKey

Manage BLS public key data for a validator

```
USAGE
$ celocli validator:publicKey
OPTIONS
--from=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d (required) Validator's address
--publicKey=0x (required) Public Key
EXAMPLE
publickey --from 0x47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95 --publicKey
0xc52f3fab06e22a54915a8765c4f6826090cfac5e40282b43844bf1c0df83aaa632e55b67869758f2291d1aabe0ebecc7cbf4236aaa45e3e0cfbf
997eda082ae19d3e1d8f49f6b0d8e9a03d80ca07b1d24cf1cc0557bdcc04f5e17a46e35d02d0d411d956dbd5d2d2464eebd7b74ae30005d223780d
785d2abc5644fac7ac29fb0e302bdc80c81a5d45018b68b1045068a4b3a4861c93037685fd0d252d7405011220a66a6257562d0c26dabf64485a1d
96bad27bb1c0fd6080a75b0ec9f75b50298a2a8e04b02b2688c8104fca61fb00
```

_See code: [packages/cli/src/commands/validator/publicKey.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/validator/publicKey.ts)_

### Register

Register a new Validator
Expand Down
53 changes: 46 additions & 7 deletions packages/protocol/contracts/governance/Validators.sol
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ contract Validators is
event ValidatorDeregistered(address indexed validator);
event ValidatorAffiliated(address indexed validator, address indexed group);
event ValidatorDeaffiliated(address indexed validator, address indexed group);
event ValidatorPublicKeysDataUpdated(address indexed validator, bytes publicKeysData);
event ValidatorGroupRegistered(address indexed group, uint256 commission);
event ValidatorGroupDeregistered(address indexed group);
event ValidatorGroupMemberAdded(address indexed group, address indexed validator);
Expand Down Expand Up @@ -283,18 +284,12 @@ contract Validators is
* @dev Fails if the account does not have sufficient Locked Gold.
*/
function registerValidator(bytes calldata publicKeysData) external nonReentrant returns (bool) {
require(
// secp256k1 public key + BLS public key + BLS proof of possession
publicKeysData.length == (64 + 48 + 96)
);
// Use the proof of possession bytes
require(checkProofOfPossession(msg.sender, publicKeysData.slice(64, 48 + 96)));

address account = getAccounts().activeValidationSignerToAccount(msg.sender);
require(!isValidator(account) && !isValidatorGroup(account));
uint256 lockedGoldBalance = getLockedGold().getAccountTotalLockedGold(account);
require(lockedGoldBalance >= validatorLockedGoldRequirements.value);
Validator storage validator = validators[account];
_updatePublicKeysData(validator, publicKeysData);
validator.publicKeysData = publicKeysData;
registeredValidators.push(account);
updateMembershipHistory(account, address(0));
Expand Down Expand Up @@ -471,6 +466,50 @@ contract Validators is
return true;
}

/**
* @notice Updates a validator's public keys data.
* @param publicKeysData Comprised of three tightly-packed elements:
* - publicKey - The public key that the validator is using for consensus, should match
* msg.sender. 64 bytes.
* - blsPublicKey - The BLS public key that the validator is using for consensus, should pass
* proof of possession. 48 bytes.
* - blsPoP - The BLS public key proof of possession. 96 bytes.
* @return True upon success.
*/
function updatePublicKeysData(bytes calldata publicKeysData) external returns (bool) {
address account = getAccounts().activeValidationSignerToAccount(msg.sender);
require(isValidator(account));
Validator storage validator = validators[account];
_updatePublicKeysData(validator, publicKeysData);
emit ValidatorPublicKeysDataUpdated(account, publicKeysData);
return true;
}

/**
* @notice Updates a validator's public keys data.
* @param validator The validator whose public keys data should be updated.
* @param publicKeysData Comprised of three tightly-packed elements:
* - publicKey - The public key that the validator is using for consensus, should match
* msg.sender. 64 bytes.
* - blsPublicKey - The BLS public key that the validator is using for consensus, should pass
* proof of possession. 48 bytes.
* - blsPoP - The BLS public key proof of possession. 96 bytes.
* @return True upon success.
*/
function _updatePublicKeysData(Validator storage validator, bytes memory publicKeysData)
private
returns (bool)
{
require(
// secp256k1 public key + BLS public key + BLS proof of possession
publicKeysData.length == (64 + 48 + 96)
);
// Use the proof of possession bytes
require(checkProofOfPossession(msg.sender, publicKeysData.slice(64, 48 + 96)));
validator.publicKeysData = publicKeysData;
return true;
}

/**
* @notice Registers a validator group with no member validators.
* @param commission Fixidity representation of the commission this group receives on epoch
Expand Down
52 changes: 52 additions & 0 deletions packages/protocol/test/governance/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1149,6 +1149,58 @@ contract('Validators', (accounts: string[]) => {
})
})

describe('#updatePublicKeysData()', () => {
const newPublicKey = web3.utils.randomHex(64).slice(2)
const newBlsPublicKey = web3.utils.randomHex(48).slice(2)
const newBlsPoP = web3.utils.randomHex(96).slice(2)
const newPublicKeysData = '0x' + newPublicKey + newBlsPublicKey + newBlsPoP
describe('when called by a registered validator', () => {
const validator = accounts[0]
beforeEach(async () => {
await registerValidator(validator)
})

describe('when the public keys data is the right length', () => {
let resp: any
beforeEach(async () => {
// @ts-ignore Broken typechain typing for bytes
resp = await validators.updatePublicKeysData(newPublicKeysData)
})

it('should set the validator public keys data', async () => {
const parsedValidator = parseValidatorParams(await validators.getValidator(validator))
assert.equal(parsedValidator.publicKeysData, newPublicKeysData)
})

it('should emit the ValidatorPublicKeysDataUpdated event', async () => {
assert.equal(resp.logs.length, 1)
const log = resp.logs[0]
assertContainSubset(log, {
event: 'ValidatorPublicKeysDataUpdated',
args: {
validator,
publicKeysData: newPublicKeysData,
},
})
})
})

describe('when the public keys data is too long', () => {
it('should revert', async () => {
// @ts-ignore Broken typechain typing for bytes
await assertRevert(validators.updatePublicKeysData(newPublicKeysData + '00'))
})
})

describe('when the public keys data is too short', () => {
it('should revert', async () => {
// @ts-ignore Broken typechain typing for bytes
await assertRevert(validators.updatePublicKeysData(newPublicKeysData.slice(0, -2)))
})
})
})
})

describe('#registerValidatorGroup', () => {
const group = accounts[0]
let resp: any
Expand Down

0 comments on commit cda5aaa

Please sign in to comment.