diff --git a/CHANGELOG.md b/CHANGELOG.md index a35f2a8d34..b090b6d4ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -126,6 +126,7 @@ BUG FIXES * [x/stake] fix revoke bytes ordering (was putting revoked candidates at the top of the list) * [x/stake] bond count was counting revoked validators as bonded, fixed * \#1565 - fix cliff validator persisting when validator set shrinks from max +* \#1010 - two validators can't bond with the same pubkey anymore ## 0.19.0 diff --git a/x/stake/handler.go b/x/stake/handler.go index 15d52ea9c9..265bb4e913 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -65,7 +65,11 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k // check to see if the pubkey or sender has been registered before _, found := k.GetValidator(ctx, msg.ValidatorAddr) if found { - return ErrValidatorAlreadyExists(k.Codespace()).Result() + return ErrValidatorOwnerExists(k.Codespace()).Result() + } + _, found = k.GetValidatorByPubKey(ctx, msg.PubKey) + if found { + return ErrValidatorPubKeyExists(k.Codespace()).Result() } if msg.SelfDelegation.Denom != k.GetParams(ctx).BondDenom { return ErrBadDenom(k.Codespace()).Result() diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index fffd248b3b..ec0ef35f55 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -3,6 +3,7 @@ package stake import ( "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto" @@ -118,25 +119,45 @@ func TestValidatorByPowerIndex(t *testing.T) { func TestDuplicatesMsgCreateValidator(t *testing.T) { ctx, _, keeper := keep.CreateTestInput(t, false, 1000) - validatorAddr := keep.Addrs[0] - pk := keep.PKs[0] - msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pk, 10) - got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + addr1, addr2 := keep.Addrs[0], keep.Addrs[1] + pk1, pk2 := keep.PKs[0], keep.PKs[1] + + msgCreateValidator1 := newTestMsgCreateValidator(addr1, pk1, 10) + got := handleMsgCreateValidator(ctx, msgCreateValidator1, keeper) require.True(t, got.IsOK(), "%v", got) - validator, found := keeper.GetValidator(ctx, validatorAddr) + validator, found := keeper.GetValidator(ctx, addr1) require.True(t, found) - require.Equal(t, sdk.Bonded, validator.Status()) - require.Equal(t, validatorAddr, validator.Owner) - require.Equal(t, pk, validator.PubKey) - require.Equal(t, sdk.NewRat(10), validator.PoolShares.Bonded()) - require.Equal(t, sdk.NewRat(10), validator.DelegatorShares) - require.Equal(t, Description{}, validator.Description) + assert.Equal(t, sdk.Bonded, validator.Status()) + assert.Equal(t, addr1, validator.Owner) + assert.Equal(t, pk1, validator.PubKey) + assert.Equal(t, sdk.NewRat(10), validator.PoolShares.Bonded()) + assert.Equal(t, sdk.NewRat(10), validator.DelegatorShares) + assert.Equal(t, Description{}, validator.Description) - // one validator cannot bond twice - msgCreateValidator.PubKey = keep.PKs[1] - got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + // two validators can't have the same owner address + msgCreateValidator2 := newTestMsgCreateValidator(addr1, pk2, 10) + got = handleMsgCreateValidator(ctx, msgCreateValidator2, keeper) require.False(t, got.IsOK(), "%v", got) + + // two validators can't have the same pubkey + msgCreateValidator3 := newTestMsgCreateValidator(addr2, pk1, 10) + got = handleMsgCreateValidator(ctx, msgCreateValidator3, keeper) + require.False(t, got.IsOK(), "%v", got) + + // must have different pubkey and owner + msgCreateValidator4 := newTestMsgCreateValidator(addr2, pk2, 10) + got = handleMsgCreateValidator(ctx, msgCreateValidator4, keeper) + require.True(t, got.IsOK(), "%v", got) + validator, found = keeper.GetValidator(ctx, addr2) + + require.True(t, found) + assert.Equal(t, sdk.Bonded, validator.Status()) + assert.Equal(t, addr2, validator.Owner) + assert.Equal(t, pk2, validator.PubKey) + assert.Equal(t, sdk.NewRat(10), validator.PoolShares.Bonded()) + assert.Equal(t, sdk.NewRat(10), validator.DelegatorShares) + assert.Equal(t, Description{}, validator.Description) } func TestIncrementsMsgDelegate(t *testing.T) { diff --git a/x/stake/stake.go b/x/stake/stake.go index 869b838ffa..c582fb6357 100644 --- a/x/stake/stake.go +++ b/x/stake/stake.go @@ -92,14 +92,15 @@ const ( ) var ( - ErrNilValidatorAddr = types.ErrNilValidatorAddr - ErrNoValidatorFound = types.ErrNoValidatorFound - ErrValidatorAlreadyExists = types.ErrValidatorAlreadyExists - ErrValidatorRevoked = types.ErrValidatorRevoked - ErrBadRemoveValidator = types.ErrBadRemoveValidator - ErrDescriptionLength = types.ErrDescriptionLength - ErrCommissionNegative = types.ErrCommissionNegative - ErrCommissionHuge = types.ErrCommissionHuge + ErrNilValidatorAddr = types.ErrNilValidatorAddr + ErrNoValidatorFound = types.ErrNoValidatorFound + ErrValidatorOwnerExists = types.ErrValidatorOwnerExists + ErrValidatorPubKeyExists = types.ErrValidatorPubKeyExists + ErrValidatorRevoked = types.ErrValidatorRevoked + ErrBadRemoveValidator = types.ErrBadRemoveValidator + ErrDescriptionLength = types.ErrDescriptionLength + ErrCommissionNegative = types.ErrCommissionNegative + ErrCommissionHuge = types.ErrCommissionHuge ErrNilDelegatorAddr = types.ErrNilDelegatorAddr ErrBadDenom = types.ErrBadDenom diff --git a/x/stake/types/errors.go b/x/stake/types/errors.go index 09ed2d3696..237340b89d 100644 --- a/x/stake/types/errors.go +++ b/x/stake/types/errors.go @@ -30,8 +30,12 @@ func ErrNoValidatorFound(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidValidator, "validator does not exist for that address") } -func ErrValidatorAlreadyExists(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeInvalidValidator, "validator already exist, cannot re-create validator") +func ErrValidatorOwnerExists(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "validator already exist for this owner-address, must use new validator-owner address") +} + +func ErrValidatorPubKeyExists(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "validator already exist for this pubkey, must use new validator pubkey") } func ErrValidatorRevoked(codespace sdk.CodespaceType) sdk.Error {