Skip to content

Commit

Permalink
Merge pull request #958 from scrtlabs/test-cw-v1-bank-msg
Browse files Browse the repository at this point in the history
Fix CI & Test Bank::MsgSend for CosmWasm v1 & v0.10
  • Loading branch information
assafmo authored Jul 13, 2022
2 parents 4eae1f7 + 5071882 commit c8af174
Show file tree
Hide file tree
Showing 15 changed files with 457 additions and 258 deletions.
26 changes: 23 additions & 3 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ jobs:
with:
path: ~/.cache/sccache
key: ${{ runner.os }}-sccache
- run: rustup component add rust-src clippy
- run: |
rustup component add rust-src clippy
cd cosmwasm/enclaves/execute/
rustup component add rust-src clippy
- name: Install xargo
run: |
cargo --version
Expand Down Expand Up @@ -80,7 +83,10 @@ jobs:
with:
path: ~/.cache/sccache
key: ${{ runner.os }}-sccache
- run: rustup component add rust-src clippy
- run: |
rustup component add rust-src clippy
cd cosmwasm/enclaves/execute/
rustup component add rust-src clippy
- name: Install xargo
run: |
cargo --version
Expand Down Expand Up @@ -128,6 +134,10 @@ jobs:
with:
name: contract.wasm
path: ./x/compute/internal/keeper/testdata/test-contract/contract.wasm
- uses: actions/upload-artifact@v2
with:
name: v1-contract.wasm
path: ./x/compute/internal/keeper/testdata/v1-sanity-contract/contract.wasm
- uses: actions/upload-artifact@v2
with:
name: enclave
Expand Down Expand Up @@ -174,6 +184,10 @@ jobs:
- uses: actions/download-artifact@v2
with:
name: contract.wasm
- uses: actions/download-artifact@v2
with:
name: v1-contract.wasm
path: ./x/compute/internal/keeper/testdata/v1-sanity-contract
- uses: actions/download-artifact@v2
with:
name: contract_with_floats.wasm
Expand All @@ -185,12 +199,14 @@ jobs:
name: static-too-high-initial-memory.wasm
- name: Setup Files
run: |
find "$(pwd)" -name \*.wasm
cp libgo_cosmwasm.so ./go-cosmwasm/api/libgo_cosmwasm.so
cp librust_cosmwasm_enclave.signed.so ./go-cosmwasm/librust_cosmwasm_enclave.signed.so
cp contract.wasm ./x/compute/internal/keeper/testdata/test-contract/contract.wasm
cp too-high-initial-memory.wasm ./x/compute/internal/keeper/testdata/test-contract/too-high-initial-memory.wasm
cp contract_with_floats.wasm ./x/compute/internal/keeper/testdata/test-contract/contract_with_floats.wasm
cp static-too-high-initial-memory.wasm ./x/compute/internal/keeper/testdata/test-contract/static-too-high-initial-memory.wasm
find "$(pwd)" -name \*.wasm
- name: Test x/registration
run: |
source "$HOME/.sgxsdk/sgxsdk/environment"
Expand Down Expand Up @@ -234,9 +250,13 @@ jobs:
with:
path: ~/.cache/sccache
key: ${{ runner.os }}-sccache
- run: rustup component add rust-src clippy
- run: |
rustup component add rust-src clippy
cd cosmwasm/enclaves/execute/
rustup component add rust-src clippy
- name: Install xargo
run: |
cd cosmwasm/enclaves/execute/
cargo --version
rustc --version
cargo +stable install xargo --version 0.3.25
Expand Down
125 changes: 56 additions & 69 deletions cosmwasm/enclaves/shared/contract-engine/src/contract_operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use log::*;

use enclave_ffi_types::{Ctx, EnclaveError};

use crate::contract_validation::{ReplyParams, ValidatedMessage};
use crate::external::results::{HandleSuccess, InitSuccess, QuerySuccess};
use crate::wasm::CosmWasmApiVersion;
use cosmos_proto::tx::signing::SignMode;
Expand Down Expand Up @@ -108,7 +109,10 @@ pub fn init(

let decrypted_msg = secret_msg.decrypt()?;

let (validated_msg, reply_params) = validate_msg(&decrypted_msg, contract_code.hash(), None)?;
let ValidatedMessage {
validated_msg,
reply_params,
} = validate_msg(&decrypted_msg, contract_code.hash(), None)?;

trace!(
"init input after decryption: {:?}",
Expand Down Expand Up @@ -142,8 +146,7 @@ pub fn init(
// TODO: ref: https://github.com/CosmWasm/cosmwasm/blob/b971c037a773bf6a5f5d08a88485113d9b9e8e7b/packages/std/src/query.rs#L13
let output = encrypt_output(
output,
secret_msg.nonce,
secret_msg.user_public_key,
&secret_msg,
&canonical_contract_address,
&env_v010.contract_code_hash,
reply_params,
Expand All @@ -167,25 +170,14 @@ pub fn init(
})
}

pub struct TaggedBool {
b: bool,
pub struct ParsedMessage {
pub should_validate_sig_info: bool,
pub was_msg_encrypted: bool,
pub secret_msg: SecretMessage,
pub decrypted_msg: Vec<u8>,
pub contract_hash_for_validation: Option<Vec<u8>>,
}

impl From<bool> for TaggedBool {
fn from(b: bool) -> Self {
TaggedBool { b }
}
}

impl Into<bool> for TaggedBool {
fn into(self) -> bool {
self.b
}
}

type ShouldValidateSigInfo = TaggedBool;
type WasMessageEncrypted = TaggedBool;

pub fn reduct_custom_events(reply: &mut Reply) {
reply.result = match &reply.result {
SubMsgResult::Ok(r) => {
Expand Down Expand Up @@ -228,16 +220,7 @@ pub fn parse_message(
message: &[u8],
sig_info: &SigInfo,
handle_type: &HandleType,
) -> Result<
(
ShouldValidateSigInfo,
WasMessageEncrypted,
SecretMessage,
Vec<u8>,
Option<Vec<u8>>,
),
EnclaveError,
> {
) -> Result<ParsedMessage, EnclaveError> {
let orig_secret_msg = SecretMessage::from_slice(message)?;

return match handle_type {
Expand All @@ -247,13 +230,13 @@ pub fn parse_message(
base64::encode(&message)
);
let decrypted_msg = orig_secret_msg.decrypt()?;
Ok((
ShouldValidateSigInfo::from(true),
WasMessageEncrypted::from(true),
orig_secret_msg,
Ok(ParsedMessage {
should_validate_sig_info: true,
was_msg_encrypted: true,
secret_msg: orig_secret_msg,
decrypted_msg,
None,
))
contract_hash_for_validation: None,
})
}

HandleType::HANDLE_TYPE_REPLY => {
Expand Down Expand Up @@ -315,13 +298,13 @@ pub fn parse_message(
EnclaveError::FailedToSerialize
})?;

return Ok((
ShouldValidateSigInfo::from(false),
WasMessageEncrypted::from(false),
reply_secret_msg,
serialized_reply,
None,
));
return Ok(ParsedMessage {
should_validate_sig_info: false,
was_msg_encrypted: false,
secret_msg: reply_secret_msg,
decrypted_msg: serialized_reply,
contract_hash_for_validation: None,
});
}

// Here we are sure the reply is OK because only OK is encrypted
Expand Down Expand Up @@ -420,13 +403,15 @@ pub fn parse_message(
msg: serialized_encrypted_reply,
};

Ok((
ShouldValidateSigInfo::from(true),
WasMessageEncrypted::from(true),
reply_secret_msg,
decrypted_reply_as_vec,
Some(tmp_decrypted_msg_id[..HEX_ENCODED_HASH_SIZE].to_vec()),
))
Ok(ParsedMessage {
should_validate_sig_info: true,
was_msg_encrypted: true,
secret_msg: reply_secret_msg,
decrypted_msg: decrypted_reply_as_vec,
contract_hash_for_validation: Some(
tmp_decrypted_msg_id[..HEX_ENCODED_HASH_SIZE].to_vec(),
),
})
}
SubMsgResult::Err(response) => {
let secret_msg = SecretMessage {
Expand Down Expand Up @@ -511,19 +496,22 @@ pub fn parse_message(
msg: serialized_encrypted_reply,
};

Ok((
ShouldValidateSigInfo::from(true),
WasMessageEncrypted::from(true),
reply_secret_msg,
decrypted_reply_as_vec,
Some(tmp_decrypted_msg_id[..HEX_ENCODED_HASH_SIZE].to_vec()),
))
Ok(ParsedMessage {
should_validate_sig_info: true,
was_msg_encrypted: true,
secret_msg: reply_secret_msg,
decrypted_msg: decrypted_reply_as_vec,
contract_hash_for_validation: Some(
tmp_decrypted_msg_id[..HEX_ENCODED_HASH_SIZE].to_vec(),
),
})
}
}
}
};
}

#[cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))]
pub fn handle(
context: Ctx,
gas_limit: u64,
Expand Down Expand Up @@ -584,33 +572,33 @@ pub fn handle(
// When the message is handle, we expect it always to be encrypted while in Reply for example it might be plaintext
let parsed_handle_type = HandleType::try_from(handle_type)?;

let (
let ParsedMessage {
should_validate_sig_info,
was_msg_encrypted,
secret_msg,
decrypted_msg,
contract_hash_for_validation,
) = parse_message(msg, &parsed_sig_info, &parsed_handle_type)?;
} = parse_message(msg, &parsed_sig_info, &parsed_handle_type)?;

// 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.into() {
if should_validate_sig_info {
// Verify env parameters against the signed tx
verify_params(&parsed_sig_info, &env_v010, &secret_msg)?;
}

let mut validated_msg = decrypted_msg.clone();
let mut reply_params: Option<(Vec<u8>, u64)> = None;
if was_msg_encrypted.into() {
let mut reply_params: Option<ReplyParams> = None;
if was_msg_encrypted {
let x = validate_msg(
&decrypted_msg,
contract_code.hash(),
contract_hash_for_validation,
)?;
validated_msg = x.0;
reply_params = x.1;
validated_msg = x.validated_msg;
reply_params = x.reply_params;
}

trace!(
Expand Down Expand Up @@ -656,8 +644,7 @@ pub fn handle(

let output = encrypt_output(
output,
secret_msg.nonce,
secret_msg.user_public_key,
&secret_msg,
&canonical_contract_address,
&env_v010.contract_code_hash,
reply_params,
Expand Down Expand Up @@ -722,7 +709,8 @@ pub fn query(
"query input afer decryption: {:?}",
String::from_utf8_lossy(&decrypted_msg)
);
let validated_msg = validate_msg(&decrypted_msg, contract_code.hash(), None)?.0;
let ValidatedMessage { validated_msg, .. } =
validate_msg(&decrypted_msg, contract_code.hash(), None)?;

let mut engine = start_engine(
context,
Expand All @@ -749,8 +737,7 @@ pub fn query(

let output = encrypt_output(
output,
secret_msg.nonce,
secret_msg.user_public_key,
&secret_msg,
&CanonicalAddr(Binary(Vec::new())), // Not used for queries (can't init a new contract from a query)
&"".to_string(), // Not used for queries (can't call a sub-message from a query),
None, // Not used for queries (Query response is not replied to the caller),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,22 @@ pub fn validate_contract_key(
calculated_authentication_id == expected_authentication_id
}

pub struct ValidatedMessage {
pub validated_msg: Vec<u8>,
pub reply_params: Option<ReplyParams>,
}

pub struct ReplyParams {
pub recipient_contract_hash: Vec<u8>,
pub sub_msg_id: u64,
}

/// Validate that the message sent to the enclave (after decryption) was actually addressed to this contract.
pub fn validate_msg(
msg: &[u8],
contract_hash: [u8; HASH_SIZE],
contract_hash_for_validation: Option<Vec<u8>>,
) -> Result<(Vec<u8>, Option<(Vec<u8>, u64)>), EnclaveError> {
) -> Result<ValidatedMessage, EnclaveError> {
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");
return Err(EnclaveError::ValidationFailure);
Expand Down Expand Up @@ -182,13 +192,19 @@ pub fn validate_msg(
let mut reply_recipient_contract_hash: [u8; HEX_ENCODED_HASH_SIZE] =
[0u8; HEX_ENCODED_HASH_SIZE];
reply_recipient_contract_hash.copy_from_slice(&validated_msg[0..HEX_ENCODED_HASH_SIZE]);
return Ok((
validated_msg[HEX_ENCODED_HASH_SIZE..].to_vec(),
Some((reply_recipient_contract_hash.to_vec(), sub_msg_id)),
));
return Ok(ValidatedMessage {
validated_msg: validated_msg[HEX_ENCODED_HASH_SIZE..].to_vec(),
reply_params: Some(ReplyParams {
recipient_contract_hash: reply_recipient_contract_hash.to_vec(),
sub_msg_id,
}),
});
}

Ok((validated_msg, None))
Ok(ValidatedMessage {
validated_msg,
reply_params: None,
})
}

/// Verify all the parameters sent to the enclave match up, and were signed by the right account.
Expand Down
Loading

0 comments on commit c8af174

Please sign in to comment.