Skip to content

Commit

Permalink
Merge pull request #94 from CosmWasm/57-impl-ownedkey-for-cosmwasmstd…
Browse files Browse the repository at this point in the history
…-types

Implement `Key`/`OwnedKey` for `cosmwasm_std` types
  • Loading branch information
uint authored Jan 30, 2025
2 parents 358546a + ab00228 commit 95e6f54
Show file tree
Hide file tree
Showing 20 changed files with 732 additions and 167 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
lcov.info
/target
/Cargo.lock
dodo.py
.doit.db*
__pycache__
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ keywords = ["CosmWasm"]
[workspace.dependencies]
storey = { path = "packages/storey", version = "0.3" }
storey-encoding = { path = "packages/storey-encoding", version = "0.1" }
storey-macros = { path = "packages/storey-macros", version = "0.1" }
storey-storage = { path = "packages/storey-storage", version = "0.1" }
3 changes: 3 additions & 0 deletions packages/cw-storey/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@ rmp-serde = "1.1"
serde = "1"

storey = { workspace = true }

[dev-dependencies]
mocks = { path = "../mocks" }
31 changes: 25 additions & 6 deletions packages/cw-storey/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use storey::storage::{
};

/// A wrapper around a type implementing [`cosmwasm_std::Storage`] that integrates it with [`storey`].
#[repr(transparent)]
pub struct CwStorage<S: ?Sized>(pub S);

impl<S> StorageBackend for CwStorage<S>
Expand Down Expand Up @@ -33,9 +34,18 @@ impl<S> IterableStorage for CwStorage<S>
where
S: cosmwasm_std::Storage + ?Sized,
{
type KeysIterator<'a> = Box<dyn Iterator<Item = Vec<u8>> + 'a> where Self: 'a;
type ValuesIterator<'a> = Box<dyn Iterator<Item = Vec<u8>> + 'a> where Self: 'a;
type PairsIterator<'a> = Box<dyn Iterator<Item = (Vec<u8>, Vec<u8>)> + 'a> where Self: 'a;
type KeysIterator<'a>
= Box<dyn Iterator<Item = Vec<u8>> + 'a>
where
Self: 'a;
type ValuesIterator<'a>
= Box<dyn Iterator<Item = Vec<u8>> + 'a>
where
Self: 'a;
type PairsIterator<'a>
= Box<dyn Iterator<Item = (Vec<u8>, Vec<u8>)> + 'a>
where
Self: 'a;

fn keys<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::KeysIterator<'a> {
let (start, end) = bounds_to_option(start, end);
Expand Down Expand Up @@ -72,9 +82,18 @@ impl<S> RevIterableStorage for CwStorage<S>
where
S: cosmwasm_std::Storage + ?Sized,
{
type RevKeysIterator<'a> = Box<dyn Iterator<Item = Vec<u8>> + 'a> where Self: 'a;
type RevValuesIterator<'a> = Box<dyn Iterator<Item = Vec<u8>> + 'a> where Self: 'a;
type RevPairsIterator<'a> = Box<dyn Iterator<Item = (Vec<u8>, Vec<u8>)> + 'a> where Self: 'a;
type RevKeysIterator<'a>
= Box<dyn Iterator<Item = Vec<u8>> + 'a>
where
Self: 'a;
type RevValuesIterator<'a>
= Box<dyn Iterator<Item = Vec<u8>> + 'a>
where
Self: 'a;
type RevPairsIterator<'a>
= Box<dyn Iterator<Item = (Vec<u8>, Vec<u8>)> + 'a>
where
Self: 'a;

fn rev_keys<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::RevKeysIterator<'a> {
let (start, end) = bounds_to_option(start, end);
Expand Down
17 changes: 0 additions & 17 deletions packages/cw-storey/src/containers.rs

This file was deleted.

204 changes: 204 additions & 0 deletions packages/cw-storey/src/containers/key_set.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
use cosmwasm_std::{Addr, Int128, Int256, Int512, Int64, Uint128, Uint256, Uint512, Uint64};
use storey::containers::map::key::{
DynamicKey, FixedSizeKey, KeySetDefaults, NumericKeyDecodeError,
};
use storey::containers::map::{Key, OwnedKey};

/// The CosmWasm key set for use with storey's [`Map`](storey::containers::Map).
///
/// This key set includes the usual standard library types (like `u32` or `String`) as well as `cosmwasm_std` types (like `Addr` and `Uint128`).
///
/// For more information about key sets, take a look at the [`storey::containers::map::Key`] trait.
#[derive(KeySetDefaults)]
pub struct CwKeySet;

impl Key<CwKeySet> for Addr {
type Kind = DynamicKey;

fn encode(&self) -> Vec<u8> {
self.as_str().as_bytes().to_vec()
}
}

impl OwnedKey<CwKeySet> for Addr {
type Error = <String as OwnedKey>::Error;

fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error>
where
Self: Sized,
{
<String as OwnedKey>::from_bytes(bytes).map(Addr::unchecked)
}
}

macro_rules! cosmwasm_std_uints1 {
($($ty:ty => $size:expr, $stdty:ty),*) => {
$(
impl Key<CwKeySet> for $ty {
type Kind = FixedSizeKey<$size>;

fn encode(&self) -> Vec<u8> {
self.to_be_bytes().to_vec()
}
}

impl OwnedKey<CwKeySet> for $ty {
type Error = NumericKeyDecodeError;

fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error>
where
Self: Sized,
{
let array: [u8; $size] = bytes.try_into().map_err(|_| NumericKeyDecodeError::InvalidLength)?;
Ok(<$stdty>::from_be_bytes(array).into())
}
}
)*
}
}

cosmwasm_std_uints1!(
Uint64 => 8, u64,
Uint128 => 16, u128
);

macro_rules! cosmwasm_std_uints2 {
($($ty:ty => $size:expr),*) => {
$(
impl Key<CwKeySet> for $ty {
type Kind = FixedSizeKey<$size>;

fn encode(&self) -> Vec<u8> {
self.to_be_bytes().to_vec()
}
}

impl OwnedKey<CwKeySet> for $ty {
type Error = NumericKeyDecodeError;

fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error>
where
Self: Sized,
{
let array: [u8; $size] = bytes.try_into().map_err(|_| NumericKeyDecodeError::InvalidLength)?;
Ok(<$ty>::from_be_bytes(array))
}
}
)*
}
}

cosmwasm_std_uints2!(
Uint256 => 32,
Uint512 => 64
);

macro_rules! cosmwasm_std_ints {
($($ty:ty => $size:expr),*) => {
$(
impl Key<CwKeySet> for $ty {
type Kind = FixedSizeKey<$size>;

fn encode(&self) -> Vec<u8> {
let mut bytes = self.to_be_bytes();
bytes[0] ^= 0x80;

bytes.to_vec()
}
}

impl OwnedKey<CwKeySet> for $ty {
type Error = NumericKeyDecodeError;

fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error>
where
Self: Sized,
{
let mut array: [u8; $size] = bytes.try_into().map_err(|_| NumericKeyDecodeError::InvalidLength)?;
array[0] ^= 0x80;

Ok(<$ty>::from_be_bytes(array))
}
}
)*
}
}

cosmwasm_std_ints!(Int64 => 8, Int128 => 16, Int256 => 32, Int512 => 64);

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn unsigned_ints_1() {
let test_vector = [
(Uint64::from(0u64), [0, 0, 0, 0, 0, 0, 0, 0]),
(Uint64::from(1u64), [0, 0, 0, 0, 0, 0, 0, 1]),
(
Uint64::from(0x1234567890abcdefu64),
[0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef],
),
];

for (num, expected) in test_vector.iter() {
let encoded = num.encode();
assert_eq!(encoded, *expected);
}

for (expected, bytes) in test_vector.iter() {
let decoded = Uint64::from_bytes(bytes).unwrap();
assert_eq!(decoded, *expected);
}
}

#[test]
fn unsigned_ints_2() {
let test_vector = [
(Uint256::from(0u64), [0; 32]),
(
Uint256::new([
0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90,
0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34,
0x56, 0x78, 0x90, 0xab, 0xcd, 0xef,
]),
[
0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90,
0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34,
0x56, 0x78, 0x90, 0xab, 0xcd, 0xef,
],
),
];

for (num, expected) in test_vector.iter() {
let encoded = num.encode();
assert_eq!(encoded, *expected);
}

for (expected, bytes) in test_vector.iter() {
let decoded = Uint256::from_bytes(bytes).unwrap();
assert_eq!(decoded, *expected);
}
}

#[test]
fn signed_ints() {
let nums = [
Int256::from(-542),
Int256::from(-111),
Int256::from(0),
Int256::from(121),
Int256::from(342),
];

let mut byte_nums = nums.iter().map(|n| n.encode()).collect::<Vec<_>>();
byte_nums.sort();

let result = byte_nums
.iter()
.map(|bytes| Int256::from_bytes(bytes).unwrap())
.collect::<Vec<_>>();

assert_eq!(result, nums);
}
}
43 changes: 43 additions & 0 deletions packages/cw-storey/src/containers/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//! Storage containers for use with [*CosmWasm*] smart contracts.
//!
//! [*CosmWasm*]: https://github.com/CosmWasm/cosmwasm
mod key_set;

pub use key_set::CwKeySet;

/// The [`storey::containers::Item`] type with the default encoding for [*CosmWasm*] smart
/// contracts.
///
/// [*CosmWasm*]: https://github.com/CosmWasm/cosmwasm
pub type Item<T> = storey::containers::Item<T, crate::encoding::CwEncoding>;

/// The [`storey::containers::Column`] type with the default encoding for [*CosmWasm*] smart
/// contracts.
///
/// [*CosmWasm*]: https://github.com/CosmWasm/cosmwasm
pub type Column<T> = storey::containers::Column<T, crate::encoding::CwEncoding>;

/// The [`storey::containers::Map`] type with the [`CwKeySet`] key set, which includes the
/// usual standard library types (like `u32` or `String`) as well as `cosmwasm_std` types (like `Addr` and `Uint128`).
pub type Map<K, V> = storey::containers::Map<K, V, CwKeySet>;

#[cfg(test)]
mod tests {
use super::*;

use cosmwasm_std::Addr;
use mocks::backend::TestStorage;

#[test]
fn map_addr() {
let map: Map<Addr, Item<u32>> = Map::new(0);
let mut storage = TestStorage::new();

let key = Addr::unchecked("addr1");

map.access(&mut storage).entry_mut(&key).set(&42).unwrap();

assert_eq!(map.access(&storage).entry(&key).get().unwrap(), Some(42));
}
}
19 changes: 19 additions & 0 deletions packages/storey-macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "storey-macros"
version = "0.1.0"
edition = "2021"
authors.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
categories.workspace = true
keywords.workspace = true

[lib]
proc-macro = true

[dependencies]
proc-macro2 = "1"
proc-macro-error = "1"
syn = { version = "2", features = ["full"] }
quote = "1"
Loading

0 comments on commit 95e6f54

Please sign in to comment.