From a59b2b26cd1c43bdf3b6843c70eeb8ac6f163beb Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Thu, 25 Apr 2024 09:13:12 +0200 Subject: [PATCH] Test blobbasefee without transaction - Uses new pyrevm version https://github.com/paradigmxyz/pyrevm/pull/16 --- setup.py | 2 +- tests/evm_backends/base_env.py | 17 ++++++- tests/evm_backends/pyevm_env.py | 20 ++++++-- tests/evm_backends/revm_env.py | 29 ++++++----- .../environment_variables/test_blobbasefee.py | 51 ++++++++----------- 5 files changed, 68 insertions(+), 51 deletions(-) diff --git a/setup.py b/setup.py index d785398481..4db454ebe4 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ "eth-stdlib==0.2.7", "setuptools", "hexbytes>=1.2", - "pyrevm>=0.3.1", + "pyrevm @ git+https://github.com/DanielSchiavini/pyrevm.git@725cbfc", ], "lint": [ "black==23.12.0", diff --git a/tests/evm_backends/base_env.py b/tests/evm_backends/base_env.py index 9e7d031b2c..f4caa83e67 100644 --- a/tests/evm_backends/base_env.py +++ b/tests/evm_backends/base_env.py @@ -1,8 +1,11 @@ import json from contextlib import contextmanager from dataclasses import dataclass -from typing import Callable +from typing import Callable, Optional +from ckzg import blob_to_kzg_commitment, load_trusted_setup +from eth.precompiles.point_evaluation import kzg_to_versioned_hash +from eth_account._utils.typed_transactions.base import TRUSTED_SETUP from eth_keys.datatypes import PrivateKey from eth_utils import to_checksum_address @@ -165,6 +168,7 @@ def execute_code( gas: int | None = None, gas_price: int = 0, is_modifying: bool = True, + blob_hashes: Optional[list[bytes]] = None, # for blobbasefee >= Cancun ) -> bytes: raise NotImplementedError # must be implemented by subclasses @@ -177,6 +181,12 @@ def get_code(self, address: str) -> bytes: def time_travel(self, num_blocks=1) -> None: raise NotImplementedError # must be implemented by subclasses + def get_excess_blob_gas(self) -> Optional[int]: + raise NotImplementedError # must be implemented by subclasses + + def set_excess_blob_gas(self, param): + raise NotImplementedError # must be implemented by subclasses + def _deploy(self, code: bytes, value: int, gas: int | None = None) -> str: raise NotImplementedError # must be implemented by subclasses @@ -211,3 +221,8 @@ def _compile( parse_vyper_source(source_code) # Test grammar. json.dumps(out["metadata"]) # test metadata is json serializable return out["abi"], bytes.fromhex(out["bytecode"].removeprefix("0x")) + + +def kzg_hash(blob: bytes) -> bytes: + commitment = blob_to_kzg_commitment(blob, load_trusted_setup(TRUSTED_SETUP)) + return kzg_to_versioned_hash(commitment) diff --git a/tests/evm_backends/pyevm_env.py b/tests/evm_backends/pyevm_env.py index da1d7815b5..b3379951d8 100644 --- a/tests/evm_backends/pyevm_env.py +++ b/tests/evm_backends/pyevm_env.py @@ -1,5 +1,6 @@ import logging from contextlib import contextmanager +from typing import Optional import rlp from cached_property import cached_property @@ -65,6 +66,12 @@ def _state(self) -> StateAPI: def _vm(self): return self._chain.get_vm() + @cached_property + def _context(self) -> ExecutionContext: + context = self._state.execution_context + assert isinstance(context, ExecutionContext) + return context + @contextmanager def anchor(self): snapshot_id = self._state.snapshot() @@ -110,6 +117,7 @@ def execute_code( gas: int | None = None, gas_price: int = 0, is_modifying: bool = True, + blob_hashes: Optional[list[bytes]] = None, # for blobbasefee >= Cancun ): data = data if isinstance(data, bytes) else bytes.fromhex(data.removeprefix("0x")) sender = _addr(sender or self.deployer) @@ -157,12 +165,14 @@ def time_travel(self, num_blocks=1) -> None: """ Move the block number forward by `num_blocks` and the timestamp forward by `time_delta`. """ + self._context._block_number += num_blocks + self._context._timestamp += num_blocks - # Cast since ExecutionContextAPI does not have the properties we need to change - context = self._state.execution_context - assert isinstance(context, ExecutionContext) - context._block_number += num_blocks - context._timestamp += num_blocks + def get_excess_blob_gas(self) -> Optional[int]: + return self._context.excess_blob_gas + + def set_excess_blob_gas(self, param): + self._context._excess_blob_gas = param def _deploy(self, code: bytes, value: int, gas: int = None) -> str: sender = _addr(self.deployer) diff --git a/tests/evm_backends/revm_env.py b/tests/evm_backends/revm_env.py index 0a3d9c9dc7..95a615cc8e 100644 --- a/tests/evm_backends/revm_env.py +++ b/tests/evm_backends/revm_env.py @@ -1,5 +1,6 @@ import re from contextlib import contextmanager +from typing import Optional from eth_keys.datatypes import PrivateKey from pyrevm import EVM, BlockEnv, Env @@ -76,8 +77,12 @@ def execute_code( gas: int | None = None, gas_price: int = 0, is_modifying: bool = True, + blob_hashes: Optional[list[bytes]] = None, # for blobbasefee >= Cancun ): data = data if isinstance(data, bytes) else bytes.fromhex(data.removeprefix("0x")) + if blob_hashes is not None: + self._evm.env.tx.blob_hashes = blob_hashes + try: return self._evm.message_call( to=to, @@ -105,19 +110,17 @@ def time_travel(self, num_blocks=1) -> None: """ Move the block number forward by `num_blocks` and the timestamp forward by `time_delta`. """ - block = self._evm.env.block - self._evm.set_block_env( - BlockEnv( - number=block.number + num_blocks, - coinbase=block.coinbase, - timestamp=block.timestamp + num_blocks, - difficulty=block.difficulty, - prevrandao=block.prevrandao, - basefee=block.basefee, - gas_limit=block.gas_limit, - excess_blob_gas=block.excess_blob_gas, - ) - ) + self._evm.env.block.number += num_blocks + self._evm.env.block.timestamp += num_blocks + + def get_excess_blob_gas(self) -> Optional[int]: + return self._evm.env.block.excess_blob_gas + + def get_blob_gasprice(self) -> Optional[int]: + return self._evm.env.block.blob_gasprice + + def set_excess_blob_gas(self, value): + self._evm.env.block.excess_blob_gas = value def _deploy(self, code: bytes, value: int, gas: int = None) -> str: return self._evm.deploy(self.deployer, code, value, gas) diff --git a/tests/functional/codegen/environment_variables/test_blobbasefee.py b/tests/functional/codegen/environment_variables/test_blobbasefee.py index b92824491c..4440f665ac 100644 --- a/tests/functional/codegen/environment_variables/test_blobbasefee.py +++ b/tests/functional/codegen/environment_variables/test_blobbasefee.py @@ -2,49 +2,38 @@ from eth.vm.forks.cancun.constants import BLOB_BASE_FEE_UPDATE_FRACTION, MIN_BLOB_BASE_FEE from eth.vm.forks.cancun.state import fake_exponential +from tests.evm_backends.base_env import kzg_hash + @pytest.mark.requires_evm_version("cancun") -def test_blobbasefee(get_contract_with_gas_estimation, w3): +def test_blobbasefee(env, get_contract): code = """ @external @view def get_blobbasefee() -> uint256: return block.blobbasefee """ - c = get_contract_with_gas_estimation(code) + c = get_contract(code) assert c.get_blobbasefee() == MIN_BLOB_BASE_FEE - a0 = w3.eth.account.from_key(f"0x{'00' * 31}01") + env.set_balance(env.deployer, 10**20) + env.set_excess_blob_gas(10**6) text = b"Vyper is the language of the sneks" # Blobs contain 4096 32-byte field elements. blob_data = text.rjust(32 * 4096) - - for _i in range(10): - tx = { - "type": 3, - "chainId": 1337, - "from": a0.address, - "to": "0xb45BEc6eeCA2a09f4689Dd308F550Ad7855051B5", # random address - "value": 0, - "gas": 21000, - "maxFeePerGas": 10**10, - "maxPriorityFeePerGas": 10**10, - "maxFeePerBlobGas": 10**10, - "nonce": w3.eth.get_transaction_count(a0.address), - } - - signed = a0.sign_transaction(tx, blobs=[blob_data] * 6) - w3.eth.send_raw_transaction(signed.rawTransaction) - - block = w3.eth.get_block("latest") - excess_blob_gas = block["excessBlobGas"] - expected_blobbasefee = fake_exponential( - MIN_BLOB_BASE_FEE, excess_blob_gas, BLOB_BASE_FEE_UPDATE_FRACTION - ) - - assert c.get_blobbasefee() == expected_blobbasefee - - # sanity check that blobbasefee has increased above the minimum - assert c.get_blobbasefee() > MIN_BLOB_BASE_FEE + blob_hashes = [kzg_hash(blob_data)] * 6 + + env.execute_code( + "0xb45BEc6eeCA2a09f4689Dd308F550Ad7855051B5", # random address + gas=21000, + gas_price=10**10, + blob_hashes=blob_hashes, + ) + + excess_blob_gas = env.get_excess_blob_gas() + expected_blobbasefee = fake_exponential( + MIN_BLOB_BASE_FEE, excess_blob_gas, BLOB_BASE_FEE_UPDATE_FRACTION + ) + assert c.get_blobbasefee() == expected_blobbasefee