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

refactor: restructure timeout type #5404

Merged
merged 16 commits into from
Dec 19, 2023

Conversation

colin-axner
Copy link
Contributor

@colin-axner colin-axner commented Dec 13, 2023

Description

Applies discussion in #3483. Primarily, HasPassed is renamed to Elapsed. In addition, I added two functions to the timeout type ErrTimeoutElapsed and ErrTimeoutNotReached which will return an error msg based on whether the height or the timestamp is responsible for the error. In the case of both, it defaults to reporting the height first

here are example diffs of when used by the other handlers as well:

diff --git a/modules/core/04-channel/keeper/packet.go b/modules/core/04-channel/keeper/packet.go
index 3b826dea0..fc3129988 100644
--- a/modules/core/04-channel/keeper/packet.go
+++ b/modules/core/04-channel/keeper/packet.go
@@ -74,25 +74,14 @@ func (k Keeper) SendPacket(
                return 0, errorsmod.Wrapf(clienttypes.ErrClientNotActive, "cannot send packet using client (%s) with status %s", connectionEnd.GetClientID(), status)
        }
 
-       // check if packet is timed out on the receiving chain
-       latestHeight := clientState.GetLatestHeight()
-       if !timeoutHeight.IsZero() && latestHeight.GTE(timeoutHeight) {
-               return 0, errorsmod.Wrapf(
-                       types.ErrPacketTimeout,
-                       "receiving chain block height >= packet timeout height (%s >= %s)", latestHeight, timeoutHeight,
-               )
-       }
-
        latestTimestamp, err := k.connectionKeeper.GetTimestampAtHeight(ctx, connectionEnd, latestHeight)
        if err != nil {
                return 0, err
        }
 
-       if packet.GetTimeoutTimestamp() != 0 && latestTimestamp >= packet.GetTimeoutTimestamp() {
-               return 0, errorsmod.Wrapf(
-                       types.ErrPacketTimeout,
-                       "receiving chain block timestamp >= packet timeout timestamp (%s >= %s)", time.Unix(0, int64(latestTimestamp)).UTC(), time.Unix(0, int64(packet.GetTimeoutTimestamp())).UTC(),
-               )
+       timeout := types.NewTimeout(packet.GetTimeoutHeight().(clienttypes.Height), packet.GetTimeoutTimestamp()) // could eventually be packet.GetTimeout()
+       if timeout.Elapsed(latestHeight.(clienttypes.Height), latestTimestamp) {
+               return 0, errorsmod.Wrap(timeout.ErrTimeoutElapsed(latestHeight, latestTimestamp), "invalid packet timeout")
        }
 
        commitment := types.CommitPacket(k.cdc, packet)
@@ -172,21 +161,10 @@ func (k Keeper) RecvPacket(
        }
 
        // check if packet timeouted by comparing it with the latest height of the chain
-       selfHeight := clienttypes.GetSelfHeight(ctx)
-       timeoutHeight := packet.GetTimeoutHeight()
-       if !timeoutHeight.IsZero() && selfHeight.GTE(timeoutHeight) {
-               return errorsmod.Wrapf(
-                       types.ErrPacketTimeout,
-                       "block height >= packet timeout height (%s >= %s)", selfHeight, timeoutHeight,
-               )
-       }
-
-       // check if packet timeouted by comparing it with the latest timestamp of the chain
-       if packet.GetTimeoutTimestamp() != 0 && uint64(ctx.BlockTime().UnixNano()) >= packet.GetTimeoutTimestamp() {
-               return errorsmod.Wrapf(
-                       types.ErrPacketTimeout,
-                       "block timestamp >= packet timeout timestamp (%s >= %s)", ctx.BlockTime(), time.Unix(0, int64(packet.GetTimeoutTimestamp())).UTC(),
-               )
+       selfHeight, selfTimestamp := clienttypes.GetSelfHeight(ctx), uint64(ctx.BlockTime().UnixNano())
+       timeout := types.NewTimeout(packet.GetTimeoutHeight().(clienttypes.Height), packet.GetTimeoutTimestamp())
+       if timeout.Elapsed(selfHeight, selfTimestamp) {
+               return errorsmod.Wrap(timeout.ErrTimeoutElapsed(selfHeight, selfTimestamp), "packet timeout elapsed")
        }
 
        commitment := types.CommitPacket(k.cdc, packet)
diff --git a/modules/core/04-channel/keeper/timeout.go b/modules/core/04-channel/keeper/timeout.go
index 929824fed..a19ac05f2 100644
--- a/modules/core/04-channel/keeper/timeout.go
+++ b/modules/core/04-channel/keeper/timeout.go
@@ -68,10 +68,9 @@ func (k Keeper) TimeoutPacket(
                return err
        }
 
-       timeoutHeight := packet.GetTimeoutHeight()
-       if (timeoutHeight.IsZero() || proofHeight.LT(timeoutHeight)) &&
-               (packet.GetTimeoutTimestamp() == 0 || proofTimestamp < packet.GetTimeoutTimestamp()) {
-               return errorsmod.Wrap(types.ErrPacketTimeout, "packet timeout has not been reached for height or timestamp")
+       timeout := types.NewTimeout(packet.GetTimeoutHeight().(clienttypes.Height), packet.GetTimeoutTimestamp())
+       if !timeout.Elapsed(proofHeight.(clienttypes.Height), proofTimestamp) {
+               return errorsmod.Wrap(timeout.ErrTimeoutNotReached(proofHeight.(clienttypes.Height), proofTimestamp), "packet timeout not reached")
        }

closes: #3483

Commit Message / Changelog Entry

type: commit message

see the guidelines for commit messages. (view raw markdown for examples)


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 and Go style guide.
  • Wrote unit and integration tests.
  • Updated relevant documentation (docs/) or specification (x/<module>/spec/).
  • Added relevant godoc comments.
  • Provide a commit message to be used for the changelog entry in the PR description for review.
  • Re-reviewed Files changed in the Github PR explorer.
  • Review Codecov Report in the comment section below once CI passes.


// ErrTimeoutNotReached returns a timeout not reached error indicating which timeout value
// has not been reached.
func (t Timeout) ErrTimeoutNotReached(height clienttypes.Height, timestamp uint64) error {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this can be ErrTimeoutNotElapsed if folks want

Copy link
Contributor Author

Choose a reason for hiding this comment

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

what I like about this now is that we have a generic timeout type rather than a timeout specific to channel upgrades

@colin-axner colin-axner added the channel-upgradability Channel upgradability feature label Dec 13, 2023
// ErrTimeoutElapsed returns a timeout elapsed error indicating which timeout value
// has elapsed.
func (t Timeout) ErrTimeoutElapsed(height clienttypes.Height, timestamp uint64) error {
if t.heightElapsed(height) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

happy to add an additional statement if folks want to indicate both height and timestamp elapsed. I just opted for this for now

Copy link
Contributor

@DimitrisJim DimitrisJim left a comment

Choose a reason for hiding this comment

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

overall lgtm, thanks 💪, left a couple of minor comments

tc := tc
suite.Run(tc.name, func() {
err := tc.timeout.ErrTimeoutElapsed(height, timestamp)
suite.Require().Equal(err.Error(), tc.expError.Error())
Copy link
Contributor

Choose a reason for hiding this comment

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

switcharoo on Equal args.

Suggested change
suite.Require().Equal(err.Error(), tc.expError.Error())
suite.Require().Equal(tc.expError.Error(), err.Error())

tc := tc
suite.Run(tc.name, func() {
err := tc.timeout.ErrTimeoutNotReached(height, timestamp)
suite.Require().Equal(err.Error(), tc.expError.Error())
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto re switcharoo

// ErrTimeoutNotReached returns a timeout not reached error indicating which timeout value
// has not been reached.
func (t Timeout) ErrTimeoutNotReached(height clienttypes.Height, timestamp uint64) error {
if !t.Height.IsZero() && !t.heightElapsed(height) {
Copy link
Contributor

Choose a reason for hiding this comment

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

isn't this just !t.heightElapsed(height)? Currently would expand to:

!t.Height.IsZero() && !t.Height.IsZero() && height.GTE(t.Height)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No it is: !t.Height.IsZero() && !(!t.Height.IsZero() && height.GTE(t.Height))

When t.Height.IsZero(), the second statement returns !false. This caught me while writing tests as I originally just had !t.heightElapsed(height). It's quite unfortunate and also odd. When height is 0, heightElapsed returning false makes sense, but in the case of ErrTimeoutNotReached it doesn't as the height is not being used to determine the timeout

I could pull out the !IsZero() checks out of heightElapsed and timestampElapsed or I could add a comment to explain that when asserting timeout has not been reached, we must filter out the height if it is empty

Copy link
Contributor

Choose a reason for hiding this comment

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

aha! indeed. These are slightly tricky to read, thanks for adding a comment here!

Copy link
Member

@damiannolan damiannolan left a comment

Choose a reason for hiding this comment

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

LGTM, I think there's spots in Send/RecvPacket that could make use of the new type also! Glad to see this finally in. Thanks @colin-axner

@codecov-commenter
Copy link

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (26fbb37) 80.67% compared to head (74ebc67) 80.77%.

Additional details and impacted files

Impacted file tree graph

@@                   Coverage Diff                   @@
##           04-channel-upgrades    #5404      +/-   ##
=======================================================
+ Coverage                80.67%   80.77%   +0.10%     
=======================================================
  Files                      197      197              
  Lines                    15101    15114      +13     
=======================================================
+ Hits                     12183    12209      +26     
+ Misses                    2453     2440      -13     
  Partials                   465      465              
Files Coverage Δ
modules/core/04-channel/keeper/packet.go 99.12% <100.00%> (+<0.01%) ⬆️
modules/core/04-channel/keeper/timeout.go 95.41% <100.00%> (+0.04%) ⬆️
modules/core/04-channel/keeper/upgrade.go 92.12% <100.00%> (+0.04%) ⬆️
modules/core/04-channel/types/timeout.go 100.00% <100.00%> (+65.00%) ⬆️

@crodriguezvega crodriguezvega merged commit 5ec221a into 04-channel-upgrades Dec 19, 2023
56 checks passed
@crodriguezvega crodriguezvega deleted the colin/3483-timeout-type-impl branch December 19, 2023 22:15
@crodriguezvega crodriguezvega mentioned this pull request Jan 5, 2024
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
channel-upgradability Channel upgradability feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants