Skip to content

Commit

Permalink
Merge branch 'main' into releases/v2.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
k-yang committed Jan 20, 2025
2 parents 82302cc + 0ef4099 commit 63b2be4
Show file tree
Hide file tree
Showing 29 changed files with 2,413 additions and 52 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,12 @@ 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
- [#2162](https://github.com/NibiruChain/nibiru/pull/2162) - test(testutil): try retrying for 'panic: pebbledb: closed'
- [#2167](https://github.com/NibiruChain/nibiru/pull/2167) - refactor(evm): removed blockGasUsed transient variable
- [#2168](https://github.com/NibiruChain/nibiru/pull/2168) - chore(evm-solidity): Move unrelated docs, gen-embeds, and add Solidity docs
- [#2165](https://github.com/NibiruChain/nibiru/pull/2165) - fix(evm): use Singleton StateDB pattern for EVM txs

- [#2169](https://github.com/NibiruChain/nibiru/pull/2169) - fix(evm): Better handling erc20 metadata
#### Nibiru EVM | Before Audit 2 - 2024-12-06

The codebase went through a third-party [Code4rena
Expand Down
2 changes: 1 addition & 1 deletion eth/rpc/backend/backend_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ func (s *BackendSuite) buildContractCallTx(
nonce uint64,
gasLimit uint64,
) 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(
Expand Down
18 changes: 7 additions & 11 deletions eth/rpc/backend/gas_used_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
})
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down
299 changes: 299 additions & 0 deletions eth/rpc/backend/tx_logs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,299 @@
package backend_test

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"
"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"
)

// 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)+1)

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
ethTxIndex := 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
ethTxIndex = 0
blockRes, err = s.backend.TendermintBlockResultByNumber(&blockNumber)
s.Require().NoError(err)
s.Require().NotNil(blockRes)
}
s.assertTxLogsAndTxIndex(blockRes, txIndex, ethTxIndex, check.logs, check.expectEthTx, check.txInfo)
txIndex++
if check.expectEthTx {
ethTxIndex++
}
}
}

// assertTxLogsAndTxIndex gets tx results from the block and checks tx logs and tx index.
func (s *BackendSuite) assertTxLogsAndTxIndex(
blockRes *tmrpctypes.ResultBlockResults,
txIndex int,
ethTxIndex 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", ethTxIndex),
ethereumTx.Index,
"tx index mismatch, %s", txInfo,
)
}
}
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),
)
}
}
}
Loading

0 comments on commit 63b2be4

Please sign in to comment.