From 71438ffac0042c6966fcba8be5ffbc5f0a360a3d Mon Sep 17 00:00:00 2001 From: lumtis Date: Mon, 2 Oct 2023 17:55:29 -0700 Subject: [PATCH] add contract verification --- x/crosschain/keeper/evm_deposit.go | 39 +++++++++++++++++--------- x/crosschain/types/expected_keepers.go | 4 +-- x/fungible/keeper/deposits.go | 37 ++++++++++++------------ 3 files changed, 46 insertions(+), 34 deletions(-) diff --git a/x/crosschain/keeper/evm_deposit.go b/x/crosschain/keeper/evm_deposit.go index ee6620c950..09fcd1efc0 100644 --- a/x/crosschain/keeper/evm_deposit.go +++ b/x/crosschain/keeper/evm_deposit.go @@ -36,16 +36,29 @@ func (k msgServer) HandleEVMDeposit(ctx sdk.Context, cctx *types.CrossChainTx, m } } else { // cointype is Gas or ERC20; then it could be a ZRC20 deposit/depositAndCall cctx. - contract, data, err := parseContractAndData(msg.Message, msg.Asset) + parsedAddress, data, err := parseAddressAndData(msg.Message, msg.Asset) if err != nil { return false, errors.Wrap(types.ErrUnableToParseContract, err.Error()) } + if parsedAddress != (ethcommon.Address{}) { + to = parsedAddress + } + from, err := senderChain.DecodeAddress(msg.Sender) if err != nil { return false, fmt.Errorf("HandleEVMDeposit: unable to decode address: %s", err.Error()) } - evmTxResponse, err := k.fungibleKeeper.ZRC20DepositAndCallContract(ctx, from, to, msg.Amount.BigInt(), senderChain, msg.Message, contract, data, msg.CoinType, msg.Asset) + evmTxResponse, contractCall, err := k.fungibleKeeper.ZRC20DepositAndCallContract( + ctx, + from, + to, + msg.Amount.BigInt(), + senderChain, + data, + msg.CoinType, + msg.Asset, + ) if err != nil { isContractReverted := false if evmTxResponse != nil && evmTxResponse.Failed() { @@ -56,7 +69,7 @@ func (k msgServer) HandleEVMDeposit(ctx sdk.Context, cctx *types.CrossChainTx, m // non-empty msg.Message means this is a contract call; therefore the logs should be processed. // a withdrawal event in the logs could generate cctxs for outbound transactions. - if !evmTxResponse.Failed() && len(msg.Message) > 0 { + if !evmTxResponse.Failed() && contractCall { logs := evmtypes.LogsToEthereum(evmTxResponse.Logs) ctx = ctx.WithValue("inCctxIndex", cctx.Index) txOrigin := msg.TxOrigin @@ -64,7 +77,7 @@ func (k msgServer) HandleEVMDeposit(ctx sdk.Context, cctx *types.CrossChainTx, m txOrigin = msg.Sender } - err = k.ProcessLogs(ctx, logs, contract, txOrigin) + err = k.ProcessLogs(ctx, logs, to, txOrigin) if err != nil { // ProcessLogs should not error; error indicates exception, should abort return false, errors.Wrap(types.ErrCannotProcessWithdrawal, err.Error()) @@ -73,7 +86,7 @@ func (k msgServer) HandleEVMDeposit(ctx sdk.Context, cctx *types.CrossChainTx, m sdk.NewEvent(sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), sdk.NewAttribute("action", "DepositZRC20AndCallContract"), - sdk.NewAttribute("contract", contract.String()), + sdk.NewAttribute("contract", to.String()), sdk.NewAttribute("data", hex.EncodeToString(data)), sdk.NewAttribute("cctxIndex", cctx.Index), ), @@ -83,26 +96,26 @@ func (k msgServer) HandleEVMDeposit(ctx sdk.Context, cctx *types.CrossChainTx, m return false, nil } +// parseAddressAndData parses the message string into an address and data // message is hex encoded byte array // [ contractAddress calldata ] // [ 20B, variable] -func parseContractAndData(message string, asset string) (contractAddress ethcommon.Address, data []byte, err error) { +func parseAddressAndData(message string, asset string) (address ethcommon.Address, data []byte, err error) { if len(message) == 0 { - return contractAddress, nil, nil + return ethcommon.Address{}, nil, nil } data, err = hex.DecodeString(message) if err != nil { - return contractAddress, nil, err + return ethcommon.Address{}, nil, err } if len(data) < 20 { if len(asset) != 42 || asset[:2] != "0x" { - err = fmt.Errorf("invalid message length") - return contractAddress, nil, err + return ethcommon.Address{}, nil, fmt.Errorf("invalid message length") } - contractAddress = ethcommon.HexToAddress(asset) + address = ethcommon.HexToAddress(asset) } else { - contractAddress = ethcommon.BytesToAddress(data[:20]) + address = ethcommon.BytesToAddress(data[:20]) data = data[20:] } - return contractAddress, data, nil + return address, data, nil } diff --git a/x/crosschain/types/expected_keepers.go b/x/crosschain/types/expected_keepers.go index 0ddb010735..fc5960b5e5 100644 --- a/x/crosschain/types/expected_keepers.go +++ b/x/crosschain/types/expected_keepers.go @@ -97,12 +97,10 @@ type FungibleKeeper interface { to eth.Address, amount *big.Int, senderChain *common.Chain, - message string, - contract eth.Address, data []byte, coinType common.CoinType, asset string, - ) (*evmtypes.MsgEthereumTxResponse, error) + ) (*evmtypes.MsgEthereumTxResponse, bool, error) CallUniswapV2RouterSwapExactTokensForTokens( ctx sdk.Context, sender eth.Address, diff --git a/x/fungible/keeper/deposits.go b/x/fungible/keeper/deposits.go index 8206f744bc..2bd263a37f 100644 --- a/x/fungible/keeper/deposits.go +++ b/x/fungible/keeper/deposits.go @@ -23,39 +23,40 @@ func (k Keeper) ZRC20DepositAndCallContract( to eth.Address, amount *big.Int, senderChain *common.Chain, - message string, - contract eth.Address, data []byte, coinType common.CoinType, asset string, -) (*evmtypes.MsgEthereumTxResponse, error) { +) (*evmtypes.MsgEthereumTxResponse, bool, error) { var Zrc20Contract eth.Address var coin fungibletypes.ForeignCoins var found bool if coinType == common.CoinType_Gas { coin, found = k.GetGasCoinForForeignCoin(ctx, senderChain.ChainId) if !found { - return nil, types.ErrGasCoinNotFound + return nil, false, types.ErrGasCoinNotFound } } else { coin, found = k.GetForeignCoinFromAsset(ctx, asset, senderChain.ChainId) if !found { - return nil, types.ErrForeignCoinNotFound + return nil, false, types.ErrForeignCoinNotFound } } Zrc20Contract = eth.HexToAddress(coin.Zrc20ContractAddress) - if len(message) == 0 { // no message; transfer - return k.DepositZRC20(ctx, Zrc20Contract, to, amount) - } - // non-empty message = [contractaddress, calldata] - if len(data) == 0 { - return k.DepositZRC20(ctx, Zrc20Contract, contract, amount) - } - context := systemcontract.ZContext{ - Origin: from, - Sender: eth.Address{}, - ChainID: big.NewInt(senderChain.ChainId), - } - return k.DepositZRC20AndCallContract(ctx, context, Zrc20Contract, contract, amount, data) + // check if the receiver is a contract + // if it is, then the hook onCrossChainCall() will be called + // if not, the zrc20 are simply transferred to the receiver + acc := k.evmKeeper.GetAccount(ctx, to) + if acc != nil && acc.IsContract() { + context := systemcontract.ZContext{ + Origin: from, + Sender: eth.Address{}, + ChainID: big.NewInt(senderChain.ChainId), + } + res, err := k.DepositZRC20AndCallContract(ctx, context, Zrc20Contract, to, amount, data) + return res, true, err + } else { + res, err := k.DepositZRC20(ctx, Zrc20Contract, to, amount) + return res, false, err + } }