github.com/pokt-network/tendermint@v0.32.11-0.20230426215212-59310158d3e9/docs/architecture/adr-046-light-client-implementation.md (about) 1 # ADR 046: Lite Client Implementation 2 3 ## Changelog 4 * 13-02-2020: Initial draft 5 * 26-02-2020: Cross-checking the first header 6 * 28-02-2020: Bisection algorithm details 7 * 31-03-2020: Verify signature got changed 8 9 ## Context 10 11 A `Client` struct represents a light client, connected to a single blockchain. 12 13 The user has an option to verify headers using `VerifyHeader` or 14 `VerifyHeaderAtHeight` or `Update` methods. The latter method downloads the 15 latest header from primary and compares it with the currently trusted one. 16 17 ```go 18 type Client interface { 19 // verify new headers 20 VerifyHeaderAtHeight(height int64, now time.Time) (*types.SignedHeader, error) 21 VerifyHeader(newHeader *types.SignedHeader, newVals *types.ValidatorSet, now time.Time) error 22 Update(now time.Time) (*types.SignedHeader, error) 23 24 // get trusted headers & validators 25 TrustedHeader(height int64) (*types.SignedHeader, error) 26 TrustedValidatorSet(height int64) (valSet *types.ValidatorSet, heightUsed int64, err error) 27 LastTrustedHeight() (int64, error) 28 FirstTrustedHeight() (int64, error) 29 30 // query configuration options 31 ChainID() string 32 Primary() provider.Provider 33 Witnesses() []provider.Provider 34 35 Cleanup() error 36 } 37 ``` 38 39 A new light client can either be created from scratch (via `NewClient`) or 40 using the trusted store (via `NewClientFromTrustedStore`). When there's some 41 data in the trusted store and `NewClient` is called, the light client will a) 42 check if stored header is more recent b) optionally ask the user whenever it 43 should rollback (no confirmation required by default). 44 45 ```go 46 func NewClient( 47 chainID string, 48 trustOptions TrustOptions, 49 primary provider.Provider, 50 witnesses []provider.Provider, 51 trustedStore store.Store, 52 options ...Option) (*Client, error) { 53 ``` 54 55 `witnesses` as argument (as opposite to `Option`) is an intentional choice, 56 made to increase security by default. At least one witness is required, 57 although, right now, the light client does not check that primary != witness. 58 When cross-checking a new header with witnesses, minimum number of witnesses 59 required to respond: 1. Note the very first header (`TrustOptions.Hash`) is 60 also cross-checked with witnesses for additional security. 61 62 Due to bisection algorithm nature, some headers might be skipped. If the light 63 client does not have a header for height `X` and `VerifyHeaderAtHeight(X)` or 64 `VerifyHeader(H#X)` methods are called, these will perform either a) backwards 65 verification from the latest header back to the header at height `X` or b) 66 bisection verification from the first stored header to the header at height `X`. 67 68 `TrustedHeader`, `TrustedValidatorSet` only communicate with the trusted store. 69 If some header is not there, an error will be returned indicating that 70 verification is required. 71 72 ```go 73 type Provider interface { 74 ChainID() string 75 76 SignedHeader(height int64) (*types.SignedHeader, error) 77 ValidatorSet(height int64) (*types.ValidatorSet, error) 78 } 79 ``` 80 81 Provider is a full node usually, but can be another light client. The above 82 interface is thin and can accommodate many implementations. 83 84 If provider (primary or witness) becomes unavailable for a prolonged period of 85 time, it will be removed to ensure smooth operation. 86 87 Both `Client` and providers expose chain ID to track if there are on the same 88 chain. Note, when chain upgrades or intentionally forks, chain ID changes. 89 90 The light client stores headers & validators in the trusted store: 91 92 ```go 93 type Store interface { 94 SaveSignedHeaderAndValidatorSet(sh *types.SignedHeader, valSet *types.ValidatorSet) error 95 DeleteSignedHeaderAndValidatorSet(height int64) error 96 97 SignedHeader(height int64) (*types.SignedHeader, error) 98 ValidatorSet(height int64) (*types.ValidatorSet, error) 99 100 LastSignedHeaderHeight() (int64, error) 101 FirstSignedHeaderHeight() (int64, error) 102 103 SignedHeaderAfter(height int64) (*types.SignedHeader, error) 104 105 Prune(size uint16) error 106 107 Size() uint16 108 } 109 ``` 110 111 At the moment, the only implementation is the `db` store (wrapper around the KV 112 database, used in Tendermint). In the future, remote adapters are possible 113 (e.g. `Postgresql`). 114 115 ```go 116 func Verify( 117 chainID string, 118 trustedHeader *types.SignedHeader, // height=X 119 trustedVals *types.ValidatorSet, // height=X or height=X+1 120 untrustedHeader *types.SignedHeader, // height=Y 121 untrustedVals *types.ValidatorSet, // height=Y 122 trustingPeriod time.Duration, 123 now time.Time, 124 maxClockDrift time.Duration, 125 trustLevel tmmath.Fraction) error { 126 ``` 127 128 `Verify` pure function is exposed for a header verification. It handles both 129 cases of adjacent and non-adjacent headers. In the former case, it compares the 130 hashes directly (2/3+ signed transition). Otherwise, it verifies 1/3+ 131 (`trustLevel`) of trusted validators are still present in new validators. 132 133 While `Verify` function is certainly handy, `VerifyAdjacent` and 134 `VerifyNonAdjacent` should be used most often to avoid logic errors. 135 136 ### Bisection algorithm details 137 138 Non-recursive bisection algorithm was implemented despite the spec containing 139 the recursive version. There are two major reasons: 140 141 1) Constant memory consumption => no risk of getting OOM (Out-Of-Memory) exceptions; 142 2) Faster finality (see Fig. 1). 143 144 _Fig. 1: Differences between recursive and non-recursive bisections_ 145 146 ![Fig. 1](./img/adr-046-fig1.png) 147 148 Specification of the non-recursive bisection can be found 149 [here](https://github.com/tendermint/spec/blob/zm_non-recursive-verification/spec/consensus/light-client/non-recursive-verification.md). 150 151 ## Status 152 153 Accepted. 154 155 ## Consequences 156 157 ### Positive 158 159 * single `Client` struct, which is easy to use 160 * flexible interfaces for header providers and trusted storage 161 162 ### Negative 163 164 * `Verify` needs to be aligned with the current spec 165 166 ### Neutral 167 168 * `Verify` function might be misused (called with non-adjacent headers in 169 incorrectly implemented sequential verification)