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.

1178 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. }
  348. ```
  349. #### **[LCV-DATA-LIGHTSTORE.1]**
  350. LightBlocks are stored in a structure which stores all LightBlock from
  351. initialization or received from peers.
  352. ```go
  353. type LightStore struct {
  354. ...
  355. }
  356. ```
  357. Each LightBlock is in one of the following states:
  358. ```go
  359. type VerifiedState int
  360. const (
  361. StateUnverified = iota + 1
  362. StateVerified
  363. StateFailed
  364. StateTrusted
  365. )
  366. ```
  367. > Only the detector module sets a lightBlock state to `StateTrusted`
  368. > and only if it was `StateVerified` before.
  369. The LightStore exposes the following functions to query stored LightBlocks.
  370. #### **[LCV-FUNC-GET.1]**
  371. ```go
  372. func (ls LightStore) Get(height Height) (LightBlock, bool)
  373. ```
  374. - Expected postcondition
  375. - returns a LightBlock at a given height or false in the second argument if
  376. the LightStore does not contain the specified LightBlock.
  377. #### **[LCV-FUNC-LATEST-VERIF.1]**
  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.2]**
  385. ```go
  386. func (ls LightStore) Update(lightBlock LightBlock,
  387. verfiedState VerifiedState
  388. verifiedBy Height)
  389. ```
  390. - Expected postcondition
  391. - The state of the LightBlock is set to *verifiedState*.
  392. - verifiedBy of the Lightblock is set to *Height*
  393. > The following function is used only in the detector specification
  394. > listed here for completeness.
  395. #### **[LCV-FUNC-LATEST-TRUSTED.1]**
  396. ```go
  397. func (ls LightStore) LatestTrusted() LightBlock
  398. ```
  399. - Expected postcondition
  400. - returns the highest light block that has been verified and
  401. checked by the detector.
  402. #### **[LCV-FUNC-FILTER.1]**
  403. ```go
  404. func (ls LightStore) FilterVerified() LightSTore
  405. ```
  406. - Expected postcondition
  407. - returns only the LightBlocks with state verified.
  408. ### Inputs
  409. - *lightStore*: stores light blocks that have been downloaded and that
  410. passed verification. Initially it contains a light block with
  411. *trustedHeader*.
  412. - *primary*: peerID
  413. - *targetHeight*: the height of the needed header
  414. ### Configuration Parameters
  415. - *trustThreshold*: a float. Can be used if correctness should not be based on more voting power and 1/3.
  416. - *trustingPeriod*: a time duration [**[TMBC-TIME_PARAMS.1]**][TMBC-TIME_PARAMS-link].
  417. - *clockDrift*: a time duration. Correction parameter dealing with only approximately synchronized clocks.
  418. ### Variables
  419. - *nextHeight*: initially *targetHeight*
  420. > *nextHeight* should be thought of the "height of the next header we need
  421. > to download and verify"
  422. ### Assumptions
  423. #### **[LCV-A-INIT.1]**
  424. - *trustedHeader* is from the blockchain
  425. - *targetHeight > LightStore.LatestVerified.Header.Height*
  426. ### Invariants
  427. #### **[LCV-INV-TP.1]**
  428. It is always the case that *LightStore.LatestTrusted.Header.Time > now - trustingPeriod*.
  429. > If the invariant is violated, the light client does not have a
  430. > header it can trust. A trusted header must be obtained externally,
  431. > its trust can only be based on social consensus.
  432. ### Used Remote Functions
  433. We use the functions `commit` and `validators` that are provided
  434. by the [RPC client for Tendermint][RPC].
  435. ```go
  436. func Commit(height int64) (SignedHeader, error)
  437. ```
  438. - Implementation remark
  439. - RPC to full node *n*
  440. - JSON sent:
  441. ```javascript
  442. // POST /commit
  443. {
  444. "jsonrpc": "2.0",
  445. "id": "ccc84631-dfdb-4adc-b88c-5291ea3c2cfb", // UUID v4, unique per request
  446. "method": "commit",
  447. "params": {
  448. "height": 1234
  449. }
  450. }
  451. ```
  452. - Expected precondition
  453. - header of `height` exists on blockchain
  454. - Expected postcondition
  455. - if *n* is correct: Returns the signed header of height `height`
  456. from the blockchain if communication is timely (no timeout)
  457. - if *n* is faulty: Returns a signed header with arbitrary content
  458. - Error condition
  459. - if *n* is correct: precondition violated or timeout
  460. - if *n* is faulty: arbitrary error
  461. ----
  462. ```go
  463. func Validators(height int64) (ValidatorSet, error)
  464. ```
  465. - Implementation remark
  466. - RPC to full node *n*
  467. - JSON sent:
  468. ```javascript
  469. // POST /validators
  470. {
  471. "jsonrpc": "2.0",
  472. "id": "ccc84631-dfdb-4adc-b88c-5291ea3c2cfb", // UUID v4, unique per request
  473. "method": "validators",
  474. "params": {
  475. "height": 1234
  476. }
  477. }
  478. ```
  479. - Expected precondition
  480. - header of `height` exists on blockchain
  481. - Expected postcondition
  482. - if *n* is correct: Returns the validator set of height `height`
  483. from the blockchain if communication is timely (no timeout)
  484. - if *n* is faulty: Returns arbitrary validator set
  485. - Error condition
  486. - if *n* is correct: precondition violated or timeout
  487. - if *n* is faulty: arbitrary error
  488. ----
  489. ### Communicating Function
  490. #### **[LCV-FUNC-FETCH.1]**
  491. ```go
  492. func FetchLightBlock(peer PeerID, height Height) LightBlock
  493. ```
  494. - Implementation remark
  495. - RPC to peer at *PeerID*
  496. - calls `Commit` for *height* and `Validators` for *height* and *height+1*
  497. - Expected precondition
  498. - `height` is less than or equal to height of the peer **[LCV-IO-PRE-HEIGHT.1]**
  499. - Expected postcondition:
  500. - if *node* is correct:
  501. - Returns the LightBlock *lb* of height `height`
  502. that is consistent with the blockchain
  503. - *lb.provider = peer* **[LCV-IO-POST-PROVIDER.1]**
  504. - *lb.Header* is a header consistent with the blockchain
  505. - *lb.Validators* is the validator set of the blockchain at height *nextHeight*
  506. - *lb.NextValidators* is the validator set of the blockchain at height *nextHeight + 1*
  507. - if *node* is faulty: Returns a LightBlock with arbitrary content
  508. [**[TMBC-AUTH-BYZ.1]**][TMBC-Auth-Byz-link]
  509. - Error condition
  510. - if *n* is correct: precondition violated
  511. - if *n* is faulty: arbitrary error
  512. - if *lb.provider != peer*
  513. - times out after 2 Delta (by assumption *n* is faulty)
  514. ----
  515. ## Core Verification
  516. ### Outline
  517. The `VerifyToTarget` is the main function and uses the following functions.
  518. - `FetchLightBlock` is called to download the next light block. It is
  519. the only function that communicates with other nodes
  520. - `ValidAndVerified` checks whether header is valid and checks if a
  521. new lightBlock should be trusted
  522. based on a previously verified lightBlock.
  523. - `Schedule` decides which height to try to verify next
  524. In the following description of `VerifyToTarget` we do not deal with error
  525. handling. If any of the above function returns an error, VerifyToTarget just
  526. passes the error on.
  527. #### **[LCV-FUNC-MAIN.1]**
  528. ```go
  529. func VerifyToTarget(primary PeerID, lightStore LightStore,
  530. targetHeight Height) (LightStore, Result) {
  531. nextHeight := targetHeight
  532. for lightStore.LatestVerified.height < targetHeight {
  533. // Get next LightBlock for verification
  534. current, found := lightStore.Get(nextHeight)
  535. if !found {
  536. current = FetchLightBlock(primary, nextHeight)
  537. lightStore.Update(current, StateUnverified)
  538. }
  539. // Verify
  540. verdict = ValidAndVerified(lightStore.LatestVerified, current)
  541. // Decide whether/how to continue
  542. if verdict == SUCCESS {
  543. lightStore.Update(current, StateVerified)
  544. }
  545. else if verdict == NOT_ENOUGH_TRUST {
  546. // do nothing
  547. // the light block current passed validation, but the validator
  548. // set is too different to verify it. We keep the state of
  549. // current at StateUnverified. For a later iteration, Schedule
  550. // might decide to try verification of that light block again.
  551. }
  552. else {
  553. // verdict is some error code
  554. lightStore.Update(current, StateFailed)
  555. // possibly remove all LightBlocks from primary
  556. return (lightStore, ResultFailure)
  557. }
  558. nextHeight = Schedule(lightStore, nextHeight, targetHeight)
  559. }
  560. return (lightStore, ResultSuccess)
  561. }
  562. ```
  563. - Expected precondition
  564. - *lightStore* contains a LightBlock within the *trustingPeriod* **[LCV-PRE-TP.1]**
  565. - *targetHeight* is greater than the height of all the LightBlocks in *lightStore*
  566. - Expected postcondition:
  567. - returns *lightStore* that contains a LightBlock that corresponds to a block
  568. of the blockchain of height *targetHeight*
  569. (that is, the LightBlock has been added to *lightStore*) **[LCV-POST-LS.1]**
  570. - Error conditions
  571. - if the precondition is violated
  572. - if `ValidAndVerified` or `FetchLightBlock` report an error
  573. - if [**[LCV-INV-TP.1]**](#LCV-INV-TP.1) is violated
  574. ### Details of the Functions
  575. #### **[LCV-FUNC-VALID.1]**
  576. ```go
  577. func ValidAndVerified(trusted LightBlock, untrusted LightBlock) Result
  578. ```
  579. - Expected precondition:
  580. - *untrusted* is valid, that is, satisfies the soundness [checks][block]
  581. - *untrusted* is **well-formed**, that is,
  582. - *untrusted.Header.Time < now + clockDrift*
  583. - *untrusted.Validators = hash(untrusted.Header.Validators)*
  584. - *untrusted.NextValidators = hash(untrusted.Header.NextValidators)*
  585. - *trusted.Header.Time > now - trustingPeriod*
  586. - *trusted.Commit* is a commit for the header
  587. *trusted.Header*, i.e., it contains
  588. the correct hash of the header, and +2/3 of signatures
  589. - the `Height` and `Time` of `trusted` are smaller than the Height and
  590. `Time` of `untrusted`, respectively
  591. - the *untrusted.Header* is well-formed (passes the tests from
  592. [[block]]), and in particular
  593. - if the untrusted header `unstrusted.Header` is the immediate
  594. successor of `trusted.Header`, then it holds that
  595. - *trusted.Header.NextValidators =
  596. untrusted.Header.Validators*, and
  597. moreover,
  598. - *untrusted.Header.Commit*
  599. - contains signatures by more than two-thirds of the validators
  600. - contains no signature from nodes that are not in *trusted.Header.NextValidators*
  601. - Expected postcondition:
  602. - Returns `SUCCESS`:
  603. - if *untrusted* is the immediate successor of *trusted*, or otherwise,
  604. - if the signatures of a set of validators that have more than
  605. *max(1/3,trustThreshold)* of voting power in
  606. *trusted.Nextvalidators* is contained in
  607. *untrusted.Commit* (that is, header passes the tests
  608. [**[TMBC-VAL-CONTAINS-CORR.1]**][TMBC-VAL-CONTAINS-CORR-link]
  609. and [**[TMBC-VAL-COMMIT.1]**][TMBC-VAL-COMMIT-link])
  610. - Returns `NOT_ENOUGH_TRUST` if:
  611. - *untrusted* is *not* the immediate successor of
  612. *trusted*
  613. and the *max(1/3,trustThreshold)* threshold is not reached
  614. (that is, if
  615. [**[TMBC-VAL-CONTAINS-CORR.1]**][TMBC-VAL-CONTAINS-CORR-link]
  616. fails and header is does not violate the soundness
  617. checks [[block]]).
  618. - Error condition:
  619. - if precondition violated
  620. ----
  621. #### **[LCV-FUNC-SCHEDULE.1]**
  622. ```go
  623. func Schedule(lightStore, nextHeight, targetHeight) Height
  624. ```
  625. - Implementation remark: If picks the next height to be verified.
  626. We keep the precise choice of the next header under-specified. It is
  627. subject to performance optimizations that do not influence the correctness
  628. - Expected postcondition: **[LCV-SCHEDULE-POST.1]**
  629. Return *H* s.t.
  630. 1. if *lightStore.LatestVerified.Height = nextHeight* and
  631. *lightStore.LatestVerified < targetHeight* then
  632. *nextHeight < H <= targetHeight*
  633. 2. if *lightStore.LatestVerified.Height < nextHeight* and
  634. *lightStore.LatestVerified.Height < targetHeight* then
  635. *lightStore.LatestVerified.Height < H < nextHeight*
  636. 3. if *lightStore.LatestVerified.Height = targetHeight* then
  637. *H = targetHeight*
  638. > Case i. captures the case where the light block at height *nextHeight*
  639. > has been verified, and we can choose a height closer to the *targetHeight*.
  640. > As we get the *lightStore* as parameter, the choice of the next height can
  641. > depend on the *lightStore*, e.g., we can pick a height for which we have
  642. > already downloaded a light block.
  643. > In Case ii. the header of *nextHeight* could not be verified, and we need to pick a smaller height.
  644. > In Case iii. is a special case when we have verified the *targetHeight*.
  645. ### Solving the distributed specification
  646. *trustedStore* is implemented by the light blocks in lightStore that
  647. have the state *StateVerified*.
  648. #### Argument for [**[LCV-DIST-SAFE.1]**](#lcv-dist-safe)
  649. - `ValidAndVerified` implements the soundness checks and the checks
  650. [**[TMBC-VAL-CONTAINS-CORR.1]**][TMBC-VAL-CONTAINS-CORR-link] and
  651. [**[TMBC-VAL-COMMIT.1]**][TMBC-VAL-COMMIT-link] under
  652. the assumption [**[TMBC-FM-2THIRDS.1]**][TMBC-FM-2THIRDS-link]
  653. - Only if `ValidAndVerified` returns with `SUCCESS`, the state of a light block is
  654. set to *StateVerified*.
  655. #### Argument for [**[LCV-DIST-LIVE.1]**](#lcv-dist-life)
  656. - If *primary* is correct,
  657. - `FetchLightBlock` will always return a light block consistent
  658. with the blockchain
  659. - `ValidAndVerified` either verifies the header using the trusting
  660. period or falls back to sequential
  661. verification
  662. - If [**[LCV-INV-TP.1]**](#LCV-INV-TP.1) holds, eventually every
  663. header will be verified and core verification **terminates successfully**.
  664. - successful termination depends on the age of *lightStore.LatestVerified*
  665. (for instance, initially on the age of *trustedHeader*) and the
  666. changes of the validator sets on the blockchain.
  667. We will give some examples [below](#liveness-scenarios).
  668. - If *primary* is faulty,
  669. - it either provides headers that pass all the tests, and we
  670. return with the header
  671. - it provides one header that fails a test, core verification
  672. **terminates with failure**.
  673. - it times out and core verification
  674. **terminates with failure**.
  675. ## Liveness Scenarios
  676. The liveness argument above assumes [**[LCV-INV-TP.1]**](#LCV-INV-TP.1)
  677. which requires that there is a header that does not expire before the
  678. target height is reached. Here we discuss scenarios to ensure this.
  679. Let *startHeader* be *LightStore.LatestVerified* when core
  680. verification is called (*trustedHeader*) and *startTime* be the time
  681. core verification is invoked.
  682. In order to ensure liveness, *LightStore* always needs to contain a
  683. verified (or initially trusted) header whose time is within the
  684. trusting period. To ensure this, core verification needs to add new
  685. headers to *LightStore* and verify them, before all headers in
  686. *LightStore* expire.
  687. #### Many changes in validator set
  688. Let's consider `Schedule` implements
  689. bisection, that is, it halves the distance.
  690. Assume the case where the validator set changes completely in each
  691. block. Then the
  692. method in this specification needs to
  693. sequentially verify all headers. That is, for
  694. - *W = log_2 (targetHeight - startHeader.Height)*,
  695. *W* headers need to be downloaded and checked before the
  696. header of height *startHeader.Height + 1* is added to *LightStore*.
  697. - Let *Comp*
  698. be the local computation time needed to check headers and signatures
  699. for one header.
  700. - Then we need in the worst case *Comp + 2 Delta* to download and
  701. check one header.
  702. - Then the first time a verified header could be added to *LightStore* is
  703. startTime + W * (Comp + 2 Delta)
  704. - [TP.1] However, it can only be added if we still have a header in
  705. *LightStore*,
  706. which is not
  707. expired, that is only the case if
  708. - startHeader.Time > startTime + WCG * (Comp + 2 Delta) -
  709. trustingPeriod,
  710. - that is, if core verification is started at
  711. startTime < startHeader.Time + trustingPeriod - WCG * (Comp + 2 Delta)
  712. - one may then do an inductive argument from this point on, depending
  713. on the implementation of `Schedule`. We may have to account for the
  714. headers that are already
  715. downloaded, but they are checked against the new *LightStore.LatestVerified*.
  716. > We observe that
  717. > the worst case time it needs to verify the header of height
  718. > *targetHeight* depends mainly on how frequent the validator set on the
  719. > blockchain changes. That core verification terminates successfully
  720. > crucially depends on the check [TP.1], that is, that the headers in
  721. > *LightStore* do not expire in the time needed to download more
  722. > headers, which depends on the creation time of the headers in
  723. > *LightStore*. That is, termination of core verification is highly
  724. > depending on the data stored in the blockchain.
  725. > The current light client core verification protocol exploits that, in
  726. > practice, changes in the validator set are rare. For instance,
  727. > consider the following scenario.
  728. #### No change in validator set
  729. If on the blockchain the validator set of the block at height
  730. *targetHeight* is equal to *startHeader.NextValidators*:
  731. - there is one round trip in `FetchLightBlock` to download the light
  732. block
  733. of height
  734. *targetHeight*, and *Comp* to check it.
  735. - as the validator sets are equal, `Verify` returns `SUCCESS`, if
  736. *startHeader.Time > now - trustingPeriod*.
  737. - that is, if *startTime < startHeader.Header.Time + trustingPeriod -
  738. 2 Delta - Comp*, then core verification terminates successfully
  739. # Part V - Supporting the IBC Relayer
  740. The above specification focuses on the most common case, which also
  741. constitutes the most challenging task: using the Tendermint [security
  742. model][TMBC-FM-2THIRDS-link] to verify light blocks without
  743. downloading all intermediate blocks. To focus on this challenge, above
  744. we have restricted ourselves to the case where *targetHeight* is
  745. greater than the height of any trusted header. This simplified
  746. presentation of the algorithm as initially
  747. `lightStore.LatestVerified()` is less than *targetHeight*, and in the
  748. process of verification `lightStore.LatestVerified()` increases until
  749. *targetHeight* is reached.
  750. For [IBC][ibc-rs] it might be that some "older" header is
  751. needed, that is, *targetHeight < lightStore.LatestVerified()*. In this section we present a preliminary design, and we mark some
  752. remaining open questions.
  753. If *targetHeight < lightStore.LatestVerified()* our design separates
  754. the following cases:
  755. - A previous instance of `VerifyToTarget` has already downloaded the
  756. light block of *targetHeight*. There are two cases
  757. - the light block has been verified
  758. - the light block has not been verified yet
  759. - No light block of *targetHeight* had been downloaded before. There
  760. are two cases:
  761. - there exists a verified light block of height less than *targetHeight*
  762. - otherwise. In this case we need to do "backwards verification"
  763. using the hash of the previous block in the `LastBlockID` field
  764. of a header.
  765. **Open Question:** what are the security assumptions for backward
  766. verification. Should we check that the light block we verify from
  767. (and/or the checked light block) is within the trusting period?
  768. The design just presents the above case
  769. distinction as a function, and defines some auxiliary functions in the
  770. same way the protocol was presented in
  771. [Part IV](#part-iv---light-client-verification-protocol).
  772. ```go
  773. func (ls LightStore) LatestPrevious(height Height) (LightBlock, bool)
  774. ```
  775. - Expected postcondition
  776. - returns a light block *lb* that satisfies:
  777. - *lb* is in lightStore
  778. - *lb* is verified and not expired
  779. - *lb.Header.Height < height*
  780. - for all *b* in lightStore s.t. *b* is verified and not expired it
  781. holds *lb.Header.Height >= b.Header.Height*
  782. - *false* in the second argument if
  783. the LightStore does not contain such an *lb*.
  784. ```go
  785. func (ls LightStore) MinVerified() (LightBlock, bool)
  786. ```
  787. - Expected postcondition
  788. - returns a light block *lb* that satisfies:
  789. - *lb* is in lightStore
  790. - *lb* is verified **Open Question:** replace by trusted?
  791. - *lb.Header.Height* is minimal in the lightStore
  792. - **Open Question:** according to this, it might be expired (outside the
  793. trusting period). This approach appears safe. Are there reasons we
  794. should not do that?
  795. - *false* in the second argument if
  796. the LightStore does not contain such an *lb*.
  797. If a height that is smaller than the smallest height in the lightstore
  798. is required, we check the hashes backwards. This is done with the
  799. following function:
  800. #### **[LCV-FUNC-BACKWARDS.1]**
  801. ```go
  802. func Backwards (primary PeerID, lightStore LightStore, targetHeight Height)
  803. (LightStore, Result) {
  804. lb,res = lightStore.MinVerified()
  805. if res = false {
  806. return (lightStore, ResultFailure)
  807. }
  808. latest := lb.Header
  809. for i := lb.Header.height - 1; i >= targetHeight; i-- {
  810. // here we download height-by-height. We might first download all
  811. // headers down to targetHeight and then check them.
  812. current := FetchLightBlock(primary,i)
  813. if (hash(current) != latest.Header.LastBlockId) {
  814. return (lightStore, ResultFailure)
  815. }
  816. else {
  817. lightStore.Update(current, StateVerified)
  818. // **Open Question:** Do we need a new state type for
  819. // backwards verified light blocks?
  820. }
  821. latest = current
  822. }
  823. return (lightStore, ResultSuccess)
  824. }
  825. ```
  826. The following function just decided based on the required height which
  827. method should be used.
  828. #### **[LCV-FUNC-IBCMAIN.1]**
  829. ```go
  830. func Main (primary PeerID, lightStore LightStore, targetHeight Height)
  831. (LightStore, Result) {
  832. b1, r1 = lightStore.Get(targetHeight)
  833. if r1 = true and b1.State = StateVerified {
  834. // block already there
  835. return (lightStore, ResultSuccess)
  836. }
  837. if targetHeight > lightStore.LatestVerified.height {
  838. // case of Part IV
  839. return VerifyToTarget(primary, lightStore, targetHeight)
  840. }
  841. else {
  842. b2, r2 = lightStore.LatestPrevious(targetHeight);
  843. if r2 = true {
  844. // make auxiliary lightStore auxLS to call VerifyToTarget.
  845. // VerifyToTarget uses LatestVerified of the given lightStore
  846. // For that we need:
  847. // auxLS.LatestVerified = lightStore.LatestPrevious(targetHeight)
  848. auxLS.Init;
  849. auxLS.Update(b2,StateVerified);
  850. if r1 = true {
  851. // we need to verify a previously downloaded light block.
  852. // we add it to the auxiliary store so that VerifyToTarget
  853. // does not download it again
  854. auxLS.Update(b1,b1.State);
  855. }
  856. auxLS, res2 = VerifyToTarget(primary, auxLS, targetHeight)
  857. // move all lightblocks from auxLS to lightStore,
  858. // maintain state
  859. // we do that whether VerifyToTarget was successful or not
  860. for i, s range auxLS {
  861. lighStore.Update(s,s.State)
  862. }
  863. return (lightStore, res2)
  864. }
  865. else {
  866. return Backwards(primary, lightStore, targetHeight)
  867. }
  868. }
  869. }
  870. ```
  871. <!-- - Expected postcondition: -->
  872. <!-- - if targetHeight > lightStore.LatestVerified.height then -->
  873. <!-- return VerifyToTarget(primary, lightStore, targetHeight) -->
  874. <!-- - if targetHeight = lightStore.LatestVerified.height then -->
  875. <!-- return (lightStore, ResultSuccess) -->
  876. <!-- - if targetHeight < lightStore.LatestVerified.height -->
  877. <!-- - let b2 be in lightStore -->
  878. <!-- - that is verified and not expired -->
  879. <!-- - b2.Header.Height < targetHeight -->
  880. <!-- - for all b in lightStore s.t. b is verified and not expired it -->
  881. <!-- holds b2.Header.Height >= b.Header.Height -->
  882. <!-- - if b2 does not exists -->
  883. <!-- return Backwards(primary, lightStore, targetHeight) -->
  884. <!-- - if b2 exists -->
  885. <!-- - make auxiliary light store auxLS containing only b2 -->
  886. <!-- VerifyToTarget(primary, auxLS, targetHeight) -->
  887. <!-- - if b2 -->
  888. # References
  889. [[block]] Specification of the block data structure.
  890. [[RPC]] RPC client for Tendermint
  891. [[fork-detector]] The specification of the light client fork detector.
  892. [[fullnode]] Specification of the full node API
  893. [[ibc-rs]] Rust implementation of IBC modules and relayer.
  894. [[lightclient]] The light client ADR [77d2651 on Dec 27, 2019].
  895. [RPC]: https://docs.tendermint.com/master/rpc/
  896. [block]: https://github.com/tendermint/spec/blob/d46cd7f573a2c6a2399fcab2cde981330aa63f37/spec/core/data_structures.md
  897. [TMBC-HEADER-link]: #tmbc-header1
  898. [TMBC-SEQ-link]: #tmbc-seq1
  899. [TMBC-CorrFull-link]: #tmbc-corr-full1
  900. [TMBC-Auth-Byz-link]: #tmbc-auth-byz1
  901. [TMBC-TIME_PARAMS-link]: #tmbc-time-params1
  902. [TMBC-FM-2THIRDS-link]: #tmbc-fm-2thirds1
  903. [TMBC-VAL-CONTAINS-CORR-link]: #tmbc-val-contains-corr1
  904. [TMBC-VAL-COMMIT-link]: #tmbc-val-commit1
  905. [TMBC-SOUND-DISTR-POSS-COMMIT-link]: #tmbc-sound-distr-poss-commit1
  906. [lightclient]: https://github.com/interchainio/tendermint-rs/blob/e2cb9aca0b95430fca2eac154edddc9588038982/docs/architecture/adr-002-lite-client.md
  907. [fork-detector]: https://github.com/informalsystems/tendermint-rs/blob/master/docs/spec/lightclient/detection.md
  908. [fullnode]: https://github.com/tendermint/spec/blob/master/spec/blockchain/fullnode.md
  909. [ibc-rs]:https://github.com/informalsystems/ibc-rs
  910. [FN-LuckyCase-link]: https://github.com/tendermint/spec/blob/master/spec/blockchain/fullnode.md#fn-luckycase
  911. [blockchain-validator-set]: https://github.com/tendermint/spec/blob/master/spec/blockchain/blockchain.md#data-structures
  912. [fullnode-data-structures]: https://github.com/tendermint/spec/blob/master/spec/blockchain/fullnode.md#data-structures
  913. [FN-ManifestFaulty-link]: https://github.com/tendermint/spec/blob/master/spec/blockchain/fullnode.md#fn-manifestfaulty
  914. [arXiv]: https://arxiv.org/abs/1807.04938