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