Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(runtime/v2): Export Genesis #20009

Merged
merged 10 commits into from
Apr 25, 2024
102 changes: 82 additions & 20 deletions runtime/v2/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,14 +140,78 @@
return nil
}

// InitGenesis performs init genesis functionality for modules.
func (m *MM) InitGenesis() {
panic("implement me")
// ExportGenesis performs export genesis functionality for modules
func (m *MM) ExportGenesis(ctx context.Context, modulesToExport ...string) (map[string]json.RawMessage, error) {
if len(modulesToExport) == 0 {
modulesToExport = m.config.ExportGenesis
}

return m.ExportGenesisForModules(ctx, modulesToExport...)
}

// ExportGenesis performs export genesis functionality for modules
func (m *MM) ExportGenesis() {
panic("implement me")
// ExportGenesisForModules performs export genesis functionality for modules
func (m *MM) ExportGenesisForModules(ctx context.Context, modulesToExport ...string) (map[string]json.RawMessage, error) {
if len(modulesToExport) == 0 {
modulesToExport = m.config.ExportGenesis
hieuvubk marked this conversation as resolved.
Show resolved Hide resolved
}
// verify modules exists in app, so that we don't panic in the middle of an export
if err := m.checkModulesExists(modulesToExport); err != nil {
return nil, err
}

type genesisResult struct {
bz json.RawMessage
err error
}

type ModuleI interface {
ExportGenesis(ctx context.Context) (json.RawMessage, error)
}

channels := make(map[string]chan genesisResult)
for _, moduleName := range modulesToExport {
mod := m.modules[moduleName]
var moduleI ModuleI

if module, hasGenesis := mod.(appmodulev2.HasGenesis); hasGenesis {
moduleI = module.(ModuleI)
} else if module, hasABCIGenesis := mod.(appmodulev2.HasGenesis); hasABCIGenesis {
moduleI = module.(ModuleI)
}

channels[moduleName] = make(chan genesisResult)
go func(moduleI ModuleI, ch chan genesisResult) {
jm, err := moduleI.ExportGenesis(ctx)
if err != nil {
ch <- genesisResult{nil, err}
return
}
ch <- genesisResult{jm, nil}
}(moduleI, channels[moduleName])
Dismissed Show dismissed Hide dismissed
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modules that do not implement the export method are silently ignored. Would some log output make sense?

Copy link
Member

@julienrbrt julienrbrt Apr 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All mandatory modules are present and checked by assertNoForgottenModules.
I'd rather have assertNoForgottenModules panic when unnecessary modules are present than adding extra log personally.

We have relaxed the required modules in the previous sdk release, but you could still add modules to say OrderBeginBlock that don't implement BeginBlocker, and it will be silently skipped. Given this is a green field, imho best to have assertNoForgottenModules more pedantic in this new module manager.


genesisData := make(map[string]json.RawMessage)
for moduleName := range channels {
res := <-channels[moduleName]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this is blocking on the first channel in the map. If you would move the module name into the genesisResult, you can have a channel just for the results instead that would be a fifo. Not sure if this gives some significant performance benefit or makes it harder to read. 🤷

if res.err != nil {
return nil, fmt.Errorf("genesis export error in %s: %w", moduleName, res.err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 would it make sense to collect all error messages instead and return them together? It is also ok to fail fast on first error. You could cancel the context in this case (ctx, cancel := context.WithCancel(ctx)) (assuming the modules would abort) and exit the Go routines early, too.

}

genesisData[moduleName] = res.bz
}

Dismissed Show dismissed Hide dismissed
return genesisData, nil
}

// checkModulesExists verifies that all modules in the list exist in the app
func (m *MM) checkModulesExists(moduleName []string) error {
for _, name := range moduleName {
if _, ok := m.modules[name]; !ok {
return fmt.Errorf("module %s does not exist", name)
}
}

return nil
}

// BeginBlock runs the begin-block logic of all modules
Expand Down Expand Up @@ -365,34 +429,32 @@

if err := m.assertNoForgottenModules("InitGenesis", m.config.InitGenesis, func(moduleName string) bool {
module := m.modules[moduleName]
if _, hasGenesis := module.(appmodulev2.HasGenesis); hasGenesis {
return !hasGenesis
if _, hasGenesis := module.(appmodule.HasGenesisAuto); hasGenesis {
panic(fmt.Sprintf("module %s isn't server/v2 compatible", moduleName))
}

// TODO, if we actually don't support old genesis, let's panic here saying this module isn't server/v2 compatible
if _, hasABCIGenesis := module.(sdkmodule.HasABCIGenesis); hasABCIGenesis {
return !hasABCIGenesis
if _, hasGenesis := module.(appmodulev2.HasGenesis); hasGenesis {
return !hasGenesis
}

_, hasGenesis := module.(sdkmodule.HasGenesis)
return !hasGenesis
_, hasABCIGenesis := module.(sdkmodule.HasABCIGenesis)
return !hasABCIGenesis
}); err != nil {
return err
}

if err := m.assertNoForgottenModules("ExportGenesis", m.config.ExportGenesis, func(moduleName string) bool {
module := m.modules[moduleName]
if _, hasGenesis := module.(appmodulev2.HasGenesis); hasGenesis {
return !hasGenesis
if _, hasGenesis := module.(appmodule.HasGenesisAuto); hasGenesis {
panic(fmt.Sprintf("module %s isn't server/v2 compatible", moduleName))
}

// TODO, if we actually don't support old genesis, let's panic here saying this module isn't server/v2 compatible
if _, hasABCIGenesis := module.(sdkmodule.HasABCIGenesis); hasABCIGenesis {
return !hasABCIGenesis
if _, hasGenesis := module.(appmodulev2.HasGenesis); hasGenesis {
return !hasGenesis
}

_, hasGenesis := module.(sdkmodule.HasGenesis)
return !hasGenesis
_, hasABCIGenesis := module.(sdkmodule.HasABCIGenesis)
return !hasABCIGenesis
}); err != nil {
return err
}
Expand Down
Loading