github.com/aakash4dev/cometbft@v0.38.2/spec/light-client/detection/draft-functions.md (about) 1 # Draft of Functions for Fork detection and Proof of Fork Submisstion 2 3 This document collects drafts of function for generating and 4 submitting proof of fork in the IBC context 5 6 - [IBC](#on-chain-ibc-component) 7 8 - [Relayer](#relayer) 9 10 ## On-chain IBC Component 11 12 > The following is a suggestions to change the function defined in ICS 007 13 14 #### [TAG-IBC-MISBEHAVIOR.1] 15 16 ```go 17 func checkMisbehaviorAndUpdateState(cs: ClientState, PoF: LightNodeProofOfFork) 18 ``` 19 20 **TODO:** finish conditions 21 22 - Implementation remark 23 - Expected precondition 24 - PoF.TrustedBlock.Header is equal to lightBlock on store with 25 same height 26 - both traces end with header of same height 27 - headers are different 28 - both traces are supported by PoF.TrustedBlock (`supports` 29 defined in [CMBC-FUNC]), that is, for `t = currentTimestamp()` (see 30 ICS 024) 31 - supports(PoF.TrustedBlock, PoF.PrimaryTrace[1], t) 32 - supports(PoF.PrimaryTrace[i], PoF.PrimaryTrace[i+1], t) for 33 *0 < i < length(PoF.PrimaryTrace)* 34 - supports(PoF.TrustedBlock, PoF.SecondaryTrace[1], t) 35 - supports(PoF.SecondaryTrace[i], PoF.SecondaryTrace[i+1], t) for 36 *0 < i < length(PoF.SecondaryTrace)* 37 - Expected postcondition 38 - set cs.FrozenHeight to min(cs.FrozenHeight, PoF.TrustedBlock.Header.Height) 39 - Error condition 40 - none 41 42 ---- 43 44 > The following is a suggestions to add functionality to ICS 002 and 007. 45 > I suppose the above is the most efficient way to get the required 46 > information. Another option is to subscribe to "header install" 47 > events via CosmosSDK 48 49 #### [TAG-IBC-HEIGHTS.1] 50 51 ```go 52 func QueryHeightsRange(id, from, to) ([]Height) 53 ``` 54 55 - Expected postcondition 56 - returns all heights *h*, with *from <= h <= to* for which the 57 IBC component has a consensus state. 58 59 ---- 60 61 > This function can be used if the relayer has no information about 62 > the IBC component. This allows late-joining relayers to also 63 > participate in fork dection and the generation in proof of 64 > fork. Alternatively, we may also postulate that relayers are not 65 > responsible to detect forks for heights before they started (and 66 > subscribed to the transactions reporting fresh headers being 67 > installed at the IBC component). 68 69 ## Relayer 70 71 ### Auxiliary Functions to be implemented in the Light Client 72 73 #### [LCV-LS-FUNC-GET-PREV.1] 74 75 ```go 76 func (ls LightStore) GetPreviousVerified(height Height) (LightBlock, bool) 77 ``` 78 79 - Expected postcondition 80 - returns a verified LightBlock, whose height is maximal among all 81 verified lightblocks with height smaller than `height` 82 83 ---- 84 85 ### Relayer Submitting Proof of Fork to the IBC Component 86 87 There are two ways the relayer can detect a fork 88 89 - by the fork detector of one of its lightclients 90 - be checking the consensus state of the IBC component 91 92 The following function ignores how the proof of fork was generated. 93 It takes a proof of fork as input and computes a proof of fork that 94 will be accepted by the IBC component. 95 The problem addressed here is that both, the relayer's light client 96 and the IBC component have incomplete light stores, that might 97 not have all light blocks in common. 98 Hence the relayer has to figure out what the IBC component knows 99 (intuitively, a meeting point between the two lightstores 100 computed in `commonRoot`) and compute a proof of fork 101 (`extendPoF`) that the IBC component will accept based on its 102 knowledge. 103 104 The auxiliary functions `commonRoot` and `extendPoF` are 105 defined below. 106 107 #### [TAG-SUBMIT-POF-IBC.1] 108 109 ```go 110 func SubmitIBCProofOfFork( 111 lightStore LightStore, 112 PoF: LightNodeProofOfFork, 113 ibc IBCComponent) (Error) { 114 if ibc.queryChainConsensusState(PoF.TrustedBlock.Height) = PoF.TrustedBlock { 115 // IBC component has root of PoF on store, we can just submit 116 ibc.submitMisbehaviorToClient(ibc.id,PoF) 117 return Success 118 // note sure about the id parameter 119 } 120 else { 121 // the ibc component does not have the TrustedBlock and might 122 // even be on yet a different branch. We have to compute a PoF 123 // that the ibc component can verifiy based on its current 124 // knowledge 125 126 ibcLightBlock, lblock, _, result := commonRoot(lightStore, ibc, PoF.TrustedBlock) 127 128 if result = Success { 129 newPoF = extendPoF(ibcLightBlock, lblock, lightStore, PoF) 130 ibc.submitMisbehaviorToClient(ibc.id, newPoF) 131 return Success 132 } 133 else{ 134 return CouldNotGeneratePoF 135 } 136 } 137 } 138 ``` 139 140 **TODO:** finish conditions 141 142 - Implementation remark 143 - Expected precondition 144 - Expected postcondition 145 - Error condition 146 - none 147 148 ---- 149 150 ### Auxiliary Functions at the Relayer 151 152 > If the relayer detects a fork, it has to compute a proof of fork that 153 > will convince the IBC component. That is it has to compare the 154 > relayer's local lightstore against the lightstore of the IBC 155 > component, and find common ancestor lightblocks. 156 157 #### [TAG-COMMON-ROOT.1] 158 159 ```go 160 func commonRoot(lightStore LightStore, ibc IBCComponent, lblock 161 LightBlock) (LightBlock, LightBlock, LightStore, Result) { 162 163 auxLS.Init 164 165 // first we ask for the heights the ibc component is aware of 166 ibcHeights = ibc.QueryHeightsRange( 167 ibc.id, 168 lightStore.LowestVerified().Height, 169 lblock.Height - 1); 170 // this function does not exist yet. Alternatively, we may 171 // request all transactions that installed headers via CosmosSDK 172 173 174 for { 175 h, result = max(ibcHeights) 176 if result = Empty { 177 return (_, _, _, NoRoot) 178 } 179 ibcLightBlock = ibc.queryChainConsensusState(h) 180 auxLS.Update(ibcLightBlock, StateVerified); 181 connector, result := Connector(lightStore, ibcLightBlock, lblock.Header.Height) 182 if result = success { 183 return (ibcLightBlock, connector, auxLS, Success) 184 } 185 else{ 186 ibcHeights.remove(h) 187 } 188 } 189 } 190 ``` 191 192 - Expected postcondition 193 - returns 194 - a lightBlock b1 from the IBC component, and 195 - a lightBlock b2 196 from the local lightStore with height less than 197 lblock.Header.Hight, s.t. b1 supports b2, and 198 - a lightstore with the blocks downloaded from 199 the ibc component 200 201 ---- 202 203 #### [TAG-LS-FUNC-CONNECT.1] 204 205 ```go 206 func Connector (lightStore LightStore, lb LightBlock, h Height) (LightBlock, bool) 207 ``` 208 209 - Expected postcondition 210 - returns a verified LightBlock from lightStore with height less 211 than *h* that can be 212 verified by lb in one step. 213 214 **TODO:** for the above to work we need an invariant that all verified 215 lightblocks form a chain of trust. Otherwise, we need a lightblock 216 that has a chain of trust to height. 217 218 > Once the common root is found, a proof of fork that will be accepted 219 > by the IBC component needs to be generated. This is done in the 220 > following function. 221 222 #### [TAG-EXTEND-POF.1] 223 224 ```go 225 func extendPoF (root LightBlock, 226 connector LightBlock, 227 lightStore LightStore, 228 Pof LightNodeProofofFork) (LightNodeProofofFork} 229 ``` 230 231 - Implementation remark 232 - PoF is not sufficient to convince an IBC component, so we extend 233 the proof of fork farther in the past 234 - Expected postcondition 235 - returns a newPOF: 236 - newPoF.TrustedBlock = root 237 - let prefix = 238 connector + 239 lightStore.Subtrace(connector.Header.Height, PoF.TrustedBlock.Header.Height-1) + 240 PoF.TrustedBlock 241 - newPoF.PrimaryTrace = prefix + PoF.PrimaryTrace 242 - newPoF.SecondaryTrace = prefix + PoF.SecondaryTrace 243 244 ### Detection a fork at the IBC component 245 246 The following functions is assumed to be called regularly to check 247 that latest consensus state of the IBC component. Alternatively, this 248 logic can be executed whenever the relayer is informed (via an event) 249 that a new header has been installed. 250 251 #### [TAG-HANDLER-DETECT-FORK.1] 252 253 ```go 254 func DetectIBCFork(ibc IBCComponent, lightStore LightStore) (LightNodeProofOfFork, Error) { 255 cs = ibc.queryClientState(ibc); 256 lb, found := lightStore.Get(cs.Header.Height) 257 if !found { 258 **TODO:** need verify to target 259 lb, result = LightClient.Main(primary, lightStore, cs.Header.Height) 260 // [LCV-FUNC-IBCMAIN.1] 261 **TODO** decide what to do following the outcome of Issue #499 262 263 // I guess here we have to get into the light client 264 265 } 266 if cs != lb { 267 // IBC component disagrees with my primary. 268 // I fetch the 269 ibcLightBlock, lblock, ibcStore, result := commonRoot(lightStore, ibc, lb) 270 pof = new LightNodeProofOfFork; 271 pof.TrustedBlock := ibcLightBlock 272 pof.PrimaryTrace := ibcStore + cs 273 pof.SecondaryTrace := lightStore.Subtrace(lblock.Header.Height, 274 lb.Header.Height); 275 return(pof, Fork) 276 } 277 return(nil , NoFork) 278 } 279 ``` 280 281 **TODO:** finish conditions 282 283 - Implementation remark 284 - we ask the handler for the lastest check. Cross-check with the 285 chain. In case they deviate we generate PoF. 286 - we assume IBC component is correct. It has verified the 287 consensus state 288 - Expected precondition 289 - Expected postcondition