Merge PR #3425: Add Vesting Account Genesis Validation
This commit is contained in:
parent
3e6ce3de8d
commit
1a656e7023
@ -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
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
|
||||
@ -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 {
|
||||
@ -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)
|
||||
}
|
||||
addrMap[strAddr] = true
|
||||
|
||||
// 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[addrStr] = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
60
docs/gaia/genesis.md
Normal file
60
docs/gaia/genesis.md
Normal file
@ -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 -->
|
||||
Loading…
Reference in New Issue
Block a user