Skip to content
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] Reduce addr blackholes #21528

Merged
merged 8 commits into from Aug 3, 2021

Conversation

amitiuttarwar
Copy link
Contributor

@amitiuttarwar amitiuttarwar commented Mar 25, 2021

This PR builds on the test refactors extracted into #22306 (first 5 commits).

This PR aims to reduce addr blackholes. When we receive an addr message that contains 10 or less addresses, we forward them to 1-2 peers. This is the main technique we use for self advertisements, so sending to peers that wouldn't relay would effectively "blackhole" the trickle. Although we cannot prevent this in a malicious case, we can improve it for the normal, honest cases, and reduce the overall likelihood of occurrence. Two known cases where peers would not participate in addr relay are if they have connected to you as a block-relay-only connection, or if they are a light client.

This implementation defers initialization of m_addr_known until it is needed, then uses its presence to decide if the peer is participating in addr relay. For outbound (not block-relay-only) peers, we initialize the filter before sending the initial self announcement when processing their version message. For inbound peers, we initialize the filter if/when we get an addr related message (ADDR, ADDRV2, GETADDR). We do NOT initialize the filter based on a SENDADDRV2 message.

To communicate about these changes beyond bitcoin core & to (try to) ensure that no other software would be disrupted, I have:

@fanquake fanquake added the P2P label Mar 25, 2021
src/net_processing.cpp Outdated Show resolved Hide resolved
@DrahtBot
Copy link
Contributor

DrahtBot commented Mar 25, 2021

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

Reviewers, 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.

Copy link
Contributor

@jnewbery jnewbery left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK. Thanks for implementing this!

You've introduced a tiny thread safety issue by accessing m_addr_known from multiple threads without a lock. Y'know, this'd be much tidier if the addr data was all contained in net_processing 😁

src/net.cpp Outdated Show resolved Hide resolved
src/net_processing.cpp Outdated Show resolved Hide resolved
src/net_processing.cpp Outdated Show resolved Hide resolved
@jnewbery
Copy link
Contributor

I think that an approach like this was first suggested by AJ in #15759 (comment):

I think since we AdvertiseLocal regularly to our non-block-relay peers, we could just set a flag on the peer if we've ever seen an ADDR message from them, and only choose nodes with that flag in RelayAddress?

I think it's a good idea, since it stops us relaying addrs to both inbound block-relay-only peers and other kinds of black holes.

@amitiuttarwar
Copy link
Contributor Author

amitiuttarwar commented Mar 30, 2021

fixed tests & added a test. patch is ready, but leaving as a draft until I gain more confidence that this would not break any other software on the network.

compiling a list of other clients & compatibility with this patch:

breadwallet

Sends getaddr & only accepts addr messages afterwards (link)

btcd

✅ Confirmed by @Roasbeef (issue)

After connecting to a new outbound peer, sends addr self advertisement, and sometimes sends getaddr. Relevant code linked in the issue.

libbitcoin

✅ Confirmed by @evoskuil (issue)

When connecting to an outbound peer, sends a getaddr message.

bitcoin knots

Same behavior as Bitcoin Core, upon receiving a version message, sends a getaddr to outbound connections unless they are block-relay only, usually also self-advertises. (link)

bcoin

✅ Confirmed by @pinheadmz (issue).

Upon opening a connection, node will self-advertise & usually send a getaddr message (link).

gocoin

✅ Confirmed by @piotrnar (issue)

Upon receipt of a version message, node will self-advertise. Node will usually also send a getaddr message to peers.

bitcore

✅ Confirmed by @micahriggan (issue)

Bitcore nodes typically have dedicated bitcoin node(s) and the addresses are passed in via config.

bitcoinj

✅ (issue)

As part of connecting to a peer, the node will send a `getaddr` message on the link.

src/net_processing.cpp Outdated Show resolved Hide resolved
Copy link
Member

@glozow glozow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK to making an effort not to send addrs to peers that will likely ignore them. By "disrupt other software on the network," is the concern that some nodes who aren't immediately active in addr-related dialogue might be skipped over and not hear about addrs?

At first I was confused because I figured a lot of nodes don't care about advertising their own addr (e.g. bootstrapping, in IBD, maybe not accepting inbounds). But it seems like a node making a full relay outbound will always send its own addr and a getaddr, and should thus immediately be a candidate for getting addrs forwarded to it (is this accurate?). Was there a version of Bitcoin Core that didn't send addr stuff after getting VERSION from outbound?

src/net.h Outdated Show resolved Hide resolved
test/functional/p2p_addr_relay.py Outdated Show resolved Hide resolved
@@ -3569,6 +3584,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
pfrom.fSentAddr = true;

SetupAddressRelay(pfrom);

pfrom.vAddrToSend.clear();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side question: why do we clear this list here instead of appending to it, given that PushAddress and SendMessages filter for duplicates?

@sipa
Copy link
Member

sipa commented Mar 31, 2021

@glozow The concern would be other Bitcoin node implementations which don't send addr/getaddr, but do care about receiving them. I don't think that's particularly likely, but it's also hard to be sure there are none.

@ivanacostarubio
Copy link

Concept ACK

src/net.h Outdated Show resolved Hide resolved
@GeneFerneau
Copy link

Concept ACK

Need to review surrounding code more, and run the tests. Looks good so far!

maflcko pushed a commit to maflcko/bitcoin-core that referenced this pull request Apr 26, 2021
a732ee3 [test] Add tests for addr relay in -blocksonly mode (Amiti Uttarwar)
a6694ea [test] Add address relay tests involving outbound peers (Martin Zumsande)
8188b77 [test] Add tests for getaddr behavior (Martin Zumsande)
d2dbfe6 [test] Extract sending an addr message into a helper (Amiti Uttarwar)
c991943 [test] Refactor the addr relay test to prepare for new tests (Amiti Uttarwar)

Pull request description:

  This extends the functional test `p2p_addr_relay.py`.
  It adds test coverage for address relay involving outbound peers, tests for both outgoing and incoming `GETADDR` requests and tests for `-blocksonly` mode.

  The initial refactors and some of the new tests were taken from Amiti Uttarwar's PR bitcoin#21528 - they are general test improvements not directly tied to the change proposed there.

ACKs for top commit:
  amitiuttarwar:
    re-ACK a732ee3, small diff based on code review
  MarcoFalke:
    Concept ACK a732ee3 🌊

Tree-SHA512: e80d52683808ddd6b948a5134239f002f3fecf61b60e187877b07be6251721fde847104e495c75a1a5133a09c0b41a9255a0bec82932c0b304b516fa89bce33e
@jnewbery
Copy link
Contributor

jnewbery commented Jun 9, 2021

Reiterating my concept ACK on this. Reducing the number of addr black holes seems like a good win for addr gossipping across the network.

This branch needs rebase now that the addr data has been moved up into net_processing. I'd be very happy to help with that if you need a hand.

@ajtowns
Copy link
Contributor

ajtowns commented Jun 9, 2021

Reiterating my concept ACK on this. Reducing the number of addr black holes seems like a good win for addr gossipping across the network.

I think this was somewhat pending being sure it wouldn't break bitcore (see bitpay/bitcore#3140) which now looks like it's now resolved.

@amitiuttarwar
Copy link
Contributor Author

Reiterating my concept ACK on this

Thanks!

I think this was somewhat pending being sure it wouldn't break bitcore (see bitpay/bitcore#3140) which now looks like it's now resolved.

Indeed. I'm now working to rebase & bring this PR out of draft. I've rebased locally but am working through some test failures, I hope to have this ready for review by early next week.

PS: I also investigated bitcoinj & opened an issue on their repo, they also seem compatible 🎉.

Use SetupAddressRelay to only initialize `m_addr_known` as needed. For outbound
peers, we initialize the filter before sending our self announcement (not
applicable for block-relay-only connections). For inbound peers, we initialize
the filter when we get an addr related message (ADDR, ADDRV2, GETADDR).

These changes intend to mitigate address blackholes. Since an inbound peer has
to send us an addr related message to become eligible as a candidate for addr
relay, this should reduce our likelihood of sending them self-announcements.
Now that we have a simple boolean stored on the field, the wrapper function is
no longer necessary.
This test checks that we only relay addresses with inbound peers who have sent
us an addr related message. Uses a combination of GETADDR and ADDR to verify
when peers are eligible.
@amitiuttarwar
Copy link
Contributor Author

thanks @MarcoFalke, addressed your comments

@jnewbery
Copy link
Contributor

reACK 3f7250b

Only changes are addressing review comments from Marco.

@glozow
Copy link
Member

glozow commented Jul 30, 2021

ACK 3f7250b

Compared the SetupAddressRelay() call sites to where I thought they should be, threw in some asserts where I expected m_addr_relay_enabled to be true, did a code review looking for possibilities of m_addr_known being used before initialized. Test looks correct. Thanks for diligently checking for compatibility with other clients, seems to be a safe change. :)

@fanquake fanquake requested a review from ajtowns August 2, 2021 02:21
Copy link
Contributor

@ajtowns ajtowns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK 3f7250b

I don't think the potential interaction with #22387 should be an issue -- if the rate limit wasn't being hit before it was because addresses weren't being relayed, and if they're hit now the result is just that addresses aren't being relayed.

src/net_processing.cpp Show resolved Hide resolved
@@ -167,6 +179,9 @@ def relay_tests(self):
# of the outbound peer which is often sent before the GETADDR response.
assert_equal(inbound_peer.num_ipv4_received, 0)

# Send an empty ADDR message to intialize address relay on this connection.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/intialize/initialize

@fanquake fanquake merged commit 06788c6 into bitcoin:master Aug 3, 2021
@amitiuttarwar amitiuttarwar deleted the 2021-03-addr-defer2 branch August 3, 2021 01:50
@maflcko
Copy link
Member

maflcko commented Aug 3, 2021

Maybe add a line to the release notes saying that inbound peers will never receive address messages unless they send ADDR, ADDRV2, or GETADDR themselves?

@@ -11,14 +11,15 @@
NODE_NETWORK,
NODE_WITNESS,
msg_addr,
msg_getaddr
msg_getaddr,
msg_verack
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Would be nice to always add a trailing comma to avoid ugly diffs and keeping the git blame depth lower in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

noted

amitiuttarwar added a commit to amitiuttarwar/bitcoin that referenced this pull request Aug 3, 2021
@amitiuttarwar
Copy link
Contributor Author

amitiuttarwar commented Aug 3, 2021

@MarcoFalke

Maybe add a line to the release notes

Added in #22618, thanks :)

amitiuttarwar added a commit to amitiuttarwar/bitcoin that referenced this pull request Aug 3, 2021
And fix a typo in the test.
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Aug 4, 2021
amitiuttarwar added a commit to amitiuttarwar/bitcoin that referenced this pull request Aug 4, 2021
And fix a typo in the test.
maflcko pushed a commit to bitcoin-core/gui that referenced this pull request Aug 5, 2021
9778b0f [net_processing] Provide debug error if code assumptions change. (Amiti Uttarwar)
aa79c91 [docs] Add release notes for #21528 (Amiti Uttarwar)

Pull request description:

  Adds a release note & addresses [this](bitcoin/bitcoin#21528 (comment)) review comment to make expectations more explicit.

ACKs for top commit:
  Zero-1729:
    re-ACK 9778b0f
  jonatack:
    ACK 9778b0f

Tree-SHA512: 9507df5f2746d05c6df8c86b7a19364610ebfafc81af7650be7e68d7536a0685cce9fd2e5f287ef92b6245c584f8875b24a958109ba5bd8acf3c8fc9fd19eef2
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Jan 28, 2022
Summary:
```
This PR aims to reduce addr blackholes. When we receive an addr message that contains 10 or less addresses, we forward them to 1-2 peers. This is the main technique we use for self advertisements, so sending to peers that wouldn't relay would effectively "blackhole" the trickle. Although we cannot prevent this in a malicious case, we can improve it for the normal, honest cases, and reduce the overall likelihood of occurrence. Two known cases where peers would not participate in addr relay are if they have connected to you as a block-relay-only connection, or if they are a light client.

This implementation defers initialization of m_addr_known until it is needed, then uses its presence to decide if the peer is participating in addr relay. For outbound (not block-relay-only) peers, we initialize the filter before sending the initial self announcement when processing their version message. For inbound peers, we initialize the filter if/when we get an addr related message (ADDR, ADDRV2, GETADDR). We do NOT initialize the filter based on a SENDADDRV2 message.
```

Backport of [[bitcoin/bitcoin#21528 | core#21528]].

Ref T1696.

Test Plan:
  ninja all check-all

Reviewers: #bitcoin_abc, tyler-smith

Reviewed By: #bitcoin_abc, tyler-smith

Subscribers: tyler-smith

Maniphest Tasks: T1696

Differential Revision: https://reviews.bitcoinabc.org/D10933
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Aug 18, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet