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