mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-03 11:45:18 +00:00
lite2: store current validator set (#4472)
Before we were storing trustedHeader (height=1) and trustedNextVals (height=2). After this change, we will be storing trustedHeader (height=1) and trustedVals (height=1). This a) simplifies the code b) fixes #4399 inconsistent pairing issue c) gives a relayer access to the current validator set #4470. The only downside is more jumps during bisection. If validator set changes between trustedHeader and the next header (by 2/3 or more), the light client will be forced to download the next header and check that 2/3+ signed the transition. But we don't expect validator set change too much and too often, so it's an acceptable compromise. Closes #4470 and #4399
This commit is contained in:
@@ -91,8 +91,8 @@ The light client stores headers & validators in the trusted store:
|
||||
|
||||
```go
|
||||
type Store interface {
|
||||
SaveSignedHeaderAndNextValidatorSet(sh *types.SignedHeader, valSet *types.ValidatorSet) error
|
||||
DeleteSignedHeaderAndNextValidatorSet(height int64) error
|
||||
SaveSignedHeaderAndValidatorSet(sh *types.SignedHeader, valSet *types.ValidatorSet) error
|
||||
DeleteSignedHeaderAndValidatorSet(height int64) error
|
||||
|
||||
SignedHeader(height int64) (*types.SignedHeader, error)
|
||||
ValidatorSet(height int64) (*types.ValidatorSet, error)
|
||||
|
||||
175
lite2/client.go
175
lite2/client.go
@@ -122,8 +122,8 @@ type Client struct {
|
||||
trustedStore store.Store
|
||||
// Highest trusted header from the store (height=H).
|
||||
latestTrustedHeader *types.SignedHeader
|
||||
// Highest next validator set from the store (height=H+1).
|
||||
latestTrustedNextVals *types.ValidatorSet
|
||||
// Highest validator set from the store (height=H).
|
||||
latestTrustedVals *types.ValidatorSet
|
||||
|
||||
// See UpdatePeriod option
|
||||
updatePeriod time.Duration
|
||||
@@ -228,15 +228,16 @@ func NewClientFromTrustedStore(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := c.restoreTrustedHeaderAndNextVals(); err != nil {
|
||||
if err := c.restoreTrustedHeaderAndVals(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Load trustedHeader and trustedNextVals from trustedStore.
|
||||
func (c *Client) restoreTrustedHeaderAndNextVals() error {
|
||||
// restoreTrustedHeaderAndVals loads trustedHeader and trustedVals from
|
||||
// trustedStore.
|
||||
func (c *Client) restoreTrustedHeaderAndVals() error {
|
||||
lastHeight, err := c.trustedStore.LastSignedHeaderHeight()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "can't get last trusted header height")
|
||||
@@ -248,15 +249,15 @@ func (c *Client) restoreTrustedHeaderAndNextVals() error {
|
||||
return errors.Wrap(err, "can't get last trusted header")
|
||||
}
|
||||
|
||||
trustedNextVals, err := c.trustedStore.ValidatorSet(lastHeight + 1)
|
||||
trustedVals, err := c.trustedStore.ValidatorSet(lastHeight)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "can't get last trusted next validators")
|
||||
return errors.Wrap(err, "can't get last trusted validators")
|
||||
}
|
||||
|
||||
c.latestTrustedHeader = trustedHeader
|
||||
c.latestTrustedNextVals = trustedNextVals
|
||||
c.latestTrustedVals = trustedVals
|
||||
|
||||
c.logger.Debug("Restored trusted header and next vals", lastHeight)
|
||||
c.logger.Debug("Restored trusted header and vals", "height", lastHeight)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -334,7 +335,9 @@ func (c *Client) checkTrustedHeaderUsingOptions(options TrustOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fetch trustedHeader and trustedNextVals from primary provider.
|
||||
// initializeWithTrustOptions fetches the weakly-trusted header and vals from
|
||||
// primary provider. The header is cross-checked with witnesses for additional
|
||||
// security.
|
||||
func (c *Client) initializeWithTrustOptions(options TrustOptions) error {
|
||||
// 1) Fetch and verify the header.
|
||||
h, err := c.signedHeaderFromPrimary(options.Height)
|
||||
@@ -372,15 +375,8 @@ func (c *Client) initializeWithTrustOptions(options TrustOptions) error {
|
||||
return errors.Wrap(err, "invalid commit")
|
||||
}
|
||||
|
||||
// 3) Fetch and verify the next vals (verification happens in
|
||||
// updateTrustedHeaderAndNextVals).
|
||||
nextVals, err := c.validatorSetFromPrimary(options.Height + 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 4) Persist both of them and continue.
|
||||
return c.updateTrustedHeaderAndNextVals(h, nextVals)
|
||||
// 3) Persist both of them and continue.
|
||||
return c.updateTrustedHeaderAndVals(h, vals)
|
||||
}
|
||||
|
||||
// Start starts two processes: 1) auto updating 2) removing outdated headers.
|
||||
@@ -463,9 +459,7 @@ func (c *Client) TrustedHeader(height int64, now time.Time) (*types.SignedHeader
|
||||
|
||||
// TrustedValidatorSet returns a trusted validator set at the given height. If
|
||||
// a validator set is missing in trustedStore (e.g. the associated header was
|
||||
// skipped during bisection), it will be downloaded from primary. The second
|
||||
// return parameter is height validator set corresponds to (useful when you
|
||||
// pass 0).
|
||||
// skipped during bisection), it will be downloaded from primary.
|
||||
//
|
||||
// height must be >= 0.
|
||||
//
|
||||
@@ -482,10 +476,10 @@ func (c *Client) TrustedHeader(height int64, now time.Time) (*types.SignedHeader
|
||||
//
|
||||
// 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 (note: height - 1) is not expired.
|
||||
// Checks height is positive and header is not expired.
|
||||
// Additionally, it fetches validator set from primary if it's missing in
|
||||
// store.
|
||||
_, err := c.TrustedHeader(height-1, now)
|
||||
_, err := c.TrustedHeader(height, now)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -528,10 +522,12 @@ func (c *Client) VerifyHeaderAtHeight(height int64, now time.Time) (*types.Signe
|
||||
return nil, errors.New("negative or zero height")
|
||||
}
|
||||
|
||||
// Check if header already verified.
|
||||
h, err := c.TrustedHeader(height, now)
|
||||
switch err.(type) {
|
||||
case nil: // Return already trusted header
|
||||
case nil:
|
||||
c.logger.Info("Header has already been verified", "height", height, "hash", hash2str(h.Hash()))
|
||||
// Return already trusted header
|
||||
return h, nil
|
||||
case ErrOldHeaderExpired:
|
||||
return nil, err
|
||||
@@ -565,14 +561,11 @@ func (c *Client) VerifyHeaderAtHeight(height int64, now time.Time) (*types.Signe
|
||||
// If, at any moment, SignedHeader or ValidatorSet are not found by the primary
|
||||
// provider, provider.ErrSignedHeaderNotFound /
|
||||
// provider.ErrValidatorSetNotFound error is returned.
|
||||
//
|
||||
// NOTE: although newVals is entered as input, trustedStore will only store the
|
||||
// validator set at height newHeader.Height+1 (i.e.
|
||||
// newHeader.NextValidatorsHash).
|
||||
func (c *Client) VerifyHeader(newHeader *types.SignedHeader, newVals *types.ValidatorSet, now time.Time) error {
|
||||
// Check if newHeader already verified.
|
||||
h, err := c.TrustedHeader(newHeader.Height, now)
|
||||
switch err.(type) {
|
||||
case nil: // Return already trusted header
|
||||
case nil:
|
||||
// Make sure it's the same header.
|
||||
if !bytes.Equal(h.Hash(), newHeader.Hash()) {
|
||||
return errors.Errorf("existing trusted header %X does not match newHeader %X", h.Hash(), newHeader.Hash())
|
||||
@@ -597,9 +590,9 @@ func (c *Client) verifyHeader(newHeader *types.SignedHeader, newVals *types.Vali
|
||||
if newHeader.Height >= c.latestTrustedHeader.Height {
|
||||
switch c.verificationMode {
|
||||
case sequential:
|
||||
err = c.sequence(c.latestTrustedHeader, c.latestTrustedNextVals, newHeader, newVals, now)
|
||||
err = c.sequence(c.latestTrustedHeader, newHeader, newVals, now)
|
||||
case skipping:
|
||||
err = c.bisection(c.latestTrustedHeader, c.latestTrustedNextVals, newHeader, newVals, now)
|
||||
err = c.bisection(c.latestTrustedHeader, c.latestTrustedVals, newHeader, newVals, now)
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown verification mode: %b", c.verificationMode))
|
||||
}
|
||||
@@ -624,13 +617,7 @@ func (c *Client) verifyHeader(newHeader *types.SignedHeader, newVals *types.Vali
|
||||
return err
|
||||
}
|
||||
|
||||
// Update trusted header and vals.
|
||||
nextVals, err := c.validatorSetFromPrimary(newHeader.Height + 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.updateTrustedHeaderAndNextVals(newHeader, nextVals)
|
||||
return c.updateTrustedHeaderAndVals(newHeader, newVals)
|
||||
}
|
||||
|
||||
// Primary returns the primary provider.
|
||||
@@ -679,7 +666,7 @@ func (c *Client) cleanup(stopHeight int64) error {
|
||||
stopHeight = oldestHeight
|
||||
}
|
||||
for height := stopHeight; height <= latestHeight; height++ {
|
||||
err = c.trustedStore.DeleteSignedHeaderAndNextValidatorSet(height)
|
||||
err = c.trustedStore.DeleteSignedHeaderAndValidatorSet(height)
|
||||
if err != nil {
|
||||
c.logger.Error("can't remove a trusted header & validator set", "err", err, "height", height)
|
||||
continue
|
||||
@@ -687,8 +674,8 @@ func (c *Client) cleanup(stopHeight int64) error {
|
||||
}
|
||||
|
||||
c.latestTrustedHeader = nil
|
||||
c.latestTrustedNextVals = nil
|
||||
err = c.restoreTrustedHeaderAndNextVals()
|
||||
c.latestTrustedVals = nil
|
||||
err = c.restoreTrustedHeaderAndVals()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -698,51 +685,46 @@ func (c *Client) cleanup(stopHeight int64) error {
|
||||
|
||||
// see VerifyHeader
|
||||
func (c *Client) sequence(
|
||||
trustedHeader *types.SignedHeader,
|
||||
trustedNextVals *types.ValidatorSet,
|
||||
initiallyTrustedHeader *types.SignedHeader,
|
||||
newHeader *types.SignedHeader,
|
||||
newVals *types.ValidatorSet,
|
||||
now time.Time) error {
|
||||
|
||||
// 1) Verify any intermediate headers.
|
||||
var (
|
||||
interimHeader *types.SignedHeader
|
||||
interimNextVals *types.ValidatorSet
|
||||
err error
|
||||
trustedHeader = initiallyTrustedHeader
|
||||
|
||||
interimHeader *types.SignedHeader
|
||||
interimVals *types.ValidatorSet
|
||||
|
||||
err error
|
||||
)
|
||||
for height := trustedHeader.Height + 1; height <= newHeader.Height; height++ {
|
||||
interimHeader, err = c.signedHeaderFromPrimary(height)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to obtain the header #%d", height)
|
||||
|
||||
for height := initiallyTrustedHeader.Height + 1; height <= newHeader.Height; height++ {
|
||||
// 1) Fetch interim headers and vals if needed.
|
||||
if height == newHeader.Height { // last header
|
||||
interimHeader, interimVals = newHeader, newVals
|
||||
} else { // intermediate headers
|
||||
interimHeader, interimVals, err = c.fetchHeaderAndValsAtHeight(height)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to obtain the header #%d", height)
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Verify them
|
||||
c.logger.Debug("Verify newHeader against trustedHeader",
|
||||
"trustedHeight", c.latestTrustedHeader.Height,
|
||||
"trustedHash", hash2str(c.latestTrustedHeader.Hash()),
|
||||
"trustedHeight", trustedHeader.Height,
|
||||
"trustedHash", hash2str(trustedHeader.Hash()),
|
||||
"newHeight", interimHeader.Height,
|
||||
"newHash", hash2str(interimHeader.Hash()))
|
||||
err = VerifyAdjacent(c.chainID, trustedHeader, interimHeader, trustedNextVals,
|
||||
|
||||
err = VerifyAdjacent(c.chainID, trustedHeader, interimHeader, interimVals,
|
||||
c.trustingPeriod, now)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to verify the header #%d", height)
|
||||
}
|
||||
|
||||
// Update trusted header and vals.
|
||||
if height == newHeader.Height-1 {
|
||||
interimNextVals = newVals
|
||||
} else {
|
||||
interimNextVals, err = c.validatorSetFromPrimary(height + 1)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to obtain the vals #%d", height+1)
|
||||
}
|
||||
if !bytes.Equal(interimHeader.NextValidatorsHash, interimNextVals.Hash()) {
|
||||
return errors.Errorf("expected next validator's hash %X, but got %X (height #%d)",
|
||||
interimHeader.NextValidatorsHash,
|
||||
interimNextVals.Hash(),
|
||||
interimHeader.Height)
|
||||
}
|
||||
}
|
||||
trustedHeader, trustedNextVals = interimHeader, interimNextVals
|
||||
// 3) Update trustedHeader
|
||||
trustedHeader = interimHeader
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -750,22 +732,28 @@ func (c *Client) sequence(
|
||||
|
||||
// see VerifyHeader
|
||||
func (c *Client) bisection(
|
||||
trustedHeader *types.SignedHeader, // height h
|
||||
trustedNextVals *types.ValidatorSet, // height h + 1
|
||||
newHeader *types.SignedHeader, // height g
|
||||
newVals *types.ValidatorSet, // height g
|
||||
initiallyTrustedHeader *types.SignedHeader,
|
||||
initiallyTrustedVals *types.ValidatorSet,
|
||||
newHeader *types.SignedHeader,
|
||||
newVals *types.ValidatorSet,
|
||||
now time.Time) error {
|
||||
|
||||
interimVals := newVals
|
||||
interimHeader := newHeader
|
||||
var (
|
||||
trustedHeader = initiallyTrustedHeader
|
||||
trustedVals = initiallyTrustedVals
|
||||
|
||||
interimHeader = newHeader
|
||||
interimVals = newVals
|
||||
)
|
||||
|
||||
for {
|
||||
c.logger.Debug("Verify newHeader against trustedHeader",
|
||||
"trustedHeight", trustedHeader.Height,
|
||||
"trustedHash", hash2str(trustedHeader.Hash()),
|
||||
"newHeight", newHeader.Height,
|
||||
"newHash", hash2str(newHeader.Hash()))
|
||||
err := Verify(c.chainID, trustedHeader, trustedNextVals, interimHeader, interimVals, c.trustingPeriod, now,
|
||||
"newHeight", interimHeader.Height,
|
||||
"newHash", hash2str(interimHeader.Hash()))
|
||||
|
||||
err := Verify(c.chainID, trustedHeader, trustedVals, interimHeader, interimVals, c.trustingPeriod, now,
|
||||
c.trustLevel)
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
@@ -774,17 +762,7 @@ func (c *Client) bisection(
|
||||
}
|
||||
|
||||
// Update the lower bound to the previous upper bound
|
||||
interimNextVals, err := c.validatorSetFromPrimary(interimHeader.Height + 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !bytes.Equal(interimHeader.NextValidatorsHash, interimNextVals.Hash()) {
|
||||
return errors.Errorf("expected next validator's hash %X, but got %X (height #%d)",
|
||||
interimHeader.NextValidatorsHash,
|
||||
interimNextVals.Hash(),
|
||||
interimHeader.Height)
|
||||
}
|
||||
trustedHeader, trustedNextVals = interimHeader, interimNextVals
|
||||
trustedHeader, trustedVals = interimHeader, interimVals
|
||||
// Update the upper bound to the untrustedHeader
|
||||
interimHeader, interimVals = newHeader, newVals
|
||||
|
||||
@@ -801,18 +779,17 @@ func (c *Client) bisection(
|
||||
}
|
||||
}
|
||||
|
||||
// persist header and next validators to trustedStore.
|
||||
func (c *Client) updateTrustedHeaderAndNextVals(h *types.SignedHeader, nextVals *types.ValidatorSet) error {
|
||||
if !bytes.Equal(h.NextValidatorsHash, nextVals.Hash()) {
|
||||
return errors.Errorf("expected next validator's hash %X, but got %X", h.NextValidatorsHash, nextVals.Hash())
|
||||
func (c *Client) updateTrustedHeaderAndVals(h *types.SignedHeader, vals *types.ValidatorSet) error {
|
||||
if !bytes.Equal(h.ValidatorsHash, vals.Hash()) {
|
||||
return errors.Errorf("expected validator's hash %X, but got %X", h.ValidatorsHash, vals.Hash())
|
||||
}
|
||||
|
||||
if err := c.trustedStore.SaveSignedHeaderAndNextValidatorSet(h, nextVals); err != nil {
|
||||
if err := c.trustedStore.SaveSignedHeaderAndValidatorSet(h, vals); err != nil {
|
||||
return errors.Wrap(err, "failed to save trusted header")
|
||||
}
|
||||
|
||||
c.latestTrustedHeader = h
|
||||
c.latestTrustedNextVals = nextVals
|
||||
c.latestTrustedVals = vals
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -898,7 +875,7 @@ func (c *Client) compareNewHeaderWithWitnesses(h *types.SignedHeader) error {
|
||||
}
|
||||
|
||||
if !bytes.Equal(h.Hash(), altH.Hash()) {
|
||||
if err = c.latestTrustedNextVals.VerifyCommitTrusting(c.chainID, altH.Commit.BlockID,
|
||||
if err = c.latestTrustedVals.VerifyCommitTrusting(c.chainID, altH.Commit.BlockID,
|
||||
altH.Height, altH.Commit, c.trustLevel); err != nil {
|
||||
c.logger.Error("Witness sent us incorrect header", "err", err, "witness", witness)
|
||||
witnessesToRemove = append(witnessesToRemove, i)
|
||||
@@ -1002,7 +979,7 @@ func (c *Client) RemoveNoLongerTrustedHeaders(now time.Time) {
|
||||
break
|
||||
}
|
||||
|
||||
err = c.trustedStore.DeleteSignedHeaderAndNextValidatorSet(height)
|
||||
err = c.trustedStore.DeleteSignedHeaderAndValidatorSet(height)
|
||||
if err != nil {
|
||||
c.logger.Error("can't remove a trusted header & validator set", "err", err, "height", height)
|
||||
continue
|
||||
|
||||
@@ -50,7 +50,6 @@ var (
|
||||
1: vals,
|
||||
2: vals,
|
||||
3: vals,
|
||||
4: vals,
|
||||
},
|
||||
)
|
||||
deadNode = mockp.NewDeadMock(chainID)
|
||||
@@ -81,7 +80,6 @@ func TestClient_SequentialVerification(t *testing.T) {
|
||||
1: vals,
|
||||
2: vals,
|
||||
3: vals,
|
||||
4: vals,
|
||||
},
|
||||
false,
|
||||
false,
|
||||
@@ -115,7 +113,6 @@ func TestClient_SequentialVerification(t *testing.T) {
|
||||
1: vals,
|
||||
2: vals,
|
||||
3: vals,
|
||||
4: vals,
|
||||
},
|
||||
false,
|
||||
true,
|
||||
@@ -136,7 +133,6 @@ func TestClient_SequentialVerification(t *testing.T) {
|
||||
1: vals,
|
||||
2: vals,
|
||||
3: vals,
|
||||
4: vals,
|
||||
},
|
||||
false,
|
||||
true,
|
||||
@@ -155,7 +151,6 @@ func TestClient_SequentialVerification(t *testing.T) {
|
||||
1: vals,
|
||||
2: vals,
|
||||
3: newVals,
|
||||
4: newVals,
|
||||
},
|
||||
false,
|
||||
true,
|
||||
@@ -230,7 +225,6 @@ func TestClient_SkippingVerification(t *testing.T) {
|
||||
1: vals,
|
||||
2: vals,
|
||||
3: vals,
|
||||
4: vals,
|
||||
},
|
||||
false,
|
||||
false,
|
||||
@@ -247,7 +241,6 @@ func TestClient_SkippingVerification(t *testing.T) {
|
||||
1: vals,
|
||||
2: vals,
|
||||
3: transitVals,
|
||||
4: transitVals,
|
||||
},
|
||||
false,
|
||||
false,
|
||||
@@ -268,7 +261,6 @@ func TestClient_SkippingVerification(t *testing.T) {
|
||||
1: vals,
|
||||
2: vals,
|
||||
3: newVals,
|
||||
4: newVals,
|
||||
},
|
||||
false,
|
||||
false,
|
||||
@@ -289,7 +281,6 @@ func TestClient_SkippingVerification(t *testing.T) {
|
||||
1: vals,
|
||||
2: vals,
|
||||
3: newVals,
|
||||
4: newVals,
|
||||
},
|
||||
false,
|
||||
true,
|
||||
@@ -404,7 +395,7 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) {
|
||||
// 1. options.Hash == trustedHeader.Hash
|
||||
{
|
||||
trustedStore := dbs.New(dbm.NewMemDB(), chainID)
|
||||
err := trustedStore.SaveSignedHeaderAndNextValidatorSet(h1, vals)
|
||||
err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals)
|
||||
require.NoError(t, err)
|
||||
|
||||
c, err := NewClient(
|
||||
@@ -429,7 +420,7 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) {
|
||||
// 2. options.Hash != trustedHeader.Hash
|
||||
{
|
||||
trustedStore := dbs.New(dbm.NewMemDB(), chainID)
|
||||
err := trustedStore.SaveSignedHeaderAndNextValidatorSet(h1, vals)
|
||||
err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals)
|
||||
require.NoError(t, err)
|
||||
|
||||
// header1 != header
|
||||
@@ -444,7 +435,6 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) {
|
||||
},
|
||||
map[int64]*types.ValidatorSet{
|
||||
1: vals,
|
||||
2: vals,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -477,7 +467,7 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) {
|
||||
// 1. options.Hash == trustedHeader.Hash
|
||||
{
|
||||
trustedStore := dbs.New(dbm.NewMemDB(), chainID)
|
||||
err := trustedStore.SaveSignedHeaderAndNextValidatorSet(h1, vals)
|
||||
err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals)
|
||||
require.NoError(t, err)
|
||||
|
||||
c, err := NewClient(
|
||||
@@ -508,7 +498,7 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) {
|
||||
// This could happen if previous provider was lying to us.
|
||||
{
|
||||
trustedStore := dbs.New(dbm.NewMemDB(), chainID)
|
||||
err := trustedStore.SaveSignedHeaderAndNextValidatorSet(h1, vals)
|
||||
err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals)
|
||||
require.NoError(t, err)
|
||||
|
||||
// header1 != header
|
||||
@@ -527,7 +517,6 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) {
|
||||
map[int64]*types.ValidatorSet{
|
||||
1: vals,
|
||||
2: vals,
|
||||
3: vals,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -560,12 +549,12 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) {
|
||||
// 1. options.Hash == trustedHeader.Hash
|
||||
{
|
||||
trustedStore := dbs.New(dbm.NewMemDB(), chainID)
|
||||
err := trustedStore.SaveSignedHeaderAndNextValidatorSet(h1, vals)
|
||||
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.SaveSignedHeaderAndNextValidatorSet(h2, vals)
|
||||
err = trustedStore.SaveSignedHeaderAndValidatorSet(h2, vals)
|
||||
require.NoError(t, err)
|
||||
|
||||
primary := mockp.New(
|
||||
@@ -577,7 +566,6 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) {
|
||||
map[int64]*types.ValidatorSet{
|
||||
1: vals,
|
||||
2: vals,
|
||||
3: vals,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -610,7 +598,7 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) {
|
||||
// This could happen if previous provider was lying to us.
|
||||
{
|
||||
trustedStore := dbs.New(dbm.NewMemDB(), chainID)
|
||||
err := trustedStore.SaveSignedHeaderAndNextValidatorSet(h1, vals)
|
||||
err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals)
|
||||
require.NoError(t, err)
|
||||
|
||||
// header1 != header
|
||||
@@ -619,7 +607,7 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) {
|
||||
|
||||
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.SaveSignedHeaderAndNextValidatorSet(header2, vals)
|
||||
err = trustedStore.SaveSignedHeaderAndValidatorSet(header2, vals)
|
||||
require.NoError(t, err)
|
||||
|
||||
primary := mockp.New(
|
||||
@@ -629,7 +617,6 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) {
|
||||
},
|
||||
map[int64]*types.ValidatorSet{
|
||||
1: vals,
|
||||
2: vals,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -796,7 +783,7 @@ func TestClient_BackwardsVerification(t *testing.T) {
|
||||
func TestClient_NewClientFromTrustedStore(t *testing.T) {
|
||||
// 1) Initiate DB and fill with a "trusted" header
|
||||
db := dbs.New(dbm.NewMemDB(), chainID)
|
||||
err := db.SaveSignedHeaderAndNextValidatorSet(h1, vals)
|
||||
err := db.SaveSignedHeaderAndValidatorSet(h1, vals)
|
||||
require.NoError(t, err)
|
||||
|
||||
c, err := NewClientFromTrustedStore(
|
||||
|
||||
@@ -31,9 +31,9 @@ func New(db dbm.DB, prefix string) store.Store {
|
||||
return &dbs{db: db, prefix: prefix, cdc: cdc}
|
||||
}
|
||||
|
||||
// SaveSignedHeaderAndNextValidatorSet persists SignedHeader and ValidatorSet
|
||||
// to the db.
|
||||
func (s *dbs) SaveSignedHeaderAndNextValidatorSet(sh *types.SignedHeader, valSet *types.ValidatorSet) error {
|
||||
// SaveSignedHeaderAndValidatorSet persists SignedHeader and ValidatorSet to
|
||||
// the db.
|
||||
func (s *dbs) SaveSignedHeaderAndValidatorSet(sh *types.SignedHeader, valSet *types.ValidatorSet) error {
|
||||
if sh.Height <= 0 {
|
||||
panic("negative or zero height")
|
||||
}
|
||||
@@ -42,6 +42,7 @@ func (s *dbs) SaveSignedHeaderAndNextValidatorSet(sh *types.SignedHeader, valSet
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "marshalling header")
|
||||
}
|
||||
|
||||
valSetBz, err := s.cdc.MarshalBinaryLengthPrefixed(valSet)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "marshalling validator set")
|
||||
@@ -49,22 +50,22 @@ func (s *dbs) SaveSignedHeaderAndNextValidatorSet(sh *types.SignedHeader, valSet
|
||||
|
||||
b := s.db.NewBatch()
|
||||
b.Set(s.shKey(sh.Height), shBz)
|
||||
b.Set(s.vsKey(sh.Height+1), valSetBz)
|
||||
b.Set(s.vsKey(sh.Height), valSetBz)
|
||||
err = b.WriteSync()
|
||||
b.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteSignedHeaderAndNextValidatorSet deletes SignedHeader and ValidatorSet
|
||||
// from the db.
|
||||
func (s *dbs) DeleteSignedHeaderAndNextValidatorSet(height int64) error {
|
||||
// DeleteSignedHeaderAndValidatorSet deletes SignedHeader and ValidatorSet from
|
||||
// the db.
|
||||
func (s *dbs) DeleteSignedHeaderAndValidatorSet(height int64) error {
|
||||
if height <= 0 {
|
||||
panic("negative or zero height")
|
||||
}
|
||||
|
||||
b := s.db.NewBatch()
|
||||
b.Delete(s.shKey(height))
|
||||
b.Delete(s.vsKey(height + 1))
|
||||
b.Delete(s.vsKey(height))
|
||||
err := b.WriteSync()
|
||||
b.Close()
|
||||
return err
|
||||
|
||||
@@ -24,7 +24,7 @@ func TestLast_FirstSignedHeaderHeight(t *testing.T) {
|
||||
assert.EqualValues(t, -1, height)
|
||||
|
||||
// 1 key
|
||||
err = dbStore.SaveSignedHeaderAndNextValidatorSet(
|
||||
err = dbStore.SaveSignedHeaderAndValidatorSet(
|
||||
&types.SignedHeader{Header: &types.Header{Height: 1}}, &types.ValidatorSet{})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -37,20 +37,20 @@ func TestLast_FirstSignedHeaderHeight(t *testing.T) {
|
||||
assert.EqualValues(t, 1, height)
|
||||
}
|
||||
|
||||
func Test_SaveSignedHeaderAndNextValidatorSet(t *testing.T) {
|
||||
dbStore := New(dbm.NewMemDB(), "Test_SaveSignedHeaderAndNextValidatorSet")
|
||||
func Test_SaveSignedHeaderAndValidatorSet(t *testing.T) {
|
||||
dbStore := New(dbm.NewMemDB(), "Test_SaveSignedHeaderAndValidatorSet")
|
||||
|
||||
// Empty store
|
||||
h, err := dbStore.SignedHeader(1)
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, h)
|
||||
|
||||
valSet, err := dbStore.ValidatorSet(2)
|
||||
valSet, err := dbStore.ValidatorSet(1)
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, valSet)
|
||||
|
||||
// 1 key
|
||||
err = dbStore.SaveSignedHeaderAndNextValidatorSet(
|
||||
err = dbStore.SaveSignedHeaderAndValidatorSet(
|
||||
&types.SignedHeader{Header: &types.Header{Height: 1}}, &types.ValidatorSet{})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -58,19 +58,19 @@ func Test_SaveSignedHeaderAndNextValidatorSet(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, h)
|
||||
|
||||
valSet, err = dbStore.ValidatorSet(2)
|
||||
valSet, err = dbStore.ValidatorSet(1)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, valSet)
|
||||
|
||||
// Empty store
|
||||
err = dbStore.DeleteSignedHeaderAndNextValidatorSet(1)
|
||||
err = dbStore.DeleteSignedHeaderAndValidatorSet(1)
|
||||
require.NoError(t, err)
|
||||
|
||||
h, err = dbStore.SignedHeader(1)
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, h)
|
||||
|
||||
valSet, err = dbStore.ValidatorSet(2)
|
||||
valSet, err = dbStore.ValidatorSet(1)
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, valSet)
|
||||
}
|
||||
@@ -83,7 +83,7 @@ func Test_SignedHeaderAfter(t *testing.T) {
|
||||
dbStore.SignedHeaderAfter(100)
|
||||
})
|
||||
|
||||
err := dbStore.SaveSignedHeaderAndNextValidatorSet(
|
||||
err := dbStore.SaveSignedHeaderAndValidatorSet(
|
||||
&types.SignedHeader{Header: &types.Header{Height: 2}}, &types.ValidatorSet{})
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ var (
|
||||
// ErrSignedHeaderNotFound is returned when a store does not have the
|
||||
// requested header.
|
||||
ErrSignedHeaderNotFound = errors.New("signed header not found")
|
||||
|
||||
// ErrValidatorSetNotFound is returned when a store does not have the
|
||||
// requested validator set.
|
||||
ErrValidatorSetNotFound = errors.New("validator set not found")
|
||||
|
||||
@@ -4,17 +4,17 @@ import "github.com/tendermint/tendermint/types"
|
||||
|
||||
// Store is anything that can persistenly store headers.
|
||||
type Store interface {
|
||||
// SaveSignedHeaderAndNextValidatorSet saves a SignedHeader (h: sh.Height)
|
||||
// and a ValidatorSet (h: sh.Height+1).
|
||||
// SaveSignedHeaderAndValidatorSet saves a SignedHeader (h: sh.Height) and a
|
||||
// ValidatorSet (h: sh.Height).
|
||||
//
|
||||
// height must be > 0.
|
||||
SaveSignedHeaderAndNextValidatorSet(sh *types.SignedHeader, valSet *types.ValidatorSet) error
|
||||
SaveSignedHeaderAndValidatorSet(sh *types.SignedHeader, valSet *types.ValidatorSet) error
|
||||
|
||||
// DeleteSignedHeaderAndNextValidatorSet deletes SignedHeader (h: height) and
|
||||
// ValidatorSet (h: height+1).
|
||||
// DeleteSignedHeaderAndValidatorSet deletes SignedHeader (h: height) and
|
||||
// ValidatorSet (h: height).
|
||||
//
|
||||
// height must be > 0.
|
||||
DeleteSignedHeaderAndNextValidatorSet(height int64) error
|
||||
DeleteSignedHeaderAndValidatorSet(height int64) error
|
||||
|
||||
// SignedHeader returns the SignedHeader that corresponds to the given
|
||||
// height.
|
||||
|
||||
@@ -21,15 +21,15 @@ var (
|
||||
//
|
||||
// a) trustedHeader can still be trusted (if not, ErrOldHeaderExpired is returned);
|
||||
// b) untrustedHeader is valid;
|
||||
// c) trustLevel ([1/3, 1]) of trustedHeaderNextVals signed correctly
|
||||
// (if not, ErrNewValSetCantBeTrusted is returned);
|
||||
// c) trustLevel ([1/3, 1]) of trustedHeaderVals (or trustedHeaderNextVals)
|
||||
// signed correctly (if not, ErrNewValSetCantBeTrusted is returned);
|
||||
// d) more than 2/3 of untrustedVals have signed h2 (if not,
|
||||
// ErrNotEnoughVotingPowerSigned is returned);
|
||||
// e) headers are non-adjacent.
|
||||
func VerifyNonAdjacent(
|
||||
chainID string,
|
||||
trustedHeader *types.SignedHeader, // height=X
|
||||
trustedNextVals *types.ValidatorSet, // height=X+1
|
||||
trustedVals *types.ValidatorSet, // height=X or height=X+1
|
||||
untrustedHeader *types.SignedHeader, // height=Y
|
||||
untrustedVals *types.ValidatorSet, // height=Y
|
||||
trustingPeriod time.Duration,
|
||||
@@ -49,7 +49,7 @@ func VerifyNonAdjacent(
|
||||
}
|
||||
|
||||
// Ensure that +`trustLevel` (default 1/3) or more of last trusted validators signed correctly.
|
||||
err := trustedNextVals.VerifyCommitTrusting(chainID, untrustedHeader.Commit.BlockID, untrustedHeader.Height,
|
||||
err := trustedVals.VerifyCommitTrusting(chainID, untrustedHeader.Commit.BlockID, untrustedHeader.Height,
|
||||
untrustedHeader.Commit, trustLevel)
|
||||
if err != nil {
|
||||
switch e := err.(type) {
|
||||
@@ -78,7 +78,7 @@ func VerifyNonAdjacent(
|
||||
//
|
||||
// a) trustedHeader can still be trusted (if not, ErrOldHeaderExpired is returned);
|
||||
// b) untrustedHeader is valid;
|
||||
// c) untrustedHeader.ValidatorsHash equals trustedHeaderNextVals.Hash()
|
||||
// c) untrustedHeader.ValidatorsHash equals trustedHeader.NextValidatorsHash;
|
||||
// d) more than 2/3 of new validators (untrustedVals) have signed h2 (if not,
|
||||
// ErrNotEnoughVotingPowerSigned is returned);
|
||||
// e) headers are adjacent.
|
||||
@@ -124,7 +124,7 @@ func VerifyAdjacent(
|
||||
func Verify(
|
||||
chainID string,
|
||||
trustedHeader *types.SignedHeader, // height=X
|
||||
trustedNextVals *types.ValidatorSet, // height=X+1
|
||||
trustedVals *types.ValidatorSet, // height=X or height=X+1
|
||||
untrustedHeader *types.SignedHeader, // height=Y
|
||||
untrustedVals *types.ValidatorSet, // height=Y
|
||||
trustingPeriod time.Duration,
|
||||
@@ -132,7 +132,7 @@ func Verify(
|
||||
trustLevel tmmath.Fraction) error {
|
||||
|
||||
if untrustedHeader.Height != trustedHeader.Height+1 {
|
||||
return VerifyNonAdjacent(chainID, trustedHeader, trustedNextVals, untrustedHeader, untrustedVals,
|
||||
return VerifyNonAdjacent(chainID, trustedHeader, trustedVals, untrustedHeader, untrustedVals,
|
||||
trustingPeriod, now, trustLevel)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user