@@ -30,7 +30,7 @@ namespace silkworm::db {
3030
3131using silkworm::test_util::SetLogVerbosityGuard;
3232
33- TEST_CASE (" Storage update " ) {
33+ TEST_CASE (" Buffer storage " , " [silkworm][db][buffer] " ) {
3434 SetLogVerbosityGuard log_guard{log::Level::kNone };
3535 db::test_util::TempChainData context;
3636 auto & txn{context.rw_txn ()};
@@ -42,61 +42,206 @@ TEST_CASE("Storage update") {
4242 const auto value_a1{0x000000000000000000000000000000000000000000000000000000000000006b_bytes32};
4343 const auto value_a2{0x0000000000000000000000000000000000000000000000000000000000000085_bytes32};
4444 const auto value_a3{0x0000000000000000000000000000000000000000000000000000000000000095_bytes32};
45+ const auto value_nil{0x0000000000000000000000000000000000000000000000000000000000000000_bytes32};
4546
4647 const auto location_b{0x0000000000000000000000000000000000000000000000000000000000000002_bytes32};
4748 const auto value_b{0x0000000000000000000000000000000000000000000000000000000000000132_bytes32};
4849
50+ const auto location_c{0x0000000000000000000000000000000000000000000000000000000000000003_bytes32};
51+
4952 auto state = txn.rw_cursor_dup_sort (table::kPlainState );
5053
5154 upsert_storage_value (*state, key, location_a.bytes , value_a1.bytes );
5255 upsert_storage_value (*state, key, location_b.bytes , value_b.bytes );
5356
5457 Buffer buffer{txn};
5558
56- CHECK (buffer.read_storage (address, kDefaultIncarnation , location_a) == value_a1);
59+ SECTION (" Reads storage by address and location" ) {
60+ CHECK (buffer.read_storage (address, kDefaultIncarnation , location_a) == value_a1);
61+ CHECK (buffer.read_storage (address, kDefaultIncarnation , location_b) == value_b);
62+ }
5763
58- // Update only location A
59- buffer.update_storage (address, kDefaultIncarnation , location_a,
60- /* initial=*/ value_a1, /* current=*/ value_a2);
64+ SECTION (" Updates storage by address and location" ) {
65+ // Update only location A
66+ buffer.update_storage (address, kDefaultIncarnation , location_a,
67+ /* initial=*/ value_a1, /* current=*/ value_a2);
6168
62- REQUIRE (buffer.storage_changes ().empty () == false );
69+ REQUIRE (buffer.storage_changes ().empty () == false );
6370
64- buffer.write_to_db ();
71+ buffer.write_to_db ();
6572
66- // Location A should have the new value
67- const std::optional<ByteView> db_value_a{find_value_suffix (*state, key, location_a.bytes )};
68- REQUIRE (db_value_a.has_value ());
69- CHECK (db_value_a == zeroless_view (value_a2.bytes ));
73+ // Location A should have the new value
74+ const std::optional<ByteView> db_value_a{find_value_suffix (*state, key, location_a.bytes )};
75+ REQUIRE (db_value_a.has_value ());
76+ CHECK (db_value_a == zeroless_view (value_a2.bytes ));
7077
71- // Location B should not change
72- const std::optional<ByteView> db_value_b{find_value_suffix (*state, key, location_b.bytes )};
73- REQUIRE (db_value_b.has_value ());
74- CHECK (db_value_b == zeroless_view (value_b.bytes ));
78+ // Location B should not change
79+ const std::optional<ByteView> db_value_b{find_value_suffix (*state, key, location_b.bytes )};
80+ REQUIRE (db_value_b.has_value ());
81+ CHECK (db_value_b == zeroless_view (value_b.bytes ));
82+ }
7583
76- // Update again only location A
77- buffer.update_storage (address, kDefaultIncarnation , location_a,
78- /* initial=*/ value_a2, /* current=*/ value_a3);
84+ SECTION (" Keeps track of storage changes" ) {
85+ // Update location A
86+ buffer.update_storage (address, kDefaultIncarnation , location_a,
87+ /* initial=*/ value_a1, /* current=*/ value_a2);
88+ buffer.write_to_db ();
89+
90+ // Update again location A
91+ buffer.update_storage (address, kDefaultIncarnation , location_a,
92+ /* initial=*/ value_a2, /* current=*/ value_a3);
93+
94+ REQUIRE (buffer.storage_changes ().empty () == false );
95+
96+ // Ask state buffer to not write change sets
97+ buffer.write_to_db (/* write_change_sets=*/ false );
98+
99+ // Location A should have the previous value of old value in state changes, i.e. value_a1
100+ const auto storage_changes{db::read_storage_changes (txn, 0 )};
101+ REQUIRE (storage_changes.size () == 1 );
102+ const auto & [changed_address, changed_map] = *storage_changes.begin ();
103+ CHECK (changed_address == address);
104+ REQUIRE (changed_map.size () == 1 );
105+ const auto & [changed_incarnation, changed_storage] = *changed_map.begin ();
106+ CHECK (changed_incarnation == kDefaultIncarnation );
107+ REQUIRE (changed_storage.size () == 1 );
108+ const auto & [changed_location, changed_value] = *changed_storage.begin ();
109+ CHECK (changed_location == location_a);
110+ CHECK (changed_value == zeroless_view (value_a1.bytes ));
111+ }
79112
80- REQUIRE (buffer.storage_changes ().empty () == false );
113+ SECTION (" Multiple storage changes in a single block saves one storage change entry" ) {
114+ buffer.update_storage (address, kDefaultIncarnation , location_a,
115+ /* initial=*/ value_a1, /* current=*/ value_a2);
116+ buffer.update_storage (address, kDefaultIncarnation , location_a,
117+ /* initial=*/ value_a2, /* current=*/ value_a3);
118+
119+ REQUIRE (buffer.storage_changes ().empty () == false );
120+
121+ buffer.write_to_db ();
122+
123+ const auto storage_changes{db::read_storage_changes (txn, 0 )};
124+ REQUIRE (storage_changes.size () == 1 );
125+ const auto & [changed_address, changed_map] = *storage_changes.begin ();
126+ CHECK (changed_address == address);
127+ REQUIRE (changed_map.size () == 1 );
128+ const auto & [changed_incarnation, changed_storage] = *changed_map.begin ();
129+ CHECK (changed_incarnation == kDefaultIncarnation );
130+ REQUIRE (changed_storage.size () == 1 );
131+ const auto & [changed_location_a, changed_value_a] = *changed_storage.find (location_a);
132+ CHECK (changed_location_a == location_a);
133+ CHECK (changed_value_a == zeroless_view (value_a2.bytes ));
134+ }
81135
82- // Ask state buffer to not write change sets
83- buffer.write_to_db (/* write_change_sets=*/ false );
136+ SECTION (" Multiple storage changes in different blocks cause multiple storage changes" ) {
137+ buffer.begin_block (1 , 1 );
138+ buffer.update_storage (address, kDefaultIncarnation , location_a,
139+ /* initial=*/ value_a1, /* current=*/ value_nil);
140+ buffer.write_to_db ();
141+
142+ buffer.begin_block (2 , 1 );
143+ buffer.update_storage (address, kDefaultIncarnation , location_a,
144+ /* initial=*/ value_nil, /* current=*/ value_a3);
145+ buffer.write_to_db ();
146+
147+ buffer.begin_block (3 , 1 );
148+ buffer.update_storage (address, kDefaultIncarnation , location_a,
149+ /* initial=*/ value_a3, /* current=*/ value_a2);
150+ buffer.write_to_db ();
151+
152+ const auto storage_changes1{db::read_storage_changes (txn, 1 )};
153+ REQUIRE (storage_changes1.size () == 1 );
154+ const auto storage_changes2{db::read_storage_changes (txn, 2 )};
155+ REQUIRE (storage_changes2.size () == 1 );
156+ const auto storage_changes3{db::read_storage_changes (txn, 3 )};
157+ REQUIRE (storage_changes3.size () == 1 );
158+
159+ const std::optional<ByteView> db_value_a2{find_value_suffix (*state, key, location_a.bytes )};
160+ REQUIRE (db_value_a2.has_value ());
161+ CHECK (db_value_a2 == zeroless_view (value_a2.bytes ));
162+ }
163+
164+ SECTION (" Deletes storage by address and location" ) {
165+ // Delete location A
166+ buffer.update_storage (address, kDefaultIncarnation , location_a,
167+ /* initial=*/ value_a1, /* current=*/ value_nil);
168+
169+ // Buffer value set to nil
170+ auto current_value_a1{buffer.read_storage (address, kDefaultIncarnation , location_a)};
171+ CHECK (current_value_a1 == value_nil);
172+
173+ // Not deleted from the db yet
174+ const std::optional<ByteView> db_value_a1{find_value_suffix (*state, key, location_a.bytes )};
175+ CHECK (db_value_a1.has_value ());
176+ CHECK (db_value_a1 == zeroless_view (value_a1.bytes ));
177+
178+ buffer.write_to_db ();
179+
180+ // Buffer reads the value from the db
181+ auto current_value_a2{buffer.read_storage (address, kDefaultIncarnation , location_a)};
182+ CHECK (current_value_a2 == value_nil);
183+
184+ // Location A should be deleted
185+ const std::optional<ByteView> db_value_a2{find_value_suffix (*state, key, location_a.bytes )};
186+ CHECK (!db_value_a2.has_value ());
187+
188+ // Location B should not change
189+ const std::optional<ByteView> db_value_b{find_value_suffix (*state, key, location_b.bytes )};
190+ REQUIRE (db_value_b.has_value ());
191+ CHECK (db_value_b == zeroless_view (value_b.bytes ));
192+ }
84193
85- // Location A should have the previous value of old value in state changes, i.e. value_a1
86- const auto storage_changes{db::read_storage_changes (txn, 0 )};
87- REQUIRE (storage_changes.size () == 1 );
88- const auto & [changed_address, changed_map] = *storage_changes.begin ();
89- CHECK (changed_address == address);
90- REQUIRE (changed_map.size () == 1 );
91- const auto & [changed_incarnation, changed_storage] = *changed_map.begin ();
92- CHECK (changed_incarnation == kDefaultIncarnation );
93- REQUIRE (changed_storage.size () == 1 );
94- const auto & [changed_location, changed_value] = *changed_storage.begin ();
95- CHECK (changed_location == location_a);
96- CHECK (changed_value == zeroless_view (value_a1.bytes ));
194+ SECTION (" Can re-set value after deletion" ) {
195+ // Buffer only
196+ buffer.update_storage (address, kDefaultIncarnation , location_a,
197+ /* initial=*/ value_a1, /* current=*/ value_nil);
198+
199+ // Buffer value set to nil
200+ auto current_value_a1{buffer.read_storage (address, kDefaultIncarnation , location_a)};
201+ CHECK (current_value_a1 == value_nil);
202+
203+ buffer.update_storage (address, kDefaultIncarnation , location_a,
204+ /* initial=*/ value_nil, /* current=*/ value_a2);
205+
206+ auto current_value_a2{buffer.read_storage (address, kDefaultIncarnation , location_a)};
207+ CHECK (current_value_a2 == value_a2);
208+ }
209+
210+ SECTION (" Sets new value" ) {
211+ buffer.update_storage (address, kDefaultIncarnation , location_c,
212+ /* initial=*/ {}, /* current=*/ value_a1);
213+
214+ auto current_value_a1{buffer.read_storage (address, kDefaultIncarnation , location_c)};
215+ CHECK (current_value_a1 == value_a1);
216+
217+ buffer.write_to_db ();
218+
219+ const std::optional<ByteView> db_value_c1{find_value_suffix (*state, key, location_c.bytes )};
220+ REQUIRE (db_value_c1.has_value ());
221+ CHECK (db_value_c1 == zeroless_view (value_a1.bytes ));
222+
223+ auto current_value_a2{buffer.read_storage (address, kDefaultIncarnation , location_c)};
224+ CHECK (current_value_a2 == value_a1);
225+ }
226+
227+ SECTION (" Setting to nil deletes the value" ) {
228+ buffer.update_storage (address, kDefaultIncarnation , location_a,
229+ /* initial=*/ value_a1, /* current=*/ {});
230+
231+ auto current_value_a1{buffer.read_storage (address, kDefaultIncarnation , location_a)};
232+ CHECK (current_value_a1 == value_nil);
233+
234+ buffer.write_to_db ();
235+
236+ const std::optional<ByteView> db_value_a1{find_value_suffix (*state, key, location_a.bytes )};
237+ CHECK (!db_value_a1.has_value ());
238+
239+ auto current_value_a2{buffer.read_storage (address, kDefaultIncarnation , location_a)};
240+ CHECK (current_value_a2 == value_nil);
241+ }
97242}
98243
99- TEST_CASE (" Account update " ) {
244+ TEST_CASE (" Buffer account " , " [silkworm][db][buffer] " ) {
100245 SetLogVerbosityGuard log_guard{log::Level::kNone };
101246 db::test_util::TempChainData context;
102247 auto & txn{context.rw_txn ()};
0 commit comments