mirror of
https://github.com/tendermint/tendermint.git
synced 2026-09-16 13:04:19 +00:00
Refactor "lite" to handle delayed validator set changes.
Also, fix consensus liveness issue.
This commit is contained in:
+71
-77
@@ -2,98 +2,92 @@ package lite
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/tendermint/tendermint/types"
|
||||
|
||||
liteErr "github.com/tendermint/tendermint/lite/errors"
|
||||
)
|
||||
|
||||
// Certifier checks the votes to make sure the block really is signed properly.
|
||||
// Certifier must know the current set of validitors by some other means.
|
||||
type Certifier interface {
|
||||
Certify(check Commit) error
|
||||
ChainID() string
|
||||
}
|
||||
|
||||
// Commit is basically the rpc /commit response, but extended
|
||||
//
|
||||
// This is the basepoint for proving anything on the blockchain. It contains
|
||||
// a signed header. If the signatures are valid and > 2/3 of the known set,
|
||||
// we can store this checkpoint and use it to prove any number of aspects of
|
||||
// the system: such as txs, abci state, validator sets, etc...
|
||||
type Commit types.SignedHeader
|
||||
|
||||
// FullCommit is a commit and the actual validator set,
|
||||
// the base info you need to update to a given point,
|
||||
// assuming knowledge of some previous validator set
|
||||
// FullCommit is a signed header (the block header and a commit that signs it),
|
||||
// the validator set which signed the commit, and the next validator set. The
|
||||
// next validator set (which is proven from the block header) allows us to
|
||||
// revert to block-by-block updating of lite certifier's latest validator set,
|
||||
// even in the face of arbitrarily power changes.
|
||||
type FullCommit struct {
|
||||
Commit `json:"commit"`
|
||||
Validators *types.ValidatorSet `json:"validator_set"`
|
||||
SignedHeader types.SignedHeader `json:"signed_header"`
|
||||
Validators *types.ValidatorSet `json:"validator_set"`
|
||||
NextValidators *types.ValidatorSet `json:"next_validator_set"`
|
||||
}
|
||||
|
||||
// NewFullCommit returns a new FullCommit.
|
||||
func NewFullCommit(commit Commit, vals *types.ValidatorSet) FullCommit {
|
||||
func NewFullCommit(signedHeader types.SignedHeader, valset, nvalset *types.ValidatorSet) FullCommit {
|
||||
return FullCommit{
|
||||
Commit: commit,
|
||||
Validators: vals,
|
||||
SignedHeader: signedHeader,
|
||||
Validators: valset,
|
||||
NextValidators: nvalset,
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the components and check for consistency.
|
||||
// This also checks to make sure that Validators actually
|
||||
// signed the SignedHeader.Commit.
|
||||
// If > 2/3 did not sign the Commit from fc.Validators, it
|
||||
// is not a valid commit!
|
||||
func (fc FullCommit) ValidateBasic(chainID string) error {
|
||||
// Ensure that Validators exists and matches the header.
|
||||
if fc.Validators.Size() == 0 {
|
||||
return errors.New("need FullCommit.Validators")
|
||||
}
|
||||
if !bytes.Equal(
|
||||
fc.SignedHeader.ValidatorsHash,
|
||||
fc.Validators.Hash()) {
|
||||
return fmt.Errorf("header has vhash %X but valset hash is %X",
|
||||
fc.SignedHeader.ValidatorsHash,
|
||||
fc.Validators.Hash(),
|
||||
)
|
||||
}
|
||||
// Ensure that NextValidators exists and matches the header.
|
||||
if fc.NextValidators.Size() == 0 {
|
||||
return errors.New("need FullCommit.NextValidators")
|
||||
}
|
||||
if !bytes.Equal(
|
||||
fc.SignedHeader.NextValidatorsHash,
|
||||
fc.NextValidators.Hash()) {
|
||||
return fmt.Errorf("header has next vhash %X but next valset hash is %X",
|
||||
fc.SignedHeader.NextValidatorsHash,
|
||||
fc.NextValidators.Hash(),
|
||||
)
|
||||
}
|
||||
// Validate the header.
|
||||
err := fc.SignedHeader.ValidateBasic(chainID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Validate the signatures on the commit.
|
||||
hdr, cmt := fc.SignedHeader.Header, fc.SignedHeader.Commit
|
||||
err = fc.Validators.VerifyCommit(
|
||||
hdr.ChainID, cmt.BlockID,
|
||||
hdr.Height, cmt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// All good!
|
||||
return nil
|
||||
}
|
||||
|
||||
// Height returns the height of the header.
|
||||
func (c Commit) Height() int64 {
|
||||
if c.Header == nil {
|
||||
return 0
|
||||
func (fc FullCommit) Height() int64 {
|
||||
if fc.SignedHeader.Header == nil {
|
||||
panic("should not happen")
|
||||
}
|
||||
return c.Header.Height
|
||||
return fc.SignedHeader.Height
|
||||
}
|
||||
|
||||
// ValidatorsHash returns the hash of the validator set.
|
||||
func (c Commit) ValidatorsHash() []byte {
|
||||
if c.Header == nil {
|
||||
return nil
|
||||
// ChainID returns the chainID of the header.
|
||||
func (fc FullCommit) ChainID() string {
|
||||
if fc.SignedHeader.Header == nil {
|
||||
panic("should not happen")
|
||||
}
|
||||
return c.Header.ValidatorsHash
|
||||
}
|
||||
|
||||
// ValidateBasic does basic consistency checks and makes sure the headers
|
||||
// and commits are all consistent and refer to our chain.
|
||||
//
|
||||
// Make sure to use a Verifier to validate the signatures actually provide
|
||||
// a significantly strong proof for this header's validity.
|
||||
func (c Commit) ValidateBasic(chainID string) error {
|
||||
// make sure the header is reasonable
|
||||
if c.Header == nil {
|
||||
return errors.New("Commit missing header")
|
||||
}
|
||||
if c.Header.ChainID != chainID {
|
||||
return errors.Errorf("Header belongs to another chain '%s' not '%s'",
|
||||
c.Header.ChainID, chainID)
|
||||
}
|
||||
|
||||
if c.Commit == nil {
|
||||
return errors.New("Commit missing signatures")
|
||||
}
|
||||
|
||||
// make sure the header and commit match (height and hash)
|
||||
if c.Commit.Height() != c.Header.Height {
|
||||
return liteErr.ErrHeightMismatch(c.Commit.Height(), c.Header.Height)
|
||||
}
|
||||
hhash := c.Header.Hash()
|
||||
chash := c.Commit.BlockID.Hash
|
||||
if !bytes.Equal(hhash, chash) {
|
||||
return errors.Errorf("Commits sign block %X header is block %X",
|
||||
chash, hhash)
|
||||
}
|
||||
|
||||
// make sure the commit is reasonable
|
||||
err := c.Commit.ValidateBasic()
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
// looks good, we just need to make sure the signatures are really from
|
||||
// empowered validators
|
||||
return nil
|
||||
return fc.SignedHeader.ChainID
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user