Initial per-module genesis initialization

This commit is contained in:
Christopher Goes 2018-03-27 20:00:27 +02:00
parent d0eb05b162
commit 5b642062a7
11 changed files with 120 additions and 27 deletions

View File

@ -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

View File

@ -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)

View File

@ -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
}

View File

@ -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 {

View File

@ -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)

View File

@ -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)

View File

@ -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
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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

10
types/initgenesis.go Normal file
View File

@ -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