Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Genesis Account Command + Types #122

Merged
merged 15 commits into from
Sep 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/crisis"
distr "github.com/cosmos/cosmos-sdk/x/distribution"
"github.com/cosmos/cosmos-sdk/x/genaccounts"
"github.com/cosmos/cosmos-sdk/x/genutil"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/mint"
Expand All @@ -43,7 +42,6 @@ var (
// non-dependant module elements, such as codec registration
// and genesis verification.
ModuleBasics = module.NewBasicManager(
genaccounts.AppModuleBasic{},
genutil.AppModuleBasic{},
auth.AppModuleBasic{},
bank.AppModuleBasic{},
Expand Down Expand Up @@ -182,7 +180,6 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest b
// NOTE: Any module instantiated in the module manager that is later modified
// must be passed by reference here.
app.mm = module.NewManager(
genaccounts.NewAppModule(app.accountKeeper),
genutil.NewAppModule(app.accountKeeper, app.stakingKeeper, app.BaseApp.DeliverTx),
auth.NewAppModule(app.accountKeeper),
bank.NewAppModule(app.bankKeeper, app.accountKeeper),
Expand All @@ -205,9 +202,9 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest b
// NOTE: The genutils module must occur after staking so that pools are
// properly initialized with tokens from genesis accounts.
app.mm.SetOrderInitGenesis(
genaccounts.ModuleName, distr.ModuleName, staking.ModuleName,
auth.ModuleName, bank.ModuleName, slashing.ModuleName, gov.ModuleName,
mint.ModuleName, supply.ModuleName, crisis.ModuleName, genutil.ModuleName,
distr.ModuleName, staking.ModuleName, auth.ModuleName, bank.ModuleName,
slashing.ModuleName, gov.ModuleName, mint.ModuleName, supply.ModuleName,
crisis.ModuleName, genutil.ModuleName,
)

app.mm.RegisterInvariants(&app.crisisKeeper)
Expand All @@ -218,7 +215,6 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest b
// NOTE: This is not required for apps that don't use the simulator for fuzz testing
// transactions.
app.sm = module.NewSimulationManager(
genaccounts.NewAppModule(app.accountKeeper),
auth.NewAppModule(app.accountKeeper),
bank.NewAppModule(app.bankKeeper, app.accountKeeper),
supply.NewAppModule(app.supplyKeeper, app.accountKeeper),
Expand Down
4 changes: 2 additions & 2 deletions app/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ func TestGaiadExport(t *testing.T) {
// ensure that black listed addresses are properly set in bank keeper
func TestBlackListedAddrs(t *testing.T) {
db := db.NewMemDB()
app := NewGaiaApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, 0)
gapp := NewGaiaApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, 0)

for acc := range maccPerms {
require.True(t, app.bankKeeper.BlacklistedAddr(app.supplyKeeper.GetModuleAddress(acc)))
require.True(t, gapp.bankKeeper.BlacklistedAddr(gapp.supplyKeeper.GetModuleAddress(acc)))
}
}

Expand Down
11 changes: 11 additions & 0 deletions app/genesis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package app

import "encoding/json"

// GenesisState defines a type alias for the Gaia genesis application state.
type GenesisState map[string]json.RawMessage

// NewDefaultGenesisState generates the default state for the application.
func NewDefaultGenesisState() GenesisState {
return ModuleBasics.DefaultGenesis()
}
44 changes: 22 additions & 22 deletions app/sim_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,18 +261,18 @@ func BenchmarkFullAppSimulation(b *testing.B) {
_ = os.RemoveAll(dir)
}()

app := NewGaiaApp(logger, db, nil, true, simapp.FlagPeriodValue, interBlockCacheOpt())
gapp := NewGaiaApp(logger, db, nil, true, simapp.FlagPeriodValue, interBlockCacheOpt())

// Run randomized simulation
// TODO: parameterize numbers, save for a later PR
_, simParams, simErr := simulation.SimulateFromSeed(
b, os.Stdout, app.BaseApp, simapp.AppStateFn(app.Codec(), app.sm),
testAndRunTxs(app, config), app.ModuleAccountAddrs(), config,
b, os.Stdout, gapp.BaseApp, simapp.AppStateFn(gapp.Codec(), gapp.sm),
testAndRunTxs(gapp, config), gapp.ModuleAccountAddrs(), config,
)

// export state and params before the simulation error is checked
if config.ExportStatePath != "" {
if err := ExportStateToJSON(app, config.ExportStatePath); err != nil {
if err := ExportStateToJSON(gapp, config.ExportStatePath); err != nil {
fmt.Println(err)
b.Fail()
}
Expand Down Expand Up @@ -320,18 +320,18 @@ func TestFullAppSimulation(t *testing.T) {
_ = os.RemoveAll(dir)
}()

app := NewGaiaApp(logger, db, nil, true, simapp.FlagPeriodValue, fauxMerkleModeOpt)
require.Equal(t, "GaiaApp", app.Name())
gapp := NewGaiaApp(logger, db, nil, true, simapp.FlagPeriodValue, fauxMerkleModeOpt)
require.Equal(t, "GaiaApp", gapp.Name())

// Run randomized simulation
_, simParams, simErr := simulation.SimulateFromSeed(
t, os.Stdout, app.BaseApp, simapp.AppStateFn(app.Codec(), app.sm),
testAndRunTxs(app, config), app.ModuleAccountAddrs(), config,
t, os.Stdout, gapp.BaseApp, simapp.AppStateFn(gapp.Codec(), gapp.sm),
testAndRunTxs(gapp, config), gapp.ModuleAccountAddrs(), config,
)

// export state and params before the simulation error is checked
if config.ExportStatePath != "" {
err := ExportStateToJSON(app, config.ExportStatePath)
err := ExportStateToJSON(gapp, config.ExportStatePath)
require.NoError(t, err)
}

Expand Down Expand Up @@ -490,19 +490,19 @@ func TestAppSimulationAfterImport(t *testing.T) {
_ = os.RemoveAll(dir)
}()

app := NewGaiaApp(logger, db, nil, true, simapp.FlagPeriodValue, fauxMerkleModeOpt)
require.Equal(t, "GaiaApp", app.Name())
gapp := NewGaiaApp(logger, db, nil, true, simapp.FlagPeriodValue, fauxMerkleModeOpt)
require.Equal(t, "GaiaApp", gapp.Name())

// Run randomized simulation
// Run randomized simulation
stopEarly, simParams, simErr := simulation.SimulateFromSeed(
t, os.Stdout, app.BaseApp, simapp.AppStateFn(app.Codec(), app.sm),
testAndRunTxs(app, config), app.ModuleAccountAddrs(), config,
t, os.Stdout, gapp.BaseApp, simapp.AppStateFn(gapp.Codec(), gapp.sm),
testAndRunTxs(gapp, config), gapp.ModuleAccountAddrs(), config,
)

// export state and params before the simulation error is checked
if config.ExportStatePath != "" {
err := ExportStateToJSON(app, config.ExportStatePath)
err := ExportStateToJSON(gapp, config.ExportStatePath)
require.NoError(t, err)
}

Expand All @@ -529,7 +529,7 @@ func TestAppSimulationAfterImport(t *testing.T) {

fmt.Printf("Exporting genesis...\n")

appState, _, err := app.ExportAppStateAndValidators(true, []string{})
appState, _, err := gapp.ExportAppStateAndValidators(true, []string{})
if err != nil {
panic(err)
}
Expand All @@ -553,7 +553,7 @@ func TestAppSimulationAfterImport(t *testing.T) {

// Run randomized simulation on imported app
_, _, err = simulation.SimulateFromSeed(
t, os.Stdout, newApp.BaseApp, simapp.AppStateFn(app.Codec(), app.sm),
t, os.Stdout, newApp.BaseApp, simapp.AppStateFn(gapp.Codec(), gapp.sm),
testAndRunTxs(newApp, config), newApp.ModuleAccountAddrs(), config,
)

Expand Down Expand Up @@ -623,17 +623,17 @@ func BenchmarkInvariants(b *testing.B) {
os.RemoveAll(dir)
}()

app := NewGaiaApp(logger, db, nil, true, simapp.FlagPeriodValue, interBlockCacheOpt())
gapp := NewGaiaApp(logger, db, nil, true, simapp.FlagPeriodValue, interBlockCacheOpt())

// 2. Run parameterized simulation (w/o invariants)
_, simParams, simErr := simulation.SimulateFromSeed(
b, ioutil.Discard, app.BaseApp, simapp.AppStateFn(app.Codec(), app.sm),
testAndRunTxs(app, config), app.ModuleAccountAddrs(), config,
b, ioutil.Discard, gapp.BaseApp, simapp.AppStateFn(gapp.Codec(), gapp.sm),
testAndRunTxs(gapp, config), gapp.ModuleAccountAddrs(), config,
)

// export state and params before the simulation error is checked
if config.ExportStatePath != "" {
if err := ExportStateToJSON(app, config.ExportStatePath); err != nil {
if err := ExportStateToJSON(gapp, config.ExportStatePath); err != nil {
fmt.Println(err)
b.Fail()
}
Expand All @@ -651,13 +651,13 @@ func BenchmarkInvariants(b *testing.B) {
b.FailNow()
}

ctx := app.NewContext(true, abci.Header{Height: app.LastBlockHeight() + 1})
ctx := gapp.NewContext(true, abci.Header{Height: gapp.LastBlockHeight() + 1})

// 3. Benchmark each invariant separately
//
// NOTE: We use the crisis keeper as it has all the invariants registered with
// their respective metadata which makes it useful for testing/benchmarking.
for _, cr := range app.crisisKeeper.Routes() {
for _, cr := range gapp.crisisKeeper.Routes() {
b.Run(fmt.Sprintf("%s/%s", cr.ModuleName, cr.Route), func(b *testing.B) {
if res, stop := cr.Invar(ctx); stop {
fmt.Printf("broken invariant at block %d of %d\n%s", ctx.BlockHeight()-1, config.NumBlocks, res)
Expand Down
11 changes: 5 additions & 6 deletions cli_test/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import (
"github.com/cosmos/cosmos-sdk/tests"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/genaccounts"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/mint"
)
Expand Down Expand Up @@ -1272,12 +1271,12 @@ func TestGaiadAddGenesisAccount(t *testing.T) {
genesisState := f.GenesisState()

cdc := app.MakeCodec()
accounts := genaccounts.GetGenesisStateFromAppState(cdc, genesisState)
accounts := auth.GetGenesisStateFromAppState(cdc, genesisState).Accounts

require.Equal(t, accounts[0].Address, f.KeyAddress(keyFoo))
require.Equal(t, accounts[1].Address, f.KeyAddress(keyBar))
require.True(t, accounts[0].Coins.IsEqual(startCoins))
require.True(t, accounts[1].Coins.IsEqual(bazCoins))
require.Equal(t, accounts[0].GetAddress(), f.KeyAddress(keyFoo))
require.Equal(t, accounts[1].GetAddress(), f.KeyAddress(keyBar))
require.True(t, accounts[0].GetCoins().IsEqual(startCoins))
require.True(t, accounts[1].GetCoins().IsEqual(bazCoins))

// Cleanup testing directories
f.Cleanup()
Expand Down
142 changes: 142 additions & 0 deletions cmd/gaiad/genaccounts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package main

import (
"errors"
"fmt"

"github.com/spf13/cobra"
"github.com/spf13/viper"

"github.com/tendermint/tendermint/libs/cli"

"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
"github.com/cosmos/cosmos-sdk/x/genutil"
)

const (
flagClientHome = "home-client"
flagVestingStart = "vesting-start-time"
flagVestingEnd = "vesting-end-time"
flagVestingAmt = "vesting-amount"
)

// AddGenesisAccountCmd returns add-genesis-account cobra Command.
func AddGenesisAccountCmd(
ctx *server.Context, cdc *codec.Codec, defaultNodeHome, defaultClientHome string,
) *cobra.Command {

cmd := &cobra.Command{
Use: "add-genesis-account [address_or_key_name] [coin][,[coin]]",
Short: "Add a genesis account to genesis.json",
Long: `Add a genesis account to genesis.json. The provided account must specify
the account address or key name and a list of initial coins. If a key name is given,
the address will be looked up in the local Keybase. The list of initial tokens must
contain valid denominations. Accounts may optionally be supplied with vesting parameters.
`,
Args: cobra.ExactArgs(2),
RunE: func(_ *cobra.Command, args []string) error {
config := ctx.Config
config.SetRoot(viper.GetString(cli.HomeFlag))

addr, err := sdk.AccAddressFromBech32(args[0])
if err != nil {
// attempt to lookup address from Keybase if no address was provided
kb, err := keys.NewKeyBaseFromDir(viper.GetString(flagClientHome))
if err != nil {
return err
}

info, err := kb.Get(args[0])
if err != nil {
return fmt.Errorf("failed to get address from Keybase: %w", err)
}

addr = info.GetAddress()
}

coins, err := sdk.ParseCoins(args[1])
if err != nil {
return fmt.Errorf("failed to parse coins: %w", err)
}

vestingStart := viper.GetInt64(flagVestingStart)
vestingEnd := viper.GetInt64(flagVestingEnd)
vestingAmt, err := sdk.ParseCoins(viper.GetString(flagVestingAmt))
if err != nil {
return fmt.Errorf("failed to parse vesting amount: %w", err)
}

// create concrete account type based on input parameters
var genAccount authexported.GenesisAccount

baseAccount := auth.NewBaseAccount(addr, coins.Sort(), nil, 0, 0)
if !vestingAmt.IsZero() {
baseVestingAccount := auth.NewBaseVestingAccount(
baseAccount, vestingAmt.Sort(), sdk.Coins{}, sdk.Coins{}, vestingEnd,
)

switch {
case vestingStart != 0 && vestingEnd != 0:
genAccount = auth.NewContinuousVestingAccountRaw(baseVestingAccount, vestingStart)

case vestingEnd != 0:
genAccount = auth.NewDelayedVestingAccountRaw(baseVestingAccount)

default:
return errors.New("invalid vesting parameters; must supply start and end time or end time")
}
} else {
genAccount = baseAccount
}

if err := genAccount.Validate(); err != nil {
return fmt.Errorf("failed to validate new genesis account: %w", err)
}

genFile := config.GenesisFile()
appState, genDoc, err := genutil.GenesisStateFromGenFile(cdc, genFile)
if err != nil {
return fmt.Errorf("failed to unmarshal genesis state: %w", err)
}

authGenState := auth.GetGenesisStateFromAppState(cdc, appState)

if authGenState.Accounts.Contains(addr) {
return fmt.Errorf("cannot add account at existing address %s", addr)
}

// Add the new account to the set of genesis accounts and sanitize the
// accounts afterwards.
authGenState.Accounts = append(authGenState.Accounts, genAccount)
authGenState.Accounts = auth.SanitizeGenesisAccounts(authGenState.Accounts)

authGenStateBz, err := cdc.MarshalJSON(authGenState)
if err != nil {
return fmt.Errorf("failed to marshal auth genesis state: %w", err)
}

appState[auth.ModuleName] = authGenStateBz

appStateJSON, err := cdc.MarshalJSON(appState)
if err != nil {
return fmt.Errorf("failed to marshal application genesis state: %w", err)
}

genDoc.AppState = appStateJSON
return genutil.ExportGenesisFile(genDoc, genFile)
},
}

cmd.Flags().String(cli.HomeFlag, defaultNodeHome, "node's home directory")
cmd.Flags().String(flagClientHome, defaultClientHome, "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
}
Loading