mirror of
https://github.com/tendermint/tendermint.git
synced 2026-02-03 18:42:14 +00:00
290 lines
8.8 KiB
Markdown
290 lines
8.8 KiB
Markdown
# Draft of Functions for Fork detection and Proof of Fork Submisstion
|
|
|
|
This document collects drafts of function for generating and
|
|
submitting proof of fork in the IBC context
|
|
|
|
- [IBC](#on---chain-ibc-component)
|
|
|
|
- [Relayer](#relayer)
|
|
|
|
## On-chain IBC Component
|
|
|
|
> The following is a suggestions to change the function defined in ICS 007
|
|
|
|
#### [TAG-IBC-MISBEHAVIOR.1]
|
|
|
|
```go
|
|
func checkMisbehaviourAndUpdateState(cs: ClientState, PoF: LightNodeProofOfFork)
|
|
```
|
|
|
|
**TODO:** finish conditions
|
|
|
|
- Implementation remark
|
|
- Expected precondition
|
|
- PoF.TrustedBlock.Header is equal to lightBlock on store with
|
|
same height
|
|
- both traces end with header of same height
|
|
- headers are different
|
|
- both traces are supported by PoF.TrustedBlock (`supports`
|
|
defined in [TMBC-FUNC]), that is, for `t = currentTimestamp()` (see
|
|
ICS 024)
|
|
- supports(PoF.TrustedBlock, PoF.PrimaryTrace[1], t)
|
|
- supports(PoF.PrimaryTrace[i], PoF.PrimaryTrace[i+1], t) for
|
|
*0 < i < length(PoF.PrimaryTrace)*
|
|
- supports(PoF.TrustedBlock, PoF.SecondaryTrace[1], t)
|
|
- supports(PoF.SecondaryTrace[i], PoF.SecondaryTrace[i+1], t) for
|
|
*0 < i < length(PoF.SecondaryTrace)*
|
|
- Expected postcondition
|
|
- set cs.FrozenHeight to min(cs.FrozenHeight, PoF.TrustedBlock.Header.Height)
|
|
- Error condition
|
|
- none
|
|
|
|
----
|
|
|
|
> The following is a suggestions to add functionality to ICS 002 and 007.
|
|
> I suppose the above is the most efficient way to get the required
|
|
> information. Another option is to subscribe to "header install"
|
|
> events via CosmosSDK
|
|
|
|
#### [TAG-IBC-HEIGHTS.1]
|
|
|
|
```go
|
|
func QueryHeightsRange(id, from, to) ([]Height)
|
|
```
|
|
|
|
- Expected postcondition
|
|
- returns all heights *h*, with *from <= h <= to* for which the
|
|
IBC component has a consensus state.
|
|
|
|
----
|
|
|
|
> This function can be used if the relayer has no information about
|
|
> the IBC component. This allows late-joining relayers to also
|
|
> participate in fork dection and the generation in proof of
|
|
> fork. Alternatively, we may also postulate that relayers are not
|
|
> responsible to detect forks for heights before they started (and
|
|
> subscribed to the transactions reporting fresh headers being
|
|
> installed at the IBC component).
|
|
|
|
## Relayer
|
|
|
|
### Auxiliary Functions to be implemented in the Light Client
|
|
|
|
#### [LCV-LS-FUNC-GET-PREV.1]
|
|
|
|
```go
|
|
func (ls LightStore) GetPreviousVerified(height Height) (LightBlock, bool)
|
|
```
|
|
|
|
- Expected postcondition
|
|
- returns a verified LightBlock, whose height is maximal among all
|
|
verified lightblocks with height smaller than `height`
|
|
|
|
----
|
|
|
|
### Relayer Submitting Proof of Fork to the IBC Component
|
|
|
|
There are two ways the relayer can detect a fork
|
|
|
|
- by the fork detector of one of its lightclients
|
|
- be checking the consensus state of the IBC component
|
|
|
|
The following function ignores how the proof of fork was generated.
|
|
It takes a proof of fork as input and computes a proof of fork that
|
|
will be accepted by the IBC component.
|
|
The problem addressed here is that both, the relayer's light client
|
|
and the IBC component have incomplete light stores, that might
|
|
not have all light blocks in common.
|
|
Hence the relayer has to figure out what the IBC component knows
|
|
(intuitively, a meeting point between the two lightstores
|
|
computed in `commonRoot`) and compute a proof of fork
|
|
(`extendPoF`) that the IBC component will accept based on its
|
|
knowledge.
|
|
|
|
The auxiliary functions `commonRoot` and `extendPoF` are
|
|
defined below.
|
|
|
|
#### [TAG-SUBMIT-POF-IBC.1]
|
|
|
|
```go
|
|
func SubmitIBCProofOfFork(
|
|
lightStore LightStore,
|
|
PoF: LightNodeProofOfFork,
|
|
ibc IBCComponent) (Error) {
|
|
if ibc.queryChainConsensusState(PoF.TrustedBlock.Height) = PoF.TrustedBlock {
|
|
// IBC component has root of PoF on store, we can just submit
|
|
ibc.submitMisbehaviourToClient(ibc.id,PoF)
|
|
return Success
|
|
// note sure about the id parameter
|
|
}
|
|
else {
|
|
// the ibc component does not have the TrustedBlock and might
|
|
// even be on yet a different branch. We have to compute a PoF
|
|
// that the ibc component can verifiy based on its current
|
|
// knowledge
|
|
|
|
ibcLightBlock, lblock, _, result := commonRoot(lightStore, ibc, PoF.TrustedBlock)
|
|
|
|
if result = Success {
|
|
newPoF = extendPoF(ibcLightBlock, lblock, lightStore, PoF)
|
|
ibc.submitMisbehaviourToClient(ibc.id, newPoF)
|
|
return Success
|
|
}
|
|
else{
|
|
return CouldNotGeneratePoF
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**TODO:** finish conditions
|
|
|
|
- Implementation remark
|
|
- Expected precondition
|
|
- Expected postcondition
|
|
- Error condition
|
|
- none
|
|
|
|
----
|
|
|
|
### Auxiliary Functions at the Relayer
|
|
|
|
> If the relayer detects a fork, it has to compute a proof of fork that
|
|
> will convince the IBC component. That is it has to compare the
|
|
> relayer's local lightstore against the lightstore of the IBC
|
|
> component, and find common ancestor lightblocks.
|
|
|
|
#### [TAG-COMMON-ROOT.1]
|
|
|
|
```go
|
|
func commonRoot(lightStore LightStore, ibc IBCComponent, lblock
|
|
LightBlock) (LightBlock, LightBlock, LightStore, Result) {
|
|
|
|
auxLS.Init
|
|
|
|
// first we ask for the heights the ibc component is aware of
|
|
ibcHeights = ibc.QueryHeightsRange(
|
|
ibc.id,
|
|
lightStore.LowestVerified().Height,
|
|
lblock.Height - 1);
|
|
// this function does not exist yet. Alternatively, we may
|
|
// request all transactions that installed headers via CosmosSDK
|
|
|
|
|
|
for {
|
|
h, result = max(ibcHeights)
|
|
if result = Empty {
|
|
return (_, _, _, NoRoot)
|
|
}
|
|
ibcLightBlock = ibc.queryChainConsensusState(h)
|
|
auxLS.Update(ibcLightBlock, StateVerified);
|
|
connector, result := Connector(lightStore, ibcLightBlock, lblock.Header.Height)
|
|
if result = success {
|
|
return (ibcLightBlock, connector, auxLS, Success)
|
|
}
|
|
else{
|
|
ibcHeights.remove(h)
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
- Expected postcondition
|
|
- returns
|
|
- a lightBlock b1 from the IBC component, and
|
|
- a lightBlock b2
|
|
from the local lightStore with height less than
|
|
lblock.Header.Hight, s.t. b1 supports b2, and
|
|
- a lightstore with the blocks downloaded from
|
|
the ibc component
|
|
|
|
----
|
|
|
|
#### [TAG-LS-FUNC-CONNECT.1]
|
|
|
|
```go
|
|
func Connector (lightStore LightStore, lb LightBlock, h Height) (LightBlock, bool)
|
|
```
|
|
|
|
- Expected postcondition
|
|
- returns a verified LightBlock from lightStore with height less
|
|
than *h* that can be
|
|
verified by lb in one step.
|
|
|
|
**TODO:** for the above to work we need an invariant that all verified
|
|
lightblocks form a chain of trust. Otherwise, we need a lightblock
|
|
that has a chain of trust to height.
|
|
|
|
> Once the common root is found, a proof of fork that will be accepted
|
|
> by the IBC component needs to be generated. This is done in the
|
|
> following function.
|
|
|
|
#### [TAG-EXTEND-POF.1]
|
|
|
|
```go
|
|
func extendPoF (root LightBlock,
|
|
connector LightBlock,
|
|
lightStore LightStore,
|
|
Pof LightNodeProofofFork) (LightNodeProofofFork}
|
|
```
|
|
|
|
- Implementation remark
|
|
- PoF is not sufficient to convince an IBC component, so we extend
|
|
the proof of fork farther in the past
|
|
- Expected postcondition
|
|
- returns a newPOF:
|
|
- newPoF.TrustedBlock = root
|
|
- let prefix =
|
|
connector +
|
|
lightStore.Subtrace(connector.Header.Height, PoF.TrustedBlock.Header.Height-1) +
|
|
PoF.TrustedBlock
|
|
- newPoF.PrimaryTrace = prefix + PoF.PrimaryTrace
|
|
- newPoF.SecondaryTrace = prefix + PoF.SecondaryTrace
|
|
|
|
### Detection a fork at the IBC component
|
|
|
|
The following functions is assumed to be called regularly to check
|
|
that latest consensus state of the IBC component. Alternatively, this
|
|
logic can be executed whenever the relayer is informed (via an event)
|
|
that a new header has been installed.
|
|
|
|
#### [TAG-HANDLER-DETECT-FORK.1]
|
|
|
|
```go
|
|
func DetectIBCFork(ibc IBCComponent, lightStore LightStore) (LightNodeProofOfFork, Error) {
|
|
cs = ibc.queryClientState(ibc);
|
|
lb, found := lightStore.Get(cs.Header.Height)
|
|
if !found {
|
|
**TODO:** need verify to target
|
|
lb, result = LightClient.Main(primary, lightStore, cs.Header.Height)
|
|
// [LCV-FUNC-IBCMAIN.1]
|
|
**TODO** decide what to do following the outcome of Issue #499
|
|
|
|
// I guess here we have to get into the light client
|
|
|
|
}
|
|
if cs != lb {
|
|
// IBC component disagrees with my primary.
|
|
// I fetch the
|
|
ibcLightBlock, lblock, ibcStore, result := commonRoot(lightStore, ibc, lb)
|
|
pof = new LightNodeProofOfFork;
|
|
pof.TrustedBlock := ibcLightBlock
|
|
pof.PrimaryTrace := ibcStore + cs
|
|
pof.SecondaryTrace := lightStore.Subtrace(lblock.Header.Height,
|
|
lb.Header.Height);
|
|
return(pof, Fork)
|
|
}
|
|
return(nil , NoFork)
|
|
}
|
|
```
|
|
|
|
**TODO:** finish conditions
|
|
|
|
- Implementation remark
|
|
- we ask the handler for the lastest check. Cross-check with the
|
|
chain. In case they deviate we generate PoF.
|
|
- we assume IBC component is correct. It has verified the
|
|
consensus state
|
|
- Expected precondition
|
|
- Expected postcondition
|