Skip to content

Commit

Permalink
Test blobbasefee without transaction
Browse files Browse the repository at this point in the history
- Uses new pyrevm version paradigmxyz/pyrevm#16
  • Loading branch information
DanielSchiavini committed Apr 25, 2024
1 parent 49f42f2 commit a59b2b2
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 51 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
17 changes: 16 additions & 1 deletion tests/evm_backends/base_env.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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

Expand All @@ -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

Expand Down Expand Up @@ -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)
20 changes: 15 additions & 5 deletions tests/evm_backends/pyevm_env.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
from contextlib import contextmanager
from typing import Optional

import rlp
from cached_property import cached_property
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
29 changes: 16 additions & 13 deletions tests/evm_backends/revm_env.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
51 changes: 20 additions & 31 deletions tests/functional/codegen/environment_variables/test_blobbasefee.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit a59b2b2

Please sign in to comment.