diff --git a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs index 75540fbdd..274d3782a 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs @@ -86,7 +86,7 @@ pub fn init( let ValidatedMessage { validated_msg, reply_params, - } = validate_msg(&decrypted_msg, &contract_hash, None)?; + } = validate_msg(&decrypted_msg, &contract_hash, None, None)?; let mut engine = start_engine( context, @@ -191,10 +191,10 @@ pub fn handle( contract_hash_for_validation, } = parse_message(msg, &parsed_sig_info, &parsed_handle_type)?; - let mut canonical_sender_address = CanonicalAddr::from_vec(vec![]); - if should_validate_sig_info || was_msg_encrypted { - canonical_sender_address = to_canonical(sender)?; - } + let canonical_sender_address = match to_canonical(sender) { + Ok(can) => can, + Err(_) => CanonicalAddr::from_vec(vec![]), + }; // There is no signature to verify when the input isn't signed. // Receiving unsigned messages is only possible in Handle. (Init tx are always signed) @@ -215,7 +215,12 @@ pub fn handle( let mut validated_msg = decrypted_msg.clone(); let mut reply_params: Option = None; if was_msg_encrypted { - let x = validate_msg(&decrypted_msg, &contract_hash, contract_hash_for_validation)?; + let x = validate_msg( + &decrypted_msg, + &contract_hash, + contract_hash_for_validation, + Some(parsed_handle_type.clone()), + )?; validated_msg = x.validated_msg; reply_params = x.reply_params; } @@ -337,7 +342,7 @@ pub fn query( let decrypted_msg = secret_msg.decrypt()?; let ValidatedMessage { validated_msg, .. } = - validate_msg(&decrypted_msg, &contract_hash, None)?; + validate_msg(&decrypted_msg, &contract_hash, None, None)?; let mut engine = start_engine( context, diff --git a/cosmwasm/enclaves/shared/contract-engine/src/contract_validation.rs b/cosmwasm/enclaves/shared/contract-engine/src/contract_validation.rs index 95fa9fe9c..b2fe47d8f 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/contract_validation.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/contract_validation.rs @@ -1,17 +1,18 @@ +use cw_types_v1::ibc::IbcPacketReceiveMsg; use cw_types_v1::results::REPLY_ENCRYPTION_MAGIC_BYTES; use log::*; -use enclave_ffi_types::EnclaveError; - use cw_types_v010::types::{CanonicalAddr, Coin, HumanAddr}; use enclave_cosmos_types::traits::CosmosAminoPubkey; use enclave_cosmos_types::types::{ - ContractCode, CosmWasmMsg, CosmosPubKey, SigInfo, SignDoc, StdSignDoc, + ContractCode, CosmWasmMsg, CosmosPubKey, HandleType, SigInfo, SignDoc, StdSignDoc, }; use enclave_crypto::traits::VerifyingKey; use enclave_crypto::{sha_256, AESKey, Hmac, Kdf, HASH_SIZE, KEY_MANAGER}; +use enclave_ffi_types::EnclaveError; use crate::io::create_callback_signature; +use crate::message::is_ibc_msg; use crate::types::SecretMessage; pub type ContractKey = [u8; CONTRACT_KEY_LENGTH]; @@ -121,6 +122,64 @@ pub fn validate_msg( msg: &[u8], contract_hash: &[u8; HASH_SIZE], contract_hash_for_validation: Option>, + handle_type: Option, +) -> Result { + match handle_type { + None => validate_basic_msg(msg, contract_hash, contract_hash_for_validation), + Some(h) => match is_ibc_msg(h.clone()) { + false => validate_basic_msg(msg, contract_hash, contract_hash_for_validation), + true => validate_ibc_msg(msg, contract_hash, contract_hash_for_validation, h), + }, + } +} + +pub fn validate_ibc_msg( + msg: &[u8], + contract_hash: &[u8; HASH_SIZE], + contract_hash_for_validation: Option>, + handle_type: HandleType, +) -> Result { + match handle_type { + HandleType::HANDLE_TYPE_IBC_PACKET_RECEIVE => { + let mut parsed_ibc_packet: IbcPacketReceiveMsg = + serde_json::from_slice(&msg.to_vec()).map_err(|err| { + warn!( + "IbcPacketReceive msg got an error while trying to deserialize msg input bytes into json {:?}: {}", + String::from_utf8_lossy(&msg), + err + ); + EnclaveError::FailedToDeserialize + })?; + + let validated_msg = validate_basic_msg( + parsed_ibc_packet.packet.data.as_slice(), + contract_hash, + contract_hash_for_validation, + )?; + parsed_ibc_packet.packet.data = validated_msg.validated_msg.as_slice().into(); + + Ok(ValidatedMessage{ + validated_msg: serde_json::to_vec(&parsed_ibc_packet).map_err(|err| { + warn!( + "got an error while trying to serialize parsed_ibc_packet msg into bytes {:?}: {}", + parsed_ibc_packet, err + ); + EnclaveError::FailedToSerialize + })?, + reply_params: validated_msg.reply_params, + }) + } + _ => { + warn!("Malformed message - in IBC, only packet receive message can be encrypted"); + Err(EnclaveError::ValidationFailure) + } + } +} + +pub fn validate_basic_msg( + msg: &[u8], + contract_hash: &[u8; HASH_SIZE], + contract_hash_for_validation: Option>, ) -> Result { if contract_hash_for_validation.is_none() && msg.len() < HEX_ENCODED_HASH_SIZE { warn!("Malformed message - expected contract code hash to be prepended to the msg"); diff --git a/cosmwasm/enclaves/shared/contract-engine/src/io.rs b/cosmwasm/enclaves/shared/contract-engine/src/io.rs index 2c1c8f9fd..b5177c8cf 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/io.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/io.rs @@ -598,7 +598,7 @@ pub fn encrypt_output( if let Some(data) = &mut ok.data { if is_ibc_output { - warn!("IBC output should not containt any data"); + warn!("IBC output should not contain any data"); return Err(EnclaveError::InternalError); } diff --git a/cosmwasm/enclaves/shared/contract-engine/src/message.rs b/cosmwasm/enclaves/shared/contract-engine/src/message.rs index 605a48332..56fbf4749 100644 --- a/cosmwasm/enclaves/shared/contract-engine/src/message.rs +++ b/cosmwasm/enclaves/shared/contract-engine/src/message.rs @@ -505,7 +505,7 @@ pub fn parse_message( }) } HandleType::HANDLE_TYPE_IBC_PACKET_RECEIVE => { - // LIORRR TODO: Maybe mark whether the message was encrypted or not. + // TODO: Maybe mark whether the message was encrypted or not. parse_ibc_packet( IbcPacketReceiveMsg::default(), message, @@ -513,11 +513,11 @@ pub fn parse_message( ) } HandleType::HANDLE_TYPE_IBC_PACKET_ACK => { - // LIORRR TODO: Maybe mark whether the message was encrypted or not. + // TODO: Maybe mark whether the message was encrypted or not. 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. + // TODO: Maybe mark whether the message was encrypted or not. parse_ibc_packet( IbcPacketTimeoutMsg::default(), message, diff --git a/cosmwasm/enclaves/shared/cosmwasm-types/v0.10/src/types.rs b/cosmwasm/enclaves/shared/cosmwasm-types/v0.10/src/types.rs index 54bb82dde..e0d828335 100644 --- a/cosmwasm/enclaves/shared/cosmwasm-types/v0.10/src/types.rs +++ b/cosmwasm/enclaves/shared/cosmwasm-types/v0.10/src/types.rs @@ -133,6 +133,7 @@ pub struct ContractInfo { // This should be in correlation with cosmwasm-std/init_handle's InitResponse and HandleResponse #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[serde(deny_unknown_fields)] pub struct ContractResult { pub messages: Vec, pub log: Vec, diff --git a/cosmwasm/enclaves/shared/cosmwasm-types/v1.0/src/ibc.rs b/cosmwasm/enclaves/shared/cosmwasm-types/v1.0/src/ibc.rs index 3d38d90b5..f598d984c 100644 --- a/cosmwasm/enclaves/shared/cosmwasm-types/v1.0/src/ibc.rs +++ b/cosmwasm/enclaves/shared/cosmwasm-types/v1.0/src/ibc.rs @@ -94,6 +94,7 @@ impl IbcBasicResponse { // the calling chain. (Returning ContractResult::Err will abort processing of this packet // and not inform the calling chain). #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[serde(deny_unknown_fields)] #[non_exhaustive] pub struct IbcReceiveResponse where diff --git a/cosmwasm/enclaves/shared/cosmwasm-types/v1.0/src/results/response.rs b/cosmwasm/enclaves/shared/cosmwasm-types/v1.0/src/results/response.rs index c6f005472..06a90890f 100644 --- a/cosmwasm/enclaves/shared/cosmwasm-types/v1.0/src/results/response.rs +++ b/cosmwasm/enclaves/shared/cosmwasm-types/v1.0/src/results/response.rs @@ -60,6 +60,7 @@ use super::{Event, SubMsg}; /// } /// ``` #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[serde(deny_unknown_fields)] pub struct Response where T: Clone + fmt::Debug + PartialEq, diff --git a/go-cosmwasm/lib.go b/go-cosmwasm/lib.go index cf478cf31..bf83e12a5 100644 --- a/go-cosmwasm/lib.go +++ b/go-cosmwasm/lib.go @@ -263,7 +263,6 @@ 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 85d0c7282..10ceb1325 100644 --- a/x/compute/internal/keeper/ibc_test.go +++ b/x/compute/internal/keeper/ibc_test.go @@ -184,7 +184,7 @@ func ibcPacketReceiveHelper( t *testing.T, keeper Keeper, ctx sdk.Context, contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, shouldEncryptMsg bool, gas uint64, packet v1types.IBCPacket, -) ([]byte, cosmwasm.StdError) { +) (sdk.Context, []byte, []ContractEvent, []byte, cosmwasm.StdError) { var nonce []byte internalPacket := packet @@ -224,40 +224,29 @@ func ibcPacketReceiveHelper( require.NotZero(t, gasMeter.GetWasmCounter(), err) + // wasmEvents comes from all the callbacks as well + wasmEvents := tryDecryptWasmEvents(ctx, nonce, !shouldEncryptMsg) + if err != nil { if shouldEncryptMsg { - return nil, extractInnerError(t, err, nonce, true, true) + return ctx, nil, nil, nil, extractInnerError(t, err, nonce, true, true) } - return nil, cosmwasm.StdError{GenericErr: &cosmwasm.GenericErr{Msg: err.Error()}} + return ctx, nil, nil, nil, cosmwasm.StdError{GenericErr: &cosmwasm.GenericErr{Msg: err.Error()}} } - return res, cosmwasm.StdError{} + data := res + if shouldEncryptMsg { + data = getDecryptedData(t, res, nonce) + } + + return ctx, nonce, wasmEvents, data, cosmwasm.StdError{} } func ibcPacketAckHelper( t *testing.T, keeper Keeper, ctx sdk.Context, - contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, - shouldEncryptMsg bool, gas uint64, originalPacket v1types.IBCPacket, ack []byte, + contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, gas uint64, originalPacket v1types.IBCPacket, ack []byte, ) cosmwasm.StdError { - var nonce []byte - - if shouldEncryptMsg { - contractHash, err := keeper.GetContractHash(ctx, contractAddr) - require.NoError(t, err) - hashStr := hex.EncodeToString(contractHash) - - 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 @@ -282,10 +271,6 @@ func ibcPacketAckHelper( 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()}} } @@ -294,27 +279,8 @@ func ibcPacketAckHelper( func ibcPacketTimeoutHelper( t *testing.T, keeper Keeper, ctx sdk.Context, - contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, - shouldEncryptMsg bool, gas uint64, originalPacket v1types.IBCPacket, + contractAddr sdk.AccAddress, creatorPrivKey crypto.PrivKey, gas uint64, originalPacket v1types.IBCPacket, ) cosmwasm.StdError { - var nonce []byte - - if shouldEncryptMsg { - contractHash, err := keeper.GetContractHash(ctx, contractAddr) - require.NoError(t, err) - hashStr := hex.EncodeToString(contractHash) - - 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 @@ -336,10 +302,6 @@ func ibcPacketTimeoutHelper( 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()}} } @@ -687,3 +649,127 @@ func TestIBCChannelCloseInit(t *testing.T) { require.Equal(t, "5", queryRes) } + +func TestIBCPacketReceive(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 _, isEncrypted := range []bool{false, true} { + for _, test := range []struct { + description string + sequence uint64 + output string + isSuccess bool + hasAttributes bool + hasEvents bool + }{ + { + description: "Default", + sequence: 0, + output: "7", + isSuccess: true, + hasAttributes: false, + hasEvents: false, + }, + { + description: "SubmessageNoReply", + sequence: 1, + output: "13", + isSuccess: true, + hasAttributes: false, + hasEvents: false, + }, + { + description: "SubmessageWithReply", + sequence: 2, + output: "20", + isSuccess: true, + hasAttributes: false, + hasEvents: false, + }, + { + description: "Attributes", + sequence: 3, + output: "10", + isSuccess: true, + hasAttributes: true, + hasEvents: false, + }, + { + description: "Events", + sequence: 4, + output: "11", + isSuccess: true, + hasAttributes: false, + hasEvents: true, + }, + { + description: "Error", + sequence: 5, + output: "", + isSuccess: false, + hasAttributes: false, + hasEvents: false, + }, + } { + t.Run(fmt.Sprintf("%s-Encryption:%t", test.description, isEncrypted), func(t *testing.T) { + ibcPacket := createIBCPacket(createIBCEndpoint(PortIDForContract(contractAddress), "channel.1"), + createIBCEndpoint(PortIDForContract(contractAddress), "channel.0"), + test.sequence, + createIBCTimeout(math.MaxUint64), + []byte{}, + ) + ctx, nonce, events, data, err := ibcPacketReceiveHelper(t, keeper, ctx, contractAddress, privKeyA, isEncrypted, defaultGasForTests, ibcPacket) + + if !test.isSuccess { + require.Contains(t, fmt.Sprintf("%+v", err), "Intentional") + } else { + require.Empty(t, err) + require.Equal(t, "\"out\"", string(data)) + + 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, nonce, isEncrypted) + 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/msg_dispatcher.go b/x/compute/internal/keeper/msg_dispatcher.go index c654a9514..92792f986 100644 --- a/x/compute/internal/keeper/msg_dispatcher.go +++ b/x/compute/internal/keeper/msg_dispatcher.go @@ -146,11 +146,11 @@ func isReplyEncrypted(msg v1wasmTypes.CosmosMsg, reply v1wasmTypes.Reply) bool { } if msg.Wasm.Execute != nil { - return len(msg.Wasm.Execute.CallbackCodeHash) != 0 + return len(msg.Wasm.Execute.CallbackSignature) != 0 } if msg.Wasm.Instantiate != nil { - return len(msg.Wasm.Instantiate.CallbackCodeHash) != 0 + return len(msg.Wasm.Instantiate.CallbackSignature) != 0 } return true @@ -255,7 +255,6 @@ func (d MessageDispatcher) DispatchSubmessages(ctx sdk.Context, contractAddr sdk if len(data) > 0 { responseData = data[0] } - result = v1wasmTypes.SubMsgResult{ // Copy first 64 bytes of the OG message in order to preserve the pubkey. Ok: &v1wasmTypes.SubMsgResponse{ @@ -322,7 +321,6 @@ func (d MessageDispatcher) DispatchSubmessages(ctx sdk.Context, contractAddr sdk replySigInfo = ogSigInfo reply.ID = dataWithInternalReplyInfo.InternalMsgId replySigInfo.CallbackSignature = dataWithInternalReplyInfo.InternaReplyEnclaveSig - } rspData, err := d.keeper.reply(ctx, contractAddr, reply, ogTx, replySigInfo) diff --git a/x/compute/internal/keeper/relay.go b/x/compute/internal/keeper/relay.go index 8fe04024d..2bef5b8e7 100644 --- a/x/compute/internal/keeper/relay.go +++ b/x/compute/internal/keeper/relay.go @@ -67,9 +67,9 @@ func (k Keeper) parseThenHandleIBCBasicContractResponse(ctx sdk.Context, } return k.handleIBCBasicContractResponse(ctx, contractAddress, contractInfo.IBCPortID, inputMsg, resp) - } else { - return sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("null pointer 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)) } @@ -203,15 +203,23 @@ func (k Keeper) OnRecvPacket( if err != nil { return nil, err } - verificationInfo := types.NewVerificationInfo([]byte{}, sdktxsigning.SignMode_SIGN_MODE_UNSPECIFIED, []byte{}, []byte{}, []byte{}, nil) + verificationInfo := types.NewVerificationInfo([]byte{}, sdktxsigning.SignMode_SIGN_MODE_DIRECT, []byte{}, []byte{}, []byte{}, nil) + + ogTx := msg.Packet.Data + + // If the data contains less than 64 bytes (means plaintext) + // use the whole message just for compilation + if len(ogTx) < 64 { + ogTx = msgBz + } // 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)) + return k.handleContractResponse(ctx, contractAddress, contractInfo.IBCPortID, resp.Messages, resp.Attributes, resp.Events, resp.Acknowledgement, ogTx, verificationInfo, wasmTypes.CosmosMsgVersionV1) } + + // 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)) } diff --git a/x/compute/internal/keeper/secret_contracts_test.go b/x/compute/internal/keeper/secret_contracts_test.go index 77203516e..50c07f3c9 100644 --- a/x/compute/internal/keeper/secret_contracts_test.go +++ b/x/compute/internal/keeper/secret_contracts_test.go @@ -167,7 +167,7 @@ func decryptAttribute(attr v010cosmwasm.LogAttribute, nonce []byte) (v010cosmwas } keyPlainBz, err := wasmCtx.Decrypt(keyCipherBz, nonce) if err != nil { - return v010cosmwasm.LogAttribute{}, fmt.Errorf("Failed Decrypt for key %+v", keyPlainBz) + return v010cosmwasm.LogAttribute{}, fmt.Errorf("Failed Decrypt for key %+v", keyCipherBz) } newAttr.Key = string(keyPlainBz) @@ -232,8 +232,11 @@ func tryDecryptWasmEvents(ctx sdk.Context, nonce []byte, shouldSkipAttributes .. if !shouldSkip && newLog.Key != "contract_address" { // key + fmt.Printf("LIORRR before %+v nonce %+v", newLog, nonce) newAttr, err := decryptAttribute(newLog, nonce) + fmt.Printf("LIORRR after %+v", newAttr) if err != nil { + fmt.Printf("LIORRR here %+v", err) continue } diff --git a/x/compute/internal/keeper/testdata/ibc/src/contract.rs b/x/compute/internal/keeper/testdata/ibc/src/contract.rs index 6d93290d9..9b66e4936 100644 --- a/x/compute/internal/keeper/testdata/ibc/src/contract.rs +++ b/x/compute/internal/keeper/testdata/ibc/src/contract.rs @@ -3,8 +3,8 @@ use crate::state::{count, count_read}; use cosmwasm_std::{ entry_point, to_binary, Binary, CosmosMsg, Deps, DepsMut, Env, Event, Ibc3ChannelOpenResponse, IbcBasicResponse, IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, - IbcChannelOpenResponse, MessageInfo, Reply, ReplyOn, Response, StdError, StdResult, SubMsg, - SubMsgResult, WasmMsg, + IbcChannelOpenResponse, IbcPacketReceiveMsg, IbcReceiveResponse, MessageInfo, Reply, ReplyOn, + Response, StdError, StdResult, SubMsg, SubMsgResult, WasmMsg, }; pub const IBC_APP_VERSION: &str = "ibc-v1"; @@ -114,6 +114,41 @@ pub fn get_resp_based_on_num(env: Env, num: u64) -> StdResult } } +pub fn get_recv_resp_based_on_num(env: Env, num: u64) -> StdResult { + match num { + 0 => Ok(IbcReceiveResponse::default()), + 1 => Ok(IbcReceiveResponse::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(IbcReceiveResponse::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(IbcReceiveResponse::new().add_attribute("attr1", "😗")), + 4 => Ok(IbcReceiveResponse::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, @@ -165,3 +200,24 @@ pub fn ibc_channel_close( _ => Err(StdError::generic_err("Unsupported channel close")), } } + +#[entry_point] +/// we look for a the proper reflect contract to relay to and send the message +/// We cannot return any meaningful response value as we do not know the response value +/// of execution. We just return ok if we dispatched, error if we failed to dispatch +pub fn ibc_packet_receive( + deps: DepsMut, + env: Env, + msg: IbcPacketReceiveMsg, +) -> StdResult { + count(deps.storage).save(&(msg.packet.sequence + 7))?; + let mut resp = get_recv_resp_based_on_num(env, msg.packet.sequence); + match &mut resp { + Ok(r) => { + r.acknowledgement = to_binary(&"out".to_string())?; + } + Err(_) => {} + } + + resp +}