lite: follow up from #3989 (#4209)

* rename adjusted to adjacent

Refs https://github.com/tendermint/tendermint/pull/3989#discussion_r352140829

* rename ErrTooMuchChange to ErrNotEnoughVotingPowerSigned

Refs https://github.com/tendermint/tendermint/pull/3989#discussion_r352142785

* verify commit is properly signed

* remove no longer trusted headers

* restore trustedHeader and trustedNextVals

* check trustedHeader using options

Refs https://github.com/tendermint/tendermint/pull/4209#issuecomment-562462165

* use correct var when checking if headers are adjacent

in bisection func
+ replace TODO with a comment

https://github.com/tendermint/tendermint/pull/3989#discussion_r352125455

* return header in VerifyHeaderAtHeight

because that way we avoid DB call

+ add godoc comments
+ check if there are no headers yet in AutoClient

https://github.com/tendermint/tendermint/pull/3989#pullrequestreview-315454506

* TestVerifyAdjacentHeaders: add 2 more test-cases

+ add TestVerifyReturnsErrorIfTrustLevelIsInvalid

* lite: avoid overflow when parsing key in db store!

* lite: rename AutoClient#Err to Errs

* lite: add a test for AutoClient

* lite: fix keyPattern and call itr.Next in db store

* lite: add two tests for db store

* lite: add TestClientRemovesNoLongerTrustedHeaders

* lite: test Client#Cleanup

* lite: test restoring trustedHeader

https://github.com/tendermint/tendermint/pull/4209#issuecomment-562462165

* lite: comment out unused code in test_helpers

* fix TestVerifyReturnsErrorIfTrustLevelIsInvalid after merge

* change defaultRemoveNoLongerTrustedHeadersPeriod

and add docs

* write more doc

* lite: uncomment testable examples

* use stdlog.Fatal to stop AutoClient tests

* make lll linter happy

* separate errors for 2 cases

- the validator set of a skipped header cannot be trusted, i.e. <1/3rd
  of h1 validator set has signed (new error, something like
  ErrNewValSetCantBeTrusted)
- the validator set is trusted but < 2/3rds has signed
  (ErrNewHeaderCantBeTrusted)

https://github.com/tendermint/tendermint/pull/4209#discussion_r360331253

* remove all headers (even the last one) that are outside

of the trusting period. By doing this, we avoid checking the
trustedHeader's hash in checkTrustedHeaderUsingOptions (case #1).

https://github.com/tendermint/tendermint/pull/4209#discussion_r360332460

* explain restoreTrustedHeaderAndNextVals better

https://github.com/tendermint/tendermint/pull/4209#discussion_r360602328

* add ConfirmationFunction option

for optionally prompting for user input Y/n before removing headers

Refs https://github.com/tendermint/tendermint/pull/4209#discussion_r360602945

* make cleaning optional

https://github.com/tendermint/tendermint/pull/4209#discussion_r364838189

* return error when user refused to remove headers

* check for double votes in VerifyCommitTrusting

* leave only ErrNewValSetCantBeTrusted error

to differenciate between h2Vals.VerifyCommit and
h1NextVals.VerifyCommitTrusting

* fix example tests

* remove unnecessary if condition

https://github.com/tendermint/tendermint/pull/4209#discussion_r365171847

It will be handled by the above switch.

* verifyCommitBasic does not depend on vals

Co-authored-by: Marko <marbar3778@yahoo.com>
This commit is contained in:
Anton Kaliaev
2020-01-13 11:56:48 +04:00
committed by GitHub
parent db235c8df0
commit 86adc2c89f
17 changed files with 1356 additions and 262 deletions

View File

@@ -631,7 +631,7 @@ func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID,
if vals.Size() != len(commit.Signatures) {
return NewErrInvalidCommitSignatures(vals.Size(), len(commit.Signatures))
}
if err := vals.verifyCommitBasic(commit, height, blockID); err != nil {
if err := verifyCommitBasic(commit, height, blockID); err != nil {
return err
}
@@ -661,7 +661,7 @@ func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID,
}
if got, needed := talliedVotingPower, vals.TotalVotingPower()*2/3; got <= needed {
return ErrTooMuchChange{Got: got, Needed: needed}
return ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed}
}
return nil
@@ -738,7 +738,7 @@ func (vals *ValidatorSet) VerifyFutureCommit(newSet *ValidatorSet, chainID strin
}
if got, needed := oldVotingPower, oldVals.TotalVotingPower()*2/3; got <= needed {
return ErrTooMuchChange{Got: got, Needed: needed}
return ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed}
}
return nil
}
@@ -755,11 +755,15 @@ func (vals *ValidatorSet) VerifyCommitTrusting(chainID string, blockID BlockID,
panic(fmt.Sprintf("trustLevel must be within [1/3, 1], given %v", trustLevel))
}
if err := vals.verifyCommitBasic(commit, height, blockID); err != nil {
if err := verifyCommitBasic(commit, height, blockID); err != nil {
return err
}
talliedVotingPower := int64(0)
var (
talliedVotingPower int64
seenVals = make(map[int]int, len(commit.Signatures)) // validator index -> commit index
)
for idx, commitSig := range commit.Signatures {
if commitSig.Absent() {
continue // OK, some signatures can be absent.
@@ -767,8 +771,16 @@ func (vals *ValidatorSet) VerifyCommitTrusting(chainID string, blockID BlockID,
// We don't know the validators that committed this block, so we have to
// check for each vote if its validator is already known.
_, val := vals.GetByAddress(commitSig.ValidatorAddress)
valIdx, val := vals.GetByAddress(commitSig.ValidatorAddress)
if firstIndex, ok := seenVals[valIdx]; ok { // double vote
secondIndex := idx
return errors.Errorf("double vote from %v (%d and %d)", val, firstIndex, secondIndex)
}
if val != nil {
seenVals[valIdx] = idx
// Validate signature.
voteSignBytes := commit.VoteSignBytes(chainID, idx)
if !val.PubKey.VerifyBytes(voteSignBytes, commitSig.Signature) {
@@ -789,13 +801,13 @@ func (vals *ValidatorSet) VerifyCommitTrusting(chainID string, blockID BlockID,
got := talliedVotingPower
needed := (vals.TotalVotingPower() * trustLevel.Numerator) / trustLevel.Denominator
if got <= needed {
return ErrTooMuchChange{Got: got, Needed: needed}
return ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed}
}
return nil
}
func (vals *ValidatorSet) verifyCommitBasic(commit *Commit, height int64, blockID BlockID) error {
func verifyCommitBasic(commit *Commit, height int64, blockID BlockID) error {
if err := commit.ValidateBasic(); err != nil {
return err
}
@@ -810,23 +822,23 @@ func (vals *ValidatorSet) verifyCommitBasic(commit *Commit, height int64, blockI
}
//-----------------
// ErrTooMuchChange
// IsErrTooMuchChange returns true if err is related to changes in validator
// set exceeding max limit.
func IsErrTooMuchChange(err error) bool {
_, ok := errors.Cause(err).(ErrTooMuchChange)
// IsErrNotEnoughVotingPowerSigned returns true if err is
// ErrNotEnoughVotingPowerSigned.
func IsErrNotEnoughVotingPowerSigned(err error) bool {
_, ok := errors.Cause(err).(ErrNotEnoughVotingPowerSigned)
return ok
}
// ErrTooMuchChange indicates that changes in the validator set exceeded max limit.
type ErrTooMuchChange struct {
// ErrNotEnoughVotingPowerSigned is returned when not enough validators signed
// a commit.
type ErrNotEnoughVotingPowerSigned struct {
Got int64
Needed int64
}
func (e ErrTooMuchChange) Error() string {
return fmt.Sprintf("invalid commit -- insufficient old voting power: got %d, needed more than %d", e.Got, e.Needed)
func (e ErrNotEnoughVotingPowerSigned) Error() string {
return fmt.Sprintf("invalid commit -- insufficient voting power: got %d, needed more than %d", e.Got, e.Needed)
}
//----------------