From b5f6bfa4f97e6f027f45938bb38238ff820f9543 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Thu, 27 Feb 2020 16:10:01 +0100 Subject: [PATCH] lite2: return height as 2nd return param in TrustedValidatorSet (#4479) Closes #4473 --- .../adr-046-light-client-implementation.md | 2 +- lite2/client.go | 17 +-- lite2/client_test.go | 101 ++++++++++++++++-- 3 files changed, 103 insertions(+), 17 deletions(-) diff --git a/docs/architecture/adr-046-light-client-implementation.md b/docs/architecture/adr-046-light-client-implementation.md index dbae7e641..dca1c6692 100644 --- a/docs/architecture/adr-046-light-client-implementation.md +++ b/docs/architecture/adr-046-light-client-implementation.md @@ -28,7 +28,7 @@ type Client interface { // get trusted headers & validators TrustedHeader(height int64) (*types.SignedHeader, error) - TrustedValidatorSet(height int64) (*types.ValidatorSet, error) + TrustedValidatorSet(height int64) (valSet *types.ValidatorSet, heightUsed int64, err error) LastTrustedHeight() (int64, error) FirstTrustedHeight() (int64, error) diff --git a/lite2/client.go b/lite2/client.go index c52ffe832..b70123099 100644 --- a/lite2/client.go +++ b/lite2/client.go @@ -433,7 +433,8 @@ func (c *Client) TrustedHeader(height int64) (*types.SignedHeader, error) { } // TrustedValidatorSet returns a trusted validator set at the given height (0 - -// latest). +// latest). The second return parameter is the height used (useful if 0 was +// passed; otherwise can be ignored). // // height must be >= 0. // @@ -448,18 +449,22 @@ func (c *Client) TrustedHeader(height int64) (*types.SignedHeader, error) { // - header signed by that validator set has not been verified yet // // Safe for concurrent use by multiple goroutines. -func (c *Client) TrustedValidatorSet(height int64) (*types.ValidatorSet, error) { - height, err := c.compareWithLatestHeight(height) +func (c *Client) TrustedValidatorSet(height int64) (valSet *types.ValidatorSet, heightUsed int64, err error) { + heightUsed, err = c.compareWithLatestHeight(height) if err != nil { - return nil, err + return nil, heightUsed, err + } + valSet, err = c.trustedStore.ValidatorSet(heightUsed) + if err != nil { + return nil, heightUsed, err } - return c.trustedStore.ValidatorSet(height) + return valSet, heightUsed, err } func (c *Client) compareWithLatestHeight(height int64) (int64, error) { latestHeight, err := c.LastTrustedHeight() if err != nil { - return 0, err + return 0, errors.Wrap(err, "can't get last trusted height") } if latestHeight == -1 { return 0, errors.New("no headers exist") diff --git a/lite2/client_test.go b/lite2/client_test.go index ad9088e8c..b1079390a 100644 --- a/lite2/client_test.go +++ b/lite2/client_test.go @@ -47,7 +47,9 @@ var ( } headerSet = map[int64]*types.SignedHeader{ 1: h1, + // interim header (3/3 signed) 2: h2, + // last header (3/3 signed) 3: h3, } fullNode = mockp.New( @@ -71,14 +73,7 @@ func TestClient_SequentialVerification(t *testing.T) { }{ { "good", - map[int64]*types.SignedHeader{ - // trusted header - 1: h1, - // interim header (3/3 signed) - 2: h2, - // last header (3/3 signed) - 3: h3, - }, + headerSet, valSet, false, false, @@ -322,10 +317,14 @@ func TestClient_Cleanup(t *testing.T) { err = c.Cleanup() require.NoError(t, err) - // Check no headers exist after Cleanup. + // Check no headers/valsets exist after Cleanup. h, err := c.TrustedHeader(1) assert.Error(t, err) assert.Nil(t, h) + + valSet, _, err := c.TrustedValidatorSet(1) + assert.Error(t, err) + assert.Nil(t, valSet) } // trustedHeader.Height == options.Height @@ -350,6 +349,13 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, h) assert.Equal(t, h.Hash(), h1.Hash()) + + valSet, _, err := c.TrustedValidatorSet(1) + assert.NoError(t, err) + assert.NotNil(t, valSet) + if assert.NotNil(t, valSet) { + assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash()) + } } // 2. options.Hash != trustedHeader.Hash @@ -390,6 +396,13 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) { if assert.NotNil(t, h) { assert.Equal(t, h.Hash(), header1.Hash()) } + + valSet, _, err := c.TrustedValidatorSet(1) + assert.NoError(t, err) + assert.NotNil(t, valSet) + if assert.NotNil(t, valSet) { + assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash()) + } } } @@ -423,6 +436,13 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, h) assert.Equal(t, h.Hash(), h1.Hash()) + + valSet, _, err := c.TrustedValidatorSet(1) + assert.NoError(t, err) + assert.NotNil(t, valSet) + if assert.NotNil(t, valSet) { + assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash()) + } } // 2. options.Hash != trustedHeader.Hash @@ -469,6 +489,10 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) { h, err := c.TrustedHeader(1) assert.Error(t, err) assert.Nil(t, h) + + valSet, _, err := c.TrustedValidatorSet(1) + assert.Error(t, err) + assert.Nil(t, valSet) } } @@ -504,10 +528,21 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) { assert.NotNil(t, h) assert.Equal(t, h.Hash(), h1.Hash()) + valSet, _, err := c.TrustedValidatorSet(1) + assert.NoError(t, err) + assert.NotNil(t, valSet) + if assert.NotNil(t, valSet) { + assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash()) + } + // Check we no longer have 2nd header (+header2+). h, err = c.TrustedHeader(2) assert.Error(t, err) assert.Nil(t, h) + + valSet, _, err = c.TrustedValidatorSet(2) + assert.Error(t, err) + assert.Nil(t, valSet) } // 2. options.Hash != trustedHeader.Hash @@ -557,10 +592,21 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) { assert.NotNil(t, h) assert.Equal(t, h.Hash(), header1.Hash()) + valSet, _, err := c.TrustedValidatorSet(1) + assert.NoError(t, err) + assert.NotNil(t, valSet) + if assert.NotNil(t, valSet) { + assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash()) + } + // Check we no longer have invalid 2nd header (+header2+). h, err = c.TrustedHeader(2) assert.Error(t, err) assert.Nil(t, h) + + valSet, _, err = c.TrustedValidatorSet(2) + assert.Error(t, err) + assert.Nil(t, valSet) } } @@ -586,6 +632,12 @@ func TestClient_Update(t *testing.T) { assert.NoError(t, err) require.NotNil(t, h) assert.EqualValues(t, 3, h.Height) + + valSet, _, err := c.TrustedValidatorSet(3) + assert.NoError(t, err) + if assert.NotNil(t, valSet) { + assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash()) + } } func TestClient_Concurrency(t *testing.T) { @@ -627,7 +679,7 @@ func TestClient_Concurrency(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, h) - vals, err := c.TrustedValidatorSet(2) + vals, _, err := c.TrustedValidatorSet(2) assert.NoError(t, err) assert.NotNil(t, vals) }() @@ -784,6 +836,13 @@ func TestClient_NewClientFromTrustedStore(t *testing.T) { h, err := c.TrustedHeader(1) assert.NoError(t, err) assert.EqualValues(t, 1, h.Height) + + valSet, _, err := c.TrustedValidatorSet(1) + assert.NoError(t, err) + assert.NotNil(t, valSet) + if assert.NotNil(t, valSet) { + assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash()) + } } func TestNewClientErrorsIfAllWitnessesUnavailable(t *testing.T) { @@ -857,3 +916,25 @@ func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) { assert.Error(t, err) assert.EqualValues(t, 0, len(c.Witnesses())) } + +func TestClientTrustedValidatorSet(t *testing.T) { + c, err := NewClient( + chainID, + trustOptions, + fullNode, + []provider.Provider{fullNode}, + dbs.New(dbm.NewMemDB(), chainID), + UpdatePeriod(0), + Logger(log.TestingLogger()), + ) + + require.NoError(t, err) + + _, err = c.VerifyHeaderAtHeight(2, bTime.Add(2*time.Hour).Add(1*time.Second)) + require.NoError(t, err) + + valSet, height, err := c.TrustedValidatorSet(0) + assert.NoError(t, err) + assert.NotNil(t, valSet) + assert.EqualValues(t, 2, height) +}