Skip to content

Commit

Permalink
verify upgrade timeout path (#3342)
Browse files Browse the repository at this point in the history
  • Loading branch information
charleenfei authored Mar 28, 2023
1 parent 725c13c commit 5980f88
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 0 deletions.
42 changes: 42 additions & 0 deletions modules/core/03-connection/keeper/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,48 @@ func (k Keeper) VerifyChannelUpgradeSequence(
return nil
}

// VerifyChannelUpgradeTimeout verifies the proof that a particular timeout has been stored in the upgrade timeout path.
func (k Keeper) VerifyChannelUpgradeTimeout(
ctx sdk.Context,
connection exported.ConnectionI,
height exported.Height,
proof []byte,
portID,
channelID string,
upgradeTimeout channeltypes.UpgradeTimeout,
) error {
clientID := connection.GetClientID()
clientState, clientStore, err := k.getClientStateAndVerificationStore(ctx, clientID)
if err != nil {
return err
}

if status := k.clientKeeper.GetClientStatus(ctx, clientState, clientID); status != exported.Active {
return errorsmod.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status)
}

merklePath := commitmenttypes.NewMerklePath(host.ChannelUpgradeTimeoutPath(portID, channelID))
merklePath, err = commitmenttypes.ApplyPrefix(connection.GetCounterparty().GetPrefix(), merklePath)
if err != nil {
return err
}

bz, err := k.cdc.Marshal(&upgradeTimeout)
if err != nil {
return err
}

if err := clientState.VerifyMembership(
ctx, clientStore, k.cdc, height,
0, 0, // skip delay period checks for non-packet processing verification
proof, merklePath, bz,
); err != nil {
return errorsmod.Wrapf(err, "failed upgrade timeout verification for client (%s) on channel (%s)", clientID, channelID)
}

return nil
}

// getBlockDelay calculates the block delay period from the time delay of the connection
// and the maximum expected time per block.
func (k Keeper) getBlockDelay(ctx sdk.Context, connection exported.ConnectionI) uint64 {
Expand Down
99 changes: 99 additions & 0 deletions modules/core/03-connection/keeper/verify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,105 @@ func (suite *KeeperTestSuite) TestVerifyUpgradeSequence() {
}
}

func (suite *KeeperTestSuite) TestVerifyUpgradeTimeout() {
var (
path *ibctesting.Path
upgradeTimeout channeltypes.UpgradeTimeout
)

cases := []struct {
name string
malleate func()
expPass bool
}{
{
name: "success",
malleate: func() {
},
expPass: true,
},
{
name: "fails when client state is frozen",
malleate: func() {
clientState := path.EndpointB.GetClientState().(*ibctm.ClientState)
clientState.FrozenHeight = clienttypes.NewHeight(0, 1)
path.EndpointB.SetClientState(clientState)
},
expPass: false,
},
{
name: "fails with bad client id",
malleate: func() {
connection := path.EndpointB.GetConnection()
connection.ClientId = ibctesting.InvalidID
path.EndpointB.SetConnection(connection)
},
expPass: false,
},
{
name: "verification fails when the key does not exist",
malleate: func() {
storeKey := path.EndpointA.Chain.GetSimApp().GetKey(exported.StoreKey)
kvStore := path.EndpointA.Chain.GetContext().KVStore(storeKey)
kvStore.Delete(host.ChannelUpgradeTimeoutKey(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID))
suite.coordinator.CommitBlock(suite.chainA)
},
expPass: false,
},
}

for _, tc := range cases {
tc := tc

suite.Run(tc.name, func() {
suite.SetupTest() // reset

height := clienttypes.GetSelfHeight(suite.chainB.GetContext())
timestamp := uint64(suite.chainB.GetContext().BlockTime().UnixNano())

upgradeTimeout = channeltypes.UpgradeTimeout{
TimeoutHeight: height,
TimeoutTimestamp: timestamp,
}

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

// specify a new version to upgrade to.
path.EndpointA.ChannelConfig.Version = fmt.Sprintf("%s-v2", ibcmock.Version)

// chain A initiates an upgrade and stores the timeout height/timestamp by which point chain B must have proceeded
// to the TRY step. In this case, we set it to the current height/timestamp to ensure timeout.
err := path.EndpointA.ChanUpgradeInit(height, timestamp)
suite.Require().NoError(err)

tc.malleate()

upgradeTimeoutKey := host.ChannelUpgradeTimeoutKey(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID)
proof, proofHeight := suite.chainA.QueryProof(upgradeTimeoutKey)

// The VerifyChannelUpgrade step is called by chain B, and will prove the timeout values set by chain A
// have not passed on chain B.
err = suite.chainB.GetSimApp().IBCKeeper.ConnectionKeeper.VerifyChannelUpgradeTimeout(
suite.chainB.GetContext(),
path.EndpointB.GetConnection(),
proofHeight,
proof,
// this should be the counterparty port and channel id (chain A)
path.EndpointA.ChannelConfig.PortID,
path.EndpointA.ChannelID,
upgradeTimeout,
)

if tc.expPass {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
}
})
}
}

func malleateHeight(height exported.Height, diff uint64) exported.Height {
return clienttypes.NewHeight(height.GetRevisionNumber(), height.GetRevisionHeight()+diff)
}

0 comments on commit 5980f88

Please sign in to comment.