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

Abstract Height in IBC Clients #6686

Closed
wants to merge 34 commits into from
Closed

Conversation

AdityaSripal
Copy link
Member

@AdityaSripal AdityaSripal commented Jul 10, 2020

Description

Adds height interface to 02-client, and concrete types in 07-tendermint and 09-localhost along with appropriate update/misbehaviour handling

Important Context For Reviewers:

This PR does not implement all of the abstract height functionality necessary for upgrades. Since this is already a very large change this will be done in stages. This particular PR implements the following:

  • All reference to height (except for 23-commitment which will continue using uint64) will now use the exported.Height instead of uint64 in all of ibc and ibc-transfer
  • Updates are supported within a single epoch
  • Counterparty chains are allowed to initialize at whatever epoch-number they choose (and can continue updating EpochHeight). The self-chain, however, only supports EpochNumber: 0 for now.
  • Updates to the next EpochNumber are not supported, this will be done in future PR
  • Misbehaviour that freezes a height at an earlier epoch, will cause any updates in a future epoch to fail.
  • Packets will timeout if clientstate is at epoch number heigher than timeout height

Changes to be done in Future PR:

  • Extract EpochNumber from clientID so that validators commit to an epoch number.
  • Enable Updating a client to a new EpochNumber

closes: #6531


Before we can merge this PR, please make sure that all the following items have been
checked off. If any of the checklist items are not applicable, please leave them but
write a little note why.

  • 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
  • Review Codecov Report in the comment section below once CI passes

@colin-axner
Copy link
Contributor

what's the status of this? is it blocked?

@AdityaSripal
Copy link
Member Author

Just need to fix the many merge conflicts 😱

@AdityaSripal AdityaSripal marked this pull request as ready for review July 23, 2020 21:29
Copy link
Contributor

@cwgoes cwgoes left a comment

Choose a reason for hiding this comment

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

Thanks; good start, see comments.

x/ibc/02-client/exported/exported.go Outdated Show resolved Hide resolved
// zero if a = b
// positive if a > b
//
// Decrement will return a decremented height from the given height. If this is not possible,
Copy link
Contributor

Choose a reason for hiding this comment

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

Move comment to below function?

x/ibc/02-client/exported/exported.go Outdated Show resolved Hide resolved
histInfo, found := k.stakingKeeper.GetHistoricalInfo(ctx, int64(height))
func (k Keeper) GetSelfConsensusState(ctx sdk.Context, height exported.Height) (exported.ConsensusState, bool) {
// Only support retrieving HistoricalInfo from the current epoch for now
// TODO: check EpochNumber matches expected
Copy link
Contributor

Choose a reason for hiding this comment

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

This should work across epochs, I think, so that handshakes will work cleanly across upgrades

(we should use both the epoch number & the epoch height in the key)

height := maxHeight
var success bool
for height.Valid() {
found := k.HasClientConsensusState(ctx, clientID, height)
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, this isn't very efficient. Can we iterate on the key?

Copy link
Member Author

Choose a reason for hiding this comment

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

I can look into this on a separate PR. Should be able to use iavl.ReverseIterator

@@ -63,6 +64,9 @@ func (p Packet) GetDestChannel() string { return p.DestinationChannel }
// GetData implements PacketI interface
func (p Packet) GetData() []byte { return p.Data }

// GetTimeoutEpoch implements PacketI interface
func (p Packet) GetTimeoutEpoch() uint64 { return p.TimeoutEpoch }
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, why this two-function strategy? Better to have a GetTimeoutHeight() Height function instead, no?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this has to do with proto. Since Height is not proto defined, it cannot be referenced in the .proto files (or maybe it can?) so the work around is to define the 2 uints and then construct them when needed to compare, decrement etc.

I'm not very convinced this solution is any better than just dealing with Any. I think it will lead to more technical debt/possible bugs

Copy link
Member Author

Choose a reason for hiding this comment

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

Currently this would introduce an import cycle between 02-client/exported and 04-client/exported

Copy link
Collaborator

@fedekunze fedekunze left a comment

Choose a reason for hiding this comment

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

First pass, looks great! It'd be nice if we could add LT(), Equal(), GT() util funcs for Height (i.e basically wrappers for compare).

proto/ibc/connection/connection.proto Outdated Show resolved Hide resolved
x/ibc-transfer/client/cli/tx.go Outdated Show resolved Hide resolved
x/ibc/02-client/client/cli/query.go Outdated Show resolved Hide resolved
Comment on lines 233 to 234
EpochNumber uint64 `json:"epoch_number" yaml:"epoch_number"`
EpochHeight uint64 `json:"epoch_height" yaml:"epoch_height"`
Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe for a third party, it's not so clear what an epoch means/represents. Can you add a comment + example about that on the Height godoc and also add comments on each of the fields in order to distinguish them?

x/ibc/02-client/exported/exported.go Outdated Show resolved Hide resolved
x/ibc/02-client/exported/exported.go Outdated Show resolved Hide resolved
x/ibc/02-client/exported/exported.go Outdated Show resolved Hide resolved
x/ibc/03-connection/client/cli/tx.go Show resolved Hide resolved
Copy link
Contributor

@colin-axner colin-axner left a comment

Choose a reason for hiding this comment

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

Thanks for doing this work!!!

Did a high level scan, the general approach looks good. I have some serious concerns about the current work around to just using Any. I'd be willing to lend a hand in using Any because the current approach of separating epoch number and epoch height makes me question why you would even need a Height struct at all?

proto/ibc/connection/connection.proto Outdated Show resolved Hide resolved
// Decrement will return a decremented height from the given height. If this is not possible,
// an error is returned
// Valid returns true if height is valid, false otherwise
type Height struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we should regularly rely on concretely defining objects in exported.go. It is poor code hygiene (exported does not imply type implementation). Also, I think Height needs to be a protobuf object once we migrate 02-client?

This is outside the scope of this pr, but something we should address in the future.

x/ibc/02-client/exported/exported.go Outdated Show resolved Hide resolved
x/ibc/02-client/exported/exported.go Outdated Show resolved Hide resolved
x/ibc/03-connection/handler.go Outdated Show resolved Hide resolved
x/ibc/03-connection/keeper/handshake.go Outdated Show resolved Hide resolved
@@ -63,6 +64,9 @@ func (p Packet) GetDestChannel() string { return p.DestinationChannel }
// GetData implements PacketI interface
func (p Packet) GetData() []byte { return p.Data }

// GetTimeoutEpoch implements PacketI interface
func (p Packet) GetTimeoutEpoch() uint64 { return p.TimeoutEpoch }
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this has to do with proto. Since Height is not proto defined, it cannot be referenced in the .proto files (or maybe it can?) so the work around is to define the 2 uints and then construct them when needed to compare, decrement etc.

I'm not very convinced this solution is any better than just dealing with Any. I think it will lead to more technical debt/possible bugs

AdityaSripal and others added 4 commits July 28, 2020 21:59
Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>
Co-authored-by: colin axner <25233464+colin-axner@users.noreply.github.com>
Co-authored-by: Christopher Goes <cwgoes@pluranimity.org>
Copy link
Collaborator

@fedekunze fedekunze left a comment

Choose a reason for hiding this comment

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

LGTM

Comment on lines 243 to 246
// we can call a.Compare(b) which will return
// -1 if a < b
// 0 if a = b
// 1 if a > b
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// we can call a.Compare(b) which will return
// -1 if a < b
// 0 if a = b
// 1 if a > b
// we can call a.Compare(b) which will return:
//
// - -1 if a < b
// - 0 if a = b
// - 1 if a > b

@@ -38,8 +38,9 @@ func HandleMsgCreateClient(ctx sdk.Context, k keeper.Keeper, msg exported.MsgCre
consensusHeight = msg.GetConsensusState().GetHeight()
case exported.Localhost:
// msg client id is always "localhost"
// Localhost epoch is always 0 for now
Copy link
Collaborator

Choose a reason for hiding this comment

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

@cwgoes I think this needs a note on the ICS09 spec

Copy link
Contributor

Choose a reason for hiding this comment

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

hmm, localhost clients shouldn't even have an epoch

Copy link
Contributor

Choose a reason for hiding this comment

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

nor should solo machine clients

histInfo, found := k.stakingKeeper.GetHistoricalInfo(ctx, int64(height))
func (k Keeper) GetSelfConsensusState(ctx sdk.Context, height exported.Height) (exported.ConsensusState, bool) {
// Only support retrieving HistoricalInfo from the current epoch for now
// TODO: check EpochNumber matches expected
Copy link
Collaborator

Choose a reason for hiding this comment

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

do we need to address this TODO on this PR?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it shouldn't be hard? We just need to store historical info with an epoch as well as a height

@@ -65,9 +70,12 @@ func HandleMsgConnectionOpenTry(ctx sdk.Context, k keeper.Keeper, msg *types.Msg

// HandleMsgConnectionOpenAck defines the sdk.Handler for MsgConnectionOpenAck
func HandleMsgConnectionOpenAck(ctx sdk.Context, k keeper.Keeper, msg *types.MsgConnectionOpenAck) (*sdk.Result, error) {
// For now, convert uint64 heights to clientexported.Height
Copy link
Collaborator

Choose a reason for hiding this comment

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

ditto issue reference

@@ -95,8 +103,10 @@ func HandleMsgConnectionOpenAck(ctx sdk.Context, k keeper.Keeper, msg *types.Msg

// HandleMsgConnectionOpenConfirm defines the sdk.Handler for MsgConnectionOpenConfirm
func HandleMsgConnectionOpenConfirm(ctx sdk.Context, k keeper.Keeper, msg *types.MsgConnectionOpenConfirm) (*sdk.Result, error) {
// For now, convert uint64 heights to clientexported.Height
Copy link
Collaborator

Choose a reason for hiding this comment

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

ditto issue reference

@@ -334,14 +338,15 @@ func (msg MsgChannelCloseConfirm) GetSigners() []sdk.AccAddress {

var _ sdk.Msg = &MsgRecvPacket{}

// NewMsgRecvPacket constructs new MsgRecvPacket
// NewMsgPacket constructs new MsgPacket
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// NewMsgPacket constructs new MsgPacket
// NewMsgRecvPacket constructs new MsgRecvPacket

@@ -409,12 +411,16 @@ func (chain *TestChain) ConnectionOpenTry(

proofConsensus, consensusHeight := counterparty.QueryConsensusStateProof(counterpartyConnection.ClientID)

// For now, assume our own epoch numebr is 0, while retrieving epoch number for counterparty
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// For now, assume our own epoch numebr is 0, while retrieving epoch number for counterparty
// For now, assume our own epoch number is 0, while retrieving epoch number for counterparty

@@ -430,10 +436,14 @@ func (chain *TestChain) ConnectionOpenAck(

proofConsensus, consensusHeight := counterparty.QueryConsensusStateProof(counterpartyConnection.ClientID)

// For now, assume our own epoch numebr is 0, while retrieving epoch number for counterparty
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// For now, assume our own epoch numebr is 0, while retrieving epoch number for counterparty
// For now, assume our own epoch number is 0, while retrieving epoch number for counterparty

Copy link
Contributor

@cwgoes cwgoes left a comment

Choose a reason for hiding this comment

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

See outstanding comment on fetching past self consensus states by epoch & height.

@@ -38,8 +38,9 @@ func HandleMsgCreateClient(ctx sdk.Context, k keeper.Keeper, msg exported.MsgCre
consensusHeight = msg.GetConsensusState().GetHeight()
case exported.Localhost:
// msg client id is always "localhost"
// Localhost epoch is always 0 for now
Copy link
Contributor

Choose a reason for hiding this comment

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

hmm, localhost clients shouldn't even have an epoch

@@ -38,8 +38,9 @@ func HandleMsgCreateClient(ctx sdk.Context, k keeper.Keeper, msg exported.MsgCre
consensusHeight = msg.GetConsensusState().GetHeight()
case exported.Localhost:
// msg client id is always "localhost"
// Localhost epoch is always 0 for now
Copy link
Contributor

Choose a reason for hiding this comment

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

nor should solo machine clients

histInfo, found := k.stakingKeeper.GetHistoricalInfo(ctx, int64(height))
func (k Keeper) GetSelfConsensusState(ctx sdk.Context, height exported.Height) (exported.ConsensusState, bool) {
// Only support retrieving HistoricalInfo from the current epoch for now
// TODO: check EpochNumber matches expected
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it shouldn't be hard? We just need to store historical info with an epoch as well as a height

@lgtm-com
Copy link

lgtm-com bot commented Aug 25, 2020

This pull request introduces 4 alerts when merging 541737c into d9fd4d2 - view on LGTM.com

new alerts:

  • 4 for Useless assignment to local variable

@AdityaSripal AdityaSripal marked this pull request as draft August 25, 2020 23:04
@lgtm-com
Copy link

lgtm-com bot commented Aug 25, 2020

This pull request introduces 4 alerts when merging cde1aea into d9fd4d2 - view on LGTM.com

new alerts:

  • 4 for Useless assignment to local variable

@lgtm-com
Copy link

lgtm-com bot commented Aug 26, 2020

This pull request introduces 2 alerts when merging 43ec7bf into 9fc0dbb - view on LGTM.com

new alerts:

  • 2 for Useless assignment to local variable

@AdityaSripal
Copy link
Member Author

Closed by #7184 and #7211

@AdityaSripal AdityaSripal deleted the aditya/abstract-height branch September 15, 2020 19:38
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.

Add epoch number to Tendermint light clients, update height & timeout logic
4 participants