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

EIP-4844 and EIP-7516 support #2151

Merged
merged 16 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,12 @@ jobs:
- image: cimg/python:3.11
environment:
TOXENV: py311-native-blockchain-cancun_eip5656
py311-native-blockchain-cancun_eip4844_7516:
<<: *common
docker:
- image: cimg/python:3.11
environment:
TOXENV: py311-native-blockchain-cancun_eip4844_7516


workflows:
Expand Down Expand Up @@ -427,3 +433,4 @@ workflows:
- py311-wheel-windows

- py311-native-blockchain-cancun_eip5656
- py311-native-blockchain-cancun_eip4844_7516
3 changes: 2 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
include LICENSE
include README.md

recursive-include tests *
include eth/_utils/kzg_trusted_setup.txt

recursive-include tests *
global-include *.pyi

recursive-exclude * __pycache__
Expand Down
4,163 changes: 4,163 additions & 0 deletions eth/_utils/kzg_trusted_setup.txt

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion eth/_utils/transactions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import (
NamedTuple,
Union,
)

from eth_keys import (
Expand Down Expand Up @@ -116,7 +117,7 @@ class IntrinsicGasSchedule(NamedTuple):

def calculate_intrinsic_gas(
gas_schedule: IntrinsicGasSchedule,
transaction: SignedTransactionAPI,
transaction: Union[SignedTransactionAPI, UnsignedTransactionAPI],
) -> int:
num_zero_bytes = transaction.data.count(b"\x00")
num_non_zero_bytes = len(transaction.data) - num_zero_bytes
Expand Down
85 changes: 85 additions & 0 deletions eth/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
abstractmethod,
)
from typing import (
TYPE_CHECKING,
Any,
Callable,
ClassVar,
Expand Down Expand Up @@ -53,6 +54,11 @@
VMConfiguration,
)

if TYPE_CHECKING:
from eth.vm.forks.cancun.transactions import (
BlobTransaction,
)

T = TypeVar("T")

# A decoded RLP object of unknown interpretation, with a maximum "depth" of 1.
Expand Down Expand Up @@ -202,6 +208,22 @@ def parent_beacon_block_root(self) -> Optional[Hash32]:
"""
...

@property
@abstractmethod
def blob_gas_used(self) -> int:
"""
Return blob gas used.
"""
...

@property
@abstractmethod
def excess_blob_gas(self) -> int:
"""
Return excess blob gas.
"""
...


class LogAPI(ABC):
"""
Expand Down Expand Up @@ -464,6 +486,16 @@ def hash(self) -> Hash32:
def chain_id(self) -> Optional[int]:
...

@property
@abstractmethod
def max_fee_per_blob_gas(self) -> int:
...

@property
@abstractmethod
def blob_versioned_hashes(self) -> Sequence[Hash32]:
...


class LegacyTransactionFieldsAPI(TransactionFieldsAPI):
@property
Expand Down Expand Up @@ -1578,6 +1610,14 @@ def origin(self) -> Address:
"""
...

@property
@abstractmethod
def blob_versioned_hashes(self) -> Sequence[Hash32]:
"""
Return the blob versioned hashes of the transaction context.
"""
...


class MemoryAPI(ABC):
"""
Expand Down Expand Up @@ -1926,6 +1966,22 @@ def base_fee_per_gas(self) -> Optional[int]:
"""
...

@property
@abstractmethod
def blob_gas_used(self) -> Optional[int]:
"""
Return the blob gas used of the block
"""
...

@property
@abstractmethod
def excess_blob_gas(self) -> Optional[int]:
"""
Return the excess blob gas of the block
"""
...


class ComputationAPI(
ContextManager["ComputationAPI"],
Expand Down Expand Up @@ -2781,6 +2837,15 @@ def finalize_computation(
"""
...

# -- post-cancun -- #

@abstractmethod
def calc_data_fee(self, transaction: "BlobTransaction") -> int:
"""
For Cancun and later, calculate the data fee for a transaction.
"""
...


class ConfigurableAPI(ABC):
"""
Expand Down Expand Up @@ -2923,6 +2988,17 @@ def get_tip(self, transaction: SignedTransactionAPI) -> int:
"""
...

@property
@abstractmethod
def blob_base_fee(self) -> int:
"""
Return the current ``blob_base_fee`` from the current :attr:`~execution_context`

Raises a ``NotImplementedError`` if called in an execution context
prior to the Cancun hard fork.
"""
...

#
# Access to account db
#
Expand Down Expand Up @@ -3592,6 +3668,15 @@ def add_receipt_to_header(
"""
...

@abstractmethod
def increment_blob_gas_used(
self, old_header: BlockHeaderAPI, transaction: TransactionFieldsAPI
) -> BlockHeaderAPI:
"""
Update the header by incrementing the blob_gas_used for the transaction.
"""
...

@classmethod
@abstractmethod
def compute_difficulty(cls, parent_header: BlockHeaderAPI, timestamp: int) -> int:
Expand Down
80 changes: 80 additions & 0 deletions eth/precompiles/point_evaluation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import hashlib
import os

from ckzg import (
load_trusted_setup,
verify_kzg_proof,
)
from eth_typing import (
Hash32,
)

from eth.abc import (
ComputationAPI,
)
from eth.exceptions import (
VMError,
)
from eth.vm.forks.cancun.constants import (
BLS_MODULUS,
FIELD_ELEMENTS_PER_BLOB,
POINT_EVALUATION_PRECOMPILE_GAS,
VERSIONED_HASH_VERSION_KZG,
)

# load path from ../_utils/kzg_trusted_setup.txt
TRUSTED_SETUP_PATH = os.path.join(
os.path.dirname(__file__), "..", "_utils", "kzg_trusted_setup.txt"
)


def kzg_to_versioned_hash(commitment: bytes) -> Hash32:
return VERSIONED_HASH_VERSION_KZG + hashlib.sha256(commitment).digest()[1:]


def point_evaluation_precompile(computation: ComputationAPI) -> ComputationAPI:
"""
Verify p(z) = y given commitment that corresponds to the polynomial p(x) and a KZG
proof. Also verify that the provided commitment matches the provided versioned_hash.
"""
computation.consume_gas(
POINT_EVALUATION_PRECOMPILE_GAS, reason="Point Evaluation Precompile"
)

input_ = computation.msg.data_as_bytes

# The data is encoded as follows: versioned_hash | z | y | commitment | proof
# with z and y being padded 32 byte big endian values
try:
assert len(input_) == 192
except AssertionError:
raise VMError("Point evaluation invalid input length.")

versioned_hash = input_[:32]
z = input_[32:64]
y = input_[64:96]
commitment = input_[96:144]
proof = input_[144:192]

# Verify commitment matches versioned_hash
try:
assert kzg_to_versioned_hash(commitment) == versioned_hash
except AssertionError:
raise VMError("Point evaluation commitment does not match versioned hash.")

# Verify KZG proof with z and y in big endian format
try:
assert verify_kzg_proof(
commitment, z, y, proof, load_trusted_setup(TRUSTED_SETUP_PATH)
)
except (AssertionError, RuntimeError):
# RuntimeError is raised when the KZG proof verification fails within the C code
# from the method itself
raise VMError("Point evaluation KZG proof verification failed.")

# Return FIELD_ELEMENTS_PER_BLOB and BLS_MODULUS as padded 32 byte big endian values
computation.output = FIELD_ELEMENTS_PER_BLOB.to_bytes(
32, "big"
) + BLS_MODULUS.to_bytes(32, "big")

return computation
8 changes: 8 additions & 0 deletions eth/rlp/headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,14 @@ def base_fee_per_gas(self) -> int:
def withdrawals_root(self) -> Optional[Hash32]:
raise AttributeError("Withdrawals root not available until Shanghai fork")

@property
def blob_gas_used(self) -> int:
raise AttributeError("Blob gas used not available until Cancun fork")

@property
def excess_blob_gas(self) -> int:
raise AttributeError("Excess blob gas not available until Cancun fork")

@property
def parent_beacon_block_root(self) -> Optional[Hash32]:
raise AttributeError("Parent beacon block root not available until Cancun fork")
24 changes: 18 additions & 6 deletions eth/tools/_utils/normalization.py
Original file line number Diff line number Diff line change
Expand Up @@ -514,13 +514,18 @@ def normalize_signed_transaction(transaction: Dict[str, Any]) -> Dict[str, Any]:
"gasPrice": to_int(transaction["gasPrice"]),
"chainId": to_int(transaction["chainId"]),
}
elif type_id == 2:
elif type_id in (2, 3):
custom_fields = {
"type": type_id,
"chainId": to_int(transaction["chainId"]),
"maxFeePerGas": to_int(transaction["maxFeePerGas"]),
"maxPriorityFeePerGas": to_int(transaction["maxPriorityFeePerGas"]),
}
if type_id == 3:
if "maxFeePerBlobGas" in transaction:
custom_fields["maxFeePerBlobGas"] = to_int(
transaction["maxFeePerBlobGas"]
)
else:
raise ValidationError(f"Did not recognize transaction type {type_id}")
else:
Expand Down Expand Up @@ -576,11 +581,18 @@ def normalize_block_header(header: Dict[str, Any]) -> Dict[str, Any]:
normalized_header["blocknumber"] = to_int(header["blocknumber"])
if "baseFeePerGas" in header:
normalized_header["baseFeePerGas"] = to_int(header["baseFeePerGas"])
if "chainname" in header:
normalized_header["chainname"] = header["chainname"]
if "chainnetwork" in header:
normalized_header["chainnetwork"] = header["chainnetwork"]
return normalized_header
if "withdrawalsRoot" in header:
normalized_header["withdrawalsRoot"] = decode_hex(header["withdrawalsRoot"])
if "blobGasUsed" in header:
normalized_header["blobGasUsed"] = to_int(header["blobGasUsed"])
if "excessBlobGas" in header:
normalized_header["excessBlobGas"] = to_int(header["excessBlobGas"])
if "parentBeaconBlockRoot" in header:
normalized_header["parentBeaconBlockRoot"] = decode_hex(
header["parentBeaconBlockRoot"]
)

return merge(header, normalized_header)


def normalize_block(block: Dict[str, Any]) -> Dict[str, Any]:
Expand Down