Skip to content

Commit 13c71a7

Browse files
k-yangcoderabbitai[bot]Unique-Divine
authored
fix(evm): ensure only one copy of StateDB when executing Ethereum txs (#2165)
* cleanup * cleanup * partial refactor * fix: compilation errors * refactor: remove CallContract completely * refactor: mintOrUnescrowERC20 * refactor: use simpler evmObj creation pattern * fix: empty gas price in gethcore Message * fix: contract address collision error * fix: TestPrecompileSelfCallRevert * fix: unnecessary stateDB creation * fix: ensure stateDB singleton * fix: balance check * fix: balance transfer check had wrong direction * fix: deploy contract args * fix: use a new StateDB before CallContractWithInput * Update CHANGELOG.md * fix: clear StateDB between txs * Update account_info_test.go * Update account_info_test.go * Update app/evmante/evmante_can_transfer.go Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix base fee wei * refactor: funtoken tests * refactor: simplify NewEVM() object creation in tests * fix: stateDB should not commit on ERC20 calls * fix: commit after minting funtokens * fix: erc20 burn was committing when it shouldn't be * fix: linter --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Unique-Divine <[email protected]>
1 parent f3ca671 commit 13c71a7

38 files changed

+1801
-1726
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ needed to include double quotes around the hexadecimal string.
7979
- [#2162](https://github.com/NibiruChain/nibiru/pull/2162) - test(testutil): try retrying for 'panic: pebbledb: closed'
8080
- [#2167](https://github.com/NibiruChain/nibiru/pull/2167) - refactor(evm): removed blockGasUsed transient variable
8181
- [#2168](https://github.com/NibiruChain/nibiru/pull/2168) - chore(evm-solidity): Move unrelated docs, gen-embeds, and add Solidity docs
82+
- [#2165](https://github.com/NibiruChain/nibiru/pull/2165) - fix(evm): use Singleton StateDB pattern for EVM txs
8283

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

@@ -346,7 +347,6 @@ Nibiru v1.3.0 adds interchain accounts.
346347

347348
- [#1859](https://github.com/NibiruChain/nibiru/pull/1859) - refactor(oracle): add oracle slashing events
348349

349-
350350
---
351351

352352
[LEGACY CHANGELOG](./LEGACY-CHANGELOG.md)

app/evmante/evmante_can_transfer.go

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@ import (
77
"cosmossdk.io/errors"
88
sdk "github.com/cosmos/cosmos-sdk/types"
99
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
10-
gethcommon "github.com/ethereum/go-ethereum/common"
1110
gethcore "github.com/ethereum/go-ethereum/core/types"
1211

12+
"github.com/NibiruChain/nibiru/v2/eth"
1313
"github.com/NibiruChain/nibiru/v2/x/evm"
14-
"github.com/NibiruChain/nibiru/v2/x/evm/statedb"
1514
)
1615

1716
// CanTransferDecorator checks if the sender is allowed to transfer funds according to the EVM block
@@ -25,7 +24,6 @@ type CanTransferDecorator struct {
2524
func (ctd CanTransferDecorator) AnteHandle(
2625
ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler,
2726
) (sdk.Context, error) {
28-
params := ctd.GetParams(ctx)
2927
ethCfg := evm.EthereumConfig(ctd.EVMKeeper.EthChainID(ctx))
3028
signer := gethcore.MakeSigner(ethCfg, big.NewInt(ctx.BlockHeight()))
3129

@@ -40,7 +38,7 @@ func (ctd CanTransferDecorator) AnteHandle(
4038

4139
baseFeeWeiPerGas := evm.NativeToWei(ctd.EVMKeeper.BaseFeeMicronibiPerGas(ctx))
4240

43-
coreMsg, err := msgEthTx.AsMessage(signer, baseFeeWeiPerGas)
41+
evmMsg, err := msgEthTx.AsMessage(signer, baseFeeWeiPerGas)
4442
if err != nil {
4543
return ctx, errors.Wrapf(
4644
err,
@@ -59,37 +57,27 @@ func (ctd CanTransferDecorator) AnteHandle(
5957
return ctx, errors.Wrapf(
6058
sdkerrors.ErrInsufficientFee,
6159
"gas fee cap (wei) less than block base fee (wei); (%s < %s)",
62-
coreMsg.GasFeeCap(), baseFeeWeiPerGas,
60+
evmMsg.GasFeeCap(), baseFeeWeiPerGas,
6361
)
6462
}
6563

66-
cfg := &statedb.EVMConfig{
67-
ChainConfig: ethCfg,
68-
Params: params,
69-
// Note that we use an empty coinbase here because the field is not
70-
// used during this Ante Handler.
71-
BlockCoinbase: gethcommon.Address{},
72-
BaseFeeWei: baseFeeWeiPerGas,
73-
}
74-
75-
stateDB := ctd.NewStateDB(
76-
ctx,
77-
statedb.NewEmptyTxConfig(gethcommon.BytesToHash(ctx.HeaderHash().Bytes())),
78-
)
79-
evmInstance := ctd.EVMKeeper.NewEVM(ctx, coreMsg, cfg, evm.NewNoOpTracer(), stateDB)
80-
8164
// check that caller has enough balance to cover asset transfer for **topmost** call
8265
// NOTE: here the gas consumed is from the context with the infinite gas meter
83-
if coreMsg.Value().Sign() > 0 &&
84-
!evmInstance.Context.CanTransfer(stateDB, coreMsg.From(), coreMsg.Value()) {
85-
balanceWei := stateDB.GetBalance(coreMsg.From())
86-
return ctx, errors.Wrapf(
87-
sdkerrors.ErrInsufficientFunds,
88-
"failed to transfer %s wei (balance=%s) from address %s using the EVM block context transfer function",
89-
coreMsg.Value(),
90-
balanceWei,
91-
coreMsg.From(),
92-
)
66+
67+
if evmMsg.Value().Sign() > 0 {
68+
nibiruAddr := eth.EthAddrToNibiruAddr(evmMsg.From())
69+
balanceNative := ctd.Bank.GetBalance(ctx, nibiruAddr, evm.EVMBankDenom).Amount.BigInt()
70+
balanceWei := evm.NativeToWei(balanceNative)
71+
72+
if balanceWei.Cmp(evmMsg.Value()) < 0 {
73+
return ctx, errors.Wrapf(
74+
sdkerrors.ErrInsufficientFunds,
75+
"failed to transfer %s wei ( balance=%s )from address %s using the EVM block context transfer function",
76+
evmMsg.Value(),
77+
balanceWei,
78+
evmMsg.From(),
79+
)
80+
}
9381
}
9482
}
9583

app/evmante/evmante_can_transfer_test.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@ import (
1414
func (s *TestSuite) TestCanTransferDecorator() {
1515
testCases := []struct {
1616
name string
17-
txSetup func(deps *evmtest.TestDeps) sdk.FeeTx
18-
ctxSetup func(deps *evmtest.TestDeps)
1917
beforeTxSetup func(deps *evmtest.TestDeps, sdb *statedb.StateDB)
18+
txSetup func(deps *evmtest.TestDeps) sdk.FeeTx
2019
wantErr string
2120
}{
2221
{
@@ -92,9 +91,6 @@ func (s *TestSuite) TestCanTransferDecorator() {
9291
anteDec := evmante.CanTransferDecorator{deps.App.AppKeepers.EvmKeeper}
9392
tx := tc.txSetup(&deps)
9493

95-
if tc.ctxSetup != nil {
96-
tc.ctxSetup(&deps)
97-
}
9894
if tc.beforeTxSetup != nil {
9995
tc.beforeTxSetup(&deps, stateDB)
10096
err := stateDB.Commit()

eth/rpc/backend/backend_suite_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func (s *BackendSuite) SetupSuite() {
102102

103103
// Send Transfer TX and use the results in the tests
104104
s.Require().NoError(err)
105-
transferTxHash = s.SendNibiViaEthTransfer(recipient, amountToSend, true)
105+
transferTxHash = s.SendNibiViaEthTransfer(recipient, amountToSend, true /*waitForNextBlock*/)
106106
blockNumber, blockHash, _ := WaitForReceipt(s, transferTxHash)
107107
s.Require().NotNil(blockNumber)
108108
s.Require().NotNil(blockHash)
@@ -151,7 +151,7 @@ func (s *BackendSuite) DeployTestContract(waitForNextBlock bool) (gethcommon.Has
151151
&gethcore.LegacyTx{
152152
Nonce: uint64(nonce),
153153
Data: bytecodeForCall,
154-
Gas: 1500_000,
154+
Gas: 1_500_000,
155155
GasPrice: big.NewInt(1),
156156
},
157157
waitForNextBlock,
@@ -177,7 +177,7 @@ func SendTransaction(s *BackendSuite, tx *gethcore.LegacyTx, waitForNextBlock bo
177177

178178
// WaitForReceipt waits for a transaction to be included in a block, returns block number, block hash and receipt
179179
func WaitForReceipt(s *BackendSuite, txHash gethcommon.Hash) (*big.Int, *gethcommon.Hash, *backend.TransactionReceipt) {
180-
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
180+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
181181
defer cancel()
182182

183183
for {

eth/rpc/backend/gas_used_test.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,6 @@ func (s *BackendSuite) TestGasUsedFunTokens() {
154154
s.Require().NotNil(blockNumber2)
155155
s.Require().NotNil(blockNumber3)
156156

157-
// TXs should have been included in the same block
158-
s.Require().Equal(blockNumber1, blockNumber2)
159-
s.Require().Equal(blockNumber2, blockNumber3)
160-
161157
// 1 and 3 should pass and 2 should fail
162158
s.Require().Equal(gethcore.ReceiptStatusSuccessful, receipt1.Status)
163159
s.Require().Equal(gethcore.ReceiptStatusFailed, receipt2.Status)

eth/rpc/backend/utils.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,9 @@ func ShouldIgnoreGasUsed(res *abci.ResponseDeliverTx) bool {
270270
}
271271

272272
// GetLogsFromBlockResults returns the list of event logs from the tendermint block result response
273-
func GetLogsFromBlockResults(blockRes *tmrpctypes.ResultBlockResults) ([][]*gethcore.Log, error) {
273+
func GetLogsFromBlockResults(
274+
blockRes *tmrpctypes.ResultBlockResults,
275+
) ([][]*gethcore.Log, error) {
274276
blockLogs := [][]*gethcore.Log{}
275277
for _, txResult := range blockRes.TxsResults {
276278
logs, err := AllTxLogsFromEvents(txResult.Events)

gosdk/gosdk_test.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,25 +73,26 @@ func (s *TestSuite) ConnectGrpc() {
7373
s.grpcConn = grpcConn
7474
}
7575

76-
func (s *TestSuite) DoTestNewQueryClient() {
77-
_, err := gosdk.NewQuerier(s.grpcConn)
78-
s.NoError(err)
79-
}
80-
8176
func (s *TestSuite) TestNewNibiruSdk() {
8277
rpcEndpt := s.val.RPCAddress
8378
nibiruSdk, err := gosdk.NewNibiruSdk(s.cfg.ChainID, s.grpcConn, rpcEndpt)
8479
s.NoError(err)
8580
s.nibiruSdk = &nibiruSdk
86-
8781
s.nibiruSdk.Keyring = s.val.ClientCtx.Keyring
8882

89-
s.Run("DoTestBroadcastMsgs", func() { s.DoTestBroadcastMsgs() })
83+
s.Run("DoTestBroadcastMsgs", func() {
84+
s.DoTestBroadcastMsgs()
85+
})
9086
s.Run("DoTestBroadcastMsgsGrpc", func() {
91-
s.NoError(s.network.WaitForNextBlock())
87+
for t := 0; t < 4; t++ {
88+
s.NoError(s.network.WaitForNextBlock())
89+
}
9290
s.DoTestBroadcastMsgsGrpc()
9391
})
94-
s.Run("DoTestNewQueryClient", s.DoTestNewQueryClient)
92+
s.Run("DoTestNewQueryClient", func() {
93+
_, err := gosdk.NewQuerier(s.grpcConn)
94+
s.NoError(err)
95+
})
9596
}
9697

9798
// FIXME: Q: What is the node home for a local validator?

x/common/testutil/testnetwork/network.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,7 @@ func (n *Network) LatestHeight() (int64, error) {
494494
// committed after a given block. If that height is not reached within a timeout,
495495
// an error is returned. Regardless, the latest height queried is returned.
496496
func (n *Network) WaitForHeight(h int64) (int64, error) {
497-
return n.WaitForHeightWithTimeout(h, 40*time.Second)
497+
return n.WaitForHeightWithTimeout(h, 5*time.Minute)
498498
}
499499

500500
// WaitForHeightWithTimeout is the same as WaitForHeight except the caller can

x/evm/evmmodule/genesis_test.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ type Suite struct {
2323

2424
// TestKeeperSuite: Runs all the tests in the suite.
2525
func TestKeeperSuite(t *testing.T) {
26-
s := new(Suite)
27-
suite.Run(t, s)
26+
suite.Run(t, new(Suite))
2827
}
2928

3029
// TestExportInitGenesis
@@ -48,21 +47,23 @@ func (s *Suite) TestExportInitGenesis() {
4847
deployResp, err := evmtest.DeployContract(&deps, erc20Contract)
4948
s.Require().NoError(err)
5049
erc20Addr := deployResp.ContractAddr
50+
51+
evmObj, _ := deps.NewEVM()
5152
totalSupply, err := deps.EvmKeeper.ERC20().LoadERC20BigInt(
52-
deps.Ctx, erc20Contract.ABI, erc20Addr, "totalSupply",
53+
deps.Ctx, evmObj, erc20Contract.ABI, erc20Addr, "totalSupply",
5354
)
5455
s.Require().NoError(err)
5556

5657
// Transfer ERC-20 tokens to user A
57-
_, _, err = deps.EvmKeeper.ERC20().Transfer(erc20Addr, fromUser, toUserA, amountToSendA, deps.Ctx)
58+
_, _, err = deps.EvmKeeper.ERC20().Transfer(erc20Addr, fromUser, toUserA, amountToSendA, deps.Ctx, evmObj)
5859
s.Require().NoError(err)
5960

6061
// Transfer ERC-20 tokens to user B
61-
_, _, err = deps.EvmKeeper.ERC20().Transfer(erc20Addr, fromUser, toUserB, amountToSendB, deps.Ctx)
62+
_, _, err = deps.EvmKeeper.ERC20().Transfer(erc20Addr, fromUser, toUserB, amountToSendB, deps.Ctx, evmObj)
6263
s.Require().NoError(err)
6364

6465
// Create fungible token from bank coin
65-
funToken := evmtest.CreateFunTokenForBankCoin(&deps, "unibi", &s.Suite)
66+
funToken := evmtest.CreateFunTokenForBankCoin(deps, "unibi", &s.Suite)
6667
s.Require().NoError(err)
6768
funTokenAddr := funToken.Erc20Addr.Address
6869

@@ -98,15 +99,15 @@ func (s *Suite) TestExportInitGenesis() {
9899
evmmodule.InitGenesis(deps.Ctx, deps.EvmKeeper, deps.App.AccountKeeper, *evmGenesisState)
99100

100101
// Verify erc20 balances for users A, B and sender
101-
balance, err := deps.EvmKeeper.ERC20().BalanceOf(erc20Addr, toUserA, deps.Ctx)
102+
balance, err := deps.EvmKeeper.ERC20().BalanceOf(erc20Addr, toUserA, deps.Ctx, evmObj)
102103
s.Require().NoError(err)
103104
s.Require().Equal(amountToSendA, balance)
104105

105-
balance, err = deps.EvmKeeper.ERC20().BalanceOf(erc20Addr, toUserB, deps.Ctx)
106+
balance, err = deps.EvmKeeper.ERC20().BalanceOf(erc20Addr, toUserB, deps.Ctx, evmObj)
106107
s.Require().NoError(err)
107108
s.Require().Equal(amountToSendB, balance)
108109

109-
balance, err = deps.EvmKeeper.ERC20().BalanceOf(erc20Addr, fromUser, deps.Ctx)
110+
balance, err = deps.EvmKeeper.ERC20().BalanceOf(erc20Addr, fromUser, deps.Ctx, evmObj)
110111
s.Require().NoError(err)
111112
s.Require().Equal(
112113
new(big.Int).Sub(totalSupply, big.NewInt(amountToSendA.Int64()+amountToSendB.Int64())),
@@ -122,7 +123,7 @@ func (s *Suite) TestExportInitGenesis() {
122123
s.Require().True(funTokens[0].IsMadeFromCoin)
123124

124125
// Check that fungible token balance of user C is correct
125-
balance, err = deps.EvmKeeper.ERC20().BalanceOf(funTokenAddr, toUserC, deps.Ctx)
126+
balance, err = deps.EvmKeeper.ERC20().BalanceOf(funTokenAddr, toUserC, deps.Ctx, evmObj)
126127
s.Require().NoError(err)
127128
s.Require().Equal(amountToSendC, balance)
128129
}

0 commit comments

Comments
 (0)