According to BIP37, getdata
commands with a request for filtered blocks via type MSG_FILTERED_BLOCK
in the inv
submessage are only responded to if a filter is set:
The getdata command is extended to allow a new type in the inv submessage. The type field can now be MSG_FILTERED_BLOCK (== 3) rather than MSG_BLOCK. If no filter has been set on the connection, a request for filtered blocks is ignored. If a filter has been set, a merkleblock message is returned for the requested block hash.
Expected behavior
When no BIP37 filter is set and a we request a filtered block, there should be no response from the node.
Actual behavior
When no BIP37 filter is set and a we request a filtered block, we get a merkleblock
message in reply.
To reproduce
The behaviour can reproduced by adding the following lines to the functional test p2p_filter.py
(introduced recently with PR #18334, commit https://github.com/bitcoin/bitcoin/pull/18334/commits/fa156999695ddaeb016d8320bee62f8d96679d55), that has already a callback implemented which requests filtered blocks as reply to inv
messages:
0 # add this lines after filtered P2P connection is added to the node, but before filter is set
1 filter_node.merkleblock_received = False
2 self.nodes[0].generatetoaddress(1, self.node[0].getnewaddress())[0]
3 filter_node.sync_with_ping()
4 assert not filter_node.merkleblock_received
The assertion fails, meaning that a merkleblock was indeed received.
Possible solution
Looking at the code in net_processing.cpp
/net.h
, it is quite obvious why this happens: the condition to answer to a filtered block request is just whether the (unique) pointer pfrom->m_tx_relay->pfilter
is set, and this condition is always true. Initially the pointer is set in the constructor of the struct TxRelay
, pointing to a default-constructed instance of CBloomFilter
:
https://github.com/bitcoin/bitcoin/blob/d52ba21dfff99173abb927bc964ce7ceb711d789/src/net.h#L812
And after receiving a filterclear
message it is also reset to an empty instance again:
https://github.com/bitcoin/bitcoin/blob/d52ba21dfff99173abb927bc964ce7ceb711d789/src/net_processing.cpp#L3201
In the following code, the boolean variable sendMerkleBlock
will thus always be set to true:
https://github.com/bitcoin/bitcoin/blob/d52ba21dfff99173abb927bc964ce7ceb711d789/src/net_processing.cpp#L1500-L1508
A simple solution would be to just set pfilter
to nullptr
initially and after receiving a filterclear
message. The question is if its worth the change, as the described behaviour is IMHO not harmful at all (it just doesn’t fit the description in the BIP37 document, which could be adapted?) and connection bloom filters are deprecated anway and not recommended to be used due to privacy concerns. It is noteworthy though that possibly other parts in net_processing.cpp
assume the test on pfilter
means “a bloom filter has been set”, and the fact that this always evaluates to true
could possibly lead to some unintended behaviour. For example, in the following code part, where a NodeEvictionCandidate
is created, its member fBloomFilter
will always be set to true:
https://github.com/bitcoin/bitcoin/blob/d52ba21dfff99173abb927bc964ce7ceb711d789/src/net.cpp#L864-L875