From 360ee97088e7b4afaf23bb678cc4e0926a7fdf50 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 7 Feb 2020 18:22:41 +0100 Subject: [PATCH 1/7] Add HandleMsg types to trigger failures --- contracts/hackatom/src/contract.rs | 47 ++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/contracts/hackatom/src/contract.rs b/contracts/hackatom/src/contract.rs index 1b09931059..3eb297a68d 100644 --- a/contracts/hackatom/src/contract.rs +++ b/contracts/hackatom/src/contract.rs @@ -20,8 +20,19 @@ pub struct State { pub funder: CanonicalAddr, } +// failure modes to help test wasmd, based on this comment +// https://github.com/cosmwasm/wasmd/issues/8#issuecomment-576146751 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct HandleMsg {} +pub enum HandleMsg { + // Release is the only "proper" action, releasing funds in the contract + Release {}, + // Infinite loop to burn cpu cycles (only run when metering is enabled) + CpuLoop {}, + // Infinite loop making storage calls (to test when their limit hits) + StorageLoop {}, + // Trigger a panic to ensure framework handles gracefully + Panic {}, +} #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "lowercase")] @@ -53,8 +64,17 @@ pub fn init( pub fn handle( deps: &mut Extern, params: Params, - _: HandleMsg, + msg: HandleMsg, ) -> Result { + match msg { + HandleMsg::Release {} => try_release(deps, params), + HandleMsg::CpuLoop {} => cpu_loop(), + HandleMsg::StorageLoop {} => storage_loop(deps), + HandleMsg::Panic {} => do_panic(), + } +} + +fn try_release(deps: &mut Extern, params: Params) -> Result { let data = deps .storage .get(CONFIG_KEY) @@ -79,6 +99,29 @@ pub fn handle( } } +fn cpu_loop() -> Result { + let mut counter = 0u64; + loop { + counter += 1; + if counter >= 9000000000 { + counter = 0; + } + } +} + +fn storage_loop(deps: &mut Extern) -> Result { + let mut test_case = 0u64; + loop { + deps.storage + .set("test.key".as_bytes(), test_case.to_string().as_bytes()); + test_case += 1; + } +} + +fn do_panic() -> Result { + panic!("This page intentionally faulted"); +} + pub fn query(deps: &Extern, msg: QueryMsg) -> Result> { match msg { QueryMsg::Verifier {} => query_verifier(deps), From 72019caed2d192b7824ac96d0209692e2f29258a Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 7 Feb 2020 18:24:51 +0100 Subject: [PATCH 2/7] Fix existing tests --- contracts/hackatom/src/contract.rs | 4 ++-- contracts/hackatom/tests/integration.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/hackatom/src/contract.rs b/contracts/hackatom/src/contract.rs index 3eb297a68d..05d64f6efd 100644 --- a/contracts/hackatom/src/contract.rs +++ b/contracts/hackatom/src/contract.rs @@ -259,7 +259,7 @@ mod tests { &coin("15", "earth"), &coin("1015", "earth"), ); - let handle_res = handle(&mut deps, handle_params, HandleMsg {}).unwrap(); + let handle_res = handle(&mut deps, handle_params, HandleMsg::Release {}).unwrap(); assert_eq!(1, handle_res.messages.len()); let msg = handle_res.messages.get(0).expect("no message"); assert_eq!( @@ -301,7 +301,7 @@ mod tests { // beneficiary can release it let handle_params = mock_params(&deps.api, beneficiary.as_str(), &[], &coin("1000", "earth")); - let handle_res = handle(&mut deps, handle_params, HandleMsg {}); + let handle_res = handle(&mut deps, handle_params, HandleMsg::Release {}); assert!(handle_res.is_err()); // state should not change diff --git a/contracts/hackatom/tests/integration.rs b/contracts/hackatom/tests/integration.rs index 6326af4b03..9d22098762 100644 --- a/contracts/hackatom/tests/integration.rs +++ b/contracts/hackatom/tests/integration.rs @@ -84,7 +84,7 @@ fn init_and_query() { assert_eq!(verifier.as_str(), returned); // bad query returns parse error (pass wrong type - this connection is not enforced) - let qres = query(&mut deps, HandleMsg {}); + let qres = query(&mut deps, HandleMsg::Release {}); match qres { QueryResult::Err(msg) => assert!(msg.starts_with("Error parsing QueryMsg:"), msg), _ => panic!("Call should fail"), @@ -96,7 +96,7 @@ fn fails_on_bad_init() { let mut deps = mock_instance(WASM); let params = mock_params(&deps.api, "creator", &coin("1000", "earth"), &[]); // bad init returns parse error (pass wrong type - this connection is not enforced) - let res = init(&mut deps, params, HandleMsg {}); + let res = init(&mut deps, params, HandleMsg::Release {}); assert_eq!(true, res.is_err()); } @@ -128,7 +128,7 @@ fn proper_handle() { &coin("15", "earth"), &coin("1015", "earth"), ); - let handle_res = handle(&mut deps, handle_params, HandleMsg {}).unwrap(); + let handle_res = handle(&mut deps, handle_params, HandleMsg::Release {}).unwrap(); assert_eq!(1, handle_res.messages.len()); let msg = handle_res.messages.get(0).expect("no message"); assert_eq!( @@ -169,7 +169,7 @@ fn failed_handle() { // beneficiary can release it let handle_params = mock_params(&deps.api, beneficiary.as_str(), &[], &coin("1000", "earth")); - let handle_res = handle(&mut deps, handle_params, HandleMsg {}); + let handle_res = handle(&mut deps, handle_params, HandleMsg::Release {}); assert!(handle_res.is_err()); // state should not change From f31eab7d89cb6b832bb24fdb9708bbb84621d227 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 7 Feb 2020 18:31:14 +0100 Subject: [PATCH 3/7] Tested proper panic handling --- contracts/hackatom/src/contract.rs | 30 ++++++++++++++++++++++ contracts/hackatom/tests/integration.rs | 33 ++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/contracts/hackatom/src/contract.rs b/contracts/hackatom/src/contract.rs index 05d64f6efd..f7280d05fa 100644 --- a/contracts/hackatom/src/contract.rs +++ b/contracts/hackatom/src/contract.rs @@ -316,4 +316,34 @@ mod tests { } ); } + + #[test] + #[should_panic] + fn handle_panic() { + let mut deps = dependencies(20); + + // initialize the store + let verifier = HumanAddr(String::from("verifies")); + let beneficiary = HumanAddr(String::from("benefits")); + let creator = HumanAddr(String::from("creator")); + + let init_msg = InitMsg { + verifier: verifier.clone(), + beneficiary: beneficiary.clone(), + }; + let init_params = mock_params( + &deps.api, + creator.as_str(), + &coin("1000", "earth"), + &coin("1000", "earth"), + ); + let init_res = init(&mut deps, init_params, init_msg).unwrap(); + assert_eq!(0, init_res.messages.len()); + + // beneficiary can release it + let handle_params = + mock_params(&deps.api, beneficiary.as_str(), &[], &coin("1000", "earth")); + // this should panic + let _ = handle(&mut deps, handle_params, HandleMsg::Panic {}); + } } diff --git a/contracts/hackatom/tests/integration.rs b/contracts/hackatom/tests/integration.rs index 9d22098762..298dd7c066 100644 --- a/contracts/hackatom/tests/integration.rs +++ b/contracts/hackatom/tests/integration.rs @@ -1,10 +1,11 @@ use std::str::from_utf8; use cosmwasm::mock::mock_params; -use cosmwasm::serde::from_slice; +use cosmwasm::serde::{from_slice, to_vec}; use cosmwasm::traits::{Api, ReadonlyStorage}; use cosmwasm::types::{coin, CosmosMsg, HumanAddr, QueryResult}; +use cosmwasm_vm::{call_handle}; use cosmwasm_vm::testing::{handle, init, mock_instance, query}; use hackatom::contract::{HandleMsg, InitMsg, QueryMsg, State, CONFIG_KEY}; @@ -186,3 +187,33 @@ fn failed_handle() { ); }); } + +#[test] +fn handle_panic() { + let mut deps = mock_instance(WASM); + + // initialize the store + let verifier = HumanAddr(String::from("verifies")); + let beneficiary = HumanAddr(String::from("benefits")); + let creator = HumanAddr(String::from("creator")); + + let init_msg = InitMsg { + verifier: verifier.clone(), + beneficiary: beneficiary.clone(), + }; + let init_params = mock_params( + &deps.api, + creator.as_str(), + &coin("1000", "earth"), + &coin("1000", "earth"), + ); + let init_res = init(&mut deps, init_params, init_msg).unwrap(); + assert_eq!(0, init_res.messages.len()); + + // beneficiary can release it + let handle_params = mock_params(&deps.api, beneficiary.as_str(), &[], &coin("1000", "earth")); + // panic inside contract should not panic out here + // Note: we need to use the production-call, not the testing call (which unwraps any vm error) + let handle_res = call_handle(&mut deps, &handle_params, &to_vec(&HandleMsg::Panic {}).unwrap()); + assert!(handle_res.is_err()); +} From 7276c33f64c50b58307fb1d36fe8419d7d914e4c Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 7 Feb 2020 18:36:54 +0100 Subject: [PATCH 4/7] Test error on infinite cpu loop --- contracts/hackatom/tests/integration.rs | 11 +++++++++-- lib/vm/src/backends/singlepass.rs | 12 ++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/contracts/hackatom/tests/integration.rs b/contracts/hackatom/tests/integration.rs index 298dd7c066..68bde023b5 100644 --- a/contracts/hackatom/tests/integration.rs +++ b/contracts/hackatom/tests/integration.rs @@ -189,7 +189,7 @@ fn failed_handle() { } #[test] -fn handle_panic() { +fn handle_panic_and_loops() { let mut deps = mock_instance(WASM); // initialize the store @@ -210,10 +210,17 @@ fn handle_panic() { let init_res = init(&mut deps, init_params, init_msg).unwrap(); assert_eq!(0, init_res.messages.len()); - // beneficiary can release it + // TRY PANIC let handle_params = mock_params(&deps.api, beneficiary.as_str(), &[], &coin("1000", "earth")); // panic inside contract should not panic out here // Note: we need to use the production-call, not the testing call (which unwraps any vm error) let handle_res = call_handle(&mut deps, &handle_params, &to_vec(&HandleMsg::Panic {}).unwrap()); assert!(handle_res.is_err()); + + // TRY INFINITE LOOP + // Note: we need to use the production-call, not the testing call (which unwraps any vm error) + deps.set_gas(1_000_000); + let handle_res = call_handle(&mut deps, &handle_params, &to_vec(&HandleMsg::CpuLoop {}).unwrap()); + assert!(handle_res.is_err()); + assert_eq!(deps.get_gas(), 0); } diff --git a/lib/vm/src/backends/singlepass.rs b/lib/vm/src/backends/singlepass.rs index b49a4a0aea..0c7e9fbe84 100644 --- a/lib/vm/src/backends/singlepass.rs +++ b/lib/vm/src/backends/singlepass.rs @@ -34,11 +34,19 @@ pub fn backend() -> Backend { } pub fn set_gas(instance: &mut Instance, limit: u64) { - let used = GAS_LIMIT - limit; + let used = if limit >= GAS_LIMIT { + GAS_LIMIT + } else { + GAS_LIMIT - limit + }; metering::set_points_used(instance, used) } pub fn get_gas(instance: &Instance) -> u64 { let used = metering::get_points_used(instance); - GAS_LIMIT - used + if used >= GAS_LIMIT { + 0 + } else { + GAS_LIMIT - used + } } From e559b64c7f7b4dd3724665316cea22d717a1b1b3 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 7 Feb 2020 18:47:44 +0100 Subject: [PATCH 5/7] Clean up naming, fmt --- contracts/hackatom/src/contract.rs | 5 +++-- contracts/hackatom/tests/integration.rs | 14 +++++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/contracts/hackatom/src/contract.rs b/contracts/hackatom/src/contract.rs index f7280d05fa..69098dbfdf 100644 --- a/contracts/hackatom/src/contract.rs +++ b/contracts/hackatom/src/contract.rs @@ -23,6 +23,7 @@ pub struct State { // failure modes to help test wasmd, based on this comment // https://github.com/cosmwasm/wasmd/issues/8#issuecomment-576146751 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "lowercase")] pub enum HandleMsg { // Release is the only "proper" action, releasing funds in the contract Release {}, @@ -103,7 +104,7 @@ fn cpu_loop() -> Result { let mut counter = 0u64; loop { counter += 1; - if counter >= 9000000000 { + if counter >= 9_000_000_000 { counter = 0; } } @@ -113,7 +114,7 @@ fn storage_loop(deps: &mut Extern) -> Result let mut test_case = 0u64; loop { deps.storage - .set("test.key".as_bytes(), test_case.to_string().as_bytes()); + .set(b"test.key", test_case.to_string().as_bytes()); test_case += 1; } } diff --git a/contracts/hackatom/tests/integration.rs b/contracts/hackatom/tests/integration.rs index 68bde023b5..e9d0ad6c7e 100644 --- a/contracts/hackatom/tests/integration.rs +++ b/contracts/hackatom/tests/integration.rs @@ -5,7 +5,7 @@ use cosmwasm::serde::{from_slice, to_vec}; use cosmwasm::traits::{Api, ReadonlyStorage}; use cosmwasm::types::{coin, CosmosMsg, HumanAddr, QueryResult}; -use cosmwasm_vm::{call_handle}; +use cosmwasm_vm::call_handle; use cosmwasm_vm::testing::{handle, init, mock_instance, query}; use hackatom::contract::{HandleMsg, InitMsg, QueryMsg, State, CONFIG_KEY}; @@ -214,13 +214,21 @@ fn handle_panic_and_loops() { let handle_params = mock_params(&deps.api, beneficiary.as_str(), &[], &coin("1000", "earth")); // panic inside contract should not panic out here // Note: we need to use the production-call, not the testing call (which unwraps any vm error) - let handle_res = call_handle(&mut deps, &handle_params, &to_vec(&HandleMsg::Panic {}).unwrap()); + let handle_res = call_handle( + &mut deps, + &handle_params, + &to_vec(&HandleMsg::Panic {}).unwrap(), + ); assert!(handle_res.is_err()); // TRY INFINITE LOOP // Note: we need to use the production-call, not the testing call (which unwraps any vm error) deps.set_gas(1_000_000); - let handle_res = call_handle(&mut deps, &handle_params, &to_vec(&HandleMsg::CpuLoop {}).unwrap()); + let handle_res = call_handle( + &mut deps, + &handle_params, + &to_vec(&HandleMsg::CpuLoop {}).unwrap(), + ); assert!(handle_res.is_err()); assert_eq!(deps.get_gas(), 0); } From fdb5ec11cae306be18c384cdb8b3cddd9db21db4 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 7 Feb 2020 18:50:48 +0100 Subject: [PATCH 6/7] Rebuild new test contract and fix tests --- contracts/hackatom/schema/handle_msg.json | 47 +++++++++++++++++++++- lib/vm/src/cache.rs | 6 +-- lib/vm/src/instance.rs | 6 +-- lib/vm/testdata/contract.wasm | Bin 62845 -> 66907 bytes 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/contracts/hackatom/schema/handle_msg.json b/contracts/hackatom/schema/handle_msg.json index 651f77de3c..b1b237b664 100644 --- a/contracts/hackatom/schema/handle_msg.json +++ b/contracts/hackatom/schema/handle_msg.json @@ -1,5 +1,50 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "HandleMsg", - "type": "object" + "anyOf": [ + { + "type": "object", + "required": [ + "release" + ], + "properties": { + "release": { + "type": "object" + } + } + }, + { + "type": "object", + "required": [ + "cpuloop" + ], + "properties": { + "cpuloop": { + "type": "object" + } + } + }, + { + "type": "object", + "required": [ + "storageloop" + ], + "properties": { + "storageloop": { + "type": "object" + } + } + }, + { + "type": "object", + "required": [ + "panic" + ], + "properties": { + "panic": { + "type": "object" + } + } + } + ] } \ No newline at end of file diff --git a/lib/vm/src/cache.rs b/lib/vm/src/cache.rs index 392f98d5f2..45dbe90ce2 100644 --- a/lib/vm/src/cache.rs +++ b/lib/vm/src/cache.rs @@ -188,7 +188,7 @@ mod test { &coin("15", "earth"), &coin("1015", "earth"), ); - let msg = b"{}"; + let msg = br#"{"release":{}}"#; let res = call_handle(&mut instance, ¶ms, msg).unwrap(); let msgs = res.unwrap().messages; assert_eq!(1, msgs.len()); @@ -230,7 +230,7 @@ mod test { &coin("15", "earth"), &coin("1015", "earth"), ); - let msg = b"{}"; + let msg = br#"{"release":{}}"#; let res = call_handle(&mut instance, ¶ms, msg).unwrap(); let msgs = res.unwrap().messages; assert_eq!(1, msgs.len()); @@ -244,7 +244,7 @@ mod test { &coin("15", "earth"), &coin("1015", "earth"), ); - let msg = b"{}"; + let msg = br#"{"release":{}}"#; let res = call_handle(&mut instance, ¶ms, msg).unwrap(); let msgs = res.unwrap().messages; assert_eq!(1, msgs.len()); diff --git a/lib/vm/src/instance.rs b/lib/vm/src/instance.rs index e8b3ade3e7..7fa2f2bfaf 100644 --- a/lib/vm/src/instance.rs +++ b/lib/vm/src/instance.rs @@ -166,7 +166,7 @@ mod test { let init_used = orig_gas - instance.get_gas(); println!("init used: {}", init_used); - assert_eq!(init_used, 66_763); + assert_eq!(init_used, 66_765); // run contract - just sanity check - results validate in contract unit tests instance.set_gas(orig_gas); @@ -176,14 +176,14 @@ mod test { &coin("15", "earth"), &coin("1015", "earth"), ); - let msg = b"{}"; + let msg = br#"{"release":{}}"#; let res = call_handle(&mut instance, ¶ms, msg).unwrap(); let msgs = res.unwrap().messages; assert_eq!(1, msgs.len()); let handle_used = orig_gas - instance.get_gas(); println!("handle used: {}", handle_used); - assert_eq!(handle_used, 110_267); + assert_eq!(handle_used, 112_832); } #[test] diff --git a/lib/vm/testdata/contract.wasm b/lib/vm/testdata/contract.wasm index b616aac4c3812738ad8e84ca7364d32160bbb99f..cf773511db60d90b48c08d89ac53bd4f80ddc3db 100644 GIT binary patch delta 18737 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 Date: Sat, 8 Feb 2020 22:33:28 +0100 Subject: [PATCH 7/7] Cleanup from PR --- contracts/hackatom/src/contract.rs | 15 +++++++-------- contracts/hackatom/tests/integration.rs | 3 ++- lib/vm/src/backends/singlepass.rs | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/contracts/hackatom/src/contract.rs b/contracts/hackatom/src/contract.rs index 69098dbfdf..8a3a2b460c 100644 --- a/contracts/hackatom/src/contract.rs +++ b/contracts/hackatom/src/contract.rs @@ -68,14 +68,14 @@ pub fn handle( msg: HandleMsg, ) -> Result { match msg { - HandleMsg::Release {} => try_release(deps, params), - HandleMsg::CpuLoop {} => cpu_loop(), - HandleMsg::StorageLoop {} => storage_loop(deps), + HandleMsg::Release {} => do_release(deps, params), + HandleMsg::CpuLoop {} => do_cpu_loop(), + HandleMsg::StorageLoop {} => do_storage_loop(deps), HandleMsg::Panic {} => do_panic(), } } -fn try_release(deps: &mut Extern, params: Params) -> Result { +fn do_release(deps: &mut Extern, params: Params) -> Result { let data = deps .storage .get(CONFIG_KEY) @@ -100,7 +100,7 @@ fn try_release(deps: &mut Extern, params: Params) -> R } } -fn cpu_loop() -> Result { +fn do_cpu_loop() -> Result { let mut counter = 0u64; loop { counter += 1; @@ -110,7 +110,7 @@ fn cpu_loop() -> Result { } } -fn storage_loop(deps: &mut Extern) -> Result { +fn do_storage_loop(deps: &mut Extern) -> Result { let mut test_case = 0u64; loop { deps.storage @@ -319,7 +319,7 @@ mod tests { } #[test] - #[should_panic] + #[should_panic(expected = "This page intentionally faulted")] fn handle_panic() { let mut deps = dependencies(20); @@ -341,7 +341,6 @@ mod tests { let init_res = init(&mut deps, init_params, init_msg).unwrap(); assert_eq!(0, init_res.messages.len()); - // beneficiary can release it let handle_params = mock_params(&deps.api, beneficiary.as_str(), &[], &coin("1000", "earth")); // this should panic diff --git a/contracts/hackatom/tests/integration.rs b/contracts/hackatom/tests/integration.rs index e9d0ad6c7e..32642967d8 100644 --- a/contracts/hackatom/tests/integration.rs +++ b/contracts/hackatom/tests/integration.rs @@ -191,6 +191,8 @@ fn failed_handle() { #[test] fn handle_panic_and_loops() { let mut deps = mock_instance(WASM); + // Gas must be set so we die early on infinite loop + deps.set_gas(1_000_000); // initialize the store let verifier = HumanAddr(String::from("verifies")); @@ -223,7 +225,6 @@ fn handle_panic_and_loops() { // TRY INFINITE LOOP // Note: we need to use the production-call, not the testing call (which unwraps any vm error) - deps.set_gas(1_000_000); let handle_res = call_handle( &mut deps, &handle_params, diff --git a/lib/vm/src/backends/singlepass.rs b/lib/vm/src/backends/singlepass.rs index 0c7e9fbe84..c3eb1f7174 100644 --- a/lib/vm/src/backends/singlepass.rs +++ b/lib/vm/src/backends/singlepass.rs @@ -34,8 +34,8 @@ pub fn backend() -> Backend { } pub fn set_gas(instance: &mut Instance, limit: u64) { - let used = if limit >= GAS_LIMIT { - GAS_LIMIT + let used = if limit > GAS_LIMIT { + 0 } else { GAS_LIMIT - limit }; @@ -44,7 +44,7 @@ pub fn set_gas(instance: &mut Instance, limit: u64) { pub fn get_gas(instance: &Instance) -> u64 { let used = metering::get_points_used(instance); - if used >= GAS_LIMIT { + if used > GAS_LIMIT { 0 } else { GAS_LIMIT - used