Skip to content

Commit

Permalink
test: added integration tests for gas used
Browse files Browse the repository at this point in the history
  • Loading branch information
onikonychev committed Jan 6, 2025
2 parents 5b57729 + 350b9e9 commit 62de834
Show file tree
Hide file tree
Showing 35 changed files with 1,257 additions and 165 deletions.
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lts/jod
11 changes: 9 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#2119](https://github.com/NibiruChain/nibiru/pull/2119) - fix(evm): Guarantee
that gas consumed during any send operation of the "NibiruBankKeeper" depends
only on the "bankkeeper.BaseKeeper"'s gas consumption.
- [#2120](https://github.com/NibiruChain/nibiru/pull/2120) - fix: Use canonical hexadecimal strings for Eip155 address encoding
- [#2120](https://github.com/NibiruChain/nibiru/pull/2120) - fix: Use canonical hexadecimal strings for Eip155 address encoding
- [#2122](https://github.com/NibiruChain/nibiru/pull/2122) - test(evm): more bank extension tests and EVM ABCI integration tests to prevent regressions
- [#2124](https://github.com/NibiruChain/nibiru/pull/2124) - refactor(evm):
Remove unnecessary argument in the `VerifyFee` function, which returns the token
payment required based on the effective fee from the tx data. Improve
documentation.
- [#2131](https://github.com/NibiruChain/nibiru/pull/2131) - fix(evm): proper block gas calculation in precompile calls
- [#2125](https://github.com/NibiruChain/nibiru/pull/2125) - feat(evm-precompile):Emit EVM events created to reflect the ABCI events that occur outside the EVM to make sure that block explorers and indexers can find indexed ABCI event information.
- [#2129](https://github.com/NibiruChain/nibiru/pull/2129) - fix(evm): issue with infinite recursion in erc20 funtoken contracts
- [#2132](https://github.com/NibiruChain/nibiru/pull/2132) - fix(evm): proper tx gas refund
- [#2134](https://github.com/NibiruChain/nibiru/pull/2134) - fix(evm): query of NIBI should use bank state, not the StateDB
- [#2140](https://github.com/NibiruChain/nibiru/pull/2140) - fix(bank): bank keeper extension now charges gas for the bank operations
- [#2141](https://github.com/NibiruChain/nibiru/pull/2141) - refactor: simplify account retrieval operation in `nibid q evm account`.
- [#2142](https://github.com/NibiruChain/nibiru/pull/2142) - fix(bank): add additional missing methods to the NibiruBankKeeper


#### Nibiru EVM | Before Audit 2 - 2024-12-06

Expand Down
2 changes: 1 addition & 1 deletion app/ante/fixed_gas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ func (suite *AnteTestSuite) TestOraclePostPriceTransactionsHaveFixedPrice() {
Amount: sdk.NewCoins(sdk.NewInt64Coin(appconst.BondDenom, 200)),
},
},
expectedGas: 38175,
expectedGas: 67193,
expectedErr: nil,
},
}
Expand Down
10 changes: 5 additions & 5 deletions contrib/bashlib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,26 @@ export COLOR_BRIGHT_WHITE="\033[97m"

# log_debug: Simple wrapper for `echo` with a DEBUG prefix.
log_debug() {
echo "${COLOR_CYAN}DEBUG${COLOR_RESET}" "$@"
echo -e "${COLOR_CYAN}DEBUG${COLOR_RESET}" "$@"
}

# log_error: ERROR messages in red, output to stderr.
log_error() {
echo "${COLOR_RED}ERROR:${COLOR_RESET}" "$@" >&2
echo -e "${COLOR_RED}ERROR:${COLOR_RESET}" "$@" >&2
}

log_success() {
echo "${COLOR_GREEN}✅ SUCCESS:${COLOR_RESET}" "$@"
echo -e "${COLOR_GREEN}✅ SUCCESS:${COLOR_RESET}" "$@"
}

# log_warning: WARNING messages represent non-critical issues that might not
# require immediate action but should be noted as points of concern or failure.
log_warning() {
echo "${COLOR_YELLOW}WARNING${COLOR_RESET}" "$@" >&2
echo -e "${COLOR_YELLOW}WARNING${COLOR_RESET}" "$@" >&2
}

log_info() {
echo "${COLOR_MAGENTA}INFO${COLOR_RESET}" "$@"
echo -e "${COLOR_MAGENTA}INFO${COLOR_RESET}" "$@"
}

# —————————————————————————————————————————————————
Expand Down
34 changes: 28 additions & 6 deletions eth/rpc/backend/backend_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import (
"testing"
"time"

"github.com/cosmos/cosmos-sdk/client/flags"
sdk "github.com/cosmos/cosmos-sdk/types"
gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
gethcore "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
Expand Down Expand Up @@ -94,7 +96,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)
Expand All @@ -103,7 +105,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)
Expand Down Expand Up @@ -167,24 +169,44 @@ func SendTransaction(s *BackendSuite, tx *gethcore.LegacyTx, waitForNextBlock bo
return txHash
}

func WaitForReceipt(s *BackendSuite, txHash gethcommon.Hash) (*big.Int, *gethcommon.Hash) {
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)
}
}
}

// broadcastSDKTx broadcasts the given SDK transaction and returns the response
func (s *BackendSuite) broadcastSDKTx(sdkTx sdk.Tx) *sdk.TxResponse {

Check failure on line 195 in eth/rpc/backend/backend_suite_test.go

View workflow job for this annotation

GitHub Actions / lint

func `(*BackendSuite).broadcastSDKTx` is unused (unused)
txBytes, err := s.backend.ClientCtx().TxConfig.TxEncoder()(sdkTx)
s.Require().NoError(err)

syncCtx := s.backend.ClientCtx().WithBroadcastMode(flags.BroadcastSync)
rsp, err := syncCtx.BroadcastTx(txBytes)
s.Require().NoError(err)
return rsp
}

// broadcastSDKTx broadcasts the given SDK transaction and returns the response
func (s *BackendSuite) getUnibiBalance(address gethcommon.Address) *hexutil.Big {
latestBlock := rpc.EthLatestBlockNumber
latestBlockOrHash := rpc.BlockNumberOrHash{BlockNumber: &latestBlock}
balance, err := s.backend.GetBalance(s.fundedAccEthAddr, latestBlockOrHash)
s.Require().NoError(err)
return balance
}
5 changes: 5 additions & 0 deletions eth/rpc/backend/call_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"math/big"

errorsmod "cosmossdk.io/errors"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -338,3 +339,7 @@ func (b *Backend) GasPrice() (*hexutil.Big, error) {

return (*hexutil.Big)(result), nil
}

func (b *Backend) ClientCtx() client.Context {
return b.clientCtx
}
179 changes: 179 additions & 0 deletions eth/rpc/backend/gas_used_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
package backend_test

import (
"math/big"

"github.com/NibiruChain/nibiru/v2/eth"
"github.com/NibiruChain/nibiru/v2/eth/rpc"
"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"
"github.com/ethereum/go-ethereum/common/hexutil"
gethcore "github.com/ethereum/go-ethereum/core/types"
)

// TestGasUsedTransfers verifies that gas used is correctly calculated for simple transfers.
// Test creates 2 eth transfer txs that are supposed to be included in the same block.
// It checks that gas used is the same for both txs and the total block gas is greater than the sum of 2 gas used.
func (s *BackendSuite) TestGasUsedTransfers() {
// Start with new block
s.Require().NoError(s.network.WaitForNextBlock())
balanceBefore := s.getUnibiBalance(s.fundedAccEthAddr)

// Send 2 similar transfers
randomEthAddr := evmtest.NewEthPrivAcc().EthAddr
txHash1 := s.SendNibiViaEthTransfer(randomEthAddr, amountToSend, false)
txHash2 := s.SendNibiViaEthTransfer(randomEthAddr, amountToSend, false)

blockNumber1, _, receipt1 := WaitForReceipt(s, txHash1)
blockNumber2, _, receipt2 := WaitForReceipt(s, txHash2)

s.Require().NotNil(receipt1)
s.Require().NotNil(receipt2)

s.Require().Equal(gethcore.ReceiptStatusSuccessful, receipt1.Status)
s.Require().Equal(gethcore.ReceiptStatusSuccessful, receipt2.Status)

// Expect txs are included into one block
s.Require().Equal(blockNumber1, blockNumber2)

// Ensure that gas used is the same for both transactions
s.Require().Equal(receipt1.GasUsed, receipt2.GasUsed)

// Get block receipt and check gas used
block, err := s.backend.GetBlockByNumber(rpc.NewBlockNumber(blockNumber1), false)
s.Require().NoError(err)
s.Require().NotNil(block)
s.Require().NotNil(block["gasUsed"])
s.Require().GreaterOrEqual(block["gasUsed"].(*hexutil.Big).ToInt().Uint64(), receipt1.GasUsed+receipt2.GasUsed)

// Balance after should be equal to balance before minus gas used and amount sent
balanceAfter := s.getUnibiBalance(s.fundedAccEthAddr)
s.Require().Equal(
receipt1.GasUsed+receipt2.GasUsed+2,
evm.WeiToNative(balanceBefore.ToInt()).Uint64()-evm.WeiToNative(balanceAfter.ToInt()).Uint64(),
)
}

// TestGasUsedFunTokens verifies that gas used is correctly calculated for precompile "sendToBank" txs.
// Test creates 3 txs: 2 successful and one failing.
// Successful txs gas should be refunded and failing tx should consume 100% of the gas limit.
// It also checks that txs are included in the same block and block gas is greater or equals
// to the total gas used by txs.
func (s *BackendSuite) TestGasUsedFunTokens() {
// Create funtoken from erc20
erc20Addr, err := eth.NewEIP55AddrFromStr(testContractAddress.String())
s.Require().NoError(err)

nonce, err := s.backend.GetTransactionCount(s.fundedAccEthAddr, rpc.EthPendingBlockNumber)

Check failure on line 70 in eth/rpc/backend/gas_used_test.go

View workflow job for this annotation

GitHub Actions / lint

ineffectual assignment to nonce (ineffassign)
s.Require().NoError(err)

txResp, err := s.network.BroadcastMsgs(s.node.Address, &evm.MsgCreateFunToken{
Sender: s.node.Address.String(),
FromErc20: &erc20Addr,
})
s.Require().NoError(err)
s.Require().NotNil(txResp)
s.Require().NoError(s.network.WaitForNextBlock())

randomNibiAddress := testutil.AccAddress()
packedArgsPass, err := embeds.SmartContract_FunToken.ABI.Pack(
"sendToBank",
erc20Addr.Address,
big.NewInt(1),
randomNibiAddress.String(),
)
s.Require().NoError(err)

nonce, err = s.backend.GetTransactionCount(s.fundedAccEthAddr, rpc.EthPendingBlockNumber)
s.Require().NoError(err)

balanceBefore := s.getUnibiBalance(s.fundedAccEthAddr)

txHash1 := SendTransaction(
s,
&gethcore.LegacyTx{
Nonce: uint64(*nonce),
To: &precompile.PrecompileAddr_FunToken,
Data: packedArgsPass,
Gas: 1_500_000,
GasPrice: big.NewInt(1),
},
false,
)

packedArgsFail, err := embeds.SmartContract_FunToken.ABI.Pack(
"sendToBank",
erc20Addr.Address,
big.NewInt(1),
"invalidAddress",
)
s.Require().NoError(err)
txHash2 := SendTransaction( // should fail due to invalid recipient address
s,
&gethcore.LegacyTx{
Nonce: uint64(*nonce + 1),
To: &precompile.PrecompileAddr_FunToken,
Data: packedArgsFail,
Gas: 1_500_000,
GasPrice: big.NewInt(1),
},
false,
)
txHash3 := SendTransaction(
s,
&gethcore.LegacyTx{
Nonce: uint64(*nonce + 2),
To: &precompile.PrecompileAddr_FunToken,
Data: packedArgsPass,
Gas: 1_500_000,
GasPrice: big.NewInt(1),
},
false,
)
blockNumber1, _, receipt1 := WaitForReceipt(s, txHash1)
blockNumber2, _, receipt2 := WaitForReceipt(s, txHash2)
blockNumber3, _, receipt3 := WaitForReceipt(s, txHash3)

s.Require().NotNil(receipt1)
s.Require().NotNil(receipt2)
s.Require().NotNil(receipt3)

s.Require().NotNil(blockNumber1)
s.Require().NotNil(blockNumber2)
s.Require().NotNil(blockNumber3)

// TXs should have been included in the same block
s.Require().Equal(blockNumber1, blockNumber2)
s.Require().Equal(blockNumber2, blockNumber3)

// 1 and 3 should pass and 2 should fail
s.Require().Equal(gethcore.ReceiptStatusSuccessful, receipt1.Status)
s.Require().Equal(gethcore.ReceiptStatusFailed, receipt2.Status)
s.Require().Equal(gethcore.ReceiptStatusSuccessful, receipt3.Status)

// TX 1 and 3 should have gas used lower than specified gas limit
s.Require().Less(receipt1.GasUsed, uint64(500_000))
s.Require().Less(receipt3.GasUsed, uint64(500_000))

// TX 2 should have gas used equal to specified gas limit as it failed
s.Require().Equal(uint64(1_500_000), receipt2.GasUsed)

block, err := s.backend.GetBlockByNumber(rpc.NewBlockNumber(blockNumber1), false)
s.Require().NoError(err)
s.Require().NotNil(block)
s.Require().NotNil(block["gasUsed"])
s.Require().GreaterOrEqual(
block["gasUsed"].(*hexutil.Big).ToInt().Uint64(),
receipt1.GasUsed+receipt2.GasUsed+receipt3.GasUsed,
)

// Balance after should be equal to balance before minus gas used
balanceAfter := s.getUnibiBalance(s.fundedAccEthAddr)
s.Require().Equal(
receipt1.GasUsed+receipt2.GasUsed+receipt3.GasUsed,
evm.WeiToNative(balanceBefore.ToInt()).Uint64()-evm.WeiToNative(balanceAfter.ToInt()).Uint64(),
)
}
24 changes: 18 additions & 6 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,29 @@ build:
clean-cache:
go clean -cache -testcache -modcache

alias b := build

# Generate protobuf code (Golang) for Nibiru
# Generate protobuf-based types in Golang
proto-gen:
#!/usr/bin/env bash
make proto-gen
alias proto := proto-gen
# Generate Solidity artifacts for x/evm/embeds
gen-embeds:
#!/usr/bin/env bash
source contrib/bashlib.sh
embeds_dir="x/evm/embeds"
log_info "Begin to compile Solidity in $embeds_dir"
which_ok npm
log_info "Using system node version: $(npm exec -- node -v)"

cd "$embeds_dir" || (log_error "path $embeds_dir not found" && exit)
npx hardhat compile
log_success "Compiled Solidity in $embeds_dir"

alias gen-proto := proto-gen

# Build protobuf types (Rust)
proto-rs:
# Generate protobuf-based types in Rust
gen-proto-rs:
bash proto/buf.gen.rs.sh

lint:
Expand Down
Loading

0 comments on commit 62de834

Please sign in to comment.