-
Notifications
You must be signed in to change notification settings - Fork 639
Commit
* 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
There are no files selected for viewing
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 GitHub Actions / build (amd64)
Check failure on line 8 in modules/light-clients/08-wasm/keeper/events.go GitHub Actions / build (arm64)
Check failure on line 8 in modules/light-clients/08-wasm/keeper/events.go GitHub Actions / build (arm)
|
||
) | ||
|
||
// 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), | ||
), | ||
}) | ||
} |
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 | ||
} |
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 GitHub Actions / build (amd64)
Check failure on line 10 in modules/light-clients/08-wasm/keeper/keeper.go GitHub Actions / build (arm64)
Check failure on line 10 in modules/light-clients/08-wasm/keeper/keeper.go GitHub Actions / build (arm)
|
||
|
||
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 GitHub Actions / build (amd64)
Check failure on line 18 in modules/light-clients/08-wasm/keeper/keeper.go GitHub Actions / build (arm64)
Check failure on line 18 in modules/light-clients/08-wasm/keeper/keeper.go GitHub Actions / build (arm)
|
||
"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 GitHub Actions / build (amd64)
Check failure on line 20 in modules/light-clients/08-wasm/keeper/keeper.go GitHub Actions / build (arm64)
Check failure on line 20 in modules/light-clients/08-wasm/keeper/keeper.go GitHub Actions / build (arm)
|
||
) | ||
|
||
// 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 | ||
} |