-
Notifications
You must be signed in to change notification settings - Fork 36.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
p2p: Disconnect peer that send us tx INVs when we opted out of tx relay #16682
p2p: Disconnect peer that send us tx INVs when we opted out of tx relay #16682
Conversation
Observed while testing #15759 and suggested by @ajtowns : #15759 (comment) |
tACK 0e7bc2f |
Can you check for the |
|
Why would spy nodes claim to be blocksonly? To not get asked for txs? |
Sorry, my explanation was unclear. Let me try again:
Explanations:
The peers that I found that do this are on the spylist here: https://people.xiph.org/~greg/banlist.cli.txt |
I think the nomenclature is a bit messed up. The pull request title should probably read "non-tx-relay" instead of "blocks-only"? Or "Disconnect peer that send us tx INVs when we opted out of tx relay" |
I have negative feelings about this as it violates the robustness principle. If at some point in the future there is a bug which causes us to relay an occasional tx, it ends up disconnecting blocks only peers. |
The main reason to run I think your argument could be made against any disconnect behaviour: if at some point in the future there's a bug that makes us send a bad message to a peer, then we might get disconnected. |
I don't agree. The recent blocksonly changes as presented in #15759 (comment) is as a topology privacy benefit. Modifying this logic doesn't just affect nodes running with blocksonly with that change in mind, it affects all nodes. (I'm not sure if you intend this PR to be standalone from that one or not) I think the right thing to do would be, for example, to downgrade the connection to a non-blocksonly connection rather than disconnect and try finding a new blocksonly peer. Disagreement nonwithstanding, IIRC there is software which will send unsolicited TX messages as well, so you may want to look into handling peers who send those as well. |
0e7bc2f
to
6f4151f
Compare
Correct, but it only affects the two outbound blocks-only peers in that PR. The new behaviour would be to disconnect those blocks-only peers that are misbehaving and make new blocks-only peers. Given that the part of the justification for that PR was that the additional blocks-only peers would not cause too much additional resource usage, I think it's reasonable to disconnect them if they're wasting our bandwidth
I disagree:
Thanks! I've updated this PR to also ban peers which relay TX messages to us when we've set relay=False. |
6f4151f
to
ae63975
Compare
Only a quick github review, but looks fine to me; maybe mark blocksonly peers as misbehaving if they send a TX/WTX GETDATA as well though? Oh, per travis you need to hold cs_main before calling Misbehaving in the NetMsgType::TX case. I think I'd be more confident that this was a safe change to make if we merged #15759 first and could look through our logs to see how often the two blocks-only peers of any random node see this sort of misbehaviour? I think as much as avoiding wasting bandwidth, there's an argument for this sort of change from the POV of tolerating protocol misbehaviour isn't good for the quality of the software ecosystem, a la https://tools.ietf.org/html/draft-iab-protocol-maintenance-01 . Even if that only results in higher quality spy nodes, I guess that's still an improvement... Alternatively/longer-term, we could perhaps extend
Though even that only protects you from disconnecting your outbound nodes, it doesn't prevent them from disconnecting you as an inbound node if your client is misbehaving. But if it's your client misbehaving at least you have some chance of being able to fix that directly. Could protect some of your inbound nodes too, I guess, but that doesn't seem very robust to adversarial behaviour. |
Concept ACK - the discussion here makes sense to me, but I'm not as qualified as @ajtowns / @sdaftuar etc to comment on this. This is failing to build on Travis macOS: net_processing.cpp:2476:13: error: calling function 'Misbehaving' requires holding mutex 'cs_main' exclusively [-Werror,-Wthread-safety-analysis]
Misbehaving(pfrom->GetId(), 100, strprintf("Blocks-only peer %d sent us transaction in violation of protocol\n", pfrom->GetId()));
^
net_processing.cpp:2476:13: error: calling function 'Misbehaving' requires holding mutex 'cs_main' exclusively [-Werror,-Wthread-safety-analysis]
2 errors generated.
Makefile:7563: recipe for target 'libbitcoin_server_a-net_processing.o' failed |
I think the bandwidth exhaustion argument is weak given that there's still numerous other messages (e.g., ping, pong, notfound, etc) which can be sent in blocksonly mode (unless you plan to block those too?). I'd much rather think about bandwidth issues as a more general concept and actually track the bandwidth usage from a node and disconnect if it exceeds a limit of 'not useful' data over a certain period... We can track the This is more of a whitelist approach (we count how much good stuff we're getting) v.s. a blacklist approach counting INVs as being bad while ignoring PINGs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concept ACK
src/net_processing.cpp
Outdated
@@ -2254,7 +2254,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr | |||
{ | |||
pfrom->AddInventoryKnown(inv); | |||
if (fBlocksOnly) { | |||
LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->GetId()); | |||
Misbehaving(pfrom->GetId(), 100, strprintf("Blocks-only peer %d sent us transaction inv (%s) in violation of protocol\n", pfrom->GetId(), inv.hash.ToString())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The peer isn't blocks-only, we are...
ae63975
to
46caa27
Compare
Fixed.
Agreed. This interacts with #15759 and it's more important for that to get in, so I'll mark this as WIP for now.
Done.
I don't think this small, focused fix precludes us from doing anything smarter long term. Happy to have those discussions, but I think this PR should be judged on whether it makes a small, incremental improvement (which I argue it does). |
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
46caa27
to
5ff415d
Compare
#15759 implemented parts of this, tough I think a more wholesale cleanup of this logic is still required. |
bac862c
to
cff3323
Compare
Rebased on master, which makes the first two commits test-only changes. Note the test change in the final commit. Previously, there was a test "Check that txs from rpc are not rejected and relayed to other peers". This is no longer possible, since when the node sends an INV to its peer, it'll be disconnected. Even if it isn't disconnected and the peer responds with a TX GETDATA, then the node will disconnect its peer for sending that GETDATA. |
Commit message for first commit needs to be updated to reflect it's not a behaviour change anymore. Worth updating the test framework so we can have python-p2p nodes as block-relay-only outbound connections rather than just inbound connections as part of this PR? There's old commits from luke-jr that update the test framework in #10593 and I had an independant go at the same idea at https://github.com/ajtowns/bitcoin/commits/201909-p2poutbound. I'm not sure the "txs from rpc not rejected and are relayed" change makes sense -- this means you can't use a |
txid = self.nodes[0].testmempoolaccept([sigtx])[0]['txid'] | ||
with self.nodes[0].assert_debug_log(['received getdata for: tx {} peer=1'.format(txid)]): | ||
self.nodes[0].sendrawtransaction(sigtx) | ||
self.nodes[0].p2p.wait_for_tx(txid) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW: after this removal wait_for_tx
is no longer used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've made some change to this PR, so wait_for_tx
is still used.
cff3323
to
b2741ec
Compare
Done. I thought the commit log gave more useful information than commit 0ba0802 that was merged, but you're right that it doesn't belong on this test-change-only commit.
I think that's definitely worth doing, but it doesn't have to be as part of this PR. I'll happily review that change.
I've changed this so we only disconnect if the tx is not found in our mapRelay. Is that what you meant? |
b2741ec
to
dc84c42
Compare
The macos linter is complaining about a commit that isn't part of this PR and I have no idea why. Rebasing on master to try to resolve. |
I think the linter should be removed when it is causing issues. It was added only for experimentation. |
Btcd or Lnd also does this, and I noticed this while opening a channel using Lnd that was connected to my local The macOS linter is being removed in #17176 |
@Sjors how is a client mean to detect that a backing node is on blocks only mode on the RPC level? On the p2p end, iirc there's no node/p2p level signalling so nodes have no idea if they're meant to relay to another node or not. |
@Roasbeef p2p nodes can use the |
TIL! Fixed in lightninglabs/neutrino#190 |
src/net_processing.cpp
Outdated
@@ -1542,6 +1542,14 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm | |||
} | |||
} | |||
if (!push) { | |||
if (!g_relay_txes && !pfrom->HasPermission(PF_RELAY)) { | |||
// If a blocks-only peer requests a tx and its not in our mapRelay, then disconnect them. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But we'll have already tried relaying from the mempool and set push to true in that case, causing this code path to only work if we didn't have the tx at all? Am I missing something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no, you're not missing anything. You're completely right.
I think this logic needs to be moved up between the if (mi != mapRelay.end())
block and else if (pfrom->m_tx_relay->m_last_mempool_req.load().count())
. I've done that in https://github.com/bitcoin/bitcoin/compare/dc84c42a69557bff3d41baea498ceee822566423..dffa26f05fca414ecd2e72c6c9a7efc953d54651. Let me know what you think.
dc84c42
to
dffa26f
Compare
… us tx INVs This commit restructures the p2p_blocksonly.py test case in preparation for adding more tests. In future commits, we'll test for disconnecting blocks-only peers that send us TX messages or tx GETDATA messages.
If a blocks-only peer sends us a TX message, we should disconnect. Add a test for this.
Disconnect any blocks-only peer that sends us tx/witness tx GETDATAs for a tx that isn't in our mapRelay. We continue to allow GETDATAs for txs in our mapRelay so that transactions submitted locally (by RPC/wallet) can still be relayed over blocks-only links.
dffa26f
to
1c6fb2d
Compare
That change caused a conflict with master so I've rebased. |
// If a blocks-only peer requests a tx and it isn't in our mapRelay, then disconnect them. | ||
// We allow blocks-only peers to request txs in our mapRelay so we can relay txs submitted | ||
// locally when in blocks-only mode. | ||
LogPrintf("Blocks-only peer %d sent us tx GETDATA in violation of protocol\n", pfrom->GetId()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this is incorrect -- it should not be a protocol violation if we announce a transaction to a peer and it happens to wait more than 15 minutes to respond with a GETDATA. This already happens for reasons that are benign (due to us not immediately requesting a transaction from all peers who announce it).
For example: one way this can happen is if we announce a transaction to a peer, but it gets the announcement as well from other peers first and queues up a request to go to us far in the future, and in the meantime a block is found that includes the transaction as well as spends of its outputs, and then at some point when it's time for the peer to consider requesting the tx from us it has at that point forgotten about the tx completely and it'll re-request. (This type of behavior is present in all the version of Bitcoin Core I can remember.)
I think to do this right we would need to either track the tx's we've announced to a peer, or consider changing blocks-only behavior so that we never announce anything at all (which makes more logical sense to me, but I think others disagree, so probably a non-starter).
ok, closing this. #15759 took the more important changes from this PR (disconnecting peers that send us TXs and tx INVs), and we can't seem to reach agreement on the remaining change:
|
-blocksonly
mode was introduced in4044f07 and allows nodes to request
that their peers don't relay txs to them. This is done by setting the
'relay' field in the VERSION message (introduced in BIP37) to false.
Tx INVs received from peers when running in blocksonly mode previously
resulted in a log message "transaction inv sent in violation of
protocol". When running in -blocksonly, it has been observed that
several peers advertising as Satoshi:0.18.0 were persistently sending us
tx INVs in violation of the protocol. These are suspected of being spy
nodes.
Change the behaviour to disconnect nodes that send us tx INVs after
we've requested no tx relay.