From b7c439a7daab63c110fdd797e1363c64c9696fff Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Thu, 14 Jul 2022 16:14:09 +0300 Subject: [PATCH 01/44] WIP: Start to implement IBC receive hooks Here specifically is ibc-open-channel --- go-cosmwasm/types/types.go | 6 + x/compute/internal/keeper/keeper.go | 14 +- x/compute/internal/keeper/relay.go | 241 ++++++++++++++++++++++++++++ x/compute/internal/types/errors.go | 3 - 4 files changed, 255 insertions(+), 9 deletions(-) create mode 100644 x/compute/internal/keeper/relay.go diff --git a/go-cosmwasm/types/types.go b/go-cosmwasm/types/types.go index 9c8713186..83d166bb4 100644 --- a/go-cosmwasm/types/types.go +++ b/go-cosmwasm/types/types.go @@ -72,6 +72,12 @@ type HandleType int const ( HandleTypeExecute HandleType = iota HandleTypeReply + HandleTypeIbcChannelOpen + HandleTypeIbcChannelConnect + HandleTypeIbcChannelClose + HandleTypeIbcPacketReceive + HandleTypeIbcPacketAck + HandleTypeIbcPacketTimeout ) type CosmosMsgVersion int diff --git a/x/compute/internal/keeper/keeper.go b/x/compute/internal/keeper/keeper.go index 72dc060d2..6215d155a 100644 --- a/x/compute/internal/keeper/keeper.go +++ b/x/compute/internal/keeper/keeper.go @@ -440,6 +440,7 @@ func (k Keeper) Instantiate(ctx sdk.Context, codeID uint64, creator sdk.AccAddre gas := gasForContract(ctx) response, key, gasUsed, err := k.wasmer.Instantiate(codeInfo.CodeHash, env, initMsg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, verificationInfo, contractAddress) consumeGas(ctx, gasUsed) + if err != nil { switch res := response.(type) { case v1wasmTypes.DataWithInternalReplyInfo: @@ -659,7 +660,7 @@ func (k Keeper) querySmartImpl(ctx sdk.Context, contractAddr sdk.AccAddress, req params := types.NewEnv( ctx, sdk.AccAddress{}, /* empty because it's unused in queries */ - []sdk.Coin{}, /* empty because it's unused in queries */ + sdk.NewCoins(), /* empty because it's unused in queries */ contractAddr, contractKey, ) @@ -1082,15 +1083,16 @@ func (k Keeper) reply(ctx sdk.Context, contractAddress sdk.AccAddress, reply v1w } response, gasUsed, execErr := k.wasmer.Execute(codeInfo.CodeHash, env, marshaledReply, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, ogSigInfo, wasmTypes.HandleTypeReply) + consumeGas(ctx, gasUsed) + if execErr != nil { - return nil, sdkerrors.Wrap(types.ErrReplyFailed, execErr.Error()) + return nil, sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) } switch res := response.(type) { case *v010wasmTypes.HandleResponse: - return nil, sdkerrors.Wrap(types.ErrReplyFailed, fmt.Sprintf("response of reply should always be a CosmWasm v1 response type: %+v", res)) + return nil, sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("response of reply should always be a CosmWasm v1 response type: %+v", res)) case *v1wasmTypes.Response: - consumeGas(ctx, gasUsed) ctx.EventManager().EmitEvent(sdk.NewEvent( types.EventTypeReply, @@ -1099,11 +1101,11 @@ func (k Keeper) reply(ctx sdk.Context, contractAddress sdk.AccAddress, reply v1w data, err := k.handleContractResponse(ctx, contractAddress, contractInfo.IBCPortID, res.Messages, res.Attributes, res.Events, res.Data, ogTx, ogSigInfo, wasmTypes.CosmosMsgVersionV1) if err != nil { - return nil, sdkerrors.Wrap(types.ErrReplyFailed, err.Error()) + return nil, sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } return data, nil default: - return nil, sdkerrors.Wrap(types.ErrReplyFailed, fmt.Sprintf("cannot detect response type: %+v", res)) + return nil, sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("cannot detect response type: %+v", res)) } } diff --git a/x/compute/internal/keeper/relay.go b/x/compute/internal/keeper/relay.go new file mode 100644 index 000000000..587f5a14b --- /dev/null +++ b/x/compute/internal/keeper/relay.go @@ -0,0 +1,241 @@ +package keeper + +import ( + "encoding/json" + "time" + + "github.com/cosmos/cosmos-sdk/telemetry" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + sdktxsigning "github.com/cosmos/cosmos-sdk/types/tx/signing" + wasmTypes "github.com/enigmampc/SecretNetwork/go-cosmwasm/types" + v1types "github.com/enigmampc/SecretNetwork/go-cosmwasm/types/v1" + + "github.com/enigmampc/SecretNetwork/x/compute/internal/types" +) + +var _ types.IBCContractKeeper = (*Keeper)(nil) + +// OnOpenChannel calls the contract to participate in the IBC channel handshake step. +// In the IBC protocol this is either the `Channel Open Init` event on the initiating chain or +// `Channel Open Try` on the counterparty chain. +// Protocol version and channel ordering should be verified for example. +// See https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#channel-lifecycle-management +func (k Keeper) OnOpenChannel( + ctx sdk.Context, + contractAddress sdk.AccAddress, + msg v1types.IBCChannelOpenMsg, +) (string, error) { + defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-open-channel") + version := "" + + ctx.GasMeter().ConsumeGas(types.InstanceCost, "Loading Compute module: ibc-open-channel") + + verificationInfo := types.NewVerificationInfo([]byte{}, sdktxsigning.SignMode_SIGN_MODE_UNSPECIFIED, []byte{}, []byte{}, []byte{}, nil) + + _, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddress) + if err != nil { + return "", err + } + + store := ctx.KVStore(k.storeKey) + + contractKey := store.Get(types.GetContractEnclaveKey(contractAddress)) + env := types.NewEnv( + ctx, + sdk.AccAddress{}, /* empty because it's unused in queries */ + sdk.NewCoins(), /* empty because it's unused in queries */ + contractAddress, + contractKey, + ) + + // prepare querier + querier := QueryHandler{ + Ctx: ctx, + Plugins: k.queryPlugins, + } + + msgBz, err := json.Marshal(msg) + if err != nil { + return "", sdkerrors.Wrap(err, "ibc-open-channel") + } + + gas := gasForContract(ctx) + res, gasUsed, err := k.wasmer.Execute(codeInfo.CodeHash, env, msgBz, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, verificationInfo, wasmTypes.HandleTypeReply) + consumeGas(ctx, gasUsed) + + if err != nil { + return "", sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) + } + + ///////// + res, gasUsed, execErr := k.wasmVM.IBCChannelOpen(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) + + if execErr != nil { + return "", sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + } + + if res != nil { + version = res.Version + } + + return version, nil +} + +// OnConnectChannel calls the contract to let it know the IBC channel was established. +// In the IBC protocol this is either the `Channel Open Ack` event on the initiating chain or +// `Channel Open Confirm` on the counterparty chain. +// +// There is an open issue with the [cosmos-sdk](https://github.com/cosmos/cosmos-sdk/issues/8334) +// that the counterparty channelID is empty on the initiating chain +// See https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#channel-lifecycle-management +func (k Keeper) OnConnectChannel( + ctx sdk.Context, + contractAddr sdk.AccAddress, + msg v1types.IBCChannelConnectMsg, +) error { + defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-connect-channel") + contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) + if err != nil { + return err + } + + env := types.NewEnv(ctx, contractAddr) + querier := k.newQueryHandler(ctx, contractAddr) + + gas := k.runtimeGasForContract(ctx) + res, gasUsed, execErr := k.wasmVM.IBCChannelConnect(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) + k.consumeRuntimeGas(ctx, gasUsed) + if execErr != nil { + return sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + } + + return k.handleIBCBasicContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res) +} + +// OnCloseChannel calls the contract to let it know the IBC channel is closed. +// Calling modules MAY atomically execute appropriate application logic in conjunction with calling chanCloseConfirm. +// +// Once closed, channels cannot be reopened and identifiers cannot be reused. Identifier reuse is prevented because +// we want to prevent potential replay of previously sent packets +// See https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#channel-lifecycle-management +func (k Keeper) OnCloseChannel( + ctx sdk.Context, + contractAddr sdk.AccAddress, + msg v1types.IBCChannelCloseMsg, +) error { + defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-close-channel") + + contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) + if err != nil { + return err + } + + params := types.NewEnv(ctx, contractAddr) + querier := k.newQueryHandler(ctx, contractAddr) + + gas := k.runtimeGasForContract(ctx) + res, gasUsed, execErr := k.wasmVM.IBCChannelClose(codeInfo.CodeHash, params, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) + k.consumeRuntimeGas(ctx, gasUsed) + if execErr != nil { + return sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + } + + return k.handleIBCBasicContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res) +} + +// OnRecvPacket calls the contract to process the incoming IBC packet. The contract fully owns the data processing and +// returns the acknowledgement data for the chain level. This allows custom applications and protocols on top +// of IBC. Although it is recommended to use the standard acknowledgement envelope defined in +// https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#acknowledgement-envelope +// +// For more information see: https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#packet-flow--handling +func (k Keeper) OnRecvPacket( + ctx sdk.Context, + contractAddr sdk.AccAddress, + msg v1types.IBCPacketReceiveMsg, +) ([]byte, error) { + defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-recv-packet") + contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) + if err != nil { + return nil, err + } + + env := types.NewEnv(ctx, contractAddr) + querier := k.newQueryHandler(ctx, contractAddr) + + gas := k.runtimeGasForContract(ctx) + res, gasUsed, execErr := k.wasmVM.IBCPacketReceive(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) + k.consumeRuntimeGas(ctx, gasUsed) + if execErr != nil { + return nil, sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + } + if res.Err != "" { // handle error case as before https://github.com/CosmWasm/wasmvm/commit/c300106fe5c9426a495f8e10821e00a9330c56c6 + return nil, sdkerrors.Wrap(types.ErrExecuteFailed, res.Err) + } + // note submessage reply results can overwrite the `Acknowledgement` data + return k.handleContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res.Ok.Messages, res.Ok.Attributes, res.Ok.Acknowledgement, res.Ok.Events) +} + +// OnAckPacket calls the contract to handle the "acknowledgement" data which can contain success or failure of a packet +// acknowledgement written on the receiving chain for example. This is application level data and fully owned by the +// contract. The use of the standard acknowledgement envelope is recommended: https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#acknowledgement-envelope +// +// On application errors the contract can revert an operation like returning tokens as in ibc-transfer. +// +// For more information see: https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#packet-flow--handling +func (k Keeper) OnAckPacket( + ctx sdk.Context, + contractAddr sdk.AccAddress, + msg v1types.IBCPacketAckMsg, +) error { + defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-ack-packet") + contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) + if err != nil { + return err + } + + env := types.NewEnv(ctx, contractAddr) + querier := k.newQueryHandler(ctx, contractAddr) + + gas := k.runtimeGasForContract(ctx) + res, gasUsed, execErr := k.wasmVM.IBCPacketAck(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) + k.consumeRuntimeGas(ctx, gasUsed) + if execErr != nil { + return sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + } + return k.handleIBCBasicContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res) +} + +// OnTimeoutPacket calls the contract to let it know the packet was never received on the destination chain within +// the timeout boundaries. +// The contract should handle this on the application level and undo the original operation +func (k Keeper) OnTimeoutPacket( + ctx sdk.Context, + contractAddr sdk.AccAddress, + msg v1types.IBCPacketTimeoutMsg, +) error { + defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-timeout-packet") + + contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) + if err != nil { + return err + } + + env := types.NewEnv(ctx, contractAddr) + querier := k.newQueryHandler(ctx, contractAddr) + + gas := k.runtimeGasForContract(ctx) + res, gasUsed, execErr := k.wasmVM.IBCPacketTimeout(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) + k.consumeRuntimeGas(ctx, gasUsed) + if execErr != nil { + return sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + } + + return k.handleIBCBasicContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res) +} + +func (k Keeper) handleIBCBasicContractResponse(ctx sdk.Context, addr sdk.AccAddress, id string, res *v1types.IBCBasicResponse) error { + _, err := k.handleContractResponse(ctx, addr, id, res.Messages, res.Attributes, nil, res.Events) + return err +} diff --git a/x/compute/internal/types/errors.go b/x/compute/internal/types/errors.go index 7c7dfff50..1cd214613 100644 --- a/x/compute/internal/types/errors.go +++ b/x/compute/internal/types/errors.go @@ -64,9 +64,6 @@ var ( // ErrUnknownMsg error by a message handler to show that it is not responsible for this message type ErrUnknownMsg = sdkErrors.Register(DefaultCodespace, 18, "unknown message from the contract") - // ErrReplyFailed error for rust execution contract failure - ErrReplyFailed = sdkErrors.Register(DefaultCodespace, 19, "reply to contract failed") - // ErrInvalidEvent error if an attribute/event from the contract is invalid ErrInvalidEvent = sdkErrors.Register(DefaultCodespace, 21, "invalid event") ) From f769b836cfaa161b1ccee21d1532b2d30ed71782 Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Tue, 19 Jul 2022 15:17:59 +0300 Subject: [PATCH 02/44] CosmWasm v1 IBC OnOpenChannel --- go-cosmwasm/lib.go | 58 +++++++++++--------- go-cosmwasm/types/v1/ibc.go | 30 ++++++---- x/compute/internal/keeper/relay.go | 22 ++++---- x/compute/internal/types/exported_keepers.go | 46 ++++++++++++++++ 4 files changed, 107 insertions(+), 49 deletions(-) create mode 100644 x/compute/internal/types/exported_keepers.go diff --git a/go-cosmwasm/lib.go b/go-cosmwasm/lib.go index a14ae2d61..523e6fdaa 100644 --- a/go-cosmwasm/lib.go +++ b/go-cosmwasm/lib.go @@ -85,11 +85,17 @@ func (w *Wasmer) GetCode(code CodeID) (WasmCode, error) { } // This struct helps us to distinguish between v0.10 contract response and v1 contract response -type V010orV1ContractExecResponse struct { - V1 *V1ContractExecResponse `json:"v1,omitempty"` - V010 *V010ContractExecResponse `json:"v010,omitempty"` - InternaReplyEnclaveSig []byte `json:"internal_reply_enclave_sig"` - InternalMsgId []byte `json:"internal_msg_id"` +type ContractExecResponse struct { + V1 *V1ContractExecResponse `json:"v1,omitempty"` + V010 *V010ContractExecResponse `json:"v010,omitempty"` + InternaReplyEnclaveSig []byte `json:"internal_reply_enclave_sig"` + InternalMsgId []byte `json:"internal_msg_id"` + IBCChannelOpen *v1types.IBCChannelOpenResult `json:"ibc_channel_open,omitempty"` + IBCChannelConnect *v1types.IBCBasicResponse `json:"ibc_channel_connect,omitempty"` + IBCChannelClose *v1types.IBCBasicResponse `json:"ibc_channel_close,omitempty"` + IBCPacketReceive *v1types.IBCReceiveResult `json:"ibc_packet_receive,omitempty"` + IBCPacketAck *v1types.IBCBasicResponse `json:"ibc_packet_ack,omitempty"` + IBCPacketTimeout *v1types.IBCBasicResponse `json:"ibc_packet_timeout,omitempty"` } type V010ContractExecResponse struct { @@ -260,55 +266,55 @@ func (w *Wasmer) Execute( return nil, gasUsed, err } - var respV010orV1 V010orV1ContractExecResponse - err = json.Unmarshal(data, &respV010orV1) + var resp ContractExecResponse + err = json.Unmarshal(data, &resp) if err != nil { // unidentified response 🤷 return nil, gasUsed, fmt.Errorf("handle: cannot parse response from json: %w", err) } - isOutputAddressedToReply := (len(respV010orV1.InternaReplyEnclaveSig) > 0 && len(respV010orV1.InternalMsgId) > 0) + isOutputAddressedToReply := (len(resp.InternaReplyEnclaveSig) > 0 && len(resp.InternalMsgId) > 0) // handle v0.10 response - if respV010orV1.V010 != nil { - if respV010orV1.V010.Err != nil { + if resp.V010 != nil { + if resp.V010.Err != nil { return v1types.DataWithInternalReplyInfo{ - InternalMsgId: respV010orV1.InternalMsgId, - InternaReplyEnclaveSig: respV010orV1.InternaReplyEnclaveSig, - Data: []byte(respV010orV1.V010.Err.GenericErr.Msg), - }, gasUsed, fmt.Errorf("%+v", respV010orV1.V010.Err) + InternalMsgId: resp.InternalMsgId, + InternaReplyEnclaveSig: resp.InternaReplyEnclaveSig, + Data: []byte(resp.V010.Err.GenericErr.Msg), + }, gasUsed, fmt.Errorf("%+v", resp.V010.Err) } - if respV010orV1.V010.Ok != nil { + if resp.V010.Ok != nil { if isOutputAddressedToReply { - respV010orV1.V010.Ok.Data, err = AppendReplyInternalDataToData(respV010orV1.V010.Ok.Data, respV010orV1.InternaReplyEnclaveSig, respV010orV1.InternalMsgId) + resp.V010.Ok.Data, err = AppendReplyInternalDataToData(resp.V010.Ok.Data, resp.InternaReplyEnclaveSig, resp.InternalMsgId) if err != nil { return nil, gasUsed, fmt.Errorf("cannot serialize v010 DataWithInternalReplyInfo into binary : %w", err) } } - return respV010orV1.V010.Ok, gasUsed, nil + return resp.V010.Ok, gasUsed, nil } } // handle v1 response - if respV010orV1.V1 != nil { - if respV010orV1.V1.Err != nil { + if resp.V1 != nil { + if resp.V1.Err != nil { return v1types.DataWithInternalReplyInfo{ - InternalMsgId: respV010orV1.InternalMsgId, - InternaReplyEnclaveSig: respV010orV1.InternaReplyEnclaveSig, - Data: []byte(respV010orV1.V1.Err.GenericErr.Msg), - }, gasUsed, fmt.Errorf("%+v", respV010orV1.V1.Err) + InternalMsgId: resp.InternalMsgId, + InternaReplyEnclaveSig: resp.InternaReplyEnclaveSig, + Data: []byte(resp.V1.Err.GenericErr.Msg), + }, gasUsed, fmt.Errorf("%+v", resp.V1.Err) } - if respV010orV1.V1.Ok != nil { + if resp.V1.Ok != nil { if isOutputAddressedToReply { - respV010orV1.V1.Ok.Data, err = AppendReplyInternalDataToData(respV010orV1.V1.Ok.Data, respV010orV1.InternaReplyEnclaveSig, respV010orV1.InternalMsgId) + resp.V1.Ok.Data, err = AppendReplyInternalDataToData(resp.V1.Ok.Data, resp.InternaReplyEnclaveSig, resp.InternalMsgId) if err != nil { return nil, gasUsed, fmt.Errorf("cannot serialize v1 DataWithInternalReplyInfo into binary : %w", err) } } - return respV010orV1.V1.Ok, gasUsed, nil + return resp.V1.Ok, gasUsed, nil } } diff --git a/go-cosmwasm/types/v1/ibc.go b/go-cosmwasm/types/v1/ibc.go index 87b60a361..1d5d994fb 100644 --- a/go-cosmwasm/types/v1/ibc.go +++ b/go-cosmwasm/types/v1/ibc.go @@ -1,8 +1,6 @@ package v1types -import ( - abci "github.com/tendermint/tendermint/abci/types" -) +import v010msgtypes "github.com/enigmampc/SecretNetwork/go-cosmwasm/types/v010" type IBCEndpoint struct { PortID string `json:"port_id"` @@ -137,16 +135,19 @@ func (m *IBCCloseConfirm) ToMsg() IBCChannelCloseMsg { } type IBCPacketReceiveMsg struct { - Packet IBCPacket `json:"packet"` + Packet IBCPacket `json:"packet"` + Relayer string `json:"relayer"` } type IBCPacketAckMsg struct { Acknowledgement IBCAcknowledgement `json:"acknowledgement"` OriginalPacket IBCPacket `json:"original_packet"` + Relayer string `json:"relayer"` } type IBCPacketTimeoutMsg struct { - Packet IBCPacket `json:"packet"` + Packet IBCPacket `json:"packet"` + Relayer string `json:"relayer"` } // TODO: test what the sdk Order.String() represents and how to parse back @@ -196,10 +197,17 @@ type IBCPacket struct { // IBCChannelOpenResult is the raw response from the ibc_channel_open call. // This is mirrors Rust's ContractResult<()>. -// We just check if Err == "" to see if this is success (no other data on success) +// Check if Err == "" to see if this is success +// On Success, IBCV3ChannelOpenResponse *may* be set if the contract is ibcv3 compatible and wishes to +// define a custom version in the handshake. type IBCChannelOpenResult struct { - Ok *struct{} `json:"ok,omitempty"` - Err string `json:"error,omitempty"` + Ok *IBC3ChannelOpenResponse `json:"ok,omitempty"` + Err string `json:"error,omitempty"` +} + +// IBC3ChannelOpenResponse is version negotiation data for the handshake +type IBC3ChannelOpenResponse struct { + Version string `json:"version"` } // This is the return value for the majority of the ibc handlers. @@ -223,7 +231,7 @@ type IBCBasicResponse struct { // "fire and forget". Messages []SubMsg `json:"messages"` // attributes for a log event to return over abci interface - Attributes []abci.EventAttribute `json:"attributes"` + Attributes []v010msgtypes.LogAttribute `json:"attributes"` // custom events (separate from the main one that contains the attributes // above) Events []Event `json:"events"` @@ -254,8 +262,8 @@ type IBCReceiveResponse struct { // If the ReplyOn value matches the result, the runtime will invoke this // contract's `reply` entry point after execution. Otherwise, this is all // "fire and forget". - Messages []SubMsg `json:"messages"` - Attributes []abci.EventAttribute `json:"attributes"` + Messages []SubMsg `json:"messages"` + Attributes []v010msgtypes.LogAttribute `json:"attributes"` // custom events (separate from the main one that contains the attributes // above) Events []Event `json:"events"` diff --git a/x/compute/internal/keeper/relay.go b/x/compute/internal/keeper/relay.go index 587f5a14b..d21e76661 100644 --- a/x/compute/internal/keeper/relay.go +++ b/x/compute/internal/keeper/relay.go @@ -2,6 +2,7 @@ package keeper import ( "encoding/json" + "fmt" "time" "github.com/cosmos/cosmos-sdk/telemetry" @@ -27,7 +28,6 @@ func (k Keeper) OnOpenChannel( msg v1types.IBCChannelOpenMsg, ) (string, error) { defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-open-channel") - version := "" ctx.GasMeter().ConsumeGas(types.InstanceCost, "Loading Compute module: ibc-open-channel") @@ -68,18 +68,16 @@ func (k Keeper) OnOpenChannel( return "", sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } - ///////// - res, gasUsed, execErr := k.wasmVM.IBCChannelOpen(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) - - if execErr != nil { - return "", sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + switch resp := res.(type) { + case *v1types.IBC3ChannelOpenResponse: + if resp != nil { + return resp.Version, nil + } else { + return "", nil + } + default: + return "", sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("cannot cast res to IBC3ChannelOpenResponse: %+v", res)) } - - if res != nil { - version = res.Version - } - - return version, nil } // OnConnectChannel calls the contract to let it know the IBC channel was established. diff --git a/x/compute/internal/types/exported_keepers.go b/x/compute/internal/types/exported_keepers.go new file mode 100644 index 000000000..477f668e7 --- /dev/null +++ b/x/compute/internal/types/exported_keepers.go @@ -0,0 +1,46 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + v1types "github.com/enigmampc/SecretNetwork/go-cosmwasm/types/v1" +) + +// IBCContractKeeper IBC lifecycle event handler +type IBCContractKeeper interface { + OnOpenChannel( + ctx sdk.Context, + contractAddr sdk.AccAddress, + msg v1types.IBCChannelOpenMsg, + ) (string, error) + OnConnectChannel( + ctx sdk.Context, + contractAddr sdk.AccAddress, + msg v1types.IBCChannelConnectMsg, + ) error + OnCloseChannel( + ctx sdk.Context, + contractAddr sdk.AccAddress, + msg v1types.IBCChannelCloseMsg, + ) error + OnRecvPacket( + ctx sdk.Context, + contractAddr sdk.AccAddress, + msg v1types.IBCPacketReceiveMsg, + ) ([]byte, error) + OnAckPacket( + ctx sdk.Context, + contractAddr sdk.AccAddress, + acknowledgement v1types.IBCPacketAckMsg, + ) error + OnTimeoutPacket( + ctx sdk.Context, + contractAddr sdk.AccAddress, + msg v1types.IBCPacketTimeoutMsg, + ) error + // ClaimCapability allows the transfer module to claim a capability + // that IBC module passes to it + ClaimCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) error + // AuthenticateCapability wraps the scopedKeeper's AuthenticateCapability function + AuthenticateCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) bool +} From 26413fdb8c61f22b4cfd9d7247546097dd940669 Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Mon, 25 Jul 2022 12:54:49 +0300 Subject: [PATCH 03/44] Parse IBC results in go-cosmwasm --- go-cosmwasm/lib.go | 88 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 11 deletions(-) diff --git a/go-cosmwasm/lib.go b/go-cosmwasm/lib.go index 523e6fdaa..45411542c 100644 --- a/go-cosmwasm/lib.go +++ b/go-cosmwasm/lib.go @@ -91,11 +91,11 @@ type ContractExecResponse struct { InternaReplyEnclaveSig []byte `json:"internal_reply_enclave_sig"` InternalMsgId []byte `json:"internal_msg_id"` IBCChannelOpen *v1types.IBCChannelOpenResult `json:"ibc_channel_open,omitempty"` - IBCChannelConnect *v1types.IBCBasicResponse `json:"ibc_channel_connect,omitempty"` - IBCChannelClose *v1types.IBCBasicResponse `json:"ibc_channel_close,omitempty"` + IBCChannelConnect *v1types.IBCBasicResult `json:"ibc_channel_connect,omitempty"` + IBCChannelClose *v1types.IBCBasicResult `json:"ibc_channel_close,omitempty"` IBCPacketReceive *v1types.IBCReceiveResult `json:"ibc_packet_receive,omitempty"` - IBCPacketAck *v1types.IBCBasicResponse `json:"ibc_packet_ack,omitempty"` - IBCPacketTimeout *v1types.IBCBasicResponse `json:"ibc_packet_timeout,omitempty"` + IBCPacketAck *v1types.IBCBasicResult `json:"ibc_packet_ack,omitempty"` + IBCPacketTimeout *v1types.IBCBasicResult `json:"ibc_packet_timeout,omitempty"` } type V010ContractExecResponse struct { @@ -284,16 +284,16 @@ func (w *Wasmer) Execute( InternaReplyEnclaveSig: resp.InternaReplyEnclaveSig, Data: []byte(resp.V010.Err.GenericErr.Msg), }, gasUsed, fmt.Errorf("%+v", resp.V010.Err) - } - - if resp.V010.Ok != nil { + } else if resp.V010.Ok != nil { if isOutputAddressedToReply { resp.V010.Ok.Data, err = AppendReplyInternalDataToData(resp.V010.Ok.Data, resp.InternaReplyEnclaveSig, resp.InternalMsgId) if err != nil { - return nil, gasUsed, fmt.Errorf("cannot serialize v010 DataWithInternalReplyInfo into binary : %w", err) + return nil, gasUsed, fmt.Errorf("cannot serialize v0.10 DataWithInternalReplyInfo into binary : %w", err) } } return resp.V010.Ok, gasUsed, nil + } else { + return nil, gasUsed, fmt.Errorf("cannot parse v0.10 handle response: %+v", resp) } } @@ -305,9 +305,7 @@ func (w *Wasmer) Execute( InternaReplyEnclaveSig: resp.InternaReplyEnclaveSig, Data: []byte(resp.V1.Err.GenericErr.Msg), }, gasUsed, fmt.Errorf("%+v", resp.V1.Err) - } - - if resp.V1.Ok != nil { + } else if resp.V1.Ok != nil { if isOutputAddressedToReply { resp.V1.Ok.Data, err = AppendReplyInternalDataToData(resp.V1.Ok.Data, resp.InternaReplyEnclaveSig, resp.InternalMsgId) if err != nil { @@ -315,6 +313,74 @@ func (w *Wasmer) Execute( } } return resp.V1.Ok, gasUsed, nil + } else { + return nil, gasUsed, fmt.Errorf("cannot parse v1 handle response: %+v", resp) + } + } + + // handle IBCChannelOpen response + if resp.IBCChannelOpen != nil { + if resp.IBCChannelOpen.Err != "" { + return nil, gasUsed, fmt.Errorf("%s", resp.IBCChannelOpen.Err) + } else if resp.IBCChannelOpen.Ok != nil { + return resp.IBCChannelOpen.Ok, gasUsed, nil + } else { + return nil, gasUsed, fmt.Errorf("cannot parse IBCChannelOpen response: %+v", resp) + } + } + + // handle IBCChannelConnect response + if resp.IBCChannelConnect != nil { + if resp.IBCChannelConnect.Err != "" { + return nil, gasUsed, fmt.Errorf("%s", resp.IBCChannelConnect.Err) + } else if resp.IBCChannelConnect.Ok != nil { + return resp.IBCChannelConnect.Ok, gasUsed, nil + } else { + return nil, gasUsed, fmt.Errorf("cannot parse IBCChannelConnect response: %+v", resp) + } + } + + // handle IBCChannelClose response + if resp.IBCChannelClose != nil { + if resp.IBCChannelClose.Err != "" { + return nil, gasUsed, fmt.Errorf("%s", resp.IBCChannelClose.Err) + } else if resp.IBCChannelClose.Ok != nil { + return resp.IBCChannelClose.Ok, gasUsed, nil + } else { + return nil, gasUsed, fmt.Errorf("cannot parse IBCChannelClose response: %+v", resp) + } + } + + // handle IBCPacketReceive response + if resp.IBCPacketReceive != nil { + if resp.IBCPacketReceive.Err != "" { + return nil, gasUsed, fmt.Errorf("%s", resp.IBCPacketReceive.Err) + } else if resp.IBCPacketReceive.Ok != nil { + return resp.IBCPacketReceive.Ok, gasUsed, nil + } else { + return nil, gasUsed, fmt.Errorf("cannot parse IBCPacketReceive response: %+v", resp) + } + } + + // handle IBCPacketAck response + if resp.IBCPacketAck != nil { + if resp.IBCPacketAck.Err != "" { + return nil, gasUsed, fmt.Errorf("%s", resp.IBCPacketAck.Err) + } else if resp.IBCPacketAck.Ok != nil { + return resp.IBCPacketAck.Ok, gasUsed, nil + } else { + return nil, gasUsed, fmt.Errorf("cannot parse IBCPacketAck response: %+v", resp) + } + } + + // handle IBCPacketTimeout response + if resp.IBCPacketTimeout != nil { + if resp.IBCPacketTimeout.Err != "" { + return nil, gasUsed, fmt.Errorf("%s", resp.IBCPacketTimeout.Err) + } else if resp.IBCPacketTimeout.Ok != nil { + return resp.IBCPacketTimeout.Ok, gasUsed, nil + } else { + return nil, gasUsed, fmt.Errorf("cannot parse IBCPacketTimeout response: %+v", resp) } } From d3cda943dabdc9184022e35133f64b4bed5794cc Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Mon, 25 Jul 2022 13:19:24 +0300 Subject: [PATCH 04/44] Refactor IBC contract calls, lots of shared code --- x/compute/internal/keeper/ibc.go | 5 ++++ x/compute/internal/keeper/keeper.go | 3 ++ x/compute/internal/keeper/relay.go | 46 +++++++++++++++++------------ 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/x/compute/internal/keeper/ibc.go b/x/compute/internal/keeper/ibc.go index 8df6fdecc..900927b82 100644 --- a/x/compute/internal/keeper/ibc.go +++ b/x/compute/internal/keeper/ibc.go @@ -37,3 +37,8 @@ func PortIDForContract(addr sdk.AccAddress) string { func (k Keeper) ClaimCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) error { return k.capabilityKeeper.ClaimCapability(ctx, cap, name) } + +// AuthenticateCapability wraps the scopedKeeper's AuthenticateCapability function +func (k Keeper) AuthenticateCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) bool { + return k.capabilityKeeper.AuthenticateCapability(ctx, cap, name) +} diff --git a/x/compute/internal/keeper/keeper.go b/x/compute/internal/keeper/keeper.go index 6215d155a..d3417db06 100644 --- a/x/compute/internal/keeper/keeper.go +++ b/x/compute/internal/keeper/keeper.go @@ -10,6 +10,7 @@ import ( "time" capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" channelkeeper "github.com/cosmos/ibc-go/v3/modules/core/04-channel/keeper" portkeeper "github.com/cosmos/ibc-go/v3/modules/core/05-port/keeper" wasmTypes "github.com/enigmampc/SecretNetwork/go-cosmwasm/types" @@ -1109,3 +1110,5 @@ func (k Keeper) reply(ctx sdk.Context, contractAddress sdk.AccAddress, reply v1w return nil, sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("cannot detect response type: %+v", res)) } } + + diff --git a/x/compute/internal/keeper/relay.go b/x/compute/internal/keeper/relay.go index d21e76661..b855d3513 100644 --- a/x/compute/internal/keeper/relay.go +++ b/x/compute/internal/keeper/relay.go @@ -17,20 +17,10 @@ import ( var _ types.IBCContractKeeper = (*Keeper)(nil) -// OnOpenChannel calls the contract to participate in the IBC channel handshake step. -// In the IBC protocol this is either the `Channel Open Init` event on the initiating chain or -// `Channel Open Try` on the counterparty chain. -// Protocol version and channel ordering should be verified for example. -// See https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#channel-lifecycle-management -func (k Keeper) OnOpenChannel( - ctx sdk.Context, +func (k Keeper) ibcContractCall(ctx sdk.Context, contractAddress sdk.AccAddress, - msg v1types.IBCChannelOpenMsg, -) (string, error) { - defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-open-channel") - - ctx.GasMeter().ConsumeGas(types.InstanceCost, "Loading Compute module: ibc-open-channel") - + msgBz []byte, +) (interface{}, error) { verificationInfo := types.NewVerificationInfo([]byte{}, sdktxsigning.SignMode_SIGN_MODE_UNSPECIFIED, []byte{}, []byte{}, []byte{}, nil) _, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddress) @@ -43,8 +33,8 @@ func (k Keeper) OnOpenChannel( contractKey := store.Get(types.GetContractEnclaveKey(contractAddress)) env := types.NewEnv( ctx, - sdk.AccAddress{}, /* empty because it's unused in queries */ - sdk.NewCoins(), /* empty because it's unused in queries */ + sdk.AccAddress{}, /* there's no MessageInfo for IBC contract calls */ + sdk.NewCoins(), /* there's no MessageInfo for IBC contract calls */ contractAddress, contractKey, ) @@ -55,15 +45,33 @@ func (k Keeper) OnOpenChannel( Plugins: k.queryPlugins, } + gas := gasForContract(ctx) + res, gasUsed, err := k.wasmer.Execute(codeInfo.CodeHash, env, msgBz, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, verificationInfo, wasmTypes.HandleTypeReply) + consumeGas(ctx, gasUsed) + + return res, err +} + +// OnOpenChannel calls the contract to participate in the IBC channel handshake step. +// In the IBC protocol this is either the `Channel Open Init` event on the initiating chain or +// `Channel Open Try` on the counterparty chain. +// Protocol version and channel ordering should be verified for example. +// See https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#channel-lifecycle-management +func (k Keeper) OnOpenChannel( + ctx sdk.Context, + contractAddress sdk.AccAddress, + msg v1types.IBCChannelOpenMsg, +) (string, error) { + defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-open-channel") + + ctx.GasMeter().ConsumeGas(types.InstanceCost, "Loading Compute module: ibc-open-channel") + msgBz, err := json.Marshal(msg) if err != nil { return "", sdkerrors.Wrap(err, "ibc-open-channel") } - gas := gasForContract(ctx) - res, gasUsed, err := k.wasmer.Execute(codeInfo.CodeHash, env, msgBz, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, verificationInfo, wasmTypes.HandleTypeReply) - consumeGas(ctx, gasUsed) - + res, err := k.ibcContractCall(ctx, contractAddress, msgBz) if err != nil { return "", sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } From bd46776cc7aee99616380e0e551feecce0343f98 Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Mon, 25 Jul 2022 13:39:46 +0300 Subject: [PATCH 05/44] IBC keeper OnTimeoutPacket OnAckPacket OnCloseChannel OnConnectChannel --- x/compute/internal/keeper/keeper.go | 3 - x/compute/internal/keeper/relay.go | 129 +++++++++++++++++----------- 2 files changed, 79 insertions(+), 53 deletions(-) diff --git a/x/compute/internal/keeper/keeper.go b/x/compute/internal/keeper/keeper.go index d3417db06..6215d155a 100644 --- a/x/compute/internal/keeper/keeper.go +++ b/x/compute/internal/keeper/keeper.go @@ -10,7 +10,6 @@ import ( "time" capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" - capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" channelkeeper "github.com/cosmos/ibc-go/v3/modules/core/04-channel/keeper" portkeeper "github.com/cosmos/ibc-go/v3/modules/core/05-port/keeper" wasmTypes "github.com/enigmampc/SecretNetwork/go-cosmwasm/types" @@ -1110,5 +1109,3 @@ func (k Keeper) reply(ctx sdk.Context, contractAddress sdk.AccAddress, reply v1w return nil, sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("cannot detect response type: %+v", res)) } } - - diff --git a/x/compute/internal/keeper/relay.go b/x/compute/internal/keeper/relay.go index b855d3513..2d05822f2 100644 --- a/x/compute/internal/keeper/relay.go +++ b/x/compute/internal/keeper/relay.go @@ -20,6 +20,7 @@ var _ types.IBCContractKeeper = (*Keeper)(nil) func (k Keeper) ibcContractCall(ctx sdk.Context, contractAddress sdk.AccAddress, msgBz []byte, + callType wasmTypes.HandleType, ) (interface{}, error) { verificationInfo := types.NewVerificationInfo([]byte{}, sdktxsigning.SignMode_SIGN_MODE_UNSPECIFIED, []byte{}, []byte{}, []byte{}, nil) @@ -46,12 +47,33 @@ func (k Keeper) ibcContractCall(ctx sdk.Context, } gas := gasForContract(ctx) - res, gasUsed, err := k.wasmer.Execute(codeInfo.CodeHash, env, msgBz, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, verificationInfo, wasmTypes.HandleTypeReply) + res, gasUsed, err := k.wasmer.Execute(codeInfo.CodeHash, env, msgBz, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, verificationInfo, callType) consumeGas(ctx, gasUsed) return res, err } +func (k Keeper) parseThenHandleIBCBasicContractResponse(ctx sdk.Context, + contractAddress sdk.AccAddress, + res interface{}, +) error { + switch resp := res.(type) { + case *v1types.IBCBasicResponse: + if resp != nil { + contractInfo, _, _, err := k.contractInstance(ctx, contractAddress) + if err != nil { + return err + } + + return k.handleIBCBasicContractResponse(ctx, contractAddress, contractInfo.IBCPortID, resp) + } else { + return sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("cannot parse IBCBasicResponse: %+v", res)) + } + default: + return sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("cannot cast res to IBCBasicResponse: %+v", res)) + } +} + // OnOpenChannel calls the contract to participate in the IBC channel handshake step. // In the IBC protocol this is either the `Channel Open Init` event on the initiating chain or // `Channel Open Try` on the counterparty chain. @@ -71,7 +93,7 @@ func (k Keeper) OnOpenChannel( return "", sdkerrors.Wrap(err, "ibc-open-channel") } - res, err := k.ibcContractCall(ctx, contractAddress, msgBz) + res, err := k.ibcContractCall(ctx, contractAddress, msgBz, wasmTypes.HandleTypeIbcChannelConnect) if err != nil { return "", sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } @@ -84,7 +106,7 @@ func (k Keeper) OnOpenChannel( return "", nil } default: - return "", sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("cannot cast res to IBC3ChannelOpenResponse: %+v", res)) + return "", sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("ibc-open-channel: cannot cast res to IBC3ChannelOpenResponse: %+v", res)) } } @@ -97,26 +119,28 @@ func (k Keeper) OnOpenChannel( // See https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#channel-lifecycle-management func (k Keeper) OnConnectChannel( ctx sdk.Context, - contractAddr sdk.AccAddress, + contractAddress sdk.AccAddress, msg v1types.IBCChannelConnectMsg, ) error { defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-connect-channel") - contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) + + ctx.GasMeter().ConsumeGas(types.InstanceCost, "Loading Compute module: ibc-connect-channel") + + msgBz, err := json.Marshal(msg) if err != nil { - return err + return sdkerrors.Wrap(err, "ibc-connect-channel") } - env := types.NewEnv(ctx, contractAddr) - querier := k.newQueryHandler(ctx, contractAddr) - - gas := k.runtimeGasForContract(ctx) - res, gasUsed, execErr := k.wasmVM.IBCChannelConnect(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) - k.consumeRuntimeGas(ctx, gasUsed) - if execErr != nil { - return sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + res, err := k.ibcContractCall(ctx, contractAddress, msgBz, wasmTypes.HandleTypeIbcChannelConnect) + if err != nil { + return sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } - return k.handleIBCBasicContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res) + err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, res) + if err != nil { + sdkerrors.Wrap(err, "ibc-connect-channel") + } + return nil } // OnCloseChannel calls the contract to let it know the IBC channel is closed. @@ -127,27 +151,28 @@ func (k Keeper) OnConnectChannel( // See https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#channel-lifecycle-management func (k Keeper) OnCloseChannel( ctx sdk.Context, - contractAddr sdk.AccAddress, + contractAddress sdk.AccAddress, msg v1types.IBCChannelCloseMsg, ) error { defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-close-channel") - contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) + ctx.GasMeter().ConsumeGas(types.InstanceCost, "Loading Compute module: ibc-close-channel") + + msgBz, err := json.Marshal(msg) if err != nil { - return err + return sdkerrors.Wrap(err, "ibc-close-channel") } - params := types.NewEnv(ctx, contractAddr) - querier := k.newQueryHandler(ctx, contractAddr) - - gas := k.runtimeGasForContract(ctx) - res, gasUsed, execErr := k.wasmVM.IBCChannelClose(codeInfo.CodeHash, params, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) - k.consumeRuntimeGas(ctx, gasUsed) - if execErr != nil { - return sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + res, err := k.ibcContractCall(ctx, contractAddress, msgBz, wasmTypes.HandleTypeIbcChannelClose) + if err != nil { + return sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } - return k.handleIBCBasicContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res) + err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, res) + if err != nil { + sdkerrors.Wrap(err, "ibc-close-channel") + } + return nil } // OnRecvPacket calls the contract to process the incoming IBC packet. The contract fully owns the data processing and @@ -192,25 +217,28 @@ func (k Keeper) OnRecvPacket( // For more information see: https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#packet-flow--handling func (k Keeper) OnAckPacket( ctx sdk.Context, - contractAddr sdk.AccAddress, + contractAddress sdk.AccAddress, msg v1types.IBCPacketAckMsg, ) error { defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-ack-packet") - contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) + + ctx.GasMeter().ConsumeGas(types.InstanceCost, "Loading Compute module: ibc-ack-packet") + + msgBz, err := json.Marshal(msg) if err != nil { - return err + return sdkerrors.Wrap(err, "ibc-ack-packet") } - env := types.NewEnv(ctx, contractAddr) - querier := k.newQueryHandler(ctx, contractAddr) + res, err := k.ibcContractCall(ctx, contractAddress, msgBz, wasmTypes.HandleTypeIbcPacketAck) + if err != nil { + return sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) + } - gas := k.runtimeGasForContract(ctx) - res, gasUsed, execErr := k.wasmVM.IBCPacketAck(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) - k.consumeRuntimeGas(ctx, gasUsed) - if execErr != nil { - return sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, res) + if err != nil { + sdkerrors.Wrap(err, "ibc-ack-packet") } - return k.handleIBCBasicContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res) + return nil } // OnTimeoutPacket calls the contract to let it know the packet was never received on the destination chain within @@ -218,27 +246,28 @@ func (k Keeper) OnAckPacket( // The contract should handle this on the application level and undo the original operation func (k Keeper) OnTimeoutPacket( ctx sdk.Context, - contractAddr sdk.AccAddress, + contractAddress sdk.AccAddress, msg v1types.IBCPacketTimeoutMsg, ) error { defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-timeout-packet") - contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) + ctx.GasMeter().ConsumeGas(types.InstanceCost, "Loading Compute module: ibc-timeout-packet") + + msgBz, err := json.Marshal(msg) if err != nil { - return err + return sdkerrors.Wrap(err, "ibc-timeout-packet") } - env := types.NewEnv(ctx, contractAddr) - querier := k.newQueryHandler(ctx, contractAddr) - - gas := k.runtimeGasForContract(ctx) - res, gasUsed, execErr := k.wasmVM.IBCPacketTimeout(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) - k.consumeRuntimeGas(ctx, gasUsed) - if execErr != nil { - return sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + res, err := k.ibcContractCall(ctx, contractAddress, msgBz, wasmTypes.HandleTypeIbcPacketTimeout) + if err != nil { + return sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } - return k.handleIBCBasicContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res) + err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, res) + if err != nil { + sdkerrors.Wrap(err, "ibc-timeout-packet") + } + return nil } func (k Keeper) handleIBCBasicContractResponse(ctx sdk.Context, addr sdk.AccAddress, id string, res *v1types.IBCBasicResponse) error { From 1277f839a64288ae41069ce0769191c7414844f1 Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Tue, 26 Jul 2022 11:24:09 +0300 Subject: [PATCH 06/44] Keeper OnRecvPacket --- x/compute/internal/keeper/relay.go | 62 +++++++++++++++++++----------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/x/compute/internal/keeper/relay.go b/x/compute/internal/keeper/relay.go index 2d05822f2..a60a928e0 100644 --- a/x/compute/internal/keeper/relay.go +++ b/x/compute/internal/keeper/relay.go @@ -55,6 +55,7 @@ func (k Keeper) ibcContractCall(ctx sdk.Context, func (k Keeper) parseThenHandleIBCBasicContractResponse(ctx sdk.Context, contractAddress sdk.AccAddress, + inputMsg []byte, res interface{}, ) error { switch resp := res.(type) { @@ -65,9 +66,9 @@ func (k Keeper) parseThenHandleIBCBasicContractResponse(ctx sdk.Context, return err } - return k.handleIBCBasicContractResponse(ctx, contractAddress, contractInfo.IBCPortID, resp) + return k.handleIBCBasicContractResponse(ctx, contractAddress, contractInfo.IBCPortID, inputMsg, resp) } else { - return sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("cannot parse IBCBasicResponse: %+v", res)) + return sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("null pointer IBCBasicResponse: %+v", res)) } default: return sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("cannot cast res to IBCBasicResponse: %+v", res)) @@ -136,7 +137,7 @@ func (k Keeper) OnConnectChannel( return sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } - err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, res) + err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, msgBz, res) if err != nil { sdkerrors.Wrap(err, "ibc-connect-channel") } @@ -168,7 +169,7 @@ func (k Keeper) OnCloseChannel( return sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } - err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, res) + err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, msgBz, res) if err != nil { sdkerrors.Wrap(err, "ibc-close-channel") } @@ -183,29 +184,42 @@ func (k Keeper) OnCloseChannel( // For more information see: https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#packet-flow--handling func (k Keeper) OnRecvPacket( ctx sdk.Context, - contractAddr sdk.AccAddress, + contractAddress sdk.AccAddress, msg v1types.IBCPacketReceiveMsg, ) ([]byte, error) { defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-recv-packet") - contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) + + ctx.GasMeter().ConsumeGas(types.InstanceCost, "Loading Compute module: ibc-recv-packet") + + msgBz, err := json.Marshal(msg) if err != nil { - return nil, err + return nil, sdkerrors.Wrap(err, "ibc-recv-packet") } - env := types.NewEnv(ctx, contractAddr) - querier := k.newQueryHandler(ctx, contractAddr) - - gas := k.runtimeGasForContract(ctx) - res, gasUsed, execErr := k.wasmVM.IBCPacketReceive(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) - k.consumeRuntimeGas(ctx, gasUsed) - if execErr != nil { - return nil, sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + res, err := k.ibcContractCall(ctx, contractAddress, msgBz, wasmTypes.HandleTypeIbcChannelConnect) + if err != nil { + return nil, sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } - if res.Err != "" { // handle error case as before https://github.com/CosmWasm/wasmvm/commit/c300106fe5c9426a495f8e10821e00a9330c56c6 - return nil, sdkerrors.Wrap(types.ErrExecuteFailed, res.Err) + + switch resp := res.(type) { + case *v1types.IBCReceiveResponse: + if resp != nil { + contractInfo, _, _, err := k.contractInstance(ctx, contractAddress) + if err != nil { + return nil, err + } + verificationInfo := types.NewVerificationInfo([]byte{}, sdktxsigning.SignMode_SIGN_MODE_UNSPECIFIED, []byte{}, []byte{}, []byte{}, nil) + + // note submessage reply results can overwrite the `Acknowledgement` data + return k.handleContractResponse(ctx, contractAddress, contractInfo.IBCPortID, resp.Messages, resp.Attributes, resp.Events, resp.Acknowledgement, msgBz, verificationInfo, wasmTypes.CosmosMsgVersionV1) + } else { + // should never get here as it's already checked in + // https://github.com/scrtlabs/SecretNetwork/blob/bd46776c/go-cosmwasm/lib.go#L358 + return nil, sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("ibc-recv-packet: null pointer IBCReceiveResponse: %+v", res)) + } + default: + return nil, sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("ibc-recv-packet: cannot cast res to IBCReceiveResponse: %+v", res)) } - // note submessage reply results can overwrite the `Acknowledgement` data - return k.handleContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res.Ok.Messages, res.Ok.Attributes, res.Ok.Acknowledgement, res.Ok.Events) } // OnAckPacket calls the contract to handle the "acknowledgement" data which can contain success or failure of a packet @@ -234,7 +248,7 @@ func (k Keeper) OnAckPacket( return sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } - err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, res) + err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, msgBz, res) if err != nil { sdkerrors.Wrap(err, "ibc-ack-packet") } @@ -263,14 +277,16 @@ func (k Keeper) OnTimeoutPacket( return sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } - err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, res) + err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, msgBz, res) if err != nil { sdkerrors.Wrap(err, "ibc-timeout-packet") } return nil } -func (k Keeper) handleIBCBasicContractResponse(ctx sdk.Context, addr sdk.AccAddress, id string, res *v1types.IBCBasicResponse) error { - _, err := k.handleContractResponse(ctx, addr, id, res.Messages, res.Attributes, nil, res.Events) +func (k Keeper) handleIBCBasicContractResponse(ctx sdk.Context, addr sdk.AccAddress, ibcPortID string, inputMsg []byte, res *v1types.IBCBasicResponse) error { + verificationInfo := types.NewVerificationInfo([]byte{}, sdktxsigning.SignMode_SIGN_MODE_UNSPECIFIED, []byte{}, []byte{}, []byte{}, nil) + + _, err := k.handleContractResponse(ctx, addr, ibcPortID, res.Messages, res.Attributes, res.Events, nil, inputMsg, verificationInfo, wasmTypes.CosmosMsgVersionV1) return err } From 1d801535b04b6817744212f4905db5c5f7e0f214 Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Tue, 26 Jul 2022 17:33:18 +0300 Subject: [PATCH 07/44] WIP: IBC on the enclave side --- .../src/contract_operations.rs | 71 +++++++++++++++++-- .../enclaves/shared/cosmos-types/src/types.rs | 6 ++ 2 files changed, 72 insertions(+), 5 deletions(-) diff --git a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs index 4a5ec1661..82b69eb50 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs @@ -221,12 +221,12 @@ pub fn parse_message( sig_info: &SigInfo, handle_type: &HandleType, ) -> Result { - let orig_secret_msg = SecretMessage::from_slice(message)?; - return match handle_type { HandleType::HANDLE_TYPE_EXECUTE => { + let orig_secret_msg = SecretMessage::from_slice(message)?; + trace!( - "handle input before decryption: {:?}", + "execute input before decryption: {:?}", base64::encode(&message) ); let decrypted_msg = orig_secret_msg.decrypt()?; @@ -238,10 +238,14 @@ pub fn parse_message( contract_hash_for_validation: None, }) } - HandleType::HANDLE_TYPE_REPLY => { + let orig_secret_msg = SecretMessage::from_slice(message)?; + if sig_info.sign_mode == SignMode::SIGN_MODE_UNSPECIFIED { - trace!("reply input is not encrypted"); + trace!( + "reply input is not encrypted: {:?}", + base64::encode(&message) + ); let decrypted_msg = orig_secret_msg.msg.clone(); let mut reply: Reply = serde_json::from_slice(&decrypted_msg) .map_err(|err| { @@ -508,6 +512,63 @@ pub fn parse_message( } } } + HandleType::HANDLE_TYPE_IBC_CHANNEL_OPEN => todo!(), + HandleType::HANDLE_TYPE_IBC_CHANNEL_CONNECT => todo!(), + HandleType::HANDLE_TYPE_IBC_CHANNEL_CLOSE => todo!(), + HandleType::HANDLE_TYPE_IBC_PACKET_RECEIVE => { + let orig_secret_msg = match SecretMessage::from_slice(message) { + Ok(orig_secret_msg) => orig_secret_msg, + Err(_) => { + trace!( + "ibc_packet_receive msg is not SecretMessage (probably plaintext): {:?}", + base64::encode(&message) + ); + + SecretMessage { + nonce: [0; 32], + user_public_key: [0; 32], + msg: message.into(), + } + } + }; + + match orig_secret_msg.decrypt() { + Ok(decrypted_msg) => { + // IBC packet is encrypted + + trace!( + "ibc_packet_receive input before decryption: {:?}", + base64::encode(&message) + ); + + Ok(ParsedMessage { + should_validate_sig_info: true, + was_msg_encrypted: true, + secret_msg: orig_secret_msg, + decrypted_msg, + contract_hash_for_validation: None, + }) + } + Err(_) => { + // assume packet is not encrypted, continue in plaintext mode + + trace!( + "ibc_packet_receive input is not encrypted: {:?}", + base64::encode(&message) + ); + + Ok(ParsedMessage { + should_validate_sig_info: false, + was_msg_encrypted: false, + secret_msg: orig_secret_msg, + decrypted_msg: orig_secret_msg.msg, + contract_hash_for_validation: None, + }) + } + } + } + HandleType::HANDLE_TYPE_IBC_PACKET_ACK => todo!(), + HandleType::HANDLE_TYPE_IBC_PACKET_TIMEOUT => todo!(), }; } diff --git a/cosmwasm/enclaves/shared/cosmos-types/src/types.rs b/cosmwasm/enclaves/shared/cosmos-types/src/types.rs index 873a80733..b86c0460d 100644 --- a/cosmwasm/enclaves/shared/cosmos-types/src/types.rs +++ b/cosmwasm/enclaves/shared/cosmos-types/src/types.rs @@ -146,6 +146,12 @@ pub enum SignModeDef { pub enum HandleType { HANDLE_TYPE_EXECUTE = 0, HANDLE_TYPE_REPLY = 1, + HANDLE_TYPE_IBC_CHANNEL_OPEN = 2, + HANDLE_TYPE_IBC_CHANNEL_CONNECT = 3, + HANDLE_TYPE_IBC_CHANNEL_CLOSE = 4, + HANDLE_TYPE_IBC_PACKET_RECEIVE = 5, + HANDLE_TYPE_IBC_PACKET_ACK = 6, + HANDLE_TYPE_IBC_PACKET_TIMEOUT = 7, } impl HandleType { From 0e412a90295352ccb16e883d405675e3911dd8d8 Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Wed, 27 Jul 2022 09:12:36 +0300 Subject: [PATCH 08/44] Remove old CosmWasm remnants --- x/compute/client/cli/tx.go | 22 +---- x/compute/internal/keeper/keeper.go | 27 +----- x/compute/internal/types/msg.go | 135 +--------------------------- 3 files changed, 5 insertions(+), 179 deletions(-) diff --git a/x/compute/client/cli/tx.go b/x/compute/client/cli/tx.go index 606cc6c07..82beb5b14 100644 --- a/x/compute/client/cli/tx.go +++ b/x/compute/client/cli/tx.go @@ -31,7 +31,6 @@ const ( flagProposalType = "type" flagIoMasterKey = "enclave-key" flagCodeHash = "code-hash" - // flagAdmin = "admin" ) // GetTxCmd returns the transaction commands for this module @@ -47,10 +46,6 @@ func GetTxCmd() *cobra.Command { StoreCodeCmd(), InstantiateContractCmd(), ExecuteContractCmd(), - // Currently not supporting these commands - // MigrateContractCmd(cdc), - // UpdateContractAdminCmd(cdc), - // ClearContractAdminCmd(cdc), ) return txCmd } @@ -105,20 +100,6 @@ func parseStoreCodeArgs(args []string, cliCtx client.Context, flags *flag.FlagSe return types.MsgStoreCode{}, fmt.Errorf("invalid input file. Use wasm binary or gzip") } - /* - var perm *types.AccessConfig - if onlyAddrStr := viper.GetString(flagInstantiateByAddress); onlyAddrStr != "" { - allowedAddr, err := sdk.AccAddressFromBech32(onlyAddrStr) - if err != nil { - return types.MsgStoreCode{}, sdkerrors.Wrap(err, flagInstantiateByAddress) - } - x := types.OnlyAddress.With(allowedAddr) - perm = &x - } else if everybody := viper.GetBool(flagInstantiateByEverybody); everybody { - perm = &types.AllowEverybody - } - */ - source, err := flags.GetString(flagSource) if err != nil { return types.MsgStoreCode{}, fmt.Errorf("source: %s", err) @@ -134,7 +115,6 @@ func parseStoreCodeArgs(args []string, cliCtx client.Context, flags *flag.FlagSe WASMByteCode: wasm, Source: source, Builder: builder, - // InstantiatePermission: perm, } return msg, nil } @@ -142,7 +122,7 @@ func parseStoreCodeArgs(args []string, cliCtx client.Context, flags *flag.FlagSe // InstantiateContractCmd will instantiate a contract from previously uploaded code. func InstantiateContractCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "instantiate [code_id_int64] [json_encoded_init_args] --label [text] " /* --admin [address,optional] */ + "--amount [coins,optional]", + Use: "instantiate [code_id_int64] [json_encoded_init_args] --label [text] --amount [coins,optional]", Short: "Instantiate a wasm contract", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { diff --git a/x/compute/internal/keeper/keeper.go b/x/compute/internal/keeper/keeper.go index 6215d155a..5980b8672 100644 --- a/x/compute/internal/keeper/keeper.go +++ b/x/compute/internal/keeper/keeper.go @@ -163,15 +163,6 @@ func (k Keeper) setParams(ctx sdk.Context, ps types.Params) { // Create uploads and compiles a WASM contract, returning a short identifier for the contract func (k Keeper) Create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, source string, builder string) (codeID uint64, err error) { - /* - return k.create(ctx, creator, wasmCode, source, builder, &types.AccessConfig{Type: types.Everybody} , k.authZPolicy ) - } - - func (k Keeper) create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, source string, builder string, instantiateAccess *types.AccessConfig ) (codeID uint64, err error) { - if !authZ.CanCreateCode(k.getUploadAccessConfig(ctx), creator) { - return 0, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "can not create code") - } - */ wasmCode, err = uncompress(wasmCode) if err != nil { return 0, sdkerrors.Wrap(types.ErrCreateFailed, err.Error()) @@ -180,18 +171,12 @@ func (k Keeper) Create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, codeHash, err := k.wasmer.Create(wasmCode) if err != nil { - // return 0, sdkerrors.Wrap(err, "cosmwasm create") return 0, sdkerrors.Wrap(types.ErrCreateFailed, err.Error()) } store := ctx.KVStore(k.storeKey) codeID = k.autoIncrementID(ctx, types.KeyLastCodeID) - /* - if instantiateAccess == nil { - defaultAccessConfig := k.getInstantiateAccessConfig(ctx).With(creator) - instantiateAccess = &defaultAccessConfig - } - */ - codeInfo := types.NewCodeInfo(codeHash, creator, source, builder /* , *instantiateAccess */) + + codeInfo := types.NewCodeInfo(codeHash, creator, source, builder) // 0x01 | codeID (uint64) -> ContractInfo store.Set(types.GetCodeKey(codeID), k.cdc.MustMarshal(&codeInfo)) @@ -418,10 +403,6 @@ func (k Keeper) Instantiate(ctx sdk.Context, codeID uint64, creator sdk.AccAddre var codeInfo types.CodeInfo k.cdc.MustUnmarshal(bz, &codeInfo) - // if !authZ.CanInstantiateContract(codeInfo.InstantiateConfig, creator) { - // return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "can not instantiate") - // } - // prepare env for contract instantiate call env := types.NewEnv(ctx, creator, deposit, contractAddress, nil) @@ -464,8 +445,6 @@ func (k Keeper) Instantiate(ctx sdk.Context, codeID uint64, creator sdk.AccAddre contractInfo := types.NewContractInfo(codeID, creator, label, createdAt) store.Set(types.GetContractAddressKey(contractAddress), k.cdc.MustMarshal(&contractInfo)) - // fmt.Printf("Storing key: %v for account %s\n", key, contractAddress) - store.Set(types.GetContractEnclaveKey(contractAddress), key) store.Set(types.GetContractLabelPrefix(label), contractAddress) @@ -993,8 +972,6 @@ func (k Keeper) importContract(ctx sdk.Context, contractAddr sdk.AccAddress, cus return sdkerrors.Wrapf(types.ErrDuplicate, "contract: %s", contractAddr) } - // historyEntry := c.ResetFromGenesis(ctx) - // k.appendToContractHistory(ctx, contractAddr, historyEntry) k.setContractCustomInfo(ctx, contractAddr, customInfo) k.setContractInfo(ctx, contractAddr, c) return k.importContractState(ctx, contractAddr, state) diff --git a/x/compute/internal/types/msg.go b/x/compute/internal/types/msg.go index 7eb0f4877..5d7b8fcc4 100644 --- a/x/compute/internal/types/msg.go +++ b/x/compute/internal/types/msg.go @@ -29,13 +29,7 @@ func (msg MsgStoreCode) ValidateBasic() error { if err := validateBuilder(msg.Builder); err != nil { return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "builder %s", err.Error()) } - /* - if msg.InstantiatePermission != nil { - if err := msg.InstantiatePermission.ValidateBasic(); err != nil { - return sdkerrors.Wrap(err, "instantiate permission") - } - } - */ + return nil } @@ -72,16 +66,6 @@ func (msg MsgInstantiateContract) ValidateBasic() error { return sdkerrors.ErrInvalidCoins } - /* - if len(msg.Admin) != 0 { - if err := sdk.VerifyAddressFormat(msg.Admin); err != nil { - return err - } - } - if !json.Valid(msg.InitMsg) { - return sdkerrors.Wrap(ErrInvalid, "init msg json") - } - */ return nil } @@ -112,11 +96,7 @@ func (msg MsgExecuteContract) ValidateBasic() error { if !msg.SentFunds.IsValid() { return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, "sentFunds") } - /* - if !json.Valid(msg.Msg) { - return sdkerrors.Wrap(ErrInvalid, "msg json") - } - */ + return nil } @@ -127,114 +107,3 @@ func (msg MsgExecuteContract) GetSignBytes() []byte { func (msg MsgExecuteContract) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Sender} } - -/* -type MsgMigrateContract struct { - Sender sdk.AccAddress `json:"sender" yaml:"sender"` - Contract sdk.AccAddress `json:"contract" yaml:"contract"` - CodeID uint64 `json:"code_id" yaml:"code_id"` - MigrateMsg json.RawMessage `json:"msg" yaml:"msg"` -} - -func (msg MsgMigrateContract) Route() string { - return RouterKey -} - -func (msg MsgMigrateContract) Type() string { - return "migrate" -} - -func (msg MsgMigrateContract) ValidateBasic() error { - if msg.CodeID == 0 { - return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "code_id is required") - } - if err := sdk.VerifyAddressFormat(msg.Sender); err != nil { - return sdkerrors.Wrap(err, "sender") - } - if err := sdk.VerifyAddressFormat(msg.Contract); err != nil { - return sdkerrors.Wrap(err, "contract") - } - if !json.Valid(msg.MigrateMsg) { - return sdkerrors.Wrap(ErrInvalid, "migrate msg json") - } - - return nil -} - -func (msg MsgMigrateContract) GetSignBytes() []byte { - return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) -} - -func (msg MsgMigrateContract) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.Sender} -} - -type MsgUpdateAdmin struct { - Sender sdk.AccAddress `json:"sender" yaml:"sender"` - NewAdmin sdk.AccAddress `json:"new_admin" yaml:"new_admin"` - Contract sdk.AccAddress `json:"contract" yaml:"contract"` -} - -func (msg MsgUpdateAdmin) Route() string { - return RouterKey -} - -func (msg MsgUpdateAdmin) Type() string { - return "update-contract-admin" -} - -func (msg MsgUpdateAdmin) ValidateBasic() error { - if err := sdk.VerifyAddressFormat(msg.Sender); err != nil { - return sdkerrors.Wrap(err, "sender") - } - if err := sdk.VerifyAddressFormat(msg.Contract); err != nil { - return sdkerrors.Wrap(err, "contract") - } - if err := sdk.VerifyAddressFormat(msg.NewAdmin); err != nil { - return sdkerrors.Wrap(err, "new admin") - } - if msg.Sender.Equals(msg.NewAdmin) { - return sdkerrors.Wrap(ErrInvalidMsg, "new admin is the same as the old") - } - return nil -} - -func (msg MsgUpdateAdmin) GetSignBytes() []byte { - return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) -} - -func (msg MsgUpdateAdmin) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.Sender} -} - -type MsgClearAdmin struct { - Sender sdk.AccAddress `json:"sender" yaml:"sender"` - Contract sdk.AccAddress `json:"contract" yaml:"contract"` -} - -func (msg MsgClearAdmin) Route() string { - return RouterKey -} - -func (msg MsgClearAdmin) Type() string { - return "clear-contract-admin" -} - -func (msg MsgClearAdmin) ValidateBasic() error { - if err := sdk.VerifyAddressFormat(msg.Sender); err != nil { - return sdkerrors.Wrap(err, "sender") - } - if err := sdk.VerifyAddressFormat(msg.Contract); err != nil { - return sdkerrors.Wrap(err, "contract") - } - return nil -} - -func (msg MsgClearAdmin) GetSignBytes() []byte { - return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) -} - -func (msg MsgClearAdmin) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.Sender} -} -*/ From a60a2cbf5a2093e1a000655393e7740002b9dec5 Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Tue, 2 Aug 2022 14:41:55 +0300 Subject: [PATCH 09/44] secret-contract-optimizer v1.0.8 --- deployment/dockerfiles/secret-contract-optimizer.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/dockerfiles/secret-contract-optimizer.Dockerfile b/deployment/dockerfiles/secret-contract-optimizer.Dockerfile index c37099388..91639d96d 100644 --- a/deployment/dockerfiles/secret-contract-optimizer.Dockerfile +++ b/deployment/dockerfiles/secret-contract-optimizer.Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.59-slim-bullseye +FROM rust:1.62-slim-bullseye RUN rustup target add wasm32-unknown-unknown RUN apt update && apt install -y binaryen clang && rm -rf /var/lib/apt/lists/* From a4a2b1caf11b99c2221e42c1df07b4fca0b67920 Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Tue, 2 Aug 2022 14:42:10 +0300 Subject: [PATCH 10/44] Some IBC progress --- .vscode/settings.json | 4 +- .../src/contract_operations.rs | 2 +- .../shared/cosmwasm-v1-types/src/ibc.rs | 123 ++++++++++++++++++ .../shared/cosmwasm-v1-types/src/lib.rs | 1 + .../testdata/v1-sanity-contract/src/msg.rs | 6 + 5 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 244f2f6bb..c7134c83c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,7 +3,9 @@ "./cosmwasm/Cargo.toml", "./cosmwasm/enclaves/Cargo.toml", "./x/compute/internal/keeper/testdata/v1-sanity-contract/Cargo.toml", - "./x/compute/internal/keeper/testdata/test-contract/Cargo.toml" + "./x/compute/internal/keeper/testdata/test-contract/Cargo.toml", + "./cosmwasm/enclaves/shared/cosmwasm-v1-types/Cargo.toml", + "./cosmwasm/enclaves/shared/cosmwasm-v010-types/Cargo.toml" ], "rust-analyzer.diagnostics.experimental.enable": true, "rust-analyzer.rustfmt.rangeFormatting.enable": true, diff --git a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs index 82b69eb50..5721d86b3 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs @@ -534,7 +534,7 @@ pub fn parse_message( match orig_secret_msg.decrypt() { Ok(decrypted_msg) => { - // IBC packet is encrypted + // IBC packet was encrypted trace!( "ibc_packet_receive input before decryption: {:?}", diff --git a/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs b/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs new file mode 100644 index 000000000..311e89d0b --- /dev/null +++ b/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs @@ -0,0 +1,123 @@ +use crate::addresses::Addr; +use crate::timestamp::Timestamp; +use serde::{Deserialize, Serialize}; + +/// The message that is passed into `ibc_channel_open` +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum IbcChannelOpenMsg { + /// The ChanOpenInit step from https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#channel-lifecycle-management + OpenInit { channel: IbcChannel }, + /// The ChanOpenTry step from https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#channel-lifecycle-management + OpenTry { + channel: IbcChannel, + counterparty_version: String, + }, +} + +/// IbcChannel defines all information on a channel. +/// This is generally used in the hand-shake process, but can be queried directly. +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct IbcChannel { + pub endpoint: IbcEndpoint, + pub counterparty_endpoint: IbcEndpoint, + pub order: IbcOrder, + /// Note: in ibcv3 this may be "", in the IbcOpenChannel handshake messages + pub version: String, + /// The connection upon which this channel was created. If this is a multi-hop + /// channel, we only expose the first hop. + pub connection_id: String, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct IbcEndpoint { + pub port_id: String, + pub channel_id: String, +} + +/// IbcOrder defines if a channel is ORDERED or UNORDERED +/// Values come from https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/core/channel/v1/channel.proto#L69-L80 +/// Naming comes from the protobuf files and go translations. +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub enum IbcOrder { + #[serde(rename = "ORDER_UNORDERED")] + Unordered, + #[serde(rename = "ORDER_ORDERED")] + Ordered, +} + +/// The message that is passed into `ibc_channel_connect` +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum IbcChannelConnectMsg { + /// The ChanOpenAck step from https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#channel-lifecycle-management + OpenAck { + channel: IbcChannel, + counterparty_version: String, + }, + /// The ChanOpenConfirm step from https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#channel-lifecycle-management + OpenConfirm { channel: IbcChannel }, +} + +/// The message that is passed into `ibc_channel_close` +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum IbcChannelCloseMsg { + /// The ChanCloseInit step from https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#channel-lifecycle-management + CloseInit { channel: IbcChannel }, + /// The ChanCloseConfirm step from https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#channel-lifecycle-management + CloseConfirm { channel: IbcChannel }, // pub channel: IbcChannel, +} + +/// The message that is passed into `ibc_packet_receive` +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct IbcPacketReceiveMsg { + pub packet: IbcPacket, + pub relayer: Addr, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct IbcPacket { + /// The raw data sent from the other side in the packet + pub data: Binary, + /// identifies the channel and port on the sending chain. + pub src: IbcEndpoint, + /// identifies the channel and port on the receiving chain. + pub dest: IbcEndpoint, + /// The sequence number of the packet on the given channel + pub sequence: u64, + pub timeout: IbcTimeout, +} + +/// In IBC each package must set at least one type of timeout: +/// the timestamp or the block height. Using this rather complex enum instead of +/// two timeout fields we ensure that at least one timeout is set. +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[serde(rename_all = "snake_case")] +pub struct IbcTimeout { + // use private fields to enforce the use of constructors, which ensure that at least one is set + block: Option, + timestamp: Option, +} + +/// IBCTimeoutHeight Height is a monotonically increasing data type +/// that can be compared against another Height for the purposes of updating and +/// freezing clients. +/// Ordering is (revision_number, timeout_height) +#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq)] +pub struct IbcTimeoutBlock { + /// the version that the client is currently on + /// (eg. after reseting the chain this could increment 1 as height drops to 0) + pub revision: u64, + /// block height after which the packet times out. + /// the height within the given revision + pub height: u64, +} + +/// The message that is passed into `ibc_packet_ack` +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct IbcPacketAckMsg { + pub acknowledgement: IbcAcknowledgement, + pub original_packet: IbcPacket, + pub relayer: Addr, +} diff --git a/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/lib.rs b/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/lib.rs index 310434644..62f52dc66 100644 --- a/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/lib.rs +++ b/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/lib.rs @@ -2,6 +2,7 @@ pub mod addresses; pub mod coins; pub mod errors; +pub mod ibc; pub mod math; pub mod results; pub mod timestamp; diff --git a/x/compute/internal/keeper/testdata/v1-sanity-contract/src/msg.rs b/x/compute/internal/keeper/testdata/v1-sanity-contract/src/msg.rs index 2e8fc7f76..3198ffab5 100644 --- a/x/compute/internal/keeper/testdata/v1-sanity-contract/src/msg.rs +++ b/x/compute/internal/keeper/testdata/v1-sanity-contract/src/msg.rs @@ -88,6 +88,12 @@ pub enum ExecuteMsg { SubMsgLoopIner { iter: u64, }, + SentFunds { +to_type: "init"/"exec" +to_contract: "secret1.." +to_contract_hash: "ABC" +funds + }, MultipleSubMessages {}, MultipleSubMessagesNoReply {}, From 88827c0115e19b08c105e0d9fab4dd060385fcd2 Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Thu, 14 Jul 2022 16:14:09 +0300 Subject: [PATCH 11/44] WIP: Start to implement IBC receive hooks Here specifically is ibc-open-channel --- go-cosmwasm/types/types.go | 6 + x/compute/internal/keeper/keeper.go | 11 +- x/compute/internal/keeper/relay.go | 241 ++++++++++++++++++++++++++++ x/compute/internal/types/errors.go | 3 - 4 files changed, 253 insertions(+), 8 deletions(-) create mode 100644 x/compute/internal/keeper/relay.go diff --git a/go-cosmwasm/types/types.go b/go-cosmwasm/types/types.go index 9c8713186..83d166bb4 100644 --- a/go-cosmwasm/types/types.go +++ b/go-cosmwasm/types/types.go @@ -72,6 +72,12 @@ type HandleType int const ( HandleTypeExecute HandleType = iota HandleTypeReply + HandleTypeIbcChannelOpen + HandleTypeIbcChannelConnect + HandleTypeIbcChannelClose + HandleTypeIbcPacketReceive + HandleTypeIbcPacketAck + HandleTypeIbcPacketTimeout ) type CosmosMsgVersion int diff --git a/x/compute/internal/keeper/keeper.go b/x/compute/internal/keeper/keeper.go index 808d18abd..e763a8033 100644 --- a/x/compute/internal/keeper/keeper.go +++ b/x/compute/internal/keeper/keeper.go @@ -440,6 +440,7 @@ func (k Keeper) Instantiate(ctx sdk.Context, codeID uint64, creator sdk.AccAddre gas := gasForContract(ctx) response, key, gasUsed, err := k.wasmer.Instantiate(codeInfo.CodeHash, env, initMsg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, verificationInfo, contractAddress) consumeGas(ctx, gasUsed) + if err != nil { switch res := response.(type) { case v1wasmTypes.DataWithInternalReplyInfo: @@ -659,7 +660,7 @@ func (k Keeper) querySmartImpl(ctx sdk.Context, contractAddr sdk.AccAddress, req params := types.NewEnv( ctx, sdk.AccAddress{}, /* empty because it's unused in queries */ - []sdk.Coin{}, /* empty because it's unused in queries */ + sdk.NewCoins(), /* empty because it's unused in queries */ contractAddr, contractKey, ) @@ -1088,12 +1089,12 @@ func (k Keeper) reply(ctx sdk.Context, contractAddress sdk.AccAddress, reply v1w consumeGas(ctx, gasUsed) if execErr != nil { - return nil, sdkerrors.Wrap(types.ErrReplyFailed, execErr.Error()) + return nil, sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) } switch res := response.(type) { case *v010wasmTypes.HandleResponse: - return nil, sdkerrors.Wrap(types.ErrReplyFailed, fmt.Sprintf("response of reply should always be a CosmWasm v1 response type: %+v", res)) + return nil, sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("response of reply should always be a CosmWasm v1 response type: %+v", res)) case *v1wasmTypes.Response: ctx.EventManager().EmitEvent(sdk.NewEvent( types.EventTypeReply, @@ -1102,11 +1103,11 @@ func (k Keeper) reply(ctx sdk.Context, contractAddress sdk.AccAddress, reply v1w data, err := k.handleContractResponse(ctx, contractAddress, contractInfo.IBCPortID, res.Messages, res.Attributes, res.Events, res.Data, ogTx, ogSigInfo, wasmTypes.CosmosMsgVersionV1) if err != nil { - return nil, sdkerrors.Wrap(types.ErrReplyFailed, err.Error()) + return nil, sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } return data, nil default: - return nil, sdkerrors.Wrap(types.ErrReplyFailed, fmt.Sprintf("cannot detect response type: %+v", res)) + return nil, sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("cannot detect response type: %+v", res)) } } diff --git a/x/compute/internal/keeper/relay.go b/x/compute/internal/keeper/relay.go new file mode 100644 index 000000000..587f5a14b --- /dev/null +++ b/x/compute/internal/keeper/relay.go @@ -0,0 +1,241 @@ +package keeper + +import ( + "encoding/json" + "time" + + "github.com/cosmos/cosmos-sdk/telemetry" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + sdktxsigning "github.com/cosmos/cosmos-sdk/types/tx/signing" + wasmTypes "github.com/enigmampc/SecretNetwork/go-cosmwasm/types" + v1types "github.com/enigmampc/SecretNetwork/go-cosmwasm/types/v1" + + "github.com/enigmampc/SecretNetwork/x/compute/internal/types" +) + +var _ types.IBCContractKeeper = (*Keeper)(nil) + +// OnOpenChannel calls the contract to participate in the IBC channel handshake step. +// In the IBC protocol this is either the `Channel Open Init` event on the initiating chain or +// `Channel Open Try` on the counterparty chain. +// Protocol version and channel ordering should be verified for example. +// See https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#channel-lifecycle-management +func (k Keeper) OnOpenChannel( + ctx sdk.Context, + contractAddress sdk.AccAddress, + msg v1types.IBCChannelOpenMsg, +) (string, error) { + defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-open-channel") + version := "" + + ctx.GasMeter().ConsumeGas(types.InstanceCost, "Loading Compute module: ibc-open-channel") + + verificationInfo := types.NewVerificationInfo([]byte{}, sdktxsigning.SignMode_SIGN_MODE_UNSPECIFIED, []byte{}, []byte{}, []byte{}, nil) + + _, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddress) + if err != nil { + return "", err + } + + store := ctx.KVStore(k.storeKey) + + contractKey := store.Get(types.GetContractEnclaveKey(contractAddress)) + env := types.NewEnv( + ctx, + sdk.AccAddress{}, /* empty because it's unused in queries */ + sdk.NewCoins(), /* empty because it's unused in queries */ + contractAddress, + contractKey, + ) + + // prepare querier + querier := QueryHandler{ + Ctx: ctx, + Plugins: k.queryPlugins, + } + + msgBz, err := json.Marshal(msg) + if err != nil { + return "", sdkerrors.Wrap(err, "ibc-open-channel") + } + + gas := gasForContract(ctx) + res, gasUsed, err := k.wasmer.Execute(codeInfo.CodeHash, env, msgBz, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, verificationInfo, wasmTypes.HandleTypeReply) + consumeGas(ctx, gasUsed) + + if err != nil { + return "", sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) + } + + ///////// + res, gasUsed, execErr := k.wasmVM.IBCChannelOpen(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) + + if execErr != nil { + return "", sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + } + + if res != nil { + version = res.Version + } + + return version, nil +} + +// OnConnectChannel calls the contract to let it know the IBC channel was established. +// In the IBC protocol this is either the `Channel Open Ack` event on the initiating chain or +// `Channel Open Confirm` on the counterparty chain. +// +// There is an open issue with the [cosmos-sdk](https://github.com/cosmos/cosmos-sdk/issues/8334) +// that the counterparty channelID is empty on the initiating chain +// See https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#channel-lifecycle-management +func (k Keeper) OnConnectChannel( + ctx sdk.Context, + contractAddr sdk.AccAddress, + msg v1types.IBCChannelConnectMsg, +) error { + defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-connect-channel") + contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) + if err != nil { + return err + } + + env := types.NewEnv(ctx, contractAddr) + querier := k.newQueryHandler(ctx, contractAddr) + + gas := k.runtimeGasForContract(ctx) + res, gasUsed, execErr := k.wasmVM.IBCChannelConnect(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) + k.consumeRuntimeGas(ctx, gasUsed) + if execErr != nil { + return sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + } + + return k.handleIBCBasicContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res) +} + +// OnCloseChannel calls the contract to let it know the IBC channel is closed. +// Calling modules MAY atomically execute appropriate application logic in conjunction with calling chanCloseConfirm. +// +// Once closed, channels cannot be reopened and identifiers cannot be reused. Identifier reuse is prevented because +// we want to prevent potential replay of previously sent packets +// See https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#channel-lifecycle-management +func (k Keeper) OnCloseChannel( + ctx sdk.Context, + contractAddr sdk.AccAddress, + msg v1types.IBCChannelCloseMsg, +) error { + defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-close-channel") + + contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) + if err != nil { + return err + } + + params := types.NewEnv(ctx, contractAddr) + querier := k.newQueryHandler(ctx, contractAddr) + + gas := k.runtimeGasForContract(ctx) + res, gasUsed, execErr := k.wasmVM.IBCChannelClose(codeInfo.CodeHash, params, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) + k.consumeRuntimeGas(ctx, gasUsed) + if execErr != nil { + return sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + } + + return k.handleIBCBasicContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res) +} + +// OnRecvPacket calls the contract to process the incoming IBC packet. The contract fully owns the data processing and +// returns the acknowledgement data for the chain level. This allows custom applications and protocols on top +// of IBC. Although it is recommended to use the standard acknowledgement envelope defined in +// https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#acknowledgement-envelope +// +// For more information see: https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#packet-flow--handling +func (k Keeper) OnRecvPacket( + ctx sdk.Context, + contractAddr sdk.AccAddress, + msg v1types.IBCPacketReceiveMsg, +) ([]byte, error) { + defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-recv-packet") + contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) + if err != nil { + return nil, err + } + + env := types.NewEnv(ctx, contractAddr) + querier := k.newQueryHandler(ctx, contractAddr) + + gas := k.runtimeGasForContract(ctx) + res, gasUsed, execErr := k.wasmVM.IBCPacketReceive(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) + k.consumeRuntimeGas(ctx, gasUsed) + if execErr != nil { + return nil, sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + } + if res.Err != "" { // handle error case as before https://github.com/CosmWasm/wasmvm/commit/c300106fe5c9426a495f8e10821e00a9330c56c6 + return nil, sdkerrors.Wrap(types.ErrExecuteFailed, res.Err) + } + // note submessage reply results can overwrite the `Acknowledgement` data + return k.handleContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res.Ok.Messages, res.Ok.Attributes, res.Ok.Acknowledgement, res.Ok.Events) +} + +// OnAckPacket calls the contract to handle the "acknowledgement" data which can contain success or failure of a packet +// acknowledgement written on the receiving chain for example. This is application level data and fully owned by the +// contract. The use of the standard acknowledgement envelope is recommended: https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#acknowledgement-envelope +// +// On application errors the contract can revert an operation like returning tokens as in ibc-transfer. +// +// For more information see: https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#packet-flow--handling +func (k Keeper) OnAckPacket( + ctx sdk.Context, + contractAddr sdk.AccAddress, + msg v1types.IBCPacketAckMsg, +) error { + defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-ack-packet") + contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) + if err != nil { + return err + } + + env := types.NewEnv(ctx, contractAddr) + querier := k.newQueryHandler(ctx, contractAddr) + + gas := k.runtimeGasForContract(ctx) + res, gasUsed, execErr := k.wasmVM.IBCPacketAck(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) + k.consumeRuntimeGas(ctx, gasUsed) + if execErr != nil { + return sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + } + return k.handleIBCBasicContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res) +} + +// OnTimeoutPacket calls the contract to let it know the packet was never received on the destination chain within +// the timeout boundaries. +// The contract should handle this on the application level and undo the original operation +func (k Keeper) OnTimeoutPacket( + ctx sdk.Context, + contractAddr sdk.AccAddress, + msg v1types.IBCPacketTimeoutMsg, +) error { + defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-timeout-packet") + + contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) + if err != nil { + return err + } + + env := types.NewEnv(ctx, contractAddr) + querier := k.newQueryHandler(ctx, contractAddr) + + gas := k.runtimeGasForContract(ctx) + res, gasUsed, execErr := k.wasmVM.IBCPacketTimeout(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) + k.consumeRuntimeGas(ctx, gasUsed) + if execErr != nil { + return sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + } + + return k.handleIBCBasicContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res) +} + +func (k Keeper) handleIBCBasicContractResponse(ctx sdk.Context, addr sdk.AccAddress, id string, res *v1types.IBCBasicResponse) error { + _, err := k.handleContractResponse(ctx, addr, id, res.Messages, res.Attributes, nil, res.Events) + return err +} diff --git a/x/compute/internal/types/errors.go b/x/compute/internal/types/errors.go index 7c7dfff50..1cd214613 100644 --- a/x/compute/internal/types/errors.go +++ b/x/compute/internal/types/errors.go @@ -64,9 +64,6 @@ var ( // ErrUnknownMsg error by a message handler to show that it is not responsible for this message type ErrUnknownMsg = sdkErrors.Register(DefaultCodespace, 18, "unknown message from the contract") - // ErrReplyFailed error for rust execution contract failure - ErrReplyFailed = sdkErrors.Register(DefaultCodespace, 19, "reply to contract failed") - // ErrInvalidEvent error if an attribute/event from the contract is invalid ErrInvalidEvent = sdkErrors.Register(DefaultCodespace, 21, "invalid event") ) From eb9f61759f638a04d82c52aa32df6eddfb73d529 Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Tue, 19 Jul 2022 15:17:59 +0300 Subject: [PATCH 12/44] CosmWasm v1 IBC OnOpenChannel --- go-cosmwasm/lib.go | 58 +++++++++++--------- go-cosmwasm/types/v1/ibc.go | 30 ++++++---- x/compute/internal/keeper/relay.go | 22 ++++---- x/compute/internal/types/exported_keepers.go | 46 ++++++++++++++++ 4 files changed, 107 insertions(+), 49 deletions(-) create mode 100644 x/compute/internal/types/exported_keepers.go diff --git a/go-cosmwasm/lib.go b/go-cosmwasm/lib.go index a6f14b5fd..9ce15c57c 100644 --- a/go-cosmwasm/lib.go +++ b/go-cosmwasm/lib.go @@ -85,11 +85,17 @@ func (w *Wasmer) GetCode(code CodeID) (WasmCode, error) { } // This struct helps us to distinguish between v0.10 contract response and v1 contract response -type V010orV1ContractExecResponse struct { - V1 *V1ContractExecResponse `json:"v1,omitempty"` - V010 *V010ContractExecResponse `json:"v010,omitempty"` - InternaReplyEnclaveSig []byte `json:"internal_reply_enclave_sig"` - InternalMsgId []byte `json:"internal_msg_id"` +type ContractExecResponse struct { + V1 *V1ContractExecResponse `json:"v1,omitempty"` + V010 *V010ContractExecResponse `json:"v010,omitempty"` + InternaReplyEnclaveSig []byte `json:"internal_reply_enclave_sig"` + InternalMsgId []byte `json:"internal_msg_id"` + IBCChannelOpen *v1types.IBCChannelOpenResult `json:"ibc_channel_open,omitempty"` + IBCChannelConnect *v1types.IBCBasicResponse `json:"ibc_channel_connect,omitempty"` + IBCChannelClose *v1types.IBCBasicResponse `json:"ibc_channel_close,omitempty"` + IBCPacketReceive *v1types.IBCReceiveResult `json:"ibc_packet_receive,omitempty"` + IBCPacketAck *v1types.IBCBasicResponse `json:"ibc_packet_ack,omitempty"` + IBCPacketTimeout *v1types.IBCBasicResponse `json:"ibc_packet_timeout,omitempty"` } type V010ContractExecResponse struct { @@ -260,55 +266,55 @@ func (w *Wasmer) Execute( return nil, gasUsed, err } - var respV010orV1 V010orV1ContractExecResponse - err = json.Unmarshal(data, &respV010orV1) + var resp ContractExecResponse + err = json.Unmarshal(data, &resp) if err != nil { // unidentified response 🤷 return nil, gasUsed, fmt.Errorf("handle: cannot parse response from json: %w", err) } - isOutputAddressedToReply := (len(respV010orV1.InternaReplyEnclaveSig) > 0 && len(respV010orV1.InternalMsgId) > 0) + isOutputAddressedToReply := (len(resp.InternaReplyEnclaveSig) > 0 && len(resp.InternalMsgId) > 0) // handle v0.10 response - if respV010orV1.V010 != nil { - if respV010orV1.V010.Err != nil { + if resp.V010 != nil { + if resp.V010.Err != nil { return v1types.DataWithInternalReplyInfo{ - InternalMsgId: respV010orV1.InternalMsgId, - InternaReplyEnclaveSig: respV010orV1.InternaReplyEnclaveSig, - Data: []byte(respV010orV1.V010.Err.GenericErr.Msg), - }, gasUsed, fmt.Errorf("%+v", respV010orV1.V010.Err) + InternalMsgId: resp.InternalMsgId, + InternaReplyEnclaveSig: resp.InternaReplyEnclaveSig, + Data: []byte(resp.V010.Err.GenericErr.Msg), + }, gasUsed, fmt.Errorf("%+v", resp.V010.Err) } - if respV010orV1.V010.Ok != nil { + if resp.V010.Ok != nil { if isOutputAddressedToReply { - respV010orV1.V010.Ok.Data, err = AppendReplyInternalDataToData(respV010orV1.V010.Ok.Data, respV010orV1.InternaReplyEnclaveSig, respV010orV1.InternalMsgId) + resp.V010.Ok.Data, err = AppendReplyInternalDataToData(resp.V010.Ok.Data, resp.InternaReplyEnclaveSig, resp.InternalMsgId) if err != nil { return nil, gasUsed, fmt.Errorf("cannot serialize v010 DataWithInternalReplyInfo into binary : %w", err) } } - return respV010orV1.V010.Ok, gasUsed, nil + return resp.V010.Ok, gasUsed, nil } } // handle v1 response - if respV010orV1.V1 != nil { - if respV010orV1.V1.Err != nil { + if resp.V1 != nil { + if resp.V1.Err != nil { return v1types.DataWithInternalReplyInfo{ - InternalMsgId: respV010orV1.InternalMsgId, - InternaReplyEnclaveSig: respV010orV1.InternaReplyEnclaveSig, - Data: []byte(respV010orV1.V1.Err.GenericErr.Msg), - }, gasUsed, fmt.Errorf("%+v", respV010orV1.V1.Err) + InternalMsgId: resp.InternalMsgId, + InternaReplyEnclaveSig: resp.InternaReplyEnclaveSig, + Data: []byte(resp.V1.Err.GenericErr.Msg), + }, gasUsed, fmt.Errorf("%+v", resp.V1.Err) } - if respV010orV1.V1.Ok != nil { + if resp.V1.Ok != nil { if isOutputAddressedToReply { - respV010orV1.V1.Ok.Data, err = AppendReplyInternalDataToData(respV010orV1.V1.Ok.Data, respV010orV1.InternaReplyEnclaveSig, respV010orV1.InternalMsgId) + resp.V1.Ok.Data, err = AppendReplyInternalDataToData(resp.V1.Ok.Data, resp.InternaReplyEnclaveSig, resp.InternalMsgId) if err != nil { return nil, gasUsed, fmt.Errorf("cannot serialize v1 DataWithInternalReplyInfo into binary : %w", err) } } - return respV010orV1.V1.Ok, gasUsed, nil + return resp.V1.Ok, gasUsed, nil } } diff --git a/go-cosmwasm/types/v1/ibc.go b/go-cosmwasm/types/v1/ibc.go index 87b60a361..1d5d994fb 100644 --- a/go-cosmwasm/types/v1/ibc.go +++ b/go-cosmwasm/types/v1/ibc.go @@ -1,8 +1,6 @@ package v1types -import ( - abci "github.com/tendermint/tendermint/abci/types" -) +import v010msgtypes "github.com/enigmampc/SecretNetwork/go-cosmwasm/types/v010" type IBCEndpoint struct { PortID string `json:"port_id"` @@ -137,16 +135,19 @@ func (m *IBCCloseConfirm) ToMsg() IBCChannelCloseMsg { } type IBCPacketReceiveMsg struct { - Packet IBCPacket `json:"packet"` + Packet IBCPacket `json:"packet"` + Relayer string `json:"relayer"` } type IBCPacketAckMsg struct { Acknowledgement IBCAcknowledgement `json:"acknowledgement"` OriginalPacket IBCPacket `json:"original_packet"` + Relayer string `json:"relayer"` } type IBCPacketTimeoutMsg struct { - Packet IBCPacket `json:"packet"` + Packet IBCPacket `json:"packet"` + Relayer string `json:"relayer"` } // TODO: test what the sdk Order.String() represents and how to parse back @@ -196,10 +197,17 @@ type IBCPacket struct { // IBCChannelOpenResult is the raw response from the ibc_channel_open call. // This is mirrors Rust's ContractResult<()>. -// We just check if Err == "" to see if this is success (no other data on success) +// Check if Err == "" to see if this is success +// On Success, IBCV3ChannelOpenResponse *may* be set if the contract is ibcv3 compatible and wishes to +// define a custom version in the handshake. type IBCChannelOpenResult struct { - Ok *struct{} `json:"ok,omitempty"` - Err string `json:"error,omitempty"` + Ok *IBC3ChannelOpenResponse `json:"ok,omitempty"` + Err string `json:"error,omitempty"` +} + +// IBC3ChannelOpenResponse is version negotiation data for the handshake +type IBC3ChannelOpenResponse struct { + Version string `json:"version"` } // This is the return value for the majority of the ibc handlers. @@ -223,7 +231,7 @@ type IBCBasicResponse struct { // "fire and forget". Messages []SubMsg `json:"messages"` // attributes for a log event to return over abci interface - Attributes []abci.EventAttribute `json:"attributes"` + Attributes []v010msgtypes.LogAttribute `json:"attributes"` // custom events (separate from the main one that contains the attributes // above) Events []Event `json:"events"` @@ -254,8 +262,8 @@ type IBCReceiveResponse struct { // If the ReplyOn value matches the result, the runtime will invoke this // contract's `reply` entry point after execution. Otherwise, this is all // "fire and forget". - Messages []SubMsg `json:"messages"` - Attributes []abci.EventAttribute `json:"attributes"` + Messages []SubMsg `json:"messages"` + Attributes []v010msgtypes.LogAttribute `json:"attributes"` // custom events (separate from the main one that contains the attributes // above) Events []Event `json:"events"` diff --git a/x/compute/internal/keeper/relay.go b/x/compute/internal/keeper/relay.go index 587f5a14b..d21e76661 100644 --- a/x/compute/internal/keeper/relay.go +++ b/x/compute/internal/keeper/relay.go @@ -2,6 +2,7 @@ package keeper import ( "encoding/json" + "fmt" "time" "github.com/cosmos/cosmos-sdk/telemetry" @@ -27,7 +28,6 @@ func (k Keeper) OnOpenChannel( msg v1types.IBCChannelOpenMsg, ) (string, error) { defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-open-channel") - version := "" ctx.GasMeter().ConsumeGas(types.InstanceCost, "Loading Compute module: ibc-open-channel") @@ -68,18 +68,16 @@ func (k Keeper) OnOpenChannel( return "", sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } - ///////// - res, gasUsed, execErr := k.wasmVM.IBCChannelOpen(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) - - if execErr != nil { - return "", sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + switch resp := res.(type) { + case *v1types.IBC3ChannelOpenResponse: + if resp != nil { + return resp.Version, nil + } else { + return "", nil + } + default: + return "", sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("cannot cast res to IBC3ChannelOpenResponse: %+v", res)) } - - if res != nil { - version = res.Version - } - - return version, nil } // OnConnectChannel calls the contract to let it know the IBC channel was established. diff --git a/x/compute/internal/types/exported_keepers.go b/x/compute/internal/types/exported_keepers.go new file mode 100644 index 000000000..477f668e7 --- /dev/null +++ b/x/compute/internal/types/exported_keepers.go @@ -0,0 +1,46 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + v1types "github.com/enigmampc/SecretNetwork/go-cosmwasm/types/v1" +) + +// IBCContractKeeper IBC lifecycle event handler +type IBCContractKeeper interface { + OnOpenChannel( + ctx sdk.Context, + contractAddr sdk.AccAddress, + msg v1types.IBCChannelOpenMsg, + ) (string, error) + OnConnectChannel( + ctx sdk.Context, + contractAddr sdk.AccAddress, + msg v1types.IBCChannelConnectMsg, + ) error + OnCloseChannel( + ctx sdk.Context, + contractAddr sdk.AccAddress, + msg v1types.IBCChannelCloseMsg, + ) error + OnRecvPacket( + ctx sdk.Context, + contractAddr sdk.AccAddress, + msg v1types.IBCPacketReceiveMsg, + ) ([]byte, error) + OnAckPacket( + ctx sdk.Context, + contractAddr sdk.AccAddress, + acknowledgement v1types.IBCPacketAckMsg, + ) error + OnTimeoutPacket( + ctx sdk.Context, + contractAddr sdk.AccAddress, + msg v1types.IBCPacketTimeoutMsg, + ) error + // ClaimCapability allows the transfer module to claim a capability + // that IBC module passes to it + ClaimCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) error + // AuthenticateCapability wraps the scopedKeeper's AuthenticateCapability function + AuthenticateCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) bool +} From a3e703ae40ed049d12814d5420c47463e034eeb1 Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Mon, 25 Jul 2022 12:54:49 +0300 Subject: [PATCH 13/44] Parse IBC results in go-cosmwasm --- go-cosmwasm/lib.go | 88 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 11 deletions(-) diff --git a/go-cosmwasm/lib.go b/go-cosmwasm/lib.go index 9ce15c57c..e9d600ff0 100644 --- a/go-cosmwasm/lib.go +++ b/go-cosmwasm/lib.go @@ -91,11 +91,11 @@ type ContractExecResponse struct { InternaReplyEnclaveSig []byte `json:"internal_reply_enclave_sig"` InternalMsgId []byte `json:"internal_msg_id"` IBCChannelOpen *v1types.IBCChannelOpenResult `json:"ibc_channel_open,omitempty"` - IBCChannelConnect *v1types.IBCBasicResponse `json:"ibc_channel_connect,omitempty"` - IBCChannelClose *v1types.IBCBasicResponse `json:"ibc_channel_close,omitempty"` + IBCChannelConnect *v1types.IBCBasicResult `json:"ibc_channel_connect,omitempty"` + IBCChannelClose *v1types.IBCBasicResult `json:"ibc_channel_close,omitempty"` IBCPacketReceive *v1types.IBCReceiveResult `json:"ibc_packet_receive,omitempty"` - IBCPacketAck *v1types.IBCBasicResponse `json:"ibc_packet_ack,omitempty"` - IBCPacketTimeout *v1types.IBCBasicResponse `json:"ibc_packet_timeout,omitempty"` + IBCPacketAck *v1types.IBCBasicResult `json:"ibc_packet_ack,omitempty"` + IBCPacketTimeout *v1types.IBCBasicResult `json:"ibc_packet_timeout,omitempty"` } type V010ContractExecResponse struct { @@ -284,16 +284,16 @@ func (w *Wasmer) Execute( InternaReplyEnclaveSig: resp.InternaReplyEnclaveSig, Data: []byte(resp.V010.Err.GenericErr.Msg), }, gasUsed, fmt.Errorf("%+v", resp.V010.Err) - } - - if resp.V010.Ok != nil { + } else if resp.V010.Ok != nil { if isOutputAddressedToReply { resp.V010.Ok.Data, err = AppendReplyInternalDataToData(resp.V010.Ok.Data, resp.InternaReplyEnclaveSig, resp.InternalMsgId) if err != nil { - return nil, gasUsed, fmt.Errorf("cannot serialize v010 DataWithInternalReplyInfo into binary : %w", err) + return nil, gasUsed, fmt.Errorf("cannot serialize v0.10 DataWithInternalReplyInfo into binary : %w", err) } } return resp.V010.Ok, gasUsed, nil + } else { + return nil, gasUsed, fmt.Errorf("cannot parse v0.10 handle response: %+v", resp) } } @@ -305,9 +305,7 @@ func (w *Wasmer) Execute( InternaReplyEnclaveSig: resp.InternaReplyEnclaveSig, Data: []byte(resp.V1.Err.GenericErr.Msg), }, gasUsed, fmt.Errorf("%+v", resp.V1.Err) - } - - if resp.V1.Ok != nil { + } else if resp.V1.Ok != nil { if isOutputAddressedToReply { resp.V1.Ok.Data, err = AppendReplyInternalDataToData(resp.V1.Ok.Data, resp.InternaReplyEnclaveSig, resp.InternalMsgId) if err != nil { @@ -315,6 +313,74 @@ func (w *Wasmer) Execute( } } return resp.V1.Ok, gasUsed, nil + } else { + return nil, gasUsed, fmt.Errorf("cannot parse v1 handle response: %+v", resp) + } + } + + // handle IBCChannelOpen response + if resp.IBCChannelOpen != nil { + if resp.IBCChannelOpen.Err != "" { + return nil, gasUsed, fmt.Errorf("%s", resp.IBCChannelOpen.Err) + } else if resp.IBCChannelOpen.Ok != nil { + return resp.IBCChannelOpen.Ok, gasUsed, nil + } else { + return nil, gasUsed, fmt.Errorf("cannot parse IBCChannelOpen response: %+v", resp) + } + } + + // handle IBCChannelConnect response + if resp.IBCChannelConnect != nil { + if resp.IBCChannelConnect.Err != "" { + return nil, gasUsed, fmt.Errorf("%s", resp.IBCChannelConnect.Err) + } else if resp.IBCChannelConnect.Ok != nil { + return resp.IBCChannelConnect.Ok, gasUsed, nil + } else { + return nil, gasUsed, fmt.Errorf("cannot parse IBCChannelConnect response: %+v", resp) + } + } + + // handle IBCChannelClose response + if resp.IBCChannelClose != nil { + if resp.IBCChannelClose.Err != "" { + return nil, gasUsed, fmt.Errorf("%s", resp.IBCChannelClose.Err) + } else if resp.IBCChannelClose.Ok != nil { + return resp.IBCChannelClose.Ok, gasUsed, nil + } else { + return nil, gasUsed, fmt.Errorf("cannot parse IBCChannelClose response: %+v", resp) + } + } + + // handle IBCPacketReceive response + if resp.IBCPacketReceive != nil { + if resp.IBCPacketReceive.Err != "" { + return nil, gasUsed, fmt.Errorf("%s", resp.IBCPacketReceive.Err) + } else if resp.IBCPacketReceive.Ok != nil { + return resp.IBCPacketReceive.Ok, gasUsed, nil + } else { + return nil, gasUsed, fmt.Errorf("cannot parse IBCPacketReceive response: %+v", resp) + } + } + + // handle IBCPacketAck response + if resp.IBCPacketAck != nil { + if resp.IBCPacketAck.Err != "" { + return nil, gasUsed, fmt.Errorf("%s", resp.IBCPacketAck.Err) + } else if resp.IBCPacketAck.Ok != nil { + return resp.IBCPacketAck.Ok, gasUsed, nil + } else { + return nil, gasUsed, fmt.Errorf("cannot parse IBCPacketAck response: %+v", resp) + } + } + + // handle IBCPacketTimeout response + if resp.IBCPacketTimeout != nil { + if resp.IBCPacketTimeout.Err != "" { + return nil, gasUsed, fmt.Errorf("%s", resp.IBCPacketTimeout.Err) + } else if resp.IBCPacketTimeout.Ok != nil { + return resp.IBCPacketTimeout.Ok, gasUsed, nil + } else { + return nil, gasUsed, fmt.Errorf("cannot parse IBCPacketTimeout response: %+v", resp) } } From 7929c348a817af5cae1ae8bce7658c2c1d6c59d6 Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Mon, 25 Jul 2022 13:19:24 +0300 Subject: [PATCH 14/44] Refactor IBC contract calls, lots of shared code --- x/compute/internal/keeper/ibc.go | 5 ++++ x/compute/internal/keeper/keeper.go | 3 ++ x/compute/internal/keeper/relay.go | 46 +++++++++++++++++------------ 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/x/compute/internal/keeper/ibc.go b/x/compute/internal/keeper/ibc.go index 8df6fdecc..900927b82 100644 --- a/x/compute/internal/keeper/ibc.go +++ b/x/compute/internal/keeper/ibc.go @@ -37,3 +37,8 @@ func PortIDForContract(addr sdk.AccAddress) string { func (k Keeper) ClaimCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) error { return k.capabilityKeeper.ClaimCapability(ctx, cap, name) } + +// AuthenticateCapability wraps the scopedKeeper's AuthenticateCapability function +func (k Keeper) AuthenticateCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) bool { + return k.capabilityKeeper.AuthenticateCapability(ctx, cap, name) +} diff --git a/x/compute/internal/keeper/keeper.go b/x/compute/internal/keeper/keeper.go index e763a8033..ae053a22e 100644 --- a/x/compute/internal/keeper/keeper.go +++ b/x/compute/internal/keeper/keeper.go @@ -10,6 +10,7 @@ import ( "time" capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" channelkeeper "github.com/cosmos/ibc-go/v3/modules/core/04-channel/keeper" portkeeper "github.com/cosmos/ibc-go/v3/modules/core/05-port/keeper" wasmTypes "github.com/enigmampc/SecretNetwork/go-cosmwasm/types" @@ -1111,3 +1112,5 @@ func (k Keeper) reply(ctx sdk.Context, contractAddress sdk.AccAddress, reply v1w return nil, sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("cannot detect response type: %+v", res)) } } + + diff --git a/x/compute/internal/keeper/relay.go b/x/compute/internal/keeper/relay.go index d21e76661..b855d3513 100644 --- a/x/compute/internal/keeper/relay.go +++ b/x/compute/internal/keeper/relay.go @@ -17,20 +17,10 @@ import ( var _ types.IBCContractKeeper = (*Keeper)(nil) -// OnOpenChannel calls the contract to participate in the IBC channel handshake step. -// In the IBC protocol this is either the `Channel Open Init` event on the initiating chain or -// `Channel Open Try` on the counterparty chain. -// Protocol version and channel ordering should be verified for example. -// See https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#channel-lifecycle-management -func (k Keeper) OnOpenChannel( - ctx sdk.Context, +func (k Keeper) ibcContractCall(ctx sdk.Context, contractAddress sdk.AccAddress, - msg v1types.IBCChannelOpenMsg, -) (string, error) { - defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-open-channel") - - ctx.GasMeter().ConsumeGas(types.InstanceCost, "Loading Compute module: ibc-open-channel") - + msgBz []byte, +) (interface{}, error) { verificationInfo := types.NewVerificationInfo([]byte{}, sdktxsigning.SignMode_SIGN_MODE_UNSPECIFIED, []byte{}, []byte{}, []byte{}, nil) _, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddress) @@ -43,8 +33,8 @@ func (k Keeper) OnOpenChannel( contractKey := store.Get(types.GetContractEnclaveKey(contractAddress)) env := types.NewEnv( ctx, - sdk.AccAddress{}, /* empty because it's unused in queries */ - sdk.NewCoins(), /* empty because it's unused in queries */ + sdk.AccAddress{}, /* there's no MessageInfo for IBC contract calls */ + sdk.NewCoins(), /* there's no MessageInfo for IBC contract calls */ contractAddress, contractKey, ) @@ -55,15 +45,33 @@ func (k Keeper) OnOpenChannel( Plugins: k.queryPlugins, } + gas := gasForContract(ctx) + res, gasUsed, err := k.wasmer.Execute(codeInfo.CodeHash, env, msgBz, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, verificationInfo, wasmTypes.HandleTypeReply) + consumeGas(ctx, gasUsed) + + return res, err +} + +// OnOpenChannel calls the contract to participate in the IBC channel handshake step. +// In the IBC protocol this is either the `Channel Open Init` event on the initiating chain or +// `Channel Open Try` on the counterparty chain. +// Protocol version and channel ordering should be verified for example. +// See https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#channel-lifecycle-management +func (k Keeper) OnOpenChannel( + ctx sdk.Context, + contractAddress sdk.AccAddress, + msg v1types.IBCChannelOpenMsg, +) (string, error) { + defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-open-channel") + + ctx.GasMeter().ConsumeGas(types.InstanceCost, "Loading Compute module: ibc-open-channel") + msgBz, err := json.Marshal(msg) if err != nil { return "", sdkerrors.Wrap(err, "ibc-open-channel") } - gas := gasForContract(ctx) - res, gasUsed, err := k.wasmer.Execute(codeInfo.CodeHash, env, msgBz, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, verificationInfo, wasmTypes.HandleTypeReply) - consumeGas(ctx, gasUsed) - + res, err := k.ibcContractCall(ctx, contractAddress, msgBz) if err != nil { return "", sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } From 4604b60c352b86ea1e1f3444d4330409b2e6238a Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Mon, 25 Jul 2022 13:39:46 +0300 Subject: [PATCH 15/44] IBC keeper OnTimeoutPacket OnAckPacket OnCloseChannel OnConnectChannel --- x/compute/internal/keeper/keeper.go | 3 - x/compute/internal/keeper/relay.go | 129 +++++++++++++++++----------- 2 files changed, 79 insertions(+), 53 deletions(-) diff --git a/x/compute/internal/keeper/keeper.go b/x/compute/internal/keeper/keeper.go index ae053a22e..e763a8033 100644 --- a/x/compute/internal/keeper/keeper.go +++ b/x/compute/internal/keeper/keeper.go @@ -10,7 +10,6 @@ import ( "time" capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" - capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" channelkeeper "github.com/cosmos/ibc-go/v3/modules/core/04-channel/keeper" portkeeper "github.com/cosmos/ibc-go/v3/modules/core/05-port/keeper" wasmTypes "github.com/enigmampc/SecretNetwork/go-cosmwasm/types" @@ -1112,5 +1111,3 @@ func (k Keeper) reply(ctx sdk.Context, contractAddress sdk.AccAddress, reply v1w return nil, sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("cannot detect response type: %+v", res)) } } - - diff --git a/x/compute/internal/keeper/relay.go b/x/compute/internal/keeper/relay.go index b855d3513..2d05822f2 100644 --- a/x/compute/internal/keeper/relay.go +++ b/x/compute/internal/keeper/relay.go @@ -20,6 +20,7 @@ var _ types.IBCContractKeeper = (*Keeper)(nil) func (k Keeper) ibcContractCall(ctx sdk.Context, contractAddress sdk.AccAddress, msgBz []byte, + callType wasmTypes.HandleType, ) (interface{}, error) { verificationInfo := types.NewVerificationInfo([]byte{}, sdktxsigning.SignMode_SIGN_MODE_UNSPECIFIED, []byte{}, []byte{}, []byte{}, nil) @@ -46,12 +47,33 @@ func (k Keeper) ibcContractCall(ctx sdk.Context, } gas := gasForContract(ctx) - res, gasUsed, err := k.wasmer.Execute(codeInfo.CodeHash, env, msgBz, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, verificationInfo, wasmTypes.HandleTypeReply) + res, gasUsed, err := k.wasmer.Execute(codeInfo.CodeHash, env, msgBz, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, verificationInfo, callType) consumeGas(ctx, gasUsed) return res, err } +func (k Keeper) parseThenHandleIBCBasicContractResponse(ctx sdk.Context, + contractAddress sdk.AccAddress, + res interface{}, +) error { + switch resp := res.(type) { + case *v1types.IBCBasicResponse: + if resp != nil { + contractInfo, _, _, err := k.contractInstance(ctx, contractAddress) + if err != nil { + return err + } + + return k.handleIBCBasicContractResponse(ctx, contractAddress, contractInfo.IBCPortID, resp) + } else { + return sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("cannot parse IBCBasicResponse: %+v", res)) + } + default: + return sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("cannot cast res to IBCBasicResponse: %+v", res)) + } +} + // OnOpenChannel calls the contract to participate in the IBC channel handshake step. // In the IBC protocol this is either the `Channel Open Init` event on the initiating chain or // `Channel Open Try` on the counterparty chain. @@ -71,7 +93,7 @@ func (k Keeper) OnOpenChannel( return "", sdkerrors.Wrap(err, "ibc-open-channel") } - res, err := k.ibcContractCall(ctx, contractAddress, msgBz) + res, err := k.ibcContractCall(ctx, contractAddress, msgBz, wasmTypes.HandleTypeIbcChannelConnect) if err != nil { return "", sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } @@ -84,7 +106,7 @@ func (k Keeper) OnOpenChannel( return "", nil } default: - return "", sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("cannot cast res to IBC3ChannelOpenResponse: %+v", res)) + return "", sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("ibc-open-channel: cannot cast res to IBC3ChannelOpenResponse: %+v", res)) } } @@ -97,26 +119,28 @@ func (k Keeper) OnOpenChannel( // See https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#channel-lifecycle-management func (k Keeper) OnConnectChannel( ctx sdk.Context, - contractAddr sdk.AccAddress, + contractAddress sdk.AccAddress, msg v1types.IBCChannelConnectMsg, ) error { defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-connect-channel") - contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) + + ctx.GasMeter().ConsumeGas(types.InstanceCost, "Loading Compute module: ibc-connect-channel") + + msgBz, err := json.Marshal(msg) if err != nil { - return err + return sdkerrors.Wrap(err, "ibc-connect-channel") } - env := types.NewEnv(ctx, contractAddr) - querier := k.newQueryHandler(ctx, contractAddr) - - gas := k.runtimeGasForContract(ctx) - res, gasUsed, execErr := k.wasmVM.IBCChannelConnect(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) - k.consumeRuntimeGas(ctx, gasUsed) - if execErr != nil { - return sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + res, err := k.ibcContractCall(ctx, contractAddress, msgBz, wasmTypes.HandleTypeIbcChannelConnect) + if err != nil { + return sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } - return k.handleIBCBasicContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res) + err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, res) + if err != nil { + sdkerrors.Wrap(err, "ibc-connect-channel") + } + return nil } // OnCloseChannel calls the contract to let it know the IBC channel is closed. @@ -127,27 +151,28 @@ func (k Keeper) OnConnectChannel( // See https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#channel-lifecycle-management func (k Keeper) OnCloseChannel( ctx sdk.Context, - contractAddr sdk.AccAddress, + contractAddress sdk.AccAddress, msg v1types.IBCChannelCloseMsg, ) error { defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-close-channel") - contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) + ctx.GasMeter().ConsumeGas(types.InstanceCost, "Loading Compute module: ibc-close-channel") + + msgBz, err := json.Marshal(msg) if err != nil { - return err + return sdkerrors.Wrap(err, "ibc-close-channel") } - params := types.NewEnv(ctx, contractAddr) - querier := k.newQueryHandler(ctx, contractAddr) - - gas := k.runtimeGasForContract(ctx) - res, gasUsed, execErr := k.wasmVM.IBCChannelClose(codeInfo.CodeHash, params, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) - k.consumeRuntimeGas(ctx, gasUsed) - if execErr != nil { - return sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + res, err := k.ibcContractCall(ctx, contractAddress, msgBz, wasmTypes.HandleTypeIbcChannelClose) + if err != nil { + return sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } - return k.handleIBCBasicContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res) + err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, res) + if err != nil { + sdkerrors.Wrap(err, "ibc-close-channel") + } + return nil } // OnRecvPacket calls the contract to process the incoming IBC packet. The contract fully owns the data processing and @@ -192,25 +217,28 @@ func (k Keeper) OnRecvPacket( // For more information see: https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#packet-flow--handling func (k Keeper) OnAckPacket( ctx sdk.Context, - contractAddr sdk.AccAddress, + contractAddress sdk.AccAddress, msg v1types.IBCPacketAckMsg, ) error { defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-ack-packet") - contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) + + ctx.GasMeter().ConsumeGas(types.InstanceCost, "Loading Compute module: ibc-ack-packet") + + msgBz, err := json.Marshal(msg) if err != nil { - return err + return sdkerrors.Wrap(err, "ibc-ack-packet") } - env := types.NewEnv(ctx, contractAddr) - querier := k.newQueryHandler(ctx, contractAddr) + res, err := k.ibcContractCall(ctx, contractAddress, msgBz, wasmTypes.HandleTypeIbcPacketAck) + if err != nil { + return sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) + } - gas := k.runtimeGasForContract(ctx) - res, gasUsed, execErr := k.wasmVM.IBCPacketAck(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) - k.consumeRuntimeGas(ctx, gasUsed) - if execErr != nil { - return sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, res) + if err != nil { + sdkerrors.Wrap(err, "ibc-ack-packet") } - return k.handleIBCBasicContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res) + return nil } // OnTimeoutPacket calls the contract to let it know the packet was never received on the destination chain within @@ -218,27 +246,28 @@ func (k Keeper) OnAckPacket( // The contract should handle this on the application level and undo the original operation func (k Keeper) OnTimeoutPacket( ctx sdk.Context, - contractAddr sdk.AccAddress, + contractAddress sdk.AccAddress, msg v1types.IBCPacketTimeoutMsg, ) error { defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-timeout-packet") - contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) + ctx.GasMeter().ConsumeGas(types.InstanceCost, "Loading Compute module: ibc-timeout-packet") + + msgBz, err := json.Marshal(msg) if err != nil { - return err + return sdkerrors.Wrap(err, "ibc-timeout-packet") } - env := types.NewEnv(ctx, contractAddr) - querier := k.newQueryHandler(ctx, contractAddr) - - gas := k.runtimeGasForContract(ctx) - res, gasUsed, execErr := k.wasmVM.IBCPacketTimeout(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) - k.consumeRuntimeGas(ctx, gasUsed) - if execErr != nil { - return sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + res, err := k.ibcContractCall(ctx, contractAddress, msgBz, wasmTypes.HandleTypeIbcPacketTimeout) + if err != nil { + return sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } - return k.handleIBCBasicContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res) + err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, res) + if err != nil { + sdkerrors.Wrap(err, "ibc-timeout-packet") + } + return nil } func (k Keeper) handleIBCBasicContractResponse(ctx sdk.Context, addr sdk.AccAddress, id string, res *v1types.IBCBasicResponse) error { From 73b3e3abd7758075f0c0a548f6ba4e8fad0019f7 Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Tue, 26 Jul 2022 11:24:09 +0300 Subject: [PATCH 16/44] Keeper OnRecvPacket --- x/compute/internal/keeper/relay.go | 62 +++++++++++++++++++----------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/x/compute/internal/keeper/relay.go b/x/compute/internal/keeper/relay.go index 2d05822f2..a60a928e0 100644 --- a/x/compute/internal/keeper/relay.go +++ b/x/compute/internal/keeper/relay.go @@ -55,6 +55,7 @@ func (k Keeper) ibcContractCall(ctx sdk.Context, func (k Keeper) parseThenHandleIBCBasicContractResponse(ctx sdk.Context, contractAddress sdk.AccAddress, + inputMsg []byte, res interface{}, ) error { switch resp := res.(type) { @@ -65,9 +66,9 @@ func (k Keeper) parseThenHandleIBCBasicContractResponse(ctx sdk.Context, return err } - return k.handleIBCBasicContractResponse(ctx, contractAddress, contractInfo.IBCPortID, resp) + return k.handleIBCBasicContractResponse(ctx, contractAddress, contractInfo.IBCPortID, inputMsg, resp) } else { - return sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("cannot parse IBCBasicResponse: %+v", res)) + return sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("null pointer IBCBasicResponse: %+v", res)) } default: return sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("cannot cast res to IBCBasicResponse: %+v", res)) @@ -136,7 +137,7 @@ func (k Keeper) OnConnectChannel( return sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } - err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, res) + err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, msgBz, res) if err != nil { sdkerrors.Wrap(err, "ibc-connect-channel") } @@ -168,7 +169,7 @@ func (k Keeper) OnCloseChannel( return sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } - err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, res) + err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, msgBz, res) if err != nil { sdkerrors.Wrap(err, "ibc-close-channel") } @@ -183,29 +184,42 @@ func (k Keeper) OnCloseChannel( // For more information see: https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#packet-flow--handling func (k Keeper) OnRecvPacket( ctx sdk.Context, - contractAddr sdk.AccAddress, + contractAddress sdk.AccAddress, msg v1types.IBCPacketReceiveMsg, ) ([]byte, error) { defer telemetry.MeasureSince(time.Now(), "compute", "keeper", "ibc-recv-packet") - contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) + + ctx.GasMeter().ConsumeGas(types.InstanceCost, "Loading Compute module: ibc-recv-packet") + + msgBz, err := json.Marshal(msg) if err != nil { - return nil, err + return nil, sdkerrors.Wrap(err, "ibc-recv-packet") } - env := types.NewEnv(ctx, contractAddr) - querier := k.newQueryHandler(ctx, contractAddr) - - gas := k.runtimeGasForContract(ctx) - res, gasUsed, execErr := k.wasmVM.IBCPacketReceive(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) - k.consumeRuntimeGas(ctx, gasUsed) - if execErr != nil { - return nil, sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + res, err := k.ibcContractCall(ctx, contractAddress, msgBz, wasmTypes.HandleTypeIbcChannelConnect) + if err != nil { + return nil, sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } - if res.Err != "" { // handle error case as before https://github.com/CosmWasm/wasmvm/commit/c300106fe5c9426a495f8e10821e00a9330c56c6 - return nil, sdkerrors.Wrap(types.ErrExecuteFailed, res.Err) + + switch resp := res.(type) { + case *v1types.IBCReceiveResponse: + if resp != nil { + contractInfo, _, _, err := k.contractInstance(ctx, contractAddress) + if err != nil { + return nil, err + } + verificationInfo := types.NewVerificationInfo([]byte{}, sdktxsigning.SignMode_SIGN_MODE_UNSPECIFIED, []byte{}, []byte{}, []byte{}, nil) + + // note submessage reply results can overwrite the `Acknowledgement` data + return k.handleContractResponse(ctx, contractAddress, contractInfo.IBCPortID, resp.Messages, resp.Attributes, resp.Events, resp.Acknowledgement, msgBz, verificationInfo, wasmTypes.CosmosMsgVersionV1) + } else { + // should never get here as it's already checked in + // https://github.com/scrtlabs/SecretNetwork/blob/bd46776c/go-cosmwasm/lib.go#L358 + return nil, sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("ibc-recv-packet: null pointer IBCReceiveResponse: %+v", res)) + } + default: + return nil, sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("ibc-recv-packet: cannot cast res to IBCReceiveResponse: %+v", res)) } - // note submessage reply results can overwrite the `Acknowledgement` data - return k.handleContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res.Ok.Messages, res.Ok.Attributes, res.Ok.Acknowledgement, res.Ok.Events) } // OnAckPacket calls the contract to handle the "acknowledgement" data which can contain success or failure of a packet @@ -234,7 +248,7 @@ func (k Keeper) OnAckPacket( return sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } - err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, res) + err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, msgBz, res) if err != nil { sdkerrors.Wrap(err, "ibc-ack-packet") } @@ -263,14 +277,16 @@ func (k Keeper) OnTimeoutPacket( return sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } - err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, res) + err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, msgBz, res) if err != nil { sdkerrors.Wrap(err, "ibc-timeout-packet") } return nil } -func (k Keeper) handleIBCBasicContractResponse(ctx sdk.Context, addr sdk.AccAddress, id string, res *v1types.IBCBasicResponse) error { - _, err := k.handleContractResponse(ctx, addr, id, res.Messages, res.Attributes, nil, res.Events) +func (k Keeper) handleIBCBasicContractResponse(ctx sdk.Context, addr sdk.AccAddress, ibcPortID string, inputMsg []byte, res *v1types.IBCBasicResponse) error { + verificationInfo := types.NewVerificationInfo([]byte{}, sdktxsigning.SignMode_SIGN_MODE_UNSPECIFIED, []byte{}, []byte{}, []byte{}, nil) + + _, err := k.handleContractResponse(ctx, addr, ibcPortID, res.Messages, res.Attributes, res.Events, nil, inputMsg, verificationInfo, wasmTypes.CosmosMsgVersionV1) return err } From e4ffb9cdcf784e678db84d17937d8089a9742e55 Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Tue, 26 Jul 2022 17:33:18 +0300 Subject: [PATCH 17/44] WIP: IBC on the enclave side --- .../src/contract_operations.rs | 71 +++++++++++++++++-- .../enclaves/shared/cosmos-types/src/types.rs | 6 ++ 2 files changed, 72 insertions(+), 5 deletions(-) diff --git a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs index 02bc35d2a..dd9d91d94 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs @@ -225,12 +225,12 @@ pub fn parse_message( sig_info: &SigInfo, handle_type: &HandleType, ) -> Result { - let orig_secret_msg = SecretMessage::from_slice(message)?; - return match handle_type { HandleType::HANDLE_TYPE_EXECUTE => { + let orig_secret_msg = SecretMessage::from_slice(message)?; + trace!( - "handle input before decryption: {:?}", + "execute input before decryption: {:?}", base64::encode(&message) ); let decrypted_msg = orig_secret_msg.decrypt()?; @@ -242,10 +242,14 @@ pub fn parse_message( contract_hash_for_validation: None, }) } - HandleType::HANDLE_TYPE_REPLY => { + let orig_secret_msg = SecretMessage::from_slice(message)?; + if sig_info.sign_mode == SignMode::SIGN_MODE_UNSPECIFIED { - trace!("reply input is not encrypted"); + trace!( + "reply input is not encrypted: {:?}", + base64::encode(&message) + ); let decrypted_msg = orig_secret_msg.msg.clone(); let mut reply: Reply = serde_json::from_slice(&decrypted_msg) .map_err(|err| { @@ -512,6 +516,63 @@ pub fn parse_message( } } } + HandleType::HANDLE_TYPE_IBC_CHANNEL_OPEN => todo!(), + HandleType::HANDLE_TYPE_IBC_CHANNEL_CONNECT => todo!(), + HandleType::HANDLE_TYPE_IBC_CHANNEL_CLOSE => todo!(), + HandleType::HANDLE_TYPE_IBC_PACKET_RECEIVE => { + let orig_secret_msg = match SecretMessage::from_slice(message) { + Ok(orig_secret_msg) => orig_secret_msg, + Err(_) => { + trace!( + "ibc_packet_receive msg is not SecretMessage (probably plaintext): {:?}", + base64::encode(&message) + ); + + SecretMessage { + nonce: [0; 32], + user_public_key: [0; 32], + msg: message.into(), + } + } + }; + + match orig_secret_msg.decrypt() { + Ok(decrypted_msg) => { + // IBC packet is encrypted + + trace!( + "ibc_packet_receive input before decryption: {:?}", + base64::encode(&message) + ); + + Ok(ParsedMessage { + should_validate_sig_info: true, + was_msg_encrypted: true, + secret_msg: orig_secret_msg, + decrypted_msg, + contract_hash_for_validation: None, + }) + } + Err(_) => { + // assume packet is not encrypted, continue in plaintext mode + + trace!( + "ibc_packet_receive input is not encrypted: {:?}", + base64::encode(&message) + ); + + Ok(ParsedMessage { + should_validate_sig_info: false, + was_msg_encrypted: false, + secret_msg: orig_secret_msg, + decrypted_msg: orig_secret_msg.msg, + contract_hash_for_validation: None, + }) + } + } + } + HandleType::HANDLE_TYPE_IBC_PACKET_ACK => todo!(), + HandleType::HANDLE_TYPE_IBC_PACKET_TIMEOUT => todo!(), }; } diff --git a/cosmwasm/enclaves/shared/cosmos-types/src/types.rs b/cosmwasm/enclaves/shared/cosmos-types/src/types.rs index 873a80733..b86c0460d 100644 --- a/cosmwasm/enclaves/shared/cosmos-types/src/types.rs +++ b/cosmwasm/enclaves/shared/cosmos-types/src/types.rs @@ -146,6 +146,12 @@ pub enum SignModeDef { pub enum HandleType { HANDLE_TYPE_EXECUTE = 0, HANDLE_TYPE_REPLY = 1, + HANDLE_TYPE_IBC_CHANNEL_OPEN = 2, + HANDLE_TYPE_IBC_CHANNEL_CONNECT = 3, + HANDLE_TYPE_IBC_CHANNEL_CLOSE = 4, + HANDLE_TYPE_IBC_PACKET_RECEIVE = 5, + HANDLE_TYPE_IBC_PACKET_ACK = 6, + HANDLE_TYPE_IBC_PACKET_TIMEOUT = 7, } impl HandleType { From 5bee32a09966063f6b2830e54ef87ace9465d71b Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Wed, 27 Jul 2022 09:12:36 +0300 Subject: [PATCH 18/44] Remove old CosmWasm remnants --- x/compute/client/cli/tx.go | 22 +---- x/compute/internal/keeper/keeper.go | 27 +----- x/compute/internal/types/msg.go | 135 +--------------------------- 3 files changed, 5 insertions(+), 179 deletions(-) diff --git a/x/compute/client/cli/tx.go b/x/compute/client/cli/tx.go index 606cc6c07..82beb5b14 100644 --- a/x/compute/client/cli/tx.go +++ b/x/compute/client/cli/tx.go @@ -31,7 +31,6 @@ const ( flagProposalType = "type" flagIoMasterKey = "enclave-key" flagCodeHash = "code-hash" - // flagAdmin = "admin" ) // GetTxCmd returns the transaction commands for this module @@ -47,10 +46,6 @@ func GetTxCmd() *cobra.Command { StoreCodeCmd(), InstantiateContractCmd(), ExecuteContractCmd(), - // Currently not supporting these commands - // MigrateContractCmd(cdc), - // UpdateContractAdminCmd(cdc), - // ClearContractAdminCmd(cdc), ) return txCmd } @@ -105,20 +100,6 @@ func parseStoreCodeArgs(args []string, cliCtx client.Context, flags *flag.FlagSe return types.MsgStoreCode{}, fmt.Errorf("invalid input file. Use wasm binary or gzip") } - /* - var perm *types.AccessConfig - if onlyAddrStr := viper.GetString(flagInstantiateByAddress); onlyAddrStr != "" { - allowedAddr, err := sdk.AccAddressFromBech32(onlyAddrStr) - if err != nil { - return types.MsgStoreCode{}, sdkerrors.Wrap(err, flagInstantiateByAddress) - } - x := types.OnlyAddress.With(allowedAddr) - perm = &x - } else if everybody := viper.GetBool(flagInstantiateByEverybody); everybody { - perm = &types.AllowEverybody - } - */ - source, err := flags.GetString(flagSource) if err != nil { return types.MsgStoreCode{}, fmt.Errorf("source: %s", err) @@ -134,7 +115,6 @@ func parseStoreCodeArgs(args []string, cliCtx client.Context, flags *flag.FlagSe WASMByteCode: wasm, Source: source, Builder: builder, - // InstantiatePermission: perm, } return msg, nil } @@ -142,7 +122,7 @@ func parseStoreCodeArgs(args []string, cliCtx client.Context, flags *flag.FlagSe // InstantiateContractCmd will instantiate a contract from previously uploaded code. func InstantiateContractCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "instantiate [code_id_int64] [json_encoded_init_args] --label [text] " /* --admin [address,optional] */ + "--amount [coins,optional]", + Use: "instantiate [code_id_int64] [json_encoded_init_args] --label [text] --amount [coins,optional]", Short: "Instantiate a wasm contract", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { diff --git a/x/compute/internal/keeper/keeper.go b/x/compute/internal/keeper/keeper.go index e763a8033..0789b31fb 100644 --- a/x/compute/internal/keeper/keeper.go +++ b/x/compute/internal/keeper/keeper.go @@ -163,15 +163,6 @@ func (k Keeper) setParams(ctx sdk.Context, ps types.Params) { // Create uploads and compiles a WASM contract, returning a short identifier for the contract func (k Keeper) Create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, source string, builder string) (codeID uint64, err error) { - /* - return k.create(ctx, creator, wasmCode, source, builder, &types.AccessConfig{Type: types.Everybody} , k.authZPolicy ) - } - - func (k Keeper) create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, source string, builder string, instantiateAccess *types.AccessConfig ) (codeID uint64, err error) { - if !authZ.CanCreateCode(k.getUploadAccessConfig(ctx), creator) { - return 0, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "can not create code") - } - */ wasmCode, err = uncompress(wasmCode) if err != nil { return 0, sdkerrors.Wrap(types.ErrCreateFailed, err.Error()) @@ -180,18 +171,12 @@ func (k Keeper) Create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, codeHash, err := k.wasmer.Create(wasmCode) if err != nil { - // return 0, sdkerrors.Wrap(err, "cosmwasm create") return 0, sdkerrors.Wrap(types.ErrCreateFailed, err.Error()) } store := ctx.KVStore(k.storeKey) codeID = k.autoIncrementID(ctx, types.KeyLastCodeID) - /* - if instantiateAccess == nil { - defaultAccessConfig := k.getInstantiateAccessConfig(ctx).With(creator) - instantiateAccess = &defaultAccessConfig - } - */ - codeInfo := types.NewCodeInfo(codeHash, creator, source, builder /* , *instantiateAccess */) + + codeInfo := types.NewCodeInfo(codeHash, creator, source, builder) // 0x01 | codeID (uint64) -> ContractInfo store.Set(types.GetCodeKey(codeID), k.cdc.MustMarshal(&codeInfo)) @@ -418,10 +403,6 @@ func (k Keeper) Instantiate(ctx sdk.Context, codeID uint64, creator sdk.AccAddre var codeInfo types.CodeInfo k.cdc.MustUnmarshal(bz, &codeInfo) - // if !authZ.CanInstantiateContract(codeInfo.InstantiateConfig, creator) { - // return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "can not instantiate") - // } - // prepare env for contract instantiate call env := types.NewEnv(ctx, creator, deposit, contractAddress, nil) @@ -464,8 +445,6 @@ func (k Keeper) Instantiate(ctx sdk.Context, codeID uint64, creator sdk.AccAddre contractInfo := types.NewContractInfo(codeID, creator, label, createdAt) store.Set(types.GetContractAddressKey(contractAddress), k.cdc.MustMarshal(&contractInfo)) - // fmt.Printf("Storing key: %v for account %s\n", key, contractAddress) - store.Set(types.GetContractEnclaveKey(contractAddress), key) store.Set(types.GetContractLabelPrefix(label), contractAddress) @@ -997,8 +976,6 @@ func (k Keeper) importContract(ctx sdk.Context, contractAddr sdk.AccAddress, cus return sdkerrors.Wrapf(types.ErrDuplicate, "contract: %s", contractAddr) } - // historyEntry := c.ResetFromGenesis(ctx) - // k.appendToContractHistory(ctx, contractAddr, historyEntry) k.setContractCustomInfo(ctx, contractAddr, customInfo) k.setContractInfo(ctx, contractAddr, c) return k.importContractState(ctx, contractAddr, state) diff --git a/x/compute/internal/types/msg.go b/x/compute/internal/types/msg.go index 7eb0f4877..5d7b8fcc4 100644 --- a/x/compute/internal/types/msg.go +++ b/x/compute/internal/types/msg.go @@ -29,13 +29,7 @@ func (msg MsgStoreCode) ValidateBasic() error { if err := validateBuilder(msg.Builder); err != nil { return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "builder %s", err.Error()) } - /* - if msg.InstantiatePermission != nil { - if err := msg.InstantiatePermission.ValidateBasic(); err != nil { - return sdkerrors.Wrap(err, "instantiate permission") - } - } - */ + return nil } @@ -72,16 +66,6 @@ func (msg MsgInstantiateContract) ValidateBasic() error { return sdkerrors.ErrInvalidCoins } - /* - if len(msg.Admin) != 0 { - if err := sdk.VerifyAddressFormat(msg.Admin); err != nil { - return err - } - } - if !json.Valid(msg.InitMsg) { - return sdkerrors.Wrap(ErrInvalid, "init msg json") - } - */ return nil } @@ -112,11 +96,7 @@ func (msg MsgExecuteContract) ValidateBasic() error { if !msg.SentFunds.IsValid() { return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, "sentFunds") } - /* - if !json.Valid(msg.Msg) { - return sdkerrors.Wrap(ErrInvalid, "msg json") - } - */ + return nil } @@ -127,114 +107,3 @@ func (msg MsgExecuteContract) GetSignBytes() []byte { func (msg MsgExecuteContract) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Sender} } - -/* -type MsgMigrateContract struct { - Sender sdk.AccAddress `json:"sender" yaml:"sender"` - Contract sdk.AccAddress `json:"contract" yaml:"contract"` - CodeID uint64 `json:"code_id" yaml:"code_id"` - MigrateMsg json.RawMessage `json:"msg" yaml:"msg"` -} - -func (msg MsgMigrateContract) Route() string { - return RouterKey -} - -func (msg MsgMigrateContract) Type() string { - return "migrate" -} - -func (msg MsgMigrateContract) ValidateBasic() error { - if msg.CodeID == 0 { - return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "code_id is required") - } - if err := sdk.VerifyAddressFormat(msg.Sender); err != nil { - return sdkerrors.Wrap(err, "sender") - } - if err := sdk.VerifyAddressFormat(msg.Contract); err != nil { - return sdkerrors.Wrap(err, "contract") - } - if !json.Valid(msg.MigrateMsg) { - return sdkerrors.Wrap(ErrInvalid, "migrate msg json") - } - - return nil -} - -func (msg MsgMigrateContract) GetSignBytes() []byte { - return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) -} - -func (msg MsgMigrateContract) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.Sender} -} - -type MsgUpdateAdmin struct { - Sender sdk.AccAddress `json:"sender" yaml:"sender"` - NewAdmin sdk.AccAddress `json:"new_admin" yaml:"new_admin"` - Contract sdk.AccAddress `json:"contract" yaml:"contract"` -} - -func (msg MsgUpdateAdmin) Route() string { - return RouterKey -} - -func (msg MsgUpdateAdmin) Type() string { - return "update-contract-admin" -} - -func (msg MsgUpdateAdmin) ValidateBasic() error { - if err := sdk.VerifyAddressFormat(msg.Sender); err != nil { - return sdkerrors.Wrap(err, "sender") - } - if err := sdk.VerifyAddressFormat(msg.Contract); err != nil { - return sdkerrors.Wrap(err, "contract") - } - if err := sdk.VerifyAddressFormat(msg.NewAdmin); err != nil { - return sdkerrors.Wrap(err, "new admin") - } - if msg.Sender.Equals(msg.NewAdmin) { - return sdkerrors.Wrap(ErrInvalidMsg, "new admin is the same as the old") - } - return nil -} - -func (msg MsgUpdateAdmin) GetSignBytes() []byte { - return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) -} - -func (msg MsgUpdateAdmin) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.Sender} -} - -type MsgClearAdmin struct { - Sender sdk.AccAddress `json:"sender" yaml:"sender"` - Contract sdk.AccAddress `json:"contract" yaml:"contract"` -} - -func (msg MsgClearAdmin) Route() string { - return RouterKey -} - -func (msg MsgClearAdmin) Type() string { - return "clear-contract-admin" -} - -func (msg MsgClearAdmin) ValidateBasic() error { - if err := sdk.VerifyAddressFormat(msg.Sender); err != nil { - return sdkerrors.Wrap(err, "sender") - } - if err := sdk.VerifyAddressFormat(msg.Contract); err != nil { - return sdkerrors.Wrap(err, "contract") - } - return nil -} - -func (msg MsgClearAdmin) GetSignBytes() []byte { - return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) -} - -func (msg MsgClearAdmin) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.Sender} -} -*/ From ee0a8e081b4f06e4ac388bf2bfea84a57af78d7a Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Tue, 2 Aug 2022 14:41:55 +0300 Subject: [PATCH 19/44] secret-contract-optimizer v1.0.8 --- deployment/dockerfiles/secret-contract-optimizer.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/dockerfiles/secret-contract-optimizer.Dockerfile b/deployment/dockerfiles/secret-contract-optimizer.Dockerfile index c37099388..91639d96d 100644 --- a/deployment/dockerfiles/secret-contract-optimizer.Dockerfile +++ b/deployment/dockerfiles/secret-contract-optimizer.Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.59-slim-bullseye +FROM rust:1.62-slim-bullseye RUN rustup target add wasm32-unknown-unknown RUN apt update && apt install -y binaryen clang && rm -rf /var/lib/apt/lists/* From a921247234a73a06c913e530b5c0d29f702f5481 Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Tue, 2 Aug 2022 14:42:10 +0300 Subject: [PATCH 20/44] Some IBC progress --- .vscode/settings.json | 4 +- .../src/contract_operations.rs | 2 +- .../shared/cosmwasm-v1-types/src/ibc.rs | 123 ++++++++++++++++++ .../shared/cosmwasm-v1-types/src/lib.rs | 1 + .../testdata/v1-sanity-contract/src/msg.rs | 6 + 5 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 0b900924e..45d553f7d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,7 +3,9 @@ "./cosmwasm/Cargo.toml", "./cosmwasm/enclaves/Cargo.toml", "./x/compute/internal/keeper/testdata/v1-sanity-contract/Cargo.toml", - "./x/compute/internal/keeper/testdata/test-contract/Cargo.toml" + "./x/compute/internal/keeper/testdata/test-contract/Cargo.toml", + "./cosmwasm/enclaves/shared/cosmwasm-v1-types/Cargo.toml", + "./cosmwasm/enclaves/shared/cosmwasm-v010-types/Cargo.toml" ], "rust-analyzer.diagnostics.experimental.enable": true, "rust-analyzer.rustfmt.rangeFormatting.enable": true, diff --git a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs index dd9d91d94..980734985 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs @@ -538,7 +538,7 @@ pub fn parse_message( match orig_secret_msg.decrypt() { Ok(decrypted_msg) => { - // IBC packet is encrypted + // IBC packet was encrypted trace!( "ibc_packet_receive input before decryption: {:?}", diff --git a/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs b/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs new file mode 100644 index 000000000..311e89d0b --- /dev/null +++ b/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs @@ -0,0 +1,123 @@ +use crate::addresses::Addr; +use crate::timestamp::Timestamp; +use serde::{Deserialize, Serialize}; + +/// The message that is passed into `ibc_channel_open` +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum IbcChannelOpenMsg { + /// The ChanOpenInit step from https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#channel-lifecycle-management + OpenInit { channel: IbcChannel }, + /// The ChanOpenTry step from https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#channel-lifecycle-management + OpenTry { + channel: IbcChannel, + counterparty_version: String, + }, +} + +/// IbcChannel defines all information on a channel. +/// This is generally used in the hand-shake process, but can be queried directly. +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct IbcChannel { + pub endpoint: IbcEndpoint, + pub counterparty_endpoint: IbcEndpoint, + pub order: IbcOrder, + /// Note: in ibcv3 this may be "", in the IbcOpenChannel handshake messages + pub version: String, + /// The connection upon which this channel was created. If this is a multi-hop + /// channel, we only expose the first hop. + pub connection_id: String, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct IbcEndpoint { + pub port_id: String, + pub channel_id: String, +} + +/// IbcOrder defines if a channel is ORDERED or UNORDERED +/// Values come from https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/core/channel/v1/channel.proto#L69-L80 +/// Naming comes from the protobuf files and go translations. +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub enum IbcOrder { + #[serde(rename = "ORDER_UNORDERED")] + Unordered, + #[serde(rename = "ORDER_ORDERED")] + Ordered, +} + +/// The message that is passed into `ibc_channel_connect` +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum IbcChannelConnectMsg { + /// The ChanOpenAck step from https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#channel-lifecycle-management + OpenAck { + channel: IbcChannel, + counterparty_version: String, + }, + /// The ChanOpenConfirm step from https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#channel-lifecycle-management + OpenConfirm { channel: IbcChannel }, +} + +/// The message that is passed into `ibc_channel_close` +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum IbcChannelCloseMsg { + /// The ChanCloseInit step from https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#channel-lifecycle-management + CloseInit { channel: IbcChannel }, + /// The ChanCloseConfirm step from https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#channel-lifecycle-management + CloseConfirm { channel: IbcChannel }, // pub channel: IbcChannel, +} + +/// The message that is passed into `ibc_packet_receive` +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct IbcPacketReceiveMsg { + pub packet: IbcPacket, + pub relayer: Addr, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct IbcPacket { + /// The raw data sent from the other side in the packet + pub data: Binary, + /// identifies the channel and port on the sending chain. + pub src: IbcEndpoint, + /// identifies the channel and port on the receiving chain. + pub dest: IbcEndpoint, + /// The sequence number of the packet on the given channel + pub sequence: u64, + pub timeout: IbcTimeout, +} + +/// In IBC each package must set at least one type of timeout: +/// the timestamp or the block height. Using this rather complex enum instead of +/// two timeout fields we ensure that at least one timeout is set. +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[serde(rename_all = "snake_case")] +pub struct IbcTimeout { + // use private fields to enforce the use of constructors, which ensure that at least one is set + block: Option, + timestamp: Option, +} + +/// IBCTimeoutHeight Height is a monotonically increasing data type +/// that can be compared against another Height for the purposes of updating and +/// freezing clients. +/// Ordering is (revision_number, timeout_height) +#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq)] +pub struct IbcTimeoutBlock { + /// the version that the client is currently on + /// (eg. after reseting the chain this could increment 1 as height drops to 0) + pub revision: u64, + /// block height after which the packet times out. + /// the height within the given revision + pub height: u64, +} + +/// The message that is passed into `ibc_packet_ack` +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct IbcPacketAckMsg { + pub acknowledgement: IbcAcknowledgement, + pub original_packet: IbcPacket, + pub relayer: Addr, +} diff --git a/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/lib.rs b/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/lib.rs index 310434644..62f52dc66 100644 --- a/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/lib.rs +++ b/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/lib.rs @@ -2,6 +2,7 @@ pub mod addresses; pub mod coins; pub mod errors; +pub mod ibc; pub mod math; pub mod results; pub mod timestamp; diff --git a/x/compute/internal/keeper/testdata/v1-sanity-contract/src/msg.rs b/x/compute/internal/keeper/testdata/v1-sanity-contract/src/msg.rs index 929213d67..4b8253550 100644 --- a/x/compute/internal/keeper/testdata/v1-sanity-contract/src/msg.rs +++ b/x/compute/internal/keeper/testdata/v1-sanity-contract/src/msg.rs @@ -141,6 +141,12 @@ pub enum ExecuteMsg { SubMsgLoopIner { iter: u64, }, + SentFunds { +to_type: "init"/"exec" +to_contract: "secret1.." +to_contract_hash: "ABC" +funds + }, MultipleSubMessages {}, MultipleSubMessagesNoReply {}, QuickError {}, From d7ee442943c655770871a606c77f72caf7da8b21 Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Thu, 11 Aug 2022 10:02:07 +0300 Subject: [PATCH 21/44] Revert a stupid change --- .../internal/keeper/testdata/v1-sanity-contract/src/msg.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/x/compute/internal/keeper/testdata/v1-sanity-contract/src/msg.rs b/x/compute/internal/keeper/testdata/v1-sanity-contract/src/msg.rs index 4b8253550..929213d67 100644 --- a/x/compute/internal/keeper/testdata/v1-sanity-contract/src/msg.rs +++ b/x/compute/internal/keeper/testdata/v1-sanity-contract/src/msg.rs @@ -141,12 +141,6 @@ pub enum ExecuteMsg { SubMsgLoopIner { iter: u64, }, - SentFunds { -to_type: "init"/"exec" -to_contract: "secret1.." -to_contract_hash: "ABC" -funds - }, MultipleSubMessages {}, MultipleSubMessagesNoReply {}, QuickError {}, From 5c22a983865a6057547ff91c8ea05ed6f99f6ebc Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Thu, 11 Aug 2022 23:31:39 +0300 Subject: [PATCH 22/44] Decrypt only data from IBC --- .../src/contract_operations.rs | 37 +++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs index 980734985..ab9010c5c 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs @@ -11,6 +11,7 @@ use enclave_cosmos_types::types::{ContractCode, HandleType, SigInfo}; use enclave_cosmwasm_v010_types as cosmwasm_v010_types; use enclave_cosmwasm_v010_types::encoding::Binary; use enclave_cosmwasm_v1_types::addresses::Addr; +use enclave_cosmwasm_v1_types::ibc::{IbcPacket, IbcPacketReceiveMsg}; use enclave_cosmwasm_v1_types::results::{ DecryptedReply, Event, Reply, SubMsgResponse, SubMsgResult, }; @@ -315,7 +316,6 @@ pub fn parse_message( }); } - // Here we are sure the reply is OK because only OK is encrypted trace!( "reply input before decryption: {:?}", base64::encode(&message) @@ -520,6 +520,7 @@ pub fn parse_message( HandleType::HANDLE_TYPE_IBC_CHANNEL_CONNECT => todo!(), HandleType::HANDLE_TYPE_IBC_CHANNEL_CLOSE => todo!(), HandleType::HANDLE_TYPE_IBC_PACKET_RECEIVE => { + // LIORRR TODO: Maybe mark whether the message was encrypted or not. let orig_secret_msg = match SecretMessage::from_slice(message) { Ok(orig_secret_msg) => orig_secret_msg, Err(_) => { @@ -536,7 +537,29 @@ pub fn parse_message( } }; - match orig_secret_msg.decrypt() { + let mut parsed_encrypted_ibc_packet_receive_msg: IbcPacketReceiveMsg = serde_json::from_slice( + &orig_secret_msg.msg.as_slice().to_vec(), + ) + .map_err(|err| { + warn!( + "IbcPacketReceiveMsg got an error while trying to deserialize msg input bytes into json {:?}: {}", + String::from_utf8_lossy(&orig_secret_msg.msg), + err + ); + EnclaveError::FailedToDeserialize + })?; + + let tmp_secret_data = SecretMessage { + nonce: orig_secret_msg.nonce, + user_public_key: orig_secret_msg.user_public_key, + msg: parsed_encrypted_ibc_packet_receive_msg + .packet + .data + .as_slice() + .to_vec(), + }; + + match tmp_secret_data.decrypt() { Ok(decrypted_msg) => { // IBC packet was encrypted @@ -545,11 +568,19 @@ pub fn parse_message( base64::encode(&message) ); + parsed_encrypted_ibc_packet_receive_msg.packet.data = tmp_secret_data; + Ok(ParsedMessage { should_validate_sig_info: true, was_msg_encrypted: true, secret_msg: orig_secret_msg, - decrypted_msg, + decrypted_msg: serde_json::to_vec(&parsed_encrypted_ibc_packet_receive_msg).map_err(|err| { + warn!( + "got an error while trying to serialize IbcPacketReceive into bytes {:?}: {}", + parsed_encrypted_ibc_packet_receive_msg, err + ); + EnclaveError::FailedToSerialize + })?, contract_hash_for_validation: None, }) } From 752c3379ac01cfba7c441631577b612b8f574c61 Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Fri, 12 Aug 2022 01:03:17 +0300 Subject: [PATCH 23/44] Fix compilation --- .../src/contract_operations.rs | 9 ++-- .../shared/contract-engine/src/wasm/engine.rs | 42 +++++++++++++++++++ .../shared/cosmwasm-v1-types/src/ibc.rs | 7 ++++ 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs index ab9010c5c..5aec01c35 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs @@ -11,7 +11,7 @@ use enclave_cosmos_types::types::{ContractCode, HandleType, SigInfo}; use enclave_cosmwasm_v010_types as cosmwasm_v010_types; use enclave_cosmwasm_v010_types::encoding::Binary; use enclave_cosmwasm_v1_types::addresses::Addr; -use enclave_cosmwasm_v1_types::ibc::{IbcPacket, IbcPacketReceiveMsg}; +use enclave_cosmwasm_v1_types::ibc::IbcPacketReceiveMsg; use enclave_cosmwasm_v1_types::results::{ DecryptedReply, Event, Reply, SubMsgResponse, SubMsgResult, }; @@ -568,7 +568,8 @@ pub fn parse_message( base64::encode(&message) ); - parsed_encrypted_ibc_packet_receive_msg.packet.data = tmp_secret_data; + parsed_encrypted_ibc_packet_receive_msg.packet.data = + decrypted_msg.as_slice().into(); Ok(ParsedMessage { should_validate_sig_info: true, @@ -592,11 +593,13 @@ pub fn parse_message( base64::encode(&message) ); + let decrypted_msg = orig_secret_msg.msg.clone(); + Ok(ParsedMessage { should_validate_sig_info: false, was_msg_encrypted: false, secret_msg: orig_secret_msg, - decrypted_msg: orig_secret_msg.msg, + decrypted_msg, contract_hash_for_validation: None, }) } diff --git a/cosmwasm/enclaves/shared/contract-engine/src/wasm/engine.rs b/cosmwasm/enclaves/shared/contract-engine/src/wasm/engine.rs index 880cb9970..957ac76ad 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/wasm/engine.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/wasm/engine.rs @@ -146,6 +146,48 @@ impl Engine { RuntimeValue::I32(msg_ptr as i32), ], ), + HandleType::HANDLE_TYPE_IBC_CHANNEL_OPEN => ( + "ibc_channel_open", + vec![ + RuntimeValue::I32(env_ptr as i32), + RuntimeValue::I32(msg_ptr as i32), + ], + ), + HandleType::HANDLE_TYPE_IBC_CHANNEL_CONNECT => ( + "ibc_channel_connect", + vec![ + RuntimeValue::I32(env_ptr as i32), + RuntimeValue::I32(msg_ptr as i32), + ], + ), + HandleType::HANDLE_TYPE_IBC_CHANNEL_CLOSE => ( + "ibc_channel_close", + vec![ + RuntimeValue::I32(env_ptr as i32), + RuntimeValue::I32(msg_ptr as i32), + ], + ), + HandleType::HANDLE_TYPE_IBC_PACKET_RECEIVE => ( + "ibc_packet_receive", + vec![ + RuntimeValue::I32(env_ptr as i32), + RuntimeValue::I32(msg_ptr as i32), + ], + ), + HandleType::HANDLE_TYPE_IBC_PACKET_ACK => ( + "ibc_packet_ack", + vec![ + RuntimeValue::I32(env_ptr as i32), + RuntimeValue::I32(msg_ptr as i32), + ], + ), + HandleType::HANDLE_TYPE_IBC_PACKET_TIMEOUT => ( + "ibc_packet_timeout", + vec![ + RuntimeValue::I32(env_ptr as i32), + RuntimeValue::I32(msg_ptr as i32), + ], + ), }, }; diff --git a/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs b/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs index 311e89d0b..ed1854d70 100644 --- a/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs +++ b/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs @@ -1,5 +1,6 @@ use crate::addresses::Addr; use crate::timestamp::Timestamp; +use enclave_cosmwasm_v010_types::encoding::Binary; use serde::{Deserialize, Serialize}; /// The message that is passed into `ibc_channel_open` @@ -89,6 +90,12 @@ pub struct IbcPacket { pub timeout: IbcTimeout, } +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[non_exhaustive] +pub struct IbcAcknowledgement { + pub data: Binary, +} + /// In IBC each package must set at least one type of timeout: /// the timestamp or the block height. Using this rather complex enum instead of /// two timeout fields we ensure that at least one timeout is set. From 05bad147c095dd40d95f50ea3c206dfd06b062ec Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Sun, 14 Aug 2022 13:55:43 +0300 Subject: [PATCH 24/44] Parse every IBC submessage --- .../src/contract_operations.rs | 253 ++++++++++++++++-- .../enclaves/shared/contract-engine/src/io.rs | 29 ++ .../enclaves/shared/cosmos-types/src/types.rs | 13 + .../shared/cosmwasm-v1-types/src/ibc.rs | 45 +++- 4 files changed, 322 insertions(+), 18 deletions(-) diff --git a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs index 5aec01c35..98aa1e219 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs @@ -11,7 +11,7 @@ use enclave_cosmos_types::types::{ContractCode, HandleType, SigInfo}; use enclave_cosmwasm_v010_types as cosmwasm_v010_types; use enclave_cosmwasm_v010_types::encoding::Binary; use enclave_cosmwasm_v1_types::addresses::Addr; -use enclave_cosmwasm_v1_types::ibc::IbcPacketReceiveMsg; +use enclave_cosmwasm_v1_types::ibc::{IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg}; use enclave_cosmwasm_v1_types::results::{ DecryptedReply, Event, Reply, SubMsgResponse, SubMsgResult, }; @@ -174,6 +174,7 @@ pub fn init( pub struct ParsedMessage { pub should_validate_sig_info: bool, pub was_msg_encrypted: bool, + pub is_ibc_msg: bool, pub secret_msg: SecretMessage, pub decrypted_msg: Vec, pub contract_hash_for_validation: Option>, @@ -238,6 +239,7 @@ pub fn parse_message( Ok(ParsedMessage { should_validate_sig_info: true, was_msg_encrypted: true, + is_ibc_msg: false, secret_msg: orig_secret_msg, decrypted_msg, contract_hash_for_validation: None, @@ -310,6 +312,7 @@ pub fn parse_message( return Ok(ParsedMessage { should_validate_sig_info: false, was_msg_encrypted: false, + is_ibc_msg: false, secret_msg: reply_secret_msg, decrypted_msg: serialized_reply, contract_hash_for_validation: None, @@ -414,6 +417,7 @@ pub fn parse_message( Ok(ParsedMessage { should_validate_sig_info: true, was_msg_encrypted: true, + is_ibc_msg: false, secret_msg: reply_secret_msg, decrypted_msg: decrypted_reply_as_vec, contract_hash_for_validation: Some( @@ -507,6 +511,7 @@ pub fn parse_message( Ok(ParsedMessage { should_validate_sig_info: true, was_msg_encrypted: true, + is_ibc_msg: false, secret_msg: reply_secret_msg, decrypted_msg: decrypted_reply_as_vec, contract_hash_for_validation: Some( @@ -516,9 +521,32 @@ pub fn parse_message( } } } - HandleType::HANDLE_TYPE_IBC_CHANNEL_OPEN => todo!(), - HandleType::HANDLE_TYPE_IBC_CHANNEL_CONNECT => todo!(), - HandleType::HANDLE_TYPE_IBC_CHANNEL_CLOSE => todo!(), + HandleType::HANDLE_TYPE_IBC_CHANNEL_OPEN + | HandleType::HANDLE_TYPE_IBC_CHANNEL_CONNECT + | HandleType::HANDLE_TYPE_IBC_CHANNEL_CLOSE => { + trace!( + "parsing {} msg (Should always be plaintext): {:?}", + HandleType::to_export_name(&handle_type), + base64::encode(&message) + ); + + let scrt_msg = SecretMessage { + nonce: [0; 32], + user_public_key: [0; 32], + msg: message.into(), + }; + + let decrypted_msg = scrt_msg.msg.clone(); + + Ok(ParsedMessage { + should_validate_sig_info: false, + was_msg_encrypted: false, + is_ibc_msg: true, + secret_msg: scrt_msg, + decrypted_msg, + contract_hash_for_validation: None, + }) + } HandleType::HANDLE_TYPE_IBC_PACKET_RECEIVE => { // LIORRR TODO: Maybe mark whether the message was encrypted or not. let orig_secret_msg = match SecretMessage::from_slice(message) { @@ -542,7 +570,7 @@ pub fn parse_message( ) .map_err(|err| { warn!( - "IbcPacketReceiveMsg got an error while trying to deserialize msg input bytes into json {:?}: {}", + "ibc_packet_receive msg got an error while trying to deserialize msg input bytes into json {:?}: {}", String::from_utf8_lossy(&orig_secret_msg.msg), err ); @@ -574,6 +602,7 @@ pub fn parse_message( Ok(ParsedMessage { should_validate_sig_info: true, was_msg_encrypted: true, + is_ibc_msg: true, secret_msg: orig_secret_msg, decrypted_msg: serde_json::to_vec(&parsed_encrypted_ibc_packet_receive_msg).map_err(|err| { warn!( @@ -598,6 +627,194 @@ pub fn parse_message( Ok(ParsedMessage { should_validate_sig_info: false, was_msg_encrypted: false, + is_ibc_msg: true, + secret_msg: orig_secret_msg, + decrypted_msg, + contract_hash_for_validation: None, + }) + } + } + } + HandleType::HANDLE_TYPE_IBC_PACKET_ACK => { + // LIORRR TODO: Maybe mark whether the message was encrypted or not. + let orig_secret_msg = match SecretMessage::from_slice(message) { + Ok(orig_secret_msg) => orig_secret_msg, + Err(_) => { + trace!( + "ibc_packet_ack msg is not SecretMessage (probably plaintext): {:?}", + base64::encode(&message) + ); + + SecretMessage { + nonce: [0; 32], + user_public_key: [0; 32], + msg: message.into(), + } + } + }; + + let mut parsed_encrypted_ibc_packet_ack_msg: IbcPacketAckMsg = serde_json::from_slice( + &orig_secret_msg.msg.as_slice().to_vec(), + ).map_err(|err| { + warn!( + "ibc_packet_ack msg got an error while trying to deserialize msg input bytes into json {:?}: {}", + String::from_utf8_lossy(&orig_secret_msg.msg), + err + ); + EnclaveError::FailedToDeserialize + })?; + + let tmp_secret_data = SecretMessage { + nonce: orig_secret_msg.nonce, + user_public_key: orig_secret_msg.user_public_key, + msg: parsed_encrypted_ibc_packet_ack_msg + .original_packet + .data + .as_slice() + .to_vec(), + }; + + match tmp_secret_data.decrypt() { + Ok(decrypted_msg) => { + // IBC packet was encrypted + + trace!( + "ibc_packet_receive input before decryption: {:?}", + base64::encode(&message) + ); + + parsed_encrypted_ibc_packet_ack_msg.original_packet.data = + decrypted_msg.as_slice().into(); + + let tmp_secret_ack_data = SecretMessage { + nonce: orig_secret_msg.nonce, + user_public_key: orig_secret_msg.user_public_key, + msg: parsed_encrypted_ibc_packet_ack_msg + .acknowledgement + .data + .as_slice() + .to_vec(), + }; + + parsed_encrypted_ibc_packet_ack_msg.acknowledgement.data = + tmp_secret_ack_data.decrypt()?.as_slice().into(); + + Ok(ParsedMessage { + should_validate_sig_info: true, + was_msg_encrypted: true, + is_ibc_msg: true, + secret_msg: orig_secret_msg, + decrypted_msg: serde_json::to_vec(&parsed_encrypted_ibc_packet_ack_msg).map_err(|err| { + warn!( + "got an error while trying to serialize ibc_packet_ack msg into bytes {:?}: {}", + parsed_encrypted_ibc_packet_ack_msg, err + ); + EnclaveError::FailedToSerialize + })?, + contract_hash_for_validation: None, + }) + } + Err(_) => { + // assume packet is not encrypted, continue in plaintext mode + + trace!( + "ibc_packet_ack input is not encrypted: {:?}", + base64::encode(&message) + ); + + let decrypted_msg = orig_secret_msg.msg.clone(); + + Ok(ParsedMessage { + should_validate_sig_info: false, + was_msg_encrypted: false, + is_ibc_msg: true, + secret_msg: orig_secret_msg, + decrypted_msg, + contract_hash_for_validation: None, + }) + } + } + } + HandleType::HANDLE_TYPE_IBC_PACKET_TIMEOUT => { + // LIORRR TODO: Maybe mark whether the message was encrypted or not. + let orig_secret_msg = match SecretMessage::from_slice(message) { + Ok(orig_secret_msg) => orig_secret_msg, + Err(_) => { + trace!( + "ibc_packet_timeout msg is not SecretMessage (probably plaintext): {:?}", + base64::encode(&message) + ); + + SecretMessage { + nonce: [0; 32], + user_public_key: [0; 32], + msg: message.into(), + } + } + }; + + let mut parsed_encrypted_ibc_packet_timeout_msg: IbcPacketTimeoutMsg = serde_json::from_slice( + &orig_secret_msg.msg.as_slice().to_vec(), + ).map_err(|err| { + warn!( + "ibc_packet_timeout msg got an error while trying to deserialize msg input bytes into json {:?}: {}", + String::from_utf8_lossy(&orig_secret_msg.msg), + err + ); + EnclaveError::FailedToDeserialize + })?; + + let tmp_secret_data = SecretMessage { + nonce: orig_secret_msg.nonce, + user_public_key: orig_secret_msg.user_public_key, + msg: parsed_encrypted_ibc_packet_timeout_msg + .packet + .data + .as_slice() + .to_vec(), + }; + + match tmp_secret_data.decrypt() { + Ok(decrypted_msg) => { + // IBC packet was encrypted + + trace!( + "ibc_packet_timeout input before decryption: {:?}", + base64::encode(&message) + ); + + parsed_encrypted_ibc_packet_timeout_msg.packet.data = + decrypted_msg.as_slice().into(); + + Ok(ParsedMessage { + should_validate_sig_info: true, + was_msg_encrypted: true, + is_ibc_msg: true, + secret_msg: orig_secret_msg, + decrypted_msg: serde_json::to_vec(&parsed_encrypted_ibc_packet_timeout_msg).map_err(|err| { + warn!( + "got an error while trying to serialize ibc_packet_timeout msg into bytes {:?}: {}", + parsed_encrypted_ibc_packet_timeout_msg, err + ); + EnclaveError::FailedToSerialize + })?, + contract_hash_for_validation: None, + }) + } + Err(_) => { + // assume packet is not encrypted, continue in plaintext mode + + trace!( + "ibc_packet_timeout input is not encrypted: {:?}", + base64::encode(&message) + ); + + let decrypted_msg = orig_secret_msg.msg.clone(); + + Ok(ParsedMessage { + should_validate_sig_info: false, + was_msg_encrypted: false, + is_ibc_msg: true, secret_msg: orig_secret_msg, decrypted_msg, contract_hash_for_validation: None, @@ -605,8 +822,6 @@ pub fn parse_message( } } } - HandleType::HANDLE_TYPE_IBC_PACKET_ACK => todo!(), - HandleType::HANDLE_TYPE_IBC_PACKET_TIMEOUT => todo!(), }; } @@ -674,6 +889,7 @@ pub fn handle( let ParsedMessage { should_validate_sig_info, was_msg_encrypted, + is_ibc_msg, secret_msg, decrypted_msg, contract_hash_for_validation, @@ -734,22 +950,25 @@ pub fn handle( let output = coalesce!(EnclaveError, { let vec_ptr = engine.handle(env_ptr, msg_info_ptr, msg_ptr, parsed_handle_type)?; - let output = engine.extract_vector(vec_ptr)?; + let mut output = engine.extract_vector(vec_ptr)?; debug!( "(2) nonce just before encrypt_output: nonce = {:?} pubkey = {:?}", secret_msg.nonce, secret_msg.user_public_key ); - let output = encrypt_output( - output, - &secret_msg, - &canonical_contract_address, - &env_v010.contract_code_hash, - reply_params, - &canonical_sender_address, - false, - )?; + if !is_ibc_msg || was_msg_encrypted { + output = encrypt_output( + output, + &secret_msg, + &canonical_contract_address, + &env_v010.contract_code_hash, + reply_params, + &canonical_sender_address, + false, + )?; + } + Ok(output) }) .map_err(|err| { diff --git a/cosmwasm/enclaves/shared/contract-engine/src/io.rs b/cosmwasm/enclaves/shared/contract-engine/src/io.rs index 4ddcc6e8f..99685f8af 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/io.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/io.rs @@ -52,6 +52,12 @@ enum RawWasmOutput { internal_reply_enclave_sig: Option, internal_msg_id: Option, }, + OkIBC { + #[serde(rename = "Ok")] + ok: enclave_cosmwasm_v1_types::ibc::IbcBasicResponse, + internal_reply_enclave_sig: Option, + internal_msg_id: Option, + }, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] @@ -429,6 +435,13 @@ pub fn encrypt_output( None => None, // Not a reply, we don't need enclave sig } } + RawWasmOutput::OkIBC { + ok, + internal_reply_enclave_sig, + internal_msg_id, + } => { + //LIORRR TODO encryption + } }; let final_output: WasmOutput = match output { @@ -499,6 +512,22 @@ pub fn encrypt_output( internal_reply_enclave_sig: None, internal_msg_id: None, }, + + // LIORRRR TODO: make it work, maybe the same as in v1 response? + RawWasmOutput::OkIBC { + ok, + internal_reply_enclave_sig, + internal_msg_id, + } => WasmOutput { + v010: None, + v1: None, + query: Some(QueryOutput { + ok: None, + err: None, + }), + internal_reply_enclave_sig: None, + internal_msg_id: None, + }, }; trace!("WasmOutput: {:?}", final_output); diff --git a/cosmwasm/enclaves/shared/cosmos-types/src/types.rs b/cosmwasm/enclaves/shared/cosmos-types/src/types.rs index b86c0460d..39cf0dc5e 100644 --- a/cosmwasm/enclaves/shared/cosmos-types/src/types.rs +++ b/cosmwasm/enclaves/shared/cosmos-types/src/types.rs @@ -165,6 +165,19 @@ impl HandleType { } } } + + pub fn to_export_name(h: &HandleType) -> String { + match h { + HandleType::HANDLE_TYPE_EXECUTE => "execute".to_string(), + HandleType::HANDLE_TYPE_REPLY => "reply".to_string(), + HandleType::HANDLE_TYPE_IBC_CHANNEL_OPEN => "ibc_channel_open".to_string(), + HandleType::HANDLE_TYPE_IBC_CHANNEL_CONNECT => "ibc_channel_connect".to_string(), + HandleType::HANDLE_TYPE_IBC_CHANNEL_CLOSE => "ibc_channel_close".to_string(), + HandleType::HANDLE_TYPE_IBC_PACKET_RECEIVE => "ibc_packet_receive".to_string(), + HandleType::HANDLE_TYPE_IBC_PACKET_ACK => "ibc_packet_ack".to_string(), + HandleType::HANDLE_TYPE_IBC_PACKET_TIMEOUT => "ibc_packet_timeout".to_string(), + } + } } // This is called `VerificationInfo` on the Go side diff --git a/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs b/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs index ed1854d70..7ec4cdd0e 100644 --- a/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs +++ b/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs @@ -1,7 +1,9 @@ use crate::addresses::Addr; +use crate::results::{Event, SubMsg}; use crate::timestamp::Timestamp; -use enclave_cosmwasm_v010_types::encoding::Binary; +use enclave_cosmwasm_v010_types::{encoding::Binary, types::Empty, types::LogAttribute}; use serde::{Deserialize, Serialize}; +use std::fmt; /// The message that is passed into `ibc_channel_open` #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] @@ -30,6 +32,39 @@ pub struct IbcChannel { pub connection_id: String, } +/// This is the return value for the majority of the ibc handlers. +/// That are able to dispatch messages / events on their own, +/// but have no meaningful return value to the calling code. +/// +/// Callbacks that have return values (like receive_packet) +/// or that cannot redispatch messages (like the handshake callbacks) +/// will use other Response types +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[non_exhaustive] +pub struct IbcBasicResponse +where + T: Clone + fmt::Debug + PartialEq, +{ + /// Optional list of messages to pass. These will be executed in order. + /// If the ReplyOn member is set, they will invoke this contract's `reply` entry point + /// after execution. Otherwise, they act like "fire and forget". + /// Use `SubMsg::new` to create messages with the older "fire and forget" semantics. + pub messages: Vec>, + /// The attributes that will be emitted as part of a `wasm` event. + /// + /// More info about events (and their attributes) can be found in [*Cosmos SDK* docs]. + /// + /// [*Cosmos SDK* docs]: https://docs.cosmos.network/v0.42/core/events.html + pub attributes: Vec, + /// Extra, custom events separate from the main `wasm` one. These will have + /// `wasm-` prepended to the type. + /// + /// More info about events can be found in [*Cosmos SDK* docs]. + /// + /// [*Cosmos SDK* docs]: https://docs.cosmos.network/v0.42/core/events.html + pub events: Vec, +} + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct IbcEndpoint { pub port_id: String, @@ -128,3 +163,11 @@ pub struct IbcPacketAckMsg { pub original_packet: IbcPacket, pub relayer: Addr, } + +/// The message that is passed into `ibc_packet_timeout` +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[non_exhaustive] +pub struct IbcPacketTimeoutMsg { + pub packet: IbcPacket, + pub relayer: Addr, +} From 92772fe6a12693b3a2caee1efacebb744e107b6a Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Mon, 15 Aug 2022 19:03:52 +0300 Subject: [PATCH 25/44] Make IBC handling nicer --- .../src/contract_operations.rs | 416 +++++++----------- .../shared/cosmwasm-v1-types/src/ibc.rs | 112 +++++ 2 files changed, 262 insertions(+), 266 deletions(-) diff --git a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs index 98aa1e219..958dc2021 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs @@ -1,4 +1,7 @@ use log::*; +use serde::de::DeserializeOwned; +use serde::Serialize; +use std::fmt::Debug; use enclave_ffi_types::{Ctx, EnclaveError}; @@ -11,7 +14,9 @@ use enclave_cosmos_types::types::{ContractCode, HandleType, SigInfo}; use enclave_cosmwasm_v010_types as cosmwasm_v010_types; use enclave_cosmwasm_v010_types::encoding::Binary; use enclave_cosmwasm_v1_types::addresses::Addr; -use enclave_cosmwasm_v1_types::ibc::{IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg}; +use enclave_cosmwasm_v1_types::ibc::{ + IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg, IbcPacketTrait, +}; use enclave_cosmwasm_v1_types::results::{ DecryptedReply, Event, Reply, SubMsgResponse, SubMsgResult, }; @@ -180,6 +185,11 @@ pub struct ParsedMessage { pub contract_hash_for_validation: Option>, } +pub struct DecryptedSecretMessage { + pub secret_msg: SecretMessage, + pub decrypted_msg: Vec, +} + pub fn redact_custom_events(reply: &mut Reply) { reply.result = match &reply.result { SubMsgResult::Ok(r) => { @@ -221,6 +231,125 @@ pub fn redact_custom_events(reply: &mut Reply) { }; } +pub fn get_secret_msg(message: &[u8]) -> SecretMessage { + match SecretMessage::from_slice(message) { + Ok(orig_secret_msg) => orig_secret_msg, + Err(_) => { + trace!( + "Msg is not SecretMessage (probably plaintext): {:?}", + base64::encode(&message) + ); + + return SecretMessage { + nonce: [0; 32], + user_public_key: [0; 32], + msg: message.into(), + }; + } + } +} + +pub fn try_get_decrypted_secret_msg(message: &[u8]) -> Option { + let secret_msg = get_secret_msg(message); + match secret_msg.decrypt() { + Ok(decrypted_msg) => Some(DecryptedSecretMessage { + secret_msg, + decrypted_msg, + }), + Err(_) => None, + } +} + +pub fn parse_ibc_packet( + _t: T, + message: &[u8], + orig_secret_msg: SecretMessage, + function_name: &str, +) -> Result +where + T: IbcPacketTrait + Serialize + DeserializeOwned + Debug, +{ + let mut parsed_encrypted_ibc_packet: T = + serde_json::from_slice(&orig_secret_msg.msg.as_slice().to_vec()).map_err(|err| { + warn!( + "{} msg got an error while trying to deserialize msg input bytes into json {:?}: {}", + function_name, + String::from_utf8_lossy(&orig_secret_msg.msg), + err + ); + EnclaveError::FailedToDeserialize + })?; + + let tmp_secret_data = SecretMessage { + nonce: orig_secret_msg.nonce, + user_public_key: orig_secret_msg.user_public_key, + msg: parsed_encrypted_ibc_packet.get_packet().as_slice().to_vec(), + }; + + match tmp_secret_data.decrypt() { + Ok(decrypted_msg) => { + // IBC packet was encrypted + + trace!( + "{} input before decryption: {:?}", + function_name, + base64::encode(&message) + ); + + parsed_encrypted_ibc_packet.set_packet(decrypted_msg.as_slice().into()); + + match parsed_encrypted_ibc_packet.get_ack() { + Some(ack_data) => { + let tmp_secret_ack_data = SecretMessage { + nonce: orig_secret_msg.nonce, + user_public_key: orig_secret_msg.user_public_key, + msg: ack_data.as_slice().to_vec(), + }; + + parsed_encrypted_ibc_packet + .set_ack(tmp_secret_ack_data.decrypt()?.as_slice().into()); + } + None => {} + } + + Ok(ParsedMessage { + should_validate_sig_info: true, + was_msg_encrypted: true, + is_ibc_msg: true, + secret_msg: orig_secret_msg, + decrypted_msg: serde_json::to_vec(&parsed_encrypted_ibc_packet).map_err(|err| { + warn!( + "got an error while trying to serialize {} msg into bytes {:?}: {}", + function_name, parsed_encrypted_ibc_packet, err + ); + EnclaveError::FailedToSerialize + })?, + contract_hash_for_validation: None, + }) + } + Err(_) => { + // assume packet is not encrypted, continue in plaintext mode + + trace!( + "{} input is not encrypted: {:?}", + function_name, + base64::encode(&message) + ); + + let decrypted_msg = orig_secret_msg.msg.clone(); + + Ok(ParsedMessage { + should_validate_sig_info: false, + was_msg_encrypted: false, + is_ibc_msg: true, + secret_msg: orig_secret_msg, + decrypted_msg, + contract_hash_for_validation: None, + }) + } + } +} + // Parse the message that was passed to handle (Based on the assumption that it might be a reply or IBC as well) pub fn parse_message( message: &[u8], @@ -549,278 +678,33 @@ pub fn parse_message( } HandleType::HANDLE_TYPE_IBC_PACKET_RECEIVE => { // LIORRR TODO: Maybe mark whether the message was encrypted or not. - let orig_secret_msg = match SecretMessage::from_slice(message) { - Ok(orig_secret_msg) => orig_secret_msg, - Err(_) => { - trace!( - "ibc_packet_receive msg is not SecretMessage (probably plaintext): {:?}", - base64::encode(&message) - ); - - SecretMessage { - nonce: [0; 32], - user_public_key: [0; 32], - msg: message.into(), - } - } - }; - - let mut parsed_encrypted_ibc_packet_receive_msg: IbcPacketReceiveMsg = serde_json::from_slice( - &orig_secret_msg.msg.as_slice().to_vec(), + let orig_secret_msg = get_secret_msg(message); + parse_ibc_packet( + IbcPacketReceiveMsg::default(), + message, + orig_secret_msg, + "ibc_packet_receive", ) - .map_err(|err| { - warn!( - "ibc_packet_receive msg got an error while trying to deserialize msg input bytes into json {:?}: {}", - String::from_utf8_lossy(&orig_secret_msg.msg), - err - ); - EnclaveError::FailedToDeserialize - })?; - - let tmp_secret_data = SecretMessage { - nonce: orig_secret_msg.nonce, - user_public_key: orig_secret_msg.user_public_key, - msg: parsed_encrypted_ibc_packet_receive_msg - .packet - .data - .as_slice() - .to_vec(), - }; - - match tmp_secret_data.decrypt() { - Ok(decrypted_msg) => { - // IBC packet was encrypted - - trace!( - "ibc_packet_receive input before decryption: {:?}", - base64::encode(&message) - ); - - parsed_encrypted_ibc_packet_receive_msg.packet.data = - decrypted_msg.as_slice().into(); - - Ok(ParsedMessage { - should_validate_sig_info: true, - was_msg_encrypted: true, - is_ibc_msg: true, - secret_msg: orig_secret_msg, - decrypted_msg: serde_json::to_vec(&parsed_encrypted_ibc_packet_receive_msg).map_err(|err| { - warn!( - "got an error while trying to serialize IbcPacketReceive into bytes {:?}: {}", - parsed_encrypted_ibc_packet_receive_msg, err - ); - EnclaveError::FailedToSerialize - })?, - contract_hash_for_validation: None, - }) - } - Err(_) => { - // assume packet is not encrypted, continue in plaintext mode - - trace!( - "ibc_packet_receive input is not encrypted: {:?}", - base64::encode(&message) - ); - - let decrypted_msg = orig_secret_msg.msg.clone(); - - Ok(ParsedMessage { - should_validate_sig_info: false, - was_msg_encrypted: false, - is_ibc_msg: true, - secret_msg: orig_secret_msg, - decrypted_msg, - contract_hash_for_validation: None, - }) - } - } } HandleType::HANDLE_TYPE_IBC_PACKET_ACK => { // LIORRR TODO: Maybe mark whether the message was encrypted or not. - let orig_secret_msg = match SecretMessage::from_slice(message) { - Ok(orig_secret_msg) => orig_secret_msg, - Err(_) => { - trace!( - "ibc_packet_ack msg is not SecretMessage (probably plaintext): {:?}", - base64::encode(&message) - ); - - SecretMessage { - nonce: [0; 32], - user_public_key: [0; 32], - msg: message.into(), - } - } - }; - - let mut parsed_encrypted_ibc_packet_ack_msg: IbcPacketAckMsg = serde_json::from_slice( - &orig_secret_msg.msg.as_slice().to_vec(), - ).map_err(|err| { - warn!( - "ibc_packet_ack msg got an error while trying to deserialize msg input bytes into json {:?}: {}", - String::from_utf8_lossy(&orig_secret_msg.msg), - err - ); - EnclaveError::FailedToDeserialize - })?; - - let tmp_secret_data = SecretMessage { - nonce: orig_secret_msg.nonce, - user_public_key: orig_secret_msg.user_public_key, - msg: parsed_encrypted_ibc_packet_ack_msg - .original_packet - .data - .as_slice() - .to_vec(), - }; - - match tmp_secret_data.decrypt() { - Ok(decrypted_msg) => { - // IBC packet was encrypted - - trace!( - "ibc_packet_receive input before decryption: {:?}", - base64::encode(&message) - ); - - parsed_encrypted_ibc_packet_ack_msg.original_packet.data = - decrypted_msg.as_slice().into(); - - let tmp_secret_ack_data = SecretMessage { - nonce: orig_secret_msg.nonce, - user_public_key: orig_secret_msg.user_public_key, - msg: parsed_encrypted_ibc_packet_ack_msg - .acknowledgement - .data - .as_slice() - .to_vec(), - }; - - parsed_encrypted_ibc_packet_ack_msg.acknowledgement.data = - tmp_secret_ack_data.decrypt()?.as_slice().into(); - - Ok(ParsedMessage { - should_validate_sig_info: true, - was_msg_encrypted: true, - is_ibc_msg: true, - secret_msg: orig_secret_msg, - decrypted_msg: serde_json::to_vec(&parsed_encrypted_ibc_packet_ack_msg).map_err(|err| { - warn!( - "got an error while trying to serialize ibc_packet_ack msg into bytes {:?}: {}", - parsed_encrypted_ibc_packet_ack_msg, err - ); - EnclaveError::FailedToSerialize - })?, - contract_hash_for_validation: None, - }) - } - Err(_) => { - // assume packet is not encrypted, continue in plaintext mode - - trace!( - "ibc_packet_ack input is not encrypted: {:?}", - base64::encode(&message) - ); - - let decrypted_msg = orig_secret_msg.msg.clone(); - - Ok(ParsedMessage { - should_validate_sig_info: false, - was_msg_encrypted: false, - is_ibc_msg: true, - secret_msg: orig_secret_msg, - decrypted_msg, - contract_hash_for_validation: None, - }) - } - } + let orig_secret_msg = get_secret_msg(message); + parse_ibc_packet( + IbcPacketAckMsg::default(), + message, + orig_secret_msg, + "ibc_packet_receive", + ) } HandleType::HANDLE_TYPE_IBC_PACKET_TIMEOUT => { // LIORRR TODO: Maybe mark whether the message was encrypted or not. - let orig_secret_msg = match SecretMessage::from_slice(message) { - Ok(orig_secret_msg) => orig_secret_msg, - Err(_) => { - trace!( - "ibc_packet_timeout msg is not SecretMessage (probably plaintext): {:?}", - base64::encode(&message) - ); - - SecretMessage { - nonce: [0; 32], - user_public_key: [0; 32], - msg: message.into(), - } - } - }; - - let mut parsed_encrypted_ibc_packet_timeout_msg: IbcPacketTimeoutMsg = serde_json::from_slice( - &orig_secret_msg.msg.as_slice().to_vec(), - ).map_err(|err| { - warn!( - "ibc_packet_timeout msg got an error while trying to deserialize msg input bytes into json {:?}: {}", - String::from_utf8_lossy(&orig_secret_msg.msg), - err - ); - EnclaveError::FailedToDeserialize - })?; - - let tmp_secret_data = SecretMessage { - nonce: orig_secret_msg.nonce, - user_public_key: orig_secret_msg.user_public_key, - msg: parsed_encrypted_ibc_packet_timeout_msg - .packet - .data - .as_slice() - .to_vec(), - }; - - match tmp_secret_data.decrypt() { - Ok(decrypted_msg) => { - // IBC packet was encrypted - - trace!( - "ibc_packet_timeout input before decryption: {:?}", - base64::encode(&message) - ); - - parsed_encrypted_ibc_packet_timeout_msg.packet.data = - decrypted_msg.as_slice().into(); - - Ok(ParsedMessage { - should_validate_sig_info: true, - was_msg_encrypted: true, - is_ibc_msg: true, - secret_msg: orig_secret_msg, - decrypted_msg: serde_json::to_vec(&parsed_encrypted_ibc_packet_timeout_msg).map_err(|err| { - warn!( - "got an error while trying to serialize ibc_packet_timeout msg into bytes {:?}: {}", - parsed_encrypted_ibc_packet_timeout_msg, err - ); - EnclaveError::FailedToSerialize - })?, - contract_hash_for_validation: None, - }) - } - Err(_) => { - // assume packet is not encrypted, continue in plaintext mode - - trace!( - "ibc_packet_timeout input is not encrypted: {:?}", - base64::encode(&message) - ); - - let decrypted_msg = orig_secret_msg.msg.clone(); - - Ok(ParsedMessage { - should_validate_sig_info: false, - was_msg_encrypted: false, - is_ibc_msg: true, - secret_msg: orig_secret_msg, - decrypted_msg, - contract_hash_for_validation: None, - }) - } - } + let orig_secret_msg = get_secret_msg(message); + parse_ibc_packet( + IbcPacketTimeoutMsg::default(), + message, + orig_secret_msg, + "ibc_packet_timeout", + ) } }; } diff --git a/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs b/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs index 7ec4cdd0e..3ab599cc6 100644 --- a/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs +++ b/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs @@ -71,6 +71,15 @@ pub struct IbcEndpoint { pub channel_id: String, } +impl IbcEndpoint { + pub fn default() -> Self { + IbcEndpoint { + port_id: String::default(), + channel_id: String::default(), + } + } +} + /// IbcOrder defines if a channel is ORDERED or UNORDERED /// Values come from https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/core/channel/v1/channel.proto#L69-L80 /// Naming comes from the protobuf files and go translations. @@ -112,6 +121,61 @@ pub struct IbcPacketReceiveMsg { pub relayer: Addr, } +pub trait IbcPacketTrait { + type Data; + fn get_packet(&self) -> &Self::Data; + fn set_packet(&mut self, data: Self::Data); + fn get_ack(&self) -> Option; + fn set_ack(&mut self, data: Self::Data); +} + +macro_rules! impl_IbcPacketTrait { + ($($t:ty),+) => { + $(impl IbcPacketTrait for $t { + type Data = Binary; + fn get_packet(&self) -> &Self::Data { + &self.packet.data + } + fn set_packet(&mut self, data: Self::Data) { + self.packet.data = data; + } + fn get_ack(&self) -> Option { + return None; + } + fn set_ack(&mut self, _: Self::Data) { + // Nothing to do here + } + })* + } +} + +impl_IbcPacketTrait! {IbcPacketReceiveMsg, IbcPacketTimeoutMsg} + +impl IbcPacketTrait for IbcPacketAckMsg { + type Data = Binary; + fn get_packet(&self) -> &Self::Data { + &self.original_packet.data + } + fn set_packet(&mut self, data: Self::Data) { + self.original_packet.data = data; + } + fn get_ack(&self) -> Option { + return Some(self.acknowledgement.data.clone()); + } + fn set_ack(&mut self, data: Self::Data) { + self.acknowledgement.data = data; + } +} + +impl IbcPacketReceiveMsg { + pub fn default() -> Self { + Self { + packet: IbcPacket::default(), + relayer: Addr::unchecked("".to_string()), + } + } +} + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct IbcPacket { /// The raw data sent from the other side in the packet @@ -125,12 +189,32 @@ pub struct IbcPacket { pub timeout: IbcTimeout, } +impl IbcPacket { + pub fn default() -> Self { + Self { + data: vec![].as_slice().into(), + src: IbcEndpoint::default(), + dest: IbcEndpoint::default(), + sequence: 0, + timeout: IbcTimeout::default(), + } + } +} + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] #[non_exhaustive] pub struct IbcAcknowledgement { pub data: Binary, } +impl IbcAcknowledgement { + pub fn default() -> Self { + Self { + data: vec![].as_slice().into(), + } + } +} + /// In IBC each package must set at least one type of timeout: /// the timestamp or the block height. Using this rather complex enum instead of /// two timeout fields we ensure that at least one timeout is set. @@ -142,6 +226,15 @@ pub struct IbcTimeout { timestamp: Option, } +impl IbcTimeout { + pub fn default() -> Self { + Self { + block: None, + timestamp: None, + } + } +} + /// IBCTimeoutHeight Height is a monotonically increasing data type /// that can be compared against another Height for the purposes of updating and /// freezing clients. @@ -164,6 +257,16 @@ pub struct IbcPacketAckMsg { pub relayer: Addr, } +impl IbcPacketAckMsg { + pub fn default() -> Self { + Self { + acknowledgement: IbcAcknowledgement::default(), + original_packet: IbcPacket::default(), + relayer: Addr::unchecked("".to_string()), + } + } +} + /// The message that is passed into `ibc_packet_timeout` #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] #[non_exhaustive] @@ -171,3 +274,12 @@ pub struct IbcPacketTimeoutMsg { pub packet: IbcPacket, pub relayer: Addr, } + +impl IbcPacketTimeoutMsg { + pub fn default() -> Self { + Self { + packet: IbcPacket::default(), + relayer: Addr::unchecked("".to_string()), + } + } +} From 084849d93e4b751b38197a1fb96c464f20c6d2e4 Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Tue, 16 Aug 2022 12:06:42 +0300 Subject: [PATCH 26/44] Encrypt IBC outputs for encrypted inputs --- .../enclaves/shared/contract-engine/src/io.rs | 264 +++++++++++++++++- .../shared/cosmwasm-v1-types/src/ibc.rs | 34 +++ go-cosmwasm/lib.go | 85 +++--- go-cosmwasm/types/v1/ibc.go | 11 +- x/compute/internal/keeper/relay.go | 2 +- 5 files changed, 334 insertions(+), 62 deletions(-) diff --git a/cosmwasm/enclaves/shared/contract-engine/src/io.rs b/cosmwasm/enclaves/shared/contract-engine/src/io.rs index 99685f8af..25f4452f6 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/io.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/io.rs @@ -58,6 +58,12 @@ enum RawWasmOutput { internal_reply_enclave_sig: Option, internal_msg_id: Option, }, + OkIBCReceive { + #[serde(rename = "Ok")] + ok: enclave_cosmwasm_v1_types::ibc::IbcReceiveResponse, + internal_reply_enclave_sig: Option, + internal_msg_id: Option, + }, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] @@ -76,6 +82,22 @@ struct V1WasmOutput { pub err: Option, } +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +struct IBCOutput { + #[serde(rename = "Ok")] + pub ok: Option, + #[serde(rename = "Err")] + pub err: Option, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +struct IBCReceiveOutput { + #[serde(rename = "Ok")] + pub ok: Option, + #[serde(rename = "Err")] + pub err: Option, +} + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] struct QueryOutput { #[serde(rename = "Ok")] @@ -88,6 +110,8 @@ struct QueryOutput { struct WasmOutput { pub v010: Option, pub v1: Option, + pub ibc: Option, + pub ibc_receive: Option, pub query: Option, pub internal_reply_enclave_sig: Option, pub internal_msg_id: Option, @@ -440,7 +464,207 @@ pub fn encrypt_output( internal_reply_enclave_sig, internal_msg_id, } => { - //LIORRR TODO encryption + for sub_msg in &mut ok.messages { + if let enclave_cosmwasm_v1_types::results::CosmosMsg::Wasm(wasm_msg) = + &mut sub_msg.msg + { + encrypt_v1_wasm_msg( + wasm_msg, + &sub_msg.reply_on, + sub_msg.id, + secret_msg.nonce, + secret_msg.user_public_key, + contract_addr, + contract_hash, + )?; + + // The ID can be extracted from the encrypted wasm msg + // We don't encrypt it here to remain with the same type (u64) + sub_msg.id = 0; + } + } + + // v1: The attributes that will be emitted as part of a "wasm" event. + for attr in ok.attributes.iter_mut().filter(|attr| attr.encrypted) { + attr.key = encrypt_preserialized_string(&encryption_key, &attr.key, &None)?; + attr.value = encrypt_preserialized_string(&encryption_key, &attr.value, &None)?; + } + + // v1: Extra, custom events separate from the main wasm one. These will have "wasm-"" prepended to the type. + for event in ok.events.iter_mut() { + for attr in event.attributes.iter_mut().filter(|attr| attr.encrypted) { + attr.key = encrypt_preserialized_string(&encryption_key, &attr.key, &None)?; + attr.value = encrypt_preserialized_string(&encryption_key, &attr.value, &None)?; + } + } + + let msg_id = match reply_params { + Some(ref r) => { + let encrypted_id = Binary::from_base64(&encrypt_preserialized_string( + &encryption_key, + &r.sub_msg_id.to_string(), + &reply_params, + )?)?; + + Some(encrypted_id) + } + None => None, + }; + + *internal_msg_id = msg_id.clone(); + + *internal_reply_enclave_sig = match reply_params { + Some(_) => { + let mut events: Vec = vec![]; + + if ok.attributes.len() > 0 { + events.push(Event { + ty: "wasm".to_string(), + attributes: ok.attributes.clone(), + }) + } + + events.extend_from_slice(&ok.events.clone().as_slice()); + let custom_contract_event_prefix: String = "wasm-".to_string(); + for event in events.iter_mut() { + if event.ty != "wasm" { + event.ty = custom_contract_event_prefix.clone() + event.ty.as_str(); + } + } + + let reply = Reply { + id: msg_id.unwrap(), + result: SubMsgResult::Ok(SubMsgResponse { events, data: None }), + }; + + let reply_as_vec = serde_json::to_vec(&reply).map_err(|err| { + warn!( + "got an error while trying to serialize reply into bytes for internal_reply_enclave_sig {:?}: {}", + reply, err + ); + EnclaveError::FailedToSerialize + })?; + + let tmp_secret_msg = SecretMessage { + nonce: secret_msg.nonce, + user_public_key: secret_msg.user_public_key, + msg: reply_as_vec, + }; + + Some(Binary::from( + create_callback_signature(sender_addr, &tmp_secret_msg, &[]).as_slice(), + )) + } + None => None, // Not a reply, we don't need enclave sig + } + } + RawWasmOutput::OkIBCReceive { + ok, + internal_reply_enclave_sig, + internal_msg_id, + } => { + for sub_msg in &mut ok.messages { + if let enclave_cosmwasm_v1_types::results::CosmosMsg::Wasm(wasm_msg) = + &mut sub_msg.msg + { + encrypt_v1_wasm_msg( + wasm_msg, + &sub_msg.reply_on, + sub_msg.id, + secret_msg.nonce, + secret_msg.user_public_key, + contract_addr, + contract_hash, + )?; + + // The ID can be extracted from the encrypted wasm msg + // We don't encrypt it here to remain with the same type (u64) + sub_msg.id = 0; + } + } + + // v1: The attributes that will be emitted as part of a "wasm" event. + for attr in ok.attributes.iter_mut().filter(|attr| attr.encrypted) { + attr.key = encrypt_preserialized_string(&encryption_key, &attr.key, &None)?; + attr.value = encrypt_preserialized_string(&encryption_key, &attr.value, &None)?; + } + + // v1: Extra, custom events separate from the main wasm one. These will have "wasm-"" prepended to the type. + for event in ok.events.iter_mut() { + for attr in event.attributes.iter_mut().filter(|attr| attr.encrypted) { + attr.key = encrypt_preserialized_string(&encryption_key, &attr.key, &None)?; + attr.value = encrypt_preserialized_string(&encryption_key, &attr.value, &None)?; + } + } + + ok.acknowledgement = Binary::from_base64(&encrypt_serializable( + &encryption_key, + &ok.acknowledgement, + &reply_params, + )?)?; + + let msg_id = match reply_params { + Some(ref r) => { + let encrypted_id = Binary::from_base64(&encrypt_preserialized_string( + &encryption_key, + &r.sub_msg_id.to_string(), + &reply_params, + )?)?; + + Some(encrypted_id) + } + None => None, + }; + + *internal_msg_id = msg_id.clone(); + + *internal_reply_enclave_sig = match reply_params { + Some(_) => { + let mut events: Vec = vec![]; + + if ok.attributes.len() > 0 { + events.push(Event { + ty: "wasm".to_string(), + attributes: ok.attributes.clone(), + }) + } + + events.extend_from_slice(&ok.events.clone().as_slice()); + let custom_contract_event_prefix: String = "wasm-".to_string(); + for event in events.iter_mut() { + if event.ty != "wasm" { + event.ty = custom_contract_event_prefix.clone() + event.ty.as_str(); + } + } + + let reply = Reply { + id: msg_id.unwrap(), + result: SubMsgResult::Ok(SubMsgResponse { + events, + data: Some(ok.acknowledgement.clone()), + }), + }; + + let reply_as_vec = serde_json::to_vec(&reply).map_err(|err| { + warn!( + "got an error while trying to serialize reply into bytes for internal_reply_enclave_sig {:?}: {}", + reply, err + ); + EnclaveError::FailedToSerialize + })?; + + let tmp_secret_msg = SecretMessage { + nonce: secret_msg.nonce, + user_public_key: secret_msg.user_public_key, + msg: reply_as_vec, + }; + + Some(Binary::from( + create_callback_signature(sender_addr, &tmp_secret_msg, &[]).as_slice(), + )) + } + None => None, // Not a reply, we don't need enclave sig + } } }; @@ -454,6 +678,8 @@ pub fn encrypt_output( WasmOutput { v010: None, v1: None, + ibc: None, + ibc_receive: None, query: Some(QueryOutput { ok: None, err: Some(err), @@ -468,6 +694,8 @@ pub fn encrypt_output( ok: None, }), v1: None, + ibc: None, + ibc_receive: None, query: None, internal_reply_enclave_sig, internal_msg_id, @@ -484,6 +712,8 @@ pub fn encrypt_output( ok: Some(ok), }), v1: None, + ibc: None, + ibc_receive: None, query: None, internal_reply_enclave_sig, internal_msg_id, @@ -498,6 +728,8 @@ pub fn encrypt_output( err: None, ok: Some(ok), }), + ibc: None, + ibc_receive: None, query: None, internal_reply_enclave_sig, internal_msg_id, @@ -505,6 +737,8 @@ pub fn encrypt_output( RawWasmOutput::QueryOkV010 { ok } | RawWasmOutput::QueryOkV1 { ok } => WasmOutput { v010: None, v1: None, + ibc: None, + ibc_receive: None, query: Some(QueryOutput { ok: Some(ok), err: None, @@ -512,8 +746,6 @@ pub fn encrypt_output( internal_reply_enclave_sig: None, internal_msg_id: None, }, - - // LIORRRR TODO: make it work, maybe the same as in v1 response? RawWasmOutput::OkIBC { ok, internal_reply_enclave_sig, @@ -521,12 +753,30 @@ pub fn encrypt_output( } => WasmOutput { v010: None, v1: None, - query: Some(QueryOutput { - ok: None, + ibc: Some(IBCOutput { err: None, + ok: Some(ok), }), - internal_reply_enclave_sig: None, - internal_msg_id: None, + ibc_receive: None, + query: None, + internal_reply_enclave_sig, + internal_msg_id, + }, + RawWasmOutput::OkIBCReceive { + ok, + internal_reply_enclave_sig, + internal_msg_id, + } => WasmOutput { + v010: None, + v1: None, + ibc: None, + ibc_receive: Some(IBCReceiveOutput { + err: None, + ok: Some(ok), + }), + query: None, + internal_reply_enclave_sig, + internal_msg_id, }, }; diff --git a/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs b/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs index 3ab599cc6..cbb02ec70 100644 --- a/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs +++ b/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs @@ -65,6 +65,40 @@ where pub events: Vec, } +// This defines the return value on packet response processing. +// This "success" case should be returned even in application-level errors, +// Where the acknowledgement bytes contain an encoded error message to be returned to +// the calling chain. (Returning ContractResult::Err will abort processing of this packet +// and not inform the calling chain). +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[non_exhaustive] +pub struct IbcReceiveResponse +where + T: Clone + fmt::Debug + PartialEq, +{ + /// The bytes we return to the contract that sent the packet. + /// This may represent a success or error of exection + pub acknowledgement: Binary, + /// Optional list of messages to pass. These will be executed in order. + /// If the ReplyOn member is set, they will invoke this contract's `reply` entry point + /// after execution. Otherwise, they act like "fire and forget". + /// Use `call` or `msg.into()` to create messages with the older "fire and forget" semantics. + pub messages: Vec>, + /// The attributes that will be emitted as part of a "wasm" event. + /// + /// More info about events (and their attributes) can be found in [*Cosmos SDK* docs]. + /// + /// [*Cosmos SDK* docs]: https://docs.cosmos.network/v0.42/core/events.html + pub attributes: Vec, + /// Extra, custom events separate from the main `wasm` one. These will have + /// `wasm-` prepended to the type. + /// + /// More info about events can be found in [*Cosmos SDK* docs]. + /// + /// [*Cosmos SDK* docs]: https://docs.cosmos.network/v0.42/core/events.html + pub events: Vec, +} + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct IbcEndpoint { pub port_id: String, diff --git a/go-cosmwasm/lib.go b/go-cosmwasm/lib.go index e9d600ff0..0809da070 100644 --- a/go-cosmwasm/lib.go +++ b/go-cosmwasm/lib.go @@ -91,11 +91,8 @@ type ContractExecResponse struct { InternaReplyEnclaveSig []byte `json:"internal_reply_enclave_sig"` InternalMsgId []byte `json:"internal_msg_id"` IBCChannelOpen *v1types.IBCChannelOpenResult `json:"ibc_channel_open,omitempty"` - IBCChannelConnect *v1types.IBCBasicResult `json:"ibc_channel_connect,omitempty"` - IBCChannelClose *v1types.IBCBasicResult `json:"ibc_channel_close,omitempty"` - IBCPacketReceive *v1types.IBCReceiveResult `json:"ibc_packet_receive,omitempty"` - IBCPacketAck *v1types.IBCBasicResult `json:"ibc_packet_ack,omitempty"` - IBCPacketTimeout *v1types.IBCBasicResult `json:"ibc_packet_timeout,omitempty"` + IBC *v1types.IBCBasicResult `json:"ibc,omitempty"` + IBCReceive *v1types.IBCReceiveResult `json:"ibc_receive,omitempty"` } type V010ContractExecResponse struct { @@ -330,57 +327,43 @@ func (w *Wasmer) Execute( } // handle IBCChannelConnect response - if resp.IBCChannelConnect != nil { - if resp.IBCChannelConnect.Err != "" { - return nil, gasUsed, fmt.Errorf("%s", resp.IBCChannelConnect.Err) - } else if resp.IBCChannelConnect.Ok != nil { - return resp.IBCChannelConnect.Ok, gasUsed, nil - } else { - return nil, gasUsed, fmt.Errorf("cannot parse IBCChannelConnect response: %+v", resp) - } - } - - // handle IBCChannelClose response - if resp.IBCChannelClose != nil { - if resp.IBCChannelClose.Err != "" { - return nil, gasUsed, fmt.Errorf("%s", resp.IBCChannelClose.Err) - } else if resp.IBCChannelClose.Ok != nil { - return resp.IBCChannelClose.Ok, gasUsed, nil - } else { - return nil, gasUsed, fmt.Errorf("cannot parse IBCChannelClose response: %+v", resp) - } - } - - // handle IBCPacketReceive response - if resp.IBCPacketReceive != nil { - if resp.IBCPacketReceive.Err != "" { - return nil, gasUsed, fmt.Errorf("%s", resp.IBCPacketReceive.Err) - } else if resp.IBCPacketReceive.Ok != nil { - return resp.IBCPacketReceive.Ok, gasUsed, nil - } else { - return nil, gasUsed, fmt.Errorf("cannot parse IBCPacketReceive response: %+v", resp) - } - } - - // handle IBCPacketAck response - if resp.IBCPacketAck != nil { - if resp.IBCPacketAck.Err != "" { - return nil, gasUsed, fmt.Errorf("%s", resp.IBCPacketAck.Err) - } else if resp.IBCPacketAck.Ok != nil { - return resp.IBCPacketAck.Ok, gasUsed, nil + if resp.IBC != nil { + if resp.IBC.Err != nil { + return v1types.DataWithInternalReplyInfo{ + InternalMsgId: resp.InternalMsgId, + InternaReplyEnclaveSig: resp.InternaReplyEnclaveSig, + Data: []byte(resp.IBC.Err.GenericErr.Msg), + }, gasUsed, fmt.Errorf("%+v", resp.IBC.Err) + } else if resp.IBC.Ok != nil { + if isOutputAddressedToReply { + resp.IBC.Ok.InternalData, err = AppendReplyInternalDataToData([]byte{}, resp.InternaReplyEnclaveSig, resp.InternalMsgId) + if err != nil { + return nil, gasUsed, fmt.Errorf("cannot serialize v1 DataWithInternalReplyInfo into binary : %w", err) + } + } + return resp.V1.Ok, gasUsed, nil } else { - return nil, gasUsed, fmt.Errorf("cannot parse IBCPacketAck response: %+v", resp) + return nil, gasUsed, fmt.Errorf("cannot parse v1 handle response: %+v", resp) } } - // handle IBCPacketTimeout response - if resp.IBCPacketTimeout != nil { - if resp.IBCPacketTimeout.Err != "" { - return nil, gasUsed, fmt.Errorf("%s", resp.IBCPacketTimeout.Err) - } else if resp.IBCPacketTimeout.Ok != nil { - return resp.IBCPacketTimeout.Ok, gasUsed, nil + if resp.IBCReceive != nil { + if resp.IBCReceive.Err != nil { + return v1types.DataWithInternalReplyInfo{ + InternalMsgId: resp.InternalMsgId, + InternaReplyEnclaveSig: resp.InternaReplyEnclaveSig, + Data: []byte(resp.IBCReceive.Err.GenericErr.Msg), + }, gasUsed, fmt.Errorf("%+v", resp.IBCReceive.Err) + } else if resp.IBCReceive.Ok != nil { + if isOutputAddressedToReply { + resp.IBCReceive.Ok.Acknowledgement, err = AppendReplyInternalDataToData(resp.IBCReceive.Ok.Acknowledgement, resp.InternaReplyEnclaveSig, resp.InternalMsgId) + if err != nil { + return nil, gasUsed, fmt.Errorf("cannot serialize v1 DataWithInternalReplyInfo into binary : %w", err) + } + } + return resp.V1.Ok, gasUsed, nil } else { - return nil, gasUsed, fmt.Errorf("cannot parse IBCPacketTimeout response: %+v", resp) + return nil, gasUsed, fmt.Errorf("cannot parse v1 handle response: %+v", resp) } } diff --git a/go-cosmwasm/types/v1/ibc.go b/go-cosmwasm/types/v1/ibc.go index 1d5d994fb..d98c6826b 100644 --- a/go-cosmwasm/types/v1/ibc.go +++ b/go-cosmwasm/types/v1/ibc.go @@ -1,6 +1,9 @@ package v1types -import v010msgtypes "github.com/enigmampc/SecretNetwork/go-cosmwasm/types/v010" +import ( + types "github.com/enigmampc/SecretNetwork/go-cosmwasm/types" + v010msgtypes "github.com/enigmampc/SecretNetwork/go-cosmwasm/types/v010" +) type IBCEndpoint struct { PortID string `json:"port_id"` @@ -219,12 +222,14 @@ type IBC3ChannelOpenResponse struct { // will use other Response types type IBCBasicResult struct { Ok *IBCBasicResponse `json:"ok,omitempty"` - Err string `json:"error,omitempty"` + Err *types.StdError `json:"Err,omitempty"` } // IBCBasicResponse defines the return value on a successful processing. // This is the counterpart of [IbcBasicResponse](https://github.com/CosmWasm/cosmwasm/blob/v0.14.0-beta1/packages/std/src/ibc.rs#L194-L216). type IBCBasicResponse struct { + // Used for internal purposes + InternalData []byte `json:"internal_data,omitempty"` // Messages comes directly from the contract and is its request for action. // If the ReplyOn value matches the result, the runtime will invoke this // contract's `reply` entry point after execution. Otherwise, this is all @@ -246,7 +251,7 @@ type IBCBasicResponse struct { // will use other Response types type IBCReceiveResult struct { Ok *IBCReceiveResponse `json:"ok,omitempty"` - Err string `json:"error,omitempty"` + Err *types.StdError `json:"Err,omitempty"` } // IBCReceiveResponse defines the return value on packet response processing. diff --git a/x/compute/internal/keeper/relay.go b/x/compute/internal/keeper/relay.go index a60a928e0..776f3e52d 100644 --- a/x/compute/internal/keeper/relay.go +++ b/x/compute/internal/keeper/relay.go @@ -287,6 +287,6 @@ func (k Keeper) OnTimeoutPacket( func (k Keeper) handleIBCBasicContractResponse(ctx sdk.Context, addr sdk.AccAddress, ibcPortID string, inputMsg []byte, res *v1types.IBCBasicResponse) error { verificationInfo := types.NewVerificationInfo([]byte{}, sdktxsigning.SignMode_SIGN_MODE_UNSPECIFIED, []byte{}, []byte{}, []byte{}, nil) - _, err := k.handleContractResponse(ctx, addr, ibcPortID, res.Messages, res.Attributes, res.Events, nil, inputMsg, verificationInfo, wasmTypes.CosmosMsgVersionV1) + _, err := k.handleContractResponse(ctx, addr, ibcPortID, res.Messages, res.Attributes, res.Events, res.InternalData, inputMsg, verificationInfo, wasmTypes.CosmosMsgVersionV1) return err } From ede75fab9fd663490fd475c1ddf197687571f02e Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Tue, 16 Aug 2022 12:12:58 +0300 Subject: [PATCH 27/44] Finalize output in a function --- .../enclaves/shared/contract-engine/src/io.rs | 227 +++++++++--------- 1 file changed, 115 insertions(+), 112 deletions(-) diff --git a/cosmwasm/enclaves/shared/contract-engine/src/io.rs b/cosmwasm/enclaves/shared/contract-engine/src/io.rs index 25f4452f6..edc64a5f1 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/io.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/io.rs @@ -184,6 +184,120 @@ fn b64_encode(data: &[u8]) -> String { base64::encode(data) } +pub fn finalize_raw_output(raw_output: RawWasmOutput) -> WasmOutput { + return match raw_output { + RawWasmOutput::Err { + err, + internal_msg_id, + internal_reply_enclave_sig, + } => { + if is_query_output { + WasmOutput { + v010: None, + v1: None, + ibc: None, + ibc_receive: None, + query: Some(QueryOutput { + ok: None, + err: Some(err), + }), + internal_reply_enclave_sig: None, + internal_msg_id: None, + } + } else { + WasmOutput { + v010: Some(V010WasmOutput { + err: Some(err), + ok: None, + }), + v1: None, + ibc: None, + ibc_receive: None, + query: None, + internal_reply_enclave_sig, + internal_msg_id, + } + } + } + RawWasmOutput::OkV010 { + ok, + internal_reply_enclave_sig, + internal_msg_id, + } => WasmOutput { + v010: Some(V010WasmOutput { + err: None, + ok: Some(ok), + }), + v1: None, + ibc: None, + ibc_receive: None, + query: None, + internal_reply_enclave_sig, + internal_msg_id, + }, + RawWasmOutput::OkV1 { + ok, + internal_reply_enclave_sig, + internal_msg_id, + } => WasmOutput { + v010: None, + v1: Some(V1WasmOutput { + err: None, + ok: Some(ok), + }), + ibc: None, + ibc_receive: None, + query: None, + internal_reply_enclave_sig, + internal_msg_id, + }, + RawWasmOutput::QueryOkV010 { ok } | RawWasmOutput::QueryOkV1 { ok } => WasmOutput { + v010: None, + v1: None, + ibc: None, + ibc_receive: None, + query: Some(QueryOutput { + ok: Some(ok), + err: None, + }), + internal_reply_enclave_sig: None, + internal_msg_id: None, + }, + RawWasmOutput::OkIBC { + ok, + internal_reply_enclave_sig, + internal_msg_id, + } => WasmOutput { + v010: None, + v1: None, + ibc: Some(IBCOutput { + err: None, + ok: Some(ok), + }), + ibc_receive: None, + query: None, + internal_reply_enclave_sig, + internal_msg_id, + }, + RawWasmOutput::OkIBCReceive { + ok, + internal_reply_enclave_sig, + internal_msg_id, + } => WasmOutput { + v010: None, + v1: None, + ibc: None, + ibc_receive: Some(IBCReceiveOutput { + err: None, + ok: Some(ok), + }), + query: None, + internal_reply_enclave_sig, + internal_msg_id, + }, + }; +} + pub fn encrypt_output( output: Vec, secret_msg: &SecretMessage, @@ -668,118 +782,7 @@ pub fn encrypt_output( } }; - let final_output: WasmOutput = match output { - RawWasmOutput::Err { - err, - internal_msg_id, - internal_reply_enclave_sig, - } => { - if is_query_output { - WasmOutput { - v010: None, - v1: None, - ibc: None, - ibc_receive: None, - query: Some(QueryOutput { - ok: None, - err: Some(err), - }), - internal_reply_enclave_sig: None, - internal_msg_id: None, - } - } else { - WasmOutput { - v010: Some(V010WasmOutput { - err: Some(err), - ok: None, - }), - v1: None, - ibc: None, - ibc_receive: None, - query: None, - internal_reply_enclave_sig, - internal_msg_id, - } - } - } - RawWasmOutput::OkV010 { - ok, - internal_reply_enclave_sig, - internal_msg_id, - } => WasmOutput { - v010: Some(V010WasmOutput { - err: None, - ok: Some(ok), - }), - v1: None, - ibc: None, - ibc_receive: None, - query: None, - internal_reply_enclave_sig, - internal_msg_id, - }, - RawWasmOutput::OkV1 { - ok, - internal_reply_enclave_sig, - internal_msg_id, - } => WasmOutput { - v010: None, - v1: Some(V1WasmOutput { - err: None, - ok: Some(ok), - }), - ibc: None, - ibc_receive: None, - query: None, - internal_reply_enclave_sig, - internal_msg_id, - }, - RawWasmOutput::QueryOkV010 { ok } | RawWasmOutput::QueryOkV1 { ok } => WasmOutput { - v010: None, - v1: None, - ibc: None, - ibc_receive: None, - query: Some(QueryOutput { - ok: Some(ok), - err: None, - }), - internal_reply_enclave_sig: None, - internal_msg_id: None, - }, - RawWasmOutput::OkIBC { - ok, - internal_reply_enclave_sig, - internal_msg_id, - } => WasmOutput { - v010: None, - v1: None, - ibc: Some(IBCOutput { - err: None, - ok: Some(ok), - }), - ibc_receive: None, - query: None, - internal_reply_enclave_sig, - internal_msg_id, - }, - RawWasmOutput::OkIBCReceive { - ok, - internal_reply_enclave_sig, - internal_msg_id, - } => WasmOutput { - v010: None, - v1: None, - ibc: None, - ibc_receive: Some(IBCReceiveOutput { - err: None, - ok: Some(ok), - }), - query: None, - internal_reply_enclave_sig, - internal_msg_id, - }, - }; - + let final_output = finalize_raw_output(output); trace!("WasmOutput: {:?}", final_output); let encrypted_output = serde_json::to_vec(&final_output).map_err(|err| { From 86501b029e41ed73da1040350314e592bcef071e Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Tue, 16 Aug 2022 12:35:23 +0300 Subject: [PATCH 28/44] Finalize plaintext messages --- .../contract-engine/src/contract_operations.rs | 18 +++++++++++++++++- .../enclaves/shared/contract-engine/src/io.rs | 18 +++++++++--------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs index 958dc2021..33a88a2b7 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs @@ -30,7 +30,7 @@ use super::contract_validation::{ verify_params, ContractKey, }; use super::gas::WasmCosts; -use super::io::encrypt_output; +use super::io::{encrypt_output, finalize_raw_output, RawWasmOutput}; use super::module_cache::create_module_instance; use super::types::{IoNonce, SecretMessage}; use super::wasm::{ContractInstance, ContractOperation, Engine}; @@ -851,6 +851,22 @@ pub fn handle( &canonical_sender_address, false, )?; + } else { + let raw_output: RawWasmOutput = serde_json::from_slice(&output).map_err(|err| { + warn!("got an error while trying to deserialize output bytes into json"); + trace!("output: {:?} error: {:?}", output, err); + EnclaveError::FailedToDeserialize + })?; + + let finalized_output = finalize_raw_output(raw_output, false); + + output = serde_json::to_vec(&finalized_output).map_err(|err| { + debug!( + "got an error while trying to serialize output json into bytes {:?}: {}", + finalized_output, err + ); + EnclaveError::FailedToSerialize + })?; } Ok(output) diff --git a/cosmwasm/enclaves/shared/contract-engine/src/io.rs b/cosmwasm/enclaves/shared/contract-engine/src/io.rs index edc64a5f1..539847227 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/io.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/io.rs @@ -25,7 +25,7 @@ use sha2::Digest; /// b. Authenticate the reply. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] #[serde(untagged)] -enum RawWasmOutput { +pub enum RawWasmOutput { Err { #[serde(rename = "Err")] err: Value, @@ -67,7 +67,7 @@ enum RawWasmOutput { } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -struct V010WasmOutput { +pub struct V010WasmOutput { #[serde(rename = "Ok")] pub ok: Option, #[serde(rename = "Err")] @@ -75,7 +75,7 @@ struct V010WasmOutput { } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -struct V1WasmOutput { +pub struct V1WasmOutput { #[serde(rename = "Ok")] pub ok: Option, #[serde(rename = "Err")] @@ -83,7 +83,7 @@ struct V1WasmOutput { } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -struct IBCOutput { +pub struct IBCOutput { #[serde(rename = "Ok")] pub ok: Option, #[serde(rename = "Err")] @@ -91,7 +91,7 @@ struct IBCOutput { } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -struct IBCReceiveOutput { +pub struct IBCReceiveOutput { #[serde(rename = "Ok")] pub ok: Option, #[serde(rename = "Err")] @@ -99,7 +99,7 @@ struct IBCReceiveOutput { } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -struct QueryOutput { +pub struct QueryOutput { #[serde(rename = "Ok")] pub ok: Option, #[serde(rename = "Err")] @@ -107,7 +107,7 @@ struct QueryOutput { } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -struct WasmOutput { +pub struct WasmOutput { pub v010: Option, pub v1: Option, pub ibc: Option, @@ -184,7 +184,7 @@ fn b64_encode(data: &[u8]) -> String { base64::encode(data) } -pub fn finalize_raw_output(raw_output: RawWasmOutput) -> WasmOutput { +pub fn finalize_raw_output(raw_output: RawWasmOutput, is_query_output: bool) -> WasmOutput { return match raw_output { RawWasmOutput::Err { err, @@ -782,7 +782,7 @@ pub fn encrypt_output( } }; - let final_output = finalize_raw_output(output); + let final_output = finalize_raw_output(output, is_query_output); trace!("WasmOutput: {:?}", final_output); let encrypted_output = serde_json::to_vec(&final_output).map_err(|err| { From cc236c7a123969f59e0c64e65f9bcae58f2e6781 Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Wed, 17 Aug 2022 12:15:51 +0300 Subject: [PATCH 29/44] Support plaintext execute messages --- .../src/contract_operations.rs | 62 +++--- .../enclaves/shared/contract-engine/src/io.rs | 195 +++--------------- go-cosmwasm/lib.go | 69 ++----- go-cosmwasm/types/v1/ibc.go | 2 - x/compute/internal/keeper/relay.go | 2 +- 5 files changed, 80 insertions(+), 250 deletions(-) diff --git a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs index 33a88a2b7..4572797a2 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs @@ -179,7 +179,6 @@ pub fn init( pub struct ParsedMessage { pub should_validate_sig_info: bool, pub was_msg_encrypted: bool, - pub is_ibc_msg: bool, pub secret_msg: SecretMessage, pub decrypted_msg: Vec, pub contract_hash_for_validation: Option>, @@ -313,9 +312,8 @@ where } Ok(ParsedMessage { - should_validate_sig_info: true, + should_validate_sig_info: false, was_msg_encrypted: true, - is_ibc_msg: true, secret_msg: orig_secret_msg, decrypted_msg: serde_json::to_vec(&parsed_encrypted_ibc_packet).map_err(|err| { warn!( @@ -331,7 +329,7 @@ where // assume packet is not encrypted, continue in plaintext mode trace!( - "{} input is not encrypted: {:?}", + "{} input was plaintext: {:?}", function_name, base64::encode(&message) ); @@ -341,7 +339,6 @@ where Ok(ParsedMessage { should_validate_sig_info: false, was_msg_encrypted: false, - is_ibc_msg: true, secret_msg: orig_secret_msg, decrypted_msg, contract_hash_for_validation: None, @@ -357,23 +354,39 @@ pub fn parse_message( handle_type: &HandleType, ) -> Result { return match handle_type { - HandleType::HANDLE_TYPE_EXECUTE => { - let orig_secret_msg = SecretMessage::from_slice(message)?; + HandleType::HANDLE_TYPE_EXECUTE => match try_get_decrypted_secret_msg(message) { + Some(decrypted_secret_msg) => { + trace!( + "execute input before decryption: {:?}", + base64::encode(&message) + ); - trace!( - "execute input before decryption: {:?}", - base64::encode(&message) - ); - let decrypted_msg = orig_secret_msg.decrypt()?; - Ok(ParsedMessage { - should_validate_sig_info: true, - was_msg_encrypted: true, - is_ibc_msg: false, - secret_msg: orig_secret_msg, - decrypted_msg, - contract_hash_for_validation: None, - }) - } + Ok(ParsedMessage { + should_validate_sig_info: true, + was_msg_encrypted: true, + secret_msg: decrypted_secret_msg.secret_msg, + decrypted_msg: decrypted_secret_msg.decrypted_msg, + contract_hash_for_validation: None, + }) + } + None => { + trace!( + "execute input was plaintext: {:?}", + base64::encode(&message) + ); + + let secret_msg = get_secret_msg(message); + let decrypted_msg = secret_msg.msg.clone(); + + Ok(ParsedMessage { + should_validate_sig_info: true, + was_msg_encrypted: false, + secret_msg, + decrypted_msg, + contract_hash_for_validation: None, + }) + } + }, HandleType::HANDLE_TYPE_REPLY => { let orig_secret_msg = SecretMessage::from_slice(message)?; @@ -441,7 +454,6 @@ pub fn parse_message( return Ok(ParsedMessage { should_validate_sig_info: false, was_msg_encrypted: false, - is_ibc_msg: false, secret_msg: reply_secret_msg, decrypted_msg: serialized_reply, contract_hash_for_validation: None, @@ -546,7 +558,6 @@ pub fn parse_message( Ok(ParsedMessage { should_validate_sig_info: true, was_msg_encrypted: true, - is_ibc_msg: false, secret_msg: reply_secret_msg, decrypted_msg: decrypted_reply_as_vec, contract_hash_for_validation: Some( @@ -640,7 +651,6 @@ pub fn parse_message( Ok(ParsedMessage { should_validate_sig_info: true, was_msg_encrypted: true, - is_ibc_msg: false, secret_msg: reply_secret_msg, decrypted_msg: decrypted_reply_as_vec, contract_hash_for_validation: Some( @@ -670,7 +680,6 @@ pub fn parse_message( Ok(ParsedMessage { should_validate_sig_info: false, was_msg_encrypted: false, - is_ibc_msg: true, secret_msg: scrt_msg, decrypted_msg, contract_hash_for_validation: None, @@ -773,7 +782,6 @@ pub fn handle( let ParsedMessage { should_validate_sig_info, was_msg_encrypted, - is_ibc_msg, secret_msg, decrypted_msg, contract_hash_for_validation, @@ -841,7 +849,7 @@ pub fn handle( secret_msg.nonce, secret_msg.user_public_key ); - if !is_ibc_msg || was_msg_encrypted { + if was_msg_encrypted { output = encrypt_output( output, &secret_msg, diff --git a/cosmwasm/enclaves/shared/contract-engine/src/io.rs b/cosmwasm/enclaves/shared/contract-engine/src/io.rs index 539847227..3ef7b0cdb 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/io.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/io.rs @@ -52,17 +52,13 @@ pub enum RawWasmOutput { internal_reply_enclave_sig: Option, internal_msg_id: Option, }, - OkIBC { + OkIBCBasic { #[serde(rename = "Ok")] ok: enclave_cosmwasm_v1_types::ibc::IbcBasicResponse, - internal_reply_enclave_sig: Option, - internal_msg_id: Option, }, - OkIBCReceive { + OkIBCPacketReceive { #[serde(rename = "Ok")] ok: enclave_cosmwasm_v1_types::ibc::IbcReceiveResponse, - internal_reply_enclave_sig: Option, - internal_msg_id: Option, }, } @@ -110,8 +106,8 @@ pub struct QueryOutput { pub struct WasmOutput { pub v010: Option, pub v1: Option, - pub ibc: Option, - pub ibc_receive: Option, + pub ibc_basic: Option, + pub ibc_packet_receive: Option, pub query: Option, pub internal_reply_enclave_sig: Option, pub internal_msg_id: Option, @@ -195,8 +191,8 @@ pub fn finalize_raw_output(raw_output: RawWasmOutput, is_query_output: bool) -> WasmOutput { v010: None, v1: None, - ibc: None, - ibc_receive: None, + ibc_basic: None, + ibc_packet_receive: None, query: Some(QueryOutput { ok: None, err: Some(err), @@ -211,8 +207,8 @@ pub fn finalize_raw_output(raw_output: RawWasmOutput, is_query_output: bool) -> ok: None, }), v1: None, - ibc: None, - ibc_receive: None, + ibc_basic: None, + ibc_packet_receive: None, query: None, internal_reply_enclave_sig, internal_msg_id, @@ -229,8 +225,8 @@ pub fn finalize_raw_output(raw_output: RawWasmOutput, is_query_output: bool) -> ok: Some(ok), }), v1: None, - ibc: None, - ibc_receive: None, + ibc_basic: None, + ibc_packet_receive: None, query: None, internal_reply_enclave_sig, internal_msg_id, @@ -245,8 +241,8 @@ pub fn finalize_raw_output(raw_output: RawWasmOutput, is_query_output: bool) -> err: None, ok: Some(ok), }), - ibc: None, - ibc_receive: None, + ibc_basic: None, + ibc_packet_receive: None, query: None, internal_reply_enclave_sig, internal_msg_id, @@ -254,8 +250,8 @@ pub fn finalize_raw_output(raw_output: RawWasmOutput, is_query_output: bool) -> RawWasmOutput::QueryOkV010 { ok } | RawWasmOutput::QueryOkV1 { ok } => WasmOutput { v010: None, v1: None, - ibc: None, - ibc_receive: None, + ibc_basic: None, + ibc_packet_receive: None, query: Some(QueryOutput { ok: Some(ok), err: None, @@ -263,37 +259,29 @@ pub fn finalize_raw_output(raw_output: RawWasmOutput, is_query_output: bool) -> internal_reply_enclave_sig: None, internal_msg_id: None, }, - RawWasmOutput::OkIBC { - ok, - internal_reply_enclave_sig, - internal_msg_id, - } => WasmOutput { + RawWasmOutput::OkIBCBasic { ok } => WasmOutput { v010: None, v1: None, - ibc: Some(IBCOutput { + ibc_basic: Some(IBCOutput { err: None, ok: Some(ok), }), - ibc_receive: None, + ibc_packet_receive: None, query: None, - internal_reply_enclave_sig, - internal_msg_id, + internal_reply_enclave_sig: None, + internal_msg_id: None, }, - RawWasmOutput::OkIBCReceive { - ok, - internal_reply_enclave_sig, - internal_msg_id, - } => WasmOutput { + RawWasmOutput::OkIBCPacketReceive { ok } => WasmOutput { v010: None, v1: None, - ibc: None, - ibc_receive: Some(IBCReceiveOutput { + ibc_basic: None, + ibc_packet_receive: Some(IBCReceiveOutput { err: None, ok: Some(ok), }), query: None, - internal_reply_enclave_sig, - internal_msg_id, + internal_reply_enclave_sig: None, + internal_msg_id: None, }, }; } @@ -573,11 +561,7 @@ pub fn encrypt_output( None => None, // Not a reply, we don't need enclave sig } } - RawWasmOutput::OkIBC { - ok, - internal_reply_enclave_sig, - internal_msg_id, - } => { + RawWasmOutput::OkIBCBasic { ok } => { for sub_msg in &mut ok.messages { if let enclave_cosmwasm_v1_types::results::CosmosMsg::Wasm(wasm_msg) = &mut sub_msg.msg @@ -611,72 +595,8 @@ pub fn encrypt_output( attr.value = encrypt_preserialized_string(&encryption_key, &attr.value, &None)?; } } - - let msg_id = match reply_params { - Some(ref r) => { - let encrypted_id = Binary::from_base64(&encrypt_preserialized_string( - &encryption_key, - &r.sub_msg_id.to_string(), - &reply_params, - )?)?; - - Some(encrypted_id) - } - None => None, - }; - - *internal_msg_id = msg_id.clone(); - - *internal_reply_enclave_sig = match reply_params { - Some(_) => { - let mut events: Vec = vec![]; - - if ok.attributes.len() > 0 { - events.push(Event { - ty: "wasm".to_string(), - attributes: ok.attributes.clone(), - }) - } - - events.extend_from_slice(&ok.events.clone().as_slice()); - let custom_contract_event_prefix: String = "wasm-".to_string(); - for event in events.iter_mut() { - if event.ty != "wasm" { - event.ty = custom_contract_event_prefix.clone() + event.ty.as_str(); - } - } - - let reply = Reply { - id: msg_id.unwrap(), - result: SubMsgResult::Ok(SubMsgResponse { events, data: None }), - }; - - let reply_as_vec = serde_json::to_vec(&reply).map_err(|err| { - warn!( - "got an error while trying to serialize reply into bytes for internal_reply_enclave_sig {:?}: {}", - reply, err - ); - EnclaveError::FailedToSerialize - })?; - - let tmp_secret_msg = SecretMessage { - nonce: secret_msg.nonce, - user_public_key: secret_msg.user_public_key, - msg: reply_as_vec, - }; - - Some(Binary::from( - create_callback_signature(sender_addr, &tmp_secret_msg, &[]).as_slice(), - )) - } - None => None, // Not a reply, we don't need enclave sig - } } - RawWasmOutput::OkIBCReceive { - ok, - internal_reply_enclave_sig, - internal_msg_id, - } => { + RawWasmOutput::OkIBCPacketReceive { ok } => { for sub_msg in &mut ok.messages { if let enclave_cosmwasm_v1_types::results::CosmosMsg::Wasm(wasm_msg) = &mut sub_msg.msg @@ -716,69 +636,6 @@ pub fn encrypt_output( &ok.acknowledgement, &reply_params, )?)?; - - let msg_id = match reply_params { - Some(ref r) => { - let encrypted_id = Binary::from_base64(&encrypt_preserialized_string( - &encryption_key, - &r.sub_msg_id.to_string(), - &reply_params, - )?)?; - - Some(encrypted_id) - } - None => None, - }; - - *internal_msg_id = msg_id.clone(); - - *internal_reply_enclave_sig = match reply_params { - Some(_) => { - let mut events: Vec = vec![]; - - if ok.attributes.len() > 0 { - events.push(Event { - ty: "wasm".to_string(), - attributes: ok.attributes.clone(), - }) - } - - events.extend_from_slice(&ok.events.clone().as_slice()); - let custom_contract_event_prefix: String = "wasm-".to_string(); - for event in events.iter_mut() { - if event.ty != "wasm" { - event.ty = custom_contract_event_prefix.clone() + event.ty.as_str(); - } - } - - let reply = Reply { - id: msg_id.unwrap(), - result: SubMsgResult::Ok(SubMsgResponse { - events, - data: Some(ok.acknowledgement.clone()), - }), - }; - - let reply_as_vec = serde_json::to_vec(&reply).map_err(|err| { - warn!( - "got an error while trying to serialize reply into bytes for internal_reply_enclave_sig {:?}: {}", - reply, err - ); - EnclaveError::FailedToSerialize - })?; - - let tmp_secret_msg = SecretMessage { - nonce: secret_msg.nonce, - user_public_key: secret_msg.user_public_key, - msg: reply_as_vec, - }; - - Some(Binary::from( - create_callback_signature(sender_addr, &tmp_secret_msg, &[]).as_slice(), - )) - } - None => None, // Not a reply, we don't need enclave sig - } } }; diff --git a/go-cosmwasm/lib.go b/go-cosmwasm/lib.go index 0809da070..970da2127 100644 --- a/go-cosmwasm/lib.go +++ b/go-cosmwasm/lib.go @@ -86,13 +86,12 @@ func (w *Wasmer) GetCode(code CodeID) (WasmCode, error) { // This struct helps us to distinguish between v0.10 contract response and v1 contract response type ContractExecResponse struct { - V1 *V1ContractExecResponse `json:"v1,omitempty"` - V010 *V010ContractExecResponse `json:"v010,omitempty"` - InternaReplyEnclaveSig []byte `json:"internal_reply_enclave_sig"` - InternalMsgId []byte `json:"internal_msg_id"` - IBCChannelOpen *v1types.IBCChannelOpenResult `json:"ibc_channel_open,omitempty"` - IBC *v1types.IBCBasicResult `json:"ibc,omitempty"` - IBCReceive *v1types.IBCReceiveResult `json:"ibc_receive,omitempty"` + V1 *V1ContractExecResponse `json:"v1,omitempty"` + V010 *V010ContractExecResponse `json:"v010,omitempty"` + InternaReplyEnclaveSig []byte `json:"internal_reply_enclave_sig"` + InternalMsgId []byte `json:"internal_msg_id"` + IBCBasic *v1types.IBCBasicResult `json:"ibc_basic,omitempty"` + IBCPacketReceive *v1types.IBCReceiveResult `json:"ibc_packet_receive,omitempty"` } type V010ContractExecResponse struct { @@ -315,55 +314,23 @@ func (w *Wasmer) Execute( } } - // handle IBCChannelOpen response - if resp.IBCChannelOpen != nil { - if resp.IBCChannelOpen.Err != "" { - return nil, gasUsed, fmt.Errorf("%s", resp.IBCChannelOpen.Err) - } else if resp.IBCChannelOpen.Ok != nil { - return resp.IBCChannelOpen.Ok, gasUsed, nil - } else { - return nil, gasUsed, fmt.Errorf("cannot parse IBCChannelOpen response: %+v", resp) - } - } - - // handle IBCChannelConnect response - if resp.IBC != nil { - if resp.IBC.Err != nil { - return v1types.DataWithInternalReplyInfo{ - InternalMsgId: resp.InternalMsgId, - InternaReplyEnclaveSig: resp.InternaReplyEnclaveSig, - Data: []byte(resp.IBC.Err.GenericErr.Msg), - }, gasUsed, fmt.Errorf("%+v", resp.IBC.Err) - } else if resp.IBC.Ok != nil { - if isOutputAddressedToReply { - resp.IBC.Ok.InternalData, err = AppendReplyInternalDataToData([]byte{}, resp.InternaReplyEnclaveSig, resp.InternalMsgId) - if err != nil { - return nil, gasUsed, fmt.Errorf("cannot serialize v1 DataWithInternalReplyInfo into binary : %w", err) - } - } - return resp.V1.Ok, gasUsed, nil + if resp.IBCBasic != nil { + if resp.IBCBasic.Err != nil { + return nil, gasUsed, fmt.Errorf("%+v", resp.IBCBasic.Err) + } else if resp.IBCBasic.Ok != nil { + return resp.IBCBasic.Ok, gasUsed, nil } else { - return nil, gasUsed, fmt.Errorf("cannot parse v1 handle response: %+v", resp) + return nil, gasUsed, fmt.Errorf("cannot parse IBCBasic response: %+v", resp) } } - if resp.IBCReceive != nil { - if resp.IBCReceive.Err != nil { - return v1types.DataWithInternalReplyInfo{ - InternalMsgId: resp.InternalMsgId, - InternaReplyEnclaveSig: resp.InternaReplyEnclaveSig, - Data: []byte(resp.IBCReceive.Err.GenericErr.Msg), - }, gasUsed, fmt.Errorf("%+v", resp.IBCReceive.Err) - } else if resp.IBCReceive.Ok != nil { - if isOutputAddressedToReply { - resp.IBCReceive.Ok.Acknowledgement, err = AppendReplyInternalDataToData(resp.IBCReceive.Ok.Acknowledgement, resp.InternaReplyEnclaveSig, resp.InternalMsgId) - if err != nil { - return nil, gasUsed, fmt.Errorf("cannot serialize v1 DataWithInternalReplyInfo into binary : %w", err) - } - } - return resp.V1.Ok, gasUsed, nil + if resp.IBCPacketReceive != nil { + if resp.IBCPacketReceive.Err != nil { + return nil, gasUsed, fmt.Errorf("%+v", resp.IBCPacketReceive.Err) + } else if resp.IBCPacketReceive.Ok != nil { + return resp.IBCPacketReceive.Ok, gasUsed, nil } else { - return nil, gasUsed, fmt.Errorf("cannot parse v1 handle response: %+v", resp) + return nil, gasUsed, fmt.Errorf("cannot parse IBCPacketReceive response: %+v", resp) } } diff --git a/go-cosmwasm/types/v1/ibc.go b/go-cosmwasm/types/v1/ibc.go index d98c6826b..efe2a462d 100644 --- a/go-cosmwasm/types/v1/ibc.go +++ b/go-cosmwasm/types/v1/ibc.go @@ -228,8 +228,6 @@ type IBCBasicResult struct { // IBCBasicResponse defines the return value on a successful processing. // This is the counterpart of [IbcBasicResponse](https://github.com/CosmWasm/cosmwasm/blob/v0.14.0-beta1/packages/std/src/ibc.rs#L194-L216). type IBCBasicResponse struct { - // Used for internal purposes - InternalData []byte `json:"internal_data,omitempty"` // Messages comes directly from the contract and is its request for action. // If the ReplyOn value matches the result, the runtime will invoke this // contract's `reply` entry point after execution. Otherwise, this is all diff --git a/x/compute/internal/keeper/relay.go b/x/compute/internal/keeper/relay.go index 776f3e52d..a60a928e0 100644 --- a/x/compute/internal/keeper/relay.go +++ b/x/compute/internal/keeper/relay.go @@ -287,6 +287,6 @@ func (k Keeper) OnTimeoutPacket( func (k Keeper) handleIBCBasicContractResponse(ctx sdk.Context, addr sdk.AccAddress, ibcPortID string, inputMsg []byte, res *v1types.IBCBasicResponse) error { verificationInfo := types.NewVerificationInfo([]byte{}, sdktxsigning.SignMode_SIGN_MODE_UNSPECIFIED, []byte{}, []byte{}, []byte{}, nil) - _, err := k.handleContractResponse(ctx, addr, ibcPortID, res.Messages, res.Attributes, res.Events, res.InternalData, inputMsg, verificationInfo, wasmTypes.CosmosMsgVersionV1) + _, err := k.handleContractResponse(ctx, addr, ibcPortID, res.Messages, res.Attributes, res.Events, nil, inputMsg, verificationInfo, wasmTypes.CosmosMsgVersionV1) return err } From 28a412ac7fa14c640968eb8227105b1988d6b2a7 Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Wed, 17 Aug 2022 13:10:05 +0300 Subject: [PATCH 30/44] Correct errors for replies --- x/compute/internal/keeper/keeper.go | 8 ++++---- x/compute/internal/types/errors.go | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/x/compute/internal/keeper/keeper.go b/x/compute/internal/keeper/keeper.go index 115bd8e54..9fd58eb6e 100644 --- a/x/compute/internal/keeper/keeper.go +++ b/x/compute/internal/keeper/keeper.go @@ -1066,12 +1066,12 @@ func (k Keeper) reply(ctx sdk.Context, contractAddress sdk.AccAddress, reply v1w consumeGas(ctx, gasUsed) if execErr != nil { - return nil, sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) + return nil, sdkerrors.Wrap(types.ErrReplyFailed, execErr.Error()) } switch res := response.(type) { case *v010wasmTypes.HandleResponse: - return nil, sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("response of reply should always be a CosmWasm v1 response type: %+v", res)) + return nil, sdkerrors.Wrap(types.ErrReplyFailed, fmt.Sprintf("response of reply should always be a CosmWasm v1 response type: %+v", res)) case *v1wasmTypes.Response: consumeGas(ctx, gasUsed) @@ -1082,11 +1082,11 @@ func (k Keeper) reply(ctx sdk.Context, contractAddress sdk.AccAddress, reply v1w data, err := k.handleContractResponse(ctx, contractAddress, contractInfo.IBCPortID, res.Messages, res.Attributes, res.Events, res.Data, ogTx, ogSigInfo, wasmTypes.CosmosMsgVersionV1) if err != nil { - return nil, sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) + return nil, sdkerrors.Wrap(types.ErrReplyFailed, err.Error()) } return data, nil default: - return nil, sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("cannot detect response type: %+v", res)) + return nil, sdkerrors.Wrap(types.ErrReplyFailed, fmt.Sprintf("cannot detect response type: %+v", res)) } } diff --git a/x/compute/internal/types/errors.go b/x/compute/internal/types/errors.go index 1cd214613..7c7dfff50 100644 --- a/x/compute/internal/types/errors.go +++ b/x/compute/internal/types/errors.go @@ -64,6 +64,9 @@ var ( // ErrUnknownMsg error by a message handler to show that it is not responsible for this message type ErrUnknownMsg = sdkErrors.Register(DefaultCodespace, 18, "unknown message from the contract") + // ErrReplyFailed error for rust execution contract failure + ErrReplyFailed = sdkErrors.Register(DefaultCodespace, 19, "reply to contract failed") + // ErrInvalidEvent error if an attribute/event from the contract is invalid ErrInvalidEvent = sdkErrors.Register(DefaultCodespace, 21, "invalid event") ) From 94b816a7e0be6b8ecc33944c64d18de04dea8554 Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Thu, 18 Aug 2022 12:24:24 +0300 Subject: [PATCH 31/44] parse the message correctly --- .../src/contract_operations.rs | 107 +++++++----------- 1 file changed, 43 insertions(+), 64 deletions(-) diff --git a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs index 5f3bb724c..03839d239 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs @@ -254,91 +254,73 @@ pub fn try_get_decrypted_secret_msg(message: &[u8]) -> Option( - _t: T, - message: &[u8], - orig_secret_msg: SecretMessage, - function_name: &str, -) -> Result +pub fn parse_ibc_packet(_t: T, message: &[u8], function_name: &str) -> ParsedMessage where T: IbcPacketTrait + Serialize + DeserializeOwned + Debug, { - let mut parsed_encrypted_ibc_packet: T = - serde_json::from_slice(&orig_secret_msg.msg.as_slice().to_vec()).map_err(|err| { - warn!( + let mut parsed_encrypted_ibc_packet: T = serde_json::from_slice(&message.as_slice().to_vec()) + .map_err(|err| { + warn!( "{} msg got an error while trying to deserialize msg input bytes into json {:?}: {}", function_name, String::from_utf8_lossy(&orig_secret_msg.msg), err ); - EnclaveError::FailedToDeserialize - })?; + EnclaveError::FailedToDeserialize + })?; - let tmp_secret_data = SecretMessage { - nonce: orig_secret_msg.nonce, - user_public_key: orig_secret_msg.user_public_key, - msg: parsed_encrypted_ibc_packet.get_packet().as_slice().to_vec(), - }; + let tmp_secret_data = get_secret_msg(parsed_encrypted_ibc_packet.get_packet().as_slice()); + let was_msg_encrypted = false; + let orig_secret_msg = tmp_secret_data; match tmp_secret_data.decrypt() { Ok(decrypted_msg) => { // IBC packet was encrypted trace!( - "{} input before decryption: {:?}", + "{} data before decryption: {:?}", function_name, base64::encode(&message) ); parsed_encrypted_ibc_packet.set_packet(decrypted_msg.as_slice().into()); - - match parsed_encrypted_ibc_packet.get_ack() { - Some(ack_data) => { - let tmp_secret_ack_data = SecretMessage { - nonce: orig_secret_msg.nonce, - user_public_key: orig_secret_msg.user_public_key, - msg: ack_data.as_slice().to_vec(), - }; - - parsed_encrypted_ibc_packet - .set_ack(tmp_secret_ack_data.decrypt()?.as_slice().into()); - } - None => {} - } - - Ok(ParsedMessage { - should_validate_sig_info: false, - was_msg_encrypted: true, - secret_msg: orig_secret_msg, - decrypted_msg: serde_json::to_vec(&parsed_encrypted_ibc_packet).map_err(|err| { - warn!( - "got an error while trying to serialize {} msg into bytes {:?}: {}", - function_name, parsed_encrypted_ibc_packet, err - ); - EnclaveError::FailedToSerialize - })?, - contract_hash_for_validation: None, - }) + was_msg_encrypted = true; } Err(_) => { - // assume packet is not encrypted, continue in plaintext mode + // assume data is not encrypted trace!( - "{} input was plaintext: {:?}", + "{} data was plaintext: {:?}", function_name, base64::encode(&message) ); + } + } - let decrypted_msg = orig_secret_msg.msg.clone(); + let tmp_secret_ack = get_secret_msg(parsed_encrypted_ibc_packet.get_ack().as_slice()); - Ok(ParsedMessage { - should_validate_sig_info: false, - was_msg_encrypted: false, - secret_msg: orig_secret_msg, - decrypted_msg, - contract_hash_for_validation: None, - }) + match tmp_secret_ack.decrypt() { + Ok(ack_data) => { + parsed_encrypted_ibc_packet.set_ack(ack_data.as_slice().into()); + was_msg_encrypted = true; + + orig_secret_msg = tmp_secret_ack; } + Err(_) => {} + } + + ParsedMessage { + should_validate_sig_info: false, + was_msg_encrypted, + secret_msg: orig_secret_msg, + decrypted_msg: serde_json::to_vec(&parsed_encrypted_ibc_packet).map_err(|err| { + warn!( + "got an error while trying to serialize {} msg into bytes {:?}: {}", + function_name, parsed_encrypted_ibc_packet, err + ); + EnclaveError::FailedToSerialize + })?, + contract_hash_for_validation: None, } } @@ -683,32 +665,29 @@ pub fn parse_message( HandleType::HANDLE_TYPE_IBC_PACKET_RECEIVE => { // LIORRR TODO: Maybe mark whether the message was encrypted or not. let orig_secret_msg = get_secret_msg(message); - parse_ibc_packet( + Ok(parse_ibc_packet( IbcPacketReceiveMsg::default(), message, - orig_secret_msg, "ibc_packet_receive", - ) + )) } HandleType::HANDLE_TYPE_IBC_PACKET_ACK => { // LIORRR TODO: Maybe mark whether the message was encrypted or not. let orig_secret_msg = get_secret_msg(message); - parse_ibc_packet( + Ok(parse_ibc_packet( IbcPacketAckMsg::default(), message, - orig_secret_msg, "ibc_packet_receive", - ) + )) } HandleType::HANDLE_TYPE_IBC_PACKET_TIMEOUT => { // LIORRR TODO: Maybe mark whether the message was encrypted or not. let orig_secret_msg = get_secret_msg(message); - parse_ibc_packet( + Ok(parse_ibc_packet( IbcPacketTimeoutMsg::default(), message, - orig_secret_msg, "ibc_packet_timeout", - ) + )) } }; } From 18abe47270a59088dc49e3fef0a712601241dd20 Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Fri, 19 Aug 2022 20:06:42 +0300 Subject: [PATCH 32/44] Create IBC tests suite --- x/compute/internal/keeper/ibc_test.go | 332 +++++++++++++++++++++++ x/compute/internal/keeper/test_common.go | 38 ++- 2 files changed, 358 insertions(+), 12 deletions(-) create mode 100644 x/compute/internal/keeper/ibc_test.go diff --git a/x/compute/internal/keeper/ibc_test.go b/x/compute/internal/keeper/ibc_test.go new file mode 100644 index 000000000..9092290b1 --- /dev/null +++ b/x/compute/internal/keeper/ibc_test.go @@ -0,0 +1,332 @@ +package keeper + +import ( + "encoding/hex" + "testing" + + crypto "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + cosmwasm "github.com/enigmampc/SecretNetwork/go-cosmwasm/types" + v1types "github.com/enigmampc/SecretNetwork/go-cosmwasm/types/v1" + "github.com/enigmampc/SecretNetwork/x/compute/internal/types" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/log" +) + +func ibcChannelConnectHelper( + t *testing.T, keeper Keeper, ctx sdk.Context, + contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, msg string, + gas uint64, shouldSendOpenAck bool, channel v1types.IBCChannel, +) cosmwasm.StdError { + // create new ctx with the same storage and a gas limit + // this is to reset the event manager, so we won't get + // events from past calls + gasMeter := &WasmCounterGasMeter{0, sdk.NewGasMeter(gas)} + ctx = sdk.NewContext( + ctx.MultiStore(), + ctx.BlockHeader(), + ctx.IsCheckTx(), + log.NewNopLogger(), + ).WithGasMeter(gasMeter) + + var ibcChannelConnectMsg v1types.IBCChannelConnectMsg + if shouldSendOpenAck { + ibcChannelConnectMsg = v1types.IBCChannelConnectMsg{ + OpenAck: &v1types.IBCOpenAck{ + Channel: channel, + CounterpartyVersion: "", + }, + OpenConfirm: nil, + } + } else { + ibcChannelConnectMsg = v1types.IBCChannelConnectMsg{ + OpenAck: nil, + OpenConfirm: &v1types.IBCOpenConfirm{ + Channel: channel, + }, + } + } + + err := keeper.OnConnectChannel(ctx, contractAddr, ibcChannelConnectMsg) + + require.NotZero(t, gasMeter.GetWasmCounter(), err) + + if err != nil { + return cosmwasm.StdError{GenericErr: &cosmwasm.GenericErr{Msg: err.Error()}} + } + + return cosmwasm.StdError{} +} + +func ibcChannelOpenHelper( + t *testing.T, keeper Keeper, ctx sdk.Context, + contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, msg string, + gas uint64, shouldSendOpenTry bool, channel v1types.IBCChannel, +) (string, cosmwasm.StdError) { + // create new ctx with the same storage and a gas limit + // this is to reset the event manager, so we won't get + // events from past calls + gasMeter := &WasmCounterGasMeter{0, sdk.NewGasMeter(gas)} + ctx = sdk.NewContext( + ctx.MultiStore(), + ctx.BlockHeader(), + ctx.IsCheckTx(), + log.NewNopLogger(), + ).WithGasMeter(gasMeter) + + var ibcChannelOpenMsg v1types.IBCChannelOpenMsg + if shouldSendOpenTry { + ibcChannelOpenMsg = v1types.IBCChannelOpenMsg{ + OpenTry: &v1types.IBCOpenTry{ + Channel: channel, + CounterpartyVersion: "", + }, + OpenInit: nil, + } + } else { + ibcChannelOpenMsg = v1types.IBCChannelOpenMsg{ + OpenTry: nil, + OpenInit: &v1types.IBCOpenInit{ + Channel: channel, + }, + } + } + + res, err := keeper.OnOpenChannel(ctx, contractAddr, ibcChannelOpenMsg) + + require.NotZero(t, gasMeter.GetWasmCounter(), err) + + if err != nil { + return "", cosmwasm.StdError{GenericErr: &cosmwasm.GenericErr{Msg: err.Error()}} + } + + return res, cosmwasm.StdError{} +} + +func ibcChannelCloseHelper( + t *testing.T, keeper Keeper, ctx sdk.Context, + contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, msg string, + gas uint64, shouldSendCloseConfirn bool, channel v1types.IBCChannel, +) cosmwasm.StdError { + // create new ctx with the same storage and a gas limit + // this is to reset the event manager, so we won't get + // events from past calls + gasMeter := &WasmCounterGasMeter{0, sdk.NewGasMeter(gas)} + ctx = sdk.NewContext( + ctx.MultiStore(), + ctx.BlockHeader(), + ctx.IsCheckTx(), + log.NewNopLogger(), + ).WithGasMeter(gasMeter) + + var ibcChannelCloseMsg v1types.IBCChannelCloseMsg + if shouldSendCloseConfirn { + ibcChannelCloseMsg = v1types.IBCChannelCloseMsg{ + CloseConfirm: &v1types.IBCCloseConfirm{ + Channel: channel, + }, + CloseInit: nil, + } + } else { + ibcChannelCloseMsg = v1types.IBCChannelCloseMsg{ + CloseConfirm: nil, + CloseInit: &v1types.IBCCloseInit{ + Channel: channel, + }, + } + } + + err := keeper.OnCloseChannel(ctx, contractAddr, ibcChannelCloseMsg) + + require.NotZero(t, gasMeter.GetWasmCounter(), err) + + if err != nil { + return cosmwasm.StdError{GenericErr: &cosmwasm.GenericErr{Msg: err.Error()}} + } + + return cosmwasm.StdError{} +} + +func createIBCEndpoint(port string, channel string) v1types.IBCEndpoint { + return v1types.IBCEndpoint{ + PortID: port, + ChannelID: channel, + } +} + +func createIBCTimeout(timeout uint64) v1types.IBCTimeout { + return v1types.IBCTimeout{ + Block: nil, + Timestamp: timeout, + } +} + +func createIBCPacket(src v1types.IBCEndpoint, dest v1types.IBCEndpoint, sequence uint64, timeout v1types.IBCTimeout, data []byte) v1types.IBCPacket { + return v1types.IBCPacket{ + Data: data, + Src: src, + Dest: dest, + Sequence: sequence, + Timeout: timeout, + } +} + +func ibcPacketReceiveHelper( + t *testing.T, keeper Keeper, ctx sdk.Context, + contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, msg string, + shouldEncryptMsg bool, gas uint64, packet v1types.IBCPacket, +) ([]byte, cosmwasm.StdError) { + var nonce []byte + internalPacket := packet + + if shouldEncryptMsg { + hashStr := hex.EncodeToString(keeper.GetContractHash(ctx, contractAddr)) + + msg := types.SecretMsg{ + CodeHash: []byte(hashStr), + Msg: packet.Data, + } + + dataBz, err := wasmCtx.Encrypt(msg.Serialize()) + require.NoError(t, err) + nonce = dataBz[0:32] + internalPacket.Data = dataBz + } + + // create new ctx with the same storage and a gas limit + // this is to reset the event manager, so we won't get + // events from past calls + gasMeter := &WasmCounterGasMeter{0, sdk.NewGasMeter(gas)} + ctx = sdk.NewContext( + ctx.MultiStore(), + ctx.BlockHeader(), + ctx.IsCheckTx(), + log.NewNopLogger(), + ).WithGasMeter(gasMeter) + + ibcPacketReceiveMsg := v1types.IBCPacketReceiveMsg{ + Packet: internalPacket, + Relayer: "relayer", + } + + res, err := keeper.OnRecvPacket(ctx, contractAddr, ibcPacketReceiveMsg) + + require.NotZero(t, gasMeter.GetWasmCounter(), err) + + if err != nil { + if shouldEncryptMsg { + return nil, extractInnerError(t, err, nonce, true, true) + } + + return nil, cosmwasm.StdError{GenericErr: &cosmwasm.GenericErr{Msg: err.Error()}} + } + + return res, cosmwasm.StdError{} +} + +func ibcPacketAckHelper( + t *testing.T, keeper Keeper, ctx sdk.Context, + contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, msg string, + shouldEncryptMsg bool, gas uint64, originalPacket v1types.IBCPacket, ack []byte, +) cosmwasm.StdError { + var nonce []byte + + if shouldEncryptMsg { + hashStr := hex.EncodeToString(keeper.GetContractHash(ctx, contractAddr)) + + msg := types.SecretMsg{ + CodeHash: []byte(hashStr), + Msg: ack, + } + + ackBz, err := wasmCtx.Encrypt(msg.Serialize()) + require.NoError(t, err) + nonce = ackBz[0:32] + ack = ackBz + } + + // create new ctx with the same storage and a gas limit + // this is to reset the event manager, so we won't get + // events from past calls + gasMeter := &WasmCounterGasMeter{0, sdk.NewGasMeter(gas)} + ctx = sdk.NewContext( + ctx.MultiStore(), + ctx.BlockHeader(), + ctx.IsCheckTx(), + log.NewNopLogger(), + ).WithGasMeter(gasMeter) + + ibcPacketAckMsg := v1types.IBCPacketAckMsg{ + Acknowledgement: v1types.IBCAcknowledgement{ + Data: ack, + }, + OriginalPacket: originalPacket, + Relayer: "relayer", + } + + err := keeper.OnAckPacket(ctx, contractAddr, ibcPacketAckMsg) + + require.NotZero(t, gasMeter.GetWasmCounter(), err) + + if err != nil { + if shouldEncryptMsg { + return extractInnerError(t, err, nonce, true, true) + } + + return cosmwasm.StdError{GenericErr: &cosmwasm.GenericErr{Msg: err.Error()}} + } + + return cosmwasm.StdError{} +} + +func ibcPacketTimeoutHelper( + t *testing.T, keeper Keeper, ctx sdk.Context, + contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, msg string, + shouldEncryptMsg bool, gas uint64, originalPacket v1types.IBCPacket, +) cosmwasm.StdError { + var nonce []byte + + if shouldEncryptMsg { + hashStr := hex.EncodeToString(keeper.GetContractHash(ctx, contractAddr)) + + msg := types.SecretMsg{ + CodeHash: []byte(hashStr), + Msg: originalPacket.Data, + } + + dataBz, err := wasmCtx.Encrypt(msg.Serialize()) + require.NoError(t, err) + nonce = dataBz[0:32] + originalPacket.Data = dataBz + } + + // create new ctx with the same storage and a gas limit + // this is to reset the event manager, so we won't get + // events from past calls + gasMeter := &WasmCounterGasMeter{0, sdk.NewGasMeter(gas)} + ctx = sdk.NewContext( + ctx.MultiStore(), + ctx.BlockHeader(), + ctx.IsCheckTx(), + log.NewNopLogger(), + ).WithGasMeter(gasMeter) + + ibcPacketTimeoutMsg := v1types.IBCPacketTimeoutMsg{ + Packet: originalPacket, + Relayer: "relayer", + } + + err := keeper.OnTimeoutPacket(ctx, contractAddr, ibcPacketTimeoutMsg) + + require.NotZero(t, gasMeter.GetWasmCounter(), err) + + if err != nil { + if shouldEncryptMsg { + return extractInnerError(t, err, nonce, true, true) + } + + return cosmwasm.StdError{GenericErr: &cosmwasm.GenericErr{Msg: err.Error()}} + } + + return cosmwasm.StdError{} +} diff --git a/x/compute/internal/keeper/test_common.go b/x/compute/internal/keeper/test_common.go index c4269232b..bb47de2dd 100644 --- a/x/compute/internal/keeper/test_common.go +++ b/x/compute/internal/keeper/test_common.go @@ -2,6 +2,7 @@ package keeper import ( "encoding/binary" + "encoding/json" "fmt" "io/ioutil" "os" @@ -89,6 +90,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/upgrade" upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" + v1types "github.com/enigmampc/SecretNetwork/go-cosmwasm/types/v1" wasmtypes "github.com/enigmampc/SecretNetwork/x/compute/internal/types" "github.com/enigmampc/SecretNetwork/x/registration" ) @@ -494,20 +496,13 @@ func handleExecute(ctx sdk.Context, k Keeper, msg *wasmtypes.MsgExecuteContract) return res, nil } -func PrepareInitSignedTx(t *testing.T, keeper Keeper, ctx sdk.Context, creator sdk.AccAddress, privKey crypto.PrivKey, encMsg []byte, codeID uint64, funds sdk.Coins) sdk.Context { - creatorAcc, err := ante.GetSignerAcc(ctx, keeper.accountKeeper, creator) - require.NoError(t, err) - - initMsg := wasmtypes.MsgInstantiateContract{ - Sender: creator, - CodeID: codeID, - Label: "demo contract 1", - InitMsg: encMsg, - InitFunds: funds, +func PrepareIBCOpenAck(t *testing.T, keeper Keeper, ctx sdk.Context, ibcOpenAck v1types.IBCOpenAck, ibcOpenConfirm v1types.IBCOpenConfirm) sdk.Context { + channelConnectMsg := v1types.IBCChannelConnectMsg{ + OpenAck: &ibcOpenAck, + OpenConfirm: &ibcOpenConfirm, } - tx := NewTestTx(&initMsg, creatorAcc, privKey) - txBytes, err := tx.Marshal() + txBytes, err := json.Marshal(channelConnectMsg) require.NoError(t, err) return ctx.WithTxBytes(txBytes) @@ -531,6 +526,25 @@ func PrepareExecSignedTx(t *testing.T, keeper Keeper, ctx sdk.Context, sender sd return ctx.WithTxBytes(txBytes) } +func PrepareInitSignedTx(t *testing.T, keeper Keeper, ctx sdk.Context, creator sdk.AccAddress, privKey crypto.PrivKey, encMsg []byte, codeID uint64, funds sdk.Coins) sdk.Context { + creatorAcc, err := ante.GetSignerAcc(ctx, keeper.accountKeeper, creator) + require.NoError(t, err) + + initMsg := wasmtypes.MsgInstantiateContract{ + Sender: creator, + CodeID: codeID, + Label: "demo contract 1", + InitMsg: encMsg, + InitFunds: funds, + } + tx := NewTestTx(&initMsg, creatorAcc, privKey) + + txBytes, err := tx.Marshal() + require.NoError(t, err) + + return ctx.WithTxBytes(txBytes) +} + func NewTestTx(msg sdk.Msg, creatorAcc authtypes.AccountI, privKey crypto.PrivKey) *sdktx.Tx { return NewTestTxMultiple([]sdk.Msg{msg}, []authtypes.AccountI{creatorAcc}, []crypto.PrivKey{privKey}) } From 215237373764a14bf51e6884310bdd2d9a1ae290 Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Sun, 21 Aug 2022 00:14:15 +0300 Subject: [PATCH 33/44] Fix compilation --- .../src/contract_operations.rs | 48 +++++++++---------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs index 03839d239..7231f010a 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs @@ -254,26 +254,30 @@ pub fn try_get_decrypted_secret_msg(message: &[u8]) -> Option(_t: T, message: &[u8], function_name: &str) -> ParsedMessage +pub fn parse_ibc_packet( + _t: T, + message: &[u8], + function_name: &str, +) -> Result where T: IbcPacketTrait + Serialize + DeserializeOwned + Debug, { - let mut parsed_encrypted_ibc_packet: T = serde_json::from_slice(&message.as_slice().to_vec()) - .map_err(|err| { - warn!( + let mut parsed_encrypted_ibc_packet: T = + serde_json::from_slice(&message.to_vec()).map_err(|err| { + warn!( "{} msg got an error while trying to deserialize msg input bytes into json {:?}: {}", function_name, - String::from_utf8_lossy(&orig_secret_msg.msg), + String::from_utf8_lossy(&message), err ); - EnclaveError::FailedToDeserialize - })?; + EnclaveError::FailedToDeserialize + })?; let tmp_secret_data = get_secret_msg(parsed_encrypted_ibc_packet.get_packet().as_slice()); - let was_msg_encrypted = false; - let orig_secret_msg = tmp_secret_data; + let mut was_msg_encrypted = false; + let mut orig_secret_msg = tmp_secret_data; - match tmp_secret_data.decrypt() { + match orig_secret_msg.decrypt() { Ok(decrypted_msg) => { // IBC packet was encrypted @@ -297,7 +301,8 @@ where } } - let tmp_secret_ack = get_secret_msg(parsed_encrypted_ibc_packet.get_ack().as_slice()); + let ack = parsed_encrypted_ibc_packet.get_ack(); + let tmp_secret_ack = get_secret_msg(ack.unwrap_or(Binary::from(vec![].as_slice())).as_slice()); match tmp_secret_ack.decrypt() { Ok(ack_data) => { @@ -309,7 +314,7 @@ where Err(_) => {} } - ParsedMessage { + Ok(ParsedMessage { should_validate_sig_info: false, was_msg_encrypted, secret_msg: orig_secret_msg, @@ -321,7 +326,7 @@ where EnclaveError::FailedToSerialize })?, contract_hash_for_validation: None, - } + }) } // Parse the message that was passed to handle (Based on the assumption that it might be a reply or IBC as well) @@ -664,30 +669,23 @@ pub fn parse_message( } HandleType::HANDLE_TYPE_IBC_PACKET_RECEIVE => { // LIORRR TODO: Maybe mark whether the message was encrypted or not. - let orig_secret_msg = get_secret_msg(message); - Ok(parse_ibc_packet( + parse_ibc_packet( IbcPacketReceiveMsg::default(), message, "ibc_packet_receive", - )) + ) } HandleType::HANDLE_TYPE_IBC_PACKET_ACK => { // LIORRR TODO: Maybe mark whether the message was encrypted or not. - let orig_secret_msg = get_secret_msg(message); - Ok(parse_ibc_packet( - IbcPacketAckMsg::default(), - message, - "ibc_packet_receive", - )) + parse_ibc_packet(IbcPacketAckMsg::default(), message, "ibc_packet_receive") } HandleType::HANDLE_TYPE_IBC_PACKET_TIMEOUT => { // LIORRR TODO: Maybe mark whether the message was encrypted or not. - let orig_secret_msg = get_secret_msg(message); - Ok(parse_ibc_packet( + parse_ibc_packet( IbcPacketTimeoutMsg::default(), message, "ibc_packet_timeout", - )) + ) } }; } From f512b1c1f2227edd8d761086d94954cd349d2607 Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Mon, 22 Aug 2022 21:17:37 +0300 Subject: [PATCH 34/44] Fix possible null dref in tests --- x/compute/internal/keeper/keeper.go | 12 +++++++--- x/compute/internal/keeper/keeper_test.go | 8 +++++-- x/compute/internal/keeper/recurse_test.go | 23 ++++++++++++++----- .../internal/keeper/secret_contracts_test.go | 16 ++++++++++--- 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/x/compute/internal/keeper/keeper.go b/x/compute/internal/keeper/keeper.go index 9fd58eb6e..261f2bda3 100644 --- a/x/compute/internal/keeper/keeper.go +++ b/x/compute/internal/keeper/keeper.go @@ -712,12 +712,18 @@ func (k Keeper) GetContractAddress(ctx sdk.Context, label string) sdk.AccAddress return contractAddress } -func (k Keeper) GetContractHash(ctx sdk.Context, contractAddress sdk.AccAddress) []byte { - codeId := k.GetContractInfo(ctx, contractAddress).CodeID +func (k Keeper) GetContractHash(ctx sdk.Context, contractAddress sdk.AccAddress) ([]byte, error) { + contractInfo := k.GetContractInfo(ctx, contractAddress) + + if contractInfo == nil { + return nil, fmt.Errorf("failed to contract info for the following address: %s", contractAddress.String()) + } + + codeId := contractInfo.CodeID hash := k.GetCodeInfo(ctx, codeId).CodeHash - return hash + return hash, nil } func (k Keeper) GetContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo { diff --git a/x/compute/internal/keeper/keeper_test.go b/x/compute/internal/keeper/keeper_test.go index c4cb36bb6..30c431d06 100644 --- a/x/compute/internal/keeper/keeper_test.go +++ b/x/compute/internal/keeper/keeper_test.go @@ -916,7 +916,9 @@ func TestExecuteWithCpuLoop(t *testing.T) { ctx = ctx.WithGasMeter(sdk.NewGasMeter(gasLimit)) require.Equal(t, uint64(0), ctx.GasMeter().GasConsumed()) - codeHash := keeper.GetContractHash(ctx, addr) + codeHash, err := keeper.GetContractHash(ctx, addr) + require.NoError(t, err) + codeHashStr := hex.EncodeToString(codeHash) msg2 := types.SecretMsg{ @@ -1002,7 +1004,9 @@ func TestExecuteWithStorageLoop(t *testing.T) { require.True(t, ok, "%+v", r) }() - codeHash := keeper.GetContractHash(ctx, addr) + codeHash, err := keeper.GetContractHash(ctx, addr) + require.NoError(t, err) + codeHashStr := hex.EncodeToString(codeHash) msg := types.SecretMsg{ diff --git a/x/compute/internal/keeper/recurse_test.go b/x/compute/internal/keeper/recurse_test.go index 51efdfd50..3e9cdb52b 100644 --- a/x/compute/internal/keeper/recurse_test.go +++ b/x/compute/internal/keeper/recurse_test.go @@ -155,7 +155,10 @@ func TestGasCostOnQuery(t *testing.T) { recurse := tc.msg recurse.Contract = contractAddr - msg := buildQuery(t, recurse, hex.EncodeToString(keeper.GetContractHash(ctx, contractAddr))) + codeHash, err := keeper.GetContractHash(ctx, contractAddr) + require.NoError(t, err) + + msg := buildQuery(t, recurse, hex.EncodeToString(codeHash)) data, qErr := queryHelper(t, keeper, ctx, contractAddr, string(msg), true, false, tc.gasLimit) require.Empty(t, qErr) @@ -169,7 +172,7 @@ func TestGasCostOnQuery(t *testing.T) { // assert result is 32 byte sha256 hash (if hashed), or contractAddr if not var resp recurseResponse - err := json.Unmarshal([]byte(data), &resp) + err = json.Unmarshal([]byte(data), &resp) require.NoError(t, err) if recurse.Work == 0 { assert.Equal(t, len(resp.Hashed), len(creator.String())) @@ -233,14 +236,18 @@ func TestGasOnExternalQuery(t *testing.T) { recurse := tc.msg recurse.Contract = contractAddr - msg := buildQuery(t, recurse, hex.EncodeToString(keeper.GetContractHash(ctx, contractAddr))) + + codeHash, err := keeper.GetContractHash(ctx, contractAddr) + require.NoError(t, err) + + msg := buildQuery(t, recurse, hex.EncodeToString(codeHash)) secretMsg := types.SecretMsg{ - CodeHash: []byte(hex.EncodeToString(keeper.GetContractHash(ctx, contractAddr))), + CodeHash: []byte(hex.EncodeToString(codeHash)), Msg: msg, } - msg, err := wasmCtx.Encrypt(secretMsg.Serialize()) + msg, err = wasmCtx.Encrypt(secretMsg.Serialize()) require.NoError(t, err) // do the query @@ -349,7 +356,11 @@ func TestLimitRecursiveQueryGas(t *testing.T) { // prepare the query recurse := tc.msg recurse.Contract = contractAddr - msg := buildQuery(t, recurse, hex.EncodeToString(keeper.GetContractHash(ctx, contractAddr))) + + codeHash, err := keeper.GetContractHash(ctx, contractAddr) + require.NoError(t, err) + + msg := buildQuery(t, recurse, hex.EncodeToString(codeHash)) // if we expect out of gas, make sure this panics if tc.expectOutOfGas { diff --git a/x/compute/internal/keeper/secret_contracts_test.go b/x/compute/internal/keeper/secret_contracts_test.go index bb3a70a7a..03caffbf0 100644 --- a/x/compute/internal/keeper/secret_contracts_test.go +++ b/x/compute/internal/keeper/secret_contracts_test.go @@ -53,10 +53,14 @@ var testContracts = []TestContract{ // if codeID isn't 0, it will try to use that. Otherwise will take the contractAddress func testEncrypt(t *testing.T, keeper Keeper, ctx sdk.Context, contractAddress sdk.AccAddress, codeId uint64, msg []byte) ([]byte, error) { var hash []byte + var err error + if codeId != 0 { hash = keeper.GetCodeInfo(ctx, codeId).CodeHash } else { - hash = keeper.GetContractHash(ctx, contractAddress) + hash, err = keeper.GetContractHash(ctx, contractAddress) + require.NoError(t, err) + } if hash == nil { @@ -400,7 +404,10 @@ func queryHelperImpl( contractAddr sdk.AccAddress, input string, isErrorEncrypted bool, isV1Contract bool, gas uint64, wasmCallCount int64, ) (string, cosmwasm.StdError) { - hashStr := hex.EncodeToString(keeper.GetContractHash(ctx, contractAddr)) + hash, err := keeper.GetContractHash(ctx, contractAddr) + require.NoError(t, err) + + hashStr := hex.EncodeToString(hash) msg := types.SecretMsg{ CodeHash: []byte(hashStr), @@ -473,7 +480,10 @@ func execHelperMultipleCoinsImpl( contractAddress sdk.AccAddress, txSender sdk.AccAddress, senderPrivKey crypto.PrivKey, execMsg string, isErrorEncrypted bool, isV1Contract bool, gas uint64, coins sdk.Coins, wasmCallCount int64, shouldSkipAttributes ...bool, ) ([]byte, sdk.Context, []byte, []ContractEvent, uint64, cosmwasm.StdError) { - hashStr := hex.EncodeToString(keeper.GetContractHash(ctx, contractAddress)) + hash, err := keeper.GetContractHash(ctx, contractAddress) + require.NoError(t, err) + + hashStr := hex.EncodeToString(hash) msg := types.SecretMsg{ CodeHash: []byte(hashStr), From 68b8a00cf0b0f630c0048ab9064d4c90b109153c Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Mon, 22 Aug 2022 21:58:39 +0300 Subject: [PATCH 35/44] Fix second optional null dref --- x/compute/internal/keeper/ibc_test.go | 12 +- x/compute/internal/keeper/keeper.go | 15 ++- x/compute/internal/keeper/keeper_test.go | 119 +++--------------- x/compute/internal/keeper/querier.go | 46 +++---- x/compute/internal/keeper/querier_test.go | 15 ++- .../internal/keeper/secret_contracts_test.go | 68 +++++++--- 6 files changed, 109 insertions(+), 166 deletions(-) diff --git a/x/compute/internal/keeper/ibc_test.go b/x/compute/internal/keeper/ibc_test.go index 9092290b1..c5a16e000 100644 --- a/x/compute/internal/keeper/ibc_test.go +++ b/x/compute/internal/keeper/ibc_test.go @@ -180,7 +180,9 @@ func ibcPacketReceiveHelper( internalPacket := packet if shouldEncryptMsg { - hashStr := hex.EncodeToString(keeper.GetContractHash(ctx, contractAddr)) + contractHash, err := keeper.GetContractHash(ctx, contractAddr) + require.NoError(t, err) + hashStr := hex.EncodeToString(contractHash) msg := types.SecretMsg{ CodeHash: []byte(hashStr), @@ -232,7 +234,9 @@ func ibcPacketAckHelper( var nonce []byte if shouldEncryptMsg { - hashStr := hex.EncodeToString(keeper.GetContractHash(ctx, contractAddr)) + contractHash, err := keeper.GetContractHash(ctx, contractAddr) + require.NoError(t, err) + hashStr := hex.EncodeToString(contractHash) msg := types.SecretMsg{ CodeHash: []byte(hashStr), @@ -287,7 +291,9 @@ func ibcPacketTimeoutHelper( var nonce []byte if shouldEncryptMsg { - hashStr := hex.EncodeToString(keeper.GetContractHash(ctx, contractAddr)) + contractHash, err := keeper.GetContractHash(ctx, contractAddr) + require.NoError(t, err) + hashStr := hex.EncodeToString(contractHash) msg := types.SecretMsg{ CodeHash: []byte(hashStr), diff --git a/x/compute/internal/keeper/keeper.go b/x/compute/internal/keeper/keeper.go index 261f2bda3..5f1b3a01f 100644 --- a/x/compute/internal/keeper/keeper.go +++ b/x/compute/internal/keeper/keeper.go @@ -716,14 +716,17 @@ func (k Keeper) GetContractHash(ctx sdk.Context, contractAddress sdk.AccAddress) contractInfo := k.GetContractInfo(ctx, contractAddress) if contractInfo == nil { - return nil, fmt.Errorf("failed to contract info for the following address: %s", contractAddress.String()) + return nil, fmt.Errorf("failed to get contract info for the following address: %s", contractAddress.String()) } codeId := contractInfo.CodeID - hash := k.GetCodeInfo(ctx, codeId).CodeHash + codeInfo, err := k.GetCodeInfo(ctx, codeId) + if err != nil { + return nil, err + } - return hash, nil + return codeInfo.CodeHash, nil } func (k Keeper) GetContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo { @@ -825,15 +828,15 @@ func (k Keeper) fixContractState(ctx sdk.Context, contractAddress sdk.AccAddress return nil } -func (k Keeper) GetCodeInfo(ctx sdk.Context, codeID uint64) *types.CodeInfo { +func (k Keeper) GetCodeInfo(ctx sdk.Context, codeID uint64) (types.CodeInfo, error) { store := ctx.KVStore(k.storeKey) var codeInfo types.CodeInfo codeInfoBz := store.Get(types.GetCodeKey(codeID)) if codeInfoBz == nil { - return nil + return types.CodeInfo{}, fmt.Errorf("failed to get code info for code id %d", codeID) } k.cdc.MustUnmarshal(codeInfoBz, &codeInfo) - return &codeInfo + return codeInfo, nil } func (k Keeper) containsCodeInfo(ctx sdk.Context, codeID uint64) bool { diff --git a/x/compute/internal/keeper/keeper_test.go b/x/compute/internal/keeper/keeper_test.go index 30c431d06..8b69ed572 100644 --- a/x/compute/internal/keeper/keeper_test.go +++ b/x/compute/internal/keeper/keeper_test.go @@ -89,104 +89,6 @@ func TestCreate(t *testing.T) { require.Equal(t, wasmCode, storedCode) } -/* -func TestCreateStoresInstantiatePermission(t *testing.T) { - wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm") - require.NoError(t, err) - var ( - deposit = sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - myAddr = bytes.Repeat([]byte{1}, sdk.AddrLen) - ) - - specs := map[string]struct { - srcPermission types.AccessType - expInstConf types.AccessConfig - }{ - "default": { - srcPermission: types.DefaultParams().DefaultInstantiatePermission, - expInstConf: types.AllowEverybody, - }, - "everybody": { - srcPermission: types.Everybody, - expInstConf: types.AllowEverybody, - }, - "nobody": { - srcPermission: types.Nobody, - expInstConf: types.AllowNobody, - }, - "onlyAddress with matching address": { - srcPermission: types.OnlyAddress, - expInstConf: types.AccessConfig{Type: types.OnlyAddress, Address: myAddr}, - }, - } - for msg, spec := range specs { - t.Run(msg, func(t *testing.T) { - tempDir, err := ioutil.TempDir("", "wasm") - require.NoError(t, err) - defer os.RemoveAll(tempDir) - - ctx, keepers := CreateTestInput(t, false, tempDir, SupportedFeatures, nil, nil) - accKeeper, keeper := keepers.AccountKeeper, keepers.WasmKeeper - fundAccounts(ctx, accKeeper, myAddr, deposit) - - codeID, err := keeper.Create(ctx, myAddr, wasmCode, "https://github.com/CosmWasm/wasmd/blob/master/x/wasm/testdata/escrow.wasm", "any/builder:tag") - require.NoError(t, err) - - codeInfo := keeper.GetCodeInfo(ctx, codeID) - require.NotNil(t, codeInfo) - assert.True(t, spec.expInstConf.Equals(codeInfo.InstantiateConfig), "got %#v", codeInfo.InstantiateConfig) - }) - } -} - -func TestCreateWithParamPermissions(t *testing.T) { - tempDir, err := ioutil.TempDir("", "wasm") - require.NoError(t, err) - defer os.RemoveAll(tempDir) - ctx, keepers := CreateTestInput(t, false, tempDir, SupportedFeatures, nil, nil) - accKeeper, keeper := keepers.AccountKeeper, keepers.WasmKeeper - - deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := CreateFakeFundedAccount(ctx, accKeeper, deposit) - otherAddr := CreateFakeFundedAccount(ctx, accKeeper, deposit) - - wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm") - require.NoError(t, err) - - specs := map[string]struct { - srcPermission types.AccessConfig - expError *sdkerrors.Error - }{ - "default": { - srcPermission: types.DefaultUploadAccess, - }, - "everybody": { - srcPermission: types.AllowEverybody, - }, - "nobody": { - srcPermission: types.AllowNobody, - expError: sdkerrors.ErrUnauthorized, - }, - "onlyAddress with matching address": { - srcPermission: types.OnlyAddress.With(creator), - }, - "onlyAddress with non matching address": { - srcPermission: types.OnlyAddress.With(otherAddr), - expError: sdkerrors.ErrUnauthorized, - }, - } - for msg, spec := range specs { - t.Run(msg, func(t *testing.T) { - _, err := keeper.Create(ctx, creator, wasmCode, "https://github.com/CosmWasm/wasmd/blob/master/x/wasm/testdata/escrow.wasm", "any/builder:tag") - require.True(t, spec.expError.Is(err), err) - if spec.expError != nil { - return - } - }) - } -} -*/ - func TestCreateDuplicate(t *testing.T) { encodingConfig := MakeEncodingConfig() var transferPortSource types.ICS20TransferPortSource @@ -340,7 +242,10 @@ func TestInstantiate(t *testing.T) { initMsgBz, err := json.Marshal(initMsg) require.NoError(t, err) - key := keeper.GetCodeInfo(ctx, contractID).CodeHash + codeInfo, err := keeper.GetCodeInfo(ctx, contractID) + require.NoError(t, err) + + key := codeInfo.CodeHash msg := types.SecretMsg{ CodeHash: []byte(hex.EncodeToString(key)), @@ -595,8 +500,10 @@ func TestExecute(t *testing.T) { } initMsgBz, err := json.Marshal(initMsg) - key := keeper.GetCodeInfo(ctx, contractID).CodeHash - // keyStr := hex.EncodeToString(key) + codeInfo, err := keeper.GetCodeInfo(ctx, contractID) + require.NoError(t, err) + + key := codeInfo.CodeHash msg := types.SecretMsg{ CodeHash: []byte(hex.EncodeToString(key)), @@ -651,8 +558,7 @@ func TestExecute(t *testing.T) { initMsgBz = []byte(`{"release":{}}`) - key = keeper.GetCodeInfo(ctx, contractID).CodeHash - // keyStr := hex.EncodeToString(key) + require.NoError(t, err) msg = types.SecretMsg{ CodeHash: []byte(hex.EncodeToString(key)), @@ -880,7 +786,10 @@ func TestExecuteWithCpuLoop(t *testing.T) { initMsgBz, err := json.Marshal(initMsg) require.NoError(t, err) - hash := keeper.GetCodeInfo(ctx, contractID).CodeHash + codeInfo, err := keeper.GetCodeInfo(ctx, contractID) + require.NoError(t, err) + + hash := codeInfo.CodeHash msg := types.SecretMsg{ CodeHash: []byte(hex.EncodeToString(hash)), @@ -918,7 +827,7 @@ func TestExecuteWithCpuLoop(t *testing.T) { codeHash, err := keeper.GetContractHash(ctx, addr) require.NoError(t, err) - + codeHashStr := hex.EncodeToString(codeHash) msg2 := types.SecretMsg{ diff --git a/x/compute/internal/keeper/querier.go b/x/compute/internal/keeper/querier.go index 5a30da3cf..1e14d66b0 100644 --- a/x/compute/internal/keeper/querier.go +++ b/x/compute/internal/keeper/querier.go @@ -233,17 +233,18 @@ func queryCode(ctx sdk.Context, codeID uint64, keeper Keeper) (*types.QueryCodeR if codeID == 0 { return nil, nil } - res := keeper.GetCodeInfo(ctx, codeID) - if res == nil { - // nil, nil leads to 404 in rest handler + + codeInfo, err := keeper.GetCodeInfo(ctx, codeID) + if err != nil { return nil, nil } + info := types.CodeInfoResponse{ CodeID: codeID, - Creator: res.Creator, - DataHash: res.CodeHash, - Source: res.Source, - Builder: res.Builder, + Creator: codeInfo.Creator, + DataHash: codeInfo.CodeHash, + Source: codeInfo.Source, + Builder: codeInfo.Builder, } code, err := keeper.GetByteCode(ctx, codeID) @@ -269,30 +270,6 @@ func queryCodeList(ctx sdk.Context, keeper Keeper) ([]types.CodeInfoResponse, er return info, nil } -/* -func queryContractHistory(ctx sdk.Context, bech string, keeper Keeper) ([]byte, error) { - contractAddr, err := sdk.AccAddressFromBech32(bech) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, err.Error()) - } - entries := keeper.GetContractHistory(ctx, contractAddr) - if entries == nil { - // nil, nil leads to 404 in rest handler - return nil, nil - } - // redact response - for i := range entries { - entries[i].Updated = nil - } - - bz, err := json.MarshalIndent(entries, "", " ") - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) - } - return bz, nil -} -*/ - func queryContractAddress(ctx sdk.Context, label string, keeper Keeper) (sdk.AccAddress, error) { res := keeper.GetContractAddress(ctx, label) if res == nil { @@ -317,5 +294,10 @@ func queryContractHash(ctx sdk.Context, address sdk.AccAddress, keeper Keeper) ( return nil, nil } - return keeper.GetCodeInfo(ctx, res.CodeID).CodeHash, nil + codeInfo, err := keeper.GetCodeInfo(ctx, res.CodeID) + if err != nil { + return nil, nil + } + + return codeInfo.CodeHash, nil } diff --git a/x/compute/internal/keeper/querier_test.go b/x/compute/internal/keeper/querier_test.go index e92d834a3..aaddbd5b0 100644 --- a/x/compute/internal/keeper/querier_test.go +++ b/x/compute/internal/keeper/querier_test.go @@ -48,7 +48,10 @@ func TestQueryContractLabel(t *testing.T) { initMsgBz, err := json.Marshal(initMsg) require.NoError(t, err) - hash := keeper.GetCodeInfo(ctx, contractID).CodeHash + codeInfo, err := keeper.GetCodeInfo(ctx, contractID) + require.NoError(t, err) + + hash := codeInfo.CodeHash msg := types.SecretMsg{ CodeHash: []byte(hex.EncodeToString(hash)), @@ -149,7 +152,10 @@ func TestQueryContractState(t *testing.T) { initMsgBz, err := json.Marshal(initMsg) require.NoError(t, err) - key := keeper.GetCodeInfo(ctx, contractID).CodeHash + codeInfo, err := keeper.GetCodeInfo(ctx, contractID) + require.NoError(t, err) + + key := codeInfo.CodeHash keyStr := hex.EncodeToString(key) msg := types.SecretMsg{ @@ -290,7 +296,10 @@ func TestListContractByCodeOrdering(t *testing.T) { initMsgBz, err := json.Marshal(initMsg) require.NoError(t, err) - key := keeper.GetCodeInfo(ctx, codeID).CodeHash + codeInfo, err := keeper.GetCodeInfo(ctx, codeID) + require.NoError(t, err) + + key := codeInfo.CodeHash keyStr := hex.EncodeToString(key) msg := types.SecretMsg{ diff --git a/x/compute/internal/keeper/secret_contracts_test.go b/x/compute/internal/keeper/secret_contracts_test.go index 03caffbf0..50f833e46 100644 --- a/x/compute/internal/keeper/secret_contracts_test.go +++ b/x/compute/internal/keeper/secret_contracts_test.go @@ -56,7 +56,10 @@ func testEncrypt(t *testing.T, keeper Keeper, ctx sdk.Context, contractAddress s var err error if codeId != 0 { - hash = keeper.GetCodeInfo(ctx, codeId).CodeHash + codeInfo, err := keeper.GetCodeInfo(ctx, codeId) + require.NoError(t, err) + + hash = codeInfo.CodeHash } else { hash, err = keeper.GetContractHash(ctx, contractAddress) require.NoError(t, err) @@ -85,7 +88,10 @@ func uploadCode(ctx sdk.Context, t *testing.T, keeper Keeper, wasmPath string, w codeID, err := keeper.Create(ctx, walletA, wasmCode, "", "") require.NoError(t, err) - codeHash := hex.EncodeToString(keeper.GetCodeInfo(ctx, codeID).CodeHash) + codeInfo, err := keeper.GetCodeInfo(ctx, codeID) + require.NoError(t, err) + + codeHash := hex.EncodeToString(codeInfo.CodeHash) return codeID, codeHash } @@ -547,7 +553,10 @@ func initHelperImpl( codeID uint64, creator sdk.AccAddress, creatorPrivKey crypto.PrivKey, initMsg string, isErrorEncrypted bool, isV1Contract bool, gas uint64, wasmCallCount int64, sentFunds sdk.Coins, shouldSkipAttributes ...bool, ) ([]byte, sdk.Context, sdk.AccAddress, []ContractEvent, cosmwasm.StdError) { - hashStr := hex.EncodeToString(keeper.GetCodeInfo(ctx, codeID).CodeHash) + codeInfo, err := keeper.GetCodeInfo(ctx, codeID) + require.NoError(t, err) + + hashStr := hex.EncodeToString(codeInfo.CodeHash) msg := types.SecretMsg{ CodeHash: []byte(hashStr), @@ -3700,7 +3709,6 @@ func TestSendFunds(t *testing.T) { originType, destinationType := callTypes[0], callTypes[1] t.Run(originType+"->"+destinationType, func(t *testing.T) { - alreadyTested := make(map[string]bool) alreadyTested["useruser->useruser"] = true // we are only testing contracts here for _, currentSubjects := range multisetsFrom(testContracts, 2) { @@ -3945,7 +3953,9 @@ func TestWasmMsgStructure(t *testing.T) { require.NoError(t, err) toCodeID, err := keeper.Create(ctx, walletA, wasmCode, "", "") - toCodeHash := hex.EncodeToString(keeper.GetCodeInfo(ctx, toCodeID).CodeHash) + codeInfo, err := keeper.GetCodeInfo(ctx, toCodeID) + require.NoError(t, err) + toCodeHash := hex.EncodeToString(codeInfo.CodeHash) require.NoError(t, err) toAddress := sdk.AccAddress{} @@ -4114,7 +4124,9 @@ func TestV1InitV010ContractWithReply(t *testing.T) { v010CodeID, err := keeper.Create(ctx, walletA, wasmCode, "", "") require.NoError(t, err) - v010CodeHash := hex.EncodeToString(keeper.GetCodeInfo(ctx, v010CodeID).CodeHash) + codeInfo, err := keeper.GetCodeInfo(ctx, v010CodeID) + require.NoError(t, err) + v010CodeHash := hex.EncodeToString(codeInfo.CodeHash) _, _, contractAddress, _, _ := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, `{"counter":{"counter":10, "expires":100}}`, true, true, defaultGasForTests) msg := fmt.Sprintf(`{"init_v10":{"counter":80, "code_id":%d, "code_hash":"%s"}}`, v010CodeID, v010CodeHash) @@ -4143,7 +4155,9 @@ func TestV1ExecuteV010ContractWithReply(t *testing.T) { v010CodeID, err := keeper.Create(ctx, walletA, wasmCode, "", "") require.NoError(t, err) - v010CodeHash := hex.EncodeToString(keeper.GetCodeInfo(ctx, v010CodeID).CodeHash) + codeInfo, err := keeper.GetCodeInfo(ctx, v010CodeID) + require.NoError(t, err) + v010CodeHash := hex.EncodeToString(codeInfo.CodeHash) _, _, contractAddress, _, err := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, `{"nop":{}}`, true, true, defaultGasForTests) require.Empty(t, err) @@ -4167,7 +4181,9 @@ func TestV1InitV010ContractNoReply(t *testing.T) { v010CodeID, err := keeper.Create(ctx, walletA, wasmCode, "", "") require.NoError(t, err) - v010CodeHash := hex.EncodeToString(keeper.GetCodeInfo(ctx, v010CodeID).CodeHash) + codeInfo, err := keeper.GetCodeInfo(ctx, v010CodeID) + require.NoError(t, err) + v010CodeHash := hex.EncodeToString(codeInfo.CodeHash) _, _, contractAddress, _, _ := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, `{"counter":{"counter":10, "expires":100}}`, true, true, defaultGasForTests) msg := fmt.Sprintf(`{"init_v10_no_reply":{"counter":180, "code_id":%d, "code_hash":"%s"}}`, v010CodeID, v010CodeHash) @@ -4196,7 +4212,9 @@ func TestV1ExecuteV010ContractNoReply(t *testing.T) { v010CodeID, err := keeper.Create(ctx, walletA, wasmCode, "", "") require.NoError(t, err) - v010CodeHash := hex.EncodeToString(keeper.GetCodeInfo(ctx, v010CodeID).CodeHash) + codeInfo, err := keeper.GetCodeInfo(ctx, v010CodeID) + require.NoError(t, err) + v010CodeHash := hex.EncodeToString(codeInfo.CodeHash) _, _, contractAddress, _, err := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, `{"nop":{}}`, true, true, defaultGasForTests) require.Empty(t, err) @@ -4225,7 +4243,9 @@ func TestV1QueryV010Contract(t *testing.T) { v010CodeID, err := keeper.Create(ctx, walletA, wasmCode, "", "") require.NoError(t, err) - v010CodeHash := hex.EncodeToString(keeper.GetCodeInfo(ctx, v010CodeID).CodeHash) + codeInfo, err := keeper.GetCodeInfo(ctx, v010CodeID) + require.NoError(t, err) + v010CodeHash := hex.EncodeToString(codeInfo.CodeHash) _, _, contractAddress, _, err := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, `{"nop":{}}`, true, true, defaultGasForTests) require.Empty(t, err) @@ -4249,7 +4269,9 @@ func TestV1InitV010ContractWithReplyWithError(t *testing.T) { v010CodeID, err := keeper.Create(ctx, walletA, wasmCode, "", "") require.NoError(t, err) - v010CodeHash := hex.EncodeToString(keeper.GetCodeInfo(ctx, v010CodeID).CodeHash) + codeInfo, err := keeper.GetCodeInfo(ctx, v010CodeID) + require.NoError(t, err) + v010CodeHash := hex.EncodeToString(codeInfo.CodeHash) _, _, contractAddress, _, _ := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, `{"counter":{"counter":10, "expires":100}}`, true, true, defaultGasForTests) msg := fmt.Sprintf(`{"init_v10_with_error":{"code_id":%d, "code_hash":"%s"}}`, v010CodeID, v010CodeHash) @@ -4268,7 +4290,9 @@ func TestV1ExecuteV010ContractWithReplyWithError(t *testing.T) { v010CodeID, err := keeper.Create(ctx, walletA, wasmCode, "", "") require.NoError(t, err) - v010CodeHash := hex.EncodeToString(keeper.GetCodeInfo(ctx, v010CodeID).CodeHash) + codeInfo, err := keeper.GetCodeInfo(ctx, v010CodeID) + require.NoError(t, err) + v010CodeHash := hex.EncodeToString(codeInfo.CodeHash) _, _, contractAddress, _, err := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, `{"nop":{}}`, true, true, defaultGasForTests) require.Empty(t, err) @@ -4291,7 +4315,9 @@ func TestV1InitV010ContractNoReplyWithError(t *testing.T) { v010CodeID, err := keeper.Create(ctx, walletA, wasmCode, "", "") require.NoError(t, err) - v010CodeHash := hex.EncodeToString(keeper.GetCodeInfo(ctx, v010CodeID).CodeHash) + codeInfo, err := keeper.GetCodeInfo(ctx, v010CodeID) + require.NoError(t, err) + v010CodeHash := hex.EncodeToString(codeInfo.CodeHash) _, _, contractAddress, _, _ := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, `{"counter":{"counter":10, "expires":100}}`, true, true, defaultGasForTests) msg := fmt.Sprintf(`{"init_v10_no_reply_with_error":{"code_id":%d, "code_hash":"%s"}}`, v010CodeID, v010CodeHash) @@ -4311,7 +4337,9 @@ func TestV1ExecuteV010ContractNoReplyWithError(t *testing.T) { v010CodeID, err := keeper.Create(ctx, walletA, wasmCode, "", "") require.NoError(t, err) - v010CodeHash := hex.EncodeToString(keeper.GetCodeInfo(ctx, v010CodeID).CodeHash) + codeInfo, err := keeper.GetCodeInfo(ctx, v010CodeID) + require.NoError(t, err) + v010CodeHash := hex.EncodeToString(codeInfo.CodeHash) _, _, contractAddress, _, err := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, `{"nop":{}}`, true, true, defaultGasForTests) require.Empty(t, err) @@ -4335,7 +4363,9 @@ func TestV1QueryV010ContractWithError(t *testing.T) { v010CodeID, err := keeper.Create(ctx, walletA, wasmCode, "", "") require.NoError(t, err) - v010CodeHash := hex.EncodeToString(keeper.GetCodeInfo(ctx, v010CodeID).CodeHash) + codeInfo, err := keeper.GetCodeInfo(ctx, v010CodeID) + require.NoError(t, err) + v010CodeHash := hex.EncodeToString(codeInfo.CodeHash) _, _, contractAddress, _, err := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, `{"nop":{}}`, true, true, defaultGasForTests) require.Empty(t, err) @@ -5873,7 +5903,9 @@ func TestV1SendsLogsMixedWithV010WithoutReply(t *testing.T) { v010CodeID, err := keeper.Create(ctx, walletA, wasmCode, "", "") require.NoError(t, err) - v010CodeHash := hex.EncodeToString(keeper.GetCodeInfo(ctx, v010CodeID).CodeHash) + codeInfo, err := keeper.GetCodeInfo(ctx, v010CodeID) + require.NoError(t, err) + v010CodeHash := hex.EncodeToString(codeInfo.CodeHash) _, _, v010ContractAddress, _, err := initHelper(t, keeper, ctx, v010CodeID, walletA, privKeyA, `{"nop":{}}`, true, false, defaultGasForTests) require.Empty(t, err) @@ -5933,7 +5965,9 @@ func TestV1SendsLogsMixedWithV010WithReply(t *testing.T) { v010CodeID, err := keeper.Create(ctx, walletA, wasmCode, "", "") require.NoError(t, err) - v010CodeHash := hex.EncodeToString(keeper.GetCodeInfo(ctx, v010CodeID).CodeHash) + codeInfo, err := keeper.GetCodeInfo(ctx, v010CodeID) + require.NoError(t, err) + v010CodeHash := hex.EncodeToString(codeInfo.CodeHash) _, _, v010ContractAddress, _, err := initHelper(t, keeper, ctx, v010CodeID, walletA, privKeyA, `{"nop":{}}`, true, false, defaultGasForTests) require.Empty(t, err) From c87d153aebdca3d7e3db38960a278ba12cd5e320 Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Wed, 24 Aug 2022 17:14:05 +0300 Subject: [PATCH 36/44] Fix the majority of linting problems --- cmd/secretd/attestation.go | 20 ++++++++++--------- cmd/secretd/genaccounts.go | 6 ++---- go-cosmwasm/cmd/main.go | 6 +++++- go-cosmwasm/types/queries.go | 2 +- x/registration/client/cli/query.go | 2 +- x/registration/internal/keeper/keeper.go | 5 +++-- .../internal/keeper/querier_test.go | 1 + 7 files changed, 24 insertions(+), 18 deletions(-) diff --git a/cmd/secretd/attestation.go b/cmd/secretd/attestation.go index a9bc95ce9..5e3cd9175 100644 --- a/cmd/secretd/attestation.go +++ b/cmd/secretd/attestation.go @@ -17,7 +17,6 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/x/genutil" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" @@ -124,8 +123,7 @@ blockchain. Writes the certificate in DER format to ~/attestation_cert Args: cobra.MaximumNArgs(2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx := client.GetClientContextFromCmd(cmd) - depCdc := clientCtx.Codec - cdc := depCdc.(codec.Codec) + cdc := clientCtx.Codec serverCtx := server.GetServerContextFromCmd(cmd) config := serverCtx.Config @@ -175,8 +173,8 @@ blockchain. Writes the certificate in DER format to ~/attestation_cert return err } - fmt.Println(fmt.Sprintf("%s", hex.EncodeToString(pubkey))) - fmt.Println(fmt.Sprintf("%s", hex.EncodeToString(masterKey))) + fmt.Printf("%s\n", hex.EncodeToString(pubkey)) + fmt.Printf("%s\n", hex.EncodeToString(masterKey)) // sanity check - make sure the certificate we're using matches the generated key if hex.EncodeToString(pubkey) != hex.EncodeToString(masterKey) { @@ -239,7 +237,7 @@ func ParseCert() *cobra.Command { return err } - fmt.Println(fmt.Sprintf("0x%s", hex.EncodeToString(pubkey))) + fmt.Printf("0x%s\n", hex.EncodeToString(pubkey)) return nil }, } @@ -313,7 +311,7 @@ func HealthCheck() *cobra.Command { return fmt.Errorf("failed to start enclave. Enclave returned: %s", err) } - fmt.Println(fmt.Sprintf("SGX enclave health status: %s", res)) + fmt.Printf("SGX enclave health status: %s\n", res) return nil }, } @@ -491,7 +489,11 @@ Please report any issues with this command "certificate": "%s" }`, base64.StdEncoding.EncodeToString(cert))) - resp, err := http.Post(fmt.Sprintf(`%s`, regUrl), "application/json", bytes.NewBuffer(data)) + resp, err := http.Post(regUrl, "application/json", bytes.NewBuffer(data)) + if err != nil { + log.Fatalln(err) + } + defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) @@ -515,7 +517,7 @@ Please report any issues with this command } seed := details.Details.Value - log.Printf(fmt.Sprintf(`seed: %s`, seed)) + log.Printf(`seed: %s\n`, seed) if len(seed) > 2 { seed = seed[2:] diff --git a/cmd/secretd/genaccounts.go b/cmd/secretd/genaccounts.go index e4bf9b8c7..3ed771043 100644 --- a/cmd/secretd/genaccounts.go +++ b/cmd/secretd/genaccounts.go @@ -14,7 +14,6 @@ import ( "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/server" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/genutil" @@ -42,8 +41,7 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx := client.GetClientContextFromCmd(cmd) - depCdc := clientCtx.Codec - cdc := depCdc.(codec.Codec) + cdc := clientCtx.Codec serverCtx := server.GetServerContextFromCmd(cmd) config := serverCtx.Config @@ -161,7 +159,7 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa appState[authtypes.ModuleName] = authGenStateBz - bankGenState := banktypes.GetGenesisStateFromAppState(depCdc, appState) + bankGenState := banktypes.GetGenesisStateFromAppState(cdc, appState) bankGenState.Balances = append(bankGenState.Balances, balances) bankGenState.Balances = banktypes.SanitizeGenesisBalances(bankGenState.Balances) diff --git a/go-cosmwasm/cmd/main.go b/go-cosmwasm/cmd/main.go index 149d3ccf4..c088ecbaf 100644 --- a/go-cosmwasm/cmd/main.go +++ b/go-cosmwasm/cmd/main.go @@ -18,7 +18,11 @@ func main() { } fmt.Println("Loaded!") - os.MkdirAll("tmp", 0o755) + err = os.MkdirAll("tmp", 0o755) + if err != nil { + panic(err) + } + wasmer, err := wasm.NewWasmer("tmp", "staking", 0, 15) if err != nil { panic(err) diff --git a/go-cosmwasm/types/queries.go b/go-cosmwasm/types/queries.go index 90519e2a4..ffc7bb4c7 100644 --- a/go-cosmwasm/types/queries.go +++ b/go-cosmwasm/types/queries.go @@ -101,7 +101,7 @@ type StakingQuery struct { Validators *ValidatorsQuery `json:"validators,omitempty"` AllDelegations *AllDelegationsQuery `json:"all_delegations,omitempty"` Delegation *DelegationQuery `json:"delegation,omitempty"` - UnBondingDelegations *UnbondingDeletionsQuery `json:"unbonding_delegations, omitempty"` + UnBondingDelegations *UnbondingDeletionsQuery `json:"unbonding_delegations,omitempty"` BondedDenom *struct{} `json:"bonded_denom,omitempty"` } diff --git a/x/registration/client/cli/query.go b/x/registration/client/cli/query.go index e879ddc49..a9bcfb0a3 100644 --- a/x/registration/client/cli/query.go +++ b/x/registration/client/cli/query.go @@ -56,7 +56,7 @@ func GetCmdEncryptedSeed() *cobra.Command { if err != nil { return err } - fmt.Println(fmt.Sprintf("0x%s", hex.EncodeToString(res))) + fmt.Printf("0x%s\n", hex.EncodeToString(res)) return nil }, } diff --git a/x/registration/internal/keeper/keeper.go b/x/registration/internal/keeper/keeper.go index 3c49c55b1..f83c24b19 100644 --- a/x/registration/internal/keeper/keeper.go +++ b/x/registration/internal/keeper/keeper.go @@ -47,6 +47,9 @@ func InitializeNode(homeDir string, enclave EnclaveInterface) { // get PK from CLI // get encrypted master key byteValue, err := getFile(seedPath) + if err != nil { + panic(sdkerrors.Wrap(types.ErrSeedInitFailed, err.Error())) + } var seedCfg types.SeedConfig @@ -69,8 +72,6 @@ func InitializeNode(homeDir string, enclave EnclaveInterface) { if err != nil { panic(sdkerrors.Wrap(types.ErrSeedInitFailed, err.Error())) } - - return } func (k Keeper) RegisterNode(ctx sdk.Context, certificate ra.Certificate) ([]byte, error) { diff --git a/x/registration/internal/keeper/querier_test.go b/x/registration/internal/keeper/querier_test.go index 13034f152..51cac4e25 100644 --- a/x/registration/internal/keeper/querier_test.go +++ b/x/registration/internal/keeper/querier_test.go @@ -100,5 +100,6 @@ func TestNewQuerier(t *testing.T) { keeper.setMasterCertificate(ctx, types.MasterCertificate{Bytes: regInfo.Certificate}, types.MasterIoKeyId) binResult, err := querier(ctx, []string{QueryMasterCertificate}, abci.RequestQuery{Data: []byte("")}) + require.NoError(t, err) require.Equal(t, string(binResult), string(expectedSecretParams)) } From 0d91925cb0a9fbbfccb4da7aca198b3eab41fe1c Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Sun, 28 Aug 2022 11:23:45 +0300 Subject: [PATCH 37/44] Fix relay --- x/compute/internal/keeper/relay.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/compute/internal/keeper/relay.go b/x/compute/internal/keeper/relay.go index a60a928e0..7bb821966 100644 --- a/x/compute/internal/keeper/relay.go +++ b/x/compute/internal/keeper/relay.go @@ -94,7 +94,7 @@ func (k Keeper) OnOpenChannel( return "", sdkerrors.Wrap(err, "ibc-open-channel") } - res, err := k.ibcContractCall(ctx, contractAddress, msgBz, wasmTypes.HandleTypeIbcChannelConnect) + res, err := k.ibcContractCall(ctx, contractAddress, msgBz, wasmTypes.HandleTypeIbcChannelOpen) if err != nil { return "", sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } @@ -196,7 +196,7 @@ func (k Keeper) OnRecvPacket( return nil, sdkerrors.Wrap(err, "ibc-recv-packet") } - res, err := k.ibcContractCall(ctx, contractAddress, msgBz, wasmTypes.HandleTypeIbcChannelConnect) + res, err := k.ibcContractCall(ctx, contractAddress, msgBz, wasmTypes.HandleTypeIbcPacketReceive) if err != nil { return nil, sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } From 22a04de9e3fe1979cb3d216f08349b4f32a2ee3a Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Sun, 28 Aug 2022 16:45:01 +0300 Subject: [PATCH 38/44] New contract for ibc tests --- .vscode/settings.json | 1 + Makefile | 1 + .../internal/keeper/testdata/ibc/.gitignore | 13 + .../internal/keeper/testdata/ibc/Cargo.lock | 639 ++++++++++++++++++ .../internal/keeper/testdata/ibc/Cargo.toml | 44 ++ .../internal/keeper/testdata/ibc/Makefile | 8 + .../keeper/testdata/ibc/rust-toolchain | 1 + .../keeper/testdata/ibc/src/contract.rs | 36 + .../internal/keeper/testdata/ibc/src/lib.rs | 3 + .../internal/keeper/testdata/ibc/src/msg.rs | 16 + .../internal/keeper/testdata/ibc/src/state.rs | 16 + 11 files changed, 778 insertions(+) create mode 100644 x/compute/internal/keeper/testdata/ibc/.gitignore create mode 100644 x/compute/internal/keeper/testdata/ibc/Cargo.lock create mode 100644 x/compute/internal/keeper/testdata/ibc/Cargo.toml create mode 100644 x/compute/internal/keeper/testdata/ibc/Makefile create mode 100644 x/compute/internal/keeper/testdata/ibc/rust-toolchain create mode 100644 x/compute/internal/keeper/testdata/ibc/src/contract.rs create mode 100644 x/compute/internal/keeper/testdata/ibc/src/lib.rs create mode 100644 x/compute/internal/keeper/testdata/ibc/src/msg.rs create mode 100644 x/compute/internal/keeper/testdata/ibc/src/state.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 45d553f7d..4f88206e5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,6 +4,7 @@ "./cosmwasm/enclaves/Cargo.toml", "./x/compute/internal/keeper/testdata/v1-sanity-contract/Cargo.toml", "./x/compute/internal/keeper/testdata/test-contract/Cargo.toml", + "./x/compute/internal/keeper/testdata/ibc/Cargo.toml", "./cosmwasm/enclaves/shared/cosmwasm-v1-types/Cargo.toml", "./cosmwasm/enclaves/shared/cosmwasm-v010-types/Cargo.toml" ], diff --git a/Makefile b/Makefile index 4b9793536..fdab5964d 100644 --- a/Makefile +++ b/Makefile @@ -348,6 +348,7 @@ build-test-contract: # sudo apt install -y binaryen $(MAKE) -C ./x/compute/internal/keeper/testdata/test-contract $(MAKE) -C ./x/compute/internal/keeper/testdata/v1-sanity-contract + $(MAKE) -C ./x/compute/internal/keeper/testdata/ibc-contract prep-go-tests: build-test-contract diff --git a/x/compute/internal/keeper/testdata/ibc/.gitignore b/x/compute/internal/keeper/testdata/ibc/.gitignore new file mode 100644 index 000000000..0e24e33d5 --- /dev/null +++ b/x/compute/internal/keeper/testdata/ibc/.gitignore @@ -0,0 +1,13 @@ +# Build results +/target +*.wasm + +# Text file backups +**/*.rs.bk + +# macOS +.DS_Store + +# IDEs +*.iml +.idea diff --git a/x/compute/internal/keeper/testdata/ibc/Cargo.lock b/x/compute/internal/keeper/testdata/ibc/Cargo.lock new file mode 100644 index 000000000..cd7e008a5 --- /dev/null +++ b/x/compute/internal/keeper/testdata/ibc/Cargo.lock @@ -0,0 +1,639 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "base64ct" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bdca834647821e0b13d9539a8634eb62d3501b6b6c2cec1722786ee6671b851" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-oid" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" + +[[package]] +name = "cosmwasm-crypto" +version = "1.0.0" +source = "git+https://github.com/scrtlabs/cosmwasm?branch=secret#e69869a8565e4e2318bdbfd3711fd1ceda0db209" +dependencies = [ + "digest", + "ed25519-zebra", + "k256", + "rand_core 0.6.3", + "thiserror", +] + +[[package]] +name = "cosmwasm-derive" +version = "1.0.0" +source = "git+https://github.com/scrtlabs/cosmwasm?branch=secret#e69869a8565e4e2318bdbfd3711fd1ceda0db209" +dependencies = [ + "syn", +] + +[[package]] +name = "cosmwasm-std" +version = "1.0.0" +source = "git+https://github.com/scrtlabs/cosmwasm?branch=secret#e69869a8565e4e2318bdbfd3711fd1ceda0db209" +dependencies = [ + "base64", + "cosmwasm-crypto", + "cosmwasm-derive", + "forward_ref", + "schemars", + "serde", + "serde-json-wasm 0.4.1", + "thiserror", + "uint", +] + +[[package]] +name = "cosmwasm-storage" +version = "1.0.0" +source = "git+https://github.com/scrtlabs/cosmwasm?branch=secret#e69869a8565e4e2318bdbfd3711fd1ceda0db209" +dependencies = [ + "cosmwasm-std", + "serde", +] + +[[package]] +name = "cpufeatures" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +dependencies = [ + "generic-array", + "rand_core 0.6.3", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" +dependencies = [ + "byteorder", + "digest", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "der" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +dependencies = [ + "const-oid", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "dyn-clone" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d07a982d1fb29db01e5a59b1918e03da4df7297eaeee7686ac45542fd4e59c8" + +[[package]] +name = "ecdsa" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0d69ae62e0ce582d56380743515fefaf1a8c70cec685d9677636d7e30ae9dc9" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "ed25519-zebra" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403ef3e961ab98f0ba902771d29f842058578bb1ce7e3c59dad5a6a93e784c69" +dependencies = [ + "curve25519-dalek", + "hex", + "rand_core 0.6.3", + "serde", + "sha2", + "thiserror", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b477563c2bfed38a3b7a60964c49e058b2510ad3f12ba3483fd8f62c2306d6" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "ff", + "generic-array", + "group", + "rand_core 0.6.3", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "ff" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "131655483be284720a17d74ff97592b8e76576dc25563148601df2d7c9080924" +dependencies = [ + "rand_core 0.6.3", + "subtle", +] + +[[package]] +name = "forward_ref" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "group" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89" +dependencies = [ + "ff", + "rand_core 0.6.3", + "subtle", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac", + "digest", +] + +[[package]] +name = "ibc" +version = "0.0.1" +dependencies = [ + "cosmwasm-std", + "cosmwasm-storage", + "schemars", + "secp256k1", + "serde", + "serde-json-wasm 0.2.3", +] + +[[package]] +name = "itoa" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" + +[[package]] +name = "k256" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sec1", + "sha2", +] + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "pkcs8" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" +dependencies = [ + "der", + "spki", + "zeroize", +] + +[[package]] +name = "proc-macro2" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom 0.2.7", +] + +[[package]] +name = "rfc6979" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525" +dependencies = [ + "crypto-bigint", + "hmac", + "zeroize", +] + +[[package]] +name = "ryu" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" + +[[package]] +name = "schemars" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1847b767a3d62d95cbf3d8a9f0e421cf57a0d8aa4f411d4b16525afb0284d4ed" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af4d7e1b012cb3d9129567661a63755ea4b8a7386d339dc945ae187e403c6743" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn", +] + +[[package]] +name = "sec1" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" +dependencies = [ + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d03ceae636d0fed5bae6a7f4f664354c5f4fcedf6eef053fef17e49f837d0a" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957da2573cde917463ece3570eab4a0b3f19de6f1646cde62e6fd3868f566036" +dependencies = [ + "cc", +] + +[[package]] +name = "serde" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-json-wasm" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120bad73306616e91acd7ceed522ba96032a51cffeef3cc813de7f367df71e37" +dependencies = [ + "serde", +] + +[[package]] +name = "serde-json-wasm" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + +[[package]] +name = "signature" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788" +dependencies = [ + "digest", + "rand_core 0.6.3", +] + +[[package]] +name = "spki" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "uint" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-ident" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "zeroize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" diff --git a/x/compute/internal/keeper/testdata/ibc/Cargo.toml b/x/compute/internal/keeper/testdata/ibc/Cargo.toml new file mode 100644 index 000000000..f9427e903 --- /dev/null +++ b/x/compute/internal/keeper/testdata/ibc/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "ibc" +version = "0.0.1" +authors = ["Enigma "] +edition = "2018" +description = "A Test contract intended to use in system tests for the Secret Netowrk" +license = "MIT" +exclude = [ + # Those files are cosmwasm-opt artifacts. You might want to commit them for convenience but they should not be part of the source code publication. + "ibc.wasm", +] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +crate-type = ["cdylib", "rlib"] + +[profile.release] +opt-level = 3 +debug = false +rpath = false +lto = true +debug-assertions = false +codegen-units = 1 +panic = 'abort' +incremental = false +overflow-checks = true + +[features] +default = [] +backtraces = ["cosmwasm-std/backtraces"] +with_floats = [] +stargate = [] +ibc3 = ["stargate"] + +[dependencies] +cosmwasm-std = { git = "https://github.com/scrtlabs/cosmwasm", branch = "secret", features=["stargate"] } +cosmwasm-storage = { git = "https://github.com/scrtlabs/cosmwasm", branch = "secret" } +schemars = "0.8.1" +serde = { version = "1.0.114", default-features = false, features = [ + "derive", + "alloc" +] } +serde-json-wasm = "0.2.1" +secp256k1 = "0.20.3" diff --git a/x/compute/internal/keeper/testdata/ibc/Makefile b/x/compute/internal/keeper/testdata/ibc/Makefile new file mode 100644 index 000000000..b6bb9953e --- /dev/null +++ b/x/compute/internal/keeper/testdata/ibc/Makefile @@ -0,0 +1,8 @@ +all: src/contract.rs src/lib.rs src/msg.rs src/state.rs Cargo.toml Cargo.lock + rustup target add wasm32-unknown-unknown + RUSTFLAGS='-C link-arg=-s' cargo build --release --target wasm32-unknown-unknown + cp ./target/wasm32-unknown-unknown/release/ibc.wasm ./contract.wasm + +clean: + cargo clean + -rm -f ./contract.wasm \ No newline at end of file diff --git a/x/compute/internal/keeper/testdata/ibc/rust-toolchain b/x/compute/internal/keeper/testdata/ibc/rust-toolchain new file mode 100644 index 000000000..a7efc46ca --- /dev/null +++ b/x/compute/internal/keeper/testdata/ibc/rust-toolchain @@ -0,0 +1 @@ +1.61 \ No newline at end of file diff --git a/x/compute/internal/keeper/testdata/ibc/src/contract.rs b/x/compute/internal/keeper/testdata/ibc/src/contract.rs new file mode 100644 index 000000000..771e99267 --- /dev/null +++ b/x/compute/internal/keeper/testdata/ibc/src/contract.rs @@ -0,0 +1,36 @@ +use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use crate::state::{count, count_read}; +use cosmwasm_std::{ + entry_point, Binary, Deps, DepsMut, Env, IbcChannelOpenMsg, MessageInfo, Response, StdResult, +}; + +#[entry_point] +pub fn instantiate( + _deps: DepsMut, + _env: Env, + _info: MessageInfo, + _msg: InstantiateMsg, +) -> StdResult { + Ok(Response::default()) +} + +#[entry_point] +pub fn execute( + _deps: DepsMut, + _env: Env, + _info: MessageInfo, + _msg: ExecuteMsg, +) -> StdResult { + Ok(Response::default()) +} + +#[entry_point] +pub fn query(deps: Deps, _env: Env, _msg: QueryMsg) -> StdResult { + Ok(count_read(deps.storage).load()?.to_be_bytes().into()) +} + +#[entry_point] +pub fn ibc_channel_open(deps: DepsMut, _env: Env, _msg: IbcChannelOpenMsg) -> StdResult<()> { + count(deps.storage).save(&1)?; + Ok(()) +} diff --git a/x/compute/internal/keeper/testdata/ibc/src/lib.rs b/x/compute/internal/keeper/testdata/ibc/src/lib.rs new file mode 100644 index 000000000..04ade40ed --- /dev/null +++ b/x/compute/internal/keeper/testdata/ibc/src/lib.rs @@ -0,0 +1,3 @@ +pub mod contract; +pub mod msg; +pub mod state; \ No newline at end of file diff --git a/x/compute/internal/keeper/testdata/ibc/src/msg.rs b/x/compute/internal/keeper/testdata/ibc/src/msg.rs new file mode 100644 index 000000000..0a45c67d3 --- /dev/null +++ b/x/compute/internal/keeper/testdata/ibc/src/msg.rs @@ -0,0 +1,16 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum InstantiateMsg { + Init {}, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum ExecuteMsg {} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum QueryMsg {} diff --git a/x/compute/internal/keeper/testdata/ibc/src/state.rs b/x/compute/internal/keeper/testdata/ibc/src/state.rs new file mode 100644 index 000000000..58b97292f --- /dev/null +++ b/x/compute/internal/keeper/testdata/ibc/src/state.rs @@ -0,0 +1,16 @@ +use cosmwasm_std::{Storage}; +use cosmwasm_storage::{ + singleton, singleton_read, ReadonlySingleton, Singleton, +}; + +pub const COUNT_KEY: &[u8] = b"count"; + +pub fn count(storage: &mut dyn Storage) -> Singleton { + singleton(storage, COUNT_KEY) +} + +pub fn count_read(storage: &dyn Storage) -> ReadonlySingleton { + singleton_read(storage, COUNT_KEY) +} + + From 16455f03e49a48ac4df36547086930bb8f17c4fe Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Sun, 28 Aug 2022 16:45:34 +0300 Subject: [PATCH 39/44] New ibc test --- x/compute/internal/keeper/ibc_test.go | 37 +++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/x/compute/internal/keeper/ibc_test.go b/x/compute/internal/keeper/ibc_test.go index c5a16e000..26fa0aad1 100644 --- a/x/compute/internal/keeper/ibc_test.go +++ b/x/compute/internal/keeper/ibc_test.go @@ -2,6 +2,8 @@ package keeper import ( "encoding/hex" + "encoding/json" + "math" "testing" crypto "github.com/cosmos/cosmos-sdk/crypto/types" @@ -60,7 +62,7 @@ func ibcChannelConnectHelper( func ibcChannelOpenHelper( t *testing.T, keeper Keeper, ctx sdk.Context, - contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, msg string, + contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, gas uint64, shouldSendOpenTry bool, channel v1types.IBCChannel, ) (string, cosmwasm.StdError) { // create new ctx with the same storage and a gas limit @@ -105,7 +107,7 @@ func ibcChannelOpenHelper( func ibcChannelCloseHelper( t *testing.T, keeper Keeper, ctx sdk.Context, - contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, msg string, + contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, gas uint64, shouldSendCloseConfirn bool, channel v1types.IBCChannel, ) cosmwasm.StdError { // create new ctx with the same storage and a gas limit @@ -173,7 +175,7 @@ func createIBCPacket(src v1types.IBCEndpoint, dest v1types.IBCEndpoint, sequence func ibcPacketReceiveHelper( t *testing.T, keeper Keeper, ctx sdk.Context, - contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, msg string, + contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, shouldEncryptMsg bool, gas uint64, packet v1types.IBCPacket, ) ([]byte, cosmwasm.StdError) { var nonce []byte @@ -228,7 +230,7 @@ func ibcPacketReceiveHelper( func ibcPacketAckHelper( t *testing.T, keeper Keeper, ctx sdk.Context, - contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, msg string, + contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, shouldEncryptMsg bool, gas uint64, originalPacket v1types.IBCPacket, ack []byte, ) cosmwasm.StdError { var nonce []byte @@ -285,7 +287,7 @@ func ibcPacketAckHelper( func ibcPacketTimeoutHelper( t *testing.T, keeper Keeper, ctx sdk.Context, - contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, msg string, + contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, shouldEncryptMsg bool, gas uint64, originalPacket v1types.IBCPacket, ) cosmwasm.StdError { var nonce []byte @@ -336,3 +338,28 @@ func ibcPacketTimeoutHelper( return cosmwasm.StdError{} } + +func TestIBCChannelOpen(t *testing.T) { + ctx, keeper, codeID, _, walletA, privKeyA, _, _ := setupTest(t, "./testdata/ibc/contract.wasm", sdk.NewCoins()) + + _, _, contractAddress, _, err := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, `{"init":{}}`, true, true, defaultGasForTests) + require.Empty(t, err) + + ibcChannel := v1types.IBCChannel{ + Endpoint: createIBCEndpoint(PortIDForContract(contractAddress), "channel.0"), + CounterpartyEndpoint: createIBCEndpoint(PortIDForContract(contractAddress), "channel.1"), + Order: v1types.Unordered, + Version: "1", + ConnectionID: "1", + } + _, err = ibcChannelOpenHelper(t, keeper, ctx, contractAddress, privKeyA, defaultGasForTests, false, ibcChannel) + require.Empty(t, err) + + queryRes, err := queryHelper(t, keeper, ctx, contractAddress, `{}`, true, true, math.MaxUint64) + require.Empty(t, err) + + var resp uint64 + e := json.Unmarshal([]byte(queryRes), &resp) + require.NoError(t, e) + require.Equal(t, uint64(1), resp) +} From 488cf00b4b6abb34700a3e1f659d0dba7ef712e5 Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Sun, 28 Aug 2022 16:45:43 +0300 Subject: [PATCH 40/44] Fix ibc --- app/app.go | 2 +- .../src/contract_operations.rs | 17 ++++---- .../enclaves/shared/contract-engine/src/io.rs | 41 ++++++++++++++++++- .../enclaves/shared/cosmos-types/src/types.rs | 6 +++ .../shared/cosmwasm-v1-types/src/ibc.rs | 9 ++++ go-cosmwasm/cmd/main.go | 2 +- go-cosmwasm/lib.go | 24 ++++++++--- go-cosmwasm/types/v1/ibc.go | 5 +++ x/compute/internal/keeper/keeper_test.go | 2 +- 9 files changed, 89 insertions(+), 19 deletions(-) diff --git a/app/app.go b/app/app.go index 326fddf9f..81ec0ec9e 100644 --- a/app/app.go +++ b/app/app.go @@ -419,7 +419,7 @@ func NewSecretNetworkApp( // The last arguments can contain custom message handlers, and custom query handlers, // if we want to allow any custom callbacks - supportedFeatures := "staking" + supportedFeatures := "staking,stargate,ibc3" app.computeKeeper = compute.NewKeeper( appCodec, diff --git a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs index 7231f010a..2b0da1b0d 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs @@ -723,14 +723,6 @@ pub fn handle( EnclaveError::FailedToDeserialize })?; - let canonical_sender_address = CanonicalAddr::from_human(&env_v010.message.sender).map_err(|err| { - warn!( - "init got an error while trying to deserialize env_v010.message.sender from bech32 string to bytes {:?}: {}", - env_v010.message.sender, err - ); - EnclaveError::FailedToDeserialize - })?; - let contract_key = extract_contract_key(&env_v010)?; if !validate_contract_key(&contract_key, &canonical_contract_address, &contract_code) { @@ -822,6 +814,15 @@ pub fn handle( ); if was_msg_encrypted { + + let canonical_sender_address = CanonicalAddr::from_human(&env_v010.message.sender).map_err(|err| { + warn!( + "handle got an error while trying to deserialize env_v010.message.sender from bech32 string to bytes {:?}: {}", + env_v010.message.sender, err + ); + EnclaveError::FailedToDeserialize + })?; + output = encrypt_output( output, &secret_msg, diff --git a/cosmwasm/enclaves/shared/contract-engine/src/io.rs b/cosmwasm/enclaves/shared/contract-engine/src/io.rs index 3ef7b0cdb..2e6db65e4 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/io.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/io.rs @@ -60,6 +60,10 @@ pub enum RawWasmOutput { #[serde(rename = "Ok")] ok: enclave_cosmwasm_v1_types::ibc::IbcReceiveResponse, }, + OkIBCOpenChannel { + #[serde(rename = "Ok")] + ok: enclave_cosmwasm_v1_types::ibc::IbcChannelOpenResponse, + }, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] @@ -80,7 +84,7 @@ pub struct V1WasmOutput { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct IBCOutput { - #[serde(rename = "Ok")] + #[serde(rename = "ok")] pub ok: Option, #[serde(rename = "Err")] pub err: Option, @@ -88,12 +92,20 @@ pub struct IBCOutput { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct IBCReceiveOutput { - #[serde(rename = "Ok")] + #[serde(rename = "ok")] pub ok: Option, #[serde(rename = "Err")] pub err: Option, } +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct IBCOpenChannelOutput { + #[serde(rename = "ok")] + pub ok: Option, + #[serde(rename = "Err")] + pub err: Option, +} + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct QueryOutput { #[serde(rename = "Ok")] @@ -108,6 +120,7 @@ pub struct WasmOutput { pub v1: Option, pub ibc_basic: Option, pub ibc_packet_receive: Option, + pub ibc_open_channel: Option, pub query: Option, pub internal_reply_enclave_sig: Option, pub internal_msg_id: Option, @@ -193,6 +206,7 @@ pub fn finalize_raw_output(raw_output: RawWasmOutput, is_query_output: bool) -> v1: None, ibc_basic: None, ibc_packet_receive: None, + ibc_open_channel: None, query: Some(QueryOutput { ok: None, err: Some(err), @@ -209,6 +223,7 @@ pub fn finalize_raw_output(raw_output: RawWasmOutput, is_query_output: bool) -> v1: None, ibc_basic: None, ibc_packet_receive: None, + ibc_open_channel: None, query: None, internal_reply_enclave_sig, internal_msg_id, @@ -227,6 +242,7 @@ pub fn finalize_raw_output(raw_output: RawWasmOutput, is_query_output: bool) -> v1: None, ibc_basic: None, ibc_packet_receive: None, + ibc_open_channel: None, query: None, internal_reply_enclave_sig, internal_msg_id, @@ -243,6 +259,7 @@ pub fn finalize_raw_output(raw_output: RawWasmOutput, is_query_output: bool) -> }), ibc_basic: None, ibc_packet_receive: None, + ibc_open_channel: None, query: None, internal_reply_enclave_sig, internal_msg_id, @@ -252,6 +269,7 @@ pub fn finalize_raw_output(raw_output: RawWasmOutput, is_query_output: bool) -> v1: None, ibc_basic: None, ibc_packet_receive: None, + ibc_open_channel: None, query: Some(QueryOutput { ok: Some(ok), err: None, @@ -267,6 +285,7 @@ pub fn finalize_raw_output(raw_output: RawWasmOutput, is_query_output: bool) -> ok: Some(ok), }), ibc_packet_receive: None, + ibc_open_channel: None, query: None, internal_reply_enclave_sig: None, internal_msg_id: None, @@ -279,6 +298,23 @@ pub fn finalize_raw_output(raw_output: RawWasmOutput, is_query_output: bool) -> err: None, ok: Some(ok), }), + ibc_open_channel: None, + query: None, + internal_reply_enclave_sig: None, + internal_msg_id: None, + }, + RawWasmOutput::OkIBCOpenChannel { ok } => WasmOutput { + v010: None, + v1: None, + ibc_basic: None, + ibc_packet_receive: None, + ibc_open_channel: Some(IBCOpenChannelOutput { + err: None, + ok: match ok { + Some(o) => Some(o.version), + None => None, + }, + }), query: None, internal_reply_enclave_sig: None, internal_msg_id: None, @@ -637,6 +673,7 @@ pub fn encrypt_output( &reply_params, )?)?; } + RawWasmOutput::OkIBCOpenChannel { ok: _ } => {} }; let final_output = finalize_raw_output(output, is_query_output); diff --git a/cosmwasm/enclaves/shared/cosmos-types/src/types.rs b/cosmwasm/enclaves/shared/cosmos-types/src/types.rs index 39cf0dc5e..5a1b58bb5 100644 --- a/cosmwasm/enclaves/shared/cosmos-types/src/types.rs +++ b/cosmwasm/enclaves/shared/cosmos-types/src/types.rs @@ -159,6 +159,12 @@ impl HandleType { match value { 0 => Ok(HandleType::HANDLE_TYPE_EXECUTE), 1 => Ok(HandleType::HANDLE_TYPE_REPLY), + 2 => Ok(HandleType::HANDLE_TYPE_IBC_CHANNEL_OPEN), + 3 => Ok(HandleType::HANDLE_TYPE_IBC_CHANNEL_CONNECT), + 4 => Ok(HandleType::HANDLE_TYPE_IBC_CHANNEL_CLOSE), + 5 => Ok(HandleType::HANDLE_TYPE_IBC_PACKET_RECEIVE), + 6 => Ok(HandleType::HANDLE_TYPE_IBC_PACKET_ACK), + 7 => Ok(HandleType::HANDLE_TYPE_IBC_PACKET_TIMEOUT), _ => { error!("unrecognized handle type: {}", value); Err(EnclaveError::FailedToDeserialize) diff --git a/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs b/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs index cbb02ec70..751bb4caf 100644 --- a/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs +++ b/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs @@ -32,6 +32,15 @@ pub struct IbcChannel { pub connection_id: String, } +/// This serializes either as "null" or a JSON object. +pub type IbcChannelOpenResponse = Option; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +pub struct Ibc3ChannelOpenResponse { + /// We can set the channel version to a different one than we were called with + pub version: String, +} + /// This is the return value for the majority of the ibc handlers. /// That are able to dispatch messages / events on their own, /// but have no meaningful return value to the calling code. diff --git a/go-cosmwasm/cmd/main.go b/go-cosmwasm/cmd/main.go index c088ecbaf..ac130b90b 100644 --- a/go-cosmwasm/cmd/main.go +++ b/go-cosmwasm/cmd/main.go @@ -23,7 +23,7 @@ func main() { panic(err) } - wasmer, err := wasm.NewWasmer("tmp", "staking", 0, 15) + wasmer, err := wasm.NewWasmer("tmp", "staking,stargate,ibc3", 0, 15) if err != nil { panic(err) } diff --git a/go-cosmwasm/lib.go b/go-cosmwasm/lib.go index 970da2127..dd28a2ddf 100644 --- a/go-cosmwasm/lib.go +++ b/go-cosmwasm/lib.go @@ -86,12 +86,13 @@ func (w *Wasmer) GetCode(code CodeID) (WasmCode, error) { // This struct helps us to distinguish between v0.10 contract response and v1 contract response type ContractExecResponse struct { - V1 *V1ContractExecResponse `json:"v1,omitempty"` - V010 *V010ContractExecResponse `json:"v010,omitempty"` - InternaReplyEnclaveSig []byte `json:"internal_reply_enclave_sig"` - InternalMsgId []byte `json:"internal_msg_id"` - IBCBasic *v1types.IBCBasicResult `json:"ibc_basic,omitempty"` - IBCPacketReceive *v1types.IBCReceiveResult `json:"ibc_packet_receive,omitempty"` + V1 *V1ContractExecResponse `json:"v1,omitempty"` + V010 *V010ContractExecResponse `json:"v010,omitempty"` + InternaReplyEnclaveSig []byte `json:"internal_reply_enclave_sig"` + InternalMsgId []byte `json:"internal_msg_id"` + IBCBasic *v1types.IBCBasicResult `json:"ibc_basic,omitempty"` + IBCPacketReceive *v1types.IBCReceiveResult `json:"ibc_packet_receive,omitempty"` + IBCChannelOpen *v1types.IBCOpenChannelResult `json:"ibc_open_channel,omitempty"` } type V010ContractExecResponse struct { @@ -334,6 +335,17 @@ func (w *Wasmer) Execute( } } + if resp.IBCChannelOpen != nil { + if resp.IBCChannelOpen.Err != nil { + return nil, gasUsed, fmt.Errorf("%+v", resp.IBCChannelOpen.Err) + } else if resp.IBCChannelOpen.Ok != nil { + // ibc_channel_open actually returns no data + return resp.IBCChannelOpen.Ok, gasUsed, nil + } else { + return nil, gasUsed, fmt.Errorf("cannot parse IBCChannelOpen response: %+v", resp) + } + } + return nil, gasUsed, fmt.Errorf("handle: cannot detect response type (v0.10 or v1)") } diff --git a/go-cosmwasm/types/v1/ibc.go b/go-cosmwasm/types/v1/ibc.go index efe2a462d..78099b298 100644 --- a/go-cosmwasm/types/v1/ibc.go +++ b/go-cosmwasm/types/v1/ibc.go @@ -252,6 +252,11 @@ type IBCReceiveResult struct { Err *types.StdError `json:"Err,omitempty"` } +type IBCOpenChannelResult struct { + Ok *string `json:"ok,omitempty"` + Err *types.StdError `json:"Err,omitempty"` +} + // IBCReceiveResponse defines the return value on packet response processing. // This "success" case should be returned even in application-level errors, // Where the Acknowledgement bytes contain an encoded error message to be returned to diff --git a/x/compute/internal/keeper/keeper_test.go b/x/compute/internal/keeper/keeper_test.go index 8b69ed572..82e9d22f7 100644 --- a/x/compute/internal/keeper/keeper_test.go +++ b/x/compute/internal/keeper/keeper_test.go @@ -22,7 +22,7 @@ import ( reg "github.com/enigmampc/SecretNetwork/x/registration" ) -const SupportedFeatures = "staking" +const SupportedFeatures = "staking,stargate,ibc3" var wasmCtx = wasmUtils.WASMContext{ TestKeyPairPath: "/tmp/id_tx_io.json", From 171e7699f1fe3ff9220133b81f3dc99e162e1f7b Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Sun, 28 Aug 2022 17:24:14 +0300 Subject: [PATCH 41/44] Now channel open works --- Makefile | 2 +- cosmwasm/enclaves/shared/contract-engine/src/io.rs | 2 +- x/compute/internal/keeper/ibc_test.go | 8 ++------ x/compute/internal/keeper/relay.go | 8 ++------ x/compute/internal/keeper/testdata/ibc/src/contract.rs | 6 ++++-- x/compute/internal/keeper/testdata/ibc/src/msg.rs | 4 +++- 6 files changed, 13 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index fdab5964d..e06ef7e31 100644 --- a/Makefile +++ b/Makefile @@ -348,7 +348,7 @@ build-test-contract: # sudo apt install -y binaryen $(MAKE) -C ./x/compute/internal/keeper/testdata/test-contract $(MAKE) -C ./x/compute/internal/keeper/testdata/v1-sanity-contract - $(MAKE) -C ./x/compute/internal/keeper/testdata/ibc-contract + $(MAKE) -C ./x/compute/internal/keeper/testdata/ibc prep-go-tests: build-test-contract diff --git a/cosmwasm/enclaves/shared/contract-engine/src/io.rs b/cosmwasm/enclaves/shared/contract-engine/src/io.rs index 2e6db65e4..6d747cddf 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/io.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/io.rs @@ -312,7 +312,7 @@ pub fn finalize_raw_output(raw_output: RawWasmOutput, is_query_output: bool) -> err: None, ok: match ok { Some(o) => Some(o.version), - None => None, + None => Some("".to_string()), }, }), query: None, diff --git a/x/compute/internal/keeper/ibc_test.go b/x/compute/internal/keeper/ibc_test.go index 26fa0aad1..8dd249175 100644 --- a/x/compute/internal/keeper/ibc_test.go +++ b/x/compute/internal/keeper/ibc_test.go @@ -2,7 +2,6 @@ package keeper import ( "encoding/hex" - "encoding/json" "math" "testing" @@ -355,11 +354,8 @@ func TestIBCChannelOpen(t *testing.T) { _, err = ibcChannelOpenHelper(t, keeper, ctx, contractAddress, privKeyA, defaultGasForTests, false, ibcChannel) require.Empty(t, err) - queryRes, err := queryHelper(t, keeper, ctx, contractAddress, `{}`, true, true, math.MaxUint64) + queryRes, err := queryHelper(t, keeper, ctx, contractAddress, `{"q":{}}`, true, true, math.MaxUint64) require.Empty(t, err) - var resp uint64 - e := json.Unmarshal([]byte(queryRes), &resp) - require.NoError(t, e) - require.Equal(t, uint64(1), resp) + require.Equal(t, "1", queryRes) } diff --git a/x/compute/internal/keeper/relay.go b/x/compute/internal/keeper/relay.go index 7bb821966..4497b95c9 100644 --- a/x/compute/internal/keeper/relay.go +++ b/x/compute/internal/keeper/relay.go @@ -100,12 +100,8 @@ func (k Keeper) OnOpenChannel( } switch resp := res.(type) { - case *v1types.IBC3ChannelOpenResponse: - if resp != nil { - return resp.Version, nil - } else { - return "", nil - } + case *string: + return *resp, nil default: return "", sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("ibc-open-channel: cannot cast res to IBC3ChannelOpenResponse: %+v", res)) } diff --git a/x/compute/internal/keeper/testdata/ibc/src/contract.rs b/x/compute/internal/keeper/testdata/ibc/src/contract.rs index 771e99267..3f892ac72 100644 --- a/x/compute/internal/keeper/testdata/ibc/src/contract.rs +++ b/x/compute/internal/keeper/testdata/ibc/src/contract.rs @@ -1,7 +1,8 @@ use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; use crate::state::{count, count_read}; use cosmwasm_std::{ - entry_point, Binary, Deps, DepsMut, Env, IbcChannelOpenMsg, MessageInfo, Response, StdResult, + entry_point, to_binary, Binary, Deps, DepsMut, Env, IbcChannelOpenMsg, MessageInfo, Response, + StdResult, }; #[entry_point] @@ -26,7 +27,8 @@ pub fn execute( #[entry_point] pub fn query(deps: Deps, _env: Env, _msg: QueryMsg) -> StdResult { - Ok(count_read(deps.storage).load()?.to_be_bytes().into()) + let answer = count_read(deps.storage).load()?; + Ok(to_binary(&answer)?) } #[entry_point] diff --git a/x/compute/internal/keeper/testdata/ibc/src/msg.rs b/x/compute/internal/keeper/testdata/ibc/src/msg.rs index 0a45c67d3..3d5253b37 100644 --- a/x/compute/internal/keeper/testdata/ibc/src/msg.rs +++ b/x/compute/internal/keeper/testdata/ibc/src/msg.rs @@ -13,4 +13,6 @@ pub enum ExecuteMsg {} #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] -pub enum QueryMsg {} +pub enum QueryMsg { + Q {}, +} From 9f7097267d00ad3530a81a7ac2ee73e1d3dc9cb2 Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Sun, 28 Aug 2022 18:21:26 +0300 Subject: [PATCH 42/44] IBC Channel Connect WIP --- x/compute/internal/keeper/ibc_test.go | 70 ++++++++++- .../internal/keeper/testdata/ibc/Cargo.toml | 2 +- .../keeper/testdata/ibc/src/contract.rs | 115 ++++++++++++++++-- .../internal/keeper/testdata/ibc/src/msg.rs | 4 +- 4 files changed, 179 insertions(+), 12 deletions(-) diff --git a/x/compute/internal/keeper/ibc_test.go b/x/compute/internal/keeper/ibc_test.go index 8dd249175..b67838c60 100644 --- a/x/compute/internal/keeper/ibc_test.go +++ b/x/compute/internal/keeper/ibc_test.go @@ -16,7 +16,7 @@ import ( func ibcChannelConnectHelper( t *testing.T, keeper Keeper, ctx sdk.Context, - contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, msg string, + contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, gas uint64, shouldSendOpenAck bool, channel v1types.IBCChannel, ) cosmwasm.StdError { // create new ctx with the same storage and a gas limit @@ -351,11 +351,77 @@ func TestIBCChannelOpen(t *testing.T) { Version: "1", ConnectionID: "1", } - _, err = ibcChannelOpenHelper(t, keeper, ctx, contractAddress, privKeyA, defaultGasForTests, false, ibcChannel) + + version, err := ibcChannelOpenHelper(t, keeper, ctx, contractAddress, privKeyA, defaultGasForTests, false, ibcChannel) require.Empty(t, err) + require.Equal(t, version, "ibc-v1") queryRes, err := queryHelper(t, keeper, ctx, contractAddress, `{"q":{}}`, true, true, math.MaxUint64) require.Empty(t, err) require.Equal(t, "1", queryRes) } + +func TestIBCChannelOpenTry(t *testing.T) { + ctx, keeper, codeID, _, walletA, privKeyA, _, _ := setupTest(t, "./testdata/ibc/contract.wasm", sdk.NewCoins()) + + _, _, contractAddress, _, err := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, `{"init":{}}`, true, true, defaultGasForTests) + require.Empty(t, err) + + ibcChannel := v1types.IBCChannel{ + Endpoint: createIBCEndpoint(PortIDForContract(contractAddress), "channel.0"), + CounterpartyEndpoint: createIBCEndpoint(PortIDForContract(contractAddress), "channel.1"), + Order: v1types.Unordered, + Version: "1", + ConnectionID: "1", + } + + version, err := ibcChannelOpenHelper(t, keeper, ctx, contractAddress, privKeyA, defaultGasForTests, true, ibcChannel) + require.Empty(t, err) + require.Equal(t, version, "ibc-v1") + + queryRes, err := queryHelper(t, keeper, ctx, contractAddress, `{"q":{}}`, true, true, math.MaxUint64) + require.Empty(t, err) + + require.Equal(t, "2", queryRes) +} + +func TestIBCChannelConnect(t *testing.T) { + ctx, keeper, codeID, _, walletA, privKeyA, _, _ := setupTest(t, "./testdata/ibc/contract.wasm", sdk.NewCoins()) + + _, _, contractAddress, _, err := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, `{"init":{}}`, true, true, defaultGasForTests) + require.Empty(t, err) + + for _, test := range []struct { + connectionID string + output string + hasAttributes bool + hasEvents bool + }{ + { + connectionID: "1", + output: ``, + isSuccuss: true, + balancesBefore: "5000assaf,200000denom 5000assaf,5000denom", + balancesAfter: "4998assaf,199998denom 5000assaf,5002denom", + }, + } { + t.Run(test.description, func(t *testing.T) { + ibcChannel := v1types.IBCChannel{ + Endpoint: createIBCEndpoint(PortIDForContract(contractAddress), "channel.0"), + CounterpartyEndpoint: createIBCEndpoint(PortIDForContract(contractAddress), "channel.1"), + Order: v1types.Unordered, + Version: "1", + ConnectionID: "1", + } + + err = ibcChannelConnectHelper(t, keeper, ctx, contractAddress, privKeyA, defaultGasForTests, false, ibcChannel) + require.Empty(t, err) + + queryRes, err := queryHelper(t, keeper, ctx, contractAddress, `{"q":{}}`, true, true, math.MaxUint64) + require.Empty(t, err) + + require.Equal(t, "3", queryRes) + }) + } +} diff --git a/x/compute/internal/keeper/testdata/ibc/Cargo.toml b/x/compute/internal/keeper/testdata/ibc/Cargo.toml index f9427e903..f1271dd60 100644 --- a/x/compute/internal/keeper/testdata/ibc/Cargo.toml +++ b/x/compute/internal/keeper/testdata/ibc/Cargo.toml @@ -33,7 +33,7 @@ stargate = [] ibc3 = ["stargate"] [dependencies] -cosmwasm-std = { git = "https://github.com/scrtlabs/cosmwasm", branch = "secret", features=["stargate"] } +cosmwasm-std = { git = "https://github.com/scrtlabs/cosmwasm", branch = "secret", features=["stargate", "ibc3"] } cosmwasm-storage = { git = "https://github.com/scrtlabs/cosmwasm", branch = "secret" } schemars = "0.8.1" serde = { version = "1.0.114", default-features = false, features = [ diff --git a/x/compute/internal/keeper/testdata/ibc/src/contract.rs b/x/compute/internal/keeper/testdata/ibc/src/contract.rs index 3f892ac72..8d24233eb 100644 --- a/x/compute/internal/keeper/testdata/ibc/src/contract.rs +++ b/x/compute/internal/keeper/testdata/ibc/src/contract.rs @@ -1,10 +1,14 @@ use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; use crate::state::{count, count_read}; use cosmwasm_std::{ - entry_point, to_binary, Binary, Deps, DepsMut, Env, IbcChannelOpenMsg, MessageInfo, Response, - StdResult, + coins, entry_point, to_binary, Binary, CosmosMsg, Deps, DepsMut, Env, Event, + Ibc3ChannelOpenResponse, IbcBasicResponse, IbcChannelConnectMsg, IbcChannelOpenMsg, + IbcChannelOpenResponse, MessageInfo, Reply, ReplyOn, Response, StdError, StdResult, SubMsg, + SubMsgResult, WasmMsg, }; +pub const IBC_APP_VERSION: &str = "ibc-v1"; + #[entry_point] pub fn instantiate( _deps: DepsMut, @@ -17,12 +21,14 @@ pub fn instantiate( #[entry_point] pub fn execute( - _deps: DepsMut, + deps: DepsMut, _env: Env, _info: MessageInfo, - _msg: ExecuteMsg, + msg: ExecuteMsg, ) -> StdResult { - Ok(Response::default()) + match msg { + ExecuteMsg::Increment { addition } => increment(deps, addition), + } } #[entry_point] @@ -32,7 +38,100 @@ pub fn query(deps: Deps, _env: Env, _msg: QueryMsg) -> StdResult { } #[entry_point] -pub fn ibc_channel_open(deps: DepsMut, _env: Env, _msg: IbcChannelOpenMsg) -> StdResult<()> { - count(deps.storage).save(&1)?; - Ok(()) +pub fn reply(deps: DepsMut, _env: Env, reply: Reply) -> StdResult { + match (reply.id, reply.result) { + (1, SubMsgResult::Err(_)) => Err(StdError::generic_err("Failed to inc")), + (1, SubMsgResult::Ok(_)) => { + increment(deps, 6)?; + Ok(Response::default()) + } + _ => Err(StdError::generic_err("invalid reply id or result")), + } +} + +#[entry_point] +pub fn ibc_channel_open( + deps: DepsMut, + _env: Env, + msg: IbcChannelOpenMsg, +) -> StdResult { + match msg { + IbcChannelOpenMsg::OpenInit { channel: _ } => count(deps.storage).save(&1)?, + IbcChannelOpenMsg::OpenTry { + channel: _, + counterparty_version: _, + } => count(deps.storage).save(&2)?, + _ => {} + } + + Ok(Some(Ibc3ChannelOpenResponse { + version: IBC_APP_VERSION.to_string(), + })) +} + +pub fn increment(deps: DepsMut, c: u64) -> StdResult { + let new_count = count_read(deps.storage).load()? + c; + count(deps.storage).save(&new_count)?; + + let mut resp = Response::default(); + resp.data = Some((new_count as u32).to_be_bytes().into()); + + Ok(resp) +} + +#[entry_point] +pub fn ibc_channel_connect( + deps: DepsMut, + env: Env, + msg: IbcChannelConnectMsg, +) -> StdResult { + match msg { + IbcChannelConnectMsg::OpenAck { + channel: _, + counterparty_version: _, + } => { + count(deps.storage).save(&3)?; + Ok(IbcBasicResponse::default()) + } + IbcChannelConnectMsg::OpenConfirm { channel } => { + let num: u64 = channel.connection_id.parse::().map_err(|err| { + StdError::generic_err(format!("Got an error from parsing: {:?}", err)) + })?; + + count(deps.storage).save(&(num + 4))?; + + match num { + 0 => Ok(IbcBasicResponse::default()), + 1 => Ok(IbcBasicResponse::new().add_submessage(SubMsg { + id: 1, + msg: CosmosMsg::Wasm(WasmMsg::Execute { + code_hash: env.contract.code_hash, + contract_addr: env.contract.address.into_string(), + msg: Binary::from("{\"increment\":{\"addition\":5}}".as_bytes().to_vec()), + funds: vec![], + }) + .into(), + reply_on: ReplyOn::Never, + gas_limit: None, + })), + 2 => Ok(IbcBasicResponse::new().add_submessage(SubMsg { + id: 1, + msg: CosmosMsg::Wasm(WasmMsg::Execute { + code_hash: env.contract.code_hash, + contract_addr: env.contract.address.into_string(), + msg: Binary::from("{\"increment\":{\"addition\":5}}".as_bytes().to_vec()), + funds: vec![], + }) + .into(), + reply_on: ReplyOn::Always, + gas_limit: None, + })), + 3 => Ok(IbcBasicResponse::new().add_attribute("attr1", "😗")), + 4 => Ok(IbcBasicResponse::new() + .add_event(Event::new("cyber1".to_string()).add_attribute("attr1", "🤯"))), + _ => Err(StdError::generic_err("Unsupported channel connect type")), + } + } + _ => Err(StdError::generic_err("Unsupported channel connect")), + } } diff --git a/x/compute/internal/keeper/testdata/ibc/src/msg.rs b/x/compute/internal/keeper/testdata/ibc/src/msg.rs index 3d5253b37..8b51690a7 100644 --- a/x/compute/internal/keeper/testdata/ibc/src/msg.rs +++ b/x/compute/internal/keeper/testdata/ibc/src/msg.rs @@ -9,7 +9,9 @@ pub enum InstantiateMsg { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] #[serde(rename_all = "snake_case")] -pub enum ExecuteMsg {} +pub enum ExecuteMsg { + Increment { addition: u64 }, +} #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] From 4c43604535641019a7f9d136398ada2448e835a9 Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Sun, 28 Aug 2022 23:32:05 +0300 Subject: [PATCH 43/44] Channel connect is now workins (Including submessages) --- .../src/contract_operations.rs | 34 ++- .../enclaves/shared/contract-engine/src/io.rs | 196 +++++++++++------- .../shared/cosmwasm-v1-types/src/ibc.rs | 14 ++ .../cosmwasm-v1-types/src/results/response.rs | 4 +- go-cosmwasm/lib.go | 1 + x/compute/internal/keeper/ibc_test.go | 121 +++++++++-- x/compute/internal/keeper/keeper.go | 1 + x/compute/internal/keeper/msg_dispatcher.go | 16 +- x/compute/internal/keeper/relay.go | 7 +- .../keeper/testdata/ibc/src/contract.rs | 8 +- 10 files changed, 294 insertions(+), 108 deletions(-) diff --git a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs index 2b0da1b0d..714ed49fe 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs @@ -30,7 +30,10 @@ use super::contract_validation::{ verify_params, ContractKey, }; use super::gas::WasmCosts; -use super::io::{encrypt_output, finalize_raw_output, RawWasmOutput}; +use super::io::{ + encrypt_output, finalize_raw_output, manipulate_callback_sig_for_plaintext, + set_all_logs_to_plaintext, +}; use super::module_cache::create_module_instance; use super::types::{IoNonce, SecretMessage}; use super::wasm::{ContractInstance, ContractOperation, Engine}; @@ -158,6 +161,7 @@ pub fn init( reply_params, &canonical_sender_address, false, + false, )?; Ok(output) @@ -361,7 +365,7 @@ pub fn parse_message( let decrypted_msg = secret_msg.msg.clone(); Ok(ParsedMessage { - should_validate_sig_info: true, + should_validate_sig_info: false, was_msg_encrypted: false, secret_msg, decrypted_msg, @@ -690,6 +694,18 @@ pub fn parse_message( }; } +pub fn is_ibc_msg(handle_type: HandleType) -> bool { + match handle_type { + HandleType::HANDLE_TYPE_EXECUTE | HandleType::HANDLE_TYPE_REPLY => false, + HandleType::HANDLE_TYPE_IBC_CHANNEL_OPEN + | HandleType::HANDLE_TYPE_IBC_CHANNEL_CONNECT + | HandleType::HANDLE_TYPE_IBC_CHANNEL_CLOSE + | HandleType::HANDLE_TYPE_IBC_PACKET_RECEIVE + | HandleType::HANDLE_TYPE_IBC_PACKET_ACK + | HandleType::HANDLE_TYPE_IBC_PACKET_TIMEOUT => true, + } +} + #[cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))] pub fn handle( context: Ctx, @@ -804,7 +820,7 @@ pub fn handle( // This wrapper is used to coalesce all errors in this block to one object // so we can `.map_err()` in one place for all of them let output = coalesce!(EnclaveError, { - let vec_ptr = engine.handle(env_ptr, msg_info_ptr, msg_ptr, parsed_handle_type)?; + let vec_ptr = engine.handle(env_ptr, msg_info_ptr, msg_ptr, parsed_handle_type.clone())?; let mut output = engine.extract_vector(vec_ptr)?; @@ -831,15 +847,14 @@ pub fn handle( reply_params, &canonical_sender_address, false, + is_ibc_msg(parsed_handle_type.clone()), )?; } else { - let raw_output: RawWasmOutput = serde_json::from_slice(&output).map_err(|err| { - warn!("got an error while trying to deserialize output bytes into json"); - trace!("output: {:?} error: {:?}", output, err); - EnclaveError::FailedToDeserialize - })?; + let mut raw_output = manipulate_callback_sig_for_plaintext(output)?; + set_all_logs_to_plaintext(&mut raw_output); - let finalized_output = finalize_raw_output(raw_output, false); + let finalized_output = finalize_raw_output(raw_output, false, is_ibc_msg(parsed_handle_type), false); + trace!("Wasm output for plaintext message is: {:?}", finalized_output); output = serde_json::to_vec(&finalized_output).map_err(|err| { debug!( @@ -942,6 +957,7 @@ pub fn query( None, // Not used for queries (Query response is not replied to the caller), &CanonicalAddr(Binary(Vec::new())), // Not used for queries (used only for replies) true, + false, )?; Ok(output) }) diff --git a/cosmwasm/enclaves/shared/contract-engine/src/io.rs b/cosmwasm/enclaves/shared/contract-engine/src/io.rs index 6d747cddf..f3251cf6d 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/io.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/io.rs @@ -6,7 +6,7 @@ use crate::contract_validation::ReplyParams; /// use super::types::{IoNonce, SecretMessage}; use enclave_cosmwasm_v010_types::encoding::Binary; -use enclave_cosmwasm_v010_types::types::{CanonicalAddr, Coin}; +use enclave_cosmwasm_v010_types::types::{CanonicalAddr, Coin, LogAttribute}; use enclave_cosmwasm_v1_types::results::{Event, Reply, ReplyOn, SubMsgResponse, SubMsgResult}; use enclave_ffi_types::EnclaveError; @@ -52,10 +52,6 @@ pub enum RawWasmOutput { internal_reply_enclave_sig: Option, internal_msg_id: Option, }, - OkIBCBasic { - #[serde(rename = "Ok")] - ok: enclave_cosmwasm_v1_types::ibc::IbcBasicResponse, - }, OkIBCPacketReceive { #[serde(rename = "Ok")] ok: enclave_cosmwasm_v1_types::ibc::IbcReceiveResponse, @@ -193,7 +189,12 @@ fn b64_encode(data: &[u8]) -> String { base64::encode(data) } -pub fn finalize_raw_output(raw_output: RawWasmOutput, is_query_output: bool) -> WasmOutput { +pub fn finalize_raw_output( + raw_output: RawWasmOutput, + is_query_output: bool, + is_ibc: bool, + is_msg_encrypted: bool, +) -> WasmOutput { return match raw_output { RawWasmOutput::Err { err, @@ -217,7 +218,10 @@ pub fn finalize_raw_output(raw_output: RawWasmOutput, is_query_output: bool) -> } else { WasmOutput { v010: Some(V010WasmOutput { - err: Some(err), + err: match is_msg_encrypted { + true => Some(err), + false => Some(json!({"generic_err":{"msg":err}})), + }, ok: None, }), v1: None, @@ -251,18 +255,37 @@ pub fn finalize_raw_output(raw_output: RawWasmOutput, is_query_output: bool) -> ok, internal_reply_enclave_sig, internal_msg_id, - } => WasmOutput { - v010: None, - v1: Some(V1WasmOutput { - err: None, - ok: Some(ok), - }), - ibc_basic: None, - ibc_packet_receive: None, - ibc_open_channel: None, - query: None, - internal_reply_enclave_sig, - internal_msg_id, + } => match is_ibc { + false => WasmOutput { + v010: None, + v1: Some(V1WasmOutput { + err: None, + ok: Some(ok), + }), + ibc_basic: None, + ibc_packet_receive: None, + ibc_open_channel: None, + query: None, + internal_reply_enclave_sig, + internal_msg_id, + }, + true => WasmOutput { + v010: None, + v1: None, + ibc_basic: Some(IBCOutput { + err: None, + ok: Some(enclave_cosmwasm_v1_types::ibc::IbcBasicResponse::new( + ok.messages, + ok.attributes, + ok.events, + )), + }), + ibc_packet_receive: None, + ibc_open_channel: None, + query: None, + internal_reply_enclave_sig, + internal_msg_id, + }, }, RawWasmOutput::QueryOkV010 { ok } | RawWasmOutput::QueryOkV1 { ok } => WasmOutput { v010: None, @@ -277,19 +300,6 @@ pub fn finalize_raw_output(raw_output: RawWasmOutput, is_query_output: bool) -> internal_reply_enclave_sig: None, internal_msg_id: None, }, - RawWasmOutput::OkIBCBasic { ok } => WasmOutput { - v010: None, - v1: None, - ibc_basic: Some(IBCOutput { - err: None, - ok: Some(ok), - }), - ibc_packet_receive: None, - ibc_open_channel: None, - query: None, - internal_reply_enclave_sig: None, - internal_msg_id: None, - }, RawWasmOutput::OkIBCPacketReceive { ok } => WasmOutput { v010: None, v1: None, @@ -322,6 +332,82 @@ pub fn finalize_raw_output(raw_output: RawWasmOutput, is_query_output: bool) -> }; } +pub fn manipulate_callback_sig_for_plaintext( + output: Vec, +) -> Result { + let mut raw_output: RawWasmOutput = serde_json::from_slice(&output).map_err(|err| { + warn!("got an error while trying to deserialize output bytes into json"); + trace!("output: {:?} error: {:?}", output, err); + EnclaveError::FailedToDeserialize + })?; + + match &mut raw_output { + RawWasmOutput::OkV1 { ok, .. } => { + for sub_msg in &mut ok.messages { + if let enclave_cosmwasm_v1_types::results::CosmosMsg::Wasm(wasm_msg) = + &mut sub_msg.msg + { + match wasm_msg { + enclave_cosmwasm_v1_types::results::WasmMsg::Execute { + callback_sig, + .. + } + | enclave_cosmwasm_v1_types::results::WasmMsg::Instantiate { + callback_sig, + .. + } => *callback_sig = Some(vec![]), + } + } + } + } + RawWasmOutput::OkIBCPacketReceive { ok } => { + for sub_msg in &mut ok.messages { + if let enclave_cosmwasm_v1_types::results::CosmosMsg::Wasm(wasm_msg) = + &mut sub_msg.msg + { + match wasm_msg { + enclave_cosmwasm_v1_types::results::WasmMsg::Execute { + callback_sig, + .. + } + | enclave_cosmwasm_v1_types::results::WasmMsg::Instantiate { + callback_sig, + .. + } => *callback_sig = Some(vec![]), + } + } + } + } + _ => {} + } + + Ok(raw_output) +} + +pub fn set_attributes_to_plaintext(attributes: &mut Vec) { + for attr in attributes { + attr.encrypted = false; + } +} + +pub fn set_all_logs_to_plaintext(raw_output: &mut RawWasmOutput) { + match raw_output { + RawWasmOutput::OkV1 { ok, .. } => { + set_attributes_to_plaintext(&mut ok.attributes); + for ev in &mut ok.events { + set_attributes_to_plaintext(&mut ev.attributes); + } + } + RawWasmOutput::OkIBCPacketReceive { ok } => { + set_attributes_to_plaintext(&mut ok.attributes); + for ev in &mut ok.events { + set_attributes_to_plaintext(&mut ev.attributes); + } + } + _ => {} + } +} + pub fn encrypt_output( output: Vec, secret_msg: &SecretMessage, @@ -330,6 +416,7 @@ pub fn encrypt_output( reply_params: Option, sender_addr: &CanonicalAddr, is_query_output: bool, + is_ibc_output: bool, ) -> Result, EnclaveError> { // When encrypting an output we might encrypt an output that is a reply to a caller contract (Via "Reply" endpoint). // Therefore if reply_recipient_contract_hash is not "None" we append it to any encrypted data besided submessages that are irrelevant for replies. @@ -353,10 +440,7 @@ pub fn encrypt_output( internal_msg_id, } => { let encrypted_err = encrypt_serializable(&encryption_key, err, &reply_params)?; - - // Putting the error inside a 'generic_err' envelope, so we can encrypt the error itself *err = json!({"generic_err":{"msg":encrypted_err}}); - let msg_id = match reply_params { Some(ref r) => { let encrypted_id = Binary::from_base64(&encrypt_preserialized_string( @@ -527,6 +611,11 @@ pub fn encrypt_output( } if let Some(data) = &mut ok.data { + if is_ibc_output { + warn!("IBC output should not containt any data"); + return Err(EnclaveError::InternalError); + } + *data = Binary::from_base64(&encrypt_serializable( &encryption_key, data, @@ -597,41 +686,6 @@ pub fn encrypt_output( None => None, // Not a reply, we don't need enclave sig } } - RawWasmOutput::OkIBCBasic { ok } => { - for sub_msg in &mut ok.messages { - if let enclave_cosmwasm_v1_types::results::CosmosMsg::Wasm(wasm_msg) = - &mut sub_msg.msg - { - encrypt_v1_wasm_msg( - wasm_msg, - &sub_msg.reply_on, - sub_msg.id, - secret_msg.nonce, - secret_msg.user_public_key, - contract_addr, - contract_hash, - )?; - - // The ID can be extracted from the encrypted wasm msg - // We don't encrypt it here to remain with the same type (u64) - sub_msg.id = 0; - } - } - - // v1: The attributes that will be emitted as part of a "wasm" event. - for attr in ok.attributes.iter_mut().filter(|attr| attr.encrypted) { - attr.key = encrypt_preserialized_string(&encryption_key, &attr.key, &None)?; - attr.value = encrypt_preserialized_string(&encryption_key, &attr.value, &None)?; - } - - // v1: Extra, custom events separate from the main wasm one. These will have "wasm-"" prepended to the type. - for event in ok.events.iter_mut() { - for attr in event.attributes.iter_mut().filter(|attr| attr.encrypted) { - attr.key = encrypt_preserialized_string(&encryption_key, &attr.key, &None)?; - attr.value = encrypt_preserialized_string(&encryption_key, &attr.value, &None)?; - } - } - } RawWasmOutput::OkIBCPacketReceive { ok } => { for sub_msg in &mut ok.messages { if let enclave_cosmwasm_v1_types::results::CosmosMsg::Wasm(wasm_msg) = @@ -676,7 +730,7 @@ pub fn encrypt_output( RawWasmOutput::OkIBCOpenChannel { ok: _ } => {} }; - let final_output = finalize_raw_output(output, is_query_output); + let final_output = finalize_raw_output(output, is_query_output, is_ibc_output, true); trace!("WasmOutput: {:?}", final_output); let encrypted_output = serde_json::to_vec(&final_output).map_err(|err| { diff --git a/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs b/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs index 751bb4caf..53956fce0 100644 --- a/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs +++ b/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/ibc.rs @@ -74,6 +74,20 @@ where pub events: Vec, } +impl IbcBasicResponse { + pub fn new( + messages: Vec>, + attributes: Vec, + events: Vec, + ) -> Self { + IbcBasicResponse { + messages, + attributes, + events, + } + } +} + // This defines the return value on packet response processing. // This "success" case should be returned even in application-level errors, // Where the acknowledgement bytes contain an encoded error message to be returned to diff --git a/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/results/response.rs b/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/results/response.rs index 1a6054a0c..fceff9804 100644 --- a/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/results/response.rs +++ b/cosmwasm/enclaves/shared/cosmwasm-v1-types/src/results/response.rs @@ -1,9 +1,9 @@ use serde::{Deserialize, Serialize}; use std::fmt; -use enclave_cosmwasm_v010_types::{encoding::Binary, types::LogAttribute}; +use enclave_cosmwasm_v010_types::{encoding::Binary, types::Empty, types::LogAttribute}; -use super::{Empty, Event, SubMsg}; +use super::{Event, SubMsg}; /// A response of a contract entry point, such as `instantiate` or `execute`. /// diff --git a/go-cosmwasm/lib.go b/go-cosmwasm/lib.go index dd28a2ddf..3add1592a 100644 --- a/go-cosmwasm/lib.go +++ b/go-cosmwasm/lib.go @@ -263,6 +263,7 @@ func (w *Wasmer) Execute( return nil, gasUsed, err } + fmt.Printf("LIORRR data %+v", data) var resp ContractExecResponse err = json.Unmarshal(data, &resp) diff --git a/x/compute/internal/keeper/ibc_test.go b/x/compute/internal/keeper/ibc_test.go index b67838c60..8d4edcd64 100644 --- a/x/compute/internal/keeper/ibc_test.go +++ b/x/compute/internal/keeper/ibc_test.go @@ -2,12 +2,14 @@ package keeper import ( "encoding/hex" + "fmt" "math" "testing" crypto "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" cosmwasm "github.com/enigmampc/SecretNetwork/go-cosmwasm/types" + v010cosmwasm "github.com/enigmampc/SecretNetwork/go-cosmwasm/types/v010" v1types "github.com/enigmampc/SecretNetwork/go-cosmwasm/types/v1" "github.com/enigmampc/SecretNetwork/x/compute/internal/types" "github.com/stretchr/testify/require" @@ -18,7 +20,7 @@ func ibcChannelConnectHelper( t *testing.T, keeper Keeper, ctx sdk.Context, contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, gas uint64, shouldSendOpenAck bool, channel v1types.IBCChannel, -) cosmwasm.StdError { +) (sdk.Context, []ContractEvent, cosmwasm.StdError) { // create new ctx with the same storage and a gas limit // this is to reset the event manager, so we won't get // events from past calls @@ -53,10 +55,13 @@ func ibcChannelConnectHelper( require.NotZero(t, gasMeter.GetWasmCounter(), err) if err != nil { - return cosmwasm.StdError{GenericErr: &cosmwasm.GenericErr{Msg: err.Error()}} + return ctx, nil, cosmwasm.StdError{GenericErr: &cosmwasm.GenericErr{Msg: err.Error()}} } - return cosmwasm.StdError{} + // wasmEvents comes from all the callbacks as well + wasmEvents := tryDecryptWasmEvents(ctx, []byte{}, true) + + return ctx, wasmEvents, cosmwasm.StdError{} } func ibcChannelOpenHelper( @@ -393,17 +398,60 @@ func TestIBCChannelConnect(t *testing.T) { require.Empty(t, err) for _, test := range []struct { + description string connectionID string output string + isSuccess bool hasAttributes bool hasEvents bool }{ { - connectionID: "1", - output: ``, - isSuccuss: true, - balancesBefore: "5000assaf,200000denom 5000assaf,5000denom", - balancesAfter: "4998assaf,199998denom 5000assaf,5002denom", + description: "Default", + connectionID: "0", + output: "4", + isSuccess: true, + hasAttributes: false, + hasEvents: false, + }, + { + description: "SubmessageNoReply", + connectionID: "1", + output: "10", + isSuccess: true, + hasAttributes: false, + hasEvents: false, + }, + { + description: "SubmessageWithReply", + connectionID: "2", + output: "17", + isSuccess: true, + hasAttributes: false, + hasEvents: false, + }, + { + description: "Attributes", + connectionID: "3", + output: "7", + isSuccess: true, + hasAttributes: true, + hasEvents: false, + }, + { + description: "Events", + connectionID: "4", + output: "8", + isSuccess: true, + hasAttributes: false, + hasEvents: true, + }, + { + description: "Error", + connectionID: "5", + output: "", + isSuccess: false, + hasAttributes: false, + hasEvents: false, }, } { t.Run(test.description, func(t *testing.T) { @@ -412,16 +460,57 @@ func TestIBCChannelConnect(t *testing.T) { CounterpartyEndpoint: createIBCEndpoint(PortIDForContract(contractAddress), "channel.1"), Order: v1types.Unordered, Version: "1", - ConnectionID: "1", + ConnectionID: test.connectionID, } - err = ibcChannelConnectHelper(t, keeper, ctx, contractAddress, privKeyA, defaultGasForTests, false, ibcChannel) - require.Empty(t, err) - - queryRes, err := queryHelper(t, keeper, ctx, contractAddress, `{"q":{}}`, true, true, math.MaxUint64) - require.Empty(t, err) - - require.Equal(t, "3", queryRes) + ctx, events, err := ibcChannelConnectHelper(t, keeper, ctx, contractAddress, privKeyA, defaultGasForTests, false, ibcChannel) + + if !test.isSuccess { + require.Contains(t, fmt.Sprintf("%+v", err), "Intentional") + } else { + require.Empty(t, err) + if test.hasAttributes { + require.Equal(t, + []ContractEvent{ + { + {Key: "contract_address", Value: contractAddress.String()}, + {Key: "attr1", Value: "😗"}, + }, + }, + events, + ) + } + + if test.hasEvents { + hadCyber1 := false + evts := ctx.EventManager().Events() + for _, e := range evts { + if e.Type == "wasm-cyber1" { + require.False(t, hadCyber1) + attrs, err := parseAndDecryptAttributes(e.Attributes, []byte{}, false) + require.Empty(t, err) + + require.Equal(t, + []v010cosmwasm.LogAttribute{ + {Key: "contract_address", Value: contractAddress.String()}, + {Key: "attr1", Value: "🤯"}, + }, + attrs, + ) + + hadCyber1 = true + } + } + + require.True(t, hadCyber1) + } + + queryRes, err := queryHelper(t, keeper, ctx, contractAddress, `{"q":{}}`, true, true, math.MaxUint64) + + require.Empty(t, err) + + require.Equal(t, test.output, queryRes) + } }) } } diff --git a/x/compute/internal/keeper/keeper.go b/x/compute/internal/keeper/keeper.go index 5f1b3a01f..2cb0b9968 100644 --- a/x/compute/internal/keeper/keeper.go +++ b/x/compute/internal/keeper/keeper.go @@ -885,6 +885,7 @@ func (k *Keeper) handleContractResponse( ogCosmosMessageVersion wasmTypes.CosmosMsgVersion, ) ([]byte, error) { events := types.ContractLogsToSdkEvents(logs, contractAddr) + ctx.EventManager().EmitEvents(events) if len(evts) > 0 { diff --git a/x/compute/internal/keeper/msg_dispatcher.go b/x/compute/internal/keeper/msg_dispatcher.go index 80486471f..17ef1d2ac 100644 --- a/x/compute/internal/keeper/msg_dispatcher.go +++ b/x/compute/internal/keeper/msg_dispatcher.go @@ -139,7 +139,19 @@ func (e UnsupportedRequest) Error() string { // Reply is encrypted only when it is a contract reply func isReplyEncrypted(msg v1wasmTypes.CosmosMsg, reply v1wasmTypes.Reply) bool { - return (msg.Wasm != nil) + if msg.Wasm == nil { + return false + } + + if msg.Wasm.Execute != nil { + return len(msg.Wasm.Execute.CallbackCodeHash) != 0 + } + + if msg.Wasm.Instantiate != nil { + return len(msg.Wasm.Instantiate.CallbackCodeHash) != 0 + } + + return true } // Issue #759 - we don't return error string for worries of non-determinism @@ -177,7 +189,7 @@ func (d MessageDispatcher) DispatchSubmessages(ctx sdk.Context, contractAddr sdk switch msg.ReplyOn { case v1wasmTypes.ReplySuccess, v1wasmTypes.ReplyError, v1wasmTypes.ReplyAlways, v1wasmTypes.ReplyNever: default: - return nil, sdkerrors.Wrap(types.ErrInvalid, "replyOn value") + return nil, sdkerrors.Wrap(types.ErrInvalid, "ReplyOn value") } // first, we build a sub-context which we can use inside the submessages diff --git a/x/compute/internal/keeper/relay.go b/x/compute/internal/keeper/relay.go index 4497b95c9..8fe04024d 100644 --- a/x/compute/internal/keeper/relay.go +++ b/x/compute/internal/keeper/relay.go @@ -132,10 +132,9 @@ func (k Keeper) OnConnectChannel( if err != nil { return sdkerrors.Wrap(types.ErrExecuteFailed, err.Error()) } - err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, msgBz, res) if err != nil { - sdkerrors.Wrap(err, "ibc-connect-channel") + return sdkerrors.Wrap(err, "ibc-connect-channel") } return nil } @@ -246,7 +245,7 @@ func (k Keeper) OnAckPacket( err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, msgBz, res) if err != nil { - sdkerrors.Wrap(err, "ibc-ack-packet") + return sdkerrors.Wrap(err, "ibc-ack-packet") } return nil } @@ -275,7 +274,7 @@ func (k Keeper) OnTimeoutPacket( err = k.parseThenHandleIBCBasicContractResponse(ctx, contractAddress, msgBz, res) if err != nil { - sdkerrors.Wrap(err, "ibc-timeout-packet") + return sdkerrors.Wrap(err, "ibc-timeout-packet") } return nil } diff --git a/x/compute/internal/keeper/testdata/ibc/src/contract.rs b/x/compute/internal/keeper/testdata/ibc/src/contract.rs index 8d24233eb..182027ba5 100644 --- a/x/compute/internal/keeper/testdata/ibc/src/contract.rs +++ b/x/compute/internal/keeper/testdata/ibc/src/contract.rs @@ -1,10 +1,9 @@ use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; use crate::state::{count, count_read}; use cosmwasm_std::{ - coins, entry_point, to_binary, Binary, CosmosMsg, Deps, DepsMut, Env, Event, - Ibc3ChannelOpenResponse, IbcBasicResponse, IbcChannelConnectMsg, IbcChannelOpenMsg, - IbcChannelOpenResponse, MessageInfo, Reply, ReplyOn, Response, StdError, StdResult, SubMsg, - SubMsgResult, WasmMsg, + entry_point, to_binary, Binary, CosmosMsg, Deps, DepsMut, Env, Event, Ibc3ChannelOpenResponse, + IbcBasicResponse, IbcChannelConnectMsg, IbcChannelOpenMsg, IbcChannelOpenResponse, MessageInfo, + Reply, ReplyOn, Response, StdError, StdResult, SubMsg, SubMsgResult, WasmMsg, }; pub const IBC_APP_VERSION: &str = "ibc-v1"; @@ -129,6 +128,7 @@ pub fn ibc_channel_connect( 3 => Ok(IbcBasicResponse::new().add_attribute("attr1", "😗")), 4 => Ok(IbcBasicResponse::new() .add_event(Event::new("cyber1".to_string()).add_attribute("attr1", "🤯"))), + 5 => Err(StdError::generic_err("Intentional")), _ => Err(StdError::generic_err("Unsupported channel connect type")), } } From 4058c9e4c2558dc10e13afd94902bbf89b24ae6e Mon Sep 17 00:00:00 2001 From: Lior Bondarevski Date: Sun, 28 Aug 2022 23:48:28 +0300 Subject: [PATCH 44/44] Channel close tests --- x/compute/internal/keeper/ibc_test.go | 179 +++++++++++++++++- .../keeper/testdata/ibc/src/contract.rs | 98 ++++++---- 2 files changed, 240 insertions(+), 37 deletions(-) diff --git a/x/compute/internal/keeper/ibc_test.go b/x/compute/internal/keeper/ibc_test.go index 8d4edcd64..85d0c7282 100644 --- a/x/compute/internal/keeper/ibc_test.go +++ b/x/compute/internal/keeper/ibc_test.go @@ -113,7 +113,7 @@ func ibcChannelCloseHelper( t *testing.T, keeper Keeper, ctx sdk.Context, contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, gas uint64, shouldSendCloseConfirn bool, channel v1types.IBCChannel, -) cosmwasm.StdError { +) (sdk.Context, []ContractEvent, cosmwasm.StdError) { // create new ctx with the same storage and a gas limit // this is to reset the event manager, so we won't get // events from past calls @@ -147,10 +147,13 @@ func ibcChannelCloseHelper( require.NotZero(t, gasMeter.GetWasmCounter(), err) if err != nil { - return cosmwasm.StdError{GenericErr: &cosmwasm.GenericErr{Msg: err.Error()}} + return ctx, nil, cosmwasm.StdError{GenericErr: &cosmwasm.GenericErr{Msg: err.Error()}} } - return cosmwasm.StdError{} + // wasmEvents comes from all the callbacks as well + wasmEvents := tryDecryptWasmEvents(ctx, []byte{}, true) + + return ctx, wasmEvents, cosmwasm.StdError{} } func createIBCEndpoint(port string, channel string) v1types.IBCEndpoint { @@ -514,3 +517,173 @@ func TestIBCChannelConnect(t *testing.T) { }) } } + +func TestIBCChannelConnectOpenAck(t *testing.T) { + ctx, keeper, codeID, _, walletA, privKeyA, _, _ := setupTest(t, "./testdata/ibc/contract.wasm", sdk.NewCoins()) + + _, _, contractAddress, _, err := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, `{"init":{}}`, true, true, defaultGasForTests) + require.Empty(t, err) + + ibcChannel := v1types.IBCChannel{ + Endpoint: createIBCEndpoint(PortIDForContract(contractAddress), "channel.0"), + CounterpartyEndpoint: createIBCEndpoint(PortIDForContract(contractAddress), "channel.1"), + Order: v1types.Unordered, + Version: "1", + ConnectionID: "1", + } + + ctx, _, err = ibcChannelConnectHelper(t, keeper, ctx, contractAddress, privKeyA, defaultGasForTests, true, ibcChannel) + require.Empty(t, err) + + queryRes, err := queryHelper(t, keeper, ctx, contractAddress, `{"q":{}}`, true, true, math.MaxUint64) + require.Empty(t, err) + + require.Equal(t, "3", queryRes) +} + +func TestIBCChannelClose(t *testing.T) { + ctx, keeper, codeID, _, walletA, privKeyA, _, _ := setupTest(t, "./testdata/ibc/contract.wasm", sdk.NewCoins()) + + _, _, contractAddress, _, err := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, `{"init":{}}`, true, true, defaultGasForTests) + require.Empty(t, err) + + for _, test := range []struct { + description string + connectionID string + output string + isSuccess bool + hasAttributes bool + hasEvents bool + }{ + { + description: "Default", + connectionID: "0", + output: "6", + isSuccess: true, + hasAttributes: false, + hasEvents: false, + }, + { + description: "SubmessageNoReply", + connectionID: "1", + output: "12", + isSuccess: true, + hasAttributes: false, + hasEvents: false, + }, + { + description: "SubmessageWithReply", + connectionID: "2", + output: "19", + isSuccess: true, + hasAttributes: false, + hasEvents: false, + }, + { + description: "Attributes", + connectionID: "3", + output: "9", + isSuccess: true, + hasAttributes: true, + hasEvents: false, + }, + { + description: "Events", + connectionID: "4", + output: "10", + isSuccess: true, + hasAttributes: false, + hasEvents: true, + }, + { + description: "Error", + connectionID: "5", + output: "", + isSuccess: false, + hasAttributes: false, + hasEvents: false, + }, + } { + t.Run(test.description, func(t *testing.T) { + ibcChannel := v1types.IBCChannel{ + Endpoint: createIBCEndpoint(PortIDForContract(contractAddress), "channel.0"), + CounterpartyEndpoint: createIBCEndpoint(PortIDForContract(contractAddress), "channel.1"), + Order: v1types.Unordered, + Version: "1", + ConnectionID: test.connectionID, + } + + ctx, events, err := ibcChannelCloseHelper(t, keeper, ctx, contractAddress, privKeyA, defaultGasForTests, true, ibcChannel) + + if !test.isSuccess { + require.Contains(t, fmt.Sprintf("%+v", err), "Intentional") + } else { + require.Empty(t, err) + if test.hasAttributes { + require.Equal(t, + []ContractEvent{ + { + {Key: "contract_address", Value: contractAddress.String()}, + {Key: "attr1", Value: "😗"}, + }, + }, + events, + ) + } + + if test.hasEvents { + hadCyber1 := false + evts := ctx.EventManager().Events() + for _, e := range evts { + if e.Type == "wasm-cyber1" { + require.False(t, hadCyber1) + attrs, err := parseAndDecryptAttributes(e.Attributes, []byte{}, false) + require.Empty(t, err) + + require.Equal(t, + []v010cosmwasm.LogAttribute{ + {Key: "contract_address", Value: contractAddress.String()}, + {Key: "attr1", Value: "🤯"}, + }, + attrs, + ) + + hadCyber1 = true + } + } + + require.True(t, hadCyber1) + } + + queryRes, err := queryHelper(t, keeper, ctx, contractAddress, `{"q":{}}`, true, true, math.MaxUint64) + + require.Empty(t, err) + + require.Equal(t, test.output, queryRes) + } + }) + } +} + +func TestIBCChannelCloseInit(t *testing.T) { + ctx, keeper, codeID, _, walletA, privKeyA, _, _ := setupTest(t, "./testdata/ibc/contract.wasm", sdk.NewCoins()) + + _, _, contractAddress, _, err := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, `{"init":{}}`, true, true, defaultGasForTests) + require.Empty(t, err) + + ibcChannel := v1types.IBCChannel{ + Endpoint: createIBCEndpoint(PortIDForContract(contractAddress), "channel.0"), + CounterpartyEndpoint: createIBCEndpoint(PortIDForContract(contractAddress), "channel.1"), + Order: v1types.Unordered, + Version: "1", + ConnectionID: "1", + } + + ctx, _, err = ibcChannelCloseHelper(t, keeper, ctx, contractAddress, privKeyA, defaultGasForTests, false, ibcChannel) + require.Empty(t, err) + + queryRes, err := queryHelper(t, keeper, ctx, contractAddress, `{"q":{}}`, true, true, math.MaxUint64) + require.Empty(t, err) + + require.Equal(t, "5", queryRes) +} diff --git a/x/compute/internal/keeper/testdata/ibc/src/contract.rs b/x/compute/internal/keeper/testdata/ibc/src/contract.rs index 182027ba5..6d93290d9 100644 --- a/x/compute/internal/keeper/testdata/ibc/src/contract.rs +++ b/x/compute/internal/keeper/testdata/ibc/src/contract.rs @@ -2,8 +2,9 @@ use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; use crate::state::{count, count_read}; use cosmwasm_std::{ entry_point, to_binary, Binary, CosmosMsg, Deps, DepsMut, Env, Event, Ibc3ChannelOpenResponse, - IbcBasicResponse, IbcChannelConnectMsg, IbcChannelOpenMsg, IbcChannelOpenResponse, MessageInfo, - Reply, ReplyOn, Response, StdError, StdResult, SubMsg, SubMsgResult, WasmMsg, + IbcBasicResponse, IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, + IbcChannelOpenResponse, MessageInfo, Reply, ReplyOn, Response, StdError, StdResult, SubMsg, + SubMsgResult, WasmMsg, }; pub const IBC_APP_VERSION: &str = "ibc-v1"; @@ -78,6 +79,41 @@ pub fn increment(deps: DepsMut, c: u64) -> StdResult { Ok(resp) } +pub fn get_resp_based_on_num(env: Env, num: u64) -> StdResult { + match num { + 0 => Ok(IbcBasicResponse::default()), + 1 => Ok(IbcBasicResponse::new().add_submessage(SubMsg { + id: 1, + msg: CosmosMsg::Wasm(WasmMsg::Execute { + code_hash: env.contract.code_hash, + contract_addr: env.contract.address.into_string(), + msg: Binary::from("{\"increment\":{\"addition\":5}}".as_bytes().to_vec()), + funds: vec![], + }) + .into(), + reply_on: ReplyOn::Never, + gas_limit: None, + })), + 2 => Ok(IbcBasicResponse::new().add_submessage(SubMsg { + id: 1, + msg: CosmosMsg::Wasm(WasmMsg::Execute { + code_hash: env.contract.code_hash, + contract_addr: env.contract.address.into_string(), + msg: Binary::from("{\"increment\":{\"addition\":5}}".as_bytes().to_vec()), + funds: vec![], + }) + .into(), + reply_on: ReplyOn::Always, + gas_limit: None, + })), + 3 => Ok(IbcBasicResponse::new().add_attribute("attr1", "😗")), + 4 => Ok(IbcBasicResponse::new() + .add_event(Event::new("cyber1".to_string()).add_attribute("attr1", "🤯"))), + 5 => Err(StdError::generic_err("Intentional")), + _ => Err(StdError::generic_err("Unsupported channel connect type")), + } +} + #[entry_point] pub fn ibc_channel_connect( deps: DepsMut, @@ -99,39 +135,33 @@ pub fn ibc_channel_connect( count(deps.storage).save(&(num + 4))?; - match num { - 0 => Ok(IbcBasicResponse::default()), - 1 => Ok(IbcBasicResponse::new().add_submessage(SubMsg { - id: 1, - msg: CosmosMsg::Wasm(WasmMsg::Execute { - code_hash: env.contract.code_hash, - contract_addr: env.contract.address.into_string(), - msg: Binary::from("{\"increment\":{\"addition\":5}}".as_bytes().to_vec()), - funds: vec![], - }) - .into(), - reply_on: ReplyOn::Never, - gas_limit: None, - })), - 2 => Ok(IbcBasicResponse::new().add_submessage(SubMsg { - id: 1, - msg: CosmosMsg::Wasm(WasmMsg::Execute { - code_hash: env.contract.code_hash, - contract_addr: env.contract.address.into_string(), - msg: Binary::from("{\"increment\":{\"addition\":5}}".as_bytes().to_vec()), - funds: vec![], - }) - .into(), - reply_on: ReplyOn::Always, - gas_limit: None, - })), - 3 => Ok(IbcBasicResponse::new().add_attribute("attr1", "😗")), - 4 => Ok(IbcBasicResponse::new() - .add_event(Event::new("cyber1".to_string()).add_attribute("attr1", "🤯"))), - 5 => Err(StdError::generic_err("Intentional")), - _ => Err(StdError::generic_err("Unsupported channel connect type")), - } + get_resp_based_on_num(env, num) } _ => Err(StdError::generic_err("Unsupported channel connect")), } } + +#[entry_point] +/// On closed channel, we take all tokens from reflect contract to this contract. +/// We also delete the channel entry from accounts. +pub fn ibc_channel_close( + deps: DepsMut, + env: Env, + msg: IbcChannelCloseMsg, +) -> StdResult { + match msg { + IbcChannelCloseMsg::CloseInit { channel: _ } => { + count(deps.storage).save(&5)?; + Ok(IbcBasicResponse::default()) + } + IbcChannelCloseMsg::CloseConfirm { channel } => { + let num: u64 = channel.connection_id.parse::().map_err(|err| { + StdError::generic_err(format!("Got an error from parsing: {:?}", err)) + })?; + + count(deps.storage).save(&(num + 6))?; + get_resp_based_on_num(env, num) + } + _ => Err(StdError::generic_err("Unsupported channel close")), + } +}