mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-06 21:36:26 +00:00
light: added more tests for pruning, initialization and bisection (#4978)
This commit is contained in:
@@ -81,6 +81,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi
|
||||
- [rpc] [\#4532](https://github.com/tendermint/tendermint/pull/4923) Support `BlockByHash` query (@fedekunze)
|
||||
- [rpc] \#4979 Support EXISTS operator in `/tx_search` query (@melekes)
|
||||
- [p2p] \#4981 Expose `SaveAs` func on NodeKey (@melekes)
|
||||
- [evidence] [#4821](https://github.com/tendermint/tendermint/pull/4821) Amnesia evidence can be detected, verified and committed (@cmwaters)
|
||||
|
||||
### IMPROVEMENTS:
|
||||
|
||||
|
||||
@@ -477,6 +477,8 @@ func (c *Client) compareWithLatestHeight(height int64) (int64, error) {
|
||||
//
|
||||
// It returns provider.ErrSignedHeaderNotFound if header is not found by
|
||||
// primary.
|
||||
//
|
||||
// It will replace the primary provider if an error from a request to the provider occurs
|
||||
func (c *Client) VerifyHeaderAtHeight(height int64, now time.Time) (*types.SignedHeader, error) {
|
||||
if height <= 0 {
|
||||
return nil, errors.New("negative or zero height")
|
||||
@@ -639,7 +641,7 @@ func (c *Client) sequence(
|
||||
}
|
||||
|
||||
// 2) Verify them
|
||||
c.logger.Debug("Verify newHeader against trustedHeader",
|
||||
c.logger.Debug("Verify adjacent newHeader against trustedHeader",
|
||||
"trustedHeight", trustedHeader.Height,
|
||||
"trustedHash", hash2str(trustedHeader.Hash()),
|
||||
"newHeight", interimHeader.Height,
|
||||
@@ -648,7 +650,7 @@ func (c *Client) sequence(
|
||||
err = VerifyAdjacent(c.chainID, trustedHeader, interimHeader, interimVals,
|
||||
c.trustingPeriod, now, c.maxClockDrift)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("verify adjacent from #%d to #%d failed: %w",
|
||||
err := fmt.Errorf("verify adjacent from #%d to #%d failed: %w",
|
||||
trustedHeader.Height, interimHeader.Height, err)
|
||||
|
||||
switch errors.Unwrap(err).(type) {
|
||||
@@ -657,7 +659,7 @@ func (c *Client) sequence(
|
||||
replaceErr := c.replacePrimaryProvider()
|
||||
if replaceErr != nil {
|
||||
c.logger.Error("Can't replace primary", "err", replaceErr)
|
||||
return err // return original error
|
||||
return fmt.Errorf("%v. Tried to replace primary but: %w", err.Error(), replaceErr)
|
||||
}
|
||||
// attempt to verify header again
|
||||
height--
|
||||
@@ -700,7 +702,7 @@ func (c *Client) bisection(
|
||||
)
|
||||
|
||||
for {
|
||||
c.logger.Debug("Verify newHeader against trustedHeader",
|
||||
c.logger.Debug("Verify non-adjacent newHeader against trustedHeader",
|
||||
"trustedHeight", trustedHeader.Height,
|
||||
"trustedHash", hash2str(trustedHeader.Hash()),
|
||||
"newHeight", headerCache[depth].sh.Height,
|
||||
@@ -752,8 +754,18 @@ func (c *Client) bisection(
|
||||
return fmt.Errorf("verify non adjacent from #%d to #%d failed: %w",
|
||||
trustedHeader.Height, headerCache[depth].sh.Height, err)
|
||||
}
|
||||
newProviderHeader, newProviderVals, err := c.fetchHeaderAndValsAtHeight(newHeader.Height)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !bytes.Equal(newProviderHeader.Hash(), newHeader.Hash()) || !bytes.Equal(newProviderVals.Hash(), newVals.Hash()) {
|
||||
err := fmt.Errorf("replacement provider has a different header: %X and/or vals: %X at height: %d"+
|
||||
"to the one being verified", newProviderHeader.Hash(), newProviderVals.Hash(), newHeader.Height)
|
||||
return fmt.Errorf("verify non adjacent from #%d to #%d failed: %w",
|
||||
trustedHeader.Height, headerCache[depth].sh.Height, err)
|
||||
}
|
||||
// attempt to verify the header again
|
||||
continue
|
||||
return c.bisection(initiallyTrustedHeader, initiallyTrustedVals, newHeader, newVals, now)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("verify non adjacent from #%d to #%d failed: %w",
|
||||
|
||||
@@ -62,6 +62,52 @@ var (
|
||||
largeFullNode = mockp.New(GenMockNode(chainID, 10, 3, 0, bTime))
|
||||
)
|
||||
|
||||
func TestValidateTrustOptions(t *testing.T) {
|
||||
testCases := []struct {
|
||||
err bool
|
||||
to light.TrustOptions
|
||||
}{
|
||||
{
|
||||
false,
|
||||
trustOptions,
|
||||
},
|
||||
{
|
||||
true,
|
||||
light.TrustOptions{
|
||||
Period: -1 * time.Hour,
|
||||
Height: 1,
|
||||
Hash: h1.Hash(),
|
||||
},
|
||||
},
|
||||
{
|
||||
true,
|
||||
light.TrustOptions{
|
||||
Period: 1 * time.Hour,
|
||||
Height: 0,
|
||||
Hash: h1.Hash(),
|
||||
},
|
||||
},
|
||||
{
|
||||
true,
|
||||
light.TrustOptions{
|
||||
Period: 1 * time.Hour,
|
||||
Height: 1,
|
||||
Hash: []byte("incorrect hash"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
err := tc.to.ValidateBasic()
|
||||
if tc.err {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestClient_SequentialVerification(t *testing.T) {
|
||||
newKeys := genPrivKeys(4)
|
||||
newVals := newKeys.ToValidators(10, 1)
|
||||
@@ -296,8 +342,11 @@ func TestClient_SkippingVerification(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// start from a large header to make sure that the pivot height doesn't select a height outside
|
||||
// the appropriate range
|
||||
}
|
||||
|
||||
// start from a large header to make sure that the pivot height doesn't select a height outside
|
||||
// the appropriate range
|
||||
func TestClientLargeBisectionVerification(t *testing.T) {
|
||||
veryLargeFullNode := mockp.New(GenMockNode(chainID, 100, 3, 1, bTime))
|
||||
h1, err := veryLargeFullNode.SignedHeader(90)
|
||||
require.NoError(t, err)
|
||||
@@ -321,6 +370,34 @@ func TestClient_SkippingVerification(t *testing.T) {
|
||||
assert.Equal(t, h, h2)
|
||||
}
|
||||
|
||||
func TestClientBisectionBetweenTrustedHeaders(t *testing.T) {
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
light.TrustOptions{
|
||||
Period: 4 * time.Hour,
|
||||
Height: 1,
|
||||
Hash: h1.Hash(),
|
||||
},
|
||||
fullNode,
|
||||
[]provider.Provider{fullNode},
|
||||
dbs.New(dbm.NewMemDB(), chainID),
|
||||
light.SkippingVerification(light.DefaultTrustLevel),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = c.VerifyHeaderAtHeight(3, bTime.Add(2*time.Hour))
|
||||
require.NoError(t, err)
|
||||
|
||||
// confirm that the client already doesn't have the header
|
||||
_, err = c.TrustedHeader(2)
|
||||
require.Error(t, err)
|
||||
|
||||
// verify using bisection the header between the two trusted headers
|
||||
_, err = c.VerifyHeaderAtHeight(2, bTime.Add(1*time.Hour))
|
||||
assert.NoError(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestClient_Cleanup(t *testing.T) {
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
@@ -514,12 +591,11 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) {
|
||||
func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) {
|
||||
// 1. options.Hash == trustedHeader.Hash
|
||||
{
|
||||
// load the first three headers into the trusted store
|
||||
trustedStore := dbs.New(dbm.NewMemDB(), chainID)
|
||||
err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals)
|
||||
require.NoError(t, err)
|
||||
|
||||
//header2 := keys.GenSignedHeader(chainID, 2, bTime.Add(2*time.Hour), nil, vals, vals,
|
||||
// []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys))
|
||||
err = trustedStore.SaveSignedHeaderAndValidatorSet(h2, vals)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -554,6 +630,10 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) {
|
||||
valSet, _, err = c.TrustedValidatorSet(2)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, valSet)
|
||||
|
||||
h, err = c.TrustedHeader(3)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, h)
|
||||
}
|
||||
|
||||
// 2. options.Hash != trustedHeader.Hash
|
||||
@@ -907,26 +987,60 @@ func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) {
|
||||
// header should still be verified
|
||||
assert.EqualValues(t, 2, h.Height)
|
||||
|
||||
// no witnesses left to verify -> error
|
||||
// remaining withness doesn't have header -> error
|
||||
_, err = c.VerifyHeaderAtHeight(3, bTime.Add(2*time.Hour))
|
||||
assert.Error(t, err)
|
||||
if assert.Error(t, err) {
|
||||
assert.Equal(t, "awaiting response from all witnesses exceeded dropout time", err.Error())
|
||||
}
|
||||
assert.EqualValues(t, 0, len(c.Witnesses()))
|
||||
|
||||
// no witnesses left, will not be allowed to verify a header
|
||||
_, err = c.VerifyHeaderAtHeight(3, bTime.Add(2*time.Hour))
|
||||
if assert.Error(t, err) {
|
||||
assert.Equal(t, "no witnesses connected. please reset light client", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientTrustedValidatorSet(t *testing.T) {
|
||||
noValSetNode := mockp.New(
|
||||
chainID,
|
||||
headerSet,
|
||||
map[int64]*types.ValidatorSet{
|
||||
1: nil,
|
||||
2: nil,
|
||||
3: nil,
|
||||
},
|
||||
)
|
||||
|
||||
differentVals, _ := types.RandValidatorSet(10, 100)
|
||||
|
||||
badValSetNode := mockp.New(
|
||||
chainID,
|
||||
headerSet,
|
||||
map[int64]*types.ValidatorSet{
|
||||
1: vals,
|
||||
2: differentVals,
|
||||
3: differentVals,
|
||||
},
|
||||
)
|
||||
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
trustOptions,
|
||||
fullNode,
|
||||
[]provider.Provider{fullNode},
|
||||
noValSetNode,
|
||||
[]provider.Provider{badValSetNode, fullNode, fullNode},
|
||||
dbs.New(dbm.NewMemDB(), chainID),
|
||||
light.Logger(log.TestingLogger()),
|
||||
)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 2, len(c.Witnesses()))
|
||||
|
||||
_, err = c.VerifyHeaderAtHeight(2, bTime.Add(2*time.Hour).Add(1*time.Second))
|
||||
require.NoError(t, err)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, 1, len(c.Witnesses()))
|
||||
|
||||
_, err = c.VerifyHeaderAtHeight(2, bTime.Add(2*time.Hour).Add(1*time.Second))
|
||||
assert.NoError(t, err)
|
||||
|
||||
valSet, height, err := c.TrustedValidatorSet(0)
|
||||
assert.NoError(t, err)
|
||||
@@ -974,6 +1088,28 @@ func TestClientReportsConflictingHeadersEvidence(t *testing.T) {
|
||||
assert.True(t, fullNode.HasEvidence(ev))
|
||||
}
|
||||
|
||||
func TestClientPrunesHeadersAndValidatorSets(t *testing.T) {
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
trustOptions,
|
||||
fullNode,
|
||||
[]provider.Provider{fullNode},
|
||||
dbs.New(dbm.NewMemDB(), chainID),
|
||||
light.Logger(log.TestingLogger()),
|
||||
light.PruningSize(1),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
_, err = c.TrustedHeader(1)
|
||||
require.NoError(t, err)
|
||||
|
||||
h, err := c.Update(bTime.Add(2 * time.Hour))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(3), h.Height)
|
||||
|
||||
_, err = c.TrustedHeader(1)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestClientEnsureValidHeadersAndValSets(t *testing.T) {
|
||||
emptyValSet := &types.ValidatorSet{
|
||||
Validators: nil,
|
||||
|
||||
Reference in New Issue
Block a user