You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1162 lines
42 KiB

  1. # Light Client Verification
  2. The light client implements a read operation of a
  3. [header][TMBC-HEADER-link] from the [blockchain][TMBC-SEQ-link], by
  4. communicating with full nodes. As some full nodes may be faulty, this
  5. functionality must be implemented in a fault-tolerant way.
  6. In the Tendermint blockchain, the validator set may change with every
  7. new block. The staking and unbonding mechanism induces a [security
  8. model][TMBC-FM-2THIRDS-link]: starting at time *Time* of the
  9. [header][TMBC-HEADER-link],
  10. more than two-thirds of the next validators of a new block are correct
  11. for the duration of *TrustedPeriod*. The fault-tolerant read
  12. operation is designed for this security model.
  13. The challenge addressed here is that the light client might have a
  14. block of height *h1* and needs to read the block of height *h2*
  15. greater than *h1*. Checking all headers of heights from *h1* to *h2*
  16. might be too costly (e.g., in terms of energy for mobile devices).
  17. This specification tries to reduce the number of intermediate blocks
  18. that need to be checked, by exploiting the guarantees provided by the
  19. [security model][TMBC-FM-2THIRDS-link].
  20. # Status
  21. This document is thoroughly reviewed, and the protocol has been
  22. formalized in TLA+ and model checked.
  23. ## Issues that need to be addressed
  24. As it is part of the larger light node, its data structures and
  25. functions interact with the fork dectection functionality of the light
  26. client. As a result of the work on
  27. [Pull Request 479](https://github.com/informalsystems/tendermint-rs/pull/479) we
  28. established the need for an update in the data structures in [Issue 499](https://github.com/informalsystems/tendermint-rs/issues/499). This
  29. will not change the verification logic, but it will record information
  30. about verification that can be used in fork detection (in particular
  31. in computing more efficiently the proof of fork).
  32. # Outline
  33. - [Part I](#part-i---tendermint-blockchain): Introduction of
  34. relevant terms of the Tendermint
  35. blockchain.
  36. - [Part II](#part-ii---sequential-definition-of-the-verification-problem): Introduction
  37. of the problem addressed by the Lightclient Verification protocol.
  38. - [Verification Informal Problem
  39. statement](#Verification-Informal-Problem-statement): For the general
  40. audience, that is, engineers who want to get an overview over what
  41. the component is doing from a bird's eye view.
  42. - [Sequential Problem statement](#Sequential-Problem-statement):
  43. Provides a mathematical definition of the problem statement in
  44. its sequential form, that is, ignoring the distributed aspect of
  45. the implementation of the blockchain.
  46. - [Part III](#part-iii---light-client-as-distributed-system): Distributed
  47. aspects of the light client, system assumptions and temporal
  48. logic specifications.
  49. - [Incentives](#incentives): how faulty full nodes may benefit from
  50. misbehaving and how correct full nodes benefit from cooperating.
  51. - [Computational Model](#Computational-Model):
  52. timing and correctness assumptions.
  53. - [Distributed Problem Statement](#Distributed-Problem-Statement):
  54. temporal properties that formalize safety and liveness
  55. properties in the distributed setting.
  56. - [Part IV](#part-iv---light-client-verification-protocol):
  57. Specification of the protocols.
  58. - [Definitions](#Definitions): Describes inputs, outputs,
  59. variables used by the protocol, auxiliary functions
  60. - [Core Verification](#core-verification): gives an outline of the solution,
  61. and details of the functions used (with preconditions,
  62. postconditions, error conditions).
  63. - [Liveness Scenarios](#liveness-scenarios): when the light
  64. client makes progress depends heavily on the changes in the
  65. validator sets of the blockchain. We discuss some typical scenarios.
  66. - [Part V](#part-v---supporting-the-ibc-relayer): The above parts
  67. focus on a common case where the last verified block has height *h1*
  68. and the
  69. requested height *h2* satisfies *h2 > h1*. For IBC, there are
  70. scenarios where this might not be the case. In this part, we provide
  71. some preliminaries for supporting this. As not all details of the
  72. IBC requirements are clear by now, we do not provide a complete
  73. specification at this point. We mark with "Open Question" points
  74. that need to be addressed in order to finalize this specification.
  75. It should be noted that the technically
  76. most challenging case is the one specified in Part IV.
  77. In this document we quite extensively use tags in order to be able to
  78. reference assumptions, invariants, etc. in future communication. In
  79. these tags we frequently use the following short forms:
  80. - TMBC: Tendermint blockchain
  81. - SEQ: for sequential specifications
  82. - LCV: Lightclient Verification
  83. - LIVE: liveness
  84. - SAFE: safety
  85. - FUNC: function
  86. - INV: invariant
  87. - A: assumption
  88. # Part I - Tendermint Blockchain
  89. ## Header Fields necessary for the Light Client
  90. #### **[TMBC-HEADER.1]**
  91. A set of blockchain transactions is stored in a data structure called
  92. *block*, which contains a field called *header*. (The data structure
  93. *block* is defined [here][block]). As the header contains hashes to
  94. the relevant fields of the block, for the purpose of this
  95. specification, we will assume that the blockchain is a list of
  96. headers, rather than a list of blocks.
  97. #### **[TMBC-HASH-UNIQUENESS.1]**
  98. We assume that every hash in the header identifies the data it hashes.
  99. Therefore, in this specification, we do not distinguish between hashes and the
  100. data they represent.
  101. #### **[TMBC-HEADER-FIELDS.1]**
  102. A header contains the following fields:
  103. - `Height`: non-negative integer
  104. - `Time`: time (integer)
  105. - `LastBlockID`: Hashvalue
  106. - `LastCommit` DomainCommit
  107. - `Validators`: DomainVal
  108. - `NextValidators`: DomainVal
  109. - `Data`: DomainTX
  110. - `AppState`: DomainApp
  111. - `LastResults`: DomainRes
  112. #### **[TMBC-SEQ.1]**
  113. The Tendermint blockchain is a list *chain* of headers.
  114. #### **[TMBC-VALIDATOR-PAIR.1]**
  115. Given a full node, a
  116. *validator pair* is a pair *(peerID, voting_power)*, where
  117. - *peerID* is the PeerID (public key) of a full node,
  118. - *voting_power* is an integer (representing the full node's
  119. voting power in a certain consensus instance).
  120. > In the Golang implementation the data type for *validator
  121. pair* is called `Validator`
  122. #### **[TMBC-VALIDATOR-SET.1]**
  123. A *validator set* is a set of validator pairs. For a validator set
  124. *vs*, we write *TotalVotingPower(vs)* for the sum of the voting powers
  125. of its validator pairs.
  126. #### **[TMBC-VOTE.1]**
  127. A *vote* contains a `prevote` or `precommit` message sent and signed by
  128. a validator node during the execution of [consensus][arXiv]. Each
  129. message contains the following fields
  130. - `Type`: prevote or precommit
  131. - `Height`: positive integer
  132. - `Round` a positive integer
  133. - `BlockID` a Hashvalue of a block (not necessarily a block of the chain)
  134. #### **[TMBC-COMMIT.1]**
  135. A commit is a set of `precommit` message.
  136. ## Tendermint Failure Model
  137. #### **[TMBC-AUTH-BYZ.1]**
  138. We assume the authenticated Byzantine fault model in which no node (faulty or
  139. correct) may break digital signatures, but otherwise, no additional
  140. assumption is made about the internal behavior of faulty
  141. nodes. That is, faulty nodes are only limited in that they cannot forge
  142. messages.
  143. #### **[TMBC-TIME-PARAMS.1]**
  144. A Tendermint blockchain has the following configuration parameters:
  145. - *unbondingPeriod*: a time duration.
  146. - *trustingPeriod*: a time duration smaller than *unbondingPeriod*.
  147. #### **[TMBC-CORRECT.1]**
  148. We define a predicate *correctUntil(n, t)*, where *n* is a node and *t* is a
  149. time point.
  150. The predicate *correctUntil(n, t)* is true if and only if the node *n*
  151. follows all the protocols (at least) until time *t*.
  152. #### **[TMBC-FM-2THIRDS.1]**
  153. If a block *h* is in the chain,
  154. then there exists a subset *CorrV*
  155. of *h.NextValidators*, such that:
  156. - *TotalVotingPower(CorrV) > 2/3
  157. TotalVotingPower(h.NextValidators)*; cf. [TMBC-VALIDATOR-SET.1]
  158. - For every validator pair *(n,p)* in *CorrV*, it holds *correctUntil(n,
  159. h.Time + trustingPeriod)*; cf. [TMBC-CORRECT.1]
  160. > The definition of correct
  161. > [**[TMBC-CORRECT.1]**][TMBC-CORRECT-link] refers to realtime, while it
  162. > is used here with *Time* and *trustingPeriod*, which are "hardware
  163. > times". We do not make a distinction here.
  164. #### **[TMBC-CORR-FULL.1]**
  165. Every correct full node locally stores a prefix of the
  166. current list of headers from [**[TMBC-SEQ.1]**][TMBC-SEQ-link].
  167. ## What the Light Client Checks
  168. > From [TMBC-FM-2THIRDS.1] we directly derive the following observation:
  169. #### **[TMBC-VAL-CONTAINS-CORR.1]**
  170. Given a (trusted) block *tb* of the blockchain, a given set of full nodes
  171. *N* contains a correct node at a real-time *t*, if
  172. - *t - trustingPeriod < tb.Time < t*
  173. - the voting power in tb.NextValidators of nodes in *N* is more
  174. than 1/3 of *TotalVotingPower(tb.NextValidators)*
  175. > The following describes how a commit for a given block *b* must look
  176. > like.
  177. #### **[TMBC-SOUND-DISTR-POSS-COMMIT.1]**
  178. For a block *b*, each element *pc* of *PossibleCommit(b)* satisfies:
  179. - *pc* contains only votes (cf. [TMBC-VOTE.1])
  180. by validators from *b.Validators*
  181. - the sum of the voting powers in *pc* is greater than 2/3
  182. *TotalVotingPower(b.Validators)*
  183. - and there is an *r* such that each vote *v* in *pc* satisfies
  184. - v.Type = precommit
  185. - v.Height = b.Height
  186. - v.Round = r
  187. - v.blockID = hash(b)
  188. > The following property comes from the validity of the [consensus][arXiv]: A
  189. > correct validator node only sends `prevote` or `precommit`, if
  190. > `BlockID` of the new (to-be-decided) block is equal to the hash of
  191. > the last block.
  192. #### **[TMBC-VAL-COMMIT.1]**
  193. If for a block *b*, a commit *c*
  194. - contains at least one validator pair *(v,p)* such that *v* is a
  195. **correct** validator node, and
  196. - is contained in *PossibleCommit(b)*
  197. then the block *b* is on the blockchain.
  198. ## Context of this document
  199. In this document we specify the light client verification component,
  200. called *Core Verification*. The *Core Verification* communicates with
  201. a full node. As full nodes may be faulty, it cannot trust the
  202. received information, but the light client has to check whether the
  203. header it receives coincides with the one generated by Tendermint
  204. consensus.
  205. The two
  206. properties [[TMBC-VAL-CONTAINS-CORR.1]][TMBC-VAL-CONTAINS-CORR-link] and
  207. [[TMBC-VAL-COMMIT]][TMBC-VAL-COMMIT-link] formalize the checks done
  208. by this specification:
  209. Given a trusted block *tb* and an untrusted block *ub* with a commit *cub*,
  210. one has to check that *cub* is in *PossibleCommit(ub)*, and that *cub*
  211. contains a correct node using *tb*.
  212. # Part II - Sequential Definition of the Verification Problem
  213. ## Verification Informal Problem statement
  214. Given a height *targetHeight* as an input, the *Verifier* eventually
  215. stores a header *h* of height *targetHeight* locally. This header *h*
  216. is generated by the Tendermint [blockchain][block]. In
  217. particular, a header that was not generated by the blockchain should
  218. never be stored.
  219. ## Sequential Problem statement
  220. #### **[LCV-SEQ-LIVE.1]**
  221. The *Verifier* gets as input a height *targetHeight*, and eventually stores the
  222. header of height *targetHeight* of the blockchain.
  223. #### **[LCV-SEQ-SAFE.1]**
  224. The *Verifier* never stores a header which is not in the blockchain.
  225. # Part III - Light Client as Distributed System
  226. ## Incentives
  227. Faulty full nodes may benefit from lying to the light client, by making the
  228. light client accept a block that deviates (e.g., contains additional
  229. transactions) from the one generated by Tendermint consensus.
  230. Users using the light client might be harmed by accepting a forged header.
  231. The [fork detector][fork-detector] of the light client may help the
  232. correct full nodes to understand whether their header is a good one.
  233. Hence, in combination with the light client detector, the correct full
  234. nodes have the incentive to respond. We can thus base liveness
  235. arguments on the assumption that correct full nodes reliably talk to
  236. the light client.
  237. ## Computational Model
  238. #### **[LCV-A-PEER.1]**
  239. The verifier communicates with a full node called *primary*. No assumption is made about the full node (it may be correct or faulty).
  240. #### **[LCV-A-COMM.1]**
  241. Communication between the light client and a correct full node is
  242. reliable and bounded in time. Reliable communication means that
  243. messages are not lost, not duplicated, and eventually delivered. There
  244. is a (known) end-to-end delay *Delta*, such that if a message is sent
  245. at time *t* then it is received and processes by time *t + Delta*.
  246. This implies that we need a timeout of at least *2 Delta* for remote
  247. procedure calls to ensure that the response of a correct peer arrives
  248. before the timeout expires.
  249. #### **[LCV-A-TFM.1]**
  250. The Tendermint blockchain satisfies the Tendermint failure model [**[TMBC-FM-2THIRDS.1]**][TMBC-FM-2THIRDS-link].
  251. #### **[LCV-A-VAL.1]**
  252. The system satisfies [**[TMBC-AUTH-BYZ.1]**][TMBC-Auth-Byz-link] and
  253. [**[TMBC-FM-2THIRDS.1]**][TMBC-FM-2THIRDS-link]. Thus, there is a
  254. blockchain that satisfies the soundness requirements (that is, the
  255. validation rules in [[block]]).
  256. ## Distributed Problem Statement
  257. ### Two Kinds of Termination
  258. We do not assume that *primary* is correct. Under this assumption no
  259. protocol can guarantee the combination of the sequential
  260. properties. Thus, in the (unreliable) distributed setting, we consider
  261. two kinds of termination (successful and failure) and we will specify
  262. below under what (favorable) conditions *Core Verification* ensures to
  263. terminate successfully, and satisfy the requirements of the sequential
  264. problem statement:
  265. #### **[LCV-DIST-TERM.1]**
  266. *Core Verification* either *terminates
  267. successfully* or it *terminates with failure*.
  268. ### Design choices
  269. #### **[LCV-DIST-STORE.1]**
  270. *Core Verification* has a local data structure called *LightStore* that
  271. contains light blocks (that contain a header). For each light block we
  272. record whether it is verified.
  273. #### **[LCV-DIST-PRIMARY.1]**
  274. *Core Verification* has a local variable *primary* that contains the PeerID of a full node.
  275. #### **[LCV-DIST-INIT.1]**
  276. *LightStore* is initialized with a header *trustedHeader* that was correctly
  277. generated by the Tendermint consensus. We say *trustedHeader* is verified.
  278. ### Temporal Properties
  279. #### **[LCV-DIST-SAFE.1]**
  280. It is always the case that every verified header in *LightStore* was
  281. generated by an instance of Tendermint consensus.
  282. #### **[LCV-DIST-LIVE.1]**
  283. From time to time, a new instance of *Core Verification* is called with a
  284. height *targetHeight* greater than the height of any header in *LightStore*.
  285. Each instance must eventually terminate.
  286. - If
  287. - the *primary* is correct (and locally has the block of
  288. *targetHeight*), and
  289. - *LightStore* always contains a verified header whose age is less than the
  290. trusting period,
  291. then *Core Verification* adds a verified header *hd* with height
  292. *targetHeight* to *LightStore* and it **terminates successfully**
  293. > These definitions imply that if the primary is faulty, a header may or
  294. > may not be added to *LightStore*. In any case,
  295. > [**[LCV-DIST-SAFE.1]**](#lcv-vc-inv) must hold.
  296. > The invariant [**[LCV-DIST-SAFE.1]**](#lcv-dist-safe) and the liveness
  297. > requirement [**[LCV-DIST-LIVE.1]**](#lcv-dist-life)
  298. > allow that verified headers are added to *LightStore* whose
  299. > height was not passed
  300. > to the verifier (e.g., intermediate headers used in bisection; see below).
  301. > Note that for liveness, initially having a *trustedHeader* within
  302. > the *trustinPeriod* is not sufficient. However, as this
  303. > specification will leave some freedom with respect to the strategy
  304. > in which order to download intermediate headers, we do not give a
  305. > more precise liveness specification here. After giving the
  306. > specification of the protocol, we will discuss some liveness
  307. > scenarios [below](#liveness-scenarios).
  308. ### Solving the sequential specification
  309. This specification provides a partial solution to the sequential specification.
  310. The *Verifier* solves the invariant of the sequential part
  311. [**[LCV-DIST-SAFE.1]**](#lcv-vc-inv) => [**[LCV-SEQ-SAFE.1]**](#lcv-seq-inv)
  312. In the case the primary is correct, and there is a recent header in *LightStore*, the verifier satisfies the liveness requirements.
  313. *primary is correct*
  314. ⋀ always ∃ verified header in LightStore. *header.Time* > *now* - *trustingPeriod*
  315. ⋀ [**[LCV-A-Comm.1]**](#lcv-a-comm) ⋀ (
  316. ( [**[TMBC-CorrFull.1]**][TMBC-CorrFull-link] ⋀
  317. [**[LCV-DIST-LIVE.1]**](#lcv-vc-live) )
  318. ⟹ [**[LCV-SEQ-LIVE.1]**](#lcv-seq-live)
  319. )
  320. # Part IV - Light Client Verification Protocol
  321. We provide a specification for Light Client Verification. The local
  322. code for verification is presented by a sequential function
  323. `VerifyToTarget` to highlight the control flow of this functionality.
  324. We note that if a different concurrency model is considered for
  325. an implementation, the sequential flow of the function may be
  326. implemented with mutexes, etc. However, the light client verification
  327. is partitioned into three blocks that can be implemented and tested
  328. independently:
  329. - `FetchLightBlock` is called to download a light block (header) of a
  330. given height from a peer.
  331. - `ValidAndVerified` is a local code that checks the header.
  332. - `Schedule` decides which height to try to verify next. We keep this
  333. underspecified as different implementations (currently in Goland and
  334. Rust) may implement different optimizations here. We just provide
  335. necessary conditions on how the height may evolve.
  336. <!-- > `ValidAndVerified` is the function that is sometimes called "Light -->
  337. <!-- > Client" in the IBC context. -->
  338. ## Definitions
  339. ### Data Types
  340. The core data structure of the protocol is the LightBlock.
  341. #### **[LCV-DATA-LIGHTBLOCK.1]**
  342. ```go
  343. type LightBlock struct {
  344. Header Header
  345. Commit Commit
  346. Validators ValidatorSet
  347. NextValidators ValidatorSet
  348. Provider PeerID
  349. }
  350. ```
  351. #### **[LCV-DATA-LIGHTSTORE.1]**
  352. LightBlocks are stored in a structure which stores all LightBlock from
  353. initialization or received from peers.
  354. ```go
  355. type LightStore struct {
  356. ...
  357. }
  358. ```
  359. Each LightBlock is in one of the following states:
  360. ```go
  361. type VerifiedState int
  362. const (
  363. StateUnverified = iota + 1
  364. StateVerified
  365. StateFailed
  366. StateTrusted
  367. )
  368. ```
  369. > Only the detector module sets a lightBlock state to `StateTrusted`
  370. > and only if it was `StateVerified` before.
  371. The LightStore exposes the following functions to query stored LightBlocks.
  372. ```go
  373. func (ls LightStore) Get(height Height) (LightBlock, bool)
  374. ```
  375. - Expected postcondition
  376. - returns a LightBlock at a given height or false in the second argument if
  377. the LightStore does not contain the specified LightBlock.
  378. ```go
  379. func (ls LightStore) LatestVerified() LightBlock
  380. ```
  381. - Expected postcondition
  382. - returns the highest light block whose state is `StateVerified`
  383. or `StateTrusted`
  384. #### **[LCV-FUNC-UPDATE.1]**
  385. ```go
  386. func (ls LightStore) Update(lightBlock LightBlock, verfiedState VerifiedState)
  387. ```
  388. - Expected postcondition
  389. - The state of the LightBlock is set to *verifiedState*.
  390. > The following function is used only in the detector specification
  391. > listed here for completeness.
  392. ```go
  393. func (ls LightStore) LatestTrusted() LightBlock
  394. ```
  395. - Expected postcondition
  396. - returns the highest light block that has been verified and
  397. checked by the detector.
  398. ### Inputs
  399. - *lightStore*: stores light blocks that have been downloaded and that
  400. passed verification. Initially it contains a light block with
  401. *trustedHeader*.
  402. - *primary*: peerID
  403. - *targetHeight*: the height of the needed header
  404. ### Configuration Parameters
  405. - *trustThreshold*: a float. Can be used if correctness should not be based on more voting power and 1/3.
  406. - *trustingPeriod*: a time duration [**[TMBC-TIME_PARAMS.1]**][TMBC-TIME_PARAMS-link].
  407. - *clockDrift*: a time duration. Correction parameter dealing with only approximately synchronized clocks.
  408. ### Variables
  409. - *nextHeight*: initially *targetHeight*
  410. > *nextHeight* should be thought of the "height of the next header we need
  411. > to download and verify"
  412. ### Assumptions
  413. #### **[LCV-A-INIT.1]**
  414. - *trustedHeader* is from the blockchain
  415. - *targetHeight > LightStore.LatestVerified.Header.Height*
  416. ### Invariants
  417. #### **[LCV-INV-TP.1]**
  418. It is always the case that *LightStore.LatestTrusted.Header.Time > now - trustingPeriod*.
  419. > If the invariant is violated, the light client does not have a
  420. > header it can trust. A trusted header must be obtained externally,
  421. > its trust can only be based on social consensus.
  422. ### Used Remote Functions
  423. We use the functions `commit` and `validators` that are provided
  424. by the [RPC client for Tendermint][RPC].
  425. ```go
  426. func Commit(height int64) (SignedHeader, error)
  427. ```
  428. - Implementation remark
  429. - RPC to full node *n*
  430. - JSON sent:
  431. ```javascript
  432. // POST /commit
  433. {
  434. "jsonrpc": "2.0",
  435. "id": "ccc84631-dfdb-4adc-b88c-5291ea3c2cfb", // UUID v4, unique per request
  436. "method": "commit",
  437. "params": {
  438. "height": 1234
  439. }
  440. }
  441. ```
  442. - Expected precondition
  443. - header of `height` exists on blockchain
  444. - Expected postcondition
  445. - if *n* is correct: Returns the signed header of height `height`
  446. from the blockchain if communication is timely (no timeout)
  447. - if *n* is faulty: Returns a signed header with arbitrary content
  448. - Error condition
  449. - if *n* is correct: precondition violated or timeout
  450. - if *n* is faulty: arbitrary error
  451. ----
  452. ```go
  453. func Validators(height int64) (ValidatorSet, error)
  454. ```
  455. - Implementation remark
  456. - RPC to full node *n*
  457. - JSON sent:
  458. ```javascript
  459. // POST /validators
  460. {
  461. "jsonrpc": "2.0",
  462. "id": "ccc84631-dfdb-4adc-b88c-5291ea3c2cfb", // UUID v4, unique per request
  463. "method": "validators",
  464. "params": {
  465. "height": 1234
  466. }
  467. }
  468. ```
  469. - Expected precondition
  470. - header of `height` exists on blockchain
  471. - Expected postcondition
  472. - if *n* is correct: Returns the validator set of height `height`
  473. from the blockchain if communication is timely (no timeout)
  474. - if *n* is faulty: Returns arbitrary validator set
  475. - Error condition
  476. - if *n* is correct: precondition violated or timeout
  477. - if *n* is faulty: arbitrary error
  478. ----
  479. ### Communicating Function
  480. #### **[LCV-FUNC-FETCH.1]**
  481. ```go
  482. func FetchLightBlock(peer PeerID, height Height) LightBlock
  483. ```
  484. - Implementation remark
  485. - RPC to peer at *PeerID*
  486. - calls `Commit` for *height* and `Validators` for *height* and *height+1*
  487. - Expected precondition
  488. - `height` is less than or equal to height of the peer **[LCV-IO-PRE-HEIGHT.1]**
  489. - Expected postcondition:
  490. - if *node* is correct:
  491. - Returns the LightBlock *lb* of height `height`
  492. that is consistent with the blockchain
  493. - *lb.provider = peer* **[LCV-IO-POST-PROVIDER.1]**
  494. - *lb.Header* is a header consistent with the blockchain
  495. - *lb.Validators* is the validator set of the blockchain at height *nextHeight*
  496. - *lb.NextValidators* is the validator set of the blockchain at height *nextHeight + 1*
  497. - if *node* is faulty: Returns a LightBlock with arbitrary content
  498. [**[TMBC-AUTH-BYZ.1]**][TMBC-Auth-Byz-link]
  499. - Error condition
  500. - if *n* is correct: precondition violated
  501. - if *n* is faulty: arbitrary error
  502. - if *lb.provider != peer*
  503. - times out after 2 Delta (by assumption *n* is faulty)
  504. ----
  505. ## Core Verification
  506. ### Outline
  507. The `VerifyToTarget` is the main function and uses the following functions.
  508. - `FetchLightBlock` is called to download the next light block. It is
  509. the only function that communicates with other nodes
  510. - `ValidAndVerified` checks whether header is valid and checks if a
  511. new lightBlock should be trusted
  512. based on a previously verified lightBlock.
  513. - `Schedule` decides which height to try to verify next
  514. In the following description of `VerifyToTarget` we do not deal with error
  515. handling. If any of the above function returns an error, VerifyToTarget just
  516. passes the error on.
  517. #### **[LCV-FUNC-MAIN.1]**
  518. ```go
  519. func VerifyToTarget(primary PeerID, lightStore LightStore,
  520. targetHeight Height) (LightStore, Result) {
  521. nextHeight := targetHeight
  522. for lightStore.LatestVerified.height < targetHeight {
  523. // Get next LightBlock for verification
  524. current, found := lightStore.Get(nextHeight)
  525. if !found {
  526. current = FetchLightBlock(primary, nextHeight)
  527. lightStore.Update(current, StateUnverified)
  528. }
  529. // Verify
  530. verdict = ValidAndVerified(lightStore.LatestVerified, current)
  531. // Decide whether/how to continue
  532. if verdict == OK {
  533. lightStore.Update(current, StateVerified)
  534. }
  535. else if verdict == CANNOT_VERIFY {
  536. // do nothing
  537. // the light block current passed validation, but the validator
  538. // set is too different to verify it. We keep the state of
  539. // current at StateUnverified. For a later iteration, Schedule
  540. // might decide to try verification of that light block again.
  541. }
  542. else {
  543. // verdict is some error code
  544. lightStore.Update(current, StateFailed)
  545. // possibly remove all LightBlocks from primary
  546. return (lightStore, ResultFailure)
  547. }
  548. nextHeight = Schedule(lightStore, nextHeight, targetHeight)
  549. }
  550. return (lightStore, ResultSuccess)
  551. }
  552. ```
  553. - Expected precondition
  554. - *lightStore* contains a LightBlock within the *trustingPeriod* **[LCV-PRE-TP.1]**
  555. - *targetHeight* is greater than the height of all the LightBlocks in *lightStore*
  556. - Expected postcondition:
  557. - returns *lightStore* that contains a LightBlock that corresponds to a block
  558. of the blockchain of height *targetHeight*
  559. (that is, the LightBlock has been added to *lightStore*) **[LCV-POST-LS.1]**
  560. - Error conditions
  561. - if the precondition is violated
  562. - if `ValidAndVerified` or `FetchLightBlock` report an error
  563. - if [**[LCV-INV-TP.1]**](#LCV-INV-TP.1) is violated
  564. ### Details of the Functions
  565. #### **[LCV-FUNC-VALID.1]**
  566. ```go
  567. func ValidAndVerified(trusted LightBlock, untrusted LightBlock) Result
  568. ```
  569. - Expected precondition:
  570. - *untrusted* is valid, that is, satisfies the soundness [checks][block]
  571. - *untrusted* is **well-formed**, that is,
  572. - *untrusted.Header.Time < now + clockDrift*
  573. - *untrusted.Validators = hash(untrusted.Header.Validators)*
  574. - *untrusted.NextValidators = hash(untrusted.Header.NextValidators)*
  575. - *trusted.Header.Time > now - trustingPeriod*
  576. - *trusted.Commit* is a commit for the header
  577. *trusted.Header*, i.e., it contains
  578. the correct hash of the header, and +2/3 of signatures
  579. - the `Height` and `Time` of `trusted` are smaller than the Height and
  580. `Time` of `untrusted`, respectively
  581. - the *untrusted.Header* is well-formed (passes the tests from
  582. [[block]]), and in particular
  583. - if the untrusted header `unstrusted.Header` is the immediate
  584. successor of `trusted.Header`, then it holds that
  585. - *trusted.Header.NextValidators =
  586. untrusted.Header.Validators*, and
  587. moreover,
  588. - *untrusted.Header.Commit*
  589. - contains signatures by more than two-thirds of the validators
  590. - contains no signature from nodes that are not in *trusted.Header.NextValidators*
  591. - Expected postcondition:
  592. - Returns `OK`:
  593. - if *untrusted* is the immediate successor of *trusted*, or otherwise,
  594. - if the signatures of a set of validators that have more than
  595. *max(1/3,trustThreshold)* of voting power in
  596. *trusted.Nextvalidators* is contained in
  597. *untrusted.Commit* (that is, header passes the tests
  598. [**[TMBC-VAL-CONTAINS-CORR.1]**][TMBC-VAL-CONTAINS-CORR-link]
  599. and [**[TMBC-VAL-COMMIT.1]**][TMBC-VAL-COMMIT-link])
  600. - Returns `CANNOT_VERIFY` if:
  601. - *untrusted* is *not* the immediate successor of
  602. *trusted*
  603. and the *max(1/3,trustThreshold)* threshold is not reached
  604. (that is, if
  605. [**[TMBC-VAL-CONTAINS-CORR.1]**][TMBC-VAL-CONTAINS-CORR-link]
  606. fails and header is does not violate the soundness
  607. checks [[block]]).
  608. - Error condition:
  609. - if precondition violated
  610. ----
  611. #### **[LCV-FUNC-SCHEDULE.1]**
  612. ```go
  613. func Schedule(lightStore, nextHeight, targetHeight) Height
  614. ```
  615. - Implementation remark: If picks the next height to be verified.
  616. We keep the precise choice of the next header under-specified. It is
  617. subject to performance optimizations that do not influence the correctness
  618. - Expected postcondition: **[LCV-SCHEDULE-POST.1]**
  619. Return *H* s.t.
  620. 1. if *lightStore.LatestVerified.Height = nextHeight* and
  621. *lightStore.LatestVerified < targetHeight* then
  622. *nextHeight < H <= targetHeight*
  623. 2. if *lightStore.LatestVerified.Height < nextHeight* and
  624. *lightStore.LatestVerified.Height < targetHeight* then
  625. *lightStore.LatestVerified.Height < H < nextHeight*
  626. 3. if *lightStore.LatestVerified.Height = targetHeight* then
  627. *H = targetHeight*
  628. > Case i. captures the case where the light block at height *nextHeight*
  629. > has been verified, and we can choose a height closer to the *targetHeight*.
  630. > As we get the *lightStore* as parameter, the choice of the next height can
  631. > depend on the *lightStore*, e.g., we can pick a height for which we have
  632. > already downloaded a light block.
  633. > In Case ii. the header of *nextHeight* could not be verified, and we need to pick a smaller height.
  634. > In Case iii. is a special case when we have verified the *targetHeight*.
  635. ### Solving the distributed specification
  636. *trustedStore* is implemented by the light blocks in lightStore that
  637. have the state *StateVerified*.
  638. #### Argument for [**[LCV-DIST-SAFE.1]**](#lcv-dist-safe)
  639. - `ValidAndVerified` implements the soundness checks and the checks
  640. [**[TMBC-VAL-CONTAINS-CORR.1]**][TMBC-VAL-CONTAINS-CORR-link] and
  641. [**[TMBC-VAL-COMMIT.1]**][TMBC-VAL-COMMIT-link] under
  642. the assumption [**[TMBC-FM-2THIRDS.1]**][TMBC-FM-2THIRDS-link]
  643. - Only if `ValidAndVerified` returns with `OK`, the state of a light block is
  644. set to *StateVerified*.
  645. #### Argument for [**[LCV-DIST-LIVE.1]**](#lcv-dist-life)
  646. - If *primary* is correct,
  647. - `FetchLightBlock` will always return a light block consistent
  648. with the blockchain
  649. - `ValidAndVerified` either verifies the header using the trusting
  650. period or falls back to sequential
  651. verification
  652. - If [**[LCV-INV-TP.1]**](#LCV-INV-TP.1) holds, eventually every
  653. header will be verified and core verification **terminates successfully**.
  654. - successful termination depends on the age of *lightStore.LatestVerified*
  655. (for instance, initially on the age of *trustedHeader*) and the
  656. changes of the validator sets on the blockchain.
  657. We will give some examples [below](#liveness-scenarios).
  658. - If *primary* is faulty,
  659. - it either provides headers that pass all the tests, and we
  660. return with the header
  661. - it provides one header that fails a test, core verification
  662. **terminates with failure**.
  663. - it times out and core verification
  664. **terminates with failure**.
  665. ## Liveness Scenarios
  666. The liveness argument above assumes [**[LCV-INV-TP.1]**](#LCV-INV-TP.1)
  667. which requires that there is a header that does not expire before the
  668. target height is reached. Here we discuss scenarios to ensure this.
  669. Let *startHeader* be *LightStore.LatestVerified* when core
  670. verification is called (*trustedHeader*) and *startTime* be the time
  671. core verification is invoked.
  672. In order to ensure liveness, *LightStore* always needs to contain a
  673. verified (or initially trusted) header whose time is within the
  674. trusting period. To ensure this, core verification needs to add new
  675. headers to *LightStore* and verify them, before all headers in
  676. *LightStore* expire.
  677. #### Many changes in validator set
  678. Let's consider `Schedule` implements
  679. bisection, that is, it halves the distance.
  680. Assume the case where the validator set changes completely in each
  681. block. Then the
  682. method in this specification needs to
  683. sequentially verify all headers. That is, for
  684. - *W = log_2 (targetHeight - startHeader.Height)*,
  685. *W* headers need to be downloaded and checked before the
  686. header of height *startHeader.Height + 1* is added to *LightStore*.
  687. - Let *Comp*
  688. be the local computation time needed to check headers and signatures
  689. for one header.
  690. - Then we need in the worst case *Comp + 2 Delta* to download and
  691. check one header.
  692. - Then the first time a verified header could be added to *LightStore* is
  693. startTime + W * (Comp + 2 Delta)
  694. - [TP.1] However, it can only be added if we still have a header in
  695. *LightStore*,
  696. which is not
  697. expired, that is only the case if
  698. - startHeader.Time > startTime + WCG * (Comp + 2 Delta) -
  699. trustingPeriod,
  700. - that is, if core verification is started at
  701. startTime < startHeader.Time + trustingPeriod - WCG * (Comp + 2 Delta)
  702. - one may then do an inductive argument from this point on, depending
  703. on the implementation of `Schedule`. We may have to account for the
  704. headers that are already
  705. downloaded, but they are checked against the new *LightStore.LatestVerified*.
  706. > We observe that
  707. > the worst case time it needs to verify the header of height
  708. > *targetHeight* depends mainly on how frequent the validator set on the
  709. > blockchain changes. That core verification terminates successfully
  710. > crucially depends on the check [TP.1], that is, that the headers in
  711. > *LightStore* do not expire in the time needed to download more
  712. > headers, which depends on the creation time of the headers in
  713. > *LightStore*. That is, termination of core verification is highly
  714. > depending on the data stored in the blockchain.
  715. > The current light client core verification protocol exploits that, in
  716. > practice, changes in the validator set are rare. For instance,
  717. > consider the following scenario.
  718. #### No change in validator set
  719. If on the blockchain the validator set of the block at height
  720. *targetHeight* is equal to *startHeader.NextValidators*:
  721. - there is one round trip in `FetchLightBlock` to download the light
  722. block
  723. of height
  724. *targetHeight*, and *Comp* to check it.
  725. - as the validator sets are equal, `Verify` returns `OK`, if
  726. *startHeader.Time > now - trustingPeriod*.
  727. - that is, if *startTime < startHeader.Header.Time + trustingPeriod -
  728. 2 Delta - Comp*, then core verification terminates successfully
  729. # Part V - Supporting the IBC Relayer
  730. The above specification focuses on the most common case, which also
  731. constitutes the most challenging task: using the Tendermint [security
  732. model][TMBC-FM-2THIRDS-link] to verify light blocks without
  733. downloading all intermediate blocks. To focus on this challenge, above
  734. we have restricted ourselves to the case where *targetHeight* is
  735. greater than the height of any trusted header. This simplified
  736. presentation of the algorithm as initially
  737. `lightStore.LatestVerified()` is less than *targetHeight*, and in the
  738. process of verification `lightStore.LatestVerified()` increases until
  739. *targetHeight* is reached.
  740. For [IBC][ibc-rs] it might be that some "older" header is
  741. needed, that is, *targetHeight < lightStore.LatestVerified()*. In this section we present a preliminary design, and we mark some
  742. remaining open questions.
  743. If *targetHeight < lightStore.LatestVerified()* our design separates
  744. the following cases:
  745. - A previous instance of `VerifyToTarget` has already downloaded the
  746. light block of *targetHeight*. There are two cases
  747. - the light block has been verified
  748. - the light block has not been verified yet
  749. - No light block of *targetHeight* had been downloaded before. There
  750. are two cases:
  751. - there exists a verified light block of height less than *targetHeight*
  752. - otherwise. In this case we need to do "backwards verification"
  753. using the hash of the previous block in the `LastBlockID` field
  754. of a header.
  755. **Open Question:** what are the security assumptions for backward
  756. verification. Should we check that the light block we verify from
  757. (and/or the checked light block) is within the trusting period?
  758. The design just presents the above case
  759. distinction as a function, and defines some auxiliary functions in the
  760. same way the protocol was presented in
  761. [Part IV](#part-iv---light-client-verification-protocol).
  762. ```go
  763. func (ls LightStore) LatestPrevious(height Height) (LightBlock, bool)
  764. ```
  765. - Expected postcondition
  766. - returns a light block *lb* that satisfies:
  767. - *lb* is in lightStore
  768. - *lb* is verified and not expired
  769. - *lb.Header.Height < height*
  770. - for all *b* in lightStore s.t. *b* is verified and not expired it
  771. holds *lb.Header.Height >= b.Header.Height*
  772. - *false* in the second argument if
  773. the LightStore does not contain such an *lb*.
  774. ```go
  775. func (ls LightStore) MinVerified() (LightBlock, bool)
  776. ```
  777. - Expected postcondition
  778. - returns a light block *lb* that satisfies:
  779. - *lb* is in lightStore
  780. - *lb* is verified **Open Question:** replace by trusted?
  781. - *lb.Header.Height* is minimal in the lightStore
  782. - **Open Question:** according to this, it might be expired (outside the
  783. trusting period). This approach appears safe. Are there reasons we
  784. should not do that?
  785. - *false* in the second argument if
  786. the LightStore does not contain such an *lb*.
  787. If a height that is smaller than the smallest height in the lightstore
  788. is required, we check the hashes backwards. This is done with the
  789. following function:
  790. #### **[LCV-FUNC-BACKWARDS.1]**
  791. ```go
  792. func Backwards (primary PeerID, lightStore LightStore, targetHeight Height)
  793. (LightStore, Result) {
  794. lb,res = lightStore.MinVerified()
  795. if res = false {
  796. return (lightStore, ResultFailure)
  797. }
  798. latest := lb.Header
  799. for i := lb.Header.height - 1; i >= targetHeight; i-- {
  800. // here we download height-by-height. We might first download all
  801. // headers down to targetHeight and then check them.
  802. current := FetchLightBlock(primary,i)
  803. if (hash(current) != latest.Header.LastBlockId) {
  804. return (lightStore, ResultFailure)
  805. }
  806. else {
  807. lightStore.Update(current, StateVerified)
  808. // **Open Question:** Do we need a new state type for
  809. // backwards verified light blocks?
  810. }
  811. latest = current
  812. }
  813. return (lightStore, ResultSuccess)
  814. }
  815. ```
  816. The following function just decided based on the required height which
  817. method should be used.
  818. #### **[LCV-FUNC-IBCMAIN.1]**
  819. ```go
  820. func Main (primary PeerID, lightStore LightStore, targetHeight Height)
  821. (LightStore, Result) {
  822. b1, r1 = lightStore.Get(targetHeight)
  823. if r1 = true and b1.State = StateVerified {
  824. // block already there
  825. return (lightStore, ResultSuccess)
  826. }
  827. if targetHeight > lightStore.LatestVerified.height {
  828. // case of Part IV
  829. return VerifyToTarget(primary, lightStore, targetHeight)
  830. }
  831. else {
  832. b2, r2 = lightStore.LatestPrevious(targetHeight);
  833. if r2 = true {
  834. // make auxiliary lightStore auxLS to call VerifyToTarget.
  835. // VerifyToTarget uses LatestVerified of the given lightStore
  836. // For that we need:
  837. // auxLS.LatestVerified = lightStore.LatestPrevious(targetHeight)
  838. auxLS.Init;
  839. auxLS.Update(b2,StateVerified);
  840. if r1 = true {
  841. // we need to verify a previously downloaded light block.
  842. // we add it to the auxiliary store so that VerifyToTarget
  843. // does not download it again
  844. auxLS.Update(b1,b1.State);
  845. }
  846. auxLS, res2 = VerifyToTarget(primary, auxLS, targetHeight)
  847. // move all lightblocks from auxLS to lightStore,
  848. // maintain state
  849. // we do that whether VerifyToTarget was successful or not
  850. for i, s range auxLS {
  851. lighStore.Update(s,s.State)
  852. }
  853. return (lightStore, res2)
  854. }
  855. else {
  856. return Backwards(primary, lightStore, targetHeight)
  857. }
  858. }
  859. }
  860. ```
  861. <!-- - Expected postcondition: -->
  862. <!-- - if targetHeight > lightStore.LatestVerified.height then -->
  863. <!-- return VerifyToTarget(primary, lightStore, targetHeight) -->
  864. <!-- - if targetHeight = lightStore.LatestVerified.height then -->
  865. <!-- return (lightStore, ResultSuccess) -->
  866. <!-- - if targetHeight < lightStore.LatestVerified.height -->
  867. <!-- - let b2 be in lightStore -->
  868. <!-- - that is verified and not expired -->
  869. <!-- - b2.Header.Height < targetHeight -->
  870. <!-- - for all b in lightStore s.t. b is verified and not expired it -->
  871. <!-- holds b2.Header.Height >= b.Header.Height -->
  872. <!-- - if b2 does not exists -->
  873. <!-- return Backwards(primary, lightStore, targetHeight) -->
  874. <!-- - if b2 exists -->
  875. <!-- - make auxiliary light store auxLS containing only b2 -->
  876. <!-- VerifyToTarget(primary, auxLS, targetHeight) -->
  877. <!-- - if b2 -->
  878. # References
  879. [[block]] Specification of the block data structure.
  880. [[RPC]] RPC client for Tendermint
  881. [[fork-detector]] The specification of the light client fork detector.
  882. [[fullnode]] Specification of the full node API
  883. [[ibc-rs]] Rust implementation of IBC modules and relayer.
  884. [[lightclient]] The light client ADR [77d2651 on Dec 27, 2019].
  885. [RPC]: https://docs.tendermint.com/master/rpc/
  886. [block]: https://github.com/tendermint/spec/blob/d46cd7f573a2c6a2399fcab2cde981330aa63f37/spec/core/data_structures.md
  887. [TMBC-HEADER-link]: #tmbc-header1
  888. [TMBC-SEQ-link]: #tmbc-seq1
  889. [TMBC-CorrFull-link]: #tmbc-corr-full1
  890. [TMBC-Auth-Byz-link]: #tmbc-auth-byz1
  891. [TMBC-TIME_PARAMS-link]: #tmbc-time-params1
  892. [TMBC-FM-2THIRDS-link]: #tmbc-fm-2thirds1
  893. [TMBC-VAL-CONTAINS-CORR-link]: #tmbc-val-contains-corr1
  894. [TMBC-VAL-COMMIT-link]: #tmbc-val-commit1
  895. [TMBC-SOUND-DISTR-POSS-COMMIT-link]: #tmbc-sound-distr-poss-commit1
  896. [lightclient]: https://github.com/interchainio/tendermint-rs/blob/e2cb9aca0b95430fca2eac154edddc9588038982/docs/architecture/adr-002-lite-client.md
  897. [fork-detector]: https://github.com/tendermint/spec/blob/master/rust-spec/lightclient/detection/detection_001_reviewed.md
  898. <!-- [fullnode]: https://github.com/tendermint/spec/blob/master/spec/blockchain/fullnode.md -->
  899. [ibc-rs]:https://github.com/informalsystems/ibc-rs
  900. <!-- [FN-LuckyCase-link]: https://github.com/tendermint/spec/blob/master/spec/blockchain/fullnode.md#fn-luckycase -->
  901. [blockchain-validator-set]: https://github.com/tendermint/spec/blob/master/spec/blockchain/blockchain.md#data-structures
  902. [fullnode-data-structures]: https://github.com/tendermint/spec/blob/master/spec/core/data_structures.md
  903. <!-- [FN-ManifestFaulty-link]: https://github.com/tendermint/spec/blob/master/spec/blockchain/fullnode.md#fn-manifestfaulty -->
  904. [arXiv]: https://arxiv.org/abs/1807.04938