github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/spec/light-client/verification/verification_001_published.md (about)

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