Upgrade to tm version 23.0. (#1927)

* Start upgrade. Currently go test ./... hangs.

* (squash this) Fix staking tests

* wip

* note what changes need to be made to make this work on tm v0.23.0

* Fix addr -> pubkey map

* cleanup code

* Fix slashing test failures except for begin blocker

* fix all slashing tests

* fix lcd tests

* Address PR comments

* add link to changelog.

* (wip) start making addrToPubkey map persisted. Since amino can't handle maps,
we have to change from what this commit is doing.

* Use the correct method of storing a map

* (squash this) address PR comments

* Did you run 'make'?

* remove gaiadebug binary
This commit is contained in:
Dev Ojha
2018-08-12 03:33:48 -04:00
committed by Ethan Buchman
parent 691048c88a
commit b2a4aecc44
42 changed files with 260 additions and 166 deletions
+4 -4
View File
@@ -158,10 +158,10 @@ func (msg StdSignMsg) Bytes() []byte {
// Standard Signature
type StdSignature struct {
crypto.PubKey `json:"pub_key"` // optional
crypto.Signature `json:"signature"`
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
crypto.PubKey `json:"pub_key"` // optional
Signature []byte `json:"signature"`
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
}
// logic for standard transaction decoding
+14
View File
@@ -0,0 +1,14 @@
package slashing
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/types"
)
// InitGenesis initializes the keeper's address to pubkey map.
func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) {
for _, validator := range data.Validators {
keeper.addPubkey(ctx, validator.GetPubKey())
}
return
}
+1 -1
View File
@@ -39,7 +39,7 @@ func handleMsgUnrevoke(ctx sdk.Context, msg MsgUnrevoke, k Keeper) sdk.Result {
}
// Cannot be unrevoked until out of jail
if ctx.BlockHeader().Time < info.JailedUntil {
if ctx.BlockHeader().Time.Before(info.JailedUntil) {
return ErrValidatorJailed(k.codespace).Result()
}
+59 -10
View File
@@ -2,10 +2,14 @@ package slashing
import (
"fmt"
"time"
tmtypes "github.com/tendermint/tendermint/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/params"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
)
@@ -15,7 +19,6 @@ type Keeper struct {
cdc *wire.Codec
validatorSet sdk.ValidatorSet
params params.Getter
// codespace
codespace sdk.CodespaceType
}
@@ -33,10 +36,10 @@ func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, vs sdk.ValidatorSet, params pa
}
// handle a validator signing two blocks at the same height
func (k Keeper) handleDoubleSign(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight int64, timestamp int64, power int64) {
func (k Keeper) handleDoubleSign(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight int64, timestamp time.Time, power int64) {
logger := ctx.Logger().With("module", "x/slashing")
time := ctx.BlockHeader().Time
age := time - timestamp
age := time.Sub(timestamp)
address := sdk.ValAddress(pubkey.Address())
// Double sign too old
@@ -60,23 +63,26 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, pubkey crypto.PubKey, infracti
if !found {
panic(fmt.Sprintf("Expected signing info for validator %s but not found", address))
}
signInfo.JailedUntil = time + k.DoubleSignUnbondDuration(ctx)
signInfo.JailedUntil = time.Add(k.DoubleSignUnbondDuration(ctx))
k.setValidatorSigningInfo(ctx, address, signInfo)
}
// handle a validator signature, must be called once per validator per block
// nolint gocyclo
func (k Keeper) handleValidatorSignature(ctx sdk.Context, pubkey crypto.PubKey, power int64, signed bool) {
func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, power int64, signed bool) {
logger := ctx.Logger().With("module", "x/slashing")
height := ctx.BlockHeight()
address := sdk.ValAddress(pubkey.Address())
address := sdk.ValAddress(addr)
pubkey, err := k.getPubkey(ctx, addr)
if err != nil {
panic(fmt.Sprintf("Validator address %v not found", addr))
}
// Local index, so counts blocks validator *should* have signed
// Will use the 0-value default signing info if not present, except for start height
signInfo, found := k.getValidatorSigningInfo(ctx, address)
if !found {
// If this validator has never been seen before, construct a new SigningInfo with the correct start height
signInfo = NewValidatorSigningInfo(height, 0, 0, 0)
signInfo = NewValidatorSigningInfo(height, 0, time.Unix(0, 0), 0)
}
index := signInfo.IndexOffset % k.SignedBlocksWindow(ctx)
signInfo.IndexOffset++
@@ -98,7 +104,7 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, pubkey crypto.PubKey,
}
if !signed {
logger.Info(fmt.Sprintf("Absent validator %s at height %d, %d signed, threshold %d", pubkey.Address(), height, signInfo.SignedBlocksCounter, k.MinSignedPerWindow(ctx)))
logger.Info(fmt.Sprintf("Absent validator %s at height %d, %d signed, threshold %d", addr, height, signInfo.SignedBlocksCounter, k.MinSignedPerWindow(ctx)))
}
minHeight := signInfo.StartHeight + k.SignedBlocksWindow(ctx)
if height > minHeight && signInfo.SignedBlocksCounter < k.MinSignedPerWindow(ctx) {
@@ -109,7 +115,7 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, pubkey crypto.PubKey,
pubkey.Address(), minHeight, k.MinSignedPerWindow(ctx)))
k.validatorSet.Slash(ctx, pubkey, height, power, k.SlashFractionDowntime(ctx))
k.validatorSet.Revoke(ctx, pubkey)
signInfo.JailedUntil = ctx.BlockHeader().Time + k.DowntimeUnbondDuration(ctx)
signInfo.JailedUntil = ctx.BlockHeader().Time.Add(k.DowntimeUnbondDuration(ctx))
} else {
// Validator was (a) not found or (b) already revoked, don't slash
logger.Info(fmt.Sprintf("Validator %s would have been slashed for downtime, but was either not found in store or already revoked",
@@ -120,3 +126,46 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, pubkey crypto.PubKey,
// Set the updated signing info
k.setValidatorSigningInfo(ctx, address, signInfo)
}
// AddValidators adds the validators to the keepers validator addr to pubkey mapping.
func (k Keeper) AddValidators(ctx sdk.Context, vals []abci.Validator) {
for i := 0; i < len(vals); i++ {
val := vals[i]
pubkey, err := tmtypes.PB2TM.PubKey(val.PubKey)
if err != nil {
panic(err)
}
k.addPubkey(ctx, pubkey)
}
}
// TODO: Make a method to remove the pubkey from the map when a validator is unbonded.
func (k Keeper) addPubkey(ctx sdk.Context, pubkey crypto.PubKey) {
addr := pubkey.Address()
k.setAddrPubkeyRelation(ctx, addr, pubkey)
}
func (k Keeper) getPubkey(ctx sdk.Context, address crypto.Address) (crypto.PubKey, error) {
store := ctx.KVStore(k.storeKey)
var pubkey crypto.PubKey
err := k.cdc.UnmarshalBinary(store.Get(getAddrPubkeyRelationKey(address)), &pubkey)
if err != nil {
return nil, fmt.Errorf("address %v not found", address)
}
return pubkey, nil
}
func (k Keeper) setAddrPubkeyRelation(ctx sdk.Context, addr crypto.Address, pubkey crypto.PubKey) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinary(pubkey)
store.Set(getAddrPubkeyRelationKey(addr), bz)
}
func (k Keeper) deleteAddrPubkeyRelation(ctx sdk.Context, addr crypto.Address) {
store := ctx.KVStore(k.storeKey)
store.Delete(getAddrPubkeyRelationKey(addr))
}
func getAddrPubkeyRelationKey(address []byte) []byte {
return append([]byte{0x03}, address...)
}
+31 -26
View File
@@ -2,13 +2,12 @@ package slashing
import (
"testing"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
)
// Have to change these parameters for tests
@@ -29,15 +28,16 @@ func TestHandleDoubleSign(t *testing.T) {
addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
got := stake.NewHandler(sk)(ctx, newTestMsgCreateValidator(addr, val, amt))
require.True(t, got.IsOK())
stake.EndBlocker(ctx, sk)
validatorUpdates := stake.EndBlocker(ctx, sk)
keeper.AddValidators(ctx, validatorUpdates)
require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}})
require.True(t, sdk.NewRatFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower()))
// handle a signature to set signing info
keeper.handleValidatorSignature(ctx, val, amtInt, true)
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, true)
// double sign less than max age
keeper.handleDoubleSign(ctx, val, 0, 0, amtInt)
keeper.handleDoubleSign(ctx, val, 0, time.Unix(0, 0), amtInt)
// should be revoked
require.True(t, sk.Validator(ctx, addr).GetRevoked())
@@ -45,10 +45,10 @@ func TestHandleDoubleSign(t *testing.T) {
sk.Unrevoke(ctx, val)
// power should be reduced
require.Equal(t, sdk.NewRatFromInt(amt).Mul(sdk.NewRat(19).Quo(sdk.NewRat(20))), sk.Validator(ctx, addr).GetPower())
ctx = ctx.WithBlockHeader(abci.Header{Time: 1 + keeper.MaxEvidenceAge(ctx)})
ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(1, 0).Add(keeper.MaxEvidenceAge(ctx))})
// double sign past max age
keeper.handleDoubleSign(ctx, val, 0, 0, amtInt)
keeper.handleDoubleSign(ctx, val, 0, time.Unix(0, 0), amtInt)
require.Equal(t, sdk.NewRatFromInt(amt).Mul(sdk.NewRat(19).Quo(sdk.NewRat(20))), sk.Validator(ctx, addr).GetPower())
}
@@ -64,7 +64,8 @@ func TestHandleAbsentValidator(t *testing.T) {
slh := NewHandler(keeper)
got := sh(ctx, newTestMsgCreateValidator(addr, val, amt))
require.True(t, got.IsOK())
stake.EndBlocker(ctx, sk)
validatorUpdates := stake.EndBlocker(ctx, sk)
keeper.AddValidators(ctx, validatorUpdates)
require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}})
require.True(t, sdk.NewRatFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower()))
info, found := keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address()))
@@ -72,13 +73,15 @@ func TestHandleAbsentValidator(t *testing.T) {
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, int64(0), info.IndexOffset)
require.Equal(t, int64(0), info.SignedBlocksCounter)
require.Equal(t, int64(0), info.JailedUntil)
// default time.Time value
var blankTime time.Time
require.Equal(t, blankTime, info.JailedUntil)
height := int64(0)
// 1000 first blocks OK
for ; height < keeper.SignedBlocksWindow(ctx); height++ {
ctx = ctx.WithBlockHeight(height)
keeper.handleValidatorSignature(ctx, val, amtInt, true)
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, true)
}
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address()))
require.True(t, found)
@@ -88,7 +91,7 @@ func TestHandleAbsentValidator(t *testing.T) {
// 500 blocks missed
for ; height < keeper.SignedBlocksWindow(ctx)+(keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)); height++ {
ctx = ctx.WithBlockHeight(height)
keeper.handleValidatorSignature(ctx, val, amtInt, false)
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
}
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address()))
require.True(t, found)
@@ -103,7 +106,7 @@ func TestHandleAbsentValidator(t *testing.T) {
// 501st block missed
ctx = ctx.WithBlockHeight(height)
keeper.handleValidatorSignature(ctx, val, amtInt, false)
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
@@ -118,7 +121,7 @@ func TestHandleAbsentValidator(t *testing.T) {
require.False(t, got.IsOK())
// unrevocation should succeed after jail expiration
ctx = ctx.WithBlockHeader(abci.Header{Time: keeper.DowntimeUnbondDuration(ctx) + 1})
ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(1, 0).Add(keeper.DowntimeUnbondDuration(ctx))})
got = slh(ctx, NewMsgUnrevoke(addr))
require.True(t, got.IsOK())
@@ -140,7 +143,7 @@ func TestHandleAbsentValidator(t *testing.T) {
// validator should not be immediately revoked again
height++
ctx = ctx.WithBlockHeight(height)
keeper.handleValidatorSignature(ctx, val, amtInt, false)
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
validator, _ = sk.GetValidatorByPubKey(ctx, val)
require.Equal(t, sdk.Bonded, validator.GetStatus())
@@ -148,14 +151,14 @@ func TestHandleAbsentValidator(t *testing.T) {
nextHeight := height + keeper.MinSignedPerWindow(ctx) + 1
for ; height < nextHeight; height++ {
ctx = ctx.WithBlockHeight(height)
keeper.handleValidatorSignature(ctx, val, amtInt, false)
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
}
// validator should be revoked again after 500 unsigned blocks
nextHeight = height + keeper.MinSignedPerWindow(ctx) + 1
for ; height <= nextHeight; height++ {
ctx = ctx.WithBlockHeight(height)
keeper.handleValidatorSignature(ctx, val, amtInt, false)
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
}
validator, _ = sk.GetValidatorByPubKey(ctx, val)
require.Equal(t, sdk.Unbonded, validator.GetStatus())
@@ -171,7 +174,8 @@ func TestHandleNewValidator(t *testing.T) {
sh := stake.NewHandler(sk)
got := sh(ctx, newTestMsgCreateValidator(addr, val, sdk.NewInt(amt)))
require.True(t, got.IsOK())
stake.EndBlocker(ctx, sk)
validatorUpdates := stake.EndBlocker(ctx, sk)
keeper.AddValidators(ctx, validatorUpdates)
require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.SubRaw(amt)}})
require.Equal(t, sdk.NewRat(amt), sk.Validator(ctx, addr).GetPower())
@@ -179,16 +183,16 @@ func TestHandleNewValidator(t *testing.T) {
ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 1)
// Now a validator, for two blocks
keeper.handleValidatorSignature(ctx, val, 100, true)
keeper.handleValidatorSignature(ctx, val.Address(), 100, true)
ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 2)
keeper.handleValidatorSignature(ctx, val, 100, false)
keeper.handleValidatorSignature(ctx, val.Address(), 100, false)
info, found := keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(keeper.SignedBlocksWindow(ctx)+1), info.StartHeight)
require.Equal(t, int64(2), info.IndexOffset)
require.Equal(t, int64(1), info.SignedBlocksCounter)
require.Equal(t, int64(0), info.JailedUntil)
require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil)
// validator should be bonded still, should not have been revoked or slashed
validator, _ := sk.GetValidatorByPubKey(ctx, val)
@@ -208,19 +212,20 @@ func TestHandleAlreadyRevoked(t *testing.T) {
sh := stake.NewHandler(sk)
got := sh(ctx, newTestMsgCreateValidator(addr, val, amt))
require.True(t, got.IsOK())
stake.EndBlocker(ctx, sk)
validatorUpdates := stake.EndBlocker(ctx, sk)
keeper.AddValidators(ctx, validatorUpdates)
// 1000 first blocks OK
height := int64(0)
for ; height < keeper.SignedBlocksWindow(ctx); height++ {
ctx = ctx.WithBlockHeight(height)
keeper.handleValidatorSignature(ctx, val, amtInt, true)
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, true)
}
// 501 blocks missed
for ; height < keeper.SignedBlocksWindow(ctx)+(keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx))+1; height++ {
ctx = ctx.WithBlockHeight(height)
keeper.handleValidatorSignature(ctx, val, amtInt, false)
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
}
// validator should have been revoked and slashed
@@ -232,7 +237,7 @@ func TestHandleAlreadyRevoked(t *testing.T) {
// another block missed
ctx = ctx.WithBlockHeight(height)
keeper.handleValidatorSignature(ctx, val, amtInt, false)
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
// validator should not have been slashed twice
validator, _ = sk.GetValidatorByPubKey(ctx, val)
+8 -6
View File
@@ -1,6 +1,8 @@
package slashing
import (
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@@ -17,8 +19,8 @@ const (
// MaxEvidenceAge - Max age for evidence - 21 days (3 weeks)
// MaxEvidenceAge = 60 * 60 * 24 * 7 * 3
func (k Keeper) MaxEvidenceAge(ctx sdk.Context) int64 {
return k.params.GetInt64WithDefault(ctx, MaxEvidenceAgeKey, defaultMaxEvidenceAge)
func (k Keeper) MaxEvidenceAge(ctx sdk.Context) time.Duration {
return time.Duration(k.params.GetInt64WithDefault(ctx, MaxEvidenceAgeKey, defaultMaxEvidenceAge)) * time.Second
}
// SignedBlocksWindow - sliding window for downtime slashing
@@ -34,13 +36,13 @@ func (k Keeper) MinSignedPerWindow(ctx sdk.Context) int64 {
}
// Double-sign unbond duration
func (k Keeper) DoubleSignUnbondDuration(ctx sdk.Context) int64 {
return k.params.GetInt64WithDefault(ctx, DoubleSignUnbondDurationKey, defaultDoubleSignUnbondDuration)
func (k Keeper) DoubleSignUnbondDuration(ctx sdk.Context) time.Duration {
return time.Duration(k.params.GetInt64WithDefault(ctx, DoubleSignUnbondDurationKey, defaultDoubleSignUnbondDuration)) * time.Second
}
// Downtime unbond duration
func (k Keeper) DowntimeUnbondDuration(ctx sdk.Context) int64 {
return k.params.GetInt64WithDefault(ctx, DowntimeUnbondDurationKey, defaultDowntimeUnbondDuration)
func (k Keeper) DowntimeUnbondDuration(ctx sdk.Context) time.Duration {
return time.Duration(k.params.GetInt64WithDefault(ctx, DowntimeUnbondDurationKey, defaultDowntimeUnbondDuration)) * time.Second
}
// SlashFractionDoubleSign - currently default 5%
+7 -6
View File
@@ -3,6 +3,7 @@ package slashing
import (
"encoding/binary"
"fmt"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@@ -48,7 +49,7 @@ func (k Keeper) setValidatorSigningBitArray(ctx sdk.Context, address sdk.ValAddr
}
// Construct a new `ValidatorSigningInfo` struct
func NewValidatorSigningInfo(startHeight int64, indexOffset int64, jailedUntil int64, signedBlocksCounter int64) ValidatorSigningInfo {
func NewValidatorSigningInfo(startHeight int64, indexOffset int64, jailedUntil time.Time, signedBlocksCounter int64) ValidatorSigningInfo {
return ValidatorSigningInfo{
StartHeight: startHeight,
IndexOffset: indexOffset,
@@ -59,15 +60,15 @@ func NewValidatorSigningInfo(startHeight int64, indexOffset int64, jailedUntil i
// Signing info for a validator
type ValidatorSigningInfo struct {
StartHeight int64 `json:"start_height"` // height at which validator was first a candidate OR was unrevoked
IndexOffset int64 `json:"index_offset"` // index offset into signed block bit array
JailedUntil int64 `json:"jailed_until"` // timestamp validator cannot be unrevoked until
SignedBlocksCounter int64 `json:"signed_blocks_counter"` // signed blocks counter (to avoid scanning the array every time)
StartHeight int64 `json:"start_height"` // height at which validator was first a candidate OR was unrevoked
IndexOffset int64 `json:"index_offset"` // index offset into signed block bit array
JailedUntil time.Time `json:"jailed_until"` // timestamp validator cannot be unrevoked until
SignedBlocksCounter int64 `json:"signed_blocks_counter"` // signed 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: %d, signed blocks counter: %d",
return fmt.Sprintf("Start height: %d, index offset: %d, jailed until: %v, signed blocks counter: %d",
i.StartHeight, i.IndexOffset, i.JailedUntil, i.SignedBlocksCounter)
}
+3 -2
View File
@@ -2,6 +2,7 @@ package slashing
import (
"testing"
"time"
"github.com/stretchr/testify/require"
@@ -15,7 +16,7 @@ func TestGetSetValidatorSigningInfo(t *testing.T) {
newInfo := ValidatorSigningInfo{
StartHeight: int64(4),
IndexOffset: int64(3),
JailedUntil: int64(2),
JailedUntil: time.Unix(2, 0),
SignedBlocksCounter: int64(10),
}
keeper.setValidatorSigningInfo(ctx, sdk.ValAddress(addrs[0]), newInfo)
@@ -23,7 +24,7 @@ func TestGetSetValidatorSigningInfo(t *testing.T) {
require.True(t, found)
require.Equal(t, info.StartHeight, int64(4))
require.Equal(t, info.IndexOffset, int64(3))
require.Equal(t, info.JailedUntil, int64(2))
require.Equal(t, info.JailedUntil, time.Unix(2, 0).UTC())
require.Equal(t, info.SignedBlocksCounter, int64(10))
}
+2 -6
View File
@@ -20,13 +20,9 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, sk Keeper) (tags
// Iterate over all the validators which *should* have signed this block
// 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 _, signingValidator := range req.Validators {
for _, signingValidator := range req.LastCommitInfo.GetValidators() {
present := signingValidator.SignedLastBlock
pubkey, err := tmtypes.PB2TM.PubKey(signingValidator.Validator.PubKey)
if err != nil {
panic(err)
}
sk.handleValidatorSignature(ctx, pubkey, signingValidator.Validator.Power, present)
sk.handleValidatorSignature(ctx, signingValidator.Validator.Address, signingValidator.Validator.Power, present)
}
// Iterate through any newly discovered evidence of infraction
+24 -17
View File
@@ -2,11 +2,11 @@ package slashing
import (
"testing"
"time"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake"
@@ -19,21 +19,24 @@ func TestBeginBlocker(t *testing.T) {
// bond the validator
got := stake.NewHandler(sk)(ctx, newTestMsgCreateValidator(addr, pk, amt))
require.True(t, got.IsOK())
stake.EndBlocker(ctx, sk)
validatorUpdates := stake.EndBlocker(ctx, sk)
keeper.AddValidators(ctx, validatorUpdates)
require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}})
require.True(t, sdk.NewRatFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower()))
val := abci.Validator{
PubKey: tmtypes.TM2PB.PubKey(pk),
Power: amt.Int64(),
Address: pk.Address(),
Power: amt.Int64(),
}
// mark the validator as having signed
req := abci.RequestBeginBlock{
Validators: []abci.SigningValidator{{
Validator: val,
SignedLastBlock: true,
}},
LastCommitInfo: abci.LastCommitInfo{
Validators: []abci.SigningValidator{{
Validator: val,
SignedLastBlock: true,
}},
},
}
BeginBlocker(ctx, req, keeper)
@@ -41,7 +44,7 @@ func TestBeginBlocker(t *testing.T) {
require.True(t, found)
require.Equal(t, ctx.BlockHeight(), info.StartHeight)
require.Equal(t, int64(1), info.IndexOffset)
require.Equal(t, int64(0), info.JailedUntil)
require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil)
require.Equal(t, int64(1), info.SignedBlocksCounter)
height := int64(0)
@@ -50,10 +53,12 @@ func TestBeginBlocker(t *testing.T) {
for ; height < keeper.SignedBlocksWindow(ctx); height++ {
ctx = ctx.WithBlockHeight(height)
req = abci.RequestBeginBlock{
Validators: []abci.SigningValidator{{
Validator: val,
SignedLastBlock: true,
}},
LastCommitInfo: abci.LastCommitInfo{
Validators: []abci.SigningValidator{{
Validator: val,
SignedLastBlock: true,
}},
},
}
BeginBlocker(ctx, req, keeper)
}
@@ -62,10 +67,12 @@ func TestBeginBlocker(t *testing.T) {
for ; height < ((keeper.SignedBlocksWindow(ctx) * 2) - keeper.MinSignedPerWindow(ctx) + 1); height++ {
ctx = ctx.WithBlockHeight(height)
req = abci.RequestBeginBlock{
Validators: []abci.SigningValidator{{
Validator: val,
SignedLastBlock: false,
}},
LastCommitInfo: abci.LastCommitInfo{
Validators: []abci.SigningValidator{{
Validator: val,
SignedLastBlock: false,
}},
},
}
BeginBlocker(ctx, req, keeper)
}
+3 -1
View File
@@ -1,6 +1,8 @@
package stake
import (
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/keeper"
"github.com/cosmos/cosmos-sdk/x/stake/tags"
@@ -38,7 +40,7 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Valid
// Process provision inflation
blockTime := ctx.BlockHeader().Time
if blockTime-pool.InflationLastTime >= 3600 {
if blockTime.Sub(pool.InflationLastTime) >= time.Hour {
params := k.GetParams(ctx)
pool.InflationLastTime = blockTime
pool = pool.ProcessProvisions(params)
+9 -8
View File
@@ -2,6 +2,7 @@ package stake
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -500,7 +501,7 @@ func TestUnbondingPeriod(t *testing.T) {
// set the unbonding time
params := keeper.GetParams(ctx)
params.UnbondingTime = 7
params.UnbondingTime = 7 * time.Second
keeper.SetParams(ctx, params)
// create the validator
@@ -516,19 +517,19 @@ func TestUnbondingPeriod(t *testing.T) {
// cannot complete unbonding at same time
msgCompleteUnbonding := NewMsgCompleteUnbonding(validatorAddr, validatorAddr)
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
require.True(t, !got.IsOK(), "expected no error")
require.True(t, !got.IsOK(), "expected an error")
// cannot complete unbonding at time 6 seconds later
origHeader := ctx.BlockHeader()
headerTime6 := origHeader
headerTime6.Time += 6
headerTime6.Time = headerTime6.Time.Add(time.Second * 6)
ctx = ctx.WithBlockHeader(headerTime6)
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
require.True(t, !got.IsOK(), "expected no error")
require.True(t, !got.IsOK(), "expected an error")
// can complete unbonding at time 7 seconds later
headerTime7 := origHeader
headerTime7.Time += 7
headerTime7.Time = headerTime7.Time.Add(time.Second * 7)
ctx = ctx.WithBlockHeader(headerTime7)
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
require.True(t, got.IsOK(), "expected no error")
@@ -541,7 +542,7 @@ func TestRedelegationPeriod(t *testing.T) {
// set the unbonding time
params := keeper.GetParams(ctx)
params.UnbondingTime = 7
params.UnbondingTime = 7 * time.Second
keeper.SetParams(ctx, params)
// create the validators
@@ -580,14 +581,14 @@ func TestRedelegationPeriod(t *testing.T) {
// cannot complete redelegation at time 6 seconds later
origHeader := ctx.BlockHeader()
headerTime6 := origHeader
headerTime6.Time += 6
headerTime6.Time = headerTime6.Time.Add(time.Second * 6)
ctx = ctx.WithBlockHeader(headerTime6)
got = handleMsgCompleteRedelegate(ctx, msgCompleteRedelegate, keeper)
require.True(t, !got.IsOK(), "expected an error")
// can complete redelegation at time 7 seconds later
headerTime7 := origHeader
headerTime7.Time += 7
headerTime7.Time = headerTime7.Time.Add(time.Second * 7)
ctx = ctx.WithBlockHeader(headerTime7)
got = handleMsgCompleteRedelegate(ctx, msgCompleteRedelegate, keeper)
require.True(t, got.IsOK(), "expected no error")
+4 -4
View File
@@ -327,7 +327,7 @@ func (k Keeper) BeginUnbonding(ctx sdk.Context, delegatorAddr, validatorAddr sdk
// create the unbonding delegation
params := k.GetParams(ctx)
minTime := ctx.BlockHeader().Time + params.UnbondingTime
minTime := ctx.BlockHeader().Time.Add(params.UnbondingTime)
balance := sdk.Coin{params.BondDenom, returnAmount.RoundInt()}
ubd := types.UnbondingDelegation{
@@ -351,7 +351,7 @@ func (k Keeper) CompleteUnbonding(ctx sdk.Context, delegatorAddr, validatorAddr
// ensure that enough time has passed
ctxTime := ctx.BlockHeader().Time
if ubd.MinTime > ctxTime {
if ubd.MinTime.After(ctxTime) {
return types.ErrNotMature(k.Codespace(), "unbonding", "unit-time", ubd.MinTime, ctxTime)
}
@@ -389,7 +389,7 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delegatorAddr, validatorSrcAd
}
// create the unbonding delegation
minTime := ctx.BlockHeader().Time + params.UnbondingTime
minTime := ctx.BlockHeader().Time.Add(params.UnbondingTime)
red := types.Redelegation{
DelegatorAddr: delegatorAddr,
@@ -415,7 +415,7 @@ func (k Keeper) CompleteRedelegation(ctx sdk.Context, delegatorAddr, validatorSr
// ensure that enough time has passed
ctxTime := ctx.BlockHeader().Time
if red.MinTime > ctxTime {
if red.MinTime.After(ctxTime) {
return types.ErrNotMature(k.Codespace(), "redelegation", "unit-time", red.MinTime, ctxTime)
}
+4 -3
View File
@@ -2,6 +2,7 @@ package keeper
import (
"testing"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/types"
@@ -115,7 +116,7 @@ func TestUnbondingDelegation(t *testing.T) {
DelegatorAddr: addrDels[0],
ValidatorAddr: addrVals[0],
CreationHeight: 0,
MinTime: 0,
MinTime: time.Unix(0, 0),
Balance: sdk.NewInt64Coin("steak", 5),
}
@@ -188,7 +189,7 @@ func TestGetRedelegationsFromValidator(t *testing.T) {
ValidatorSrcAddr: addrVals[0],
ValidatorDstAddr: addrVals[1],
CreationHeight: 0,
MinTime: 0,
MinTime: time.Unix(0, 0),
SharesSrc: sdk.NewRat(5),
SharesDst: sdk.NewRat(5),
}
@@ -218,7 +219,7 @@ func TestRedelegation(t *testing.T) {
ValidatorSrcAddr: addrVals[0],
ValidatorDstAddr: addrVals[1],
CreationHeight: 0,
MinTime: 0,
MinTime: time.Unix(0, 0),
SharesSrc: sdk.NewRat(5),
SharesDst: sdk.NewRat(5),
}
+2 -2
View File
@@ -159,7 +159,7 @@ func (k Keeper) slashUnbondingDelegation(ctx sdk.Context, unbondingDelegation ty
return sdk.ZeroRat()
}
if unbondingDelegation.MinTime < now {
if unbondingDelegation.MinTime.Before(now) {
// Unbonding delegation no longer eligible for slashing, skip it
// TODO Settle and delete it automatically?
return sdk.ZeroRat()
@@ -203,7 +203,7 @@ func (k Keeper) slashRedelegation(ctx sdk.Context, validator types.Validator, re
return sdk.ZeroRat()
}
if redelegation.MinTime < now {
if redelegation.MinTime.Before(now) {
// Redelegation no longer eligible for slashing, skip it
// TODO Delete it automatically?
return sdk.ZeroRat()
+11 -10
View File
@@ -2,6 +2,7 @@ package keeper
import (
"testing"
"time"
"github.com/stretchr/testify/require"
@@ -70,7 +71,7 @@ func TestSlashUnbondingDelegation(t *testing.T) {
ValidatorAddr: addrVals[0],
CreationHeight: 0,
// expiration timestamp (beyond which the unbonding delegation shouldn't be slashed)
MinTime: 0,
MinTime: time.Unix(0, 0),
InitialBalance: sdk.NewInt64Coin(params.BondDenom, 10),
Balance: sdk.NewInt64Coin(params.BondDenom, 10),
}
@@ -81,14 +82,14 @@ func TestSlashUnbondingDelegation(t *testing.T) {
require.Equal(t, int64(0), slashAmount.RoundInt64())
// after the expiration time, no longer eligible for slashing
ctx = ctx.WithBlockHeader(abci.Header{Time: int64(10)})
ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(10, 0)})
keeper.SetUnbondingDelegation(ctx, ubd)
slashAmount = keeper.slashUnbondingDelegation(ctx, ubd, 0, fraction)
require.Equal(t, int64(0), slashAmount.RoundInt64())
// test valid slash, before expiration timestamp and to which stake contributed
oldPool := keeper.GetPool(ctx)
ctx = ctx.WithBlockHeader(abci.Header{Time: int64(0)})
ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(0, 0)})
keeper.SetUnbondingDelegation(ctx, ubd)
slashAmount = keeper.slashUnbondingDelegation(ctx, ubd, 0, fraction)
require.Equal(t, int64(5), slashAmount.RoundInt64())
@@ -114,7 +115,7 @@ func TestSlashRedelegation(t *testing.T) {
ValidatorDstAddr: addrVals[1],
CreationHeight: 0,
// expiration timestamp (beyond which the redelegation shouldn't be slashed)
MinTime: 0,
MinTime: time.Unix(0, 0),
SharesSrc: sdk.NewRat(10),
SharesDst: sdk.NewRat(10),
InitialBalance: sdk.NewInt64Coin(params.BondDenom, 10),
@@ -137,7 +138,7 @@ func TestSlashRedelegation(t *testing.T) {
require.Equal(t, int64(0), slashAmount.RoundInt64())
// after the expiration time, no longer eligible for slashing
ctx = ctx.WithBlockHeader(abci.Header{Time: int64(10)})
ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(10, 0)})
keeper.SetRedelegation(ctx, rd)
validator, found = keeper.GetValidator(ctx, addrVals[1])
require.True(t, found)
@@ -146,7 +147,7 @@ func TestSlashRedelegation(t *testing.T) {
// test valid slash, before expiration timestamp and to which stake contributed
oldPool := keeper.GetPool(ctx)
ctx = ctx.WithBlockHeader(abci.Header{Time: int64(0)})
ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(0, 0)})
keeper.SetRedelegation(ctx, rd)
validator, found = keeper.GetValidator(ctx, addrVals[1])
require.True(t, found)
@@ -209,7 +210,7 @@ func TestSlashWithUnbondingDelegation(t *testing.T) {
ValidatorAddr: addrVals[0],
CreationHeight: 11,
// expiration timestamp (beyond which the unbonding delegation shouldn't be slashed)
MinTime: 0,
MinTime: time.Unix(0, 0),
InitialBalance: sdk.NewInt64Coin(params.BondDenom, 4),
Balance: sdk.NewInt64Coin(params.BondDenom, 4),
}
@@ -310,7 +311,7 @@ func TestSlashWithRedelegation(t *testing.T) {
ValidatorSrcAddr: addrVals[0],
ValidatorDstAddr: addrVals[1],
CreationHeight: 11,
MinTime: 0,
MinTime: time.Unix(0, 0),
SharesSrc: sdk.NewRat(6),
SharesDst: sdk.NewRat(6),
InitialBalance: sdk.NewInt64Coin(params.BondDenom, 6),
@@ -432,7 +433,7 @@ func TestSlashBoth(t *testing.T) {
ValidatorDstAddr: addrVals[1],
CreationHeight: 11,
// expiration timestamp (beyond which the redelegation shouldn't be slashed)
MinTime: 0,
MinTime: time.Unix(0, 0),
SharesSrc: sdk.NewRat(6),
SharesDst: sdk.NewRat(6),
InitialBalance: sdk.NewInt64Coin(params.BondDenom, 6),
@@ -454,7 +455,7 @@ func TestSlashBoth(t *testing.T) {
ValidatorAddr: addrVals[0],
CreationHeight: 11,
// expiration timestamp (beyond which the unbonding delegation shouldn't be slashed)
MinTime: 0,
MinTime: time.Unix(0, 0),
InitialBalance: sdk.NewInt64Coin(params.BondDenom, 4),
Balance: sdk.NewInt64Coin(params.BondDenom, 4),
}
+5 -4
View File
@@ -4,6 +4,7 @@ import (
"bytes"
"errors"
"fmt"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
@@ -100,14 +101,14 @@ type UnbondingDelegation struct {
DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // delegator
ValidatorAddr sdk.AccAddress `json:"validator_addr"` // validator unbonding from owner addr
CreationHeight int64 `json:"creation_height"` // height which the unbonding took place
MinTime int64 `json:"min_time"` // unix time for unbonding completion
MinTime time.Time `json:"min_time"` // unix time for unbonding completion
InitialBalance sdk.Coin `json:"initial_balance"` // atoms initially scheduled to receive at completion
Balance sdk.Coin `json:"balance"` // atoms to receive at completion
}
type ubdValue struct {
CreationHeight int64
MinTime int64
MinTime time.Time
InitialBalance sdk.Coin
Balance sdk.Coin
}
@@ -186,7 +187,7 @@ type Redelegation struct {
ValidatorSrcAddr sdk.AccAddress `json:"validator_src_addr"` // validator redelegation source owner addr
ValidatorDstAddr sdk.AccAddress `json:"validator_dst_addr"` // validator redelegation destination owner addr
CreationHeight int64 `json:"creation_height"` // height which the redelegation took place
MinTime int64 `json:"min_time"` // unix time for redelegation completion
MinTime time.Time `json:"min_time"` // unix time for redelegation completion
InitialBalance sdk.Coin `json:"initial_balance"` // initial balance when redelegation started
Balance sdk.Coin `json:"balance"` // current balance
SharesSrc sdk.Rat `json:"shares_src"` // amount of source shares redelegating
@@ -195,7 +196,7 @@ type Redelegation struct {
type redValue struct {
CreationHeight int64
MinTime int64
MinTime time.Time
InitialBalance sdk.Coin
Balance sdk.Coin
SharesSrc sdk.Rat
+3 -2
View File
@@ -2,6 +2,7 @@ package types
import (
"testing"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
@@ -57,7 +58,7 @@ func TestUnbondingDelegationEqual(t *testing.T) {
require.True(t, ok)
ud2.ValidatorAddr = addr3
ud2.MinTime = 20 * 20 * 2
ud2.MinTime = time.Unix(20*20*2, 0)
ok = ud1.Equal(ud2)
require.False(t, ok)
@@ -93,7 +94,7 @@ func TestRedelegationEqual(t *testing.T) {
r2.SharesDst = sdk.NewRat(10)
r2.SharesSrc = sdk.NewRat(20)
r2.MinTime = 20 * 20 * 2
r2.MinTime = time.Unix(20*20*2, 0)
ok = r1.Equal(r2)
require.False(t, ok)
+2 -1
View File
@@ -3,6 +3,7 @@ package types
import (
"fmt"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@@ -110,7 +111,7 @@ func ErrBadSharesPercent(codespace sdk.CodespaceType) sdk.Error {
return sdk.NewError(codespace, CodeInvalidDelegation, "shares percent must be >0 and <=1")
}
func ErrNotMature(codespace sdk.CodespaceType, operation, descriptor string, got, min int64) sdk.Error {
func ErrNotMature(codespace sdk.CodespaceType, operation, descriptor string, got, min time.Time) sdk.Error {
msg := fmt.Sprintf("%v is not mature requires a min %v of %v, currently it is %v",
operation, descriptor, got, min)
return sdk.NewError(codespace, CodeUnauthorized, msg)
+3 -2
View File
@@ -2,13 +2,14 @@ package types
import (
"bytes"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// defaultUnbondingTime reflects three weeks in seconds as the default
// unbonding time.
const defaultUnbondingTime int64 = 60 * 60 * 24 * 3
const defaultUnbondingTime time.Duration = 60 * 60 * 24 * 3 * time.Second
// Params defines the high level settings for staking
type Params struct {
@@ -17,7 +18,7 @@ type Params struct {
InflationMin sdk.Rat `json:"inflation_min"` // minimum inflation rate
GoalBonded sdk.Rat `json:"goal_bonded"` // Goal of percent bonded atoms
UnbondingTime int64 `json:"unbonding_time"`
UnbondingTime time.Duration `json:"unbonding_time"`
MaxValidators uint16 `json:"max_validators"` // maximum number of validators
BondDenom string `json:"bond_denom"` // bondable coin denomination
+6 -5
View File
@@ -3,16 +3,17 @@ package types
import (
"bytes"
"fmt"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Pool - dynamic parameters of the current state
type Pool struct {
LooseTokens sdk.Rat `json:"loose_tokens"` // tokens which are not bonded in a validator
BondedTokens sdk.Rat `json:"bonded_tokens"` // reserve of bonded tokens
InflationLastTime int64 `json:"inflation_last_time"` // block which the last inflation was processed // TODO make time
Inflation sdk.Rat `json:"inflation"` // current annual inflation rate
LooseTokens sdk.Rat `json:"loose_tokens"` // tokens which are not bonded in a validator
BondedTokens sdk.Rat `json:"bonded_tokens"` // reserve of bonded tokens
InflationLastTime time.Time `json:"inflation_last_time"` // block which the last inflation was processed
Inflation sdk.Rat `json:"inflation"` // current annual inflation rate
DateLastCommissionReset int64 `json:"date_last_commission_reset"` // unix timestamp for last commission accounting reset (daily)
@@ -32,7 +33,7 @@ func InitialPool() Pool {
return Pool{
LooseTokens: sdk.ZeroRat(),
BondedTokens: sdk.ZeroRat(),
InflationLastTime: 0,
InflationLastTime: time.Unix(0, 0),
Inflation: sdk.NewRat(7, 100),
DateLastCommissionReset: 0,
PrevBondedShares: sdk.ZeroRat(),
+3 -2
View File
@@ -3,6 +3,7 @@ package types
import (
"fmt"
"testing"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/assert"
@@ -184,7 +185,7 @@ func TestRemoveDelShares(t *testing.T) {
pool := Pool{
BondedTokens: sdk.NewRat(248305),
LooseTokens: sdk.NewRat(232147),
InflationLastTime: 0,
InflationLastTime: time.Unix(0, 0),
Inflation: sdk.NewRat(7, 100),
}
shares := sdk.NewRat(29)
@@ -232,7 +233,7 @@ func TestPossibleOverflow(t *testing.T) {
pool := Pool{
LooseTokens: sdk.NewRat(100),
BondedTokens: poolTokens,
InflationLastTime: 0,
InflationLastTime: time.Unix(0, 0),
Inflation: sdk.NewRat(7, 100),
}
tokens := int64(71)