mirror of https://github.com/bitcoin/bitcoin.git
Merge bitcoin/bitcoin#33344: [29.x] Backports
f2bd79f80c
doc: update manual pages for v29.2rc1 (fanquake)461dd13faf
build: bump version to v29.2rc1 (fanquake)9bc4afb62c
doc: update release notes for 29.x (fanquake)61cdc04a83
net: Do not apply whitelist permission to onion inbounds (Martin Zumsande)1288d44804
test: send duplicate blocktxn message in p2p_compactblocks.py (Eugene Siegel)569ceb0df4
net: check for empty header before calling FillBlock (Eugene Siegel)4c940d4789
p2p: remove vestigial READ_STATUS_CHECKBLOCK_FAILED (Greg Sanders)9b95ab5e9d
p2p: Add witness mutation check inside FillBlock (Greg Sanders)e97588fc3d
trace: Workaround GCC bug compiling with old systemtap (Luke Dashjr)324caa8497
ci: always use tag for LLVM checkout (fanquake)2717331981
Fix benchmark CSV output (Hennadii Stepanov) Pull request description: Backports: * #32646 * #33296 * #33310 * #33340 * #33364 * #33395 Plus changes for 29.2rc1. ACKs for top commit: darosior: utACKf2bd79f80c
mzumsande: utACKf2bd79f80c
Tree-SHA512: 346a92032b7a069e2941056c6273ff65e360c5834832b106350a9cd42b634518cc75b807da6e51a6292e3a33342bb7b145777d3538a2792e03c63962d747a025
This commit is contained in:
commit
7e1eca4882
|
@ -28,9 +28,9 @@ get_directory_property(precious_variables CACHE_VARIABLES)
|
|||
#=============================
|
||||
set(CLIENT_NAME "Bitcoin Core")
|
||||
set(CLIENT_VERSION_MAJOR 29)
|
||||
set(CLIENT_VERSION_MINOR 1)
|
||||
set(CLIENT_VERSION_MINOR 2)
|
||||
set(CLIENT_VERSION_BUILD 0)
|
||||
set(CLIENT_VERSION_RC 0)
|
||||
set(CLIENT_VERSION_RC 1)
|
||||
set(CLIENT_VERSION_IS_RELEASE "true")
|
||||
set(COPYRIGHT_YEAR "2025")
|
||||
|
||||
|
|
|
@ -55,10 +55,9 @@ if [ -n "$PIP_PACKAGES" ]; then
|
|||
fi
|
||||
|
||||
if [[ -n "${USE_INSTRUMENTED_LIBCPP}" ]]; then
|
||||
${CI_RETRY_EXE} git clone --depth=1 https://github.com/llvm/llvm-project -b "llvmorg-21.1.1" /llvm-project
|
||||
|
||||
if [ -n "${APT_LLVM_V}" ]; then
|
||||
${CI_RETRY_EXE} git clone --depth=1 https://github.com/llvm/llvm-project -b "llvmorg-$( clang --version | sed --silent 's@.*clang version \([0-9.]*\).*@\1@p' )" /llvm-project
|
||||
else
|
||||
${CI_RETRY_EXE} git clone --depth=1 https://github.com/llvm/llvm-project -b "llvmorg-21.1.0" /llvm-project
|
||||
|
||||
cmake -G Ninja -B /clang_build/ \
|
||||
-DLLVM_ENABLE_PROJECTS="clang" \
|
||||
|
|
|
@ -36,6 +36,10 @@ if(USDT_INCLUDE_DIR)
|
|||
include(CheckCXXSourceCompiles)
|
||||
set(CMAKE_REQUIRED_INCLUDES ${USDT_INCLUDE_DIR})
|
||||
check_cxx_source_compiles("
|
||||
#if defined(__arm__)
|
||||
# define STAP_SDT_ARG_CONSTRAINT g
|
||||
#endif
|
||||
|
||||
// Setting SDT_USE_VARIADIC lets systemtap (sys/sdt.h) know that we want to use
|
||||
// the optional variadic macros to define tracepoints.
|
||||
#define SDT_USE_VARIADIC 1
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH BITCOIN-CLI "1" "September 2025" "bitcoin-cli v29.1.0" "User Commands"
|
||||
.TH BITCOIN-CLI "1" "September 2025" "bitcoin-cli v29.2.0rc1" "User Commands"
|
||||
.SH NAME
|
||||
bitcoin-cli \- manual page for bitcoin-cli v29.1.0
|
||||
bitcoin-cli \- manual page for bitcoin-cli v29.2.0rc1
|
||||
.SH SYNOPSIS
|
||||
.B bitcoin-cli
|
||||
[\fI\,options\/\fR] \fI\,<command> \/\fR[\fI\,params\/\fR]
|
||||
|
@ -15,7 +15,7 @@ bitcoin-cli \- manual page for bitcoin-cli v29.1.0
|
|||
.B bitcoin-cli
|
||||
[\fI\,options\/\fR] \fI\,help <command>\/\fR
|
||||
.SH DESCRIPTION
|
||||
Bitcoin Core RPC client version v29.1.0
|
||||
Bitcoin Core RPC client version v29.2.0rc1
|
||||
.PP
|
||||
The bitcoin\-cli utility provides a command line interface to interact with a Bitcoin Core RPC server.
|
||||
.PP
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH BITCOIN-QT "1" "September 2025" "bitcoin-qt v29.1.0" "User Commands"
|
||||
.TH BITCOIN-QT "1" "September 2025" "bitcoin-qt v29.2.0rc1" "User Commands"
|
||||
.SH NAME
|
||||
bitcoin-qt \- manual page for bitcoin-qt v29.1.0
|
||||
bitcoin-qt \- manual page for bitcoin-qt v29.2.0rc1
|
||||
.SH SYNOPSIS
|
||||
.B bitcoin-qt
|
||||
[\fI\,options\/\fR] [\fI\,URI\/\fR]
|
||||
.SH DESCRIPTION
|
||||
Bitcoin Core version v29.1.0
|
||||
Bitcoin Core version v29.2.0rc1
|
||||
.PP
|
||||
The bitcoin\-qt application provides a graphical interface for interacting with Bitcoin Core.
|
||||
.PP
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH BITCOIN-TX "1" "September 2025" "bitcoin-tx v29.1.0" "User Commands"
|
||||
.TH BITCOIN-TX "1" "September 2025" "bitcoin-tx v29.2.0rc1" "User Commands"
|
||||
.SH NAME
|
||||
bitcoin-tx \- manual page for bitcoin-tx v29.1.0
|
||||
bitcoin-tx \- manual page for bitcoin-tx v29.2.0rc1
|
||||
.SH SYNOPSIS
|
||||
.B bitcoin-tx
|
||||
[\fI\,options\/\fR] \fI\,<hex-tx> \/\fR[\fI\,commands\/\fR]
|
||||
|
@ -9,7 +9,7 @@ bitcoin-tx \- manual page for bitcoin-tx v29.1.0
|
|||
.B bitcoin-tx
|
||||
[\fI\,options\/\fR] \fI\,-create \/\fR[\fI\,commands\/\fR]
|
||||
.SH DESCRIPTION
|
||||
Bitcoin Core bitcoin\-tx utility version v29.1.0
|
||||
Bitcoin Core bitcoin\-tx utility version v29.2.0rc1
|
||||
.PP
|
||||
The bitcoin\-tx tool is used for creating and modifying bitcoin transactions.
|
||||
.PP
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH BITCOIN-UTIL "1" "September 2025" "bitcoin-util v29.1.0" "User Commands"
|
||||
.TH BITCOIN-UTIL "1" "September 2025" "bitcoin-util v29.2.0rc1" "User Commands"
|
||||
.SH NAME
|
||||
bitcoin-util \- manual page for bitcoin-util v29.1.0
|
||||
bitcoin-util \- manual page for bitcoin-util v29.2.0rc1
|
||||
.SH SYNOPSIS
|
||||
.B bitcoin-util
|
||||
[\fI\,options\/\fR] [\fI\,command\/\fR]
|
||||
|
@ -9,7 +9,7 @@ bitcoin-util \- manual page for bitcoin-util v29.1.0
|
|||
.B bitcoin-util
|
||||
[\fI\,options\/\fR] \fI\,grind <hex-block-header>\/\fR
|
||||
.SH DESCRIPTION
|
||||
Bitcoin Core bitcoin\-util utility version v29.1.0
|
||||
Bitcoin Core bitcoin\-util utility version v29.2.0rc1
|
||||
.PP
|
||||
The bitcoin\-util tool provides bitcoin related functionality that does not rely on the ability to access a running node. Available [commands] are listed below.
|
||||
.SH OPTIONS
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH BITCOIN-WALLET "1" "September 2025" "bitcoin-wallet v29.1.0" "User Commands"
|
||||
.TH BITCOIN-WALLET "1" "September 2025" "bitcoin-wallet v29.2.0rc1" "User Commands"
|
||||
.SH NAME
|
||||
bitcoin-wallet \- manual page for bitcoin-wallet v29.1.0
|
||||
bitcoin-wallet \- manual page for bitcoin-wallet v29.2.0rc1
|
||||
.SH SYNOPSIS
|
||||
.B bitcoin-wallet
|
||||
[\fI\,options\/\fR] \fI\,<command>\/\fR
|
||||
.SH DESCRIPTION
|
||||
Bitcoin Core bitcoin\-wallet utility version v29.1.0
|
||||
Bitcoin Core bitcoin\-wallet utility version v29.2.0rc1
|
||||
.PP
|
||||
bitcoin\-wallet is an offline tool for creating and interacting with Bitcoin Core wallet files.
|
||||
.PP
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH BITCOIND "1" "September 2025" "bitcoind v29.1.0" "User Commands"
|
||||
.TH BITCOIND "1" "September 2025" "bitcoind v29.2.0rc1" "User Commands"
|
||||
.SH NAME
|
||||
bitcoind \- manual page for bitcoind v29.1.0
|
||||
bitcoind \- manual page for bitcoind v29.2.0rc1
|
||||
.SH SYNOPSIS
|
||||
.B bitcoind
|
||||
[\fI\,options\/\fR]
|
||||
.SH DESCRIPTION
|
||||
Bitcoin Core daemon version v29.1.0
|
||||
Bitcoin Core daemon version v29.2.0rc1
|
||||
.PP
|
||||
The Bitcoin Core daemon (bitcoind) is a headless program that connects to the Bitcoin network to validate and relay transactions and blocks, as well as relaying addresses.
|
||||
.PP
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
Bitcoin Core version 29.x is now available from:
|
||||
Bitcoin Core version 29.2rc1 is now available from:
|
||||
|
||||
<https://bitcoincore.org/bin/bitcoin-core-29.x/>
|
||||
<https://bitcoincore.org/bin/bitcoin-core-29.2/test.rc1/>
|
||||
|
||||
This release includes various bug fixes and performance
|
||||
improvements, as well as updated translations.
|
||||
|
@ -37,19 +37,37 @@ unsupported systems.
|
|||
Notable changes
|
||||
===============
|
||||
|
||||
### P2P
|
||||
|
||||
- #32646 p2p: Add witness mutation check inside FillBlock
|
||||
- #33296 net: check for empty header before calling FillBlock
|
||||
- #33395 net: do not apply whitelist permissions to onion inbounds
|
||||
|
||||
### CI
|
||||
|
||||
- #32999 ci: Use APT_LLVM_V in msan task
|
||||
- #33099 ci: allow for any libc++ intrumentation & use it for TSAN
|
||||
- #33258 ci: use LLVM 21
|
||||
- #33364 ci: always use tag for LLVM checkout
|
||||
|
||||
### Misc
|
||||
|
||||
- #33310 trace: Workaround GCC bug compiling with old systemtap
|
||||
- #33340 Fix benchmark CSV output
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Thanks to everyone who directly contributed to this release:
|
||||
|
||||
- Eugene Siegel
|
||||
- fanquake
|
||||
- Greg Sanders
|
||||
- Hennadii Stepanov
|
||||
- Luke Dashjr
|
||||
- MarcoFalke
|
||||
- Martin Zumsande
|
||||
- Vasil Dimov
|
||||
|
||||
As well as to everyone that helped with translations on
|
||||
[Transifex](https://explore.transifex.com/bitcoin/bitcoin/).
|
||||
|
|
|
@ -180,7 +180,7 @@ bool PartiallyDownloadedBlock::IsTxAvailable(size_t index) const
|
|||
return txn_available[index] != nullptr;
|
||||
}
|
||||
|
||||
ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<CTransactionRef>& vtx_missing)
|
||||
ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<CTransactionRef>& vtx_missing, bool segwit_active)
|
||||
{
|
||||
if (header.IsNull()) return READ_STATUS_INVALID;
|
||||
|
||||
|
@ -205,16 +205,11 @@ ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<
|
|||
if (vtx_missing.size() != tx_missing_offset)
|
||||
return READ_STATUS_INVALID;
|
||||
|
||||
BlockValidationState state;
|
||||
CheckBlockFn check_block = m_check_block_mock ? m_check_block_mock : CheckBlock;
|
||||
if (!check_block(block, state, Params().GetConsensus(), /*fCheckPoW=*/true, /*fCheckMerkleRoot=*/true)) {
|
||||
// TODO: We really want to just check merkle tree manually here,
|
||||
// but that is expensive, and CheckBlock caches a block's
|
||||
// "checked-status" (in the CBlock?). CBlock should be able to
|
||||
// check its own merkle root and cache that check.
|
||||
if (state.GetResult() == BlockValidationResult::BLOCK_MUTATED)
|
||||
return READ_STATUS_FAILED; // Possible Short ID collision
|
||||
return READ_STATUS_CHECKBLOCK_FAILED;
|
||||
// Check for possible mutations early now that we have a seemingly good block
|
||||
IsBlockMutatedFn check_mutated{m_check_block_mutated_mock ? m_check_block_mutated_mock : IsBlockMutated};
|
||||
if (check_mutated(/*block=*/block,
|
||||
/*check_witness_root=*/segwit_active)) {
|
||||
return READ_STATUS_FAILED; // Possible Short ID collision
|
||||
}
|
||||
|
||||
LogDebug(BCLog::CMPCTBLOCK, "Successfully reconstructed block %s with %lu txn prefilled, %lu txn from mempool (incl at least %lu from extra pool) and %lu txn requested\n", hash.ToString(), prefilled_count, mempool_count, extra_count, vtx_missing.size());
|
||||
|
|
|
@ -84,8 +84,6 @@ typedef enum ReadStatus_t
|
|||
READ_STATUS_OK,
|
||||
READ_STATUS_INVALID, // Invalid object, peer is sending bogus crap
|
||||
READ_STATUS_FAILED, // Failed to process object
|
||||
READ_STATUS_CHECKBLOCK_FAILED, // Used only by FillBlock to indicate a
|
||||
// failure in CheckBlock.
|
||||
} ReadStatus;
|
||||
|
||||
class CBlockHeaderAndShortTxIDs {
|
||||
|
@ -141,15 +139,16 @@ public:
|
|||
CBlockHeader header;
|
||||
|
||||
// Can be overridden for testing
|
||||
using CheckBlockFn = std::function<bool(const CBlock&, BlockValidationState&, const Consensus::Params&, bool, bool)>;
|
||||
CheckBlockFn m_check_block_mock{nullptr};
|
||||
using IsBlockMutatedFn = std::function<bool(const CBlock&, bool)>;
|
||||
IsBlockMutatedFn m_check_block_mutated_mock{nullptr};
|
||||
|
||||
explicit PartiallyDownloadedBlock(CTxMemPool* poolIn) : pool(poolIn) {}
|
||||
|
||||
// extra_txn is a list of extra orphan/conflicted/etc transactions to look at
|
||||
ReadStatus InitData(const CBlockHeaderAndShortTxIDs& cmpctblock, const std::vector<CTransactionRef>& extra_txn);
|
||||
bool IsTxAvailable(size_t index) const;
|
||||
ReadStatus FillBlock(CBlock& block, const std::vector<CTransactionRef>& vtx_missing);
|
||||
// segwit_active enforces witness mutation checks just before reporting a healthy status
|
||||
ReadStatus FillBlock(CBlock& block, const std::vector<CTransactionRef>& vtx_missing, bool segwit_active);
|
||||
};
|
||||
|
||||
#endif // BITCOIN_BLOCKENCODINGS_H
|
||||
|
|
|
@ -627,7 +627,7 @@ std::string SHA256AutoDetect(sha256_implementation::UseImplementation use_implem
|
|||
Transform = sha256_x86_shani::Transform;
|
||||
TransformD64 = TransformD64Wrapper<sha256_x86_shani::Transform>;
|
||||
TransformD64_2way = sha256d64_x86_shani::Transform_2way;
|
||||
ret = "x86_shani(1way,2way)";
|
||||
ret = "x86_shani(1way;2way)";
|
||||
have_sse4 = false; // Disable SSE4/AVX2;
|
||||
have_avx2 = false;
|
||||
}
|
||||
|
@ -641,14 +641,14 @@ std::string SHA256AutoDetect(sha256_implementation::UseImplementation use_implem
|
|||
#endif
|
||||
#if defined(ENABLE_SSE41)
|
||||
TransformD64_4way = sha256d64_sse41::Transform_4way;
|
||||
ret += ",sse41(4way)";
|
||||
ret += ";sse41(4way)";
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(ENABLE_AVX2)
|
||||
if (have_avx2 && have_avx && enabled_avx) {
|
||||
TransformD64_8way = sha256d64_avx2::Transform_8way;
|
||||
ret += ",avx2(8way)";
|
||||
ret += ";avx2(8way)";
|
||||
}
|
||||
#endif
|
||||
#endif // defined(HAVE_GETCPUID)
|
||||
|
@ -682,7 +682,7 @@ std::string SHA256AutoDetect(sha256_implementation::UseImplementation use_implem
|
|||
Transform = sha256_arm_shani::Transform;
|
||||
TransformD64 = TransformD64Wrapper<sha256_arm_shani::Transform>;
|
||||
TransformD64_2way = sha256d64_arm_shani::Transform_2way;
|
||||
ret = "arm_shani(1way,2way)";
|
||||
ret = "arm_shani(1way;2way)";
|
||||
}
|
||||
#endif
|
||||
#endif // DISABLE_OPTIMIZED_SHA256
|
||||
|
|
11
src/net.cpp
11
src/net.cpp
|
@ -575,9 +575,9 @@ void CNode::CloseSocketDisconnect()
|
|||
m_i2p_sam_session.reset();
|
||||
}
|
||||
|
||||
void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr, const std::vector<NetWhitelistPermissions>& ranges) const {
|
||||
void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, std::optional<CNetAddr> addr, const std::vector<NetWhitelistPermissions>& ranges) const {
|
||||
for (const auto& subnet : ranges) {
|
||||
if (subnet.m_subnet.Match(addr)) {
|
||||
if (addr.has_value() && subnet.m_subnet.Match(addr.value())) {
|
||||
NetPermissions::AddFlag(flags, subnet.m_flags);
|
||||
}
|
||||
}
|
||||
|
@ -1767,7 +1767,11 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
|
|||
{
|
||||
int nInbound = 0;
|
||||
|
||||
AddWhitelistPermissionFlags(permission_flags, addr, vWhitelistedRangeIncoming);
|
||||
const bool inbound_onion = std::find(m_onion_binds.begin(), m_onion_binds.end(), addr_bind) != m_onion_binds.end();
|
||||
|
||||
// Tor inbound connections do not reveal the peer's actual network address.
|
||||
// Therefore do not apply address-based whitelist permissions to them.
|
||||
AddWhitelistPermissionFlags(permission_flags, inbound_onion ? std::optional<CNetAddr>{} : addr, vWhitelistedRangeIncoming);
|
||||
|
||||
{
|
||||
LOCK(m_nodes_mutex);
|
||||
|
@ -1822,7 +1826,6 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
|
|||
NodeId id = GetNewNodeId();
|
||||
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
|
||||
|
||||
const bool inbound_onion = std::find(m_onion_binds.begin(), m_onion_binds.end(), addr_bind) != m_onion_binds.end();
|
||||
// The V2Transport transparently falls back to V1 behavior when an incoming V1 connection is
|
||||
// detected, so use it whenever we signal NODE_P2P_V2.
|
||||
ServiceFlags local_services = GetLocalServices();
|
||||
|
|
|
@ -1364,7 +1364,7 @@ private:
|
|||
|
||||
bool AttemptToEvictConnection();
|
||||
CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type, bool use_v2transport) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
|
||||
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr, const std::vector<NetWhitelistPermissions>& ranges) const;
|
||||
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, std::optional<CNetAddr> addr, const std::vector<NetWhitelistPermissions>& ranges) const;
|
||||
|
||||
void DeleteNode(CNode* pnode);
|
||||
|
||||
|
|
|
@ -3314,7 +3314,21 @@ void PeerManagerImpl::ProcessCompactBlockTxns(CNode& pfrom, Peer& peer, const Bl
|
|||
}
|
||||
|
||||
PartiallyDownloadedBlock& partialBlock = *range_flight.first->second.second->partialBlock;
|
||||
ReadStatus status = partialBlock.FillBlock(*pblock, block_transactions.txn);
|
||||
|
||||
if (partialBlock.header.IsNull()) {
|
||||
// It is possible for the header to be empty if a previous call to FillBlock wiped the header, but left
|
||||
// the PartiallyDownloadedBlock pointer around (i.e. did not call RemoveBlockRequest). In this case, we
|
||||
// should not call LookupBlockIndex below.
|
||||
RemoveBlockRequest(block_transactions.blockhash, pfrom.GetId());
|
||||
Misbehaving(peer, "previous compact block reconstruction attempt failed");
|
||||
LogDebug(BCLog::NET, "Peer %d sent compact block transactions multiple times", pfrom.GetId());
|
||||
return;
|
||||
}
|
||||
|
||||
// We should not have gotten this far in compact block processing unless it's attached to a known header
|
||||
const CBlockIndex* prev_block{Assume(m_chainman.m_blockman.LookupBlockIndex(partialBlock.header.hashPrevBlock))};
|
||||
ReadStatus status = partialBlock.FillBlock(*pblock, block_transactions.txn,
|
||||
/*segwit_active=*/DeploymentActiveAfter(prev_block, m_chainman, Consensus::DEPLOYMENT_SEGWIT));
|
||||
if (status == READ_STATUS_INVALID) {
|
||||
RemoveBlockRequest(block_transactions.blockhash, pfrom.GetId()); // Reset in-flight state in case Misbehaving does not result in a disconnect
|
||||
Misbehaving(peer, "invalid compact block/non-matching block transactions");
|
||||
|
@ -3322,6 +3336,9 @@ void PeerManagerImpl::ProcessCompactBlockTxns(CNode& pfrom, Peer& peer, const Bl
|
|||
} else if (status == READ_STATUS_FAILED) {
|
||||
if (first_in_flight) {
|
||||
// Might have collided, fall back to getdata now :(
|
||||
// We keep the failed partialBlock to disallow processing another compact block announcement from the same
|
||||
// peer for the same block. We let the full block download below continue under the same m_downloading_since
|
||||
// timer.
|
||||
std::vector<CInv> invs;
|
||||
invs.emplace_back(MSG_BLOCK | GetFetchFlags(peer), block_transactions.blockhash);
|
||||
MakeAndPushMessage(pfrom, NetMsgType::GETDATA, invs);
|
||||
|
@ -3331,23 +3348,7 @@ void PeerManagerImpl::ProcessCompactBlockTxns(CNode& pfrom, Peer& peer, const Bl
|
|||
return;
|
||||
}
|
||||
} else {
|
||||
// Block is either okay, or possibly we received
|
||||
// READ_STATUS_CHECKBLOCK_FAILED.
|
||||
// Note that CheckBlock can only fail for one of a few reasons:
|
||||
// 1. bad-proof-of-work (impossible here, because we've already
|
||||
// accepted the header)
|
||||
// 2. merkleroot doesn't match the transactions given (already
|
||||
// caught in FillBlock with READ_STATUS_FAILED, so
|
||||
// impossible here)
|
||||
// 3. the block is otherwise invalid (eg invalid coinbase,
|
||||
// block is too big, too many legacy sigops, etc).
|
||||
// So if CheckBlock failed, #3 is the only possibility.
|
||||
// Under BIP 152, we don't discourage the peer unless proof of work is
|
||||
// invalid (we don't require all the stateless checks to have
|
||||
// been run). This is handled below, so just treat this as
|
||||
// though the block was successfully read, and rely on the
|
||||
// handling in ProcessNewBlock to ensure the block index is
|
||||
// updated, etc.
|
||||
// Block is okay for further processing
|
||||
RemoveBlockRequest(block_transactions.blockhash, pfrom.GetId()); // it is now an empty pointer
|
||||
fBlockRead = true;
|
||||
// mapBlockSource is used for potentially punishing peers and
|
||||
|
@ -4462,7 +4463,9 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|||
return;
|
||||
}
|
||||
std::vector<CTransactionRef> dummy;
|
||||
status = tempBlock.FillBlock(*pblock, dummy);
|
||||
const CBlockIndex* prev_block{Assume(m_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.hashPrevBlock))};
|
||||
status = tempBlock.FillBlock(*pblock, dummy,
|
||||
/*segwit_active=*/DeploymentActiveAfter(prev_block, m_chainman, Consensus::DEPLOYMENT_SEGWIT));
|
||||
if (status == READ_STATUS_OK) {
|
||||
fBlockReconstructed = true;
|
||||
}
|
||||
|
|
|
@ -95,21 +95,21 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
|
|||
CBlock block2;
|
||||
{
|
||||
PartiallyDownloadedBlock tmp = partialBlock;
|
||||
BOOST_CHECK(partialBlock.FillBlock(block2, {}) == READ_STATUS_INVALID); // No transactions
|
||||
BOOST_CHECK(partialBlock.FillBlock(block2, {}, /*segwit_active=*/true) == READ_STATUS_INVALID); // No transactions
|
||||
partialBlock = tmp;
|
||||
}
|
||||
|
||||
// Wrong transaction
|
||||
{
|
||||
PartiallyDownloadedBlock tmp = partialBlock;
|
||||
partialBlock.FillBlock(block2, {block.vtx[2]}); // Current implementation doesn't check txn here, but don't require that
|
||||
partialBlock.FillBlock(block2, {block.vtx[2]}, /*segwit_active=*/true); // Current implementation doesn't check txn here, but don't require that
|
||||
partialBlock = tmp;
|
||||
}
|
||||
bool mutated;
|
||||
BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated));
|
||||
|
||||
CBlock block3;
|
||||
BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[1]}) == READ_STATUS_OK);
|
||||
BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[1]}, /*segwit_active=*/true) == READ_STATUS_OK);
|
||||
BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
|
||||
BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString());
|
||||
BOOST_CHECK(!mutated);
|
||||
|
@ -182,14 +182,14 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
|
|||
CBlock block2;
|
||||
{
|
||||
PartiallyDownloadedBlock tmp = partialBlock;
|
||||
BOOST_CHECK(partialBlock.FillBlock(block2, {}) == READ_STATUS_INVALID); // No transactions
|
||||
BOOST_CHECK(partialBlock.FillBlock(block2, {}, /*segwit_active=*/true) == READ_STATUS_INVALID); // No transactions
|
||||
partialBlock = tmp;
|
||||
}
|
||||
|
||||
// Wrong transaction
|
||||
{
|
||||
PartiallyDownloadedBlock tmp = partialBlock;
|
||||
partialBlock.FillBlock(block2, {block.vtx[1]}); // Current implementation doesn't check txn here, but don't require that
|
||||
partialBlock.FillBlock(block2, {block.vtx[1]}, /*segwit_active=*/true); // Current implementation doesn't check txn here, but don't require that
|
||||
partialBlock = tmp;
|
||||
}
|
||||
BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 2); // +2 because of partialBlock and block2
|
||||
|
@ -198,7 +198,7 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
|
|||
|
||||
CBlock block3;
|
||||
PartiallyDownloadedBlock partialBlockCopy = partialBlock;
|
||||
BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[0]}) == READ_STATUS_OK);
|
||||
BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[0]}, /*segwit_active=*/true) == READ_STATUS_OK);
|
||||
BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
|
||||
BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString());
|
||||
BOOST_CHECK(!mutated);
|
||||
|
@ -252,7 +252,7 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
|
|||
|
||||
CBlock block2;
|
||||
PartiallyDownloadedBlock partialBlockCopy = partialBlock;
|
||||
BOOST_CHECK(partialBlock.FillBlock(block2, {}) == READ_STATUS_OK);
|
||||
BOOST_CHECK(partialBlock.FillBlock(block2, {}, /*segwit_active=*/true) == READ_STATUS_OK);
|
||||
BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
|
||||
bool mutated;
|
||||
BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString());
|
||||
|
@ -300,7 +300,7 @@ BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
|
|||
|
||||
CBlock block2;
|
||||
std::vector<CTransactionRef> vtx_missing;
|
||||
BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_OK);
|
||||
BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing, /*segwit_active=*/true) == READ_STATUS_OK);
|
||||
BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
|
||||
BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString());
|
||||
BOOST_CHECK(!mutated);
|
||||
|
|
|
@ -32,14 +32,10 @@ void initialize_pdb()
|
|||
g_setup = testing_setup.get();
|
||||
}
|
||||
|
||||
PartiallyDownloadedBlock::CheckBlockFn FuzzedCheckBlock(std::optional<BlockValidationResult> result)
|
||||
PartiallyDownloadedBlock::IsBlockMutatedFn FuzzedIsBlockMutated(bool result)
|
||||
{
|
||||
return [result](const CBlock&, BlockValidationState& state, const Consensus::Params&, bool, bool) {
|
||||
if (result) {
|
||||
return state.Invalid(*result);
|
||||
}
|
||||
|
||||
return true;
|
||||
return [result](const CBlock& block, bool) {
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -111,36 +107,22 @@ FUZZ_TARGET(partially_downloaded_block, .init = initialize_pdb)
|
|||
skipped_missing |= (!pdb.IsTxAvailable(i) && skip);
|
||||
}
|
||||
|
||||
// Mock CheckBlock
|
||||
bool fail_check_block{fuzzed_data_provider.ConsumeBool()};
|
||||
auto validation_result =
|
||||
fuzzed_data_provider.PickValueInArray(
|
||||
{BlockValidationResult::BLOCK_RESULT_UNSET,
|
||||
BlockValidationResult::BLOCK_CONSENSUS,
|
||||
BlockValidationResult::BLOCK_CACHED_INVALID,
|
||||
BlockValidationResult::BLOCK_INVALID_HEADER,
|
||||
BlockValidationResult::BLOCK_MUTATED,
|
||||
BlockValidationResult::BLOCK_MISSING_PREV,
|
||||
BlockValidationResult::BLOCK_INVALID_PREV,
|
||||
BlockValidationResult::BLOCK_TIME_FUTURE,
|
||||
BlockValidationResult::BLOCK_CHECKPOINT,
|
||||
BlockValidationResult::BLOCK_HEADER_LOW_WORK});
|
||||
pdb.m_check_block_mock = FuzzedCheckBlock(
|
||||
fail_check_block ?
|
||||
std::optional<BlockValidationResult>{validation_result} :
|
||||
std::nullopt);
|
||||
bool segwit_active{fuzzed_data_provider.ConsumeBool()};
|
||||
|
||||
// Mock IsBlockMutated
|
||||
bool fail_block_mutated{fuzzed_data_provider.ConsumeBool()};
|
||||
pdb.m_check_block_mutated_mock = FuzzedIsBlockMutated(fail_block_mutated);
|
||||
|
||||
CBlock reconstructed_block;
|
||||
auto fill_status{pdb.FillBlock(reconstructed_block, missing)};
|
||||
auto fill_status{pdb.FillBlock(reconstructed_block, missing, segwit_active)};
|
||||
switch (fill_status) {
|
||||
case READ_STATUS_OK:
|
||||
assert(!skipped_missing);
|
||||
assert(!fail_check_block);
|
||||
assert(!fail_block_mutated);
|
||||
assert(block->GetHash() == reconstructed_block.GetHash());
|
||||
break;
|
||||
case READ_STATUS_CHECKBLOCK_FAILED: [[fallthrough]];
|
||||
case READ_STATUS_FAILED:
|
||||
assert(fail_check_block);
|
||||
assert(fail_block_mutated);
|
||||
break;
|
||||
case READ_STATUS_INVALID:
|
||||
break;
|
||||
|
|
|
@ -9,6 +9,13 @@
|
|||
|
||||
#ifdef ENABLE_TRACING
|
||||
|
||||
// Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103395
|
||||
// systemtap 4.6 on 32-bit ARM triggers internal compiler error
|
||||
// (this workaround is included in systemtap 4.7+)
|
||||
#if defined(__arm__)
|
||||
# define STAP_SDT_ARG_CONSTRAINT g
|
||||
#endif
|
||||
|
||||
// Setting SDT_USE_VARIADIC lets systemtap (sys/sdt.h) know that we want to use
|
||||
// the optional variadic macros to define tracepoints.
|
||||
#define SDT_USE_VARIADIC 1
|
||||
|
|
|
@ -566,6 +566,42 @@ class CompactBlocksTest(BitcoinTestFramework):
|
|||
test_node.send_and_ping(msg_block(block))
|
||||
assert_equal(int(node.getbestblockhash(), 16), block.sha256)
|
||||
|
||||
# Multiple blocktxn responses will cause a node to get disconnected.
|
||||
def test_multiple_blocktxn_response(self, test_node):
|
||||
node = self.nodes[0]
|
||||
utxo = self.utxos[0]
|
||||
|
||||
block = self.build_block_with_transactions(node, utxo, 2)
|
||||
|
||||
# Send compact block
|
||||
comp_block = HeaderAndShortIDs()
|
||||
comp_block.initialize_from_block(block, prefill_list=[0], use_witness=True)
|
||||
test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
|
||||
absolute_indexes = []
|
||||
with p2p_lock:
|
||||
assert "getblocktxn" in test_node.last_message
|
||||
absolute_indexes = test_node.last_message["getblocktxn"].block_txn_request.to_absolute()
|
||||
assert_equal(absolute_indexes, [1, 2])
|
||||
|
||||
# Send a blocktxn that does not succeed in reconstruction, triggering
|
||||
# getdata fallback.
|
||||
msg = msg_blocktxn()
|
||||
msg.block_transactions = BlockTransactions(block.sha256, [block.vtx[2]] + [block.vtx[1]])
|
||||
test_node.send_and_ping(msg)
|
||||
|
||||
# Tip should not have updated
|
||||
assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock)
|
||||
|
||||
# We should receive a getdata request
|
||||
test_node.wait_for_getdata([block.sha256], timeout=10)
|
||||
assert test_node.last_message["getdata"].inv[0].type == MSG_BLOCK or \
|
||||
test_node.last_message["getdata"].inv[0].type == MSG_BLOCK | MSG_WITNESS_FLAG
|
||||
|
||||
# Send the same blocktxn and assert the sender gets disconnected.
|
||||
with node.assert_debug_log(['previous compact block reconstruction attempt failed']):
|
||||
test_node.send_message(msg)
|
||||
test_node.wait_for_disconnect()
|
||||
|
||||
def test_getblocktxn_handler(self, test_node):
|
||||
node = self.nodes[0]
|
||||
# bitcoind will not send blocktxn responses for blocks whose height is
|
||||
|
@ -957,6 +993,12 @@ class CompactBlocksTest(BitcoinTestFramework):
|
|||
self.log.info("Testing handling of invalid compact blocks...")
|
||||
self.test_invalid_tx_in_compactblock(self.segwit_node)
|
||||
|
||||
self.log.info("Testing handling of multiple blocktxn responses...")
|
||||
self.test_multiple_blocktxn_response(self.segwit_node)
|
||||
|
||||
# The previous test will lead to a disconnection. Reconnect before continuing.
|
||||
self.segwit_node = self.nodes[0].add_p2p_connection(TestP2PConn())
|
||||
|
||||
self.log.info("Testing invalid index in cmpctblock message...")
|
||||
self.test_invalid_cmpctblock_message()
|
||||
|
||||
|
|
Loading…
Reference in New Issue