2018-02-09 16:52:05 -05:00
|
|
|
# ADR 008: PrivValidator
|
|
|
|
|
|
|
|
## Context
|
|
|
|
|
|
|
|
The current PrivValidator is monolithic and isn't easily reuseable by alternative signers.
|
|
|
|
|
|
|
|
For instance, see https://github.com/tendermint/tendermint/issues/673
|
|
|
|
|
|
|
|
The goal is to have a clean PrivValidator interface like:
|
|
|
|
|
2018-01-17 17:17:23 +01:00
|
|
|
```
|
2018-02-09 16:52:05 -05:00
|
|
|
type PrivValidator interface {
|
|
|
|
Address() data.Bytes
|
|
|
|
PubKey() crypto.PubKey
|
|
|
|
|
|
|
|
SignVote(chainID string, vote *types.Vote) error
|
|
|
|
SignProposal(chainID string, proposal *types.Proposal) error
|
|
|
|
SignHeartbeat(chainID string, heartbeat *types.Heartbeat) error
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
It should also be easy to re-use the LastSignedInfo logic to avoid double signing.
|
|
|
|
|
|
|
|
## Decision
|
|
|
|
|
|
|
|
Tendermint node's should support only two in-process PrivValidator implementations:
|
|
|
|
|
|
|
|
- PrivValidatorUnencrypted uses an unencrypted private key in a "priv_validator.json" file - no configuration required (just `tendermint init`).
|
|
|
|
- PrivValidatorSocket uses a socket to send signing requests to another process - user is responsible for starting that process themselves.
|
|
|
|
|
|
|
|
The PrivValidatorSocket address can be provided via flags at the command line -
|
2018-02-28 09:35:52 -05:00
|
|
|
doing so will cause Tendermint to ignore any "priv_validator.json" file and to listen
|
|
|
|
on the given address for incoming connections from an external priv_validator process.
|
2018-03-05 17:38:05 +01:00
|
|
|
It will halt any operation until at least one external process succesfully
|
|
|
|
connected.
|
2018-02-28 09:35:52 -05:00
|
|
|
|
|
|
|
The external priv_validator process will dial the address to connect to Tendermint,
|
|
|
|
and then Tendermint will send requests on the ensuing connection to sign votes and proposals.
|
|
|
|
Thus the external process initiates the connection, but the Tendermint process makes all requests.
|
2018-03-05 17:38:05 +01:00
|
|
|
In a later stage we're going to support multiple validators for fault
|
|
|
|
tolerance. To prevent double signing they need to be synced, which is deferred
|
|
|
|
to an external solution (see #1185).
|
2018-02-09 16:52:05 -05:00
|
|
|
|
|
|
|
In addition, Tendermint will provide implementations that can be run in that external process.
|
|
|
|
These include:
|
|
|
|
|
|
|
|
- PrivValidatorEncrypted uses an encrypted private key persisted to disk - user must enter password to decrypt key when process is started.
|
|
|
|
- PrivValidatorLedger uses a Ledger Nano S to handle all signing.
|
|
|
|
|
|
|
|
What follows are descriptions of useful types
|
|
|
|
|
|
|
|
### Signer
|
|
|
|
|
|
|
|
```
|
|
|
|
type Signer interface {
|
|
|
|
Sign(msg []byte) (crypto.Signature, error)
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Signer signs a message. It can also return an error.
|
|
|
|
|
|
|
|
### ValidatorID
|
|
|
|
|
|
|
|
|
|
|
|
ValidatorID is just the Address and PubKey
|
|
|
|
|
|
|
|
```
|
|
|
|
type ValidatorID struct {
|
|
|
|
Address data.Bytes `json:"address"`
|
|
|
|
PubKey crypto.PubKey `json:"pub_key"`
|
|
|
|
}
|
2018-01-17 17:17:23 +01:00
|
|
|
```
|
2018-02-09 16:52:05 -05:00
|
|
|
|
|
|
|
### LastSignedInfo
|
|
|
|
|
|
|
|
LastSignedInfo tracks the last thing we signed:
|
|
|
|
|
|
|
|
```
|
|
|
|
type LastSignedInfo struct {
|
|
|
|
Height int64 `json:"height"`
|
|
|
|
Round int `json:"round"`
|
|
|
|
Step int8 `json:"step"`
|
|
|
|
Signature crypto.Signature `json:"signature,omitempty"` // so we dont lose signatures
|
|
|
|
SignBytes data.Bytes `json:"signbytes,omitempty"` // so we dont lose signatures
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
It exposes methods for signing votes and proposals using a `Signer`.
|
|
|
|
|
|
|
|
This allows it to easily be reused by developers implemented their own PrivValidator.
|
|
|
|
|
|
|
|
### PrivValidatorUnencrypted
|
|
|
|
|
|
|
|
```
|
|
|
|
type PrivValidatorUnencrypted struct {
|
|
|
|
ID types.ValidatorID `json:"id"`
|
|
|
|
PrivKey PrivKey `json:"priv_key"`
|
|
|
|
LastSignedInfo *LastSignedInfo `json:"last_signed_info"`
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Has the same structure as currently, but broken up into sub structs.
|
|
|
|
|
|
|
|
Note the LastSignedInfo is mutated in place every time we sign.
|
|
|
|
|
|
|
|
### PrivValidatorJSON
|
|
|
|
|
|
|
|
The "priv_validator.json" file supports only the PrivValidatorUnencrypted type.
|
|
|
|
|
|
|
|
It unmarshals into PrivValidatorJSON, which is used as the default PrivValidator type.
|
|
|
|
It wraps the PrivValidatorUnencrypted and persists it to disk after every signature.
|
|
|
|
|
|
|
|
## Status
|
|
|
|
|
2018-02-28 09:35:52 -05:00
|
|
|
Accepted.
|
2018-02-09 16:52:05 -05:00
|
|
|
|
|
|
|
## Consequences
|
|
|
|
|
|
|
|
### Positive
|
|
|
|
|
|
|
|
- Cleaner separation of components enabling re-use.
|
|
|
|
|
|
|
|
### Negative
|
|
|
|
|
|
|
|
- More files - led to creation of new directory.
|
|
|
|
|
|
|
|
### Neutral
|
|
|
|
|