cosmos-sdk/runtime/v2/services/genesis.go

184 lines
5.2 KiB
Go

package services
import (
"context"
"errors"
"fmt"
"cosmossdk.io/core/event"
"cosmossdk.io/core/header"
"cosmossdk.io/core/store"
"cosmossdk.io/core/transaction"
)
var (
_ store.KVStoreService = (*GenesisKVStoreService)(nil)
_ header.Service = (*GenesisHeaderService)(nil)
_ store.KVStore = (*readonlyKVStore)(nil)
)
type genesisContextKeyType struct{}
var genesisContextKey = genesisContextKeyType{}
// genesisContext is a context that is used during genesis initialization.
// it backs the store.KVStoreService and header.Service interface implementations
// defined in this file.
type genesisContext struct {
state store.ReaderMap
}
// NewGenesisContext creates a new genesis context.
func NewGenesisContext(state store.ReaderMap) genesisContext {
return genesisContext{
state: state,
}
}
// Mutate runs the provided function within the genesis context and returns an
// updated store.WriterMap containing the state modifications made during InitGenesis.
func (g genesisContext) Mutate(
ctx context.Context,
fn func(ctx context.Context) error,
) (store.WriterMap, error) {
writerMap, ok := g.state.(store.WriterMap)
if !ok {
return nil, fmt.Errorf("mutate requires a store.WriterMap, got a %T", g.state)
}
ctx = context.WithValue(ctx, genesisContextKey, g)
err := fn(ctx)
if err != nil {
return nil, err
}
return writerMap, nil
}
// Read runs the provided function within the genesis context.
func (g genesisContext) Read(
ctx context.Context,
fn func(ctx context.Context) error,
) error {
ctx = context.WithValue(ctx, genesisContextKey, g)
return fn(ctx)
}
// GenesisKVStoreService is a store.KVStoreService implementation that is used during
// genesis initialization. It wraps an inner execution context store.KVStoreService.
type GenesisKVStoreService struct {
actor []byte
executionService store.KVStoreService
}
// NewGenesisKVService creates a new GenesisKVStoreService.
// - actor is the module store key.
// - executionService is the store.KVStoreService to use when the genesis context is not active.
func NewGenesisKVService(
actor []byte,
executionService store.KVStoreService,
) *GenesisKVStoreService {
return &GenesisKVStoreService{
actor: actor,
executionService: executionService,
}
}
// OpenKVStore implements store.KVStoreService.
func (g *GenesisKVStoreService) OpenKVStore(ctx context.Context) store.KVStore {
v := ctx.Value(genesisContextKey)
if v == nil {
return g.executionService.OpenKVStore(ctx)
}
genCtx, ok := v.(genesisContext)
if !ok {
panic(fmt.Errorf("unexpected genesis context type: %T", v))
}
writerMap, ok := genCtx.state.(store.WriterMap)
if ok {
state, err := writerMap.GetWriter(g.actor)
if err != nil {
panic(err)
}
return state
}
state, err := genCtx.state.GetReader(g.actor)
if err != nil {
panic(err)
}
return readonlyKVStore{state}
}
type readonlyKVStore struct {
store.Reader
}
func (r readonlyKVStore) Set(key, value []byte) error {
return errors.New("tried to call Set on a readonly store")
}
func (r readonlyKVStore) Delete(key []byte) error {
return errors.New("tried to call Delete on a readonly store")
}
// GenesisHeaderService is a header.Service implementation that is used during
// genesis initialization. It wraps an inner execution context header.Service.
type GenesisHeaderService struct {
executionService header.Service
}
// HeaderInfo implements header.Service.
// During genesis initialization, it returns an empty header.Info.
func (g *GenesisHeaderService) HeaderInfo(ctx context.Context) header.Info {
v := ctx.Value(genesisContextKey)
if v == nil {
return g.executionService.HeaderInfo(ctx)
}
return header.Info{}
}
// NewGenesisHeaderService creates a new GenesisHeaderService.
// - executionService is the header.Service to use when the genesis context is not active.
func NewGenesisHeaderService(executionService header.Service) header.Service {
return &GenesisHeaderService{
executionService: executionService,
}
}
// GenesisEventService is an event.Service implementation that is used during
// genesis initialization. It wraps an inner execution context event.Service.
// During genesis initialization, it returns a blackHoleEventManager into which
// events enter and disappear completely.
type GenesisEventService struct {
executionService event.Service
}
// NewGenesisEventService creates a new GenesisEventService.
// - executionService is the event.Service to use when the genesis context is not active.
func NewGenesisEventService(executionService event.Service) event.Service {
return &GenesisEventService{
executionService: executionService,
}
}
func (g *GenesisEventService) EventManager(ctx context.Context) event.Manager {
v := ctx.Value(genesisContextKey)
if v == nil {
return g.executionService.EventManager(ctx)
}
return &blackHoleEventManager{}
}
var _ event.Manager = (*blackHoleEventManager)(nil)
// blackHoleEventManager is an event.Manager that does nothing.
// It is used during genesis initialization, genesis events are not emitted.
type blackHoleEventManager struct{}
func (b *blackHoleEventManager) Emit(_ transaction.Msg) error {
return nil
}
func (b *blackHoleEventManager) EmitKV(_ string, _ ...event.Attribute) error {
return nil
}