github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/spec/light-client/supervisor/supervisor_001_draft.md (about) 1 <!-- markdown-link-check-disable --> 2 3 # Draft of Light Client Supervisor for discussion 4 5 ## TODOs 6 7 This specification in done in parallel with updates on the 8 verification specification. So some hyperlinks have to be placed to 9 the correct files eventually. 10 11 # Light Client Sequential Supervisor 12 13 The light client implements a read operation of a 14 [header](TMBC-HEADER-link) from the [blockchain](TMBC-SEQ-link), by 15 communicating with full nodes, a so-called primary and several 16 so-called witnesses. As some full nodes may be faulty, this 17 functionality must be implemented in a fault-tolerant way. 18 19 In the Tendermint blockchain, the validator set may change with every 20 new block. The staking and unbonding mechanism induces a [security 21 model](TMBC-FM-2THIRDS-link): starting at time *Time* of the 22 [header](TMBC-HEADER-link), 23 more than two-thirds of the next validators of a new block are correct 24 for the duration of *TrustedPeriod*. 25 26 [Light Client Verification](https://informal.systems) implements the fault-tolerant read 27 operation designed for this security model. That is, it is safe if the 28 model assumptions are satisfied and makes progress if it communicates 29 to a correct primary. 30 31 However, if the [security model](TMBC-FM-2THIRDS-link) is violated, 32 faulty peers (that have been validators at some point in the past) may 33 launch attacks on the Tendermint network, and on the light 34 client. These attacks as well as an axiomatization of blocks in 35 general are defined in [a document that contains the definitions that 36 are currently in detection.md](https://informal.systems). 37 38 If there is a light client attack (but no 39 successful attack on the network), the safety of the verification step 40 may be violated (as we operate outside its basic assumption). 41 The light client also 42 contains a defense mechanism against light clients attacks, called detection. 43 44 [Light Client Detection](https://informal.systems) implements a cross check of the result 45 of the verification step. If there is a light client attack, and the 46 light client is connected to a correct peer, the light client as a 47 whole is safe, that is, it will not operate on invalid 48 blocks. However, in this case it cannot successfully read, as 49 inconsistent blocks are in the system. However, in this case the 50 detection performs a distributed computation that results in so-called 51 evidence. Evidence can be used to prove 52 to a correct full node that there has been a 53 light client attack. 54 55 [Light Client Evidence Accountability](https://informal.systems) is a protocol run on a 56 full node to check whether submitted evidence indeed proves the 57 existence of a light client attack. Further, from the evidence and its 58 own knowledge about the blockchain, the full node computes a set of 59 bonded full nodes (that at some point had more than one third of the 60 voting power) that participated in the attack that will be reported 61 via ABCI to the application. 62 63 In this document we specify 64 65 - Initialization of the Light Client 66 - The interaction of [verification](https://informal.systems) and [detection](https://informal.systems) 67 68 The details of these two protocols are captured in their own 69 documents, as is the [accountability](https://informal.systems) protocol. 70 71 > Another related line is IBC attack detection and submission at the 72 > relayer, as well as attack verification at the IBC handler. This 73 > will call for yet another spec. 74 75 # Status 76 77 This document is work in progress. In order to develop the 78 specification step-by-step, 79 it assumes certain details of [verification](https://informal.systems) and 80 [detection](https://informal.systems) that are not specified in the respective current 81 versions yet. This inconsistencies will be addresses over several 82 upcoming PRs. 83 84 # Part I - Tendermint Blockchain 85 86 See [verification spec](addLinksWhenDone) 87 88 # Part II - Sequential Problem Definition 89 90 #### **[LC-SEQ-INIT-LIVE.1]** 91 92 Upon initialization, the light client gets as input a header of the 93 blockchain, or the genesis file of the blockchain, and eventually 94 stores a header of the blockchain. 95 96 #### **[LC-SEQ-LIVE.1]** 97 98 The light client gets a sequence of heights as inputs. For each input 99 height *targetHeight*, it eventually stores the header of height 100 *targetHeight*. 101 102 #### **[LC-SEQ-SAFE.1]** 103 104 The light client never stores a header which is not in the blockchain. 105 106 # Part III - Light Client as Distributed System 107 108 ## Computational Model 109 110 The light client communicates with remote processes only via the 111 [verification](TODO) and the [detection](TODO) protocols. The 112 respective assumptions are given there. 113 114 ## Distributed Problem Statement 115 116 ### Two Kinds of Liveness 117 118 In case of light client attacks, the sequential problem statement 119 cannot always be satisfied. The lightclient cannot decide which block 120 is from the chain and which is not. As a result, the light client just 121 creates evidence, submits it, and terminates. 122 For the liveness property, we thus add the 123 possibility that instead of adding a lightblock, we also might terminate 124 in case there is an attack. 125 126 #### **[LC-DIST-TERM.1]** 127 128 The light client either runs forever or it *terminates on attack*. 129 130 ### Design choices 131 132 #### [LC-DIST-STORE.1] 133 134 The light client has a local data structure called LightStore 135 that contains light blocks (that contain a header). 136 137 > The light store exposes functions to query and update it. They are 138 > specified [here](TODO:onceVerificationIsMerged). 139 140 **TODO:** reference light store invariant [LCV-INV-LS-ROOT.2] once 141 verification is merged 142 143 #### **[LC-DIST-SAFE.1]** 144 145 It is always the case that every header in *LightStore* was 146 generated by an instance of Tendermint consensus. 147 148 #### **[LC-DIST-LIVE.1]** 149 150 Whenever the light client gets a new height *h* as input, 151 152 - and there is 153 no light client attack up to height *h*, then the lightclient 154 eventually puts the lightblock of height *h* in the lightstore and 155 wait for another input. 156 - otherwise, that is, if there 157 is a light client attack on height *h*, then the light client 158 must perform one of the following: 159 - it terminates on attack. 160 - it eventually puts the lightblock of height *h* in the lightstore and 161 wait for another input. 162 163 > Observe that the "existence of a lightclient attack" just means that some node has generated a conflicting block. It does not necessarily mean that a (faulty) peer sends such a block to "our" lightclient. Thus, even if there is an attack somewhere in the system, our lightclient might still continue to operate normally. 164 165 ### Solving the sequential specification 166 167 [LC-DIST-SAFE.1] is guaranteed by the detector; in particular it 168 follows from 169 [[LCD-DIST-INV-STORE.1]](TODO) 170 [[LCD-DIST-LIVE.1]](TODO) 171 172 # Part IV - Light Client Supervisor Protocol 173 174 We provide a specification for a sequential Light Client Supervisor. 175 The local code for verification is presented by a sequential function 176 `Sequential-Supervisor` to highlight the control flow of this 177 functionality. Each lightblock is first verified with a primary, and then 178 cross-checked with secondaries, and if all goes well, the lightblock 179 is 180 added (with the attribute "trusted") to the 181 lightstore. Intermiate lightblocks that were used to verify the target 182 block but were not cross-checked are stored as "verified" 183 184 > We note that if a different concurrency model is considered 185 > for an implementation, the semantics of the lightstore might change: 186 > In a concurrent implementation, we might do verification for some 187 > height *h*, add the 188 > lightblock to the lightstore, and start concurrent threads that 189 > 190 > - do verification for the next height *h' != h* 191 > - do cross-checking for height *h*. If we find an attack, we remove 192 > *h* from the lightstore. 193 > - the user might already start to use *h* 194 > 195 > Thus, this concurrency model changes the semantics of the 196 > lightstore (not all lightblocks that are read by the user are 197 > trusted; they may be removed if 198 > we find a problem). Whether this is desirable, and whether the gain in 199 > performance is worth it, we keep for future versions/discussion of 200 > lightclient protocols. 201 202 ## Definitions 203 204 ### Peers 205 206 #### **[LC-DATA-PEERS.1]:** 207 208 A fixed set of full nodes is provided in the configuration upon 209 initialization. Initially this set is partitioned into 210 211 - one full node that is the *primary* (singleton set), 212 - a set *Secondaries* (of fixed size, e.g., 3), 213 - a set *FullNodes*; it excludes *primary* and *Secondaries* nodes. 214 - A set *FaultyNodes* of nodes that the light client suspects of 215 being faulty; it is initially empty 216 217 #### **[LC-INV-NODES.1]:** 218 219 The detector shall maintain the following invariants: 220 221 - *FullNodes \intersect Secondaries = {}* 222 - *FullNodes \intersect FaultyNodes = {}* 223 - *Secondaries \intersect FaultyNodes = {}* 224 225 and the following transition invariant 226 227 - *FullNodes' \union Secondaries' \union FaultyNodes' = FullNodes 228 \union Secondaries \union FaultyNodes* 229 230 #### **[LC-FUNC-REPLACE-PRIMARY.1]:** 231 232 ```go 233 Replace_Primary(root-of-trust LightBlock) 234 ``` 235 236 - Implementation remark 237 - the primary is replaced by a secondary 238 - to maintain a constant size of secondaries, need to 239 - pick a new secondary *nsec* while ensuring [LC-INV-ROOT-AGREED.1] 240 - that is, we need to ensure that root-of-trust = FetchLightBlock(nsec, root-of-trust.Header.Height) 241 - Expected precondition 242 - *FullNodes* is nonempty 243 - Expected postcondition 244 - *primary* is moved to *FaultyNodes* 245 - a secondary *s* is moved from *Secondaries* to primary 246 - Error condition 247 - if precondition is violated 248 249 #### **[LC-FUNC-REPLACE-SECONDARY.1]:** 250 251 ```go 252 Replace_Secondary(addr Address, root-of-trust LightBlock) 253 ``` 254 255 - Implementation remark 256 - maintain [LC-INV-ROOT-AGREED.1], that is, 257 ensure root-of-trust = FetchLightBlock(nsec, root-of-trust.Header.Height) 258 - Expected precondition 259 - *FullNodes* is nonempty 260 - Expected postcondition 261 - addr is moved from *Secondaries* to *FaultyNodes* 262 - an address *nsec* is moved from *FullNodes* to *Secondaries* 263 - Error condition 264 - if precondition is violated 265 266 ### Data Types 267 268 The core data structure of the protocol is the LightBlock. 269 270 #### **[LC-DATA-LIGHTBLOCK.1]** 271 272 ```go 273 type LightBlock struct { 274 Header Header 275 Commit Commit 276 Validators ValidatorSet 277 NextValidators ValidatorSet 278 Provider PeerID 279 } 280 ``` 281 282 #### **[LC-DATA-LIGHTSTORE.1]** 283 284 LightBlocks are stored in a structure which stores all LightBlock from 285 initialization or received from peers. 286 287 ```go 288 type LightStore struct { 289 ... 290 } 291 292 ``` 293 294 We use the functions that the LightStore exposes, which 295 are defined in the [verification specification](TODO). 296 297 ### Inputs 298 299 The lightclient is initialized with LCInitData 300 301 #### **[LC-DATA-INIT.1]** 302 303 ```go 304 type LCInitData struct { 305 lightBlock LightBlock 306 genesisDoc GenesisDoc 307 } 308 ``` 309 310 where only one of the components must be provided. `GenesisDoc` is 311 defined in the [Tendermint 312 Types](https://github.com/tendermint/tendermint/blob/master/types/genesis.go). 313 314 #### **[LC-DATA-GENESIS.1]** 315 316 ```go 317 type GenesisDoc struct { 318 GenesisTime time.Time `json:"genesis_time"` 319 ChainID string `json:"chain_id"` 320 InitialHeight int64 `json:"initial_height"` 321 ConsensusParams *tmproto.ConsensusParams `json:"consensus_params,omitempty"` 322 Validators []GenesisValidator `json:"validators,omitempty"` 323 AppHash tmbytes.HexBytes `json:"app_hash"` 324 AppState json.RawMessage `json:"app_state,omitempty"` 325 } 326 ``` 327 328 We use the following function 329 `makeblock` so that we create a lightblock from the genesis 330 file in order to do verification based on the data from the genesis 331 file using the same verification function we use in normal operation. 332 333 #### **[LC-FUNC-MAKEBLOCK.1]** 334 335 ```go 336 func makeblock (genesisDoc GenesisDoc) (lightBlock LightBlock)) 337 ``` 338 339 - Implementation remark 340 - none 341 - Expected precondition 342 - none 343 - Expected postcondition 344 - lightBlock.Header.Height = genesisDoc.InitialHeight 345 - lightBlock.Header.Time = genesisDoc.GenesisTime 346 - lightBlock.Header.LastBlockID = nil 347 - lightBlock.Header.LastCommit = nil 348 - lightBlock.Header.Validators = genesisDoc.Validators 349 - lightBlock.Header.NextValidators = genesisDoc.Validators 350 - lightBlock.Header.Data = nil 351 - lightBlock.Header.AppState = genesisDoc.AppState 352 - lightBlock.Header.LastResult = nil 353 - lightBlock.Commit = nil 354 - lightBlock.Validators = genesisDoc.Validators 355 - lightBlock.NextValidators = genesisDoc.Validators 356 - lightBlock.Provider = nil 357 - Error condition 358 - none 359 360 ---- 361 362 ### Configuration Parameters 363 364 #### **[LC-INV-ROOT-AGREED.1]** 365 366 In the Sequential-Supervisor, it is always the case that the primary 367 and all secondaries agree on lightStore.Latest(). 368 369 ### Assumptions 370 371 We have to assume that the initialization data (the lightblock or the 372 genesis file) are consistent with the blockchain. This is subjective 373 initialization and it cannot be checked locally. 374 375 ### Invariants 376 377 #### **[LC-INV-PEERLIST.1]:** 378 379 The peer list contains a primary and a secondary. 380 381 > If the invariant is violated, the light client does not have enough 382 > peers to download headers from. As a result, the light client 383 > needs to terminate in case this invariant is violated. 384 385 ## Supervisor 386 387 ### Outline 388 389 The supervisor implements the functionality of the lightclient. It is 390 initialized with a genesis file or with a lightblock the user 391 trusts. This initialization is subjective, that is, the security of 392 the lightclient is based on the validity of the input. If the genesis 393 file or the lightblock deviate from the actual ones on the blockchain, 394 the lightclient provides no guarantees. 395 396 After initialization, the supervisor awaits an input, that is, the 397 height of the next lightblock that should be obtained. Then it 398 downloads, verifies, and cross-checks a lightblock, and if all tests 399 go through, the light block (and possibly other lightblocks) are added 400 to the lightstore, which is returned in an output event to the user. 401 402 The following main loop does the interaction with the user (input, 403 output) and calls the following two functions: 404 405 - `InitLightClient`: it initializes the lightstore either with the 406 provided lightblock or with the lightblock that corresponds to the 407 first block generated by the blockchain (by the validators defined 408 by the genesis file) 409 - `VerifyAndDetect`: takes as input a lightstore and a height and 410 returns the updated lightstore. 411 412 #### **[LC-FUNC-SUPERVISOR.1]:** 413 414 ```go 415 func Sequential-Supervisor (initdata LCInitData) (Error) { 416 417 lightStore,result := InitLightClient(initData); 418 if result != OK { 419 return result; 420 } 421 422 loop { 423 // get the next height 424 nextHeight := input(); 425 426 lightStore,result := VerifyAndDetect(lightStore, nextHeight); 427 428 if result == OK { 429 output(LightStore.Get(targetHeight)); 430 // we only output a trusted lightblock 431 } 432 else { 433 return result 434 } 435 // QUESTION: is it OK to generate output event in normal case, 436 // and terminate with failure in the (light client) attack case? 437 } 438 } 439 ``` 440 441 - Implementation remark 442 - infinite loop unless a light client attack is detected 443 - In typical implementations (e.g., the one in Rust), 444 there are mutliple input actions: 445 `VerifytoLatest`, `LatestTrusted`, and `GetStatus`. The 446 information can be easily obtained from the lightstore, so that 447 we do not treat these requests explicitly here but just consider 448 the request for a block of a given height which requires more 449 involved computation and communication. 450 - Expected precondition 451 - *LCInitData* contains a genesis file or a lightblock. 452 - Expected postcondition 453 - if a light client attack is detected: it stops and submits 454 evidence (in `InitLightClient` or `VerifyAndDetect`) 455 - otherwise: non. It runs forever. 456 - Invariant: *lightStore* contains trusted lightblocks only. 457 - Error condition 458 - if `InitLightClient` or `VerifyAndDetect` fails (if a attack is 459 detected, or if [LCV-INV-TP.1] is violated) 460 461 ---- 462 463 ### Details of the Functions 464 465 #### Initialization 466 467 The light client is based on subjective initialization. It has to 468 trust the initial data given to it by the user. It cannot do any 469 detection of attack. So either upon initialization we obtain a 470 lightblock and just initialize the lightstore with it. Or in case of a 471 genesis file, we download, verify, and cross-check the first block, to 472 initialize the lightstore with this first block. The reason is that 473 we want to maintain [LCV-INV-TP.1] from the beginning. 474 475 > If the lightclient is initialized with a lightblock, one might think 476 > it may increase trust, when one cross-checks the initial light 477 > block. However, if a peer provides a conflicting 478 > lightblock, the question is to distinguish the case of a 479 > [bogus](https://informal.systems) block (upon which operation should proceed) from a 480 > [light client attack](https://informal.systems) (upon which operation should stop). In 481 > case of a bogus block, the lightclient might be forced to do 482 > backwards verification until the blocks are out of the trusting 483 > period, to make sure no previous validator set could have generated 484 > the bogus block, which effectively opens up a DoS attack on the lightclient 485 > without adding effective robustness. 486 487 #### **[LC-FUNC-INIT.1]:** 488 489 ```go 490 func InitLightClient (initData LCInitData) (LightStore, Error) { 491 492 if LCInitData.LightBlock != nil { 493 // we trust the provided initial block. 494 newblock := LCInitData.LightBlock 495 } 496 else { 497 genesisBlock := makeblock(initData.genesisDoc); 498 499 result := NoResult; 500 while result != ResultSuccess { 501 current = FetchLightBlock(PeerList.primary(), genesisBlock.Header.Height + 1) 502 // QUESTION: is the height with "+1" OK? 503 504 if CANNOT_VERIFY = ValidAndVerify(genesisBlock, current) { 505 Replace_Primary(); 506 } 507 else { 508 result = ResultSuccess 509 } 510 } 511 512 // cross-check 513 auxLS := new LightStore 514 auxLS.Add(current) 515 Evidences := AttackDetector(genesisBlock, auxLS) 516 if Evidences.Empty { 517 newBlock := current 518 } 519 else { 520 // [LC-SUMBIT-EVIDENCE.1] 521 submitEvidence(Evidences); 522 return(nil, ErrorAttack); 523 } 524 } 525 526 lightStore := new LightStore; 527 lightStore.Add(newBlock); 528 return (lightStore, OK); 529 } 530 531 ``` 532 533 - Implementation remark 534 - none 535 - Expected precondition 536 - *LCInitData* contains either a genesis file of a lightblock 537 - if genesis it passes `ValidateAndComplete()` see [Tendermint](https://informal.systems) 538 - Expected postcondition 539 - *lightStore* initialized with trusted lightblock. It has either been 540 cross-checked (from genesis) or it has initial trust from the 541 user. 542 - Error condition 543 - if precondition is violated 544 - empty peerList 545 546 ---- 547 548 #### Main verification and detection logic 549 550 #### **[LC-FUNC-MAIN-VERIF-DETECT.1]:** 551 552 ```go 553 func VerifyAndDetect (lightStore LightStore, targetHeight Height) 554 (LightStore, Result) { 555 556 b1, r1 = lightStore.Get(targetHeight) 557 if r1 == true { 558 if b1.State == StateTrusted { 559 // block already there and trusted 560 return (lightStore, ResultSuccess) 561 } 562 else { 563 // We have a lightblock in the store, but it has not been 564 // cross-checked by now. We do that now. 565 root_of_trust, auxLS := lightstore.TraceTo(b1); 566 567 // Cross-check 568 Evidences := AttackDetector(root_of_trust, auxLS); 569 if Evidences.Empty { 570 // no attack detected, we trust the new lightblock 571 lightStore.Update(auxLS.Latest(), 572 StateTrusted, 573 verfiedLS.Latest().verification-root); 574 return (lightStore, OK); 575 } 576 else { 577 // there is an attack, we exit 578 submitEvidence(Evidences); 579 return(lightStore, ErrorAttack); 580 } 581 } 582 } 583 584 // get the lightblock with maximum height smaller than targetHeight 585 // would typically be the heighest, if we always move forward 586 root_of_trust, r2 = lightStore.LatestPrevious(targetHeight); 587 588 if r2 = false { 589 // there is no lightblock from which we can do forward 590 // (skipping) verification. Thus we have to go backwards. 591 // No cross-check needed. We trust hashes. Therefore, we 592 // directly return the result 593 return Backwards(primary, lightStore.Lowest(), targetHeight) 594 } 595 else { 596 // Forward verification + detection 597 result := NoResult; 598 while result != ResultSuccess { 599 verifiedLS,result := VerifyToTarget(primary, 600 root_of_trust, 601 nextHeight); 602 if result == ResultFailure { 603 // pick new primary (promote a secondary to primary) 604 Replace_Primary(root_of_trust); 605 } 606 else if result == ResultExpired { 607 return (lightStore, result) 608 } 609 } 610 611 // Cross-check 612 Evidences := AttackDetector(root_of_trust, verifiedLS); 613 if Evidences.Empty { 614 // no attack detected, we trust the new lightblock 615 verifiedLS.Update(verfiedLS.Latest(), 616 StateTrusted, 617 verfiedLS.Latest().verification-root); 618 lightStore.store_chain(verifidLS); 619 return (lightStore, OK); 620 } 621 else { 622 // there is an attack, we exit 623 return(lightStore, ErrorAttack); 624 } 625 } 626 } 627 ``` 628 629 - Implementation remark 630 - none 631 - Expected precondition 632 - none 633 - Expected postcondition 634 - lightblock of height *targetHeight* (and possibly additional blocks) added to *lightStore* 635 - Error condition 636 - an attack is detected 637 - [LC-DATA-PEERLIST-INV.1] is violated 638 639 ----