diff --git a/CHANGELOG.md b/CHANGELOG.md index 65269f2c46..44fa7290ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -89,6 +89,7 @@ FEATURES * [tests] created a randomized testing framework. - Currently bank has limited functionality in the framework - Auth has its invariants checked within the framework +* [x/stake] Allow validator to be created with starting delegation by a third-party delegator on behalf of validator. * [tests] Add WaitForNextNBlocksTM helper method * [keys] New keys now have 24 word recovery keys, for heightened security diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index b5800ca292..0979a568f6 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -729,7 +729,7 @@ func doDelegate(t *testing.T, port, seed, name, password string, delegatorAddr, { "delegator_addr": "%s", "validator_addr": "%s", - "bond": { "denom": "%s", "amount": "60" } + "delegation": { "denom": "%s", "amount": "60" } } ], "begin_unbondings": [], diff --git a/x/slashing/test_common.go b/x/slashing/test_common.go index b167da24b3..823a6b96b6 100644 --- a/x/slashing/test_common.go +++ b/x/slashing/test_common.go @@ -94,9 +94,10 @@ func testAddr(addr string) sdk.AccAddress { func newTestMsgCreateValidator(address sdk.AccAddress, pubKey crypto.PubKey, amt sdk.Int) stake.MsgCreateValidator { return stake.MsgCreateValidator{ - Description: stake.Description{}, - ValidatorAddr: address, - PubKey: pubKey, - SelfDelegation: sdk.Coin{"steak", amt}, + Description: stake.Description{}, + DelegatorAddr: address, + ValidatorAddr: address, + PubKey: pubKey, + Delegation: sdk.Coin{"steak", amt}, } } diff --git a/x/stake/app_test.go b/x/stake/app_test.go index d1224230da..d73ef9f01f 100644 --- a/x/stake/app_test.go +++ b/x/stake/app_test.go @@ -80,7 +80,7 @@ func checkValidator(t *testing.T, mapp *mock.App, keeper Keeper, addr sdk.AccAddress, expFound bool) Validator { ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{}) - validator, found := keeper.GetValidator(ctxCheck, addr1) + validator, found := keeper.GetValidator(ctxCheck, addr) require.Equal(t, expFound, found) return validator @@ -138,6 +138,18 @@ func TestStakeMsgs(t *testing.T) { require.Equal(t, sdk.Bonded, validator.Status()) require.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Bonded())) + // addr1 create validator on behalf of addr2 + createValidatorMsgOnBehalfOf := NewMsgCreateValidatorOnBehalfOf(addr1, addr2, priv2.PubKey(), bondCoin, description) + + mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{createValidatorMsgOnBehalfOf}, []int64{0, 1}, []int64{1, 0}, true, priv1, priv2) + mock.CheckBalance(t, mApp, addr1, sdk.Coins{genCoin.Minus(bondCoin).Minus(bondCoin)}) + mApp.BeginBlock(abci.RequestBeginBlock{}) + + validator = checkValidator(t, mApp, keeper, addr2, true) + require.Equal(t, addr2, validator.Owner) + require.Equal(t, sdk.Bonded, validator.Status()) + require.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Bonded())) + // check the bond that should have been created as well checkDelegation(t, mApp, keeper, addr1, addr1, true, sdk.NewRat(10)) @@ -145,7 +157,7 @@ func TestStakeMsgs(t *testing.T) { description = NewDescription("bar_moniker", "", "", "") editValidatorMsg := NewMsgEditValidator(addr1, description) - mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{editValidatorMsg}, []int64{0}, []int64{1}, true, priv1) + mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{editValidatorMsg}, []int64{0}, []int64{2}, true, priv1) validator = checkValidator(t, mApp, keeper, addr1, true) require.Equal(t, description, validator.Description) @@ -153,13 +165,13 @@ func TestStakeMsgs(t *testing.T) { mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin}) delegateMsg := NewMsgDelegate(addr2, addr1, bondCoin) - mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{delegateMsg}, []int64{1}, []int64{0}, true, priv2) + mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{delegateMsg}, []int64{1}, []int64{1}, true, priv2) mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin.Minus(bondCoin)}) checkDelegation(t, mApp, keeper, addr2, addr1, true, sdk.NewRat(10)) // begin unbonding beginUnbondingMsg := NewMsgBeginUnbonding(addr2, addr1, sdk.NewRat(10)) - mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{beginUnbondingMsg}, []int64{1}, []int64{1}, true, priv2) + mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{beginUnbondingMsg}, []int64{1}, []int64{2}, true, priv2) // delegation should exist anymore checkDelegation(t, mApp, keeper, addr2, addr1, false, sdk.Rat{}) diff --git a/x/stake/client/cli/tx.go b/x/stake/client/cli/tx.go index e4c4a9dcb1..72317c82c9 100644 --- a/x/stake/client/cli/tx.go +++ b/x/stake/client/cli/tx.go @@ -50,7 +50,17 @@ func GetCmdCreateValidator(cdc *wire.Codec) *cobra.Command { Website: viper.GetString(FlagWebsite), Details: viper.GetString(FlagDetails), } - msg := stake.NewMsgCreateValidator(validatorAddr, pk, amount, description) + + var msg sdk.Msg + if viper.GetString(FlagAddressDelegator) != "" { + delegatorAddr, err := sdk.AccAddressFromBech32(viper.GetString(FlagAddressDelegator)) + if err != nil { + return err + } + msg = stake.NewMsgCreateValidatorOnBehalfOf(delegatorAddr, validatorAddr, pk, amount, description) + } else { + msg = stake.NewMsgCreateValidator(validatorAddr, pk, amount, description) + } // build and sign the transaction, then broadcast to Tendermint err = ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc) @@ -65,6 +75,7 @@ func GetCmdCreateValidator(cdc *wire.Codec) *cobra.Command { cmd.Flags().AddFlagSet(fsAmount) cmd.Flags().AddFlagSet(fsDescription) cmd.Flags().AddFlagSet(fsValidator) + cmd.Flags().AddFlagSet(fsDelegator) return cmd } diff --git a/x/stake/client/rest/tx.go b/x/stake/client/rest/tx.go index b53b43aa57..a0f0416549 100644 --- a/x/stake/client/rest/tx.go +++ b/x/stake/client/rest/tx.go @@ -27,7 +27,7 @@ func registerTxRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec, k type msgDelegationsInput struct { DelegatorAddr string `json:"delegator_addr"` // in bech32 ValidatorAddr string `json:"validator_addr"` // in bech32 - Bond sdk.Coin `json:"bond"` + Delegation sdk.Coin `json:"delegation"` } type msgBeginRedelegateInput struct { DelegatorAddr string `json:"delegator_addr"` // in bech32 @@ -119,7 +119,7 @@ func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx conte messages[i] = stake.MsgDelegate{ DelegatorAddr: delegatorAddr, ValidatorAddr: validatorAddr, - Bond: msg.Bond, + Delegation: msg.Delegation, } i++ } diff --git a/x/stake/handler.go b/x/stake/handler.go index 265bb4e913..c355179cf2 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -71,7 +71,7 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k if found { return ErrValidatorPubKeyExists(k.Codespace()).Result() } - if msg.SelfDelegation.Denom != k.GetParams(ctx).BondDenom { + if msg.Delegation.Denom != k.GetParams(ctx).BondDenom { return ErrBadDenom(k.Codespace()).Result() } @@ -81,7 +81,7 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k // move coins from the msg.Address account to a (self-delegation) delegator account // the validator account and global shares are updated within here - _, err := k.Delegate(ctx, msg.ValidatorAddr, msg.SelfDelegation, validator) + _, err := k.Delegate(ctx, msg.DelegatorAddr, msg.Delegation, validator) if err != nil { return err.Result() } @@ -130,13 +130,13 @@ func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper) if !found { return ErrNoValidatorFound(k.Codespace()).Result() } - if msg.Bond.Denom != k.GetParams(ctx).BondDenom { + if msg.Delegation.Denom != k.GetParams(ctx).BondDenom { return ErrBadDenom(k.Codespace()).Result() } if validator.Revoked == true { return ErrValidatorRevoked(k.Codespace()).Result() } - _, err := k.Delegate(ctx, msg.DelegatorAddr, msg.Bond, validator) + _, err := k.Delegate(ctx, msg.DelegatorAddr, msg.Delegation, validator) if err != nil { return err.Result() } diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index ec0ef35f55..95a0788022 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -16,19 +16,24 @@ import ( //______________________________________________________________________ func newTestMsgCreateValidator(address sdk.AccAddress, pubKey crypto.PubKey, amt int64) MsgCreateValidator { - return MsgCreateValidator{ - Description: Description{}, - ValidatorAddr: address, - PubKey: pubKey, - SelfDelegation: sdk.Coin{"steak", sdk.NewInt(amt)}, - } + return types.NewMsgCreateValidator(address, pubKey, sdk.Coin{"steak", sdk.NewInt(amt)}, Description{}) } func newTestMsgDelegate(delegatorAddr, validatorAddr sdk.AccAddress, amt int64) MsgDelegate { return MsgDelegate{ DelegatorAddr: delegatorAddr, ValidatorAddr: validatorAddr, - Bond: sdk.Coin{"steak", sdk.NewInt(amt)}, + Delegation: sdk.Coin{"steak", sdk.NewInt(amt)}, + } +} + +func newTestMsgCreateValidatorOnBehalfOf(delegatorAddr, validatorAddr sdk.AccAddress, valPubKey crypto.PubKey, amt int64) MsgCreateValidator { + return MsgCreateValidator{ + Description: Description{}, + DelegatorAddr: delegatorAddr, + ValidatorAddr: validatorAddr, + PubKey: valPubKey, + Delegation: sdk.Coin{"steak", sdk.NewInt(amt)}, } } @@ -160,6 +165,32 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) { assert.Equal(t, Description{}, validator.Description) } +func TestDuplicatesMsgCreateValidatorOnBehalfOf(t *testing.T) { + ctx, _, keeper := keep.CreateTestInput(t, false, 1000) + + validatorAddr := keep.Addrs[0] + delegatorAddr := keep.Addrs[1] + pk := keep.PKs[0] + msgCreateValidatorOnBehalfOf := newTestMsgCreateValidatorOnBehalfOf(delegatorAddr, validatorAddr, pk, 10) + got := handleMsgCreateValidator(ctx, msgCreateValidatorOnBehalfOf, keeper) + require.True(t, got.IsOK(), "%v", got) + validator, found := keeper.GetValidator(ctx, validatorAddr) + + 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) + + // one validator cannot be created twice even from different delegator + msgCreateValidatorOnBehalfOf.DelegatorAddr = keep.Addrs[2] + msgCreateValidatorOnBehalfOf.PubKey = keep.PKs[1] + got = handleMsgCreateValidator(ctx, msgCreateValidatorOnBehalfOf, keeper) + require.False(t, got.IsOK(), "%v", got) +} + func TestIncrementsMsgDelegate(t *testing.T) { initBond := int64(1000) ctx, accMapper, keeper := keep.CreateTestInput(t, false, initBond) @@ -329,11 +360,12 @@ func TestMultipleMsgCreateValidator(t *testing.T) { params := setInstantUnbondPeriod(keeper, ctx) validatorAddrs := []sdk.AccAddress{keep.Addrs[0], keep.Addrs[1], keep.Addrs[2]} + delegatorAddrs := []sdk.AccAddress{keep.Addrs[3], keep.Addrs[4], keep.Addrs[5]} // bond them all for i, validatorAddr := range validatorAddrs { - msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[i], 10) - got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + msgCreateValidatorOnBehalfOf := newTestMsgCreateValidatorOnBehalfOf(delegatorAddrs[i], validatorAddr, keep.PKs[i], 10) + got := handleMsgCreateValidator(ctx, msgCreateValidatorOnBehalfOf, keeper) require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) //Check that the account is bonded @@ -341,18 +373,18 @@ func TestMultipleMsgCreateValidator(t *testing.T) { require.Equal(t, (i + 1), len(validators)) val := validators[i] balanceExpd := sdk.NewInt(initBond - 10) - balanceGot := accMapper.GetAccount(ctx, val.Owner).GetCoins().AmountOf(params.BondDenom) + balanceGot := accMapper.GetAccount(ctx, delegatorAddrs[i]).GetCoins().AmountOf(params.BondDenom) require.Equal(t, i+1, len(validators), "expected %d validators got %d, validators: %v", i+1, len(validators), validators) require.Equal(t, 10, int(val.DelegatorShares.RoundInt64()), "expected %d shares, got %d", 10, val.DelegatorShares) require.Equal(t, balanceExpd, balanceGot, "expected account to have %d, got %d", balanceExpd, balanceGot) } - // unbond them all + // unbond them all by revoking delegation for i, validatorAddr := range validatorAddrs { - validatorPre, found := keeper.GetValidator(ctx, validatorAddr) + _, found := keeper.GetValidator(ctx, validatorAddr) require.True(t, found) - msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewRat(10)) // self-delegation - msgCompleteUnbonding := NewMsgCompleteUnbonding(validatorAddr, validatorAddr) + msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddrs[i], validatorAddr, sdk.NewRat(10)) // remove delegation + msgCompleteUnbonding := NewMsgCompleteUnbonding(delegatorAddrs[i], validatorAddr) got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper) @@ -367,7 +399,7 @@ func TestMultipleMsgCreateValidator(t *testing.T) { require.False(t, found) expBalance := sdk.NewInt(initBond) - gotBalance := accMapper.GetAccount(ctx, validatorPre.Owner).GetCoins().AmountOf(params.BondDenom) + gotBalance := accMapper.GetAccount(ctx, delegatorAddrs[i]).GetCoins().AmountOf(params.BondDenom) require.Equal(t, expBalance, gotBalance, "expected account to have %d, got %d", expBalance, gotBalance) } } diff --git a/x/stake/stake.go b/x/stake/stake.go index c582fb6357..410856489b 100644 --- a/x/stake/stake.go +++ b/x/stake/stake.go @@ -71,13 +71,14 @@ var ( DefaultGenesisState = types.DefaultGenesisState RegisterWire = types.RegisterWire - NewMsgCreateValidator = types.NewMsgCreateValidator - NewMsgEditValidator = types.NewMsgEditValidator - NewMsgDelegate = types.NewMsgDelegate - NewMsgBeginUnbonding = types.NewMsgBeginUnbonding - NewMsgCompleteUnbonding = types.NewMsgCompleteUnbonding - NewMsgBeginRedelegate = types.NewMsgBeginRedelegate - NewMsgCompleteRedelegate = types.NewMsgCompleteRedelegate + NewMsgCreateValidator = types.NewMsgCreateValidator + NewMsgCreateValidatorOnBehalfOf = types.NewMsgCreateValidatorOnBehalfOf + NewMsgEditValidator = types.NewMsgEditValidator + NewMsgDelegate = types.NewMsgDelegate + NewMsgBeginUnbonding = types.NewMsgBeginUnbonding + NewMsgCompleteUnbonding = types.NewMsgCompleteUnbonding + NewMsgBeginRedelegate = types.NewMsgBeginRedelegate + NewMsgCompleteRedelegate = types.NewMsgCompleteRedelegate ) const ( diff --git a/x/stake/types/msg.go b/x/stake/types/msg.go index 5c00002fcb..08fd80128c 100644 --- a/x/stake/types/msg.go +++ b/x/stake/types/msg.go @@ -2,6 +2,7 @@ package types import ( "math" + "reflect" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/crypto" @@ -27,38 +28,63 @@ var maximumBondingRationalDenominator sdk.Int = sdk.NewInt(int64(math.Pow10(MaxB // MsgCreateValidator - struct for unbonding transactions type MsgCreateValidator struct { Description - ValidatorAddr sdk.AccAddress `json:"address"` - PubKey crypto.PubKey `json:"pubkey"` - SelfDelegation sdk.Coin `json:"self_delegation"` + DelegatorAddr sdk.AccAddress `json:"delegator_address"` + ValidatorAddr sdk.AccAddress `json:"validator_address"` + PubKey crypto.PubKey `json:"pubkey"` + Delegation sdk.Coin `json:"delegation"` } +// Default way to create validator. Delegator address and validator address are the same func NewMsgCreateValidator(validatorAddr sdk.AccAddress, pubkey crypto.PubKey, selfDelegation sdk.Coin, description Description) MsgCreateValidator { return MsgCreateValidator{ - Description: description, - ValidatorAddr: validatorAddr, - PubKey: pubkey, - SelfDelegation: selfDelegation, + Description: description, + DelegatorAddr: validatorAddr, + ValidatorAddr: validatorAddr, + PubKey: pubkey, + Delegation: selfDelegation, + } +} + +// Creates validator msg by delegator address on behalf of validator address +func NewMsgCreateValidatorOnBehalfOf(delegatorAddr, validatorAddr sdk.AccAddress, pubkey crypto.PubKey, + delegation sdk.Coin, description Description) MsgCreateValidator { + return MsgCreateValidator{ + Description: description, + DelegatorAddr: delegatorAddr, + ValidatorAddr: validatorAddr, + PubKey: pubkey, + Delegation: delegation, } } //nolint func (msg MsgCreateValidator) Type() string { return MsgType } + +// Return address(es) that must sign over msg.GetSignBytes() func (msg MsgCreateValidator) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.ValidatorAddr} + // delegator is first signer so delegator pays fees + addrs := []sdk.AccAddress{msg.DelegatorAddr} + if !reflect.DeepEqual(msg.DelegatorAddr, msg.ValidatorAddr) { + // if validator addr is not same as delegator addr, validator must sign msg as well + addrs = append(addrs, msg.ValidatorAddr) + } + return addrs } // get the bytes for the message signer to sign on func (msg MsgCreateValidator) GetSignBytes() []byte { b, err := MsgCdc.MarshalJSON(struct { Description - ValidatorAddr sdk.AccAddress `json:"address"` + DelegatorAddr sdk.AccAddress `json:"delegator_address"` + ValidatorAddr sdk.AccAddress `json:"validator_address"` PubKey string `json:"pubkey"` - Bond sdk.Coin `json:"bond"` + Delegation sdk.Coin `json:"delegation"` }{ Description: msg.Description, ValidatorAddr: msg.ValidatorAddr, PubKey: sdk.MustBech32ifyValPub(msg.PubKey), + Delegation: msg.Delegation, }) if err != nil { panic(err) @@ -68,10 +94,13 @@ func (msg MsgCreateValidator) GetSignBytes() []byte { // quick validity check func (msg MsgCreateValidator) ValidateBasic() sdk.Error { + if msg.DelegatorAddr == nil { + return ErrNilDelegatorAddr(DefaultCodespace) + } if msg.ValidatorAddr == nil { return ErrNilValidatorAddr(DefaultCodespace) } - if !(msg.SelfDelegation.Amount.GT(sdk.ZeroInt())) { + if !(msg.Delegation.Amount.GT(sdk.ZeroInt())) { return ErrBadDelegationAmount(DefaultCodespace) } empty := Description{} @@ -135,14 +164,14 @@ func (msg MsgEditValidator) ValidateBasic() sdk.Error { type MsgDelegate struct { DelegatorAddr sdk.AccAddress `json:"delegator_addr"` ValidatorAddr sdk.AccAddress `json:"validator_addr"` - Bond sdk.Coin `json:"bond"` + Delegation sdk.Coin `json:"delegation"` } -func NewMsgDelegate(delegatorAddr, validatorAddr sdk.AccAddress, bond sdk.Coin) MsgDelegate { +func NewMsgDelegate(delegatorAddr, validatorAddr sdk.AccAddress, delegation sdk.Coin) MsgDelegate { return MsgDelegate{ DelegatorAddr: delegatorAddr, ValidatorAddr: validatorAddr, - Bond: bond, + Delegation: delegation, } } @@ -169,7 +198,7 @@ func (msg MsgDelegate) ValidateBasic() sdk.Error { if msg.ValidatorAddr == nil { return ErrNilValidatorAddr(DefaultCodespace) } - if !(msg.Bond.Amount.GT(sdk.ZeroInt())) { + if !(msg.Delegation.Amount.GT(sdk.ZeroInt())) { return ErrBadDelegationAmount(DefaultCodespace) } return nil diff --git a/x/stake/types/msg_test.go b/x/stake/types/msg_test.go index d4a971bab9..73eef084a2 100644 --- a/x/stake/types/msg_test.go +++ b/x/stake/types/msg_test.go @@ -69,6 +69,46 @@ func TestMsgEditValidator(t *testing.T) { } } +// test ValidateBasic and GetSigners for MsgCreateValidatorOnBehalfOf +func TestMsgCreateValidatorOnBehalfOf(t *testing.T) { + tests := []struct { + name, moniker, identity, website, details string + delegatorAddr sdk.AccAddress + validatorAddr sdk.AccAddress + validatorPubKey crypto.PubKey + bond sdk.Coin + expectPass bool + }{ + {"basic good", "a", "b", "c", "d", addr1, addr2, pk2, coinPos, true}, + {"partial description", "", "", "c", "", addr1, addr2, pk2, coinPos, true}, + {"empty description", "", "", "", "", addr1, addr2, pk2, coinPos, false}, + {"empty delegator address", "a", "b", "c", "d", emptyAddr, addr2, pk2, coinPos, false}, + {"empty validator address", "a", "b", "c", "d", addr1, emptyAddr, pk2, coinPos, false}, + {"empty pubkey", "a", "b", "c", "d", addr1, addr2, emptyPubkey, coinPos, true}, + {"empty bond", "a", "b", "c", "d", addr1, addr2, pk2, coinZero, false}, + {"negative bond", "a", "b", "c", "d", addr1, addr2, pk2, coinNeg, false}, + {"negative bond", "a", "b", "c", "d", addr1, addr2, pk2, coinNeg, false}, + } + + for _, tc := range tests { + description := NewDescription(tc.moniker, tc.identity, tc.website, tc.details) + msg := NewMsgCreateValidatorOnBehalfOf(tc.delegatorAddr, tc.validatorAddr, tc.validatorPubKey, tc.bond, description) + if tc.expectPass { + require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name) + } else { + require.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name) + } + } + + msg := NewMsgCreateValidator(addr1, pk1, coinPos, Description{}) + addrs := msg.GetSigners() + require.Equal(t, []sdk.AccAddress{addr1}, addrs, "Signers on default msg is wrong") + + msg = NewMsgCreateValidatorOnBehalfOf(addr2, addr1, pk1, coinPos, Description{}) + addrs = msg.GetSigners() + require.Equal(t, []sdk.AccAddress{addr2, addr1}, addrs, "Signers for onbehalfof msg is wrong") +} + // test ValidateBasic for MsgDelegate func TestMsgDelegate(t *testing.T) { tests := []struct {