Skip to content

Commit

Permalink
imp: use wasm VM CreateChecksum (#5123)
Browse files Browse the repository at this point in the history
* use wasm VM CreateChecksum

* review comments

* lint

* lint 2

* Remove raw calls to sha256, clean up handling of Checksums.

* Update modules/light-clients/08-wasm/keeper/keeper.go

Co-authored-by: Damian Nolan <[email protected]>

* Document this bad boy.

---------

Co-authored-by: DimitrisJim <[email protected]>
Co-authored-by: Damian Nolan <[email protected]>
(cherry picked from commit a231fea)

# Conflicts:
#	modules/light-clients/08-wasm/keeper/events.go
#	modules/light-clients/08-wasm/keeper/genesis.go
#	modules/light-clients/08-wasm/keeper/keeper.go
#	modules/light-clients/08-wasm/keeper/msg_server_test.go
#	modules/light-clients/08-wasm/keeper/snapshotter.go
#	modules/light-clients/08-wasm/keeper/snapshotter_test.go
#	modules/light-clients/08-wasm/testing/mock_engine.go
#	modules/light-clients/08-wasm/testing/values.go
#	modules/light-clients/08-wasm/testing/wasm_endpoint.go
#	modules/light-clients/08-wasm/types/client_state_test.go
#	modules/light-clients/08-wasm/types/migrate_contract_test.go
#	modules/light-clients/08-wasm/types/msgs_test.go
#	modules/light-clients/08-wasm/types/types_test.go
#	modules/light-clients/08-wasm/types/validation.go
#	modules/light-clients/08-wasm/types/validation_test.go
#	modules/light-clients/08-wasm/types/vm.go
#	modules/light-clients/08-wasm/types/wasm.go
#	modules/light-clients/08-wasm/types/wasm_test.go
  • Loading branch information
crodriguezvega authored and mergify[bot] committed Nov 20, 2023
1 parent bef2278 commit 768d592
Show file tree
Hide file tree
Showing 18 changed files with 3,169 additions and 0 deletions.
39 changes: 39 additions & 0 deletions modules/light-clients/08-wasm/keeper/events.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package keeper

import (
"encoding/hex"

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

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

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

View workflow job for this annotation

GitHub Actions / build (amd64)

cannot find module providing package github.com/cosmos/ibc-go/modules/light-clients/08-wasm/types: import lookup disabled by -mod=readonly

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

View workflow job for this annotation

GitHub Actions / build (arm64)

cannot find module providing package github.com/cosmos/ibc-go/modules/light-clients/08-wasm/types: import lookup disabled by -mod=readonly

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

View workflow job for this annotation

GitHub Actions / build (arm)

cannot find module providing package github.com/cosmos/ibc-go/modules/light-clients/08-wasm/types: import lookup disabled by -mod=readonly

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

View workflow job for this annotation

GitHub Actions / tests (03)

cannot find module providing package github.com/cosmos/ibc-go/modules/light-clients/08-wasm/types: import lookup disabled by -mod=readonly
)

// 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),
),
})
}

// 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),
),
})
}
42 changes: 42 additions & 0 deletions modules/light-clients/08-wasm/keeper/genesis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package keeper

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

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

// InitGenesis initializes the 08-wasm module's state from a provided genesis
// state.
func (k Keeper) InitGenesis(ctx sdk.Context, gs types.GenesisState) error {
for _, contract := range gs.Contracts {
_, err := k.storeWasmCode(ctx, contract.CodeBytes)
if err != nil {
return err
}
}
return nil
}

// ExportGenesis returns the 08-wasm module's exported genesis. This includes the code
// for all contracts previously stored.
func (k Keeper) ExportGenesis(ctx sdk.Context) types.GenesisState {
checksums, err := types.GetAllChecksums(ctx)
if err != nil {
panic(err)
}

// Grab code from wasmVM and add to genesis state.
var genesisState types.GenesisState
for _, checksum := range checksums {
code, err := k.wasmVM.GetCode(checksum)
if err != nil {
panic(err)
}
genesisState.Contracts = append(genesisState.Contracts, types.Contract{
CodeBytes: code,
})
}

return genesisState
}
192 changes: 192 additions & 0 deletions modules/light-clients/08-wasm/keeper/keeper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
package keeper

import (
"bytes"
"encoding/hex"
"errors"
"fmt"
"strings"

wasmvm "github.com/CosmWasm/wasmvm"

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

View workflow job for this annotation

GitHub Actions / build (amd64)

cannot find module providing package github.com/CosmWasm/wasmvm: import lookup disabled by -mod=readonly

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

View workflow job for this annotation

GitHub Actions / build (arm64)

cannot find module providing package github.com/CosmWasm/wasmvm: import lookup disabled by -mod=readonly

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

View workflow job for this annotation

GitHub Actions / build (arm)

cannot find module providing package github.com/CosmWasm/wasmvm: import lookup disabled by -mod=readonly

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

View workflow job for this annotation

GitHub Actions / tests (03)

cannot find module providing package github.com/CosmWasm/wasmvm: import lookup disabled by -mod=readonly

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

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

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

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

View workflow job for this annotation

GitHub Actions / build (amd64)

cannot find module providing package github.com/cosmos/ibc-go/modules/light-clients/08-wasm/internal/ibcwasm: import lookup disabled by -mod=readonly

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

View workflow job for this annotation

GitHub Actions / build (arm64)

cannot find module providing package github.com/cosmos/ibc-go/modules/light-clients/08-wasm/internal/ibcwasm: import lookup disabled by -mod=readonly

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

View workflow job for this annotation

GitHub Actions / build (arm)

cannot find module providing package github.com/cosmos/ibc-go/modules/light-clients/08-wasm/internal/ibcwasm: import lookup disabled by -mod=readonly

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

View workflow job for this annotation

GitHub Actions / tests (03)

cannot find module providing package github.com/cosmos/ibc-go/modules/light-clients/08-wasm/internal/ibcwasm: import lookup disabled by -mod=readonly
"github.com/cosmos/ibc-go/modules/light-clients/08-wasm/types"
clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types"

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

View workflow job for this annotation

GitHub Actions / build (amd64)

cannot find module providing package github.com/cosmos/ibc-go/v8/modules/core/02-client/types: import lookup disabled by -mod=readonly

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

View workflow job for this annotation

GitHub Actions / build (arm64)

cannot find module providing package github.com/cosmos/ibc-go/v8/modules/core/02-client/types: import lookup disabled by -mod=readonly

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

View workflow job for this annotation

GitHub Actions / build (arm)

cannot find module providing package github.com/cosmos/ibc-go/v8/modules/core/02-client/types: import lookup disabled by -mod=readonly

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

View workflow job for this annotation

GitHub Actions / tests (03)

cannot find module providing package github.com/cosmos/ibc-go/v8/modules/core/02-client/types: import lookup disabled by -mod=readonly
)

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

cdc codec.BinaryCodec
wasmVM ibcwasm.WasmEngine

clientKeeper types.ClientKeeper

authority string
}

// NewKeeperWithVM creates a new Keeper instance with the provided Wasm VM.
// This constructor function is meant to be used when the chain uses x/wasm
// and the same Wasm VM instance should be shared with it.
func NewKeeperWithVM(
cdc codec.BinaryCodec,
storeService storetypes.KVStoreService,
clientKeeper types.ClientKeeper,
authority string,
vm ibcwasm.WasmEngine,
) Keeper {
if clientKeeper == nil {
panic(errors.New("client keeper must be not nil"))
}

if vm == nil {
panic(errors.New("wasm VM must be not nil"))
}

if storeService == nil {
panic(errors.New("store service must be not nil"))
}

if strings.TrimSpace(authority) == "" {
panic(errors.New("authority must be non-empty"))
}

ibcwasm.SetVM(vm)
ibcwasm.SetupWasmStoreService(storeService)

return Keeper{
cdc: cdc,
wasmVM: vm,
clientKeeper: clientKeeper,
authority: authority,
}
}

// NewKeeperWithConfig creates a new Keeper instance with the provided Wasm configuration.
// This constructor function is meant to be used when the chain does not use x/wasm
// and a Wasm VM needs to be instantiated using the provided parameters.
func NewKeeperWithConfig(
cdc codec.BinaryCodec,
storeService storetypes.KVStoreService,
clientKeeper types.ClientKeeper,
authority string,
wasmConfig types.WasmConfig,
) Keeper {
vm, err := wasmvm.NewVM(wasmConfig.DataDir, wasmConfig.SupportedCapabilities, types.ContractMemoryLimit, wasmConfig.ContractDebugMode, types.MemoryCacheSize)
if err != nil {
panic(fmt.Errorf("failed to instantiate new Wasm VM instance: %v", err))
}

return NewKeeperWithVM(cdc, storeService, clientKeeper, authority, vm)
}

// GetAuthority returns the 08-wasm module's authority.
func (k Keeper) GetAuthority() string {
return k.authority
}

func (k Keeper) storeWasmCode(ctx sdk.Context, code []byte) ([]byte, error) {
var err error
if types.IsGzip(code) {
ctx.GasMeter().ConsumeGas(types.VMGasRegister.UncompressCosts(len(code)), "Uncompress gzip bytecode")
code, err = types.Uncompress(code, types.MaxWasmByteSize())
if err != nil {
return nil, errorsmod.Wrap(err, "failed to store contract")
}
}

// Check to see if store already has checksum.
checksum, err := types.CreateChecksum(code)
if err != nil {
return nil, errorsmod.Wrap(err, "wasm bytecode checksum failed")
}

if types.HasChecksum(ctx, checksum) {
return nil, types.ErrWasmCodeExists
}

// run the code through the wasm light client validation process
if err := types.ValidateWasmCode(code); err != nil {
return nil, errorsmod.Wrap(err, "wasm bytecode validation failed")
}

// create the code in the vm
ctx.GasMeter().ConsumeGas(types.VMGasRegister.CompileCosts(len(code)), "Compiling wasm bytecode")
vmChecksum, err := k.wasmVM.StoreCode(code)
if err != nil {
return nil, errorsmod.Wrap(err, "failed to store contract")
}

// SANITY: We've checked our store, additional safety check to assert that the checksum returned by WasmVM equals checksum generated by us.
if !bytes.Equal(vmChecksum, checksum) {
return nil, errorsmod.Wrapf(types.ErrInvalidChecksum, "expected %s, got %s", hex.EncodeToString(checksum), hex.EncodeToString(vmChecksum))
}

// pin the code to the vm in-memory cache
if err := k.wasmVM.Pin(vmChecksum); err != nil {
return nil, errorsmod.Wrapf(err, "failed to pin contract with checksum (%s) to vm cache", hex.EncodeToString(vmChecksum))
}

// store the checksum
err = ibcwasm.Checksums.Set(ctx, checksum)
if err != nil {
return nil, errorsmod.Wrap(err, "failed to store checksum")
}

return checksum, nil
}

func (k Keeper) migrateContractCode(ctx sdk.Context, clientID string, newChecksum, migrateMsg []byte) error {
wasmClientState, err := k.GetWasmClientState(ctx, clientID)
if err != nil {
return errorsmod.Wrap(err, "failed to retrieve wasm client state")
}
oldChecksum := wasmClientState.Checksum

clientStore := k.clientKeeper.ClientStore(ctx, clientID)

err = wasmClientState.MigrateContract(ctx, k.cdc, clientStore, clientID, newChecksum, migrateMsg)
if err != nil {
return errorsmod.Wrap(err, "contract migration failed")
}

// client state may be updated by the contract migration
wasmClientState, err = k.GetWasmClientState(ctx, clientID)
if err != nil {
// note that this also ensures that the updated client state is
// still a wasm client state
return errorsmod.Wrap(err, "failed to retrieve the updated wasm client state")
}

// update the client state checksum before persisting it
wasmClientState.Checksum = newChecksum

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

emitMigrateContractEvent(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) {
clientState, found := k.clientKeeper.GetClientState(ctx, clientID)
if !found {
return nil, errorsmod.Wrapf(clienttypes.ErrClientTypeNotFound, "clientID %s", clientID)
}

wasmClientState, ok := clientState.(*types.ClientState)
if !ok {
return nil, errorsmod.Wrapf(clienttypes.ErrInvalidClient, "expected type %T, got %T", (*types.ClientState)(nil), wasmClientState)
}

return wasmClientState, nil
}
Loading

0 comments on commit 768d592

Please sign in to comment.