github.com/aakash4dev/cometbft@v0.38.2/spec/light-client/detection/detection_001_reviewed.md (about) 1 <!-- markdown-link-check-disable --> 2 # ***This an unfinished draft. Comments are welcome!*** 3 4 **TODO:** We will need to do small adaptations to the verification 5 spec to reflect the semantics in the LightStore (verified, trusted, 6 untrusted, etc. not needed anymore). In more detail: 7 8 - The state of the Lightstore needs to go. Functions like `LatestVerified` can 9 keep the name but will ignore state as it will not exist anymore. 10 11 - verification spec should be adapted to the second parameter of 12 `VerifyToTarget` 13 being a lightblock; new version number of function tag; 14 15 - We should clarify what is the expectation of VerifyToTarget 16 so if it returns TimeoutError it can be assumed faulty. I guess that 17 VerifyToTarget with correct full node should never terminate with 18 TimeoutError. 19 20 - We need to introduce a new version number for the new 21 specification. So we should decide how 22 to handle that. 23 24 # Light Client Attack Detector 25 26 In this specification, we strengthen the light client to be resistant 27 against so-called light client attacks. In a light client attack, all 28 the correct Cosmos full nodes agree on the sequence of generated 29 blocks (no fork), but a set of faulty full nodes attack a light client 30 by generating (signing) a block that deviates from the block of the 31 same height on the blockchain. In order to do so, some of these faulty 32 full nodes must have been validators before and violate 33 [[CMBC-FM-2THIRDS]](CMBC-FM-2THIRDS-link), as otherwise, if 34 [[CMBC-FM-2THIRDS]](CMBC-FM-2THIRDS-link) would hold, 35 [verification](verification) would satisfy 36 [[LCV-SEQ-SAFE.1]](LCV-SEQ-SAFE-link). 37 38 An attack detector (or detector for short) is a mechanism that is used 39 by the light client [supervisor](supervisor) after 40 [verification](verification) of a new light block 41 with the primary, to cross-check the newly learned light block with 42 other peers (secondaries). It expects as input a light block with some 43 height *root* (that serves as a root of trust), and a verification 44 trace (a sequence of lightblocks) that the primary provided. 45 46 In case the detector observes a light client attack, it computes 47 evidence data that can be used by Cosmos full nodes to isolate a 48 set of faulty full nodes that are still within the unbonding period 49 (more than 1/3 of the voting power of the validator set at some block of the chain), 50 and report them via ABCI to the application of a Cosmos blockchain 51 in order to punish faulty nodes. 52 53 ## Context of this document 54 55 The light client [verification](verification) specification is 56 designed for the Cosmos failure model (1/3 assumption) 57 [[CMBC-FM-2THIRDS]](CMBC-FM-2THIRDS-link). It is safe under this 58 assumption, and live if it can reliably (that is, no message loss, no 59 duplication, and eventually delivered) and timely communicate with a 60 correct full node. If [[CMBC-FM-2THIRDS]](CMBC-FM-2THIRDS-link) assumption is violated, the light client 61 can be fooled to trust a light block that was not generated by 62 Tendermint consensus. 63 64 This specification, the attack detector, is a "second line of 65 defense", in case the 1/3 assumption is violated. Its goal is to 66 detect a light client attack (conflicting light blocks) and collect 67 evidence. However, it is impractical to probe all full nodes. At this 68 time we consider a simple scheme of maintaining an address book of 69 known full nodes from which a small subset (e.g., 4) are chosen 70 initially to communicate with. More involved book keeping with 71 probabilistic guarantees can be considered at later stages of the 72 project. 73 74 The light client maintains a simple address book containing addresses 75 of full nodes that it can pick as primary and secondaries. To obtain 76 a new light block, the light client first does 77 [verification](verification) with the primary, and then cross-checks 78 the light block (and the trace of light blocks that led to it) with 79 the secondaries using this specification. 80 81 ## Tendermint Consensus and Light Client Attacks 82 83 In this section we will give some mathematical definitions of what we 84 mean by light client attacks (that are considered in this 85 specification) and how they differ from main-chain forks. To this end 86 we start by defining some properties of the sequence of blocks that is 87 decided upon by Tendermint consensus in normal operation (if the 88 Cosmos failure model holds 89 [[CMBC-FM-2THIRDS]](CMBC-FM-2THIRDS-link)), 90 and then define different 91 deviations that correspond to attack scenarios. 92 93 #### **[CMBC-GENESIS.1]** 94 95 Let *Genesis* be the agreed-upon initial block (file). 96 97 #### **[CMBC-FUNC-SIGN.1]** 98 99 Let *b* and *c* be two light blocks with *b.Header.Height + 1 = 100 c.Header.Height*. We define the predicate **signs(b,c)** to hold 101 iff *c.Header.LastCommit* is in *PossibleCommit(b)*. 102 [[CMBC-SOUND-DISTR-POSS-COMMIT.1]](CMBC-SOUND-DISTR-POSS-COMMIT-link). 103 104 > The above encodes sequential verification, that is, intuitively, 105 > b.Header.NextValidators = c.Header.Validators and 2/3 of 106 > these Validators signed c? 107 108 #### **[CMBC-FUNC-SUPPORT.1]** 109 110 Let *b* and *c* be two light blocks. We define the predicate 111 **supports(b,c,t)** to hold iff 112 113 - *t - trustingPeriod < b.Header.Time < t* 114 - the voting power in *b.NextValidators* of nodes in *c.Commit* 115 is more than 1/3 of *TotalVotingPower(b.Header.NextValidators)* 116 117 > That is, if the [Cosmos failure model](CMBC-FM-2THIRDS-link) 118 > holds, then *c* has been signed by at least one correct full node, cf. 119 > [[CMBC-VAL-CONTAINS-CORR.1]](CMBC-VAL-CONTAINS-CORR-link). 120 > The following formalizes that *b* was properly generated by 121 > Tendermint; *b* can be traced back to genesis 122 123 #### **[CMBC-SEQ-ROOTED.1]** 124 125 Let *b* be a light block. 126 We define *sequ-rooted(b)* iff for all *i*, *1 <= i < h = b.Header.Height*, 127 there exist light blocks *a(i)* s.t. 128 129 - *a(1) = Genesis* and 130 - *a(h) = b* and 131 - *signs( a(i) , a(i+1) )*. 132 133 > The following formalizes that *c* is trusted based on *b* in 134 > skipping verification. Observe that we do not require here (yet) 135 > that *b* was properly generated. 136 137 #### **[CMBC-SKIP-TRACE.1]** 138 139 Let *b* and *c* be light blocks. We define *skip-trace(b,c,t)* if at 140 time t there exists an *h* and a sequence *a(1)*, ... *a(h)* s.t. 141 142 - *a(1) = b* and 143 - *a(h) = c* and 144 - *supports( a(i), a(i+1), t)*, for all i, *1 <= i < h*. 145 146 We call such a sequence *a(1)*, ... *a(h)* a **verification trace**. 147 148 > The following formalizes that two light blocks of the same height 149 > should agree on the content of the header. Observe that *b* and *c* 150 > may disagree on the Commit. This is a special case if the canonical 151 > commit has not been decided on, that is, if b.Header.Height is the 152 > maximum height of all blocks decided upon by Tendermint at this 153 > moment. 154 155 #### **[CMBC-SIGN-SKIP-MATCH.1]** 156 157 Let *a*, *b*, *c*, be light blocks and *t* a time, we define 158 *sign-skip-match(a,b,c,t) = true* iff the following implication 159 evaluates to true: 160 161 - *sequ-rooted(a)* and 162 - *b.Header.Height = c.Header.Height* and 163 - *skip-trace(a,b,t)* 164 - *skip-trace(a,c,t)* 165 166 implies *b.Header = c.Header*. 167 168 > Observe that *sign-skip-match* is defined via an implication. If it 169 > evaluates to false this means that the left-hand-side of the 170 > implication evaluates to true, and the right-hand-side evaluates to 171 > false. In particular, there are two **different** headers *b* and 172 > *c* that both can be verified from a common block *a* from the 173 > chain. Thus, the following describes an attack. 174 175 #### **[CMBC-ATTACK.1]** 176 177 If there exists three light blocks a, b, and c, with 178 *sign-skip-match(a,b,c,t) = false* then we have an *attack*. We say 179 we have **an attack at height** *b.Header.Height* and write 180 *attack(a,b,c,t)*. 181 182 > The lightblock *a* need not be unique, that is, there may be 183 > several blocks that satisfy the above requirement for the same 184 > blocks *b* and *c*. 185 186 [[CMBC-ATTACK.1]](#CMBC-ATTACK1) is a formalization of the violation 187 of the agreement property based on the result of consensus, that is, 188 the generated blocks. 189 190 **Remark.** 191 Violation of agreement is only possible if more than 1/3 of the validators (or 192 next validators) of some previous block deviated from the protocol. The 193 upcoming "accountability" specification will describe how to compute 194 a set of at least 1/3 faulty nodes from two conflicting blocks. [] 195 196 There are different ways to characterize forks 197 and attack scenarios. This specification uses the "node-based 198 characterization of attacks" which focuses on what kinds of nodes are 199 affected (light nodes vs. full nodes). For future reference and 200 discussion we also provide a 201 "block-based characterization of attacks" below. 202 203 ### Node-based characterization of attacks 204 205 #### **[CMBC-MC-FORK.1]** 206 207 We say there is a (main chain) fork at time *t* if 208 209 - there are two correct full nodes *i* and *j* and 210 - *i* is different from *j* and 211 - *i* has decided on *b* and 212 - *j* has decided on *c* and 213 - there exist *a* such that *attack(a,b,c,t)*. 214 215 #### **[CMBC-LC-ATTACK.1]** 216 217 We say there is a light client attack at time *t*, if 218 219 - there is **no** (main chain) fork [[CMBC-MC-FORK.1]](#CMBC-MC-FORK1), and 220 - there exist nodes that have computed light blocks *b* and *c* and 221 - there exist *a* such that *attack(a,b,c,t)*. 222 223 We say the attack is at height *a.Header.Height*. 224 225 > In this specification we consider detection of light client 226 > attacks. Intuitively, the case we consider is that 227 > light block *b* is the one from the 228 > blockchain, and some attacker has computed *c* and tries to wrongly 229 > convince 230 > the light client that *c* is the block from the chain. 231 232 #### **[CMBC-LC-ATTACK-EVIDENCE.1]** 233 234 We consider the following case of a light client attack 235 [[CMBC-LC-ATTACK.1]](#CMBC-LC-ATTACK1): 236 237 - *attack(a,b,c,t)* 238 - there is a peer p1 that has a sequence *chain* of blocks from *a* to *b* 239 - *skip-trace(a,c,t)*: by [[CMBC-SKIP-TRACE.1]](#CMBC-SKIP-TRACE1) there is a 240 verification trace *v* of the form *a = v(1)*, ... *v(h) = c* 241 242 Evidence for p1 (that proves an attack) consists for index i 243 of v(i) and v(i+1) such that 244 245 - E1(i). v(i) is equal to the block of *chain* at height v(i).Height, and 246 - E2(i). v(i+1) that is different from the block of *chain* at 247 height v(i+1).height 248 249 > Observe p1 can 250 > 251 > - check that v(i+1) differs from its block at that height, and 252 > - verify v(i+1) in one step from v(i) as v is a verification trace. 253 254 **Proposition.** In the case of attack, evidence exists. 255 *Proof.* First observe that 256 257 - (A). (NOT E2(i)) implies E1(i+1) 258 259 Now by contradiction assume there is no evidence. Thus 260 261 - for all i, we have NOT E1(i) or NOT E2(i) 262 - for i = 1 we have E1(1) and thus NOT E2(1) 263 thus by induction on i, by (A) we have for all i that **E1(i)** 264 - from attack we have E2(h-1), and as there is no evidence for 265 i = h - 1 we get **NOT E1(h-1)**. Contradiction. 266 QED. 267 268 #### **[CMBC-LC-EVIDENCE-DATA.1]** 269 270 To prove the attack to p1, because of Point E1, it is sufficient to 271 submit 272 273 - v(i).Height (rather than v(i)). 274 - v(i+1) 275 276 This information is *evidence for height v(i).Height*. 277 278 ### Block-based characterization of attacks 279 280 In this section we provide a different characterization of attacks. It 281 is not defined on the nodes that are affected but purely on the 282 content of the blocks. In that sense these definitions are less 283 operational. 284 285 > They might be relevant for a closer analysis of fork scenarios on the 286 > chain, which is out of the scope of this specification. 287 288 #### **[CMBC-SIGN-UNIQUE.1]** 289 290 Let *b* and *c* be light blocks, we define the predicate 291 *sign-unique(b,c)* to evaluate to true iff the following implication 292 evaluates to true: 293 294 - *b.Header.Height = c.Header.Height* and 295 - *sequ-rooted(b)* and 296 - *sequ-rooted(c)* 297 298 implies *b = c*. 299 300 #### **[CMBC-BLOCKS-MCFORK.1]** 301 302 If there exists two light blocks b and c, with *sign-unique(b,c) = 303 false* then we have a *fork*. 304 305 > The difference of the above definition to 306 > [[CMBC-MC-FORK.1]](#CMBC-MC-FORK1) is subtle. The latter requires a 307 > full node being affected by a bad block while 308 > [[CMBC-BLOCKS-MCFORK.1]](#CMBC-BLOCKS-MCFORK1) just requires that a 309 > bad block exists, possibly in memory of an attacker. 310 > The following captures a light client fork. There is no fork up to 311 > the height of block b. However, c is of that height, is different, 312 > and passes skipping verification. It is a stricter property than 313 > [[CMBC-LC-ATTACK.1]](#CMBC-LC-ATTACK1), as 314 > [[CMBC-LC-ATTACK.1]](#CMBC-LC-ATTACK1) requires that no correct full 315 > node is affected. 316 317 #### **[CMBC-BLOCKS-LCFORK.1]** 318 319 Let *a*, *b*, *c*, be light blocks and *t* a time. We define 320 *light-client-fork(a,b,c,t)* iff 321 322 - *sign-skip-match(a,b,c,t) = false* and 323 - *sequ-rooted(b)* and 324 - *b* is "unique", that is, for all *d*, *sequ-rooted(d)* and 325 *d.Header.Height = b.Header.Height* implies *d = b* 326 327 > Finally, let us also define bogus blocks that have no support. 328 > Observe that bogus is even defined if there is a fork. 329 > Also, for the definition it would be sufficient to restrict *a* to 330 > *a.height < b.height* (which is implied by the definitions which 331 > unfold until *supports()*). 332 333 #### **[CMBC-BOGUS.1]** 334 335 Let *b* be a light block and *t* a time. We define *bogus(b,t)* iff 336 337 - *sequ-rooted(b) = false* and 338 - for all *a*, *sequ-rooted(a)* implies *skip-trace(a,b,t) = false* 339 340 ### Informal Problem statement 341 342 There is no sequential specification: the detector only makes sense 343 in a distributed systems where some nodes misbehave. 344 345 We work under the assumption that full nodes and validators are 346 responsible for detecting attacks on the main chain, and the evidence 347 reactor takes care of broadcasting evidence to communicate 348 misbehaving nodes via ABCI to the application, and halt the chain in 349 case of a fork. The point of this specification is to shield a light 350 clients against attacks that cannot be detected by full nodes, and 351 are fully addressed at light clients (and consequently IBC relayers, 352 which use the light client protocols to observe the state of a 353 blockchain). In order to provide full nodes the incentive to follow 354 the protocols when communicating with the light client, this 355 specification also considers the generation of evidence that will 356 also be processed by the Cosmos blockchain. 357 358 #### **[LCD-IP-MODEL.1]** 359 360 The detector is designed under the assumption that 361 362 - [[CMBC-FM-2THIRDS]](CMBC-FM-2THIRDS-link) may be violated 363 - there is no fork on the main chain. 364 365 > As a result some faulty full nodes may launch an attack on a light 366 > client. 367 368 The following requirements are operational in that they describe how 369 things should be done, rather than what should be done. However, they 370 do not constitute temporal logic verification conditions. For those, 371 see [LCD-DIST-*] below. 372 373 The detector is called in the [supervisor](supervisor) as follows 374 375 ```go 376 Evidences := AttackDetector(root_of_trust, verifiedLS);` 377 ``` 378 379 where 380 381 - `root-of-trust` is a light block that is trusted (that is, 382 except upon initialization, the primary and the secondaries 383 agreed on in the past), and 384 - `verifiedLS` is a lightstore that contains a verification trace that 385 starts from a lightblock that can be verified with the 386 `root-of-trust` in one step and ends with a lightblock of the height 387 requested by the user 388 - `Evidences` is a list of evidences for misbehavior 389 390 #### **[LCD-IP-STATEMENT.1]** 391 392 Whenever AttackDetector is called, the detector should for each 393 secondary try to replay the verification trace `verifiedLS` with the 394 secondary 395 396 - in case replaying leads to detection of a light client attack 397 (one of the lightblocks differ from the one in verifiedLS with 398 the same height), we should return evidence 399 - if the secondary cannot provide a verification trace, we have no 400 proof for an attack. Block *b* may be bogus. In this case the 401 secondary is faulty and it should be replaced. 402 403 ## Assumptions/Incentives/Environment 404 405 It is not in the interest of faulty full nodes to talk to the 406 detector as long as the detector is connected to at least one 407 correct full node. This would only increase the likelihood of 408 misbehavior being detected. Also we cannot punish them easily 409 (cheaply). The absence of a response need not be the fault of the full 410 node. 411 412 Correct full nodes have the incentive to respond, because the 413 detector may help them to understand whether their header is a good 414 one. We can thus base liveness arguments of the detector on 415 the assumptions that correct full nodes reliably talk to the 416 detector. 417 418 ### Assumptions 419 420 #### **[LCD-A-CorrFull.1]** 421 422 At all times there is at least one correct full 423 node among the primary and the secondaries. 424 425 > For this version of the detection we take this assumption. It 426 > allows us to establish the invariant that the lightblock 427 > `root-of-trust` is always the one from the blockchain, and we can 428 > use it as starting point for the evidence computation. Moreover, it 429 > allows us to establish the invariant at the supervisor that any 430 > lightblock in the (top-level) lightstore is from the blockchain. 431 > In the future we might design a lightclient based on the assumption 432 > that at least in regular intervals the lightclient is connected to a 433 > correct full node. This will require the detector to reconsider 434 > `root-of-trust`, and remove lightblocks from the top-level 435 > lightstore. 436 437 #### **[LCD-A-RelComm.1]** 438 439 Communication between the detector and a correct full node is 440 reliable and bounded in time. Reliable communication means that 441 messages are not lost, not duplicated, and eventually delivered. There 442 is a (known) end-to-end delay *Delta*, such that if a message is sent 443 at time *t* then it is received and processed by time *t + Delta*. 444 This implies that we need a timeout of at least *2 Delta* for remote 445 procedure calls to ensure that the response of a correct peer arrives 446 before the timeout expires. 447 448 ## Definitions 449 450 ### Evidence 451 452 Following the definition of 453 [[CMBC-LC-ATTACK-EVIDENCE.1]](#CMBC-LC-ATTACK-EVIDENCE1), by evidence 454 we refer to a variable of the following type 455 456 #### **[LC-DATA-EVIDENCE.1]** 457 458 ```go 459 type LightClientAttackEvidence struct { 460 ConflictingBlock LightBlock 461 CommonHeight int64 462 } 463 ``` 464 465 As the above data is computed for a specific peer, the following 466 data structure wraps the evidence and adds the peerID. 467 468 #### **[LC-DATA-EVIDENCE-INT.1]** 469 470 ```go 471 type InternalEvidence struct { 472 Evidence LightClientAttackEvidence 473 Peer PeerID 474 } 475 ``` 476 477 #### **[LC-SUMBIT-EVIDENCE.1]** 478 479 ```go 480 func submitEvidence(Evidences []InternalEvidence) 481 ``` 482 483 - Expected postcondition 484 - for each `ev` in `Evidences`: submit `ev.Evidence` to `ev.Peer` 485 486 --- 487 488 ### LightStore 489 490 Lightblocks and LightStores are defined in the verification 491 specification [LCV-DATA-LIGHTBLOCK.1] and [LCV-DATA-LIGHTSTORE.1]. See 492 the [verification specification][verification] for details. 493 494 ## (Distributed) Problem statement 495 496 > As the attack detector is there to reduce the impact of faulty 497 > nodes, and faulty nodes imply that there is a distributed system, 498 > there is no sequential specification to which this distributed 499 > problem statement may refer to. 500 501 The detector gets as input a trusted lightblock called *root* and an 502 auxiliary lightstore called *primary_trace* with lightblocks that have 503 been verified before, and that were provided by the primary. 504 505 #### **[LCD-DIST-INV-ATTACK.1]** 506 507 If the detector returns evidence for height *h* 508 [[CMBC-LC-EVIDENCE-DATA.1]](#CMBC-LC-EVIDENCE-DATA1), then there is an 509 attack at height *h*. [[CMBC-LC-ATTACK.1]](#CMBC-LC-ATTACK1) 510 511 #### **[LCD-DIST-INV-STORE.1]** 512 513 If the detector does not return evidence, then *primary_trace* 514 contains only blocks from the blockchain. 515 516 #### **[LCD-DIST-LIVE.1]** 517 518 The detector eventually terminates. 519 520 #### **[LCD-DIST-TERM-NORMAL.1]** 521 522 If 523 524 - the *primary_trace* contains only blocks from the blockchain, and 525 - there is no attack, and 526 - *Secondaries* is always non-empty, and 527 - the age of *root* is always less than the trusting period, 528 529 then the detector does not return evidence. 530 531 #### **[LCD-DIST-TERM-ATTACK.1]** 532 533 If 534 535 - there is an attack, and 536 - a secondary reports a block that conflicts 537 with one of the blocks in *primary_trace*, and 538 - *Secondaries* is always non-empty, and 539 - the age of *root* is always less than the trusting period, 540 541 then the detector returns evidence. 542 543 > Observe that above we require that "a secondary reports a block that 544 > conflicts". If there is an attack, but no secondary tries to launch 545 > it against the detector (or the message from the secondary is lost 546 > by the network), then there is nothing to detect for us. 547 548 #### **[LCD-DIST-SAFE-SECONDARY.1]** 549 550 No correct secondary is ever replaced. 551 552 #### **[LCD-DIST-SAFE-BOGUS.1]** 553 554 If 555 556 - a secondary reports a bogus lightblock, 557 - the age of *root* is always less than the trusting period, 558 559 then the secondary is replaced before the detector terminates. 560 561 > The above property is quite operational ("reports"), but it captures 562 > quite closely the requirement. As the 563 > detector only makes sense in a distributed setting, and does 564 > not have a sequential specification, less "pure" 565 > specification are acceptable. 566 567 # Protocol 568 569 ## Functions and Data defined in other Specifications 570 571 ### From the supervisor 572 573 ```go 574 Replace_Secondary(addr Address, root-of-trust LightBlock) 575 ``` 576 577 ### From the verifier 578 579 ```go 580 func VerifyToTarget(primary PeerID, root LightBlock, 581 targetHeight Height) (LightStore, Result) 582 ``` 583 584 > Note: the above differs from the current version in the second 585 > parameter. verification will be revised. 586 587 Observe that `VerifyToTarget` does communication with the secondaries 588 via the function [FetchLightBlock](fetch). 589 590 ### Shared data of the light client 591 592 - a pool of full nodes *FullNodes* that have not been contacted before 593 - peer set called *Secondaries* 594 - primary 595 596 > Note that the lightStore is not needed to be shared. 597 598 ## Outline 599 600 The problem laid out is solved by calling the function `AttackDetector` 601 with a lightstore that contains a light block that has just been 602 verified by the verifier. 603 604 Then `AttackDetector` downloads headers from the secondaries. In case 605 a conflicting header is downloaded from a secondary, 606 `CreateEvidenceForPeer` which computes evidence in the case that 607 indeed an attack is confirmed. It could be that the secondary reports 608 a bogus block, which means that there need not be an attack, and the 609 secondary is replaced. 610 611 ## Details of the functions 612 613 #### **[LCD-FUNC-DETECTOR.1]:** 614 615 ```go 616 func AttackDetector(root LightBlock, primary_trace []LightBlock) 617 ([]InternalEvidence) { 618 619 Evidences := new []InternalEvidence; 620 621 for each secondary in Secondaries { 622 // we replay the primary trace with the secondary, in 623 // order to generate evidence that we can submit to the 624 // secodary. We return the evidence + the trace the 625 // secondary told us that spans the evidence at its local store 626 627 EvidenceForSecondary, newroot, secondary_trace, result := 628 CreateEvidenceForPeer(secondary, 629 root, 630 primary_trace); 631 if result == FaultyPeer { 632 Replace_Secondary(root); 633 } 634 else if result == FoundEvidence { 635 // the conflict is not bogus 636 Evidences.Add(EvidenceForSecondary); 637 // we replay the secondary trace with the primary, ... 638 EvidenceForPrimary, _, result := 639 CreateEvidenceForPeer(primary, 640 newroot, 641 secondary_trace); 642 if result == FoundEvidence { 643 Evidences.Add(EvidenceForPrimary); 644 } 645 // At this point we do not care about the other error 646 // codes. We already have generated evidence for an 647 // attack and need to stop the lightclient. It does not 648 // help to call replace_primary. Also we will use the 649 // same primary to check with other secondaries in 650 // later iterations of the loop 651 } 652 // In the case where the secondary reports NoEvidence 653 // we do nothing 654 } 655 return Evidences; 656 } 657 ``` 658 659 - Expected precondition 660 - root and primary trace are a verification trace 661 - Expected postcondition 662 - solves the problem statement (if attack found, then evidence is reported) 663 - Error condition 664 - `ErrorTrustExpired`: fails if root expires (outside trusting 665 period) [[LCV-INV-TP.1]](LCV-INV-TP1-link) 666 - `ErrorNoPeers`: if no peers are left to replace secondaries, and 667 no evidence was found before that happened 668 669 --- 670 671 ```go 672 func CreateEvidenceForPeer(peer PeerID, root LightBlock, trace LightStore) 673 (Evidence, LightBlock, LightStore, result) { 674 675 common := root; 676 677 for i in 1 .. len(trace) { 678 auxLS, result := VerifyToTarget(peer, common, trace[i].Header.Height) 679 680 if result != ResultSuccess { 681 // something went wrong; peer did not provide a verifyable block 682 return (nil, nil, nil, FaultyPeer) 683 } 684 else { 685 if auxLS.LatestVerified().Header != trace[i].Header { 686 // the header reported by the peer differs from the 687 // reference header in trace but both could be 688 // verified from common in one step. 689 // we can create evidence for submission to the secondary 690 ev := new InternalEvidence; 691 ev.Evidence.ConflictingBlock := trace[i]; 692 ev.Evidence.CommonHeight := common.Height; 693 ev.Peer := peer 694 return (ev, common, auxLS, FoundEvidence) 695 } 696 else { 697 // the peer agrees with the trace, we move common forward 698 // we could delete auxLS as it will be overwritten in 699 // the next iteration 700 common := trace[i] 701 } 702 } 703 } 704 return (nil, nil, nil, NoEvidence) 705 } 706 ``` 707 708 - Expected precondition 709 - root and trace are a verification trace 710 - Expected postcondition 711 - finds evidence where trace and peer diverge 712 - Error condition 713 - `ErrorTrustExpired`: fails if root expires (outside trusting 714 period) [[LCV-INV-TP.1]](LCV-INV-TP1-link) 715 - If `VerifyToTarget` returns error but root is not expired then return 716 `FaultyPeer` 717 718 --- 719 720 ## Correctness arguments 721 722 #### Argument for [[LCD-DIST-INV-ATTACK.1]](#LCD-DIST-INV-ATTACK1) 723 724 Under the assumption that root and trace are a verification trace, 725 when in `CreateEvidenceForPeer` the detector the detector creates 726 evidence, then the lightclient has seen two different headers (one via 727 `trace` and one via `VerifyToTarget` for the same height that can both 728 be verified in one step. 729 730 #### Argument for [[LCD-DIST-INV-STORE.1]](#LCD-DIST-INV-STORE1) 731 732 We assume that there is at least one correct peer, and there is no 733 fork. As a result the correct peer has the correct sequence of 734 blocks. Since the primary_trace is checked block-by-block also against 735 each secondary, and at no point evidence was generated that means at 736 no point there were conflicting blocks. 737 738 #### Argument for [[LCD-DIST-LIVE.1]](#LCD-DIST-LIVE1) 739 740 At the latest when [[LCV-INV-TP.1]](LCV-INV-TP1-link) is violated, 741 `AttackDetector` terminates. 742 743 #### Argument for [[LCD-DIST-TERM-NORMAL.1]](#LCD-DIST-TERM-NORMAL1) 744 745 As there are finitely many peers, eventually the main loop 746 terminates. As there is no attack no evidence can be generated. 747 748 #### Argument for [[LCD-DIST-TERM-ATTACK.1]](#LCD-DIST-TERM-ATTACK1) 749 750 Argument similar to [[LCD-DIST-TERM-NORMAL.1]](#LCD-DIST-TERM-NORMAL1) 751 752 #### Argument for [[LCD-DIST-SAFE-SECONDARY.1]](#LCD-DIST-SAFE-SECONDARY1) 753 754 Secondaries are only replaced if they time-out or if they report bogus 755 blocks. The former is ruled out by the timing assumption, the latter 756 by correct peers only reporting blocks from the chain. 757 758 #### Argument for [[LCD-DIST-SAFE-BOGUS.1]](#LCD-DIST-SAFE-BOGUS1) 759 760 Once a bogus block is recognized as such the secondary is removed. 761 762 # References 763 764 > links to other specifications/ADRs this document refers to 765 766 [[verification]] The specification of the light client verification. 767 768 [[supervisor]] The specification of the light client supervisor. 769 770 [verification]: https://github.com/aakash4dev/cometbft/tree/main/spec/light-client/verification 771 772 [supervisor]: https://github.com/aakash4dev/cometbft/tree/main/spec/light-client/supervisor 773 774 775 776 777 778 [CMBC-VAL-CONTAINS-CORR-link]: 779 https://github.com/aakash4dev/cometbft/blob/main/spec/light-client/verification/verification_002_draft.md#cmbc-val-contains-corr1 780 781 [fetch]: 782 https://github.com/aakash4dev/cometbft/blob/main/spec/light-client/verification/verification_002_draft.md#lcv-func-fetch1 783 784 [LCV-INV-TP1-link]: 785 https://github.com/aakash4dev/cometbft/blob/main/spec/light-client/verification/verification_002_draft.md#lcv-inv-tp1