Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 65 additions & 2 deletions packages/storage-plus/src/indexed_snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use crate::de::KeyDeserialize;
use crate::iter_helpers::deserialize_kv;
use crate::keys::{Prefixer, PrimaryKey};
use crate::prefix::{namespaced_prefix_range, Bound, Prefix, PrefixBound};
use crate::snapshot::SnapshotMap;
use crate::{IndexList, Path, Strategy};
use crate::snapshot::{ChangeSet, SnapshotMap};
use crate::{IndexList, Map, Path, Strategy};

/// `IndexedSnapshotMap` works like a `SnapshotMap` but has a secondary index
pub struct IndexedSnapshotMap<'a, K, T, I> {
Expand Down Expand Up @@ -56,6 +56,10 @@ impl<'a, K, T, I> IndexedSnapshotMap<'a, K, T, I> {
idx: indexes,
}
}

pub fn changelog(&self) -> &Map<'a, (K, u64), ChangeSet<T>> {
&self.primary.changelog()
}
}

impl<'a, K, T, I> IndexedSnapshotMap<'a, K, T, I>
Expand Down Expand Up @@ -607,6 +611,65 @@ mod test {
assert_eq!(marias[1].1, data1);
}

#[test]
fn changelog_range_works() {
let mut store = MockStorage::new();
let map = build_snapshot_map();
let mut height = 1;

// simple data for testing
// EVERY.remove(&mut store, "B", 4).unwrap();
let data1 = Data {
name: "Maria".to_string(),
last_name: "".to_string(),
age: 42,
};
let pk_a = "A";
map.save(&mut store, pk_a, &data1, height).unwrap();
height += 1;

let data2 = Data {
name: "Juan".to_string(),
last_name: "Perez".to_string(),
age: 13,
};
let pk_b = "B";
map.save(&mut store, pk_b, &data2, height).unwrap();
height += 1;

let data3 = Data {
name: "Maria".to_string(),
last_name: "Williams".to_string(),
age: 24,
};
map.update(&mut store, pk_a, height, |_| -> StdResult<Data> {
Ok(data3)
})
.unwrap();

height += 1;
map.remove(&mut store, pk_b, height).unwrap();

let changes: Vec<_> = map
.changelog()
.range(&store, None, None, Order::Ascending)
.collect::<StdResult<_>>()
.unwrap();
let count = changes.len();
assert_eq!(4, count);

// sorted by ascending key, height
assert_eq!(
changes,
vec![
(("A".into(), 1), ChangeSet { old: None }),
(("A".into(), 3), ChangeSet { old: Some(data1) }),
(("B".into(), 2), ChangeSet { old: None }),
(("B".into(), 4), ChangeSet { old: Some(data2) })
]
);
}

#[test]
fn range_raw_composite_key_by_multi_index() {
let mut store = MockStorage::new();
Expand Down
8 changes: 4 additions & 4 deletions packages/storage-plus/src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,17 @@ impl<'a, K, T> Map<'a, K, T> {
key_type: PhantomData,
}
}

pub fn namespace(&self) -> &'a [u8] {
self.namespace
}
}

impl<'a, K, T> Map<'a, K, T>
where
T: Serialize + DeserializeOwned,
K: PrimaryKey<'a>,
{
pub fn namespace(&self) -> &'a [u8] {
self.namespace
}

pub fn key(&self, k: K) -> Path<T> {
Path::new(
self.namespace,
Expand Down
59 changes: 57 additions & 2 deletions packages/storage-plus/src/snapshot/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ use serde::Serialize;

use cosmwasm_std::{StdError, StdResult, Storage};

use crate::snapshot::Snapshot;
use crate::{Item, Strategy};
use crate::snapshot::{ChangeSet, Snapshot};
use crate::{Item, Map, Strategy};

/// Item that maintains a snapshot of one or more checkpoints.
/// We can query historical data as well as current state.
/// What data is snapshotted depends on the Strategy.
pub struct SnapshotItem<'a, T> {
primary: Item<'a, T>,
changelog_namespace: &'a str,
snapshots: Snapshot<'a, (), T>,
}

Expand All @@ -34,6 +35,7 @@ impl<'a, T> SnapshotItem<'a, T> {
) -> Self {
SnapshotItem {
primary: Item::new(storage_key),
changelog_namespace: changelog,
snapshots: Snapshot::new(checkpoints, changelog, strategy),
}
}
Expand All @@ -45,6 +47,11 @@ impl<'a, T> SnapshotItem<'a, T> {
pub fn remove_checkpoint(&self, store: &mut dyn Storage, height: u64) -> StdResult<()> {
self.snapshots.remove_checkpoint(store, height)
}

pub fn changelog(&self) -> Map<u64, ChangeSet<T>> {
// Build and return a compatible Map with the proper key type
Map::new(self.changelog_namespace)
}
}

impl<'a, T> SnapshotItem<'a, T>
Expand Down Expand Up @@ -272,4 +279,52 @@ mod tests {
assert_eq!(None, EVERY.may_load_at_height(&storage, 5).unwrap());
assert_eq!(Some(2), EVERY.may_load_at_height(&storage, 6).unwrap());
}

#[test]
#[cfg(feature = "iterator")]
fn changelog_range_works() {
use crate::Bound;
use cosmwasm_std::Order;

let mut store = MockStorage::new();

// simple data for testing
EVERY.save(&mut store, &5, 1u64).unwrap();
EVERY.save(&mut store, &7, 2u64).unwrap();
EVERY
.update(&mut store, 3u64, |_| -> StdResult<u64> { Ok(8) })
.unwrap();
EVERY.remove(&mut store, 4u64).unwrap();

// let's try to iterate over the changelog
let all: StdResult<Vec<_>> = EVERY
.changelog()
.range(&store, None, None, Order::Ascending)
.collect();
let all = all.unwrap();
assert_eq!(4, all.len());
assert_eq!(
all,
vec![
(1, ChangeSet { old: None }),
(2, ChangeSet { old: Some(5) }),
(3, ChangeSet { old: Some(7) }),
(4, ChangeSet { old: Some(8) })
]
);

// let's try to iterate over a changelog range
let all: StdResult<Vec<_>> = EVERY
.changelog()
.range(
&store,
Some(Bound::exclusive_int(3u64)),
None,
Order::Ascending,
)
.collect();
let all = all.unwrap();
assert_eq!(1, all.len());
assert_eq!(all, vec![(4, ChangeSet { old: Some(8) }),]);
}
}
70 changes: 69 additions & 1 deletion packages/storage-plus/src/snapshot/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::keys::PrimaryKey;
use crate::map::Map;
use crate::path::Path;
use crate::prefix::{namespaced_prefix_range, Prefix, PrefixBound};
use crate::snapshot::Snapshot;
use crate::snapshot::{ChangeSet, Snapshot};
use crate::{Bound, Prefixer, Strategy};

/// Map that maintains a snapshots of one or more checkpoints.
Expand Down Expand Up @@ -44,6 +44,10 @@ impl<'a, K, T> SnapshotMap<'a, K, T> {
snapshots: Snapshot::new(checkpoints, changelog, strategy),
}
}

pub fn changelog(&self) -> &Map<'a, (K, u64), ChangeSet<T>> {
&self.snapshots.changelog
}
}

impl<'a, K, T> SnapshotMap<'a, K, T>
Expand Down Expand Up @@ -458,6 +462,70 @@ mod tests {
);
}

#[test]
#[cfg(feature = "iterator")]
fn changelog_range_works() {
use cosmwasm_std::Order;

let mut store = MockStorage::new();

// simple data for testing
EVERY.save(&mut store, "A", &5, 1).unwrap();
EVERY.save(&mut store, "B", &7, 2).unwrap();
EVERY
.update(&mut store, "A", 3, |_| -> StdResult<u64> { Ok(8) })
.unwrap();
EVERY.remove(&mut store, "B", 4).unwrap();

// let's try to iterate over the changelog
let all: StdResult<Vec<_>> = EVERY
.changelog()
.range(&store, None, None, Order::Ascending)
.collect();
let all = all.unwrap();
assert_eq!(4, all.len());
assert_eq!(
all,
vec![
(("A".into(), 1), ChangeSet { old: None }),
(("A".into(), 3), ChangeSet { old: Some(5) }),
(("B".into(), 2), ChangeSet { old: None }),
(("B".into(), 4), ChangeSet { old: Some(7) })
]
);

// let's try to iterate over a changelog key/prefix
let all: StdResult<Vec<_>> = EVERY
.changelog()
.prefix("B")
.range(&store, None, None, Order::Ascending)
.collect();
let all = all.unwrap();
assert_eq!(2, all.len());
assert_eq!(
all,
vec![
(2, ChangeSet { old: None }),
(4, ChangeSet { old: Some(7) })
]
);

// let's try to iterate over a changelog prefixed range
let all: StdResult<Vec<_>> = EVERY
.changelog()
.prefix("A")
.range(
&store,
Some(Bound::inclusive_int(3u64)),
None,
Order::Ascending,
)
.collect();
let all = all.unwrap();
assert_eq!(1, all.len());
assert_eq!(all, vec![(3, ChangeSet { old: Some(5) }),]);
}

#[test]
#[cfg(feature = "iterator")]
fn range_simple_string_key() {
Expand Down
2 changes: 1 addition & 1 deletion packages/storage-plus/src/snapshot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ pub enum Strategy {
}

#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
pub(crate) struct ChangeSet<T> {
pub struct ChangeSet<T> {
pub old: Option<T>,
}

Expand Down