diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 3dad0483a3..73b2de49d8 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -1,6 +1,7 @@ package baseapp import ( + "encoding/json" "fmt" "runtime/debug" @@ -233,6 +234,30 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC app.setDeliverState(abci.Header{}) app.initChainer(app.deliverState.ctx, req) // no error + // Initialize module genesis state + genesisState := new(map[string]json.RawMessage) + err := json.Unmarshal(req.AppStateBytes, genesisState) + if err != nil { + // TODO Return something intelligent + panic(err) + } + err = app.Router().ForEach(func(r string, _ sdk.Handler, i sdk.InitGenesis) error { + if i != nil { + encoded, exists := (*genesisState)[r] + if !exists { + // TODO should this be a Cosmos SDK standard error? + return errors.New(fmt.Sprintf("Expected module genesis information for module %s but it was not present", r)) + } else { + return i(app.deliverState.ctx, encoded) + } + } + return nil + }) + if err != nil { + // TODO Return something intelligent + panic(err) + } + // NOTE: we don't commit, but BeginBlock for block 1 // starts from this deliverState diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index de9a0253c5..a90c39d541 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -180,7 +180,7 @@ func TestInitChainer(t *testing.T) { // set initChainer and try again - should see the value app.SetInitChainer(initChainer) - app.InitChain(abci.RequestInitChain{}) + app.InitChain(abci.RequestInitChain{AppStateBytes: []byte("{}")}) app.Commit() res = app.Query(query) assert.Equal(t, value, res.Value) @@ -246,7 +246,7 @@ func TestDeliverTx(t *testing.T) { counter += 1 return sdk.Result{} - }) + }, nil) tx := testUpdatePowerTx{} // doesn't matter header := abci.Header{AppHash: []byte("apphash")} @@ -281,7 +281,7 @@ func TestQuery(t *testing.T) { store := ctx.KVStore(capKey) store.Set(key, value) return sdk.Result{} - }) + }, nil) query := abci.RequestQuery{ Path: "/main/key", @@ -346,7 +346,7 @@ func TestValidatorChange(t *testing.T) { app.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) sdk.Result { // TODO return sdk.Result{} - }) + }, nil) // Load latest state, which should be empty. err := app.LoadLatestVersion(capKey) diff --git a/baseapp/router.go b/baseapp/router.go index a9d7547164..5012d48cf4 100644 --- a/baseapp/router.go +++ b/baseapp/router.go @@ -8,14 +8,17 @@ import ( // Router provides handlers for each transaction type. type Router interface { - AddRoute(r string, h sdk.Handler) (rtr Router) + AddRoute(r string, h sdk.Handler, i sdk.InitGenesis) (rtr Router) Route(path string) (h sdk.Handler) + RouteGenesis(path string) (i sdk.InitGenesis) + ForEach(func(r string, h sdk.Handler, i sdk.InitGenesis) error) error } -// map a transaction type to a handler +// map a transaction type to a handler and an initgenesis function type route struct { r string h sdk.Handler + i sdk.InitGenesis } type router struct { @@ -34,22 +37,44 @@ func NewRouter() *router { var isAlpha = regexp.MustCompile(`^[a-zA-Z]+$`).MatchString // AddRoute - TODO add description -func (rtr *router) AddRoute(r string, h sdk.Handler) Router { +func (rtr *router) AddRoute(r string, h sdk.Handler, i sdk.InitGenesis) Router { if !isAlpha(r) { panic("route expressions can only contain alphanumeric characters") } - rtr.routes = append(rtr.routes, route{r, h}) + rtr.routes = append(rtr.routes, route{r, h, i}) return rtr } -// Route - TODO add description // TODO handle expressive matches. +func matchRoute(path string, route string) bool { + return path == route +} + +// Route - TODO add description func (rtr *router) Route(path string) (h sdk.Handler) { for _, route := range rtr.routes { - if route.r == path { + if matchRoute(path, route.r) { return route.h } } return nil } + +func (rtr *router) RouteGenesis(path string) (i sdk.InitGenesis) { + for _, route := range rtr.routes { + if matchRoute(path, route.r) { + return route.i + } + } + return nil +} + +func (rtr *router) ForEach(f func(string, sdk.Handler, sdk.InitGenesis) error) error { + for _, route := range rtr.routes { + if err := f(route.r, route.h, route.i); err != nil { + return err + } + } + return nil +} diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 285cf4d6d7..1e7104dae2 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -324,14 +324,17 @@ func startTMAndLCD() (*nm.Node, net.Listener, error) { } coins := sdk.Coins{{coinDenom, coinAmount}} - appState := btypes.GenesisState{ - Accounts: []*btypes.GenesisAccount{ + appState := map[string]interface{}{ + "accounts": []*btypes.GenesisAccount{ { Name: "tester", Address: pubKey.Address(), Coins: coins, }, }, + "cool": map[string]string{ + "trend": "ice-cold", + }, } stateBytes, err := json.Marshal(appState) if err != nil { diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index e5235bd667..a861e3bcf7 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -61,10 +61,11 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp { ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore) stakeKeeper := staking.NewKeeper(app.capKeyStakingStore, coinKeeper) app.Router(). - AddRoute("bank", bank.NewHandler(coinKeeper)). - AddRoute("cool", cool.NewHandler(coolKeeper)). - AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)). - AddRoute("staking", staking.NewHandler(stakeKeeper)) + AddRoute("bank", bank.NewHandler(coinKeeper), nil). + AddRoute("cool", cool.NewHandler(coolKeeper), coolKeeper.InitGenesis). + AddRoute("sketchy", sketchy.NewHandler(), nil). + AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper), nil). + AddRoute("staking", staking.NewHandler(stakeKeeper), nil) // initialize BaseApp app.SetTxDecoder(app.txDecoder) diff --git a/examples/basecoin/app/app_test.go b/examples/basecoin/app/app_test.go index b1a1c4cade..0d916b054e 100644 --- a/examples/basecoin/app/app_test.go +++ b/examples/basecoin/app/app_test.go @@ -127,10 +127,13 @@ func TestGenesis(t *testing.T) { } acc := &types.AppAccount{baseAcc, "foobart"} - genesisState := types.GenesisState{ - Accounts: []*types.GenesisAccount{ + genesisState := map[string]interface{}{ + "accounts": []*types.GenesisAccount{ types.NewGenesisAccount(acc), }, + "cool": map[string]string{ + "trend": "ice-cold", + }, } stateBytes, err := json.MarshalIndent(genesisState, "", "\t") @@ -165,10 +168,13 @@ func TestSendMsgWithAccounts(t *testing.T) { acc1 := &types.AppAccount{baseAcc, "foobart"} // Construct genesis state - genesisState := types.GenesisState{ - Accounts: []*types.GenesisAccount{ + genesisState := map[string]interface{}{ + "accounts": []*types.GenesisAccount{ types.NewGenesisAccount(acc1), }, + "cool": map[string]string{ + "trend": "ice-cold", + }, } stateBytes, err := json.MarshalIndent(genesisState, "", "\t") require.Nil(t, err) @@ -237,10 +243,13 @@ func TestQuizMsg(t *testing.T) { acc1 := &types.AppAccount{baseAcc, "foobart"} // Construct genesis state - genesisState := types.GenesisState{ - Accounts: []*types.GenesisAccount{ + genesisState := map[string]interface{}{ + "accounts": []*types.GenesisAccount{ types.NewGenesisAccount(acc1), }, + "cool": map[string]string{ + "trend": "ice-cold", + }, } stateBytes, err := json.MarshalIndent(genesisState, "", "\t") require.Nil(t, err) @@ -284,10 +293,13 @@ func TestHandler(t *testing.T) { Coins: coins, } acc1 := &types.AppAccount{baseAcc, "foobart"} - genesisState := types.GenesisState{ - Accounts: []*types.GenesisAccount{ + genesisState := map[string]interface{}{ + "accounts": []*types.GenesisAccount{ types.NewGenesisAccount(acc1), }, + "cool": map[string]string{ + "trend": "ice-cold", + }, } stateBytes, err := json.MarshalIndent(genesisState, "", "\t") require.Nil(t, err) diff --git a/examples/basecoin/x/cool/keeper.go b/examples/basecoin/x/cool/keeper.go index e1fbe8f587..1bf342fdc2 100644 --- a/examples/basecoin/x/cool/keeper.go +++ b/examples/basecoin/x/cool/keeper.go @@ -1,10 +1,17 @@ package cool import ( + "encoding/json" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/bank" ) +// Cool genesis state, containing the genesis trend +type GenesisState struct { + trend string +} + // Keeper - handlers sets/gets of custom variables for your module type Keeper struct { ck bank.CoinKeeper @@ -40,3 +47,13 @@ func (k Keeper) CheckTrend(ctx sdk.Context, guessedTrend string) bool { } return false } + +// InitGenesis - store the genesis trend +func (k Keeper) InitGenesis(ctx sdk.Context, data json.RawMessage) error { + var state GenesisState + if err := json.Unmarshal(data, &state); err != nil { + return err + } + k.setTrend(ctx, state.trend) + return nil +} diff --git a/examples/kvstore/main.go b/examples/kvstore/main.go index 5ffe590f1a..9bcabb306a 100644 --- a/examples/kvstore/main.go +++ b/examples/kvstore/main.go @@ -36,7 +36,7 @@ func main() { baseApp.SetTxDecoder(decodeTx) // Set a handler Route. - baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore)) + baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore), nil) // Load latest version. if err := baseApp.LoadLatestVersion(capKeyMainStore); err != nil { diff --git a/mock/app.go b/mock/app.go index 81518599eb..c7c6432902 100644 --- a/mock/app.go +++ b/mock/app.go @@ -38,7 +38,7 @@ func NewApp(rootDir string, logger log.Logger) (abci.Application, error) { baseApp.SetInitChainer(InitChainer(capKeyMainStore)) // Set a handler Route. - baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore)) + baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore), nil) // Load latest version. if err := baseApp.LoadLatestVersion(capKeyMainStore); err != nil { diff --git a/types/errors.go b/types/errors.go index fab28c4449..ca16584ecc 100644 --- a/types/errors.go +++ b/types/errors.go @@ -21,7 +21,7 @@ func (code CodeType) IsOK() bool { const ( CodeOK CodeType = 0 CodeInternal CodeType = 1 - CodeTxDecode CodeType = 2 + CodeTxDecode CodeType = 2 CodeInvalidSequence CodeType = 3 CodeUnauthorized CodeType = 4 CodeInsufficientFunds CodeType = 5 diff --git a/types/initgenesis.go b/types/initgenesis.go new file mode 100644 index 0000000000..8b63568301 --- /dev/null +++ b/types/initgenesis.go @@ -0,0 +1,10 @@ +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