Skip to content

Commit f3e9f95

Browse files
seantkingcolin-axnerAdityaSripal
authored
ics4 callbacks fee middleware (#580)
* feat: adding WriteAcknowledgement * updating genesis & relayer prefix * fix: comment * fix: comments * Update modules/apps/29-fee/keeper/relay.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * feat: add DeleteForwardRelayerAddr helper + use Set in ack * fix: SetForwardAddr * chore: add panic * fix: remove fmt * test: add WriteAcknowledgement test * Update modules/apps/29-fee/ibc_module.go Co-authored-by: Aditya <adityasripal@gmail.com> * fix: remove print * fix: WriteAck * fix: use constructor * Update modules/apps/29-fee/keeper/keeper.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * fix: nits * fix: remove found var not used * test: adding check that forward relayer address is successfully deleted if set * fix: merge issues Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: Aditya <adityasripal@gmail.com>
1 parent 9285133 commit f3e9f95

23 files changed

+691
-207
lines changed

docs/ibc/proto-docs.md

+20-2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
- [ibc/applications/fee/v1/genesis.proto](#ibc/applications/fee/v1/genesis.proto)
3636
- [FeeEnabledChannel](#ibc.applications.fee.v1.FeeEnabledChannel)
37+
- [ForwardRelayerAddress](#ibc.applications.fee.v1.ForwardRelayerAddress)
3738
- [GenesisState](#ibc.applications.fee.v1.GenesisState)
3839
- [RegisteredRelayerAddress](#ibc.applications.fee.v1.RegisteredRelayerAddress)
3940

@@ -749,7 +750,7 @@ and an optional list of relayers that are permitted to receive the fee.
749750
<a name="ibc.applications.fee.v1.FeeEnabledChannel"></a>
750751

751752
### FeeEnabledChannel
752-
Contains the PortID & ChannelID for a fee enabled channel
753+
FeeEnabledChannel contains the PortID & ChannelID for a fee enabled channel
753754

754755

755756
| Field | Type | Label | Description |
@@ -762,6 +763,22 @@ Contains the PortID & ChannelID for a fee enabled channel
762763

763764

764765

766+
<a name="ibc.applications.fee.v1.ForwardRelayerAddress"></a>
767+
768+
### ForwardRelayerAddress
769+
ForwardRelayerAddress contains the forward relayer address and packetId used for async acknowledgements
770+
771+
772+
| Field | Type | Label | Description |
773+
| ----- | ---- | ----- | ----------- |
774+
| `address` | [string](#string) | | |
775+
| `packet_id` | [ibc.core.channel.v1.PacketId](#ibc.core.channel.v1.PacketId) | | |
776+
777+
778+
779+
780+
781+
765782
<a name="ibc.applications.fee.v1.GenesisState"></a>
766783

767784
### GenesisState
@@ -773,6 +790,7 @@ GenesisState defines the fee middleware genesis state
773790
| `identified_fees` | [IdentifiedPacketFee](#ibc.applications.fee.v1.IdentifiedPacketFee) | repeated | |
774791
| `fee_enabled_channels` | [FeeEnabledChannel](#ibc.applications.fee.v1.FeeEnabledChannel) | repeated | |
775792
| `registered_relayers` | [RegisteredRelayerAddress](#ibc.applications.fee.v1.RegisteredRelayerAddress) | repeated | |
793+
| `forward_relayers` | [ForwardRelayerAddress](#ibc.applications.fee.v1.ForwardRelayerAddress) | repeated | |
776794

777795

778796

@@ -782,7 +800,7 @@ GenesisState defines the fee middleware genesis state
782800
<a name="ibc.applications.fee.v1.RegisteredRelayerAddress"></a>
783801

784802
### RegisteredRelayerAddress
785-
Contains the address and counterparty address for a specific relayer (for distributing fees)
803+
RegisteredRelayerAddress contains the address and counterparty address for a specific relayer (for distributing fees)
786804

787805

788806
| Field | Type | Label | Description |

go.mod

+6-7
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ require (
2727
gopkg.in/yaml.v2 v2.4.0
2828
)
2929

30+
require (
31+
github.com/gin-gonic/gin v1.7.0 // indirect
32+
github.com/opencontainers/image-spec v1.0.2 // indirect
33+
github.com/opencontainers/runc v1.0.3 // indirect
34+
)
35+
3036
require (
3137
filippo.io/edwards25519 v1.0.0-beta.2 // indirect
3238
github.com/99designs/keyring v1.1.6 // indirect
@@ -117,10 +123,3 @@ require (
117123
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
118124
nhooyr.io/websocket v1.8.6 // indirect
119125
)
120-
121-
require (
122-
github.com/cosmos/ibc-go v1.2.5
123-
github.com/gin-gonic/gin v1.7.0 // indirect
124-
github.com/opencontainers/image-spec v1.0.2 // indirect
125-
github.com/opencontainers/runc v1.0.3 // indirect
126-
)

go.sum

-4
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,6 @@ github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY
209209
github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw=
210210
github.com/cosmos/iavl v0.17.3 h1:s2N819a2olOmiauVa0WAhoIJq9EhSXE9HDBAoR9k+8Y=
211211
github.com/cosmos/iavl v0.17.3/go.mod h1:prJoErZFABYZGDHka1R6Oay4z9PrNeFFiMKHDAMOi4w=
212-
github.com/cosmos/ibc-go v1.2.5 h1:BiA48yKEDUcabBRkmp7qqSX41ZrgXTSNCtdjDURbLwE=
213-
github.com/cosmos/ibc-go v1.2.5/go.mod h1:wkGkkX8Ou6yXgE8lO2xP9NOwo+Tl5x1dJaTTE6jBDpg=
214212
github.com/cosmos/ledger-cosmos-go v0.11.1 h1:9JIYsGnXP613pb2vPjFeMMjBI5lEDsEaF6oYorTy6J4=
215213
github.com/cosmos/ledger-cosmos-go v0.11.1/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY=
216214
github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI=
@@ -630,7 +628,6 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
630628
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
631629
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
632630
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
633-
github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
634631
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
635632
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
636633
github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
@@ -1458,7 +1455,6 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
14581455
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
14591456
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
14601457
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
1461-
gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
14621458
gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=
14631459
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
14641460
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=

modules/apps/29-fee/ibc_module.go

+5-6
Original file line numberDiff line numberDiff line change
@@ -185,14 +185,13 @@ func (im IBCModule) OnRecvPacket(
185185
ack := im.app.OnRecvPacket(ctx, packet, relayer)
186186

187187
forwardRelayer, found := im.keeper.GetCounterpartyAddress(ctx, relayer.String())
188-
if !found {
189-
forwardRelayer = ""
190-
}
191188

192-
return types.IncentivizedAcknowledgement{
193-
Result: ack.Acknowledgement(),
194-
ForwardRelayerAddress: forwardRelayer,
189+
// incase of async aknowledgement (ack == nil) store the ForwardRelayer address for use later
190+
if ack == nil && found {
191+
im.keeper.SetForwardRelayerAddress(ctx, channeltypes.NewPacketId(packet.GetSourceChannel(), packet.GetSourcePort(), packet.GetSequence()), forwardRelayer)
195192
}
193+
194+
return types.NewIncentivizedAcknowledgement(forwardRelayer, ack.Acknowledgement())
196195
}
197196

198197
// OnAcknowledgementPacket implements the IBCModule interface

modules/apps/29-fee/keeper/escrow.go

-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ func (k Keeper) EscrowPacketFee(ctx sdk.Context, identifiedFee *types.Identified
3737
}
3838

3939
// Store fee in state for reference later
40-
// feeInEscrow/<port-id>/<channel-id>/packet/<sequence-id>/ -> Fee (timeout, ack, onrecv)
4140
k.SetFeeInEscrow(ctx, identifiedFee)
4241
return nil
4342
}

modules/apps/29-fee/keeper/genesis.go

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ func (k Keeper) InitGenesis(ctx sdk.Context, state types.GenesisState) {
1616
k.SetCounterpartyAddress(ctx, addr.Address, addr.CounterpartyAddress)
1717
}
1818

19+
for _, forwardAddr := range state.ForwardRelayers {
20+
k.SetForwardRelayerAddress(ctx, forwardAddr.PacketId, forwardAddr.Address)
21+
}
22+
1923
for _, enabledChan := range state.FeeEnabledChannels {
2024
k.SetFeeEnabled(ctx, enabledChan.PortId, enabledChan.ChannelId)
2125
}
@@ -27,5 +31,6 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState {
2731
IdentifiedFees: k.GetAllIdentifiedPacketFees(ctx),
2832
FeeEnabledChannels: k.GetAllFeeEnabledChannels(ctx),
2933
RegisteredRelayers: k.GetAllRelayerAddresses(ctx),
34+
ForwardRelayers: k.GetAllForwardRelayerAddresses(ctx),
3035
}
3136
}

modules/apps/29-fee/keeper/genesis_test.go

+7
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ func (suite *KeeperTestSuite) TestExportGenesis() {
9494
// set counterparty address
9595
suite.chainA.GetSimApp().IBCFeeKeeper.SetCounterpartyAddress(suite.chainA.GetContext(), sender, counterparty)
9696

97+
// set forward relayer address
98+
suite.chainA.GetSimApp().IBCFeeKeeper.SetForwardRelayerAddress(suite.chainA.GetContext(), packetId, sender)
99+
97100
// export genesis
98101
genesisState := suite.chainA.GetSimApp().IBCFeeKeeper.ExportGenesis(suite.chainA.GetContext())
99102

@@ -110,4 +113,8 @@ func (suite *KeeperTestSuite) TestExportGenesis() {
110113
// check registered relayer addresses
111114
suite.Require().Equal(sender, genesisState.RegisteredRelayers[0].Address)
112115
suite.Require().Equal(counterparty, genesisState.RegisteredRelayers[0].CounterpartyAddress)
116+
117+
// check registered relayer addresses
118+
suite.Require().Equal(sender, genesisState.ForwardRelayers[0].Address)
119+
suite.Require().Equal(packetId, genesisState.ForwardRelayers[0].PacketId)
113120
}

modules/apps/29-fee/keeper/keeper.go

+55-17
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package keeper
22

33
import (
4+
"strconv"
45
"strings"
56

67
"github.com/cosmos/cosmos-sdk/codec"
@@ -12,7 +13,6 @@ import (
1213
"github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types"
1314
channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types"
1415
host "github.com/cosmos/ibc-go/v3/modules/core/24-host"
15-
ibcexported "github.com/cosmos/ibc-go/v3/modules/core/exported"
1616
)
1717

1818
// Middleware must implement types.ChannelKeeper and types.PortKeeper expected interfaces
@@ -77,22 +77,6 @@ func (k Keeper) GetFeeModuleAddress() sdk.AccAddress {
7777
return k.authKeeper.GetModuleAddress(types.ModuleName)
7878
}
7979

80-
// SendPacket wraps IBC ChannelKeeper's SendPacket function
81-
func (k Keeper) SendPacket(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet ibcexported.PacketI) error {
82-
return k.ics4Wrapper.SendPacket(ctx, chanCap, packet)
83-
}
84-
85-
// WriteAcknowledgement wraps IBC ChannelKeeper's WriteAcknowledgement function
86-
func (k Keeper) WriteAcknowledgement(
87-
ctx sdk.Context,
88-
chanCap *capabilitytypes.Capability,
89-
packet ibcexported.PacketI,
90-
acknowledgement []byte,
91-
) error {
92-
return nil
93-
// return k.channelKeeper.WriteAcknowledgement(ctx, chanCap, packet, acknowledgement)
94-
}
95-
9680
// SetFeeEnabled sets a flag to determine if fee handling logic should run for the given channel
9781
// identified by channel and port identifiers.
9882
func (k Keeper) SetFeeEnabled(ctx sdk.Context, portID, channelID string) {
@@ -169,6 +153,7 @@ func (k Keeper) GetCounterpartyAddress(ctx sdk.Context, address string) (string,
169153
return addr, true
170154
}
171155

156+
// GetAllRelayerAddresses returns all registered relayer addresses
172157
func (k Keeper) GetAllRelayerAddresses(ctx sdk.Context) []*types.RegisteredRelayerAddress {
173158
store := ctx.KVStore(k.storeKey)
174159
iterator := sdk.KVStorePrefixIterator(store, []byte(types.RelayerAddressKeyPrefix))
@@ -189,6 +174,59 @@ func (k Keeper) GetAllRelayerAddresses(ctx sdk.Context) []*types.RegisteredRelay
189174
return registeredAddrArr
190175
}
191176

177+
// SetForwardRelayerAddress sets the forward relayer address during OnRecvPacket in case of async acknowledgement
178+
func (k Keeper) SetForwardRelayerAddress(ctx sdk.Context, packetId *channeltypes.PacketId, address string) {
179+
store := ctx.KVStore(k.storeKey)
180+
store.Set(types.KeyForwardRelayerAddress(packetId), []byte(address))
181+
}
182+
183+
// GetForwardRelayerAddress gets forward relayer address for a particular packet
184+
func (k Keeper) GetForwardRelayerAddress(ctx sdk.Context, packetId *channeltypes.PacketId) (string, bool) {
185+
store := ctx.KVStore(k.storeKey)
186+
key := types.KeyForwardRelayerAddress(packetId)
187+
if !store.Has(key) {
188+
return "", false
189+
}
190+
191+
addr := string(store.Get(key))
192+
return addr, true
193+
}
194+
195+
// GetAllForwardRelayerAddresses returns all forward relayer addresses stored for async acknowledgements
196+
func (k Keeper) GetAllForwardRelayerAddresses(ctx sdk.Context) []*types.ForwardRelayerAddress {
197+
store := ctx.KVStore(k.storeKey)
198+
iterator := sdk.KVStorePrefixIterator(store, []byte(types.ForwardRelayerPrefix))
199+
defer iterator.Close()
200+
201+
var forwardRelayerAddr []*types.ForwardRelayerAddress
202+
for ; iterator.Valid(); iterator.Next() {
203+
keySplit := strings.Split(string(iterator.Key()), "/")
204+
205+
seq, err := strconv.ParseUint(keySplit[3], 0, 64)
206+
if err != nil {
207+
panic("failed to parse packet sequence in forward relayer address mapping")
208+
}
209+
210+
packetId := channeltypes.NewPacketId(keySplit[2], keySplit[1], seq)
211+
212+
addr := &types.ForwardRelayerAddress{
213+
Address: string(iterator.Value()),
214+
PacketId: packetId,
215+
}
216+
217+
forwardRelayerAddr = append(forwardRelayerAddr, addr)
218+
}
219+
220+
return forwardRelayerAddr
221+
}
222+
223+
// Deletes the forwardRelayerAddr associated with the packetId
224+
func (k Keeper) DeleteForwardRelayerAddress(ctx sdk.Context, packetId *channeltypes.PacketId) {
225+
store := ctx.KVStore(k.storeKey)
226+
key := types.KeyForwardRelayerAddress(packetId)
227+
store.Delete(key)
228+
}
229+
192230
// Stores a Fee for a given packet in state
193231
func (k Keeper) SetFeeInEscrow(ctx sdk.Context, fee *types.IdentifiedPacketFee) {
194232
store := ctx.KVStore(k.storeKey)

modules/apps/29-fee/keeper/relay.go

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package keeper
2+
3+
import (
4+
sdk "github.com/cosmos/cosmos-sdk/types"
5+
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
6+
7+
"github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types"
8+
channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types"
9+
ibcexported "github.com/cosmos/ibc-go/v3/modules/core/exported"
10+
)
11+
12+
// SendPacket wraps IBC ChannelKeeper's SendPacket function
13+
func (k Keeper) SendPacket(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet ibcexported.PacketI) error {
14+
return k.ics4Wrapper.SendPacket(ctx, chanCap, packet)
15+
}
16+
17+
// WriteAcknowledgement wraps IBC ChannelKeeper's WriteAcknowledgement function
18+
// ICS29 WriteAcknowledgement is used for asynchronous acknowledgements
19+
func (k Keeper) WriteAcknowledgement(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet ibcexported.PacketI, acknowledgement []byte) error {
20+
// retrieve the forward relayer that was stored in `onRecvPacket`
21+
packetId := channeltypes.NewPacketId(packet.GetSourceChannel(), packet.GetSourcePort(), packet.GetSequence())
22+
relayer, _ := k.GetForwardRelayerAddress(ctx, packetId)
23+
24+
k.DeleteForwardRelayerAddress(ctx, packetId)
25+
26+
ack := types.NewIncentivizedAcknowledgement(relayer, acknowledgement)
27+
bz := ack.Acknowledgement()
28+
29+
// ics4Wrapper may be core IBC or higher-level middleware
30+
return k.ics4Wrapper.WriteAcknowledgement(ctx, chanCap, packet, bz)
31+
}
+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package keeper_test
2+
3+
import (
4+
clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types"
5+
channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types"
6+
)
7+
8+
func (suite *KeeperTestSuite) TestWriteAcknowledgementAsync() {
9+
testCases := []struct {
10+
name string
11+
malleate func()
12+
expPass bool
13+
}{
14+
{
15+
"success",
16+
func() {},
17+
true,
18+
},
19+
{
20+
"forward relayer address is successfully deleted",
21+
func() {
22+
suite.chainB.GetSimApp().IBCFeeKeeper.SetForwardRelayerAddress(suite.chainB.GetContext(), channeltypes.NewPacketId(suite.path.EndpointA.ChannelID, suite.path.EndpointA.ChannelConfig.PortID, 1), suite.chainA.SenderAccount.GetAddress().String())
23+
},
24+
true,
25+
},
26+
}
27+
28+
for _, tc := range testCases {
29+
tc := tc
30+
suite.Run(tc.name, func() {
31+
suite.SetupTest()
32+
33+
// open incentivized channel
34+
suite.coordinator.Setup(suite.path)
35+
36+
// build packet
37+
timeoutTimestamp := ^uint64(0)
38+
packet := channeltypes.NewPacket(
39+
[]byte("packetData"),
40+
1,
41+
suite.path.EndpointA.ChannelConfig.PortID,
42+
suite.path.EndpointA.ChannelID,
43+
suite.path.EndpointB.ChannelConfig.PortID,
44+
suite.path.EndpointB.ChannelID,
45+
clienttypes.ZeroHeight(),
46+
timeoutTimestamp,
47+
)
48+
49+
ack := []byte("ack")
50+
chanCap := suite.chainB.GetChannelCapability(suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID)
51+
52+
// malleate test case
53+
tc.malleate()
54+
55+
err := suite.chainB.GetSimApp().IBCFeeKeeper.WriteAcknowledgement(suite.chainB.GetContext(), chanCap, packet, ack)
56+
57+
if tc.expPass {
58+
suite.Require().NoError(err)
59+
_, found := suite.chainB.GetSimApp().IBCFeeKeeper.GetForwardRelayerAddress(suite.chainB.GetContext(), channeltypes.NewPacketId(suite.path.EndpointA.ChannelID, suite.path.EndpointA.ChannelConfig.PortID, 1))
60+
suite.Require().False(found)
61+
} else {
62+
suite.Require().Error(err)
63+
}
64+
})
65+
}
66+
}

modules/apps/29-fee/types/ack.go

+8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ import (
44
sdk "github.com/cosmos/cosmos-sdk/types"
55
)
66

7+
// NewIncentivizedAcknowledgement creates a new instance of IncentivizedAcknowledgement
8+
func NewIncentivizedAcknowledgement(relayer string, ack []byte) IncentivizedAcknowledgement {
9+
return IncentivizedAcknowledgement{
10+
Result: ack,
11+
ForwardRelayerAddress: relayer,
12+
}
13+
}
14+
715
// Success implements the Acknowledgement interface. The acknowledgement is
816
// considered successful if the forward relayer address is empty. Otherwise it is
917
// considered a failed acknowledgement.

0 commit comments

Comments
 (0)