---
# System prepended metadata

title: '`08-wasm` lightclient module using `x/wasm`'

---

# `08-wasm` lightclient module using `x/wasm`

A second opinion:

> The wasm client implementation needs tighter integration with ibc-go and the 02-client implementation. While its undesirable to treat certain clients as a special case it becomes difficult to integrate with `x/wasm` cleanly through the ibc `ClientState` interface, as ibc clients are generally protobuf msg structs.

## The problem

The `ClientState` interface and implementation are just plain objects/structs and have no ability to call into the wasm vm runtime. For example when calling [`Initialise()`](https://github.com/cosmos/ibc-go/blob/main/modules/core/02-client/keeper/client.go#L34-L36) here you lose the ability to pass any kind of wasm enabled invocator to the client state, you are forced to rely on pkg level vars.  

## Solutions

Currently the work-in-progress implementation uses a package level wasm VM and doesn't integrate with `x/wasm`. 

Alternative approach:

Integrate properly with `x/wasm` and use a PermissionedKeeper to provide access control.
Attach a permissioned wasm keeper to the application context using `context.WithValue(...)` to create a "wasm runtime context"

---

## Integrate with `x/wasm` instead of using a second VM

IBC core should accept a permissioned wasm keeper with custom wasm `AuthorizationPolicy` which meets the needs of wasm client integration. Maybe the `GovAuthorizationPolicy` works? Needs review.

- Create a permissioned keeper with custom implementation of wasm [`AuthorizationPolicy`](https://github.com/CosmWasm/wasmd/blob/88e01a98ab8a87b98dc26c03715e6aef5c92781b/x/wasm/keeper/authz_policy.go#L20) for IBC clients. E.g. `IBCAuthorizationPolicy` or `IBCClientAuthorizationPolicy`

- Pass the permissioned keeper to core IBC [`NewKeeper(...args)`]()

```go
func NewKeeper(
	cdc codec.BinaryCodec, key storetypes.StoreKey, paramSpace paramtypes.Subspace,
	stakingKeeper clienttypes.StakingKeeper, upgradeKeeper clienttypes.UpgradeKeeper,
	scopedKeeper capabilitykeeper.ScopedKeeper, permissionedKeeper clienttypes.PermissionedWasmKeeper,
) *Keeper { ... }
```

Expected keeper:
```go
type PermissionedWasmKeeper interface {
    Create(
        ctx sdk.Context, 
        creator sdk.AccAddress, 
        wasmCode []byte, 
        instantiateAccess *types.AccessConfig,
    ) (codeID uint64, checksum []byte, err error)
    
    Instantiate(
        ctx sdk.Context,
        codeID uint64,
        creator, admin sdk.AccAddress,
        initMsg []byte,
        label string,
        deposit sdk.Coins,
    ) (sdk.AccAddress, []byte, error)
    
    Execute(
        ctx sdk.Context, 
        contractAddress sdk.AccAddress, 
        caller sdk.AccAddress, 
        msg []byte, 
        coins sdk.Coins,
    ) ([]byte, error)
    
    // ...etc 
}
```

- Create new rpc handler in 02-client, leverage `authority` field for gov signer.
```protobuf
rpc RegisterWasmClient(MsgRegisterWasmClient) returns (MsgRegisterWasmClientResponse)
```

Actor submits v1 gov proposal with `sdk.Msg` execution for registering a new wasm client implementation (i.e. storing the code using the ibc permissioned wasm keeper - `permissionedWasmKeeper.Create(...)`):


--- 

Wasm code is now stored and availabe, so any actor should be able to instantiate a new `ClientState` instance.

- Pass the wasm permissioned keeper by creating a new wasm enabled execution context

```go
func (k Keeper) CreateClient(
	ctx sdk.Context, clientState exported.ClientState, consensusState exported.ConsensusState,
) (string, error) {
    // ...    
    clientID := k.GenerateClientIdentifier(ctx, clientState.ClientType())
    clientStore := k.ClientStore(ctx, clientID)

    if clientState.ClientType() == exported.Wasm {
        ctx = ctx.WithValue(ctx, wasm.ContextKey, k.permissionedKeeper)
    }

    if err := clientState.Initialize(ctx, k.cdc, clientStore, consensusState); err != nil {
        return "", err
    }
    // ...
}

// =====================================================================
// package wasm (08-wasm)
// =====================================================================

// ...maybe use an interface type, can also omit Create() ?
type WasmClientExecutor interface {
	// functionality required from wasmd to enable ibc light-client invocation
    Instatiate(...)
    
    Execute(...)
}
```

---

Within the wasm light client module you can then retrieve the `WasmClientExecutor` from the `sdk.Context` in `Initialize()`, `VerifyMembership`, `VerifyNonMembership`...etc and do what ya gotta do..

```go
func (cs ClientState) Initialize(ctx sdk.Context, cdc codec.BinaryCodec, store sdk.KVStore, consensusState exported.ConsensusState) error {
    wasmConsensusState, ok := consensusState.(*ConsensusState)
    if !ok {
    	return sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "invalid initial consensus state. expected type: %T, got: %T",
	    &ConsensusState{}, consensusState)
    }
	
    setClientState(store, cdc, &cs)
    setConsensusState(store, cdc, wasmConsensusState, cs.GetLatestHeight())
    
    wasmExecutor, ok := unwrapWasmContext(ctx)
    if !ok {
        return sdkerrors.Wrap(sdkerrors.InvalidType, "failed to unwrap wasm execution context")
    }
    
    address, res, err := wasmExecutor.Instantiate(ctx, cs.CodeId, <CREATOR_ADDR>, <ADMIN_ADDR>, initMsg, label, deposit)
    if err != nil {
        return err
    }
    
    // ...
    return nil
}
```