diff --git a/lite2/client.go b/lite2/client.go index ad7f8415f..b8ef6e807 100644 --- a/lite2/client.go +++ b/lite2/client.go @@ -209,16 +209,6 @@ func NewClient( } } - if c.removeNoLongerTrustedHeadersPeriod > 0 { - c.routinesWaitGroup.Add(1) - go c.removeNoLongerTrustedHeadersRoutine() - } - - if c.updatePeriod > 0 { - c.routinesWaitGroup.Add(1) - go c.autoUpdateRoutine() - } - return c, nil } @@ -367,10 +357,30 @@ func (c *Client) initializeWithTrustOptions(options TrustOptions) error { return c.updateTrustedHeaderAndVals(h, nextVals) } -// Stop stops all the goroutines. If you wish to remove all the data, call -// Cleanup. +// Start starts two processes: 1) auto updating 2) removing outdated headers. +func (c *Client) Start() error { + c.logger.Info("Starting light client") + + if c.removeNoLongerTrustedHeadersPeriod > 0 { + c.routinesWaitGroup.Add(1) + go c.removeNoLongerTrustedHeadersRoutine() + } + + if c.updatePeriod > 0 { + c.routinesWaitGroup.Add(1) + go c.autoUpdateRoutine() + } + + return nil +} + +// Stop stops two processes: 1) auto updating 2) removing outdated headers. +// Stop only returns after both of them are finished running. If you wish to +// remove all the data, call Cleanup. func (c *Client) Stop() { + c.logger.Info("Stopping light client") close(c.quit) + c.routinesWaitGroup.Wait() } // TrustedHeader returns a trusted header at the given height (0 - the latest) @@ -415,6 +425,30 @@ func (c *Client) TrustedHeader(height int64, now time.Time) (*types.SignedHeader return h, nil } +// TrustedValidatorSet returns a trusted validator set at the given height (0 - +// the latest) or nil if no such validator set exist. The second return +// parameter is height validator set corresponds to (useful when you pass 0). +// +// height must be >= 0. +// +// It returns an error if: +// - header signed by that validator set expired (ErrOldHeaderExpired) +// - there are some issues with the trusted store, although that should not +// happen normally; +// - negative height is passed; +// - validator set is not found. +// +// Safe for concurrent use by multiple goroutines. +func (c *Client) TrustedValidatorSet(height int64, now time.Time) (*types.ValidatorSet, error) { + // Checks height is positive and header is not expired. + _, err := c.TrustedHeader(height, now) + if err != nil { + return nil, err + } + + return c.trustedStore.ValidatorSet(height) +} + // LastTrustedHeight returns a last trusted height. -1 and nil are returned if // there are no trusted headers. // @@ -511,12 +545,10 @@ func (c *Client) VerifyHeader(newHeader *types.SignedHeader, newVals *types.Vali return c.updateTrustedHeaderAndVals(newHeader, nextVals) } -// Cleanup removes all the data (headers and validator sets) stored. It blocks -// until internal routines are finished. Note: the client must be stopped at -// this point. +// Cleanup removes all the data (headers and validator sets) stored. Note: the +// client must be stopped at this point. func (c *Client) Cleanup() error { - c.routinesWaitGroup.Wait() - c.logger.Info("Cleanup everything") + c.logger.Info("Removing all the data") return c.cleanup(0) } diff --git a/lite2/client_test.go b/lite2/client_test.go index 3065b76c0..d9f58347c 100644 --- a/lite2/client_test.go +++ b/lite2/client_test.go @@ -137,6 +137,8 @@ func TestClient_SequentialVerification(t *testing.T) { } else { require.NoError(t, err) } + err = c.Start() + require.NoError(t, err) defer c.Stop() _, err = c.VerifyHeaderAtHeight(3, bTime.Add(3*time.Hour)) @@ -235,6 +237,8 @@ func TestClient_SkippingVerification(t *testing.T) { } else { require.NoError(t, err) } + err = c.Start() + require.NoError(t, err) defer c.Stop() _, err = c.VerifyHeaderAtHeight(3, bTime.Add(3*time.Hour)) @@ -290,6 +294,8 @@ func TestClientRemovesNoLongerTrustedHeaders(t *testing.T) { Logger(log.TestingLogger()), ) require.NoError(t, err) + err = c.Start() + require.NoError(t, err) defer c.Stop() // Verify new headers. @@ -349,6 +355,8 @@ func TestClient_Cleanup(t *testing.T) { Logger(log.TestingLogger()), ) require.NoError(t, err) + err = c.Start() + require.NoError(t, err) c.Stop() c.Cleanup() @@ -402,6 +410,9 @@ func TestClientRestoreTrustedHeaderAfterStartup1(t *testing.T) { Logger(log.TestingLogger()), ) require.NoError(t, err) + err = c.Start() + require.NoError(t, err) + defer c.Stop() h, err := c.TrustedHeader(1, bTime.Add(1*time.Second)) assert.NoError(t, err) @@ -441,6 +452,9 @@ func TestClientRestoreTrustedHeaderAfterStartup1(t *testing.T) { Logger(log.TestingLogger()), ) require.NoError(t, err) + err = c.Start() + require.NoError(t, err) + defer c.Stop() h, err := c.TrustedHeader(1, bTime.Add(1*time.Second)) assert.NoError(t, err) @@ -496,6 +510,9 @@ func TestClientRestoreTrustedHeaderAfterStartup2(t *testing.T) { Logger(log.TestingLogger()), ) require.NoError(t, err) + err = c.Start() + require.NoError(t, err) + defer c.Stop() // Check we still have the 1st header (+header+). h, err := c.TrustedHeader(1, bTime.Add(2*time.Hour).Add(1*time.Second)) @@ -541,6 +558,9 @@ func TestClientRestoreTrustedHeaderAfterStartup2(t *testing.T) { Logger(log.TestingLogger()), ) require.NoError(t, err) + err = c.Start() + require.NoError(t, err) + defer c.Stop() // Check we no longer have the invalid 1st header (+header+). h, err := c.TrustedHeader(1, bTime.Add(2*time.Hour).Add(1*time.Second)) @@ -598,6 +618,9 @@ func TestClientRestoreTrustedHeaderAfterStartup3(t *testing.T) { Logger(log.TestingLogger()), ) require.NoError(t, err) + err = c.Start() + require.NoError(t, err) + defer c.Stop() // Check we still have the 1st header (+header+). h, err := c.TrustedHeader(1, bTime.Add(2*time.Hour).Add(1*time.Second)) @@ -648,6 +671,9 @@ func TestClientRestoreTrustedHeaderAfterStartup3(t *testing.T) { Logger(log.TestingLogger()), ) require.NoError(t, err) + err = c.Start() + require.NoError(t, err) + defer c.Stop() // Check we have swapped invalid 1st header (+header+) with correct one (+header1+). h, err := c.TrustedHeader(1, bTime.Add(2*time.Hour).Add(1*time.Second)) @@ -706,6 +732,8 @@ func TestClient_Update(t *testing.T) { Logger(log.TestingLogger()), ) require.NoError(t, err) + err = c.Start() + require.NoError(t, err) defer c.Stop() // should result in downloading & verifying header #3 @@ -763,6 +791,13 @@ func TestClient_Concurrency(t *testing.T) { Logger(log.TestingLogger()), ) require.NoError(t, err) + err = c.Start() + require.NoError(t, err) + defer c.Stop() + + _, err = c.VerifyHeaderAtHeight(2, bTime.Add(2*time.Hour)) + require.NoError(t, err) + var wg sync.WaitGroup for i := 0; i < 100; i++ { wg.Add(1) @@ -783,6 +818,10 @@ func TestClient_Concurrency(t *testing.T) { h, err := c.TrustedHeader(1, bTime.Add(2*time.Hour)) assert.NoError(t, err) assert.NotNil(t, h) + + vals, err := c.TrustedValidatorSet(2, bTime.Add(2*time.Hour)) + assert.NoError(t, err) + assert.NotNil(t, vals) }() } diff --git a/lite2/example_test.go b/lite2/example_test.go index 48804c77d..4a291236b 100644 --- a/lite2/example_test.go +++ b/lite2/example_test.go @@ -63,6 +63,10 @@ func TestExample_Client_AutoUpdate(t *testing.T) { if err != nil { stdlog.Fatal(err) } + err = c.Start() + if err != nil { + stdlog.Fatal(err) + } defer func() { c.Stop() c.Cleanup() @@ -125,6 +129,10 @@ func TestExample_Client_ManualUpdate(t *testing.T) { if err != nil { stdlog.Fatal(err) } + err = c.Start() + if err != nil { + stdlog.Fatal(err) + } defer func() { c.Stop() c.Cleanup()