diff --git a/modules/light-clients/08-wasm/types/client_message.go b/modules/light-clients/08-wasm/types/client_message.go index 283d663afea..434b1830d9d 100644 --- a/modules/light-clients/08-wasm/types/client_message.go +++ b/modules/light-clients/08-wasm/types/client_message.go @@ -8,7 +8,7 @@ import ( var _ exported.ClientMessage = &ClientMessage{} -// ClientType defines that the client message is a Wasm client consensus algorithm +// ClientType is a Wasm light client. func (c ClientMessage) ClientType() string { return exported.Wasm } diff --git a/modules/light-clients/08-wasm/types/client_state.go b/modules/light-clients/08-wasm/types/client_state.go index 5e12cfe99ea..68eff0b2ed9 100644 --- a/modules/light-clients/08-wasm/types/client_state.go +++ b/modules/light-clients/08-wasm/types/client_state.go @@ -54,13 +54,6 @@ func (cs ClientState) Validate() error { return nil } -type ( - statusInnerPayload struct{} - statusPayload struct { - Status statusInnerPayload `json:"status"` - } -) - // Status returns the status of the wasm client. // The client may be: // - Active: frozen height is zero and client is not expired @@ -71,7 +64,7 @@ type ( // A frozen client will become expired, so the Frozen status // has higher precedence. func (cs ClientState) Status(ctx sdk.Context, clientStore sdk.KVStore, _ codec.BinaryCodec) exported.Status { - payload := statusPayload{Status: statusInnerPayload{}} + payload := queryMsg{Status: &statusMsg{}} result, err := wasmQuery[statusResult](ctx, clientStore, &cs, payload) if err != nil { @@ -87,15 +80,6 @@ func (cs ClientState) ZeroCustomFields() exported.ClientState { return &cs } -type ( - timestampAtHeightInnerPayload struct { - Height exported.Height `json:"height"` - } - timestampAtHeightPayload struct { - TimestampAtHeight timestampAtHeightInnerPayload `json:"timestamp_at_height"` - } -) - // GetTimestampAtHeight returns the timestamp in nanoseconds of the consensus state at the given height. func (cs ClientState) GetTimestampAtHeight( ctx sdk.Context, @@ -103,8 +87,8 @@ func (cs ClientState) GetTimestampAtHeight( cdc codec.BinaryCodec, height exported.Height, ) (uint64, error) { - payload := timestampAtHeightPayload{ - TimestampAtHeight: timestampAtHeightInnerPayload{ + payload := queryMsg{ + TimestampAtHeight: ×tampAtHeightMsg{ Height: height, }, } @@ -117,11 +101,6 @@ func (cs ClientState) GetTimestampAtHeight( return result.Timestamp, nil } -type instantiateMessage struct { - ClientState *ClientState `json:"client_state"` - ConsensusState *ConsensusState `json:"consensus_state"` -} - // Initialize checks that the initial consensus state is an 08-wasm consensus state and // sets the client state, consensus state in the provided client store. // It also initializes the wasm contract for the client. @@ -152,20 +131,6 @@ func (cs ClientState) Initialize(ctx sdk.Context, _ codec.BinaryCodec, clientSto return nil } -type ( - verifyMembershipInnerPayload struct { - Height exported.Height `json:"height"` - DelayTimePeriod uint64 `json:"delay_time_period"` - DelayBlockPeriod uint64 `json:"delay_block_period"` - Proof []byte `json:"proof"` - Path exported.Path `json:"path"` - Value []byte `json:"value"` - } - verifyMembershipPayload struct { - VerifyMembership verifyMembershipInnerPayload `json:"verify_membership"` - } -) - // VerifyMembership is a generic proof verification method which verifies a proof of the existence of a value at a given CommitmentPath at the specified height. // The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). // If a zero proof height is passed in, it will fail to retrieve the associated consensus state. @@ -192,8 +157,8 @@ func (cs ClientState) VerifyMembership( return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected %T, got %T", commitmenttypes.MerklePath{}, path) } - payload := verifyMembershipPayload{ - VerifyMembership: verifyMembershipInnerPayload{ + payload := queryMsg{ + VerifyMembership: &verifyMembershipMsg{ Height: height, DelayTimePeriod: delayTimePeriod, DelayBlockPeriod: delayBlockPeriod, @@ -206,19 +171,6 @@ func (cs ClientState) VerifyMembership( return err } -type ( - verifyNonMembershipInnerPayload struct { - Height exported.Height `json:"height"` - DelayTimePeriod uint64 `json:"delay_time_period"` - DelayBlockPeriod uint64 `json:"delay_block_period"` - Proof []byte `json:"proof"` - Path exported.Path `json:"path"` - } - verifyNonMembershipPayload struct { - VerifyNonMembership verifyNonMembershipInnerPayload `json:"verify_non_membership"` - } -) - // VerifyNonMembership is a generic proof verification method which verifies the absence of a given CommitmentPath at a specified height. // The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). // If a zero proof height is passed in, it will fail to retrieve the associated consensus state. @@ -244,8 +196,8 @@ func (cs ClientState) VerifyNonMembership( return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected %T, got %T", commitmenttypes.MerklePath{}, path) } - payload := verifyNonMembershipPayload{ - VerifyNonMembership: verifyNonMembershipInnerPayload{ + payload := queryMsg{ + VerifyNonMembership: &verifyNonMembershipMsg{ Height: height, DelayTimePeriod: delayTimePeriod, DelayBlockPeriod: delayBlockPeriod, diff --git a/modules/light-clients/08-wasm/types/contract_api.go b/modules/light-clients/08-wasm/types/contract_api.go new file mode 100644 index 00000000000..d0a02fc60f6 --- /dev/null +++ b/modules/light-clients/08-wasm/types/contract_api.go @@ -0,0 +1,124 @@ +package types + +import ( + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" +) + +// instantiateMessage +type instantiateMessage struct { + ClientState *ClientState `json:"client_state"` + ConsensusState *ConsensusState `json:"consensus_state"` +} + +// queryMsg is used to encode query messages +// omitempty tag is mandatory for JSON serialization +// to be compatible with Rust contract enum matching +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"` + VerifyMembership *verifyMembershipMsg `json:"verify_membership,omitempty"` + VerifyNonMembership *verifyNonMembershipMsg `json:"verify_non_membership,omitempty"` + CheckForMisbehaviour *checkForMisbehaviourMsg `json:"check_for_misbehaviour,omitempty"` +} + +type ( + statusMsg struct{} + exportMetadataMsg struct{} + timestampAtHeightMsg struct { + Height exported.Height `json:"height"` + } +) + +type verifyClientMessageMsg struct { + ClientMessage *ClientMessage `json:"client_message"` +} +type verifyMembershipMsg struct { + Height exported.Height `json:"height"` + DelayTimePeriod uint64 `json:"delay_time_period"` + DelayBlockPeriod uint64 `json:"delay_block_period"` + Proof []byte `json:"proof"` + Path exported.Path `json:"path"` + Value []byte `json:"value"` +} +type verifyNonMembershipMsg struct { + Height exported.Height `json:"height"` + DelayTimePeriod uint64 `json:"delay_time_period"` + DelayBlockPeriod uint64 `json:"delay_block_period"` + Proof []byte `json:"proof"` + Path exported.Path `json:"path"` +} +type checkForMisbehaviourMsg struct { + ClientMessage *ClientMessage `json:"client_message"` +} + +// sudoMsg is used to encode sudo messages +// omitempty tag is mandatory for JSON serialization +// to be compatible with Rust contract enum matching +type sudoMsg struct { + UpdateState *updateStateMsg `json:"update_state,omitempty"` + UpdateStateOnMisbehaviour *updateStateOnMisbehaviourMsg `json:"update_state_on_misbehaviour,omitempty"` + VerifyUpgradeAndUpdateState *verifyUpgradeAndUpdateStateMsg `json:"verify_upgrade_and_update_state,omitempty"` + CheckSubstituteAndUpdateState *checkSubstituteAndUpdateStateMsg `json:"check_substitute_and_update_state,omitempty"` +} + +type updateStateMsg struct { + ClientMessage *ClientMessage `json:"client_message"` +} +type updateStateOnMisbehaviourMsg struct { + ClientMessage *ClientMessage `json:"client_message"` +} +type verifyUpgradeAndUpdateStateMsg struct { + UpgradeClientState exported.ClientState `json:"upgrade_client_state"` + UpgradeConsensusState exported.ConsensusState `json:"upgrade_consensus_state"` + ProofUpgradeClient []byte `json:"proof_upgrade_client"` + ProofUpgradeConsensusState []byte `json:"proof_upgrade_consensus_state"` +} +type checkSubstituteAndUpdateStateMsg struct{} + +// ContractResult defines the expected result returned by a contract call +type ContractResult interface { + Validate() bool + Error() string +} + +type contractResult struct { + IsValid bool `json:"is_valid,omitempty"` + ErrorMsg string `json:"error_msg,omitempty"` + Data []byte `json:"data,omitempty"` +} + +func (r contractResult) Validate() bool { + return r.IsValid +} + +func (r contractResult) Error() string { + return r.ErrorMsg +} + +type statusResult struct { + contractResult + Status exported.Status `json:"status"` +} + +type exportMetadataResult struct { + contractResult + GenesisMetadata []clienttypes.GenesisMetadata `json:"genesis_metadata"` +} + +type timestampAtHeightResult struct { + contractResult + Timestamp uint64 `json:"timestamp"` +} + +type checkForMisbehaviourResult struct { + contractResult + FoundMisbehaviour bool `json:"found_misbehaviour"` +} + +type updateStateResult struct { + contractResult + Heights []exported.Height `json:"heights"` +} diff --git a/modules/light-clients/08-wasm/types/errors.go b/modules/light-clients/08-wasm/types/errors.go index 330ecb07cdd..4c2ba1c34cd 100644 --- a/modules/light-clients/08-wasm/types/errors.go +++ b/modules/light-clients/08-wasm/types/errors.go @@ -3,13 +3,14 @@ package types import errorsmod "cosmossdk.io/errors" var ( - ErrInvalid = errorsmod.Register(ModuleName, 1, "invalid") - ErrInvalidData = errorsmod.Register(ModuleName, 2, "invalid data") - ErrInvalidCodeHash = errorsmod.Register(ModuleName, 3, "invalid code hash") + ErrInvalid = errorsmod.Register(ModuleName, 2, "invalid") + ErrInvalidData = errorsmod.Register(ModuleName, 3, "invalid data") + ErrInvalidCodeHash = errorsmod.Register(ModuleName, 4, "invalid code hash") + ErrInvalidClientMessage = errorsmod.Register(ModuleName, 5, "invalid client message") // Wasm specific - ErrWasmEmptyCode = errorsmod.Register(ModuleName, 4, "empty wasm code") - ErrWasmCodeTooLarge = errorsmod.Register(ModuleName, 5, "wasm code too large") - ErrWasmCodeExists = errorsmod.Register(ModuleName, 6, "wasm code already exists") - ErrWasmCodeHashNotFound = errorsmod.Register(ModuleName, 7, "wasm code hash not found") - ErrWasmSubMessagesNotAllowed = errorsmod.Register(ModuleName, 8, "execution of sub messages is not allowed") + ErrWasmEmptyCode = errorsmod.Register(ModuleName, 6, "empty wasm code") + ErrWasmCodeTooLarge = errorsmod.Register(ModuleName, 7, "wasm code too large") + ErrWasmCodeExists = errorsmod.Register(ModuleName, 8, "wasm code already exists") + ErrWasmCodeHashNotFound = errorsmod.Register(ModuleName, 9, "wasm code hash not found") + ErrWasmSubMessagesNotAllowed = errorsmod.Register(ModuleName, 10, "execution of sub messages is not allowed") ) diff --git a/modules/light-clients/08-wasm/types/genesis.go b/modules/light-clients/08-wasm/types/genesis.go index ba66b6f4c0a..6398df09664 100644 --- a/modules/light-clients/08-wasm/types/genesis.go +++ b/modules/light-clients/08-wasm/types/genesis.go @@ -10,13 +10,6 @@ import ( "github.com/cosmos/ibc-go/v7/modules/core/exported" ) -type ( - exportMetadataInnerPayload struct{} - exportMetadataPayload struct { - ExportMetadata exportMetadataInnerPayload `json:"export_metadata"` - } -) - // NewGenesisState creates an 08-wasm GenesisState instance. func NewGenesisState(contracts []Contract) *GenesisState { return &GenesisState{Contracts: contracts} @@ -25,10 +18,12 @@ func NewGenesisState(contracts []Contract) *GenesisState { // ExportMetadata exports all the consensus metadata in the client store so they // can be included in clients genesis and imported by a ClientKeeper func (cs ClientState) ExportMetadata(store sdk.KVStore) []exported.GenesisMetadata { - var payload exportMetadataPayload + payload := queryMsg{ + ExportMetadata: &exportMetadataMsg{}, + } ctx := sdk.NewContext(nil, tmproto.Header{Height: 1, Time: time.Now()}, true, nil) // context with infinite gas meter - result, err := wasmQuery[metadataResult](ctx, store, &cs, payload) + result, err := wasmQuery[exportMetadataResult](ctx, store, &cs, payload) if err != nil { panic(err) } diff --git a/modules/light-clients/08-wasm/types/misbehaviour_handle.go b/modules/light-clients/08-wasm/types/misbehaviour_handle.go index 27756da01d2..0d9015f08d7 100644 --- a/modules/light-clients/08-wasm/types/misbehaviour_handle.go +++ b/modules/light-clients/08-wasm/types/misbehaviour_handle.go @@ -7,15 +7,6 @@ import ( "github.com/cosmos/ibc-go/v7/modules/core/exported" ) -type ( - checkForMisbehaviourInnerPayload struct { - ClientMessage *ClientMessage `json:"client_message"` - } - checkForMisbehaviourPayload struct { - CheckForMisbehaviour checkForMisbehaviourInnerPayload `json:"check_for_misbehaviour"` - } -) - // CheckForMisbehaviour detects misbehaviour in a submitted Header message and verifies // the correctness of a submitted Misbehaviour ClientMessage func (cs ClientState) CheckForMisbehaviour(ctx sdk.Context, _ codec.BinaryCodec, clientStore sdk.KVStore, clientMsg exported.ClientMessage) bool { @@ -24,10 +15,8 @@ func (cs ClientState) CheckForMisbehaviour(ctx sdk.Context, _ codec.BinaryCodec, return false } - payload := checkForMisbehaviourPayload{ - CheckForMisbehaviour: checkForMisbehaviourInnerPayload{ - ClientMessage: clientMessage, - }, + payload := queryMsg{ + CheckForMisbehaviour: &checkForMisbehaviourMsg{ClientMessage: clientMessage}, } result, err := wasmQuery[checkForMisbehaviourResult](ctx, clientStore, &cs, payload) diff --git a/modules/light-clients/08-wasm/types/proposal_handle.go b/modules/light-clients/08-wasm/types/proposal_handle.go index 21f90751143..e543ed494ed 100644 --- a/modules/light-clients/08-wasm/types/proposal_handle.go +++ b/modules/light-clients/08-wasm/types/proposal_handle.go @@ -12,13 +12,6 @@ import ( "github.com/cosmos/ibc-go/v7/modules/core/exported" ) -type ( - checkSubstituteAndUpdateStateInnerPayload struct{} - checkSubstituteAndUpdateStatePayload struct { - CheckSubstituteAndUpdateState checkSubstituteAndUpdateStateInnerPayload `json:"check_substitute_and_update_state"` - } -) - // CheckSubstituteAndUpdateState will try to update the client with the state of the // substitute. func (cs ClientState) CheckSubstituteAndUpdateState( @@ -42,8 +35,8 @@ func (cs ClientState) CheckSubstituteAndUpdateState( store := newUpdateProposalWrappedStore(subjectClientStore, substituteClientStore, SubjectPrefix, SubstitutePrefix) - payload := checkSubstituteAndUpdateStatePayload{ - CheckSubstituteAndUpdateState: checkSubstituteAndUpdateStateInnerPayload{}, + payload := sudoMsg{ + CheckSubstituteAndUpdateState: &checkSubstituteAndUpdateStateMsg{}, } _, err := call[contractResult](ctx, store, &cs, payload) diff --git a/modules/light-clients/08-wasm/types/update.go b/modules/light-clients/08-wasm/types/update.go index 1a99e847dbb..b6d8fbddbeb 100644 --- a/modules/light-clients/08-wasm/types/update.go +++ b/modules/light-clients/08-wasm/types/update.go @@ -14,15 +14,6 @@ import ( var _ exported.ClientState = (*ClientState)(nil) -type ( - verifyClientMessageInnerPayload struct { - ClientMessage *ClientMessage `json:"client_message"` - } - verifyClientMessagePayload struct { - VerifyClientMessage verifyClientMessageInnerPayload `json:"verify_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 @@ -33,24 +24,13 @@ func (cs ClientState) VerifyClientMessage(ctx sdk.Context, _ codec.BinaryCodec, return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected type: %T, got: %T", &ClientMessage{}, clientMsg) } - payload := verifyClientMessagePayload{ - VerifyClientMessage: verifyClientMessageInnerPayload{ - ClientMessage: clientMessage, - }, + payload := queryMsg{ + VerifyClientMessage: &verifyClientMessageMsg{ClientMessage: clientMessage}, } _, err := wasmQuery[contractResult](ctx, clientStore, &cs, payload) return err } -type ( - updateStateInnerPayload struct { - ClientMessage *ClientMessage `json:"client_message"` - } - updateStatePayload struct { - UpdateState updateStateInnerPayload `json:"update_state"` - } -) - // Client state and new consensus states are updated in the store by the contract func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg exported.ClientMessage) []exported.Height { clientMessage, ok := clientMsg.(*ClientMessage) @@ -58,10 +38,8 @@ func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, client panic(fmt.Errorf("expected type %T, got %T", &ClientMessage{}, clientMsg)) } - payload := updateStatePayload{ - UpdateState: updateStateInnerPayload{ - ClientMessage: clientMessage, - }, + payload := sudoMsg{ + UpdateState: &updateStateMsg{ClientMessage: clientMessage}, } result, err := call[updateStateResult](ctx, clientStore, &cs, payload) @@ -72,15 +50,6 @@ func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, client return result.Heights } -type ( - updateStateOnMisbehaviourInnerPayload struct { - ClientMessage *ClientMessage `json:"client_message"` - } - updateStateOnMisbehaviourPayload struct { - UpdateStateOnMisbehaviour updateStateOnMisbehaviourInnerPayload `json:"update_state_on_misbehaviour"` - } -) - // UpdateStateOnMisbehaviour should perform appropriate state changes on a client state given that misbehaviour has been detected and verified // Client state is updated in the store by contract. func (cs ClientState) UpdateStateOnMisbehaviour(ctx sdk.Context, _ codec.BinaryCodec, clientStore sdk.KVStore, clientMsg exported.ClientMessage) { @@ -89,10 +58,8 @@ func (cs ClientState) UpdateStateOnMisbehaviour(ctx sdk.Context, _ codec.BinaryC panic(fmt.Errorf("expected type %T, got %T", &ClientMessage{}, clientMsg)) } - payload := updateStateOnMisbehaviourPayload{ - UpdateStateOnMisbehaviour: updateStateOnMisbehaviourInnerPayload{ - ClientMessage: clientMessage, - }, + payload := sudoMsg{ + UpdateStateOnMisbehaviour: &updateStateOnMisbehaviourMsg{ClientMessage: clientMessage}, } _, err := call[contractResult](ctx, clientStore, &cs, payload) diff --git a/modules/light-clients/08-wasm/types/upgrade.go b/modules/light-clients/08-wasm/types/upgrade.go index a6550ae4031..930361e5dda 100644 --- a/modules/light-clients/08-wasm/types/upgrade.go +++ b/modules/light-clients/08-wasm/types/upgrade.go @@ -11,18 +11,6 @@ import ( "github.com/cosmos/ibc-go/v7/modules/core/exported" ) -type ( - verifyUpgradeAndUpdateStateInnerPayload struct { - UpgradeClientState exported.ClientState `json:"upgrade_client_state"` - UpgradeConsensusState exported.ConsensusState `json:"upgrade_consensus_state"` - ProofUpgradeClient []byte `json:"proof_upgrade_client"` - ProofUpgradeConsensusState []byte `json:"proof_upgrade_consensus_state"` - } - verifyUpgradeAndUpdateStatePayload struct { - VerifyUpgradeAndUpdateState verifyUpgradeAndUpdateStateInnerPayload `json:"verify_upgrade_and_update_state"` - } -) - // VerifyUpgradeAndUpdateState, on a successful verification expects the contract to update // the new client state, consensus state, and any other client metadata. func (cs ClientState) VerifyUpgradeAndUpdateState( @@ -54,8 +42,8 @@ func (cs ClientState) VerifyUpgradeAndUpdateState( upgradedClient.GetLatestHeight(), lastHeight) } - payload := verifyUpgradeAndUpdateStatePayload{ - VerifyUpgradeAndUpdateState: verifyUpgradeAndUpdateStateInnerPayload{ + payload := sudoMsg{ + VerifyUpgradeAndUpdateState: &verifyUpgradeAndUpdateStateMsg{ UpgradeClientState: upgradedClient, UpgradeConsensusState: upgradedConsState, ProofUpgradeClient: proofUpgradeClient, diff --git a/modules/light-clients/08-wasm/types/vm.go b/modules/light-clients/08-wasm/types/vm.go index 5cdb1bb5d71..05bf25222f9 100644 --- a/modules/light-clients/08-wasm/types/vm.go +++ b/modules/light-clients/08-wasm/types/vm.go @@ -6,9 +6,6 @@ import ( storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - - clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v7/modules/core/exported" ) var ( @@ -21,50 +18,6 @@ var ( VMGasRegister = NewDefaultWasmGasRegister() ) -type ContractResult interface { - Validate() bool - Error() string -} - -type contractResult struct { - IsValid bool `json:"is_valid,omitempty"` - ErrorMsg string `json:"error_msg,omitempty"` - Data []byte `json:"data,omitempty"` -} - -func (r contractResult) Validate() bool { - return r.IsValid -} - -func (r contractResult) Error() string { - return r.ErrorMsg -} - -type statusResult struct { - contractResult - Status exported.Status `json:"status"` -} - -type metadataResult struct { - contractResult - GenesisMetadata []clienttypes.GenesisMetadata `json:"genesis_metadata"` -} - -type timestampAtHeightResult struct { - contractResult - Timestamp uint64 `json:"timestamp"` -} - -type checkForMisbehaviourResult struct { - contractResult - FoundMisbehaviour bool `json:"found_misbehaviour"` -} - -type updateStateResult struct { - contractResult - Heights []exported.Height `json:"heights"` -} - // initContract calls vm.Init with appropriate arguments. func initContract(ctx sdk.Context, clientStore sdk.KVStore, codeHash []byte, msg []byte) (*wasmvmtypes.Response, error) { sdkGasMeter := ctx.GasMeter() diff --git a/proto/ibc/lightclients/wasm/v1/wasm.proto b/proto/ibc/lightclients/wasm/v1/wasm.proto index 6e67cf9161c..d43efd9d34b 100644 --- a/proto/ibc/lightclients/wasm/v1/wasm.proto +++ b/proto/ibc/lightclients/wasm/v1/wasm.proto @@ -30,4 +30,4 @@ message ClientMessage { option (gogoproto.goproto_getters) = false; bytes data = 1; -} +} \ No newline at end of file