You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3.8 KiB

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 listen on the given address for incoming connections from an external priv_validator process. It will halt any operation until at least one external process succesfully connected.

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. 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).

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

Accepted.

Consequences

Positive

  • Cleaner separation of components enabling re-use.

Negative

  • More files - led to creation of new directory.

Neutral