github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/docs/interfaces/lite/specification.md (about)

     1  # Specifications
     2  
     3  This specification describes how to implement the LCD. LCD supports modular APIs. Currently, only
     4  ICS0 (TendermintAPI), ICS1 (Key API) and ICS20 (Token API) are supported. Later, if necessary, more
     5  APIs can be included.
     6  
     7  ## Build and Verify Proof of ABCI States
     8  
     9  As we all know,  storage of cosmos-sdk based application contains multi-substores. Each substore is
    10  implemented by a IAVL store. These substores are organized by simple Merkle tree. To build the tree,
    11  we need to extract name, height and store root hash from these substores to build a set of simple
    12  Merkle leaf nodes, then calculate hash from leaf nodes to root. The root hash of the simple Merkle
    13  tree is the AppHash which will be included in block header.
    14  
    15  ![Simple Merkle Tree](./pics/simpleMerkleTree.png)
    16  
    17  As we have discussed in LCD trust-propagation,
    18  the AppHash can be verified by checking voting power against a trusted validator set. Here we just
    19  need to build proof from ABCI state to AppHash. The proof contains two parts:
    20  
    21  * IAVL proof
    22  * Substore to AppHash proof
    23  
    24  ### IAVL Proof
    25  
    26  The proof has two types: existence proof and absence proof. If the query key exists in the IAVL
    27  store, then it returns key-value and its existence proof. On the other hand, if the key doesn't
    28  exist, then it only returns absence proof which can demonstrate the key definitely doesn't exist.
    29  
    30  ### IAVL Existence Proof
    31  
    32  ```go
    33  type CommitID struct {
    34      Version int64
    35      Hash    []byte
    36  }
    37  
    38  type storeCore struct {
    39      CommitID CommitID
    40  }
    41  
    42  type MultiStoreCommitID struct {
    43      Name string
    44      Core storeCore
    45  }
    46  
    47  type proofInnerNode struct {
    48      Height  int8
    49      Size    int64
    50      Version int64
    51      Left    []byte
    52      Right   []byte
    53  }
    54  
    55  type KeyExistsProof struct {
    56      MultiStoreCommitInfo []MultiStoreCommitID //All substore commitIDs
    57      StoreName string //Current substore name
    58      Height  int64 //The commit height of current substore
    59      RootHash cmn.HexBytes //The root hash of this IAVL tree
    60      Version  int64 //The version of the key-value in this IAVL tree
    61      InnerNodes []proofInnerNode //The path from to root node to key-value leaf node
    62  }
    63  ```
    64  
    65  The data structure of exist proof is shown as above. The process to build and verify existence proof
    66  is shown as follows:
    67  
    68  ![Exist Proof](./pics/existProof.png)
    69  
    70  Steps to build proof:
    71  
    72  * Access the IAVL tree from the root node.
    73  * Record the visited nodes in InnerNodes,
    74  * Once the target leaf node is found, assign leaf node version to proof version
    75  * Assign the current IAVL tree height to proof height
    76  * Assign the current IAVL tree rootHash to proof rootHash
    77  * Assign the current substore name to proof StoreName
    78  * Read multistore commitInfo from db by height and assign it to proof StoreCommitInfo
    79  
    80  Steps to verify proof:
    81  
    82  * Build leaf node with key, value and proof version.
    83  * Calculate leaf node hash
    84  * Assign the hash to the first innerNode's rightHash, then calculate first innerNode hash
    85  * Propagate the hash calculation process. If prior innerNode is the left child of next innerNode, then assign the prior innerNode hash to the left hash of next innerNode. Otherwise, assign the prior innerNode hash to the right hash of next innerNode.
    86  * The hash of last innerNode should be equal to the rootHash of this proof. Otherwise, the proof is invalid.
    87  
    88  ### IAVL Absence Proof
    89  
    90  As we all know, all IAVL leaf nodes are sorted by the key of each leaf nodes. So we can calculate
    91  the position of the target key in the whole key set of this IAVL tree. As shown below, we can find
    92  out the left key and the right key. If we can demonstrate that both left key and right key
    93  definitely exist, and they are adjacent nodes. Thus the target key definitely doesn't exist.
    94  
    95  ![Absence Proof1](./pics/absence1.png)
    96  
    97  If the target key is larger than the right most leaf node or less than the left most key, then the
    98  target key definitely doesn't exist.
    99  
   100  ![Absence Proof2](./pics/absence2.png)![Absence Proof3](./pics/absence3.png)
   101  
   102  ```go
   103  type proofLeafNode struct {
   104      KeyBytes   cmn.HexBytes
   105      ValueBytes cmn.HexBytes
   106      Version    int64
   107  }
   108  
   109  type pathWithNode struct {
   110      InnerNodes []proofInnerNode
   111      Node proofLeafNode
   112  }
   113  
   114  type KeyAbsentProof struct {
   115      MultiStoreCommitInfo []MultiStoreCommitID
   116      StoreName string
   117      Height  int64
   118      RootHash cmn.HexBytes
   119      Left  *pathWithNode // Proof the left key exist
   120      Right *pathWithNode  //Proof the right key exist
   121  }
   122  ```
   123  
   124  The above is the data structure of absence proof. Steps to build proof:
   125  
   126  * Access the IAVL tree from the root node.
   127  * Get the deserved index(Marked as INDEX) of the key in whole key set.
   128  * If the returned index equals to 0, the right index should be 0 and left node doesn't exist
   129  * If the returned index equals to the size of the whole key set, the left node index should be INDEX-1 and the right node doesn't exist.
   130  * Otherwise, the right node index should be INDEX and the left node index should be INDEX-1
   131  * Assign the current IAVL tree height to proof height
   132  * Assign the current IAVL tree rootHash to proof rootHash
   133  * Assign the current substore name to proof StoreName
   134  * Read multistore commitInfo from db by height and assign it to proof StoreCommitInfo
   135  
   136  Steps to verify proof:
   137  
   138  * If only right node exist, verify its exist proof and verify if it is the left most node
   139  * If only left node exist, verify its exist proof and verify if it is the right most node.
   140  * If both right node and left node exist, verify if they are adjacent.
   141  
   142  ### Substores to AppHash Proof
   143  
   144  After verify the IAVL proof, then we can start to verify substore proof against AppHash. Firstly,
   145  iterate MultiStoreCommitInfo and find the substore commitID by proof StoreName. Verify if yhe Hash
   146  in commitID equals to proof RootHash. If not, the proof is invalid. Then sort the substore
   147  commitInfo array by the hash of substore name. Finally, build the simple Merkle tree with all
   148  substore commitInfo array and verify if the Merkle root hash equal to appHash.
   149  
   150  ![substore proof](./pics/substoreProof.png)
   151  
   152  ```go
   153  func SimpleHashFromTwoHashes(left []byte, right []byte) []byte {
   154      var hasher = ripemd160.New()
   155  
   156      err := encodeByteSlice(hasher, left)
   157      if err != nil {
   158          panic(err)
   159      }
   160  
   161      err = encodeByteSlice(hasher, right)
   162      if err != nil {
   163          panic(err)
   164      }
   165  
   166      return hasher.Sum(nil)
   167  }
   168  
   169  func SimpleHashFromHashes(hashes [][]byte) []byte {
   170      // Recursive impl.
   171      switch len(hashes) {
   172          case 0:
   173              return nil
   174          case 1:
   175              return hashes[0]
   176          default:
   177              left := SimpleHashFromHashes(hashes[:(len(hashes)+1)/2])
   178              right := SimpleHashFromHashes(hashes[(len(hashes)+1)/2:])
   179              return SimpleHashFromTwoHashes(left, right)
   180      }
   181  }
   182  ```
   183  
   184  ## Verify block header against validator set
   185  
   186  Above sections refer appHash frequently. But where does the trusted appHash come from? Actually,
   187  the appHash exist in block header, next we need to verify blocks header at specific height against
   188  LCD trusted validator set. The validation flow is shown as follows:
   189  
   190  ![commit verification](./pics/commitValidation.png)
   191  
   192  When the trusted validator set doesn't match the block header, we need to try to update our
   193  validator set to the height of this block. LCD has a rule that each validator set change should not
   194  affect more than 1/3 voting power. Compare with the trusted validator set, if the voting power of
   195  target validator set changes more than 1/3. We have to verify if there are hidden validator set
   196  changes before the target validator set. Only when all validator set changes obey this rule, can our
   197  validator set update be accomplished.
   198  
   199  For instance:
   200  
   201  ![Update validator set to height](./pics/updateValidatorToHeight.png)
   202  
   203  * Update to 10000, tooMuchChangeErr
   204  * Update to 5050,  tooMuchChangeErr
   205  * Update to 2575, Success
   206  * Update to 5050, Success
   207  * Update to 10000,tooMuchChangeErr
   208  * Update to 7525, Success
   209  * Update to 10000, Success