github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/ibc-go/modules/apps/29-fee/ibc_middleware.go (about) 1 package fee 2 3 import ( 4 "strings" 5 6 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 7 sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors" 8 capabilitytypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/capability/types" 9 channeltypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/04-channel/types" 10 porttypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/05-port/types" 11 12 "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/apps/29-fee/keeper" 13 "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/apps/29-fee/types" 14 "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/exported" 15 ) 16 17 var _ porttypes.Middleware = &IBCMiddleware{} 18 19 // IBCMiddleware implements the ICS26 callbacks for the fee middleware given the 20 // fee keeper and the underlying application. 21 type IBCMiddleware struct { 22 app porttypes.IBCModule 23 keeper keeper.Keeper 24 } 25 26 // NewIBCMiddleware creates a new IBCMiddlware given the keeper and underlying application 27 func NewIBCMiddleware(app porttypes.IBCModule, k keeper.Keeper) IBCMiddleware { 28 return IBCMiddleware{ 29 app: app, 30 keeper: k, 31 } 32 } 33 34 // OnChanOpenInit implements the IBCMiddleware interface 35 func (im IBCMiddleware) OnChanOpenInit( 36 ctx sdk.Context, 37 order channeltypes.Order, 38 connectionHops []string, 39 portID string, 40 channelID string, 41 chanCap *capabilitytypes.Capability, 42 counterparty channeltypes.Counterparty, 43 version string, 44 ) (string, error) { 45 var versionMetadata types.Metadata 46 47 if strings.TrimSpace(version) == "" { 48 // default version 49 versionMetadata = types.Metadata{ 50 FeeVersion: types.Version, 51 AppVersion: "", 52 } 53 } else { 54 if err := types.ModuleCdc.UnmarshalJSON([]byte(version), &versionMetadata); err != nil { 55 // Since it is valid for fee version to not be specified, the above middleware version may be for a middleware 56 // lower down in the stack. Thus, if it is not a fee version we pass the entire version string onto the underlying 57 // application. 58 return im.app.OnChanOpenInit(ctx, order, connectionHops, portID, channelID, 59 chanCap, counterparty, version) 60 } 61 } 62 63 if versionMetadata.FeeVersion != types.Version { 64 return "", sdkerrors.Wrapf(types.ErrInvalidVersion, "expected %s, got %s", types.Version, versionMetadata.FeeVersion) 65 } 66 67 appVersion, err := im.app.OnChanOpenInit(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, versionMetadata.AppVersion) 68 if err != nil { 69 return "", err 70 } 71 72 versionMetadata.AppVersion = appVersion 73 versionBytes, err := types.ModuleCdc.MarshalJSON(&versionMetadata) 74 if err != nil { 75 return "", err 76 } 77 78 im.keeper.SetFeeEnabled(ctx, portID, channelID) 79 80 // call underlying app's OnChanOpenInit callback with the appVersion 81 return string(versionBytes), nil 82 } 83 84 // OnChanOpenTry implements the IBCMiddleware interface 85 // If the channel is not fee enabled the underlying application version will be returned 86 // If the channel is fee enabled we merge the underlying application version with the ics29 version 87 func (im IBCMiddleware) OnChanOpenTry( 88 ctx sdk.Context, 89 order channeltypes.Order, 90 connectionHops []string, 91 portID, 92 channelID string, 93 chanCap *capabilitytypes.Capability, 94 counterparty channeltypes.Counterparty, 95 version string, 96 counterpartyVersion string, 97 ) (string, error) { 98 var versionMetadata types.Metadata 99 if err := types.ModuleCdc.UnmarshalJSON([]byte(counterpartyVersion), &versionMetadata); err != nil { 100 // Since it is valid for fee version to not be specified, the above middleware version may be for a middleware 101 // lower down in the stack. Thus, if it is not a fee version we pass the entire version string onto the underlying 102 // application. 103 return im.app.OnChanOpenTry(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, version, counterpartyVersion) 104 } 105 106 if versionMetadata.FeeVersion != types.Version { 107 return "", sdkerrors.Wrapf(types.ErrInvalidVersion, "expected %s, got %s", types.Version, versionMetadata.FeeVersion) 108 } 109 110 im.keeper.SetFeeEnabled(ctx, portID, channelID) 111 112 // call underlying app's OnChanOpenTry callback with the app versions 113 appVersion, err := im.app.OnChanOpenTry(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, version, versionMetadata.AppVersion) 114 if err != nil { 115 return "", err 116 } 117 118 versionMetadata.AppVersion = appVersion 119 versionBytes, err := types.ModuleCdc.MarshalJSON(&versionMetadata) 120 if err != nil { 121 return "", err 122 } 123 124 return string(versionBytes), nil 125 } 126 127 // OnChanOpenAck implements the IBCMiddleware interface 128 func (im IBCMiddleware) OnChanOpenAck( 129 ctx sdk.Context, 130 portID, 131 channelID string, 132 counterpartyChannelID string, 133 counterpartyVersion string, 134 ) error { 135 // If handshake was initialized with fee enabled it must complete with fee enabled. 136 // If handshake was initialized with fee disabled it must complete with fee disabled. 137 if im.keeper.IsFeeEnabled(ctx, portID, channelID) { 138 var versionMetadata types.Metadata 139 if err := types.ModuleCdc.UnmarshalJSON([]byte(counterpartyVersion), &versionMetadata); err != nil { 140 return sdkerrors.Wrapf(err, "failed to unmarshal ICS29 counterparty version metadata: %s", counterpartyVersion) 141 } 142 143 if versionMetadata.FeeVersion != types.Version { 144 return sdkerrors.Wrapf(types.ErrInvalidVersion, "expected counterparty fee version: %s, got: %s", types.Version, versionMetadata.FeeVersion) 145 } 146 147 // call underlying app's OnChanOpenAck callback with the counterparty app version. 148 return im.app.OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, versionMetadata.AppVersion) 149 } 150 151 // call underlying app's OnChanOpenAck callback with the counterparty app version. 152 return im.app.OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, counterpartyVersion) 153 } 154 155 // OnChanOpenConfirm implements the IBCMiddleware interface 156 func (im IBCMiddleware) OnChanOpenConfirm( 157 ctx sdk.Context, 158 portID, 159 channelID string, 160 ) error { 161 // call underlying app's OnChanOpenConfirm callback. 162 return im.app.OnChanOpenConfirm(ctx, portID, channelID) 163 } 164 165 // OnChanCloseInit implements the IBCMiddleware interface 166 func (im IBCMiddleware) OnChanCloseInit( 167 ctx sdk.Context, 168 portID, 169 channelID string, 170 ) error { 171 if err := im.app.OnChanCloseInit(ctx, portID, channelID); err != nil { 172 return err 173 } 174 175 if !im.keeper.IsFeeEnabled(ctx, portID, channelID) { 176 return nil 177 } 178 179 if im.keeper.IsLocked(ctx) { 180 return types.ErrFeeModuleLocked 181 } 182 183 if err := im.keeper.RefundFeesOnChannelClosure(ctx, portID, channelID); err != nil { 184 return err 185 } 186 187 return nil 188 } 189 190 // OnChanCloseConfirm implements the IBCMiddleware interface 191 func (im IBCMiddleware) OnChanCloseConfirm( 192 ctx sdk.Context, 193 portID, 194 channelID string, 195 ) error { 196 if err := im.app.OnChanCloseConfirm(ctx, portID, channelID); err != nil { 197 return err 198 } 199 200 if !im.keeper.IsFeeEnabled(ctx, portID, channelID) { 201 return nil 202 } 203 204 if im.keeper.IsLocked(ctx) { 205 return types.ErrFeeModuleLocked 206 } 207 208 if err := im.keeper.RefundFeesOnChannelClosure(ctx, portID, channelID); err != nil { 209 return err 210 } 211 212 return nil 213 } 214 215 // OnRecvPacket implements the IBCMiddleware interface. 216 // If fees are not enabled, this callback will default to the ibc-core packet callback 217 func (im IBCMiddleware) OnRecvPacket( 218 ctx sdk.Context, 219 packet channeltypes.Packet, 220 relayer sdk.AccAddress, 221 ) exported.Acknowledgement { 222 if !im.keeper.IsFeeEnabled(ctx, packet.DestinationPort, packet.DestinationChannel) { 223 return im.app.OnRecvPacket(ctx, packet, relayer) 224 } 225 226 ack := im.app.OnRecvPacket(ctx, packet, relayer) 227 228 // in case of async aknowledgement (ack == nil) store the relayer address for use later during async WriteAcknowledgement 229 if ack == nil { 230 im.keeper.SetRelayerAddressForAsyncAck(ctx, channeltypes.NewPacketId(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()), relayer.String()) 231 return nil 232 } 233 234 // if forwardRelayer is not found we refund recv_fee 235 forwardRelayer, _ := im.keeper.GetCounterpartyPayeeAddress(ctx, relayer.String(), packet.GetDestChannel()) 236 237 return types.NewIncentivizedAcknowledgement(forwardRelayer, ack.Acknowledgement(), ack.Success()) 238 } 239 240 // OnAcknowledgementPacket implements the IBCMiddleware interface 241 // If fees are not enabled, this callback will default to the ibc-core packet callback 242 func (im IBCMiddleware) OnAcknowledgementPacket( 243 ctx sdk.Context, 244 packet channeltypes.Packet, 245 acknowledgement []byte, 246 relayer sdk.AccAddress, 247 ) error { 248 if !im.keeper.IsFeeEnabled(ctx, packet.SourcePort, packet.SourceChannel) { 249 return im.app.OnAcknowledgementPacket(ctx, packet, acknowledgement, relayer) 250 } 251 252 var ack = &types.IncentivizedAcknowledgement{} 253 if err := types.ModuleCdc.UnmarshalJSON(acknowledgement, ack); err != nil { 254 return sdkerrors.Wrapf(err, "cannot unmarshal ICS-29 incentivized packet acknowledgement: %v", ack) 255 } 256 257 if im.keeper.IsLocked(ctx) { 258 // if the fee keeper is locked then fee logic should be skipped 259 // this may occur in the presence of a severe bug which leads to invalid state 260 // the fee keeper will be unlocked after manual intervention 261 // the acknowledgement has been unmarshalled into an ics29 acknowledgement 262 // since the counterparty is still sending incentivized acknowledgements 263 // for fee enabled channels 264 // 265 // Please see ADR 004 for more information. 266 return im.app.OnAcknowledgementPacket(ctx, packet, ack.AppAcknowledgement, relayer) 267 } 268 269 packetID := channeltypes.NewPacketId(packet.SourcePort, packet.SourceChannel, packet.Sequence) 270 feesInEscrow, found := im.keeper.GetFeesInEscrow(ctx, packetID) 271 if !found { 272 // call underlying callback 273 return im.app.OnAcknowledgementPacket(ctx, packet, ack.AppAcknowledgement, relayer) 274 } 275 276 payee, found := im.keeper.GetPayeeAddress(ctx, relayer.String(), packet.SourceChannel) 277 if !found { 278 im.keeper.DistributePacketFeesOnAcknowledgement(ctx, ack.ForwardRelayerAddress, relayer, feesInEscrow.PacketFees, packetID) 279 280 // call underlying callback 281 return im.app.OnAcknowledgementPacket(ctx, packet, ack.AppAcknowledgement, relayer) 282 } 283 284 payeeAddr, err := sdk.AccAddressFromBech32(payee) 285 if err != nil { 286 return sdkerrors.Wrapf(err, "failed to create sdk.Address from payee: %s", payee) 287 } 288 289 im.keeper.DistributePacketFeesOnAcknowledgement(ctx, ack.ForwardRelayerAddress, payeeAddr, feesInEscrow.PacketFees, packetID) 290 291 // call underlying callback 292 return im.app.OnAcknowledgementPacket(ctx, packet, ack.AppAcknowledgement, relayer) 293 } 294 295 // OnTimeoutPacket implements the IBCMiddleware interface 296 // If fees are not enabled, this callback will default to the ibc-core packet callback 297 func (im IBCMiddleware) OnTimeoutPacket( 298 ctx sdk.Context, 299 packet channeltypes.Packet, 300 relayer sdk.AccAddress, 301 ) error { 302 // if the fee keeper is locked then fee logic should be skipped 303 // this may occur in the presence of a severe bug which leads to invalid state 304 // the fee keeper will be unlocked after manual intervention 305 // 306 // Please see ADR 004 for more information. 307 if !im.keeper.IsFeeEnabled(ctx, packet.SourcePort, packet.SourceChannel) || im.keeper.IsLocked(ctx) { 308 return im.app.OnTimeoutPacket(ctx, packet, relayer) 309 } 310 311 packetID := channeltypes.NewPacketId(packet.SourcePort, packet.SourceChannel, packet.Sequence) 312 feesInEscrow, found := im.keeper.GetFeesInEscrow(ctx, packetID) 313 if !found { 314 // call underlying callback 315 return im.app.OnTimeoutPacket(ctx, packet, relayer) 316 } 317 318 payee, found := im.keeper.GetPayeeAddress(ctx, relayer.String(), packet.SourceChannel) 319 if !found { 320 im.keeper.DistributePacketFeesOnTimeout(ctx, relayer, feesInEscrow.PacketFees, packetID) 321 322 // call underlying callback 323 return im.app.OnTimeoutPacket(ctx, packet, relayer) 324 } 325 326 payeeAddr, err := sdk.AccAddressFromBech32(payee) 327 if err != nil { 328 return sdkerrors.Wrapf(err, "failed to create sdk.Address from payee: %s", payee) 329 } 330 331 im.keeper.DistributePacketFeesOnTimeout(ctx, payeeAddr, feesInEscrow.PacketFees, packetID) 332 333 // call underlying callback 334 return im.app.OnTimeoutPacket(ctx, packet, relayer) 335 } 336 337 // SendPacket implements the ICS4 Wrapper interface 338 func (im IBCMiddleware) SendPacket( 339 ctx sdk.Context, 340 chanCap *capabilitytypes.Capability, 341 packet exported.PacketI, 342 ) error { 343 return im.keeper.SendPacket(ctx, chanCap, packet) 344 } 345 346 // WriteAcknowledgement implements the ICS4 Wrapper interface 347 func (im IBCMiddleware) WriteAcknowledgement( 348 ctx sdk.Context, 349 chanCap *capabilitytypes.Capability, 350 packet exported.PacketI, 351 ack exported.Acknowledgement, 352 ) error { 353 return im.keeper.WriteAcknowledgement(ctx, chanCap, packet, ack) 354 } 355 356 // GetAppVersion returns the application version of the underlying application 357 func (im IBCMiddleware) GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) { 358 return im.keeper.GetAppVersion(ctx, portID, channelID) 359 } 360 361 func (im IBCMiddleware) NegotiateAppVersion(ctx sdk.Context, order channeltypes.Order, connectionID string, portID string, counterparty channeltypes.Counterparty, proposedVersion string) (version string, err error) { 362 return version, nil 363 }