Skip to content

Commit 88b09ff

Browse files
authored
db: add extra tests for Buffer (#2171)
1 parent e6e7344 commit 88b09ff

File tree

1 file changed

+179
-34
lines changed

1 file changed

+179
-34
lines changed

silkworm/db/buffer_test.cpp

Lines changed: 179 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ namespace silkworm::db {
3030

3131
using 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

Comments
 (0)