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
Changes from 7 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
203 changes: 200 additions & 3 deletions test/functional/feature_evm_contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
"""Test EVM contract"""

from test_framework.util import assert_equal
from test_framework.util import assert_equal, assert_greater_than
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
from solcx import compile_source


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

def should_create_contract(self):
def generate_contract(self, node: TestNode, num_functions: int, contract_name: str):
contract_start = """
pragma solidity ^0.8.0;\n
contract {} {{ \n
""".format(
contract_name
)

function_template = lambda index: """
function func{}() public pure returns(uint256) {{
return {};
}}""".format(
index, index
)

contract_end = """
}"""
cuongquangnam marked this conversation as resolved.
Show resolved Hide resolved

list_sig = []
contract_body = ""

for i in range(0, num_functions):
func_sig = "func${}()".format(i)
cuongquangnam marked this conversation as resolved.
Show resolved Hide resolved
sig_hash = node.w3.keccak(text=func_sig)[:4]
if sig_hash in list_sig:
continue
list_sig.append(sig_hash)
contract_body += function_template(i)

utf8SourceCode = contract_start + contract_body + contract_end

compiled_output = compile_source(
source=utf8SourceCode,
output_values=["abi", "bin"],
solc_version="0.8.20",
)
cuongquangnam marked this conversation as resolved.
Show resolved Hide resolved

abi = compiled_output["<stdin>:{}".format(contract_name)]["abi"]
bytecode = compiled_output["<stdin>:{}".format(contract_name)]["bin"]
compiled_contract = node.w3.eth.contract(abi=abi, bytecode=bytecode)

return compiled_contract

def should_deploy_contract_less_than_1KB(self):
node = self.nodes[0]
self.evm_key_pair = EvmKeyPair.from_node(node)

Expand Down Expand Up @@ -99,6 +144,9 @@ def should_create_contract(self):
self.contract = node.w3.eth.contract(
address=receipt["contractAddress"], abi=abi
)
size_of_runtime_bytecode = len(node.w3.eth.get_code(self.contract.address))
assert_greater_than(size_of_runtime_bytecode, 0)
assert_greater_than(1_000, size_of_runtime_bytecode)
canonbrother marked this conversation as resolved.
Show resolved Hide resolved

def should_contract_get_set(self):
# set variable
Expand Down Expand Up @@ -181,15 +229,164 @@ def failed_tx_should_increment_nonce(self):

assert_equal(before_tx_count + 1, after_tx_count)

def should_deploy_contract_1KB_To_10KB(self):
cuongquangnam marked this conversation as resolved.
Show resolved Hide resolved
node = self.nodes[0]
shohamc1 marked this conversation as resolved.
Show resolved Hide resolved

compiled_contract = self.generate_contract(
node, 2**7, "ContractSize1KBTo10KB"
)

tx = compiled_contract.constructor().build_transaction(
{
"chainId": node.w3.eth.chain_id,
"nonce": node.w3.eth.get_transaction_count(self.evm_key_pair.address),
"maxFeePerGas": 10_000_000_000,
"maxPriorityFeePerGas": 1_500_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)
receipt = node.w3.eth.wait_for_transaction_receipt(hash)
size_of_runtime_bytecode = len(node.w3.eth.get_code(receipt["contractAddress"]))
assert_equal(receipt["status"], 1)
assert_greater_than(size_of_runtime_bytecode, 1_000)
assert_greater_than(10_000, size_of_runtime_bytecode)

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

compiled_contract = self.generate_contract(
node, 2**8, "ContractSize10KBTo19KB"
)

tx = compiled_contract.constructor().build_transaction(
{
"chainId": node.w3.eth.chain_id,
"nonce": node.w3.eth.get_transaction_count(self.evm_key_pair.address),
"maxFeePerGas": 10_000_000_000,
"maxPriorityFeePerGas": 1_500_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)
receipt = node.w3.eth.wait_for_transaction_receipt(hash)
size_of_runtime_bytecode = len(node.w3.eth.get_code(receipt["contractAddress"]))
assert_equal(receipt["status"], 1)
assert_greater_than(size_of_runtime_bytecode, 10_000)
assert_greater_than(19_000, size_of_runtime_bytecode)

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

compiled_contract = self.generate_contract(node, 400, "ContractSize20KBTo29KB")

tx = compiled_contract.constructor().build_transaction(
{
"chainId": node.w3.eth.chain_id,
"nonce": node.w3.eth.get_transaction_count(self.evm_key_pair.address),
"maxFeePerGas": 10_000_000_000,
"maxPriorityFeePerGas": 1_500_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)
receipt = node.w3.eth.wait_for_transaction_receipt(hash)
size_of_runtime_bytecode = len(node.w3.eth.get_code(receipt["contractAddress"]))
assert_equal(receipt["status"], 1)
assert_greater_than(size_of_runtime_bytecode, 20_000)
assert_greater_than(29_000, size_of_runtime_bytecode)

# 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):
node = self.nodes[0]

compiled_contract = self.generate_contract(
node, 2**9 - 1, "ContractLargeRunTimeCode"
)

tx = compiled_contract.constructor().build_transaction(
{
"chainId": node.w3.eth.chain_id,
"nonce": node.w3.eth.get_transaction_count(self.evm_key_pair.address),
"maxFeePerGas": 10_000_000_000,
"maxPriorityFeePerGas": 1_500_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)

receipt = node.w3.eth.wait_for_transaction_receipt(hash)
size_of_runtime_bytecode = len(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):
node = self.nodes[0]

compiled_contract = self.generate_contract(
node, 2**12 - 1, "ContractLargeInitCode"
)

tx = compiled_contract.constructor().build_transaction(
{
"chainId": node.w3.eth.chain_id,
"nonce": 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_greater_than((len(tx["data"]) - 2) / 2, 49152)
signed = node.w3.eth.account.sign_transaction(tx, self.evm_key_pair.privkey)

try:
node.w3.eth.send_raw_transaction(signed.rawTransaction)
shohamc1 marked this conversation as resolved.
Show resolved Hide resolved
except Exception as e:
error_code = e.args[0]["code"]
error_message = e.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.should_deploy_contract_less_than_1KB()

self.should_contract_get_set()

self.failed_tx_should_increment_nonce()

self.should_deploy_contract_1KB_To_10KB()

self.should_deploy_contract_10KB_To_19KB()

self.should_deploy_contract_20KB_To_29KB()

self.fail_deploy_contract_extremely_large_runtime_code()

self.fail_deploy_contract_extremely_large_init_code()


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