package certifiers
|
|
|
|
import (
|
|
"bytes"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
rtypes "github.com/tendermint/tendermint/rpc/core/types"
|
|
"github.com/tendermint/tendermint/types"
|
|
|
|
certerr "github.com/tendermint/tendermint/certifiers/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 rtypes.ResultCommit
|
|
|
|
// 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
|
|
type FullCommit struct {
|
|
*Commit `json:"commit"`
|
|
Validators *types.ValidatorSet `json:"validator_set"`
|
|
}
|
|
|
|
func NewFullCommit(commit *Commit, vals *types.ValidatorSet) FullCommit {
|
|
return FullCommit{
|
|
Commit: commit,
|
|
Validators: vals,
|
|
}
|
|
}
|
|
|
|
func CommitFromResult(commit *rtypes.ResultCommit) *Commit {
|
|
return (*Commit)(commit)
|
|
}
|
|
|
|
func (c *Commit) Height() int {
|
|
if c == nil || c.Header == nil {
|
|
return 0
|
|
}
|
|
return c.Header.Height
|
|
}
|
|
|
|
func (c *Commit) ValidatorsHash() []byte {
|
|
if c == nil || c.Header == nil {
|
|
return nil
|
|
}
|
|
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 certerr.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
|
|
}
|