|
|
- <!-- markdown-link-check-disable -->
-
- # Light Client Attack Detector
-
- In this specification, we strengthen the light client to be resistant
- against so-called light client attacks. In a light client attack, all
- the correct Tendermint full nodes agree on the sequence of generated
- blocks (no fork), but a set of faulty full nodes attack a light client
- by generating (signing) a block that deviates from the block of the
- same height on the blockchain. In order to do so, some of these faulty
- full nodes must have been validators before and violate the assumption
- of more than two thirds of "correct voting power"
- [[TMBC-FM-2THIRDS]][TMBC-FM-2THIRDS-link], as otherwise, if
- [[TMBC-FM-2THIRDS]][TMBC-FM-2THIRDS-link] would hold,
- [verification][verification] would satisfy
- [[LCV-SEQ-SAFE.1]][LCV-SEQ-SAFE-link].
-
- An attack detector (or detector for short) is a mechanism that is used
- by the light client [supervisor][supervisor] after
- [verification][verification] of a new light block
- with the primary, to cross-check the newly learned light block with
- other peers (secondaries). It expects as input a light block with some
- height *root* (that serves as a root of trust), and a verification
- trace (a sequence of lightblocks) that the primary provided.
-
- In case the detector observes a light client attack, it computes
- evidence data that can be used by Tendermint full nodes to isolate a
- set of faulty full nodes that are still within the unbonding period
- (more than 1/3 of the voting power of the validator set at some block
- of the chain), and report them via ABCI (application/blockchain
- interface)
- to the application of a
- Tendermint blockchain in order to punish faulty nodes.
-
- ## Context of this document
-
- The light client [verification][verification] specification is
- designed for the Tendermint failure model (1/3 assumption)
- [[TMBC-FM-2THIRDS]][TMBC-FM-2THIRDS-link]. It is safe under this
- assumption, and live if it can reliably (that is, no message loss, no
- duplication, and eventually delivered) and timely communicate with a
- correct full node. If [[TMBC-FM-2THIRDS]][TMBC-FM-2THIRDS-link]
- assumption is violated, the light client can be fooled to trust a
- light block that was not generated by Tendermint consensus.
-
- This specification, the attack detector, is a "second line of
- defense", in case the 1/3 assumption is violated. Its goal is to
- detect a light client attack (conflicting light blocks) and collect
- evidence. However, it is impractical to probe all full nodes. At this
- time we consider a simple scheme of maintaining an address book of
- known full nodes from which a small subset (e.g., 4) are chosen
- initially to communicate with. More involved book keeping with
- probabilistic guarantees can be considered at later stages of the
- project.
-
- The light client maintains a simple address book containing addresses
- of full nodes that it can pick as primary and secondaries. To obtain
- a new light block, the light client first does
- [verification][verification] with the primary, and then cross-checks
- the light block (and the trace of light blocks that led to it) with
- the secondaries using this specification.
-
- # Outline
-
- - [Part I](#part-i---Tendermint-Consensus-and-Light-Client-Attacks):
- Formal definitions of lightclient attacks, based on basic
- properties of Tendermint consensus.
- - [Node-based characterization of
- attacks](#Node-based-characterization-of-attacks). The
- definition of attacks used in the problem statement of
- this specification.
-
- - [Block-based characterization of attacks](#Block-based-characterization-of-attacks). Alternative definitions
- provided for future reference.
-
- - [Part II](#part-ii---problem-statement): Problem statement of
- lightclient attack detection
-
- - [Informal Problem Statement](#informal-problem-statement)
- - [Assumptions](#Assumptions)
- - [Definitions](#definitions)
- - [Distributed Problem statement](#Distributed-Problem-statement)
-
- - [Part III](#part-iii---protocol): The protocol
-
- - [Functions and Data defined in other Specifications](#Functions-and-Data-defined-in-other-Specifications)
- - [Outline of Solution](#Outline-of-solution)
- - [Details of the functions](#Details-of-the-functions)
- - [Correctness arguments](#Correctness-arguments)
-
- # Part I - Tendermint Consensus and Light Client Attacks
-
- In this section we will give some mathematical definitions of what we
- mean by light client attacks (that are considered in this
- specification) and how they differ from main-chain forks. To this end,
- we start by defining some properties of the sequence of blocks that is
- decided upon by Tendermint consensus in normal operation (if the
- Tendermint failure model holds
- [[TMBC-FM-2THIRDS]][TMBC-FM-2THIRDS-link]),
- and then define different
- deviations that correspond to attack scenarios. We consider the notion
- of [light blocks][LCV-LB-link] and [headers][LVC-HD-link].
-
- #### **[TMBC-GENESIS.1]**
-
- Let *Genesis* be the agreed-upon initial block (file).
-
- #### **[TMBC-FUNC-SIGN.1]**
-
- Let *b* and *c* be two light blocks with *b.Header.Height + 1 =
- c.Header.Height*. We define the predicate **signs(b,c)** to hold
- iff *c.Header.LastCommit* is in *PossibleCommit(b)*.
- [[TMBC-SOUND-DISTR-POSS-COMMIT.1]][TMBC-SOUND-DISTR-POSS-COMMIT-link].
-
- > The above encodes sequential verification, that is, intuitively,
- > b.Header.NextValidators = c.Header.Validators and 2/3 of
- > these Validators signed c.
-
- #### **[TMBC-FUNC-SUPPORT.1]**
-
- Let *b* and *c* be two light blocks. We define the predicate
- **supports(b,c,t)** to hold iff
-
- - *t - trustingPeriod < b.Header.Time < t*
- - the voting power in *b.NextValidators* of nodes in *c.Commit*
- is more than 1/3 of *TotalVotingPower(b.Header.NextValidators)*
-
- > That is, if the [Tendermint failure model][TMBC-FM-2THIRDS-link]
- > holds, then *c* has been signed by at least one correct full node, cf.
- > [[TMBC-VAL-CONTAINS-CORR.1]][TMBC-VAL-CONTAINS-CORR-link].
- > The following formalizes that *b* was properly generated by
- > Tendermint; *b* can be traced back to genesis.
-
- #### **[TMBC-SEQ-ROOTED.1]**
-
- Let *b* be a light block.
- We define *sequ-rooted(b)* iff for all *i*, *1 <= i < h = b.Header.Height*,
- there exist light blocks *a(i)* s.t.
-
- - *a(1) = Genesis* and
- - *a(h) = b* and
- - *signs( a(i) , a(i+1) )*.
-
- > The following formalizes that *c* is trusted based on *b* in
- > skipping verification. Observe that we do not require here (yet)
- > that *b* was properly generated.
-
- #### **[TMBC-SKIP-TRACE.1]**
-
- Let *b* and *c* be light blocks. We define *skip-trace(b,c,t)* if at
- time t there exists an integer *h* and a sequence *a(1)*, ... *a(h)* s.t.
-
- - *a(1) = b* and
- - *a(h) = c* and
- - *supports( a(i), a(i+1), t)*, for all i, *1 <= i < h*.
-
- We call such a sequence *a(1)*, ... *a(h)* a **verification trace**.
-
- > The following formalizes that two light blocks of the same height
- > should agree on the content of the header. Observe that *b* and *c*
- > may disagree on the Commit. This is a special case if the canonical
- > commit has not been decided on yet, that is, if b.Header.Height is the
- > maximum height of all blocks decided upon by Tendermint at this
- > moment.
-
- #### **[TMBC-SIGN-SKIP-MATCH.1]**
-
- Let *a*, *b*, *c*, be light blocks and *t* a time, we define
- *sign-skip-match(a,b,c,t) = true* iff the following implication
- evaluates to true:
-
- - *sequ-rooted(a)* and
- - *b.Header.Height = c.Header.Height* and
- - *skip-trace(a,b,t)*
- - *skip-trace(a,c,t)*
-
- implies *b.Header = c.Header*.
-
- > Observe that *sign-skip-match* is defined via an implication. If it
- > evaluates to false this means that the left-hand-side of the
- > implication evaluates to true, and the right-hand-side evaluates to
- > false. In particular, there are two **different** headers *b* and
- > *c* that both can be verified from a common block *a* from the
- > chain. Thus, the following describes an attack.
-
- #### **[TMBC-ATTACK.1]**
-
- If there exists three light blocks a, b, and c, with
- *sign-skip-match(a,b,c,t) = false* then we have an *attack*. We say
- we have **an attack at height** *b.Header.Height* and write
- *attack(a,b,c,t)*.
-
- > The lightblock *a* need not be unique, that is, there may be
- > several blocks that satisfy the above requirement for the same
- > blocks *b* and *c*.
-
- [[TMBC-ATTACK.1]](#TMBC-ATTACK1) is a formalization of the violation
- of the agreement property based on the result of consensus, that is,
- the generated blocks.
-
- **Remark.**
- Violation of agreement is only possible if more than 1/3 of the validators (or
- next validators) of some previous block deviated from the protocol. The
- upcoming "accountability" specification will describe how to compute
- a set of at least 1/3 faulty nodes from two conflicting blocks. []
-
- There are different ways to characterize forks
- and attack scenarios. This specification uses the "node-based
- characterization of attacks" which focuses on what kinds of nodes are
- affected (light nodes vs. full nodes). For future reference and
- discussion we also provide a
- "block-based characterization of attacks" below.
-
- ## Node-based characterization of attacks
-
- #### **[TMBC-MC-FORK.1]**
-
- We say there is a (main chain) fork at time *t* if
-
- - there are two correct full nodes *i* and *j* and
- - *i* is different from *j* and
- - *i* has decided on *b* and
- - *j* has decided on *c* and
- - there exist *a* such that *attack(a,b,c,t)*.
-
- #### **[TMBC-LC-ATTACK.1]**
-
- We say there is a light client attack at time *t*, if
-
- - there is **no** (main chain) fork [[TMBC-MC-FORK.1]](#TMBC-MC-FORK1), and
- - there exist nodes that have computed light blocks *b* and *c* and
- - there exist *a* such that *attack(a,b,c,t)*.
-
- We say the attack is at height *a.Header.Height*.
-
- > In this specification we consider detection of light client
- > attacks. Intuitively, the case we consider is that
- > light block *b* is the one from the
- > blockchain, and some attacker has computed *c* and tries to wrongly
- > convince
- > the light client that *c* is the block from the chain.
-
- #### **[TMBC-LC-ATTACK-EVIDENCE.1]**
-
- We consider the following case of a light client attack
- [[TMBC-LC-ATTACK.1]](#TMBC-LC-ATTACK1):
-
- - *attack(a,b,c,t)*
- - there is a peer p1 that has a sequence *chain* of blocks from *a* to *b*
- - *skip-trace(a,c,t)*: by [[TMBC-SKIP-TRACE.1]](#TMBC-SKIP-TRACE1) there is a
- verification trace *v* of the form *a = v(1)*, ... *v(h) = c*
-
- Evidence for p1 (that proves an attack to p1) consists for index i
- of v(i) and v(i+1) such that
-
- - E1(i). v(i) is equal to the block of *chain* at height v(i).Height, and
- - E2(i). v(i+1) that is different from the block of *chain* at
- height v(i+1).height
-
- > Observe p1 can
- >
- > - check that v(i+1) differs from its block at that height, and
- > - verify v(i+1) in one step from v(i) as v is a verification trace.
-
- #### **[TMBC-LC-EVIDENCE-DATA.1]**
-
- To prove the attack to p1, because of Point E1, it is sufficient to
- submit
-
- - v(i).Height (rather than v(i)).
- - v(i+1)
-
- This information is *evidence for height v(i).Height*.
-
- ## Block-based characterization of attacks
-
- In this section we provide a different characterization of attacks. It
- is not defined on the nodes that are affected but purely on the
- content of the blocks. In that sense these definitions are less
- operational.
-
- > They might be relevant for a closer analysis of fork scenarios on the
- > chain, which is out of the scope of this specification.
-
- #### **[TMBC-SIGN-UNIQUE.1]**
-
- Let *b* and *c* be light blocks, we define the predicate
- *sign-unique(b,c)* to evaluate to true iff the following implication
- evaluates to true:
-
- - *b.Header.Height = c.Header.Height* and
- - *sequ-rooted(b)* and
- - *sequ-rooted(c)*
-
- implies *b = c*.
-
- #### **[TMBC-BLOCKS-MCFORK.1]**
-
- If there exists two light blocks b and c, with *sign-unique(b,c) =
- false* then we have a *fork*.
-
- > The difference of the above definition to
- > [[TMBC-MC-FORK.1]](#TMBC-MC-FORK1) is subtle. The latter requires a
- > full node being affected by a bad block while
- > [[TMBC-BLOCKS-MCFORK.1]](#TMBC-BLOCKS-MCFORK1) just requires that a
- > bad block exists, possibly in memory of an attacker.
- > The following captures a light client fork. There is no fork up to
- > the height of block b. However, c is of that height, is different,
- > and passes skipping verification. It is a stricter property than
- > [[TMBC-LC-ATTACK.1]](#TMBC-LC-ATTACK1), as
- > [[TMBC-LC-ATTACK.1]](#TMBC-LC-ATTACK1) requires that no correct full
- > node is affected.
-
- #### **[TMBC-BLOCKS-LCFORK.1]**
-
- Let *a*, *b*, *c*, be light blocks and *t* a time. We define
- *light-client-fork(a,b,c,t)* iff
-
- - *sign-skip-match(a,b,c,t) = false* and
- - *sequ-rooted(b)* and
- - *b* is "unique", that is, for all *d*, *sequ-rooted(d)* and
- *d.Header.Height = b.Header.Height* implies *d = b*
-
- > Finally, let us also define bogus blocks that have no support.
- > Observe that bogus is even defined if there is a fork.
- > Also, for the definition it would be sufficient to restrict *a* to
- > *a.height < b.height* (which is implied by the definitions which
- > unfold until *supports()*).
-
- #### **[TMBC-BOGUS.1]**
-
- Let *b* be a light block and *t* a time. We define *bogus(b,t)* iff
-
- - *sequ-rooted(b) = false* and
- - for all *a*, *sequ-rooted(a)* implies *skip-trace(a,b,t) = false*
-
- # Part II - Problem Statement
-
- ## Informal Problem statement
-
- There is no sequential specification: the detector only makes sense
- in a distributed systems where some nodes misbehave.
-
- We work under the assumption that full nodes and validators are
- responsible for detecting attacks on the main chain, and the evidence
- reactor takes care of broadcasting evidence to communicate
- misbehaving nodes via ABCI to the application, and halt the chain in
- case of a fork. The point of this specification is to shield a light
- clients against attacks that cannot be detected by full nodes, and
- are fully addressed at light clients (and consequently IBC relayers,
- which use the light client protocols to observe the state of a
- blockchain). In order to provide full nodes the incentive to follow
- the protocols when communicating with the light client, this
- specification also considers the generation of evidence that will
- also be processed by the Tendermint blockchain.
-
- #### **[LCD-IP-MODEL.1]**
-
- The detector is designed under the assumption that
-
- - [[TMBC-FM-2THIRDS]][TMBC-FM-2THIRDS-link] may be violated
- - there is no fork on the main chain.
-
- > As a result some faulty full nodes may launch an attack on a light
- > client.
-
- The following requirements are operational in that they describe how
- things should be done, rather than what should be done. However, they
- do not constitute temporal logic verification conditions. For those,
- see [LCD-DIST-*] below.
-
- The detector is called in the [supervisor][supervisor] as follows
-
- ```go
- Evidences := AttackDetector(root_of_trust, verifiedLS);`
- ```
-
- where
-
- - `root-of-trust` is a light block that is trusted (that is,
- except upon initialization, the primary and the secondaries
- agreed on in the past), and
- - `verifiedLS` is a lightstore that contains a verification trace that
- starts from a lightblock that can be verified with the
- `root-of-trust` in one step and ends with a lightblock of the height
- requested by the user
- - `Evidences` is a list of evidences for misbehavior
-
- #### **[LCD-IP-STATEMENT.1]**
-
- Whenever AttackDetector is called, the detector should for each
- secondary cross check the largest header in verifiedLS with the
- corresponding header of the same height provided by the secondary. If
- there is a deviation, the detector should
- try to replay the verification trace `verifiedLS` with the
- secondary
-
- - in case replaying leads to detection of a light client attack
- (one of the lightblocks differ from the one in verifiedLS with
- the same height), we should return evidence
- - if the secondary cannot provide a verification trace, we have no
- proof for an attack. Block *b* may be bogus. In this case the
- secondary is faulty and it should be replaced.
-
- ## Assumptions
-
- It is not in the interest of faulty full nodes to talk to the
- detector as long as the detector is connected to at least one
- correct full node. This would only increase the likelihood of
- misbehavior being detected. Also we cannot punish them easily
- (cheaply). The absence of a response need not be the fault of the full
- node.
-
- Correct full nodes have the incentive to respond, because the
- detector may help them to understand whether their header is a good
- one. We can thus base liveness arguments of the detector on
- the assumptions that correct full nodes reliably talk to the
- detector.
-
- #### **[LCD-A-CorrFull.1]**
-
- At all times there is at least one correct full
- node among the primary and the secondaries.
-
- > For this version of the detection we take this assumption. It
- > allows us to establish the invariant that the lightblock
- > `root-of-trust` is always the one from the blockchain, and we can
- > use it as starting point for the evidence computation. Moreover, it
- > allows us to establish the invariant at the supervisor that any
- > lightblock in the (top-level) lightstore is from the blockchain.
- > In the future we might design a lightclient based on the assumption
- > that at least in regular intervals the lightclient is connected to a
- > correct full node. This will require the detector to reconsider
- > `root-of-trust`, and remove lightblocks from the top-level
- > lightstore.
-
- #### **[LCD-A-RelComm.1]**
-
- Communication between the detector and a correct full node is
- reliable and bounded in time. Reliable communication means that
- messages are not lost, not duplicated, and eventually delivered. There
- is a (known) end-to-end delay *Delta*, such that if a message is sent
- at time *t* then it is received and processed by time *t + Delta*.
- This implies that we need a timeout of at least *2 Delta* for remote
- procedure calls to ensure that the response of a correct peer arrives
- before the timeout expires.
-
- ## Definitions
-
- ### Evidence
-
- Following the definition of
- [[TMBC-LC-ATTACK-EVIDENCE.1]](#TMBC-LC-ATTACK-EVIDENCE1), by evidence
- we refer to a variable of the following type
-
- #### **[LC-DATA-EVIDENCE.1]**
-
- ```go
- type LightClientAttackEvidence struct {
- ConflictingBlock LightBlock
- CommonHeight int64
-
- // Evidence also includes application specific data which is not
- // part of verification but is sent to the application once the
- // evidence gets committed on chain.
- }
- ```
-
- As the above data is computed for a specific peer, the following
- data structure wraps the evidence and adds the peerID.
-
- #### **[LC-DATA-EVIDENCE-INT.1]**
-
- ```go
- type InternalEvidence struct {
- Evidence LightClientAttackEvidence
- Peer PeerID
- }
- ```
-
- #### **[LC-SUMBIT-EVIDENCE.1]**
-
- ```go
- func submitEvidence(Evidences []InternalEvidence)
- ```
-
- - Expected postcondition
- - for each `ev` in `Evidences`: submit `ev.Evidence` to `ev.Peer`
-
- ---
-
- ### LightStore
-
- Lightblocks and LightStores are defined in the verification
- specification [[LCV-DATA-LIGHTBLOCK.1]][LCV-LB-link]
- and [[LCV-DATA-LIGHTSTORE.2]][LCV-LS-link]. See
- the [verification specification][verification] for details.
-
- ## Distributed Problem statement
-
- > As the attack detector is there to reduce the impact of faulty
- > nodes, and faulty nodes imply that there is a distributed system,
- > there is no sequential specification to which this distributed
- > problem statement may refer to.
-
- The detector gets as input a trusted lightblock called *root* and an
- auxiliary lightstore called *primary_trace* with lightblocks that have
- been verified before, and that were provided by the primary.
-
- #### **[LCD-DIST-INV-ATTACK.1]**
-
- If the detector returns evidence for height *h*
- [[TMBC-LC-EVIDENCE-DATA.1]](#TMBC-LC-EVIDENCE-DATA1), then there is an
- attack at height *h*. [[TMBC-LC-ATTACK.1]](#TMBC-LC-ATTACK1)
-
- #### **[LCD-DIST-INV-STORE.1]**
-
- If the detector does not return evidence, then *primary_trace*
- contains only blocks from the blockchain.
-
- #### **[LCD-DIST-LIVE.1]**
-
- The detector eventually terminates.
-
- #### **[LCD-DIST-TERM-NORMAL.1]**
-
- If
-
- - the *primary_trace* contains only blocks from the blockchain, and
- - there is no attack, and
- - *Secondaries* is always non-empty, and
- - the age of *root* is always less than the trusting period,
-
- then the detector does not return evidence.
-
- #### **[LCD-DIST-TERM-ATTACK.1]**
-
- If
-
- - there is an attack, and
- - a secondary reports a block that conflicts
- with one of the blocks in *primary_trace*, and
- - *Secondaries* is always non-empty, and
- - the age of *root* is always less than the trusting period,
-
- then the detector returns evidence.
-
- > Observe that above we require that "a secondary reports a block that
- > conflicts". If there is an attack, but no secondary tries to launch
- > it against the detector (or the message from the secondary is lost
- > by the network), then there is nothing to detect for us.
-
- #### **[LCD-DIST-SAFE-SECONDARY.1]**
-
- No correct secondary is ever replaced.
-
- #### **[LCD-DIST-SAFE-BOGUS.1]**
-
- If
-
- - a secondary reports a bogus lightblock,
- - the age of *root* is always less than the trusting period,
-
- then the secondary is replaced before the detector terminates.
-
- > The above property is quite operational (e.g., the usage of
- > "reports"), but it captures closely the requirement. As the
- > detector only makes sense in a distributed setting, and does not
- > have a sequential specification, a less "pure" specification are
- > acceptable.
-
- # Part III - Protocol
-
- ## Functions and Data defined in other Specifications
-
- ### From the [supervisor][supervisor]
-
- [[LC-FUNC-REPLACE-SECONDARY.1]][repl]
-
- ```go
- Replace_Secondary(addr Address, root-of-trust LightBlock)
- ```
-
- ### From the [verifier][verification]
-
- [[LCV-FUNC-MAIN.2]][vtt]
-
- ```go
- func VerifyToTarget(primary PeerID, root LightBlock,
- targetHeight Height) (LightStore, Result)
- ```
-
- Observe that `VerifyToTarget` does communication with the secondaries
- via the function [FetchLightBlock][fetch].
-
- ### Shared data of the light client
-
- - a pool of full nodes *FullNodes* that have not been contacted before
- - peer set called *Secondaries*
- - primary
-
- > Note that the lightStore is not needed to be shared.
-
- ## Outline of solution
-
- The problem laid out is solved by calling the function `AttackDetector`
- with a lightstore that contains a light block that has just been
- verified by the verifier.
-
- Then `AttackDetector` downloads headers from the secondaries. In case
- a conflicting header is downloaded from a secondary, it calls
- `CreateEvidenceForPeer` which computes evidence in the case that
- indeed an attack is confirmed. It could be that the secondary reports
- a bogus block, which means that there need not be an attack, and the
- secondary is replaced.
-
- ## Details of the functions
-
- #### **[LCD-FUNC-DETECTOR.2]:**
-
- ```go
- func AttackDetector(root LightBlock, primary_trace []LightBlock)
- ([]InternalEvidence) {
-
- Evidences := new []InternalEvidence;
-
- for each secondary in Secondaries {
- lb, result := FetchLightBlock(secondary,primary_trace.Latest().Header.Height);
- if result != ResultSuccess {
- Replace_Secondary(root);
- }
- else if lb.Header != primary_trace.Latest().Header {
-
- // we replay the primary trace with the secondary, in
- // order to generate evidence that we can submit to the
- // secondary. We return the evidence + the trace the
- // secondary told us that spans the evidence at its local store
-
- EvidenceForSecondary, newroot, secondary_trace, result :=
- CreateEvidenceForPeer(secondary,
- root,
- primary_trace);
- if result == FaultyPeer {
- Replace_Secondary(root);
- }
- else if result == FoundEvidence {
- // the conflict is not bogus
- Evidences.Add(EvidenceForSecondary);
- // we replay the secondary trace with the primary, ...
- EvidenceForPrimary, _, result :=
- CreateEvidenceForPeer(primary,
- newroot,
- secondary_trace);
- if result == FoundEvidence {
- Evidences.Add(EvidenceForPrimary);
- }
- // At this point we do not care about the other error
- // codes. We already have generated evidence for an
- // attack and need to stop the lightclient. It does not
- // help to call replace_primary. Also we will use the
- // same primary to check with other secondaries in
- // later iterations of the loop
- }
- // In the case where the secondary reports NoEvidence
- // after initially it reported a conflicting header.
- // secondary is faulty
- Replace_Secondary(root);
- }
- }
- return Evidences;
- }
- ```
-
- - Expected precondition
- - root and primary trace are a verification trace
- - Expected postcondition
- - solves the problem statement (if attack found, then evidence is reported)
- - Error condition
- - `ErrorTrustExpired`: fails if root expires (outside trusting
- period) [[LCV-INV-TP.1]][LCV-INV-TP1-link]
- - `ErrorNoPeers`: if no peers are left to replace secondaries, and
- no evidence was found before that happened
-
- ---
-
- ```go
- func CreateEvidenceForPeer(peer PeerID, root LightBlock, trace LightStore)
- (Evidence, LightBlock, LightStore, result) {
-
- common := root;
-
- for i in 1 .. len(trace) {
- auxLS, result := VerifyToTarget(peer, common, trace[i].Header.Height)
-
- if result != ResultSuccess {
- // something went wrong; peer did not provide a verifiable block
- return (nil, nil, nil, FaultyPeer)
- }
- else {
- if auxLS.LatestVerified().Header != trace[i].Header {
- // the header reported by the peer differs from the
- // reference header in trace but both could be
- // verified from common in one step.
- // we can create evidence for submission to the secondary
- ev := new InternalEvidence;
- ev.Evidence.ConflictingBlock := trace[i];
- // CommonHeight is used to indicate the type of attack
- // if the CommonHeight != ConflictingBlock.Height this
- // is by definition a lunatic attack else it is an
- // equivocation attack
- ev.Evidence.CommonHeight := common.Height;
- ev.Peer := peer
- return (ev, common, auxLS, FoundEvidence)
- }
- else {
- // the peer agrees with the trace, we move common forward.
- // we could delete auxLS as it will be overwritten in
- // the next iteration
- common := trace[i]
- }
- }
- }
- return (nil, nil, nil, NoEvidence)
- }
- ```
-
- - Expected precondition
- - root and trace are a verification trace
- - Expected postcondition
- - finds evidence where trace and peer diverge
- - Error condition
- - `ErrorTrustExpired`: fails if root expires (outside trusting
- period) [[LCV-INV-TP.1]][LCV-INV-TP1-link]
- - If `VerifyToTarget` returns error but root is not expired then return
- `FaultyPeer`
-
- ---
-
- ## Correctness arguments
-
- #### On the existence of evidence
-
- **Proposition.** In the case of attack,
- evidence [[TMBC-LC-ATTACK-EVIDENCE.1]](#TMBC-LC-ATTACK-EVIDENCE1)
- exists.
- *Proof.* First observe that
-
- - (A). (NOT E2(i)) implies E1(i+1)
-
- Now by contradiction assume there is no evidence. Thus
-
- - for all i, we have NOT E1(i) or NOT E2(i)
- - for i = 1 we have E1(1) and thus NOT E2(1)
- thus by induction on i, by (A) we have for all i that **E1(i)**
- - from attack we have E2(h-1), and as there is no evidence for
- i = h - 1 we get **NOT E1(h-1)**. Contradiction.
- QED.
-
- #### Argument for [[LCD-DIST-INV-ATTACK.1]](#LCD-DIST-INV-ATTACK1)
-
- Under the assumption that root and trace are a verification trace,
- when in `CreateEvidenceForPeer` the detector creates
- evidence, then the lightclient has seen two different headers (one via
- `trace` and one via `VerifyToTarget`) for the same height that can both
- be verified in one step.
-
- #### Argument for [[LCD-DIST-INV-STORE.1]](#LCD-DIST-INV-STORE1)
-
- We assume that there is at least one correct peer, and there is no
- fork. As a result, the correct peer has the correct sequence of
- blocks. Since the primary_trace is checked block-by-block also against
- each secondary, and at no point evidence was generated that means at
- no point there were conflicting blocks.
-
- #### Argument for [[LCD-DIST-LIVE.1]](#LCD-DIST-LIVE1)
-
- At the latest when [[LCV-INV-TP.1]][LCV-INV-TP1-link] is violated,
- `AttackDetector` terminates.
-
- #### Argument for [[LCD-DIST-TERM-NORMAL.1]](#LCD-DIST-TERM-NORMAL1)
-
- As there are finitely many peers, eventually the main loop
- terminates. As there is no attack no evidence can be generated.
-
- #### Argument for [[LCD-DIST-TERM-ATTACK.1]](#LCD-DIST-TERM-ATTACK1)
-
- Argument similar to [[LCD-DIST-TERM-NORMAL.1]](#LCD-DIST-TERM-NORMAL1)
-
- #### Argument for [[LCD-DIST-SAFE-SECONDARY.1]](#LCD-DIST-SAFE-SECONDARY1)
-
- Secondaries are only replaced if they time-out or if they report bogus
- blocks. The former is ruled out by the timing assumption, the latter
- by correct peers only reporting blocks from the chain.
-
- #### Argument for [[LCD-DIST-SAFE-BOGUS.1]](#LCD-DIST-SAFE-BOGUS1)
-
- Once a bogus block is recognized as such the secondary is removed.
-
- # References
-
- > links to other specifications/ADRs this document refers to
-
- [[verification]] The specification of the light client verification.
-
- [[supervisor]] The specification of the light client supervisor.
-
- [verification]: https://github.com/tendermint/spec/blob/master/rust-spec/lightclient/verification/verification_002_draft.md
-
- [supervisor]: https://github.com/tendermint/spec/blob/master/rust-spec/lightclient/supervisor/supervisor_001_draft.md
-
- [block]: https://github.com/tendermint/spec/blob/d46cd7f573a2c6a2399fcab2cde981330aa63f37/spec/core/data_structures.md
-
- [TMBC-FM-2THIRDS-link]: https://github.com/tendermint/spec/blob/master/rust-spec/lightclient/verification/verification_002_draft.md#tmbc-fm-2thirds1
-
- [TMBC-SOUND-DISTR-POSS-COMMIT-link]: https://github.com/tendermint/spec/blob/master/rust-spec/lightclient/verification/verification_002_draft.md#tmbc-sound-distr-poss-commit1
-
- [LCV-SEQ-SAFE-link]:https://github.com/tendermint/spec/blob/master/rust-spec/lightclient/verification/verification_002_draft.md#lcv-seq-safe1
-
- [TMBC-VAL-CONTAINS-CORR-link]:
- https://github.com/tendermint/spec/blob/master/rust-spec/lightclient/verification/verification_002_draft.md#tmbc-val-contains-corr1
-
- [fetch]:
- https://github.com/tendermint/spec/blob/master/rust-spec/lightclient/verification/verification_002_draft.md#lcv-func-fetch1
-
- [LCV-INV-TP1-link]:
- https://github.com/tendermint/spec/blob/master/rust-spec/lightclient/verification/verification_002_draft.md#lcv-inv-tp1
-
- [LCV-LB-link]:
- https://github.com/tendermint/spec/blob/master/rust-spec/lightclient/verification/verification_002_draft.md#lcv-data-lightblock1
-
- [LCV-LS-link]:
- https://github.com/tendermint/spec/blob/master/rust-spec/lightclient/verification/verification_002_draft.md#lcv-data-lightstore2
-
- [LVC-HD-link]:
- https://github.com/tendermint/spec/blob/master/rust-spec/lightclient/verification/verification_002_draft.md#tmbc-header-fields2
-
- [repl]:
- https://github.com/tendermint/spec/blob/master/rust-spec/lightclient/supervisor/supervisor_001_draft.md#lc-func-replace-secondary1
-
- [vtt]:
- https://github.com/tendermint/spec/blob/master/rust-spec/lightclient/verification/verification_002_draft.md#lcv-func-main2
|