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