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.

1180 lines
42 KiB

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