Skip to content

Commit

Permalink
Merge branch 'main' into solidity-hygiene
Browse files Browse the repository at this point in the history
  • Loading branch information
Unique-Divine committed Jan 16, 2025
2 parents 503ea58 + 8731a9e commit 1745531
Show file tree
Hide file tree
Showing 10 changed files with 256 additions and 47 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#2127](https://github.com/NibiruChain/nibiru/pull/2127) - fix(vesting): disabled built in auth/vesting module functionality
- [#2129](https://github.com/NibiruChain/nibiru/pull/2129) - fix(evm): issue with infinite recursion in erc20 funtoken contracts
- [#2130](https://github.com/NibiruChain/nibiru/pull/2130) - fix(evm): proper nonce management in statedb
- [#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
- [#2139](https://github.com/NibiruChain/nibiru/pull/2139) - fix(evm): erc20 born funtoken: properly burn bank coins after converting coin back to erc20
- [#2140](https://github.com/NibiruChain/nibiru/pull/2140) - fix(bank): bank keeper extension now charges gas for the bank operations
Expand Down
32 changes: 21 additions & 11 deletions eth/rpc/backend/backend_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"crypto/ecdsa"
"fmt"
"math/big"
"sync"
"testing"
"time"

Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
188 changes: 188 additions & 0 deletions eth/rpc/backend/gas_used_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package backend_test

import (
"math/big"

"github.com/ethereum/go-ethereum/common/hexutil"
gethcore "github.com/ethereum/go-ethereum/core/types"

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

// 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() {
// Test is broadcasting txs. Lock to avoid nonce conflicts.
testMutex.Lock()
defer testMutex.Unlock()

// 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,
balanceBefore.Uint64()-balanceAfter.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() {
// Test is broadcasting txs. Lock to avoid nonce conflicts.
testMutex.Lock()
defer testMutex.Unlock()

// Create funtoken from erc20
erc20Addr, err := eth.NewEIP55AddrFromStr(testContractAddress.String())
s.Require().NoError(err)

_, err = s.backend.GetTransactionCount(s.fundedAccEthAddr, rpc.EthPendingBlockNumber)
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,
balanceBefore.Uint64()-balanceAfter.Uint64(),
)
}
6 changes: 5 additions & 1 deletion eth/rpc/backend/nonce_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
}
Expand Down
9 changes: 0 additions & 9 deletions x/evm/keeper/call_contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,11 @@ func (k Keeper) CallContractWithInput(
ctx, evmMsg, evm.NewNoOpTracer(), commit, evmCfg, txConfig, true,
)
if err != nil {
// We don't know the actual gas used, so consuming the gas limit
k.ResetGasMeterAndConsumeGas(ctx, gasLimit)
err = errors.Wrap(err, "failed to apply ethereum core message")
return
}

if evmResp.Failed() {
k.ResetGasMeterAndConsumeGas(ctx, evmResp.GasUsed)
if strings.Contains(evmResp.VmError, vm.ErrOutOfGas.Error()) {
err = fmt.Errorf("gas required exceeds allowance (%d)", gasLimit)
return
Expand All @@ -130,12 +127,6 @@ func (k Keeper) CallContractWithInput(

// Success, update block gas used and bloom filter
if commit {
blockGasUsed, err := k.AddToBlockGasUsed(ctx, evmResp.GasUsed)
if err != nil {
k.ResetGasMeterAndConsumeGas(ctx, ctx.GasMeter().Limit())
return nil, nil, errors.Wrap(err, "error adding transient gas used to block")
}
k.ResetGasMeterAndConsumeGas(ctx, blockGasUsed)
k.updateBlockBloom(ctx, evmResp, uint64(txConfig.LogIndex))
// TODO: remove after migrating logs
//err = k.EmitLogEvents(ctx, evmResp)
Expand Down
8 changes: 7 additions & 1 deletion x/evm/keeper/funtoken_from_coin.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,18 @@ func (k *Keeper) deployERC20ForBankCoin(
bytecodeForCall := append(embeds.SmartContract_ERC20Minter.Bytecode, packedArgs...)

// nil address for contract creation
_, _, err = k.CallContractWithInput(
evmResp, _, err := k.CallContractWithInput(
ctx, evm.EVM_MODULE_ADDRESS, nil, true, bytecodeForCall, Erc20GasLimitDeploy,
)
if err != nil {
k.ResetGasMeterAndConsumeGas(ctx, ctx.GasMeter().Limit())
return gethcommon.Address{}, errors.Wrap(err, "failed to deploy ERC20 contract")
}
blockGasUsed, errBlockGasUsed := k.AddToBlockGasUsed(ctx, evmResp.GasUsed)
if errBlockGasUsed != nil {
return gethcommon.Address{}, errors.Wrap(errBlockGasUsed, "error adding transient gas used")
}
k.ResetGasMeterAndConsumeGas(ctx, blockGasUsed)

return erc20Addr, nil
}
2 changes: 2 additions & 0 deletions x/evm/keeper/funtoken_from_coin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,8 @@ func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() {
s.Require().NoError(err)
s.Require().Equal("0", balance.String())

deps.ResetGasMeter()

s.T().Log("sad: Convert more erc-20 to back to bank coin, insufficient funds")
_, err = deps.EvmKeeper.CallContract(
deps.Ctx,
Expand Down
3 changes: 2 additions & 1 deletion x/evm/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func (k *Keeper) AddToBlockGasUsed(
) (blockGasUsed uint64, err error) {
// Either k.EvmState.BlockGasUsed.GetOr() or k.EvmState.BlockGasUsed.Set()
// also consume gas and could panic.
defer HandleOutOfGasPanic(&err, "")
defer HandleOutOfGasPanic(&err, "")()

blockGasUsed = k.EvmState.BlockGasUsed.GetOr(ctx, 0) + gasUsed
if blockGasUsed < gasUsed {
Expand Down Expand Up @@ -147,6 +147,7 @@ func HandleOutOfGasPanic(err *error, format string) func() {
if r := recover(); r != nil {
switch r.(type) {
case sdk.ErrorOutOfGas:

*err = vm.ErrOutOfGas
default:
panic(r)
Expand Down
Loading

0 comments on commit 1745531

Please sign in to comment.