From 7fd2438761ff675f436e0b1c181c1465b2350201 Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Tue, 19 Mar 2019 14:35:52 +0800 Subject: [PATCH 01/18] Add validator remove interface --- x/gov/proposals.go | 9 +++- x/stake/handler.go | 86 +++++++++++++++++++++++++++++------ x/stake/keeper/sdk_types.go | 19 ++++++++ x/stake/keeper/test_common.go | 1 + x/stake/stake.go | 1 + x/stake/types/codec.go | 1 + x/stake/types/msg.go | 6 +++ 7 files changed, 109 insertions(+), 14 deletions(-) diff --git a/x/gov/proposals.go b/x/gov/proposals.go index 43427c03f..cfe836d5e 100644 --- a/x/gov/proposals.go +++ b/x/gov/proposals.go @@ -119,6 +119,7 @@ const ( // ProposalTypeFeeChange belongs to ProposalTypeParameterChange. We use this to make it easily to distinguish。 ProposalTypeFeeChange ProposalKind = 0x05 ProposalTypeCreateValidator ProposalKind = 0x06 + ProposalTypeRemoveValidator ProposalKind = 0x07 ) // String to proposalType byte. Returns ff if invalid. @@ -136,6 +137,8 @@ func ProposalTypeFromString(str string) (ProposalKind, error) { return ProposalTypeFeeChange, nil case "CreateValidator": return ProposalTypeCreateValidator, nil + case "RemoveValidator": + return ProposalTypeRemoveValidator, nil default: return ProposalKind(0xff), errors.Errorf("'%s' is not a valid proposal type", str) } @@ -147,7 +150,9 @@ func validProposalType(pt ProposalKind) bool { pt == ProposalTypeParameterChange || pt == ProposalTypeSoftwareUpgrade || pt == ProposalTypeListTradingPair || - pt == ProposalTypeFeeChange { + pt == ProposalTypeFeeChange || + pt == ProposalTypeCreateValidator || + pt == ProposalTypeRemoveValidator { return true } return false @@ -200,6 +205,8 @@ func (pt ProposalKind) String() string { return "FeeChange" case ProposalTypeCreateValidator: return "CreateValidator" + case ProposalTypeRemoveValidator: + return "RemoveValidator" default: return "" } diff --git a/x/stake/handler.go b/x/stake/handler.go index f0d8b3437..add021dcb 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -20,6 +20,8 @@ func NewHandler(k keeper.Keeper, govKeeper gov.Keeper) sdk.Handler { switch msg := msg.(type) { case types.MsgCreateValidatorProposal: return handleMsgCreateValidatorAfterProposal(ctx, msg, k, govKeeper) + case types.MsgRemoveValidator: + return handleMsgRemoveValidatorAfterProposal(ctx, msg, k, govKeeper) // disabled other msg handling //case types.MsgEditValidator: // return handleMsgEditValidator(ctx, msg, k) @@ -105,7 +107,7 @@ func handleMsgCreateValidatorAfterProposal(ctx sdk.Context, msg MsgCreateValidat height := ctx.BlockHeader().Height // do not checkProposal for the genesis txs if height != 0 { - if err := checkProposal(ctx, k.Codec(), govKeeper, msg); err != nil { + if err := checkCreateProposal(ctx, k.Codec(), govKeeper, msg); err != nil { return ErrInvalidProposal(k.Codespace(), err.Error()).Result() } } @@ -113,6 +115,39 @@ func handleMsgCreateValidatorAfterProposal(ctx sdk.Context, msg MsgCreateValidat return handleMsgCreateValidator(ctx, msg.MsgCreateValidator, k) } +func handleMsgRemoveValidatorAfterProposal(ctx sdk.Context, msg MsgRemoveValidator, k keeper.Keeper, govKeeper gov.Keeper) sdk.Result { + // do not checkProposal for the genesis txs + if ctx.BlockHeight() != 0 { + if err := checkRemoveProposal(ctx, k.Codec(), govKeeper, msg); err != nil { + return ErrInvalidProposal(k.Codespace(), err.Error()).Result() + } + } + + var tags sdk.Tags + var result sdk.Result + k.IterateDelegationsToValidator(ctx, msg.ValidatorAddr, func(del sdk.Delegation) (stop bool) { + msgBeginUnbonding := MsgBeginUnbonding{ + ValidatorAddr: del.GetValidatorAddr(), + DelegatorAddr: del.GetDelegatorAddr(), + SharesAmount: del.GetShares(), + } + result = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, k) + // handleMsgBeginUnbonding return error, abort execution + if !result.IsOK() { + return true + } + tags = tags.AppendTags(result.Tags) + return false + }) + + // If there is a failure in handleMsgBeginUnbonding, return the error message + if !result.IsOK() { + return result + } + + return sdk.Result{Tags: tags} +} + func handleMsgCreateValidator(ctx sdk.Context, msg MsgCreateValidator, k keeper.Keeper) sdk.Result { // check to see if the pubkey or sender has been registered before _, found := k.GetValidator(ctx, msg.ValidatorAddr) @@ -164,28 +199,55 @@ func handleMsgCreateValidator(ctx sdk.Context, msg MsgCreateValidator, k keeper. } } -func checkProposal(ctx sdk.Context, cdc *codec.Codec, govKeeper gov.Keeper, msg MsgCreateValidatorProposal) error { +func checkCreateProposal(ctx sdk.Context, cdc *codec.Codec, govKeeper gov.Keeper, msg MsgCreateValidatorProposal) error { proposal := govKeeper.GetProposal(ctx, msg.ProposalId) if proposal == nil { - return errors.New(fmt.Sprintf("proposal %d does not exist", msg.ProposalId)) + return fmt.Errorf("proposal %d does not exist", msg.ProposalId) } if proposal.GetProposalType() != gov.ProposalTypeCreateValidator { - return errors.New(fmt.Sprintf("proposal type %s is not equal to %s", - proposal.GetProposalType(), gov.ProposalTypeCreateValidator)) + return fmt.Errorf("proposal type %s is not equal to %s", + proposal.GetProposalType().String(), gov.ProposalTypeCreateValidator.String()) } if proposal.GetStatus() != gov.StatusPassed { - return errors.New(fmt.Sprintf("proposal status %d is not not passed", - proposal.GetStatus())) + return fmt.Errorf("proposal status %s is not not passed", + proposal.GetStatus().String()) } var createValidatorParams MsgCreateValidator err := cdc.UnmarshalJSON([]byte(proposal.GetDescription()), &createValidatorParams) if err != nil { - return errors.New(fmt.Sprintf("unmarshal createValidator params failed, err=%s", err.Error())) + return fmt.Errorf("unmarshal createValidator params failed, err=%s", err.Error()) } if !msg.MsgCreateValidator.Equals(createValidatorParams) { - return errors.New("createValidator msg is not identical to the proposal one") + return fmt.Errorf("createValidator msg is not identical to the proposal one") + } + + return nil +} + +func checkRemoveProposal(ctx sdk.Context, cdc *codec.Codec, govKeeper gov.Keeper, msg MsgRemoveValidator) error { + proposal := govKeeper.GetProposal(ctx, msg.ProposalId) + if proposal == nil { + return fmt.Errorf("proposal %d does not exist", msg.ProposalId) + } + if proposal.GetProposalType() != gov.ProposalTypeRemoveValidator { + return fmt.Errorf("proposal type %s is not equal to %s", + proposal.GetProposalType().String(), gov.ProposalTypeRemoveValidator.String()) + } + if proposal.GetStatus() != gov.StatusPassed { + return fmt.Errorf("proposal status %s is not not passed", + proposal.GetStatus().String()) + } + + var removeValidator MsgRemoveValidator + err := cdc.UnmarshalJSON([]byte(proposal.GetDescription()), &removeValidator) + if err != nil { + return fmt.Errorf("unmarshal removeValidator params failed, err=%s", err.Error()) + } + + if !msg.PubKey.Equals(removeValidator.PubKey) || !msg.ValidatorAddr.Equals(removeValidator.ValidatorAddr) { + return fmt.Errorf("removeValidator msg is not identical to the proposal one") } return nil @@ -265,15 +327,13 @@ func handleMsgBeginUnbonding(ctx sdk.Context, msg types.MsgBeginUnbonding, k kee return err.Result() } - finishTime := types.MsgCdc.MustMarshalBinaryLengthPrefixed(ubd.MinTime) - tags := sdk.NewTags( tags.Action, tags.ActionBeginUnbonding, tags.Delegator, []byte(msg.DelegatorAddr.String()), tags.SrcValidator, []byte(msg.ValidatorAddr.String()), - tags.EndTime, finishTime, + tags.EndTime, []byte(ubd.MinTime.String()), ) - return sdk.Result{Data: finishTime, Tags: tags} + return sdk.Result{Tags: tags} } func handleMsgBeginRedelegate(ctx sdk.Context, msg types.MsgBeginRedelegate, k keeper.Keeper) sdk.Result { diff --git a/x/stake/keeper/sdk_types.go b/x/stake/keeper/sdk_types.go index 5569c979b..2b9042b4b 100644 --- a/x/stake/keeper/sdk_types.go +++ b/x/stake/keeper/sdk_types.go @@ -123,3 +123,22 @@ func (k Keeper) IterateDelegations(ctx sdk.Context, delAddr sdk.AccAddress, i++ } } + +// iterate through all of the delegations to a validator +func (k Keeper) IterateDelegationsToValidator(ctx sdk.Context, valAddr sdk.ValAddress, + fn func(del sdk.Delegation) (stop bool)) { + + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, DelegationKey) + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + del := types.MustUnmarshalDelegation(k.cdc, iterator.Key(), iterator.Value()) + if !del.ValidatorAddr.Equals(valAddr) { + continue + } + stop := fn(del) + if stop { + break + } + } +} diff --git a/x/stake/keeper/test_common.go b/x/stake/keeper/test_common.go index b0758c272..5c67f5428 100644 --- a/x/stake/keeper/test_common.go +++ b/x/stake/keeper/test_common.go @@ -60,6 +60,7 @@ func MakeTestCodec() *codec.Codec { cdc.RegisterInterface((*sdk.Msg)(nil), nil) cdc.RegisterConcrete(bank.MsgSend{}, "test/stake/Send", nil) cdc.RegisterConcrete(types.MsgCreateValidator{}, "test/stake/CreateValidator", nil) + cdc.RegisterConcrete(types.MsgRemoveValidator{}, "test/stake/RemoveValidator", nil) cdc.RegisterConcrete(types.MsgCreateValidatorProposal{}, "test/stake/CreateValidatorProposal", nil) cdc.RegisterConcrete(types.MsgEditValidator{}, "test/stake/EditValidator", nil) cdc.RegisterConcrete(types.MsgBeginUnbonding{}, "test/stake/BeginUnbonding", nil) diff --git a/x/stake/stake.go b/x/stake/stake.go index b9a9cfae9..9be1dc5b2 100644 --- a/x/stake/stake.go +++ b/x/stake/stake.go @@ -19,6 +19,7 @@ type ( Params = types.Params Pool = types.Pool MsgCreateValidator = types.MsgCreateValidator + MsgRemoveValidator = types.MsgRemoveValidator MsgCreateValidatorProposal = types.MsgCreateValidatorProposal MsgEditValidator = types.MsgEditValidator MsgDelegate = types.MsgDelegate diff --git a/x/stake/types/codec.go b/x/stake/types/codec.go index 175b92ae1..95df5e9bb 100644 --- a/x/stake/types/codec.go +++ b/x/stake/types/codec.go @@ -7,6 +7,7 @@ import ( // Register concrete types on codec codec func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(MsgCreateValidator{}, "cosmos-sdk/MsgCreateValidator", nil) + cdc.RegisterConcrete(MsgRemoveValidator{}, "cosmos-sdk/MsgRemoveValidator", nil) cdc.RegisterConcrete(MsgCreateValidatorProposal{}, "cosmos-sdk/MsgCreateValidatorProposal", nil) cdc.RegisterConcrete(MsgEditValidator{}, "cosmos-sdk/MsgEditValidator", nil) cdc.RegisterConcrete(MsgDelegate{}, "cosmos-sdk/MsgDelegate", nil) diff --git a/x/stake/types/msg.go b/x/stake/types/msg.go index 0cfd42530..9a19c2f5a 100644 --- a/x/stake/types/msg.go +++ b/x/stake/types/msg.go @@ -30,6 +30,12 @@ type MsgCreateValidatorProposal struct { ProposalId int64 `json:"proposal_id"` } +type MsgRemoveValidator struct { + ValidatorAddr sdk.ValAddress `json:"validator_address"` + PubKey crypto.PubKey `json:"pubkey"` + ProposalId int64 `json:"proposal_id"` +} + // Default way to create validator. Delegator address and validator address are the same func NewMsgCreateValidator(valAddr sdk.ValAddress, pubkey crypto.PubKey, selfDelegation sdk.Coin, description Description, commission CommissionMsg) MsgCreateValidator { From 524a3a5d0881d5e99b55b7a9e3df316b01fccfed Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Tue, 19 Mar 2019 16:23:13 +0800 Subject: [PATCH 02/18] add unit test --- x/stake/handler.go | 35 ++++++++++++------ x/stake/handler_test.go | 82 +++++++++++++++++++++++++++++++++++++++++ x/stake/stake.go | 1 + x/stake/types/errors.go | 4 ++ x/stake/types/msg.go | 57 +++++++++++++++++++++++++--- 5 files changed, 162 insertions(+), 17 deletions(-) diff --git a/x/stake/handler.go b/x/stake/handler.go index add021dcb..42f02b18b 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -2,10 +2,8 @@ package stake import ( "bytes" - "errors" "fmt" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/stake/keeper" @@ -107,7 +105,7 @@ func handleMsgCreateValidatorAfterProposal(ctx sdk.Context, msg MsgCreateValidat height := ctx.BlockHeader().Height // do not checkProposal for the genesis txs if height != 0 { - if err := checkCreateProposal(ctx, k.Codec(), govKeeper, msg); err != nil { + if err := checkCreateProposal(ctx, k, govKeeper, msg); err != nil { return ErrInvalidProposal(k.Codespace(), err.Error()).Result() } } @@ -118,7 +116,7 @@ func handleMsgCreateValidatorAfterProposal(ctx sdk.Context, msg MsgCreateValidat func handleMsgRemoveValidatorAfterProposal(ctx sdk.Context, msg MsgRemoveValidator, k keeper.Keeper, govKeeper gov.Keeper) sdk.Result { // do not checkProposal for the genesis txs if ctx.BlockHeight() != 0 { - if err := checkRemoveProposal(ctx, k.Codec(), govKeeper, msg); err != nil { + if err := checkRemoveProposal(ctx, k, govKeeper, msg); err != nil { return ErrInvalidProposal(k.Codespace(), err.Error()).Result() } } @@ -140,7 +138,7 @@ func handleMsgRemoveValidatorAfterProposal(ctx sdk.Context, msg MsgRemoveValidat return false }) - // If there is a failure in handleMsgBeginUnbonding, return the error message + // If there is a failure in handling MsgBeginUnbonding, return an error if !result.IsOK() { return result } @@ -199,7 +197,7 @@ func handleMsgCreateValidator(ctx sdk.Context, msg MsgCreateValidator, k keeper. } } -func checkCreateProposal(ctx sdk.Context, cdc *codec.Codec, govKeeper gov.Keeper, msg MsgCreateValidatorProposal) error { +func checkCreateProposal(ctx sdk.Context, keeper keeper.Keeper, govKeeper gov.Keeper, msg MsgCreateValidatorProposal) error { proposal := govKeeper.GetProposal(ctx, msg.ProposalId) if proposal == nil { return fmt.Errorf("proposal %d does not exist", msg.ProposalId) @@ -214,7 +212,7 @@ func checkCreateProposal(ctx sdk.Context, cdc *codec.Codec, govKeeper gov.Keeper } var createValidatorParams MsgCreateValidator - err := cdc.UnmarshalJSON([]byte(proposal.GetDescription()), &createValidatorParams) + err := keeper.Codec().UnmarshalJSON([]byte(proposal.GetDescription()), &createValidatorParams) if err != nil { return fmt.Errorf("unmarshal createValidator params failed, err=%s", err.Error()) } @@ -226,7 +224,7 @@ func checkCreateProposal(ctx sdk.Context, cdc *codec.Codec, govKeeper gov.Keeper return nil } -func checkRemoveProposal(ctx sdk.Context, cdc *codec.Codec, govKeeper gov.Keeper, msg MsgRemoveValidator) error { +func checkRemoveProposal(ctx sdk.Context, keeper keeper.Keeper, govKeeper gov.Keeper, msg MsgRemoveValidator) error { proposal := govKeeper.GetProposal(ctx, msg.ProposalId) if proposal == nil { return fmt.Errorf("proposal %d does not exist", msg.ProposalId) @@ -241,15 +239,30 @@ func checkRemoveProposal(ctx sdk.Context, cdc *codec.Codec, govKeeper gov.Keeper } var removeValidator MsgRemoveValidator - err := cdc.UnmarshalJSON([]byte(proposal.GetDescription()), &removeValidator) + err := keeper.Codec().UnmarshalJSON([]byte(proposal.GetDescription()), &removeValidator) if err != nil { return fmt.Errorf("unmarshal removeValidator params failed, err=%s", err.Error()) } - - if !msg.PubKey.Equals(removeValidator.PubKey) || !msg.ValidatorAddr.Equals(removeValidator.ValidatorAddr) { + if !msg.ValidatorAddr.Equals(removeValidator.ValidatorAddr) { return fmt.Errorf("removeValidator msg is not identical to the proposal one") } + _, ok := keeper.GetValidator(ctx, msg.ValidatorAddr) + if !ok { + return fmt.Errorf("trying to remove a non-existing validator") + } + // Remove self validator + if sdk.ValAddress(msg.LauncherAddr).Equals(msg.ValidatorAddr) { + return nil + } + // If the launcher isn't the target validator operator, then the launcher must be the operator of other active validator + launcherValidator, ok := keeper.GetValidator(ctx, sdk.ValAddress(msg.LauncherAddr)) + if !ok { + return fmt.Errorf("the launcher is not a validator operator") + } + if launcherValidator.Status != sdk.Bonded { + return fmt.Errorf("the status of launcher validator is not bonded") + } return nil } diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index c2c9ebd9a..5e93b068a 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -1055,3 +1055,85 @@ func TestCreateValidatorAfterProposal(t *testing.T) { require.False(t, result.IsOK()) } + +func TestRemoveValidatorAfterProposal(t *testing.T) { + ctx, _, keeper, govKeeper, _ := keep.CreateTestInputWithGov(t, false, 1000) + + err := govKeeper.SetInitialProposalID(ctx, 0) + require.Nil(t, err) + valA := sdk.ValAddress(keep.Addrs[0]) + valB := sdk.ValAddress(keep.Addrs[1]) + valC := sdk.ValAddress(keep.Addrs[2]) + valD := sdk.ValAddress(keep.Addrs[3]) + valE := sdk.ValAddress(keep.Addrs[4]) + valF := sdk.ValAddress(keep.Addrs[5]) + + msgCreateValidator := NewTestMsgCreateValidator(valA, keep.PKs[0], 10) + got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + + msgCreateValidator = NewTestMsgCreateValidator(valB, keep.PKs[1], 10) + got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + + msgCreateValidator = NewTestMsgCreateValidator(valC, keep.PKs[2], 10) + got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + + + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 3, len(updates)) + + msgCreateValidator = NewTestMsgCreateValidator(valD, keep.PKs[3], 10) + got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + + msgCreateValidator = NewTestMsgCreateValidator(valF, keep.PKs[5], 10) + got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + + ctx = ctx.WithBlockHeight(1) + proposalDesc := fmt.Sprintf("{\"type\": \"test/stake/RemoveValidator\",\"value\": {\"proposal_id\": \"%d\",\"validator_address\": \"%s\"}}", 0, valA.String()) + + proposal := govKeeper.NewTextProposal(ctx, "RemoveValidatorProposal", proposalDesc, gov.ProposalTypeRemoveValidator) + proposal.SetStatus(gov.StatusPassed) + govKeeper.SetProposal(ctx, proposal) + + // Launcher isn't a bonded validator + msgRemoveValidator := NewMsgRemoveValidator(sdk.AccAddress(valD), valA, 0) + result := handleMsgRemoveValidatorAfterProposal(ctx, msgRemoveValidator, keeper, govKeeper) + require.False(t, result.IsOK()) + + // Launcher isn't a validator + msgRemoveValidator = NewMsgRemoveValidator(sdk.AccAddress(valE), valA, 0) + result = handleMsgRemoveValidatorAfterProposal(ctx, msgRemoveValidator, keeper, govKeeper) + require.False(t, result.IsOK()) + + // Launcher is a bonded validator + msgRemoveValidator = NewMsgRemoveValidator(sdk.AccAddress(valC), valA, 0) + result = handleMsgRemoveValidatorAfterProposal(ctx, msgRemoveValidator, keeper, govKeeper) + require.True(t, result.IsOK()) + + + ctx = ctx.WithBlockHeight(2) + proposalDesc = fmt.Sprintf("{\"type\": \"test/stake/RemoveValidator\",\"value\": {\"proposal_id\": \"%d\",\"validator_address\": \"%s\"}}", 1, valD.String()) + proposal = govKeeper.NewTextProposal(ctx, "RemoveValidatorProposal", proposalDesc, gov.ProposalTypeRemoveValidator) + proposal.SetStatus(gov.StatusPassed) + govKeeper.SetProposal(ctx, proposal) + + // Launcher is the operator of target validator + msgRemoveValidator = NewMsgRemoveValidator(sdk.AccAddress(valD), valD, 1) + result = handleMsgRemoveValidatorAfterProposal(ctx, msgRemoveValidator, keeper, govKeeper) + require.True(t, result.IsOK()) + + ctx = ctx.WithBlockHeight(2) + proposalDesc = fmt.Sprintf("{\"type\": \"test/stake/RemoveValidator\",\"value\": {\"proposal_id\": \"%d\",\"validator_address\": \"%s\"}}", 2, valF.String()) + proposal = govKeeper.NewTextProposal(ctx, "RemoveValidatorProposal", proposalDesc, gov.ProposalTypeRemoveValidator) + proposal.SetStatus(gov.StatusPassed) + govKeeper.SetProposal(ctx, proposal) + + // Try to remove a different validator + msgRemoveValidator = NewMsgRemoveValidator(sdk.AccAddress(valF), valB, 2) + result = handleMsgRemoveValidatorAfterProposal(ctx, msgRemoveValidator, keeper, govKeeper) + require.False(t, result.IsOK()) +} diff --git a/x/stake/stake.go b/x/stake/stake.go index 9be1dc5b2..46eefdc67 100644 --- a/x/stake/stake.go +++ b/x/stake/stake.go @@ -77,6 +77,7 @@ var ( RegisterCodec = types.RegisterCodec NewMsgCreateValidator = types.NewMsgCreateValidator + NewMsgRemoveValidator = types.NewMsgRemoveValidator NewMsgCreateValidatorOnBehalfOf = types.NewMsgCreateValidatorOnBehalfOf NewMsgEditValidator = types.NewMsgEditValidator NewMsgDelegate = types.NewMsgDelegate diff --git a/x/stake/types/errors.go b/x/stake/types/errors.go index 475c26f0e..60e018690 100644 --- a/x/stake/types/errors.go +++ b/x/stake/types/errors.go @@ -90,6 +90,10 @@ func ErrNilDelegatorAddr(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidInput, "delegator address is nil") } +func ErrNilLauncherAddr(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidInput, "launcher address of remove validator is nil") +} + func ErrBadDenom(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidDelegation, "invalid coin denomination") } diff --git a/x/stake/types/msg.go b/x/stake/types/msg.go index 9a19c2f5a..67a0da20a 100644 --- a/x/stake/types/msg.go +++ b/x/stake/types/msg.go @@ -30,12 +30,6 @@ type MsgCreateValidatorProposal struct { ProposalId int64 `json:"proposal_id"` } -type MsgRemoveValidator struct { - ValidatorAddr sdk.ValAddress `json:"validator_address"` - PubKey crypto.PubKey `json:"pubkey"` - ProposalId int64 `json:"proposal_id"` -} - // Default way to create validator. Delegator address and validator address are the same func NewMsgCreateValidator(valAddr sdk.ValAddress, pubkey crypto.PubKey, selfDelegation sdk.Coin, description Description, commission CommissionMsg) MsgCreateValidator { @@ -366,3 +360,54 @@ func (msg MsgBeginUnbonding) ValidateBasic() sdk.Error { func (msg MsgBeginUnbonding) GetInvolvedAddresses() []sdk.AccAddress { return []sdk.AccAddress{msg.DelegatorAddr, sdk.AccAddress(msg.ValidatorAddr)} } + +type MsgRemoveValidator struct { + LauncherAddr sdk.AccAddress `json:"launcher_addr"` + ValidatorAddr sdk.ValAddress `json:"validator_address"` + ProposalId int64 `json:"proposal_id"` +} + +func NewMsgRemoveValidator(launcherAddr sdk.AccAddress, valAddr sdk.ValAddress, proposalId int64) MsgRemoveValidator { + return MsgRemoveValidator{ + LauncherAddr:launcherAddr, + ValidatorAddr: valAddr, + ProposalId: proposalId, + } +} + +//nolint +func (msg MsgRemoveValidator) Route() string { return MsgRoute } +func (msg MsgRemoveValidator) Type() string { return "remove_validator" } +func (msg MsgRemoveValidator) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.LauncherAddr} } + +// get the bytes for the message signer to sign on +func (msg MsgRemoveValidator) GetSignBytes() []byte { + b, err := MsgCdc.MarshalJSON(struct { + LauncherAddr sdk.AccAddress `json:"launcher_addr"` + ValidatorAddr sdk.ValAddress `json:"validator_address"` + ProposalId int64 `json:"proposal_id"` + }{ + LauncherAddr: msg.LauncherAddr, + ValidatorAddr: msg.ValidatorAddr, + ProposalId: msg.ProposalId, + }) + if err != nil { + panic(err) + } + return sdk.MustSortJSON(b) +} + +// quick validity check +func (msg MsgRemoveValidator) ValidateBasic() sdk.Error { + if msg.LauncherAddr == nil { + return ErrNilLauncherAddr(DefaultCodespace) + } + if msg.ValidatorAddr == nil { + return ErrNilValidatorAddr(DefaultCodespace) + } + return nil +} + +func (msg MsgRemoveValidator) GetInvolvedAddresses() []sdk.AccAddress { + return []sdk.AccAddress{msg.LauncherAddr} +} From b827e8b7211864cd1cfe95cb92c3cd61f1be98a7 Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Tue, 19 Mar 2019 16:40:11 +0800 Subject: [PATCH 03/18] Fix failed unit test in gov and stake --- x/gov/msgs_test.go | 2 +- x/stake/handler.go | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/x/gov/msgs_test.go b/x/gov/msgs_test.go index 1e25b7334..8bb3784df 100644 --- a/x/gov/msgs_test.go +++ b/x/gov/msgs_test.go @@ -34,7 +34,7 @@ func TestMsgSubmitProposal(t *testing.T) { {"Test Proposal", "", gov.ProposalTypeText, addrs[0], coinsPos, false}, {"Test Proposal", "the purpose of this proposal is to test", gov.ProposalTypeParameterChange, addrs[0], coinsPos, true}, {"Test Proposal", "the purpose of this proposal is to test", gov.ProposalTypeSoftwareUpgrade, addrs[0], coinsPos, true}, - {"Test Proposal", "the purpose of this proposal is to test", 0x06, addrs[0], coinsPos, false}, + {"Test Proposal", "the purpose of this proposal is to test", 0x08, addrs[0], coinsPos, false}, {"Test Proposal", "the purpose of this proposal is to test", gov.ProposalTypeText, sdk.AccAddress{}, coinsPos, false}, {"Test Proposal", "the purpose of this proposal is to test", gov.ProposalTypeText, addrs[0], coinsZero, true}, {"Test Proposal", "the purpose of this proposal is to test", gov.ProposalTypeText, addrs[0], coinsNeg, false}, diff --git a/x/stake/handler.go b/x/stake/handler.go index 42f02b18b..9d8785086 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -340,13 +340,15 @@ func handleMsgBeginUnbonding(ctx sdk.Context, msg types.MsgBeginUnbonding, k kee return err.Result() } + finishTime := types.MsgCdc.MustMarshalBinaryLengthPrefixed(ubd.MinTime) + tags := sdk.NewTags( tags.Action, tags.ActionBeginUnbonding, tags.Delegator, []byte(msg.DelegatorAddr.String()), tags.SrcValidator, []byte(msg.ValidatorAddr.String()), - tags.EndTime, []byte(ubd.MinTime.String()), + tags.EndTime, finishTime, ) - return sdk.Result{Tags: tags} + return sdk.Result{Data: finishTime, Tags: tags} } func handleMsgBeginRedelegate(ctx sdk.Context, msg types.MsgBeginRedelegate, k keeper.Keeper) sdk.Result { From c5a0e3910a5726ff513385f1f31bf31ade1e4065 Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Tue, 19 Mar 2019 16:48:02 +0800 Subject: [PATCH 04/18] Add proposal id checking in msg validate --- x/stake/types/msg.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x/stake/types/msg.go b/x/stake/types/msg.go index 67a0da20a..8f89272a7 100644 --- a/x/stake/types/msg.go +++ b/x/stake/types/msg.go @@ -2,6 +2,7 @@ package types import ( "bytes" + "fmt" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/crypto" @@ -405,6 +406,9 @@ func (msg MsgRemoveValidator) ValidateBasic() sdk.Error { if msg.ValidatorAddr == nil { return ErrNilValidatorAddr(DefaultCodespace) } + if msg.ProposalId < 0 { + return ErrInvalidProposal(DefaultCodespace, fmt.Sprintf("negative proposal id %d", msg.ProposalId)) + } return nil } From 869137abde7cc22f460bd55585e7d0f697d3859f Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Tue, 19 Mar 2019 17:48:41 +0800 Subject: [PATCH 05/18] Add validator consensus addr into proposal decription --- x/stake/handler.go | 10 +++++----- x/stake/handler_test.go | 23 ++++++++++------------- x/stake/types/errors.go | 4 ++++ x/stake/types/msg.go | 34 +++++++++++++++++++++------------- 4 files changed, 40 insertions(+), 31 deletions(-) diff --git a/x/stake/handler.go b/x/stake/handler.go index 9d8785086..35c369394 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -123,7 +123,7 @@ func handleMsgRemoveValidatorAfterProposal(ctx sdk.Context, msg MsgRemoveValidat var tags sdk.Tags var result sdk.Result - k.IterateDelegationsToValidator(ctx, msg.ValidatorAddr, func(del sdk.Delegation) (stop bool) { + k.IterateDelegationsToValidator(ctx, msg.ValAddr, func(del sdk.Delegation) (stop bool) { msgBeginUnbonding := MsgBeginUnbonding{ ValidatorAddr: del.GetValidatorAddr(), DelegatorAddr: del.GetDelegatorAddr(), @@ -243,16 +243,16 @@ func checkRemoveProposal(ctx sdk.Context, keeper keeper.Keeper, govKeeper gov.Ke if err != nil { return fmt.Errorf("unmarshal removeValidator params failed, err=%s", err.Error()) } - if !msg.ValidatorAddr.Equals(removeValidator.ValidatorAddr) { + if !msg.ValAddr.Equals(removeValidator.ValAddr) || !msg.ValConsAddr.Equals(removeValidator.ValConsAddr) { return fmt.Errorf("removeValidator msg is not identical to the proposal one") } - _, ok := keeper.GetValidator(ctx, msg.ValidatorAddr) + _, ok := keeper.GetValidator(ctx, msg.ValAddr) if !ok { return fmt.Errorf("trying to remove a non-existing validator") } - // Remove self validator - if sdk.ValAddress(msg.LauncherAddr).Equals(msg.ValidatorAddr) { + // Remove it own validator + if sdk.ValAddress(msg.LauncherAddr).Equals(msg.ValAddr) { return nil } // If the launcher isn't the target validator operator, then the launcher must be the operator of other active validator diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index 5e93b068a..af1846c3f 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -5,13 +5,12 @@ import ( "testing" "time" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/gov" keep "github.com/cosmos/cosmos-sdk/x/stake/keeper" "github.com/cosmos/cosmos-sdk/x/stake/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) //______________________________________________________________________ @@ -1080,7 +1079,6 @@ func TestRemoveValidatorAfterProposal(t *testing.T) { got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) require.Equal(t, 3, len(updates)) @@ -1093,47 +1091,46 @@ func TestRemoveValidatorAfterProposal(t *testing.T) { require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") ctx = ctx.WithBlockHeight(1) - proposalDesc := fmt.Sprintf("{\"type\": \"test/stake/RemoveValidator\",\"value\": {\"proposal_id\": \"%d\",\"validator_address\": \"%s\"}}", 0, valA.String()) + proposalDesc := fmt.Sprintf("{\"type\": \"test/stake/RemoveValidator\",\"value\": {\"val_addr\": \"%s\", \"val_cons_addr\": \"%s\"}}", valA.String(), sdk.ConsAddress(keep.PKs[0].Address()).String()) proposal := govKeeper.NewTextProposal(ctx, "RemoveValidatorProposal", proposalDesc, gov.ProposalTypeRemoveValidator) proposal.SetStatus(gov.StatusPassed) govKeeper.SetProposal(ctx, proposal) // Launcher isn't a bonded validator - msgRemoveValidator := NewMsgRemoveValidator(sdk.AccAddress(valD), valA, 0) + msgRemoveValidator := NewMsgRemoveValidator(sdk.AccAddress(valD), valA, sdk.ConsAddress(keep.PKs[0].Address()), 0) result := handleMsgRemoveValidatorAfterProposal(ctx, msgRemoveValidator, keeper, govKeeper) require.False(t, result.IsOK()) // Launcher isn't a validator - msgRemoveValidator = NewMsgRemoveValidator(sdk.AccAddress(valE), valA, 0) + msgRemoveValidator = NewMsgRemoveValidator(sdk.AccAddress(valE), valA, sdk.ConsAddress(keep.PKs[0].Address()), 0) result = handleMsgRemoveValidatorAfterProposal(ctx, msgRemoveValidator, keeper, govKeeper) require.False(t, result.IsOK()) // Launcher is a bonded validator - msgRemoveValidator = NewMsgRemoveValidator(sdk.AccAddress(valC), valA, 0) + msgRemoveValidator = NewMsgRemoveValidator(sdk.AccAddress(valC), valA, sdk.ConsAddress(keep.PKs[0].Address()), 0) result = handleMsgRemoveValidatorAfterProposal(ctx, msgRemoveValidator, keeper, govKeeper) require.True(t, result.IsOK()) - ctx = ctx.WithBlockHeight(2) - proposalDesc = fmt.Sprintf("{\"type\": \"test/stake/RemoveValidator\",\"value\": {\"proposal_id\": \"%d\",\"validator_address\": \"%s\"}}", 1, valD.String()) + proposalDesc = fmt.Sprintf("{\"type\": \"test/stake/RemoveValidator\",\"value\": {\"val_addr\": \"%s\",\"val_cons_addr\": \"%s\"}}", valD.String(), sdk.ConsAddress(keep.PKs[3].Address()).String()) proposal = govKeeper.NewTextProposal(ctx, "RemoveValidatorProposal", proposalDesc, gov.ProposalTypeRemoveValidator) proposal.SetStatus(gov.StatusPassed) govKeeper.SetProposal(ctx, proposal) // Launcher is the operator of target validator - msgRemoveValidator = NewMsgRemoveValidator(sdk.AccAddress(valD), valD, 1) + msgRemoveValidator = NewMsgRemoveValidator(sdk.AccAddress(valD), valD, sdk.ConsAddress(keep.PKs[3].Address()), 1) result = handleMsgRemoveValidatorAfterProposal(ctx, msgRemoveValidator, keeper, govKeeper) require.True(t, result.IsOK()) ctx = ctx.WithBlockHeight(2) - proposalDesc = fmt.Sprintf("{\"type\": \"test/stake/RemoveValidator\",\"value\": {\"proposal_id\": \"%d\",\"validator_address\": \"%s\"}}", 2, valF.String()) + proposalDesc = fmt.Sprintf("{\"type\": \"test/stake/RemoveValidator\",\"value\": {\"validator_address\": \"%s\",\"val_cons_addr\": \"%s\"}}", valF.String(), sdk.ConsAddress(keep.PKs[5].Address()).String()) proposal = govKeeper.NewTextProposal(ctx, "RemoveValidatorProposal", proposalDesc, gov.ProposalTypeRemoveValidator) proposal.SetStatus(gov.StatusPassed) govKeeper.SetProposal(ctx, proposal) // Try to remove a different validator - msgRemoveValidator = NewMsgRemoveValidator(sdk.AccAddress(valF), valB, 2) + msgRemoveValidator = NewMsgRemoveValidator(sdk.AccAddress(valF), valB, sdk.ConsAddress(keep.PKs[1].Address()), 2) result = handleMsgRemoveValidatorAfterProposal(ctx, msgRemoveValidator, keeper, govKeeper) require.False(t, result.IsOK()) } diff --git a/x/stake/types/errors.go b/x/stake/types/errors.go index 60e018690..c744a9748 100644 --- a/x/stake/types/errors.go +++ b/x/stake/types/errors.go @@ -29,6 +29,10 @@ func ErrNilValidatorAddr(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidInput, "validator address is nil") } +func ErrNilValidatorConsAddr(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidInput, "validator consensus address is nil") +} + func ErrBadValidatorAddr(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidAddress, "validator address is invalid") } diff --git a/x/stake/types/msg.go b/x/stake/types/msg.go index 8f89272a7..0f430035f 100644 --- a/x/stake/types/msg.go +++ b/x/stake/types/msg.go @@ -363,16 +363,19 @@ func (msg MsgBeginUnbonding) GetInvolvedAddresses() []sdk.AccAddress { } type MsgRemoveValidator struct { - LauncherAddr sdk.AccAddress `json:"launcher_addr"` - ValidatorAddr sdk.ValAddress `json:"validator_address"` - ProposalId int64 `json:"proposal_id"` + LauncherAddr sdk.AccAddress `json:"launcher_addr"` + ValAddr sdk.ValAddress `json:"val_addr"` + ValConsAddr sdk.ConsAddress `json:"val_cons_addr"` + ProposalId int64 `json:"proposal_id"` } -func NewMsgRemoveValidator(launcherAddr sdk.AccAddress, valAddr sdk.ValAddress, proposalId int64) MsgRemoveValidator { +func NewMsgRemoveValidator(launcherAddr sdk.AccAddress, valAddr sdk.ValAddress, + valConsAddr sdk.ConsAddress, proposalId int64) MsgRemoveValidator { return MsgRemoveValidator{ - LauncherAddr:launcherAddr, - ValidatorAddr: valAddr, - ProposalId: proposalId, + LauncherAddr: launcherAddr, + ValAddr: valAddr, + ValConsAddr: valConsAddr, + ProposalId: proposalId, } } @@ -384,13 +387,15 @@ func (msg MsgRemoveValidator) GetSigners() []sdk.AccAddress { return []sdk.AccAd // get the bytes for the message signer to sign on func (msg MsgRemoveValidator) GetSignBytes() []byte { b, err := MsgCdc.MarshalJSON(struct { - LauncherAddr sdk.AccAddress `json:"launcher_addr"` - ValidatorAddr sdk.ValAddress `json:"validator_address"` - ProposalId int64 `json:"proposal_id"` + LauncherAddr sdk.AccAddress `json:"launcher_addr"` + ValAddr sdk.ValAddress `json:"val_addr"` + ValConsAddr sdk.ConsAddress `json:"val_cons_addr"` + ProposalId int64 `json:"proposal_id"` }{ LauncherAddr: msg.LauncherAddr, - ValidatorAddr: msg.ValidatorAddr, - ProposalId: msg.ProposalId, + ValAddr: msg.ValAddr, + ValConsAddr: msg.ValConsAddr, + ProposalId: msg.ProposalId, }) if err != nil { panic(err) @@ -403,9 +408,12 @@ func (msg MsgRemoveValidator) ValidateBasic() sdk.Error { if msg.LauncherAddr == nil { return ErrNilLauncherAddr(DefaultCodespace) } - if msg.ValidatorAddr == nil { + if msg.ValAddr == nil { return ErrNilValidatorAddr(DefaultCodespace) } + if msg.ValConsAddr == nil { + return ErrNilValidatorConsAddr(DefaultCodespace) + } if msg.ProposalId < 0 { return ErrInvalidProposal(DefaultCodespace, fmt.Sprintf("negative proposal id %d", msg.ProposalId)) } From 5732834fef9f59aaba33675823db8c301f537ddf Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Tue, 19 Mar 2019 19:10:54 +0800 Subject: [PATCH 06/18] Add remove validator command line interface --- server/tm_cmds.go | 2 +- x/stake/client/cli/flags.go | 3 ++- x/stake/client/cli/tx.go | 44 +++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/server/tm_cmds.go b/server/tm_cmds.go index eadb07b20..73c28117f 100644 --- a/server/tm_cmds.go +++ b/server/tm_cmds.go @@ -67,7 +67,7 @@ func ShowAddressCmd(ctx *Context) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { cfg := ctx.Config privValidator := pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()) - valAddr := (sdk.ValAddress)(privValidator.GetAddress()) + valAddr := (sdk.ConsAddress)(privValidator.GetAddress()) if viper.GetBool(client.FlagJson) { return printlnJSON(valAddr) diff --git a/x/stake/client/cli/flags.go b/x/stake/client/cli/flags.go index 66378ec7a..f767c794a 100644 --- a/x/stake/client/cli/flags.go +++ b/x/stake/client/cli/flags.go @@ -30,7 +30,8 @@ const ( FlagNodeID = "node-id" FlagIP = "ip" - FlagProposalID = "proposal-id" + FlagProposalID = "proposal-id" + FlagConsAddrValidator = "cons-validator" FlagOutputDocument = "output-document" // inspired by wget -O ) diff --git a/x/stake/client/cli/tx.go b/x/stake/client/cli/tx.go index 6843c81c6..b15aa9308 100644 --- a/x/stake/client/cli/tx.go +++ b/x/stake/client/cli/tx.go @@ -134,6 +134,50 @@ func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command { return cmd } +// GetCmdEditValidator implements the create edit validator command. +func GetCmdRemoveValidator(cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "remove-validator", + Short: "remove validator", + RunE: func(cmd *cobra.Command, args []string) error { + txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) + cliCtx := context.NewCLIContext(). + WithCodec(cdc). + WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) + + launcher, err := cliCtx.GetFromAddress() + if err != nil { + return err + } + + proposalId := viper.GetInt64(FlagProposalID) + validatorAddr, err := sdk.ValAddressFromBech32(viper.GetString(FlagAddressValidator)) + if err != nil { + return err + } + validatorConsAddr, err := sdk.ConsAddressFromBech32(viper.GetString(FlagConsAddrValidator)) + if err != nil { + return err + } + + msg := stake.NewMsgRemoveValidator(launcher, validatorAddr, validatorConsAddr,proposalId) + + if cliCtx.GenerateOnly { + return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) + } + + // build and sign the transaction, then broadcast to Tendermint + return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) + }, + } + + cmd.Flags().Int64(FlagProposalID, 0, "id of the remove validator proposal") + cmd.Flags().String(FlagAddressValidator, "", "validator address") + cmd.Flags().String(FlagConsAddrValidator, "", "validator consensus address") + + return cmd +} + // GetCmdEditValidator implements the create edit validator command. func GetCmdEditValidator(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ From 98da7c3d389822b9a8beab70924ad31e574640cb Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Tue, 19 Mar 2019 20:20:45 +0800 Subject: [PATCH 07/18] Add new proposal type to NormalizeProposalType --- x/gov/client/utils.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/x/gov/client/utils.go b/x/gov/client/utils.go index 013f3944a..9bed23a3d 100644 --- a/x/gov/client/utils.go +++ b/x/gov/client/utils.go @@ -24,6 +24,14 @@ func NormalizeProposalType(proposalType string) string { return "ParameterChange" case "SoftwareUpgrade", "software_upgrade": return "SoftwareUpgrade" + case "ListTradingPair", "list_trading_pair": + return "ListTradingPair" + case "FeeChange", "fee_change": + return "FeeChange" + case "CreateValidator", "create_validator": + return "CreateValidator" + case "RemoveValidator", "remove_validator": + return "RemoveValidator" } return "" } From 786b9a75d507e4ab56214618973359b6f2adb8d4 Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Wed, 20 Mar 2019 10:22:36 +0800 Subject: [PATCH 08/18] add validator consensus address checking --- x/stake/handler.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/x/stake/handler.go b/x/stake/handler.go index 35c369394..a8352b9d5 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -238,20 +238,26 @@ func checkRemoveProposal(ctx sdk.Context, keeper keeper.Keeper, govKeeper gov.Ke proposal.GetStatus().String()) } - var removeValidator MsgRemoveValidator - err := keeper.Codec().UnmarshalJSON([]byte(proposal.GetDescription()), &removeValidator) + // Check proposal description + var proposalRemoveValidator MsgRemoveValidator + err := keeper.Codec().UnmarshalJSON([]byte(proposal.GetDescription()), &proposalRemoveValidator) if err != nil { return fmt.Errorf("unmarshal removeValidator params failed, err=%s", err.Error()) } - if !msg.ValAddr.Equals(removeValidator.ValAddr) || !msg.ValConsAddr.Equals(removeValidator.ValConsAddr) { + if !msg.ValAddr.Equals(proposalRemoveValidator.ValAddr) || !msg.ValConsAddr.Equals(proposalRemoveValidator.ValConsAddr) { return fmt.Errorf("removeValidator msg is not identical to the proposal one") } - _, ok := keeper.GetValidator(ctx, msg.ValAddr) + // Check validator information + validatorToRemove, ok := keeper.GetValidator(ctx, msg.ValAddr) if !ok { return fmt.Errorf("trying to remove a non-existing validator") } - // Remove it own validator + if !validatorToRemove.ConsAddress().Equals(msg.ValConsAddr) { + return fmt.Errorf("consensus address can't match actual validator consensus address") + } + + // Check launcher authority if sdk.ValAddress(msg.LauncherAddr).Equals(msg.ValAddr) { return nil } From 18e874ba546d517547f01d5c16f7c53375a4a86b Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Wed, 20 Mar 2019 12:19:10 +0800 Subject: [PATCH 09/18] Rename consensus address flag name --- x/stake/client/cli/flags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/stake/client/cli/flags.go b/x/stake/client/cli/flags.go index f767c794a..4e96dbcf2 100644 --- a/x/stake/client/cli/flags.go +++ b/x/stake/client/cli/flags.go @@ -31,7 +31,7 @@ const ( FlagIP = "ip" FlagProposalID = "proposal-id" - FlagConsAddrValidator = "cons-validator" + FlagConsAddrValidator = "cons-addr-validator" FlagOutputDocument = "output-document" // inspired by wget -O ) From 01ac6a52a5c06c3fce43aa8a8b961699178a3ac5 Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Wed, 20 Mar 2019 12:21:32 +0800 Subject: [PATCH 10/18] refactor handler test import --- x/stake/handler_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index af1846c3f..c3ee983d6 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -5,12 +5,13 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/gov" keep "github.com/cosmos/cosmos-sdk/x/stake/keeper" "github.com/cosmos/cosmos-sdk/x/stake/types" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) //______________________________________________________________________ From 764cdc97fc93440365c4c36d1acd8015e4b7fd96 Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Wed, 20 Mar 2019 14:40:45 +0800 Subject: [PATCH 11/18] Use json marshal to replace cdc marshal --- x/stake/handler.go | 13 +++++++++---- x/stake/handler_test.go | 37 +++++++++++++++++++++---------------- x/stake/keeper/keeper.go | 5 ----- x/stake/stake.go | 1 + x/stake/types/msg.go | 34 +++++++++++++++++++++++++++++++++- 5 files changed, 64 insertions(+), 26 deletions(-) diff --git a/x/stake/handler.go b/x/stake/handler.go index a8352b9d5..494b21645 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -2,6 +2,7 @@ package stake import ( "bytes" + "encoding/json" "fmt" sdk "github.com/cosmos/cosmos-sdk/types" @@ -211,13 +212,17 @@ func checkCreateProposal(ctx sdk.Context, keeper keeper.Keeper, govKeeper gov.Ke proposal.GetStatus().String()) } - var createValidatorParams MsgCreateValidator - err := keeper.Codec().UnmarshalJSON([]byte(proposal.GetDescription()), &createValidatorParams) + var createValidatorJson CreateValidatorJsonMsg + err := json.Unmarshal([]byte(proposal.GetDescription()), &createValidatorJson) if err != nil { return fmt.Errorf("unmarshal createValidator params failed, err=%s", err.Error()) } + createValidatorMsgProposal, err := createValidatorJson.ToMsgCreateValidator() + if err != nil { + return fmt.Errorf("invalid pubkey, err=%s", err.Error()) + } - if !msg.MsgCreateValidator.Equals(createValidatorParams) { + if !msg.MsgCreateValidator.Equals(createValidatorMsgProposal) { return fmt.Errorf("createValidator msg is not identical to the proposal one") } @@ -240,7 +245,7 @@ func checkRemoveProposal(ctx sdk.Context, keeper keeper.Keeper, govKeeper gov.Ke // Check proposal description var proposalRemoveValidator MsgRemoveValidator - err := keeper.Codec().UnmarshalJSON([]byte(proposal.GetDescription()), &proposalRemoveValidator) + err := json.Unmarshal([]byte(proposal.GetDescription()), &proposalRemoveValidator) if err != nil { return fmt.Errorf("unmarshal removeValidator params failed, err=%s", err.Error()) } diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index c3ee983d6..55b6a8039 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -1,7 +1,7 @@ package stake import ( - "fmt" + "encoding/json" "testing" "time" @@ -1023,33 +1023,35 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) { func TestCreateValidatorAfterProposal(t *testing.T) { ctx, _, keeper, govKeeper, _ := keep.CreateTestInputWithGov(t, false, 1000) - err := govKeeper.SetInitialProposalID(ctx, 0) + err := govKeeper.SetInitialProposalID(ctx, 1) require.Nil(t, err) valA := sdk.ValAddress(keep.Addrs[0]) valB := sdk.ValAddress(keep.Addrs[1]) ctx = ctx.WithBlockHeight(1) - proposalDescA := fmt.Sprintf("{\"type\": \"test/stake/CreateValidator\",\"value\": {\"Description\": {\"moniker\": \"\",\"identity\": \"\",\"website\": \"\",\"details\": \"\"},\"Commission\": {\"rate\": \"0\",\"max_rate\": \"0\",\"max_change_rate\": \"0\"},\"delegator_address\": \"%s\",\"validator_address\": \"%s\",\"pubkey\": {\"type\": \"tendermint/PubKeyEd25519\",\"value\": \"C0hc/A7sxhlEBEhDb4/J30BWbyNp5yQAKBRUy1Uq8QA=\"},\"delegation\": {\"denom\": \"steak\",\"amount\": \"10000000000\"}}}", keep.Addrs[0].String(), valA.String()) - proposalA := govKeeper.NewTextProposal(ctx, "CreateValidatorProposal", proposalDescA, gov.ProposalTypeCreateValidator) + msgCreateValidator := NewTestMsgCreateValidator(valA, keep.PKs[0], 10) + proposalDesc, _ := json.Marshal(msgCreateValidator) + proposalA := govKeeper.NewTextProposal(ctx, "CreateValidatorProposal", string(proposalDesc), gov.ProposalTypeCreateValidator) proposalA.SetStatus(gov.StatusPassed) govKeeper.SetProposal(ctx, proposalA) msgCreateValidatorA := MsgCreateValidatorProposal{ - MsgCreateValidator: NewTestMsgCreateValidator(valA, keep.PKs[0], 100), - ProposalId: 0, + MsgCreateValidator: NewTestMsgCreateValidator(valA, keep.PKs[0], 10), + ProposalId: 1, } result := handleMsgCreateValidatorAfterProposal(ctx, msgCreateValidatorA, keeper, govKeeper) require.True(t, result.IsOK()) ctx = ctx.WithBlockHeight(2) - proposalDescB := fmt.Sprintf("{\"type\": \"test/stake/CreateValidator\",\"value\": {\"Description\": {\"moniker\": \"\",\"identity\": \"\",\"website\": \"\",\"details\": \"\"},\"Commission\": {\"rate\": \"0\",\"max_rate\": \"0\",\"max_change_rate\": \"0\"},\"delegator_address\": \"%s\",\"validator_address\": \"%s\",\"pubkey\": {\"type\": \"tendermint/PubKeyEd25519\",\"value\": \"C0hc/A7sxhlEBEhDb4/J30BWbyNp5yQAKBRUy1Uq8QE=\"},\"delegation\": {\"denom\": \"steak\",\"amount\": \"10000000000\"}}}", keep.Addrs[1].String(), valB.String()) - proposalB := govKeeper.NewTextProposal(ctx, "CreateValidatorProposal", proposalDescB, gov.ProposalTypeCreateValidator) + msgCreateValidator = NewTestMsgCreateValidator(valB, keep.PKs[1], 10) + proposalDesc, _ = json.Marshal(msgCreateValidator) + proposalB := govKeeper.NewTextProposal(ctx, "CreateValidatorProposal", string(proposalDesc), gov.ProposalTypeCreateValidator) proposalB.SetStatus(gov.StatusPassed) govKeeper.SetProposal(ctx, proposalB) msgCreateValidatorB := MsgCreateValidatorProposal{ - MsgCreateValidator: NewTestMsgCreateValidator(valB, keep.PKs[1], 1000), // I deliberately changed amount value to 1000, amount should be 100 - ProposalId: 1, + MsgCreateValidator: NewTestMsgCreateValidator(valB, keep.PKs[1], 100), // I deliberately changed amount value to 100, amount should be 10 + ProposalId: 2, } result = handleMsgCreateValidatorAfterProposal(ctx, msgCreateValidatorB, keeper, govKeeper) require.False(t, result.IsOK()) @@ -1092,9 +1094,10 @@ func TestRemoveValidatorAfterProposal(t *testing.T) { require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") ctx = ctx.WithBlockHeight(1) - proposalDesc := fmt.Sprintf("{\"type\": \"test/stake/RemoveValidator\",\"value\": {\"val_addr\": \"%s\", \"val_cons_addr\": \"%s\"}}", valA.String(), sdk.ConsAddress(keep.PKs[0].Address()).String()) + removeValidatorMsg := NewMsgRemoveValidator(nil, valA, sdk.ConsAddress(keep.PKs[0].Address()), 0) + proposalDesc, _ := json.Marshal(removeValidatorMsg) - proposal := govKeeper.NewTextProposal(ctx, "RemoveValidatorProposal", proposalDesc, gov.ProposalTypeRemoveValidator) + proposal := govKeeper.NewTextProposal(ctx, "RemoveValidatorProposal", string(proposalDesc), gov.ProposalTypeRemoveValidator) proposal.SetStatus(gov.StatusPassed) govKeeper.SetProposal(ctx, proposal) @@ -1114,8 +1117,9 @@ func TestRemoveValidatorAfterProposal(t *testing.T) { require.True(t, result.IsOK()) ctx = ctx.WithBlockHeight(2) - proposalDesc = fmt.Sprintf("{\"type\": \"test/stake/RemoveValidator\",\"value\": {\"val_addr\": \"%s\",\"val_cons_addr\": \"%s\"}}", valD.String(), sdk.ConsAddress(keep.PKs[3].Address()).String()) - proposal = govKeeper.NewTextProposal(ctx, "RemoveValidatorProposal", proposalDesc, gov.ProposalTypeRemoveValidator) + removeValidatorMsg = NewMsgRemoveValidator(nil, valD, sdk.ConsAddress(keep.PKs[3].Address()), 0) + proposalDesc, _ = json.Marshal(removeValidatorMsg) + proposal = govKeeper.NewTextProposal(ctx, "RemoveValidatorProposal", string(proposalDesc), gov.ProposalTypeRemoveValidator) proposal.SetStatus(gov.StatusPassed) govKeeper.SetProposal(ctx, proposal) @@ -1125,8 +1129,9 @@ func TestRemoveValidatorAfterProposal(t *testing.T) { require.True(t, result.IsOK()) ctx = ctx.WithBlockHeight(2) - proposalDesc = fmt.Sprintf("{\"type\": \"test/stake/RemoveValidator\",\"value\": {\"validator_address\": \"%s\",\"val_cons_addr\": \"%s\"}}", valF.String(), sdk.ConsAddress(keep.PKs[5].Address()).String()) - proposal = govKeeper.NewTextProposal(ctx, "RemoveValidatorProposal", proposalDesc, gov.ProposalTypeRemoveValidator) + removeValidatorMsg = NewMsgRemoveValidator(nil, valF, sdk.ConsAddress(keep.PKs[5].Address()), 0) + proposalDesc, _ = json.Marshal(removeValidatorMsg) + proposal = govKeeper.NewTextProposal(ctx, "RemoveValidatorProposal", string(proposalDesc), gov.ProposalTypeRemoveValidator) proposal.SetStatus(gov.StatusPassed) govKeeper.SetProposal(ctx, proposal) diff --git a/x/stake/keeper/keeper.go b/x/stake/keeper/keeper.go index 09bde360d..3431eaed8 100644 --- a/x/stake/keeper/keeper.go +++ b/x/stake/keeper/keeper.go @@ -51,11 +51,6 @@ func (k Keeper) Codespace() sdk.CodespaceType { return k.codespace } -// return the amino codec -func (k Keeper) Codec() *codec.Codec { - return k.cdc -} - //_______________________________________________________________________ // load the pool diff --git a/x/stake/stake.go b/x/stake/stake.go index 46eefdc67..eb8e777c4 100644 --- a/x/stake/stake.go +++ b/x/stake/stake.go @@ -29,6 +29,7 @@ type ( QueryDelegatorParams = querier.QueryDelegatorParams QueryValidatorParams = querier.QueryValidatorParams QueryBondsParams = querier.QueryBondsParams + CreateValidatorJsonMsg = types.CreateValidatorJsonMsg ) var ( diff --git a/x/stake/types/msg.go b/x/stake/types/msg.go index 0f430035f..07f4e7775 100644 --- a/x/stake/types/msg.go +++ b/x/stake/types/msg.go @@ -3,6 +3,7 @@ package types import ( "bytes" "fmt" + "github.com/tendermint/tendermint/crypto/ed25519" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/crypto" @@ -18,7 +19,7 @@ var _, _, _ sdk.Msg = &MsgCreateValidator{}, &MsgEditValidator{}, &MsgDelegate{} // MsgCreateValidator - struct for bonding transactions type MsgCreateValidator struct { - Description + Description Description Commission CommissionMsg DelegatorAddr sdk.AccAddress `json:"delegator_address"` ValidatorAddr sdk.ValAddress `json:"validator_address"` @@ -26,6 +27,33 @@ type MsgCreateValidator struct { Delegation sdk.Coin `json:"delegation"` } +type CreateValidatorJsonMsg struct { + Description Description + Commission CommissionMsg + DelegatorAddr sdk.AccAddress `json:"delegator_address"` + ValidatorAddr sdk.ValAddress `json:"validator_address"` + PubKey []byte `json:"pubkey"` + Delegation sdk.Coin `json:"delegation"` +} + +func (jsonMsg CreateValidatorJsonMsg) ToMsgCreateValidator() (MsgCreateValidator, error) { + if len(jsonMsg.PubKey) != ed25519.PubKeyEd25519Size { + return MsgCreateValidator{}, fmt.Errorf("pubkey size should be %d", ed25519.PubKeyEd25519Size) + } + + var pubkey ed25519.PubKeyEd25519 + copy(pubkey[:], jsonMsg.PubKey) + + return MsgCreateValidator{ + Description: jsonMsg.Description, + Commission: jsonMsg.Commission, + DelegatorAddr:jsonMsg.DelegatorAddr, + ValidatorAddr: jsonMsg.ValidatorAddr, + PubKey: pubkey, + Delegation: jsonMsg.Delegation, + }, nil +} + type MsgCreateValidatorProposal struct { MsgCreateValidator ProposalId int64 `json:"proposal_id"` @@ -117,6 +145,10 @@ func (msg MsgCreateValidator) Equals(other MsgCreateValidator) bool { return false } + if !msg.PubKey.Equals(other.PubKey) { + return false + } + return msg.Delegation.IsEqual(other.Delegation) && msg.DelegatorAddr.Equals(other.DelegatorAddr) && msg.ValidatorAddr.Equals(other.ValidatorAddr) && From 6184baff36db922b4b31563208f7ebf150c5c764 Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Wed, 20 Mar 2019 14:52:26 +0800 Subject: [PATCH 12/18] Add automatic gov proposal when proposal id is zero --- x/stake/client/cli/tx.go | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/x/stake/client/cli/tx.go b/x/stake/client/cli/tx.go index b15aa9308..3997ebee2 100644 --- a/x/stake/client/cli/tx.go +++ b/x/stake/client/cli/tx.go @@ -159,13 +159,29 @@ func GetCmdRemoveValidator(cdc *codec.Codec) *cobra.Command { if err != nil { return err } - - msg := stake.NewMsgRemoveValidator(launcher, validatorAddr, validatorConsAddr,proposalId) + var msg sdk.Msg + msg = stake.NewMsgRemoveValidator(launcher, validatorAddr, validatorConsAddr,proposalId) + if proposalId == 0 { + amounstStr := viper.GetString(FlagAmount) + if amounstStr == "" { + return fmt.Errorf("Must specify amount to gov deposite when proposalId is zero using --amount") + } + amount, err := sdk.ParseCoin(amounstStr) + if err != nil { + return err + } + title := "remove validator" + description, err := json.Marshal(msg) + if err != nil { + return err + } + msg = gov.NewMsgSubmitProposal(title, string(description), + gov.ProposalTypeRemoveValidator, launcher, sdk.Coins{amount}) + } if cliCtx.GenerateOnly { return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) } - // build and sign the transaction, then broadcast to Tendermint return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) }, @@ -174,6 +190,7 @@ func GetCmdRemoveValidator(cdc *codec.Codec) *cobra.Command { cmd.Flags().Int64(FlagProposalID, 0, "id of the remove validator proposal") cmd.Flags().String(FlagAddressValidator, "", "validator address") cmd.Flags().String(FlagConsAddrValidator, "", "validator consensus address") + cmd.Flags().AddFlagSet(fsAmount) return cmd } From 57c179597a9d933ca8448dab6d7d29670524919b Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Wed, 20 Mar 2019 15:29:34 +0800 Subject: [PATCH 13/18] add deposit flag --- x/stake/client/cli/flags.go | 1 + x/stake/client/cli/tx.go | 36 +++++++++++++++++++++++------------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/x/stake/client/cli/flags.go b/x/stake/client/cli/flags.go index 4e96dbcf2..1f3a4d116 100644 --- a/x/stake/client/cli/flags.go +++ b/x/stake/client/cli/flags.go @@ -32,6 +32,7 @@ const ( FlagProposalID = "proposal-id" FlagConsAddrValidator = "cons-addr-validator" + FlagDeposit = "deposit" FlagOutputDocument = "output-document" // inspired by wget -O ) diff --git a/x/stake/client/cli/tx.go b/x/stake/client/cli/tx.go index 3997ebee2..cd06d6e02 100644 --- a/x/stake/client/cli/tx.go +++ b/x/stake/client/cli/tx.go @@ -28,11 +28,11 @@ func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command { WithCodec(cdc). WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) - amounstStr := viper.GetString(FlagAmount) - if amounstStr == "" { + amountStr := viper.GetString(FlagAmount) + if amountStr == "" { return fmt.Errorf("Must specify amount to stake using --amount") } - amount, err := sdk.ParseCoin(amounstStr) + amount, err := sdk.ParseCoin(amountStr) if err != nil { return err } @@ -102,13 +102,21 @@ func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command { proposalId := viper.GetInt64(FlagProposalID) if proposalId == 0 { - title := "" + depositStr := viper.GetString(FlagDeposit) + if depositStr == "" { + return fmt.Errorf("must specify deposit amount when proposalId is zero using --deposit") + } + deposit, err := sdk.ParseCoin(depositStr) + if err != nil { + return err + } + title := "create validator" description, err := json.Marshal(msg) if err != nil { return err } msg = gov.NewMsgSubmitProposal(title, string(description), - gov.ProposalTypeCreateValidator, valAddr, sdk.Coins{amount}) + gov.ProposalTypeCreateValidator, valAddr, sdk.Coins{deposit}) } else { msg = stake.MsgCreateValidatorProposal{ MsgCreateValidator: msg.(stake.MsgCreateValidator), @@ -123,6 +131,7 @@ func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command { cmd.Flags().Int64(FlagProposalID, 0, "id of the CreateValidator proposal") cmd.Flags().AddFlagSet(fsPk) cmd.Flags().AddFlagSet(fsAmount) + cmd.Flags().String(FlagDeposit, "", "deposit token amount") cmd.Flags().AddFlagSet(fsDescriptionCreate) cmd.Flags().AddFlagSet(fsCommissionCreate) cmd.Flags().AddFlagSet(fsDelegator) @@ -150,7 +159,6 @@ func GetCmdRemoveValidator(cdc *codec.Codec) *cobra.Command { return err } - proposalId := viper.GetInt64(FlagProposalID) validatorAddr, err := sdk.ValAddressFromBech32(viper.GetString(FlagAddressValidator)) if err != nil { return err @@ -159,14 +167,16 @@ func GetCmdRemoveValidator(cdc *codec.Codec) *cobra.Command { if err != nil { return err } + proposalId := viper.GetInt64(FlagProposalID) + var msg sdk.Msg - msg = stake.NewMsgRemoveValidator(launcher, validatorAddr, validatorConsAddr,proposalId) + msg = stake.NewMsgRemoveValidator(launcher, validatorAddr, validatorConsAddr, proposalId) if proposalId == 0 { - amounstStr := viper.GetString(FlagAmount) - if amounstStr == "" { - return fmt.Errorf("Must specify amount to gov deposite when proposalId is zero using --amount") + depositStr := viper.GetString(FlagDeposit) + if depositStr == "" { + return fmt.Errorf("must specify deposit amount when proposalId is zero using --deposit") } - amount, err := sdk.ParseCoin(amounstStr) + deposit, err := sdk.ParseCoin(depositStr) if err != nil { return err } @@ -176,7 +186,7 @@ func GetCmdRemoveValidator(cdc *codec.Codec) *cobra.Command { return err } msg = gov.NewMsgSubmitProposal(title, string(description), - gov.ProposalTypeRemoveValidator, launcher, sdk.Coins{amount}) + gov.ProposalTypeRemoveValidator, launcher, sdk.Coins{deposit}) } if cliCtx.GenerateOnly { @@ -190,7 +200,7 @@ func GetCmdRemoveValidator(cdc *codec.Codec) *cobra.Command { cmd.Flags().Int64(FlagProposalID, 0, "id of the remove validator proposal") cmd.Flags().String(FlagAddressValidator, "", "validator address") cmd.Flags().String(FlagConsAddrValidator, "", "validator consensus address") - cmd.Flags().AddFlagSet(fsAmount) + cmd.Flags().String(FlagDeposit, "", "deposit token amount") return cmd } From 4c60daefac96a2e89d99db42ccdabd623d65889a Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Wed, 20 Mar 2019 20:59:53 +0800 Subject: [PATCH 14/18] Add routerCallRecord --- baseapp/baseapp.go | 9 ++++++++- types/context.go | 10 ++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 72fd27476..cf79e87cb 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -779,6 +779,7 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, txHash string, mode var data []byte // NOTE: we just append them all (?!) var tags sdk.Tags // also just append them all var code sdk.ABCICodeType + routerCallRecordTx := make(map[string]bool) for msgIdx, msg := range msgs { // Match route. msgRoute := msg.Route() @@ -800,11 +801,17 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, txHash string, mode code = msgResult.Code break } + routerCallRecordTx[msgRoute] = true // Construct usable logs in multi-message transactions. logs = append(logs, fmt.Sprintf("Msg %d: %s", msgIdx, msgResult.Log)) } - + // All msgs are handled successfully + if code == sdk.ABCICodeOK { + for key, _ := range routerCallRecordTx { + ctx.RouterCallRecord()[key] = true + } + } result = sdk.Result{ Code: code, Data: data, diff --git a/types/context.go b/types/context.go index 07ca5e381..367021726 100644 --- a/types/context.go +++ b/types/context.go @@ -45,6 +45,7 @@ func NewContext(ms MultiStore, header abci.Header, runTxMode RunTxMode, logger l c = c.WithTxBytes(nil) c = c.WithLogger(logger) c = c.WithVoteInfos(nil) + c = c.WithRouterCallRecord(make(map[string]bool)) return c } @@ -137,6 +138,7 @@ const ( contextKeyLogger contextKeyVoteInfos contextKeyAccountCache + contextKeyRouterCallRecord ) // NOTE: Do not expose MultiStore. @@ -183,6 +185,10 @@ func (c Context) AccountCache() AccountCache { return c.Value(contextKeyAccountCache).(AccountCache) } +func (c Context) RouterCallRecord() map[string]bool { + return c.Value(contextKeyRouterCallRecord).(map[string]bool) +} + func (c Context) WithMultiStore(ms MultiStore) Context { return c.withValue(contextKeyMultiStore, ms) } func (c Context) WithBlockHeader(header abci.Header) Context { @@ -233,6 +239,10 @@ func (c Context) WithAccountCache(cache AccountCache) Context { return c.withValue(contextKeyAccountCache, cache) } +func (c Context) WithRouterCallRecord(record map[string]bool) Context { + return c.withValue(contextKeyRouterCallRecord, record) +} + // Cache the multistore and return a new cached context. The cached context is // written to the context when writeCache is called. func (c Context) CacheContext() (cc Context, writeCache func()) { From cb309c857772c6e4c2841009afcd056a832c1bfe Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Thu, 21 Mar 2019 10:42:05 +0800 Subject: [PATCH 15/18] Resolve comment, a tx only contains one msg --- baseapp/baseapp.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index cf79e87cb..f85653c21 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -779,7 +779,6 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, txHash string, mode var data []byte // NOTE: we just append them all (?!) var tags sdk.Tags // also just append them all var code sdk.ABCICodeType - routerCallRecordTx := make(map[string]bool) for msgIdx, msg := range msgs { // Match route. msgRoute := msg.Route() @@ -801,16 +800,14 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, txHash string, mode code = msgResult.Code break } - routerCallRecordTx[msgRoute] = true // Construct usable logs in multi-message transactions. logs = append(logs, fmt.Sprintf("Msg %d: %s", msgIdx, msgResult.Log)) } - // All msgs are handled successfully + // A tx must only contain one msg. If the msg execution is success, record it if code == sdk.ABCICodeOK { - for key, _ := range routerCallRecordTx { - ctx.RouterCallRecord()[key] = true - } + routerName := msgs[0].Route() + ctx.RouterCallRecord()[routerName] = true } result = sdk.Result{ Code: code, From fd1e621ee178488a860a8da7dee44e864d363eb7 Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Thu, 21 Mar 2019 11:04:29 +0800 Subject: [PATCH 16/18] improve the validate logic for remove validator --- x/stake/types/msg.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/x/stake/types/msg.go b/x/stake/types/msg.go index 07f4e7775..a3858cc2b 100644 --- a/x/stake/types/msg.go +++ b/x/stake/types/msg.go @@ -437,17 +437,17 @@ func (msg MsgRemoveValidator) GetSignBytes() []byte { // quick validity check func (msg MsgRemoveValidator) ValidateBasic() sdk.Error { - if msg.LauncherAddr == nil { + if msg.LauncherAddr.Empty() { return ErrNilLauncherAddr(DefaultCodespace) } - if msg.ValAddr == nil { + if msg.ValAddr.Empty() { return ErrNilValidatorAddr(DefaultCodespace) } - if msg.ValConsAddr == nil { + if msg.ValConsAddr.Empty() { return ErrNilValidatorConsAddr(DefaultCodespace) } - if msg.ProposalId < 0 { - return ErrInvalidProposal(DefaultCodespace, fmt.Sprintf("negative proposal id %d", msg.ProposalId)) + if msg.ProposalId <= 0 { + return ErrInvalidProposal(DefaultCodespace, fmt.Sprintf("Proposal id is expected to be positive, actual value is %d", msg.ProposalId)) } return nil } From 958d078881e8ade28ac3e605bb6af319cc094db1 Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Thu, 21 Mar 2019 11:14:50 +0800 Subject: [PATCH 17/18] refactor go import --- x/stake/types/msg.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/x/stake/types/msg.go b/x/stake/types/msg.go index a3858cc2b..10cbfe67c 100644 --- a/x/stake/types/msg.go +++ b/x/stake/types/msg.go @@ -3,10 +3,10 @@ package types import ( "bytes" "fmt" - "github.com/tendermint/tendermint/crypto/ed25519" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/ed25519" ) // name to identify transaction routes @@ -19,7 +19,7 @@ var _, _, _ sdk.Msg = &MsgCreateValidator{}, &MsgEditValidator{}, &MsgDelegate{} // MsgCreateValidator - struct for bonding transactions type MsgCreateValidator struct { - Description Description + Description Description Commission CommissionMsg DelegatorAddr sdk.AccAddress `json:"delegator_address"` ValidatorAddr sdk.ValAddress `json:"validator_address"` @@ -45,12 +45,12 @@ func (jsonMsg CreateValidatorJsonMsg) ToMsgCreateValidator() (MsgCreateValidator copy(pubkey[:], jsonMsg.PubKey) return MsgCreateValidator{ - Description: jsonMsg.Description, - Commission: jsonMsg.Commission, - DelegatorAddr:jsonMsg.DelegatorAddr, + Description: jsonMsg.Description, + Commission: jsonMsg.Commission, + DelegatorAddr: jsonMsg.DelegatorAddr, ValidatorAddr: jsonMsg.ValidatorAddr, - PubKey: pubkey, - Delegation: jsonMsg.Delegation, + PubKey: pubkey, + Delegation: jsonMsg.Delegation, }, nil } From 65d71483817a322086c566e0a98e697d0645e52f Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Thu, 21 Mar 2019 11:37:46 +0800 Subject: [PATCH 18/18] remove necessary checking --- x/stake/handler.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/x/stake/handler.go b/x/stake/handler.go index 494b21645..b2f018ca9 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -115,11 +115,8 @@ func handleMsgCreateValidatorAfterProposal(ctx sdk.Context, msg MsgCreateValidat } func handleMsgRemoveValidatorAfterProposal(ctx sdk.Context, msg MsgRemoveValidator, k keeper.Keeper, govKeeper gov.Keeper) sdk.Result { - // do not checkProposal for the genesis txs - if ctx.BlockHeight() != 0 { - if err := checkRemoveProposal(ctx, k, govKeeper, msg); err != nil { - return ErrInvalidProposal(k.Codespace(), err.Error()).Result() - } + if err := checkRemoveProposal(ctx, k, govKeeper, msg); err != nil { + return ErrInvalidProposal(k.Codespace(), err.Error()).Result() } var tags sdk.Tags