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

chore: use context.Context and appmodule.Environment in 08-wasm #7880

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 47 additions & 35 deletions modules/light-clients/08-wasm/keeper/contract_keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ package keeper

import (
"bytes"
"context"
"encoding/hex"
"encoding/json"
"errors"
"fmt"

wasmvm "github.com/CosmWasm/wasmvm/v2"
wasmvmtypes "github.com/CosmWasm/wasmvm/v2/types"

"cosmossdk.io/core/header"
errorsmod "cosmossdk.io/errors"
storetypes "cosmossdk.io/store/types"

Expand All @@ -34,77 +37,86 @@ var (
)

// instantiateContract calls vm.Instantiate with appropriate arguments.
func (k Keeper) instantiateContract(ctx sdk.Context, clientID string, clientStore storetypes.KVStore, checksum types.Checksum, msg []byte) (*wasmvmtypes.ContractResult, error) {
sdkGasMeter := ctx.GasMeter()
func (k Keeper) instantiateContract(ctx context.Context, clientID string, clientStore storetypes.KVStore, checksum types.Checksum, msg []byte) (*wasmvmtypes.ContractResult, error) {
sdkGasMeter := k.GasService.GasMeter(ctx)
multipliedGasMeter := types.NewMultipliedGasMeter(sdkGasMeter, types.VMGasRegister)
gasLimit := VMGasRegister.RuntimeGasForContract(ctx)
gasLimit := VMGasRegister.RuntimeGasForContract(sdkGasMeter)

env := getEnv(ctx, clientID)
env := getEnv(k.HeaderService.HeaderInfo(ctx), clientID)

msgInfo := wasmvmtypes.MessageInfo{
Sender: "",
Funds: nil,
}

ctx.GasMeter().ConsumeGas(types.VMGasRegister.SetupContractCost(true, len(msg)), "Loading CosmWasm module: instantiate")
if err := sdkGasMeter.Consume(types.VMGasRegister.SetupContractCost(true, len(msg)), "Loading CosmWasm module: instantiate"); err != nil {
return nil, fmt.Errorf("failed to consume gas for Loading Cosmwasm module instantiate: %w", err)
}

resp, gasUsed, err := k.GetVM().Instantiate(checksum, env, msgInfo, msg, internaltypes.NewStoreAdapter(clientStore), wasmvmAPI, k.newQueryHandler(ctx, clientID), multipliedGasMeter, gasLimit, types.CostJSONDeserialization)
types.VMGasRegister.ConsumeRuntimeGas(ctx, gasUsed)
types.VMGasRegister.ConsumeRuntimeGas(sdkGasMeter, gasUsed)
return resp, err
}

// callContract calls vm.Sudo with internally constructed gas meter and environment.
func (k Keeper) callContract(ctx sdk.Context, clientID string, clientStore storetypes.KVStore, checksum types.Checksum, msg []byte) (*wasmvmtypes.ContractResult, error) {
sdkGasMeter := ctx.GasMeter()
func (k Keeper) callContract(ctx context.Context, clientID string, clientStore storetypes.KVStore, checksum types.Checksum, msg []byte) (*wasmvmtypes.ContractResult, error) {
sdkGasMeter := k.GasService.GasMeter(ctx)
multipliedGasMeter := types.NewMultipliedGasMeter(sdkGasMeter, VMGasRegister)
gasLimit := VMGasRegister.RuntimeGasForContract(ctx)
gasLimit := VMGasRegister.RuntimeGasForContract(sdkGasMeter)

env := getEnv(ctx, clientID)
env := getEnv(k.HeaderService.HeaderInfo(ctx), clientID)

ctx.GasMeter().ConsumeGas(VMGasRegister.SetupContractCost(true, len(msg)), "Loading CosmWasm module: sudo")
if err := sdkGasMeter.Consume(VMGasRegister.SetupContractCost(true, len(msg)), "Loading CosmWasm module: sudo"); err != nil {
return nil, fmt.Errorf("failed to consume gas for Loading Cosmwasm module sudo: %w", err)
}
resp, gasUsed, err := k.GetVM().Sudo(checksum, env, msg, internaltypes.NewStoreAdapter(clientStore), wasmvmAPI, k.newQueryHandler(ctx, clientID), multipliedGasMeter, gasLimit, types.CostJSONDeserialization)
VMGasRegister.ConsumeRuntimeGas(ctx, gasUsed)
VMGasRegister.ConsumeRuntimeGas(sdkGasMeter, gasUsed)
return resp, err
}

// queryContract calls vm.Query.
func (k Keeper) queryContract(ctx sdk.Context, clientID string, clientStore storetypes.KVStore, checksum types.Checksum, msg []byte) (*wasmvmtypes.QueryResult, error) {
sdkGasMeter := ctx.GasMeter()
func (k Keeper) queryContract(ctx context.Context, clientID string, clientStore storetypes.KVStore, checksum types.Checksum, msg []byte) (*wasmvmtypes.QueryResult, error) {
sdkGasMeter := k.GasService.GasMeter(ctx)
multipliedGasMeter := types.NewMultipliedGasMeter(sdkGasMeter, VMGasRegister)
gasLimit := VMGasRegister.RuntimeGasForContract(ctx)
gasLimit := VMGasRegister.RuntimeGasForContract(sdkGasMeter)

env := getEnv(ctx, clientID)
env := getEnv(k.HeaderService.HeaderInfo(ctx), clientID)

ctx.GasMeter().ConsumeGas(VMGasRegister.SetupContractCost(true, len(msg)), "Loading CosmWasm module: query")
if err := sdkGasMeter.Consume(VMGasRegister.SetupContractCost(true, len(msg)), "Loading CosmWasm module: query"); err != nil {
return nil, fmt.Errorf("failed to consume gas for Loading Cosmwasm module query: %w", err)
}
resp, gasUsed, err := k.GetVM().Query(checksum, env, msg, internaltypes.NewStoreAdapter(clientStore), wasmvmAPI, k.newQueryHandler(ctx, clientID), multipliedGasMeter, gasLimit, types.CostJSONDeserialization)
VMGasRegister.ConsumeRuntimeGas(ctx, gasUsed)
VMGasRegister.ConsumeRuntimeGas(sdkGasMeter, gasUsed)

return resp, err
}

// migrateContract calls vm.Migrate with internally constructed gas meter and environment.
func (k Keeper) migrateContract(ctx sdk.Context, clientID string, clientStore storetypes.KVStore, checksum types.Checksum, msg []byte) (*wasmvmtypes.ContractResult, error) {
sdkGasMeter := ctx.GasMeter()
func (k Keeper) migrateContract(ctx context.Context, clientID string, clientStore storetypes.KVStore, checksum types.Checksum, msg []byte) (*wasmvmtypes.ContractResult, error) {
sdkGasMeter := k.GasService.GasMeter(ctx)
multipliedGasMeter := types.NewMultipliedGasMeter(sdkGasMeter, VMGasRegister)
gasLimit := VMGasRegister.RuntimeGasForContract(ctx)
gasLimit := VMGasRegister.RuntimeGasForContract(sdkGasMeter)

env := getEnv(ctx, clientID)
env := getEnv(k.HeaderService.HeaderInfo(ctx), clientID)

ctx.GasMeter().ConsumeGas(VMGasRegister.SetupContractCost(true, len(msg)), "Loading CosmWasm module: migrate")
if err := sdkGasMeter.Consume(VMGasRegister.SetupContractCost(true, len(msg)), "Loading CosmWasm module: migrate"); err != nil {
return nil, fmt.Errorf("failed to consume gas for Loading Cosmwasm module migrate: %w", err)
}
resp, gasUsed, err := k.GetVM().Migrate(checksum, env, msg, internaltypes.NewStoreAdapter(clientStore), wasmvmAPI, k.newQueryHandler(ctx, clientID), multipliedGasMeter, gasLimit, types.CostJSONDeserialization)
VMGasRegister.ConsumeRuntimeGas(ctx, gasUsed)
VMGasRegister.ConsumeRuntimeGas(sdkGasMeter, gasUsed)

return resp, err
}

// WasmInstantiate accepts a message to instantiate a wasm contract, JSON encodes it and calls instantiateContract.
func (k Keeper) WasmInstantiate(ctx sdk.Context, clientID string, clientStore storetypes.KVStore, cs *types.ClientState, payload types.InstantiateMessage) error {
func (k Keeper) WasmInstantiate(ctx context.Context, clientID string, clientStore storetypes.KVStore, cs *types.ClientState, payload types.InstantiateMessage) error {
encodedData, err := json.Marshal(payload)
if err != nil {
return errorsmod.Wrap(err, "failed to marshal payload for wasm contract instantiation")
}

checksum := cs.Checksum
res, err := k.instantiateContract(ctx, clientID, clientStore, checksum, encodedData)
res, err := k.instantiateContract(sdk.UnwrapSDKContext(ctx), clientID, clientStore, checksum, encodedData)
if err != nil {
return errorsmod.Wrap(types.ErrVMError, err.Error())
}
Expand Down Expand Up @@ -136,14 +148,14 @@ func (k Keeper) WasmInstantiate(ctx sdk.Context, clientID string, clientStore st
// - the response of the contract call contains non-empty events
// - the response of the contract call contains non-empty attributes
// - the data bytes of the response cannot be unmarshaled into the result type
func (k Keeper) WasmSudo(ctx sdk.Context, clientID string, clientStore storetypes.KVStore, cs *types.ClientState, payload types.SudoMsg) ([]byte, error) {
func (k Keeper) WasmSudo(ctx context.Context, clientID string, clientStore storetypes.KVStore, cs *types.ClientState, payload types.SudoMsg) ([]byte, error) {
encodedData, err := json.Marshal(payload)
if err != nil {
return nil, errorsmod.Wrap(err, "failed to marshal payload for wasm execution")
}

checksum := cs.Checksum
res, err := k.callContract(ctx, clientID, clientStore, checksum, encodedData)
res, err := k.callContract(sdk.UnwrapSDKContext(ctx), clientID, clientStore, checksum, encodedData)
if err != nil {
return nil, errorsmod.Wrap(types.ErrVMError, err.Error())
}
Expand Down Expand Up @@ -171,7 +183,7 @@ func (k Keeper) WasmSudo(ctx sdk.Context, clientID string, clientStore storetype
// WasmMigrate migrate calls the migrate entry point of the contract with the given payload and returns the result.
// WasmMigrate returns an error if:
// - the contract migration returns an error
func (k Keeper) WasmMigrate(ctx sdk.Context, clientStore storetypes.KVStore, cs *types.ClientState, clientID string, payload []byte) error {
func (k Keeper) WasmMigrate(ctx context.Context, clientStore storetypes.KVStore, cs *types.ClientState, clientID string, payload []byte) error {
res, err := k.migrateContract(ctx, clientID, clientStore, cs.Checksum, payload)
if err != nil {
return errorsmod.Wrap(types.ErrVMError, err.Error())
Expand All @@ -192,13 +204,13 @@ func (k Keeper) WasmMigrate(ctx sdk.Context, clientStore storetypes.KVStore, cs
// WasmQuery returns an error if:
// - the contract query returns an error
// - the data bytes of the response cannot be unmarshal into the result type
func (k Keeper) WasmQuery(ctx sdk.Context, clientID string, clientStore storetypes.KVStore, cs *types.ClientState, payload types.QueryMsg) ([]byte, error) {
func (k Keeper) WasmQuery(ctx context.Context, clientID string, clientStore storetypes.KVStore, cs *types.ClientState, payload types.QueryMsg) ([]byte, error) {
encodedData, err := json.Marshal(payload)
if err != nil {
return nil, errorsmod.Wrap(err, "failed to marshal payload for wasm query")
}

res, err := k.queryContract(ctx, clientID, clientStore, cs.Checksum, encodedData)
res, err := k.queryContract(sdk.UnwrapSDKContext(ctx), clientID, clientStore, cs.Checksum, encodedData)
if err != nil {
return nil, errorsmod.Wrap(types.ErrVMError, err.Error())
}
Expand Down Expand Up @@ -250,15 +262,15 @@ func unmarshalClientState(cdc codec.BinaryCodec, bz []byte) (exported.ClientStat
}

// getEnv returns the state of the blockchain environment the contract is running on
func getEnv(ctx sdk.Context, contractAddr string) wasmvmtypes.Env {
chainID := ctx.BlockHeader().ChainID
height := ctx.BlockHeader().Height
func getEnv(headerInfo header.Info, contractAddr string) wasmvmtypes.Env {
chainID := headerInfo.ChainID
height := headerInfo.Height

// safety checks before casting below
if height < 0 {
panic(errors.New("block height must never be negative"))
}
nsec := ctx.BlockTime().UnixNano()
nsec := headerInfo.Time.UnixNano()
if nsec < 0 {
panic(errors.New("block (unix) time must never be negative "))
}
Expand Down
35 changes: 19 additions & 16 deletions modules/light-clients/08-wasm/keeper/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,41 @@ package keeper

import (
"encoding/hex"
"errors"

"cosmossdk.io/core/event"

sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/cosmos/ibc-go/modules/light-clients/08-wasm/types"
)

// emitStoreWasmCodeEvent emits a store wasm code event
func emitStoreWasmCodeEvent(ctx sdk.Context, checksum types.Checksum) {
Copy link
Contributor

Choose a reason for hiding this comment

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

For this, we have done another approach in transfer which I think is a little better: make the function a receiver function on the keeper and just pass in the context:
func (k Keeper) EmitTransferEvent(ctx context.Context

ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
func emitStoreWasmCodeEvent(em event.Manager, checksum types.Checksum) error {
return errors.Join(
em.EmitKV(
types.EventTypeStoreWasmCode,
sdk.NewAttribute(types.AttributeKeyWasmChecksum, hex.EncodeToString(checksum)),
event.NewAttribute(types.AttributeKeyWasmChecksum, hex.EncodeToString(checksum)),
),
sdk.NewEvent(
em.EmitKV(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
event.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
),
})
)
}

// emitMigrateContractEvent emits a migrate contract event
func emitMigrateContractEvent(ctx sdk.Context, clientID string, checksum, newChecksum types.Checksum) {
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
func emitMigrateContractEvent(em event.Manager, clientID string, checksum, newChecksum types.Checksum) error {
return errors.Join(
em.EmitKV(
types.EventTypeMigrateContract,
sdk.NewAttribute(types.AttributeKeyClientID, clientID),
sdk.NewAttribute(types.AttributeKeyWasmChecksum, hex.EncodeToString(checksum)),
sdk.NewAttribute(types.AttributeKeyNewChecksum, hex.EncodeToString(newChecksum)),
event.NewAttribute(types.AttributeKeyClientID, clientID),
event.NewAttribute(types.AttributeKeyWasmChecksum, hex.EncodeToString(checksum)),
event.NewAttribute(types.AttributeKeyNewChecksum, hex.EncodeToString(newChecksum)),
),
sdk.NewEvent(
em.EmitKV(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
event.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
),
})
)
}
43 changes: 21 additions & 22 deletions modules/light-clients/08-wasm/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@ import (
"bytes"
"context"
"encoding/hex"
"fmt"

wasmvm "github.com/CosmWasm/wasmvm/v2"

"cosmossdk.io/collections"
"cosmossdk.io/core/store"
"cosmossdk.io/core/appmodule"
"cosmossdk.io/core/log"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/log"

"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/cosmos/ibc-go/modules/light-clients/08-wasm/types"
clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types"
"github.com/cosmos/ibc-go/v9/modules/core/exported"
)

// Keeper defines the 08-wasm keeper
type Keeper struct {
appmodule.Environment
// implements gRPC QueryServer interface
types.QueryServer

Expand All @@ -30,8 +30,7 @@ type Keeper struct {

vm types.WasmEngine

checksums collections.KeySet[[]byte]
storeService store.KVStoreService
checksums collections.KeySet[[]byte]

queryPlugins QueryPlugins

Expand All @@ -49,12 +48,8 @@ func (k Keeper) GetAuthority() string {
}

// Logger returns a module-specific logger.
func (Keeper) Logger(ctx sdk.Context) log.Logger {
return moduleLogger(ctx)
}

func moduleLogger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", "x/"+exported.ModuleName+"-"+types.ModuleName)
func (k Keeper) Logger(_ context.Context) log.Logger {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the context still there due to some interface that needs to be satisifed?

return k.Environment.Logger
}

// GetVM returns the keeper's vm engine.
Expand All @@ -77,8 +72,8 @@ func (k *Keeper) setQueryPlugins(plugins QueryPlugins) {
k.queryPlugins = plugins
}

func (k Keeper) newQueryHandler(ctx sdk.Context, callerID string) *queryHandler {
return newQueryHandler(ctx, k.getQueryPlugins(), callerID)
func (k Keeper) newQueryHandler(ctx context.Context, callerID string) *queryHandler {
return newQueryHandler(ctx, k.Environment, k.getQueryPlugins(), callerID)
}

// storeWasmCode stores the contract to the VM, pins the checksum in the VM's in memory cache and stores the checksum
Expand All @@ -87,10 +82,12 @@ func (k Keeper) newQueryHandler(ctx sdk.Context, callerID string) *queryHandler
// - Size bounds are checked. Contract length must not be 0 or exceed a specific size (maxWasmSize).
// - The contract must not have already been stored in store.
func (k Keeper) storeWasmCode(ctx context.Context, code []byte, storeFn func(code wasmvm.WasmCode, gasLimit uint64) (wasmvm.Checksum, uint64, error)) ([]byte, error) {
sdkCtx := sdk.UnwrapSDKContext(ctx) // TODO: https://github.com/cosmos/ibc-go/issues/5917
meter := k.GasService.GasMeter(ctx)
var err error
if types.IsGzip(code) {
sdkCtx.GasMeter().ConsumeGas(types.VMGasRegister.UncompressCosts(len(code)), "Uncompress gzip bytecode")
if err := meter.Consume(types.VMGasRegister.UncompressCosts(len(code)), "Uncompress gzip bytecode"); err != nil {
return nil, fmt.Errorf("failed to consume gas for Uncompress gzip bytecode: %w", err)
}
code, err = types.Uncompress(code, types.MaxWasmSize)
if err != nil {
return nil, errorsmod.Wrap(err, "failed to store contract")
Expand All @@ -113,9 +110,9 @@ func (k Keeper) storeWasmCode(ctx context.Context, code []byte, storeFn func(cod
}

// create the code in the vm
gasLeft := types.VMGasRegister.RuntimeGasForContract(sdkCtx)
gasLeft := types.VMGasRegister.RuntimeGasForContract(meter)
vmChecksum, gasUsed, err := storeFn(code, gasLeft)
types.VMGasRegister.ConsumeRuntimeGas(sdkCtx, gasUsed)
types.VMGasRegister.ConsumeRuntimeGas(meter, gasUsed)
if err != nil {
return nil, errorsmod.Wrap(err, "failed to store contract")
}
Expand All @@ -141,7 +138,7 @@ func (k Keeper) storeWasmCode(ctx context.Context, code []byte, storeFn func(cod

// migrateContractCode migrates the contract for a given light client to one denoted by the given new checksum. The checksum we
// are migrating to must first be stored using storeWasmCode and must not match the checksum currently stored for this light client.
func (k Keeper) migrateContractCode(ctx sdk.Context, clientID string, newChecksum, migrateMsg []byte) error {
func (k Keeper) migrateContractCode(ctx context.Context, clientID string, newChecksum, migrateMsg []byte) error {
clientStore := k.clientKeeper.ClientStore(ctx, clientID)
wasmClientState, found := types.GetClientState(clientStore, k.cdc)
if !found {
Expand Down Expand Up @@ -180,13 +177,15 @@ func (k Keeper) migrateContractCode(ctx sdk.Context, clientID string, newChecksu

k.clientKeeper.SetClientState(ctx, clientID, wasmClientState)

emitMigrateContractEvent(ctx, clientID, oldChecksum, newChecksum)
if err = emitMigrateContractEvent(k.EventService.EventManager(ctx), clientID, oldChecksum, newChecksum); err != nil {
return fmt.Errorf("failed to emit migrate contract events: %w", err)
}

return nil
}

// GetWasmClientState returns the 08-wasm client state for the given client identifier.
func (k Keeper) GetWasmClientState(ctx sdk.Context, clientID string) (*types.ClientState, error) {
func (k Keeper) GetWasmClientState(ctx context.Context, clientID string) (*types.ClientState, error) {
clientState, found := k.clientKeeper.GetClientState(ctx, clientID)
if !found {
return nil, errorsmod.Wrapf(clienttypes.ErrClientTypeNotFound, "clientID %s", clientID)
Expand Down Expand Up @@ -233,7 +232,7 @@ func (k Keeper) HasChecksum(ctx context.Context, checksum types.Checksum) bool {
}

// InitializePinnedCodes updates wasmvm to pin to cache all contracts marked as pinned
func (k Keeper) InitializePinnedCodes(ctx sdk.Context) error {
func (k Keeper) InitializePinnedCodes(ctx context.Context) error {
checksums, err := k.GetAllChecksums(ctx)
if err != nil {
return err
Expand Down
2 changes: 2 additions & 0 deletions modules/light-clients/08-wasm/keeper/keeper_no_vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
// custom build directive: nolink_libwasmvm.
// This function is intended to panic and notify users that 08-wasm keeper functionality is not available.
func NewKeeperWithVM(
_ environment.Environment,
_ codec.BinaryCodec,
_ storetypes.KVStoreService,
_ types.ClientKeeper,
Expand All @@ -31,6 +32,7 @@ func NewKeeperWithVM(
// custom build directive: nolink_libwasmvm.
// This function is intended to panic and notify users that 08-wasm keeper functionality is not available.
func NewKeeperWithConfig(
_ environment.Environment,
_ codec.BinaryCodec,
_ storetypes.KVStoreService,
_ types.ClientKeeper,
Expand Down
Loading
Loading