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