-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #183 from quasar-finance/feature/ibc-transfer-modu…
…le_refactor IBC transfer module refactor
- Loading branch information
Showing
52 changed files
with
2,770 additions
and
288 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
package decorators | ||
|
||
import ( | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" | ||
ibctransfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" | ||
channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" | ||
porttypes "github.com/cosmos/ibc-go/v3/modules/core/05-port/types" | ||
intergammkeeper "github.com/quasarlabs/quasarnode/x/intergamm/keeper" | ||
intergammtypes "github.com/quasarlabs/quasarnode/x/intergamm/types" | ||
"github.com/tendermint/tendermint/libs/log" | ||
) | ||
|
||
var _ porttypes.IBCModule = IBCTransferIntergammDecorator{} | ||
|
||
// IBCModule implements the ICS26 interface for interchain accounts controller chains | ||
type IBCTransferIntergammDecorator struct { | ||
k *intergammkeeper.Keeper | ||
porttypes.IBCModule | ||
} | ||
|
||
// NewIBCModule creates a new IBCModule given the intergamm keeper | ||
func NewIBCTransferIntergammDecorator(k *intergammkeeper.Keeper, m porttypes.IBCModule) IBCTransferIntergammDecorator { | ||
return IBCTransferIntergammDecorator{ | ||
k: k, | ||
IBCModule: m, | ||
} | ||
} | ||
|
||
// OnChanOpenAck implements the IBCModule.OnChanOpenAck | ||
func (im IBCTransferIntergammDecorator) OnChanOpenAck( | ||
ctx sdk.Context, | ||
portID, | ||
channelID string, | ||
counterpartyChannelID string, | ||
counterpartyVersion string, | ||
) error { | ||
connectionID, _, err := im.k.GetChannelKeeper(ctx).GetChannelConnection(ctx, portID, channelID) | ||
if err != nil { | ||
return err | ||
} | ||
destinationChain, _ := im.k.GetChainID(ctx, connectionID) | ||
|
||
_, found := im.k.GetPortDetail(ctx, destinationChain, portID) | ||
// Don't update the im.k.SetPortDetail. As updating the new channel id will cause denom changes | ||
// to ibc token transfer. This make sure we use constant value of channel id for a given connection id/chain id | ||
if !found { | ||
pi := intergammtypes.PortInfo{ | ||
PortID: portID, | ||
ChannelID: channelID, | ||
CounterpartyChannelID: counterpartyChannelID, | ||
ConnectionID: connectionID, | ||
} | ||
im.k.SetPortDetail(ctx, pi) | ||
im.logger(ctx).Info( | ||
"created new port detail", | ||
"port_detail", pi, | ||
) | ||
} | ||
|
||
return im.IBCModule.OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, counterpartyVersion) | ||
} | ||
|
||
// OnAcknowledgementPacket implements the IBCModule.OnAcknowledgementPacket | ||
func (im IBCTransferIntergammDecorator) OnAcknowledgementPacket( | ||
ctx sdk.Context, | ||
packet channeltypes.Packet, | ||
acknowledgement []byte, | ||
relayer sdk.AccAddress, | ||
) error { | ||
err := im.IBCModule.OnAcknowledgementPacket(ctx, packet, acknowledgement, relayer) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
transferPacket, err := unmarshalTransferPacket(packet) | ||
if err != nil { | ||
return err | ||
} | ||
ack, err := unmarshalAcknowledgement(acknowledgement) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return im.k.HandleIbcTransferAcknowledgement(ctx, packet.GetSequence(), transferPacket, ack) | ||
} | ||
|
||
// OnTimeoutPacket implements the IBCModule interface. | ||
func (im IBCTransferIntergammDecorator) OnTimeoutPacket( | ||
ctx sdk.Context, | ||
packet channeltypes.Packet, | ||
relayer sdk.AccAddress, | ||
) error { | ||
err := im.IBCModule.OnTimeoutPacket(ctx, packet, relayer) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
transferPacket, err := unmarshalTransferPacket(packet) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return im.k.HandleIbcTransferTimeout(ctx, packet.GetSequence(), transferPacket) | ||
} | ||
|
||
func (im IBCTransferIntergammDecorator) logger(ctx sdk.Context) log.Logger { | ||
return ctx.Logger().With("decorator", "IBCTransferIntergammDecorator") | ||
} | ||
|
||
func unmarshalTransferPacket(packet channeltypes.Packet) (ibctransfertypes.FungibleTokenPacketData, error) { | ||
var transferPacket ibctransfertypes.FungibleTokenPacketData | ||
err := intergammtypes.ModuleCdc.UnmarshalJSON(packet.GetData(), &transferPacket) | ||
if err != nil { | ||
return transferPacket, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet data: %s", err.Error()) | ||
} | ||
|
||
return transferPacket, nil | ||
} | ||
|
||
func unmarshalAcknowledgement(acknowledgement []byte) (channeltypes.Acknowledgement, error) { | ||
var ack channeltypes.Acknowledgement | ||
err := intergammtypes.ModuleCdc.UnmarshalJSON(acknowledgement, &ack) | ||
if err != nil { | ||
return ack, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet acknowledgement: %v", err) | ||
} | ||
return ack, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
package decorators | ||
|
||
import ( | ||
"github.com/CosmWasm/wasmd/x/wasm" | ||
wasmvmtypes "github.com/CosmWasm/wasmvm/types" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" | ||
channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" | ||
porttypes "github.com/cosmos/ibc-go/v3/modules/core/05-port/types" | ||
"github.com/tendermint/tendermint/libs/log" | ||
) | ||
|
||
var _ porttypes.IBCModule = IBCTransferWasmDecorator{} | ||
|
||
// IBCTransferWasmDecorator is a decorator for ibc transfer module that will | ||
// pass ack and timeout callbacks of wasm contracts that were the sender of packet to them. | ||
// Note that the contracts should implement the IBC interface to receive the callbacks | ||
// otherwise they won't receive any callbacks from this decorator and will be treated like | ||
// a regular account. | ||
// NOTICE: Potential Security Issue | ||
type IBCTransferWasmDecorator struct { | ||
k *wasm.Keeper | ||
porttypes.IBCModule | ||
} | ||
|
||
// NewIBCTransferWasmDecorator returns a new IBCTransferWasmDecorator with the given wasm keeper and transfer ibc module. | ||
func NewIBCTransferWasmDecorator(k *wasm.Keeper, m porttypes.IBCModule) IBCTransferWasmDecorator { | ||
return IBCTransferWasmDecorator{ | ||
k: k, | ||
IBCModule: m, | ||
} | ||
} | ||
|
||
// OnAcknowledgementPacket implements the IBCModule.OnAcknowledgementPacket | ||
func (im IBCTransferWasmDecorator) OnAcknowledgementPacket( | ||
ctx sdk.Context, | ||
packet channeltypes.Packet, | ||
acknowledgement []byte, | ||
relayer sdk.AccAddress, | ||
) error { | ||
err := im.IBCModule.OnAcknowledgementPacket(ctx, packet, acknowledgement, relayer) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
transferPacket, err := unmarshalTransferPacket(packet) | ||
if err != nil { | ||
return err | ||
} | ||
ack, err := unmarshalAcknowledgement(acknowledgement) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
contractAddr, err := sdk.AccAddressFromBech32(transferPacket.GetSender()) | ||
if err != nil { | ||
return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "failed to decode address from bech32: %v", err) | ||
} | ||
contractInfo := im.k.GetContractInfo(ctx, contractAddr) | ||
// Skip if there's no contract with this address (it's a regular address) or the contract doesn't support IBC | ||
if contractInfo == nil || contractInfo.IBCPortID == "" { | ||
return nil | ||
} | ||
|
||
if !ack.Success() { | ||
im.logger(ctx).Debug( | ||
"passing an error acknowledgment to contract", | ||
"contract_address", contractAddr, | ||
"error", ack.GetError(), | ||
) | ||
} | ||
err = im.k.OnAckPacket(ctx, contractAddr, wasmvmtypes.IBCPacketAckMsg{ | ||
Acknowledgement: wasmvmtypes.IBCAcknowledgement{Data: acknowledgement}, | ||
OriginalPacket: newWasmIBCPacket(packet), | ||
Relayer: relayer.String(), | ||
}) | ||
if err != nil { | ||
im.logger(ctx).Error( | ||
"contract returned error for acknowledgment", | ||
"contract_address", contractAddr, | ||
"error", err, | ||
) | ||
return sdkerrors.Wrap(err, "contract returned error for acknowledgment") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// OnTimeoutPacket implements the IBCModule.OnTimeoutPacket | ||
func (im IBCTransferWasmDecorator) OnTimeoutPacket( | ||
ctx sdk.Context, | ||
packet channeltypes.Packet, | ||
relayer sdk.AccAddress, | ||
) error { | ||
err := im.IBCModule.OnTimeoutPacket(ctx, packet, relayer) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
transferPacket, err := unmarshalTransferPacket(packet) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
contractAddr, err := sdk.AccAddressFromBech32(transferPacket.GetSender()) | ||
if err != nil { | ||
return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "failed to decode address from bech32: %v", err) | ||
} | ||
contractInfo := im.k.GetContractInfo(ctx, contractAddr) | ||
if contractInfo.IBCPortID == "" { | ||
return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "contract %s is not a valid IBC contract", contractAddr) | ||
} | ||
// Skip if there's no contract with this address (it's a regular address) or the contract doesn't support IBC | ||
if contractInfo == nil || contractInfo.IBCPortID == "" { | ||
return nil | ||
} | ||
|
||
err = im.k.OnTimeoutPacket(ctx, contractAddr, wasmvmtypes.IBCPacketTimeoutMsg{ | ||
Packet: newWasmIBCPacket(packet), | ||
Relayer: relayer.String(), | ||
}) | ||
if err != nil { | ||
im.logger(ctx).Error( | ||
"contract returned error for timeout", | ||
"contract_address", contractAddr, | ||
"error", err, | ||
) | ||
return sdkerrors.Wrap(err, "contract returned error for timeout") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func newWasmIBCPacket(packet channeltypes.Packet) wasmvmtypes.IBCPacket { | ||
timeout := wasmvmtypes.IBCTimeout{ | ||
Timestamp: packet.TimeoutTimestamp, | ||
} | ||
if !packet.TimeoutHeight.IsZero() { | ||
timeout.Block = &wasmvmtypes.IBCTimeoutBlock{ | ||
Height: packet.TimeoutHeight.RevisionHeight, | ||
Revision: packet.TimeoutHeight.RevisionNumber, | ||
} | ||
} | ||
|
||
return wasmvmtypes.IBCPacket{ | ||
Data: packet.Data, | ||
Src: wasmvmtypes.IBCEndpoint{ChannelID: packet.SourceChannel, PortID: packet.SourcePort}, | ||
Dest: wasmvmtypes.IBCEndpoint{ChannelID: packet.DestinationChannel, PortID: packet.DestinationPort}, | ||
Sequence: packet.Sequence, | ||
Timeout: timeout, | ||
} | ||
} | ||
|
||
func (im IBCTransferWasmDecorator) logger(ctx sdk.Context) log.Logger { | ||
return ctx.Logger().With("decorator", "IBCTransferWasmCallback") | ||
} |
Oops, something went wrong.