Skip to content

Commit

Permalink
New method get_handle to help implementing nested storage maps
Browse files Browse the repository at this point in the history
  • Loading branch information
mohammadfawaz committed Mar 23, 2023
1 parent 100653a commit 3945de9
Show file tree
Hide file tree
Showing 8 changed files with 238 additions and 30 deletions.
29 changes: 29 additions & 0 deletions sway-lib-std/src/experimental/storage.sw
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,35 @@ impl<K, V> StorageMap<K, V> {
read::<V>(key, 0)
}

/// Retrieves the `StorageHandle` that describes the raw location in storage of the value
/// stored at `key`, regardless if whether a value is actually stored at that location or not.
///
/// ### Arguments
///
/// * `key` - The key to which the value is paired.
///
/// ### Examples
///
/// ```sway
/// storage {
/// map: StorageMap<u64, bool> = StorageMap {}
/// }
///
/// fn foo() {
/// let key = 5_u64;
/// let value = true;
/// storage.map.insert(key, value);
/// let retrieved_value = storage.map.get_handle(key).read().unwrap();
/// assert(value == retrieved_value);
/// }
/// ```
pub fn get_handle(self: StorageHandle<Self>, key: K) -> StorageHandle<V> {
StorageHandle {
key: sha256((key, self.key)),
offset: 0
}
}

/// Clears a value previously stored using a key
///
/// Return a Boolean indicating whether there was a value previously stored at `key`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,3 @@ async fn maps_in_struct_access() {
(None, None)
);
}

#[tokio::test]
async fn maps_in_map_access() {
let methods = test_experimental_storage_instance().await.methods();

methods.map_in_map_access().call().await.unwrap();
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ storage {
z: 0,
w: 0,
},
map_in_map: StorageMap<u64, StorageHandle<StorageMap<u64, u64>>> = StorageMap {},
}

abi ExperimentalStorageTest {
Expand All @@ -107,9 +106,6 @@ abi ExperimentalStorageTest {

#[storage(read, write)]
fn map_in_struct_write(key: (u64, u64), value: (u64, u64));

#[storage(read, write)]
fn map_in_map_access();
}

impl ExperimentalStorageTest for Contract {
Expand Down Expand Up @@ -220,23 +216,4 @@ impl ExperimentalStorageTest for Contract {
storage.s2.map0.insert(key.0, value.0);
storage.s2.map1.insert(key.1, value.1);
}

#[storage(read, write)]
fn map_in_map_access() {
storage.map_in_map.insert(0, StorageHandle {
key: std::hash::sha256((storage.map_in_map, 0)),
offset: 0,
});

storage.map_in_map.insert(1, StorageHandle {
key: std::hash::sha256((storage.map_in_map, 1)),
offset: 0,
});

storage.map_in_map.get(0).unwrap().insert(1, 42);
assert(storage.map_in_map.get(0).unwrap().get(1).unwrap() == 42);

storage.map_in_map.get(1).unwrap().insert(1, 24);
assert(storage.map_in_map.get(1).unwrap().get(1).unwrap() == 24);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[[package]]
name = 'core'
source = 'path+from-root-2064A4F50B965AB3'

[[package]]
name = 'experimental_storage_nested_maps'
source = 'member'
dependencies = ['std']

[[package]]
name = 'std'
source = 'path+from-root-2064A4F50B965AB3'
dependencies = ['core']
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
license = "Apache-2.0"
name = "experimental_storage_nested_maps"

[dependencies]
std = { path = "../../../../../sway-lib-std" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use fuels::prelude::*;

abigen!(Contract(
name = "TestExperimentalStorageNestedMapsContract",
abi = "test_projects/experimental_storage_nested_maps/out/debug/experimental_storage_nested_maps-abi.json",
));

async fn test_experimental_storage_nested_maps_instance(
) -> TestExperimentalStorageNestedMapsContract {
let wallet = launch_provider_and_get_wallet().await;
let id = Contract::deploy(
"test_projects/experimental_storage_nested_maps/out/debug/experimental_storage_nested_maps.bin",
&wallet,
TxParameters::default(),
StorageConfiguration::with_storage_path(Some(
"test_projects/experimental_storage_nested_maps/out/debug/experimental_storage_nested_maps-storage_slots.json"
.to_string(),
)),
)
.await
.unwrap();

TestExperimentalStorageNestedMapsContract::new(id.clone(), wallet)
}

#[tokio::test]
async fn nested_map_1_access() {
let methods = test_experimental_storage_nested_maps_instance()
.await
.methods();

methods.nested_map_1_access().call().await.unwrap();
}

#[tokio::test]
async fn nested_map_2_access() {
let methods = test_experimental_storage_nested_maps_instance()
.await
.methods();

methods.nested_map_2_access().call().await.unwrap();
}

#[tokio::test]
async fn nested_map_3_access() {
let methods = test_experimental_storage_nested_maps_instance()
.await
.methods();

methods.nested_map_3_access().call().await.unwrap();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
contract;

use core::experimental::storage::*;
use std::experimental::storage::*;

struct M {
u: b256,
v: u64,
}

impl core::ops::Eq for M {
fn eq(self, other: Self) -> bool {
self.u == other.u && self.v == other.v
}
}

pub enum E {
A: u64,
B: b256,
}

impl core::ops::Eq for E {
fn eq(self, other: Self) -> bool {
match (self, other) {
(E::A(l), E::A(r)) => l == r,
(E::B(l), E::B(r)) => l == r,
_ => false,
}
}
}

storage {
nested_map_1: StorageMap<u64, StorageMap<u64, StorageMap<u64, u64>>> = StorageMap {},
nested_map_2: StorageMap<(u64, u64), StorageMap<str[4], StorageMap<u64, M>>> = StorageMap {},
nested_map_3: StorageMap<u64, StorageMap<M, StorageMap<u64, E>>> = StorageMap {},
}

abi ExperimentalStorageTest {
#[storage(read, write)]
fn nested_map_1_access();

#[storage(read, write)]
fn nested_map_2_access();

#[storage(read, write)]
fn nested_map_3_access();
}

impl ExperimentalStorageTest for Contract {
#[storage(read, write)]
fn nested_map_1_access() {
storage.nested_map_1.get_handle(0).get_handle(0).insert(0, 1);
storage.nested_map_1.get_handle(0).get_handle(0).insert(1, 2);
storage.nested_map_1.get_handle(0).get_handle(1).insert(0, 3);
storage.nested_map_1.get_handle(0).get_handle(1).insert(1, 4);
storage.nested_map_1.get_handle(1).get_handle(0).insert(0, 5);
storage.nested_map_1.get_handle(1).get_handle(0).insert(1, 6);
storage.nested_map_1.get_handle(1).get_handle(1).insert(0, 7);
storage.nested_map_1.get_handle(1).get_handle(1).insert(1, 8);

assert(storage.nested_map_1.get_handle(0).get_handle(0).get(0).unwrap() == 1);
assert(storage.nested_map_1.get_handle(0).get_handle(0).get(1).unwrap() == 2);
assert(storage.nested_map_1.get_handle(0).get_handle(1).get(0).unwrap() == 3);
assert(storage.nested_map_1.get_handle(0).get_handle(1).get(1).unwrap() == 4);
assert(storage.nested_map_1.get_handle(1).get_handle(0).get(0).unwrap() == 5);
assert(storage.nested_map_1.get_handle(1).get_handle(0).get(1).unwrap() == 6);
assert(storage.nested_map_1.get_handle(1).get_handle(1).get(0).unwrap() == 7);
assert(storage.nested_map_1.get_handle(1).get_handle(1).get(1).unwrap() == 8);

assert(storage.nested_map_1.get_handle(2).get_handle(1).get(1).is_none());
assert(storage.nested_map_1.get_handle(1).get_handle(2).get(1).is_none());
assert(storage.nested_map_1.get_handle(1).get_handle(1).get(2).is_none());
}

#[storage(read, write)]
fn nested_map_2_access() {
let m1 = M {
u: 0x1111111111111111111111111111111111111111111111111111111111111111,
v: 1
};
let m2 = M {
u: 0x2222222222222222222222222222222222222222222222222222222222222222,
v: 2
};

storage.nested_map_2.get_handle((0, 0)).get_handle("0000").insert(0, m1);
storage.nested_map_2.get_handle((0, 0)).get_handle("0001").insert(1, m2);
storage.nested_map_2.get_handle((0, 1)).get_handle("0000").insert(0, m1);
storage.nested_map_2.get_handle((0, 1)).get_handle("0001").insert(1, m2);
storage.nested_map_2.get_handle((1, 0)).get_handle("0000").insert(0, m1);
storage.nested_map_2.get_handle((1, 0)).get_handle("0001").insert(1, m2);
storage.nested_map_2.get_handle((1, 1)).get_handle("0000").insert(0, m1);
storage.nested_map_2.get_handle((1, 1)).get_handle("0001").insert(1, m2);

assert(storage.nested_map_2.get_handle((0, 0)).get_handle("0000").get(0).unwrap() == m1);
assert(storage.nested_map_2.get_handle((0, 0)).get_handle("0001").get(1).unwrap() == m2);
assert(storage.nested_map_2.get_handle((0, 1)).get_handle("0000").get(0).unwrap() == m1);
assert(storage.nested_map_2.get_handle((0, 1)).get_handle("0001").get(1).unwrap() == m2);
assert(storage.nested_map_2.get_handle((1, 0)).get_handle("0000").get(0).unwrap() == m1);
assert(storage.nested_map_2.get_handle((1, 0)).get_handle("0001").get(1).unwrap() == m2);
assert(storage.nested_map_2.get_handle((1, 1)).get_handle("0000").get(0).unwrap() == m1);
assert(storage.nested_map_2.get_handle((1, 1)).get_handle("0001").get(1).unwrap() == m2);
}

#[storage(read, write)]
fn nested_map_3_access() {
let m1 = M {
u: 0x1111111111111111111111111111111111111111111111111111111111111111,
v: 1
};
let m2 = M {
u: 0x2222222222222222222222222222222222222222222222222222222222222222,
v: 2
};
let e1 = E::A(42);
let e2 = E::B(0x3333333333333333333333333333333333333333333333333333333333333333);

storage.nested_map_3.get_handle(0).get_handle(m1).insert(0, e1);
storage.nested_map_3.get_handle(0).get_handle(m2).insert(1, e2);
storage.nested_map_3.get_handle(0).get_handle(m1).insert(0, e1);
storage.nested_map_3.get_handle(0).get_handle(m2).insert(1, e2);
storage.nested_map_3.get_handle(1).get_handle(m1).insert(0, e1);
storage.nested_map_3.get_handle(1).get_handle(m2).insert(1, e2);
storage.nested_map_3.get_handle(1).get_handle(m1).insert(0, e1);
storage.nested_map_3.get_handle(1).get_handle(m2).insert(1, e2);

assert(storage.nested_map_3.get_handle(0).get_handle(m1).get(0).unwrap() == e1);
assert(storage.nested_map_3.get_handle(0).get_handle(m2).get(1).unwrap() == e2);
assert(storage.nested_map_3.get_handle(0).get_handle(m1).get(0).unwrap() == e1);
assert(storage.nested_map_3.get_handle(0).get_handle(m2).get(1).unwrap() == e2);
assert(storage.nested_map_3.get_handle(1).get_handle(m1).get(0).unwrap() == e1);
assert(storage.nested_map_3.get_handle(1).get_handle(m2).get(1).unwrap() == e2);
assert(storage.nested_map_3.get_handle(1).get_handle(m1).get(0).unwrap() == e1);
assert(storage.nested_map_3.get_handle(1).get_handle(m2).get(1).unwrap() == e2);
}
}
1 change: 1 addition & 0 deletions test/src/sdk-harness/test_projects/harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ mod evm_ec_recover;
mod experimental_storage;
mod experimental_storage_init;
mod experimental_storage_map;
mod experimental_storage_nested_maps;
mod exponentiation;
mod generics_in_abi;
mod hashing;
Expand Down

0 comments on commit 3945de9

Please sign in to comment.