mirror of
https://github.com/tendermint/tendermint.git
synced 2026-09-20 23:14:37 +00:00
docs: add reactor sections (#6510)
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
---
|
||||
order: 1
|
||||
parent:
|
||||
title: Peer Exchange
|
||||
order: 5
|
||||
---
|
||||
|
||||
# Peer Strategy and Exchange
|
||||
|
||||
Here we outline the design of the PeerStore
|
||||
and how it used by the Peer Exchange Reactor (PEX) to ensure we are connected
|
||||
to good peers and to gossip peers to others.
|
||||
|
||||
## Peer Types
|
||||
|
||||
Certain peers are special in that they are specified by the user as `persistent`,
|
||||
which means we auto-redial them if the connection fails, or if we fail to dial
|
||||
them.
|
||||
Some peers can be marked as `private`, which means
|
||||
we will not put them in the peer store or gossip them to others.
|
||||
|
||||
All peers except private peers and peers coming from them are tracked using the
|
||||
peer store.
|
||||
|
||||
The rest of our peers are only distinguished by being either
|
||||
inbound (they dialed our public address) or outbound (we dialed them).
|
||||
|
||||
## Discovery
|
||||
|
||||
Peer discovery begins with a list of seeds.
|
||||
|
||||
When we don't have enough peers, we
|
||||
|
||||
1. ask existing peers
|
||||
2. dial seeds if we're not dialing anyone currently
|
||||
|
||||
On startup, we will also immediately dial the given list of `persistent_peers`,
|
||||
and will attempt to maintain persistent connections with them. If the
|
||||
connections die, or we fail to dial, we will redial every 5s for a few minutes,
|
||||
then switch to an exponential backoff schedule, and after about a day of
|
||||
trying, stop dialing the peer. This behavior is when `persistent_peers_max_dial_period` is configured to zero.
|
||||
|
||||
But If `persistent_peers_max_dial_period` is set greater than zero, terms between each dial to each persistent peer
|
||||
will not exceed `persistent_peers_max_dial_period` during exponential backoff.
|
||||
Therefore, `dial_period` = min(`persistent_peers_max_dial_period`, `exponential_backoff_dial_period`)
|
||||
and we keep trying again regardless of `maxAttemptsToDial`
|
||||
|
||||
As long as we have less than `MaxNumOutboundPeers`, we periodically request
|
||||
additional peers from each of our own and try seeds.
|
||||
|
||||
## Listening
|
||||
|
||||
Peers listen on a configurable ListenAddr that they self-report in their
|
||||
NodeInfo during handshakes with other peers. Peers accept up to
|
||||
`MaxNumInboundPeers` incoming peers.
|
||||
|
||||
## Address Book
|
||||
|
||||
Peers are tracked via their ID (their PubKey.Address()).
|
||||
Peers are added to the peer store from the PEX when they first connect to us or
|
||||
when we hear about them from other peers.
|
||||
|
||||
The peer store is arranged in sets of buckets, and distinguishes between
|
||||
vetted (old) and unvetted (new) peers. It keeps different sets of buckets for
|
||||
vetted and unvetted peers. Buckets provide randomization over peer selection.
|
||||
Peers are put in buckets according to their IP groups.
|
||||
|
||||
IP group can be a masked IP (e.g. `1.2.0.0` or `2602:100::`) or `local` for
|
||||
local addresses or `unroutable` for unroutable addresses. The mask which
|
||||
corresponds to the `/16` subnet is used for IPv4, `/32` subnet - for IPv6.
|
||||
Each group has a limited number of buckets to prevent DoS attacks coming from
|
||||
that group (e.g. an attacker buying a `/16` block of IPs and launching a DoS
|
||||
attack).
|
||||
|
||||
[highwayhash](https://arxiv.org/abs/1612.06257) is used as a hashing function
|
||||
when calculating a bucket.
|
||||
|
||||
When placing a peer into a new bucket:
|
||||
|
||||
```md
|
||||
hash(key + sourcegroup + int64(hash(key + group + sourcegroup)) % bucket_per_group) % num_new_buckets
|
||||
```
|
||||
|
||||
When placing a peer into an old bucket:
|
||||
|
||||
```md
|
||||
hash(key + group + int64(hash(key + addr)) % buckets_per_group) % num_old_buckets
|
||||
```
|
||||
|
||||
where `key` - random 24 HEX string, `group` - IP group of the peer (e.g. `1.2.0.0`),
|
||||
`sourcegroup` - IP group of the sender (peer who sent us this address) (e.g. `174.11.0.0`),
|
||||
`addr` - string representation of the peer's address (e.g. `174.11.10.2:26656`).
|
||||
|
||||
A vetted peer can only be in one bucket. An unvetted peer can be in multiple buckets, and
|
||||
each instance of the peer can have a different IP:PORT.
|
||||
|
||||
If we're trying to add a new peer but there's no space in its bucket, we'll
|
||||
remove the worst peer from that bucket to make room.
|
||||
|
||||
## Vetting
|
||||
|
||||
When a peer is first added, it is unvetted.
|
||||
Marking a peer as vetted is outside the scope of the `p2p` package.
|
||||
For Tendermint, a Peer becomes vetted once it has contributed sufficiently
|
||||
at the consensus layer; ie. once it has sent us valid and not-yet-known
|
||||
votes and/or block parts for `NumBlocksForVetted` blocks.
|
||||
Other users of the p2p package can determine their own conditions for when a peer is marked vetted.
|
||||
|
||||
If a peer becomes vetted but there are already too many vetted peers,
|
||||
a randomly selected one of the vetted peers becomes unvetted.
|
||||
|
||||
If a peer becomes unvetted (either a new peer, or one that was previously vetted),
|
||||
a randomly selected one of the unvetted peers is removed from the peer store.
|
||||
|
||||
More fine-grained tracking of peer behaviour can be done using
|
||||
a trust metric (see below), but it's best to start with something simple.
|
||||
|
||||
## Select Peers to Dial
|
||||
|
||||
When we need more peers, we pick addresses randomly from the addrbook with some
|
||||
configurable bias for unvetted peers. The bias should be lower when we have
|
||||
fewer peers and can increase as we obtain more, ensuring that our first peers
|
||||
are more trustworthy, but always giving us the chance to discover new good
|
||||
peers.
|
||||
|
||||
We track the last time we dialed a peer and the number of unsuccessful attempts
|
||||
we've made. If too many attempts are made, we mark the peer as bad.
|
||||
|
||||
Connection attempts are made with exponential backoff (plus jitter). Because
|
||||
the selection process happens every `ensurePeersPeriod`, we might not end up
|
||||
dialing a peer for much longer than the backoff duration.
|
||||
|
||||
If we fail to connect to the peer after 16 tries (with exponential backoff), we
|
||||
remove from peer store completely. But for persistent peers, we indefinitely try to
|
||||
dial all persistent peers unless `persistent_peers_max_dial_period` is configured to zero
|
||||
|
||||
## Select Peers to Exchange
|
||||
|
||||
When we’re asked for peers, we select them as follows:
|
||||
|
||||
- select at most `maxGetSelection` peers
|
||||
- try to select at least `minGetSelection` peers - if we have less than that, select them all.
|
||||
- select a random, unbiased `getSelectionPercent` of the peers
|
||||
|
||||
Send the selected peers. Note we select peers for sending without bias for vetted/unvetted.
|
||||
|
||||
## Preventing Spam
|
||||
|
||||
There are various cases where we decide a peer has misbehaved and we disconnect from them.
|
||||
When this happens, the peer is removed from the peer store and black listed for
|
||||
some amount of time. We call this "Disconnect and Mark".
|
||||
Note that the bad behaviour may be detected outside the PEX reactor itself
|
||||
(for instance, in the mconnection, or another reactor), but it must be communicated to the PEX reactor
|
||||
so it can remove and mark the peer.
|
||||
|
||||
In the PEX, if a peer sends us an unsolicited list of peers,
|
||||
or if the peer sends a request too soon after another one,
|
||||
we Disconnect and MarkBad.
|
||||
|
||||
## Trust Metric
|
||||
|
||||
The quality of peers can be tracked in more fine-grained detail using a
|
||||
Proportional-Integral-Derivative (PID) controller that incorporates
|
||||
current, past, and rate-of-change data to inform peer quality.
|
||||
|
||||
While a PID trust metric has been implemented, it remains for future work
|
||||
to use it in the PEX.
|
||||
|
||||
See the [trustmetric](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-006-trust-metric.md)
|
||||
and [trustmetric useage](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-007-trust-metric-usage.md)
|
||||
architecture docs for more details.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- todo: diagrams!!! -->
|
||||
Reference in New Issue
Block a user