Branch data Line data Source code
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/log.h>
16 : : #include <util/vector.h>
17 : :
18 : : #include <cassert>
19 : : #include <cstdlib>
20 : : #include <iterator>
21 : : #include <utility>
22 : :
23 : : static constexpr uint8_t DB_COIN{'C'};
24 : : static constexpr uint8_t DB_BEST_BLOCK{'B'};
25 : : static constexpr uint8_t DB_HEAD_BLOCKS{'H'};
26 : : // Keys used in previous version that might still be found in the DB:
27 : : static constexpr uint8_t DB_COINS{'c'};
28 : :
29 : : // Threshold for warning when writing this many dirty cache entries to disk.
30 : : static constexpr size_t WARN_FLUSH_COINS_COUNT{10'000'000};
31 : :
32 : 198 : bool CCoinsViewDB::NeedsUpgrade()
33 : : {
34 [ + - ]: 198 : std::unique_ptr<CDBIterator> cursor{m_db->NewIterator()};
35 : : // DB_COINS was deprecated in v0.15.0, commit
36 : : // 1088b02f0ccd7358d2b7076bb9e122d59d502d02
37 [ + - ]: 198 : cursor->Seek(std::make_pair(DB_COINS, uint256{}));
38 [ + - ]: 396 : return cursor->Valid();
39 : 198 : }
40 : :
41 : : namespace {
42 : :
43 : : struct CoinEntry {
44 : : COutPoint* outpoint;
45 : : uint8_t key{DB_COIN};
46 [ + - ]: 5551391 : explicit CoinEntry(const COutPoint* ptr) : outpoint(const_cast<COutPoint*>(ptr)) {}
47 : :
48 : 11312078 : SERIALIZE_METHODS(CoinEntry, obj) { READWRITE(obj.key, obj.outpoint->hash, VARINT(obj.outpoint->n)); }
49 : : };
50 : :
51 : : } // namespace
52 : :
53 : 225 : CCoinsViewDB::CCoinsViewDB(DBParams db_params, CoinsViewOptions options) :
54 : 225 : m_db_params{std::move(db_params)},
55 : 225 : m_options{std::move(options)},
56 [ + - ]: 225 : m_db{std::make_unique<CDBWrapper>(m_db_params)} { }
57 : :
58 : 52 : void CCoinsViewDB::ResizeCache(size_t new_cache_size)
59 : : {
60 : : // We can't do this operation with an in-memory DB since we'll lose all the coins upon
61 : : // reset.
62 [ + + ]: 52 : if (!m_db_params.memory_only) {
63 : : // Have to do a reset first to get the original `m_db` state to release its
64 : : // filesystem lock.
65 [ + - ]: 44 : m_db.reset();
66 : 44 : m_db_params.cache_bytes = new_cache_size;
67 : 44 : m_db_params.wipe_data = false;
68 : 44 : m_db = std::make_unique<CDBWrapper>(m_db_params);
69 : : }
70 : 52 : }
71 : :
72 : 5551347 : std::optional<Coin> CCoinsViewDB::GetCoin(const COutPoint& outpoint) const
73 : : {
74 [ + - + + ]: 5551347 : if (Coin coin; m_db->Read(CoinEntry(&outpoint), coin)) {
75 [ - + ]: 85201 : Assert(!coin.IsSpent()); // The UTXO database should never contain spent coins
76 : 85201 : return coin;
77 : 85201 : }
78 : 5466146 : return std::nullopt;
79 : : }
80 : :
81 : 44 : bool CCoinsViewDB::HaveCoin(const COutPoint &outpoint) const {
82 : 44 : return m_db->Exists(CoinEntry(&outpoint));
83 : : }
84 : :
85 : 967 : uint256 CCoinsViewDB::GetBestBlock() const {
86 : 967 : uint256 hashBestChain;
87 [ + + ]: 967 : if (!m_db->Read(DB_BEST_BLOCK, hashBestChain))
88 : 595 : return uint256();
89 : 372 : return hashBestChain;
90 : : }
91 : :
92 : 230 : std::vector<uint256> CCoinsViewDB::GetHeadBlocks() const {
93 : 230 : std::vector<uint256> vhashHeadBlocks;
94 [ + - + - ]: 230 : if (!m_db->Read(DB_HEAD_BLOCKS, vhashHeadBlocks)) {
95 : 230 : return std::vector<uint256>();
96 : : }
97 : 0 : return vhashHeadBlocks;
98 : 230 : }
99 : :
100 : 264 : void CCoinsViewDB::BatchWrite(CoinsViewCacheCursor& cursor, const uint256& hashBlock)
101 : : {
102 : 264 : CDBBatch batch(*m_db);
103 : 264 : size_t count = 0;
104 : 264 : const size_t dirty_count{cursor.GetDirtyCount()};
105 [ - + ]: 528 : assert(!hashBlock.IsNull());
106 : :
107 [ + - ]: 264 : uint256 old_tip = GetBestBlock();
108 [ + + ]: 264 : if (old_tip.IsNull()) {
109 : : // We may be in the middle of replaying.
110 [ + - ]: 32 : std::vector<uint256> old_heads = GetHeadBlocks();
111 [ - + - + ]: 32 : if (old_heads.size() == 2) {
112 [ # # ]: 0 : if (old_heads[0] != hashBlock) {
113 [ # # ]: 0 : 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");
114 : : }
115 [ # # ]: 0 : assert(old_heads[0] == hashBlock);
116 : 0 : old_tip = old_heads[1];
117 : : }
118 : 32 : }
119 : :
120 [ - + - - ]: 264 : if (dirty_count > WARN_FLUSH_COINS_COUNT) LogWarning("Flushing large (%d entries) UTXO set to disk, it may take several minutes", dirty_count);
121 [ + - + - : 528 : LOG_TIME_MILLIS_WITH_CATEGORY(strprintf("write coins cache to disk (%d out of %d cached coins)",
+ - ]
122 : : dirty_count, cursor.GetTotalCount()), BCLog::BENCH);
123 : :
124 : : // In the first batch, mark the database as being in the middle of a
125 : : // transition from old_tip to hashBlock.
126 : : // A vector is used for future extensibility, as we may want to support
127 : : // interrupting after partial writes from multiple independent reorgs.
128 [ + - ]: 264 : batch.Erase(DB_BEST_BLOCK);
129 [ + - + - ]: 264 : batch.Write(DB_HEAD_BLOCKS, Vector(hashBlock, old_tip));
130 : :
131 [ + + ]: 95505 : for (auto it{cursor.Begin()}; it != cursor.End();) {
132 [ + - ]: 95241 : if (it->second.IsDirty()) {
133 : 95241 : CoinEntry entry(&it->first);
134 [ + + ]: 95241 : if (it->second.coin.IsSpent()) {
135 [ + - ]: 14283 : batch.Erase(entry);
136 : : } else {
137 [ + - ]: 80958 : batch.Write(entry, it->second.coin);
138 : : }
139 : : }
140 : 95241 : count++;
141 : 95241 : it = cursor.NextAndMaybeErase(*it);
142 [ + - - + ]: 95241 : if (batch.ApproximateSize() > m_options.batch_write_bytes) {
143 [ # # # # : 0 : LogDebug(BCLog::COINDB, "Writing partial batch of %.2f MiB\n", batch.ApproximateSize() * (1.0 / 1048576.0));
# # # # ]
144 : :
145 [ # # ]: 0 : m_db->WriteBatch(batch);
146 [ # # ]: 0 : batch.Clear();
147 [ # # ]: 0 : if (m_options.simulate_crash_ratio) {
148 [ # # # # ]: 0 : static FastRandomContext rng;
149 [ # # ]: 0 : if (rng.randrange(m_options.simulate_crash_ratio) == 0) {
150 [ # # ]: 0 : LogError("Simulating a crash. Goodbye.");
151 : 0 : _Exit(0);
152 : : }
153 : : }
154 : : }
155 : : }
156 : :
157 : : // In the last batch, mark the database as consistent with hashBlock again.
158 [ + - ]: 264 : batch.Erase(DB_HEAD_BLOCKS);
159 [ + - ]: 264 : batch.Write(DB_BEST_BLOCK, hashBlock);
160 : :
161 [ + - + + : 264 : LogDebug(BCLog::COINDB, "Writing final batch of %.2f MiB\n", batch.ApproximateSize() * (1.0 / 1048576.0));
+ - + - ]
162 [ + - ]: 264 : m_db->WriteBatch(batch);
163 [ + - + + : 264 : LogDebug(BCLog::COINDB, "Committed %u changed transaction outputs (out of %u) to coin database...", (unsigned int)dirty_count, (unsigned int)count);
+ - ]
164 : 264 : }
165 : :
166 : 46 : size_t CCoinsViewDB::EstimateSize() const
167 : : {
168 : 46 : return m_db->EstimateSize(DB_COIN, uint8_t(DB_COIN + 1));
169 : : }
170 : :
171 : : /** Specialization of CCoinsViewCursor to iterate over a CCoinsViewDB */
172 : : class CCoinsViewDBCursor: public CCoinsViewCursor
173 : : {
174 : : public:
175 : : // Prefer using CCoinsViewDB::Cursor() since we want to perform some
176 : : // cache warmup on instantiation.
177 : 79 : CCoinsViewDBCursor(CDBIterator* pcursorIn, const uint256&hashBlockIn):
178 : 79 : CCoinsViewCursor(hashBlockIn), pcursor(pcursorIn) {}
179 : 79 : ~CCoinsViewDBCursor() = default;
180 : :
181 : : bool GetKey(COutPoint &key) const override;
182 : : bool GetValue(Coin &coin) const override;
183 : :
184 : : bool Valid() const override;
185 : : void Next() override;
186 : :
187 : : private:
188 : : std::unique_ptr<CDBIterator> pcursor;
189 : : std::pair<char, COutPoint> keyTmp;
190 : :
191 : : friend class CCoinsViewDB;
192 : : };
193 : :
194 : 79 : std::unique_ptr<CCoinsViewCursor> CCoinsViewDB::Cursor() const
195 : : {
196 : 79 : auto i = std::make_unique<CCoinsViewDBCursor>(
197 : 79 : const_cast<CDBWrapper&>(*m_db).NewIterator(), GetBestBlock());
198 : : /* It seems that there are no "const iterators" for LevelDB. Since we
199 : : only need read operations on it, use a const-cast to get around
200 : : that restriction. */
201 [ + - ]: 79 : i->pcursor->Seek(DB_COIN);
202 : : // Cache key of first record
203 [ + - + - ]: 79 : if (i->pcursor->Valid()) {
204 [ + - ]: 79 : CoinEntry entry(&i->keyTmp.second);
205 [ + - ]: 79 : i->pcursor->GetKey(entry);
206 : 79 : i->keyTmp.first = entry.key;
207 : : } else {
208 : 0 : i->keyTmp.first = 0; // Make sure Valid() and GetKey() return false
209 : : }
210 : 79 : return i;
211 : 79 : }
212 : :
213 : 9440 : bool CCoinsViewDBCursor::GetKey(COutPoint &key) const
214 : : {
215 : : // Return cached key
216 [ + - ]: 9440 : if (keyTmp.first == DB_COIN) {
217 : 9440 : key = keyTmp.second;
218 : 9440 : return true;
219 : : }
220 : : return false;
221 : : }
222 : :
223 : 9407 : bool CCoinsViewDBCursor::GetValue(Coin &coin) const
224 : : {
225 : 9407 : return pcursor->GetValue(coin);
226 : : }
227 : :
228 : 9486 : bool CCoinsViewDBCursor::Valid() const
229 : : {
230 : 9486 : return keyTmp.first == DB_COIN;
231 : : }
232 : :
233 : 9407 : void CCoinsViewDBCursor::Next()
234 : : {
235 : 9407 : pcursor->Next();
236 : 9407 : CoinEntry entry(&keyTmp.second);
237 [ + + - + ]: 9407 : if (!pcursor->Valid() || !pcursor->GetKey(entry)) {
238 : 79 : keyTmp.first = 0; // Invalidate cached key after last record so that Valid() and GetKey() return false
239 : : } else {
240 : 9328 : keyTmp.first = entry.key;
241 : : }
242 : 9407 : }
|