Skip to content

Commit

Permalink
chore(tests) add gas estimation cases (#1952)
Browse files Browse the repository at this point in the history
* chore(tests) add gas estimation cases

* test: add sc call to precompile

* add changelog

* fix lint issues

* update gas used exp
  • Loading branch information
GAtom22 committed Nov 3, 2023
1 parent 8582d16 commit 1d138aa
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 24 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
- (stride-outpost) [#1935](https://github.com/evmos/evmos/pull/1935) Refactor RedeemStake method.
- (osmosis-outpost) [#1921](https://github.com/evmos/evmos/pull/1921) Add Osmosis outpost types and errors.
- (distribution) [#1949](https://github.com/evmos/evmos/pull/1949) Add `ClaimRewards` custom transaction.
- (rpc) [#1952](https://github.com/evmos/evmos/pull/1952) Add tests for EVM extensions transactions gas estimation (related to changes in [#1943](https://github.com/evmos/evmos/pull/1943)).
- (distribution) [#1954](https://github.com/evmos/evmos/pull/1954) Add `ClaimRewards` unit and event tests.
- (osmosis-outpost) [#1944](https://github.com/evmos/evmos/pull/1944) Add more validation to Osmosis outpost.
- (precompiles) [#1973](https://github.com/evmos/evmos/pull/1973) Use `LoadABI` from precompiles common package in outposts.
Expand Down
44 changes: 36 additions & 8 deletions tests/nix_tests/test_gas.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import json

from .utils import (
ADDRS,
CONTRACTS,
KEYS,
build_deploy_contract_tx,
deploy_contract,
send_transaction,
w3_wait_for_new_blocks,
Expand All @@ -27,12 +30,28 @@ def test_gas_eth_tx(geth, evmos_cluster):
def test_gas_deployment(geth, evmos_cluster):
# deploy an identical contract on geth and evmos
# ensure that the gasUsed is equivalent
_, geth_contract_receipt = deploy_contract(geth.w3, CONTRACTS["TestERC20A"])
_, evmos_contract_receipt = deploy_contract(
evmos_cluster.w3, CONTRACTS["TestERC20A"]
)
info = json.loads(CONTRACTS["TestERC20A"].read_text())
geth_tx = build_deploy_contract_tx(geth.w3, info)
evmos_tx = build_deploy_contract_tx(evmos_cluster.w3, info)

# estimate tx gas
geth_gas_estimation = geth.w3.eth.estimate_gas(geth_tx)
evmos_gas_estimation = evmos_cluster.w3.eth.estimate_gas(evmos_tx)

assert geth_gas_estimation == evmos_gas_estimation

# sign and send tx
geth_contract_receipt = send_transaction(geth.w3, geth_tx)
evmos_contract_receipt = send_transaction(evmos_cluster.w3, evmos_tx)
assert geth_contract_receipt.status == 1
assert evmos_contract_receipt.status == 1

assert geth_contract_receipt.gasUsed == evmos_contract_receipt.gasUsed

# gasUsed should be same as estimation
assert geth_contract_receipt.gasUsed == geth_gas_estimation
assert evmos_contract_receipt.gasUsed == evmos_gas_estimation


def test_gas_call(geth, evmos_cluster):
function_input = 10
Expand All @@ -44,21 +63,30 @@ def test_gas_call(geth, evmos_cluster):

# call the contract and get tx receipt for geth
geth_gas_price = geth.w3.eth.gas_price
geth_txhash = geth_contract.functions.burnGas(function_input).transact(
geth_tx = geth_contract.functions.burnGas(function_input).build_transaction(
{"from": ADDRS["validator"], "gasPrice": geth_gas_price}
)
geth_call_receipt = geth.w3.eth.wait_for_transaction_receipt(geth_txhash)
geth_gas_estimation = geth.w3.eth.estimate_gas(geth_tx)
geth_call_receipt = send_transaction(geth.w3, geth_tx)

# repeat the above for evmos
evmos_gas_price = evmos_cluster.w3.eth.gas_price
evmos_txhash = evmos_contract.functions.burnGas(function_input).transact(
evmos_tx = evmos_contract.functions.burnGas(function_input).build_transaction(
{"from": ADDRS["validator"], "gasPrice": evmos_gas_price}
)
evmos_call_receipt = evmos_cluster.w3.eth.wait_for_transaction_receipt(evmos_txhash)
evmos_gas_estimation = evmos_cluster.w3.eth.estimate_gas(evmos_tx)
evmos_call_receipt = send_transaction(evmos_cluster.w3, evmos_tx)

# ensure gas estimation is the same
assert geth_gas_estimation == evmos_gas_estimation

# ensure that the gasUsed is equivalent
assert geth_call_receipt.gasUsed == evmos_call_receipt.gasUsed

# ensure gasUsed == gas estimation
assert geth_call_receipt.gasUsed == geth_gas_estimation
assert evmos_call_receipt.gasUsed == evmos_gas_estimation


def test_block_gas_limit(evmos_cluster):
tx_value = 10
Expand Down
109 changes: 96 additions & 13 deletions tests/nix_tests/test_precompiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,16 @@
import pytest

from .ibc_utils import EVMOS_IBC_DENOM, assert_ready, get_balance, prepare_network
from .utils import ADDRS, get_precompile_contract, wait_for_fn
from .network import Evmos
from .utils import (
ADDRS,
CONTRACTS,
KEYS,
deploy_contract,
get_precompile_contract,
send_transaction,
wait_for_fn,
)


@pytest.fixture(scope="module", params=["evmos", "evmos-rocksdb"])
Expand Down Expand Up @@ -37,7 +46,7 @@ def test_ibc_transfer(ibc):
pc = get_precompile_contract(ibc.chains["evmos"].w3, "ICS20I")
evmos_gas_price = ibc.chains["evmos"].w3.eth.gas_price

tx_hash = pc.functions.transfer(
tx = pc.functions.transfer(
"transfer",
"channel-0",
src_denom,
Expand All @@ -47,13 +56,21 @@ def test_ibc_transfer(ibc):
[1, 10000000000],
0,
"",
).transact({"from": ADDRS["signer2"], "gasPrice": evmos_gas_price})

receipt = ibc.chains["evmos"].w3.eth.wait_for_transaction_receipt(tx_hash)
).build_transaction(
{
"from": ADDRS["signer2"],
"gasPrice": evmos_gas_price,
}
)
gas_estimation = ibc.chains["evmos"].w3.eth.estimate_gas(tx)
receipt = send_transaction(ibc.chains["evmos"].w3, tx, KEYS["signer2"])

assert receipt.status == 1
# check gas used
assert receipt.gasUsed == 74092
assert receipt.gasUsed == 48184

# check gas estimation is accurate
assert receipt.gasUsed == gas_estimation

fee = receipt.gasUsed * evmos_gas_price

Expand Down Expand Up @@ -161,31 +178,97 @@ def test_ibc_transfer_timeout(ibc):
def test_staking(ibc):
assert_ready(ibc)

evmos: Evmos = ibc.chains["evmos"]
w3 = evmos.w3
amt = 1000000
cli = ibc.chains["evmos"].cosmos_cli()
cli = evmos.cosmos_cli()
del_addr = cli.address("signer2")
src_denom = "aevmos"
validator_addr = cli.validators()[0]["operator_address"]

old_src_balance = get_balance(ibc.chains["evmos"], del_addr, src_denom)
old_src_balance = get_balance(evmos, del_addr, src_denom)

pc = get_precompile_contract(ibc.chains["evmos"].w3, "StakingI")
evmos_gas_price = ibc.chains["evmos"].w3.eth.gas_price
pc = get_precompile_contract(w3, "StakingI")
evmos_gas_price = w3.eth.gas_price

tx_hash = pc.functions.delegate(ADDRS["signer2"], validator_addr, amt).transact(
tx = pc.functions.delegate(ADDRS["signer2"], validator_addr, amt).build_transaction(
{"from": ADDRS["signer2"], "gasPrice": evmos_gas_price}
)
gas_estimation = evmos.w3.eth.estimate_gas(tx)
receipt = send_transaction(w3, tx, KEYS["signer2"])

assert receipt.status == 1
# check gas estimation is accurate
assert receipt.gasUsed == gas_estimation

fee = receipt.gasUsed * evmos_gas_price

delegations = cli.get_delegated_amount(del_addr)["delegation_responses"]
assert len(delegations) == 1
assert delegations[0]["delegation"]["validator_address"] == validator_addr
assert int(delegations[0]["balance"]["amount"]) == amt

new_src_balance = get_balance(evmos, del_addr, src_denom)
assert old_src_balance - amt - fee == new_src_balance


def test_staking_via_sc(ibc):
assert_ready(ibc)

evmos: Evmos = ibc.chains["evmos"]
w3 = evmos.w3
amt = 1000000
cli = evmos.cosmos_cli()
del_addr = cli.address("signer1")
src_denom = "aevmos"
validator_addr = cli.validators()[0]["operator_address"]

old_src_balance = get_balance(evmos, del_addr, src_denom)

contract, receipt = deploy_contract(w3, CONTRACTS["StakingCaller"])
evmos_gas_price = w3.eth.gas_price

# create grant - need to specify gas otherwise will fail with out of gas
approve_tx = contract.functions.testApprove(
receipt.contractAddress, ["/cosmos.staking.v1beta1.MsgDelegate"], amt
).build_transaction(
{"from": ADDRS["signer1"], "gasPrice": evmos_gas_price, "gas": 60000}
)

receipt = ibc.chains["evmos"].w3.eth.wait_for_transaction_receipt(tx_hash)
gas_estimation = evmos.w3.eth.estimate_gas(approve_tx)
receipt = send_transaction(w3, approve_tx, KEYS["signer1"])

assert receipt.status == 1
# check gas estimation is accurate
print(f"gas used: {receipt.gasUsed}")
print(f"gas estimation: {gas_estimation}")
# FIXME gas estimation > than gasUsed. Should be equal
# assert receipt.gasUsed == gas_estimation

fee = receipt.gasUsed * evmos_gas_price

# delegate - need to specify gas otherwise will fail with out of gas
delegate_tx = contract.functions.testDelegate(
ADDRS["signer1"], validator_addr, amt
).build_transaction(
{"from": ADDRS["signer1"], "gasPrice": evmos_gas_price, "gas": 180000}
)
gas_estimation = evmos.w3.eth.estimate_gas(delegate_tx)
receipt = send_transaction(w3, delegate_tx, KEYS["signer1"])

assert receipt.status == 1
# check gas estimation is accurate
print(f"gas used: {receipt.gasUsed}")
print(f"gas estimation: {gas_estimation}")
# FIXME gas estimation > than gasUsed. Should be equal
# assert receipt.gasUsed == gas_estimation

fee += receipt.gasUsed * evmos_gas_price

delegations = cli.get_delegated_amount(del_addr)["delegation_responses"]
assert len(delegations) == 1
assert delegations[0]["delegation"]["validator_address"] == validator_addr
assert int(delegations[0]["balance"]["amount"]) == amt

new_src_balance = get_balance(ibc.chains["evmos"], del_addr, src_denom)
new_src_balance = get_balance(evmos, del_addr, src_denom)
assert old_src_balance - amt - fee == new_src_balance
14 changes: 11 additions & 3 deletions tests/nix_tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"ICS20I": "evmos/ics20/ICS20I.sol",
"DistributionI": "evmos/distribution/DistributionI.sol",
"StakingI": "evmos/staking/StakingI.sol",
"StakingCaller": "evmos/staking/testdata/StakingCaller.sol",
"IStrideOutpost": "evmos/outposts/stride/IStrideOutpost.sol",
"IERC20": "evmos/erc20/IERC20.sol",
}
Expand Down Expand Up @@ -199,14 +200,21 @@ def get_precompile_contract(w3, name):
return w3.eth.contract(addr, abi=info["abi"])


def build_deploy_contract_tx(w3, info, args=(), key=KEYS["validator"]):
"""
builds a tx to deploy contract without signature and returns it
"""
acct = Account.from_key(key)
contract = w3.eth.contract(abi=info["abi"], bytecode=info["bytecode"])
return contract.constructor(*args).build_transaction({"from": acct.address})


def deploy_contract(w3, jsonfile, args=(), key=KEYS["validator"]):
"""
deploy contract and return the deployed contract instance
"""
acct = Account.from_key(key)
info = json.loads(jsonfile.read_text())
contract = w3.eth.contract(abi=info["abi"], bytecode=info["bytecode"])
tx = contract.constructor(*args).build_transaction({"from": acct.address})
tx = build_deploy_contract_tx(w3, info, args, key)
txreceipt = send_transaction(w3, tx, key)
assert txreceipt.status == 1
address = txreceipt.contractAddress
Expand Down

0 comments on commit 1d138aa

Please sign in to comment.