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

startFlushUpgradeHandshake #3671

Merged
merged 24 commits into from
Jun 6, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a293750
first draft startflushupgradehandshake
charleenfei May 26, 2023
6e404c9
chore: adding check for upgrade sequence and additional TODOs
chatton May 29, 2023
7eb8b6e
Merge branch '04-channel-upgrades' into charly/#3648-start-flush-upgrade
chatton May 30, 2023
4355431
Merge remote-tracking branch 'origin/04-channel-upgrades' into charly…
colin-axner May 31, 2023
f212a81
refactor: update startFlushUpgradeHandshake function to latest spec c…
colin-axner May 31, 2023
bf97d38
chore: self nits on in-code documentation
colin-axner May 31, 2023
3209e57
test: add startFlushUpgradeHandshake unit tests
colin-axner May 31, 2023
cfe4502
test: fix build and test cases for startFlushUpgradeHandshake
colin-axner May 31, 2023
a04108f
chore: apply self nits
colin-axner Jun 1, 2023
7f8130d
chore: rename proofUpgrade to proofCounterpartyUpgrade
colin-axner Jun 1, 2023
c03258a
Merge remote-tracking branch 'origin/04-channel-upgrades' into charly…
colin-axner Jun 1, 2023
cf86f93
Merge remote-tracking branch 'origin/04-channel-upgrades' into charly…
colin-axner Jun 1, 2023
b73ab6a
Merge remote-tracking branch 'origin/04-channel-upgrades' into charly…
colin-axner Jun 1, 2023
9adbb34
Merge branch '04-channel-upgrades' into charly/#3648-start-flush-upgrade
colin-axner Jun 1, 2023
fb320aa
fix linter and broken test
charleenfei Jun 2, 2023
f242385
Update modules/core/04-channel/keeper/upgrade.go
colin-axner Jun 5, 2023
85df989
nit: replace invalidConnection var with ibctesting.InvalidID
colin-axner Jun 5, 2023
d3029e4
Merge remote-tracking branch 'origin/04-channel-upgrades' into charly…
colin-axner Jun 5, 2023
fa9e447
imp: use QueryChannelUpgradeProof helper function
colin-axner Jun 5, 2023
44b28e6
imp: apply todo by asserting returned err is upgrade error in certain…
colin-axner Jun 5, 2023
d46665c
Merge remote-tracking branch 'origin/04-channel-upgrades' into charly…
colin-axner Jun 5, 2023
270f067
Merge branch '04-channel-upgrades' into charly/#3648-start-flush-upgrade
charleenfei Jun 6, 2023
16f0631
Merge branch '04-channel-upgrades' into charly/#3648-start-flush-upgrade
charleenfei Jun 6, 2023
32a7865
address pr comments
charleenfei Jun 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions modules/core/04-channel/keeper/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,25 @@ package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"

clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types"
"github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"
)

// StartFlushUpgradeHandshake is a wrapper around startFlushUpgradeHandshake to allow the function to be directly called in tests.
func (k Keeper) StartFlushUpgradeHandshake(
ctx sdk.Context,
portID,
channelID string,
proposedUpgradeFields types.UpgradeFields,
counterpartyChannel types.Channel,
counterpartyUpgrade types.Upgrade,
proofCounterpartyChannel,
proofCounterpartyUpgrade []byte,
proofHeight clienttypes.Height,
) error {
return k.startFlushUpgradeHandshake(ctx, portID, channelID, proposedUpgradeFields, counterpartyChannel, counterpartyUpgrade, proofCounterpartyChannel, proofCounterpartyUpgrade, proofHeight)
}
Comment on lines +15 to +27
Copy link
Contributor

Choose a reason for hiding this comment

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

is there a strong reason that we don't just want to let StartFlushUpgradeHandshake be an exported function and not need these wrappers?

To me, adding this additional level of indirection hurts my ability to reason about where things can be used in certain ways and why.

I don't think it's so bad to just export k.startFlushUpgradeHandshake .

This is a mild preference though, if the general consensus is to have this additional wrapper I'm okay with it 👍

Copy link
Member

Choose a reason for hiding this comment

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

I did a quick check on this since it wasn't clear if having the function in a _test.go file would prevent it being used.

I added the method sig to the expected keeper interface of ChannelKeeper in transfer and there was no compiler issues and you could call the method with no issues. So to me, it seems like just making it exported from the get go is the same thing

Copy link
Contributor

@DimitrisJim DimitrisJim Jun 6, 2023

Choose a reason for hiding this comment

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

wait, the function should not be added to the binary in non-test mode, this is in go help build:

When compiling packages, build ignores files that end in '_test.go'.

the compiler as in the LSP that does checking in the IDE or does a go build succeed? (the second case would sound like a serious bug 😄, edit: well, so would the first, actually)

Copy link
Member

@damiannolan damiannolan Jun 6, 2023

Choose a reason for hiding this comment

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

You sir are correct. It was the LSP that has me misled. A make install fails rightly as you say!

Copy link
Contributor

@colin-axner colin-axner Jun 7, 2023

Choose a reason for hiding this comment

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

startFlushUpgradeHandshake should only be used during the upgrade handshake and thus does not need to be referenced outside of the 04-channel/keeper package. Given that our versioning is dependent upon API breaking changes, it gives us more flexibility to handle fixes without incrementing our major version if this function is unexported. In my opinion, only the Msg's and grpc handlers should be exported. It is unsafe to reference this function outside of the context it is called in (the upgrade message handlers).

I think in general we should aim to limit our public API as it gives us maximum development flexibility while also establishing a clear public API external developers can rely on. Unless there's a reason to make the function public, I think it is healthy to default to leaving the function private


// ValidateUpgradeFields is a wrapper around validateUpgradeFields to allow the function to be directly called in tests.
func (k Keeper) ValidateUpgradeFields(ctx sdk.Context, proposedUpgrade types.UpgradeFields, currentChannel types.Channel) error {
return k.validateUpgradeFields(ctx, proposedUpgrade, currentChannel)
Expand Down
105 changes: 105 additions & 0 deletions modules/core/04-channel/keeper/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"reflect"

errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/telemetry"
sdk "github.com/cosmos/cosmos-sdk/types"

Expand Down Expand Up @@ -109,6 +110,110 @@ func (k Keeper) WriteUpgradeTryChannel(
emitChannelUpgradeTryEvent(ctx, portID, channelID, channel, proposedUpgrade)
}

// startFlushUpgradeHandshake will verify the counterparty proposed upgrade and the current channel state.
// Once the counterparty information has been verified, it will be validated against the self proposed upgrade.
// If any of the proposed upgrade fields are incompatible, an upgrade error will be returned resulting in an
// aborted upgrade.
//
//lint:ignore U1000 Ignore unused function temporarily for debugging
func (k Keeper) startFlushUpgradeHandshake(
Copy link
Member

Choose a reason for hiding this comment

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

My understanding is that the writes that happen in the spec will happen in a separate function in the implementation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

they will happen in the WriteUpgradeInit/Try/Ack etc functions

ctx sdk.Context,
portID,
channelID string,
proposedUpgradeFields types.UpgradeFields,
counterpartyChannel types.Channel,
counterpartyUpgrade types.Upgrade,
proofCounterpartyChannel,
proofCounterpartyUpgrade []byte,
proofHeight clienttypes.Height,
) error {
channel, found := k.GetChannel(ctx, portID, channelID)
if !found {
return errorsmod.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID)
}

connection, err := k.GetConnection(ctx, channel.ConnectionHops[0])
if err != nil {
return errorsmod.Wrap(err, "failed to retrieve connection using the channel connection hops")
}

if connection.GetState() != int32(connectiontypes.OPEN) {
return errorsmod.Wrapf(connectiontypes.ErrInvalidConnectionState, "connection state is not OPEN (got %s)", connectiontypes.State(connection.GetState()).String())
}

// verify the counterparty channel state containing the upgrade sequence
if err := k.connectionKeeper.VerifyChannelState(
ctx,
connection,
proofHeight, proofCounterpartyChannel,
channel.Counterparty.PortId,
channel.Counterparty.ChannelId,
counterpartyChannel,
); err != nil {
return errorsmod.Wrap(err, "failed to verify counterparty channel state")
}

// verifies the proof that a particular proposed upgrade has been stored in the upgrade path of the counterparty
if err := k.connectionKeeper.VerifyChannelUpgrade(
ctx,
connection,
proofHeight, proofCounterpartyUpgrade,
channel.Counterparty.PortId,
channel.Counterparty.ChannelId,
counterpartyUpgrade,
); err != nil {
return errorsmod.Wrap(err, "failed to verify counterparty upgrade")
}

// the current upgrade handshake must only continue if both channels are using the same upgrade sequence,
// otherwise an error receipt must be written so that the upgrade handshake may be attempted again with synchronized sequences
if counterpartyChannel.UpgradeSequence != channel.UpgradeSequence {
// save the previous upgrade sequence for the error message
prevUpgradeSequence := channel.UpgradeSequence

// error on the higher sequence so that both chains synchronize on a fresh sequence
channel.UpgradeSequence = sdkmath.Max(counterpartyChannel.UpgradeSequence, channel.UpgradeSequence)
k.SetChannel(ctx, portID, channelID, channel)

return types.NewUpgradeError(channel.UpgradeSequence, errorsmod.Wrapf(
types.ErrIncompatibleCounterpartyUpgrade, "expected upgrade sequence (%d) to match counterparty upgrade sequence (%d)", prevUpgradeSequence, counterpartyChannel.UpgradeSequence),
)
}

// assert that both sides propose the same channel ordering
Copy link
Contributor

Choose a reason for hiding this comment

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

if we think we might need to add more checks in the future, we could consider making a helper function for all the below checks to validate the counterparty upgrade against the self upgrade, maybe something like:

    err := k.checkUpgradesForCompatibility(proposedUpgrade, counterpartyUpgrade)
    if err != nil {
        return types.NewUpgradeError(channel.UpgradeSequence, err)
    }

Copy link
Contributor

Choose a reason for hiding this comment

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

I like this suggestion a lot, this also adds a clear location for where all checks can go, and also clarifies the purpose of certain checks a lot more.

if proposedUpgradeFields.Ordering != counterpartyUpgrade.Fields.Ordering {
return types.NewUpgradeError(channel.UpgradeSequence, errorsmod.Wrapf(
types.ErrIncompatibleCounterpartyUpgrade, "expected upgrade ordering (%s) to match counterparty upgrade ordering (%d)", proposedUpgradeFields.Ordering, counterpartyUpgrade.Fields.Ordering),
colin-axner marked this conversation as resolved.
Show resolved Hide resolved
)
}

proposedConnection, err := k.GetConnection(ctx, proposedUpgradeFields.ConnectionHops[0])
if err != nil {
// NOTE: this error is expected to be unreachable as the proposed upgrade connectionID should have been
// validated in the upgrade INIT and TRY handlers
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the reason to validate in both places? The spec seems to be doing the validation only in this function and not in the handlers, right? If we keep validation in the handlers then behaviour of the implementation deviates from the spec because the implementation will fail the transaction, but not write the error receipt, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we need to use the proposedConnection in the handlers to construct the UpgradeFields so they should be verified there at least.

Copy link
Contributor

@colin-axner colin-axner Jun 5, 2023

Choose a reason for hiding this comment

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

The spec does this check twice:

// initUpgradeHandshake 

...

// proposedConnection must exist and be in OPEN state for 
// channel upgrade to be accepted
proposedConnection = provableStore.Get(connectionPath(proposedUpgradeFields.connectionHops[0])
abortTransactionUnless(proposedConnection != null && proposedConnection.state == OPEN)

and

// startFlushUpgradeHandshake 

...

// connectionHops can change in a channelUpgrade, however both sides must still be each other's counterparty.
proposedConnection = provableStore.get(connectionPath(proposedUpgradeFields.connectionHops[0])
if (proposedConnection == null || proposedConnection.state != OPEN) {
    restoreChannel(portIdentifier, channelIdentifier)
}

return types.NewUpgradeError(channel.UpgradeSequence, errorsmod.Wrap(
err, "expected proposed connection to be found"),
)
}

if proposedConnection.GetState() != int32(connectiontypes.OPEN) {
// NOTE: this error is expected to be unreachable as the proposed upgrade connectionID should have been
// validated in the upgrade INIT and TRY handlers
return types.NewUpgradeError(channel.UpgradeSequence, errorsmod.Wrapf(
connectiontypes.ErrInvalidConnectionState, "expected proposed connection to be OPEN (got %s)", connectiontypes.State(proposedConnection.GetState()).String()),
)
}

// connectionHops can change in a channelUpgrade, however both sides must still be each other's counterparty.
if counterpartyUpgrade.Fields.ConnectionHops[0] != proposedConnection.GetCounterparty().GetConnectionID() {
return types.NewUpgradeError(channel.UpgradeSequence, errorsmod.Wrapf(
types.ErrIncompatibleCounterpartyUpgrade, "counterparty upgrade connection end is not a counterparty of self proposed connection end (%s != %s)", counterpartyUpgrade.Fields.ConnectionHops[0], proposedConnection.GetCounterparty().GetConnectionID()),
)
}

return nil
}

// validateUpgradeFields validates the proposed upgrade fields against the existing channel.
// It returns an error if the following constraints are not met:
// - there exists at least one valid proposed change to the existing channel fields
Expand Down
184 changes: 184 additions & 0 deletions modules/core/04-channel/keeper/upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@ package keeper_test
import (
"fmt"

errorsmod "cosmossdk.io/errors"

connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types"
"github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"
commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types"
host "github.com/cosmos/ibc-go/v7/modules/core/24-host"
"github.com/cosmos/ibc-go/v7/modules/core/exported"
ibctesting "github.com/cosmos/ibc-go/v7/testing"
"github.com/cosmos/ibc-go/v7/testing/mock"
)

var invalidConnection = "invalid-connection"

func (suite *KeeperTestSuite) TestChanUpgradeInit() {
var (
path *ibctesting.Path
Expand Down Expand Up @@ -126,6 +131,185 @@ func (suite *KeeperTestSuite) TestChanUpgradeInit() {
}
}

// UpgradeInit will be run on chainA and startFlushUpgradeHandshake
// will be called on chainB
Comment on lines +133 to +134
Copy link
Contributor

Choose a reason for hiding this comment

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

[nit] if we're adding a docstring, we should make sure it starts with the name of the function

// TestStartFlushUpgradeHandshake tests the startFlushUpgradeHandshake.
// UpgradeInit will be run on chainA and startFlushUpgradeHandshake
// will be called on chainB

func (suite *KeeperTestSuite) TestStartFlushUpgradeHandshake() {
var (
path *ibctesting.Path
upgrade types.Upgrade
counterpartyChannel types.Channel
counterpartyUpgrade types.Upgrade
)

testCases := []struct {
name string
malleate func()
// TODO: currently the code asserts based on an expected sdk error
// Allow support for asserting either the sdk error type or that the error is an upgrade error
colin-axner marked this conversation as resolved.
Show resolved Hide resolved
expError error
}{
{
"success",
func() {},
nil,
},
{
"channel not found",
func() {
path.EndpointB.ChannelID = "invalid-channel"
path.EndpointB.ChannelConfig.PortID = "invalid-port"
},
types.ErrChannelNotFound,
},
{
"connection not found",
func() {
channel := path.EndpointB.GetChannel()
channel.ConnectionHops[0] = invalidConnection
path.EndpointB.SetChannel(channel)
},
connectiontypes.ErrConnectionNotFound,
},
{
"connection state is not in OPEN state",
func() {
conn := path.EndpointB.GetConnection()
conn.State = connectiontypes.INIT
path.EndpointB.SetConnection(conn)
},
connectiontypes.ErrInvalidConnectionState,
},
{
"failed verification for counterparty channel state due to incorrectly constructed counterparty channel",
func() {
counterpartyChannel.State = types.CLOSED
},
commitmenttypes.ErrInvalidProof,
},
{
"failed verification for counterparty upgrade due to incorrectly constructed counterparty upgrade",
func() {
counterpartyUpgrade.LatestSequenceSend = 100
},
commitmenttypes.ErrInvalidProof,
},
{
"upgrade sequence mismatch, endpointB channel upgrade sequence is ahead",
func() {
channel := path.EndpointB.GetChannel()
channel.UpgradeSequence++
path.EndpointB.SetChannel(channel)
},
types.ErrIncompatibleCounterpartyUpgrade,
colin-axner marked this conversation as resolved.
Show resolved Hide resolved
},
{
"upgrade ordering is not the same on both sides",
func() {
upgrade.Fields.Ordering = types.ORDERED
},
types.ErrIncompatibleCounterpartyUpgrade,
},
{
"proposed connection is not found",
func() {
upgrade.Fields.ConnectionHops[0] = invalidConnection
colin-axner marked this conversation as resolved.
Show resolved Hide resolved
},
connectiontypes.ErrConnectionNotFound,
},
{
"proposed connection is not in OPEN state",
func() {
// reuse existing connection to create a new connection in a non OPEN state
connectionEnd := path.EndpointB.GetConnection()
connectionEnd.State = connectiontypes.UNINITIALIZED
connectionEnd.Counterparty.ConnectionId = counterpartyUpgrade.Fields.ConnectionHops[0] // both sides must be each other's counterparty

// set proposed connection in state
proposedConnectionID := "connection-100"
suite.chainB.GetSimApp().GetIBCKeeper().ConnectionKeeper.SetConnection(suite.chainB.GetContext(), proposedConnectionID, connectionEnd)
upgrade.Fields.ConnectionHops[0] = proposedConnectionID
},
connectiontypes.ErrInvalidConnectionState,
},
{
"proposed connection ends are not each other's counterparty",
func() {
// reuse existing connection to create a new connection in a non OPEN state
connectionEnd := path.EndpointB.GetConnection()
// ensure counterparty connectionID does not match connectionID set in counterparty proposed upgrade
connectionEnd.Counterparty.ConnectionId = "connection-50"

// set proposed connection in state
proposedConnectionID := "connection-100"
suite.chainB.GetSimApp().GetIBCKeeper().ConnectionKeeper.SetConnection(suite.chainB.GetContext(), proposedConnectionID, connectionEnd)
upgrade.Fields.ConnectionHops[0] = proposedConnectionID
},
types.ErrIncompatibleCounterpartyUpgrade,
},
}

for _, tc := range testCases {
tc := tc
suite.Run(tc.name, func() {
suite.SetupTest()

path = ibctesting.NewPath(suite.chainA, suite.chainB)
suite.coordinator.Setup(path)

upgradeVersion := fmt.Sprintf("%s-v2", mock.Version)
damiannolan marked this conversation as resolved.
Show resolved Hide resolved
path.EndpointA.ChannelConfig.ProposedUpgrade.Fields.Version = upgradeVersion
err := path.EndpointA.ChanUpgradeInit()
suite.Require().NoError(err)

// ensure proof verification succeeds
err = path.EndpointB.UpdateClient()
suite.Require().NoError(err)

// same construction used by endpoint.ChanUpgradeTry
channelKey := host.ChannelKey(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)
proofChannel, proofHeight := path.EndpointA.Chain.QueryProof(channelKey)
upgradeKey := host.ChannelUpgradeKey(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)
proofUpgrade, _ := path.EndpointA.Chain.QueryProof(upgradeKey)
damiannolan marked this conversation as resolved.
Show resolved Hide resolved

counterpartyChannel = path.EndpointA.GetChannel()

var found bool
counterpartyUpgrade, found = path.EndpointA.Chain.App.GetIBCKeeper().ChannelKeeper.GetUpgrade(path.EndpointA.Chain.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)
suite.Require().True(found)

// ensure that the channel has a valid upgrade sequence
channel := path.EndpointB.GetChannel()
channel.UpgradeSequence = 1
path.EndpointB.SetChannel(channel)

upgrade = types.Upgrade{
Fields: types.UpgradeFields{
Ordering: types.UNORDERED,
ConnectionHops: []string{path.EndpointB.ConnectionID},
Version: upgradeVersion,
},
Timeout: types.NewTimeout(path.EndpointA.Chain.GetTimeoutHeight(), 0),
LatestSequenceSend: 1,
}

tc.malleate()

err = suite.chainB.GetSimApp().IBCKeeper.ChannelKeeper.StartFlushUpgradeHandshake(
suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, upgrade.Fields,
counterpartyChannel, counterpartyUpgrade, proofChannel, proofUpgrade, proofHeight,
)

if tc.expError != nil {
suite.Require().Error(err)

suite.Require().True(errorsmod.IsOf(err, tc.expError), err)
} else {
suite.Require().NoError(err)
}
})
}
}

func (suite *KeeperTestSuite) TestValidateProposedUpgradeFields() {
var (
proposedUpgrade *types.UpgradeFields
Expand Down
19 changes: 10 additions & 9 deletions modules/core/04-channel/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,14 @@ var (
// Perform a no-op on the current Msg
ErrNoOpMsg = errorsmod.Register(SubModuleName, 23, "message is redundant, no-op will be performed")

ErrInvalidChannelVersion = errorsmod.Register(SubModuleName, 24, "invalid channel version")
ErrPacketNotSent = errorsmod.Register(SubModuleName, 25, "packet has not been sent")
ErrInvalidTimeout = errorsmod.Register(SubModuleName, 26, "invalid packet timeout")
ErrInvalidUpgradeTimeout = errorsmod.Register(SubModuleName, 27, "either timeout height or timeout timestamp must be non-zero")
ErrUpgradeSequenceNotFound = errorsmod.Register(SubModuleName, 28, "upgrade sequence not found")
ErrUpgradeErrorNotFound = errorsmod.Register(SubModuleName, 29, "upgrade error receipt not found")
ErrInvalidUpgrade = errorsmod.Register(SubModuleName, 30, "invalid upgrade")
ErrInvalidUpgradeSequence = errorsmod.Register(SubModuleName, 31, "invalid upgrade sequence")
ErrUpgradeNotFound = errorsmod.Register(SubModuleName, 32, "upgrade not found")
ErrInvalidChannelVersion = errorsmod.Register(SubModuleName, 24, "invalid channel version")
ErrPacketNotSent = errorsmod.Register(SubModuleName, 25, "packet has not been sent")
ErrInvalidTimeout = errorsmod.Register(SubModuleName, 26, "invalid packet timeout")
ErrInvalidUpgradeTimeout = errorsmod.Register(SubModuleName, 27, "either timeout height or timeout timestamp must be non-zero")
damiannolan marked this conversation as resolved.
Show resolved Hide resolved
ErrUpgradeSequenceNotFound = errorsmod.Register(SubModuleName, 28, "upgrade sequence not found")
damiannolan marked this conversation as resolved.
Show resolved Hide resolved
ErrUpgradeErrorNotFound = errorsmod.Register(SubModuleName, 29, "upgrade error receipt not found")
ErrInvalidUpgrade = errorsmod.Register(SubModuleName, 30, "invalid upgrade")
ErrInvalidUpgradeSequence = errorsmod.Register(SubModuleName, 31, "invalid upgrade sequence")
ErrUpgradeNotFound = errorsmod.Register(SubModuleName, 32, "upgrade not found")
ErrIncompatibleCounterpartyUpgrade = errorsmod.Register(SubModuleName, 33, "incompatible counterparty upgrade")
)
9 changes: 9 additions & 0 deletions modules/core/04-channel/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ type ConnectionKeeper interface {
channelID string,
nextSequenceRecv uint64,
) error
VerifyChannelUpgrade(
ctx sdk.Context,
connection exported.ConnectionI,
height exported.Height,
proof []byte,
portID,
channelID string,
upgrade Upgrade,
) error
}

// PortKeeper expected account IBC port keeper
Expand Down