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 Tendermint Client implementation #5485

Merged
merged 39 commits into from
Jan 27, 2020

Conversation

fedekunze
Copy link
Collaborator

@fedekunze fedekunze commented Jan 6, 2020

Closes: #5312
Closes: #5476

NOTE: this PR is ready for review, pending tests fixes

Please review against each individual ICS specification.

TODO:

  • fix x/ibc/03-connection/keeper/handshake_test.go
  • fix x/ibc/03-connection/keeper/verify_test.go
  • fix x/ibc/04-channel/keeper/handshake_test.go
  • add x/ibc/04-channel/keeper/packet_test.go (nice-to-have)
  • add x/ibc/04-channel/keeper/timeout_test.go (nice-to-have)
  • fix x/ibc/07-tendermint/client_state_test.go (uncomment test cases) Blocked by proof verification

Description

  • Implements ICS07 within x/ibc/07-tendermint/. Previous tendermint client types (x/ibc/02-client/types/tendermint/) are now in the former package
  • Moves client errors package (x/ibc/02-client/types/errors/) to client types (x/ibc/02-client/types/errors.go)
  • Standardize calls to clients using switch statement
  • Remove unused queriers
  • Refactor types and implement ChannelI and ConnectionI interfaces to x/ibc/XX-ICS/exported/ to prevent circle depencencies
  • Remove client MsgSubmitMisbehaviour and replace it for evidence's module MsgSubmitEvidence
  • Update implementation to latest IBC spec (rc5)
  • Comment port authentication-related functions until [R4R] ADR 003: Dynamic Capability Store #5397 is implemented

For contributor use:

  • Targeted PR against correct branch (see CONTRIBUTING.md)
  • Linked to Github issue with discussion and accepted design OR link to spec that describes this work.
  • Code follows the module structure standards.
  • Wrote unit and integration tests
  • Updated relevant documentation (docs/) or specification (x/<module>/spec/)
  • Added relevant godoc comments.
  • Added a relevant changelog entry to the Unreleased section in CHANGELOG.md
  • Re-reviewed Files changed in the Github PR explorer

For admin use:

  • Added appropriate labels to PR (ex. WIP, R4R, docs, etc)
  • Reviewers assigned
  • Squashed all commits, uses message "Merge pull request #XYZ: [title]" (coding standards)

@lgtm-com
Copy link

lgtm-com bot commented Jan 6, 2020

This pull request introduces 1 alert when merging 7705818 into 682c8ad - view on LGTM.com

new alerts:

  • 1 for Useless assignment to field

@lgtm-com
Copy link

lgtm-com bot commented Jan 8, 2020

This pull request introduces 1 alert when merging 062b748 into 682c8ad - view on LGTM.com

new alerts:

  • 1 for Useless assignment to field

@cwgoes
Copy link
Contributor

cwgoes commented Jan 14, 2020

Let's use the Tendermint verify function if possible - https://github.com/tendermint/tendermint/blob/master/lite2/verifier.go#L28

@jackzampolin
Copy link
Member

Update to lite2 verifier blocked on #5527

Copy link
Member

@jackzampolin jackzampolin left a comment

Choose a reason for hiding this comment

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

Just one quick comment rn

x/ibc/02-client/client/cli/tx.go Show resolved Hide resolved
@cwgoes
Copy link
Contributor

cwgoes commented Jan 20, 2020

Update to lite2 verifier blocked on #5527

Should be unblocked now?

@@ -119,53 +120,3 @@ func (msg MsgUpdateClient) GetSignBytes() []byte {
func (msg MsgUpdateClient) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.Signer}
}

// MsgSubmitMisbehaviour defines a message to update an IBC client
type MsgSubmitMisbehaviour struct {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

replaced by evicence module's MsgSubmitEvidence

x/ibc/02-client/client/cli/query.go Show resolved Hide resolved
x/ibc/02-client/client/cli/query.go Show resolved Hide resolved
x/ibc/02-client/client/cli/tx.go Show resolved Hide resolved

// State verification functions

VerifyClientConsensusState(
Copy link
Member

Choose a reason for hiding this comment

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

Are these verification functions the ones needed to complete the handshakes? Might be helpful to annotate the methods with the different places they are anticipated to be used.

x/ibc/02-client/exported/exported.go Show resolved Hide resolved
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors"
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint"
tendermint "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint"
)

// CreateClient creates a new client state and populates it with a given consensus
// state as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#create
func (k Keeper) CreateClient(
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 this function needs some comments in it. It is quite long...

x/ibc/03-connection/keeper/handshake.go Show resolved Hide resolved
Copy link
Member

@jackzampolin jackzampolin left a comment

Choose a reason for hiding this comment

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

This PR is pretty massive. Added a few minor comments.

x/ibc/03-connection/keeper/handshake_test.go Outdated Show resolved Hide resolved
x/ibc/03-connection/types/keys.go Show resolved Hide resolved
x/ibc/04-channel/keeper/handshake.go Outdated Show resolved Hide resolved
x/ibc/04-channel/keeper/handshake.go Outdated Show resolved Hide resolved
x/ibc/04-channel/keeper/handshake.go Outdated Show resolved Hide resolved
x/ibc/04-channel/keeper/timeout.go Show resolved Hide resolved
x/ibc/04-channel/keeper/timeout.go Outdated Show resolved Hide resolved
ctx sdk.Context, connection connection.ConnectionEnd, height uint64,
proof commitment.ProofI, path string,
) bool
GetConnection(ctx sdk.Context, connectionID string) (connectiontypes.ConnectionEnd, bool)
Copy link
Member

Choose a reason for hiding this comment

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

I feel like more comments would be very helpful here.

fedekunze and others added 8 commits January 23, 2020 12:39
* Update ICS 7 tests

* Fix one problem

* Also set consensus state for height 1

* Apply same fixes to ICS 4 tests

* Remove unnecessary change

* Fix ante tests; remove printfs

* Remove printf

* Update x/ibc/ante/ante_test.go

Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>
* Add skeleton for ValidateBasic tests

* Move tests; basic one passes

* Add more ValidateBasic tests

* Add more ValidateBasic testcases

* Fix minor typo

* Add `ValidateBasic` tests for Packet

* Move to packet_test.go
Copy link
Member

@AdityaSripal AdityaSripal left a comment

Choose a reason for hiding this comment

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

I believe with the removal of the Committer type we lost some correctness properties. Think there are some issues to be resolved here that may need to be reflected up to the spec.

  1. Still need a way to retrieve the validatorSet that approved updates to client state. This can be done by persisting in the IBC store (perhaps in ConsensusState) or by passing it in as part of the message (like with fromValidatorSet) and verifying against the hash saved in ConsensusState

  2. Bisection algorithm needs to be enforced for the update functions. This requires access to lastValidatorSet

  3. Misbehavior may not be on a height that was explicitly passed in as an update. This can be accounted for by loading last consensus state saved before misbehavior

  4. I believe there needs to be a way to submit misbehavior to a frozen connection if it lowers the frozen height. Suppose height is frozen at 15, but there's evidence of misbehavior at 10. User should still be allowed to submit this evidence to lower the frozen height even further.

  5. ClientState should have an UnbondingPeriod field so that chains have some notion of what the unbonding period is for the connecting chain.

  • This can then be used to prevent updates that fastforward height beyond last_height + unbonding_period even if the header can be verified by the last ValidatorSet
  • It can also be used to prevent misbehavior from being submitted at height below last_height - unbonding_period since the keys of validator set that early may be compromised and can be used to create fake misbehavior and allow malicious user to freeze a connection. Ex: Chain is at block 1000000, validator set keys at block 1 are no longer used and compromised. User creates fake commit at height 1 with compromised keys and freezes chain all the way back at height 1.

I can work on implementing these changes once i get agreement that the problems i raised are real and consensus on the solutions i propose

if err := header.ValidateBasic(chainID); err != nil {
return err
}

Copy link
Member

Choose a reason for hiding this comment

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

Seems like with removing the Committer interface an important check was removed.

I believe we still need to make sure that this Header will be verified by the last update's ValidatorSet.

To do this, we need a way to retrieve the last ValidatorSet that caused an update on this connection and then call

lastValidatorSet.VerifyFutureCommit(header.ChainID, header.Commit.BlockID, header.Height, header.Commit)

Not sure how to do this with the current datastructures now that Committer is removed. If the full ValidatorSet gets saved in ConsensusState rather than just the hash then this is possible

The code as is seems like it will allow arbitrary updates to a connection from an arbitrary validatorset.

Need to implement the bisection algorithm for correct and safe updating

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If the full ValidatorSet gets saved in ConsensusState rather than just the hash then this is possible

@cwgoes can we update the spec to include the validator set instead of the hash?

Copy link
Contributor

Choose a reason for hiding this comment

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

Sure. cosmos/ibc#366. I'll work on this now.

clientState clientexported.ClientState,
consensusState clientexported.ConsensusState,
misbehaviour clientexported.Misbehaviour,
height uint64, // height at which the consensus state was loaded
Copy link
Member

Choose a reason for hiding this comment

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

Doesn't look like it is:
https://github.com/cosmos/cosmos-sdk/pull/5485/files#diff-be64020aa203f351d8bd639c630aa097R124

Since the height passed in is the height of the misbehaviour not the height of the consensusState. While this PR makes them the same height, i believe they may not be in general

}

committer, found := k.GetCommitter(ctx, misbehaviour.ClientID, uint64(misbehaviour.GetHeight()))
consensusState, found := k.GetConsensusState(ctx, misbehaviour.GetClientID(), uint64(misbehaviour.GetHeight()))
Copy link
Member

Choose a reason for hiding this comment

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

The consensusState may not exist for the height where the misbehavior is found.

For example, if there were client state updates at height 10 and height 20, then there will be consensus states in the store at height 10 and 20

However, a misbehavior may have first happened at height 15 forwhich there is no consensus state stored. In this case, the fromValidatorSet should be the validatorSet from height 10.

And thus the ConsensusState passed in should also be from height 10

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ok, so change it tomin(clientState.GetLatestHeight(), misbehaviour.GetHeight()) = min(20, 15) but that won't work since it'll be still 15... there won't be a consensus state stored at that height.

We'd need to iterate over the keys to get the latest height at which the consensus state was stored that satisfies height <= min(clientState.GetLatestHeight(), misbehaviour.GetHeight()) and min(clientState.GetLatestHeight(), misbehaviour.GetHeight()) - height <= trusting period

Copy link
Contributor

@cwgoes cwgoes Jan 24, 2020

Choose a reason for hiding this comment

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

Do you mean for the case where there was misbehaviour at height 15 but we are already at height 20? If so, there should be an intermediary set of two conflicting headers, and if we have a consensus state before height 15 stored the evidence should be valid (since both will be valid updates to it). I don't think this requires changes (at least to the spec). This line needs to be changed though.

Copy link
Member

@AdityaSripal AdityaSripal Jan 24, 2020

Choose a reason for hiding this comment

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

We'd need to iterate over the keys to get the latest height at which the consensus state

Yes this is true. This is what the old GetConsensusState function did under the hood. You could reimplement this (either in GetConsensusState or in another helper function)

Or you could use the iavlStore.ReverseIterator and just grab the first element. This should be faster, but i couldn't find how to get this working on my first try hence issue #5365

if we have a consensus state before height 15 stored the evidence should be valid

Yup the evidence will still be valid because the update at 20 must have been valid. However, we still need to iterate through the store to find this past consensus state

Copy link
Contributor

Choose a reason for hiding this comment

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

Yup the evidence will still be valid because the update at 20 must have been valid. However, we still need to iterate through the store to find this past consensus state

Why do we need to iterate? We know what the old height was (from the Evidence).

@cwgoes
Copy link
Contributor

cwgoes commented Jan 24, 2020

Still need a way to retrieve the validatorSet that approved updates to client state. This can be done by persisting in the IBC store (perhaps in ConsensusState) or by passing it in as part of the message (like with fromValidatorSet) and verifying against the hash saved in ConsensusState

Yes, I suggest the latter approach since it saves state space, but we could also store & prune the validator sets, that would be fine as well.

Bisection algorithm needs to be enforced for the update functions. This requires access to lastValidatorSet

This should be imported from Tendermint with the Tendermint lite client "Verify" function. The lastValidatorSet can be checked against the known hash, or we can store it.

Misbehavior may not be on a height that was explicitly passed in as an update. This can be accounted for by loading last consensus state saved before misbehavior

Misbehaviour should be able to load a particular past consensus state, as per the spec. Looking at your comment above, this is not quite what the implementation does - it should be.

I believe there needs to be a way to submit misbehavior to a frozen connection if it lowers the frozen height. Suppose height is frozen at 15, but there's evidence of misbehavior at 10. User should still be allowed to submit this evidence to lower the frozen height even further.

I agree (see above comment about "min" for frozen height).

ClientState should have an UnbondingPeriod field so that chains have some notion of what the unbonding period is for the connecting chain.

That's probably the right place to put it. Any Tendermint lite client parameters also need to be in the ClientState, I suppose. The spec should be updated for this - cosmos/ibc#365.

This can then be used to prevent updates that fastforward height beyond last_height + unbonding_period even if the header can be verified by the last ValidatorSet

This should not be necessary to separately check if the Tendermint light client's Verify function is used, as Verify (with appropriate parameters) performs bisection.

It can also be used to prevent misbehavior from being submitted at height below last_height - unbonding_period since the keys of validator set that early may be compromised and can be used to create fake misbehavior and allow malicious user to freeze a connection. Ex: Chain is at block 1000000, validator set keys at block 1 are no longer used and compromised. User creates fake commit at height 1 with compromised keys and freezes chain all the way back at height 1.

Ah yes, I agree with this, it should also be changed in the spec - cosmos/ibc#364.

* ICS 07 Debugging

* WIP Debugging

* Foo bar baz, baz bar foo, 🤷‍♂️

* cleanup print statements

* cleanup verification test

* add return err for proof verification

* cleanup; start handshake testing

* connection handshake tests

* scopelint

* WIP Test refactor

* fix ICS03 tests

* fix ICS04 tests

* nolint

Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>
x/ibc/07-tendermint/consensus_state_test.go Outdated Show resolved Hide resolved
x/ibc/02-client/keeper/client_test.go Outdated Show resolved Hide resolved
x/ibc/23-commitment/merkle_test.go Show resolved Hide resolved
x/ibc/23-commitment/merkle_test.go Show resolved Hide resolved
x/ibc/23-commitment/merkle_test.go Show resolved Hide resolved
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
suite.SetupTest() // reset

tc.malleate()
Copy link
Contributor

Choose a reason for hiding this comment

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

Using the variable on range scope tc in function literal (from scopelint)

x/ibc/03-connection/keeper/handshake_test.go Show resolved Hide resolved
x/ibc/03-connection/keeper/handshake_test.go Show resolved Hide resolved
@fedekunze fedekunze dismissed AdityaSripal’s stale review January 27, 2020 17:00

Will address in a follow up PR

@fedekunze fedekunze merged commit 3df016e into ibc-alpha Jan 27, 2020
@fedekunze fedekunze deleted the fedekunze/5476-ics-07-tendermint branch January 27, 2020 17:00
This was referenced Jan 27, 2020
// }

previousConnection, found := k.GetConnection(ctx, connectionID)
if found && !(previousConnection.State == exported.INIT &&
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe a good idea to extract this into a function previousConnection.????(exported, counterparty, version)

}
if h.ValidatorSet == nil {
return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "validator set is nil")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

don't you wanna check SignedHeader.ValidatorsHash == ValidatorSet.Hash() here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm surprised that the check is not already done on the SignedHeader.ValidateBasic() @marbar3778 @tessr

@fedekunze fedekunze mentioned this pull request Feb 3, 2020
11 tasks
@AdityaSripal AdityaSripal mentioned this pull request Feb 10, 2020
11 tasks
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.

6 participants