Skip to content

Commit 7a57674

Browse files
committed
Merge #18808: [net processing] Drop unknown types in getdata
9847e20 [docs] Improve commenting in ProcessGetData() (John Newbery) 2f03255 [test] test that an invalid GETDATA doesn't prevent processing of future messages (Amiti Uttarwar) e257cf7 [net processing] ignore unknown INV types in GETDATA messages (Amiti Uttarwar) 047ceac [net processing] ignore tx GETDATA from blocks-only peers (Amiti Uttarwar) Pull request description: Currently we'll stall peers that send us an unknown INV type in a GETDATA message. Be a bit more friendly and just drop the invalid request. Ditto for blocks-relay-only peers that send us a GETDATA for a transaction. There's a test for the first part. The second is difficult to test in the functional test framework since we aren't able to make blocks-relay-only connections. ACKs for top commit: sipa: utACK 9847e20 brakmic: ACK bitcoin/bitcoin@9847e20 luke-jr: utACK 9847e20 naumenkogs: utACK 9847e20 ajtowns: utACK 9847e20 Tree-SHA512: 6007f2fd839ffe737727f6fb8e8f083b2d9e05a510748f1d40b8f9be8fdf7b5419a36d8f1039923eec1ba2983e8f6f0436ec5fc196d9f6dcb0657f2ff8ff8e4c
2 parents eb2ffbb + 9847e20 commit 7a57674

File tree

3 files changed

+74
-18
lines changed

3 files changed

+74
-18
lines changed

src/net_processing.cpp

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1612,26 +1612,32 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
16121612
std::vector<CInv> vNotFound;
16131613
const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
16141614

1615-
// Note that if we receive a getdata for a MSG_TX or MSG_WITNESS_TX from a
1616-
// block-relay-only outbound peer, we will stop processing further getdata
1617-
// messages from this peer (likely resulting in our peer eventually
1618-
// disconnecting us).
1619-
if (pfrom->m_tx_relay != nullptr) {
1620-
// mempool entries added before this time have likely expired from mapRelay
1621-
const std::chrono::seconds longlived_mempool_time = GetTime<std::chrono::seconds>() - RELAY_TX_CACHE_TIME;
1622-
const std::chrono::seconds mempool_req = pfrom->m_tx_relay->m_last_mempool_req.load();
1615+
// mempool entries added before this time have likely expired from mapRelay
1616+
const std::chrono::seconds longlived_mempool_time = GetTime<std::chrono::seconds>() - RELAY_TX_CACHE_TIME;
1617+
// Get last mempool request time
1618+
const std::chrono::seconds mempool_req = pfrom->m_tx_relay != nullptr ? pfrom->m_tx_relay->m_last_mempool_req.load()
1619+
: std::chrono::seconds::min();
16231620

1621+
{
16241622
LOCK(cs_main);
16251623

1624+
// Process as many TX items from the front of the getdata queue as
1625+
// possible, since they're common and it's efficient to batch process
1626+
// them.
16261627
while (it != pfrom->vRecvGetData.end() && (it->type == MSG_TX || it->type == MSG_WITNESS_TX)) {
16271628
if (interruptMsgProc)
16281629
return;
1629-
// Don't bother if send buffer is too full to respond anyway
1630+
// The send buffer provides backpressure. If there's no space in
1631+
// the buffer, pause processing until the next call.
16301632
if (pfrom->fPauseSend)
16311633
break;
16321634

1633-
const CInv &inv = *it;
1634-
it++;
1635+
const CInv &inv = *it++;
1636+
1637+
if (pfrom->m_tx_relay == nullptr) {
1638+
// Ignore GETDATA requests for transactions from blocks-only peers.
1639+
continue;
1640+
}
16351641

16361642
// Send stream from relay memory
16371643
bool push = false;
@@ -1665,19 +1671,17 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
16651671
}
16661672
} // release cs_main
16671673

1674+
// Only process one BLOCK item per call, since they're uncommon and can be
1675+
// expensive to process.
16681676
if (it != pfrom->vRecvGetData.end() && !pfrom->fPauseSend) {
1669-
const CInv &inv = *it;
1677+
const CInv &inv = *it++;
16701678
if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK) {
1671-
it++;
16721679
ProcessGetBlockData(pfrom, chainparams, inv, connman);
16731680
}
1681+
// else: If the first item on the queue is an unknown type, we erase it
1682+
// and continue processing the queue on the next call.
16741683
}
16751684

1676-
// Unknown types in the GetData stay in vRecvGetData and block any future
1677-
// message from this peer, see vRecvGetData check in ProcessMessages().
1678-
// Depending on future p2p changes, we might either drop unknown getdata on
1679-
// the floor or disconnect the peer.
1680-
16811685
pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it);
16821686

16831687
if (!vNotFound.empty()) {

test/functional/p2p_getdata.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2020 The Bitcoin Core developers
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
"""Test GETDATA processing behavior"""
6+
from collections import defaultdict
7+
8+
from test_framework.messages import (
9+
CInv,
10+
msg_getdata,
11+
)
12+
from test_framework.mininode import (
13+
mininode_lock,
14+
P2PInterface,
15+
)
16+
from test_framework.test_framework import BitcoinTestFramework
17+
from test_framework.util import wait_until
18+
19+
class P2PStoreBlock(P2PInterface):
20+
21+
def __init__(self):
22+
super().__init__()
23+
self.blocks = defaultdict(int)
24+
25+
def on_block(self, message):
26+
message.block.calc_sha256()
27+
self.blocks[message.block.sha256] += 1
28+
29+
class GetdataTest(BitcoinTestFramework):
30+
def set_test_params(self):
31+
self.num_nodes = 1
32+
33+
def run_test(self):
34+
self.nodes[0].add_p2p_connection(P2PStoreBlock())
35+
36+
self.log.info("test that an invalid GETDATA doesn't prevent processing of future messages")
37+
38+
# Send invalid message and verify that node responds to later ping
39+
invalid_getdata = msg_getdata()
40+
invalid_getdata.inv.append(CInv(t=0, h=0)) # INV type 0 is invalid.
41+
self.nodes[0].p2ps[0].send_and_ping(invalid_getdata)
42+
43+
# Check getdata still works by fetching tip block
44+
best_block = int(self.nodes[0].getbestblockhash(), 16)
45+
good_getdata = msg_getdata()
46+
good_getdata.inv.append(CInv(t=2, h=best_block))
47+
self.nodes[0].p2ps[0].send_and_ping(good_getdata)
48+
wait_until(lambda: self.nodes[0].p2ps[0].blocks[best_block] == 1, timeout=30, lock=mininode_lock)
49+
50+
if __name__ == '__main__':
51+
GetdataTest().main()

test/functional/test_runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@
157157
'rpc_deprecated.py',
158158
'wallet_disable.py',
159159
'p2p_addr_relay.py',
160+
'p2p_getdata.py',
160161
'rpc_net.py',
161162
'wallet_keypool.py',
162163
'wallet_keypool.py --descriptors',

0 commit comments

Comments
 (0)