diff --git a/CHANGELOG.md b/CHANGELOG.md index 27d5656b7..99c51a044 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -556,7 +556,7 @@ BREAKING CHANGES: - use scripts/wal2json to convert to json for debugging FEATURES: - - new `certifiers` pkg contains the tendermint light-client library (name subject to change)! + - new `Verifiers` pkg contains the tendermint light-client library (name subject to change)! - rpc: `/genesis` includes the `app_options` . - rpc: `/abci_query` takes an additional `height` parameter to support historical queries. - rpc/client: new ABCIQueryWithOptions supports options like `trusted` (set false to get a proof) and `height` to query a historical height. diff --git a/Gopkg.lock b/Gopkg.lock index bf98a0af1..31d04b36c 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -11,7 +11,7 @@ branch = "master" name = "github.com/btcsuite/btcd" packages = ["btcec"] - revision = "9a2f9524024889e129a5422aca2cff73cb3eabf6" + revision = "f5e261fc9ec3437697fb31d8b38453c293204b29" [[projects]] name = "github.com/btcsuite/btcutil" @@ -342,7 +342,7 @@ "cpu", "unix" ] - revision = "bd9dbc187b6e1dacfdd2722a87e83093c2d7bd6e" + revision = "3dc4335d56c789b04b0ba99b7a37249d9b614314" [[projects]] name = "golang.org/x/text" diff --git a/cmd/tendermint/commands/lite.go b/cmd/tendermint/commands/lite.go index 14d584b72..edad4fbb7 100644 --- a/cmd/tendermint/commands/lite.go +++ b/cmd/tendermint/commands/lite.go @@ -68,10 +68,10 @@ func runProxy(cmd *cobra.Command, args []string) error { logger.Info("Connecting to source HTTP client...") node := rpcclient.NewHTTP(nodeAddr, "/websocket") - logger.Info("Constructing certifier...") - cert, err := proxy.NewCertifier(chainID, home, node, logger) + logger.Info("Constructing Verifier...") + cert, err := proxy.NewVerifier(chainID, home, node, logger) if err != nil { - return cmn.ErrorWrap(err, "constructing certifier") + return cmn.ErrorWrap(err, "constructing Verifier") } cert.SetLogger(logger) sc := proxy.SecureClient(node, cert) diff --git a/lite/base_certifier.go b/lite/base_verifier.go similarity index 63% rename from lite/base_certifier.go rename to lite/base_verifier.go index 0f9faba37..e60d3953a 100644 --- a/lite/base_certifier.go +++ b/lite/base_verifier.go @@ -3,48 +3,48 @@ package lite import ( "bytes" + cmn "github.com/tendermint/tendermint/libs/common" lerr "github.com/tendermint/tendermint/lite/errors" "github.com/tendermint/tendermint/types" - cmn "github.com/tendermint/tendermint/libs/common" ) -var _ Certifier = (*BaseCertifier)(nil) +var _ Verifier = (*BaseVerifier)(nil) -// BaseCertifier lets us check the validity of SignedHeaders at height or +// BaseVerifier lets us check the validity of SignedHeaders at height or // later, requiring sufficient votes (> 2/3) from the given valset. // To certify blocks produced by a blockchain with mutable validator sets, -// use the InquiringCertifier. +// use the DynamicVerifier. // TODO: Handle unbonding time. -type BaseCertifier struct { +type BaseVerifier struct { chainID string height int64 valset *types.ValidatorSet } -// NewBaseCertifier returns a new certifier initialized with a validator set at +// NewBaseVerifier returns a new Verifier initialized with a validator set at // some height. -func NewBaseCertifier(chainID string, height int64, valset *types.ValidatorSet) *BaseCertifier { - if valset == nil || len(valset.Hash()) == 0 { - panic("NewBaseCertifier requires a valid valset") +func NewBaseVerifier(chainID string, height int64, valset *types.ValidatorSet) *BaseVerifier { + if valset.IsNilOrEmpty() { + panic("NewBaseVerifier requires a valid valset") } - return &BaseCertifier{ + return &BaseVerifier{ chainID: chainID, height: height, valset: valset, } } -// Implements Certifier. -func (bc *BaseCertifier) ChainID() string { +// Implements Verifier. +func (bc *BaseVerifier) ChainID() string { return bc.chainID } -// Implements Certifier. -func (bc *BaseCertifier) Certify(signedHeader types.SignedHeader) error { +// Implements Verifier. +func (bc *BaseVerifier) Certify(signedHeader types.SignedHeader) error { // We can't certify commits older than bc.height. if signedHeader.Height < bc.height { - return cmn.NewError("BaseCertifier height is %v, cannot certify height %v", + return cmn.NewError("BaseVerifier height is %v, cannot certify height %v", bc.height, signedHeader.Height) } diff --git a/lite/base_certifier_test.go b/lite/base_verifier_test.go similarity index 89% rename from lite/base_certifier_test.go rename to lite/base_verifier_test.go index 20342c90a..dab7885f6 100644 --- a/lite/base_certifier_test.go +++ b/lite/base_verifier_test.go @@ -10,16 +10,14 @@ import ( ) func TestBaseCert(t *testing.T) { - // assert, require := assert.New(t), require.New(t) assert := assert.New(t) - // require := require.New(t) keys := genPrivKeys(4) // 20, 30, 40, 50 - the first 3 don't have 2/3, the last 3 do! vals := keys.ToValidators(20, 10) - // and a certifier based on our known set + // and a Verifier based on our known set chainID := "test-static" - cert := NewBaseCertifier(chainID, 2, vals) + cert := NewBaseVerifier(chainID, 2, vals) cases := []struct { keys privKeys diff --git a/lite/client/provider.go b/lite/client/provider.go index 8087be71c..e0c0a331b 100644 --- a/lite/client/provider.go +++ b/lite/client/provider.go @@ -8,12 +8,12 @@ package client import ( "fmt" + log "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/lite" lerr "github.com/tendermint/tendermint/lite/errors" rpcclient "github.com/tendermint/tendermint/rpc/client" ctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/tendermint/tendermint/types" - log "github.com/tendermint/tendermint/libs/log" ) // SignStatusClient combines a SignClient and StatusClient. @@ -106,12 +106,10 @@ func (p *provider) getValidatorSet(chainID string, height int64) (valset *types. err = fmt.Errorf("expected height >= 1, got height %v", height) return } - heightPtr := new(int64) - *heightPtr = height - res, err := p.client.Validators(heightPtr) + res, err := p.client.Validators(&height) if err != nil { // TODO pass through other types of errors. - return nil, lerr.ErrMissingValidators(chainID, height) + return nil, lerr.ErrUnknownValidators(chainID, height) } valset = types.NewValidatorSet(res.Validators) return diff --git a/lite/client/provider_test.go b/lite/client/provider_test.go index f4da423f1..d8704a52e 100644 --- a/lite/client/provider_test.go +++ b/lite/client/provider_test.go @@ -13,7 +13,6 @@ import ( "github.com/tendermint/tendermint/types" ) -// TODO fix tests!! func TestMain(m *testing.M) { app := kvstore.NewKVStoreApplication() node := rpctest.StartTendermint(app) @@ -59,15 +58,4 @@ func TestProvider(t *testing.T) { assert.Nil(err, "%+v", err) assert.Equal(lower, fc.Height()) - /* - // also get by hash (given the match) - fc, err = p.GetByHash(vhash) - require.Nil(err, "%+v", err) - require.Equal(vhash, fc.Header.ValidatorsHash) - - // get by hash fails without match - fc, err = p.GetByHash([]byte("foobar")) - assert.NotNil(err) - assert.True(liteErr.IsCommitNotFoundErr(err)) - */ } diff --git a/lite/commit.go b/lite/commit.go index 89f044177..25efb8dc0 100644 --- a/lite/commit.go +++ b/lite/commit.go @@ -11,7 +11,7 @@ import ( // FullCommit is a signed header (the block header and a commit that signs it), // the validator set which signed the commit, and the next validator set. The // next validator set (which is proven from the block header) allows us to -// revert to block-by-block updating of lite certifier's latest validator set, +// revert to block-by-block updating of lite Verifier's latest validator set, // even in the face of arbitrarily large power changes. type FullCommit struct { SignedHeader types.SignedHeader `json:"signed_header"` diff --git a/lite/dbprovider.go b/lite/dbprovider.go index 8392fceaa..cab695b4a 100644 --- a/lite/dbprovider.go +++ b/lite/dbprovider.go @@ -22,7 +22,10 @@ type DBProvider struct { } func NewDBProvider(label string, db dbm.DB) *DBProvider { + + // NOTE: when debugging, this type of construction might be useful. //db = dbm.NewDebugDB("db provider "+cmn.RandStr(4), db) + cdc := amino.NewCodec() cryptoAmino.RegisterAmino(cdc) dbp := &DBProvider{ @@ -127,8 +130,8 @@ func (dbp *DBProvider) LatestFullCommit(chainID string, minHeight, maxHeight int dbp.logger.Info("DBProvider.LatestFullCommit() found latest.", "height", lfc.Height()) return lfc, nil } else { - dbp.logger.Info("DBProvider.LatestFullCommit() got error", "lfc", lfc) - dbp.logger.Info(fmt.Sprintf("%+v", err)) + dbp.logger.Error("DBProvider.LatestFullCommit() got error", "lfc", lfc) + dbp.logger.Error(fmt.Sprintf("%+v", err)) return lfc, err } } @@ -144,14 +147,19 @@ func (dbp *DBProvider) ValidatorSet(chainID string, height int64) (valset *types func (dbp *DBProvider) getValidatorSet(chainID string, height int64) (valset *types.ValidatorSet, err error) { vsBz := dbp.db.Get(validatorSetKey(chainID, height)) if vsBz == nil { - err = lerr.ErrMissingValidators(chainID, height) + err = lerr.ErrUnknownValidators(chainID, height) return } err = dbp.cdc.UnmarshalBinary(vsBz, &valset) if err != nil { return } - valset.TotalVotingPower() // to test deep equality. + + // To test deep equality. This makes it easier to test for e.g. valset + // equivalence using assert.Equal (tests for deep equality) in our tests, + // which also tests for unexported/private field equivalence. + valset.TotalVotingPower() + return } @@ -209,52 +217,52 @@ func (dbp *DBProvider) deleteAfterN(chainID string, after int) error { itr.Next() } - dbp.logger.Info(fmt.Sprintf("DBProvider.deleteAfterN() deleted %v items\n", numDeleted)) + dbp.logger.Info(fmt.Sprintf("DBProvider.deleteAfterN() deleted %v items", numDeleted)) return nil } //---------------------------------------- +// key encoding func signedHeaderKey(chainID string, height int64) []byte { return []byte(fmt.Sprintf("%s/%010d/sh", chainID, height)) } -var signedHeaderKeyPattern = regexp.MustCompile(`([^/]+)/([0-9]*)/sh`) - -func parseSignedHeaderKey(key []byte) (chainID string, height int64, ok bool) { - submatch := signedHeaderKeyPattern.FindSubmatch(key) - if submatch == nil { - return "", 0, false - } - chainID = string(submatch[1]) - heightStr := string(submatch[2]) - heightInt, err := strconv.Atoi(heightStr) - if err != nil { - return "", 0, false - } - height = int64(heightInt) - ok = true // good! - return -} - func validatorSetKey(chainID string, height int64) []byte { return []byte(fmt.Sprintf("%s/%010d/vs", chainID, height)) } -var chainKeyPrefixPattern = regexp.MustCompile(`([^/]+)/([0-9]*)/`) +//---------------------------------------- +// key parsing -func parseChainKeyPrefix(key []byte) (chainID string, height int64, ok bool) { - submatch := chainKeyPrefixPattern.FindSubmatch(key) +var keyPattern = regexp.MustCompile(`^([^/]+)/([0-9]*)/(.*)$`) + +func parseKey(key []byte) (chainID string, height int64, part string, ok bool) { + submatch := keyPattern.FindSubmatch(key) if submatch == nil { - return "", 0, false + return "", 0, "", false } chainID = string(submatch[1]) heightStr := string(submatch[2]) heightInt, err := strconv.Atoi(heightStr) if err != nil { - return "", 0, false + return "", 0, "", false } height = int64(heightInt) + part = string(submatch[3]) ok = true // good! return } + +func parseSignedHeaderKey(key []byte) (chainID string, height int64, ok bool) { + chainID, height, part, ok := parseKey(key) + if part != "sh" { + return "", 0, false + } + return chainID, height, true +} + +func parseChainKeyPrefix(key []byte) (chainID string, height int64, ok bool) { + chainID, height, _, ok = parseKey(key) + return chainID, height, true +} diff --git a/lite/doc.go b/lite/doc.go index 07977ebe7..59f770567 100644 --- a/lite/doc.go +++ b/lite/doc.go @@ -35,29 +35,29 @@ change on the chain. In practice, most applications will not have frequent drastic updates to the validator set, so the logic defined in this package for lite client syncing is optimized to use intelligent bisection and block-skipping for efficient sourcing and verification of these data structures -and updates to the validator set (see the InquiringCertifier for more +and updates to the validator set (see the DynamicVerifier for more information). The FullCommit is also declared in this package as a convenience structure, which includes the SignedHeader along with the full current and next ValidatorSets. -## Certifier +## Verifier -A Certifier validates a new SignedHeader given the currently known state. There -are two different types of Certifiers provided. +A Verifier validates a new SignedHeader given the currently known state. There +are two different types of Verifiers provided. -BaseCertifier - given a validator set and a height, this Certifier verifies +BaseVerifier - given a validator set and a height, this Verifier verifies that > 2/3 of the voting power of the given validator set had signed the SignedHeader, and that the SignedHeader was to be signed by the exact given validator set, and that the height of the commit is at least height (or greater). SignedHeader.Commit may be signed by a different validator set, it can get -certified with a BaseCertifier as long as sufficient signatures from the +certified with a BaseVerifier as long as sufficient signatures from the previous validator set are present in the commit. -InquiringCertifier - this certifier implements an auto-update and persistence +DynamicVerifier - this Verifier implements an auto-update and persistence strategy to certify any SignedHeader of the blockchain. ## Provider and PersistentProvider @@ -77,7 +77,7 @@ type Provider interface { * client.NewHTTPProvider - query Tendermint rpc. A PersistentProvider is a Provider that also allows for saving state. This is -used by the InquiringCertifier for persistence. +used by the DynamicVerifier for persistence. ```go type PersistentProvider interface { @@ -131,7 +131,7 @@ important to verify that you have the proper validator set when initializing the client, as that is the root of all trust. The software currently assumes that the unbonding period is infinite in -duration. If the InquiringCertifier hasn't been updated in a while, you should +duration. If the DynamicVerifier hasn't been updated in a while, you should manually verify the block headers using other sources. TODO: Update the software to handle cases around the unbonding period. diff --git a/lite/inquiring_certifier.go b/lite/dynamic_verifier.go similarity index 64% rename from lite/inquiring_certifier.go rename to lite/dynamic_verifier.go index 316374471..3d1a70f27 100644 --- a/lite/inquiring_certifier.go +++ b/lite/dynamic_verifier.go @@ -3,18 +3,18 @@ package lite import ( "bytes" + log "github.com/tendermint/tendermint/libs/log" lerr "github.com/tendermint/tendermint/lite/errors" "github.com/tendermint/tendermint/types" - log "github.com/tendermint/tendermint/libs/log" ) -var _ Certifier = (*InquiringCertifier)(nil) +var _ Verifier = (*DynamicVerifier)(nil) -// InquiringCertifier implements an auto-updating certifier. It uses a +// DynamicVerifier implements an auto-updating Verifier. It uses a // "source" provider to obtain the needed FullCommits to securely sync with // validator set changes. It stores properly validated data on the // "trusted" local system. -type InquiringCertifier struct { +type DynamicVerifier struct { logger log.Logger chainID string // These are only properly validated data, from local system. @@ -23,14 +23,14 @@ type InquiringCertifier struct { source Provider } -// NewInquiringCertifier returns a new InquiringCertifier. It uses the +// NewDynamicVerifier returns a new DynamicVerifier. It uses the // trusted provider to store validated data and the source provider to // obtain missing data (e.g. FullCommits). // // The trusted provider should a CacheProvider, MemProvider or // files.Provider. The source provider should be a client.HTTPProvider. -func NewInquiringCertifier(chainID string, trusted PersistentProvider, source Provider) *InquiringCertifier { - return &InquiringCertifier{ +func NewDynamicVerifier(chainID string, trusted PersistentProvider, source Provider) *DynamicVerifier { + return &DynamicVerifier{ logger: log.NewNopLogger(), chainID: chainID, trusted: trusted, @@ -38,64 +38,64 @@ func NewInquiringCertifier(chainID string, trusted PersistentProvider, source Pr } } -func (ic *InquiringCertifier) SetLogger(logger log.Logger) { +func (ic *DynamicVerifier) SetLogger(logger log.Logger) { logger = logger.With("module", "lite") ic.logger = logger ic.trusted.SetLogger(logger) ic.source.SetLogger(logger) } -// Implements Certifier. -func (ic *InquiringCertifier) ChainID() string { +// Implements Verifier. +func (ic *DynamicVerifier) ChainID() string { return ic.chainID } -// Implements Certifier. +// Implements Verifier. // -// If the validators have changed since the last know time, it looks to +// If the validators have changed since the last known time, it looks to // ic.trusted and ic.source to prove the new validators. On success, it will // try to store the SignedHeader in ic.trusted if the next // validator can be sourced. -func (ic *InquiringCertifier) Certify(shdr types.SignedHeader) error { +func (ic *DynamicVerifier) Certify(shdr types.SignedHeader) error { // Get the latest known full commit <= h-1 from our trusted providers. // The full commit at h-1 contains the valset to sign for h. h := shdr.Height - 1 - tfc, err := ic.trusted.LatestFullCommit(ic.chainID, 1, h) + trustedFC, err := ic.trusted.LatestFullCommit(ic.chainID, 1, h) if err != nil { return err } - if tfc.Height() == h { + if trustedFC.Height() == h { // Return error if valset doesn't match. if !bytes.Equal( - tfc.NextValidators.Hash(), + trustedFC.NextValidators.Hash(), shdr.Header.ValidatorsHash) { return lerr.ErrUnexpectedValidators( - tfc.NextValidators.Hash(), + trustedFC.NextValidators.Hash(), shdr.Header.ValidatorsHash) } } else { // If valset doesn't match... - if !bytes.Equal(tfc.NextValidators.Hash(), + if !bytes.Equal(trustedFC.NextValidators.Hash(), shdr.Header.ValidatorsHash) { // ... update. - tfc, err = ic.updateToHeight(h) + trustedFC, err = ic.updateToHeight(h) if err != nil { return err } // Return error if valset _still_ doesn't match. - if !bytes.Equal(tfc.NextValidators.Hash(), + if !bytes.Equal(trustedFC.NextValidators.Hash(), shdr.Header.ValidatorsHash) { return lerr.ErrUnexpectedValidators( - tfc.NextValidators.Hash(), + trustedFC.NextValidators.Hash(), shdr.Header.ValidatorsHash) } } } // Certify the signed header using the matching valset. - cert := NewBaseCertifier(ic.chainID, tfc.Height()+1, tfc.NextValidators) + cert := NewBaseVerifier(ic.chainID, trustedFC.Height()+1, trustedFC.NextValidators) err = cert.Certify(shdr) if err != nil { return err @@ -103,7 +103,7 @@ func (ic *InquiringCertifier) Certify(shdr types.SignedHeader) error { // Get the next validator set. nextValset, err := ic.source.ValidatorSet(ic.chainID, shdr.Height+1) - if lerr.IsErrMissingValidators(err) { + if lerr.IsErrUnknownValidators(err) { // Ignore this error. return nil } else if err != nil { @@ -113,7 +113,7 @@ func (ic *InquiringCertifier) Certify(shdr types.SignedHeader) error { // Create filled FullCommit. nfc := FullCommit{ SignedHeader: shdr, - Validators: tfc.NextValidators, + Validators: trustedFC.NextValidators, NextValidators: nextValset, } // Validate the full commit. This checks the cryptographic @@ -127,22 +127,22 @@ func (ic *InquiringCertifier) Certify(shdr types.SignedHeader) error { // verifyAndSave will verify if this is a valid source full commit given the // best match trusted full commit, and if good, persist to ic.trusted. -// Returns ErrTooMuchChange when >2/3 of tfc did not sign sfc. -// Panics if tfc.Height() >= sfc.Height(). -func (ic *InquiringCertifier) verifyAndSave(tfc, sfc FullCommit) error { - if tfc.Height() >= sfc.Height() { +// Returns ErrTooMuchChange when >2/3 of trustedFC did not sign sourceFC. +// Panics if trustedFC.Height() >= sourceFC.Height(). +func (ic *DynamicVerifier) verifyAndSave(trustedFC, sourceFC FullCommit) error { + if trustedFC.Height() >= sourceFC.Height() { panic("should not happen") } - err := tfc.NextValidators.VerifyFutureCommit( - sfc.Validators, - ic.chainID, sfc.SignedHeader.Commit.BlockID, - sfc.SignedHeader.Height, sfc.SignedHeader.Commit, + err := trustedFC.NextValidators.VerifyFutureCommit( + sourceFC.Validators, + ic.chainID, sourceFC.SignedHeader.Commit.BlockID, + sourceFC.SignedHeader.Height, sourceFC.SignedHeader.Commit, ) if err != nil { return err } - return ic.trusted.SaveFullCommit(sfc) + return ic.trusted.SaveFullCommit(sourceFC) } // updateToHeight will use divide-and-conquer to find a path to h. @@ -150,48 +150,48 @@ func (ic *InquiringCertifier) verifyAndSave(tfc, sfc FullCommit) error { // for height h, using repeated applications of bisection if necessary. // // Returns ErrCommitNotFound if source provider doesn't have the commit for h. -func (ic *InquiringCertifier) updateToHeight(h int64) (FullCommit, error) { +func (ic *DynamicVerifier) updateToHeight(h int64) (FullCommit, error) { // Fetch latest full commit from source. - sfc, err := ic.source.LatestFullCommit(ic.chainID, h, h) + sourceFC, err := ic.source.LatestFullCommit(ic.chainID, h, h) if err != nil { return FullCommit{}, err } // Validate the full commit. This checks the cryptographic // signatures of Commit against Validators. - if err := sfc.ValidateFull(ic.chainID); err != nil { + if err := sourceFC.ValidateFull(ic.chainID); err != nil { return FullCommit{}, err } - // If sfc.Height() != h, we can't do it. - if sfc.Height() != h { + // If sourceFC.Height() != h, we can't do it. + if sourceFC.Height() != h { return FullCommit{}, lerr.ErrCommitNotFound() } FOR_LOOP: for { // Fetch latest full commit from trusted. - tfc, err := ic.trusted.LatestFullCommit(ic.chainID, 1, h) + trustedFC, err := ic.trusted.LatestFullCommit(ic.chainID, 1, h) if err != nil { return FullCommit{}, err } // We have nothing to do. - if tfc.Height() == h { - return tfc, nil + if trustedFC.Height() == h { + return trustedFC, nil } // Try to update to full commit with checks. - err = ic.verifyAndSave(tfc, sfc) + err = ic.verifyAndSave(trustedFC, sourceFC) if err == nil { // All good! - return sfc, nil + return sourceFC, nil } // Handle special case when err is ErrTooMuchChange. if lerr.IsErrTooMuchChange(err) { // Divide and conquer. - start, end := tfc.Height(), sfc.Height() + start, end := trustedFC.Height(), sourceFC.Height() if !(start < end) { panic("should not happen") } @@ -207,7 +207,7 @@ FOR_LOOP: } } -func (ic *InquiringCertifier) LastTrustedHeight() int64 { +func (ic *DynamicVerifier) LastTrustedHeight() int64 { fc, err := ic.trusted.LatestFullCommit(ic.chainID, 1, 1<<63-1) if err != nil { panic("should not happen") diff --git a/lite/inquiring_certifier_test.go b/lite/dynamic_verifier_test.go similarity index 95% rename from lite/inquiring_certifier_test.go rename to lite/dynamic_verifier_test.go index 5eb63727c..74e2d55a9 100644 --- a/lite/inquiring_certifier_test.go +++ b/lite/dynamic_verifier_test.go @@ -41,10 +41,10 @@ func TestInquirerValidPath(t *testing.T) { nkeys = nkeys.Extend(1) } - // Initialize a certifier with the initial state. + // Initialize a Verifier with the initial state. err := trust.SaveFullCommit(fcz[0]) require.Nil(err) - cert := NewInquiringCertifier(chainID, trust, source) + cert := NewDynamicVerifier(chainID, trust, source) cert.SetLogger(log.TestingLogger()) // This should fail validation: @@ -99,10 +99,10 @@ func TestInquirerVerifyHistorical(t *testing.T) { nkeys = nkeys.Extend(1) } - // Initialize a certifier with the initial state. + // Initialize a Verifier with the initial state. err := trust.SaveFullCommit(fcz[0]) require.Nil(err) - cert := NewInquiringCertifier(chainID, trust, source) + cert := NewDynamicVerifier(chainID, trust, source) cert.SetLogger(log.TestingLogger()) // Store a few full commits as trust. diff --git a/lite/errors/errors.go b/lite/errors/errors.go index 96a5a02ad..61426b234 100644 --- a/lite/errors/errors.go +++ b/lite/errors/errors.go @@ -31,12 +31,12 @@ func (e errTooMuchChange) Error() string { return "Insufficient signatures to validate due to valset changes" } -type errMissingValidators struct { +type errUnknownValidators struct { chainID string height int64 } -func (e errMissingValidators) Error() string { +func (e errUnknownValidators) Error() string { return fmt.Sprintf("Validators are unknown or missing for chain %s and height %d", e.chainID, e.height) } @@ -96,16 +96,16 @@ func IsErrTooMuchChange(err error) bool { } //----------------- -// ErrMissingValidators +// ErrUnknownValidators -// ErrMissingValidators indicates that some validator set was missing or unknown. -func ErrMissingValidators(chainID string, height int64) error { - return cmn.ErrorWrap(errMissingValidators{chainID, height}, "") +// ErrUnknownValidators indicates that some validator set was missing or unknown. +func ErrUnknownValidators(chainID string, height int64) error { + return cmn.ErrorWrap(errUnknownValidators{chainID, height}, "") } -func IsErrMissingValidators(err error) bool { +func IsErrUnknownValidators(err error) bool { if err_, ok := err.(cmn.Error); ok { - _, ok := err_.Data().(errMissingValidators) + _, ok := err_.Data().(errUnknownValidators) return ok } return false diff --git a/lite/multiprovider.go b/lite/multiprovider.go index 991a12d71..734d042c4 100644 --- a/lite/multiprovider.go +++ b/lite/multiprovider.go @@ -1,9 +1,9 @@ package lite import ( + log "github.com/tendermint/tendermint/libs/log" lerr "github.com/tendermint/tendermint/lite/errors" "github.com/tendermint/tendermint/types" - log "github.com/tendermint/tendermint/libs/log" ) // multiProvider allows you to place one or more caches in front of a source @@ -79,5 +79,5 @@ func (mc *multiProvider) ValidatorSet(chainID string, height int64) (valset *typ return valset, nil } } - return nil, lerr.ErrMissingValidators(chainID, height) + return nil, lerr.ErrUnknownValidators(chainID, height) } diff --git a/lite/proxy/query.go b/lite/proxy/query.go index a7132223d..6f5a28992 100644 --- a/lite/proxy/query.go +++ b/lite/proxy/query.go @@ -28,13 +28,13 @@ type KeyProof interface { } // GetWithProof will query the key on the given node, and verify it has -// a valid proof, as defined by the certifier. +// a valid proof, as defined by the Verifier. // // If there is any error in checking, returns an error. // If val is non-empty, proof should be KeyExistsProof // If val is empty, proof should be KeyMissingProof func GetWithProof(key []byte, reqHeight int64, node rpcclient.Client, - cert lite.Certifier) ( + cert lite.Verifier) ( val cmn.HexBytes, height int64, proof KeyProof, err error) { if reqHeight < 0 { @@ -54,7 +54,7 @@ func GetWithProof(key []byte, reqHeight int64, node rpcclient.Client, // GetWithProofOptions is useful if you want full access to the ABCIQueryOptions func GetWithProofOptions(path string, key []byte, opts rpcclient.ABCIQueryOptions, - node rpcclient.Client, cert lite.Certifier) ( + node rpcclient.Client, cert lite.Verifier) ( *ctypes.ResultABCIQuery, KeyProof, error) { _resp, err := node.ABCIQueryWithOptions(path, key, opts) @@ -128,7 +128,7 @@ func GetWithProofOptions(path string, key []byte, opts rpcclient.ABCIQueryOption // GetCertifiedCommit gets the signed header for a given height and certifies // it. Returns error if unable to get a proven header. -func GetCertifiedCommit(h int64, client rpcclient.Client, cert lite.Certifier) (types.SignedHeader, error) { +func GetCertifiedCommit(h int64, client rpcclient.Client, cert lite.Verifier) (types.SignedHeader, error) { // FIXME: cannot use cert.GetByHeight for now, as it also requires // Validators and will fail on querying tendermint for non-current height. diff --git a/lite/proxy/query_test.go b/lite/proxy/query_test.go index fcc6659af..7f759cc69 100644 --- a/lite/proxy/query_test.go +++ b/lite/proxy/query_test.go @@ -58,7 +58,7 @@ func _TestAppProofs(t *testing.T) { source := certclient.NewProvider(chainID, cl) seed, err := source.LatestFullCommit(chainID, brh-2, brh-2) require.NoError(err, "%+v", err) - cert := lite.NewBaseCertifier("my-chain", seed.Height(), seed.Validators) + cert := lite.NewBaseVerifier("my-chain", seed.Height(), seed.Validators) client.WaitForHeight(cl, 3, nil) latest, err := source.LatestFullCommit(chainID, 1, 1<<63-1) @@ -117,7 +117,7 @@ func _TestTxProofs(t *testing.T) { source := certclient.NewProvider(chainID, cl) seed, err := source.LatestFullCommit(chainID, brh-2, brh-2) require.NoError(err, "%+v", err) - cert := lite.NewBaseCertifier("my-chain", seed.Height(), seed.Validators) + cert := lite.NewBaseVerifier("my-chain", seed.Height(), seed.Validators) // First let's make sure a bogus transaction hash returns a valid non-existence proof. key := types.Tx([]byte("bogus")).Hash() diff --git a/lite/proxy/certifier.go b/lite/proxy/verifier.go similarity index 73% rename from lite/proxy/certifier.go rename to lite/proxy/verifier.go index bd09b1ab0..6686def0d 100644 --- a/lite/proxy/certifier.go +++ b/lite/proxy/verifier.go @@ -8,10 +8,10 @@ import ( log "github.com/tendermint/tendermint/libs/log" ) -func NewCertifier(chainID, rootDir string, client lclient.SignStatusClient, logger log.Logger) (*lite.InquiringCertifier, error) { +func NewVerifier(chainID, rootDir string, client lclient.SignStatusClient, logger log.Logger) (*lite.DynamicVerifier, error) { logger = logger.With("module", "lite/proxy") - logger.Info("lite/proxy/NewCertifier()...", "chainID", chainID, "rootDir", rootDir, "client", client) + logger.Info("lite/proxy/NewVerifier()...", "chainID", chainID, "rootDir", rootDir, "client", client) memProvider := lite.NewDBProvider("trusted.mem", dbm.NewMemDB()).SetLimit(10) lvlProvider := lite.NewDBProvider("trusted.lvl", dbm.NewDB("trust-base", dbm.LevelDBBackend, rootDir)) @@ -20,13 +20,13 @@ func NewCertifier(chainID, rootDir string, client lclient.SignStatusClient, logg lvlProvider, ) source := lclient.NewProvider(chainID, client) - cert := lite.NewInquiringCertifier(chainID, trust, source) + cert := lite.NewDynamicVerifier(chainID, trust, source) cert.SetLogger(logger) // Sets logger recursively. // TODO: Make this more secure, e.g. make it interactive in the console? _, err := trust.LatestFullCommit(chainID, 1, 1<<63-1) if err != nil { - logger.Info("lite/proxy/NewCertifier found no trusted full commit, initializing from source from height 1...") + logger.Info("lite/proxy/NewVerifier found no trusted full commit, initializing from source from height 1...") fc, err := source.LatestFullCommit(chainID, 1, 1) if err != nil { return nil, cmn.ErrorWrap(err, "fetching source full commit @ height 1") diff --git a/lite/proxy/wrapper.go b/lite/proxy/wrapper.go index ac1d1dbc3..522511a81 100644 --- a/lite/proxy/wrapper.go +++ b/lite/proxy/wrapper.go @@ -10,18 +10,18 @@ import ( var _ rpcclient.Client = Wrapper{} -// Wrapper wraps a rpcclient with a Certifier and double-checks any input that is +// Wrapper wraps a rpcclient with a Verifier and double-checks any input that is // provable before passing it along. Allows you to make any rpcclient fully secure. type Wrapper struct { rpcclient.Client - cert *lite.InquiringCertifier + cert *lite.DynamicVerifier } -// SecureClient uses a given certifier to wrap an connection to an untrusted +// SecureClient uses a given Verifier to wrap an connection to an untrusted // host and return a cryptographically secure rpc client. // // If it is wrapping an HTTP rpcclient, it will also wrap the websocket interface -func SecureClient(c rpcclient.Client, cert *lite.InquiringCertifier) Wrapper { +func SecureClient(c rpcclient.Client, cert *lite.DynamicVerifier) Wrapper { wrap := Wrapper{c, cert} // TODO: no longer possible as no more such interface exposed.... // if we wrap http client, then we can swap out the event switch to filter diff --git a/lite/types.go b/lite/types.go index 1f4797992..7228c74a9 100644 --- a/lite/types.go +++ b/lite/types.go @@ -4,10 +4,10 @@ import ( "github.com/tendermint/tendermint/types" ) -// Certifier checks the votes to make sure the block really is signed properly. -// Certifier must know the current or recent set of validitors by some other +// Verifier checks the votes to make sure the block really is signed properly. +// Verifier must know the current or recent set of validitors by some other // means. -type Certifier interface { +type Verifier interface { Certify(sheader types.SignedHeader) error ChainID() string } diff --git a/types/block.go b/types/block.go index 0b64c7b8f..c112ee501 100644 --- a/types/block.go +++ b/types/block.go @@ -446,7 +446,7 @@ type SignedHeader struct { // and commit are consistent. // // NOTE: This does not actually check the cryptographic signatures. Make -// sure to use a Certifier to validate the signatures actually provide a +// 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 { diff --git a/types/validator_set.go b/types/validator_set.go index 6d580acef..4dab4d840 100644 --- a/types/validator_set.go +++ b/types/validator_set.go @@ -48,6 +48,11 @@ func NewValidatorSet(valz []*Validator) *ValidatorSet { return vals } +// Nil or empty validator sets are invalid. +func (vals *ValidatorSet) IsNilOrEmpty() bool { + return vals == nil || len(vals.Validators) == 0 +} + // Increment Accum and update the proposer on a copy, and return it. func (vals *ValidatorSet) CopyIncrementAccum(times int) *ValidatorSet { copy := vals.Copy()