Merge pull request #714 from cosmos/cwgoes/per-module-genesis

Per-module genesis initialization
This commit is contained in:
Rigel 2018-03-28 18:29:09 +02:00 committed by GitHub
commit 144ea05f54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 106 additions and 27 deletions

View File

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

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("{}")}) // 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)

View File

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

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

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

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

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

View File

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