2018-09-21 07:39:55 -04:00
# Peers
2017-12-31 17:07:08 -05:00
2018-01-19 17:10:08 -05:00
This document explains how Tendermint Peers are identified and how they connect to one another.
2018-05-23 10:01:32 -04:00
For details on peer discovery, see the [peer exchange (PEX) reactor doc ](https://github.com/tendermint/tendermint/blob/master/docs/spec/reactors/pex/pex.md ).
2017-12-31 17:07:08 -05:00
## Peer Identity
2018-01-09 12:44:49 -05:00
Tendermint peers are expected to maintain long-term persistent identities in the form of a public key.
2018-06-22 06:24:36 +02:00
Each peer has an ID defined as `peer.ID == peer.PubKey.Address()` , where `Address` uses the scheme defined in `crypto` package.
2017-12-31 17:07:08 -05:00
2018-04-28 17:14:58 -04:00
A single peer ID can have multiple IP addresses associated with it, but a node
will only ever connect to one at a time.
2017-12-31 17:07:08 -05:00
When attempting to connect to a peer, we use the PeerURL: `<ID>@<IP>:<PORT>` .
We will attempt to connect to the peer at IP:PORT, and verify,
via authenticated encryption, that it is in possession of the private key
corresponding to `<ID>` . This prevents man-in-the-middle attacks on the peer layer.
## Connections
All p2p connections use TCP.
Upon establishing a successful TCP connection with a peer,
two handhsakes are performed: one for authenticated encryption, and one for Tendermint versioning.
Both handshakes have configurable timeouts (they should complete quickly).
### Authenticated Encryption Handshake
Tendermint implements the Station-to-Station protocol
2018-07-24 12:33:02 -07:00
using X25519 keys for Diffie-Helman key-exchange and chacha20poly1305 for encryption.
2017-12-31 17:07:08 -05:00
It goes as follows:
2018-08-27 15:33:46 +08:00
2018-07-24 12:33:02 -07:00
- generate an ephemeral X25519 keypair
2017-12-31 17:07:08 -05:00
- send the ephemeral public key to the peer
- wait to receive the peer's ephemeral public key
- compute the Diffie-Hellman shared secret using the peers ephemeral public key and our ephemeral private key
2018-07-24 12:33:02 -07:00
- generate two keys to use for encryption (sending and receiving) and a challenge for authentication as follows:
2018-08-27 15:33:46 +08:00
- create a hkdf-sha256 instance with the key being the diffie hellman shared secret, and info parameter as
`TENDERMINT_SECRET_CONNECTION_KEY_AND_CHALLENGE_GEN`
- get 96 bytes of output from hkdf-sha256
- if we had the smaller ephemeral pubkey, use the first 32 bytes for the key for receiving, the second 32 bytes for sending; else the opposite
- use the last 32 bytes of output for the challenge
2018-09-07 11:40:16 +04:00
- use a separate nonce for receiving and sending. Both nonces start at 0, and should support the full 96 bit nonce range
2018-07-24 12:33:02 -07:00
- all communications from now on are encrypted in 1024 byte frames,
2018-08-27 15:33:46 +08:00
using the respective secret and nonce. Each nonce is incremented by one after each use.
2018-01-09 12:44:49 -05:00
- we now have an encrypted channel, but still need to authenticate
2018-07-24 12:33:02 -07:00
- sign the common challenge obtained from the hkdf with our persistent private key
- send the amino encoded persistent pubkey and signature to the peer
2018-01-01 15:59:53 -05:00
- wait to receive the persistent public key and signature from the peer
- verify the signature on the challenge using the peer's persistent public key
2017-12-31 17:07:08 -05:00
If this is an outgoing connection (we dialed the peer) and we used a peer ID,
2018-01-01 15:59:53 -05:00
then finally verify that the peer's persistent public key corresponds to the peer ID we dialed,
2017-12-31 17:07:08 -05:00
ie. `peer.PubKey.Address() == <ID>` .
The connection has now been authenticated. All traffic is encrypted.
2018-01-25 05:02:26 +00:00
Note: only the dialer can authenticate the identity of the peer,
2017-12-31 17:07:08 -05:00
but this is what we care about since when we join the network we wish to
ensure we have reached the intended peer (and are not being MITMd).
### Peer Filter
2018-01-01 15:59:53 -05:00
Before continuing, we check if the new peer has the same ID as ourselves or
2017-12-31 17:07:08 -05:00
an existing peer. If so, we disconnect.
We also check the peer's address and public key against
an optional whitelist which can be managed through the ABCI app -
2018-01-09 12:44:49 -05:00
if the whitelist is enabled and the peer does not qualify, the connection is
2017-12-31 17:07:08 -05:00
terminated.
### Tendermint Version Handshake
2018-01-01 15:59:53 -05:00
The Tendermint Version Handshake allows the peers to exchange their NodeInfo:
2017-12-31 17:07:08 -05:00
2018-01-25 05:02:26 +00:00
```golang
2017-12-31 17:07:08 -05:00
type NodeInfo struct {
2018-10-18 10:29:59 -04:00
Version p2p.Version
2018-04-11 11:11:11 +03:00
ID p2p.ID
2018-01-09 12:44:49 -05:00
ListenAddr string
2018-04-28 17:14:58 -04:00
Network string
2018-10-18 10:29:59 -04:00
SoftwareVersion string
2018-01-09 12:44:49 -05:00
Channels []int8
2018-04-28 17:14:58 -04:00
Moniker string
2018-09-17 20:39:52 +04:00
Other NodeInfoOther
}
2018-10-18 10:29:59 -04:00
type Version struct {
P2P uint64
Block uint64
App uint64
}
2018-09-17 20:39:52 +04:00
type NodeInfoOther struct {
TxIndex string
2018-09-18 13:28:32 +04:00
RPCAddress string
2017-12-31 17:07:08 -05:00
}
```
The connection is disconnected if:
2018-08-27 15:33:46 +08:00
2018-04-11 11:11:11 +03:00
- `peer.NodeInfo.ID` is not equal `peerConn.ID`
2018-10-18 10:29:59 -04:00
- `peer.NodeInfo.Version.Block` does not match ours
2017-12-31 17:07:08 -05:00
- `peer.NodeInfo.Network` is not the same as ours
2018-01-01 15:59:53 -05:00
- `peer.Channels` does not intersect with our known Channels.
2018-04-28 17:14:58 -04:00
- `peer.NodeInfo.ListenAddr` is malformed or is a DNS host that cannot be
resolved
2017-12-31 17:07:08 -05:00
2018-01-01 15:59:53 -05:00
At this point, if we have not disconnected, the peer is valid.
It is added to the switch and hence all reactors via the `AddPeer` method.
Note that each reactor may handle multiple channels.
2017-12-31 17:07:08 -05:00
2018-01-01 15:59:53 -05:00
## Connection Activity
2017-12-31 17:07:08 -05:00
2018-01-01 15:59:53 -05:00
Once a peer is added, incoming messages for a given reactor are handled through
that reactor's `Receive` method, and output messages are sent directly by the Reactors
2018-01-25 05:02:26 +00:00
on each peer. A typical reactor maintains per-peer go-routine(s) that handle this.