# `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
}
```