Skip to content

Commit

Permalink
fix(runtime/v2): return genesis val updates (#22729)
Browse files Browse the repository at this point in the history
  • Loading branch information
julienrbrt authored Dec 3, 2024
1 parent 371e05a commit 7d11742
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 23 deletions.
16 changes: 9 additions & 7 deletions runtime/v2/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,35 +128,37 @@ func (a *AppBuilder[T]) Build(opts ...AppBuilderOption[T]) (*App[T], error) {
}

// initGenesis returns the app initialization genesis for modules
func (a *AppBuilder[T]) initGenesis(ctx context.Context, src io.Reader, txHandler func(json.RawMessage) error) (store.WriterMap, error) {
func (a *AppBuilder[T]) initGenesis(ctx context.Context, src io.Reader, txHandler func(json.RawMessage) error) (store.WriterMap, []appmodule.ValidatorUpdate, error) {
// this implementation assumes that the state is a JSON object
bz, err := io.ReadAll(src)
if err != nil {
return nil, fmt.Errorf("failed to read import state: %w", err)
return nil, nil, fmt.Errorf("failed to read import state: %w", err)
}

var genesisJSON map[string]json.RawMessage
if err = json.Unmarshal(bz, &genesisJSON); err != nil {
return nil, err
return nil, nil, err
}

v, zeroState, err := a.app.db.StateLatest()
if err != nil {
return nil, fmt.Errorf("unable to get latest state: %w", err)
return nil, nil, fmt.Errorf("unable to get latest state: %w", err)
}
if v != 0 { // TODO: genesis state may be > 0, we need to set version on store
return nil, errors.New("cannot init genesis on non-zero state")
return nil, nil, errors.New("cannot init genesis on non-zero state")
}
genesisCtx := services.NewGenesisContext(a.branch(zeroState))
var valUpdates []appmodulev2.ValidatorUpdate
genesisState, err := genesisCtx.Mutate(ctx, func(ctx context.Context) error {
err = a.app.moduleManager.InitGenesisJSON(ctx, genesisJSON, txHandler)
valUpdates, err = a.app.moduleManager.InitGenesisJSON(ctx, genesisJSON, txHandler)
if err != nil {
return fmt.Errorf("failed to init genesis: %w", err)
}

return nil
})

return genesisState, err
return genesisState, valUpdates, err
}

// exportGenesis returns the app export genesis logic for modules
Expand Down
26 changes: 14 additions & 12 deletions runtime/v2/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,10 @@ func (m *MM[T]) InitGenesisJSON(
ctx context.Context,
genesisData map[string]json.RawMessage,
txHandler func(json.RawMessage) error,
) error {
) ([]appmodulev2.ValidatorUpdate, error) {
m.logger.Info("initializing blockchain state from genesis.json", "order", m.config.InitGenesis)
var seenValUpdates bool

var validatorUpdates []appmodulev2.ValidatorUpdate
for _, moduleName := range m.config.InitGenesis {
if genesisData[moduleName] == nil {
continue
Expand All @@ -158,38 +159,39 @@ func (m *MM[T]) InitGenesisJSON(
case appmodulev2.GenesisDecoder: // GenesisDecoder needs to supersede HasGenesis and HasABCIGenesis.
genTxs, err := module.DecodeGenesisJSON(genesisData[moduleName])
if err != nil {
return err
return nil, err
}
for _, jsonTx := range genTxs {
if err := txHandler(jsonTx); err != nil {
return fmt.Errorf("failed to handle genesis transaction: %w", err)
return nil, fmt.Errorf("failed to handle genesis transaction: %w", err)
}
}
case appmodulev2.HasGenesis:
m.logger.Debug("running initialization for module", "module", moduleName)
if err := module.InitGenesis(ctx, genesisData[moduleName]); err != nil {
return fmt.Errorf("init module %s: %w", moduleName, err)
return nil, fmt.Errorf("init module %s: %w", moduleName, err)
}
case appmodulev2.HasABCIGenesis:
m.logger.Debug("running initialization for module", "module", moduleName)
var err error
moduleValUpdates, err := module.InitGenesis(ctx, genesisData[moduleName])
if err != nil {
return err
return nil, err
}

// use these validator updates if provided, the module manager assumes
// only one module will update the validator set
if len(moduleValUpdates) > 0 {
if seenValUpdates {
return fmt.Errorf("validator InitGenesis updates already set by a previous module: current module %s", moduleName)
} else {
seenValUpdates = true
if len(validatorUpdates) > 0 {
return nil, fmt.Errorf("validator InitGenesis updates already set by a previous module: current module %s", moduleName)
}

validatorUpdates = append(validatorUpdates, moduleValUpdates...)
}
}

}
return nil

return validatorUpdates, nil
}

// ExportGenesisForModules performs export genesis functionality for modules
Expand Down
14 changes: 13 additions & 1 deletion server/v2/appmanager/appmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func (a appManager[T]) InitGenesis(
txDecoder transaction.Codec[T],
) (*server.BlockResponse, corestore.WriterMap, error) {
var genTxs []T
genesisState, err := a.initGenesis(
genesisState, valUpdates, err := a.initGenesis(
ctx,
bytes.NewBuffer(initGenesisJSON),
func(jsonTx json.RawMessage) error {
Expand All @@ -122,6 +122,7 @@ func (a appManager[T]) InitGenesis(
if err != nil {
return nil, nil, fmt.Errorf("failed to import genesis state: %w", err)
}

// run block
blockRequest.Txs = genTxs

Expand All @@ -141,6 +142,17 @@ func (a appManager[T]) InitGenesis(
return nil, nil, fmt.Errorf("failed to apply block zero state changes to genesis state: %w", err)
}

// override validator updates with the ones from the init genesis state
// this triggers only when x/staking or another module that returns validator updates in InitGenesis
// otherwise, genutil validator updates takes precedence (returned from executing the genesis txs (as it implements appmodule.GenesisDecoder) in the end block)
if len(valUpdates) > 0 && len(blockResponse.ValidatorUpdates) > 0 {
return nil, nil, errors.New("validator updates returned from InitGenesis and genesis transactions, only one can be used")
}

if len(valUpdates) > 0 {
blockResponse.ValidatorUpdates = valUpdates
}

return blockResponse, genesisState, err
}

Expand Down
3 changes: 2 additions & 1 deletion server/v2/appmanager/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"io"

appmodulev2 "cosmossdk.io/core/appmodule/v2"
"cosmossdk.io/core/store"
)

Expand All @@ -21,7 +22,7 @@ type (
ctx context.Context,
src io.Reader,
txHandler func(json.RawMessage) error,
) (store.WriterMap, error)
) (store.WriterMap, []appmodulev2.ValidatorUpdate, error)

// ExportGenesis is a function type that represents the export of the genesis state.
ExportGenesis func(ctx context.Context, version uint64) ([]byte, error)
Expand Down
4 changes: 2 additions & 2 deletions server/v2/cometbft/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -701,10 +701,10 @@ func setUpConsensus(t *testing.T, gasLimit uint64, mempool mempool.Mempool[mock.
},
mockStore,
s,
func(ctx context.Context, src io.Reader, txHandler func(json.RawMessage) error) (store.WriterMap, error) {
func(ctx context.Context, src io.Reader, txHandler func(json.RawMessage) error) (store.WriterMap, []appmodulev2.ValidatorUpdate, error) {
_, st, err := mockStore.StateLatest()
require.NoError(t, err)
return branch.DefaultNewWriterMap(st), nil
return branch.DefaultNewWriterMap(st), nil, nil
},
nil,
)
Expand Down
1 change: 1 addition & 0 deletions x/genutil/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ var (

_ appmodule.AppModule = AppModule{}
_ appmodulev2.GenesisDecoder = AppModule{}
_ appmodulev2.HasABCIGenesis = AppModule{}
)

// AppModule implements an application module for the genutil module.
Expand Down

0 comments on commit 7d11742

Please sign in to comment.