cosmos / ibc

Interchain Standards (ICS) for the Cosmos network & interchain ecosystem.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ICS-4(upgrades): TRY upgrade sequencing does not support crossing hellos

damiannolan opened this issue · comments

Problem definition

In the current ICS4 channel upgrades spec the upgrade sequencing does not allow for crossing hellos.

The current upgrade sequencing logic expects the initiating party to always be ahead. It is as follows:

currentSequence = provableStore.get(channelUpgradeSequencePath(portIdentifier, channelIdentifier))
if counterpartySequence > currentSequence {
    provableStore.set(channelUpgradeSequencePath(portIdentifier, channelIdentifier), counterpartySequence)
} else {
    // error on the higher sequence so that both chains move to a fresh sequence
    errorReceipt = ErrorReceipt{
        sequence: currentSequence,
        errorMsg: ""
    }
    provableStore.set(channelUpgradeErrorPath(portIdentifier, channelIdentifier), errorReceipt)
    provableStore.set(channelUpgradeSequencePath(portIdentifier, channelIdentifier), currentSequence+1)
    return
}

If INIT state transitions have already occurred on the host, then currentSequence is returned as 1 and counterpartySequence is 1 and an error receipt will be written and the upgrade is aborted due to the condition counterpartySequence > currentSequence not being satisfied.

Proposed solution

If current channel state is INITUPGRADE then just lookup current sequence and assert that it is equal to counterpartySequence then return, otherwise proceed with regular sequencing checks if channel state is OPEN.

This can be done by simply moving the sequencing logic to within the channel state conditional, which is currently done just before this logic. Below is an example of the suggested changes.

if currentChannel.state == INITUPGRADE {
    // if there is a crossing hello, ie an UpgradeInit has been called on both channelEnds,
    // then we must ensure that the proposedUpgrade by the counterparty is the same as the currentChannel
    // except for the channel state (upgrade channel will be in TRYUPGRADE and current channel will be in INITUPGRADE)
    // if the proposed upgrades on either side are incompatible, then we will restore the channel and cancel the upgrade.
+    currentSequence = provableStore.get(channelUpgradeSequencePath(portIdentifier, channelIdentifier))
+    abortTransactionUnless(currentSequence == counterpartySequence)
    
    currentChannel.state = TRYUPGRADE
    if !currentChannel.IsEqual(proposedUpgradeChannel) {
        restoreChannel(portIdentifier, channelIdentifier)
        return
    }
} else if currentChannel.state == OPEN {
    // this is first message in upgrade handshake on this chain so we must store original channel in restore channel path
    // in case we need to restore channel later.
    privateStore.set(channelRestorePath(portIdentifier, channelIdentifier), currentChannel)
    
+    // -----------------------------------
+    // MOVE ORIGINAL SEQUENCING LOGIC HERE
+    // -----------------------------------
} else {
    // abort transaction if current channel is not in state: INITUPGRADE or OPEN
    abortTransactionUnless(false)
}

Closed by #951