Documentation
¶
Overview ¶
* PrivValidator * * Package privval implements the BFT validator interface defined in tm2/pkg/bft/types.PrivValidator. * The validator role is to sign votes and proposals for the consensus protocol, ensuring that it never * double-signs, even in the case of a crash during the signing process or a malicious attack. * * To achieve this, the PrivValidator relies on two components: * - a signer that generates cryptographic signatures for arbitrary byte slices without any checks. * - a state that both stores and verifies the last signature and signed data to prevent double-signing. * * * Signer * * The signer implements the BFT signer interface defined in tm2/pkg/bft/types.Signer. Two implementations * are provided in this package: * - a local signer that uses a keypair encoded with amino and persisted to disk (default for gnoland nodes). * - a remote signer that uses a client sending signing requests to a remote signer server. * * Both the remote signer client and server are provided in tm2/pkg/bft/privval/signer/remote. The current * implementation supports TCP and UNIX socket connections. * * TCP connections are secured using the cryptographic handshake defined in tm2/pkg/p2p/conn.MakeSecretConnection * which is an implementation of the STS protocol described in this whitepaper: * https://github.com/tendermint/tendermint/blob/0.1/docs/sts-final.pdf * TCP connections can optionally be mutually authenticated using a whitelist of authorized public keys for both * the client and the server. * * By default, the remote signer client will indefinitely try to connect to the remote signer server for each * request it sends. Consequently, a node using a private validator with a remote signer will not fail due to * temporary network issues or a crash of the remote signer server. * * The remote signer server provided by this package is a generic bridge that take any types.Signer as a * parameter and proxies the client requests to it. Additionally, gnokms is a CLI tool available in * contribs/gnokms that aims to provide a remote signer server along with a set of backend signers, including * one based on gnokey. * * * State * * The state manager defined in tm2/pkg/bft/privval/state does not implement any interface. It basically keeps * track of the last signature and signed data to prevent double-signing. The state is persisted to disk in a * file encoded with amino and all checks are performed locally.
Index ¶
- func NewPrivValidatorFromConfig(config *PrivValidatorConfig, clientPrivKey ed25519.PrivKeyEd25519, ...) (types.PrivValidator, error)
- func NewSignerFromConfig(ctx context.Context, config *PrivValidatorConfig, ...) (types.Signer, error)
- type PrivValidator
- type PrivValidatorConfig
- type TmkmsListenerConfig
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NewPrivValidatorFromConfig ¶
func NewPrivValidatorFromConfig( config *PrivValidatorConfig, clientPrivKey ed25519.PrivKeyEd25519, clientLogger *slog.Logger, ) (types.PrivValidator, error)
NewPrivValidatorFromConfig returns a types.PrivValidator chosen by config:
- if TmkmsListener is enabled, build the upstream-protocol listener stack (TCPListener → SignerListenerEndpoint → SignerClient → RetrySignerClient);
- otherwise return the existing local-or-remote-signer concrete *PrivValidator (with FileState-backed HRS gating).
Return type is the interface so callers don't need to special-case the listener path. The concrete *PrivValidator type is still accessible via type assertion for paths that need it (e.g., tests poking at the inner signer).
The clientPrivKey is the validator's node identity key; it's used as the SecretConnection identity for both remote-signer-client and tmkms-listener modes.
func NewSignerFromConfig ¶
func NewSignerFromConfig( ctx context.Context, config *PrivValidatorConfig, clientPrivKey ed25519.PrivKeyEd25519, clientLogger *slog.Logger, ) (types.Signer, error)
NewSignerFromConfig returns a new Signer instance based on the configuration. The ctx and clientLogger are only used for the remote signer client. The clientPrivKey is only used for the remote signer client using a TCP connection.
Types ¶
type PrivValidator ¶
type PrivValidator struct {
// contains filtered or unexported fields
}
PrivValidator signs votes and proposals for the consensus protocol using a signer (which can be either local or remote) and a state file to ensure that the validator never double sign, even in the case of a crash.
func NewPrivValidator ¶
func NewPrivValidator(signer types.Signer, stateFilePath string) (*PrivValidator, error)
NewPrivValidator returns a new PrivValidator instance with the given signer and state file path. If the state file does not exist, it will be created.
func (*PrivValidator) Close ¶
func (pv *PrivValidator) Close() error
Close implements types.PrivValidator.
func (*PrivValidator) PubKey ¶
func (pv *PrivValidator) PubKey() crypto.PubKey
PubKey returns the public key of the private validator signer.
func (*PrivValidator) SignProposal ¶
func (pv *PrivValidator) SignProposal(chainID string, proposal *types.Proposal) error
SignProposal signs a proposal using the private validator's signer and updates the state file to prevent double signing.
func (*PrivValidator) SignVote ¶
func (pv *PrivValidator) SignVote(chainID string, vote *types.Vote) error
SignVote signs a vote using the private validator's signer and updates the state file to prevent double signing.
func (*PrivValidator) String ¶
func (pv *PrivValidator) String() string
String implements fmt.Stringer.
type PrivValidatorConfig ¶
type PrivValidatorConfig struct {
// File path configuration.
RootDir string `json:"home" toml:"home"`
SignState string `` /* 129-byte string literal not displayed */
LocalSigner string `` /* 138-byte string literal not displayed */
// Remote Signer configuration (tm2-native protocol; validator dials gnokms).
RemoteSigner *rsclient.RemoteSignerClientConfig `json:"remote_signer" toml:"remote_signer" comment:"Configuration for the remote signer client (gnokms)"`
// TmkmsListener configures the upstream-Tendermint-protocol listener (validator
// listens for tmkms / Horcrux to dial in). Mutually exclusive with RemoteSigner;
// see upstream_config.go.
TmkmsListener *TmkmsListenerConfig `` /* 164-byte string literal not displayed */
}
PrivValidatorConfig defines the configuration for the PrivValidator, with a local signer (file-based key), a gnokms remote signer (tm2-native protocol), or a tmkms listener (upstream Tendermint privval protocol — the validator listens for tmkms to dial in). At most one of RemoteSigner or TmkmsListener may be enabled.
func DefaultPrivValidatorConfig ¶
func DefaultPrivValidatorConfig() *PrivValidatorConfig
DefaultPrivValidatorConfig returns a default configuration for the PrivValidator.
func TestPrivValidatorConfig ¶
func TestPrivValidatorConfig() *PrivValidatorConfig
TestPrivValidatorConfig returns a configuration for testing the PrivValidator.
func (*PrivValidatorConfig) LocalSignerPath ¶
func (cfg *PrivValidatorConfig) LocalSignerPath() string
LocalSignerPath returns the complete path for the local signer file.
func (*PrivValidatorConfig) SignStatePath ¶
func (cfg *PrivValidatorConfig) SignStatePath() string
SignStatePath returns the complete path for the sign state file.
func (*PrivValidatorConfig) ValidateBasic ¶
func (cfg *PrivValidatorConfig) ValidateBasic() error
ValidateBasic performs basic validation (checking param bounds, etc.) and returns an error if any check fails.
type TmkmsListenerConfig ¶
type TmkmsListenerConfig struct {
// ListenAddr is the validator's listen address for inbound signer
// connections. Format: "tcp://0.0.0.0:26659" or
// "unix:///var/run/gnoland/privval.sock". An empty value disables
// this mode (the existing local/remote-client paths apply).
ListenAddr string `` /* 141-byte string literal not displayed */
// AllowedKMSPubKeys is the allowlist of expected signer pubkeys.
// Each entry is a hex-encoded ed25519 pubkey (32 bytes → 64 hex
// chars), optionally with an "ed25519:" prefix. It is the only
// authorization control on a tcp:// listener and is required there
// (an empty list would accept any peer that completes the
// SecretConnection handshake). On a unix:// listener it is ignored —
// the boundary is filesystem permissions, not the pubkey allowlist.
AllowedKMSPubKeys []string `` /* 204-byte string literal not displayed */
// ChainID is sent in PubKeyRequest/SignVoteRequest/SignProposalRequest.
// tmkms verifies its configured chain_id matches, refusing to sign
// for a different network. Required for production; equivalent to
// the chain_id field in tmkms's [[validator]] block.
ChainID string `json:"chain_id" toml:"chain_id" comment:"Chain ID sent to the signer; must match tmkms.toml's [[validator]] chain_id."`
// ProtocolVersion pins the upstream Tendermint privval dialect.
// Today only "v0.34" is supported — the canonical sign-bytes in
// upstreampb are wired to v0.34's protobuf shape. Mirrors tmkms's
// [[validator]].protocol_version; both sides MUST agree. We refuse
// any other value at startup rather than silently misencode votes.
ProtocolVersion string `` /* 186-byte string literal not displayed */
// TimeoutReadWrite is the read/write deadline applied to the held
// signer connection. Default 5s, matching cometbft.
TimeoutReadWrite time.Duration `json:"timeout_read_write" toml:"timeout_read_write" comment:"Read/write deadline for signer connections (default 5s)."`
// WaitForConnectionTimeout caps how long Init() blocks waiting for
// the signer to dial in at startup. Default 60s.
WaitForConnectionTimeout time.Duration `` /* 144-byte string literal not displayed */
// Retries is the per-Sign retry budget on transient errors. Zero
// means retry forever (matches cometbft's RetrySignerClient
// convention). Default 5.
Retries int `json:"retries" toml:"retries" comment:"Per-Sign retry attempts on transient errors. 0 = infinite (default 5)."`
// RetryTimeout is the sleep between retry attempts. Default 1s.
RetryTimeout time.Duration `json:"retry_timeout" toml:"retry_timeout" comment:"Sleep between retry attempts (default 1s)."`
}
TmkmsListenerConfig configures the upstream-Tendermint-protocol listener. Field types and JSON/TOML tags chosen to match cometbft's config style for operator familiarity.
func DefaultTmkmsListenerConfig ¶
func DefaultTmkmsListenerConfig() *TmkmsListenerConfig
DefaultTmkmsListenerConfig returns a config with reasonable defaults but ListenAddr unset (so the mode is OFF by default — operator must opt in). AllowedKMSPubKeys is an empty slice (not nil) so TOML round- trip is byte-stable; matches the convention in rsclient.DefaultRemoteSignerClientConfig.
func (*TmkmsListenerConfig) IsEnabled ¶
func (c *TmkmsListenerConfig) IsEnabled() bool
IsEnabled reports whether the listener mode is configured (i.e., ListenAddr is set). Used by NewPrivValidatorFromConfig to decide which factory branch to take.
func (*TmkmsListenerConfig) ParseAllowlist ¶
func (c *TmkmsListenerConfig) ParseAllowlist() ([]ed25519.PubKeyEd25519, error)
ParseAllowlist decodes AllowedKMSPubKeys into typed ed25519 pubkeys. An entry may be a bare 64-hex-char string or prefixed with "ed25519:". An empty list returns nil — the caller treats nil as "accept any".
func (*TmkmsListenerConfig) ValidateBasic ¶
func (c *TmkmsListenerConfig) ValidateBasic() error
ValidateBasic is invoked by the parent PrivValidatorConfig.ValidateBasic. Only checks fields when the mode is enabled.
The allowlist requirement is scheme-dependent. On a tcp:// listener the allowlist is the only authorization control: an empty allowlist accepts any peer that completes the SecretConnection handshake — a footgun in production (a misconfigured firewall plus an attacker who can mint an ed25519 keypair would be enough to substitute the signer), so a non-empty allowlist is required. On a unix:// listener the allowlist is not enforced at all (NewUnixListener does no SecretConnection); the boundary there is filesystem permissions, so we neither require nor honor it — if one is configured, newTmkmsListenerPrivValidator logs that it is ignored.