package light
|
|
|
|
import (
|
|
"github.com/tendermint/tendermint/types"
|
|
|
|
lightErr "github.com/tendermint/tendermint/light/errors"
|
|
)
|
|
|
|
var _ Certifier = &Dynamic{}
|
|
|
|
// Dynamic uses a Static for Certify, but adds an
|
|
// Update method to allow for a change of validators.
|
|
//
|
|
// You can pass in a FullCommit with another validator set,
|
|
// and if this is a provably secure transition (< 1/3 change,
|
|
// sufficient signatures), then it will update the
|
|
// validator set for the next Certify call.
|
|
// For security, it will only follow validator set changes
|
|
// going forward.
|
|
type Dynamic struct {
|
|
cert *Static
|
|
lastHeight int
|
|
}
|
|
|
|
// NewDynamic returns a new dynamic certifier.
|
|
func NewDynamic(chainID string, vals *types.ValidatorSet, height int) *Dynamic {
|
|
return &Dynamic{
|
|
cert: NewStatic(chainID, vals),
|
|
lastHeight: height,
|
|
}
|
|
}
|
|
|
|
// ChainID returns the chain id of this certifier.
|
|
func (c *Dynamic) ChainID() string {
|
|
return c.cert.ChainID()
|
|
}
|
|
|
|
// Validators returns the validators of this certifier.
|
|
func (c *Dynamic) Validators() *types.ValidatorSet {
|
|
return c.cert.vSet
|
|
}
|
|
|
|
// Hash returns the hash of this certifier.
|
|
func (c *Dynamic) Hash() []byte {
|
|
return c.cert.Hash()
|
|
}
|
|
|
|
// LastHeight returns the last height of this certifier.
|
|
func (c *Dynamic) LastHeight() int {
|
|
return c.lastHeight
|
|
}
|
|
|
|
// Certify will verify whether the commit is valid and will update the height if it is or return an
|
|
// error if it is not.
|
|
func (c *Dynamic) Certify(check Commit) error {
|
|
err := c.cert.Certify(check)
|
|
if err == nil {
|
|
// update last seen height if input is valid
|
|
c.lastHeight = check.Height()
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Update will verify if this is a valid change and update
|
|
// the certifying validator set if safe to do so.
|
|
//
|
|
// Returns an error if update is impossible (invalid proof or IsTooMuchChangeErr)
|
|
func (c *Dynamic) Update(fc FullCommit) error {
|
|
// ignore all checkpoints in the past -> only to the future
|
|
h := fc.Height()
|
|
if h <= c.lastHeight {
|
|
return lightErr.ErrPastTime()
|
|
}
|
|
|
|
// first, verify if the input is self-consistent....
|
|
err := fc.ValidateBasic(c.ChainID())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// now, make sure not too much change... meaning this commit
|
|
// would be approved by the currently known validator set
|
|
// as well as the new set
|
|
commit := fc.Commit.Commit
|
|
err = c.Validators().VerifyCommitAny(fc.Validators, c.ChainID(),
|
|
commit.BlockID, h, commit)
|
|
if err != nil {
|
|
return lightErr.ErrTooMuchChange()
|
|
}
|
|
|
|
// looks good, we can update
|
|
c.cert = NewStatic(c.ChainID(), fc.Validators)
|
|
c.lastHeight = h
|
|
return nil
|
|
}
|