mirror of
https://github.com/fluencelabs/tendermint
synced 2025-07-31 04:01:55 +00:00
Compare commits
10 Commits
marko/bloc
...
anton/lc-a
Author | SHA1 | Date | |
---|---|---|---|
|
7402276325 | ||
|
804023299a | ||
|
09db7253d5 | ||
|
ed15b562df | ||
|
29e148a36d | ||
|
c696489da4 | ||
|
91f66592f3 | ||
|
a79a325011 | ||
|
fc8dc9bfde | ||
|
c13863ad0f |
@@ -3,6 +3,7 @@
|
||||
## Changelog
|
||||
* 13-07-2019: Initial draft
|
||||
* 14-08-2019: Address cwgoes comments
|
||||
* 22-08-2019: Second version
|
||||
|
||||
## Context
|
||||
|
||||
@@ -53,22 +54,25 @@ network or when a light client that has been offline for longer than the
|
||||
unbonding period connects to the network. Specifically, the node needs to
|
||||
initialize the following structure before syncing from user input:
|
||||
|
||||
```
|
||||
```go
|
||||
type TrustOptions struct {
|
||||
// Required: only trust commits up to this old.
|
||||
// Should be equal to the unbonding period minus some delta for evidence reporting.
|
||||
TrustPeriod time.Duration `json:"trust-period"`
|
||||
// Required: only trust commits up to this old.
|
||||
// Should be equal to the unbonding period minus some delta for evidence reporting.
|
||||
TrustPeriod time.Duration `json:"trust-period"`
|
||||
|
||||
// Option 1: TrustHeight and TrustHash can both be provided
|
||||
// to force the trusting of a particular height and hash.
|
||||
// If the latest trusted height/hash is more recent, then this option is
|
||||
// ignored.
|
||||
TrustHeight int64 `json:"trust-height"`
|
||||
TrustHash []byte `json:"trust-hash"`
|
||||
// Required: validator whom we've got the TrustHeight/Hash from
|
||||
ValidatorAddress types.Address `json:"validator-address"`
|
||||
|
||||
// Option 2: Callback can be set to implement a confirmation
|
||||
// step if the trust store is uninitialized, or expired.
|
||||
Callback func(height int64, hash []byte) error
|
||||
// Option 1: TrustHeight and TrustHash can both be provided
|
||||
// to force the trusting of a particular height and hash.
|
||||
// If the latest trusted height/hash is more recent, then this option is
|
||||
// ignored.
|
||||
TrustHeight int64 `json:"trust-height"`
|
||||
TrustHash []byte `json:"trust-hash"`
|
||||
|
||||
// Option 2: Callback can be set to implement a confirmation
|
||||
// step if the trust store is uninitialized, or expired.
|
||||
Callback func(height int64, hash []byte) error
|
||||
}
|
||||
```
|
||||
|
||||
@@ -121,6 +125,182 @@ network usage.
|
||||
Check out the formal specification
|
||||
[here](https://github.com/tendermint/tendermint/blob/master/docs/spec/consensus/light-client.md).
|
||||
|
||||
### Implementation
|
||||
|
||||
There are two primary modes of usage right now:
|
||||
|
||||
1) Trusted RPC proxy (wrapping multiple RPC clients + verification)
|
||||
2) Part of the IBC light client (only verification bit, no RPC) [spec](https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics)
|
||||
|
||||
First, we'll need something, which will provide us secure headers & validator sets.
|
||||
|
||||
```go
|
||||
type Provider interface {
|
||||
// 0 - latest
|
||||
GetFullCommit(height int64) (FullCommit, error)
|
||||
}
|
||||
```
|
||||
|
||||
In case of the proxy it will be a `http` provider (wrapping RPC client). For
|
||||
IBC, it will be a `ibc` provider, receiving information from IBC transactions.
|
||||
|
||||
Once we have the information, we need to verify it.
|
||||
|
||||
```go
|
||||
type mode int
|
||||
|
||||
const (
|
||||
sequential mode = iota
|
||||
bisecting
|
||||
)
|
||||
|
||||
// default mode - DefaultBisectingVerification
|
||||
type Verifier struct {
|
||||
chainID string
|
||||
options TrustOptions
|
||||
lastVerifiedHeight int64
|
||||
logger log.Logger
|
||||
|
||||
mode mode
|
||||
trustLevel float
|
||||
|
||||
// Source of new FullCommit(s).
|
||||
source Provider
|
||||
|
||||
// Alternative sources for checking the primary for misbehavior by comparing data.
|
||||
// If the primary misbehaves, we report the evidence to them.
|
||||
verifiers []ProviderAndEvidenceReporter
|
||||
|
||||
// Where trusted FullCommit(s) are stored.
|
||||
trusted PersistentProvider
|
||||
}
|
||||
```
|
||||
|
||||
Since providers themselves don't know when they have received a new header (or
|
||||
may choose to do so upon a request), we must add a new function to `Verifier` -
|
||||
`Verify(height int64) error` (0 - latest). It will try to fetch a new header &
|
||||
validator set and verify it. nop if already verified.
|
||||
|
||||
**Sequential vs bisecting verifier**
|
||||
|
||||
Verifier should use bisection by default, but provide options to choose a
|
||||
different mode OR tweak bisection.
|
||||
|
||||
```go
|
||||
func SequentialVerification() Option {
|
||||
return func(v *Verifier) {
|
||||
v.mode = sequential
|
||||
}
|
||||
}
|
||||
|
||||
// trustLevel - maximum change between two not consequitive headers in terms of
|
||||
// validators & their respective voting power, required to trust a new header
|
||||
// (default: 1/3).
|
||||
func BisectingVerification(trustLevel float) Option {
|
||||
if trustLevel > 1 || trustLevel < 1/3 {
|
||||
panic(fmt.Sprintf("trustLevel must be within [1/3, 1], given %v", trustLevel))
|
||||
}
|
||||
|
||||
return func(v *Verifier) {
|
||||
v.mode = bisecting
|
||||
v.trustLevel = trustLevel
|
||||
}
|
||||
}
|
||||
|
||||
var DefaultBisectingVerification = func() Option {
|
||||
return BisectingVerification(1/3)
|
||||
}
|
||||
```
|
||||
|
||||
Once we verified the header, we will need to store it somewhere.
|
||||
|
||||
```
|
||||
type PersistentProvider interface {
|
||||
Provider
|
||||
|
||||
SaveFullCommit(fc FullCommit) error
|
||||
}
|
||||
```
|
||||
|
||||
In case of the proxy it will be a `db` provider (levelDB + in-memory cache in
|
||||
front). For IBC, it will be a `keeper` provider.
|
||||
|
||||
**Minimal test for (1)**
|
||||
|
||||
```go
|
||||
c, err := lite.NewClient(
|
||||
chainID,
|
||||
lite.TrustOptions{TrustPeriod: 336 * time.Hour},
|
||||
rpcclient.NewHTTP(remote1, "/websocket"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
commit, err := c.Commit()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, chainID, commit.ChainID)
|
||||
```
|
||||
|
||||
`lite.Client` here is a `struct`, which uses `Verifier` and exposes
|
||||
`rpcclient.Client` API.
|
||||
|
||||
```go
|
||||
type Client struct {
|
||||
verifier *Verifier
|
||||
client rpcclient.Client
|
||||
}
|
||||
|
||||
var rpcclient.Client = (*Client)(nil)
|
||||
```
|
||||
|
||||
**Minimal test for (2)**
|
||||
|
||||
```go
|
||||
c, err := lite.NewVerifier(
|
||||
chainID,
|
||||
lite.TrustOptions{TrustPeriod: 24 * time.Hour},
|
||||
ibc.New(chainID),
|
||||
Trusted(ibcKeeper{}),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = c.Verify(height)
|
||||
require.NoError(t, err)
|
||||
```
|
||||
|
||||
**Evidence Handling and Reporting**
|
||||
|
||||
light client should also be able to submit evidence of malfeasance and handle
|
||||
evidence coming from a full node or another source.
|
||||
|
||||
We'll need to add evidence to `FullCommit`.
|
||||
|
||||
```go
|
||||
type FullCommit struct {
|
||||
SignedHeader types.SignedHeader `json:"signed_header"`
|
||||
Validators *types.ValidatorSet `json:"validator_set"`
|
||||
NextValidators *types.ValidatorSet `json:"next_validator_set"`
|
||||
Evidence types.EvidenceList `json:"evidence"`
|
||||
}
|
||||
```
|
||||
|
||||
When/if evidence is received, client should check it and disconnect from the
|
||||
node if `evidence.Address == TrustOptions.ValidatorAddress`. It's unwise to
|
||||
think that a node will send an evidence of its misbehavior. That's why we
|
||||
should also check `verifiers` sources in the background.
|
||||
|
||||
_Evidence handling can be implemented in the second version._
|
||||
|
||||
Submitting an evidence comes down to calling `ReportEvidence(ev types.Evidence)
|
||||
error` on the `verifiers` sources.
|
||||
|
||||
```go
|
||||
type ProviderAndEvidenceReporter interface {
|
||||
Provider
|
||||
|
||||
ReportEvidence(ev types.Evidence) error
|
||||
}
|
||||
```
|
||||
|
||||
## Status
|
||||
|
||||
Accepted.
|
||||
|
Reference in New Issue
Block a user