Bitcoin Core  31.0.0
P2P Digital Currency
db_tests.cpp
Go to the documentation of this file.
1 // Copyright (c) 2018-present The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include <boost/test/unit_test.hpp>
6 
7 #include <test/util/common.h>
9 #include <util/check.h>
10 #include <util/fs.h>
11 #include <util/translation.h>
12 #include <wallet/sqlite.h>
13 #include <wallet/migrate.h>
14 #include <wallet/test/util.h>
15 #include <wallet/walletutil.h>
16 
17 #include <cstddef>
18 #include <memory>
19 #include <span>
20 #include <string>
21 #include <string_view>
22 #include <utility>
23 #include <vector>
24 
25 inline std::ostream& operator<<(std::ostream& os, const std::pair<const SerializeData, SerializeData>& kv)
26 {
27  std::span key{kv.first}, value{kv.second};
28  os << "(\"" << std::string_view{reinterpret_cast<const char*>(key.data()), key.size()} << "\", \""
29  << std::string_view{reinterpret_cast<const char*>(value.data()), value.size()} << "\")";
30  return os;
31 }
32 
33 namespace wallet {
34 
35 inline std::span<const std::byte> StringBytes(std::string_view str)
36 {
37  return std::as_bytes(std::span{str});
38 }
39 
40 static SerializeData StringData(std::string_view str)
41 {
42  auto bytes = StringBytes(str);
43  return SerializeData{bytes.begin(), bytes.end()};
44 }
45 
46 static void CheckPrefix(DatabaseBatch& batch, std::span<const std::byte> prefix, MockableData expected)
47 {
48  std::unique_ptr<DatabaseCursor> cursor = batch.GetNewPrefixCursor(prefix);
49  MockableData actual;
50  while (true) {
51  DataStream key, value;
52  DatabaseCursor::Status status = cursor->Next(key, value);
53  if (status == DatabaseCursor::Status::DONE) break;
56  actual.emplace(SerializeData(key.begin(), key.end()), SerializeData(value.begin(), value.end())).second);
57  }
58  BOOST_CHECK_EQUAL_COLLECTIONS(actual.begin(), actual.end(), expected.begin(), expected.end());
59 }
60 
62 
63 static std::vector<std::unique_ptr<WalletDatabase>> TestDatabases(const fs::path& path_root)
64 {
65  std::vector<std::unique_ptr<WalletDatabase>> dbs;
66  DatabaseOptions options;
67  DatabaseStatus status;
68  bilingual_str error;
69  // Unable to test BerkeleyRO since we cannot create a new BDB database to open
70  dbs.emplace_back(MakeSQLiteDatabase(path_root / "sqlite", options, status, error));
71  dbs.emplace_back(CreateMockableWalletDatabase());
72  return dbs;
73 }
74 
75 BOOST_AUTO_TEST_CASE(db_cursor_prefix_range_test)
76 {
77  // Test each supported db
78  for (const auto& database : TestDatabases(m_path_root)) {
79  std::vector<std::string> prefixes = {"", "FIRST", "SECOND", "P\xfe\xff", "P\xff\x01", "\xff\xff"};
80 
81  std::unique_ptr<DatabaseBatch> handler = Assert(database)->MakeBatch();
82  // Write elements to it
83  for (unsigned int i = 0; i < 10; i++) {
84  for (const auto& prefix : prefixes) {
85  BOOST_CHECK(handler->Write(std::make_pair(prefix, i), i));
86  }
87  }
88 
89  // Now read all the items by prefix and verify that each element gets parsed correctly
90  for (const auto& prefix : prefixes) {
91  DataStream s_prefix;
92  s_prefix << prefix;
93  std::unique_ptr<DatabaseCursor> cursor = handler->GetNewPrefixCursor(s_prefix);
94  DataStream key;
95  DataStream value;
96  for (int i = 0; i < 10; i++) {
97  DatabaseCursor::Status status = cursor->Next(key, value);
99 
100  std::string key_back;
101  unsigned int i_back;
102  key >> key_back >> i_back;
103  BOOST_CHECK_EQUAL(key_back, prefix);
104 
105  unsigned int value_back;
106  value >> value_back;
107  BOOST_CHECK_EQUAL(value_back, i_back);
108  }
109 
110  // Let's now read it once more, it should return DONE
111  BOOST_CHECK(cursor->Next(key, value) == DatabaseCursor::Status::DONE);
112  }
113  handler.reset();
114  database->Close();
115  }
116 }
117 
118 // Lower level DatabaseBase::GetNewPrefixCursor test, to cover cases that aren't
119 // covered in the higher level test above. The higher level test uses
120 // serialized strings which are prefixed with string length, so it doesn't test
121 // truly empty prefixes or prefixes that begin with \xff
122 BOOST_AUTO_TEST_CASE(db_cursor_prefix_byte_test)
123 {
124  const MockableData::value_type
125  e{StringData(""), StringData("e")},
126  p{StringData("prefix"), StringData("p")},
127  ps{StringData("prefixsuffix"), StringData("ps")},
128  f{StringData("\xff"), StringData("f")},
129  fs{StringData("\xffsuffix"), StringData("fs")},
130  ff{StringData("\xff\xff"), StringData("ff")},
131  ffs{StringData("\xff\xffsuffix"), StringData("ffs")};
132  for (const auto& database : TestDatabases(m_path_root)) {
133  std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();
134 
135  // Write elements to it if not berkeleyro
136  for (const auto& [k, v] : {e, p, ps, f, fs, ff, ffs}) {
137  batch->Write(std::span{k}, std::span{v});
138  }
139 
140  CheckPrefix(*batch, StringBytes(""), {e, p, ps, f, fs, ff, ffs});
141  CheckPrefix(*batch, StringBytes("prefix"), {p, ps});
142  CheckPrefix(*batch, StringBytes("\xff"), {f, fs, ff, ffs});
143  CheckPrefix(*batch, StringBytes("\xff\xff"), {ff, ffs});
144  batch.reset();
145  database->Close();
146  }
147 }
148 
149 BOOST_AUTO_TEST_CASE(db_availability_after_write_error)
150 {
151  // Ensures the database remains accessible without deadlocking after a write error.
152  // To simulate the behavior, record overwrites are disallowed, and the test verifies
153  // that the database remains active after failing to store an existing record.
154  for (const auto& database : TestDatabases(m_path_root)) {
155  // Write original record
156  std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();
157  std::string key = "key";
158  std::string value = "value";
159  std::string value2 = "value_2";
160  BOOST_CHECK(batch->Write(key, value));
161  // Attempt to overwrite the record (expect failure)
162  BOOST_CHECK(!batch->Write(key, value2, /*fOverwrite=*/false));
163  // Successfully overwrite the record
164  BOOST_CHECK(batch->Write(key, value2, /*fOverwrite=*/true));
165  // Sanity-check; read and verify the overwritten value
166  std::string read_value;
167  BOOST_CHECK(batch->Read(key, read_value));
168  BOOST_CHECK_EQUAL(read_value, value2);
169  }
170 }
171 
172 // Verify 'ErasePrefix' functionality using db keys similar to the ones used by the wallet.
173 // Keys are in the form of std::pair<TYPE, ENTRY_ID>
174 BOOST_AUTO_TEST_CASE(erase_prefix)
175 {
176  const std::string key = "key";
177  const std::string key2 = "key2";
178  const std::string value = "value";
179  const std::string value2 = "value_2";
180  auto make_key = [](std::string type, std::string id) { return std::make_pair(type, id); };
181 
182  for (const auto& database : TestDatabases(m_path_root)) {
183  if (dynamic_cast<BerkeleyRODatabase*>(database.get())) {
184  // Skip this test if BerkeleyRO
185  continue;
186  }
187  std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();
188 
189  // Write two entries with the same key type prefix, a third one with a different prefix
190  // and a fourth one with the type-id values inverted
191  BOOST_CHECK(batch->Write(make_key(key, value), value));
192  BOOST_CHECK(batch->Write(make_key(key, value2), value2));
193  BOOST_CHECK(batch->Write(make_key(key2, value), value));
194  BOOST_CHECK(batch->Write(make_key(value, key), value));
195 
196  // Erase the ones with the same prefix and verify result
197  BOOST_CHECK(batch->TxnBegin());
198  BOOST_CHECK(batch->ErasePrefix(DataStream() << key));
199  BOOST_CHECK(batch->TxnCommit());
200 
201  BOOST_CHECK(!batch->Exists(make_key(key, value)));
202  BOOST_CHECK(!batch->Exists(make_key(key, value2)));
203  // Also verify that entries with a different prefix were not erased
204  BOOST_CHECK(batch->Exists(make_key(key2, value)));
205  BOOST_CHECK(batch->Exists(make_key(value, key)));
206  }
207 }
208 
209 // Test-only statement execution error
210 constexpr int TEST_SQLITE_ERROR = -999;
211 
213 {
214 private:
216  std::set<std::string> m_blocked_statements;
217 public:
218  DbExecBlocker(std::set<std::string> blocked_statements) : m_blocked_statements(blocked_statements) {}
219  int Exec(SQLiteDatabase& database, const std::string& statement) override {
220  if (m_blocked_statements.contains(statement)) return TEST_SQLITE_ERROR;
221  return m_base_exec.Exec(database, statement);
222  }
223 };
224 
225 BOOST_AUTO_TEST_CASE(txn_close_failure_dangling_txn)
226 {
227  // Verifies that there is no active dangling, to-be-reversed db txn
228  // after the batch object that initiated it is destroyed.
229  DatabaseOptions options;
230  DatabaseStatus status;
231  bilingual_str error;
232  std::unique_ptr<SQLiteDatabase> database = MakeSQLiteDatabase(m_path_root / "sqlite", options, status, error);
233 
234  std::string key = "key";
235  std::string value = "value";
236 
237  std::unique_ptr<SQLiteBatch> batch = std::make_unique<SQLiteBatch>(*database);
238  BOOST_CHECK(batch->TxnBegin());
239  BOOST_CHECK(batch->Write(key, value));
240  // Set a handler to prevent txn abortion during destruction.
241  // Mimicking a db statement execution failure.
242  batch->SetExecHandler(std::make_unique<DbExecBlocker>(std::set<std::string>{"ROLLBACK TRANSACTION"}));
243  // Destroy batch
244  batch.reset();
245 
246  // Ensure there is no dangling, to-be-reversed db txn
247  BOOST_CHECK(!database->HasActiveTxn());
248 
249  // And, just as a sanity check; verify that new batchs only write what they suppose to write
250  // and nothing else.
251  std::string key2 = "key2";
252  std::unique_ptr<SQLiteBatch> batch2 = std::make_unique<SQLiteBatch>(*database);
253  BOOST_CHECK(batch2->Write(key2, value));
254  // The first key must not exist
255  BOOST_CHECK(!batch2->Exists(key));
256 }
257 
258 BOOST_AUTO_TEST_CASE(concurrent_txn_dont_interfere)
259 {
260  std::string key = "key";
261  std::string value = "value";
262  std::string value2 = "value_2";
263 
264  DatabaseOptions options;
265  DatabaseStatus status;
266  bilingual_str error;
267  const auto& database = MakeSQLiteDatabase(m_path_root / "sqlite", options, status, error);
268 
269  std::unique_ptr<DatabaseBatch> handler = Assert(database)->MakeBatch();
270 
271  // Verify concurrent db transactions does not interfere between each other.
272  // Start db txn, write key and check the key does exist within the db txn.
273  BOOST_CHECK(handler->TxnBegin());
274  BOOST_CHECK(handler->Write(key, value));
275  BOOST_CHECK(handler->Exists(key));
276 
277  // But, the same key, does not exist in another handler
278  std::unique_ptr<DatabaseBatch> handler2 = Assert(database)->MakeBatch();
279  BOOST_CHECK(handler2->Exists(key));
280 
281  // Attempt to commit the handler txn calling the handler2 methods.
282  // Which, must not be possible.
283  BOOST_CHECK(!handler2->TxnCommit());
284  BOOST_CHECK(!handler2->TxnAbort());
285 
286  // Only the first handler can commit the changes.
287  BOOST_CHECK(handler->TxnCommit());
288  // And, once commit is completed, handler2 can read the record
289  std::string read_value;
290  BOOST_CHECK(handler2->Read(key, read_value));
291  BOOST_CHECK_EQUAL(read_value, value);
292 
293  // Also, once txn is committed, single write statements are re-enabled.
294  // Which means that handler2 can read the record changes directly.
295  BOOST_CHECK(handler->Write(key, value2, /*fOverwrite=*/true));
296  BOOST_CHECK(handler2->Read(key, read_value));
297  BOOST_CHECK_EQUAL(read_value, value2);
298 }
299 
301 } // namespace wallet
virtual std::unique_ptr< DatabaseCursor > GetNewPrefixCursor(std::span< const std::byte > prefix)=0
Bilingual messages:
Definition: translation.h:24
static SerializeData StringData(std::string_view str)
Definition: db_tests.cpp:40
std::unique_ptr< SQLiteDatabase > MakeSQLiteDatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
Definition: sqlite.cpp:691
std::map< SerializeData, SerializeData, std::less<> > MockableData
Definition: util.h:49
const char * prefix
Definition: rest.cpp:1141
int Exec(SQLiteDatabase &database, const std::string &statement) override
Definition: db_tests.cpp:219
Definition: common.h:29
bool(* handler)(const std::any &context, HTTPRequest *req, const std::string &strReq)
Definition: rest.cpp:1142
RAII class that provides access to a WalletDatabase.
Definition: db.h:50
Basic testing setup.
Definition: setup_common.h:64
virtual int Exec(SQLiteDatabase &database, const std::string &statement)
Definition: sqlite.cpp:385
SQliteExecHandler m_base_exec
Definition: db_tests.cpp:215
const_iterator end() const
Definition: streams.h:165
BOOST_FIXTURE_TEST_SUITE(cuckoocache_tests, BasicTestingSetup)
Test Suite for CuckooCache.
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:132
std::vector< std::byte, zero_after_free_allocator< std::byte > > SerializeData
Byte-vector that clears its contents before deletion.
Definition: zeroafterfree.h:44
BOOST_AUTO_TEST_SUITE_END()
std::span< const std::byte > StringBytes(std::string_view str)
Definition: db_tests.cpp:35
static std::vector< std::unique_ptr< WalletDatabase > > TestDatabases(const fs::path &path_root)
Definition: db_tests.cpp:63
constexpr int TEST_SQLITE_ERROR
Definition: db_tests.cpp:210
const_iterator begin() const
Definition: streams.h:163
DatabaseStatus
Definition: db.h:186
BOOST_AUTO_TEST_CASE(bnb_test)
std::unique_ptr< WalletDatabase > CreateMockableWalletDatabase(MockableData records)
Definition: util.cpp:211
DbExecBlocker(std::set< std::string > blocked_statements)
Definition: db_tests.cpp:218
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:17
static void CheckPrefix(DatabaseBatch &batch, std::span< const std::byte > prefix, MockableData expected)
Definition: db_tests.cpp:46
std::set< std::string > m_blocked_statements
Definition: db_tests.cpp:216
Class responsible for executing SQL statements in SQLite databases.
Definition: sqlite.h:43
An instance of this class represents one SQLite3 database.
Definition: sqlite.h:102
An instance of this class represents one database.
Definition: db.h:129
#define Assert(val)
Identity function.
Definition: check.h:113
#define BOOST_CHECK(expr)
Definition: object.cpp:16