Browse Source

lite2: cross-check first header and update tests (#4471)

closes #4464
pull/4476/head
Callum Waters 4 years ago
committed by GitHub
parent
commit
9231b52e0d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 42 additions and 21 deletions
  1. +3
    -1
      docs/architecture/adr-046-light-client-implementation.md
  2. +5
    -0
      lite2/client.go
  3. +34
    -20
      lite2/client_test.go

+ 3
- 1
docs/architecture/adr-046-light-client-implementation.md View File

@ -2,6 +2,7 @@
## Changelog
* 13-02-2020: Initial draft
* 26-02-2020: Cross-checking the first header
## Context
@ -62,7 +63,8 @@ func NewClient(
made to increase security by default. At least one witness is required,
although, right now, the light client does not check that primary != witness.
When cross-checking a new header with witnesses, minimum number of witnesses
required to respond: 1.
required to respond: 1. Note the very first header (`TrustOptions.Hash`) is
also cross-checked with witnesses for additional security.
Due to bisection algorithm nature, some headers might be skipped. If the light
client does not have a header for height `X` and `TrustedHeader(X)` or


+ 5
- 0
lite2/client.go View File

@ -356,6 +356,11 @@ func (c *Client) initializeWithTrustOptions(options TrustOptions) error {
return errors.Errorf("expected header's hash %X, but got %X", options.Hash, h.Hash())
}
err = c.compareNewHeaderWithWitnesses(h)
if err != nil {
return err
}
// 2) Fetch and verify the vals.
vals, err := c.validatorSetFromPrimary(options.Height)
if err != nil {


+ 34
- 20
lite2/client_test.go View File

@ -802,44 +802,49 @@ func TestClient_NewClientFromTrustedStore(t *testing.T) {
assert.EqualValues(t, 1, h.Height)
}
func TestClientUpdateErrorsIfAllWitnessesUnavailable(t *testing.T) {
c, err := NewClient(
func TestNewClientErrorsIfAllWitnessesUnavailable(t *testing.T) {
_, err := NewClient(
chainID,
trustOptions,
fullNode,
[]provider.Provider{deadNode, deadNode, deadNode},
[]provider.Provider{deadNode, deadNode},
dbs.New(dbm.NewMemDB(), chainID),
UpdatePeriod(0),
Logger(log.TestingLogger()),
MaxRetryAttempts(1),
)
require.NoError(t, err)
err = c.Update(bTime.Add(2 * time.Hour))
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "awaiting response from all witnesses exceeded dropout time")
}
}
func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) {
// straight invalid header
// different headers hash then primary plus less than 1/3 signed (no fork)
badProvider1 := mockp.New(
chainID,
map[int64]*types.SignedHeader{
3: {Header: nil, Commit: nil},
1: h1,
2: keys.GenSignedHeaderLastBlockID(chainID, 2, bTime.Add(30*time.Minute), nil, vals, vals,
[]byte("app_hash2"), []byte("cons_hash"), []byte("results_hash"),
len(keys), len(keys), types.BlockID{Hash: h1.Hash()}),
},
map[int64]*types.ValidatorSet{
1: vals,
2: vals,
},
map[int64]*types.ValidatorSet{},
)
// less than 1/3 signed
// header is empty
badProvider2 := mockp.New(
chainID,
map[int64]*types.SignedHeader{
3: keys.GenSignedHeaderLastBlockID(chainID, 3, bTime.Add(1*time.Hour), nil, vals, vals,
[]byte("app_hash2"), []byte("cons_hash"), []byte("results_hash"),
len(keys), len(keys), types.BlockID{Hash: h2.Hash()}),
1: h1,
2: h2,
3: {Header: nil, Commit: nil},
},
map[int64]*types.ValidatorSet{
1: vals,
2: vals,
},
map[int64]*types.ValidatorSet{},
)
c, err := NewClient(
@ -850,12 +855,21 @@ func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) {
dbs.New(dbm.NewMemDB(), chainID),
UpdatePeriod(0),
Logger(log.TestingLogger()),
MaxRetryAttempts(1),
)
// witness should have behaved properly -> no error
require.NoError(t, err)
assert.EqualValues(t, 2, len(c.Witnesses()))
err = c.Update(bTime.Add(2 * time.Hour))
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "could not find any witnesses")
}
assert.Zero(t, 0, len(c.Witnesses()))
// witness behaves incorrectly -> removed from list, no error
h, err := c.VerifyHeaderAtHeight(2, bTime.Add(2*time.Hour))
assert.NoError(t, err)
assert.EqualValues(t, 1, len(c.Witnesses()))
// header should still be verified
assert.EqualValues(t, 2, h.Height)
// no witnesses left to verify -> error
_, err = c.VerifyHeaderAtHeight(3, bTime.Add(2*time.Hour))
assert.Error(t, err)
assert.EqualValues(t, 0, len(c.Witnesses()))
}

Loading…
Cancel
Save