-
Notifications
You must be signed in to change notification settings - Fork 224
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix predicate bug #474
fix predicate bug #474
Conversation
Failing |
@@ -231,6 +249,9 @@ pub fn verify( | |||
now, | |||
)?; | |||
|
|||
// Ensure the header isn't from a future time | |||
vp.is_header_from_past(&untrusted.signed_header.header, options.clock_drift, now)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great find! 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a great find. It's also a reminder that there's an underlying assumption in the spec which should perhaps be more explicitly surfaced that the light client's local clock is closely synchronized (within clock_drift) to that of the chain's BFT time (ie. currently the median timestamp from the last commit).
We might want to better motivate this assumption and its relationship with the trusting period and expired headers. Currently, if our trusted header hasn't expired and trusting_period >> clock_drift
, this is_header_from_past
check ensures the new header is also within the trusting period, even though we don't run is_within_trusting_period(untrusted)
directly. But. is_header_from_past(untrusted)
is a much stronger condition than is_within_trusting_period(untrusted)
and it may have subtle consequences in IBC, so we may want to reconsider it (!).
In IBC, a client's "local" clock is the BFT time of that chain (ie. the median timestamp of the last commit from the validator set ...), so this check would imply that the BFT time ("on chain clock") of any two chains in IBC is within clock_drift of each other, which might be too strong (?!).
Note that BFT time in Tendermint is currently subject to unaccountable control by +1/3 validators - they can arbitrarily fast forward time. This is somewhat counteracted by a punishment mechanism that uses both a hybrid of unbonding period (eg. ~3 weeks) AND minimum number of blocks (eg. ~30,000 blocks) to determine evidence validity, so even if validators fast forward the clock beyond the unbonding period, they can still be punished for some number of blocks.
And of course there are plans to change how BFT time is generated (proposer-based instead of median of timestamps in commits) and that would prevent +1/3 from controlling it, but would assume +2/3 have synchronized clocks on a given chain, and would still assume synchronize clocks between chains.
If validators fast forward the clock, they can make it difficult for light clients to keep up and act on current information (since it will look like the chain is always ahead). This may lead to certain kinds of attacks on IBC channels where validators on one chain can delay the validity of client updates on a chain its connected to (maybe there's an equivalent of Miner Extractable Value attacks on IBC here ...) . Not sure how big a problem this is, and/or how much it can be addressed by just checking is_within_trusting_period(untrusted)
instead of is_header_from_past(untrusted)
cc @cwgoes @josef-widder ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I gave it its own issue: #478
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some minor (optional) renamings that could clarify wether the functions are expecting a trusted/vs untrusted header (maybe we can use the type system for that on the long-run instead). Can be addressed here or in followups.
Thanks @Shivani912 🙏 Great work!
Thanks for reviewing @liamsi ! I think doing that makes a lot of sense. @romac would be best person to comment on this as he has a better view of it than I do. |
) -> Result<(), VerificationError> { | ||
let expires_at = trusted_header.time + trusting_period; | ||
ensure!( | ||
expires_at > now, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we need to account for clock drift here as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I remember the spec applies the clockdrift only to untrusted headers. If we change that here we should feed that back into the spec too:
https://github.com/informalsystems/tendermint-rs/blob/master/docs/spec/lightclient/verification/verification.md#lcv-func-valid1
Found this gem in the english spec 🤣
If trusted.Header.Time > now - trustingPeriod the blabla
who knows if "the blabla" isn't the clockDrift though...
cc @milosevic
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A benefit to not adding the clock_drift
is that we have some extra time to account for the time taken by the next verification steps. Also this could mean that if the header
has already expired like a second ago but because we add the clock_drift
there, it could satisfy the condition and consider it valid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we shouldn't need to worry about clock drift here since trusting_period is orders of magnitude greater than clock_drift but I'm not sure how best to surface that reasoning in the code (maybe just a comment). Also cc @josef-widder
Great stuff @Shivani912, I just have one question about clock drift inlined above. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM (regarding the clock drift: https://github.com/informalsystems/tendermint-rs/pull/474/files#r459496060 it seems that it is safer here to not account for clockdrift ...)
Great catch @Shivani912 - how'd you find this? I think this is fine to merge but it does open a can of worms (which already existed but we may not have been paying attention to): #478 |
Thanks for looking into this @ebuchman !
I wrote a test specifically for the case where verification must fail because of this: |
Thanks everyone for your input here! Merging this now and clock synchronization issue can be followed at #478 |
This reverts commit db54050.
closes #473