Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/cw-1-ibc-receive-hooks' into esh…
Browse files Browse the repository at this point in the history
…el-ibc
  • Loading branch information
Lior Bondarevski committed Aug 29, 2022
2 parents c29e8a6 + 4058c9e commit c32ba0b
Show file tree
Hide file tree
Showing 11 changed files with 536 additions and 157 deletions.
50 changes: 27 additions & 23 deletions cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ use log::*;

use crate::contract_validation::{ReplyParams, ValidatedMessage};
use crate::external::results::{HandleSuccess, InitSuccess, QuerySuccess};
use crate::message::{parse_message, ParsedMessage};
use crate::message::{is_ibc_msg, parse_message, ParsedMessage};

use super::contract_validation::{
generate_encryption_key, validate_contract_key, validate_msg, 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};
Expand Down Expand Up @@ -119,6 +122,7 @@ pub fn init(
reply_params,
&canonical_sender_address,
false,
false,
)?;

Ok(output)
Expand Down Expand Up @@ -169,8 +173,6 @@ pub fn handle(

let canonical_contract_address = to_canonical(contract_address)?;

let canonical_sender_address = to_canonical(sender)?;

let contract_key = base_env.get_contract_key()?;

validate_contract_key(&contract_key, &canonical_contract_address, &contract_code)?;
Expand All @@ -189,12 +191,18 @@ 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)?;
}

// 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)
// All of these functions go through handle but the data isn't signed:
// Reply (that is not WASM reply)
if should_validate_sig_info {
// Verify env parameters against the signed tx

verify_params(
&parsed_sig_info,
sent_funds,
Expand Down Expand Up @@ -225,8 +233,9 @@ pub fn handle(
secret_msg.user_public_key,
)?;

let mut versioned_env =
base_env.clone().into_versioned_env(&engine.contract_instance.cosmwasm_api_version);
let mut versioned_env = base_env
.clone()
.into_versioned_env(&engine.contract_instance.cosmwasm_api_version);

versioned_env.set_contract_hash(&contract_hash);

Expand All @@ -239,7 +248,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)?;

Expand All @@ -249,15 +258,6 @@ pub fn handle(
);

if was_msg_encrypted {

let canonical_sender_address = CanonicalAddr::from_human(&sender).map_err(|err| {
warn!(
"handle got an error while trying to deserialize sender from bech32 string to bytes {:?}: {}",
sender, err
);
EnclaveError::FailedToDeserialize
})?;

output = encrypt_output(
output,
&secret_msg,
Expand All @@ -266,15 +266,18 @@ 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 finalized_output = finalize_raw_output(raw_output, false);
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, 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!(
Expand Down Expand Up @@ -371,6 +374,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)
})
Expand Down
182 changes: 113 additions & 69 deletions cosmwasm/enclaves/shared/contract-engine/src/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::contract_validation::ReplyParams;
///
use super::types::{IoNonce, SecretMessage};
use cw_types_v010::encoding::Binary;
use cw_types_v010::types::{CanonicalAddr, Coin};
use cw_types_v010::types::{CanonicalAddr, Coin, LogAttribute};
use cw_types_v1::results::{Event, Reply, ReplyOn, SubMsgResponse, SubMsgResult};

use enclave_ffi_types::EnclaveError;
Expand Down Expand Up @@ -52,10 +52,6 @@ pub enum RawWasmOutput {
internal_reply_enclave_sig: Option<Binary>,
internal_msg_id: Option<Binary>,
},
OkIBCBasic {
#[serde(rename = "Ok")]
ok: cw_types_v1::ibc::IbcBasicResponse,
},
OkIBCPacketReceive {
#[serde(rename = "Ok")]
ok: cw_types_v1::ibc::IbcReceiveResponse,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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(cw_types_v1::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,
Expand All @@ -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,
Expand Down Expand Up @@ -322,6 +332,70 @@ pub fn finalize_raw_output(raw_output: RawWasmOutput, is_query_output: bool) ->
};
}

pub fn manipulate_callback_sig_for_plaintext(
output: Vec<u8>,
) -> Result<RawWasmOutput, EnclaveError> {
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 cw_types_v1::results::CosmosMsg::Wasm(wasm_msg) = &mut sub_msg.msg {
match wasm_msg {
cw_types_v1::results::WasmMsg::Execute { callback_sig, .. }
| cw_types_v1::results::WasmMsg::Instantiate { callback_sig, .. } => {
*callback_sig = Some(vec![])
}
}
}
}
}
RawWasmOutput::OkIBCPacketReceive { ok } => {
for sub_msg in &mut ok.messages {
if let cw_types_v1::results::CosmosMsg::Wasm(wasm_msg) = &mut sub_msg.msg {
match wasm_msg {
cw_types_v1::results::WasmMsg::Execute { callback_sig, .. }
| cw_types_v1::results::WasmMsg::Instantiate { callback_sig, .. } => {
*callback_sig = Some(vec![])
}
}
}
}
}
_ => {}
}

Ok(raw_output)
}

pub fn set_attributes_to_plaintext(attributes: &mut Vec<LogAttribute>) {
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<u8>,
secret_msg: &SecretMessage,
Expand All @@ -330,6 +404,7 @@ pub fn encrypt_output(
reply_params: Option<ReplyParams>,
sender_addr: &CanonicalAddr,
is_query_output: bool,
is_ibc_output: bool,
) -> Result<Vec<u8>, 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.
Expand All @@ -353,10 +428,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(
Expand Down Expand Up @@ -525,6 +597,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,
Expand Down Expand Up @@ -595,39 +672,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 cw_types_v1::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 cw_types_v1::results::CosmosMsg::Wasm(wasm_msg) = &mut sub_msg.msg {
Expand Down Expand Up @@ -670,7 +714,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| {
Expand Down
Loading

0 comments on commit c32ba0b

Please sign in to comment.