From 5bb4f813d57a6969dc9fa4e0e371b0e814e07962 Mon Sep 17 00:00:00 2001 From: Tomasz Kurcz Date: Wed, 12 Oct 2022 18:23:41 +0200 Subject: [PATCH 1/3] Remove cosmwasm-storage dependency Because cosmwasm-storage is going to be deprecated --- Cargo.lock | 11 - packages/multi-test/Cargo.toml | 1 - packages/multi-test/src/bank.rs | 2 +- packages/multi-test/src/lib.rs | 1 + packages/multi-test/src/prefixed_storage.rs | 185 +++++++++++++++ .../src/prefixed_storage/length_prefixed.rs | 176 ++++++++++++++ .../src/prefixed_storage/namespace_helpers.rs | 220 ++++++++++++++++++ packages/multi-test/src/wasm.rs | 2 +- 8 files changed, 584 insertions(+), 14 deletions(-) create mode 100644 packages/multi-test/src/prefixed_storage.rs create mode 100644 packages/multi-test/src/prefixed_storage/length_prefixed.rs create mode 100644 packages/multi-test/src/prefixed_storage/namespace_helpers.rs diff --git a/Cargo.lock b/Cargo.lock index 0f790ab7c..3a57deba7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -235,16 +235,6 @@ dependencies = [ "uint", ] -[[package]] -name = "cosmwasm-storage" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3205d5210ba4c7b7cc7329b9607c37d00dc08263c2479fd99541a2194ebe3b22" -dependencies = [ - "cosmwasm-std", - "serde", -] - [[package]] name = "cpufeatures" version = "0.2.4" @@ -417,7 +407,6 @@ version = "0.15.1" dependencies = [ "anyhow", "cosmwasm-std", - "cosmwasm-storage", "cw-storage-plus", "cw-utils", "derivative", diff --git a/packages/multi-test/Cargo.toml b/packages/multi-test/Cargo.toml index 916ca0ef5..7a48b49c6 100644 --- a/packages/multi-test/Cargo.toml +++ b/packages/multi-test/Cargo.toml @@ -20,7 +20,6 @@ backtrace = ["anyhow/backtrace"] cw-utils = { path = "../../packages/utils", version = "0.15.1" } cw-storage-plus = { path = "../../packages/storage-plus", version = "0.15.1"} cosmwasm-std = { version = "1.1.0", features = ["staking"] } -cosmwasm-storage = "1.1.0" itertools = "0.10.1" schemars = "0.8.1" serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/packages/multi-test/src/bank.rs b/packages/multi-test/src/bank.rs index 71493d2c0..15cc66172 100644 --- a/packages/multi-test/src/bank.rs +++ b/packages/multi-test/src/bank.rs @@ -6,13 +6,13 @@ use cosmwasm_std::{ coin, to_binary, Addr, AllBalanceResponse, Api, BalanceResponse, BankMsg, BankQuery, Binary, BlockInfo, Coin, Event, Querier, Storage, }; -use cosmwasm_storage::{prefixed, prefixed_read}; use cw_storage_plus::Map; use cw_utils::NativeBalance; use crate::app::CosmosRouter; use crate::executor::AppResponse; use crate::module::Module; +use crate::prefixed_storage::{prefixed, prefixed_read}; const BALANCES: Map<&Addr, NativeBalance> = Map::new("balances"); diff --git a/packages/multi-test/src/lib.rs b/packages/multi-test/src/lib.rs index 29ad0846e..662db3bb6 100644 --- a/packages/multi-test/src/lib.rs +++ b/packages/multi-test/src/lib.rs @@ -14,6 +14,7 @@ pub mod custom_handler; pub mod error; mod executor; mod module; +mod prefixed_storage; mod staking; mod test_helpers; mod transactions; diff --git a/packages/multi-test/src/prefixed_storage.rs b/packages/multi-test/src/prefixed_storage.rs new file mode 100644 index 000000000..b74e4d764 --- /dev/null +++ b/packages/multi-test/src/prefixed_storage.rs @@ -0,0 +1,185 @@ +mod length_prefixed; +mod namespace_helpers; + +use cosmwasm_std::Storage; +#[cfg(feature = "iterator")] +use cosmwasm_std::{Order, Record}; + +use length_prefixed::{to_length_prefixed, to_length_prefixed_nested}; +#[cfg(feature = "iterator")] +use namespace_helpers::range_with_prefix; +use namespace_helpers::{get_with_prefix, remove_with_prefix, set_with_prefix}; + +/// An alias of PrefixedStorage::new for less verbose usage +pub fn prefixed<'a>(storage: &'a mut dyn Storage, namespace: &[u8]) -> PrefixedStorage<'a> { + PrefixedStorage::new(storage, namespace) +} + +/// An alias of ReadonlyPrefixedStorage::new for less verbose usage +pub fn prefixed_read<'a>( + storage: &'a dyn Storage, + namespace: &[u8], +) -> ReadonlyPrefixedStorage<'a> { + ReadonlyPrefixedStorage::new(storage, namespace) +} + +pub struct PrefixedStorage<'a> { + storage: &'a mut dyn Storage, + prefix: Vec, +} + +impl<'a> PrefixedStorage<'a> { + pub fn new(storage: &'a mut dyn Storage, namespace: &[u8]) -> Self { + PrefixedStorage { + storage, + prefix: to_length_prefixed(namespace), + } + } + + // Nested namespaces as documented in + // https://github.com/webmaster128/key-namespacing#nesting + pub fn multilevel(storage: &'a mut dyn Storage, namespaces: &[&[u8]]) -> Self { + PrefixedStorage { + storage, + prefix: to_length_prefixed_nested(namespaces), + } + } +} + +impl<'a> Storage for PrefixedStorage<'a> { + fn get(&self, key: &[u8]) -> Option> { + get_with_prefix(self.storage, &self.prefix, key) + } + + fn set(&mut self, key: &[u8], value: &[u8]) { + set_with_prefix(self.storage, &self.prefix, key, value); + } + + fn remove(&mut self, key: &[u8]) { + remove_with_prefix(self.storage, &self.prefix, key); + } + + #[cfg(feature = "iterator")] + /// range allows iteration over a set of keys, either forwards or backwards + /// uses standard rust range notation, and eg db.range(b"foo"..b"bar") also works reverse + fn range<'b>( + &'b self, + start: Option<&[u8]>, + end: Option<&[u8]>, + order: Order, + ) -> Box + 'b> { + range_with_prefix(self.storage, &self.prefix, start, end, order) + } +} + +pub struct ReadonlyPrefixedStorage<'a> { + storage: &'a dyn Storage, + prefix: Vec, +} + +impl<'a> ReadonlyPrefixedStorage<'a> { + pub fn new(storage: &'a dyn Storage, namespace: &[u8]) -> Self { + ReadonlyPrefixedStorage { + storage, + prefix: to_length_prefixed(namespace), + } + } + + // Nested namespaces as documented in + // https://github.com/webmaster128/key-namespacing#nesting + pub fn multilevel(storage: &'a dyn Storage, namespaces: &[&[u8]]) -> Self { + ReadonlyPrefixedStorage { + storage, + prefix: to_length_prefixed_nested(namespaces), + } + } +} + +impl<'a> Storage for ReadonlyPrefixedStorage<'a> { + fn get(&self, key: &[u8]) -> Option> { + get_with_prefix(self.storage, &self.prefix, key) + } + + fn set(&mut self, _key: &[u8], _value: &[u8]) { + unimplemented!(); + } + + fn remove(&mut self, _key: &[u8]) { + unimplemented!(); + } + + #[cfg(feature = "iterator")] + /// range allows iteration over a set of keys, either forwards or backwards + fn range<'b>( + &'b self, + start: Option<&[u8]>, + end: Option<&[u8]>, + order: Order, + ) -> Box + 'b> { + range_with_prefix(self.storage, &self.prefix, start, end, order) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use cosmwasm_std::testing::MockStorage; + + #[test] + fn prefixed_storage_set_and_get() { + let mut storage = MockStorage::new(); + + // set + let mut s1 = PrefixedStorage::new(&mut storage, b"foo"); + s1.set(b"bar", b"gotcha"); + assert_eq!(storage.get(b"\x00\x03foobar").unwrap(), b"gotcha".to_vec()); + + // get + let s2 = PrefixedStorage::new(&mut storage, b"foo"); + assert_eq!(s2.get(b"bar"), Some(b"gotcha".to_vec())); + assert_eq!(s2.get(b"elsewhere"), None); + } + + #[test] + fn prefixed_storage_multilevel_set_and_get() { + let mut storage = MockStorage::new(); + + // set + let mut bar = PrefixedStorage::multilevel(&mut storage, &[b"foo", b"bar"]); + bar.set(b"baz", b"winner"); + assert_eq!( + storage.get(b"\x00\x03foo\x00\x03barbaz").unwrap(), + b"winner".to_vec() + ); + + // get + let bar = PrefixedStorage::multilevel(&mut storage, &[b"foo", b"bar"]); + assert_eq!(bar.get(b"baz"), Some(b"winner".to_vec())); + assert_eq!(bar.get(b"elsewhere"), None); + } + + #[test] + fn readonly_prefixed_storage_get() { + let mut storage = MockStorage::new(); + storage.set(b"\x00\x03foobar", b"gotcha"); + + // try readonly correctly + let s1 = ReadonlyPrefixedStorage::new(&storage, b"foo"); + assert_eq!(s1.get(b"bar"), Some(b"gotcha".to_vec())); + assert_eq!(s1.get(b"elsewhere"), None); + + // no collisions with other prefixes + let s2 = ReadonlyPrefixedStorage::new(&storage, b"fo"); + assert_eq!(s2.get(b"obar"), None); + } + + #[test] + fn readonly_prefixed_storage_multilevel_get() { + let mut storage = MockStorage::new(); + storage.set(b"\x00\x03foo\x00\x03barbaz", b"winner"); + + let bar = ReadonlyPrefixedStorage::multilevel(&storage, &[b"foo", b"bar"]); + assert_eq!(bar.get(b"baz"), Some(b"winner".to_vec())); + assert_eq!(bar.get(b"elsewhere"), None); + } +} diff --git a/packages/multi-test/src/prefixed_storage/length_prefixed.rs b/packages/multi-test/src/prefixed_storage/length_prefixed.rs new file mode 100644 index 000000000..5d97b3b41 --- /dev/null +++ b/packages/multi-test/src/prefixed_storage/length_prefixed.rs @@ -0,0 +1,176 @@ +//! This module is an implemention of a namespacing scheme described +//! in https://github.com/webmaster128/key-namespacing#length-prefixed-keys +//! +//! Everything in this file is only responsible for building such keys +//! and is in no way specific to any kind of storage. + +/// Calculates the raw key prefix for a given namespace as documented +/// in https://github.com/webmaster128/key-namespacing#length-prefixed-keys +pub fn to_length_prefixed(namespace: &[u8]) -> Vec { + let mut out = Vec::with_capacity(namespace.len() + 2); + out.extend_from_slice(&encode_length(namespace)); + out.extend_from_slice(namespace); + out +} + +/// Calculates the raw key prefix for a given nested namespace +/// as documented in https://github.com/webmaster128/key-namespacing#nesting +pub fn to_length_prefixed_nested(namespaces: &[&[u8]]) -> Vec { + let mut size = 0; + for &namespace in namespaces { + size += namespace.len() + 2; + } + + let mut out = Vec::with_capacity(size); + for &namespace in namespaces { + out.extend_from_slice(&encode_length(namespace)); + out.extend_from_slice(namespace); + } + out +} + +/// Encodes the length of a given namespace as a 2 byte big endian encoded integer +fn encode_length(namespace: &[u8]) -> [u8; 2] { + if namespace.len() > 0xFFFF { + panic!("only supports namespaces up to length 0xFFFF") + } + let length_bytes = (namespace.len() as u32).to_be_bytes(); + [length_bytes[2], length_bytes[3]] +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn to_length_prefixed_works() { + assert_eq!(to_length_prefixed(b""), b"\x00\x00"); + assert_eq!(to_length_prefixed(b"a"), b"\x00\x01a"); + assert_eq!(to_length_prefixed(b"ab"), b"\x00\x02ab"); + assert_eq!(to_length_prefixed(b"abc"), b"\x00\x03abc"); + } + + #[test] + fn to_length_prefixed_works_for_long_prefix() { + let long_namespace1 = vec![0; 256]; + let prefix1 = to_length_prefixed(&long_namespace1); + assert_eq!(prefix1.len(), 256 + 2); + assert_eq!(&prefix1[0..2], b"\x01\x00"); + + let long_namespace2 = vec![0; 30000]; + let prefix2 = to_length_prefixed(&long_namespace2); + assert_eq!(prefix2.len(), 30000 + 2); + assert_eq!(&prefix2[0..2], b"\x75\x30"); + + let long_namespace3 = vec![0; 0xFFFF]; + let prefix3 = to_length_prefixed(&long_namespace3); + assert_eq!(prefix3.len(), 0xFFFF + 2); + assert_eq!(&prefix3[0..2], b"\xFF\xFF"); + } + + #[test] + #[should_panic(expected = "only supports namespaces up to length 0xFFFF")] + fn to_length_prefixed_panics_for_too_long_prefix() { + let limit = 0xFFFF; + let long_namespace = vec![0; limit + 1]; + to_length_prefixed(&long_namespace); + } + + #[test] + fn to_length_prefixed_calculates_capacity_correctly() { + // Those tests cannot guarantee the required capacity was calculated correctly before + // the vector allocation but increase the likelyhood of a proper implementation. + + let key = to_length_prefixed(b""); + assert_eq!(key.capacity(), key.len()); + + let key = to_length_prefixed(b"h"); + assert_eq!(key.capacity(), key.len()); + + let key = to_length_prefixed(b"hij"); + assert_eq!(key.capacity(), key.len()); + } + + #[test] + fn to_length_prefixed_nested_works() { + assert_eq!(to_length_prefixed_nested(&[]), b""); + assert_eq!(to_length_prefixed_nested(&[b""]), b"\x00\x00"); + assert_eq!(to_length_prefixed_nested(&[b"", b""]), b"\x00\x00\x00\x00"); + + assert_eq!(to_length_prefixed_nested(&[b"a"]), b"\x00\x01a"); + assert_eq!( + to_length_prefixed_nested(&[b"a", b"ab"]), + b"\x00\x01a\x00\x02ab" + ); + assert_eq!( + to_length_prefixed_nested(&[b"a", b"ab", b"abc"]), + b"\x00\x01a\x00\x02ab\x00\x03abc" + ); + } + + #[test] + fn to_length_prefixed_nested_allows_many_long_namespaces() { + // The 0xFFFF limit is for each namespace, not for the combination of them + + let long_namespace1 = vec![0xaa; 0xFFFD]; + let long_namespace2 = vec![0xbb; 0xFFFE]; + let long_namespace3 = vec![0xcc; 0xFFFF]; + + let prefix = + to_length_prefixed_nested(&[&long_namespace1, &long_namespace2, &long_namespace3]); + assert_eq!(&prefix[0..2], b"\xFF\xFD"); + assert_eq!(&prefix[2..(2 + 0xFFFD)], long_namespace1.as_slice()); + assert_eq!(&prefix[(2 + 0xFFFD)..(2 + 0xFFFD + 2)], b"\xFF\xFe"); + assert_eq!( + &prefix[(2 + 0xFFFD + 2)..(2 + 0xFFFD + 2 + 0xFFFE)], + long_namespace2.as_slice() + ); + assert_eq!( + &prefix[(2 + 0xFFFD + 2 + 0xFFFE)..(2 + 0xFFFD + 2 + 0xFFFE + 2)], + b"\xFF\xFf" + ); + assert_eq!( + &prefix[(2 + 0xFFFD + 2 + 0xFFFE + 2)..(2 + 0xFFFD + 2 + 0xFFFE + 2 + 0xFFFF)], + long_namespace3.as_slice() + ); + } + + #[test] + fn to_length_prefixed_nested_calculates_capacity_correctly() { + // Those tests cannot guarantee the required capacity was calculated correctly before + // the vector allocation but increase the likelyhood of a proper implementation. + + let key = to_length_prefixed_nested(&[]); + assert_eq!(key.capacity(), key.len()); + + let key = to_length_prefixed_nested(&[b""]); + assert_eq!(key.capacity(), key.len()); + + let key = to_length_prefixed_nested(&[b"a"]); + assert_eq!(key.capacity(), key.len()); + + let key = to_length_prefixed_nested(&[b"a", b"bc"]); + assert_eq!(key.capacity(), key.len()); + + let key = to_length_prefixed_nested(&[b"a", b"bc", b"def"]); + assert_eq!(key.capacity(), key.len()); + } + + #[test] + fn encode_length_works() { + assert_eq!(encode_length(b""), *b"\x00\x00"); + assert_eq!(encode_length(b"a"), *b"\x00\x01"); + assert_eq!(encode_length(b"aa"), *b"\x00\x02"); + assert_eq!(encode_length(b"aaa"), *b"\x00\x03"); + assert_eq!(encode_length(&vec![1; 255]), *b"\x00\xff"); + assert_eq!(encode_length(&vec![1; 256]), *b"\x01\x00"); + assert_eq!(encode_length(&vec![1; 12345]), *b"\x30\x39"); + assert_eq!(encode_length(&vec![1; 65535]), *b"\xff\xff"); + } + + #[test] + #[should_panic(expected = "only supports namespaces up to length 0xFFFF")] + fn encode_length_panics_for_large_values() { + encode_length(&vec![1; 65536]); + } +} diff --git a/packages/multi-test/src/prefixed_storage/namespace_helpers.rs b/packages/multi-test/src/prefixed_storage/namespace_helpers.rs new file mode 100644 index 000000000..2e69605de --- /dev/null +++ b/packages/multi-test/src/prefixed_storage/namespace_helpers.rs @@ -0,0 +1,220 @@ +use cosmwasm_std::Storage; +#[cfg(feature = "iterator")] +use cosmwasm_std::{Order, Record}; + +pub(crate) fn get_with_prefix( + storage: &dyn Storage, + namespace: &[u8], + key: &[u8], +) -> Option> { + storage.get(&concat(namespace, key)) +} + +pub(crate) fn set_with_prefix( + storage: &mut dyn Storage, + namespace: &[u8], + key: &[u8], + value: &[u8], +) { + storage.set(&concat(namespace, key), value); +} + +pub(crate) fn remove_with_prefix(storage: &mut dyn Storage, namespace: &[u8], key: &[u8]) { + storage.remove(&concat(namespace, key)); +} + +#[inline] +fn concat(namespace: &[u8], key: &[u8]) -> Vec { + let mut k = namespace.to_vec(); + k.extend_from_slice(key); + k +} + +#[cfg(feature = "iterator")] +pub(crate) fn range_with_prefix<'a>( + storage: &'a dyn Storage, + namespace: &[u8], + start: Option<&[u8]>, + end: Option<&[u8]>, + order: Order, +) -> Box + 'a> { + // prepare start, end with prefix + let start = match start { + Some(s) => concat(namespace, s), + None => namespace.to_vec(), + }; + let end = match end { + Some(e) => concat(namespace, e), + // end is updating last byte by one + None => namespace_upper_bound(namespace), + }; + + // get iterator from storage + let base_iterator = storage.range(Some(&start), Some(&end), order); + + // make a copy for the closure to handle lifetimes safely + let prefix = namespace.to_vec(); + let mapped = base_iterator.map(move |(k, v)| (trim(&prefix, &k), v)); + Box::new(mapped) +} + +#[cfg(feature = "iterator")] +#[inline] +fn trim(namespace: &[u8], key: &[u8]) -> Vec { + key[namespace.len()..].to_vec() +} + +/// Returns a new vec of same length and last byte incremented by one +/// If last bytes are 255, we handle overflow up the chain. +/// If all bytes are 255, this returns wrong data - but that is never possible as a namespace +#[cfg(feature = "iterator")] +fn namespace_upper_bound(input: &[u8]) -> Vec { + let mut copy = input.to_vec(); + // zero out all trailing 255, increment first that is not such + for i in (0..input.len()).rev() { + if copy[i] == 255 { + copy[i] = 0; + } else { + copy[i] += 1; + break; + } + } + copy +} + +#[cfg(test)] +mod tests { + use super::super::length_prefixed::to_length_prefixed; + use super::*; + use cosmwasm_std::testing::MockStorage; + + #[test] + fn prefix_get_set() { + let mut storage = MockStorage::new(); + let prefix = to_length_prefixed(b"foo"); + + set_with_prefix(&mut storage, &prefix, b"bar", b"gotcha"); + let rfoo = get_with_prefix(&storage, &prefix, b"bar"); + assert_eq!(rfoo, Some(b"gotcha".to_vec())); + + // no collisions with other prefixes + let other_prefix = to_length_prefixed(b"fo"); + let collision = get_with_prefix(&storage, &other_prefix, b"obar"); + assert_eq!(collision, None); + } + + #[test] + #[cfg(feature = "iterator")] + fn range_works() { + let mut storage = MockStorage::new(); + let prefix = to_length_prefixed(b"foo"); + let other_prefix = to_length_prefixed(b"food"); + + // set some values in this range + set_with_prefix(&mut storage, &prefix, b"bar", b"none"); + set_with_prefix(&mut storage, &prefix, b"snowy", b"day"); + + // set some values outside this range + set_with_prefix(&mut storage, &other_prefix, b"moon", b"buggy"); + + // ensure we get proper result from prefixed_range iterator + let mut iter = range_with_prefix(&storage, &prefix, None, None, Order::Descending); + let first = iter.next().unwrap(); + assert_eq!(first, (b"snowy".to_vec(), b"day".to_vec())); + let second = iter.next().unwrap(); + assert_eq!(second, (b"bar".to_vec(), b"none".to_vec())); + assert!(iter.next().is_none()); + + // ensure we get raw result from base range + let iter = storage.range(None, None, Order::Ascending); + assert_eq!(3, iter.count()); + + // foo comes first + let mut iter = storage.range(None, None, Order::Ascending); + let first = iter.next().unwrap(); + let expected_key = concat(&prefix, b"bar"); + assert_eq!(first, (expected_key, b"none".to_vec())); + } + + #[test] + #[cfg(feature = "iterator")] + fn range_with_prefix_wrapover() { + let mut storage = MockStorage::new(); + // if we don't properly wrap over there will be issues here (note 255+1 is used to calculate end) + let prefix = to_length_prefixed(b"f\xff\xff"); + let other_prefix = to_length_prefixed(b"f\xff\x44"); + + // set some values in this range + set_with_prefix(&mut storage, &prefix, b"bar", b"none"); + set_with_prefix(&mut storage, &prefix, b"snowy", b"day"); + + // set some values outside this range + set_with_prefix(&mut storage, &other_prefix, b"moon", b"buggy"); + + // ensure we get proper result from prefixed_range iterator + let iter = range_with_prefix(&storage, &prefix, None, None, Order::Descending); + let elements: Vec = iter.collect(); + assert_eq!( + elements, + vec![ + (b"snowy".to_vec(), b"day".to_vec()), + (b"bar".to_vec(), b"none".to_vec()), + ] + ); + } + + #[test] + #[cfg(feature = "iterator")] + fn range_with_start_end_set() { + let mut storage = MockStorage::new(); + // if we don't properly wrap over there will be issues here (note 255+1 is used to calculate end) + let prefix = to_length_prefixed(b"f\xff\xff"); + let other_prefix = to_length_prefixed(b"f\xff\x44"); + + // set some values in this range + set_with_prefix(&mut storage, &prefix, b"bar", b"none"); + set_with_prefix(&mut storage, &prefix, b"snowy", b"day"); + + // set some values outside this range + set_with_prefix(&mut storage, &other_prefix, b"moon", b"buggy"); + + // make sure start and end are applied properly + let res: Vec = + range_with_prefix(&storage, &prefix, Some(b"b"), Some(b"c"), Order::Ascending) + .collect(); + assert_eq!(res.len(), 1); + assert_eq!(res[0], (b"bar".to_vec(), b"none".to_vec())); + + // make sure start and end are applied properly + let res_count = range_with_prefix( + &storage, + &prefix, + Some(b"bas"), + Some(b"sno"), + Order::Ascending, + ) + .count(); + assert_eq!(res_count, 0); + + let res: Vec = + range_with_prefix(&storage, &prefix, Some(b"ant"), None, Order::Ascending).collect(); + assert_eq!(res.len(), 2); + assert_eq!(res[0], (b"bar".to_vec(), b"none".to_vec())); + assert_eq!(res[1], (b"snowy".to_vec(), b"day".to_vec())); + } + + #[test] + #[cfg(feature = "iterator")] + fn namespace_upper_bound_works() { + assert_eq!(namespace_upper_bound(b"bob"), b"boc".to_vec()); + assert_eq!(namespace_upper_bound(b"fo\xfe"), b"fo\xff".to_vec()); + assert_eq!(namespace_upper_bound(b"fo\xff"), b"fp\x00".to_vec()); + // multiple \xff roll over + assert_eq!( + namespace_upper_bound(b"fo\xff\xff\xff"), + b"fp\x00\x00\x00".to_vec() + ); + // \xff not at the end are ignored + assert_eq!(namespace_upper_bound(b"\xffabc"), b"\xffabd".to_vec()); + } +} diff --git a/packages/multi-test/src/wasm.rs b/packages/multi-test/src/wasm.rs index 54afe9681..f615cc0df 100644 --- a/packages/multi-test/src/wasm.rs +++ b/packages/multi-test/src/wasm.rs @@ -8,7 +8,6 @@ use cosmwasm_std::{ QuerierWrapper, Record, Reply, ReplyOn, Response, StdResult, Storage, SubMsg, SubMsgResponse, SubMsgResult, TransactionInfo, WasmMsg, WasmQuery, }; -use cosmwasm_storage::{prefixed, prefixed_read, PrefixedStorage, ReadonlyPrefixedStorage}; use prost::Message; use schemars::JsonSchema; use serde::de::DeserializeOwned; @@ -20,6 +19,7 @@ use crate::app::{CosmosRouter, RouterQuerier}; use crate::contracts::Contract; use crate::error::Error; use crate::executor::AppResponse; +use crate::prefixed_storage::{prefixed, prefixed_read, PrefixedStorage, ReadonlyPrefixedStorage}; use crate::transactions::transactional; use cosmwasm_std::testing::mock_wasmd_attr; From 01eed0491a8eeb575cdade0e23e860f042e5639b Mon Sep 17 00:00:00 2001 From: Tomasz Kurcz Date: Wed, 12 Oct 2022 18:26:22 +0200 Subject: [PATCH 2/3] Update README - remove `cosmwasm-storage` reference --- packages/storage-plus/README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/storage-plus/README.md b/packages/storage-plus/README.md index 017dba7ba..b87b84223 100644 --- a/packages/storage-plus/README.md +++ b/packages/storage-plus/README.md @@ -30,10 +30,6 @@ for interacting with it without dealing with raw bytes. And `Map`, which allows you to store multiple unique typed objects under a prefix, indexed by a simple or compound (eg. `(&[u8], &[u8])`) primary key. -These correspond to the concepts represented in `cosmwasm_storage` by -`Singleton` and `Bucket`, but with a re-designed API and implementation -to require less typing for developers and less gas usage in the contracts. - ## Item The usage of an [`Item`](./src/item.rs) is pretty straight-forward. @@ -703,4 +699,4 @@ fn demo() -> StdResult<()> { Ok(()) } -``` \ No newline at end of file +``` From 972546f52f65f2c9df5d4c89eafa9cea300401fb Mon Sep 17 00:00:00 2001 From: Tomasz Kurcz Date: Wed, 12 Oct 2022 18:36:22 +0200 Subject: [PATCH 3/3] Remove `legacy_helpers.rs` --- packages/storage-plus/src/legacy_helpers.rs | 136 -------------------- 1 file changed, 136 deletions(-) delete mode 100644 packages/storage-plus/src/legacy_helpers.rs diff --git a/packages/storage-plus/src/legacy_helpers.rs b/packages/storage-plus/src/legacy_helpers.rs deleted file mode 100644 index d5931fa87..000000000 --- a/packages/storage-plus/src/legacy_helpers.rs +++ /dev/null @@ -1,136 +0,0 @@ -// This code is intentionally included not in lib.rs -// Most of it will be deleted. But maybe we want to borrow some chunks, so keeping them here. - -/// Calculates the raw key prefix for a given nested namespace -/// as documented in https://github.com/webmaster128/key-namespacing#nesting -pub(crate) fn to_length_prefixed_nested(namespaces: &[&[u8]]) -> Vec { - let mut size = 0; - for &namespace in namespaces { - size += namespace.len() + 2; - } - - let mut out = Vec::with_capacity(size); - for &namespace in namespaces { - out.extend_from_slice(&encode_length(namespace)); - out.extend_from_slice(namespace); - } - out -} - -pub(crate) fn length_prefixed_with_key(namespace: &[u8], key: &[u8]) -> Vec { - let mut out = Vec::with_capacity(namespace.len() + 2 + key.len()); - out.extend_from_slice(&encode_length(namespace)); - out.extend_from_slice(namespace); - out.extend_from_slice(key); - out -} - -pub(crate) fn get_with_prefix( - storage: &dyn Storage, - namespace: &[u8], - key: &[u8], -) -> Option> { - storage.get(&concat(namespace, key)) -} - -pub(crate) fn set_with_prefix( - storage: &mut dyn Storage, - namespace: &[u8], - key: &[u8], - value: &[u8], -) { - storage.set(&concat(namespace, key), value); -} - -pub(crate) fn remove_with_prefix(storage: &mut dyn Storage, namespace: &[u8], key: &[u8]) { - storage.remove(&concat(namespace, key)); -} - -#[cfg(test)] -mod legacy_test { - use super::*; - use crate::helpers::*; - use cosmwasm_std::testing::MockStorage; - - #[test] - fn to_length_prefixed_nested_works() { - assert_eq!(to_length_prefixed_nested(&[]), b""); - assert_eq!(to_length_prefixed_nested(&[b""]), b"\x00\x00"); - assert_eq!(to_length_prefixed_nested(&[b"", b""]), b"\x00\x00\x00\x00"); - - assert_eq!(to_length_prefixed_nested(&[b"a"]), b"\x00\x01a"); - assert_eq!( - to_length_prefixed_nested(&[b"a", b"ab"]), - b"\x00\x01a\x00\x02ab" - ); - assert_eq!( - to_length_prefixed_nested(&[b"a", b"ab", b"abc"]), - b"\x00\x01a\x00\x02ab\x00\x03abc" - ); - } - - #[test] - fn to_length_prefixed_nested_allows_many_long_namespaces() { - // The 0xFFFF limit is for each namespace, not for the combination of them - - let long_namespace1 = vec![0xaa; 0xFFFD]; - let long_namespace2 = vec![0xbb; 0xFFFE]; - let long_namespace3 = vec![0xcc; 0xFFFF]; - - let prefix = - to_length_prefixed_nested(&[&long_namespace1, &long_namespace2, &long_namespace3]); - assert_eq!(&prefix[0..2], b"\xFF\xFD"); - assert_eq!(&prefix[2..(2 + 0xFFFD)], long_namespace1.as_slice()); - assert_eq!(&prefix[(2 + 0xFFFD)..(2 + 0xFFFD + 2)], b"\xFF\xFe"); - assert_eq!( - &prefix[(2 + 0xFFFD + 2)..(2 + 0xFFFD + 2 + 0xFFFE)], - long_namespace2.as_slice() - ); - assert_eq!( - &prefix[(2 + 0xFFFD + 2 + 0xFFFE)..(2 + 0xFFFD + 2 + 0xFFFE + 2)], - b"\xFF\xFf" - ); - assert_eq!( - &prefix[(2 + 0xFFFD + 2 + 0xFFFE + 2)..(2 + 0xFFFD + 2 + 0xFFFE + 2 + 0xFFFF)], - long_namespace3.as_slice() - ); - } - - #[test] - fn to_length_prefixed_nested_calculates_capacity_correctly() { - // Those tests cannot guarantee the required capacity was calculated correctly before - // the vector allocation but increase the likelyhood of a proper implementation. - - let key = to_length_prefixed_nested(&[]); - assert_eq!(key.capacity(), key.len()); - - let key = to_length_prefixed_nested(&[b""]); - assert_eq!(key.capacity(), key.len()); - - let key = to_length_prefixed_nested(&[b"a"]); - assert_eq!(key.capacity(), key.len()); - - let key = to_length_prefixed_nested(&[b"a", b"bc"]); - assert_eq!(key.capacity(), key.len()); - - let key = to_length_prefixed_nested(&[b"a", b"bc", b"def"]); - assert_eq!(key.capacity(), key.len()); - } - - - #[test] - fn prefix_get_set() { - let mut storage = MockStorage::new(); - let prefix = to_length_prefixed(b"foo"); - - set_with_prefix(&mut storage, &prefix, b"bar", b"gotcha"); - let rfoo = get_with_prefix(&storage, &prefix, b"bar"); - assert_eq!(rfoo, Some(b"gotcha".to_vec())); - - // no collisions with other prefixes - let other_prefix = to_length_prefixed(b"fo"); - let collision = get_with_prefix(&storage, &other_prefix, b"obar"); - assert_eq!(collision, None); - } - -} \ No newline at end of file