From 0e0b5b7613e07fcbaae15fdb37ea31649dfc58aa Mon Sep 17 00:00:00 2001 From: bugarela Date: Tue, 14 May 2024 15:51:59 -0300 Subject: [PATCH 01/19] Use `allListsUpTo` for nondet lists, and collect nondet picks --- src/nondet.rs | 28 +++++++--------------------- src/quint-lib-files/basicSpells.qnt | 15 +++++++++++++++ src/types.rs | 1 + 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/nondet.rs b/src/nondet.rs index 635414d..bbe894f 100644 --- a/src/nondet.rs +++ b/src/nondet.rs @@ -17,15 +17,16 @@ pub trait NondetValue: Translatable { fn nondet_definition(&self, ctx: &mut Context, ident: &str) -> String { let info = self.nondet_info(ctx, ident); let ty = self.translate(ctx); - nondet_info_to_def(info, &ty, ident) + nondet_info_to_def(ctx, info, &ty, ident) } } /// Converts a NondetInfo to a definition for a nondeterministic value with the /// given identifier, ready to be printed. -fn nondet_info_to_def(info: NondetInfo, ty: &str, ident: &str) -> String { +fn nondet_info_to_def(ctx: &mut Context, info: NondetInfo, ty: &str, ident: &str) -> String { let (auxiliary_defs, value) = info; if auxiliary_defs.is_empty() { + ctx.nondet_picks.push((ident.to_string(), ty.to_string())); format!("nondet {}: {} = {}.oneOf()", ident, ty, value) } else { format!( @@ -82,25 +83,9 @@ fn nondet_value_for_list(ty: &rustc_hir::Ty<'_>, ctx: &mut Context, ident: &str) return (element_defs, format!("[{element_value}]")); } - let defs = vec![ - format!( - "val possibilities = {}.map(i => Some(i)).union(Set(None))", - element_value - ), - "nondet v1 = possibilities.oneOf()".to_string(), - "nondet v2 = possibilities.oneOf()".to_string(), - "nondet v3 = possibilities.oneOf()".to_string(), - ]; - - let value = " - [v1, v2, v3].foldl([], (acc, v) => match v { - | Some(i) => acc.append(i) - | None => acc - }) -" - .to_string(); + let value = format!("{element_value}.allListsUpTo(3).oneOf()"); - (defs, value) + (vec![], value) } fn nondet_value_for_option(ty: &rustc_hir::Ty<'_>, ctx: &mut Context, ident: &str) -> NondetInfo { @@ -122,13 +107,14 @@ fn nondet_value_for_option(ty: &rustc_hir::Ty<'_>, ctx: &mut Context, ident: &st } impl NondetValue for Vec { - fn nondet_info(&self, _ctx: &mut Context, ident: &str) -> NondetInfo { + fn nondet_info(&self, ctx: &mut Context, ident: &str) -> NondetInfo { // Each field needs its own named nondet value, in the form of // `nondet ident_field_name: field_type = something.oneOf()` let defs = self .iter() .map(|field| { nondet_info_to_def( + ctx, field.nondet_info.clone(), &field.ty, format!("{}_{}", ident, field.name).as_str(), diff --git a/src/quint-lib-files/basicSpells.qnt b/src/quint-lib-files/basicSpells.qnt index 02f8c54..5e788f0 100644 --- a/src/quint-lib-files/basicSpells.qnt +++ b/src/quint-lib-files/basicSpells.qnt @@ -171,4 +171,19 @@ module basicSpells { pure def listFilter(l: List[a], f: (a) => bool): List[a] = l.foldl([], (acc, e) => if (f(e)) acc.append(e) else acc) + + pure def listMap(l: List[a], f: (a) => b): List[b] = + l.foldl([], (acc, e) => acc.append(f(e))) + + //// Returns a set of the elements in the list. + //// + //// - @param __list a list + //// - @returns a set of the elements in __list + pure def toSet(__list: List[a]): Set[a] = { + __list.foldl(Set(), (__s, __e) => __s.union(Set(__e))) + } + + pure def allListsUpTo(s: Set[a], max_length: int): Set[List[a]] = { + s.allLists().filter(l => l.length() <= max_length) + } } diff --git a/src/types.rs b/src/types.rs index 8536500..8bc37b4 100644 --- a/src/types.rs +++ b/src/types.rs @@ -36,6 +36,7 @@ pub struct Context<'tcx> { pub ops_with_mutability: Vec, pub tcx: TyCtxt<'tcx>, pub contract_state: Vec<(String, String)>, + pub nondet_picks: Vec<(String, String)>, // scoped // FIXME: This should be a stack to account for nested scopes. // No need to worry about nested scopes for stub generation. From 34fe6e9449568db265fce463dfe25bf071c28334 Mon Sep 17 00:00:00 2001 From: bugarela Date: Tue, 14 May 2024 15:52:40 -0300 Subject: [PATCH 02/19] Rename return -> result to avoid rust's keyword on tests --- src/boilerplate.rs | 14 +++++++------- src/quint-lib-files/messaging.qnt | 18 +++++++++--------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/boilerplate.rs b/src/boilerplate.rs index 32a436e..a22adf6 100644 --- a/src/boilerplate.rs +++ b/src/boilerplate.rs @@ -11,7 +11,7 @@ pub const IMPORTS: &str = " pub const VARS: &str = " var contract_state: ContractState - var return: Result + var result: Result var bank: bank::Bank var time: int "; @@ -34,7 +34,7 @@ pub const INITIALIZERS: &str = " action init = all { contract_state' = init_contract_state, bank' = init_bank_state, - return' = Err(\"No previous request\"), + result' = Err(\"No previous request\"), time' = 0, } "; @@ -52,7 +52,7 @@ pub const ACTIONS: &str = " bank.get(sender).get(denom) >= amount, bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)), - return' = r._1, + result' = r._1, contract_state' = r._2, } } @@ -60,16 +60,16 @@ pub const ACTIONS: &str = " action advance_time = time' = time + 1 action step = { - val message_getting = get_message(return) - val new_return = message_getting._1 + val message_getting = get_message(result) + val new_result = message_getting._1 val opt_message = message_getting._2 match opt_message { | Some(submsg) => { - val current_state = { bank: bank, return: new_return, contract_state: contract_state } + val current_state = { bank: bank, result: new_result, contract_state: contract_state } val new_state = process_message(current_state, env_val, CONTRACT_ADDRESS, submsg, reply) all { bank' = new_state.bank, - return' = new_state.return, + result' = new_state.result, contract_state' = new_state.contract_state, advance_time, } diff --git a/src/quint-lib-files/messaging.qnt b/src/quint-lib-files/messaging.qnt index 32ea905..b0ef7dc 100644 --- a/src/quint-lib-files/messaging.qnt +++ b/src/quint-lib-files/messaging.qnt @@ -5,18 +5,18 @@ module messaging { import bank from "./bank" import basicSpells.* from "./basicSpells" - pure def get_message(return_val: Result[Response, ContractError]): (Result[Response, ContractError], Option[SubMsg]) = { - match return_val { + pure def get_message(result_val: Result[Response, ContractError]): (Result[Response, ContractError], Option[SubMsg]) = { + match result_val { | Ok(response) => if (response.messages.indices().size() > 0) { val submsg = response.messages.head() - val new_return = Ok({ ...response, messages: response.messages.listFilter(m => m != submsg) }) - (new_return, Some(submsg)) + val new_result = Ok({ ...response, messages: response.messages.listFilter(m => m != submsg) }) + (new_result, Some(submsg)) } else { - (return_val, None) + (result_val, None) } - | _ => (return_val, None) + | _ => (result_val, None) } } @@ -29,16 +29,16 @@ module messaging { | Ok(new_bank) => if (should_reply_on_success(submsg.reply_on)) { val reply_result = reply(state.contract_state, env, { id: submsg.id, result: Ok({ events: [/* TODO */], data: None }) }) - { bank: new_bank, return: reply_result._1, contract_state: reply_result._2 } + { bank: new_bank, result: reply_result._1, contract_state: reply_result._2 } } else { { ...state, bank: new_bank } } | Err(err) => if (should_reply_on_error(submsg.reply_on)) { val reply_result = reply(state.contract_state, env, { id: submsg.id, result: Err(err) }) - { ...state, return: reply_result._1, contract_state: reply_result._2 } + { ...state, result: reply_result._1, contract_state: reply_result._2 } } else { - state + { ...state, result: Err(err) } } } } From 7a117508ef0b5878a088beb70ce400f953ef72c7 Mon Sep 17 00:00:00 2001 From: bugarela Date: Tue, 14 May 2024 15:53:38 -0300 Subject: [PATCH 03/19] Add translation for the structs part of test --- src/lib.rs | 9 +++ src/test_generation/mod.rs | 1 + src/test_generation/structs.rs | 139 +++++++++++++++++++++++++++++++++ src/translate.rs | 3 + 4 files changed, 152 insertions(+) create mode 100644 src/test_generation/mod.rs create mode 100644 src/test_generation/structs.rs diff --git a/src/lib.rs b/src/lib.rs index bc347ca..6ddc0b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ pub mod boilerplate; pub mod nondet; pub mod state_extraction; +pub mod test_generation; pub mod translate; pub mod types; @@ -30,6 +31,7 @@ use translate::Translatable; use crate::types::{Constructor, Context}; use crate::boilerplate::{post_items, pre_items}; +use crate::test_generation::structs::translate_structs; // This struct is the plugin provided to the rustc_plugin framework, // and it must be exported for use by the CLI/driver binaries. @@ -147,6 +149,11 @@ fn traslate_items(tcx: TyCtxt, crate_name: &str, items: Vec<&rustc_hir::Item>) { structs: HashMap::new(), ops_with_mutability: vec![], contract_state: vec![], + nondet_picks: vec![ + ("sender".to_string(), "Addr".to_string()), + ("denom".to_string(), "str".to_string()), + ("amount".to_string(), "int".to_string()), + ], // scoped record_fields: vec![], struct_fields: vec![], @@ -173,6 +180,8 @@ fn traslate_items(tcx: TyCtxt, crate_name: &str, items: Vec<&rustc_hir::Item>) { println!("{}", pre_items(crate_name)); println!("{}", translated_items); println!("{}", post_items(&ctx)); + println!("-----------------"); + println!("{}", translate_structs(ctx)); } // This is the main entry point for the plugin. It prints the generated quint code to STDOUT. diff --git a/src/test_generation/mod.rs b/src/test_generation/mod.rs new file mode 100644 index 0000000..21dab61 --- /dev/null +++ b/src/test_generation/mod.rs @@ -0,0 +1 @@ +pub mod structs; diff --git a/src/test_generation/structs.rs b/src/test_generation/structs.rs new file mode 100644 index 0000000..8ea2757 --- /dev/null +++ b/src/test_generation/structs.rs @@ -0,0 +1,139 @@ +use crate::types::Context; +use itertools::Itertools; + +pub fn translate_structs(ctx: Context) -> String { + let mut structs = " +pub mod state_structs { + use num_bigint::BigInt; + use serde::Deserialize; + use std::collections::HashMap; +" + .to_string(); + for (name, fields) in ctx.structs { + structs.push_str( + format_struct( + name, + fields + .iter() + .map(|f| (f.name.clone(), f.ty.clone())) + .collect_vec(), + false, + ) + .as_str(), + ); + } + structs.push_str( + format_struct( + "ContractState".to_string(), + ctx.contract_state.clone(), + false, + ) + .as_str(), + ); + + structs.push_str( + format_struct("NondetPicks".to_string(), ctx.nondet_picks.clone(), true).as_str(), + ); + + structs.push_str(BOILERPLATE_STRUCTS); + + format!("{}\n}}", structs) +} + +fn format_struct(name: String, fields: Vec<(String, String)>, optional: bool) -> String { + let fields = fields + .iter() + .map(|(name, ty)| { + let actual_type = translate_type(ty.clone()); + let typ = if optional { + format!("QuintOption<{}>", actual_type) + } else { + actual_type + }; + format!(" {}: {}", name, typ) + }) + .join(",\n"); + format!( + " #[derive(Clone, Debug, Deserialize)]\n pub struct {} {{\n{}\n }}\n\n", + name, fields + ) +} + +fn translate_type(ty: String) -> String { + if ty.contains("->") { + let it = ty.split("->").collect_vec(); + let key = it[0]; + let value = it[1..].join("->"); + return format!( + "HashMap<{}, {}>", + translate_type(key.trim().to_string()), + translate_type(value.trim().to_string()) + ); + } + + if ty.starts_with("List") { + return format!( + "Vec<{}>", + translate_type( + ty.trim_start_matches("List[") + .trim_end_matches(']') + .to_string() + ) + ); + } + + match ty.as_str() { + "str" => "String".to_string(), + "int" => "BigInt".to_string(), + "Addr" => "String".to_string(), + _ => ty, + } +} + +const BOILERPLATE_STRUCTS: &str = " + #[derive(Copy, Clone, Debug, Deserialize)] + #[serde(tag = \"tag\", content = \"value\")] + pub enum QuintOption { + Some(T), + None, + } + + #[derive(Copy, Clone, Debug, Deserialize)] + #[serde(tag = \"tag\", content = \"value\")] + pub enum QuintResult { + Ok(T), + Err(E), + } + + #[derive(Clone, Debug, Deserialize)] + pub struct Message {} + + #[derive(Clone, Debug, Deserialize)] + pub struct Response { + pub messages: Vec, + } + + #[derive(Clone, Debug, Deserialize)] + pub struct State { + pub contract_state: ContractState, + pub bank: HashMap>, + pub result: QuintResult, + pub action_taken: String, + pub nondet_picks: NondetPicks, + pub time: BigInt, + } + + pub fn to_option(op: QuintOption) -> Option { + match op { + QuintOption::None => None, + QuintOption::Some(a) => Some(a), + } + } + + pub fn to_result(res: QuintResult) -> Result { + match res { + QuintResult::Ok(a) => Ok(a), + QuintResult::Err(e) => Err(e), + } + } +"; diff --git a/src/translate.rs b/src/translate.rs index 19012e2..6a63883 100644 --- a/src/translate.rs +++ b/src/translate.rs @@ -485,6 +485,9 @@ impl Translatable for rustc_hir::Item<'_> { || name.starts_with("ContractError") // skip items from proto files || format!("{:?}", self.span).contains("protos") + // skip items from generated test file + // TODO: use parameterized name instead of hardcoded + || format!("{:?}", self.span).contains("src/mbt.rs") { // skip irrelevant items return "".to_string(); From 7072cfe1a41993bfbc4c1ecb3fef41318e092329 Mon Sep 17 00:00:00 2001 From: bugarela Date: Tue, 14 May 2024 17:43:56 -0300 Subject: [PATCH 04/19] Generate test part for user-defined actions --- src/lib.rs | 4 +- src/test_generation/actions.rs | 94 +++++++++++++++++++ src/test_generation/boilerplate.rs | 141 +++++++++++++++++++++++++++++ src/test_generation/mod.rs | 16 +++- 4 files changed, 252 insertions(+), 3 deletions(-) create mode 100644 src/test_generation/actions.rs create mode 100644 src/test_generation/boilerplate.rs diff --git a/src/lib.rs b/src/lib.rs index 6ddc0b5..35c96bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,7 +31,7 @@ use translate::Translatable; use crate::types::{Constructor, Context}; use crate::boilerplate::{post_items, pre_items}; -use crate::test_generation::structs::translate_structs; +use crate::test_generation::generate_tests; // This struct is the plugin provided to the rustc_plugin framework, // and it must be exported for use by the CLI/driver binaries. @@ -181,7 +181,7 @@ fn traslate_items(tcx: TyCtxt, crate_name: &str, items: Vec<&rustc_hir::Item>) { println!("{}", translated_items); println!("{}", post_items(&ctx)); println!("-----------------"); - println!("{}", translate_structs(ctx)); + println!("{}", generate_tests(ctx.clone())); } // This is the main entry point for the plugin. It prints the generated quint code to STDOUT. diff --git a/src/test_generation/actions.rs b/src/test_generation/actions.rs new file mode 100644 index 0000000..0b3a535 --- /dev/null +++ b/src/test_generation/actions.rs @@ -0,0 +1,94 @@ +use crate::types::Context; +use itertools::Itertools; + +pub fn translate_actions(ctx: Context) -> String { + let msgs = ctx.message_type_for_action.iter().map(|(action, ty)| { + if action == "execute" || action == "instantiate" || action == "reply" { + return "".to_string(); + } + let constructor = ctx.constructors.get(ty.as_str()).unwrap(); + let nondet_picks = constructor + .fields + .iter() + .map(|f| { + let body = type_conversion( + format!("to_option(nondet_picks.{}.clone()).unwrap()", f.name), + f.ty.clone(), + ); + + format!(" let message_{} = {};", f.name, body) + }) + .collect_vec(); + + let fields = constructor + .fields + .iter() + .map(|f| format!("{}: message_{}", f.name, f.name)) + .collect_vec() + .join(", "); + let msg = format!("{} {{ {} }}", constructor.name, fields); + + translate_action(action, msg, nondet_picks.clone()) + }); + + msgs.clone().join("\n") +} + +fn translate_action(action: &str, msg: String, nondet_picks: Vec) -> String { + let header = format!( + " + \"{}_action\" => {{ + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + +", + action + ); + + let footer = " + println!(\"Message: {:?}\", msg); + println!(\"Sender: {:?}\", sender); + println!(\"Funds: {:?}\", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(to_result(s.value.result.clone()), res) + }} + +"; + + format!( + "{}{}\n let msg = {};{}", + header, + nondet_picks.clone().join("\n"), + msg, + footer, + ) +} + +fn type_conversion(value: String, ty: String) -> String { + if ty.starts_with("List") { + return format!( + "{}.iter().map(|x| {}).collect()", + value, + type_conversion( + "x".to_string(), + ty.trim_start_matches("List[") + .trim_end_matches(']') + .to_string() + ) + ); + } + + match ty.as_str() { + "str" => value, + "int" => format!("{}.to_u64().unwrap()", value), + "Addr" => format!("Addr::unchecked_from({})", value), + _ => value, + } +} diff --git a/src/test_generation/boilerplate.rs b/src/test_generation/boilerplate.rs new file mode 100644 index 0000000..5e70349 --- /dev/null +++ b/src/test_generation/boilerplate.rs @@ -0,0 +1,141 @@ +pub const TEST_HEADER: &str = " +#[cfg(test)] +pub mod tests { + use crate::{ + contract::{DENOM, LOCK_PERIOD}, + mbt::state_structs::*, + msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, + }; + use cosmwasm_std::{coin, Addr, Uint128}; + use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; + use itf::trace_from_str; + use num_bigint::BigInt; + use num_traits::{ToPrimitive, Zero}; + + pub const ADMIN: &str = \"admin\"; + + pub fn mint_tokens(mut app: App, recipient: String, denom: String, amount: Uint128) -> App { + app.sudo(cw_multi_test::SudoMsg::Bank( + cw_multi_test::BankSudo::Mint { + to_address: recipient.to_owned(), + amount: vec![coin(amount.u128(), denom)], + }, + )) + .unwrap(); + app + } + + fn compare_state(test_state: &TestState, app: &App, state: &State) { + // compare contract balances + let balance = app + .wrap() + .query_balance(&test_state.contract_addr, DENOM) + .unwrap() + .amount; + let trace_balance = state + .bank + .get(&test_state.contract_addr.to_string()) + .and_then(|x| x.get(DENOM)) + .and_then(|x| x.to_u128()) + .unwrap_or(0); + println!( + \"Contract balance ({:?}) for {DENOM}: {:?} vs {:?}\", + test_state.contract_addr, + balance, + Uint128::new(trace_balance) + ); + assert_eq!(balance, Uint128::new(trace_balance)); + + // TODO: Query the contract and compare the state as you wish + } + + fn compare_result( + trace_result: Result, + app_result: Result, + ) { + if trace_result.is_ok() { + assert!( + app_result.is_ok(), + \"Action unexpectedly failed, error: {:?}\", + app_result.err() + ); + println!(\"Action successful as expected\"); + } else { + assert!( + app_result.is_err(), + \"Expected action to fail with error: {:?}\", + trace_result.err() + ); + println!(\"Action failed as expected\"); + } + } + + fn funds_from_trace(amount: Option, denom: Option) -> Vec { + if amount.is_none() || denom.is_none() || amount == Some(Zero::zero()) { + return vec![]; + } + + vec![coin( + amount.as_ref().unwrap().to_u128().unwrap(), + denom.unwrap(), + )] + } + + // Testing is stateful. + struct TestState { + // we will only know the contract address once we have processed an `instantiate` step + pub contract_addr: Addr, + } + + #[test] + fn model_test() { + let mut app = App::default(); + let code = ContractWrapper::new( + crate::contract::execute, + crate::contract::instantiate, + crate::contract::query, + ); + let code_id = app.store_code(Box::new(code)); + + // create test state + let mut test_state = TestState { + contract_addr: Addr::unchecked(\"contract0\"), + }; + + // load trace data + let data = include_str!(\"./out/test.itf.json\"); + let trace: itf::Trace = trace_from_str(data).unwrap(); + + for s in trace.states { + let last_result = to_result(s.value.result.clone()); + if last_result.is_ok() && !last_result.unwrap().messages.is_empty() { + println!(\"Processing messages, skipping\"); + continue; + } + + let action_taken = &s.value.action_taken; + let nondet_picks = &s.value.nondet_picks; + let amount = to_option(nondet_picks.amount.clone()); + let denom = to_option(nondet_picks.denom.clone()); + let sender = to_option(nondet_picks.sender.clone()); + + println!(\"Step number: {:?}\", s.meta.index); + + match action_taken.as_str() { +"; + +pub const TEST_FOOTER: &str = " + _ => panic!(\"Invalid action taken\"), + } + compare_state(&test_state, &app, &(s.value.clone())); + println!( + \"clock is advancing for {} seconds (LOCK_PERIOD)\", + LOCK_PERIOD + ); + app.update_block(|block| { + block.time = block.time.plus_seconds(LOCK_PERIOD); + }); + println!(\"-----------------------------------\"); + } + } +"; diff --git a/src/test_generation/mod.rs b/src/test_generation/mod.rs index 21dab61..ddb2796 100644 --- a/src/test_generation/mod.rs +++ b/src/test_generation/mod.rs @@ -1 +1,15 @@ -pub mod structs; +use crate::types::Context; + +mod actions; +mod boilerplate; +mod structs; + +pub fn generate_tests(ctx: Context) -> String { + format!( + "{}{}{}{}", + structs::translate_structs(ctx.clone()), + boilerplate::TEST_HEADER, + actions::translate_actions(ctx), + boilerplate::TEST_FOOTER + ) +} From 39fca813e4111c3c73f53a60fe4c6adec1ecbb24 Mon Sep 17 00:00:00 2001 From: bugarela Date: Wed, 15 May 2024 15:47:28 -0300 Subject: [PATCH 05/19] Initialize model through instantiate --- src/boilerplate.rs | 53 ++++++++++++++++++-------- src/test_generation/actions.rs | 68 ++++++++++++++++++++++++++++++++-- 2 files changed, 102 insertions(+), 19 deletions(-) diff --git a/src/boilerplate.rs b/src/boilerplate.rs index a22adf6..e723b94 100644 --- a/src/boilerplate.rs +++ b/src/boilerplate.rs @@ -26,19 +26,6 @@ pub const VALUES: &str = " pure val MAX_AMOUNT = 200 "; -pub const INITIALIZERS: &str = " - pure val init_bank_state = ADDRESSES.mapBy(_ => DENOMS.mapBy(_ => MAX_AMOUNT)) - - val env_val = { block: { time: time } } - - action init = all { - contract_state' = init_contract_state, - bank' = init_bank_state, - result' = Err(\"No previous request\"), - time' = 0, - } -"; - pub const ACTIONS: &str = " action execute_message(message, max_funds) = { nondet sender = ADDRESSES.oneOf() @@ -90,10 +77,43 @@ pub fn pre_items(crate_name: &str) -> String { ) } +pub fn initializers(ctx: &Context) -> String { + let instatiate_msg = ctx.message_type_for_action.get("instantiate").unwrap(); + format!( + " + pure val init_bank_state = ADDRESSES.mapBy(_ => DENOMS.mapBy(_ => MAX_AMOUNT)) + + val env_val = {{ block: {{ time: time }} }} + + action init = {{ + // TODO: Change next line according to fund expectations + pure val max_funds = 0 + + nondet sender = Set(\"admin\").oneOf() + nondet denom = DENOMS.oneOf() + nondet amount = 0.to(max_funds).oneOf() + val funds = [{{ denom: denom, amount: amount }}] + val info = {{ sender: sender, funds: funds }} + + pure val message: InstantiateMsg = {} + pure val r = instantiate(init_contract_state, {{ block: {{ time: 0 }} }}, info, message) + + all {{ + contract_state' = r._2, + bank' = init_bank_state, + result' = r._1, + time' = 0, + }} + }} +", + init_value_for_type(ctx, instatiate_msg.clone()) + ) +} + // TODO: This doesn't belong here, but I'm not sure where to put it /// Generates a default value for a given type, used to initialize values of all /// contract state fields -fn init_value_for_type(ctx: &Context, ty: String) -> String { +pub fn init_value_for_type(ctx: &Context, ty: String) -> String { if ty.contains("->") { return "Map()".to_string(); } @@ -190,8 +210,9 @@ pub fn post_items(ctx: &Context) -> String { advance_time, }} {reply} -{INITIALIZERS} +{} {ACTIONS} -}}" +}}", + initializers(ctx) ) } diff --git a/src/test_generation/actions.rs b/src/test_generation/actions.rs index 0b3a535..c812a07 100644 --- a/src/test_generation/actions.rs +++ b/src/test_generation/actions.rs @@ -1,8 +1,22 @@ +use crate::boilerplate::init_value_for_type; use crate::types::Context; use itertools::Itertools; pub fn translate_actions(ctx: Context) -> String { let msgs = ctx.message_type_for_action.iter().map(|(action, ty)| { + if action == "instantiate" { + let msg_struct = ctx.structs.get("InstantiateMsg").unwrap(); + let msg_fields = msg_struct + .iter() + .map(|f| { + let body = init_value_for_type(&ctx, f.ty.clone()); + + format!("{}: {}", f.name, body) + }) + .collect_vec(); + let msg = format!("InstantiateMsg {{ {} }}", msg_fields.join(", ")); + return translate_init(msg); + } if action == "execute" || action == "instantiate" || action == "reply" { return "".to_string(); } @@ -12,7 +26,10 @@ pub fn translate_actions(ctx: Context) -> String { .iter() .map(|f| { let body = type_conversion( - format!("to_option(nondet_picks.{}.clone()).unwrap()", f.name), + format!( + "to_option(nondet_picks.message_{}.clone()).unwrap()", + f.name + ), f.ty.clone(), ); @@ -26,7 +43,7 @@ pub fn translate_actions(ctx: Context) -> String { .map(|f| format!("{}: message_{}", f.name, f.name)) .collect_vec() .join(", "); - let msg = format!("{} {{ {} }}", constructor.name, fields); + let msg = format!("{} {{ {} }}", constructor.name.replace('_', "::"), fields); translate_action(action, msg, nondet_picks.clone()) }); @@ -58,7 +75,7 @@ fn translate_action(action: &str, msg: String, nondet_picks: Vec) -> Str ); compare_result(to_result(s.value.result.clone()), res) - }} + } "; @@ -71,6 +88,51 @@ fn translate_action(action: &str, msg: String, nondet_picks: Vec) -> Str ) } +fn translate_init(msg: String) -> String { + let header = " + \"q::init\" => { + println!(\"Initializing contract.\"); + + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + +" + .to_string(); + + let footer = " + println!(\"Message: {:?}\", msg); + println!(\"Sender: {:?}\", sender); + println!(\"Funds: {:?}\", funds); + + test_state.contract_addr = app.instantiate_contract( + code_id, + sender, + &msg, + &funds, + \"test\", + None, + ).unwrap(); + + for (addr, coins) in s.value.bank.clone().iter() { + for (denom, amount) in coins.iter() { + app = mint_tokens( + app, + addr.clone(), + denom.to_string(), + Uint128::new(amount.to_u128().unwrap()), + ); + } + } + } + +"; + + format!( + "{}\n let msg = {};{}", + header, msg, footer, + ) +} + fn type_conversion(value: String, ty: String) -> String { if ty.starts_with("List") { return format!( From 4e4068956025cac06093efe9b1443d0220f3519c Mon Sep 17 00:00:00 2001 From: bugarela Date: Wed, 15 May 2024 15:48:05 -0300 Subject: [PATCH 06/19] Fix small syntax issues --- src/test_generation/boilerplate.rs | 1 + src/test_generation/structs.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test_generation/boilerplate.rs b/src/test_generation/boilerplate.rs index 5e70349..8fd9e39 100644 --- a/src/test_generation/boilerplate.rs +++ b/src/test_generation/boilerplate.rs @@ -138,4 +138,5 @@ pub const TEST_FOOTER: &str = " println!(\"-----------------------------------\"); } } +} "; diff --git a/src/test_generation/structs.rs b/src/test_generation/structs.rs index 8ea2757..16d0fbe 100644 --- a/src/test_generation/structs.rs +++ b/src/test_generation/structs.rs @@ -50,7 +50,7 @@ fn format_struct(name: String, fields: Vec<(String, String)>, optional: bool) -> } else { actual_type }; - format!(" {}: {}", name, typ) + format!(" pub {}: {}", name, typ) }) .join(",\n"); format!( From 6e51b374d009b25a5543d1b9cdc7f57bb325c7ac Mon Sep 17 00:00:00 2001 From: bugarela Date: Wed, 15 May 2024 18:23:57 -0300 Subject: [PATCH 07/19] Write to files and other tweaks --- src/boilerplate.rs | 20 ++++++++++------- src/lib.rs | 35 +++++++++++++++++++++++++----- src/nondet.rs | 2 +- src/quint-lib-files/cw_utils.qnt | 4 ++-- src/test_generation/boilerplate.rs | 12 +++++----- 5 files changed, 51 insertions(+), 22 deletions(-) diff --git a/src/boilerplate.rs b/src/boilerplate.rs index e723b94..d1f47d7 100644 --- a/src/boilerplate.rs +++ b/src/boilerplate.rs @@ -3,10 +3,11 @@ use itertools::Itertools; use crate::types::Context; pub const IMPORTS: &str = " - import basicSpells.* from \"../lib/basicSpells\" - import cw_types.* from \"../lib/cw_types\" - import messaging.* from \"../lib/messaging\" - import bank from \"../lib/bank\" + import basicSpells.* from \"./lib/basicSpells\" + import cw_types.* from \"./lib/cw_types\" + import cw_utils.* from \"./lib/cw_utils\" + import messaging.* from \"./lib/messaging\" + import bank from \"./lib/bank\" "; pub const VARS: &str = " @@ -17,11 +18,11 @@ pub const VARS: &str = " "; pub const CONTRACT_ADDRESS: &str = " - pure val CONTRACT_ADDRESS = \"\" + pure val CONTRACT_ADDRESS = \"contract0\" "; pub const VALUES: &str = " - pure val ADDRESSES = Set(\"s1\", \"s2\", \"s3\", CONTRACT_ADDRESS) + pure val ADDRESSES = Set(\"sender1\", \"sender2\", \"sender3\", CONTRACT_ADDRESS) pure val DENOMS = Set(\"d1\", \"uawesome\") pure val MAX_AMOUNT = 200 "; @@ -37,8 +38,11 @@ pub const ACTIONS: &str = " val r = execute(contract_state, env_val, info, message) all { bank.get(sender).get(denom) >= amount, - bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) - .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)), + match r._1 { + | Ok(_) => bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) + .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)) + | Err(_) => bank' = bank + }, result' = r._1, contract_state' = r._2, } diff --git a/src/lib.rs b/src/lib.rs index 35c96bf..4a88067 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -177,11 +177,36 @@ fn traslate_items(tcx: TyCtxt, crate_name: &str, items: Vec<&rustc_hir::Item>) { return; } - println!("{}", pre_items(crate_name)); - println!("{}", translated_items); - println!("{}", post_items(&ctx)); - println!("-----------------"); - println!("{}", generate_tests(ctx.clone())); + let module = format!( + "{}\n{}\n{}\n", + pre_items(crate_name), + translated_items, + post_items(&ctx) + ); + let tests = generate_tests(ctx.clone()); + + // create generated directory + std::fs::create_dir_all("quint/lib").expect("Unable to create directory"); + + let bank = include_str!("./quint-lib-files/bank.qnt"); + let basic_spells = include_str!("./quint-lib-files/basicSpells.qnt"); + let bounded_uint = include_str!("./quint-lib-files/BoundedUInt.qnt"); + let cw_types = include_str!("./quint-lib-files/cw_types.qnt"); + let cw_utils = include_str!("./quint-lib-files/cw_utils.qnt"); + let messaging = include_str!("./quint-lib-files/messaging.qnt"); + + std::fs::write("quint/lib/bank.qnt", bank).expect("Unable to write file"); + std::fs::write("quint/lib/basicSpells.qnt", basic_spells).expect("Unable to write file"); + std::fs::write("quint/lib/BoundedUInt.qnt", bounded_uint).expect("Unable to write file"); + std::fs::write("quint/lib/cw_types.qnt", cw_types).expect("Unable to write file"); + std::fs::write("quint/lib/cw_utils.qnt", cw_utils).expect("Unable to write file"); + std::fs::write("quint/lib/messaging.qnt", messaging).expect("Unable to write file"); + + // write module to file + std::fs::write("quint/stubs.qnt", module).expect("Unable to write file"); + + // write tests to file + std::fs::write("src/mbt.rs", tests).expect("Unable to write file"); } // This is the main entry point for the plugin. It prints the generated quint code to STDOUT. diff --git a/src/nondet.rs b/src/nondet.rs index bbe894f..60b111c 100644 --- a/src/nondet.rs +++ b/src/nondet.rs @@ -83,7 +83,7 @@ fn nondet_value_for_list(ty: &rustc_hir::Ty<'_>, ctx: &mut Context, ident: &str) return (element_defs, format!("[{element_value}]")); } - let value = format!("{element_value}.allListsUpTo(3).oneOf()"); + let value = format!("{element_value}.allListsUpTo(2)"); (vec![], value) } diff --git a/src/quint-lib-files/cw_utils.qnt b/src/quint-lib-files/cw_utils.qnt index 65597a6..e662dbe 100644 --- a/src/quint-lib-files/cw_utils.qnt +++ b/src/quint-lib-files/cw_utils.qnt @@ -10,7 +10,7 @@ module cw_utils { } else if (info.funds.indices().size() == 1) { val coin = info.funds[0] if (coin.amount == 0) { - Err("No funds") + Err("Zero funds") } else { Ok(coin) } @@ -24,7 +24,7 @@ module cw_utils { | Err(err) => Err(err) | Ok(coin) => { if (coin.denom != denom) { - Err("Missing Denom") + Err("Wrong Denom") } else { Ok(coin.amount) } diff --git a/src/test_generation/boilerplate.rs b/src/test_generation/boilerplate.rs index 8fd9e39..3b79c9f 100644 --- a/src/test_generation/boilerplate.rs +++ b/src/test_generation/boilerplate.rs @@ -2,7 +2,6 @@ pub const TEST_HEADER: &str = " #[cfg(test)] pub mod tests { use crate::{ - contract::{DENOM, LOCK_PERIOD}, mbt::state_structs::*, msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, }; @@ -12,7 +11,8 @@ pub mod tests { use num_bigint::BigInt; use num_traits::{ToPrimitive, Zero}; - pub const ADMIN: &str = \"admin\"; + pub const DENOM: &str = \"uawesome\"; + pub const TICK: u64 = 1; pub fn mint_tokens(mut app: App, recipient: String, denom: String, amount: Uint128) -> App { app.sudo(cw_multi_test::SudoMsg::Bank( @@ -103,7 +103,7 @@ pub mod tests { }; // load trace data - let data = include_str!(\"./out/test.itf.json\"); + let data = include_str!(\"../quint/test.itf.json\"); let trace: itf::Trace = trace_from_str(data).unwrap(); for s in trace.states { @@ -129,11 +129,11 @@ pub const TEST_FOOTER: &str = " } compare_state(&test_state, &app, &(s.value.clone())); println!( - \"clock is advancing for {} seconds (LOCK_PERIOD)\", - LOCK_PERIOD + \"clock is advancing for {} seconds\", + TICK ); app.update_block(|block| { - block.time = block.time.plus_seconds(LOCK_PERIOD); + block.time = block.time.plus_seconds(TICK); }); println!(\"-----------------------------------\"); } From d689dc3ee33375b84427e11940dfe99bf2647b5b Mon Sep 17 00:00:00 2001 From: bugarela Date: Thu, 16 May 2024 08:15:41 -0300 Subject: [PATCH 08/19] Update with Option/Result from itf-rs 0.2.4 (thanks @romac) --- src/test_generation/actions.rs | 7 ++--- src/test_generation/boilerplate.rs | 8 ++--- src/test_generation/structs.rs | 48 ++++++++---------------------- 3 files changed, 19 insertions(+), 44 deletions(-) diff --git a/src/test_generation/actions.rs b/src/test_generation/actions.rs index c812a07..8a9c269 100644 --- a/src/test_generation/actions.rs +++ b/src/test_generation/actions.rs @@ -26,10 +26,7 @@ pub fn translate_actions(ctx: Context) -> String { .iter() .map(|f| { let body = type_conversion( - format!( - "to_option(nondet_picks.message_{}.clone()).unwrap()", - f.name - ), + format!("nondet_picks.message_{}.clone().unwrap()", f.name), f.ty.clone(), ); @@ -74,7 +71,7 @@ fn translate_action(action: &str, msg: String, nondet_picks: Vec) -> Str &funds, ); - compare_result(to_result(s.value.result.clone()), res) + compare_result(s.value.result.clone(), res) } "; diff --git a/src/test_generation/boilerplate.rs b/src/test_generation/boilerplate.rs index 3b79c9f..139a9d3 100644 --- a/src/test_generation/boilerplate.rs +++ b/src/test_generation/boilerplate.rs @@ -107,7 +107,7 @@ pub mod tests { let trace: itf::Trace = trace_from_str(data).unwrap(); for s in trace.states { - let last_result = to_result(s.value.result.clone()); + let last_result = s.value.result.clone(); if last_result.is_ok() && !last_result.unwrap().messages.is_empty() { println!(\"Processing messages, skipping\"); continue; @@ -115,9 +115,9 @@ pub mod tests { let action_taken = &s.value.action_taken; let nondet_picks = &s.value.nondet_picks; - let amount = to_option(nondet_picks.amount.clone()); - let denom = to_option(nondet_picks.denom.clone()); - let sender = to_option(nondet_picks.sender.clone()); + let amount = nondet_picks.amount.clone(); + let denom = nondet_picks.denom.clone(); + let sender = nondet_picks.sender.clone(); println!(\"Step number: {:?}\", s.meta.index); diff --git a/src/test_generation/structs.rs b/src/test_generation/structs.rs index 16d0fbe..943ef76 100644 --- a/src/test_generation/structs.rs +++ b/src/test_generation/structs.rs @@ -7,6 +7,7 @@ pub mod state_structs { use num_bigint::BigInt; use serde::Deserialize; use std::collections::HashMap; + use itf::de::{self, As}; " .to_string(); for (name, fields) in ctx.structs { @@ -44,13 +45,17 @@ fn format_struct(name: String, fields: Vec<(String, String)>, optional: bool) -> let fields = fields .iter() .map(|(name, ty)| { - let actual_type = translate_type(ty.clone()); - let typ = if optional { - format!("QuintOption<{}>", actual_type) + let typ = translate_type(ty.clone()); + if optional { + format!( + " + #[serde(with = \"As::>\")] + pub {}: Option<{}>", + name, typ + ) } else { - actual_type - }; - format!(" pub {}: {}", name, typ) + format!(" pub {}: {}", name, typ) + } }) .join(",\n"); format!( @@ -91,20 +96,6 @@ fn translate_type(ty: String) -> String { } const BOILERPLATE_STRUCTS: &str = " - #[derive(Copy, Clone, Debug, Deserialize)] - #[serde(tag = \"tag\", content = \"value\")] - pub enum QuintOption { - Some(T), - None, - } - - #[derive(Copy, Clone, Debug, Deserialize)] - #[serde(tag = \"tag\", content = \"value\")] - pub enum QuintResult { - Ok(T), - Err(E), - } - #[derive(Clone, Debug, Deserialize)] pub struct Message {} @@ -117,23 +108,10 @@ const BOILERPLATE_STRUCTS: &str = " pub struct State { pub contract_state: ContractState, pub bank: HashMap>, - pub result: QuintResult, + #[serde(with = \"As::>\")] + pub result: Result, pub action_taken: String, pub nondet_picks: NondetPicks, pub time: BigInt, } - - pub fn to_option(op: QuintOption) -> Option { - match op { - QuintOption::None => None, - QuintOption::Some(a) => Some(a), - } - } - - pub fn to_result(res: QuintResult) -> Result { - match res { - QuintResult::Ok(a) => Ok(a), - QuintResult::Err(e) => Err(e), - } - } "; From 47b5e6a71ac4dead9210e8a68b3dbb4737f8ce5e Mon Sep 17 00:00:00 2001 From: bugarela Date: Wed, 22 May 2024 09:23:35 -0300 Subject: [PATCH 09/19] Remove spell that is now a builtin --- src/quint-lib-files/basicSpells.qnt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/quint-lib-files/basicSpells.qnt b/src/quint-lib-files/basicSpells.qnt index 5e788f0..1c6d5e3 100644 --- a/src/quint-lib-files/basicSpells.qnt +++ b/src/quint-lib-files/basicSpells.qnt @@ -182,8 +182,4 @@ module basicSpells { pure def toSet(__list: List[a]): Set[a] = { __list.foldl(Set(), (__s, __e) => __s.union(Set(__e))) } - - pure def allListsUpTo(s: Set[a], max_length: int): Set[List[a]] = { - s.allLists().filter(l => l.length() <= max_length) - } } From 5ce01e09034d0e53781f7be4f665ab6d61aac75c Mon Sep 17 00:00:00 2001 From: bugarela Date: Wed, 22 May 2024 09:24:12 -0300 Subject: [PATCH 10/19] Improve integer conversion --- src/test_generation/actions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test_generation/actions.rs b/src/test_generation/actions.rs index 8a9c269..a3aa06d 100644 --- a/src/test_generation/actions.rs +++ b/src/test_generation/actions.rs @@ -146,7 +146,7 @@ fn type_conversion(value: String, ty: String) -> String { match ty.as_str() { "str" => value, - "int" => format!("{}.to_u64().unwrap()", value), + "int" => format!("{}.to_u64().unwrap().into()", value), "Addr" => format!("Addr::unchecked_from({})", value), _ => value, } From ab2644adef00688879357691cc44c906af89241b Mon Sep 17 00:00:00 2001 From: bugarela Date: Wed, 22 May 2024 09:52:17 -0300 Subject: [PATCH 11/19] Update tests to read from files --- tests/integration_tests.rs | 15 +- tests/snapshots/integration_tests__ctf01.snap | 349 +++++++++++-- tests/snapshots/integration_tests__ctf02.snap | 386 +++++++++++++- tests/snapshots/integration_tests__ctf04.snap | 340 +++++++++++- tests/snapshots/integration_tests__ctf05.snap | 433 +++++++++++++++- tests/snapshots/integration_tests__ctf06.snap | 389 +++++++++++++- tests/snapshots/integration_tests__ctf07.snap | 389 +++++++++++++- tests/snapshots/integration_tests__ctf08.snap | 482 +++++++++++++++++- tests/snapshots/integration_tests__ctf09.snap | 388 +++++++++++++- tests/snapshots/integration_tests__ctf10.snap | 319 +++++++++++- 10 files changed, 3308 insertions(+), 182 deletions(-) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index a7d3579..0ba97f0 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -44,7 +44,20 @@ fn run(dir: &str, f: impl FnOnce(&mut Command)) -> Result { String::from_utf8(output.stderr)? ); - Ok(String::from_utf8(output.stdout)?) + let quint_file = ws.join("quint/stubs.qnt"); + ensure!(quint_file.exists(), "quint/stubs.qnt not found"); + let quint_file_contents = + fs::read_to_string(quint_file).expect("Should have been able to read the file"); + + let mbt_file = ws.join("src/mbt.rs"); + ensure!(mbt_file.exists(), "src/mbt.rs not found"); + let mbt_file_contents = + fs::read_to_string(mbt_file).expect("Should have been able to read the file"); + + Ok(format!( + "quint/stubs.qnt:\n{}\n\nsrc/mbt.rs:\n{}", + quint_file_contents, mbt_file_contents + )) } #[test] diff --git a/tests/snapshots/integration_tests__ctf01.snap b/tests/snapshots/integration_tests__ctf01.snap index 2e4d5b8..cbeab05 100644 --- a/tests/snapshots/integration_tests__ctf01.snap +++ b/tests/snapshots/integration_tests__ctf01.snap @@ -2,24 +2,26 @@ source: tests/integration_tests.rs expression: output --- +quint/stubs.qnt: module oaksecurity_cosmwasm_ctf_01 { - import basicSpells.* from "../lib/basicSpells" - import cw_types.* from "../lib/cw_types" - import messaging.* from "../lib/messaging" - import bank from "../lib/bank" + import basicSpells.* from "./lib/basicSpells" + import cw_types.* from "./lib/cw_types" + import cw_utils.* from "./lib/cw_utils" + import messaging.* from "./lib/messaging" + import bank from "./lib/bank" var contract_state: ContractState - var return: Result + var result: Result var bank: bank::Bank var time: int - pure val CONTRACT_ADDRESS = "" + pure val CONTRACT_ADDRESS = "contract0" - pure val ADDRESSES = Set("s1", "s2", "s3", CONTRACT_ADDRESS) + pure val ADDRESSES = Set("sender1", "sender2", "sender3", CONTRACT_ADDRESS) pure val DENOMS = Set("d1", "uawesome") pure val MAX_AMOUNT = 200 @@ -48,16 +50,7 @@ module oaksecurity_cosmwasm_ctf_01 { action withdraw_action = { // TODO: Change next line according to fund expectations pure val max_funds = MAX_AMOUNT - val possibilities = 0.to(MAX_AMOUNT).map(i => Some(i)).union(Set(None)) - nondet v1 = possibilities.oneOf() - nondet v2 = possibilities.oneOf() - nondet v3 = possibilities.oneOf() - pure val message_ids: List[int] = - [v1, v2, v3].foldl([], (acc, v) => match v { - | Some(i) => acc.append(i) - | None => acc - }) - + nondet message_ids: List[int] = 0.to(MAX_AMOUNT).allListsUpTo(2).oneOf() pure val message: ExecuteMsg = ExecuteMsg_Withdraw({ ids: message_ids }) execute_message(message, max_funds) } @@ -92,11 +85,25 @@ module oaksecurity_cosmwasm_ctf_01 { val env_val = { block: { time: time } } - action init = all { - contract_state' = init_contract_state, - bank' = init_bank_state, - return' = Err("No previous request"), - time' = 0, + action init = { + // TODO: Change next line according to fund expectations + pure val max_funds = 0 + + nondet sender = Set("admin").oneOf() + nondet denom = DENOMS.oneOf() + nondet amount = 0.to(max_funds).oneOf() + val funds = [{ denom: denom, amount: amount }] + val info = { sender: sender, funds: funds } + + pure val message: InstantiateMsg = { count: 0 } + pure val r = instantiate(init_contract_state, { block: { time: 0 } }, info, message) + + all { + contract_state' = r._2, + bank' = init_bank_state, + result' = r._1, + time' = 0, + } } @@ -110,9 +117,12 @@ module oaksecurity_cosmwasm_ctf_01 { val r = execute(contract_state, env_val, info, message) all { bank.get(sender).get(denom) >= amount, - bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) - .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)), - return' = r._1, + match r._1 { + | Ok(_) => bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) + .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)) + | Err(_) => bank' = bank + }, + result' = r._1, contract_state' = r._2, } } @@ -120,16 +130,16 @@ module oaksecurity_cosmwasm_ctf_01 { action advance_time = time' = time + 1 action step = { - val message_getting = get_message(return) - val new_return = message_getting._1 + val message_getting = get_message(result) + val new_result = message_getting._1 val opt_message = message_getting._2 match opt_message { | Some(submsg) => { - val current_state = { bank: bank, return: new_return, contract_state: contract_state } + val current_state = { bank: bank, result: new_result, contract_state: contract_state } val new_state = process_message(current_state, env_val, CONTRACT_ADDRESS, submsg, reply) all { bank' = new_state.bank, - return' = new_state.return, + result' = new_state.result, contract_state' = new_state.contract_state, advance_time, } @@ -139,3 +149,284 @@ module oaksecurity_cosmwasm_ctf_01 { } } + + +src/mbt.rs: + +pub mod state_structs { + use num_bigint::BigInt; + use serde::Deserialize; + use std::collections::HashMap; + use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] + pub struct InstantiateMsg { + pub count: BigInt + } + + #[derive(Clone, Debug, Deserialize)] + pub struct Lockup { + pub id: BigInt, + pub owner: String, + pub amount: BigInt, + pub release_timestamp: BigInt + } + + #[derive(Clone, Debug, Deserialize)] + pub struct ContractState { + pub last_id: BigInt, + pub lockups: HashMap + } + + #[derive(Clone, Debug, Deserialize)] + pub struct NondetPicks { + + #[serde(with = "As::>")] + pub sender: Option, + + #[serde(with = "As::>")] + pub denom: Option, + + #[serde(with = "As::>")] + pub amount: Option, + + #[serde(with = "As::>")] + pub message_ids: Option> + } + + + #[derive(Clone, Debug, Deserialize)] + pub struct Message {} + + #[derive(Clone, Debug, Deserialize)] + pub struct Response { + pub messages: Vec, + } + + #[derive(Clone, Debug, Deserialize)] + pub struct State { + pub contract_state: ContractState, + pub bank: HashMap>, + #[serde(with = "As::>")] + pub result: Result, + pub action_taken: String, + pub nondet_picks: NondetPicks, + pub time: BigInt, + } + +} +#[cfg(test)] +pub mod tests { + use crate::{ + mbt::state_structs::*, + msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, + }; + use cosmwasm_std::{coin, Addr, Uint128}; + use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; + use itf::trace_from_str; + use num_bigint::BigInt; + use num_traits::{ToPrimitive, Zero}; + + pub const DENOM: &str = "uawesome"; + pub const TICK: u64 = 1; + + pub fn mint_tokens(mut app: App, recipient: String, denom: String, amount: Uint128) -> App { + app.sudo(cw_multi_test::SudoMsg::Bank( + cw_multi_test::BankSudo::Mint { + to_address: recipient.to_owned(), + amount: vec![coin(amount.u128(), denom)], + }, + )) + .unwrap(); + app + } + + fn compare_state(test_state: &TestState, app: &App, state: &State) { + // compare contract balances + let balance = app + .wrap() + .query_balance(&test_state.contract_addr, DENOM) + .unwrap() + .amount; + let trace_balance = state + .bank + .get(&test_state.contract_addr.to_string()) + .and_then(|x| x.get(DENOM)) + .and_then(|x| x.to_u128()) + .unwrap_or(0); + println!( + "Contract balance ({:?}) for {DENOM}: {:?} vs {:?}", + test_state.contract_addr, + balance, + Uint128::new(trace_balance) + ); + assert_eq!(balance, Uint128::new(trace_balance)); + + // TODO: Query the contract and compare the state as you wish + } + + fn compare_result( + trace_result: Result, + app_result: Result, + ) { + if trace_result.is_ok() { + assert!( + app_result.is_ok(), + "Action unexpectedly failed, error: {:?}", + app_result.err() + ); + println!("Action successful as expected"); + } else { + assert!( + app_result.is_err(), + "Expected action to fail with error: {:?}", + trace_result.err() + ); + println!("Action failed as expected"); + } + } + + fn funds_from_trace(amount: Option, denom: Option) -> Vec { + if amount.is_none() || denom.is_none() || amount == Some(Zero::zero()) { + return vec![]; + } + + vec![coin( + amount.as_ref().unwrap().to_u128().unwrap(), + denom.unwrap(), + )] + } + + // Testing is stateful. + struct TestState { + // we will only know the contract address once we have processed an `instantiate` step + pub contract_addr: Addr, + } + + #[test] + fn model_test() { + let mut app = App::default(); + let code = ContractWrapper::new( + crate::contract::execute, + crate::contract::instantiate, + crate::contract::query, + ); + let code_id = app.store_code(Box::new(code)); + + // create test state + let mut test_state = TestState { + contract_addr: Addr::unchecked("contract0"), + }; + + // load trace data + let data = include_str!("../quint/test.itf.json"); + let trace: itf::Trace = trace_from_str(data).unwrap(); + + for s in trace.states { + let last_result = s.value.result.clone(); + if last_result.is_ok() && !last_result.unwrap().messages.is_empty() { + println!("Processing messages, skipping"); + continue; + } + + let action_taken = &s.value.action_taken; + let nondet_picks = &s.value.nondet_picks; + let amount = nondet_picks.amount.clone(); + let denom = nondet_picks.denom.clone(); + let sender = nondet_picks.sender.clone(); + + println!("Step number: {:?}", s.meta.index); + + match action_taken.as_str() { + + "q::init" => { + println!("Initializing contract."); + + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = InstantiateMsg { count: 0 }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + test_state.contract_addr = app.instantiate_contract( + code_id, + sender, + &msg, + &funds, + "test", + None, + ).unwrap(); + + for (addr, coins) in s.value.bank.clone().iter() { + for (denom, amount) in coins.iter() { + app = mint_tokens( + app, + addr.clone(), + denom.to_string(), + Uint128::new(amount.to_u128().unwrap()), + ); + } + } + } + + + + "withdraw_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_ids = nondet_picks.message_ids.clone().unwrap().iter().map(|x| x.to_u64().unwrap().into()).collect(); + let msg = ExecuteMsg::Withdraw { ids: message_ids }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "deposit_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ExecuteMsg::Deposit { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + _ => panic!("Invalid action taken"), + } + compare_state(&test_state, &app, &(s.value.clone())); + println!( + "clock is advancing for {} seconds", + TICK + ); + app.update_block(|block| { + block.time = block.time.plus_seconds(TICK); + }); + println!("-----------------------------------"); + } + } +} diff --git a/tests/snapshots/integration_tests__ctf02.snap b/tests/snapshots/integration_tests__ctf02.snap index 12af37e..f5c196e 100644 --- a/tests/snapshots/integration_tests__ctf02.snap +++ b/tests/snapshots/integration_tests__ctf02.snap @@ -2,24 +2,26 @@ source: tests/integration_tests.rs expression: output --- +quint/stubs.qnt: module oaksecurity_cosmwasm_ctf_02 { - import basicSpells.* from "../lib/basicSpells" - import cw_types.* from "../lib/cw_types" - import messaging.* from "../lib/messaging" - import bank from "../lib/bank" + import basicSpells.* from "./lib/basicSpells" + import cw_types.* from "./lib/cw_types" + import cw_utils.* from "./lib/cw_utils" + import messaging.* from "./lib/messaging" + import bank from "./lib/bank" var contract_state: ContractState - var return: Result + var result: Result var bank: bank::Bank var time: int - pure val CONTRACT_ADDRESS = "" + pure val CONTRACT_ADDRESS = "contract0" - pure val ADDRESSES = Set("s1", "s2", "s3", CONTRACT_ADDRESS) + pure val ADDRESSES = Set("sender1", "sender2", "sender3", CONTRACT_ADDRESS) pure val DENOMS = Set("d1", "uawesome") pure val MAX_AMOUNT = 200 @@ -104,11 +106,25 @@ module oaksecurity_cosmwasm_ctf_02 { val env_val = { block: { time: time } } - action init = all { - contract_state' = init_contract_state, - bank' = init_bank_state, - return' = Err("No previous request"), - time' = 0, + action init = { + // TODO: Change next line according to fund expectations + pure val max_funds = 0 + + nondet sender = Set("admin").oneOf() + nondet denom = DENOMS.oneOf() + nondet amount = 0.to(max_funds).oneOf() + val funds = [{ denom: denom, amount: amount }] + val info = { sender: sender, funds: funds } + + pure val message: InstantiateMsg = { } + pure val r = instantiate(init_contract_state, { block: { time: 0 } }, info, message) + + all { + contract_state' = r._2, + bank' = init_bank_state, + result' = r._1, + time' = 0, + } } @@ -122,9 +138,12 @@ module oaksecurity_cosmwasm_ctf_02 { val r = execute(contract_state, env_val, info, message) all { bank.get(sender).get(denom) >= amount, - bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) - .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)), - return' = r._1, + match r._1 { + | Ok(_) => bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) + .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)) + | Err(_) => bank' = bank + }, + result' = r._1, contract_state' = r._2, } } @@ -132,16 +151,16 @@ module oaksecurity_cosmwasm_ctf_02 { action advance_time = time' = time + 1 action step = { - val message_getting = get_message(return) - val new_return = message_getting._1 + val message_getting = get_message(result) + val new_result = message_getting._1 val opt_message = message_getting._2 match opt_message { | Some(submsg) => { - val current_state = { bank: bank, return: new_return, contract_state: contract_state } + val current_state = { bank: bank, result: new_result, contract_state: contract_state } val new_state = process_message(current_state, env_val, CONTRACT_ADDRESS, submsg, reply) all { bank' = new_state.bank, - return' = new_state.return, + result' = new_state.result, contract_state' = new_state.contract_state, advance_time, } @@ -151,3 +170,332 @@ module oaksecurity_cosmwasm_ctf_02 { } } + + +src/mbt.rs: + +pub mod state_structs { + use num_bigint::BigInt; + use serde::Deserialize; + use std::collections::HashMap; + use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] + pub struct InstantiateMsg { + + } + + #[derive(Clone, Debug, Deserialize)] + pub struct UserInfo { + pub total_tokens: BigInt, + pub voting_power: BigInt, + pub released_time: BigInt + } + + #[derive(Clone, Debug, Deserialize)] + pub struct ContractState { + pub voting_power: HashMap + } + + #[derive(Clone, Debug, Deserialize)] + pub struct NondetPicks { + + #[serde(with = "As::>")] + pub sender: Option, + + #[serde(with = "As::>")] + pub denom: Option, + + #[serde(with = "As::>")] + pub amount: Option, + + #[serde(with = "As::>")] + pub message_amount: Option, + + #[serde(with = "As::>")] + pub message_lock_amount: Option, + + #[serde(with = "As::>")] + pub message_unlock_amount: Option + } + + + #[derive(Clone, Debug, Deserialize)] + pub struct Message {} + + #[derive(Clone, Debug, Deserialize)] + pub struct Response { + pub messages: Vec, + } + + #[derive(Clone, Debug, Deserialize)] + pub struct State { + pub contract_state: ContractState, + pub bank: HashMap>, + #[serde(with = "As::>")] + pub result: Result, + pub action_taken: String, + pub nondet_picks: NondetPicks, + pub time: BigInt, + } + +} +#[cfg(test)] +pub mod tests { + use crate::{ + mbt::state_structs::*, + msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, + }; + use cosmwasm_std::{coin, Addr, Uint128}; + use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; + use itf::trace_from_str; + use num_bigint::BigInt; + use num_traits::{ToPrimitive, Zero}; + + pub const DENOM: &str = "uawesome"; + pub const TICK: u64 = 1; + + pub fn mint_tokens(mut app: App, recipient: String, denom: String, amount: Uint128) -> App { + app.sudo(cw_multi_test::SudoMsg::Bank( + cw_multi_test::BankSudo::Mint { + to_address: recipient.to_owned(), + amount: vec![coin(amount.u128(), denom)], + }, + )) + .unwrap(); + app + } + + fn compare_state(test_state: &TestState, app: &App, state: &State) { + // compare contract balances + let balance = app + .wrap() + .query_balance(&test_state.contract_addr, DENOM) + .unwrap() + .amount; + let trace_balance = state + .bank + .get(&test_state.contract_addr.to_string()) + .and_then(|x| x.get(DENOM)) + .and_then(|x| x.to_u128()) + .unwrap_or(0); + println!( + "Contract balance ({:?}) for {DENOM}: {:?} vs {:?}", + test_state.contract_addr, + balance, + Uint128::new(trace_balance) + ); + assert_eq!(balance, Uint128::new(trace_balance)); + + // TODO: Query the contract and compare the state as you wish + } + + fn compare_result( + trace_result: Result, + app_result: Result, + ) { + if trace_result.is_ok() { + assert!( + app_result.is_ok(), + "Action unexpectedly failed, error: {:?}", + app_result.err() + ); + println!("Action successful as expected"); + } else { + assert!( + app_result.is_err(), + "Expected action to fail with error: {:?}", + trace_result.err() + ); + println!("Action failed as expected"); + } + } + + fn funds_from_trace(amount: Option, denom: Option) -> Vec { + if amount.is_none() || denom.is_none() || amount == Some(Zero::zero()) { + return vec![]; + } + + vec![coin( + amount.as_ref().unwrap().to_u128().unwrap(), + denom.unwrap(), + )] + } + + // Testing is stateful. + struct TestState { + // we will only know the contract address once we have processed an `instantiate` step + pub contract_addr: Addr, + } + + #[test] + fn model_test() { + let mut app = App::default(); + let code = ContractWrapper::new( + crate::contract::execute, + crate::contract::instantiate, + crate::contract::query, + ); + let code_id = app.store_code(Box::new(code)); + + // create test state + let mut test_state = TestState { + contract_addr: Addr::unchecked("contract0"), + }; + + // load trace data + let data = include_str!("../quint/test.itf.json"); + let trace: itf::Trace = trace_from_str(data).unwrap(); + + for s in trace.states { + let last_result = s.value.result.clone(); + if last_result.is_ok() && !last_result.unwrap().messages.is_empty() { + println!("Processing messages, skipping"); + continue; + } + + let action_taken = &s.value.action_taken; + let nondet_picks = &s.value.nondet_picks; + let amount = nondet_picks.amount.clone(); + let denom = nondet_picks.denom.clone(); + let sender = nondet_picks.sender.clone(); + + println!("Step number: {:?}", s.meta.index); + + match action_taken.as_str() { + + "deposit_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ExecuteMsg::Deposit { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "withdraw_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::Withdraw { amount: message_amount }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "q::init" => { + println!("Initializing contract."); + + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = InstantiateMsg { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + test_state.contract_addr = app.instantiate_contract( + code_id, + sender, + &msg, + &funds, + "test", + None, + ).unwrap(); + + for (addr, coins) in s.value.bank.clone().iter() { + for (denom, amount) in coins.iter() { + app = mint_tokens( + app, + addr.clone(), + denom.to_string(), + Uint128::new(amount.to_u128().unwrap()), + ); + } + } + } + + + + "stake_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_lock_amount = nondet_picks.message_lock_amount.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::Stake { lock_amount: message_lock_amount }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "unstake_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_unlock_amount = nondet_picks.message_unlock_amount.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::Unstake { unlock_amount: message_unlock_amount }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + _ => panic!("Invalid action taken"), + } + compare_state(&test_state, &app, &(s.value.clone())); + println!( + "clock is advancing for {} seconds", + TICK + ); + app.update_block(|block| { + block.time = block.time.plus_seconds(TICK); + }); + println!("-----------------------------------"); + } + } +} diff --git a/tests/snapshots/integration_tests__ctf04.snap b/tests/snapshots/integration_tests__ctf04.snap index c267c88..1928991 100644 --- a/tests/snapshots/integration_tests__ctf04.snap +++ b/tests/snapshots/integration_tests__ctf04.snap @@ -2,24 +2,26 @@ source: tests/integration_tests.rs expression: output --- +quint/stubs.qnt: module oaksecurity_cosmwasm_ctf_04 { - import basicSpells.* from "../lib/basicSpells" - import cw_types.* from "../lib/cw_types" - import messaging.* from "../lib/messaging" - import bank from "../lib/bank" + import basicSpells.* from "./lib/basicSpells" + import cw_types.* from "./lib/cw_types" + import cw_utils.* from "./lib/cw_utils" + import messaging.* from "./lib/messaging" + import bank from "./lib/bank" var contract_state: ContractState - var return: Result + var result: Result var bank: bank::Bank var time: int - pure val CONTRACT_ADDRESS = "" + pure val CONTRACT_ADDRESS = "contract0" - pure val ADDRESSES = Set("s1", "s2", "s3", CONTRACT_ADDRESS) + pure val ADDRESSES = Set("sender1", "sender2", "sender3", CONTRACT_ADDRESS) pure val DENOMS = Set("d1", "uawesome") pure val MAX_AMOUNT = 200 @@ -82,11 +84,25 @@ module oaksecurity_cosmwasm_ctf_04 { val env_val = { block: { time: time } } - action init = all { - contract_state' = init_contract_state, - bank' = init_bank_state, - return' = Err("No previous request"), - time' = 0, + action init = { + // TODO: Change next line according to fund expectations + pure val max_funds = 0 + + nondet sender = Set("admin").oneOf() + nondet denom = DENOMS.oneOf() + nondet amount = 0.to(max_funds).oneOf() + val funds = [{ denom: denom, amount: amount }] + val info = { sender: sender, funds: funds } + + pure val message: InstantiateMsg = { offset: 0 } + pure val r = instantiate(init_contract_state, { block: { time: 0 } }, info, message) + + all { + contract_state' = r._2, + bank' = init_bank_state, + result' = r._1, + time' = 0, + } } @@ -100,9 +116,12 @@ module oaksecurity_cosmwasm_ctf_04 { val r = execute(contract_state, env_val, info, message) all { bank.get(sender).get(denom) >= amount, - bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) - .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)), - return' = r._1, + match r._1 { + | Ok(_) => bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) + .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)) + | Err(_) => bank' = bank + }, + result' = r._1, contract_state' = r._2, } } @@ -110,16 +129,16 @@ module oaksecurity_cosmwasm_ctf_04 { action advance_time = time' = time + 1 action step = { - val message_getting = get_message(return) - val new_return = message_getting._1 + val message_getting = get_message(result) + val new_result = message_getting._1 val opt_message = message_getting._2 match opt_message { | Some(submsg) => { - val current_state = { bank: bank, return: new_return, contract_state: contract_state } + val current_state = { bank: bank, result: new_result, contract_state: contract_state } val new_state = process_message(current_state, env_val, CONTRACT_ADDRESS, submsg, reply) all { bank' = new_state.bank, - return' = new_state.return, + result' = new_state.result, contract_state' = new_state.contract_state, advance_time, } @@ -129,3 +148,286 @@ module oaksecurity_cosmwasm_ctf_04 { } } + + +src/mbt.rs: + +pub mod state_structs { + use num_bigint::BigInt; + use serde::Deserialize; + use std::collections::HashMap; + use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] + pub struct InstantiateMsg { + pub offset: BigInt + } + + #[derive(Clone, Debug, Deserialize)] + pub struct Balance { + pub amount: BigInt + } + + #[derive(Clone, Debug, Deserialize)] + pub struct Config { + pub total_supply: BigInt + } + + #[derive(Clone, Debug, Deserialize)] + pub struct ContractState { + pub config: Config, + pub balances: HashMap + } + + #[derive(Clone, Debug, Deserialize)] + pub struct NondetPicks { + + #[serde(with = "As::>")] + pub sender: Option, + + #[serde(with = "As::>")] + pub denom: Option, + + #[serde(with = "As::>")] + pub amount: Option, + + #[serde(with = "As::>")] + pub message_shares: Option + } + + + #[derive(Clone, Debug, Deserialize)] + pub struct Message {} + + #[derive(Clone, Debug, Deserialize)] + pub struct Response { + pub messages: Vec, + } + + #[derive(Clone, Debug, Deserialize)] + pub struct State { + pub contract_state: ContractState, + pub bank: HashMap>, + #[serde(with = "As::>")] + pub result: Result, + pub action_taken: String, + pub nondet_picks: NondetPicks, + pub time: BigInt, + } + +} +#[cfg(test)] +pub mod tests { + use crate::{ + mbt::state_structs::*, + msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, + }; + use cosmwasm_std::{coin, Addr, Uint128}; + use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; + use itf::trace_from_str; + use num_bigint::BigInt; + use num_traits::{ToPrimitive, Zero}; + + pub const DENOM: &str = "uawesome"; + pub const TICK: u64 = 1; + + pub fn mint_tokens(mut app: App, recipient: String, denom: String, amount: Uint128) -> App { + app.sudo(cw_multi_test::SudoMsg::Bank( + cw_multi_test::BankSudo::Mint { + to_address: recipient.to_owned(), + amount: vec![coin(amount.u128(), denom)], + }, + )) + .unwrap(); + app + } + + fn compare_state(test_state: &TestState, app: &App, state: &State) { + // compare contract balances + let balance = app + .wrap() + .query_balance(&test_state.contract_addr, DENOM) + .unwrap() + .amount; + let trace_balance = state + .bank + .get(&test_state.contract_addr.to_string()) + .and_then(|x| x.get(DENOM)) + .and_then(|x| x.to_u128()) + .unwrap_or(0); + println!( + "Contract balance ({:?}) for {DENOM}: {:?} vs {:?}", + test_state.contract_addr, + balance, + Uint128::new(trace_balance) + ); + assert_eq!(balance, Uint128::new(trace_balance)); + + // TODO: Query the contract and compare the state as you wish + } + + fn compare_result( + trace_result: Result, + app_result: Result, + ) { + if trace_result.is_ok() { + assert!( + app_result.is_ok(), + "Action unexpectedly failed, error: {:?}", + app_result.err() + ); + println!("Action successful as expected"); + } else { + assert!( + app_result.is_err(), + "Expected action to fail with error: {:?}", + trace_result.err() + ); + println!("Action failed as expected"); + } + } + + fn funds_from_trace(amount: Option, denom: Option) -> Vec { + if amount.is_none() || denom.is_none() || amount == Some(Zero::zero()) { + return vec![]; + } + + vec![coin( + amount.as_ref().unwrap().to_u128().unwrap(), + denom.unwrap(), + )] + } + + // Testing is stateful. + struct TestState { + // we will only know the contract address once we have processed an `instantiate` step + pub contract_addr: Addr, + } + + #[test] + fn model_test() { + let mut app = App::default(); + let code = ContractWrapper::new( + crate::contract::execute, + crate::contract::instantiate, + crate::contract::query, + ); + let code_id = app.store_code(Box::new(code)); + + // create test state + let mut test_state = TestState { + contract_addr: Addr::unchecked("contract0"), + }; + + // load trace data + let data = include_str!("../quint/test.itf.json"); + let trace: itf::Trace = trace_from_str(data).unwrap(); + + for s in trace.states { + let last_result = s.value.result.clone(); + if last_result.is_ok() && !last_result.unwrap().messages.is_empty() { + println!("Processing messages, skipping"); + continue; + } + + let action_taken = &s.value.action_taken; + let nondet_picks = &s.value.nondet_picks; + let amount = nondet_picks.amount.clone(); + let denom = nondet_picks.denom.clone(); + let sender = nondet_picks.sender.clone(); + + println!("Step number: {:?}", s.meta.index); + + match action_taken.as_str() { + + "q::init" => { + println!("Initializing contract."); + + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = InstantiateMsg { offset: 0 }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + test_state.contract_addr = app.instantiate_contract( + code_id, + sender, + &msg, + &funds, + "test", + None, + ).unwrap(); + + for (addr, coins) in s.value.bank.clone().iter() { + for (denom, amount) in coins.iter() { + app = mint_tokens( + app, + addr.clone(), + denom.to_string(), + Uint128::new(amount.to_u128().unwrap()), + ); + } + } + } + + + + "mint_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ExecuteMsg::Mint { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "burn_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_shares = nondet_picks.message_shares.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::Burn { shares: message_shares }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + _ => panic!("Invalid action taken"), + } + compare_state(&test_state, &app, &(s.value.clone())); + println!( + "clock is advancing for {} seconds", + TICK + ); + app.update_block(|block| { + block.time = block.time.plus_seconds(TICK); + }); + println!("-----------------------------------"); + } + } +} diff --git a/tests/snapshots/integration_tests__ctf05.snap b/tests/snapshots/integration_tests__ctf05.snap index c8b271d..11d90b8 100644 --- a/tests/snapshots/integration_tests__ctf05.snap +++ b/tests/snapshots/integration_tests__ctf05.snap @@ -2,24 +2,26 @@ source: tests/integration_tests.rs expression: output --- +quint/stubs.qnt: module oaksecurity_cosmwasm_ctf_05 { - import basicSpells.* from "../lib/basicSpells" - import cw_types.* from "../lib/cw_types" - import messaging.* from "../lib/messaging" - import bank from "../lib/bank" + import basicSpells.* from "./lib/basicSpells" + import cw_types.* from "./lib/cw_types" + import cw_utils.* from "./lib/cw_utils" + import messaging.* from "./lib/messaging" + import bank from "./lib/bank" var contract_state: ContractState - var return: Result + var result: Result var bank: bank::Bank var time: int - pure val CONTRACT_ADDRESS = "" + pure val CONTRACT_ADDRESS = "contract0" - pure val ADDRESSES = Set("s1", "s2", "s3", CONTRACT_ADDRESS) + pure val ADDRESSES = Set("sender1", "sender2", "sender3", CONTRACT_ADDRESS) pure val DENOMS = Set("d1", "uawesome") pure val MAX_AMOUNT = 200 @@ -130,11 +132,25 @@ module oaksecurity_cosmwasm_ctf_05 { val env_val = { block: { time: time } } - action init = all { - contract_state' = init_contract_state, - bank' = init_bank_state, - return' = Err("No previous request"), - time' = 0, + action init = { + // TODO: Change next line according to fund expectations + pure val max_funds = 0 + + nondet sender = Set("admin").oneOf() + nondet denom = DENOMS.oneOf() + nondet amount = 0.to(max_funds).oneOf() + val funds = [{ denom: denom, amount: amount }] + val info = { sender: sender, funds: funds } + + pure val message: InstantiateMsg = { owner: "" } + pure val r = instantiate(init_contract_state, { block: { time: 0 } }, info, message) + + all { + contract_state' = r._2, + bank' = init_bank_state, + result' = r._1, + time' = 0, + } } @@ -148,9 +164,12 @@ module oaksecurity_cosmwasm_ctf_05 { val r = execute(contract_state, env_val, info, message) all { bank.get(sender).get(denom) >= amount, - bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) - .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)), - return' = r._1, + match r._1 { + | Ok(_) => bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) + .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)) + | Err(_) => bank' = bank + }, + result' = r._1, contract_state' = r._2, } } @@ -158,16 +177,16 @@ module oaksecurity_cosmwasm_ctf_05 { action advance_time = time' = time + 1 action step = { - val message_getting = get_message(return) - val new_return = message_getting._1 + val message_getting = get_message(result) + val new_result = message_getting._1 val opt_message = message_getting._2 match opt_message { | Some(submsg) => { - val current_state = { bank: bank, return: new_return, contract_state: contract_state } + val current_state = { bank: bank, result: new_result, contract_state: contract_state } val new_state = process_message(current_state, env_val, CONTRACT_ADDRESS, submsg, reply) all { bank' = new_state.bank, - return' = new_state.return, + result' = new_state.result, contract_state' = new_state.contract_state, advance_time, } @@ -177,3 +196,379 @@ module oaksecurity_cosmwasm_ctf_05 { } } + + +src/mbt.rs: + +pub mod state_structs { + use num_bigint::BigInt; + use serde::Deserialize; + use std::collections::HashMap; + use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] + pub struct InstantiateMsg { + pub owner: String + } + + #[derive(Clone, Debug, Deserialize)] + pub struct State { + pub current_owner: String, + pub proposed_owner: Option[Addr] + } + + #[derive(Clone, Debug, Deserialize)] + pub struct ContractState { + pub state: State, + pub balances: HashMap + } + + #[derive(Clone, Debug, Deserialize)] + pub struct NondetPicks { + + #[serde(with = "As::>")] + pub sender: Option, + + #[serde(with = "As::>")] + pub denom: Option, + + #[serde(with = "As::>")] + pub amount: Option, + + #[serde(with = "As::>")] + pub proposed_owner_element: Option, + + #[serde(with = "As::>")] + pub message_amount: Option, + + #[serde(with = "As::>")] + pub message_msg: Option, + + #[serde(with = "As::>")] + pub message_new_owner: Option + } + + + #[derive(Clone, Debug, Deserialize)] + pub struct Message {} + + #[derive(Clone, Debug, Deserialize)] + pub struct Response { + pub messages: Vec, + } + + #[derive(Clone, Debug, Deserialize)] + pub struct State { + pub contract_state: ContractState, + pub bank: HashMap>, + #[serde(with = "As::>")] + pub result: Result, + pub action_taken: String, + pub nondet_picks: NondetPicks, + pub time: BigInt, + } + +} +#[cfg(test)] +pub mod tests { + use crate::{ + mbt::state_structs::*, + msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, + }; + use cosmwasm_std::{coin, Addr, Uint128}; + use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; + use itf::trace_from_str; + use num_bigint::BigInt; + use num_traits::{ToPrimitive, Zero}; + + pub const DENOM: &str = "uawesome"; + pub const TICK: u64 = 1; + + pub fn mint_tokens(mut app: App, recipient: String, denom: String, amount: Uint128) -> App { + app.sudo(cw_multi_test::SudoMsg::Bank( + cw_multi_test::BankSudo::Mint { + to_address: recipient.to_owned(), + amount: vec![coin(amount.u128(), denom)], + }, + )) + .unwrap(); + app + } + + fn compare_state(test_state: &TestState, app: &App, state: &State) { + // compare contract balances + let balance = app + .wrap() + .query_balance(&test_state.contract_addr, DENOM) + .unwrap() + .amount; + let trace_balance = state + .bank + .get(&test_state.contract_addr.to_string()) + .and_then(|x| x.get(DENOM)) + .and_then(|x| x.to_u128()) + .unwrap_or(0); + println!( + "Contract balance ({:?}) for {DENOM}: {:?} vs {:?}", + test_state.contract_addr, + balance, + Uint128::new(trace_balance) + ); + assert_eq!(balance, Uint128::new(trace_balance)); + + // TODO: Query the contract and compare the state as you wish + } + + fn compare_result( + trace_result: Result, + app_result: Result, + ) { + if trace_result.is_ok() { + assert!( + app_result.is_ok(), + "Action unexpectedly failed, error: {:?}", + app_result.err() + ); + println!("Action successful as expected"); + } else { + assert!( + app_result.is_err(), + "Expected action to fail with error: {:?}", + trace_result.err() + ); + println!("Action failed as expected"); + } + } + + fn funds_from_trace(amount: Option, denom: Option) -> Vec { + if amount.is_none() || denom.is_none() || amount == Some(Zero::zero()) { + return vec![]; + } + + vec![coin( + amount.as_ref().unwrap().to_u128().unwrap(), + denom.unwrap(), + )] + } + + // Testing is stateful. + struct TestState { + // we will only know the contract address once we have processed an `instantiate` step + pub contract_addr: Addr, + } + + #[test] + fn model_test() { + let mut app = App::default(); + let code = ContractWrapper::new( + crate::contract::execute, + crate::contract::instantiate, + crate::contract::query, + ); + let code_id = app.store_code(Box::new(code)); + + // create test state + let mut test_state = TestState { + contract_addr: Addr::unchecked("contract0"), + }; + + // load trace data + let data = include_str!("../quint/test.itf.json"); + let trace: itf::Trace = trace_from_str(data).unwrap(); + + for s in trace.states { + let last_result = s.value.result.clone(); + if last_result.is_ok() && !last_result.unwrap().messages.is_empty() { + println!("Processing messages, skipping"); + continue; + } + + let action_taken = &s.value.action_taken; + let nondet_picks = &s.value.nondet_picks; + let amount = nondet_picks.amount.clone(); + let denom = nondet_picks.denom.clone(); + let sender = nondet_picks.sender.clone(); + + println!("Step number: {:?}", s.meta.index); + + match action_taken.as_str() { + + "drop_owner_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ExecuteMsg::DropOwnershipProposal { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "owner_action_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_msg = nondet_picks.message_msg.clone().unwrap(); + let msg = ExecuteMsg::OwnerAction { msg: message_msg }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "withdraw_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::Withdraw { amount: message_amount }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "propose_owner_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_new_owner = nondet_picks.message_new_owner.clone().unwrap(); + let msg = ExecuteMsg::ProposeNewOwner { new_owner: message_new_owner }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "deposit_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ExecuteMsg::Deposit { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "accept_owner_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ExecuteMsg::AcceptOwnership { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "q::init" => { + println!("Initializing contract."); + + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = InstantiateMsg { owner: "" }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + test_state.contract_addr = app.instantiate_contract( + code_id, + sender, + &msg, + &funds, + "test", + None, + ).unwrap(); + + for (addr, coins) in s.value.bank.clone().iter() { + for (denom, amount) in coins.iter() { + app = mint_tokens( + app, + addr.clone(), + denom.to_string(), + Uint128::new(amount.to_u128().unwrap()), + ); + } + } + } + + + _ => panic!("Invalid action taken"), + } + compare_state(&test_state, &app, &(s.value.clone())); + println!( + "clock is advancing for {} seconds", + TICK + ); + app.update_block(|block| { + block.time = block.time.plus_seconds(TICK); + }); + println!("-----------------------------------"); + } + } +} diff --git a/tests/snapshots/integration_tests__ctf06.snap b/tests/snapshots/integration_tests__ctf06.snap index 7518a5a..45f429e 100644 --- a/tests/snapshots/integration_tests__ctf06.snap +++ b/tests/snapshots/integration_tests__ctf06.snap @@ -2,24 +2,26 @@ source: tests/integration_tests.rs expression: output --- +quint/stubs.qnt: module oaksecurity_cosmwasm_ctf_06 { - import basicSpells.* from "../lib/basicSpells" - import cw_types.* from "../lib/cw_types" - import messaging.* from "../lib/messaging" - import bank from "../lib/bank" + import basicSpells.* from "./lib/basicSpells" + import cw_types.* from "./lib/cw_types" + import cw_utils.* from "./lib/cw_utils" + import messaging.* from "./lib/messaging" + import bank from "./lib/bank" var contract_state: ContractState - var return: Result + var result: Result var bank: bank::Bank var time: int - pure val CONTRACT_ADDRESS = "" + pure val CONTRACT_ADDRESS = "contract0" - pure val ADDRESSES = Set("s1", "s2", "s3", CONTRACT_ADDRESS) + pure val ADDRESSES = Set("sender1", "sender2", "sender3", CONTRACT_ADDRESS) pure val DENOMS = Set("d1", "uawesome") pure val MAX_AMOUNT = 200 @@ -108,11 +110,25 @@ module oaksecurity_cosmwasm_ctf_06 { val env_val = { block: { time: time } } - action init = all { - contract_state' = init_contract_state, - bank' = init_bank_state, - return' = Err("No previous request"), - time' = 0, + action init = { + // TODO: Change next line according to fund expectations + pure val max_funds = 0 + + nondet sender = Set("admin").oneOf() + nondet denom = DENOMS.oneOf() + nondet amount = 0.to(max_funds).oneOf() + val funds = [{ denom: denom, amount: amount }] + val info = { sender: sender, funds: funds } + + pure val message: InstantiateMsg = { token: "",owner: "",window: 0 } + pure val r = instantiate(init_contract_state, { block: { time: 0 } }, info, message) + + all { + contract_state' = r._2, + bank' = init_bank_state, + result' = r._1, + time' = 0, + } } @@ -126,9 +142,12 @@ module oaksecurity_cosmwasm_ctf_06 { val r = execute(contract_state, env_val, info, message) all { bank.get(sender).get(denom) >= amount, - bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) - .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)), - return' = r._1, + match r._1 { + | Ok(_) => bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) + .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)) + | Err(_) => bank' = bank + }, + result' = r._1, contract_state' = r._2, } } @@ -136,16 +155,16 @@ module oaksecurity_cosmwasm_ctf_06 { action advance_time = time' = time + 1 action step = { - val message_getting = get_message(return) - val new_return = message_getting._1 + val message_getting = get_message(result) + val new_result = message_getting._1 val opt_message = message_getting._2 match opt_message { | Some(submsg) => { - val current_state = { bank: bank, return: new_return, contract_state: contract_state } + val current_state = { bank: bank, result: new_result, contract_state: contract_state } val new_state = process_message(current_state, env_val, CONTRACT_ADDRESS, submsg, reply) all { bank' = new_state.bank, - return' = new_state.return, + result' = new_state.result, contract_state' = new_state.contract_state, advance_time, } @@ -155,3 +174,335 @@ module oaksecurity_cosmwasm_ctf_06 { } } + + +src/mbt.rs: + +pub mod state_structs { + use num_bigint::BigInt; + use serde::Deserialize; + use std::collections::HashMap; + use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] + pub struct Config { + pub voting_window: BigInt, + pub voting_token: String, + pub owner: String + } + + #[derive(Clone, Debug, Deserialize)] + pub struct Proposal { + pub proposer: String, + pub timestamp: BigInt + } + + #[derive(Clone, Debug, Deserialize)] + pub struct InstantiateMsg { + pub token: String, + pub owner: String, + pub window: BigInt + } + + #[derive(Clone, Debug, Deserialize)] + pub struct ContractState { + pub config: Config, + pub proposal: Proposal + } + + #[derive(Clone, Debug, Deserialize)] + pub struct NondetPicks { + + #[serde(with = "As::>")] + pub sender: Option, + + #[serde(with = "As::>")] + pub denom: Option, + + #[serde(with = "As::>")] + pub amount: Option, + + #[serde(with = "As::>")] + pub message_action: Option + } + + + #[derive(Clone, Debug, Deserialize)] + pub struct Message {} + + #[derive(Clone, Debug, Deserialize)] + pub struct Response { + pub messages: Vec, + } + + #[derive(Clone, Debug, Deserialize)] + pub struct State { + pub contract_state: ContractState, + pub bank: HashMap>, + #[serde(with = "As::>")] + pub result: Result, + pub action_taken: String, + pub nondet_picks: NondetPicks, + pub time: BigInt, + } + +} +#[cfg(test)] +pub mod tests { + use crate::{ + mbt::state_structs::*, + msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, + }; + use cosmwasm_std::{coin, Addr, Uint128}; + use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; + use itf::trace_from_str; + use num_bigint::BigInt; + use num_traits::{ToPrimitive, Zero}; + + pub const DENOM: &str = "uawesome"; + pub const TICK: u64 = 1; + + pub fn mint_tokens(mut app: App, recipient: String, denom: String, amount: Uint128) -> App { + app.sudo(cw_multi_test::SudoMsg::Bank( + cw_multi_test::BankSudo::Mint { + to_address: recipient.to_owned(), + amount: vec![coin(amount.u128(), denom)], + }, + )) + .unwrap(); + app + } + + fn compare_state(test_state: &TestState, app: &App, state: &State) { + // compare contract balances + let balance = app + .wrap() + .query_balance(&test_state.contract_addr, DENOM) + .unwrap() + .amount; + let trace_balance = state + .bank + .get(&test_state.contract_addr.to_string()) + .and_then(|x| x.get(DENOM)) + .and_then(|x| x.to_u128()) + .unwrap_or(0); + println!( + "Contract balance ({:?}) for {DENOM}: {:?} vs {:?}", + test_state.contract_addr, + balance, + Uint128::new(trace_balance) + ); + assert_eq!(balance, Uint128::new(trace_balance)); + + // TODO: Query the contract and compare the state as you wish + } + + fn compare_result( + trace_result: Result, + app_result: Result, + ) { + if trace_result.is_ok() { + assert!( + app_result.is_ok(), + "Action unexpectedly failed, error: {:?}", + app_result.err() + ); + println!("Action successful as expected"); + } else { + assert!( + app_result.is_err(), + "Expected action to fail with error: {:?}", + trace_result.err() + ); + println!("Action failed as expected"); + } + } + + fn funds_from_trace(amount: Option, denom: Option) -> Vec { + if amount.is_none() || denom.is_none() || amount == Some(Zero::zero()) { + return vec![]; + } + + vec![coin( + amount.as_ref().unwrap().to_u128().unwrap(), + denom.unwrap(), + )] + } + + // Testing is stateful. + struct TestState { + // we will only know the contract address once we have processed an `instantiate` step + pub contract_addr: Addr, + } + + #[test] + fn model_test() { + let mut app = App::default(); + let code = ContractWrapper::new( + crate::contract::execute, + crate::contract::instantiate, + crate::contract::query, + ); + let code_id = app.store_code(Box::new(code)); + + // create test state + let mut test_state = TestState { + contract_addr: Addr::unchecked("contract0"), + }; + + // load trace data + let data = include_str!("../quint/test.itf.json"); + let trace: itf::Trace = trace_from_str(data).unwrap(); + + for s in trace.states { + let last_result = s.value.result.clone(); + if last_result.is_ok() && !last_result.unwrap().messages.is_empty() { + println!("Processing messages, skipping"); + continue; + } + + let action_taken = &s.value.action_taken; + let nondet_picks = &s.value.nondet_picks; + let amount = nondet_picks.amount.clone(); + let denom = nondet_picks.denom.clone(); + let sender = nondet_picks.sender.clone(); + + println!("Step number: {:?}", s.meta.index); + + match action_taken.as_str() { + + "propose_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ExecuteMsg::Propose { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "q::init" => { + println!("Initializing contract."); + + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = InstantiateMsg { token: "", owner: "", window: 0 }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + test_state.contract_addr = app.instantiate_contract( + code_id, + sender, + &msg, + &funds, + "test", + None, + ).unwrap(); + + for (addr, coins) in s.value.bank.clone().iter() { + for (denom, amount) in coins.iter() { + app = mint_tokens( + app, + addr.clone(), + denom.to_string(), + Uint128::new(amount.to_u128().unwrap()), + ); + } + } + } + + + + "resolve_proposal_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ExecuteMsg::ResolveProposal { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "receive_cw20_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ExecuteMsg::Receive { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "owner_action_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_action = nondet_picks.message_action.clone().unwrap(); + let msg = ExecuteMsg::OwnerAction { action: message_action }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + _ => panic!("Invalid action taken"), + } + compare_state(&test_state, &app, &(s.value.clone())); + println!( + "clock is advancing for {} seconds", + TICK + ); + app.update_block(|block| { + block.time = block.time.plus_seconds(TICK); + }); + println!("-----------------------------------"); + } + } +} diff --git a/tests/snapshots/integration_tests__ctf07.snap b/tests/snapshots/integration_tests__ctf07.snap index c0e34c2..fddb142 100644 --- a/tests/snapshots/integration_tests__ctf07.snap +++ b/tests/snapshots/integration_tests__ctf07.snap @@ -2,24 +2,26 @@ source: tests/integration_tests.rs expression: output --- +quint/stubs.qnt: module oaksecurity_cosmwasm_ctf_07 { - import basicSpells.* from "../lib/basicSpells" - import cw_types.* from "../lib/cw_types" - import messaging.* from "../lib/messaging" - import bank from "../lib/bank" + import basicSpells.* from "./lib/basicSpells" + import cw_types.* from "./lib/cw_types" + import cw_utils.* from "./lib/cw_utils" + import messaging.* from "./lib/messaging" + import bank from "./lib/bank" var contract_state: ContractState - var return: Result + var result: Result var bank: bank::Bank var time: int - pure val CONTRACT_ADDRESS = "" + pure val CONTRACT_ADDRESS = "contract0" - pure val ADDRESSES = Set("s1", "s2", "s3", CONTRACT_ADDRESS) + pure val ADDRESSES = Set("sender1", "sender2", "sender3", CONTRACT_ADDRESS) pure val DENOMS = Set("d1", "uawesome") pure val MAX_AMOUNT = 200 @@ -109,11 +111,25 @@ module oaksecurity_cosmwasm_ctf_07 { val env_val = { block: { time: time } } - action init = all { - contract_state' = init_contract_state, - bank' = init_bank_state, - return' = Err("No previous request"), - time' = 0, + action init = { + // TODO: Change next line according to fund expectations + pure val max_funds = 0 + + nondet sender = Set("admin").oneOf() + nondet denom = DENOMS.oneOf() + nondet amount = 0.to(max_funds).oneOf() + val funds = [{ denom: denom, amount: amount }] + val info = { sender: sender, funds: funds } + + pure val message: InstantiateMsg = { owner: "",threshold: 0 } + pure val r = instantiate(init_contract_state, { block: { time: 0 } }, info, message) + + all { + contract_state' = r._2, + bank' = init_bank_state, + result' = r._1, + time' = 0, + } } @@ -127,9 +143,12 @@ module oaksecurity_cosmwasm_ctf_07 { val r = execute(contract_state, env_val, info, message) all { bank.get(sender).get(denom) >= amount, - bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) - .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)), - return' = r._1, + match r._1 { + | Ok(_) => bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) + .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)) + | Err(_) => bank' = bank + }, + result' = r._1, contract_state' = r._2, } } @@ -137,16 +156,16 @@ module oaksecurity_cosmwasm_ctf_07 { action advance_time = time' = time + 1 action step = { - val message_getting = get_message(return) - val new_return = message_getting._1 + val message_getting = get_message(result) + val new_result = message_getting._1 val opt_message = message_getting._2 match opt_message { | Some(submsg) => { - val current_state = { bank: bank, return: new_return, contract_state: contract_state } + val current_state = { bank: bank, result: new_result, contract_state: contract_state } val new_state = process_message(current_state, env_val, CONTRACT_ADDRESS, submsg, reply) all { bank' = new_state.bank, - return' = new_state.return, + result' = new_state.result, contract_state' = new_state.contract_state, advance_time, } @@ -156,3 +175,335 @@ module oaksecurity_cosmwasm_ctf_07 { } } + + +src/mbt.rs: + +pub mod state_structs { + use num_bigint::BigInt; + use serde::Deserialize; + use std::collections::HashMap; + use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] + pub struct ConfigQueryResponse { + pub owner: String, + pub threshold: BigInt + } + + #[derive(Clone, Debug, Deserialize)] + pub struct InstantiateMsg { + pub owner: String, + pub threshold: BigInt + } + + #[derive(Clone, Debug, Deserialize)] + pub struct ContractState { + pub top_depositor: String, + pub owner: String, + pub threshold: BigInt, + pub balances: HashMap + } + + #[derive(Clone, Debug, Deserialize)] + pub struct NondetPicks { + + #[serde(with = "As::>")] + pub sender: Option, + + #[serde(with = "As::>")] + pub denom: Option, + + #[serde(with = "As::>")] + pub amount: Option, + + #[serde(with = "As::>")] + pub message_amount: Option, + + #[serde(with = "As::>")] + pub message_new_threshold: Option, + + #[serde(with = "As::>")] + pub message_msg: Option + } + + + #[derive(Clone, Debug, Deserialize)] + pub struct Message {} + + #[derive(Clone, Debug, Deserialize)] + pub struct Response { + pub messages: Vec, + } + + #[derive(Clone, Debug, Deserialize)] + pub struct State { + pub contract_state: ContractState, + pub bank: HashMap>, + #[serde(with = "As::>")] + pub result: Result, + pub action_taken: String, + pub nondet_picks: NondetPicks, + pub time: BigInt, + } + +} +#[cfg(test)] +pub mod tests { + use crate::{ + mbt::state_structs::*, + msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, + }; + use cosmwasm_std::{coin, Addr, Uint128}; + use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; + use itf::trace_from_str; + use num_bigint::BigInt; + use num_traits::{ToPrimitive, Zero}; + + pub const DENOM: &str = "uawesome"; + pub const TICK: u64 = 1; + + pub fn mint_tokens(mut app: App, recipient: String, denom: String, amount: Uint128) -> App { + app.sudo(cw_multi_test::SudoMsg::Bank( + cw_multi_test::BankSudo::Mint { + to_address: recipient.to_owned(), + amount: vec![coin(amount.u128(), denom)], + }, + )) + .unwrap(); + app + } + + fn compare_state(test_state: &TestState, app: &App, state: &State) { + // compare contract balances + let balance = app + .wrap() + .query_balance(&test_state.contract_addr, DENOM) + .unwrap() + .amount; + let trace_balance = state + .bank + .get(&test_state.contract_addr.to_string()) + .and_then(|x| x.get(DENOM)) + .and_then(|x| x.to_u128()) + .unwrap_or(0); + println!( + "Contract balance ({:?}) for {DENOM}: {:?} vs {:?}", + test_state.contract_addr, + balance, + Uint128::new(trace_balance) + ); + assert_eq!(balance, Uint128::new(trace_balance)); + + // TODO: Query the contract and compare the state as you wish + } + + fn compare_result( + trace_result: Result, + app_result: Result, + ) { + if trace_result.is_ok() { + assert!( + app_result.is_ok(), + "Action unexpectedly failed, error: {:?}", + app_result.err() + ); + println!("Action successful as expected"); + } else { + assert!( + app_result.is_err(), + "Expected action to fail with error: {:?}", + trace_result.err() + ); + println!("Action failed as expected"); + } + } + + fn funds_from_trace(amount: Option, denom: Option) -> Vec { + if amount.is_none() || denom.is_none() || amount == Some(Zero::zero()) { + return vec![]; + } + + vec![coin( + amount.as_ref().unwrap().to_u128().unwrap(), + denom.unwrap(), + )] + } + + // Testing is stateful. + struct TestState { + // we will only know the contract address once we have processed an `instantiate` step + pub contract_addr: Addr, + } + + #[test] + fn model_test() { + let mut app = App::default(); + let code = ContractWrapper::new( + crate::contract::execute, + crate::contract::instantiate, + crate::contract::query, + ); + let code_id = app.store_code(Box::new(code)); + + // create test state + let mut test_state = TestState { + contract_addr: Addr::unchecked("contract0"), + }; + + // load trace data + let data = include_str!("../quint/test.itf.json"); + let trace: itf::Trace = trace_from_str(data).unwrap(); + + for s in trace.states { + let last_result = s.value.result.clone(); + if last_result.is_ok() && !last_result.unwrap().messages.is_empty() { + println!("Processing messages, skipping"); + continue; + } + + let action_taken = &s.value.action_taken; + let nondet_picks = &s.value.nondet_picks; + let amount = nondet_picks.amount.clone(); + let denom = nondet_picks.denom.clone(); + let sender = nondet_picks.sender.clone(); + + println!("Step number: {:?}", s.meta.index); + + match action_taken.as_str() { + + "q::init" => { + println!("Initializing contract."); + + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = InstantiateMsg { owner: "", threshold: 0 }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + test_state.contract_addr = app.instantiate_contract( + code_id, + sender, + &msg, + &funds, + "test", + None, + ).unwrap(); + + for (addr, coins) in s.value.bank.clone().iter() { + for (denom, amount) in coins.iter() { + app = mint_tokens( + app, + addr.clone(), + denom.to_string(), + Uint128::new(amount.to_u128().unwrap()), + ); + } + } + } + + + + "owner_action_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_msg = nondet_picks.message_msg.clone().unwrap(); + let msg = ExecuteMsg::OwnerAction { msg: message_msg }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "update_config_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_new_threshold = nondet_picks.message_new_threshold.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::UpdateConfig { new_threshold: message_new_threshold }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "deposit_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ExecuteMsg::Deposit { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "withdraw_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::Withdraw { amount: message_amount }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + _ => panic!("Invalid action taken"), + } + compare_state(&test_state, &app, &(s.value.clone())); + println!( + "clock is advancing for {} seconds", + TICK + ); + app.update_block(|block| { + block.time = block.time.plus_seconds(TICK); + }); + println!("-----------------------------------"); + } + } +} diff --git a/tests/snapshots/integration_tests__ctf08.snap b/tests/snapshots/integration_tests__ctf08.snap index bec0354..eef0cb4 100644 --- a/tests/snapshots/integration_tests__ctf08.snap +++ b/tests/snapshots/integration_tests__ctf08.snap @@ -2,24 +2,26 @@ source: tests/integration_tests.rs expression: output --- +quint/stubs.qnt: module oaksecurity_cosmwasm_ctf_08 { - import basicSpells.* from "../lib/basicSpells" - import cw_types.* from "../lib/cw_types" - import messaging.* from "../lib/messaging" - import bank from "../lib/bank" + import basicSpells.* from "./lib/basicSpells" + import cw_types.* from "./lib/cw_types" + import cw_utils.* from "./lib/cw_utils" + import messaging.* from "./lib/messaging" + import bank from "./lib/bank" var contract_state: ContractState - var return: Result + var result: Result var bank: bank::Bank var time: int - pure val CONTRACT_ADDRESS = "" + pure val CONTRACT_ADDRESS = "contract0" - pure val ADDRESSES = Set("s1", "s2", "s3", CONTRACT_ADDRESS) + pure val ADDRESSES = Set("sender1", "sender2", "sender3", CONTRACT_ADDRESS) pure val DENOMS = Set("d1", "uawesome") pure val MAX_AMOUNT = 200 @@ -141,11 +143,25 @@ module oaksecurity_cosmwasm_ctf_08 { val env_val = { block: { time: time } } - action init = all { - contract_state' = init_contract_state, - bank' = init_bank_state, - return' = Err("No previous request"), - time' = 0, + action init = { + // TODO: Change next line according to fund expectations + pure val max_funds = 0 + + nondet sender = Set("admin").oneOf() + nondet denom = DENOMS.oneOf() + nondet amount = 0.to(max_funds).oneOf() + val funds = [{ denom: denom, amount: amount }] + val info = { sender: sender, funds: funds } + + pure val message: InstantiateMsg = { nft_address: "" } + pure val r = instantiate(init_contract_state, { block: { time: 0 } }, info, message) + + all { + contract_state' = r._2, + bank' = init_bank_state, + result' = r._1, + time' = 0, + } } @@ -159,9 +175,12 @@ module oaksecurity_cosmwasm_ctf_08 { val r = execute(contract_state, env_val, info, message) all { bank.get(sender).get(denom) >= amount, - bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) - .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)), - return' = r._1, + match r._1 { + | Ok(_) => bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) + .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)) + | Err(_) => bank' = bank + }, + result' = r._1, contract_state' = r._2, } } @@ -169,16 +188,16 @@ module oaksecurity_cosmwasm_ctf_08 { action advance_time = time' = time + 1 action step = { - val message_getting = get_message(return) - val new_return = message_getting._1 + val message_getting = get_message(result) + val new_result = message_getting._1 val opt_message = message_getting._2 match opt_message { | Some(submsg) => { - val current_state = { bank: bank, return: new_return, contract_state: contract_state } + val current_state = { bank: bank, result: new_result, contract_state: contract_state } val new_state = process_message(current_state, env_val, CONTRACT_ADDRESS, submsg, reply) all { bank' = new_state.bank, - return' = new_state.return, + result' = new_state.result, contract_state' = new_state.contract_state, advance_time, } @@ -188,3 +207,428 @@ module oaksecurity_cosmwasm_ctf_08 { } } + + +src/mbt.rs: + +pub mod state_structs { + use num_bigint::BigInt; + use serde::Deserialize; + use std::collections::HashMap; + use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] + pub struct Operations { + pub n_trades: BigInt, + pub n_sales: BigInt + } + + #[derive(Clone, Debug, Deserialize)] + pub struct Config { + pub nft_contract: String + } + + #[derive(Clone, Debug, Deserialize)] + pub struct GetCountResponse { + pub count: BigInt + } + + #[derive(Clone, Debug, Deserialize)] + pub struct InstantiateMsg { + pub nft_address: String + } + + #[derive(Clone, Debug, Deserialize)] + pub struct Trade { + pub asked_id: String, + pub to_trade_id: String, + pub trader: String + } + + #[derive(Clone, Debug, Deserialize)] + pub struct Sale { + pub nft_id: String, + pub price: BigInt, + pub owner: String, + pub tradable: bool + } + + #[derive(Clone, Debug, Deserialize)] + pub struct ContractState { + pub config: Config, + pub sales: HashMap, + pub trades: HashMap, + pub operations: Operations + } + + #[derive(Clone, Debug, Deserialize)] + pub struct NondetPicks { + + #[serde(with = "As::>")] + pub sender: Option, + + #[serde(with = "As::>")] + pub denom: Option, + + #[serde(with = "As::>")] + pub amount: Option, + + #[serde(with = "As::>")] + pub message_id: Option, + + #[serde(with = "As::>")] + pub message_price: Option, + + #[serde(with = "As::>")] + pub message_tradable: Option, + + #[serde(with = "As::>")] + pub message_id: Option, + + #[serde(with = "As::>")] + pub message_id: Option, + + #[serde(with = "As::>")] + pub message_target: Option, + + #[serde(with = "As::>")] + pub message_offered: Option, + + #[serde(with = "As::>")] + pub message_id: Option, + + #[serde(with = "As::>")] + pub message_trader: Option, + + #[serde(with = "As::>")] + pub message_id: Option + } + + + #[derive(Clone, Debug, Deserialize)] + pub struct Message {} + + #[derive(Clone, Debug, Deserialize)] + pub struct Response { + pub messages: Vec, + } + + #[derive(Clone, Debug, Deserialize)] + pub struct State { + pub contract_state: ContractState, + pub bank: HashMap>, + #[serde(with = "As::>")] + pub result: Result, + pub action_taken: String, + pub nondet_picks: NondetPicks, + pub time: BigInt, + } + +} +#[cfg(test)] +pub mod tests { + use crate::{ + mbt::state_structs::*, + msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, + }; + use cosmwasm_std::{coin, Addr, Uint128}; + use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; + use itf::trace_from_str; + use num_bigint::BigInt; + use num_traits::{ToPrimitive, Zero}; + + pub const DENOM: &str = "uawesome"; + pub const TICK: u64 = 1; + + pub fn mint_tokens(mut app: App, recipient: String, denom: String, amount: Uint128) -> App { + app.sudo(cw_multi_test::SudoMsg::Bank( + cw_multi_test::BankSudo::Mint { + to_address: recipient.to_owned(), + amount: vec![coin(amount.u128(), denom)], + }, + )) + .unwrap(); + app + } + + fn compare_state(test_state: &TestState, app: &App, state: &State) { + // compare contract balances + let balance = app + .wrap() + .query_balance(&test_state.contract_addr, DENOM) + .unwrap() + .amount; + let trace_balance = state + .bank + .get(&test_state.contract_addr.to_string()) + .and_then(|x| x.get(DENOM)) + .and_then(|x| x.to_u128()) + .unwrap_or(0); + println!( + "Contract balance ({:?}) for {DENOM}: {:?} vs {:?}", + test_state.contract_addr, + balance, + Uint128::new(trace_balance) + ); + assert_eq!(balance, Uint128::new(trace_balance)); + + // TODO: Query the contract and compare the state as you wish + } + + fn compare_result( + trace_result: Result, + app_result: Result, + ) { + if trace_result.is_ok() { + assert!( + app_result.is_ok(), + "Action unexpectedly failed, error: {:?}", + app_result.err() + ); + println!("Action successful as expected"); + } else { + assert!( + app_result.is_err(), + "Expected action to fail with error: {:?}", + trace_result.err() + ); + println!("Action failed as expected"); + } + } + + fn funds_from_trace(amount: Option, denom: Option) -> Vec { + if amount.is_none() || denom.is_none() || amount == Some(Zero::zero()) { + return vec![]; + } + + vec![coin( + amount.as_ref().unwrap().to_u128().unwrap(), + denom.unwrap(), + )] + } + + // Testing is stateful. + struct TestState { + // we will only know the contract address once we have processed an `instantiate` step + pub contract_addr: Addr, + } + + #[test] + fn model_test() { + let mut app = App::default(); + let code = ContractWrapper::new( + crate::contract::execute, + crate::contract::instantiate, + crate::contract::query, + ); + let code_id = app.store_code(Box::new(code)); + + // create test state + let mut test_state = TestState { + contract_addr: Addr::unchecked("contract0"), + }; + + // load trace data + let data = include_str!("../quint/test.itf.json"); + let trace: itf::Trace = trace_from_str(data).unwrap(); + + for s in trace.states { + let last_result = s.value.result.clone(); + if last_result.is_ok() && !last_result.unwrap().messages.is_empty() { + println!("Processing messages, skipping"); + continue; + } + + let action_taken = &s.value.action_taken; + let nondet_picks = &s.value.nondet_picks; + let amount = nondet_picks.amount.clone(); + let denom = nondet_picks.denom.clone(); + let sender = nondet_picks.sender.clone(); + + println!("Step number: {:?}", s.meta.index); + + match action_taken.as_str() { + + "exec_accept_trade_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_id = nondet_picks.message_id.clone().unwrap(); + let message_trader = nondet_picks.message_trader.clone().unwrap(); + let msg = ExecuteMsg::AcceptTrade { id: message_id, trader: message_trader }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "exec_cancel_trade_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_id = nondet_picks.message_id.clone().unwrap(); + let msg = ExecuteMsg::CancelTrade { id: message_id }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "exec_cancel_sale_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_id = nondet_picks.message_id.clone().unwrap(); + let msg = ExecuteMsg::CancelSale { id: message_id }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "exec_new_sale_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_id = nondet_picks.message_id.clone().unwrap(); + let message_price = nondet_picks.message_price.clone().unwrap().to_u64().unwrap().into(); + let message_tradable = nondet_picks.message_tradable.clone().unwrap(); + let msg = ExecuteMsg::NewSale { id: message_id, price: message_price, tradable: message_tradable }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "exec_buy_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_id = nondet_picks.message_id.clone().unwrap(); + let msg = ExecuteMsg::BuyNFT { id: message_id }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "exec_new_trade_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_target = nondet_picks.message_target.clone().unwrap(); + let message_offered = nondet_picks.message_offered.clone().unwrap(); + let msg = ExecuteMsg::NewTrade { target: message_target, offered: message_offered }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "q::init" => { + println!("Initializing contract."); + + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = InstantiateMsg { nft_address: "" }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + test_state.contract_addr = app.instantiate_contract( + code_id, + sender, + &msg, + &funds, + "test", + None, + ).unwrap(); + + for (addr, coins) in s.value.bank.clone().iter() { + for (denom, amount) in coins.iter() { + app = mint_tokens( + app, + addr.clone(), + denom.to_string(), + Uint128::new(amount.to_u128().unwrap()), + ); + } + } + } + + + _ => panic!("Invalid action taken"), + } + compare_state(&test_state, &app, &(s.value.clone())); + println!( + "clock is advancing for {} seconds", + TICK + ); + app.update_block(|block| { + block.time = block.time.plus_seconds(TICK); + }); + println!("-----------------------------------"); + } + } +} diff --git a/tests/snapshots/integration_tests__ctf09.snap b/tests/snapshots/integration_tests__ctf09.snap index 11c3aa6..cf896e4 100644 --- a/tests/snapshots/integration_tests__ctf09.snap +++ b/tests/snapshots/integration_tests__ctf09.snap @@ -2,24 +2,26 @@ source: tests/integration_tests.rs expression: output --- +quint/stubs.qnt: module oaksecurity_cosmwasm_ctf_09 { - import basicSpells.* from "../lib/basicSpells" - import cw_types.* from "../lib/cw_types" - import messaging.* from "../lib/messaging" - import bank from "../lib/bank" + import basicSpells.* from "./lib/basicSpells" + import cw_types.* from "./lib/cw_types" + import cw_utils.* from "./lib/cw_utils" + import messaging.* from "./lib/messaging" + import bank from "./lib/bank" var contract_state: ContractState - var return: Result + var result: Result var bank: bank::Bank var time: int - pure val CONTRACT_ADDRESS = "" + pure val CONTRACT_ADDRESS = "contract0" - pure val ADDRESSES = Set("s1", "s2", "s3", CONTRACT_ADDRESS) + pure val ADDRESSES = Set("sender1", "sender2", "sender3", CONTRACT_ADDRESS) pure val DENOMS = Set("d1", "uawesome") pure val MAX_AMOUNT = 200 @@ -107,11 +109,25 @@ module oaksecurity_cosmwasm_ctf_09 { val env_val = { block: { time: time } } - action init = all { - contract_state' = init_contract_state, - bank' = init_bank_state, - return' = Err("No previous request"), - time' = 0, + action init = { + // TODO: Change next line according to fund expectations + pure val max_funds = 0 + + nondet sender = Set("admin").oneOf() + nondet denom = DENOMS.oneOf() + nondet amount = 0.to(max_funds).oneOf() + val funds = [{ denom: denom, amount: amount }] + val info = { sender: sender, funds: funds } + + pure val message: InstantiateMsg = { } + pure val r = instantiate(init_contract_state, { block: { time: 0 } }, info, message) + + all { + contract_state' = r._2, + bank' = init_bank_state, + result' = r._1, + time' = 0, + } } @@ -125,9 +141,12 @@ module oaksecurity_cosmwasm_ctf_09 { val r = execute(contract_state, env_val, info, message) all { bank.get(sender).get(denom) >= amount, - bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) - .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)), - return' = r._1, + match r._1 { + | Ok(_) => bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) + .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)) + | Err(_) => bank' = bank + }, + result' = r._1, contract_state' = r._2, } } @@ -135,16 +154,16 @@ module oaksecurity_cosmwasm_ctf_09 { action advance_time = time' = time + 1 action step = { - val message_getting = get_message(return) - val new_return = message_getting._1 + val message_getting = get_message(result) + val new_result = message_getting._1 val opt_message = message_getting._2 match opt_message { | Some(submsg) => { - val current_state = { bank: bank, return: new_return, contract_state: contract_state } + val current_state = { bank: bank, result: new_result, contract_state: contract_state } val new_state = process_message(current_state, env_val, CONTRACT_ADDRESS, submsg, reply) all { bank' = new_state.bank, - return' = new_state.return, + result' = new_state.result, contract_state' = new_state.contract_state, advance_time, } @@ -154,3 +173,334 @@ module oaksecurity_cosmwasm_ctf_09 { } } + + +src/mbt.rs: + +pub mod state_structs { + use num_bigint::BigInt; + use serde::Deserialize; + use std::collections::HashMap; + use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] + pub struct UserRewardInfo { + pub staked_amount: BigInt, + pub user_index: BigInt, + pub pending_rewards: BigInt + } + + #[derive(Clone, Debug, Deserialize)] + pub struct State { + pub owner: String, + pub total_staked: BigInt, + pub global_index: BigInt + } + + #[derive(Clone, Debug, Deserialize)] + pub struct InstantiateMsg { + + } + + #[derive(Clone, Debug, Deserialize)] + pub struct ContractState { + pub state: State, + pub users: HashMap + } + + #[derive(Clone, Debug, Deserialize)] + pub struct NondetPicks { + + #[serde(with = "As::>")] + pub sender: Option, + + #[serde(with = "As::>")] + pub denom: Option, + + #[serde(with = "As::>")] + pub amount: Option, + + #[serde(with = "As::>")] + pub message_amount: Option + } + + + #[derive(Clone, Debug, Deserialize)] + pub struct Message {} + + #[derive(Clone, Debug, Deserialize)] + pub struct Response { + pub messages: Vec, + } + + #[derive(Clone, Debug, Deserialize)] + pub struct State { + pub contract_state: ContractState, + pub bank: HashMap>, + #[serde(with = "As::>")] + pub result: Result, + pub action_taken: String, + pub nondet_picks: NondetPicks, + pub time: BigInt, + } + +} +#[cfg(test)] +pub mod tests { + use crate::{ + mbt::state_structs::*, + msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, + }; + use cosmwasm_std::{coin, Addr, Uint128}; + use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; + use itf::trace_from_str; + use num_bigint::BigInt; + use num_traits::{ToPrimitive, Zero}; + + pub const DENOM: &str = "uawesome"; + pub const TICK: u64 = 1; + + pub fn mint_tokens(mut app: App, recipient: String, denom: String, amount: Uint128) -> App { + app.sudo(cw_multi_test::SudoMsg::Bank( + cw_multi_test::BankSudo::Mint { + to_address: recipient.to_owned(), + amount: vec![coin(amount.u128(), denom)], + }, + )) + .unwrap(); + app + } + + fn compare_state(test_state: &TestState, app: &App, state: &State) { + // compare contract balances + let balance = app + .wrap() + .query_balance(&test_state.contract_addr, DENOM) + .unwrap() + .amount; + let trace_balance = state + .bank + .get(&test_state.contract_addr.to_string()) + .and_then(|x| x.get(DENOM)) + .and_then(|x| x.to_u128()) + .unwrap_or(0); + println!( + "Contract balance ({:?}) for {DENOM}: {:?} vs {:?}", + test_state.contract_addr, + balance, + Uint128::new(trace_balance) + ); + assert_eq!(balance, Uint128::new(trace_balance)); + + // TODO: Query the contract and compare the state as you wish + } + + fn compare_result( + trace_result: Result, + app_result: Result, + ) { + if trace_result.is_ok() { + assert!( + app_result.is_ok(), + "Action unexpectedly failed, error: {:?}", + app_result.err() + ); + println!("Action successful as expected"); + } else { + assert!( + app_result.is_err(), + "Expected action to fail with error: {:?}", + trace_result.err() + ); + println!("Action failed as expected"); + } + } + + fn funds_from_trace(amount: Option, denom: Option) -> Vec { + if amount.is_none() || denom.is_none() || amount == Some(Zero::zero()) { + return vec![]; + } + + vec![coin( + amount.as_ref().unwrap().to_u128().unwrap(), + denom.unwrap(), + )] + } + + // Testing is stateful. + struct TestState { + // we will only know the contract address once we have processed an `instantiate` step + pub contract_addr: Addr, + } + + #[test] + fn model_test() { + let mut app = App::default(); + let code = ContractWrapper::new( + crate::contract::execute, + crate::contract::instantiate, + crate::contract::query, + ); + let code_id = app.store_code(Box::new(code)); + + // create test state + let mut test_state = TestState { + contract_addr: Addr::unchecked("contract0"), + }; + + // load trace data + let data = include_str!("../quint/test.itf.json"); + let trace: itf::Trace = trace_from_str(data).unwrap(); + + for s in trace.states { + let last_result = s.value.result.clone(); + if last_result.is_ok() && !last_result.unwrap().messages.is_empty() { + println!("Processing messages, skipping"); + continue; + } + + let action_taken = &s.value.action_taken; + let nondet_picks = &s.value.nondet_picks; + let amount = nondet_picks.amount.clone(); + let denom = nondet_picks.denom.clone(); + let sender = nondet_picks.sender.clone(); + + println!("Step number: {:?}", s.meta.index); + + match action_taken.as_str() { + + "deposit_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ExecuteMsg::Deposit { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "increase_reward_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ExecuteMsg::IncreaseReward { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "claim_rewards_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ExecuteMsg::ClaimRewards { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "q::init" => { + println!("Initializing contract."); + + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = InstantiateMsg { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + test_state.contract_addr = app.instantiate_contract( + code_id, + sender, + &msg, + &funds, + "test", + None, + ).unwrap(); + + for (addr, coins) in s.value.bank.clone().iter() { + for (denom, amount) in coins.iter() { + app = mint_tokens( + app, + addr.clone(), + denom.to_string(), + Uint128::new(amount.to_u128().unwrap()), + ); + } + } + } + + + + "withdraw_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::Withdraw { amount: message_amount }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + _ => panic!("Invalid action taken"), + } + compare_state(&test_state, &app, &(s.value.clone())); + println!( + "clock is advancing for {} seconds", + TICK + ); + app.update_block(|block| { + block.time = block.time.plus_seconds(TICK); + }); + println!("-----------------------------------"); + } + } +} diff --git a/tests/snapshots/integration_tests__ctf10.snap b/tests/snapshots/integration_tests__ctf10.snap index 1090f62..9167abf 100644 --- a/tests/snapshots/integration_tests__ctf10.snap +++ b/tests/snapshots/integration_tests__ctf10.snap @@ -2,24 +2,26 @@ source: tests/integration_tests.rs expression: output --- +quint/stubs.qnt: module oaksecurity_cosmwasm_ctf_10 { - import basicSpells.* from "../lib/basicSpells" - import cw_types.* from "../lib/cw_types" - import messaging.* from "../lib/messaging" - import bank from "../lib/bank" + import basicSpells.* from "./lib/basicSpells" + import cw_types.* from "./lib/cw_types" + import cw_utils.* from "./lib/cw_utils" + import messaging.* from "./lib/messaging" + import bank from "./lib/bank" var contract_state: ContractState - var return: Result + var result: Result var bank: bank::Bank var time: int - pure val CONTRACT_ADDRESS = "" + pure val CONTRACT_ADDRESS = "contract0" - pure val ADDRESSES = Set("s1", "s2", "s3", CONTRACT_ADDRESS) + pure val ADDRESSES = Set("sender1", "sender2", "sender3", CONTRACT_ADDRESS) pure val DENOMS = Set("d1", "uawesome") pure val MAX_AMOUNT = 200 @@ -68,11 +70,25 @@ module oaksecurity_cosmwasm_ctf_10 { val env_val = { block: { time: time } } - action init = all { - contract_state' = init_contract_state, - bank' = init_bank_state, - return' = Err("No previous request"), - time' = 0, + action init = { + // TODO: Change next line according to fund expectations + pure val max_funds = 0 + + nondet sender = Set("admin").oneOf() + nondet denom = DENOMS.oneOf() + nondet amount = 0.to(max_funds).oneOf() + val funds = [{ denom: denom, amount: amount }] + val info = { sender: sender, funds: funds } + + pure val message: InstantiateMsg = { cw721_code_id: 0,mint_per_user: 0,whitelisted_users: [] } + pure val r = instantiate(init_contract_state, { block: { time: 0 } }, info, message) + + all { + contract_state' = r._2, + bank' = init_bank_state, + result' = r._1, + time' = 0, + } } @@ -86,9 +102,12 @@ module oaksecurity_cosmwasm_ctf_10 { val r = execute(contract_state, env_val, info, message) all { bank.get(sender).get(denom) >= amount, - bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) - .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)), - return' = r._1, + match r._1 { + | Ok(_) => bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) + .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)) + | Err(_) => bank' = bank + }, + result' = r._1, contract_state' = r._2, } } @@ -96,16 +115,16 @@ module oaksecurity_cosmwasm_ctf_10 { action advance_time = time' = time + 1 action step = { - val message_getting = get_message(return) - val new_return = message_getting._1 + val message_getting = get_message(result) + val new_result = message_getting._1 val opt_message = message_getting._2 match opt_message { | Some(submsg) => { - val current_state = { bank: bank, return: new_return, contract_state: contract_state } + val current_state = { bank: bank, result: new_result, contract_state: contract_state } val new_state = process_message(current_state, env_val, CONTRACT_ADDRESS, submsg, reply) all { bank' = new_state.bank, - return' = new_state.return, + result' = new_state.result, contract_state' = new_state.contract_state, advance_time, } @@ -115,3 +134,265 @@ module oaksecurity_cosmwasm_ctf_10 { } } + + +src/mbt.rs: + +pub mod state_structs { + use num_bigint::BigInt; + use serde::Deserialize; + use std::collections::HashMap; + use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] + pub struct InstantiateMsg { + pub cw721_code_id: BigInt, + pub mint_per_user: BigInt, + pub whitelisted_users: Vec + } + + #[derive(Clone, Debug, Deserialize)] + pub struct Config { + pub nft_contract: String, + pub mint_per_user: BigInt, + pub total_tokens: BigInt + } + + #[derive(Clone, Debug, Deserialize)] + pub struct Whitelist { + pub users: Vec + } + + #[derive(Clone, Debug, Deserialize)] + pub struct ContractState { + pub config: Config, + pub whitelist: Whitelist + } + + #[derive(Clone, Debug, Deserialize)] + pub struct NondetPicks { + + #[serde(with = "As::>")] + pub sender: Option, + + #[serde(with = "As::>")] + pub denom: Option, + + #[serde(with = "As::>")] + pub amount: Option + } + + + #[derive(Clone, Debug, Deserialize)] + pub struct Message {} + + #[derive(Clone, Debug, Deserialize)] + pub struct Response { + pub messages: Vec, + } + + #[derive(Clone, Debug, Deserialize)] + pub struct State { + pub contract_state: ContractState, + pub bank: HashMap>, + #[serde(with = "As::>")] + pub result: Result, + pub action_taken: String, + pub nondet_picks: NondetPicks, + pub time: BigInt, + } + +} +#[cfg(test)] +pub mod tests { + use crate::{ + mbt::state_structs::*, + msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, + }; + use cosmwasm_std::{coin, Addr, Uint128}; + use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; + use itf::trace_from_str; + use num_bigint::BigInt; + use num_traits::{ToPrimitive, Zero}; + + pub const DENOM: &str = "uawesome"; + pub const TICK: u64 = 1; + + pub fn mint_tokens(mut app: App, recipient: String, denom: String, amount: Uint128) -> App { + app.sudo(cw_multi_test::SudoMsg::Bank( + cw_multi_test::BankSudo::Mint { + to_address: recipient.to_owned(), + amount: vec![coin(amount.u128(), denom)], + }, + )) + .unwrap(); + app + } + + fn compare_state(test_state: &TestState, app: &App, state: &State) { + // compare contract balances + let balance = app + .wrap() + .query_balance(&test_state.contract_addr, DENOM) + .unwrap() + .amount; + let trace_balance = state + .bank + .get(&test_state.contract_addr.to_string()) + .and_then(|x| x.get(DENOM)) + .and_then(|x| x.to_u128()) + .unwrap_or(0); + println!( + "Contract balance ({:?}) for {DENOM}: {:?} vs {:?}", + test_state.contract_addr, + balance, + Uint128::new(trace_balance) + ); + assert_eq!(balance, Uint128::new(trace_balance)); + + // TODO: Query the contract and compare the state as you wish + } + + fn compare_result( + trace_result: Result, + app_result: Result, + ) { + if trace_result.is_ok() { + assert!( + app_result.is_ok(), + "Action unexpectedly failed, error: {:?}", + app_result.err() + ); + println!("Action successful as expected"); + } else { + assert!( + app_result.is_err(), + "Expected action to fail with error: {:?}", + trace_result.err() + ); + println!("Action failed as expected"); + } + } + + fn funds_from_trace(amount: Option, denom: Option) -> Vec { + if amount.is_none() || denom.is_none() || amount == Some(Zero::zero()) { + return vec![]; + } + + vec![coin( + amount.as_ref().unwrap().to_u128().unwrap(), + denom.unwrap(), + )] + } + + // Testing is stateful. + struct TestState { + // we will only know the contract address once we have processed an `instantiate` step + pub contract_addr: Addr, + } + + #[test] + fn model_test() { + let mut app = App::default(); + let code = ContractWrapper::new( + crate::contract::execute, + crate::contract::instantiate, + crate::contract::query, + ); + let code_id = app.store_code(Box::new(code)); + + // create test state + let mut test_state = TestState { + contract_addr: Addr::unchecked("contract0"), + }; + + // load trace data + let data = include_str!("../quint/test.itf.json"); + let trace: itf::Trace = trace_from_str(data).unwrap(); + + for s in trace.states { + let last_result = s.value.result.clone(); + if last_result.is_ok() && !last_result.unwrap().messages.is_empty() { + println!("Processing messages, skipping"); + continue; + } + + let action_taken = &s.value.action_taken; + let nondet_picks = &s.value.nondet_picks; + let amount = nondet_picks.amount.clone(); + let denom = nondet_picks.denom.clone(); + let sender = nondet_picks.sender.clone(); + + println!("Step number: {:?}", s.meta.index); + + match action_taken.as_str() { + + "q::init" => { + println!("Initializing contract."); + + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = InstantiateMsg { cw721_code_id: 0, mint_per_user: 0, whitelisted_users: [] }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + test_state.contract_addr = app.instantiate_contract( + code_id, + sender, + &msg, + &funds, + "test", + None, + ).unwrap(); + + for (addr, coins) in s.value.bank.clone().iter() { + for (denom, amount) in coins.iter() { + app = mint_tokens( + app, + addr.clone(), + denom.to_string(), + Uint128::new(amount.to_u128().unwrap()), + ); + } + } + } + + + + "mint_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ExecuteMsg::Mint { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + _ => panic!("Invalid action taken"), + } + compare_state(&test_state, &app, &(s.value.clone())); + println!( + "clock is advancing for {} seconds", + TICK + ); + app.update_block(|block| { + block.time = block.time.plus_seconds(TICK); + }); + println!("-----------------------------------"); + } + } +} From c1622c0dae758ad450c134d05b58df773c57db02 Mon Sep 17 00:00:00 2001 From: bugarela Date: Wed, 22 May 2024 16:13:05 -0300 Subject: [PATCH 12/19] Fix implementation and tests to handle multiple crates --- src/lib.rs | 24 +- src/test_generation/actions.rs | 15 +- tests/integration_tests.rs | 57 +- tests/snapshots/integration_tests__ctf01.snap | 8 +- tests/snapshots/integration_tests__ctf02.snap | 64 +- tests/snapshots/integration_tests__ctf03.snap | 994 +++++++++++++++++- tests/snapshots/integration_tests__ctf04.snap | 28 +- tests/snapshots/integration_tests__ctf05.snap | 48 +- tests/snapshots/integration_tests__ctf06.snap | 74 +- tests/snapshots/integration_tests__ctf07.snap | 68 +- tests/snapshots/integration_tests__ctf08.snap | 118 ++- tests/snapshots/integration_tests__ctf09.snap | 66 +- tests/snapshots/integration_tests__ctf10.snap | 22 +- 13 files changed, 1270 insertions(+), 316 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4a88067..9c7a6b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -127,11 +127,11 @@ fn translate_all_items(tcx: TyCtxt) { items_by_crate.into_iter().for_each(|(crate_id, items)| { let crate_name = tcx.crate_name(crate_id); - traslate_items(tcx, crate_name.as_str(), items.collect_vec()) + translate_items(tcx, crate_name.as_str(), items.collect_vec()) }); } -fn traslate_items(tcx: TyCtxt, crate_name: &str, items: Vec<&rustc_hir::Item>) { +fn translate_items(tcx: TyCtxt, crate_name: &str, items: Vec<&rustc_hir::Item>) { let mut ctx = Context { tcx, message_type_for_action: HashMap::from([( @@ -185,6 +185,17 @@ fn traslate_items(tcx: TyCtxt, crate_name: &str, items: Vec<&rustc_hir::Item>) { ); let tests = generate_tests(ctx.clone()); + // write module to file + std::fs::write(format!("quint/{}_stubs.qnt", crate_name), module) + .expect("Unable to write file"); + + // write tests to file + std::fs::create_dir_all("src/mbt").expect("Unable to create directory"); + std::fs::write(format!("src/mbt/{}.rs", crate_name), tests).expect("Unable to write file"); +} + +// This is the main entry point for the plugin. It prints the generated quint code to STDOUT. +fn cosmwasm_to_quint(tcx: TyCtxt, _args: &CosmwasmToQuintPluginArgs) { // create generated directory std::fs::create_dir_all("quint/lib").expect("Unable to create directory"); @@ -202,14 +213,5 @@ fn traslate_items(tcx: TyCtxt, crate_name: &str, items: Vec<&rustc_hir::Item>) { std::fs::write("quint/lib/cw_utils.qnt", cw_utils).expect("Unable to write file"); std::fs::write("quint/lib/messaging.qnt", messaging).expect("Unable to write file"); - // write module to file - std::fs::write("quint/stubs.qnt", module).expect("Unable to write file"); - - // write tests to file - std::fs::write("src/mbt.rs", tests).expect("Unable to write file"); -} - -// This is the main entry point for the plugin. It prints the generated quint code to STDOUT. -fn cosmwasm_to_quint(tcx: TyCtxt, _args: &CosmwasmToQuintPluginArgs) { translate_all_items(tcx); } diff --git a/src/test_generation/actions.rs b/src/test_generation/actions.rs index a3aa06d..7a72bff 100644 --- a/src/test_generation/actions.rs +++ b/src/test_generation/actions.rs @@ -1,11 +1,15 @@ use crate::boilerplate::init_value_for_type; -use crate::types::Context; +use crate::types::{fallback_constructor, Context}; use itertools::Itertools; pub fn translate_actions(ctx: Context) -> String { let msgs = ctx.message_type_for_action.iter().map(|(action, ty)| { if action == "instantiate" { - let msg_struct = ctx.structs.get("InstantiateMsg").unwrap(); + let msg_struct = ctx + .structs + .get("InstantiateMsg") + .cloned() + .unwrap_or_default(); let msg_fields = msg_struct .iter() .map(|f| { @@ -20,7 +24,12 @@ pub fn translate_actions(ctx: Context) -> String { if action == "execute" || action == "instantiate" || action == "reply" { return "".to_string(); } - let constructor = ctx.constructors.get(ty.as_str()).unwrap(); + let constructor = ctx + .constructors + .get(ty.as_str()) + .cloned() + .unwrap_or_else(|| fallback_constructor(ty)); + let nondet_picks = constructor .fields .iter() diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 0ba97f0..7db5b21 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1,4 +1,4 @@ -use std::{env, fs, path::Path, process::Command, sync::Once}; +use std::{env, fs, io, path::Path, process::Command, sync::Once}; use anyhow::{ensure, Context, Result}; @@ -44,20 +44,47 @@ fn run(dir: &str, f: impl FnOnce(&mut Command)) -> Result { String::from_utf8(output.stderr)? ); - let quint_file = ws.join("quint/stubs.qnt"); - ensure!(quint_file.exists(), "quint/stubs.qnt not found"); - let quint_file_contents = - fs::read_to_string(quint_file).expect("Should have been able to read the file"); - - let mbt_file = ws.join("src/mbt.rs"); - ensure!(mbt_file.exists(), "src/mbt.rs not found"); - let mbt_file_contents = - fs::read_to_string(mbt_file).expect("Should have been able to read the file"); - - Ok(format!( - "quint/stubs.qnt:\n{}\n\nsrc/mbt.rs:\n{}", - quint_file_contents, mbt_file_contents - )) + let mut quint_entries = fs::read_dir(ws.join("quint"))? + .map(|res| res.map(|e| e.path())) + .collect::, io::Error>>()?; + quint_entries.sort(); + + let quint_files = quint_entries + .iter() + .map(|path| { + if path.is_dir() { + return "".to_string(); + } + format!( + "{}:\n{}", + path.clone().file_name().unwrap().to_string_lossy(), + fs::read_to_string(path).expect("Should have been able to read the file"), + ) + }) + .collect::>() + .join("\n\n"); + + let mut mbt_entries = fs::read_dir(ws.join("src/mbt"))? + .map(|res| res.map(|e| e.path())) + .collect::, io::Error>>()?; + mbt_entries.sort(); + + let mbt_files = mbt_entries + .iter() + .map(|path| { + if path.is_dir() { + return "".to_string(); + } + format!( + "{}:\n{}", + path.clone().file_name().unwrap().to_string_lossy(), + fs::read_to_string(path).expect("Should have been able to read the file"), + ) + }) + .collect::>() + .join("\n\n"); + + Ok(format!("quint:\n{}\n\nmbt:\n{}", quint_files, mbt_files)) } #[test] diff --git a/tests/snapshots/integration_tests__ctf01.snap b/tests/snapshots/integration_tests__ctf01.snap index cbeab05..a326d0e 100644 --- a/tests/snapshots/integration_tests__ctf01.snap +++ b/tests/snapshots/integration_tests__ctf01.snap @@ -2,7 +2,10 @@ source: tests/integration_tests.rs expression: output --- -quint/stubs.qnt: +quint: + + +oaksecurity_cosmwasm_ctf_01_stubs.qnt: module oaksecurity_cosmwasm_ctf_01 { import basicSpells.* from "./lib/basicSpells" @@ -151,7 +154,8 @@ module oaksecurity_cosmwasm_ctf_01 { } -src/mbt.rs: +mbt: +oaksecurity_cosmwasm_ctf_01.rs: pub mod state_structs { use num_bigint::BigInt; diff --git a/tests/snapshots/integration_tests__ctf02.snap b/tests/snapshots/integration_tests__ctf02.snap index f5c196e..ca4c6c0 100644 --- a/tests/snapshots/integration_tests__ctf02.snap +++ b/tests/snapshots/integration_tests__ctf02.snap @@ -2,7 +2,10 @@ source: tests/integration_tests.rs expression: output --- -quint/stubs.qnt: +quint: + + +oaksecurity_cosmwasm_ctf_02_stubs.qnt: module oaksecurity_cosmwasm_ctf_02 { import basicSpells.* from "./lib/basicSpells" @@ -172,7 +175,8 @@ module oaksecurity_cosmwasm_ctf_02 { } -src/mbt.rs: +mbt: +oaksecurity_cosmwasm_ctf_02.rs: pub mod state_structs { use num_bigint::BigInt; @@ -363,28 +367,6 @@ pub mod tests { match action_taken.as_str() { - "deposit_action" => { - let sender = Addr::unchecked(sender.unwrap()); - let funds = funds_from_trace(amount, denom); - - - let msg = ExecuteMsg::Deposit { }; - println!("Message: {:?}", msg); - println!("Sender: {:?}", sender); - println!("Funds: {:?}", funds); - - let res = app.execute_contract( - sender, - test_state.contract_addr.clone(), - &msg, - &funds, - ); - - compare_result(s.value.result.clone(), res) - } - - - "withdraw_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -442,12 +424,12 @@ pub mod tests { - "stake_action" => { + "unstake_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_lock_amount = nondet_picks.message_lock_amount.clone().unwrap().to_u64().unwrap().into(); - let msg = ExecuteMsg::Stake { lock_amount: message_lock_amount }; + let message_unlock_amount = nondet_picks.message_unlock_amount.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::Unstake { unlock_amount: message_unlock_amount }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -464,12 +446,34 @@ pub mod tests { - "unstake_action" => { + "deposit_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_unlock_amount = nondet_picks.message_unlock_amount.clone().unwrap().to_u64().unwrap().into(); - let msg = ExecuteMsg::Unstake { unlock_amount: message_unlock_amount }; + + let msg = ExecuteMsg::Deposit { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "stake_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_lock_amount = nondet_picks.message_lock_amount.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::Stake { lock_amount: message_lock_amount }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); diff --git a/tests/snapshots/integration_tests__ctf03.snap b/tests/snapshots/integration_tests__ctf03.snap index a76fdb1..e2977a6 100644 --- a/tests/snapshots/integration_tests__ctf03.snap +++ b/tests/snapshots/integration_tests__ctf03.snap @@ -2,24 +2,27 @@ source: tests/integration_tests.rs expression: output --- +quint: +flash_loan_stubs.qnt: module flash_loan { - import basicSpells.* from "../lib/basicSpells" - import cw_types.* from "../lib/cw_types" - import messaging.* from "../lib/messaging" - import bank from "../lib/bank" + import basicSpells.* from "./lib/basicSpells" + import cw_types.* from "./lib/cw_types" + import cw_utils.* from "./lib/cw_utils" + import messaging.* from "./lib/messaging" + import bank from "./lib/bank" var contract_state: ContractState - var return: Result + var result: Result var bank: bank::Bank var time: int - pure val CONTRACT_ADDRESS = "" + pure val CONTRACT_ADDRESS = "contract0" - pure val ADDRESSES = Set("s1", "s2", "s3", CONTRACT_ADDRESS) + pure val ADDRESSES = Set("sender1", "sender2", "sender3", CONTRACT_ADDRESS) pure val DENOMS = Set("d1", "uawesome") pure val MAX_AMOUNT = 200 @@ -110,11 +113,25 @@ module flash_loan { val env_val = { block: { time: time } } - action init = all { - contract_state' = init_contract_state, - bank' = init_bank_state, - return' = Err("No previous request"), - time' = 0, + action init = { + // TODO: Change next line according to fund expectations + pure val max_funds = 0 + + nondet sender = Set("admin").oneOf() + nondet denom = DENOMS.oneOf() + nondet amount = 0.to(max_funds).oneOf() + val funds = [{ denom: denom, amount: amount }] + val info = { sender: sender, funds: funds } + + pure val message: InstantiateMsg = + pure val r = instantiate(init_contract_state, { block: { time: 0 } }, info, message) + + all { + contract_state' = r._2, + bank' = init_bank_state, + result' = r._1, + time' = 0, + } } @@ -128,9 +145,12 @@ module flash_loan { val r = execute(contract_state, env_val, info, message) all { bank.get(sender).get(denom) >= amount, - bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) - .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)), - return' = r._1, + match r._1 { + | Ok(_) => bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) + .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)) + | Err(_) => bank' = bank + }, + result' = r._1, contract_state' = r._2, } } @@ -138,16 +158,16 @@ module flash_loan { action advance_time = time' = time + 1 action step = { - val message_getting = get_message(return) - val new_return = message_getting._1 + val message_getting = get_message(result) + val new_result = message_getting._1 val opt_message = message_getting._2 match opt_message { | Some(submsg) => { - val current_state = { bank: bank, return: new_return, contract_state: contract_state } + val current_state = { bank: bank, result: new_result, contract_state: contract_state } val new_state = process_message(current_state, env_val, CONTRACT_ADDRESS, submsg, reply) all { bank' = new_state.bank, - return' = new_state.return, + result' = new_state.result, contract_state' = new_state.contract_state, advance_time, } @@ -157,24 +177,30 @@ module flash_loan { } } + + + + +mock_arb_stubs.qnt: module mock_arb { - import basicSpells.* from "../lib/basicSpells" - import cw_types.* from "../lib/cw_types" - import messaging.* from "../lib/messaging" - import bank from "../lib/bank" + import basicSpells.* from "./lib/basicSpells" + import cw_types.* from "./lib/cw_types" + import cw_utils.* from "./lib/cw_utils" + import messaging.* from "./lib/messaging" + import bank from "./lib/bank" var contract_state: ContractState - var return: Result + var result: Result var bank: bank::Bank var time: int - pure val CONTRACT_ADDRESS = "" + pure val CONTRACT_ADDRESS = "contract0" - pure val ADDRESSES = Set("s1", "s2", "s3", CONTRACT_ADDRESS) + pure val ADDRESSES = Set("sender1", "sender2", "sender3", CONTRACT_ADDRESS) pure val DENOMS = Set("d1", "uawesome") pure val MAX_AMOUNT = 200 @@ -218,11 +244,25 @@ module mock_arb { val env_val = { block: { time: time } } - action init = all { - contract_state' = init_contract_state, - bank' = init_bank_state, - return' = Err("No previous request"), - time' = 0, + action init = { + // TODO: Change next line according to fund expectations + pure val max_funds = 0 + + nondet sender = Set("admin").oneOf() + nondet denom = DENOMS.oneOf() + nondet amount = 0.to(max_funds).oneOf() + val funds = [{ denom: denom, amount: amount }] + val info = { sender: sender, funds: funds } + + pure val message: InstantiateMsg = + pure val r = instantiate(init_contract_state, { block: { time: 0 } }, info, message) + + all { + contract_state' = r._2, + bank' = init_bank_state, + result' = r._1, + time' = 0, + } } @@ -236,9 +276,12 @@ module mock_arb { val r = execute(contract_state, env_val, info, message) all { bank.get(sender).get(denom) >= amount, - bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) - .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)), - return' = r._1, + match r._1 { + | Ok(_) => bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) + .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)) + | Err(_) => bank' = bank + }, + result' = r._1, contract_state' = r._2, } } @@ -246,16 +289,16 @@ module mock_arb { action advance_time = time' = time + 1 action step = { - val message_getting = get_message(return) - val new_return = message_getting._1 + val message_getting = get_message(result) + val new_result = message_getting._1 val opt_message = message_getting._2 match opt_message { | Some(submsg) => { - val current_state = { bank: bank, return: new_return, contract_state: contract_state } + val current_state = { bank: bank, result: new_result, contract_state: contract_state } val new_state = process_message(current_state, env_val, CONTRACT_ADDRESS, submsg, reply) all { bank' = new_state.bank, - return' = new_state.return, + result' = new_state.result, contract_state' = new_state.contract_state, advance_time, } @@ -265,24 +308,28 @@ module mock_arb { } } + + +proxy_stubs.qnt: module proxy { - import basicSpells.* from "../lib/basicSpells" - import cw_types.* from "../lib/cw_types" - import messaging.* from "../lib/messaging" - import bank from "../lib/bank" + import basicSpells.* from "./lib/basicSpells" + import cw_types.* from "./lib/cw_types" + import cw_utils.* from "./lib/cw_utils" + import messaging.* from "./lib/messaging" + import bank from "./lib/bank" var contract_state: ContractState - var return: Result + var result: Result var bank: bank::Bank var time: int - pure val CONTRACT_ADDRESS = "" + pure val CONTRACT_ADDRESS = "contract0" - pure val ADDRESSES = Set("s1", "s2", "s3", CONTRACT_ADDRESS) + pure val ADDRESSES = Set("sender1", "sender2", "sender3", CONTRACT_ADDRESS) pure val DENOMS = Set("d1", "uawesome") pure val MAX_AMOUNT = 200 @@ -326,11 +373,25 @@ module proxy { val env_val = { block: { time: time } } - action init = all { - contract_state' = init_contract_state, - bank' = init_bank_state, - return' = Err("No previous request"), - time' = 0, + action init = { + // TODO: Change next line according to fund expectations + pure val max_funds = 0 + + nondet sender = Set("admin").oneOf() + nondet denom = DENOMS.oneOf() + nondet amount = 0.to(max_funds).oneOf() + val funds = [{ denom: denom, amount: amount }] + val info = { sender: sender, funds: funds } + + pure val message: InstantiateMsg = + pure val r = instantiate(init_contract_state, { block: { time: 0 } }, info, message) + + all { + contract_state' = r._2, + bank' = init_bank_state, + result' = r._1, + time' = 0, + } } @@ -344,9 +405,12 @@ module proxy { val r = execute(contract_state, env_val, info, message) all { bank.get(sender).get(denom) >= amount, - bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) - .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)), - return' = r._1, + match r._1 { + | Ok(_) => bank' = bank.setBy(sender, balances => balances.setBy(denom, balance => balance - amount)) + .setBy(CONTRACT_ADDRESS, balances => balances.setBy(denom, balance => balance + amount)) + | Err(_) => bank' = bank + }, + result' = r._1, contract_state' = r._2, } } @@ -354,16 +418,16 @@ module proxy { action advance_time = time' = time + 1 action step = { - val message_getting = get_message(return) - val new_return = message_getting._1 + val message_getting = get_message(result) + val new_result = message_getting._1 val opt_message = message_getting._2 match opt_message { | Some(submsg) => { - val current_state = { bank: bank, return: new_return, contract_state: contract_state } + val current_state = { bank: bank, result: new_result, contract_state: contract_state } val new_state = process_message(current_state, env_val, CONTRACT_ADDRESS, submsg, reply) all { bank' = new_state.bank, - return' = new_state.return, + result' = new_state.result, contract_state' = new_state.contract_state, advance_time, } @@ -373,3 +437,819 @@ module proxy { } } + + +mbt: +flash_loan.rs: + +pub mod state_structs { + use num_bigint::BigInt; + use serde::Deserialize; + use std::collections::HashMap; + use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] + pub struct ContractState { + pub config: Config, + pub flash_loan: FlashLoanState + } + + #[derive(Clone, Debug, Deserialize)] + pub struct NondetPicks { + + #[serde(with = "As::>")] + pub sender: Option, + + #[serde(with = "As::>")] + pub denom: Option, + + #[serde(with = "As::>")] + pub amount: Option + } + + + #[derive(Clone, Debug, Deserialize)] + pub struct Message {} + + #[derive(Clone, Debug, Deserialize)] + pub struct Response { + pub messages: Vec, + } + + #[derive(Clone, Debug, Deserialize)] + pub struct State { + pub contract_state: ContractState, + pub bank: HashMap>, + #[serde(with = "As::>")] + pub result: Result, + pub action_taken: String, + pub nondet_picks: NondetPicks, + pub time: BigInt, + } + +} +#[cfg(test)] +pub mod tests { + use crate::{ + mbt::state_structs::*, + msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, + }; + use cosmwasm_std::{coin, Addr, Uint128}; + use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; + use itf::trace_from_str; + use num_bigint::BigInt; + use num_traits::{ToPrimitive, Zero}; + + pub const DENOM: &str = "uawesome"; + pub const TICK: u64 = 1; + + pub fn mint_tokens(mut app: App, recipient: String, denom: String, amount: Uint128) -> App { + app.sudo(cw_multi_test::SudoMsg::Bank( + cw_multi_test::BankSudo::Mint { + to_address: recipient.to_owned(), + amount: vec![coin(amount.u128(), denom)], + }, + )) + .unwrap(); + app + } + + fn compare_state(test_state: &TestState, app: &App, state: &State) { + // compare contract balances + let balance = app + .wrap() + .query_balance(&test_state.contract_addr, DENOM) + .unwrap() + .amount; + let trace_balance = state + .bank + .get(&test_state.contract_addr.to_string()) + .and_then(|x| x.get(DENOM)) + .and_then(|x| x.to_u128()) + .unwrap_or(0); + println!( + "Contract balance ({:?}) for {DENOM}: {:?} vs {:?}", + test_state.contract_addr, + balance, + Uint128::new(trace_balance) + ); + assert_eq!(balance, Uint128::new(trace_balance)); + + // TODO: Query the contract and compare the state as you wish + } + + fn compare_result( + trace_result: Result, + app_result: Result, + ) { + if trace_result.is_ok() { + assert!( + app_result.is_ok(), + "Action unexpectedly failed, error: {:?}", + app_result.err() + ); + println!("Action successful as expected"); + } else { + assert!( + app_result.is_err(), + "Expected action to fail with error: {:?}", + trace_result.err() + ); + println!("Action failed as expected"); + } + } + + fn funds_from_trace(amount: Option, denom: Option) -> Vec { + if amount.is_none() || denom.is_none() || amount == Some(Zero::zero()) { + return vec![]; + } + + vec![coin( + amount.as_ref().unwrap().to_u128().unwrap(), + denom.unwrap(), + )] + } + + // Testing is stateful. + struct TestState { + // we will only know the contract address once we have processed an `instantiate` step + pub contract_addr: Addr, + } + + #[test] + fn model_test() { + let mut app = App::default(); + let code = ContractWrapper::new( + crate::contract::execute, + crate::contract::instantiate, + crate::contract::query, + ); + let code_id = app.store_code(Box::new(code)); + + // create test state + let mut test_state = TestState { + contract_addr: Addr::unchecked("contract0"), + }; + + // load trace data + let data = include_str!("../quint/test.itf.json"); + let trace: itf::Trace = trace_from_str(data).unwrap(); + + for s in trace.states { + let last_result = s.value.result.clone(); + if last_result.is_ok() && !last_result.unwrap().messages.is_empty() { + println!("Processing messages, skipping"); + continue; + } + + let action_taken = &s.value.action_taken; + let nondet_picks = &s.value.nondet_picks; + let amount = nondet_picks.amount.clone(); + let denom = nondet_picks.denom.clone(); + let sender = nondet_picks.sender.clone(); + + println!("Step number: {:?}", s.meta.index); + + match action_taken.as_str() { + + "flash_loan_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ConstructorForExecuteMsg::FlashLoan { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "transfer_owner_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ConstructorForExecuteMsg::TransferOwner { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "withdraw_funds_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ConstructorForExecuteMsg::WithdrawFunds { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "q::init" => { + println!("Initializing contract."); + + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = InstantiateMsg { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + test_state.contract_addr = app.instantiate_contract( + code_id, + sender, + &msg, + &funds, + "test", + None, + ).unwrap(); + + for (addr, coins) in s.value.bank.clone().iter() { + for (denom, amount) in coins.iter() { + app = mint_tokens( + app, + addr.clone(), + denom.to_string(), + Uint128::new(amount.to_u128().unwrap()), + ); + } + } + } + + + + "settle_loan_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ConstructorForExecuteMsg::SettleLoan { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "set_proxy_addr_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ConstructorForExecuteMsg::SetProxyAddr { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + _ => panic!("Invalid action taken"), + } + compare_state(&test_state, &app, &(s.value.clone())); + println!( + "clock is advancing for {} seconds", + TICK + ); + app.update_block(|block| { + block.time = block.time.plus_seconds(TICK); + }); + println!("-----------------------------------"); + } + } +} + + +mock_arb.rs: + +pub mod state_structs { + use num_bigint::BigInt; + use serde::Deserialize; + use std::collections::HashMap; + use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] + pub struct ContractState { + pub config: Config + } + + #[derive(Clone, Debug, Deserialize)] + pub struct NondetPicks { + + #[serde(with = "As::>")] + pub sender: Option, + + #[serde(with = "As::>")] + pub denom: Option, + + #[serde(with = "As::>")] + pub amount: Option + } + + + #[derive(Clone, Debug, Deserialize)] + pub struct Message {} + + #[derive(Clone, Debug, Deserialize)] + pub struct Response { + pub messages: Vec, + } + + #[derive(Clone, Debug, Deserialize)] + pub struct State { + pub contract_state: ContractState, + pub bank: HashMap>, + #[serde(with = "As::>")] + pub result: Result, + pub action_taken: String, + pub nondet_picks: NondetPicks, + pub time: BigInt, + } + +} +#[cfg(test)] +pub mod tests { + use crate::{ + mbt::state_structs::*, + msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, + }; + use cosmwasm_std::{coin, Addr, Uint128}; + use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; + use itf::trace_from_str; + use num_bigint::BigInt; + use num_traits::{ToPrimitive, Zero}; + + pub const DENOM: &str = "uawesome"; + pub const TICK: u64 = 1; + + pub fn mint_tokens(mut app: App, recipient: String, denom: String, amount: Uint128) -> App { + app.sudo(cw_multi_test::SudoMsg::Bank( + cw_multi_test::BankSudo::Mint { + to_address: recipient.to_owned(), + amount: vec![coin(amount.u128(), denom)], + }, + )) + .unwrap(); + app + } + + fn compare_state(test_state: &TestState, app: &App, state: &State) { + // compare contract balances + let balance = app + .wrap() + .query_balance(&test_state.contract_addr, DENOM) + .unwrap() + .amount; + let trace_balance = state + .bank + .get(&test_state.contract_addr.to_string()) + .and_then(|x| x.get(DENOM)) + .and_then(|x| x.to_u128()) + .unwrap_or(0); + println!( + "Contract balance ({:?}) for {DENOM}: {:?} vs {:?}", + test_state.contract_addr, + balance, + Uint128::new(trace_balance) + ); + assert_eq!(balance, Uint128::new(trace_balance)); + + // TODO: Query the contract and compare the state as you wish + } + + fn compare_result( + trace_result: Result, + app_result: Result, + ) { + if trace_result.is_ok() { + assert!( + app_result.is_ok(), + "Action unexpectedly failed, error: {:?}", + app_result.err() + ); + println!("Action successful as expected"); + } else { + assert!( + app_result.is_err(), + "Expected action to fail with error: {:?}", + trace_result.err() + ); + println!("Action failed as expected"); + } + } + + fn funds_from_trace(amount: Option, denom: Option) -> Vec { + if amount.is_none() || denom.is_none() || amount == Some(Zero::zero()) { + return vec![]; + } + + vec![coin( + amount.as_ref().unwrap().to_u128().unwrap(), + denom.unwrap(), + )] + } + + // Testing is stateful. + struct TestState { + // we will only know the contract address once we have processed an `instantiate` step + pub contract_addr: Addr, + } + + #[test] + fn model_test() { + let mut app = App::default(); + let code = ContractWrapper::new( + crate::contract::execute, + crate::contract::instantiate, + crate::contract::query, + ); + let code_id = app.store_code(Box::new(code)); + + // create test state + let mut test_state = TestState { + contract_addr: Addr::unchecked("contract0"), + }; + + // load trace data + let data = include_str!("../quint/test.itf.json"); + let trace: itf::Trace = trace_from_str(data).unwrap(); + + for s in trace.states { + let last_result = s.value.result.clone(); + if last_result.is_ok() && !last_result.unwrap().messages.is_empty() { + println!("Processing messages, skipping"); + continue; + } + + let action_taken = &s.value.action_taken; + let nondet_picks = &s.value.nondet_picks; + let amount = nondet_picks.amount.clone(); + let denom = nondet_picks.denom.clone(); + let sender = nondet_picks.sender.clone(); + + println!("Step number: {:?}", s.meta.index); + + match action_taken.as_str() { + + "q::init" => { + println!("Initializing contract."); + + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = InstantiateMsg { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + test_state.contract_addr = app.instantiate_contract( + code_id, + sender, + &msg, + &funds, + "test", + None, + ).unwrap(); + + for (addr, coins) in s.value.bank.clone().iter() { + for (denom, amount) in coins.iter() { + app = mint_tokens( + app, + addr.clone(), + denom.to_string(), + Uint128::new(amount.to_u128().unwrap()), + ); + } + } + } + + + + "arbitrage_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ConstructorForExecuteMsg::Arbitrage { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + _ => panic!("Invalid action taken"), + } + compare_state(&test_state, &app, &(s.value.clone())); + println!( + "clock is advancing for {} seconds", + TICK + ); + app.update_block(|block| { + block.time = block.time.plus_seconds(TICK); + }); + println!("-----------------------------------"); + } + } +} + + +proxy.rs: + +pub mod state_structs { + use num_bigint::BigInt; + use serde::Deserialize; + use std::collections::HashMap; + use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] + pub struct ContractState { + pub config: Config + } + + #[derive(Clone, Debug, Deserialize)] + pub struct NondetPicks { + + #[serde(with = "As::>")] + pub sender: Option, + + #[serde(with = "As::>")] + pub denom: Option, + + #[serde(with = "As::>")] + pub amount: Option + } + + + #[derive(Clone, Debug, Deserialize)] + pub struct Message {} + + #[derive(Clone, Debug, Deserialize)] + pub struct Response { + pub messages: Vec, + } + + #[derive(Clone, Debug, Deserialize)] + pub struct State { + pub contract_state: ContractState, + pub bank: HashMap>, + #[serde(with = "As::>")] + pub result: Result, + pub action_taken: String, + pub nondet_picks: NondetPicks, + pub time: BigInt, + } + +} +#[cfg(test)] +pub mod tests { + use crate::{ + mbt::state_structs::*, + msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, + }; + use cosmwasm_std::{coin, Addr, Uint128}; + use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; + use itf::trace_from_str; + use num_bigint::BigInt; + use num_traits::{ToPrimitive, Zero}; + + pub const DENOM: &str = "uawesome"; + pub const TICK: u64 = 1; + + pub fn mint_tokens(mut app: App, recipient: String, denom: String, amount: Uint128) -> App { + app.sudo(cw_multi_test::SudoMsg::Bank( + cw_multi_test::BankSudo::Mint { + to_address: recipient.to_owned(), + amount: vec![coin(amount.u128(), denom)], + }, + )) + .unwrap(); + app + } + + fn compare_state(test_state: &TestState, app: &App, state: &State) { + // compare contract balances + let balance = app + .wrap() + .query_balance(&test_state.contract_addr, DENOM) + .unwrap() + .amount; + let trace_balance = state + .bank + .get(&test_state.contract_addr.to_string()) + .and_then(|x| x.get(DENOM)) + .and_then(|x| x.to_u128()) + .unwrap_or(0); + println!( + "Contract balance ({:?}) for {DENOM}: {:?} vs {:?}", + test_state.contract_addr, + balance, + Uint128::new(trace_balance) + ); + assert_eq!(balance, Uint128::new(trace_balance)); + + // TODO: Query the contract and compare the state as you wish + } + + fn compare_result( + trace_result: Result, + app_result: Result, + ) { + if trace_result.is_ok() { + assert!( + app_result.is_ok(), + "Action unexpectedly failed, error: {:?}", + app_result.err() + ); + println!("Action successful as expected"); + } else { + assert!( + app_result.is_err(), + "Expected action to fail with error: {:?}", + trace_result.err() + ); + println!("Action failed as expected"); + } + } + + fn funds_from_trace(amount: Option, denom: Option) -> Vec { + if amount.is_none() || denom.is_none() || amount == Some(Zero::zero()) { + return vec![]; + } + + vec![coin( + amount.as_ref().unwrap().to_u128().unwrap(), + denom.unwrap(), + )] + } + + // Testing is stateful. + struct TestState { + // we will only know the contract address once we have processed an `instantiate` step + pub contract_addr: Addr, + } + + #[test] + fn model_test() { + let mut app = App::default(); + let code = ContractWrapper::new( + crate::contract::execute, + crate::contract::instantiate, + crate::contract::query, + ); + let code_id = app.store_code(Box::new(code)); + + // create test state + let mut test_state = TestState { + contract_addr: Addr::unchecked("contract0"), + }; + + // load trace data + let data = include_str!("../quint/test.itf.json"); + let trace: itf::Trace = trace_from_str(data).unwrap(); + + for s in trace.states { + let last_result = s.value.result.clone(); + if last_result.is_ok() && !last_result.unwrap().messages.is_empty() { + println!("Processing messages, skipping"); + continue; + } + + let action_taken = &s.value.action_taken; + let nondet_picks = &s.value.nondet_picks; + let amount = nondet_picks.amount.clone(); + let denom = nondet_picks.denom.clone(); + let sender = nondet_picks.sender.clone(); + + println!("Step number: {:?}", s.meta.index); + + match action_taken.as_str() { + + "q::init" => { + println!("Initializing contract."); + + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = InstantiateMsg { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + test_state.contract_addr = app.instantiate_contract( + code_id, + sender, + &msg, + &funds, + "test", + None, + ).unwrap(); + + for (addr, coins) in s.value.bank.clone().iter() { + for (denom, amount) in coins.iter() { + app = mint_tokens( + app, + addr.clone(), + denom.to_string(), + Uint128::new(amount.to_u128().unwrap()), + ); + } + } + } + + + + "request_flash_loan_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ConstructorForExecuteMsg::RequestFlashLoan { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + _ => panic!("Invalid action taken"), + } + compare_state(&test_state, &app, &(s.value.clone())); + println!( + "clock is advancing for {} seconds", + TICK + ); + app.update_block(|block| { + block.time = block.time.plus_seconds(TICK); + }); + println!("-----------------------------------"); + } + } +} diff --git a/tests/snapshots/integration_tests__ctf04.snap b/tests/snapshots/integration_tests__ctf04.snap index 1928991..f454c7d 100644 --- a/tests/snapshots/integration_tests__ctf04.snap +++ b/tests/snapshots/integration_tests__ctf04.snap @@ -2,7 +2,10 @@ source: tests/integration_tests.rs expression: output --- -quint/stubs.qnt: +quint: + + +oaksecurity_cosmwasm_ctf_04_stubs.qnt: module oaksecurity_cosmwasm_ctf_04 { import basicSpells.* from "./lib/basicSpells" @@ -150,7 +153,8 @@ module oaksecurity_cosmwasm_ctf_04 { } -src/mbt.rs: +mbt: +oaksecurity_cosmwasm_ctf_04.rs: pub mod state_structs { use num_bigint::BigInt; @@ -163,13 +167,13 @@ pub mod state_structs { } #[derive(Clone, Debug, Deserialize)] - pub struct Balance { - pub amount: BigInt + pub struct Config { + pub total_supply: BigInt } #[derive(Clone, Debug, Deserialize)] - pub struct Config { - pub total_supply: BigInt + pub struct Balance { + pub amount: BigInt } #[derive(Clone, Debug, Deserialize)] @@ -374,12 +378,12 @@ pub mod tests { - "mint_action" => { + "burn_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - - let msg = ExecuteMsg::Mint { }; + let message_shares = nondet_picks.message_shares.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::Burn { shares: message_shares }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -396,12 +400,12 @@ pub mod tests { - "burn_action" => { + "mint_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_shares = nondet_picks.message_shares.clone().unwrap().to_u64().unwrap().into(); - let msg = ExecuteMsg::Burn { shares: message_shares }; + + let msg = ExecuteMsg::Mint { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); diff --git a/tests/snapshots/integration_tests__ctf05.snap b/tests/snapshots/integration_tests__ctf05.snap index 11d90b8..e94d909 100644 --- a/tests/snapshots/integration_tests__ctf05.snap +++ b/tests/snapshots/integration_tests__ctf05.snap @@ -2,7 +2,10 @@ source: tests/integration_tests.rs expression: output --- -quint/stubs.qnt: +quint: + + +oaksecurity_cosmwasm_ctf_05_stubs.qnt: module oaksecurity_cosmwasm_ctf_05 { import basicSpells.* from "./lib/basicSpells" @@ -198,24 +201,25 @@ module oaksecurity_cosmwasm_ctf_05 { } -src/mbt.rs: +mbt: +oaksecurity_cosmwasm_ctf_05.rs: pub mod state_structs { use num_bigint::BigInt; use serde::Deserialize; use std::collections::HashMap; use itf::de::{self, As}; - #[derive(Clone, Debug, Deserialize)] - pub struct InstantiateMsg { - pub owner: String - } - #[derive(Clone, Debug, Deserialize)] pub struct State { pub current_owner: String, pub proposed_owner: Option[Addr] } + #[derive(Clone, Debug, Deserialize)] + pub struct InstantiateMsg { + pub owner: String + } + #[derive(Clone, Debug, Deserialize)] pub struct ContractState { pub state: State, @@ -392,12 +396,12 @@ pub mod tests { match action_taken.as_str() { - "drop_owner_action" => { + "withdraw_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - - let msg = ExecuteMsg::DropOwnershipProposal { }; + let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::Withdraw { amount: message_amount }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -414,12 +418,12 @@ pub mod tests { - "owner_action_action" => { + "drop_owner_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_msg = nondet_picks.message_msg.clone().unwrap(); - let msg = ExecuteMsg::OwnerAction { msg: message_msg }; + + let msg = ExecuteMsg::DropOwnershipProposal { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -436,12 +440,12 @@ pub mod tests { - "withdraw_action" => { + "owner_action_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); - let msg = ExecuteMsg::Withdraw { amount: message_amount }; + let message_msg = nondet_picks.message_msg.clone().unwrap(); + let msg = ExecuteMsg::OwnerAction { msg: message_msg }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -458,12 +462,12 @@ pub mod tests { - "propose_owner_action" => { + "accept_owner_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_new_owner = nondet_picks.message_new_owner.clone().unwrap(); - let msg = ExecuteMsg::ProposeNewOwner { new_owner: message_new_owner }; + + let msg = ExecuteMsg::AcceptOwnership { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -502,12 +506,12 @@ pub mod tests { - "accept_owner_action" => { + "propose_owner_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - - let msg = ExecuteMsg::AcceptOwnership { }; + let message_new_owner = nondet_picks.message_new_owner.clone().unwrap(); + let msg = ExecuteMsg::ProposeNewOwner { new_owner: message_new_owner }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); diff --git a/tests/snapshots/integration_tests__ctf06.snap b/tests/snapshots/integration_tests__ctf06.snap index 45f429e..b640b4d 100644 --- a/tests/snapshots/integration_tests__ctf06.snap +++ b/tests/snapshots/integration_tests__ctf06.snap @@ -2,7 +2,10 @@ source: tests/integration_tests.rs expression: output --- -quint/stubs.qnt: +quint: + + +oaksecurity_cosmwasm_ctf_06_stubs.qnt: module oaksecurity_cosmwasm_ctf_06 { import basicSpells.* from "./lib/basicSpells" @@ -176,13 +179,21 @@ module oaksecurity_cosmwasm_ctf_06 { } -src/mbt.rs: +mbt: +oaksecurity_cosmwasm_ctf_06.rs: pub mod state_structs { use num_bigint::BigInt; use serde::Deserialize; use std::collections::HashMap; use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] + pub struct InstantiateMsg { + pub token: String, + pub owner: String, + pub window: BigInt + } + #[derive(Clone, Debug, Deserialize)] pub struct Config { pub voting_window: BigInt, @@ -196,13 +207,6 @@ pub mod state_structs { pub timestamp: BigInt } - #[derive(Clone, Debug, Deserialize)] - pub struct InstantiateMsg { - pub token: String, - pub owner: String, - pub window: BigInt - } - #[derive(Clone, Debug, Deserialize)] pub struct ContractState { pub config: Config, @@ -370,28 +374,6 @@ pub mod tests { match action_taken.as_str() { - "propose_action" => { - let sender = Addr::unchecked(sender.unwrap()); - let funds = funds_from_trace(amount, denom); - - - let msg = ExecuteMsg::Propose { }; - println!("Message: {:?}", msg); - println!("Sender: {:?}", sender); - println!("Funds: {:?}", funds); - - let res = app.execute_contract( - sender, - test_state.contract_addr.clone(), - &msg, - &funds, - ); - - compare_result(s.value.result.clone(), res) - } - - - "q::init" => { println!("Initializing contract."); @@ -427,12 +409,12 @@ pub mod tests { - "resolve_proposal_action" => { + "propose_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::ResolveProposal { }; + let msg = ExecuteMsg::Propose { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -449,12 +431,12 @@ pub mod tests { - "receive_cw20_action" => { + "resolve_proposal_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::Receive { }; + let msg = ExecuteMsg::ResolveProposal { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -492,6 +474,28 @@ pub mod tests { } + + "receive_cw20_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ExecuteMsg::Receive { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + _ => panic!("Invalid action taken"), } compare_state(&test_state, &app, &(s.value.clone())); diff --git a/tests/snapshots/integration_tests__ctf07.snap b/tests/snapshots/integration_tests__ctf07.snap index fddb142..327d3ac 100644 --- a/tests/snapshots/integration_tests__ctf07.snap +++ b/tests/snapshots/integration_tests__ctf07.snap @@ -2,7 +2,10 @@ source: tests/integration_tests.rs expression: output --- -quint/stubs.qnt: +quint: + + +oaksecurity_cosmwasm_ctf_07_stubs.qnt: module oaksecurity_cosmwasm_ctf_07 { import basicSpells.* from "./lib/basicSpells" @@ -177,7 +180,8 @@ module oaksecurity_cosmwasm_ctf_07 { } -src/mbt.rs: +mbt: +oaksecurity_cosmwasm_ctf_07.rs: pub mod state_structs { use num_bigint::BigInt; @@ -371,6 +375,28 @@ pub mod tests { match action_taken.as_str() { + "update_config_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_new_threshold = nondet_picks.message_new_threshold.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::UpdateConfig { new_threshold: message_new_threshold }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + "q::init" => { println!("Initializing contract."); @@ -406,34 +432,12 @@ pub mod tests { - "owner_action_action" => { + "deposit_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_msg = nondet_picks.message_msg.clone().unwrap(); - let msg = ExecuteMsg::OwnerAction { msg: message_msg }; - println!("Message: {:?}", msg); - println!("Sender: {:?}", sender); - println!("Funds: {:?}", funds); - - let res = app.execute_contract( - sender, - test_state.contract_addr.clone(), - &msg, - &funds, - ); - - compare_result(s.value.result.clone(), res) - } - - - - "update_config_action" => { - let sender = Addr::unchecked(sender.unwrap()); - let funds = funds_from_trace(amount, denom); - let message_new_threshold = nondet_picks.message_new_threshold.clone().unwrap().to_u64().unwrap().into(); - let msg = ExecuteMsg::UpdateConfig { new_threshold: message_new_threshold }; + let msg = ExecuteMsg::Deposit { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -450,12 +454,12 @@ pub mod tests { - "deposit_action" => { + "withdraw_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - - let msg = ExecuteMsg::Deposit { }; + let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::Withdraw { amount: message_amount }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -472,12 +476,12 @@ pub mod tests { - "withdraw_action" => { + "owner_action_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); - let msg = ExecuteMsg::Withdraw { amount: message_amount }; + let message_msg = nondet_picks.message_msg.clone().unwrap(); + let msg = ExecuteMsg::OwnerAction { msg: message_msg }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); diff --git a/tests/snapshots/integration_tests__ctf08.snap b/tests/snapshots/integration_tests__ctf08.snap index eef0cb4..8620067 100644 --- a/tests/snapshots/integration_tests__ctf08.snap +++ b/tests/snapshots/integration_tests__ctf08.snap @@ -2,7 +2,10 @@ source: tests/integration_tests.rs expression: output --- -quint/stubs.qnt: +quint: + + +oaksecurity_cosmwasm_ctf_08_stubs.qnt: module oaksecurity_cosmwasm_ctf_08 { import basicSpells.* from "./lib/basicSpells" @@ -209,7 +212,8 @@ module oaksecurity_cosmwasm_ctf_08 { } -src/mbt.rs: +mbt: +oaksecurity_cosmwasm_ctf_08.rs: pub mod state_structs { use num_bigint::BigInt; @@ -222,14 +226,24 @@ pub mod state_structs { pub n_sales: BigInt } + #[derive(Clone, Debug, Deserialize)] + pub struct Trade { + pub asked_id: String, + pub to_trade_id: String, + pub trader: String + } + #[derive(Clone, Debug, Deserialize)] pub struct Config { pub nft_contract: String } #[derive(Clone, Debug, Deserialize)] - pub struct GetCountResponse { - pub count: BigInt + pub struct Sale { + pub nft_id: String, + pub price: BigInt, + pub owner: String, + pub tradable: bool } #[derive(Clone, Debug, Deserialize)] @@ -238,18 +252,8 @@ pub mod state_structs { } #[derive(Clone, Debug, Deserialize)] - pub struct Trade { - pub asked_id: String, - pub to_trade_id: String, - pub trader: String - } - - #[derive(Clone, Debug, Deserialize)] - pub struct Sale { - pub nft_id: String, - pub price: BigInt, - pub owner: String, - pub tradable: bool + pub struct GetCountResponse { + pub count: BigInt } #[derive(Clone, Debug, Deserialize)] @@ -448,13 +452,12 @@ pub mod tests { match action_taken.as_str() { - "exec_accept_trade_action" => { + "exec_cancel_sale_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); let message_id = nondet_picks.message_id.clone().unwrap(); - let message_trader = nondet_picks.message_trader.clone().unwrap(); - let msg = ExecuteMsg::AcceptTrade { id: message_id, trader: message_trader }; + let msg = ExecuteMsg::CancelSale { id: message_id }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -493,12 +496,14 @@ pub mod tests { - "exec_cancel_sale_action" => { + "exec_new_sale_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); let message_id = nondet_picks.message_id.clone().unwrap(); - let msg = ExecuteMsg::CancelSale { id: message_id }; + let message_price = nondet_picks.message_price.clone().unwrap().to_u64().unwrap().into(); + let message_tradable = nondet_picks.message_tradable.clone().unwrap(); + let msg = ExecuteMsg::NewSale { id: message_id, price: message_price, tradable: message_tradable }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -515,36 +520,48 @@ pub mod tests { - "exec_new_sale_action" => { + "q::init" => { + println!("Initializing contract."); + let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_id = nondet_picks.message_id.clone().unwrap(); - let message_price = nondet_picks.message_price.clone().unwrap().to_u64().unwrap().into(); - let message_tradable = nondet_picks.message_tradable.clone().unwrap(); - let msg = ExecuteMsg::NewSale { id: message_id, price: message_price, tradable: message_tradable }; + + let msg = InstantiateMsg { nft_address: "" }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); - let res = app.execute_contract( + test_state.contract_addr = app.instantiate_contract( + code_id, sender, - test_state.contract_addr.clone(), &msg, &funds, - ); + "test", + None, + ).unwrap(); - compare_result(s.value.result.clone(), res) + for (addr, coins) in s.value.bank.clone().iter() { + for (denom, amount) in coins.iter() { + app = mint_tokens( + app, + addr.clone(), + denom.to_string(), + Uint128::new(amount.to_u128().unwrap()), + ); + } + } } - "exec_buy_action" => { + "exec_new_trade_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_id = nondet_picks.message_id.clone().unwrap(); - let msg = ExecuteMsg::BuyNFT { id: message_id }; + let message_target = nondet_picks.message_target.clone().unwrap(); + let message_offered = nondet_picks.message_offered.clone().unwrap(); + let msg = ExecuteMsg::NewTrade { target: message_target, offered: message_offered }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -561,13 +578,12 @@ pub mod tests { - "exec_new_trade_action" => { + "exec_buy_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_target = nondet_picks.message_target.clone().unwrap(); - let message_offered = nondet_picks.message_offered.clone().unwrap(); - let msg = ExecuteMsg::NewTrade { target: message_target, offered: message_offered }; + let message_id = nondet_picks.message_id.clone().unwrap(); + let msg = ExecuteMsg::BuyNFT { id: message_id }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -584,37 +600,25 @@ pub mod tests { - "q::init" => { - println!("Initializing contract."); - + "exec_accept_trade_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - - let msg = InstantiateMsg { nft_address: "" }; + let message_id = nondet_picks.message_id.clone().unwrap(); + let message_trader = nondet_picks.message_trader.clone().unwrap(); + let msg = ExecuteMsg::AcceptTrade { id: message_id, trader: message_trader }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); - test_state.contract_addr = app.instantiate_contract( - code_id, + let res = app.execute_contract( sender, + test_state.contract_addr.clone(), &msg, &funds, - "test", - None, - ).unwrap(); + ); - for (addr, coins) in s.value.bank.clone().iter() { - for (denom, amount) in coins.iter() { - app = mint_tokens( - app, - addr.clone(), - denom.to_string(), - Uint128::new(amount.to_u128().unwrap()), - ); - } - } + compare_result(s.value.result.clone(), res) } diff --git a/tests/snapshots/integration_tests__ctf09.snap b/tests/snapshots/integration_tests__ctf09.snap index cf896e4..7125ea3 100644 --- a/tests/snapshots/integration_tests__ctf09.snap +++ b/tests/snapshots/integration_tests__ctf09.snap @@ -2,7 +2,10 @@ source: tests/integration_tests.rs expression: output --- -quint/stubs.qnt: +quint: + + +oaksecurity_cosmwasm_ctf_09_stubs.qnt: module oaksecurity_cosmwasm_ctf_09 { import basicSpells.* from "./lib/basicSpells" @@ -175,20 +178,14 @@ module oaksecurity_cosmwasm_ctf_09 { } -src/mbt.rs: +mbt: +oaksecurity_cosmwasm_ctf_09.rs: pub mod state_structs { use num_bigint::BigInt; use serde::Deserialize; use std::collections::HashMap; use itf::de::{self, As}; - #[derive(Clone, Debug, Deserialize)] - pub struct UserRewardInfo { - pub staked_amount: BigInt, - pub user_index: BigInt, - pub pending_rewards: BigInt - } - #[derive(Clone, Debug, Deserialize)] pub struct State { pub owner: String, @@ -201,6 +198,13 @@ pub mod state_structs { } + #[derive(Clone, Debug, Deserialize)] + pub struct UserRewardInfo { + pub staked_amount: BigInt, + pub user_index: BigInt, + pub pending_rewards: BigInt + } + #[derive(Clone, Debug, Deserialize)] pub struct ContractState { pub state: State, @@ -368,28 +372,6 @@ pub mod tests { match action_taken.as_str() { - "deposit_action" => { - let sender = Addr::unchecked(sender.unwrap()); - let funds = funds_from_trace(amount, denom); - - - let msg = ExecuteMsg::Deposit { }; - println!("Message: {:?}", msg); - println!("Sender: {:?}", sender); - println!("Funds: {:?}", funds); - - let res = app.execute_contract( - sender, - test_state.contract_addr.clone(), - &msg, - &funds, - ); - - compare_result(s.value.result.clone(), res) - } - - - "increase_reward_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -490,6 +472,28 @@ pub mod tests { } + + "deposit_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ExecuteMsg::Deposit { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + _ => panic!("Invalid action taken"), } compare_state(&test_state, &app, &(s.value.clone())); diff --git a/tests/snapshots/integration_tests__ctf10.snap b/tests/snapshots/integration_tests__ctf10.snap index 9167abf..496217d 100644 --- a/tests/snapshots/integration_tests__ctf10.snap +++ b/tests/snapshots/integration_tests__ctf10.snap @@ -2,7 +2,10 @@ source: tests/integration_tests.rs expression: output --- -quint/stubs.qnt: +quint: + + +oaksecurity_cosmwasm_ctf_10_stubs.qnt: module oaksecurity_cosmwasm_ctf_10 { import basicSpells.* from "./lib/basicSpells" @@ -136,20 +139,14 @@ module oaksecurity_cosmwasm_ctf_10 { } -src/mbt.rs: +mbt: +oaksecurity_cosmwasm_ctf_10.rs: pub mod state_structs { use num_bigint::BigInt; use serde::Deserialize; use std::collections::HashMap; use itf::de::{self, As}; - #[derive(Clone, Debug, Deserialize)] - pub struct InstantiateMsg { - pub cw721_code_id: BigInt, - pub mint_per_user: BigInt, - pub whitelisted_users: Vec - } - #[derive(Clone, Debug, Deserialize)] pub struct Config { pub nft_contract: String, @@ -162,6 +159,13 @@ pub mod state_structs { pub users: Vec } + #[derive(Clone, Debug, Deserialize)] + pub struct InstantiateMsg { + pub cw721_code_id: BigInt, + pub mint_per_user: BigInt, + pub whitelisted_users: Vec + } + #[derive(Clone, Debug, Deserialize)] pub struct ContractState { pub config: Config, From f4580daa27ab9b287197c72da61209a6b3619e8f Mon Sep 17 00:00:00 2001 From: bugarela Date: Thu, 23 May 2024 10:33:58 -0300 Subject: [PATCH 13/19] Write tests to a `tests/` folder to avoid handling modules --- README.md | 10 ++ src/lib.rs | 5 +- src/quint-lib-files/basicSpells.qnt | 11 -- src/test_generation/boilerplate.rs | 26 +-- src/test_generation/mod.rs | 2 +- src/translate.rs | 3 - src/types.rs | 3 +- tests/integration_tests.rs | 2 +- tests/snapshots/integration_tests__ctf01.snap | 74 ++++---- tests/snapshots/integration_tests__ctf02.snap | 80 +++++---- tests/snapshots/integration_tests__ctf03.snap | 158 +++++++++--------- tests/snapshots/integration_tests__ctf04.snap | 40 +++-- tests/snapshots/integration_tests__ctf05.snap | 98 ++++++----- tests/snapshots/integration_tests__ctf06.snap | 92 +++++----- tests/snapshots/integration_tests__ctf07.snap | 72 ++++---- tests/snapshots/integration_tests__ctf08.snap | 120 +++++++------ tests/snapshots/integration_tests__ctf09.snap | 76 ++++----- tests/snapshots/integration_tests__ctf10.snap | 18 +- 18 files changed, 434 insertions(+), 456 deletions(-) diff --git a/README.md b/README.md index db99882..b565ec7 100644 --- a/README.md +++ b/README.md @@ -26,3 +26,13 @@ rustup target add wasm32-unknown-unknown ``` bash cargo clean && cargo cosmwasm-to-quint ``` + +4. In order to run the generated tests, you'll need to add some dependencies: +```bash +cargo add itf@0.2.4 --dev +cargo add anyhow@1.0.83 --dev +cargo add num-bigint@0.4.4 --dev +cargo add num-traits@0.2.17 --dev +``` + +5. TODO: Add instructions on how to produce traces for the test diff --git a/src/lib.rs b/src/lib.rs index 9c7a6b2..4e2838d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -134,6 +134,7 @@ fn translate_all_items(tcx: TyCtxt) { fn translate_items(tcx: TyCtxt, crate_name: &str, items: Vec<&rustc_hir::Item>) { let mut ctx = Context { tcx, + crate_name, message_type_for_action: HashMap::from([( "instantiate".to_string(), "InstantiateMsg".to_string(), @@ -190,8 +191,8 @@ fn translate_items(tcx: TyCtxt, crate_name: &str, items: Vec<&rustc_hir::Item>) .expect("Unable to write file"); // write tests to file - std::fs::create_dir_all("src/mbt").expect("Unable to create directory"); - std::fs::write(format!("src/mbt/{}.rs", crate_name), tests).expect("Unable to write file"); + std::fs::create_dir_all("tests").expect("Unable to create directory"); + std::fs::write(format!("tests/mbt_{}.rs", crate_name), tests).expect("Unable to write file"); } // This is the main entry point for the plugin. It prints the generated quint code to STDOUT. diff --git a/src/quint-lib-files/basicSpells.qnt b/src/quint-lib-files/basicSpells.qnt index 1c6d5e3..02f8c54 100644 --- a/src/quint-lib-files/basicSpells.qnt +++ b/src/quint-lib-files/basicSpells.qnt @@ -171,15 +171,4 @@ module basicSpells { pure def listFilter(l: List[a], f: (a) => bool): List[a] = l.foldl([], (acc, e) => if (f(e)) acc.append(e) else acc) - - pure def listMap(l: List[a], f: (a) => b): List[b] = - l.foldl([], (acc, e) => acc.append(f(e))) - - //// Returns a set of the elements in the list. - //// - //// - @param __list a list - //// - @returns a set of the elements in __list - pure def toSet(__list: List[a]): Set[a] = { - __list.foldl(Set(), (__s, __e) => __s.union(Set(__e))) - } } diff --git a/src/test_generation/boilerplate.rs b/src/test_generation/boilerplate.rs index 139a9d3..01156fd 100644 --- a/src/test_generation/boilerplate.rs +++ b/src/test_generation/boilerplate.rs @@ -1,10 +1,18 @@ -pub const TEST_HEADER: &str = " +pub fn test_header(crate_name: &str) -> String { + format!( + " #[cfg(test)] -pub mod tests { - use crate::{ - mbt::state_structs::*, - msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, - }; +pub mod tests {{ + use {crate_name}::contract; + use {crate_name}::msg::{{ExecuteMsg, InstantiateMsg, QueryMsg}}; + +{TEST_AUX} +" + ) +} + +const TEST_AUX: &str = " + use crate::state_structs::*; use cosmwasm_std::{coin, Addr, Uint128}; use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; use itf::trace_from_str; @@ -90,11 +98,7 @@ pub mod tests { #[test] fn model_test() { let mut app = App::default(); - let code = ContractWrapper::new( - crate::contract::execute, - crate::contract::instantiate, - crate::contract::query, - ); + let code = ContractWrapper::new(contract::execute, contract::instantiate, contract::query); let code_id = app.store_code(Box::new(code)); // create test state diff --git a/src/test_generation/mod.rs b/src/test_generation/mod.rs index ddb2796..9abbbab 100644 --- a/src/test_generation/mod.rs +++ b/src/test_generation/mod.rs @@ -8,7 +8,7 @@ pub fn generate_tests(ctx: Context) -> String { format!( "{}{}{}{}", structs::translate_structs(ctx.clone()), - boilerplate::TEST_HEADER, + boilerplate::test_header(ctx.crate_name), actions::translate_actions(ctx), boilerplate::TEST_FOOTER ) diff --git a/src/translate.rs b/src/translate.rs index 6a63883..19012e2 100644 --- a/src/translate.rs +++ b/src/translate.rs @@ -485,9 +485,6 @@ impl Translatable for rustc_hir::Item<'_> { || name.starts_with("ContractError") // skip items from proto files || format!("{:?}", self.span).contains("protos") - // skip items from generated test file - // TODO: use parameterized name instead of hardcoded - || format!("{:?}", self.span).contains("src/mbt.rs") { // skip irrelevant items return "".to_string(); diff --git a/src/types.rs b/src/types.rs index 8bc37b4..ad1637f 100644 --- a/src/types.rs +++ b/src/types.rs @@ -28,8 +28,9 @@ pub struct Function<'f> { } #[derive(Clone)] -pub struct Context<'tcx> { +pub struct Context<'tcx, 'c> { // global + pub crate_name: &'c str, pub message_type_for_action: HashMap, pub constructors: HashMap, pub structs: HashMap>, diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 7db5b21..e6a8297 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -64,7 +64,7 @@ fn run(dir: &str, f: impl FnOnce(&mut Command)) -> Result { .collect::>() .join("\n\n"); - let mut mbt_entries = fs::read_dir(ws.join("src/mbt"))? + let mut mbt_entries = fs::read_dir(ws.join("tests"))? .map(|res| res.map(|e| e.path())) .collect::, io::Error>>()?; mbt_entries.sort(); diff --git a/tests/snapshots/integration_tests__ctf01.snap b/tests/snapshots/integration_tests__ctf01.snap index a326d0e..d1c3535 100644 --- a/tests/snapshots/integration_tests__ctf01.snap +++ b/tests/snapshots/integration_tests__ctf01.snap @@ -155,7 +155,7 @@ module oaksecurity_cosmwasm_ctf_01 { mbt: -oaksecurity_cosmwasm_ctf_01.rs: +mbt_oaksecurity_cosmwasm_ctf_01.rs: pub mod state_structs { use num_bigint::BigInt; @@ -220,10 +220,11 @@ pub mod state_structs { } #[cfg(test)] pub mod tests { - use crate::{ - mbt::state_structs::*, - msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, - }; + use oaksecurity_cosmwasm_ctf_01::contract; + use oaksecurity_cosmwasm_ctf_01::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + + + use crate::state_structs::*; use cosmwasm_std::{coin, Addr, Uint128}; use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; use itf::trace_from_str; @@ -309,11 +310,7 @@ pub mod tests { #[test] fn model_test() { let mut app = App::default(); - let code = ContractWrapper::new( - crate::contract::execute, - crate::contract::instantiate, - crate::contract::query, - ); + let code = ContractWrapper::new(contract::execute, contract::instantiate, contract::query); let code_id = app.store_code(Box::new(code)); // create test state @@ -342,47 +339,35 @@ pub mod tests { match action_taken.as_str() { - "q::init" => { - println!("Initializing contract."); + "withdraw_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - - let msg = InstantiateMsg { count: 0 }; + let message_ids = nondet_picks.message_ids.clone().unwrap().iter().map(|x| x.to_u64().unwrap().into()).collect(); + let msg = ExecuteMsg::Withdraw { ids: message_ids }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); - test_state.contract_addr = app.instantiate_contract( - code_id, + let res = app.execute_contract( sender, + test_state.contract_addr.clone(), &msg, &funds, - "test", - None, - ).unwrap(); + ); - for (addr, coins) in s.value.bank.clone().iter() { - for (denom, amount) in coins.iter() { - app = mint_tokens( - app, - addr.clone(), - denom.to_string(), - Uint128::new(amount.to_u128().unwrap()), - ); - } - } + compare_result(s.value.result.clone(), res) } - "withdraw_action" => { + "deposit_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_ids = nondet_picks.message_ids.clone().unwrap().iter().map(|x| x.to_u64().unwrap().into()).collect(); - let msg = ExecuteMsg::Withdraw { ids: message_ids }; + + let msg = ExecuteMsg::Deposit { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -399,24 +384,37 @@ pub mod tests { - "deposit_action" => { + "q::init" => { + println!("Initializing contract."); + let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::Deposit { }; + let msg = InstantiateMsg { count: 0 }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); - let res = app.execute_contract( + test_state.contract_addr = app.instantiate_contract( + code_id, sender, - test_state.contract_addr.clone(), &msg, &funds, - ); + "test", + None, + ).unwrap(); - compare_result(s.value.result.clone(), res) + for (addr, coins) in s.value.bank.clone().iter() { + for (denom, amount) in coins.iter() { + app = mint_tokens( + app, + addr.clone(), + denom.to_string(), + Uint128::new(amount.to_u128().unwrap()), + ); + } + } } diff --git a/tests/snapshots/integration_tests__ctf02.snap b/tests/snapshots/integration_tests__ctf02.snap index ca4c6c0..3a9a707 100644 --- a/tests/snapshots/integration_tests__ctf02.snap +++ b/tests/snapshots/integration_tests__ctf02.snap @@ -176,7 +176,7 @@ module oaksecurity_cosmwasm_ctf_02 { mbt: -oaksecurity_cosmwasm_ctf_02.rs: +mbt_oaksecurity_cosmwasm_ctf_02.rs: pub mod state_structs { use num_bigint::BigInt; @@ -245,10 +245,11 @@ pub mod state_structs { } #[cfg(test)] pub mod tests { - use crate::{ - mbt::state_structs::*, - msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, - }; + use oaksecurity_cosmwasm_ctf_02::contract; + use oaksecurity_cosmwasm_ctf_02::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + + + use crate::state_structs::*; use cosmwasm_std::{coin, Addr, Uint128}; use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; use itf::trace_from_str; @@ -334,11 +335,7 @@ pub mod tests { #[test] fn model_test() { let mut app = App::default(); - let code = ContractWrapper::new( - crate::contract::execute, - crate::contract::instantiate, - crate::contract::query, - ); + let code = ContractWrapper::new(contract::execute, contract::instantiate, contract::query); let code_id = app.store_code(Box::new(code)); // create test state @@ -367,12 +364,35 @@ pub mod tests { match action_taken.as_str() { - "withdraw_action" => { + + "unstake_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); - let msg = ExecuteMsg::Withdraw { amount: message_amount }; + let message_unlock_amount = nondet_picks.message_unlock_amount.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::Unstake { unlock_amount: message_unlock_amount }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + + "deposit_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ExecuteMsg::Deposit { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -424,34 +444,12 @@ pub mod tests { - "unstake_action" => { - let sender = Addr::unchecked(sender.unwrap()); - let funds = funds_from_trace(amount, denom); - - let message_unlock_amount = nondet_picks.message_unlock_amount.clone().unwrap().to_u64().unwrap().into(); - let msg = ExecuteMsg::Unstake { unlock_amount: message_unlock_amount }; - println!("Message: {:?}", msg); - println!("Sender: {:?}", sender); - println!("Funds: {:?}", funds); - - let res = app.execute_contract( - sender, - test_state.contract_addr.clone(), - &msg, - &funds, - ); - - compare_result(s.value.result.clone(), res) - } - - - - "deposit_action" => { + "stake_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - - let msg = ExecuteMsg::Deposit { }; + let message_lock_amount = nondet_picks.message_lock_amount.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::Stake { lock_amount: message_lock_amount }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -468,12 +466,12 @@ pub mod tests { - "stake_action" => { + "withdraw_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_lock_amount = nondet_picks.message_lock_amount.clone().unwrap().to_u64().unwrap().into(); - let msg = ExecuteMsg::Stake { lock_amount: message_lock_amount }; + let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::Withdraw { amount: message_amount }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); diff --git a/tests/snapshots/integration_tests__ctf03.snap b/tests/snapshots/integration_tests__ctf03.snap index e2977a6..9b50f7c 100644 --- a/tests/snapshots/integration_tests__ctf03.snap +++ b/tests/snapshots/integration_tests__ctf03.snap @@ -440,7 +440,7 @@ module proxy { mbt: -flash_loan.rs: +mbt_flash_loan.rs: pub mod state_structs { use num_bigint::BigInt; @@ -489,10 +489,11 @@ pub mod state_structs { } #[cfg(test)] pub mod tests { - use crate::{ - mbt::state_structs::*, - msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, - }; + use flash_loan::contract; + use flash_loan::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + + + use crate::state_structs::*; use cosmwasm_std::{coin, Addr, Uint128}; use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; use itf::trace_from_str; @@ -578,11 +579,7 @@ pub mod tests { #[test] fn model_test() { let mut app = App::default(); - let code = ContractWrapper::new( - crate::contract::execute, - crate::contract::instantiate, - crate::contract::query, - ); + let code = ContractWrapper::new(contract::execute, contract::instantiate, contract::query); let code_id = app.store_code(Box::new(code)); // create test state @@ -611,6 +608,7 @@ pub mod tests { match action_taken.as_str() { + "flash_loan_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -633,12 +631,12 @@ pub mod tests { - "transfer_owner_action" => { + "withdraw_funds_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ConstructorForExecuteMsg::TransferOwner { }; + let msg = ConstructorForExecuteMsg::WithdrawFunds { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -655,12 +653,12 @@ pub mod tests { - "withdraw_funds_action" => { + "settle_loan_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ConstructorForExecuteMsg::WithdrawFunds { }; + let msg = ConstructorForExecuteMsg::SettleLoan { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -712,12 +710,12 @@ pub mod tests { - "settle_loan_action" => { + "set_proxy_addr_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ConstructorForExecuteMsg::SettleLoan { }; + let msg = ConstructorForExecuteMsg::SetProxyAddr { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -734,12 +732,12 @@ pub mod tests { - "set_proxy_addr_action" => { + "transfer_owner_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ConstructorForExecuteMsg::SetProxyAddr { }; + let msg = ConstructorForExecuteMsg::TransferOwner { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -771,7 +769,7 @@ pub mod tests { } -mock_arb.rs: +mbt_mock_arb.rs: pub mod state_structs { use num_bigint::BigInt; @@ -819,10 +817,11 @@ pub mod state_structs { } #[cfg(test)] pub mod tests { - use crate::{ - mbt::state_structs::*, - msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, - }; + use mock_arb::contract; + use mock_arb::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + + + use crate::state_structs::*; use cosmwasm_std::{coin, Addr, Uint128}; use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; use itf::trace_from_str; @@ -908,11 +907,7 @@ pub mod tests { #[test] fn model_test() { let mut app = App::default(); - let code = ContractWrapper::new( - crate::contract::execute, - crate::contract::instantiate, - crate::contract::query, - ); + let code = ContractWrapper::new(contract::execute, contract::instantiate, contract::query); let code_id = app.store_code(Box::new(code)); // create test state @@ -941,6 +936,29 @@ pub mod tests { match action_taken.as_str() { + + "arbitrage_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ConstructorForExecuteMsg::Arbitrage { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + "q::init" => { println!("Initializing contract."); @@ -975,28 +993,6 @@ pub mod tests { } - - "arbitrage_action" => { - let sender = Addr::unchecked(sender.unwrap()); - let funds = funds_from_trace(amount, denom); - - - let msg = ConstructorForExecuteMsg::Arbitrage { }; - println!("Message: {:?}", msg); - println!("Sender: {:?}", sender); - println!("Funds: {:?}", funds); - - let res = app.execute_contract( - sender, - test_state.contract_addr.clone(), - &msg, - &funds, - ); - - compare_result(s.value.result.clone(), res) - } - - _ => panic!("Invalid action taken"), } compare_state(&test_state, &app, &(s.value.clone())); @@ -1013,7 +1009,7 @@ pub mod tests { } -proxy.rs: +mbt_proxy.rs: pub mod state_structs { use num_bigint::BigInt; @@ -1061,10 +1057,11 @@ pub mod state_structs { } #[cfg(test)] pub mod tests { - use crate::{ - mbt::state_structs::*, - msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, - }; + use proxy::contract; + use proxy::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + + + use crate::state_structs::*; use cosmwasm_std::{coin, Addr, Uint128}; use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; use itf::trace_from_str; @@ -1150,11 +1147,7 @@ pub mod tests { #[test] fn model_test() { let mut app = App::default(); - let code = ContractWrapper::new( - crate::contract::execute, - crate::contract::instantiate, - crate::contract::query, - ); + let code = ContractWrapper::new(contract::execute, contract::instantiate, contract::query); let code_id = app.store_code(Box::new(code)); // create test state @@ -1183,6 +1176,29 @@ pub mod tests { match action_taken.as_str() { + + "request_flash_loan_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ConstructorForExecuteMsg::RequestFlashLoan { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + "q::init" => { println!("Initializing contract."); @@ -1217,28 +1233,6 @@ pub mod tests { } - - "request_flash_loan_action" => { - let sender = Addr::unchecked(sender.unwrap()); - let funds = funds_from_trace(amount, denom); - - - let msg = ConstructorForExecuteMsg::RequestFlashLoan { }; - println!("Message: {:?}", msg); - println!("Sender: {:?}", sender); - println!("Funds: {:?}", funds); - - let res = app.execute_contract( - sender, - test_state.contract_addr.clone(), - &msg, - &funds, - ); - - compare_result(s.value.result.clone(), res) - } - - _ => panic!("Invalid action taken"), } compare_state(&test_state, &app, &(s.value.clone())); diff --git a/tests/snapshots/integration_tests__ctf04.snap b/tests/snapshots/integration_tests__ctf04.snap index f454c7d..4697b9e 100644 --- a/tests/snapshots/integration_tests__ctf04.snap +++ b/tests/snapshots/integration_tests__ctf04.snap @@ -154,13 +154,18 @@ module oaksecurity_cosmwasm_ctf_04 { mbt: -oaksecurity_cosmwasm_ctf_04.rs: +mbt_oaksecurity_cosmwasm_ctf_04.rs: pub mod state_structs { use num_bigint::BigInt; use serde::Deserialize; use std::collections::HashMap; use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] + pub struct Balance { + pub amount: BigInt + } + #[derive(Clone, Debug, Deserialize)] pub struct InstantiateMsg { pub offset: BigInt @@ -171,11 +176,6 @@ pub mod state_structs { pub total_supply: BigInt } - #[derive(Clone, Debug, Deserialize)] - pub struct Balance { - pub amount: BigInt - } - #[derive(Clone, Debug, Deserialize)] pub struct ContractState { pub config: Config, @@ -221,10 +221,11 @@ pub mod state_structs { } #[cfg(test)] pub mod tests { - use crate::{ - mbt::state_structs::*, - msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, - }; + use oaksecurity_cosmwasm_ctf_04::contract; + use oaksecurity_cosmwasm_ctf_04::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + + + use crate::state_structs::*; use cosmwasm_std::{coin, Addr, Uint128}; use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; use itf::trace_from_str; @@ -310,11 +311,7 @@ pub mod tests { #[test] fn model_test() { let mut app = App::default(); - let code = ContractWrapper::new( - crate::contract::execute, - crate::contract::instantiate, - crate::contract::query, - ); + let code = ContractWrapper::new(contract::execute, contract::instantiate, contract::query); let code_id = app.store_code(Box::new(code)); // create test state @@ -343,6 +340,7 @@ pub mod tests { match action_taken.as_str() { + "q::init" => { println!("Initializing contract."); @@ -378,12 +376,12 @@ pub mod tests { - "burn_action" => { + "mint_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_shares = nondet_picks.message_shares.clone().unwrap().to_u64().unwrap().into(); - let msg = ExecuteMsg::Burn { shares: message_shares }; + + let msg = ExecuteMsg::Mint { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -400,12 +398,12 @@ pub mod tests { - "mint_action" => { + "burn_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - - let msg = ExecuteMsg::Mint { }; + let message_shares = nondet_picks.message_shares.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::Burn { shares: message_shares }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); diff --git a/tests/snapshots/integration_tests__ctf05.snap b/tests/snapshots/integration_tests__ctf05.snap index e94d909..da8fa04 100644 --- a/tests/snapshots/integration_tests__ctf05.snap +++ b/tests/snapshots/integration_tests__ctf05.snap @@ -202,7 +202,7 @@ module oaksecurity_cosmwasm_ctf_05 { mbt: -oaksecurity_cosmwasm_ctf_05.rs: +mbt_oaksecurity_cosmwasm_ctf_05.rs: pub mod state_structs { use num_bigint::BigInt; @@ -274,10 +274,11 @@ pub mod state_structs { } #[cfg(test)] pub mod tests { - use crate::{ - mbt::state_structs::*, - msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, - }; + use oaksecurity_cosmwasm_ctf_05::contract; + use oaksecurity_cosmwasm_ctf_05::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + + + use crate::state_structs::*; use cosmwasm_std::{coin, Addr, Uint128}; use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; use itf::trace_from_str; @@ -363,11 +364,7 @@ pub mod tests { #[test] fn model_test() { let mut app = App::default(); - let code = ContractWrapper::new( - crate::contract::execute, - crate::contract::instantiate, - crate::contract::query, - ); + let code = ContractWrapper::new(contract::execute, contract::instantiate, contract::query); let code_id = app.store_code(Box::new(code)); // create test state @@ -396,12 +393,13 @@ pub mod tests { match action_taken.as_str() { - "withdraw_action" => { + + "drop_owner_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); - let msg = ExecuteMsg::Withdraw { amount: message_amount }; + + let msg = ExecuteMsg::DropOwnershipProposal { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -418,34 +416,47 @@ pub mod tests { - "drop_owner_action" => { + "q::init" => { + println!("Initializing contract."); + let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::DropOwnershipProposal { }; + let msg = InstantiateMsg { owner: "" }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); - let res = app.execute_contract( + test_state.contract_addr = app.instantiate_contract( + code_id, sender, - test_state.contract_addr.clone(), &msg, &funds, - ); + "test", + None, + ).unwrap(); - compare_result(s.value.result.clone(), res) + for (addr, coins) in s.value.bank.clone().iter() { + for (denom, amount) in coins.iter() { + app = mint_tokens( + app, + addr.clone(), + denom.to_string(), + Uint128::new(amount.to_u128().unwrap()), + ); + } + } } - "owner_action_action" => { + "withdraw_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_msg = nondet_picks.message_msg.clone().unwrap(); - let msg = ExecuteMsg::OwnerAction { msg: message_msg }; + let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::Withdraw { amount: message_amount }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -462,12 +473,12 @@ pub mod tests { - "accept_owner_action" => { + "deposit_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::AcceptOwnership { }; + let msg = ExecuteMsg::Deposit { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -484,12 +495,12 @@ pub mod tests { - "deposit_action" => { + "propose_owner_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - - let msg = ExecuteMsg::Deposit { }; + let message_new_owner = nondet_picks.message_new_owner.clone().unwrap(); + let msg = ExecuteMsg::ProposeNewOwner { new_owner: message_new_owner }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -506,12 +517,12 @@ pub mod tests { - "propose_owner_action" => { + "accept_owner_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_new_owner = nondet_picks.message_new_owner.clone().unwrap(); - let msg = ExecuteMsg::ProposeNewOwner { new_owner: message_new_owner }; + + let msg = ExecuteMsg::AcceptOwnership { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -528,37 +539,24 @@ pub mod tests { - "q::init" => { - println!("Initializing contract."); - + "owner_action_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - - let msg = InstantiateMsg { owner: "" }; + let message_msg = nondet_picks.message_msg.clone().unwrap(); + let msg = ExecuteMsg::OwnerAction { msg: message_msg }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); - test_state.contract_addr = app.instantiate_contract( - code_id, + let res = app.execute_contract( sender, + test_state.contract_addr.clone(), &msg, &funds, - "test", - None, - ).unwrap(); + ); - for (addr, coins) in s.value.bank.clone().iter() { - for (denom, amount) in coins.iter() { - app = mint_tokens( - app, - addr.clone(), - denom.to_string(), - Uint128::new(amount.to_u128().unwrap()), - ); - } - } + compare_result(s.value.result.clone(), res) } diff --git a/tests/snapshots/integration_tests__ctf06.snap b/tests/snapshots/integration_tests__ctf06.snap index b640b4d..505d09c 100644 --- a/tests/snapshots/integration_tests__ctf06.snap +++ b/tests/snapshots/integration_tests__ctf06.snap @@ -180,20 +180,13 @@ module oaksecurity_cosmwasm_ctf_06 { mbt: -oaksecurity_cosmwasm_ctf_06.rs: +mbt_oaksecurity_cosmwasm_ctf_06.rs: pub mod state_structs { use num_bigint::BigInt; use serde::Deserialize; use std::collections::HashMap; use itf::de::{self, As}; - #[derive(Clone, Debug, Deserialize)] - pub struct InstantiateMsg { - pub token: String, - pub owner: String, - pub window: BigInt - } - #[derive(Clone, Debug, Deserialize)] pub struct Config { pub voting_window: BigInt, @@ -201,6 +194,13 @@ pub mod state_structs { pub owner: String } + #[derive(Clone, Debug, Deserialize)] + pub struct InstantiateMsg { + pub token: String, + pub owner: String, + pub window: BigInt + } + #[derive(Clone, Debug, Deserialize)] pub struct Proposal { pub proposer: String, @@ -252,10 +252,11 @@ pub mod state_structs { } #[cfg(test)] pub mod tests { - use crate::{ - mbt::state_structs::*, - msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, - }; + use oaksecurity_cosmwasm_ctf_06::contract; + use oaksecurity_cosmwasm_ctf_06::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + + + use crate::state_structs::*; use cosmwasm_std::{coin, Addr, Uint128}; use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; use itf::trace_from_str; @@ -341,11 +342,7 @@ pub mod tests { #[test] fn model_test() { let mut app = App::default(); - let code = ContractWrapper::new( - crate::contract::execute, - crate::contract::instantiate, - crate::contract::query, - ); + let code = ContractWrapper::new(contract::execute, contract::instantiate, contract::query); let code_id = app.store_code(Box::new(code)); // create test state @@ -374,47 +371,35 @@ pub mod tests { match action_taken.as_str() { - "q::init" => { - println!("Initializing contract."); + "propose_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = InstantiateMsg { token: "", owner: "", window: 0 }; + let msg = ExecuteMsg::Propose { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); - test_state.contract_addr = app.instantiate_contract( - code_id, + let res = app.execute_contract( sender, + test_state.contract_addr.clone(), &msg, &funds, - "test", - None, - ).unwrap(); + ); - for (addr, coins) in s.value.bank.clone().iter() { - for (denom, amount) in coins.iter() { - app = mint_tokens( - app, - addr.clone(), - denom.to_string(), - Uint128::new(amount.to_u128().unwrap()), - ); - } - } + compare_result(s.value.result.clone(), res) } - "propose_action" => { + "owner_action_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - - let msg = ExecuteMsg::Propose { }; + let message_action = nondet_picks.message_action.clone().unwrap(); + let msg = ExecuteMsg::OwnerAction { action: message_action }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -431,34 +416,47 @@ pub mod tests { - "resolve_proposal_action" => { + "q::init" => { + println!("Initializing contract."); + let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::ResolveProposal { }; + let msg = InstantiateMsg { token: "", owner: "", window: 0 }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); - let res = app.execute_contract( + test_state.contract_addr = app.instantiate_contract( + code_id, sender, - test_state.contract_addr.clone(), &msg, &funds, - ); + "test", + None, + ).unwrap(); - compare_result(s.value.result.clone(), res) + for (addr, coins) in s.value.bank.clone().iter() { + for (denom, amount) in coins.iter() { + app = mint_tokens( + app, + addr.clone(), + denom.to_string(), + Uint128::new(amount.to_u128().unwrap()), + ); + } + } } - "owner_action_action" => { + "resolve_proposal_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_action = nondet_picks.message_action.clone().unwrap(); - let msg = ExecuteMsg::OwnerAction { action: message_action }; + + let msg = ExecuteMsg::ResolveProposal { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); diff --git a/tests/snapshots/integration_tests__ctf07.snap b/tests/snapshots/integration_tests__ctf07.snap index 327d3ac..4c4a15c 100644 --- a/tests/snapshots/integration_tests__ctf07.snap +++ b/tests/snapshots/integration_tests__ctf07.snap @@ -181,7 +181,7 @@ module oaksecurity_cosmwasm_ctf_07 { mbt: -oaksecurity_cosmwasm_ctf_07.rs: +mbt_oaksecurity_cosmwasm_ctf_07.rs: pub mod state_structs { use num_bigint::BigInt; @@ -253,10 +253,11 @@ pub mod state_structs { } #[cfg(test)] pub mod tests { - use crate::{ - mbt::state_structs::*, - msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, - }; + use oaksecurity_cosmwasm_ctf_07::contract; + use oaksecurity_cosmwasm_ctf_07::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + + + use crate::state_structs::*; use cosmwasm_std::{coin, Addr, Uint128}; use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; use itf::trace_from_str; @@ -342,11 +343,7 @@ pub mod tests { #[test] fn model_test() { let mut app = App::default(); - let code = ContractWrapper::new( - crate::contract::execute, - crate::contract::instantiate, - crate::contract::query, - ); + let code = ContractWrapper::new(contract::execute, contract::instantiate, contract::query); let code_id = app.store_code(Box::new(code)); // create test state @@ -375,27 +372,6 @@ pub mod tests { match action_taken.as_str() { - "update_config_action" => { - let sender = Addr::unchecked(sender.unwrap()); - let funds = funds_from_trace(amount, denom); - - let message_new_threshold = nondet_picks.message_new_threshold.clone().unwrap().to_u64().unwrap().into(); - let msg = ExecuteMsg::UpdateConfig { new_threshold: message_new_threshold }; - println!("Message: {:?}", msg); - println!("Sender: {:?}", sender); - println!("Funds: {:?}", funds); - - let res = app.execute_contract( - sender, - test_state.contract_addr.clone(), - &msg, - &funds, - ); - - compare_result(s.value.result.clone(), res) - } - - "q::init" => { println!("Initializing contract."); @@ -432,6 +408,28 @@ pub mod tests { + "owner_action_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_msg = nondet_picks.message_msg.clone().unwrap(); + let msg = ExecuteMsg::OwnerAction { msg: message_msg }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + "deposit_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -454,12 +452,12 @@ pub mod tests { - "withdraw_action" => { + "update_config_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); - let msg = ExecuteMsg::Withdraw { amount: message_amount }; + let message_new_threshold = nondet_picks.message_new_threshold.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::UpdateConfig { new_threshold: message_new_threshold }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -476,12 +474,12 @@ pub mod tests { - "owner_action_action" => { + "withdraw_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_msg = nondet_picks.message_msg.clone().unwrap(); - let msg = ExecuteMsg::OwnerAction { msg: message_msg }; + let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::Withdraw { amount: message_amount }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); diff --git a/tests/snapshots/integration_tests__ctf08.snap b/tests/snapshots/integration_tests__ctf08.snap index 8620067..6f8e8a6 100644 --- a/tests/snapshots/integration_tests__ctf08.snap +++ b/tests/snapshots/integration_tests__ctf08.snap @@ -213,19 +213,13 @@ module oaksecurity_cosmwasm_ctf_08 { mbt: -oaksecurity_cosmwasm_ctf_08.rs: +mbt_oaksecurity_cosmwasm_ctf_08.rs: pub mod state_structs { use num_bigint::BigInt; use serde::Deserialize; use std::collections::HashMap; use itf::de::{self, As}; - #[derive(Clone, Debug, Deserialize)] - pub struct Operations { - pub n_trades: BigInt, - pub n_sales: BigInt - } - #[derive(Clone, Debug, Deserialize)] pub struct Trade { pub asked_id: String, @@ -238,6 +232,11 @@ pub mod state_structs { pub nft_contract: String } + #[derive(Clone, Debug, Deserialize)] + pub struct GetCountResponse { + pub count: BigInt + } + #[derive(Clone, Debug, Deserialize)] pub struct Sale { pub nft_id: String, @@ -247,13 +246,14 @@ pub mod state_structs { } #[derive(Clone, Debug, Deserialize)] - pub struct InstantiateMsg { - pub nft_address: String + pub struct Operations { + pub n_trades: BigInt, + pub n_sales: BigInt } #[derive(Clone, Debug, Deserialize)] - pub struct GetCountResponse { - pub count: BigInt + pub struct InstantiateMsg { + pub nft_address: String } #[derive(Clone, Debug, Deserialize)] @@ -330,10 +330,11 @@ pub mod state_structs { } #[cfg(test)] pub mod tests { - use crate::{ - mbt::state_structs::*, - msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, - }; + use oaksecurity_cosmwasm_ctf_08::contract; + use oaksecurity_cosmwasm_ctf_08::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + + + use crate::state_structs::*; use cosmwasm_std::{coin, Addr, Uint128}; use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; use itf::trace_from_str; @@ -419,11 +420,7 @@ pub mod tests { #[test] fn model_test() { let mut app = App::default(); - let code = ContractWrapper::new( - crate::contract::execute, - crate::contract::instantiate, - crate::contract::query, - ); + let code = ContractWrapper::new(contract::execute, contract::instantiate, contract::query); let code_id = app.store_code(Box::new(code)); // create test state @@ -452,6 +449,7 @@ pub mod tests { match action_taken.as_str() { + "exec_cancel_sale_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -474,12 +472,12 @@ pub mod tests { - "exec_cancel_trade_action" => { + "exec_buy_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); let message_id = nondet_picks.message_id.clone().unwrap(); - let msg = ExecuteMsg::CancelTrade { id: message_id }; + let msg = ExecuteMsg::BuyNFT { id: message_id }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -496,14 +494,13 @@ pub mod tests { - "exec_new_sale_action" => { + "exec_new_trade_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_id = nondet_picks.message_id.clone().unwrap(); - let message_price = nondet_picks.message_price.clone().unwrap().to_u64().unwrap().into(); - let message_tradable = nondet_picks.message_tradable.clone().unwrap(); - let msg = ExecuteMsg::NewSale { id: message_id, price: message_price, tradable: message_tradable }; + let message_target = nondet_picks.message_target.clone().unwrap(); + let message_offered = nondet_picks.message_offered.clone().unwrap(); + let msg = ExecuteMsg::NewTrade { target: message_target, offered: message_offered }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -520,48 +517,35 @@ pub mod tests { - "q::init" => { - println!("Initializing contract."); - + "exec_accept_trade_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - - let msg = InstantiateMsg { nft_address: "" }; + let message_id = nondet_picks.message_id.clone().unwrap(); + let message_trader = nondet_picks.message_trader.clone().unwrap(); + let msg = ExecuteMsg::AcceptTrade { id: message_id, trader: message_trader }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); - test_state.contract_addr = app.instantiate_contract( - code_id, + let res = app.execute_contract( sender, + test_state.contract_addr.clone(), &msg, &funds, - "test", - None, - ).unwrap(); + ); - for (addr, coins) in s.value.bank.clone().iter() { - for (denom, amount) in coins.iter() { - app = mint_tokens( - app, - addr.clone(), - denom.to_string(), - Uint128::new(amount.to_u128().unwrap()), - ); - } - } + compare_result(s.value.result.clone(), res) } - "exec_new_trade_action" => { + "exec_cancel_trade_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_target = nondet_picks.message_target.clone().unwrap(); - let message_offered = nondet_picks.message_offered.clone().unwrap(); - let msg = ExecuteMsg::NewTrade { target: message_target, offered: message_offered }; + let message_id = nondet_picks.message_id.clone().unwrap(); + let msg = ExecuteMsg::CancelTrade { id: message_id }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -578,12 +562,14 @@ pub mod tests { - "exec_buy_action" => { + "exec_new_sale_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); let message_id = nondet_picks.message_id.clone().unwrap(); - let msg = ExecuteMsg::BuyNFT { id: message_id }; + let message_price = nondet_picks.message_price.clone().unwrap().to_u64().unwrap().into(); + let message_tradable = nondet_picks.message_tradable.clone().unwrap(); + let msg = ExecuteMsg::NewSale { id: message_id, price: message_price, tradable: message_tradable }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -600,25 +586,37 @@ pub mod tests { - "exec_accept_trade_action" => { + "q::init" => { + println!("Initializing contract."); + let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_id = nondet_picks.message_id.clone().unwrap(); - let message_trader = nondet_picks.message_trader.clone().unwrap(); - let msg = ExecuteMsg::AcceptTrade { id: message_id, trader: message_trader }; + + let msg = InstantiateMsg { nft_address: "" }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); - let res = app.execute_contract( + test_state.contract_addr = app.instantiate_contract( + code_id, sender, - test_state.contract_addr.clone(), &msg, &funds, - ); + "test", + None, + ).unwrap(); - compare_result(s.value.result.clone(), res) + for (addr, coins) in s.value.bank.clone().iter() { + for (denom, amount) in coins.iter() { + app = mint_tokens( + app, + addr.clone(), + denom.to_string(), + Uint128::new(amount.to_u128().unwrap()), + ); + } + } } diff --git a/tests/snapshots/integration_tests__ctf09.snap b/tests/snapshots/integration_tests__ctf09.snap index 7125ea3..74d2410 100644 --- a/tests/snapshots/integration_tests__ctf09.snap +++ b/tests/snapshots/integration_tests__ctf09.snap @@ -179,20 +179,13 @@ module oaksecurity_cosmwasm_ctf_09 { mbt: -oaksecurity_cosmwasm_ctf_09.rs: +mbt_oaksecurity_cosmwasm_ctf_09.rs: pub mod state_structs { use num_bigint::BigInt; use serde::Deserialize; use std::collections::HashMap; use itf::de::{self, As}; - #[derive(Clone, Debug, Deserialize)] - pub struct State { - pub owner: String, - pub total_staked: BigInt, - pub global_index: BigInt - } - #[derive(Clone, Debug, Deserialize)] pub struct InstantiateMsg { @@ -205,6 +198,13 @@ pub mod state_structs { pub pending_rewards: BigInt } + #[derive(Clone, Debug, Deserialize)] + pub struct State { + pub owner: String, + pub total_staked: BigInt, + pub global_index: BigInt + } + #[derive(Clone, Debug, Deserialize)] pub struct ContractState { pub state: State, @@ -250,10 +250,11 @@ pub mod state_structs { } #[cfg(test)] pub mod tests { - use crate::{ - mbt::state_structs::*, - msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, - }; + use oaksecurity_cosmwasm_ctf_09::contract; + use oaksecurity_cosmwasm_ctf_09::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + + + use crate::state_structs::*; use cosmwasm_std::{coin, Addr, Uint128}; use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; use itf::trace_from_str; @@ -339,11 +340,7 @@ pub mod tests { #[test] fn model_test() { let mut app = App::default(); - let code = ContractWrapper::new( - crate::contract::execute, - crate::contract::instantiate, - crate::contract::query, - ); + let code = ContractWrapper::new(contract::execute, contract::instantiate, contract::query); let code_id = app.store_code(Box::new(code)); // create test state @@ -372,6 +369,7 @@ pub mod tests { match action_taken.as_str() { + "increase_reward_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -394,6 +392,28 @@ pub mod tests { + "deposit_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ExecuteMsg::Deposit { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + "claim_rewards_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -472,28 +492,6 @@ pub mod tests { } - - "deposit_action" => { - let sender = Addr::unchecked(sender.unwrap()); - let funds = funds_from_trace(amount, denom); - - - let msg = ExecuteMsg::Deposit { }; - println!("Message: {:?}", msg); - println!("Sender: {:?}", sender); - println!("Funds: {:?}", funds); - - let res = app.execute_contract( - sender, - test_state.contract_addr.clone(), - &msg, - &funds, - ); - - compare_result(s.value.result.clone(), res) - } - - _ => panic!("Invalid action taken"), } compare_state(&test_state, &app, &(s.value.clone())); diff --git a/tests/snapshots/integration_tests__ctf10.snap b/tests/snapshots/integration_tests__ctf10.snap index 496217d..eb36b0e 100644 --- a/tests/snapshots/integration_tests__ctf10.snap +++ b/tests/snapshots/integration_tests__ctf10.snap @@ -140,7 +140,7 @@ module oaksecurity_cosmwasm_ctf_10 { mbt: -oaksecurity_cosmwasm_ctf_10.rs: +mbt_oaksecurity_cosmwasm_ctf_10.rs: pub mod state_structs { use num_bigint::BigInt; @@ -208,10 +208,11 @@ pub mod state_structs { } #[cfg(test)] pub mod tests { - use crate::{ - mbt::state_structs::*, - msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, - }; + use oaksecurity_cosmwasm_ctf_10::contract; + use oaksecurity_cosmwasm_ctf_10::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + + + use crate::state_structs::*; use cosmwasm_std::{coin, Addr, Uint128}; use cw_multi_test::{App, AppResponse, ContractWrapper, Executor}; use itf::trace_from_str; @@ -297,11 +298,7 @@ pub mod tests { #[test] fn model_test() { let mut app = App::default(); - let code = ContractWrapper::new( - crate::contract::execute, - crate::contract::instantiate, - crate::contract::query, - ); + let code = ContractWrapper::new(contract::execute, contract::instantiate, contract::query); let code_id = app.store_code(Box::new(code)); // create test state @@ -330,6 +327,7 @@ pub mod tests { match action_taken.as_str() { + "q::init" => { println!("Initializing contract."); From 3aec37b2aab0c83ccd91afa2f5ff1f207b63921e Mon Sep 17 00:00:00 2001 From: bugarela Date: Thu, 23 May 2024 14:51:58 -0300 Subject: [PATCH 14/19] Make code generation deterministic so snapshot testing works --- src/test_generation/actions.rs | 93 +++++++-------- src/test_generation/structs.rs | 30 ++--- tests/snapshots/integration_tests__ctf01.snap | 44 ++++---- tests/snapshots/integration_tests__ctf02.snap | 44 ++++---- tests/snapshots/integration_tests__ctf03.snap | 106 +++++++++--------- tests/snapshots/integration_tests__ctf04.snap | 52 ++++----- tests/snapshots/integration_tests__ctf05.snap | 88 +++++++-------- tests/snapshots/integration_tests__ctf06.snap | 58 +++++----- tests/snapshots/integration_tests__ctf07.snap | 44 ++++---- tests/snapshots/integration_tests__ctf08.snap | 60 +++++----- tests/snapshots/integration_tests__ctf09.snap | 22 ++-- tests/snapshots/integration_tests__ctf10.snap | 10 +- 12 files changed, 330 insertions(+), 321 deletions(-) diff --git a/src/test_generation/actions.rs b/src/test_generation/actions.rs index 7a72bff..6d4b07e 100644 --- a/src/test_generation/actions.rs +++ b/src/test_generation/actions.rs @@ -3,56 +3,61 @@ use crate::types::{fallback_constructor, Context}; use itertools::Itertools; pub fn translate_actions(ctx: Context) -> String { - let msgs = ctx.message_type_for_action.iter().map(|(action, ty)| { - if action == "instantiate" { - let msg_struct = ctx - .structs - .get("InstantiateMsg") + let msgs = ctx + .message_type_for_action + .iter() + // Sort items by name so that the generated code is deterministic + .sorted_by(|a, b| a.0.cmp(b.0)) + .map(|(action, ty)| { + if action == "instantiate" { + let msg_struct = ctx + .structs + .get("InstantiateMsg") + .cloned() + .unwrap_or_default(); + let msg_fields = msg_struct + .iter() + .map(|f| { + let body = init_value_for_type(&ctx, f.ty.clone()); + + format!("{}: {}", f.name, body) + }) + .collect_vec(); + let msg = format!("InstantiateMsg {{ {} }}", msg_fields.join(", ")); + return translate_init(msg); + } + if action == "execute" || action == "instantiate" || action == "reply" { + return "".to_string(); + } + let constructor = ctx + .constructors + .get(ty.as_str()) .cloned() - .unwrap_or_default(); - let msg_fields = msg_struct + .unwrap_or_else(|| fallback_constructor(ty)); + + let nondet_picks = constructor + .fields .iter() .map(|f| { - let body = init_value_for_type(&ctx, f.ty.clone()); + let body = type_conversion( + format!("nondet_picks.message_{}.clone().unwrap()", f.name), + f.ty.clone(), + ); - format!("{}: {}", f.name, body) + format!(" let message_{} = {};", f.name, body) }) .collect_vec(); - let msg = format!("InstantiateMsg {{ {} }}", msg_fields.join(", ")); - return translate_init(msg); - } - if action == "execute" || action == "instantiate" || action == "reply" { - return "".to_string(); - } - let constructor = ctx - .constructors - .get(ty.as_str()) - .cloned() - .unwrap_or_else(|| fallback_constructor(ty)); - - let nondet_picks = constructor - .fields - .iter() - .map(|f| { - let body = type_conversion( - format!("nondet_picks.message_{}.clone().unwrap()", f.name), - f.ty.clone(), - ); - - format!(" let message_{} = {};", f.name, body) - }) - .collect_vec(); - - let fields = constructor - .fields - .iter() - .map(|f| format!("{}: message_{}", f.name, f.name)) - .collect_vec() - .join(", "); - let msg = format!("{} {{ {} }}", constructor.name.replace('_', "::"), fields); - - translate_action(action, msg, nondet_picks.clone()) - }); + + let fields = constructor + .fields + .iter() + .map(|f| format!("{}: message_{}", f.name, f.name)) + .collect_vec() + .join(", "); + let msg = format!("{} {{ {} }}", constructor.name.replace('_', "::"), fields); + + translate_action(action, msg, nondet_picks.clone()) + }); msgs.clone().join("\n") } diff --git a/src/test_generation/structs.rs b/src/test_generation/structs.rs index 943ef76..243f88e 100644 --- a/src/test_generation/structs.rs +++ b/src/test_generation/structs.rs @@ -10,19 +10,23 @@ pub mod state_structs { use itf::de::{self, As}; " .to_string(); - for (name, fields) in ctx.structs { - structs.push_str( - format_struct( - name, - fields - .iter() - .map(|f| (f.name.clone(), f.ty.clone())) - .collect_vec(), - false, - ) - .as_str(), - ); - } + ctx.structs + .iter() + // Sort items by name so that the generated code is deterministic + .sorted_by(|a, b| a.0.cmp(b.0)) + .for_each(|(name, fields)| { + structs.push_str( + format_struct( + name.to_string(), + fields + .iter() + .map(|f| (f.name.clone(), f.ty.clone())) + .collect_vec(), + false, + ) + .as_str(), + ); + }); structs.push_str( format_struct( "ContractState".to_string(), diff --git a/tests/snapshots/integration_tests__ctf01.snap b/tests/snapshots/integration_tests__ctf01.snap index d1c3535..995f47d 100644 --- a/tests/snapshots/integration_tests__ctf01.snap +++ b/tests/snapshots/integration_tests__ctf01.snap @@ -340,28 +340,6 @@ pub mod tests { match action_taken.as_str() { - "withdraw_action" => { - let sender = Addr::unchecked(sender.unwrap()); - let funds = funds_from_trace(amount, denom); - - let message_ids = nondet_picks.message_ids.clone().unwrap().iter().map(|x| x.to_u64().unwrap().into()).collect(); - let msg = ExecuteMsg::Withdraw { ids: message_ids }; - println!("Message: {:?}", msg); - println!("Sender: {:?}", sender); - println!("Funds: {:?}", funds); - - let res = app.execute_contract( - sender, - test_state.contract_addr.clone(), - &msg, - &funds, - ); - - compare_result(s.value.result.clone(), res) - } - - - "deposit_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -418,6 +396,28 @@ pub mod tests { } + + "withdraw_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_ids = nondet_picks.message_ids.clone().unwrap().iter().map(|x| x.to_u64().unwrap().into()).collect(); + let msg = ExecuteMsg::Withdraw { ids: message_ids }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + _ => panic!("Invalid action taken"), } compare_state(&test_state, &app, &(s.value.clone())); diff --git a/tests/snapshots/integration_tests__ctf02.snap b/tests/snapshots/integration_tests__ctf02.snap index 3a9a707..b3cbbf5 100644 --- a/tests/snapshots/integration_tests__ctf02.snap +++ b/tests/snapshots/integration_tests__ctf02.snap @@ -365,28 +365,6 @@ pub mod tests { match action_taken.as_str() { - "unstake_action" => { - let sender = Addr::unchecked(sender.unwrap()); - let funds = funds_from_trace(amount, denom); - - let message_unlock_amount = nondet_picks.message_unlock_amount.clone().unwrap().to_u64().unwrap().into(); - let msg = ExecuteMsg::Unstake { unlock_amount: message_unlock_amount }; - println!("Message: {:?}", msg); - println!("Sender: {:?}", sender); - println!("Funds: {:?}", funds); - - let res = app.execute_contract( - sender, - test_state.contract_addr.clone(), - &msg, - &funds, - ); - - compare_result(s.value.result.clone(), res) - } - - - "deposit_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -466,6 +444,28 @@ pub mod tests { + "unstake_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_unlock_amount = nondet_picks.message_unlock_amount.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::Unstake { unlock_amount: message_unlock_amount }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + "withdraw_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); diff --git a/tests/snapshots/integration_tests__ctf03.snap b/tests/snapshots/integration_tests__ctf03.snap index 9b50f7c..09fb9d7 100644 --- a/tests/snapshots/integration_tests__ctf03.snap +++ b/tests/snapshots/integration_tests__ctf03.snap @@ -631,34 +631,47 @@ pub mod tests { - "withdraw_funds_action" => { + "q::init" => { + println!("Initializing contract."); + let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ConstructorForExecuteMsg::WithdrawFunds { }; + let msg = InstantiateMsg { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); - let res = app.execute_contract( + test_state.contract_addr = app.instantiate_contract( + code_id, sender, - test_state.contract_addr.clone(), &msg, &funds, - ); + "test", + None, + ).unwrap(); - compare_result(s.value.result.clone(), res) + for (addr, coins) in s.value.bank.clone().iter() { + for (denom, amount) in coins.iter() { + app = mint_tokens( + app, + addr.clone(), + denom.to_string(), + Uint128::new(amount.to_u128().unwrap()), + ); + } + } } - "settle_loan_action" => { + "set_proxy_addr_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ConstructorForExecuteMsg::SettleLoan { }; + let msg = ConstructorForExecuteMsg::SetProxyAddr { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -675,47 +688,34 @@ pub mod tests { - "q::init" => { - println!("Initializing contract."); - + "settle_loan_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = InstantiateMsg { }; + let msg = ConstructorForExecuteMsg::SettleLoan { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); - test_state.contract_addr = app.instantiate_contract( - code_id, + let res = app.execute_contract( sender, + test_state.contract_addr.clone(), &msg, &funds, - "test", - None, - ).unwrap(); + ); - for (addr, coins) in s.value.bank.clone().iter() { - for (denom, amount) in coins.iter() { - app = mint_tokens( - app, - addr.clone(), - denom.to_string(), - Uint128::new(amount.to_u128().unwrap()), - ); - } - } + compare_result(s.value.result.clone(), res) } - "set_proxy_addr_action" => { + "transfer_owner_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ConstructorForExecuteMsg::SetProxyAddr { }; + let msg = ConstructorForExecuteMsg::TransferOwner { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -732,12 +732,12 @@ pub mod tests { - "transfer_owner_action" => { + "withdraw_funds_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ConstructorForExecuteMsg::TransferOwner { }; + let msg = ConstructorForExecuteMsg::WithdrawFunds { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -1177,28 +1177,6 @@ pub mod tests { match action_taken.as_str() { - "request_flash_loan_action" => { - let sender = Addr::unchecked(sender.unwrap()); - let funds = funds_from_trace(amount, denom); - - - let msg = ConstructorForExecuteMsg::RequestFlashLoan { }; - println!("Message: {:?}", msg); - println!("Sender: {:?}", sender); - println!("Funds: {:?}", funds); - - let res = app.execute_contract( - sender, - test_state.contract_addr.clone(), - &msg, - &funds, - ); - - compare_result(s.value.result.clone(), res) - } - - - "q::init" => { println!("Initializing contract."); @@ -1233,6 +1211,28 @@ pub mod tests { } + + "request_flash_loan_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ConstructorForExecuteMsg::RequestFlashLoan { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + _ => panic!("Invalid action taken"), } compare_state(&test_state, &app, &(s.value.clone())); diff --git a/tests/snapshots/integration_tests__ctf04.snap b/tests/snapshots/integration_tests__ctf04.snap index 4697b9e..e73ad7f 100644 --- a/tests/snapshots/integration_tests__ctf04.snap +++ b/tests/snapshots/integration_tests__ctf04.snap @@ -167,13 +167,13 @@ pub mod state_structs { } #[derive(Clone, Debug, Deserialize)] - pub struct InstantiateMsg { - pub offset: BigInt + pub struct Config { + pub total_supply: BigInt } #[derive(Clone, Debug, Deserialize)] - pub struct Config { - pub total_supply: BigInt + pub struct InstantiateMsg { + pub offset: BigInt } #[derive(Clone, Debug, Deserialize)] @@ -341,6 +341,28 @@ pub mod tests { match action_taken.as_str() { + "burn_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + let message_shares = nondet_picks.message_shares.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::Burn { shares: message_shares }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + "q::init" => { println!("Initializing contract."); @@ -397,28 +419,6 @@ pub mod tests { } - - "burn_action" => { - let sender = Addr::unchecked(sender.unwrap()); - let funds = funds_from_trace(amount, denom); - - let message_shares = nondet_picks.message_shares.clone().unwrap().to_u64().unwrap().into(); - let msg = ExecuteMsg::Burn { shares: message_shares }; - println!("Message: {:?}", msg); - println!("Sender: {:?}", sender); - println!("Funds: {:?}", funds); - - let res = app.execute_contract( - sender, - test_state.contract_addr.clone(), - &msg, - &funds, - ); - - compare_result(s.value.result.clone(), res) - } - - _ => panic!("Invalid action taken"), } compare_state(&test_state, &app, &(s.value.clone())); diff --git a/tests/snapshots/integration_tests__ctf05.snap b/tests/snapshots/integration_tests__ctf05.snap index da8fa04..ca92e5b 100644 --- a/tests/snapshots/integration_tests__ctf05.snap +++ b/tests/snapshots/integration_tests__ctf05.snap @@ -210,14 +210,14 @@ pub mod state_structs { use std::collections::HashMap; use itf::de::{self, As}; #[derive(Clone, Debug, Deserialize)] - pub struct State { - pub current_owner: String, - pub proposed_owner: Option[Addr] + pub struct InstantiateMsg { + pub owner: String } #[derive(Clone, Debug, Deserialize)] - pub struct InstantiateMsg { - pub owner: String + pub struct State { + pub current_owner: String, + pub proposed_owner: Option[Addr] } #[derive(Clone, Debug, Deserialize)] @@ -394,12 +394,12 @@ pub mod tests { match action_taken.as_str() { - "drop_owner_action" => { + "accept_owner_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::DropOwnershipProposal { }; + let msg = ExecuteMsg::AcceptOwnership { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -416,47 +416,34 @@ pub mod tests { - "q::init" => { - println!("Initializing contract."); - + "deposit_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = InstantiateMsg { owner: "" }; + let msg = ExecuteMsg::Deposit { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); - test_state.contract_addr = app.instantiate_contract( - code_id, + let res = app.execute_contract( sender, + test_state.contract_addr.clone(), &msg, &funds, - "test", - None, - ).unwrap(); + ); - for (addr, coins) in s.value.bank.clone().iter() { - for (denom, amount) in coins.iter() { - app = mint_tokens( - app, - addr.clone(), - denom.to_string(), - Uint128::new(amount.to_u128().unwrap()), - ); - } - } + compare_result(s.value.result.clone(), res) } - "withdraw_action" => { + "drop_owner_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); - let msg = ExecuteMsg::Withdraw { amount: message_amount }; + + let msg = ExecuteMsg::DropOwnershipProposal { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -473,34 +460,47 @@ pub mod tests { - "deposit_action" => { + "q::init" => { + println!("Initializing contract."); + let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::Deposit { }; + let msg = InstantiateMsg { owner: "" }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); - let res = app.execute_contract( + test_state.contract_addr = app.instantiate_contract( + code_id, sender, - test_state.contract_addr.clone(), &msg, &funds, - ); + "test", + None, + ).unwrap(); - compare_result(s.value.result.clone(), res) + for (addr, coins) in s.value.bank.clone().iter() { + for (denom, amount) in coins.iter() { + app = mint_tokens( + app, + addr.clone(), + denom.to_string(), + Uint128::new(amount.to_u128().unwrap()), + ); + } + } } - "propose_owner_action" => { + "owner_action_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_new_owner = nondet_picks.message_new_owner.clone().unwrap(); - let msg = ExecuteMsg::ProposeNewOwner { new_owner: message_new_owner }; + let message_msg = nondet_picks.message_msg.clone().unwrap(); + let msg = ExecuteMsg::OwnerAction { msg: message_msg }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -517,12 +517,12 @@ pub mod tests { - "accept_owner_action" => { + "propose_owner_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - - let msg = ExecuteMsg::AcceptOwnership { }; + let message_new_owner = nondet_picks.message_new_owner.clone().unwrap(); + let msg = ExecuteMsg::ProposeNewOwner { new_owner: message_new_owner }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -539,12 +539,12 @@ pub mod tests { - "owner_action_action" => { + "withdraw_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_msg = nondet_picks.message_msg.clone().unwrap(); - let msg = ExecuteMsg::OwnerAction { msg: message_msg }; + let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); + let msg = ExecuteMsg::Withdraw { amount: message_amount }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); diff --git a/tests/snapshots/integration_tests__ctf06.snap b/tests/snapshots/integration_tests__ctf06.snap index 505d09c..ddf3a31 100644 --- a/tests/snapshots/integration_tests__ctf06.snap +++ b/tests/snapshots/integration_tests__ctf06.snap @@ -372,24 +372,37 @@ pub mod tests { match action_taken.as_str() { - "propose_action" => { + "q::init" => { + println!("Initializing contract."); + let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::Propose { }; + let msg = InstantiateMsg { token: "", owner: "", window: 0 }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); - let res = app.execute_contract( + test_state.contract_addr = app.instantiate_contract( + code_id, sender, - test_state.contract_addr.clone(), &msg, &funds, - ); + "test", + None, + ).unwrap(); - compare_result(s.value.result.clone(), res) + for (addr, coins) in s.value.bank.clone().iter() { + for (denom, amount) in coins.iter() { + app = mint_tokens( + app, + addr.clone(), + denom.to_string(), + Uint128::new(amount.to_u128().unwrap()), + ); + } + } } @@ -416,47 +429,34 @@ pub mod tests { - "q::init" => { - println!("Initializing contract."); - + "propose_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = InstantiateMsg { token: "", owner: "", window: 0 }; + let msg = ExecuteMsg::Propose { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); - test_state.contract_addr = app.instantiate_contract( - code_id, + let res = app.execute_contract( sender, + test_state.contract_addr.clone(), &msg, &funds, - "test", - None, - ).unwrap(); + ); - for (addr, coins) in s.value.bank.clone().iter() { - for (denom, amount) in coins.iter() { - app = mint_tokens( - app, - addr.clone(), - denom.to_string(), - Uint128::new(amount.to_u128().unwrap()), - ); - } - } + compare_result(s.value.result.clone(), res) } - "resolve_proposal_action" => { + "receive_cw20_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::ResolveProposal { }; + let msg = ExecuteMsg::Receive { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -473,12 +473,12 @@ pub mod tests { - "receive_cw20_action" => { + "resolve_proposal_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::Receive { }; + let msg = ExecuteMsg::ResolveProposal { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); diff --git a/tests/snapshots/integration_tests__ctf07.snap b/tests/snapshots/integration_tests__ctf07.snap index 4c4a15c..1e4711a 100644 --- a/tests/snapshots/integration_tests__ctf07.snap +++ b/tests/snapshots/integration_tests__ctf07.snap @@ -373,6 +373,28 @@ pub mod tests { match action_taken.as_str() { + "deposit_action" => { + let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); + + + let msg = ExecuteMsg::Deposit { }; + println!("Message: {:?}", msg); + println!("Sender: {:?}", sender); + println!("Funds: {:?}", funds); + + let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); + + compare_result(s.value.result.clone(), res) + } + + + "q::init" => { println!("Initializing contract."); @@ -430,28 +452,6 @@ pub mod tests { - "deposit_action" => { - let sender = Addr::unchecked(sender.unwrap()); - let funds = funds_from_trace(amount, denom); - - - let msg = ExecuteMsg::Deposit { }; - println!("Message: {:?}", msg); - println!("Sender: {:?}", sender); - println!("Funds: {:?}", funds); - - let res = app.execute_contract( - sender, - test_state.contract_addr.clone(), - &msg, - &funds, - ); - - compare_result(s.value.result.clone(), res) - } - - - "update_config_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); diff --git a/tests/snapshots/integration_tests__ctf08.snap b/tests/snapshots/integration_tests__ctf08.snap index 6f8e8a6..015ab90 100644 --- a/tests/snapshots/integration_tests__ctf08.snap +++ b/tests/snapshots/integration_tests__ctf08.snap @@ -220,13 +220,6 @@ pub mod state_structs { use serde::Deserialize; use std::collections::HashMap; use itf::de::{self, As}; - #[derive(Clone, Debug, Deserialize)] - pub struct Trade { - pub asked_id: String, - pub to_trade_id: String, - pub trader: String - } - #[derive(Clone, Debug, Deserialize)] pub struct Config { pub nft_contract: String @@ -238,11 +231,8 @@ pub mod state_structs { } #[derive(Clone, Debug, Deserialize)] - pub struct Sale { - pub nft_id: String, - pub price: BigInt, - pub owner: String, - pub tradable: bool + pub struct InstantiateMsg { + pub nft_address: String } #[derive(Clone, Debug, Deserialize)] @@ -252,8 +242,18 @@ pub mod state_structs { } #[derive(Clone, Debug, Deserialize)] - pub struct InstantiateMsg { - pub nft_address: String + pub struct Sale { + pub nft_id: String, + pub price: BigInt, + pub owner: String, + pub tradable: bool + } + + #[derive(Clone, Debug, Deserialize)] + pub struct Trade { + pub asked_id: String, + pub to_trade_id: String, + pub trader: String } #[derive(Clone, Debug, Deserialize)] @@ -450,12 +450,13 @@ pub mod tests { match action_taken.as_str() { - "exec_cancel_sale_action" => { + "exec_accept_trade_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); let message_id = nondet_picks.message_id.clone().unwrap(); - let msg = ExecuteMsg::CancelSale { id: message_id }; + let message_trader = nondet_picks.message_trader.clone().unwrap(); + let msg = ExecuteMsg::AcceptTrade { id: message_id, trader: message_trader }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -494,13 +495,12 @@ pub mod tests { - "exec_new_trade_action" => { + "exec_cancel_sale_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_target = nondet_picks.message_target.clone().unwrap(); - let message_offered = nondet_picks.message_offered.clone().unwrap(); - let msg = ExecuteMsg::NewTrade { target: message_target, offered: message_offered }; + let message_id = nondet_picks.message_id.clone().unwrap(); + let msg = ExecuteMsg::CancelSale { id: message_id }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -517,13 +517,12 @@ pub mod tests { - "exec_accept_trade_action" => { + "exec_cancel_trade_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); let message_id = nondet_picks.message_id.clone().unwrap(); - let message_trader = nondet_picks.message_trader.clone().unwrap(); - let msg = ExecuteMsg::AcceptTrade { id: message_id, trader: message_trader }; + let msg = ExecuteMsg::CancelTrade { id: message_id }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -540,12 +539,14 @@ pub mod tests { - "exec_cancel_trade_action" => { + "exec_new_sale_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); let message_id = nondet_picks.message_id.clone().unwrap(); - let msg = ExecuteMsg::CancelTrade { id: message_id }; + let message_price = nondet_picks.message_price.clone().unwrap().to_u64().unwrap().into(); + let message_tradable = nondet_picks.message_tradable.clone().unwrap(); + let msg = ExecuteMsg::NewSale { id: message_id, price: message_price, tradable: message_tradable }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -562,14 +563,13 @@ pub mod tests { - "exec_new_sale_action" => { + "exec_new_trade_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_id = nondet_picks.message_id.clone().unwrap(); - let message_price = nondet_picks.message_price.clone().unwrap().to_u64().unwrap().into(); - let message_tradable = nondet_picks.message_tradable.clone().unwrap(); - let msg = ExecuteMsg::NewSale { id: message_id, price: message_price, tradable: message_tradable }; + let message_target = nondet_picks.message_target.clone().unwrap(); + let message_offered = nondet_picks.message_offered.clone().unwrap(); + let msg = ExecuteMsg::NewTrade { target: message_target, offered: message_offered }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); diff --git a/tests/snapshots/integration_tests__ctf09.snap b/tests/snapshots/integration_tests__ctf09.snap index 74d2410..4487302 100644 --- a/tests/snapshots/integration_tests__ctf09.snap +++ b/tests/snapshots/integration_tests__ctf09.snap @@ -191,13 +191,6 @@ pub mod state_structs { } - #[derive(Clone, Debug, Deserialize)] - pub struct UserRewardInfo { - pub staked_amount: BigInt, - pub user_index: BigInt, - pub pending_rewards: BigInt - } - #[derive(Clone, Debug, Deserialize)] pub struct State { pub owner: String, @@ -205,6 +198,13 @@ pub mod state_structs { pub global_index: BigInt } + #[derive(Clone, Debug, Deserialize)] + pub struct UserRewardInfo { + pub staked_amount: BigInt, + pub user_index: BigInt, + pub pending_rewards: BigInt + } + #[derive(Clone, Debug, Deserialize)] pub struct ContractState { pub state: State, @@ -370,12 +370,12 @@ pub mod tests { match action_taken.as_str() { - "increase_reward_action" => { + "claim_rewards_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::IncreaseReward { }; + let msg = ExecuteMsg::ClaimRewards { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); @@ -414,12 +414,12 @@ pub mod tests { - "claim_rewards_action" => { + "increase_reward_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::ClaimRewards { }; + let msg = ExecuteMsg::IncreaseReward { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); println!("Funds: {:?}", funds); diff --git a/tests/snapshots/integration_tests__ctf10.snap b/tests/snapshots/integration_tests__ctf10.snap index eb36b0e..afaf3a6 100644 --- a/tests/snapshots/integration_tests__ctf10.snap +++ b/tests/snapshots/integration_tests__ctf10.snap @@ -154,11 +154,6 @@ pub mod state_structs { pub total_tokens: BigInt } - #[derive(Clone, Debug, Deserialize)] - pub struct Whitelist { - pub users: Vec - } - #[derive(Clone, Debug, Deserialize)] pub struct InstantiateMsg { pub cw721_code_id: BigInt, @@ -166,6 +161,11 @@ pub mod state_structs { pub whitelisted_users: Vec } + #[derive(Clone, Debug, Deserialize)] + pub struct Whitelist { + pub users: Vec + } + #[derive(Clone, Debug, Deserialize)] pub struct ContractState { pub config: Config, From d6990e6f0af1302672f4e9cff75ddbe5c4153e3e Mon Sep 17 00:00:00 2001 From: bugarela Date: Fri, 24 May 2024 09:41:49 -0300 Subject: [PATCH 15/19] Improve readability and fix some spacing --- README.md | 1 + src/lib.rs | 7 +- src/test_generation/actions.rs | 194 +++++++----------- src/test_generation/boilerplate.rs | 41 ++++ tests/snapshots/integration_tests__ctf01.snap | 6 +- tests/snapshots/integration_tests__ctf02.snap | 8 +- tests/snapshots/integration_tests__ctf03.snap | 19 +- tests/snapshots/integration_tests__ctf04.snap | 6 +- tests/snapshots/integration_tests__ctf05.snap | 10 +- tests/snapshots/integration_tests__ctf06.snap | 8 +- tests/snapshots/integration_tests__ctf07.snap | 8 +- tests/snapshots/integration_tests__ctf08.snap | 10 +- tests/snapshots/integration_tests__ctf09.snap | 8 +- tests/snapshots/integration_tests__ctf10.snap | 5 +- 14 files changed, 149 insertions(+), 182 deletions(-) diff --git a/README.md b/README.md index b565ec7..b69454a 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ cargo clean && cargo cosmwasm-to-quint 4. In order to run the generated tests, you'll need to add some dependencies: ```bash +cargo add cw-multi-test@0.16.2 --dev cargo add itf@0.2.4 --dev cargo add anyhow@1.0.83 --dev cargo add num-bigint@0.4.4 --dev diff --git a/src/lib.rs b/src/lib.rs index 4e2838d..92de14e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -191,15 +191,17 @@ fn translate_items(tcx: TyCtxt, crate_name: &str, items: Vec<&rustc_hir::Item>) .expect("Unable to write file"); // write tests to file - std::fs::create_dir_all("tests").expect("Unable to create directory"); std::fs::write(format!("tests/mbt_{}.rs", crate_name), tests).expect("Unable to write file"); } // This is the main entry point for the plugin. It prints the generated quint code to STDOUT. fn cosmwasm_to_quint(tcx: TyCtxt, _args: &CosmwasmToQuintPluginArgs) { - // create generated directory + // create directories for the output files (if they don't already exist) std::fs::create_dir_all("quint/lib").expect("Unable to create directory"); + std::fs::create_dir_all("tests").expect("Unable to create directory"); + // Read quint lib files. `include_str!` makes it so they are read at + // compilation time, and therefore get embeded in the cosmwasm-to-quint executable let bank = include_str!("./quint-lib-files/bank.qnt"); let basic_spells = include_str!("./quint-lib-files/basicSpells.qnt"); let bounded_uint = include_str!("./quint-lib-files/BoundedUInt.qnt"); @@ -207,6 +209,7 @@ fn cosmwasm_to_quint(tcx: TyCtxt, _args: &CosmwasmToQuintPluginArgs) { let cw_utils = include_str!("./quint-lib-files/cw_utils.qnt"); let messaging = include_str!("./quint-lib-files/messaging.qnt"); + // Write the lib files in runtime std::fs::write("quint/lib/bank.qnt", bank).expect("Unable to write file"); std::fs::write("quint/lib/basicSpells.qnt", basic_spells).expect("Unable to write file"); std::fs::write("quint/lib/BoundedUInt.qnt", bounded_uint).expect("Unable to write file"); diff --git a/src/test_generation/actions.rs b/src/test_generation/actions.rs index 6d4b07e..584d25a 100644 --- a/src/test_generation/actions.rs +++ b/src/test_generation/actions.rs @@ -1,146 +1,108 @@ use crate::boilerplate::init_value_for_type; +use crate::test_generation::boilerplate::*; use crate::types::{fallback_constructor, Context}; use itertools::Itertools; pub fn translate_actions(ctx: Context) -> String { - let msgs = ctx - .message_type_for_action + ctx.message_type_for_action .iter() // Sort items by name so that the generated code is deterministic .sorted_by(|a, b| a.0.cmp(b.0)) .map(|(action, ty)| { if action == "instantiate" { - let msg_struct = ctx - .structs - .get("InstantiateMsg") - .cloned() - .unwrap_or_default(); - let msg_fields = msg_struct - .iter() - .map(|f| { - let body = init_value_for_type(&ctx, f.ty.clone()); - - format!("{}: {}", f.name, body) - }) - .collect_vec(); - let msg = format!("InstantiateMsg {{ {} }}", msg_fields.join(", ")); + let msg = generate_instantiate_msg(ctx.clone()); return translate_init(msg); } - if action == "execute" || action == "instantiate" || action == "reply" { - return "".to_string(); - } - let constructor = ctx - .constructors - .get(ty.as_str()) - .cloned() - .unwrap_or_else(|| fallback_constructor(ty)); - - let nondet_picks = constructor - .fields - .iter() - .map(|f| { - let body = type_conversion( - format!("nondet_picks.message_{}.clone().unwrap()", f.name), - f.ty.clone(), - ); - - format!(" let message_{} = {};", f.name, body) - }) - .collect_vec(); - - let fields = constructor - .fields - .iter() - .map(|f| format!("{}: message_{}", f.name, f.name)) - .collect_vec() - .join(", "); - let msg = format!("{} {{ {} }}", constructor.name.replace('_', "::"), fields); - - translate_action(action, msg, nondet_picks.clone()) - }); - - msgs.clone().join("\n") + + let msg = generate_message(ctx.clone(), ty.clone()); + translate_action(action, msg) + }) + .join("\n") } -fn translate_action(action: &str, msg: String, nondet_picks: Vec) -> String { - let header = format!( +fn translate_action(action_name: &str, msg: String) -> String { + format!( " - \"{}_action\" => {{ - let sender = Addr::unchecked(sender.unwrap()); - let funds = funds_from_trace(amount, denom); + \"{action_name}_action\" => {{ + {EXTRACT_SENDER_AND_FUNDS} +{msg} + {PRINT_MESSAGE_FIELDS} + {EXECUTE_CONTRACT} + {COMPARE_RESULT} + }} +" + ) +} + +fn translate_init(msg: String) -> String { + format!( + " + \"q::init\" => {{ + println!(\"Initializing contract.\"); -", - action - ); + {EXTRACT_SENDER_AND_FUNDS} + {msg} + {PRINT_MESSAGE_FIELDS} + {INITIALIZE_CONTRACT} + {MINT_TOKENS} + }} - let footer = " - println!(\"Message: {:?}\", msg); - println!(\"Sender: {:?}\", sender); - println!(\"Funds: {:?}\", funds); +" + ) +} - let res = app.execute_contract( - sender, - test_state.contract_addr.clone(), - &msg, - &funds, - ); +fn generate_instantiate_msg(ctx: Context) -> String { + let msg_struct = ctx + .structs + .get("InstantiateMsg") + .cloned() + .unwrap_or_default(); - compare_result(s.value.result.clone(), res) - } + let msg_fields = msg_struct + .iter() + .map(|f| { + let body = init_value_for_type(&ctx, f.ty.clone()); -"; + format!("{}: {}", f.name, body) + }) + .collect_vec(); - format!( - "{}{}\n let msg = {};{}", - header, - nondet_picks.clone().join("\n"), - msg, - footer, - ) + format!("let msg = InstantiateMsg {{ {} }};", msg_fields.join(", ")) } -fn translate_init(msg: String) -> String { - let header = " - \"q::init\" => { - println!(\"Initializing contract.\"); +fn generate_message(ctx: Context, ty: String) -> String { + let constructor = ctx + .constructors + .get(ty.as_str()) + .cloned() + .unwrap_or_else(|| fallback_constructor(ty.as_ref())); - let sender = Addr::unchecked(sender.unwrap()); - let funds = funds_from_trace(amount, denom); + let nondet_picks = constructor + .fields + .iter() + .map(|f| { + let body = type_conversion( + format!("nondet_picks.message_{}.clone().unwrap()", f.name), + f.ty.clone(), + ); + + format!(" let message_{} = {};", f.name, body) + }) + .collect_vec() + .join("\n"); + + let fields = constructor + .fields + .iter() + .map(|f| format!("{}: message_{}", f.name, f.name)) + .collect_vec() + .join(", "); -" - .to_string(); - - let footer = " - println!(\"Message: {:?}\", msg); - println!(\"Sender: {:?}\", sender); - println!(\"Funds: {:?}\", funds); - - test_state.contract_addr = app.instantiate_contract( - code_id, - sender, - &msg, - &funds, - \"test\", - None, - ).unwrap(); - - for (addr, coins) in s.value.bank.clone().iter() { - for (denom, amount) in coins.iter() { - app = mint_tokens( - app, - addr.clone(), - denom.to_string(), - Uint128::new(amount.to_u128().unwrap()), - ); - } - } - } - -"; + let message_type = constructor.name.replace('_', "::"); format!( - "{}\n let msg = {};{}", - header, msg, footer, + "{nondet_picks} + let msg = {message_type} {{ {fields} }};" ) } diff --git a/src/test_generation/boilerplate.rs b/src/test_generation/boilerplate.rs index 01156fd..7199d48 100644 --- a/src/test_generation/boilerplate.rs +++ b/src/test_generation/boilerplate.rs @@ -144,3 +144,44 @@ pub const TEST_FOOTER: &str = " } } "; + +pub const PRINT_MESSAGE_FIELDS: &str = "println!(\"Message: {:?}\", msg); + println!(\"Sender: {:?}\", sender); + println!(\"Funds: {:?}\", funds); +"; + +pub const EXECUTE_CONTRACT: &str = "let res = app.execute_contract( + sender, + test_state.contract_addr.clone(), + &msg, + &funds, + ); +"; + +pub const COMPARE_RESULT: &str = "compare_result(s.value.result.clone(), res)"; + +pub const EXTRACT_SENDER_AND_FUNDS: &str = "let sender = Addr::unchecked(sender.unwrap()); + let funds = funds_from_trace(amount, denom); +"; + +pub const INITIALIZE_CONTRACT: &str = "test_state.contract_addr = app.instantiate_contract( + code_id, + sender, + &msg, + &funds, + \"test\", + None, + ).unwrap(); +"; + +pub const MINT_TOKENS: &str = "for (addr, coins) in s.value.bank.clone().iter() { + for (denom, amount) in coins.iter() { + app = mint_tokens( + app, + addr.clone(), + denom.to_string(), + Uint128::new(amount.to_u128().unwrap()), + ); + } + } +"; diff --git a/tests/snapshots/integration_tests__ctf01.snap b/tests/snapshots/integration_tests__ctf01.snap index 995f47d..da85522 100644 --- a/tests/snapshots/integration_tests__ctf01.snap +++ b/tests/snapshots/integration_tests__ctf01.snap @@ -361,14 +361,12 @@ pub mod tests { } - "q::init" => { println!("Initializing contract."); let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = InstantiateMsg { count: 0 }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -393,7 +391,8 @@ pub mod tests { ); } } - } + + } @@ -417,7 +416,6 @@ pub mod tests { compare_result(s.value.result.clone(), res) } - _ => panic!("Invalid action taken"), } compare_state(&test_state, &app, &(s.value.clone())); diff --git a/tests/snapshots/integration_tests__ctf02.snap b/tests/snapshots/integration_tests__ctf02.snap index b3cbbf5..8cee177 100644 --- a/tests/snapshots/integration_tests__ctf02.snap +++ b/tests/snapshots/integration_tests__ctf02.snap @@ -386,14 +386,12 @@ pub mod tests { } - "q::init" => { println!("Initializing contract."); let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = InstantiateMsg { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -418,7 +416,8 @@ pub mod tests { ); } } - } + + } @@ -443,7 +442,6 @@ pub mod tests { } - "unstake_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -465,7 +463,6 @@ pub mod tests { } - "withdraw_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -486,7 +483,6 @@ pub mod tests { compare_result(s.value.result.clone(), res) } - _ => panic!("Invalid action taken"), } compare_state(&test_state, &app, &(s.value.clone())); diff --git a/tests/snapshots/integration_tests__ctf03.snap b/tests/snapshots/integration_tests__ctf03.snap index 09fb9d7..5af7afc 100644 --- a/tests/snapshots/integration_tests__ctf03.snap +++ b/tests/snapshots/integration_tests__ctf03.snap @@ -630,14 +630,12 @@ pub mod tests { } - "q::init" => { println!("Initializing contract."); let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = InstantiateMsg { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -662,7 +660,8 @@ pub mod tests { ); } } - } + + } @@ -687,7 +686,6 @@ pub mod tests { } - "settle_loan_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -709,7 +707,6 @@ pub mod tests { } - "transfer_owner_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -731,7 +728,6 @@ pub mod tests { } - "withdraw_funds_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -752,7 +748,6 @@ pub mod tests { compare_result(s.value.result.clone(), res) } - _ => panic!("Invalid action taken"), } compare_state(&test_state, &app, &(s.value.clone())); @@ -958,14 +953,12 @@ pub mod tests { } - "q::init" => { println!("Initializing contract."); let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = InstantiateMsg { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -990,7 +983,8 @@ pub mod tests { ); } } - } + + } _ => panic!("Invalid action taken"), @@ -1183,7 +1177,6 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = InstantiateMsg { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -1208,7 +1201,8 @@ pub mod tests { ); } } - } + + } @@ -1232,7 +1226,6 @@ pub mod tests { compare_result(s.value.result.clone(), res) } - _ => panic!("Invalid action taken"), } compare_state(&test_state, &app, &(s.value.clone())); diff --git a/tests/snapshots/integration_tests__ctf04.snap b/tests/snapshots/integration_tests__ctf04.snap index e73ad7f..109d364 100644 --- a/tests/snapshots/integration_tests__ctf04.snap +++ b/tests/snapshots/integration_tests__ctf04.snap @@ -362,14 +362,12 @@ pub mod tests { } - "q::init" => { println!("Initializing contract."); let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = InstantiateMsg { offset: 0 }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -394,7 +392,8 @@ pub mod tests { ); } } - } + + } @@ -418,7 +417,6 @@ pub mod tests { compare_result(s.value.result.clone(), res) } - _ => panic!("Invalid action taken"), } compare_state(&test_state, &app, &(s.value.clone())); diff --git a/tests/snapshots/integration_tests__ctf05.snap b/tests/snapshots/integration_tests__ctf05.snap index ca92e5b..e9e254b 100644 --- a/tests/snapshots/integration_tests__ctf05.snap +++ b/tests/snapshots/integration_tests__ctf05.snap @@ -415,7 +415,6 @@ pub mod tests { } - "deposit_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -437,7 +436,6 @@ pub mod tests { } - "drop_owner_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -459,14 +457,12 @@ pub mod tests { } - "q::init" => { println!("Initializing contract."); let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = InstantiateMsg { owner: "" }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -491,7 +487,8 @@ pub mod tests { ); } } - } + + } @@ -516,7 +513,6 @@ pub mod tests { } - "propose_owner_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -538,7 +534,6 @@ pub mod tests { } - "withdraw_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -559,7 +554,6 @@ pub mod tests { compare_result(s.value.result.clone(), res) } - _ => panic!("Invalid action taken"), } compare_state(&test_state, &app, &(s.value.clone())); diff --git a/tests/snapshots/integration_tests__ctf06.snap b/tests/snapshots/integration_tests__ctf06.snap index ddf3a31..96f6721 100644 --- a/tests/snapshots/integration_tests__ctf06.snap +++ b/tests/snapshots/integration_tests__ctf06.snap @@ -378,7 +378,6 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = InstantiateMsg { token: "", owner: "", window: 0 }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -403,7 +402,8 @@ pub mod tests { ); } } - } + + } @@ -428,7 +428,6 @@ pub mod tests { } - "propose_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -450,7 +449,6 @@ pub mod tests { } - "receive_cw20_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -472,7 +470,6 @@ pub mod tests { } - "resolve_proposal_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -493,7 +490,6 @@ pub mod tests { compare_result(s.value.result.clone(), res) } - _ => panic!("Invalid action taken"), } compare_state(&test_state, &app, &(s.value.clone())); diff --git a/tests/snapshots/integration_tests__ctf07.snap b/tests/snapshots/integration_tests__ctf07.snap index 1e4711a..e9ac002 100644 --- a/tests/snapshots/integration_tests__ctf07.snap +++ b/tests/snapshots/integration_tests__ctf07.snap @@ -394,14 +394,12 @@ pub mod tests { } - "q::init" => { println!("Initializing contract."); let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = InstantiateMsg { owner: "", threshold: 0 }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -426,7 +424,8 @@ pub mod tests { ); } } - } + + } @@ -451,7 +450,6 @@ pub mod tests { } - "update_config_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -473,7 +471,6 @@ pub mod tests { } - "withdraw_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -494,7 +491,6 @@ pub mod tests { compare_result(s.value.result.clone(), res) } - _ => panic!("Invalid action taken"), } compare_state(&test_state, &app, &(s.value.clone())); diff --git a/tests/snapshots/integration_tests__ctf08.snap b/tests/snapshots/integration_tests__ctf08.snap index 015ab90..d43a98f 100644 --- a/tests/snapshots/integration_tests__ctf08.snap +++ b/tests/snapshots/integration_tests__ctf08.snap @@ -472,7 +472,6 @@ pub mod tests { } - "exec_buy_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -494,7 +493,6 @@ pub mod tests { } - "exec_cancel_sale_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -516,7 +514,6 @@ pub mod tests { } - "exec_cancel_trade_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -538,7 +535,6 @@ pub mod tests { } - "exec_new_sale_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -562,7 +558,6 @@ pub mod tests { } - "exec_new_trade_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -585,14 +580,12 @@ pub mod tests { } - "q::init" => { println!("Initializing contract."); let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = InstantiateMsg { nft_address: "" }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -617,7 +610,8 @@ pub mod tests { ); } } - } + + } _ => panic!("Invalid action taken"), diff --git a/tests/snapshots/integration_tests__ctf09.snap b/tests/snapshots/integration_tests__ctf09.snap index 4487302..72a1dbd 100644 --- a/tests/snapshots/integration_tests__ctf09.snap +++ b/tests/snapshots/integration_tests__ctf09.snap @@ -391,7 +391,6 @@ pub mod tests { } - "deposit_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -413,7 +412,6 @@ pub mod tests { } - "increase_reward_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); @@ -435,14 +433,12 @@ pub mod tests { } - "q::init" => { println!("Initializing contract."); let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = InstantiateMsg { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -467,7 +463,8 @@ pub mod tests { ); } } - } + + } @@ -491,7 +488,6 @@ pub mod tests { compare_result(s.value.result.clone(), res) } - _ => panic!("Invalid action taken"), } compare_state(&test_state, &app, &(s.value.clone())); diff --git a/tests/snapshots/integration_tests__ctf10.snap b/tests/snapshots/integration_tests__ctf10.snap index afaf3a6..89e1bf7 100644 --- a/tests/snapshots/integration_tests__ctf10.snap +++ b/tests/snapshots/integration_tests__ctf10.snap @@ -334,7 +334,6 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = InstantiateMsg { cw721_code_id: 0, mint_per_user: 0, whitelisted_users: [] }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -359,7 +358,8 @@ pub mod tests { ); } } - } + + } @@ -383,7 +383,6 @@ pub mod tests { compare_result(s.value.result.clone(), res) } - _ => panic!("Invalid action taken"), } compare_state(&test_state, &app, &(s.value.clone())); From a366a8d9e6bc29a813f9e3d8c17a80b7391a6099 Mon Sep 17 00:00:00 2001 From: bugarela Date: Fri, 24 May 2024 10:48:24 -0300 Subject: [PATCH 16/19] Improve struct definitions and more spacing fixes --- src/test_generation/actions.rs | 34 +++--- src/test_generation/boilerplate.rs | 26 +++++ src/test_generation/mod.rs | 2 +- src/test_generation/structs.rs | 103 +++++++----------- tests/snapshots/integration_tests__ctf01.snap | 29 +++-- tests/snapshots/integration_tests__ctf02.snap | 39 ++++--- tests/snapshots/integration_tests__ctf03.snap | 64 +++++------ tests/snapshots/integration_tests__ctf04.snap | 31 +++--- tests/snapshots/integration_tests__ctf05.snap | 41 ++++--- tests/snapshots/integration_tests__ctf06.snap | 33 +++--- tests/snapshots/integration_tests__ctf07.snap | 37 +++---- tests/snapshots/integration_tests__ctf08.snap | 72 ++++++------ tests/snapshots/integration_tests__ctf09.snap | 35 +++--- tests/snapshots/integration_tests__ctf10.snap | 27 +++-- 14 files changed, 284 insertions(+), 289 deletions(-) diff --git a/src/test_generation/actions.rs b/src/test_generation/actions.rs index 584d25a..0cb37ac 100644 --- a/src/test_generation/actions.rs +++ b/src/test_generation/actions.rs @@ -3,7 +3,10 @@ use crate::test_generation::boilerplate::*; use crate::types::{fallback_constructor, Context}; use itertools::Itertools; -pub fn translate_actions(ctx: Context) -> String { +/// Generates one match arm per action on the model: +/// "foo_action" => // run foo and compare result +/// "bar_action" => // run bar and compare result +pub fn arms_for_actions(ctx: Context) -> String { ctx.message_type_for_action .iter() // Sort items by name so that the generated code is deterministic @@ -11,21 +14,21 @@ pub fn translate_actions(ctx: Context) -> String { .map(|(action, ty)| { if action == "instantiate" { let msg = generate_instantiate_msg(ctx.clone()); - return translate_init(msg); + return arm_for_init(msg); } let msg = generate_message(ctx.clone(), ty.clone()); - translate_action(action, msg) + arm_for_action(action, msg) }) .join("\n") } -fn translate_action(action_name: &str, msg: String) -> String { +fn arm_for_action(action_name: &str, msg: String) -> String { format!( " \"{action_name}_action\" => {{ {EXTRACT_SENDER_AND_FUNDS} -{msg} + {msg} {PRINT_MESSAGE_FIELDS} {EXECUTE_CONTRACT} {COMPARE_RESULT} @@ -34,7 +37,7 @@ fn translate_action(action_name: &str, msg: String) -> String { ) } -fn translate_init(msg: String) -> String { +fn arm_for_init(msg: String) -> String { format!( " \"q::init\" => {{ @@ -46,7 +49,6 @@ fn translate_init(msg: String) -> String { {INITIALIZE_CONTRACT} {MINT_TOKENS} }} - " ) } @@ -86,10 +88,9 @@ fn generate_message(ctx: Context, ty: String) -> String { f.ty.clone(), ); - format!(" let message_{} = {};", f.name, body) + format!("let message_{} = {};", f.name, body) }) - .collect_vec() - .join("\n"); + .collect_vec(); let fields = constructor .fields @@ -100,14 +101,17 @@ fn generate_message(ctx: Context, ty: String) -> String { let message_type = constructor.name.replace('_', "::"); - format!( - "{nondet_picks} - let msg = {message_type} {{ {fields} }};" - ) + let msg_def = format!("let msg = {message_type} {{ {fields} }};"); + + [nondet_picks, vec![msg_def]] + .concat() + .join("\n ") } fn type_conversion(value: String, ty: String) -> String { if ty.starts_with("List") { + // FIXME: we should have an intermediate representation for types so we + // don't have to convert to and from strings like this. return format!( "{}.iter().map(|x| {}).collect()", value, @@ -120,6 +124,8 @@ fn type_conversion(value: String, ty: String) -> String { ); } + // TODO: convert other types + match ty.as_str() { "str" => value, "int" => format!("{}.to_u64().unwrap().into()", value), diff --git a/src/test_generation/boilerplate.rs b/src/test_generation/boilerplate.rs index 7199d48..290fd74 100644 --- a/src/test_generation/boilerplate.rs +++ b/src/test_generation/boilerplate.rs @@ -185,3 +185,29 @@ pub const MINT_TOKENS: &str = "for (addr, coins) in s.value.bank.clone().iter() } } "; + +pub const STRUCTS_MODULE_IMPORTS: &str = "use num_bigint::BigInt; + use serde::Deserialize; + use std::collections::HashMap; + use itf::de::{self, As};"; + +pub const DEFAULT_STRUCTS: &str = " + #[derive(Clone, Debug, Deserialize)] + pub struct Message {} + + #[derive(Clone, Debug, Deserialize)] + pub struct Response { + pub messages: Vec, + } + + #[derive(Clone, Debug, Deserialize)] + pub struct State { + pub contract_state: ContractState, + pub bank: HashMap>, + #[serde(with = \"As::>\")] + pub result: Result, + pub action_taken: String, + pub nondet_picks: NondetPicks, + pub time: BigInt, + } +"; diff --git a/src/test_generation/mod.rs b/src/test_generation/mod.rs index 9abbbab..811b6e1 100644 --- a/src/test_generation/mod.rs +++ b/src/test_generation/mod.rs @@ -9,7 +9,7 @@ pub fn generate_tests(ctx: Context) -> String { "{}{}{}{}", structs::translate_structs(ctx.clone()), boilerplate::test_header(ctx.crate_name), - actions::translate_actions(ctx), + actions::arms_for_actions(ctx), boilerplate::TEST_FOOTER ) } diff --git a/src/test_generation/structs.rs b/src/test_generation/structs.rs index 243f88e..93444d0 100644 --- a/src/test_generation/structs.rs +++ b/src/test_generation/structs.rs @@ -1,48 +1,45 @@ +use crate::test_generation::boilerplate::*; use crate::types::Context; use itertools::Itertools; +/// Defines a module with all necesary structs for the contract state and messages. pub fn translate_structs(ctx: Context) -> String { - let mut structs = " -pub mod state_structs { - use num_bigint::BigInt; - use serde::Deserialize; - use std::collections::HashMap; - use itf::de::{self, As}; -" - .to_string(); - ctx.structs + let contract_structs = ctx + .structs .iter() // Sort items by name so that the generated code is deterministic .sorted_by(|a, b| a.0.cmp(b.0)) - .for_each(|(name, fields)| { - structs.push_str( - format_struct( - name.to_string(), - fields - .iter() - .map(|f| (f.name.clone(), f.ty.clone())) - .collect_vec(), - false, - ) - .as_str(), - ); - }); - structs.push_str( - format_struct( - "ContractState".to_string(), - ctx.contract_state.clone(), - false, - ) - .as_str(), - ); + .map(|(name, fields)| { + let field_tuples = fields + .iter() + .map(|f| (f.name.clone(), f.ty.clone())) + .collect_vec(); + + format_struct(name.to_string(), field_tuples, false) + }) + .collect_vec() + .join("\n"); - structs.push_str( - format_struct("NondetPicks".to_string(), ctx.nondet_picks.clone(), true).as_str(), + let contract_state_struct = format_struct( + "ContractState".to_string(), + ctx.contract_state.clone(), + false, ); - structs.push_str(BOILERPLATE_STRUCTS); + let nondet_picks_struct = + format_struct("NondetPicks".to_string(), ctx.nondet_picks.clone(), true); - format!("{}\n}}", structs) + format!( + " +pub mod state_structs {{ + {STRUCTS_MODULE_IMPORTS} + {contract_structs} + {contract_state_struct} + {nondet_picks_struct} + {DEFAULT_STRUCTS} +}} + " + ) } fn format_struct(name: String, fields: Vec<(String, String)>, optional: bool) -> String { @@ -54,21 +51,26 @@ fn format_struct(name: String, fields: Vec<(String, String)>, optional: bool) -> format!( " #[serde(with = \"As::>\")] - pub {}: Option<{}>", - name, typ + pub {name}: Option<{typ}>" ) } else { - format!(" pub {}: {}", name, typ) + format!("pub {name}: {typ}") } }) - .join(",\n"); + .join(",\n "); + format!( - " #[derive(Clone, Debug, Deserialize)]\n pub struct {} {{\n{}\n }}\n\n", - name, fields + " + #[derive(Clone, Debug, Deserialize)] + pub struct {name} {{ + {fields} + }}" ) } fn translate_type(ty: String) -> String { + // FIXME: we should have an intermediate representation for types so we + // don't have to convert to and from strings like this. if ty.contains("->") { let it = ty.split("->").collect_vec(); let key = it[0]; @@ -98,24 +100,3 @@ fn translate_type(ty: String) -> String { _ => ty, } } - -const BOILERPLATE_STRUCTS: &str = " - #[derive(Clone, Debug, Deserialize)] - pub struct Message {} - - #[derive(Clone, Debug, Deserialize)] - pub struct Response { - pub messages: Vec, - } - - #[derive(Clone, Debug, Deserialize)] - pub struct State { - pub contract_state: ContractState, - pub bank: HashMap>, - #[serde(with = \"As::>\")] - pub result: Result, - pub action_taken: String, - pub nondet_picks: NondetPicks, - pub time: BigInt, - } -"; diff --git a/tests/snapshots/integration_tests__ctf01.snap b/tests/snapshots/integration_tests__ctf01.snap index da85522..d95bf07 100644 --- a/tests/snapshots/integration_tests__ctf01.snap +++ b/tests/snapshots/integration_tests__ctf01.snap @@ -162,42 +162,42 @@ pub mod state_structs { use serde::Deserialize; use std::collections::HashMap; use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] - pub struct InstantiateMsg { + pub struct InstantiateMsg { pub count: BigInt } #[derive(Clone, Debug, Deserialize)] - pub struct Lockup { + pub struct Lockup { pub id: BigInt, pub owner: String, pub amount: BigInt, pub release_timestamp: BigInt } - + #[derive(Clone, Debug, Deserialize)] - pub struct ContractState { + pub struct ContractState { pub last_id: BigInt, pub lockups: HashMap } - + #[derive(Clone, Debug, Deserialize)] - pub struct NondetPicks { - + pub struct NondetPicks { + #[serde(with = "As::>")] pub sender: Option, - + #[serde(with = "As::>")] pub denom: Option, - + #[serde(with = "As::>")] pub amount: Option, - + #[serde(with = "As::>")] pub message_ids: Option> } - - + #[derive(Clone, Debug, Deserialize)] pub struct Message {} @@ -218,6 +218,7 @@ pub mod state_structs { } } + #[cfg(test)] pub mod tests { use oaksecurity_cosmwasm_ctf_01::contract; @@ -344,7 +345,6 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::Deposit { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -395,12 +395,11 @@ pub mod tests { } - "withdraw_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_ids = nondet_picks.message_ids.clone().unwrap().iter().map(|x| x.to_u64().unwrap().into()).collect(); + let message_ids = nondet_picks.message_ids.clone().unwrap().iter().map(|x| x.to_u64().unwrap().into()).collect(); let msg = ExecuteMsg::Withdraw { ids: message_ids }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); diff --git a/tests/snapshots/integration_tests__ctf02.snap b/tests/snapshots/integration_tests__ctf02.snap index 8cee177..e6bcd41 100644 --- a/tests/snapshots/integration_tests__ctf02.snap +++ b/tests/snapshots/integration_tests__ctf02.snap @@ -183,46 +183,46 @@ pub mod state_structs { use serde::Deserialize; use std::collections::HashMap; use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] - pub struct InstantiateMsg { - + pub struct InstantiateMsg { + } #[derive(Clone, Debug, Deserialize)] - pub struct UserInfo { + pub struct UserInfo { pub total_tokens: BigInt, pub voting_power: BigInt, pub released_time: BigInt } - + #[derive(Clone, Debug, Deserialize)] - pub struct ContractState { + pub struct ContractState { pub voting_power: HashMap } - + #[derive(Clone, Debug, Deserialize)] - pub struct NondetPicks { - + pub struct NondetPicks { + #[serde(with = "As::>")] pub sender: Option, - + #[serde(with = "As::>")] pub denom: Option, - + #[serde(with = "As::>")] pub amount: Option, - + #[serde(with = "As::>")] pub message_amount: Option, - + #[serde(with = "As::>")] pub message_lock_amount: Option, - + #[serde(with = "As::>")] pub message_unlock_amount: Option } - - + #[derive(Clone, Debug, Deserialize)] pub struct Message {} @@ -243,6 +243,7 @@ pub mod state_structs { } } + #[cfg(test)] pub mod tests { use oaksecurity_cosmwasm_ctf_02::contract; @@ -369,7 +370,6 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::Deposit { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -420,12 +420,11 @@ pub mod tests { } - "stake_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_lock_amount = nondet_picks.message_lock_amount.clone().unwrap().to_u64().unwrap().into(); + let message_lock_amount = nondet_picks.message_lock_amount.clone().unwrap().to_u64().unwrap().into(); let msg = ExecuteMsg::Stake { lock_amount: message_lock_amount }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -446,7 +445,7 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_unlock_amount = nondet_picks.message_unlock_amount.clone().unwrap().to_u64().unwrap().into(); + let message_unlock_amount = nondet_picks.message_unlock_amount.clone().unwrap().to_u64().unwrap().into(); let msg = ExecuteMsg::Unstake { unlock_amount: message_unlock_amount }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -467,7 +466,7 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); + let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); let msg = ExecuteMsg::Withdraw { amount: message_amount }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); diff --git a/tests/snapshots/integration_tests__ctf03.snap b/tests/snapshots/integration_tests__ctf03.snap index 5af7afc..62aa18f 100644 --- a/tests/snapshots/integration_tests__ctf03.snap +++ b/tests/snapshots/integration_tests__ctf03.snap @@ -447,26 +447,27 @@ pub mod state_structs { use serde::Deserialize; use std::collections::HashMap; use itf::de::{self, As}; + + #[derive(Clone, Debug, Deserialize)] - pub struct ContractState { + pub struct ContractState { pub config: Config, pub flash_loan: FlashLoanState } - + #[derive(Clone, Debug, Deserialize)] - pub struct NondetPicks { - + pub struct NondetPicks { + #[serde(with = "As::>")] pub sender: Option, - + #[serde(with = "As::>")] pub denom: Option, - + #[serde(with = "As::>")] pub amount: Option } - - + #[derive(Clone, Debug, Deserialize)] pub struct Message {} @@ -487,6 +488,7 @@ pub mod state_structs { } } + #[cfg(test)] pub mod tests { use flash_loan::contract; @@ -613,7 +615,6 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ConstructorForExecuteMsg::FlashLoan { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -664,12 +665,10 @@ pub mod tests { } - "set_proxy_addr_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ConstructorForExecuteMsg::SetProxyAddr { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -690,7 +689,6 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ConstructorForExecuteMsg::SettleLoan { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -711,7 +709,6 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ConstructorForExecuteMsg::TransferOwner { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -732,7 +729,6 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ConstructorForExecuteMsg::WithdrawFunds { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -771,25 +767,26 @@ pub mod state_structs { use serde::Deserialize; use std::collections::HashMap; use itf::de::{self, As}; + + #[derive(Clone, Debug, Deserialize)] - pub struct ContractState { + pub struct ContractState { pub config: Config } - + #[derive(Clone, Debug, Deserialize)] - pub struct NondetPicks { - + pub struct NondetPicks { + #[serde(with = "As::>")] pub sender: Option, - + #[serde(with = "As::>")] pub denom: Option, - + #[serde(with = "As::>")] pub amount: Option } - - + #[derive(Clone, Debug, Deserialize)] pub struct Message {} @@ -810,6 +807,7 @@ pub mod state_structs { } } + #[cfg(test)] pub mod tests { use mock_arb::contract; @@ -936,7 +934,6 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ConstructorForExecuteMsg::Arbitrage { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -986,7 +983,6 @@ pub mod tests { } - _ => panic!("Invalid action taken"), } compare_state(&test_state, &app, &(s.value.clone())); @@ -1010,25 +1006,26 @@ pub mod state_structs { use serde::Deserialize; use std::collections::HashMap; use itf::de::{self, As}; + + #[derive(Clone, Debug, Deserialize)] - pub struct ContractState { + pub struct ContractState { pub config: Config } - + #[derive(Clone, Debug, Deserialize)] - pub struct NondetPicks { - + pub struct NondetPicks { + #[serde(with = "As::>")] pub sender: Option, - + #[serde(with = "As::>")] pub denom: Option, - + #[serde(with = "As::>")] pub amount: Option } - - + #[derive(Clone, Debug, Deserialize)] pub struct Message {} @@ -1049,6 +1046,7 @@ pub mod state_structs { } } + #[cfg(test)] pub mod tests { use proxy::contract; @@ -1205,12 +1203,10 @@ pub mod tests { } - "request_flash_loan_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ConstructorForExecuteMsg::RequestFlashLoan { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); diff --git a/tests/snapshots/integration_tests__ctf04.snap b/tests/snapshots/integration_tests__ctf04.snap index 109d364..2c600d1 100644 --- a/tests/snapshots/integration_tests__ctf04.snap +++ b/tests/snapshots/integration_tests__ctf04.snap @@ -161,44 +161,44 @@ pub mod state_structs { use serde::Deserialize; use std::collections::HashMap; use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] - pub struct Balance { + pub struct Balance { pub amount: BigInt } #[derive(Clone, Debug, Deserialize)] - pub struct Config { + pub struct Config { pub total_supply: BigInt } #[derive(Clone, Debug, Deserialize)] - pub struct InstantiateMsg { + pub struct InstantiateMsg { pub offset: BigInt } - + #[derive(Clone, Debug, Deserialize)] - pub struct ContractState { + pub struct ContractState { pub config: Config, pub balances: HashMap } - + #[derive(Clone, Debug, Deserialize)] - pub struct NondetPicks { - + pub struct NondetPicks { + #[serde(with = "As::>")] pub sender: Option, - + #[serde(with = "As::>")] pub denom: Option, - + #[serde(with = "As::>")] pub amount: Option, - + #[serde(with = "As::>")] pub message_shares: Option } - - + #[derive(Clone, Debug, Deserialize)] pub struct Message {} @@ -219,6 +219,7 @@ pub mod state_structs { } } + #[cfg(test)] pub mod tests { use oaksecurity_cosmwasm_ctf_04::contract; @@ -345,7 +346,7 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_shares = nondet_picks.message_shares.clone().unwrap().to_u64().unwrap().into(); + let message_shares = nondet_picks.message_shares.clone().unwrap().to_u64().unwrap().into(); let msg = ExecuteMsg::Burn { shares: message_shares }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -396,12 +397,10 @@ pub mod tests { } - "mint_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::Mint { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); diff --git a/tests/snapshots/integration_tests__ctf05.snap b/tests/snapshots/integration_tests__ctf05.snap index e9e254b..67ef825 100644 --- a/tests/snapshots/integration_tests__ctf05.snap +++ b/tests/snapshots/integration_tests__ctf05.snap @@ -209,49 +209,49 @@ pub mod state_structs { use serde::Deserialize; use std::collections::HashMap; use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] - pub struct InstantiateMsg { + pub struct InstantiateMsg { pub owner: String } #[derive(Clone, Debug, Deserialize)] - pub struct State { + pub struct State { pub current_owner: String, pub proposed_owner: Option[Addr] } - + #[derive(Clone, Debug, Deserialize)] - pub struct ContractState { + pub struct ContractState { pub state: State, pub balances: HashMap } - + #[derive(Clone, Debug, Deserialize)] - pub struct NondetPicks { - + pub struct NondetPicks { + #[serde(with = "As::>")] pub sender: Option, - + #[serde(with = "As::>")] pub denom: Option, - + #[serde(with = "As::>")] pub amount: Option, - + #[serde(with = "As::>")] pub proposed_owner_element: Option, - + #[serde(with = "As::>")] pub message_amount: Option, - + #[serde(with = "As::>")] pub message_msg: Option, - + #[serde(with = "As::>")] pub message_new_owner: Option } - - + #[derive(Clone, Debug, Deserialize)] pub struct Message {} @@ -272,6 +272,7 @@ pub mod state_structs { } } + #[cfg(test)] pub mod tests { use oaksecurity_cosmwasm_ctf_05::contract; @@ -398,7 +399,6 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::AcceptOwnership { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -419,7 +419,6 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::Deposit { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -440,7 +439,6 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::DropOwnershipProposal { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -491,12 +489,11 @@ pub mod tests { } - "owner_action_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_msg = nondet_picks.message_msg.clone().unwrap(); + let message_msg = nondet_picks.message_msg.clone().unwrap(); let msg = ExecuteMsg::OwnerAction { msg: message_msg }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -517,7 +514,7 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_new_owner = nondet_picks.message_new_owner.clone().unwrap(); + let message_new_owner = nondet_picks.message_new_owner.clone().unwrap(); let msg = ExecuteMsg::ProposeNewOwner { new_owner: message_new_owner }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -538,7 +535,7 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); + let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); let msg = ExecuteMsg::Withdraw { amount: message_amount }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); diff --git a/tests/snapshots/integration_tests__ctf06.snap b/tests/snapshots/integration_tests__ctf06.snap index 96f6721..48c3ae7 100644 --- a/tests/snapshots/integration_tests__ctf06.snap +++ b/tests/snapshots/integration_tests__ctf06.snap @@ -187,49 +187,49 @@ pub mod state_structs { use serde::Deserialize; use std::collections::HashMap; use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] - pub struct Config { + pub struct Config { pub voting_window: BigInt, pub voting_token: String, pub owner: String } #[derive(Clone, Debug, Deserialize)] - pub struct InstantiateMsg { + pub struct InstantiateMsg { pub token: String, pub owner: String, pub window: BigInt } #[derive(Clone, Debug, Deserialize)] - pub struct Proposal { + pub struct Proposal { pub proposer: String, pub timestamp: BigInt } - + #[derive(Clone, Debug, Deserialize)] - pub struct ContractState { + pub struct ContractState { pub config: Config, pub proposal: Proposal } - + #[derive(Clone, Debug, Deserialize)] - pub struct NondetPicks { - + pub struct NondetPicks { + #[serde(with = "As::>")] pub sender: Option, - + #[serde(with = "As::>")] pub denom: Option, - + #[serde(with = "As::>")] pub amount: Option, - + #[serde(with = "As::>")] pub message_action: Option } - - + #[derive(Clone, Debug, Deserialize)] pub struct Message {} @@ -250,6 +250,7 @@ pub mod state_structs { } } + #[cfg(test)] pub mod tests { use oaksecurity_cosmwasm_ctf_06::contract; @@ -406,12 +407,11 @@ pub mod tests { } - "owner_action_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_action = nondet_picks.message_action.clone().unwrap(); + let message_action = nondet_picks.message_action.clone().unwrap(); let msg = ExecuteMsg::OwnerAction { action: message_action }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -432,7 +432,6 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::Propose { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -453,7 +452,6 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::Receive { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -474,7 +472,6 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::ResolveProposal { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); diff --git a/tests/snapshots/integration_tests__ctf07.snap b/tests/snapshots/integration_tests__ctf07.snap index e9ac002..e885c6e 100644 --- a/tests/snapshots/integration_tests__ctf07.snap +++ b/tests/snapshots/integration_tests__ctf07.snap @@ -188,49 +188,49 @@ pub mod state_structs { use serde::Deserialize; use std::collections::HashMap; use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] - pub struct ConfigQueryResponse { + pub struct ConfigQueryResponse { pub owner: String, pub threshold: BigInt } #[derive(Clone, Debug, Deserialize)] - pub struct InstantiateMsg { + pub struct InstantiateMsg { pub owner: String, pub threshold: BigInt } - + #[derive(Clone, Debug, Deserialize)] - pub struct ContractState { + pub struct ContractState { pub top_depositor: String, pub owner: String, pub threshold: BigInt, pub balances: HashMap } - + #[derive(Clone, Debug, Deserialize)] - pub struct NondetPicks { - + pub struct NondetPicks { + #[serde(with = "As::>")] pub sender: Option, - + #[serde(with = "As::>")] pub denom: Option, - + #[serde(with = "As::>")] pub amount: Option, - + #[serde(with = "As::>")] pub message_amount: Option, - + #[serde(with = "As::>")] pub message_new_threshold: Option, - + #[serde(with = "As::>")] pub message_msg: Option } - - + #[derive(Clone, Debug, Deserialize)] pub struct Message {} @@ -251,6 +251,7 @@ pub mod state_structs { } } + #[cfg(test)] pub mod tests { use oaksecurity_cosmwasm_ctf_07::contract; @@ -377,7 +378,6 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::Deposit { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -428,12 +428,11 @@ pub mod tests { } - "owner_action_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_msg = nondet_picks.message_msg.clone().unwrap(); + let message_msg = nondet_picks.message_msg.clone().unwrap(); let msg = ExecuteMsg::OwnerAction { msg: message_msg }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -454,7 +453,7 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_new_threshold = nondet_picks.message_new_threshold.clone().unwrap().to_u64().unwrap().into(); + let message_new_threshold = nondet_picks.message_new_threshold.clone().unwrap().to_u64().unwrap().into(); let msg = ExecuteMsg::UpdateConfig { new_threshold: message_new_threshold }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -475,7 +474,7 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); + let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); let msg = ExecuteMsg::Withdraw { amount: message_amount }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); diff --git a/tests/snapshots/integration_tests__ctf08.snap b/tests/snapshots/integration_tests__ctf08.snap index d43a98f..db01c37 100644 --- a/tests/snapshots/integration_tests__ctf08.snap +++ b/tests/snapshots/integration_tests__ctf08.snap @@ -220,29 +220,30 @@ pub mod state_structs { use serde::Deserialize; use std::collections::HashMap; use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] - pub struct Config { + pub struct Config { pub nft_contract: String } #[derive(Clone, Debug, Deserialize)] - pub struct GetCountResponse { + pub struct GetCountResponse { pub count: BigInt } #[derive(Clone, Debug, Deserialize)] - pub struct InstantiateMsg { + pub struct InstantiateMsg { pub nft_address: String } #[derive(Clone, Debug, Deserialize)] - pub struct Operations { + pub struct Operations { pub n_trades: BigInt, pub n_sales: BigInt } #[derive(Clone, Debug, Deserialize)] - pub struct Sale { + pub struct Sale { pub nft_id: String, pub price: BigInt, pub owner: String, @@ -250,64 +251,63 @@ pub mod state_structs { } #[derive(Clone, Debug, Deserialize)] - pub struct Trade { + pub struct Trade { pub asked_id: String, pub to_trade_id: String, pub trader: String } - + #[derive(Clone, Debug, Deserialize)] - pub struct ContractState { + pub struct ContractState { pub config: Config, pub sales: HashMap, pub trades: HashMap, pub operations: Operations } - + #[derive(Clone, Debug, Deserialize)] - pub struct NondetPicks { - + pub struct NondetPicks { + #[serde(with = "As::>")] pub sender: Option, - + #[serde(with = "As::>")] pub denom: Option, - + #[serde(with = "As::>")] pub amount: Option, - + #[serde(with = "As::>")] pub message_id: Option, - + #[serde(with = "As::>")] pub message_price: Option, - + #[serde(with = "As::>")] pub message_tradable: Option, - + #[serde(with = "As::>")] pub message_id: Option, - + #[serde(with = "As::>")] pub message_id: Option, - + #[serde(with = "As::>")] pub message_target: Option, - + #[serde(with = "As::>")] pub message_offered: Option, - + #[serde(with = "As::>")] pub message_id: Option, - + #[serde(with = "As::>")] pub message_trader: Option, - + #[serde(with = "As::>")] pub message_id: Option } - - + #[derive(Clone, Debug, Deserialize)] pub struct Message {} @@ -328,6 +328,7 @@ pub mod state_structs { } } + #[cfg(test)] pub mod tests { use oaksecurity_cosmwasm_ctf_08::contract; @@ -454,8 +455,8 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_id = nondet_picks.message_id.clone().unwrap(); - let message_trader = nondet_picks.message_trader.clone().unwrap(); + let message_id = nondet_picks.message_id.clone().unwrap(); + let message_trader = nondet_picks.message_trader.clone().unwrap(); let msg = ExecuteMsg::AcceptTrade { id: message_id, trader: message_trader }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -476,7 +477,7 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_id = nondet_picks.message_id.clone().unwrap(); + let message_id = nondet_picks.message_id.clone().unwrap(); let msg = ExecuteMsg::BuyNFT { id: message_id }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -497,7 +498,7 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_id = nondet_picks.message_id.clone().unwrap(); + let message_id = nondet_picks.message_id.clone().unwrap(); let msg = ExecuteMsg::CancelSale { id: message_id }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -518,7 +519,7 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_id = nondet_picks.message_id.clone().unwrap(); + let message_id = nondet_picks.message_id.clone().unwrap(); let msg = ExecuteMsg::CancelTrade { id: message_id }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -539,9 +540,9 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_id = nondet_picks.message_id.clone().unwrap(); - let message_price = nondet_picks.message_price.clone().unwrap().to_u64().unwrap().into(); - let message_tradable = nondet_picks.message_tradable.clone().unwrap(); + let message_id = nondet_picks.message_id.clone().unwrap(); + let message_price = nondet_picks.message_price.clone().unwrap().to_u64().unwrap().into(); + let message_tradable = nondet_picks.message_tradable.clone().unwrap(); let msg = ExecuteMsg::NewSale { id: message_id, price: message_price, tradable: message_tradable }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -562,8 +563,8 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_target = nondet_picks.message_target.clone().unwrap(); - let message_offered = nondet_picks.message_offered.clone().unwrap(); + let message_target = nondet_picks.message_target.clone().unwrap(); + let message_offered = nondet_picks.message_offered.clone().unwrap(); let msg = ExecuteMsg::NewTrade { target: message_target, offered: message_offered }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -613,7 +614,6 @@ pub mod tests { } - _ => panic!("Invalid action taken"), } compare_state(&test_state, &app, &(s.value.clone())); diff --git a/tests/snapshots/integration_tests__ctf09.snap b/tests/snapshots/integration_tests__ctf09.snap index 72a1dbd..a4dbb19 100644 --- a/tests/snapshots/integration_tests__ctf09.snap +++ b/tests/snapshots/integration_tests__ctf09.snap @@ -186,48 +186,48 @@ pub mod state_structs { use serde::Deserialize; use std::collections::HashMap; use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] - pub struct InstantiateMsg { - + pub struct InstantiateMsg { + } #[derive(Clone, Debug, Deserialize)] - pub struct State { + pub struct State { pub owner: String, pub total_staked: BigInt, pub global_index: BigInt } #[derive(Clone, Debug, Deserialize)] - pub struct UserRewardInfo { + pub struct UserRewardInfo { pub staked_amount: BigInt, pub user_index: BigInt, pub pending_rewards: BigInt } - + #[derive(Clone, Debug, Deserialize)] - pub struct ContractState { + pub struct ContractState { pub state: State, pub users: HashMap } - + #[derive(Clone, Debug, Deserialize)] - pub struct NondetPicks { - + pub struct NondetPicks { + #[serde(with = "As::>")] pub sender: Option, - + #[serde(with = "As::>")] pub denom: Option, - + #[serde(with = "As::>")] pub amount: Option, - + #[serde(with = "As::>")] pub message_amount: Option } - - + #[derive(Clone, Debug, Deserialize)] pub struct Message {} @@ -248,6 +248,7 @@ pub mod state_structs { } } + #[cfg(test)] pub mod tests { use oaksecurity_cosmwasm_ctf_09::contract; @@ -374,7 +375,6 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::ClaimRewards { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -395,7 +395,6 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::Deposit { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -416,7 +415,6 @@ pub mod tests { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::IncreaseReward { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); @@ -467,12 +465,11 @@ pub mod tests { } - "withdraw_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); + let message_amount = nondet_picks.message_amount.clone().unwrap().to_u64().unwrap().into(); let msg = ExecuteMsg::Withdraw { amount: message_amount }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); diff --git a/tests/snapshots/integration_tests__ctf10.snap b/tests/snapshots/integration_tests__ctf10.snap index 89e1bf7..abdde42 100644 --- a/tests/snapshots/integration_tests__ctf10.snap +++ b/tests/snapshots/integration_tests__ctf10.snap @@ -147,45 +147,45 @@ pub mod state_structs { use serde::Deserialize; use std::collections::HashMap; use itf::de::{self, As}; + #[derive(Clone, Debug, Deserialize)] - pub struct Config { + pub struct Config { pub nft_contract: String, pub mint_per_user: BigInt, pub total_tokens: BigInt } #[derive(Clone, Debug, Deserialize)] - pub struct InstantiateMsg { + pub struct InstantiateMsg { pub cw721_code_id: BigInt, pub mint_per_user: BigInt, pub whitelisted_users: Vec } #[derive(Clone, Debug, Deserialize)] - pub struct Whitelist { + pub struct Whitelist { pub users: Vec } - + #[derive(Clone, Debug, Deserialize)] - pub struct ContractState { + pub struct ContractState { pub config: Config, pub whitelist: Whitelist } - + #[derive(Clone, Debug, Deserialize)] - pub struct NondetPicks { - + pub struct NondetPicks { + #[serde(with = "As::>")] pub sender: Option, - + #[serde(with = "As::>")] pub denom: Option, - + #[serde(with = "As::>")] pub amount: Option } - - + #[derive(Clone, Debug, Deserialize)] pub struct Message {} @@ -206,6 +206,7 @@ pub mod state_structs { } } + #[cfg(test)] pub mod tests { use oaksecurity_cosmwasm_ctf_10::contract; @@ -362,12 +363,10 @@ pub mod tests { } - "mint_action" => { let sender = Addr::unchecked(sender.unwrap()); let funds = funds_from_trace(amount, denom); - let msg = ExecuteMsg::Mint { }; println!("Message: {:?}", msg); println!("Sender: {:?}", sender); From 1d27e021ef083ae427c9d66b2fe6bd686b22e3ea Mon Sep 17 00:00:00 2001 From: bugarela Date: Mon, 27 May 2024 11:30:45 -0300 Subject: [PATCH 17/19] Print message after generation --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 92de14e..8345b58 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -218,4 +218,6 @@ fn cosmwasm_to_quint(tcx: TyCtxt, _args: &CosmwasmToQuintPluginArgs) { std::fs::write("quint/lib/messaging.qnt", messaging).expect("Unable to write file"); translate_all_items(tcx); + + println!("Sucessfully generated files. Quint files are inside quint/ and test files are inside tests/.") } From 77db3a0bc18d5d1ee2bd45f6c9d5f575cdb78a66 Mon Sep 17 00:00:00 2001 From: bugarela Date: Mon, 27 May 2024 12:38:42 -0300 Subject: [PATCH 18/19] Add custom spells back --- src/quint-lib-files/basicSpells.qnt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/quint-lib-files/basicSpells.qnt b/src/quint-lib-files/basicSpells.qnt index 02f8c54..f7588e4 100644 --- a/src/quint-lib-files/basicSpells.qnt +++ b/src/quint-lib-files/basicSpells.qnt @@ -171,4 +171,15 @@ module basicSpells { pure def listFilter(l: List[a], f: (a) => bool): List[a] = l.foldl([], (acc, e) => if (f(e)) acc.append(e) else acc) + + pure def listMap(l: List[a], f: (a) => b): List[b] = + l.foldl([], (acc, e) => acc.append(f(e))) + + //// Returns a set of the elements in the list. + //// + //// - @param l a list + //// - @returns a set of the elements in l + pure def toSet(l: List[a]): Set[a] = { + l.foldl(Set(), (s, e) => s.union(Set(e))) + } } From 27fad74bf68e74b21c6b3de4c41cb55a18cc7008 Mon Sep 17 00:00:00 2001 From: bugarela Date: Mon, 27 May 2024 12:45:04 -0300 Subject: [PATCH 19/19] Improve formatting and print expected result from trace on generated tests --- src/boilerplate.rs | 14 +- src/test_generation/boilerplate.rs | 17 ++- src/translate.rs | 20 ++- tests/snapshots/integration_tests__ctf01.snap | 41 ++++-- tests/snapshots/integration_tests__ctf02.snap | 51 +++++-- tests/snapshots/integration_tests__ctf03.snap | 125 ++++++++++++++---- tests/snapshots/integration_tests__ctf04.snap | 41 ++++-- tests/snapshots/integration_tests__ctf05.snap | 65 +++++++-- tests/snapshots/integration_tests__ctf06.snap | 53 ++++++-- tests/snapshots/integration_tests__ctf07.snap | 57 ++++++-- tests/snapshots/integration_tests__ctf08.snap | 73 ++++++++-- tests/snapshots/integration_tests__ctf09.snap | 53 ++++++-- tests/snapshots/integration_tests__ctf10.snap | 39 +++++- 13 files changed, 519 insertions(+), 130 deletions(-) diff --git a/src/boilerplate.rs b/src/boilerplate.rs index d1f47d7..dae6a0c 100644 --- a/src/boilerplate.rs +++ b/src/boilerplate.rs @@ -161,29 +161,21 @@ pub fn post_items(ctx: &Context) -> String { .iter() .map(|x| format!("{}: {}", x.0, x.1)) .collect_vec() - .join(",\n "); + .join(",\n "); // After all items were visited, we can produce the complete contract state initializer let initializer = ctx .contract_state .iter() - .map(|field| { - format!( - " {}: {}", - field.0, - init_value_for_type(ctx, field.1.clone()) - ) - }) + .map(|field| format!("{}: {}", field.0, init_value_for_type(ctx, field.1.clone()))) .collect_vec() - .join(",\n"); + .join(",\n "); let special_actions = ["execute", "instantiate", "reply"]; let reply = if !ctx.ops_with_mutability.contains(&"reply".to_string()) { // Generate default reply to be given for the message handler " - pure def reply(state: ContractState, _env: Env, _reply: Reply): (Result, ContractState) = (Ok(Response_new), state) - " } else { "\n" diff --git a/src/test_generation/boilerplate.rs b/src/test_generation/boilerplate.rs index 290fd74..28c3a77 100644 --- a/src/test_generation/boilerplate.rs +++ b/src/test_generation/boilerplate.rs @@ -4,7 +4,7 @@ pub fn test_header(crate_name: &str) -> String { #[cfg(test)] pub mod tests {{ use {crate_name}::contract; - use {crate_name}::msg::{{ExecuteMsg, InstantiateMsg, QueryMsg}}; + use {crate_name}::msg::{{ExecuteMsg, InstantiateMsg}}; {TEST_AUX} " @@ -124,6 +124,7 @@ const TEST_AUX: &str = " let sender = nondet_picks.sender.clone(); println!(\"Step number: {:?}\", s.meta.index); + println!(\"Result from trace: {:?}\", s.value.result.clone()); match action_taken.as_str() { "; @@ -194,10 +195,24 @@ pub const STRUCTS_MODULE_IMPORTS: &str = "use num_bigint::BigInt; pub const DEFAULT_STRUCTS: &str = " #[derive(Clone, Debug, Deserialize)] pub struct Message {} + #[derive(Clone, Debug, Deserialize)] + pub struct Attribute { + pub key: String, + pub value: QuintSerializedValue, + } + + #[derive(Clone, Debug, Deserialize)] + #[serde(tag = \"tag\", content = \"value\")] + pub enum QuintSerializedValue { + FromInt(BigInt), + FromStr(String), + FromListInt(Vec), + } #[derive(Clone, Debug, Deserialize)] pub struct Response { pub messages: Vec, + pub attributes: Vec, } #[derive(Clone, Debug, Deserialize)] diff --git a/src/translate.rs b/src/translate.rs index 19012e2..9dd4d5f 100644 --- a/src/translate.rs +++ b/src/translate.rs @@ -534,18 +534,20 @@ impl Translatable for rustc_hir::Item<'_> { if name == "instantiate" || name == "reply" { // instantiate explanation: // - // FIXME: We need to do something about instantiate - // Instantiate is a stateful function (taking state as an - // argument and returning it) But currently we don't call it - // in the state machine (from `step`). We probably need to - // update the boilerplate stuff to call it. + // Instantiate is a special def that will be called on init // // reply explanation: // // Reply is a special def that will be called when // processing a message with a compatible reply_on field. We // don't want to generate a nondet action for it. - return format!(" pure def {name}{sig} = ({body_value}, state)"); + return format!( + " pure def {name}{sig} = {{ + // TODO: Update body + ({body_value}, state) + }} +" + ); } let ctor: Constructor = ctx @@ -557,7 +559,11 @@ impl Translatable for rustc_hir::Item<'_> { let nondet_value = ctor.nondet_definition(ctx, "message"); format!( - " pure def {name}{sig} = ({body_value}, state) + " + pure def {name}{sig} = {{ + // TODO: Update body + ({body_value}, state) + }} action {name}_action = {{ // TODO: Change next line according to fund expectations diff --git a/tests/snapshots/integration_tests__ctf01.snap b/tests/snapshots/integration_tests__ctf01.snap index d95bf07..1baa8cb 100644 --- a/tests/snapshots/integration_tests__ctf01.snap +++ b/tests/snapshots/integration_tests__ctf01.snap @@ -34,12 +34,20 @@ module oaksecurity_cosmwasm_ctf_01 { type ExecuteMsg = | ExecuteMsg_Deposit | ExecuteMsg_Withdraw({ ids: List[int] }) - pure def instantiate(state: ContractState, _env: Env, _info: MessageInfo, _msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + pure def instantiate(state: ContractState, _env: Env, _info: MessageInfo, _msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } + pure def execute(state: ContractState, env: Env, info: MessageInfo, msg: ExecuteMsg): (Result[Response, ContractError], ContractState) = match msg { | ExecuteMsg_Deposit(__r) => deposit(state, env, info) | ExecuteMsg_Withdraw(__r) => withdraw(state, env, info, __r.ids) } - pure def deposit(state: ContractState, env: Env, info: MessageInfo): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def deposit(state: ContractState, env: Env, info: MessageInfo): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action deposit_action = { // TODO: Change next line according to fund expectations @@ -48,7 +56,11 @@ module oaksecurity_cosmwasm_ctf_01 { pure val message: ExecuteMsg = ExecuteMsg_Deposit execute_message(message, max_funds) } - pure def withdraw(state: ContractState, env: Env, info: MessageInfo, ids: List[int]): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def withdraw(state: ContractState, env: Env, info: MessageInfo, ids: List[int]): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action withdraw_action = { // TODO: Change next line according to fund expectations @@ -63,11 +75,11 @@ module oaksecurity_cosmwasm_ctf_01 { type ContractState = { last_id: int, - lockups: int -> Lockup + lockups: int -> Lockup } pure val init_contract_state = { - last_id: 0, + last_id: 0, lockups: Map() } @@ -79,11 +91,9 @@ module oaksecurity_cosmwasm_ctf_01 { advance_time, } - pure def reply(state: ContractState, _env: Env, _reply: Reply): (Result, ContractState) = (Ok(Response_new), state) - pure val init_bank_state = ADDRESSES.mapBy(_ => DENOMS.mapBy(_ => MAX_AMOUNT)) val env_val = { block: { time: time } } @@ -200,10 +210,24 @@ pub mod state_structs { #[derive(Clone, Debug, Deserialize)] pub struct Message {} + #[derive(Clone, Debug, Deserialize)] + pub struct Attribute { + pub key: String, + pub value: QuintSerializedValue, + } + + #[derive(Clone, Debug, Deserialize)] + #[serde(tag = "tag", content = "value")] + pub enum QuintSerializedValue { + FromInt(BigInt), + FromStr(String), + FromListInt(Vec), + } #[derive(Clone, Debug, Deserialize)] pub struct Response { pub messages: Vec, + pub attributes: Vec, } #[derive(Clone, Debug, Deserialize)] @@ -222,7 +246,7 @@ pub mod state_structs { #[cfg(test)] pub mod tests { use oaksecurity_cosmwasm_ctf_01::contract; - use oaksecurity_cosmwasm_ctf_01::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + use oaksecurity_cosmwasm_ctf_01::msg::{ExecuteMsg, InstantiateMsg}; use crate::state_structs::*; @@ -337,6 +361,7 @@ pub mod tests { let sender = nondet_picks.sender.clone(); println!("Step number: {:?}", s.meta.index); + println!("Result from trace: {:?}", s.value.result.clone()); match action_taken.as_str() { diff --git a/tests/snapshots/integration_tests__ctf02.snap b/tests/snapshots/integration_tests__ctf02.snap index e6bcd41..88f4efe 100644 --- a/tests/snapshots/integration_tests__ctf02.snap +++ b/tests/snapshots/integration_tests__ctf02.snap @@ -36,14 +36,22 @@ module oaksecurity_cosmwasm_ctf_02 { | ExecuteMsg_Withdraw({ amount: int }) | ExecuteMsg_Stake({ lock_amount: int }) | ExecuteMsg_Unstake({ unlock_amount: int }) - pure def instantiate(state: ContractState, _env: Env, _info: MessageInfo, _msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + pure def instantiate(state: ContractState, _env: Env, _info: MessageInfo, _msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } + pure def execute(state: ContractState, env: Env, info: MessageInfo, msg: ExecuteMsg): (Result[Response, ContractError], ContractState) = match msg { | ExecuteMsg_Deposit(__r) => deposit(state, info) | ExecuteMsg_Withdraw(__r) => withdraw(state, info, __r.amount) | ExecuteMsg_Stake(__r) => stake(state, env, info, __r.lock_amount) | ExecuteMsg_Unstake(__r) => unstake(state, env, info, __r.unlock_amount) } - pure def deposit(state: ContractState, info: MessageInfo): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def deposit(state: ContractState, info: MessageInfo): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action deposit_action = { // TODO: Change next line according to fund expectations @@ -52,7 +60,11 @@ module oaksecurity_cosmwasm_ctf_02 { pure val message: ExecuteMsg = ExecuteMsg_Deposit execute_message(message, max_funds) } - pure def withdraw(state: ContractState, info: MessageInfo, amount: int): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def withdraw(state: ContractState, info: MessageInfo, amount: int): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action withdraw_action = { // TODO: Change next line according to fund expectations @@ -61,7 +73,11 @@ module oaksecurity_cosmwasm_ctf_02 { pure val message: ExecuteMsg = ExecuteMsg_Withdraw({ amount: message_amount }) execute_message(message, max_funds) } - pure def stake(state: ContractState, env: Env, info: MessageInfo, lock_amount: int): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def stake(state: ContractState, env: Env, info: MessageInfo, lock_amount: int): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action stake_action = { // TODO: Change next line according to fund expectations @@ -70,7 +86,11 @@ module oaksecurity_cosmwasm_ctf_02 { pure val message: ExecuteMsg = ExecuteMsg_Stake({ lock_amount: message_lock_amount }) execute_message(message, max_funds) } - pure def unstake(state: ContractState, env: Env, info: MessageInfo, unlock_amount: int): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def unstake(state: ContractState, env: Env, info: MessageInfo, unlock_amount: int): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action unstake_action = { // TODO: Change next line according to fund expectations @@ -87,7 +107,7 @@ module oaksecurity_cosmwasm_ctf_02 { } pure val init_contract_state = { - voting_power: Map() + voting_power: Map() } action execute_step = all { @@ -100,11 +120,9 @@ module oaksecurity_cosmwasm_ctf_02 { advance_time, } - pure def reply(state: ContractState, _env: Env, _reply: Reply): (Result, ContractState) = (Ok(Response_new), state) - pure val init_bank_state = ADDRESSES.mapBy(_ => DENOMS.mapBy(_ => MAX_AMOUNT)) val env_val = { block: { time: time } } @@ -225,10 +243,24 @@ pub mod state_structs { #[derive(Clone, Debug, Deserialize)] pub struct Message {} + #[derive(Clone, Debug, Deserialize)] + pub struct Attribute { + pub key: String, + pub value: QuintSerializedValue, + } + + #[derive(Clone, Debug, Deserialize)] + #[serde(tag = "tag", content = "value")] + pub enum QuintSerializedValue { + FromInt(BigInt), + FromStr(String), + FromListInt(Vec), + } #[derive(Clone, Debug, Deserialize)] pub struct Response { pub messages: Vec, + pub attributes: Vec, } #[derive(Clone, Debug, Deserialize)] @@ -247,7 +279,7 @@ pub mod state_structs { #[cfg(test)] pub mod tests { use oaksecurity_cosmwasm_ctf_02::contract; - use oaksecurity_cosmwasm_ctf_02::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + use oaksecurity_cosmwasm_ctf_02::msg::{ExecuteMsg, InstantiateMsg}; use crate::state_structs::*; @@ -362,6 +394,7 @@ pub mod tests { let sender = nondet_picks.sender.clone(); println!("Step number: {:?}", s.meta.index); + println!("Result from trace: {:?}", s.value.result.clone()); match action_taken.as_str() { diff --git a/tests/snapshots/integration_tests__ctf03.snap b/tests/snapshots/integration_tests__ctf03.snap index 62aa18f..696818a 100644 --- a/tests/snapshots/integration_tests__ctf03.snap +++ b/tests/snapshots/integration_tests__ctf03.snap @@ -27,7 +27,11 @@ module flash_loan { pure val MAX_AMOUNT = 200 - pure def instantiate(state: ContractState, _env: Env, info: MessageInfo, _msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + pure def instantiate(state: ContractState, _env: Env, info: MessageInfo, _msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } + pure def execute(state: ContractState, env: Env, info: MessageInfo, msg: ExecuteMsg): (Result[Response, ContractError], ContractState) = match msg { | ExecuteMsg_FlashLoan(__r) => flash_loan(state, env, info) | ExecuteMsg_SettleLoan(__r) => settle_loan(state, env, info) @@ -35,7 +39,11 @@ module flash_loan { | ExecuteMsg_WithdrawFunds(__r) => withdraw_funds(state, env, info, __r.recipient) | ExecuteMsg_TransferOwner(__r) => transfer_owner(state, info, __r.new_owner) } - pure def flash_loan(state: ContractState, env: Env, info: MessageInfo): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def flash_loan(state: ContractState, env: Env, info: MessageInfo): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action flash_loan_action = { // TODO: Change next line according to fund expectations @@ -44,7 +52,11 @@ module flash_loan { pure val message: UnknownType = ConstructorForflash_loan execute_message(message, max_funds) } - pure def settle_loan(state: ContractState, env: Env, info: MessageInfo): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def settle_loan(state: ContractState, env: Env, info: MessageInfo): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action settle_loan_action = { // TODO: Change next line according to fund expectations @@ -53,7 +65,11 @@ module flash_loan { pure val message: UnknownType = ConstructorForsettle_loan execute_message(message, max_funds) } - pure def set_proxy_addr(state: ContractState, info: MessageInfo, proxy_addr: str): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def set_proxy_addr(state: ContractState, info: MessageInfo, proxy_addr: str): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action set_proxy_addr_action = { // TODO: Change next line according to fund expectations @@ -62,7 +78,11 @@ module flash_loan { pure val message: UnknownType = ConstructorForset_proxy_addr execute_message(message, max_funds) } - pure def withdraw_funds(state: ContractState, env: Env, info: MessageInfo, recipient: Addr): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def withdraw_funds(state: ContractState, env: Env, info: MessageInfo, recipient: Addr): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action withdraw_funds_action = { // TODO: Change next line according to fund expectations @@ -71,7 +91,11 @@ module flash_loan { pure val message: UnknownType = ConstructorForwithdraw_funds execute_message(message, max_funds) } - pure def transfer_owner(state: ContractState, info: MessageInfo, new_owner: Addr): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def transfer_owner(state: ContractState, info: MessageInfo, new_owner: Addr): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action transfer_owner_action = { // TODO: Change next line according to fund expectations @@ -85,11 +109,11 @@ module flash_loan { type ContractState = { config: Config, - flash_loan: FlashLoanState + flash_loan: FlashLoanState } pure val init_contract_state = { - config: , + config: , flash_loan: } @@ -104,11 +128,9 @@ module flash_loan { advance_time, } - pure def reply(state: ContractState, _env: Env, _reply: Reply): (Result, ContractState) = (Ok(Response_new), state) - pure val init_bank_state = ADDRESSES.mapBy(_ => DENOMS.mapBy(_ => MAX_AMOUNT)) val env_val = { block: { time: time } } @@ -205,11 +227,19 @@ module mock_arb { pure val MAX_AMOUNT = 200 - pure def instantiate(state: ContractState, _env: Env, _info: MessageInfo, _msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + pure def instantiate(state: ContractState, _env: Env, _info: MessageInfo, _msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } + pure def execute(state: ContractState, env: Env, info: MessageInfo, msg: ExecuteMsg): (Result[Response, ContractError], ContractState) = match msg { | ExecuteMsg_Arbitrage(__r) => arbitrage(state, env, info, __r.recipient) } - pure def arbitrage(state: ContractState, env: Env, info: MessageInfo, recipient: Addr): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def arbitrage(state: ContractState, env: Env, info: MessageInfo, recipient: Addr): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action arbitrage_action = { // TODO: Change next line according to fund expectations @@ -225,7 +255,7 @@ module mock_arb { } pure val init_contract_state = { - config: + config: } action execute_step = all { @@ -235,11 +265,9 @@ module mock_arb { advance_time, } - pure def reply(state: ContractState, _env: Env, _reply: Reply): (Result, ContractState) = (Ok(Response_new), state) - pure val init_bank_state = ADDRESSES.mapBy(_ => DENOMS.mapBy(_ => MAX_AMOUNT)) val env_val = { block: { time: time } } @@ -334,11 +362,19 @@ module proxy { pure val MAX_AMOUNT = 200 - pure def instantiate(state: ContractState, _env: Env, _info: MessageInfo, msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + pure def instantiate(state: ContractState, _env: Env, _info: MessageInfo, msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } + pure def execute(state: ContractState, env: Env, info: MessageInfo, msg: ExecuteMsg): (Result[Response, ContractError], ContractState) = match msg { | ExecuteMsg_RequestFlashLoan(__r) => request_flash_loan(state, env, info, __r.recipient, __r.msg) } - pure def request_flash_loan(state: ContractState, env: Env, _info: MessageInfo, recipient: Addr, msg: Binary): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def request_flash_loan(state: ContractState, env: Env, _info: MessageInfo, recipient: Addr, msg: Binary): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action request_flash_loan_action = { // TODO: Change next line according to fund expectations @@ -354,7 +390,7 @@ module proxy { } pure val init_contract_state = { - config: + config: } action execute_step = all { @@ -364,11 +400,9 @@ module proxy { advance_time, } - pure def reply(state: ContractState, _env: Env, _reply: Reply): (Result, ContractState) = (Ok(Response_new), state) - pure val init_bank_state = ADDRESSES.mapBy(_ => DENOMS.mapBy(_ => MAX_AMOUNT)) val env_val = { block: { time: time } } @@ -470,10 +504,24 @@ pub mod state_structs { #[derive(Clone, Debug, Deserialize)] pub struct Message {} + #[derive(Clone, Debug, Deserialize)] + pub struct Attribute { + pub key: String, + pub value: QuintSerializedValue, + } + + #[derive(Clone, Debug, Deserialize)] + #[serde(tag = "tag", content = "value")] + pub enum QuintSerializedValue { + FromInt(BigInt), + FromStr(String), + FromListInt(Vec), + } #[derive(Clone, Debug, Deserialize)] pub struct Response { pub messages: Vec, + pub attributes: Vec, } #[derive(Clone, Debug, Deserialize)] @@ -492,7 +540,7 @@ pub mod state_structs { #[cfg(test)] pub mod tests { use flash_loan::contract; - use flash_loan::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + use flash_loan::msg::{ExecuteMsg, InstantiateMsg}; use crate::state_structs::*; @@ -607,6 +655,7 @@ pub mod tests { let sender = nondet_picks.sender.clone(); println!("Step number: {:?}", s.meta.index); + println!("Result from trace: {:?}", s.value.result.clone()); match action_taken.as_str() { @@ -789,10 +838,24 @@ pub mod state_structs { #[derive(Clone, Debug, Deserialize)] pub struct Message {} + #[derive(Clone, Debug, Deserialize)] + pub struct Attribute { + pub key: String, + pub value: QuintSerializedValue, + } + + #[derive(Clone, Debug, Deserialize)] + #[serde(tag = "tag", content = "value")] + pub enum QuintSerializedValue { + FromInt(BigInt), + FromStr(String), + FromListInt(Vec), + } #[derive(Clone, Debug, Deserialize)] pub struct Response { pub messages: Vec, + pub attributes: Vec, } #[derive(Clone, Debug, Deserialize)] @@ -811,7 +874,7 @@ pub mod state_structs { #[cfg(test)] pub mod tests { use mock_arb::contract; - use mock_arb::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + use mock_arb::msg::{ExecuteMsg, InstantiateMsg}; use crate::state_structs::*; @@ -926,6 +989,7 @@ pub mod tests { let sender = nondet_picks.sender.clone(); println!("Step number: {:?}", s.meta.index); + println!("Result from trace: {:?}", s.value.result.clone()); match action_taken.as_str() { @@ -1028,10 +1092,24 @@ pub mod state_structs { #[derive(Clone, Debug, Deserialize)] pub struct Message {} + #[derive(Clone, Debug, Deserialize)] + pub struct Attribute { + pub key: String, + pub value: QuintSerializedValue, + } + + #[derive(Clone, Debug, Deserialize)] + #[serde(tag = "tag", content = "value")] + pub enum QuintSerializedValue { + FromInt(BigInt), + FromStr(String), + FromListInt(Vec), + } #[derive(Clone, Debug, Deserialize)] pub struct Response { pub messages: Vec, + pub attributes: Vec, } #[derive(Clone, Debug, Deserialize)] @@ -1050,7 +1128,7 @@ pub mod state_structs { #[cfg(test)] pub mod tests { use proxy::contract; - use proxy::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + use proxy::msg::{ExecuteMsg, InstantiateMsg}; use crate::state_structs::*; @@ -1165,6 +1243,7 @@ pub mod tests { let sender = nondet_picks.sender.clone(); println!("Step number: {:?}", s.meta.index); + println!("Result from trace: {:?}", s.value.result.clone()); match action_taken.as_str() { diff --git a/tests/snapshots/integration_tests__ctf04.snap b/tests/snapshots/integration_tests__ctf04.snap index 2c600d1..438f2cb 100644 --- a/tests/snapshots/integration_tests__ctf04.snap +++ b/tests/snapshots/integration_tests__ctf04.snap @@ -35,12 +35,20 @@ module oaksecurity_cosmwasm_ctf_04 { type ExecuteMsg = | ExecuteMsg_Mint | ExecuteMsg_Burn({ shares: int }) - pure def instantiate(state: ContractState, _env: Env, _info: MessageInfo, _msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + pure def instantiate(state: ContractState, _env: Env, _info: MessageInfo, _msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } + pure def execute(state: ContractState, env: Env, info: MessageInfo, msg: ExecuteMsg): (Result[Response, ContractError], ContractState) = match msg { | ExecuteMsg_Mint(__r) => mint(state, env, info) | ExecuteMsg_Burn(__r) => burn(state, env, info, __r.shares) } - pure def mint(state: ContractState, env: Env, info: MessageInfo): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def mint(state: ContractState, env: Env, info: MessageInfo): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action mint_action = { // TODO: Change next line according to fund expectations @@ -49,7 +57,11 @@ module oaksecurity_cosmwasm_ctf_04 { pure val message: ExecuteMsg = ExecuteMsg_Mint execute_message(message, max_funds) } - pure def burn(state: ContractState, env: Env, info: MessageInfo, shares: int): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def burn(state: ContractState, env: Env, info: MessageInfo, shares: int): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action burn_action = { // TODO: Change next line according to fund expectations @@ -62,11 +74,11 @@ module oaksecurity_cosmwasm_ctf_04 { type ContractState = { config: Config, - balances: Addr -> Balance + balances: Addr -> Balance } pure val init_contract_state = { - config: { total_supply: 0 }, + config: { total_supply: 0 }, balances: Map() } @@ -78,11 +90,9 @@ module oaksecurity_cosmwasm_ctf_04 { advance_time, } - pure def reply(state: ContractState, _env: Env, _reply: Reply): (Result, ContractState) = (Ok(Response_new), state) - pure val init_bank_state = ADDRESSES.mapBy(_ => DENOMS.mapBy(_ => MAX_AMOUNT)) val env_val = { block: { time: time } } @@ -201,10 +211,24 @@ pub mod state_structs { #[derive(Clone, Debug, Deserialize)] pub struct Message {} + #[derive(Clone, Debug, Deserialize)] + pub struct Attribute { + pub key: String, + pub value: QuintSerializedValue, + } + + #[derive(Clone, Debug, Deserialize)] + #[serde(tag = "tag", content = "value")] + pub enum QuintSerializedValue { + FromInt(BigInt), + FromStr(String), + FromListInt(Vec), + } #[derive(Clone, Debug, Deserialize)] pub struct Response { pub messages: Vec, + pub attributes: Vec, } #[derive(Clone, Debug, Deserialize)] @@ -223,7 +247,7 @@ pub mod state_structs { #[cfg(test)] pub mod tests { use oaksecurity_cosmwasm_ctf_04::contract; - use oaksecurity_cosmwasm_ctf_04::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + use oaksecurity_cosmwasm_ctf_04::msg::{ExecuteMsg, InstantiateMsg}; use crate::state_structs::*; @@ -338,6 +362,7 @@ pub mod tests { let sender = nondet_picks.sender.clone(); println!("Step number: {:?}", s.meta.index); + println!("Result from trace: {:?}", s.value.result.clone()); match action_taken.as_str() { diff --git a/tests/snapshots/integration_tests__ctf05.snap b/tests/snapshots/integration_tests__ctf05.snap index 67ef825..c6247ee 100644 --- a/tests/snapshots/integration_tests__ctf05.snap +++ b/tests/snapshots/integration_tests__ctf05.snap @@ -38,7 +38,11 @@ module oaksecurity_cosmwasm_ctf_05 { | ExecuteMsg_ProposeNewOwner({ new_owner: str }) | ExecuteMsg_AcceptOwnership | ExecuteMsg_DropOwnershipProposal - pure def instantiate(state: ContractState, _env: Env, _info: MessageInfo, msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + pure def instantiate(state: ContractState, _env: Env, _info: MessageInfo, msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } + pure def execute(state: ContractState, _env: Env, info: MessageInfo, msg: ExecuteMsg): (Result[Response, ContractError], ContractState) = match msg { | ExecuteMsg_Deposit(__r) => deposit(state, info) | ExecuteMsg_Withdraw(__r) => withdraw(state, info, __r.amount) @@ -47,7 +51,11 @@ module oaksecurity_cosmwasm_ctf_05 { | ExecuteMsg_AcceptOwnership(__r) => accept_owner(state, info) | ExecuteMsg_DropOwnershipProposal(__r) => drop_owner(state, info) } - pure def deposit(state: ContractState, info: MessageInfo): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def deposit(state: ContractState, info: MessageInfo): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action deposit_action = { // TODO: Change next line according to fund expectations @@ -56,7 +64,11 @@ module oaksecurity_cosmwasm_ctf_05 { pure val message: ExecuteMsg = ExecuteMsg_Deposit execute_message(message, max_funds) } - pure def withdraw(state: ContractState, info: MessageInfo, amount: int): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def withdraw(state: ContractState, info: MessageInfo, amount: int): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action withdraw_action = { // TODO: Change next line according to fund expectations @@ -65,7 +77,11 @@ module oaksecurity_cosmwasm_ctf_05 { pure val message: ExecuteMsg = ExecuteMsg_Withdraw({ amount: message_amount }) execute_message(message, max_funds) } - pure def owner_action(state: ContractState, info: MessageInfo, msg: CosmosMsg): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def owner_action(state: ContractState, info: MessageInfo, msg: CosmosMsg): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action owner_action_action = { // TODO: Change next line according to fund expectations @@ -74,7 +90,11 @@ module oaksecurity_cosmwasm_ctf_05 { pure val message: ExecuteMsg = ExecuteMsg_OwnerAction({ msg: message_msg }) execute_message(message, max_funds) } - pure def propose_owner(state: ContractState, info: MessageInfo, new_owner: str): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def propose_owner(state: ContractState, info: MessageInfo, new_owner: str): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action propose_owner_action = { // TODO: Change next line according to fund expectations @@ -83,7 +103,11 @@ module oaksecurity_cosmwasm_ctf_05 { pure val message: ExecuteMsg = ExecuteMsg_ProposeNewOwner({ new_owner: message_new_owner }) execute_message(message, max_funds) } - pure def accept_owner(state: ContractState, info: MessageInfo): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def accept_owner(state: ContractState, info: MessageInfo): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action accept_owner_action = { // TODO: Change next line according to fund expectations @@ -92,7 +116,11 @@ module oaksecurity_cosmwasm_ctf_05 { pure val message: ExecuteMsg = ExecuteMsg_AcceptOwnership execute_message(message, max_funds) } - pure def drop_owner(state: ContractState, info: MessageInfo): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def drop_owner(state: ContractState, info: MessageInfo): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action drop_owner_action = { // TODO: Change next line according to fund expectations @@ -106,11 +134,11 @@ module oaksecurity_cosmwasm_ctf_05 { type ContractState = { state: State, - balances: Addr -> int + balances: Addr -> int } pure val init_contract_state = { - state: { current_owner: "s1",proposed_owner: }, + state: { current_owner: "s1",proposed_owner: }, balances: Map() } @@ -126,11 +154,9 @@ module oaksecurity_cosmwasm_ctf_05 { advance_time, } - pure def reply(state: ContractState, _env: Env, _reply: Reply): (Result, ContractState) = (Ok(Response_new), state) - pure val init_bank_state = ADDRESSES.mapBy(_ => DENOMS.mapBy(_ => MAX_AMOUNT)) val env_val = { block: { time: time } } @@ -254,10 +280,24 @@ pub mod state_structs { #[derive(Clone, Debug, Deserialize)] pub struct Message {} + #[derive(Clone, Debug, Deserialize)] + pub struct Attribute { + pub key: String, + pub value: QuintSerializedValue, + } + + #[derive(Clone, Debug, Deserialize)] + #[serde(tag = "tag", content = "value")] + pub enum QuintSerializedValue { + FromInt(BigInt), + FromStr(String), + FromListInt(Vec), + } #[derive(Clone, Debug, Deserialize)] pub struct Response { pub messages: Vec, + pub attributes: Vec, } #[derive(Clone, Debug, Deserialize)] @@ -276,7 +316,7 @@ pub mod state_structs { #[cfg(test)] pub mod tests { use oaksecurity_cosmwasm_ctf_05::contract; - use oaksecurity_cosmwasm_ctf_05::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + use oaksecurity_cosmwasm_ctf_05::msg::{ExecuteMsg, InstantiateMsg}; use crate::state_structs::*; @@ -391,6 +431,7 @@ pub mod tests { let sender = nondet_picks.sender.clone(); println!("Step number: {:?}", s.meta.index); + println!("Result from trace: {:?}", s.value.result.clone()); match action_taken.as_str() { diff --git a/tests/snapshots/integration_tests__ctf06.snap b/tests/snapshots/integration_tests__ctf06.snap index 48c3ae7..5cef446 100644 --- a/tests/snapshots/integration_tests__ctf06.snap +++ b/tests/snapshots/integration_tests__ctf06.snap @@ -39,14 +39,22 @@ module oaksecurity_cosmwasm_ctf_06 { | ExecuteMsg_Receive(Cw20ReceiveMsg) type Cw20HookMsg = | Cw20HookMsg_CastVote - pure def instantiate(state: ContractState, _env: Env, _info: MessageInfo, msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + pure def instantiate(state: ContractState, _env: Env, _info: MessageInfo, msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } + pure def execute(state: ContractState, env: Env, info: MessageInfo, msg: ExecuteMsg): (Result[Response, ContractError], ContractState) = match msg { | ExecuteMsg_Propose(__r) => propose(state, env, info) | ExecuteMsg_ResolveProposal(__r) => resolve_proposal(state, env, info) | ExecuteMsg_OwnerAction(__r) => owner_action(state, info, __r.action) | ExecuteMsg_Receive(msg) => receive_cw20(state, env, info, msg) } - pure def receive_cw20(state: ContractState, env: Env, info: MessageInfo, cw20_msg: Cw20ReceiveMsg): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def receive_cw20(state: ContractState, env: Env, info: MessageInfo, cw20_msg: Cw20ReceiveMsg): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action receive_cw20_action = { // TODO: Change next line according to fund expectations @@ -55,7 +63,11 @@ module oaksecurity_cosmwasm_ctf_06 { pure val message: ExecuteMsg = ExecuteMsg_Receive execute_message(message, max_funds) } - pure def propose(state: ContractState, env: Env, info: MessageInfo): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def propose(state: ContractState, env: Env, info: MessageInfo): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action propose_action = { // TODO: Change next line according to fund expectations @@ -64,7 +76,11 @@ module oaksecurity_cosmwasm_ctf_06 { pure val message: ExecuteMsg = ExecuteMsg_Propose execute_message(message, max_funds) } - pure def resolve_proposal(state: ContractState, env: Env, _info: MessageInfo): (Result[Response, ContractError], ContractState) = (Ok(response), state) + + pure def resolve_proposal(state: ContractState, env: Env, _info: MessageInfo): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(response), state) + } action resolve_proposal_action = { // TODO: Change next line according to fund expectations @@ -73,7 +89,11 @@ module oaksecurity_cosmwasm_ctf_06 { pure val message: ExecuteMsg = ExecuteMsg_ResolveProposal execute_message(message, max_funds) } - pure def owner_action(state: ContractState, info: MessageInfo, msg: CosmosMsg): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def owner_action(state: ContractState, info: MessageInfo, msg: CosmosMsg): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action owner_action_action = { // TODO: Change next line according to fund expectations @@ -86,11 +106,11 @@ module oaksecurity_cosmwasm_ctf_06 { type ContractState = { config: Config, - proposal: Proposal + proposal: Proposal } pure val init_contract_state = { - config: { voting_window: 0,voting_token: "s1",owner: "s1" }, + config: { voting_window: 0,voting_token: "s1",owner: "s1" }, proposal: { proposer: "s1",timestamp: 0 } } @@ -104,11 +124,9 @@ module oaksecurity_cosmwasm_ctf_06 { advance_time, } - pure def reply(state: ContractState, _env: Env, _reply: Reply): (Result, ContractState) = (Ok(Response_new), state) - pure val init_bank_state = ADDRESSES.mapBy(_ => DENOMS.mapBy(_ => MAX_AMOUNT)) val env_val = { block: { time: time } } @@ -232,10 +250,24 @@ pub mod state_structs { #[derive(Clone, Debug, Deserialize)] pub struct Message {} + #[derive(Clone, Debug, Deserialize)] + pub struct Attribute { + pub key: String, + pub value: QuintSerializedValue, + } + + #[derive(Clone, Debug, Deserialize)] + #[serde(tag = "tag", content = "value")] + pub enum QuintSerializedValue { + FromInt(BigInt), + FromStr(String), + FromListInt(Vec), + } #[derive(Clone, Debug, Deserialize)] pub struct Response { pub messages: Vec, + pub attributes: Vec, } #[derive(Clone, Debug, Deserialize)] @@ -254,7 +286,7 @@ pub mod state_structs { #[cfg(test)] pub mod tests { use oaksecurity_cosmwasm_ctf_06::contract; - use oaksecurity_cosmwasm_ctf_06::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + use oaksecurity_cosmwasm_ctf_06::msg::{ExecuteMsg, InstantiateMsg}; use crate::state_structs::*; @@ -369,6 +401,7 @@ pub mod tests { let sender = nondet_picks.sender.clone(); println!("Step number: {:?}", s.meta.index); + println!("Result from trace: {:?}", s.value.result.clone()); match action_taken.as_str() { diff --git a/tests/snapshots/integration_tests__ctf07.snap b/tests/snapshots/integration_tests__ctf07.snap index e885c6e..f4b9bb4 100644 --- a/tests/snapshots/integration_tests__ctf07.snap +++ b/tests/snapshots/integration_tests__ctf07.snap @@ -36,14 +36,22 @@ module oaksecurity_cosmwasm_ctf_07 { | ExecuteMsg_Withdraw({ amount: int }) | ExecuteMsg_OwnerAction({ msg: CosmosMsg }) | ExecuteMsg_UpdateConfig({ new_threshold: int }) - pure def instantiate(state: ContractState, _env: Env, _info: MessageInfo, msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + pure def instantiate(state: ContractState, _env: Env, _info: MessageInfo, msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } + pure def execute(state: ContractState, _env: Env, info: MessageInfo, msg: ExecuteMsg): (Result[Response, ContractError], ContractState) = match msg { | ExecuteMsg_Deposit(__r) => deposit(state, info) | ExecuteMsg_Withdraw(__r) => withdraw(state, info, __r.amount) | ExecuteMsg_OwnerAction(__r) => owner_action(state, info, __r.msg) | ExecuteMsg_UpdateConfig(__r) => update_config(state, info, __r.new_threshold) } - pure def deposit(state: ContractState, info: MessageInfo): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def deposit(state: ContractState, info: MessageInfo): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action deposit_action = { // TODO: Change next line according to fund expectations @@ -52,7 +60,11 @@ module oaksecurity_cosmwasm_ctf_07 { pure val message: ExecuteMsg = ExecuteMsg_Deposit execute_message(message, max_funds) } - pure def withdraw(state: ContractState, info: MessageInfo, amount: int): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def withdraw(state: ContractState, info: MessageInfo, amount: int): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action withdraw_action = { // TODO: Change next line according to fund expectations @@ -61,7 +73,11 @@ module oaksecurity_cosmwasm_ctf_07 { pure val message: ExecuteMsg = ExecuteMsg_Withdraw({ amount: message_amount }) execute_message(message, max_funds) } - pure def update_config(state: ContractState, info: MessageInfo, new_threshold: int): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def update_config(state: ContractState, info: MessageInfo, new_threshold: int): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action update_config_action = { // TODO: Change next line according to fund expectations @@ -70,7 +86,11 @@ module oaksecurity_cosmwasm_ctf_07 { pure val message: ExecuteMsg = ExecuteMsg_UpdateConfig({ new_threshold: message_new_threshold }) execute_message(message, max_funds) } - pure def owner_action(state: ContractState, info: MessageInfo, msg: CosmosMsg): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def owner_action(state: ContractState, info: MessageInfo, msg: CosmosMsg): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action owner_action_action = { // TODO: Change next line according to fund expectations @@ -83,13 +103,13 @@ module oaksecurity_cosmwasm_ctf_07 { type ContractState = { top_depositor: Addr, - owner: Addr, - threshold: int, - balances: Addr -> int + owner: Addr, + threshold: int, + balances: Addr -> int } pure val init_contract_state = { - top_depositor: "s1", + top_depositor: "s1", owner: "s1", threshold: 0, balances: Map() @@ -105,11 +125,9 @@ module oaksecurity_cosmwasm_ctf_07 { advance_time, } - pure def reply(state: ContractState, _env: Env, _reply: Reply): (Result, ContractState) = (Ok(Response_new), state) - pure val init_bank_state = ADDRESSES.mapBy(_ => DENOMS.mapBy(_ => MAX_AMOUNT)) val env_val = { block: { time: time } } @@ -233,10 +251,24 @@ pub mod state_structs { #[derive(Clone, Debug, Deserialize)] pub struct Message {} + #[derive(Clone, Debug, Deserialize)] + pub struct Attribute { + pub key: String, + pub value: QuintSerializedValue, + } + + #[derive(Clone, Debug, Deserialize)] + #[serde(tag = "tag", content = "value")] + pub enum QuintSerializedValue { + FromInt(BigInt), + FromStr(String), + FromListInt(Vec), + } #[derive(Clone, Debug, Deserialize)] pub struct Response { pub messages: Vec, + pub attributes: Vec, } #[derive(Clone, Debug, Deserialize)] @@ -255,7 +287,7 @@ pub mod state_structs { #[cfg(test)] pub mod tests { use oaksecurity_cosmwasm_ctf_07::contract; - use oaksecurity_cosmwasm_ctf_07::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + use oaksecurity_cosmwasm_ctf_07::msg::{ExecuteMsg, InstantiateMsg}; use crate::state_structs::*; @@ -370,6 +402,7 @@ pub mod tests { let sender = nondet_picks.sender.clone(); println!("Step number: {:?}", s.meta.index); + println!("Result from trace: {:?}", s.value.result.clone()); match action_taken.as_str() { diff --git a/tests/snapshots/integration_tests__ctf08.snap b/tests/snapshots/integration_tests__ctf08.snap index db01c37..f7c6b89 100644 --- a/tests/snapshots/integration_tests__ctf08.snap +++ b/tests/snapshots/integration_tests__ctf08.snap @@ -42,7 +42,11 @@ module oaksecurity_cosmwasm_ctf_08 { | ExecuteMsg_NewTrade({ target: str, offered: str }) | ExecuteMsg_AcceptTrade({ id: str, trader: str }) | ExecuteMsg_CancelTrade({ id: str }) - pure def instantiate(state: ContractState, _env: Env, _info: MessageInfo, msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + pure def instantiate(state: ContractState, _env: Env, _info: MessageInfo, msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } + pure def execute(state: ContractState, env: Env, info: MessageInfo, msg: ExecuteMsg): (Result[Response, ContractError], ContractState) = match msg { | ExecuteMsg_BuyNFT(__r) => exec_buy(state, env, info, __r.id) | ExecuteMsg_NewSale(__r) => exec_new_sale(state, env, info, __r.id, __r.price, __r.tradable) @@ -51,7 +55,11 @@ module oaksecurity_cosmwasm_ctf_08 { | ExecuteMsg_AcceptTrade(__r) => exec_accept_trade(state, info, __r.id, __r.trader) | ExecuteMsg_CancelTrade(__r) => exec_cancel_trade(state, info, __r.id) } - pure def exec_new_sale(state: ContractState, env: Env, info: MessageInfo, id: str, price: int, tradable: bool): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def exec_new_sale(state: ContractState, env: Env, info: MessageInfo, id: str, price: int, tradable: bool): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action exec_new_sale_action = { // TODO: Change next line according to fund expectations @@ -62,7 +70,11 @@ module oaksecurity_cosmwasm_ctf_08 { pure val message: ExecuteMsg = ExecuteMsg_NewSale({ id: message_id, price: message_price, tradable: message_tradable }) execute_message(message, max_funds) } - pure def exec_buy(state: ContractState, _env: Env, info: MessageInfo, id: str): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def exec_buy(state: ContractState, _env: Env, info: MessageInfo, id: str): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action exec_buy_action = { // TODO: Change next line according to fund expectations @@ -71,7 +83,11 @@ module oaksecurity_cosmwasm_ctf_08 { pure val message: ExecuteMsg = ExecuteMsg_BuyNFT({ id: message_id }) execute_message(message, max_funds) } - pure def exec_cancel_sale(state: ContractState, info: MessageInfo, id: str): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def exec_cancel_sale(state: ContractState, info: MessageInfo, id: str): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action exec_cancel_sale_action = { // TODO: Change next line according to fund expectations @@ -80,7 +96,11 @@ module oaksecurity_cosmwasm_ctf_08 { pure val message: ExecuteMsg = ExecuteMsg_CancelSale({ id: message_id }) execute_message(message, max_funds) } - pure def exec_new_trade(state: ContractState, env: Env, info: MessageInfo, asked_id: str, offered_id: str): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def exec_new_trade(state: ContractState, env: Env, info: MessageInfo, asked_id: str, offered_id: str): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action exec_new_trade_action = { // TODO: Change next line according to fund expectations @@ -90,7 +110,11 @@ module oaksecurity_cosmwasm_ctf_08 { pure val message: ExecuteMsg = ExecuteMsg_NewTrade({ target: message_target, offered: message_offered }) execute_message(message, max_funds) } - pure def exec_accept_trade(state: ContractState, info: MessageInfo, asked_id: str, trader: str): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def exec_accept_trade(state: ContractState, info: MessageInfo, asked_id: str, trader: str): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action exec_accept_trade_action = { // TODO: Change next line according to fund expectations @@ -100,7 +124,11 @@ module oaksecurity_cosmwasm_ctf_08 { pure val message: ExecuteMsg = ExecuteMsg_AcceptTrade({ id: message_id, trader: message_trader }) execute_message(message, max_funds) } - pure def exec_cancel_trade(state: ContractState, info: MessageInfo, asked_id: str): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def exec_cancel_trade(state: ContractState, info: MessageInfo, asked_id: str): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action exec_cancel_trade_action = { // TODO: Change next line according to fund expectations @@ -109,20 +137,24 @@ module oaksecurity_cosmwasm_ctf_08 { pure val message: ExecuteMsg = ExecuteMsg_CancelTrade({ id: message_id }) execute_message(message, max_funds) } - pure def reply(state: ContractState, _env: Env, reply: Reply): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + pure def reply(state: ContractState, _env: Env, reply: Reply): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } + pure val DENOM = "uawesome" pure val TRADE_REPLY = 1 pure val SALE_REPLY = 2 type ContractState = { config: Config, - sales: str -> Sale, - trades: str -> Trade, - operations: Operations + sales: str -> Sale, + trades: str -> Trade, + operations: Operations } pure val init_contract_state = { - config: { nft_contract: "s1" }, + config: { nft_contract: "s1" }, sales: Map(), trades: Map(), operations: { n_trades: 0,n_sales: 0 } @@ -310,10 +342,24 @@ pub mod state_structs { #[derive(Clone, Debug, Deserialize)] pub struct Message {} + #[derive(Clone, Debug, Deserialize)] + pub struct Attribute { + pub key: String, + pub value: QuintSerializedValue, + } + + #[derive(Clone, Debug, Deserialize)] + #[serde(tag = "tag", content = "value")] + pub enum QuintSerializedValue { + FromInt(BigInt), + FromStr(String), + FromListInt(Vec), + } #[derive(Clone, Debug, Deserialize)] pub struct Response { pub messages: Vec, + pub attributes: Vec, } #[derive(Clone, Debug, Deserialize)] @@ -332,7 +378,7 @@ pub mod state_structs { #[cfg(test)] pub mod tests { use oaksecurity_cosmwasm_ctf_08::contract; - use oaksecurity_cosmwasm_ctf_08::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + use oaksecurity_cosmwasm_ctf_08::msg::{ExecuteMsg, InstantiateMsg}; use crate::state_structs::*; @@ -447,6 +493,7 @@ pub mod tests { let sender = nondet_picks.sender.clone(); println!("Step number: {:?}", s.meta.index); + println!("Result from trace: {:?}", s.value.result.clone()); match action_taken.as_str() { diff --git a/tests/snapshots/integration_tests__ctf09.snap b/tests/snapshots/integration_tests__ctf09.snap index a4dbb19..f591a40 100644 --- a/tests/snapshots/integration_tests__ctf09.snap +++ b/tests/snapshots/integration_tests__ctf09.snap @@ -37,14 +37,22 @@ module oaksecurity_cosmwasm_ctf_09 { | ExecuteMsg_Deposit | ExecuteMsg_Withdraw({ amount: int }) | ExecuteMsg_ClaimRewards - pure def instantiate(state: ContractState, _env: Env, info: MessageInfo, _msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + pure def instantiate(state: ContractState, _env: Env, info: MessageInfo, _msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } + pure def execute(state: ContractState, env: Env, info: MessageInfo, msg: ExecuteMsg): (Result[Response, ContractError], ContractState) = match msg { | ExecuteMsg_IncreaseReward(__r) => increase_reward(state, env, info) | ExecuteMsg_Deposit(__r) => deposit(state, info) | ExecuteMsg_Withdraw(__r) => withdraw(state, info, __r.amount) | ExecuteMsg_ClaimRewards(__r) => claim_rewards(state, info) } - pure def increase_reward(state: ContractState, _env: Env, info: MessageInfo): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def increase_reward(state: ContractState, _env: Env, info: MessageInfo): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action increase_reward_action = { // TODO: Change next line according to fund expectations @@ -53,7 +61,11 @@ module oaksecurity_cosmwasm_ctf_09 { pure val message: ExecuteMsg = ExecuteMsg_IncreaseReward execute_message(message, max_funds) } - pure def deposit(state: ContractState, info: MessageInfo): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def deposit(state: ContractState, info: MessageInfo): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action deposit_action = { // TODO: Change next line according to fund expectations @@ -62,7 +74,11 @@ module oaksecurity_cosmwasm_ctf_09 { pure val message: ExecuteMsg = ExecuteMsg_Deposit execute_message(message, max_funds) } - pure def withdraw(state: ContractState, info: MessageInfo, amount: int): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def withdraw(state: ContractState, info: MessageInfo, amount: int): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action withdraw_action = { // TODO: Change next line according to fund expectations @@ -71,7 +87,11 @@ module oaksecurity_cosmwasm_ctf_09 { pure val message: ExecuteMsg = ExecuteMsg_Withdraw({ amount: message_amount }) execute_message(message, max_funds) } - pure def claim_rewards(state: ContractState, info: MessageInfo): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def claim_rewards(state: ContractState, info: MessageInfo): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action claim_rewards_action = { // TODO: Change next line according to fund expectations @@ -85,11 +105,11 @@ module oaksecurity_cosmwasm_ctf_09 { type ContractState = { state: State, - users: Addr -> UserRewardInfo + users: Addr -> UserRewardInfo } pure val init_contract_state = { - state: { owner: "s1",total_staked: 0,global_index: 0 }, + state: { owner: "s1",total_staked: 0,global_index: 0 }, users: Map() } @@ -103,11 +123,9 @@ module oaksecurity_cosmwasm_ctf_09 { advance_time, } - pure def reply(state: ContractState, _env: Env, _reply: Reply): (Result, ContractState) = (Ok(Response_new), state) - pure val init_bank_state = ADDRESSES.mapBy(_ => DENOMS.mapBy(_ => MAX_AMOUNT)) val env_val = { block: { time: time } } @@ -230,10 +248,24 @@ pub mod state_structs { #[derive(Clone, Debug, Deserialize)] pub struct Message {} + #[derive(Clone, Debug, Deserialize)] + pub struct Attribute { + pub key: String, + pub value: QuintSerializedValue, + } + + #[derive(Clone, Debug, Deserialize)] + #[serde(tag = "tag", content = "value")] + pub enum QuintSerializedValue { + FromInt(BigInt), + FromStr(String), + FromListInt(Vec), + } #[derive(Clone, Debug, Deserialize)] pub struct Response { pub messages: Vec, + pub attributes: Vec, } #[derive(Clone, Debug, Deserialize)] @@ -252,7 +284,7 @@ pub mod state_structs { #[cfg(test)] pub mod tests { use oaksecurity_cosmwasm_ctf_09::contract; - use oaksecurity_cosmwasm_ctf_09::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + use oaksecurity_cosmwasm_ctf_09::msg::{ExecuteMsg, InstantiateMsg}; use crate::state_structs::*; @@ -367,6 +399,7 @@ pub mod tests { let sender = nondet_picks.sender.clone(); println!("Step number: {:?}", s.meta.index); + println!("Result from trace: {:?}", s.value.result.clone()); match action_taken.as_str() { diff --git a/tests/snapshots/integration_tests__ctf10.snap b/tests/snapshots/integration_tests__ctf10.snap index abdde42..2c4d97c 100644 --- a/tests/snapshots/integration_tests__ctf10.snap +++ b/tests/snapshots/integration_tests__ctf10.snap @@ -34,11 +34,19 @@ module oaksecurity_cosmwasm_ctf_10 { type Whitelist = { users: List[str] } type ExecuteMsg = | ExecuteMsg_Mint - pure def instantiate(state: ContractState, env: Env, _info: MessageInfo, msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + pure def instantiate(state: ContractState, env: Env, _info: MessageInfo, msg: InstantiateMsg): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } + pure def execute(state: ContractState, env: Env, info: MessageInfo, msg: ExecuteMsg): (Result[Response, ContractError], ContractState) = match msg { | ExecuteMsg_Mint(__r) => mint(state, env, info) } - pure def mint(state: ContractState, _env: Env, info: MessageInfo): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + + pure def mint(state: ContractState, _env: Env, info: MessageInfo): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } action mint_action = { // TODO: Change next line according to fund expectations @@ -47,16 +55,20 @@ module oaksecurity_cosmwasm_ctf_10 { pure val message: ExecuteMsg = ExecuteMsg_Mint execute_message(message, max_funds) } - pure def reply(state: ContractState, _env: Env, reply: Reply): (Result[Response, ContractError], ContractState) = (Ok(Response_new), state) + pure def reply(state: ContractState, _env: Env, reply: Reply): (Result[Response, ContractError], ContractState) = { + // TODO: Update body + (Ok(Response_new), state) + } + pure val DENOM = "uawesome" type ContractState = { config: Config, - whitelist: Whitelist + whitelist: Whitelist } pure val init_contract_state = { - config: { nft_contract: "s1",mint_per_user: 0,total_tokens: 0 }, + config: { nft_contract: "s1",mint_per_user: 0,total_tokens: 0 }, whitelist: { users: [] } } @@ -188,10 +200,24 @@ pub mod state_structs { #[derive(Clone, Debug, Deserialize)] pub struct Message {} + #[derive(Clone, Debug, Deserialize)] + pub struct Attribute { + pub key: String, + pub value: QuintSerializedValue, + } + + #[derive(Clone, Debug, Deserialize)] + #[serde(tag = "tag", content = "value")] + pub enum QuintSerializedValue { + FromInt(BigInt), + FromStr(String), + FromListInt(Vec), + } #[derive(Clone, Debug, Deserialize)] pub struct Response { pub messages: Vec, + pub attributes: Vec, } #[derive(Clone, Debug, Deserialize)] @@ -210,7 +236,7 @@ pub mod state_structs { #[cfg(test)] pub mod tests { use oaksecurity_cosmwasm_ctf_10::contract; - use oaksecurity_cosmwasm_ctf_10::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + use oaksecurity_cosmwasm_ctf_10::msg::{ExecuteMsg, InstantiateMsg}; use crate::state_structs::*; @@ -325,6 +351,7 @@ pub mod tests { let sender = nondet_picks.sender.clone(); println!("Step number: {:?}", s.meta.index); + println!("Result from trace: {:?}", s.value.result.clone()); match action_taken.as_str() {