Skip to content

Commit 00bedae

Browse files
adlerjohnnfurfaromohammadfawaz
authored
Storage with more than one word. (#1235)
* Store more than one word. * test: add new storage test project * build: rename default contract & abi * test: fix path to abi.json * test: work on tests * test: work on test contract * test: add storage tests to harness * test: add test for store_u64 * test: add tests for b256 * test: remove dbg! * test: update tests * feat: start to draft the read function * Fixing some issues and adding to the test * more tests * minor improvements * fix some contract IDs * address comments from reviewers * small renaming for clarity * fix contract IDs again * fixing SDK API in storage test Co-authored-by: Nick Furfaro <nfurfaro33@gmail.com> Co-authored-by: Mohammad Fawaz <mohammadfawaz89@gmail.com>
1 parent 953dad9 commit 00bedae

File tree

9 files changed

+486
-7
lines changed

9 files changed

+486
-7
lines changed

sway-lib-std/src/storage.sw

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,93 @@
11
library storage;
22

3+
use ::hash::sha256;
4+
use ::context::registers::stack_ptr;
5+
6+
/// Store a stack variable in storage.
37
pub fn store<T>(key: b256, value: T) {
4-
asm(r1: key, r2: value) {
5-
sww r1 r2;
8+
if !__is_reference_type::<T>() {
9+
// If copy type, then it's a single word and can be stored with a single SWW.
10+
asm(k: key, v: value) {
11+
sww k v;
12+
};
13+
} else {
14+
// If reference type, then it's more than a word. Loop over every 32
15+
// bytes and store sequentially.
16+
let mut size_left = __size_of::<T>();
17+
let mut local_key = key;
18+
19+
// Cast the the pointer to `value` to a u64. This lets us increment
20+
// this pointer later on to iterate over 32 byte chunks of `value`.
21+
let mut ptr_to_value = asm(v: value) {
22+
v
23+
};
24+
25+
while size_left > 32 {
26+
// Store a 4 words (32 byte) at a time using `swwq`
27+
asm(k: local_key, v: ptr_to_value) {
28+
swwq k v;
29+
};
30+
31+
// Move by 32 bytes
32+
ptr_to_value = ptr_to_value + 32;
33+
size_left = size_left - 32;
34+
35+
// Generate a new key for each 32 byte chunk TODO Should eventually
36+
// replace this with `local_key = local_key + 1
37+
local_key = sha256(local_key);
38+
}
39+
40+
// Store the leftover bytes using a single `swwq`
41+
asm(k: local_key, v: ptr_to_value) {
42+
swwq k v;
43+
};
644
};
745
}
846

47+
/// Load a stack variable from storage.
948
pub fn get<T>(key: b256) -> T {
10-
asm(r1: key, r2) {
11-
srw r2 r1;
12-
r2: T
49+
if !__is_reference_type::<T>() {
50+
// If copy type, then it's a single word and can be read with a single
51+
// SRW.
52+
asm(k: key, v) {
53+
srw v k;
54+
v: T
55+
}
56+
} else {
57+
// If reference type, then it's more than a word. Loop over every 32
58+
// bytes and read sequentially.
59+
let mut size_left = __size_of::<T>();
60+
let mut local_key = key;
61+
62+
// Keep track of the base pointer for the final result
63+
let result_ptr = stack_ptr();
64+
65+
while size_left > 32 {
66+
// Read 4 words (32 bytes) at a time using `srwq`
67+
let current_pointer = stack_ptr();
68+
asm(k: local_key, v: current_pointer) {
69+
cfei i32;
70+
srwq v k;
71+
};
72+
73+
// Move by 32 bytes
74+
size_left = size_left - 32;
75+
76+
// Generate a new key for each 32 byte chunk TODO Should eventually
77+
// replace this with `local_key = local_key + 1
78+
local_key = sha256(local_key);
79+
}
80+
81+
// Read the leftover bytes using a single `srwq`
82+
let current_pointer = stack_ptr();
83+
asm(k: local_key, v: current_pointer) {
84+
cfei i32;
85+
srwq v k;
86+
}
87+
88+
// Return the final result as type T
89+
asm(res: result_ptr) {
90+
res: T
91+
}
1392
}
1493
}

test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use basic_storage_abi::StoreU64;
33
use std::assert::assert;
44

55
fn main() -> u64 {
6-
let addr = abi(StoreU64, 0x804a1248edf8b46a40e31283020337da84bb81eb8a2eaaa929b99bf0c93fbf12);
6+
let addr = abi(StoreU64, 0x76c37f1f22b25f7f063483fe57ada77db8bb9799c43a3105996ada9e7dcd20dc);
77
let key = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
88
let value = 4242;
99

test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_increment_contract/src/main.sw

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use increment_abi::Incrementor;
44
use std::assert::assert;
55

66
fn main() -> bool {
7-
let the_abi = abi(Incrementor, 0x924e7fba0dee39fa0e5506b631e664c0adf63cda6589833bf47f5926ef065477);
7+
let the_abi = abi(Incrementor, 0xb735d4f30161b1898a99816eac7c4ed02b80253af1f4ce6d10b5773bd3004e9e);
88
the_abi.initialize(0); // comment this line out to just increment without initializing
99
the_abi.increment(5);
1010
the_abi.increment(5);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ mod hashing;
77
mod logging;
88
mod reentrancy;
99
mod registers;
10+
mod storage;
1011
mod token_ops;
1112
mod tx_fields;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
out
2+
target
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[[package]]
2+
name = 'core'
3+
dependencies = []
4+
5+
[[package]]
6+
name = 'std'
7+
dependencies = ['core']
8+
9+
[[package]]
10+
name = 'storage'
11+
dependencies = ['std']
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[project]
2+
authors = ["Fuel Labs <contact@fuel.sh>"]
3+
entry = "main.sw"
4+
license = "Apache-2.0"
5+
name = "storage"
6+
7+
[dependencies]
8+
std = { path = "../../../../../sway-lib-std" }
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
use fuels::prelude::*;
2+
use fuels_abigen_macro::abigen;
3+
4+
abigen!(
5+
TestStorageContract,
6+
"test_projects/storage/out/debug/storage-abi.json",
7+
);
8+
9+
async fn get_test_storage_instance() -> TestStorageContract {
10+
let wallet = launch_provider_and_get_single_wallet().await;
11+
let id = Contract::deploy(
12+
"test_projects/storage/out/debug/storage.bin",
13+
&wallet,
14+
TxParameters::default(),
15+
)
16+
.await
17+
.unwrap();
18+
19+
TestStorageContract::new(id.to_string(), wallet)
20+
}
21+
22+
#[tokio::test]
23+
async fn can_store_and_get_bool() {
24+
let instance = get_test_storage_instance().await;
25+
let b = true;
26+
instance.store_bool(b).call().await.unwrap();
27+
let result = instance.get_bool().call().await.unwrap();
28+
assert_eq!(result.value, b);
29+
}
30+
31+
#[tokio::test]
32+
async fn can_store_and_get_u8() {
33+
let instance = get_test_storage_instance().await;
34+
let n = 8;
35+
instance.store_u8(n).call().await.unwrap();
36+
let result = instance.get_u8().call().await.unwrap();
37+
assert_eq!(result.value, n);
38+
}
39+
40+
#[tokio::test]
41+
async fn can_store_and_get_u16() {
42+
let instance = get_test_storage_instance().await;
43+
let n = 16;
44+
instance.store_u16(n).call().await.unwrap();
45+
let result = instance.get_u16().call().await.unwrap();
46+
assert_eq!(result.value, n);
47+
}
48+
49+
#[tokio::test]
50+
async fn can_store_and_get_u32() {
51+
let instance = get_test_storage_instance().await;
52+
let n = 32;
53+
instance.store_u32(n).call().await.unwrap();
54+
let result = instance.get_u32().call().await.unwrap();
55+
assert_eq!(result.value, n);
56+
}
57+
58+
#[tokio::test]
59+
async fn can_store_and_get_u64() {
60+
let instance = get_test_storage_instance().await;
61+
let n = 64;
62+
instance.store_u64(n).call().await.unwrap();
63+
let result = instance.get_u64().call().await.unwrap();
64+
assert_eq!(result.value, n);
65+
}
66+
67+
#[tokio::test]
68+
async fn can_store_b256() {
69+
let instance = get_test_storage_instance().await;
70+
let n: [u8; 32] = [2; 32];
71+
instance.store_b256(n).call().await.unwrap();
72+
let result = instance.get_b256().call().await.unwrap();
73+
assert_eq!(result.value, n);
74+
}
75+
76+
#[tokio::test]
77+
async fn can_store_small_struct() {
78+
let instance = get_test_storage_instance().await;
79+
let s = SmallStruct { x: 42 };
80+
instance.store_small_struct(s.clone()).call().await.unwrap();
81+
let result = instance.get_small_struct().call().await.unwrap();
82+
assert_eq!(result.value, s);
83+
}
84+
85+
#[tokio::test]
86+
async fn can_store_medium_struct() {
87+
let instance = get_test_storage_instance().await;
88+
let s = MediumStruct { x: 42, y: 66 };
89+
instance
90+
.store_medium_struct(s.clone())
91+
.call()
92+
.await
93+
.unwrap();
94+
let result = instance.get_medium_struct().call().await.unwrap();
95+
assert_eq!(result.value, s);
96+
}
97+
98+
#[tokio::test]
99+
async fn can_store_large_struct() {
100+
let instance = get_test_storage_instance().await;
101+
let s = LargeStruct {
102+
x: 13,
103+
y: [6; 32],
104+
z: 77,
105+
};
106+
instance.store_large_struct(s.clone()).call().await.unwrap();
107+
let result = instance.get_large_struct().call().await.unwrap();
108+
assert_eq!(result.value, s);
109+
}
110+
111+
#[tokio::test]
112+
async fn can_store_very_large_struct() {
113+
let instance = get_test_storage_instance().await;
114+
let s = VeryLargeStruct {
115+
x: 42,
116+
y: [9; 32],
117+
z: [7; 32],
118+
};
119+
instance
120+
.store_very_large_struct(s.clone())
121+
.call()
122+
.await
123+
.unwrap();
124+
let result = instance.get_very_large_struct().call().await.unwrap();
125+
assert_eq!(result.value, s);
126+
}
127+
128+
#[tokio::test]
129+
async fn can_store_enum() {
130+
let instance = get_test_storage_instance().await;
131+
let e1 = StorageEnum::V1([3; 32]);
132+
instance.store_enum(e1.clone()).call().await.unwrap();
133+
let result = instance.get_enum().call().await.unwrap();
134+
assert_eq!(result.value, e1);
135+
136+
let e2 = StorageEnum::V2(99);
137+
instance.store_enum(e2.clone()).call().await.unwrap();
138+
let result = instance.get_enum().call().await.unwrap();
139+
assert_eq!(result.value, e2);
140+
141+
let e3 = StorageEnum::V3([4; 32]);
142+
instance.store_enum(e3.clone()).call().await.unwrap();
143+
let result = instance.get_enum().call().await.unwrap();
144+
assert_eq!(result.value, e3);
145+
}
146+
147+
#[tokio::test]
148+
async fn can_store_tuple() {
149+
let instance = get_test_storage_instance().await;
150+
let t = ([7; 32], 8, [6; 32]);
151+
instance.store_tuple(t.clone()).call().await.unwrap();
152+
let result = instance.get_tuple().call().await.unwrap();
153+
assert_eq!(result.value, t);
154+
}
155+
156+
#[tokio::test]
157+
async fn can_store_string() {
158+
let instance = get_test_storage_instance().await;
159+
let s = "fastest_modular_execution_layer".to_string();
160+
instance.store_string(s.clone()).call().await.unwrap();
161+
let result = instance.get_string().call().await.unwrap();
162+
assert_eq!(result.value, s);
163+
}
164+
165+
#[tokio::test]
166+
async fn can_store_array() {
167+
let instance = get_test_storage_instance().await;
168+
let a = [[153; 32], [136; 32], [119; 32]].to_vec();
169+
instance.store_array().call().await.unwrap();
170+
let result = instance.get_array().call().await.unwrap();
171+
assert_eq!(result.value, a);
172+
}

0 commit comments

Comments
 (0)