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