Skip to content
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

Merged
merged 3 commits into from
Jul 27, 2020
Merged

fix predicate bug #474

merged 3 commits into from
Jul 27, 2020

Conversation

Shivani912
Copy link
Contributor

closes #473

  • Referenced an issue explaining the need for the change
  • Updated all relevant documentation in docs
  • Updated all code comments where relevant
  • Wrote tests
  • Updated CHANGES.md

@Shivani912
Copy link
Contributor Author

Shivani912 commented Jul 22, 2020

Failing test-nightly-coverage issue #471

@Shivani912 Shivani912 requested a review from romac July 22, 2020 17:23
@@ -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)?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great find! 👍

Copy link
Member

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

Copy link
Member

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

liamsi
liamsi previously approved these changes Jul 23, 2020
Copy link
Member

@liamsi liamsi left a 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!

@Shivani912
Copy link
Contributor Author

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,
Copy link
Member

@romac romac Jul 23, 2020

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?

Copy link
Member

@liamsi liamsi Jul 23, 2020

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

Copy link
Contributor Author

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.

Copy link
Member

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

@romac romac mentioned this pull request Jul 23, 2020
5 tasks
@romac
Copy link
Member

romac commented Jul 23, 2020

Great stuff @Shivani912, I just have one question about clock drift inlined above.

Copy link
Member

@liamsi liamsi left a 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 ...)

@ebuchman
Copy link
Member

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

@Shivani912
Copy link
Contributor Author

Thanks for looking into this @ebuchman !

Great catch @Shivani912 - how'd you find this?

I wrote a test specifically for the case where verification must fail because of this: header_time > now + clock_drift. And upon investigating why the test failed, it wasn't too difficult to find this!

@Shivani912
Copy link
Contributor Author

Thanks everyone for your input here! Merging this now and clock synchronization issue can be followed at #478

@Shivani912 Shivani912 merged commit db54050 into master Jul 27, 2020
@Shivani912 Shivani912 deleted the shivani/fix-predicate branch July 27, 2020 10:32
Shivani912 added a commit that referenced this pull request Jul 27, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Bug in predicate - is_within_trust_period
4 participants