-
Notifications
You must be signed in to change notification settings - Fork 14
Add delegation rewards and commission #114
Changes from all commits
df6dc3f
e8ede85
8532966
764ed91
bc0cc6e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -258,7 +258,8 @@ state.accounts[sender].nonce += 1 | |
|
||
state.accounts[sender].balance -= totalCost(tx.amount, bytesPaid) | ||
state.accounts[tx.to].balance += tx.amount | ||
state.activeValidatorSet[block.header.proposerAddress].pendingRewards += tipCost(bytesPaid) | ||
|
||
state.activeValidatorSet.proposerBlockReward += tipCost(bytesPaid) | ||
``` | ||
|
||
#### SignedTransactionDataPayForMessage | ||
|
@@ -284,7 +285,7 @@ Apply the following to the state: | |
state.accounts[sender].nonce += 1 | ||
state.accounts[sender].balance -= totalCost(tx.amount, bytesPaid) | ||
|
||
state.activeValidatorSet[block.header.proposerAddress].pendingRewards += tipCost(bytesPaid) | ||
state.activeValidatorSet.proposerBlockReward += tipCost(bytesPaid) | ||
``` | ||
|
||
#### SignedTransactionDataCreateValidator | ||
|
@@ -298,7 +299,7 @@ The following checks must be `true`: | |
1. `tx.type` == [`TransactionType.CreateValidator`](./data_structures.md#signedtransactiondata). | ||
1. `tx.fee.baseRateMax` >= `block.header.feeHeader.baseRate`. | ||
1. `tx.fee.tipRateMax` >= `block.header.feeHeader.tipRate`. | ||
1. `totalCost(tx.amount, bytesPaid)` <= `state.accounts[sender].balance`. | ||
1. `totalCost(0, bytesPaid)` <= `state.accounts[sender].balance`. | ||
1. `tx.nonce` == `state.accounts[sender].nonce + 1`. | ||
1. `tx.commissionRate` <!--TODO check some bounds here--> | ||
1. `state.accounts[sender].status` == `AccountStatus.None`. | ||
|
@@ -307,14 +308,13 @@ Apply the following to the state: | |
|
||
```py | ||
state.accounts[sender].nonce += 1 | ||
state.accounts[sender].balance -= totalCost(tx.amount, bytesPaid) | ||
state.accounts[sender].balance -= totalCost(0, bytesPaid) | ||
state.accounts[sender].status = AccountStatus.ValidatorQueued | ||
|
||
validator = new Validator | ||
validator.stakedBalance = tx.amount | ||
validator.commissionRate = tx.commissionRate | ||
validator.delegatedCount = 0 | ||
validator.votingPower = tx.amount | ||
validator.votingPower = 0 | ||
validator.pendingRewards = 0 | ||
validator.latestEntry = PeriodEntry(0) | ||
validator.unbondingHeight = 0 | ||
|
@@ -324,7 +324,7 @@ validatorQueueInsert(validator) | |
|
||
state.inactiveValidatorSet[sender] = validator | ||
|
||
state.activeValidatorSet[block.header.proposerAddress].pendingRewards += tipCost(bytesPaid) | ||
state.activeValidatorSet.proposerBlockReward += tipCost(bytesPaid) | ||
``` | ||
|
||
#### SignedTransactionDataBeginUnbondingValidator | ||
|
@@ -362,8 +362,9 @@ validatorQueueRemove(validator, sender) | |
|
||
state.inactiveValidatorSet[sender] = validator | ||
|
||
state.activeValidatorSet.votingPower -= validator.votingPower | ||
state.activeValidatorSet[block.header.proposerAddress].pendingRewards += tipCost(bytesPaid) | ||
state.activeValidatorSet.activeVotingPower -= validator.votingPower | ||
|
||
state.activeValidatorSet.proposerBlockReward += tipCost(bytesPaid) | ||
``` | ||
|
||
#### SignedTransactionDataUnbondValidator | ||
|
@@ -388,20 +389,18 @@ Apply the following to the state: | |
validator = state.inactiveValidatorSet[sender] | ||
|
||
state.accounts[sender].nonce += 1 | ||
state.accounts[sender].balance += validator.stakedBalance | ||
state.accounts[sender].balance -= totalCost(0, bytesPaid) | ||
state.accounts[sender].status = AccountStatus.ValidatorUnbonded | ||
|
||
validator.votingPower -= validator.stakedBalance | ||
validator.stakedBalance = 0 | ||
state.accounts[sender].balance += validator.commissionRewards | ||
|
||
state.inactiveValidatorSet[sender] = validator | ||
|
||
if validator.delegatedCount == 0 | ||
state.accounts[sender].status = AccountStatus.None | ||
delete state.inactiveValidatorSet[sender] | ||
|
||
state.activeValidatorSet[block.header.proposerAddress].pendingRewards += tipCost(bytesPaid) | ||
state.activeValidatorSet.proposerBlockReward += tipCost(bytesPaid) | ||
``` | ||
|
||
#### SignedTransactionDataCreateDelegation | ||
|
@@ -455,9 +454,9 @@ if state.accounts[tx.to].status == AccountStatus.ValidatorQueued | |
state.inactiveValidatorSet[tx.to] = validator | ||
else if state.accounts[tx.to].status == AccountStatus.ValidatorBonded | ||
state.activeValidatorSet[tx.to] = validator | ||
state.activeValidatorSet.votingPower += tx.amount | ||
state.activeValidatorSet.activeVotingPower += tx.amount | ||
|
||
state.activeValidatorSet[block.header.proposerAddress].pendingRewards += tipCost(bytesPaid) | ||
state.activeValidatorSet.proposerBlockReward += tipCost(bytesPaid) | ||
``` | ||
|
||
#### SignedTransactionDataBeginUnbondingDelegation | ||
|
@@ -498,7 +497,7 @@ delegation.unbondingHeight = block.height + 1 | |
validator.latestEntry += validator.pendingRewards // validator.votingPower | ||
validator.pendingRewards = 0 | ||
validator.delegatedCount -= 1 | ||
validator.votingPower -= delegation.votingPower | ||
validator.votingPower -= delegation.stakedBalance | ||
|
||
# Update the validator in the linked list by first removing then inserting | ||
# Only do this if the validator is actually in the queue (i.e. bonded or queued) | ||
|
@@ -515,9 +514,9 @@ if state.accounts[delegation.validator].status == AccountStatus.ValidatorQueued | |
state.inactiveValidatorSet[delegation.validator] = validator | ||
else if state.accounts[delegation.validator].status == AccountStatus.ValidatorBonded | ||
state.activeValidatorSet[delegation.validator] = validator | ||
state.activeValidatorSet.votingPower -= delegation.votingPower | ||
state.activeValidatorSet.activeVotingPower -= delegation.stakedBalance | ||
|
||
state.activeValidatorSet[block.header.proposerAddress].pendingRewards += tipCost(bytesPaid) | ||
state.activeValidatorSet.proposerBlockReward += tipCost(bytesPaid) | ||
``` | ||
|
||
#### SignedTransactionDataUnbondDelegation | ||
|
@@ -542,11 +541,13 @@ Apply the following to the state: | |
delegation = state.accounts[sender].delegationInfo | ||
|
||
state.accounts[sender].nonce += 1 | ||
state.accounts[sender].balance += delegation.stakedBalance | ||
state.accounts[sender].balance -= totalCost(0, bytesPaid) | ||
state.accounts[sender].status = None | ||
|
||
# Return the delegated stake | ||
state.accounts[sender].balance += delegation.stakedBalance | ||
# Also disperse rewards (commission has already been levied) | ||
state.accounts[sender].balance += delegation.stakedBalance * (delegation.endEntry - delegation.beginEntry) | ||
|
||
if state.accounts[delegation.validator].status == AccountStatus.ValidatorQueued || | ||
state.accounts[delegation.validator].status == AccountStatus.ValidatorUnbonding | ||
|
@@ -562,7 +563,7 @@ if validator.delegatedCount == 0 && | |
|
||
delete state.accounts[sender].delegationInfo | ||
|
||
state.activeValidatorSet[block.header.proposerAddress].pendingRewards += tipCost(bytesPaid) | ||
state.activeValidatorSet.proposerBlockReward += tipCost(bytesPaid) | ||
``` | ||
|
||
#### SignedTransactionDataBurn | ||
|
@@ -585,7 +586,7 @@ Apply the following to the state: | |
state.accounts[sender].nonce += 1 | ||
state.accounts[sender].balance -= totalCost(tx.amount, bytesPaid) | ||
|
||
state.activeValidatorSet[block.header.proposerAddress].pendingRewards += tipCost(bytesPaid) | ||
state.activeValidatorSet.proposerBlockReward += tipCost(bytesPaid) | ||
``` | ||
|
||
#### Begin Block | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This spec assumes these computations will be done every block. Why not epoch it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess this is because this is how it works in the SDK currently. While we want epochs on the long run I'm against putting more effort into this now. It is "just" an optimization (yeah, a really cool one but I think there are higher prio ones). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cosmos/cosmos-sdk#8328 😉 Should land in the sdk before launch. May be worth leaving a note that epochs are preferred. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice! Thanks for bringing this to our attention. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The only real benefit to epochs for our usecase is reducing light client overhead since fewer block headers need to be downloaded. It'll probably be important long-term, but it's just an optimization and we can worry about those later. |
||
|
@@ -595,12 +596,47 @@ At the beginning of the block, rewards are distributed to the block proposer. | |
Apply the following to the state: | ||
|
||
```py | ||
proposer = state.activeValidatorSet[block.header.proposerAddress] | ||
|
||
rewardFactor = (TARGET_ANNUAL_ISSUANCE * BLOCK_TIME) / (SECONDS_PER_YEAR * sqrt(GENESIS_COIN_COUNT)) | ||
state.activeValidatorSet[block.header.proposerAddress].pendingRewards += rewardFactor * sqrt(state.activeValidatorSet.votingPower) | ||
blockReward = rewardFactor * sqrt(state.activeValidatorSet.activeVotingPower) | ||
state.activeValidatorSet.proposerBlockReward = blockReward | ||
|
||
state.activeValidatorSet[block.header.proposerAddress] = proposer | ||
``` | ||
|
||
#### End Block | ||
|
||
```py | ||
account = state.accounts[block.header.proposerAddress] | ||
|
||
if account.status == AccountStatus.ValidatorUnbonding | ||
account.status == AccountStatus.ValidatorUnbonded | ||
proposer = state.inactiveValidatorSet[block.header.proposerAddress] | ||
else if account.status == AccountStatus.ValidatorBonded | ||
proposer = state.activeValidatorSet[block.header.proposerAddress] | ||
|
||
blockReward = state.activeValidatorSet.proposerBlockReward | ||
commissionReward = proposer.commission * blockReward | ||
proposer.commissionRewards += commissionReward | ||
proposer.pendingRewards += blockReward - commissionReward | ||
|
||
# Even though the voting power hasn't changed yet, we consider this a period change. | ||
# Note: this isn't perfect because the block reward is shared with delegations | ||
# _through_ this block rather than up to the beginning of this block. | ||
proposer.latestEntry += proposer.pendingRewards // proposer.votingPower | ||
proposer.pendingRewards = 0 | ||
|
||
proposer.votingPower += blockReward | ||
state.activeValidatorSet.activeVotingPower += blockReward | ||
|
||
if account.status == AccountStatus.ValidatorUnbonding | ||
account.status == AccountStatus.ValidatorUnbonded | ||
state.inactiveValidatorSet[block.header.proposerAddress] = proposer | ||
else if account.status == AccountStatus.ValidatorBonded | ||
state.activeValidatorSet[block.header.proposerAddress] = proposer | ||
``` | ||
|
||
adlerjohn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
At the end of a block, the top `MAX_VALIDATORS` validators by voting power are or become active (bonded). For newly-bonded validators, the entire validator object is moved to the active validators subtree and their status is changed to bonded. For previously-bonded validators that are no longer in the top `MAX_VALIDATORS` validators begin unbonding. | ||
|
||
Bonding validators is simply setting their status to `AccountStatus.ValidatorBonded`. The logic for validator unbonding is found [here](#signedtransactiondatabeginunbondingvalidator), minus transaction sender updates (nonce, balance, and fee). | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the benefit here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is part of the refactor of removing staked balance for validators:
(Note that this does introduce a divide-by-zero case when computing entries, but I'll take care of that in a future PR, issue in #115.)