txdb.cpp raw
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-present The Bitcoin Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6 #include <txdb.h>
7
8 #include <coins.h>
9 #include <dbwrapper.h>
10 #include <logging/timer.h>
11 #include <primitives/transaction.h>
12 #include <random.h>
13 #include <serialize.h>
14 #include <uint256.h>
15 #include <util/byte_units.h>
16 #include <util/log.h>
17 #include <util/threadnames.h>
18 #include <util/vector.h>
19
20 #include <cassert>
21 #include <chrono>
22 #include <cstdlib>
23 #include <exception>
24 #include <future>
25 #include <iterator>
26 #include <utility>
27
28 static constexpr uint8_t DB_COIN{'C'};
29 static constexpr uint8_t DB_BEST_BLOCK{'B'};
30 static constexpr uint8_t DB_HEAD_BLOCKS{'H'};
31 // Keys used in previous version that might still be found in the DB:
32 static constexpr uint8_t DB_COINS{'c'};
33
34 // Threshold for warning when writing this many dirty cache entries to disk.
35 static constexpr size_t WARN_FLUSH_COINS_COUNT{10'000'000};
36
37 bool CCoinsViewDB::NeedsUpgrade()
38 {
39 std::unique_ptr<CDBIterator> cursor{m_db->NewIterator()};
40 // DB_COINS was deprecated in v0.15.0, commit
41 // 1088b02f0ccd7358d2b7076bb9e122d59d502d02
42 cursor->Seek(std::make_pair(DB_COINS, uint256{}));
43 return cursor->Valid();
44 }
45
46 namespace {
47
48 struct CoinEntry {
49 COutPoint* outpoint;
50 uint8_t key{DB_COIN};
51 explicit CoinEntry(const COutPoint* ptr) : outpoint(const_cast<COutPoint*>(ptr)) {}
52
53 SERIALIZE_METHODS(CoinEntry, obj) { READWRITE(obj.key, obj.outpoint->hash, VARINT(obj.outpoint->n)); }
54 };
55
56 } // namespace
57
58 CCoinsViewDB::CCoinsViewDB(DBParams db_params, CoinsViewOptions options) :
59 m_db_params{std::move(db_params)},
60 m_options{std::move(options)},
61 m_db{std::make_unique<CDBWrapper>(m_db_params)} { }
62
63 CCoinsViewDB::~CCoinsViewDB()
64 {
65 if (m_compaction.valid()) {
66 if (m_compaction.wait_for(std::chrono::seconds{0}) != std::future_status::ready) {
67 LogInfo("Waiting for background chainstate compaction of %s", fs::PathToString(m_db_params.path));
68 }
69 m_compaction.wait();
70 }
71 }
72
73 void CCoinsViewDB::ResizeCache(size_t new_cache_size)
74 {
75 // We can't do this operation with an in-memory DB since we'll lose all the coins upon
76 // reset.
77 if (!m_db_params.memory_only) {
78 LOCK(m_db_mutex);
79 // Have to do a reset first to get the original `m_db` state to release its
80 // filesystem lock.
81 m_db.reset();
82 m_db_params.cache_bytes = new_cache_size;
83 m_db_params.wipe_data = false;
84 m_db = std::make_unique<CDBWrapper>(m_db_params);
85 }
86 }
87
88 std::optional<Coin> CCoinsViewDB::GetCoin(const COutPoint& outpoint) const
89 {
90 if (Coin coin; m_db->Read(CoinEntry(&outpoint), coin)) {
91 Assert(!coin.IsSpent()); // The UTXO database should never contain spent coins
92 return coin;
93 }
94 return std::nullopt;
95 }
96
97 std::optional<Coin> CCoinsViewDB::PeekCoin(const COutPoint& outpoint) const
98 {
99 return GetCoin(outpoint);
100 }
101
102 bool CCoinsViewDB::HaveCoin(const COutPoint& outpoint) const
103 {
104 return m_db->Exists(CoinEntry(&outpoint));
105 }
106
107 uint256 CCoinsViewDB::GetBestBlock() const {
108 uint256 hashBestChain;
109 if (!m_db->Read(DB_BEST_BLOCK, hashBestChain))
110 return uint256();
111 return hashBestChain;
112 }
113
114 std::vector<uint256> CCoinsViewDB::GetHeadBlocks() const {
115 std::vector<uint256> vhashHeadBlocks;
116 if (!m_db->Read(DB_HEAD_BLOCKS, vhashHeadBlocks)) {
117 return std::vector<uint256>();
118 }
119 return vhashHeadBlocks;
120 }
121
122 void CCoinsViewDB::BatchWrite(CoinsViewCacheCursor& cursor, const uint256& block_hash)
123 {
124 CDBBatch batch(*m_db);
125 size_t count = 0;
126 const size_t dirty_count{cursor.GetDirtyCount()};
127 assert(!block_hash.IsNull());
128
129 uint256 old_tip = GetBestBlock();
130 if (old_tip.IsNull()) {
131 // We may be in the middle of replaying.
132 std::vector<uint256> old_heads = GetHeadBlocks();
133 if (old_heads.size() == 2) {
134 if (old_heads[0] != block_hash) {
135 LogError("The coins database detected an inconsistent state, likely due to a previous crash or shutdown. You will need to restart bitcoind with the -reindex-chainstate or -reindex configuration option.\n");
136 }
137 assert(old_heads[0] == block_hash);
138 old_tip = old_heads[1];
139 }
140 }
141
142 if (dirty_count > WARN_FLUSH_COINS_COUNT) LogWarning("Flushing large (%d entries) UTXO set to disk, it may take several minutes", dirty_count);
143 LOG_TIME_MILLIS_WITH_CATEGORY(strprintf("write coins cache to disk (%d out of %d cached coins)",
144 dirty_count, cursor.GetTotalCount()), BCLog::BENCH);
145
146 // In the first batch, mark the database as being in the middle of a
147 // transition from old_tip to block_hash.
148 // A vector is used for future extensibility, as we may want to support
149 // interrupting after partial writes from multiple independent reorgs.
150 batch.Erase(DB_BEST_BLOCK);
151 batch.Write(DB_HEAD_BLOCKS, Vector(block_hash, old_tip));
152
153 for (auto it{cursor.Begin()}; it != cursor.End();) {
154 if (it->second.IsDirty()) {
155 CoinEntry entry(&it->first);
156 if (it->second.coin.IsSpent()) {
157 batch.Erase(entry);
158 } else {
159 batch.Write(entry, it->second.coin);
160 }
161 }
162 count++;
163 it = cursor.NextAndMaybeErase(*it);
164 if (batch.ApproximateSize() > m_options.batch_write_bytes) {
165 LogDebug(BCLog::COINDB, "Writing partial batch of %.2f MiB\n", batch.ApproximateSize() / double(1_MiB));
166
167 m_db->WriteBatch(batch);
168 batch.Clear();
169 if (m_options.simulate_crash_ratio) {
170 static FastRandomContext rng;
171 if (rng.randrange(m_options.simulate_crash_ratio) == 0) {
172 LogError("Simulating a crash. Goodbye.");
173 _Exit(0);
174 }
175 }
176 }
177 }
178
179 // In the last batch, mark the database as consistent with block_hash again.
180 batch.Erase(DB_HEAD_BLOCKS);
181 batch.Write(DB_BEST_BLOCK, block_hash);
182
183 LogDebug(BCLog::COINDB, "Writing final batch of %.2f MiB\n", batch.ApproximateSize() / double(1_MiB));
184 m_db->WriteBatch(batch);
185 LogDebug(BCLog::COINDB, "Committed %u changed transaction outputs (out of %u) to coin database...", (unsigned int)dirty_count, (unsigned int)count);
186 }
187
188 size_t CCoinsViewDB::EstimateSize() const
189 {
190 return m_db->EstimateSize(DB_COIN, uint8_t(DB_COIN + 1));
191 }
192
193 std::optional<std::string> CCoinsViewDB::GetDBProperty(const std::string& property)
194 {
195 return m_db->GetProperty(property);
196 }
197
198 std::shared_future<void> CCoinsViewDB::CompactFullAsync()
199 {
200 AssertLockHeld(::cs_main);
201 if (m_compaction.valid() && m_compaction.wait_for(std::chrono::seconds{0}) != std::future_status::ready) return m_compaction;
202 m_compaction = std::async(std::launch::async, [this] {
203 try {
204 util::ThreadRename("utxocompact");
205 LOCK(m_db_mutex);
206
207 LogDebug(BCLog::COINDB, "Starting chainstate compaction of %s", fs::PathToString(m_db_params.path));
208 m_db->CompactFull();
209 LogDebug(BCLog::COINDB, "Finished chainstate compaction of %s", fs::PathToString(m_db_params.path));
210 } catch (const std::exception& e) {
211 LogWarning("Failed chainstate compaction (%s)", e.what());
212 }
213 }).share();
214 return m_compaction;
215 }
216
217 /** Specialization of CCoinsViewCursor to iterate over a CCoinsViewDB */
218 class CCoinsViewDBCursor: public CCoinsViewCursor
219 {
220 public:
221 // Prefer using CCoinsViewDB::Cursor() since we want to perform some
222 // cache warmup on instantiation.
223 CCoinsViewDBCursor(CDBIterator* pcursorIn, const uint256& in_block_hash):
224 CCoinsViewCursor(in_block_hash), pcursor(pcursorIn) {}
225 ~CCoinsViewDBCursor() = default;
226
227 bool GetKey(COutPoint &key) const override;
228 bool GetValue(Coin &coin) const override;
229
230 bool Valid() const override;
231 void Next() override;
232
233 private:
234 std::unique_ptr<CDBIterator> pcursor;
235 std::pair<char, COutPoint> keyTmp;
236
237 friend class CCoinsViewDB;
238 };
239
240 std::unique_ptr<CCoinsViewCursor> CCoinsViewDB::Cursor() const
241 {
242 auto i = std::make_unique<CCoinsViewDBCursor>(
243 const_cast<CDBWrapper&>(*m_db).NewIterator(), GetBestBlock());
244 /* It seems that there are no "const iterators" for LevelDB. Since we
245 only need read operations on it, use a const-cast to get around
246 that restriction. */
247 i->pcursor->Seek(DB_COIN);
248 // Cache key of first record
249 if (i->pcursor->Valid()) {
250 CoinEntry entry(&i->keyTmp.second);
251 i->pcursor->GetKey(entry);
252 i->keyTmp.first = entry.key;
253 } else {
254 i->keyTmp.first = 0; // Make sure Valid() and GetKey() return false
255 }
256 return i;
257 }
258
259 bool CCoinsViewDBCursor::GetKey(COutPoint &key) const
260 {
261 // Return cached key
262 if (keyTmp.first == DB_COIN) {
263 key = keyTmp.second;
264 return true;
265 }
266 return false;
267 }
268
269 bool CCoinsViewDBCursor::GetValue(Coin &coin) const
270 {
271 return pcursor->GetValue(coin);
272 }
273
274 bool CCoinsViewDBCursor::Valid() const
275 {
276 return keyTmp.first == DB_COIN;
277 }
278
279 void CCoinsViewDBCursor::Next()
280 {
281 pcursor->Next();
282 CoinEntry entry(&keyTmp.second);
283 if (!pcursor->Valid() || !pcursor->GetKey(entry)) {
284 keyTmp.first = 0; // Invalidate cached key after last record so that Valid() and GetKey() return false
285 } else {
286 keyTmp.first = entry.key;
287 }
288 }
289