working fee distribution reorg

This commit is contained in:
rigelrozanski 2018-05-09 21:39:14 -04:00
parent 22e9fc276d
commit fa64487e65
19 changed files with 1017 additions and 347 deletions

View File

@ -431,7 +431,7 @@ func TestStakeMsgs(t *testing.T) {
ctxDeliver = gapp.BaseApp.NewContext(false, abci.Header{})
res2 = gapp.accountMapper.GetAccount(ctxDeliver, addr2)
require.Equal(t, genCoins.Minus(sdk.Coins{bondCoin}), res2.GetCoins())
bond, found := gapp.stakeKeeper.GetDelegatorBond(ctxDeliver, addr2, addr1)
bond, found := gapp.stakeKeeper.GetDelegation(ctxDeliver, addr2, addr1)
require.True(t, found)
require.Equal(t, bond.DelegatorAddr, addr2)
@ -445,7 +445,7 @@ func TestStakeMsgs(t *testing.T) {
ctxDeliver = gapp.BaseApp.NewContext(false, abci.Header{})
res2 = gapp.accountMapper.GetAccount(ctxDeliver, addr2)
require.Equal(t, genCoins, res2.GetCoins())
_, found = gapp.stakeKeeper.GetDelegatorBond(ctxDeliver, addr2, addr1)
_, found = gapp.stakeKeeper.GetDelegation(ctxDeliver, addr2, addr1)
require.False(t, found)
}

View File

@ -47,8 +47,8 @@ func main() {
authcmd.GetAccountCmd("acc", cdc, authcmd.GetAccountDecoder(cdc)),
stakecmd.GetCmdQueryCandidate("stake", cdc),
stakecmd.GetCmdQueryCandidates("stake", cdc),
stakecmd.GetCmdQueryDelegatorBond("stake", cdc),
//stakecmd.GetCmdQueryDelegatorBonds("stake", cdc),
stakecmd.GetCmdQueryDelegation("stake", cdc),
stakecmd.GetCmdQueryDelegations("stake", cdc),
)...)
rootCmd.AddCommand(
client.PostCommands(

View File

@ -297,12 +297,12 @@ type TxProveLive struct {
## Delegator bond
Atom holders may delegate coins to validators, under this circumstance their
funds are held in a `DelegatorBond`. It is owned by one delegator, and is
funds are held in a `Delegation`. It is owned by one delegator, and is
associated with the shares for one validator. The sender of the transaction is
considered to be the owner of the bond,
``` golang
type DelegatorBond struct {
type Delegation struct {
Candidate crypto.PubKey
Shares rational.Rat
AdjustmentFeePool coin.Coins
@ -318,11 +318,11 @@ Description:
- AdjustmentRewardPool: Adjustment factor used to passively calculate each
bonds entitled fees from `Candidate.ProposerRewardPool``
Each `DelegatorBond` is individually indexed within the store by delegator
Each `Delegation` is individually indexed within the store by delegator
address and candidate pubkey.
- key: Delegator and Candidate-Pubkey
- value: DelegatorBond
- value: Delegation
### Delegating
@ -330,7 +330,7 @@ address and candidate pubkey.
Delegator bonds are created using the TxDelegate transaction. Within this
transaction the validator candidate queried with an amount of coins, whereby
given the current exchange rate of candidate's delegator-shares-to-atoms the
candidate will return shares which are assigned in `DelegatorBond.Shares`.
candidate will return shares which are assigned in `Delegation.Shares`.
``` golang
type TxDelegate struct {
@ -671,5 +671,5 @@ rate, all commission on fees must be simultaneously withdrawn.
`candidate.Adjustment` must be set to the value of `canidate.Count` for the
height which the candidate is added on the validator set.
- The feePool of a new delegator bond will be 0 for the height at which the bond
was added. This is achieved by setting `DelegatorBond.FeeWithdrawalHeight` to
was added. This is achieved by setting `Delegation.FeeWithdrawalHeight` to
the height which the bond was added.

View File

@ -34,7 +34,7 @@ The staking module persists the following to the store:
- `GlobalState`, describing the global pools
- a `Candidate` for each candidate validator, indexed by public key
- a `Candidate` for each candidate validator, indexed by shares in the global pool (ie. ordered)
- a `DelegatorBond` for each delegation to a candidate by a delegator, indexed by delegator and candidate
- a `Delegation` for each delegation to a candidate by a delegator, indexed by delegator and candidate
public keys
- a `Queue` of unbonding delegations (TODO)
@ -146,15 +146,15 @@ When validators are kicked from the validator set they are removed from this
list.
### DelegatorBond
### Delegation
Atom holders may delegate coins to validators, under this circumstance their
funds are held in a `DelegatorBond`. It is owned by one delegator, and is
funds are held in a `Delegation`. It is owned by one delegator, and is
associated with the shares for one validator. The sender of the transaction is
considered to be the owner of the bond,
``` golang
type DelegatorBond struct {
type Delegation struct {
Candidate crypto.PubKey
Shares rational.Rat
AdjustmentFeePool coin.Coins
@ -170,11 +170,11 @@ Description:
- AdjustmentRewardPool: Adjustment factor used to passively calculate each
bonds entitled fees from `Candidate.ProposerRewardPool``
Each `DelegatorBond` is individually indexed within the store by delegator
Each `Delegation` is individually indexed within the store by delegator
address and candidate pubkey.
- key: Delegator and Candidate-Pubkey
- value: DelegatorBond
- value: Delegation
### Unbonding Queue
@ -308,7 +308,7 @@ All bonding, whether self-bonding or delegation, is done via
Delegator bonds are created using the TxDelegate transaction. Within this
transaction the validator candidate queried with an amount of coins, whereby
given the current exchange rate of candidate's delegator-shares-to-atoms the
candidate will return shares which are assigned in `DelegatorBond.Shares`.
candidate will return shares which are assigned in `Delegation.Shares`.
``` golang
type TxDelegate struct {
@ -694,5 +694,5 @@ rate, all commission on fees must be simultaneously withdrawn.
`candidate.Adjustment` must be set to the value of `canidate.Count` for the
height which the candidate is added on the validator set.
- The feePool of a new delegator bond will be 0 for the height at which the bond
was added. This is achieved by setting `DelegatorBond.FeeWithdrawalHeight` to
was added. This is achieved by setting `Delegation.FeeWithdrawalHeight` to
the height which the bond was added.

View File

@ -0,0 +1,624 @@
# Staking Module
## Overview
The Cosmos Hub is a Tendermint-based Proof of Stake blockchain system that
serves as a backbone of the Cosmos ecosystem. It is operated and secured by an
open and globally decentralized set of validators. Tendermint consensus is a
Byzantine fault-tolerant distributed protocol that involves all validators in
the process of exchanging protocol messages in the production of each block. To
avoid Nothing-at-Stake problem, a validator in Tendermint needs to lock up
coins in a bond deposit. Tendermint protocol messages are signed by the
validator's private key, and this is a basis for Tendermint strict
accountability that allows punishing misbehaving validators by slashing
(burning) their bonded Atoms. On the other hand, validators are rewarded for
their service of securing blockchain network by the inflationary provisions and
transactions fees. This incentives correct behavior of the validators and
provides the economic security of the network.
The native token of the Cosmos Hub is called Atom; becoming a validator of the
Cosmos Hub requires holding Atoms. However, not all Atom holders are validators
of the Cosmos Hub. More precisely, there is a selection process that determines
the validator set as a subset of all validator candidates (Atom holders that
wants to become a validator). The other option for Atom holder is to delegate
their atoms to validators, i.e., being a delegator. A delegator is an Atom
holder that has bonded its Atoms by delegating it to a validator (or validator
candidate). By bonding Atoms to secure the network (and taking a risk of being
slashed in case of misbehaviour), a user is rewarded with inflationary
provisions and transaction fees proportional to the amount of its bonded Atoms.
The Cosmos Hub is designed to efficiently facilitate a small numbers of
validators (hundreds), and large numbers of delegators (tens of thousands).
More precisely, it is the role of the Staking module of the Cosmos Hub to
support various staking functionality including validator set selection,
delegating, bonding and withdrawing Atoms, and the distribution of inflationary
provisions and transaction fees.
## State
The staking module persists the following information to the store:
* `GlobalState`, describing the global pools and the inflation related fields
* validator candidates (including current validators), indexed by public key and shares in the global pool
(bonded or unbonded depending on candidate status)
* delegator bonds (for each delegation to a candidate by a delegator), indexed by the delegator address and the candidate
public key
* the queue of unbonding delegations
* the queue of re-delegations
### Global State
The GlobalState data structure contains total Atom supply, amount of Atoms in
the bonded pool, sum of all shares distributed for the bonded pool, amount of
Atoms in the unbonded pool, sum of all shares distributed for the unbonded
pool, a timestamp of the last processing of inflation, the current annual
inflation rate, a timestamp for the last comission accounting reset, the global
fee pool, a pool of reserve taxes collected for the governance use and an
adjustment factor for calculating global fee accum. `Params` is global data
structure that stores system parameters and defines overall functioning of the
module.
``` go
type GlobalState struct {
TotalSupply int64 // total supply of Atoms
BondedPool int64 // reserve of bonded tokens
BondedShares rational.Rat // sum of all shares distributed for the BondedPool
UnbondedPool int64 // reserve of unbonding tokens held with candidates
UnbondedShares rational.Rat // sum of all shares distributed for the UnbondedPool
InflationLastTime int64 // timestamp of last processing of inflation
Inflation rational.Rat // current annual inflation rate
DateLastCommissionReset int64 // unix timestamp for last commission accounting reset
FeePool coin.Coins // fee pool for all the fee shares which have already been distributed
ReservePool coin.Coins // pool of reserve taxes collected on all fees for governance use
Adjustment rational.Rat // Adjustment factor for calculating global fee accum
}
type Params struct {
HoldBonded Address // account where all bonded coins are held
HoldUnbonding Address // account where all delegated but unbonding coins are held
InflationRateChange rational.Rational // maximum annual change in inflation rate
InflationMax rational.Rational // maximum inflation rate
InflationMin rational.Rational // minimum inflation rate
GoalBonded rational.Rational // Goal of percent bonded atoms
ReserveTax rational.Rational // Tax collected on all fees
MaxVals uint16 // maximum number of validators
AllowedBondDenom string // bondable coin denomination
// gas costs for txs
GasDeclareCandidacy int64
GasEditCandidacy int64
GasDelegate int64
GasRedelegate int64
GasUnbond int64
}
```
### Candidate
The `Candidate` data structure holds the current state and some historical
actions of validators or candidate-validators.
``` go
type Candidate struct {
Status CandidateStatus
ConsensusPubKey crypto.PubKey
GovernancePubKey crypto.PubKey
Owner crypto.Address
GlobalStakeShares rational.Rat
IssuedDelegatorShares rational.Rat
RedelegatingShares rational.Rat
VotingPower rational.Rat
Commission rational.Rat
CommissionMax rational.Rat
CommissionChangeRate rational.Rat
CommissionChangeToday rational.Rat
ProposerRewardPool coin.Coins
Adjustment rational.Rat
Description Description
}
type Description struct {
Name string
DateBonded string
Identity string
Website string
Details string
}
```
Candidate parameters are described:
* Status: it can be Bonded (active validator), Unbonding (validator candidate)
or Revoked
* ConsensusPubKey: candidate public key that is used strictly for participating in
consensus
* GovernancePubKey: public key used by the validator for governance voting
* Owner: Address that is allowed to unbond coins.
* GlobalStakeShares: Represents shares of `GlobalState.BondedPool` if
`Candidate.Status` is `Bonded`; or shares of `GlobalState.Unbondingt Pool`
otherwise
* IssuedDelegatorShares: Sum of all shares a candidate issued to delegators
(which includes the candidate's self-bond); a delegator share represents
their stake in the Candidate's `GlobalStakeShares`
* RedelegatingShares: The portion of `IssuedDelegatorShares` which are
currently re-delegating to a new validator
* VotingPower: Proportional to the amount of bonded tokens which the validator
has if `Candidate.Status` is `Bonded`; otherwise it is equal to `0`
* Commission: The commission rate of fees charged to any delegators
* CommissionMax: The maximum commission rate this candidate can charge each
day from the date `GlobalState.DateLastCommissionReset`
* CommissionChangeRate: The maximum daily increase of the candidate commission
* CommissionChangeToday: Counter for the amount of change to commission rate
which has occurred today, reset on the first block of each day (UTC time)
* ProposerRewardPool: reward pool for extra fees collected when this candidate
is the proposer of a block
* Adjustment factor used to passively calculate each validators entitled fees
from `GlobalState.FeePool`
* Description
* Name: moniker
* DateBonded: date determined which the validator was bonded
* Identity: optional field to provide a signature which verifies the
validators identity (ex. UPort or Keybase)
* Website: optional website link
* Details: optional details
### Delegation
Atom holders may delegate coins to candidates; under this circumstance their
funds are held in a `Delegation` data structure. It is owned by one
delegator, and is associated with the shares for one candidate. The sender of
the transaction is the owner of the bond.
``` go
type Delegation struct {
Candidate crypto.PubKey
Shares rational.Rat
AdjustmentFeePool coin.Coins
AdjustmentRewardPool coin.Coins
}
```
Description:
* Candidate: the public key of the validator candidate: bonding too
* Shares: the number of delegator shares received from the validator candidate
* AdjustmentFeePool: Adjustment factor used to passively calculate each bonds
entitled fees from `GlobalState.FeePool`
* AdjustmentRewardPool: Adjustment factor used to passively calculate each
bonds entitled fees from `Candidate.ProposerRewardPool`
### QueueElem
The Unbonding and re-delegation process is implemented using the ordered queue
data structure. All queue elements share a common structure:
```golang
type QueueElem struct {
Candidate crypto.PubKey
InitTime int64 // when the element was added to the queue
}
```
The queue is ordered so the next element to unbond/re-delegate is at the head.
Every tick the head of the queue is checked and if the unbonding period has
passed since `InitTime`, the final settlement of the unbonding is started or
re-delegation is executed, and the element is popped from the queue. Each
`QueueElem` is persisted in the store until it is popped from the queue.
### QueueElemUnbondDelegation
QueueElemUnbondDelegation structure is used in the unbonding queue.
```golang
type QueueElemUnbondDelegation struct {
QueueElem
Payout Address // account to pay out to
Tokens coin.Coins // the value in Atoms of the amount of delegator shares which are unbonding
StartSlashRatio rational.Rat // candidate slash ratio
}
```
### QueueElemReDelegate
QueueElemReDelegate structure is used in the re-delegation queue.
```golang
type QueueElemReDelegate struct {
QueueElem
Payout Address // account to pay out to
Shares rational.Rat // amount of shares which are unbonding
NewCandidate crypto.PubKey // validator to bond to after unbond
}
```
### Transaction Overview
Available Transactions:
* TxDeclareCandidacy
* TxEditCandidacy
* TxDelegate
* TxUnbond
* TxRedelegate
* TxLivelinessCheck
* TxProveLive
## Transaction processing
In this section we describe the processing of the transactions and the
corresponding updates to the global state. In the following text we will use
`gs` to refer to the `GlobalState` data structure, `unbondDelegationQueue` is a
reference to the queue of unbond delegations, `reDelegationQueue` is the
reference for the queue of redelegations. We use `tx` to denote a
reference to a transaction that is being processed, and `sender` to denote the
address of the sender of the transaction. We use function
`loadCandidate(store, PubKey)` to obtain a Candidate structure from the store,
and `saveCandidate(store, candidate)` to save it. Similarly, we use
`loadDelegation(store, sender, PubKey)` to load a delegator bond with the
key (sender and PubKey) from the store, and
`saveDelegation(store, sender, bond)` to save it.
`removeDelegation(store, sender, bond)` is used to remove the bond from the
store.
### TxDeclareCandidacy
A validator candidacy is declared using the `TxDeclareCandidacy` transaction.
```golang
type TxDeclareCandidacy struct {
ConsensusPubKey crypto.PubKey
Amount coin.Coin
GovernancePubKey crypto.PubKey
Commission rational.Rat
CommissionMax int64
CommissionMaxChange int64
Description Description
}
declareCandidacy(tx TxDeclareCandidacy):
candidate = loadCandidate(store, tx.PubKey)
if candidate != nil return // candidate with that public key already exists
candidate = NewCandidate(tx.PubKey)
candidate.Status = Unbonded
candidate.Owner = sender
init candidate VotingPower, GlobalStakeShares, IssuedDelegatorShares, RedelegatingShares and Adjustment to rational.Zero
init commision related fields based on the values from tx
candidate.ProposerRewardPool = Coin(0)
candidate.Description = tx.Description
saveCandidate(store, candidate)
txDelegate = TxDelegate(tx.PubKey, tx.Amount)
return delegateWithCandidate(txDelegate, candidate)
// see delegateWithCandidate function in [TxDelegate](TxDelegate)
```
### TxEditCandidacy
If either the `Description` (excluding `DateBonded` which is constant),
`Commission`, or the `GovernancePubKey` need to be updated, the
`TxEditCandidacy` transaction should be sent from the owner account:
```golang
type TxEditCandidacy struct {
GovernancePubKey crypto.PubKey
Commission int64
Description Description
}
editCandidacy(tx TxEditCandidacy):
candidate = loadCandidate(store, tx.PubKey)
if candidate == nil or candidate.Status == Revoked return
if tx.GovernancePubKey != nil candidate.GovernancePubKey = tx.GovernancePubKey
if tx.Commission >= 0 candidate.Commission = tx.Commission
if tx.Description != nil candidate.Description = tx.Description
saveCandidate(store, candidate)
return
```
### TxDelegate
Delegator bonds are created using the `TxDelegate` transaction. Within this
transaction the delegator provides an amount of coins, and in return receives
some amount of candidate's delegator shares that are assigned to
`Delegation.Shares`.
```golang
type TxDelegate struct {
PubKey crypto.PubKey
Amount coin.Coin
}
delegate(tx TxDelegate):
candidate = loadCandidate(store, tx.PubKey)
if candidate == nil return
return delegateWithCandidate(tx, candidate)
delegateWithCandidate(tx TxDelegate, candidate Candidate):
if candidate.Status == Revoked return
if candidate.Status == Bonded
poolAccount = params.HoldBonded
else
poolAccount = params.HoldUnbonded
err = transfer(sender, poolAccount, tx.Amount)
if err != nil return
bond = loadDelegation(store, sender, tx.PubKey)
if bond == nil then bond = Delegation(tx.PubKey, rational.Zero, Coin(0), Coin(0))
issuedDelegatorShares = addTokens(tx.Amount, candidate)
bond.Shares += issuedDelegatorShares
saveCandidate(store, candidate)
saveDelegation(store, sender, bond)
saveGlobalState(store, gs)
return
addTokens(amount coin.Coin, candidate Candidate):
if candidate.Status == Bonded
gs.BondedPool += amount
issuedShares = amount / exchangeRate(gs.BondedShares, gs.BondedPool)
gs.BondedShares += issuedShares
else
gs.UnbondedPool += amount
issuedShares = amount / exchangeRate(gs.UnbondedShares, gs.UnbondedPool)
gs.UnbondedShares += issuedShares
candidate.GlobalStakeShares += issuedShares
if candidate.IssuedDelegatorShares.IsZero()
exRate = rational.One
else
exRate = candidate.GlobalStakeShares / candidate.IssuedDelegatorShares
issuedDelegatorShares = issuedShares / exRate
candidate.IssuedDelegatorShares += issuedDelegatorShares
return issuedDelegatorShares
exchangeRate(shares rational.Rat, tokenAmount int64):
if shares.IsZero() then return rational.One
return tokenAmount / shares
```
### TxUnbond
Delegator unbonding is defined with the following transaction:
```golang
type TxUnbond struct {
PubKey crypto.PubKey
Shares rational.Rat
}
unbond(tx TxUnbond):
bond = loadDelegation(store, sender, tx.PubKey)
if bond == nil return
if bond.Shares < tx.Shares return
bond.Shares -= tx.Shares
candidate = loadCandidate(store, tx.PubKey)
revokeCandidacy = false
if bond.Shares.IsZero()
if sender == candidate.Owner and candidate.Status != Revoked then revokeCandidacy = true then removeDelegation(store, sender, bond)
else
saveDelegation(store, sender, bond)
if candidate.Status == Bonded
poolAccount = params.HoldBonded
else
poolAccount = params.HoldUnbonded
returnedCoins = removeShares(candidate, shares)
unbondDelegationElem = QueueElemUnbondDelegation(tx.PubKey, currentHeight(), sender, returnedCoins, startSlashRatio)
unbondDelegationQueue.add(unbondDelegationElem)
transfer(poolAccount, unbondingPoolAddress, returnCoins)
if revokeCandidacy
if candidate.Status == Bonded then bondedToUnbondedPool(candidate)
candidate.Status = Revoked
if candidate.IssuedDelegatorShares.IsZero()
removeCandidate(store, tx.PubKey)
else
saveCandidate(store, candidate)
saveGlobalState(store, gs)
return
removeShares(candidate Candidate, shares rational.Rat):
globalPoolSharesToRemove = delegatorShareExRate(candidate) * shares
if candidate.Status == Bonded
gs.BondedShares -= globalPoolSharesToRemove
removedTokens = exchangeRate(gs.BondedShares, gs.BondedPool) * globalPoolSharesToRemove
gs.BondedPool -= removedTokens
else
gs.UnbondedShares -= globalPoolSharesToRemove
removedTokens = exchangeRate(gs.UnbondedShares, gs.UnbondedPool) * globalPoolSharesToRemove
gs.UnbondedPool -= removedTokens
candidate.GlobalStakeShares -= removedTokens
candidate.IssuedDelegatorShares -= shares
return returnedCoins
delegatorShareExRate(candidate Candidate):
if candidate.IssuedDelegatorShares.IsZero() then return rational.One
return candidate.GlobalStakeShares / candidate.IssuedDelegatorShares
bondedToUnbondedPool(candidate Candidate):
removedTokens = exchangeRate(gs.BondedShares, gs.BondedPool) * candidate.GlobalStakeShares
gs.BondedShares -= candidate.GlobalStakeShares
gs.BondedPool -= removedTokens
gs.UnbondedPool += removedTokens
issuedShares = removedTokens / exchangeRate(gs.UnbondedShares, gs.UnbondedPool)
gs.UnbondedShares += issuedShares
candidate.GlobalStakeShares = issuedShares
candidate.Status = Unbonded
return transfer(address of the bonded pool, address of the unbonded pool, removedTokens)
```
### TxRedelegate
The re-delegation command allows delegators to switch validators while still
receiving equal reward to as if they had never unbonded.
```golang
type TxRedelegate struct {
PubKeyFrom crypto.PubKey
PubKeyTo crypto.PubKey
Shares rational.Rat
}
redelegate(tx TxRedelegate):
bond = loadDelegation(store, sender, tx.PubKey)
if bond == nil then return
if bond.Shares < tx.Shares return
candidate = loadCandidate(store, tx.PubKeyFrom)
if candidate == nil return
candidate.RedelegatingShares += tx.Shares
reDelegationElem = QueueElemReDelegate(tx.PubKeyFrom, currentHeight(), sender, tx.Shares, tx.PubKeyTo)
redelegationQueue.add(reDelegationElem)
return
```
### TxLivelinessCheck
Liveliness issues are calculated by keeping track of the block precommits in
the block header. A queue is persisted which contains the block headers from
all recent blocks for the duration of the unbonding period. A validator is
defined as having livliness issues if they have not been included in more than
33% of the blocks over:
* The most recent 24 Hours if they have >= 20% of global stake
* The most recent week if they have = 0% of global stake
* Linear interpolation of the above two scenarios
Liveliness kicks are only checked when a `TxLivelinessCheck` transaction is
submitted.
```golang
type TxLivelinessCheck struct {
PubKey crypto.PubKey
RewardAccount Addresss
}
```
If the `TxLivelinessCheck` is successful in kicking a validator, 5% of the
liveliness punishment is provided as a reward to `RewardAccount`.
### TxProveLive
If the validator was kicked for liveliness issues and is able to regain
liveliness then all delegators in the temporary unbonding pool which have not
transacted to move will be bonded back to the now-live validator and begin to
once again collect provisions and rewards. Regaining liveliness is demonstrated
by sending in a `TxProveLive` transaction:
```golang
type TxProveLive struct {
PubKey crypto.PubKey
}
```
### End of block handling
```golang
tick(ctx Context):
hrsPerYr = 8766 // as defined by a julian year of 365.25 days
time = ctx.Time()
if time > gs.InflationLastTime + ProvisionTimeout
gs.InflationLastTime = time
gs.Inflation = nextInflation(hrsPerYr).Round(1000000000)
provisions = gs.Inflation * (gs.TotalSupply / hrsPerYr)
gs.BondedPool += provisions
gs.TotalSupply += provisions
saveGlobalState(store, gs)
if time > unbondDelegationQueue.head().InitTime + UnbondingPeriod
for each element elem in the unbondDelegationQueue where time > elem.InitTime + UnbondingPeriod do
transfer(unbondingQueueAddress, elem.Payout, elem.Tokens)
unbondDelegationQueue.remove(elem)
if time > reDelegationQueue.head().InitTime + UnbondingPeriod
for each element elem in the unbondDelegationQueue where time > elem.InitTime + UnbondingPeriod do
candidate = getCandidate(store, elem.PubKey)
returnedCoins = removeShares(candidate, elem.Shares)
candidate.RedelegatingShares -= elem.Shares
delegateWithCandidate(TxDelegate(elem.NewCandidate, returnedCoins), candidate)
reDelegationQueue.remove(elem)
return UpdateValidatorSet()
nextInflation(hrsPerYr rational.Rat):
if gs.TotalSupply > 0
bondedRatio = gs.BondedPool / gs.TotalSupply
else
bondedRation = 0
inflationRateChangePerYear = (1 - bondedRatio / params.GoalBonded) * params.InflationRateChange
inflationRateChange = inflationRateChangePerYear / hrsPerYr
inflation = gs.Inflation + inflationRateChange
if inflation > params.InflationMax then inflation = params.InflationMax
if inflation < params.InflationMin then inflation = params.InflationMin
return inflation
UpdateValidatorSet():
candidates = loadCandidates(store)
v1 = candidates.Validators()
v2 = updateVotingPower(candidates).Validators()
change = v1.validatorsUpdated(v2) // determine all updated validators between two validator sets
return change
updateVotingPower(candidates Candidates):
foreach candidate in candidates do
candidate.VotingPower = (candidate.IssuedDelegatorShares - candidate.RedelegatingShares) * delegatorShareExRate(candidate)
candidates.Sort()
foreach candidate in candidates do
if candidate is not in the first params.MaxVals
candidate.VotingPower = rational.Zero
if candidate.Status == Bonded then bondedToUnbondedPool(candidate Candidate)
else if candidate.Status == UnBonded then unbondedToBondedPool(candidate)
saveCandidate(store, c)
return candidates
unbondedToBondedPool(candidate Candidate):
removedTokens = exchangeRate(gs.UnbondedShares, gs.UnbondedPool) * candidate.GlobalStakeShares
gs.UnbondedShares -= candidate.GlobalStakeShares
gs.UnbondedPool -= removedTokens
gs.BondedPool += removedTokens
issuedShares = removedTokens / exchangeRate(gs.BondedShares, gs.BondedPool)
gs.BondedShares += issuedShares
candidate.GlobalStakeShares = issuedShares
candidate.Status = Bonded
return transfer(address of the unbonded pool, address of the bonded pool, removedTokens)
```

57
types/stake.go Normal file
View File

@ -0,0 +1,57 @@
package types
import (
abci "github.com/tendermint/abci/types"
"github.com/tendermint/go-crypto"
)
// status of a validator
type ValidatorStatus byte
// nolint
const (
Bonded ValidatorStatus = 0x00
Unbonding ValidatorStatus = 0x01
Unbonded ValidatorStatus = 0x02
Revoked ValidatorStatus = 0x03
)
// validator for a delegated proof of stake system
type Validator interface {
Status() ValidatorStatus // status of the validator
GetOwner() Address // owner address to receive/return validators coins
GetPubKey() crypto.PubKey // validation pubkey
GetPower() Rat // validation power
GetBondHeight() int64 // height in which the validator became active
}
// validator which fulfills abci validator interface for use in Tendermint
func ABCIValidator(v Validator) abci.Validator {
return abci.Validator{
PubKey: v.GetPubKey().Bytes(),
Power: v.GetPower().Evaluate(),
}
}
// properties for the set of all validators
type ValidatorSet interface {
Iterate(func(index int64, validator Validator)) // execute arbitrary logic for each validator
Validator(Context, Address) Validator // get a particular validator by owner address
TotalPower(Context) Rat // total power of the validator set
}
//_______________________________________________________________________________
// delegation bond for a delegated proof of stake system
type Delegation interface {
GetDelegator() Address // delegator address for the bond
GetValidator() Address // validator owner address for the bond
GetBondShares() Rat // amount of validator's shares
}
// properties for the set of all delegations for a particular
type DelegationSet interface {
// execute arbitrary logic for each validator which a delegator has a delegation for
Iterate(delegator Address, fn func(index int64, delegation Delegation))
}

View File

@ -1,43 +0,0 @@
package types
import (
abci "github.com/tendermint/abci/types"
"github.com/tendermint/go-crypto"
)
type Validator interface {
GetAddress() Address
GetPubKey() crypto.PubKey
GetPower() Rat
}
func ABCIValidator(v Validator) abci.Validator {
return abci.Validator{
PubKey: v.GetPubKey().Bytes(),
Power: v.GetPower().Evaluate(),
}
}
type ValidatorSet interface {
Iterate(func(int, Validator))
Size() int
}
type ValidatorSetKeeper interface {
ValidatorSet(Context) ValidatorSet
Validator(Context, Address) Validator
TotalPower(Context) Rat
DelegationSet(Context, Address) DelegationSet
Delegation(Context, Address, Address) Delegation
}
type Delegation interface {
GetDelegator() Address
GetValidator() Address
GetBondAmount() Rat
}
type DelegationSet interface {
Iterate(func(int, Delegation))
Size() int
}

View File

@ -4,6 +4,9 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// burn burn burn
func BurnFeeHandler(ctx sdk.Context, collectedFees sdk.Coins) {}
// Handle fee distribution to the validators and delegators
func (k Keeper) FeeHandler(ctx sdk.Context, collectedFees sdk.Coins) {
pool := k.GetPool(ctx)

113
x/fee_distribution/types.go Normal file
View File

@ -0,0 +1,113 @@
package stake
import (
"encoding/binary"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// GenesisState - all staking state that must be provided at genesis
type GenesisState struct {
Pool Pool `json:"pool"`
Params Params `json:"params"`
}
func NewGenesisState(pool Pool, params Params, candidates []Candidate, bonds []Delegation) GenesisState {
return GenesisState{
Pool: pool,
Params: params,
}
}
// get raw genesis raw message for testing
func DefaultGenesisState() GenesisState {
return GenesisState{
Pool: initialPool(),
Params: defaultParams(),
}
}
// fee information for a validator
type Validator struct {
Adjustments []sdk.Rat `json:"fee_adjustments"` // XXX Adjustment factors for lazy fee accounting, couples with Params.BondDenoms
PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // total shares of a global hold pools
}
//_________________________________________________________________________
// Params defines the high level settings for staking
type Params struct {
FeeDenoms []string `json:"fee_denoms"` // accepted fee denoms
ReservePoolFee sdk.Rat `json:"reserve_pool_fee"` // percent of fees which go to reserve pool
}
func (p Params) equal(p2 Params) bool {
return p.BondDenom == p2.BondDenom &&
p.ReservePoolFee.Equal(p2.ReservePoolFee)
}
func defaultParams() Params {
return Params{
FeeDenoms: []string{"steak"},
ReservePoolFee: sdk.NewRat(5, 100),
}
}
//_________________________________________________________________________
// Pool - dynamic parameters of the current state
type Pool struct {
FeeReservePool sdk.Coins `json:"fee_reserve_pool"` // XXX reserve pool of collected fees for use by governance
FeePool sdk.Coins `json:"fee_pool"` // XXX fee pool for all the fee shares which have already been distributed
FeeSumReceived sdk.Coins `json:"fee_sum_received"` // XXX sum of all fees received, post reserve pool `json:"fee_sum_received"`
FeeRecent sdk.Coins `json:"fee_recent"` // XXX most recent fee collected
FeeAdjustments []sdk.Rat `json:"fee_adjustments"` // XXX Adjustment factors for lazy fee accounting, couples with Params.BondDenoms
PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // XXX last recorded bonded shares
}
func (p Pool) equal(p2 Pool) bool {
return p.FeeReservePool.IsEqual(p2.FeeReservePool) &&
p.FeePool.IsEqual(p2.FeePool) &&
p.FeeSumReceived.IsEqual(p2.FeeSumReceived) &&
p.FeeRecent.IsEqual(p2.FeeRecent) &&
sdk.RatsEqual(p.FeeAdjustments, p2.FeeAdjustments) &&
p.PrevBondedShares.Equal(p2.PrevBondedShares)
}
// initial pool for testing
func initialPool() Pool {
return Pool{
FeeReservePool: sdk.Coins(nil),
FeePool: sdk.Coins(nil),
FeeSumReceived: sdk.Coins(nil),
FeeRecent: sdk.Coins(nil),
FeeAdjustments: []sdk.Rat{sdk.ZeroRat()},
PrevBondedShares: sdk.ZeroRat(),
}
}
//_________________________________________________________________________
// Used in calculation of fee shares, added to a queue for each block where a power change occures
type PowerChange struct {
Height int64 `json:"height"` // block height at change
Power sdk.Rat `json:"power"` // total power at change
PrevPower sdk.Rat `json:"prev_power"` // total power at previous height-1
FeesIn sdk.Coins `json:"fees_in"` // fees in at block height
PrevFeePool sdk.Coins `json:"prev_fee_pool"` // total fees in at previous block height
}
//_________________________________________________________________________
// KEY MANAGEMENT
var (
// Keys for store prefixes
PowerChangeKey = []byte{0x09} // prefix for power change object
)
// get the key for the accumulated update validators
func GetPowerChangeKey(height int64) []byte {
heightBytes := make([]byte, binary.MaxVarintLen64)
binary.BigEndian.PutUint64(heightBytes, ^uint64(height)) // invert height (older validators first)
return append(PowerChangeKey, heightBytes...)
}

View File

@ -87,7 +87,7 @@ func GetCmdQueryCandidates(storeName string, cdc *wire.Codec) *cobra.Command {
}
// get the command to query a single delegator bond
func GetCmdQueryDelegatorBond(storeName string, cdc *wire.Codec) *cobra.Command {
func GetCmdQueryDelegation(storeName string, cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "delegator-bond",
Short: "Query a delegators bond based on address and candidate pubkey",
@ -104,7 +104,7 @@ func GetCmdQueryDelegatorBond(storeName string, cdc *wire.Codec) *cobra.Command
}
delegator := crypto.Address(bz)
key := stake.GetDelegatorBondKey(delegator, addr, cdc)
key := stake.GetDelegationKey(delegator, addr, cdc)
ctx := context.NewCoreContextFromViper()
res, err := ctx.Query(key, storeName)
if err != nil {
@ -112,7 +112,7 @@ func GetCmdQueryDelegatorBond(storeName string, cdc *wire.Codec) *cobra.Command
}
// parse out the bond
bond := new(stake.DelegatorBond)
bond := new(stake.Delegation)
cdc.MustUnmarshalBinary(res, bond)
output, err := wire.MarshalJSONIndent(cdc, bond)
if err != nil {

View File

@ -43,7 +43,7 @@ func BondingStatusHandlerFn(storeName string, cdc *wire.Codec, kb keys.Keybase,
}
candidateAddr := sdk.Address(bz)
key := stake.GetDelegatorBondKey(delegatorAddr, candidateAddr, cdc)
key := stake.GetDelegationKey(delegatorAddr, candidateAddr, cdc)
res, err := ctx.Query(key, storeName)
if err != nil {
@ -58,7 +58,7 @@ func BondingStatusHandlerFn(storeName string, cdc *wire.Codec, kb keys.Keybase,
return
}
var bond stake.DelegatorBond
var bond stake.Delegation
err = cdc.UnmarshalBinary(res, &bond)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)

View File

@ -56,7 +56,7 @@ func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) {
k.setCandidate(ctx, candidate)
}
for _, bond := range data.Bonds {
k.setDelegatorBond(ctx, bond)
k.setDelegation(ctx, bond)
}
}
@ -169,9 +169,9 @@ func delegate(ctx sdk.Context, k Keeper, delegatorAddr sdk.Address,
bondAmt sdk.Coin, candidate Candidate) (sdk.Tags, sdk.Error) {
// Get or create the delegator bond
bond, found := k.GetDelegatorBond(ctx, delegatorAddr, candidate.Address)
bond, found := k.GetDelegation(ctx, delegatorAddr, candidate.Address)
if !found {
bond = DelegatorBond{
bond = Delegation{
DelegatorAddr: delegatorAddr,
CandidateAddr: candidate.Address,
Shares: sdk.ZeroRat(),
@ -190,7 +190,7 @@ func delegate(ctx sdk.Context, k Keeper, delegatorAddr sdk.Address,
// Update bond height
bond.Height = ctx.BlockHeight()
k.setDelegatorBond(ctx, bond)
k.setDelegation(ctx, bond)
k.setCandidate(ctx, candidate)
k.setPool(ctx, pool)
tags := sdk.NewTags("action", []byte("delegate"), "delegator", delegatorAddr.Bytes(), "candidate", candidate.Address.Bytes())
@ -200,7 +200,7 @@ func delegate(ctx sdk.Context, k Keeper, delegatorAddr sdk.Address,
func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
// check if bond has any shares in it unbond
bond, found := k.GetDelegatorBond(ctx, msg.DelegatorAddr, msg.CandidateAddr)
bond, found := k.GetDelegation(ctx, msg.DelegatorAddr, msg.CandidateAddr)
if !found {
return ErrNoDelegatorForAddress(k.codespace).Result()
}
@ -257,11 +257,11 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
revokeCandidacy = true
}
k.removeDelegatorBond(ctx, bond)
k.removeDelegation(ctx, bond)
} else {
// Update bond height
bond.Height = ctx.BlockHeight()
k.setDelegatorBond(ctx, bond)
k.setDelegation(ctx, bond)
}
// Add the coins

View File

@ -86,7 +86,7 @@ func TestIncrementsMsgDelegate(t *testing.T) {
//Check that the accounts and the bond account have the appropriate values
candidate, found := keeper.GetCandidate(ctx, candidateAddr)
require.True(t, found)
bond, found := keeper.GetDelegatorBond(ctx, delegatorAddr, candidateAddr)
bond, found := keeper.GetDelegation(ctx, delegatorAddr, candidateAddr)
require.True(t, found)
expBond := int64(i+1) * bondAmount
@ -144,7 +144,7 @@ func TestIncrementsMsgUnbond(t *testing.T) {
//Check that the accounts and the bond account have the appropriate values
candidate, found = keeper.GetCandidate(ctx, candidateAddr)
require.True(t, found)
bond, found := keeper.GetDelegatorBond(ctx, delegatorAddr, candidateAddr)
bond, found := keeper.GetDelegation(ctx, delegatorAddr, candidateAddr)
require.True(t, found)
expBond := initBond - int64(i+1)*unbondShares
@ -259,7 +259,7 @@ func TestMultipleMsgDelegate(t *testing.T) {
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
//Check that the account is bonded
bond, found := keeper.GetDelegatorBond(ctx, delegatorAddr, candidateAddr)
bond, found := keeper.GetDelegation(ctx, delegatorAddr, candidateAddr)
require.True(t, found)
require.NotNil(t, bond, "expected delegatee bond %d to exist", bond)
}
@ -271,7 +271,7 @@ func TestMultipleMsgDelegate(t *testing.T) {
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
//Check that the account is unbonded
_, found := keeper.GetDelegatorBond(ctx, delegatorAddr, candidateAddr)
_, found := keeper.GetDelegation(ctx, delegatorAddr, candidateAddr)
require.False(t, found)
}
}

View File

@ -4,6 +4,7 @@ import (
"bytes"
"sort"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/bank"
@ -133,12 +134,12 @@ func (k Keeper) setCandidate(ctx sdk.Context, candidate Candidate) {
store.Set(GetValidatorKey(validator), bzVal)
// add to the validators to update list if is already a validator
if store.Get(GetRecentValidatorKey(candidate.PubKey)) != nil {
if store.Get(GetCurrentValidatorsKey(candidate.PubKey)) != nil {
bzAbci := k.cdc.MustMarshalBinary(validator.abciValidator(k.cdc))
store.Set(GetAccUpdateValidatorKey(address), bzAbci)
// also update the recent validator store
store.Set(GetRecentValidatorKey(validator.PubKey), bzVal)
// also update the current validator store
store.Set(GetCurrentValidatorsKey(validator.PubKey), bzVal)
return
}
@ -160,19 +161,19 @@ func (k Keeper) removeCandidate(ctx sdk.Context, address sdk.Address) {
store.Delete(GetCandidateKey(address))
store.Delete(GetValidatorKey(candidate.validator()))
// delete from recent and power weighted validator groups if the validator
// delete from current and power weighted validator groups if the validator
// exists and add validator with zero power to the validator updates
if store.Get(GetRecentValidatorKey(candidate.PubKey)) == nil {
if store.Get(GetCurrentValidatorsKey(candidate.PubKey)) == nil {
return
}
bz := k.cdc.MustMarshalBinary(candidate.validator().abciValidatorZero(k.cdc))
store.Set(GetAccUpdateValidatorKey(address), bz)
store.Delete(GetRecentValidatorKey(candidate.PubKey))
store.Delete(GetCurrentValidatorsKey(candidate.PubKey))
}
//___________________________________________________________________________
// get the group of the most recent validators
// get the group of the most current validators
func (k Keeper) GetValidators(ctx sdk.Context) (validators []Validator) {
store := ctx.KVStore(k.storeKey)
@ -180,7 +181,7 @@ func (k Keeper) GetValidators(ctx sdk.Context) (validators []Validator) {
maxValidators := k.GetParams(ctx).MaxValidators
validators = make([]Validator, maxValidators)
iterator := store.SubspaceIterator(RecentValidatorsKey)
iterator := store.SubspaceIterator(CurrentValidatorsKey)
i := 0
for ; iterator.Valid(); iterator.Next() {
bz := iterator.Value()
@ -194,17 +195,17 @@ func (k Keeper) GetValidators(ctx sdk.Context) (validators []Validator) {
}
// Only used for testing
// get the group of the most recent validators
// get the group of the most current validators
func (k Keeper) getValidatorsOrdered(ctx sdk.Context) []Validator {
vals := k.GetValidators(ctx)
sort.Sort(sort.Reverse(validators(vals)))
return vals
}
// Is the address provided a part of the most recently saved validator group?
// Is the address provided a part of the current validator set?
func (k Keeper) IsValidator(ctx sdk.Context, pk crypto.PubKey) bool {
store := ctx.KVStore(k.storeKey)
if store.Get(GetRecentValidatorKey(pk)) == nil {
if store.Get(GetCurrentValidatorsKey(pk)) == nil {
return false
}
return true
@ -215,14 +216,14 @@ func (k Keeper) IsValidator(ctx sdk.Context, pk crypto.PubKey) bool {
//
// The correct subset is retrieved by iterating through an index of the
// candidates sorted by power, stored using the ValidatorsKey. Simultaniously
// the most recent the validator records are updated in store with the
// RecentValidatorsKey. This store is used to determine if a candidate is a
// the current validator records are updated in store with the
// CurrentValidatorsKey. This store is used to determine if a candidate is a
// validator without needing to iterate over the subspace as we do in
// GetValidators
func (k Keeper) addNewValidatorOrNot(ctx sdk.Context, store sdk.KVStore, address sdk.Address) {
// clear the recent validators store, add to the ToKickOut temp store
iterator := store.SubspaceIterator(RecentValidatorsKey)
// clear the current validators store, add to the ToKickOut temp store
iterator := store.SubspaceIterator(CurrentValidatorsKey)
for ; iterator.Valid(); iterator.Next() {
bz := iterator.Value()
@ -253,8 +254,8 @@ func (k Keeper) addNewValidatorOrNot(ctx sdk.Context, store sdk.KVStore, address
// remove from ToKickOut group
store.Delete(GetToKickOutValidatorKey(validator.Address))
// also add to the recent validators group
store.Set(GetRecentValidatorKey(validator.PubKey), bz)
// also add to the current validators group
store.Set(GetCurrentValidatorsKey(validator.PubKey), bz)
// MOST IMPORTANTLY, add to the accumulated changes if this is the modified candidate
if bytes.Equal(address, validator.Address) {
@ -292,7 +293,7 @@ func (k Keeper) GetTotalPrecommitVotingPower(ctx sdk.Context) sdk.Rat {
TotalPower := sdk.ZeroRat()
i := int32(0)
iterator := store.SubspaceIterator(RecentValidatorsKey)
iterator := store.SubspaceIterator(CurrentValidatorsKey)
for ; iterator.Valid(); iterator.Next() {
skip := false
@ -340,7 +341,7 @@ func (k Keeper) getAccUpdateValidators(ctx sdk.Context) (updates []abci.Validato
return
}
// remove all validator update entries
// remove all validator update entries after applied to Tendermint
func (k Keeper) clearAccUpdateValidators(ctx sdk.Context) {
store := ctx.KVStore(k.storeKey)
@ -355,11 +356,11 @@ func (k Keeper) clearAccUpdateValidators(ctx sdk.Context) {
//_____________________________________________________________________
// load a delegator bond
func (k Keeper) GetDelegatorBond(ctx sdk.Context,
delegatorAddr, candidateAddr sdk.Address) (bond DelegatorBond, found bool) {
func (k Keeper) GetDelegation(ctx sdk.Context,
delegatorAddr, candidateAddr sdk.Address) (bond Delegation, found bool) {
store := ctx.KVStore(k.storeKey)
delegatorBytes := store.Get(GetDelegatorBondKey(delegatorAddr, candidateAddr, k.cdc))
delegatorBytes := store.Get(GetDelegationKey(delegatorAddr, candidateAddr, k.cdc))
if delegatorBytes == nil {
return bond, false
}
@ -369,11 +370,11 @@ func (k Keeper) GetDelegatorBond(ctx sdk.Context,
}
// load all bonds
func (k Keeper) getBonds(ctx sdk.Context, maxRetrieve int16) (bonds []DelegatorBond) {
func (k Keeper) getBonds(ctx sdk.Context, maxRetrieve int16) (bonds []Delegation) {
store := ctx.KVStore(k.storeKey)
iterator := store.SubspaceIterator(DelegatorBondKeyPrefix)
iterator := store.SubspaceIterator(DelegationKeyPrefix)
bonds = make([]DelegatorBond, maxRetrieve)
bonds = make([]Delegation, maxRetrieve)
i := 0
for ; ; i++ {
if !iterator.Valid() || i > int(maxRetrieve-1) {
@ -381,7 +382,7 @@ func (k Keeper) getBonds(ctx sdk.Context, maxRetrieve int16) (bonds []DelegatorB
break
}
bondBytes := iterator.Value()
var bond DelegatorBond
var bond Delegation
k.cdc.MustUnmarshalBinary(bondBytes, &bond)
bonds[i] = bond
iterator.Next()
@ -390,12 +391,12 @@ func (k Keeper) getBonds(ctx sdk.Context, maxRetrieve int16) (bonds []DelegatorB
}
// load all bonds of a delegator
func (k Keeper) GetDelegatorBonds(ctx sdk.Context, delegator sdk.Address, maxRetrieve int16) (bonds []DelegatorBond) {
func (k Keeper) GetDelegations(ctx sdk.Context, delegator sdk.Address, maxRetrieve int16) (bonds []Delegation) {
store := ctx.KVStore(k.storeKey)
delegatorPrefixKey := GetDelegatorBondsKey(delegator, k.cdc)
delegatorPrefixKey := GetDelegationsKey(delegator, k.cdc)
iterator := store.SubspaceIterator(delegatorPrefixKey) //smallest to largest
bonds = make([]DelegatorBond, maxRetrieve)
bonds = make([]Delegation, maxRetrieve)
i := 0
for ; ; i++ {
if !iterator.Valid() || i > int(maxRetrieve-1) {
@ -403,7 +404,7 @@ func (k Keeper) GetDelegatorBonds(ctx sdk.Context, delegator sdk.Address, maxRet
break
}
bondBytes := iterator.Value()
var bond DelegatorBond
var bond Delegation
k.cdc.MustUnmarshalBinary(bondBytes, &bond)
bonds[i] = bond
iterator.Next()
@ -411,15 +412,15 @@ func (k Keeper) GetDelegatorBonds(ctx sdk.Context, delegator sdk.Address, maxRet
return bonds[:i] // trim
}
func (k Keeper) setDelegatorBond(ctx sdk.Context, bond DelegatorBond) {
func (k Keeper) setDelegation(ctx sdk.Context, bond Delegation) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshalBinary(bond)
store.Set(GetDelegatorBondKey(bond.DelegatorAddr, bond.CandidateAddr, k.cdc), b)
store.Set(GetDelegationKey(bond.DelegatorAddr, bond.CandidateAddr, k.cdc), b)
}
func (k Keeper) removeDelegatorBond(ctx sdk.Context, bond DelegatorBond) {
func (k Keeper) removeDelegation(ctx sdk.Context, bond Delegation) {
store := ctx.KVStore(k.storeKey)
store.Delete(GetDelegatorBondKey(bond.DelegatorAddr, bond.CandidateAddr, k.cdc))
store.Delete(GetDelegationKey(bond.DelegatorAddr, bond.CandidateAddr, k.cdc))
}
//_______________________________________________________________________
@ -501,15 +502,25 @@ func (k Keeper) setPool(ctx sdk.Context, p Pool) {
//__________________________________________________________________________
// Implements ValidatorSetKeeper
// Implements ValidatorSet
var _ sdk.ValidatorSetKeeper = Keeper{}
var _ sdk.ValidatorSet = Keeper{}
func (k Keeper) ValidatorSet(ctx sdk.Context) sdk.ValidatorSet {
vals := k.GetValidators(ctx)
return ValidatorSet(vals)
// iterate through the active validator set and perform the provided function
func (k Keeper) Iterate(fn func(index int64, validator sdk.Validator)) {
iterator := store.SubspaceIterator(CurrentValidatorsKey)
i := 0
for ; iterator.Valid(); iterator.Next() {
bz := iterator.Value()
var validator Validator
k.cdc.MustUnmarshalBinary(bz, &validator)
fn(i, validator) // XXX is this safe will the validator unexposed fields be able to get written to?
i++
}
iterator.Close()
}
// get the sdk.validator for a particular address
func (k Keeper) Validator(ctx sdk.Context, addr sdk.Address) sdk.Validator {
can, ok := k.GetCandidate(ctx, addr)
if !ok {
@ -521,20 +532,38 @@ func (k Keeper) Validator(ctx sdk.Context, addr sdk.Address) sdk.Validator {
return can.validator()
}
// total power from the bond
func (k Keeper) TotalPower(ctx sdk.Context) sdk.Rat {
pool := k.GetPool(ctx)
return pool.BondedShares
}
func (k Keeper) Delegation(ctx sdk.Context, del sdk.Address, val sdk.Address) sdk.Delegation {
bond, ok := k.GetDelegatorBond(ctx, del, val)
//__________________________________________________________________________
// Implements DelegationSet
var _ sdk.ValidatorSet = Keeper{}
// get the delegation for a particular set of delegator and validator addresses
func (k Keeper) Delegation(ctx sdk.Context, addrDel sdk.Address, addrVal sdk.Address) sdk.Delegation {
bond, ok := k.GetDelegation(ctx, addrDel, addrVal)
if !ok {
return nil
}
return bond
}
func (k Keeper) DelegationSet(ctx sdk.Context, del sdk.Address) sdk.DelegationSet {
bs := k.GetDelegatorBonds(ctx, del, 32767)
return DelegationSet(bs)
// iterate through the active validator set and perform the provided function
func (k Keeper) Iterate(delAddr sdk.Address, fn func(index int64, delegator sdk.Delegator)) {
key := GetDelegationsKey(delAddr, k.cdc)
iterator := store.SubspaceIterator(CurrentValidatorsKey)
i := 0
for ; iterator.Valid(); iterator.Next() {
bz := iterator.Value()
var delegation Delegation
k.cdc.MustUnmarshalBinary(bz, &delegation)
fn(i, delegator) // XXX is this safe will the fields be able to get written to?
i++
}
iterator.Close()
}

View File

@ -18,11 +18,10 @@ var (
CandidatesKey = []byte{0x02} // prefix for each key to a candidate
ValidatorsKey = []byte{0x03} // prefix for each key to a validator
AccUpdateValidatorsKey = []byte{0x04} // prefix for each key to a validator which is being updated
RecentValidatorsKey = []byte{0x05} // prefix for each key to the last updated validator group
CurrentValidatorsKey = []byte{0x05} // prefix for each key to the last updated validator group
ToKickOutValidatorsKey = []byte{0x06} // prefix for each key to the last updated validator group
DelegatorBondKeyPrefix = []byte{0x07} // prefix for each key to a delegator's bond
DelegationKeyPrefix = []byte{0x07} // prefix for each key to a delegator's bond
IntraTxCounterKey = []byte{0x08} // key for block-local tx index
PowerChangeKey = []byte{0x09} // prefix for power change object
)
const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch
@ -53,15 +52,10 @@ func GetAccUpdateValidatorKey(addr sdk.Address) []byte {
return append(AccUpdateValidatorsKey, addr.Bytes()...)
}
// get the key for the recent validator group, ordered like tendermint
func GetRecentValidatorKey(pk crypto.PubKey) []byte {
// get the key for the current validator group, ordered like tendermint
func GetCurrentValidatorsKey(pk crypto.PubKey) []byte {
addr := pk.Address()
return append(RecentValidatorsKey, addr.Bytes()...)
}
// remove the prefix byte from a key
func AddrFromKey(key []byte) sdk.Address {
return key[1:]
return append(CurrentValidatorsKey, addr.Bytes()...)
}
// get the key for the accumulated update validators
@ -70,22 +64,22 @@ func GetToKickOutValidatorKey(addr sdk.Address) []byte {
}
// get the key for delegator bond with candidate
func GetDelegatorBondKey(delegatorAddr, candidateAddr sdk.Address, cdc *wire.Codec) []byte {
return append(GetDelegatorBondsKey(delegatorAddr, cdc), candidateAddr.Bytes()...)
func GetDelegationKey(delegatorAddr, candidateAddr sdk.Address, cdc *wire.Codec) []byte {
return append(GetDelegationsKey(delegatorAddr, cdc), candidateAddr.Bytes()...)
}
// get the prefix for a delegator for all candidates
func GetDelegatorBondsKey(delegatorAddr sdk.Address, cdc *wire.Codec) []byte {
func GetDelegationsKey(delegatorAddr sdk.Address, cdc *wire.Codec) []byte {
res, err := cdc.MarshalBinary(&delegatorAddr)
if err != nil {
panic(err)
}
return append(DelegatorBondKeyPrefix, res...)
return append(DelegationKeyPrefix, res...)
}
// get the key for the accumulated update validators
func GetPowerChangeKey(height int64) []byte {
heightBytes := make([]byte, binary.MaxVarintLen64)
binary.BigEndian.PutUint64(heightBytes, ^uint64(height)) // invert height (older validators first)
return append(PowerChangeKey, heightBytes...)
//______________________________________________________________
// remove the prefix byte from a key, possibly revealing and address
func AddrFromKey(key []byte) sdk.Address {
return key[1:]
}

View File

@ -81,7 +81,7 @@ func TestCandidate(t *testing.T) {
assert.False(t, found)
}
// tests GetDelegatorBond, GetDelegatorBonds, SetDelegatorBond, removeDelegatorBond, GetBonds
// tests GetDelegation, GetDelegations, SetDelegation, removeDelegation, GetBonds
func TestBond(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, 0)
@ -97,54 +97,54 @@ func TestBond(t *testing.T) {
// first add a candidates[0] to delegate too
keeper.setCandidate(ctx, candidates[0])
bond1to1 := DelegatorBond{
bond1to1 := Delegation{
DelegatorAddr: addrDels[0],
CandidateAddr: addrVals[0],
Shares: sdk.NewRat(9),
}
// check the empty keeper first
_, found := keeper.GetDelegatorBond(ctx, addrDels[0], addrVals[0])
_, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
assert.False(t, found)
// set and retrieve a record
keeper.setDelegatorBond(ctx, bond1to1)
resBond, found := keeper.GetDelegatorBond(ctx, addrDels[0], addrVals[0])
keeper.setDelegation(ctx, bond1to1)
resBond, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
assert.True(t, found)
assert.True(t, bond1to1.equal(resBond))
// modify a records, save, and retrieve
bond1to1.Shares = sdk.NewRat(99)
keeper.setDelegatorBond(ctx, bond1to1)
resBond, found = keeper.GetDelegatorBond(ctx, addrDels[0], addrVals[0])
keeper.setDelegation(ctx, bond1to1)
resBond, found = keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
assert.True(t, found)
assert.True(t, bond1to1.equal(resBond))
// add some more records
keeper.setCandidate(ctx, candidates[1])
keeper.setCandidate(ctx, candidates[2])
bond1to2 := DelegatorBond{addrDels[0], addrVals[1], sdk.NewRat(9), 0}
bond1to3 := DelegatorBond{addrDels[0], addrVals[2], sdk.NewRat(9), 1}
bond2to1 := DelegatorBond{addrDels[1], addrVals[0], sdk.NewRat(9), 2}
bond2to2 := DelegatorBond{addrDels[1], addrVals[1], sdk.NewRat(9), 3}
bond2to3 := DelegatorBond{addrDels[1], addrVals[2], sdk.NewRat(9), 4}
keeper.setDelegatorBond(ctx, bond1to2)
keeper.setDelegatorBond(ctx, bond1to3)
keeper.setDelegatorBond(ctx, bond2to1)
keeper.setDelegatorBond(ctx, bond2to2)
keeper.setDelegatorBond(ctx, bond2to3)
bond1to2 := Delegation{addrDels[0], addrVals[1], sdk.NewRat(9), 0}
bond1to3 := Delegation{addrDels[0], addrVals[2], sdk.NewRat(9), 1}
bond2to1 := Delegation{addrDels[1], addrVals[0], sdk.NewRat(9), 2}
bond2to2 := Delegation{addrDels[1], addrVals[1], sdk.NewRat(9), 3}
bond2to3 := Delegation{addrDels[1], addrVals[2], sdk.NewRat(9), 4}
keeper.setDelegation(ctx, bond1to2)
keeper.setDelegation(ctx, bond1to3)
keeper.setDelegation(ctx, bond2to1)
keeper.setDelegation(ctx, bond2to2)
keeper.setDelegation(ctx, bond2to3)
// test all bond retrieve capabilities
resBonds := keeper.GetDelegatorBonds(ctx, addrDels[0], 5)
resBonds := keeper.GetDelegations(ctx, addrDels[0], 5)
require.Equal(t, 3, len(resBonds))
assert.True(t, bond1to1.equal(resBonds[0]))
assert.True(t, bond1to2.equal(resBonds[1]))
assert.True(t, bond1to3.equal(resBonds[2]))
resBonds = keeper.GetDelegatorBonds(ctx, addrDels[0], 3)
resBonds = keeper.GetDelegations(ctx, addrDels[0], 3)
require.Equal(t, 3, len(resBonds))
resBonds = keeper.GetDelegatorBonds(ctx, addrDels[0], 2)
resBonds = keeper.GetDelegations(ctx, addrDels[0], 2)
require.Equal(t, 2, len(resBonds))
resBonds = keeper.GetDelegatorBonds(ctx, addrDels[1], 5)
resBonds = keeper.GetDelegations(ctx, addrDels[1], 5)
require.Equal(t, 3, len(resBonds))
assert.True(t, bond2to1.equal(resBonds[0]))
assert.True(t, bond2to2.equal(resBonds[1]))
@ -159,22 +159,22 @@ func TestBond(t *testing.T) {
assert.True(t, bond2to3.equal(allBonds[5]))
// delete a record
keeper.removeDelegatorBond(ctx, bond2to3)
_, found = keeper.GetDelegatorBond(ctx, addrDels[1], addrVals[2])
keeper.removeDelegation(ctx, bond2to3)
_, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[2])
assert.False(t, found)
resBonds = keeper.GetDelegatorBonds(ctx, addrDels[1], 5)
resBonds = keeper.GetDelegations(ctx, addrDels[1], 5)
require.Equal(t, 2, len(resBonds))
assert.True(t, bond2to1.equal(resBonds[0]))
assert.True(t, bond2to2.equal(resBonds[1]))
// delete all the records from delegator 2
keeper.removeDelegatorBond(ctx, bond2to1)
keeper.removeDelegatorBond(ctx, bond2to2)
_, found = keeper.GetDelegatorBond(ctx, addrDels[1], addrVals[0])
keeper.removeDelegation(ctx, bond2to1)
keeper.removeDelegation(ctx, bond2to2)
_, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[0])
assert.False(t, found)
_, found = keeper.GetDelegatorBond(ctx, addrDels[1], addrVals[1])
_, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[1])
assert.False(t, found)
resBonds = keeper.GetDelegatorBonds(ctx, addrDels[1], 5)
resBonds = keeper.GetDelegations(ctx, addrDels[1], 5)
require.Equal(t, 0, len(resBonds))
}

View File

@ -11,13 +11,13 @@ import (
// GenesisState - all staking state that must be provided at genesis
type GenesisState struct {
Pool Pool `json:"pool"`
Params Params `json:"params"`
Candidates []Candidate `json:"candidates"`
Bonds []DelegatorBond `json:"bonds"`
Pool Pool `json:"pool"`
Params Params `json:"params"`
Candidates []Candidate `json:"candidates"`
Bonds []Delegation `json:"bonds"`
}
func NewGenesisState(pool Pool, params Params, candidates []Candidate, bonds []DelegatorBond) GenesisState {
func NewGenesisState(pool Pool, params Params, candidates []Candidate, bonds []Delegation) GenesisState {
return GenesisState{
Pool: pool,
Params: params,
@ -45,9 +45,6 @@ type Params struct {
MaxValidators uint16 `json:"max_validators"` // maximum number of validators
BondDenom string `json:"bond_denom"` // bondable coin denomination
FeeDenoms []string `json:"fee_denoms"` // accepted fee denoms
ReservePoolFee sdk.Rat `json:"reserve_pool_fee"` // percent of fees which go to reserve pool
}
func (p Params) equal(p2 Params) bool {
@ -56,8 +53,7 @@ func (p Params) equal(p2 Params) bool {
p.InflationMin.Equal(p2.InflationMin) &&
p.GoalBonded.Equal(p2.GoalBonded) &&
p.MaxValidators == p2.MaxValidators &&
p.BondDenom == p2.BondDenom &&
p.ReservePoolFee.Equal(p2.ReservePoolFee)
p.BondDenom == p2.BondDenom
}
func defaultParams() Params {
@ -68,8 +64,6 @@ func defaultParams() Params {
GoalBonded: sdk.NewRat(67, 100),
MaxValidators: 100,
BondDenom: "steak",
FeeDenoms: []string{"steak"},
ReservePoolFee: sdk.NewRat(5, 100),
}
}
@ -90,12 +84,7 @@ type Pool struct {
DateLastCommissionReset int64 `json:"date_last_commission_reset"` // unix timestamp for last commission accounting reset (daily)
// Fee Related
FeeReservePool sdk.Coins `json:"fee_reserve_pool"` // XXX reserve pool of collected fees for use by governance
FeePool sdk.Coins `json:"fee_pool"` // XXX fee pool for all the fee shares which have already been distributed
FeeSumReceived sdk.Coins `json:"fee_sum_received"` // XXX sum of all fees received, post reserve pool `json:"fee_sum_received"`
FeeRecent sdk.Coins `json:"fee_recent"` // XXX most recent fee collected
FeeAdjustments []sdk.Rat `json:"fee_adjustments"` // XXX Adjustment factors for lazy fee accounting, couples with Params.BondDenoms
PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // XXX last recorded bonded shares
PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // last recorded bonded shares - for fee calcualtions
}
func (p Pool) equal(p2 Pool) bool {
@ -107,11 +96,6 @@ func (p Pool) equal(p2 Pool) bool {
p.InflationLastTime == p2.InflationLastTime &&
p.Inflation.Equal(p2.Inflation) &&
p.DateLastCommissionReset == p2.DateLastCommissionReset &&
p.FeeReservePool.IsEqual(p2.FeeReservePool) &&
p.FeePool.IsEqual(p2.FeePool) &&
p.FeeSumReceived.IsEqual(p2.FeeSumReceived) &&
p.FeeRecent.IsEqual(p2.FeeRecent) &&
sdk.RatsEqual(p.FeeAdjustments, p2.FeeAdjustments) &&
p.PrevBondedShares.Equal(p2.PrevBondedShares)
}
@ -128,42 +112,16 @@ func initialPool() Pool {
InflationLastTime: 0,
Inflation: sdk.NewRat(7, 100),
DateLastCommissionReset: 0,
FeeReservePool: sdk.Coins(nil),
FeePool: sdk.Coins(nil),
FeeSumReceived: sdk.Coins(nil),
FeeRecent: sdk.Coins(nil),
FeeAdjustments: []sdk.Rat{sdk.ZeroRat()},
PrevBondedShares: sdk.ZeroRat(),
}
}
//_________________________________________________________________________
// Used in calculation of fee shares, added to a queue for each block where a power change occures
type PowerChange struct {
Height int64 `json:"height"` // block height at change
Power sdk.Rat `json:"power"` // total power at change
PrevPower sdk.Rat `json:"prev_power"` // total power at previous height-1
FeesIn sdk.Coins `json:"fees_in"` // fees in at block height
PrevFeePool sdk.Coins `json:"prev_fee_pool"` // total fees in at previous block height
}
//_________________________________________________________________________
// CandidateStatus - status of a validator-candidate
type CandidateStatus byte
const (
// nolint
Bonded CandidateStatus = 0x00
Unbonded CandidateStatus = 0x01
Revoked CandidateStatus = 0x02
)
// Candidate defines the total amount of bond shares and their exchange rate to
// coins. Accumulation of interest is modelled as an in increase in the
// exchange rate, and slashing as a decrease. When coins are delegated to this
// candidate, the candidate is credited with a DelegatorBond whose number of
// candidate, the candidate is credited with a Delegation whose number of
// bond shares is based on the amount of coins delegated divided by the current
// exchange rate. Voting power can be calculated as total bonds multiplied by
// exchange rate.
@ -187,8 +145,7 @@ type Candidate struct {
CommissionChangeToday sdk.Rat `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time)
// fee related
FeeAdjustments []sdk.Rat `json:"fee_adjustments"` // XXX Adjustment factors for lazy fee accounting, couples with Params.BondDenoms
PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // total shares of a global hold pools
PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // total shares of a global hold pools
}
// Candidates - list of Candidates
@ -197,21 +154,21 @@ type Candidates []Candidate
// NewCandidate - initialize a new candidate
func NewCandidate(address sdk.Address, pubKey crypto.PubKey, description Description) Candidate {
return Candidate{
Status: Unbonded,
Address: address,
PubKey: pubKey,
BondedShares: sdk.ZeroRat(),
DelegatorShares: sdk.ZeroRat(),
Description: description,
ValidatorBondHeight: int64(0),
ValidatorBondCounter: int16(0),
ProposerRewardPool: sdk.Coins{},
Commission: sdk.ZeroRat(),
CommissionMax: sdk.ZeroRat(),
CommissionChangeRate: sdk.ZeroRat(),
CommissionChangeToday: sdk.ZeroRat(),
FeeAdjustments: []sdk.Rat(nil),
PrevBondedShares: sdk.ZeroRat(),
Status: Unbonded,
Address: address,
PubKey: pubKey,
BondedShares: sdk.ZeroRat(),
DelegatorShares: sdk.ZeroRat(),
Description: description,
ValidatorBondHeight: int64(0),
ValidatorBondIntraTxCounter: int16(0),
ProposerRewardPool: sdk.Coins{},
Commission: sdk.ZeroRat(),
CommissionMax: sdk.ZeroRat(),
CommissionChangeRate: sdk.ZeroRat(),
CommissionChangeToday: sdk.ZeroRat(),
FeeAdjustments: []sdk.Rat(nil),
PrevBondedShares: sdk.ZeroRat(),
}
}
@ -258,39 +215,6 @@ func (c Candidate) delegatorShareExRate() sdk.Rat {
return c.BondedShares.Quo(c.DelegatorShares)
}
// Validator returns a copy of the Candidate as a Validator.
// Should only be called when the Candidate qualifies as a validator.
func (c Candidate) validator() Validator {
return Validator{
Address: c.Address,
PubKey: c.PubKey,
Power: c.BondedShares,
Height: c.ValidatorBondHeight,
Counter: c.ValidatorBondCounter,
}
}
//XXX updateDescription function
//XXX enforce limit to number of description characters
//______________________________________________________________________
// Validator is one of the top Candidates
type Validator struct {
Address sdk.Address `json:"address"`
PubKey crypto.PubKey `json:"pub_key"`
Power sdk.Rat `json:"power"`
Height int64 `json:"height"` // Earliest height as a validator
Counter int16 `json:"counter"` // Block-local tx index for resolving equal voting power & height
}
// verify equal not including height or counter
func (v Validator) equal(v2 Validator) bool {
return bytes.Equal(v.Address, v2.Address) &&
v.PubKey.Equals(v2.PubKey) &&
v.Power.Equal(v2.Power)
}
// abci validator from stake validator type
func (v Validator) abciValidator(cdc *wire.Codec) abci.Validator {
return abci.Validator{
@ -308,74 +232,43 @@ func (v Validator) abciValidatorZero(cdc *wire.Codec) abci.Validator {
}
}
//XXX updateDescription function
//XXX enforce limit to number of description characters
//______________________________________________________________________
// ensure fulfills the sdk validator types
var _ sdk.Validator = Validator{}
func (v Validator) GetAddress() sdk.Address {
return v.Address
}
func (v Validator) GetPubKey() crypto.PubKey {
return v.PubKey
}
func (v Validator) GetPower() sdk.Rat {
return v.Power
}
type ValidatorSet []Validator
var _ sdk.ValidatorSet = ValidatorSet{}
func (vs ValidatorSet) Iterate(fn func(int, sdk.Validator)) {
for i, v := range vs {
fn(i, v)
}
}
func (vs ValidatorSet) Size() int {
return len(vs)
}
// nolint - for sdk.Validator
func (v Validator) GetAddress() sdk.Address { return v.Address }
func (v Validator) GetPubKey() crypto.PubKey { return v.PubKey }
func (v Validator) GetPower() sdk.Rat { return v.Power }
//_________________________________________________________________________
// DelegatorBond represents the bond with tokens held by an account. It is
// Delegation represents the bond with tokens held by an account. It is
// owned by one delegator, and is associated with the voting power of one
// pubKey.
// TODO better way of managing space
type DelegatorBond struct {
type Delegation struct {
DelegatorAddr sdk.Address `json:"delegator_addr"`
CandidateAddr sdk.Address `json:"candidate_addr"`
Shares sdk.Rat `json:"shares"`
Height int64 `json:"height"` // Last height bond updated
}
func (b DelegatorBond) equal(b2 DelegatorBond) bool {
func (b Delegation) equal(b2 Delegation) bool {
return bytes.Equal(b.DelegatorAddr, b2.DelegatorAddr) &&
bytes.Equal(b.CandidateAddr, b2.CandidateAddr) &&
b.Height == b2.Height &&
b.Shares.Equal(b2.Shares)
}
func (b DelegatorBond) GetDelegator() sdk.Address {
return b.DelegatorAddr
}
// ensure fulfills the sdk validator types
var _ sdk.Delegation = Delegation{}
func (b DelegatorBond) GetValidator() sdk.Address {
return b.CandidateAddr
}
func (b DelegatorBond) GetBondAmount() sdk.Rat {
return b.Shares
}
type DelegationSet []DelegatorBond
func (ds DelegationSet) Iterate(fn func(int, sdk.Delegation)) {
for i, d := range ds {
fn(i, d)
}
}
func (ds DelegationSet) Size() int {
return len(ds)
}
// nolint - for sdk.Delegation
func (b Delegation) GetDelegator() sdk.Address { return b.DelegatorAddr }
func (b Delegation) GetValidator() sdk.Address { return b.CandidateAddr }
func (b Delegation) GetBondAmount() sdk.Rat { return b.Shares }

View File

@ -17,13 +17,13 @@ func NewViewSlashKeeper(k Keeper) ViewSlashKeeper {
}
// load a delegator bond
func (v ViewSlashKeeper) GetDelegatorBond(ctx sdk.Context,
delegatorAddr, candidateAddr sdk.Address) (bond DelegatorBond, found bool) {
return v.keeper.GetDelegatorBond(ctx, delegatorAddr, candidateAddr)
func (v ViewSlashKeeper) GetDelegation(ctx sdk.Context,
delegatorAddr, candidateAddr sdk.Address) (bond Delegation, found bool) {
return v.keeper.GetDelegation(ctx, delegatorAddr, candidateAddr)
}
// load n delegator bonds
func (v ViewSlashKeeper) GetDelegatorBonds(ctx sdk.Context,
delegator sdk.Address, maxRetrieve int16) (bonds []DelegatorBond) {
return v.keeper.GetDelegatorBonds(ctx, delegator, maxRetrieve)
func (v ViewSlashKeeper) GetDelegations(ctx sdk.Context,
delegator sdk.Address, maxRetrieve int16) (bonds []Delegation) {
return v.keeper.GetDelegations(ctx, delegator, maxRetrieve)
}

View File

@ -9,7 +9,7 @@ import (
"github.com/stretchr/testify/require"
)
// tests GetDelegatorBond, GetDelegatorBonds
// tests GetDelegation, GetDelegations
func TestViewSlashBond(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, 0)
@ -28,7 +28,7 @@ func TestViewSlashBond(t *testing.T) {
// first add a candidates[0] to delegate too
keeper.setCandidate(ctx, candidates[0])
bond1to1 := DelegatorBond{
bond1to1 := Delegation{
DelegatorAddr: addrDels[0],
CandidateAddr: addrVals[0],
Shares: sdk.NewRat(9),
@ -37,47 +37,47 @@ func TestViewSlashBond(t *testing.T) {
viewSlashKeeper := NewViewSlashKeeper(keeper)
// check the empty keeper first
_, found := viewSlashKeeper.GetDelegatorBond(ctx, addrDels[0], addrVals[0])
_, found := viewSlashKeeper.GetDelegation(ctx, addrDels[0], addrVals[0])
assert.False(t, found)
// set and retrieve a record
keeper.setDelegatorBond(ctx, bond1to1)
resBond, found := viewSlashKeeper.GetDelegatorBond(ctx, addrDels[0], addrVals[0])
keeper.setDelegation(ctx, bond1to1)
resBond, found := viewSlashKeeper.GetDelegation(ctx, addrDels[0], addrVals[0])
assert.True(t, found)
assert.True(t, bond1to1.equal(resBond))
// modify a records, save, and retrieve
bond1to1.Shares = sdk.NewRat(99)
keeper.setDelegatorBond(ctx, bond1to1)
resBond, found = viewSlashKeeper.GetDelegatorBond(ctx, addrDels[0], addrVals[0])
keeper.setDelegation(ctx, bond1to1)
resBond, found = viewSlashKeeper.GetDelegation(ctx, addrDels[0], addrVals[0])
assert.True(t, found)
assert.True(t, bond1to1.equal(resBond))
// add some more records
keeper.setCandidate(ctx, candidates[1])
keeper.setCandidate(ctx, candidates[2])
bond1to2 := DelegatorBond{addrDels[0], addrVals[1], sdk.NewRat(9), 0}
bond1to3 := DelegatorBond{addrDels[0], addrVals[2], sdk.NewRat(9), 1}
bond2to1 := DelegatorBond{addrDels[1], addrVals[0], sdk.NewRat(9), 2}
bond2to2 := DelegatorBond{addrDels[1], addrVals[1], sdk.NewRat(9), 3}
bond2to3 := DelegatorBond{addrDels[1], addrVals[2], sdk.NewRat(9), 4}
keeper.setDelegatorBond(ctx, bond1to2)
keeper.setDelegatorBond(ctx, bond1to3)
keeper.setDelegatorBond(ctx, bond2to1)
keeper.setDelegatorBond(ctx, bond2to2)
keeper.setDelegatorBond(ctx, bond2to3)
bond1to2 := Delegation{addrDels[0], addrVals[1], sdk.NewRat(9), 0}
bond1to3 := Delegation{addrDels[0], addrVals[2], sdk.NewRat(9), 1}
bond2to1 := Delegation{addrDels[1], addrVals[0], sdk.NewRat(9), 2}
bond2to2 := Delegation{addrDels[1], addrVals[1], sdk.NewRat(9), 3}
bond2to3 := Delegation{addrDels[1], addrVals[2], sdk.NewRat(9), 4}
keeper.setDelegation(ctx, bond1to2)
keeper.setDelegation(ctx, bond1to3)
keeper.setDelegation(ctx, bond2to1)
keeper.setDelegation(ctx, bond2to2)
keeper.setDelegation(ctx, bond2to3)
// test all bond retrieve capabilities
resBonds := viewSlashKeeper.GetDelegatorBonds(ctx, addrDels[0], 5)
resBonds := viewSlashKeeper.GetDelegations(ctx, addrDels[0], 5)
require.Equal(t, 3, len(resBonds))
assert.True(t, bond1to1.equal(resBonds[0]))
assert.True(t, bond1to2.equal(resBonds[1]))
assert.True(t, bond1to3.equal(resBonds[2]))
resBonds = viewSlashKeeper.GetDelegatorBonds(ctx, addrDels[0], 3)
resBonds = viewSlashKeeper.GetDelegations(ctx, addrDels[0], 3)
require.Equal(t, 3, len(resBonds))
resBonds = viewSlashKeeper.GetDelegatorBonds(ctx, addrDels[0], 2)
resBonds = viewSlashKeeper.GetDelegations(ctx, addrDels[0], 2)
require.Equal(t, 2, len(resBonds))
resBonds = viewSlashKeeper.GetDelegatorBonds(ctx, addrDels[1], 5)
resBonds = viewSlashKeeper.GetDelegations(ctx, addrDels[1], 5)
require.Equal(t, 3, len(resBonds))
assert.True(t, bond2to1.equal(resBonds[0]))
assert.True(t, bond2to2.equal(resBonds[1]))