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

Syncing headers with feeler-peers #16859

Open
sdaftuar opened this issue Sep 12, 2019 · 6 comments
Open

Syncing headers with feeler-peers #16859

sdaftuar opened this issue Sep 12, 2019 · 6 comments

Comments

@sdaftuar
Copy link
Member

It occurred to me that we should eliminate the distinction between feelers -- peers we connect to on average every couple minutes to test entries in addrman -- and the extra outbound peers we connect to if our tip is stale (#11560). Instead, why not just sync headers with feeler connections, and if we happen to learn of a new block from that feeler when our tip had been stale, consider evicting an older connection (just as we do with our extra outbounds)?

It seems to me that this should increase the cost to an adversary trying to eclipse a node, because they'd need to control a large fraction of the addrman in order to withhold the most-work chain for any sizeable amount of time.

Moreover since we're already making these feeler connections already, it should be a small amount of additional bandwidth to sync headers with such a peer. (Perhaps we should avoid doing this in IBD...?)

@EthanHeilman Any thoughts?

@practicalswift
Copy link
Contributor

Tentative Concept ACK: this seems like a low cost way to make eclipse attacks significantly harder to carry out. Nice idea!

Can we think of any drawbacks beyond the small amount of additional bandwidth?

What would be the pros/cons of keeping the connect-if-tip-is-stale logic unchanged and only adjust the feeler logic so that feelers sync headers?

@EthanHeilman
Copy link
Contributor

EthanHeilman commented Sep 17, 2019

I would break this into two features:

Feature one: syncing block headers from feelers:
I don't see any drawbacks to this and it makes eclipse attacks more detectable when all your regular connections are eclipsed but your feeler connections are not since your feelers will tell you about blocks which no one else will.

Feature two: evicting stale connections and replacing them with feeler connections:
I can see the advantages of doing this, however I worry that an attacker with mining power will exploit this eviction strategy to slowly take over your outgoing connections by making your existing outgoing connections stale. I'm not sure how possible this attack is in practice as I'm not familiar with the precise definition of what makes something a stale tip.

Maybe instead of evicting connections if you get a block header that none of your other peers know about download it from that feeler connection?

@naumenkogs
Copy link
Member

Concept ACK.

This is a trivial way to make eclipsing more expensive, and bandwidth overhead is small.

Perhaps instead of evicting we should try to tell our peers about this new block? What if they are not attacking us but are just eclipsed too.
Or at least send a signal to that node (I know, adding a new p2p message is maybe a big change for this use-case).

@Crypt-iQ
Copy link
Contributor

I'm a little confused how this increases eclipse cost -- can't an attacker just fill up the new table with "trash"? If the connect-if-tip-is-stale logic is removed then this could potentially backfire and never sync headers if the table is filled with garbage?

@gmaxwell
Copy link
Contributor

however I worry that an attacker with mining power will exploit this eviction strategy to slowly take over your outgoing connections

So long as the implementation is such that it accepts the block before disconnecting anyone all it would do is let the attacker disconnect essentially one honest peer each of a few nodes per new block they make at the tip, at a considerable cost of their block getting orphaned (unless they can prevent the orphaning with >50% hashpower).

This is because as soon as they relay their new block to you, it'll floodfill the honest network (assuming you're connected to it) and that same block won't be usable to cause any other eviction. This depends, however, on actually accepting the block before disconnecting anyone.

One simple way to implement that is that eviction could just eliminate outbound peer which least recently sent us a block we accepted (and if none have, the most recently connected). The block-denying-header-making attack would then do nothing interesting, as the new connection would be the one that got evicted even if we learned a header from it.

Suhas may have been assuming this because the existing stale tip eviction works that way... if the new stale tip peer didn't help us after all, it'll be the one that gets evicted.

I'm not familiar with the precise definition of what makes something a stale tip.

#11560

@EthanHeilman
Copy link
Contributor

I'm kinda thinking out loud here so I apologize for any important details I might have missed.

Thanks for that link, if I understand the code correctly after 30 minutes if no block has been announced to us our tip becomes stale.

static bool TipMayBeStale(const Consensus::Params &consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
    AssertLockHeld(cs_main);
    if (g_last_tip_update == 0) {
        g_last_tip_update = GetTime();
    }
    return g_last_tip_update < GetTime() - consensusParams.nPowTargetSpacing * 3 && mapBlocksInFlight.empty();
}

https://github.com/bitcoin/bitcoin/blob/master/src/net_processing.cpp#L570-L577

My understanding of the proposed behavior in this issue:

When we make a feeler connection to a node A and it has a new block AND our tip is stale, we evict one of our outgoing connections and replace the connection with node A. Assuming I didn't bork up the math the probability that no miner finds a block within 30 minutes is: 0.0497 = e^(-(1/600) * 3*600). That is, ~5% of the time a block is found it is found after 30 minutes. This should occur multiple times a day.

An attack to exploit this proposed behavior without mining power:

Assume an attacker that can get blocks faster than the rest of the p2p network, say by connecting directly to miners and relaying blocks without verifying all the transactions:

  1. Attacker has many nodes in different /16s.
  2. Attacker floods new table with attacker IP addresses.
  3. Each time a victim node makes a feeler connection to an attacker node AND the tip is stale AND a new block has just been announced the attacker has a chance to replace an outgoing connection.

Let's say a node makes a feeler connection and waits 90 seconds for a header reply (not sure what the actual time limit is). The probability that a block is announced in that 90 second period is: ~0.36 = 1- e^(-(1/600) * 3*90). Thus, conditioned on a feeler connection being made to an attacker node AND the tip being stale, the probability that a block is announced allowing an attacker to take over an outgoing connection is 36%.

This attack doesn't require restarts. A well connected attacker could just perform this attack against the entire network slowly getting more outgoing connections until it manages to fully eclipse a node. Attacker nodes would never be replaced as outgoing connections by non-attacker nodes because attacker nodes would always get the blocks first.

One positive factor is that while this many make eclipsing easier, it makes an eclipse position harder to exploit because once an attacker begins using their eclipse position to filter the view of a victim node, then the attacker is in danger of being replaced as an outgoing connection due to stale tips. Unfortunately this doesn't help much with eclipse attacks on lightning network participants because the attacker could give the victim node an unfiltered view of the Bitcoin's blockchain but just not rebroadcast breach remedy transactions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants