aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml4
-rw-r--r--.gitignore1
-rwxr-xr-xci/test/00_setup_env_native_nowallet_libbitcoinkernel.sh (renamed from ci/test/00_setup_env_native_nowallet.sh)4
-rw-r--r--configure.ac13
-rw-r--r--src/Makefile.am100
-rw-r--r--src/bitcoin-chainstate.cpp262
6 files changed, 380 insertions, 4 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 9b0e2dbd2e..80546fb85e 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -258,13 +258,13 @@ task:
FILE_ENV: "./ci/test/00_setup_env_i686_multiprocess.sh"
task:
- name: '[no wallet] [bionic]'
+ name: '[no wallet, libbitcoinkernel] [bionic]'
<< : *GLOBAL_TASK_TEMPLATE
container:
image: ubuntu:bionic
env:
<< : *CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV
- FILE_ENV: "./ci/test/00_setup_env_native_nowallet.sh"
+ FILE_ENV: "./ci/test/00_setup_env_native_nowallet_libbitcoinkernel.sh"
task:
name: 'macOS 10.15 [gui, no tests] [focal]'
diff --git a/.gitignore b/.gitignore
index f84a53178e..0f07a86401 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@ src/bitcoin-gui
src/bitcoin-node
src/bitcoin-tx
src/bitcoin-util
+src/bitcoin-chainstate
src/bitcoin-wallet
src/test/fuzz/fuzz
src/test/test_bitcoin
diff --git a/ci/test/00_setup_env_native_nowallet.sh b/ci/test/00_setup_env_native_nowallet_libbitcoinkernel.sh
index d80a7f9633..89d6256298 100755
--- a/ci/test/00_setup_env_native_nowallet.sh
+++ b/ci/test/00_setup_env_native_nowallet_libbitcoinkernel.sh
@@ -6,9 +6,9 @@
export LC_ALL=C.UTF-8
-export CONTAINER_NAME=ci_native_nowallet
+export CONTAINER_NAME=ci_native_nowallet_libbitcoinkernel
export DOCKER_NAME_TAG=ubuntu:18.04 # Use bionic to have one config run the tests in python3.6, see doc/dependencies.md
export PACKAGES="python3-zmq clang-7 llvm-7 libc++abi-7-dev libc++-7-dev" # Use clang-7 to test C++17 compatibility, see doc/dependencies.md
export DEP_OPTS="NO_WALLET=1 CC=clang-7 CXX='clang++-7 -stdlib=libc++'"
export GOAL="install"
-export BITCOIN_CONFIG="--enable-reduce-exports CC=clang-7 CXX='clang++-7 -stdlib=libc++'"
+export BITCOIN_CONFIG="--enable-reduce-exports CC=clang-7 CXX='clang++-7 -stdlib=libc++' --enable-experimental-util-chainstate"
diff --git a/configure.ac b/configure.ac
index ef9d894d9e..48684c7c98 100644
--- a/configure.ac
+++ b/configure.ac
@@ -24,6 +24,7 @@ BITCOIN_GUI_NAME=bitcoin-qt
BITCOIN_CLI_NAME=bitcoin-cli
BITCOIN_TX_NAME=bitcoin-tx
BITCOIN_UTIL_NAME=bitcoin-util
+BITCOIN_CHAINSTATE_NAME=bitcoin-chainstate
BITCOIN_WALLET_TOOL_NAME=bitcoin-wallet
dnl Multi Process
BITCOIN_MP_NODE_NAME=bitcoin-node
@@ -645,6 +646,12 @@ AC_ARG_ENABLE([util-util],
[build_bitcoin_util=$enableval],
[build_bitcoin_util=$build_bitcoin_utils])
+AC_ARG_ENABLE([experimental-util-chainstate],
+ [AS_HELP_STRING([--enable-experimental-util-chainstate],
+ [build experimental bitcoin-chainstate executable (default=no)])],
+ [build_bitcoin_chainstate=$enableval],
+ [build_bitcoin_chainstate=no])
+
AC_ARG_WITH([libs],
[AS_HELP_STRING([--with-libs],
[build libraries (default=yes)])],
@@ -1268,6 +1275,7 @@ if test "$enable_fuzz" = "yes"; then
build_bitcoin_cli=no
build_bitcoin_tx=no
build_bitcoin_util=no
+ build_bitcoin_chainstate=no
build_bitcoin_wallet=no
build_bitcoind=no
build_bitcoin_libs=no
@@ -1624,6 +1632,10 @@ AC_MSG_CHECKING([whether to build bitcoin-util])
AM_CONDITIONAL([BUILD_BITCOIN_UTIL], [test $build_bitcoin_util = "yes"])
AC_MSG_RESULT($build_bitcoin_util)
+AC_MSG_CHECKING([whether to build experimental bitcoin-chainstate])
+AM_CONDITIONAL([BUILD_BITCOIN_CHAINSTATE], [test $build_bitcoin_chainstate = "yes"])
+AC_MSG_RESULT($build_bitcoin_chainstate)
+
AC_MSG_CHECKING([whether to build libraries])
AM_CONDITIONAL([BUILD_BITCOIN_LIBS], [test $build_bitcoin_libs = "yes"])
if test "$build_bitcoin_libs" = "yes"; then
@@ -1843,6 +1855,7 @@ AC_SUBST(BITCOIN_GUI_NAME)
AC_SUBST(BITCOIN_CLI_NAME)
AC_SUBST(BITCOIN_TX_NAME)
AC_SUBST(BITCOIN_UTIL_NAME)
+AC_SUBST(BITCOIN_CHAINSTATE_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 417a611181..8f4cbee62f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -106,6 +106,10 @@ if BUILD_BITCOIN_UTIL
bin_PROGRAMS += bitcoin-util
endif
+if BUILD_BITCOIN_CHAINSTATE
+ bin_PROGRAMS += bitcoin-chainstate
+endif
+
.PHONY: FORCE check-symbols check-security
# bitcoin core #
BITCOIN_CORE_H = \
@@ -769,6 +773,102 @@ bitcoin_util_LDADD = \
$(LIBSECP256K1)
#
+# bitcoin-chainstate binary #
+bitcoin_chainstate_SOURCES = \
+ bitcoin-chainstate.cpp \
+ arith_uint256.cpp \
+ blockfilter.cpp \
+ chain.cpp \
+ chainparamsbase.cpp \
+ chainparams.cpp \
+ clientversion.cpp \
+ coins.cpp \
+ compat/glibcxx_sanity.cpp \
+ compressor.cpp \
+ consensus/merkle.cpp \
+ consensus/tx_check.cpp \
+ consensus/tx_verify.cpp \
+ core_read.cpp \
+ dbwrapper.cpp \
+ deploymentinfo.cpp \
+ deploymentstatus.cpp \
+ flatfile.cpp \
+ fs.cpp \
+ hash.cpp \
+ index/base.cpp \
+ index/blockfilterindex.cpp \
+ index/coinstatsindex.cpp \
+ init/common.cpp \
+ key.cpp \
+ logging.cpp \
+ netaddress.cpp \
+ node/blockstorage.cpp \
+ node/chainstate.cpp \
+ node/coinstats.cpp \
+ node/ui_interface.cpp \
+ policy/feerate.cpp \
+ policy/fees.cpp \
+ policy/packages.cpp \
+ policy/policy.cpp \
+ policy/rbf.cpp \
+ policy/settings.cpp \
+ pow.cpp \
+ primitives/block.cpp \
+ primitives/transaction.cpp \
+ pubkey.cpp \
+ random.cpp \
+ randomenv.cpp \
+ scheduler.cpp \
+ script/interpreter.cpp \
+ script/script.cpp \
+ script/script_error.cpp \
+ script/sigcache.cpp \
+ script/standard.cpp \
+ shutdown.cpp \
+ signet.cpp \
+ support/cleanse.cpp \
+ support/lockedpool.cpp \
+ sync.cpp \
+ threadinterrupt.cpp \
+ timedata.cpp \
+ txdb.cpp \
+ txmempool.cpp \
+ uint256.cpp \
+ util/asmap.cpp \
+ util/bytevectorhash.cpp \
+ util/getuniquepath.cpp \
+ util/hasher.cpp \
+ util/moneystr.cpp \
+ util/rbf.cpp \
+ util/serfloat.cpp \
+ util/settings.cpp \
+ util/strencodings.cpp \
+ util/syscall_sandbox.cpp \
+ util/system.cpp \
+ util/thread.cpp \
+ util/threadnames.cpp \
+ util/time.cpp \
+ util/tokenpipe.cpp \
+ validation.cpp \
+ validationinterface.cpp \
+ versionbits.cpp \
+ warnings.cpp
+bitcoin_chainstate_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+bitcoin_chainstate_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+bitcoin_chainstate_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
+bitcoin_chainstate_LDADD = \
+ $(LIBBITCOIN_CRYPTO) \
+ $(LIBUNIVALUE) \
+ $(LIBSECP256K1) \
+ $(LIBLEVELDB) \
+ $(LIBLEVELDB_SSE42) \
+ $(LIBMEMENV)
+
+# Required for obj/build.h to be generated first.
+# More details: https://www.gnu.org/software/automake/manual/html_node/Built-Sources-Example.html
+bitcoin_chainstate-clientversion.$(OBJEXT): obj/build.h
+#
+
# bitcoinconsensus library #
if BUILD_BITCOIN_LIBS
include_HEADERS = script/bitcoinconsensus.h
diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp
new file mode 100644
index 0000000000..f93197350d
--- /dev/null
+++ b/src/bitcoin-chainstate.cpp
@@ -0,0 +1,262 @@
+// Copyright (c) 2022 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+//
+// The bitcoin-chainstate executable serves to surface the dependencies required
+// by a program wishing to use Bitcoin Core's consensus engine as it is right
+// now.
+//
+// DEVELOPER NOTE: Since this is a "demo-only", experimental, etc. executable,
+// it may diverge from Bitcoin Core's coding style.
+//
+// It is part of the libbitcoinkernel project.
+
+#include <chainparams.h>
+#include <consensus/validation.h>
+#include <core_io.h>
+#include <init/common.h>
+#include <node/blockstorage.h>
+#include <node/chainstate.h>
+#include <scheduler.h>
+#include <script/sigcache.h>
+#include <util/system.h>
+#include <util/thread.h>
+#include <validation.h>
+#include <validationinterface.h>
+
+#include <filesystem>
+#include <functional>
+#include <iosfwd>
+
+const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
+
+int main(int argc, char* argv[])
+{
+ // SETUP: Argument parsing and handling
+ if (argc != 2) {
+ std::cerr
+ << "Usage: " << argv[0] << " DATADIR" << std::endl
+ << "Display DATADIR information, and process hex-encoded blocks on standard input." << std::endl
+ << std::endl
+ << "IMPORTANT: THIS EXECUTABLE IS EXPERIMENTAL, FOR TESTING ONLY, AND EXPECTED TO" << std::endl
+ << " BREAK IN FUTURE VERSIONS. DO NOT USE ON YOUR ACTUAL DATADIR." << std::endl;
+ return 1;
+ }
+ std::filesystem::path abs_datadir = std::filesystem::absolute(argv[1]);
+ std::filesystem::create_directories(abs_datadir);
+ gArgs.ForceSetArg("-datadir", abs_datadir.string());
+
+
+ // SETUP: Misc Globals
+ SelectParams(CBaseChainParams::MAIN);
+ const CChainParams& chainparams = Params();
+
+ init::SetGlobals(); // ECC_Start, etc.
+
+ // Necessary for CheckInputScripts (eventually called by ProcessNewBlock),
+ // which will try the script cache first and fall back to actually
+ // performing the check with the signature cache.
+ InitSignatureCache();
+ InitScriptExecutionCache();
+
+
+ // SETUP: Scheduling and Background Signals
+ CScheduler scheduler{};
+ // Start the lightweight task scheduler thread
+ scheduler.m_service_thread = std::thread(util::TraceThread, "scheduler", [&] { scheduler.serviceQueue(); });
+
+ // Gather some entropy once per minute.
+ scheduler.scheduleEvery(RandAddPeriodic, std::chrono::minutes{1});
+
+ GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
+
+
+ // SETUP: Chainstate
+ ChainstateManager chainman;
+
+ auto rv = node::LoadChainstate(false,
+ std::ref(chainman),
+ nullptr,
+ false,
+ chainparams.GetConsensus(),
+ false,
+ 2 << 20,
+ 2 << 22,
+ (450 << 20) - (2 << 20) - (2 << 22),
+ false,
+ false,
+ []() { return false; });
+ if (rv.has_value()) {
+ std::cerr << "Failed to load Chain state from your datadir." << std::endl;
+ goto epilogue;
+ } else {
+ auto maybe_verify_error = node::VerifyLoadedChainstate(std::ref(chainman),
+ false,
+ false,
+ chainparams.GetConsensus(),
+ DEFAULT_CHECKBLOCKS,
+ DEFAULT_CHECKLEVEL,
+ /*get_unix_time_seconds=*/static_cast<int64_t (*)()>(GetTime));
+ if (maybe_verify_error.has_value()) {
+ std::cerr << "Failed to verify loaded Chain state from your datadir." << std::endl;
+ goto epilogue;
+ }
+ }
+
+ for (CChainState* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
+ BlockValidationState state;
+ if (!chainstate->ActivateBestChain(state, nullptr)) {
+ std::cerr << "Failed to connect best block (" << state.ToString() << ")" << std::endl;
+ goto epilogue;
+ }
+ }
+
+ // Main program logic starts here
+ std::cout
+ << "Hello! I'm going to print out some information about your datadir." << std::endl
+ << "\t" << "Path: " << gArgs.GetDataDirNet() << std::endl
+ << "\t" << "Reindexing: " << std::boolalpha << node::fReindex.load() << std::noboolalpha << std::endl
+ << "\t" << "Snapshot Active: " << std::boolalpha << chainman.IsSnapshotActive() << std::noboolalpha << std::endl
+ << "\t" << "Active Height: " << chainman.ActiveHeight() << std::endl
+ << "\t" << "Active IBD: " << std::boolalpha << chainman.ActiveChainstate().IsInitialBlockDownload() << std::noboolalpha << std::endl;
+ {
+ CBlockIndex* tip = chainman.ActiveTip();
+ if (tip) {
+ std::cout << "\t" << tip->ToString() << std::endl;
+ }
+ }
+
+ for (std::string line; std::getline(std::cin, line);) {
+ if (line.empty()) {
+ std::cerr << "Empty line found" << std::endl;
+ break;
+ }
+
+ std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
+ CBlock& block = *blockptr;
+
+ if (!DecodeHexBlk(block, line)) {
+ std::cerr << "Block decode failed" << std::endl;
+ break;
+ }
+
+ if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) {
+ std::cerr << "Block does not start with a coinbase" << std::endl;
+ break;
+ }
+
+ uint256 hash = block.GetHash();
+ {
+ LOCK(cs_main);
+ const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
+ if (pindex) {
+ if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) {
+ std::cerr << "duplicate" << std::endl;
+ break;
+ }
+ if (pindex->nStatus & BLOCK_FAILED_MASK) {
+ std::cerr << "duplicate-invalid" << std::endl;
+ break;
+ }
+ }
+ }
+
+ {
+ LOCK(cs_main);
+ const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock);
+ if (pindex) {
+ UpdateUncommittedBlockStructures(block, pindex, chainparams.GetConsensus());
+ }
+ }
+
+ // Adapted from rpc/mining.cpp
+ class submitblock_StateCatcher final : public CValidationInterface
+ {
+ public:
+ uint256 hash;
+ bool found;
+ BlockValidationState state;
+
+ explicit submitblock_StateCatcher(const uint256& hashIn) : hash(hashIn), found(false), state() {}
+
+ protected:
+ void BlockChecked(const CBlock& block, const BlockValidationState& stateIn) override
+ {
+ if (block.GetHash() != hash)
+ return;
+ found = true;
+ state = stateIn;
+ }
+ };
+
+ bool new_block;
+ auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
+ RegisterSharedValidationInterface(sc);
+ bool accepted = chainman.ProcessNewBlock(chainparams, blockptr, /* force_processing */ true, /* new_block */ &new_block);
+ UnregisterSharedValidationInterface(sc);
+ if (!new_block && accepted) {
+ std::cerr << "duplicate" << std::endl;
+ break;
+ }
+ if (!sc->found) {
+ std::cerr << "inconclusive" << std::endl;
+ break;
+ }
+ std::cout << sc->state.ToString() << std::endl;
+ switch (sc->state.GetResult()) {
+ case BlockValidationResult::BLOCK_RESULT_UNSET:
+ std::cerr << "initial value. Block has not yet been rejected" << std::endl;
+ break;
+ case BlockValidationResult::BLOCK_CONSENSUS:
+ std::cerr << "invalid by consensus rules (excluding any below reasons)" << std::endl;
+ break;
+ case BlockValidationResult::BLOCK_RECENT_CONSENSUS_CHANGE:
+ std::cerr << "Invalid by a change to consensus rules more recent than SegWit." << std::endl;
+ break;
+ case BlockValidationResult::BLOCK_CACHED_INVALID:
+ std::cerr << "this block was cached as being invalid and we didn't store the reason why" << std::endl;
+ break;
+ case BlockValidationResult::BLOCK_INVALID_HEADER:
+ std::cerr << "invalid proof of work or time too old" << std::endl;
+ break;
+ case BlockValidationResult::BLOCK_MUTATED:
+ std::cerr << "the block's data didn't match the data committed to by the PoW" << std::endl;
+ break;
+ case BlockValidationResult::BLOCK_MISSING_PREV:
+ std::cerr << "We don't have the previous block the checked one is built on" << std::endl;
+ break;
+ case BlockValidationResult::BLOCK_INVALID_PREV:
+ std::cerr << "A block this one builds on is invalid" << std::endl;
+ break;
+ case BlockValidationResult::BLOCK_TIME_FUTURE:
+ std::cerr << "block timestamp was > 2 hours in the future (or our clock is bad)" << std::endl;
+ break;
+ case BlockValidationResult::BLOCK_CHECKPOINT:
+ std::cerr << "the block failed to meet one of our checkpoints" << std::endl;
+ break;
+ }
+ }
+
+epilogue:
+ // Without this precise shutdown sequence, there will be a lot of nullptr
+ // dereferencing and UB.
+ scheduler.stop();
+ if (chainman.m_load_block.joinable()) chainman.m_load_block.join();
+ StopScriptCheckWorkerThreads();
+
+ GetMainSignals().FlushBackgroundCallbacks();
+ {
+ LOCK(cs_main);
+ for (CChainState* chainstate : chainman.GetAll()) {
+ if (chainstate->CanFlushToDisk()) {
+ chainstate->ForceFlushStateToDisk();
+ chainstate->ResetCoinsViews();
+ }
+ }
+ }
+ GetMainSignals().UnregisterBackgroundSignalScheduler();
+
+ UnloadBlockIndex(nullptr, chainman);
+
+ init::UnsetGlobals();
+}