// Copyright (c) 2009-2021 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const int CONTINUE_EXECUTION=-1; const std::function G_TRANSLATION_FUN = nullptr; static void SetupBitcoinUtilArgs(ArgsManager &argsman) { SetupHelpOptions(argsman); argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddCommand("grind", "Perform proof of work on hex header string"); SetupChainParamsBaseOptions(argsman); } // This function returns either one of EXIT_ codes when it's expected to stop the process or // CONTINUE_EXECUTION when it's expected to continue further. static int AppInitUtil(ArgsManager& args, int argc, char* argv[]) { SetupBitcoinUtilArgs(args); std::string error; if (!args.ParseParameters(argc, argv, error)) { tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error); return EXIT_FAILURE; } if (HelpRequested(args) || args.IsArgSet("-version")) { // First part of help message is specific to this utility std::string strUsage = PACKAGE_NAME " bitcoin-util utility version " + FormatFullVersion() + "\n"; if (args.IsArgSet("-version")) { strUsage += FormatParagraph(LicenseInfo()); } else { strUsage += "\n" "Usage: bitcoin-util [options] [commands] Do stuff\n"; strUsage += "\n" + args.GetHelpMessage(); } tfm::format(std::cout, "%s", strUsage); if (argc < 2) { tfm::format(std::cerr, "Error: too few parameters\n"); return EXIT_FAILURE; } return EXIT_SUCCESS; } // Check for chain settings (Params() calls are only valid after this clause) try { SelectParams(args.GetChainName()); } catch (const std::exception& e) { tfm::format(std::cerr, "Error: %s\n", e.what()); return EXIT_FAILURE; } return CONTINUE_EXECUTION; } static void grind_task(uint32_t nBits, CBlockHeader& header_orig, uint32_t offset, uint32_t step, std::atomic& found) { arith_uint256 target; bool neg, over; target.SetCompact(nBits, &neg, &over); if (target == 0 || neg || over) return; CBlockHeader header = header_orig; // working copy header.nNonce = offset; uint32_t finish = std::numeric_limits::max() - step; finish = finish - (finish % step) + offset; while (!found && header.nNonce < finish) { const uint32_t next = (finish - header.nNonce < 5000*step) ? finish : header.nNonce + 5000*step; do { if (UintToArith256(header.GetHash()) <= target) { if (!found.exchange(true)) { header_orig.nNonce = header.nNonce; } return; } header.nNonce += step; } while(header.nNonce != next); } } static int Grind(const std::vector& args, std::string& strPrint) { if (args.size() != 1) { strPrint = "Must specify block header to grind"; return EXIT_FAILURE; } CBlockHeader header; if (!DecodeHexBlockHeader(header, args[0])) { strPrint = "Could not decode block header"; return EXIT_FAILURE; } uint32_t nBits = header.nBits; std::atomic found{false}; std::vector threads; int n_tasks = std::max(1u, std::thread::hardware_concurrency()); for (int i = 0; i < n_tasks; ++i) { threads.emplace_back( grind_task, nBits, std::ref(header), i, n_tasks, std::ref(found) ); } for (auto& t : threads) { t.join(); } if (!found) { strPrint = "Could not satisfy difficulty target"; return EXIT_FAILURE; } CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss << header; strPrint = HexStr(ss); return EXIT_SUCCESS; } MAIN_FUNCTION { ArgsManager& args = gArgs; SetupEnvironment(); try { int ret = AppInitUtil(args, argc, argv); if (ret != CONTINUE_EXECUTION) { return ret; } } catch (const std::exception& e) { PrintExceptionContinue(&e, "AppInitUtil()"); return EXIT_FAILURE; } catch (...) { PrintExceptionContinue(nullptr, "AppInitUtil()"); return EXIT_FAILURE; } const auto cmd = args.GetCommand(); if (!cmd) { tfm::format(std::cerr, "Error: must specify a command\n"); return EXIT_FAILURE; } int ret = EXIT_FAILURE; std::string strPrint; try { if (cmd->command == "grind") { ret = Grind(cmd->args, strPrint); } else { assert(false); // unknown command should be caught earlier } } catch (const std::exception& e) { strPrint = std::string("error: ") + e.what(); } catch (...) { strPrint = "unknown error"; } if (strPrint != "") { tfm::format(ret == 0 ? std::cout : std::cerr, "%s\n", strPrint); } return ret; }