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

refactor: attestation handler #553

Merged
merged 1 commit into from
Jun 7, 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
93 changes: 5 additions & 88 deletions x/crosschain/keeper/attestation_handler.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package keeper

import (
"fmt"

errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"

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

Expand All @@ -16,99 +13,19 @@ import (
func (k Keeper) AttestationHandler(ctx sdk.Context, externalClaim types.ExternalClaim) error {
switch claim := externalClaim.(type) {
case *types.MsgSendToFxClaim:
bridgeToken := k.GetBridgeTokenDenom(ctx, claim.TokenContract)
if bridgeToken == nil {
return errorsmod.Wrap(types.ErrInvalid, "bridge token is not exist")
}

coin := sdk.NewCoin(bridgeToken.Denom, claim.Amount)
receiveAddr, err := sdk.AccAddressFromBech32(claim.Receiver)
if err != nil {
return errorsmod.Wrap(types.ErrInvalid, "receiver address")
}
isOriginOrConverted := k.erc20Keeper.IsOriginOrConvertedDenom(ctx, bridgeToken.Denom)
if !isOriginOrConverted {
// If it is not fxcore originated, mint the coins (aka vouchers)
if err := k.bankKeeper.MintCoins(ctx, k.moduleName, sdk.NewCoins(coin)); err != nil {
return errorsmod.Wrapf(err, "mint vouchers coins")
}
}
if err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, k.moduleName, receiveAddr, sdk.NewCoins(coin)); err != nil {
return errorsmod.Wrap(err, "transfer vouchers")
}

// convert to base denom
cacheCtx, commit := ctx.CacheContext()
targetCoin, err := k.erc20Keeper.ConvertDenomToTarget(cacheCtx, receiveAddr, coin, fxtypes.ParseFxTarget(fxtypes.ERC20Target))
if err != nil {
k.Logger(ctx).Info("failed to convert base denom", "error", err)
return nil
}
commit()

// relay transfer
if err = k.RelayTransferHandler(ctx, claim.EventNonce, claim.TargetIbc, receiveAddr, targetCoin); err != nil {
k.Logger(ctx).Info("failed to relay transfer", "error", err)
return nil
}

k.HandlePendingOutgoingTx(ctx, receiveAddr, externalClaim.GetEventNonce(), bridgeToken)

case *types.MsgBridgeCallClaim:
return k.BridgeCallHandler(ctx, claim)
return k.SendToFxExecuted(ctx, claim)

case *types.MsgSendToExternalClaim:
k.OutgoingTxBatchExecuted(ctx, claim.TokenContract, claim.BatchNonce)

case *types.MsgBridgeTokenClaim:
// Check if it already exists
if has := k.HasBridgeToken(ctx, claim.TokenContract); has {
return types.ErrInvalid.Wrap("bridge token is exist")
}

k.Logger(ctx).Info("add bridge token claim", "symbol", claim.Symbol, "token",
claim.TokenContract, "channelIbc", claim.ChannelIbc)
if claim.Symbol == fxtypes.DefaultDenom {
// Check if denom exists
if !k.bankKeeper.HasDenomMetaData(ctx, claim.Symbol) {
return types.ErrUnknown.Wrapf("denom not found %s", claim.Symbol)
}

if uint64(fxtypes.DenomUnit) != claim.Decimals {
return types.ErrInvalid.Wrapf("%s denom decimals not match %d, expect %d", fxtypes.DefaultDenom,
claim.Decimals, fxtypes.DenomUnit)
}

k.AddBridgeToken(ctx, claim.TokenContract, fxtypes.DefaultDenom)
return nil
}

denom, err := k.SetIbcDenomTrace(ctx, claim.TokenContract, claim.ChannelIbc)
if err != nil {
return err
}
k.AddBridgeToken(ctx, claim.TokenContract, denom)
return k.AddBridgeTokenExecuted(ctx, claim)

case *types.MsgOracleSetUpdatedClaim:
observedOracleSet := &types.OracleSet{
Nonce: claim.OracleSetNonce,
Members: claim.Members,
}
// check the contents of the validator set against the store
if claim.OracleSetNonce != 0 {
trustedOracleSet := k.GetOracleSet(ctx, claim.OracleSetNonce)
if trustedOracleSet == nil {
ctx.Logger().Error("Received attestation for a oracle set which does not exist in store", "oracleSetNonce", claim.OracleSetNonce, "claim", claim)
return errorsmod.Wrapf(types.ErrInvalid, "attested oracleSet (%v) does not exist in store", claim.OracleSetNonce)
}
// overwrite the height, since it's not part of the claim
observedOracleSet.Height = trustedOracleSet.Height
return k.UpdateOracleSetExecuted(ctx, claim)

if _, err := trustedOracleSet.Equal(observedOracleSet); err != nil {
panic(fmt.Sprintf("Potential bridge highjacking: observed oracleSet (%+v) does not match stored oracleSet (%+v)! %s", observedOracleSet, trustedOracleSet, err.Error()))
}
}
k.SetLastObservedOracleSet(ctx, observedOracleSet)
case *types.MsgBridgeCallClaim:
return k.BridgeCallHandler(ctx, claim)

case *types.MsgBridgeCallResultClaim:
k.BridgeCallResultHandler(ctx, claim)
Expand Down
31 changes: 31 additions & 0 deletions x/crosschain/keeper/bridge_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,37 @@ import (
"github.com/functionx/fx-core/v7/x/crosschain/types"
)

func (k Keeper) AddBridgeTokenExecuted(ctx sdk.Context, claim *types.MsgBridgeTokenClaim) error {
// Check if it already exists
if has := k.HasBridgeToken(ctx, claim.TokenContract); has {
return types.ErrInvalid.Wrap("bridge token is exist")
}

k.Logger(ctx).Info("add bridge token claim", "symbol", claim.Symbol, "token",
claim.TokenContract, "channelIbc", claim.ChannelIbc)
if claim.Symbol == fxtypes.DefaultDenom {
// Check if denom exists
if !k.bankKeeper.HasDenomMetaData(ctx, claim.Symbol) {
return types.ErrUnknown.Wrapf("denom not found %s", claim.Symbol)
}

if uint64(fxtypes.DenomUnit) != claim.Decimals {
return types.ErrInvalid.Wrapf("%s denom decimals not match %d, expect %d", fxtypes.DefaultDenom,
claim.Decimals, fxtypes.DenomUnit)
}

k.AddBridgeToken(ctx, claim.TokenContract, fxtypes.DefaultDenom)
return nil
}

denom, err := k.SetIbcDenomTrace(ctx, claim.TokenContract, claim.ChannelIbc)
if err != nil {
return err
}
k.AddBridgeToken(ctx, claim.TokenContract, denom)
return nil
}

func (k Keeper) GetBridgeTokenDenom(ctx sdk.Context, tokenContract string) *types.BridgeToken {
store := ctx.KVStore(k.storeKey)
data := store.Get(types.GetDenomToTokenKey(tokenContract))
Expand Down
24 changes: 24 additions & 0 deletions x/crosschain/keeper/oracle_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,36 @@ import (
"fmt"
"math"

errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"

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

func (k Keeper) UpdateOracleSetExecuted(ctx sdk.Context, claim *types.MsgOracleSetUpdatedClaim) error {
observedOracleSet := &types.OracleSet{
Nonce: claim.OracleSetNonce,
Members: claim.Members,
}
// check the contents of the validator set against the store
if claim.OracleSetNonce != 0 {
trustedOracleSet := k.GetOracleSet(ctx, claim.OracleSetNonce)
if trustedOracleSet == nil {
k.Logger(ctx).Error("Received attestation for a oracle set which does not exist in store", "oracleSetNonce", claim.OracleSetNonce, "claim", claim)
return errorsmod.Wrapf(types.ErrInvalid, "attested oracleSet (%v) does not exist in store", claim.OracleSetNonce)
}
// overwrite the height, since it's not part of the claim
observedOracleSet.Height = trustedOracleSet.Height

if _, err := trustedOracleSet.Equal(observedOracleSet); err != nil {
panic(fmt.Sprintf("Potential bridge highjacking: observed oracleSet (%+v) does not match stored oracleSet (%+v)! %s", observedOracleSet, trustedOracleSet, err.Error()))
}
}
k.SetLastObservedOracleSet(ctx, observedOracleSet)
return nil
}
zakir-code marked this conversation as resolved.
Show resolved Hide resolved

// --- ORACLE SET REQUESTS --- //

// GetCurrentOracleSet gets powers from the store and normalizes them
Expand Down
50 changes: 50 additions & 0 deletions x/crosschain/keeper/send_to_fx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package keeper

import (
errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"

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

func (k Keeper) SendToFxExecuted(ctx sdk.Context, claim *types.MsgSendToFxClaim) error {
bridgeToken := k.GetBridgeTokenDenom(ctx, claim.TokenContract)
if bridgeToken == nil {
return errorsmod.Wrap(types.ErrInvalid, "bridge token is not exist")
}

coin := sdk.NewCoin(bridgeToken.Denom, claim.Amount)
receiveAddr, err := sdk.AccAddressFromBech32(claim.Receiver)
if err != nil {
return errorsmod.Wrap(types.ErrInvalid, "receiver address")
}
isOriginOrConverted := k.erc20Keeper.IsOriginOrConvertedDenom(ctx, bridgeToken.Denom)
if !isOriginOrConverted {
// If it is not fxcore originated, mint the coins (aka vouchers)
if err = k.bankKeeper.MintCoins(ctx, k.moduleName, sdk.NewCoins(coin)); err != nil {
return errorsmod.Wrapf(err, "mint vouchers coins")
}
}
if err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, k.moduleName, receiveAddr, sdk.NewCoins(coin)); err != nil {
return errorsmod.Wrap(err, "transfer vouchers")
}

// convert to base denom
cacheCtx, commit := ctx.CacheContext()
targetCoin, err := k.erc20Keeper.ConvertDenomToTarget(cacheCtx, receiveAddr, coin, fxtypes.ParseFxTarget(fxtypes.ERC20Target))
if err != nil {
k.Logger(ctx).Info("failed to convert base denom", "error", err)
return nil
}
commit()

// relay transfer
if err = k.RelayTransferHandler(ctx, claim.EventNonce, claim.TargetIbc, receiveAddr, targetCoin); err != nil {
k.Logger(ctx).Info("failed to relay transfer", "error", err)
return nil
}

k.HandlePendingOutgoingTx(ctx, receiveAddr, claim.GetEventNonce(), bridgeToken)
return nil
}
zakir-code marked this conversation as resolved.
Show resolved Hide resolved