mirror of
https://github.com/tendermint/tendermint.git
synced 2026-05-29 10:30:20 +00:00
Address reviewer's comments
This commit is contained in:
@@ -122,6 +122,7 @@ if *l* has set *trust(h) = true*,
|
||||
|
||||
Upon initialization, the lite client is given a header *inithead* it trusts (by
|
||||
social consensus). It is assumed that *inithead* satisfies the lite client invariant. (If *inithead* has been correctly generated by Tendermint consensus, the invariant follows from the Tendermint Failure Model.)
|
||||
Note that the *inithead* should be within its trusted period during initialization.
|
||||
|
||||
When a lite clients sees a signed new header *snh*, it has to decide whether to trust the new
|
||||
header. Trust can be obtained by (possibly) the combination of three methods.
|
||||
@@ -186,7 +187,9 @@ We consider the following set-up:
|
||||
- the lite client locally stores all the headers that has passed basic verification and that are within lite client trust period. In the pseudo code below we write *Store(header)* for this. If a header failed to verify, then
|
||||
the full node we are talking to is faulty and we should disconnect from it and reinitialise lite client.
|
||||
- If *CanTrust* returns *error*, then the lite client has seen a forged header or the trusted header has expired (it is outside its trusted period).
|
||||
* In case of forged header, the full node is faulty so lite client should disconnect and reinitialise with new trusted header.
|
||||
* In case of forged header, the full node is faulty so lite client should disconnect and reinitialise with new trusted header. If the trusted header has expired,
|
||||
we need to reinitialise lite client with new trusted header (that is within its trusted period), but we don't necessarily need to disconnect from the full node
|
||||
we are talking to (as we haven't observed full node misbehavior in this case).
|
||||
|
||||
**Auxiliary Functions.** We will use the function ```votingpower_in(V1,V2)``` to compute the voting power the validators in set V1 have according to their voting power in set V2;
|
||||
we will write ```totalVotingPower(V)``` for ```votingpower_in(V,V)```, which returns the total voting power in V.
|
||||
@@ -203,28 +206,35 @@ true in case the header is within its lite client trusted period.
|
||||
return h.Header.bfttime + LITE_CLIENT_TRUSTED_PERIOD > now
|
||||
}
|
||||
|
||||
// return true if header is correctly signed by 2/3+ voting power in the corresponding validator set;
|
||||
// otherwise false. Additional checks should be done in the implementation
|
||||
// return true if header is correctly signed by 2/3+ voting power in the corresponding
|
||||
// validator set; otherwise false. Additional checks should be done in the implementation
|
||||
// to ensure header is well formed.
|
||||
func verify(h) bool {
|
||||
vp_all := totalVotingPower(h.Header.V) // total sum of voting power of validators in h
|
||||
return votingpower_in(signers(h.Commit),h.Header.V) > 2/3 * vp_all
|
||||
}
|
||||
|
||||
// Captures skipping condition. h1 and h2 has already passed basic validation (function `verify`).
|
||||
// returns nil in case h2 can be trusted based on h1, otherwise returns error.
|
||||
// ErrHeaderExpired is used to signal that h1 has expired with respect lite client trusted period,
|
||||
// ErrInvalidAdjacentHeaders that adjacent headers are not consistent and
|
||||
// ErrTooMuchChange that there is not enough intersection between validator sets to have skipping condition true.
|
||||
func CheckSupport(h1,h2,trustlevel) error {
|
||||
assume h1.Header.Height < h2.header.Height and h1.Header.bfttime < h2.Header.bfttime and h2.Header.bfttime < now
|
||||
// Captures skipping condition. h1 and h2 have already passed basic validation
|
||||
// (function `verify`).
|
||||
// Returns nil in case h2 can be trusted based on h1, otherwise returns error.
|
||||
// ErrHeaderExpired is used when h1 has expired with respect to lite client trusted period,
|
||||
// ErrInvalidAdjacentHeaders when that adjacent headers are not consistent and
|
||||
// ErrTooMuchChange when there is not enough intersection between validator sets to have
|
||||
// skipping condition true.
|
||||
func CheckSupport(h1,h2,trustThreshold) error {
|
||||
assume h1.Header.Height < h2.header.Height and
|
||||
h1.Header.bfttime < h2.Header.bfttime and
|
||||
h2.Header.bfttime < now
|
||||
|
||||
if !isWithinTrustedPeriod(h1) return ErrHeaderNotWithinTrustedPeriod(h1)
|
||||
if !isWithinTrustedPeriod(h1) return ErrHeaderNotWithinTrustedPeriod(h1)
|
||||
|
||||
// Although while executing the rest of CheckSupport function, h1 can expiry based on the lite client trusted period, this is not problem as
|
||||
// lite client trusted period is smaller than trusted period of the header based on Tendermint Failure model, i.e., there is a significant
|
||||
// time period (measure in days) during which validator set that has signed h1 can be trusted
|
||||
// Furthermore, CheckSupport function is not doing expensive operation (neither rpc nor signature verification), so it should execute fast.
|
||||
// Although while executing the rest of CheckSupport function, h1 can expiry based
|
||||
// on the lite client trusted period, this is not problem as lite client trusted
|
||||
// period is smaller than trusted period of the header based on Tendermint Failure
|
||||
// model, i.e., there is a significant time period (measure in days) during which
|
||||
// validator set that has signed h1 can be trusted. Furthermore, CheckSupport function
|
||||
// is not doing expensive operation (neither rpc nor signature verification), so it
|
||||
// should execute fast.
|
||||
|
||||
// total sum of voting power of validators in h1.NextV
|
||||
vp_all := totalVotingPower(h1.Header.NextV)
|
||||
@@ -236,7 +246,9 @@ true in case the header is within its lite client trusted period.
|
||||
return ErrInvalidAdjacentHeaders
|
||||
}
|
||||
// check for non-adjacent headers
|
||||
if votingpower_in(signers(h2.Commit),h1.Header.NextV) > max(1/3,trustlevel) * vp_all return nil
|
||||
if votingpower_in(signers(h2.Commit),h1.Header.NextV) > max(1/3,trustThreshold) * vp_all {
|
||||
return nil
|
||||
}
|
||||
return ErrTooMuchChange
|
||||
|
||||
}
|
||||
@@ -257,7 +269,7 @@ Towards Lite Client Completeness:
|
||||
|
||||
*Verification Condition:* We may need a Tendermint invariant stating that if *h2.Header.height = h1.Header.height + 1* then *signers(h2.Commit) \subseteq h1.Header.NextV*.
|
||||
|
||||
*Remark*: The variable *trustlevel* can be used if the user believes that relying on one correct validator is not sufficient. However, in case of (frequent) changes in the validator set, the higher the *trustlevel* is chosen, the more unlikely it becomes that CheckSupport returns true for non-adjacent headers.
|
||||
*Remark*: The variable *trustThreshold* can be used if the user believes that relying on one correct validator is not sufficient. However, in case of (frequent) changes in the validator set, the higher the *trustThreshold* is chosen, the more unlikely it becomes that CheckSupport returns true for non-adjacent headers.
|
||||
|
||||
**VerifyHeader.** The function *VerifyHeader* captures high level logic, i.e., application call to the lite client module to (optionally download) and
|
||||
verify header for some height. The core verification logic is captured by *CanTrust* function that iteratively try to establish trust in given header
|
||||
@@ -265,97 +277,81 @@ by relying on *CheckSupport* function.
|
||||
|
||||
|
||||
```go
|
||||
func VerifyHeader(height, trustlevel) error {
|
||||
if h2, exists := Store.Get(height); exists {
|
||||
if isWithinTrustedPeriod(h2) return nil
|
||||
return ErrHeaderNotWithinTrustedPeriod(h2)
|
||||
}
|
||||
else {
|
||||
h2 := Commit(height)
|
||||
if !verify(h2) return ErrInvalidHeader(h2)
|
||||
if !isWithinTrustedPeriod(h2) return ErrHeaderNotWithinTrustedPeriod(h2)
|
||||
}
|
||||
func VerifyHeader(height, trustThreshold) error {
|
||||
if h2, exists := Store.Get(height); exists {
|
||||
if isWithinTrustedPeriod(h2) { return nil }
|
||||
return ErrHeaderNotWithinTrustedPeriod(h2)
|
||||
}
|
||||
|
||||
// get the highest trusted headers lower than h2
|
||||
h1 = Store.HighestTrustedSmallerThan(height)
|
||||
if h1 == nil
|
||||
return ErrNoTrustedHeader
|
||||
h2 := Commit(height)
|
||||
if !verify(h2) { return ErrInvalidHeader(h2) }
|
||||
if !isWithinTrustedPeriod(h2) { return ErrHeaderNotWithinTrustedPeriod(h2) }
|
||||
|
||||
err = CanTrust(h1, h2, trustlevel) // or CanTrustBisection((h1, h2, trustlevel)
|
||||
if err != nil return err
|
||||
// get the highest trusted headers lower than h2
|
||||
h1 = Store.HighestTrustedSmallerThan(height)
|
||||
if h1 == nil { return ErrNoTrustedHeader }
|
||||
|
||||
if isWithinTrustedPeriod(h2) {
|
||||
Store.add(h2) // we store only trusted headers, as we assume that only trusted headers are influencing end user business decisions.
|
||||
return nil
|
||||
}
|
||||
return ErrHeaderNotTrusted(h2)
|
||||
err = CanTrust(h1, h2, trustThreshold) // or CanTrustBisection((h1, h2, trustThreshold)
|
||||
if err != nil { return err }
|
||||
|
||||
if isWithinTrustedPeriod(h2) {
|
||||
Store.add(h2)
|
||||
// we store only trusted headers, as we assume that only trusted headers
|
||||
// are influencing end user business decisions.
|
||||
return nil
|
||||
}
|
||||
return ErrHeaderNotTrusted(h2)
|
||||
}
|
||||
|
||||
|
||||
// return nil in case we can trust header h2 based on header h1; otherwise return error where error captures the nature of the error.
|
||||
func CanTrust(h1,h2,trustlevel) error {
|
||||
// return nil in case we can trust header h2 based on header h1; otherwise return error
|
||||
// where error captures the nature of the error.
|
||||
func CanTrust(h1,h2,trustThreshold) error {
|
||||
assume h1.Header.Height < h2.header.Height
|
||||
|
||||
err = CheckSupport(h1,h2,trustlevel)
|
||||
if err == nil {
|
||||
Store.Add(h2)
|
||||
return nil
|
||||
}
|
||||
if err != ErrTooMuchChange return err
|
||||
|
||||
// we cannot verify h2 based on h1, so we try to move trusted header closer to h2 so we can verify h2
|
||||
th := h1 // th is trusted header
|
||||
untrustedHeaders := []
|
||||
untrustedHeaders := [h2]
|
||||
|
||||
while true {
|
||||
endHeight = h2.Header.height
|
||||
foundPivot = false
|
||||
while(!foundPivot) {
|
||||
pivot := (th.Header.height + endHeight) / 2
|
||||
hp := Commit(pivot)
|
||||
if !verify(hp) return ErrInvalidHeader(hp)
|
||||
// try to move trusted header forward to hp
|
||||
err = CheckSupport(th,hp,trustlevel)
|
||||
if (err != nil and err != ErrTooMuchChange) return err
|
||||
if err == nil {
|
||||
th = hp
|
||||
Store.Add(hp)
|
||||
foundPivot = true
|
||||
}
|
||||
untrustedHeaders.add(hp)
|
||||
endHeight = pivot
|
||||
}
|
||||
|
||||
// try to move trusted header forward
|
||||
for h in untrustedHeaders {
|
||||
// we assume here that iteration is done in the order of header heights
|
||||
err = CheckSupport(th,h,trustlevel)
|
||||
if (err != nil and err != ErrTooMuchChange) return err
|
||||
if err == nil {
|
||||
th = h
|
||||
Store.Add(h)
|
||||
untrustedHeaders.Remove(h)
|
||||
}
|
||||
}
|
||||
|
||||
// at this point we have potentially updated th based on stored headers so we try to verify h2
|
||||
// based on new trusted header
|
||||
err = CheckSupport(h1,h2,trustlevel)
|
||||
for h in untrustedHeaders {
|
||||
// we assume here that iteration is done in the order of header heights
|
||||
err = CheckSupport(th,h,trustThreshold)
|
||||
if err == nil {
|
||||
Store.Add(h2)
|
||||
return nil
|
||||
th = h
|
||||
Store.Add(h)
|
||||
untrustedHeaders.RemoveHeadersSmallerOrEqual(h.Header.Height)
|
||||
if th == h2 { return nil }
|
||||
}
|
||||
if err != ErrTooMuchChange return err
|
||||
if (err != ErrTooMuchChange) { return err }
|
||||
}
|
||||
|
||||
endHeight = min(untrustedHeaders)
|
||||
foundPivot = false
|
||||
while(!foundPivot) {
|
||||
pivot := (th.Header.height + endHeight) / 2
|
||||
hp := Commit(pivot)
|
||||
if !verify(hp) { return ErrInvalidHeader(hp) }
|
||||
// try to move trusted header forward to hp
|
||||
err = CheckSupport(th,hp,trustThreshold)
|
||||
if (err != nil and err != ErrTooMuchChange) return err
|
||||
if err == nil {
|
||||
th = hp
|
||||
Store.Add(hp)
|
||||
foundPivot = true
|
||||
}
|
||||
untrustedHeaders.add(hp)
|
||||
endHeight = pivot
|
||||
}
|
||||
}
|
||||
return nil // this line should never be reached
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
func CanTrustBisection(h1,h2,trustlevel) error {
|
||||
func CanTrustBisection(h1,h2,trustThreshold) error {
|
||||
assume h1.Header.Height < h2.header.Height
|
||||
|
||||
err = CheckSupport(h1,h2,trustlevel)
|
||||
err = CheckSupport(h1,h2,trustThreshold)
|
||||
if err == nil {
|
||||
Store.Add(h2)
|
||||
return nil
|
||||
@@ -366,10 +362,10 @@ func CanTrustBisection(h1,h2,trustlevel) error {
|
||||
hp := Commit(pivot)
|
||||
if !verify(hp) return ErrInvalidHeader(hp)
|
||||
|
||||
err = CanTrustBisection(h1,hp,trustlevel)
|
||||
err = CanTrustBisection(h1,hp,trustThreshold)
|
||||
if err == nil {
|
||||
Store.Add(hp)
|
||||
err2 = CanTrustBisection(hp,h2,trustlevel)
|
||||
err2 = CanTrustBisection(hp,h2,trustThreshold)
|
||||
if err2 == nil {
|
||||
Store.Add(h2)
|
||||
return nil
|
||||
@@ -423,7 +419,7 @@ func Backwards(h1,h2) error {
|
||||
old := new
|
||||
if !isWithinTrustedPeriod(h1) return ErrHeaderNotTrusted(h1)
|
||||
}
|
||||
if hash(h2) == old.Header.hash return ErrInvalidAdjacentHeaders
|
||||
if hash(h2) != old.Header.hash return ErrInvalidAdjacentHeaders
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user