package lite import ( "github.com/tendermint/tendermint/types" liteErr "github.com/tendermint/tendermint/lite/errors" ) var _ Certifier = (*DynamicCertifier)(nil) // DynamicCertifier uses a StaticCertifier 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 DynamicCertifier struct { cert *StaticCertifier lastHeight int64 } // NewDynamic returns a new dynamic certifier. func NewDynamicCertifier(chainID string, vals *types.ValidatorSet, height int64) *DynamicCertifier { return &DynamicCertifier{ cert: NewStaticCertifier(chainID, vals), lastHeight: height, } } // ChainID returns the chain id of this certifier. // Implements Certifier. func (dc *DynamicCertifier) ChainID() string { return dc.cert.ChainID() } // Validators returns the validators of this certifier. func (dc *DynamicCertifier) Validators() *types.ValidatorSet { return dc.cert.vSet } // Hash returns the hash of this certifier. func (dc *DynamicCertifier) Hash() []byte { return dc.cert.Hash() } // LastHeight returns the last height of this certifier. func (dc *DynamicCertifier) LastHeight() int64 { return dc.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. // Implements Certifier. func (dc *DynamicCertifier) Certify(check Commit) error { err := dc.cert.Certify(check) if err == nil { // update last seen height if input is valid dc.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 (dc *DynamicCertifier) Update(fc FullCommit) error { // ignore all checkpoints in the past -> only to the future h := fc.Height() if h <= dc.lastHeight { return liteErr.ErrPastTime() } // first, verify if the input is self-consistent.... err := fc.ValidateBasic(dc.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 = dc.Validators().VerifyCommitAny(fc.Validators, dc.ChainID(), commit.BlockID, h, commit) if err != nil { return liteErr.ErrTooMuchChange() } // looks good, we can update dc.cert = NewStaticCertifier(dc.ChainID(), fc.Validators) dc.lastHeight = h return nil }