Skip to content

Commit

Permalink
converted archived cctxs JSON files to GoLang variables; resolved a f…
Browse files Browse the repository at this point in the history
…ew PR comments
  • Loading branch information
ws4charlie committed Apr 11, 2024
1 parent be565b3 commit 9f61d50
Show file tree
Hide file tree
Showing 35 changed files with 831 additions and 551 deletions.
14 changes: 7 additions & 7 deletions zetaclient/bitcoin/bitcoin_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -504,15 +504,15 @@ func (ob *BTCChainClient) ConfirmationsThreshold(amount *big.Int) int64 {
return int64(ob.GetChainParams().ConfirmationCount)
}

// IsCctxOutTxProcessed returns isIncluded(or inMempool), isConfirmed, Error
func (ob *BTCChainClient) IsCctxOutTxProcessed(cctx *types.CrossChainTx, logger zerolog.Logger) (bool, bool, error) {
// IsOutboundProcessed returns isIncluded(or inMempool), isConfirmed, Error
func (ob *BTCChainClient) IsOutboundProcessed(cctx *types.CrossChainTx, logger zerolog.Logger) (bool, bool, error) {
params := *cctx.GetCurrentOutTxParam()
sendHash := cctx.Index
nonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce

// get broadcasted outtx and tx result
outTxID := ob.GetTxID(nonce)
logger.Info().Msgf("IsCctxOutTxProcessed %s", outTxID)
logger.Info().Msgf("IsOutboundProcessed %s", outTxID)

ob.Mu.Lock()
txnHash, broadcasted := ob.broadcastedTx[outTxID]
Expand All @@ -537,7 +537,7 @@ func (ob *BTCChainClient) IsCctxOutTxProcessed(cctx *types.CrossChainTx, logger
if txResult == nil { // check failed, try again next time
return false, false, nil
} else if inMempool { // still in mempool (should avoid unnecessary Tss keysign)
ob.logger.OutTx.Info().Msgf("IsCctxOutTxProcessed: outTx %s is still in mempool", outTxID)
ob.logger.OutTx.Info().Msgf("IsOutboundProcessed: outTx %s is still in mempool", outTxID)
return true, false, nil
}
// included
Expand All @@ -548,7 +548,7 @@ func (ob *BTCChainClient) IsCctxOutTxProcessed(cctx *types.CrossChainTx, logger
if res == nil {
return false, false, nil
}
ob.logger.OutTx.Info().Msgf("IsCctxOutTxProcessed: setIncludedTx succeeded for outTx %s", outTxID)
ob.logger.OutTx.Info().Msgf("IsOutboundProcessed: setIncludedTx succeeded for outTx %s", outTxID)
}

// It's safe to use cctx's amount to post confirmation because it has already been verified in observeOutTx()
Expand All @@ -573,9 +573,9 @@ func (ob *BTCChainClient) IsCctxOutTxProcessed(cctx *types.CrossChainTx, logger
coin.CoinType_Gas,
)
if err != nil {
logger.Error().Err(err).Msgf("IsCctxOutTxProcessed: error confirming bitcoin outTx %s, nonce %d ballot %s", res.TxID, nonce, ballot)
logger.Error().Err(err).Msgf("IsOutboundProcessed: error confirming bitcoin outTx %s, nonce %d ballot %s", res.TxID, nonce, ballot)
} else if zetaHash != "" {
logger.Info().Msgf("IsCctxOutTxProcessed: confirmed Bitcoin outTx %s, zeta tx hash %s nonce %d ballot %s", res.TxID, zetaHash, nonce, ballot)
logger.Info().Msgf("IsOutboundProcessed: confirmed Bitcoin outTx %s, zeta tx hash %s nonce %d ballot %s", res.TxID, zetaHash, nonce, ballot)
}
return true, true, nil
}
Expand Down
16 changes: 13 additions & 3 deletions zetaclient/evm/evm_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,15 @@ func NewEVMChainClient(

return &ob, nil
}

// WithChain attaches a new chain to the chain client
func (ob *ChainClient) WithChain(chain chains.Chain) {
ob.Mu.Lock()
defer ob.Mu.Unlock()
ob.chain = chain
}

// WithLogger attaches a new logger to the chain client
func (ob *ChainClient) WithLogger(logger zerolog.Logger) {
ob.Mu.Lock()
defer ob.Mu.Unlock()
Expand All @@ -174,36 +178,42 @@ func (ob *ChainClient) WithLogger(logger zerolog.Logger) {
}
}

// WithEvmClient attaches a new evm client to the chain client
func (ob *ChainClient) WithEvmClient(client interfaces.EVMRPCClient) {
ob.Mu.Lock()
defer ob.Mu.Unlock()
ob.evmClient = client
}

// WithEvmJSONRPC attaches a new evm json rpc client to the chain client
func (ob *ChainClient) WithEvmJSONRPC(client interfaces.EVMJSONRPCClient) {
ob.Mu.Lock()
defer ob.Mu.Unlock()
ob.evmJSONRPC = client
}

// WithZetaBridge attaches a new bridge to interact with ZetaCore to the chain client
func (ob *ChainClient) WithZetaBridge(bridge interfaces.ZetaCoreBridger) {
ob.Mu.Lock()
defer ob.Mu.Unlock()
ob.zetaBridge = bridge
}

// WithBlockCache attaches a new block cache to the chain client
func (ob *ChainClient) WithBlockCache(cache *lru.Cache) {
ob.Mu.Lock()
defer ob.Mu.Unlock()
ob.blockCache = cache
}

// SetChainParams sets the chain params for the chain client
func (ob *ChainClient) SetChainParams(params observertypes.ChainParams) {
ob.Mu.Lock()
defer ob.Mu.Unlock()
ob.chainParams = params
}

// GetChainParams returns the chain params for the chain client
func (ob *ChainClient) GetChainParams() observertypes.ChainParams {
ob.Mu.Lock()
defer ob.Mu.Unlock()
Expand Down Expand Up @@ -339,7 +349,7 @@ func (ob *ChainClient) WatchOutTx() {
}
for _, tracker := range trackers {
nonceInt := tracker.Nonce
if ob.isTxConfirmed(nonceInt) { // Go to next tracker if this one already has a confirmed tx
if ob.IsTxConfirmed(nonceInt) { // Go to next tracker if this one already has a confirmed tx
continue
}
txCount := 0
Expand Down Expand Up @@ -403,8 +413,8 @@ func (ob *ChainClient) GetTxNReceipt(nonce uint64) (*ethtypes.Receipt, *ethtypes
return receipt, transaction
}

// isTxConfirmed returns true if there is a confirmed tx for 'nonce'
func (ob *ChainClient) isTxConfirmed(nonce uint64) bool {
// IsTxConfirmed returns true if there is a confirmed tx for 'nonce'
func (ob *ChainClient) IsTxConfirmed(nonce uint64) bool {
ob.Mu.Lock()
defer ob.Mu.Unlock()
return ob.outTXConfirmedReceipts[ob.GetTxID(nonce)] != nil && ob.outTXConfirmedTransactions[ob.GetTxID(nonce)] != nil
Expand Down
12 changes: 5 additions & 7 deletions zetaclient/evm/evm_signer_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package evm

import (
"path"
"testing"

sdktypes "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -67,15 +66,14 @@ func getNewOutTxProcessor() *outtxprocessor.Processor {
}

func getCCTX(t *testing.T) *crosschaintypes.CrossChainTx {
var cctx crosschaintypes.CrossChainTx
testutils.LoadObjectFromJSONFile(t, &cctx, path.Join("../", testutils.TestDataPathCctx, "cctx_56_68270.json"))
return &cctx
return testutils.LoadCctxByNonce(t, 56, 68270)
}

func getInvalidCCTX(t *testing.T) *crosschaintypes.CrossChainTx {
var cctx crosschaintypes.CrossChainTx
testutils.LoadObjectFromJSONFile(t, &cctx, path.Join("../", testutils.TestDataPathCctx, "cctx_56_68270_invalidChainID.json"))
return &cctx
cctx := getCCTX(t)
// modify receiver chain id to make it invalid
cctx.GetCurrentOutTxParam().ReceiverChainId = 13378337
return cctx
}

func TestSigner_SetGetConnectorAddress(t *testing.T) {
Expand Down
6 changes: 3 additions & 3 deletions zetaclient/evm/inbounds_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ func TestEVM_BuildInboundVoteMsgForZetaSentEvent(t *testing.T) {
chain := chains.EthChain()
intxHash := "0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76"
receipt := testutils.LoadEVMIntxReceipt(t, chainID, intxHash, coin.CoinType_Zeta)
cctx := testutils.LoadEVMIntxCctx(t, chainID, intxHash, coin.CoinType_Zeta)
cctx := testutils.LoadCctxByIntx(t, chainID, coin.CoinType_Zeta, intxHash)

// parse ZetaSent event
ob := MockEVMClient(t, chain, nil, nil, nil, nil, 1, stub.MockChainParams(1, 1))
Expand Down Expand Up @@ -237,7 +237,7 @@ func TestEVM_BuildInboundVoteMsgForDepositedEvent(t *testing.T) {
chainID := chain.ChainId
intxHash := "0x4ea69a0e2ff36f7548ab75791c3b990e076e2a4bffeb616035b239b7d33843da"
tx, receipt := testutils.LoadEVMIntxNReceipt(t, chainID, intxHash, coin.CoinType_ERC20)
cctx := testutils.LoadEVMIntxCctx(t, chainID, intxHash, coin.CoinType_ERC20)
cctx := testutils.LoadCctxByIntx(t, chainID, coin.CoinType_ERC20, intxHash)

// parse Deposited event
ob := MockEVMClient(t, chain, nil, nil, nil, nil, 1, stub.MockChainParams(1, 1))
Expand Down Expand Up @@ -283,7 +283,7 @@ func TestEVM_BuildInboundVoteMsgForTokenSentToTSS(t *testing.T) {
intxHash := "0xeaec67d5dd5d85f27b21bef83e01cbdf59154fd793ea7a22c297f7c3a722c532"
tx, receipt := testutils.LoadEVMIntxNReceipt(t, chainID, intxHash, coin.CoinType_Gas)
require.NoError(t, evm.ValidateEvmTransaction(tx))
cctx := testutils.LoadEVMIntxCctx(t, chainID, intxHash, coin.CoinType_Gas)
cctx := testutils.LoadCctxByIntx(t, chainID, coin.CoinType_Gas, intxHash)

// load archived gas token donation to TSS
// https://etherscan.io/tx/0x52f214cf7b10be71f4d274193287d47bc9632b976e69b9d2cdeb527c2ba32155
Expand Down
4 changes: 2 additions & 2 deletions zetaclient/evm/outbound_transaction_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,9 @@ func NewOutBoundTransactionData(

// Get nonce, Early return if the cctx is already processed
nonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce
included, confirmed, err := evmClient.IsCctxOutTxProcessed(cctx, logger)
included, confirmed, err := evmClient.IsOutboundProcessed(cctx, logger)
if err != nil {
return nil, true, errors.New("IsCctxOutTxProcessed failed")
return nil, true, errors.New("IsOutboundProcessed failed")
}
if included || confirmed {
logger.Info().Msgf("CCTX already processed; exit signer")
Expand Down
14 changes: 8 additions & 6 deletions zetaclient/evm/outbounds.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@ func (ob *ChainClient) PostVoteOutbound(
}
}

// IsCctxOutTxProcessed checks outtx status and returns (isIncluded, isConfirmed, error)
// IsOutboundProcessed checks outtx status and returns (isIncluded, isConfirmed, error)
// It also posts vote to zetacore if the tx is confirmed
func (ob *ChainClient) IsCctxOutTxProcessed(cctx *crosschaintypes.CrossChainTx, logger zerolog.Logger) (bool, bool, error) {
func (ob *ChainClient) IsOutboundProcessed(cctx *crosschaintypes.CrossChainTx, logger zerolog.Logger) (bool, bool, error) {
// skip if outtx is not confirmed
nonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce
if !ob.isTxConfirmed(nonce) {
if !ob.IsTxConfirmed(nonce) {
return false, false, nil
}
receipt, transaction := ob.GetTxNReceipt(nonce)
Expand Down Expand Up @@ -92,7 +92,7 @@ func (ob *ChainClient) IsCctxOutTxProcessed(cctx *crosschaintypes.CrossChainTx,
// parse the received value from the outtx receipt
receiveValue, receiveStatus, err = ParseOuttxReceivedValue(cctx, receipt, transaction, cointype, connectorAddr, connector, custodyAddr, custody)
if err != nil {
logger.Error().Err(err).Msgf("IsCctxOutTxProcessed: error parsing outtx event for chain %d txhash %s", ob.chain.ChainId, receipt.TxHash)
logger.Error().Err(err).Msgf("IsOutboundProcessed: error parsing outtx event for chain %d txhash %s", ob.chain.ChainId, receipt.TxHash)
return false, false, err
}

Expand Down Expand Up @@ -189,7 +189,8 @@ func ParseAndCheckWithdrawnEvent(
return nil, errors.New("no ERC20 Withdrawn event found")
}

// ParseOuttxReceivedValue parses the received value from the outtx receipt
// ParseOuttxReceivedValue parses the received value and status from the outtx receipt
// The receivd value is the amount of Zeta/ERC20/Gas token (released from connector/custody/TSS) sent to the receiver
func ParseOuttxReceivedValue(
cctx *crosschaintypes.CrossChainTx,
receipt *ethtypes.Receipt,
Expand All @@ -198,7 +199,8 @@ func ParseOuttxReceivedValue(
connectorAddress ethcommon.Address,
connector *zetaconnector.ZetaConnectorNonEth,
custodyAddress ethcommon.Address,
custody *erc20custody.ERC20Custody) (*big.Int, chains.ReceiveStatus, error) {
custody *erc20custody.ERC20Custody,
) (*big.Int, chains.ReceiveStatus, error) {
// determine the receive status and value
// https://docs.nethereum.com/en/latest/nethereum-receipt-status/
receiveValue := big.NewInt(0)
Expand Down
104 changes: 74 additions & 30 deletions zetaclient/evm/outbounds_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,32 +27,7 @@ func getContractsByChainID(chainID int64) (*zetaconnector.ZetaConnectorNonEth, e
return connector, connectorAddress, custody, custodyAddress
}

func Test_PostVoteOutbound(t *testing.T) {
// Note: outtx of Gas/ERC20 token can also be used for this test
// load archived cctx, outtx and receipt for a ZetaReceived event
// https://etherscan.io/tx/0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f
chain := chains.EthChain()
nonce := uint64(9718)
coinType := coin.CoinType_Zeta
cctx, outtx, receipt := testutils.LoadEVMCctxNOuttxNReceipt(t, chain.ChainId, nonce, testutils.EventZetaReceived)

t.Run("post vote outbound successfully", func(t *testing.T) {
// the amount and status to be used for vote
receiveValue := cctx.GetCurrentOutTxParam().Amount.BigInt()
receiveStatus := chains.ReceiveStatus_Success

// create evm client using mock zetaBridge and post outbound vote
zetaBridge := stub.NewMockZetaCoreBridge()
client := MockEVMClient(t, chain, nil, nil, zetaBridge, nil, 1, observertypes.ChainParams{})
client.PostVoteOutbound(cctx.Index, receipt, outtx, receiveValue, receiveStatus, nonce, coinType, zerolog.Logger{})

// pause the mock zetaBridge to simulate error posting vote
zetaBridge.Pause()
client.PostVoteOutbound(cctx.Index, receipt, outtx, receiveValue, receiveStatus, nonce, coinType, zerolog.Logger{})
})
}

func Test_IsCctxOutTxProcessed(t *testing.T) {
func Test_IsOutboundProcessed(t *testing.T) {
// load archived outtx receipt that contains ZetaReceived event
// https://etherscan.io/tx/0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f
chain := chains.EthChain()
Expand All @@ -69,7 +44,7 @@ func Test_IsCctxOutTxProcessed(t *testing.T) {
client := MockEVMClient(t, chain, nil, nil, nil, nil, 1, chainParam)
client.SetTxNReceipt(nonce, receipt, outtx)
// post outbound vote
isIncluded, isConfirmed, err := client.IsCctxOutTxProcessed(cctx, zerolog.Logger{})
isIncluded, isConfirmed, err := client.IsOutboundProcessed(cctx, zerolog.Logger{})
require.NoError(t, err)
require.True(t, isIncluded)
require.True(t, isConfirmed)
Expand All @@ -93,15 +68,15 @@ func Test_IsCctxOutTxProcessed(t *testing.T) {
config.LoadComplianceConfig(cfg)

// post outbound vote
isIncluded, isConfirmed, err := client.IsCctxOutTxProcessed(cctx, zerolog.Logger{})
isIncluded, isConfirmed, err := client.IsOutboundProcessed(cctx, zerolog.Logger{})
require.NoError(t, err)
require.True(t, isIncluded)
require.True(t, isConfirmed)
})
t.Run("should return false if outtx is not confirmed", func(t *testing.T) {
// create evm client and DO NOT set outtx as confirmed
client := MockEVMClient(t, chain, nil, nil, nil, nil, 1, chainParam)
isIncluded, isConfirmed, err := client.IsCctxOutTxProcessed(cctx, zerolog.Logger{})
isIncluded, isConfirmed, err := client.IsOutboundProcessed(cctx, zerolog.Logger{})
require.NoError(t, err)
require.False(t, isIncluded)
require.False(t, isConfirmed)
Expand All @@ -115,13 +90,82 @@ func Test_IsCctxOutTxProcessed(t *testing.T) {
chainParamsNew := client.GetChainParams()
chainParamsNew.ConnectorContractAddress = sample.EthAddress().Hex()
client.SetChainParams(chainParamsNew)
isIncluded, isConfirmed, err := client.IsCctxOutTxProcessed(cctx, zerolog.Logger{})
isIncluded, isConfirmed, err := client.IsOutboundProcessed(cctx, zerolog.Logger{})
require.Error(t, err)
require.False(t, isIncluded)
require.False(t, isConfirmed)
})
}

func Test_IsOutboundProcessed_ContractError(t *testing.T) {
// Note: this test is skipped because it will cause CI failure.
// The only way to replicate a contract error is to use an invalid ABI.
// See the code: https://github.com/ethereum/go-ethereum/blob/v1.10.26/accounts/abi/bind/base.go#L97
// The ABI is hardcoded in the protocol-contracts package and initialized the 1st time it binds the contract.
// Any subsequent modification to the ABI will not work and therefor fail the unit test.
t.Skip("uncomment this line to run this test separately, otherwise it will fail CI")

// load archived outtx receipt that contains ZetaReceived event
// https://etherscan.io/tx/0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f
chain := chains.EthChain()
chainID := chains.EthChain().ChainId
nonce := uint64(9718)
chainParam := stub.MockChainParams(chain.ChainId, 1)
outtxHash := "0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f"
cctx := testutils.LoadCctxByNonce(t, chainID, nonce)
receipt := testutils.LoadEVMOuttxReceipt(t, chainID, outtxHash, coin.CoinType_Zeta, testutils.EventZetaReceived)
cctx, outtx, receipt := testutils.LoadEVMCctxNOuttxNReceipt(t, chainID, nonce, testutils.EventZetaReceived)

t.Run("should fail if unable to get connector/custody contract", func(t *testing.T) {
// create evm client and set outtx and receipt
client := MockEVMClient(t, chain, nil, nil, nil, nil, 1, chainParam)
client.SetTxNReceipt(nonce, receipt, outtx)
abiConnector := zetaconnector.ZetaConnectorNonEthMetaData.ABI
abiCustody := erc20custody.ERC20CustodyMetaData.ABI

// set invalid connector ABI
zetaconnector.ZetaConnectorNonEthMetaData.ABI = "invalid abi"
isIncluded, isConfirmed, err := client.IsOutboundProcessed(cctx, zerolog.Logger{})
zetaconnector.ZetaConnectorNonEthMetaData.ABI = abiConnector // reset connector ABI
require.ErrorContains(t, err, "error getting zeta connector")
require.False(t, isIncluded)
require.False(t, isConfirmed)

// set invalid custody ABI
erc20custody.ERC20CustodyMetaData.ABI = "invalid abi"
isIncluded, isConfirmed, err = client.IsOutboundProcessed(cctx, zerolog.Logger{})
require.ErrorContains(t, err, "error getting erc20 custody")
require.False(t, isIncluded)
require.False(t, isConfirmed)
erc20custody.ERC20CustodyMetaData.ABI = abiCustody // reset custody ABI
})
}

func Test_PostVoteOutbound(t *testing.T) {
// Note: outtx of Gas/ERC20 token can also be used for this test
// load archived cctx, outtx and receipt for a ZetaReceived event
// https://etherscan.io/tx/0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f
chain := chains.EthChain()
nonce := uint64(9718)
coinType := coin.CoinType_Zeta
cctx, outtx, receipt := testutils.LoadEVMCctxNOuttxNReceipt(t, chain.ChainId, nonce, testutils.EventZetaReceived)

t.Run("post vote outbound successfully", func(t *testing.T) {
// the amount and status to be used for vote
receiveValue := cctx.GetCurrentOutTxParam().Amount.BigInt()
receiveStatus := chains.ReceiveStatus_Success

// create evm client using mock zetaBridge and post outbound vote
zetaBridge := stub.NewMockZetaCoreBridge()
client := MockEVMClient(t, chain, nil, nil, zetaBridge, nil, 1, observertypes.ChainParams{})
client.PostVoteOutbound(cctx.Index, receipt, outtx, receiveValue, receiveStatus, nonce, coinType, zerolog.Logger{})

// pause the mock zetaBridge to simulate error posting vote
zetaBridge.Pause()
client.PostVoteOutbound(cctx.Index, receipt, outtx, receiveValue, receiveStatus, nonce, coinType, zerolog.Logger{})
})
}

func Test_ParseZetaReceived(t *testing.T) {
// load archived outtx receipt that contains ZetaReceived event
// https://etherscan.io/tx/0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f
Expand Down
2 changes: 1 addition & 1 deletion zetaclient/interfaces/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const (
type ChainClient interface {
Start()
Stop()
IsCctxOutTxProcessed(cctx *crosschaintypes.CrossChainTx, logger zerolog.Logger) (bool, bool, error)
IsOutboundProcessed(cctx *crosschaintypes.CrossChainTx, logger zerolog.Logger) (bool, bool, error)
SetChainParams(observertypes.ChainParams)
GetChainParams() observertypes.ChainParams
GetTxID(nonce uint64) string
Expand Down
Loading

0 comments on commit 9f61d50

Please sign in to comment.