diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 210900d614..84a18aa06d 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -241,6 +241,7 @@ func (app *BaseApp) setDeliverState(header abci.Header) { } //______________________________________________________________________________ + // ABCI // Implements ABCI diff --git a/client/lcd/root.go b/client/lcd/root.go index b166d4ce46..a7be5079bf 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -80,7 +80,7 @@ func createHandler(cdc *wire.Codec) http.Handler { keys.RegisterRoutes(r) rpc.RegisterRoutes(ctx, r) tx.RegisterRoutes(ctx, r, cdc) - auth.RegisterRoutes(ctx, r, cdc, "main") + auth.RegisterRoutes(ctx, r, cdc, "acc") bank.RegisterRoutes(ctx, r, cdc, kb) ibc.RegisterRoutes(ctx, r, cdc, kb) return r diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 690dafe538..a9cf248e1c 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -1,6 +1,7 @@ package app import ( + "encoding/json" "os" abci "github.com/tendermint/abci/types" @@ -58,8 +59,14 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp { keyStake: sdk.NewKVStoreKey("stake"), } - // add accountMapper/handlers - app.accountMapper = auth.NewAccountMapper(app.cdc, app.keyMain, &auth.BaseAccount{}) + // define the accountMapper + app.accountMapper = auth.NewAccountMapper( + app.cdc, + app.keyAccount, // target store + &auth.BaseAccount{}, // prototype + ) + + // add handlers app.coinKeeper = bank.NewKeeper(app.accountMapper) app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace)) app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace)) @@ -117,3 +124,23 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci return abci.ResponseInitChain{} } + +// export the state of gaia for a genesis f +func (app *GaiaApp) ExportAppStateJSON() (appState json.RawMessage, err error) { + ctx := app.NewContext(true, abci.Header{}) + + // iterate to get the accounts + accounts := []GenesisAccount{} + appendAccount := func(acc sdk.Account) (stop bool) { + account := NewGenesisAccountI(acc) + accounts = append(accounts, account) + return false + } + app.accountMapper.IterateAccounts(ctx, appendAccount) + + genState := GenesisState{ + Accounts: accounts, + StakeData: stake.WriteGenesis(ctx, app.stakeKeeper), + } + return wire.MarshalJSONIndent(app.cdc, genState) +} diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index 513430ec9a..7d0fb01d0c 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -35,6 +35,13 @@ func NewGenesisAccount(acc *auth.BaseAccount) GenesisAccount { } } +func NewGenesisAccountI(acc sdk.Account) GenesisAccount { + return GenesisAccount{ + Address: acc.GetAddress(), + Coins: acc.GetCoins(), + } +} + // convert GenesisAccount to auth.BaseAccount func (ga *GenesisAccount) ToAccount() (acc *auth.BaseAccount) { return &auth.BaseAccount{ diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index ab0a98c695..93d6b57b91 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -44,7 +44,7 @@ func main() { // add query/post commands (custom to binary) rootCmd.AddCommand( client.GetCommands( - authcmd.GetAccountCmd("main", cdc, authcmd.GetAccountDecoder(cdc)), + authcmd.GetAccountCmd("acc", cdc, authcmd.GetAccountDecoder(cdc)), stakecmd.GetCmdQueryCandidate("stake", cdc), //stakecmd.GetCmdQueryCandidates("stake", cdc), stakecmd.GetCmdQueryDelegatorBond("stake", cdc), diff --git a/cmd/gaia/cmd/gaiad/main.go b/cmd/gaia/cmd/gaiad/main.go index 0417671a2f..f64498d850 100644 --- a/cmd/gaia/cmd/gaiad/main.go +++ b/cmd/gaia/cmd/gaiad/main.go @@ -1,7 +1,7 @@ package main import ( - "path/filepath" + "encoding/json" "github.com/spf13/cobra" @@ -23,19 +23,20 @@ func main() { PersistentPreRunE: server.PersistentPreRunEFn(ctx), } - server.AddCommands(ctx, cdc, rootCmd, app.GaiaAppInit(), generateApp) + server.AddCommands(ctx, cdc, rootCmd, app.GaiaAppInit(), + server.ConstructAppCreator(newApp, "gaia"), + server.ConstructAppExporter(exportAppState, "gaia")) // prepare and add flags executor := cli.PrepareBaseCmd(rootCmd, "GA", app.DefaultNodeHome) executor.Execute() } -func generateApp(rootDir string, logger log.Logger) (abci.Application, error) { - dataDir := filepath.Join(rootDir, "data") - db, err := dbm.NewGoLevelDB("gaia", dataDir) - if err != nil { - return nil, err - } - bapp := app.NewGaiaApp(logger, db) - return bapp, nil +func newApp(logger log.Logger, db dbm.DB) abci.Application { + return app.NewGaiaApp(logger, db) +} + +func exportAppState(logger log.Logger, db dbm.DB) (json.RawMessage, error) { + gapp := app.NewGaiaApp(logger, db) + return gapp.ExportAppStateJSON() } diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index a1ec9a38bf..d3eae85c04 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -1,6 +1,8 @@ package app import ( + "encoding/json" + abci "github.com/tendermint/abci/types" cmn "github.com/tendermint/tmlibs/common" dbm "github.com/tendermint/tmlibs/db" @@ -57,7 +59,7 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp { // Define the accountMapper. app.accountMapper = auth.NewAccountMapper( cdc, - app.keyMain, // target store + app.keyAccount, // target store &types.AppAccount{}, // prototype ) @@ -119,3 +121,25 @@ func (app *BasecoinApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) } return abci.ResponseInitChain{} } + +// Custom logic for state export +func (app *BasecoinApp) ExportAppStateJSON() (appState json.RawMessage, err error) { + ctx := app.NewContext(true, abci.Header{}) + + // iterate to get the accounts + accounts := []*types.GenesisAccount{} + appendAccount := func(acc sdk.Account) (stop bool) { + account := &types.GenesisAccount{ + Address: acc.GetAddress(), + Coins: acc.GetCoins(), + } + accounts = append(accounts, account) + return false + } + app.accountMapper.IterateAccounts(ctx, appendAccount) + + genState := types.GenesisState{ + Accounts: accounts, + } + return wire.MarshalJSONIndent(app.cdc, genState) +} diff --git a/examples/basecoin/cmd/basecli/main.go b/examples/basecoin/cmd/basecli/main.go index 50800450fe..f5385b5596 100644 --- a/examples/basecoin/cmd/basecli/main.go +++ b/examples/basecoin/cmd/basecli/main.go @@ -14,11 +14,13 @@ import ( "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/version" + authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli" ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/client/cli" stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli" "github.com/cosmos/cosmos-sdk/examples/basecoin/app" + "github.com/cosmos/cosmos-sdk/examples/basecoin/types" ) // rootCmd is the entry point for this binary @@ -47,6 +49,11 @@ func main() { rootCmd.AddCommand(client.LineBreak) // add query/post commands (custom to binary) + rootCmd.AddCommand( + client.GetCommands( + authcmd.GetAccountCmd("acc", cdc, types.GetAccountDecoder(cdc)), + )...) + rootCmd.AddCommand( client.PostCommands( bankcmd.SendTxCmd(cdc), diff --git a/examples/basecoin/cmd/basecoind/main.go b/examples/basecoin/cmd/basecoind/main.go index 8d14821c6a..4e6a30a086 100644 --- a/examples/basecoin/cmd/basecoind/main.go +++ b/examples/basecoin/cmd/basecoind/main.go @@ -1,8 +1,8 @@ package main import ( + "encoding/json" "os" - "path/filepath" "github.com/spf13/cobra" @@ -25,7 +25,9 @@ func main() { PersistentPreRunE: server.PersistentPreRunEFn(ctx), } - server.AddCommands(ctx, cdc, rootCmd, server.DefaultAppInit, generateApp) + server.AddCommands(ctx, cdc, rootCmd, server.DefaultAppInit, + server.ConstructAppCreator(newApp, "basecoin"), + server.ConstructAppExporter(exportAppState, "basecoin")) // prepare and add flags rootDir := os.ExpandEnv("$HOME/.basecoind") @@ -33,12 +35,11 @@ func main() { executor.Execute() } -func generateApp(rootDir string, logger log.Logger) (abci.Application, error) { - dataDir := filepath.Join(rootDir, "data") - db, err := dbm.NewGoLevelDB("basecoin", dataDir) - if err != nil { - return nil, err - } - bapp := app.NewBasecoinApp(logger, db) - return bapp, nil +func newApp(logger log.Logger, db dbm.DB) abci.Application { + return app.NewBasecoinApp(logger, db) +} + +func exportAppState(logger log.Logger, db dbm.DB) (json.RawMessage, error) { + bapp := app.NewBasecoinApp(logger, db) + return bapp.ExportAppStateJSON() } diff --git a/examples/democoin/app/app.go b/examples/democoin/app/app.go index aabd4c41c5..7c8250b189 100644 --- a/examples/democoin/app/app.go +++ b/examples/democoin/app/app.go @@ -1,6 +1,8 @@ package app import ( + "encoding/json" + abci "github.com/tendermint/abci/types" cmn "github.com/tendermint/tmlibs/common" dbm "github.com/tendermint/tmlibs/db" @@ -36,6 +38,13 @@ type DemocoinApp struct { capKeyIBCStore *sdk.KVStoreKey capKeyStakingStore *sdk.KVStoreKey + // keepers + coinKeeper bank.Keeper + coolKeeper cool.Keeper + powKeeper pow.Keeper + ibcMapper ibc.Mapper + stakeKeeper simplestake.Keeper + // Manage getting and setting accounts accountMapper sdk.AccountMapper } @@ -59,26 +68,26 @@ func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp { // Define the accountMapper. app.accountMapper = auth.NewAccountMapper( cdc, - app.capKeyMainStore, // target store - &types.AppAccount{}, // prototype + app.capKeyAccountStore, // target store + &types.AppAccount{}, // prototype ) // Add handlers. - coinKeeper := bank.NewKeeper(app.accountMapper) - coolKeeper := cool.NewKeeper(app.capKeyMainStore, coinKeeper, app.RegisterCodespace(cool.DefaultCodespace)) - powKeeper := pow.NewKeeper(app.capKeyPowStore, pow.NewConfig("pow", int64(1)), coinKeeper, app.RegisterCodespace(pow.DefaultCodespace)) - ibcMapper := ibc.NewMapper(app.cdc, app.capKeyIBCStore, app.RegisterCodespace(ibc.DefaultCodespace)) - stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper, app.RegisterCodespace(simplestake.DefaultCodespace)) + app.coinKeeper = bank.NewKeeper(app.accountMapper) + app.coolKeeper = cool.NewKeeper(app.capKeyMainStore, app.coinKeeper, app.RegisterCodespace(cool.DefaultCodespace)) + app.powKeeper = pow.NewKeeper(app.capKeyPowStore, pow.NewConfig("pow", int64(1)), app.coinKeeper, app.RegisterCodespace(pow.DefaultCodespace)) + app.ibcMapper = ibc.NewMapper(app.cdc, app.capKeyIBCStore, app.RegisterCodespace(ibc.DefaultCodespace)) + app.stakeKeeper = simplestake.NewKeeper(app.capKeyStakingStore, app.coinKeeper, app.RegisterCodespace(simplestake.DefaultCodespace)) app.Router(). - AddRoute("bank", bank.NewHandler(coinKeeper)). - AddRoute("cool", cool.NewHandler(coolKeeper)). - AddRoute("pow", powKeeper.Handler). + AddRoute("bank", bank.NewHandler(app.coinKeeper)). + AddRoute("cool", cool.NewHandler(app.coolKeeper)). + AddRoute("pow", app.powKeeper.Handler). AddRoute("sketchy", sketchy.NewHandler()). - AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)). - AddRoute("simplestake", simplestake.NewHandler(stakeKeeper)) + AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)). + AddRoute("simplestake", simplestake.NewHandler(app.stakeKeeper)) // Initialize BaseApp. - app.SetInitChainer(app.initChainerFn(coolKeeper, powKeeper)) + app.SetInitChainer(app.initChainerFn(app.coolKeeper, app.powKeeper)) app.MountStoresIAVL(app.capKeyMainStore, app.capKeyAccountStore, app.capKeyPowStore, app.capKeyIBCStore, app.capKeyStakingStore) app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, auth.BurnFeeHandler)) err := app.LoadLatestVersion(app.capKeyMainStore) @@ -127,13 +136,13 @@ func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper, powKeeper pow.Keep } // Application specific genesis handling - err = coolKeeper.InitGenesis(ctx, genesisState.CoolGenesis) + err = cool.InitGenesis(ctx, app.coolKeeper, genesisState.CoolGenesis) if err != nil { panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 // return sdk.ErrGenesisParse("").TraceCause(err, "") } - err = powKeeper.InitGenesis(ctx, genesisState.POWGenesis) + err = pow.InitGenesis(ctx, app.powKeeper, genesisState.POWGenesis) if err != nil { panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 // return sdk.ErrGenesisParse("").TraceCause(err, "") @@ -142,3 +151,27 @@ func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper, powKeeper pow.Keep return abci.ResponseInitChain{} } } + +// Custom logic for state export +func (app *DemocoinApp) ExportAppStateJSON() (appState json.RawMessage, err error) { + ctx := app.NewContext(true, abci.Header{}) + + // iterate to get the accounts + accounts := []*types.GenesisAccount{} + appendAccount := func(acc sdk.Account) (stop bool) { + account := &types.GenesisAccount{ + Address: acc.GetAddress(), + Coins: acc.GetCoins(), + } + accounts = append(accounts, account) + return false + } + app.accountMapper.IterateAccounts(ctx, appendAccount) + + genState := types.GenesisState{ + Accounts: accounts, + POWGenesis: pow.WriteGenesis(ctx, app.powKeeper), + CoolGenesis: cool.WriteGenesis(ctx, app.coolKeeper), + } + return wire.MarshalJSONIndent(app.cdc, genState) +} diff --git a/examples/democoin/cmd/democli/main.go b/examples/democoin/cmd/democli/main.go index 45a448c2ef..cdf9396d6c 100644 --- a/examples/democoin/cmd/democli/main.go +++ b/examples/democoin/cmd/democli/main.go @@ -54,7 +54,7 @@ func main() { // start with commands common to basecoin rootCmd.AddCommand( client.GetCommands( - authcmd.GetAccountCmd("main", cdc, types.GetAccountDecoder(cdc)), + authcmd.GetAccountCmd("acc", cdc, types.GetAccountDecoder(cdc)), )...) rootCmd.AddCommand( client.PostCommands( diff --git a/examples/democoin/cmd/democoind/main.go b/examples/democoin/cmd/democoind/main.go index afca80bce3..3283da58ae 100644 --- a/examples/democoin/cmd/democoind/main.go +++ b/examples/democoin/cmd/democoind/main.go @@ -3,7 +3,6 @@ package main import ( "encoding/json" "os" - "path/filepath" "github.com/spf13/cobra" @@ -32,18 +31,24 @@ func CoolAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState jso key := "cool" value := json.RawMessage(`{ "trend": "ice-cold" + }`) + appState, err = server.AppendJSON(cdc, appState, key, value) + key = "pow" + value = json.RawMessage(`{ + "difficulty": 1, + "count": 0 }`) appState, err = server.AppendJSON(cdc, appState, key, value) return } -func generateApp(rootDir string, logger log.Logger) (abci.Application, error) { - db, err := dbm.NewGoLevelDB("democoin", filepath.Join(rootDir, "data")) - if err != nil { - return nil, err - } - bapp := app.NewDemocoinApp(logger, db) - return bapp, nil +func newApp(logger log.Logger, db dbm.DB) abci.Application { + return app.NewDemocoinApp(logger, db) +} + +func exportAppState(logger log.Logger, db dbm.DB) (json.RawMessage, error) { + dapp := app.NewDemocoinApp(logger, db) + return dapp.ExportAppStateJSON() } func main() { @@ -56,7 +61,9 @@ func main() { PersistentPreRunE: server.PersistentPreRunEFn(ctx), } - server.AddCommands(ctx, cdc, rootCmd, CoolAppInit, generateApp) + server.AddCommands(ctx, cdc, rootCmd, CoolAppInit, + server.ConstructAppCreator(newApp, "democoin"), + server.ConstructAppExporter(exportAppState, "democoin")) // prepare and add flags rootDir := os.ExpandEnv("$HOME/.democoind") diff --git a/examples/democoin/x/cool/keeper.go b/examples/democoin/x/cool/keeper.go index b180cd23c4..fef93b954d 100644 --- a/examples/democoin/x/cool/keeper.go +++ b/examples/democoin/x/cool/keeper.go @@ -44,7 +44,13 @@ func (k Keeper) CheckTrend(ctx sdk.Context, guessedTrend string) bool { } // InitGenesis - store the genesis trend -func (k Keeper) InitGenesis(ctx sdk.Context, data Genesis) error { +func InitGenesis(ctx sdk.Context, k Keeper, data Genesis) error { k.setTrend(ctx, data.Trend) return nil } + +// WriteGenesis - output the genesis trend +func WriteGenesis(ctx sdk.Context, k Keeper) Genesis { + trend := k.GetTrend(ctx) + return Genesis{trend} +} diff --git a/examples/democoin/x/cool/keeper_test.go b/examples/democoin/x/cool/keeper_test.go new file mode 100644 index 0000000000..9aca3cc2a0 --- /dev/null +++ b/examples/democoin/x/cool/keeper_test.go @@ -0,0 +1,50 @@ +package cool + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + abci "github.com/tendermint/abci/types" + dbm "github.com/tendermint/tmlibs/db" + + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" + auth "github.com/cosmos/cosmos-sdk/x/auth" + bank "github.com/cosmos/cosmos-sdk/x/bank" +) + +func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey) { + db := dbm.NewMemDB() + capKey := sdk.NewKVStoreKey("capkey") + ms := store.NewCommitMultiStore(db) + ms.MountStoreWithDB(capKey, sdk.StoreTypeIAVL, db) + ms.LoadLatestVersion() + return ms, capKey +} + +func TestCoolKeeper(t *testing.T) { + ms, capKey := setupMultiStore() + cdc := wire.NewCodec() + auth.RegisterBaseAccount(cdc) + + am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{}) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil) + ck := bank.NewKeeper(am) + keeper := NewKeeper(capKey, ck, DefaultCodespace) + + err := InitGenesis(ctx, keeper, Genesis{"icy"}) + assert.Nil(t, err) + + genesis := WriteGenesis(ctx, keeper) + assert.Nil(t, err) + assert.Equal(t, genesis, Genesis{"icy"}) + + res := keeper.GetTrend(ctx) + assert.Equal(t, res, "icy") + + keeper.setTrend(ctx, "fiery") + res = keeper.GetTrend(ctx) + assert.Equal(t, res, "fiery") +} diff --git a/examples/democoin/x/pow/handler_test.go b/examples/democoin/x/pow/handler_test.go index 8a4f81de24..0fffb8f5b2 100644 --- a/examples/democoin/x/pow/handler_test.go +++ b/examples/democoin/x/pow/handler_test.go @@ -30,7 +30,7 @@ func TestPowHandler(t *testing.T) { count := uint64(1) difficulty := uint64(2) - err := keeper.InitGenesis(ctx, Genesis{uint64(1), uint64(0)}) + err := InitGenesis(ctx, keeper, Genesis{uint64(1), uint64(0)}) assert.Nil(t, err) nonce, proof := mine(addr, count, difficulty) diff --git a/examples/democoin/x/pow/keeper.go b/examples/democoin/x/pow/keeper.go index 9a5edf0b5c..931e41a320 100644 --- a/examples/democoin/x/pow/keeper.go +++ b/examples/democoin/x/pow/keeper.go @@ -36,13 +36,29 @@ func NewKeeper(key sdk.StoreKey, config Config, ck bank.Keeper, codespace sdk.Co return Keeper{key, config, ck, codespace} } -// Init Genessis for the POW module -func (k Keeper) InitGenesis(ctx sdk.Context, genesis Genesis) error { +// InitGenesis for the POW module +func InitGenesis(ctx sdk.Context, k Keeper, genesis Genesis) error { k.SetLastDifficulty(ctx, genesis.Difficulty) k.SetLastCount(ctx, genesis.Count) return nil } +// WriteGenesis for the PoW module +func WriteGenesis(ctx sdk.Context, k Keeper) Genesis { + difficulty, err := k.GetLastDifficulty(ctx) + if err != nil { + panic(err) + } + count, err := k.GetLastCount(ctx) + if err != nil { + panic(err) + } + return Genesis{ + difficulty, + count, + } +} + var lastDifficultyKey = []byte("lastDifficultyKey") // get the last mining difficulty diff --git a/examples/democoin/x/pow/keeper_test.go b/examples/democoin/x/pow/keeper_test.go index 4c01559f6c..cb021eb16b 100644 --- a/examples/democoin/x/pow/keeper_test.go +++ b/examples/democoin/x/pow/keeper_test.go @@ -37,9 +37,13 @@ func TestPowKeeperGetSet(t *testing.T) { ck := bank.NewKeeper(am) keeper := NewKeeper(capKey, config, ck, DefaultCodespace) - err := keeper.InitGenesis(ctx, Genesis{uint64(1), uint64(0)}) + err := InitGenesis(ctx, keeper, Genesis{uint64(1), uint64(0)}) assert.Nil(t, err) + genesis := WriteGenesis(ctx, keeper) + assert.Nil(t, err) + assert.Equal(t, genesis, Genesis{uint64(1), uint64(0)}) + res, err := keeper.GetLastDifficulty(ctx) assert.Nil(t, err) assert.Equal(t, res, uint64(1)) diff --git a/server/constructors.go b/server/constructors.go new file mode 100644 index 0000000000..3d6950062b --- /dev/null +++ b/server/constructors.go @@ -0,0 +1,42 @@ +package server + +import ( + "encoding/json" + "path/filepath" + + abci "github.com/tendermint/abci/types" + dbm "github.com/tendermint/tmlibs/db" + "github.com/tendermint/tmlibs/log" +) + +// AppCreator lets us lazily initialize app, using home dir +// and other flags (?) to start +type AppCreator func(string, log.Logger) (abci.Application, error) + +// AppExporter dumps all app state to JSON-serializable structure +type AppExporter func(home string, log log.Logger) (json.RawMessage, error) + +// ConstructAppCreator returns an application generation function +func ConstructAppCreator(appFn func(log.Logger, dbm.DB) abci.Application, name string) AppCreator { + return func(rootDir string, logger log.Logger) (abci.Application, error) { + dataDir := filepath.Join(rootDir, "data") + db, err := dbm.NewGoLevelDB(name, dataDir) + if err != nil { + return nil, err + } + app := appFn(logger, db) + return app, nil + } +} + +// ConstructAppExporter returns an application export function +func ConstructAppExporter(appFn func(log.Logger, dbm.DB) (json.RawMessage, error), name string) AppExporter { + return func(rootDir string, logger log.Logger) (json.RawMessage, error) { + dataDir := filepath.Join(rootDir, "data") + db, err := dbm.NewGoLevelDB(name, dataDir) + if err != nil { + return nil, err + } + return appFn(logger, db) + } +} diff --git a/server/export.go b/server/export.go new file mode 100644 index 0000000000..3d0e49a126 --- /dev/null +++ b/server/export.go @@ -0,0 +1,28 @@ +package server + +import ( + "fmt" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/cosmos/cosmos-sdk/wire" +) + +// ExportCmd dumps app state to JSON +func ExportCmd(ctx *Context, cdc *wire.Codec, appExporter AppExporter) *cobra.Command { + return &cobra.Command{ + Use: "export", + Short: "Export state to JSON", + RunE: func(cmd *cobra.Command, args []string) error { + home := viper.GetString("home") + appState, err := appExporter(home, ctx.Logger) + if err != nil { + return errors.Errorf("Error exporting state: %v\n", err) + } + fmt.Println(string(appState)) + return nil + }, + } +} diff --git a/server/start.go b/server/start.go index c9a38abdff..23e38749be 100644 --- a/server/start.go +++ b/server/start.go @@ -6,14 +6,12 @@ import ( "github.com/spf13/viper" "github.com/tendermint/abci/server" - abci "github.com/tendermint/abci/types" tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/proxy" pvm "github.com/tendermint/tendermint/types/priv_validator" cmn "github.com/tendermint/tmlibs/common" - "github.com/tendermint/tmlibs/log" ) const ( @@ -21,10 +19,6 @@ const ( flagAddress = "address" ) -// AppCreator lets us lazily initialize app, using home dir -// and other flags (?) to start -type AppCreator func(string, log.Logger) (abci.Application, error) - // StartCmd runs the service passed in, either // stand-alone, or in-process with tendermint func StartCmd(ctx *Context, appCreator AppCreator) *cobra.Command { diff --git a/server/util.go b/server/util.go index ed91f30489..313d4fc5b2 100644 --- a/server/util.go +++ b/server/util.go @@ -68,7 +68,7 @@ func PersistentPreRunEFn(context *Context) func(*cobra.Command, []string) error func AddCommands( ctx *Context, cdc *wire.Codec, rootCmd *cobra.Command, appInit AppInit, - appCreator AppCreator) { + appCreator AppCreator, appExport AppExporter) { rootCmd.PersistentFlags().String("log_level", ctx.Config.LogLevel, "Log level") @@ -78,6 +78,8 @@ func AddCommands( UnsafeResetAllCmd(ctx), ShowNodeIDCmd(ctx), ShowValidatorCmd(ctx), + ExportCmd(ctx, cdc, appExport), + UnsafeResetAllCmd(ctx), version.VersionCmd, ) } diff --git a/types/account.go b/types/account.go index 91ad499795..7f4de12d30 100644 --- a/types/account.go +++ b/types/account.go @@ -48,6 +48,7 @@ type AccountMapper interface { NewAccountWithAddress(ctx Context, addr Address) Account GetAccount(ctx Context, addr Address) Account SetAccount(ctx Context, acc Account) + IterateAccounts(ctx Context, process func(Account) (stop bool)) } // AccountDecoder unmarshals account bytes diff --git a/types/initgenesis.go b/types/initgenesis.go deleted file mode 100644 index ff3c03cf4d..0000000000 --- a/types/initgenesis.go +++ /dev/null @@ -1,10 +0,0 @@ -package types - -import ( - "encoding/json" -) - -// Run only once on chain initialization, should write genesis state to store -// or throw an error if some required information was not provided, in which case -// the application will panic. -type InitGenesis func(ctx Context, data json.RawMessage) error diff --git a/x/auth/mapper.go b/x/auth/mapper.go index b815fada7a..f9e202c8a2 100644 --- a/x/auth/mapper.go +++ b/x/auth/mapper.go @@ -62,6 +62,23 @@ func (am accountMapper) SetAccount(ctx sdk.Context, acc sdk.Account) { store.Set(addr, bz) } +// Implements sdk.AccountMapper. +func (am accountMapper) IterateAccounts(ctx sdk.Context, process func(sdk.Account) (stop bool)) { + store := ctx.KVStore(am.key) + iter := store.Iterator(nil, nil) + for { + if !iter.Valid() { + return + } + val := iter.Value() + acc := am.decodeAccount(val) + if process(acc) { + return + } + iter.Next() + } +} + //---------------------------------------- // misc. diff --git a/x/stake/handler.go b/x/stake/handler.go index e397203857..2b55ba3b1e 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -55,6 +55,23 @@ func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) { for _, candidate := range data.Candidates { k.setCandidate(ctx, candidate) } + for _, bond := range data.Bonds { + k.setDelegatorBond(ctx, bond) + } +} + +// WriteGenesis - output genesis parameters +func WriteGenesis(ctx sdk.Context, k Keeper) GenesisState { + pool := k.GetPool(ctx) + params := k.GetParams(ctx) + candidates := k.GetCandidates(ctx, 32767) + bonds := k.getBonds(ctx, 32767) + return GenesisState{ + pool, + params, + candidates, + bonds, + } } //_____________________________________________________________________ diff --git a/x/stake/keeper.go b/x/stake/keeper.go index f1b4363140..e53d5ce09b 100644 --- a/x/stake/keeper.go +++ b/x/stake/keeper.go @@ -371,6 +371,30 @@ func (k Keeper) GetDelegatorBond(ctx sdk.Context, return bond, true } +// load all bonds +func (k Keeper) getBonds(ctx sdk.Context, maxRetrieve int16) (bonds []DelegatorBond) { + store := ctx.KVStore(k.storeKey) + iterator := store.Iterator(subspace(DelegatorBondKeyPrefix)) + + bonds = make([]DelegatorBond, maxRetrieve) + i := 0 + for ; ; i++ { + if !iterator.Valid() || i > int(maxRetrieve-1) { + iterator.Close() + break + } + bondBytes := iterator.Value() + var bond DelegatorBond + err := k.cdc.UnmarshalBinary(bondBytes, &bond) + if err != nil { + panic(err) + } + bonds[i] = bond + iterator.Next() + } + return bonds[:i] // trim +} + // load all bonds of a delegator func (k Keeper) GetDelegatorBonds(ctx sdk.Context, delegator sdk.Address, maxRetrieve int16) (bonds []DelegatorBond) { store := ctx.KVStore(k.storeKey) diff --git a/x/stake/keeper_test.go b/x/stake/keeper_test.go index e3edfbf9e2..3878b7bbb6 100644 --- a/x/stake/keeper_test.go +++ b/x/stake/keeper_test.go @@ -94,7 +94,7 @@ func TestCandidate(t *testing.T) { assert.False(t, found) } -// tests GetDelegatorBond, GetDelegatorBonds, SetDelegatorBond, removeDelegatorBond +// tests GetDelegatorBond, GetDelegatorBonds, SetDelegatorBond, removeDelegatorBond, GetBonds func TestBond(t *testing.T) { ctx, _, keeper := createTestInput(t, false, 0) @@ -172,6 +172,14 @@ func TestBond(t *testing.T) { assert.True(t, bondsEqual(bond2to1, resBonds[0])) assert.True(t, bondsEqual(bond2to2, resBonds[1])) assert.True(t, bondsEqual(bond2to3, resBonds[2])) + allBonds := keeper.getBonds(ctx, 1000) + require.Equal(t, 6, len(allBonds)) + assert.True(t, bondsEqual(bond1to1, allBonds[0])) + assert.True(t, bondsEqual(bond1to2, allBonds[1])) + assert.True(t, bondsEqual(bond1to3, allBonds[2])) + assert.True(t, bondsEqual(bond2to1, allBonds[3])) + assert.True(t, bondsEqual(bond2to2, allBonds[4])) + assert.True(t, bondsEqual(bond2to3, allBonds[5])) // delete a record keeper.removeDelegatorBond(ctx, bond2to3) diff --git a/x/stake/types.go b/x/stake/types.go index 634b51186b..6e15acd96d 100644 --- a/x/stake/types.go +++ b/x/stake/types.go @@ -9,9 +9,10 @@ import ( // GenesisState - all staking state that must be provided at genesis type GenesisState struct { - Pool Pool `json:"pool"` - Params Params `json:"params"` - Candidates []Candidate `json:"candidates"` + Pool Pool `json:"pool"` + Params Params `json:"params"` + Candidates []Candidate `json:"candidates"` + Bonds []DelegatorBond `json:"bonds"` } //_________________________________________________________________________