Merge PR #3549: Support Vesting Accounts in add-genesis-account

This commit is contained in:
Alexander Bezobchuk 2019-02-08 12:50:06 -08:00 committed by Christopher Goes
parent b5fdb83830
commit d759bef4d1
5 changed files with 139 additions and 28 deletions

View File

@ -46,6 +46,8 @@ FEATURES
* [\#3429](https://github.com/cosmos/cosmos-sdk/issues/3429) Support querying
for all delegator distribution rewards.
* \#3449 Proof verification now works with absence proofs
* [\#3484](https://github.com/cosmos/cosmos-sdk/issues/3484) Add support
vesting accounts to the add-genesis-account command.
* Gaia
- [\#3397](https://github.com/cosmos/cosmos-sdk/pull/3397) Implement genesis file sanitization to avoid failures at chain init.

View File

@ -8,6 +8,7 @@ import (
"path/filepath"
"strings"
"testing"
"time"
"github.com/stretchr/testify/require"
@ -34,15 +35,22 @@ const (
feeDenom = "feetoken"
fee2Denom = "fee2token"
keyBaz = "baz"
keyVesting = "vesting"
keyFooBarBaz = "foobarbaz"
)
var startCoins = sdk.Coins{
sdk.NewCoin(feeDenom, staking.TokensFromTendermintPower(1000000)),
sdk.NewCoin(fee2Denom, staking.TokensFromTendermintPower(1000000)),
sdk.NewCoin(fooDenom, staking.TokensFromTendermintPower(1000)),
sdk.NewCoin(denom, staking.TokensFromTendermintPower(150)),
}
var (
startCoins = sdk.Coins{
sdk.NewCoin(feeDenom, staking.TokensFromTendermintPower(1000000)),
sdk.NewCoin(fee2Denom, staking.TokensFromTendermintPower(1000000)),
sdk.NewCoin(fooDenom, staking.TokensFromTendermintPower(1000)),
sdk.NewCoin(denom, staking.TokensFromTendermintPower(150)),
}
vestingCoins = sdk.Coins{
sdk.NewCoin(feeDenom, staking.TokensFromTendermintPower(500000)),
}
)
//___________________________________________________________________________________
// Fixtures
@ -108,6 +116,7 @@ func InitFixtures(t *testing.T) (f *Fixtures) {
f.KeysAdd(keyFoo)
f.KeysAdd(keyBar)
f.KeysAdd(keyBaz)
f.KeysAdd(keyVesting)
f.KeysAdd(keyFooBarBaz, "--multisig-threshold=2", fmt.Sprintf(
"--multisig=%s,%s,%s", keyFoo, keyBar, keyBaz))
@ -120,6 +129,12 @@ func InitFixtures(t *testing.T) (f *Fixtures) {
// Start an account with tokens
f.AddGenesisAccount(f.KeyAddress(keyFoo), startCoins)
f.AddGenesisAccount(
f.KeyAddress(keyVesting), startCoins,
fmt.Sprintf("--vesting-amount=%s", vestingCoins),
fmt.Sprintf("--vesting-start-time=%d", time.Now().UTC().UnixNano()),
fmt.Sprintf("--vesting-end-time=%d", time.Now().Add(60*time.Second).UTC().UnixNano()),
)
f.GenTx(keyFoo)
f.CollectGenTxs()
return

View File

@ -16,7 +16,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth"
)
// AddGenesisAccountCmd returns add-genesis-account cobra Command
// AddGenesisAccountCmd returns add-genesis-account cobra Command.
func AddGenesisAccountCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "add-genesis-account [address_or_key_name] [coin][,[coin]]",
@ -32,22 +32,32 @@ func AddGenesisAccountCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command
if err != nil {
return err
}
info, err := kb.Get(args[0])
if err != nil {
return err
}
addr = info.GetAddress()
}
coins, err := sdk.ParseCoins(args[1])
if err != nil {
return err
}
coins.Sort()
vestingStart := viper.GetInt64(flagVestingStart)
vestingEnd := viper.GetInt64(flagVestingEnd)
vestingAmt, err := sdk.ParseCoins(viper.GetString(flagVestingAmt))
if err != nil {
return err
}
genFile := config.GenesisFile()
if !common.FileExists(genFile) {
return fmt.Errorf("%s does not exist, run `gaiad init` first", genFile)
}
genDoc, err := LoadGenesisDoc(cdc, genFile)
if err != nil {
return err
@ -58,7 +68,7 @@ func AddGenesisAccountCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command
return err
}
appState, err = addGenesisAccount(cdc, appState, addr, coins)
appState, err = addGenesisAccount(cdc, appState, addr, coins, vestingAmt, vestingStart, vestingEnd)
if err != nil {
return err
}
@ -74,10 +84,18 @@ func AddGenesisAccountCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command
cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory")
cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory")
cmd.Flags().String(flagVestingAmt, "", "amount of coins for vesting accounts")
cmd.Flags().Uint64(flagVestingStart, 0, "schedule start time (unix epoch) for vesting accounts")
cmd.Flags().Uint64(flagVestingEnd, 0, "schedule end time (unix epoch) for vesting accounts")
return cmd
}
func addGenesisAccount(cdc *codec.Codec, appState app.GenesisState, addr sdk.AccAddress, coins sdk.Coins) (app.GenesisState, error) {
func addGenesisAccount(
cdc *codec.Codec, appState app.GenesisState, addr sdk.AccAddress,
coins, vestingAmt sdk.Coins, vestingStart, vestingEnd int64,
) (app.GenesisState, error) {
for _, stateAcc := range appState.Accounts {
if stateAcc.Address.Equals(addr) {
return appState, fmt.Errorf("the application state already contains account %v", addr)
@ -86,6 +104,38 @@ func addGenesisAccount(cdc *codec.Codec, appState app.GenesisState, addr sdk.Acc
acc := auth.NewBaseAccountWithAddress(addr)
acc.Coins = coins
appState.Accounts = append(appState.Accounts, app.NewGenesisAccount(&acc))
if !vestingAmt.IsZero() {
var vacc auth.VestingAccount
bvacc := &auth.BaseVestingAccount{
BaseAccount: &acc,
OriginalVesting: vestingAmt,
EndTime: vestingEnd,
}
if bvacc.OriginalVesting.IsAllGT(acc.Coins) {
return appState, fmt.Errorf("vesting amount cannot be greater than total amount")
}
if vestingStart >= vestingEnd {
return appState, fmt.Errorf("vesting start time must before end time")
}
if vestingStart != 0 {
vacc = &auth.ContinuousVestingAccount{
BaseVestingAccount: bvacc,
StartTime: vestingStart,
}
} else {
vacc = &auth.DelayedVestingAccount{
BaseVestingAccount: bvacc,
}
}
appState.Accounts = append(appState.Accounts, app.NewGenesisAccountI(vacc))
} else {
appState.Accounts = append(appState.Accounts, app.NewGenesisAccount(&acc))
}
return appState, nil
}

View File

@ -15,9 +15,12 @@ func TestAddGenesisAccount(t *testing.T) {
cdc := codec.New()
addr1 := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
type args struct {
appState app.GenesisState
addr sdk.AccAddress
coins sdk.Coins
appState app.GenesisState
addr sdk.AccAddress
coins sdk.Coins
vestingAmt sdk.Coins
vestingStart int64
vestingEnd int64
}
tests := []struct {
name string
@ -30,16 +33,55 @@ func TestAddGenesisAccount(t *testing.T) {
app.GenesisState{},
addr1,
sdk.Coins{},
sdk.Coins{},
0,
0,
},
false},
{"dup account", args{
app.GenesisState{Accounts: []app.GenesisAccount{{Address: addr1}}},
addr1,
sdk.Coins{}}, true},
false,
},
{
"dup account",
args{
app.GenesisState{Accounts: []app.GenesisAccount{{Address: addr1}}},
addr1,
sdk.Coins{},
sdk.Coins{},
0,
0,
},
true,
},
{
"invalid vesting amount",
args{
app.GenesisState{},
addr1,
sdk.Coins{sdk.NewInt64Coin("stake", 50)},
sdk.Coins{sdk.NewInt64Coin("stake", 100)},
0,
0,
},
true,
},
{
"invalid vesting times",
args{
app.GenesisState{},
addr1,
sdk.Coins{sdk.NewInt64Coin("stake", 50)},
sdk.Coins{sdk.NewInt64Coin("stake", 50)},
1654668078,
1554668078,
},
true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := addGenesisAccount(cdc, tt.args.appState, tt.args.addr, tt.args.coins)
_, err := addGenesisAccount(
cdc, tt.args.appState, tt.args.addr, tt.args.coins,
tt.args.vestingAmt, tt.args.vestingStart, tt.args.vestingEnd,
)
require.Equal(t, tt.wantErr, (err != nil))
})
}

View File

@ -19,8 +19,11 @@ import (
)
const (
flagOverwrite = "overwrite"
flagClientHome = "home-client"
flagOverwrite = "overwrite"
flagClientHome = "home-client"
flagVestingStart = "vesting-start-time"
flagVestingEnd = "vesting-end-time"
flagVestingAmt = "vesting-amount"
)
type printInfo struct {
@ -31,19 +34,19 @@ type printInfo struct {
AppMessage json.RawMessage `json:"app_message"`
}
// nolint: errcheck
func displayInfo(cdc *codec.Codec, info printInfo) error {
out, err := codec.MarshalJSONIndent(cdc, info)
if err != nil {
return err
}
fmt.Fprintf(os.Stderr, "%s\n", string(out))
fmt.Fprintf(os.Stderr, "%s\n", string(out)) // nolint: errcheck
return nil
}
// get cmd to initialize all files for tendermint and application
// nolint
func InitCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command {
// InitCmd returns a command that initializes all files needed for Tendermint
// and the respective application.
func InitCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { // nolint: golint
cmd := &cobra.Command{
Use: "init [moniker]",
Short: "Initialize private validator, p2p, genesis, and application configuration files",
@ -80,7 +83,6 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command {
toPrint := newPrintInfo(config.Moniker, chainID, nodeID, "", appState)
cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config)
return displayInfo(cdc, toPrint)
},
}