bitcoin-util.cpp
1 // Copyright (c) 2009-present 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 #include <bitcoin-build-config.h> // IWYU pragma: keep 6 7 #include <arith_uint256.h> 8 #include <chain.h> 9 #include <chainparams.h> 10 #include <chainparamsbase.h> 11 #include <clientversion.h> 12 #include <common/args.h> 13 #include <common/license_info.h> 14 #include <common/system.h> 15 #include <compat/compat.h> 16 #include <core_io.h> 17 #include <streams.h> 18 #include <util/exception.h> 19 #include <util/strencodings.h> 20 #include <util/translation.h> 21 22 #include <atomic> 23 #include <cstdio> 24 #include <functional> 25 #include <memory> 26 #include <thread> 27 28 static const int CONTINUE_EXECUTION=-1; 29 30 const TranslateFn G_TRANSLATION_FUN{nullptr}; 31 32 static void SetupBitcoinUtilArgs(ArgsManager &argsman) 33 { 34 SetupHelpOptions(argsman); 35 36 argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); 37 38 argsman.AddCommand("grind", "Perform proof of work on hex header string"); 39 40 SetupChainParamsBaseOptions(argsman); 41 } 42 43 // This function returns either one of EXIT_ codes when it's expected to stop the process or 44 // CONTINUE_EXECUTION when it's expected to continue further. 45 static int AppInitUtil(ArgsManager& args, int argc, char* argv[]) 46 { 47 SetupBitcoinUtilArgs(args); 48 std::string error; 49 if (!args.ParseParameters(argc, argv, error)) { 50 tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error); 51 return EXIT_FAILURE; 52 } 53 54 if (HelpRequested(args) || args.GetBoolArg("-version", false)) { 55 // First part of help message is specific to this utility 56 std::string strUsage = CLIENT_NAME " bitcoin-util utility version " + FormatFullVersion() + "\n"; 57 58 if (args.GetBoolArg("-version", false)) { 59 strUsage += FormatParagraph(LicenseInfo()); 60 } else { 61 strUsage += "\n" 62 "The bitcoin-util tool provides bitcoin related functionality that does not rely on the ability to access a running node. Available [commands] are listed below.\n" 63 "\n" 64 "Usage: bitcoin-util [options] [command]\n" 65 "or: bitcoin-util [options] grind <hex-block-header>\n"; 66 strUsage += "\n" + args.GetHelpMessage(); 67 } 68 69 tfm::format(std::cout, "%s", strUsage); 70 71 if (argc < 2) { 72 tfm::format(std::cerr, "Error: too few parameters\n"); 73 return EXIT_FAILURE; 74 } 75 return EXIT_SUCCESS; 76 } 77 78 // Check for chain settings (Params() calls are only valid after this clause) 79 try { 80 SelectParams(args.GetChainType()); 81 } catch (const std::exception& e) { 82 tfm::format(std::cerr, "Error: %s\n", e.what()); 83 return EXIT_FAILURE; 84 } 85 86 return CONTINUE_EXECUTION; 87 } 88 89 static void grind_task(uint32_t nBits, CBlockHeader header, uint32_t offset, uint32_t step, std::atomic<bool>& found, uint32_t& proposed_nonce) 90 { 91 arith_uint256 target; 92 bool neg, over; 93 target.SetCompact(nBits, &neg, &over); 94 if (target == 0 || neg || over) return; 95 header.nNonce = offset; 96 97 uint32_t finish = std::numeric_limits<uint32_t>::max() - step; 98 finish = finish - (finish % step) + offset; 99 100 while (!found && header.nNonce < finish) { 101 const uint32_t next = (finish - header.nNonce < 5000*step) ? finish : header.nNonce + 5000*step; 102 do { 103 if (UintToArith256(header.GetHash()) <= target) { 104 if (!found.exchange(true)) { 105 proposed_nonce = header.nNonce; 106 } 107 return; 108 } 109 header.nNonce += step; 110 } while(header.nNonce != next); 111 } 112 } 113 114 static int Grind(const std::vector<std::string>& args, std::string& strPrint) 115 { 116 if (args.size() != 1) { 117 strPrint = "Must specify block header to grind"; 118 return EXIT_FAILURE; 119 } 120 121 CBlockHeader header; 122 if (!DecodeHexBlockHeader(header, args[0])) { 123 strPrint = "Could not decode block header"; 124 return EXIT_FAILURE; 125 } 126 127 uint32_t nBits = header.nBits; 128 std::atomic<bool> found{false}; 129 uint32_t proposed_nonce{}; 130 131 std::vector<std::thread> threads; 132 int n_tasks = std::max(1u, std::thread::hardware_concurrency()); 133 threads.reserve(n_tasks); 134 for (int i = 0; i < n_tasks; ++i) { 135 threads.emplace_back(grind_task, nBits, header, i, n_tasks, std::ref(found), std::ref(proposed_nonce)); 136 } 137 for (auto& t : threads) { 138 t.join(); 139 } 140 if (found) { 141 header.nNonce = proposed_nonce; 142 } else { 143 strPrint = "Could not satisfy difficulty target"; 144 return EXIT_FAILURE; 145 } 146 147 DataStream ss{}; 148 ss << header; 149 strPrint = HexStr(ss); 150 return EXIT_SUCCESS; 151 } 152 153 MAIN_FUNCTION 154 { 155 ArgsManager& args = gArgs; 156 SetupEnvironment(); 157 158 try { 159 int ret = AppInitUtil(args, argc, argv); 160 if (ret != CONTINUE_EXECUTION) { 161 return ret; 162 } 163 } catch (const std::exception& e) { 164 PrintExceptionContinue(&e, "AppInitUtil()"); 165 return EXIT_FAILURE; 166 } catch (...) { 167 PrintExceptionContinue(nullptr, "AppInitUtil()"); 168 return EXIT_FAILURE; 169 } 170 171 const auto cmd = args.GetCommand(); 172 if (!cmd) { 173 tfm::format(std::cerr, "Error: must specify a command\n"); 174 return EXIT_FAILURE; 175 } 176 177 int ret = EXIT_FAILURE; 178 std::string strPrint; 179 try { 180 if (cmd->command == "grind") { 181 ret = Grind(cmd->args, strPrint); 182 } else { 183 assert(false); // unknown command should be caught earlier 184 } 185 } catch (const std::exception& e) { 186 strPrint = std::string("error: ") + e.what(); 187 } catch (...) { 188 strPrint = "unknown error"; 189 } 190 191 if (strPrint != "") { 192 tfm::format(ret == 0 ? std::cout : std::cerr, "%s\n", strPrint); 193 } 194 195 return ret; 196 }