lite

package
v0.22.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 10, 2018 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package lite allows you to securely validate headers without a full node.

This library pulls together all the crypto and algorithms, so given a relatively recent (< unbonding period) known validator set, one can get indisputable proof that data is in the chain (current state) or detect if the node is lying to the client.

Tendermint RPC exposes a lot of info, but a malicious node could return any data it wants to queries, or even to block headers, even making up fake signatures from non-existent validators to justify it. This is a lot of logic to get right, to be contained in a small, easy to use library, that does this for you, so you can just build nice UI.

We design for clients who have no strong trust relationship with any tendermint node, just the validator set as a whole. Beyond building nice mobile or desktop applications, the cosmos hub is another important example of a client, that needs undeniable proof without syncing the full chain, in order to efficiently implement IBC.

Commits

There are two main data structures that we pass around - Commit and FullCommit. Both of them mirror what information is exposed in tendermint rpc.

Commit is a block header along with enough validator signatures to prove its validity (> 2/3 of the voting power). A FullCommit is a Commit along with the full validator set. When the validator set doesn't change, the Commit is enough, but since the block header only has a hash, we need the FullCommit to follow any changes to the validator set.

Certifiers

A Certifier validates a new Commit given the currently known state. There are three different types of Certifiers exposed, each one building on the last one, with additional complexity.

Static - given the validator set upon initialization. Verifies all signatures against that set and if the validator set changes, it will reject all headers.

Dynamic - This wraps Static and has the same Certify method. However, it adds an Update method, which can be called with a FullCommit when the validator set changes. If it can prove this is a valid transition, it will update the validator set.

Inquiring - this wraps Dynamic and implements an auto-update strategy on top of the Dynamic update. If a call to Certify fails as the validator set has changed, then it attempts to find a FullCommit and Update to that header. To get these FullCommits, it makes use of a Provider.

Providers

A Provider allows us to store and retrieve the FullCommits, to provide memory to the Inquiring Certifier.

NewMemStoreProvider - in-memory cache.

files.NewProvider - disk backed storage.

client.NewHTTPProvider - query tendermint rpc.

NewCacheProvider - combine multiple providers.

The suggested use for local light clients is client.NewHTTPProvider for getting new data (Source), and NewCacheProvider(NewMemStoreProvider(), files.NewProvider()) to store confirmed headers (Trusted)

How We Track Validators

Unless you want to blindly trust the node you talk with, you need to trace every response back to a hash in a block header and validate the commit signatures of that block header match the proper validator set. If there is a contant validator set, you store it locally upon initialization of the client, and check against that every time.

Once there is a dynamic validator set, the issue of verifying a block becomes a bit more tricky. There is background information in a github issue (https://github.com/tendermint/tendermint/issues/377).

In short, if there is a block at height H with a known (trusted) validator set V, and another block at height H' (H' > H) with validator set V' != V, then we want a way to safely update it.

First, get the new (unconfirmed) validator set V' and verify H' is internally consistent and properly signed by this V'. Assuming it is a valid block, we check that at least 2/3 of the validators in V also signed it, meaning it would also be valid under our old assumptions. That should be enough, but we can also check that the V counts for at least 2/3 of the total votes in H' for extra safety (we can have a discussion if this is strictly required). If we can verify all this, then we can accept H' and V' as valid and use that to validate all blocks X > H'.

If we cannot update directly from H -> H' because there was too much change to the validator set, then we can look for some Hm (H < Hm < H') with a validator set Vm. Then we try to update H -> Hm and Hm -> H' in two separate steps. If one of these steps doesn't work, then we continue bisecting, until we eventually have to externally validate the valdiator set changes at every block.

Since we never trust any server in this protocol, only the signatures themselves, it doesn't matter if the seed comes from a (possibly malicious) node or a (possibly malicious) user. We can accept it or reject it based only on our trusted validator set and cryptographic proofs. This makes it extremely important to verify that you have the proper validator set when initializing the client, as that is the root of all trust.

Or course, this assumes that the known block is within the unbonding period to avoid the "nothing at stake" problem. If you haven't seen the state in a few months, you will need to manually verify the new validator set hash using off-chain means (the same as getting the initial hash).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Certifier

type Certifier interface {
	Certify(check Commit) error
	ChainID() string
}

Certifier checks the votes to make sure the block really is signed properly. Certifier must know the current set of validitors by some other means.

type Commit

type Commit types.SignedHeader

Commit is basically the rpc /commit response, but extended

This is the basepoint for proving anything on the blockchain. It contains a signed header. If the signatures are valid and > 2/3 of the known set, we can store this checkpoint and use it to prove any number of aspects of the system: such as txs, abci state, validator sets, etc...

func (Commit) Height

func (c Commit) Height() int64

Height returns the height of the header.

func (Commit) ValidateBasic

func (c Commit) ValidateBasic(chainID string) error

ValidateBasic does basic consistency checks and makes sure the headers and commits are all consistent and refer to our chain.

Make sure to use a Verifier to validate the signatures actually provide a significantly strong proof for this header's validity.

func (Commit) ValidatorsHash

func (c Commit) ValidatorsHash() []byte

ValidatorsHash returns the hash of the validator set.

type DynamicCertifier added in v0.16.0

type DynamicCertifier struct {
	// contains filtered or unexported fields
}

DynamicCertifier uses a StaticCertifier for Certify, but adds an Update method to allow for a change of validators.

You can pass in a FullCommit with another validator set, and if this is a provably secure transition (< 1/3 change, sufficient signatures), then it will update the validator set for the next Certify call. For security, it will only follow validator set changes going forward.

func NewDynamicCertifier added in v0.16.0

func NewDynamicCertifier(chainID string, vals *types.ValidatorSet, height int64) *DynamicCertifier

NewDynamic returns a new dynamic certifier.

func (*DynamicCertifier) Certify added in v0.16.0

func (dc *DynamicCertifier) Certify(check Commit) error

Certify will verify whether the commit is valid and will update the height if it is or return an error if it is not. Implements Certifier.

func (*DynamicCertifier) ChainID added in v0.16.0

func (dc *DynamicCertifier) ChainID() string

ChainID returns the chain id of this certifier. Implements Certifier.

func (*DynamicCertifier) Hash added in v0.16.0

func (dc *DynamicCertifier) Hash() []byte

Hash returns the hash of this certifier.

func (*DynamicCertifier) LastHeight added in v0.16.0

func (dc *DynamicCertifier) LastHeight() int64

LastHeight returns the last height of this certifier.

func (*DynamicCertifier) Update added in v0.16.0

func (dc *DynamicCertifier) Update(fc FullCommit) error

Update will verify if this is a valid change and update the certifying validator set if safe to do so.

Returns an error if update is impossible (invalid proof or IsTooMuchChangeErr)

func (*DynamicCertifier) Validators added in v0.16.0

func (dc *DynamicCertifier) Validators() *types.ValidatorSet

Validators returns the validators of this certifier.

type FullCommit

type FullCommit struct {
	Commit     `json:"commit"`
	Validators *types.ValidatorSet `json:"validator_set"`
}

FullCommit is a commit and the actual validator set, the base info you need to update to a given point, assuming knowledge of some previous validator set

func NewFullCommit

func NewFullCommit(commit Commit, vals *types.ValidatorSet) FullCommit

NewFullCommit returns a new FullCommit.

type InquiringCertifier added in v0.16.0

type InquiringCertifier struct {

	// This is a source of new info, like a node rpc, or other import method
	Source Provider
	// contains filtered or unexported fields
}

InquiringCertifier wraps a dynamic certifier and implements an auto-update strategy. If a call to Certify fails due to a change it validator set, InquiringCertifier will try and find a previous FullCommit which it can use to safely update the validator set. It uses a source provider to obtain the needed FullCommits. It stores properly validated data on the local system.

func NewInquiringCertifier added in v0.16.0

func NewInquiringCertifier(chainID string, fc FullCommit, trusted Provider,
	source Provider) (*InquiringCertifier, error)

NewInquiringCertifier returns a new Inquiring object. It uses the trusted provider to store validated data and the source provider to obtain missing FullCommits.

Example: The trusted provider should a CacheProvider, MemProvider or files.Provider. The source provider should be a client.HTTPProvider.

func (*InquiringCertifier) Certify added in v0.16.0

func (ic *InquiringCertifier) Certify(commit Commit) error

Certify makes sure this is checkpoint is valid.

If the validators have changed since the last know time, it looks for a path to prove the new validators.

On success, it will store the checkpoint in the store for later viewing Implements Certifier.

func (*InquiringCertifier) ChainID added in v0.16.0

func (ic *InquiringCertifier) ChainID() string

ChainID returns the chain id. Implements Certifier.

func (*InquiringCertifier) LastHeight added in v0.16.0

func (ic *InquiringCertifier) LastHeight() int64

LastHeight returns the last height.

func (*InquiringCertifier) Update added in v0.16.0

func (ic *InquiringCertifier) Update(fc FullCommit) error

Update will verify if this is a valid change and update the certifying validator set if safe to do so.

func (*InquiringCertifier) Validators added in v0.16.0

func (ic *InquiringCertifier) Validators() *types.ValidatorSet

Validators returns the validator set.

type Provider

type Provider interface {
	// StoreCommit saves a FullCommit after we have verified it,
	// so we can query for it later. Important for updating our
	// store of trusted commits.
	StoreCommit(fc FullCommit) error
	// GetByHeight returns the closest commit with height <= h.
	GetByHeight(h int64) (FullCommit, error)
	// GetByHash returns a commit exactly matching this validator hash.
	GetByHash(hash []byte) (FullCommit, error)
	// LatestCommit returns the newest commit stored.
	LatestCommit() (FullCommit, error)
}

Provider is used to get more validators by other means.

Examples: MemProvider, files.Provider, client.Provider, CacheProvider....

func NewCacheProvider

func NewCacheProvider(providers ...Provider) Provider

NewCacheProvider returns a new provider which wraps multiple other providers.

func NewMemStoreProvider

func NewMemStoreProvider() Provider

NewMemStoreProvider returns a new in-memory provider.

type StaticCertifier added in v0.16.0

type StaticCertifier struct {
	// contains filtered or unexported fields
}

StaticCertifier assumes a static set of validators, set on initilization and checks against them. The signatures on every header is checked for > 2/3 votes against the known validator set upon Certify

Good for testing or really simple chains. Building block to support real-world functionality.

func NewStaticCertifier added in v0.16.0

func NewStaticCertifier(chainID string, vals *types.ValidatorSet) *StaticCertifier

NewStaticCertifier returns a new certifier with a static validator set.

func (*StaticCertifier) Certify added in v0.16.0

func (sc *StaticCertifier) Certify(commit Commit) error

Certify makes sure that the commit is valid. Implements Certifier.

func (*StaticCertifier) ChainID added in v0.16.0

func (sc *StaticCertifier) ChainID() string

ChainID returns the chain id. Implements Certifier.

func (*StaticCertifier) Hash added in v0.16.0

func (sc *StaticCertifier) Hash() []byte

Hash returns the hash of the validator set.

func (*StaticCertifier) Validators added in v0.16.0

func (sc *StaticCertifier) Validators() *types.ValidatorSet

Validators returns the validator set.

type ValKeys

type ValKeys []crypto.PrivKey

ValKeys is a helper for testing.

It lets us simulate signing with many keys, either ed25519 or secp256k1. The main use case is to create a set, and call GenCommit to get properly signed header for testing.

You can set different weights of validators each time you call ToValidators, and can optionally extend the validator set later with Extend or ExtendSecp

func GenSecpValKeys

func GenSecpValKeys(n int) ValKeys

GenSecpValKeys produces an array of secp256k1 private keys to generate commits.

func GenValKeys

func GenValKeys(n int) ValKeys

GenValKeys produces an array of private keys to generate commits.

func (ValKeys) Change

func (v ValKeys) Change(i int) ValKeys

Change replaces the key at index i.

func (ValKeys) Extend

func (v ValKeys) Extend(n int) ValKeys

Extend adds n more keys (to remove, just take a slice).

func (ValKeys) ExtendSecp

func (v ValKeys) ExtendSecp(n int) ValKeys

ExtendSecp adds n more secp256k1 keys (to remove, just take a slice).

func (ValKeys) GenCommit

func (v ValKeys) GenCommit(chainID string, height int64, txs types.Txs,
	vals *types.ValidatorSet, appHash, consHash, resHash []byte, first, last int) Commit

GenCommit calls genHeader and signHeader and combines them into a Commit.

func (ValKeys) GenFullCommit

func (v ValKeys) GenFullCommit(chainID string, height int64, txs types.Txs,
	vals *types.ValidatorSet, appHash, consHash, resHash []byte, first, last int) FullCommit

GenFullCommit calls genHeader and signHeader and combines them into a Commit.

func (ValKeys) ToValidators

func (v ValKeys) ToValidators(init, inc int64) *types.ValidatorSet

ToValidators produces a list of validators from the set of keys The first key has weight `init` and it increases by `inc` every step so we can have all the same weight, or a simple linear distribution (should be enough for testing).

Directories

Path Synopsis
Package client defines a provider that uses a rpcclient to get information, which is used to get new headers and validators directly from a node.
Package client defines a provider that uses a rpcclient to get information, which is used to get new headers and validators directly from a node.
Package files defines a Provider that stores all data in the filesystem We assume the same validator hash may be reused by many different headers/Commits, and thus store it separately.
Package files defines a Provider that stores all data in the filesystem We assume the same validator hash may be reused by many different headers/Commits, and thus store it separately.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL