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: evm signer - tryprocessout #1809

Merged
merged 29 commits into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b2bd171
initial commit
kevinssgh Feb 23, 2024
c1aa5f7
fix lint errors
kevinssgh Feb 23, 2024
da6a663
Merge branch 'develop' into refactor-tryprocessout
kevinssgh Feb 26, 2024
2bcb9ba
add changelog entry
kevinssgh Feb 26, 2024
11ef7c5
fix e2e test case
kevinssgh Feb 26, 2024
92f5e8f
merge develop
kevinssgh Feb 26, 2024
2a440b8
merge develop
kevinssgh Feb 26, 2024
32e218d
update mocks and test files
kevinssgh Feb 28, 2024
53e87b9
fix tryprocessout test case
kevinssgh Feb 29, 2024
95b7f2f
complete evm_signer test cases and remove hardcoded private key
kevinssgh Feb 29, 2024
9715ef2
make generate and lint
kevinssgh Feb 29, 2024
1fe018f
Merge branch 'develop' into refactor-tryprocessout
kevinssgh Feb 29, 2024
6cfd6e9
print error in mock tss signer
kevinssgh Feb 29, 2024
3b8ce2a
integrate develop changes and fix test cases
kevinssgh Feb 29, 2024
68fff48
ran go imports
kevinssgh Feb 29, 2024
aca80ad
addressed comments
kevinssgh Mar 1, 2024
fd2e812
fix e2e test
kevinssgh Mar 1, 2024
51bfa74
addressed more comments
kevinssgh Mar 4, 2024
e69928e
Merge branch 'develop' into refactor-tryprocessout
kevinssgh Mar 4, 2024
8ab5ef0
fix unit tests
kevinssgh Mar 4, 2024
2401f3d
address comments
kevinssgh Mar 5, 2024
4289771
Merge branch 'develop' into refactor-tryprocessout
kevinssgh Mar 5, 2024
f8f83c5
renamed file
kevinssgh Mar 5, 2024
d1f1430
added test cases and converted SetTransactionData into constructor
kevinssgh Mar 6, 2024
908bc52
update comments
kevinssgh Mar 6, 2024
8b07017
Merge branch 'develop' into refactor-tryprocessout
kevinssgh Mar 6, 2024
a05f46d
add unit test
kevinssgh Mar 6, 2024
322ac2d
add more unit tests
kevinssgh Mar 6, 2024
b2fe4d3
Merge branch 'develop' into refactor-tryprocessout
kevinssgh Mar 6, 2024
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
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
* [1766](https://github.com/zeta-chain/node/pull/1766) - Refactors the `PostTxProcessing` EVM hook functionality to deal with invalid withdraw events
* [1630](https://github.com/zeta-chain/node/pull/1630) - added password prompts for hotkey and tss keyshare in zetaclient
* [1760](https://github.com/zeta-chain/node/pull/1760) - Make staking keeper private in crosschain module
* [1809](https://github.com/zeta-chain/node/pull/1809) - Refactored tryprocessout function in evm signer

### Fixes

Expand Down
5 changes: 3 additions & 2 deletions zetaclient/bitcoin/bitcoin_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ import (
crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types"
observertypes "github.com/zeta-chain/zetacore/x/observer/types"
"github.com/zeta-chain/zetacore/zetaclient/testutils"
"github.com/zeta-chain/zetacore/zetaclient/testutils/stub"
)

func MockBTCClientMainnet() *BTCChainClient {
return &BTCChainClient{
chain: common.BtcMainnetChain(),
zetaClient: testutils.MockCoreBridge(),
Tss: testutils.NewMockTSSMainnet(),
zetaClient: stub.NewZetaCoreBridge(),
Tss: stub.NewTSSMainnet(),
}
}

Expand Down
3 changes: 2 additions & 1 deletion zetaclient/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ type Config struct {

func NewConfig() *Config {
return &Config{
cfgLock: &sync.RWMutex{},
cfgLock: &sync.RWMutex{},
EVMChainConfigs: make(map[int64]*EVMConfig),
}
}

Expand Down
179 changes: 179 additions & 0 deletions zetaclient/evm/base_transaction_data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
package evm
kevinssgh marked this conversation as resolved.
Show resolved Hide resolved

import (
"context"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"math/big"

ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/rs/zerolog"
"github.com/zeta-chain/zetacore/common"
"github.com/zeta-chain/zetacore/x/crosschain/types"
"github.com/zeta-chain/zetacore/zetaclient/interfaces"
)

const (
MinGasLimit = 100_000
MaxGasLimit = 1_000_000
)

// BaseTransactionData is a data structure containing input fields used to construct each type of transaction.
// This is populated using cctx and other input parameters passed to TryProcessOutTx
type BaseTransactionData struct {
kevinssgh marked this conversation as resolved.
Show resolved Hide resolved
srcChainID *big.Int
toChainID *big.Int
sender ethcommon.Address
to ethcommon.Address
asset ethcommon.Address
amount *big.Int
gasPrice *big.Int
gasLimit uint64
message []byte
nonce uint64
height uint64

// sendHash field is the inbound message digest that is sent to the destination contract
sendHash [32]byte

// outboundParams field contains data detailing the receiver chain and outbound transaction
outboundParams *types.OutboundTxParams
}

// SetChainAndSender populates the destination address and Chain ID based on the status of the cross chain tx
// returns true if transaction should be skipped
// returns false otherwise
func (txData *BaseTransactionData) SetChainAndSender(cctx *types.CrossChainTx, logger zerolog.Logger) bool {
switch cctx.CctxStatus.Status {
case types.CctxStatus_PendingRevert:
txData.to = ethcommon.HexToAddress(cctx.InboundTxParams.Sender)
txData.toChainID = big.NewInt(cctx.InboundTxParams.SenderChainId)
logger.Info().Msgf("Abort: reverting inbound")
case types.CctxStatus_PendingOutbound:
txData.to = ethcommon.HexToAddress(cctx.GetCurrentOutTxParam().Receiver)
txData.toChainID = big.NewInt(cctx.GetCurrentOutTxParam().ReceiverChainId)
default:
logger.Info().Msgf("Transaction doesn't need to be processed status: %d", cctx.CctxStatus.Status)
return true
}
return false
}

// SetupGas sets the gas limit and price
func (txData *BaseTransactionData) SetupGas(
cctx *types.CrossChainTx,
logger zerolog.Logger,
client interfaces.EVMRPCClient,
chain *common.Chain,
kevinssgh marked this conversation as resolved.
Show resolved Hide resolved
) error {

txData.gasLimit = cctx.GetCurrentOutTxParam().OutboundTxGasLimit
if txData.gasLimit < MinGasLimit {
txData.gasLimit = MinGasLimit
logger.Warn().Msgf("gasLimit %d is too low; set to %d", cctx.GetCurrentOutTxParam().OutboundTxGasLimit, txData.gasLimit)
}
if txData.gasLimit > MaxGasLimit {
txData.gasLimit = MaxGasLimit
logger.Warn().Msgf("gasLimit %d is too high; set to %d", cctx.GetCurrentOutTxParam().OutboundTxGasLimit, txData.gasLimit)

Check warning on line 79 in zetaclient/evm/base_transaction_data.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/evm/base_transaction_data.go#L78-L79

Added lines #L78 - L79 were not covered by tests
}

// use dynamic gas price for ethereum chains.
// The code below is a fix for https://github.com/zeta-chain/node/issues/1085
// doesn't close directly the issue because we should determine if we want to keep using SuggestGasPrice if no OutboundTxGasPrice
// we should possibly remove it completely and return an error if no OutboundTxGasPrice is provided because it means no fee is processed on ZetaChain
specified, ok := new(big.Int).SetString(cctx.GetCurrentOutTxParam().OutboundTxGasPrice, 10)
if !ok {
if common.IsEthereumChain(chain.ChainId) {
suggested, err := client.SuggestGasPrice(context.Background())
if err != nil {
return errors.Join(err, fmt.Errorf("cannot get gas price from chain %s ", chain))

Check warning on line 91 in zetaclient/evm/base_transaction_data.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/evm/base_transaction_data.go#L89-L91

Added lines #L89 - L91 were not covered by tests
}
txData.gasPrice = roundUpToNearestGwei(suggested)

Check warning on line 93 in zetaclient/evm/base_transaction_data.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/evm/base_transaction_data.go#L93

Added line #L93 was not covered by tests
} else {
return fmt.Errorf("cannot convert gas price %s ", cctx.GetCurrentOutTxParam().OutboundTxGasPrice)
}
} else {
txData.gasPrice = specified
}
return nil
}

// SetTransactionData populates transaction input fields parsed from the cctx and other parameters
// returns
// bool (skipTx) - if the transaction doesn't qualify to be processed the function will return true, meaning that this
//
// cctx will be skipped and false otherwise.
//
// error
func (txData *BaseTransactionData) SetTransactionData(
cctx *types.CrossChainTx,
evmClient *ChainClient,
evmRPC interfaces.EVMRPCClient,
logger zerolog.Logger,
) (bool, error) {

txData.outboundParams = cctx.GetCurrentOutTxParam()
txData.amount = cctx.GetCurrentOutTxParam().Amount.BigInt()
txData.nonce = cctx.GetCurrentOutTxParam().OutboundTxTssNonce
txData.sender = ethcommon.HexToAddress(cctx.InboundTxParams.Sender)
txData.srcChainID = big.NewInt(cctx.InboundTxParams.SenderChainId)
txData.asset = ethcommon.HexToAddress(cctx.InboundTxParams.Asset)

skipTx := txData.SetChainAndSender(cctx, logger)
if skipTx {
return true, nil

Check warning on line 126 in zetaclient/evm/base_transaction_data.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/evm/base_transaction_data.go#L126

Added line #L126 was not covered by tests
}

toChain := common.GetChainFromChainID(txData.toChainID.Int64())
if toChain == nil {
return true, fmt.Errorf("unknown chain: %d", txData.toChainID.Int64())

Check warning on line 131 in zetaclient/evm/base_transaction_data.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/evm/base_transaction_data.go#L131

Added line #L131 was not covered by tests
}

// Get nonce, Early return if the cctx is already processed
nonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce
included, confirmed, err := evmClient.IsSendOutTxProcessed(cctx, logger)
if err != nil {
return true, errors.New("IsSendOutTxProcessed failed")

Check warning on line 138 in zetaclient/evm/base_transaction_data.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/evm/base_transaction_data.go#L138

Added line #L138 was not covered by tests
}
if included || confirmed {
logger.Info().Msgf("CCTX already processed; exit signer")
return true, nil

Check warning on line 142 in zetaclient/evm/base_transaction_data.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/evm/base_transaction_data.go#L141-L142

Added lines #L141 - L142 were not covered by tests
}

// Set up gas limit and gas price
err = txData.SetupGas(cctx, logger, evmRPC, toChain)
if err != nil {
return true, err

Check warning on line 148 in zetaclient/evm/base_transaction_data.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/evm/base_transaction_data.go#L148

Added line #L148 was not covered by tests
}

// Get sendHash
logger.Info().Msgf("chain %s minting %d to %s, nonce %d, finalized zeta bn %d", toChain, cctx.InboundTxParams.Amount, txData.to.Hex(), nonce, cctx.InboundTxParams.InboundTxFinalizedZetaHeight)
sendHash, err := hex.DecodeString(cctx.Index[2:]) // remove the leading 0x
if err != nil || len(sendHash) != 32 {
return true, fmt.Errorf("decode CCTX %s error", cctx.Index)

Check warning on line 155 in zetaclient/evm/base_transaction_data.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/evm/base_transaction_data.go#L155

Added line #L155 was not covered by tests
}
copy(txData.sendHash[:32], sendHash[:32])

// In case there is a pending transaction, make sure this keysign is a transaction replacement
pendingTx := evmClient.GetPendingTx(nonce)
if pendingTx != nil {
if txData.gasPrice.Cmp(pendingTx.GasPrice()) > 0 {
logger.Info().Msgf("replace pending outTx %s nonce %d using gas price %d", pendingTx.Hash().Hex(), nonce, txData.gasPrice)
} else {
logger.Info().Msgf("please wait for pending outTx %s nonce %d to be included", pendingTx.Hash().Hex(), nonce)
return true, nil

Check warning on line 166 in zetaclient/evm/base_transaction_data.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/evm/base_transaction_data.go#L162-L166

Added lines #L162 - L166 were not covered by tests
}
}

// Base64 decode message
if cctx.GetCurrentOutTxParam().CoinType != common.CoinType_Cmd {
txData.message, err = base64.StdEncoding.DecodeString(cctx.RelayedMessage)
if err != nil {
logger.Err(err).Msgf("decode CCTX.Message %s error", cctx.RelayedMessage)

Check warning on line 174 in zetaclient/evm/base_transaction_data.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/evm/base_transaction_data.go#L174

Added line #L174 was not covered by tests
}
}

return false, nil
}
85 changes: 85 additions & 0 deletions zetaclient/evm/base_transaction_data_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package evm

import (
"math/big"
"testing"

ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/rs/zerolog"
"github.com/stretchr/testify/require"
corecommon "github.com/zeta-chain/zetacore/common"
"github.com/zeta-chain/zetacore/x/crosschain/types"
)

func TestSigner_SetChainAndSender(t *testing.T) {
// setup inputs
cctx, err := getCCTX()
require.NoError(t, err)

txData := &BaseTransactionData{}
logger := zerolog.Logger{}

t.Run("SetChainAndSender PendingRevert", func(t *testing.T) {
cctx.CctxStatus.Status = types.CctxStatus_PendingRevert
skipTx := txData.SetChainAndSender(cctx, logger)

require.False(t, skipTx)
require.Equal(t, ethcommon.HexToAddress(cctx.InboundTxParams.Sender), txData.to)
require.Equal(t, big.NewInt(cctx.InboundTxParams.SenderChainId), txData.toChainID)
})

t.Run("SetChainAndSender PendingOutBound", func(t *testing.T) {
cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound
skipTx := txData.SetChainAndSender(cctx, logger)

require.False(t, skipTx)
require.Equal(t, ethcommon.HexToAddress(cctx.GetCurrentOutTxParam().Receiver), txData.to)
require.Equal(t, big.NewInt(cctx.GetCurrentOutTxParam().ReceiverChainId), txData.toChainID)
})

t.Run("SetChainAndSender Should skip cctx", func(t *testing.T) {
cctx.CctxStatus.Status = types.CctxStatus_PendingInbound
skipTx := txData.SetChainAndSender(cctx, logger)
require.True(t, skipTx)
})
}

func TestSigner_SetupGas(t *testing.T) {
cctx, err := getCCTX()
require.NoError(t, err)

evmSigner, err := getNewEvmSigner()
require.NoError(t, err)

txData := &BaseTransactionData{}
logger := zerolog.Logger{}

t.Run("SetupGas_success", func(t *testing.T) {
chain := corecommon.BscMainnetChain()
err := txData.SetupGas(cctx, logger, evmSigner.EvmClient(), &chain)
require.NoError(t, err)
})

t.Run("SetupGas_error", func(t *testing.T) {
cctx.GetCurrentOutTxParam().OutboundTxGasPrice = "invalidGasPrice"
chain := corecommon.BtcMainnetChain()
err := txData.SetupGas(cctx, logger, evmSigner.EvmClient(), &chain)
require.Error(t, err)
kevinssgh marked this conversation as resolved.
Show resolved Hide resolved
})
}

func TestSigner_SetTransactionData(t *testing.T) {
// Setup evm signer
evmSigner, err := getNewEvmSigner()
require.NoError(t, err)

// Setup txData struct
txData := BaseTransactionData{}
cctx, err := getCCTX()
require.NoError(t, err)
mockChainClient, err := getNewEvmChainClient()
require.NoError(t, err)
skip, err := txData.SetTransactionData(cctx, mockChainClient, evmSigner.EvmClient(), zerolog.Logger{})
require.False(t, skip)
require.NoError(t, err)
}
Loading
Loading