This PR fixes #18483. On the master branch, there is currently always a BIP37 filter set for every peer: if not a specific filter is set through a filterload message, a default match-everything filter is instanciated and pointed to via the CBloomFilter default constructor; that happens both initially, when the containing structure TxRelay is constructed:
https://github.com/bitcoin/bitcoin/blob/c0b389b33516fb3eaaad7c30bd11dba768882a7e/src/net.h#L812
and after a loaded filter is removed again through a filterclear message:
The behaviour was introduced by commit https://github.com/bitcoin/bitcoin/commit/37c6389c5a0ca63ae3573440ecdfe95d28ad8f07 (an intentional covert fix for CVE-2013-5700, according to gmaxwell).
This default match-everything filter leads to some unintended side-effects:
getdatarequest for filtered blocks (i.e. typeMSG_FILTERED_BLOCK) are always responded to withmerkleblocks, even if no filter was set by the peer, see issue #18483 (strictly speaking, this is a violation of BIP37) https://github.com/bitcoin/bitcoin/blob/c0b389b33516fb3eaaad7c30bd11dba768882a7e/src/net_processing.cpp#L1504-L1507- if a peer sends a
filteraddmessage without having loaded a filter viafilterloadbefore, the intended increasing of the banscore never happens (triggered ifbadis set to true, a few lines below) https://github.com/bitcoin/bitcoin/blob/c0b389b33516fb3eaaad7c30bd11dba768882a7e/src/net_processing.cpp#L3182-L3186
This PR basically activates the else-branch code paths for all checks of pfilter again (on the master branch, they are dead code) by limiting the pointer's lifespan: instead of always having a filter set, the pfilter is only pointing to a CBloomFilter-instance after receiving a filterload message and the instance is destroyed again (and the pointer nullified) after receiving a filterclear message.
Here is a before/after comparison in behaviour:
| code part / scenario | master branch | PR branch |
|---|---|---|
getdata processing for MSG_FILTERED_BLOCK |
always responds with merkleblock |
only responds if filter was set via filterload |
filteradd processing, no filter was loaded |
nothing | peer's banscore increases by 100 (i.e. disconnect) |
On the other code parts where pfilter is checked there is no change in the logic behaviour (except that CBloomFilter::IsRelevantAndUpdate() is unnecessarily called and immediately returned in the master branch).
Note that the default constructor of CBloomFilter is only used for deserializing the received filterload message and nowhere else. The PR also contains a functional test checking that sending getdata for filtered blocks is ignored by the node if no bloom filter is set.