Skip to content

Commit

Permalink
Merge PR #3803: Validator Creation Updates
Browse files Browse the repository at this point in the history
  • Loading branch information
alexanderbez authored and cwgoes committed Mar 5, 2019
1 parent 57fe79f commit 51c03aa
Show file tree
Hide file tree
Showing 11 changed files with 52 additions and 239 deletions.
5 changes: 5 additions & 0 deletions PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@

### Gaia

* [\#3789] Update validator creation flow:
* Remove `NewMsgCreateValidatorOnBehalfOf` and corresponding business logic
* Ensure the validator address equals the delegator address during
`MsgCreateValidator#ValidateBasic`

### SDK

* [\#3669] Ensure consistency in message naming, codec registration, and JSON
Expand Down
62 changes: 16 additions & 46 deletions docs/gaia/validators/validator-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,19 @@ __Note__: If unspecified, `consensus_pubkey` will default to the output of `gaia

## Participate in genesis as a validator

__Note__: This section only concerns validators that want to be in the genesis file. If the chain you want to validate is already live, skip this section.
__Note__: This section only concerns validators that want to be in the genesis
file. If the chain you want to validate is already live, skip this section.

__Note__: `Gaia-9002` and `Game of stakes` will not use this process. They will be bootsrapped using Tendermint seed validators. You will just need to use the [create-validator](#create-your-validator) command in order to join as a validator for these networks.
__Note__: `Gaia-9002` and `Game of stakes` will not use this process. They will
be bootstrapped using validators operated by Tendermint. You will just need to use the
[create-validator](#create-your-validator) command in order to join as a validator
for these networks.

If you want to participate in genesis as a validator, you need to justify that you (or a delegator) have some stake at genesis, create one (or multiple) transaction to bond this stake to your validator address, and include this transaction in the genesis file.
If you want to participate in genesis as a validator, you need to justify that
you have some stake at genesis, create one (or multiple) transactions to bond this
stake to your validator address, and include this transaction in the genesis file.

We thus need to distinguish two cases:

- Case 1: You want to bond the initial stake from your validator's address.
- Case 2: You want to bond the initial stake from a delegator's address.

### Case 1: The initial stake comes from your validator's address

In this case, you will create a `gentx`:
You will need create a `gentx`:

```bash
gaiad gentx \
Expand All @@ -78,46 +77,17 @@ gaiad gentx \
--name <key_name>
```

__Note__: This command automatically store your `gentx` in `~/.gaiad/config/gentx` for it to be processed at genesis.
__Note__: This command automatically store your `gentx` in `~/.gaiad/config/gentx`
for it to be processed at genesis.

::: tip
Consult `gaiad gentx --help` for more information on the flags defaults.
:::

A `gentx` is a JSON file carrying a self-delegation. All genesis transactions are collected by a `genesis coordinator` and validated against an initial `genesis.json`. Such initial `genesis.json` contains only a list of accounts and their coins. Once the transactions are processed, they are merged in the `genesis.json`'s `gentxs` field.

### Case 2: The initial stake comes from a delegator's address

In this case, you need both the signature of the validator and the delegator. Start by creating an unsigned `create-validator` transaction, and save it in a file called `unsignedValTx`:

```bash
gaiacli tx staking create-validator \
--amount=5STAKE \
--pubkey=$(gaiad tendermint show-validator) \
--moniker="choose a moniker" \
--chain-id=<chain_id> \
--from=<key_name> \
--commission-rate="0.10" \
--commission-max-rate="0.20" \
--commission-max-change-rate="0.01" \
--address-delegator="address of the delegator" \
--generate-only \
> unsignedValTx.json
```

Then, sign this `unsignedValTx` with your validator's private key, and save the output in a new file `signedValTx.json`:

```bash
gaiacli tx sign unsignedValTx.json --from=<validator_key_name> > signedValTx.json
```

Then, pass this file to the delegator, who needs to run the following command:

```bash
gaiacli tx sign signedValTx.json --from=<delegator_key_name> > gentx.json
```

This `gentx.json` needs to be included in the `~/.gaiad/config/gentx` folder on the validator's machine to be processed at genesis, just like in case 1 (except here it needs to be copied manually into the folder).
A `gentx` is a JSON file carrying a self-delegation. All genesis transactions are
collected by a `genesis coordinator` and validated against an initial `genesis.json`.
Such initial `genesis.json` contains only a list of accounts and their coins.
Once the transactions are processed, they are merged in the `genesis.json`'s `gentxs` field.

### Copy the Initial Genesis File and Process Genesis Transactions

Expand Down
41 changes: 0 additions & 41 deletions docs/translations/kr/gaia/validators/validator-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,6 @@ __참고__: 이 문항은 제네시스 파일에 참가하려는 밸리데이터

밸리데이터로써 제네시스에 참가하고 싶으시다면 우선 본인(또는 위임자)가 stake를 보유하고 있다는 것을 증명해야 합니다. 스테이크를 검증인에게 본딩하는 하나 이상의 트랜잭션을 발생하신 후, 해당 트랜잭션을 제네시스 파일에 추가하시기 바랍니다.

우선 두가지의 케이스가 존재합니다:

- 경우 1: 본인 밸리데이터의 stake를 본딩(위임)한다.
- 경우 2: 타인(위임자)의 stake를 본딩한다.

### Case 1: 최초 위임이 밸리데이터 본인 주소에서 발생하는 경우

이런 경우에는 `gentx`를 생성하셔야 합니다:

```bash
Expand All @@ -84,40 +77,6 @@ __참고__: 이 명령어는 제네시스에서의 처리를 위해 `gentx`를 `

`gentx`는 자체위임 정보가 포함된 JSON 파일입니다. 모든 제네시스 트랜잭셕은 `genesis coordinator`에 의하여 모아진 후 최초 `genesis.json`파일과 대치하여 검증합니다. 최초 `genesis.json`에는 계정 리스트와 각 계정이 보유하고 있는 코인 정보가 포함되어있습니다. 트랜잭션이 처리되었다면 해당 정보는 `genesis.json``gentx` 항목에 머지(merge)됩니다.

### Case 2: 최초 위임이 위임자(delegator) 주소에서 발생하는 경우

이런 경우에는 위임자와 검증인의 서명이 둘다 필요합니다. 우선 서명이 되지 않은 `create-validator` 트랜잭션을 생성하신 후 `unsignedValTx`라는 파일에 저장하십시오:

```bash
gaiacli tx staking create-validator \
--amount=5STAKE \
--pubkey=$(gaiad tendermint show-validator) \
--moniker="choose a moniker" \
--chain-id=<chain_id> \
--from=<key_name> \
--commission-rate="0.10" \
--commission-max-rate="0.20" \
--commission-max-change-rate="0.01" \
--address-delegator="address of the delegator" \
--generate-only \
> unsignedValTx.json
```

이제 해당 `unsignedValTx`를 밸리데이터의 프라이빗 키를 이용해 서명합니다. 서명이된 아웃풋을 `signedValTx.json`이라는 파일에 저장합니다:

```bash
gaiacli tx sign unsignedValTx.json --from=<validator_key_name> > signedValTx.json
```

이제 이 파일을 위임자에게 전달하세요. 위임인은 다음 명령어를 실행하면 됩니다:

```bash
gaiacli tx sign signedValTx.json --from=<delegator_key_name> > gentx.json
```

이 파일은 제네시스 절차에서 필요하기 때문에 Case 1과 동일하게 `gentx.json`은 밸리데이터 머신의 `~/.gaiad/config/gentx` 폴더에 포함되어야 합니다 (Case 2 에서는 직접 해당 파을을 이동해야 합니다).


### 제네시스 파일 복사, 제네시스 트랜잭션 처리하기

우선 `genesis.json`파일을 `gaiad`의 config 디렉토리로 가져옵니다.
Expand Down
11 changes: 5 additions & 6 deletions x/staking/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,11 @@ var (
DefaultGenesisState = types.DefaultGenesisState
RegisterCodec = types.RegisterCodec

NewMsgCreateValidator = types.NewMsgCreateValidator
NewMsgCreateValidatorOnBehalfOf = types.NewMsgCreateValidatorOnBehalfOf
NewMsgEditValidator = types.NewMsgEditValidator
NewMsgDelegate = types.NewMsgDelegate
NewMsgUndelegate = types.NewMsgUndelegate
NewMsgBeginRedelegate = types.NewMsgBeginRedelegate
NewMsgCreateValidator = types.NewMsgCreateValidator
NewMsgEditValidator = types.NewMsgEditValidator
NewMsgDelegate = types.NewMsgDelegate
NewMsgUndelegate = types.NewMsgUndelegate
NewMsgBeginRedelegate = types.NewMsgBeginRedelegate

NewQuerier = querier.NewQuerier
NewQueryDelegatorParams = querier.NewQueryDelegatorParams
Expand Down
21 changes: 3 additions & 18 deletions x/staking/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,42 +131,27 @@ func TestStakingMsgs(t *testing.T) {
require.Equal(t, sdk.Bonded, validator.Status)
require.True(sdk.IntEq(t, bondTokens, validator.BondedTokens()))

// addr1 create validator on behalf of addr2
createValidatorMsgOnBehalfOf := NewMsgCreateValidatorOnBehalfOf(
addr1, sdk.ValAddress(addr2), priv2.PubKey(), bondCoin, description, commissionMsg, sdk.OneInt(),
)

mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, []sdk.Msg{createValidatorMsgOnBehalfOf}, []uint64{0, 0}, []uint64{1, 0}, true, true, priv1, priv2)
mock.CheckBalance(t, mApp, addr1, sdk.Coins{genCoin.Sub(bondCoin).Sub(bondCoin)})
mApp.BeginBlock(abci.RequestBeginBlock{})

validator = checkValidator(t, mApp, keeper, sdk.ValAddress(addr2), true)
require.Equal(t, sdk.ValAddress(addr2), validator.OperatorAddress)
require.Equal(t, sdk.Bonded, validator.Status)
require.True(sdk.IntEq(t, bondTokens, validator.Tokens))

// check the bond that should have been created as well
checkDelegation(t, mApp, keeper, addr1, sdk.ValAddress(addr1), true, bondTokens.ToDec())

// edit the validator
description = NewDescription("bar_moniker", "", "", "")
editValidatorMsg := NewMsgEditValidator(sdk.ValAddress(addr1), description, nil, nil)

mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, []sdk.Msg{editValidatorMsg}, []uint64{0}, []uint64{2}, true, true, priv1)
mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, []sdk.Msg{editValidatorMsg}, []uint64{0}, []uint64{1}, true, true, priv1)
validator = checkValidator(t, mApp, keeper, sdk.ValAddress(addr1), true)
require.Equal(t, description, validator.Description)

// delegate
mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin})
delegateMsg := NewMsgDelegate(addr2, sdk.ValAddress(addr1), bondCoin)

mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, []sdk.Msg{delegateMsg}, []uint64{0}, []uint64{1}, true, true, priv2)
mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, []sdk.Msg{delegateMsg}, []uint64{0}, []uint64{0}, true, true, priv2)
mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin.Sub(bondCoin)})
checkDelegation(t, mApp, keeper, addr2, sdk.ValAddress(addr1), true, bondTokens.ToDec())

// begin unbonding
beginUnbondingMsg := NewMsgUndelegate(addr2, sdk.ValAddress(addr1), bondTokens.ToDec())
mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, []sdk.Msg{beginUnbondingMsg}, []uint64{0}, []uint64{2}, true, true, priv2)
mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, []sdk.Msg{beginUnbondingMsg}, []uint64{0}, []uint64{1}, true, true, priv2)

// delegation should exist anymore
checkDelegation(t, mApp, keeper, addr2, sdk.ValAddress(addr1), false, sdk.Dec{})
Expand Down
2 changes: 0 additions & 2 deletions x/staking/client/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (

// nolint
const (
FlagAddressDelegator = "address-delegator"
FlagAddressValidator = "validator"
FlagAddressValidatorSrc = "addr-validator-source"
FlagAddressValidatorDst = "addr-validator-dest"
Expand Down Expand Up @@ -67,7 +66,6 @@ func init() {
fsDescriptionEdit.String(FlagWebsite, types.DoNotModifyDesc, "The validator's (optional) website")
fsDescriptionEdit.String(FlagDetails, types.DoNotModifyDesc, "The validator's (optional) details")
fsValidator.String(FlagAddressValidator, "", "The Bech32 address of the validator")
fsDelegator.String(FlagAddressDelegator, "", "The Bech32 address of the delegator")
fsRedelegation.String(FlagAddressValidatorSrc, "", "The Bech32 address of the source validator")
fsRedelegation.String(FlagAddressValidatorDst, "", "The Bech32 address of the destination validator")
}
23 changes: 5 additions & 18 deletions x/staking/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command {
cmd.Flags().AddFlagSet(fsDescriptionCreate)
cmd.Flags().AddFlagSet(FsCommissionCreate)
cmd.Flags().AddFlagSet(FsMinSelfDelegation)
cmd.Flags().AddFlagSet(fsDelegator)

cmd.Flags().String(FlagIP, "", fmt.Sprintf("The node's public IP. It takes effect only when used in combination with --%s", client.FlagGenerateOnly))
cmd.Flags().String(FlagNodeID, "", "The node's ID")

Expand Down Expand Up @@ -259,23 +259,9 @@ func BuildCreateValidatorMsg(cliCtx context.CLIContext, txBldr authtxb.TxBuilder
return txBldr, nil, fmt.Errorf(staking.ErrMinSelfDelegationInvalid(staking.DefaultCodespace).Error())
}

delAddr := viper.GetString(FlagAddressDelegator)

var msg sdk.Msg
if delAddr != "" {
delAddr, err := sdk.AccAddressFromBech32(delAddr)
if err != nil {
return txBldr, nil, err
}

msg = staking.NewMsgCreateValidatorOnBehalfOf(
delAddr, sdk.ValAddress(valAddr), pk, amount, description, commissionMsg, minSelfDelegation,
)
} else {
msg = staking.NewMsgCreateValidator(
sdk.ValAddress(valAddr), pk, amount, description, commissionMsg, minSelfDelegation,
)
}
msg := staking.NewMsgCreateValidator(
sdk.ValAddress(valAddr), pk, amount, description, commissionMsg, minSelfDelegation,
)

if viper.GetBool(client.FlagGenerateOnly) {
ip := viper.GetString(FlagIP)
Expand All @@ -284,5 +270,6 @@ func BuildCreateValidatorMsg(cliCtx context.CLIContext, txBldr authtxb.TxBuilder
txBldr = txBldr.WithMemo(fmt.Sprintf("%s@%s:26656", nodeID, ip))
}
}

return txBldr, msg, nil
}
46 changes: 8 additions & 38 deletions x/staking/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,38 +178,6 @@ func TestInvalidPubKeyTypeMsgCreateValidator(t *testing.T) {
require.True(t, got.IsOK(), "%v", got)
}

func TestDuplicatesMsgCreateValidatorOnBehalfOf(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)

validatorAddr := sdk.ValAddress(keep.Addrs[0])
delegatorAddr := keep.Addrs[1]
pk := keep.PKs[0]
valTokens := sdk.TokensFromTendermintPower(10)
msgCreateValidatorOnBehalfOf := NewTestMsgCreateValidatorOnBehalfOf(delegatorAddr, validatorAddr, pk, valTokens)
got := handleMsgCreateValidator(ctx, msgCreateValidatorOnBehalfOf, keeper)
require.True(t, got.IsOK(), "%v", got)

// must end-block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))

validator, found := keeper.GetValidator(ctx, validatorAddr)

require.True(t, found)
assert.Equal(t, sdk.Bonded, validator.Status)
assert.Equal(t, validatorAddr, validator.OperatorAddress)
assert.Equal(t, pk, validator.ConsPubKey)
assert.True(sdk.IntEq(t, valTokens, validator.Tokens))
assert.True(sdk.DecEq(t, valTokens.ToDec(), validator.DelegatorShares))
assert.Equal(t, Description{}, validator.Description)

// one validator cannot be created twice even from different delegator
msgCreateValidatorOnBehalfOf.DelegatorAddress = keep.Addrs[2]
msgCreateValidatorOnBehalfOf.PubKey = keep.PKs[1]
got = handleMsgCreateValidator(ctx, msgCreateValidatorOnBehalfOf, keeper)
require.False(t, got.IsOK(), "%v", got)
}

func TestLegacyValidatorDelegations(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, int64(1000))
setInstantUnbondPeriod(keeper, ctx)
Expand Down Expand Up @@ -555,25 +523,27 @@ func TestMultipleMsgCreateValidator(t *testing.T) {
sdk.ValAddress(keep.Addrs[2]),
}
delegatorAddrs := []sdk.AccAddress{
keep.Addrs[3],
keep.Addrs[4],
keep.Addrs[5],
keep.Addrs[0],
keep.Addrs[1],
keep.Addrs[2],
}

// bond them all
for i, validatorAddr := range validatorAddrs {
valTokens := sdk.TokensFromTendermintPower(10)
msgCreateValidatorOnBehalfOf := NewTestMsgCreateValidatorOnBehalfOf(
delegatorAddrs[i], validatorAddr, keep.PKs[i], valTokens)
msgCreateValidatorOnBehalfOf := NewTestMsgCreateValidator(validatorAddr, keep.PKs[i], valTokens)

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
// verify that the account is bonded
validators := keeper.GetValidators(ctx, 100)
require.Equal(t, (i + 1), len(validators))

val := validators[i]
balanceExpd := initTokens.Sub(valTokens)
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, valTokens, val.DelegatorShares.RoundInt(), "expected %d shares, got %d", 10, val.DelegatorShares)
require.Equal(t, balanceExpd, balanceGot, "expected account to have %d, got %d", balanceExpd, balanceGot)
Expand Down
7 changes: 0 additions & 7 deletions x/staking/test_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,3 @@ func NewTestMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amt sdk.
amount := sdk.NewCoin(sdk.DefaultBondDenom, amt)
return NewMsgDelegate(delAddr, valAddr, amount)
}

func NewTestMsgCreateValidatorOnBehalfOf(delAddr sdk.AccAddress, valAddr sdk.ValAddress,
valPubKey crypto.PubKey, amt sdk.Int) MsgCreateValidator {

amount := sdk.NewCoin(sdk.DefaultBondDenom, amt)
return NewMsgCreateValidatorOnBehalfOf(delAddr, valAddr, valPubKey, amount, Description{}, commissionMsg, sdk.OneInt())
}
23 changes: 10 additions & 13 deletions x/staking/types/msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,23 +42,17 @@ type msgCreateValidatorJSON struct {
}

// 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, minSelfDelegation sdk.Int) MsgCreateValidator {
func NewMsgCreateValidator(
valAddr sdk.ValAddress, pubKey crypto.PubKey, selfDelegation sdk.Coin,
description Description, commission CommissionMsg, minSelfDelegation sdk.Int,
) MsgCreateValidator {

return NewMsgCreateValidatorOnBehalfOf(
sdk.AccAddress(valAddr), valAddr, pubkey, selfDelegation, description, commission, minSelfDelegation,
)
}

// Creates validator msg by delegator address on behalf of validator address
func NewMsgCreateValidatorOnBehalfOf(delAddr sdk.AccAddress, valAddr sdk.ValAddress,
pubkey crypto.PubKey, value sdk.Coin, description Description, commission CommissionMsg, minSelfDelegation sdk.Int) MsgCreateValidator {
return MsgCreateValidator{
Description: description,
DelegatorAddress: delAddr,
DelegatorAddress: sdk.AccAddress(valAddr),
ValidatorAddress: valAddr,
PubKey: pubkey,
Value: value,
PubKey: pubKey,
Value: selfDelegation,
Commission: commission,
MinSelfDelegation: minSelfDelegation,
}
Expand Down Expand Up @@ -133,6 +127,9 @@ func (msg MsgCreateValidator) ValidateBasic() sdk.Error {
if msg.ValidatorAddress.Empty() {
return ErrNilValidatorAddr(DefaultCodespace)
}
if !sdk.AccAddress(msg.ValidatorAddress).Equals(msg.DelegatorAddress) {

This comment has been minimized.

Copy link
@jaekwon

jaekwon Mar 6, 2019

Contributor

+1

return ErrBadValidatorAddr(DefaultCodespace)
}
if msg.Value.Amount.LTE(sdk.ZeroInt()) {
return ErrBadDelegationAmount(DefaultCodespace)
}
Expand Down
Loading

0 comments on commit 51c03aa

Please sign in to comment.