Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ jobs:
run: |
# Run tests on commits after the last merge commit and before the PR head commit
# Use clang++, because it is a bit faster and uses less memory than g++
git rebase --exec "echo Running test-one-commit on \$( git log -1 ) && CC=clang CXX=clang++ cmake -B build -DWERROR=ON -DWITH_ZMQ=ON -DBUILD_GUI=ON -DBUILD_BENCH=ON -DBUILD_FUZZ_BINARY=ON -DWITH_BDB=ON -DWARN_INCOMPATIBLE_BDB=OFF -DWITH_MINIUPNPC=ON -DWITH_USDT=ON -DCMAKE_CXX_FLAGS='-Wno-error=unused-member-function' && cmake --build build -j $(nproc) && ctest --output-on-failure --stop-on-failure --test-dir build -j $(nproc) && ./build/test/functional/test_runner.py -j $(( $(nproc) * 2 )) --combinedlogslen=99999999" ${{ env.TEST_BASE }}
git rebase --exec "echo Running test-one-commit on \$( git log -1 ) && CC=clang CXX=clang++ cmake -B build -DWERROR=ON -DWITH_ZMQ=ON -DBUILD_GUI=ON -DBUILD_BENCH=ON -DBUILD_FUZZ_BINARY=ON -DWITH_BDB=ON -DWARN_INCOMPATIBLE_BDB=OFF -DWITH_MINIUPNPC=ON -DWITH_USDT=ON -DCMAKE_CXX_FLAGS='-Wno-error=unused-member-function' -D RDTS_CONSENT=RUNTIME_CHECK && cmake --build build -j $(nproc) && ctest --output-on-failure --stop-on-failure --test-dir build -j $(nproc) && ./build/test/functional/test_runner.py -j $(( $(nproc) * 2 )) --combinedlogslen=99999999" ${{ env.TEST_BASE }}

macos-native-arm64:
name: ${{ matrix.job-name }}
Expand Down Expand Up @@ -240,7 +240,7 @@ jobs:

- name: Generate build system
run: |
cmake -B build --preset vs2022-static -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT\scripts\buildsystems\vcpkg.cmake" ${{ matrix.generate-options }}
cmake -B build --preset vs2022-static -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT\scripts\buildsystems\vcpkg.cmake" -D RDTS_CONSENT=RUNTIME_CHECK ${{ matrix.generate-options }}

- name: Save vcpkg binary cache
uses: actions/cache/save@v5
Expand Down
33 changes: 33 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,38 @@ if(BUILD_FOR_FUZZING)
)
endif()

set(RDTS_CONSENT "UNSET" CACHE STRING "RDTS (BIP110) consent behaviour.")
set(RDTS_CONSENT_OPTIONS "RUNTIME_CHECK" "RUNTIME_WARN" "IMPLICIT")
set_property(CACHE RDTS_CONSENT PROPERTY STRINGS "UNSET" ${RDTS_CONSENT_OPTIONS})
list(APPEND RDTS_CONSENT_OPTIONS "UNSUPPORTED_UNSAFE_NO_ENFORCEMENT")
if(RDTS_CONSENT MATCHES "^(1|ON|YES|TRUE|Y)$")
set(RDTS_CONSENT "IMPLICIT")
endif()
if(BUILD_DAEMON OR BUILD_GUI OR BUILD_KERNEL_LIB)
if(NOT RDTS_CONSENT IN_LIST RDTS_CONSENT_OPTIONS)
message(FATAL_ERROR
"RDTS_CONSENT is not set.\n"
"This version of Bitcoin Knots applies the BIP110 (RDTS) network upgrade, which fixes critical vulnerabilities in long-standing network design. To avoid applying this upgrade by accident, please choose whether to accept it now, or defer explicit confirmation until runtime.\n"
"Important: Because this upgrade already has broad community support, reverting to an older software version does not reject it. Running outdated software after any network upgrade only leaves your node vulnerable to displaying fake or fraudulent transactions. To effectively reject this upgrade, you need to run alternative software designed to split away from the upgraded network.\n"
"To accept this upgrade now, add to your CMake options:\n"
" -D RDTS_CONSENT=IMPLICIT\n"
"Or to defer explicit confirmation until runtime (via GUI prompt or consensusrules=rdts config setting):\n"
" -D RDTS_CONSENT=RUNTIME_CHECK (causes bitcoind to exit if absent)\n"
" -D RDTS_CONSENT=RUNTIME_WARN (causes bitcoind to warn hourly if absent)\n"
"For more information, see: https://bitcoinknots.org/learn/2026-rdts"
)
endif()
else()
# Nothing else enforces consensus rules, but RDTSConsentFlags::UNSET doesn't actually exist so would break the build
set(RDTS_CONSENT "IMPLICIT")
endif()
if(RDTS_CONSENT STREQUAL "UNSUPPORTED_UNSAFE_NO_ENFORCEMENT")
list(APPEND configure_warnings
"RDTS_CONSENT=UNSUPPORTED_UNSAFE_NO_ENFORCEMENT is NOT a supported option. Use at your own risk!
Note that this will NOT make your node reject RDTS\; it will merely be insecure after RDTS activates."
)
endif()

# The external_lib_interface interface library aims to encapsulate build flags, which
# are specific to non-internal libraries.
# It is intended to be a usage requirement for non-internal library
Expand Down Expand Up @@ -787,6 +819,7 @@ message(" ZeroMQ .............................. ${WITH_ZMQ}")
message(" USDT tracing ........................ ${WITH_USDT}")
message(" QR code (GUI) ....................... ${WITH_QRENCODE}")
message(" DBus (GUI) .......................... ${WITH_DBUS}")
message(" RDTS consent ........................ ${RDTS_CONSENT}")
message("Tests:")
message(" test_bitcoin ........................ ${BUILD_TESTS}")
message(" test_bitcoin-qt ..................... ${BUILD_GUI_TESTS}")
Expand Down
2 changes: 1 addition & 1 deletion ci/test/03_test_script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ if [ "$DOWNLOAD_PREVIOUS_RELEASES" = "true" ]; then
test/get_previous_releases.py -b -t "$PREVIOUS_RELEASES_DIR"
fi

BITCOIN_CONFIG_ALL="-DBUILD_BENCH=ON -DBUILD_FUZZ_BINARY=ON"
BITCOIN_CONFIG_ALL="-DBUILD_BENCH=ON -DBUILD_FUZZ_BINARY=ON -D RDTS_CONSENT=RUNTIME_CHECK"
if [ -z "$NO_DEPENDS" ]; then
BITCOIN_CONFIG_ALL="${BITCOIN_CONFIG_ALL} -DCMAKE_TOOLCHAIN_FILE=$DEPENDS_DIR/$HOST/toolchain.cmake"
fi
Expand Down
2 changes: 2 additions & 0 deletions cmake/bitcoin-build-config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,6 @@
/* Define if building embedded LevelDB */
#cmakedefine EMBEDDED_LEVELDB 1

#define RDTS_CONSENT RDTSConsentFlag::@RDTS_CONSENT@

#endif //BITCOIN_CONFIG_H
45 changes: 45 additions & 0 deletions contrib/docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,46 @@
# syntax=docker/dockerfile:1
FROM alpine:3.22 AS builder

ARG RDTS_CONSENT="UNSET"

RUN <<EORUN
cat <<\EOSCRIPT >/tmp/rdts_check
case "$(echo "$RDTS_CONSENT" | tr '[:lower:]' '[:upper:]')" in
1|ON|YES|TRUE|Y) RDTS_CONSENT="IMPLICIT" ;;
esac
case "$RDTS_CONSENT" in
RUNTIME_CHECK|RUNTIME_WARN|IMPLICIT)
;;
*)
red=$(printf '\033[31m')
term_reset=$(printf '\033[0m')
cat <<EOF >&2

${red}ERROR: RDTS_CONSENT is not set.

This version of Bitcoin Knots applies the BIP110 (RDTS) network upgrade, which fixes critical vulnerabilities in long-standing network design. To avoid applying this upgrade by accident, please choose whether to accept it now, or defer explicit confirmation until runtime.

Important: Because this upgrade already has broad community support, reverting to an older software version does not reject it. Running outdated software after any network upgrade only leaves your node vulnerable to displaying fake or fraudulent transactions. To effectively reject this upgrade, you need to run alternative software designed to split away from the upgraded network.

To accept this upgrade now, add to your Docker build arguments:

--build-arg RDTS_CONSENT=IMPLICIT

Or to defer explicit confirmation until runtime (via consensusrules=rdts config setting):

--build-arg RDTS_CONSENT=RUNTIME_CHECK (exits if absent)
--build-arg RDTS_CONSENT=RUNTIME_WARN (warns hourly if absent)

For more information, see: https://bitcoinknots.org/learn/2026-rdts ${term_reset}
EOF
exit 1
;;
esac
EOSCRIPT
EORUN

RUN . /tmp/rdts_check

RUN apk add --no-cache \
build-base \
cmake \
Expand All @@ -17,6 +58,7 @@ COPY . .
WORKDIR /opt/bitcoin/build

RUN cmake .. \
-D RDTS_CONSENT="${RDTS_CONSENT}" \
-DCMAKE_INSTALL_PREFIX="/usr/local/" \
-DBUILD_DAEMON="ON" \
-DBUILD_CLI="ON" \
Expand All @@ -43,6 +85,9 @@ RUN strip --strip-unneeded /usr/local/bin/*

FROM alpine:3.22 AS final

ARG RDTS_CONSENT
LABEL org.bitcoinknots.rdts_consent="${RDTS_CONSENT}"

ARG USER_ID=1000
ARG GROUP_ID=1000

Expand Down
1 change: 1 addition & 0 deletions contrib/guix/libexec/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ mkdir -p "$OUTDIR"
# CONFIGFLAGS
CONFIGFLAGS="-DREDUCE_EXPORTS=ON -DBUILD_BENCH=OFF -DBUILD_GUI_TESTS=OFF -DBUILD_FUZZ_BINARY=OFF"
CONFIGFLAGS="$CONFIGFLAGS -DCMAKE_SKIP_BUILD_RPATH=TRUE" # check-symbols is fussy about rpath and we don't need it
CONFIGFLAGS="$CONFIGFLAGS -D RDTS_CONSENT=RUNTIME_WARN"

# CFLAGS
HOST_CFLAGS="-O2 -g"
Expand Down
12 changes: 12 additions & 0 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <common/args.h>
#include <consensus/params.h>
#include <deploymentinfo.h>
#include <kernel/chainparams.h>
#include <logging.h>
#include <tinyformat.h>
#include <util/chaintype.h>
Expand Down Expand Up @@ -137,6 +138,17 @@ const CChainParams &Params() {

std::unique_ptr<const CChainParams> CreateChainParams(const ArgsManager& args, const ChainType chain)
{
g_rdts_consent = static_cast<RDTSConsentFlag>(args.GetIntArg("rdts_consent_flag", static_cast<int64_t>(g_rdts_consent)));
g_enable_rdts = g_rdts_consent != RDTSConsentFlag::UNSUPPORTED_UNSAFE_NO_ENFORCEMENT;
if (g_rdts_consent == RDTSConsentFlag::UNSUPPORTED_UNSAFE_NO_ENFORCEMENT && !g_enable_rdts) {
for (const auto& rulesok : args.GetArgs(CONSENSUSRULES_CONFIG_NAME)) {
if (rulesok == CONSENSUSRULES_REQUIRED) {
g_enable_rdts = true;
break;
}
}
}

switch (chain) {
case ChainType::MAIN:
return CChainParams::Main();
Expand Down
3 changes: 3 additions & 0 deletions src/chainparamsbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@

class ArgsManager;

static const std::string CONSENSUSRULES_CONFIG_NAME{"consensusrules"};
static const std::string CONSENSUSRULES_REQUIRED{"rdts"};

/**
* CBaseChainParams defines the base parameters (shared between bitcoin-cli and bitcoind)
* of a given instance of the Bitcoin system.
Expand Down
111 changes: 111 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <interfaces/mining.h>
#include <interfaces/node.h>
#include <kernel/caches.h>
#include <kernel/chainparams.h>
#include <kernel/context.h>
#include <kernel/warning.h>
#include <key.h>
Expand Down Expand Up @@ -693,6 +694,8 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
argsman.AddArg("-uaspoof=<ua>", strprintf("Replace entire user agent string with custom identifier (should be formatted '%s' as specified in BIP 14)", BIP14_EXAMPLE_UA), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION);

SetupChainParamsBaseOptions(argsman);
argsman.AddArg("-consensusrules=<rules>", strprintf("Enforce the specified consensus rules (default: none). Must be %s to use this software.", CONSENSUSRULES_REQUIRED), ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
Comment thread
luke-jr marked this conversation as resolved.
argsman.AddArg("-rdts_consent_flag=<n>", strprintf("Test RDTS consent flag <n> (default: %u)", static_cast<int64_t>(g_rdts_consent)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);

argsman.AddArg("-acceptnonstddatacarrier",
strprintf("Relay and mine non-OP_RETURN datacarrier injection (default: %u)",
Expand Down Expand Up @@ -1559,6 +1562,84 @@ static ChainstateLoadResult InitAndLoadChainstate(
return {status, error};
};

bool UserProtocolRulesCheck()
{
const auto rules_requested{gArgs.GetArgs(CONSENSUSRULES_CONFIG_NAME)};
for (const auto& rulesok : rules_requested) {
if (rulesok == CONSENSUSRULES_REQUIRED) continue;
return InitError(strprintf(_("Unknown rule specified in -%s: %s"), CONSENSUSRULES_CONFIG_NAME, rulesok));
}
return true;
}

bool UserProtocolRulesConsent()
{
if (g_rdts_consent == RDTSConsentFlag::IMPLICIT) {
LogPrintf("User already consented to '%s' consensus rules (at installation)\n", CONSENSUSRULES_REQUIRED);
return true;
}
for (const auto& rulesok : gArgs.GetArgs(CONSENSUSRULES_CONFIG_NAME)) {
if (rulesok == CONSENSUSRULES_REQUIRED) {
LogPrintf("User already consented to '%s' consensus rules (in config)\n", CONSENSUSRULES_REQUIRED);
return true;
}
}

bilingual_str msg = strprintf(_(
"BIP110/RDTS Network Upgrade\n"
"\n"
"This version of %s applies the BIP110 (RDTS) network upgrade, "
"which fixes critical vulnerabilities in long-standing network design. "
"However, you are in control of your own software, and this application asks for explicit confirmation.\n"
"\n"
Comment thread
luke-jr marked this conversation as resolved.
"Important: "
"Because this upgrade already has broad community support, "
"reverting to an older software version does not reject it. "
"Running outdated software after any network upgrade only leaves your node vulnerable to displaying fake or fraudulent transactions. "
"To effectively reject this upgrade, you need to run alternative software designed to split away from the upgraded network.\n"
"\n"
"For more information, see: %s"
),
CLIENT_NAME,
"https://bitcoinknots.org/learn/2026-rdts");
const bilingual_str msg_manual_suffix = strprintf(_(
"To confirm this upgrade, add to your %s file: %s"
),
gArgs.GetPathArg("-conf", BITCOIN_CONF_FILENAME).utf8string(),
CONSENSUSRULES_CONFIG_NAME + "=" + CONSENSUSRULES_REQUIRED);
const bilingual_str msg_manual = msg + Untranslated("\n\n") + msg_manual_suffix;

if (!gArgs.GetSettingsPath()) {
msg = msg_manual;
}

const bool consent = uiInterface.ThreadSafeQuestion(
_("Attention:") + Untranslated(" ") + msg,
msg_manual.original
, "Attention", CClientUIInterface::MSG_WARNING | CClientUIInterface::BTN_ABORT);
Comment thread
luke-jr marked this conversation as resolved.

if (consent) {
if (gArgs.GetSettingsPath()) {
// Write to settings.json so we don't ask anymore
LogPrintf("User interactively consented to '%s' consensus rules (%s)\n", CONSENSUSRULES_REQUIRED, "remembering for next time");
gArgs.LockSettings([&](common::Settings& settings) {
auto& setting = settings.rw_settings[CONSENSUSRULES_CONFIG_NAME];
if (setting.isArray()) {
// Normally, it doesn't make sense to support multiple rulesets, but if the user has done so already, don't lose the current set
setting.push_back(CONSENSUSRULES_REQUIRED);
} else {
setting = CONSENSUSRULES_REQUIRED;
}
});
gArgs.WriteSettingsFile();
} else {
LogPrintf("User interactively consented to '%s' consensus rules (%s)\n", CONSENSUSRULES_REQUIRED, "settings disabled, so can't save");
}
}

return consent;
}

bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
{
const ArgsManager& args = *Assert(node.args);
Expand Down Expand Up @@ -1633,6 +1714,36 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
g_wallet_init_interface.Construct(node);
uiInterface.InitWallet();

if (!UserProtocolRulesCheck()) {
return false;
}

if (!(chainparams.IsTestChain() || UserProtocolRulesConsent())) {
if (g_rdts_consent == RDTSConsentFlag::RUNTIME_CHECK) {
return InitError(_("User has not consented to supported protocol rules. Exiting"));
} else if (g_rdts_consent == RDTSConsentFlag::UNSUPPORTED_UNSAFE_NO_ENFORCEMENT) {
LogError("User has not consented to supported protocol rules. This node will NOT enforce them. Warning every hour.");
g_local_services = ServiceFlags(g_local_services & ~NODE_REDUCED_DATA);
scheduler.scheduleEvery([]{
LogError("RDTS is not enabled. This node is therefore vulnerable to displaying fake or fraudulent transactions.\n");
LogError("For more information, see: %s\n", "https://bitcoinknots.org/learn/2026-rdts");
LogError("To enable RDTS enforcement and disable this warning, add to %s: %s\n",
gArgs.GetPathArg("-conf", BITCOIN_CONF_FILENAME).utf8string(),
CONSENSUSRULES_CONFIG_NAME + "=" + CONSENSUSRULES_REQUIRED);
}, std::chrono::hours{1});
} else {
LogError("User has not consented to supported protocol rules. This node will STILL enforce them. Warning every hour.");
g_rdts_warning = true;
scheduler.scheduleEvery([]{
LogError("This software applies the BIP110/RDTS network upgrade, which fixes critical vulnerabilities, but explicit user confirmation has not been configured.\n");
LogError("For more information, see: %s\n", "https://bitcoinknots.org/learn/2026-rdts");
LogError("To confirm this upgrade and dismiss this warning, add to your %s file: %s\n",
gArgs.GetPathArg("-conf", BITCOIN_CONF_FILENAME).utf8string(),
CONSENSUSRULES_CONFIG_NAME + "=" + CONSENSUSRULES_REQUIRED);
}, std::chrono::hours{1});
}
}

if (interfaces::Ipc* ipc = node.init->ipc()) {
for (std::string address : gArgs.GetArgs("-ipcbind")) {
try {
Expand Down
14 changes: 14 additions & 0 deletions src/kernel/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <bitcoin-build-config.h> // IWYU pragma: keep

#include <kernel/chainparams.h>

#include <chainparamsseeds.h>
Expand All @@ -28,6 +30,10 @@

using namespace util::hex_literals;

RDTSConsentFlag g_rdts_consent{RDTS_CONSENT};
bool g_enable_rdts{g_rdts_consent != RDTSConsentFlag::UNSUPPORTED_UNSAFE_NO_ENFORCEMENT};
bool g_rdts_warning{false};

// Workaround MSVC bug triggering C7595 when calling consteval constructors in
// initializer lists.
// A fix may be on the way:
Expand Down Expand Up @@ -126,6 +132,14 @@ class CMainParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].active_duration = 52416; // ~1 year
consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].threshold = 1109; // 55% of 2016

if (g_rdts_consent == RDTSConsentFlag::UNSUPPORTED_UNSAFE_NO_ENFORCEMENT && !g_enable_rdts) {
consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].max_activation_height = std::numeric_limits<int>::max();
consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].active_duration = std::numeric_limits<int>::max();
consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].threshold = 0;
}

consensus.nMinimumChainWork = uint256{"0000000000000000000000000000000000000000dee8e2a309ad8a9820433c68"};
consensus.defaultAssumeValid = uint256{"00000000000000000000611fd22f2df7c8fbd0688745c3a6c3bb5109cc2a12cb"}; // 912683

Expand Down
11 changes: 11 additions & 0 deletions src/kernel/chainparams.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@
#include <utility>
#include <vector>

enum class RDTSConsentFlag {
RUNTIME_CHECK,
IMPLICIT,
RUNTIME_WARN,
UNSUPPORTED_UNSAFE_NO_ENFORCEMENT,
};

extern RDTSConsentFlag g_rdts_consent;
extern bool g_enable_rdts;
extern bool g_rdts_warning;

typedef std::map<int, uint256> MapCheckpoints;

struct CCheckpointData {
Expand Down
1 change: 1 addition & 0 deletions src/kernel/warning.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace kernel {
enum class Warning {
RULES_NOT_CONSENTED,
UNKNOWN_NEW_RULES_ACTIVATED,
LARGE_WORK_INVALID_CHAIN,
UNKNOWN_NEW_RULES_SIGNAL_VBITS,
Expand Down
Loading
Loading