diff --git a/.github/ci-windows.py b/.github/ci-windows.py index 60864e7a1522..f4931fee3065 100755 --- a/.github/ci-windows.py +++ b/.github/ci-windows.py @@ -33,7 +33,6 @@ def run(cmd, **kwargs): ], "fuzz": [ "-DVCPKG_MANIFEST_NO_DEFAULT_FEATURES=ON", - "-DVCPKG_MANIFEST_FEATURES=wallet", "-DWITH_ZMQ=OFF", "-DBUILD_FOR_FUZZING=ON", "-DCMAKE_COMPILE_WARNING_AS_ERROR=ON", @@ -171,7 +170,6 @@ def run_tests(ci_type): "BITCOIN_BENCH": "bench_bitcoin.exe", "BITCOINTX": "bitcoin-tx.exe", "BITCOINUTIL": "bitcoin-util.exe", - "BITCOINWALLET": "bitcoin-wallet.exe", "BITCOINCHAINSTATE": "bitcoin-chainstate.exe", } for var, exe in test_envs.items(): diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1822f9cb97f9..693842bc0aa5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -116,7 +116,7 @@ jobs: git config user.name "CI" - run: | sudo apt-get update - sudo apt-get install clang mold ccache build-essential cmake ninja-build pkgconf python3-zmq libevent-dev libboost-dev libsqlite3-dev systemtap-sdt-dev libzmq3-dev capnproto libcapnp-dev -y + sudo apt-get install clang mold ccache build-essential cmake ninja-build pkgconf python3-zmq libevent-dev libboost-dev systemtap-sdt-dev libzmq3-dev capnproto libcapnp-dev -y sudo pip3 install --break-system-packages pycapnp - name: Compile and run tests run: | @@ -485,12 +485,6 @@ jobs: timeout-minutes: 120 file-env: './ci/test/00_setup_env_freebsd_cross.sh' - - name: 'No wallet' - cirrus-runner: 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-sm' - fallback-runner: 'ubuntu-24.04' - timeout-minutes: 120 - file-env: './ci/test/00_setup_env_native_nowallet.sh' - - name: 'i686, no IPC' cirrus-runner: 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-md' fallback-runner: 'ubuntu-24.04' diff --git a/CMakeLists.txt b/CMakeLists.txt index 92a82cf03165..7baaf235aa67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -115,21 +115,6 @@ option(BUILD_UTIL_CHAINSTATE "Build experimental bitcoin-chainstate executable." option(BUILD_KERNEL_LIB "Build experimental bitcoinkernel library." ${BUILD_UTIL_CHAINSTATE}) cmake_dependent_option(BUILD_KERNEL_TEST "Build tests for the experimental bitcoinkernel library." ON "BUILD_KERNEL_LIB" OFF) -option(ENABLE_WALLET "Enable wallet." ON) -if(ENABLE_WALLET) - if(VCPKG_TARGET_TRIPLET) - # Use of the `unofficial::` namespace is a vcpkg package manager convention. - find_package(unofficial-sqlite3 CONFIG REQUIRED) - add_library(SQLite3::SQLite3 ALIAS unofficial::sqlite3::sqlite3) - else() - find_package(SQLite3 3.7.17 REQUIRED) - if(NOT TARGET SQLite3::SQLite3) # CMake < 4.3 - add_library(SQLite3::SQLite3 ALIAS SQLite::SQLite3) - endif() - endif() -endif() -cmake_dependent_option(BUILD_WALLET_TOOL "Build bitcoin-wallet tool." ${BUILD_TESTS} "ENABLE_WALLET" OFF) - option(REDUCE_EXPORTS "Attempt to reduce exported symbols in the resulting executables." OFF) option(CMAKE_COMPILE_WARNING_AS_ERROR "Treat compiler warnings as errors." OFF) option(WITH_CCACHE "Attempt to use ccache for compiling." ON) @@ -146,8 +131,6 @@ if(WITH_USDT) find_package(USDT MODULE REQUIRED) endif() -option(ENABLE_EXTERNAL_SIGNER "Enable external signer support." ON) - cmake_dependent_option(ENABLE_IPC "Build multiprocess bitcoin-node executable in addition to monolithic bitcoind executable." ON "NOT WIN32" OFF) cmake_dependent_option(WITH_EXTERNAL_LIBMULTIPROCESS "Build with external libmultiprocess library instead of with local git subtree when ENABLE_IPC is enabled. This is not normally recommended, but can be useful for developing libmultiprocess itself." OFF "ENABLE_IPC" OFF) if(ENABLE_IPC AND WITH_EXTERNAL_LIBMULTIPROCESS) @@ -207,8 +190,6 @@ if(BUILD_FOR_FUZZING) set(BUILD_UTIL_CHAINSTATE OFF) set(BUILD_KERNEL_LIB OFF) set(BUILD_KERNEL_TEST OFF) - set(BUILD_WALLET_TOOL OFF) - set(ENABLE_EXTERNAL_SIGNER OFF) set(WITH_ZMQ OFF) set(WITH_EMBEDDED_ASMAP OFF) set(BUILD_TESTS OFF) @@ -624,13 +605,10 @@ message(" bitcoin-node (multiprocess) ......... ${bitcoin_daemon_status}") message(" bitcoin-cli ......................... ${BUILD_CLI}") message(" bitcoin-tx .......................... ${BUILD_TX}") message(" bitcoin-util ........................ ${BUILD_UTIL}") -message(" bitcoin-wallet ...................... ${BUILD_WALLET_TOOL}") message(" bitcoin-chainstate (experimental) ... ${BUILD_UTIL_CHAINSTATE}") message(" libbitcoinkernel (experimental) ..... ${BUILD_KERNEL_LIB}") message(" kernel-test (experimental) .......... ${BUILD_KERNEL_TEST}") message("Optional features:") -message(" wallet support ...................... ${ENABLE_WALLET}") -message(" external signer ..................... ${ENABLE_EXTERNAL_SIGNER}") message(" ZeroMQ .............................. ${WITH_ZMQ}") if(ENABLE_IPC) if (WITH_EXTERNAL_LIBMULTIPROCESS) diff --git a/CMakePresets.json b/CMakePresets.json index 3181d28d5abd..90a2c4f5da85 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -73,9 +73,6 @@ "BUILD_TX": "ON", "BUILD_UTIL": "ON", "BUILD_UTIL_CHAINSTATE": "ON", - "BUILD_WALLET_TOOL": "ON", - "ENABLE_EXTERNAL_SIGNER": "ON", - "ENABLE_WALLET": "ON", "ENABLE_IPC": "ON", "WITH_USDT": "ON", "WITH_ZMQ": "ON" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index efcfaa7ef286..b9a1ed333352 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -151,7 +151,6 @@ the pull request affects. Valid areas are: - `contrib` or `cli` for changes to the scripts and tools - `test`, `qa` or `ci` for changes to the unit tests, QA tests or CI code - `util` or `lib` for changes to the utils or libraries - - `wallet` for changes to the wallet code - `build` for changes to CMake - `guix` for changes to the GUIX reproducible builds diff --git a/README.md b/README.md index 07e09e4f169c..0d9a4022252c 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,7 @@ What is Bitcoin Core? --------------------- Bitcoin Core connects to the Bitcoin peer-to-peer network to download and fully -validate blocks and transactions. It also includes a wallet and graphical user -interface, which can be optionally built. +validate blocks and transactions. Further information about Bitcoin Core is available in the [doc folder](/doc). diff --git a/ci/test/00_setup_env_native_asan.sh b/ci/test/00_setup_env_native_asan.sh index 7d9c7d9b5622..b1b42f333916 100755 --- a/ci/test/00_setup_env_native_asan.sh +++ b/ci/test/00_setup_env_native_asan.sh @@ -20,7 +20,7 @@ fi export CONTAINER_NAME=ci_native_asan export APT_LLVM_V="22" -export PACKAGES="systemtap-sdt-dev clang-${APT_LLVM_V} llvm-${APT_LLVM_V} libclang-rt-${APT_LLVM_V}-dev mold python3-zmq libevent-dev libboost-dev libzmq3-dev libsqlite3-dev ${BPFCC_PACKAGE} libcapnp-dev capnproto python3-pip" +export PACKAGES="systemtap-sdt-dev clang-${APT_LLVM_V} llvm-${APT_LLVM_V} libclang-rt-${APT_LLVM_V}-dev mold python3-zmq libevent-dev libboost-dev libzmq3-dev ${BPFCC_PACKAGE} libcapnp-dev capnproto python3-pip" export PIP_PACKAGES="--break-system-packages pycapnp" export NO_DEPENDS=1 export GOAL="install" diff --git a/ci/test/00_setup_env_native_fuzz.sh b/ci/test/00_setup_env_native_fuzz.sh index cdfe2a0d35b3..61cacbc87138 100755 --- a/ci/test/00_setup_env_native_fuzz.sh +++ b/ci/test/00_setup_env_native_fuzz.sh @@ -9,7 +9,7 @@ export LC_ALL=C.UTF-8 export CI_IMAGE_NAME_TAG="mirror.gcr.io/ubuntu:24.04" export CONTAINER_NAME=ci_native_fuzz export APT_LLVM_V="22" -export PACKAGES="clang-${APT_LLVM_V} llvm-${APT_LLVM_V} libclang-rt-${APT_LLVM_V}-dev libevent-dev libboost-dev libsqlite3-dev libcapnp-dev capnproto" +export PACKAGES="clang-${APT_LLVM_V} llvm-${APT_LLVM_V} libclang-rt-${APT_LLVM_V}-dev libevent-dev libboost-dev libcapnp-dev capnproto" export NO_DEPENDS=1 export RUN_UNIT_TESTS=false export RUN_FUNCTIONAL_TESTS=false diff --git a/ci/test/00_setup_env_native_fuzz_with_valgrind.sh b/ci/test/00_setup_env_native_fuzz_with_valgrind.sh index ed84ae84cf35..6ea545239012 100755 --- a/ci/test/00_setup_env_native_fuzz_with_valgrind.sh +++ b/ci/test/00_setup_env_native_fuzz_with_valgrind.sh @@ -8,7 +8,7 @@ export LC_ALL=C.UTF-8 export CI_IMAGE_NAME_TAG="mirror.gcr.io/debian:trixie" export CONTAINER_NAME=ci_native_fuzz_valgrind -export PACKAGES="clang llvm libclang-rt-dev libevent-dev libboost-dev libsqlite3-dev valgrind libcapnp-dev capnproto" +export PACKAGES="clang llvm libclang-rt-dev libevent-dev libboost-dev valgrind libcapnp-dev capnproto" export NO_DEPENDS=1 export RUN_UNIT_TESTS=false export RUN_FUNCTIONAL_TESTS=false diff --git a/ci/test/00_setup_env_native_iwyu.sh b/ci/test/00_setup_env_native_iwyu.sh index 1afd7aaec63a..fc4dc2c37dc5 100755 --- a/ci/test/00_setup_env_native_iwyu.sh +++ b/ci/test/00_setup_env_native_iwyu.sh @@ -10,7 +10,7 @@ export CI_IMAGE_NAME_TAG="mirror.gcr.io/debian:trixie" # To build codegen, CMak export CONTAINER_NAME=ci_native_iwyu export IWYU_LLVM_V="22" export APT_LLVM_V="${IWYU_LLVM_V}" -export PACKAGES="clang-${IWYU_LLVM_V} clang-format-${IWYU_LLVM_V} libclang-${IWYU_LLVM_V}-dev llvm-${IWYU_LLVM_V}-dev jq libevent-dev libboost-dev libzmq3-dev systemtap-sdt-dev libsqlite3-dev libcapnp-dev capnproto" +export PACKAGES="clang-${IWYU_LLVM_V} clang-format-${IWYU_LLVM_V} libclang-${IWYU_LLVM_V}-dev llvm-${IWYU_LLVM_V}-dev jq libevent-dev libboost-dev libzmq3-dev systemtap-sdt-dev libcapnp-dev capnproto" export NO_DEPENDS=1 export RUN_UNIT_TESTS=false export RUN_FUNCTIONAL_TESTS=false diff --git a/ci/test/00_setup_env_native_nowallet.sh b/ci/test/00_setup_env_native_nowallet.sh deleted file mode 100755 index 0b21ab226a91..000000000000 --- a/ci/test/00_setup_env_native_nowallet.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright (c) 2019-present The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. - -export LC_ALL=C.UTF-8 - -export CONTAINER_NAME=ci_native_nowallet -export CI_IMAGE_NAME_TAG="mirror.gcr.io/ubuntu:24.04" -# Use minimum supported python3.10 (or best-effort 3.12) and clang-17, see doc/dependencies.md -export PACKAGES="python3-zmq python3-pip clang-17 llvm-17 libc++abi-17-dev libc++-17-dev" -export PIP_PACKAGES="--break-system-packages pycapnp" -export DEP_OPTS="NO_WALLET=1 CC=clang-17 CXX='clang++-17 -stdlib=libc++'" -export GOAL="install" -export BITCOIN_CONFIG="\ - --preset=dev-mode \ - -DREDUCE_EXPORTS=ON \ - -DENABLE_WALLET=OFF \ - -DWITH_EMBEDDED_ASMAP=OFF \ -" diff --git a/ci/test/00_setup_env_native_tidy.sh b/ci/test/00_setup_env_native_tidy.sh index 0e8240c2e5e6..6234d596520d 100755 --- a/ci/test/00_setup_env_native_tidy.sh +++ b/ci/test/00_setup_env_native_tidy.sh @@ -10,7 +10,7 @@ export CI_IMAGE_NAME_TAG="mirror.gcr.io/ubuntu:24.04" export CONTAINER_NAME=ci_native_tidy export TIDY_LLVM_V="22" export APT_LLVM_V="${TIDY_LLVM_V}" -export PACKAGES="clang-${TIDY_LLVM_V} libclang-${TIDY_LLVM_V}-dev llvm-${TIDY_LLVM_V}-dev libomp-${TIDY_LLVM_V}-dev clang-tidy-${TIDY_LLVM_V} jq libevent-dev libboost-dev libzmq3-dev systemtap-sdt-dev libsqlite3-dev libcapnp-dev capnproto" +export PACKAGES="clang-${TIDY_LLVM_V} libclang-${TIDY_LLVM_V}-dev llvm-${TIDY_LLVM_V}-dev libomp-${TIDY_LLVM_V}-dev clang-tidy-${TIDY_LLVM_V} jq libevent-dev libboost-dev libzmq3-dev systemtap-sdt-dev libcapnp-dev capnproto" export NO_DEPENDS=1 export RUN_UNIT_TESTS=false export RUN_FUNCTIONAL_TESTS=false diff --git a/ci/test/00_setup_env_native_valgrind.sh b/ci/test/00_setup_env_native_valgrind.sh index f1b3d529bb70..3c997f24b36c 100755 --- a/ci/test/00_setup_env_native_valgrind.sh +++ b/ci/test/00_setup_env_native_valgrind.sh @@ -8,7 +8,7 @@ export LC_ALL=C.UTF-8 export CI_IMAGE_NAME_TAG="mirror.gcr.io/debian:trixie" export CONTAINER_NAME=ci_native_valgrind -export PACKAGES="clang llvm libclang-rt-dev valgrind python3-zmq libevent-dev libboost-dev libzmq3-dev libsqlite3-dev libcapnp-dev capnproto python3-pip" +export PACKAGES="clang llvm libclang-rt-dev valgrind python3-zmq libevent-dev libboost-dev libzmq3-dev libcapnp-dev capnproto python3-pip" export PIP_PACKAGES="--break-system-packages pycapnp" export USE_VALGRIND=1 export NO_DEPENDS=1 diff --git a/cmake/bitcoin-build-config.h.in b/cmake/bitcoin-build-config.h.in index 9d7ff1e9d236..0897decbebdb 100644 --- a/cmake/bitcoin-build-config.h.in +++ b/cmake/bitcoin-build-config.h.in @@ -29,16 +29,10 @@ /* Copyright year */ #define COPYRIGHT_YEAR @COPYRIGHT_YEAR@ -/* Define if external signer support is enabled */ -#cmakedefine ENABLE_EXTERNAL_SIGNER 1 - /* Define to 1 to enable tracepoints for Userspace, Statically Defined Tracing */ #cmakedefine ENABLE_TRACING 1 -/* Define to 1 to enable wallet functions. */ -#cmakedefine ENABLE_WALLET 1 - /* Define to 1 if you have the declaration of `fork', and to 0 if you don't. */ #cmakedefine01 HAVE_DECL_FORK diff --git a/contrib/completions/bash/bitcoin-cli.bash b/contrib/completions/bash/bitcoin-cli.bash index ff34ad9ff6e2..ffb107dbe3bb 100644 --- a/contrib/completions/bash/bitcoin-cli.bash +++ b/contrib/completions/bash/bitcoin-cli.bash @@ -28,31 +28,9 @@ _bitcoin_cli() { COMPREPLY=() _get_comp_words_by_ref -n = cur prev words cword - if ((cword > 5)); then - case ${words[cword-5]} in - sendtoaddress) - COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) - return 0 - ;; - esac - fi - - if ((cword > 4)); then - case ${words[cword-4]} in - listtransactions|setban) - COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) - return 0 - ;; - signrawtransactionwithkey|signrawtransactionwithwallet) - COMPREPLY=( $( compgen -W "ALL NONE SINGLE ALL|ANYONECANPAY NONE|ANYONECANPAY SINGLE|ANYONECANPAY" -- "$cur" ) ) - return 0 - ;; - esac - fi - if ((cword > 3)); then case ${words[cword-3]} in - getbalance|gettxout|listreceivedbyaddress|listsinceblock) + gettxout) COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) return 0 ;; @@ -69,7 +47,7 @@ _bitcoin_cli() { COMPREPLY=( $( compgen -W "add remove" -- "$cur" ) ) return 0 ;; - fundrawtransaction|getblock|getblockheader|getmempoolancestors|getmempooldescendants|getrawtransaction|gettransaction|listreceivedbyaddress|sendrawtransaction) + getblock|getblockheader|getmempoolancestors|getmempooldescendants|getrawtransaction|sendrawtransaction) COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) return 0 ;; @@ -77,17 +55,10 @@ _bitcoin_cli() { fi case "$prev" in - backupwallet) - _filedir - return 0 - ;; - getaddednodeinfo|getrawmempool|lockunspent) + getaddednodeinfo|getrawmempool) COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) return 0 ;; - getbalance|getnewaddress|listtransactions|sendmany) - return 0 - ;; esac case "$cur" in diff --git a/contrib/completions/bash/bitcoind.bash b/contrib/completions/bash/bitcoind.bash index 78e4d4e5ec04..e291a4a4b7e5 100644 --- a/contrib/completions/bash/bitcoind.bash +++ b/contrib/completions/bash/bitcoind.bash @@ -15,7 +15,7 @@ _bitcoind() { _get_comp_words_by_ref -n = cur prev words cword case "$cur" in - -conf=*|-pid=*|-loadblock=*|-rpccookiefile=*|-wallet=*) + -conf=*|-pid=*|-loadblock=*|-rpccookiefile=*) cur="${cur#*=}" _filedir return 0 diff --git a/contrib/completions/fish/bitcoin-cli.fish b/contrib/completions/fish/bitcoin-cli.fish index 2f034c475c1d..3fa663b2ccb5 100644 --- a/contrib/completions/fish/bitcoin-cli.fish +++ b/contrib/completions/fish/bitcoin-cli.fish @@ -40,7 +40,7 @@ function __fish_bitcoin_cli_get_commands end if string match -q -r '^.*error.*$' $commands[1] - # RPC offline or RPC wallet not loaded + # RPC offline return else for command in $commands diff --git a/contrib/completions/fish/bitcoin-wallet.fish b/contrib/completions/fish/bitcoin-wallet.fish deleted file mode 100644 index 82d8277c9b4f..000000000000 --- a/contrib/completions/fish/bitcoin-wallet.fish +++ /dev/null @@ -1,35 +0,0 @@ -# Disable files from being included in completions by default -complete --command bitcoin-wallet --no-files - -# Extract options -function __fish_bitcoin_wallet_get_options - set --local cmd (commandline -opc)[1] - for option in ($cmd -help 2>&1 | string match -r '^ -.*' | string replace -r ' -' '-' | string replace -r '=.*' '=') - echo $option - end -end - -# Extract commands -function __fish_bitcoin_wallet_get_commands - set --local cmd (commandline -opc)[1] - for command in ($cmd -help | sed -e '1,/Commands:/d' -e 's/=/=\t/' -e 's/(=/=/' -e '/^ [a-z]/ p' -e d | string replace -r '\ \ ' '') - echo $command - end -end - -# Add options -complete \ - --command bitcoin-wallet \ - --condition "not __fish_seen_subcommand_from (__fish_bitcoin_wallet_get_commands)" \ - --arguments "(__fish_bitcoin_wallet_get_options)" - -# Add commands -complete \ - --command bitcoin-wallet \ - --condition "not __fish_seen_subcommand_from (__fish_bitcoin_wallet_get_commands)" \ - --arguments "(__fish_bitcoin_wallet_get_commands)" - -# Add file completions for load and set commands -complete --command bitcoin-wallet \ - --condition "string match -r -- '(dumpfile|datadir)*=' (commandline -pt)" \ - --force-files diff --git a/contrib/devtools/check-deps.sh b/contrib/devtools/check-deps.sh index 0ae817254d78..e1ae45d08a60 100755 --- a/contrib/devtools/check-deps.sh +++ b/contrib/devtools/check-deps.sh @@ -11,7 +11,6 @@ LIBS[consensus]="libbitcoin_consensus.a" LIBS[crypto]="libbitcoin_crypto.a" LIBS[node]="libbitcoin_node.a" LIBS[util]="libbitcoin_util.a" -LIBS[wallet]="libbitcoin_wallet.a" # Declare allowed dependencies "X Y" where X is allowed to depend on Y. This # list is taken from doc/design/libraries.md. @@ -28,15 +27,6 @@ ALLOWED_DEPENDENCIES=( "node kernel" "node util" "util crypto" - "wallet common" - "wallet crypto" - "wallet util" -) - -# Add minor dependencies omitted from doc/design/libraries.md to keep the -# dependency diagram simple. -ALLOWED_DEPENDENCIES+=( - "wallet consensus" ) # Declare list of known errors that should be suppressed. diff --git a/contrib/devtools/gen-manpages.py b/contrib/devtools/gen-manpages.py index 6266c42d41c6..b01650e71806 100755 --- a/contrib/devtools/gen-manpages.py +++ b/contrib/devtools/gen-manpages.py @@ -14,7 +14,6 @@ 'bin/bitcoind', 'bin/bitcoin-cli', 'bin/bitcoin-tx', -'bin/bitcoin-wallet', 'bin/bitcoin-util', ] diff --git a/contrib/signet/README.md b/contrib/signet/README.md deleted file mode 100644 index 499479857197..000000000000 --- a/contrib/signet/README.md +++ /dev/null @@ -1,83 +0,0 @@ -Contents -======== -This directory contains tools related to Signet, both for running a Signet yourself and for using one. - -getcoins.py -=========== - -A script to call a faucet to get Signet coins. - -Syntax: `getcoins.py [-h|--help] [-c|--cmd=] [-f|--faucet=] [-a|--addr=] [-p|--password=] [--] []` - -* `--cmd` lets you customize the bitcoin-cli path. By default it will look for it in the PATH -* `--faucet` lets you specify which faucet to use; the faucet is assumed to be compatible with https://github.com/kallewoof/bitcoin-faucet -* `--addr` lets you specify a Signet address; by default, the address must be a bech32 address. This and `--cmd` above complement each other (i.e. you do not need `bitcoin-cli` if you use `--addr`) -* `--password` lets you specify a faucet password; this is handy if you are in a classroom and set up your own faucet for your students; (above faucet does not limit by IP when password is enabled) - -If using the default network, invoking the script with no arguments should be sufficient under normal -circumstances, but if multiple people are behind the same IP address, the faucet will by default only -accept one claim per day. See `--password` above. - -miner -===== - -You will first need to pick a difficulty target. Since signet chains are primarily protected by a signature rather than proof of work, there is no need to spend as much energy as possible mining, however you may wish to choose to spend more time than the absolute minimum. The calibrate subcommand can be used to pick a target appropriate for your hardware, eg: - - MINER="./contrib/signet/miner" - GRIND="./build/bin/bitcoin-util grind" - $MINER calibrate --grind-cmd="$GRIND" - nbits=1e00f403 for 25s average mining time - -It defaults to estimating an nbits value resulting in 25s average time to find a block, but the --seconds parameter can be used to pick a different target, or the --nbits parameter can be used to estimate how long it will take for a given difficulty. - -To mine the first block in your custom chain, you can run: - - CLI="./build/bin/bitcoin-cli -conf=mysignet.conf" - ADDR=$($CLI -signet getnewaddress) - NBITS=1e00f403 - $MINER --cli="$CLI" generate --grind-cmd="$GRIND" --address="$ADDR" --nbits=$NBITS - -This will mine a single block with a backdated timestamp designed to allow 100 blocks to be mined as quickly as possible, so that it is possible to do transactions. - -Adding the --ongoing parameter will then cause the signet miner to create blocks indefinitely. It will pick the time between blocks so that difficulty is adjusted to match the provided --nbits value. - - $MINER --cli="$CLI" generate --grind-cmd="$GRIND" --address="$ADDR" --nbits=$NBITS --ongoing - -Other options -------------- - -The --debug and --quiet options are available to control how noisy the signet miner's output is. Note that the --debug, --quiet and --cli parameters must all appear before the subcommand (generate, calibrate, etc) if used. - -Instead of specifying --ongoing, you can specify --max-blocks=N to mine N blocks and stop. - -The --set-block-time option is available to manually move timestamps forward or backward (subject to the rules that blocktime must be greater than mediantime, and dates can't be more than two hours in the future). It can only be used when mining a single block (ie, not when using --ongoing or --max-blocks greater than 1). - -Instead of using a single address, a ranged descriptor may be provided via the --descriptor parameter, with the reward for the block at height H being sent to the H'th address generated from the descriptor. - -Instead of calculating a specific nbits value, --min-nbits can be specified instead, in which case the minimum signet difficulty will be targeted. Signet's minimum difficulty corresponds to --nbits=1e0377ae. - -By default, the signet miner mines blocks at fixed intervals with minimal variation. If you want blocks to appear more randomly, as they do in mainnet, specify the --poisson option. - -Using the --multiminer parameter allows mining to be distributed amongst multiple miners. For example, if you have 3 miners and want to share blocks between them, specify --multiminer=1/3 on one, --multiminer=2/3 on another, and --multiminer=3/3 on the last one. If you want one to do 10% of blocks and two others to do 45% each, --multiminer=1-10/100 on the first, and --multiminer=11-55 and --multiminer=56-100 on the others. Note that which miner mines which block is determined by the previous block hash, so occasional runs of one miner doing many blocks in a row is to be expected. - -When --multiminer is used, if a miner is down and does not mine a block within five minutes of when it is due, the other miners will automatically act as redundant backups ensuring the chain does not halt. The --backup-delay parameter can be used to change how long a given miner waits, allowing one to be the primary backup (after five minutes) and another to be the secondary backup (after six minutes, eg). - -The --standby-delay parameter can be used to make a backup miner that only mines if a block doesn't arrive on time. This can be combined with --multiminer if desired. Setting --standby-delay also prevents the first block from being mined immediately. - -Advanced usage --------------- - -The process generate follows internally is to get a block template, convert that into a PSBT, sign the PSBT, move the signature from the signed PSBT into the block template's coinbase, grind proof of work for the block, and then submit the block to the network. - -These steps can instead be done explicitly: - - $CLI -signet getblocktemplate '{"rules": ["signet","segwit"]}' | - $MINER --cli="$CLI" genpsbt --address="$ADDR" | - $CLI -signet -stdin walletprocesspsbt | - jq -r .psbt | - $MINER --cli="$CLI" solvepsbt --grind-cmd="$GRIND" | - $CLI -signet -stdin submitblock - -This is intended to allow you to replace part of the pipeline for further experimentation (eg, to sign the block with a hardware wallet). - -For custom signets with a trivial challenge such as `OP_TRUE` and `OP_2` the walletprocesspsbt step can be skipped. diff --git a/contrib/signet/getcoins.py b/contrib/signet/getcoins.py deleted file mode 100755 index b58ed04aabae..000000000000 --- a/contrib/signet/getcoins.py +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2020-present The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. - -import argparse -import io -import requests -import subprocess -import sys -import xml.etree.ElementTree - -DEFAULT_GLOBAL_FAUCET = 'https://signetfaucet.com/claim' -DEFAULT_GLOBAL_CAPTCHA = 'https://signetfaucet.com/captcha' -GLOBAL_FIRST_BLOCK_HASH = '00000086d6b2636cb2a392d45edc4ec544a10024d30141c9adf4bfd9de533b53' - -# braille unicode block -BASE = 0x2800 -BIT_PER_PIXEL = [ - [0x01, 0x08], - [0x02, 0x10], - [0x04, 0x20], - [0x40, 0x80], -] -BW = 2 -BH = 4 - -# imagemagick or compatible fork (used for converting SVG) -CONVERT = 'convert' - -class PPMImage: - ''' - Load a PPM image (Pillow-ish API). - ''' - def __init__(self, f): - if f.readline() != b'P6\n': - raise ValueError('Invalid ppm format: header') - line = f.readline() - (width, height) = (int(x) for x in line.rstrip().split(b' ')) - if f.readline() != b'255\n': - raise ValueError('Invalid ppm format: color depth') - data = f.read(width * height * 3) - stride = width * 3 - self.size = (width, height) - self._grid = [[tuple(data[stride * y + 3 * x:stride * y + 3 * (x + 1)]) for x in range(width)] for y in range(height)] - - def getpixel(self, pos): - return self._grid[pos[1]][pos[0]] - -def print_image(img, threshold=128): - '''Print black-and-white image to terminal in braille unicode characters.''' - x_blocks = (img.size[0] + BW - 1) // BW - y_blocks = (img.size[1] + BH - 1) // BH - - for yb in range(y_blocks): - line = [] - for xb in range(x_blocks): - ch = BASE - for y in range(BH): - for x in range(BW): - try: - val = img.getpixel((xb * BW + x, yb * BH + y)) - except IndexError: - pass - else: - if val[0] < threshold: - ch |= BIT_PER_PIXEL[y][x] - line.append(chr(ch)) - print(''.join(line)) - -parser = argparse.ArgumentParser(description='Script to get coins from a faucet.', epilog='You may need to start with double-dash (--) when providing bitcoin-cli arguments.') -parser.add_argument('-c', '--cmd', dest='cmd', default='bitcoin-cli', help='bitcoin-cli command to use') -parser.add_argument('-f', '--faucet', dest='faucet', default=DEFAULT_GLOBAL_FAUCET, help='URL of the faucet') -parser.add_argument('-g', '--captcha', dest='captcha', default=DEFAULT_GLOBAL_CAPTCHA, help='URL of the faucet captcha, or empty if no captcha is needed') -parser.add_argument('-a', '--addr', dest='addr', default='', help='Bitcoin address to which the faucet should send') -parser.add_argument('-p', '--password', dest='password', default='', help='Faucet password, if any') -parser.add_argument('-n', '--amount', dest='amount', default='0.001', help='Amount to request (0.001-0.1, default is 0.001)') -parser.add_argument('-i', '--imagemagick', dest='imagemagick', default=CONVERT, help='Path to imagemagick convert utility') -parser.add_argument('bitcoin_cli_args', nargs='*', help='Arguments to pass on to bitcoin-cli (default: -signet)') - -args = parser.parse_args() - -if args.bitcoin_cli_args == []: - args.bitcoin_cli_args = ['-signet'] - - -def bitcoin_cli(rpc_command_and_params): - argv = [args.cmd] + args.bitcoin_cli_args + rpc_command_and_params - try: - return subprocess.check_output(argv).strip().decode() - except FileNotFoundError: - raise SystemExit(f"The binary {args.cmd} could not be found") - except subprocess.CalledProcessError: - cmdline = ' '.join(argv) - raise SystemExit(f"-----\nError while calling {cmdline} (see output above).") - - -if args.faucet.lower() == DEFAULT_GLOBAL_FAUCET: - # Get the hash of the block at height 1 of the currently active signet chain - curr_signet_hash = bitcoin_cli(['getblockhash', '1']) - if curr_signet_hash != GLOBAL_FIRST_BLOCK_HASH: - raise SystemExit('The global faucet cannot be used with a custom Signet network. Please use the global signet or setup your custom faucet to use this functionality.\n') -else: - # For custom faucets, don't request captcha by default. - if args.captcha == DEFAULT_GLOBAL_CAPTCHA: - args.captcha = '' - -if args.addr == '': - # get address for receiving coins - args.addr = bitcoin_cli(['getnewaddress', 'faucet', 'bech32']) - -data = {'address': args.addr, 'password': args.password, 'amount': args.amount} - -# Store cookies -# for debugging: print(session.cookies.get_dict()) -session = requests.Session() - -if args.captcha != '': # Retrieve a captcha - try: - res = session.get(args.captcha) - res.raise_for_status() - except requests.exceptions.RequestException as e: - raise SystemExit(f"Unexpected error when contacting faucet: {e}") - - # Size limitation - svg = xml.etree.ElementTree.fromstring(res.content) - if svg.attrib.get('width') != '150' or svg.attrib.get('height') != '50': - raise SystemExit("Captcha size doesn't match expected dimensions 150x50") - - # Convert SVG image to PPM, and load it - try: - rv = subprocess.run([args.imagemagick, 'svg:-', '-depth', '8', 'ppm:-'], input=res.content, check=True, capture_output=True) - except FileNotFoundError: - raise SystemExit(f"The binary {args.imagemagick} could not be found. Please make sure ImageMagick (or a compatible fork) is installed and that the correct path is specified.") - - img = PPMImage(io.BytesIO(rv.stdout)) - - # Terminal interaction - print_image(img) - print(f"Captcha from URL {args.captcha}") - data['captcha'] = input('Enter captcha: ') - -try: - res = session.post(args.faucet, data=data) -except Exception: - raise SystemExit(f"Unexpected error when contacting faucet: {sys.exc_info()[0]}") - -# Display the output as per the returned status code -if res: - # When the return code is in between 200 and 400 i.e. successful - print(res.text) -elif res.status_code == 404: - print('The specified faucet URL does not exist. Please check for any server issues/typo.') -elif res.status_code == 429: - print('The script does not allow for repeated transactions as the global faucet is rate-limited to 1 request/IP/day. You can access the faucet website to get more coins manually') -else: - print(f'Returned Error Code {res.status_code}\n{res.text}\n') - print('Please check the provided arguments for their validity and/or any possible typo.') diff --git a/contrib/signet/miner b/contrib/signet/miner deleted file mode 100755 index 1d9f87203c13..000000000000 --- a/contrib/signet/miner +++ /dev/null @@ -1,604 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2020-present The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. - -import argparse -import json -import logging -import math -import os -import re -import shlex -import sys -import time -import subprocess - -PATH_BASE_CONTRIB_SIGNET = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) -PATH_BASE_TEST_FUNCTIONAL = os.path.abspath(os.path.join(PATH_BASE_CONTRIB_SIGNET, "..", "..", "test", "functional")) -sys.path.insert(0, PATH_BASE_TEST_FUNCTIONAL) - -from test_framework.blocktools import get_witness_script, script_BIP34_coinbase_height, SIGNET_HEADER # noqa: E402 -from test_framework.messages import CBlock, CBlockHeader, COutPoint, CTransaction, CTxIn, CTxInWitness, CTxOut, from_binary, from_hex, ser_string, ser_uint256, tx_from_hex, MAX_SEQUENCE_NONFINAL # noqa: E402 -from test_framework.psbt import PSBT, PSBTMap, PSBT_GLOBAL_UNSIGNED_TX, PSBT_IN_FINAL_SCRIPTSIG, PSBT_IN_FINAL_SCRIPTWITNESS, PSBT_IN_NON_WITNESS_UTXO, PSBT_IN_SIGHASH_TYPE # noqa: E402 -from test_framework.script import CScript, CScriptOp # noqa: E402 - -logging.basicConfig( - format='%(asctime)s %(levelname)s %(message)s', - level=logging.INFO, - datefmt='%Y-%m-%d %H:%M:%S') - -PSBT_SIGNET_BLOCK = b"\xfc\x06signetb" # proprietary PSBT global field holding the block being signed -RE_MULTIMINER = re.compile(r"^(\d+)(-(\d+))?/(\d+)$") - -def signet_txs(block, challenge): - # assumes signet solution has not been added yet so does not need - # to be removed - - txs = block.vtx[:] - txs[0] = CTransaction(txs[0]) - txs[0].vout[-1].scriptPubKey += CScriptOp.encode_op_pushdata(SIGNET_HEADER) - hashes = [] - for tx in txs: - hashes.append(ser_uint256(tx.txid_int)) - mroot = block.get_merkle_root(hashes) - - sd = b"" - sd += block.nVersion.to_bytes(4, "little", signed=True) - sd += ser_uint256(block.hashPrevBlock) - sd += ser_uint256(mroot) - sd += block.nTime.to_bytes(4, "little") - - to_spend = CTransaction() - to_spend.version = 0 - to_spend.nLockTime = 0 - to_spend.vin = [CTxIn(COutPoint(0, 0xFFFFFFFF), b"\x00" + CScriptOp.encode_op_pushdata(sd), 0)] - to_spend.vout = [CTxOut(0, challenge)] - - spend = CTransaction() - spend.version = 0 - spend.nLockTime = 0 - spend.vin = [CTxIn(COutPoint(to_spend.txid_int, 0), b"", 0)] - spend.vout = [CTxOut(0, b"\x6a")] - - return spend, to_spend - -def decode_challenge_psbt(b64psbt): - psbt = PSBT.from_base64(b64psbt) - - assert len(psbt.i) == 1 - assert len(psbt.o) == 1 - assert PSBT_SIGNET_BLOCK in psbt.g.map - return psbt - -def get_block_from_psbt(psbt): - return from_binary(CBlock, psbt.g.map[PSBT_SIGNET_BLOCK]) - -def get_solution_from_psbt(psbt, emptyok=False): - scriptSig = psbt.i[0].map.get(PSBT_IN_FINAL_SCRIPTSIG, b"") - scriptWitness = psbt.i[0].map.get(PSBT_IN_FINAL_SCRIPTWITNESS, b"\x00") - if emptyok and len(scriptSig) == 0 and scriptWitness == b"\x00": - return None - return ser_string(scriptSig) + scriptWitness - -def finish_block(block, signet_solution, grind_cmd): - if signet_solution is None: - pass # Don't need to add a signet commitment if there's no signet signature needed - else: - block.vtx[0].vout[-1].scriptPubKey += CScriptOp.encode_op_pushdata(SIGNET_HEADER + signet_solution) - block.hashMerkleRoot = block.calc_merkle_root() - if grind_cmd is None: - block.solve() - else: - headhex = CBlockHeader.serialize(block).hex() - cmd = shlex.split(grind_cmd) + [headhex] - newheadhex = subprocess.run(cmd, stdout=subprocess.PIPE, input=b"", check=True).stdout.strip() - newhead = from_hex(CBlockHeader(), newheadhex.decode('utf8')) - block.nNonce = newhead.nNonce - return block - -def new_block(tmpl, reward_spk, *, blocktime=None, poolid=None): - scriptSig = script_BIP34_coinbase_height(tmpl["height"]) - if poolid is not None: - scriptSig = CScript(b"" + scriptSig + CScriptOp.encode_op_pushdata(poolid)) - - cbtx = CTransaction() - cbtx.nLockTime = tmpl["height"] - 1 - cbtx.vin = [CTxIn(COutPoint(0, 0xffffffff), scriptSig, MAX_SEQUENCE_NONFINAL)] - cbtx.vout = [CTxOut(tmpl["coinbasevalue"], reward_spk)] - cbtx.vin[0].nSequence = 2**32-2 - - block = CBlock() - block.nVersion = tmpl["version"] - block.hashPrevBlock = int(tmpl["previousblockhash"], 16) - block.nTime = tmpl["curtime"] if blocktime is None else blocktime - if block.nTime < tmpl["mintime"]: - block.nTime = tmpl["mintime"] - block.nBits = int(tmpl["bits"], 16) - block.nNonce = 0 - block.vtx = [cbtx] + [tx_from_hex(t["data"]) for t in tmpl["transactions"]] - - witnonce = 0 - witroot = block.calc_witness_merkle_root() - cbwit = CTxInWitness() - cbwit.scriptWitness.stack = [ser_uint256(witnonce)] - block.vtx[0].wit.vtxinwit = [cbwit] - block.vtx[0].vout.append(CTxOut(0, bytes(get_witness_script(witroot, witnonce)))) - - block.hashMerkleRoot = block.calc_merkle_root() - - return block - -def generate_psbt(block, signet_spk): - signet_spk_bin = bytes.fromhex(signet_spk) - signme, spendme = signet_txs(block, signet_spk_bin) - psbt = PSBT() - psbt.g = PSBTMap( {PSBT_GLOBAL_UNSIGNED_TX: signme.serialize(), - PSBT_SIGNET_BLOCK: block.serialize() - } ) - psbt.i = [ PSBTMap( {PSBT_IN_NON_WITNESS_UTXO: spendme.serialize(), - PSBT_IN_SIGHASH_TYPE: bytes([1,0,0,0])}) - ] - psbt.o = [ PSBTMap() ] - return psbt.to_base64() - -def get_poolid(args): - if args.poolid is not None: - return args.poolid.encode('utf8') - elif args.poolnum is not None: - return b"/signet:%d/" % (args.poolnum) - else: - return None - -def get_reward_addr_spk(args, height): - assert args.address is not None or args.descriptor is not None - - if hasattr(args, "reward_spk"): - return args.address, args.reward_spk - - if args.address is not None: - reward_addr = args.address - elif '*' not in args.descriptor: - reward_addr = args.address = json.loads(args.bcli("deriveaddresses", args.descriptor))[0] - else: - remove = [k for k in args.derived_addresses.keys() if k+20 <= height] - for k in remove: - del args.derived_addresses[k] - if height not in args.derived_addresses: - addrs = json.loads(args.bcli("deriveaddresses", args.descriptor, "[%d,%d]" % (height, height+20))) - for k, a in enumerate(addrs): - args.derived_addresses[height+k] = a - reward_addr = args.derived_addresses[height] - - reward_spk = bytes.fromhex(json.loads(args.bcli("getaddressinfo", reward_addr))["scriptPubKey"]) - if args.address is not None: - # will always be the same, so cache - args.reward_spk = reward_spk - - return reward_addr, reward_spk - -def do_genpsbt(args): - poolid = get_poolid(args) - tmpl = json.load(sys.stdin) - signet_spk = tmpl["signet_challenge"] - _, reward_spk = get_reward_addr_spk(args, tmpl["height"]) - block = new_block(tmpl, reward_spk, poolid=poolid) - psbt = generate_psbt(block, signet_spk) - print(psbt) - -def do_solvepsbt(args): - psbt = decode_challenge_psbt(sys.stdin.read()) - block = get_block_from_psbt(psbt) - signet_solution = get_solution_from_psbt(psbt, emptyok=True) - block = finish_block(block, signet_solution, args.grind_cmd) - print(block.serialize().hex()) - -def nbits_to_target(nbits): - shift = (nbits >> 24) & 0xff - return (nbits & 0x00ffffff) * 2**(8*(shift - 3)) - -def target_to_nbits(target): - tstr = "{0:x}".format(target) - if len(tstr) < 6: - tstr = ("000000"+tstr)[-6:] - if len(tstr) % 2 != 0: - tstr = "0" + tstr - if int(tstr[0],16) >= 0x8: - # avoid "negative" - tstr = "00" + tstr - fix = int(tstr[:6], 16) - sz = len(tstr)//2 - if tstr[6:] != "0"*(sz*2-6): - fix += 1 - - return int("%02x%06x" % (sz,fix), 16) - -def seconds_to_hms(s): - if s == 0: - return "0s" - neg = (s < 0) - if neg: - s = -s - out = "" - if s % 60 > 0: - out = "%ds" % (s % 60) - s //= 60 - if s % 60 > 0: - out = "%dm%s" % (s % 60, out) - s //= 60 - if s > 0: - out = "%dh%s" % (s, out) - if neg: - out = "-" + out - return out - -def trivial_challenge(spkhex): - """ - BIP325 allows omitting the signet commitment when scriptSig and - scriptWitness are both empty. This is the case for trivial - challenges such as OP_TRUE or a single data push. - """ - spk = bytes.fromhex(spkhex) - if len(spk) == 1 and 0x51 <= spk[0] <= 0x60: - # OP_TRUE/OP_1...OP_16 - return True - elif 2 <= len(spk) <= 76 and spk[0] + 1 == len(spk): - # Single fixed push of 1-75 bytes - return True - return False - -class Generate: - INTERVAL = 600.0*2016/2015 # 10 minutes, adjusted for the off-by-one bug - - - def __init__(self, multiminer=None, ultimate_target=None, poisson=False, max_interval=1800, - standby_delay=0, backup_delay=0, set_block_time=None, - poolid=None): - if multiminer is None: - multiminer = (0, 1, 1) - (self.multi_low, self.multi_high, self.multi_period) = multiminer - self.ultimate_target = ultimate_target - self.poisson = poisson - self.max_interval = max_interval - self.standby_delay = standby_delay - self.backup_delay = backup_delay - self.set_block_time = set_block_time - self.poolid = poolid - - def next_block_delta(self, last_nbits, last_hash): - # strategy: - # 1) work out how far off our desired target we are - # 2) cap it to a factor of 4 since that's the best we can do in a single retarget period - # 3) use that to work out the desired average interval in this retarget period - # 4) if doing poisson, use the last hash to pick a uniformly random number in [0,1), and work out a random multiplier to vary the average by - # 5) cap the resulting interval between 1 second and 1 hour to avoid extremes - - current_target = nbits_to_target(last_nbits) - retarget_factor = self.ultimate_target / current_target - retarget_factor = max(0.25, min(retarget_factor, 4.0)) - - avg_interval = self.INTERVAL * retarget_factor - - if self.poisson: - det_rand = int(last_hash[-8:], 16) * 2**-32 - this_interval_variance = -math.log1p(-det_rand) - else: - this_interval_variance = 1 - - this_interval = avg_interval * this_interval_variance - this_interval = max(1, min(this_interval, self.max_interval)) - - return this_interval - - def next_block_is_mine(self, last_hash): - det_rand = int(last_hash[-16:-8], 16) - return self.multi_low <= (det_rand % self.multi_period) < self.multi_high - - def next_block_time(self, now, bestheader, is_first_block): - if self.set_block_time is not None: - logging.debug("Setting start time to %d", self.set_block_time) - self.mine_time = self.set_block_time - self.action_time = now - self.is_mine = True - elif bestheader["height"] == 0: - time_delta = self.INTERVAL * 100 # plenty of time to mine 100 blocks - logging.info("Backdating time for first block to %d minutes ago" % (time_delta/60)) - self.mine_time = now - time_delta - self.action_time = now - self.is_mine = True - else: - time_delta = self.next_block_delta(int(bestheader["bits"], 16), bestheader["hash"]) - self.mine_time = bestheader["time"] + time_delta - - self.is_mine = self.next_block_is_mine(bestheader["hash"]) - - self.action_time = self.mine_time - if not self.is_mine: - self.action_time += self.backup_delay - - if self.standby_delay > 0: - self.action_time += self.standby_delay - elif is_first_block: - # for non-standby, always mine immediately on startup, - # even if the next block shouldn't be ours - self.action_time = now - - # don't want fractional times so round down - self.mine_time = int(self.mine_time) - self.action_time = int(self.action_time) - - # can't mine a block 2h in the future; 1h55m for some safety - self.action_time = max(self.action_time, self.mine_time - 6900) - - def gbt(self, bcli, bestblockhash, now): - tmpl = json.loads(bcli("getblocktemplate", '{"rules":["signet","segwit"]}')) - if tmpl["previousblockhash"] != bestblockhash: - logging.warning("GBT based off unexpected block (%s not %s), retrying", tmpl["previousblockhash"], bci["bestblockhash"]) - time.sleep(1) - return None - - if tmpl["mintime"] > self.mine_time: - logging.info("Updating block time from %d to %d", self.mine_time, tmpl["mintime"]) - self.mine_time = tmpl["mintime"] - if self.mine_time > now: - logging.error("GBT mintime is in the future: %d is %d seconds later than %d", self.mine_time, (self.mine_time-now), now) - return None - - return tmpl - - def mine(self, bcli, grind_cmd, tmpl, reward_spk): - block = new_block(tmpl, reward_spk, blocktime=self.mine_time, poolid=self.poolid) - - signet_spk = tmpl["signet_challenge"] - if trivial_challenge(signet_spk): - signet_solution = None - else: - psbt = generate_psbt(block, signet_spk) - input_stream = os.linesep.join([psbt, "true", "ALL"]).encode('utf8') - psbt_signed = json.loads(bcli("-stdin", "walletprocesspsbt", input=input_stream)) - if not psbt_signed.get("complete",False): - logging.debug("Generated PSBT: %s" % (psbt,)) - sys.stderr.write("PSBT signing failed\n") - return None - psbt = decode_challenge_psbt(psbt_signed["psbt"]) - signet_solution = get_solution_from_psbt(psbt) - - return finish_block(block, signet_solution, grind_cmd) - -def do_generate(args): - if args.set_block_time is not None: - max_blocks = 1 - elif args.max_blocks is not None: - if args.max_blocks < 1: - logging.error("--max_blocks must specify a positive integer") - return 1 - max_blocks = args.max_blocks - elif args.ongoing: - max_blocks = None - else: - max_blocks = 1 - - if args.set_block_time is not None and args.set_block_time < 0: - args.set_block_time = time.time() - logging.info("Treating negative block time as current time (%d)" % (args.set_block_time)) - - if args.min_nbits: - args.nbits = "1e0377ae" - logging.info("Using nbits=%s" % (args.nbits)) - - if args.set_block_time is None: - if args.nbits is None or len(args.nbits) != 8: - logging.error("Must specify --nbits (use calibrate command to determine value)") - return 1 - - if args.multiminer is None: - my_blocks = (0,1,1) - else: - if not args.ongoing: - logging.error("Cannot specify --multiminer without --ongoing") - return 1 - m = RE_MULTIMINER.match(args.multiminer) - if m is None: - logging.error("--multiminer argument must be k/m or j-k/m") - return 1 - start,_,stop,total = m.groups() - if stop is None: - stop = start - start, stop, total = map(int, (start, stop, total)) - if stop < start or start <= 0 or total < stop or total == 0: - logging.error("Inconsistent values for --multiminer") - return 1 - my_blocks = (start-1, stop, total) - - if args.max_interval < 960: - logging.error("--max-interval must be at least 960 (16 minutes)") - return 1 - - poolid = get_poolid(args) - - ultimate_target = nbits_to_target(int(args.nbits,16)) - - gen = Generate(multiminer=my_blocks, ultimate_target=ultimate_target, poisson=args.poisson, max_interval=args.max_interval, - standby_delay=args.standby_delay, backup_delay=args.backup_delay, set_block_time=args.set_block_time, poolid=poolid) - - mined_blocks = 0 - bestheader = {"hash": None} - lastheader = None - while max_blocks is None or mined_blocks < max_blocks: - - # current status? - bci = json.loads(args.bcli("getblockchaininfo")) - - if bestheader["hash"] != bci["bestblockhash"]: - bestheader = json.loads(args.bcli("getblockheader", bci["bestblockhash"])) - - if lastheader is None: - lastheader = bestheader["hash"] - elif bestheader["hash"] != lastheader: - next_delta = gen.next_block_delta(int(bestheader["bits"], 16), bestheader["hash"]) - next_delta += bestheader["time"] - time.time() - next_is_mine = gen.next_block_is_mine(bestheader["hash"]) - logging.info("Received new block at height %d; next in %s (%s)", bestheader["height"], seconds_to_hms(next_delta), ("mine" if next_is_mine else "backup")) - lastheader = bestheader["hash"] - - # when is the next block due to be mined? - now = time.time() - gen.next_block_time(now, bestheader, (mined_blocks == 0)) - - # ready to go? otherwise sleep and check for new block - if now < gen.action_time: - sleep_for = min(gen.action_time - now, 60) - if gen.mine_time < now: - # someone else might have mined the block, - # so check frequently, so we don't end up late - # mining the next block if it's ours - sleep_for = min(20, sleep_for) - minestr = "mine" if gen.is_mine else "backup" - logging.debug("Sleeping for %s, next block due in %s (%s)" % (seconds_to_hms(sleep_for), seconds_to_hms(gen.mine_time - now), minestr)) - time.sleep(sleep_for) - continue - - # gbt - tmpl = gen.gbt(args.bcli, bci["bestblockhash"], now) - if tmpl is None: - continue - - logging.debug("GBT template: %s", tmpl) - - # address for reward - reward_addr, reward_spk = get_reward_addr_spk(args, tmpl["height"]) - - # mine block - logging.debug("Mining block delta=%s start=%s mine=%s", seconds_to_hms(gen.mine_time-bestheader["time"]), gen.mine_time, gen.is_mine) - mined_blocks += 1 - block = gen.mine(args.bcli, args.grind_cmd, tmpl, reward_spk) - if block is None: - return 1 - - # submit block - r = args.bcli("-stdin", "submitblock", input=block.serialize().hex().encode('utf8')) - - # report - bstr = "block" if gen.is_mine else "backup block" - - next_delta = gen.next_block_delta(block.nBits, block.hash_hex) - next_delta += block.nTime - time.time() - next_is_mine = gen.next_block_is_mine(block.hash_hex) - - logging.debug("Block hash %s payout to %s", block.hash_hex, reward_addr) - logging.info("Mined %s at height %d; next in %s (%s)", bstr, tmpl["height"], seconds_to_hms(next_delta), ("mine" if next_is_mine else "backup")) - if r != "": - logging.warning("submitblock returned %s for height %d hash %s", r, tmpl["height"], block.hash_hex) - lastheader = block.hash_hex - -def do_calibrate(args): - if args.nbits is not None and args.seconds is not None: - sys.stderr.write("Can only specify one of --nbits or --seconds\n") - return 1 - if args.nbits is not None and len(args.nbits) != 8: - sys.stderr.write("Must specify 8 hex digits for --nbits\n") - return 1 - - TRIALS = 600 # gets variance down pretty low - TRIAL_BITS = 0x1e3ea75f # takes about 5m to do 600 trials - - header = CBlockHeader() - header.nBits = TRIAL_BITS - targ = nbits_to_target(header.nBits) - - start = time.time() - count = 0 - for i in range(TRIALS): - header.nTime = i - header.nNonce = 0 - headhex = header.serialize().hex() - cmd = shlex.split(args.grind_cmd) + [headhex] - newheadhex = subprocess.run(cmd, stdout=subprocess.PIPE, input=b"", check=True).stdout.strip() - - avg = (time.time() - start) * 1.0 / TRIALS - - if args.nbits is not None: - want_targ = nbits_to_target(int(args.nbits,16)) - want_time = avg*targ/want_targ - else: - want_time = args.seconds if args.seconds is not None else 25 - want_targ = int(targ*(avg/want_time)) - - print("nbits=%08x for %ds average mining time" % (target_to_nbits(want_targ), want_time)) - return 0 - -def bitcoin_cli(basecmd, args, **kwargs): - cmd = basecmd + ["-signet"] + args - logging.debug("Calling bitcoin-cli: %r", cmd) - out = subprocess.run(cmd, stdout=subprocess.PIPE, **kwargs, check=True).stdout - if isinstance(out, bytes): - out = out.decode('utf8') - return out.strip() - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument("--cli", default="bitcoin-cli", type=str, help="bitcoin-cli command") - parser.add_argument("--debug", action="store_true", help="Print debugging info") - parser.add_argument("--quiet", action="store_true", help="Only print warnings/errors") - - cmds = parser.add_subparsers(help="sub-commands") - genpsbt = cmds.add_parser("genpsbt", help="Generate a block PSBT for signing") - genpsbt.set_defaults(fn=do_genpsbt) - - solvepsbt = cmds.add_parser("solvepsbt", help="Solve a signed block PSBT") - solvepsbt.set_defaults(fn=do_solvepsbt) - - generate = cmds.add_parser("generate", help="Mine blocks") - generate.set_defaults(fn=do_generate) - howmany = generate.add_mutually_exclusive_group() - howmany.add_argument("--ongoing", action="store_true", help="Keep mining blocks") - howmany.add_argument("--max-blocks", default=None, type=int, help="Max blocks to mine (default=1)") - howmany.add_argument("--set-block-time", default=None, type=int, help="Set block time (unix timestamp); implies --max-blocks=1") - nbit_target = generate.add_mutually_exclusive_group() - nbit_target.add_argument("--nbits", default=None, type=str, help="Target nBits (specify difficulty)") - nbit_target.add_argument("--min-nbits", action="store_true", help="Target minimum nBits (use min difficulty)") - generate.add_argument("--poisson", action="store_true", help="Simulate randomised block times") - generate.add_argument("--multiminer", default=None, type=str, help="Specify which set of blocks to mine (eg: 1-40/100 for the first 40%%, 2/3 for the second 3rd)") - generate.add_argument("--backup-delay", default=300, type=int, help="Seconds to delay before mining blocks reserved for other miners (default=300)") - generate.add_argument("--standby-delay", default=0, type=int, help="Seconds to delay before mining blocks (default=0)") - generate.add_argument("--max-interval", default=1800, type=int, help="Maximum interblock interval (seconds)") - - calibrate = cmds.add_parser("calibrate", help="Calibrate difficulty") - calibrate.set_defaults(fn=do_calibrate) - calibrate_by = calibrate.add_mutually_exclusive_group() - calibrate_by.add_argument("--nbits", type=str, default=None) - calibrate_by.add_argument("--seconds", type=int, default=None) - - for sp in [genpsbt, generate]: - payto = sp.add_mutually_exclusive_group(required=True) - payto.add_argument("--address", default=None, type=str, help="Address for block reward payment") - payto.add_argument("--descriptor", default=None, type=str, help="Descriptor for block reward payment") - pool = sp.add_mutually_exclusive_group() - pool.add_argument("--poolnum", default=None, type=int, help="Identify blocks that you mine") - pool.add_argument("--poolid", default=None, type=str, help="Identify blocks that you mine (eg: /signet:1/)") - - for sp in [solvepsbt, generate, calibrate]: - sp.add_argument("--grind-cmd", default=None, type=str, required=(sp==calibrate), help="Command to grind a block header for proof-of-work") - - args = parser.parse_args(sys.argv[1:]) - - args.bcli = lambda *a, input=b"", **kwargs: bitcoin_cli(shlex.split(args.cli), list(a), input=input, **kwargs) - - if hasattr(args, "address") and hasattr(args, "descriptor"): - args.derived_addresses = {} - - if args.debug: - logging.getLogger().setLevel(logging.DEBUG) - elif args.quiet: - logging.getLogger().setLevel(logging.WARNING) - else: - logging.getLogger().setLevel(logging.INFO) - - if hasattr(args, "fn"): - return args.fn(args) - else: - logging.error("Must specify command") - return 1 - -if __name__ == "__main__": - main() diff --git a/depends/Makefile b/depends/Makefile index b45eb41f90dd..3b7ad6c3cb13 100644 --- a/depends/Makefile +++ b/depends/Makefile @@ -36,7 +36,6 @@ BASE_CACHE ?= $(BASEDIR)/built SDK_PATH ?= $(BASEDIR)/SDKs NO_BOOST ?= NO_LIBEVENT ?= -NO_WALLET ?= NO_ZMQ ?= NO_USDT ?= # Default NO_IPC value is 1 on Windows @@ -154,13 +153,11 @@ boost_packages_$(NO_BOOST) = $(boost_packages) libevent_packages_$(NO_LIBEVENT) = $(libevent_packages) -wallet_packages_$(NO_WALLET) = $(sqlite_packages) - zmq_packages_$(NO_ZMQ) = $(zmq_packages) ipc_packages_$(NO_IPC) = $(ipc_packages) usdt_packages_$(NO_USDT) = $(usdt_$(host_os)_packages) -packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(boost_packages_) $(libevent_packages_) $(wallet_packages_) $(usdt_packages_) +packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(boost_packages_) $(libevent_packages_) $(usdt_packages_) native_packages += $($(host_arch)_$(host_os)_native_packages) $($(host_os)_native_packages) ifneq ($(zmq_packages_),) @@ -223,7 +220,6 @@ $(host_prefix)/toolchain.cmake : toolchain.cmake.in $(host_prefix)/.stamp_$(fina -e 's|@LDFLAGS_RELEASE@|$(strip $(host_release_LDFLAGS))|' \ -e 's|@LDFLAGS_DEBUG@|$(strip $(host_debug_LDFLAGS))|' \ -e 's|@zmq_packages@|$(zmq_packages_)|' \ - -e 's|@wallet_packages@|$(wallet_packages_)|' \ -e 's|@usdt_packages@|$(usdt_packages_)|' \ -e 's|@ipc_packages@|$(ipc_packages_)|' \ $< > $@ diff --git a/depends/README.md b/depends/README.md index 3c03cf3174ac..a1bb5a019f0b 100644 --- a/depends/README.md +++ b/depends/README.md @@ -80,7 +80,6 @@ The following can be set when running make: `make FOO=bar` - `NO_BOOST`: Don't download/build/cache Boost - `NO_LIBEVENT`: Don't download/build/cache Libevent - `NO_ZMQ`: Don't download/build/cache packages needed for enabling ZeroMQ -- `NO_WALLET`: Don't download/build/cache libs needed to enable the wallet (SQLite) - `NO_USDT`: Don't download/build/cache packages needed for enabling USDT tracepoints - `NO_IPC`: Don't build Cap’n Proto and libmultiprocess packages. Default on Windows. - `DEBUG`: Disable some optimizations and enable more runtime checking @@ -91,8 +90,8 @@ The following can be set when running make: `make FOO=bar` of build error. After successful build log files are moved along with package archives - `LTO`: Enable options needed for LTO. Does not add `-flto` related options to *FLAGS. -If some packages are not built, for example `make NO_WALLET=1`, the appropriate CMake cache -variables will be set when generating the Bitcoin Core buildsystem. In this case, `-DENABLE_WALLET=OFF`. +If some packages are not built (e.g. `make NO_ZMQ=1`), the appropriate CMake cache +variables will be set when generating the Bitcoin Core buildsystem. ## Compiler Configuration diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index 840d5cd2a1c3..95f20d0e5946 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -4,8 +4,6 @@ boost_packages = boost libevent_packages = libevent -sqlite_packages=sqlite - zmq_packages=zeromq ipc_packages = capnp diff --git a/depends/packages/sqlite.mk b/depends/packages/sqlite.mk deleted file mode 100644 index 632ead7491d1..000000000000 --- a/depends/packages/sqlite.mk +++ /dev/null @@ -1,35 +0,0 @@ -package=sqlite -$(package)_version=3500400 -$(package)_download_path=https://sqlite.org/2025/ -$(package)_file_name=sqlite-autoconf-$($(package)_version).tar.gz -$(package)_sha256_hash=a3db587a1b92ee5ddac2f66b3edb41b26f9c867275782d46c3a088977d6a5b18 -$(package)_patches = autosetup-fixup.patch - -define $(package)_set_vars -$(package)_config_env := CC_FOR_BUILD="$$(build_CC)" -$(package)_config_opts = --disable-shared --disable-readline --disable-rtree -$(package)_config_opts += --disable-fts4 --disable-fts5 -$(package)_config_opts_debug += --debug -$(package)_cppflags += -DSQLITE_DQS=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_OMIT_DEPRECATED -$(package)_cppflags += -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_JSON -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -$(package)_cppflags += -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_AUTOINIT -$(package)_cppflags += -DSQLITE_OMIT_LOAD_EXTENSION -endef - -define $(package)_preprocess_cmds - patch -p1 < $($(package)_patch_dir)/autosetup-fixup.patch -endef - -# Remove --with-pic, which is applied globally to configure -# invocations but is incompatible with Autosetup -define $(package)_config_cmds - $$(filter-out --with-pic,$($(package)_autoconf)) -endef - -define $(package)_build_cmds - $(MAKE) libsqlite3.a -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install-headers install-lib -endef diff --git a/depends/toolchain.cmake.in b/depends/toolchain.cmake.in index 1094baece577..9e308d11359b 100644 --- a/depends/toolchain.cmake.in +++ b/depends/toolchain.cmake.in @@ -135,12 +135,6 @@ else() set(WITH_ZMQ ON CACHE BOOL "") endif() -if("@wallet_packages@" MATCHES "^[ ]*$") - set(ENABLE_WALLET OFF CACHE BOOL "") -else() - set(ENABLE_WALLET ON CACHE BOOL "") -endif() - if("@usdt_packages@" MATCHES "^[ ]*$") set(WITH_USDT OFF CACHE BOOL "") else() diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index cbbb6551f1b4..b77eb800c4d5 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -2071,7 +2071,7 @@ INCLUDE_FILE_PATTERNS = # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -PREDEFINED = ENABLE_EXTERNAL_SIGNER +PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The diff --git a/doc/JSON-RPC-interface.md b/doc/JSON-RPC-interface.md index 9c3b9416344f..e1bc622632b8 100644 --- a/doc/JSON-RPC-interface.md +++ b/doc/JSON-RPC-interface.md @@ -3,39 +3,15 @@ The daemon `bitcoind` has the JSON-RPC API enabled by default. This can be changed with the `-server` option. -## Endpoints +## Endpoint -There are two JSON-RPC endpoints on the server: +The JSON-RPC server exposes a single endpoint at `/`. -1. `/` -2. `/wallet//` - -### `/` endpoint - -This endpoint is always active. -It can always service non-wallet requests and can service wallet requests when -exactly one wallet is loaded. - -### `/wallet//` endpoint - -This endpoint is only activated when the wallet component has been compiled in. -It can service both wallet and non-wallet requests. -It MUST be used for wallet requests when two or more wallets are loaded. - -This is the endpoint used by bitcoin-cli when a `-rpcwallet=` parameter is passed in. - -Best practice would dictate using the `/wallet//` endpoint for ALL -requests when multiple wallets are in use. - -### Examples +### Example ```sh # Get block count from the / endpoint when rpcuser=alice and rpcport=38332 $ curl --user alice --data-binary '{"jsonrpc": "2.0", "id": "0", "method": "getblockcount", "params": []}' -H 'content-type: application/json' localhost:38332/ - -# Get balance from the /wallet/walletname endpoint when rpcuser=alice, rpcport=38332 and rpcwallet=desc-wallet -$ curl --user alice --data-binary '{"jsonrpc": "2.0", "id": "0", "method": "getbalance", "params": []}' -H 'content-type: application/json' localhost:38332/wallet/desc-wallet - ``` ## Parameter passing @@ -50,14 +26,11 @@ are combined with named values. Examples: ```sh -# "params": ["mywallet", false, false, "", false, false, true] -bitcoin-cli createwallet mywallet false false "" false false true +# "params": ["fakeaddr", 1] +bitcoin-cli setban fakeaddr add -# "params": {"wallet_name": "mywallet", "load_on_startup": true} -bitcoin-cli -named createwallet wallet_name=mywallet load_on_startup=true - -# "params": {"args": ["mywallet"], "load_on_startup": true} -bitcoin-cli -named createwallet mywallet load_on_startup=true +# "params": {"subnet": "fakeaddr", "command": "add"} +bitcoin-cli -named setban subnet=fakeaddr command=add ``` `bitcoin rpc` can also be substituted for `bitcoin-cli -named`, and is a newer alternative. @@ -93,17 +66,15 @@ protocol in v27.0 and prior releases. ## Security The RPC interface allows other programs to control Bitcoin Core, -including the ability to spend funds from your wallets, affect consensus -verification, read private data, and otherwise perform operations that -can cause loss of money, data, or privacy. This section suggests how -you should use and configure Bitcoin Core to reduce the risk that its -RPC interface will be abused. +including the ability to affect consensus verification, read private data, +and otherwise perform operations that can cause loss of data or privacy. +This section suggests how you should use and configure Bitcoin Core to +reduce the risk that its RPC interface will be abused. - **Securing the executable:** Anyone with physical or remote access to the computer, container, or virtual machine running Bitcoin Core can compromise either the whole program or just the RPC interface. This - includes being able to record any passphrases you enter for unlocking - your encrypted wallets or changing settings so that your Bitcoin Core + includes being able to change settings so that your Bitcoin Core program tells you that certain transactions have multiple confirmations even when they aren't part of the best block chain. For this reason, you should not use Bitcoin Core for security sensitive @@ -125,18 +96,17 @@ RPC interface will be abused. - **RPC Credentials Security Boundary:** Any client with valid RPC credentials should be treated as having significant control over both the Bitcoin Core node and the filesystem resources accessible by the `bitcoind` process. RPC commands - can load wallet files from paths that the `bitcoind` process has permission to - access, specify file paths for operations, and potentially gain broader access + can specify file paths for operations, and potentially gain broader access than intended. This means that someone with RPC access can potentially compromise not only the Bitcoin Core node, but also the machine it is running on. Bitcoin Core provides the `-rpcwhitelist` option to restrict which RPC commands specific users can access, and `-rpcwhitelistdefault` to control the default behavior for users - without explicit whitelists. However, when using multiple wallets or sharing access - with different users, these should not be considered robust security boundaries, as - users with access to certain commands may still be able to exploit functionality in - unexpected ways. For security-sensitive operations, implement proper system-level - isolation (containers, virtualization, separate user accounts with restricted - permissions) rather than relying solely on RPC access controls. + without explicit whitelists. However, when sharing access with different users, + these should not be considered robust security boundaries, as users with access + to certain commands may still be able to exploit functionality in unexpected ways. + For security-sensitive operations, implement proper system-level isolation + (containers, virtualization, separate user accounts with restricted permissions) + rather than relying solely on RPC access controls. - **Securing remote network access:** You may optionally allow other computers to remotely control Bitcoin Core by setting the `rpcallowip` @@ -179,12 +149,10 @@ RPC interface will be abused. although it does usually provide serialized data using a hex representation of the bytes. If you use RPC data in your programs or provide its data to other programs, you must ensure any problem strings - are properly escaped. For example, the `createwallet` RPC accepts - arguments such as `wallet_name` which is a string and could be used - for a path traversal attack without application level checks. Multiple - websites have been manipulated because they displayed decoded hex strings - that included HTML `