diff --git a/lite2/example_test.go b/lite2/example_test.go index c8d883958..65997f906 100644 --- a/lite2/example_test.go +++ b/lite2/example_test.go @@ -11,7 +11,6 @@ import ( dbm "github.com/tendermint/tm-db" "github.com/tendermint/tendermint/abci/example/kvstore" - "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/lite2/provider" httpp "github.com/tendermint/tendermint/lite2/provider/http" dbs "github.com/tendermint/tendermint/lite2/store/db" @@ -19,7 +18,7 @@ import ( ) // Automatically getting new headers and verifying them. -func TestExample_Client_AutoUpdate(t *testing.T) { +func ExampleClient_Update() { // give Tendermint time to generate some blocks time.Sleep(5 * time.Second) @@ -57,10 +56,10 @@ func TestExample_Client_AutoUpdate(t *testing.T) { Hash: header.Hash(), }, primary, - []provider.Provider{primary}, // TODO: primary should not be used here + []provider.Provider{primary}, // NOTE: primary should not be used here dbs.New(db, chainID), - UpdatePeriod(1*time.Second), - Logger(log.TestingLogger()), + UpdatePeriod(0), // NOTE: value should be greater than zero + // Logger(log.TestingLogger()), ) if err != nil { stdlog.Fatal(err) @@ -76,17 +75,29 @@ func TestExample_Client_AutoUpdate(t *testing.T) { time.Sleep(2 * time.Second) + // XXX: 30 * time.Minute clock drift is needed because a) Tendermint strips + // monotonic component (see types/time/time.go) b) single instance is being + // run. + err = c.Update(time.Now().Add(30 * time.Minute)) + if err != nil { + stdlog.Fatal(err) + } + h, err := c.TrustedHeader(0) if err != nil { stdlog.Fatal(err) } - fmt.Println("got header", h.Height) - // Output: got header 3 + if h.Height > 2 { + fmt.Println("successful update") + } else { + fmt.Println("update failed") + } + // Output: successful update } // Manually getting headers and verifying them. -func TestExample_Client_ManualUpdate(t *testing.T) { +func ExampleClient_VerifyHeaderAtHeight() { // give Tendermint time to generate some blocks time.Sleep(5 * time.Second) @@ -127,17 +138,12 @@ func TestExample_Client_ManualUpdate(t *testing.T) { []provider.Provider{primary}, // TODO: primary should not be used here dbs.New(db, chainID), UpdatePeriod(0), - Logger(log.TestingLogger()), + // Logger(log.TestingLogger()), ) if err != nil { stdlog.Fatal(err) } - err = c.Start() - if err != nil { - stdlog.Fatal(err) - } defer func() { - c.Stop() c.Cleanup() }() @@ -158,7 +164,7 @@ func TestExample_Client_ManualUpdate(t *testing.T) { func TestMain(m *testing.M) { // start a tendermint node (and kvstore) in the background to test against app := kvstore.NewApplication() - node := rpctest.StartTendermint(app) + node := rpctest.StartTendermint(app, rpctest.SuppressStdout) code := m.Run() diff --git a/lite2/verifier.go b/lite2/verifier.go index c0732a69c..250c8b204 100644 --- a/lite2/verifier.go +++ b/lite2/verifier.go @@ -10,6 +10,10 @@ import ( "github.com/tendermint/tendermint/types" ) +const ( + maxClockDrift = 10 * time.Second +) + var ( // DefaultTrustLevel - new header can be trusted if at least one correct // validator signed it. @@ -162,10 +166,11 @@ func verifyNewHeaderAndVals( trustedHeader.Time) } - if !untrustedHeader.Time.Before(now) { - return errors.Errorf("new header has a time from the future %v (now: %v)", + if !untrustedHeader.Time.Before(now.Add(maxClockDrift)) { + return errors.Errorf("new header has a time from the future %v (now: %v; max clock drift: %v)", untrustedHeader.Time, - now) + now, + maxClockDrift) } if !bytes.Equal(untrustedHeader.ValidatorsHash, untrustedVals.Hash()) { diff --git a/lite2/verifier_test.go b/lite2/verifier_test.go index 6e118d7ae..241d33b05 100644 --- a/lite2/verifier_test.go +++ b/lite2/verifier_test.go @@ -75,8 +75,19 @@ func TestVerifyAdjacentHeaders(t *testing.T) { nil, "new header has a time from the future", }, - // 3/3 signed -> no error + // new header's time is from the future, but it's acceptable (< maxClockDrift) -> no error 4: { + keys.GenSignedHeader(chainID, nextHeight, + bTime.Add(2*time.Hour).Add(maxClockDrift).Add(-1*time.Millisecond), nil, vals, vals, + []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)), + vals, + 3 * time.Hour, + bTime.Add(2 * time.Hour), + nil, + "", + }, + // 3/3 signed -> no error + 5: { keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, vals, vals, []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)), vals, @@ -86,7 +97,7 @@ func TestVerifyAdjacentHeaders(t *testing.T) { "", }, // 2/3 signed -> no error - 5: { + 6: { keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, vals, vals, []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 1, len(keys)), vals, @@ -96,7 +107,7 @@ func TestVerifyAdjacentHeaders(t *testing.T) { "", }, // 1/3 signed -> error - 6: { + 7: { keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, vals, vals, []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), len(keys)-1, len(keys)), vals, @@ -106,7 +117,7 @@ func TestVerifyAdjacentHeaders(t *testing.T) { "", }, // vals does not match with what we have -> error - 7: { + 8: { keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, keys.ToValidators(10, 1), vals, []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)), keys.ToValidators(10, 1), @@ -116,7 +127,7 @@ func TestVerifyAdjacentHeaders(t *testing.T) { "to match those from new header", }, // vals are inconsistent with newHeader -> error - 8: { + 9: { keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, vals, vals, []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)), keys.ToValidators(10, 1), @@ -126,7 +137,7 @@ func TestVerifyAdjacentHeaders(t *testing.T) { "to match those that were supplied", }, // old header has expired -> error - 9: { + 10: { keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, vals, vals, []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)), keys.ToValidators(10, 1),