From cb7ffb57b34450df7c8e32c06d5a7f6a01861787 Mon Sep 17 00:00:00 2001 From: Oleg Nikonychev Date: Fri, 10 Jan 2025 21:59:58 +0400 Subject: [PATCH 1/6] fix: added tx logs emitting to the funtoken related txs --- eth/rpc/backend/backend_suite_test.go | 34 ++- eth/rpc/backend/nonce_test.go | 6 +- eth/rpc/backend/tx_logs_test.go | 288 +++++++++++++++++++++++ eth/rpc/backend/utils_test.go | 18 +- x/common/testutil/testnetwork/tx.go | 15 +- x/common/testutil/testnetwork/tx_test.go | 2 +- x/evm/evmtest/tx.go | 4 +- x/evm/keeper/call_contract.go | 13 +- x/evm/keeper/grpc_query_test.go | 4 +- x/oracle/keeper/app_test.go | 4 +- 10 files changed, 354 insertions(+), 34 deletions(-) create mode 100644 eth/rpc/backend/tx_logs_test.go diff --git a/eth/rpc/backend/backend_suite_test.go b/eth/rpc/backend/backend_suite_test.go index 0cc6fa7b6..34b8dd88d 100644 --- a/eth/rpc/backend/backend_suite_test.go +++ b/eth/rpc/backend/backend_suite_test.go @@ -5,6 +5,7 @@ import ( "crypto/ecdsa" "fmt" "math/big" + "sync" "testing" "time" @@ -33,6 +34,9 @@ import ( "github.com/NibiruChain/nibiru/v2/x/common/testutil/testnetwork" ) +// testMutex is used to synchronize the tests which are broadcasting transactions concurrently +var testMutex sync.Mutex + var ( recipient = evmtest.NewEthPrivAcc().EthAddr amountToSend = evm.NativeToWei(big.NewInt(1)) @@ -95,7 +99,7 @@ func (s *BackendSuite) SetupSuite() { // Send Transfer TX and use the results in the tests s.Require().NoError(err) transferTxHash = s.SendNibiViaEthTransfer(recipient, amountToSend, true) - blockNumber, blockHash := WaitForReceipt(s, transferTxHash) + blockNumber, blockHash, _ := WaitForReceipt(s, transferTxHash) s.Require().NotNil(blockNumber) s.Require().NotNil(blockHash) transferTxBlockNumber = rpc.NewBlockNumber(blockNumber) @@ -104,7 +108,7 @@ func (s *BackendSuite) SetupSuite() { // Deploy test erc20 contract deployContractTxHash, contractAddress := s.DeployTestContract(true) testContractAddress = contractAddress - blockNumber, blockHash = WaitForReceipt(s, deployContractTxHash) + blockNumber, blockHash, _ = WaitForReceipt(s, deployContractTxHash) s.Require().NotNil(blockNumber) s.Require().NotNil(blockHash) deployContractBlockNumber = rpc.NewBlockNumber(blockNumber) @@ -167,35 +171,41 @@ func SendTransaction(s *BackendSuite, tx *gethcore.LegacyTx, waitForNextBlock bo return txHash } -func WaitForReceipt(s *BackendSuite, txHash gethcommon.Hash) (*big.Int, *gethcommon.Hash) { +// WaitForReceipt waits for a transaction to be included in a block, returns block number, block hash and receipt +func WaitForReceipt(s *BackendSuite, txHash gethcommon.Hash) (*big.Int, *gethcommon.Hash, *backend.TransactionReceipt) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() for { receipt, err := s.backend.GetTransactionReceipt(txHash) if err != nil { - return nil, nil + return nil, nil, nil } if receipt != nil { - return receipt.BlockNumber, &receipt.BlockHash + return receipt.BlockNumber, &receipt.BlockHash, receipt } select { case <-ctx.Done(): fmt.Println("Timeout reached, transaction not included in a block yet.") - return nil, nil + return nil, nil, nil default: time.Sleep(1 * time.Second) } } } -// getCurrentNonce returns the current nonce of the funded account -func (s *BackendSuite) getCurrentNonce(address gethcommon.Address) uint64 { - bn, err := s.backend.BlockNumber() +// getUnibiBalance returns the balance of an address in unibi +func (s *BackendSuite) getUnibiBalance(address gethcommon.Address) *big.Int { + latestBlock := rpc.EthLatestBlockNumber + latestBlockOrHash := rpc.BlockNumberOrHash{BlockNumber: &latestBlock} + balance, err := s.backend.GetBalance(address, latestBlockOrHash) s.Require().NoError(err) - currentHeight := rpc.BlockNumber(bn) + return evm.WeiToNative(balance.ToInt()) +} - nonce, err := s.backend.GetTransactionCount(address, currentHeight) +// getCurrentNonce returns the current nonce of the funded account +func (s *BackendSuite) getCurrentNonce(address gethcommon.Address) uint64 { + nonce, err := s.backend.GetTransactionCount(address, rpc.EthPendingBlockNumber) s.Require().NoError(err) return uint64(*nonce) @@ -234,7 +244,7 @@ func (s *BackendSuite) buildContractCreationTx(nonce uint64) gethcore.Transactio // buildContractCallTx builds a contract call transaction func (s *BackendSuite) buildContractCallTx(nonce uint64, contractAddr gethcommon.Address) gethcore.Transaction { - // recipient := crypto.CreateAddress(s.fundedAccEthAddr, 29381) + //recipient := crypto.CreateAddress(s.fundedAccEthAddr, 29381) transferAmount := big.NewInt(100) packedArgs, err := embeds.SmartContract_TestERC20.ABI.Pack( diff --git a/eth/rpc/backend/nonce_test.go b/eth/rpc/backend/nonce_test.go index 8f79fef1e..cd35c9088 100644 --- a/eth/rpc/backend/nonce_test.go +++ b/eth/rpc/backend/nonce_test.go @@ -13,6 +13,10 @@ import ( // TestNonceIncrementWithMultipleMsgsTx tests that the nonce is incremented correctly // when multiple messages are included in a single transaction. func (s *BackendSuite) TestNonceIncrementWithMultipleMsgsTx() { + // Test is broadcasting txs. Lock to avoid nonce conflicts. + testMutex.Lock() + defer testMutex.Unlock() + nonce := s.getCurrentNonce(s.fundedAccEthAddr) // Create series of 3 tx messages. Expecting nonce to be incremented by 3 @@ -38,7 +42,7 @@ func (s *BackendSuite) TestNonceIncrementWithMultipleMsgsTx() { // Assert all transactions included in block for _, tx := range []gethcore.Transaction{creationTx, firstTransferTx, secondTransferTx} { - blockNum, blockHash := WaitForReceipt(s, tx.Hash()) + blockNum, blockHash, _ := WaitForReceipt(s, tx.Hash()) s.Require().NotNil(blockNum) s.Require().NotNil(blockHash) } diff --git a/eth/rpc/backend/tx_logs_test.go b/eth/rpc/backend/tx_logs_test.go new file mode 100644 index 000000000..bfcfde152 --- /dev/null +++ b/eth/rpc/backend/tx_logs_test.go @@ -0,0 +1,288 @@ +package backend_test + +import ( + "fmt" + "math/big" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/eth/rpc/backend" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + "github.com/NibiruChain/nibiru/v2/x/evm/precompile" + tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" +) + +// TestEthLogs checks that eth txs as well as funtoken txs produce tx_logs events and update tx index properly. +// To check that, we send a series of transactions: +// - 1: simple eth transfer +// - 2: deploying erc20 contract +// - 3. creating funtoken from erc20 +// - 4: creating funtoken from coin +// - 5. converting coin to erc20 +// - 6. converting erc20 born token to coin via precompile +// Each tx should emit some tx logs and emit proper tx index within ethereum tx event. +func (s *BackendSuite) TestLogs() { + // Test is broadcasting txs. Lock to avoid nonce conflicts. + testMutex.Lock() + defer testMutex.Unlock() + + // Start with fresh block + s.Require().NoError(s.network.WaitForNextBlock()) + + s.T().Log("TX1: Send simple nibi transfer") + randomEthAddr := evmtest.NewEthPrivAcc().EthAddr + txHashFirst := s.SendNibiViaEthTransfer(randomEthAddr, amountToSend, false) + + s.T().Log("TX2: Deploy ERC20 contract") + _, erc20ContractAddr := s.DeployTestContract(false) + erc20Addr, _ := eth.NewEIP55AddrFromStr(erc20ContractAddr.String()) + + s.T().Log("TX3: Create FunToken from ERC20") + nonce := s.getCurrentNonce(eth.NibiruAddrToEthAddr(s.node.Address)) + txResp, err := s.network.BroadcastMsgs(s.node.Address, &nonce, &evm.MsgCreateFunToken{ + Sender: s.node.Address.String(), + FromErc20: &erc20Addr, + }) + s.Require().NoError(err) + s.Require().NotNil(txResp) + s.Require().Equal( + uint32(0), + txResp.Code, + fmt.Sprintf("Failed to create FunToken from ERC20. RawLog: %s", txResp.RawLog), + ) + + s.T().Log("TX4: Create FunToken from unibi coin") + nonce++ + erc20FromCoinAddr := crypto.CreateAddress(evm.EVM_MODULE_ADDRESS, s.getCurrentNonce(evm.EVM_MODULE_ADDRESS)) + + txResp, err = s.network.BroadcastMsgs(s.node.Address, &nonce, &evm.MsgCreateFunToken{ + Sender: s.node.Address.String(), + FromBankDenom: evm.EVMBankDenom, + }) + s.Require().NoError(err) + s.Require().NotNil(txResp) + s.Require().Equal( + uint32(0), + txResp.Code, + fmt.Sprintf("Failed to create FunToken from unibi coin. RawLog: %s", txResp.RawLog), + ) + + s.T().Log("TX5: Convert coin to EVM") + nonce++ + txResp, err = s.network.BroadcastMsgs(s.node.Address, &nonce, &evm.MsgConvertCoinToEvm{ + Sender: s.node.Address.String(), + BankCoin: sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(1)), + ToEthAddr: eth.EIP55Addr{ + Address: s.fundedAccEthAddr, + }, + }) + s.Require().NoError(err) + s.Require().NotNil(txResp) + s.Require().Equal( + uint32(0), + txResp.Code, + fmt.Sprintf("Failed converting coin to evm. RawLog: %s", txResp.RawLog), + ) + + s.T().Log("TX6: Send erc20 token to coin using precompile") + randomNibiAddress := testutil.AccAddress() + packedArgsPass, err := embeds.SmartContract_FunToken.ABI.Pack( + "sendToBank", + erc20Addr.Address, + big.NewInt(1), + randomNibiAddress.String(), + ) + s.Require().NoError(err) + txHashLast := SendTransaction( + s, + &gethcore.LegacyTx{ + Nonce: s.getCurrentNonce(s.fundedAccEthAddr), + To: &precompile.PrecompileAddr_FunToken, + Data: packedArgsPass, + Gas: 1_500_000, + GasPrice: big.NewInt(1), + }, + false, + ) + + // Wait for all txs to be included in a block + blockNumFirstTx, _, _ := WaitForReceipt(s, txHashFirst) + blockNumLastTx, _, _ := WaitForReceipt(s, txHashLast) + s.Require().NotNil(blockNumFirstTx) + s.Require().NotNil(blockNumLastTx) + + // Check tx logs for each tx + type logsCheck struct { + txInfo string + logs []*gethcore.Log + expectEthTx bool + } + checks := []logsCheck{ + { + txInfo: "TX1 - simple eth transfer, should have empty logs", + logs: nil, + expectEthTx: true, + }, + { + txInfo: "TX2 - deploying erc20 contract, should have logs", + logs: []*gethcore.Log{ + // minting initial balance to the account + { + Address: erc20ContractAddr, + Topics: []gethcommon.Hash{ + crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")), + gethcommon.Address{}.Hash(), + s.fundedAccEthAddr.Hash(), + }, + }, + }, + expectEthTx: true, + }, + { + txInfo: "TX3 - create FunToken from ERC20, no eth tx, no logs", + logs: nil, + expectEthTx: false, + }, + { + txInfo: "TX4 - create FunToken from unibi coin, no eth tx, logs for contract deployment", + logs: []*gethcore.Log{ + // contract ownership to evm module + { + Address: erc20FromCoinAddr, + Topics: []gethcommon.Hash{ + crypto.Keccak256Hash([]byte("OwnershipTransferred(address,address)")), + gethcommon.Address{}.Hash(), + evm.EVM_MODULE_ADDRESS.Hash(), + }, + }, + }, + expectEthTx: false, + }, + { + txInfo: "TX5 - Convert coin to EVM, no eth tx, logs for minting tokens to the account", + logs: []*gethcore.Log{ + // minting to the account + { + Address: erc20FromCoinAddr, + Topics: []gethcommon.Hash{ + crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")), + gethcommon.Address{}.Hash(), + s.fundedAccEthAddr.Hash(), + }, + }, + }, + expectEthTx: false, + }, + { + txInfo: "TX6 - Send erc20 token to coin using precompile, eth tx, logs for transferring tokens to evm module", + logs: []*gethcore.Log{ + // transfer from account to evm module + { + Address: erc20ContractAddr, + Topics: []gethcommon.Hash{ + crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")), + s.fundedAccEthAddr.Hash(), + evm.EVM_MODULE_ADDRESS.Hash(), + }, + }, + }, + expectEthTx: true, + }, + } + + // Getting block results. Note: txs could be included in more than one block + blockNumber := blockNumFirstTx.Int64() + blockRes, err := s.backend.TendermintBlockResultByNumber(&blockNumber) + s.Require().NoError(err) + s.Require().NotNil(blockRes) + txIndex := 0 + for idx, check := range checks { + if txIndex+1 > len(blockRes.TxsResults) { + blockNumber++ + if blockNumber > blockNumLastTx.Int64() { + s.Fail("TX %d not found in block results", idx) + } + txIndex = 0 + blockRes, err = s.backend.TendermintBlockResultByNumber(&blockNumber) + s.Require().NoError(err) + s.Require().NotNil(blockRes) + } + s.assertTxLogsAndTxIndex(blockRes, txIndex, check.logs, check.expectEthTx, check.txInfo) + txIndex++ + } +} + +// assertTxLogsAndTxIndex gets tx results from the block and checks tx logs and tx index. +func (s *BackendSuite) assertTxLogsAndTxIndex( + blockRes *tmrpctypes.ResultBlockResults, + txIndex int, + expectedTxLogs []*gethcore.Log, + expectedEthTx bool, + txInfo string, +) { + txResults := blockRes.TxsResults[txIndex] + s.Require().Equal(uint32(0x0), txResults.Code, "tx failed, %s. RawLog: %s", txInfo, txResults.Log) + + events := blockRes.TxsResults[txIndex].Events + + foundEthTx := false + for _, event := range events { + if event.Type == evm.TypeUrlEventTxLog { + logs, err := backend.ParseTxLogsFromEvent(event) + s.Require().NoError(err) + if len(expectedTxLogs) > 0 { + s.Require().GreaterOrEqual(len(logs), len(expectedTxLogs)) + s.assertTxLogsMatch(expectedTxLogs, logs, txInfo) + } else { + s.Require().Nil(logs) + } + } + if event.Type == evm.TypeUrlEventEthereumTx { + foundEthTx = true + if !expectedEthTx { + s.Fail("unexpected EventEthereumTx event for non-eth tx, %s", txInfo) + } + ethereumTx, err := evm.EventEthereumTxFromABCIEvent(event) + s.Require().NoError(err) + s.Require().Equal(fmt.Sprintf("%d", txIndex), ethereumTx.Index) + } + } + if expectedEthTx && !foundEthTx { + s.Fail("expected EventEthereumTx event not found, %s", txInfo) + } +} + +// assertTxLogsMatch checks that actual tx logs include the expected logs +func (s *BackendSuite) assertTxLogsMatch( + expectedLogs []*gethcore.Log, + actualLogs []*gethcore.Log, + txInfo string, +) { + for idx, expectedLog := range expectedLogs { + actualLog := actualLogs[idx] + s.Require().Equal( + expectedLog.Address, + actualLog.Address, fmt.Sprintf("log contract address mismatch, log index %d, %s", idx, txInfo), + ) + + s.Require().Equal( + len(expectedLog.Topics), + len(actualLog.Topics), + fmt.Sprintf("topics length mismatch, log index %d, %s", idx, txInfo), + ) + + for idx, topic := range expectedLog.Topics { + s.Require().Equal( + topic, + actualLog.Topics[idx], + fmt.Sprintf("topic mismatch, log index %d, %s", idx, txInfo), + ) + } + } +} diff --git a/eth/rpc/backend/utils_test.go b/eth/rpc/backend/utils_test.go index 94fc90fe4..8166ce075 100644 --- a/eth/rpc/backend/utils_test.go +++ b/eth/rpc/backend/utils_test.go @@ -4,12 +4,15 @@ import ( "fmt" "github.com/cometbft/cometbft/proto/tendermint/crypto" + gethcommon "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" "github.com/NibiruChain/nibiru/v2/eth/rpc/backend" + gethcrypto "github.com/ethereum/go-ethereum/crypto" ) func (s *BackendSuite) TestGetLogsFromBlockResults() { - blockWithTx := transferTxBlockNumber.Int64() + blockWithTx := deployContractBlockNumber.Int64() blockResults, err := s.backend.TendermintBlockResultByNumber(&blockWithTx) s.Require().NoError(err) s.Require().NotNil(blockResults) @@ -18,8 +21,17 @@ func (s *BackendSuite) TestGetLogsFromBlockResults() { s.Require().NoError(err) s.Require().NotNil(logs) - // TODO: ON: the structured event eth.evm.v1.EventTxLog is not emitted properly, so the logs are not retrieved - // Add proper checks after implementing + s.assertTxLogsMatch([]*gethcore.Log{ + { + Address: testContractAddress, + Topics: []gethcommon.Hash{ + gethcrypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")), + gethcommon.Address{}.Hash(), + s.fundedAccEthAddr.Hash(), + }, + }, + }, logs[0], "deploy contract tx") + } func (s *BackendSuite) TestGetHexProofs() { diff --git a/x/common/testutil/testnetwork/tx.go b/x/common/testutil/testnetwork/tx.go index a368c8a86..279381a2a 100644 --- a/x/common/testutil/testnetwork/tx.go +++ b/x/common/testutil/testnetwork/tx.go @@ -126,7 +126,9 @@ func (network *Network) ExecTxCmd( } func (chain *Network) BroadcastMsgs( - from sdk.AccAddress, msgs ...sdk.Msg, + from sdk.AccAddress, + accountSequence *uint64, + msgs ...sdk.Msg, ) (*sdk.TxResponse, error) { cfg := chain.Config kb, info, err := chain.keyBaseAndInfoForAddr(from) @@ -142,13 +144,18 @@ func (chain *Network) BroadcastMsgs( } txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(cfg.BondDenom, math.NewInt(1000)))) - txBuilder.SetGasLimit(uint64(1 * common.TO_MICRO)) + txBuilder.SetGasLimit(uint64(10 * common.TO_MICRO)) acc, err := cfg.AccountRetriever.GetAccount(chain.Validators[0].ClientCtx, from) if err != nil { return nil, err } - + var sequence uint64 + if accountSequence != nil { + sequence = *accountSequence + } else { + sequence = acc.GetSequence() + } txFactory := tx.Factory{} txFactory = txFactory. WithChainID(cfg.ChainID). @@ -156,7 +163,7 @@ func (chain *Network) BroadcastMsgs( WithTxConfig(cfg.TxConfig). WithAccountRetriever(cfg.AccountRetriever). WithAccountNumber(acc.GetAccountNumber()). - WithSequence(acc.GetSequence()) + WithSequence(sequence) err = tx.Sign(txFactory, info.Name, txBuilder, true) if err != nil { diff --git a/x/common/testutil/testnetwork/tx_test.go b/x/common/testutil/testnetwork/tx_test.go index 7912b8cea..267140460 100644 --- a/x/common/testutil/testnetwork/tx_test.go +++ b/x/common/testutil/testnetwork/tx_test.go @@ -16,7 +16,7 @@ func (s *TestSuite) TestSendTx() { fromAddr := s.network.Validators[0].Address toAddr := testutil.AccAddress() sendCoin := sdk.NewCoin(denoms.NIBI, math.NewInt(42)) - txResp, err := s.network.BroadcastMsgs(fromAddr, &banktypes.MsgSend{ + txResp, err := s.network.BroadcastMsgs(fromAddr, nil, &banktypes.MsgSend{ FromAddress: fromAddr.String(), ToAddress: toAddr.String(), Amount: sdk.NewCoins(sendCoin), diff --git a/x/evm/evmtest/tx.go b/x/evm/evmtest/tx.go index d68d16621..75022f62a 100644 --- a/x/evm/evmtest/tx.go +++ b/x/evm/evmtest/tx.go @@ -26,7 +26,7 @@ import ( ) // ExecuteNibiTransfer executes nibi transfer -func ExecuteNibiTransfer(deps *TestDeps, t *testing.T) *evm.MsgEthereumTx { +func ExecuteNibiTransfer(deps *TestDeps, t *testing.T) (*evm.MsgEthereumTx, *evm.MsgEthereumTxResponse) { nonce := deps.NewStateDB().GetNonce(deps.Sender.EthAddr) recipient := NewEthPrivAcc().EthAddr @@ -43,7 +43,7 @@ func ExecuteNibiTransfer(deps *TestDeps, t *testing.T) *evm.MsgEthereumTx { resp, err := deps.App.EvmKeeper.EthereumTx(sdk.WrapSDKContext(deps.Ctx), ethTxMsg) require.NoError(t, err) require.Empty(t, resp.VmError) - return ethTxMsg + return ethTxMsg, resp } type DeployContractResult struct { diff --git a/x/evm/keeper/call_contract.go b/x/evm/keeper/call_contract.go index ad4ac90c7..612dedb8f 100644 --- a/x/evm/keeper/call_contract.go +++ b/x/evm/keeper/call_contract.go @@ -137,14 +137,13 @@ func (k Keeper) CallContractWithInput( } k.ResetGasMeterAndConsumeGas(ctx, blockGasUsed) k.updateBlockBloom(ctx, evmResp, uint64(txConfig.LogIndex)) - // TODO: remove after migrating logs - //err = k.EmitLogEvents(ctx, evmResp) - //if err != nil { - // return nil, nil, errors.Wrap(err, "error emitting tx logs") - //} - // blockTxIdx := uint64(txConfig.TxIndex) + 1 - // k.EvmState.BlockTxIndex.Set(ctx, blockTxIdx) + err = k.EmitLogEvents(ctx, evmResp) + if err != nil { + return nil, nil, errors.Wrap(err, "error emitting tx logs") + } + blockTxIdx := uint64(txConfig.TxIndex) + 1 + k.EvmState.BlockTxIndex.Set(ctx, blockTxIdx) } return evmResp, evmObj, nil } diff --git a/x/evm/keeper/grpc_query_test.go b/x/evm/keeper/grpc_query_test.go index 28da63173..2b75914b2 100644 --- a/x/evm/keeper/grpc_query_test.go +++ b/x/evm/keeper/grpc_query_test.go @@ -767,7 +767,7 @@ func (s *Suite) TestTraceTx() { { name: "happy: simple nibi transfer tx", scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { - txMsg := evmtest.ExecuteNibiTransfer(deps, s.T()) + txMsg, _ := evmtest.ExecuteNibiTransfer(deps, s.T()) req = &evm.QueryTraceTxRequest{ Msg: txMsg, } @@ -843,7 +843,7 @@ func (s *Suite) TestTraceBlock() { name: "happy: simple nibi transfer tx", setup: nil, scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { - txMsg := evmtest.ExecuteNibiTransfer(deps, s.T()) + txMsg, _ := evmtest.ExecuteNibiTransfer(deps, s.T()) req = &evm.QueryTraceBlockRequest{ Txs: []*evm.MsgEthereumTx{ txMsg, diff --git a/x/oracle/keeper/app_test.go b/x/oracle/keeper/app_test.go index aac101285..69bad68c3 100644 --- a/x/oracle/keeper/app_test.go +++ b/x/oracle/keeper/app_test.go @@ -117,7 +117,7 @@ func (s *TestSuite) sendPrevotes(prices []map[asset.Pair]sdk.Dec) []string { pricesStr, err := votes.ToString() s.Require().NoError(err) - _, err = s.network.BroadcastMsgs(val.Address, &types.MsgAggregateExchangeRatePrevote{ + _, err = s.network.BroadcastMsgs(val.Address, nil, &types.MsgAggregateExchangeRatePrevote{ Hash: types.GetAggregateVoteHash("1", pricesStr, val.ValAddress).String(), Feeder: val.Address.String(), Validator: val.ValAddress.String(), @@ -133,7 +133,7 @@ func (s *TestSuite) sendPrevotes(prices []map[asset.Pair]sdk.Dec) []string { func (s *TestSuite) sendVotes(rates []string) { for i, val := range s.network.Validators { - _, err := s.network.BroadcastMsgs(val.Address, &types.MsgAggregateExchangeRateVote{ + _, err := s.network.BroadcastMsgs(val.Address, nil, &types.MsgAggregateExchangeRateVote{ Salt: "1", ExchangeRates: rates[i], Feeder: val.Address.String(), From eab54cf3128187582188427bd79dc3daea333b3c Mon Sep 17 00:00:00 2001 From: Oleg Nikonychev Date: Fri, 10 Jan 2025 22:02:12 +0400 Subject: [PATCH 2/6] chore: changelog update --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd3c85c40..3be69297c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -75,6 +75,7 @@ needed to include double quotes around the hexadecimal string. - [#2156](https://github.com/NibiruChain/nibiru/pull/2156) - test(evm-e2e): add E2E test using the Nibiru Oracle's ChainLink impl - [#2157](https://github.com/NibiruChain/nibiru/pull/2157) - fix(evm): Fix unit inconsistency related to AuthInfo.Fee and txData.Fee using effective fee - [#2160](https://github.com/NibiruChain/nibiru/pull/2160) - fix(evm-precompile): use bank.MsgServer Send in precompile IFunToken.bankMsgSend +- [#2161](https://github.com/NibiruChain/nibiru/pull/2161) - fix(evm): added tx logs events to the funtoken related txs #### Nibiru EVM | Before Audit 2 - 2024-12-06 From 7a49a1bdfdc36049c9bcb9742c4110d8769f1c64 Mon Sep 17 00:00:00 2001 From: Oleg Nikonychev Date: Fri, 10 Jan 2025 22:20:41 +0400 Subject: [PATCH 3/6] chore: lint --- eth/rpc/backend/backend_suite_test.go | 9 --------- eth/rpc/backend/tx_logs_test.go | 11 ++++++----- eth/rpc/backend/utils_test.go | 4 ++-- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/eth/rpc/backend/backend_suite_test.go b/eth/rpc/backend/backend_suite_test.go index 34b8dd88d..a7efa8665 100644 --- a/eth/rpc/backend/backend_suite_test.go +++ b/eth/rpc/backend/backend_suite_test.go @@ -194,15 +194,6 @@ func WaitForReceipt(s *BackendSuite, txHash gethcommon.Hash) (*big.Int, *gethcom } } -// getUnibiBalance returns the balance of an address in unibi -func (s *BackendSuite) getUnibiBalance(address gethcommon.Address) *big.Int { - latestBlock := rpc.EthLatestBlockNumber - latestBlockOrHash := rpc.BlockNumberOrHash{BlockNumber: &latestBlock} - balance, err := s.backend.GetBalance(address, latestBlockOrHash) - s.Require().NoError(err) - return evm.WeiToNative(balance.ToInt()) -} - // getCurrentNonce returns the current nonce of the funded account func (s *BackendSuite) getCurrentNonce(address gethcommon.Address) uint64 { nonce, err := s.backend.GetTransactionCount(address, rpc.EthPendingBlockNumber) diff --git a/eth/rpc/backend/tx_logs_test.go b/eth/rpc/backend/tx_logs_test.go index bfcfde152..e9e43a927 100644 --- a/eth/rpc/backend/tx_logs_test.go +++ b/eth/rpc/backend/tx_logs_test.go @@ -4,6 +4,12 @@ import ( "fmt" "math/big" + tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/NibiruChain/nibiru/v2/eth" "github.com/NibiruChain/nibiru/v2/eth/rpc/backend" "github.com/NibiruChain/nibiru/v2/x/common/testutil" @@ -11,11 +17,6 @@ import ( "github.com/NibiruChain/nibiru/v2/x/evm/embeds" "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" "github.com/NibiruChain/nibiru/v2/x/evm/precompile" - tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" - sdk "github.com/cosmos/cosmos-sdk/types" - gethcommon "github.com/ethereum/go-ethereum/common" - gethcore "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" ) // TestEthLogs checks that eth txs as well as funtoken txs produce tx_logs events and update tx index properly. diff --git a/eth/rpc/backend/utils_test.go b/eth/rpc/backend/utils_test.go index 8166ce075..083d24361 100644 --- a/eth/rpc/backend/utils_test.go +++ b/eth/rpc/backend/utils_test.go @@ -7,8 +7,9 @@ import ( gethcommon "github.com/ethereum/go-ethereum/common" gethcore "github.com/ethereum/go-ethereum/core/types" - "github.com/NibiruChain/nibiru/v2/eth/rpc/backend" gethcrypto "github.com/ethereum/go-ethereum/crypto" + + "github.com/NibiruChain/nibiru/v2/eth/rpc/backend" ) func (s *BackendSuite) TestGetLogsFromBlockResults() { @@ -31,7 +32,6 @@ func (s *BackendSuite) TestGetLogsFromBlockResults() { }, }, }, logs[0], "deploy contract tx") - } func (s *BackendSuite) TestGetHexProofs() { From ef106f550e882817f6027f7bda7a027c2dcaefb4 Mon Sep 17 00:00:00 2001 From: Oleg Nikonychev Date: Mon, 13 Jan 2025 19:00:39 +0400 Subject: [PATCH 4/6] chore: debug failing integration test --- eth/rpc/backend/tx_logs_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/eth/rpc/backend/tx_logs_test.go b/eth/rpc/backend/tx_logs_test.go index e9e43a927..7d15fb077 100644 --- a/eth/rpc/backend/tx_logs_test.go +++ b/eth/rpc/backend/tx_logs_test.go @@ -251,7 +251,11 @@ func (s *BackendSuite) assertTxLogsAndTxIndex( } ethereumTx, err := evm.EventEthereumTxFromABCIEvent(event) s.Require().NoError(err) - s.Require().Equal(fmt.Sprintf("%d", txIndex), ethereumTx.Index) + s.Require().Equal( + fmt.Sprintf("%d", txIndex), + ethereumTx.Index, + "tx index mismatch, %s", txInfo, + ) } } if expectedEthTx && !foundEthTx { From b5d5595b6e23a9d2487017c4a96481d8ea9d5887 Mon Sep 17 00:00:00 2001 From: Oleg Nikonychev Date: Tue, 14 Jan 2025 13:19:36 +0400 Subject: [PATCH 5/6] fix: removed tx index updating for non eth txs --- eth/rpc/backend/tx_logs_test.go | 10 ++++++++-- x/evm/keeper/call_contract.go | 2 -- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/eth/rpc/backend/tx_logs_test.go b/eth/rpc/backend/tx_logs_test.go index 7d15fb077..262db1ff9 100644 --- a/eth/rpc/backend/tx_logs_test.go +++ b/eth/rpc/backend/tx_logs_test.go @@ -203,6 +203,7 @@ func (s *BackendSuite) TestLogs() { s.Require().NoError(err) s.Require().NotNil(blockRes) txIndex := 0 + ethTxIndex := 0 for idx, check := range checks { if txIndex+1 > len(blockRes.TxsResults) { blockNumber++ @@ -210,12 +211,16 @@ func (s *BackendSuite) TestLogs() { s.Fail("TX %d not found in block results", idx) } txIndex = 0 + ethTxIndex = 0 blockRes, err = s.backend.TendermintBlockResultByNumber(&blockNumber) s.Require().NoError(err) s.Require().NotNil(blockRes) } - s.assertTxLogsAndTxIndex(blockRes, txIndex, check.logs, check.expectEthTx, check.txInfo) + s.assertTxLogsAndTxIndex(blockRes, txIndex, ethTxIndex, check.logs, check.expectEthTx, check.txInfo) txIndex++ + if check.expectEthTx { + ethTxIndex++ + } } } @@ -223,6 +228,7 @@ func (s *BackendSuite) TestLogs() { func (s *BackendSuite) assertTxLogsAndTxIndex( blockRes *tmrpctypes.ResultBlockResults, txIndex int, + ethTxIndex int, expectedTxLogs []*gethcore.Log, expectedEthTx bool, txInfo string, @@ -252,7 +258,7 @@ func (s *BackendSuite) assertTxLogsAndTxIndex( ethereumTx, err := evm.EventEthereumTxFromABCIEvent(event) s.Require().NoError(err) s.Require().Equal( - fmt.Sprintf("%d", txIndex), + fmt.Sprintf("%d", ethTxIndex), ethereumTx.Index, "tx index mismatch, %s", txInfo, ) diff --git a/x/evm/keeper/call_contract.go b/x/evm/keeper/call_contract.go index 612dedb8f..d6508eef3 100644 --- a/x/evm/keeper/call_contract.go +++ b/x/evm/keeper/call_contract.go @@ -142,8 +142,6 @@ func (k Keeper) CallContractWithInput( if err != nil { return nil, nil, errors.Wrap(err, "error emitting tx logs") } - blockTxIdx := uint64(txConfig.TxIndex) + 1 - k.EvmState.BlockTxIndex.Set(ctx, blockTxIdx) } return evmResp, evmObj, nil } From 16f47a419b7504c08a158e3ae2f77d035bc13420 Mon Sep 17 00:00:00 2001 From: Oleg Nikonychev Date: Mon, 20 Jan 2025 17:52:44 +0400 Subject: [PATCH 6/6] fix: tests --- eth/rpc/backend/gas_used_test.go | 18 +++++++----------- eth/rpc/backend/tx_logs_test.go | 2 +- x/evm/keeper/call_contract.go | 2 +- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/eth/rpc/backend/gas_used_test.go b/eth/rpc/backend/gas_used_test.go index 0ad3ed0c3..8362d599f 100644 --- a/eth/rpc/backend/gas_used_test.go +++ b/eth/rpc/backend/gas_used_test.go @@ -76,10 +76,10 @@ func (s *BackendSuite) TestGasUsedFunTokens() { erc20Addr, err := eth.NewEIP55AddrFromStr(testContractAddress.String()) s.Require().NoError(err) - _, err = s.backend.GetTransactionCount(s.fundedAccEthAddr, rpc.EthPendingBlockNumber) - s.Require().NoError(err) + nonce := s.getCurrentNonce(s.node.EthAddress) + balanceBefore := s.getUnibiBalance(s.fundedAccEthAddr) - txResp, err := s.network.BroadcastMsgs(s.node.Address, &evm.MsgCreateFunToken{ + txResp, err := s.network.BroadcastMsgs(s.node.Address, &nonce, &evm.MsgCreateFunToken{ Sender: s.node.Address.String(), FromErc20: &erc20Addr, }) @@ -96,15 +96,11 @@ func (s *BackendSuite) TestGasUsedFunTokens() { ) s.Require().NoError(err) - nonce, err := s.backend.GetTransactionCount(s.fundedAccEthAddr, rpc.EthPendingBlockNumber) - s.Require().NoError(err) - - balanceBefore := s.getUnibiBalance(s.fundedAccEthAddr) - + nonce = s.getCurrentNonce(s.fundedAccEthAddr) txHash1 := SendTransaction( s, &gethcore.LegacyTx{ - Nonce: uint64(*nonce), + Nonce: nonce, To: &precompile.PrecompileAddr_FunToken, Data: packedArgsPass, Gas: 1_500_000, @@ -123,7 +119,7 @@ func (s *BackendSuite) TestGasUsedFunTokens() { txHash2 := SendTransaction( // should fail due to invalid recipient address s, &gethcore.LegacyTx{ - Nonce: uint64(*nonce + 1), + Nonce: nonce + 1, To: &precompile.PrecompileAddr_FunToken, Data: packedArgsFail, Gas: 1_500_000, @@ -134,7 +130,7 @@ func (s *BackendSuite) TestGasUsedFunTokens() { txHash3 := SendTransaction( s, &gethcore.LegacyTx{ - Nonce: uint64(*nonce + 2), + Nonce: nonce + 2, To: &precompile.PrecompileAddr_FunToken, Data: packedArgsPass, Gas: 1_500_000, diff --git a/eth/rpc/backend/tx_logs_test.go b/eth/rpc/backend/tx_logs_test.go index 262db1ff9..6bd1132cf 100644 --- a/eth/rpc/backend/tx_logs_test.go +++ b/eth/rpc/backend/tx_logs_test.go @@ -60,7 +60,7 @@ func (s *BackendSuite) TestLogs() { s.T().Log("TX4: Create FunToken from unibi coin") nonce++ - erc20FromCoinAddr := crypto.CreateAddress(evm.EVM_MODULE_ADDRESS, s.getCurrentNonce(evm.EVM_MODULE_ADDRESS)) + erc20FromCoinAddr := crypto.CreateAddress(evm.EVM_MODULE_ADDRESS, s.getCurrentNonce(evm.EVM_MODULE_ADDRESS)+1) txResp, err = s.network.BroadcastMsgs(s.node.Address, &nonce, &evm.MsgCreateFunToken{ Sender: s.node.Address.String(), diff --git a/x/evm/keeper/call_contract.go b/x/evm/keeper/call_contract.go index 143bf2574..f9fdc2c8b 100644 --- a/x/evm/keeper/call_contract.go +++ b/x/evm/keeper/call_contract.go @@ -87,7 +87,7 @@ func (k Keeper) CallContractWithInput( err = k.EmitLogEvents(ctx, evmResp) if err != nil { - return nil, nil, errors.Wrap(err, "error emitting tx logs") + return nil, errors.Wrap(err, "error emitting tx logs") } } return evmResp, nil