From 2b58a6272150a4edf40d3aaf42a756ba88efa9b3 Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Tue, 1 Sep 2020 17:45:55 +0200 Subject: [PATCH] light: implement light block (#5298) --- CHANGELOG_PENDING.md | 5 +- light/client.go | 743 +++++++++++------------------ light/client_benchmark_test.go | 18 +- light/client_test.go | 273 ++++------- light/errors.go | 9 +- light/example_test.go | 21 +- light/provider/errors.go | 7 +- light/provider/http/http.go | 30 +- light/provider/http/http_test.go | 20 +- light/provider/mock/deadmock.go | 5 +- light/provider/mock/mock.go | 28 +- light/provider/provider.go | 19 +- light/rpc/client.go | 46 +- light/store/db/db.go | 148 ++---- light/store/db/db_test.go | 116 ++--- light/store/errors.go | 8 +- light/store/store.go | 31 +- light/verifier.go | 8 +- proto/tendermint/types/types.pb.go | 416 ++++++++++++---- proto/tendermint/types/types.proto | 6 + statesync/stateprovider.go | 39 +- types/block.go | 110 ----- types/block_test.go | 53 -- types/light.go | 221 +++++++++ types/light_test.go | 161 +++++++ 25 files changed, 1344 insertions(+), 1197 deletions(-) create mode 100644 types/light.go create mode 100644 types/light_test.go diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index a78a03caa..1daf1bef2 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -9,13 +9,14 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi - [crypto/secp256k1] \#5280 `secp256k1` has been removed from the Tendermint repo. (@marbar3778) - CLI/RPC/Config - - [config] \#5315 Rename `prof_laddr` to `pprof_laddr` and move it to `rpc` section (@melekes) - - [rpc] \#5315 Remove `/unsafe_start_cpu_profiler`, `/unsafe_stop_cpu_profiler` and `/unsafe_write_heap_profile`. Please use pprof functionality instead (@melekes) +- [config] \#5315 Rename `prof_laddr` to `pprof_laddr` and move it to `rpc` section (@melekes) +- [rpc] \#5315 Remove `/unsafe_start_cpu_profiler`, `/unsafe_stop_cpu_profiler` and `/unsafe_write_heap_profile`. Please use pprof functionality instead (@melekes) ## FEATURES - [privval] \#5239 Add `chainID` to requests from client. (@marbar3778) - [config] Add `--consensus.double_sign_check_height` flag and `DoubleSignCheckHeight` config variable. See [ADR-51](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-051-double-signing-risk-reduction.md) +- [light] [\#5298](https://github.com/tendermint/tendermint/pull/5298) Morph validator set and signed header into light block (@cmwaters) ## IMPROVEMENTS diff --git a/light/client.go b/light/client.go index ecca44556..0a82032d5 100644 --- a/light/client.go +++ b/light/client.go @@ -23,11 +23,11 @@ const ( defaultPruningSize = 1000 defaultMaxRetryAttempts = 10 - // For bisection, when using the cache of headers from the previous batch, - // they will always be at a height greater than 1/2 (normal bisection) so to + // For verifySkipping, when using the cache of headers from the previous batch, + // they will always be at a height greater than 1/2 (normal verifySkipping) so to // find something in between the range, 9/16 is used. - bisectionNumerator = 9 - bisectionDenominator = 16 + verifySkippingNumerator = 9 + verifySkippingDenominator = 16 // 10s should cover most of the clients. // References: @@ -40,7 +40,7 @@ const ( type Option func(*Client) // SequentialVerification option configures the light client to sequentially -// check the headers (every header, in ascending height order). Note this is +// check the blocks (every block, in ascending height order). Note this is // much slower than SkippingVerification, albeit more secure. func SequentialVerification() Option { return func(c *Client) { @@ -48,9 +48,9 @@ func SequentialVerification() Option { } } -// SkippingVerification option configures the light client to skip headers as +// SkippingVerification option configures the light client to skip blocks as // long as {trustLevel} of the old validator set signed the new header. The -// bisection algorithm from the specification is used for finding the minimal +// verifySkipping algorithm from the specification is used for finding the minimal // "trust path". // // trustLevel - fraction of the old validator set (in terms of voting power), @@ -64,11 +64,10 @@ func SkippingVerification(trustLevel tmmath.Fraction) Option { } } -// PruningSize option sets the maximum amount of headers & validator set pairs -// that the light client stores. When Prune() is run, all headers (along with -// the associated validator sets) that are earlier than the h amount of headers -// will be removed from the store. Default: 1000. A pruning size of 0 will not -// prune the light client at all. +// PruningSize option sets the maximum amount of light blocks that the light +// client stores. When Prune() is run, all light blocks that are earlier than +// the h amount of light blocks will be removed from the store. +// Default: 1000. A pruning size of 0 will not prune the light client at all. func PruningSize(h uint16) Option { return func(c *Client) { c.pruningSize = h @@ -99,7 +98,7 @@ func MaxRetryAttempts(max uint16) Option { } } -// MaxClockDrift defines how much new (untrusted) header's Time can drift into +// MaxClockDrift defines how much new header's time can drift into // the future. Default: 10s. func MaxClockDrift(d time.Duration) Option { return func(c *Client) { @@ -108,7 +107,7 @@ func MaxClockDrift(d time.Duration) Option { } // Client represents a light client, connected to a single chain, which gets -// headers from a primary provider, verifies them either sequentially or by +// light blocks from a primary provider, verifies them either sequentially or by // skipping some and stores them in a trusted store (usually, a local FS). // // Default verification: SkippingVerification(DefaultTrustLevel) @@ -127,12 +126,10 @@ type Client struct { // See Witnesses option witnesses []provider.Provider - // Where trusted headers are stored. + // Where trusted light blocks are stored. trustedStore store.Store - // Highest trusted header from the store (height=H). - latestTrustedHeader *types.SignedHeader - // Highest validator set from the store (height=H). - latestTrustedVals *types.ValidatorSet + // Highest trusted light block from the store (height=H). + latestTrustedBlock *types.LightBlock // See RemoveNoLongerTrustedHeadersPeriod option pruningSize uint16 @@ -145,8 +142,8 @@ type Client struct { } // NewClient returns a new light client. It returns an error if it fails to -// obtain the header & vals from the primary or they are invalid (e.g. trust -// hash does not match with the one from the header). +// obtain the light block from the primary or they are invalid (e.g. trust +// hash does not match with the one from the headers). // // Witnesses are providers, which will be used for cross-checking the primary // provider. At least one witness must be given when skipping verification is @@ -171,15 +168,15 @@ func NewClient( return nil, err } - if c.latestTrustedHeader != nil { - c.logger.Info("Checking trusted header using options") + if c.latestTrustedBlock != nil { + c.logger.Info("Checking trusted light block using options") if err := c.checkTrustedHeaderUsingOptions(trustOptions); err != nil { return nil, err } } - if c.latestTrustedHeader == nil || c.latestTrustedHeader.Height < trustOptions.Height { - c.logger.Info("Downloading trusted header using options") + if c.latestTrustedBlock == nil || c.latestTrustedBlock.Height < trustOptions.Height { + c.logger.Info("Downloading trusted light block using options") if err := c.initializeWithTrustOptions(trustOptions); err != nil { return nil, err } @@ -237,36 +234,27 @@ func NewClientFromTrustedStore( return nil, err } - if err := c.restoreTrustedHeaderAndVals(); err != nil { + if err := c.restoreTrustedLightBlock(); err != nil { return nil, err } return c, nil } -// restoreTrustedHeaderAndVals loads trustedHeader and trustedVals from -// trustedStore. -func (c *Client) restoreTrustedHeaderAndVals() error { - lastHeight, err := c.trustedStore.LastSignedHeaderHeight() +// restoreTrustedLightBlock loads the latest trusted light block from the store +func (c *Client) restoreTrustedLightBlock() error { + lastHeight, err := c.trustedStore.LastLightBlockHeight() if err != nil { - return fmt.Errorf("can't get last trusted header height: %w", err) + return fmt.Errorf("can't get last trusted light block height: %w", err) } if lastHeight > 0 { - trustedHeader, err := c.trustedStore.SignedHeader(lastHeight) + trustedBlock, err := c.trustedStore.LightBlock(lastHeight) if err != nil { - return fmt.Errorf("can't get last trusted header: %w", err) + return fmt.Errorf("can't get last trusted light block: %w", err) } - - trustedVals, err := c.trustedStore.ValidatorSet(lastHeight) - if err != nil { - return fmt.Errorf("can't get last trusted validators: %w", err) - } - - c.latestTrustedHeader = trustedHeader - c.latestTrustedVals = trustedVals - - c.logger.Info("Restored trusted header and vals", "height", lastHeight) + c.latestTrustedBlock = trustedBlock + c.logger.Info("Restored trusted light block", "height", lastHeight) } return nil @@ -274,43 +262,43 @@ func (c *Client) restoreTrustedHeaderAndVals() error { // if options.Height: // -// 1) ahead of trustedHeader.Height => fetch header (same height as -// trustedHeader) from primary provider and check it's hash matches the -// trustedHeader's hash (if not, remove trustedHeader and all the headers +// 1) ahead of trustedLightBlock.Height => fetch light blocks (same height as +// trustedLightBlock) from primary provider and check it's hash matches the +// trustedLightBlock's hash (if not, remove trustedLightBlock and all the light blocks // before) // -// 2) equals trustedHeader.Height => check options.Hash matches the -// trustedHeader's hash (if not, remove trustedHeader and all the headers +// 2) equals trustedLightBlock.Height => check options.Hash matches the +// trustedLightBlock's hash (if not, remove trustedLightBlock and all the light blocks // before) // -// 3) behind trustedHeader.Height => remove all the headers between -// options.Height and trustedHeader.Height, update trustedHeader, then -// check options.Hash matches the trustedHeader's hash (if not, remove -// trustedHeader and all the headers before) +// 3) behind trustedLightBlock.Height => remove all the light blocks between +// options.Height and trustedLightBlock.Height, update trustedLightBlock, then +// check options.Hash matches the trustedLightBlock's hash (if not, remove +// trustedLightBlock and all the light blocks before) // // The intuition here is the user is always right. I.e. if she decides to reset // the light client with an older header, there must be a reason for it. func (c *Client) checkTrustedHeaderUsingOptions(options TrustOptions) error { var primaryHash []byte switch { - case options.Height > c.latestTrustedHeader.Height: - h, err := c.signedHeaderFromPrimary(c.latestTrustedHeader.Height) + case options.Height > c.latestTrustedBlock.Height: + h, err := c.lightBlockFromPrimary(c.latestTrustedBlock.Height) if err != nil { return err } primaryHash = h.Hash() - case options.Height == c.latestTrustedHeader.Height: + case options.Height == c.latestTrustedBlock.Height: primaryHash = options.Hash - case options.Height < c.latestTrustedHeader.Height: + case options.Height < c.latestTrustedBlock.Height: c.logger.Info("Client initialized with old header (trusted is more recent)", "old", options.Height, - "trustedHeight", c.latestTrustedHeader.Height, - "trustedHash", hash2str(c.latestTrustedHeader.Hash())) + "trustedHeight", c.latestTrustedBlock.Height, + "trustedHash", hash2str(c.latestTrustedBlock.Hash())) action := fmt.Sprintf( - "Rollback to %d (%X)? Note this will remove newer headers up to %d (%X)", + "Rollback to %d (%X)? Note this will remove newer light blocks up to %d (%X)", options.Height, options.Hash, - c.latestTrustedHeader.Height, c.latestTrustedHeader.Hash()) + c.latestTrustedBlock.Height, c.latestTrustedBlock.Hash()) if c.confirmationFn(action) { // remove all the headers (options.Height, trustedHeader.Height] err := c.cleanupAfter(options.Height) @@ -327,31 +315,31 @@ func (c *Client) checkTrustedHeaderUsingOptions(options TrustOptions) error { primaryHash = options.Hash } - if !bytes.Equal(primaryHash, c.latestTrustedHeader.Hash()) { + if !bytes.Equal(primaryHash, c.latestTrustedBlock.Hash()) { c.logger.Info("Prev. trusted header's hash (h1) doesn't match hash from primary provider (h2)", - "h1", hash2str(c.latestTrustedHeader.Hash()), "h2", hash2str(primaryHash)) + "h1", hash2str(c.latestTrustedBlock.Hash()), "h2", hash2str(primaryHash)) action := fmt.Sprintf( - "Prev. trusted header's hash %X doesn't match hash %X from primary provider. Remove all the stored headers?", - c.latestTrustedHeader.Hash(), primaryHash) + "Prev. trusted header's hash %X doesn't match hash %X from primary provider. Remove all the stored light blocks?", + c.latestTrustedBlock.Hash(), primaryHash) if c.confirmationFn(action) { err := c.Cleanup() if err != nil { return fmt.Errorf("failed to cleanup: %w", err) } } else { - return errors.New("refused to remove the stored headers despite hashes mismatch") + return errors.New("refused to remove the stored light blocks despite hashes mismatch") } } return nil } -// initializeWithTrustOptions fetches the weakly-trusted header and vals from +// initializeWithTrustOptions fetches the weakly-trusted light block from // primary provider. func (c *Client) initializeWithTrustOptions(options TrustOptions) error { - // 1) Fetch and verify the header. - h, err := c.signedHeaderFromPrimary(options.Height) + // 1) Fetch and verify the light block. + l, err := c.lightBlockFromPrimary(options.Height) if err != nil { return err } @@ -359,44 +347,25 @@ func (c *Client) initializeWithTrustOptions(options TrustOptions) error { // NOTE: - Verify func will check if it's expired or not. // - h.Time is not being checked against time.Now() because we don't // want to add yet another argument to NewClient* functions. - if err := h.ValidateBasic(c.chainID); err != nil { + if err := l.ValidateBasic(c.chainID); err != nil { return err } - if !bytes.Equal(h.Hash(), options.Hash) { - return fmt.Errorf("expected header's hash %X, but got %X", options.Hash, h.Hash()) - } - - // 2) Fetch and verify the vals. - vals, err := c.validatorSetFromPrimary(options.Height) - if err != nil { - return err - } - - if !bytes.Equal(h.ValidatorsHash, vals.Hash()) { - return fmt.Errorf("expected header's validators (%X) to match those that were supplied (%X)", - h.ValidatorsHash, - vals.Hash(), - ) + if !bytes.Equal(l.Hash(), options.Hash) { + return fmt.Errorf("expected header's hash %X, but got %X", options.Hash, l.Hash()) } // Ensure that +2/3 of validators signed correctly. - err = vals.VerifyCommitLight(c.chainID, h.Commit.BlockID, h.Height, h.Commit) + err = l.ValidatorSet.VerifyCommitLight(c.chainID, l.Commit.BlockID, l.Height, l.Commit) if err != nil { return fmt.Errorf("invalid commit: %w", err) } // 3) Persist both of them and continue. - return c.updateTrustedHeaderAndVals(h, vals) + return c.updateTrustedLightBlock(l) } -// TrustedHeader returns a trusted header at the given height (0 - the latest). -// -// Headers along with validator sets, which can't be trusted anymore, are -// removed once a day (can be changed with RemoveNoLongerTrustedHeadersPeriod -// option). -// . -// height must be >= 0. +// TrustedLightBlock returns a trusted light block at the given height (0 - the latest). // // It returns an error if: // - there are some issues with the trusted store, although that should not @@ -405,41 +374,12 @@ func (c *Client) initializeWithTrustOptions(options TrustOptions) error { // - header has not been verified yet and is therefore not in the store // // Safe for concurrent use by multiple goroutines. -func (c *Client) TrustedHeader(height int64) (*types.SignedHeader, error) { +func (c *Client) TrustedLightBlock(height int64) (*types.LightBlock, error) { height, err := c.compareWithLatestHeight(height) if err != nil { return nil, err } - return c.trustedStore.SignedHeader(height) -} - -// TrustedValidatorSet returns a trusted validator set at the given height (0 - -// latest). The second return parameter is the height used (useful if 0 was -// passed; otherwise can be ignored). -// -// height must be >= 0. -// -// Headers along with validator sets are -// removed once a day (can be changed with RemoveNoLongerTrustedHeadersPeriod -// option). -// -// Function returns an error if: -// - there are some issues with the trusted store, although that should not -// happen normally; -// - negative height is passed; -// - header signed by that validator set has not been verified yet -// -// Safe for concurrent use by multiple goroutines. -func (c *Client) TrustedValidatorSet(height int64) (valSet *types.ValidatorSet, heightUsed int64, err error) { - heightUsed, err = c.compareWithLatestHeight(height) - if err != nil { - return nil, heightUsed, err - } - valSet, err = c.trustedStore.ValidatorSet(heightUsed) - if err != nil { - return nil, heightUsed, err - } - return valSet, heightUsed, err + return c.trustedStore.LightBlock(height) } func (c *Client) compareWithLatestHeight(height int64) (int64, error) { @@ -463,39 +403,39 @@ func (c *Client) compareWithLatestHeight(height int64) (int64, error) { return height, nil } -// VerifyHeaderAtHeight fetches header and validators at the given height -// and calls VerifyHeader. It returns header immediately if such exists in -// trustedStore (no verification is needed). +// VerifyLightBlockAtHeight fetches the light block at the given height +// and calls verifyLightBlock. It returns the block immediately if it exists in +// the trustedStore (no verification is needed). // // height must be > 0. // -// It returns provider.ErrSignedHeaderNotFound if header is not found by +// It returns provider.ErrlightBlockNotFound if light block 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) { +func (c *Client) VerifyLightBlockAtHeight(height int64, now time.Time) (*types.LightBlock, error) { if height <= 0 { return nil, errors.New("negative or zero height") } - // Check if header already verified. - h, err := c.TrustedHeader(height) + // Check if the light block already verified. + h, err := c.TrustedLightBlock(height) if err == nil { c.logger.Info("Header has already been verified", "height", height, "hash", hash2str(h.Hash())) - // Return already trusted header + // Return already trusted light block return h, nil } - // Request the header and the vals. - newHeader, newVals, err := c.signedHeaderAndValSetFromPrimary(height) + // Request the light block from primary + l, err := c.lightBlockFromPrimary(height) if err != nil { return nil, err } - return newHeader, c.verifyHeader(newHeader, newVals, now) + return l, c.verifyLightBlock(l, now) } -// VerifyHeader verifies new header against the trusted state. It returns +// VerifyHeader verifies a new header against the trusted state. It returns // immediately if newHeader exists in trustedStore (no verification is // needed). Else it performs one of the two types of verification: // @@ -505,14 +445,14 @@ func (c *Client) VerifyHeaderAtHeight(height int64, now time.Time) (*types.Signe // // SkippingVerification(trustLevel): verifies that {trustLevel} of the trusted // validator set has signed the new header. If it's not the case and the -// headers are not adjacent, bisection is performed and necessary (not all) +// headers are not adjacent, verifySkipping is performed and necessary (not all) // intermediate headers will be requested. See the specification for details. // Intermediate headers are not saved to database. // https://github.com/tendermint/spec/blob/master/spec/consensus/light-client.md // // If the header, which is older than the currently trusted header, is // requested and the light client does not have it, VerifyHeader will perform: -// a) bisection verification if nearest trusted header is found & not expired +// a) verifySkipping verification if nearest trusted header is found & not expired // b) backwards verification in all other cases // // It returns ErrOldHeaderExpired if the latest trusted header expired. @@ -520,136 +460,135 @@ func (c *Client) VerifyHeaderAtHeight(height int64, now time.Time) (*types.Signe // If the primary provides an invalid header (ErrInvalidHeader), it is rejected // and replaced by another provider until all are exhausted. // -// If, at any moment, SignedHeader or ValidatorSet are not found by the primary -// provider, provider.ErrSignedHeaderNotFound / -// provider.ErrValidatorSetNotFound error is returned. -func (c *Client) VerifyHeader(newHeader *types.SignedHeader, newVals *types.ValidatorSet, now time.Time) error { +// If, at any moment, a LightBlock is not found by the primary provider as part of +// verification then the provider will be replaced by another and the process will +// restart. +func (c *Client) VerifyHeader(newHeader *types.Header, now time.Time) error { + if newHeader == nil { + return errors.New("nil header") + } if newHeader.Height <= 0 { return errors.New("negative or zero height") } // Check if newHeader already verified. - h, err := c.TrustedHeader(newHeader.Height) + l, err := c.TrustedLightBlock(newHeader.Height) if err == nil { // Make sure it's the same header. - if !bytes.Equal(h.Hash(), newHeader.Hash()) { - return fmt.Errorf("existing trusted header %X does not match newHeader %X", h.Hash(), newHeader.Hash()) + if !bytes.Equal(l.Hash(), newHeader.Hash()) { + return fmt.Errorf("existing trusted header %X does not match newHeader %X", l.Hash(), newHeader.Hash()) } c.logger.Info("Header has already been verified", "height", newHeader.Height, "hash", hash2str(newHeader.Hash())) return nil } - return c.verifyHeader(newHeader, newVals, now) + // Request the header and the vals. + l, err = c.lightBlockFromPrimary(newHeader.Height) + if err != nil { + return fmt.Errorf("failed to retrieve light block from primary to verify against: %w", err) + } + + if !bytes.Equal(l.Hash(), newHeader.Hash()) { + return fmt.Errorf("light block header %X does not match newHeader %X", l.Hash(), newHeader.Hash()) + } + + return c.verifyLightBlock(l, now) } -func (c *Client) verifyHeader(newHeader *types.SignedHeader, newVals *types.ValidatorSet, now time.Time) error { - c.logger.Info("VerifyHeader", "height", newHeader.Height, "hash", hash2str(newHeader.Hash()), - "vals", hash2str(newVals.Hash())) +func (c *Client) verifyLightBlock(newLightBlock *types.LightBlock, now time.Time) error { + c.logger.Info("VerifyHeader", "height", newLightBlock.Height, "hash", hash2str(newLightBlock.Hash())) + + var ( + verifyFunc func(trusted *types.LightBlock, new *types.LightBlock, now time.Time) error + err error + ) - var err error + switch c.verificationMode { + case sequential: + verifyFunc = c.verifySequential + case skipping: + verifyFunc = c.verifySkippingAgainstPrimary + default: + panic(fmt.Sprintf("Unknown verification mode: %b", c.verificationMode)) + } - // 1) If going forward, perform either bisection or sequential verification. - if newHeader.Height >= c.latestTrustedHeader.Height { - switch c.verificationMode { - case sequential: - err = c.sequence(c.latestTrustedHeader, newHeader, newVals, now) - case skipping: - err = c.bisectionAgainstPrimary(c.latestTrustedHeader, c.latestTrustedVals, newHeader, newVals, now) - default: - panic(fmt.Sprintf("Unknown verification mode: %b", c.verificationMode)) - } - } else { - // 2) If verifying before the first trusted header, perform backwards - // verification. - var ( - closestHeader *types.SignedHeader - firstHeaderHeight int64 - ) - firstHeaderHeight, err = c.FirstTrustedHeight() + firstBlockHeight, err := c.FirstTrustedHeight() + if err != nil { + return fmt.Errorf("can't get first light block height: %w", err) + } + + switch { + // Verifying forwards + case newLightBlock.Height >= c.latestTrustedBlock.Height: + err = verifyFunc(c.latestTrustedBlock, newLightBlock, now) + + // Verifying backwards + case newLightBlock.Height < firstBlockHeight: + var firstBlock *types.LightBlock + firstBlock, err = c.trustedStore.LightBlock(firstBlockHeight) if err != nil { - return fmt.Errorf("can't get first header height: %w", err) + return fmt.Errorf("can't get first light block: %w", err) } - if newHeader.Height < firstHeaderHeight { - closestHeader, err = c.TrustedHeader(firstHeaderHeight) - if err != nil { - return fmt.Errorf("can't get first signed header: %w", err) - } - if HeaderExpired(closestHeader, c.trustingPeriod, now) { - closestHeader = c.latestTrustedHeader - } - err = c.backwards(closestHeader, newHeader, now) - } else { - // 3) OR if between trusted headers where the nearest has not expired, - // perform bisection verification, else backwards. - closestHeader, err = c.trustedStore.SignedHeaderBefore(newHeader.Height) - if err != nil { - return fmt.Errorf("can't get signed header before height %d: %w", newHeader.Height, err) - } - var closestValidatorSet *types.ValidatorSet - if c.verificationMode == sequential || HeaderExpired(closestHeader, c.trustingPeriod, now) { - err = c.backwards(c.latestTrustedHeader, newHeader, now) - } else { - closestValidatorSet, _, err = c.TrustedValidatorSet(closestHeader.Height) - if err != nil { - return fmt.Errorf("can't get validator set at height %d: %w", closestHeader.Height, err) - } - err = c.bisectionAgainstPrimary(closestHeader, closestValidatorSet, newHeader, newVals, now) - } + err = c.backwards(firstBlock.Header, newLightBlock.Header) + + // Verifying between first and last trusted light block + default: + var closestBlock *types.LightBlock + closestBlock, err = c.trustedStore.LightBlockBefore(newLightBlock.Height) + if err != nil { + return fmt.Errorf("can't get signed header before height %d: %w", newLightBlock.Height, err) } + err = verifyFunc(closestBlock, newLightBlock, now) } if err != nil { c.logger.Error("Can't verify", "err", err) return err } - // 4) Once verified, save and return - return c.updateTrustedHeaderAndVals(newHeader, newVals) + // Once verified, save and return + return c.updateTrustedLightBlock(newLightBlock) } // see VerifyHeader -func (c *Client) sequence( - initiallyTrustedHeader *types.SignedHeader, - newHeader *types.SignedHeader, - newVals *types.ValidatorSet, +func (c *Client) verifySequential( + trustedBlock *types.LightBlock, + newLightBlock *types.LightBlock, now time.Time) error { var ( - trustedHeader = initiallyTrustedHeader - - interimHeader *types.SignedHeader - interimVals *types.ValidatorSet - - err error + verifiedBlock = trustedBlock + interimBlock *types.LightBlock + err error ) - 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.signedHeaderAndValSetFromPrimary(height) + for height := trustedBlock.Height + 1; height <= newLightBlock.Height; height++ { + // 1) Fetch interim light block if needed. + if height == newLightBlock.Height { // last light block + interimBlock = newLightBlock + } else { // intermediate light blocks + interimBlock, err = c.lightBlockFromPrimary(height) if err != nil { - return ErrVerificationFailed{From: trustedHeader.Height, To: height, Reason: err} + return ErrVerificationFailed{From: verifiedBlock.Height, To: height, Reason: err} } } // 2) Verify them - c.logger.Debug("Verify adjacent newHeader against trustedHeader", - "trustedHeight", trustedHeader.Height, - "trustedHash", hash2str(trustedHeader.Hash()), - "newHeight", interimHeader.Height, - "newHash", hash2str(interimHeader.Hash())) + c.logger.Debug("Verify adjacent newLightBlock against verifiedBlock", + "trustedHeight", verifiedBlock.Height, + "trustedHash", hash2str(verifiedBlock.Hash()), + "newHeight", interimBlock.Height, + "newHash", hash2str(interimBlock.Hash())) - err = VerifyAdjacent(c.chainID, trustedHeader, interimHeader, interimVals, + err = VerifyAdjacent(c.chainID, verifiedBlock.SignedHeader, interimBlock.SignedHeader, interimBlock.ValidatorSet, c.trustingPeriod, now, c.maxClockDrift) if err != nil { - err := ErrVerificationFailed{From: trustedHeader.Height, To: interimHeader.Height, Reason: err} + err := ErrVerificationFailed{From: verifiedBlock.Height, To: interimBlock.Height, Reason: err} switch errors.Unwrap(err).(type) { case ErrInvalidHeader: // If the target header is invalid, return immediately. - if err.To == newHeader.Height { + if err.To == newLightBlock.Height { c.logger.Debug("Target header is invalid", "err", err) return err } @@ -664,20 +603,17 @@ func (c *Client) sequence( return err } - replacementHeader, replacementVals, fErr := c.signedHeaderAndValSetFromPrimary(newHeader.Height) + replacementBlock, fErr := c.lightBlockFromPrimary(newLightBlock.Height) if fErr != nil { - c.logger.Error("Can't fetch header/vals from primary", "err", fErr) + c.logger.Error("Can't fetch light block from primary", "err", fErr) // return original error return err } - if !bytes.Equal(replacementHeader.Hash(), newHeader.Hash()) || - !bytes.Equal(replacementVals.Hash(), newVals.Hash()) { - c.logger.Error("Replacement provider has a different header/vals", - "newHash", newHeader.Hash(), - "newVals", newVals.Hash(), - "replHash", replacementHeader.Hash(), - "replVals", replacementVals.Hash()) + if !bytes.Equal(replacementBlock.Hash(), newLightBlock.Hash()) { + c.logger.Error("Replacement provider has a different light block", + "newHash", newLightBlock.Hash(), + "replHash", replacementBlock.Hash()) // return original error return err } @@ -691,8 +627,8 @@ func (c *Client) sequence( } } - // 3) Update trustedHeader - trustedHeader = interimHeader + // 3) Update verifiedBlock + verifiedBlock = interimBlock } return nil @@ -700,41 +636,33 @@ func (c *Client) sequence( // see VerifyHeader // -// Bisection finds the middle header between a trusted and new header, -// reiterating the action until it verifies a header. A cache of headers +// verifySkipping finds the middle light block between a trusted and new light block, +// reiterating the action until it verifies a light block. A cache of light blocks // requested from source is kept such that when a verification is made, and the -// light client tries again to verify the new header in the middle, the light -// client does not need to ask for all the same headers again. -func (c *Client) bisection( +// light client tries again to verify the new light block in the middle, the light +// client does not need to ask for all the same light blocks again. +func (c *Client) verifySkipping( source provider.Provider, - initiallyTrustedHeader *types.SignedHeader, - initiallyTrustedVals *types.ValidatorSet, - newHeader *types.SignedHeader, - newVals *types.ValidatorSet, + trustedBlock *types.LightBlock, + newLightBlock *types.LightBlock, now time.Time) error { - type headerSet struct { - sh *types.SignedHeader - valSet *types.ValidatorSet - } - var ( - headerCache = []headerSet{{newHeader, newVals}} - depth = 0 + blockCache = []*types.LightBlock{newLightBlock} + depth = 0 - trustedHeader = initiallyTrustedHeader - trustedVals = initiallyTrustedVals + verifiedBlock = trustedBlock ) for { - c.logger.Debug("Verify non-adjacent newHeader against trustedHeader", - "trustedHeight", trustedHeader.Height, - "trustedHash", hash2str(trustedHeader.Hash()), - "newHeight", headerCache[depth].sh.Height, - "newHash", hash2str(headerCache[depth].sh.Hash())) - - err := Verify(c.chainID, trustedHeader, trustedVals, headerCache[depth].sh, headerCache[depth].valSet, - c.trustingPeriod, now, c.maxClockDrift, c.trustLevel) + c.logger.Debug("Verify non-adjacent newHeader against verifiedBlock", + "trustedHeight", verifiedBlock.Height, + "trustedHash", hash2str(verifiedBlock.Hash()), + "newHeight", blockCache[depth].Height, + "newHash", hash2str(blockCache[depth].Hash())) + + err := Verify(c.chainID, verifiedBlock.SignedHeader, verifiedBlock.ValidatorSet, blockCache[depth].SignedHeader, + blockCache[depth].ValidatorSet, c.trustingPeriod, now, c.maxClockDrift, c.trustLevel) switch err.(type) { case nil: // Have we verified the last header @@ -742,48 +670,46 @@ func (c *Client) bisection( return nil } // If not, update the lower bound to the previous upper bound - trustedHeader, trustedVals = headerCache[depth].sh, headerCache[depth].valSet - // Remove the untrusted header at the lower bound in the header cache - it's no longer useful - headerCache = headerCache[:depth] + verifiedBlock = blockCache[depth] + // Remove the light block at the lower bound in the header cache - it will no longer be needed + blockCache = blockCache[:depth] // Reset the cache depth so that we start from the upper bound again depth = 0 case ErrNewValSetCantBeTrusted: // do add another header to the end of the cache - if depth == len(headerCache)-1 { - pivotHeight := trustedHeader.Height + (headerCache[depth].sh.Height-trustedHeader. - Height)*bisectionNumerator/bisectionDenominator - interimHeader, interimVals, err := c.signedHeaderAndValSetFrom(pivotHeight, source) + if depth == len(blockCache)-1 { + pivotHeight := verifiedBlock.Height + (blockCache[depth].Height-verifiedBlock. + Height)*verifySkippingNumerator/verifySkippingDenominator + interimBlock, err := c.lightBlockFrom(pivotHeight, source) if err != nil { - return ErrVerificationFailed{From: trustedHeader.Height, To: pivotHeight, Reason: err} + return ErrVerificationFailed{From: verifiedBlock.Height, To: pivotHeight, Reason: err} } - headerCache = append(headerCache, headerSet{interimHeader, interimVals}) + blockCache = append(blockCache, interimBlock) } depth++ default: - return ErrVerificationFailed{From: trustedHeader.Height, To: headerCache[depth].sh.Height, Reason: err} + return ErrVerificationFailed{From: verifiedBlock.Height, To: blockCache[depth].Height, Reason: err} } } } -// bisectionAgainstPrimary does bisection plus it compares new header with +// verifySkippingAgainstPrimary does verifySkipping plus it compares new header with // witnesses and replaces primary if it does not respond after // MaxRetryAttempts. -func (c *Client) bisectionAgainstPrimary( - initiallyTrustedHeader *types.SignedHeader, - initiallyTrustedVals *types.ValidatorSet, - newHeader *types.SignedHeader, - newVals *types.ValidatorSet, +func (c *Client) verifySkippingAgainstPrimary( + trustedBlock *types.LightBlock, + newLightBlock *types.LightBlock, now time.Time) error { - err := c.bisection(c.primary, initiallyTrustedHeader, initiallyTrustedVals, newHeader, newVals, now) + err := c.verifySkipping(c.primary, trustedBlock, newLightBlock, now) switch errors.Unwrap(err).(type) { case ErrInvalidHeader: // If the target header is invalid, return immediately. invalidHeaderHeight := err.(ErrVerificationFailed).To - if invalidHeaderHeight == newHeader.Height { + if invalidHeaderHeight == newLightBlock.Height { c.logger.Debug("Target header is invalid", "err", err) return err } @@ -798,39 +724,30 @@ func (c *Client) bisectionAgainstPrimary( return err } - replacementHeader, replacementVals, fErr := c.signedHeaderAndValSetFromPrimary(newHeader.Height) + replacementBlock, fErr := c.lightBlockFromPrimary(newLightBlock.Height) if fErr != nil { - c.logger.Error("Can't fetch header/vals from primary", "err", fErr) + c.logger.Error("Can't fetch light block from primary", "err", fErr) // return original error return err } - if !bytes.Equal(replacementHeader.Hash(), newHeader.Hash()) || - !bytes.Equal(replacementVals.Hash(), newVals.Hash()) { - c.logger.Error("Replacement provider has a different header/vals", - "newHash", newHeader.Hash(), - "newVals", newVals.Hash(), - "replHash", replacementHeader.Hash(), - "replVals", replacementVals.Hash()) + if !bytes.Equal(replacementBlock.Hash(), newLightBlock.Hash()) { + c.logger.Error("Replacement provider has a different light block", + "newHash", newLightBlock.Hash(), + "replHash", replacementBlock.Hash()) // return original error return err } // attempt to verify the header again - return c.bisectionAgainstPrimary( - initiallyTrustedHeader, - initiallyTrustedVals, - replacementHeader, - replacementVals, - now, - ) + return c.verifySkippingAgainstPrimary(trustedBlock, replacementBlock, now) case nil: // Compare header with the witnesses to ensure it's not a fork. // More witnesses we have, more chance to notice one. // // CORRECTNESS ASSUMPTION: there's at least 1 correct full node // (primary or one of the witnesses). - if cmpErr := c.compareNewHeaderWithWitnesses(newHeader, now); cmpErr != nil { + if cmpErr := c.compareNewHeaderWithWitnesses(newLightBlock, now); cmpErr != nil { return cmpErr } default: @@ -845,7 +762,7 @@ func (c *Client) bisectionAgainstPrimary( // // Safe for concurrent use by multiple goroutines. func (c *Client) LastTrustedHeight() (int64, error) { - return c.trustedStore.LastSignedHeaderHeight() + return c.trustedStore.LastLightBlockHeight() } // FirstTrustedHeight returns a first trusted height. -1 and nil are returned if @@ -853,7 +770,7 @@ func (c *Client) LastTrustedHeight() (int64, error) { // // Safe for concurrent use by multiple goroutines. func (c *Client) FirstTrustedHeight() (int64, error) { - return c.trustedStore.FirstSignedHeaderHeight() + return c.trustedStore.FirstLightBlockHeight() } // ChainID returns the chain ID the light client was configured with. @@ -885,25 +802,24 @@ func (c *Client) Witnesses() []provider.Provider { // client must be stopped at this point. func (c *Client) Cleanup() error { c.logger.Info("Removing all the data") - c.latestTrustedHeader = nil - c.latestTrustedVals = nil + c.latestTrustedBlock = nil return c.trustedStore.Prune(0) } // cleanupAfter deletes all headers & validator sets after +height+. It also -// resets latestTrustedHeader to the latest header. +// resets latestTrustedBlock to the latest header. func (c *Client) cleanupAfter(height int64) error { - prevHeight := c.latestTrustedHeader.Height + prevHeight := c.latestTrustedBlock.Height for { - h, err := c.trustedStore.SignedHeaderBefore(prevHeight) - if err == store.ErrSignedHeaderNotFound || (h != nil && h.Height <= height) { + h, err := c.trustedStore.LightBlockBefore(prevHeight) + if err == store.ErrLightBlockNotFound || (h != nil && h.Height <= height) { break } else if err != nil { return fmt.Errorf("failed to get header before %d: %w", prevHeight, err) } - err = c.trustedStore.DeleteSignedHeaderAndValidatorSet(h.Height) + err = c.trustedStore.DeleteLightBlock(h.Height) if err != nil { c.logger.Error("can't remove a trusted header & validator set", "err", err, "height", h.Height) @@ -912,9 +828,8 @@ func (c *Client) cleanupAfter(height int64) error { prevHeight = h.Height } - c.latestTrustedHeader = nil - c.latestTrustedVals = nil - err := c.restoreTrustedHeaderAndVals() + c.latestTrustedBlock = nil + err := c.restoreTrustedLightBlock() if err != nil { return err } @@ -922,12 +837,8 @@ func (c *Client) cleanupAfter(height int64) error { return nil } -func (c *Client) updateTrustedHeaderAndVals(h *types.SignedHeader, vals *types.ValidatorSet) error { - if !bytes.Equal(h.ValidatorsHash, vals.Hash()) { - return fmt.Errorf("expected validator's hash %X, but got %X", h.ValidatorsHash, vals.Hash()) - } - - if err := c.trustedStore.SaveSignedHeaderAndValidatorSet(h, vals); err != nil { +func (c *Client) updateTrustedLightBlock(l *types.LightBlock) error { + if err := c.trustedStore.SaveLightBlock(l); err != nil { return fmt.Errorf("failed to save trusted header: %w", err) } @@ -937,120 +848,85 @@ func (c *Client) updateTrustedHeaderAndVals(h *types.SignedHeader, vals *types.V } } - if c.latestTrustedHeader == nil || h.Height > c.latestTrustedHeader.Height { - c.latestTrustedHeader = h - c.latestTrustedVals = vals + if c.latestTrustedBlock == nil || l.Height > c.latestTrustedBlock.Height { + c.latestTrustedBlock = l } return nil } -// 0 - latest header -// Note it swaps the primary with a witness if primary is not responding after -// MaxRetryAttempts. -func (c *Client) signedHeaderAndValSetFromPrimary(height int64) (*types.SignedHeader, *types.ValidatorSet, error) { - h, err := c.signedHeaderFromPrimary(height) - if err != nil { - return nil, nil, fmt.Errorf("can't fetch header: %w", err) - } - vals, err := c.validatorSetFromPrimary(height) - if err != nil { - return nil, nil, fmt.Errorf("can't fetch vals: %w", err) - } - return h, vals, nil -} - // 0 - latest header // Note it does not do retries nor swapping. -func (c *Client) signedHeaderAndValSetFromWitness(height int64, - witness provider.Provider) (*types.SignedHeader, *types.ValidatorSet, *errBadWitness) { - - h, err := witness.SignedHeader(height) - if err != nil { - return nil, nil, &errBadWitness{err, noResponse, -1} - } - err = c.validateHeader(h, height) - if err != nil { - return nil, nil, &errBadWitness{err, invalidHeader, -1} - } +func (c *Client) lightBlockFromWitness(height int64, + witness provider.Provider) (*types.LightBlock, *errBadWitness) { - vals, err := witness.ValidatorSet(height) + l, err := witness.LightBlock(height) if err != nil { - return nil, nil, &errBadWitness{err, noResponse, -1} + return nil, &errBadWitness{err, noResponse, -1} } - err = c.validateValidatorSet(vals) + err = c.validateLightBlock(l, height) if err != nil { - return nil, nil, &errBadWitness{err, invalidValidatorSet, -1} + return nil, &errBadWitness{err, invalidLightBlock, -1} } - return h, vals, nil + return l, nil } -func (c *Client) signedHeaderAndValSetFrom(height int64, - source provider.Provider) (*types.SignedHeader, *types.ValidatorSet, error) { +func (c *Client) lightBlockFrom(height int64, + source provider.Provider) (*types.LightBlock, error) { c.providerMutex.Lock() sourceIsPrimary := (c.primary == source) c.providerMutex.Unlock() if sourceIsPrimary { - return c.signedHeaderAndValSetFromPrimary(height) + return c.lightBlockFromPrimary(height) } - return c.signedHeaderAndValSetFromWitness(height, source) + return c.lightBlockFromWitness(height, source) } // backwards verification (see VerifyHeaderBackwards func in the spec) verifies // headers before a trusted header. If a sent header is invalid the primary is // replaced with another provider and the operation is repeated. func (c *Client) backwards( - initiallyTrustedHeader *types.SignedHeader, - newHeader *types.SignedHeader, - now time.Time) error { - - if HeaderExpired(initiallyTrustedHeader, c.trustingPeriod, now) { - c.logger.Error("Header Expired") - return ErrOldHeaderExpired{initiallyTrustedHeader.Time.Add(c.trustingPeriod), now} - } + trustedHeader *types.Header, + newHeader *types.Header) error { var ( - trustedHeader = initiallyTrustedHeader - interimHeader *types.SignedHeader - err error + verifiedHeader = trustedHeader + interimHeader *types.Header ) - for trustedHeader.Height > newHeader.Height { - interimHeader, err = c.signedHeaderFromPrimary(trustedHeader.Height - 1) + for verifiedHeader.Height > newHeader.Height { + interimBlock, err := c.lightBlockFromPrimary(verifiedHeader.Height - 1) if err != nil { - return fmt.Errorf("failed to obtain the header at height #%d: %w", trustedHeader.Height-1, err) + return fmt.Errorf("failed to obtain the header at height #%d: %w", verifiedHeader.Height-1, err) } - c.logger.Debug("Verify newHeader against trustedHeader", - "trustedHeight", trustedHeader.Height, - "trustedHash", hash2str(trustedHeader.Hash()), + interimHeader = interimBlock.Header + c.logger.Debug("Verify newHeader against verifiedHeader", + "trustedHeight", verifiedHeader.Height, + "trustedHash", hash2str(verifiedHeader.Hash()), "newHeight", interimHeader.Height, "newHash", hash2str(interimHeader.Hash())) - if err := VerifyBackwards(c.chainID, interimHeader, trustedHeader); err != nil { + if err := VerifyBackwards(interimHeader, verifiedHeader); err != nil { c.logger.Error("primary sent invalid header -> replacing", "err", err) if replaceErr := c.replacePrimaryProvider(); replaceErr != nil { c.logger.Error("Can't replace primary", "err", replaceErr) // return original error return fmt.Errorf("verify backwards from %d to %d failed: %w", - trustedHeader.Height, interimHeader.Height, err) + verifiedHeader.Height, interimHeader.Height, err) } + // we need to verify the header at the same height again + continue } - - trustedHeader = interimHeader - } - - // Initially trusted header might have expired at this point. - if HeaderExpired(initiallyTrustedHeader, c.trustingPeriod, now) { - return ErrOldHeaderExpired{initiallyTrustedHeader.Time.Add(c.trustingPeriod), now} + verifiedHeader = interimHeader } return nil } // compare header with all witnesses provided. -func (c *Client) compareNewHeaderWithWitnesses(h *types.SignedHeader, now time.Time) error { +func (c *Client) compareNewHeaderWithWitnesses(l *types.LightBlock, now time.Time) error { c.providerMutex.Lock() defer c.providerMutex.Unlock() @@ -1067,7 +943,7 @@ func (c *Client) compareNewHeaderWithWitnesses(h *types.SignedHeader, now time.T // launch one goroutine per witness errc := make(chan error, len(c.witnesses)) for i, witness := range c.witnesses { - go c.compareNewHeaderWithWitness(errc, h, witness, i, now) + go c.compareNewHeaderWithWitness(errc, l, witness, i, now) } witnessesToRemove := make([]int, 0) @@ -1081,12 +957,12 @@ func (c *Client) compareNewHeaderWithWitnesses(h *types.SignedHeader, now time.T headerMatched = true case ErrConflictingHeaders: // fork detected c.logger.Info("FORK DETECTED", "witness", e.Witness, "err", err) - c.sendConflictingHeadersEvidence(&types.ConflictingHeadersEvidence{H1: h, H2: e.H2}) + c.sendConflictingHeadersEvidence(&types.ConflictingHeadersEvidence{H1: e.H1, H2: e.H2}) lastErrConfHeaders = e case errBadWitness: c.logger.Info("Bad witness", "witness", c.witnesses[e.WitnessIndex], "err", err) // if witness sent us invalid header / vals, remove it - if e.Code == invalidHeader || e.Code == invalidValidatorSet { + if e.Code == invalidLightBlock { c.logger.Info("Witness sent us invalid header / vals -> removing it", "witness", c.witnesses[e.WitnessIndex]) witnessesToRemove = append(witnessesToRemove, e.WitnessIndex) } @@ -1112,22 +988,22 @@ func (c *Client) compareNewHeaderWithWitnesses(h *types.SignedHeader, now time.T return errors.New("awaiting response from all witnesses exceeded dropout time") } -func (c *Client) compareNewHeaderWithWitness(errc chan error, h *types.SignedHeader, +func (c *Client) compareNewHeaderWithWitness(errc chan error, l *types.LightBlock, witness provider.Provider, witnessIndex int, now time.Time) { - altH, altVals, err := c.signedHeaderAndValSetFromWitness(h.Height, witness) + altBlock, err := c.lightBlockFromWitness(l.Height, witness) if err != nil { err.WitnessIndex = witnessIndex errc <- err return } - if !bytes.Equal(h.Hash(), altH.Hash()) { - if bsErr := c.bisection(witness, c.latestTrustedHeader, c.latestTrustedVals, altH, altVals, now); bsErr != nil { - errc <- errBadWitness{bsErr, invalidHeader, witnessIndex} + if !bytes.Equal(l.Hash(), altBlock.Hash()) { + if bsErr := c.verifySkipping(witness, c.latestTrustedBlock, altBlock, now); bsErr != nil { + errc <- errBadWitness{bsErr, invalidLightBlock, witnessIndex} return } - errc <- ErrConflictingHeaders{H1: h, Primary: c.primary, H2: altH, Witness: witness} + errc <- ErrConflictingHeaders{H1: l.SignedHeader, Primary: c.primary, H2: altBlock.SignedHeader, Witness: witness} } errc <- nil @@ -1146,32 +1022,32 @@ func (c *Client) removeWitness(idx int) { } } -// Update attempts to advance the state by downloading the latest header and -// comparing it with the existing one. It returns a new header on a successful +// Update attempts to advance the state by downloading the latest light +// block and verifying it. It returns a new light block on a successful // update. Otherwise, it returns nil (plus an error, if any). -func (c *Client) Update(now time.Time) (*types.SignedHeader, error) { +func (c *Client) Update(now time.Time) (*types.LightBlock, error) { lastTrustedHeight, err := c.LastTrustedHeight() if err != nil { return nil, fmt.Errorf("can't get last trusted height: %w", err) } if lastTrustedHeight == -1 { - // no headers yet => wait + // no light blocks yet => wait return nil, nil } - latestHeader, latestVals, err := c.signedHeaderAndValSetFromPrimary(0) + latestBlock, err := c.lightBlockFromPrimary(0) if err != nil { return nil, err } - if latestHeader.Height > lastTrustedHeight { - err = c.VerifyHeader(latestHeader, latestVals, now) + if latestBlock.Height > lastTrustedHeight { + err = c.verifyLightBlock(latestBlock, now) if err != nil { return nil, err } - c.logger.Info("Advanced to new state", "height", latestHeader.Height, "hash", hash2str(latestHeader.Hash())) - return latestHeader, nil + c.logger.Info("Advanced to new state", "height", latestBlock.Height, "hash", hash2str(latestBlock.Hash())) + return latestBlock, nil } return nil, nil @@ -1183,39 +1059,38 @@ func (c *Client) replacePrimaryProvider() error { c.providerMutex.Lock() defer c.providerMutex.Unlock() - c.logger.Info("Primary is unavailable. Replacing with the first witness") if len(c.witnesses) <= 1 { return errNoWitnesses{} } c.primary = c.witnesses[0] c.witnesses = c.witnesses[1:] - c.logger.Info("New primary", "p", c.primary) + c.logger.Info("Replacing primary with the first witness", "new_primary", c.primary) return nil } -// signedHeaderFromPrimary retrieves the SignedHeader from the primary provider +// lightBlockFromPrimary retrieves the lightBlock from the primary provider // at the specified height. Handles dropout by the primary provider by swapping // with an alternative provider. -func (c *Client) signedHeaderFromPrimary(height int64) (*types.SignedHeader, error) { +func (c *Client) lightBlockFromPrimary(height int64) (*types.LightBlock, error) { for attempt := uint16(1); attempt <= c.maxRetryAttempts; attempt++ { c.providerMutex.Lock() - h, providerErr := c.primary.SignedHeader(height) + l, providerErr := c.primary.LightBlock(height) c.providerMutex.Unlock() if providerErr == nil { - err := c.validateHeader(h, height) + err := c.validateLightBlock(l, height) if err != nil { replaceErr := c.replacePrimaryProvider() if replaceErr != nil { return nil, fmt.Errorf("%v. Tried to replace primary but: %w", err.Error(), replaceErr) } - // replace primary and request signed header again - return c.signedHeaderFromPrimary(height) + // replace primary and request a light block again + return c.lightBlockFromPrimary(height) } - // valid header has been received - return h, nil + // valid light block has been received + return l, nil } - if providerErr == provider.ErrSignedHeaderNotFound { + if providerErr == provider.ErrLightBlockNotFound { return nil, providerErr } c.logger.Error("Failed to get signed header from primary", "attempt", attempt, "err", providerErr) @@ -1227,64 +1102,22 @@ func (c *Client) signedHeaderFromPrimary(height int64) (*types.SignedHeader, err return nil, fmt.Errorf("primary dropped out. Tried to replace but: %w", err) } - return c.signedHeaderFromPrimary(height) + return c.lightBlockFromPrimary(height) } -func (c *Client) validateHeader(h *types.SignedHeader, expectedHeight int64) error { - if h == nil { +func (c *Client) validateLightBlock(l *types.LightBlock, expectedHeight int64) error { + if l == nil { return errors.New("nil header") } - err := h.ValidateBasic(c.chainID) + err := l.ValidateBasic(c.chainID) if err != nil { return err } - if expectedHeight > 0 && h.Height != expectedHeight { - return errors.New("height mismatch") - } - return nil -} - -// validatorSetFromPrimary retrieves the ValidatorSet from the primary provider -// at the specified height. Handles dropout by the primary provider after 5 -// attempts by replacing it with an alternative provider. -func (c *Client) validatorSetFromPrimary(height int64) (*types.ValidatorSet, error) { - for attempt := uint16(1); attempt <= c.maxRetryAttempts; attempt++ { - c.providerMutex.Lock() - vals, providerErr := c.primary.ValidatorSet(height) - c.providerMutex.Unlock() - if providerErr == nil { - err := c.validateValidatorSet(vals) - if err != nil { - replaceErr := c.replacePrimaryProvider() - if replaceErr != nil { - return nil, fmt.Errorf("%v. Tried to replace primary but: %w", err.Error(), replaceErr) - } - // replace primary and request signed header again - return c.validatorSetFromPrimary(height) - } - return vals, nil - } - - if providerErr == provider.ErrValidatorSetNotFound { - return vals, providerErr - } - c.logger.Error("Failed to get validator set from primary", "attempt", attempt, "err", providerErr) - time.Sleep(backoffTimeout(attempt)) - } - - err := c.replacePrimaryProvider() - if err != nil { - return nil, fmt.Errorf("primary dropped out. Tried to replace but: %w", err) - } - return c.validatorSetFromPrimary(height) -} - -func (c *Client) validateValidatorSet(vals *types.ValidatorSet) error { - if vals == nil { - return errors.New("validator set is nil") + if expectedHeight > 0 && l.Height != expectedHeight { + return fmt.Errorf("height mismatch, got: %d, expected: %d", l.Height, expectedHeight) } - return vals.ValidateBasic() + return nil } // sendConflictingHeadersEvidence sends evidence to all witnesses and primary diff --git a/light/client_benchmark_test.go b/light/client_benchmark_test.go index ef0494c99..42bf58187 100644 --- a/light/client_benchmark_test.go +++ b/light/client_benchmark_test.go @@ -22,7 +22,7 @@ import ( // Remember that none of these benchmarks account for network latency. var ( benchmarkFullNode = mockp.New(GenMockNode(chainID, 1000, 100, 1, bTime)) - genesisHeader, _ = benchmarkFullNode.SignedHeader(1) + genesisBlock, _ = benchmarkFullNode.LightBlock(1) ) func BenchmarkSequence(b *testing.B) { @@ -31,7 +31,7 @@ func BenchmarkSequence(b *testing.B) { light.TrustOptions{ Period: 24 * time.Hour, Height: 1, - Hash: genesisHeader.Hash(), + Hash: genesisBlock.Hash(), }, benchmarkFullNode, []provider.Provider{benchmarkFullNode}, @@ -45,7 +45,7 @@ func BenchmarkSequence(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - _, err = c.VerifyHeaderAtHeight(1000, bTime.Add(1000*time.Minute)) + _, err = c.VerifyLightBlockAtHeight(1000, bTime.Add(1000*time.Minute)) if err != nil { b.Fatal(err) } @@ -58,7 +58,7 @@ func BenchmarkBisection(b *testing.B) { light.TrustOptions{ Period: 24 * time.Hour, Height: 1, - Hash: genesisHeader.Hash(), + Hash: genesisBlock.Hash(), }, benchmarkFullNode, []provider.Provider{benchmarkFullNode}, @@ -71,7 +71,7 @@ func BenchmarkBisection(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - _, err = c.VerifyHeaderAtHeight(1000, bTime.Add(1000*time.Minute)) + _, err = c.VerifyLightBlockAtHeight(1000, bTime.Add(1000*time.Minute)) if err != nil { b.Fatal(err) } @@ -79,13 +79,13 @@ func BenchmarkBisection(b *testing.B) { } func BenchmarkBackwards(b *testing.B) { - trustedHeader, _ := benchmarkFullNode.SignedHeader(0) + trustedBlock, _ := benchmarkFullNode.LightBlock(0) c, err := light.NewClient( chainID, light.TrustOptions{ Period: 24 * time.Hour, - Height: trustedHeader.Height, - Hash: trustedHeader.Hash(), + Height: trustedBlock.Height, + Hash: trustedBlock.Hash(), }, benchmarkFullNode, []provider.Provider{benchmarkFullNode}, @@ -98,7 +98,7 @@ func BenchmarkBackwards(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - _, err = c.VerifyHeaderAtHeight(1, bTime) + _, err = c.VerifyLightBlockAtHeight(1, bTime) if err != nil { b.Fatal(err) } diff --git a/light/client_test.go b/light/client_test.go index 5e0c520bf..ee1b1fd5f 100644 --- a/light/client_test.go +++ b/light/client_test.go @@ -53,6 +53,8 @@ var ( // last header (3/3 signed) 3: h3, } + l1 = &types.LightBlock{SignedHeader: h1, ValidatorSet: vals} + l2 = &types.LightBlock{SignedHeader: h2, ValidatorSet: vals} fullNode = mockp.New( chainID, headerSet, @@ -108,6 +110,11 @@ func TestValidateTrustOptions(t *testing.T) { } +func TestMock(t *testing.T) { + l, _ := fullNode.LightBlock(3) + assert.Equal(t, int64(3), l.Height) +} + func TestClient_SequentialVerification(t *testing.T) { newKeys := genPrivKeys(4) newVals := newKeys.ToValidators(10, 1) @@ -212,7 +219,7 @@ func TestClient_SequentialVerification(t *testing.T) { require.NoError(t, err) - _, err = c.VerifyHeaderAtHeight(3, bTime.Add(3*time.Hour)) + _, err = c.VerifyLightBlockAtHeight(3, bTime.Add(3*time.Hour)) if tc.verifyErr { assert.Error(t, err) } else { @@ -335,7 +342,7 @@ func TestClient_SkippingVerification(t *testing.T) { require.NoError(t, err) - _, err = c.VerifyHeaderAtHeight(3, bTime.Add(3*time.Hour)) + _, err = c.VerifyLightBlockAtHeight(3, bTime.Add(3*time.Hour)) if tc.verifyErr { assert.Error(t, err) } else { @@ -346,18 +353,18 @@ func TestClient_SkippingVerification(t *testing.T) { } -// start from a large header to make sure that the pivot height doesn't select a height outside +// start from a large light block 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) + l1, err := veryLargeFullNode.LightBlock(90) require.NoError(t, err) c, err := light.NewClient( chainID, light.TrustOptions{ Period: 4 * time.Hour, - Height: 90, - Hash: h1.Hash(), + Height: l1.Height, + Hash: l1.Hash(), }, veryLargeFullNode, []provider.Provider{veryLargeFullNode}, @@ -367,7 +374,7 @@ func TestClientLargeBisectionVerification(t *testing.T) { require.NoError(t, err) h, err := c.Update(bTime.Add(100 * time.Minute)) assert.NoError(t, err) - h2, err := veryLargeFullNode.SignedHeader(100) + h2, err := veryLargeFullNode.LightBlock(100) require.NoError(t, err) assert.Equal(t, h, h2) } @@ -387,15 +394,15 @@ func TestClientBisectionBetweenTrustedHeaders(t *testing.T) { ) require.NoError(t, err) - _, err = c.VerifyHeaderAtHeight(3, bTime.Add(2*time.Hour)) + _, err = c.VerifyLightBlockAtHeight(3, bTime.Add(2*time.Hour)) require.NoError(t, err) - // confirm that the client already doesn't have the header - _, err = c.TrustedHeader(2) + // confirm that the client already doesn't have the light block + _, err = c.TrustedLightBlock(2) require.Error(t, err) - // verify using bisection the header between the two trusted headers - _, err = c.VerifyHeaderAtHeight(2, bTime.Add(1*time.Hour)) + // verify using bisection the light block between the two trusted light blocks + _, err = c.VerifyLightBlockAtHeight(2, bTime.Add(1*time.Hour)) assert.NoError(t, err) } @@ -409,20 +416,16 @@ func TestClient_Cleanup(t *testing.T) { light.Logger(log.TestingLogger()), ) require.NoError(t, err) - _, err = c.TrustedHeader(1) + _, err = c.TrustedLightBlock(1) require.NoError(t, err) err = c.Cleanup() require.NoError(t, err) - // Check no headers/valsets exist after Cleanup. - h, err := c.TrustedHeader(1) + // Check no light blocks exist after Cleanup. + l, err := c.TrustedLightBlock(1) assert.Error(t, err) - assert.Nil(t, h) - - valSet, _, err := c.TrustedValidatorSet(1) - assert.Error(t, err) - assert.Nil(t, valSet) + assert.Nil(t, l) } // trustedHeader.Height == options.Height @@ -430,7 +433,7 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) { // 1. options.Hash == trustedHeader.Hash { trustedStore := dbs.New(dbm.NewMemDB(), chainID) - err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals) + err := trustedStore.SaveLightBlock(l1) require.NoError(t, err) c, err := light.NewClient( @@ -443,23 +446,17 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) { ) require.NoError(t, err) - h, err := c.TrustedHeader(1) + l, err := c.TrustedLightBlock(1) 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()) - } + assert.NotNil(t, l) + assert.Equal(t, l.Hash(), h1.Hash()) + assert.Equal(t, l.ValidatorSet.Hash(), h1.ValidatorsHash.Bytes()) } // 2. options.Hash != trustedHeader.Hash { trustedStore := dbs.New(dbm.NewMemDB(), chainID) - err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals) + err := trustedStore.SaveLightBlock(l1) require.NoError(t, err) // header1 != header @@ -489,17 +486,11 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) { ) require.NoError(t, err) - h, err := c.TrustedHeader(1) - assert.NoError(t, err) - if assert.NotNil(t, h) { - assert.Equal(t, h.Hash(), header1.Hash()) - } - - valSet, _, err := c.TrustedValidatorSet(1) + l, err := c.TrustedLightBlock(1) assert.NoError(t, err) - assert.NotNil(t, valSet) - if assert.NotNil(t, valSet) { - assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash()) + if assert.NotNil(t, l) { + assert.Equal(t, l.Hash(), header1.Hash()) + assert.NoError(t, l.ValidateBasic(chainID)) } } } @@ -509,7 +500,7 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) { // 1. options.Hash == trustedHeader.Hash { trustedStore := dbs.New(dbm.NewMemDB(), chainID) - err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals) + err := trustedStore.SaveLightBlock(l1) require.NoError(t, err) c, err := light.NewClient( @@ -527,24 +518,18 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) { require.NoError(t, err) // Check we still have the 1st header (+header+). - h, err := c.TrustedHeader(1) + l, err := c.TrustedLightBlock(1) 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()) - } + assert.NotNil(t, l) + assert.Equal(t, l.Hash(), h1.Hash()) + assert.NoError(t, l.ValidateBasic(chainID)) } // 2. options.Hash != trustedHeader.Hash // This could happen if previous provider was lying to us. { trustedStore := dbs.New(dbm.NewMemDB(), chainID) - err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals) + err := trustedStore.SaveLightBlock(l1) require.NoError(t, err) // header1 != header @@ -578,13 +563,9 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) { require.NoError(t, err) // Check we no longer have the invalid 1st header (+header+). - h, err := c.TrustedHeader(1) - assert.Error(t, err) - assert.Nil(t, h) - - valSet, _, err := c.TrustedValidatorSet(1) + l, err := c.TrustedLightBlock(1) assert.Error(t, err) - assert.Nil(t, valSet) + assert.Nil(t, l) } } @@ -594,10 +575,10 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) { { // load the first three headers into the trusted store trustedStore := dbs.New(dbm.NewMemDB(), chainID) - err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals) + err := trustedStore.SaveLightBlock(l1) require.NoError(t, err) - err = trustedStore.SaveSignedHeaderAndValidatorSet(h2, vals) + err = trustedStore.SaveLightBlock(l2) require.NoError(t, err) c, err := light.NewClient( @@ -610,38 +591,28 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) { ) require.NoError(t, err) - // Check we still have the 1st header (+header+). - h, err := c.TrustedHeader(1) - assert.NoError(t, err) - assert.NotNil(t, h) - assert.Equal(t, h.Hash(), h1.Hash()) - - valSet, _, err := c.TrustedValidatorSet(1) + // Check we still have the 1st light block. + l, err := c.TrustedLightBlock(1) assert.NoError(t, err) - assert.NotNil(t, valSet) - if assert.NotNil(t, valSet) { - assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash()) - } + assert.NotNil(t, l) + assert.Equal(t, l.Hash(), h1.Hash()) + assert.NoError(t, l.ValidateBasic(chainID)) - // Check we no longer have 2nd header (+header2+). - h, err = c.TrustedHeader(2) + // Check we no longer have 2nd light block. + l, err = c.TrustedLightBlock(2) assert.Error(t, err) - assert.Nil(t, h) + assert.Nil(t, l) - valSet, _, err = c.TrustedValidatorSet(2) + l, err = c.TrustedLightBlock(3) assert.Error(t, err) - assert.Nil(t, valSet) - - h, err = c.TrustedHeader(3) - assert.Error(t, err) - assert.Nil(t, h) + assert.Nil(t, l) } // 2. options.Hash != trustedHeader.Hash // This could happen if previous provider was lying to us. { trustedStore := dbs.New(dbm.NewMemDB(), chainID) - err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals) + err := trustedStore.SaveLightBlock(l1) require.NoError(t, err) // header1 != header @@ -650,7 +621,10 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) { header2 := keys.GenSignedHeader(chainID, 2, bTime.Add(2*time.Hour), nil, vals, vals, hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)) - err = trustedStore.SaveSignedHeaderAndValidatorSet(header2, vals) + err = trustedStore.SaveLightBlock(&types.LightBlock{ + SignedHeader: header2, + ValidatorSet: vals, + }) require.NoError(t, err) primary := mockp.New( @@ -675,27 +649,17 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) { ) require.NoError(t, err) - // Check we have swapped invalid 1st header (+header+) with correct one (+header1+). - h, err := c.TrustedHeader(1) - assert.NoError(t, err) - assert.NotNil(t, h) - assert.Equal(t, h.Hash(), header1.Hash()) - - valSet, _, err := c.TrustedValidatorSet(1) + // Check we have swapped invalid 1st light block (+lightblock+) with correct one (+lightblock2+). + l, err := c.TrustedLightBlock(1) assert.NoError(t, err) - assert.NotNil(t, valSet) - if assert.NotNil(t, valSet) { - assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash()) - } + assert.NotNil(t, l) + assert.Equal(t, l.Hash(), header1.Hash()) + assert.NoError(t, l.ValidateBasic(chainID)) - // Check we no longer have invalid 2nd header (+header2+). - h, err = c.TrustedHeader(2) + // Check we no longer have invalid 2nd light block (+lightblock2+). + l, err = c.TrustedLightBlock(2) assert.Error(t, err) - assert.Nil(t, h) - - valSet, _, err = c.TrustedValidatorSet(2) - assert.Error(t, err) - assert.Nil(t, valSet) + assert.Nil(t, l) } } @@ -711,16 +675,11 @@ func TestClient_Update(t *testing.T) { require.NoError(t, err) // should result in downloading & verifying header #3 - h, err := c.Update(bTime.Add(2 * time.Hour)) - assert.NoError(t, err) - if assert.NotNil(t, h) { - assert.EqualValues(t, 3, h.Height) - } - - valSet, _, err := c.TrustedValidatorSet(3) + l, err := c.Update(bTime.Add(2 * time.Hour)) assert.NoError(t, err) - if assert.NotNil(t, valSet) { - assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash()) + if assert.NotNil(t, l) { + assert.EqualValues(t, 3, l.Height) + assert.NoError(t, l.ValidateBasic(chainID)) } } @@ -735,7 +694,7 @@ func TestClient_Concurrency(t *testing.T) { ) require.NoError(t, err) - _, err = c.VerifyHeaderAtHeight(2, bTime.Add(2*time.Hour)) + _, err = c.VerifyLightBlockAtHeight(2, bTime.Add(2*time.Hour)) require.NoError(t, err) var wg sync.WaitGroup @@ -744,7 +703,7 @@ func TestClient_Concurrency(t *testing.T) { go func() { defer wg.Done() - // NOTE: Cleanup, Stop, VerifyHeaderAtHeight and Verify are not supposed + // NOTE: Cleanup, Stop, VerifyLightBlockAtHeight and Verify are not supposed // to be concurrenly safe. assert.Equal(t, chainID, c.ChainID()) @@ -755,13 +714,9 @@ func TestClient_Concurrency(t *testing.T) { _, err = c.FirstTrustedHeight() assert.NoError(t, err) - h, err := c.TrustedHeader(1) + l, err := c.TrustedLightBlock(1) assert.NoError(t, err) - assert.NotNil(t, h) - - vals, _, err := c.TrustedValidatorSet(2) - assert.NoError(t, err) - assert.NotNil(t, vals) + assert.NotNil(t, l) }() } @@ -789,7 +744,7 @@ func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) { func TestClient_BackwardsVerification(t *testing.T) { { - trustHeader, _ := largeFullNode.SignedHeader(6) + trustHeader, _ := largeFullNode.LightBlock(6) c, err := light.NewClient( chainID, light.TrustOptions{ @@ -805,42 +760,38 @@ func TestClient_BackwardsVerification(t *testing.T) { require.NoError(t, err) // 1) verify before the trusted header using backwards => expect no error - h, err := c.VerifyHeaderAtHeight(5, bTime.Add(6*time.Minute)) + h, err := c.VerifyLightBlockAtHeight(5, bTime.Add(6*time.Minute)) require.NoError(t, err) if assert.NotNil(t, h) { assert.EqualValues(t, 5, h.Height) } // 2) untrusted header is expired but trusted header is not => expect no error - h, err = c.VerifyHeaderAtHeight(3, bTime.Add(8*time.Minute)) + h, err = c.VerifyLightBlockAtHeight(3, bTime.Add(8*time.Minute)) assert.NoError(t, err) assert.NotNil(t, h) // 3) already stored headers should return the header without error - h, err = c.VerifyHeaderAtHeight(5, bTime.Add(6*time.Minute)) + h, err = c.VerifyLightBlockAtHeight(5, bTime.Add(6*time.Minute)) assert.NoError(t, err) assert.NotNil(t, h) // 4a) First verify latest header - _, err = c.VerifyHeaderAtHeight(9, bTime.Add(9*time.Minute)) + _, err = c.VerifyLightBlockAtHeight(9, bTime.Add(9*time.Minute)) require.NoError(t, err) // 4b) Verify backwards using bisection => expect no error - _, err = c.VerifyHeaderAtHeight(7, bTime.Add(10*time.Minute)) + _, err = c.VerifyLightBlockAtHeight(7, bTime.Add(9*time.Minute)) assert.NoError(t, err) // shouldn't have verified this header in the process - _, err = c.TrustedHeader(8) + _, err = c.TrustedLightBlock(8) assert.Error(t, err) - // 5) trusted header has expired => expect error - _, err = c.VerifyHeaderAtHeight(1, bTime.Add(20*time.Minute)) + // 5) Try bisection method, but closest header (at 7) has expired + // so expect error + _, err = c.VerifyLightBlockAtHeight(8, bTime.Add(12*time.Minute)) assert.Error(t, err) - // 6) Try bisection method, but closest header (at 7) has expired - // so change to backwards => expect no error - _, err = c.VerifyHeaderAtHeight(8, bTime.Add(12*time.Minute)) - assert.NoError(t, err) - } { testCases := []struct { @@ -852,7 +803,7 @@ func TestClient_BackwardsVerification(t *testing.T) { chainID, map[int64]*types.SignedHeader{ 1: h1, - 2: keys.GenSignedHeader(chainID, 1, bTime.Add(1*time.Hour), nil, vals, vals, + 2: keys.GenSignedHeader(chainID, 1, bTime.Add(30*time.Minute), nil, vals, vals, hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)), 3: h3, }, @@ -866,7 +817,7 @@ func TestClient_BackwardsVerification(t *testing.T) { map[int64]*types.SignedHeader{ 1: h1, 2: keys.GenSignedHeader(chainID, 2, bTime.Add(30*time.Minute), nil, vals, vals, - hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)), + hash("app_hash2"), hash("cons_hash23"), hash("results_hash30"), 0, len(keys)), 3: h3, }, valSet, @@ -874,7 +825,7 @@ func TestClient_BackwardsVerification(t *testing.T) { }, } - for _, tc := range testCases { + for idx, tc := range testCases { c, err := light.NewClient( chainID, light.TrustOptions{ @@ -887,10 +838,10 @@ func TestClient_BackwardsVerification(t *testing.T) { dbs.New(dbm.NewMemDB(), chainID), light.Logger(log.TestingLogger()), ) - require.NoError(t, err) + require.NoError(t, err, idx) - _, err = c.VerifyHeaderAtHeight(2, bTime.Add(1*time.Hour).Add(1*time.Second)) - assert.Error(t, err) + _, err = c.VerifyLightBlockAtHeight(2, bTime.Add(1*time.Hour).Add(1*time.Second)) + assert.Error(t, err, idx) } } } @@ -898,7 +849,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.SaveSignedHeaderAndValidatorSet(h1, vals) + err := db.SaveLightBlock(l1) require.NoError(t, err) c, err := light.NewClientFromTrustedStore( @@ -910,18 +861,11 @@ func TestClient_NewClientFromTrustedStore(t *testing.T) { ) require.NoError(t, err) - // 2) Check header exists (deadNode is being used to ensure we're not getting + // 2) Check light block exists (deadNode is being used to ensure we're not getting // it from primary) - h, err := c.TrustedHeader(1) - assert.NoError(t, err) - assert.EqualValues(t, 1, h.Height) - - valSet, _, err := c.TrustedValidatorSet(1) + h, err := c.TrustedLightBlock(1) assert.NoError(t, err) - assert.NotNil(t, valSet) - if assert.NotNil(t, valSet) { - assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash()) - } + assert.EqualValues(t, l1.Height, h.Height) } func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) { @@ -967,18 +911,18 @@ func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) { assert.EqualValues(t, 2, len(c.Witnesses())) // witness behaves incorrectly -> removed from list, no error - h, err := c.VerifyHeaderAtHeight(2, bTime.Add(2*time.Hour)) + l, err := c.VerifyLightBlockAtHeight(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) + // light block should still be verified + assert.EqualValues(t, 2, l.Height) - // remaining witnesses doesn't have header -> error - _, err = c.VerifyHeaderAtHeight(3, bTime.Add(2*time.Hour)) + // remaining witnesses don't have light block -> error + _, err = c.VerifyLightBlockAtHeight(3, bTime.Add(2*time.Hour)) if assert.Error(t, err) { assert.Equal(t, "awaiting response from all witnesses exceeded dropout time", err.Error()) } - // witness does not have a header -> left in the list + // witness does not have a light block -> left in the list assert.EqualValues(t, 1, len(c.Witnesses())) } @@ -1017,21 +961,16 @@ func TestClient_TrustedValidatorSet(t *testing.T) { chainID, trustOptions, noValSetNode, - []provider.Provider{fullNode, badValSetNode, fullNode}, + []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)) + _, err = c.VerifyLightBlockAtHeight(2, bTime.Add(2*time.Hour).Add(1*time.Second)) assert.NoError(t, err) assert.Equal(t, 1, len(c.Witnesses())) - - valSet, height, err := c.TrustedValidatorSet(0) - assert.NoError(t, err) - assert.NotNil(t, valSet) - assert.EqualValues(t, 2, height) } func TestClientReportsConflictingHeadersEvidence(t *testing.T) { @@ -1063,7 +1002,7 @@ func TestClientReportsConflictingHeadersEvidence(t *testing.T) { require.NoError(t, err) // Check verification returns an error. - _, err = c.VerifyHeaderAtHeight(2, bTime.Add(2*time.Hour)) + _, err = c.VerifyLightBlockAtHeight(2, bTime.Add(2*time.Hour)) if assert.Error(t, err) { assert.Contains(t, err.Error(), "does not match one") } @@ -1085,14 +1024,14 @@ func TestClientPrunesHeadersAndValidatorSets(t *testing.T) { light.PruningSize(1), ) require.NoError(t, err) - _, err = c.TrustedHeader(1) + _, err = c.TrustedLightBlock(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) + _, err = c.TrustedLightBlock(1) assert.Error(t, err) } @@ -1157,7 +1096,7 @@ func TestClientEnsureValidHeadersAndValSets(t *testing.T) { ) require.NoError(t, err) - _, err = c.VerifyHeaderAtHeight(3, bTime.Add(2*time.Hour)) + _, err = c.VerifyLightBlockAtHeight(3, bTime.Add(2*time.Hour)) if tc.err { assert.Error(t, err) } else { diff --git a/light/errors.go b/light/errors.go index 3846b00b3..5b14b959a 100644 --- a/light/errors.go +++ b/light/errors.go @@ -87,8 +87,7 @@ type badWitnessCode int const ( noResponse badWitnessCode = iota + 1 - invalidHeader - invalidValidatorSet + invalidLightBlock ) // errBadWitness is returned when the witness either does not respond or @@ -103,10 +102,8 @@ func (e errBadWitness) Error() string { switch e.Code { case noResponse: return fmt.Sprintf("failed to get a header/vals from witness: %v", e.Reason) - case invalidHeader: - return fmt.Sprintf("witness sent us invalid header: %v", e.Reason) - case invalidValidatorSet: - return fmt.Sprintf("witness sent us invalid validator set: %v", e.Reason) + case invalidLightBlock: + return fmt.Sprintf("witness sent us an invalid light block: %v", e.Reason) default: return fmt.Sprintf("unknown code: %d", e.Code) } diff --git a/light/example_test.go b/light/example_test.go index bd8aec92f..d17c3f059 100644 --- a/light/example_test.go +++ b/light/example_test.go @@ -11,6 +11,7 @@ import ( dbm "github.com/tendermint/tm-db" "github.com/tendermint/tendermint/abci/example/kvstore" + "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/light" "github.com/tendermint/tendermint/light/provider" httpp "github.com/tendermint/tendermint/light/provider/http" @@ -39,7 +40,7 @@ func ExampleClient_Update() { stdlog.Fatal(err) } - header, err := primary.SignedHeader(2) + block, err := primary.LightBlock(2) if err != nil { stdlog.Fatal(err) } @@ -54,12 +55,12 @@ func ExampleClient_Update() { light.TrustOptions{ Period: 504 * time.Hour, // 21 days Height: 2, - Hash: header.Hash(), + Hash: block.Hash(), }, primary, []provider.Provider{primary}, // NOTE: primary should not be used here dbs.New(db, chainID), - // Logger(log.TestingLogger()), + light.Logger(log.TestingLogger()), ) if err != nil { stdlog.Fatal(err) @@ -87,8 +88,8 @@ func ExampleClient_Update() { // Output: successful update } -// Manually getting headers and verifying them. -func ExampleClient_VerifyHeaderAtHeight() { +// Manually getting light blocks and verifying them. +func ExampleClient_VerifyLightBlockAtHeight() { // give Tendermint time to generate some blocks time.Sleep(5 * time.Second) @@ -108,7 +109,7 @@ func ExampleClient_VerifyHeaderAtHeight() { stdlog.Fatal(err) } - header, err := primary.SignedHeader(2) + block, err := primary.LightBlock(2) if err != nil { stdlog.Fatal(err) } @@ -123,12 +124,12 @@ func ExampleClient_VerifyHeaderAtHeight() { light.TrustOptions{ Period: 504 * time.Hour, // 21 days Height: 2, - Hash: header.Hash(), + Hash: block.Hash(), }, primary, []provider.Provider{primary}, // NOTE: primary should not be used here dbs.New(db, chainID), - // Logger(log.TestingLogger()), + light.Logger(log.TestingLogger()), ) if err != nil { stdlog.Fatal(err) @@ -137,12 +138,12 @@ func ExampleClient_VerifyHeaderAtHeight() { c.Cleanup() }() - _, err = c.VerifyHeaderAtHeight(3, time.Now()) + _, err = c.VerifyLightBlockAtHeight(3, time.Now()) if err != nil { stdlog.Fatal(err) } - h, err := c.TrustedHeader(3) + h, err := c.TrustedLightBlock(3) if err != nil { stdlog.Fatal(err) } diff --git a/light/provider/errors.go b/light/provider/errors.go index 05a242acd..0dea0dd1d 100644 --- a/light/provider/errors.go +++ b/light/provider/errors.go @@ -3,10 +3,7 @@ package provider import "errors" var ( - // ErrSignedHeaderNotFound is returned when a provider can't find the + // ErrLightBlockNotFound is returned when a provider can't find the // requested header. - ErrSignedHeaderNotFound = errors.New("signed header not found") - // ErrValidatorSetNotFound is returned when a provider can't find the - // requested validator set. - ErrValidatorSetNotFound = errors.New("validator set not found") + ErrLightBlockNotFound = errors.New("light block not found") ) diff --git a/light/provider/http/http.go b/light/provider/http/http.go index 5c67a9d15..b996c5059 100644 --- a/light/provider/http/http.go +++ b/light/provider/http/http.go @@ -55,9 +55,9 @@ func (p *http) String() string { return fmt.Sprintf("http{%s}", p.client.Remote()) } -// SignedHeader fetches a SignedHeader at the given height and checks the +// LightBlock fetches a LightBlock at the given height and checks the // chainID matches. -func (p *http) SignedHeader(height int64) (*types.SignedHeader, error) { +func (p *http) LightBlock(height int64) (*types.LightBlock, error) { h, err := validateHeight(height) if err != nil { return nil, err @@ -65,9 +65,9 @@ func (p *http) SignedHeader(height int64) (*types.SignedHeader, error) { commit, err := p.client.Commit(h) if err != nil { - // TODO: standartise errors on the RPC side + // TODO: standardize errors on the RPC side if regexpMissingHeight.MatchString(err.Error()) { - return nil, provider.ErrSignedHeaderNotFound + return nil, provider.ErrLightBlockNotFound } return nil, err } @@ -81,23 +81,12 @@ func (p *http) SignedHeader(height int64) (*types.SignedHeader, error) { return nil, fmt.Errorf("expected chainID %s, got %s", p.chainID, commit.Header.ChainID) } - return &commit.SignedHeader, nil -} - -// ValidatorSet fetches a ValidatorSet at the given height. Multiple HTTP -// requests might be required if the validator set size is over 100. -func (p *http) ValidatorSet(height int64) (*types.ValidatorSet, error) { - h, err := validateHeight(height) - if err != nil { - return nil, err - } - maxPerPage := 100 res, err := p.client.Validators(h, nil, &maxPerPage) if err != nil { - // TODO: standartise errors on the RPC side + // TODO: standardize errors on the RPC side if regexpMissingHeight.MatchString(err.Error()) { - return nil, provider.ErrValidatorSetNotFound + return nil, provider.ErrLightBlockNotFound } return nil, err } @@ -119,7 +108,12 @@ func (p *http) ValidatorSet(height int64) (*types.ValidatorSet, error) { page++ } - return types.ValidatorSetFromExistingValidators(vals) + valset := types.NewValidatorSet(vals) + + return &types.LightBlock{ + SignedHeader: &commit.SignedHeader, + ValidatorSet: valset, + }, nil } // ReportEvidence calls `/broadcast_evidence` endpoint. diff --git a/light/provider/http/http_test.go b/light/provider/http/http_test.go index a79103176..6952763db 100644 --- a/light/provider/http/http_test.go +++ b/light/provider/http/http_test.go @@ -65,7 +65,7 @@ func TestProvider(t *testing.T) { require.NoError(t, err) // let's get the highest block - sh, err := p.SignedHeader(0) + sh, err := p.LightBlock(0) require.NoError(t, err) assert.True(t, sh.Height < 1000) @@ -75,24 +75,16 @@ func TestProvider(t *testing.T) { // historical queries now work :) lower := sh.Height - 3 - sh, err = p.SignedHeader(lower) + sh, err = p.LightBlock(lower) require.NoError(t, err) assert.Equal(t, lower, sh.Height) // fetching missing heights (both future and pruned) should return appropriate errors - _, err = p.SignedHeader(1000) + _, err = p.LightBlock(1000) require.Error(t, err) - assert.Equal(t, provider.ErrSignedHeaderNotFound, err) + assert.Equal(t, provider.ErrLightBlockNotFound, err) - _, err = p.ValidatorSet(1000) + _, err = p.LightBlock(1) require.Error(t, err) - assert.Equal(t, provider.ErrValidatorSetNotFound, err) - - _, err = p.SignedHeader(1) - require.Error(t, err) - assert.Equal(t, provider.ErrSignedHeaderNotFound, err) - - _, err = p.ValidatorSet(1) - require.Error(t, err) - assert.Equal(t, provider.ErrValidatorSetNotFound, err) + assert.Equal(t, provider.ErrLightBlockNotFound, err) } diff --git a/light/provider/mock/deadmock.go b/light/provider/mock/deadmock.go index 3dcd16c2a..e4dc35f91 100644 --- a/light/provider/mock/deadmock.go +++ b/light/provider/mock/deadmock.go @@ -22,13 +22,10 @@ func (p *deadMock) ChainID() string { return p.chainID } func (p *deadMock) String() string { return "deadMock" } -func (p *deadMock) SignedHeader(height int64) (*types.SignedHeader, error) { +func (p *deadMock) LightBlock(height int64) (*types.LightBlock, error) { return nil, errNoResp } -func (p *deadMock) ValidatorSet(height int64) (*types.ValidatorSet, error) { - return nil, errNoResp -} func (p *deadMock) ReportEvidence(ev types.Evidence) error { return errNoResp } diff --git a/light/provider/mock/mock.go b/light/provider/mock/mock.go index a948a2b63..23f8f3821 100644 --- a/light/provider/mock/mock.go +++ b/light/provider/mock/mock.go @@ -47,24 +47,24 @@ func (p *Mock) String() string { return fmt.Sprintf("Mock{headers: %s, vals: %v}", headers.String(), vals.String()) } -func (p *Mock) SignedHeader(height int64) (*types.SignedHeader, error) { +func (p *Mock) LightBlock(height int64) (*types.LightBlock, error) { if height == 0 && len(p.headers) > 0 { - return p.headers[int64(len(p.headers))], nil + sh := p.headers[int64(len(p.headers))] + vals := p.vals[int64(len(p.vals))] + return &types.LightBlock{ + SignedHeader: sh, + ValidatorSet: vals, + }, nil } if _, ok := p.headers[height]; ok { - return p.headers[height], nil + sh := p.headers[height] + vals := p.vals[height] + return &types.LightBlock{ + SignedHeader: sh, + ValidatorSet: vals, + }, nil } - return nil, provider.ErrSignedHeaderNotFound -} - -func (p *Mock) ValidatorSet(height int64) (*types.ValidatorSet, error) { - if height == 0 && len(p.vals) > 0 { - return p.vals[int64(len(p.vals))], nil - } - if _, ok := p.vals[height]; ok { - return p.vals[height], nil - } - return nil, provider.ErrValidatorSetNotFound + return nil, provider.ErrLightBlockNotFound } func (p *Mock) ReportEvidence(ev types.Evidence) error { diff --git a/light/provider/provider.go b/light/provider/provider.go index 8af6e8650..1cf60da26 100644 --- a/light/provider/provider.go +++ b/light/provider/provider.go @@ -10,28 +10,17 @@ type Provider interface { // ChainID returns the blockchain ID. ChainID() string - // SignedHeader returns the SignedHeader that corresponds to the given + // LightBlock returns the LightBlock that corresponds to the given // height. // // 0 - the latest. // height must be >= 0. // - // If the provider fails to fetch the SignedHeader due to the IO or other + // If the provider fails to fetch the LightBlock due to the IO or other // issues, an error will be returned. - // If there's no SignedHeader for the given height, ErrSignedHeaderNotFound + // If there's no LightBlock for the given height, ErrLightBlockNotFound // error is returned. - SignedHeader(height int64) (*types.SignedHeader, error) - - // ValidatorSet returns the ValidatorSet that corresponds to height. - // - // 0 - the latest. - // height must be >= 0. - // - // If the provider fails to fetch the ValidatorSet due to the IO or other - // issues, an error will be returned. - // If there's no ValidatorSet for the given height, ErrValidatorSetNotFound - // error is returned. - ValidatorSet(height int64) (*types.ValidatorSet, error) + LightBlock(height int64) (*types.LightBlock, error) // ReportEvidence reports an evidence of misbehavior. ReportEvidence(ev types.Evidence) error diff --git a/light/rpc/client.go b/light/rpc/client.go index 2b167ebbe..ea4f79111 100644 --- a/light/rpc/client.go +++ b/light/rpc/client.go @@ -95,7 +95,7 @@ func (c *Client) ABCIQueryWithOptions(path string, data tmbytes.HexBytes, // Update the light client if we're behind. // NOTE: AppHash for height H is in header H+1. - h, err := c.updateLightClientIfNeededTo(resp.Height + 1) + l, err := c.updateLightClientIfNeededTo(resp.Height + 1) if err != nil { return nil, err } @@ -111,7 +111,7 @@ func (c *Client) ABCIQueryWithOptions(path string, data tmbytes.HexBytes, kp := merkle.KeyPath{} kp = kp.AppendKey([]byte(storeName), merkle.KeyEncodingURL) kp = kp.AppendKey(resp.Key, merkle.KeyEncodingURL) - err = c.prt.VerifyValue(resp.ProofOps, h.AppHash, kp.String(), resp.Value) + err = c.prt.VerifyValue(resp.ProofOps, l.AppHash, kp.String(), resp.Value) if err != nil { return nil, fmt.Errorf("verify value proof: %w", err) } @@ -120,7 +120,7 @@ func (c *Client) ABCIQueryWithOptions(path string, data tmbytes.HexBytes, // OR validate the ansence proof against the trusted header. // XXX How do we encode the key into a string... - err = c.prt.VerifyAbsence(resp.ProofOps, h.AppHash, string(resp.Key)) + err = c.prt.VerifyAbsence(resp.ProofOps, l.AppHash, string(resp.Key)) if err != nil { return nil, fmt.Errorf("verify absence proof: %w", err) } @@ -178,13 +178,13 @@ func (c *Client) ConsensusParams(height *int64) (*ctypes.ResultConsensusParams, } // Update the light client if we're behind. - h, err := c.updateLightClientIfNeededTo(res.BlockHeight) + l, err := c.updateLightClientIfNeededTo(res.BlockHeight) if err != nil { return nil, err } // Verify hash. - if cH, tH := types.HashConsensusParams(res.ConsensusParams), h.ConsensusHash; !bytes.Equal(cH, tH) { + if cH, tH := types.HashConsensusParams(res.ConsensusParams), l.ConsensusHash; !bytes.Equal(cH, tH) { return nil, fmt.Errorf("params hash %X does not match trusted hash %X", cH, tH) } @@ -224,7 +224,7 @@ func (c *Client) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlock // Verify each of the BlockMetas. for _, meta := range res.BlockMetas { - h, err := c.lc.TrustedHeader(meta.Header.Height) + h, err := c.lc.TrustedLightBlock(meta.Header.Height) if err != nil { return nil, fmt.Errorf("trusted header %d: %w", meta.Header.Height, err) } @@ -261,13 +261,13 @@ func (c *Client) Block(height *int64) (*ctypes.ResultBlock, error) { } // Update the light client if we're behind. - h, err := c.updateLightClientIfNeededTo(res.Block.Height) + l, err := c.updateLightClientIfNeededTo(res.Block.Height) if err != nil { return nil, err } // Verify block. - if bH, tH := res.Block.Hash(), h.Hash(); !bytes.Equal(bH, tH) { + if bH, tH := res.Block.Hash(), l.Hash(); !bytes.Equal(bH, tH) { return nil, fmt.Errorf("block header %X does not match with trusted header %X", bH, tH) } @@ -295,13 +295,13 @@ func (c *Client) BlockByHash(hash []byte) (*ctypes.ResultBlock, error) { } // Update the light client if we're behind. - h, err := c.updateLightClientIfNeededTo(res.Block.Height) + l, err := c.updateLightClientIfNeededTo(res.Block.Height) if err != nil { return nil, err } // Verify block. - if bH, tH := res.Block.Hash(), h.Hash(); !bytes.Equal(bH, tH) { + if bH, tH := res.Block.Hash(), l.Hash(); !bytes.Equal(bH, tH) { return nil, fmt.Errorf("block header %X does not match with trusted header %X", bH, tH) } @@ -336,7 +336,7 @@ func (c *Client) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) } // Update the light client if we're behind. - trustedHeader, err := c.updateLightClientIfNeededTo(h + 1) + trustedBlock, err := c.updateLightClientIfNeededTo(h + 1) if err != nil { return nil, err } @@ -364,9 +364,9 @@ func (c *Client) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) rH := merkle.HashFromByteSlices([][]byte{bbeBytes, results.Hash(), ebeBytes}) // Verify block results. - if !bytes.Equal(rH, trustedHeader.LastResultsHash) { + if !bytes.Equal(rH, trustedBlock.LastResultsHash) { return nil, fmt.Errorf("last results %X does not match with trusted last results %X", - rH, trustedHeader.LastResultsHash) + rH, trustedBlock.LastResultsHash) } return res, nil @@ -387,13 +387,13 @@ func (c *Client) Commit(height *int64) (*ctypes.ResultCommit, error) { } // Update the light client if we're behind. - h, err := c.updateLightClientIfNeededTo(res.Height) + l, err := c.updateLightClientIfNeededTo(res.Height) if err != nil { return nil, err } // Verify commit. - if rH, tH := res.Hash(), h.Hash(); !bytes.Equal(rH, tH) { + if rH, tH := res.Hash(), l.Hash(); !bytes.Equal(rH, tH) { return nil, fmt.Errorf("header %X does not match with trusted header %X", rH, tH) } @@ -415,13 +415,13 @@ func (c *Client) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) { } // Update the light client if we're behind. - h, err := c.updateLightClientIfNeededTo(res.Height) + l, err := c.updateLightClientIfNeededTo(res.Height) if err != nil { return nil, err } // Validate the proof. - return res, res.Proof.Validate(h.DataHash) + return res, res.Proof.Validate(l.DataHash) } func (c *Client) TxSearch(query string, prove bool, page, perPage *int, orderBy string) ( @@ -452,7 +452,7 @@ func (c *Client) Validators(height *int64, page, perPage *int) (*ctypes.ResultVa } // Update the light client if we're behind. - h, err := c.updateLightClientIfNeededTo(updateHeight) + l, err := c.updateLightClientIfNeededTo(updateHeight) if err != nil { return nil, err } @@ -462,9 +462,9 @@ func (c *Client) Validators(height *int64, page, perPage *int) (*ctypes.ResultVa case 1: // if it's the first block we need to validate with the current validator hash as opposed to the // next validator hash - tH = h.ValidatorsHash + tH = l.ValidatorsHash default: - tH = h.NextValidatorsHash + tH = l.NextValidatorsHash } // Verify validators. @@ -495,12 +495,12 @@ func (c *Client) UnsubscribeAll(ctx context.Context, subscriber string) error { return c.next.UnsubscribeAll(ctx, subscriber) } -func (c *Client) updateLightClientIfNeededTo(height int64) (*types.SignedHeader, error) { - h, err := c.lc.VerifyHeaderAtHeight(height, time.Now()) +func (c *Client) updateLightClientIfNeededTo(height int64) (*types.LightBlock, error) { + l, err := c.lc.VerifyLightBlockAtHeight(height, time.Now()) if err != nil { return nil, fmt.Errorf("failed to update light client to %d: %w", height, err) } - return h, nil + return l, nil } func (c *Client) RegisterOpDecoder(typ string, dec merkle.OpDecoder) { diff --git a/light/store/db/db.go b/light/store/db/db.go index 390b02991..adbb33871 100644 --- a/light/store/db/db.go +++ b/light/store/db/db.go @@ -6,7 +6,6 @@ import ( "regexp" "strconv" - "github.com/gogo/protobuf/proto" dbm "github.com/tendermint/tm-db" tmsync "github.com/tendermint/tendermint/libs/sync" @@ -40,30 +39,22 @@ func New(db dbm.DB, prefix string) store.Store { return &dbs{db: db, prefix: prefix, size: size} } -// SaveSignedHeaderAndValidatorSet persists SignedHeader and ValidatorSet to -// the db. +// SaveLightBlock persists LightBlock to the db. // // Safe for concurrent use by multiple goroutines. -func (s *dbs) SaveSignedHeaderAndValidatorSet(sh *types.SignedHeader, valSet *types.ValidatorSet) error { - if sh.Height <= 0 { +func (s *dbs) SaveLightBlock(lb *types.LightBlock) error { + if lb.Height <= 0 { panic("negative or zero height") } - pbsh := sh.ToProto() - - shBz, err := proto.Marshal(pbsh) - if err != nil { - return fmt.Errorf("marshalling SignedHeader: %w", err) - } - - pbvs, err := valSet.ToProto() + lbpb, err := lb.ToProto() if err != nil { - return fmt.Errorf("unable to transition validator set to protobuf: %w", err) + return fmt.Errorf("unable to convert light block to protobuf: %w", err) } - valSetBz, err := proto.Marshal(pbvs) + lbBz, err := lbpb.Marshal() if err != nil { - return fmt.Errorf("marshalling validator set: %w", err) + return fmt.Errorf("marshalling LightBlock: %w", err) } s.mtx.Lock() @@ -71,10 +62,7 @@ func (s *dbs) SaveSignedHeaderAndValidatorSet(sh *types.SignedHeader, valSet *ty b := s.db.NewBatch() defer b.Close() - if err = b.Set(s.shKey(sh.Height), shBz); err != nil { - return err - } - if err = b.Set(s.vsKey(sh.Height), valSetBz); err != nil { + if err = b.Set(s.lbKey(lb.Height), lbBz); err != nil { return err } if err = b.Set(sizeKey, marshalSize(s.size+1)); err != nil { @@ -88,11 +76,11 @@ func (s *dbs) SaveSignedHeaderAndValidatorSet(sh *types.SignedHeader, valSet *ty return nil } -// DeleteSignedHeaderAndValidatorSet deletes SignedHeader and ValidatorSet from +// DeleteLightBlockAndValidatorSet deletes the LightBlock from // the db. // // Safe for concurrent use by multiple goroutines. -func (s *dbs) DeleteSignedHeaderAndValidatorSet(height int64) error { +func (s *dbs) DeleteLightBlock(height int64) error { if height <= 0 { panic("negative or zero height") } @@ -102,10 +90,7 @@ func (s *dbs) DeleteSignedHeaderAndValidatorSet(height int64) error { b := s.db.NewBatch() defer b.Close() - if err := b.Delete(s.shKey(height)); err != nil { - return err - } - if err := b.Delete(s.vsKey(height)); err != nil { + if err := b.Delete(s.lbKey(height)); err != nil { return err } if err := b.Set(sizeKey, marshalSize(s.size-1)); err != nil { @@ -119,73 +104,43 @@ func (s *dbs) DeleteSignedHeaderAndValidatorSet(height int64) error { return nil } -// SignedHeader loads SignedHeader at the given height. +// LightBlock retrieves the LightBlock at the given height. // // Safe for concurrent use by multiple goroutines. -func (s *dbs) SignedHeader(height int64) (*types.SignedHeader, error) { +func (s *dbs) LightBlock(height int64) (*types.LightBlock, error) { if height <= 0 { panic("negative or zero height") } - bz, err := s.db.Get(s.shKey(height)) + bz, err := s.db.Get(s.lbKey(height)) if err != nil { panic(err) } if len(bz) == 0 { - return nil, store.ErrSignedHeaderNotFound + return nil, store.ErrLightBlockNotFound } - var pbsh tmproto.SignedHeader - err = proto.Unmarshal(bz, &pbsh) + var lbpb tmproto.LightBlock + err = lbpb.Unmarshal(bz) if err != nil { - return nil, err + return nil, fmt.Errorf("unmarshal error: %w", err) } - signedHeader, err := types.SignedHeaderFromProto(&pbsh) + lightBlock, err := types.LightBlockFromProto(&lbpb) if err != nil { - return nil, err + return nil, fmt.Errorf("proto conversion error: %w", err) } - return signedHeader, err + return lightBlock, err } -// ValidatorSet loads ValidatorSet at the given height. +// LastLightBlockHeight returns the last LightBlock height stored. // // Safe for concurrent use by multiple goroutines. -func (s *dbs) ValidatorSet(height int64) (*types.ValidatorSet, error) { - if height <= 0 { - panic("negative or zero height") - } - - bz, err := s.db.Get(s.vsKey(height)) - if err != nil { - panic(err) - } - if len(bz) == 0 { - return nil, store.ErrValidatorSetNotFound - } - - var pbvs tmproto.ValidatorSet - err = proto.Unmarshal(bz, &pbvs) - if err != nil { - return nil, err - } - - valSet, err := types.ValidatorSetFromProto(&pbvs) - if err != nil { - return nil, err - } - - return valSet, err -} - -// LastSignedHeaderHeight returns the last SignedHeader height stored. -// -// Safe for concurrent use by multiple goroutines. -func (s *dbs) LastSignedHeaderHeight() (int64, error) { +func (s *dbs) LastLightBlockHeight() (int64, error) { itr, err := s.db.ReverseIterator( - s.shKey(1), - append(s.shKey(1<<63-1), byte(0x00)), + s.lbKey(1), + append(s.lbKey(1<<63-1), byte(0x00)), ) if err != nil { panic(err) @@ -194,7 +149,7 @@ func (s *dbs) LastSignedHeaderHeight() (int64, error) { for itr.Valid() { key := itr.Key() - _, height, ok := parseShKey(key) + _, height, ok := parseLbKey(key) if ok { return height, nil } @@ -204,13 +159,13 @@ func (s *dbs) LastSignedHeaderHeight() (int64, error) { return -1, itr.Error() } -// FirstSignedHeaderHeight returns the first SignedHeader height stored. +// FirstLightBlockHeight returns the first LightBlock height stored. // // Safe for concurrent use by multiple goroutines. -func (s *dbs) FirstSignedHeaderHeight() (int64, error) { +func (s *dbs) FirstLightBlockHeight() (int64, error) { itr, err := s.db.Iterator( - s.shKey(1), - append(s.shKey(1<<63-1), byte(0x00)), + s.lbKey(1), + append(s.lbKey(1<<63-1), byte(0x00)), ) if err != nil { panic(err) @@ -219,7 +174,7 @@ func (s *dbs) FirstSignedHeaderHeight() (int64, error) { for itr.Valid() { key := itr.Key() - _, height, ok := parseShKey(key) + _, height, ok := parseLbKey(key) if ok { return height, nil } @@ -229,18 +184,18 @@ func (s *dbs) FirstSignedHeaderHeight() (int64, error) { return -1, itr.Error() } -// SignedHeaderBefore iterates over headers until it finds a header before -// the given height. It returns ErrSignedHeaderNotFound if no such header exists. +// LightBlockBefore iterates over light blocks until it finds a block before +// the given height. It returns ErrLightBlockNotFound if no such block exists. // // Safe for concurrent use by multiple goroutines. -func (s *dbs) SignedHeaderBefore(height int64) (*types.SignedHeader, error) { +func (s *dbs) LightBlockBefore(height int64) (*types.LightBlock, error) { if height <= 0 { panic("negative or zero height") } itr, err := s.db.ReverseIterator( - s.shKey(1), - s.shKey(height), + s.lbKey(1), + s.lbKey(height), ) if err != nil { panic(err) @@ -249,9 +204,9 @@ func (s *dbs) SignedHeaderBefore(height int64) (*types.SignedHeader, error) { for itr.Valid() { key := itr.Key() - _, existingHeight, ok := parseShKey(key) + _, existingHeight, ok := parseLbKey(key) if ok { - return s.SignedHeader(existingHeight) + return s.LightBlock(existingHeight) } itr.Next() } @@ -259,7 +214,7 @@ func (s *dbs) SignedHeaderBefore(height int64) (*types.SignedHeader, error) { return nil, err } - return nil, store.ErrSignedHeaderNotFound + return nil, store.ErrLightBlockNotFound } // Prune prunes header & validator set pairs until there are only size pairs @@ -279,8 +234,8 @@ func (s *dbs) Prune(size uint16) error { // 2) Iterate over headers and perform a batch operation. itr, err := s.db.Iterator( - s.shKey(1), - append(s.shKey(1<<63-1), byte(0x00)), + s.lbKey(1), + append(s.lbKey(1<<63-1), byte(0x00)), ) if err != nil { return err @@ -293,12 +248,9 @@ func (s *dbs) Prune(size uint16) error { pruned := 0 for itr.Valid() && numToPrune > 0 { key := itr.Key() - _, height, ok := parseShKey(key) + _, height, ok := parseLbKey(key) if ok { - if err = b.Delete(s.shKey(height)); err != nil { - return err - } - if err = b.Delete(s.vsKey(height)); err != nil { + if err = b.Delete(s.lbKey(height)); err != nil { return err } } @@ -337,15 +289,11 @@ func (s *dbs) Size() uint16 { return s.size } -func (s *dbs) shKey(height int64) []byte { - return []byte(fmt.Sprintf("sh/%s/%020d", s.prefix, height)) -} - -func (s *dbs) vsKey(height int64) []byte { - return []byte(fmt.Sprintf("vs/%s/%020d", s.prefix, height)) +func (s *dbs) lbKey(height int64) []byte { + return []byte(fmt.Sprintf("lb/%s/%020d", s.prefix, height)) } -var keyPattern = regexp.MustCompile(`^(sh|vs)/([^/]*)/([0-9]+)$`) +var keyPattern = regexp.MustCompile(`^(lb)/([^/]*)/([0-9]+)$`) func parseKey(key []byte) (part string, prefix string, height int64, ok bool) { submatch := keyPattern.FindSubmatch(key) @@ -362,10 +310,10 @@ func parseKey(key []byte) (part string, prefix string, height int64, ok bool) { return } -func parseShKey(key []byte) (prefix string, height int64, ok bool) { +func parseLbKey(key []byte) (prefix string, height int64, ok bool) { var part string part, prefix, height, ok = parseKey(key) - if part != "sh" { + if part != "lb" { return "", 0, false } return diff --git a/light/store/db/db_test.go b/light/store/db/db_test.go index f3f705120..a0b71e6fa 100644 --- a/light/store/db/db_test.go +++ b/light/store/db/db_test.go @@ -3,6 +3,7 @@ package db import ( "sync" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -10,91 +11,78 @@ import ( dbm "github.com/tendermint/tm-db" "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/tmhash" tmrand "github.com/tendermint/tendermint/libs/rand" "github.com/tendermint/tendermint/types" ) -func TestLast_FirstSignedHeaderHeight(t *testing.T) { - dbStore := New(dbm.NewMemDB(), "TestLast_FirstSignedHeaderHeight") - vals, _ := types.RandValidatorSet(10, 100) +func TestLast_FirstLightBlockHeight(t *testing.T) { + dbStore := New(dbm.NewMemDB(), "TestLast_FirstLightBlockHeight") // Empty store - height, err := dbStore.LastSignedHeaderHeight() + height, err := dbStore.LastLightBlockHeight() require.NoError(t, err) assert.EqualValues(t, -1, height) - height, err = dbStore.FirstSignedHeaderHeight() + height, err = dbStore.FirstLightBlockHeight() require.NoError(t, err) assert.EqualValues(t, -1, height) // 1 key - err = dbStore.SaveSignedHeaderAndValidatorSet( - &types.SignedHeader{Header: &types.Header{Height: 1}}, vals) + err = dbStore.SaveLightBlock(randLightBlock(int64(1))) require.NoError(t, err) - height, err = dbStore.LastSignedHeaderHeight() + height, err = dbStore.LastLightBlockHeight() require.NoError(t, err) assert.EqualValues(t, 1, height) - height, err = dbStore.FirstSignedHeaderHeight() + height, err = dbStore.FirstLightBlockHeight() require.NoError(t, err) assert.EqualValues(t, 1, height) } -func Test_SaveSignedHeaderAndValidatorSet(t *testing.T) { - dbStore := New(dbm.NewMemDB(), "Test_SaveSignedHeaderAndValidatorSet") - vals, _ := types.RandValidatorSet(10, 100) +func Test_SaveLightBlock(t *testing.T) { + dbStore := New(dbm.NewMemDB(), "Test_SaveLightBlockAndValidatorSet") + // Empty store - h, err := dbStore.SignedHeader(1) + h, err := dbStore.LightBlock(1) require.Error(t, err) assert.Nil(t, h) - valSet, err := dbStore.ValidatorSet(1) - require.Error(t, err) - assert.Nil(t, valSet) - // 1 key - pa := vals.Validators[0].Address - err = dbStore.SaveSignedHeaderAndValidatorSet( - &types.SignedHeader{Header: &types.Header{Height: 1, ProposerAddress: pa}}, vals) + err = dbStore.SaveLightBlock(randLightBlock(1)) require.NoError(t, err) - h, err = dbStore.SignedHeader(1) - require.NoError(t, err) - assert.NotNil(t, h) + size := dbStore.Size() + assert.Equal(t, uint16(1), size) + t.Log(size) - valSet, err = dbStore.ValidatorSet(1) + h, err = dbStore.LightBlock(1) require.NoError(t, err) - assert.NotNil(t, valSet) + assert.NotNil(t, h) // Empty store - err = dbStore.DeleteSignedHeaderAndValidatorSet(1) + err = dbStore.DeleteLightBlock(1) require.NoError(t, err) - h, err = dbStore.SignedHeader(1) + h, err = dbStore.LightBlock(1) require.Error(t, err) assert.Nil(t, h) - valSet, err = dbStore.ValidatorSet(1) - require.Error(t, err) - assert.Nil(t, valSet) } -func Test_SignedHeaderBefore(t *testing.T) { - dbStore := New(dbm.NewMemDB(), "Test_SignedHeaderBefore") - valSet, _ := types.RandValidatorSet(10, 100) - pa := valSet.Proposer.Address +func Test_LightBlockBefore(t *testing.T) { + dbStore := New(dbm.NewMemDB(), "Test_LightBlockBefore") assert.Panics(t, func() { - _, _ = dbStore.SignedHeaderBefore(0) - _, _ = dbStore.SignedHeaderBefore(100) + _, _ = dbStore.LightBlockBefore(0) + _, _ = dbStore.LightBlockBefore(100) }) - err := dbStore.SaveSignedHeaderAndValidatorSet( - &types.SignedHeader{Header: &types.Header{Height: 2, ProposerAddress: pa}}, valSet) + err := dbStore.SaveLightBlock(randLightBlock(int64(2))) require.NoError(t, err) - h, err := dbStore.SignedHeaderBefore(3) + h, err := dbStore.LightBlockBefore(3) require.NoError(t, err) if assert.NotNil(t, h) { assert.EqualValues(t, 2, h.Height) @@ -103,7 +91,6 @@ func Test_SignedHeaderBefore(t *testing.T) { func Test_Prune(t *testing.T) { dbStore := New(dbm.NewMemDB(), "Test_Prune") - valSet, _ := types.RandValidatorSet(10, 100) // Empty store assert.EqualValues(t, 0, dbStore.Size()) @@ -111,8 +98,7 @@ func Test_Prune(t *testing.T) { require.NoError(t, err) // One header - err = dbStore.SaveSignedHeaderAndValidatorSet( - &types.SignedHeader{Header: &types.Header{Height: 2}}, valSet) + err = dbStore.SaveLightBlock(randLightBlock(2)) require.NoError(t, err) assert.EqualValues(t, 1, dbStore.Size()) @@ -127,8 +113,7 @@ func Test_Prune(t *testing.T) { // Multiple headers for i := 1; i <= 10; i++ { - err = dbStore.SaveSignedHeaderAndValidatorSet( - &types.SignedHeader{Header: &types.Header{Height: int64(i)}}, valSet) + err = dbStore.SaveLightBlock(randLightBlock(int64(i))) require.NoError(t, err) } @@ -143,7 +128,6 @@ func Test_Prune(t *testing.T) { func Test_Concurrency(t *testing.T) { dbStore := New(dbm.NewMemDB(), "Test_Prune") - vals, _ := types.RandValidatorSet(10, 100) var wg sync.WaitGroup for i := 1; i <= 100; i++ { @@ -151,24 +135,19 @@ func Test_Concurrency(t *testing.T) { go func(i int64) { defer wg.Done() - err := dbStore.SaveSignedHeaderAndValidatorSet( - &types.SignedHeader{Header: &types.Header{Height: i, - ProposerAddress: tmrand.Bytes(crypto.AddressSize)}}, vals) + err := dbStore.SaveLightBlock(randLightBlock(i)) require.NoError(t, err) - _, err = dbStore.SignedHeader(i) + _, err = dbStore.LightBlock(i) if err != nil { t.Log(err) } - _, err = dbStore.ValidatorSet(i) - if err != nil { - t.Log(err) // could not find validator set - } - _, err = dbStore.LastSignedHeaderHeight() + + _, err = dbStore.LastLightBlockHeight() if err != nil { t.Log(err) } - _, err = dbStore.FirstSignedHeaderHeight() + _, err = dbStore.FirstLightBlockHeight() if err != nil { t.Log(err) } @@ -179,7 +158,7 @@ func Test_Concurrency(t *testing.T) { } _ = dbStore.Size() - err = dbStore.DeleteSignedHeaderAndValidatorSet(1) + err = dbStore.DeleteLightBlock(1) if err != nil { t.Log(err) } @@ -188,3 +167,28 @@ func Test_Concurrency(t *testing.T) { wg.Wait() } + +func randLightBlock(height int64) *types.LightBlock { + vals, _ := types.RandValidatorSet(2, 1) + return &types.LightBlock{ + SignedHeader: &types.SignedHeader{ + Header: &types.Header{ + ChainID: tmrand.Str(12), + Height: height, + Time: time.Now(), + LastBlockID: types.BlockID{}, + LastCommitHash: crypto.CRandBytes(tmhash.Size), + DataHash: crypto.CRandBytes(tmhash.Size), + ValidatorsHash: crypto.CRandBytes(tmhash.Size), + NextValidatorsHash: crypto.CRandBytes(tmhash.Size), + ConsensusHash: crypto.CRandBytes(tmhash.Size), + AppHash: crypto.CRandBytes(tmhash.Size), + LastResultsHash: crypto.CRandBytes(tmhash.Size), + EvidenceHash: crypto.CRandBytes(tmhash.Size), + ProposerAddress: crypto.CRandBytes(crypto.AddressSize), + }, + Commit: &types.Commit{}, + }, + ValidatorSet: vals, + } +} diff --git a/light/store/errors.go b/light/store/errors.go index 2f77bc893..099b5964d 100644 --- a/light/store/errors.go +++ b/light/store/errors.go @@ -3,11 +3,7 @@ package store import "errors" var ( - // ErrSignedHeaderNotFound is returned when a store does not have the + // ErrLightBlockNotFound 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") + ErrLightBlockNotFound = errors.New("light block not found") ) diff --git a/light/store/store.go b/light/store/store.go index 13f3a0492..7c29f233d 100644 --- a/light/store/store.go +++ b/light/store/store.go @@ -8,43 +8,36 @@ type Store interface { // ValidatorSet (h: sh.Height). // // height must be > 0. - SaveSignedHeaderAndValidatorSet(sh *types.SignedHeader, valSet *types.ValidatorSet) error + SaveLightBlock(lb *types.LightBlock) error // DeleteSignedHeaderAndValidatorSet deletes SignedHeader (h: height) and // ValidatorSet (h: height). // // height must be > 0. - DeleteSignedHeaderAndValidatorSet(height int64) error + DeleteLightBlock(height int64) error - // SignedHeader returns the SignedHeader that corresponds to the given + // LightBlock returns the LightBlock that corresponds to the given // height. // // height must be > 0. // - // If SignedHeader is not found, ErrSignedHeaderNotFound is returned. - SignedHeader(height int64) (*types.SignedHeader, error) + // If LightBlock is not found, ErrLightBlockNotFound is returned. + LightBlock(height int64) (*types.LightBlock, error) - // ValidatorSet returns the ValidatorSet that corresponds to height. - // - // height must be > 0. - // - // If ValidatorSet is not found, ErrValidatorSetNotFound is returned. - ValidatorSet(height int64) (*types.ValidatorSet, error) - - // LastSignedHeaderHeight returns the last (newest) SignedHeader height. + // LastLightBlockHeight returns the last (newest) LightBlock height. // // If the store is empty, -1 and nil error are returned. - LastSignedHeaderHeight() (int64, error) + LastLightBlockHeight() (int64, error) - // FirstSignedHeaderHeight returns the first (oldest) SignedHeader height. + // FirstLightBlockHeight returns the first (oldest) LightBlock height. // // If the store is empty, -1 and nil error are returned. - FirstSignedHeaderHeight() (int64, error) + FirstLightBlockHeight() (int64, error) - // SignedHeaderBefore returns the SignedHeader before a certain height. + // LightBlockBefore returns the LightBlock before a certain height. // - // height must be > 0 && <= LastSignedHeaderHeight. - SignedHeaderBefore(height int64) (*types.SignedHeader, error) + // height must be > 0 && <= LastLightBlockHeight. + LightBlockBefore(height int64) (*types.LightBlock, error) // Prune removes headers & the associated validator sets when Store reaches a // defined size (number of header & validator set pairs). diff --git a/light/verifier.go b/light/verifier.go index 173a30bf6..75c2b5d10 100644 --- a/light/verifier.go +++ b/light/verifier.go @@ -224,11 +224,15 @@ func HeaderExpired(h *types.SignedHeader, trustingPeriod time.Duration, now time // of the trusted header // // For any of these cases ErrInvalidHeader is returned. -func VerifyBackwards(chainID string, untrustedHeader, trustedHeader *types.SignedHeader) error { - if err := untrustedHeader.ValidateBasic(chainID); err != nil { +func VerifyBackwards(untrustedHeader, trustedHeader *types.Header) error { + if err := untrustedHeader.ValidateBasic(); err != nil { return ErrInvalidHeader{err} } + if untrustedHeader.ChainID != trustedHeader.ChainID { + return ErrInvalidHeader{errors.New("header belongs to another chain")} + } + if !untrustedHeader.Time.Before(trustedHeader.Time) { return ErrInvalidHeader{ fmt.Errorf("expected older header time %v to be before new header time %v", diff --git a/proto/tendermint/types/types.pb.go b/proto/tendermint/types/types.pb.go index 2048bdf07..c38e60c29 100644 --- a/proto/tendermint/types/types.pb.go +++ b/proto/tendermint/types/types.pb.go @@ -873,6 +873,58 @@ func (m *SignedHeader) GetCommit() *Commit { return nil } +type LightBlock struct { + SignedHeader *SignedHeader `protobuf:"bytes,1,opt,name=signed_header,json=signedHeader,proto3" json:"signed_header,omitempty"` + ValidatorSet *ValidatorSet `protobuf:"bytes,2,opt,name=validator_set,json=validatorSet,proto3" json:"validator_set,omitempty"` +} + +func (m *LightBlock) Reset() { *m = LightBlock{} } +func (m *LightBlock) String() string { return proto.CompactTextString(m) } +func (*LightBlock) ProtoMessage() {} +func (*LightBlock) Descriptor() ([]byte, []int) { + return fileDescriptor_d3a6e55e2345de56, []int{10} +} +func (m *LightBlock) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *LightBlock) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_LightBlock.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *LightBlock) XXX_Merge(src proto.Message) { + xxx_messageInfo_LightBlock.Merge(m, src) +} +func (m *LightBlock) XXX_Size() int { + return m.Size() +} +func (m *LightBlock) XXX_DiscardUnknown() { + xxx_messageInfo_LightBlock.DiscardUnknown(m) +} + +var xxx_messageInfo_LightBlock proto.InternalMessageInfo + +func (m *LightBlock) GetSignedHeader() *SignedHeader { + if m != nil { + return m.SignedHeader + } + return nil +} + +func (m *LightBlock) GetValidatorSet() *ValidatorSet { + if m != nil { + return m.ValidatorSet + } + return nil +} + type BlockMeta struct { BlockID BlockID `protobuf:"bytes,1,opt,name=block_id,json=blockId,proto3" json:"block_id"` BlockSize int64 `protobuf:"varint,2,opt,name=block_size,json=blockSize,proto3" json:"block_size,omitempty"` @@ -884,7 +936,7 @@ func (m *BlockMeta) Reset() { *m = BlockMeta{} } func (m *BlockMeta) String() string { return proto.CompactTextString(m) } func (*BlockMeta) ProtoMessage() {} func (*BlockMeta) Descriptor() ([]byte, []int) { - return fileDescriptor_d3a6e55e2345de56, []int{10} + return fileDescriptor_d3a6e55e2345de56, []int{11} } func (m *BlockMeta) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -952,7 +1004,7 @@ func (m *TxProof) Reset() { *m = TxProof{} } func (m *TxProof) String() string { return proto.CompactTextString(m) } func (*TxProof) ProtoMessage() {} func (*TxProof) Descriptor() ([]byte, []int) { - return fileDescriptor_d3a6e55e2345de56, []int{11} + return fileDescriptor_d3a6e55e2345de56, []int{12} } func (m *TxProof) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1015,6 +1067,7 @@ func init() { proto.RegisterType((*CommitSig)(nil), "tendermint.types.CommitSig") proto.RegisterType((*Proposal)(nil), "tendermint.types.Proposal") proto.RegisterType((*SignedHeader)(nil), "tendermint.types.SignedHeader") + proto.RegisterType((*LightBlock)(nil), "tendermint.types.LightBlock") proto.RegisterType((*BlockMeta)(nil), "tendermint.types.BlockMeta") proto.RegisterType((*TxProof)(nil), "tendermint.types.TxProof") } @@ -1022,89 +1075,93 @@ func init() { func init() { proto.RegisterFile("tendermint/types/types.proto", fileDescriptor_d3a6e55e2345de56) } var fileDescriptor_d3a6e55e2345de56 = []byte{ - // 1312 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xcf, 0x6e, 0xdb, 0xc6, - 0x13, 0x36, 0x25, 0xca, 0x92, 0x46, 0x96, 0x2d, 0x2f, 0x9c, 0x44, 0x51, 0x62, 0x99, 0xd0, 0x0f, - 0xbf, 0xd6, 0x49, 0x03, 0x2a, 0x75, 0x8a, 0xfe, 0x41, 0xd1, 0x83, 0x64, 0x3b, 0x89, 0x10, 0x5b, - 0x56, 0x29, 0x25, 0x45, 0x7b, 0x21, 0x28, 0x71, 0x23, 0xb1, 0xa1, 0xb8, 0x04, 0xb9, 0x72, 0xed, - 0x3c, 0x41, 0xe1, 0x53, 0xfa, 0x00, 0x3a, 0xb5, 0x87, 0xde, 0xfb, 0x06, 0x3d, 0xe5, 0x98, 0x5b, - 0x7b, 0x69, 0x5a, 0x38, 0x40, 0xd1, 0xc7, 0x28, 0xf6, 0x8f, 0x28, 0xca, 0xb2, 0xdb, 0x20, 0x08, - 0x7a, 0x11, 0x76, 0x67, 0xbe, 0x99, 0xdd, 0xf9, 0xf6, 0xdb, 0x1d, 0x0a, 0xae, 0x53, 0xec, 0xd9, - 0x38, 0x18, 0x3a, 0x1e, 0xad, 0xd2, 0x63, 0x1f, 0x87, 0xe2, 0x57, 0xf7, 0x03, 0x42, 0x09, 0x2a, - 0x4c, 0xbd, 0x3a, 0xb7, 0x97, 0xd6, 0xfa, 0xa4, 0x4f, 0xb8, 0xb3, 0xca, 0x46, 0x02, 0x57, 0xda, - 0xe8, 0x13, 0xd2, 0x77, 0x71, 0x95, 0xcf, 0xba, 0xa3, 0xc7, 0x55, 0xea, 0x0c, 0x71, 0x48, 0xad, - 0xa1, 0x2f, 0x01, 0x5a, 0x6c, 0x19, 0xd7, 0xe9, 0x86, 0xd5, 0xae, 0x43, 0x67, 0x96, 0x2a, 0xad, - 0xc7, 0x10, 0xbd, 0xe0, 0xd8, 0xa7, 0x84, 0x65, 0x23, 0x8f, 0xa5, 0xbb, 0x1c, 0x73, 0x1f, 0xe2, - 0x20, 0x74, 0x88, 0x17, 0x0f, 0xaf, 0x7c, 0x02, 0xf9, 0x96, 0x15, 0xd0, 0x36, 0xa6, 0xf7, 0xb1, - 0x65, 0xe3, 0x00, 0xad, 0x41, 0x8a, 0x12, 0x6a, 0xb9, 0x45, 0x45, 0x53, 0x36, 0xf3, 0x86, 0x98, - 0x20, 0x04, 0xea, 0xc0, 0x0a, 0x07, 0xc5, 0x84, 0xa6, 0x6c, 0x2e, 0x19, 0x7c, 0x5c, 0x19, 0x80, - 0xca, 0x42, 0x59, 0x84, 0xe3, 0xd9, 0xf8, 0x68, 0x12, 0xc1, 0x27, 0xcc, 0xda, 0x3d, 0xa6, 0x38, - 0x94, 0x21, 0x62, 0x82, 0x3e, 0x80, 0x14, 0xdf, 0x5d, 0x31, 0xa9, 0x29, 0x9b, 0xb9, 0xad, 0xa2, - 0x1e, 0x23, 0x4a, 0xec, 0x5e, 0x6f, 0x31, 0x7f, 0x5d, 0x7d, 0xfe, 0x72, 0x63, 0xc1, 0x10, 0xe0, - 0x8a, 0x0b, 0xe9, 0xba, 0x4b, 0x7a, 0x4f, 0x1a, 0x3b, 0xd1, 0x46, 0x94, 0xe9, 0x46, 0xd0, 0x3e, - 0xac, 0xf8, 0x56, 0x40, 0xcd, 0x10, 0x53, 0x73, 0xc0, 0xab, 0xe0, 0x8b, 0xe6, 0xb6, 0x36, 0xf4, - 0xb3, 0xe7, 0xa0, 0xcf, 0x14, 0x2b, 0x57, 0xc9, 0xfb, 0x71, 0x63, 0xe5, 0x4f, 0x15, 0x16, 0x25, - 0x19, 0x9f, 0x41, 0x5a, 0x92, 0xc6, 0x17, 0xcc, 0x6d, 0xad, 0xc7, 0x33, 0x4a, 0x97, 0xbe, 0x4d, - 0xbc, 0x10, 0x7b, 0xe1, 0x28, 0x94, 0xf9, 0x26, 0x31, 0xe8, 0x1d, 0xc8, 0xf4, 0x06, 0x96, 0xe3, - 0x99, 0x8e, 0xcd, 0x77, 0x94, 0xad, 0xe7, 0x4e, 0x5f, 0x6e, 0xa4, 0xb7, 0x99, 0xad, 0xb1, 0x63, - 0xa4, 0xb9, 0xb3, 0x61, 0xa3, 0xcb, 0xb0, 0x38, 0xc0, 0x4e, 0x7f, 0x40, 0x39, 0x2d, 0x49, 0x43, - 0xce, 0xd0, 0xc7, 0xa0, 0x32, 0x41, 0x14, 0x55, 0xbe, 0x76, 0x49, 0x17, 0x6a, 0xd1, 0x27, 0x6a, - 0xd1, 0x3b, 0x13, 0xb5, 0xd4, 0x33, 0x6c, 0xe1, 0x67, 0xbf, 0x6f, 0x28, 0x06, 0x8f, 0x40, 0xdb, - 0x90, 0x77, 0xad, 0x90, 0x9a, 0x5d, 0x46, 0x1b, 0x5b, 0x3e, 0xc5, 0x53, 0x5c, 0x9d, 0x27, 0x44, - 0x12, 0x2b, 0xb7, 0x9e, 0x63, 0x51, 0xc2, 0x64, 0xa3, 0x4d, 0x28, 0xf0, 0x24, 0x3d, 0x32, 0x1c, - 0x3a, 0xd4, 0xe4, 0xbc, 0x2f, 0x72, 0xde, 0x97, 0x99, 0x7d, 0x9b, 0x9b, 0xef, 0xb3, 0x13, 0xb8, - 0x06, 0x59, 0xdb, 0xa2, 0x96, 0x80, 0xa4, 0x39, 0x24, 0xc3, 0x0c, 0xdc, 0xf9, 0x2e, 0xac, 0x1c, - 0x5a, 0xae, 0x63, 0x5b, 0x94, 0x04, 0xa1, 0x80, 0x64, 0x44, 0x96, 0xa9, 0x99, 0x03, 0x6f, 0xc3, - 0x9a, 0x87, 0x8f, 0xa8, 0x79, 0x16, 0x9d, 0xe5, 0x68, 0xc4, 0x7c, 0x8f, 0x66, 0x23, 0xfe, 0x0f, - 0xcb, 0xbd, 0x09, 0xf9, 0x02, 0x0b, 0x1c, 0x9b, 0x8f, 0xac, 0x1c, 0x76, 0x15, 0x32, 0x96, 0xef, - 0x0b, 0x40, 0x8e, 0x03, 0xd2, 0x96, 0xef, 0x73, 0xd7, 0x4d, 0x58, 0xe5, 0x35, 0x06, 0x38, 0x1c, - 0xb9, 0x54, 0x26, 0x59, 0xe2, 0x98, 0x15, 0xe6, 0x30, 0x84, 0x9d, 0x63, 0xff, 0x07, 0x79, 0x7c, - 0xe8, 0xd8, 0xd8, 0xeb, 0x61, 0x81, 0xcb, 0x73, 0xdc, 0xd2, 0xc4, 0xc8, 0x41, 0x37, 0xa0, 0xe0, - 0x07, 0xc4, 0x27, 0x21, 0x0e, 0x4c, 0xcb, 0xb6, 0x03, 0x1c, 0x86, 0xc5, 0x65, 0x91, 0x6f, 0x62, - 0xaf, 0x09, 0x73, 0xe5, 0x16, 0xa8, 0x3b, 0x16, 0xb5, 0x50, 0x01, 0x92, 0xf4, 0x28, 0x2c, 0x2a, - 0x5a, 0x72, 0x73, 0xc9, 0x60, 0xc3, 0x73, 0xaf, 0xdb, 0x5f, 0x09, 0x50, 0x1f, 0x11, 0x8a, 0xd1, - 0x1d, 0x50, 0xd9, 0xd1, 0x71, 0x45, 0x2e, 0x9f, 0xa7, 0xf1, 0xb6, 0xd3, 0xf7, 0xb0, 0xbd, 0x1f, - 0xf6, 0x3b, 0xc7, 0x3e, 0x36, 0x38, 0x38, 0x26, 0xb1, 0xc4, 0x8c, 0xc4, 0xd6, 0x20, 0x15, 0x90, - 0x91, 0x67, 0x73, 0xe5, 0xa5, 0x0c, 0x31, 0x41, 0xbb, 0x90, 0x89, 0x94, 0xa3, 0xfe, 0x9b, 0x72, - 0x56, 0x98, 0x72, 0x98, 0xae, 0xa5, 0xc1, 0x48, 0x77, 0xa5, 0x80, 0xea, 0x90, 0x8d, 0x1e, 0x34, - 0xa9, 0xc0, 0xd7, 0x13, 0xf1, 0x34, 0x0c, 0xbd, 0x07, 0xab, 0x91, 0x1e, 0x22, 0x42, 0x85, 0x0a, - 0x0b, 0x91, 0x43, 0x32, 0x3a, 0x23, 0x35, 0x53, 0x3c, 0x4a, 0x69, 0x5e, 0xd7, 0x54, 0x6a, 0x0d, - 0xfe, 0x3a, 0x5d, 0x87, 0x6c, 0xe8, 0xf4, 0x3d, 0x8b, 0x8e, 0x02, 0x2c, 0xd5, 0x38, 0x35, 0x54, - 0xbe, 0x4b, 0xc0, 0xa2, 0x50, 0x77, 0x8c, 0x37, 0xe5, 0x7c, 0xde, 0x12, 0x17, 0xf1, 0x96, 0x7c, - 0x73, 0xde, 0x6a, 0x00, 0xd1, 0x66, 0xc2, 0xa2, 0xaa, 0x25, 0x37, 0x73, 0x5b, 0xd7, 0xe6, 0x13, - 0x89, 0x2d, 0xb6, 0x9d, 0xbe, 0xbc, 0xbc, 0xb1, 0xa0, 0x48, 0x41, 0xa9, 0xd8, 0x3b, 0xf9, 0x29, - 0x64, 0xbb, 0x0e, 0x35, 0xad, 0x20, 0xb0, 0x8e, 0x39, 0x85, 0xb9, 0xad, 0x72, 0x3c, 0x2b, 0x6b, - 0x30, 0x3a, 0x6b, 0x30, 0x7a, 0xdd, 0xa1, 0x35, 0x86, 0x32, 0x32, 0x5d, 0x39, 0xaa, 0xfc, 0xa6, - 0x40, 0x36, 0x5a, 0x10, 0xd5, 0x20, 0x3f, 0x29, 0xd4, 0x7c, 0xec, 0x5a, 0x7d, 0x29, 0xc6, 0xf5, - 0x0b, 0xab, 0xbd, 0xeb, 0x5a, 0x7d, 0x23, 0x27, 0x0b, 0x64, 0x93, 0xf3, 0x0f, 0x36, 0x71, 0xc1, - 0xc1, 0xce, 0x28, 0x29, 0xf9, 0x66, 0x4a, 0x9a, 0x39, 0x73, 0xf5, 0xec, 0x99, 0xff, 0x94, 0x80, - 0x4c, 0x8b, 0x5f, 0x50, 0xcb, 0xfd, 0x2f, 0xae, 0xd8, 0x35, 0xc8, 0xfa, 0xc4, 0x35, 0x85, 0x47, - 0xe5, 0x9e, 0x8c, 0x4f, 0x5c, 0x63, 0x4e, 0x47, 0xa9, 0xb7, 0x74, 0xff, 0x16, 0xdf, 0x02, 0x6b, - 0xe9, 0xb3, 0xac, 0x05, 0xb0, 0x24, 0xa8, 0x90, 0x0d, 0xf3, 0x36, 0xe3, 0x80, 0x77, 0x60, 0x65, - 0xbe, 0xc1, 0x8b, 0x6d, 0x0b, 0xa4, 0x21, 0x71, 0x2c, 0x42, 0xf4, 0x17, 0xd9, 0xb3, 0x8b, 0x17, - 0xe9, 0xdc, 0x90, 0xb8, 0xca, 0xcf, 0x0a, 0x64, 0x79, 0xa9, 0xfb, 0x98, 0x5a, 0x33, 0x54, 0x29, - 0x6f, 0x4e, 0xd5, 0x3a, 0x80, 0x48, 0x13, 0x3a, 0x4f, 0xb1, 0x3c, 0xc0, 0x2c, 0xb7, 0xb4, 0x9d, - 0xa7, 0x18, 0x7d, 0x18, 0xd5, 0x95, 0xfc, 0xe7, 0xba, 0xe4, 0x55, 0x9c, 0x54, 0x77, 0x05, 0xd2, - 0xde, 0x68, 0x68, 0xb2, 0xe7, 0x5d, 0x15, 0xa2, 0xf0, 0x46, 0xc3, 0xce, 0x51, 0x58, 0xf9, 0x1a, - 0xd2, 0x9d, 0x23, 0xfe, 0xa9, 0xc3, 0x94, 0x10, 0x10, 0x22, 0xfb, 0xab, 0xf8, 0xae, 0xc9, 0x30, - 0x03, 0x6f, 0x27, 0x08, 0x54, 0xd6, 0x48, 0x27, 0x9d, 0x80, 0x8d, 0x91, 0xfe, 0x9a, 0x1f, 0x51, - 0xf2, 0xf3, 0xe9, 0xe6, 0x2f, 0x0a, 0xe4, 0x62, 0xd7, 0x10, 0xbd, 0x0f, 0x97, 0xea, 0x7b, 0x07, - 0xdb, 0x0f, 0xcc, 0xc6, 0x8e, 0x79, 0x77, 0xaf, 0x76, 0xcf, 0x7c, 0xd8, 0x7c, 0xd0, 0x3c, 0xf8, - 0xa2, 0x59, 0x58, 0x28, 0x5d, 0x3e, 0x19, 0x6b, 0x28, 0x86, 0x7d, 0xe8, 0x3d, 0xf1, 0xc8, 0x37, - 0x1e, 0xaa, 0xc2, 0xda, 0x6c, 0x48, 0xad, 0xde, 0xde, 0x6d, 0x76, 0x0a, 0x4a, 0xe9, 0xd2, 0xc9, - 0x58, 0x5b, 0x8d, 0x45, 0xd4, 0xba, 0x21, 0xf6, 0xe8, 0x7c, 0xc0, 0xf6, 0xc1, 0xfe, 0x7e, 0xa3, - 0x53, 0x48, 0xcc, 0x05, 0xc8, 0x87, 0xf6, 0x06, 0xac, 0xce, 0x06, 0x34, 0x1b, 0x7b, 0x85, 0x64, - 0x09, 0x9d, 0x8c, 0xb5, 0xe5, 0x18, 0xba, 0xe9, 0xb8, 0xa5, 0xcc, 0xb7, 0xdf, 0x97, 0x17, 0x7e, - 0xfc, 0xa1, 0xac, 0xb0, 0xca, 0xf2, 0x33, 0x57, 0x11, 0xdd, 0x82, 0x2b, 0xed, 0xc6, 0xbd, 0xe6, - 0xee, 0x8e, 0xb9, 0xdf, 0xbe, 0x67, 0x76, 0xbe, 0x6c, 0xed, 0xc6, 0xaa, 0x5b, 0x39, 0x19, 0x6b, - 0x39, 0x59, 0xd2, 0x45, 0xe8, 0x96, 0xb1, 0xfb, 0xe8, 0xa0, 0xb3, 0x5b, 0x50, 0x04, 0xba, 0x15, - 0xe0, 0x43, 0x42, 0x31, 0x47, 0xdf, 0x86, 0xab, 0xe7, 0xa0, 0xa3, 0xc2, 0x56, 0x4f, 0xc6, 0x5a, - 0xbe, 0x15, 0x60, 0x21, 0x53, 0x1e, 0xa1, 0x43, 0x71, 0x3e, 0xe2, 0xa0, 0x75, 0xd0, 0xae, 0xed, - 0x15, 0xb4, 0x52, 0xe1, 0x64, 0xac, 0x2d, 0x4d, 0xde, 0x1c, 0x86, 0x9f, 0x56, 0x56, 0xff, 0xfc, - 0xf9, 0x69, 0x59, 0x79, 0x71, 0x5a, 0x56, 0xfe, 0x38, 0x2d, 0x2b, 0xcf, 0x5e, 0x95, 0x17, 0x5e, - 0xbc, 0x2a, 0x2f, 0xfc, 0xfa, 0xaa, 0xbc, 0xf0, 0xd5, 0x47, 0x7d, 0x87, 0x0e, 0x46, 0x5d, 0xbd, - 0x47, 0x86, 0xd5, 0xf8, 0x9f, 0x90, 0xe9, 0x50, 0xfc, 0xcd, 0x38, 0xfb, 0x07, 0xa5, 0xbb, 0xc8, - 0xed, 0x77, 0xfe, 0x0e, 0x00, 0x00, 0xff, 0xff, 0xbd, 0x5b, 0x78, 0xe7, 0xbb, 0x0c, 0x00, 0x00, + // 1364 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xcd, 0x6e, 0xdb, 0x46, + 0x10, 0x36, 0x25, 0xca, 0x92, 0x46, 0x92, 0x2d, 0x13, 0x4e, 0xa2, 0x28, 0xb1, 0x4c, 0xa8, 0x68, + 0xeb, 0xa4, 0x01, 0x95, 0x3a, 0x45, 0x7f, 0x50, 0xf4, 0x20, 0xc9, 0x4e, 0x22, 0xc4, 0x96, 0x55, + 0x4a, 0x49, 0xd1, 0x5e, 0x08, 0x4a, 0xdc, 0x48, 0x6c, 0x28, 0x92, 0xe0, 0xae, 0x5c, 0x3b, 0x4f, + 0x50, 0xf8, 0x94, 0x5e, 0x7a, 0xf3, 0xa9, 0x3d, 0xf4, 0xde, 0x37, 0xe8, 0x29, 0xc7, 0xdc, 0xda, + 0x4b, 0xd3, 0xc2, 0x01, 0x8a, 0x3e, 0x46, 0xb1, 0x3f, 0xa2, 0x48, 0xcb, 0x6e, 0x03, 0x23, 0xe8, + 0x45, 0xe0, 0xce, 0x7c, 0x33, 0x3b, 0xf3, 0xed, 0xc7, 0x1d, 0x0a, 0xae, 0x13, 0xe4, 0x5a, 0x28, + 0x18, 0xdb, 0x2e, 0xa9, 0x91, 0x43, 0x1f, 0x61, 0xfe, 0xab, 0xf9, 0x81, 0x47, 0x3c, 0xa5, 0x38, + 0xf3, 0x6a, 0xcc, 0x5e, 0x5e, 0x1d, 0x7a, 0x43, 0x8f, 0x39, 0x6b, 0xf4, 0x89, 0xe3, 0xca, 0xeb, + 0x43, 0xcf, 0x1b, 0x3a, 0xa8, 0xc6, 0x56, 0xfd, 0xc9, 0xe3, 0x1a, 0xb1, 0xc7, 0x08, 0x13, 0x73, + 0xec, 0x0b, 0x80, 0x1a, 0xd9, 0xc6, 0xb1, 0xfb, 0xb8, 0xd6, 0xb7, 0x49, 0x6c, 0xab, 0xf2, 0x5a, + 0x04, 0x31, 0x08, 0x0e, 0x7d, 0xe2, 0xd1, 0x6c, 0xde, 0x63, 0xe1, 0xae, 0x44, 0xdc, 0xfb, 0x28, + 0xc0, 0xb6, 0xe7, 0xc6, 0xc2, 0xd5, 0xb9, 0x3e, 0xf6, 0x4d, 0xc7, 0xb6, 0x4c, 0xe2, 0x05, 0x1c, + 0x51, 0xfd, 0x04, 0x0a, 0x1d, 0x33, 0x20, 0x5d, 0x44, 0xee, 0x23, 0xd3, 0x42, 0x81, 0xb2, 0x0a, + 0x29, 0xe2, 0x11, 0xd3, 0x29, 0x49, 0xaa, 0xb4, 0x51, 0xd0, 0xf9, 0x42, 0x51, 0x40, 0x1e, 0x99, + 0x78, 0x54, 0x4a, 0xa8, 0xd2, 0x46, 0x5e, 0x67, 0xcf, 0xd5, 0x11, 0xc8, 0x34, 0x94, 0x46, 0xd8, + 0xae, 0x85, 0x0e, 0xa6, 0x11, 0x6c, 0x41, 0xad, 0xfd, 0x43, 0x82, 0xb0, 0x08, 0xe1, 0x0b, 0xe5, + 0x03, 0x48, 0xb1, 0xfa, 0x4b, 0x49, 0x55, 0xda, 0xc8, 0x6d, 0x96, 0xb4, 0x08, 0x95, 0xbc, 0x3f, + 0xad, 0x43, 0xfd, 0x0d, 0xf9, 0xf9, 0xcb, 0xf5, 0x05, 0x9d, 0x83, 0xab, 0x0e, 0xa4, 0x1b, 0x8e, + 0x37, 0x78, 0xd2, 0xda, 0x0a, 0x0b, 0x91, 0x66, 0x85, 0x28, 0xbb, 0xb0, 0xec, 0x9b, 0x01, 0x31, + 0x30, 0x22, 0xc6, 0x88, 0x75, 0xc1, 0x36, 0xcd, 0x6d, 0xae, 0x6b, 0xa7, 0x4f, 0x4a, 0x8b, 0x35, + 0x2b, 0x76, 0x29, 0xf8, 0x51, 0x63, 0xf5, 0x2f, 0x19, 0x16, 0x05, 0x19, 0x9f, 0x41, 0x5a, 0xd0, + 0xca, 0x36, 0xcc, 0x6d, 0xae, 0x45, 0x33, 0x0a, 0x97, 0xd6, 0xf4, 0x5c, 0x8c, 0x5c, 0x3c, 0xc1, + 0x22, 0xdf, 0x34, 0x46, 0x79, 0x07, 0x32, 0x83, 0x91, 0x69, 0xbb, 0x86, 0x6d, 0xb1, 0x8a, 0xb2, + 0x8d, 0xdc, 0xc9, 0xcb, 0xf5, 0x74, 0x93, 0xda, 0x5a, 0x5b, 0x7a, 0x9a, 0x39, 0x5b, 0x96, 0x72, + 0x19, 0x16, 0x47, 0xc8, 0x1e, 0x8e, 0x08, 0xa3, 0x25, 0xa9, 0x8b, 0x95, 0xf2, 0x31, 0xc8, 0x54, + 0x32, 0x25, 0x99, 0xed, 0x5d, 0xd6, 0xb8, 0x9e, 0xb4, 0xa9, 0x9e, 0xb4, 0xde, 0x54, 0x4f, 0x8d, + 0x0c, 0xdd, 0xf8, 0xd9, 0x1f, 0xeb, 0x92, 0xce, 0x22, 0x94, 0x26, 0x14, 0x1c, 0x13, 0x13, 0xa3, + 0x4f, 0x69, 0xa3, 0xdb, 0xa7, 0x58, 0x8a, 0xab, 0xf3, 0x84, 0x08, 0x62, 0x45, 0xe9, 0x39, 0x1a, + 0xc5, 0x4d, 0x96, 0xb2, 0x01, 0x45, 0x96, 0x64, 0xe0, 0x8d, 0xc7, 0x36, 0x31, 0x18, 0xef, 0x8b, + 0x8c, 0xf7, 0x25, 0x6a, 0x6f, 0x32, 0xf3, 0x7d, 0x7a, 0x02, 0xd7, 0x20, 0x6b, 0x99, 0xc4, 0xe4, + 0x90, 0x34, 0x83, 0x64, 0xa8, 0x81, 0x39, 0xdf, 0x85, 0xe5, 0x50, 0x75, 0x98, 0x43, 0x32, 0x3c, + 0xcb, 0xcc, 0xcc, 0x80, 0xb7, 0x61, 0xd5, 0x45, 0x07, 0xc4, 0x38, 0x8d, 0xce, 0x32, 0xb4, 0x42, + 0x7d, 0x8f, 0xe2, 0x11, 0x6f, 0xc3, 0xd2, 0x60, 0x4a, 0x3e, 0xc7, 0x02, 0xc3, 0x16, 0x42, 0x2b, + 0x83, 0x5d, 0x85, 0x8c, 0xe9, 0xfb, 0x1c, 0x90, 0x63, 0x80, 0xb4, 0xe9, 0xfb, 0xcc, 0x75, 0x13, + 0x56, 0x58, 0x8f, 0x01, 0xc2, 0x13, 0x87, 0x88, 0x24, 0x79, 0x86, 0x59, 0xa6, 0x0e, 0x9d, 0xdb, + 0x19, 0xf6, 0x2d, 0x28, 0xa0, 0x7d, 0xdb, 0x42, 0xee, 0x00, 0x71, 0x5c, 0x81, 0xe1, 0xf2, 0x53, + 0x23, 0x03, 0xdd, 0x80, 0xa2, 0x1f, 0x78, 0xbe, 0x87, 0x51, 0x60, 0x98, 0x96, 0x15, 0x20, 0x8c, + 0x4b, 0x4b, 0x3c, 0xdf, 0xd4, 0x5e, 0xe7, 0xe6, 0xea, 0x2d, 0x90, 0xb7, 0x4c, 0x62, 0x2a, 0x45, + 0x48, 0x92, 0x03, 0x5c, 0x92, 0xd4, 0xe4, 0x46, 0x5e, 0xa7, 0x8f, 0x67, 0xbe, 0x6e, 0x7f, 0x27, + 0x40, 0x7e, 0xe4, 0x11, 0xa4, 0xdc, 0x01, 0x99, 0x1e, 0x1d, 0x53, 0xe4, 0xd2, 0x59, 0x1a, 0xef, + 0xda, 0x43, 0x17, 0x59, 0xbb, 0x78, 0xd8, 0x3b, 0xf4, 0x91, 0xce, 0xc0, 0x11, 0x89, 0x25, 0x62, + 0x12, 0x5b, 0x85, 0x54, 0xe0, 0x4d, 0x5c, 0x8b, 0x29, 0x2f, 0xa5, 0xf3, 0x85, 0xb2, 0x0d, 0x99, + 0x50, 0x39, 0xf2, 0x7f, 0x29, 0x67, 0x99, 0x2a, 0x87, 0xea, 0x5a, 0x18, 0xf4, 0x74, 0x5f, 0x08, + 0xa8, 0x01, 0xd9, 0xf0, 0xca, 0x13, 0x0a, 0x7c, 0x3d, 0x11, 0xcf, 0xc2, 0x94, 0xf7, 0x60, 0x25, + 0xd4, 0x43, 0x48, 0x28, 0x57, 0x61, 0x31, 0x74, 0x08, 0x46, 0x63, 0x52, 0x33, 0xf8, 0xa5, 0x94, + 0x66, 0x7d, 0xcd, 0xa4, 0xd6, 0x62, 0xb7, 0xd3, 0x75, 0xc8, 0x62, 0x7b, 0xe8, 0x9a, 0x64, 0x12, + 0x20, 0xa1, 0xc6, 0x99, 0xa1, 0xfa, 0x5d, 0x02, 0x16, 0xb9, 0xba, 0x23, 0xbc, 0x49, 0x67, 0xf3, + 0x96, 0x38, 0x8f, 0xb7, 0xe4, 0xc5, 0x79, 0xab, 0x03, 0x84, 0xc5, 0xe0, 0x92, 0xac, 0x26, 0x37, + 0x72, 0x9b, 0xd7, 0xe6, 0x13, 0xf1, 0x12, 0xbb, 0xf6, 0x50, 0xbc, 0xbc, 0x91, 0xa0, 0x50, 0x41, + 0xa9, 0xc8, 0x3d, 0xf9, 0x29, 0x64, 0xfb, 0x36, 0x31, 0xcc, 0x20, 0x30, 0x0f, 0x19, 0x85, 0xb9, + 0xcd, 0x4a, 0x34, 0x2b, 0x1d, 0x41, 0x1a, 0x1d, 0x41, 0x5a, 0xc3, 0x26, 0x75, 0x8a, 0xd2, 0x33, + 0x7d, 0xf1, 0x54, 0xfd, 0x5d, 0x82, 0x6c, 0xb8, 0xa1, 0x52, 0x87, 0xc2, 0xb4, 0x51, 0xe3, 0xb1, + 0x63, 0x0e, 0x85, 0x18, 0xd7, 0xce, 0xed, 0xf6, 0xae, 0x63, 0x0e, 0xf5, 0x9c, 0x68, 0x90, 0x2e, + 0xce, 0x3e, 0xd8, 0xc4, 0x39, 0x07, 0x1b, 0x53, 0x52, 0xf2, 0x62, 0x4a, 0x8a, 0x9d, 0xb9, 0x7c, + 0xfa, 0xcc, 0x7f, 0x4e, 0x40, 0xa6, 0xc3, 0x5e, 0x50, 0xd3, 0xf9, 0x3f, 0x5e, 0xb1, 0x6b, 0x90, + 0xf5, 0x3d, 0xc7, 0xe0, 0x1e, 0x99, 0x79, 0x32, 0xbe, 0xe7, 0xe8, 0x73, 0x3a, 0x4a, 0xbd, 0xa1, + 0xf7, 0x6f, 0xf1, 0x0d, 0xb0, 0x96, 0x3e, 0xcd, 0x5a, 0x00, 0x79, 0x4e, 0x85, 0x18, 0x98, 0xb7, + 0x29, 0x07, 0x6c, 0x02, 0x4b, 0xf3, 0x03, 0x9e, 0x97, 0xcd, 0x91, 0xba, 0xc0, 0xd1, 0x08, 0x3e, + 0x5f, 0xc4, 0xcc, 0x2e, 0x9d, 0xa7, 0x73, 0x5d, 0xe0, 0xaa, 0xdf, 0x4b, 0x00, 0x3b, 0x94, 0x59, + 0xd6, 0x2f, 0x1d, 0x75, 0x98, 0x95, 0x60, 0xc4, 0x76, 0xae, 0x9c, 0x77, 0x68, 0x62, 0xff, 0x3c, + 0x8e, 0xd6, 0xdd, 0x84, 0xc2, 0x4c, 0x8c, 0x18, 0x4d, 0x8b, 0x39, 0x23, 0x49, 0x38, 0x81, 0xba, + 0x88, 0xe8, 0xf9, 0xfd, 0xc8, 0xaa, 0xfa, 0x8b, 0x04, 0x59, 0x56, 0xd3, 0x2e, 0x22, 0x66, 0xec, + 0x0c, 0xa5, 0x8b, 0x9f, 0xe1, 0x1a, 0x00, 0x4f, 0x83, 0xed, 0xa7, 0x48, 0x28, 0x2b, 0xcb, 0x2c, + 0x5d, 0xfb, 0x29, 0x52, 0x3e, 0x0c, 0x09, 0x4f, 0xfe, 0x3b, 0xe1, 0xe2, 0x8e, 0x98, 0xd2, 0x7e, + 0x05, 0xd2, 0xee, 0x64, 0x6c, 0xd0, 0xb9, 0x23, 0x73, 0xb5, 0xba, 0x93, 0x71, 0xef, 0x00, 0x57, + 0xbf, 0x86, 0x74, 0xef, 0x80, 0x7d, 0x83, 0x51, 0x89, 0x06, 0x9e, 0x27, 0x06, 0x3f, 0xff, 0xe0, + 0xca, 0x50, 0x03, 0x9b, 0x73, 0x0a, 0xc8, 0x74, 0xc2, 0x4f, 0x47, 0x14, 0x7d, 0x56, 0xb4, 0xd7, + 0xfc, 0xba, 0x13, 0xdf, 0x75, 0x37, 0x7f, 0x95, 0x20, 0x17, 0xb9, 0x1f, 0x94, 0xf7, 0xe1, 0x52, + 0x63, 0x67, 0xaf, 0xf9, 0xc0, 0x68, 0x6d, 0x19, 0x77, 0x77, 0xea, 0xf7, 0x8c, 0x87, 0xed, 0x07, + 0xed, 0xbd, 0x2f, 0xda, 0xc5, 0x85, 0xf2, 0xe5, 0xa3, 0x63, 0x55, 0x89, 0x60, 0x1f, 0xba, 0x4f, + 0x5c, 0xef, 0x1b, 0x57, 0xa9, 0xc1, 0x6a, 0x3c, 0xa4, 0xde, 0xe8, 0x6e, 0xb7, 0x7b, 0x45, 0xa9, + 0x7c, 0xe9, 0xe8, 0x58, 0x5d, 0x89, 0x44, 0xd4, 0xfb, 0x18, 0xb9, 0x64, 0x3e, 0xa0, 0xb9, 0xb7, + 0xbb, 0xdb, 0xea, 0x15, 0x13, 0x73, 0x01, 0x62, 0x02, 0xdc, 0x80, 0x95, 0x78, 0x40, 0xbb, 0xb5, + 0x53, 0x4c, 0x96, 0x95, 0xa3, 0x63, 0x75, 0x29, 0x82, 0x6e, 0xdb, 0x4e, 0x39, 0xf3, 0xed, 0x0f, + 0x95, 0x85, 0x9f, 0x7e, 0xac, 0x48, 0xb4, 0xb3, 0x42, 0xec, 0x8e, 0x50, 0x6e, 0xc1, 0x95, 0x6e, + 0xeb, 0x5e, 0x7b, 0x7b, 0xcb, 0xd8, 0xed, 0xde, 0x33, 0x7a, 0x5f, 0x76, 0xb6, 0x23, 0xdd, 0x2d, + 0x1f, 0x1d, 0xab, 0x39, 0xd1, 0xd2, 0x79, 0xe8, 0x8e, 0xbe, 0xfd, 0x68, 0xaf, 0xb7, 0x5d, 0x94, + 0x38, 0xba, 0x13, 0xa0, 0x7d, 0x8f, 0x20, 0x86, 0xbe, 0x0d, 0x57, 0xcf, 0x40, 0x87, 0x8d, 0xad, + 0x1c, 0x1d, 0xab, 0x85, 0x4e, 0x80, 0xf8, 0xfb, 0xc3, 0x22, 0x34, 0x28, 0xcd, 0x47, 0xec, 0x75, + 0xf6, 0xba, 0xf5, 0x9d, 0xa2, 0x5a, 0x2e, 0x1e, 0x1d, 0xab, 0xf9, 0xe9, 0x65, 0x48, 0xf1, 0xb3, + 0xce, 0x1a, 0x9f, 0x3f, 0x3f, 0xa9, 0x48, 0x2f, 0x4e, 0x2a, 0xd2, 0x9f, 0x27, 0x15, 0xe9, 0xd9, + 0xab, 0xca, 0xc2, 0x8b, 0x57, 0x95, 0x85, 0xdf, 0x5e, 0x55, 0x16, 0xbe, 0xfa, 0x68, 0x68, 0x93, + 0xd1, 0xa4, 0xaf, 0x0d, 0xbc, 0x71, 0x2d, 0xfa, 0xbf, 0x63, 0xf6, 0xc8, 0xff, 0x21, 0x9d, 0xfe, + 0x4f, 0xd2, 0x5f, 0x64, 0xf6, 0x3b, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0xb5, 0xf9, 0x01, 0xed, + 0x76, 0x0d, 0x00, 0x00, } func (m *PartSetHeader) Marshal() (dAtA []byte, err error) { @@ -1708,6 +1765,53 @@ func (m *SignedHeader) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *LightBlock) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *LightBlock) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LightBlock) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ValidatorSet != nil { + { + size, err := m.ValidatorSet.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.SignedHeader != nil { + { + size, err := m.SignedHeader.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *BlockMeta) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -2079,6 +2183,23 @@ func (m *SignedHeader) Size() (n int) { return n } +func (m *LightBlock) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SignedHeader != nil { + l = m.SignedHeader.Size() + n += 1 + l + sovTypes(uint64(l)) + } + if m.ValidatorSet != nil { + l = m.ValidatorSet.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} + func (m *BlockMeta) Size() (n int) { if m == nil { return 0 @@ -4136,6 +4257,131 @@ func (m *SignedHeader) Unmarshal(dAtA []byte) error { } return nil } +func (m *LightBlock) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: LightBlock: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LightBlock: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignedHeader", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SignedHeader == nil { + m.SignedHeader = &SignedHeader{} + } + if err := m.SignedHeader.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorSet", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ValidatorSet == nil { + m.ValidatorSet = &ValidatorSet{} + } + if err := m.ValidatorSet.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *BlockMeta) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/proto/tendermint/types/types.proto b/proto/tendermint/types/types.proto index 7ba7de8bd..2762f4a78 100644 --- a/proto/tendermint/types/types.proto +++ b/proto/tendermint/types/types.proto @@ -8,6 +8,7 @@ import "google/protobuf/timestamp.proto"; import "tendermint/libs/bits/types.proto"; import "tendermint/crypto/proof.proto"; import "tendermint/version/types.proto"; +import "tendermint/types/validator.proto"; // BlockIdFlag indicates which BlcokID the signature is for enum BlockIDFlag { @@ -141,6 +142,11 @@ message SignedHeader { Commit commit = 2; } +message LightBlock { + SignedHeader signed_header = 1; + tendermint.types.ValidatorSet validator_set = 2; +} + message BlockMeta { BlockID block_id = 1 [(gogoproto.customname) = "BlockID", (gogoproto.nullable) = false]; int64 block_size = 2; diff --git a/statesync/stateprovider.go b/statesync/stateprovider.go index 1844c4b60..f805e0f2c 100644 --- a/statesync/stateprovider.go +++ b/statesync/stateprovider.go @@ -88,7 +88,7 @@ func (s *lightClientStateProvider) AppHash(height uint64) ([]byte, error) { defer s.Unlock() // We have to fetch the next height, which contains the app hash for the previous height. - header, err := s.lc.VerifyHeaderAtHeight(int64(height+1), time.Now()) + header, err := s.lc.VerifyLightBlockAtHeight(int64(height+1), time.Now()) if err != nil { return nil, err } @@ -99,7 +99,7 @@ func (s *lightClientStateProvider) AppHash(height uint64) ([]byte, error) { func (s *lightClientStateProvider) Commit(height uint64) (*types.Commit, error) { s.Lock() defer s.Unlock() - header, err := s.lc.VerifyHeaderAtHeight(int64(height), time.Now()) + header, err := s.lc.VerifyLightBlockAtHeight(int64(height), time.Now()) if err != nil { return nil, err } @@ -121,36 +121,27 @@ func (s *lightClientStateProvider) State(height uint64) (sm.State, error) { } // We need to verify the previous block to get the validator set. - _, err := s.lc.VerifyHeaderAtHeight(int64(height-1), time.Now()) + prevLightBlock, err := s.lc.VerifyLightBlockAtHeight(int64(height-1), time.Now()) if err != nil { return sm.State{}, err } - header, err := s.lc.VerifyHeaderAtHeight(int64(height), time.Now()) + lightBlock, err := s.lc.VerifyLightBlockAtHeight(int64(height), time.Now()) if err != nil { return sm.State{}, err } - nextHeader, err := s.lc.VerifyHeaderAtHeight(int64(height+1), time.Now()) + nextLightBlock, err := s.lc.VerifyLightBlockAtHeight(int64(height+1), time.Now()) if err != nil { return sm.State{}, err } - state.LastBlockHeight = header.Height - state.LastBlockTime = header.Time - state.LastBlockID = header.Commit.BlockID - state.AppHash = nextHeader.AppHash - state.LastResultsHash = nextHeader.LastResultsHash - state.LastValidators, _, err = s.lc.TrustedValidatorSet(int64(height - 1)) - if err != nil { - return sm.State{}, err - } - state.Validators, _, err = s.lc.TrustedValidatorSet(int64(height)) - if err != nil { - return sm.State{}, err - } - state.NextValidators, _, err = s.lc.TrustedValidatorSet(int64(height + 1)) - if err != nil { - return sm.State{}, err - } + state.LastBlockHeight = lightBlock.Height + state.LastBlockTime = lightBlock.Time + state.LastBlockID = lightBlock.Commit.BlockID + state.AppHash = nextLightBlock.AppHash + state.LastResultsHash = nextLightBlock.LastResultsHash + state.LastValidators = prevLightBlock.ValidatorSet + state.Validators = lightBlock.ValidatorSet + state.NextValidators = nextLightBlock.ValidatorSet state.LastHeightValidatorsChanged = int64(height) // We'll also need to fetch consensus params via RPC, using light client verification. @@ -163,10 +154,10 @@ func (s *lightClientStateProvider) State(height uint64) (sm.State, error) { return sm.State{}, fmt.Errorf("unable to create RPC client: %w", err) } rpcclient := lightrpc.NewClient(primaryRPC, s.lc) - result, err := rpcclient.ConsensusParams(&nextHeader.Height) + result, err := rpcclient.ConsensusParams(&nextLightBlock.Height) if err != nil { return sm.State{}, fmt.Errorf("unable to fetch consensus parameters for height %v: %w", - nextHeader.Height, err) + nextLightBlock.Height, err) } state.ConsensusParams = result.ConsensusParams diff --git a/types/block.go b/types/block.go index 2959ee123..527e3ef28 100644 --- a/types/block.go +++ b/types/block.go @@ -987,116 +987,6 @@ func CommitFromProto(cp *tmproto.Commit) (*Commit, error) { //----------------------------------------------------------------------------- -// SignedHeader is a header along with the commits that prove it. -// It is the basis of the light client. -type SignedHeader struct { - *Header `json:"header"` - - Commit *Commit `json:"commit"` -} - -// ValidateBasic does basic consistency checks and makes sure the header -// and commit are consistent. -// -// NOTE: This does not actually check the cryptographic signatures. Make sure -// to use a Verifier to validate the signatures actually provide a -// significantly strong proof for this header's validity. -func (sh SignedHeader) ValidateBasic(chainID string) error { - if sh.Header == nil { - return errors.New("missing header") - } - if sh.Commit == nil { - return errors.New("missing commit") - } - - if err := sh.Header.ValidateBasic(); err != nil { - return fmt.Errorf("invalid header: %w", err) - } - if err := sh.Commit.ValidateBasic(); err != nil { - return fmt.Errorf("invalid commit: %w", err) - } - - if sh.ChainID != chainID { - return fmt.Errorf("header belongs to another chain %q, not %q", sh.ChainID, chainID) - } - - // Make sure the header is consistent with the commit. - if sh.Commit.Height != sh.Height { - return fmt.Errorf("header and commit height mismatch: %d vs %d", sh.Height, sh.Commit.Height) - } - if hhash, chash := sh.Hash(), sh.Commit.BlockID.Hash; !bytes.Equal(hhash, chash) { - return fmt.Errorf("commit signs block %X, header is block %X", chash, hhash) - } - - return nil -} - -// String returns a string representation of SignedHeader. -func (sh SignedHeader) String() string { - return sh.StringIndented("") -} - -// StringIndented returns an indented string representation of SignedHeader. -// -// Header -// Commit -func (sh SignedHeader) StringIndented(indent string) string { - return fmt.Sprintf(`SignedHeader{ -%s %v -%s %v -%s}`, - indent, sh.Header.StringIndented(indent+" "), - indent, sh.Commit.StringIndented(indent+" "), - indent) -} - -// ToProto converts SignedHeader to protobuf -func (sh *SignedHeader) ToProto() *tmproto.SignedHeader { - if sh == nil { - return nil - } - - psh := new(tmproto.SignedHeader) - if sh.Header != nil { - psh.Header = sh.Header.ToProto() - } - if sh.Commit != nil { - psh.Commit = sh.Commit.ToProto() - } - - return psh -} - -// FromProto sets a protobuf SignedHeader to the given pointer. -// It returns an error if the hader or the commit is invalid. -func SignedHeaderFromProto(shp *tmproto.SignedHeader) (*SignedHeader, error) { - if shp == nil { - return nil, errors.New("nil SignedHeader") - } - - sh := new(SignedHeader) - - if shp.Header != nil { - h, err := HeaderFromProto(shp.Header) - if err != nil { - return nil, err - } - sh.Header = &h - } - - if shp.Commit != nil { - c, err := CommitFromProto(shp.Commit) - if err != nil { - return nil, err - } - sh.Commit = c - } - - return sh, nil -} - -//----------------------------------------------------------------------------- - // Data contains the set of transactions included in the block type Data struct { diff --git a/types/block_test.go b/types/block_test.go index f6c5a03b8..07150b08c 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -543,59 +543,6 @@ func TestCommitToVoteSetWithVotesForNilBlock(t *testing.T) { } } -func TestSignedHeaderValidateBasic(t *testing.T) { - commit := randCommit(time.Now()) - chainID := "𠜎" - timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) - h := Header{ - Version: version.Consensus{Block: math.MaxInt64, App: math.MaxInt64}, - ChainID: chainID, - Height: commit.Height, - Time: timestamp, - LastBlockID: commit.BlockID, - LastCommitHash: commit.Hash(), - DataHash: commit.Hash(), - ValidatorsHash: commit.Hash(), - NextValidatorsHash: commit.Hash(), - ConsensusHash: commit.Hash(), - AppHash: commit.Hash(), - LastResultsHash: commit.Hash(), - EvidenceHash: commit.Hash(), - ProposerAddress: crypto.AddressHash([]byte("proposer_address")), - } - - validSignedHeader := SignedHeader{Header: &h, Commit: commit} - validSignedHeader.Commit.BlockID.Hash = validSignedHeader.Hash() - invalidSignedHeader := SignedHeader{} - - testCases := []struct { - testName string - shHeader *Header - shCommit *Commit - expectErr bool - }{ - {"Valid Signed Header", validSignedHeader.Header, validSignedHeader.Commit, false}, - {"Invalid Signed Header", invalidSignedHeader.Header, validSignedHeader.Commit, true}, - {"Invalid Signed Header", validSignedHeader.Header, invalidSignedHeader.Commit, true}, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.testName, func(t *testing.T) { - sh := SignedHeader{ - Header: tc.shHeader, - Commit: tc.shCommit, - } - assert.Equal( - t, - tc.expectErr, - sh.ValidateBasic(validSignedHeader.Header.ChainID) != nil, - "Validate Basic had an unexpected result", - ) - }) - } -} - func TestBlockIDValidateBasic(t *testing.T) { validBlockID := BlockID{ Hash: bytes.HexBytes{}, diff --git a/types/light.go b/types/light.go new file mode 100644 index 000000000..8f09d8205 --- /dev/null +++ b/types/light.go @@ -0,0 +1,221 @@ +package types + +import ( + "bytes" + "errors" + "fmt" + + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" +) + +// LightBlock is a SignedHeader and a ValidatorSet. +// It is the basis of the light client +type LightBlock struct { + *SignedHeader `json:"signed_header"` + ValidatorSet *ValidatorSet `json:"validator_set"` +} + +// ValidateBasic checks that the data is correct and consistent +// +// This does no verification of the signatures +func (lb LightBlock) ValidateBasic(chainID string) error { + if lb.SignedHeader == nil { + return errors.New("missing signed header") + } + if lb.ValidatorSet == nil { + return errors.New("missing validator set") + } + + if err := lb.SignedHeader.ValidateBasic(chainID); err != nil { + return fmt.Errorf("invalid signed header: %w", err) + } + if err := lb.ValidatorSet.ValidateBasic(); err != nil { + return fmt.Errorf("invalid validator set: %w", err) + } + + // make sure the validator set is consistent with the header + if valSetHash := lb.ValidatorSet.Hash(); !bytes.Equal(lb.SignedHeader.ValidatorsHash, valSetHash) { + return fmt.Errorf("expected validator hash of header to match validator set hash (%X != %X)", + lb.SignedHeader.ValidatorsHash, valSetHash, + ) + } + + return nil +} + +// String returns a string representation of the LightBlock +func (lb LightBlock) String() string { + return lb.StringIndented("") +} + +// StringIndented returns an indented string representation of the LightBlock +// +// SignedHeader +// ValidatorSet +func (lb LightBlock) StringIndented(indent string) string { + return fmt.Sprintf(`LightBlock{ +%s %v +%s %v +%s}`, + indent, lb.SignedHeader.StringIndented(indent+" "), + indent, lb.ValidatorSet.StringIndented(indent+" "), + indent) +} + +// ToProto converts the LightBlock to protobuf +func (lb *LightBlock) ToProto() (*tmproto.LightBlock, error) { + if lb == nil { + return nil, nil + } + + lbp := new(tmproto.LightBlock) + var err error + if lb.SignedHeader != nil { + lbp.SignedHeader = lb.SignedHeader.ToProto() + } + if lb.ValidatorSet != nil { + lbp.ValidatorSet, err = lb.ValidatorSet.ToProto() + if err != nil { + return nil, err + } + } + + return lbp, nil +} + +// LightBlockFromProto converts from protobuf back into the Lightblock. +// An error is returned if either the validator set or signed header are invalid +func LightBlockFromProto(pb *tmproto.LightBlock) (*LightBlock, error) { + if pb == nil { + return nil, errors.New("nil light block") + } + + lb := new(LightBlock) + + if pb.SignedHeader != nil { + sh, err := SignedHeaderFromProto(pb.SignedHeader) + if err != nil { + return nil, err + } + lb.SignedHeader = sh + } + + if pb.ValidatorSet != nil { + vals, err := ValidatorSetFromProto(pb.ValidatorSet) + if err != nil { + return nil, err + } + lb.ValidatorSet = vals + } + + return lb, nil +} + +//----------------------------------------------------------------------------- + +// SignedHeader is a header along with the commits that prove it. +type SignedHeader struct { + *Header `json:"header"` + + Commit *Commit `json:"commit"` +} + +// ValidateBasic does basic consistency checks and makes sure the header +// and commit are consistent. +// +// NOTE: This does not actually check the cryptographic signatures. Make sure +// to use a Verifier to validate the signatures actually provide a +// significantly strong proof for this header's validity. +func (sh SignedHeader) ValidateBasic(chainID string) error { + if sh.Header == nil { + return errors.New("missing header") + } + if sh.Commit == nil { + return errors.New("missing commit") + } + + if err := sh.Header.ValidateBasic(); err != nil { + return fmt.Errorf("invalid header: %w", err) + } + if err := sh.Commit.ValidateBasic(); err != nil { + return fmt.Errorf("invalid commit: %w", err) + } + + if sh.ChainID != chainID { + return fmt.Errorf("header belongs to another chain %q, not %q", sh.ChainID, chainID) + } + + // Make sure the header is consistent with the commit. + if sh.Commit.Height != sh.Height { + return fmt.Errorf("header and commit height mismatch: %d vs %d", sh.Height, sh.Commit.Height) + } + if hhash, chash := sh.Hash(), sh.Commit.BlockID.Hash; !bytes.Equal(hhash, chash) { + return fmt.Errorf("commit signs block %X, header is block %X", chash, hhash) + } + + return nil +} + +// String returns a string representation of SignedHeader. +func (sh SignedHeader) String() string { + return sh.StringIndented("") +} + +// StringIndented returns an indented string representation of SignedHeader. +// +// Header +// Commit +func (sh SignedHeader) StringIndented(indent string) string { + return fmt.Sprintf(`SignedHeader{ +%s %v +%s %v +%s}`, + indent, sh.Header.StringIndented(indent+" "), + indent, sh.Commit.StringIndented(indent+" "), + indent) +} + +// ToProto converts SignedHeader to protobuf +func (sh *SignedHeader) ToProto() *tmproto.SignedHeader { + if sh == nil { + return nil + } + + psh := new(tmproto.SignedHeader) + if sh.Header != nil { + psh.Header = sh.Header.ToProto() + } + if sh.Commit != nil { + psh.Commit = sh.Commit.ToProto() + } + + return psh +} + +// FromProto sets a protobuf SignedHeader to the given pointer. +// It returns an error if the header or the commit is invalid. +func SignedHeaderFromProto(shp *tmproto.SignedHeader) (*SignedHeader, error) { + if shp == nil { + return nil, errors.New("nil SignedHeader") + } + + sh := new(SignedHeader) + + if shp.Header != nil { + h, err := HeaderFromProto(shp.Header) + if err != nil { + return nil, err + } + sh.Header = &h + } + + if shp.Commit != nil { + c, err := CommitFromProto(shp.Commit) + if err != nil { + return nil, err + } + sh.Commit = c + } + + return sh, nil +} diff --git a/types/light_test.go b/types/light_test.go new file mode 100644 index 000000000..267c18961 --- /dev/null +++ b/types/light_test.go @@ -0,0 +1,161 @@ +package types + +import ( + "math" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/proto/tendermint/version" +) + +func TestLightBlockValidateBasic(t *testing.T) { + header := makeRandHeader() + commit := randCommit(time.Now()) + vals, _ := RandValidatorSet(5, 1) + header.Height = commit.Height + header.LastBlockID = commit.BlockID + header.ValidatorsHash = vals.Hash() + vals2, _ := RandValidatorSet(3, 1) + vals3 := vals.Copy() + vals3.Proposer = &Validator{} + commit.BlockID.Hash = header.Hash() + + sh := &SignedHeader{ + Header: &header, + Commit: commit, + } + + testCases := []struct { + name string + sh *SignedHeader + vals *ValidatorSet + expectErr bool + }{ + {"valid light block", sh, vals, false}, + {"hashes don't match", sh, vals2, true}, + {"invalid validator set", sh, vals3, true}, + {"invalid signed header", &SignedHeader{Header: &header, Commit: randCommit(time.Now())}, vals, true}, + } + + for _, tc := range testCases { + lightBlock := LightBlock{ + SignedHeader: tc.sh, + ValidatorSet: tc.vals, + } + err := lightBlock.ValidateBasic(header.ChainID) + if tc.expectErr { + assert.Error(t, err, tc.name) + } else { + assert.NoError(t, err, tc.name) + } + } + +} + +func TestLightBlockProtobuf(t *testing.T) { + header := makeRandHeader() + commit := randCommit(time.Now()) + vals, _ := RandValidatorSet(5, 1) + header.Height = commit.Height + header.LastBlockID = commit.BlockID + header.ValidatorsHash = vals.Hash() + vals3 := vals.Copy() + vals3.Proposer = &Validator{} + commit.BlockID.Hash = header.Hash() + + sh := &SignedHeader{ + Header: &header, + Commit: commit, + } + + testCases := []struct { + name string + sh *SignedHeader + vals *ValidatorSet + toProtoErr bool + toBlockErr bool + }{ + {"valid light block", sh, vals, false, false}, + {"empty signed header", &SignedHeader{}, vals, false, false}, + {"empty validator set", sh, &ValidatorSet{}, false, true}, + {"empty light block", &SignedHeader{}, &ValidatorSet{}, false, true}, + } + + for _, tc := range testCases { + lightBlock := &LightBlock{ + SignedHeader: tc.sh, + ValidatorSet: tc.vals, + } + lbp, err := lightBlock.ToProto() + if tc.toProtoErr { + assert.Error(t, err, tc.name) + } else { + assert.NoError(t, err, tc.name) + } + + lb, err := LightBlockFromProto(lbp) + if tc.toBlockErr { + assert.Error(t, err, tc.name) + } else { + assert.NoError(t, err, tc.name) + assert.Equal(t, lightBlock, lb) + } + } + +} + +func TestSignedHeaderValidateBasic(t *testing.T) { + commit := randCommit(time.Now()) + chainID := "𠜎" + timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) + h := Header{ + Version: version.Consensus{Block: math.MaxInt64, App: math.MaxInt64}, + ChainID: chainID, + Height: commit.Height, + Time: timestamp, + LastBlockID: commit.BlockID, + LastCommitHash: commit.Hash(), + DataHash: commit.Hash(), + ValidatorsHash: commit.Hash(), + NextValidatorsHash: commit.Hash(), + ConsensusHash: commit.Hash(), + AppHash: commit.Hash(), + LastResultsHash: commit.Hash(), + EvidenceHash: commit.Hash(), + ProposerAddress: crypto.AddressHash([]byte("proposer_address")), + } + + validSignedHeader := SignedHeader{Header: &h, Commit: commit} + validSignedHeader.Commit.BlockID.Hash = validSignedHeader.Hash() + invalidSignedHeader := SignedHeader{} + + testCases := []struct { + testName string + shHeader *Header + shCommit *Commit + expectErr bool + }{ + {"Valid Signed Header", validSignedHeader.Header, validSignedHeader.Commit, false}, + {"Invalid Signed Header", invalidSignedHeader.Header, validSignedHeader.Commit, true}, + {"Invalid Signed Header", validSignedHeader.Header, invalidSignedHeader.Commit, true}, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.testName, func(t *testing.T) { + sh := SignedHeader{ + Header: tc.shHeader, + Commit: tc.shCommit, + } + assert.Equal( + t, + tc.expectErr, + sh.ValidateBasic(validSignedHeader.Header.ChainID) != nil, + "Validate Basic had an unexpected result", + ) + }) + } +}