/ src / bitcoin-util.cpp
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  }