mirror of
https://github.com/tendermint/tendermint.git
synced 2026-05-31 11:26:20 +00:00
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user