From 715dd3a996b4dc5dd14972d9a71f0ac97f4ef3af Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Tue, 3 Dec 2024 13:51:23 +0100 Subject: [PATCH 1/6] fix(runtime/v2): return genesis val updates --- runtime/v2/manager.go | 28 +++++++++++++++------------- tests/systemtests/Makefile | 2 +- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/runtime/v2/manager.go b/runtime/v2/manager.go index 9e99a2b08c4f..ff2edb343fce 100644 --- a/runtime/v2/manager.go +++ b/runtime/v2/manager.go @@ -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 @@ -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 { + if len(moduleValUpdates) > 0 { + return nil, fmt.Errorf("validator InitGenesis updates already set by a previous module: current module %s", moduleName) } + + validatorUpdates = append(validatorUpdates, validatorUpdates...) } } - } - return nil + + return validatorUpdates, nil } // ExportGenesisForModules performs export genesis functionality for modules diff --git a/tests/systemtests/Makefile b/tests/systemtests/Makefile index ad00d32310eb..c6d552e2bfad 100644 --- a/tests/systemtests/Makefile +++ b/tests/systemtests/Makefile @@ -5,7 +5,7 @@ WAIT_TIME ?= 45s all: test format test: - go test -mod=readonly -failfast -timeout=15m -tags='system_test' ./... --wait-time=$(WAIT_TIME) --verbose $(if $(findstring v2,$(COSMOS_BUILD_OPTIONS)),--binary=simdv2) + go test -mod=readonly -failfast -timeout=15m -tags='system_test' -run=TestChainExportImport ./... --wait-time=$(WAIT_TIME) --verbose $(if $(findstring v2,$(COSMOS_BUILD_OPTIONS)),--binary=simdv2) format: find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs gofumpt -w From d82ab2347f03d004cbe988360ce41884b0fd0bfb Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Tue, 3 Dec 2024 13:55:47 +0100 Subject: [PATCH 2/6] updates --- runtime/v2/builder.go | 16 +++++++++------- server/v2/appmanager/appmanager.go | 5 ++++- server/v2/appmanager/genesis.go | 3 ++- server/v2/cometbft/abci_test.go | 4 ++-- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/runtime/v2/builder.go b/runtime/v2/builder.go index e6e8cb7c4ea5..eff701590a61 100644 --- a/runtime/v2/builder.go +++ b/runtime/v2/builder.go @@ -128,34 +128,36 @@ 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 diff --git a/server/v2/appmanager/appmanager.go b/server/v2/appmanager/appmanager.go index 597d1c8f4110..442577ef031b 100644 --- a/server/v2/appmanager/appmanager.go +++ b/server/v2/appmanager/appmanager.go @@ -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 { @@ -125,6 +125,9 @@ func (a appManager[T]) InitGenesis( // run block blockRequest.Txs = genTxs + // do something with valUpdates + _ = valUpdates + blockResponse, blockZeroState, err := a.stf.DeliverBlock(ctx, blockRequest, genesisState) if err != nil { return blockResponse, nil, fmt.Errorf("failed to deliver block %d: %w", blockRequest.Height, err) diff --git a/server/v2/appmanager/genesis.go b/server/v2/appmanager/genesis.go index 347d0f30e07b..40d95e094f47 100644 --- a/server/v2/appmanager/genesis.go +++ b/server/v2/appmanager/genesis.go @@ -5,6 +5,7 @@ import ( "encoding/json" "io" + appmodulev2 "cosmossdk.io/core/appmodule/v2" "cosmossdk.io/core/store" ) @@ -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) diff --git a/server/v2/cometbft/abci_test.go b/server/v2/cometbft/abci_test.go index ab1fdc722879..3f4566d3a42b 100644 --- a/server/v2/cometbft/abci_test.go +++ b/server/v2/cometbft/abci_test.go @@ -702,10 +702,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, ) From 8ee8b481b8fe446b1d1dcdc86a9dc15bab9a5170 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Tue, 3 Dec 2024 14:54:15 +0100 Subject: [PATCH 3/6] fixes --- runtime/v2/manager.go | 4 ++-- server/v2/appmanager/appmanager.go | 15 ++++++++++++--- x/genutil/module.go | 1 + 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/runtime/v2/manager.go b/runtime/v2/manager.go index ff2edb343fce..f7e84e1cbee6 100644 --- a/runtime/v2/manager.go +++ b/runtime/v2/manager.go @@ -181,8 +181,8 @@ func (m *MM[T]) InitGenesisJSON( // use these validator updates if provided, the module manager assumes // only one module will update the validator set - if len(validatorUpdates) > 0 { - if len(moduleValUpdates) > 0 { + if len(moduleValUpdates) > 0 { + if len(validatorUpdates) > 0 { return nil, fmt.Errorf("validator InitGenesis updates already set by a previous module: current module %s", moduleName) } diff --git a/server/v2/appmanager/appmanager.go b/server/v2/appmanager/appmanager.go index 442577ef031b..abac753a8ace 100644 --- a/server/v2/appmanager/appmanager.go +++ b/server/v2/appmanager/appmanager.go @@ -122,12 +122,10 @@ 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 - // do something with valUpdates - _ = valUpdates - blockResponse, blockZeroState, err := a.stf.DeliverBlock(ctx, blockRequest, genesisState) if err != nil { return blockResponse, nil, fmt.Errorf("failed to deliver block %d: %w", blockRequest.Height, err) @@ -144,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 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 { + blockResponse.ValidatorUpdates = valUpdates + } + + 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") + } + return blockResponse, genesisState, err } diff --git a/x/genutil/module.go b/x/genutil/module.go index ba33eab26b1f..18ebc6a06c6c 100644 --- a/x/genutil/module.go +++ b/x/genutil/module.go @@ -19,6 +19,7 @@ var ( _ appmodule.AppModule = AppModule{} _ appmodulev2.GenesisDecoder = AppModule{} + _ appmodulev2.HasABCIGenesis = AppModule{} ) // AppModule implements an application module for the genutil module. From b85430eb5046e83e7ff3418ffc156ff537fcbb9f Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Tue, 3 Dec 2024 15:02:22 +0100 Subject: [PATCH 4/6] revert --- tests/systemtests/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/systemtests/Makefile b/tests/systemtests/Makefile index c6d552e2bfad..ad00d32310eb 100644 --- a/tests/systemtests/Makefile +++ b/tests/systemtests/Makefile @@ -5,7 +5,7 @@ WAIT_TIME ?= 45s all: test format test: - go test -mod=readonly -failfast -timeout=15m -tags='system_test' -run=TestChainExportImport ./... --wait-time=$(WAIT_TIME) --verbose $(if $(findstring v2,$(COSMOS_BUILD_OPTIONS)),--binary=simdv2) + go test -mod=readonly -failfast -timeout=15m -tags='system_test' ./... --wait-time=$(WAIT_TIME) --verbose $(if $(findstring v2,$(COSMOS_BUILD_OPTIONS)),--binary=simdv2) format: find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs gofumpt -w From 4f611a22753832964c43576af6bf9ec28e7342b1 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Tue, 3 Dec 2024 15:07:48 +0100 Subject: [PATCH 5/6] readbility --- server/v2/appmanager/appmanager.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/v2/appmanager/appmanager.go b/server/v2/appmanager/appmanager.go index abac753a8ace..b31bd33db977 100644 --- a/server/v2/appmanager/appmanager.go +++ b/server/v2/appmanager/appmanager.go @@ -142,17 +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 genesis state + // 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 { - blockResponse.ValidatorUpdates = valUpdates - } - 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 } From 1489945991b61c8d0baa66646e2249d12b527dcf Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Tue, 3 Dec 2024 15:58:25 +0100 Subject: [PATCH 6/6] fix --- runtime/v2/manager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/v2/manager.go b/runtime/v2/manager.go index f7e84e1cbee6..18b6587e86a4 100644 --- a/runtime/v2/manager.go +++ b/runtime/v2/manager.go @@ -186,7 +186,7 @@ func (m *MM[T]) InitGenesisJSON( return nil, fmt.Errorf("validator InitGenesis updates already set by a previous module: current module %s", moduleName) } - validatorUpdates = append(validatorUpdates, validatorUpdates...) + validatorUpdates = append(validatorUpdates, moduleValUpdates...) } } }