b7ee5fb8a5 Merge bitcoin/bitcoin#33001: test: Do not pass tests on unhandled exceptions (merge-script)
1ac1f75ec0 Merge bitcoin/bitcoin#32765: test: Fix list index out of range error in feature_bip68_sequence.py (merge-script)
0f06cebc61 Merge bitcoin/bitcoin#33395: net: do not apply whitelist permissions to onion inbounds (merge-script)
53a5bd0790 Merge bitcoin/bitcoin#33296: net: check for empty header before calling FillBlock (Ava Chow)

Pull request description:

  Cherry-picks of bugfix patches included in the Bitcoin v30 release.

  net: check for empty header before calling FillBlock
  https://github.com/bitcoin/bitcoin/pull/33296

  net: do not apply whitelist permissions to onion inbounds
  https://github.com/bitcoin/bitcoin/pull/33395

  test: Fix list index out of range error in feature_bip68_sequence.py
  https://github.com/bitcoin/bitcoin/pull/32765

  test: Do not pass tests on unhandled exceptions
  https://github.com/bitcoin/bitcoin/pull/33001

ACKs for top commit:
  delta1:
    ACK b7ee5fb8a5

Tree-SHA512: 7fee367e549a0a5f8b2461be317dc8469feb0f907993f86638e9ca8ca333f9643fa90ecec4dd4edeba2d7e9098aa953f047dbaa0d35a9fc75a68fb62391c7616
This commit is contained in:
merge-script 2025-11-27 12:56:42 +02:00
commit 6664587c2f
No known key found for this signature in database
GPG key ID: DE8F6EA20A661697
6 changed files with 69 additions and 23 deletions

View file

@ -569,9 +569,9 @@ void CNode::CloseSocketDisconnect()
}
}
void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const {
void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, std::optional<CNetAddr> addr) const {
for (const auto& subnet : vWhitelistedRange) {
if (subnet.m_subnet.Match(addr)) NetPermissions::AddFlag(flags, subnet.m_flags);
if (addr.has_value() && subnet.m_subnet.Match(addr.value())) NetPermissions::AddFlag(flags, subnet.m_flags);
}
}
@ -1179,7 +1179,10 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
int nInbound = 0;
int nMaxInbound = nMaxConnections - m_max_outbound;
AddWhitelistPermissionFlags(permissionFlags, addr);
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(permissionFlags, inbound_onion ? std::optional<CNetAddr>{} : addr);
if (NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::Implicit)) {
NetPermissions::ClearFlag(permissionFlags, NetPermissionFlags::Implicit);
if (gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) NetPermissions::AddFlag(permissionFlags, NetPermissionFlags::ForceRelay);
@ -1243,7 +1246,6 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
nodeServices = static_cast<ServiceFlags>(nodeServices | NODE_BLOOM);
}
const bool inbound_onion = std::find(m_onion_binds.begin(), m_onion_binds.end(), addr_bind) != m_onion_binds.end();
CNode* pnode = new CNode(id,
nodeServices,
std::move(sock),

View file

@ -1077,7 +1077,7 @@ private:
bool AttemptToEvictConnection();
CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type);
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const;
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, std::optional<CNetAddr> addr) const;
void DeleteNode(CNode* pnode);

View file

@ -3772,6 +3772,17 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
PartiallyDownloadedBlock& partialBlock = *it->second.second->partialBlock;
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(resp.blockhash, pfrom.GetId());
Misbehaving(pfrom.GetId(), 100, "previous compact block reconstruction attempt failed");
LogPrint(BCLog::NET, "Peer %d sent compact block transactions multiple times\n", pfrom.GetId());
return;
}
ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn);
if (status == READ_STATUS_INVALID) {
RemoveBlockRequest(resp.blockhash, pfrom.GetId()); // Reset in-flight state in case Misbehaving does not result in a disconnect
@ -3779,6 +3790,9 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
return;
} else if (status == READ_STATUS_FAILED) {
// 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.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom), resp.blockhash));
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, invs));

View file

@ -161,8 +161,10 @@ class BIP68Test(BitcoinTestFramework):
# between height/time locking). Small random chance of making the locks
# all pass.
for _ in range(400):
available_utxos = len(utxos)
# Randomly choose up to 10 inputs
num_inputs = random.randint(1, 10)
num_inputs = random.randint(1, min(10, available_utxos))
random.shuffle(utxos)
# Track whether any sequence locks used should fail

View file

@ -285,7 +285,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# This index will be too high
prefilled_txn = PrefilledTransaction(1, block.vtx[0])
cmpct_block.prefilled_txn = [prefilled_txn]
self.segwit_node.send_await_disconnect(msg_cmpctblock(cmpct_block))
self.additional_segwit_node.send_await_disconnect(msg_cmpctblock(cmpct_block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.hashPrevBlock)
# Compare the generated shortids to what we expect based on BIP 152, given
@ -603,6 +603,42 @@ class CompactBlocksTest(BitcoinTestFramework):
test_node.send_and_ping(msg_no_witness_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):
version = test_node.cmpct_version
node = self.nodes[0]
@ -897,12 +933,16 @@ class CompactBlocksTest(BitcoinTestFramework):
self.test_invalid_tx_in_compactblock(self.segwit_node)
self.test_invalid_tx_in_compactblock(self.old_node)
self.log.info("Testing handling of multiple blocktxn responses...")
self.test_multiple_blocktxn_response(self.segwit_node)
self.segwit_node = self.nodes[0].add_p2p_connection(TestP2PConn(cmpct_version=2))
self.log.info("Testing invalid index in cmpctblock message...")
self.test_invalid_cmpctblock_message()
self.log.info("Testing high-bandwidth mode states via getpeerinfo...")
self.test_highbandwidth_mode_states_via_getpeerinfo()
if __name__ == '__main__':
CompactBlocksTest().main()

View file

@ -130,26 +130,14 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
try:
self.setup()
self.run_test()
except JSONRPCException:
self.log.exception("JSONRPC error")
self.success = TestStatus.FAILED
except SkipTest as e:
self.log.warning("Test Skipped: %s" % e.message)
self.success = TestStatus.SKIPPED
except AssertionError:
self.log.exception("Assertion failed")
self.success = TestStatus.FAILED
except KeyError:
self.log.exception("Key error")
self.success = TestStatus.FAILED
except subprocess.CalledProcessError as e:
self.log.exception("Called Process failed with '{}'".format(e.output))
self.log.exception(f"Called Process failed with stdout='{e.stdout}'; stderr='{e.stderr}';")
self.success = TestStatus.FAILED
except Exception:
self.log.exception("Unexpected exception caught during testing")
self.success = TestStatus.FAILED
except KeyboardInterrupt:
self.log.warning("Exiting after keyboard interrupt")
except BaseException:
self.log.exception("Unexpected exception")
self.success = TestStatus.FAILED
finally:
exit_code = self.shutdown()