From ccb6574c46f01aad6c75e9ab1c157131ae2374a3 Mon Sep 17 00:00:00 2001 From: Stuart Reed Date: Wed, 15 Jan 2025 09:17:20 -0700 Subject: [PATCH] Contract opcodes to compare gas cost for each precompile Include all test cases and expect precompiles at addresses 01-0A All other cases expect no precompile to exist at the specified address --- tests/precompiles/__init__.py | 0 tests/precompiles/test_precompiles.py | 341 ++++++++++++++++++++++++++ 2 files changed, 341 insertions(+) create mode 100644 tests/precompiles/__init__.py create mode 100644 tests/precompiles/test_precompiles.py diff --git a/tests/precompiles/__init__.py b/tests/precompiles/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/precompiles/test_precompiles.py b/tests/precompiles/test_precompiles.py new file mode 100644 index 00000000000..70567957826 --- /dev/null +++ b/tests/precompiles/test_precompiles.py @@ -0,0 +1,341 @@ +import pytest + +from ethereum_test_tools import ( + Account, + Alloc, + Environment, + StateTestFiller, + Transaction, +) +from ethereum_test_tools.vm.opcode import Opcodes as Op + + +@pytest.mark.parametrize( + ["address", "exists"], + [ + ("01", True), + ("02", True), + ("03", True), + ("04", True), + ("05", True), + ("06", True), + ("07", True), + ("08", True), + ("09", True), + ("0A", True), + ("0B", False), + ("0C", False), + ("0D", False), + ("0E", False), + ("0F", False), + ("10", False), + ("11", False), + ("12", False), + ("13", False), + ("14", False), + ("15", False), + ("16", False), + ("17", False), + ("18", False), + ("19", False), + ("1A", False), + ("1B", False), + ("1C", False), + ("1D", False), + ("1E", False), + ("1F", False), + ("20", False), + ("21", False), + ("22", False), + ("23", False), + ("24", False), + ("25", False), + ("26", False), + ("27", False), + ("28", False), + ("29", False), + ("2A", False), + ("2B", False), + ("2C", False), + ("2D", False), + ("2E", False), + ("2F", False), + ("30", False), + ("31", False), + ("32", False), + ("33", False), + ("34", False), + ("35", False), + ("36", False), + ("37", False), + ("38", False), + ("39", False), + ("3A", False), + ("3B", False), + ("3C", False), + ("3D", False), + ("3E", False), + ("3F", False), + ("40", False), + ("41", False), + ("42", False), + ("43", False), + ("44", False), + ("45", False), + ("46", False), + ("47", False), + ("48", False), + ("49", False), + ("4A", False), + ("4B", False), + ("4C", False), + ("4D", False), + ("4E", False), + ("4F", False), + ("50", False), + ("51", False), + ("52", False), + ("53", False), + ("54", False), + ("55", False), + ("56", False), + ("57", False), + ("58", False), + ("59", False), + ("5A", False), + ("5B", False), + ("5C", False), + ("5D", False), + ("5E", False), + ("5F", False), + ("60", False), + ("61", False), + ("62", False), + ("63", False), + ("64", False), + ("65", False), + ("66", False), + ("67", False), + ("68", False), + ("69", False), + ("6A", False), + ("6B", False), + ("6C", False), + ("6D", False), + ("6E", False), + ("6F", False), + ("70", False), + ("71", False), + ("72", False), + ("73", False), + ("74", False), + ("75", False), + ("76", False), + ("77", False), + ("78", False), + ("79", False), + ("7A", False), + ("7B", False), + ("7C", False), + ("7D", False), + ("7E", False), + ("7F", False), + ("80", False), + ("81", False), + ("82", False), + ("83", False), + ("84", False), + ("85", False), + ("86", False), + ("87", False), + ("88", False), + ("89", False), + ("8A", False), + ("8B", False), + ("8C", False), + ("8D", False), + ("8E", False), + ("8F", False), + ("90", False), + ("91", False), + ("92", False), + ("93", False), + ("94", False), + ("95", False), + ("96", False), + ("97", False), + ("98", False), + ("99", False), + ("9A", False), + ("9B", False), + ("9C", False), + ("9D", False), + ("9E", False), + ("9F", False), + ("A0", False), + ("A1", False), + ("A2", False), + ("A3", False), + ("A4", False), + ("A5", False), + ("A6", False), + ("A7", False), + ("A8", False), + ("A9", False), + ("AA", False), + ("AB", False), + ("AC", False), + ("AD", False), + ("AE", False), + ("AF", False), + ("B0", False), + ("B1", False), + ("B2", False), + ("B3", False), + ("B4", False), + ("B5", False), + ("B6", False), + ("B7", False), + ("B8", False), + ("B9", False), + ("BA", False), + ("BB", False), + ("BC", False), + ("BD", False), + ("BE", False), + ("BF", False), + ("C0", False), + ("C1", False), + ("C2", False), + ("C3", False), + ("C4", False), + ("C5", False), + ("C6", False), + ("C7", False), + ("C8", False), + ("C9", False), + ("CA", False), + ("CB", False), + ("CC", False), + ("CD", False), + ("CE", False), + ("CF", False), + ("D0", False), + ("D1", False), + ("D2", False), + ("D3", False), + ("D4", False), + ("D5", False), + ("D6", False), + ("D7", False), + ("D8", False), + ("D9", False), + ("DA", False), + ("DB", False), + ("DC", False), + ("DD", False), + ("DE", False), + ("DF", False), + ("E0", False), + ("E1", False), + ("E2", False), + ("E3", False), + ("E4", False), + ("E5", False), + ("E6", False), + ("E7", False), + ("E8", False), + ("E9", False), + ("EA", False), + ("EB", False), + ("EC", False), + ("ED", False), + ("EE", False), + ("EF", False), + ("F0", False), + ("F1", False), + ("F2", False), + ("F3", False), + ("F4", False), + ("F5", False), + ("F6", False), + ("F7", False), + ("F8", False), + ("F9", False), + ("FA", False), + ("FB", False), + ("FC", False), + ("FD", False), + ("FE", False), + ("FF", False), + ], +) +def test_precompiles(state_test: StateTestFiller, pre: Alloc, address: str, exists: bool): + """Test the MODEXP precompile.""" + env = Environment() + + address_10000 = 0x10000 + + input_buf = 0x1000 + output_buf = 0x2000 + + args_offset = 0x1000 + args_size = 0x20 + output_offset = 0x2000 + output_size = 0x20 + + gas_test = 0x20 + gas_10000 = 0x40 + + account = pre.deploy_contract( + Op.CALLDATACOPY(0, 0, Op.CALLDATASIZE()) + + Op.MSTORE(args_size, 0x20) + + Op.MSTORE(input_buf, 0xFF) + + Op.MSTORE(output_buf, 0xFF) + + Op.MSTORE8(input_buf, 0xFF) + + Op.MSTORE8(output_buf, 0xFF) + + Op.BALANCE(Op.MLOAD(0)) + + Op.BALANCE(address_10000) + + Op.MSTORE(gas_test, Op.GAS()) + # Setup stack to CALL into precompile with the CALLDATA and CALL into it (+ pop value) + + Op.CALL( + address=Op.MLOAD(0), + args_offset=args_offset, + args_size=args_size, + output_offset=output_offset, + output_size=output_size, + ) + + Op.MSTORE(gas_test, (Op.SUB(Op.GAS(), Op.MLOAD(gas_test)))) + + Op.MSTORE(gas_10000, Op.GAS()) + + Op.CALL( + address=address_10000, + args_offset=args_offset, + args_size=args_size, + output_offset=output_offset, + output_size=output_size, + ) + + Op.MSTORE(gas_10000, (Op.SUB(Op.GAS(), Op.MLOAD(gas_10000)))) + + Op.GT(Op.MLOAD(gas_test), Op.MLOAD(gas_10000)) + + Op.JUMPI(Op.JUMPDEST(), 0x1) + + Op.SSTORE(0, Op.SUB(Op.MLOAD(gas_test), Op.MLOAD(gas_10000))) + + Op.SSTORE(0, Op.LT(Op.SLOAD(gas_test), 0x10)) + + Op.STOP(), + ) + + sender = pre.fund_eoa() + + tx = Transaction( + ty=0x0, + to=account, + data=bytes.fromhex(address), + gas_limit=500000, + gas_price=10, + protected=True, + sender=sender, + ) + + # A high gas cost will result from calling a precompile + # Expect 0x00 when a precompile exists at the address, 0x01 otherwise + post = {account: Account(storage={0: "0x00" if exists else "0x01"})} + + state_test(env=env, pre=pre, post=post, tx=tx)