diff options
author | Anthony Towns <aj@erisian.com.au> | 2020-09-10 08:09:07 +1000 |
---|---|---|
committer | Anthony Towns <aj@erisian.com.au> | 2021-01-12 18:34:25 +1000 |
commit | 13762bcc9618138dd28b53c2031defdc9d762d26 (patch) | |
tree | 58e51911fd2579f677a50962876a80383d011baf | |
parent | 95d5d5e6257825bb385cee318d5681597f7f7646 (diff) |
Add bitcoin-util command line utility
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile.am | 5 | ||||
-rw-r--r-- | configure.ac | 17 | ||||
-rw-r--r-- | src/Makefile.am | 27 | ||||
-rw-r--r-- | src/bitcoin-util-res.rc | 35 | ||||
-rw-r--r-- | src/bitcoin-util.cpp | 207 |
6 files changed, 290 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore index 5726b18928..0bcf91b440 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ src/bitcoin-cli src/bitcoin-gui src/bitcoin-node src/bitcoin-tx +src/bitcoin-util src/bitcoin-wallet src/test/fuzz/* !src/test/fuzz/*.* diff --git a/Makefile.am b/Makefile.am index 958d68061f..5289239222 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,6 +24,7 @@ BITCOIND_BIN=$(top_builddir)/src/$(BITCOIN_DAEMON_NAME)$(EXEEXT) BITCOIN_QT_BIN=$(top_builddir)/src/qt/$(BITCOIN_GUI_NAME)$(EXEEXT) BITCOIN_CLI_BIN=$(top_builddir)/src/$(BITCOIN_CLI_NAME)$(EXEEXT) BITCOIN_TX_BIN=$(top_builddir)/src/$(BITCOIN_TX_NAME)$(EXEEXT) +BITCOIN_UTIL_BIN=$(top_builddir)/src/$(BITCOIN_UTIL_NAME)$(EXEEXT) BITCOIN_WALLET_BIN=$(top_builddir)/src/$(BITCOIN_WALLET_TOOL_NAME)$(EXEEXT) BITCOIN_NODE_BIN=$(top_builddir)/src/$(BITCOIN_MP_NODE_NAME)$(EXEEXT) BITCOIN_GUI_BIN=$(top_builddir)/src/$(BITCOIN_MP_GUI_NAME)$(EXEEXT) @@ -78,6 +79,7 @@ $(BITCOIN_WIN_INSTALLER): all-recursive STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_CLI_BIN) $(top_builddir)/release STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_TX_BIN) $(top_builddir)/release STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_WALLET_BIN) $(top_builddir)/release + STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_UTIL_BIN) $(top_builddir)/release @test -f $(MAKENSIS) && echo 'OutFile "$@"' | cat $(top_builddir)/share/setup.nsi - | $(MAKENSIS) -V2 - || \ echo error: could not build $@ @echo built $@ @@ -171,6 +173,9 @@ $(BITCOIN_CLI_BIN): FORCE $(BITCOIN_TX_BIN): FORCE $(MAKE) -C src $(@F) +$(BITCOIN_UTIL_BIN): FORCE + $(MAKE) -C src $(@F) + $(BITCOIN_WALLET_BIN): FORCE $(MAKE) -C src $(@F) diff --git a/configure.ac b/configure.ac index 9d951afba1..9f966fe05d 100644 --- a/configure.ac +++ b/configure.ac @@ -23,6 +23,7 @@ BITCOIN_DAEMON_NAME=bitcoind BITCOIN_GUI_NAME=bitcoin-qt BITCOIN_CLI_NAME=bitcoin-cli BITCOIN_TX_NAME=bitcoin-tx +BITCOIN_UTIL_NAME=bitcoin-util BITCOIN_WALLET_TOOL_NAME=bitcoin-wallet dnl Multi Process BITCOIN_MP_NODE_NAME=bitcoin-node @@ -553,7 +554,7 @@ CPPFLAGS="$CPPFLAGS -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS" AC_ARG_WITH([utils], [AS_HELP_STRING([--with-utils], - [build bitcoin-cli bitcoin-tx bitcoin-wallet (default=yes)])], + [build bitcoin-cli bitcoin-tx bitcoin-util bitcoin-wallet (default=yes)])], [build_bitcoin_utils=$withval], [build_bitcoin_utils=yes]) @@ -575,6 +576,12 @@ AC_ARG_ENABLE([util-wallet], [build_bitcoin_wallet=$enableval], [build_bitcoin_wallet=$build_bitcoin_utils]) +AC_ARG_ENABLE([util-util], + [AS_HELP_STRING([--enable-util-util], + [build bitcoin-util])], + [build_bitcoin_util=$enableval], + [build_bitcoin_util=$build_bitcoin_utils]) + AC_ARG_WITH([libs], [AS_HELP_STRING([--with-libs], [build libraries (default=yes)])], @@ -1185,6 +1192,7 @@ if test "x$enable_fuzz" = "xyes"; then build_bitcoin_utils=no build_bitcoin_cli=no build_bitcoin_tx=no + build_bitcoin_util=no build_bitcoin_wallet=no build_bitcoind=no build_bitcoin_libs=no @@ -1401,7 +1409,7 @@ fi dnl univalue check need_bundled_univalue=yes -if test x$build_bitcoin_wallet$build_bitcoin_cli$build_bitcoin_tx$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnonononononono; then +if test x$build_bitcoin_wallet$build_bitcoin_cli$build_bitcoin_tx$build_bitcoin_util$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnononononononono; then need_bundled_univalue=no else if test x$system_univalue != xno; then @@ -1484,6 +1492,10 @@ AC_MSG_CHECKING([whether to build bitcoin-wallet]) AM_CONDITIONAL([BUILD_BITCOIN_WALLET], [test x$build_bitcoin_wallet = xyes]) AC_MSG_RESULT($build_bitcoin_wallet) +AC_MSG_CHECKING([whether to build bitcoin-util]) +AM_CONDITIONAL([BUILD_BITCOIN_UTIL], [test x$build_bitcoin_util = xyes]) +AC_MSG_RESULT($build_bitcoin_util) + AC_MSG_CHECKING([whether to build libraries]) AM_CONDITIONAL([BUILD_BITCOIN_LIBS], [test x$build_bitcoin_libs = xyes]) if test x$build_bitcoin_libs = xyes; then @@ -1659,6 +1671,7 @@ AC_SUBST(BITCOIN_DAEMON_NAME) AC_SUBST(BITCOIN_GUI_NAME) AC_SUBST(BITCOIN_CLI_NAME) AC_SUBST(BITCOIN_TX_NAME) +AC_SUBST(BITCOIN_UTIL_NAME) AC_SUBST(BITCOIN_WALLET_TOOL_NAME) AC_SUBST(BITCOIN_MP_NODE_NAME) AC_SUBST(BITCOIN_MP_GUI_NAME) diff --git a/src/Makefile.am b/src/Makefile.am index 4a080ef1fb..f37db7bde7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -92,15 +92,21 @@ endif if BUILD_BITCOIN_CLI bin_PROGRAMS += bitcoin-cli endif + if BUILD_BITCOIN_TX bin_PROGRAMS += bitcoin-tx endif + if ENABLE_WALLET if BUILD_BITCOIN_WALLET bin_PROGRAMS += bitcoin-wallet endif endif +if BUILD_BITCOIN_UTIL + bin_PROGRAMS += bitcoin-util +endif + .PHONY: FORCE check-symbols check-security # bitcoin core # BITCOIN_CORE_H = \ @@ -660,6 +666,27 @@ bitcoin_wallet_SOURCES += bitcoin-wallet-res.rc endif # +# bitcoin-util binary # +bitcoin_util_SOURCES = bitcoin-util.cpp +bitcoin_util_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +bitcoin_util_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +bitcoin_util_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) + +if TARGET_WINDOWS +bitcoin_util_SOURCES += bitcoin-util-res.rc +endif + +bitcoin_util_LDADD = \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBSECP256K1) + +bitcoin_util_LDADD += $(BOOST_LIBS) +# + # bitcoinconsensus library # if BUILD_BITCOIN_LIBS include_HEADERS = script/bitcoinconsensus.h diff --git a/src/bitcoin-util-res.rc b/src/bitcoin-util-res.rc new file mode 100644 index 0000000000..3f0fa8ab6d --- /dev/null +++ b/src/bitcoin-util-res.rc @@ -0,0 +1,35 @@ +#include <windows.h> // needed for VERSIONINFO +#include "clientversion.h" // holds the needed client version information + +#define VER_PRODUCTVERSION CLIENT_VERSION_MAJOR,CLIENT_VERSION_MINOR,CLIENT_VERSION_BUILD +#define VER_PRODUCTVERSION_STR STRINGIZE(CLIENT_VERSION_MAJOR) "." STRINGIZE(CLIENT_VERSION_MINOR) "." STRINGIZE(CLIENT_VERSION_BUILD) +#define VER_FILEVERSION VER_PRODUCTVERSION +#define VER_FILEVERSION_STR VER_PRODUCTVERSION_STR + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_PRODUCTVERSION +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" // U.S. English - multilingual (hex) + BEGIN + VALUE "CompanyName", "Bitcoin" + VALUE "FileDescription", "bitcoin-util (CLI Bitcoin utility)" + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "InternalName", "bitcoin-util" + VALUE "LegalCopyright", COPYRIGHT_STR + VALUE "LegalTrademarks1", "Distributed under the MIT software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php." + VALUE "OriginalFilename", "bitcoin-util.exe" + VALUE "ProductName", "bitcoin-util" + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1252 // language neutral - multilingual (decimal) + END +END diff --git a/src/bitcoin-util.cpp b/src/bitcoin-util.cpp new file mode 100644 index 0000000000..f7e670f4e0 --- /dev/null +++ b/src/bitcoin-util.cpp @@ -0,0 +1,207 @@ +// Copyright (c) 2009-2020 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 <config/bitcoin-config.h> +#endif + +#include <arith_uint256.h> +#include <clientversion.h> +#include <coins.h> +#include <consensus/consensus.h> +#include <core_io.h> +#include <key_io.h> +#include <policy/rbf.h> +#include <primitives/transaction.h> +#include <script/script.h> +#include <script/sign.h> +#include <script/signingprovider.h> +#include <univalue.h> +#include <util/moneystr.h> +#include <util/rbf.h> +#include <util/strencodings.h> +#include <util/string.h> +#include <util/system.h> +#include <util/translation.h> + +#include <functional> +#include <memory> +#include <stdio.h> +#include <thread> + +#include <boost/algorithm/string.hpp> + +static const int CONTINUE_EXECUTION=-1; + +const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr; + +static void SetupBitcoinUtilArgs(ArgsManager &argsman) +{ + SetupHelpOptions(argsman); + + 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(int argc, char* argv[]) +{ + SetupBitcoinUtilArgs(gArgs); + std::string error; + if (!gArgs.ParseParameters(argc, argv, error)) { + tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error); + return EXIT_FAILURE; + } + + // Check for chain settings (Params() calls are only valid after this clause) + try { + SelectParams(gArgs.GetChainName()); + } catch (const std::exception& e) { + tfm::format(std::cerr, "Error: %s\n", e.what()); + return EXIT_FAILURE; + } + + if (argc < 2 || HelpRequested(gArgs)) { + // First part of help message is specific to this utility + std::string strUsage = PACKAGE_NAME " bitcoin-util utility version " + FormatFullVersion() + "\n\n" + + "Usage: bitcoin-util [options] [commands] Do stuff\n" + + "\n"; + strUsage += gArgs.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; + } + return CONTINUE_EXECUTION; +} + +static void grind_task(uint32_t nBits, CBlockHeader& header_orig, uint32_t offset, uint32_t step, std::atomic<bool>& 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<uint32_t>::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(int argc, char* argv[], std::string& strPrint) +{ + if (argc != 1) { + strPrint = "Must specify block header to grind"; + return 1; + } + + CBlockHeader header; + if (!DecodeHexBlockHeader(header, argv[0])) { + strPrint = "Could not decode block header"; + return 1; + } + + uint32_t nBits = header.nBits; + std::atomic<bool> found{false}; + + std::vector<std::thread> 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 1; + } + + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << header; + strPrint = HexStr(ss); + return 0; +} + +static int CommandLineUtil(int argc, char* argv[]) +{ + if (argc <= 1) return 1; + + std::string strPrint; + int nRet = 0; + + try { + while (argc > 1 && IsSwitchChar(argv[1][0]) && (argv[1][1] != 0)) { + --argc; + ++argv; + } + + char* command = argv[1]; + if (strcmp(command, "grind") == 0) { + nRet = Grind(argc-2, argv+2, strPrint); + } else { + strPrint = strprintf("Unknown command %s", command); + nRet = 1; + } + } + catch (const std::exception& e) { + strPrint = std::string("error: ") + e.what(); + nRet = EXIT_FAILURE; + } + catch (...) { + PrintExceptionContinue(nullptr, "CommandLineUtil()"); + throw; + } + + if (strPrint != "") { + tfm::format(nRet == 0 ? std::cout : std::cerr, "%s\n", strPrint); + } + return nRet; +} + +int main(int argc, char* argv[]) +{ + SetupEnvironment(); + + try { + int ret = AppInitUtil(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; + } + + int ret = EXIT_FAILURE; + try { + ret = CommandLineUtil(argc, argv); + } + catch (const std::exception& e) { + PrintExceptionContinue(&e, "CommandLineUtil()"); + } catch (...) { + PrintExceptionContinue(nullptr, "CommandLineUtil()"); + } + return ret; +} |