github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/ibc-go/modules/core/04-channel/keeper/timeout.go (about) 1 package keeper 2 3 import ( 4 "bytes" 5 6 types2 "github.com/fibonacci-chain/fbc/libs/tendermint/types" 7 8 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 9 sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors" 10 capabilitytypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/capability/types" 11 connectiontypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/03-connection/types" 12 "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/04-channel/types" 13 host "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/24-host" 14 "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/exported" 15 ) 16 17 // TimeoutPacket is called by a module which originally attempted to send a 18 // packet to a counterparty module, where the timeout height has passed on the 19 // counterparty chain without the packet being committed, to prove that the 20 // packet can no longer be executed and to allow the calling module to safely 21 // perform appropriate state transitions. Its intended usage is within the 22 // ante handler. 23 func (k Keeper) TimeoutPacket( 24 ctx sdk.Context, 25 packet exported.PacketI, 26 proof []byte, 27 proofHeight exported.Height, 28 nextSequenceRecv uint64, 29 ) error { 30 channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) 31 if !found { 32 return sdkerrors.Wrapf( 33 types.ErrChannelNotFound, 34 "port ID (%s) channel ID (%s)", packet.GetSourcePort(), packet.GetSourceChannel(), 35 ) 36 } 37 38 // NOTE: TimeoutPacket is called by the AnteHandler which acts upon the packet.Route(), 39 // so the capability authentication can be omitted here 40 41 if packet.GetDestPort() != channel.Counterparty.PortId { 42 return sdkerrors.Wrapf( 43 types.ErrInvalidPacket, 44 "packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortId, 45 ) 46 } 47 48 if packet.GetDestChannel() != channel.Counterparty.ChannelId { 49 return sdkerrors.Wrapf( 50 types.ErrInvalidPacket, 51 "packet destination channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetDestChannel(), channel.Counterparty.ChannelId, 52 ) 53 } 54 55 connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) 56 if !found { 57 return sdkerrors.Wrap( 58 connectiontypes.ErrConnectionNotFound, 59 channel.ConnectionHops[0], 60 ) 61 } 62 63 // check that timeout height or timeout timestamp has passed on the other end 64 proofTimestamp, err := k.connectionKeeper.GetTimestampAtHeight(ctx, connectionEnd, proofHeight) 65 if err != nil { 66 return err 67 } 68 69 timeoutHeight := packet.GetTimeoutHeight() 70 if (timeoutHeight.IsZero() || proofHeight.LT(timeoutHeight)) && 71 (packet.GetTimeoutTimestamp() == 0 || proofTimestamp < packet.GetTimeoutTimestamp()) { 72 return sdkerrors.Wrap(types.ErrPacketTimeout, "packet timeout has not been reached for height or timestamp") 73 } 74 75 commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) 76 77 if len(commitment) == 0 { 78 EmitTimeoutPacketEvent(ctx, packet, channel) 79 // This error indicates that the timeout has already been relayed 80 // or there is a misconfigured relayer attempting to prove a timeout 81 // for a packet never sent. Core IBC will treat this error as a no-op in order to 82 // prevent an entire relay transaction from failing and consuming unnecessary fees. 83 return types.ErrNoOpMsg 84 } 85 86 if channel.State != types.OPEN { 87 return sdkerrors.Wrapf( 88 types.ErrInvalidChannelState, 89 "channel state is not OPEN (got %s)", channel.State.String(), 90 ) 91 } 92 93 packetCommitment := types.CommitPacket(k.cdc, packet) 94 95 // verify we sent the packet and haven't cleared it out yet 96 if !bytes.Equal(commitment, packetCommitment) { 97 return sdkerrors.Wrapf(types.ErrInvalidPacket, "packet commitment bytes are not equal: got (%v), expected (%v)", commitment, packetCommitment) 98 } 99 100 switch channel.Ordering { 101 case types.ORDERED: 102 // check that packet has not been received 103 if nextSequenceRecv > packet.GetSequence() { 104 return sdkerrors.Wrapf( 105 types.ErrPacketReceived, 106 "packet already received, next sequence receive > packet sequence (%d > %d)", nextSequenceRecv, packet.GetSequence(), 107 ) 108 } 109 110 // check that the recv sequence is as claimed 111 err = k.connectionKeeper.VerifyNextSequenceRecv( 112 ctx, connectionEnd, proofHeight, proof, 113 packet.GetDestPort(), packet.GetDestChannel(), nextSequenceRecv, 114 ) 115 case types.UNORDERED: 116 err = k.connectionKeeper.VerifyPacketReceiptAbsence( 117 ctx, connectionEnd, proofHeight, proof, 118 packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), 119 ) 120 default: 121 panic(sdkerrors.Wrapf(types.ErrInvalidChannelOrdering, channel.Ordering.String())) 122 } 123 124 if err != nil { 125 return err 126 } 127 128 // NOTE: the remaining code is located in the TimeoutExecuted function 129 return nil 130 } 131 132 // TimeoutExecuted deletes the commitment send from this chain after it verifies timeout. 133 // If the timed-out packet came from an ORDERED channel then this channel will be closed. 134 // 135 // CONTRACT: this function must be called in the IBC handler 136 func (k Keeper) TimeoutExecuted( 137 ctx sdk.Context, 138 chanCap *capabilitytypes.Capability, 139 packet exported.PacketI, 140 ) error { 141 channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) 142 if !found { 143 return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", packet.GetSourcePort(), packet.GetSourceChannel()) 144 } 145 146 capName := host.ChannelCapabilityPath(packet.GetSourcePort(), packet.GetSourceChannel()) 147 if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, capName) { 148 return sdkerrors.Wrapf( 149 types.ErrChannelCapabilityNotFound, 150 "caller does not own capability for channel with capability name %s", capName, 151 ) 152 } 153 154 k.deletePacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) 155 156 if channel.Ordering == types.ORDERED { 157 channel.State = types.CLOSED 158 k.SetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), channel) 159 } 160 161 k.Logger(ctx).Info( 162 "packet timed-out", 163 "sequence", packet.GetSequence(), 164 "src_port", packet.GetSourcePort(), 165 "src_channel", packet.GetSourceChannel(), 166 "dst_port", packet.GetDestPort(), 167 "dst_channel", packet.GetDestChannel(), 168 ) 169 170 // emit an event marking that we have processed the timeout 171 EmitTimeoutPacketEvent(ctx, packet, channel) 172 173 if channel.Ordering == types.ORDERED && channel.State == types.CLOSED { 174 EmitChannelClosedEvent(ctx, packet, channel) 175 } 176 177 return nil 178 } 179 180 // TimeoutOnClose is called by a module in order to prove that the channel to 181 // which an unreceived packet was addressed has been closed, so the packet will 182 // never be received (even if the timeoutHeight has not yet been reached). 183 func (k Keeper) TimeoutOnClose( 184 ctx sdk.Context, 185 chanCap *capabilitytypes.Capability, 186 packet exported.PacketI, 187 proof, 188 proofClosed []byte, 189 proofHeight exported.Height, 190 nextSequenceRecv uint64, 191 ) error { 192 channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) 193 if !found { 194 return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", packet.GetSourcePort(), packet.GetSourceChannel()) 195 } 196 197 capName := host.ChannelCapabilityPath(packet.GetSourcePort(), packet.GetSourceChannel()) 198 if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, capName) { 199 return sdkerrors.Wrapf( 200 types.ErrInvalidChannelCapability, 201 "channel capability failed authentication with capability name %s", capName, 202 ) 203 } 204 205 if packet.GetDestPort() != channel.Counterparty.PortId { 206 return sdkerrors.Wrapf( 207 types.ErrInvalidPacket, 208 "packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortId, 209 ) 210 } 211 212 if packet.GetDestChannel() != channel.Counterparty.ChannelId { 213 return sdkerrors.Wrapf( 214 types.ErrInvalidPacket, 215 "packet destination channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetDestChannel(), channel.Counterparty.ChannelId, 216 ) 217 } 218 219 connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) 220 if !found { 221 return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) 222 } 223 224 commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) 225 226 if len(commitment) == 0 { 227 EmitTimeoutPacketEvent(ctx, packet, channel) 228 // This error indicates that the timeout has already been relayed 229 // or there is a misconfigured relayer attempting to prove a timeout 230 // for a packet never sent. Core IBC will treat this error as a no-op in order to 231 // prevent an entire relay transaction from failing and consuming unnecessary fees. 232 return types.ErrNoOpMsg 233 } 234 235 packetCommitment := types.CommitPacket(k.cdc, packet) 236 237 // verify we sent the packet and haven't cleared it out yet 238 if !bytes.Equal(commitment, packetCommitment) { 239 return sdkerrors.Wrapf(types.ErrInvalidPacket, "packet commitment bytes are not equal: got (%v), expected (%v)", commitment, packetCommitment) 240 } 241 242 var counterpartyHops []string 243 if types2.HigherThanVenus4(ctx.BlockHeight()) { 244 counterpartyHops = []string{connectionEnd.GetCounterparty().GetConnectionID()} 245 } else { 246 counterpartyHops, found = k.CounterpartyHops(ctx, channel) 247 if !found { 248 // Should not reach here, connectionEnd was able to be retrieved above 249 panic("cannot find connection") 250 } 251 } 252 253 counterparty := types.NewCounterparty(packet.GetSourcePort(), packet.GetSourceChannel()) 254 expectedChannel := types.NewChannel( 255 types.CLOSED, channel.Ordering, counterparty, counterpartyHops, channel.Version, 256 ) 257 258 // check that the opposing channel end has closed 259 if err := k.connectionKeeper.VerifyChannelState( 260 ctx, connectionEnd, proofHeight, proofClosed, 261 channel.Counterparty.PortId, channel.Counterparty.ChannelId, 262 expectedChannel, 263 ); err != nil { 264 return err 265 } 266 267 var err error 268 switch channel.Ordering { 269 case types.ORDERED: 270 // check that packet has not been received 271 if nextSequenceRecv > packet.GetSequence() { 272 return sdkerrors.Wrapf(types.ErrInvalidPacket, "packet already received, next sequence receive > packet sequence (%d > %d", nextSequenceRecv, packet.GetSequence()) 273 } 274 275 // check that the recv sequence is as claimed 276 err = k.connectionKeeper.VerifyNextSequenceRecv( 277 ctx, connectionEnd, proofHeight, proof, 278 packet.GetDestPort(), packet.GetDestChannel(), nextSequenceRecv, 279 ) 280 case types.UNORDERED: 281 err = k.connectionKeeper.VerifyPacketReceiptAbsence( 282 ctx, connectionEnd, proofHeight, proof, 283 packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), 284 ) 285 default: 286 panic(sdkerrors.Wrapf(types.ErrInvalidChannelOrdering, channel.Ordering.String())) 287 } 288 289 if err != nil { 290 return err 291 } 292 293 // NOTE: the remaining code is located in the TimeoutExecuted function 294 return nil 295 }