working fee distribution reorg
This commit is contained in:
parent
22e9fc276d
commit
fa64487e65
@ -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)
|
||||
}
|
||||
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
624
docs/spec/staking/spec-technical.md
Normal file
624
docs/spec/staking/spec-technical.md
Normal 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
57
types/stake.go
Normal 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))
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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
113
x/fee_distribution/types.go
Normal 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...)
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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:]
|
||||
}
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
|
||||
193
x/stake/types.go
193
x/stake/types.go
@ -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 }
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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]))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user