bitcoin-chainstate.cpp
1 // Copyright (c) 2022 The Bitcoin Core developers 2 // Distributed under the MIT software license, see the accompanying 3 // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 // 5 // The bitcoin-chainstate executable serves to surface the dependencies required 6 // by a program wishing to use Bitcoin Core's consensus engine as it is right 7 // now. 8 // 9 // DEVELOPER NOTE: Since this is a "demo-only", experimental, etc. executable, 10 // it may diverge from Bitcoin Core's coding style. 11 // 12 // It is part of the libbitcoinkernel project. 13 14 #include <kernel/chainparams.h> 15 #include <kernel/chainstatemanager_opts.h> 16 #include <kernel/checks.h> 17 #include <kernel/context.h> 18 #include <kernel/warning.h> 19 20 #include <consensus/validation.h> 21 #include <core_io.h> 22 #include <kernel/caches.h> 23 #include <logging.h> 24 #include <node/blockstorage.h> 25 #include <node/chainstate.h> 26 #include <random.h> 27 #include <script/sigcache.h> 28 #include <util/chaintype.h> 29 #include <util/fs.h> 30 #include <util/signalinterrupt.h> 31 #include <util/task_runner.h> 32 #include <util/translation.h> 33 #include <validation.h> 34 #include <validationinterface.h> 35 36 #include <cassert> 37 #include <cstdint> 38 #include <functional> 39 #include <iosfwd> 40 #include <memory> 41 #include <string> 42 43 int main(int argc, char* argv[]) 44 { 45 // We do not enable logging for this app, so explicitly disable it. 46 // To enable logging instead, replace with: 47 // LogInstance().m_print_to_console = true; 48 // LogInstance().StartLogging(); 49 LogInstance().DisableLogging(); 50 51 // SETUP: Argument parsing and handling 52 if (argc != 2) { 53 std::cerr 54 << "Usage: " << argv[0] << " DATADIR" << std::endl 55 << "Display DATADIR information, and process hex-encoded blocks on standard input." << std::endl 56 << std::endl 57 << "IMPORTANT: THIS EXECUTABLE IS EXPERIMENTAL, FOR TESTING ONLY, AND EXPECTED TO" << std::endl 58 << " BREAK IN FUTURE VERSIONS. DO NOT USE ON YOUR ACTUAL DATADIR." << std::endl; 59 return 1; 60 } 61 fs::path abs_datadir{fs::absolute(argv[1])}; 62 fs::create_directories(abs_datadir); 63 64 65 // SETUP: Context 66 kernel::Context kernel_context{}; 67 // We can't use a goto here, but we can use an assert since none of the 68 // things instantiated so far requires running the epilogue to be torn down 69 // properly 70 assert(kernel::SanityChecks(kernel_context)); 71 72 ValidationSignals validation_signals{std::make_unique<util::ImmediateTaskRunner>()}; 73 74 class KernelNotifications : public kernel::Notifications 75 { 76 public: 77 kernel::InterruptResult blockTip(SynchronizationState, CBlockIndex&) override 78 { 79 std::cout << "Block tip changed" << std::endl; 80 return {}; 81 } 82 void headerTip(SynchronizationState, int64_t height, int64_t timestamp, bool presync) override 83 { 84 std::cout << "Header tip changed: " << height << ", " << timestamp << ", " << presync << std::endl; 85 } 86 void progress(const bilingual_str& title, int progress_percent, bool resume_possible) override 87 { 88 std::cout << "Progress: " << title.original << ", " << progress_percent << ", " << resume_possible << std::endl; 89 } 90 void warningSet(kernel::Warning id, const bilingual_str& message) override 91 { 92 std::cout << "Warning " << static_cast<int>(id) << " set: " << message.original << std::endl; 93 } 94 void warningUnset(kernel::Warning id) override 95 { 96 std::cout << "Warning " << static_cast<int>(id) << " unset" << std::endl; 97 } 98 void flushError(const bilingual_str& message) override 99 { 100 std::cerr << "Error flushing block data to disk: " << message.original << std::endl; 101 } 102 void fatalError(const bilingual_str& message) override 103 { 104 std::cerr << "Error: " << message.original << std::endl; 105 } 106 }; 107 auto notifications = std::make_unique<KernelNotifications>(); 108 109 kernel::CacheSizes cache_sizes{DEFAULT_KERNEL_CACHE}; 110 111 // SETUP: Chainstate 112 auto chainparams = CChainParams::Main(); 113 const ChainstateManager::Options chainman_opts{ 114 .chainparams = *chainparams, 115 .datadir = abs_datadir, 116 .notifications = *notifications, 117 .signals = &validation_signals, 118 }; 119 const node::BlockManager::Options blockman_opts{ 120 .chainparams = chainman_opts.chainparams, 121 .blocks_dir = abs_datadir / "blocks", 122 .notifications = chainman_opts.notifications, 123 .block_tree_db_params = DBParams{ 124 .path = abs_datadir / "blocks" / "index", 125 .cache_bytes = cache_sizes.block_tree_db, 126 }, 127 }; 128 util::SignalInterrupt interrupt; 129 ChainstateManager chainman{interrupt, chainman_opts, blockman_opts}; 130 131 node::ChainstateLoadOptions options; 132 auto [status, error] = node::LoadChainstate(chainman, cache_sizes, options); 133 if (status != node::ChainstateLoadStatus::SUCCESS) { 134 std::cerr << "Failed to load Chain state from your datadir." << std::endl; 135 goto epilogue; 136 } else { 137 std::tie(status, error) = node::VerifyLoadedChainstate(chainman, options); 138 if (status != node::ChainstateLoadStatus::SUCCESS) { 139 std::cerr << "Failed to verify loaded Chain state from your datadir." << std::endl; 140 goto epilogue; 141 } 142 } 143 144 for (Chainstate* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) { 145 BlockValidationState state; 146 if (!chainstate->ActivateBestChain(state, nullptr)) { 147 std::cerr << "Failed to connect best block (" << state.ToString() << ")" << std::endl; 148 goto epilogue; 149 } 150 } 151 152 // Main program logic starts here 153 std::cout 154 << "Hello! I'm going to print out some information about your datadir." << std::endl 155 << "\t" 156 << "Path: " << abs_datadir << std::endl; 157 { 158 LOCK(chainman.GetMutex()); 159 std::cout 160 << "\t" << "Blockfiles Indexed: " << std::boolalpha << chainman.m_blockman.m_blockfiles_indexed.load() << std::noboolalpha << std::endl 161 << "\t" << "Snapshot Active: " << std::boolalpha << chainman.IsSnapshotActive() << std::noboolalpha << std::endl 162 << "\t" << "Active Height: " << chainman.ActiveHeight() << std::endl 163 << "\t" << "Active IBD: " << std::boolalpha << chainman.IsInitialBlockDownload() << std::noboolalpha << std::endl; 164 CBlockIndex* tip = chainman.ActiveTip(); 165 if (tip) { 166 std::cout << "\t" << tip->ToString() << std::endl; 167 } 168 } 169 170 for (std::string line; std::getline(std::cin, line);) { 171 if (line.empty()) { 172 std::cerr << "Empty line found" << std::endl; 173 break; 174 } 175 176 std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>(); 177 CBlock& block = *blockptr; 178 179 if (!DecodeHexBlk(block, line)) { 180 std::cerr << "Block decode failed" << std::endl; 181 break; 182 } 183 184 { 185 LOCK(cs_main); 186 const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock); 187 if (pindex) { 188 chainman.UpdateUncommittedBlockStructures(block, pindex); 189 } 190 } 191 192 // Adapted from rpc/mining.cpp 193 class submitblock_StateCatcher final : public CValidationInterface 194 { 195 public: 196 uint256 hash; 197 bool found; 198 BlockValidationState state; 199 200 explicit submitblock_StateCatcher(const uint256& hashIn) : hash(hashIn), found(false), state() {} 201 202 protected: 203 void BlockChecked(const CBlock& block, const BlockValidationState& stateIn) override 204 { 205 if (block.GetHash() != hash) 206 return; 207 found = true; 208 state = stateIn; 209 } 210 }; 211 212 bool new_block; 213 auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash()); 214 validation_signals.RegisterSharedValidationInterface(sc); 215 bool accepted = chainman.ProcessNewBlock(blockptr, /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/&new_block); 216 validation_signals.UnregisterSharedValidationInterface(sc); 217 if (!new_block && accepted) { 218 std::cerr << "duplicate" << std::endl; 219 break; 220 } 221 if (!sc->found) { 222 std::cerr << "inconclusive" << std::endl; 223 break; 224 } 225 std::cout << sc->state.ToString() << std::endl; 226 switch (sc->state.GetResult()) { 227 case BlockValidationResult::BLOCK_RESULT_UNSET: 228 std::cerr << "initial value. Block has not yet been rejected" << std::endl; 229 break; 230 case BlockValidationResult::BLOCK_HEADER_LOW_WORK: 231 std::cerr << "the block header may be on a too-little-work chain" << std::endl; 232 break; 233 case BlockValidationResult::BLOCK_CONSENSUS: 234 std::cerr << "invalid by consensus rules (excluding any below reasons)" << std::endl; 235 break; 236 case BlockValidationResult::BLOCK_CACHED_INVALID: 237 std::cerr << "this block was cached as being invalid and we didn't store the reason why" << std::endl; 238 break; 239 case BlockValidationResult::BLOCK_INVALID_HEADER: 240 std::cerr << "invalid proof of work or time too old" << std::endl; 241 break; 242 case BlockValidationResult::BLOCK_MUTATED: 243 std::cerr << "the block's data didn't match the data committed to by the PoW" << std::endl; 244 break; 245 case BlockValidationResult::BLOCK_MISSING_PREV: 246 std::cerr << "We don't have the previous block the checked one is built on" << std::endl; 247 break; 248 case BlockValidationResult::BLOCK_INVALID_PREV: 249 std::cerr << "A block this one builds on is invalid" << std::endl; 250 break; 251 case BlockValidationResult::BLOCK_TIME_FUTURE: 252 std::cerr << "block timestamp was > 2 hours in the future (or our clock is bad)" << std::endl; 253 break; 254 } 255 } 256 257 epilogue: 258 // Without this precise shutdown sequence, there will be a lot of nullptr 259 // dereferencing and UB. 260 validation_signals.FlushBackgroundCallbacks(); 261 { 262 LOCK(cs_main); 263 for (Chainstate* chainstate : chainman.GetAll()) { 264 if (chainstate->CanFlushToDisk()) { 265 chainstate->ForceFlushStateToDisk(); 266 chainstate->ResetCoinsViews(); 267 } 268 } 269 } 270 }