From e011fecf3eb36075f4bde7d7ea1f3eb9bef4eac1 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 5 Feb 2024 20:18:49 +0100 Subject: [PATCH 1/3] docs: update ADR 27 --- docs/architecture/adr-027-ibc-wasm.md | 105 ++++++++++++-------------- 1 file changed, 47 insertions(+), 58 deletions(-) diff --git a/docs/architecture/adr-027-ibc-wasm.md b/docs/architecture/adr-027-ibc-wasm.md index 2abe7d6d82d..a910e2ba735 100644 --- a/docs/architecture/adr-027-ibc-wasm.md +++ b/docs/architecture/adr-027-ibc-wasm.md @@ -4,10 +4,11 @@ - 26/11/2020: Initial Draft - 26/05/2023: Update after 02-client refactor and re-implementation by Strangelove +- 13/12/2023: Update after upstreaming of module to ibc-go ## Status -*Draft, needs updates* +*Accepted and applied in v0.1.0 of 08-wasm* ## Abstract @@ -23,8 +24,8 @@ corresponding hard-fork event. Currently in ibc-go light clients are defined as part of the codebase and are implemented as modules under `modules/light-clients`. Adding support for new light clients or updating an existing light client in the event of a security issue or consensus update is a multi-step process which is both time consuming and error prone. -In order to enable new IBC light client implementations it is necessary to modify the codebase of ibc-go, -re-build chains' binaries, pass a governance proposal and validators upgrade their nodes. +In order to enable new IBC light client implementations it is necessary to modify the codebase of ibc-go (if the light +client is part of its codebase), re-build chains' binaries, pass a governance proposal and validators upgrade their nodes. Another problem stemming from the above process is that if a chain wants to upgrade its own consensus, it will need to convince every chain or hub connected to it to upgrade its light client in order to stay connected. Due @@ -54,13 +55,15 @@ clientKeeper.SetParams(ctx, params) Adding a new light client contract is governance-gated. To upload a new light client users need to submit a [governance v1 proposal](https://docs.cosmos.network/main/modules/gov#proposals) that contains the `sdk.Msg` for storing -the Wasm contract's bytecode. The required message is `MsgStoreCode` and the bytecode is provided in the field `code`: +the Wasm contract's bytecode. The required message is `MsgStoreCode` and the bytecode is provided in the field `wasm_byte_code`: ```proto // MsgStoreCode defines the request type for the StoreCode rpc. message MsgStoreCode { + // signer address string signer = 1; - bytes code = 2; + // wasm byte code of light client contract. It can be raw or gzip compressed + bytes wasm_byte_code = 2; } ``` @@ -70,36 +73,28 @@ submit this message (which is normally the address of the governance module). ```go // StoreCode defines a rpc handler method for MsgStoreCode func (k Keeper) StoreCode(goCtx context.Context, msg *types.MsgStoreCode) (*types.MsgStoreCodeResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - - if k.authority != msg.Signer { - return nil, sdkerrors.Wrapf(govtypes.ErrInvalidSigner, "invalid authority: expected %s, got %s", k.authority, msg.Signer) + if k.GetAuthority() != msg.Signer { + return nil, errorsmod.Wrapf(ibcerrors.ErrUnauthorized, "expected %s, got %s", k.GetAuthority(), msg.Signer) } - codeHash, err := k.storeWasmCode(ctx, msg.Code) + ctx := sdk.UnwrapSDKContext(goCtx) + checksum, err := k.storeWasmCode(ctx, msg.WasmByteCode, ibcwasm.GetVM().StoreCode) if err != nil { - return nil, sdkerrors.Wrap(err, "storing wasm code failed") + return nil, errorsmod.Wrap(err, "failed to store wasm bytecode") } - ctx.EventManager().EmitEvents(sdk.Events{ - sdk.NewEvent( - clienttypes.EventTypeStoreWasmCode, - sdk.NewAttribute(clienttypes.AttributeKeyWasmCodeHash, hex.EncodeToString(codeHash)), - ), - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, clienttypes.AttributeValueCategory), - ), - }) + emitStoreWasmCodeEvent(ctx, checksum) return &types.MsgStoreCodeResponse{ - CodeHash: codeHash, + Checksum: checksum, }, nil } ``` -The contract's bytecode is stored in state in an entry indexed by the code hash: `codeHash/{code hash}`. The code hash is simply -the hash of the bytecode of the contract. +The contract's bytecode is not stored in state (it is actually unnecessary and wasteful to store it, since +the Wasm VM already stores it and can be queried back, if needed). The checksum is simply the hash of the bytecode +of the contract and it is stored in state in an entry with key `checksums` that contains a list of the checksums +of bytecodes that have been stored. ### How light client proxy works? @@ -108,50 +103,44 @@ in JSON format with appropriate environment information. Data returned by the sm returned to the caller. Consider the example of the `VerifyClientMessage` function of `ClientState` interface. Incoming arguments are -packaged inside a payload object that is then JSON serialized and passed to `callContract`, which execute `WasmVm.Execute` +packaged inside a payload object that is then JSON serialized and passed to `queryContract`, which executes `WasmVm.Query` and returns the slice of bytes returned by the smart contract. This data is deserialized and passed as return argument. ```go -type ( - verifyClientMessageInnerPayload struct { - ClientMessage clientMessage `json:"client_message"` - } - clientMessage struct { - Header *Header `json:"header,omitempty"` - Misbehaviour *Misbehaviour `json:"misbehaviour,omitempty"` - } - verifyClientMessagePayload struct { - VerifyClientMessage verifyClientMessageInnerPayload `json:"verify_client_message"` - } -) +type QueryMsg struct { + Status *StatusMsg `json:"status,omitempty"` + ExportMetadata *ExportMetadataMsg `json:"export_metadata,omitempty"` + TimestampAtHeight *TimestampAtHeightMsg `json:"timestamp_at_height,omitempty"` + VerifyClientMessage *VerifyClientMessageMsg `json:"verify_client_message,omitempty"` + CheckForMisbehaviour *CheckForMisbehaviourMsg `json:"check_for_misbehaviour,omitempty"` +} + +type verifyClientMessageMsg struct { + ClientMessage *ClientMessage `json:"client_message"` +} -// VerifyClientMessage must verify a ClientMessage. A ClientMessage could be a Header, Misbehaviour, or batch update. -// It must handle each type of ClientMessage appropriately. Calls to CheckForMisbehaviour, UpdateState, and UpdateStateOnMisbehaviour -// will assume that the content of the ClientMessage has been verified and can be trusted. An error should be returned +// VerifyClientMessage must verify a ClientMessage. +// A ClientMessage could be a Header, Misbehaviour, or batch update. +// It must handle each type of ClientMessage appropriately. +// Calls to CheckForMisbehaviour, UpdateStaåte, and UpdateStateOnMisbehaviour +// will assume that the content of the ClientMessage has been verified +// and can be trusted. An error should be returned // if the ClientMessage fails to verify. func (cs ClientState) VerifyClientMessage( - ctx sdk.Context, - _ codec.BinaryCodec, - clientStore sdk.KVStore, + ctx sdk.Context, + _ codec.BinaryCodec, + clientStore storetypes.KVStore, clientMsg exported.ClientMessage ) error { - clientMsgConcrete := clientMessage{ - Header: nil, - Misbehaviour: nil, + clientMessage, ok := clientMsg.(*ClientMessage) + if !ok { + return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected type: %T, got: %T", &ClientMessage{}, clientMsg) } - switch clientMsg := clientMsg.(type) { - case *Header: - clientMsgConcrete.Header = clientMsg - case *Misbehaviour: - clientMsgConcrete.Misbehaviour = clientMsg - } - inner := verifyClientMessageInnerPayload{ - ClientMessage: clientMsgConcrete, - } - payload := verifyClientMessagePayload{ - VerifyClientMessage: inner, + + payload := QueryMsg{ + VerifyClientMessage: &VerifyClientMessageMsg{ClientMessage: clientMessage.Data}, } - _, err := call[contractResult](ctx, clientStore, &cs, payload) + _, err := wasmQuery[EmptyResult](ctx, clientStore, &cs, payload) return err } ``` From 59dcfb9e0ca2cfce66b32d20889588bc1c2cbf19 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 11 Mar 2024 19:27:03 +0100 Subject: [PATCH 2/3] Update docs/architecture/adr-027-ibc-wasm.md Co-authored-by: DimitrisJim --- docs/architecture/adr-027-ibc-wasm.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/architecture/adr-027-ibc-wasm.md b/docs/architecture/adr-027-ibc-wasm.md index a910e2ba735..caae924aa76 100644 --- a/docs/architecture/adr-027-ibc-wasm.md +++ b/docs/architecture/adr-027-ibc-wasm.md @@ -93,8 +93,7 @@ func (k Keeper) StoreCode(goCtx context.Context, msg *types.MsgStoreCode) (*type The contract's bytecode is not stored in state (it is actually unnecessary and wasteful to store it, since the Wasm VM already stores it and can be queried back, if needed). The checksum is simply the hash of the bytecode -of the contract and it is stored in state in an entry with key `checksums` that contains a list of the checksums -of bytecodes that have been stored. +of the contract and it is stored in state in an entry with key `checksums` that contains the checksums for the bytecodes that have been stored. ### How light client proxy works? From 3af85080093eae205ff3bd66776e843c602d02c1 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 11 Mar 2024 20:00:35 +0100 Subject: [PATCH 3/3] Apply suggestions from code review Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- docs/architecture/adr-027-ibc-wasm.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/architecture/adr-027-ibc-wasm.md b/docs/architecture/adr-027-ibc-wasm.md index caae924aa76..1a2a1e8a8a9 100644 --- a/docs/architecture/adr-027-ibc-wasm.md +++ b/docs/architecture/adr-027-ibc-wasm.md @@ -23,7 +23,7 @@ corresponding hard-fork event. Currently in ibc-go light clients are defined as part of the codebase and are implemented as modules under `modules/light-clients`. Adding support for new light clients or updating an existing light client in the event -of a security issue or consensus update is a multi-step process which is both time consuming and error prone. +of a security issue or consensus update is a multi-step process which is both time-consuming and error-prone. In order to enable new IBC light client implementations it is necessary to modify the codebase of ibc-go (if the light client is part of its codebase), re-build chains' binaries, pass a governance proposal and validators upgrade their nodes.