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

evm: Add tests related to contracts size #2380

Merged
merged 26 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1044dd9
add tests for different contract size
Aug 24, 2023
3d872d7
format test python file
Aug 25, 2023
1463572
Generate contract programmatically
Aug 25, 2023
20c81d0
add sanity check
Aug 25, 2023
6872e52
add ignore of linter
Aug 25, 2023
5338cce
try another way of ignoring
Aug 25, 2023
0a085d1
fix linter issue
Aug 25, 2023
7521b38
add changes as requested
Aug 28, 2023
08204ad
remove unnecessary imports
Aug 28, 2023
c168513
remove unecessary compile_from_str function
Aug 28, 2023
2bce263
remove redundant import
Aug 28, 2023
979f802
Merge branch 'master' into cuong/add_tests_contracts
Jouzo Aug 28, 2023
1d980fb
Merge branch 'master' into cuong/add_tests_contracts
cuongquangnam Aug 29, 2023
cc10044
small change due to output of compile()
Aug 29, 2023
f4aff43
Remove repeated node assignment
shohamc1 Aug 30, 2023
b705a6e
Remove try-except block
shohamc1 Aug 30, 2023
212db22
Use snake_case for variable name
shohamc1 Aug 30, 2023
3b733e8
refactor code
Sep 4, 2023
e552303
merge branch master
Sep 4, 2023
4017c31
fix python lint and refactor
Sep 4, 2023
62ba208
fix extra parameters in compile output
Sep 4, 2023
72975c3
Merge branch 'master' into cuong/add_tests_contracts
cuongquangnam Sep 4, 2023
9fbc3dc
Merge branch 'master' into cuong/add_tests_contracts
prasannavl Sep 5, 2023
2718f70
Format Python test
shohamc1 Sep 5, 2023
3270f0d
Introduce `assert_raises_web3_error`, remove pytest dependency
shohamc1 Sep 6, 2023
25424ce
Merge branch 'master' into cuong/add_tests_contracts
shohamc1 Sep 6, 2023
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 make.sh
Original file line number Diff line number Diff line change
Expand Up @@ -744,7 +744,7 @@ pkg_local_install_py_deps() {
python3 -m pip install black shellcheck-py codespell==2.2.4 flake8==6.0.0 vulture==2.7

# test deps
python3 -m pip install py-solc-x web3
python3 -m pip install py-solc-x web3 pytest
python3 -c 'from solcx import install_solc;install_solc("0.8.20")'

py_env_deactivate
Expand Down
2 changes: 1 addition & 1 deletion test/functional/feature_evm.py
Original file line number Diff line number Diff line change
Expand Up @@ -1209,7 +1209,7 @@ def multiple_eth_rbf(self):
assert_equal(block_txs[2], tx1)

def mempool_block_limit(self):
abi, bytecode = EVMContract.from_file("Loop.sol", "Loop").compile()
abi, bytecode, _ = EVMContract.from_file("Loop.sol", "Loop").compile()
compiled = self.nodes[0].w3.eth.contract(abi=abi, bytecode=bytecode)
tx = compiled.constructor().build_transaction(
{
Expand Down
2 changes: 1 addition & 1 deletion test/functional/feature_evm_contract_env_vars.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def should_create_contract(self):
)
node.generate(1)

abi, bytecode = EVMContract.from_file(
abi, bytecode, _ = EVMContract.from_file(
"GlobalVariable.sol", "GlobalVariable"
).compile()
compiled = node.w3.eth.contract(abi=abi, bytecode=bytecode)
Expand Down
277 changes: 228 additions & 49 deletions test/functional/feature_evm_contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from test_framework.test_framework import DefiTestFramework
from test_framework.evm_contract import EVMContract
from test_framework.evm_key_pair import EvmKeyPair
from test_framework.test_node import TestNode
import pytest


class EVMTest(DefiTestFramework):
Expand Down Expand Up @@ -60,11 +62,43 @@ def setup(self):
)
self.nodes[0].generate(1)

def should_create_contract(self):
node = self.nodes[0]
self.evm_key_pair = EvmKeyPair.from_node(node)
def generate_contract(self, node: TestNode, num_functions: int, contract_name: str):
contract_start = f"""
pragma solidity ^0.8.0;
contract {contract_name} {{
"""
function_template = (
lambda index: f"""
function func{index}() public pure returns(uint256) {{
return {index};
}}"""
)
contract_end = "\n}"

list_sig = []
contract_body = ""

for i in range(0, num_functions):
func_sig = f"func${i}()"
sig_hash = self.node.w3.keccak(text=func_sig)[:4]
if sig_hash in list_sig:
continue
list_sig.append(sig_hash)
contract_body += function_template(i)

utf8_source_code = contract_start + contract_body + contract_end

abi, bytecode, runtime_bytecode = EVMContract.from_str(
utf8_source_code, contract_name
).compile()
compiled_contract = self.node.w3.eth.contract(abi=abi, bytecode=bytecode)

return compiled_contract, runtime_bytecode

def should_deploy_contract_less_than_1KB(self):
self.evm_key_pair = EvmKeyPair.from_node(self.node)

node.transferdomain(
self.node.transferdomain(
[
{
"src": {"address": self.address, "amount": "50@DFI", "domain": 2},
Expand All @@ -76,120 +110,265 @@ def should_create_contract(self):
}
]
)
node.generate(1)
self.node.generate(1)

abi, bytecode = EVMContract.from_file("SimpleStorage.sol", "Test").compile()
compiled = node.w3.eth.contract(abi=abi, bytecode=bytecode)
abi, bytecode, _ = EVMContract.from_file("SimpleStorage.sol", "Test").compile()
compiled = self.node.w3.eth.contract(abi=abi, bytecode=bytecode)

tx = compiled.constructor().build_transaction(
{
"chainId": node.w3.eth.chain_id,
"nonce": node.w3.eth.get_transaction_count(self.evm_key_pair.address),
"chainId": self.node.w3.eth.chain_id,
"nonce": self.node.w3.eth.get_transaction_count(
self.evm_key_pair.address
),
"maxFeePerGas": 10_000_000_000,
"maxPriorityFeePerGas": 1_500_000_000,
"gas": 1_000_000,
}
)
signed = node.w3.eth.account.sign_transaction(tx, self.evm_key_pair.privkey)
hash = node.w3.eth.send_raw_transaction(signed.rawTransaction)
signed = self.node.w3.eth.account.sign_transaction(
tx, self.evm_key_pair.privkey
)
hash = self.node.w3.eth.send_raw_transaction(signed.rawTransaction)

node.generate(1)
self.node.generate(1)

receipt = node.w3.eth.wait_for_transaction_receipt(hash)
self.contract = node.w3.eth.contract(
receipt = self.node.w3.eth.wait_for_transaction_receipt(hash)
self.contract = self.node.w3.eth.contract(
address=receipt["contractAddress"], abi=abi
)
size_of_runtime_bytecode = len(self.node.w3.eth.get_code(self.contract.address))
assert_equal(size_of_runtime_bytecode, 323)

def should_contract_get_set(self):
# set variable
node = self.nodes[0]
tx = self.contract.functions.store(10).build_transaction(
{
"chainId": node.w3.eth.chain_id,
"nonce": node.w3.eth.get_transaction_count(self.evm_key_pair.address),
"chainId": self.node.w3.eth.chain_id,
"nonce": self.node.w3.eth.get_transaction_count(
self.evm_key_pair.address
),
"gasPrice": 10_000_000_000,
}
)
signed = node.w3.eth.account.sign_transaction(tx, self.evm_key_pair.privkey)
hash = node.w3.eth.send_raw_transaction(signed.rawTransaction)
signed = self.node.w3.eth.account.sign_transaction(
tx, self.evm_key_pair.privkey
)
hash = self.node.w3.eth.send_raw_transaction(signed.rawTransaction)

node.generate(1)
self.node.generate(1)

node.w3.eth.wait_for_transaction_receipt(hash)
self.node.w3.eth.wait_for_transaction_receipt(hash)

# get variable
assert_equal(self.contract.functions.retrieve().call(), 10)

def failed_tx_should_increment_nonce(self):
node = self.nodes[0]

abi, bytecode = EVMContract.from_file("Reverter.sol", "Reverter").compile()
compiled = node.w3.eth.contract(abi=abi, bytecode=bytecode)
abi, bytecode, _ = EVMContract.from_file("Reverter.sol", "Reverter").compile()
compiled = self.node.w3.eth.contract(abi=abi, bytecode=bytecode)

tx = compiled.constructor().build_transaction(
{
"chainId": node.w3.eth.chain_id,
"nonce": node.w3.eth.get_transaction_count(self.evm_key_pair.address),
"chainId": self.node.w3.eth.chain_id,
"nonce": self.node.w3.eth.get_transaction_count(
self.evm_key_pair.address
),
"maxFeePerGas": 10_000_000_000,
"maxPriorityFeePerGas": 1_500_000_000,
"gas": 1_000_000,
}
)
signed = node.w3.eth.account.sign_transaction(tx, self.evm_key_pair.privkey)
hash = node.w3.eth.send_raw_transaction(signed.rawTransaction)
signed = self.node.w3.eth.account.sign_transaction(
tx, self.evm_key_pair.privkey
)
hash = self.node.w3.eth.send_raw_transaction(signed.rawTransaction)

node.generate(1)
self.node.generate(1)

receipt = node.w3.eth.wait_for_transaction_receipt(hash)
contract = node.w3.eth.contract(address=receipt["contractAddress"], abi=abi)
receipt = self.node.w3.eth.wait_for_transaction_receipt(hash)
contract = self.node.w3.eth.contract(
address=receipt["contractAddress"], abi=abi
)

# for successful TX
before_tx_count = node.w3.eth.get_transaction_count(self.evm_key_pair.address)
before_tx_count = self.node.w3.eth.get_transaction_count(
self.evm_key_pair.address
)

tx = contract.functions.trySuccess().build_transaction(
{
"chainId": node.w3.eth.chain_id,
"nonce": node.w3.eth.get_transaction_count(self.evm_key_pair.address),
"chainId": self.node.w3.eth.chain_id,
"nonce": self.node.w3.eth.get_transaction_count(
self.evm_key_pair.address
),
"gasPrice": 10_000_000_000,
}
)
signed = node.w3.eth.account.sign_transaction(tx, self.evm_key_pair.privkey)
hash = node.w3.eth.send_raw_transaction(signed.rawTransaction)
node.generate(1)
node.w3.eth.wait_for_transaction_receipt(hash)
signed = self.node.w3.eth.account.sign_transaction(
tx, self.evm_key_pair.privkey
)
hash = self.node.w3.eth.send_raw_transaction(signed.rawTransaction)
self.node.generate(1)
self.node.w3.eth.wait_for_transaction_receipt(hash)

after_tx_count = node.w3.eth.get_transaction_count(self.evm_key_pair.address)
after_tx_count = self.node.w3.eth.get_transaction_count(
self.evm_key_pair.address
)

assert_equal(before_tx_count + 1, after_tx_count)

# for failed TX
before_tx_count = node.w3.eth.get_transaction_count(self.evm_key_pair.address)
before_tx_count = self.node.w3.eth.get_transaction_count(
self.evm_key_pair.address
)

tx = contract.functions.tryRevert().build_transaction(
{
"chainId": node.w3.eth.chain_id,
"nonce": node.w3.eth.get_transaction_count(self.evm_key_pair.address),
"chainId": self.node.w3.eth.chain_id,
"nonce": self.node.w3.eth.get_transaction_count(
self.evm_key_pair.address
),
"gasPrice": 10_000_000_000,
}
)
signed = node.w3.eth.account.sign_transaction(tx, self.evm_key_pair.privkey)
hash = node.w3.eth.send_raw_transaction(signed.rawTransaction)
node.generate(1)
node.w3.eth.wait_for_transaction_receipt(hash)
signed = self.node.w3.eth.account.sign_transaction(
tx, self.evm_key_pair.privkey
)
hash = self.node.w3.eth.send_raw_transaction(signed.rawTransaction)
self.node.generate(1)
self.node.w3.eth.wait_for_transaction_receipt(hash)

after_tx_count = node.w3.eth.get_transaction_count(self.evm_key_pair.address)
after_tx_count = self.node.w3.eth.get_transaction_count(
self.evm_key_pair.address
)

assert_equal(before_tx_count + 1, after_tx_count)


def should_deploy_contract_with_different_sizes(self):
iterationToContractName = {128: "ContractSize1KBTo10KB", 256: "ContractSize10KBTo19KB", 400: "ContractSize20KBTo29KB"}
iterationToContractSize = {128: 6901, 256: 13685, 400: 21140}
for iteration in [128, 256, 400]:
cuongquangnam marked this conversation as resolved.
Show resolved Hide resolved
compiled_contract, compiler_runtime_bytecode = self.generate_contract(
self.node, iteration, iterationToContractName[iteration]
)

tx = compiled_contract.constructor().build_transaction(
{
"chainId": self.node.w3.eth.chain_id,
"nonce": self.node.w3.eth.get_transaction_count(
self.evm_key_pair.address
),
"maxFeePerGas": 10_000_000_000,
"maxPriorityFeePerGas": 1_500_000_000,
}
)
signed = self.node.w3.eth.account.sign_transaction(
tx, self.evm_key_pair.privkey
)
hash = self.node.w3.eth.send_raw_transaction(signed.rawTransaction)

self.node.generate(1)
receipt = self.node.w3.eth.wait_for_transaction_receipt(hash)
runtime_bytecode = self.node.w3.eth.get_code(receipt["contractAddress"])
size_of_runtime_bytecode = len(runtime_bytecode)
assert_equal(receipt["status"], 1)
assert_equal(size_of_runtime_bytecode, iterationToContractSize[iteration])
# sanity check for the equality between the runtime bytecode generated by the compiler
# and the runtime code deployed
assert_equal(compiler_runtime_bytecode, runtime_bytecode.hex()[2:])

# EIP 170, contract size is limited to 24576 bytes
# this test deploys a smart contract with an estimated size larger than this number
def fail_deploy_contract_extremely_large_runtime_code(self):
compiled_contract, compiler_runtime_bytecode = self.generate_contract(
self.node, 2**9 - 1, "ContractLargeRunTimeCode"
)
assert_equal(len(compiler_runtime_bytecode) / 2, 27458)

tx = compiled_contract.constructor().build_transaction(
{
"chainId": self.node.w3.eth.chain_id,
"nonce": self.node.w3.eth.get_transaction_count(
self.evm_key_pair.address
),
"maxFeePerGas": 10_000_000_000,
"maxPriorityFeePerGas": 1_500_000_000,
}
)
signed = self.node.w3.eth.account.sign_transaction(
tx, self.evm_key_pair.privkey
)
hash = self.node.w3.eth.send_raw_transaction(signed.rawTransaction)

self.node.generate(1)

receipt = self.node.w3.eth.wait_for_transaction_receipt(hash)
size_of_runtime_bytecode = len(
self.node.w3.eth.get_code(receipt["contractAddress"])
)
assert_equal(receipt["status"], 0)
assert_equal(size_of_runtime_bytecode, 0)

# EIP 3860, contract initcode is limited up till 49152 bytes
# This test takes in a contract with init code of 243542 bytes
# However, because the current implementation of DMC limits the size of EVM transaction to 32768 bytes
# the error returned is evm tx size too large
def fail_deploy_contract_extremely_large_init_code(self):
compiled_contract, _ = self.generate_contract(
self.node, 2**12 - 1, "ContractLargeInitCode"
)

tx = compiled_contract.constructor().build_transaction(
{
"chainId": self.node.w3.eth.chain_id,
"nonce": self.node.w3.eth.get_transaction_count(
self.evm_key_pair.address
),
"maxFeePerGas": 10_000_000_000,
"maxPriorityFeePerGas": 1_500_000_000,
}
)
# to check the init code is larger than 49152
assert_equal((len(tx["data"]) - 2) / 2, 243542)
signed = self.node.w3.eth.account.sign_transaction(
tx, self.evm_key_pair.privkey
)

with pytest.raises(Exception) as e:
self.node.w3.eth.send_raw_transaction(signed.rawTransaction)

error_code = e.value.args[0]["code"]
error_message = e.value.args[0]["message"]

assert_equal(error_code, -32001)
assert_equal(
"Custom error: Could not publish raw transaction:" in error_message,
True,
)
assert_equal(
"reason: Test EvmTxTx execution failed:\nevm tx size too large"
in error_message,
True,
)

def run_test(self):
self.setup()

self.should_create_contract()
self.node = self.nodes[0]

self.should_deploy_contract_less_than_1KB()

self.should_contract_get_set()

self.failed_tx_should_increment_nonce()

self.should_deploy_contract_with_different_sizes()

self.fail_deploy_contract_extremely_large_runtime_code()

self.fail_deploy_contract_extremely_large_init_code()


if __name__ == "__main__":
EVMTest().main()
Loading