Bitcoin Core 31.1.0
P2P Digital Currency
Loading...
Searching...
No Matches
txdb.cpp
Go to the documentation of this file.
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>
12#include <random.h>
13#include <serialize.h>
14#include <uint256.h>
15#include <util/log.h>
16#include <util/threadnames.h>
17#include <util/vector.h>
18
19#include <cassert>
20#include <chrono>
21#include <cstdlib>
22#include <exception>
23#include <future>
24#include <iterator>
25#include <utility>
26
27static constexpr uint8_t DB_COIN{'C'};
28static constexpr uint8_t DB_BEST_BLOCK{'B'};
29static constexpr uint8_t DB_HEAD_BLOCKS{'H'};
30// Keys used in previous version that might still be found in the DB:
31static constexpr uint8_t DB_COINS{'c'};
32
33// Threshold for warning when writing this many dirty cache entries to disk.
34static constexpr size_t WARN_FLUSH_COINS_COUNT{10'000'000};
35
37{
38 std::unique_ptr<CDBIterator> cursor{m_db->NewIterator()};
39 // DB_COINS was deprecated in v0.15.0, commit
40 // 1088b02f0ccd7358d2b7076bb9e122d59d502d02
41 cursor->Seek(std::make_pair(DB_COINS, uint256{}));
42 return cursor->Valid();
43}
44
45namespace {
46
47struct CoinEntry {
48 COutPoint* outpoint;
49 uint8_t key{DB_COIN};
50 explicit CoinEntry(const COutPoint* ptr) : outpoint(const_cast<COutPoint*>(ptr)) {}
51
52 SERIALIZE_METHODS(CoinEntry, obj) { READWRITE(obj.key, obj.outpoint->hash, VARINT(obj.outpoint->n)); }
53};
54
55} // namespace
56
58 m_db_params{std::move(db_params)},
59 m_options{std::move(options)},
60 m_db{std::make_unique<CDBWrapper>(m_db_params)} { }
61
63{
64 if (m_compaction.valid()) {
65 if (m_compaction.wait_for(std::chrono::seconds{0}) != std::future_status::ready) {
66 LogInfo("Waiting for background chainstate compaction of %s", fs::PathToString(m_db_params.path));
67 }
68 m_compaction.wait();
69 }
70}
71
72void CCoinsViewDB::ResizeCache(size_t new_cache_size)
73{
74 // We can't do this operation with an in-memory DB since we'll lose all the coins upon
75 // reset.
76 if (!m_db_params.memory_only) {
78 // Have to do a reset first to get the original `m_db` state to release its
79 // filesystem lock.
80 m_db.reset();
81 m_db_params.cache_bytes = new_cache_size;
82 m_db_params.wipe_data = false;
83 m_db = std::make_unique<CDBWrapper>(m_db_params);
84 }
85}
86
87std::optional<Coin> CCoinsViewDB::GetCoin(const COutPoint& outpoint) const
88{
89 if (Coin coin; m_db->Read(CoinEntry(&outpoint), coin)) {
90 Assert(!coin.IsSpent()); // The UTXO database should never contain spent coins
91 return coin;
92 }
93 return std::nullopt;
94}
95
96bool CCoinsViewDB::HaveCoin(const COutPoint &outpoint) const {
97 return m_db->Exists(CoinEntry(&outpoint));
98}
99
101 uint256 hashBestChain;
102 if (!m_db->Read(DB_BEST_BLOCK, hashBestChain))
103 return uint256();
104 return hashBestChain;
105}
106
107std::vector<uint256> CCoinsViewDB::GetHeadBlocks() const {
108 std::vector<uint256> vhashHeadBlocks;
109 if (!m_db->Read(DB_HEAD_BLOCKS, vhashHeadBlocks)) {
110 return std::vector<uint256>();
111 }
112 return vhashHeadBlocks;
113}
114
116{
117 CDBBatch batch(*m_db);
118 size_t count = 0;
119 const size_t dirty_count{cursor.GetDirtyCount()};
120 assert(!hashBlock.IsNull());
121
122 uint256 old_tip = GetBestBlock();
123 if (old_tip.IsNull()) {
124 // We may be in the middle of replaying.
125 std::vector<uint256> old_heads = GetHeadBlocks();
126 if (old_heads.size() == 2) {
127 if (old_heads[0] != hashBlock) {
128 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");
129 }
130 assert(old_heads[0] == hashBlock);
131 old_tip = old_heads[1];
132 }
133 }
134
135 if (dirty_count > WARN_FLUSH_COINS_COUNT) LogWarning("Flushing large (%d entries) UTXO set to disk, it may take several minutes", dirty_count);
136 LOG_TIME_MILLIS_WITH_CATEGORY(strprintf("write coins cache to disk (%d out of %d cached coins)",
137 dirty_count, cursor.GetTotalCount()), BCLog::BENCH);
138
139 // In the first batch, mark the database as being in the middle of a
140 // transition from old_tip to hashBlock.
141 // A vector is used for future extensibility, as we may want to support
142 // interrupting after partial writes from multiple independent reorgs.
143 batch.Erase(DB_BEST_BLOCK);
144 batch.Write(DB_HEAD_BLOCKS, Vector(hashBlock, old_tip));
145
146 for (auto it{cursor.Begin()}; it != cursor.End();) {
147 if (it->second.IsDirty()) {
148 CoinEntry entry(&it->first);
149 if (it->second.coin.IsSpent()) {
150 batch.Erase(entry);
151 } else {
152 batch.Write(entry, it->second.coin);
153 }
154 }
155 count++;
156 it = cursor.NextAndMaybeErase(*it);
157 if (batch.ApproximateSize() > m_options.batch_write_bytes) {
158 LogDebug(BCLog::COINDB, "Writing partial batch of %.2f MiB\n", batch.ApproximateSize() * (1.0 / 1048576.0));
159
160 m_db->WriteBatch(batch);
161 batch.Clear();
162 if (m_options.simulate_crash_ratio) {
163 static FastRandomContext rng;
164 if (rng.randrange(m_options.simulate_crash_ratio) == 0) {
165 LogError("Simulating a crash. Goodbye.");
166 _Exit(0);
167 }
168 }
169 }
170 }
171
172 // In the last batch, mark the database as consistent with hashBlock again.
173 batch.Erase(DB_HEAD_BLOCKS);
174 batch.Write(DB_BEST_BLOCK, hashBlock);
175
176 LogDebug(BCLog::COINDB, "Writing final batch of %.2f MiB\n", batch.ApproximateSize() * (1.0 / 1048576.0));
177 m_db->WriteBatch(batch);
178 LogDebug(BCLog::COINDB, "Committed %u changed transaction outputs (out of %u) to coin database...", (unsigned int)dirty_count, (unsigned int)count);
179}
180
182{
183 return m_db->EstimateSize(DB_COIN, uint8_t(DB_COIN + 1));
184}
185
186std::optional<std::string> CCoinsViewDB::GetDBProperty(const std::string& property)
187{
188 return m_db->GetProperty(property);
189}
190
191std::shared_future<void> CCoinsViewDB::CompactFull()
192{
194 if (m_compaction.valid() && m_compaction.wait_for(std::chrono::seconds{0}) != std::future_status::ready) return m_compaction;
195 m_compaction = std::async(std::launch::async, [this] {
196 try {
197 util::ThreadRename("utxocompact");
199
200 LogDebug(BCLog::COINDB, "Starting chainstate compaction of %s", fs::PathToString(m_db_params.path));
201 m_db->CompactFull();
202 LogDebug(BCLog::COINDB, "Finished chainstate compaction of %s", fs::PathToString(m_db_params.path));
203 } catch (const std::exception& e) {
204 LogWarning("Failed chainstate compaction (%s)", e.what());
205 }
206 }).share();
207 return m_compaction;
208}
209
212{
213public:
214 // Prefer using CCoinsViewDB::Cursor() since we want to perform some
215 // cache warmup on instantiation.
216 CCoinsViewDBCursor(CDBIterator* pcursorIn, const uint256&hashBlockIn):
217 CCoinsViewCursor(hashBlockIn), pcursor(pcursorIn) {}
219
220 bool GetKey(COutPoint &key) const override;
221 bool GetValue(Coin &coin) const override;
222
223 bool Valid() const override;
224 void Next() override;
225
226private:
227 std::unique_ptr<CDBIterator> pcursor;
228 std::pair<char, COutPoint> keyTmp;
229
230 friend class CCoinsViewDB;
231};
232
233std::unique_ptr<CCoinsViewCursor> CCoinsViewDB::Cursor() const
234{
235 auto i = std::make_unique<CCoinsViewDBCursor>(
236 const_cast<CDBWrapper&>(*m_db).NewIterator(), GetBestBlock());
237 /* It seems that there are no "const iterators" for LevelDB. Since we
238 only need read operations on it, use a const-cast to get around
239 that restriction. */
240 i->pcursor->Seek(DB_COIN);
241 // Cache key of first record
242 if (i->pcursor->Valid()) {
243 CoinEntry entry(&i->keyTmp.second);
244 i->pcursor->GetKey(entry);
245 i->keyTmp.first = entry.key;
246 } else {
247 i->keyTmp.first = 0; // Make sure Valid() and GetKey() return false
248 }
249 return i;
250}
251
253{
254 // Return cached key
255 if (keyTmp.first == DB_COIN) {
256 key = keyTmp.second;
257 return true;
258 }
259 return false;
260}
261
263{
264 return pcursor->GetValue(coin);
265}
266
268{
269 return keyTmp.first == DB_COIN;
270}
271
273{
274 pcursor->Next();
275 CoinEntry entry(&keyTmp.second);
276 if (!pcursor->Valid() || !pcursor->GetKey(entry)) {
277 keyTmp.first = 0; // Invalidate cached key after last record so that Valid() and GetKey() return false
278 } else {
279 keyTmp.first = entry.key;
280 }
281}
constexpr uint8_t DB_BEST_BLOCK
Definition base.cpp:47
#define Assert(val)
Identity function.
Definition check.h:113
CCoinsViewCursor(const uint256 &hashBlockIn)
Definition coins.h:232
std::unique_ptr< CDBIterator > pcursor
Definition txdb.cpp:227
bool GetKey(COutPoint &key) const override
Definition txdb.cpp:252
~CCoinsViewDBCursor()=default
bool GetValue(Coin &coin) const override
Definition txdb.cpp:262
CCoinsViewDBCursor(CDBIterator *pcursorIn, const uint256 &hashBlockIn)
Definition txdb.cpp:216
bool Valid() const override
Definition txdb.cpp:267
friend class CCoinsViewDB
Definition txdb.cpp:230
void Next() override
Definition txdb.cpp:272
std::pair< char, COutPoint > keyTmp
Definition txdb.cpp:228
Mutex m_db_mutex
Prevents CompactFull() from using m_db while ResizeCache() replaces it.
Definition txdb.h:42
std::shared_future< void > m_compaction
Definition txdb.h:44
bool HaveCoin(const COutPoint &outpoint) const override
Definition txdb.cpp:96
std::unique_ptr< CDBWrapper > m_db
Definition txdb.h:43
~CCoinsViewDB() override
Definition txdb.cpp:62
CCoinsViewDB(DBParams db_params, CoinsViewOptions options)
Definition txdb.cpp:57
std::optional< std::string > GetDBProperty(const std::string &property)
Return an underlying LevelDB property value, if available.
Definition txdb.cpp:186
uint256 GetBestBlock() const override
Retrieve the block hash whose state this CCoinsView currently represents.
Definition txdb.cpp:100
std::optional< Coin > GetCoin(const COutPoint &outpoint) const override
Definition txdb.cpp:87
void ResizeCache(size_t new_cache_size) EXCLUSIVE_LOCKS_REQUIRED(cs_main
Dynamically alter the underlying leveldb cache size.
Definition txdb.cpp:72
void BatchWrite(CoinsViewCacheCursor &cursor, const uint256 &hashBlock) override
Definition txdb.cpp:115
std::unique_ptr< CCoinsViewCursor > Cursor() const override
Get a cursor to iterate over the whole state.
Definition txdb.cpp:233
std::shared_future< void > CompactFull() EXCLUSIVE_LOCKS_REQUIRED(cs_main
Perform a full compaction of the underlying LevelDB on a one-shot background thread.
Definition txdb.cpp:191
CoinsViewOptions m_options
Definition txdb.h:40
std::vector< uint256 > GetHeadBlocks() const override
Definition txdb.cpp:107
bool NeedsUpgrade()
Whether an unsupported database format is used.
Definition txdb.cpp:36
size_t EstimateSize() const override
Estimate database size (0 if not implemented).
Definition txdb.cpp:181
DBParams m_db_params
Definition txdb.h:39
Batch of changes queued to be written to a CDBWrapper.
Definition dbwrapper.h:72
void Erase(const K &key)
Definition dbwrapper.h:108
void Write(const K &key, const V &value)
Definition dbwrapper.h:96
void Clear()
size_t ApproximateSize() const
CDBIterator * NewIterator()
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition transaction.h:29
A UTXO entry.
Definition coins.h:35
Fast randomness source.
Definition random.h:386
I randrange(I range) noexcept
Generate a random integer in the range [0..range), with range > 0.
Definition random.h:254
constexpr bool IsNull() const
Definition uint256.h:48
256-bit opaque blob.
Definition uint256.h:195
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition cs_main.cpp:8
#define LogWarning(...)
Definition log.h:96
#define LogInfo(...)
Definition log.h:95
#define LogError(...)
Definition log.h:97
#define LogDebug(category,...)
Definition log.h:115
@ COINDB
Definition categories.h:34
@ BENCH
Definition categories.h:20
Definition common.h:29
void ThreadRename(const std::string &)
#define VARINT(obj)
Definition serialize.h:491
#define SERIALIZE_METHODS(cls, obj)
Implement the Serialize and Unserialize methods by delegating to a single templated static method tha...
Definition serialize.h:229
#define READWRITE(...)
Definition serialize.h:145
constexpr CoinEntry(const CAmount v, const State s)
Cursor for iterating over the linked list of flagged entries in CCoinsViewCache.
Definition coins.h:261
CoinsCachePair * NextAndMaybeErase(CoinsCachePair &current) noexcept
Return the next entry after current, possibly erasing current.
Definition coins.h:279
size_t GetTotalCount() const noexcept
Definition coins.h:298
size_t GetDirtyCount() const noexcept
Definition coins.h:297
CoinsCachePair * Begin() const noexcept
Definition coins.h:275
CoinsCachePair * End() const noexcept
Definition coins.h:276
User-controlled performance and debug options.
Definition txdb.h:28
Application-specific storage settings.
Definition dbwrapper.h:33
#define LOCK(cs)
Definition sync.h:258
#define AssertLockHeld(cs)
Definition sync.h:136
static int count
#define LOG_TIME_MILLIS_WITH_CATEGORY(end_msg, log_category)
Definition timer.h:103
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
static constexpr size_t WARN_FLUSH_COINS_COUNT
Definition txdb.cpp:34
static constexpr uint8_t DB_HEAD_BLOCKS
Definition txdb.cpp:29
static constexpr uint8_t DB_COIN
Definition txdb.cpp:27
static constexpr uint8_t DB_COINS
Definition txdb.cpp:31
assert(!tx.IsCoinBase())
std::vector< std::common_type_t< Args... > > Vector(Args &&... args)
Construct a vector with the specified elements.
Definition vector.h:23