diff --git a/contracts/README.md b/contracts/README.md index 7496e5fb38..6662351191 100644 --- a/contracts/README.md +++ b/contracts/README.md @@ -21,7 +21,7 @@ docker run --rm -v "$(pwd)":/code \ cosmwasm/rust-optimizer:0.12.5 ./contracts/burner docker run --rm -v "$(pwd)":/code \ - --mount type=volume,source="devcontract_cache_burner",target=/code/contracts/crypto-verify/target \ + --mount type=volume,source="devcontract_cache_crypto_verify",target=/code/contracts/crypto-verify/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ cosmwasm/rust-optimizer:0.12.5 ./contracts/crypto-verify diff --git a/contracts/queue/Cargo.toml b/contracts/queue/Cargo.toml index bb5f4afa12..07012987ea 100644 --- a/contracts/queue/Cargo.toml +++ b/contracts/queue/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "queue" version = "0.0.0" -authors = ["Ethan Frey "] +authors = ["Simon Warta ", "Ethan Frey "] edition = "2021" publish = false license = "Apache-2.0" @@ -23,8 +23,9 @@ incremental = false overflow-checks = true [features] -# Change this to [] if you don't need Windows support and want faster integration tests. -default = ["cranelift"] +# Change this to ["cranelift"] if you don't need Windows support and want faster integration tests. +#default = ["cranelift"] +default = [] # Use cranelift backend instead of singlepass. This is required for development on Windows. cranelift = ["cosmwasm-vm/cranelift"] # For quicker tests, cargo test --lib. for more explicit tests, cargo test --features=backtraces diff --git a/contracts/queue/examples/schema.rs b/contracts/queue/examples/schema.rs index 92443fe9a0..c603a88627 100644 --- a/contracts/queue/examples/schema.rs +++ b/contracts/queue/examples/schema.rs @@ -3,8 +3,10 @@ use std::fs::create_dir_all; use cosmwasm_schema::{export_schema, remove_schemas, schema_for}; -use queue::contract::{CountResponse, ExecuteMsg, Item, ListResponse, QueryMsg, SumResponse}; -use queue::msg::{InstantiateMsg, MigrateMsg}; +use queue::msg::{ + CountResponse, ExecuteMsg, InstantiateMsg, ListResponse, MigrateMsg, QueryMsg, SumResponse, +}; +use queue::state::Item; fn main() { let mut out_dir = current_dir().unwrap(); diff --git a/contracts/queue/schema/query_msg.json b/contracts/queue/schema/query_msg.json index 1c2b4499b6..8d1d2914d3 100644 --- a/contracts/queue/schema/query_msg.json +++ b/contracts/queue/schema/query_msg.json @@ -49,6 +49,29 @@ } }, "additionalProperties": false + }, + { + "description": "Opens the given number of iterators for no reason other than testing. Returns and `Empty` response.", + "type": "object", + "required": [ + "open_iterators" + ], + "properties": { + "open_iterators": { + "type": "object", + "required": [ + "count" + ], + "properties": { + "count": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + } + } + }, + "additionalProperties": false } ] } diff --git a/contracts/queue/src/contract.rs b/contracts/queue/src/contract.rs index 270fbc6c8b..381ed0fcd6 100644 --- a/contracts/queue/src/contract.rs +++ b/contracts/queue/src/contract.rs @@ -1,66 +1,13 @@ -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - use cosmwasm_std::{ - entry_point, from_slice, to_binary, to_vec, Binary, Deps, DepsMut, Env, MessageInfo, Order, - QueryResponse, Response, StdResult, Storage, + entry_point, from_slice, to_binary, to_vec, Binary, Deps, DepsMut, Empty, Env, MessageInfo, + Order, QueryResponse, Response, StdResult, Storage, }; -use crate::msg::{InstantiateMsg, MigrateMsg}; - -// we store one entry for each item in the queue -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct Item { - pub value: i32, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum ExecuteMsg { - // Enqueue will add some value to the end of list - Enqueue { value: i32 }, - // Dequeue will remove value from start of the list - Dequeue {}, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum QueryMsg { - // how many items are in the queue - Count {}, - // total of all values in the queue - Sum {}, - // Reducer holds open two iterators at once - Reducer {}, - List {}, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct CountResponse { - pub count: u32, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct SumResponse { - pub sum: i32, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -// the Vec contains pairs for every element in the queue -// (value of item i, sum of all elements where value > value[i]) -pub struct ReducerResponse { - pub counters: Vec<(i32, i32)>, -} - -#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] -pub struct ListResponse { - /// List an empty range, both bounded - pub empty: Vec, - /// List all IDs lower than 0x20 - pub early: Vec, - /// List all IDs starting from 0x20 - pub late: Vec, -} +use crate::msg::{ + CountResponse, ExecuteMsg, InstantiateMsg, ListResponse, MigrateMsg, QueryMsg, ReducerResponse, + SumResponse, +}; +use crate::state::Item; // A no-op, just empty data #[entry_point] @@ -86,7 +33,7 @@ pub fn execute( } } -const FIRST_KEY: u8 = 0; +const FIRST_KEY: [u8; 4] = [0, 0, 0, 0]; fn handle_enqueue(deps: DepsMut, value: i32) -> StdResult { enqueue(deps.storage, value)?; @@ -99,13 +46,14 @@ fn enqueue(storage: &mut dyn Storage, value: i32) -> StdResult<()> { let new_key = match last_item { None => FIRST_KEY, - Some((key, _)) => { - key[0] + 1 // all keys are one byte + Some((key, _value)) => { + let last_key = u32::from_be_bytes(key.try_into().unwrap()); + (last_key + 1).to_be_bytes() } }; let new_value = to_vec(&Item { value })?; - storage.set(&[new_key], &new_value); + storage.set(&new_key, &new_value); Ok(()) } @@ -149,6 +97,7 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { QueryMsg::Sum {} => to_binary(&query_sum(deps)?), QueryMsg::Reducer {} => to_binary(&query_reducer(deps)?), QueryMsg::List {} => to_binary(&query_list(deps)), + QueryMsg::OpenIterators { count } => to_binary(&query_open_iterators(deps, count)), } } @@ -197,24 +146,33 @@ fn query_reducer(deps: Deps) -> StdResult { /// Does a range query with both bounds set. Not really useful but to debug an issue /// between VM and Wasm: https://github.com/CosmWasm/cosmwasm/issues/508 fn query_list(deps: Deps) -> ListResponse { + const THRESHOLD: [u8; 4] = [0x00, 0x00, 0x00, 0x20]; let empty: Vec = deps .storage - .range(Some(b"large"), Some(b"larger"), Order::Ascending) - .map(|(k, _)| k[0] as u32) + .range(Some(&THRESHOLD), Some(&THRESHOLD), Order::Ascending) + .map(|(k, _)| u32::from_be_bytes(k.try_into().unwrap())) .collect(); let early: Vec = deps .storage - .range(None, Some(b"\x20"), Order::Ascending) - .map(|(k, _)| k[0] as u32) + .range(None, Some(&THRESHOLD), Order::Ascending) + .map(|(k, _)| u32::from_be_bytes(k.try_into().unwrap())) .collect(); let late: Vec = deps .storage - .range(Some(b"\x20"), None, Order::Ascending) - .map(|(k, _)| k[0] as u32) + .range(Some(&THRESHOLD), None, Order::Ascending) + .map(|(k, _)| u32::from_be_bytes(k.try_into().unwrap())) .collect(); ListResponse { empty, early, late } } +/// Opens iterators and does nothing with them. Because we can. +fn query_open_iterators(deps: Deps, count: u32) -> Empty { + for _ in 0..count { + let _ = deps.storage.range(None, None, Order::Ascending); + } + Empty::default() +} + #[cfg(test)] mod tests { use super::*; @@ -223,6 +181,7 @@ mod tests { }; use cosmwasm_std::{coins, from_binary, OwnedDeps}; + /// Instantiates a contract with no elements fn create_contract() -> (OwnedDeps, MessageInfo) { let mut deps = mock_dependencies_with_balance(&coins(1000, "earth")); let info = mock_info("creator", &coins(1000, "earth")); @@ -384,4 +343,18 @@ mod tests { assert_eq!(ids.early, vec![0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f]); assert_eq!(ids.late, vec![0x20, 0x21, 0x22, 0x23, 0x24]); } + + #[test] + fn query_open_iterators() { + let (deps, _info) = create_contract(); + + let query_msg = QueryMsg::OpenIterators { count: 0 }; + let _ = query(deps.as_ref(), mock_env(), query_msg).unwrap(); + + let query_msg = QueryMsg::OpenIterators { count: 1 }; + let _ = query(deps.as_ref(), mock_env(), query_msg).unwrap(); + + let query_msg = QueryMsg::OpenIterators { count: 321 }; + let _ = query(deps.as_ref(), mock_env(), query_msg).unwrap(); + } } diff --git a/contracts/queue/src/lib.rs b/contracts/queue/src/lib.rs index 112ecadc84..4934c19d5b 100644 --- a/contracts/queue/src/lib.rs +++ b/contracts/queue/src/lib.rs @@ -1,2 +1,3 @@ pub mod contract; pub mod msg; +pub mod state; diff --git a/contracts/queue/src/msg.rs b/contracts/queue/src/msg.rs index fe84d2459f..a3084e2374 100644 --- a/contracts/queue/src/msg.rs +++ b/contracts/queue/src/msg.rs @@ -1,8 +1,61 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum ExecuteMsg { + // Enqueue will add some value to the end of list + Enqueue { value: i32 }, + // Dequeue will remove value from start of the list + Dequeue {}, +} + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct InstantiateMsg {} #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct MigrateMsg {} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum QueryMsg { + // how many items are in the queue + Count {}, + // total of all values in the queue + Sum {}, + // Reducer holds open two iterators at once + Reducer {}, + List {}, + /// Opens the given number of iterators for no reason other than testing. + /// Returns and `Empty` response. + OpenIterators { + count: u32, + }, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct CountResponse { + pub count: u32, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct SumResponse { + pub sum: i32, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +// the Vec contains pairs for every element in the queue +// (value of item i, sum of all elements where value > value[i]) +pub struct ReducerResponse { + pub counters: Vec<(i32, i32)>, +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] +pub struct ListResponse { + /// List an empty range, both bounded + pub empty: Vec, + /// List all IDs lower than 0x20 + pub early: Vec, + /// List all IDs starting from 0x20 + pub late: Vec, +} diff --git a/contracts/queue/src/state.rs b/contracts/queue/src/state.rs new file mode 100644 index 0000000000..6808fb3366 --- /dev/null +++ b/contracts/queue/src/state.rs @@ -0,0 +1,8 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +// we store one entry for each item in the queue +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct Item { + pub value: i32, +} diff --git a/contracts/queue/tests/integration.rs b/contracts/queue/tests/integration.rs index 7fa7380545..98a5b21f54 100644 --- a/contracts/queue/tests/integration.rs +++ b/contracts/queue/tests/integration.rs @@ -26,13 +26,15 @@ use cosmwasm_vm::{ Instance, }; -use queue::contract::{ - CountResponse, ExecuteMsg, Item, ListResponse, QueryMsg, ReducerResponse, SumResponse, +use queue::msg::{ + CountResponse, ExecuteMsg, InstantiateMsg, ListResponse, MigrateMsg, QueryMsg, ReducerResponse, + SumResponse, }; -use queue::msg::{InstantiateMsg, MigrateMsg}; +use queue::state::Item; static WASM: &[u8] = include_bytes!("../target/wasm32-unknown-unknown/release/queue.wasm"); +/// Instantiates a contract with no elements fn create_contract() -> (Instance, MessageInfo) { let gas_limit = 1_000_000_000_000; // ~1ms, enough for many executions within one instance let mut deps = mock_instance_with_gas_limit(WASM, gas_limit); @@ -226,3 +228,17 @@ fn query_list() { assert_eq!(ids.early, vec![0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f]); assert_eq!(ids.late, vec![0x20, 0x21, 0x22, 0x23, 0x24]); } + +#[test] +fn query_open_iterators() { + let (mut deps, _info) = create_contract(); + + let query_msg = QueryMsg::OpenIterators { count: 0 }; + let _ = query(&mut deps, mock_env(), query_msg).unwrap(); + + let query_msg = QueryMsg::OpenIterators { count: 1 }; + let _ = query(&mut deps, mock_env(), query_msg).unwrap(); + + let query_msg = QueryMsg::OpenIterators { count: 321 }; + let _ = query(&mut deps, mock_env(), query_msg).unwrap(); +} diff --git a/packages/std/src/results/empty.rs b/packages/std/src/results/empty.rs index 59e8d08dd4..8aff8c82fc 100644 --- a/packages/std/src/results/empty.rs +++ b/packages/std/src/results/empty.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; /// It is designed to be expressable in correct JSON and JSON Schema but /// contains no meaningful data. Previously we used enums without cases, /// but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451) -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, Default)] pub struct Empty {} #[cfg(test)] @@ -16,6 +16,12 @@ mod tests { use crate::serde::{from_slice, to_vec}; + #[test] + fn empty_can_be_instantiated() { + let instance = Empty::default(); + assert_eq!(instance, Empty {}); + } + #[test] fn empty_can_be_instantiated_serialized_and_deserialized() { let instance = Empty {};