Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement proof-of-stake changes #1177

Merged
merged 59 commits into from
Oct 16, 2019
Merged

Implement proof-of-stake changes #1177

merged 59 commits into from
Oct 16, 2019

Conversation

asaj
Copy link
Contributor

@asaj asaj commented Oct 2, 2019

Description

This PR implements changes to the proof of stake design.

Most notably, it:

  • Removes notice periods, and their effect on voting weight
  • Introduces the concept of voting and non-voting locked gold
  • Allows users to vote for multiple validator groups
  • Introduces the concept of pending and active votes in validator elections, so that epoch rewards can be paid out to voters more efficiently
  • Introduces a cap on the number of votes a validator group can receive
  • Factors out validator elections into a separate contract

Tested

Unit tests, manual tests of the CLI with ganache (with proof-of-possession disabled):

celocli lockedgold:register --from 0x5409ED021D9299bf6814279A6A1411A7e866A631
celocli lockedgold:register --from 0x6Ecbe1DB9EF729CBe972C83Fb886247691Fb6beb
celocli lockedgold:lock --from 0x5409ED021D9299bf6814279A6A1411A7e866A631 --goldAmount 1000000000000000000
celocli lockedgold:lock --from 0x6Ecbe1DB9EF729CBe972C83Fb886247691Fb6beb --goldAmount 1000000000000000000
celocli validatorgroup:register --from 0x5409ED021D9299bf6814279A6A1411A7e866A631 --name testGroup --url celo.org --commission 0.1
celocli validator:register --from 0x6Ecbe1DB9EF729CBe972C83Fb886247691Fb6beb --name testValidator --url celo.org --publicKey 0xc52f3fab06e22a54915a8765c4f6826090cfac5e40282b43844bf1c0df83aaa632e55b67869758f2291d1aabe0ebecc7cbf4236aaa45e3e0cfbf997eda082ae19d3e1d8f49f6b0d8e9a03d80ca07b1d24cf1cc0557bdcc04f5e17a46e35d02d0d411d956dbd5d2d2464eebd7b74ae30005d223780d785d2abc5644fac7ac29fb0e302bdc80c81a5d45018b68b1045068a4b3a4861c93037685fd0d252d7405011220a66a6257562d0c26dabf64485a1d96bad27bb1c0fd6080a75b0ec9f75b50298a2a8e04b02b2688c8104fca61fb00
celocli validator:affiliation --from 0x6Ecbe1DB9EF729CBe972C83Fb886247691Fb6beb --set 0x5409ED021D9299bf6814279A6A1411A7e866A631
celocli validatorgroup:member --accept 0x6Ecbe1DB9EF729CBe972C83Fb886247691Fb6beb --from 0x5409ED021D9299bf6814279A6A1411A7e866A631
celocli election:vote --from 0x5409ED021D9299bf6814279A6A1411A7e866A631 --for 0x5409ED021D9299bf6814279A6A1411A7e866A631 --value 9

Other changes

  • CLI
  • ContractKit
  • Typos
  • UsingRegistry

Related issues

Backwards compatibility

Not backwards compatible

@asaj asaj requested a review from m-chrzan as a code owner October 2, 2019 00:50
@asaj asaj requested a review from mcortesi as a code owner October 3, 2019 19:29
Copy link
Contributor

@m-chrzan m-chrzan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got through Governance.sol and LockedGold.sol.

uint128 value;
uint128 index;
struct Authorizations {
// The address that is authorized to vote on behalf of the account.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this always set to something? I imagine if it's 0, the account itself is authorized to vote. If so, might be worth adding info on this edge case to the comment. Ditto below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, this behavior is unclear, made it more detailed in the comments.

}

struct Balances {
// This contract does not store an account's locked gold that is being used in electing
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like a weird location to put this comment - comments above struct fields usually describe what a field represents. This one I guess is more of a "what this field does not represent" comment?

Not sure what a better place would be. Maybe just prefixing it with "NOTE:" would clear up the confusion for me personally?

Also, describing what exactly nonvoting's value represents might be helpful.

* @param _account The address of the account.
* @param noticePeriod The notice period of the Locked Gold commitment.
* @return The value and index of the specified Locked Gold commitment.
* @notice Withdraws a gold that has been unlocked after the unlocking period has passed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: unnecessary "a".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

)
private
public
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be external?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure can

* @param r Output value r of the ECDSA signature.
* @param s Output value s of the ECDSA signature.
* @dev Fails if the address is already authorized or is an account.
* @dev v, r, s constitute `authorize`'s signature on `msg.sender`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no variable called authorize. Presume this should be current?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Danger of search and replace

Copy link
Contributor

@m-chrzan m-chrzan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got through contracts and migrations, started looking at unit tests.

// minimum amount of Locked Gold required in order to earn epoch rewards. Furthermore, the
// account will not be able to unlock Gold if it would cause the account to fall below
// these values.
// If an account has deregistered a validator or validator group and is still subject to the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this doesn't sound like the best contract for a validator. One could be afraid of a situation in which, after registering, DeregistrationLockup goes up and BalanceRequirements is increased above one's balance. Sounds like with the current setup, a validator's locked funds could be made unexpectedly unavailable to them?

Maybe this is something we just have to live with, rely on the governance process to not have these move unfairly. The other option would be to store historical values per validator and honor the contract they made when registering.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, that's exactly correct. If I, as a validator, lock up more than the required gold, and then the requirement goes up, that extra gold which was previously relatively accessible to me becomes locked up until I deregister and wait out the lockup period.

How comfortable validators are with this I suppose is dependent on their faith in the governance process.

The other option would be to store historical values per validator and honor the contract they made when registering.

This would work, but IMO the ability to adjust the requirements/lockups is a feature and not a bug.

}

// After deregistering a validator or validator group, the account will remain subject to the
// current balance requirements for this long (in seconds).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to the above, this comment could be read as "whatever the current requirement is at time of deregistration, that is the requirement that will be enforced when trying to unlock", which I believe is not the case, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, Validators are subject to whatever the current requirements are. How would you suggest phrasing this?

packages/protocol/contracts/governance/Validators.sol Outdated Show resolved Hide resolved
packages/protocol/contracts/governance/Validators.sol Outdated Show resolved Hide resolved
packages/protocol/contracts/governance/Validators.sol Outdated Show resolved Hide resolved
resp = await election.markGroupIneligible(group)
})

describe('when the group has votes', () => {})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty describe. Did you mean to put the its below inside of it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, not sure where this came from

election.revokeActive(group, value + 1, NULL_ADDRESS, NULL_ADDRESS, index)
)
})
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. A whole scenario where a voters cast votes, (mock?) rewards are distributed, more voters cast votes, and then ensuring everyone ends up with the correct balance would be nice.

packages/protocol/test/governance/election.ts Outdated Show resolved Hide resolved
packages/protocol/test/governance/election.ts Outdated Show resolved Hide resolved

it('should elect only n members from that group', async () => {
assertSameAddresses(await election.electValidators(), [
validator7,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean to have these out of order?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe they're ordered in the order that they would be elected by d'hondt

Copy link
Contributor

@mcortesi mcortesi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

final comments. Maybe only one to review, still approving it so can be merged after solving the comments

packages/cli/src/commands/lockedgold/lock.ts Outdated Show resolved Hide resolved
*/

async markGroupEligible(validatorGroup: Address): Promise<CeloTransactionObject<boolean>> {
if (this.kit.defaultAccount == null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no. Maybe add a TODO so it's addresses on another PR


async markGroupEligible(validatorGroup: Address): Promise<CeloTransactionObject<boolean>> {
if (this.kit.defaultAccount == null) {
throw new Error(`missing from at new ValdidatorUtils()`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same... add a todo?

packages/contractkit/src/wrappers/LockedGold.ts Outdated Show resolved Hide resolved
function isValidating(address validator) external view returns (bool) {
IValidators validators = IValidators(registry.getAddressFor(VALIDATORS_REGISTRY_ID));
return validators.isValidating(validator);
function _decrementNonvotingAccountBalance(address account, uint256 value) private {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I though solidity linter expects all internal methods to go after public/external ones

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the new version of solhint has this check disabled by default. I'll reorder these in a follow-up PR to avoid merge conflicts in asaj/pos-2

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can disable that if we want. Not sure what we prefer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be good to enable, the function ordering in my PR is a bit nonsensical and hard to read. Only keeping it this way to minimize merge conflicts with asaj/pos-2

packages/protocol/contracts/governance/LockedGold.sol Outdated Show resolved Hide resolved
packages/protocol/contracts/governance/Election.sol Outdated Show resolved Hide resolved
*/
function getActiveVotesUnitsDelta(address group, uint256 value) private view returns (uint256) {
// Preserve unitsDelta * total = value * totalUnits
return value.mul(votes.active.forGroup[group].totalUnits.add(1)).div(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure to follow the math here.
So the relation is:

accountUnits / totalUnits = accountValue / totalValue

where value = votes

The question is: how much units would i gain if a add X value/votes

that means i need to solve this equation:

(newUnits) / (totalUnits + newUnits) = (addedValue) / (totalValue + addedValue)

where we need to get newUnits and the rest is given.

which doesn't look similar to what we are doing here... plus we are adding 1 to both unit and totalValue. And is not like we are (a) creating only one unit. (b) adding only 1 to totalValue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The equation you wrote simplifies to totalUnits * addedValue = newUnits * totalValue, which, solving for newUnits, gives us totalUnits * addedValue / totalValue, i.e. the equation in the code.

If we don't add 1 to those values no one can ever place a vote, since the numerator and denominator will both be zero

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, thanks. I see the math now. The +1 were confusing.
I understand why you add them, but wouldn't be easier (and mathematically accurate) to first check if totalUnits == 0; and if make delta = value

So that we end with:

totalUnits = value
totalValue = value
delta = value
userUnits = value

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is much better, thanks!

packages/protocol/contracts/governance/Election.sol Outdated Show resolved Hide resolved
@mcortesi mcortesi assigned asaj and unassigned mcortesi Oct 15, 2019
@asaj asaj added the automerge Have PR merge automatically when checks pass label Oct 16, 2019
@celo-ci-bot-user celo-ci-bot-user merged commit 17ff683 into master Oct 16, 2019
aaronmgdr added a commit that referenced this pull request Oct 23, 2019
* master: (37 commits)
  [Wallet] Add support for social wallet (Safeguards) import  (#1414)
  [Wallet] Implement new backup flows including social backup (#1399)
  Use validator set precompiles in Attestations (#1248)
  E2E Attestations test + various e2e improvements (#1417)
  Update Footer (#1331)
  [Wallet] New camera permission flow (#1398)
  [Wallet] Enable push notifications on iOS (#1389)
  [Wallet] Add Celo Lite toggle (UI only, zeroSync on/off in other PR) (#1369)
  Document npm inter-repo dependencies instructions (#1370)
  [wallet]Add more documentation on ZeroSync mode (#1367)
  [wallet]Add documentation for jndcrash (#1364)
  Point end-to-end tests back to master (#1372)
  Alfajores changes & comment on unlocking accounts (#1297)
  Reconfigure terraform local configuration during init to allow multiple envs (#773)
  Implement proof-of-stake changes (#1177)
  [Celotool] Update blockchain-api deploy script to automatically update faucet address (#1347)
  Allow most recently reporting oracle to report again (#1288)
  [wallet]Add documentation for ZeroSync mode (#1361)
  Fix Metadata registration during contract deploy (#1346)
  [Wallet] Enable firebase on iOS (#1344)
  ...
aaronmgdr added a commit that referenced this pull request Oct 23, 2019
* master: (128 commits)
  [Wallet] Add support for social wallet (Safeguards) import  (#1414)
  [Wallet] Implement new backup flows including social backup (#1399)
  Use validator set precompiles in Attestations (#1248)
  E2E Attestations test + various e2e improvements (#1417)
  Update Footer (#1331)
  [Wallet] New camera permission flow (#1398)
  [Wallet] Enable push notifications on iOS (#1389)
  [Wallet] Add Celo Lite toggle (UI only, zeroSync on/off in other PR) (#1369)
  Document npm inter-repo dependencies instructions (#1370)
  [wallet]Add more documentation on ZeroSync mode (#1367)
  [wallet]Add documentation for jndcrash (#1364)
  Point end-to-end tests back to master (#1372)
  Alfajores changes & comment on unlocking accounts (#1297)
  Reconfigure terraform local configuration during init to allow multiple envs (#773)
  Implement proof-of-stake changes (#1177)
  [Celotool] Update blockchain-api deploy script to automatically update faucet address (#1347)
  Allow most recently reporting oracle to report again (#1288)
  [wallet]Add documentation for ZeroSync mode (#1361)
  Fix Metadata registration during contract deploy (#1346)
  [Wallet] Enable firebase on iOS (#1344)
  ...
aaronmgdr added a commit that referenced this pull request Oct 23, 2019
* master: (37 commits)
  [Wallet] Add support for social wallet (Safeguards) import  (#1414)
  [Wallet] Implement new backup flows including social backup (#1399)
  Use validator set precompiles in Attestations (#1248)
  E2E Attestations test + various e2e improvements (#1417)
  Update Footer (#1331)
  [Wallet] New camera permission flow (#1398)
  [Wallet] Enable push notifications on iOS (#1389)
  [Wallet] Add Celo Lite toggle (UI only, zeroSync on/off in other PR) (#1369)
  Document npm inter-repo dependencies instructions (#1370)
  [wallet]Add more documentation on ZeroSync mode (#1367)
  [wallet]Add documentation for jndcrash (#1364)
  Point end-to-end tests back to master (#1372)
  Alfajores changes & comment on unlocking accounts (#1297)
  Reconfigure terraform local configuration during init to allow multiple envs (#773)
  Implement proof-of-stake changes (#1177)
  [Celotool] Update blockchain-api deploy script to automatically update faucet address (#1347)
  Allow most recently reporting oracle to report again (#1288)
  [wallet]Add documentation for ZeroSync mode (#1361)
  Fix Metadata registration during contract deploy (#1346)
  [Wallet] Enable firebase on iOS (#1344)
  ...
cmcewen added a commit that referenced this pull request Oct 25, 2019
@mcortesi mcortesi deleted the asaj/pos branch December 4, 2019 16:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
automerge Have PR merge automatically when checks pass
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Users SBAT withdraw locked gold in latest proof-of-stake design
5 participants