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  }