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

feat: attestation bridge call claim #211

Merged
merged 1 commit into from
Feb 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
8 changes: 8 additions & 0 deletions app/keepers/keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ func NewAppKeeper(
appKeepers.IBCTransferKeeper,
appKeepers.Erc20Keeper,
appKeepers.AccountKeeper,
appKeepers.EvmKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)
appKeepers.PolygonKeeper = crosschainkeeper.NewKeeper(
Expand All @@ -359,6 +360,7 @@ func NewAppKeeper(
appKeepers.IBCTransferKeeper,
appKeepers.Erc20Keeper,
appKeepers.AccountKeeper,
appKeepers.EvmKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)
appKeepers.AvalancheKeeper = crosschainkeeper.NewKeeper(
Expand All @@ -372,6 +374,7 @@ func NewAppKeeper(
appKeepers.IBCTransferKeeper,
appKeepers.Erc20Keeper,
appKeepers.AccountKeeper,
appKeepers.EvmKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)
appKeepers.EthKeeper = crosschainkeeper.NewKeeper(
Expand All @@ -385,6 +388,7 @@ func NewAppKeeper(
appKeepers.IBCTransferKeeper,
appKeepers.Erc20Keeper,
appKeepers.AccountKeeper,
appKeepers.EvmKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)
appKeepers.ArbitrumKeeper = crosschainkeeper.NewKeeper(
Expand All @@ -398,6 +402,7 @@ func NewAppKeeper(
appKeepers.IBCTransferKeeper,
appKeepers.Erc20Keeper,
appKeepers.AccountKeeper,
appKeepers.EvmKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)
appKeepers.OptimismKeeper = crosschainkeeper.NewKeeper(
Expand All @@ -411,6 +416,7 @@ func NewAppKeeper(
appKeepers.IBCTransferKeeper,
appKeepers.Erc20Keeper,
appKeepers.AccountKeeper,
appKeepers.EvmKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)
appKeepers.Layer2Keeper = crosschainkeeper.NewKeeper(
Expand All @@ -424,6 +430,7 @@ func NewAppKeeper(
appKeepers.IBCTransferKeeper,
appKeepers.Erc20Keeper,
appKeepers.AccountKeeper,
appKeepers.EvmKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)
appKeepers.TronKeeper = tronkeeper.NewKeeper(crosschainkeeper.NewKeeper(
Expand All @@ -437,6 +444,7 @@ func NewAppKeeper(
appKeepers.IBCTransferKeeper,
appKeepers.Erc20Keeper,
appKeepers.AccountKeeper,
appKeepers.EvmKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
), appKeepers.Erc20Keeper)

Expand Down
16 changes: 16 additions & 0 deletions testutil/helpers/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import (
"github.com/evmos/ethermint/crypto/ethsecp256k1"
hd2 "github.com/evmos/ethermint/crypto/hd"
tronaddress "github.com/fbsobreira/gotron-sdk/pkg/address"

fxtypes "github.com/functionx/fx-core/v7/types"
)

func NewMnemonic() string {
Expand Down Expand Up @@ -103,6 +105,20 @@ func GenerateAddressByModule(module string) string {
return addr.String()
}

func AddressToBytesByModule(addr, module string) ([]byte, error) {
if module == "tron" {
tronAddr, err := tronaddress.Base58ToAddress(addr)
if err != nil {
return nil, err
}
return tronAddr.Bytes(), nil
}
if err := fxtypes.ValidateEthereumAddress(addr); err != nil {
return nil, err
}
return common.HexToAddress(addr).Bytes(), nil
}

// GenerateZeroAddressByModule generates an Ethereum or Tron zero address.
func GenerateZeroAddressByModule(module string) string {
addr := common.HexToAddress(common.Address{}.Hex())
Expand Down
12 changes: 12 additions & 0 deletions x/crosschain/keeper/attestation_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ func (k Keeper) AttestationHandler(ctx sdk.Context, externalClaim types.External
return nil
}

case *types.MsgBridgeCallClaim:
assetType, assetData, err := types.UnpackAssetType(claim.Asset)
if err != nil {
return errorsmod.Wrap(types.ErrInvalid, "asset")
}
switch assetType {
case types.AssetERC20:
return k.bridgeCallERC20Handler(ctx, assetData, claim.MustSenderBytes(), claim.MustToBytes(),
claim.MustReceiverBytes(), claim.DstChainId, claim.Message, claim.Value, claim.GasLimit, claim.EventNonce)
default:
return errorsmod.Wrap(types.ErrInvalid, "asset not support")
}
case *types.MsgSendToExternalClaim:
k.OutgoingTxBatchExecuted(ctx, claim.TokenContract, claim.BatchNonce)

Expand Down
4 changes: 3 additions & 1 deletion x/crosschain/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type Keeper struct {
bankKeeper types.BankKeeper
ibcTransferKeeper types.IBCTransferKeeper
erc20Keeper types.Erc20Keeper
evmKeeper types.EVMKeeper

authority string
}
Expand All @@ -34,7 +35,7 @@ type Keeper struct {
func NewKeeper(cdc codec.BinaryCodec, moduleName string, storeKey storetypes.StoreKey,
stakingKeeper types.StakingKeeper, stakingMsgServer types.StakingMsgServer, distributionKeeper types.DistributionMsgServer,
bankKeeper types.BankKeeper, ibcTransferKeeper types.IBCTransferKeeper, erc20Keeper types.Erc20Keeper, ak types.AccountKeeper,
authority string,
evmKeeper types.EVMKeeper, authority string,
) Keeper {
if addr := ak.GetModuleAddress(moduleName); addr == nil {
panic(fmt.Sprintf("%s module account has not been set", moduleName))
Expand All @@ -51,6 +52,7 @@ func NewKeeper(cdc codec.BinaryCodec, moduleName string, storeKey storetypes.Sto
bankKeeper: bankKeeper,
ibcTransferKeeper: ibcTransferKeeper,
erc20Keeper: erc20Keeper,
evmKeeper: evmKeeper,
authority: authority,
}
}
Expand Down
64 changes: 54 additions & 10 deletions x/crosschain/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import (
"encoding/hex"
"fmt"
"math"
"math/big"
"sort"
"testing"

errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
Expand All @@ -20,6 +22,7 @@ import (

"github.com/functionx/fx-core/v7/testutil/helpers"
"github.com/functionx/fx-core/v7/x/crosschain/types"
erc20types "github.com/functionx/fx-core/v7/x/erc20/types"
ethtypes "github.com/functionx/fx-core/v7/x/eth/types"
trontypes "github.com/functionx/fx-core/v7/x/tron/types"
)
Expand Down Expand Up @@ -1111,10 +1114,50 @@ func (suite *KeeperTestSuite) TestBridgeCallClaim() {
oracleLastEventNonce := suite.Keeper().GetLastEventNonceByOracle(suite.ctx, suite.oracleAddrs[0])
require.EqualValues(suite.T(), 0, oracleLastEventNonce)

sender := helpers.GenerateAddress().String()
if suite.chainName == trontypes.ModuleName {
sender = trontypes.AddressFromHex(sender)
}
tokenContract := helpers.GenerateAddressByModule(suite.chainName)
_, err = suite.MsgServer().BridgeTokenClaim(sdk.WrapSDKContext(suite.ctx), &types.MsgBridgeTokenClaim{
EventNonce: oracleLastEventNonce + 1,
BlockHeight: uint64(suite.ctx.BlockHeight()),
TokenContract: tokenContract,
Name: "Token Test",
Symbol: "TTT",
Decimals: 18,
BridgerAddress: suite.bridgerAddrs[0].String(),
ChannelIbc: "",
ChainName: suite.chainName,
})
require.NoError(suite.T(), err)

oracleLastEventNonce = suite.Keeper().GetLastEventNonceByOracle(suite.ctx, suite.oracleAddrs[0])
require.EqualValues(suite.T(), 1, oracleLastEventNonce)

_, err = suite.app.Erc20Keeper.RegisterCoin(sdk.WrapSDKContext(suite.ctx), &erc20types.MsgRegisterCoin{
Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(),
Metadata: banktypes.Metadata{
Description: "Test token",
DenomUnits: []*banktypes.DenomUnit{
{
Denom: "ttt",
Exponent: 0,
Aliases: []string{fmt.Sprintf("%s%s", suite.chainName, tokenContract)},
},
{
Denom: "TTT",
Exponent: 18,
},
},
Base: "ttt",
Display: "TTT",
Name: "Test Token",
Symbol: "TTT",
},
})
require.NoError(suite.T(), err)

tokenAddrBytes, err := helpers.AddressToBytesByModule(tokenContract, suite.chainName)
require.NoError(suite.T(), err)
assetBytes, err := types.PackERC20Asset([][]byte{tokenAddrBytes}, []*big.Int{big.NewInt(100)})
require.NoError(suite.T(), err)

testMsgs := []struct {
name string
Expand All @@ -1125,14 +1168,15 @@ func (suite *KeeperTestSuite) TestBridgeCallClaim() {
{
name: "success",
msg: &types.MsgBridgeCallClaim{
DstChainId: "123",
DstChainId: "530",
EventNonce: oracleLastEventNonce + 1,
Sender: sender,
Asset: "123",
Receiver: helpers.GenerateAddress().String(),
To: helpers.GenerateAddress().String(),
Message: "123",
Sender: helpers.GenerateAddressByModule(suite.chainName),
Asset: hex.EncodeToString(assetBytes),
Receiver: helpers.GenerateAddressByModule(suite.chainName),
To: helpers.GenerateAddressByModule(suite.chainName),
Message: "",
Value: sdkmath.NewInt(0),
GasLimit: 3000000,
BlockHeight: 1,
BridgerAddress: suite.bridgerAddrs[0].String(),
ChainName: suite.chainName,
Expand Down
103 changes: 103 additions & 0 deletions x/crosschain/keeper/relay_transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ package keeper
import (
"encoding/hex"
"fmt"
"math/big"
"strconv"
"strings"
"time"

errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/bech32"
Expand Down Expand Up @@ -103,3 +106,103 @@ func (k Keeper) transferIBCHandler(ctx sdk.Context, eventNonce uint64, receive s
))
return err
}

func (k Keeper) bridgeCallERC20Handler(
ctx sdk.Context,
asset, sender, to, receiver []byte,
dstChainID, message string,
value sdkmath.Int,
gasLimit, eventNonce uint64,
) error {
tokens, amounts, err := types.UnpackERC20Asset(asset)
if err != nil {
return errorsmod.Wrap(types.ErrInvalid, "asset erc20")
}
senderAddr := common.BytesToAddress(sender)
targetCoins, err := k.bridgeCallTargetCoinsHandler(ctx, senderAddr, tokens, amounts)
if err != nil {
return err
}

switch dstChainID {
case types.FxcoreChainID:
// convert coin to erc20
for _, coin := range targetCoins {
// not convert FX
if coin.Denom == fxtypes.DefaultDenom {
continue
}
if err = k.transferErc20Handler(ctx, eventNonce, receiver, coin); err != nil {
return err
}
}
var toAddrPtr *common.Address
if len(to) > 0 {
toAddr := common.BytesToAddress(to)
toAddrPtr = &toAddr
}
if len(message) > 0 || toAddrPtr != nil {
k.bridgeCallEvmHandler(ctx, senderAddr, toAddrPtr, message, value, gasLimit, eventNonce)
}
default:
// not support chain, refund
}
// todo refund asset

return nil
}

func (k Keeper) bridgeCallTargetCoinsHandler(ctx sdk.Context, receiver common.Address, tokens []common.Address, amounts []*big.Int) (sdk.Coins, error) {
tokens, amounts = types.MergeDuplicationERC20(tokens, amounts)
targetCoins := sdk.NewCoins()
for i := 0; i < len(tokens); i++ {
bridgeToken := k.GetBridgeTokenDenom(ctx, tokens[i].String())
if bridgeToken == nil {
return nil, errorsmod.Wrap(types.ErrInvalid, "bridge token is not exist")
}
coin := sdk.NewCoin(bridgeToken.Denom, sdkmath.NewIntFromBigInt(amounts[i]))
isOriginOrConverted := k.erc20Keeper.IsOriginOrConvertedDenom(ctx, bridgeToken.Denom)
if !isOriginOrConverted {
if err := k.bankKeeper.MintCoins(ctx, k.moduleName, sdk.NewCoins(coin)); err != nil {
return nil, errorsmod.Wrapf(err, "mint vouchers coins")
}
}
if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, k.moduleName, receiver.Bytes(), sdk.NewCoins(coin)); err != nil {
return nil, errorsmod.Wrap(err, "transfer vouchers")
}

targetCoin, err := k.erc20Keeper.ConvertDenomToTarget(ctx, receiver.Bytes(), coin, fxtypes.ParseFxTarget(fxtypes.ERC20Target))
if err != nil {
return nil, errorsmod.Wrap(err, "convert to target coin")
}
targetCoins = targetCoins.Add(targetCoin)
}
return targetCoins, nil
}

func (k Keeper) bridgeCallEvmHandler(
ctx sdk.Context,
sender common.Address,
to *common.Address,
message string, value sdkmath.Int,
gasLimit, eventNonce uint64,
) {
cacheCtx, commit := ctx.CacheContext()
txResp, err := k.evmKeeper.CallEVM(cacheCtx, sender, to, value.BigInt(), gasLimit, fxtypes.MustDecodeHex(message), true)
if err != nil {
k.Logger(ctx).Error("bridge call evm error", "nonce", eventNonce, "error", err.Error())
attrs := []sdk.Attribute{
sdk.NewAttribute(types.AttributeKeyEvmCallResult, strconv.FormatBool(false)),
sdk.NewAttribute(types.AttributeKeyEvmCallError, err.Error()),
}
ctx.EventManager().EmitEvents(sdk.Events{sdk.NewEvent(types.EventTypeBridgeCallEvm, attrs...)})
return
}
// whether the tx succeeds or fails, commit with tx logs
commit()
attrs := []sdk.Attribute{sdk.NewAttribute(types.AttributeKeyEvmCallResult, strconv.FormatBool(!txResp.Failed()))}
if txResp.Failed() {
attrs = append(attrs, sdk.NewAttribute(types.AttributeKeyEvmCallError, txResp.VmError))
}
ctx.EventManager().EmitEvents(sdk.Events{sdk.NewEvent(types.EventTypeBridgeCallEvm, attrs...)})
}
Loading
Loading