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

ics04: Channel Upgrade Packet Flushing Logic #982

Merged
merged 7 commits into from
Jul 6, 2023
Merged
Changes from all commits
Commits
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
30 changes: 26 additions & 4 deletions spec/core/ics-004-channel-and-packet-semantics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ In order to provide the desired ordering, exactly-once delivery, and module perm

`Identifier`, `get`, `set`, `delete`, `getCurrentHeight`, and module-system related primitives are as defined in [ICS 24](../ics-024-host-requirements).

See [upgrades spec](./UPGRADES.md) for definition of `pendingInflightPackets`.

A *channel* is a pipeline for exactly-once packet delivery between specific modules on separate blockchains, which has at least one end capable of sending packets and one end capable of receiving packets.

A *bidirectional* channel is a channel where packets can flow in both directions: from `A` to `B` and from `B` to `A`.
Expand Down Expand Up @@ -75,6 +77,8 @@ interface ChannelEnd {
counterpartyChannelIdentifier: Identifier
connectionHops: [Identifier]
version: string
upgradeSequence: uint64
flushStatus: FlushStatus
}
```

Expand All @@ -88,6 +92,8 @@ interface ChannelEnd {
- The `connectionHops` stores the list of connection identifiers, in order, along which packets sent on this channel will travel. At the moment this list must be of length 1. In the future multi-hop channels may be supported.
- The `version` string stores an opaque channel version, which is agreed upon during the handshake. This can determine module-level configuration such as which packet encoding is used for the channel. This version is not used by the core IBC protocol. If the version string contains structured metadata for the application to parse and interpret, then it is considered best practice to encode all metadata in a JSON struct and include the marshalled string in the version field.

See the [upgrade spec](./UPGRADES.md) for details on `sequence` and `flushStatus`.

AdityaSripal marked this conversation as resolved.
Show resolved Hide resolved
Channel ends have a *state*:

```typescript
Expand Down Expand Up @@ -487,6 +493,7 @@ function chanCloseInit(
abortTransactionUnless(connection !== null)
abortTransactionUnless(connection.state === OPEN)
channel.state = CLOSED
channel.flushStatus = NOTINFLUSH
provableStore.set(channelPath(portIdentifier, channelIdentifier), channel)
}
```
Expand Down Expand Up @@ -527,6 +534,7 @@ function chanCloseConfirm(
expected
))
channel.state = CLOSED
channel.flushStatus = NOTINFLUSH
provableStore.set(channelPath(portIdentifier, channelIdentifier), channel)
}
```
Expand Down Expand Up @@ -589,7 +597,7 @@ function sendPacket(

// check that the channel is not closed to send packets;
abortTransactionUnless(channel !== null)
abortTransactionUnless(channel.state !== CLOSED)
abortTransactionUnless(channel.state !== CLOSED && channel.flushStatus === NOTINFLUSH)
connection = provableStore.get(connectionPath(channel.connectionHops[0]))
abortTransactionUnless(connection !== null)

Expand Down Expand Up @@ -653,7 +661,8 @@ function recvPacket(

channel = provableStore.get(channelPath(packet.destPort, packet.destChannel))
abortTransactionUnless(channel !== null)
abortTransactionUnless(channel.state === OPEN)
counterpartyLastPacketSent = privateStore.get(channelCounterpartyLastPacketSequencePath(packet.destPort, packet.destChannel)
abortTransactionUnless(channel.state === OPEN || (channel.flushStatus === FLUSHING && packet.sequence <= counterpartyLasPacketSent))
abortTransactionUnless(authenticateCapability(channelCapabilityPath(packet.destPort, packet.destChannel), capability))
abortTransactionUnless(packet.sourcePort === channel.counterpartyPortIdentifier)
abortTransactionUnless(packet.sourceChannel === channel.counterpartyChannelIdentifier)
Expand Down Expand Up @@ -843,7 +852,7 @@ function acknowledgePacket(
// abort transaction unless that channel is open, calling module owns the associated port, and the packet fields match
channel = provableStore.get(channelPath(packet.sourcePort, packet.sourceChannel))
abortTransactionUnless(channel !== null)
abortTransactionUnless(channel.state === OPEN)
abortTransactionUnless(channel.state === OPEN || channel.flushStatus === FLUSHING)
abortTransactionUnless(authenticateCapability(channelCapabilityPath(packet.sourcePort, packet.sourceChannel), capability))
abortTransactionUnless(packet.destPort === channel.counterpartyPortIdentifier)
abortTransactionUnless(packet.destChannel === channel.counterpartyChannelIdentifier)
Expand Down Expand Up @@ -879,6 +888,12 @@ function acknowledgePacket(
// delete our commitment so we can't "acknowledge" again
provableStore.delete(packetCommitmentPath(packet.sourcePort, packet.sourceChannel, packet.sequence))

// if there are no in-flight packets on our end, we can automatically go to FLUSHCOMPLETE
if channel.flushStatus === FLUSHING && pendingInflightPackets(packet.sourcePort, packet.sourceChannel) == nil {
channel.flushStatus = FLUSHCOMPLETE
provableStore.set(channelPath(packet.sourcePort, packet.sourceChannel), channel)
}

// return transparent packet
return packet
}
Expand Down Expand Up @@ -1013,10 +1028,17 @@ function timeoutPacket(
// delete our commitment
provableStore.delete(packetCommitmentPath(packet.sourcePort, packet.sourceChannel, packet.sequence))

// if there are no in-flight packets on our end, we can automatically go to FLUSHCOMPLETE
if channel.flushStatus === FLUSHING && pendingInflightPackets(packet.sourcePort, packet.sourceChannel) == nil {
channel.flushStatus = FLUSHCOMPLETE
provableStore.set(channelPath(packet.sourcePort, packet.sourceChannel), channel)
}

// only close on strictly ORDERED channels
if channel.order === ORDERED {
// ordered channel: close the channel
// ordered channel: close the channel and reset flushStatus
channel.state = CLOSED
channel.flushStatus = NOTINFLUSH
provableStore.set(channelPath(packet.sourcePort, packet.sourceChannel), channel)
}
// on ORDERED_ALLOW_TIMEOUT, increment NextSequenceAck so that next packet can be acknowledged after this packet timed out.
Expand Down