|
|
- /*
- Package light 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).
-
- */
- package light
|