Skip to content

Basic implementation of EIP-7932 #1292

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

Draft
wants to merge 14 commits into
base: forks/osaka
Choose a base branch
from
6 changes: 6 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ packages =
ethereum/prague/vm/instructions
ethereum/prague/vm/precompiled_contracts
ethereum/prague/vm/precompiled_contracts/bls12_381
ethereum/osaka
ethereum/osaka/utils
ethereum/osaka/vm
ethereum/osaka/vm/instructions
ethereum/osaka/vm/precompiled_contracts
ethereum/osaka/vm/precompiled_contracts/bls12_381


package_dir =
Expand Down
4 changes: 2 additions & 2 deletions src/ethereum/cancun/vm/gas.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,8 @@ def calculate_excess_blob_gas(parent_header: Header) -> U64:
parent_blob_gas = excess_blob_gas + blob_gas_used
if parent_blob_gas < TARGET_BLOB_GAS_PER_BLOCK:
return U64(0)
else:
return parent_blob_gas - TARGET_BLOB_GAS_PER_BLOCK

return parent_blob_gas - TARGET_BLOB_GAS_PER_BLOCK


def calculate_total_blob_gas(tx: Transaction) -> U64:
Expand Down
7 changes: 7 additions & 0 deletions src/ethereum/osaka/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""
The Osaka fork.
"""

from ethereum.fork_criteria import Unscheduled

FORK_CRITERIA = Unscheduled()
141 changes: 141 additions & 0 deletions src/ethereum/osaka/blocks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
"""
A `Block` is a single link in the chain that is Ethereum. Each `Block` contains
a `Header` and zero or more transactions. Each `Header` contains associated
metadata like the block number, parent block hash, and how much gas was
consumed by its transactions.

Together, these blocks form a cryptographically secure journal recording the
history of all state transitions that have happened since the genesis of the
chain.
"""
from dataclasses import dataclass
from typing import Tuple, Union

from ethereum_rlp import rlp
from ethereum_types.bytes import Bytes, Bytes8, Bytes32
from ethereum_types.frozen import slotted_freezable
from ethereum_types.numeric import U64, U256, Uint

from ..crypto.hash import Hash32
from .fork_types import Address, Bloom, Root
from .transactions import (
AccessListTransaction,
AlgorithmicTransaction,
BlobTransaction,
FeeMarketTransaction,
LegacyTransaction,
SetCodeTransaction,
Transaction,
decode_transaction,
)


@slotted_freezable
@dataclass
class Withdrawal:
"""
Withdrawals that have been validated on the consensus layer.
"""

index: U64
validator_index: U64
address: Address
amount: U256


@slotted_freezable
@dataclass
class Header:
"""
Header portion of a block on the chain.
"""

parent_hash: Hash32
ommers_hash: Hash32
coinbase: Address
state_root: Root
transactions_root: Root
receipt_root: Root
bloom: Bloom
difficulty: Uint
number: Uint
gas_limit: Uint
gas_used: Uint
timestamp: U256
extra_data: Bytes
prev_randao: Bytes32
nonce: Bytes8
base_fee_per_gas: Uint
withdrawals_root: Root
blob_gas_used: U64
excess_blob_gas: U64
parent_beacon_block_root: Root
requests_hash: Hash32


@slotted_freezable
@dataclass
class Block:
"""
A complete block.
"""

header: Header
transactions: Tuple[Union[Bytes, LegacyTransaction], ...]
ommers: Tuple[Header, ...]
withdrawals: Tuple[Withdrawal, ...]


@slotted_freezable
@dataclass
class Log:
"""
Data record produced during the execution of a transaction.
"""

address: Address
topics: Tuple[Hash32, ...]
data: Bytes


@slotted_freezable
@dataclass
class Receipt:
"""
Result of a transaction.
"""

succeeded: bool
cumulative_gas_used: Uint
bloom: Bloom
logs: Tuple[Log, ...]


def encode_receipt(tx: Transaction, receipt: Receipt) -> Union[Bytes, Receipt]:
"""
Encodes a receipt.
"""
if isinstance(tx, AccessListTransaction):
return b"\x01" + rlp.encode(receipt)
elif isinstance(tx, FeeMarketTransaction):
return b"\x02" + rlp.encode(receipt)
elif isinstance(tx, BlobTransaction):
return b"\x03" + rlp.encode(receipt)
elif isinstance(tx, SetCodeTransaction):
return b"\x04" + rlp.encode(receipt)
elif isinstance(tx, AlgorithmicTransaction):
unwrapped_tx = decode_transaction(tx.parent)
return encode_receipt(unwrapped_tx, receipt)
else:
return receipt


def decode_receipt(receipt: Union[Bytes, Receipt]) -> Receipt:
"""
Decodes a receipt.
"""
if isinstance(receipt, Bytes):
assert receipt[0] in (1, 2, 3, 4, 7)
return rlp.decode_to(Receipt, receipt[1:])
else:
return receipt
86 changes: 86 additions & 0 deletions src/ethereum/osaka/bloom.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"""
Ethereum Logs Bloom
^^^^^^^^^^^^^^^^^^^

.. contents:: Table of Contents
:backlinks: none
:local:

Introduction
------------

This modules defines functions for calculating bloom filters of logs. For the
general theory of bloom filters see e.g. `Wikipedia
<https://en.wikipedia.org/wiki/Bloom_filter>`_. Bloom filters are used to allow
for efficient searching of logs by address and/or topic, by rapidly
eliminating blocks and receipts from their search.
"""

from typing import Tuple

from ethereum_types.bytes import Bytes
from ethereum_types.numeric import Uint

from ethereum.crypto.hash import keccak256

from .blocks import Log
from .fork_types import Bloom


def add_to_bloom(bloom: bytearray, bloom_entry: Bytes) -> None:
"""
Add a bloom entry to the bloom filter (`bloom`).

The number of hash functions used is 3. They are calculated by taking the
least significant 11 bits from the first 3 16-bit words of the
`keccak_256()` hash of `bloom_entry`.

Parameters
----------
bloom :
The bloom filter.
bloom_entry :
An entry which is to be added to bloom filter.
"""
hash = keccak256(bloom_entry)

for idx in (0, 2, 4):
# Obtain the least significant 11 bits from the pair of bytes
# (16 bits), and set this bit in bloom bytearray.
# The obtained bit is 0-indexed in the bloom filter from the least
# significant bit to the most significant bit.
bit_to_set = Uint.from_be_bytes(hash[idx : idx + 2]) & Uint(0x07FF)
# Below is the index of the bit in the bytearray (where 0-indexed
# byte is the most significant byte)
bit_index = 0x07FF - int(bit_to_set)

byte_index = bit_index // 8
bit_value = 1 << (7 - (bit_index % 8))
bloom[byte_index] = bloom[byte_index] | bit_value


def logs_bloom(logs: Tuple[Log, ...]) -> Bloom:
"""
Obtain the logs bloom from a list of log entries.

The address and each topic of a log are added to the bloom filter.

Parameters
----------
logs :
List of logs for which the logs bloom is to be obtained.

Returns
-------
logs_bloom : `Bloom`
The logs bloom obtained which is 256 bytes with some bits set as per
the caller address and the log topics.
"""
bloom: bytearray = bytearray(b"\x00" * 256)

for log in logs:
add_to_bloom(bloom, log.address)
for topic in log.topics:
add_to_bloom(bloom, topic)

return Bloom(bloom)
24 changes: 24 additions & 0 deletions src/ethereum/osaka/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""
Exceptions specific to this fork.
"""

from typing import Final

from ethereum.exceptions import InvalidTransaction


class TransactionTypeError(InvalidTransaction):
"""
Unknown [EIP-2718] transaction type byte.

[EIP-2718]: https://eips.ethereum.org/EIPS/eip-2718
"""

transaction_type: Final[int]
"""
The type byte of the transaction that caused the error.
"""

def __init__(self, transaction_type: int):
super().__init__(f"unknown transaction type `{transaction_type}`")
self.transaction_type = transaction_type
Loading