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

ICS07: Add verification method for client update handler #1252

Closed
wants to merge 43 commits into from
Closed

Conversation

cezarad
Copy link
Contributor

@cezarad cezarad commented Aug 2, 2021

Closes: #XXX

Description

This is the former PR check_client_and_update_state:

  • implements the ICS07 verification of the client update checkValidityAndUpdateState in https://github.com/cosmos/ibc/tree/master/spec/client/ics-007-tendermint-client

  • modifications in the tendermint testgen to make test pass with this extra check.

  • added timestamp to Anyclient: On one hand in AnyClientState we have the methodexpired but in AnyHeader we do not have the timestamp so we cannot check in ICS02 if the update is expired. On the other hand the pseudo code of ICS02 talks only about the height.

TODO!

  • use directly tendermint-rs functions not to duplicate the code: The duplicates are : the function is voting_power_in and the function that is calls total_power_of and non_absent_vote from ics07_tendermint/header.rs
    The return type of is voting_power_in is a bit simplified but is a modification easily revertible.

For contributor use:

  • Added a changelog entry, using unclog.
  • If applicable: Unit tests written, added test to CI.
  • Linked to Github issue with discussion and accepted design OR link to spec that describes this work.
  • Updated relevant documentation (docs/) and code comments.
  • Re-reviewed Files changed in the Github PR explorer.

@cezarad cezarad changed the title Cezara Verification update Aug 2, 2021
@cezarad cezarad mentioned this pull request Aug 2, 2021
5 tasks
@adizere adizere marked this pull request as draft August 9, 2021 14:30
@adizere adizere added A: blocked Admin: blocked by another (internal/external) issue or PR I: dependencies Internal: related to dependencies modules labels Aug 9, 2021
@adizere
Copy link
Member

adizere commented Aug 9, 2021

Update: This is currently blocked on some changes on tm-rs side. The problem is that we’d like to merge this PR without duplicating the code from tendermint-rs. After talking with @thanethomson, the plan is:

  1. We use approach of adding Clone and other necessary trait bounds so that we can use VerificationPredicates as a trait object
  2. Thane can do a bit of refactoring of Cezara’s code to remove the duplicate code and clean up the PR. I will help reviewing this.

Many thanks to @thanethomson and @cezarad for shepherding this!

Copy link
Collaborator

@ancazamfir ancazamfir left a comment

Choose a reason for hiding this comment

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

I only reviewed modules/src/ics07_tendermint/client_def.rs for now. A lot of those changes should go in the tendermint light client API imho.

Comment on lines +55 to +59
//freeze the client and return the installed consensus state
return Ok((
client_state.with_set_frozen(header.height()),
consensus_state,
));
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should freeze the client only if the conflicting header verifies. i.e. we need to run the checks/ validity predicates (from the new tendermint-rs verify function) below. If the header in update doesn't verify we should return an error.

Comment on lines +65 to +66
let latest_consensus_state =
match ctx.consensus_state(&client_id, client_state.latest_height) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

We have to get the consensus state based on the header trusted height which might not be the latest:

Suggested change
let latest_consensus_state =
match ctx.consensus_state(&client_id, client_state.latest_height) {
let trusted_consensus_state =
match ctx.consensus_state(&client_id, header.trusted_height) {

Comment on lines +80 to +81
monotonicity_checks(latest_consensus_state, header.clone(), client_state.clone())
.map_err(Ics02Error::tendermint_handler_error)?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

The monotonicity.. check should also deal with in-the-middle updates where a consensus state with higher height than the header is present. This requires a new ctx api to get the next consensus state, something like this

Suggested change
monotonicity_checks(latest_consensus_state, header.clone(), client_state.clone())
.map_err(Ics02Error::tendermint_handler_error)?;
let next_higher_consensus_state: Option<ConsensusState> = ctx.next_consensus_state(&client_id, header.height())?;
monotonicity_checks(trusted_consensus_state, next_higher_consensus_state, header.clone(), client_state.clone())
.map_err(Ics02Error::tendermint_handler_error)?;

Comment on lines +92 to +103
let trusted_consensus_state = match ctx.consensus_state(&client_id, header.trusted_height) {
Some(ts) => downcast!(
ts => AnyConsensusState::Tendermint
)
.ok_or_else(|| Ics02Error::client_args_type_mismatch(ClientType::Tendermint))?,
None => {
return Err(Ics02Error::consensus_state_not_found(
client_id,
header.trusted_height,
))
}
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

this can be removed as we should use this above instead of latest_consensus_state.


// check that the header's trusted validator set is
// the next_validator_set of the trusted consensus state
if Set::hash(&header.validator_set) != trusted_consensus_state.next_validators_hash {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This check should be done for both adjacent and non-adjacent. And you need the trusted validator instead:

Suggested change
if Set::hash(&header.validator_set) != trusted_consensus_state.next_validators_hash {
if Set::hash(&header.trusted_validator_set) != trusted_consensus_state.next_validators_hash {

This check should be done in the tendermint light client API.

Comment on lines +118 to +126
// check that the validators that sign the commit of the untrusted header
// have 2/3 of the voting power of the current validator set.
if let Err(e) = voting_power_in(
&header.signed_header,
&header.validator_set,
TrustThresholdFraction::TWO_THIRDS,
) {
return Err(Error::insufficient_voting_power(e.to_string()).into());
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

same here, the check should happen for both adjacent and non-adjacent headers. In fact you have this below in the else branch.

Comment on lines +118 to +149
// check that the validators that sign the commit of the untrusted header
// have 2/3 of the voting power of the current validator set.
if let Err(e) = voting_power_in(
&header.signed_header,
&header.validator_set,
TrustThresholdFraction::TWO_THIRDS,
) {
return Err(Error::insufficient_voting_power(e.to_string()).into());
}
} else {
//Non-adjacent

//check that a subset of the trusted validator set, having 1/3 of the voting power
//signes the commit of the untrusted header
if let Err(e) = voting_power_in(
&header.signed_header,
&header.trusted_validator_set,
TrustThresholdFraction::default(),
) {
return Err(Error::not_enough_trusted_vals_signed(e.to_string()).into());
};

// check that the validators that sign the commit of the untrusted header
// have 2/3 of the voting power of the current validator set.
if let Err(e) = voting_power_in(
&header.signed_header,
&header.validator_set,
TrustThresholdFraction::TWO_THIRDS,
) {
return Err(Error::insufficient_voting_power(e.to_string()).into());
};
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

All these checks should happen in the tendermint-rs light client API. It should also include the following checks:

  • trusted header is not expired, i.e. it is within trusting period (note that we cannot provide a full trusted header to the API, just what we can get from the consensus state: height, time and next val hash)
  • update header timestamp is not beyond now() plus clock drift

Probably most of the monotonicity check between trusted and update headers should happen in the tendermint light client API. The only one left in this function should be between the update and next higher headers, just check the timestamps are monotonically increased and nothing else.

@romac
Copy link
Member

romac commented Sep 8, 2021

Superseded by #1321.

@romac romac closed this Sep 8, 2021
@romac romac deleted the cezara branch January 26, 2022 14:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A: blocked Admin: blocked by another (internal/external) issue or PR I: dependencies Internal: related to dependencies
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants