-
Notifications
You must be signed in to change notification settings - Fork 684
/
Copy pathibc_module.go
203 lines (170 loc) · 7.93 KB
/
ibc_module.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package v2
import (
"bytes"
"context"
"fmt"
"strings"
errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/ibc-go/v9/modules/apps/transfer/keeper"
"github.com/cosmos/ibc-go/v9/modules/apps/transfer/types"
channeltypes "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types"
channeltypesv2 "github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/types"
"github.com/cosmos/ibc-go/v9/modules/core/api"
ibcerrors "github.com/cosmos/ibc-go/v9/modules/core/errors"
)
var _ api.IBCModule = (*IBCModule)(nil)
// NewIBCModule creates a new IBCModule given the keeper
func NewIBCModule(k keeper.Keeper) *IBCModule {
return &IBCModule{
keeper: k,
}
}
type IBCModule struct {
keeper keeper.Keeper
}
func (im *IBCModule) OnSendPacket(goCtx context.Context, sourceChannel string, destinationChannel string, sequence uint64, payload channeltypesv2.Payload, signer sdk.AccAddress) error {
// Enforce that the source and destination portIDs are the same and equal to the transfer portID
// This is necessary for IBC Eureka since the portIDs (and thus the application-application connection) is not prenegotiated
// by the channel handshake
// This restriction can be removed in a future where the trace hop on receive commits to **both** the source and destination portIDs
// rather than just the destination port
if payload.SourcePort != types.PortID || payload.DestinationPort != types.PortID {
return errorsmod.Wrapf(channeltypesv2.ErrInvalidPacket, "payload port ID is invalid: expected %s, got sourcePort: %s destPort: %s", types.PortID, payload.SourcePort, payload.DestinationPort)
}
data, err := types.UnmarshalPacketData(payload.Value, payload.Version, payload.Encoding)
if err != nil {
return err
}
sender, err := sdk.AccAddressFromBech32(data.Sender)
if err != nil {
return err
}
if !signer.Equals(sender) {
return errorsmod.Wrapf(ibcerrors.ErrUnauthorized, "sender %s is different from signer %s", sender, signer)
}
// Enforce that the base denom does not contain any slashes
// Since IBC v2 packets will no longer have channel identifiers, we cannot rely
// on the channel format to easily divide the trace from the base denomination in ICS20 v1 packets
// The simplest way to prevent any potential issues from arising is to simply disallow any slashes in the base denomination
// This prevents such denominations from being sent with IBCV v2 packets, however we can still support them in IBC v1 packets
// If we enforce that IBC v2 packets are sent with ICS20 v2 and above versions that separate the trace from the base denomination
// in the packet data, then we can remove this restriction.
for _, token := range data.Tokens {
if strings.Contains(token.Denom.Base, "/") {
return errorsmod.Wrapf(types.ErrInvalidDenomForTransfer, "base denomination %s cannot contain slashes for IBC v2 packet", token.Denom.Base)
}
}
if err := im.keeper.SendTransfer(goCtx, payload.SourcePort, sourceChannel, data.Tokens, signer); err != nil {
return err
}
// TODO: events
// events.EmitTransferEvent(ctx, sender.String(), receiver, tokens, memo, hops)
// TODO: telemetry
// telemetry.ReportTransfer(sourcePort, sourceChannel, destinationPort, destinationChannel, tokens)
return nil
}
func (im *IBCModule) OnRecvPacket(ctx context.Context, sourceChannel string, destinationChannel string, sequence uint64, payload channeltypesv2.Payload, relayer sdk.AccAddress) channeltypesv2.RecvPacketResult {
// Enforce that the source and destination portIDs are the same and equal to the transfer portID
// This is necessary for IBC Eureka since the portIDs (and thus the application-application connection) is not prenegotiated
// by the channel handshake
// This restriction can be removed in a future where the trace hop on receive commits to **both** the source and destination portIDs
// rather than just the destination port
if payload.SourcePort != types.PortID || payload.DestinationPort != types.PortID {
return channeltypesv2.RecvPacketResult{
Status: channeltypesv2.PacketStatus_Failure,
}
}
var (
ackErr error
data types.FungibleTokenPacketDataV2
)
ack := channeltypes.NewResultAcknowledgement([]byte{byte(1)})
recvResult := channeltypesv2.RecvPacketResult{
Status: channeltypesv2.PacketStatus_Success,
Acknowledgement: ack.Acknowledgement(),
}
// we are explicitly wrapping this emit event call in an anonymous function so that
// the packet data is evaluated after it has been assigned a value.
defer func() {
if err := im.keeper.EmitOnRecvPacketEvent(ctx, data, ack, ackErr); err != nil {
im.keeper.Logger.Error(fmt.Sprintf("failed to emit %T event", channeltypesv2.EventTypeRecvPacket), "error", err)
}
}()
data, ackErr = types.UnmarshalPacketData(payload.Value, payload.Version, payload.Encoding)
if ackErr != nil {
im.keeper.Logger.Error(fmt.Sprintf("%s sequence %d", ackErr.Error(), sequence))
return channeltypesv2.RecvPacketResult{
Status: channeltypesv2.PacketStatus_Failure,
}
}
if _, ackErr = im.keeper.OnRecvPacket(
ctx,
data,
payload.SourcePort,
sourceChannel,
payload.DestinationPort,
destinationChannel,
); ackErr != nil {
im.keeper.Logger.Error(fmt.Sprintf("%s sequence %d", ackErr.Error(), sequence))
return channeltypesv2.RecvPacketResult{
Status: channeltypesv2.PacketStatus_Failure,
}
}
im.keeper.Logger.Info("successfully handled ICS-20 packet", "sequence", sequence)
// TODO: telemetry
// telemetry.ReportOnRecvPacket(packet, data.Tokens)
if data.HasForwarding() {
// we are now sending from the forward escrow address to the final receiver address.
return channeltypesv2.RecvPacketResult{
Status: channeltypesv2.PacketStatus_Failure,
}
// TODO: handle forwarding
// TODO: inside this version of the function, we should fetch the packet that was stored in IBC core in order to set it for forwarding.
// if err := k.forwardPacket(ctx, data, packet, receivedCoins); err != nil {
// return err
// }
// NOTE: acknowledgement will be written asynchronously
// return types.RecvPacketResult{
// Status: types.PacketStatus_Async,
// }
}
// NOTE: acknowledgement will be written synchronously during IBC handler execution.
return recvResult
}
func (im *IBCModule) OnTimeoutPacket(ctx context.Context, sourceChannel string, destinationChannel string, sequence uint64, payload channeltypesv2.Payload, relayer sdk.AccAddress) error {
data, err := types.UnmarshalPacketData(payload.Value, payload.Version, payload.Encoding)
if err != nil {
return err
}
// refund tokens
if err := im.keeper.OnTimeoutPacket(ctx, payload.SourcePort, sourceChannel, data); err != nil {
return err
}
// TODO: handle forwarding
return im.keeper.EmitOnTimeoutEvent(ctx, data)
}
func (im *IBCModule) OnAcknowledgementPacket(ctx context.Context, sourceChannel string, destinationChannel string, sequence uint64, acknowledgement []byte, payload channeltypesv2.Payload, relayer sdk.AccAddress) error {
var ack channeltypes.Acknowledgement
// construct an error acknowledgement if the acknowledgement bytes are the sentinel error acknowledgement so we can use the shared transfer logic
if bytes.Equal(acknowledgement, channeltypesv2.ErrorAcknowledgement[:]) {
// the specific error does not matter
ack = channeltypes.NewErrorAcknowledgement(types.ErrReceiveFailed)
} else {
if err := types.ModuleCdc.UnmarshalJSON(acknowledgement, &ack); err != nil {
return errorsmod.Wrapf(ibcerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet acknowledgement: %v", err)
}
if !ack.Success() {
return errorsmod.Wrapf(ibcerrors.ErrInvalidRequest, "cannot pass in a custom error acknowledgement with IBC v2")
}
}
data, err := types.UnmarshalPacketData(payload.Value, payload.Version, payload.Encoding)
if err != nil {
return err
}
if err := im.keeper.OnAcknowledgementPacket(ctx, payload.SourcePort, sourceChannel, data, ack); err != nil {
return err
}
// TODO: handle forwarding
return im.keeper.EmitOnAcknowledgementPacketEvent(ctx, data, ack)
}