Skip to content

Commit 3b94da2

Browse files
committed
New method get_handle to help implementing nested storage maps
1 parent 100653a commit 3b94da2

File tree

8 files changed

+238
-30
lines changed

8 files changed

+238
-30
lines changed

sway-lib-std/src/experimental/storage.sw

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,35 @@ impl<K, V> StorageMap<K, V> {
225225
read::<V>(key, 0)
226226
}
227227

228+
/// Retrieves the `StorageHandle` that describes the raw location in storage of the value
229+
/// stored at `key`, regardless of whether a value is actually stored at that location or not.
230+
///
231+
/// ### Arguments
232+
///
233+
/// * `key` - The key to which the value is paired.
234+
///
235+
/// ### Examples
236+
///
237+
/// ```sway
238+
/// storage {
239+
/// map: StorageMap<u64, bool> = StorageMap {}
240+
/// }
241+
///
242+
/// fn foo() {
243+
/// let key = 5_u64;
244+
/// let value = true;
245+
/// storage.map.insert(key, value);
246+
/// let retrieved_value = storage.map.get_handle(key).read().unwrap();
247+
/// assert(value == retrieved_value);
248+
/// }
249+
/// ```
250+
pub fn get_handle(self: StorageHandle<Self>, key: K) -> StorageHandle<V> {
251+
StorageHandle {
252+
key: sha256((key, self.key)),
253+
offset: 0
254+
}
255+
}
256+
228257
/// Clears a value previously stored using a key
229258
///
230259
/// Return a Boolean indicating whether there was a value previously stored at `key`.

test/src/sdk-harness/test_projects/experimental_storage/mod.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -156,10 +156,3 @@ async fn maps_in_struct_access() {
156156
(None, None)
157157
);
158158
}
159-
160-
#[tokio::test]
161-
async fn maps_in_map_access() {
162-
let methods = test_experimental_storage_instance().await.methods();
163-
164-
methods.map_in_map_access().call().await.unwrap();
165-
}

test/src/sdk-harness/test_projects/experimental_storage/src/main.sw

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ storage {
8080
z: 0,
8181
w: 0,
8282
},
83-
map_in_map: StorageMap<u64, StorageHandle<StorageMap<u64, u64>>> = StorageMap {},
8483
}
8584

8685
abi ExperimentalStorageTest {
@@ -107,9 +106,6 @@ abi ExperimentalStorageTest {
107106

108107
#[storage(read, write)]
109108
fn map_in_struct_write(key: (u64, u64), value: (u64, u64));
110-
111-
#[storage(read, write)]
112-
fn map_in_map_access();
113109
}
114110

115111
impl ExperimentalStorageTest for Contract {
@@ -220,23 +216,4 @@ impl ExperimentalStorageTest for Contract {
220216
storage.s2.map0.insert(key.0, value.0);
221217
storage.s2.map1.insert(key.1, value.1);
222218
}
223-
224-
#[storage(read, write)]
225-
fn map_in_map_access() {
226-
storage.map_in_map.insert(0, StorageHandle {
227-
key: std::hash::sha256((storage.map_in_map, 0)),
228-
offset: 0,
229-
});
230-
231-
storage.map_in_map.insert(1, StorageHandle {
232-
key: std::hash::sha256((storage.map_in_map, 1)),
233-
offset: 0,
234-
});
235-
236-
storage.map_in_map.get(0).unwrap().insert(1, 42);
237-
assert(storage.map_in_map.get(0).unwrap().get(1).unwrap() == 42);
238-
239-
storage.map_in_map.get(1).unwrap().insert(1, 24);
240-
assert(storage.map_in_map.get(1).unwrap().get(1).unwrap() == 24);
241-
}
242219
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[[package]]
2+
name = 'core'
3+
source = 'path+from-root-2064A4F50B965AB3'
4+
5+
[[package]]
6+
name = 'experimental_storage_nested_maps'
7+
source = 'member'
8+
dependencies = ['std']
9+
10+
[[package]]
11+
name = 'std'
12+
source = 'path+from-root-2064A4F50B965AB3'
13+
dependencies = ['core']
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[project]
2+
authors = ["Fuel Labs <[email protected]>"]
3+
entry = "main.sw"
4+
license = "Apache-2.0"
5+
name = "experimental_storage_nested_maps"
6+
7+
[dependencies]
8+
std = { path = "../../../../../sway-lib-std" }
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
use fuels::prelude::*;
2+
3+
abigen!(Contract(
4+
name = "TestExperimentalStorageNestedMapsContract",
5+
abi = "test_projects/experimental_storage_nested_maps/out/debug/experimental_storage_nested_maps-abi.json",
6+
));
7+
8+
async fn test_experimental_storage_nested_maps_instance(
9+
) -> TestExperimentalStorageNestedMapsContract {
10+
let wallet = launch_provider_and_get_wallet().await;
11+
let id = Contract::deploy(
12+
"test_projects/experimental_storage_nested_maps/out/debug/experimental_storage_nested_maps.bin",
13+
&wallet,
14+
TxParameters::default(),
15+
StorageConfiguration::with_storage_path(Some(
16+
"test_projects/experimental_storage_nested_maps/out/debug/experimental_storage_nested_maps-storage_slots.json"
17+
.to_string(),
18+
)),
19+
)
20+
.await
21+
.unwrap();
22+
23+
TestExperimentalStorageNestedMapsContract::new(id.clone(), wallet)
24+
}
25+
26+
#[tokio::test]
27+
async fn nested_map_1_access() {
28+
let methods = test_experimental_storage_nested_maps_instance()
29+
.await
30+
.methods();
31+
32+
methods.nested_map_1_access().call().await.unwrap();
33+
}
34+
35+
#[tokio::test]
36+
async fn nested_map_2_access() {
37+
let methods = test_experimental_storage_nested_maps_instance()
38+
.await
39+
.methods();
40+
41+
methods.nested_map_2_access().call().await.unwrap();
42+
}
43+
44+
#[tokio::test]
45+
async fn nested_map_3_access() {
46+
let methods = test_experimental_storage_nested_maps_instance()
47+
.await
48+
.methods();
49+
50+
methods.nested_map_3_access().call().await.unwrap();
51+
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
contract;
2+
3+
use core::experimental::storage::*;
4+
use std::experimental::storage::*;
5+
6+
struct M {
7+
u: b256,
8+
v: u64,
9+
}
10+
11+
impl core::ops::Eq for M {
12+
fn eq(self, other: Self) -> bool {
13+
self.u == other.u && self.v == other.v
14+
}
15+
}
16+
17+
pub enum E {
18+
A: u64,
19+
B: b256,
20+
}
21+
22+
impl core::ops::Eq for E {
23+
fn eq(self, other: Self) -> bool {
24+
match (self, other) {
25+
(E::A(l), E::A(r)) => l == r,
26+
(E::B(l), E::B(r)) => l == r,
27+
_ => false,
28+
}
29+
}
30+
}
31+
32+
storage {
33+
nested_map_1: StorageMap<u64, StorageMap<u64, StorageMap<u64, u64>>> = StorageMap {},
34+
nested_map_2: StorageMap<(u64, u64), StorageMap<str[4], StorageMap<u64, M>>> = StorageMap {},
35+
nested_map_3: StorageMap<u64, StorageMap<M, StorageMap<u64, E>>> = StorageMap {},
36+
}
37+
38+
abi ExperimentalStorageTest {
39+
#[storage(read, write)]
40+
fn nested_map_1_access();
41+
42+
#[storage(read, write)]
43+
fn nested_map_2_access();
44+
45+
#[storage(read, write)]
46+
fn nested_map_3_access();
47+
}
48+
49+
impl ExperimentalStorageTest for Contract {
50+
#[storage(read, write)]
51+
fn nested_map_1_access() {
52+
storage.nested_map_1.get_handle(0).get_handle(0).insert(0, 1);
53+
storage.nested_map_1.get_handle(0).get_handle(0).insert(1, 2);
54+
storage.nested_map_1.get_handle(0).get_handle(1).insert(0, 3);
55+
storage.nested_map_1.get_handle(0).get_handle(1).insert(1, 4);
56+
storage.nested_map_1.get_handle(1).get_handle(0).insert(0, 5);
57+
storage.nested_map_1.get_handle(1).get_handle(0).insert(1, 6);
58+
storage.nested_map_1.get_handle(1).get_handle(1).insert(0, 7);
59+
storage.nested_map_1.get_handle(1).get_handle(1).insert(1, 8);
60+
61+
assert(storage.nested_map_1.get_handle(0).get_handle(0).get(0).unwrap() == 1);
62+
assert(storage.nested_map_1.get_handle(0).get_handle(0).get(1).unwrap() == 2);
63+
assert(storage.nested_map_1.get_handle(0).get_handle(1).get(0).unwrap() == 3);
64+
assert(storage.nested_map_1.get_handle(0).get_handle(1).get(1).unwrap() == 4);
65+
assert(storage.nested_map_1.get_handle(1).get_handle(0).get(0).unwrap() == 5);
66+
assert(storage.nested_map_1.get_handle(1).get_handle(0).get(1).unwrap() == 6);
67+
assert(storage.nested_map_1.get_handle(1).get_handle(1).get(0).unwrap() == 7);
68+
assert(storage.nested_map_1.get_handle(1).get_handle(1).get(1).unwrap() == 8);
69+
70+
assert(storage.nested_map_1.get_handle(2).get_handle(1).get(1).is_none());
71+
assert(storage.nested_map_1.get_handle(1).get_handle(2).get(1).is_none());
72+
assert(storage.nested_map_1.get_handle(1).get_handle(1).get(2).is_none());
73+
}
74+
75+
#[storage(read, write)]
76+
fn nested_map_2_access() {
77+
let m1 = M {
78+
u: 0x1111111111111111111111111111111111111111111111111111111111111111,
79+
v: 1
80+
};
81+
let m2 = M {
82+
u: 0x2222222222222222222222222222222222222222222222222222222222222222,
83+
v: 2
84+
};
85+
86+
storage.nested_map_2.get_handle((0, 0)).get_handle("0000").insert(0, m1);
87+
storage.nested_map_2.get_handle((0, 0)).get_handle("0001").insert(1, m2);
88+
storage.nested_map_2.get_handle((0, 1)).get_handle("0000").insert(0, m1);
89+
storage.nested_map_2.get_handle((0, 1)).get_handle("0001").insert(1, m2);
90+
storage.nested_map_2.get_handle((1, 0)).get_handle("0000").insert(0, m1);
91+
storage.nested_map_2.get_handle((1, 0)).get_handle("0001").insert(1, m2);
92+
storage.nested_map_2.get_handle((1, 1)).get_handle("0000").insert(0, m1);
93+
storage.nested_map_2.get_handle((1, 1)).get_handle("0001").insert(1, m2);
94+
95+
assert(storage.nested_map_2.get_handle((0, 0)).get_handle("0000").get(0).unwrap() == m1);
96+
assert(storage.nested_map_2.get_handle((0, 0)).get_handle("0001").get(1).unwrap() == m2);
97+
assert(storage.nested_map_2.get_handle((0, 1)).get_handle("0000").get(0).unwrap() == m1);
98+
assert(storage.nested_map_2.get_handle((0, 1)).get_handle("0001").get(1).unwrap() == m2);
99+
assert(storage.nested_map_2.get_handle((1, 0)).get_handle("0000").get(0).unwrap() == m1);
100+
assert(storage.nested_map_2.get_handle((1, 0)).get_handle("0001").get(1).unwrap() == m2);
101+
assert(storage.nested_map_2.get_handle((1, 1)).get_handle("0000").get(0).unwrap() == m1);
102+
assert(storage.nested_map_2.get_handle((1, 1)).get_handle("0001").get(1).unwrap() == m2);
103+
}
104+
105+
#[storage(read, write)]
106+
fn nested_map_3_access() {
107+
let m1 = M {
108+
u: 0x1111111111111111111111111111111111111111111111111111111111111111,
109+
v: 1
110+
};
111+
let m2 = M {
112+
u: 0x2222222222222222222222222222222222222222222222222222222222222222,
113+
v: 2
114+
};
115+
let e1 = E::A(42);
116+
let e2 = E::B(0x3333333333333333333333333333333333333333333333333333333333333333);
117+
118+
storage.nested_map_3.get_handle(0).get_handle(m1).insert(0, e1);
119+
storage.nested_map_3.get_handle(0).get_handle(m2).insert(1, e2);
120+
storage.nested_map_3.get_handle(0).get_handle(m1).insert(0, e1);
121+
storage.nested_map_3.get_handle(0).get_handle(m2).insert(1, e2);
122+
storage.nested_map_3.get_handle(1).get_handle(m1).insert(0, e1);
123+
storage.nested_map_3.get_handle(1).get_handle(m2).insert(1, e2);
124+
storage.nested_map_3.get_handle(1).get_handle(m1).insert(0, e1);
125+
storage.nested_map_3.get_handle(1).get_handle(m2).insert(1, e2);
126+
127+
assert(storage.nested_map_3.get_handle(0).get_handle(m1).get(0).unwrap() == e1);
128+
assert(storage.nested_map_3.get_handle(0).get_handle(m2).get(1).unwrap() == e2);
129+
assert(storage.nested_map_3.get_handle(0).get_handle(m1).get(0).unwrap() == e1);
130+
assert(storage.nested_map_3.get_handle(0).get_handle(m2).get(1).unwrap() == e2);
131+
assert(storage.nested_map_3.get_handle(1).get_handle(m1).get(0).unwrap() == e1);
132+
assert(storage.nested_map_3.get_handle(1).get_handle(m2).get(1).unwrap() == e2);
133+
assert(storage.nested_map_3.get_handle(1).get_handle(m1).get(0).unwrap() == e1);
134+
assert(storage.nested_map_3.get_handle(1).get_handle(m2).get(1).unwrap() == e2);
135+
}
136+
}

test/src/sdk-harness/test_projects/harness.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ mod evm_ec_recover;
1414
mod experimental_storage;
1515
mod experimental_storage_init;
1616
mod experimental_storage_map;
17+
mod experimental_storage_nested_maps;
1718
mod exponentiation;
1819
mod generics_in_abi;
1920
mod hashing;

0 commit comments

Comments
 (0)