Skip to content

Commit 232a23a

Browse files
committed
refactor(runtime/v2): simplify app manager
1 parent f01baf3 commit 232a23a

File tree

13 files changed

+138
-155
lines changed

13 files changed

+138
-155
lines changed

runtime/v2/app.go

+82-20
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
package runtime
22

33
import (
4+
"context"
45
"encoding/json"
6+
"errors"
7+
"fmt"
8+
"io"
59

610
runtimev2 "cosmossdk.io/api/cosmos/app/runtime/v2"
711
appmodulev2 "cosmossdk.io/core/appmodule/v2"
812
"cosmossdk.io/core/registry"
13+
"cosmossdk.io/core/store"
914
"cosmossdk.io/core/transaction"
1015
"cosmossdk.io/log"
16+
"cosmossdk.io/runtime/v2/services"
1117
"cosmossdk.io/schema/decoding"
1218
"cosmossdk.io/server/v2/appmanager"
1319
"cosmossdk.io/server/v2/stf"
@@ -23,26 +29,80 @@ import (
2329
// done declaratively with an app config and the rest of it is done the old way.
2430
// See simapp/app_v2.go for an example of this setup.
2531
type App[T transaction.Tx] struct {
26-
*appmanager.AppManager[T]
32+
// app configuration
33+
logger log.Logger
34+
config *runtimev2.Module
2735

28-
// app manager dependencies
36+
// state
2937
stf *stf.STF[T]
3038
msgRouterBuilder *stf.MsgRouterBuilder
3139
queryRouterBuilder *stf.MsgRouterBuilder
40+
appm *appmanager.AppManager[T]
41+
branch func(state store.ReaderMap) store.WriterMap
3242
db Store
43+
storeLoader StoreLoader
3344

34-
// app configuration
35-
logger log.Logger
36-
config *runtimev2.Module
37-
45+
// modules
3846
interfaceRegistrar registry.InterfaceRegistrar
3947
amino registry.AminoRegistrar
4048
moduleManager *MM[T]
49+
queryHandlers map[string]appmodulev2.Handler // queryHandlers defines the query handlers
50+
}
51+
52+
// initGenesis initializes the genesis state of the application.
53+
func (a *App[T]) initGenesis(ctx context.Context, src io.Reader, txHandler func(json.RawMessage) error) (store.WriterMap, error) {
54+
// this implementation assumes that the state is a JSON object
55+
bz, err := io.ReadAll(src)
56+
if err != nil {
57+
return nil, fmt.Errorf("failed to read import state: %w", err)
58+
}
59+
var genesisJSON map[string]json.RawMessage
60+
if err = json.Unmarshal(bz, &genesisJSON); err != nil {
61+
return nil, err
62+
}
63+
64+
v, zeroState, err := a.db.StateLatest()
65+
if err != nil {
66+
return nil, fmt.Errorf("unable to get latest state: %w", err)
67+
}
68+
if v != 0 { // TODO: genesis state may be > 0, we need to set version on store
69+
return nil, errors.New("cannot init genesis on non-zero state")
70+
}
71+
genesisCtx := services.NewGenesisContext(a.branch(zeroState))
72+
genesisState, err := genesisCtx.Mutate(ctx, func(ctx context.Context) error {
73+
err = a.moduleManager.InitGenesisJSON(ctx, genesisJSON, txHandler)
74+
if err != nil {
75+
return fmt.Errorf("failed to init genesis: %w", err)
76+
}
77+
return nil
78+
})
79+
80+
return genesisState, err
81+
}
4182

42-
// QueryHandlers defines the query handlers
43-
QueryHandlers map[string]appmodulev2.Handler
83+
// exportGenesis exports the genesis state of the application.
84+
func (a *App[T]) exportGenesis(ctx context.Context, version uint64) ([]byte, error) {
85+
state, err := a.db.StateAt(version)
86+
if err != nil {
87+
return nil, fmt.Errorf("unable to get state at given version: %w", err)
88+
}
89+
90+
genesisJson, err := a.moduleManager.ExportGenesisForModules(
91+
ctx,
92+
func() store.WriterMap {
93+
return a.branch(state)
94+
},
95+
)
96+
if err != nil {
97+
return nil, fmt.Errorf("failed to export genesis: %w", err)
98+
}
99+
100+
bz, err := json.Marshal(genesisJson)
101+
if err != nil {
102+
return nil, fmt.Errorf("failed to marshal genesis: %w", err)
103+
}
44104

45-
storeLoader StoreLoader
105+
return bz, nil
46106
}
47107

48108
// Name returns the app name.
@@ -85,24 +145,26 @@ func (a *App[T]) LoadLatestHeight() (uint64, error) {
85145
return a.db.GetLatestVersion()
86146
}
87147

88-
// Close is called in start cmd to gracefully cleanup resources.
89-
func (a *App[T]) Close() error {
90-
return nil
148+
// AppManager returns the app's appamanger
149+
func (a *App[T]) AppManager() *appmanager.AppManager[T] {
150+
return a.appm
91151
}
92152

93-
func (a *App[T]) GetAppManager() *appmanager.AppManager[T] {
94-
return a.AppManager
153+
// GetQueryHandlers returns the query handlers.
154+
func (a *App[T]) QueryHandlers() map[string]appmodulev2.Handler {
155+
return a.queryHandlers
95156
}
96157

97-
func (a *App[T]) GetQueryHandlers() map[string]appmodulev2.Handler {
98-
return a.QueryHandlers
99-
}
100-
101-
// GetSchemaDecoderResolver returns the module schema resolver.
102-
func (a *App[T]) GetSchemaDecoderResolver() decoding.DecoderResolver {
158+
// SchemaDecoderResolver returns the module schema resolver.
159+
func (a *App[T]) SchemaDecoderResolver() decoding.DecoderResolver {
103160
moduleSet := map[string]any{}
104161
for moduleName, module := range a.moduleManager.Modules() {
105162
moduleSet[moduleName] = module
106163
}
107164
return decoding.ModuleSetDecoderResolver(moduleSet)
108165
}
166+
167+
// Close is called in start cmd to gracefully cleanup resources.
168+
func (a *App[T]) Close() error {
169+
return nil
170+
}

runtime/v2/builder.go

+16-74
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,12 @@ package runtime
33
import (
44
"context"
55
"encoding/json"
6-
"errors"
76
"fmt"
8-
"io"
97

108
"cosmossdk.io/core/appmodule"
119
appmodulev2 "cosmossdk.io/core/appmodule/v2"
1210
"cosmossdk.io/core/store"
1311
"cosmossdk.io/core/transaction"
14-
"cosmossdk.io/runtime/v2/services"
1512
"cosmossdk.io/server/v2/appmanager"
1613
"cosmossdk.io/server/v2/stf"
1714
"cosmossdk.io/server/v2/stf/branch"
@@ -26,7 +23,6 @@ type AppBuilder[T transaction.Tx] struct {
2623
storeBuilder root.Builder
2724

2825
// the following fields are used to overwrite the default
29-
branch func(state store.ReaderMap) store.WriterMap
3026
txValidator func(ctx context.Context, tx T) error
3127
postTxExec func(ctx context.Context, tx T, success bool) error
3228
}
@@ -71,8 +67,8 @@ func (a *AppBuilder[T]) Build(opts ...AppBuilderOption[T]) (*App[T], error) {
7167
}
7268

7369
// default branch
74-
if a.branch == nil {
75-
a.branch = branch.DefaultNewWriterMap
70+
if a.app.branch == nil {
71+
a.app.branch = branch.DefaultNewWriterMap
7672
}
7773

7874
// default tx validator
@@ -108,82 +104,28 @@ func (a *AppBuilder[T]) Build(opts ...AppBuilderOption[T]) (*App[T], error) {
108104
a.txValidator,
109105
valUpdate,
110106
a.postTxExec,
111-
a.branch,
107+
a.app.branch,
112108
)
113109
if err != nil {
114110
return nil, fmt.Errorf("failed to create STF: %w", err)
115111
}
116112
a.app.stf = stf
117113

118-
appManagerBuilder := appmanager.Builder[T]{
119-
STF: a.app.stf,
120-
DB: a.app.db,
121-
ValidateTxGasLimit: a.app.config.GasConfig.ValidateTxGasLimit,
122-
QueryGasLimit: a.app.config.GasConfig.QueryGasLimit,
123-
SimulationGasLimit: a.app.config.GasConfig.SimulationGasLimit,
124-
InitGenesis: func(
125-
ctx context.Context,
126-
src io.Reader,
127-
txHandler func(json.RawMessage) error,
128-
) (store.WriterMap, error) {
129-
// this implementation assumes that the state is a JSON object
130-
bz, err := io.ReadAll(src)
131-
if err != nil {
132-
return nil, fmt.Errorf("failed to read import state: %w", err)
133-
}
134-
var genesisJSON map[string]json.RawMessage
135-
if err = json.Unmarshal(bz, &genesisJSON); err != nil {
136-
return nil, err
137-
}
138-
139-
v, zeroState, err := a.app.db.StateLatest()
140-
if err != nil {
141-
return nil, fmt.Errorf("unable to get latest state: %w", err)
142-
}
143-
if v != 0 { // TODO: genesis state may be > 0, we need to set version on store
144-
return nil, errors.New("cannot init genesis on non-zero state")
145-
}
146-
genesisCtx := services.NewGenesisContext(a.branch(zeroState))
147-
genesisState, err := genesisCtx.Mutate(ctx, func(ctx context.Context) error {
148-
err = a.app.moduleManager.InitGenesisJSON(ctx, genesisJSON, txHandler)
149-
if err != nil {
150-
return fmt.Errorf("failed to init genesis: %w", err)
151-
}
152-
return nil
153-
})
154-
155-
return genesisState, err
114+
appManager, err := appmanager.NewAppManager[T](
115+
appmanager.Config{
116+
ValidateTxGasLimit: a.app.config.GasConfig.ValidateTxGasLimit,
117+
QueryGasLimit: a.app.config.GasConfig.QueryGasLimit,
118+
SimulationGasLimit: a.app.config.GasConfig.SimulationGasLimit,
156119
},
157-
ExportGenesis: func(ctx context.Context, version uint64) ([]byte, error) {
158-
state, err := a.app.db.StateAt(version)
159-
if err != nil {
160-
return nil, fmt.Errorf("unable to get state at given version: %w", err)
161-
}
162-
163-
genesisJson, err := a.app.moduleManager.ExportGenesisForModules(
164-
ctx,
165-
func() store.WriterMap {
166-
return a.branch(state)
167-
},
168-
)
169-
if err != nil {
170-
return nil, fmt.Errorf("failed to export genesis: %w", err)
171-
}
172-
173-
bz, err := json.Marshal(genesisJson)
174-
if err != nil {
175-
return nil, fmt.Errorf("failed to marshal genesis: %w", err)
176-
}
177-
178-
return bz, nil
179-
},
180-
}
181-
182-
appManager, err := appManagerBuilder.Build()
120+
a.app.db,
121+
a.app.stf,
122+
a.app.initGenesis,
123+
a.app.exportGenesis,
124+
)
183125
if err != nil {
184-
return nil, fmt.Errorf("failed to build app manager: %w", err)
126+
return nil, fmt.Errorf("failed to create AppManager: %w", err)
185127
}
186-
a.app.AppManager = appManager
128+
a.app.appm = appManager
187129

188130
return a.app, nil
189131
}
@@ -194,7 +136,7 @@ type AppBuilderOption[T transaction.Tx] func(*AppBuilder[T])
194136
// AppBuilderWithBranch sets a custom branch implementation for the app.
195137
func AppBuilderWithBranch[T transaction.Tx](branch func(state store.ReaderMap) store.WriterMap) AppBuilderOption[T] {
196138
return func(a *AppBuilder[T]) {
197-
a.branch = branch
139+
a.app.branch = branch
198140
}
199141
}
200142

runtime/v2/manager.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,7 @@ func registerServices[T transaction.Tx](s appmodulev2.AppModule, app *App[T], re
636636

637637
// merge maps
638638
for path, decoder := range c.queryHandlers {
639-
app.QueryHandlers[path] = decoder
639+
app.queryHandlers[path] = decoder
640640
}
641641
}
642642

@@ -655,7 +655,7 @@ func registerServices[T transaction.Tx](s appmodulev2.AppModule, app *App[T], re
655655
module.RegisterQueryHandlers(&wrapper)
656656

657657
for path, handler := range wrapper.handlers {
658-
app.QueryHandlers[path] = handler
658+
app.queryHandlers[path] = handler
659659
}
660660
}
661661

runtime/v2/module.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ func ProvideAppBuilder[T transaction.Tx](
131131
amino: amino,
132132
msgRouterBuilder: msgRouterBuilder,
133133
queryRouterBuilder: stf.NewMsgRouterBuilder(), // TODO dedicated query router
134-
QueryHandlers: map[string]appmodulev2.Handler{},
134+
queryHandlers: map[string]appmodulev2.Handler{},
135135
storeLoader: DefaultStoreLoader,
136136
}
137137
appBuilder := &AppBuilder[T]{app: app, storeBuilder: storeBuilder}

server/v2/api/grpc/server.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,14 @@ func (s *Server[T]) Init(appI serverv2.AppI[T], cfg map[string]any, logger log.L
5757
return fmt.Errorf("failed to unmarshal config: %w", err)
5858
}
5959
}
60-
methodsMap := appI.GetQueryHandlers()
60+
methodsMap := appI.QueryHandlers()
6161

6262
grpcSrv := grpc.NewServer(
6363
grpc.ForceServerCodec(newProtoCodec(appI.InterfaceRegistry()).GRPCCodec()),
6464
grpc.MaxSendMsgSize(serverCfg.MaxSendMsgSize),
6565
grpc.MaxRecvMsgSize(serverCfg.MaxRecvMsgSize),
6666
grpc.UnknownServiceHandler(
67-
makeUnknownServiceHandler(methodsMap, appI.GetAppManager()),
67+
makeUnknownServiceHandler(methodsMap, appI.AppManager()),
6868
),
6969
)
7070

server/v2/api/rest/server.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func (s *Server[T]) Init(appI serverv2.AppI[T], cfg map[string]any, logger log.L
4545
}
4646

4747
s.router = http.NewServeMux()
48-
s.router.Handle("/", NewDefaultHandler(appI.GetAppManager()))
48+
s.router.Handle("/", NewDefaultHandler(appI.AppManager()))
4949
s.config = serverCfg
5050

5151
return nil

server/v2/appmanager/appmanager.go

+24-4
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,34 @@ type Store interface {
2626

2727
// AppManager is a coordinator for all things related to an application
2828
type AppManager[T transaction.Tx] struct {
29+
// Gas limits for validating, querying, and simulating transactions.
2930
config Config
30-
31+
// InitGenesis is a function that initializes the application state from a genesis file.
32+
// It takes a context, a source reader for the genesis file, and a transaction handler function.
33+
initGenesis InitGenesis
34+
// ExportGenesis is a function that exports the application state to a genesis file.
35+
// It takes a context and a version number for the genesis file.
36+
exportGenesis ExportGenesis
37+
// The database for storing application data.
3138
db Store
39+
// The state transition function for processing transactions.
40+
stf StateTransitionFunction[T]
41+
}
3242

33-
initGenesis InitGenesis
34-
exportGenesis ExportGenesis
43+
func NewAppManager[T transaction.Tx](
44+
config Config,
45+
db Store,
46+
stf StateTransitionFunction[T],
47+
initGenesisImpl InitGenesis,
48+
exportGenesisImpl ExportGenesis,
49+
) (*AppManager[T], error) {
50+
appManager := &AppManager[T]{
51+
config: config,
52+
db: db,
53+
stf: stf,
54+
}
3555

36-
stf StateTransitionFunction[T]
56+
return appManager, nil
3757
}
3858

3959
// InitGenesis initializes the genesis state of the application.

0 commit comments

Comments
 (0)