diff --git a/Gopkg.lock b/Gopkg.lock index 145716a47b86..8fcb2fb5e5b5 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -423,7 +423,7 @@ version = "v0.9.2" [[projects]] - digest = "1:eb0f8bee357e6c28c9ad5fa074545b5085d0dcf580ba0e7024ab8c3285a5c815" + digest = "1:26146cdb2811ce481e72138439b9b1aa17a64d54364f96bb92f97a9ef8ba4f01" name = "github.com/tendermint/tendermint" packages = [ "abci/client", @@ -486,8 +486,8 @@ "version", ] pruneopts = "UT" - revision = "d542d2c3945116697f60451e6a407082c41c3cc9" - version = "v0.22.8" + revision = "013b9cef642f875634c614019ab13b17570778ad" + version = "v0.23.0" [[projects]] digest = "1:5bd938386bd1f61a581bf8cd6ff2b7b2f79c542929176db4ceb44965440dae07" @@ -498,11 +498,14 @@ [[projects]] branch = "master" - digest = "1:65a21a9e051d54eb6a3f70c659a765f706a998d9287c302269f4ed8054b2a852" + digest = "1:7a71fffde456d746c52f9cd09c50b034533a3180fb1f6320abb149f2ccc579e5" name = "golang.org/x/crypto" packages = [ "blowfish", + "chacha20poly1305", "curve25519", + "hkdf", + "internal/chacha20", "internal/subtle", "nacl/box", "nacl/secretbox", @@ -534,9 +537,12 @@ [[projects]] branch = "master" - digest = "1:4d7a8265af700258feaff86722049eb5b787240d66dfaf45ff4962f09de6e0be" + digest = "1:4d64ef38a30b73db6e8e7a2824b7fd356d921e0ee3fdd3248658996821d3b47d" name = "golang.org/x/sys" - packages = ["unix"] + packages = [ + "cpu", + "unix", + ] pruneopts = "UT" revision = "acbc56fc7007d2a01796d5bde54f39e3b3e95945" diff --git a/Gopkg.toml b/Gopkg.toml index 10c992351d0b..1c7019ae6e6a 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -57,7 +57,7 @@ [[override]] name = "github.com/tendermint/tendermint" - version = "=v0.22.8" + version = "=v0.23.0" [[constraint]] name = "github.com/bartekn/go-bip39" diff --git a/PENDING.md b/PENDING.md index a0274a4430da..f76dbecb3e90 100644 --- a/PENDING.md +++ b/PENDING.md @@ -1,3 +1,10 @@ +## v0.24.0 PENDING +^--- PENDING wasn't purged on sdk v0.23.0 release. + +BREAKING CHANGES +* Update to tendermint v0.23.0. This involves removing crypto.Pubkey, +maintaining a validator address to pubkey map, and using time.Time instead of int64 for time. [SDK PR](https://github.com/cosmos/cosmos-sdk/pull/1927) + ## PENDING BREAKING CHANGES diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 03a374bcd522..d7398d899396 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -387,7 +387,8 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg } // set the signed validators for addition to context in deliverTx - app.signedValidators = req.Validators + // TODO: communicate this result to the address to pubkey map in slashing + app.signedValidators = req.LastCommitInfo.GetValidators() return } @@ -412,11 +413,7 @@ func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) { Log: result.Log, GasWanted: result.GasWanted, GasUsed: result.GasUsed, - Fee: cmn.KI64Pair{ - []byte(result.FeeDenom), - result.FeeAmount, - }, - Tags: result.Tags, + Tags: result.Tags, } } diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index b8909e226a02..cbc1a2c20d7c 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -6,6 +6,7 @@ import ( "net/http" "regexp" "testing" + "time" "github.com/cosmos/cosmos-sdk/client/tx" @@ -564,7 +565,7 @@ func TestUnrevoke(t *testing.T) { signingInfo := getSigningInfo(t, port, pkString) tests.WaitForHeight(4, port) require.Equal(t, true, signingInfo.IndexOffset > 0) - require.Equal(t, int64(0), signingInfo.JailedUntil) + require.Equal(t, time.Unix(0, 0).UTC(), signingInfo.JailedUntil) require.Equal(t, true, signingInfo.SignedBlocksCounter > 0) } diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 48de9598c28d..ee46b267dd08 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -223,7 +223,7 @@ func startTM( proxy.NewLocalClientCreator(app), genDocProvider, dbProvider, - nm.DefaultMetricsProvider, + nm.DefaultMetricsProvider(tmcfg.Instrumentation), logger.With("module", "node"), ) if err != nil { diff --git a/client/tx/sign.go b/client/tx/sign.go index 75239ef7b33b..786c7fa0bd63 100644 --- a/client/tx/sign.go +++ b/client/tx/sign.go @@ -44,5 +44,5 @@ func SignTxRequstHandler(w http.ResponseWriter, r *http.Request) { return } - w.Write(sig.Bytes()) + w.Write(sig) } diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index bb4dc67f5653..9b318c8b17b0 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -148,7 +148,8 @@ func (app *GaiaApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) ab 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, @@ -181,6 +182,9 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci // return sdk.ErrGenesisParse("").TraceCause(err, "") } + // load the address to pubkey map + slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.StakeData) + gov.InitGenesis(ctx, app.govKeeper, gov.DefaultGenesisState()) return abci.ResponseInitChain{ diff --git a/cmd/gaia/cmd/gaiadebug/gaiadebug b/cmd/gaia/cmd/gaiadebug/gaiadebug deleted file mode 100755 index 642291fd3e65..000000000000 Binary files a/cmd/gaia/cmd/gaiadebug/gaiadebug and /dev/null differ diff --git a/crypto/encode_test.go b/crypto/encode_test.go index a2b5b1aea67b..9e70978555dc 100644 --- a/crypto/encode_test.go +++ b/crypto/encode_test.go @@ -15,12 +15,14 @@ type byter interface { Bytes() []byte } -func checkAminoBinary(t *testing.T, src byter, dst interface{}, size int) { +func checkAminoBinary(t *testing.T, src, dst interface{}, size int) { // Marshal to binary bytes. bz, err := cdc.MarshalBinaryBare(src) require.Nil(t, err, "%+v", err) - // Make sure this is compatible with current (Bytes()) encoding. - require.Equal(t, src.Bytes(), bz, "Amino binary vs Bytes() mismatch") + if byterSrc, ok := src.(byter); ok { + // Make sure this is compatible with current (Bytes()) encoding. + require.Equal(t, byterSrc.Bytes(), bz, "Amino binary vs Bytes() mismatch") + } // Make sure we have the expected length. if size != -1 { require.Equal(t, size, len(bz), "Amino binary size mismatch") @@ -55,8 +57,6 @@ func ExamplePrintRegisteredTypes() { //| PubKeySecp256k1 | tendermint/PubKeySecp256k1 | 0xEB5AE987 | 0x21 | | //| PrivKeyEd25519 | tendermint/PrivKeyEd25519 | 0xA3288910 | 0x40 | | //| PrivKeySecp256k1 | tendermint/PrivKeySecp256k1 | 0xE1B0F79B | 0x20 | | - //| SignatureEd25519 | tendermint/SignatureEd25519 | 0x2031EA53 | 0x40 | | - //| SignatureSecp256k1 | tendermint/SignatureSecp256k1 | 0x7FC4A495 | variable | | } func TestKeyEncodings(t *testing.T) { @@ -86,13 +86,11 @@ func TestKeyEncodings(t *testing.T) { require.EqualValues(t, tc.privKey, priv3) // Check (de/en)codings of Signatures. - var sig1, sig2, sig3 tcrypto.Signature + var sig1, sig2 []byte sig1, err := tc.privKey.Sign([]byte("something")) require.NoError(t, err) checkAminoBinary(t, sig1, &sig2, -1) // Signature size changes for Secp anyways. require.EqualValues(t, sig1, sig2) - checkAminoJSON(t, sig1, &sig3, false) // TODO also check Prefix bytes. - require.EqualValues(t, sig1, sig3) // Check (de/en)codings of PubKeys. pubKey := tc.privKey.PubKey() @@ -107,7 +105,7 @@ func TestKeyEncodings(t *testing.T) { func TestNilEncodings(t *testing.T) { // Check nil Signature. - var a, b tcrypto.Signature + var a, b []byte checkAminoJSON(t, &a, &b, true) require.EqualValues(t, a, b) diff --git a/crypto/keys/keybase.go b/crypto/keys/keybase.go index aa1c0cdeb9f1..ae036362e381 100644 --- a/crypto/keys/keybase.go +++ b/crypto/keys/keybase.go @@ -199,7 +199,7 @@ func (kb dbKeybase) Get(name string) (Info, error) { // Sign signs the msg with the named key. // It returns an error if the key doesn't exist or the decryption fails. -func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig tmcrypto.Signature, pub tmcrypto.PubKey, err error) { +func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig []byte, pub tmcrypto.PubKey, err error) { info, err := kb.Get(name) if err != nil { return diff --git a/crypto/keys/keybase_test.go b/crypto/keys/keybase_test.go index 114c0e7d597e..652a36bcb02f 100644 --- a/crypto/keys/keybase_test.go +++ b/crypto/keys/keybase_test.go @@ -146,7 +146,7 @@ func TestSignVerify(t *testing.T) { cases := []struct { key crypto.PubKey data []byte - sig crypto.Signature + sig []byte valid bool }{ // proper matches diff --git a/crypto/keys/types.go b/crypto/keys/types.go index 62ff8bd16a7a..a68626489707 100644 --- a/crypto/keys/types.go +++ b/crypto/keys/types.go @@ -16,7 +16,7 @@ type Keybase interface { Delete(name, passphrase string) error // Sign some bytes, looking up the private key to use - Sign(name, passphrase string, msg []byte) (crypto.Signature, crypto.PubKey, error) + Sign(name, passphrase string, msg []byte) ([]byte, crypto.PubKey, error) // CreateMnemonic creates a new mnemonic, and derives a hierarchical deterministic // key from that. diff --git a/crypto/ledger_secp256k1.go b/crypto/ledger_secp256k1.go index f82115a3d14b..8cb175d4f589 100644 --- a/crypto/ledger_secp256k1.go +++ b/crypto/ledger_secp256k1.go @@ -114,7 +114,7 @@ func (pkl PrivKeyLedgerSecp256k1) Equals(other tmcrypto.PrivKey) bool { // Communication is checked on NewPrivKeyLedger and PrivKeyFromBytes, returning // an error, so this should only trigger if the private key is held in memory // for a while before use. -func (pkl PrivKeyLedgerSecp256k1) Sign(msg []byte) (tmcrypto.Signature, error) { +func (pkl PrivKeyLedgerSecp256k1) Sign(msg []byte) ([]byte, error) { sig, err := pkl.signLedgerSecp256k1(msg) if err != nil { return nil, err @@ -135,13 +135,8 @@ func (pkl PrivKeyLedgerSecp256k1) getPubKey() (key tmcrypto.PubKey, err error) { return key, err } -func (pkl PrivKeyLedgerSecp256k1) signLedgerSecp256k1(msg []byte) (tmcrypto.Signature, error) { - sigBytes, err := pkl.ledger.SignSECP256K1(pkl.Path, msg) - if err != nil { - return nil, err - } - - return tmsecp256k1.SignatureSecp256k1FromBytes(sigBytes), nil +func (pkl PrivKeyLedgerSecp256k1) signLedgerSecp256k1(msg []byte) ([]byte, error) { + return pkl.ledger.SignSECP256K1(pkl.Path, msg) } func (pkl PrivKeyLedgerSecp256k1) pubkeyLedgerSecp256k1() (pub tmcrypto.PubKey, err error) { diff --git a/docs/sdk/core/app2.md b/docs/sdk/core/app2.md index 3de7f7b8d590..9a689cca608c 100644 --- a/docs/sdk/core/app2.md +++ b/docs/sdk/core/app2.md @@ -150,7 +150,7 @@ func NewCodec() *wire.Codec { ``` Note: We also register the types in the `tendermint/tendermint/crypto` module so that `crypto.PubKey` -and `crypto.Signature` are encoded/decoded correctly. +is encoded/decoded correctly. Amino supports encoding and decoding in both a binary and JSON format. See the [codec API docs](https://godoc.org/github.com/tendermint/go-amino#Codec) for more details. @@ -166,7 +166,7 @@ type app2Tx struct { sdk.Msg PubKey crypto.PubKey - Signature crypto.Signature + Signature []byte } // This tx only has one Msg. diff --git a/docs/sdk/core/app3.md b/docs/sdk/core/app3.md index 66bb05521b78..203f61e44447 100644 --- a/docs/sdk/core/app3.md +++ b/docs/sdk/core/app3.md @@ -160,7 +160,7 @@ The standard form for signatures is `StdSignature`: // the first transaction made by the account. type StdSignature struct { crypto.PubKey `json:"pub_key"` // optional - crypto.Signature `json:"signature"` + []byte `json:"signature"` AccountNumber int64 `json:"account_number"` Sequence int64 `json:"sequence"` } diff --git a/docs/sdk/core/examples/app2.go b/docs/sdk/core/examples/app2.go index 764a89356820..3c7f71f6d6c6 100644 --- a/docs/sdk/core/examples/app2.go +++ b/docs/sdk/core/examples/app2.go @@ -183,7 +183,7 @@ type app2Tx struct { sdk.Msg PubKey crypto.PubKey - Signature crypto.Signature + Signature []byte } // This tx only has one Msg. @@ -191,7 +191,7 @@ func (tx app2Tx) GetMsgs() []sdk.Msg { return []sdk.Msg{tx.Msg} } -func (tx app2Tx) GetSignature() crypto.Signature { +func (tx app2Tx) GetSignature() []byte { return tx.Signature } diff --git a/examples/README.md b/examples/README.md index e3016fb7079a..8625cead6ce1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -279,7 +279,7 @@ type TxInput struct { Address []byte `json:"address"` // Hash of the PubKey Coins Coins `json:"coins"` // Sequence int `json:"sequence"` // Must be 1 greater than the last committed TxInput - Signature crypto.Signature `json:"signature"` // Depends on the PubKey type and the whole Tx + Signature []byte `json:"signature"` // Depends on the PubKey type and the whole Tx PubKey crypto.PubKey `json:"pub_key"` // Is present iff Sequence == 0 } diff --git a/examples/kvstore/kvstore b/examples/kvstore/kvstore index 43c7dc61c4ed..5dd8b5eeaf78 100755 Binary files a/examples/kvstore/kvstore and b/examples/kvstore/kvstore differ diff --git a/server/start.go b/server/start.go index 64bd9fd457cd..8f369d517fbf 100644 --- a/server/start.go +++ b/server/start.go @@ -101,7 +101,7 @@ func startInProcess(ctx *Context, appCreator AppCreator) (*node.Node, error) { proxy.NewLocalClientCreator(app), node.DefaultGenesisDocProviderFunc(cfg), node.DefaultDBProvider, - node.DefaultMetricsProvider, + node.DefaultMetricsProvider(cfg.Instrumentation), ctx.Logger.With("module", "node"), ) if err != nil { diff --git a/x/auth/stdtx.go b/x/auth/stdtx.go index bebd24623426..c6e280157b2f 100644 --- a/x/auth/stdtx.go +++ b/x/auth/stdtx.go @@ -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 diff --git a/x/slashing/genesis.go b/x/slashing/genesis.go new file mode 100644 index 000000000000..6e2809bfcbcb --- /dev/null +++ b/x/slashing/genesis.go @@ -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 +} diff --git a/x/slashing/handler.go b/x/slashing/handler.go index 19a48b448edf..0cb64ab40904 100644 --- a/x/slashing/handler.go +++ b/x/slashing/handler.go @@ -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() } diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index cd4e69be1c2d..2f163e57a9f1 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -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...) +} diff --git a/x/slashing/keeper_test.go b/x/slashing/keeper_test.go index 5eeca8305df7..d3d6b06ed690 100644 --- a/x/slashing/keeper_test.go +++ b/x/slashing/keeper_test.go @@ -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) diff --git a/x/slashing/params.go b/x/slashing/params.go index 45d2833ce854..9d1bc39609db 100644 --- a/x/slashing/params.go +++ b/x/slashing/params.go @@ -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% diff --git a/x/slashing/signing_info.go b/x/slashing/signing_info.go index 3118793aac14..a21917e796ca 100644 --- a/x/slashing/signing_info.go +++ b/x/slashing/signing_info.go @@ -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) } diff --git a/x/slashing/signing_info_test.go b/x/slashing/signing_info_test.go index b2da974e79a0..f92c43581b5a 100644 --- a/x/slashing/signing_info_test.go +++ b/x/slashing/signing_info_test.go @@ -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)) } diff --git a/x/slashing/tick.go b/x/slashing/tick.go index da157ca7a564..7478b511cf87 100644 --- a/x/slashing/tick.go +++ b/x/slashing/tick.go @@ -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 diff --git a/x/slashing/tick_test.go b/x/slashing/tick_test.go index 38e339e590b1..8e0d66ed63f8 100644 --- a/x/slashing/tick_test.go +++ b/x/slashing/tick_test.go @@ -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) } diff --git a/x/stake/handler.go b/x/stake/handler.go index 9a8cee446bc6..8f7475de9b79 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -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) diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index f183b279a640..edad64f4477a 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -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") diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index 23b58108f85c..484e85ad5c42 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -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) } diff --git a/x/stake/keeper/delegation_test.go b/x/stake/keeper/delegation_test.go index 80e903ea6b76..5d512f0cf5ba 100644 --- a/x/stake/keeper/delegation_test.go +++ b/x/stake/keeper/delegation_test.go @@ -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), } diff --git a/x/stake/keeper/slash.go b/x/stake/keeper/slash.go index fb9297e9cae5..a741b5a85766 100644 --- a/x/stake/keeper/slash.go +++ b/x/stake/keeper/slash.go @@ -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() diff --git a/x/stake/keeper/slash_test.go b/x/stake/keeper/slash_test.go index 9566bd1f56d4..878c44d1eb80 100644 --- a/x/stake/keeper/slash_test.go +++ b/x/stake/keeper/slash_test.go @@ -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), } diff --git a/x/stake/types/delegation.go b/x/stake/types/delegation.go index 2ceedd5f314a..77fd327a5e4d 100644 --- a/x/stake/types/delegation.go +++ b/x/stake/types/delegation.go @@ -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 diff --git a/x/stake/types/delegation_test.go b/x/stake/types/delegation_test.go index 640f5d9791aa..69823db14521 100644 --- a/x/stake/types/delegation_test.go +++ b/x/stake/types/delegation_test.go @@ -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) diff --git a/x/stake/types/errors.go b/x/stake/types/errors.go index 2ef747bae63e..b3e279c8eea0 100644 --- a/x/stake/types/errors.go +++ b/x/stake/types/errors.go @@ -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) diff --git a/x/stake/types/params.go b/x/stake/types/params.go index 5a7dd6ef5565..0ae1ade090a1 100644 --- a/x/stake/types/params.go +++ b/x/stake/types/params.go @@ -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 diff --git a/x/stake/types/pool.go b/x/stake/types/pool.go index 6ddadf94b55f..5aab4294b36c 100644 --- a/x/stake/types/pool.go +++ b/x/stake/types/pool.go @@ -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(), diff --git a/x/stake/types/validator_test.go b/x/stake/types/validator_test.go index 536f44849ccc..f0dff4732c4d 100644 --- a/x/stake/types/validator_test.go +++ b/x/stake/types/validator_test.go @@ -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)