Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem: trace response for failed tx is not aligned with go-ethereum #441

Merged
merged 3 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
- (ante) [#422](https://github.com/crypto-org-chain/ethermint/pull/422) vendor `NewDeductFeeDecorator` to re-use the custom `checkTxFeeWithValidatorMinGasPrices` method, so it'll repsect the `DefaultPriorityReduction` config.
- (feemarket) [#433](https://github.com/crypto-org-chain/ethermint/pull/433) Fix sdk int conversion panic with baseFee.
* (rpc) [#434](https://github.com/crypto-org-chain/ethermint/pull/434) No need gasPrice when patch gasUsed for `eth_getTransactionReceipt`.
* (rpc) [#439](https://github.com/crypto-org-chain/ethermint/pull/439) Align trace response for failed tx with go-ethereum.
* (rpc) [#439](https://github.com/crypto-org-chain/ethermint/pull/439), [#441](https://github.com/crypto-org-chain/ethermint/pull/441) Align trace response for failed tx with go-ethereum.

### Features

Expand Down
125 changes: 74 additions & 51 deletions tests/integration_tests/test_estimate_gas.py
Original file line number Diff line number Diff line change
@@ -1,53 +1,76 @@
import json
from concurrent.futures import ThreadPoolExecutor, as_completed

import pytest

from .network import setup_ethermint
from .utils import ADDRS, CONTRACTS, deploy_contract


@pytest.fixture(scope="module")
def custom_ethermint(tmp_path_factory):
path = tmp_path_factory.mktemp("estimate-gas")
yield from setup_ethermint(path, 27010, long_timeout_commit=True)


@pytest.fixture(scope="module", params=["ethermint", "geth"])
def cluster(request, custom_ethermint, geth):
"""
run on both ethermint and geth
"""
provider = request.param
if provider == "ethermint":
yield custom_ethermint
elif provider == "geth":
yield geth
else:
raise NotImplementedError


def test_revert(cluster):
w3 = cluster.w3
call = w3.provider.make_request
validator = ADDRS["validator"]
erc20, _ = deploy_contract(
w3,
CONTRACTS["TestRevert"],
)
method = "eth_estimateGas"

def do_call(data):
params = {"from": validator, "to": erc20.address, "data": data}
return call(method, [params])["error"]

# revertWithMsg
error = do_call("0x9ffb86a5")
assert error["code"] == 3
assert error["message"] == "execution reverted: Function has been reverted"
assert (
error["data"]
== "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001a46756e6374696f6e20686173206265656e207265766572746564000000000000" # noqa: E501
)

# revertWithoutMsg
error = do_call("0x3246485d")
assert error["code"] == -32000
assert error["message"] == "execution reverted"
from .utils import CONTRACTS, create_contract_transaction, deploy_contract

METHOD = "eth_estimateGas"


pytestmark = pytest.mark.filter


def test_revert(ethermint, geth):
def process(w3):
contract, _ = deploy_contract(w3, CONTRACTS["TestRevert"])
res = []
call = w3.provider.make_request
# revertWithoutMsg
data = "0x9ffb86a5"
params = {"to": contract.address, "data": data}
rsp = call(METHOD, [params])
error = rsp["error"]
assert error["code"] == 3
assert error["message"] == "execution reverted: Function has been reverted"
assert (
error["data"]
== "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001a46756e6374696f6e20686173206265656e207265766572746564000000000000" # noqa: E501
)
res = [json.dumps(error, sort_keys=True)]
return res

providers = [ethermint.w3, geth.w3]
with ThreadPoolExecutor(len(providers)) as exec:
tasks = [exec.submit(process, w3) for w3 in providers]
res = [future.result() for future in as_completed(tasks)]
assert len(res) == len(providers)
assert res[0] == res[-1], res


def test_out_of_gas_error(ethermint, geth):
iterations = 1
gas = 21204

def process(w3):
contract, _ = deploy_contract(w3, CONTRACTS["TestMessageCall"])
tx = contract.functions.test(iterations).build_transaction()
tx = {"to": contract.address, "data": tx["data"], "gas": hex(gas)}
call = w3.provider.make_request
error = call(METHOD, [tx])["error"]
assert error["code"] == -32000
assert f"gas required exceeds allowance ({gas})" in error["message"]

providers = [ethermint.w3, geth.w3]
with ThreadPoolExecutor(len(providers)) as exec:
tasks = [exec.submit(process, w3) for w3 in providers]
res = [future.result() for future in as_completed(tasks)]
assert len(res) == len(providers)


def test_storage_out_of_gas_error(ethermint, geth):
gas = 210000

def process(w3):
tx = create_contract_transaction(w3, CONTRACTS["TestMessageCall"])
tx = {"data": tx["data"], "gas": hex(gas)}
call = w3.provider.make_request
error = call(METHOD, [tx])["error"]
assert error["code"] == -32000
assert "contract creation code storage out of gas" in error["message"]

providers = [ethermint.w3, geth.w3]
with ThreadPoolExecutor(len(providers)) as exec:
tasks = [exec.submit(process, w3) for w3 in providers]
res = [future.result() for future in as_completed(tasks)]
assert len(res) == len(providers)
40 changes: 32 additions & 8 deletions tests/integration_tests/test_tracers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from .utils import (
ADDRS,
CONTRACTS,
create_contract_transaction,
deploy_contract,
derive_new_account,
derive_random_account,
Expand All @@ -24,10 +25,9 @@
)


def test_trace_error(ethermint, geth):
def test_out_of_gas_error(ethermint, geth):
method = "debug_traceTransaction"
tracer = {"tracer": "callTracer"}
tracers = [[tracer]]
iterations = 1
acc = derive_random_account()

Expand All @@ -39,12 +39,36 @@ def process(w3):
tx_hash = send_transaction(w3, tx)["transactionHash"].hex()
res = []
call = w3.provider.make_request
with ThreadPoolExecutor(len(tracers)) as exec:
params = [([tx_hash] + cfg) for cfg in tracers]
exec_map = exec.map(call, itertools.repeat(method), params)
for resp in exec_map:
assert "out of gas" in resp["result"]["error"], resp
res = [json.dumps(resp["result"], sort_keys=True)]
resp = call(method, [tx_hash, tracer])
assert "out of gas" in resp["result"]["error"], resp
res = [json.dumps(resp["result"], sort_keys=True)]
return res

providers = [ethermint.w3, geth.w3]
with ThreadPoolExecutor(len(providers)) as exec:
tasks = [exec.submit(process, w3) for w3 in providers]
res = [future.result() for future in as_completed(tasks)]
assert len(res) == len(providers)
assert res[0] == res[-1], res


def test_storage_out_of_gas_error(ethermint, geth):
method = "debug_traceTransaction"
tracer = {"tracer": "callTracer"}
acc = derive_new_account(8)

def process(w3):
# fund new sender to deploy contract with same address
fund_acc(w3, acc)
tx = create_contract_transaction(w3, CONTRACTS["TestMessageCall"], key=acc.key)
tx["gas"] = 210000
tx_hash = send_transaction(w3, tx, key=acc.key)["transactionHash"].hex()
res = []
call = w3.provider.make_request
resp = call(method, [tx_hash, tracer])
msg = "contract creation code storage out of gas"
assert msg in resp["result"]["error"], resp
res = [json.dumps(resp["result"], sort_keys=True)]
return res

providers = [ethermint.w3, geth.w3]
Expand Down
2 changes: 1 addition & 1 deletion x/evm/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,7 @@
}

if res.VmError != "" {
if res.VmError != vm.ErrExecutionReverted.Error() && res.VmError != vm.ErrOutOfGas.Error() {
if res.VmError == vm.ErrInsufficientBalance.Error() {

Check warning on line 719 in x/evm/keeper/grpc_query.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/grpc_query.go#L719

Added line #L719 was not covered by tests
return nil, 0, status.Error(codes.Internal, res.VmError)
}
}
Expand Down
Loading