Branch data Line data Source code
1 : : // Copyright (c) 2018-2022 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 <config/bitcoin-config.h> // IWYU pragma: keep
6 : :
7 : : #include <boost/test/unit_test.hpp>
8 : :
9 : : #include <test/util/setup_common.h>
10 : : #include <util/check.h>
11 : : #include <util/fs.h>
12 : : #include <util/translation.h>
13 : : #ifdef USE_BDB
14 : : #include <wallet/bdb.h>
15 : : #endif
16 : : #ifdef USE_SQLITE
17 : : #include <wallet/sqlite.h>
18 : : #endif
19 : : #include <wallet/migrate.h>
20 : : #include <wallet/test/util.h>
21 : : #include <wallet/walletutil.h> // for WALLET_FLAG_DESCRIPTORS
22 : :
23 : : #include <fstream>
24 : : #include <memory>
25 : : #include <string>
26 : :
27 : 0 : inline std::ostream& operator<<(std::ostream& os, const std::pair<const SerializeData, SerializeData>& kv)
28 : : {
29 : 0 : Span key{kv.first}, value{kv.second};
30 : 0 : os << "(\"" << std::string_view{reinterpret_cast<const char*>(key.data()), key.size()} << "\", \""
31 : 0 : << std::string_view{reinterpret_cast<const char*>(key.data()), key.size()} << "\")";
32 : 0 : return os;
33 : : }
34 : :
35 : : namespace wallet {
36 : :
37 : 30 : static Span<const std::byte> StringBytes(std::string_view str)
38 : : {
39 : 30 : return AsBytes<const char>({str.data(), str.size()});
40 : : }
41 : :
42 : 14 : static SerializeData StringData(std::string_view str)
43 : : {
44 : 14 : auto bytes = StringBytes(str);
45 : 14 : return SerializeData{bytes.begin(), bytes.end()};
46 : : }
47 : :
48 : 16 : static void CheckPrefix(DatabaseBatch& batch, Span<const std::byte> prefix, MockableData expected)
49 : : {
50 : 16 : std::unique_ptr<DatabaseCursor> cursor = batch.GetNewPrefixCursor(prefix);
51 : 16 : MockableData actual;
52 : 136 : while (true) {
53 : 76 : DataStream key, value;
54 [ + - ]: 76 : DatabaseCursor::Status status = cursor->Next(key, value);
55 [ + + ]: 76 : if (status == DatabaseCursor::Status::DONE) break;
56 [ + - + - : 120 : BOOST_CHECK(status == DatabaseCursor::Status::MORE);
+ - ]
57 [ + - + - : 120 : BOOST_CHECK(
+ - + - +
- ]
58 : : actual.emplace(SerializeData(key.begin(), key.end()), SerializeData(value.begin(), value.end())).second);
59 : 76 : }
60 [ + - + - : 32 : BOOST_CHECK_EQUAL_COLLECTIONS(actual.begin(), actual.end(), expected.begin(), expected.end());
+ - ]
61 : 16 : }
62 : :
63 : : BOOST_FIXTURE_TEST_SUITE(db_tests, BasicTestingSetup)
64 : :
65 : 9 : static std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& path, fs::path& database_filename)
66 : : {
67 : 9 : fs::path data_file = BDBDataFile(path);
68 [ + - ]: 18 : database_filename = data_file.filename();
69 [ + - + - ]: 36 : return GetBerkeleyEnv(data_file.parent_path(), false);
70 : 9 : }
71 : :
72 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(getwalletenv_file)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
73 : : {
74 : 1 : fs::path test_name = "test_name.dat";
75 [ + - ]: 1 : const fs::path datadir = m_args.GetDataDirNet();
76 [ + - + - ]: 2 : fs::path file_path = datadir / test_name;
77 [ + - ]: 1 : std::ofstream f{file_path};
78 [ + - ]: 1 : f.close();
79 : :
80 : 1 : fs::path filename;
81 [ + - ]: 1 : std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
82 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(filename, test_name);
83 [ + - + - : 2 : BOOST_CHECK_EQUAL(env->Directory(), datadir);
+ - + - +
- ]
84 : 5 : }
85 : :
86 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(getwalletenv_directory)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
87 : : {
88 : 1 : fs::path expected_name = "wallet.dat";
89 [ + - ]: 1 : const fs::path datadir = m_args.GetDataDirNet();
90 : :
91 : 1 : fs::path filename;
92 [ + - ]: 1 : std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(datadir, filename);
93 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(filename, expected_name);
94 [ + - + - : 2 : BOOST_CHECK_EQUAL(env->Directory(), datadir);
+ - + - ]
95 : 4 : }
96 : :
97 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_multiple)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
98 : : {
99 [ + - ]: 2 : fs::path datadir = m_args.GetDataDirNet() / "1";
100 [ + - + - ]: 2 : fs::path datadir_2 = m_args.GetDataDirNet() / "2";
101 : 1 : fs::path filename;
102 : :
103 [ + - ]: 1 : std::shared_ptr<BerkeleyEnvironment> env_1 = GetWalletEnv(datadir, filename);
104 [ + - ]: 1 : std::shared_ptr<BerkeleyEnvironment> env_2 = GetWalletEnv(datadir, filename);
105 [ + - ]: 1 : std::shared_ptr<BerkeleyEnvironment> env_3 = GetWalletEnv(datadir_2, filename);
106 : :
107 [ + - + - : 2 : BOOST_CHECK(env_1 == env_2);
+ - ]
108 [ + - + - : 2 : BOOST_CHECK(env_2 != env_3);
+ - ]
109 [ + - + - ]: 6 : }
110 : :
111 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_free_instance)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
112 : : {
113 [ + - ]: 2 : fs::path datadir = gArgs.GetDataDirNet() / "1";
114 [ + - + - ]: 2 : fs::path datadir_2 = gArgs.GetDataDirNet() / "2";
115 : 1 : fs::path filename;
116 : :
117 [ + - ]: 1 : std::shared_ptr <BerkeleyEnvironment> env_1_a = GetWalletEnv(datadir, filename);
118 [ + - ]: 1 : std::shared_ptr <BerkeleyEnvironment> env_2_a = GetWalletEnv(datadir_2, filename);
119 : 1 : env_1_a.reset();
120 : :
121 [ + - ]: 1 : std::shared_ptr<BerkeleyEnvironment> env_1_b = GetWalletEnv(datadir, filename);
122 [ + - ]: 1 : std::shared_ptr<BerkeleyEnvironment> env_2_b = GetWalletEnv(datadir_2, filename);
123 : :
124 [ + - + - : 2 : BOOST_CHECK(env_1_a != env_1_b);
+ - ]
125 [ + - + - : 2 : BOOST_CHECK(env_2_a == env_2_b);
+ - ]
126 [ + - + - : 6 : }
- + ]
127 : :
128 : 4 : static std::vector<std::unique_ptr<WalletDatabase>> TestDatabases(const fs::path& path_root)
129 : : {
130 : 4 : std::vector<std::unique_ptr<WalletDatabase>> dbs;
131 [ + - ]: 4 : DatabaseOptions options;
132 : 4 : DatabaseStatus status;
133 [ + - ]: 4 : bilingual_str error;
134 : : #ifdef USE_BDB
135 [ + - + - : 12 : dbs.emplace_back(MakeBerkeleyDatabase(path_root / "bdb", options, status, error));
+ - + - ]
136 : : // Needs BDB to make the DB to read
137 [ + - + - : 16 : dbs.emplace_back(std::make_unique<BerkeleyRODatabase>(BDBDataFile(path_root / "bdb"), /*open=*/false));
+ - + - +
- ]
138 : : #endif
139 : : #ifdef USE_SQLITE
140 [ + - + - : 12 : dbs.emplace_back(MakeSQLiteDatabase(path_root / "sqlite", options, status, error));
+ - + - ]
141 : : #endif
142 [ + - + - ]: 4 : dbs.emplace_back(CreateMockableWalletDatabase());
143 : 4 : return dbs;
144 : 4 : }
145 : :
146 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(db_cursor_prefix_range_test)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
147 : : {
148 : : // Test each supported db
149 [ + + ]: 5 : for (const auto& database : TestDatabases(m_path_root)) {
150 [ + - ]: 4 : std::vector<std::string> prefixes = {"", "FIRST", "SECOND", "P\xfe\xff", "P\xff\x01", "\xff\xff"};
151 : :
152 [ + - + - ]: 4 : std::unique_ptr<DatabaseBatch> handler = Assert(database)->MakeBatch();
153 [ + - + + ]: 4 : if (dynamic_cast<BerkeleyRODatabase*>(database.get())) {
154 : : // For BerkeleyRO, open the file now. This must happen after BDB has written to the file
155 [ + - ]: 1 : database->Open();
156 : : } else {
157 : : // Write elements to it if not berkeleyro
158 [ + + ]: 33 : for (unsigned int i = 0; i < 10; i++) {
159 [ + + ]: 210 : for (const auto& prefix : prefixes) {
160 [ + - + - : 360 : BOOST_CHECK(handler->Write(std::make_pair(prefix, i), i));
+ - + - ]
161 : : }
162 : : }
163 : : }
164 : :
165 : : // Now read all the items by prefix and verify that each element gets parsed correctly
166 [ + + ]: 28 : for (const auto& prefix : prefixes) {
167 : 24 : DataStream s_prefix;
168 [ + - ]: 24 : s_prefix << prefix;
169 [ + - ]: 24 : std::unique_ptr<DatabaseCursor> cursor = handler->GetNewPrefixCursor(s_prefix);
170 : 24 : DataStream key;
171 : 24 : DataStream value;
172 [ + + ]: 264 : for (int i = 0; i < 10; i++) {
173 [ + - ]: 240 : DatabaseCursor::Status status = cursor->Next(key, value);
174 [ + - + - ]: 240 : BOOST_CHECK_EQUAL(status, DatabaseCursor::Status::MORE);
175 : :
176 [ + - ]: 240 : std::string key_back;
177 : 240 : unsigned int i_back;
178 [ + - + - ]: 240 : key >> key_back >> i_back;
179 [ + - + - ]: 240 : BOOST_CHECK_EQUAL(key_back, prefix);
180 : :
181 : 240 : unsigned int value_back;
182 [ + - ]: 240 : value >> value_back;
183 [ + - + - ]: 240 : BOOST_CHECK_EQUAL(value_back, i_back);
184 : 240 : }
185 : :
186 : : // Let's now read it once more, it should return DONE
187 [ + - + - : 48 : BOOST_CHECK(cursor->Next(key, value) == DatabaseCursor::Status::DONE);
+ - ]
188 : 24 : }
189 [ + - ]: 4 : handler.reset();
190 [ + - ]: 4 : database->Close();
191 : 5 : }
192 : 1 : }
193 : :
194 : : // Lower level DatabaseBase::GetNewPrefixCursor test, to cover cases that aren't
195 : : // covered in the higher level test above. The higher level test uses
196 : : // serialized strings which are prefixed with string length, so it doesn't test
197 : : // truly empty prefixes or prefixes that begin with \xff
198 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(db_cursor_prefix_byte_test)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
199 : : {
200 : 1 : const MockableData::value_type
201 [ + - + - ]: 1 : e{StringData(""), StringData("e")},
202 [ + - + - : 1 : p{StringData("prefix"), StringData("p")},
+ - ]
203 [ + - + - : 1 : ps{StringData("prefixsuffix"), StringData("ps")},
+ - ]
204 [ + - + - : 1 : f{StringData("\xff"), StringData("f")},
+ - ]
205 [ + - + - : 1 : fs{StringData("\xffsuffix"), StringData("fs")},
+ - ]
206 [ + - + - : 1 : ff{StringData("\xff\xff"), StringData("ff")},
+ - ]
207 [ + - + - ]: 1 : ffs{StringData("\xff\xffsuffix"), StringData("ffs")};
208 [ + - + + ]: 5 : for (const auto& database : TestDatabases(m_path_root)) {
209 [ + - ]: 4 : std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();
210 : :
211 [ + - + + ]: 4 : if (dynamic_cast<BerkeleyRODatabase*>(database.get())) {
212 : : // For BerkeleyRO, open the file now. This must happen after BDB has written to the file
213 [ + - ]: 1 : database->Open();
214 : : } else {
215 : : // Write elements to it if not berkeleyro
216 [ + - + - : 66 : for (const auto& [k, v] : {e, p, ps, f, fs, ff, ffs}) {
+ - + - +
- + - + -
- + + + -
- ]
217 [ - + ]: 21 : batch->Write(Span{k}, Span{v});
218 [ + + - - ]: 24 : }
219 : : }
220 : :
221 [ + - + - : 36 : CheckPrefix(*batch, StringBytes(""), {e, p, ps, f, fs, ff, ffs});
+ + - - ]
222 [ + - + - : 16 : CheckPrefix(*batch, StringBytes("prefix"), {p, ps});
+ + - - ]
223 [ + - + - : 24 : CheckPrefix(*batch, StringBytes("\xff"), {f, fs, ff, ffs});
+ + - - ]
224 [ + - + - : 16 : CheckPrefix(*batch, StringBytes("\xff\xff"), {ff, ffs});
+ + - - ]
225 [ + - ]: 4 : batch.reset();
226 [ + - ]: 4 : database->Close();
227 : 5 : }
228 [ + - + - : 23 : }
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- - - - -
- - - - ]
229 : :
230 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(db_availability_after_write_error)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
231 : : {
232 : : // Ensures the database remains accessible without deadlocking after a write error.
233 : : // To simulate the behavior, record overwrites are disallowed, and the test verifies
234 : : // that the database remains active after failing to store an existing record.
235 [ + + ]: 5 : for (const auto& database : TestDatabases(m_path_root)) {
236 [ + - + + ]: 4 : if (dynamic_cast<BerkeleyRODatabase*>(database.get())) {
237 : : // Skip this test if BerkeleyRO
238 : 1 : continue;
239 : : }
240 : : // Write original record
241 [ + - ]: 3 : std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();
242 [ + - ]: 3 : std::string key = "key";
243 [ + - ]: 3 : std::string value = "value";
244 [ + - ]: 3 : std::string value2 = "value_2";
245 [ + - + - : 6 : BOOST_CHECK(batch->Write(key, value));
+ - + - ]
246 : : // Attempt to overwrite the record (expect failure)
247 [ + - + - : 6 : BOOST_CHECK(!batch->Write(key, value2, /*fOverwrite=*/false));
+ - + - ]
248 : : // Successfully overwrite the record
249 [ + - + - : 6 : BOOST_CHECK(batch->Write(key, value2, /*fOverwrite=*/true));
+ - + - ]
250 : : // Sanity-check; read and verify the overwritten value
251 [ + - ]: 3 : std::string read_value;
252 [ + - + - : 6 : BOOST_CHECK(batch->Read(key, read_value));
+ - + - ]
253 [ + - + - ]: 3 : BOOST_CHECK_EQUAL(read_value, value2);
254 : 4 : }
255 : 1 : }
256 : :
257 : : // Verify 'ErasePrefix' functionality using db keys similar to the ones used by the wallet.
258 : : // Keys are in the form of std::pair<TYPE, ENTRY_ID>
259 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(erase_prefix)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
260 : : {
261 : 1 : const std::string key = "key";
262 [ + - ]: 1 : const std::string key2 = "key2";
263 [ + - ]: 1 : const std::string value = "value";
264 [ + - ]: 1 : const std::string value2 = "value_2";
265 [ + - + - : 24 : auto make_key = [](std::string type, std::string id) { return std::make_pair(type, id); };
+ - + - +
- + - + -
+ - ]
266 : :
267 [ + - + + ]: 5 : for (const auto& database : TestDatabases(m_path_root)) {
268 [ + - + + ]: 4 : if (dynamic_cast<BerkeleyRODatabase*>(database.get())) {
269 : : // Skip this test if BerkeleyRO
270 : 1 : continue;
271 : : }
272 [ + - ]: 3 : std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();
273 : :
274 : : // Write two entries with the same key type prefix, a third one with a different prefix
275 : : // and a fourth one with the type-id values inverted
276 [ + - + - : 12 : BOOST_CHECK(batch->Write(make_key(key, value), value));
+ - + - +
- + - ]
277 [ + - + - : 12 : BOOST_CHECK(batch->Write(make_key(key, value2), value2));
+ - + - +
- + - ]
278 [ + - + - : 12 : BOOST_CHECK(batch->Write(make_key(key2, value), value));
+ - + - +
- + - ]
279 [ + - + - : 12 : BOOST_CHECK(batch->Write(make_key(value, key), value));
+ - + - +
- + - ]
280 : :
281 : : // Erase the ones with the same prefix and verify result
282 [ + - + - : 6 : BOOST_CHECK(batch->TxnBegin());
+ - + - ]
283 [ + - + - : 6 : BOOST_CHECK(batch->ErasePrefix(DataStream() << key));
+ - + - +
- ]
284 [ + - + - : 6 : BOOST_CHECK(batch->TxnCommit());
+ - + - ]
285 : :
286 [ + - + - : 12 : BOOST_CHECK(!batch->Exists(make_key(key, value)));
+ - + - +
- + - ]
287 [ + - + - : 12 : BOOST_CHECK(!batch->Exists(make_key(key, value2)));
+ - + - +
- + - ]
288 : : // Also verify that entries with a different prefix were not erased
289 [ + - + - : 12 : BOOST_CHECK(batch->Exists(make_key(key2, value)));
+ - + - +
- + - ]
290 [ + - + - : 12 : BOOST_CHECK(batch->Exists(make_key(value, key)));
+ - + - +
- ]
291 : 4 : }
292 : 1 : }
293 : :
294 : : #ifdef USE_SQLITE
295 : :
296 : : // Test-only statement execution error
297 : : constexpr int TEST_SQLITE_ERROR = -999;
298 : :
299 : : class DbExecBlocker : public SQliteExecHandler
300 : : {
301 : : private:
302 : : SQliteExecHandler m_base_exec;
303 : : std::set<std::string> m_blocked_statements;
304 : : public:
305 [ + - ]: 1 : DbExecBlocker(std::set<std::string> blocked_statements) : m_blocked_statements(blocked_statements) {}
306 : 1 : int Exec(SQLiteDatabase& database, const std::string& statement) override {
307 [ - + ]: 1 : if (m_blocked_statements.contains(statement)) return TEST_SQLITE_ERROR;
308 : 0 : return m_base_exec.Exec(database, statement);
309 : : }
310 : : };
311 : :
312 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(txn_close_failure_dangling_txn)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
313 : : {
314 : : // Verifies that there is no active dangling, to-be-reversed db txn
315 : : // after the batch object that initiated it is destroyed.
316 [ + - ]: 1 : DatabaseOptions options;
317 : 1 : DatabaseStatus status;
318 [ + - ]: 1 : bilingual_str error;
319 [ + - + - : 3 : std::unique_ptr<SQLiteDatabase> database = MakeSQLiteDatabase(m_path_root / "sqlite", options, status, error);
+ - ]
320 : :
321 [ + - ]: 1 : std::string key = "key";
322 [ + - ]: 1 : std::string value = "value";
323 : :
324 [ + - ]: 1 : std::unique_ptr<SQLiteBatch> batch = std::make_unique<SQLiteBatch>(*database);
325 [ + - + - : 2 : BOOST_CHECK(batch->TxnBegin());
+ - + - ]
326 [ + - + - : 2 : BOOST_CHECK(batch->Write(key, value));
+ - ]
327 : : // Set a handler to prevent txn abortion during destruction.
328 : : // Mimicking a db statement execution failure.
329 [ + - + + : 3 : batch->SetExecHandler(std::make_unique<DbExecBlocker>(std::set<std::string>{"ROLLBACK TRANSACTION"}));
+ - + - +
+ - - -
- ]
330 : : // Destroy batch
331 [ + - ]: 1 : batch.reset();
332 : :
333 : : // Ensure there is no dangling, to-be-reversed db txn
334 [ + - + - : 2 : BOOST_CHECK(!database->HasActiveTxn());
+ - + - ]
335 : :
336 : : // And, just as a sanity check; verify that new batchs only write what they suppose to write
337 : : // and nothing else.
338 [ + - ]: 1 : std::string key2 = "key2";
339 [ + - ]: 1 : std::unique_ptr<SQLiteBatch> batch2 = std::make_unique<SQLiteBatch>(*database);
340 [ + - + - : 2 : BOOST_CHECK(batch2->Write(key2, value));
+ - + - ]
341 : : // The first key must not exist
342 [ + - + - : 2 : BOOST_CHECK(!batch2->Exists(key));
+ - ]
343 : 2 : }
344 : :
345 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(concurrent_txn_dont_interfere)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
346 : : {
347 : 1 : std::string key = "key";
348 [ + - ]: 1 : std::string value = "value";
349 [ + - ]: 1 : std::string value2 = "value_2";
350 : :
351 [ + - ]: 1 : DatabaseOptions options;
352 : 1 : DatabaseStatus status;
353 [ + - ]: 1 : bilingual_str error;
354 [ + - + - : 3 : const auto& database = MakeSQLiteDatabase(m_path_root / "sqlite", options, status, error);
+ - ]
355 : :
356 [ + - + - ]: 1 : std::unique_ptr<DatabaseBatch> handler = Assert(database)->MakeBatch();
357 : :
358 : : // Verify concurrent db transactions does not interfere between each other.
359 : : // Start db txn, write key and check the key does exist within the db txn.
360 [ + - + - : 2 : BOOST_CHECK(handler->TxnBegin());
+ - + - ]
361 [ + - + - : 2 : BOOST_CHECK(handler->Write(key, value));
+ - + - ]
362 [ + - + - : 2 : BOOST_CHECK(handler->Exists(key));
+ - + - ]
363 : :
364 : : // But, the same key, does not exist in another handler
365 [ + - + - ]: 1 : std::unique_ptr<DatabaseBatch> handler2 = Assert(database)->MakeBatch();
366 [ + - + - : 2 : BOOST_CHECK(handler2->Exists(key));
+ - + - ]
367 : :
368 : : // Attempt to commit the handler txn calling the handler2 methods.
369 : : // Which, must not be possible.
370 [ + - + - : 2 : BOOST_CHECK(!handler2->TxnCommit());
+ - + - ]
371 [ + - + - : 2 : BOOST_CHECK(!handler2->TxnAbort());
+ - + - ]
372 : :
373 : : // Only the first handler can commit the changes.
374 [ + - + - : 2 : BOOST_CHECK(handler->TxnCommit());
+ - + - ]
375 : : // And, once commit is completed, handler2 can read the record
376 [ + - ]: 1 : std::string read_value;
377 [ + - + - : 2 : BOOST_CHECK(handler2->Read(key, read_value));
+ - + - ]
378 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(read_value, value);
379 : :
380 : : // Also, once txn is committed, single write statements are re-enabled.
381 : : // Which means that handler2 can read the record changes directly.
382 [ + - + - : 2 : BOOST_CHECK(handler->Write(key, value2, /*fOverwrite=*/true));
+ - + - ]
383 [ + - + - : 2 : BOOST_CHECK(handler2->Read(key, read_value));
+ - + - ]
384 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(read_value, value2);
385 : 2 : }
386 : : #endif // USE_SQLITE
387 : :
388 : : BOOST_AUTO_TEST_SUITE_END()
389 : : } // namespace wallet
|