Skip to content

Commit

Permalink
lite: follow up from tendermint#3989 (tendermint#4209)
Browse files Browse the repository at this point in the history
* rename adjusted to adjacent

Refs tendermint#3989 (comment)

* rename ErrTooMuchChange to ErrNotEnoughVotingPowerSigned

Refs tendermint#3989 (comment)

* verify commit is properly signed

* remove no longer trusted headers

* restore trustedHeader and trustedNextVals

* check trustedHeader using options

Refs tendermint#4209 (comment)

* use correct var when checking if headers are adjacent

in bisection func
+ replace TODO with a comment

tendermint#3989 (comment)

* return header in VerifyHeaderAtHeight

because that way we avoid DB call

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

tendermint#3989 (review)

* 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

tendermint#4209 (comment)

* 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)

tendermint#4209 (comment)

* 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).

tendermint#4209 (comment)

* explain restoreTrustedHeaderAndNextVals better

tendermint#4209 (comment)

* add ConfirmationFunction option

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

Refs tendermint#4209 (comment)

* make cleaning optional

tendermint#4209 (comment)

* 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

tendermint#4209 (comment)

It will be handled by the above switch.

* verifyCommitBasic does not depend on vals

Co-authored-by: Marko <[email protected]>
  • Loading branch information
melekes and tac0turtle authored Jan 13, 2020
1 parent db235c8 commit 86adc2c
Show file tree
Hide file tree
Showing 17 changed files with 1,371 additions and 277 deletions.
6 changes: 3 additions & 3 deletions lite/dynamic_verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ func (dv *DynamicVerifier) Verify(shdr types.SignedHeader) error {

// verifyAndSave will verify if this is a valid source full commit given the
// best match trusted full commit, and if good, persist to dv.trusted.
// Returns ErrTooMuchChange when >2/3 of trustedFC did not sign sourceFC.
// Returns ErrNotEnoughVotingPowerSigned when >2/3 of trustedFC did not sign sourceFC.
// Panics if trustedFC.Height() >= sourceFC.Height().
func (dv *DynamicVerifier) verifyAndSave(trustedFC, sourceFC FullCommit) error {
if trustedFC.Height() >= sourceFC.Height() {
Expand Down Expand Up @@ -247,8 +247,8 @@ FOR_LOOP:
return sourceFC, nil
}

// Handle special case when err is ErrTooMuchChange.
if types.IsErrTooMuchChange(err) {
// Handle special case when err is ErrNotEnoughVotingPowerSigned.
if types.IsErrNotEnoughVotingPowerSigned(err) {
// Divide and conquer.
start, end := trustedFC.Height(), sourceFC.Height()
if !(start < end) {
Expand Down
30 changes: 15 additions & 15 deletions lite2/auto_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type AutoClient struct {
quit chan struct{}

trustedHeaders chan *types.SignedHeader
err chan error
errs chan error
}

// NewAutoClient creates a new client and starts a polling goroutine.
Expand All @@ -23,7 +23,7 @@ func NewAutoClient(base *Client, updatePeriod time.Duration) *AutoClient {
updatePeriod: updatePeriod,
quit: make(chan struct{}),
trustedHeaders: make(chan *types.SignedHeader),
err: make(chan error),
errs: make(chan error),
}
go c.autoUpdate()
return c
Expand All @@ -35,8 +35,8 @@ func (c *AutoClient) TrustedHeaders() <-chan *types.SignedHeader {
}

// Err returns a channel onto which errors are posted.
func (c *AutoClient) Err() <-chan error {
return c.err
func (c *AutoClient) Errs() <-chan error {
return c.errs
}

// Stop stops the client.
Expand All @@ -45,30 +45,30 @@ func (c *AutoClient) Stop() {
}

func (c *AutoClient) autoUpdate() {
lastTrustedHeight, err := c.base.LastTrustedHeight()
if err != nil {
c.err <- err
return
}

ticker := time.NewTicker(c.updatePeriod)
defer ticker.Stop()

for {
select {
case <-ticker.C:
err := c.base.VerifyHeaderAtHeight(lastTrustedHeight+1, time.Now())
lastTrustedHeight, err := c.base.LastTrustedHeight()
if err != nil {
c.err <- err
c.errs <- err
continue
}

if lastTrustedHeight == -1 {
// no headers yet => wait
continue
}
h, err := c.base.TrustedHeader(lastTrustedHeight+1, time.Now())

h, err := c.base.VerifyHeaderAtHeight(lastTrustedHeight+1, time.Now())
if err != nil {
c.err <- err
// no header yet or verification error => try again after updatePeriod
c.errs <- err
continue
}
c.trustedHeaders <- h
lastTrustedHeight = h.Height
case <-c.quit:
return
}
Expand Down
76 changes: 76 additions & 0 deletions lite2/auto_client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package lite

import (
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

dbm "github.com/tendermint/tm-db"

"github.com/tendermint/tendermint/libs/log"
mockp "github.com/tendermint/tendermint/lite2/provider/mock"
dbs "github.com/tendermint/tendermint/lite2/store/db"
"github.com/tendermint/tendermint/types"
)

func TestAutoClient(t *testing.T) {
const (
chainID = "TestAutoClient"
)

var (
keys = genPrivKeys(4)
// 20, 30, 40, 50 - the first 3 don't have 2/3, the last 3 do!
vals = keys.ToValidators(20, 10)
bTime = time.Now().Add(-1 * time.Hour)
header = keys.GenSignedHeader(chainID, 1, bTime, nil, vals, vals,
[]byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys))
)

base, err := NewClient(
chainID,
TrustOptions{
Period: 4 * time.Hour,
Height: 1,
Hash: header.Hash(),
},
mockp.New(
chainID,
map[int64]*types.SignedHeader{
// trusted header
1: header,
// interim header (3/3 signed)
2: keys.GenSignedHeader(chainID, 2, bTime.Add(30*time.Minute), nil, vals, vals,
[]byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)),
// last header (3/3 signed)
3: keys.GenSignedHeader(chainID, 3, bTime.Add(1*time.Hour), nil, vals, vals,
[]byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)),
},
map[int64]*types.ValidatorSet{
1: vals,
2: vals,
3: vals,
4: vals,
},
),
dbs.New(dbm.NewMemDB(), chainID),
)
require.NoError(t, err)
base.SetLogger(log.TestingLogger())

c := NewAutoClient(base, 1*time.Second)
defer c.Stop()

for i := 2; i <= 3; i++ {
select {
case h := <-c.TrustedHeaders():
assert.EqualValues(t, i, h.Height)
case err := <-c.Errs():
require.NoError(t, err)
case <-time.After(2 * time.Second):
t.Fatal("no headers/errors received in 2 sec")
}
}
}
Loading

0 comments on commit 86adc2c

Please sign in to comment.