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