From a3dd434a64222f8919cba8310e5869ad6080511d Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Sat, 19 Dec 2020 12:16:58 +0100 Subject: [PATCH 01/27] Change marker to bool --- packages/storage-plus/src/indexes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index a27cc4974..99c64d65c 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -11,7 +11,7 @@ use crate::prefix::range_with_prefix; use crate::{Bound, Endian}; /// MARKER is stored in the multi-index as value, but we only look at the key (which is pk) -const MARKER: u32 = 1; +const MARKER: bool = true; pub fn index_string(data: &str) -> Vec { data.as_bytes().to_vec() @@ -41,7 +41,7 @@ where pub struct MultiIndex<'a, T> { index: fn(&T) -> Vec, - idx_map: Map<'a, (&'a [u8], &'a [u8]), u32>, + idx_map: Map<'a, (&'a [u8], &'a [u8]), bool>, // note, we collapse the pubkey - combining everything under the namespace - even if it is composite pk_map: Map<'a, &'a [u8], T>, } From 2444735e5c1d96097d6b69ee2ec2301556a5f826 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Sat, 19 Dec 2020 21:22:23 +0100 Subject: [PATCH 02/27] Add U8Key type alias --- packages/storage-plus/src/keys.rs | 1 + packages/storage-plus/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/storage-plus/src/keys.rs b/packages/storage-plus/src/keys.rs index f42a69aa7..d38be12e4 100644 --- a/packages/storage-plus/src/keys.rs +++ b/packages/storage-plus/src/keys.rs @@ -156,6 +156,7 @@ impl<'a, T: AsRef> Prefixer<'a> for T { } } +pub type U8Key = IntKey; pub type U16Key = IntKey; pub type U32Key = IntKey; pub type U64Key = IntKey; diff --git a/packages/storage-plus/src/lib.rs b/packages/storage-plus/src/lib.rs index 9af4d96cf..7771fd770 100644 --- a/packages/storage-plus/src/lib.rs +++ b/packages/storage-plus/src/lib.rs @@ -16,7 +16,7 @@ pub use indexed_map::{IndexList, IndexedMap}; #[cfg(feature = "iterator")] pub use indexes::{index_int, index_string, Index, MultiIndex, UniqueIndex}; pub use item::Item; -pub use keys::{PkOwned, Prefixer, PrimaryKey, U128Key, U16Key, U32Key, U64Key}; +pub use keys::{PkOwned, Prefixer, PrimaryKey, U128Key, U16Key, U32Key, U64Key, U8Key}; pub use map::Map; pub use path::Path; #[cfg(feature = "iterator")] From 7ab9db900d07beef7906e8a204e020158be6d6ee Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Sat, 19 Dec 2020 21:23:27 +0100 Subject: [PATCH 03/27] Add composite int keys map tests / example --- packages/storage-plus/src/map.rs | 54 ++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/packages/storage-plus/src/map.rs b/packages/storage-plus/src/map.rs index 133618002..9eb3702b9 100644 --- a/packages/storage-plus/src/map.rs +++ b/packages/storage-plus/src/map.rs @@ -104,6 +104,7 @@ mod test { use serde::{Deserialize, Serialize}; use std::ops::Deref; + use crate::{U64Key, U8Key}; use cosmwasm_std::testing::MockStorage; #[cfg(feature = "iterator")] use cosmwasm_std::{Order, StdResult}; @@ -118,6 +119,8 @@ mod test { const ALLOWANCE: Map<(&[u8], &[u8]), u64> = Map::new("allow"); + const INT_KEYS: Map<(U8Key, U64Key), u64> = Map::new("integers_tuple"); + #[test] fn create_path() { let path = PEOPLE.key(b"john"); @@ -180,6 +183,25 @@ mod test { assert_eq!(1234, same); } + #[test] + fn composite_integer_keys() { + let mut store = MockStorage::new(); + + // save and load on a composite key + let test = INT_KEYS.key((U8Key::new(2), U64Key::new(12345))); + assert_eq!(None, test.may_load(&store).unwrap()); + test.save(&mut store, &1234).unwrap(); + assert_eq!(1234, test.load(&store).unwrap()); + + // not under other key + let different = INT_KEYS.may_load(&store, (2.into(), 12346.into())).unwrap(); + assert_eq!(None, different); + + // matches under a proper copy + let same = INT_KEYS.load(&store, (2.into(), 12345.into())).unwrap(); + assert_eq!(1234, same); + } + #[test] #[cfg(feature = "iterator")] fn range_simple_key() { @@ -237,6 +259,38 @@ mod test { ); } + #[test] + #[cfg(feature = "iterator")] + fn range_int_composite_key() { + let mut store = MockStorage::new(); + + // save and load on three keys, one under different owner + INT_KEYS + .save(&mut store, (1.into(), 123456.into()), &1000) + .unwrap(); + INT_KEYS + .save(&mut store, (1.into(), 123457.into()), &3000) + .unwrap(); + INT_KEYS + .save(&mut store, (2.into(), 123456.into()), &5000) + .unwrap(); + + // let's try to iterate! + let all: StdResult> = INT_KEYS + .prefix(1.into()) + .range(&store, None, None, Order::Ascending) + .collect(); + let all = all.unwrap(); + assert_eq!(2, all.len()); + assert_eq!( + all, + vec![ + (123456u64.to_be_bytes().to_vec(), 1000), + (123457u64.to_be_bytes().to_vec(), 3000) + ] + ); + } + #[test] fn basic_update() { let mut store = MockStorage::new(); From 08f2c8ebccf066286bde7f2ce691de7dca24f3de Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Sun, 20 Dec 2020 09:56:34 +0100 Subject: [PATCH 04/27] Impl (canonical) prefix and range for MultiIndex --- contracts/cw721-base/src/state.rs | 2 +- packages/storage-plus/src/indexed_map.rs | 2 +- packages/storage-plus/src/indexes.rs | 49 ++++++++++++++++++++---- 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/contracts/cw721-base/src/state.rs b/contracts/cw721-base/src/state.rs index a18931bb8..35c6add69 100644 --- a/contracts/cw721-base/src/state.rs +++ b/contracts/cw721-base/src/state.rs @@ -46,7 +46,7 @@ pub fn increment_tokens(storage: &mut dyn Storage) -> StdResult { } pub struct TokenIndexes<'a> { - pub owner: MultiIndex<'a, TokenInfo>, + pub owner: MultiIndex<'a, &'a [u8], TokenInfo>, } impl<'a> IndexList for TokenIndexes<'a> { diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index 7d08d0111..6fa4225a2 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -168,7 +168,7 @@ mod test { } struct DataIndexes<'a> { - pub name: MultiIndex<'a, Data>, + pub name: MultiIndex<'a, &'a [u8], Data>, pub age: UniqueIndex<'a, Data>, } diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index 99c64d65c..18f148222 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -6,9 +6,11 @@ use serde::{Deserialize, Serialize}; use cosmwasm_std::{Binary, Order, StdError, StdResult, Storage, KV}; +use crate::keys::{EmptyPrefix, Prefixer}; use crate::map::Map; use crate::prefix::range_with_prefix; -use crate::{Bound, Endian}; +use crate::{Bound, Endian, Prefix, PrimaryKey}; +use std::marker::PhantomData; /// MARKER is stored in the multi-index as value, but we only look at the key (which is pk) const MARKER: bool = true; @@ -39,27 +41,32 @@ where fn remove(&self, store: &mut dyn Storage, pk: &[u8], old_data: &T) -> StdResult<()>; } -pub struct MultiIndex<'a, T> { +pub struct MultiIndex<'a, K, T> { index: fn(&T) -> Vec, + idx_namespace: &'a [u8], idx_map: Map<'a, (&'a [u8], &'a [u8]), bool>, - // note, we collapse the pubkey - combining everything under the namespace - even if it is composite + // note, we collapse the pk - combining everything under the namespace - even if it is composite pk_map: Map<'a, &'a [u8], T>, + idx_type: PhantomData, } -impl<'a, T> MultiIndex<'a, T> { +impl<'a, K, T> MultiIndex<'a, K, T> { // TODO: make this a const fn pub fn new(idx_fn: fn(&T) -> Vec, pk_namespace: &'a str, idx_namespace: &'a str) -> Self { MultiIndex { index: idx_fn, pk_map: Map::new(pk_namespace), + idx_namespace: idx_namespace.as_bytes(), idx_map: Map::new(idx_namespace), + idx_type: PhantomData, } } } -impl<'a, T> Index for MultiIndex<'a, T> +impl<'a, K, T> Index for MultiIndex<'a, K, T> where T: Serialize + DeserializeOwned + Clone, + K: PrimaryKey<'a>, { fn save(&self, store: &mut dyn Storage, pk: &[u8], data: &T) -> StdResult<()> { let idx = (self.index)(data); @@ -73,9 +80,10 @@ where } } -impl<'a, T> MultiIndex<'a, T> +impl<'a, K, T> MultiIndex<'a, K, T> where T: Serialize + DeserializeOwned + Clone, + K: PrimaryKey<'a>, { pub fn pks<'c>( &self, @@ -106,6 +114,10 @@ where Box::new(mapped) } + pub fn prefix(&self, p: K::Prefix) -> Prefix { + Prefix::new(self.idx_namespace, &p.prefix()) + } + #[cfg(test)] pub fn count<'c>(&self, store: &'c dyn Storage, idx: &[u8]) -> usize { self.pks(store, idx, None, None, Order::Ascending).count() @@ -123,9 +135,32 @@ where } } +// short-cut for simple keys, rather than .prefix(()).range(...) +impl<'a, K, T> MultiIndex<'a, K, T> +where + T: Serialize + DeserializeOwned + Clone, + K: PrimaryKey<'a>, + K::Prefix: EmptyPrefix, +{ + // I would prefer not to copy code from Prefix, but no other way + // with lifetimes (create Prefix inside function and return ref = no no) + pub fn range<'c>( + &self, + store: &'c dyn Storage, + min: Option, + max: Option, + order: cosmwasm_std::Order, + ) -> Box>> + 'c> + where + T: 'c, + { + self.prefix(K::Prefix::new()).range(store, min, max, order) + } +} + #[derive(Deserialize, Serialize)] pub(crate) struct UniqueRef { - // note, we collapse the pubkey - combining everything under the namespace - even if it is composite + // note, we collapse the pk - combining everything under the namespace - even if it is composite pk: Binary, value: T, } From 79d5bae95c4ac23e3b53da21cf37500e0cc233de Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Mon, 21 Dec 2020 10:59:54 +0100 Subject: [PATCH 05/27] Add additional Map range test / example --- packages/storage-plus/src/map.rs | 34 +++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/packages/storage-plus/src/map.rs b/packages/storage-plus/src/map.rs index 9eb3702b9..608dd1bdf 100644 --- a/packages/storage-plus/src/map.rs +++ b/packages/storage-plus/src/map.rs @@ -226,8 +226,40 @@ mod test { assert_eq!(2, all.len()); assert_eq!( all, - vec![(b"jim".to_vec(), data2), (b"john".to_vec(), data)] + vec![ + (b"jim".to_vec(), data2.clone()), + (b"john".to_vec(), data.clone()) + ] ); + + // let's try to iterate over a range + let all: StdResult> = PEOPLE + .range( + &store, + Some(Bound::Inclusive(b"j".to_vec())), + None, + Order::Ascending, + ) + .collect(); + let all = all.unwrap(); + assert_eq!(2, all.len()); + assert_eq!( + all, + vec![(b"jim".to_vec(), data2), (b"john".to_vec(), data.clone())] + ); + + // let's try to iterate over a more restrictive range + let all: StdResult> = PEOPLE + .range( + &store, + Some(Bound::Inclusive(b"jo".to_vec())), + None, + Order::Ascending, + ) + .collect(); + let all = all.unwrap(); + assert_eq!(1, all.len()); + assert_eq!(all, vec![(b"john".to_vec(), data)]); } #[test] From 8cb0a77f514aaaf89ebe60cd1fe264371922d301 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Mon, 21 Dec 2020 11:08:37 +0100 Subject: [PATCH 06/27] WIP: Make range work with original prefix() --- packages/storage-plus/src/indexes.rs | 22 ++++++++++++++++++---- packages/storage-plus/src/lib.rs | 2 +- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index 18f148222..d3f2fdedf 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -13,12 +13,19 @@ use crate::{Bound, Endian, Prefix, PrimaryKey}; use std::marker::PhantomData; /// MARKER is stored in the multi-index as value, but we only look at the key (which is pk) -const MARKER: bool = true; +// FIXME: Re-introduce this for MultiIndex +// const MARKER: bool = true; pub fn index_string(data: &str) -> Vec { data.as_bytes().to_vec() } +pub fn index_tuple(name: &str, age: u32) -> Vec { + let mut name = name.as_bytes().to_vec(); + name.extend_from_slice(&age.to_be_bytes()); + name +} + // Look at https://docs.rs/endiannezz/0.4.1/endiannezz/trait.Primitive.html // if you want to make this generic over all ints pub fn index_int(data: T) -> Vec { @@ -44,7 +51,7 @@ where pub struct MultiIndex<'a, K, T> { index: fn(&T) -> Vec, idx_namespace: &'a [u8], - idx_map: Map<'a, (&'a [u8], &'a [u8]), bool>, + idx_map: Map<'a, (&'a [u8], &'a [u8]), T>, // note, we collapse the pk - combining everything under the namespace - even if it is composite pk_map: Map<'a, &'a [u8], T>, idx_type: PhantomData, @@ -70,7 +77,7 @@ where { fn save(&self, store: &mut dyn Storage, pk: &[u8], data: &T) -> StdResult<()> { let idx = (self.index)(data); - self.idx_map.save(store, (&idx, &pk), &MARKER) + self.idx_map.save(store, (&idx, &pk), &data) } fn remove(&self, store: &mut dyn Storage, pk: &[u8], old_data: &T) -> StdResult<()> { @@ -145,7 +152,7 @@ where // I would prefer not to copy code from Prefix, but no other way // with lifetimes (create Prefix inside function and return ref = no no) pub fn range<'c>( - &self, + &'c self, store: &'c dyn Storage, min: Option, max: Option, @@ -155,6 +162,13 @@ where T: 'c, { self.prefix(K::Prefix::new()).range(store, min, max, order) + // let mapped = self.prefix(K::Prefix::new()).range(store, min, max, order).map(move |res| { + // let kv = res?; + // let pk = kv.0; + // let v = self.pk_map.load(store, &pk)?; + // Ok((pk, v)) + // }); + // Box::new(mapped) } } diff --git a/packages/storage-plus/src/lib.rs b/packages/storage-plus/src/lib.rs index 7771fd770..33d281072 100644 --- a/packages/storage-plus/src/lib.rs +++ b/packages/storage-plus/src/lib.rs @@ -14,7 +14,7 @@ pub use endian::Endian; #[cfg(feature = "iterator")] pub use indexed_map::{IndexList, IndexedMap}; #[cfg(feature = "iterator")] -pub use indexes::{index_int, index_string, Index, MultiIndex, UniqueIndex}; +pub use indexes::{index_int, index_string, index_tuple, Index, MultiIndex, UniqueIndex}; pub use item::Item; pub use keys::{PkOwned, Prefixer, PrimaryKey, U128Key, U16Key, U32Key, U64Key, U8Key}; pub use map::Map; From 8aa000253bca3204659f90b7c3f1a726e2a90d22 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Mon, 21 Dec 2020 11:09:56 +0100 Subject: [PATCH 07/27] WIP: Add simple and composite multi index tests --- packages/storage-plus/src/indexed_map.rs | 170 +++++++++++++++++++++-- 1 file changed, 157 insertions(+), 13 deletions(-) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index 6fa4225a2..b966cfe80 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -156,7 +156,9 @@ where mod test { use super::*; - use crate::indexes::{index_int, index_string, MultiIndex, UniqueIndex}; + use crate::indexes::{index_int, index_string, index_tuple, MultiIndex, UniqueIndex}; + use crate::iter_helpers::to_length_prefixed; + use crate::U32Key; use cosmwasm_std::testing::MockStorage; use cosmwasm_std::{MemoryStorage, Order}; use serde::{Deserialize, Serialize}; @@ -164,7 +166,7 @@ mod test { #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] struct Data { pub name: String, - pub age: i32, + pub age: u32, } struct DataIndexes<'a> { @@ -180,6 +182,19 @@ mod test { } } + // For composite multi index tests + struct DataCompositeMultiIndex<'a> { + pub name_age: MultiIndex<'a, (&'a [u8], U32Key), Data>, + } + + // Future Note: this can likely be macro-derived + impl<'a> IndexList for DataCompositeMultiIndex<'a> { + fn get_indexes(&'_ self) -> Box> + '_> { + let v: Vec<&dyn Index> = vec![&self.name_age]; + Box::new(v.into_iter()) + } + } + // Can we make it easier to define this? (less wordy generic) fn build_map<'a>() -> IndexedMap<'a, &'a [u8], Data, DataIndexes<'a>> { let indexes = DataIndexes { @@ -195,16 +210,37 @@ mod test { let mut map = build_map(); // save data - let data = Data { + let data1 = Data { name: "Maria".to_string(), age: 42, }; - let pk: &[u8] = b"5627"; - map.save(&mut store, pk, &data).unwrap(); + let pk1: &[u8] = b"5627"; + map.save(&mut store, pk1, &data1).unwrap(); + + let data2 = Data { + name: "Juan".to_string(), + age: 13, + }; + let pk2: &[u8] = b"5628"; + map.save(&mut store, pk2, &data2).unwrap(); + + let data3 = Data { + name: "Maria".to_string(), + age: 24, + }; + let pk3: &[u8] = b"5629"; + map.save(&mut store, pk3, &data3).unwrap(); + + let data4 = Data { + name: "Maria Luisa".to_string(), + age: 12, + }; + let pk4: &[u8] = b"5630"; + map.save(&mut store, pk4, &data4).unwrap(); // load it properly - let loaded = map.load(&store, pk).unwrap(); - assert_eq!(data, loaded); + let loaded = map.load(&store, pk1).unwrap(); + assert_eq!(data1, loaded); let count = map .idx @@ -212,7 +248,7 @@ mod test { .all_items(&store, &index_string("Maria")) .unwrap() .len(); - assert_eq!(1, count); + assert_eq!(2, count); // TODO: we load by wrong keys - get full storage key! @@ -223,10 +259,10 @@ mod test { .name .all_items(&store, &index_string("Maria")) .unwrap(); - assert_eq!(1, marias.len()); + assert_eq!(2, marias.len()); let (k, v) = &marias[0]; - assert_eq!(pk, k.as_slice()); - assert_eq!(&data, v); + assert_eq!(pk1, k.as_slice()); + assert_eq!(&data1, v); // other index doesn't match (1 byte after) let count = map @@ -258,8 +294,8 @@ mod test { // match on proper age let proper = index_int(42); let aged = map.idx.age.item(&store, &proper).unwrap().unwrap(); - assert_eq!(pk.to_vec(), aged.0); - assert_eq!(data, aged.1); + assert_eq!(pk1.to_vec(), aged.0); + assert_eq!(data1, aged.1); // no match on wrong age let too_old = index_int(43); @@ -267,6 +303,114 @@ mod test { assert_eq!(None, aged); } + #[test] + fn range_simple_key_by_multi_index() { + let mut store = MockStorage::new(); + let mut map = build_map(); + + // save data + let data1 = Data { + name: "Maria".to_string(), + age: 42, + }; + let pk: &[u8] = b"5627"; + map.save(&mut store, pk, &data1).unwrap(); + + let data2 = Data { + name: "Juan".to_string(), + age: 13, + }; + let pk: &[u8] = b"5628"; + map.save(&mut store, pk, &data2).unwrap(); + + let data3 = Data { + name: "Maria".to_string(), + age: 24, + }; + let pk: &[u8] = b"5629"; + map.save(&mut store, pk, &data3).unwrap(); + + let data4 = Data { + name: "Maria Luisa".to_string(), + age: 12, + }; + let pk: &[u8] = b"5630"; + map.save(&mut store, pk, &data4).unwrap(); + + let marias: StdResult> = map + .idx + .name + .range( + &store, + Some(Bound::Inclusive(to_length_prefixed(b"Maria"))), + None, + Order::Ascending, + ) + .collect(); + let count = marias.unwrap().len(); + assert_eq!(3, count); + } + + #[test] + fn range_composite_key_by_multi_index() { + let mut store = MockStorage::new(); + + let indexes = DataCompositeMultiIndex { + name_age: MultiIndex::new(|d| index_tuple(&d.name, d.age), "data", "data__name_age"), + }; + let mut map = IndexedMap::new("data", indexes); + + // save data + let data1 = Data { + name: "Maria".to_string(), + age: 42, + }; + let pk: &[u8] = b"5627"; + map.save(&mut store, pk, &data1).unwrap(); + + let data2 = Data { + name: "Juan".to_string(), + age: 13, + }; + let pk: &[u8] = b"5628"; + map.save(&mut store, pk, &data2).unwrap(); + + let data3 = Data { + name: "Maria".to_string(), + age: 24, + }; + let pk: &[u8] = b"5629"; + map.save(&mut store, pk, &data3).unwrap(); + + let data4 = Data { + name: "Maria Luisa".to_string(), + age: 43, + }; + let pk: &[u8] = b"5630"; + map.save(&mut store, pk, &data4).unwrap(); + + // a duplication + let pk: &[u8] = b"5631"; + map.save(&mut store, pk, &data3).unwrap(); + + let marias: StdResult> = map + .idx + .name_age + // .prefix(&index_tuple("Maria", 24)) + // .prefix(&index_tuple("", 0)) + .prefix(b"") + // .range(&store, None, None, Order::Ascending) + .range( + &store, + Some(Bound::Inclusive(index_tuple("Maria", 24))), + None, + Order::Ascending, + ) + .collect(); + let count = marias.unwrap().len(); + assert_eq!(2, count); + } + #[test] fn unique_index_enforced() { let mut store = MockStorage::new(); From e534d4f05a2f8e696251711bf39960c337958f2d Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Mon, 21 Dec 2020 23:27:23 +0100 Subject: [PATCH 08/27] Add save_raw() / remove_raw() methods to Map --- packages/storage-plus/src/map.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/storage-plus/src/map.rs b/packages/storage-plus/src/map.rs index 608dd1bdf..441e3a5ad 100644 --- a/packages/storage-plus/src/map.rs +++ b/packages/storage-plus/src/map.rs @@ -46,10 +46,20 @@ where self.key(k).save(store, data) } + pub fn save_raw(&self, store: &mut dyn Storage, k: &[u8], data: &T) -> StdResult<()> { + let path = Path::::new(self.namespace, &[k]); + path.save(store, data) + } + pub fn remove(&self, store: &mut dyn Storage, k: K) { self.key(k).remove(store) } + pub fn remove_raw(&self, store: &mut dyn Storage, k: &[u8]) { + let path = Path::::new(self.namespace, &[k]); + path.remove(store) + } + /// load will return an error if no data is set at the given key, or on parse error pub fn load(&self, store: &dyn Storage, k: K) -> StdResult { self.key(k).load(store) From d3c18d3be3ac03490689e289e318df7f322db42d Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Mon, 21 Dec 2020 23:33:39 +0100 Subject: [PATCH 09/27] Make range() work by using save_raw() --- packages/storage-plus/src/indexes.rs | 111 +++++++++++++-------------- 1 file changed, 55 insertions(+), 56 deletions(-) diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index d3f2fdedf..f89aeea29 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize}; use cosmwasm_std::{Binary, Order, StdError, StdResult, Storage, KV}; +use crate::iter_helpers::to_length_prefixed; use crate::keys::{EmptyPrefix, Prefixer}; use crate::map::Map; use crate::prefix::range_with_prefix; @@ -20,10 +21,11 @@ pub fn index_string(data: &str) -> Vec { data.as_bytes().to_vec() } +// FIXME: Return tuple, and handle length-prefixed strings internally pub fn index_tuple(name: &str, age: u32) -> Vec { - let mut name = name.as_bytes().to_vec(); - name.extend_from_slice(&age.to_be_bytes()); - name + let mut name_age = to_length_prefixed(name.as_bytes()); + name_age.extend_from_slice(&to_length_prefixed(&age.to_be_bytes())); + name_age } // Look at https://docs.rs/endiannezz/0.4.1/endiannezz/trait.Primitive.html @@ -51,7 +53,7 @@ where pub struct MultiIndex<'a, K, T> { index: fn(&T) -> Vec, idx_namespace: &'a [u8], - idx_map: Map<'a, (&'a [u8], &'a [u8]), T>, + idx_map: Map<'a, K, T>, // note, we collapse the pk - combining everything under the namespace - even if it is composite pk_map: Map<'a, &'a [u8], T>, idx_type: PhantomData, @@ -76,13 +78,16 @@ where K: PrimaryKey<'a>, { fn save(&self, store: &mut dyn Storage, pk: &[u8], data: &T) -> StdResult<()> { - let idx = (self.index)(data); - self.idx_map.save(store, (&idx, &pk), &data) + let mut idx = (self.index)(data); + // self.idx_map.save(store, (&idx, &pk), &data) + idx.extend_from_slice(pk); + self.idx_map.save_raw(store, &idx, &data) } fn remove(&self, store: &mut dyn Storage, pk: &[u8], old_data: &T) -> StdResult<()> { - let idx = (self.index)(old_data); - self.idx_map.remove(store, (&idx, &pk)); + let mut idx = (self.index)(old_data); + idx.extend_from_slice(pk); + self.idx_map.remove_raw(store, &idx); Ok(()) } } @@ -92,54 +97,55 @@ where T: Serialize + DeserializeOwned + Clone, K: PrimaryKey<'a>, { - pub fn pks<'c>( - &self, - store: &'c dyn Storage, - idx: &[u8], - min: Option, - max: Option, - order: Order, - ) -> Box> + 'c> { - let prefix = self.idx_map.prefix(idx); - let mapped = range_with_prefix(store, &prefix, min, max, order).map(|(k, _)| k); - Box::new(mapped) - } + // FIXME: Re-introduce, or replace these by range() + // pub fn pks<'c>( + // &self, + // store: &'c dyn Storage, + // idx: &[u8], + // min: Option, + // max: Option, + // order: Order, + // ) -> Box> + 'c> { + // let prefix = self.idx_map.prefix(idx); + // let mapped = range_with_prefix(store, &prefix, min, max, order).map(|(k, _)| k); + // Box::new(mapped) + // } /// returns all items that match this secondary index, always by pk Ascending - pub fn items<'c>( - &'c self, - store: &'c dyn Storage, - idx: &[u8], - min: Option, - max: Option, - order: Order, - ) -> Box>> + 'c> { - let mapped = self.pks(store, idx, min, max, order).map(move |pk| { - let v = self.pk_map.load(store, &pk)?; - Ok((pk, v)) - }); - Box::new(mapped) - } + // pub fn items<'c>( + // &'c self, + // store: &'c dyn Storage, + // idx: &[u8], + // min: Option, + // max: Option, + // order: Order, + // ) -> Box>> + 'c> { + // let mapped = self.pks(store, idx, min, max, order).map(move |pk| { + // let v = self.pk_map.load(store, &pk)?; + // Ok((pk, v)) + // }); + // Box::new(mapped) + // } pub fn prefix(&self, p: K::Prefix) -> Prefix { Prefix::new(self.idx_namespace, &p.prefix()) } - #[cfg(test)] - pub fn count<'c>(&self, store: &'c dyn Storage, idx: &[u8]) -> usize { - self.pks(store, idx, None, None, Order::Ascending).count() - } - - #[cfg(test)] - pub fn all_pks<'c>(&self, store: &'c dyn Storage, idx: &[u8]) -> Vec> { - self.pks(store, idx, None, None, Order::Ascending).collect() - } - - #[cfg(test)] - pub fn all_items<'c>(&self, store: &'c dyn Storage, idx: &[u8]) -> StdResult>> { - self.items(store, idx, None, None, Order::Ascending) - .collect() - } + // #[cfg(test)] + // pub fn count<'c>(&self, store: &'c dyn Storage, idx: &[u8]) -> usize { + // self.pks(store, idx, None, None, Order::Ascending).count() + // } + + // #[cfg(test)] + // pub fn all_pks<'c>(&self, store: &'c dyn Storage, idx: &[u8]) -> Vec> { + // self.pks(store, idx, None, None, Order::Ascending).collect() + // } + + // #[cfg(test)] + // pub fn all_items<'c>(&self, store: &'c dyn Storage, idx: &[u8]) -> StdResult>> { + // self.items(store, idx, None, None, Order::Ascending) + // .collect() + // } } // short-cut for simple keys, rather than .prefix(()).range(...) @@ -162,13 +168,6 @@ where T: 'c, { self.prefix(K::Prefix::new()).range(store, min, max, order) - // let mapped = self.prefix(K::Prefix::new()).range(store, min, max, order).map(move |res| { - // let kv = res?; - // let pk = kv.0; - // let v = self.pk_map.load(store, &pk)?; - // Ok((pk, v)) - // }); - // Box::new(mapped) } } From b9a9a999f5e3c35b07b2b1d33d2c1adaebe2f21c Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Mon, 21 Dec 2020 23:35:43 +0100 Subject: [PATCH 10/27] Fix simple and composite key range() tests --- packages/storage-plus/src/indexed_map.rs | 70 ++++++++++++++---------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index b966cfe80..febbb4648 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -204,6 +204,7 @@ mod test { IndexedMap::new("data", indexes) } + /* #[test] fn store_and_load_by_index() { let mut store = MockStorage::new(); @@ -302,6 +303,7 @@ mod test { let aged = map.idx.age.item(&store, &too_old).unwrap(); assert_eq!(None, aged); } + */ #[test] fn range_simple_key_by_multi_index() { @@ -337,18 +339,24 @@ mod test { let pk: &[u8] = b"5630"; map.save(&mut store, pk, &data4).unwrap(); - let marias: StdResult> = map + let marias: Vec<_> = map .idx .name .range( &store, - Some(Bound::Inclusive(to_length_prefixed(b"Maria"))), + Some(Bound::Inclusive("Maria".into())), None, - Order::Ascending, + Order::Descending, ) - .collect(); - let count = marias.unwrap().len(); + .collect::>() + .unwrap(); + let count = marias.len(); assert_eq!(3, count); + + // Sorted by age ascending + assert_eq!(marias[0].1, data3); + assert_eq!(marias[1].1, data1); + assert_eq!(marias[2].1, data4); } #[test] @@ -365,50 +373,50 @@ mod test { name: "Maria".to_string(), age: 42, }; - let pk: &[u8] = b"5627"; - map.save(&mut store, pk, &data1).unwrap(); + let pk1: &[u8] = b"5627"; + map.save(&mut store, pk1, &data1).unwrap(); let data2 = Data { name: "Juan".to_string(), age: 13, }; - let pk: &[u8] = b"5628"; - map.save(&mut store, pk, &data2).unwrap(); + let pk2: &[u8] = b"5628"; + map.save(&mut store, pk2, &data2).unwrap(); let data3 = Data { name: "Maria".to_string(), age: 24, }; - let pk: &[u8] = b"5629"; - map.save(&mut store, pk, &data3).unwrap(); + let pk3: &[u8] = b"5629"; + map.save(&mut store, pk3, &data3).unwrap(); let data4 = Data { name: "Maria Luisa".to_string(), age: 43, }; - let pk: &[u8] = b"5630"; - map.save(&mut store, pk, &data4).unwrap(); - - // a duplication - let pk: &[u8] = b"5631"; - map.save(&mut store, pk, &data3).unwrap(); + let pk4: &[u8] = b"5630"; + map.save(&mut store, pk4, &data4).unwrap(); - let marias: StdResult> = map + let marias: Vec<_> = map .idx .name_age - // .prefix(&index_tuple("Maria", 24)) - // .prefix(&index_tuple("", 0)) - .prefix(b"") - // .range(&store, None, None, Order::Ascending) - .range( - &store, - Some(Bound::Inclusive(index_tuple("Maria", 24))), - None, - Order::Ascending, - ) - .collect(); - let count = marias.unwrap().len(); + .prefix(b"Maria") + .range(&store, None, None, Order::Descending) + .collect::>() + .unwrap(); + let count = marias.len(); assert_eq!(2, count); + + // Sorted by age descending + assert_eq!(data1, marias[0].1); + assert_eq!(data3, marias[1].1); + + // FIXME! The rest of the key is a mess + let key_size = marias[0].0.len(); + let pk_size = pk1.len(); + let offset = key_size - pk_size; + assert_eq!(pk1, &marias[0].0[offset..]); + assert_eq!(pk3, &marias[1].0[offset..]); } #[test] @@ -466,6 +474,7 @@ mod test { assert_eq!(v.age, 42); } + /* #[test] fn remove_and_update_reflected_on_indexes() { let mut store = MockStorage::new(); @@ -522,4 +531,5 @@ mod test { assert_eq!(name_count(&map, &store, "Fred"), 0); assert_eq!(name_count(&map, &store, "Mary"), 1); } + */ } From 5495ca210cd8beb1ed9121b89a98793d6581192a Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Mon, 21 Dec 2020 23:37:01 +0100 Subject: [PATCH 11/27] WIP: Comment tokens by owner query --- contracts/cw721-base/src/contract.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/contracts/cw721-base/src/contract.rs b/contracts/cw721-base/src/contract.rs index c166c7cec..ba244bff1 100644 --- a/contracts/cw721-base/src/contract.rs +++ b/contracts/cw721-base/src/contract.rs @@ -497,14 +497,16 @@ fn query_tokens( let start = start_after.map(Bound::exclusive); let owner_raw = deps.api.canonical_address(&owner)?; - let tokens: Result, _> = tokens() - .idx - .owner - .pks(deps.storage, &owner_raw, start, None, Order::Ascending) - .take(limit) - .map(String::from_utf8) - .collect(); - let tokens = tokens.map_err(StdError::invalid_utf8)?; + // FIXME! Modify the MultiMap to use range() + // let tokens: Result, _> = tokens() + // .idx + // .owner + // .pks(deps.storage, &owner_raw, start, None, Order::Ascending) + // .take(limit) + // .map(String::from_utf8) + // .collect(); + // let tokens = tokens.map_err(StdError::invalid_utf8)?; + let tokens = vec![]; Ok(TokensResponse { tokens }) } From a5d0b623e3017158a159035ad658640be2a79211 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 22 Dec 2020 11:02:17 +0100 Subject: [PATCH 12/27] WIP: Replace pks() by range() query --- contracts/cw721-base/src/contract.rs | 23 +++++++++++++---------- contracts/cw721-base/src/state.rs | 11 +++++++++-- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/contracts/cw721-base/src/contract.rs b/contracts/cw721-base/src/contract.rs index ba244bff1..4be9645bd 100644 --- a/contracts/cw721-base/src/contract.rs +++ b/contracts/cw721-base/src/contract.rs @@ -497,16 +497,19 @@ fn query_tokens( let start = start_after.map(Bound::exclusive); let owner_raw = deps.api.canonical_address(&owner)?; - // FIXME! Modify the MultiMap to use range() - // let tokens: Result, _> = tokens() - // .idx - // .owner - // .pks(deps.storage, &owner_raw, start, None, Order::Ascending) - // .take(limit) - // .map(String::from_utf8) - // .collect(); - // let tokens = tokens.map_err(StdError::invalid_utf8)?; - let tokens = vec![]; + let res: Result, _> = tokens() + .idx + .owner + .prefix(&owner_raw) + .range(deps.storage, start, None, Order::Ascending) + .take(limit) + .collect(); + let tuples: Vec<_> = res?; + let res: Result, _> = tuples + .iter() + .map(|t| String::from_utf8(t.0.clone())) + .collect(); + let tokens = res.map_err(StdError::invalid_utf8)?; Ok(TokensResponse { tokens }) } diff --git a/contracts/cw721-base/src/state.rs b/contracts/cw721-base/src/state.rs index 35c6add69..eab449a59 100644 --- a/contracts/cw721-base/src/state.rs +++ b/contracts/cw721-base/src/state.rs @@ -46,7 +46,9 @@ pub fn increment_tokens(storage: &mut dyn Storage) -> StdResult { } pub struct TokenIndexes<'a> { - pub owner: MultiIndex<'a, &'a [u8], TokenInfo>, + // Use tuple here, so pk goes to second tuple element, and range() works, + // provided that index_fn result is length prefixed + pub owner: MultiIndex<'a, (&'a [u8], &'a [u8]), TokenInfo>, } impl<'a> IndexList for TokenIndexes<'a> { @@ -58,7 +60,12 @@ impl<'a> IndexList for TokenIndexes<'a> { pub fn tokens<'a>() -> IndexedMap<'a, &'a str, TokenInfo, TokenIndexes<'a>> { let indexes = TokenIndexes { - owner: MultiIndex::new(|d| d.owner.to_vec(), "tokens", "tokens__owner"), + // FIXME: Publish and use to_length_prefixed + owner: MultiIndex::new( + |d| [b"\x00", &[d.owner.len() as u8], &*d.owner].concat(), + "tokens", + "tokens__owner", + ), }; IndexedMap::new("tokens", indexes) } From 1cf8f70c8744ffe649940bf790fd2526761ef30c Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 22 Dec 2020 12:00:21 +0100 Subject: [PATCH 13/27] Fix pks(), combine with Prefix --- contracts/cw721-base/src/contract.rs | 18 +++++++-------- packages/storage-plus/src/indexed_map.rs | 2 +- packages/storage-plus/src/indexes.rs | 28 +++++++++++++----------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/contracts/cw721-base/src/contract.rs b/contracts/cw721-base/src/contract.rs index 4be9645bd..4a6d6c585 100644 --- a/contracts/cw721-base/src/contract.rs +++ b/contracts/cw721-base/src/contract.rs @@ -15,7 +15,7 @@ use crate::msg::{HandleMsg, InitMsg, MintMsg, MinterResponse, QueryMsg}; use crate::state::{ increment_tokens, num_tokens, tokens, Approval, TokenInfo, CONTRACT_INFO, MINTER, OPERATORS, }; -use cw_storage_plus::Bound; +use cw_storage_plus::{Bound, Prefix}; // version info for migration info const CONTRACT_NAME: &str = "crates.io:cw721-base"; @@ -497,19 +497,17 @@ fn query_tokens( let start = start_after.map(Bound::exclusive); let owner_raw = deps.api.canonical_address(&owner)?; - let res: Result, _> = tokens() + // Build prefix + let prefix = tokens().idx.owner.prefix(&owner_raw); + // Pass prefix to pks + let tokens: Result, _> = tokens() .idx .owner - .prefix(&owner_raw) - .range(deps.storage, start, None, Order::Ascending) + .pks(deps.storage, prefix, start, None, Order::Ascending) .take(limit) + .map(String::from_utf8) .collect(); - let tuples: Vec<_> = res?; - let res: Result, _> = tuples - .iter() - .map(|t| String::from_utf8(t.0.clone())) - .collect(); - let tokens = res.map_err(StdError::invalid_utf8)?; + let tokens = tokens.map_err(StdError::invalid_utf8)?; Ok(TokensResponse { tokens }) } diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index febbb4648..23ffc2c04 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -160,7 +160,7 @@ mod test { use crate::iter_helpers::to_length_prefixed; use crate::U32Key; use cosmwasm_std::testing::MockStorage; - use cosmwasm_std::{MemoryStorage, Order}; + use cosmwasm_std::Order; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index f89aeea29..4b9ecb824 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -24,6 +24,7 @@ pub fn index_string(data: &str) -> Vec { // FIXME: Return tuple, and handle length-prefixed strings internally pub fn index_tuple(name: &str, age: u32) -> Vec { let mut name_age = to_length_prefixed(name.as_bytes()); + // name_age.extend_from_slice(&age.to_be_bytes()); name_age.extend_from_slice(&to_length_prefixed(&age.to_be_bytes())); name_age } @@ -97,19 +98,20 @@ where T: Serialize + DeserializeOwned + Clone, K: PrimaryKey<'a>, { - // FIXME: Re-introduce, or replace these by range() - // pub fn pks<'c>( - // &self, - // store: &'c dyn Storage, - // idx: &[u8], - // min: Option, - // max: Option, - // order: Order, - // ) -> Box> + 'c> { - // let prefix = self.idx_map.prefix(idx); - // let mapped = range_with_prefix(store, &prefix, min, max, order).map(|(k, _)| k); - // Box::new(mapped) - // } + // FIXME?: Use range() syntax / return values + // FIXME?: Move to Prefix for ergonomics + // FIXME: Add pk recovery from composite keys + pub fn pks<'c>( + &self, + store: &'c dyn Storage, + prefix: Prefix, + min: Option, + max: Option, + order: Order, + ) -> Box> + 'c> { + let mapped = range_with_prefix(store, &prefix, min, max, order).map(|(k, _)| k); + Box::new(mapped) + } /// returns all items that match this secondary index, always by pk Ascending // pub fn items<'c>( From 8705d20111546bcf9b19e693d9623bfd36afe204 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Thu, 24 Dec 2020 12:17:23 +0100 Subject: [PATCH 14/27] Implement PrimaryKey for generic (T, U, V) triplet --- packages/storage-plus/src/keys.rs | 43 +++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/packages/storage-plus/src/keys.rs b/packages/storage-plus/src/keys.rs index d38be12e4..a84c66a1f 100644 --- a/packages/storage-plus/src/keys.rs +++ b/packages/storage-plus/src/keys.rs @@ -26,6 +26,7 @@ pub trait PrimaryKey<'a>: Clone { type Pk0 = (); type Pk1<'a> = &'a [u8]; type Pk2<'a, T = &'a [u8], U = &'a [u8]> = (T, U); +type Pk3<'a, T = &'a [u8], U = &'a [u8], V = &'a [u8]> = (T, U, V); type PkStr<'a> = &'a str; @@ -74,6 +75,33 @@ impl<'a, T: PrimaryKey<'a> + Prefixer<'a>, U: PrimaryKey<'a>> PrimaryKey<'a> for } } +// use generics for combining there - so we can use &[u8], PkOwned, or IntKey +impl<'a, T: PrimaryKey<'a> + Prefixer<'a>, U: PrimaryKey<'a> + Prefixer<'a>, V: PrimaryKey<'a>> + PrimaryKey<'a> for (T, U, V) +{ + type Prefix = T; + + fn key(&self) -> Vec<&[u8]> { + let mut keys = self.0.key(); + keys.extend(&self.1.key()); + keys.extend(&self.2.key()); + keys + } + + fn parse_key(serialized: &'a [u8]) -> Self { + let l1 = decode_length(&serialized[0..2]); + let first = &serialized[2..2 + l1]; + let l2 = decode_length(&serialized[2 + l1..2 + l1 + 2]); + let second = &serialized[2 + l1 + 2..2 + l1 + 2 + l2]; + let third = &serialized[2 + l1 + 2 + l2..]; + ( + T::parse_key(first), + U::parse_key(second), + V::parse_key(third), + ) + } +} + // pub trait Prefixer<'a>: Copy { pub trait Prefixer<'a> { /// returns 0 or more namespaces that should length-prefixed and concatenated for range searches @@ -98,6 +126,12 @@ impl<'a> Prefixer<'a> for Pk2<'a> { } } +impl<'a> Prefixer<'a> for Pk3<'a> { + fn prefix(&self) -> Vec<&[u8]> { + vec![self.0, self.1, self.2] + } +} + // Provide a string version of this to raw encode strings impl<'a> Prefixer<'a> for PkStr<'a> { fn prefix<'b>(&'b self) -> Vec<&'b [u8]> { @@ -313,6 +347,15 @@ mod test { assert_eq!(key, parsed); } + #[test] + fn parse_joined_keys_pk3() { + let key: Pk3 = (b"four", b"square", b"cinco"); + let joined = key.joined_key(); + assert_eq!(4 + 6 + 5 + 2 * (3 - 1), joined.len()); + let parsed = Pk3::parse_key(&joined); + assert_eq!(key, parsed); + } + #[test] fn parse_joined_keys_int() { let key: U64Key = 12345678.into(); From aa100da3ec0072c7e159df23c19ec9a60c0c8d92 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Thu, 24 Dec 2020 18:10:25 +0100 Subject: [PATCH 15/27] Implement generic index function return values for MultiIndex Include pk as last element of the K --- packages/storage-plus/src/indexed_map.rs | 47 +++++++++++++++--------- packages/storage-plus/src/indexes.rs | 38 +++++++++---------- packages/storage-plus/src/lib.rs | 4 +- 3 files changed, 52 insertions(+), 37 deletions(-) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index 23ffc2c04..d70086686 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -157,8 +157,7 @@ mod test { use super::*; use crate::indexes::{index_int, index_string, index_tuple, MultiIndex, UniqueIndex}; - use crate::iter_helpers::to_length_prefixed; - use crate::U32Key; + use crate::{index_triple, PkOwned, U32Key}; use cosmwasm_std::testing::MockStorage; use cosmwasm_std::Order; use serde::{Deserialize, Serialize}; @@ -170,7 +169,8 @@ mod test { } struct DataIndexes<'a> { - pub name: MultiIndex<'a, &'a [u8], Data>, + // Second arg is for storing pk + pub name: MultiIndex<'a, (PkOwned, PkOwned), Data>, pub age: UniqueIndex<'a, Data>, } @@ -184,7 +184,8 @@ mod test { // For composite multi index tests struct DataCompositeMultiIndex<'a> { - pub name_age: MultiIndex<'a, (&'a [u8], U32Key), Data>, + // Third arg needed for storing pk + pub name_age: MultiIndex<'a, (PkOwned, U32Key, PkOwned), Data>, } // Future Note: this can likely be macro-derived @@ -198,7 +199,11 @@ mod test { // Can we make it easier to define this? (less wordy generic) fn build_map<'a>() -> IndexedMap<'a, &'a [u8], Data, DataIndexes<'a>> { let indexes = DataIndexes { - name: MultiIndex::new(|d| index_string(&d.name), "data", "data__name"), + name: MultiIndex::new( + |d, k| (PkOwned(d.name.as_bytes().to_vec()), PkOwned(k)), + "data", + "data__name", + ), age: UniqueIndex::new(|d| index_int(d.age), "data__age"), }; IndexedMap::new("data", indexes) @@ -342,21 +347,19 @@ mod test { let marias: Vec<_> = map .idx .name - .range( - &store, - Some(Bound::Inclusive("Maria".into())), - None, - Order::Descending, - ) + .prefix(PkOwned(b"Maria".to_vec())) + .range(&store, None, None, Order::Descending) .collect::>() .unwrap(); let count = marias.len(); - assert_eq!(3, count); + assert_eq!(2, count); - // Sorted by age ascending + // Sorted by (descending) pk + assert_eq!(marias[0].0, b"5629"); + assert_eq!(marias[1].0, b"5627"); + // Data is correct assert_eq!(marias[0].1, data3); assert_eq!(marias[1].1, data1); - assert_eq!(marias[2].1, data4); } #[test] @@ -364,7 +367,11 @@ mod test { let mut store = MockStorage::new(); let indexes = DataCompositeMultiIndex { - name_age: MultiIndex::new(|d| index_tuple(&d.name, d.age), "data", "data__name_age"), + name_age: MultiIndex::new( + |d, k| index_triple(&d.name, d.age, k), + "data", + "data__name_age", + ), }; let mut map = IndexedMap::new("data", indexes); @@ -400,7 +407,7 @@ mod test { let marias: Vec<_> = map .idx .name_age - .prefix(b"Maria") + .prefix(PkOwned(b"Maria".to_vec())) .range(&store, None, None, Order::Descending) .collect::>() .unwrap(); @@ -411,10 +418,16 @@ mod test { assert_eq!(data1, marias[0].1); assert_eq!(data3, marias[1].1); - // FIXME! The rest of the key is a mess + // The rest of the key is a mess, but can be parsed let key_size = marias[0].0.len(); let pk_size = pk1.len(); let offset = key_size - pk_size; + + // (encoded) ages + assert_eq!(42u32.to_be_bytes(), &marias[0].0[2..offset]); + assert_eq!(24u32.to_be_bytes(), &marias[1].0[2..offset]); + + // pks assert_eq!(pk1, &marias[0].0[offset..]); assert_eq!(pk3, &marias[1].0[offset..]); } diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index 4b9ecb824..e6f970946 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -6,27 +6,26 @@ use serde::{Deserialize, Serialize}; use cosmwasm_std::{Binary, Order, StdError, StdResult, Storage, KV}; -use crate::iter_helpers::to_length_prefixed; use crate::keys::{EmptyPrefix, Prefixer}; use crate::map::Map; use crate::prefix::range_with_prefix; -use crate::{Bound, Endian, Prefix, PrimaryKey}; +use crate::{Bound, Endian, PkOwned, Prefix, PrimaryKey, U32Key}; use std::marker::PhantomData; /// MARKER is stored in the multi-index as value, but we only look at the key (which is pk) // FIXME: Re-introduce this for MultiIndex // const MARKER: bool = true; -pub fn index_string(data: &str) -> Vec { - data.as_bytes().to_vec() +pub fn index_string(data: &str) -> PkOwned { + PkOwned(data.as_bytes().to_vec()) } -// FIXME: Return tuple, and handle length-prefixed strings internally -pub fn index_tuple(name: &str, age: u32) -> Vec { - let mut name_age = to_length_prefixed(name.as_bytes()); - // name_age.extend_from_slice(&age.to_be_bytes()); - name_age.extend_from_slice(&to_length_prefixed(&age.to_be_bytes())); - name_age +pub fn index_tuple(name: &str, age: u32) -> (PkOwned, U32Key) { + (index_string(name), U32Key::new(age)) +} + +pub fn index_triple(name: &str, age: u32, pk: Vec) -> (PkOwned, U32Key, PkOwned) { + (index_string(name), U32Key::new(age), PkOwned(pk)) } // Look at https://docs.rs/endiannezz/0.4.1/endiannezz/trait.Primitive.html @@ -52,7 +51,7 @@ where } pub struct MultiIndex<'a, K, T> { - index: fn(&T) -> Vec, + index: fn(&T, Vec) -> K, idx_namespace: &'a [u8], idx_map: Map<'a, K, T>, // note, we collapse the pk - combining everything under the namespace - even if it is composite @@ -62,7 +61,11 @@ pub struct MultiIndex<'a, K, T> { impl<'a, K, T> MultiIndex<'a, K, T> { // TODO: make this a const fn - pub fn new(idx_fn: fn(&T) -> Vec, pk_namespace: &'a str, idx_namespace: &'a str) -> Self { + pub fn new( + idx_fn: fn(&T, Vec) -> K, + pk_namespace: &'a str, + idx_namespace: &'a str, + ) -> Self { MultiIndex { index: idx_fn, pk_map: Map::new(pk_namespace), @@ -79,16 +82,13 @@ where K: PrimaryKey<'a>, { fn save(&self, store: &mut dyn Storage, pk: &[u8], data: &T) -> StdResult<()> { - let mut idx = (self.index)(data); - // self.idx_map.save(store, (&idx, &pk), &data) - idx.extend_from_slice(pk); - self.idx_map.save_raw(store, &idx, &data) + let idx = (self.index)(data, pk.to_vec()); + self.idx_map.save(store, idx, &data) } fn remove(&self, store: &mut dyn Storage, pk: &[u8], old_data: &T) -> StdResult<()> { - let mut idx = (self.index)(old_data); - idx.extend_from_slice(pk); - self.idx_map.remove_raw(store, &idx); + let idx = (self.index)(old_data, pk.to_vec()); + self.idx_map.remove(store, idx); Ok(()) } } diff --git a/packages/storage-plus/src/lib.rs b/packages/storage-plus/src/lib.rs index 33d281072..ed2ee66f1 100644 --- a/packages/storage-plus/src/lib.rs +++ b/packages/storage-plus/src/lib.rs @@ -14,7 +14,9 @@ pub use endian::Endian; #[cfg(feature = "iterator")] pub use indexed_map::{IndexList, IndexedMap}; #[cfg(feature = "iterator")] -pub use indexes::{index_int, index_string, index_tuple, Index, MultiIndex, UniqueIndex}; +pub use indexes::{ + index_int, index_string, index_triple, index_tuple, Index, MultiIndex, UniqueIndex, +}; pub use item::Item; pub use keys::{PkOwned, Prefixer, PrimaryKey, U128Key, U16Key, U32Key, U64Key, U8Key}; pub use map::Map; From 9d0bd824771aac1b78b993f27433b29cc2624afa Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Thu, 24 Dec 2020 20:34:38 +0100 Subject: [PATCH 16/27] Fix pks in cw721-base Uses pk as last element of K --- contracts/cw721-base/src/contract.rs | 7 +++++-- contracts/cw721-base/src/state.rs | 11 ++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/contracts/cw721-base/src/contract.rs b/contracts/cw721-base/src/contract.rs index c166c7cec..fb5e1c07a 100644 --- a/contracts/cw721-base/src/contract.rs +++ b/contracts/cw721-base/src/contract.rs @@ -15,7 +15,7 @@ use crate::msg::{HandleMsg, InitMsg, MintMsg, MinterResponse, QueryMsg}; use crate::state::{ increment_tokens, num_tokens, tokens, Approval, TokenInfo, CONTRACT_INFO, MINTER, OPERATORS, }; -use cw_storage_plus::Bound; +use cw_storage_plus::{Bound, PkOwned}; // version info for migration info const CONTRACT_NAME: &str = "crates.io:cw721-base"; @@ -497,10 +497,13 @@ fn query_tokens( let start = start_after.map(Bound::exclusive); let owner_raw = deps.api.canonical_address(&owner)?; + // Build prefix + let prefix = tokens().idx.owner.prefix(PkOwned(owner_raw.into())); + // Pass prefix to pks let tokens: Result, _> = tokens() .idx .owner - .pks(deps.storage, &owner_raw, start, None, Order::Ascending) + .pks(deps.storage, prefix, start, None, Order::Ascending) .take(limit) .map(String::from_utf8) .collect(); diff --git a/contracts/cw721-base/src/state.rs b/contracts/cw721-base/src/state.rs index a18931bb8..fd9910b26 100644 --- a/contracts/cw721-base/src/state.rs +++ b/contracts/cw721-base/src/state.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use cosmwasm_std::{CanonicalAddr, StdResult, Storage}; use cw721::{ContractInfoResponse, Expiration}; -use cw_storage_plus::{Index, IndexList, IndexedMap, Item, Map, MultiIndex}; +use cw_storage_plus::{Index, IndexList, IndexedMap, Item, Map, MultiIndex, PkOwned}; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct TokenInfo { @@ -46,7 +46,8 @@ pub fn increment_tokens(storage: &mut dyn Storage) -> StdResult { } pub struct TokenIndexes<'a> { - pub owner: MultiIndex<'a, TokenInfo>, + // pk goes to second tuple element + pub owner: MultiIndex<'a, (PkOwned, PkOwned), TokenInfo>, } impl<'a> IndexList for TokenIndexes<'a> { @@ -58,7 +59,11 @@ impl<'a> IndexList for TokenIndexes<'a> { pub fn tokens<'a>() -> IndexedMap<'a, &'a str, TokenInfo, TokenIndexes<'a>> { let indexes = TokenIndexes { - owner: MultiIndex::new(|d| d.owner.to_vec(), "tokens", "tokens__owner"), + owner: MultiIndex::new( + |d, k| (PkOwned(d.owner.to_vec()), PkOwned(k)), + "tokens", + "tokens__owner", + ), }; IndexedMap::new("tokens", indexes) } From 7f8897364df04d51d5a55f7cc9df7df233efaa39 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Thu, 24 Dec 2020 20:59:41 +0100 Subject: [PATCH 17/27] Change deserialize_unique_kv access to private --- packages/storage-plus/src/indexes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index 111c69995..5d61094d1 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -223,7 +223,7 @@ where } } -pub(crate) fn deserialize_unique_kv(kv: KV) -> StdResult> { +fn deserialize_unique_kv(kv: KV) -> StdResult> { let (_, v) = kv; let t = from_slice::>(&v)?; Ok((t.pk.into(), t.value)) From d8a4302984e6f20153bc3f192f505da6e88b393a Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Sat, 26 Dec 2020 12:11:34 +0100 Subject: [PATCH 18/27] MultiIndex range() properly returns pk and T --- packages/storage-plus/src/indexed_map.rs | 23 +++------ packages/storage-plus/src/indexes.rs | 59 +++++++++++++++++------- packages/storage-plus/src/prefix.rs | 22 ++++++--- 3 files changed, 65 insertions(+), 39 deletions(-) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index fd3c278d7..46d299cf1 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -156,10 +156,10 @@ where mod test { use super::*; - use crate::indexes::{index_string, index_string_tuple, index_tuple, MultiIndex, UniqueIndex}; + use crate::indexes::{index_string_tuple, MultiIndex, UniqueIndex}; use crate::{index_triple, PkOwned, U32Key}; use cosmwasm_std::testing::MockStorage; - use cosmwasm_std::{MemoryStorage, Order}; + use cosmwasm_std::Order; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] @@ -429,22 +429,13 @@ mod test { let count = marias.len(); assert_eq!(2, count); - // Sorted by age descending + // Pks (sorted by age descending) + assert_eq!(pk1, marias[0].0); + assert_eq!(pk3, marias[1].0); + + // Data assert_eq!(data1, marias[0].1); assert_eq!(data3, marias[1].1); - - // The rest of the key is a mess, but can be parsed - let key_size = marias[0].0.len(); - let pk_size = pk1.len(); - let offset = key_size - pk_size; - - // (encoded) ages - assert_eq!(42u32.to_be_bytes(), &marias[0].0[2..offset]); - assert_eq!(24u32.to_be_bytes(), &marias[1].0[2..offset]); - - // pks - assert_eq!(pk1, &marias[0].0[offset..]); - assert_eq!(pk3, &marias[1].0[offset..]); } #[test] diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index 5d61094d1..c26e98322 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -6,15 +6,11 @@ use serde::{Deserialize, Serialize}; use cosmwasm_std::{from_slice, Binary, Order, StdError, StdResult, Storage, KV}; +use crate::helpers::namespaces_with_key; use crate::keys::EmptyPrefix; use crate::map::Map; use crate::prefix::range_with_prefix; use crate::{Bound, PkOwned, Prefix, Prefixer, PrimaryKey, U32Key}; -use std::marker::PhantomData; - -/// MARKER is stored in the multi-index as value, but we only look at the key (which is pk) -// FIXME: Re-introduce this for MultiIndex -// const MARKER: bool = true; pub fn index_string(data: &str) -> PkOwned { PkOwned(data.as_bytes().to_vec()) @@ -51,13 +47,14 @@ where pub struct MultiIndex<'a, K, T> { index: fn(&T, Vec) -> K, idx_namespace: &'a [u8], - idx_map: Map<'a, K, T>, - // note, we collapse the pk - combining everything under the namespace - even if it is composite - pk_map: Map<'a, &'a [u8], T>, - idx_type: PhantomData, + idx_map: Map<'a, K, u32>, + pk_namespace: &'a [u8], } -impl<'a, K, T> MultiIndex<'a, K, T> { +impl<'a, K, T> MultiIndex<'a, K, T> +where + T: Serialize + DeserializeOwned + Clone, +{ // TODO: make this a const fn pub fn new( idx_fn: fn(&T, Vec) -> K, @@ -66,14 +63,37 @@ impl<'a, K, T> MultiIndex<'a, K, T> { ) -> Self { MultiIndex { index: idx_fn, - pk_map: Map::new(pk_namespace), idx_namespace: idx_namespace.as_bytes(), idx_map: Map::new(idx_namespace), - idx_type: PhantomData, + pk_namespace: pk_namespace.as_bytes(), } } } +fn deserialize_multi_kv( + store: &dyn Storage, + pk_namespace: &[u8], + kv: KV, +) -> StdResult> { + let (key, pk_len) = kv; + + // Deserialize pk_len + let pk_len = from_slice::(pk_len.as_slice())?; + + // Recover pk from last part of k + let offset = key.len() - pk_len as usize; + let pk = &key[offset..]; + + let full_key = namespaces_with_key(&[pk_namespace], pk); + + let v = store + .get(&full_key) + .ok_or_else(|| StdError::generic_err("pk not found"))?; + let v = from_slice::(&v)?; + + Ok((pk.into(), v)) +} + impl<'a, K, T> Index for MultiIndex<'a, K, T> where T: Serialize + DeserializeOwned + Clone, @@ -81,7 +101,7 @@ where { fn save(&self, store: &mut dyn Storage, pk: &[u8], data: &T) -> StdResult<()> { let idx = (self.index)(data, pk.to_vec()); - self.idx_map.save(store, idx, &data) + self.idx_map.save(store, idx, &(pk.len() as u32)) } fn remove(&self, store: &mut dyn Storage, pk: &[u8], old_data: &T) -> StdResult<()> { @@ -98,7 +118,7 @@ where { // FIXME?: Use range() syntax / return values // FIXME?: Move to Prefix for ergonomics - // FIXME: Add pk recovery from composite keys + // FIXME: Recover pk from (last part of) k pub fn pks<'c>( &self, store: &'c dyn Storage, @@ -128,7 +148,12 @@ where // } pub fn prefix(&self, p: K::Prefix) -> Prefix { - Prefix::new(self.idx_namespace, &p.prefix()) + Prefix::new_de_fn( + self.idx_namespace, + &p.prefix(), + self.pk_namespace, + deserialize_multi_kv, + ) } // #[cfg(test)] @@ -235,7 +260,9 @@ where K: PrimaryKey<'a>, { pub fn prefix(&self, p: K::Prefix) -> Prefix { - Prefix::new_de_fn(self.idx_namespace, &p.prefix(), deserialize_unique_kv) + Prefix::new_de_fn(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { + deserialize_unique_kv(kv) + }) } /// returns all items that match this secondary index, always by pk Ascending diff --git a/packages/storage-plus/src/prefix.rs b/packages/storage-plus/src/prefix.rs index 854d29f85..3d181d7d7 100644 --- a/packages/storage-plus/src/prefix.rs +++ b/packages/storage-plus/src/prefix.rs @@ -42,7 +42,9 @@ impl Bound { } } -#[derive(Debug, Clone)] +type DeserializeFn = fn(&dyn Storage, &[u8], KV) -> StdResult>; + +#[derive(Clone)] pub struct Prefix where T: Serialize + DeserializeOwned, @@ -51,7 +53,8 @@ where storage_prefix: Vec, // see https://doc.rust-lang.org/std/marker/struct.PhantomData.html#unused-type-parameters for why this is needed data: PhantomData, - de_fn: fn(KV) -> StdResult>, + pk_name: Vec, + de_fn: DeserializeFn, } impl Deref for Prefix @@ -70,19 +73,21 @@ where T: Serialize + DeserializeOwned, { pub fn new(top_name: &[u8], sub_names: &[&[u8]]) -> Self { - Prefix::new_de_fn(top_name, sub_names, deserialize_kv) + Prefix::new_de_fn(top_name, sub_names, &[], |_, _, kv| deserialize_kv(kv)) } pub fn new_de_fn( top_name: &[u8], sub_names: &[&[u8]], - de_fn: fn(KV) -> StdResult>, + pk_name: &[u8], + de_fn: DeserializeFn, ) -> Self { // FIXME: we can use a custom function here, probably make this cleaner let storage_prefix = nested_namespaces_with_key(&[top_name], sub_names, b""); Prefix { storage_prefix, data: PhantomData, + pk_name: pk_name.to_vec(), de_fn, } } @@ -97,8 +102,10 @@ where where T: 'a, { - let mapped = - range_with_prefix(store, &self.storage_prefix, min, max, order).map(self.de_fn); + let de_fn = self.de_fn; + let pk_name = self.pk_name.clone(); + let mapped = range_with_prefix(store, &self.storage_prefix, min, max, order) + .map(move |kv| (de_fn)(store, &*pk_name, kv)); Box::new(mapped) } } @@ -175,7 +182,8 @@ mod test { let prefix = Prefix { storage_prefix: b"foo".to_vec(), data: PhantomData::, - de_fn: deserialize_kv, + pk_name: vec![], + de_fn: |_, _, kv| deserialize_kv(kv), }; // set some data, we care about "foo" prefix From 4529c566fe0fd671b11e53fb3901a6881f7f51a4 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Sat, 26 Dec 2020 12:35:39 +0100 Subject: [PATCH 19/27] Replace all_items() by prefix() plus range() --- packages/storage-plus/src/indexed_map.rs | 41 ++++++++++++++---------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index 46d299cf1..90c515a81 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -156,8 +156,8 @@ where mod test { use super::*; - use crate::indexes::{index_string_tuple, MultiIndex, UniqueIndex}; - use crate::{index_triple, PkOwned, U32Key}; + use crate::indexes::{index_string_tuple, index_triple, MultiIndex, UniqueIndex}; + use crate::{PkOwned, U32Key}; use cosmwasm_std::testing::MockStorage; use cosmwasm_std::Order; use serde::{Deserialize, Serialize}; @@ -215,7 +215,6 @@ mod test { IndexedMap::new("data", indexes) } - /* #[test] fn store_and_load_by_index() { let mut store = MockStorage::new(); @@ -232,6 +231,7 @@ mod test { let data2 = Data { name: "Juan".to_string(), + last_name: "Perez".to_string(), age: 13, }; let pk2: &[u8] = b"5628"; @@ -239,6 +239,7 @@ mod test { let data3 = Data { name: "Maria".to_string(), + last_name: "Young".to_string(), age: 24, }; let pk3: &[u8] = b"5629"; @@ -246,6 +247,7 @@ mod test { let data4 = Data { name: "Maria Luisa".to_string(), + last_name: "Bemberg".to_string(), age: 12, }; let pk4: &[u8] = b"5630"; @@ -258,19 +260,22 @@ mod test { let count = map .idx .name - .all_items(&store, &index_string("Maria")) - .unwrap() + .prefix(PkOwned(b"Maria".to_vec())) + .range(&store, None, None, Order::Ascending) + .collect::>() .len(); assert_eq!(2, count); // TODO: we load by wrong keys - get full storage key! // load it by secondary index (we must know how to compute this) - // let marias: StdResult> = map - let marias = map + // let marias: Vec<_>> = map + let marias: Vec<_> = map .idx .name - .all_items(&store, &index_string("Maria")) + .prefix(PkOwned(b"Maria".to_vec())) + .range(&store, None, None, Order::Ascending) + .collect::>() .unwrap(); assert_eq!(2, marias.len()); let (k, v) = &marias[0]; @@ -281,8 +286,9 @@ mod test { let count = map .idx .name - .all_items(&store, &index_string("Marib")) - .unwrap() + .prefix(PkOwned(b"Marib".to_vec())) + .range(&store, None, None, Order::Ascending) + .collect::>() .len(); assert_eq!(0, count); @@ -290,8 +296,9 @@ mod test { let count = map .idx .name - .all_items(&store, &index_string("Mari`")) - .unwrap() + .prefix(PkOwned(b"Mari`".to_vec())) + .range(&store, None, None, Order::Ascending) + .collect::>() .len(); assert_eq!(0, count); @@ -299,23 +306,23 @@ mod test { let count = map .idx .name - .all_items(&store, &index_string("Maria5")) - .unwrap() + .prefix(PkOwned(b"Maria5".to_vec())) + .range(&store, None, None, Order::Ascending) + .collect::>() .len(); assert_eq!(0, count); // match on proper age let proper = U32Key::new(42); let aged = map.idx.age.item(&store, proper).unwrap().unwrap(); - assert_eq!(pk.to_vec(), aged.0); - assert_eq!(data, aged.1); + assert_eq!(pk1.to_vec(), aged.0); + assert_eq!(data1, aged.1); // no match on wrong age let too_old = U32Key::new(43); let aged = map.idx.age.item(&store, too_old).unwrap(); assert_eq!(None, aged); } - */ #[test] fn range_simple_key_by_multi_index() { From 2868af401ad1f80368c347642f20e6db560f8fde Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Sat, 26 Dec 2020 18:02:59 +0100 Subject: [PATCH 20/27] Implement pks() in terms of range() Changes pks() return value --- contracts/cw721-base/src/contract.rs | 18 +++-- packages/storage-plus/src/indexed_map.rs | 12 ++-- packages/storage-plus/src/indexes.rs | 89 +++++++++++------------- 3 files changed, 62 insertions(+), 57 deletions(-) diff --git a/contracts/cw721-base/src/contract.rs b/contracts/cw721-base/src/contract.rs index fb5e1c07a..d9cb984bf 100644 --- a/contracts/cw721-base/src/contract.rs +++ b/contracts/cw721-base/src/contract.rs @@ -497,17 +497,23 @@ fn query_tokens( let start = start_after.map(Bound::exclusive); let owner_raw = deps.api.canonical_address(&owner)?; - // Build prefix - let prefix = tokens().idx.owner.prefix(PkOwned(owner_raw.into())); // Pass prefix to pks - let tokens: Result, _> = tokens() + let res: Result, _> = tokens() .idx .owner - .pks(deps.storage, prefix, start, None, Order::Ascending) + .pks( + deps.storage, + PkOwned(owner_raw.into()), + start, + None, + Order::Ascending, + ) .take(limit) - .map(String::from_utf8) .collect(); - let tokens = tokens.map_err(StdError::invalid_utf8)?; + let pks = res?; + + let res: Result, _> = pks.iter().map(|v| String::from_utf8(v.to_vec())).collect(); + let tokens = res.map_err(StdError::invalid_utf8)?; Ok(TokensResponse { tokens }) } diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index 90c515a81..0372c33a8 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -159,7 +159,7 @@ mod test { use crate::indexes::{index_string_tuple, index_triple, MultiIndex, UniqueIndex}; use crate::{PkOwned, U32Key}; use cosmwasm_std::testing::MockStorage; - use cosmwasm_std::Order; + use cosmwasm_std::{MemoryStorage, Order}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] @@ -546,7 +546,6 @@ mod test { map.save(&mut store, pk4, &data4).unwrap_err(); } - /* #[test] fn remove_and_update_reflected_on_indexes() { let mut store = MockStorage::new(); @@ -558,7 +557,13 @@ mod test { -> usize { map.idx .name - .pks(store, &index_string(name), None, None, Order::Ascending) + .pks( + store, + PkOwned(name.as_bytes().to_vec()), + None, + None, + Order::Ascending, + ) .count() }; @@ -606,7 +611,6 @@ mod test { assert_eq!(name_count(&map, &store, "Fred"), 0); assert_eq!(name_count(&map, &store, "Mary"), 1); } - */ #[test] fn unique_index_simple_key_range() { diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index c26e98322..3482098b3 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -9,7 +9,6 @@ use cosmwasm_std::{from_slice, Binary, Order, StdError, StdResult, Storage, KV}; use crate::helpers::namespaces_with_key; use crate::keys::EmptyPrefix; use crate::map::Map; -use crate::prefix::range_with_prefix; use crate::{Bound, PkOwned, Prefix, Prefixer, PrimaryKey, U32Key}; pub fn index_string(data: &str) -> PkOwned { @@ -31,7 +30,7 @@ pub fn index_string_tuple(data1: &str, data2: &str) -> (PkOwned, PkOwned) { // 2 main variants: // * store (namespace, index_name, idx_value, key) -> b"1" - allows many and references pk // * store (namespace, index_name, idx_value) -> {key, value} - allows one and copies pk and data -// // this would be the primary key - we abstract that too??? +// // this would be the primary key // * store (namespace, index_name, pk) -> value - allows one with data // // Note: we cannot store traits with generic functions inside `Box`, @@ -116,61 +115,57 @@ where T: Serialize + DeserializeOwned + Clone, K: PrimaryKey<'a>, { - // FIXME?: Use range() syntax / return values + pub fn prefix(&self, p: K::Prefix) -> Prefix { + Prefix::new_de_fn( + self.idx_namespace, + &p.prefix(), + self.pk_namespace, + deserialize_multi_kv, + ) + } + // FIXME?: Move to Prefix for ergonomics - // FIXME: Recover pk from (last part of) k pub fn pks<'c>( &self, store: &'c dyn Storage, - prefix: Prefix, + p: K::Prefix, min: Option, max: Option, order: Order, - ) -> Box> + 'c> { - let mapped = range_with_prefix(store, &prefix, min, max, order).map(|(k, _)| k); + ) -> Box>> + 'c> + where + T: 'c, + { + let prefix = self.prefix(p); + let mapped = prefix.range(store, min, max, order).map(|res| { + let t = res?; + Ok(t.0) + }); Box::new(mapped) } - /// returns all items that match this secondary index, always by pk Ascending - // pub fn items<'c>( - // &'c self, - // store: &'c dyn Storage, - // idx: &[u8], - // min: Option, - // max: Option, - // order: Order, - // ) -> Box>> + 'c> { - // let mapped = self.pks(store, idx, min, max, order).map(move |pk| { - // let v = self.pk_map.load(store, &pk)?; - // Ok((pk, v)) - // }); - // Box::new(mapped) - // } + #[cfg(test)] + pub fn count<'c>(&self, store: &'c dyn Storage, p: K::Prefix) -> usize { + self.pks(store, p, None, None, Order::Ascending).count() + } - pub fn prefix(&self, p: K::Prefix) -> Prefix { - Prefix::new_de_fn( - self.idx_namespace, - &p.prefix(), - self.pk_namespace, - deserialize_multi_kv, - ) + #[cfg(test)] + pub fn all_pks<'c>(&self, store: &'c dyn Storage, p: K::Prefix) -> Vec> { + self.pks(store, p, None, None, Order::Ascending) + .collect::>>>() + .unwrap() } - // #[cfg(test)] - // pub fn count<'c>(&self, store: &'c dyn Storage, idx: &[u8]) -> usize { - // self.pks(store, idx, None, None, Order::Ascending).count() - // } - - // #[cfg(test)] - // pub fn all_pks<'c>(&self, store: &'c dyn Storage, idx: &[u8]) -> Vec> { - // self.pks(store, idx, None, None, Order::Ascending).collect() - // } - - // #[cfg(test)] - // pub fn all_items<'c>(&self, store: &'c dyn Storage, idx: &[u8]) -> StdResult>> { - // self.items(store, idx, None, None, Order::Ascending) - // .collect() - // } + #[cfg(test)] + pub fn all_items<'c>( + &self, + store: &'c dyn Storage, + prefix: K::Prefix, + ) -> StdResult>> { + self.prefix(prefix) + .range(store, None, None, Order::Ascending) + .collect() + } } // short-cut for simple keys, rather than .prefix(()).range(...) @@ -187,8 +182,8 @@ where store: &'c dyn Storage, min: Option, max: Option, - order: cosmwasm_std::Order, - ) -> Box>> + 'c> + order: Order, + ) -> Box>> + 'c> where T: 'c, { @@ -289,7 +284,7 @@ where store: &'c dyn Storage, min: Option, max: Option, - order: cosmwasm_std::Order, + order: Order, ) -> Box>> + 'c> where T: 'c, From 1ef6fc4a7ecdb1f5f72adccd3cdd6c47dd5d85ea Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Mon, 28 Dec 2020 09:27:15 +0100 Subject: [PATCH 21/27] Remove outdated comment --- contracts/cw721-base/src/contract.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/cw721-base/src/contract.rs b/contracts/cw721-base/src/contract.rs index d9cb984bf..b43b035d6 100644 --- a/contracts/cw721-base/src/contract.rs +++ b/contracts/cw721-base/src/contract.rs @@ -497,7 +497,6 @@ fn query_tokens( let start = start_after.map(Bound::exclusive); let owner_raw = deps.api.canonical_address(&owner)?; - // Pass prefix to pks let res: Result, _> = tokens() .idx .owner From 4bc99a9cb0bd3929d2c7db49f9ed170e468d10a0 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Mon, 28 Dec 2020 09:33:52 +0100 Subject: [PATCH 22/27] Set version: 0.4.1 --- Cargo.lock | 44 ++++++++++++------------- contracts/cw1-subkeys/Cargo.toml | 10 +++--- contracts/cw1-whitelist/Cargo.toml | 8 ++--- contracts/cw20-atomic-swap/Cargo.toml | 8 ++--- contracts/cw20-base/Cargo.toml | 8 ++--- contracts/cw20-bonding/Cargo.toml | 12 +++---- contracts/cw20-escrow/Cargo.toml | 12 +++---- contracts/cw20-staking/Cargo.toml | 12 +++---- contracts/cw3-fixed-multisig/Cargo.toml | 10 +++--- contracts/cw3-flex-multisig/Cargo.toml | 16 ++++----- contracts/cw4-group/Cargo.toml | 12 +++---- contracts/cw4-stake/Cargo.toml | 12 +++---- contracts/cw721-base/Cargo.toml | 10 +++--- packages/controllers/Cargo.toml | 6 ++-- packages/cw0/Cargo.toml | 2 +- packages/cw1/Cargo.toml | 2 +- packages/cw2/Cargo.toml | 4 +-- packages/cw20/Cargo.toml | 4 +-- packages/cw3/Cargo.toml | 4 +-- packages/cw4/Cargo.toml | 2 +- packages/cw721/Cargo.toml | 4 +-- packages/multi-test/Cargo.toml | 4 +-- packages/storage-plus/Cargo.toml | 2 +- 23 files changed, 104 insertions(+), 104 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2380c2828..7918b53ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,7 +80,7 @@ dependencies = [ [[package]] name = "cw-controllers" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cosmwasm-std", "cw-storage-plus", @@ -92,7 +92,7 @@ dependencies = [ [[package]] name = "cw-multi-test" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cosmwasm-std", "cw0", @@ -102,7 +102,7 @@ dependencies = [ [[package]] name = "cw-storage-plus" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cosmwasm-std", "schemars", @@ -111,7 +111,7 @@ dependencies = [ [[package]] name = "cw0" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cosmwasm-std", "schemars", @@ -121,7 +121,7 @@ dependencies = [ [[package]] name = "cw1" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -131,7 +131,7 @@ dependencies = [ [[package]] name = "cw1-subkeys" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -147,7 +147,7 @@ dependencies = [ [[package]] name = "cw1-whitelist" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -162,7 +162,7 @@ dependencies = [ [[package]] name = "cw2" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cosmwasm-std", "cw-storage-plus", @@ -172,7 +172,7 @@ dependencies = [ [[package]] name = "cw20" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -183,7 +183,7 @@ dependencies = [ [[package]] name = "cw20-atomic-swap" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -200,7 +200,7 @@ dependencies = [ [[package]] name = "cw20-base" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -215,7 +215,7 @@ dependencies = [ [[package]] name = "cw20-bonding" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -233,7 +233,7 @@ dependencies = [ [[package]] name = "cw20-escrow" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -250,7 +250,7 @@ dependencies = [ [[package]] name = "cw20-staking" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -267,7 +267,7 @@ dependencies = [ [[package]] name = "cw3" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -278,7 +278,7 @@ dependencies = [ [[package]] name = "cw3-fixed-multisig" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -293,7 +293,7 @@ dependencies = [ [[package]] name = "cw3-flex-multisig" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -311,7 +311,7 @@ dependencies = [ [[package]] name = "cw4" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -321,7 +321,7 @@ dependencies = [ [[package]] name = "cw4-group" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -337,7 +337,7 @@ dependencies = [ [[package]] name = "cw4-stake" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -353,7 +353,7 @@ dependencies = [ [[package]] name = "cw721" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -364,7 +364,7 @@ dependencies = [ [[package]] name = "cw721-base" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", diff --git a/contracts/cw1-subkeys/Cargo.toml b/contracts/cw1-subkeys/Cargo.toml index 163ac1234..9c7714b66 100644 --- a/contracts/cw1-subkeys/Cargo.toml +++ b/contracts/cw1-subkeys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw1-subkeys" -version = "0.4.0" +version = "0.4.1" authors = ["Ethan Frey "] edition = "2018" description = "Implement subkeys for authorizing native tokens as a cw1 proxy contract" @@ -18,10 +18,10 @@ backtraces = ["cosmwasm-std/backtraces"] library = [] [dependencies] -cw0 = { path = "../../packages/cw0", version = "0.4.0" } -cw1 = { path = "../../packages/cw1", version = "0.4.0" } -cw2 = { path = "../../packages/cw2", version = "0.4.0" } -cw1-whitelist = { path = "../cw1-whitelist", version = "0.4.0", features = ["library"] } +cw0 = { path = "../../packages/cw0", version = "0.4.1" } +cw1 = { path = "../../packages/cw1", version = "0.4.1" } +cw2 = { path = "../../packages/cw2", version = "0.4.1" } +cw1-whitelist = { path = "../cw1-whitelist", version = "0.4.1", features = ["library"] } cosmwasm-std = { version = "0.12.2", features = ["iterator", "staking"] } cosmwasm-storage = { version = "0.12.2", features = ["iterator"] } schemars = "0.7" diff --git a/contracts/cw1-whitelist/Cargo.toml b/contracts/cw1-whitelist/Cargo.toml index 9466aef7e..95a15e233 100644 --- a/contracts/cw1-whitelist/Cargo.toml +++ b/contracts/cw1-whitelist/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw1-whitelist" -version = "0.4.0" +version = "0.4.1" authors = ["Ethan Frey "] edition = "2018" description = "Implementation of an proxy contract using a whitelist" @@ -18,9 +18,9 @@ backtraces = ["cosmwasm-std/backtraces"] library = [] [dependencies] -cw0 = { path = "../../packages/cw0", version = "0.4.0" } -cw1 = { path = "../../packages/cw1", version = "0.4.0" } -cw2 = { path = "../../packages/cw2", version = "0.4.0" } +cw0 = { path = "../../packages/cw0", version = "0.4.1" } +cw1 = { path = "../../packages/cw1", version = "0.4.1" } +cw2 = { path = "../../packages/cw2", version = "0.4.1" } cosmwasm-std = { version = "0.12.2", features = ["iterator"] } cosmwasm-storage = { version = "0.12.2", features = ["iterator"] } schemars = "0.7" diff --git a/contracts/cw20-atomic-swap/Cargo.toml b/contracts/cw20-atomic-swap/Cargo.toml index c86000277..55312bc54 100644 --- a/contracts/cw20-atomic-swap/Cargo.toml +++ b/contracts/cw20-atomic-swap/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw20-atomic-swap" -version = "0.4.0" +version = "0.4.1" authors = ["Mauro Lacy "] edition = "2018" description = "Implementation of Atomic Swaps" @@ -15,9 +15,9 @@ backtraces = ["cosmwasm-std/backtraces"] library = [] [dependencies] -cw0 = { path = "../../packages/cw0", version = "0.4.0" } -cw2 = { path = "../../packages/cw2", version = "0.4.0" } -cw20 = { path = "../../packages/cw20", version = "0.4.0" } +cw0 = { path = "../../packages/cw0", version = "0.4.1" } +cw2 = { path = "../../packages/cw2", version = "0.4.1" } +cw20 = { path = "../../packages/cw20", version = "0.4.1" } cosmwasm-std = { version = "0.12.2", features = ["iterator"] } cosmwasm-storage = { version = "0.12.2", features = ["iterator"] } schemars = "0.7" diff --git a/contracts/cw20-base/Cargo.toml b/contracts/cw20-base/Cargo.toml index d9504550b..ce0db6d59 100644 --- a/contracts/cw20-base/Cargo.toml +++ b/contracts/cw20-base/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw20-base" -version = "0.4.0" +version = "0.4.1" authors = ["Ethan Frey "] edition = "2018" description = "Basic implementation of a CosmWasm-20 compliant token" @@ -18,9 +18,9 @@ backtraces = ["cosmwasm-std/backtraces"] library = [] [dependencies] -cw0 = { path = "../../packages/cw0", version = "0.4.0" } -cw2 = { path = "../../packages/cw2", version = "0.4.0" } -cw20 = { path = "../../packages/cw20", version = "0.4.0" } +cw0 = { path = "../../packages/cw0", version = "0.4.1" } +cw2 = { path = "../../packages/cw2", version = "0.4.1" } +cw20 = { path = "../../packages/cw20", version = "0.4.1" } cosmwasm-std = { version = "0.12.2", features = ["iterator"] } cosmwasm-storage = { version = "0.12.2", features = ["iterator"] } schemars = "0.7" diff --git a/contracts/cw20-bonding/Cargo.toml b/contracts/cw20-bonding/Cargo.toml index 354813cc5..b934d3ef9 100644 --- a/contracts/cw20-bonding/Cargo.toml +++ b/contracts/cw20-bonding/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw20-bonding" -version = "0.4.0" +version = "0.4.1" authors = ["Ethan Frey "] edition = "2018" description = "Implement basic bonding curve to issue cw20 tokens" @@ -20,11 +20,11 @@ backtraces = ["cosmwasm-std/backtraces"] library = [] [dependencies] -cw0 = { path = "../../packages/cw0", version = "0.4.0" } -cw2 = { path = "../../packages/cw2", version = "0.4.0" } -cw20 = { path = "../../packages/cw20", version = "0.4.0" } -cw20-base = { path = "../../contracts/cw20-base", version = "0.4.0", features = ["library"] } -cw-storage-plus = { path = "../../packages/storage-plus", version = "0.4.0" } +cw0 = { path = "../../packages/cw0", version = "0.4.1" } +cw2 = { path = "../../packages/cw2", version = "0.4.1" } +cw20 = { path = "../../packages/cw20", version = "0.4.1" } +cw20-base = { path = "../../contracts/cw20-base", version = "0.4.1", features = ["library"] } +cw-storage-plus = { path = "../../packages/storage-plus", version = "0.4.1" } cosmwasm-std = { version = "0.12.2", features = ["staking"] } schemars = "0.7" serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/contracts/cw20-escrow/Cargo.toml b/contracts/cw20-escrow/Cargo.toml index 59a4b8eb3..98d3ffc08 100644 --- a/contracts/cw20-escrow/Cargo.toml +++ b/contracts/cw20-escrow/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw20-escrow" -version = "0.4.0" +version = "0.4.1" authors = ["Ethan Frey "] edition = "2018" description = "Implementation of an escrow that accepts CosmWasm-20 tokens as well as native tokens" @@ -18,9 +18,9 @@ backtraces = ["cosmwasm-std/backtraces"] library = [] [dependencies] -cw0 = { path = "../../packages/cw0", version = "0.4.0" } -cw2 = { path = "../../packages/cw2", version = "0.4.0" } -cw20 = { path = "../../packages/cw20", version = "0.4.0" } +cw0 = { path = "../../packages/cw0", version = "0.4.1" } +cw2 = { path = "../../packages/cw2", version = "0.4.1" } +cw20 = { path = "../../packages/cw20", version = "0.4.1" } cosmwasm-std = { version = "0.12.2", features = ["iterator"] } cosmwasm-storage = { version = "0.12.2", features = ["iterator"] } schemars = "0.7" @@ -29,5 +29,5 @@ thiserror = { version = "1.0.20" } [dev-dependencies] cosmwasm-schema = { version = "0.12.2" } -cw-multi-test = { path = "../../packages/multi-test", version = "0.4.0" } -cw20-base = { path = "../cw20-base", version = "0.4.0", features = ["library"] } +cw-multi-test = { path = "../../packages/multi-test", version = "0.4.1" } +cw20-base = { path = "../cw20-base", version = "0.4.1", features = ["library"] } diff --git a/contracts/cw20-staking/Cargo.toml b/contracts/cw20-staking/Cargo.toml index dbfc1a4fc..eb8fbf9c7 100644 --- a/contracts/cw20-staking/Cargo.toml +++ b/contracts/cw20-staking/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw20-staking" -version = "0.4.0" +version = "0.4.1" authors = ["Ethan Frey "] edition = "2018" description = "Implement simple staking derivatives as a cw20 token" @@ -20,11 +20,11 @@ backtraces = ["cosmwasm-std/backtraces"] library = [] [dependencies] -cw0 = { path = "../../packages/cw0", version = "0.4.0" } -cw2 = { path = "../../packages/cw2", version = "0.4.0" } -cw20 = { path = "../../packages/cw20", version = "0.4.0" } -cw-controllers = { path = "../../packages/controllers", version = "0.4.0" } -cw20-base = { path = "../../contracts/cw20-base", version = "0.4.0", features = ["library"] } +cw0 = { path = "../../packages/cw0", version = "0.4.1" } +cw2 = { path = "../../packages/cw2", version = "0.4.1" } +cw20 = { path = "../../packages/cw20", version = "0.4.1" } +cw-controllers = { path = "../../packages/controllers", version = "0.4.1" } +cw20-base = { path = "../../contracts/cw20-base", version = "0.4.1", features = ["library"] } cosmwasm-std = { version = "0.12.2", features = ["staking"] } cosmwasm-storage = { version = "0.12.2" } schemars = "0.7" diff --git a/contracts/cw3-fixed-multisig/Cargo.toml b/contracts/cw3-fixed-multisig/Cargo.toml index e8e95690a..9e5475a1a 100644 --- a/contracts/cw3-fixed-multisig/Cargo.toml +++ b/contracts/cw3-fixed-multisig/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw3-fixed-multisig" -version = "0.4.0" +version = "0.4.1" authors = ["Ethan Frey "] edition = "2018" description = "Implementing cw3 with an fixed group multisig" @@ -18,10 +18,10 @@ backtraces = ["cosmwasm-std/backtraces"] library = [] [dependencies] -cw0 = { path = "../../packages/cw0", version = "0.4.0" } -cw2 = { path = "../../packages/cw2", version = "0.4.0" } -cw3 = { path = "../../packages/cw3", version = "0.4.0" } -cw-storage-plus = { path = "../../packages/storage-plus", version = "0.4.0", features = ["iterator"] } +cw0 = { path = "../../packages/cw0", version = "0.4.1" } +cw2 = { path = "../../packages/cw2", version = "0.4.1" } +cw3 = { path = "../../packages/cw3", version = "0.4.1" } +cw-storage-plus = { path = "../../packages/storage-plus", version = "0.4.1", features = ["iterator"] } cosmwasm-std = { version = "0.12.2", features = ["iterator"] } schemars = "0.7" serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/contracts/cw3-flex-multisig/Cargo.toml b/contracts/cw3-flex-multisig/Cargo.toml index 8b47d1fd4..6c5a6fc86 100644 --- a/contracts/cw3-flex-multisig/Cargo.toml +++ b/contracts/cw3-flex-multisig/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw3-flex-multisig" -version = "0.4.0" +version = "0.4.1" authors = ["Ethan Frey "] edition = "2018" description = "Implementing cw3 with multiple voting patterns and dynamic groups" @@ -18,11 +18,11 @@ backtraces = ["cosmwasm-std/backtraces"] library = [] [dependencies] -cw0 = { path = "../../packages/cw0", version = "0.4.0" } -cw2 = { path = "../../packages/cw2", version = "0.4.0" } -cw3 = { path = "../../packages/cw3", version = "0.4.0" } -cw4 = { path = "../../packages/cw4", version = "0.4.0" } -cw-storage-plus = { path = "../../packages/storage-plus", version = "0.4.0", features = ["iterator"] } +cw0 = { path = "../../packages/cw0", version = "0.4.1" } +cw2 = { path = "../../packages/cw2", version = "0.4.1" } +cw3 = { path = "../../packages/cw3", version = "0.4.1" } +cw4 = { path = "../../packages/cw4", version = "0.4.1" } +cw-storage-plus = { path = "../../packages/storage-plus", version = "0.4.1", features = ["iterator"] } cosmwasm-std = { version = "0.12.2", features = ["iterator"] } schemars = "0.7" serde = { version = "1.0.103", default-features = false, features = ["derive"] } @@ -30,5 +30,5 @@ thiserror = { version = "1.0.20" } [dev-dependencies] cosmwasm-schema = { version = "0.12.2" } -cw4-group = { path = "../cw4-group", version = "0.4.0" } -cw-multi-test = { path = "../../packages/multi-test", version = "0.4.0" } +cw4-group = { path = "../cw4-group", version = "0.4.1" } +cw-multi-test = { path = "../../packages/multi-test", version = "0.4.1" } diff --git a/contracts/cw4-group/Cargo.toml b/contracts/cw4-group/Cargo.toml index 6ebc6d15a..993975053 100644 --- a/contracts/cw4-group/Cargo.toml +++ b/contracts/cw4-group/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw4-group" -version = "0.4.0" +version = "0.4.1" authors = ["Ethan Frey "] edition = "2018" description = "Simple cw4 implementation of group membership controlled by admin " @@ -26,11 +26,11 @@ backtraces = ["cosmwasm-std/backtraces"] library = [] [dependencies] -cw0 = { path = "../../packages/cw0", version = "0.4.0" } -cw2 = { path = "../../packages/cw2", version = "0.4.0" } -cw4 = { path = "../../packages/cw4", version = "0.4.0" } -cw-controllers = { path = "../../packages/controllers", version = "0.4.0" } -cw-storage-plus = { path = "../../packages/storage-plus", version = "0.4.0", features = ["iterator"] } +cw0 = { path = "../../packages/cw0", version = "0.4.1" } +cw2 = { path = "../../packages/cw2", version = "0.4.1" } +cw4 = { path = "../../packages/cw4", version = "0.4.1" } +cw-controllers = { path = "../../packages/controllers", version = "0.4.1" } +cw-storage-plus = { path = "../../packages/storage-plus", version = "0.4.1", features = ["iterator"] } cosmwasm-std = { version = "0.12.2" } schemars = "0.7" serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/contracts/cw4-stake/Cargo.toml b/contracts/cw4-stake/Cargo.toml index 61c824e7a..4fd5f6eb6 100644 --- a/contracts/cw4-stake/Cargo.toml +++ b/contracts/cw4-stake/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw4-stake" -version = "0.4.0" +version = "0.4.1" authors = ["Ethan Frey "] edition = "2018" description = "CW4 implementation of group based on staked tokens" @@ -26,11 +26,11 @@ backtraces = ["cosmwasm-std/backtraces"] library = [] [dependencies] -cw0 = { path = "../../packages/cw0", version = "0.4.0" } -cw2 = { path = "../../packages/cw2", version = "0.4.0" } -cw4 = { path = "../../packages/cw4", version = "0.4.0" } -cw-controllers = { path = "../../packages/controllers", version = "0.4.0" } -cw-storage-plus = { path = "../../packages/storage-plus", version = "0.4.0", features = ["iterator"] } +cw0 = { path = "../../packages/cw0", version = "0.4.1" } +cw2 = { path = "../../packages/cw2", version = "0.4.1" } +cw4 = { path = "../../packages/cw4", version = "0.4.1" } +cw-controllers = { path = "../../packages/controllers", version = "0.4.1" } +cw-storage-plus = { path = "../../packages/storage-plus", version = "0.4.1", features = ["iterator"] } cosmwasm-std = { version = "0.12.2" } schemars = "0.7" serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/contracts/cw721-base/Cargo.toml b/contracts/cw721-base/Cargo.toml index f6313dec6..c29e8f789 100644 --- a/contracts/cw721-base/Cargo.toml +++ b/contracts/cw721-base/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw721-base" -version = "0.4.0" +version = "0.4.1" authors = ["Ethan Frey "] edition = "2018" description = "Basic implementation cw721 NFTs" @@ -25,10 +25,10 @@ backtraces = ["cosmwasm-std/backtraces"] library = [] [dependencies] -cw0 = { path = "../../packages/cw0", version = "0.4.0" } -cw2 = { path = "../../packages/cw2", version = "0.4.0" } -cw721 = { path = "../../packages/cw721", version = "0.4.0" } -cw-storage-plus = { path = "../../packages/storage-plus", version = "0.4.0" , features = ["iterator"]} +cw0 = { path = "../../packages/cw0", version = "0.4.1" } +cw2 = { path = "../../packages/cw2", version = "0.4.1" } +cw721 = { path = "../../packages/cw721", version = "0.4.1" } +cw-storage-plus = { path = "../../packages/storage-plus", version = "0.4.1" , features = ["iterator"]} cosmwasm-std = { version = "0.12.2" } schemars = "0.7" serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/packages/controllers/Cargo.toml b/packages/controllers/Cargo.toml index fe8fc6956..f950519c4 100644 --- a/packages/controllers/Cargo.toml +++ b/packages/controllers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw-controllers" -version = "0.4.0" +version = "0.4.1" authors = ["Ethan Frey "] edition = "2018" description = "Common controllers we can reuse in many contracts" @@ -13,8 +13,8 @@ documentation = "https://docs.cosmwasm.com" [dependencies] cosmwasm-std = { version = "0.12.2" } -cw0 = { path = "../cw0", version = "0.4.0" } -cw-storage-plus = { path = "../storage-plus", version = "0.4.0", features = ["iterator"] } +cw0 = { path = "../cw0", version = "0.4.1" } +cw-storage-plus = { path = "../storage-plus", version = "0.4.1", features = ["iterator"] } schemars = "0.7" serde = { version = "1.0.103", default-features = false, features = ["derive"] } thiserror = { version = "1.0.21" } diff --git a/packages/cw0/Cargo.toml b/packages/cw0/Cargo.toml index 1d0929047..b26f9af5b 100644 --- a/packages/cw0/Cargo.toml +++ b/packages/cw0/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw0" -version = "0.4.0" +version = "0.4.1" authors = ["Ethan Frey "] edition = "2018" description = "Common helpers for other cw specs" diff --git a/packages/cw1/Cargo.toml b/packages/cw1/Cargo.toml index e7ae54bba..0560eaec3 100644 --- a/packages/cw1/Cargo.toml +++ b/packages/cw1/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw1" -version = "0.4.0" +version = "0.4.1" authors = ["Ethan Frey "] edition = "2018" description = "Definition and types for the CosmWasm-1 interface" diff --git a/packages/cw2/Cargo.toml b/packages/cw2/Cargo.toml index 0a889e5e7..cf7488b67 100644 --- a/packages/cw2/Cargo.toml +++ b/packages/cw2/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw2" -version = "0.4.0" +version = "0.4.1" authors = ["Ethan Frey "] edition = "2018" description = "Definition and types for the CosmWasm-2 interface" @@ -11,6 +11,6 @@ documentation = "https://docs.cosmwasm.com" [dependencies] cosmwasm-std = { version = "0.12.2" } -cw-storage-plus = { path = "../../packages/storage-plus", version = "0.4.0" } +cw-storage-plus = { path = "../../packages/storage-plus", version = "0.4.1" } schemars = "0.7" serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/packages/cw20/Cargo.toml b/packages/cw20/Cargo.toml index fa8f8c130..2d208f253 100644 --- a/packages/cw20/Cargo.toml +++ b/packages/cw20/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw20" -version = "0.4.0" +version = "0.4.1" authors = ["Ethan Frey "] edition = "2018" description = "Definition and types for the CosmWasm-20 interface" @@ -10,7 +10,7 @@ homepage = "https://cosmwasm.com" documentation = "https://docs.cosmwasm.com" [dependencies] -cw0 = { path = "../../packages/cw0", version = "0.4.0" } +cw0 = { path = "../../packages/cw0", version = "0.4.1" } cosmwasm-std = { version = "0.12.2" } schemars = "0.7" serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/packages/cw3/Cargo.toml b/packages/cw3/Cargo.toml index 8e31c5734..b07675023 100644 --- a/packages/cw3/Cargo.toml +++ b/packages/cw3/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw3" -version = "0.4.0" +version = "0.4.1" authors = ["Ethan Frey "] edition = "2018" description = "CosmWasm-3 Interface: On-Chain MultiSig/Voting contracts" @@ -10,7 +10,7 @@ homepage = "https://cosmwasm.com" documentation = "https://docs.cosmwasm.com" [dependencies] -cw0 = { path = "../../packages/cw0", version = "0.4.0" } +cw0 = { path = "../../packages/cw0", version = "0.4.1" } cosmwasm-std = { version = "0.12.2" } schemars = "0.7" serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/packages/cw4/Cargo.toml b/packages/cw4/Cargo.toml index cfb1c9518..6094412e1 100644 --- a/packages/cw4/Cargo.toml +++ b/packages/cw4/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw4" -version = "0.4.0" +version = "0.4.1" authors = ["Ethan Frey "] edition = "2018" description = "CosmWasm-4 Interface: Groups Members" diff --git a/packages/cw721/Cargo.toml b/packages/cw721/Cargo.toml index 4c5800287..1f6d7390f 100644 --- a/packages/cw721/Cargo.toml +++ b/packages/cw721/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw721" -version = "0.4.0" +version = "0.4.1" authors = ["Ethan Frey "] edition = "2018" description = "Definition and types for the CosmWasm-721 NFT interface" @@ -10,7 +10,7 @@ homepage = "https://cosmwasm.com" documentation = "https://docs.cosmwasm.com" [dependencies] -cw0 = { path = "../../packages/cw0", version = "0.4.0" } +cw0 = { path = "../../packages/cw0", version = "0.4.1" } cosmwasm-std = { version = "0.12.2" } schemars = "0.7" serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/packages/multi-test/Cargo.toml b/packages/multi-test/Cargo.toml index a8ef845ed..b9c408fec 100644 --- a/packages/multi-test/Cargo.toml +++ b/packages/multi-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw-multi-test" -version = "0.4.0" +version = "0.4.1" authors = ["Ethan Frey "] edition = "2018" description = "Test helpers for multi-contract interactions" @@ -15,7 +15,7 @@ default = ["iterator"] iterator = ["cosmwasm-std/iterator"] [dependencies] -cw0 = { path = "../../packages/cw0", version = "0.4.0" } +cw0 = { path = "../../packages/cw0", version = "0.4.1" } cosmwasm-std = { version = "0.12.2" } schemars = "0.7" serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/packages/storage-plus/Cargo.toml b/packages/storage-plus/Cargo.toml index 10d8cc8f6..c45de3535 100644 --- a/packages/storage-plus/Cargo.toml +++ b/packages/storage-plus/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cw-storage-plus" -version = "0.4.0" +version = "0.4.1" authors = ["Ethan Frey "] edition = "2018" description = "Enhanced/experimental storage engines" From f61b20168184fa4ae0f98c95c41ba8bf6f6c027d Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Mon, 4 Jan 2021 10:33:02 +0100 Subject: [PATCH 23/27] Revert "Add save_raw() / remove_raw() methods to Map" This reverts commit e534d4f05a2f8e696251711bf39960c337958f2d. --- packages/storage-plus/src/map.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/storage-plus/src/map.rs b/packages/storage-plus/src/map.rs index 441e3a5ad..608dd1bdf 100644 --- a/packages/storage-plus/src/map.rs +++ b/packages/storage-plus/src/map.rs @@ -46,20 +46,10 @@ where self.key(k).save(store, data) } - pub fn save_raw(&self, store: &mut dyn Storage, k: &[u8], data: &T) -> StdResult<()> { - let path = Path::::new(self.namespace, &[k]); - path.save(store, data) - } - pub fn remove(&self, store: &mut dyn Storage, k: K) { self.key(k).remove(store) } - pub fn remove_raw(&self, store: &mut dyn Storage, k: &[u8]) { - let path = Path::::new(self.namespace, &[k]); - path.remove(store) - } - /// load will return an error if no data is set at the given key, or on parse error pub fn load(&self, store: &dyn Storage, k: K) -> StdResult { self.key(k).load(store) From 17316ee56052a7c539e72f54b6585196dec5c8c1 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Mon, 4 Jan 2021 11:07:50 +0100 Subject: [PATCH 24/27] Add UniqueIndex / MultiIndex docs / comments --- packages/storage-plus/src/indexes.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index 3482098b3..dcd44ff91 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -27,12 +27,6 @@ pub fn index_string_tuple(data1: &str, data2: &str) -> (PkOwned, PkOwned) { (index_string(data1), index_string(data2)) } -// 2 main variants: -// * store (namespace, index_name, idx_value, key) -> b"1" - allows many and references pk -// * store (namespace, index_name, idx_value) -> {key, value} - allows one and copies pk and data -// // this would be the primary key -// * store (namespace, index_name, pk) -> value - allows one with data -// // Note: we cannot store traits with generic functions inside `Box`, // so I pull S: Storage to a top-level pub trait Index @@ -43,6 +37,17 @@ where fn remove(&self, store: &mut dyn Storage, pk: &[u8], old_data: &T) -> StdResult<()>; } +/// MultiIndex stores (namespace, index_name, idx_value, pk) -> b"pk_len". +/// Allows many values per index, and references pk. +/// The associated primary key value is stored in the main (pk_namespace) map, +/// which stores (namespace, pk_namespace, pk) -> value. +/// +/// The stored pk_len is used to recover the pk from the index namespace, and perform +/// the secondary load of the associated value from the main map. +/// +/// The MultiIndex definition must include a field for the pk. That is, the MultiIndex K value +/// is always a n-tuple (n >= 2) and its last element must be the pk. +/// The index function must therefore put the pk as last element, when generating the index. pub struct MultiIndex<'a, K, T> { index: fn(&T, Vec) -> K, idx_namespace: &'a [u8], @@ -198,6 +203,8 @@ pub(crate) struct UniqueRef { value: T, } +/// UniqueIndex stores (namespace, index_name, idx_value) -> {key, value} +/// Allows one value per index (i.e. unique) and copies pk and data pub struct UniqueIndex<'a, K, T> { index: fn(&T) -> K, idx_map: Map<'a, K, UniqueRef>, From 6d60a11bba2c3fe2588ae7d5b6df152f9b848134 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 19 Jan 2021 21:29:38 +0100 Subject: [PATCH 25/27] Fix / complete merge from master --- packages/storage-plus/src/indexed_map.rs | 12 ++++++------ packages/storage-plus/src/indexes.rs | 4 +++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index 36ef1a41c..1b9cea9e5 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -355,7 +355,7 @@ mod test { #[test] fn range_simple_key_by_multi_index() { let mut store = MockStorage::new(); - let mut map = build_map(); + let map = build_map(); // save data let data1 = Data { @@ -419,7 +419,7 @@ mod test { "data__name_age", ), }; - let mut map = IndexedMap::new("data", indexes); + let map = IndexedMap::new("data", indexes); // save data let data1 = Data { @@ -588,10 +588,10 @@ mod test { #[test] fn unique_index_simple_key_range() { let mut store = MockStorage::new(); - let mut map = build_map(); + let map = build_map(); // save data - let (pks, datas) = save_data(&mut store, &mut map); + let (pks, datas) = save_data(&mut store, &map); let res: StdResult> = map .idx @@ -619,10 +619,10 @@ mod test { #[test] fn unique_index_composite_key_range() { let mut store = MockStorage::new(); - let mut map = build_map(); + let map = build_map(); // save data - let (pks, datas) = save_data(&mut store, &mut map); + let (pks, datas) = save_data(&mut store, &map); let res: StdResult> = map .idx diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index 7b9ce7572..caaacc1ab 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -268,7 +268,9 @@ where } pub fn sub_prefix(&self, p: K::SubPrefix) -> Prefix { - Prefix::new_de_fn(self.idx_namespace, &p.prefix(), deserialize_unique_kv) + Prefix::new_de_fn(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { + deserialize_unique_kv(kv) + }) } /// returns all items that match this secondary index, always by pk Ascending From 33d3307d615519916b1f95ca803e730d1030f7cb Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 19 Jan 2021 21:31:09 +0100 Subject: [PATCH 26/27] Add sub_prefix() to MultiIndex --- packages/storage-plus/src/indexed_map.rs | 2 +- packages/storage-plus/src/indexes.rs | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index 1b9cea9e5..e7f5f30aa 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -457,7 +457,7 @@ mod test { let marias: Vec<_> = map .idx .name_age - .prefix(PkOwned(b"Maria".to_vec())) + .sub_prefix(PkOwned(b"Maria".to_vec())) .range(&store, None, None, Order::Descending) .collect::>() .unwrap(); diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index caaacc1ab..02dc1309b 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -129,6 +129,15 @@ where ) } + pub fn sub_prefix(&self, p: K::SubPrefix) -> Prefix { + Prefix::new_de_fn( + self.idx_namespace, + &p.prefix(), + self.pk_namespace, + deserialize_multi_kv, + ) + } + // FIXME?: Move to Prefix for ergonomics pub fn pks<'c>( &self, From af0c55517475c93b63a1711d109600c33f1d0e71 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 19 Jan 2021 21:40:12 +0100 Subject: [PATCH 27/27] Renamed for clarity --- packages/storage-plus/src/indexes.rs | 8 ++++---- packages/storage-plus/src/prefix.rs | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index 02dc1309b..5491f4938 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -121,7 +121,7 @@ where K: PrimaryKey<'a>, { pub fn prefix(&self, p: K::Prefix) -> Prefix { - Prefix::new_de_fn( + Prefix::with_deserialization_function( self.idx_namespace, &p.prefix(), self.pk_namespace, @@ -130,7 +130,7 @@ where } pub fn sub_prefix(&self, p: K::SubPrefix) -> Prefix { - Prefix::new_de_fn( + Prefix::with_deserialization_function( self.idx_namespace, &p.prefix(), self.pk_namespace, @@ -271,13 +271,13 @@ where K: PrimaryKey<'a>, { pub fn prefix(&self, p: K::Prefix) -> Prefix { - Prefix::new_de_fn(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { + Prefix::with_deserialization_function(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { deserialize_unique_kv(kv) }) } pub fn sub_prefix(&self, p: K::SubPrefix) -> Prefix { - Prefix::new_de_fn(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { + Prefix::with_deserialization_function(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { deserialize_unique_kv(kv) }) } diff --git a/packages/storage-plus/src/prefix.rs b/packages/storage-plus/src/prefix.rs index 3d181d7d7..c544d89e6 100644 --- a/packages/storage-plus/src/prefix.rs +++ b/packages/storage-plus/src/prefix.rs @@ -73,10 +73,12 @@ where T: Serialize + DeserializeOwned, { pub fn new(top_name: &[u8], sub_names: &[&[u8]]) -> Self { - Prefix::new_de_fn(top_name, sub_names, &[], |_, _, kv| deserialize_kv(kv)) + Prefix::with_deserialization_function(top_name, sub_names, &[], |_, _, kv| { + deserialize_kv(kv) + }) } - pub fn new_de_fn( + pub fn with_deserialization_function( top_name: &[u8], sub_names: &[&[u8]], pk_name: &[u8],