Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem: not possible to cancel SendToEthereum transactions (fixes #389) #532

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,8 @@ func New(

app.EvmKeeper.SetHooks(cronoskeeper.NewLogProcessEvmHook(
cronoskeeper.NewSendToAccountHandler(app.BankKeeper, app.CronosKeeper),
cronoskeeper.NewSendToEthereumHandler(gravitySrv, app.CronosKeeper),
cronoskeeper.NewSendToEthereumHandler(gravitySrv, app.BankKeeper, app.CronosKeeper),
cronoskeeper.NewCancelSendToEthereumHandler(gravitySrv, app.CronosKeeper, app.GravityKeeper),
cronoskeeper.NewSendToIbcHandler(app.BankKeeper, app.CronosKeeper),
cronoskeeper.NewSendCroToIbcHandler(app.BankKeeper, app.CronosKeeper),
))
Expand Down
2 changes: 1 addition & 1 deletion x/cronos/keeper/evm_hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (h LogProcessEvmHook) PostTxProcessing(ctx sdk.Context, msg core.Message, r
if !ok {
continue
}
err := handler.Handle(ctx, log.Address, log.Data, addLogToReceiptFunc)
err := handler.Handle(ctx, msg.From(), log.Address, log.Data, addLogToReceiptFunc)
if err != nil {
return err
}
Expand Down
135 changes: 130 additions & 5 deletions x/cronos/keeper/evm_log_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
var (
_ types.EvmLogHandler = SendToAccountHandler{}
_ types.EvmLogHandler = SendToEthereumHandler{}
_ types.EvmLogHandler = CancelSendToEthereumHandler{}
_ types.EvmLogHandler = SendToIbcHandler{}
_ types.EvmLogHandler = SendCroToIbcHandler{}
)
Expand All @@ -23,6 +24,7 @@ const (
SendToAccountEventName = "__CronosSendToAccount"
SendToEthereumEventName = "__CronosSendToEthereum"
SendToEthereumResponseEventName = "__CronosSendToEthereumResponse"
CancelSendToEthereumEventName = "__CronosCancelSendToEthereum"
SendToIbcEventName = "__CronosSendToIbc"
SendCroToIbcEventName = "__CronosSendCroToIbc"
)
Expand All @@ -40,6 +42,10 @@ var (
// `event __CronosSendToEthereumResponse(uint256 id)`
SendToEthereumResponseEvent abi.Event

// CancelSendToEthereumEvent represent the signature of
// `event __CronosCancelSendToEthereum(uint256 id)`
CancelSendToEthereumEvent abi.Event

// SendToIbcEvent represent the signature of
// `event __CronosSendToIbc(string recipient, uint256 amount)`
SendToIbcEvent abi.Event
Expand Down Expand Up @@ -95,6 +101,16 @@ func init() {
Indexed: false,
}},
)
CancelSendToEthereumEvent = abi.NewEvent(
CancelSendToEthereumEventName,
CancelSendToEthereumEventName,
false,
abi.Arguments{abi.Argument{
Name: "id",
Type: uint256Type,
Indexed: false,
}},
)
SendToIbcEvent = abi.NewEvent(
SendToIbcEventName,
SendToIbcEventName,
Expand Down Expand Up @@ -150,7 +166,12 @@ func (h SendToAccountHandler) EventID() common.Hash {
return SendToAccountEvent.ID
}

func (h SendToAccountHandler) Handle(ctx sdk.Context, contract common.Address, data []byte, _ func(contractAddress common.Address, logSig common.Hash, logData []byte)) error {
func (h SendToAccountHandler) Handle(
ctx sdk.Context,
_ common.Address,
contract common.Address,
data []byte,
_ func(contractAddress common.Address, logSig common.Hash, logData []byte)) error {
unpacked, err := SendToAccountEvent.Inputs.Unpack(data)
if err != nil {
// log and ignore
Expand All @@ -177,12 +198,14 @@ func (h SendToAccountHandler) Handle(ctx sdk.Context, contract common.Address, d
// SendToEthereumHandler handles `__CronosSendToEthereum` log
type SendToEthereumHandler struct {
gravitySrv gravitytypes.MsgServer
bankKeeper types.BankKeeper
cronosKeeper Keeper
}

func NewSendToEthereumHandler(gravitySrv gravitytypes.MsgServer, cronosKeeper Keeper) *SendToEthereumHandler {
func NewSendToEthereumHandler(gravitySrv gravitytypes.MsgServer, bankKeeper types.BankKeeper, cronosKeeper Keeper) *SendToEthereumHandler {
return &SendToEthereumHandler{
gravitySrv: gravitySrv,
bankKeeper: bankKeeper,
cronosKeeper: cronosKeeper,
}
}
Expand All @@ -194,6 +217,7 @@ func (h SendToEthereumHandler) EventID() common.Hash {
// Handle `__CronosSendToEthereum` log only if gravity is activated.
func (h SendToEthereumHandler) Handle(
ctx sdk.Context,
sender common.Address,
contract common.Address,
data []byte,
addLogToReceipt func(contractAddress common.Address, logSig common.Hash, logData []byte)) error {
Expand Down Expand Up @@ -221,8 +245,16 @@ func (h SendToEthereumHandler) Handle(
ethRecipient := unpacked[0].(common.Address)
amount := sdk.NewIntFromBigInt(unpacked[1].(*big.Int))
bridgeFee := sdk.NewIntFromBigInt(unpacked[2].(*big.Int))

coins := sdk.NewCoins(sdk.NewCoin(denom, amount))
// First, transfer the coin to user so that he will be able to cancel later on
if err = h.bankKeeper.SendCoins(ctx, contractAddr, sender.Bytes(), coins); err != nil {
return err
}

// Initialize a gravity transfer
msg := gravitytypes.MsgSendToEthereum{
Sender: contractAddr.String(),
Sender: sender.String(),
EthereumRecipient: ethRecipient.Hex(),
Amount: sdk.NewCoin(denom, amount),
BridgeFee: sdk.NewCoin(denom, bridgeFee),
Expand All @@ -237,6 +269,89 @@ func (h SendToEthereumHandler) Handle(
return nil
}

// CancelSendToEthereumHandler handles `__CronosCancelSendToEthereum` log
type CancelSendToEthereumHandler struct {
gravitySrv gravitytypes.MsgServer
cronosKeeper Keeper
bankKeeper types.BankKeeper
gravityKeeper types.GravityKeeper
}

func NewCancelSendToEthereumHandler(
gravitySrv gravitytypes.MsgServer,
cronosKeeper Keeper,
gravityKeeper types.GravityKeeper) *CancelSendToEthereumHandler {
return &CancelSendToEthereumHandler{
gravitySrv: gravitySrv,
cronosKeeper: cronosKeeper,
gravityKeeper: gravityKeeper,
}
}

func (h CancelSendToEthereumHandler) EventID() common.Hash {
return CancelSendToEthereumEvent.ID
}

// Handle `__CronosCancelSendToEthereum` log only if gravity is activated.
func (h CancelSendToEthereumHandler) Handle(
ctx sdk.Context,
sender common.Address,
contract common.Address,
data []byte,
_ func(contractAddress common.Address, logSig common.Hash, logData []byte)) error {
if h.gravitySrv == nil {
return fmt.Errorf("native action %s is not implemented", SendToEthereumEventName)
}

unpacked, err := CancelSendToEthereumEvent.Inputs.Unpack(data)
if err != nil {
// log and ignore
h.cronosKeeper.Logger(ctx).Info("log signature matches but failed to decode")
return nil
}

denom, found := h.cronosKeeper.GetDenomByContract(ctx, contract)
if !found {
return fmt.Errorf("contract %s is not connected to native token", contract)
}

if !types.IsValidGravityDenom(denom) {
return fmt.Errorf("the native token associated with the contract %s is not a gravity voucher", contract)
}

id := sdk.NewIntFromBigInt(unpacked[0].(*big.Int))

// Need to retrieve the batch to get the amount to refund
var unbatched []*gravitytypes.SendToEthereum
h.gravityKeeper.IterateUnbatchedSendToEthereums(ctx, func(ste *gravitytypes.SendToEthereum) bool {
unbatched = append(unbatched, ste)
return false
})

var send *gravitytypes.SendToEthereum
for _, ste := range unbatched {
if ste.Id == id.Uint64() {
send = ste
}
}
if send == nil {
return fmt.Errorf("id not found or the transaction is already included in a batch")
}

msg := gravitytypes.MsgCancelSendToEthereum{
Sender: sender.String(),
Id: id.Uint64(),
}
_, err = h.gravitySrv.CancelSendToEthereum(sdk.WrapSDKContext(ctx), &msg)
if err != nil {
return err
}
refundAmount := sdk.NewCoins(sdk.NewCoin(denom, send.Erc20Token.Amount.Add(send.Erc20Fee.Amount)))
// If cancel has no error, we need to convert back the native token to evm tokens
h.cronosKeeper.ConvertVouchersToEvmCoins(ctx, sender.String(), refundAmount)
return nil
}

// SendToIbcHandler handles `__CronosSendToIbc` log
type SendToIbcHandler struct {
bankKeeper types.BankKeeper
Expand All @@ -254,7 +369,12 @@ func (h SendToIbcHandler) EventID() common.Hash {
return SendToIbcEvent.ID
}

func (h SendToIbcHandler) Handle(ctx sdk.Context, contract common.Address, data []byte, _ func(contractAddress common.Address, logSig common.Hash, logData []byte)) error {
func (h SendToIbcHandler) Handle(
ctx sdk.Context,
_ common.Address,
contract common.Address,
data []byte,
_ func(contractAddress common.Address, logSig common.Hash, logData []byte)) error {
unpacked, err := SendToIbcEvent.Inputs.Unpack(data)
if err != nil {
// log and ignore
Expand Down Expand Up @@ -305,7 +425,12 @@ func (h SendCroToIbcHandler) EventID() common.Hash {
return SendCroToIbcEvent.ID
}

func (h SendCroToIbcHandler) Handle(ctx sdk.Context, contract common.Address, data []byte, _ func(contractAddress common.Address, logSig common.Hash, logData []byte)) error {
func (h SendCroToIbcHandler) Handle(
ctx sdk.Context,
_ common.Address,
contract common.Address,
data []byte,
_ func(contractAddress common.Address, logSig common.Hash, logData []byte)) error {
unpacked, err := SendCroToIbcEvent.Inputs.Unpack(data)
if err != nil {
// log and ignore
Expand Down
4 changes: 3 additions & 1 deletion x/cronos/types/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type AccountKeeper interface {
// GravityKeeper defines the expected gravity keeper interface
type GravityKeeper interface {
ERC20ToDenomLookup(ctx sdk.Context, tokenContract common.Address) (bool, string)
IterateUnbatchedSendToEthereums(ctx sdk.Context, cb func(*gravitytypes.SendToEthereum) bool)
GetParams(ctx sdk.Context) (params gravitytypes.Params)
}

Expand All @@ -60,7 +61,8 @@ type EvmLogHandler interface {
// Return the id of the log signature it handles
EventID() common.Hash
// Process the log
Handle(ctx sdk.Context, contract common.Address, data []byte, addLogToReceipt func(contractAddress common.Address, logSig common.Hash, logData []byte)) error
Handle(ctx sdk.Context, sender common.Address, contract common.Address, data []byte,
thomas-nguy marked this conversation as resolved.
Show resolved Hide resolved
addLogToReceipt func(contractAddress common.Address, logSig common.Hash, logData []byte)) error
}

// EvmKeeper defines the interface for evm keeper
Expand Down