diff --git a/packages/cw-snapshot-vector-map/README.md b/packages/cw-snapshot-vector-map/README.md index bcb7c8d43..b00d96ed7 100644 --- a/packages/cw-snapshot-vector-map/README.md +++ b/packages/cw-snapshot-vector-map/README.md @@ -44,21 +44,9 @@ compact ID storage. ## Example ```rust -use cosmwasm_std::{testing::mock_dependencies, Addr, BlockInfo, Timestamp}; -use cw20::Expiration; -use cw_utils::Duration; +use cosmwasm_std::{testing::mock_dependencies, Addr}; use cw_snapshot_vector_map::{LoadedItem, SnapshotVectorMap}; -macro_rules! b { - ($x:expr) => { - &BlockInfo { - chain_id: "CHAIN".to_string(), - height: $x, - time: Timestamp::from_seconds($x), - } - }; -} - let storage = &mut mock_dependencies().storage; let svm: SnapshotVectorMap = SnapshotVectorMap::new( "svm__items", @@ -72,32 +60,32 @@ let first = "first".to_string(); let second = "second".to_string(); // store the first item at block 1, expiring in 10 blocks (at block 11) -svm.push(storage, &key, &first, b!(1), Some(Duration::Height(10))).unwrap(); +svm.push(storage, &key, &first, 1, Some(10)).unwrap(); // store the second item at block 5, which does not expire -svm.push(storage, &key, &second, b!(5), None).unwrap(); +svm.push(storage, &key, &second, 5, None).unwrap(); // remove the second item (ID: 1) at height 15 -svm.remove(storage, &key, 1, b!(15)).unwrap(); +svm.remove(storage, &key, 1, 15).unwrap(); // the vector at block 3 should contain only the first item assert_eq!( - svm.load_all(storage, &key, b!(3)).unwrap(), + svm.load_all(storage, &key, 3).unwrap(), vec![LoadedItem { id: 0, item: first.clone(), - expiration: Some(Expiration::AtHeight(11)), + expiration: Some(11), }] ); // the vector at block 7 should contain both items assert_eq!( - svm.load_all(storage, &key, b!(7)).unwrap(), + svm.load_all(storage, &key, 7).unwrap(), vec![ LoadedItem { id: 0, item: first.clone(), - expiration: Some(Expiration::AtHeight(11)), + expiration: Some(11), }, LoadedItem { id: 1, @@ -109,7 +97,7 @@ assert_eq!( // the vector at block 12 should contain only the first item assert_eq!( - svm.load_all(storage, &key, b!(12)).unwrap(), + svm.load_all(storage, &key, 12).unwrap(), vec![LoadedItem { id: 1, item: second.clone(), @@ -118,8 +106,5 @@ assert_eq!( ); // the vector at block 17 should contain nothing -assert_eq!( - svm.load_all(storage, &key, b!(17)).unwrap(), - vec![] -); +assert_eq!(svm.load_all(storage, &key, 17).unwrap(), vec![]); ``` diff --git a/packages/cw-snapshot-vector-map/src/lib.rs b/packages/cw-snapshot-vector-map/src/lib.rs index 4ac228df1..acce6fdad 100644 --- a/packages/cw-snapshot-vector-map/src/lib.rs +++ b/packages/cw-snapshot-vector-map/src/lib.rs @@ -1,11 +1,9 @@ #![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))] -use cw20::Expiration; -use cw_utils::Duration; use serde::de::DeserializeOwned; use serde::Serialize; -use cosmwasm_std::{BlockInfo, StdResult, Storage}; +use cosmwasm_std::{StdResult, Storage}; use cw_storage_plus::{KeyDeserialize, Map, Prefixer, PrimaryKey, SnapshotMap, Strategy}; /// Map to a vector that allows reading the subset of items that existed at a @@ -17,8 +15,8 @@ pub struct SnapshotVectorMap<'a, K, V> { /// The next item ID to use per-key. next_ids: Map<'a, K, u64>, /// The IDs of the items that are active for a key at a given height, and - /// optionally when they expire. - active: SnapshotMap<'a, K, Vec<(u64, Option)>>, + /// optionally the height at which they expire. + active: SnapshotMap<'a, K, Vec<(u64, Option)>>, } /// A loaded item from the vector, including its ID and expiration. @@ -29,8 +27,8 @@ pub struct LoadedItem { pub id: u64, /// The item. pub item: V, - /// When the item expires, if set. - pub expiration: Option, + /// The block height at which the item expires, if set. + pub expiration: Option, } impl<'a, K, V> SnapshotVectorMap<'a, K, V> { @@ -78,17 +76,17 @@ where // &(key, ID) is a key in a map for<'b> &'b (K, u64): PrimaryKey<'b>, { - /// Adds an item to the vector at the current block, optionally expiring in - /// the future, returning the ID of the new item. This block should be - /// greater than or equal to the blocks all previous items were + /// Adds an item to the vector at the current block height, optionally + /// expiring in the future, returning the ID of the new item. This block + /// should be greater than or equal to the blocks all previous items were /// added/removed at. Pushing to the past will lead to incorrect behavior. pub fn push( &self, store: &mut dyn Storage, k: &K, data: &V, - block: &BlockInfo, - expire_in: Option, + curr_height: u64, + expire_in: Option, ) -> StdResult { // get next ID for the key, defaulting to 0 let next_id = self @@ -104,14 +102,14 @@ where // remove expired items active.retain(|(_, expiration)| { - expiration.map_or(true, |expiration| !expiration.is_expired(block)) + expiration.map_or(true, |expiration| expiration > curr_height) }); // add new item and save list - active.push((next_id, expire_in.map(|d| d.after(block)))); + active.push((next_id, expire_in.map(|d| curr_height + d))); // save the new list - self.active.save(store, k.clone(), &active, block.height)?; + self.active.save(store, k.clone(), &active, curr_height)?; // update next ID self.next_ids.save(store, k.clone(), &(next_id + 1))?; @@ -119,8 +117,8 @@ where Ok(next_id) } - /// Removes an item from the vector by ID and returns it. The block should - /// be greater than or equal to the blocks all previous items were + /// Removes an item from the vector by ID and returns it. The block height + /// should be greater than or equal to the blocks all previous items were /// added/removed at. Removing from the past will lead to incorrect /// behavior. pub fn remove( @@ -128,18 +126,18 @@ where store: &mut dyn Storage, k: &K, id: u64, - block: &BlockInfo, + curr_height: u64, ) -> StdResult { // get active list for the key let mut active = self.active.may_load(store, k.clone())?.unwrap_or_default(); // remove item and any expired items active.retain(|(active_id, expiration)| { - active_id != &id && expiration.map_or(true, |expiration| !expiration.is_expired(block)) + active_id != &id && expiration.map_or(true, |expiration| expiration > curr_height) }); // save the new list - self.active.save(store, k.clone(), &active, block.height)?; + self.active.save(store, k.clone(), &active, curr_height)?; // load and return the item self.load_item(store, k, id) @@ -150,7 +148,7 @@ where &self, store: &dyn Storage, k: &K, - block: &BlockInfo, + height: u64, limit: Option, offset: Option, ) -> StdResult>> { @@ -159,13 +157,13 @@ where let active_ids = self .active - .may_load_at_height(store, k.clone(), block.height)? + .may_load_at_height(store, k.clone(), height)? .unwrap_or_default(); // load paged items, skipping expired ones let items = active_ids .iter() - .filter(|(_, expiration)| expiration.map_or(true, |exp| !exp.is_expired(block))) + .filter(|(_, expiration)| expiration.map_or(true, |exp| exp > height)) .skip(offset) .take(limit) .map(|(id, expiration)| -> StdResult> { @@ -181,14 +179,14 @@ where Ok(items) } - /// Loads all items at the given block that are not expired. + /// Loads all items at the given block height that are not expired. pub fn load_all( &self, store: &dyn Storage, k: &K, - block: &BlockInfo, + height: u64, ) -> StdResult>> { - self.load(store, k, block, None, None) + self.load(store, k, height, None, None) } /// Loads an item from the vector by ID. diff --git a/packages/cw-snapshot-vector-map/src/tests.rs b/packages/cw-snapshot-vector-map/src/tests.rs index 79e1978aa..e5663dd45 100644 --- a/packages/cw-snapshot-vector-map/src/tests.rs +++ b/packages/cw-snapshot-vector-map/src/tests.rs @@ -1,19 +1,7 @@ -use cosmwasm_std::{testing::mock_dependencies, Addr, BlockInfo, Timestamp}; -use cw20::Expiration; -use cw_utils::Duration; +use cosmwasm_std::{testing::mock_dependencies, Addr}; use crate::{LoadedItem, SnapshotVectorMap}; -macro_rules! b { - ($x:expr) => { - &BlockInfo { - chain_id: "".to_string(), - height: $x, - time: Timestamp::from_seconds($x), - } - }; -} - #[test] fn test_basic() { let storage = &mut mock_dependencies().storage; @@ -28,16 +16,16 @@ fn test_basic() { let k2 = &Addr::unchecked("ekez"); // add 1, 2, 3 to k1 at corresponding blocks - svm.push(storage, k1, &1, b!(1), None).unwrap(); - svm.push(storage, k1, &2, b!(2), None).unwrap(); - svm.push(storage, k1, &3, b!(3), None).unwrap(); + svm.push(storage, k1, &1, 1, None).unwrap(); + svm.push(storage, k1, &2, 2, None).unwrap(); + svm.push(storage, k1, &3, 3, None).unwrap(); // add 1, 3 to k2 at corresponding blocks - svm.push(storage, k2, &1, b!(1), None).unwrap(); - svm.push(storage, k2, &3, b!(3), None).unwrap(); + svm.push(storage, k2, &1, 1, None).unwrap(); + svm.push(storage, k2, &3, 3, None).unwrap(); // items update one block later - let items1_b2 = svm.load_all(storage, k1, b!(2)).unwrap(); + let items1_b2 = svm.load_all(storage, k1, 2).unwrap(); assert_eq!( items1_b2, vec![LoadedItem { @@ -48,7 +36,7 @@ fn test_basic() { ); // items update one block later - let items1_b4 = svm.load_all(storage, k1, b!(4)).unwrap(); + let items1_b4 = svm.load_all(storage, k1, 4).unwrap(); assert_eq!( items1_b4, vec![ @@ -71,7 +59,7 @@ fn test_basic() { ); // items update one block later - let items2_b3 = svm.load_all(storage, k2, b!(3)).unwrap(); + let items2_b3 = svm.load_all(storage, k2, 3).unwrap(); assert_eq!( items2_b3, vec![LoadedItem { @@ -82,10 +70,10 @@ fn test_basic() { ); // remove item 2 (ID 1) from k1 at block 4 - svm.remove(storage, k1, 1, b!(4)).unwrap(); + svm.remove(storage, k1, 1, 4).unwrap(); // items update one block later - let items1_b5 = svm.load_all(storage, k1, b!(5)).unwrap(); + let items1_b5 = svm.load_all(storage, k1, 5).unwrap(); assert_eq!( items1_b5, vec![ @@ -115,45 +103,44 @@ fn test_expiration() { ); let k1 = &Addr::unchecked("haon"); - svm.push(storage, k1, &1, b!(1), Some(Duration::Height(3))) - .unwrap(); - svm.push(storage, k1, &4, b!(4), None).unwrap(); + svm.push(storage, k1, &1, 1, Some(3)).unwrap(); + svm.push(storage, k1, &4, 4, None).unwrap(); // items update one block later - let items1_b2 = svm.load_all(storage, k1, b!(2)).unwrap(); + let items1_b2 = svm.load_all(storage, k1, 2).unwrap(); assert_eq!( items1_b2, vec![LoadedItem { id: 0, item: 1, - expiration: Some(Expiration::AtHeight(4)), + expiration: Some(4), }] ); // not expired yet - let items1_b3 = svm.load_all(storage, k1, b!(3)).unwrap(); + let items1_b3 = svm.load_all(storage, k1, 3).unwrap(); assert_eq!( items1_b3, vec![LoadedItem { id: 0, item: 1, - expiration: Some(Expiration::AtHeight(4)), + expiration: Some(4), }] ); // expired: // load returns nothing - let items1_b4 = svm.load_all(storage, k1, b!(4)).unwrap(); + let items1_b4 = svm.load_all(storage, k1, 4).unwrap(); assert_eq!(items1_b4, vec![]); // but vector still has item since the list hasn't been updated let active = svm .active .may_load_at_height(storage, k1.clone(), 4) .unwrap(); - assert_eq!(active, Some(vec![(0, Some(Expiration::AtHeight(4)))])); + assert_eq!(active, Some(vec![(0, Some(4))])); // new item exists now - let items1_b5 = svm.load_all(storage, k1, b!(5)).unwrap(); + let items1_b5 = svm.load_all(storage, k1, 5).unwrap(); assert_eq!( items1_b5, vec![LoadedItem { @@ -164,10 +151,9 @@ fn test_expiration() { ); // add item that will expire - svm.push(storage, k1, &5, b!(5), Some(Duration::Height(3))) - .unwrap(); + svm.push(storage, k1, &5, 5, Some(3)).unwrap(); - let items1_b6 = svm.load_all(storage, k1, b!(6)).unwrap(); + let items1_b6 = svm.load_all(storage, k1, 6).unwrap(); assert_eq!( items1_b6, vec![ @@ -179,16 +165,16 @@ fn test_expiration() { LoadedItem { id: 2, item: 5, - expiration: Some(Expiration::AtHeight(8)), + expiration: Some(8), } ] ); // removing first item at block 8 should expire the second item as well - svm.remove(storage, k1, 1, b!(8)).unwrap(); + svm.remove(storage, k1, 1, 8).unwrap(); // load returns nothing (items update one block later) - let items1_b9 = svm.load_all(storage, k1, b!(9)).unwrap(); + let items1_b9 = svm.load_all(storage, k1, 9).unwrap(); assert_eq!(items1_b9, vec![]); // and vector is empty since the remove updated the list let active = svm @@ -198,24 +184,23 @@ fn test_expiration() { assert_eq!(active, Some(vec![])); // add item that will expire - svm.push(storage, k1, &9, b!(9), Some(Duration::Height(2))) - .unwrap(); + svm.push(storage, k1, &9, 9, Some(2)).unwrap(); - let items1_b10 = svm.load_all(storage, k1, b!(10)).unwrap(); + let items1_b10 = svm.load_all(storage, k1, 10).unwrap(); assert_eq!( items1_b10, vec![LoadedItem { id: 3, item: 9, - expiration: Some(Expiration::AtHeight(11)) + expiration: Some(11) }] ); // push item at block 11, which should expire the existing item - svm.push(storage, k1, &11, b!(11), None).unwrap(); + svm.push(storage, k1, &11, 11, None).unwrap(); // load returns just the pushed item - let items1_b12 = svm.load_all(storage, k1, b!(12)).unwrap(); + let items1_b12 = svm.load_all(storage, k1, 12).unwrap(); assert_eq!( items1_b12, vec![LoadedItem {