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