Skip to content
Merged
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
5 changes: 4 additions & 1 deletion ci/test/01_base_install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,10 @@ fi

if [[ "${RUN_IWYU}" == true ]]; then
${CI_RETRY_EXE} git clone --depth=1 https://github.com/include-what-you-use/include-what-you-use -b clang_"${IWYU_LLVM_V}" /include-what-you-use
(cd /include-what-you-use && patch -p1 < /ci_container_base/ci/test/01_iwyu.patch)
pushd /include-what-you-use
patch -p1 < /ci_container_base/ci/test/01_iwyu.patch
patch -p1 < /ci_container_base/ci/test/02_iwyu_hash.patch
popd
cmake -B /iwyu-build/ -G 'Unix Makefiles' -DCMAKE_PREFIX_PATH=/usr/lib/llvm-"${IWYU_LLVM_V}" -S /include-what-you-use
make -C /iwyu-build/ install "$MAKEJOBS"
fi
Expand Down
44 changes: 44 additions & 0 deletions ci/test/02_iwyu_hash.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
Map std::hash to its providing standard headers.
Backport of https://github.com/include-what-you-use/include-what-you-use/pull/2013
(commit 52f85e1f4d990f55fc6556d543eb051d79364a16) to the clang_22 release
branch. Drop once the upstream fix lands in the IWYU branch tracked by
ci/test/00_setup_env_native_iwyu.sh.

--- a/std_symbol_map.inc
+++ b/std_symbol_map.inc
@@ -1054,12 +1054,27 @@
{ "std::has_unique_object_representations_v", kPrivate, "<type_traits>", kPublic },
{ "std::has_virtual_destructor", kPrivate, "<type_traits>", kPublic },
{ "std::has_virtual_destructor_v", kPrivate, "<type_traits>", kPublic },
+{ "std::hash", kPrivate, "<functional>", kPublic },
+{ "std::hash", kPrivate, "<bitset>", kPublic },
+{ "std::hash", kPrivate, "<coroutine>", kPublic },
+{ "std::hash", kPrivate, "<filesystem>", kPublic },
+{ "std::hash", kPrivate, "<memory>", kPublic },
+{ "std::hash", kPrivate, "<optional>", kPublic },
+{ "std::hash", kPrivate, "<stacktrace>", kPublic },
+{ "std::hash", kPrivate, "<string>", kPublic },
+{ "std::hash", kPrivate, "<string_view>", kPublic },
+{ "std::hash", kPrivate, "<system_error>", kPublic },
+{ "std::hash", kPrivate, "<thread>", kPublic },
+{ "std::hash", kPrivate, "<typeindex>", kPublic },
+{ "std::hash", kPrivate, "<variant>", kPublic },
+{ "std::hash", kPrivate, "<vector>", kPublic },
{ "std::hash<std::basic_stacktrace<:0>>", kPrivate, "<stacktrace>", kPublic },
{ "std::hash<std::basic_string<char, std::char_traits<char>, :0>>", kPrivate, "<string>", kPublic },
{ "std::hash<std::basic_string<char16_t, std::char_traits<char16_t>, :0>>", kPrivate, "<string>", kPublic },
{ "std::hash<std::basic_string<char32_t, std::char_traits<char32_t>, :0>>", kPrivate, "<string>", kPublic },
{ "std::hash<std::basic_string<char8_t, std::char_traits<char8_t>, :0>>", kPrivate, "<string>", kPublic },
{ "std::hash<std::basic_string<wchar_t, std::char_traits<wchar_t>, :0>>", kPrivate, "<string>", kPublic },
+{ "std::hash<std::bitset<:0>>", kPrivate, "<bitset>", kPublic },
{ "std::hash<std::coroutine_handle<:0>>", kPrivate, "<coroutine>", kPublic },
{ "std::hash<std::error_code>", kPrivate, "<system_error>", kPublic },
{ "std::hash<std::error_condition>", kPrivate, "<system_error>", kPublic },
@@ -1069,6 +1084,7 @@
{ "std::hash<std::shared_ptr<:0>>", kPrivate, "<memory>", kPublic },
{ "std::hash<std::stacktrace_entry>", kPrivate, "<stacktrace>", kPublic },
{ "std::hash<std::string_view>", kPrivate, "<string_view>", kPublic },
+{ "std::hash<std::thread::id>", kPrivate, "<thread>", kPublic },
{ "std::hash<std::type_index>", kPrivate, "<typeindex>", kPublic },
{ "std::hash<std::u16string_view>", kPrivate, "<string_view>", kPublic },
{ "std::hash<std::u32string_view>", kPrivate, "<string_view>", kPublic },
3 changes: 2 additions & 1 deletion ci/test_imagefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ ENV BASE_ROOT_DIR=${BASE_ROOT_DIR}

# Make retry available in PATH, needed for CI_RETRY_EXE
COPY ./ci/retry/retry /usr/bin/retry
COPY ./ci/test/00_setup_env.sh ./${FILE_ENV} ./ci/test/01_base_install.sh ./ci/test/01_iwyu.patch /ci_container_base/ci/test/
COPY ./ci/test/00_setup_env.sh ./${FILE_ENV} ./ci/test/01_base_install.sh /ci_container_base/ci/test/
COPY ./ci/test/*.patch /ci_container_base/ci/test/

# Bash is required, so install it when missing
RUN sh -c "bash -c 'true' || ( apk update && apk add --no-cache bash )"
Expand Down
4 changes: 4 additions & 0 deletions src/crypto/hmac_sha256.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <crypto/hmac_sha256.h>

#include <crypto/sha256.h>
#include <support/cleanse.h>

#include <cstring>

Expand All @@ -26,11 +27,14 @@ CHMAC_SHA256::CHMAC_SHA256(const unsigned char* key, size_t keylen)
for (int n = 0; n < 64; n++)
rkey[n] ^= 0x5c ^ 0x36;
inner.Write(rkey, 64);

memory_cleanse(rkey, sizeof(rkey));
}

void CHMAC_SHA256::Finalize(unsigned char hash[OUTPUT_SIZE])
{
unsigned char temp[32];
inner.Finalize(temp);
outer.Write(temp, 32).Finalize(hash);
memory_cleanse(temp, sizeof(temp));
}
4 changes: 4 additions & 0 deletions src/crypto/hmac_sha512.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <crypto/hmac_sha512.h>

#include <crypto/sha512.h>
#include <support/cleanse.h>

#include <cstring>

Expand All @@ -26,11 +27,14 @@ CHMAC_SHA512::CHMAC_SHA512(const unsigned char* key, size_t keylen)
for (int n = 0; n < 128; n++)
rkey[n] ^= 0x5c ^ 0x36;
inner.Write(rkey, 128);

memory_cleanse(rkey, sizeof(rkey));
}

void CHMAC_SHA512::Finalize(unsigned char hash[OUTPUT_SIZE])
{
unsigned char temp[64];
inner.Finalize(temp);
outer.Write(temp, 64).Finalize(hash);
memory_cleanse(temp, sizeof(temp));
}
10 changes: 9 additions & 1 deletion src/hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,20 @@
#include <prevector.h>
#include <serialize.h>
#include <span.h>
#include <support/cleanse.h>
#include <uint256.h>

#include <string>
#include <vector>

typedef uint256 ChainCode;
/** A BIP32 chain code. Cleansed on destruction. */
class ChainCode : public base_blob<256> {
public:
constexpr ChainCode() = default;
constexpr explicit ChainCode(std::span<const unsigned char> vch) : base_blob<256>(vch) {}
constexpr explicit ChainCode(const base_blob<256>& b) : base_blob<256>(b) {}
~ChainCode() { memory_cleanse(data(), size()); }
};

/** A hasher class for Bitcoin's 256-bit hash (double SHA-256). */
class CHash256 {
Expand Down
5 changes: 1 addition & 4 deletions src/musig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@

//! MuSig2 chaincode as defined by BIP 328
using namespace util::hex_literals;
constexpr uint256 MUSIG_CHAINCODE{
// Use immediate lambda to work around GCC-14 bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117966
[]() consteval { return uint256{"868087ca02a6f974c4598924c36b57762d32cb45717167e300622c7167e38965"_hex_u8}; }(),
};
const ChainCode MUSIG_CHAINCODE{"868087ca02a6f974c4598924c36b57762d32cb45717167e300622c7167e38965"_hex_u8};

static bool GetMuSig2KeyAggCache(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache)
{
Expand Down
5 changes: 3 additions & 2 deletions src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <random.h>
#include <scheduler.h>
#include <util/fs.h>
#include <util/overflow.h>
#include <util/sock.h>
#include <util/strencodings.h>
#include <util/thread.h>
Expand Down Expand Up @@ -296,7 +297,7 @@ bool AddLocal(const CService& addr_, int nScore)
const auto [it, is_newly_added] = mapLocalHost.emplace(addr, LocalServiceInfo());
LocalServiceInfo &info = it->second;
if (is_newly_added || nScore >= info.nScore) {
info.nScore = nScore + (is_newly_added ? 0 : 1);
info.nScore = SaturatingAdd(nScore, is_newly_added ? 0 : 1);
info.nPort = addr.GetPort();
}
}
Expand Down Expand Up @@ -325,7 +326,7 @@ bool SeenLocal(const CService& addr)
LOCK(g_maplocalhost_mutex);
const auto it = mapLocalHost.find(addr);
if (it == mapLocalHost.end()) return false;
++it->second.nScore;
it->second.nScore = SaturatingAdd(it->second.nScore, 1);
return true;
}

Expand Down
4 changes: 3 additions & 1 deletion src/node/blockstorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,9 @@ bool BlockManager::LoadBlockIndex(const std::optional<uint256>& snapshot_blockha
pindex->m_chain_tx_count = pindex->pprev->m_chain_tx_count + pindex->nTx;
} else {
pindex->m_chain_tx_count = 0;
m_blocks_unlinked.insert(std::make_pair(pindex->pprev, pindex));
if (pindex->nStatus & BLOCK_HAVE_DATA) {
m_blocks_unlinked.insert(std::make_pair(pindex->pprev, pindex));
}
}
} else {
pindex->m_chain_tx_count = pindex->nTx;
Expand Down
12 changes: 12 additions & 0 deletions src/primitives/transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -403,4 +403,16 @@ struct CMutableTransaction
typedef std::shared_ptr<const CTransaction> CTransactionRef;
template <typename Tx> static inline CTransactionRef MakeTransactionRef(Tx&& txIn) { return std::make_shared<const CTransaction>(std::forward<Tx>(txIn)); }

namespace std {
/** Disable default std::hash for CTransactionRef to prevent accidentally
* comparing by pointer. Use CTransactionRefHash or provide a custom
* hasher. */
template <>
struct hash<CTransactionRef> {
hash() = delete;
// Belt-and-suspenders, already implied by the above.
size_t operator()(const CTransactionRef&) const = delete;
};
} // namespace std

#endif // BITCOIN_PRIMITIVES_TRANSACTION_H
2 changes: 0 additions & 2 deletions src/pubkey.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ class CKeyID : public uint160
explicit CKeyID(const uint160& in) : uint160(in) {}
};

typedef uint256 ChainCode;

/** An encapsulated public key. */
class CPubKey
{
Expand Down
4 changes: 2 additions & 2 deletions src/test/fuzz/key.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ FUZZ_TARGET(key, .init = initialize_key)
{
CKey child_key;
ChainCode child_chaincode;
const bool ok = key.Derive(child_key, child_chaincode, 0, random_uint256);
const bool ok = key.Derive(child_key, child_chaincode, 0, ChainCode{random_uint256});
assert(ok);
assert(child_key.IsValid());
assert(!(child_key == key));
Expand Down Expand Up @@ -275,7 +275,7 @@ FUZZ_TARGET(key, .init = initialize_key)
{
CPubKey child_pubkey;
ChainCode child_chaincode;
const bool ok = pubkey.Derive(child_pubkey, child_chaincode, 0, random_uint256);
const bool ok = pubkey.Derive(child_pubkey, child_chaincode, 0, ChainCode{random_uint256});
assert(ok);
assert(child_pubkey != pubkey);
assert(child_pubkey.IsCompressed());
Expand Down
1 change: 1 addition & 0 deletions src/test/fuzz/util/check_globals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ struct CheckGlobalsImpl {
g_seeded_g_prng_zero = false;
g_used_system_time = false;
SetMockTime(0s);
MockableSteadyClock::ClearMockTime();
}
~CheckGlobalsImpl()
{
Expand Down
35 changes: 35 additions & 0 deletions src/test/net_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,41 @@ BOOST_AUTO_TEST_CASE(LocalAddress_BasicLifecycle)
BOOST_CHECK(!IsLocal(addr));
}

BOOST_AUTO_TEST_CASE(LocalAddress_nScore_Overflow)
{
g_reachable_nets.Add(NET_IPV4);
const CService addr{UtilBuildAddress(0x002, 0x001, 0x001, 0x001), 1000}; // 2.1.1.1:1000

const auto get_score = [](const CService& service) -> int {
LOCK(g_maplocalhost_mutex);
const auto it = mapLocalHost.find(service);
return it != mapLocalHost.end() ? it->second.nScore : 0;
};

const int initial_score = 1000;
BOOST_REQUIRE(AddLocal(addr, initial_score));
BOOST_REQUIRE(IsLocal(addr));
BOOST_CHECK_EQUAL(get_score(addr), initial_score);

// SeenLocal should increment nScore by 1.
BOOST_CHECK(SeenLocal(addr));
BOOST_CHECK_EQUAL(get_score(addr), initial_score + 1);

// AddLocal() saturates nScore when updating an existing entry at INT_MAX.
BOOST_REQUIRE(AddLocal(addr, std::numeric_limits<int>::max()));
BOOST_CHECK_EQUAL(get_score(addr), std::numeric_limits<int>::max());

BOOST_CHECK(AddLocal(addr, std::numeric_limits<int>::max()));
BOOST_CHECK_EQUAL(get_score(addr), std::numeric_limits<int>::max());

// SeenLocal() also saturates at INT_MAX.
BOOST_CHECK(SeenLocal(addr));
BOOST_CHECK_EQUAL(get_score(addr), std::numeric_limits<int>::max());

RemoveLocal(addr);
BOOST_CHECK(!IsLocal(addr));
}

BOOST_AUTO_TEST_CASE(initial_advertise_from_version_message)
{
LOCK(NetEventsInterface::g_msgproc_mutex);
Expand Down
39 changes: 39 additions & 0 deletions test/functional/feature_prune_stale_fork.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env python3
# Copyright (c) 2026-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.
"""Test node restart with a pruned stale-fork block whose parent has no transactions."""

from test_framework.blocktools import create_empty_fork
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error


class FeaturePruneStaleForkTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.extra_args = [["-prune=1", "-fastprune"]]

def run_test(self):
node = self.nodes[0]

self.log.info("Create a 2-block stale fork: parent has no transactions, child has transactions")
[side_parent, side_child] = create_empty_fork(node, 2)

node.submitheader(side_parent.serialize().hex())
node.submitblock(side_child.serialize().hex())
assert_equal(node.getblockheader(side_parent.hash_hex)["nTx"], 0)
assert_equal(node.getblockheader(side_child.hash_hex)["nTx"], 1)

self.log.info("Advance and prune so the stale-fork child's block data is removed from disk")
self.generate(node, 500)
node.pruneblockchain(node.getblockcount() - 100)
assert_raises_rpc_error(-1, "Block not available (pruned data)", node.getblock, side_child.hash_hex)

self.log.info("Restart and mine; node must reload cleanly after the stale-fork child was pruned")
self.restart_node(0)
self.generate(node, 1)


if __name__ == '__main__':
FeaturePruneStaleForkTest(__file__).main()
1 change: 1 addition & 0 deletions test/functional/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@
'wallet_startup.py',
'p2p_private_broadcast_retry_v1.py',
'feature_remove_pruned_files_on_startup.py',
'feature_prune_stale_fork.py',
'p2p_i2p_ports.py',
'p2p_i2p_sessions.py',
'feature_presegwit_node_upgrade.py',
Expand Down
Loading