diff --git a/CHANGELOG.md b/CHANGELOG.md index 555fa5992..42f040fdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -119,6 +119,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#2023](https://github.com/NibiruChain/nibiru/pull/2023) - fix(evm)!: adjusted generation and parsing of the block bloom events - [#2030](https://github.com/NibiruChain/nibiru/pull/2030) - refactor(eth/rpc): Delete unused code and improve logging in the eth and debug namespaces - [#2031](https://github.com/NibiruChain/nibiru/pull/2031) - fix(evm): debug calls with custom tracer and tracer options +- [#2037](https://github.com/NibiruChain/nibiru/pull/2037) - test(evm-rpc-backend): Add testnetwork tests using a node with a real RPC backend - [#2039](https://github.com/NibiruChain/nibiru/pull/2039) - refactor(rpc-backend): remove unnecessary interface code - [#2039](https://github.com/NibiruChain/nibiru/pull/2039) - refactor(rpc-backend): Remove mocks from eth/rpc/backend, partially completing [nibiru#2037](https://github.com/NibiruChain/nibiru/issue/2037). diff --git a/app/server/config/server_config.go b/app/server/config/server_config.go index b72638ed2..aa001c8d8 100644 --- a/app/server/config/server_config.go +++ b/app/server/config/server_config.go @@ -52,9 +52,6 @@ const ( // DefaultEVMTracer is the default vm.Tracer type DefaultEVMTracer = "" - // DefaultFixRevertGasRefundHeight is the default height at which to overwrite gas refund - DefaultFixRevertGasRefundHeight = 0 - // DefaultMaxTxGasWanted is the default gas wanted for each eth tx returned in ante handler in check tx mode DefaultMaxTxGasWanted = 0 @@ -160,8 +157,6 @@ type JSONRPCConfig struct { EnableIndexer bool `mapstructure:"enable-indexer"` // MetricsAddress defines the metrics server to listen on MetricsAddress string `mapstructure:"metrics-address"` - // FixRevertGasRefundHeight defines the upgrade height for fix of revert gas refund logic when transaction reverted - FixRevertGasRefundHeight int64 `mapstructure:"fix-revert-gas-refund-height"` } // TLSConfig defines the certificate and matching private key for the server. @@ -254,24 +249,23 @@ func GetAPINamespaces() []string { // DefaultJSONRPCConfig returns an EVM config with the JSON-RPC API enabled by default func DefaultJSONRPCConfig() *JSONRPCConfig { return &JSONRPCConfig{ - Enable: false, - API: GetDefaultAPINamespaces(), - Address: DefaultJSONRPCAddress, - WsAddress: DefaultJSONRPCWsAddress, - GasCap: DefaultEthCallGasLimit, - EVMTimeout: DefaultEVMTimeout, - TxFeeCap: DefaultTxFeeCap, - FilterCap: DefaultFilterCap, - FeeHistoryCap: DefaultFeeHistoryCap, - BlockRangeCap: DefaultBlockRangeCap, - LogsCap: DefaultLogsCap, - HTTPTimeout: DefaultHTTPTimeout, - HTTPIdleTimeout: DefaultHTTPIdleTimeout, - AllowUnprotectedTxs: DefaultAllowUnprotectedTxs, - MaxOpenConnections: DefaultMaxOpenConnections, - EnableIndexer: false, - MetricsAddress: DefaultJSONRPCMetricsAddress, - FixRevertGasRefundHeight: DefaultFixRevertGasRefundHeight, + Enable: false, + API: GetDefaultAPINamespaces(), + Address: DefaultJSONRPCAddress, + WsAddress: DefaultJSONRPCWsAddress, + GasCap: DefaultEthCallGasLimit, + EVMTimeout: DefaultEVMTimeout, + TxFeeCap: DefaultTxFeeCap, + FilterCap: DefaultFilterCap, + FeeHistoryCap: DefaultFeeHistoryCap, + BlockRangeCap: DefaultBlockRangeCap, + LogsCap: DefaultLogsCap, + HTTPTimeout: DefaultHTTPTimeout, + HTTPIdleTimeout: DefaultHTTPIdleTimeout, + AllowUnprotectedTxs: DefaultAllowUnprotectedTxs, + MaxOpenConnections: DefaultMaxOpenConnections, + EnableIndexer: false, + MetricsAddress: DefaultJSONRPCMetricsAddress, } } @@ -478,9 +472,6 @@ enable-indexer = {{ .JSONRPC.EnableIndexer }} # Prometheus metrics path: /debug/metrics/prometheus metrics-address = "{{ .JSONRPC.MetricsAddress }}" -# Upgrade height for fix of revert gas refund logic when transaction reverted. -fix-revert-gas-refund-height = {{ .JSONRPC.FixRevertGasRefundHeight }} - ############################################################################### ### TLS Configuration ### ############################################################################### diff --git a/eth/rpc/backend/tx_info.go b/eth/rpc/backend/tx_info.go index 9783ca747..347edaf16 100644 --- a/eth/rpc/backend/tx_info.go +++ b/eth/rpc/backend/tx_info.go @@ -127,17 +127,22 @@ func (b *Backend) getTransactionByHashPending(txHash gethcommon.Hash) (*rpc.EthT return nil, nil } -// GetGasUsed returns gasUsed from transaction, patching to -// price * gas in the event the tx is reverted. -func (b *Backend) GetGasUsed(res *eth.TxResult, price *big.Int, gas uint64) uint64 { - if res.Failed && res.Height < b.cfg.JSONRPC.FixRevertGasRefundHeight { - return new(big.Int).Mul(price, new(big.Int).SetUint64(gas)).Uint64() - } - return res.GasUsed +// TransactionReceipt represents the results of a transaction. TransactionReceipt +// is an extension of gethcore.Receipt, the response type for the +// "eth_getTransactionReceipt" JSON-RPC method. +// Reason being, the gethcore.Receipt struct has an incorrect JSON struct tag on one +// field and doesn't marshal JSON as expected, so we embed and extend it here. +type TransactionReceipt struct { + gethcore.Receipt + + ContractAddress *gethcommon.Address `json:"contractAddress,omitempty"` + From gethcommon.Address + To *gethcommon.Address + EffectiveGasPrice *big.Int } // GetTransactionReceipt returns the transaction receipt identified by hash. -func (b *Backend) GetTransactionReceipt(hash gethcommon.Hash) (map[string]interface{}, error) { +func (b *Backend) GetTransactionReceipt(hash gethcommon.Hash) (*TransactionReceipt, error) { hexTx := hash.Hex() b.logger.Debug("eth_getTransactionReceipt", "hash", hexTx) @@ -175,12 +180,11 @@ func (b *Backend) GetTransactionReceipt(hash gethcommon.Hash) (map[string]interf } cumulativeGasUsed += res.CumulativeGasUsed - var status hexutil.Uint + var status uint64 = gethcore.ReceiptStatusSuccessful if res.Failed { - status = hexutil.Uint(gethcore.ReceiptStatusFailed) - } else { - status = hexutil.Uint(gethcore.ReceiptStatusSuccessful) + status = gethcore.ReceiptStatusFailed } + chainID, err := b.ChainID() if err != nil { return nil, err @@ -213,40 +217,38 @@ func (b *Backend) GetTransactionReceipt(hash gethcommon.Hash) (map[string]interf return nil, errors.New("can't find index of ethereum tx") } - // TODO: refactor(evm-rpc-backend): Replace interface with gethcore.Receipt - // in eth_getTransactionReceipt - receipt := map[string]interface{}{ - // Consensus fields: These fields are defined by the Yellow Paper - "status": status, - "cumulativeGasUsed": hexutil.Uint64(cumulativeGasUsed), - "logsBloom": gethcore.BytesToBloom(gethcore.LogsBloom(logs)), - "logs": logs, + receipt := TransactionReceipt{ + Receipt: gethcore.Receipt{ + Type: ethMsg.AsTransaction().Type(), - // Implementation fields: These fields are added by geth when processing a transaction. - // They are stored in the chain database. - "transactionHash": hash, - "contractAddress": nil, - "gasUsed": hexutil.Uint64(b.GetGasUsed(res, txData.GetGasPrice(), txData.GetGas())), + // Consensus fields: These fields are defined by the Etheruem Yellow Paper + Status: status, + CumulativeGasUsed: cumulativeGasUsed, + Bloom: gethcore.BytesToBloom(gethcore.LogsBloom(logs)), + Logs: logs, - // Inclusion information: These fields provide information about the inclusion of the - // transaction corresponding to this receipt. - "blockHash": gethcommon.BytesToHash(resBlock.Block.Header.Hash()).Hex(), - "blockNumber": hexutil.Uint64(res.Height), - "transactionIndex": hexutil.Uint64(res.EthTxIndex), + // Implementation fields: These fields are added by geth when processing a transaction. + // They are stored in the chain database. + TxHash: hash, + GasUsed: res.GasUsed, - // sender and receiver (contract or EOA) addreses - "from": from, - "to": txData.GetTo(), - "type": hexutil.Uint(ethMsg.AsTransaction().Type()), + BlockHash: gethcommon.BytesToHash(resBlock.Block.Header.Hash()), + BlockNumber: big.NewInt(res.Height), + TransactionIndex: uint(res.EthTxIndex), + }, + ContractAddress: nil, + From: from, + To: txData.GetTo(), } if logs == nil { - receipt["logs"] = [][]*gethcore.Log{} + receipt.Logs = []*gethcore.Log{} } // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation if txData.GetTo() == nil { - receipt["contractAddress"] = crypto.CreateAddress(from, txData.GetNonce()) + addr := crypto.CreateAddress(from, txData.GetNonce()) + receipt.ContractAddress = &addr } if dynamicTx, ok := txData.(*evm.DynamicFeeTx); ok { @@ -255,11 +257,10 @@ func (b *Backend) GetTransactionReceipt(hash gethcommon.Hash) (map[string]interf // tolerate the error for pruned node. b.logger.Error("fetch basefee failed, node is pruned?", "height", res.Height, "error", err) } else { - receipt["effectiveGasPrice"] = hexutil.Big(*dynamicTx.EffectiveGasPriceWei(baseFee)) + receipt.EffectiveGasPrice = dynamicTx.EffectiveGasPriceWei(baseFee) } } - - return receipt, nil + return &receipt, nil } // GetTransactionByBlockHashAndIndex returns the transaction identified by hash and index. diff --git a/eth/rpc/backend/tx_info_test.go b/eth/rpc/backend/tx_info_test.go index d203b2bc4..3d14d60bd 100644 --- a/eth/rpc/backend/tx_info_test.go +++ b/eth/rpc/backend/tx_info_test.go @@ -1,12 +1,16 @@ package backend_test import ( + "encoding/json" "math/big" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + gethcore "github.com/ethereum/go-ethereum/core/types" "github.com/NibiruChain/nibiru/v2/eth/rpc" + "github.com/NibiruChain/nibiru/v2/eth/rpc/backend" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" ) func (s *BackendSuite) TestGetTransactionByHash() { @@ -73,13 +77,13 @@ func (s *BackendSuite) TestGetTransactionReceipt() { s.Require().NotNil(receipt) // Check fields - // s.Equal(s.fundedAccEthAddr, receipt.From) - // s.Equal(&recipient, receipt.To) - // s.Greater(receipt.GasUsed, uint64(0)) - // s.Equal(receipt.GasUsed, receipt.CumulativeGasUsed) - // s.Equal(tc.txHash, receipt.TxHash) - // s.Nil(receipt.ContractAddress) - // s.Require().Equal(gethcore.ReceiptStatusSuccessful, receipt.Status) + s.Equal(s.fundedAccEthAddr, receipt.From) + s.Equal(&recipient, receipt.To) + s.Greater(receipt.GasUsed, uint64(0)) + s.Equal(receipt.GasUsed, receipt.CumulativeGasUsed) + s.Equal(tc.txHash, receipt.TxHash) + s.Nil(receipt.ContractAddress) + s.Require().Equal(gethcore.ReceiptStatusSuccessful, receipt.Status) }) } } @@ -177,3 +181,38 @@ func AssertTxResults(s *BackendSuite, tx *rpc.EthTxJsonRPC, expectedTxHash gethc s.Require().Equal(expectedTxHash, tx.Hash) s.Require().Equal(uint64(1), uint64(*tx.TransactionIndex)) } + +func (s *BackendSuite) TestReceiptMarshalJson() { + toAddr := evmtest.NewEthPrivAcc().EthAddr + tr := backend.TransactionReceipt{ + Receipt: gethcore.Receipt{ + Type: 0, + PostState: []byte{}, + Status: 0, + CumulativeGasUsed: 0, + Bloom: [256]byte{}, + Logs: []*gethcore.Log{}, + TxHash: [32]byte{}, + ContractAddress: [20]byte{}, + GasUsed: 0, + BlockHash: [32]byte{}, + BlockNumber: &big.Int{}, + TransactionIndex: 0, + }, + ContractAddress: nil, + From: evmtest.NewEthPrivAcc().EthAddr, + To: &toAddr, + EffectiveGasPrice: big.NewInt(1), + } + + jsonBz, err := json.Marshal(tr) + s.Require().NoError(err) + + gethReceipt := new(gethcore.Receipt) + err = json.Unmarshal(jsonBz, gethReceipt) + s.Require().NoError(err) + + receipt := new(backend.TransactionReceipt) + err = json.Unmarshal(jsonBz, receipt) + s.Require().NoError(err) +} diff --git a/eth/rpc/backend/utils.go b/eth/rpc/backend/utils.go index 499bf107e..bab9f7778 100644 --- a/eth/rpc/backend/utils.go +++ b/eth/rpc/backend/utils.go @@ -214,7 +214,7 @@ func AllTxLogsFromEvents(events []abci.Event) ([][]*gethcore.Log, error) { continue } - logs, err := ParseTxLogsFromEvent(event) + logs, err := ParseTxLogsFromABCIEvent(event) if err != nil { return nil, err } @@ -225,7 +225,9 @@ func AllTxLogsFromEvents(events []abci.Event) ([][]*gethcore.Log, error) { } // TxLogsFromEvents parses ethereum logs from cosmos events for specific msg index -func TxLogsFromEvents(events []abci.Event, msgIndex int) ([]*gethcore.Log, error) { +func TxLogsFromEvents( + events []abci.Event, msgIndex int, +) (txLogsForMsgIdx []*gethcore.Log, err error) { for _, event := range events { if event.Type != evm.EventTypeTxLog { continue @@ -237,13 +239,14 @@ func TxLogsFromEvents(events []abci.Event, msgIndex int) ([]*gethcore.Log, error continue } - return ParseTxLogsFromEvent(event) + return ParseTxLogsFromABCIEvent(event) } return nil, fmt.Errorf("eth tx logs not found for message index %d", msgIndex) } -// ParseTxLogsFromEvent parse tx logs from one event -func ParseTxLogsFromEvent(event abci.Event) ([]*gethcore.Log, error) { +// ParseTxLogsFromABCIEvent is similar to the [ParseTxLogsFromEvent] function, +// except it's used for the legacy ABCI event for transaction logs. +func ParseTxLogsFromABCIEvent(event abci.Event) ([]*gethcore.Log, error) { logs := make([]*evm.Log, 0, len(event.Attributes)) for _, attr := range event.Attributes { if attr.Key != evm.AttributeKeyTxLog { @@ -254,12 +257,24 @@ func ParseTxLogsFromEvent(event abci.Event) ([]*gethcore.Log, error) { if err := json.Unmarshal([]byte(attr.Value), &log); err != nil { return nil, err } - logs = append(logs, &log) } return evm.LogsToEthereum(logs), nil } +// ParseTxLogsFromEvent parse tx logs from one event +func ParseTxLogsFromEvent(event *evm.EventTxLog) ([]*gethcore.Log, error) { + evmTxLogs := []*gethcore.Log{} + for _, txLogStr := range event.TxLogs { + txLog := new(evm.Log) + if err := json.Unmarshal([]byte(txLogStr), txLog); err != nil { + return nil, err + } + evmTxLogs = append(evmTxLogs, txLog.ToEthereum()) + } + return evmTxLogs, nil +} + // ShouldIgnoreGasUsed returns true if the gasUsed in result should be ignored // workaround for issue: https://github.com/cosmos/cosmos-sdk/issues/10832 func ShouldIgnoreGasUsed(res *abci.ResponseDeliverTx) bool { diff --git a/eth/rpc/rpcapi/eth_api.go b/eth/rpc/rpcapi/eth_api.go index 9695832df..980ba4903 100644 --- a/eth/rpc/rpcapi/eth_api.go +++ b/eth/rpc/rpcapi/eth_api.go @@ -43,7 +43,7 @@ type IEthAPI interface { // it is a user or a smart contract. GetTransactionByHash(hash common.Hash) (*rpc.EthTxJsonRPC, error) GetTransactionCount(address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Uint64, error) - GetTransactionReceipt(hash common.Hash) (map[string]interface{}, error) + GetTransactionReceipt(hash common.Hash) (*backend.TransactionReceipt, error) GetTransactionByBlockHashAndIndex(hash common.Hash, idx hexutil.Uint) (*rpc.EthTxJsonRPC, error) GetTransactionByBlockNumberAndIndex(blockNum rpc.BlockNumber, idx hexutil.Uint) (*rpc.EthTxJsonRPC, error) // eth_getBlockReceipts @@ -192,7 +192,7 @@ func (e *EthAPI) GetTransactionCount( // GetTransactionReceipt returns the transaction receipt identified by hash. func (e *EthAPI) GetTransactionReceipt( hash common.Hash, -) (map[string]interface{}, error) { +) (*backend.TransactionReceipt, error) { hexTx := hash.Hex() e.logger.Debug("eth_getTransactionReceipt", "hash", hexTx) return e.backend.GetTransactionReceipt(hash) diff --git a/eth/rpc/rpcapi/eth_api_test.go b/eth/rpc/rpcapi/eth_api_test.go index 54e992462..74bc331ed 100644 --- a/eth/rpc/rpcapi/eth_api_test.go +++ b/eth/rpc/rpcapi/eth_api_test.go @@ -19,6 +19,8 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/params" + cmtcore "github.com/cometbft/cometbft/rpc/core/types" + "github.com/NibiruChain/nibiru/v2/app/appconst" "github.com/NibiruChain/nibiru/v2/eth" "github.com/NibiruChain/nibiru/v2/eth/rpc/rpcapi" @@ -45,7 +47,7 @@ type NodeSuite struct { suite.Suite cfg testnetwork.Config network *testnetwork.Network - val *testnetwork.Validator + node *testnetwork.Validator ethClient *ethclient.Client ethAPI *rpcapi.EthAPI @@ -55,8 +57,17 @@ type NodeSuite struct { fundedAccNibiAddr sdk.AccAddress contractData embeds.CompiledEvmContract + txHistory []*sdk.TxResponse + // txBlockHistory: Block results from successful EVM transactions + txBlockHistory []*cmtcore.ResultBlockResults } +// TODO: UD-DEBUG: Consider if necessary +// type SuccessfulTx struct { +// EthTxHash gethcommon.Hash +// BlockHeight int64 +// } + func TestSuite_RunAll(t *testing.T) { suite.Run(t, new(Suite)) suite.Run(t, new(NodeSuite)) @@ -75,9 +86,9 @@ func (s *NodeSuite) SetupSuite() { s.Require().NoError(err) s.network = network - s.val = network.Validators[0] - s.ethClient = s.val.JSONRPCClient - s.ethAPI = s.val.EthRPC_ETH + s.node = network.Validators[0] + s.ethClient = s.node.JSONRPCClient + s.ethAPI = s.node.EthRPC_ETH s.contractData = embeds.SmartContract_TestERC20 testAccPrivateKey, _ := crypto.GenerateKey() @@ -85,11 +96,12 @@ func (s *NodeSuite) SetupSuite() { s.fundedAccEthAddr = crypto.PubkeyToAddress(testAccPrivateKey.PublicKey) s.fundedAccNibiAddr = eth.EthAddrToNibiruAddr(s.fundedAccEthAddr) - funds := sdk.NewCoins(sdk.NewInt64Coin(eth.EthBaseDenom, 100_000_000)) // 10 NIBI - _, err = testnetwork.FillWalletFromValidator( - s.fundedAccNibiAddr, funds, s.val, eth.EthBaseDenom, + funds := sdk.NewCoins(sdk.NewInt64Coin(eth.EthBaseDenom, 100_000_000)) + txResp, err := testnetwork.FillWalletFromValidator( + s.fundedAccNibiAddr, funds, s.node, eth.EthBaseDenom, ) s.Require().NoError(err) + s.txHistory = append(s.txHistory, txResp) s.NoError(s.network.WaitForNextBlock()) } @@ -143,7 +155,10 @@ func (s *NodeSuite) Test_StorageAt() { context.Background(), s.fundedAccEthAddr, gethcommon.Hash{}, nil, ) s.NoError(err) - // TODO: add more checks + // TODO: Test more behavior. + // Above, there's a check for empty contract code returned at a non-contract + // account. We should add a check for the presence of code for an acocunt + // that is known to be a deployed contract. s.NotNil(storage) } @@ -154,7 +169,7 @@ func (s *NodeSuite) Test_PendingStorageAt() { ) s.NoError(err) - // TODO: add more checks + // TODO: Test more behavior - Non-empty case needed s.NotNil(storage) } @@ -163,7 +178,7 @@ func (s *NodeSuite) Test_CodeAt() { code, err := s.ethClient.CodeAt(context.Background(), s.fundedAccEthAddr, nil) s.NoError(err) - // TODO: add more checks + // TODO: Test more behavior - Non-empty case needed s.NotNil(code) } @@ -172,7 +187,7 @@ func (s *NodeSuite) Test_PendingCodeAt() { code, err := s.ethClient.PendingCodeAt(context.Background(), s.fundedAccEthAddr) s.NoError(err) - // TODO: add more checks + // TODO: Test more behavior - Non-empty case needed s.NotNil(code) } @@ -222,11 +237,12 @@ func (s *NodeSuite) Test_SimpleTransferTransaction() { s.T().Log("make sure the sender has enough funds") weiToSend := evm.NativeToWei(big.NewInt(1)) // 1 unibi funds := sdk.NewCoins(sdk.NewInt64Coin(eth.EthBaseDenom, 5_000_000)) // 5 * 10^6 unibi - _, err = testnetwork.FillWalletFromValidator( + txResp, err := testnetwork.FillWalletFromValidator( s.fundedAccNibiAddr, funds, s.network.Validators[0], eth.EthBaseDenom, ) s.Require().NoError(err) s.NoError(s.network.WaitForNextBlock()) + s.txHistory = append(s.txHistory, txResp) senderBalanceBeforeWei, err := s.ethClient.BalanceAt( context.Background(), s.fundedAccEthAddr, nil, @@ -265,17 +281,11 @@ func (s *NodeSuite) Test_SimpleTransferTransaction() { s.Require().NoError(err) s.NoError(s.network.WaitForNextBlock()) - s.NoError(s.network.WaitForNextBlock()) - s.NoError(s.network.WaitForNextBlock()) - - txReceipt, err := s.ethClient.TransactionReceipt(blankCtx, tx.Hash()) - s.NoError(err) - s.T().Log("Assert event expectations - successful eth tx") { - blockHeightOfTx := int64(txReceipt.BlockNumber.Uint64()) - blockOfTx, err := s.val.RPCClient.BlockResults(blankCtx, &blockHeightOfTx) - s.NoError(err) + blockOfTx, err := s.node.BlockByEthTx(tx.Hash()) + s.Require().NoError(err) + s.txBlockHistory = append(s.txBlockHistory, blockOfTx) ethTxEvents := []sdk.Event{} events := blockOfTx.TxsResults[0].Events for _, event := range events { @@ -291,6 +301,7 @@ func (s *NodeSuite) Test_SimpleTransferTransaction() { hash0, _ := ethTxEvents[0].GetAttribute(evm.AttributeKeyEthereumTxHash) hash1, _ := ethTxEvents[1].GetAttribute(evm.AttributeKeyEthereumTxHash) s.Require().Equal(hash0, hash1) + _ = s.network.WaitForNextBlock() } s.T().Log("Assert balances") @@ -321,11 +332,12 @@ func (s *NodeSuite) Test_SmartContract() { s.T().Log("Make sure the account has funds.") funds := sdk.NewCoins(sdk.NewInt64Coin(eth.EthBaseDenom, 1_000_000_000)) - _, err = testnetwork.FillWalletFromValidator( + txResp, err := testnetwork.FillWalletFromValidator( s.fundedAccNibiAddr, funds, s.network.Validators[0], eth.EthBaseDenom, ) s.Require().NoError(err) s.NoError(s.network.WaitForNextBlock()) + s.txHistory = append(s.txHistory, txResp) grpcUrl := s.network.Validators[0].AppConfig.GRPC.Address grpcConn, err := gosdk.GetGRPCConnection(grpcUrl, true, 5) @@ -390,7 +402,7 @@ func (s *NodeSuite) Test_SmartContract() { { weiToSend := evm.NativeToWei(big.NewInt(1)) // 1 unibi s.T().Logf("Sending %d wei (sanity check)", weiToSend) - accResp, err := s.val.EthRpcQueryClient.QueryClient.EthAccount(blankCtx, + accResp, err := s.node.EthRpcQueryClient.QueryClient.EthAccount(blankCtx, &evm.QueryEthAccountRequest{ Address: s.fundedAccEthAddr.Hex(), }) diff --git a/eth/rpc/rpcapi/net_api_test.go b/eth/rpc/rpcapi/net_api_test.go index 6b54f5094..3730b199d 100644 --- a/eth/rpc/rpcapi/net_api_test.go +++ b/eth/rpc/rpcapi/net_api_test.go @@ -5,9 +5,9 @@ import ( ) func (s *NodeSuite) TestNetNamespace() { - api := s.val.EthRpc_NET + api := s.node.EthRpc_NET s.Require().True(api.Listening()) s.EqualValues( - appconst.GetEthChainID(s.val.ClientCtx.ChainID).String(), api.Version()) + appconst.GetEthChainID(s.node.ClientCtx.ChainID).String(), api.Version()) s.Equal(0, api.PeerCount()) } diff --git a/x/evm/events.go b/x/evm/events.go index 3bf3dad0a..ef1a2c748 100644 --- a/x/evm/events.go +++ b/x/evm/events.go @@ -1,12 +1,23 @@ // Copyright (c) 2023-2024 Nibi, Inc. package evm +import ( + "cosmossdk.io/errors" + abci "github.com/cometbft/cometbft/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + // Evm module events const ( EventTypeEthereumTx = TypeMsgEthereumTx - EventTypeBlockBloom = "block_bloom" EventTypeTxLog = "tx_log" + // proto.MessageName(new(evm.EventBlockBloom)) + TypeUrlEventBlockBloom = "eth.evm.v1.EventBlockBloom" + + // proto.MessageName(new(evm.EventTxLog)) + TypeUrlEventTxLog = "eth.evm.v1.EventTxLog" + AttributeKeyRecipient = "recipient" AttributeKeyTxHash = "txHash" AttributeKeyEthereumTxHash = "ethereumTxHash" @@ -16,6 +27,36 @@ const ( AttributeKeyTxLog = "txLog" // tx failed in eth vm execution AttributeKeyEthereumTxFailed = "ethereumTxFailed" - AttributeValueCategory = ModuleName - AttributeKeyEthereumBloom = "bloom" + // JSON name of EventBlockBloom.Bloom + AttributeKeyEthereumBloom = "bloom" ) + +func (e *EventTxLog) FromABCIEvent(event abci.Event) (*EventTxLog, error) { + typeUrl := TypeUrlEventTxLog + typedProtoEvent, err := sdk.ParseTypedEvent(event) + if err != nil { + return nil, errors.Wrapf( + err, "EventTxLog.FromABCIEvent failed to parse event of type %s", typeUrl) + } + typedEvent, ok := (typedProtoEvent).(*EventTxLog) + if !ok { + return nil, errors.Wrapf( + err, "EventTxLog.FromABCIEvent failed to parse event of type %s", typeUrl) + } + return typedEvent, nil +} + +func (e *EventBlockBloom) FromABCIEvent(event abci.Event) (*EventBlockBloom, error) { + typeUrl := TypeUrlEventBlockBloom + typedProtoEvent, err := sdk.ParseTypedEvent(event) + if err != nil { + return nil, errors.Wrapf( + err, "failed to parse event of type %s", typeUrl) + } + typedEvent, ok := (typedProtoEvent).(*EventBlockBloom) + if !ok { + return nil, errors.Wrapf( + err, "failed to parse event of type %s", typeUrl) + } + return typedEvent, nil +} diff --git a/x/evm/events_test.go b/x/evm/events_test.go new file mode 100644 index 000000000..27d3c0890 --- /dev/null +++ b/x/evm/events_test.go @@ -0,0 +1,34 @@ +package evm_test + +import ( + abci "github.com/cometbft/cometbft/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +func (s *Suite) TestFromABCIEvent() { + for _, tc := range []struct { + name string + test func() + }{ + { + name: "EventTxLog - happy", + test: func() { + typed := &evm.EventTxLog{ + TxLogs: []string{}, + } + + e, err := sdk.TypedEventToEvent(typed) + s.NoError(err) + + gotTyped, err := new(evm.EventTxLog).FromABCIEvent(abci.Event(e)) + s.NoError(err) + + s.EqualValues(typed, gotTyped) + }, + }, + } { + s.Run(tc.name, tc.test) + } +} diff --git a/x/evm/keeper/msg_server.go b/x/evm/keeper/msg_server.go index 10fdfb131..b0001eada 100644 --- a/x/evm/keeper/msg_server.go +++ b/x/evm/keeper/msg_server.go @@ -88,13 +88,19 @@ func (k *Keeper) EthereumTx( evm.EventTypeTxLog, txLogAttrs..., ), - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, evm.AttributeValueCategory), - sdk.NewAttribute(sdk.AttributeKeySender, msg.From), - sdk.NewAttribute(evm.AttributeKeyTxType, fmt.Sprintf("%d", tx.Type())), - ), }) + _ = ctx.EventManager().EmitTypedEvents( + // TODO: migrate legacy event from above to typed event. + // https://github.com/NibiruChain/nibiru/issues/2034 + // &evm.EventTxLog{ + // TxLogs: txLogs, + // }, + &evm.EventMessage{ + Module: evm.ModuleName, + Sender: msg.From, + TxType: fmt.Sprintf("%d", tx.Type()), + }, + ) return resp, nil }