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  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  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  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  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  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  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  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