diff --git a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/pre_alloc.py b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/pre_alloc.py index 34e5571eca..3a3c64e378 100644 --- a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/pre_alloc.py +++ b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/pre_alloc.py @@ -116,7 +116,7 @@ def pytest_addoption(parser: pytest.Parser) -> None: "--eoa-fund-amount-default", action="store", dest="eoa_fund_amount_default", - default=10**18, + default=10**17, type=int, help="The default amount of wei to fund each EOA in each test with.", ) diff --git a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/sender.py b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/sender.py index 33bc6fe1ab..d2f59e1f9b 100644 --- a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/sender.py +++ b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/sender.py @@ -25,7 +25,7 @@ def pytest_addoption(parser: pytest.Parser) -> None: dest="seed_account_sweep_amount", type=Wei, default=None, - help="Amount of wei to sweep from the seed account to the sender account. " + help="Amount of wei to sweep from the seed account to the sender account. " # noqa: E501 "Default=None (Entire balance)", ) @@ -36,7 +36,7 @@ def pytest_addoption(parser: pytest.Parser) -> None: type=Wei, default=None, help=( - "Gas price set for the funding transactions of each worker's sender key." + "Gas price set for the funding transactions of each worker's sender key." # noqa: E501 ), ) @@ -47,7 +47,7 @@ def pytest_addoption(parser: pytest.Parser) -> None: type=Wei, default=21_000, help=( - "Gas limit set for the funding transactions of each worker's sender key." + "Gas limit set for the funding transactions of each worker's sender key." # noqa: E501 ), ) @@ -80,6 +80,7 @@ def seed_account_sweep_amount(request: pytest.FixtureRequest) -> int | None: @pytest.fixture(scope="session") def sender_key_initial_balance( + request: pytest.FixtureRequest, seed_sender: EOA, eth_rpc: EthRPC, session_temp_folder: Path, @@ -127,6 +128,31 @@ def sender_key_initial_balance( * sender_funding_transactions_gas_price ) + # ensure sender has enough balance for eoa_fund_amount_default + eoa_fund_amount_default: int = ( + request.config.option.eoa_fund_amount_default + ) + # Reserve gas for funding tx (21000 gas * gas_price) + funding_tx_cost = ( + sender_fund_refund_gas_limit + * sender_funding_transactions_gas_price + ) + if ( + sender_key_initial_balance + < eoa_fund_amount_default + funding_tx_cost + ): + raise ValueError( + f"Seed sender balance too low for --eoa-fund-amount-default=" # noqa: E501 + f"{eoa_fund_amount_default}. " + f"Available balance per worker: {sender_key_initial_balance} wei " # noqa: E501 + f"({sender_key_initial_balance / 10**18:.6f} ETH). " + f"Required: {eoa_fund_amount_default + funding_tx_cost} wei " # noqa: E501 + f"({(eoa_fund_amount_default + funding_tx_cost) / 10**18:.6f} ETH). " # noqa: E501 + f"Either fund the seed account with more ETH or use " + f"--eoa-fund-amount-default={sender_key_initial_balance - funding_tx_cost} " # noqa: E501 + f"or lower." + ) + with base_file.open("w") as f: f.write(str(sender_key_initial_balance)) return sender_key_initial_balance diff --git a/packages/testing/src/execution_testing/rpc/rpc.py b/packages/testing/src/execution_testing/rpc/rpc.py index a76485e3e3..15a91df8f6 100644 --- a/packages/testing/src/execution_testing/rpc/rpc.py +++ b/packages/testing/src/execution_testing/rpc/rpc.py @@ -188,6 +188,7 @@ def post_request( "RPC response didn't contain a result field" ) result = response_json["result"] + logger.info(f"RPC Result: {result}") return result @@ -239,6 +240,7 @@ def config(self, timeout: int | None = None) -> EthConfigResponse | None: client. """ try: + logger.info("Requesting eth_config..") response = self.post_request(method="config", timeout=timeout) if response is None: logger.warning("eth_config request: failed to get response") @@ -257,6 +259,7 @@ def config(self, timeout: int | None = None) -> EthConfigResponse | None: def chain_id(self) -> int: """`eth_chainId`: Returns the current chain id.""" + logger.info("Requesting chainid of provided RPC endpoint..") response = self.post_request(method="chainId", timeout=10) return int(response, 16) @@ -272,6 +275,7 @@ def get_block_by_number( if isinstance(block_number, int) else block_number ) + logger.info(f"Requesting info about block {block}..") params = [block, full_txs] response = self.post_request(method="getBlockByNumber", params=params) return response @@ -280,6 +284,7 @@ def get_block_by_hash( self, block_hash: Hash, full_txs: bool = True ) -> Any | None: """`eth_getBlockByHash`: Returns information about a block by hash.""" + logger.info(f"Requesting block info of {block_hash}..") params = [f"{block_hash}", full_txs] response = self.post_request(method="getBlockByHash", params=params) return response @@ -295,6 +300,7 @@ def get_balance( if isinstance(block_number, int) else block_number ) + logger.info(f"Requesting balance of {address} at block {block}") params = [f"{address}", block] response = self.post_request(method="getBalance", params=params) return int(response, 16) @@ -308,6 +314,7 @@ def get_code( if isinstance(block_number, int) else block_number ) + logger.info(f"Requesting code of {address} at block {block}") params = [f"{address}", block] response = self.post_request(method="getCode", params=params) return Bytes(response) @@ -324,6 +331,7 @@ def get_transaction_count( if isinstance(block_number, int) else block_number ) + logger.info(f"Requesting nonce of {address}") params = [f"{address}", block] response = self.post_request( method="getTransactionCount", params=params @@ -335,6 +343,7 @@ def get_transaction_by_hash( ) -> TransactionByHashResponse | None: """`eth_getTransactionByHash`: Returns transaction details.""" try: + logger.info(f"Requesting tx details of {transaction_hash}") response = self.post_request( method="getTransactionByHash", params=[f"{transaction_hash}"] ) @@ -356,6 +365,7 @@ def get_transaction_receipt( Used to get the actual gas used by a transaction for gas validation in benchmark tests. """ + logger.info(f"Requesting tx receipt of {transaction_hash}") response = self.post_request( method="getTransactionReceipt", params=[f"{transaction_hash}"] ) @@ -376,15 +386,19 @@ def get_storage_at( if isinstance(block_number, int) else block_number ) + logger.info( + f"Requesting storage value mapped to key {position} " + f"of contract {address}" + ) params = [f"{address}", f"{position}", block] response = self.post_request(method="getStorageAt", params=params) return Hash(response) def gas_price(self) -> int: """ - `eth_gasPrice`: Returns the number of transactions sent from an - address. + `eth_gasPrice`: Returns the gas price. """ + logger.info("Requesting gas price") response = self.post_request(method="gasPrice") return int(response, 16) @@ -393,6 +407,7 @@ def send_raw_transaction( ) -> Hash: """`eth_sendRawTransaction`: Send a transaction to the client.""" try: + logger.info("Sending raw tx..") response = self.post_request( method="sendRawTransaction", params=[transaction_rlp.hex()], @@ -402,6 +417,7 @@ def send_raw_transaction( assert result_hash is not None return result_hash except Exception as e: + logger.error(e) raise SendTransactionExceptionError( str(e), tx_rlp=transaction_rlp ) from e @@ -410,6 +426,7 @@ def send_transaction(self, transaction: Transaction) -> Hash: """`eth_sendRawTransaction`: Send a transaction to the client.""" # TODO: is this a copypaste error from above? try: + logger.info("Sending tx..") response = self.post_request( method="sendRawTransaction", params=[transaction.rlp().hex()], @@ -455,6 +472,7 @@ def wait_for_transaction( tx_hash = transaction.hash start_time = time.time() while True: + logger.info(f"Waiting for inclusion of tx {tx_hash} in a block..") tx = self.get_transaction_by_hash(tx_hash) if tx is not None and tx.block_number is not None: return tx @@ -476,6 +494,7 @@ def wait_for_transactions( tx_hashes = [tx.hash for tx in transactions] responses: List[TransactionByHashResponse] = [] start_time = time.time() + logger.info("Waiting for all transaction to be included in a block..") while True: i = 0 while i < len(tx_hashes): @@ -483,6 +502,9 @@ def wait_for_transactions( tx = self.get_transaction_by_hash(tx_hash) if tx is not None and tx.block_number is not None: responses.append(tx) + logger.info( + f"Tx {tx} was included in block {tx.block_number}" + ) tx_hashes.pop(i) else: i += 1 diff --git a/tests/osaka/eip7883_modexp_gas_increase/test_eip_mainnet.py b/tests/osaka/eip7883_modexp_gas_increase/test_eip_mainnet.py new file mode 100644 index 0000000000..2f9b395669 --- /dev/null +++ b/tests/osaka/eip7883_modexp_gas_increase/test_eip_mainnet.py @@ -0,0 +1,109 @@ +""" +Mainnet marked tests for EIP-7883 ModExp gas cost increase. + +Tests for ModExp gas cost increase in +[EIP-7883: ModExp Gas Cost Increase](https://eips.ethereum.org/EIPS/eip-7883). +""" + +from typing import Dict + +import pytest +from execution_testing import ( + Alloc, + StateTestFiller, + Transaction, +) + +from ...byzantium.eip198_modexp_precompile.helpers import ModExpInput +from .spec import Spec, ref_spec_7883 + +REFERENCE_SPEC_GIT_PATH = ref_spec_7883.git_path +REFERENCE_SPEC_VERSION = ref_spec_7883.version + +pytestmark = [pytest.mark.valid_at("Osaka"), pytest.mark.mainnet] + + +@pytest.mark.parametrize( + "modexp_input,modexp_expected", + [ + pytest.param( + ModExpInput( + base="ff" * 31 + "fc", + exponent="02", + modulus="05", + declared_base_length=32, + declared_exponent_length=1, + declared_modulus_length=1, + ), + # expected result: + bytes.fromhex("04"), + id="32-bytes-long-base", + ), + pytest.param( + ModExpInput( + base="ff" * 32 + "fb", + exponent="02", + modulus="05", + declared_base_length=33, + declared_exponent_length=1, + declared_modulus_length=1, + ), + # expected result: + bytes.fromhex("01"), + id="33-bytes-long-base", # higher cost than 32 bytes + ), + pytest.param( + ModExpInput( + base="ee" * 32, + exponent="ff" * Spec.MAX_LENGTH_BYTES, # 1024 is upper limit + modulus="03", + declared_base_length=32, + declared_exponent_length=1024, + declared_modulus_length=1, + ), + # expected result: + bytes.fromhex("02"), + id="1024-bytes-long-exp", + ), + pytest.param( + ModExpInput( + base="e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb5", + exponent="010001", + modulus="fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", + declared_base_length=64, + declared_exponent_length=3, + declared_modulus_length=64, + ), + # expected result: + bytes.fromhex( + "c36d804180c35d4426b57b50c5bfcca5c01856d104564cd513b461d3c8b8409128a5573e416d0ebe38f5f736766d9dc27143e4da981dfa4d67f7dc474cbee6d2" + ), + id="nagydani-1-pow0x10001", + ), + pytest.param( + ModExpInput( + base="8d74b1229cc36912165d7ed62334d5ce0683ad12dbade86cdbd705f46693d6c0", + exponent="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + modulus="8f70e8f94c5ad28ed971e258ea3854ebf57131ae4c842e5cafe1c70db8272caf", + declared_base_length=32, + declared_exponent_length=64, + declared_modulus_length=32, + ), + # expected result: + bytes.fromhex( + "0000000000000000000000000000000000000000000000000000000000000001" + ), + id="zero-exponent-64bytes", + ), + ], +) +def test_modexp_different_base_lengths( + state_test: StateTestFiller, + pre: Alloc, + tx: Transaction, + post: Dict, + modexp_input: ModExpInput, + modexp_expected: ModExpInput, +) -> None: + """Mainnet test for triggering gas cost increase.""" + state_test(pre=pre, tx=tx, post=post) diff --git a/tests/osaka/eip7951_p256verify_precompiles/test_eip_mainnet.py b/tests/osaka/eip7951_p256verify_precompiles/test_eip_mainnet.py new file mode 100644 index 0000000000..8b8d2fb7b1 --- /dev/null +++ b/tests/osaka/eip7951_p256verify_precompiles/test_eip_mainnet.py @@ -0,0 +1,91 @@ +""" +Mainnet marked tests for [EIP-7951: Precompile for secp256r1 Curve Support](https://eips.ethereum.org/EIPS/eip-7951). +""" + +import pytest +from execution_testing import ( + Alloc, + Environment, + StateTestFiller, + Transaction, +) + +from .spec import H, R, S, Spec, X, Y, ref_spec_7951 + +REFERENCE_SPEC_GIT_PATH = ref_spec_7951.git_path +REFERENCE_SPEC_VERSION = ref_spec_7951.version + +pytestmark = [pytest.mark.valid_at("Osaka"), pytest.mark.mainnet] + + +@pytest.mark.parametrize( + "expected_output", [Spec.SUCCESS_RETURN_VALUE], ids=[""] +) +@pytest.mark.parametrize( + "input_data", + [ + pytest.param( + H( # 'hello world' r1 signed with privkey 0xd946578401d1980aba1fc85df2a1ddc0d2d618aadd37b213f7f7f91a553b1499 # noqa: E501 + 0xB94D27B9934D3E08A52E52D7DA7DABFAC484EFE37A5380EE9088F7ACE2EFCDE9 + ) + + R( + 0x434287FA699FF2BE2A4475CCD9C063D1A22A424B6AB357D9BB0B31F7A71307B9 + ) + + S( + 0xBE6AF716032D408183C53F6F76945363144555FAD2A5FF7854159166E52FC1D0 + ) + + X( + 0xDA3C553A4215893E6D95D5818DF2519E13233A1E0E56E0EA7B4817A92A6973F9 + ) + + Y( + 0xD8C8877A49383E20C3FDC21D4E8E280EF1FEEB72C333036770369A4387168D33 + ), + id="valid_r1_sig", + ), + ], +) +@pytest.mark.parametrize("precompile_address", [Spec.P256VERIFY], ids=[""]) +def test_valid( + state_test: StateTestFiller, pre: Alloc, post: dict, tx: Transaction +) -> None: + """Positive mainnet test for the P256VERIFY precompile.""" + state_test(env=Environment(), pre=pre, post=post, tx=tx) + + +@pytest.mark.parametrize( + "expected_output", [Spec.INVALID_RETURN_VALUE], ids=[""] +) +@pytest.mark.parametrize( + "input_data", + [ + pytest.param( + H( # 'hello world' k1 signed (with eth prefix) with privkey 0xd946578401d1980aba1fc85df2a1ddc0d2d618aadd37b213f7f7f91a553b1499 # noqa: E501 + 0xD9EBA16ED0ECAE432B71FE008C98CC872BB4CC214D3220A36F365326CF807D68 + ) + + R( + 0x69CCCD84CA870C08D49D596342F464017F2A05B0BE539682EAA7529E4BE2DE36 + ) + + S( + 0x2CDB85FE13CB7DE39C1C7385BE9F38E8BDE9963CCBECD96281C4DF3ACA38F537 + ) + + X( + 0x82AE98A95AE76E389354F0EC660CF071309EA2D2CB14ADB6543106B790BE27FD + ) + + Y( + 0x77B2CDC82C3AA8F2CF21E6257C197D75F84DCD0BC2FF8875C3E245C0E0874751 + ), + id="invalid_r1_sig_but_valid_k1_sig", + ), + ], +) +@pytest.mark.parametrize("precompile_address", [Spec.P256VERIFY], ids=[""]) +def test_invalid( + state_test: StateTestFiller, pre: Alloc, post: dict, tx: Transaction +) -> None: + """ + Negative mainnet test for the P256VERIFY precompile. + + The signature actually is a valid secp256k1 signature, + so this is an interesting test case. + """ + state_test(env=Environment(), pre=pre, post=post, tx=tx)