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: migration to sdk environment and context.Context #7870

Closed
wants to merge 9 commits into from
82 changes: 45 additions & 37 deletions modules/light-clients/08-wasm/keeper/contract_keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@

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

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"

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

internaltypes "github.com/cosmos/ibc-go/modules/light-clients/08-wasm/internal/types"
"github.com/cosmos/ibc-go/modules/light-clients/08-wasm/types"
clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types"
Expand All @@ -34,70 +34,78 @@
)

// 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()
multipliedGasMeter := types.NewMultipliedGasMeter(sdkGasMeter, types.VMGasRegister)
gasLimit := VMGasRegister.RuntimeGasForContract(ctx)
func (k Keeper) instantiateContract(ctx context.Context, clientID string, clientStore storetypes.KVStore, checksum types.Checksum, msg []byte) (*wasmvmtypes.ContractResult, error) {
meter := k.GasService.GasMeter(ctx)
multipliedGasMeter := types.NewMultipliedGasMeter(meter, types.VMGasRegister)
gasLimit := VMGasRegister.RuntimeGasForContract(meter)

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 := meter.Consume(types.VMGasRegister.SetupContractCost(true, len(msg)), "Loading CosmWasm module: instantiate"); err != nil {
return nil, 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(meter, 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 := k.GasService.GasMeter(ctx).Consume(VMGasRegister.SetupContractCost(true, len(msg)), "Loading CosmWasm module: sudo"); err != nil {
return nil, 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()
multipliedGasMeter := types.NewMultipliedGasMeter(sdkGasMeter, VMGasRegister)
gasLimit := VMGasRegister.RuntimeGasForContract(ctx)
func (k Keeper) queryContract(ctx context.Context, clientID string, clientStore storetypes.KVStore, checksum types.Checksum, msg []byte) (*wasmvmtypes.QueryResult, error) {
gasMeter := k.GasService.GasMeter(ctx)
multipliedGasMeter := types.NewMultipliedGasMeter(gasMeter, VMGasRegister)
gasLimit := VMGasRegister.RuntimeGasForContract(gasMeter)

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 := gasMeter.Consume(VMGasRegister.SetupContractCost(true, len(msg)), "Loading CosmWasm module: query"); err != nil {
return nil, 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(gasMeter, 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()
multipliedGasMeter := types.NewMultipliedGasMeter(sdkGasMeter, VMGasRegister)
gasLimit := VMGasRegister.RuntimeGasForContract(ctx)
func (k Keeper) migrateContract(ctx context.Context, clientID string, clientStore storetypes.KVStore, checksum types.Checksum, msg []byte) (*wasmvmtypes.ContractResult, error) {
gasMeter := k.GasService.GasMeter(ctx)
multipliedGasMeter := types.NewMultipliedGasMeter(gasMeter, VMGasRegister)
gasLimit := VMGasRegister.RuntimeGasForContract(gasMeter)

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 := gasMeter.Consume(VMGasRegister.SetupContractCost(true, len(msg)), "Loading CosmWasm module: migrate"); err != nil {
return nil, 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(gasMeter, 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")
Expand Down Expand Up @@ -136,7 +144,7 @@
// - 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")
Expand Down Expand Up @@ -171,7 +179,7 @@
// 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,7 +200,7 @@
// 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")
Expand Down Expand Up @@ -250,15 +258,15 @@
}

// 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(header header.Info, contractAddr string) wasmvmtypes.Env {

Check failure on line 261 in modules/light-clients/08-wasm/keeper/contract_keeper.go

View workflow job for this annotation

GitHub Actions / lint

import-shadowing: The name 'header' shadows an import name (revive)
chainID := header.ChainID
height := header.Height

// safety checks before casting below
if height < 0 {
panic(errors.New("block height must never be negative"))
}
nsec := ctx.BlockTime().UnixNano()
nsec := header.Time.UnixNano()
if nsec < 0 {
panic(errors.New("block (unix) time must never be negative "))
}
Expand Down
46 changes: 22 additions & 24 deletions modules/light-clients/08-wasm/keeper/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,32 @@
sdk "github.com/cosmos/cosmos-sdk/types"

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

"cosmossdk.io/core/event"
)

// emitStoreWasmCodeEvent emits a store wasm code event
func emitStoreWasmCodeEvent(ctx sdk.Context, checksum types.Checksum) {
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.EventTypeStoreWasmCode,
sdk.NewAttribute(types.AttributeKeyWasmChecksum, hex.EncodeToString(checksum)),
),
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
),
})
func emitStoreWasmCodeEvent(em event.Manager, checksum types.Checksum) {
em.EmitKV(

Check failure on line 15 in modules/light-clients/08-wasm/keeper/events.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `em.EmitKV` is not checked (errcheck)
types.EventTypeStoreWasmCode,
event.NewAttribute(types.AttributeKeyWasmChecksum, hex.EncodeToString(checksum)),
)
em.EmitKV(

Check failure on line 19 in modules/light-clients/08-wasm/keeper/events.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `em.EmitKV` is not checked (errcheck)
sdk.EventTypeMessage,
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(
types.EventTypeMigrateContract,
sdk.NewAttribute(types.AttributeKeyClientID, clientID),
sdk.NewAttribute(types.AttributeKeyWasmChecksum, hex.EncodeToString(checksum)),
sdk.NewAttribute(types.AttributeKeyNewChecksum, hex.EncodeToString(newChecksum)),
),
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
),
})
func emitMigrateContractEvent(em event.Manager, clientID string, checksum, newChecksum types.Checksum) {
em.EmitKV(

Check failure on line 27 in modules/light-clients/08-wasm/keeper/events.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `em.EmitKV` is not checked (errcheck)
types.EventTypeMigrateContract,
event.NewAttribute(types.AttributeKeyClientID, clientID),
event.NewAttribute(types.AttributeKeyWasmChecksum, hex.EncodeToString(checksum)),
event.NewAttribute(types.AttributeKeyNewChecksum, hex.EncodeToString(newChecksum)),
)
em.EmitKV(

Check failure on line 33 in modules/light-clients/08-wasm/keeper/events.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `em.EmitKV` is not checked (errcheck)
sdk.EventTypeMessage,
event.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
)
}
5 changes: 2 additions & 3 deletions modules/light-clients/08-wasm/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"cosmossdk.io/collections"
errorsmod "cosmossdk.io/errors"

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

"github.com/cosmos/ibc-go/modules/light-clients/08-wasm/types"
Expand All @@ -19,7 +18,7 @@ import (
var _ types.QueryServer = (*Keeper)(nil)

// Code implements the Query/Code gRPC method
func (k Keeper) Code(goCtx context.Context, req *types.QueryCodeRequest) (*types.QueryCodeResponse, error) {
func (k Keeper) Code(ctx context.Context, req *types.QueryCodeRequest) (*types.QueryCodeResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
Expand All @@ -30,7 +29,7 @@ func (k Keeper) Code(goCtx context.Context, req *types.QueryCodeRequest) (*types
}

// Only return checksums we previously stored, not arbitrary checksums that might be stored via e.g Wasmd.
if !k.HasChecksum(sdk.UnwrapSDKContext(goCtx), checksum) {
if !k.HasChecksum(ctx, checksum) {
return nil, status.Error(codes.NotFound, errorsmod.Wrap(types.ErrWasmChecksumNotFound, req.Checksum).Error())
}

Expand Down
43 changes: 19 additions & 24 deletions modules/light-clients/08-wasm/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,26 @@ import (
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 {
// implements gRPC QueryServer interface
appmodule.Environment
types.QueryServer

cdc codec.BinaryCodec
clientKeeper types.ClientKeeper

vm types.WasmEngine

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

queryPlugins QueryPlugins

Expand All @@ -49,12 +45,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() log.Logger {
return k.Environment.Logger
}

// GetVM returns the keeper's vm engine.
Expand All @@ -77,8 +69,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 +79,13 @@ 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
var err error
gasMeter := k.GasService.GasMeter(ctx)
if types.IsGzip(code) {
sdkCtx.GasMeter().ConsumeGas(types.VMGasRegister.UncompressCosts(len(code)), "Uncompress gzip bytecode")
if err := gasMeter.Consume(types.VMGasRegister.UncompressCosts(len(code)), "Uncompress gzip bytecode"); err != nil {
return nil, errorsmod.Wrap(err, "failed to consume gas for decompression")
}

code, err = types.Uncompress(code, types.MaxWasmSize)
if err != nil {
return nil, errorsmod.Wrap(err, "failed to store contract")
Expand All @@ -113,9 +108,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(gasMeter)
vmChecksum, gasUsed, err := storeFn(code, gasLeft)
types.VMGasRegister.ConsumeRuntimeGas(sdkCtx, gasUsed)
types.VMGasRegister.ConsumeRuntimeGas(gasMeter, gasUsed)
if err != nil {
return nil, errorsmod.Wrap(err, "failed to store contract")
}
Expand All @@ -141,7 +136,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 +175,13 @@ func (k Keeper) migrateContractCode(ctx sdk.Context, clientID string, newChecksu

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

emitMigrateContractEvent(ctx, clientID, oldChecksum, newChecksum)
emitMigrateContractEvent(k.Environment.EventService.EventManager(ctx), clientID, oldChecksum, newChecksum)

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 +228,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
Loading
Loading