Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi index generic key #211

Merged
merged 35 commits into from
Mar 4, 2021
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
a3dd434
Change marker to bool
maurolacy Dec 19, 2020
2444735
Add U8Key type alias
maurolacy Dec 19, 2020
7ab9db9
Add composite int keys map tests / example
maurolacy Dec 19, 2020
08f2c8e
Impl (canonical) prefix and range for MultiIndex
maurolacy Dec 20, 2020
79d5bae
Add additional Map range test / example
maurolacy Dec 21, 2020
8cb0a77
WIP: Make range work with original prefix()
maurolacy Dec 21, 2020
8aa0002
WIP: Add simple and composite multi index tests
maurolacy Dec 21, 2020
e534d4f
Add save_raw() / remove_raw() methods to Map
maurolacy Dec 21, 2020
d3c18d3
Make range() work by using save_raw()
maurolacy Dec 21, 2020
b9a9a99
Fix simple and composite key range() tests
maurolacy Dec 21, 2020
5495ca2
WIP: Comment tokens by owner query
maurolacy Dec 21, 2020
a5d0b62
WIP: Replace pks() by range() query
maurolacy Dec 22, 2020
1cf8f70
Fix pks(), combine with Prefix
maurolacy Dec 22, 2020
8705d20
Implement PrimaryKey for generic (T, U, V) triplet
maurolacy Dec 24, 2020
aa100da
Implement generic index function return values for MultiIndex
maurolacy Dec 24, 2020
5cfe507
Merge branch 'secondary-index-composite-key' into multi-index-generic…
maurolacy Dec 24, 2020
9d0bd82
Fix pks in cw721-base
maurolacy Dec 24, 2020
7f88973
Change deserialize_unique_kv access to private
maurolacy Dec 24, 2020
d8a4302
MultiIndex range() properly returns pk and T
maurolacy Dec 26, 2020
4529c56
Replace all_items() by prefix() plus range()
maurolacy Dec 26, 2020
2868af4
Implement pks() in terms of range()
maurolacy Dec 26, 2020
1a23c2f
Merge branch 'unique-index-generic-key' into multi-index-generic-key
maurolacy Dec 27, 2020
1ef6fc4
Remove outdated comment
maurolacy Dec 28, 2020
4bc99a9
Set version: 0.4.1
maurolacy Dec 28, 2020
d66edee
Merge branch 'master' into multi-index-generic-key
maurolacy Jan 4, 2021
f61b201
Revert "Add save_raw() / remove_raw() methods to Map"
maurolacy Jan 4, 2021
17316ee
Add UniqueIndex / MultiIndex docs / comments
maurolacy Jan 4, 2021
5333c80
Merge branch 'master' into multi-index-generic-key
maurolacy Jan 5, 2021
a7a1ae4
Merge branch 'master' into multi-index-generic-key
maurolacy Jan 8, 2021
876cb15
Merge branch 'master' into multi-index-generic-key
maurolacy Jan 13, 2021
06687b1
Merge branch 'master' into multi-index-generic-key
maurolacy Jan 19, 2021
6d60a11
Fix / complete merge from master
maurolacy Jan 19, 2021
33d3307
Add sub_prefix() to MultiIndex
maurolacy Jan 19, 2021
af0c555
Renamed for clarity
maurolacy Jan 19, 2021
f38ab01
Merge branch 'master' into multi-index-generic-key
maurolacy Mar 4, 2021
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
18 changes: 13 additions & 5 deletions contracts/cw721-base/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -497,14 +497,22 @@ fn query_tokens(
let start = start_after.map(Bound::exclusive);

let owner_raw = deps.api.canonical_address(&owner)?;
let tokens: Result<Vec<String>, _> = tokens()
let res: Result<Vec<_>, _> = tokens()
.idx
.owner
.pks(deps.storage, &owner_raw, 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<Vec<_>, _> = pks.iter().map(|v| String::from_utf8(v.to_vec())).collect();
let tokens = res.map_err(StdError::invalid_utf8)?;
Ok(TokensResponse { tokens })
}

Expand Down
11 changes: 8 additions & 3 deletions contracts/cw721-base/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -46,7 +46,8 @@ pub fn increment_tokens(storage: &mut dyn Storage) -> StdResult<u64> {
}

pub struct TokenIndexes<'a> {
pub owner: MultiIndex<'a, TokenInfo>,
// pk goes to second tuple element
pub owner: MultiIndex<'a, (PkOwned, PkOwned), TokenInfo>,
maurolacy marked this conversation as resolved.
Show resolved Hide resolved
}

impl<'a> IndexList<TokenInfo> for TokenIndexes<'a> {
Expand All @@ -58,7 +59,11 @@ impl<'a> IndexList<TokenInfo> 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)
}
190 changes: 171 additions & 19 deletions packages/storage-plus/src/indexed_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ where
mod test {
use super::*;

use crate::indexes::{index_string, index_string_tuple, MultiIndex, UniqueIndex};
use crate::indexes::{index_string_tuple, index_triple, MultiIndex, UniqueIndex};
use crate::{PkOwned, U32Key};
use cosmwasm_std::testing::MockStorage;
use cosmwasm_std::{MemoryStorage, Order};
Expand All @@ -175,7 +175,8 @@ mod test {
}

struct DataIndexes<'a> {
pub name: MultiIndex<'a, Data>,
// Second arg is for storing pk
pub name: MultiIndex<'a, (PkOwned, PkOwned), Data>,
maurolacy marked this conversation as resolved.
Show resolved Hide resolved
pub age: UniqueIndex<'a, U32Key, Data>,
pub name_lastname: UniqueIndex<'a, (PkOwned, PkOwned), Data>,
}
Expand All @@ -188,10 +189,28 @@ mod test {
}
}

// For composite multi index tests
struct DataCompositeMultiIndex<'a> {
// Third arg needed for storing pk
pub name_age: MultiIndex<'a, (PkOwned, U32Key, PkOwned), Data>,
}

// Future Note: this can likely be macro-derived
impl<'a> IndexList<Data> for DataCompositeMultiIndex<'a> {
fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn Index<Data>> + '_> {
let v: Vec<&dyn Index<Data>> = 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 {
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| U32Key::new(d.age), "data__age"),
name_lastname: UniqueIndex::new(
|d| index_string_tuple(&d.name, &d.last_name),
Expand Down Expand Up @@ -269,19 +288,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::<Vec<_>>()
.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<Vec<_>> = 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::<StdResult<_>>()
.unwrap();
assert_eq!(2, marias.len());
let (k, v) = &marias[0];
Expand All @@ -292,26 +314,29 @@ 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::<Vec<_>>()
.len();
assert_eq!(0, count);

// other index doesn't match (1 byte before)
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::<Vec<_>>()
.len();
assert_eq!(0, count);

// other index doesn't match (longer)
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::<Vec<_>>()
.len();
assert_eq!(0, count);

Expand All @@ -327,6 +352,127 @@ mod test {
assert_eq!(None, aged);
}

#[test]
fn range_simple_key_by_multi_index() {
let mut store = MockStorage::new();
let map = build_map();

// save data
let data1 = Data {
name: "Maria".to_string(),
last_name: "".to_string(),
age: 42,
};
let pk: &[u8] = b"5627";
map.save(&mut store, pk, &data1).unwrap();

let data2 = Data {
name: "Juan".to_string(),
last_name: "Perez".to_string(),
age: 13,
};
let pk: &[u8] = b"5628";
map.save(&mut store, pk, &data2).unwrap();

let data3 = Data {
name: "Maria".to_string(),
last_name: "Williams".to_string(),
age: 24,
};
let pk: &[u8] = b"5629";
map.save(&mut store, pk, &data3).unwrap();

let data4 = Data {
name: "Maria Luisa".to_string(),
last_name: "Bemberg".to_string(),
age: 12,
};
let pk: &[u8] = b"5630";
map.save(&mut store, pk, &data4).unwrap();

let marias: Vec<_> = map
.idx
.name
.prefix(PkOwned(b"Maria".to_vec()))
.range(&store, None, None, Order::Descending)
.collect::<StdResult<_>>()
.unwrap();
let count = marias.len();
assert_eq!(2, count);

// 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);
}

#[test]
fn range_composite_key_by_multi_index() {
let mut store = MockStorage::new();

let indexes = DataCompositeMultiIndex {
name_age: MultiIndex::new(
|d, k| index_triple(&d.name, d.age, k),
"data",
"data__name_age",
),
};
let map = IndexedMap::new("data", indexes);

// save data
let data1 = Data {
name: "Maria".to_string(),
last_name: "".to_string(),
age: 42,
};
let pk1: &[u8] = b"5627";
map.save(&mut store, pk1, &data1).unwrap();

let data2 = Data {
name: "Juan".to_string(),
last_name: "Perez".to_string(),
age: 13,
};
let pk2: &[u8] = b"5628";
map.save(&mut store, pk2, &data2).unwrap();

let data3 = Data {
name: "Maria".to_string(),
last_name: "Young".to_string(),
age: 24,
};
let pk3: &[u8] = b"5629";
map.save(&mut store, pk3, &data3).unwrap();

let data4 = Data {
name: "Maria Luisa".to_string(),
last_name: "Bemberg".to_string(),
age: 43,
};
let pk4: &[u8] = b"5630";
map.save(&mut store, pk4, &data4).unwrap();

let marias: Vec<_> = map
.idx
.name_age
.sub_prefix(PkOwned(b"Maria".to_vec()))
.range(&store, None, None, Order::Descending)
.collect::<StdResult<_>>()
.unwrap();
let count = marias.len();
assert_eq!(2, count);

// 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);
}

#[test]
fn unique_index_enforced() {
let mut store = MockStorage::new();
Expand Down Expand Up @@ -401,7 +547,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()
};

Expand Down Expand Up @@ -436,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<Vec<_>> = map
.idx
Expand Down Expand Up @@ -467,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<Vec<_>> = map
.idx
Expand Down
Loading