|
|
- package lite
-
- import (
- "github.com/tendermint/tendermint/types"
-
- liteErr "github.com/tendermint/tendermint/lite/errors"
- )
-
- var _ Certifier = (*InquiringCertifier)(nil)
-
- // InquiringCertifier wraps a dynamic certifier and implements an auto-update strategy. If a call
- // to Certify fails due to a change it validator set, InquiringCertifier will try and find a
- // previous FullCommit which it can use to safely update the validator set. It uses a source
- // provider to obtain the needed FullCommits. It stores properly validated data on the local system.
- type InquiringCertifier struct {
- cert *DynamicCertifier
- // These are only properly validated data, from local system
- trusted Provider
- // This is a source of new info, like a node rpc, or other import method
- Source Provider
- }
-
- // NewInquiringCertifier returns a new Inquiring object. It uses the trusted provider to store
- // validated data and the source provider to obtain missing FullCommits.
- //
- // Example: The trusted provider should a CacheProvider, MemProvider or files.Provider. The source
- // provider should be a client.HTTPProvider.
- func NewInquiringCertifier(chainID string, fc FullCommit, trusted Provider,
- source Provider) (*InquiringCertifier, error) {
-
- // store the data in trusted
- err := trusted.StoreCommit(fc)
- if err != nil {
- return nil, err
- }
-
- return &InquiringCertifier{
- cert: NewDynamicCertifier(chainID, fc.Validators, fc.Height()),
- trusted: trusted,
- Source: source,
- }, nil
- }
-
- // ChainID returns the chain id.
- // Implements Certifier.
- func (ic *InquiringCertifier) ChainID() string {
- return ic.cert.ChainID()
- }
-
- // Validators returns the validator set.
- func (ic *InquiringCertifier) Validators() *types.ValidatorSet {
- return ic.cert.cert.vSet
- }
-
- // LastHeight returns the last height.
- func (ic *InquiringCertifier) LastHeight() int64 {
- return ic.cert.lastHeight
- }
-
- // Certify makes sure this is checkpoint is valid.
- //
- // If the validators have changed since the last know time, it looks
- // for a path to prove the new validators.
- //
- // On success, it will store the checkpoint in the store for later viewing
- // Implements Certifier.
- func (ic *InquiringCertifier) Certify(commit Commit) error {
- err := ic.useClosestTrust(commit.Height())
- if err != nil {
- return err
- }
-
- err = ic.cert.Certify(commit)
- if !liteErr.IsValidatorsChangedErr(err) {
- return err
- }
- err = ic.updateToHash(commit.Header.ValidatorsHash)
- if err != nil {
- return err
- }
-
- err = ic.cert.Certify(commit)
- if err != nil {
- return err
- }
-
- // store the new checkpoint
- return ic.trusted.StoreCommit(NewFullCommit(commit, ic.Validators()))
- }
-
- // Update will verify if this is a valid change and update
- // the certifying validator set if safe to do so.
- func (ic *InquiringCertifier) Update(fc FullCommit) error {
- err := ic.useClosestTrust(fc.Height())
- if err != nil {
- return err
- }
-
- err = ic.cert.Update(fc)
- if err == nil {
- err = ic.trusted.StoreCommit(fc)
- }
- return err
- }
-
- func (ic *InquiringCertifier) useClosestTrust(h int64) error {
- closest, err := ic.trusted.GetByHeight(h)
- if err != nil {
- return err
- }
-
- // if the best seed is not the one we currently use,
- // let's just reset the dynamic validator
- if closest.Height() != ic.LastHeight() {
- ic.cert = NewDynamicCertifier(ic.ChainID(), closest.Validators, closest.Height())
- }
- return nil
- }
-
- // updateToHash gets the validator hash we want to update to
- // if IsTooMuchChangeErr, we try to find a path by binary search over height
- func (ic *InquiringCertifier) updateToHash(vhash []byte) error {
- // try to get the match, and update
- fc, err := ic.Source.GetByHash(vhash)
- if err != nil {
- return err
- }
- err = ic.cert.Update(fc)
- // handle IsTooMuchChangeErr by using divide and conquer
- if liteErr.IsTooMuchChangeErr(err) {
- err = ic.updateToHeight(fc.Height())
- }
- return err
- }
-
- // updateToHeight will use divide-and-conquer to find a path to h
- func (ic *InquiringCertifier) updateToHeight(h int64) error {
- // try to update to this height (with checks)
- fc, err := ic.Source.GetByHeight(h)
- if err != nil {
- return err
- }
- start, end := ic.LastHeight(), fc.Height()
- if end <= start {
- return liteErr.ErrNoPathFound()
- }
- err = ic.Update(fc)
-
- // we can handle IsTooMuchChangeErr specially
- if !liteErr.IsTooMuchChangeErr(err) {
- return err
- }
-
- // try to update to mid
- mid := (start + end) / 2
- err = ic.updateToHeight(mid)
- if err != nil {
- return err
- }
-
- // if we made it to mid, we recurse
- return ic.updateToHeight(h)
- }
|