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