Skip to content

Commit

Permalink
Merge pull request #1292 from CosmWasm/iterator-contract
Browse files Browse the repository at this point in the history
Improve queue contract for iterator testing
  • Loading branch information
webmaster128 authored May 3, 2022
2 parents 59faf94 + 160c29d commit 36766bf
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 80 deletions.
2 changes: 1 addition & 1 deletion contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
7 changes: 4 additions & 3 deletions contracts/queue/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "queue"
version = "0.0.0"
authors = ["Ethan Frey <[email protected]>"]
authors = ["Simon Warta <[email protected]>", "Ethan Frey <[email protected]>"]
edition = "2021"
publish = false
license = "Apache-2.0"
Expand All @@ -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
Expand Down
6 changes: 4 additions & 2 deletions contracts/queue/examples/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
23 changes: 23 additions & 0 deletions contracts/queue/schema/query_msg.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
]
}
113 changes: 43 additions & 70 deletions contracts/queue/src/contract.rs
Original file line number Diff line number Diff line change
@@ -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<u32>,
/// List all IDs lower than 0x20
pub early: Vec<u32>,
/// List all IDs starting from 0x20
pub late: Vec<u32>,
}
use crate::msg::{
CountResponse, ExecuteMsg, InstantiateMsg, ListResponse, MigrateMsg, QueryMsg, ReducerResponse,
SumResponse,
};
use crate::state::Item;

// A no-op, just empty data
#[entry_point]
Expand All @@ -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<Response> {
enqueue(deps.storage, value)?;
Expand All @@ -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(())
}

Expand Down Expand Up @@ -149,6 +97,7 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<QueryResponse> {
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)),
}
}

Expand Down Expand Up @@ -197,24 +146,33 @@ fn query_reducer(deps: Deps) -> StdResult<ReducerResponse> {
/// 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<u32> = 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<u32> = 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<u32> = 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::*;
Expand All @@ -223,6 +181,7 @@ mod tests {
};
use cosmwasm_std::{coins, from_binary, OwnedDeps};

/// Instantiates a contract with no elements
fn create_contract() -> (OwnedDeps<MockStorage, MockApi, MockQuerier>, MessageInfo) {
let mut deps = mock_dependencies_with_balance(&coins(1000, "earth"));
let info = mock_info("creator", &coins(1000, "earth"));
Expand Down Expand Up @@ -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();
}
}
1 change: 1 addition & 0 deletions contracts/queue/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod contract;
pub mod msg;
pub mod state;
53 changes: 53 additions & 0 deletions contracts/queue/src/msg.rs
Original file line number Diff line number Diff line change
@@ -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<u32>,
/// List all IDs lower than 0x20
pub early: Vec<u32>,
/// List all IDs starting from 0x20
pub late: Vec<u32>,
}
8 changes: 8 additions & 0 deletions contracts/queue/src/state.rs
Original file line number Diff line number Diff line change
@@ -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,
}
22 changes: 19 additions & 3 deletions contracts/queue/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<MockApi, MockStorage, MockQuerier>, 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);
Expand Down Expand Up @@ -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();
}
8 changes: 7 additions & 1 deletion packages/std/src/results/empty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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 {};
Expand Down

0 comments on commit 36766bf

Please sign in to comment.