cosmos-sdk/tests/integration/v2/services.go
Alex | Interchain Labs 952db2b320
chore: remove baseapp from x/accounts (#23355)
Co-authored-by: Tyler <48813565+technicallyty@users.noreply.github.com>
2025-01-16 18:52:36 +00:00

341 lines
7.6 KiB
Go

package integration
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"github.com/cosmos/gogoproto/jsonpb"
gogoproto "github.com/cosmos/gogoproto/proto"
"cosmossdk.io/core/branch"
"cosmossdk.io/core/comet"
corecontext "cosmossdk.io/core/context"
"cosmossdk.io/core/event"
"cosmossdk.io/core/gas"
"cosmossdk.io/core/header"
"cosmossdk.io/core/server"
corestore "cosmossdk.io/core/store"
"cosmossdk.io/core/transaction"
"cosmossdk.io/server/v2/stf"
stfbranch "cosmossdk.io/server/v2/stf/branch"
stfgas "cosmossdk.io/server/v2/stf/gas"
)
var ErrInvalidMsgType = fmt.Errorf("invalid message type")
func (c cometServiceImpl) CometInfo(context.Context) comet.Info {
return comet.Info{}
}
// Services
var _ server.DynamicConfig = &dynamicConfigImpl{}
type dynamicConfigImpl struct {
homeDir string
}
func (d *dynamicConfigImpl) Get(key string) any {
return d.GetString(key)
}
func (d *dynamicConfigImpl) GetString(key string) string {
switch key {
case "home":
return d.homeDir
case "store.app-db-backend":
return "goleveldb"
case "server.minimum-gas-prices":
return "0stake"
default:
panic(fmt.Sprintf("unknown key: %s", key))
}
}
func (d *dynamicConfigImpl) UnmarshalSub(string, any) (bool, error) {
return false, nil
}
var _ comet.Service = &cometServiceImpl{}
type cometServiceImpl struct{}
type storeService struct {
actor []byte
executionService corestore.KVStoreService
}
type contextKeyType struct{}
var contextKey = contextKeyType{}
type integrationContext struct {
state corestore.WriterMap
gasMeter gas.Meter
header header.Info
events []event.Event
}
func SetHeaderInfo(ctx context.Context, h header.Info) context.Context {
iCtx, ok := ctx.Value(contextKey).(*integrationContext)
if !ok {
return ctx
}
iCtx.header = h
return context.WithValue(ctx, contextKey, iCtx)
}
func HeaderInfoFromContext(ctx context.Context) header.Info {
iCtx, ok := ctx.Value(contextKey).(*integrationContext)
if ok {
return iCtx.header
}
return header.Info{}
}
func SetCometInfo(ctx context.Context, c comet.Info) context.Context {
return context.WithValue(ctx, corecontext.CometInfoKey, c)
}
func EventsFromContext(ctx context.Context) []event.Event {
iCtx, ok := ctx.Value(contextKey).(*integrationContext)
if !ok {
return nil
}
return iCtx.events
}
func GetAttributes(e []event.Event, key string) ([]event.Attribute, bool) {
attrs := make([]event.Attribute, 0)
for _, event := range e {
attributes, err := event.Attributes()
if err != nil {
return nil, false
}
for _, attr := range attributes {
if attr.Key == key {
attrs = append(attrs, attr)
}
}
}
return attrs, len(attrs) > 0
}
func GetAttribute(e event.Event, key string) (event.Attribute, bool) {
attributes, err := e.Attributes()
if err != nil {
return event.Attribute{}, false
}
for _, attr := range attributes {
if attr.Key == key {
return attr, true
}
}
return event.Attribute{}, false
}
func GasMeterFromContext(ctx context.Context) gas.Meter {
iCtx, ok := ctx.Value(contextKey).(*integrationContext)
if !ok {
return nil
}
return iCtx.gasMeter
}
func GasMeterFactory(ctx context.Context) func() gas.Meter {
return func() gas.Meter {
return GasMeterFromContext(ctx)
}
}
func SetGasMeter(ctx context.Context, meter gas.Meter) context.Context {
iCtx, ok := ctx.Value(contextKey).(*integrationContext)
if !ok {
return ctx
}
iCtx.gasMeter = meter
return context.WithValue(ctx, contextKey, iCtx)
}
func (s storeService) OpenKVStore(ctx context.Context) corestore.KVStore {
const gasLimit = 1_000_000
iCtx, ok := ctx.Value(contextKey).(*integrationContext)
if !ok {
return s.executionService.OpenKVStore(ctx)
}
iCtx.gasMeter = stfgas.NewMeter(gasLimit)
writerMap := stfgas.NewMeteredWriterMap(stfgas.DefaultConfig, iCtx.gasMeter, iCtx.state)
state, err := writerMap.GetWriter(s.actor)
if err != nil {
panic(err)
}
return state
}
var (
_ event.Service = &eventService{}
_ event.Manager = &eventManager{}
)
type eventService struct{}
// EventManager implements event.Service.
func (e *eventService) EventManager(ctx context.Context) event.Manager {
iCtx, ok := ctx.Value(contextKey).(*integrationContext)
if !ok {
panic("context is not an integration context")
}
return &eventManager{ctx: iCtx}
}
type eventManager struct {
ctx *integrationContext
}
// Emit implements event.Manager.
func (e *eventManager) Emit(tev transaction.Msg) error {
ev := event.Event{
Type: gogoproto.MessageName(tev),
Attributes: func() ([]event.Attribute, error) {
outerEvent, err := stf.TypedEventToEvent(tev)
if err != nil {
return nil, err
}
return outerEvent.Attributes()
},
Data: func() (json.RawMessage, error) {
buf := new(bytes.Buffer)
jm := &jsonpb.Marshaler{OrigName: true, EmitDefaults: true, AnyResolver: nil}
if err := jm.Marshal(buf, tev); err != nil {
return nil, err
}
return buf.Bytes(), nil
},
}
e.ctx.events = append(e.ctx.events, ev)
return nil
}
// EmitKV implements event.Manager.
func (e *eventManager) EmitKV(eventType string, attrs ...event.Attribute) error {
ev := event.Event{
Type: eventType,
Attributes: func() ([]event.Attribute, error) {
return attrs, nil
},
Data: func() (json.RawMessage, error) {
return json.Marshal(attrs)
},
}
e.ctx.events = append(e.ctx.events, ev)
return nil
}
var _ branch.Service = &BranchService{}
// custom branch service for integration tests
type BranchService struct{}
func (bs *BranchService) Execute(ctx context.Context, f func(ctx context.Context) error) error {
_, ok := ctx.Value(contextKey).(*integrationContext)
if !ok {
return errors.New("context is not an integration context")
}
return f(ctx)
}
func (bs *BranchService) ExecuteWithGasLimit(
ctx context.Context,
gasLimit uint64,
f func(ctx context.Context) error,
) (gasUsed uint64, err error) {
iCtx, ok := ctx.Value(contextKey).(*integrationContext)
if !ok {
return 0, errors.New("context is not an integration context")
}
originalGasMeter := iCtx.gasMeter
iCtx.gasMeter = stfgas.DefaultGasMeter(gasLimit)
// execute branched, with predefined gas limit.
err = bs.execute(ctx, iCtx, f)
// restore original context
gasUsed = iCtx.gasMeter.Limit() - iCtx.gasMeter.Remaining()
_ = originalGasMeter.Consume(gasUsed, "execute-with-gas-limit")
iCtx.gasMeter = stfgas.DefaultGasMeter(originalGasMeter.Remaining())
return gasUsed, err
}
func (bs BranchService) execute(ctx context.Context, ictx *integrationContext, f func(ctx context.Context) error) error {
branchedState := stfbranch.DefaultNewWriterMap(ictx.state)
meteredBranchedState := stfgas.DefaultWrapWithGasMeter(ictx.gasMeter, branchedState)
branchedCtx := &integrationContext{
state: meteredBranchedState,
gasMeter: ictx.gasMeter,
header: ictx.header,
events: ictx.events,
}
newCtx := context.WithValue(ctx, contextKey, branchedCtx)
err := f(newCtx)
if err != nil {
return err
}
err = applyStateChanges(ictx.state, branchedCtx.state)
if err != nil {
return err
}
return nil
}
func applyStateChanges(dst, src corestore.WriterMap) error {
changes, err := src.GetStateChanges()
if err != nil {
return err
}
return dst.ApplyStateChanges(changes)
}
var _ header.Service = &HeaderService{}
type HeaderService struct{}
func (h *HeaderService) HeaderInfo(ctx context.Context) header.Info {
iCtx, ok := ctx.Value(contextKey).(*integrationContext)
if !ok {
return header.Info{}
}
return iCtx.header
}
var _ gas.Service = &GasService{}
type GasService struct{}
func (g *GasService) GasMeter(ctx context.Context) gas.Meter {
return GasMeterFromContext(ctx)
}
func (g *GasService) GasConfig(ctx context.Context) gas.GasConfig {
return gas.GasConfig{}
}