Merge pull request #714 from cosmos/cwgoes/per-module-genesis
Per-module genesis initialization
This commit is contained in:
commit
144ea05f54
|
@ -1,6 +1,7 @@
|
|||
package baseapp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
|
||||
|
@ -233,6 +234,19 @@ 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().InitGenesis(app.deliverState.ctx, *genesisState)
|
||||
if err != nil {
|
||||
// TODO Return something intelligent
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// NOTE: we don't commit, but BeginBlock for block 1
|
||||
// starts from this deliverState
|
||||
|
||||
|
|
|
@ -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("{}")}) // must have valid JSON genesis file, even if empty
|
||||
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)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package baseapp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
@ -8,14 +10,16 @@ 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)
|
||||
InitGenesis(ctx sdk.Context, data map[string]json.RawMessage) 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,11 +38,11 @@ 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
|
||||
}
|
||||
|
@ -53,3 +57,20 @@ func (rtr *router) Route(path string) (h sdk.Handler) {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitGenesis - call `InitGenesis`, where specified, for all routes
|
||||
// Return the first error if any, otherwise nil
|
||||
func (rtr *router) InitGenesis(ctx sdk.Context, data map[string]json.RawMessage) error {
|
||||
for _, route := range rtr.routes {
|
||||
if route.i != nil {
|
||||
encoded, found := data[route.r]
|
||||
if !found {
|
||||
return sdk.ErrGenesisParse(fmt.Sprintf("Expected module genesis information for module %s but it was not present", route.r))
|
||||
}
|
||||
if err := route.i(ctx, encoded); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
|
||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
|
||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/x/cool"
|
||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/x/sketchy"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -61,10 +62,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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
@ -32,7 +32,7 @@ const (
|
|||
CodeInsufficientCoins CodeType = 10
|
||||
CodeInvalidCoins CodeType = 11
|
||||
|
||||
CodeGenesisParse CodeType = 0xdead // TODO: remove ?
|
||||
CodeGenesisParse CodeType = 0xdead // TODO: remove ? // why remove?
|
||||
)
|
||||
|
||||
// NOTE: Don't stringer this, we'll put better messages in later.
|
||||
|
|
|
@ -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
|
|
@ -16,7 +16,7 @@ import (
|
|||
type commander struct {
|
||||
storeName string
|
||||
cdc *wire.Codec
|
||||
decoder sdk.AccountDecoder
|
||||
decoder sdk.AccountDecoder
|
||||
}
|
||||
|
||||
func QueryAccountRequestHandler(storeName string, cdc *wire.Codec, decoder sdk.AccountDecoder) func(http.ResponseWriter, *http.Request) {
|
||||
|
|
Loading…
Reference in New Issue