cosmos / ibc-go

Inter-Blockchain Communication Protocol (IBC) implementation in Golang.
https://ibc.cosmos.network/
MIT License
524 stars 564 forks source link

Comments after alpha audit of path forwarding #6696

Closed crodriguezvega closed 19 hours ago

crodriguezvega commented 1 week ago

From internal alpha audit of ICS20 v2 path forwarding:

Thank you @chatton for leading the walkthrough and the rest of the team for the feedback.

damiannolan commented 6 days ago

Add hops to the ack error while propogating back to source so we know which chain the packet failed or timed out on [could also do counter]. Include also the deterministic error string from the error acknowledgement written by the chain where the forwarding failed

Spent some time looking at this with @chatton today! This is not so straight forward to do, we must also consider that the final destination hop may or may not be on ics20-1. In any case, it seems better to add the path forwarding info here, accepting the ack as an additional argument from the caller, here.

Using something like the following (where packet is the forwardPacket, and not the reverse (prevPacket):

// NewForwardErrorAcknowledgement returns a new error acknowledgement with path forwarding information.
func NewForwardErrorAcknowledgement(packet channeltypes.Packet, ack channeltypes.Acknowledgement) channeltypes.Acknowledgement {
    ackErr := fmt.Sprintf("%s/%s/%s", packet.GetSourcePort(), packet.GetSourceChannel(), ack.GetError())
    return channeltypes.Acknowledgement{
        Response: &channeltypes.Acknowledgement_Error{
            Error: ackErr,
        },
    }
}

Consider a path of len(4), where the final destination is ics20-1. We fail with error acknolwedgement for {code}. Acks would be propagated like so:

Ultimately, we cannot control if the final hop has upgrade yet or not. However, we can add packet.DestPort()/packet.DestChannel() to ALL error acknowledgements using ibc-go/v9. For example:

-func NewErrorAcknowledgement(err error) Acknowledgement {
+func NewErrorAcknowledgement(portID, channelID string, err error) Acknowledgement {
        // the ABCI code is included in the abcitypes.ResponseDeliverTx hash
        // constructed in Tendermint and is therefore deterministic
        _, code, _ := errorsmod.ABCIInfo(err, false) // discard non-deterministic codespace and log values

        return Acknowledgement{
                Response: &Acknowledgement_Error{
-                       Error: fmt.Sprintf("ABCI code: %d: %s", code, ackErrorString),
+                       Error: fmt.Sprintf("%s/%s ABCI code: %d: %s", portID, channelID, code, ackErrorString),
                },
        }
 }

I think this would give us a bit more info, but no guarantees that the final destination has been upgraded.

Personally, I think other solutions sound pretty hacky. You could add some index to the error string in the acknowledgement, and parse that string on every async ack write, and increment. And then use the index to determine the failed hop when you have been propagated back to the original src and have all forwarding path info available. I would vote against this!

cc. @AdityaSripal

edit: We've decided to go with source port and channel only, as this will match the user provided info in Forwarding.Hop. See #6731