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

ChanUpgradeTimeout handler for 04-channel #3829

Merged
merged 30 commits into from
Jun 17, 2023

Conversation

charleenfei
Copy link
Contributor

Description

closes: #3747


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.

@charleenfei charleenfei marked this pull request as ready for review June 13, 2023 10:23
@@ -45,4 +45,8 @@ var (
ErrInvalidUpgradeSequence = errorsmod.Register(SubModuleName, 29, "invalid upgrade sequence")
ErrUpgradeNotFound = errorsmod.Register(SubModuleName, 30, "upgrade not found")
ErrIncompatibleCounterpartyUpgrade = errorsmod.Register(SubModuleName, 31, "incompatible counterparty upgrade")
ErrInvalidUpgradeError = errorsmod.Register(SubModuleName, 32, "invalid upgrade error")
ErrUpgradeRestoreFailed = errorsmod.Register(SubModuleName, 33, "restore failed")
ErrUpgradeTimeout = errorsmod.Register(SubModuleName, 34, "upgrade timed-out")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

i find these two upgrade timeout errors a bit confusingly similar, but i wasnt really sure exactly how to modify it to make it better.

@@ -45,4 +45,8 @@ var (
ErrInvalidUpgradeSequence = errorsmod.Register(SubModuleName, 29, "invalid upgrade sequence")
ErrUpgradeNotFound = errorsmod.Register(SubModuleName, 30, "upgrade not found")
ErrIncompatibleCounterpartyUpgrade = errorsmod.Register(SubModuleName, 31, "incompatible counterparty upgrade")
ErrInvalidUpgradeError = errorsmod.Register(SubModuleName, 32, "invalid upgrade error")
ErrUpgradeRestoreFailed = errorsmod.Register(SubModuleName, 33, "restore failed")
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 error is coming from the WriteUpgradeTimeoutPR

@charleenfei charleenfei changed the title upgrade timeout handler ChanUpgradeTimeout handler for 04-channel Jun 13, 2023
// was for a previous sequence by the timeout deadline.
upgradeSequence := channel.UpgradeSequence
if upgradeSequence < prevErrorReceipt.Sequence {
return errorsmod.Wrapf(types.ErrInvalidUpgradeSequence, "previous counterparty error receipt sequence is greater than our current upgrade sequence: %d > %d", prevErrorReceipt.Sequence, upgradeSequence)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

idea: maybe we could also just call ChanUpgradeCancel here in this case, instead of forcing the user to call.

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.

Approving for correctness, I have a minor request on test coverage

Copy link
Contributor

@chatton chatton 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 are a few small things we can clean up to simplify the code in the handler + test cases, but overall this looks great!

And nice test coverage!

Comment on lines 365 to 366
if (timeout.Height.IsZero() || proofHeight.LT(timeout.Height)) &&
(timeout.Timestamp == 0 || proofTimestamp < timeout.Timestamp) {
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe we can split this up into some named booleans to aid readability.

proofHeightIsInvalid := timeout.Height.IsZero() || proofHeight.LT(timeout.Height)
proofTimestampIsInvalid := timeout.Timestamp == 0 || proofTimestamp < timeout.Timestamp
if proofHeightIsInvalid && proofTimestampIsInvalid {
    return errorsmod.Wrap(types.ErrInvalidUpgradeTimeout, "timeout has not yet passed on counterparty chain")
} 

Copy link
Contributor

Choose a reason for hiding this comment

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

I also stared at this for a bit trying to grok it. Naming is definitely beneficial here.

}

// Error receipt passed in is either nil or it is a stale error receipt from a previous upgrade
if (prevErrorReceipt == types.ErrorReceipt{}) {
Copy link
Contributor

Choose a reason for hiding this comment

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

do we want to change the prevErrorReceipt to be of type *types.ErrorReceipt. This would be more in line with the spec. There is no functional difference anyway.

if err != nil {
return errorsmod.Wrap(err, "failed to verify absence of counterparty channel upgrade error receipt")
}
} else {
Copy link
Contributor

Choose a reason for hiding this comment

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

[nit] to reduce indentation we can remove the else, and just add a return nil the line above this.

modules/core/04-channel/keeper/upgrade_test.go Outdated Show resolved Hide resolved
modules/core/04-channel/keeper/upgrade_test.go Outdated Show resolved Hide resolved
"timeout has not passed",
func() {
upgrade := path.EndpointA.GetProposedUpgrade()
upgrade.Timeout.Height = clienttypes.NewHeight(1, uint64(path.EndpointA.Chain.GetContext().BlockHeight()+100))
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
upgrade.Timeout.Height = clienttypes.NewHeight(1, uint64(path.EndpointA.Chain.GetContext().BlockHeight()+100))
upgrade.Timeout.Height = suite.chainA.GetTimeoutHeight()

@charleenfei charleenfei merged commit 1625adb into 04-channel-upgrades Jun 17, 2023
46 checks passed
@charleenfei charleenfei deleted the charly/#3747-upgrade-timeout-hand branch June 17, 2023 18:12
// timeout for this sequence can only succeed if the error receipt written into the error path on the counterparty
// was for a previous sequence by the timeout deadline.
upgradeSequence := channel.UpgradeSequence
if upgradeSequence < prevErrorReceipt.Sequence {
Copy link
Contributor

@colin-axner colin-axner Jun 19, 2023

Choose a reason for hiding this comment

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

I think this needs to be <=. We should not timeout an upgrade where our sequence matches the counterparty upgrade sequence and there exists an error receipt for that upgrade attempt. That should indicate a cancellation

note, I think using <= would match the spec as the spec asserts:

abortTransactionUnless(sequence > prevErrorReceipt.sequence)

Thus if the sequence == error receipt sequence, the spec fails but this implementation does not

@AdityaSripal

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i can make a followup PR for this issue! but i think also, now looking at the two functions, im really wondering if there is a way to combine these two such that there is first a check on proofHeight to see if it has passed the Timeout. if so, then we follow the Timeout flow. Otherwise, we follow the Cancel flow. and then we just expose one API/msg_server function. wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ref #3905

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