Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ jobs:
- name: Run pypy3 tests
run: tox -e pypy3
env:
PYPY_GC_MAX: "10G"
PYPY_GC_MAX: "2G"
PYPY_GC_MIN: "1G"

json_infra:
runs-on: [self-hosted-ghr, size-xl-x64]
Expand Down
8 changes: 6 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ In particular, we appreciate support in the following areas:
- Fixing and responding to [issues](https://github.com/ethereum/execution-specs/issues), especially those tagged as [E-easy](https://github.com/ethereum/execution-specs/labels/E-easy) which are meant as introductory issues for external contributors.
- Improving the documentation.

> [!IMPORTANT]
> Generally, we do not assign issues to external contributors. If you want to work on an issue, you are very welcome to go ahead and make a pull request. We would, hoever, be happy to answer questions you may have before you start implementing.

For details about EELS usage and building, please refer the [README](https://github.com/ethereum/execution-specs/blob/master/README.md#usage)

Expand Down Expand Up @@ -36,7 +38,7 @@ When creating pull requests affecting multiple forks, we recommended submitting
2. Apply the changes across the other forks, push them, and mark the pull request as ready for review.

This saves you having to apply code review feedback repeatedly for each fork.

### Development

Running the tests necessary to merge into the repository requires:
Expand Down Expand Up @@ -82,7 +84,9 @@ Note: Make sure to run the EVM trace on a small number of tests at a time. The l
Below is an example.

```bash
pytest tests/frontier/test_state_transition.py -k 'test_general_state_tests_new' --evm-trace
uv run --extra test \
pytest 'tests/json_infra/test_state_tests.py::test_state_tests_frontier[stAttackTest - ContractCreationSpam - 0]' \
--evm_trace
```


Expand Down
17 changes: 10 additions & 7 deletions src/ethereum/forks/dao_fork/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,7 @@ def calculate_block_difficulty(
set for the genesis block since it has no parent. So, a block
can't be less difficult than the genesis block, therefore each block's
difficulty is set to the maximum value between the calculated
difficulty and the ``GENESIS_DIFFICULTY``.
difficulty and the ``MINIMUM_DIFFICULTY``.

Parameters
----------
Expand All @@ -795,12 +795,15 @@ def calculate_block_difficulty(
Computed difficulty for a block.

"""
offset = (
int(parent_difficulty)
// 2048
* max(1 - int(block_timestamp - parent_timestamp) // 10, -99)
)
difficulty = int(parent_difficulty) + offset
# Precompute helpers to avoid repeated int() conversions while
# keeping behavior.

parent_difficulty_int = int(parent_difficulty)
time_delta = int(block_timestamp) - int(parent_timestamp)

offset = (parent_difficulty_int // 2048) * max(1 - (time_delta // 10), -99)
difficulty = parent_difficulty_int + offset

# Historical Note: The difficulty bomb was not present in Ethereum at the
# start of Frontier, but was added shortly after launch. However since the
# bomb has no effect prior to block 200000 we pretend it existed from
Expand Down
40 changes: 26 additions & 14 deletions src/ethereum/forks/frontier/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -743,13 +743,22 @@ def calculate_block_difficulty(
"""
Computes difficulty of a block using its header and parent header.

The difficulty of a block is determined by the time the block was created
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This for example

after its parent. If a block's timestamp is more than 13 seconds after its
parent block then its difficulty is set as the difference between the
parent's difficulty and the ``max_adjustment_delta``. Otherwise, if the
time between parent and child blocks is too small (under 13 seconds) then,
to avoid mass forking, the block's difficulty is set to the sum of the
delta and the parent's difficulty.
The difficulty is determined by the time the block was created after its
parent. The ``offset`` is calculated using the parent block's difficulty,
``parent_difficulty``, and the timestamp between blocks. This offset is
then added to the parent difficulty and is stored as the ``difficulty``
variable. If the time between the block and its parent is too short, the
offset will result in a positive number thus making the sum of
``parent_difficulty`` and ``offset`` to be a greater value in order to
avoid mass forking. But, if the time is long enough, then the offset
results in a negative value making the block less difficult than
its parent.

The base standard for a block's difficulty is the predefined value
set for the genesis block since it has no parent. So, a block
can't be less difficult than the genesis block, therefore each block's
difficulty is set to the maximum value between the calculated
difficulty and the ``MINIMUM_DIFFICULTY``.

Parameters
----------
Expand All @@ -768,11 +777,14 @@ def calculate_block_difficulty(
Computed difficulty for a block.

"""
max_adjustment_delta = parent_difficulty // Uint(2048)
if block_timestamp < parent_timestamp + U256(13):
difficulty = parent_difficulty + max_adjustment_delta
else: # block_timestamp >= parent_timestamp + 13
difficulty = parent_difficulty - max_adjustment_delta
# Precompute helpers to avoid repeated int() conversions while
# keeping behavior.

parent_difficulty_int = int(parent_difficulty)
time_delta = int(block_timestamp) - int(parent_timestamp)

offset = (parent_difficulty_int // 2048) * max(1 - (time_delta // 10), -99)
difficulty = parent_difficulty_int + offset

# Historical Note: The difficulty bomb was not present in Ethereum at the
# start of Frontier, but was added shortly after launch. However since the
Expand All @@ -781,9 +793,9 @@ def calculate_block_difficulty(
# See https://github.com/ethereum/go-ethereum/pull/1588
num_bomb_periods = (int(block_number) // 100000) - 2
if num_bomb_periods >= 0:
difficulty += Uint(2**num_bomb_periods)
difficulty += 2**num_bomb_periods

# Some clients raise the difficulty to `MINIMUM_DIFFICULTY` prior to adding
# the bomb. This bug does not matter because the difficulty is always much
# greater than `MINIMUM_DIFFICULTY` on Mainnet.
return max(difficulty, MINIMUM_DIFFICULTY)
return Uint(max(difficulty, int(MINIMUM_DIFFICULTY)))
55 changes: 22 additions & 33 deletions src/ethereum/forks/homestead/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,6 @@ def check_gas_limit(gas_limit: Uint, parent_gas_limit: Uint) -> bool:

return True


def calculate_block_difficulty(
block_number: Uint,
block_timestamp: U256,
Expand All @@ -751,48 +750,38 @@ def calculate_block_difficulty(
offset will result in a positive number thus making the sum of
``parent_difficulty`` and ``offset`` to be a greater value in order to
avoid mass forking. But, if the time is long enough, then the offset
results in a negative value making the block less difficult than
its parent.

The base standard for a block's difficulty is the predefined value
set for the genesis block since it has no parent. So, a block
can't be less difficult than the genesis block, therefore each block's
difficulty is set to the maximum value between the calculated
difficulty and the ``GENESIS_DIFFICULTY``.
results in a negative value making the block less difficult than its
parent.

Parameters
----------
block_number :
Block number of the block.
block_timestamp :
Timestamp of the block.
parent_timestamp :
Timestamp of the parent block.
parent_difficulty :
difficulty of the parent block.

Returns
-------
difficulty : `ethereum.base_types.Uint`
Computed difficulty for a block.
The base standard for a block's difficulty is the predefined value set for
the genesis block since it has no parent. So, a block can't be less
difficult than the genesis block, therefore each block's difficulty is set
to the maximum value between the calculated difficulty and the
``MINIMUM_DIFFICULTY``.

Homestead difficulty: same behavior, fewer int() casts.
"""
offset = (
int(parent_difficulty)
// 2048
* max(1 - int(block_timestamp - parent_timestamp) // 10, -99)
# Cast only when necessary for signed arithmetic, avoiding intermediate variables.
parent_difficulty_int = int(parent_difficulty)
time_delta = int(block_timestamp) - int(parent_timestamp)

offset = (parent_difficulty_int // 2048) * max(
1 - (time_delta // 10),
-99
)
difficulty = int(parent_difficulty) + offset
difficulty = parent_difficulty_int + offset

# Historical Note: The difficulty bomb was not present in Ethereum at the
# start of Frontier, but was added shortly after launch. However since the
# bomb has no effect prior to block 200000 we pretend it existed from
# genesis.
# See https://github.com/ethereum/go-ethereum/pull/1588

num_bomb_periods = (int(block_number) // 100000) - 2
if num_bomb_periods >= 0:
difficulty += 2**num_bomb_periods

# Some clients raise the difficulty to `MINIMUM_DIFFICULTY` prior to adding
# the bomb. This bug does not matter because the difficulty is always much
# greater than `MINIMUM_DIFFICULTY` on Mainnet.
return Uint(max(difficulty, int(MINIMUM_DIFFICULTY)))
# Some clients raise the difficulty to MINIMUM_DIFFICULTY prior to adding
# the bomb. This bug does not matter because difficulty is always greater
# than MINIMUM_DIFFICULTY on Mainnet.
return Uint(max(difficulty, int(MINIMUM_DIFFICULTY)))
17 changes: 10 additions & 7 deletions src/ethereum/forks/spurious_dragon/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -767,7 +767,7 @@ def calculate_block_difficulty(
set for the genesis block since it has no parent. So, a block
can't be less difficult than the genesis block, therefore each block's
difficulty is set to the maximum value between the calculated
difficulty and the ``GENESIS_DIFFICULTY``.
difficulty and the ``MINIMUM_DIFFICULTY``.

Parameters
----------
Expand All @@ -786,12 +786,15 @@ def calculate_block_difficulty(
Computed difficulty for a block.

"""
offset = (
int(parent_difficulty)
// 2048
* max(1 - int(block_timestamp - parent_timestamp) // 10, -99)
)
difficulty = int(parent_difficulty) + offset
# Precompute helpers to avoid repeated int() conversions while
# keeping behavior.

parent_difficulty_int = int(parent_difficulty)
time_delta = int(block_timestamp) - int(parent_timestamp)

offset = (parent_difficulty_int // 2048) * max(1 - (time_delta // 10), -99)
difficulty = parent_difficulty_int + offset

# Historical Note: The difficulty bomb was not present in Ethereum at the
# start of Frontier, but was added shortly after launch. However since the
# bomb has no effect prior to block 200000 we pretend it existed from
Expand Down
17 changes: 10 additions & 7 deletions src/ethereum/forks/tangerine_whistle/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,7 @@ def calculate_block_difficulty(
set for the genesis block since it has no parent. So, a block
can't be less difficult than the genesis block, therefore each block's
difficulty is set to the maximum value between the calculated
difficulty and the ``GENESIS_DIFFICULTY``.
difficulty and the ``MINIMUM_DIFFICULTY``.

Parameters
----------
Expand All @@ -777,12 +777,15 @@ def calculate_block_difficulty(
Computed difficulty for a block.

"""
offset = (
int(parent_difficulty)
// 2048
* max(1 - int(block_timestamp - parent_timestamp) // 10, -99)
)
difficulty = int(parent_difficulty) + offset
# Precompute helpers to avoid repeated int() conversions while
# keeping behavior.

parent_difficulty_int = int(parent_difficulty)
time_delta = int(block_timestamp) - int(parent_timestamp)

offset = (parent_difficulty_int // 2048) * max(1 - (time_delta // 10), -99)
difficulty = parent_difficulty_int + offset

# Historical Note: The difficulty bomb was not present in Ethereum at the
# start of Frontier, but was added shortly after launch. However since the
# bomb has no effect prior to block 200000 we pretend it existed from
Expand Down
4 changes: 2 additions & 2 deletions src/ethereum/utils/byte.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def left_pad_zero_bytes(
value :
The byte string that needs to be padded.
size :
The number of bytes that need that need to be padded.
The number of bytes that need to be padded.

Returns
-------
Expand All @@ -48,7 +48,7 @@ def right_pad_zero_bytes(
value :
The byte string that needs to be padded.
size :
The number of bytes that need that need to be padded.
The number of bytes that need to be padded.

Returns
-------
Expand Down
2 changes: 1 addition & 1 deletion src/ethereum/utils/numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def get_sign(value: int) -> int:

def ceil32(value: Uint) -> Uint:
"""
Converts a unsigned integer to the next closest multiple of 32.
Converts an unsigned integer to the next closest multiple of 32.

Parameters
----------
Expand Down
2 changes: 1 addition & 1 deletion src/ethereum_spec_tools/evm_tools/daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ def __init__(self, options: argparse.Namespace) -> None:
from platformdirs import user_runtime_dir
except ImportError as e:
raise Exception(
"Missing plaformdirs dependency (try installing "
"Missing platformdirs dependency (try installing "
"ethereum[tools] extra)"
) from e
runtime_dir = user_runtime_dir(
Expand Down
8 changes: 6 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ extras =
commands =
fill \
-m "not slow and not zkevm and not benchmark" \
-n auto --maxprocesses 6 \
-n auto --maxprocesses 10 --dist=loadgroup \
--basetemp="{temp_dir}/pytest" \
--clean \
--until Osaka \
Expand All @@ -56,13 +56,16 @@ extras =
test,fill
passenv =
PYPY_GC_MAX
PYPY_GC_MIN
commands =
fill \
--skip-index \
--no-html \
--tb=no \
--show-capture=no \
--disable-warnings \
-m "not slow and not zkevm and not benchmark" \
-n auto --maxprocesses 3 \
-n auto --maxprocesses 7 --dist=loadgroup \
--basetemp="{temp_dir}/pytest" \
--clean \
--until Osaka \
Expand All @@ -74,6 +77,7 @@ extras =
optimized
passenv =
PYPY_GC_MAX
PYPY_GC_MIN
commands =
pytest \
-m "not slow and not evm_tools" \
Expand Down