Skip to content

Commit

Permalink
Merge PR cosmos#3425: Add Vesting Account Genesis Validation
Browse files Browse the repository at this point in the history
  • Loading branch information
alexanderbez authored and cwgoes committed Jan 29, 2019
1 parent 3e6ce3d commit 1a656e7
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 19 deletions.
2 changes: 2 additions & 0 deletions PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ IMPROVEMENTS
* Gaia CLI (`gaiacli`)

* Gaia
* [\#3418](https://github.com/cosmos/cosmos-sdk/issues/3418) Add vesting account
genesis validation checks to `GaiaValidateGenesisState`.

* SDK

Expand Down
40 changes: 31 additions & 9 deletions cmd/gaia/app/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"path/filepath"
"sort"
"strings"
"time"

"github.com/cosmos/cosmos-sdk/x/bank"

Expand Down Expand Up @@ -85,8 +86,8 @@ type GenesisAccount struct {
OriginalVesting sdk.Coins `json:"original_vesting"` // total vesting coins upon initialization
DelegatedFree sdk.Coins `json:"delegated_free"` // delegated vested coins at time of delegation
DelegatedVesting sdk.Coins `json:"delegated_vesting"` // delegated vesting coins at time of delegation
StartTime int64 `json:"start_time"` // vesting start time
EndTime int64 `json:"end_time"` // vesting end time
StartTime int64 `json:"start_time"` // vesting start time (UNIX Epoch time)
EndTime int64 `json:"end_time"` // vesting end time (UNIX Epoch time)
}

func NewGenesisAccount(acc *auth.BaseAccount) GenesisAccount {
Expand Down Expand Up @@ -252,17 +253,38 @@ func GaiaValidateGenesisState(genesisState GenesisState) error {
return slashing.ValidateGenesis(genesisState.SlashingData)
}

// Ensures that there are no duplicate accounts in the genesis state,
// validateGenesisStateAccounts performs validation of genesis accounts. It
// ensures that there are no duplicate accounts in the genesis state and any
// provided vesting accounts are valid.
func validateGenesisStateAccounts(accs []GenesisAccount) error {
addrMap := make(map[string]bool, len(accs))
for i := 0; i < len(accs); i++ {
acc := accs[i]
strAddr := string(acc.Address)
if _, ok := addrMap[strAddr]; ok {
return fmt.Errorf("Duplicate account in genesis state: Address %v", acc.Address)
for _, acc := range accs {
addrStr := acc.Address.String()

// disallow any duplicate accounts
if _, ok := addrMap[addrStr]; ok {
return fmt.Errorf("duplicate account found in genesis state; address: %s", addrStr)
}

// validate any vesting fields
if !acc.OriginalVesting.IsZero() {
if acc.EndTime == 0 {
return fmt.Errorf("missing end time for vesting account; address: %s", addrStr)
}

if acc.StartTime >= acc.EndTime {
return fmt.Errorf(
"vesting start time must before end time; address: %s, start: %s, end: %s",
addrStr,
time.Unix(acc.StartTime, 0).UTC().Format(time.RFC3339),
time.Unix(acc.EndTime, 0).UTC().Format(time.RFC3339),
)
}
}
addrMap[strAddr] = true

addrMap[addrStr] = true
}

return nil
}

Expand Down
32 changes: 22 additions & 10 deletions cmd/gaia/app/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,29 +111,41 @@ func makeMsg(name string, pk crypto.PubKey) auth.StdTx {
}

func TestGaiaGenesisValidation(t *testing.T) {
genTxs := make([]auth.StdTx, 2)
// Test duplicate accounts fails
genTxs[0] = makeMsg("test-0", pk1)
genTxs[1] = makeMsg("test-1", pk1)
genesisState := makeGenesisState(t, genTxs)
genTxs := []auth.StdTx{makeMsg("test-0", pk1), makeMsg("test-1", pk2)}
dupGenTxs := []auth.StdTx{makeMsg("test-0", pk1), makeMsg("test-1", pk1)}

// require duplicate accounts fails validation
genesisState := makeGenesisState(t, dupGenTxs)
err := GaiaValidateGenesisState(genesisState)
require.NotNil(t, err)
// Test bonded + jailed validator fails
require.Error(t, err)

// require invalid vesting account fails validation (invalid end time)
genesisState = makeGenesisState(t, genTxs)
genesisState.Accounts[0].OriginalVesting = genesisState.Accounts[0].Coins
err = GaiaValidateGenesisState(genesisState)
require.Error(t, err)
genesisState.Accounts[0].StartTime = 1548888000
genesisState.Accounts[0].EndTime = 1548775410
err = GaiaValidateGenesisState(genesisState)
require.Error(t, err)

// require bonded + jailed validator fails validation
genesisState = makeGenesisState(t, genTxs)
val1 := stakingTypes.NewValidator(addr1, pk1, stakingTypes.Description{Moniker: "test #2"})
val1.Jailed = true
val1.Status = sdk.Bonded
genesisState.StakingData.Validators = append(genesisState.StakingData.Validators, val1)
err = GaiaValidateGenesisState(genesisState)
require.NotNil(t, err)
// Test duplicate validator fails
require.Error(t, err)

// require duplicate validator fails validation
val1.Jailed = false
genesisState = makeGenesisState(t, genTxs)
val2 := stakingTypes.NewValidator(addr1, pk1, stakingTypes.Description{Moniker: "test #3"})
genesisState.StakingData.Validators = append(genesisState.StakingData.Validators, val1)
genesisState.StakingData.Validators = append(genesisState.StakingData.Validators, val2)
err = GaiaValidateGenesisState(genesisState)
require.NotNil(t, err)
require.Error(t, err)
}

func TestNewDefaultGenesisAccount(t *testing.T) {
Expand Down
60 changes: 60 additions & 0 deletions docs/gaia/genesis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Gaia Genesis State

Gaia genesis state, `GenesisState`, is composed of accounts, various module
states and metadata such as genesis transactions. Each module may specify its
own `GenesisState`. In addition, each module may specify its own genesis state
validation, import and export functionality.

The Gaia genesis state is defined as follows:

```go
type GenesisState struct {
Accounts []GenesisAccount `json:"accounts"`
AuthData auth.GenesisState `json:"auth"`
BankData bank.GenesisState `json:"bank"`
StakingData staking.GenesisState `json:"staking"`
MintData mint.GenesisState `json:"mint"`
DistrData distr.GenesisState `json:"distr"`
GovData gov.GenesisState `json:"gov"`
SlashingData slashing.GenesisState `json:"slashing"`
GenTxs []json.RawMessage `json:"gentxs"`
}
```

In the ABCI `initChainer` definition of Gaia the `initFromGenesisState` is called
which internally calls each module's `InitGenesis` providing its own respective
`GenesisState` as a parameter.

## Accounts

Genesis accounts defined in the `GenesisState` are defined as follows:

```go
type GenesisAccount struct {
Address sdk.AccAddress `json:"address"`
Coins sdk.Coins `json:"coins"`
Sequence uint64 `json:"sequence_number"`
AccountNumber uint64 `json:"account_number"`

// vesting account fields
OriginalVesting sdk.Coins `json:"original_vesting"` // total vesting coins upon initialization
DelegatedFree sdk.Coins `json:"delegated_free"` // delegated vested coins at time of delegation
DelegatedVesting sdk.Coins `json:"delegated_vesting"` // delegated vesting coins at time of delegation
StartTime int64 `json:"start_time"` // vesting start time (UNIX Epoch time)
EndTime int64 `json:"end_time"` // vesting end time (UNIX Epoch time)
}
```

Each account must have a valid and unique account number in addition to a
sequence number (nonce) and address.

Accounts may also be vesting, in which case they must provide the necessary vesting
information. Vesting accounts must provide at a minimum `OriginalVesting` and
`EndTime`. If `StartTime` is also provided, the account will be treated as a
"continuous" vesting account in which it vests coins at a predefined schedule.
Providing a `StartTime` must be less than `EndTime` but may be in the future.
In other words, it does not have to be equal to the genesis time. In a new chain
starting from a fresh state (not exported), `OriginalVesting` must be less than
or equal to `Coins.`

<!-- TODO: Remaining modules and components in GenesisState -->

0 comments on commit 1a656e7

Please sign in to comment.