mirror of
https://github.com/fluencelabs/tendermint
synced 2025-04-30 17:22:13 +00:00
119 lines
3.2 KiB
Markdown
119 lines
3.2 KiB
Markdown
|
# 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:
|
||
|
|
||
|
``
|
||
|
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 -
|
||
|
doing so will cause Tendermint to ignore any "priv_validator.json" file and to attempt
|
||
|
to connect over the socket.
|
||
|
|
||
|
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"`
|
||
|
}
|
||
|
|
||
|
### 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
|
||
|
|
||
|
Proposed.
|
||
|
|
||
|
## Consequences
|
||
|
|
||
|
### Positive
|
||
|
|
||
|
- Cleaner separation of components enabling re-use.
|
||
|
|
||
|
### Negative
|
||
|
|
||
|
- More files - led to creation of new directory.
|
||
|
|
||
|
### Neutral
|
||
|
|