Merge branch 'develop' into cwgoes/check-supply-in-simulation

This commit is contained in:
Christopher Goes 2018-10-16 23:32:49 +02:00
commit 67258c4c29
81 changed files with 2819 additions and 802 deletions

View File

@ -70,8 +70,11 @@ BREAKING CHANGES
* [x/staking] \#2244 staking now holds a consensus-address-index instead of a consensus-pubkey-index
* [x/staking] \#2236 more distribution hooks for distribution
* [x/stake] \#2394 Split up UpdateValidator into distinct state transitions applied only in EndBlock
* [x/slashing] \#2480 Fix signing info handling bugs & faulty slashing
* [x/stake] \#2412 Added an unbonding validator queue to EndBlock to automatically update validator.Status when finished Unbonding
* [x/stake] \#2500 Block conflicting redelegations until we add an index
* [x/params] Global Paramstore refactored
* [x/stake] \#2508 Utilize Tendermint power for validator power key
* Tendermint
* Update tendermint version from v0.23.0 to v0.25.0, notable changes

View File

@ -43,7 +43,7 @@ func generateSelfSignedCert(host string) (certBytes []byte, priv *ecdsa.PrivateK
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
IsCA: true,
IsCA: true,
}
hosts := strings.Split(host, ",")
for _, h := range hosts {

View File

@ -713,7 +713,7 @@ func TestUnjail(t *testing.T) {
tests.WaitForHeight(4, port)
require.Equal(t, true, signingInfo.IndexOffset > 0)
require.Equal(t, time.Unix(0, 0).UTC(), signingInfo.JailedUntil)
require.Equal(t, true, signingInfo.SignedBlocksCounter > 0)
require.Equal(t, true, signingInfo.MissedBlocksCounter == 0)
}
func TestProposalsQuery(t *testing.T) {

View File

@ -16,6 +16,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
distr "github.com/cosmos/cosmos-sdk/x/distribution"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/slashing"
@ -43,6 +44,8 @@ type GaiaApp struct {
keyStake *sdk.KVStoreKey
tkeyStake *sdk.TransientStoreKey
keySlashing *sdk.KVStoreKey
keyDistr *sdk.KVStoreKey
tkeyDistr *sdk.TransientStoreKey
keyGov *sdk.KVStoreKey
keyFeeCollection *sdk.KVStoreKey
keyParams *sdk.KVStoreKey
@ -54,6 +57,7 @@ type GaiaApp struct {
bankKeeper bank.Keeper
stakeKeeper stake.Keeper
slashingKeeper slashing.Keeper
distrKeeper distr.Keeper
govKeeper gov.Keeper
paramsKeeper params.Keeper
}
@ -72,6 +76,8 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
keyAccount: sdk.NewKVStoreKey("acc"),
keyStake: sdk.NewKVStoreKey("stake"),
tkeyStake: sdk.NewTransientStoreKey("transient_stake"),
keyDistr: sdk.NewKVStoreKey("distr"),
tkeyDistr: sdk.NewTransientStoreKey("transient_distr"),
keySlashing: sdk.NewKVStoreKey("slashing"),
keyGov: sdk.NewKVStoreKey("gov"),
keyFeeCollection: sdk.NewKVStoreKey("fee"),
@ -88,30 +94,33 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
// add handlers
app.bankKeeper = bank.NewBaseKeeper(app.accountMapper)
app.feeCollectionKeeper = auth.NewFeeCollectionKeeper(
app.cdc,
app.keyFeeCollection,
)
app.paramsKeeper = params.NewKeeper(
app.cdc,
app.keyParams, app.tkeyParams,
)
app.stakeKeeper = stake.NewKeeper(
app.cdc,
app.keyStake, app.tkeyStake,
app.bankKeeper, app.paramsKeeper.Subspace(stake.DefaultParamspace),
app.RegisterCodespace(stake.DefaultCodespace),
)
app.distrKeeper = distr.NewKeeper(
app.cdc,
app.keyDistr,
app.paramsKeeper.Subspace(distr.DefaultParamspace),
app.bankKeeper, app.stakeKeeper, app.feeCollectionKeeper,
app.RegisterCodespace(stake.DefaultCodespace),
)
app.slashingKeeper = slashing.NewKeeper(
app.cdc,
app.keySlashing,
app.stakeKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace),
app.RegisterCodespace(slashing.DefaultCodespace),
)
app.stakeKeeper = app.stakeKeeper.WithHooks(
app.slashingKeeper.Hooks(),
)
app.govKeeper = gov.NewKeeper(
app.cdc,
app.keyGov,
@ -119,15 +128,15 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
app.RegisterCodespace(gov.DefaultCodespace),
)
app.feeCollectionKeeper = auth.NewFeeCollectionKeeper(
app.cdc,
app.keyFeeCollection,
)
// register the staking hooks
app.stakeKeeper = app.stakeKeeper.WithHooks(
NewHooks(app.distrKeeper.Hooks(), app.slashingKeeper.Hooks()))
// register message routes
app.Router().
AddRoute("bank", bank.NewHandler(app.bankKeeper)).
AddRoute("stake", stake.NewHandler(app.stakeKeeper)).
AddRoute("distr", distr.NewHandler(app.distrKeeper)).
AddRoute("slashing", slashing.NewHandler(app.slashingKeeper)).
AddRoute("gov", gov.NewHandler(app.govKeeper))
@ -138,11 +147,12 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
// initialize BaseApp
app.SetInitChainer(app.initChainer)
app.SetBeginBlocker(app.BeginBlocker)
app.SetEndBlocker(app.EndBlocker)
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper))
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyStake,
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyStake, app.keyDistr,
app.keySlashing, app.keyGov, app.keyFeeCollection, app.keyParams)
app.MountStoresTransient(app.tkeyParams, app.tkeyStake)
app.MountStoresTransient(app.tkeyParams, app.tkeyStake, app.tkeyDistr)
app.SetEndBlocker(app.EndBlocker)
err := app.LoadLatestVersion(app.keyMain)
if err != nil {
cmn.Exit(err.Error())
@ -156,6 +166,7 @@ func MakeCodec() *codec.Codec {
var cdc = codec.New()
bank.RegisterCodec(cdc)
stake.RegisterCodec(cdc)
distr.RegisterCodec(cdc)
slashing.RegisterCodec(cdc)
gov.RegisterCodec(cdc)
auth.RegisterCodec(cdc)
@ -168,6 +179,9 @@ func MakeCodec() *codec.Codec {
func (app *GaiaApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
tags := slashing.BeginBlocker(ctx, req, app.slashingKeeper)
// distribute rewards from previous block
distr.BeginBlocker(ctx, req, app.distrKeeper)
return abci.ResponseBeginBlock{
Tags: tags.ToKVPairs(),
}
@ -176,10 +190,13 @@ func (app *GaiaApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) ab
// application updates every end block
// nolint: unparam
func (app *GaiaApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
tags := gov.EndBlocker(ctx, app.govKeeper)
validatorUpdates := stake.EndBlocker(ctx, app.stakeKeeper)
// Add these new validators to the addr -> pubkey map.
app.slashingKeeper.AddValidators(ctx, validatorUpdates)
return abci.ResponseEndBlock{
ValidatorUpdates: validatorUpdates,
Tags: tags,
@ -208,18 +225,17 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci
// load the initial stake information
validators, err := stake.InitGenesis(ctx, app.stakeKeeper, genesisState.StakeData)
if err != nil {
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
// return sdk.ErrGenesisParse("").TraceCause(err, "")
panic(err) // TODO find a way to do this w/o panics
}
// load the address to pubkey map
slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.SlashingData, genesisState.StakeData)
gov.InitGenesis(ctx, app.govKeeper, genesisState.GovData)
distr.InitGenesis(ctx, app.distrKeeper, genesisState.DistrData)
err = GaiaValidateGenesisState(genesisState)
if err != nil {
// TODO find a way to do this w/o panics
panic(err)
panic(err) // TODO find a way to do this w/o panics
}
return abci.ResponseInitChain{
@ -243,6 +259,7 @@ func (app *GaiaApp) ExportAppStateAndValidators() (appState json.RawMessage, val
genState := GenesisState{
Accounts: accounts,
StakeData: stake.WriteGenesis(ctx, app.stakeKeeper),
DistrData: distr.WriteGenesis(ctx, app.distrKeeper),
GovData: gov.WriteGenesis(ctx, app.govKeeper),
}
appState, err = codec.MarshalJSONIndent(app.cdc, genState)
@ -252,3 +269,43 @@ func (app *GaiaApp) ExportAppStateAndValidators() (appState json.RawMessage, val
validators = stake.WriteValidators(ctx, app.stakeKeeper)
return appState, validators, nil
}
//______________________________________________________________________________________________
// Combined Staking Hooks
type Hooks struct {
dh distr.Hooks
sh slashing.Hooks
}
func NewHooks(dh distr.Hooks, sh slashing.Hooks) Hooks {
return Hooks{dh, sh}
}
var _ sdk.StakingHooks = Hooks{}
// nolint
func (h Hooks) OnValidatorCreated(ctx sdk.Context, addr sdk.ValAddress) {
h.dh.OnValidatorCreated(ctx, addr)
}
func (h Hooks) OnValidatorCommissionChange(ctx sdk.Context, addr sdk.ValAddress) {
h.dh.OnValidatorCommissionChange(ctx, addr)
}
func (h Hooks) OnValidatorRemoved(ctx sdk.Context, addr sdk.ValAddress) {
h.dh.OnValidatorRemoved(ctx, addr)
}
func (h Hooks) OnValidatorBonded(ctx sdk.Context, addr sdk.ConsAddress) {
h.sh.OnValidatorBonded(ctx, addr)
}
func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, addr sdk.ConsAddress) {
h.sh.OnValidatorBeginUnbonding(ctx, addr)
}
func (h Hooks) OnDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
h.dh.OnDelegationCreated(ctx, delAddr, valAddr)
}
func (h Hooks) OnDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
h.dh.OnDelegationSharesModified(ctx, delAddr, valAddr)
}
func (h Hooks) OnDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
h.dh.OnDelegationRemoved(ctx, delAddr, valAddr)
}

View File

@ -6,6 +6,7 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/x/auth"
distr "github.com/cosmos/cosmos-sdk/x/distribution"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/stretchr/testify/require"
@ -24,6 +25,7 @@ func setGenesis(gapp *GaiaApp, accs ...*auth.BaseAccount) error {
genesisState := GenesisState{
Accounts: genaccs,
StakeData: stake.DefaultGenesisState(),
DistrData: distr.DefaultGenesisState(),
SlashingData: slashing.DefaultGenesisState(),
}

View File

@ -11,6 +11,7 @@ import (
"github.com/cosmos/cosmos-sdk/server/config"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
distr "github.com/cosmos/cosmos-sdk/x/distribution"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/stake"
@ -34,6 +35,7 @@ var (
type GenesisState struct {
Accounts []GenesisAccount `json:"accounts"`
StakeData stake.GenesisState `json:"stake"`
DistrData distr.GenesisState `json:"distr"`
GovData gov.GenesisState `json:"gov"`
SlashingData slashing.GenesisState `json:"slashing"`
}
@ -196,6 +198,7 @@ func GaiaAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (genesisStat
genesisState = GenesisState{
Accounts: genaccs,
StakeData: stakeData,
DistrData: distr.DefaultGenesisState(),
GovData: gov.DefaultGenesisState(),
SlashingData: slashingData,
}

View File

@ -15,6 +15,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
banksim "github.com/cosmos/cosmos-sdk/x/bank/simulation"
distr "github.com/cosmos/cosmos-sdk/x/distribution"
"github.com/cosmos/cosmos-sdk/x/gov"
govsim "github.com/cosmos/cosmos-sdk/x/gov/simulation"
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
@ -60,13 +61,18 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage {
slashingGenesis := slashing.DefaultGenesisState()
var validators []stake.Validator
var delegations []stake.Delegation
// XXX Try different numbers of initially bonded validators
numInitiallyBonded := int64(50)
valAddrs := make([]sdk.ValAddress, numInitiallyBonded)
for i := 0; i < int(numInitiallyBonded); i++ {
validator := stake.NewValidator(sdk.ValAddress(accs[i].Address), accs[i].PubKey, stake.Description{})
valAddr := sdk.ValAddress(accs[i].Address)
valAddrs[i] = valAddr
validator := stake.NewValidator(valAddr, accs[i].PubKey, stake.Description{})
validator.Tokens = sdk.NewDec(100)
validator.DelegatorShares = sdk.NewDec(100)
delegation := stake.Delegation{accs[i].Address, sdk.ValAddress(accs[i].Address), sdk.NewDec(100), 0}
delegation := stake.Delegation{accs[i].Address, valAddr, sdk.NewDec(100), 0}
validators = append(validators, validator)
delegations = append(delegations, delegation)
}
@ -76,9 +82,11 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage {
// No inflation, for now
stakeGenesis.Params.InflationMax = sdk.NewDec(0)
stakeGenesis.Params.InflationMin = sdk.NewDec(0)
genesis := GenesisState{
Accounts: genesisAccounts,
StakeData: stakeGenesis,
DistrData: distr.DefaultGenesisWithValidators(valAddrs),
SlashingData: slashingGenesis,
GovData: govGenesis,
}

View File

@ -6,6 +6,7 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
distr "github.com/cosmos/cosmos-sdk/x/distribution"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/stake"
@ -72,6 +73,7 @@ func NewTestGaiaAppGenState(
return GenesisState{
Accounts: genAccs,
StakeData: stakeData,
DistrData: distr.DefaultGenesisState(),
SlashingData: slashing.DefaultGenesisState(),
GovData: gov.DefaultGenesisState(),
}, nil

View File

@ -1,7 +1,11 @@
package main
import (
"os"
"path"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tendermint/tendermint/libs/cli"
@ -10,18 +14,17 @@ import (
"github.com/cosmos/cosmos-sdk/client/lcd"
"github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/version"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli"
distrcmd "github.com/cosmos/cosmos-sdk/x/distribution/client/cli"
govcmd "github.com/cosmos/cosmos-sdk/x/gov/client/cli"
slashingcmd "github.com/cosmos/cosmos-sdk/x/slashing/client/cli"
stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli"
_ "github.com/cosmos/cosmos-sdk/client/lcd/statik"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/spf13/viper"
"os"
"path"
)
const (
@ -101,11 +104,13 @@ func main() {
stakecmd.GetCmdCreateValidator(cdc),
stakecmd.GetCmdEditValidator(cdc),
stakecmd.GetCmdDelegate(cdc),
govcmd.GetCmdDeposit(cdc),
stakecmd.GetCmdRedelegate(storeStake, cdc),
stakecmd.GetCmdUnbond(storeStake, cdc),
distrcmd.GetCmdWithdrawRewards(cdc),
distrcmd.GetCmdSetWithdrawAddr(cdc),
govcmd.GetCmdDeposit(cdc),
bankcmd.SendTxCmd(cdc),
govcmd.GetCmdSubmitProposal(cdc),
stakecmd.GetCmdUnbond(storeStake, cdc),
slashingcmd.GetCmdUnjail(cdc),
govcmd.GetCmdVote(cdc),
)...)

View File

@ -15,7 +15,7 @@ pool which validator holds individually
(`ValidatorDistribution.ProvisionsRewardPool`).
```
func AllocateFees(feesCollected sdk.Coins, global Global, proposer ValidatorDistribution,
func AllocateFees(feesCollected sdk.Coins, feePool FeePool, proposer ValidatorDistribution,
sumPowerPrecommitValidators, totalBondedTokens, communityTax,
proposerCommissionRate sdk.Dec)
@ -28,13 +28,11 @@ func AllocateFees(feesCollected sdk.Coins, global Global, proposer ValidatorDist
proposer.Pool += proposerReward - commission
communityFunding = feesCollectedDec * communityTax
global.CommunityFund += communityFunding
feePool.CommunityFund += communityFunding
poolReceived = feesCollectedDec - proposerReward - communityFunding
global.Pool += poolReceived
global.EverReceivedPool += poolReceived
global.LastReceivedPool = poolReceived
feePool.Pool += poolReceived
SetValidatorDistribution(proposer)
SetGlobal(global)
SetFeePool(feePool)
```

View File

@ -6,7 +6,7 @@
The pool of a new delegator bond will be 0 for the height at which the bond was
added, or the withdrawal has taken place. This is achieved by setting
`DelegatorDistInfo.WithdrawalHeight` to the height of the triggering transaction.
`DelegationDistInfo.WithdrawalHeight` to the height of the triggering transaction.
## Commission rate change

View File

@ -40,7 +40,7 @@ to independently and lazily withdraw their rewards.
As a part of the lazy computations, each delegator holds an accumulation term
specific to each validator which is used to estimate what their approximate
fair portion of tokens held in the global pool is owed to them.
fair portion of tokens held in the global fee pool is owed to them.
```
entitlement = delegator-accumulation / all-delegators-accumulation

View File

@ -1,9 +1,9 @@
## State
### Global
### FeePool
All globally tracked parameters for distribution are stored within
`Global`. Rewards are collected and added to the reward pool and
`FeePool`. Rewards are collected and added to the reward pool and
distributed to validators/delegators from here.
Note that the reward pool holds decimal coins (`DecCoins`) to allow
@ -11,7 +11,7 @@ for fractions of coins to be received from operations like inflation.
When coins are distributed from the pool they are truncated back to
`sdk.Coins` which are non-decimal.
- Global: `0x00 -> amino(global)`
- FeePool: `0x00 -> amino(FeePool)`
```golang
// coins with decimal
@ -22,7 +22,7 @@ type DecCoin struct {
Denom string
}
type Global struct {
type FeePool struct {
TotalValAccumUpdateHeight int64 // last height which the total validator accum was updated
TotalValAccum sdk.Dec // total valdator accum held by validators
Pool DecCoins // funds for all validators which have yet to be withdrawn
@ -42,7 +42,7 @@ Validator distribution information for the relevant validator is updated each ti
```golang
type ValidatorDistInfo struct {
GlobalWithdrawalHeight int64 // last height this validator withdrew from the global pool
FeePoolWithdrawalHeight int64 // last height this validator withdrew from the global fee pool
Pool DecCoins // rewards owed to delegators, commission has already been charged (includes proposer reward)
PoolCommission DecCoins // commission collected by this validator (pending withdrawal)
@ -59,10 +59,10 @@ properties change (aka bonded tokens etc.) its properties will remain constant
and the delegator's _accumulation_ factor can be calculated passively knowing
only the height of the last withdrawal and its current properties.
- DelegatorDistInfo: ` 0x02 | DelegatorAddr | ValOperatorAddr -> amino(delegatorDist)`
- DelegationDistInfo: ` 0x02 | DelegatorAddr | ValOperatorAddr -> amino(delegatorDist)`
```golang
type DelegatorDistInfo struct {
type DelegationDistInfo struct {
WithdrawalHeight int64 // last time this delegation withdrew rewards
}
```

View File

@ -1,16 +1,15 @@
# Transactions
## TxWithdrawDelegationRewardsAll
## MsgWithdrawDelegationRewardsAll
When a delegator wishes to withdraw their rewards it must send
`TxWithdrawDelegationRewardsAll`. Note that parts of this transaction logic are also
`MsgWithdrawDelegationRewardsAll`. Note that parts of this transaction logic are also
triggered each with any change in individual delegations, such as an unbond,
redelegation, or delegation of additional tokens to a specific validator.
```golang
type TxWithdrawDelegationRewardsAll struct {
delegatorAddr sdk.AccAddress
withdrawAddr sdk.AccAddress // address to make the withdrawal to
type MsgWithdrawDelegationRewardsAll struct {
DelegatorAddr sdk.AccAddress
}
func WithdrawDelegationRewardsAll(delegatorAddr, withdrawAddr sdk.AccAddress)
@ -26,31 +25,30 @@ func GetDelegatorRewardsAll(delegatorAddr sdk.AccAddress, height int64) DecCoins
// collect all entitled rewards
withdraw = 0
pool = stake.GetPool()
global = GetGlobal()
feePool = GetFeePool()
for delegation = range delegations
delInfo = GetDelegationDistInfo(delegation.DelegatorAddr,
delegation.ValidatorAddr)
valInfo = GetValidatorDistInfo(delegation.ValidatorAddr)
validator = GetValidator(delegation.ValidatorAddr)
global, diWithdraw = delInfo.WithdrawRewards(global, valInfo, height, pool.BondedTokens,
feePool, diWithdraw = delInfo.WithdrawRewards(feePool, valInfo, height, pool.BondedTokens,
validator.Tokens, validator.DelegatorShares, validator.Commission)
withdraw += diWithdraw
SetGlobal(global)
SetFeePool(feePool)
return withdraw
```
## TxWithdrawDelegationReward
## MsgWithdrawDelegationReward
under special circumstances a delegator may wish to withdraw rewards from only
a single validator.
```golang
type TxWithdrawDelegationReward struct {
delegatorAddr sdk.AccAddress
validatorAddr sdk.AccAddress
withdrawAddr sdk.AccAddress // address to make the withdrawal to
type MsgWithdrawDelegationReward struct {
DelegatorAddr sdk.AccAddress
ValidatorAddr sdk.ValAddress
}
func WithdrawDelegationReward(delegatorAddr, validatorAddr, withdrawAddr sdk.AccAddress)
@ -58,39 +56,38 @@ func WithdrawDelegationReward(delegatorAddr, validatorAddr, withdrawAddr sdk.Acc
// get all distribution scenarios
pool = stake.GetPool()
global = GetGlobal()
feePool = GetFeePool()
delInfo = GetDelegationDistInfo(delegatorAddr,
validatorAddr)
valInfo = GetValidatorDistInfo(validatorAddr)
validator = GetValidator(validatorAddr)
global, withdraw = delInfo.WithdrawRewards(global, valInfo, height, pool.BondedTokens,
feePool, withdraw = delInfo.WithdrawRewards(feePool, valInfo, height, pool.BondedTokens,
validator.Tokens, validator.DelegatorShares, validator.Commission)
SetGlobal(global)
SetFeePool(feePool)
AddCoins(withdrawAddr, withdraw.TruncateDecimal())
```
## TxWithdrawValidatorRewardsAll
## MsgWithdrawValidatorRewardsAll
When a validator wishes to withdraw their rewards it must send
`TxWithdrawValidatorRewardsAll`. Note that parts of this transaction logic are also
`MsgWithdrawValidatorRewardsAll`. Note that parts of this transaction logic are also
triggered each with any change in individual delegations, such as an unbond,
redelegation, or delegation of additional tokens to a specific validator. This
transaction withdraws the validators commission fee, as well as any rewards
earning on their self-delegation.
```
type TxWithdrawValidatorRewardsAll struct {
operatorAddr sdk.AccAddress // validator address to withdraw from
withdrawAddr sdk.AccAddress // address to make the withdrawal to
type MsgWithdrawValidatorRewardsAll struct {
OperatorAddr sdk.ValAddress // validator address to withdraw from
}
func WithdrawValidatorRewardsAll(operatorAddr, withdrawAddr sdk.AccAddress)
height = GetHeight()
global = GetGlobal()
feePool = GetFeePool()
pool = GetPool()
ValInfo = GetValidatorDistInfo(delegation.ValidatorAddr)
validator = GetValidator(delegation.ValidatorAddr)
@ -99,10 +96,10 @@ func WithdrawValidatorRewardsAll(operatorAddr, withdrawAddr sdk.AccAddress)
withdraw = GetDelegatorRewardsAll(validator.OperatorAddr, height)
// withdrawal validator commission rewards
global, commission = valInfo.WithdrawCommission(global, valInfo, height, pool.BondedTokens,
feePool, commission = valInfo.WithdrawCommission(feePool, valInfo, height, pool.BondedTokens,
validator.Tokens, validator.Commission)
withdraw += commission
SetGlobal(global)
SetFeePool(feePool)
AddCoins(withdrawAddr, withdraw.TruncateDecimal())
```
@ -117,7 +114,7 @@ block. The accum is always additive to the existing accum. This term is to be
updated each time rewards are withdrawn from the system.
```
func (g Global) UpdateTotalValAccum(height int64, totalBondedTokens Dec) Global
func (g FeePool) UpdateTotalValAccum(height int64, totalBondedTokens Dec) FeePool
blocks = height - g.TotalValAccumUpdateHeight
g.TotalValAccum += totalDelShares * blocks
g.TotalValAccumUpdateHeight = height
@ -140,7 +137,7 @@ func (vi ValidatorDistInfo) UpdateTotalDelAccum(height int64, totalDelShares Dec
return vi
```
### Global pool to validator pool
### FeePool pool to validator pool
Every time a validator or delegator executes a withdrawal or the validator is
the proposer and receives new tokens, the relevant validator must move tokens
@ -148,14 +145,14 @@ from the passive global pool to their own pool. It is at this point that the
commission is withdrawn
```
func (vi ValidatorDistInfo) TakeAccum(g Global, height int64, totalBonded, vdTokens, commissionRate Dec) (
vi ValidatorDistInfo, g Global)
func (vi ValidatorDistInfo) TakeFeePoolRewards(g FeePool, height int64, totalBonded, vdTokens, commissionRate Dec) (
vi ValidatorDistInfo, g FeePool)
g.UpdateTotalValAccum(height, totalBondedShares)
// update the validators pool
blocks = height - vi.GlobalWithdrawalHeight
vi.GlobalWithdrawalHeight = height
blocks = height - vi.FeePoolWithdrawalHeight
vi.FeePoolWithdrawalHeight = height
accum = blocks * vdTokens
withdrawalTokens := g.Pool * accum / g.TotalValAccum
commission := withdrawalTokens * commissionRate
@ -175,12 +172,12 @@ For delegations (including validator's self-delegation) all rewards from reward
pool have already had the validator's commission taken away.
```
func (di DelegatorDistInfo) WithdrawRewards(g Global, vi ValidatorDistInfo,
func (di DelegationDistInfo) WithdrawRewards(g FeePool, vi ValidatorDistInfo,
height int64, totalBonded, vdTokens, totalDelShares, commissionRate Dec) (
di DelegatorDistInfo, g Global, withdrawn DecCoins)
di DelegationDistInfo, g FeePool, withdrawn DecCoins)
vi.UpdateTotalDelAccum(height, totalDelShares)
g = vi.TakeAccum(g, height, totalBonded, vdTokens, commissionRate)
g = vi.TakeFeePoolRewards(g, height, totalBonded, vdTokens, commissionRate)
blocks = height - di.WithdrawalHeight
di.WithdrawalHeight = height
@ -200,11 +197,11 @@ func (di DelegatorDistInfo) WithdrawRewards(g Global, vi ValidatorDistInfo,
Commission is calculated each time rewards enter into the validator.
```
func (vi ValidatorDistInfo) WithdrawCommission(g Global, height int64,
func (vi ValidatorDistInfo) WithdrawCommission(g FeePool, height int64,
totalBonded, vdTokens, commissionRate Dec) (
vi ValidatorDistInfo, g Global, withdrawn DecCoins)
vi ValidatorDistInfo, g FeePool, withdrawn DecCoins)
g = vi.TakeAccum(g, height, totalBonded, vdTokens, commissionRate)
g = vi.TakeFeePoolRewards(g, height, totalBonded, vdTokens, commissionRate)
withdrawalTokens := vi.PoolCommission
vi.PoolCommission = 0

View File

@ -93,25 +93,27 @@ for val in block.Validators:
index := signInfo.IndexOffset % SIGNED_BLOCKS_WINDOW
signInfo.IndexOffset++
previous = SigningBitArray.Get(val.Address, index)
previous = MissedBlockBitArray.Get(val.Address, index)
// update counter if array has changed
if previous and val in block.AbsentValidators:
SigningBitArray.Set(val.Address, index, false)
signInfo.SignedBlocksCounter--
else if !previous and val not in block.AbsentValidators:
SigningBitArray.Set(val.Address, index, true)
signInfo.SignedBlocksCounter++
if !previous and val in block.AbsentValidators:
MissedBlockBitArray.Set(val.Address, index, true)
signInfo.MissedBlocksCounter++
else if previous and val not in block.AbsentValidators:
MissedBlockBitArray.Set(val.Address, index, false)
signInfo.MissedBlocksCounter--
// else previous == val not in block.AbsentValidators, no change
// validator must be active for at least SIGNED_BLOCKS_WINDOW
// before they can be automatically unbonded for failing to be
// included in 50% of the recent LastCommits
minHeight = signInfo.StartHeight + SIGNED_BLOCKS_WINDOW
minSigned = SIGNED_BLOCKS_WINDOW / 2
if height > minHeight AND signInfo.SignedBlocksCounter < minSigned:
maxMissed = SIGNED_BLOCKS_WINDOW / 2
if height > minHeight AND signInfo.MissedBlocksCounter > maxMissed:
signInfo.JailedUntil = block.Time + DOWNTIME_UNBOND_DURATION
signInfo.IndexOffset = 0
signInfo.MissedBlocksCounter = 0
clearMissedBlockBitArray()
slash & unbond the validator
SigningInfo.Set(val.Address, signInfo)

View File

@ -12,6 +12,17 @@ and `SlashedSoFar` of `0`:
```
onValidatorBonded(address sdk.ValAddress)
signingInfo, found = getValidatorSigningInfo(address)
if !found {
signingInfo = ValidatorSigningInfo {
StartHeight : CurrentHeight,
IndexOffset : 0,
JailedUntil : time.Unix(0, 0),
MissedBloskCounter : 0
}
setValidatorSigningInfo(signingInfo)
}
slashingPeriod = SlashingPeriod{
ValidatorAddr : address,
StartHeight : CurrentHeight,

View File

@ -17,18 +17,18 @@ Information about validator activity is tracked in a `ValidatorSigningInfo`.
It is indexed in the store as follows:
- SigningInfo: ` 0x01 | ValTendermintAddr -> amino(valSigningInfo)`
- SigningBitArray: ` 0x02 | ValTendermintAddr | LittleEndianUint64(signArrayIndex) -> VarInt(didSign)`
- MissedBlocksBitArray: ` 0x02 | ValTendermintAddr | LittleEndianUint64(signArrayIndex) -> VarInt(didMiss)`
The first map allows us to easily lookup the recent signing info for a
validator, according to the Tendermint validator address. The second map acts as
a bit-array of size `SIGNED_BLOCKS_WINDOW` that tells us if the validator signed for a given index in the bit-array.
a bit-array of size `SIGNED_BLOCKS_WINDOW` that tells us if the validator missed the block for a given index in the bit-array.
The index in the bit-array is given as little endian uint64.
The result is a `varint` that takes on `0` or `1`, where `0` indicates the
validator did not sign the corresponding block, and `1` indicates they did.
validator did not miss (did sign) the corresponding block, and `1` indicates they missed the block (did not sign).
Note that the SigningBitArray is not explicitly initialized up-front. Keys are
Note that the MissedBlocksBitArray is not explicitly initialized up-front. Keys are
added as we progress through the first `SIGNED_BLOCKS_WINDOW` blocks for a newly
bonded validator.
@ -40,7 +40,7 @@ type ValidatorSigningInfo struct {
IndexOffset int64 // Offset into the signed block bit array
JailedUntilHeight int64 // Block height until which the validator is jailed,
// or sentinel value of 0 for not jailed
SignedBlocksCounter int64 // Running counter of signed blocks
MissedBlocksCounter int64 // Running counter of missed blocks
}
```
@ -49,7 +49,7 @@ Where:
* `StartHeight` is set to the height that the candidate became an active validator (with non-zero voting power).
* `IndexOffset` is incremented each time the candidate was a bonded validator in a block (and may have signed a precommit or not).
* `JailedUntil` is set whenever the candidate is jailed due to downtime
* `SignedBlocksCounter` is a counter kept to avoid unnecessary array reads. `SignedBlocksBitArray.Sum() == SignedBlocksCounter` always.
* `MissedBlocksCounter` is a counter kept to avoid unnecessary array reads. `MissedBlocksBitArray.Sum() == MissedBlocksCounter` always.
## Slashing Period

View File

@ -25,10 +25,6 @@ handleMsgUnjail(tx TxUnjail)
if block time < info.JailedUntil
fail with "Validator still jailed, cannot unjail until period has expired"
// Update the start height so the validator won't be immediately unbonded again
info.StartHeight = BlockHeight
setValidatorSigningInfo(info)
validator.Jailed = false
setValidator(validator)

View File

@ -48,6 +48,11 @@ func (v Validator) GetDelegatorShares() sdk.Dec {
return sdk.ZeroDec()
}
// Implements sdk.Validator
func (v Validator) GetCommission() sdk.Dec {
return sdk.ZeroDec()
}
// Implements sdk.Validator
func (v Validator) GetJailed() bool {
return false

108
store/list.go Normal file
View File

@ -0,0 +1,108 @@
package store
import (
"fmt"
"strconv"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Key for the length of the list
func LengthKey() []byte {
return []byte{0x00}
}
// Key for the elements of the list
func ElemKey(index uint64) []byte {
return append([]byte{0x01}, []byte(fmt.Sprintf("%020d", index))...)
}
// List defines an integer indexable mapper
// It panics when the element type cannot be (un/)marshalled by the codec
type List struct {
cdc *codec.Codec
store sdk.KVStore
}
// NewList constructs new List
func NewList(cdc *codec.Codec, store sdk.KVStore) List {
return List{
cdc: cdc,
store: store,
}
}
// Len() returns the length of the list
// The length is only increased by Push() and not decreased
// List dosen't check if an index is in bounds
// The user should check Len() before doing any actions
func (m List) Len() (res uint64) {
bz := m.store.Get(LengthKey())
if bz == nil {
return 0
}
m.cdc.MustUnmarshalBinary(bz, &res)
return
}
// Get() returns the element by its index
func (m List) Get(index uint64, ptr interface{}) error {
bz := m.store.Get(ElemKey(index))
return m.cdc.UnmarshalBinary(bz, ptr)
}
// Set() stores the element to the given position
// Setting element out of range will break length counting
// Use Push() instead of Set() to append a new element
func (m List) Set(index uint64, value interface{}) {
bz := m.cdc.MustMarshalBinary(value)
m.store.Set(ElemKey(index), bz)
}
// Delete() deletes the element in the given position
// Other elements' indices are preserved after deletion
// Panics when the index is out of range
func (m List) Delete(index uint64) {
m.store.Delete(ElemKey(index))
}
// Push() inserts the element to the end of the list
// It will increase the length when it is called
func (m List) Push(value interface{}) {
length := m.Len()
m.Set(length, value)
m.store.Set(LengthKey(), m.cdc.MustMarshalBinary(length+1))
}
// Iterate() is used to iterate over all existing elements in the list
// Return true in the continuation to break
// The second element of the continuation will indicate the position of the element
// Using it with Get() will return the same one with the provided element
// CONTRACT: No writes may happen within a domain while iterating over it.
func (m List) Iterate(ptr interface{}, fn func(uint64) bool) {
iter := sdk.KVStorePrefixIterator(m.store, []byte{0x01})
for ; iter.Valid(); iter.Next() {
v := iter.Value()
m.cdc.MustUnmarshalBinary(v, ptr)
k := iter.Key()
s := string(k[len(k)-20:])
index, err := strconv.ParseUint(s, 10, 64)
if err != nil {
panic(err)
}
if fn(index) {
break
}
}
iter.Close()
}
func subspace(prefix []byte) (start, end []byte) {
end = make([]byte, len(prefix))
copy(end, prefix)
end[len(end)-1]++
return prefix, end
}

75
store/list_test.go Normal file
View File

@ -0,0 +1,75 @@
package store
import (
"math/rand"
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
)
func TestList(t *testing.T) {
key := sdk.NewKVStoreKey("test")
ctx, cdc := defaultComponents(key)
store := ctx.KVStore(key)
lm := NewList(cdc, store)
val := S{1, true}
var res S
lm.Push(val)
require.Equal(t, uint64(1), lm.Len())
lm.Get(uint64(0), &res)
require.Equal(t, val, res)
val = S{2, false}
lm.Set(uint64(0), val)
lm.Get(uint64(0), &res)
require.Equal(t, val, res)
val = S{100, false}
lm.Push(val)
require.Equal(t, uint64(2), lm.Len())
lm.Get(uint64(1), &res)
require.Equal(t, val, res)
lm.Delete(uint64(1))
require.Equal(t, uint64(2), lm.Len())
lm.Iterate(&res, func(index uint64) (brk bool) {
var temp S
lm.Get(index, &temp)
require.Equal(t, temp, res)
require.True(t, index != 1)
return
})
lm.Iterate(&res, func(index uint64) (brk bool) {
lm.Set(index, S{res.I + 1, !res.B})
return
})
lm.Get(uint64(0), &res)
require.Equal(t, S{3, true}, res)
}
func TestListRandom(t *testing.T) {
key := sdk.NewKVStoreKey("test")
ctx, cdc := defaultComponents(key)
store := ctx.KVStore(key)
list := NewList(cdc, store)
mocklist := []uint32{}
for i := 0; i < 100; i++ {
item := rand.Uint32()
list.Push(item)
mocklist = append(mocklist, item)
}
for k, v := range mocklist {
var i uint32
require.NotPanics(t, func() { list.Get(uint64(k), &i) })
require.Equal(t, v, i)
}
}

88
store/queue.go Normal file
View File

@ -0,0 +1,88 @@
package store
import (
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Key for the top element position in the queue
func TopKey() []byte {
return []byte{0x02}
}
// Queue is a List wrapper that provides queue-like functions
// It panics when the element type cannot be (un/)marshalled by the codec
type Queue struct {
List List
}
// NewQueue constructs new Queue
func NewQueue(cdc *codec.Codec, store sdk.KVStore) Queue {
return Queue{NewList(cdc, store)}
}
func (m Queue) getTop() (res uint64) {
bz := m.List.store.Get(TopKey())
if bz == nil {
return 0
}
m.List.cdc.MustUnmarshalBinary(bz, &res)
return
}
func (m Queue) setTop(top uint64) {
bz := m.List.cdc.MustMarshalBinary(top)
m.List.store.Set(TopKey(), bz)
}
// Push() inserts the elements to the rear of the queue
func (m Queue) Push(value interface{}) {
m.List.Push(value)
}
// Popping/Peeking on an empty queue will cause panic
// The user should check IsEmpty() before doing any actions
// Peek() returns the element at the front of the queue without removing it
func (m Queue) Peek(ptr interface{}) error {
top := m.getTop()
return m.List.Get(top, ptr)
}
// Pop() returns the element at the front of the queue and removes it
func (m Queue) Pop() {
top := m.getTop()
m.List.Delete(top)
m.setTop(top + 1)
}
// IsEmpty() checks if the queue is empty
func (m Queue) IsEmpty() bool {
top := m.getTop()
length := m.List.Len()
return top >= length
}
// Flush() removes elements it processed
// Return true in the continuation to break
// The interface{} is unmarshalled before the continuation is called
// Starts from the top(head) of the queue
// CONTRACT: Pop() or Push() should not be performed while flushing
func (m Queue) Flush(ptr interface{}, fn func() bool) {
top := m.getTop()
length := m.List.Len()
var i uint64
for i = top; i < length; i++ {
err := m.List.Get(i, ptr)
if err != nil {
// TODO: Handle with #870
panic(err)
}
m.List.Delete(i)
if fn() {
break
}
}
m.setTop(i)
}

102
store/queue_test.go Normal file
View File

@ -0,0 +1,102 @@
package store
import (
"testing"
"github.com/stretchr/testify/require"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
)
type S struct {
I uint64
B bool
}
func defaultComponents(key sdk.StoreKey) (sdk.Context, *codec.Codec) {
db := dbm.NewMemDB()
cms := NewCommitMultiStore(db)
cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db)
cms.LoadLatestVersion()
ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger())
cdc := codec.New()
return ctx, cdc
}
func TestQueue(t *testing.T) {
key := sdk.NewKVStoreKey("test")
ctx, cdc := defaultComponents(key)
store := ctx.KVStore(key)
qm := NewQueue(cdc, store)
val := S{1, true}
var res S
qm.Push(val)
qm.Peek(&res)
require.Equal(t, val, res)
qm.Pop()
empty := qm.IsEmpty()
require.True(t, empty)
require.NotNil(t, qm.Peek(&res))
qm.Push(S{1, true})
qm.Push(S{2, true})
qm.Push(S{3, true})
qm.Flush(&res, func() (brk bool) {
if res.I == 3 {
brk = true
}
return
})
require.False(t, qm.IsEmpty())
qm.Pop()
require.True(t, qm.IsEmpty())
}
func TestKeys(t *testing.T) {
key := sdk.NewKVStoreKey("test")
ctx, cdc := defaultComponents(key)
store := ctx.KVStore(key)
queue := NewQueue(cdc, store)
for i := 0; i < 10; i++ {
queue.Push(i)
}
var len uint64
var top uint64
var expected int
var actual int
// Checking keys.LengthKey
err := cdc.UnmarshalBinary(store.Get(LengthKey()), &len)
require.Nil(t, err)
require.Equal(t, len, queue.List.Len())
// Checking keys.ElemKey
for i := 0; i < 10; i++ {
queue.List.Get(uint64(i), &expected)
bz := store.Get(ElemKey(uint64(i)))
err = cdc.UnmarshalBinary(bz, &actual)
require.Nil(t, err)
require.Equal(t, expected, actual)
}
queue.Pop()
err = cdc.UnmarshalBinary(store.Get(TopKey()), &top)
require.Nil(t, err)
require.Equal(t, top, queue.getTop())
}

View File

@ -187,6 +187,12 @@ func (c Context) WithBlockTime(newTime time.Time) Context {
return c.WithBlockHeader(newHeader)
}
func (c Context) WithProposer(addr ConsAddress) Context {
newHeader := c.BlockHeader()
newHeader.ProposerAddress = addr.Bytes()
return c.WithBlockHeader(newHeader)
}
func (c Context) WithBlockHeight(height int64) Context {
newHeader := c.BlockHeader()
newHeader.Height = height

View File

@ -241,6 +241,12 @@ func (d Dec) Quo(d2 Dec) Dec {
return Dec{chopped}
}
// quotient
func (d Dec) QuoInt(i Int) Dec {
mul := new(big.Int).Quo(d.Int, i.i)
return Dec{mul}
}
func (d Dec) String() string {
str := d.ToLeftPaddedWithDecimals(Precision)
placement := len(str) - Precision
@ -462,6 +468,6 @@ func MaxDec(d1, d2 Dec) Dec {
}
// intended to be used with require/assert: require.True(DecEq(...))
func DecEq(t *testing.T, exp, got Dec) (*testing.T, bool, string, Dec, Dec) {
return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp, got
func DecEq(t *testing.T, exp, got Dec) (*testing.T, bool, string, string, string) {
return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp.String(), got.String()
}

View File

@ -2,6 +2,7 @@ package types
import (
"encoding/json"
"testing"
"math/big"
"math/rand"
@ -525,3 +526,10 @@ func (i *Uint) UnmarshalJSON(bz []byte) error {
}
return unmarshalJSON(i.i, bz)
}
//__________________________________________________________________________
// intended to be used with require/assert: require.True(IntEq(...))
func IntEq(t *testing.T, exp, got Int) (*testing.T, bool, string, string, string) {
return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp.String(), got.String()
}

View File

@ -1,254 +0,0 @@
package lib
import (
"fmt"
"strconv"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Linear defines a primitive mapper type
type Linear struct {
cdc *codec.Codec
store sdk.KVStore
keys *LinearKeys
}
// LinearKeys defines keysions for the key bytes
type LinearKeys struct {
LengthKey []byte
ElemKey []byte
TopKey []byte
}
// Should never be modified
var cachedDefaultLinearKeys = DefaultLinearKeys()
// DefaultLinearKeys returns the default setting of LinearOption
func DefaultLinearKeys() *LinearKeys {
keys := LinearKeys{
LengthKey: []byte{0x00},
ElemKey: []byte{0x01},
TopKey: []byte{0x02},
}
return &keys
}
// NewLinear constructs new Linear
func NewLinear(cdc *codec.Codec, store sdk.KVStore, keys *LinearKeys) Linear {
if keys == nil {
keys = cachedDefaultLinearKeys
}
if keys.LengthKey == nil || keys.ElemKey == nil || keys.TopKey == nil {
panic("Invalid LinearKeys")
}
return Linear{
cdc: cdc,
store: store,
keys: keys,
}
}
// List is a Linear interface that provides list-like functions
// It panics when the element type cannot be (un/)marshalled by the codec
type List interface {
// Len() returns the length of the list
// The length is only increased by Push() and not decreased
// List dosen't check if an index is in bounds
// The user should check Len() before doing any actions
Len() uint64
// Get() returns the element by its index
Get(uint64, interface{}) error
// Set() stores the element to the given position
// Setting element out of range will break length counting
// Use Push() instead of Set() to append a new element
Set(uint64, interface{})
// Delete() deletes the element in the given position
// Other elements' indices are preserved after deletion
// Panics when the index is out of range
Delete(uint64)
// Push() inserts the element to the end of the list
// It will increase the length when it is called
Push(interface{})
// Iterate*() is used to iterate over all existing elements in the list
// Return true in the continuation to break
// The second element of the continuation will indicate the position of the element
// Using it with Get() will return the same one with the provided element
// CONTRACT: No writes may happen within a domain while iterating over it.
Iterate(interface{}, func(uint64) bool)
}
// NewList constructs new List
func NewList(cdc *codec.Codec, store sdk.KVStore, keys *LinearKeys) List {
return NewLinear(cdc, store, keys)
}
// Key for the length of the list
func (m Linear) LengthKey() []byte {
return m.keys.LengthKey
}
// Key for the elements of the list
func (m Linear) ElemKey(index uint64) []byte {
return append(m.keys.ElemKey, []byte(fmt.Sprintf("%020d", index))...)
}
// Len implements List
func (m Linear) Len() (res uint64) {
bz := m.store.Get(m.LengthKey())
if bz == nil {
return 0
}
m.cdc.MustUnmarshalBinary(bz, &res)
return
}
// Get implements List
func (m Linear) Get(index uint64, ptr interface{}) error {
bz := m.store.Get(m.ElemKey(index))
return m.cdc.UnmarshalBinary(bz, ptr)
}
// Set implements List
func (m Linear) Set(index uint64, value interface{}) {
bz := m.cdc.MustMarshalBinary(value)
m.store.Set(m.ElemKey(index), bz)
}
// Delete implements List
func (m Linear) Delete(index uint64) {
m.store.Delete(m.ElemKey(index))
}
// Push implements List
func (m Linear) Push(value interface{}) {
length := m.Len()
m.Set(length, value)
m.store.Set(m.LengthKey(), m.cdc.MustMarshalBinary(length+1))
}
// IterateRead implements List
func (m Linear) Iterate(ptr interface{}, fn func(uint64) bool) {
iter := sdk.KVStorePrefixIterator(m.store, []byte{0x01})
for ; iter.Valid(); iter.Next() {
v := iter.Value()
m.cdc.MustUnmarshalBinary(v, ptr)
k := iter.Key()
s := string(k[len(k)-20:])
index, err := strconv.ParseUint(s, 10, 64)
if err != nil {
panic(err)
}
if fn(index) {
break
}
}
iter.Close()
}
// Queue is a Linear interface that provides queue-like functions
// It panics when the element type cannot be (un/)marshalled by the codec
type Queue interface {
// Push() inserts the elements to the rear of the queue
Push(interface{})
// Popping/Peeking on an empty queue will cause panic
// The user should check IsEmpty() before doing any actions
// Peek() returns the element at the front of the queue without removing it
Peek(interface{}) error
// Pop() returns the element at the front of the queue and removes it
Pop()
// IsEmpty() checks if the queue is empty
IsEmpty() bool
// Flush() removes elements it processed
// Return true in the continuation to break
// The interface{} is unmarshalled before the continuation is called
// Starts from the top(head) of the queue
// CONTRACT: Pop() or Push() should not be performed while flushing
Flush(interface{}, func() bool)
}
// NewQueue constructs new Queue
func NewQueue(cdc *codec.Codec, store sdk.KVStore, keys *LinearKeys) Queue {
return NewLinear(cdc, store, keys)
}
// Key for the top element position in the queue
func (m Linear) TopKey() []byte {
return m.keys.TopKey
}
func (m Linear) getTop() (res uint64) {
bz := m.store.Get(m.TopKey())
if bz == nil {
return 0
}
m.cdc.MustUnmarshalBinary(bz, &res)
return
}
func (m Linear) setTop(top uint64) {
bz := m.cdc.MustMarshalBinary(top)
m.store.Set(m.TopKey(), bz)
}
// Peek implements Queue
func (m Linear) Peek(ptr interface{}) error {
top := m.getTop()
return m.Get(top, ptr)
}
// Pop implements Queue
func (m Linear) Pop() {
top := m.getTop()
m.Delete(top)
m.setTop(top + 1)
}
// IsEmpty implements Queue
func (m Linear) IsEmpty() bool {
top := m.getTop()
length := m.Len()
return top >= length
}
// Flush implements Queue
func (m Linear) Flush(ptr interface{}, fn func() bool) {
top := m.getTop()
length := m.Len()
var i uint64
for i = top; i < length; i++ {
err := m.Get(i, ptr)
if err != nil {
// TODO: Handle with #870
panic(err)
}
m.Delete(i)
if fn() {
break
}
}
m.setTop(i)
}
func subspace(prefix []byte) (start, end []byte) {
end = make([]byte, len(prefix))
copy(end, prefix)
end[len(end)-1]++
return prefix, end
}

View File

@ -1,169 +0,0 @@
package lib
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
)
type S struct {
I uint64
B bool
}
func defaultComponents(key sdk.StoreKey) (sdk.Context, *codec.Codec) {
db := dbm.NewMemDB()
cms := store.NewCommitMultiStore(db)
cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db)
cms.LoadLatestVersion()
ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger())
cdc := codec.New()
return ctx, cdc
}
func TestNewLinear(t *testing.T) {
cdc := codec.New()
require.NotPanics(t, func() { NewLinear(cdc, nil, nil) })
require.NotPanics(t, func() { NewLinear(cdc, nil, DefaultLinearKeys()) })
require.NotPanics(t, func() { NewLinear(cdc, nil, &LinearKeys{[]byte{0xAA}, []byte{0xBB}, []byte{0xCC}}) })
require.Panics(t, func() { NewLinear(cdc, nil, &LinearKeys{nil, nil, nil}) })
require.Panics(t, func() { NewLinear(cdc, nil, &LinearKeys{[]byte{0xAA}, nil, nil}) })
require.Panics(t, func() { NewLinear(cdc, nil, &LinearKeys{nil, []byte{0xBB}, nil}) })
require.Panics(t, func() { NewLinear(cdc, nil, &LinearKeys{nil, nil, []byte{0xCC}}) })
}
func TestList(t *testing.T) {
key := sdk.NewKVStoreKey("test")
ctx, cdc := defaultComponents(key)
store := ctx.KVStore(key)
lm := NewList(cdc, store, nil)
val := S{1, true}
var res S
lm.Push(val)
require.Equal(t, uint64(1), lm.Len())
lm.Get(uint64(0), &res)
require.Equal(t, val, res)
val = S{2, false}
lm.Set(uint64(0), val)
lm.Get(uint64(0), &res)
require.Equal(t, val, res)
val = S{100, false}
lm.Push(val)
require.Equal(t, uint64(2), lm.Len())
lm.Get(uint64(1), &res)
require.Equal(t, val, res)
lm.Delete(uint64(1))
require.Equal(t, uint64(2), lm.Len())
lm.Iterate(&res, func(index uint64) (brk bool) {
var temp S
lm.Get(index, &temp)
require.Equal(t, temp, res)
require.True(t, index != 1)
return
})
lm.Iterate(&res, func(index uint64) (brk bool) {
lm.Set(index, S{res.I + 1, !res.B})
return
})
lm.Get(uint64(0), &res)
require.Equal(t, S{3, true}, res)
}
func TestQueue(t *testing.T) {
key := sdk.NewKVStoreKey("test")
ctx, cdc := defaultComponents(key)
store := ctx.KVStore(key)
qm := NewQueue(cdc, store, nil)
val := S{1, true}
var res S
qm.Push(val)
qm.Peek(&res)
require.Equal(t, val, res)
qm.Pop()
empty := qm.IsEmpty()
require.True(t, empty)
require.NotNil(t, qm.Peek(&res))
qm.Push(S{1, true})
qm.Push(S{2, true})
qm.Push(S{3, true})
qm.Flush(&res, func() (brk bool) {
if res.I == 3 {
brk = true
}
return
})
require.False(t, qm.IsEmpty())
qm.Pop()
require.True(t, qm.IsEmpty())
}
func TestOptions(t *testing.T) {
key := sdk.NewKVStoreKey("test")
ctx, cdc := defaultComponents(key)
store := ctx.KVStore(key)
keys := &LinearKeys{
LengthKey: []byte{0xDE, 0xAD},
ElemKey: []byte{0xBE, 0xEF},
TopKey: []byte{0x12, 0x34},
}
linear := NewLinear(cdc, store, keys)
for i := 0; i < 10; i++ {
linear.Push(i)
}
var len uint64
var top uint64
var expected int
var actual int
// Checking keys.LengthKey
err := cdc.UnmarshalBinary(store.Get(keys.LengthKey), &len)
require.Nil(t, err)
require.Equal(t, len, linear.Len())
// Checking keys.ElemKey
for i := 0; i < 10; i++ {
linear.Get(uint64(i), &expected)
bz := store.Get(append(keys.ElemKey, []byte(fmt.Sprintf("%020d", i))...))
err = cdc.UnmarshalBinary(bz, &actual)
require.Nil(t, err)
require.Equal(t, expected, actual)
}
linear.Pop()
err = cdc.UnmarshalBinary(store.Get(keys.TopKey), &top)
require.Nil(t, err)
require.Equal(t, top, linear.getTop())
}

View File

@ -44,6 +44,7 @@ type Validator interface {
GetConsAddr() ConsAddress // validation consensus address
GetPower() Dec // validation power
GetTokens() Dec // validation tokens
GetCommission() Dec // validator commission rate
GetDelegatorShares() Dec // Total out standing delegator shares
GetBondHeight() int64 // height in which the validator became active
}

View File

@ -302,6 +302,3 @@ func getSignBytesList(chainID string, stdTx StdTx, stdSigs []StdSignature) (sign
}
return
}
// BurnFeeHandler burns all fees (decreasing total supply)
func BurnFeeHandler(_ sdk.Context, _ sdk.Tx, _ sdk.Coins) {}

View File

@ -20,7 +20,6 @@ type FeeCollectionKeeper struct {
cdc *codec.Codec
}
// NewFeeKeeper returns a new FeeKeeper
func NewFeeCollectionKeeper(cdc *codec.Codec, key sdk.StoreKey) FeeCollectionKeeper {
return FeeCollectionKeeper{
key: key,
@ -28,7 +27,7 @@ func NewFeeCollectionKeeper(cdc *codec.Codec, key sdk.StoreKey) FeeCollectionKee
}
}
// Adds to Collected Fee Pool
// retrieves the collected fee pool
func (fck FeeCollectionKeeper) GetCollectedFees(ctx sdk.Context) sdk.Coins {
store := ctx.KVStore(fck.key)
bz := store.Get(collectedFeesKey)
@ -41,14 +40,12 @@ func (fck FeeCollectionKeeper) GetCollectedFees(ctx sdk.Context) sdk.Coins {
return *feePool
}
// Sets to Collected Fee Pool
func (fck FeeCollectionKeeper) setCollectedFees(ctx sdk.Context, coins sdk.Coins) {
bz := fck.cdc.MustMarshalBinary(coins)
store := ctx.KVStore(fck.key)
store.Set(collectedFeesKey, bz)
}
// Adds to Collected Fee Pool
func (fck FeeCollectionKeeper) addCollectedFees(ctx sdk.Context, coins sdk.Coins) sdk.Coins {
newCoins := fck.GetCollectedFees(ctx).Plus(coins)
fck.setCollectedFees(ctx, newCoins)
@ -56,7 +53,7 @@ func (fck FeeCollectionKeeper) addCollectedFees(ctx sdk.Context, coins sdk.Coins
return newCoins
}
// Clears the collected Fee Pool
// clear the fee pool
func (fck FeeCollectionKeeper) ClearCollectedFees(ctx sdk.Context) {
fck.setCollectedFees(ctx, sdk.Coins{})
}

View File

@ -0,0 +1,39 @@
package distribution
import (
abci "github.com/tendermint/tendermint/abci/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/distribution/keeper"
)
// set the proposer for determining distribution during endblock
func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper) {
if ctx.BlockHeight() > 1 {
previousPercentPrecommitVotes := getPreviousPercentPrecommitVotes(req)
previousProposer := k.GetPreviousProposerConsAddr(ctx)
k.AllocateFees(ctx, previousPercentPrecommitVotes, previousProposer)
}
consAddr := sdk.ConsAddress(req.Header.ProposerAddress)
k.SetPreviousProposerConsAddr(ctx, consAddr)
}
// percent precommit votes for the previous block
func getPreviousPercentPrecommitVotes(req abci.RequestBeginBlock) sdk.Dec {
// determine the total number of signed power
totalPower, sumPrecommitPower := int64(0), int64(0)
for _, voteInfo := range req.LastCommitInfo.GetVotes() {
totalPower += voteInfo.Validator.Power
if voteInfo.SignedLastBlock {
sumPrecommitPower += voteInfo.Validator.Power
}
}
if totalPower == 0 {
return sdk.ZeroDec()
}
return sdk.NewDec(sumPrecommitPower).Quo(sdk.NewDec(totalPower))
}

76
x/distribution/alias.go Normal file
View File

@ -0,0 +1,76 @@
// nolint
package distribution
import (
"github.com/cosmos/cosmos-sdk/x/distribution/keeper"
"github.com/cosmos/cosmos-sdk/x/distribution/tags"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
type (
Keeper = keeper.Keeper
Hooks = keeper.Hooks
DelegatorWithdrawInfo = types.DelegatorWithdrawInfo
DelegationDistInfo = types.DelegationDistInfo
ValidatorDistInfo = types.ValidatorDistInfo
TotalAccum = types.TotalAccum
FeePool = types.FeePool
MsgSetWithdrawAddress = types.MsgSetWithdrawAddress
MsgWithdrawDelegatorRewardsAll = types.MsgWithdrawDelegatorRewardsAll
MsgWithdrawDelegatorReward = types.MsgWithdrawDelegatorReward
MsgWithdrawValidatorRewardsAll = types.MsgWithdrawValidatorRewardsAll
GenesisState = types.GenesisState
)
var (
NewKeeper = keeper.NewKeeper
GetValidatorDistInfoKey = keeper.GetValidatorDistInfoKey
GetDelegationDistInfoKey = keeper.GetDelegationDistInfoKey
GetDelegationDistInfosKey = keeper.GetDelegationDistInfosKey
GetDelegatorWithdrawAddrKey = keeper.GetDelegatorWithdrawAddrKey
FeePoolKey = keeper.FeePoolKey
ValidatorDistInfoKey = keeper.ValidatorDistInfoKey
DelegationDistInfoKey = keeper.DelegationDistInfoKey
DelegatorWithdrawInfoKey = keeper.DelegatorWithdrawInfoKey
ProposerKey = keeper.ProposerKey
DefaultParamspace = keeper.DefaultParamspace
InitialFeePool = types.InitialFeePool
NewGenesisState = types.NewGenesisState
DefaultGenesisState = types.DefaultGenesisState
DefaultGenesisWithValidators = types.DefaultGenesisWithValidators
RegisterCodec = types.RegisterCodec
NewMsgSetWithdrawAddress = types.NewMsgSetWithdrawAddress
NewMsgWithdrawDelegatorRewardsAll = types.NewMsgWithdrawDelegatorRewardsAll
NewMsgWithdrawDelegationReward = types.NewMsgWithdrawDelegatorReward
NewMsgWithdrawValidatorRewardsAll = types.NewMsgWithdrawValidatorRewardsAll
)
const (
DefaultCodespace = types.DefaultCodespace
CodeInvalidInput = types.CodeInvalidInput
)
var (
ErrNilDelegatorAddr = types.ErrNilDelegatorAddr
ErrNilWithdrawAddr = types.ErrNilWithdrawAddr
ErrNilValidatorAddr = types.ErrNilValidatorAddr
)
var (
ActionModifyWithdrawAddress = tags.ActionModifyWithdrawAddress
ActionWithdrawDelegatorRewardsAll = tags.ActionWithdrawDelegatorRewardsAll
ActionWithdrawDelegatorReward = tags.ActionWithdrawDelegatorReward
ActionWithdrawValidatorRewardsAll = tags.ActionWithdrawValidatorRewardsAll
TagAction = tags.Action
TagValidator = tags.Validator
TagDelegator = tags.Delegator
)

View File

@ -0,0 +1,114 @@
// nolint
package cli
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/utils"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
var (
flagOnlyFromValidator = "only-from-validator"
flagIsValidator = "is-validator"
)
// command to withdraw rewards
func GetCmdWithdrawRewards(cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "withdraw-rewards",
Short: "withdraw rewards for either: all-delegations, a delegation, or a validator",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
onlyFromVal := viper.GetString(flagOnlyFromValidator)
isVal := viper.GetBool(flagIsValidator)
if onlyFromVal != "" && isVal {
return fmt.Errorf("cannot use --%v, and --%v flags together",
flagOnlyFromValidator, flagIsValidator)
}
txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc)
cliCtx := context.NewCLIContext().
WithCodec(cdc).
WithAccountDecoder(authcmd.GetAccountDecoder(cdc))
var msg sdk.Msg
switch {
case isVal:
addr, err := cliCtx.GetFromAddress()
if err != nil {
return err
}
valAddr := sdk.ValAddress(addr.Bytes())
msg = types.NewMsgWithdrawValidatorRewardsAll(valAddr)
case onlyFromVal != "":
delAddr, err := cliCtx.GetFromAddress()
if err != nil {
return err
}
valAddr, err := sdk.ValAddressFromBech32(onlyFromVal)
if err != nil {
return err
}
msg = types.NewMsgWithdrawDelegatorReward(delAddr, valAddr)
default:
delAddr, err := cliCtx.GetFromAddress()
if err != nil {
return err
}
msg = types.NewMsgWithdrawDelegatorRewardsAll(delAddr)
}
// build and sign the transaction, then broadcast to Tendermint
return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg})
},
}
cmd.Flags().String(flagOnlyFromValidator, "", "only withdraw from this validator address (in bech)")
cmd.Flags().Bool(flagIsValidator, false, "also withdraw validator's commission")
return cmd
}
// GetCmdDelegate implements the delegate command.
func GetCmdSetWithdrawAddr(cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "set-withdraw-addr [withdraw-addr]",
Short: "change the default withdraw address for rewards associated with an address",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc)
cliCtx := context.NewCLIContext().
WithCodec(cdc).
WithAccountDecoder(authcmd.GetAccountDecoder(cdc))
delAddr, err := cliCtx.GetFromAddress()
if err != nil {
return err
}
withdrawAddr, err := sdk.AccAddressFromBech32(args[0])
if err != nil {
return err
}
msg := types.NewMsgSetWithdrawAddress(delAddr, withdrawAddr)
// build and sign the transaction, then broadcast to Tendermint
return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg})
},
}
return cmd
}

38
x/distribution/genesis.go Normal file
View File

@ -0,0 +1,38 @@
package distribution
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
// InitGenesis sets distribution information for genesis
func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) {
keeper.SetFeePool(ctx, data.FeePool)
keeper.SetCommunityTax(ctx, data.CommunityTax)
keeper.SetBaseProposerReward(ctx, data.BaseProposerReward)
keeper.SetBonusProposerReward(ctx, data.BonusProposerReward)
for _, vdi := range data.ValidatorDistInfos {
keeper.SetValidatorDistInfo(ctx, vdi)
}
for _, ddi := range data.DelegationDistInfos {
keeper.SetDelegationDistInfo(ctx, ddi)
}
for _, dw := range data.DelegatorWithdrawInfos {
keeper.SetDelegatorWithdrawAddr(ctx, dw.DelegatorAddr, dw.WithdrawAddr)
}
}
// WriteGenesis returns a GenesisState for a given context and keeper. The
// GenesisState will contain the pool, and validator/delegator distribution info's
func WriteGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState {
feePool := keeper.GetFeePool(ctx)
communityTax := keeper.GetCommunityTax(ctx)
baseProposerRewards := keeper.GetBaseProposerReward(ctx)
bonusProposerRewards := keeper.GetBonusProposerReward(ctx)
vdis := keeper.GetAllValidatorDistInfos(ctx)
ddis := keeper.GetAllDelegationDistInfos(ctx)
dwis := keeper.GetAllDelegatorWithdrawInfos(ctx)
return NewGenesisState(feePool, communityTax, baseProposerRewards,
bonusProposerRewards, vdis, ddis, dwis)
}

84
x/distribution/handler.go Normal file
View File

@ -0,0 +1,84 @@
package distribution
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/distribution/keeper"
"github.com/cosmos/cosmos-sdk/x/distribution/tags"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
func NewHandler(k keeper.Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
// NOTE msg already has validate basic run
switch msg := msg.(type) {
case types.MsgSetWithdrawAddress:
return handleMsgModifyWithdrawAddress(ctx, msg, k)
case types.MsgWithdrawDelegatorRewardsAll:
return handleMsgWithdrawDelegatorRewardsAll(ctx, msg, k)
case types.MsgWithdrawDelegatorReward:
return handleMsgWithdrawDelegatorReward(ctx, msg, k)
case types.MsgWithdrawValidatorRewardsAll:
return handleMsgWithdrawValidatorRewardsAll(ctx, msg, k)
default:
return sdk.ErrTxDecode("invalid message parse in distribution module").Result()
}
}
}
//_____________________________________________________________________
// These functions assume everything has been authenticated,
// now we just perform action and save
func handleMsgModifyWithdrawAddress(ctx sdk.Context, msg types.MsgSetWithdrawAddress, k keeper.Keeper) sdk.Result {
k.SetDelegatorWithdrawAddr(ctx, msg.DelegatorAddr, msg.WithdrawAddr)
tags := sdk.NewTags(
tags.Action, tags.ActionModifyWithdrawAddress,
tags.Delegator, []byte(msg.DelegatorAddr.String()),
)
return sdk.Result{
Tags: tags,
}
}
func handleMsgWithdrawDelegatorRewardsAll(ctx sdk.Context, msg types.MsgWithdrawDelegatorRewardsAll, k keeper.Keeper) sdk.Result {
k.WithdrawDelegationRewardsAll(ctx, msg.DelegatorAddr)
tags := sdk.NewTags(
tags.Action, tags.ActionWithdrawDelegatorRewardsAll,
tags.Delegator, []byte(msg.DelegatorAddr.String()),
)
return sdk.Result{
Tags: tags,
}
}
func handleMsgWithdrawDelegatorReward(ctx sdk.Context, msg types.MsgWithdrawDelegatorReward, k keeper.Keeper) sdk.Result {
k.WithdrawDelegationReward(ctx, msg.DelegatorAddr, msg.ValidatorAddr)
tags := sdk.NewTags(
tags.Action, tags.ActionWithdrawDelegatorReward,
tags.Delegator, []byte(msg.DelegatorAddr.String()),
tags.Validator, []byte(msg.ValidatorAddr.String()),
)
return sdk.Result{
Tags: tags,
}
}
func handleMsgWithdrawValidatorRewardsAll(ctx sdk.Context, msg types.MsgWithdrawValidatorRewardsAll, k keeper.Keeper) sdk.Result {
k.WithdrawValidatorRewardsAll(ctx, msg.ValidatorAddr)
tags := sdk.NewTags(
tags.Action, tags.ActionWithdrawValidatorRewardsAll,
tags.Validator, []byte(msg.ValidatorAddr.String()),
)
return sdk.Result{
Tags: tags,
}
}

View File

@ -0,0 +1,50 @@
package keeper
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
// Allocate fees handles distribution of the collected fees
func (k Keeper) AllocateFees(ctx sdk.Context, percentVotes sdk.Dec, proposer sdk.ConsAddress) {
ctx.Logger().With("module", "x/distribution").Error(fmt.Sprintf("allocation height: %v", ctx.BlockHeight()))
// get the proposer of this block
proposerValidator := k.stakeKeeper.ValidatorByConsAddr(ctx, proposer)
proposerDist := k.GetValidatorDistInfo(ctx, proposerValidator.GetOperator())
// get the fees which have been getting collected through all the
// transactions in the block
feesCollected := k.feeCollectionKeeper.GetCollectedFees(ctx)
feesCollectedDec := types.NewDecCoins(feesCollected)
// allocated rewards to proposer
baseProposerReward := k.GetBaseProposerReward(ctx)
bonusProposerReward := k.GetBonusProposerReward(ctx)
proposerMultiplier := baseProposerReward.Add(bonusProposerReward.Mul(percentVotes))
proposerReward := feesCollectedDec.MulDec(proposerMultiplier)
// apply commission
commission := proposerReward.MulDec(proposerValidator.GetCommission())
remaining := proposerReward.Minus(commission)
proposerDist.PoolCommission = proposerDist.PoolCommission.Plus(commission)
proposerDist.Pool = proposerDist.Pool.Plus(remaining)
// allocate community funding
communityTax := k.GetCommunityTax(ctx)
communityFunding := feesCollectedDec.MulDec(communityTax)
feePool := k.GetFeePool(ctx)
feePool.CommunityPool = feePool.CommunityPool.Plus(communityFunding)
// set the global pool within the distribution module
poolReceived := feesCollectedDec.Minus(proposerReward).Minus(communityFunding)
feePool.Pool = feePool.Pool.Plus(poolReceived)
k.SetValidatorDistInfo(ctx, proposerDist)
k.SetFeePool(ctx, feePool)
// clear the now distributed fees
k.feeCollectionKeeper.ClearCollectedFees(ctx)
}

View File

@ -0,0 +1,110 @@
package keeper
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAllocateFeesBasic(t *testing.T) {
// no community tax on inputs
ctx, _, keeper, sk, fck := CreateTestInputAdvanced(t, false, 100, sdk.ZeroDec())
stakeHandler := stake.NewHandler(sk)
denom := sk.GetParams(ctx).BondDenom
//first make a validator
totalPower := int64(10)
totalPowerDec := sdk.NewDec(totalPower)
msgCreateValidator := stake.NewTestMsgCreateValidator(valOpAddr1, valConsPk1, totalPower)
got := stakeHandler(ctx, msgCreateValidator)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
_ = sk.ApplyAndReturnValidatorSetUpdates(ctx)
// verify everything has been set in staking correctly
validator, found := sk.GetValidator(ctx, valOpAddr1)
require.True(t, found)
require.Equal(t, sdk.Bonded, validator.Status)
assert.True(sdk.DecEq(t, totalPowerDec, validator.Tokens))
assert.True(sdk.DecEq(t, totalPowerDec, validator.DelegatorShares))
bondedTokens := sk.TotalPower(ctx)
assert.True(sdk.DecEq(t, totalPowerDec, bondedTokens))
// initial fee pool should be empty
feePool := keeper.GetFeePool(ctx)
require.Nil(t, feePool.Pool)
// allocate 100 denom of fees
feeInputs := sdk.NewInt(100)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
// verify that these fees have been received by the feePool
percentProposer := sdk.NewDecWithPrec(5, 2)
percentRemaining := sdk.OneDec().Sub(percentProposer)
feePool = keeper.GetFeePool(ctx)
expRes := sdk.NewDecFromInt(feeInputs).Mul(percentRemaining)
require.Equal(t, 1, len(feePool.Pool))
require.True(sdk.DecEq(t, expRes, feePool.Pool[0].Amount))
}
func TestAllocateFeesWithCommunityTax(t *testing.T) {
communityTax := sdk.NewDecWithPrec(1, 2) //1%
ctx, _, keeper, sk, fck := CreateTestInputAdvanced(t, false, 100, communityTax)
stakeHandler := stake.NewHandler(sk)
denom := sk.GetParams(ctx).BondDenom
//first make a validator
totalPower := int64(10)
msgCreateValidator := stake.NewTestMsgCreateValidator(valOpAddr1, valConsPk1, totalPower)
got := stakeHandler(ctx, msgCreateValidator)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
_ = sk.ApplyAndReturnValidatorSetUpdates(ctx)
// allocate 100 denom of fees
feeInputs := sdk.NewInt(100)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
// verify that these fees have been received by the feePool
feePool := keeper.GetFeePool(ctx)
// 5% goes to proposer, 1% community tax
percentProposer := sdk.NewDecWithPrec(5, 2)
percentRemaining := sdk.OneDec().Sub(communityTax.Add(percentProposer))
expRes := sdk.NewDecFromInt(feeInputs).Mul(percentRemaining)
require.Equal(t, 1, len(feePool.Pool))
require.True(sdk.DecEq(t, expRes, feePool.Pool[0].Amount))
}
func TestAllocateFeesWithPartialPrecommitPower(t *testing.T) {
communityTax := sdk.NewDecWithPrec(1, 2)
ctx, _, keeper, sk, fck := CreateTestInputAdvanced(t, false, 100, communityTax)
stakeHandler := stake.NewHandler(sk)
denom := sk.GetParams(ctx).BondDenom
//first make a validator
totalPower := int64(100)
msgCreateValidator := stake.NewTestMsgCreateValidator(valOpAddr1, valConsPk1, totalPower)
got := stakeHandler(ctx, msgCreateValidator)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
_ = sk.ApplyAndReturnValidatorSetUpdates(ctx)
// allocate 100 denom of fees
feeInputs := sdk.NewInt(100)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
percentPrecommitVotes := sdk.NewDecWithPrec(25, 2)
keeper.AllocateFees(ctx, percentPrecommitVotes, valConsAddr1)
// verify that these fees have been received by the feePool
feePool := keeper.GetFeePool(ctx)
// 1% + 4%*0.25 to proposer + 1% community tax = 97%
percentProposer := sdk.NewDecWithPrec(1, 2).Add(sdk.NewDecWithPrec(4, 2).Mul(percentPrecommitVotes))
percentRemaining := sdk.OneDec().Sub(communityTax.Add(percentProposer))
expRes := sdk.NewDecFromInt(feeInputs).Mul(percentRemaining)
require.Equal(t, 1, len(feePool.Pool))
require.True(sdk.DecEq(t, expRes, feePool.Pool[0].Amount))
}

View File

@ -0,0 +1,130 @@
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
// get the delegator distribution info
func (k Keeper) GetDelegationDistInfo(ctx sdk.Context, delAddr sdk.AccAddress,
valOperatorAddr sdk.ValAddress) (ddi types.DelegationDistInfo) {
store := ctx.KVStore(k.storeKey)
b := store.Get(GetDelegationDistInfoKey(delAddr, valOperatorAddr))
if b == nil {
panic("Stored delegation-distribution info should not have been nil")
}
k.cdc.MustUnmarshalBinary(b, &ddi)
return
}
// set the delegator distribution info
func (k Keeper) SetDelegationDistInfo(ctx sdk.Context, ddi types.DelegationDistInfo) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshalBinary(ddi)
store.Set(GetDelegationDistInfoKey(ddi.DelegatorAddr, ddi.ValOperatorAddr), b)
}
// remove a delegator distribution info
func (k Keeper) RemoveDelegationDistInfo(ctx sdk.Context, delAddr sdk.AccAddress,
valOperatorAddr sdk.ValAddress) {
store := ctx.KVStore(k.storeKey)
store.Delete(GetDelegationDistInfoKey(delAddr, valOperatorAddr))
}
//___________________________________________________________________________________________
// get the delegator withdraw address, return the delegator address if not set
func (k Keeper) GetDelegatorWithdrawAddr(ctx sdk.Context, delAddr sdk.AccAddress) sdk.AccAddress {
store := ctx.KVStore(k.storeKey)
b := store.Get(GetDelegatorWithdrawAddrKey(delAddr))
if b == nil {
return delAddr
}
return sdk.AccAddress(b)
}
// set the delegator withdraw address
func (k Keeper) SetDelegatorWithdrawAddr(ctx sdk.Context, delAddr, withdrawAddr sdk.AccAddress) {
store := ctx.KVStore(k.storeKey)
store.Set(GetDelegatorWithdrawAddrKey(delAddr), withdrawAddr.Bytes())
}
// remove a delegator withdraw info
func (k Keeper) RemoveDelegatorWithdrawAddr(ctx sdk.Context, delAddr, withdrawAddr sdk.AccAddress) {
store := ctx.KVStore(k.storeKey)
store.Delete(GetDelegatorWithdrawAddrKey(delAddr))
}
//___________________________________________________________________________________________
// withdraw all the rewards for a single delegation
func (k Keeper) WithdrawDelegationReward(ctx sdk.Context, delegatorAddr sdk.AccAddress,
validatorAddr sdk.ValAddress) {
height := ctx.BlockHeight()
bondedTokens := k.stakeKeeper.TotalPower(ctx)
feePool := k.GetFeePool(ctx)
delInfo := k.GetDelegationDistInfo(ctx, delegatorAddr, validatorAddr)
valInfo := k.GetValidatorDistInfo(ctx, validatorAddr)
validator := k.stakeKeeper.Validator(ctx, validatorAddr)
delegation := k.stakeKeeper.Delegation(ctx, delegatorAddr, validatorAddr)
delInfo, valInfo, feePool, withdraw := delInfo.WithdrawRewards(feePool, valInfo, height, bondedTokens,
validator.GetTokens(), validator.GetDelegatorShares(), delegation.GetShares(), validator.GetCommission())
k.SetFeePool(ctx, feePool)
k.SetValidatorDistInfo(ctx, valInfo)
k.SetDelegationDistInfo(ctx, delInfo)
withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, delegatorAddr)
_, _, err := k.bankKeeper.AddCoins(ctx, withdrawAddr, withdraw.TruncateDecimal())
if err != nil {
panic(err)
}
}
//___________________________________________________________________________________________
// return all rewards for all delegations of a delegator
func (k Keeper) WithdrawDelegationRewardsAll(ctx sdk.Context, delegatorAddr sdk.AccAddress) {
height := ctx.BlockHeight()
withdraw := k.getDelegatorRewardsAll(ctx, delegatorAddr, height)
withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, delegatorAddr)
_, _, err := k.bankKeeper.AddCoins(ctx, withdrawAddr, withdraw.TruncateDecimal())
if err != nil {
panic(err)
}
}
// return all rewards for all delegations of a delegator
func (k Keeper) getDelegatorRewardsAll(ctx sdk.Context, delAddr sdk.AccAddress, height int64) types.DecCoins {
withdraw := types.DecCoins{}
bondedTokens := k.stakeKeeper.TotalPower(ctx)
feePool := k.GetFeePool(ctx)
// iterate over all the delegations
operationAtDelegation := func(_ int64, del sdk.Delegation) (stop bool) {
valAddr := del.GetValidator()
delInfo := k.GetDelegationDistInfo(ctx, delAddr, valAddr)
valInfo := k.GetValidatorDistInfo(ctx, valAddr)
validator := k.stakeKeeper.Validator(ctx, valAddr)
delegation := k.stakeKeeper.Delegation(ctx, delAddr, valAddr)
delInfo, valInfo, feePool, diWithdraw := delInfo.WithdrawRewards(feePool, valInfo, height, bondedTokens,
validator.GetTokens(), validator.GetDelegatorShares(), delegation.GetShares(), validator.GetCommission())
withdraw = withdraw.Plus(diWithdraw)
k.SetFeePool(ctx, feePool)
k.SetValidatorDistInfo(ctx, valInfo)
k.SetDelegationDistInfo(ctx, delInfo)
return false
}
k.stakeKeeper.IterateDelegations(ctx, delAddr, operationAtDelegation)
k.SetFeePool(ctx, feePool)
return withdraw
}

View File

@ -0,0 +1,250 @@
package keeper
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/stretchr/testify/require"
)
func TestWithdrawDelegationRewardBasic(t *testing.T) {
ctx, accMapper, keeper, sk, fck := CreateTestInputAdvanced(t, false, 100, sdk.ZeroDec())
stakeHandler := stake.NewHandler(sk)
denom := sk.GetParams(ctx).BondDenom
//first make a validator
msgCreateValidator := stake.NewTestMsgCreateValidator(valOpAddr1, valConsPk1, 10)
got := stakeHandler(ctx, msgCreateValidator)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
_ = sk.ApplyAndReturnValidatorSetUpdates(ctx)
// delegate
msgDelegate := stake.NewTestMsgDelegate(delAddr1, valOpAddr1, 10)
got = stakeHandler(ctx, msgDelegate)
require.True(t, got.IsOK())
amt := accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
require.Equal(t, int64(90), amt.Int64())
// allocate 100 denom of fees
feeInputs := sdk.NewInt(100)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
// withdraw delegation
ctx = ctx.WithBlockHeight(1)
keeper.WithdrawDelegationReward(ctx, delAddr1, valOpAddr1)
amt = accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
expRes := sdk.NewDec(90).Add(sdk.NewDec(100).Quo(sdk.NewDec(2))).TruncateInt() // 90 + 100 tokens * 10/20
require.True(sdk.IntEq(t, expRes, amt))
}
func TestWithdrawDelegationRewardWithCommission(t *testing.T) {
ctx, accMapper, keeper, sk, fck := CreateTestInputAdvanced(t, false, 100, sdk.ZeroDec())
stakeHandler := stake.NewHandler(sk)
denom := sk.GetParams(ctx).BondDenom
//first make a validator with 10% commission
msgCreateValidator := stake.NewTestMsgCreateValidatorWithCommission(
valOpAddr1, valConsPk1, 10, sdk.NewDecWithPrec(1, 1))
got := stakeHandler(ctx, msgCreateValidator)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
_ = sk.ApplyAndReturnValidatorSetUpdates(ctx)
// delegate
msgDelegate := stake.NewTestMsgDelegate(delAddr1, valOpAddr1, 10)
got = stakeHandler(ctx, msgDelegate)
require.True(t, got.IsOK())
amt := accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
require.Equal(t, int64(90), amt.Int64())
// allocate 100 denom of fees
feeInputs := sdk.NewInt(100)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
// withdraw delegation
ctx = ctx.WithBlockHeight(1)
keeper.WithdrawDelegationReward(ctx, delAddr1, valOpAddr1)
amt = accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
expRes := sdk.NewDec(90).Add(sdk.NewDec(90).Quo(sdk.NewDec(2))).TruncateInt() // 90 + 100*90% tokens * 10/20
require.True(sdk.IntEq(t, expRes, amt))
}
func TestWithdrawDelegationRewardTwoDelegators(t *testing.T) {
ctx, accMapper, keeper, sk, fck := CreateTestInputAdvanced(t, false, 100, sdk.ZeroDec())
stakeHandler := stake.NewHandler(sk)
denom := sk.GetParams(ctx).BondDenom
//first make a validator with 10% commission
msgCreateValidator := stake.NewTestMsgCreateValidatorWithCommission(
valOpAddr1, valConsPk1, 10, sdk.NewDecWithPrec(1, 1))
got := stakeHandler(ctx, msgCreateValidator)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
_ = sk.ApplyAndReturnValidatorSetUpdates(ctx)
// delegate
msgDelegate := stake.NewTestMsgDelegate(delAddr1, valOpAddr1, 10)
got = stakeHandler(ctx, msgDelegate)
require.True(t, got.IsOK())
amt := accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
require.Equal(t, int64(90), amt.Int64())
msgDelegate = stake.NewTestMsgDelegate(delAddr2, valOpAddr1, 20)
got = stakeHandler(ctx, msgDelegate)
require.True(t, got.IsOK())
amt = accMapper.GetAccount(ctx, delAddr2).GetCoins().AmountOf(denom)
require.Equal(t, int64(80), amt.Int64())
// allocate 100 denom of fees
feeInputs := sdk.NewInt(100)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
// delegator 1 withdraw delegation
ctx = ctx.WithBlockHeight(1)
keeper.WithdrawDelegationReward(ctx, delAddr1, valOpAddr1)
amt = accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
expRes := sdk.NewDec(90).Add(sdk.NewDec(90).Quo(sdk.NewDec(4))).TruncateInt() // 90 + 100*90% tokens * 10/40
require.True(sdk.IntEq(t, expRes, amt))
}
// this test demonstrates how two delegators with the same power can end up
// with different rewards in the end
func TestWithdrawDelegationRewardTwoDelegatorsUneven(t *testing.T) {
ctx, accMapper, keeper, sk, fck := CreateTestInputAdvanced(t, false, 100, sdk.ZeroDec())
stakeHandler := stake.NewHandler(sk)
denom := sk.GetParams(ctx).BondDenom
//first make a validator with no commission
msgCreateValidator := stake.NewTestMsgCreateValidatorWithCommission(
valOpAddr1, valConsPk1, 10, sdk.ZeroDec())
got := stakeHandler(ctx, msgCreateValidator)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
_ = sk.ApplyAndReturnValidatorSetUpdates(ctx)
// delegate
msgDelegate := stake.NewTestMsgDelegate(delAddr1, valOpAddr1, 10)
got = stakeHandler(ctx, msgDelegate)
require.True(t, got.IsOK())
amt := accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
require.Equal(t, int64(90), amt.Int64())
msgDelegate = stake.NewTestMsgDelegate(delAddr2, valOpAddr1, 10)
got = stakeHandler(ctx, msgDelegate)
require.True(t, got.IsOK())
amt = accMapper.GetAccount(ctx, delAddr2).GetCoins().AmountOf(denom)
require.Equal(t, int64(90), amt.Int64())
// allocate 100 denom of fees
feeInputs := sdk.NewInt(90)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
ctx = ctx.WithBlockHeight(1)
// delegator 1 withdraw delegation early, delegator 2 just keeps it's accum
keeper.WithdrawDelegationReward(ctx, delAddr1, valOpAddr1)
amt = accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
expRes1 := sdk.NewDec(90).Add(sdk.NewDec(90).Quo(sdk.NewDec(3))).TruncateInt() // 90 + 100 * 10/30
require.True(sdk.IntEq(t, expRes1, amt))
// allocate 200 denom of fees
feeInputs = sdk.NewInt(180)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
ctx = ctx.WithBlockHeight(2)
// delegator 2 now withdraws everything it's entitled to
keeper.WithdrawDelegationReward(ctx, delAddr2, valOpAddr1)
amt = accMapper.GetAccount(ctx, delAddr2).GetCoins().AmountOf(denom)
// existingTokens + (100+200 * (10/(20+30))
withdrawnFromVal := sdk.NewDec(60 + 180).Mul(sdk.NewDec(2)).Quo(sdk.NewDec(5))
expRes2 := sdk.NewDec(90).Add(withdrawnFromVal).TruncateInt()
require.True(sdk.IntEq(t, expRes2, amt))
// finally delegator 1 withdraws the remainder of its reward
keeper.WithdrawDelegationReward(ctx, delAddr1, valOpAddr1)
amt = accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
remainingInVal := sdk.NewDec(60 + 180).Sub(withdrawnFromVal)
expRes3 := sdk.NewDecFromInt(expRes1).Add(remainingInVal.Mul(sdk.NewDec(1)).Quo(sdk.NewDec(3))).TruncateInt()
require.True(sdk.IntEq(t, expRes3, amt))
// verify the final withdraw amounts are different
require.True(t, expRes2.GT(expRes3))
}
func TestWithdrawDelegationRewardsAll(t *testing.T) {
ctx, accMapper, keeper, sk, fck := CreateTestInputAdvanced(t, false, 100, sdk.ZeroDec())
stakeHandler := stake.NewHandler(sk)
denom := sk.GetParams(ctx).BondDenom
//make some validators with different commissions
msgCreateValidator := stake.NewTestMsgCreateValidatorWithCommission(
valOpAddr1, valConsPk1, 10, sdk.NewDecWithPrec(1, 1))
got := stakeHandler(ctx, msgCreateValidator)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
msgCreateValidator = stake.NewTestMsgCreateValidatorWithCommission(
valOpAddr2, valConsPk2, 50, sdk.NewDecWithPrec(2, 1))
got = stakeHandler(ctx, msgCreateValidator)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
msgCreateValidator = stake.NewTestMsgCreateValidatorWithCommission(
valOpAddr3, valConsPk3, 40, sdk.NewDecWithPrec(3, 1))
got = stakeHandler(ctx, msgCreateValidator)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
_ = sk.ApplyAndReturnValidatorSetUpdates(ctx)
// delegate to all the validators
msgDelegate := stake.NewTestMsgDelegate(delAddr1, valOpAddr1, 10)
require.True(t, stakeHandler(ctx, msgDelegate).IsOK())
msgDelegate = stake.NewTestMsgDelegate(delAddr1, valOpAddr2, 20)
require.True(t, stakeHandler(ctx, msgDelegate).IsOK())
msgDelegate = stake.NewTestMsgDelegate(delAddr1, valOpAddr3, 30)
require.True(t, stakeHandler(ctx, msgDelegate).IsOK())
// 40 tokens left after delegating 60 of them
amt := accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
require.Equal(t, int64(40), amt.Int64())
// total power of each validator:
// validator 1: 10 (self) + 10 (delegator) = 20
// validator 2: 50 (self) + 20 (delegator) = 70
// validator 3: 40 (self) + 30 (delegator) = 70
// grand total: 160
// allocate 100 denom of fees
feeInputs := sdk.NewInt(1000)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
// withdraw delegation
ctx = ctx.WithBlockHeight(1)
keeper.WithdrawDelegationRewardsAll(ctx, delAddr1)
amt = accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
// orig-amount + fees *(1-proposerReward)* (val1Portion * delegatorPotion * (1-val1Commission) ... etc)
// + fees *(proposerReward) * (delegatorPotion * (1-val1Commission))
// 40 + 1000 *(1- 0.95)* (20/160 * 10/20 * 0.9 + 70/160 * 20/70 * 0.8 + 70/160 * 30/70 * 0.7)
// 40 + 1000 *( 0.05) * (10/20 * 0.9)
feesInNonProposer := sdk.NewDecFromInt(feeInputs).Mul(sdk.NewDecWithPrec(95, 2))
feesInProposer := sdk.NewDecFromInt(feeInputs).Mul(sdk.NewDecWithPrec(5, 2))
feesInVal1 := feesInNonProposer.Mul(sdk.NewDec(10).Quo(sdk.NewDec(160))).Mul(sdk.NewDecWithPrec(9, 1))
feesInVal2 := feesInNonProposer.Mul(sdk.NewDec(20).Quo(sdk.NewDec(160))).Mul(sdk.NewDecWithPrec(8, 1))
feesInVal3 := feesInNonProposer.Mul(sdk.NewDec(30).Quo(sdk.NewDec(160))).Mul(sdk.NewDecWithPrec(7, 1))
feesInVal1Proposer := feesInProposer.Mul(sdk.NewDec(10).Quo(sdk.NewDec(20))).Mul(sdk.NewDecWithPrec(9, 1))
expRes := sdk.NewDec(40).Add(feesInVal1).Add(feesInVal2).Add(feesInVal3).Add(feesInVal1Proposer).TruncateInt()
require.True(sdk.IntEq(t, expRes, amt))
}

View File

@ -0,0 +1,50 @@
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
// Get the set of all validator-distribution-info's with no limits, used during genesis dump
func (k Keeper) GetAllValidatorDistInfos(ctx sdk.Context) (vdis []types.ValidatorDistInfo) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, ValidatorDistInfoKey)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var vdi types.ValidatorDistInfo
k.cdc.MustUnmarshalBinary(iterator.Value(), &vdi)
vdis = append(vdis, vdi)
}
return vdis
}
// Get the set of all delegator-distribution-info's with no limits, used during genesis dump
func (k Keeper) GetAllDelegationDistInfos(ctx sdk.Context) (ddis []types.DelegationDistInfo) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, DelegationDistInfoKey)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var ddi types.DelegationDistInfo
k.cdc.MustUnmarshalBinary(iterator.Value(), &ddi)
ddis = append(ddis, ddi)
}
return ddis
}
// Get the set of all delegator-withdraw addresses with no limits, used during genesis dump
func (k Keeper) GetAllDelegatorWithdrawInfos(ctx sdk.Context) (dwis []types.DelegatorWithdrawInfo) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, DelegationDistInfoKey)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
dw := types.DelegatorWithdrawInfo{
DelegatorAddr: sdk.AccAddress(iterator.Key()),
WithdrawAddr: sdk.AccAddress(iterator.Value()),
}
dwis = append(dwis, dw)
}
return dwis
}

View File

@ -0,0 +1,97 @@
package keeper
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
// Create a new validator distribution record
func (k Keeper) onValidatorCreated(ctx sdk.Context, addr sdk.ValAddress) {
height := ctx.BlockHeight()
vdi := types.ValidatorDistInfo{
OperatorAddr: addr,
FeePoolWithdrawalHeight: height,
Pool: types.DecCoins{},
PoolCommission: types.DecCoins{},
DelAccum: types.NewTotalAccum(height),
}
k.SetValidatorDistInfo(ctx, vdi)
}
// Withdrawal all validator rewards
func (k Keeper) onValidatorCommissionChange(ctx sdk.Context, addr sdk.ValAddress) {
k.WithdrawValidatorRewardsAll(ctx, addr)
}
// Withdrawal all validator distribution rewards and cleanup the distribution record
func (k Keeper) onValidatorRemoved(ctx sdk.Context, addr sdk.ValAddress) {
k.RemoveValidatorDistInfo(ctx, addr)
}
//_________________________________________________________________________________________
// Create a new delegator distribution record
func (k Keeper) onDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress,
valAddr sdk.ValAddress) {
ddi := types.DelegationDistInfo{
DelegatorAddr: delAddr,
ValOperatorAddr: valAddr,
WithdrawalHeight: ctx.BlockHeight(),
}
k.SetDelegationDistInfo(ctx, ddi)
ctx.Logger().With("module", "x/distribution").Error(fmt.Sprintf("ddi created: %v", ddi))
}
// Withdrawal all validator rewards
func (k Keeper) onDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress,
valAddr sdk.ValAddress) {
k.WithdrawDelegationReward(ctx, delAddr, valAddr)
}
// Withdrawal all validator distribution rewards and cleanup the distribution record
func (k Keeper) onDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress,
valAddr sdk.ValAddress) {
k.RemoveDelegationDistInfo(ctx, delAddr, valAddr)
}
//_________________________________________________________________________________________
// Wrapper struct
type Hooks struct {
k Keeper
}
var _ sdk.StakingHooks = Hooks{}
// New Validator Hooks
func (k Keeper) Hooks() Hooks { return Hooks{k} }
// nolint
func (h Hooks) OnValidatorCreated(ctx sdk.Context, addr sdk.ValAddress) {
h.k.onValidatorCreated(ctx, addr)
}
func (h Hooks) OnValidatorCommissionChange(ctx sdk.Context, addr sdk.ValAddress) {
h.k.onValidatorCommissionChange(ctx, addr)
}
func (h Hooks) OnValidatorRemoved(ctx sdk.Context, addr sdk.ValAddress) {
h.k.onValidatorRemoved(ctx, addr)
}
func (h Hooks) OnDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
h.k.onDelegationCreated(ctx, delAddr, valAddr)
}
func (h Hooks) OnDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
h.k.onDelegationSharesModified(ctx, delAddr, valAddr)
}
func (h Hooks) OnDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
h.k.onDelegationRemoved(ctx, delAddr, valAddr)
}
// nolint - unused hooks for interface
func (h Hooks) OnValidatorBonded(ctx sdk.Context, addr sdk.ConsAddress) {}
func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, addr sdk.ConsAddress) {}

View File

@ -0,0 +1,129 @@
package keeper
import (
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
"github.com/cosmos/cosmos-sdk/x/params"
)
// keeper of the stake store
type Keeper struct {
storeKey sdk.StoreKey
cdc *codec.Codec
paramSpace params.Subspace
bankKeeper types.BankKeeper
stakeKeeper types.StakeKeeper
feeCollectionKeeper types.FeeCollectionKeeper
// codespace
codespace sdk.CodespaceType
}
func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramSpace params.Subspace, ck types.BankKeeper,
sk types.StakeKeeper, fck types.FeeCollectionKeeper, codespace sdk.CodespaceType) Keeper {
keeper := Keeper{
storeKey: key,
cdc: cdc,
paramSpace: paramSpace.WithTypeTable(ParamTypeTable()),
bankKeeper: ck,
stakeKeeper: sk,
feeCollectionKeeper: fck,
codespace: codespace,
}
return keeper
}
//______________________________________________________________________
// get the global fee pool distribution info
func (k Keeper) GetFeePool(ctx sdk.Context) (feePool types.FeePool) {
store := ctx.KVStore(k.storeKey)
b := store.Get(FeePoolKey)
if b == nil {
panic("Stored fee pool should not have been nil")
}
k.cdc.MustUnmarshalBinary(b, &feePool)
return
}
// set the global fee pool distribution info
func (k Keeper) SetFeePool(ctx sdk.Context, feePool types.FeePool) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshalBinary(feePool)
store.Set(FeePoolKey, b)
}
//______________________________________________________________________
// set the proposer public key for this block
func (k Keeper) GetPreviousProposerConsAddr(ctx sdk.Context) (consAddr sdk.ConsAddress) {
store := ctx.KVStore(k.storeKey)
b := store.Get(ProposerKey)
if b == nil {
panic("Previous proposer not set")
}
k.cdc.MustUnmarshalBinary(b, &consAddr)
return
}
// get the proposer public key for this block
func (k Keeper) SetPreviousProposerConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshalBinary(consAddr)
store.Set(ProposerKey, b)
}
//______________________________________________________________________
// PARAM STORE
// Type declaration for parameters
func ParamTypeTable() params.TypeTable {
return params.NewTypeTable(
ParamStoreKeyCommunityTax, sdk.Dec{},
ParamStoreKeyBaseProposerReward, sdk.Dec{},
ParamStoreKeyBonusProposerReward, sdk.Dec{},
)
}
// Returns the current CommunityTax rate from the global param store
// nolint: errcheck
func (k Keeper) GetCommunityTax(ctx sdk.Context) sdk.Dec {
var percent sdk.Dec
k.paramSpace.Get(ctx, ParamStoreKeyCommunityTax, &percent)
return percent
}
// nolint: errcheck
func (k Keeper) SetCommunityTax(ctx sdk.Context, percent sdk.Dec) {
k.paramSpace.Set(ctx, ParamStoreKeyCommunityTax, &percent)
}
// Returns the current BaseProposerReward rate from the global param store
// nolint: errcheck
func (k Keeper) GetBaseProposerReward(ctx sdk.Context) sdk.Dec {
var percent sdk.Dec
k.paramSpace.Get(ctx, ParamStoreKeyBaseProposerReward, &percent)
return percent
}
// nolint: errcheck
func (k Keeper) SetBaseProposerReward(ctx sdk.Context, percent sdk.Dec) {
k.paramSpace.Set(ctx, ParamStoreKeyBaseProposerReward, &percent)
}
// Returns the current BaseProposerReward rate from the global param store
// nolint: errcheck
func (k Keeper) GetBonusProposerReward(ctx sdk.Context) sdk.Dec {
var percent sdk.Dec
k.paramSpace.Get(ctx, ParamStoreKeyBonusProposerReward, &percent)
return percent
}
// nolint: errcheck
func (k Keeper) SetBonusProposerReward(ctx sdk.Context, percent sdk.Dec) {
k.paramSpace.Set(ctx, ParamStoreKeyBonusProposerReward, &percent)
}

View File

@ -0,0 +1,37 @@
package keeper
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
"github.com/stretchr/testify/require"
)
func TestSetGetPreviousProposerConsAddr(t *testing.T) {
ctx, _, keeper, _, _ := CreateTestInputDefault(t, false, 0)
keeper.SetPreviousProposerConsAddr(ctx, valConsAddr1)
res := keeper.GetPreviousProposerConsAddr(ctx)
require.True(t, res.Equals(valConsAddr1), "expected: %v got: %v", valConsAddr1.String(), res.String())
}
func TestSetGetCommunityTax(t *testing.T) {
ctx, _, keeper, _, _ := CreateTestInputDefault(t, false, 0)
someDec := sdk.NewDec(333)
keeper.SetCommunityTax(ctx, someDec)
res := keeper.GetCommunityTax(ctx)
require.True(sdk.DecEq(t, someDec, res))
}
func TestSetGetFeePool(t *testing.T) {
ctx, _, keeper, _, _ := CreateTestInputDefault(t, false, 0)
fp := types.InitialFeePool()
fp.ValAccum.UpdateHeight = 777
keeper.SetFeePool(ctx, fp)
res := keeper.GetFeePool(ctx)
require.Equal(t, fp.ValAccum, res.ValAccum)
}

View File

@ -0,0 +1,46 @@
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// keys/key-prefixes
var (
FeePoolKey = []byte{0x00} // key for global distribution state
ValidatorDistInfoKey = []byte{0x01} // prefix for each key to a validator distribution
DelegationDistInfoKey = []byte{0x02} // prefix for each key to a delegation distribution
DelegatorWithdrawInfoKey = []byte{0x03} // prefix for each key to a delegator withdraw info
ProposerKey = []byte{0x04} // key for storing the proposer operator address
// params store
ParamStoreKeyCommunityTax = []byte("communitytax")
ParamStoreKeyBaseProposerReward = []byte("baseproposerreward")
ParamStoreKeyBonusProposerReward = []byte("bonusproposerreward")
)
const (
// default paramspace for params keeper
DefaultParamspace = "distr"
)
// gets the key for the validator distribution info from address
// VALUE: distribution/types.ValidatorDistInfo
func GetValidatorDistInfoKey(operatorAddr sdk.ValAddress) []byte {
return append(ValidatorDistInfoKey, operatorAddr.Bytes()...)
}
// gets the key for delegator distribution for a validator
// VALUE: distribution/types.DelegationDistInfo
func GetDelegationDistInfoKey(delAddr sdk.AccAddress, valAddr sdk.ValAddress) []byte {
return append(GetDelegationDistInfosKey(delAddr), valAddr.Bytes()...)
}
// gets the prefix for a delegator's distributions across all validators
func GetDelegationDistInfosKey(delAddr sdk.AccAddress) []byte {
return append(DelegationDistInfoKey, delAddr.Bytes()...)
}
// gets the prefix for a delegator's withdraw info
func GetDelegatorWithdrawAddrKey(delAddr sdk.AccAddress) []byte {
return append(DelegatorWithdrawInfoKey, delAddr.Bytes()...)
}

View File

@ -0,0 +1,161 @@
package keeper
import (
"testing"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
var (
delPk1 = ed25519.GenPrivKey().PubKey()
delPk2 = ed25519.GenPrivKey().PubKey()
delPk3 = ed25519.GenPrivKey().PubKey()
delAddr1 = sdk.AccAddress(delPk1.Address())
delAddr2 = sdk.AccAddress(delPk2.Address())
delAddr3 = sdk.AccAddress(delPk3.Address())
valOpPk1 = ed25519.GenPrivKey().PubKey()
valOpPk2 = ed25519.GenPrivKey().PubKey()
valOpPk3 = ed25519.GenPrivKey().PubKey()
valOpAddr1 = sdk.ValAddress(valOpPk1.Address())
valOpAddr2 = sdk.ValAddress(valOpPk2.Address())
valOpAddr3 = sdk.ValAddress(valOpPk3.Address())
valAccAddr1 = sdk.AccAddress(valOpPk1.Address()) // generate acc addresses for these validator keys too
valAccAddr2 = sdk.AccAddress(valOpPk2.Address())
valAccAddr3 = sdk.AccAddress(valOpPk3.Address())
valConsPk1 = ed25519.GenPrivKey().PubKey()
valConsPk2 = ed25519.GenPrivKey().PubKey()
valConsPk3 = ed25519.GenPrivKey().PubKey()
valConsAddr1 = sdk.ConsAddress(valConsPk1.Address())
valConsAddr2 = sdk.ConsAddress(valConsPk2.Address())
valConsAddr3 = sdk.ConsAddress(valConsPk3.Address())
addrs = []sdk.AccAddress{
delAddr1, delAddr2, delAddr3,
valAccAddr1, valAccAddr2, valAccAddr3,
}
emptyDelAddr sdk.AccAddress
emptyValAddr sdk.ValAddress
emptyPubkey crypto.PubKey
)
// create a codec used only for testing
func MakeTestCodec() *codec.Codec {
var cdc = codec.New()
bank.RegisterCodec(cdc)
stake.RegisterCodec(cdc)
auth.RegisterCodec(cdc)
sdk.RegisterCodec(cdc)
codec.RegisterCrypto(cdc)
types.RegisterCodec(cdc) // distr
return cdc
}
// test input with default values
func CreateTestInputDefault(t *testing.T, isCheckTx bool, initCoins int64) (
sdk.Context, auth.AccountMapper, Keeper, stake.Keeper, DummyFeeCollectionKeeper) {
communityTax := sdk.NewDecWithPrec(2, 2)
return CreateTestInputAdvanced(t, isCheckTx, initCoins, communityTax)
}
// hogpodge of all sorts of input required for testing
func CreateTestInputAdvanced(t *testing.T, isCheckTx bool, initCoins int64,
communityTax sdk.Dec) (
sdk.Context, auth.AccountMapper, Keeper, stake.Keeper, DummyFeeCollectionKeeper) {
keyDistr := sdk.NewKVStoreKey("distr")
keyStake := sdk.NewKVStoreKey("stake")
tkeyStake := sdk.NewTransientStoreKey("transient_stake")
keyAcc := sdk.NewKVStoreKey("acc")
keyFeeCollection := sdk.NewKVStoreKey("fee")
keyParams := sdk.NewKVStoreKey("params")
tkeyParams := sdk.NewTransientStoreKey("transient_params")
db := dbm.NewMemDB()
ms := store.NewCommitMultiStore(db)
ms.MountStoreWithDB(keyDistr, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(tkeyStake, sdk.StoreTypeTransient, nil)
ms.MountStoreWithDB(keyStake, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keyFeeCollection, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db)
err := ms.LoadLatestVersion()
require.Nil(t, err)
cdc := MakeTestCodec()
pk := params.NewKeeper(cdc, keyParams, tkeyParams)
ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, log.NewNopLogger())
accountMapper := auth.NewAccountMapper(cdc, keyAcc, auth.ProtoBaseAccount)
ck := bank.NewBaseKeeper(accountMapper)
sk := stake.NewKeeper(cdc, keyStake, tkeyStake, ck, pk.Subspace(stake.DefaultParamspace), stake.DefaultCodespace)
sk.SetPool(ctx, stake.InitialPool())
sk.SetParams(ctx, stake.DefaultParams())
sk.InitIntraTxCounter(ctx)
// fill all the addresses with some coins, set the loose pool tokens simultaneously
for _, addr := range addrs {
pool := sk.GetPool(ctx)
_, _, err := ck.AddCoins(ctx, addr, sdk.Coins{
{sk.GetParams(ctx).BondDenom, sdk.NewInt(initCoins)},
})
require.Nil(t, err)
pool.LooseTokens = pool.LooseTokens.Add(sdk.NewDec(initCoins))
sk.SetPool(ctx, pool)
}
fck := DummyFeeCollectionKeeper{}
keeper := NewKeeper(cdc, keyDistr, pk.Subspace(DefaultParamspace), ck, sk, fck, types.DefaultCodespace)
// set the distribution hooks on staking
sk = sk.WithHooks(keeper.Hooks())
// set genesis items required for distribution
keeper.SetFeePool(ctx, types.InitialFeePool())
keeper.SetCommunityTax(ctx, communityTax)
keeper.SetBaseProposerReward(ctx, sdk.NewDecWithPrec(1, 2))
keeper.SetBonusProposerReward(ctx, sdk.NewDecWithPrec(4, 2))
return ctx, accountMapper, keeper, sk, fck
}
//__________________________________________________________________________________
// fee collection keeper used only for testing
type DummyFeeCollectionKeeper struct{}
var heldFees sdk.Coins
var _ types.FeeCollectionKeeper = DummyFeeCollectionKeeper{}
// nolint
func (fck DummyFeeCollectionKeeper) GetCollectedFees(_ sdk.Context) sdk.Coins {
return heldFees
}
func (fck DummyFeeCollectionKeeper) SetCollectedFees(in sdk.Coins) {
heldFees = in
}
func (fck DummyFeeCollectionKeeper) ClearCollectedFees(_ sdk.Context) {
heldFees = sdk.Coins{}
}

View File

@ -0,0 +1,60 @@
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
// get the validator distribution info
func (k Keeper) GetValidatorDistInfo(ctx sdk.Context,
operatorAddr sdk.ValAddress) (vdi types.ValidatorDistInfo) {
store := ctx.KVStore(k.storeKey)
b := store.Get(GetValidatorDistInfoKey(operatorAddr))
if b == nil {
panic("Stored validator-distribution info should not have been nil")
}
k.cdc.MustUnmarshalBinary(b, &vdi)
return
}
// set the validator distribution info
func (k Keeper) SetValidatorDistInfo(ctx sdk.Context, vdi types.ValidatorDistInfo) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshalBinary(vdi)
store.Set(GetValidatorDistInfoKey(vdi.OperatorAddr), b)
}
// remove a validator distribution info
func (k Keeper) RemoveValidatorDistInfo(ctx sdk.Context, valAddr sdk.ValAddress) {
store := ctx.KVStore(k.storeKey)
store.Delete(GetValidatorDistInfoKey(valAddr))
}
// withdrawal all the validator rewards including the commission
func (k Keeper) WithdrawValidatorRewardsAll(ctx sdk.Context, operatorAddr sdk.ValAddress) {
// withdraw self-delegation
height := ctx.BlockHeight()
validator := k.stakeKeeper.Validator(ctx, operatorAddr)
accAddr := sdk.AccAddress(operatorAddr.Bytes())
withdraw := k.getDelegatorRewardsAll(ctx, accAddr, height)
// withdrawal validator commission rewards
bondedTokens := k.stakeKeeper.TotalPower(ctx)
valInfo := k.GetValidatorDistInfo(ctx, operatorAddr)
feePool := k.GetFeePool(ctx)
valInfo, feePool, commission := valInfo.WithdrawCommission(feePool, height, bondedTokens,
validator.GetTokens(), validator.GetCommission())
withdraw = withdraw.Plus(commission)
k.SetValidatorDistInfo(ctx, valInfo)
k.SetFeePool(ctx, feePool)
withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, accAddr)
_, _, err := k.bankKeeper.AddCoins(ctx, withdrawAddr, withdraw.TruncateDecimal())
if err != nil {
panic(err)
}
}

View File

@ -0,0 +1,192 @@
package keeper
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/stretchr/testify/require"
)
func TestWithdrawValidatorRewardsAllNoDelegator(t *testing.T) {
ctx, accMapper, keeper, sk, fck := CreateTestInputAdvanced(t, false, 100, sdk.ZeroDec())
stakeHandler := stake.NewHandler(sk)
denom := sk.GetParams(ctx).BondDenom
//first make a validator
msgCreateValidator := stake.NewTestMsgCreateValidator(valOpAddr1, valConsPk1, 10)
got := stakeHandler(ctx, msgCreateValidator)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
_ = sk.ApplyAndReturnValidatorSetUpdates(ctx)
// allocate 100 denom of fees
feeInputs := sdk.NewInt(100)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
// withdraw self-delegation reward
ctx = ctx.WithBlockHeight(1)
keeper.WithdrawValidatorRewardsAll(ctx, valOpAddr1)
amt := accMapper.GetAccount(ctx, valAccAddr1).GetCoins().AmountOf(denom)
expRes := sdk.NewDec(90).Add(sdk.NewDec(100)).TruncateInt()
require.True(sdk.IntEq(t, expRes, amt))
}
func TestWithdrawValidatorRewardsAllDelegatorNoCommission(t *testing.T) {
ctx, accMapper, keeper, sk, fck := CreateTestInputAdvanced(t, false, 100, sdk.ZeroDec())
stakeHandler := stake.NewHandler(sk)
denom := sk.GetParams(ctx).BondDenom
//first make a validator
msgCreateValidator := stake.NewTestMsgCreateValidator(valOpAddr1, valConsPk1, 10)
got := stakeHandler(ctx, msgCreateValidator)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
_ = sk.ApplyAndReturnValidatorSetUpdates(ctx)
// delegate
msgDelegate := stake.NewTestMsgDelegate(delAddr1, valOpAddr1, 10)
got = stakeHandler(ctx, msgDelegate)
require.True(t, got.IsOK())
amt := accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
require.Equal(t, int64(90), amt.Int64())
// allocate 100 denom of fees
feeInputs := sdk.NewInt(100)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
// withdraw self-delegation reward
ctx = ctx.WithBlockHeight(1)
keeper.WithdrawValidatorRewardsAll(ctx, valOpAddr1)
amt = accMapper.GetAccount(ctx, valAccAddr1).GetCoins().AmountOf(denom)
expRes := sdk.NewDec(90).Add(sdk.NewDec(100).Quo(sdk.NewDec(2))).TruncateInt() // 90 + 100 tokens * 10/20
require.True(sdk.IntEq(t, expRes, amt))
}
func TestWithdrawValidatorRewardsAllDelegatorWithCommission(t *testing.T) {
ctx, accMapper, keeper, sk, fck := CreateTestInputAdvanced(t, false, 100, sdk.ZeroDec())
stakeHandler := stake.NewHandler(sk)
denom := sk.GetParams(ctx).BondDenom
//first make a validator
commissionRate := sdk.NewDecWithPrec(1, 1)
msgCreateValidator := stake.NewTestMsgCreateValidatorWithCommission(
valOpAddr1, valConsPk1, 10, commissionRate)
got := stakeHandler(ctx, msgCreateValidator)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
_ = sk.ApplyAndReturnValidatorSetUpdates(ctx)
// delegate
msgDelegate := stake.NewTestMsgDelegate(delAddr1, valOpAddr1, 10)
got = stakeHandler(ctx, msgDelegate)
require.True(t, got.IsOK())
amt := accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
require.Equal(t, int64(90), amt.Int64())
// allocate 100 denom of fees
feeInputs := sdk.NewInt(100)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
// withdraw validator reward
ctx = ctx.WithBlockHeight(1)
keeper.WithdrawValidatorRewardsAll(ctx, valOpAddr1)
amt = accMapper.GetAccount(ctx, valAccAddr1).GetCoins().AmountOf(denom)
commissionTaken := sdk.NewDec(100).Mul(commissionRate)
afterCommission := sdk.NewDec(100).Sub(commissionTaken)
selfDelegationReward := afterCommission.Quo(sdk.NewDec(2))
expRes := sdk.NewDec(90).Add(commissionTaken).Add(selfDelegationReward).TruncateInt() // 90 + 100 tokens * 10/20
require.True(sdk.IntEq(t, expRes, amt))
}
func TestWithdrawValidatorRewardsAllMultipleValidator(t *testing.T) {
ctx, accMapper, keeper, sk, fck := CreateTestInputAdvanced(t, false, 100, sdk.ZeroDec())
stakeHandler := stake.NewHandler(sk)
denom := sk.GetParams(ctx).BondDenom
//make some validators with different commissions
msgCreateValidator := stake.NewTestMsgCreateValidatorWithCommission(
valOpAddr1, valConsPk1, 10, sdk.NewDecWithPrec(1, 1))
got := stakeHandler(ctx, msgCreateValidator)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
msgCreateValidator = stake.NewTestMsgCreateValidatorWithCommission(
valOpAddr2, valConsPk2, 50, sdk.NewDecWithPrec(2, 1))
got = stakeHandler(ctx, msgCreateValidator)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
msgCreateValidator = stake.NewTestMsgCreateValidatorWithCommission(
valOpAddr3, valConsPk3, 40, sdk.NewDecWithPrec(3, 1))
got = stakeHandler(ctx, msgCreateValidator)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
_ = sk.ApplyAndReturnValidatorSetUpdates(ctx)
// allocate 100 denom of fees
feeInputs := sdk.NewInt(1000)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
// withdraw validator reward
ctx = ctx.WithBlockHeight(1)
keeper.WithdrawValidatorRewardsAll(ctx, valOpAddr1)
amt := accMapper.GetAccount(ctx, valAccAddr1).GetCoins().AmountOf(denom)
feesInNonProposer := sdk.NewDecFromInt(feeInputs).Mul(sdk.NewDecWithPrec(95, 2))
feesInProposer := sdk.NewDecFromInt(feeInputs).Mul(sdk.NewDecWithPrec(5, 2))
expRes := sdk.NewDec(90). // orig tokens (100 - 10)
Add(feesInNonProposer.Quo(sdk.NewDec(10))). // validator 1 has 1/10 total power
Add(feesInProposer).
TruncateInt()
require.True(sdk.IntEq(t, expRes, amt))
}
func TestWithdrawValidatorRewardsAllMultipleDelegator(t *testing.T) {
ctx, accMapper, keeper, sk, fck := CreateTestInputAdvanced(t, false, 100, sdk.ZeroDec())
stakeHandler := stake.NewHandler(sk)
denom := sk.GetParams(ctx).BondDenom
//first make a validator with 10% commission
commissionRate := sdk.NewDecWithPrec(1, 1)
msgCreateValidator := stake.NewTestMsgCreateValidatorWithCommission(
valOpAddr1, valConsPk1, 10, sdk.NewDecWithPrec(1, 1))
got := stakeHandler(ctx, msgCreateValidator)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
_ = sk.ApplyAndReturnValidatorSetUpdates(ctx)
// delegate
msgDelegate := stake.NewTestMsgDelegate(delAddr1, valOpAddr1, 10)
got = stakeHandler(ctx, msgDelegate)
require.True(t, got.IsOK())
amt := accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
require.Equal(t, int64(90), amt.Int64())
msgDelegate = stake.NewTestMsgDelegate(delAddr2, valOpAddr1, 20)
got = stakeHandler(ctx, msgDelegate)
require.True(t, got.IsOK())
amt = accMapper.GetAccount(ctx, delAddr2).GetCoins().AmountOf(denom)
require.Equal(t, int64(80), amt.Int64())
// allocate 100 denom of fees
feeInputs := sdk.NewInt(100)
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
// withdraw validator reward
ctx = ctx.WithBlockHeight(1)
keeper.WithdrawValidatorRewardsAll(ctx, valOpAddr1)
amt = accMapper.GetAccount(ctx, valAccAddr1).GetCoins().AmountOf(denom)
commissionTaken := sdk.NewDec(100).Mul(commissionRate)
afterCommission := sdk.NewDec(100).Sub(commissionTaken)
expRes := sdk.NewDec(90).
Add(afterCommission.Quo(sdk.NewDec(4))).
Add(commissionTaken).
TruncateInt() // 90 + 100*90% tokens * 10/40
require.True(sdk.IntEq(t, expRes, amt))
}

View File

@ -0,0 +1,17 @@
// nolint
package tags
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
var (
ActionModifyWithdrawAddress = []byte("modify-withdraw-address")
ActionWithdrawDelegatorRewardsAll = []byte("withdraw-delegator-rewards-all")
ActionWithdrawDelegatorReward = []byte("withdraw-delegator-reward")
ActionWithdrawValidatorRewardsAll = []byte("withdraw-validator-rewards-all")
Action = sdk.TagAction
Validator = sdk.TagSrcValidator
Delegator = sdk.TagDelegator
)

View File

@ -5,16 +5,16 @@ import (
)
// distribution info for a delegation - used to determine entitled rewards
type DelegatorDistInfo struct {
type DelegationDistInfo struct {
DelegatorAddr sdk.AccAddress `json:"delegator_addr"`
ValOperatorAddr sdk.ValAddress `json:"val_operator_addr"`
WithdrawalHeight int64 `json:"withdrawal_height"` // last time this delegation withdrew rewards
}
func NewDelegatorDistInfo(delegatorAddr sdk.AccAddress, valOperatorAddr sdk.ValAddress,
currentHeight int64) DelegatorDistInfo {
func NewDelegationDistInfo(delegatorAddr sdk.AccAddress, valOperatorAddr sdk.ValAddress,
currentHeight int64) DelegationDistInfo {
return DelegatorDistInfo{
return DelegationDistInfo{
DelegatorAddr: delegatorAddr,
ValOperatorAddr: valOperatorAddr,
WithdrawalHeight: currentHeight,
@ -22,9 +22,9 @@ func NewDelegatorDistInfo(delegatorAddr sdk.AccAddress, valOperatorAddr sdk.ValA
}
// withdraw rewards from delegator
func (di DelegatorDistInfo) WithdrawRewards(fp FeePool, vi ValidatorDistInfo,
func (di DelegationDistInfo) WithdrawRewards(fp FeePool, vi ValidatorDistInfo,
height int64, totalBonded, vdTokens, totalDelShares, delegatorShares,
commissionRate sdk.Dec) (DelegatorDistInfo, ValidatorDistInfo, FeePool, DecCoins) {
commissionRate sdk.Dec) (DelegationDistInfo, ValidatorDistInfo, FeePool, DecCoins) {
vi = vi.UpdateTotalDelAccum(height, totalDelShares)

View File

@ -18,10 +18,10 @@ func TestWithdrawRewards(t *testing.T) {
validatorDelShares := sdk.NewDec(10)
totalBondedTokens := validatorTokens.Add(sdk.NewDec(90)) // validator-1 is 10% of total power
di1 := NewDelegatorDistInfo(delAddr1, valAddr1, height)
di1 := NewDelegationDistInfo(delAddr1, valAddr1, height)
di1Shares := sdk.NewDec(5) // this delegator has half the shares in the validator
di2 := NewDelegatorDistInfo(delAddr2, valAddr1, height)
di2 := NewDelegationDistInfo(delAddr2, valAddr1, height)
di2Shares := sdk.NewDec(5)
// simulate adding some stake for inflation

View File

@ -12,22 +12,56 @@ type DelegatorWithdrawInfo struct {
// GenesisState - all distribution state that must be provided at genesis
type GenesisState struct {
FeePool FeePool `json:"fee_pool"`
CommunityTax sdk.Dec `json:"community_tax"`
BaseProposerReward sdk.Dec `json:"base_proposer_reward"`
BonusProposerReward sdk.Dec `json:"bonus_proposer_reward"`
ValidatorDistInfos []ValidatorDistInfo `json:"validator_dist_infos"`
DelegatorDistInfos []DelegatorDistInfo `json:"delegator_dist_infos"`
DelegationDistInfos []DelegationDistInfo `json:"delegator_dist_infos"`
DelegatorWithdrawInfos []DelegatorWithdrawInfo `json:"delegator_withdraw_infos"`
}
func NewGenesisState(feePool FeePool, vdis []ValidatorDistInfo, ddis []DelegatorDistInfo) GenesisState {
func NewGenesisState(feePool FeePool, communityTax, baseProposerReward, bonusProposerReward sdk.Dec,
vdis []ValidatorDistInfo, ddis []DelegationDistInfo, dwis []DelegatorWithdrawInfo) GenesisState {
return GenesisState{
FeePool: feePool,
ValidatorDistInfos: vdis,
DelegatorDistInfos: ddis,
FeePool: feePool,
CommunityTax: communityTax,
BaseProposerReward: baseProposerReward,
BonusProposerReward: bonusProposerReward,
ValidatorDistInfos: vdis,
DelegationDistInfos: ddis,
DelegatorWithdrawInfos: dwis,
}
}
// get raw genesis raw message for testing
func DefaultGenesisState() GenesisState {
return GenesisState{
FeePool: InitialFeePool(),
FeePool: InitialFeePool(),
CommunityTax: sdk.NewDecWithPrec(2, 2), // 2%
BaseProposerReward: sdk.NewDecWithPrec(1, 2), // 1%
BonusProposerReward: sdk.NewDecWithPrec(4, 2), // 4%
}
}
// default genesis utility function, initialize for starting validator set
func DefaultGenesisWithValidators(valAddrs []sdk.ValAddress) GenesisState {
vdis := make([]ValidatorDistInfo, len(valAddrs))
ddis := make([]DelegationDistInfo, len(valAddrs))
for i, valAddr := range valAddrs {
vdis[i] = NewValidatorDistInfo(valAddr, 0)
accAddr := sdk.AccAddress(valAddr)
ddis[i] = NewDelegationDistInfo(accAddr, valAddr, 0)
}
return GenesisState{
FeePool: InitialFeePool(),
CommunityTax: sdk.NewDecWithPrec(2, 2), // 2%
BaseProposerReward: sdk.NewDecWithPrec(1, 2), // 1%
BonusProposerReward: sdk.NewDecWithPrec(4, 2), // 4%
ValidatorDistInfos: vdis,
DelegationDistInfos: ddis,
}
}

View File

@ -18,9 +18,9 @@ var (
valPk1 = ed25519.GenPrivKey().PubKey()
valPk2 = ed25519.GenPrivKey().PubKey()
valPk3 = ed25519.GenPrivKey().PubKey()
valAddr1 = sdk.ValAddress(delPk1.Address())
valAddr2 = sdk.ValAddress(delPk2.Address())
valAddr3 = sdk.ValAddress(delPk3.Address())
valAddr1 = sdk.ValAddress(valPk1.Address())
valAddr2 = sdk.ValAddress(valPk2.Address())
valAddr3 = sdk.ValAddress(valPk3.Address())
emptyValAddr sdk.ValAddress
emptyPubkey crypto.PubKey

View File

@ -19,9 +19,9 @@ func NewValidatorDistInfo(operatorAddr sdk.ValAddress, currentHeight int64) Vali
return ValidatorDistInfo{
OperatorAddr: operatorAddr,
FeePoolWithdrawalHeight: currentHeight,
Pool: DecCoins{},
PoolCommission: DecCoins{},
DelAccum: NewTotalAccum(currentHeight),
Pool: DecCoins{},
PoolCommission: DecCoins{},
DelAccum: NewTotalAccum(currentHeight),
}
}

View File

@ -15,6 +15,7 @@ import (
"time"
abci "github.com/tendermint/tendermint/abci/types"
common "github.com/tendermint/tendermint/libs/common"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/baseapp"
@ -81,7 +82,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
// Initially this is the same as the initial validator set
nextValidators := validators
header := abci.Header{Height: 0, Time: timestamp}
header := abci.Header{Height: 0, Time: timestamp, ProposerAddress: randomProposer(r, validators)}
opCount := 0
// Setup code to catch SIGTERM's
@ -150,6 +151,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
res := app.EndBlock(abci.RequestEndBlock{})
header.Height++
header.Time = header.Time.Add(time.Duration(minTimePerBlock) * time.Second).Add(time.Duration(int64(r.Intn(int(timeDiff)))) * time.Second)
header.ProposerAddress = randomProposer(r, validators)
logWriter("EndBlock")
if testingMode {
@ -318,6 +320,21 @@ func getKeys(validators map[string]mockValidator) []string {
return keys
}
// randomProposer picks a random proposer from the current validator set
func randomProposer(r *rand.Rand, validators map[string]mockValidator) common.HexBytes {
keys := getKeys(validators)
if len(keys) == 0 {
return nil
}
key := keys[r.Intn(len(keys))]
proposer := validators[key].val
pk, err := tmtypes.PB2TM.PubKey(proposer.PubKey)
if err != nil {
panic(err)
}
return pk.Address()
}
// RandomRequestBeginBlock generates a list of signing validators according to the provided list of validators, signing fraction, and evidence fraction
// nolint: unparam
func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator, livenessTransitions TransitionMatrix, evidenceFraction float64,

View File

@ -46,11 +46,7 @@ func handleMsgUnjail(ctx sdk.Context, msg MsgUnjail, k Keeper) sdk.Result {
return ErrValidatorJailed(k.codespace).Result()
}
// update the starting height so the validator can't be immediately jailed
// again
info.StartHeight = ctx.BlockHeight()
k.setValidatorSigningInfo(ctx, consAddr, info)
// unjail the validator
k.validatorSet.Unjail(ctx, consAddr)
tags := sdk.NewTags("action", []byte("unjail"), "validator", []byte(msg.ValidatorAddr.String()))

View File

@ -16,7 +16,7 @@ func TestCannotUnjailUnlessJailed(t *testing.T) {
slh := NewHandler(keeper)
amtInt := int64(100)
addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
msg := newTestMsgCreateValidator(addr, val, amt)
msg := NewTestMsgCreateValidator(addr, val, amt)
got := stake.NewHandler(sk)(ctx, msg)
require.True(t, got.IsOK())
stake.EndBlocker(ctx, sk)
@ -41,7 +41,7 @@ func TestJailedValidatorDelegations(t *testing.T) {
valPubKey, bondAmount := pks[0], sdk.NewInt(amount)
valAddr, consAddr := addrs[1], sdk.ConsAddress(addrs[0])
msgCreateVal := newTestMsgCreateValidator(valAddr, valPubKey, bondAmount)
msgCreateVal := NewTestMsgCreateValidator(valAddr, valPubKey, bondAmount)
got := stake.NewHandler(stakeKeeper)(ctx, msgCreateVal)
require.True(t, got.IsOK(), "expected create validator msg to be ok, got: %v", got)
@ -53,7 +53,7 @@ func TestJailedValidatorDelegations(t *testing.T) {
StartHeight: int64(0),
IndexOffset: int64(0),
JailedUntil: time.Unix(0, 0),
SignedBlocksCounter: int64(0),
MissedBlocksCounter: int64(0),
}
slashingKeeper.setValidatorSigningInfo(ctx, consAddr, newInfo)

View File

@ -1,11 +1,25 @@
package slashing
import (
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Create a new slashing period when a validator is bonded
func (k Keeper) onValidatorBonded(ctx sdk.Context, address sdk.ConsAddress) {
// Update the signing info start height or create a new signing info
_, found := k.getValidatorSigningInfo(ctx, address)
if !found {
signingInfo := ValidatorSigningInfo{
StartHeight: ctx.BlockHeight(),
IndexOffset: 0,
JailedUntil: time.Unix(0, 0),
MissedBlocksCounter: 0,
}
k.setValidatorSigningInfo(ctx, address, signingInfo)
}
// Create a new slashing period when a validator is bonded
slashingPeriod := ValidatorSlashingPeriod{
ValidatorAddr: address,
StartHeight: ctx.BlockHeight(),

View File

@ -102,8 +102,7 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, p
// Will use the 0-value default signing info if not present, except for start height
signInfo, found := k.getValidatorSigningInfo(ctx, consAddr)
if !found {
// If this validator has never been seen before, construct a new SigningInfo with the correct start height
signInfo = NewValidatorSigningInfo(height, 0, time.Unix(0, 0), 0)
panic(fmt.Sprintf("Expected signing info for validator %s but not found", consAddr))
}
index := signInfo.IndexOffset % k.SignedBlocksWindow(ctx)
signInfo.IndexOffset++
@ -111,24 +110,27 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, p
// Update signed block bit array & counter
// This counter just tracks the sum of the bit array
// That way we avoid needing to read/write the whole array each time
previous := k.getValidatorSigningBitArray(ctx, consAddr, index)
if previous == signed {
previous := k.getValidatorMissedBlockBitArray(ctx, consAddr, index)
missed := !signed
switch {
case !previous && missed:
// Array value has changed from not missed to missed, increment counter
k.setValidatorMissedBlockBitArray(ctx, consAddr, index, true)
signInfo.MissedBlocksCounter++
case previous && !missed:
// Array value has changed from missed to not missed, decrement counter
k.setValidatorMissedBlockBitArray(ctx, consAddr, index, false)
signInfo.MissedBlocksCounter--
default:
// Array value at this index has not changed, no need to update counter
} else if previous && !signed {
// Array value has changed from signed to unsigned, decrement counter
k.setValidatorSigningBitArray(ctx, consAddr, index, false)
signInfo.SignedBlocksCounter--
} else if !previous && signed {
// Array value has changed from unsigned to signed, increment counter
k.setValidatorSigningBitArray(ctx, consAddr, index, true)
signInfo.SignedBlocksCounter++
}
if !signed {
logger.Info(fmt.Sprintf("Absent validator %s at height %d, %d signed, threshold %d", addr, height, signInfo.SignedBlocksCounter, k.MinSignedPerWindow(ctx)))
if missed {
logger.Info(fmt.Sprintf("Absent validator %s at height %d, %d missed, threshold %d", addr, height, signInfo.MissedBlocksCounter, k.MinSignedPerWindow(ctx)))
}
minHeight := signInfo.StartHeight + k.SignedBlocksWindow(ctx)
if height > minHeight && signInfo.SignedBlocksCounter < k.MinSignedPerWindow(ctx) {
maxMissed := k.SignedBlocksWindow(ctx) - k.MinSignedPerWindow(ctx)
if height > minHeight && signInfo.MissedBlocksCounter > maxMissed {
validator := k.validatorSet.ValidatorByConsAddr(ctx, consAddr)
if validator != nil && !validator.GetJailed() {
// Downtime confirmed: slash and jail the validator
@ -143,6 +145,10 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, p
k.validatorSet.Slash(ctx, consAddr, distributionHeight, power, k.SlashFractionDowntime(ctx))
k.validatorSet.Jail(ctx, consAddr)
signInfo.JailedUntil = ctx.BlockHeader().Time.Add(k.DowntimeUnbondDuration(ctx))
// We need to reset the counter & array so that the validator won't be immediately slashed for downtime upon rebonding.
signInfo.MissedBlocksCounter = 0
signInfo.IndexOffset = 0
k.clearValidatorMissedBlockBitArray(ctx, consAddr)
} else {
// Validator was (a) not found or (b) already jailed, don't slash
logger.Info(fmt.Sprintf("Validator %s would have been slashed for downtime, but was either not found in store or already jailed",

View File

@ -30,10 +30,9 @@ func TestHandleDoubleSign(t *testing.T) {
ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams())
// validator added pre-genesis
ctx = ctx.WithBlockHeight(-1)
sk = sk.WithHooks(keeper.Hooks())
amtInt := int64(100)
operatorAddr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
got := stake.NewHandler(sk)(ctx, newTestMsgCreateValidator(operatorAddr, val, amt))
got := stake.NewHandler(sk)(ctx, NewTestMsgCreateValidator(operatorAddr, val, amt))
require.True(t, got.IsOK())
validatorUpdates := stake.EndBlocker(ctx, sk)
keeper.AddValidators(ctx, validatorUpdates)
@ -71,11 +70,10 @@ func TestSlashingPeriodCap(t *testing.T) {
// initial setup
ctx, ck, sk, _, keeper := createTestInput(t, DefaultParams())
sk = sk.WithHooks(keeper.Hooks())
amtInt := int64(100)
operatorAddr, amt := addrs[0], sdk.NewInt(amtInt)
valConsPubKey, valConsAddr := pks[0], pks[0].Address()
got := stake.NewHandler(sk)(ctx, newTestMsgCreateValidator(operatorAddr, valConsPubKey, amt))
got := stake.NewHandler(sk)(ctx, NewTestMsgCreateValidator(operatorAddr, valConsPubKey, amt))
require.True(t, got.IsOK())
validatorUpdates := stake.EndBlocker(ctx, sk)
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
@ -137,25 +135,23 @@ func TestHandleAbsentValidator(t *testing.T) {
// initial setup
ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams())
sk = sk.WithHooks(keeper.Hooks())
amtInt := int64(100)
addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
sh := stake.NewHandler(sk)
slh := NewHandler(keeper)
got := sh(ctx, newTestMsgCreateValidator(addr, val, amt))
got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt))
require.True(t, got.IsOK())
validatorUpdates := stake.EndBlocker(ctx, sk)
keeper.AddValidators(ctx, validatorUpdates)
require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}})
require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower()))
// will exist since the validator has been bonded
info, found := keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
require.False(t, found)
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, int64(0), info.IndexOffset)
require.Equal(t, int64(0), info.SignedBlocksCounter)
// default time.Time value
var blankTime time.Time
require.Equal(t, blankTime, info.JailedUntil)
require.Equal(t, int64(0), info.MissedBlocksCounter)
require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil)
height := int64(0)
// 1000 first blocks OK
@ -166,7 +162,7 @@ func TestHandleAbsentValidator(t *testing.T) {
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, keeper.SignedBlocksWindow(ctx), info.SignedBlocksCounter)
require.Equal(t, int64(0), info.MissedBlocksCounter)
// 500 blocks missed
for ; height < keeper.SignedBlocksWindow(ctx)+(keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)); height++ {
@ -176,7 +172,7 @@ func TestHandleAbsentValidator(t *testing.T) {
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx), info.SignedBlocksCounter)
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx), info.MissedBlocksCounter)
// validator should be bonded still
validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
@ -190,7 +186,8 @@ func TestHandleAbsentValidator(t *testing.T) {
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-1, info.SignedBlocksCounter)
// counter now reset to zero
require.Equal(t, int64(0), info.MissedBlocksCounter)
// end block
stake.EndBlocker(ctx, sk)
@ -211,7 +208,7 @@ func TestHandleAbsentValidator(t *testing.T) {
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-2, info.SignedBlocksCounter)
require.Equal(t, int64(1), info.MissedBlocksCounter)
// end block
stake.EndBlocker(ctx, sk)
@ -248,12 +245,12 @@ func TestHandleAbsentValidator(t *testing.T) {
pool = sk.GetPool(ctx)
require.Equal(t, amtInt-slashAmt-secondSlashAmt, pool.BondedTokens.RoundInt64())
// validator start height should have been changed
// validator start height should not have been changed
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, height, info.StartHeight)
// we've missed 2 blocks more than the maximum
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-2, info.SignedBlocksCounter)
require.Equal(t, int64(0), info.StartHeight)
// we've missed 2 blocks more than the maximum, so the counter was reset to 0 at 1 block more and is now 1
require.Equal(t, int64(1), info.MissedBlocksCounter)
// validator should not be immediately jailed again
height++
@ -294,16 +291,18 @@ func TestHandleNewValidator(t *testing.T) {
ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams())
addr, val, amt := addrs[0], pks[0], int64(100)
sh := stake.NewHandler(sk)
got := sh(ctx, newTestMsgCreateValidator(addr, val, sdk.NewInt(amt)))
// 1000 first blocks not a validator
ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 1)
// Validator created
got := sh(ctx, NewTestMsgCreateValidator(addr, val, sdk.NewInt(amt)))
require.True(t, got.IsOK())
validatorUpdates := stake.EndBlocker(ctx, sk)
keeper.AddValidators(ctx, validatorUpdates)
require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.SubRaw(amt)}})
require.Equal(t, sdk.NewDec(amt), sk.Validator(ctx, addr).GetPower())
// 1000 first blocks not a validator
ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 1)
// Now a validator, for two blocks
keeper.handleValidatorSignature(ctx, val.Address(), 100, true)
ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 2)
@ -313,7 +312,7 @@ func TestHandleNewValidator(t *testing.T) {
require.True(t, found)
require.Equal(t, keeper.SignedBlocksWindow(ctx)+1, info.StartHeight)
require.Equal(t, int64(2), info.IndexOffset)
require.Equal(t, int64(1), info.SignedBlocksCounter)
require.Equal(t, int64(1), info.MissedBlocksCounter)
require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil)
// validator should be bonded still, should not have been jailed or slashed
@ -332,7 +331,7 @@ func TestHandleAlreadyJailed(t *testing.T) {
amtInt := int64(100)
addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
sh := stake.NewHandler(sk)
got := sh(ctx, newTestMsgCreateValidator(addr, val, amt))
got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt))
require.True(t, got.IsOK())
validatorUpdates := stake.EndBlocker(ctx, sk)
keeper.AddValidators(ctx, validatorUpdates)
@ -369,3 +368,112 @@ func TestHandleAlreadyJailed(t *testing.T) {
require.Equal(t, amtInt-1, validator.GetTokens().RoundInt64())
}
// Test a validator dipping in and out of the validator set
// Ensure that missed blocks are tracked correctly and that
// the start height of the signing info is reset correctly
func TestValidatorDippingInAndOut(t *testing.T) {
// initial setup
// keeperTestParams set the SignedBlocksWindow to 1000 and MaxMissedBlocksPerWindow to 500
ctx, _, sk, _, keeper := createTestInput(t, keeperTestParams())
params := sk.GetParams(ctx)
params.MaxValidators = 1
sk.SetParams(ctx, params)
amtInt := int64(100)
addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
consAddr := sdk.ConsAddress(addr)
sh := stake.NewHandler(sk)
got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt))
require.True(t, got.IsOK())
validatorUpdates := stake.EndBlocker(ctx, sk)
keeper.AddValidators(ctx, validatorUpdates)
// 100 first blocks OK
height := int64(0)
for ; height < int64(100); height++ {
ctx = ctx.WithBlockHeight(height)
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, true)
}
// validator kicked out of validator set
newAmt := int64(101)
got = sh(ctx, NewTestMsgCreateValidator(addrs[1], pks[1], sdk.NewInt(newAmt)))
require.True(t, got.IsOK())
validatorUpdates = stake.EndBlocker(ctx, sk)
require.Equal(t, 2, len(validatorUpdates))
keeper.AddValidators(ctx, validatorUpdates)
validator, _ := sk.GetValidator(ctx, addr)
require.Equal(t, sdk.Unbonding, validator.Status)
// 600 more blocks happened
height = int64(700)
ctx = ctx.WithBlockHeight(height)
// validator added back in
got = sh(ctx, newTestMsgDelegate(sdk.AccAddress(addrs[2]), addrs[0], sdk.NewInt(2)))
require.True(t, got.IsOK())
validatorUpdates = stake.EndBlocker(ctx, sk)
require.Equal(t, 2, len(validatorUpdates))
validator, _ = sk.GetValidator(ctx, addr)
require.Equal(t, sdk.Bonded, validator.Status)
newAmt = int64(102)
// validator misses a block
keeper.handleValidatorSignature(ctx, val.Address(), newAmt, false)
height++
// shouldn't be jailed/kicked yet
validator, _ = sk.GetValidator(ctx, addr)
require.Equal(t, sdk.Bonded, validator.Status)
// validator misses 500 more blocks, 501 total
latest := height
for ; height < latest+500; height++ {
ctx = ctx.WithBlockHeight(height)
keeper.handleValidatorSignature(ctx, val.Address(), newAmt, false)
}
// should now be jailed & kicked
stake.EndBlocker(ctx, sk)
validator, _ = sk.GetValidator(ctx, addr)
require.Equal(t, sdk.Unbonding, validator.Status)
// check all the signing information
signInfo, found := keeper.getValidatorSigningInfo(ctx, consAddr)
require.True(t, found)
require.Equal(t, int64(0), signInfo.MissedBlocksCounter)
require.Equal(t, int64(0), signInfo.IndexOffset)
// array should be cleared
for offset := int64(0); offset < keeper.SignedBlocksWindow(ctx); offset++ {
missed := keeper.getValidatorMissedBlockBitArray(ctx, consAddr, offset)
require.False(t, missed)
}
// some blocks pass
height = int64(5000)
ctx = ctx.WithBlockHeight(height)
// validator rejoins and starts signing again
sk.Unjail(ctx, consAddr)
keeper.handleValidatorSignature(ctx, val.Address(), newAmt, true)
height++
// validator should not be kicked since we reset counter/array when it was jailed
stake.EndBlocker(ctx, sk)
validator, _ = sk.GetValidator(ctx, addr)
require.Equal(t, sdk.Bonded, validator.Status)
// validator misses 501 blocks
latest = height
for ; height < latest+501; height++ {
ctx = ctx.WithBlockHeight(height)
keeper.handleValidatorSignature(ctx, val.Address(), newAmt, false)
}
// validator should now be jailed & kicked
stake.EndBlocker(ctx, sk)
validator, _ = sk.GetValidator(ctx, addr)
require.Equal(t, sdk.Unbonding, validator.Status)
}

View File

@ -9,10 +9,10 @@ import (
// key prefix bytes
var (
ValidatorSigningInfoKey = []byte{0x01} // Prefix for signing info
ValidatorSigningBitArrayKey = []byte{0x02} // Prefix for signature bit array
ValidatorSlashingPeriodKey = []byte{0x03} // Prefix for slashing period
AddrPubkeyRelationKey = []byte{0x04} // Prefix for address-pubkey relation
ValidatorSigningInfoKey = []byte{0x01} // Prefix for signing info
ValidatorMissedBlockBitArrayKey = []byte{0x02} // Prefix for missed block bit array
ValidatorSlashingPeriodKey = []byte{0x03} // Prefix for slashing period
AddrPubkeyRelationKey = []byte{0x04} // Prefix for address-pubkey relation
)
// stored by *Tendermint* address (not operator address)
@ -21,10 +21,15 @@ func GetValidatorSigningInfoKey(v sdk.ConsAddress) []byte {
}
// stored by *Tendermint* address (not operator address)
func GetValidatorSigningBitArrayKey(v sdk.ConsAddress, i int64) []byte {
func GetValidatorMissedBlockBitArrayPrefixKey(v sdk.ConsAddress) []byte {
return append(ValidatorMissedBlockBitArrayKey, v.Bytes()...)
}
// stored by *Tendermint* address (not operator address)
func GetValidatorMissedBlockBitArrayKey(v sdk.ConsAddress, i int64) []byte {
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, uint64(i))
return append(ValidatorSigningBitArrayKey, append(v.Bytes(), b...)...)
return append(GetValidatorMissedBlockBitArrayPrefixKey(v), b...)
}
// stored by *Tendermint* address (not operator address)

View File

@ -28,32 +28,42 @@ func (k Keeper) setValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress
}
// Stored by *validator* address (not operator address)
func (k Keeper) getValidatorSigningBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64) (signed bool) {
func (k Keeper) getValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64) (missed bool) {
store := ctx.KVStore(k.storeKey)
bz := store.Get(GetValidatorSigningBitArrayKey(address, index))
bz := store.Get(GetValidatorMissedBlockBitArrayKey(address, index))
if bz == nil {
// lazy: treat empty key as unsigned
signed = false
// lazy: treat empty key as not missed
missed = false
return
}
k.cdc.MustUnmarshalBinary(bz, &signed)
k.cdc.MustUnmarshalBinary(bz, &missed)
return
}
// Stored by *validator* address (not operator address)
func (k Keeper) setValidatorSigningBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64, signed bool) {
func (k Keeper) setValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64, missed bool) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinary(signed)
store.Set(GetValidatorSigningBitArrayKey(address, index), bz)
bz := k.cdc.MustMarshalBinary(missed)
store.Set(GetValidatorMissedBlockBitArrayKey(address, index), bz)
}
// Stored by *validator* address (not operator address)
func (k Keeper) clearValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress) {
store := ctx.KVStore(k.storeKey)
iter := sdk.KVStorePrefixIterator(store, GetValidatorMissedBlockBitArrayPrefixKey(address))
for ; iter.Valid(); iter.Next() {
store.Delete(iter.Key())
}
iter.Close()
}
// Construct a new `ValidatorSigningInfo` struct
func NewValidatorSigningInfo(startHeight int64, indexOffset int64, jailedUntil time.Time, signedBlocksCounter int64) ValidatorSigningInfo {
func NewValidatorSigningInfo(startHeight int64, indexOffset int64, jailedUntil time.Time, missedBlocksCounter int64) ValidatorSigningInfo {
return ValidatorSigningInfo{
StartHeight: startHeight,
IndexOffset: indexOffset,
JailedUntil: jailedUntil,
SignedBlocksCounter: signedBlocksCounter,
MissedBlocksCounter: missedBlocksCounter,
}
}
@ -62,11 +72,11 @@ type ValidatorSigningInfo struct {
StartHeight int64 `json:"start_height"` // height at which validator was first a candidate OR was unjailed
IndexOffset int64 `json:"index_offset"` // index offset into signed block bit array
JailedUntil time.Time `json:"jailed_until"` // timestamp validator cannot be unjailed until
SignedBlocksCounter int64 `json:"signed_blocks_counter"` // signed blocks counter (to avoid scanning the array every time)
MissedBlocksCounter int64 `json:"missed_blocks_counter"` // missed blocks counter (to avoid scanning the array every time)
}
// Return human readable signing info
func (i ValidatorSigningInfo) HumanReadableString() string {
return fmt.Sprintf("Start height: %d, index offset: %d, jailed until: %v, signed blocks counter: %d",
i.StartHeight, i.IndexOffset, i.JailedUntil, i.SignedBlocksCounter)
return fmt.Sprintf("Start height: %d, index offset: %d, jailed until: %v, missed blocks counter: %d",
i.StartHeight, i.IndexOffset, i.JailedUntil, i.MissedBlocksCounter)
}

View File

@ -17,7 +17,7 @@ func TestGetSetValidatorSigningInfo(t *testing.T) {
StartHeight: int64(4),
IndexOffset: int64(3),
JailedUntil: time.Unix(2, 0),
SignedBlocksCounter: int64(10),
MissedBlocksCounter: int64(10),
}
keeper.setValidatorSigningInfo(ctx, sdk.ConsAddress(addrs[0]), newInfo)
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(addrs[0]))
@ -25,14 +25,14 @@ func TestGetSetValidatorSigningInfo(t *testing.T) {
require.Equal(t, info.StartHeight, int64(4))
require.Equal(t, info.IndexOffset, int64(3))
require.Equal(t, info.JailedUntil, time.Unix(2, 0).UTC())
require.Equal(t, info.SignedBlocksCounter, int64(10))
require.Equal(t, info.MissedBlocksCounter, int64(10))
}
func TestGetSetValidatorSigningBitArray(t *testing.T) {
func TestGetSetValidatorMissedBlockBitArray(t *testing.T) {
ctx, _, _, _, keeper := createTestInput(t, DefaultParams())
signed := keeper.getValidatorSigningBitArray(ctx, sdk.ConsAddress(addrs[0]), 0)
require.False(t, signed) // treat empty key as unsigned
keeper.setValidatorSigningBitArray(ctx, sdk.ConsAddress(addrs[0]), 0, true)
signed = keeper.getValidatorSigningBitArray(ctx, sdk.ConsAddress(addrs[0]), 0)
require.True(t, signed) // now should be signed
missed := keeper.getValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(addrs[0]), 0)
require.False(t, missed) // treat empty key as not missed
keeper.setValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(addrs[0]), 0, true)
missed = keeper.getValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(addrs[0]), 0)
require.True(t, missed) // now should be missed
}

View File

@ -88,6 +88,7 @@ func createTestInput(t *testing.T, defaults Params) (sdk.Context, bank.Keeper, s
require.Nil(t, err)
paramstore := paramsKeeper.Subspace(DefaultParamspace)
keeper := NewKeeper(cdc, keySlashing, sk, paramstore, DefaultCodespace)
sk = sk.WithHooks(keeper.Hooks())
require.NotPanics(t, func() {
InitGenesis(ctx, keeper, GenesisState{defaults}, genesis)
@ -111,7 +112,7 @@ func testAddr(addr string) sdk.AccAddress {
return res
}
func newTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt sdk.Int) stake.MsgCreateValidator {
func NewTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt sdk.Int) stake.MsgCreateValidator {
commission := stake.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec())
return stake.MsgCreateValidator{
Description: stake.Description{},

View File

@ -18,7 +18,7 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, sk Keeper) (tags
tags = sdk.NewTags("height", heightBytes)
// Iterate over all the validators which *should* have signed this block
// Store whether or not they have actually signed it and slash/unbond any
// store whether or not they have actually signed it and slash/unbond any
// which have missed too many blocks in a row (downtime slashing)
for _, voteInfo := range req.LastCommitInfo.GetVotes() {
sk.handleValidatorSignature(ctx, voteInfo.Validator.Address, voteInfo.Validator.Power, voteInfo.SignedLastBlock)

View File

@ -17,7 +17,7 @@ func TestBeginBlocker(t *testing.T) {
addr, pk, amt := addrs[2], pks[2], sdk.NewInt(100)
// bond the validator
got := stake.NewHandler(sk)(ctx, newTestMsgCreateValidator(addr, pk, amt))
got := stake.NewHandler(sk)(ctx, NewTestMsgCreateValidator(addr, pk, amt))
require.True(t, got.IsOK())
validatorUpdates := stake.EndBlocker(ctx, sk)
keeper.AddValidators(ctx, validatorUpdates)
@ -45,7 +45,7 @@ func TestBeginBlocker(t *testing.T) {
require.Equal(t, ctx.BlockHeight(), info.StartHeight)
require.Equal(t, int64(1), info.IndexOffset)
require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil)
require.Equal(t, int64(1), info.SignedBlocksCounter)
require.Equal(t, int64(0), info.MissedBlocksCounter)
height := int64(0)

View File

@ -10,24 +10,6 @@ import (
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/ed25519"
)
var (
priv1 = ed25519.GenPrivKey()
addr1 = sdk.AccAddress(priv1.PubKey().Address())
priv2 = ed25519.GenPrivKey()
addr2 = sdk.AccAddress(priv2.PubKey().Address())
addr3 = sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address())
priv4 = ed25519.GenPrivKey()
addr4 = sdk.AccAddress(priv4.PubKey().Address())
coins = sdk.Coins{sdk.NewCoin("foocoin", sdk.NewInt(10))}
fee = auth.NewStdFee(
100000,
sdk.Coins{sdk.NewCoin("foocoin", sdk.NewInt(0))}...,
)
commissionMsg = NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec())
)
// getMockApp returns an initialized mock application for this module.

View File

@ -42,10 +42,13 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [
// Manually set indices for the first time
keeper.SetValidatorByConsAddr(ctx, validator)
keeper.SetValidatorByPowerIndex(ctx, validator, data.Pool)
keeper.OnValidatorCreated(ctx, validator.OperatorAddr)
}
for _, bond := range data.Bonds {
keeper.SetDelegation(ctx, bond)
for _, delegation := range data.Bonds {
keeper.SetDelegation(ctx, delegation)
keeper.OnDelegationCreated(ctx, delegation.DelegatorAddr, delegation.ValidatorAddr)
}
res = keeper.ApplyAndReturnValidatorSetUpdates(ctx)

View File

@ -106,7 +106,7 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k
validator := NewValidator(msg.ValidatorAddr, msg.PubKey, msg.Description)
commission := NewCommissionWithTime(
msg.Commission.Rate, msg.Commission.MaxChangeRate,
msg.Commission.Rate, msg.Commission.MaxRate,
msg.Commission.MaxChangeRate, ctx.BlockHeader().Time,
)
validator, err := validator.SetInitialCommission(commission)

View File

@ -7,8 +7,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
keep "github.com/cosmos/cosmos-sdk/x/stake/keeper"
"github.com/cosmos/cosmos-sdk/x/stake/types"
@ -16,31 +14,6 @@ import (
//______________________________________________________________________
func newTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt int64) MsgCreateValidator {
return types.NewMsgCreateValidator(
address, pubKey, sdk.NewCoin("steak", sdk.NewInt(amt)), Description{}, commissionMsg,
)
}
func newTestMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amt int64) MsgDelegate {
return MsgDelegate{
DelegatorAddr: delAddr,
ValidatorAddr: valAddr,
Delegation: sdk.NewCoin("steak", sdk.NewInt(amt)),
}
}
func newTestMsgCreateValidatorOnBehalfOf(delAddr sdk.AccAddress, valAddr sdk.ValAddress, valPubKey crypto.PubKey, amt int64) MsgCreateValidator {
return MsgCreateValidator{
Description: Description{},
Commission: commissionMsg,
DelegatorAddr: delAddr,
ValidatorAddr: valAddr,
PubKey: valPubKey,
Delegation: sdk.NewCoin("steak", sdk.NewInt(amt)),
}
}
// retrieve params which are instant
func setInstantUnbondPeriod(keeper keep.Keeper, ctx sdk.Context) types.Params {
params := keeper.GetParams(ctx)
@ -59,7 +32,7 @@ func TestValidatorByPowerIndex(t *testing.T) {
_ = setInstantUnbondPeriod(keeper, ctx)
// create validator
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], initBond)
msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], initBond)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
@ -83,7 +56,7 @@ func TestValidatorByPowerIndex(t *testing.T) {
require.True(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power))
// create a second validator keep it bonded
msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, keep.PKs[2], int64(1000000))
msgCreateValidator = NewTestMsgCreateValidator(validatorAddr3, keep.PKs[2], int64(1000000))
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
@ -146,7 +119,7 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) {
addr1, addr2 := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1])
pk1, pk2 := keep.PKs[0], keep.PKs[1]
msgCreateValidator1 := newTestMsgCreateValidator(addr1, pk1, 10)
msgCreateValidator1 := NewTestMsgCreateValidator(addr1, pk1, 10)
got := handleMsgCreateValidator(ctx, msgCreateValidator1, keeper)
require.True(t, got.IsOK(), "%v", got)
@ -162,17 +135,17 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) {
assert.Equal(t, Description{}, validator.Description)
// two validators can't have the same operator address
msgCreateValidator2 := newTestMsgCreateValidator(addr1, pk2, 10)
msgCreateValidator2 := NewTestMsgCreateValidator(addr1, pk2, 10)
got = handleMsgCreateValidator(ctx, msgCreateValidator2, keeper)
require.False(t, got.IsOK(), "%v", got)
// two validators can't have the same pubkey
msgCreateValidator3 := newTestMsgCreateValidator(addr2, pk1, 10)
msgCreateValidator3 := NewTestMsgCreateValidator(addr2, pk1, 10)
got = handleMsgCreateValidator(ctx, msgCreateValidator3, keeper)
require.False(t, got.IsOK(), "%v", got)
// must have different pubkey and operator
msgCreateValidator4 := newTestMsgCreateValidator(addr2, pk2, 10)
msgCreateValidator4 := NewTestMsgCreateValidator(addr2, pk2, 10)
got = handleMsgCreateValidator(ctx, msgCreateValidator4, keeper)
require.True(t, got.IsOK(), "%v", got)
@ -197,7 +170,7 @@ func TestDuplicatesMsgCreateValidatorOnBehalfOf(t *testing.T) {
validatorAddr := sdk.ValAddress(keep.Addrs[0])
delegatorAddr := keep.Addrs[1]
pk := keep.PKs[0]
msgCreateValidatorOnBehalfOf := newTestMsgCreateValidatorOnBehalfOf(delegatorAddr, validatorAddr, pk, 10)
msgCreateValidatorOnBehalfOf := NewTestMsgCreateValidatorOnBehalfOf(delegatorAddr, validatorAddr, pk, 10)
got := handleMsgCreateValidator(ctx, msgCreateValidatorOnBehalfOf, keeper)
require.True(t, got.IsOK(), "%v", got)
@ -232,7 +205,7 @@ func TestLegacyValidatorDelegations(t *testing.T) {
delAddr := keep.Addrs[1]
// create validator
msgCreateVal := newTestMsgCreateValidator(valAddr, valConsPubKey, bondAmount)
msgCreateVal := NewTestMsgCreateValidator(valAddr, valConsPubKey, bondAmount)
got := handleMsgCreateValidator(ctx, msgCreateVal, keeper)
require.True(t, got.IsOK(), "expected create validator msg to be ok, got %v", got)
@ -248,7 +221,7 @@ func TestLegacyValidatorDelegations(t *testing.T) {
require.Equal(t, bondAmount, validator.BondedTokens().RoundInt64())
// delegate tokens to the validator
msgDelegate := newTestMsgDelegate(delAddr, valAddr, bondAmount)
msgDelegate := NewTestMsgDelegate(delAddr, valAddr, bondAmount)
got = handleMsgDelegate(ctx, msgDelegate, keeper)
require.True(t, got.IsOK(), "expected delegation to be ok, got %v", got)
@ -283,12 +256,12 @@ func TestLegacyValidatorDelegations(t *testing.T) {
require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt64())
// verify a delegator cannot create a new delegation to the now jailed validator
msgDelegate = newTestMsgDelegate(delAddr, valAddr, bondAmount)
msgDelegate = NewTestMsgDelegate(delAddr, valAddr, bondAmount)
got = handleMsgDelegate(ctx, msgDelegate, keeper)
require.False(t, got.IsOK(), "expected delegation to not be ok, got %v", got)
// verify the validator can still self-delegate
msgSelfDelegate := newTestMsgDelegate(sdk.AccAddress(valAddr), valAddr, bondAmount)
msgSelfDelegate := NewTestMsgDelegate(sdk.AccAddress(valAddr), valAddr, bondAmount)
got = handleMsgDelegate(ctx, msgSelfDelegate, keeper)
require.True(t, got.IsOK(), "expected delegation to not be ok, got %v", got)
@ -302,7 +275,7 @@ func TestLegacyValidatorDelegations(t *testing.T) {
keeper.Unjail(ctx, valConsAddr)
// verify the validator can now accept delegations
msgDelegate = newTestMsgDelegate(delAddr, valAddr, bondAmount)
msgDelegate = NewTestMsgDelegate(delAddr, valAddr, bondAmount)
got = handleMsgDelegate(ctx, msgDelegate, keeper)
require.True(t, got.IsOK(), "expected delegation to be ok, got %v", got)
@ -328,7 +301,7 @@ func TestIncrementsMsgDelegate(t *testing.T) {
validatorAddr, delegatorAddr := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1]
// first create validator
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], bondAmount)
msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], bondAmount)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected create validator msg to be ok, got %v", got)
@ -354,7 +327,7 @@ func TestIncrementsMsgDelegate(t *testing.T) {
require.Equal(t, bondAmount, pool.BondedTokens.RoundInt64())
// just send the same msgbond multiple times
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, bondAmount)
msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, bondAmount)
for i := 0; i < 5; i++ {
ctx = ctx.WithBlockHeight(int64(i))
@ -402,14 +375,14 @@ func TestIncrementsMsgUnbond(t *testing.T) {
// create validator, delegate
validatorAddr, delegatorAddr := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1]
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], initBond)
msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], initBond)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
// initial balance
amt1 := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(denom)
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, initBond)
msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, initBond)
got = handleMsgDelegate(ctx, msgDelegate, keeper)
require.True(t, got.IsOK(), "expected delegation to be ok, got %v", got)
@ -505,7 +478,7 @@ func TestMultipleMsgCreateValidator(t *testing.T) {
// bond them all
for i, validatorAddr := range validatorAddrs {
msgCreateValidatorOnBehalfOf := newTestMsgCreateValidatorOnBehalfOf(delegatorAddrs[i], validatorAddr, keep.PKs[i], 10)
msgCreateValidatorOnBehalfOf := NewTestMsgCreateValidatorOnBehalfOf(delegatorAddrs[i], validatorAddr, keep.PKs[i], 10)
got := handleMsgCreateValidator(ctx, msgCreateValidatorOnBehalfOf, keeper)
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
@ -552,13 +525,13 @@ func TestMultipleMsgDelegate(t *testing.T) {
_ = setInstantUnbondPeriod(keeper, ctx)
//first make a validator
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
// delegate multiple parties
for i, delegatorAddr := range delegatorAddrs {
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, 10)
msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, 10)
got := handleMsgDelegate(ctx, msgDelegate, keeper)
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
@ -590,12 +563,12 @@ func TestJailValidator(t *testing.T) {
_ = setInstantUnbondPeriod(keeper, ctx)
// create the validator
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// bond a delegator
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, 10)
msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, 10)
got = handleMsgDelegate(ctx, msgDelegate, keeper)
require.True(t, got.IsOK(), "expected ok, got %v", got)
@ -639,12 +612,12 @@ func TestValidatorQueue(t *testing.T) {
keeper.SetParams(ctx, params)
// create the validator
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// bond a delegator
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, 10)
msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, 10)
got = handleMsgDelegate(ctx, msgDelegate, keeper)
require.True(t, got.IsOK(), "expected ok, got %v", got)
@ -689,7 +662,7 @@ func TestUnbondingPeriod(t *testing.T) {
keeper.SetParams(ctx, params)
// create the validator
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
@ -727,12 +700,12 @@ func TestUnbondingFromUnbondingValidator(t *testing.T) {
validatorAddr, delegatorAddr := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1]
// create the validator
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// bond a delegator
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, 10)
msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, 10)
got = handleMsgDelegate(ctx, msgDelegate, keeper)
require.True(t, got.IsOK(), "expected ok, got %v", got)
@ -774,7 +747,7 @@ func TestRedelegationPeriod(t *testing.T) {
keeper.SetParams(ctx, params)
// create the validators
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
// initial balance
amt1 := AccMapper.GetAccount(ctx, sdk.AccAddress(validatorAddr)).GetCoins().AmountOf(denom)
@ -786,7 +759,7 @@ func TestRedelegationPeriod(t *testing.T) {
amt2 := AccMapper.GetAccount(ctx, sdk.AccAddress(validatorAddr)).GetCoins().AmountOf(denom)
require.Equal(t, amt1.Sub(sdk.NewInt(10)).Int64(), amt2.Int64(), "expected coins to be subtracted")
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 10)
msgCreateValidator = NewTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 10)
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
@ -833,15 +806,15 @@ func TestTransitiveRedelegation(t *testing.T) {
keeper.SetParams(ctx, params)
// create the validators
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 10)
msgCreateValidator = NewTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 10)
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, keep.PKs[2], 10)
msgCreateValidator = NewTestMsgCreateValidator(validatorAddr3, keep.PKs[2], 10)
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
@ -863,6 +836,48 @@ func TestTransitiveRedelegation(t *testing.T) {
require.True(t, got.IsOK(), "expected no error")
}
func TestConflictingRedelegation(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
validatorAddr := sdk.ValAddress(keep.Addrs[0])
validatorAddr2 := sdk.ValAddress(keep.Addrs[1])
// set the unbonding time
params := keeper.GetParams(ctx)
params.UnbondingTime = 1
keeper.SetParams(ctx, params)
// create the validators
msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
msgCreateValidator = NewTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 10)
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// end block to bond them
EndBlocker(ctx, keeper)
// begin redelegate
msgBeginRedelegate := NewMsgBeginRedelegate(sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2, sdk.NewDec(5))
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
require.True(t, got.IsOK(), "expected no error, %v", got)
// cannot redelegate again while first redelegation still exists
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
require.True(t, !got.IsOK(), "expected an error, msg: %v", msgBeginRedelegate)
// progress forward in time
ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(10 * time.Second))
// complete first redelegation
EndBlocker(ctx, keeper)
// now should be able to redelegate again
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
require.True(t, got.IsOK(), "expected no error")
}
func TestUnbondingWhenExcessValidators(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
validatorAddr1 := sdk.ValAddress(keep.Addrs[0])
@ -876,21 +891,21 @@ func TestUnbondingWhenExcessValidators(t *testing.T) {
keeper.SetParams(ctx, params)
// add three validators
msgCreateValidator := newTestMsgCreateValidator(validatorAddr1, keep.PKs[0], 50)
msgCreateValidator := NewTestMsgCreateValidator(validatorAddr1, keep.PKs[0], 50)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// apply TM updates
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(keeper.GetValidatorsBonded(ctx)))
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 30)
msgCreateValidator = NewTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 30)
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// apply TM updates
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 2, len(keeper.GetValidatorsBonded(ctx)))
msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, keep.PKs[2], 10)
msgCreateValidator = NewTestMsgCreateValidator(validatorAddr3, keep.PKs[2], 10)
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// apply TM updates
@ -920,16 +935,16 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
valA, valB, del := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]), keep.Addrs[2]
consAddr0 := sdk.ConsAddress(keep.PKs[0].Address())
msgCreateValidator := newTestMsgCreateValidator(valA, keep.PKs[0], 10)
msgCreateValidator := NewTestMsgCreateValidator(valA, keep.PKs[0], 10)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
msgCreateValidator = newTestMsgCreateValidator(valB, keep.PKs[1], 10)
msgCreateValidator = NewTestMsgCreateValidator(valB, keep.PKs[1], 10)
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// delegate 10 stake
msgDelegate := newTestMsgDelegate(del, valA, 10)
msgDelegate := NewTestMsgDelegate(del, valA, 10)
got = handleMsgDelegate(ctx, msgDelegate, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgDelegate")

View File

@ -380,8 +380,6 @@ func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Co
func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress,
shares sdk.Dec) (amount sdk.Dec, err sdk.Error) {
k.OnDelegationSharesModified(ctx, delAddr, valAddr)
// check if delegation has any shares in it unbond
delegation, found := k.GetDelegation(ctx, delAddr, valAddr)
if !found {
@ -389,6 +387,8 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA
return
}
k.OnDelegationSharesModified(ctx, delAddr, valAddr)
// retrieve the amount to remove
if delegation.Shares.LT(shares) {
err = types.ErrNotEnoughDelegationShares(k.Codespace(), delegation.Shares.String())
@ -430,7 +430,6 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA
k.RemoveValidator(ctx, validator.OperatorAddr)
}
k.OnDelegationSharesModified(ctx, delegation.DelegatorAddr, validator.OperatorAddr)
return amount, nil
}
@ -526,6 +525,13 @@ func (k Keeper) CompleteUnbonding(ctx sdk.Context, delAddr sdk.AccAddress, valAd
func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress,
valSrcAddr, valDstAddr sdk.ValAddress, sharesAmount sdk.Dec) (types.Redelegation, sdk.Error) {
// check if there is already a redelgation in progress from src to dst
// TODO quick fix, instead we should use an index, see https://github.com/cosmos/cosmos-sdk/issues/1402
_, found := k.GetRedelegation(ctx, delAddr, valSrcAddr, valDstAddr)
if found {
return types.Redelegation{}, types.ErrConflictingRedelegation(k.Codespace())
}
// check if this is a transitive redelegation
if k.HasReceivingRedelegation(ctx, delAddr, valSrcAddr) {
return types.Redelegation{}, types.ErrTransitiveRedelegation(k.Codespace())

View File

@ -25,9 +25,9 @@ func TestDelegation(t *testing.T) {
}
keeper.SetPool(ctx, pool)
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
validators[2] = testingUpdateValidator(keeper, ctx, validators[2])
validators[0] = TestingUpdateValidator(keeper, ctx, validators[0])
validators[1] = TestingUpdateValidator(keeper, ctx, validators[1])
validators[2] = TestingUpdateValidator(keeper, ctx, validators[2])
// first add a validators[0] to delegate too
@ -184,7 +184,7 @@ func TestUnbondDelegation(t *testing.T) {
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = testingUpdateValidator(keeper, ctx, validator)
validator = TestingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
require.Equal(t, int64(10), pool.BondedTokens.RoundInt64())
@ -226,7 +226,7 @@ func TestUndelegateSelfDelegation(t *testing.T) {
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = testingUpdateValidator(keeper, ctx, validator)
validator = TestingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
selfDelegation := types.Delegation{
DelegatorAddr: sdk.AccAddress(addrVals[0].Bytes()),
@ -240,7 +240,7 @@ func TestUndelegateSelfDelegation(t *testing.T) {
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = testingUpdateValidator(keeper, ctx, validator)
validator = TestingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
@ -274,7 +274,7 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) {
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = testingUpdateValidator(keeper, ctx, validator)
validator = TestingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
selfDelegation := types.Delegation{
DelegatorAddr: sdk.AccAddress(addrVals[0].Bytes()),
@ -288,7 +288,7 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) {
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = testingUpdateValidator(keeper, ctx, validator)
validator = TestingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
@ -350,7 +350,7 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) {
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = testingUpdateValidator(keeper, ctx, validator)
validator = TestingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.Delegation{
@ -365,7 +365,7 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) {
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = testingUpdateValidator(keeper, ctx, validator)
validator = TestingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
@ -512,7 +512,7 @@ func TestRedelegateSelfDelegation(t *testing.T) {
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = testingUpdateValidator(keeper, ctx, validator)
validator = TestingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.Delegation{
@ -528,14 +528,14 @@ func TestRedelegateSelfDelegation(t *testing.T) {
require.Equal(t, int64(10), issuedShares.RoundInt64())
pool.BondedTokens = pool.BondedTokens.Add(sdk.NewDec(10))
keeper.SetPool(ctx, pool)
validator2 = testingUpdateValidator(keeper, ctx, validator2)
validator2 = TestingUpdateValidator(keeper, ctx, validator2)
require.Equal(t, sdk.Bonded, validator2.Status)
// create a second delegation to this validator
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = testingUpdateValidator(keeper, ctx, validator)
validator = TestingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
@ -569,7 +569,7 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) {
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = testingUpdateValidator(keeper, ctx, validator)
validator = TestingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.Delegation{
@ -584,7 +584,7 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) {
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = testingUpdateValidator(keeper, ctx, validator)
validator = TestingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
@ -599,7 +599,7 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) {
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator2 = testingUpdateValidator(keeper, ctx, validator2)
validator2 = TestingUpdateValidator(keeper, ctx, validator2)
header := ctx.BlockHeader()
blockHeight := int64(10)
@ -653,7 +653,7 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) {
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = testingUpdateValidator(keeper, ctx, validator)
validator = TestingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.Delegation{
@ -669,7 +669,7 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) {
validator.BondIntraTxCounter = 1
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = testingUpdateValidator(keeper, ctx, validator)
validator = TestingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
@ -684,7 +684,7 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) {
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator2 = testingUpdateValidator(keeper, ctx, validator2)
validator2 = TestingUpdateValidator(keeper, ctx, validator2)
require.Equal(t, sdk.Bonded, validator2.Status)
ctx = ctx.WithBlockHeight(10)

View File

@ -71,8 +71,15 @@ func GetBondedValidatorIndexKey(operator sdk.ValAddress) []byte {
func getValidatorPowerRank(validator types.Validator) []byte {
potentialPower := validator.Tokens
powerBytes := []byte(potentialPower.ToLeftPadded(maxDigitsForAccount)) // power big-endian (more powerful validators first)
// todo: deal with cases above 2**64, ref https://github.com/cosmos/cosmos-sdk/issues/2439#issuecomment-427167556
tendermintPower := potentialPower.RoundInt64()
tendermintPowerBytes := make([]byte, 8)
binary.BigEndian.PutUint64(tendermintPowerBytes[:], uint64(tendermintPower))
powerBytes := tendermintPowerBytes
powerBytesLen := len(powerBytes)
// key is of format prefix || powerbytes || heightBytes || counterBytes
key := make([]byte, 1+powerBytesLen+8+2)

View File

@ -35,10 +35,10 @@ func TestGetValidatorPowerRank(t *testing.T) {
validator types.Validator
wantHex string
}{
{val1, "05303030303030303030303030ffffffffffffffffffff"},
{val2, "05303030303030303030303031ffffffffffffffffffff"},
{val3, "05303030303030303030303130ffffffffffffffffffff"},
{val4, "0531303939353131363237373736ffffffffffffffffffff"},
{val1, "050000000000000000ffffffffffffffffffff"},
{val2, "050000000000000001ffffffffffffffffffff"},
{val3, "05000000000000000affffffffffffffffffff"},
{val4, "050000010000000000ffffffffffffffffffff"},
}
for i, tt := range tests {
got := hex.EncodeToString(getValidatorPowerRank(tt.validator))

View File

@ -29,7 +29,7 @@ func setupHelper(t *testing.T, amt int64) (sdk.Context, Keeper, types.Params) {
validator.BondIntraTxCounter = int16(i)
pool.BondedTokens = pool.BondedTokens.Add(sdk.NewDec(amt))
keeper.SetPool(ctx, pool)
validator = testingUpdateValidator(keeper, ctx, validator)
validator = TestingUpdateValidator(keeper, ctx, validator)
keeper.SetValidatorByConsAddr(ctx, validator)
}
pool = keeper.GetPool(ctx)

View File

@ -211,7 +211,8 @@ func ValidatorByPowerIndexExists(ctx sdk.Context, keeper Keeper, power []byte) b
return store.Has(power)
}
func testingUpdateValidator(keeper Keeper, ctx sdk.Context, validator types.Validator) types.Validator {
// update validator for testing
func TestingUpdateValidator(keeper Keeper, ctx sdk.Context, validator types.Validator) types.Validator {
pool := keeper.GetPool(ctx)
keeper.SetValidator(ctx, validator)
keeper.SetValidatorByPowerIndex(ctx, validator, pool)

View File

@ -84,7 +84,7 @@ func TestUpdateValidatorByPowerIndex(t *testing.T) {
require.Equal(t, sdk.Unbonded, validator.Status)
require.Equal(t, int64(100), validator.Tokens.RoundInt64())
keeper.SetPool(ctx, pool)
testingUpdateValidator(keeper, ctx, validator)
TestingUpdateValidator(keeper, ctx, validator)
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, int64(100), validator.Tokens.RoundInt64(), "\nvalidator %v\npool %v", validator, pool)
@ -98,7 +98,7 @@ func TestUpdateValidatorByPowerIndex(t *testing.T) {
validator, pool, burned := validator.RemoveDelShares(pool, delSharesCreated.Quo(sdk.NewDec(2)))
require.Equal(t, int64(50), burned.RoundInt64())
keeper.SetPool(ctx, pool) // update the pool
testingUpdateValidator(keeper, ctx, validator) // update the validator, possibly kicking it out
TestingUpdateValidator(keeper, ctx, validator) // update the validator, possibly kicking it out
require.False(t, validatorByPowerIndexExists(keeper, ctx, power))
pool = keeper.GetPool(ctx)
@ -135,7 +135,7 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) {
val, pool, _ = val.AddTokensFromDel(pool, sdk.NewInt(int64((i+1)*10)))
keeper.SetPool(ctx, pool)
val = testingUpdateValidator(keeper, ctx, val)
val = TestingUpdateValidator(keeper, ctx, val)
validators[i] = val
}
@ -146,7 +146,7 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) {
keeper.DeleteValidatorByPowerIndex(ctx, nextCliffVal, pool)
nextCliffVal, pool, _ = nextCliffVal.RemoveDelShares(pool, sdk.NewDec(21))
keeper.SetPool(ctx, pool)
nextCliffVal = testingUpdateValidator(keeper, ctx, nextCliffVal)
nextCliffVal = TestingUpdateValidator(keeper, ctx, nextCliffVal)
expectedValStatus := map[int]sdk.BondStatus{
9: sdk.Bonded, 8: sdk.Bonded, 7: sdk.Bonded, 5: sdk.Bonded, 4: sdk.Bonded,
@ -178,7 +178,7 @@ func TestSlashToZeroPowerRemoved(t *testing.T) {
require.Equal(t, int64(100), validator.Tokens.RoundInt64())
keeper.SetPool(ctx, pool)
keeper.SetValidatorByConsAddr(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
validator = TestingUpdateValidator(keeper, ctx, validator)
require.Equal(t, int64(100), validator.Tokens.RoundInt64(), "\nvalidator %v\npool %v", validator, pool)
// slash the validator by 100%
@ -223,7 +223,7 @@ func TestValidatorBasics(t *testing.T) {
assert.True(sdk.DecEq(t, sdk.ZeroDec(), pool.BondedTokens))
// set and retrieve a record
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[0] = TestingUpdateValidator(keeper, ctx, validators[0])
keeper.SetValidatorByConsAddr(ctx, validators[0])
resVal, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
@ -250,7 +250,7 @@ func TestValidatorBasics(t *testing.T) {
validators[0].Status = sdk.Bonded
validators[0].Tokens = sdk.NewDec(10)
validators[0].DelegatorShares = sdk.NewDec(10)
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[0] = TestingUpdateValidator(keeper, ctx, validators[0])
resVal, found = keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
assert.True(ValEq(t, validators[0], resVal))
@ -260,8 +260,8 @@ func TestValidatorBasics(t *testing.T) {
assert.True(ValEq(t, validators[0], resVals[0]))
// add other validators
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
validators[2] = testingUpdateValidator(keeper, ctx, validators[2])
validators[1] = TestingUpdateValidator(keeper, ctx, validators[1])
validators[2] = TestingUpdateValidator(keeper, ctx, validators[2])
resVal, found = keeper.GetValidator(ctx, addrVals[1])
require.True(t, found)
assert.True(ValEq(t, validators[1], resVal))
@ -294,7 +294,7 @@ func GetValidatorSortingUnmixed(t *testing.T) {
validators[i].Status = sdk.Bonded
validators[i].Tokens = sdk.NewDec(amt)
validators[i].DelegatorShares = sdk.NewDec(amt)
testingUpdateValidator(keeper, ctx, validators[i])
TestingUpdateValidator(keeper, ctx, validators[i])
}
// first make sure everything made it in to the gotValidator group
@ -313,14 +313,14 @@ func GetValidatorSortingUnmixed(t *testing.T) {
// test a basic increase in voting power
validators[3].Tokens = sdk.NewDec(500)
testingUpdateValidator(keeper, ctx, validators[3])
TestingUpdateValidator(keeper, ctx, validators[3])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
assert.True(ValEq(t, validators[3], resValidators[0]))
// test a decrease in voting power
validators[3].Tokens = sdk.NewDec(300)
testingUpdateValidator(keeper, ctx, validators[3])
TestingUpdateValidator(keeper, ctx, validators[3])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
assert.True(ValEq(t, validators[3], resValidators[0]))
@ -329,7 +329,7 @@ func GetValidatorSortingUnmixed(t *testing.T) {
// test equal voting power, different age
validators[3].Tokens = sdk.NewDec(200)
ctx = ctx.WithBlockHeight(10)
testingUpdateValidator(keeper, ctx, validators[3])
TestingUpdateValidator(keeper, ctx, validators[3])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
assert.True(ValEq(t, validators[3], resValidators[0]))
@ -339,7 +339,7 @@ func GetValidatorSortingUnmixed(t *testing.T) {
// no change in voting power - no change in sort
ctx = ctx.WithBlockHeight(20)
testingUpdateValidator(keeper, ctx, validators[4])
TestingUpdateValidator(keeper, ctx, validators[4])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
assert.True(ValEq(t, validators[3], resValidators[0]))
@ -348,11 +348,11 @@ func GetValidatorSortingUnmixed(t *testing.T) {
// change in voting power of both validators, both still in v-set, no age change
validators[3].Tokens = sdk.NewDec(300)
validators[4].Tokens = sdk.NewDec(300)
testingUpdateValidator(keeper, ctx, validators[3])
TestingUpdateValidator(keeper, ctx, validators[3])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
ctx = ctx.WithBlockHeight(30)
testingUpdateValidator(keeper, ctx, validators[4])
TestingUpdateValidator(keeper, ctx, validators[4])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n, "%v", resValidators)
assert.True(ValEq(t, validators[3], resValidators[0]))
@ -390,7 +390,7 @@ func GetValidatorSortingMixed(t *testing.T) {
validators[4].Tokens = sdk.NewDec(amts[4])
for i := range amts {
testingUpdateValidator(keeper, ctx, validators[i])
TestingUpdateValidator(keeper, ctx, validators[i])
}
val0, found := keeper.GetValidator(ctx, sdk.ValAddress(Addrs[0]))
require.True(t, found)
@ -444,7 +444,7 @@ func TestGetValidatorsEdgeCases(t *testing.T) {
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
validators[i].BondIntraTxCounter = int16(i)
keeper.SetPool(ctx, pool)
validators[i] = testingUpdateValidator(keeper, ctx, validators[i])
validators[i] = TestingUpdateValidator(keeper, ctx, validators[i])
}
for i := range amts {
@ -460,7 +460,7 @@ func TestGetValidatorsEdgeCases(t *testing.T) {
keeper.DeleteValidatorByPowerIndex(ctx, validators[0], pool)
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(500))
keeper.SetPool(ctx, pool)
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[0] = TestingUpdateValidator(keeper, ctx, validators[0])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, nMax, uint16(len(resValidators)))
assert.True(ValEq(t, validators[0], resValidators[0]))
@ -478,7 +478,7 @@ func TestGetValidatorsEdgeCases(t *testing.T) {
keeper.DeleteValidatorByPowerIndex(ctx, validators[3], pool)
validators[3], pool, _ = validators[3].AddTokensFromDel(pool, sdk.NewInt(1))
keeper.SetPool(ctx, pool)
validators[3] = testingUpdateValidator(keeper, ctx, validators[3])
validators[3] = TestingUpdateValidator(keeper, ctx, validators[3])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, nMax, uint16(len(resValidators)))
assert.True(ValEq(t, validators[0], resValidators[0]))
@ -488,7 +488,7 @@ func TestGetValidatorsEdgeCases(t *testing.T) {
keeper.DeleteValidatorByPowerIndex(ctx, validators[3], pool)
validators[3], pool, _ = validators[3].RemoveDelShares(pool, sdk.NewDec(201))
keeper.SetPool(ctx, pool)
validators[3] = testingUpdateValidator(keeper, ctx, validators[3])
validators[3] = TestingUpdateValidator(keeper, ctx, validators[3])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, nMax, uint16(len(resValidators)))
assert.True(ValEq(t, validators[0], resValidators[0]))
@ -498,7 +498,7 @@ func TestGetValidatorsEdgeCases(t *testing.T) {
keeper.DeleteValidatorByPowerIndex(ctx, validators[3], pool)
validators[3], pool, _ = validators[3].AddTokensFromDel(pool, sdk.NewInt(200))
keeper.SetPool(ctx, pool)
validators[3] = testingUpdateValidator(keeper, ctx, validators[3])
validators[3] = TestingUpdateValidator(keeper, ctx, validators[3])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, nMax, uint16(len(resValidators)))
assert.True(ValEq(t, validators[0], resValidators[0]))
@ -531,13 +531,13 @@ func TestValidatorBondHeight(t *testing.T) {
validators[2], pool, _ = validators[2].AddTokensFromDel(pool, sdk.NewInt(100))
keeper.SetPool(ctx, pool)
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[0] = TestingUpdateValidator(keeper, ctx, validators[0])
////////////////////////////////////////
// If two validators both increase to the same voting power in the same block,
// the one with the first transaction should become bonded
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
validators[2] = testingUpdateValidator(keeper, ctx, validators[2])
validators[1] = TestingUpdateValidator(keeper, ctx, validators[1])
validators[2] = TestingUpdateValidator(keeper, ctx, validators[2])
pool = keeper.GetPool(ctx)
@ -551,10 +551,10 @@ func TestValidatorBondHeight(t *testing.T) {
validators[1], pool, _ = validators[1].AddTokensFromDel(pool, sdk.NewInt(50))
validators[2], pool, _ = validators[2].AddTokensFromDel(pool, sdk.NewInt(50))
keeper.SetPool(ctx, pool)
validators[2] = testingUpdateValidator(keeper, ctx, validators[2])
validators[2] = TestingUpdateValidator(keeper, ctx, validators[2])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, params.MaxValidators, uint16(len(resValidators)))
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
validators[1] = TestingUpdateValidator(keeper, ctx, validators[1])
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[2], resValidators[1]))
}
@ -575,7 +575,7 @@ func TestFullValidatorSetPowerChange(t *testing.T) {
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
validators[i].BondIntraTxCounter = int16(i)
keeper.SetPool(ctx, pool)
testingUpdateValidator(keeper, ctx, validators[i])
TestingUpdateValidator(keeper, ctx, validators[i])
}
for i := range amts {
var found bool
@ -596,7 +596,7 @@ func TestFullValidatorSetPowerChange(t *testing.T) {
pool := keeper.GetPool(ctx)
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(600))
keeper.SetPool(ctx, pool)
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[0] = TestingUpdateValidator(keeper, ctx, validators[0])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
assert.Equal(t, max, len(resValidators))
assert.True(ValEq(t, validators[0], resValidators[0]))
@ -647,14 +647,14 @@ func TestApplyAndReturnValidatorSetUpdatesIdentical(t *testing.T) {
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
}
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
validators[0] = TestingUpdateValidator(keeper, ctx, validators[0])
validators[1] = TestingUpdateValidator(keeper, ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// test identical,
// tendermintUpdate set: {} -> {}
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
validators[0] = TestingUpdateValidator(keeper, ctx, validators[0])
validators[1] = TestingUpdateValidator(keeper, ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
}
@ -669,15 +669,15 @@ func TestApplyAndReturnValidatorSetUpdatesSingleValueChange(t *testing.T) {
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
}
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
validators[0] = TestingUpdateValidator(keeper, ctx, validators[0])
validators[1] = TestingUpdateValidator(keeper, ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// test single value change
// tendermintUpdate set: {} -> {c1'}
validators[0].Status = sdk.Bonded
validators[0].Tokens = sdk.NewDec(600)
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[0] = TestingUpdateValidator(keeper, ctx, validators[0])
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
@ -696,8 +696,8 @@ func TestApplyAndReturnValidatorSetUpdatesMultipleValueChange(t *testing.T) {
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
}
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
validators[0] = TestingUpdateValidator(keeper, ctx, validators[0])
validators[1] = TestingUpdateValidator(keeper, ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// test multiple value change
@ -706,8 +706,8 @@ func TestApplyAndReturnValidatorSetUpdatesMultipleValueChange(t *testing.T) {
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(190))
validators[1], pool, _ = validators[1].AddTokensFromDel(pool, sdk.NewInt(80))
keeper.SetPool(ctx, pool)
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
validators[0] = TestingUpdateValidator(keeper, ctx, validators[0])
validators[1] = TestingUpdateValidator(keeper, ctx, validators[1])
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 2, len(updates))
@ -726,8 +726,8 @@ func TestApplyAndReturnValidatorSetUpdatesInserted(t *testing.T) {
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
}
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
validators[0] = TestingUpdateValidator(keeper, ctx, validators[0])
validators[1] = TestingUpdateValidator(keeper, ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// test validtor added at the beginning
@ -775,13 +775,13 @@ func TestApplyAndReturnValidatorSetUpdatesWithCliffValidator(t *testing.T) {
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
}
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
validators[0] = TestingUpdateValidator(keeper, ctx, validators[0])
validators[1] = TestingUpdateValidator(keeper, ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// test validator added at the end but not inserted in the valset
// tendermintUpdate set: {} -> {}
testingUpdateValidator(keeper, ctx, validators[2])
TestingUpdateValidator(keeper, ctx, validators[2])
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 0, len(updates))
@ -813,8 +813,8 @@ func TestApplyAndReturnValidatorSetUpdatesPowerDecrease(t *testing.T) {
validators[i].BondIntraTxCounter = int16(i)
keeper.SetPool(ctx, pool)
}
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
validators[0] = TestingUpdateValidator(keeper, ctx, validators[0])
validators[1] = TestingUpdateValidator(keeper, ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// check initial power
@ -827,8 +827,8 @@ func TestApplyAndReturnValidatorSetUpdatesPowerDecrease(t *testing.T) {
validators[0], pool, _ = validators[0].RemoveDelShares(pool, sdk.NewDec(20))
validators[1], pool, _ = validators[1].RemoveDelShares(pool, sdk.NewDec(30))
keeper.SetPool(ctx, pool)
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
validators[0] = TestingUpdateValidator(keeper, ctx, validators[0])
validators[1] = TestingUpdateValidator(keeper, ctx, validators[1])
// power has changed
require.Equal(t, sdk.NewDec(80).RoundInt64(), validators[0].GetPower().RoundInt64())

View File

@ -16,7 +16,9 @@ import (
// SimulateMsgCreateValidator
func SimulateMsgCreateValidator(m auth.AccountMapper, k stake.Keeper) simulation.Operation {
handler := stake.NewHandler(k)
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
accs []simulation.Account, event func(string)) (
action string, fOp []simulation.FutureOperation, err error) {
denom := k.GetParams(ctx).BondDenom
description := stake.Description{
@ -71,7 +73,9 @@ func SimulateMsgCreateValidator(m auth.AccountMapper, k stake.Keeper) simulation
// SimulateMsgEditValidator
func SimulateMsgEditValidator(k stake.Keeper) simulation.Operation {
handler := stake.NewHandler(k)
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
accs []simulation.Account, event func(string)) (
action string, fOp []simulation.FutureOperation, err error) {
description := stake.Description{
Moniker: simulation.RandStringOfLength(r, 10),
@ -109,7 +113,9 @@ func SimulateMsgEditValidator(k stake.Keeper) simulation.Operation {
// SimulateMsgDelegate
func SimulateMsgDelegate(m auth.AccountMapper, k stake.Keeper) simulation.Operation {
handler := stake.NewHandler(k)
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
accs []simulation.Account, event func(string)) (
action string, fOp []simulation.FutureOperation, err error) {
denom := k.GetParams(ctx).BondDenom
validatorAcc := simulation.RandomAcc(r, accs)
@ -145,7 +151,9 @@ func SimulateMsgDelegate(m auth.AccountMapper, k stake.Keeper) simulation.Operat
// SimulateMsgBeginUnbonding
func SimulateMsgBeginUnbonding(m auth.AccountMapper, k stake.Keeper) simulation.Operation {
handler := stake.NewHandler(k)
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
accs []simulation.Account, event func(string)) (
action string, fOp []simulation.FutureOperation, err error) {
denom := k.GetParams(ctx).BondDenom
validatorAcc := simulation.RandomAcc(r, accs)
@ -181,7 +189,9 @@ func SimulateMsgBeginUnbonding(m auth.AccountMapper, k stake.Keeper) simulation.
// SimulateMsgBeginRedelegate
func SimulateMsgBeginRedelegate(m auth.AccountMapper, k stake.Keeper) simulation.Operation {
handler := stake.NewHandler(k)
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
accs []simulation.Account, event func(string)) (
action string, fOp []simulation.FutureOperation, err error) {
denom := k.GetParams(ctx).BondDenom
sourceValidatorAcc := simulation.RandomAcc(r, accs)

View File

@ -56,6 +56,7 @@ var (
GetREDsFromValSrcIndexKey = keeper.GetREDsFromValSrcIndexKey
GetREDsToValDstIndexKey = keeper.GetREDsToValDstIndexKey
GetREDsByDelToValDstIndexKey = keeper.GetREDsByDelToValDstIndexKey
TestingUpdateValidator = keeper.TestingUpdateValidator
DefaultParamspace = keeper.DefaultParamspace
KeyInflationRateChange = types.KeyInflationRateChange

62
x/stake/test_common.go Normal file
View File

@ -0,0 +1,62 @@
package stake
import (
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/stake/types"
)
var (
priv1 = ed25519.GenPrivKey()
addr1 = sdk.AccAddress(priv1.PubKey().Address())
priv2 = ed25519.GenPrivKey()
addr2 = sdk.AccAddress(priv2.PubKey().Address())
addr3 = sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address())
priv4 = ed25519.GenPrivKey()
addr4 = sdk.AccAddress(priv4.PubKey().Address())
coins = sdk.Coins{sdk.NewCoin("foocoin", sdk.NewInt(10))}
fee = auth.NewStdFee(
100000,
sdk.Coins{sdk.NewCoin("foocoin", sdk.NewInt(0))}...,
)
commissionMsg = NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec())
)
func NewTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt int64) MsgCreateValidator {
return types.NewMsgCreateValidator(
address, pubKey, sdk.NewCoin("steak", sdk.NewInt(amt)), Description{}, commissionMsg,
)
}
func NewTestMsgCreateValidatorWithCommission(address sdk.ValAddress, pubKey crypto.PubKey,
amt int64, commissionRate sdk.Dec) MsgCreateValidator {
commission := NewCommissionMsg(commissionRate, sdk.OneDec(), sdk.ZeroDec())
return types.NewMsgCreateValidator(
address, pubKey, sdk.NewCoin("steak", sdk.NewInt(amt)), Description{}, commission,
)
}
func NewTestMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amt int64) MsgDelegate {
return MsgDelegate{
DelegatorAddr: delAddr,
ValidatorAddr: valAddr,
Delegation: sdk.NewCoin("steak", sdk.NewInt(amt)),
}
}
func NewTestMsgCreateValidatorOnBehalfOf(delAddr sdk.AccAddress, valAddr sdk.ValAddress, valPubKey crypto.PubKey, amt int64) MsgCreateValidator {
return MsgCreateValidator{
Description: Description{},
Commission: commissionMsg,
DelegatorAddr: delAddr,
ValidatorAddr: valAddr,
PubKey: valPubKey,
Delegation: sdk.NewCoin("steak", sdk.NewInt(amt)),
}
}

View File

@ -164,6 +164,11 @@ func ErrTransitiveRedelegation(codespace sdk.CodespaceType) sdk.Error {
"redelegation to this validator already in progress, first redelegation to this validator must complete before next redelegation")
}
func ErrConflictingRedelegation(codespace sdk.CodespaceType) sdk.Error {
return sdk.NewError(codespace, CodeInvalidDelegation,
"conflicting redelegation from this source validator to this dest validator already exists, you must wait for it to finish")
}
func ErrBothShareMsgsGiven(codespace sdk.CodespaceType) sdk.Error {
return sdk.NewError(codespace, CodeInvalidInput, "both shares amount and shares percent provided")
}