/ 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/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  }