22// Distributed under the MIT software license, see the accompanying
33// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44
5- #include < index/disktxpos.h>
65#include < index/txindex.h>
6+
7+ #include < index/disktxpos.h>
78#include < node/blockstorage.h>
8- #include < node/ui_interface.h>
9- #include < shutdown.h>
109#include < util/system.h>
11- #include < util/translation.h>
1210#include < validation.h>
1311
14- constexpr uint8_t DB_BEST_BLOCK{' B' };
1512constexpr uint8_t DB_TXINDEX{' t' };
16- constexpr uint8_t DB_TXINDEX_BLOCK{' T' };
1713
1814std::unique_ptr<TxIndex> g_txindex;
1915
@@ -30,10 +26,6 @@ class TxIndex::DB : public BaseIndex::DB
3026
3127 // / Write a batch of transaction positions to the DB.
3228 bool WriteTxs (const std::vector<std::pair<uint256, CDiskTxPos>>& v_pos);
33-
34- // / Migrate txindex data from the block tree DB, where it may be for older nodes that have not
35- // / been upgraded yet to the new database.
36- bool MigrateData (CBlockTreeDB& block_tree_db, const CBlockLocator& best_locator);
3729};
3830
3931TxIndex::DB::DB (size_t n_cache_size, bool f_memory, bool f_wipe) :
@@ -54,163 +46,12 @@ bool TxIndex::DB::WriteTxs(const std::vector<std::pair<uint256, CDiskTxPos>>& v_
5446 return WriteBatch (batch);
5547}
5648
57- /*
58- * Safely persist a transfer of data from the old txindex database to the new one, and compact the
59- * range of keys updated. This is used internally by MigrateData.
60- */
61- static void WriteTxIndexMigrationBatches (CDBWrapper& newdb, CDBWrapper& olddb,
62- CDBBatch& batch_newdb, CDBBatch& batch_olddb,
63- const std::pair<uint8_t , uint256>& begin_key,
64- const std::pair<uint8_t , uint256>& end_key)
65- {
66- // Sync new DB changes to disk before deleting from old DB.
67- newdb.WriteBatch (batch_newdb, /* fSync=*/ true );
68- olddb.WriteBatch (batch_olddb);
69- olddb.CompactRange (begin_key, end_key);
70-
71- batch_newdb.Clear ();
72- batch_olddb.Clear ();
73- }
74-
75- bool TxIndex::DB::MigrateData (CBlockTreeDB& block_tree_db, const CBlockLocator& best_locator)
76- {
77- // The prior implementation of txindex was always in sync with block index
78- // and presence was indicated with a boolean DB flag. If the flag is set,
79- // this means the txindex from a previous version is valid and in sync with
80- // the chain tip. The first step of the migration is to unset the flag and
81- // write the chain hash to a separate key, DB_TXINDEX_BLOCK. After that, the
82- // index entries are copied over in batches to the new database. Finally,
83- // DB_TXINDEX_BLOCK is erased from the old database and the block hash is
84- // written to the new database.
85- //
86- // Unsetting the boolean flag ensures that if the node is downgraded to a
87- // previous version, it will not see a corrupted, partially migrated index
88- // -- it will see that the txindex is disabled. When the node is upgraded
89- // again, the migration will pick up where it left off and sync to the block
90- // with hash DB_TXINDEX_BLOCK.
91- bool f_legacy_flag = false ;
92- block_tree_db.ReadFlag (" txindex" , f_legacy_flag);
93- if (f_legacy_flag) {
94- if (!block_tree_db.Write (DB_TXINDEX_BLOCK, best_locator)) {
95- return error (" %s: cannot write block indicator" , __func__);
96- }
97- if (!block_tree_db.WriteFlag (" txindex" , false )) {
98- return error (" %s: cannot write block index db flag" , __func__);
99- }
100- }
101-
102- CBlockLocator locator;
103- if (!block_tree_db.Read (DB_TXINDEX_BLOCK, locator)) {
104- return true ;
105- }
106-
107- int64_t count = 0 ;
108- LogPrintf (" Upgrading txindex database... [0%%]\n " );
109- uiInterface.ShowProgress (_ (" Upgrading txindex database" ).translated , 0 , true );
110- int report_done = 0 ;
111- const size_t batch_size = 1 << 24 ; // 16 MiB
112-
113- CDBBatch batch_newdb (*this );
114- CDBBatch batch_olddb (block_tree_db);
115-
116- std::pair<uint8_t , uint256> key;
117- std::pair<uint8_t , uint256> begin_key{DB_TXINDEX, uint256 ()};
118- std::pair<uint8_t , uint256> prev_key = begin_key;
119-
120- bool interrupted = false ;
121- std::unique_ptr<CDBIterator> cursor (block_tree_db.NewIterator ());
122- for (cursor->Seek (begin_key); cursor->Valid (); cursor->Next ()) {
123- if (ShutdownRequested ()) {
124- interrupted = true ;
125- break ;
126- }
127-
128- if (!cursor->GetKey (key)) {
129- return error (" %s: cannot get key from valid cursor" , __func__);
130- }
131- if (key.first != DB_TXINDEX) {
132- break ;
133- }
134-
135- // Log progress every 10%.
136- if (++count % 256 == 0 ) {
137- // Since txids are uniformly random and traversed in increasing order, the high 16 bits
138- // of the hash can be used to estimate the current progress.
139- const uint256& txid = key.second ;
140- uint32_t high_nibble =
141- (static_cast <uint32_t >(*(txid.begin () + 0 )) << 8 ) +
142- (static_cast <uint32_t >(*(txid.begin () + 1 )) << 0 );
143- int percentage_done = (int )(high_nibble * 100.0 / 65536.0 + 0.5 );
144-
145- uiInterface.ShowProgress (_ (" Upgrading txindex database" ).translated , percentage_done, true );
146- if (report_done < percentage_done/10 ) {
147- LogPrintf (" Upgrading txindex database... [%d%%]\n " , percentage_done);
148- report_done = percentage_done/10 ;
149- }
150- }
151-
152- CDiskTxPos value;
153- if (!cursor->GetValue (value)) {
154- return error (" %s: cannot parse txindex record" , __func__);
155- }
156- batch_newdb.Write (key, value);
157- batch_olddb.Erase (key);
158-
159- if (batch_newdb.SizeEstimate () > batch_size || batch_olddb.SizeEstimate () > batch_size) {
160- // NOTE: it's OK to delete the key pointed at by the current DB cursor while iterating
161- // because LevelDB iterators are guaranteed to provide a consistent view of the
162- // underlying data, like a lightweight snapshot.
163- WriteTxIndexMigrationBatches (*this , block_tree_db,
164- batch_newdb, batch_olddb,
165- prev_key, key);
166- prev_key = key;
167- }
168- }
169-
170- // If these final DB batches complete the migration, write the best block
171- // hash marker to the new database and delete from the old one. This signals
172- // that the former is fully caught up to that point in the blockchain and
173- // that all txindex entries have been removed from the latter.
174- if (!interrupted) {
175- batch_olddb.Erase (DB_TXINDEX_BLOCK);
176- batch_newdb.Write (DB_BEST_BLOCK, locator);
177- }
178-
179- WriteTxIndexMigrationBatches (*this , block_tree_db,
180- batch_newdb, batch_olddb,
181- begin_key, key);
182-
183- if (interrupted) {
184- LogPrintf (" [CANCELLED].\n " );
185- return false ;
186- }
187-
188- uiInterface.ShowProgress (" " , 100 , false );
189-
190- LogPrintf (" [DONE].\n " );
191- return true ;
192- }
193-
19449TxIndex::TxIndex (size_t n_cache_size, bool f_memory, bool f_wipe)
19550 : m_db(std::make_unique<TxIndex::DB>(n_cache_size, f_memory, f_wipe))
19651{}
19752
19853TxIndex::~TxIndex () {}
19954
200- bool TxIndex::Init ()
201- {
202- LOCK (cs_main);
203-
204- // Attempt to migrate txindex from the old database to the new one. Even if
205- // chain_tip is null, the node could be reindexing and we still want to
206- // delete txindex records in the old database.
207- if (!m_db->MigrateData (*m_chainstate->m_blockman .m_block_tree_db , m_chainstate->m_chain .GetLocator ())) {
208- return false ;
209- }
210-
211- return BaseIndex::Init ();
212- }
213-
21455bool TxIndex::WriteBlock (const CBlock& block, const CBlockIndex* pindex)
21556{
21657 // Exclude genesis block transaction because outputs are not spendable.
0 commit comments