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

Add EIP-7623 implementation #934

Closed
wants to merge 35 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
ec0bccf
Add more retroactive EIPs
timbeiko Apr 4, 2024
b37843d
Add EIP-7623 implementation
nerolation Apr 19, 2024
cae6ea4
Add EIP-7623 implementation
nerolation Apr 19, 2024
b20024d
Update README.md
jhfnetboy May 1, 2024
59f5b04
Merge pull request #920 from ethereum/timbeiko-patch-19
timbeiko May 6, 2024
da12435
Replace ensure with clumsy if-statements
SamWilsn Apr 22, 2024
c37c49b
Clean up conditionals outside of forks
SamWilsn Apr 22, 2024
9b7b5e6
Clean up conditionals in trie.py
SamWilsn Apr 22, 2024
68eb283
Clean up conditionals in state.py
SamWilsn Apr 22, 2024
a1f4c07
Clean up conditionals in fork.py
SamWilsn Apr 24, 2024
2bc0819
Clean up conditionals in vm/interpreter.py
SamWilsn Apr 25, 2024
f8bb35c
Clean up conditionals in vm/precompiled_contracts/point_evaluation.py
SamWilsn Apr 25, 2024
19f4e63
Clean up conditionals in vm/precompiled_contracts/alt_bn128.py
SamWilsn Apr 25, 2024
20b5751
Clean up conditionals in vm/instructions/stack.py
SamWilsn Apr 26, 2024
670d6e1
Clean up conditionals in vm/precompiled_contracts/blake2f.py
SamWilsn Apr 26, 2024
34eac7e
Clean up conditionals in vm/instructions/storage.py
SamWilsn Apr 26, 2024
3c16236
Clean up conditionals in vm/instructions/environment.py
SamWilsn Apr 26, 2024
c923da6
Clean up conditionals in vm/instructions/log.py
SamWilsn Apr 27, 2024
0f116b5
Clean up conditionals in vm/instructions/system.py
SamWilsn Apr 27, 2024
3de1092
handle to address in tx being None
gurukamath May 16, 2024
ce10966
Merge pull request #952 from gurukamath/handle-none-tx-to
petertdavies May 16, 2024
1077080
Refactor conftest.py
richardgreg Mar 28, 2024
c6ed0c8
Remove unused import 'importlib' from 'test_evm_tools.py'
richardgreg Mar 31, 2024
2f4b654
Remove unused imports from 'test_difficulty.py'
richardgreg Mar 31, 2024
ae5db68
Remove unused imports from Shanghai's 'test_state_transition.py'
richardgreg Mar 31, 2024
dd05f90
Remove unused imports from test_b11r.py'
richardgreg Mar 31, 2024
e7b90ec
Refactor Spurious Dragon, Byzantium and Frontiers test_difficulty.
richardgreg Mar 31, 2024
20b680e
Update setup.cfg
SamWilsn Apr 3, 2024
ea4d05d
Update setup.cfg
SamWilsn Apr 3, 2024
8affa29
Remove unused local variable 'pure_test_file' from load_state_tests.py
richardgreg Apr 15, 2024
3bece82
chore: fix some typos in comments
cuibuwei Apr 13, 2024
10b101b
Fix comparisons to bool in test_transaction.py
SamWilsn May 17, 2024
64a4740
Fix most other flake8 erros in tests
SamWilsn May 17, 2024
f9deecb
Disable line length (E501) for tests
SamWilsn May 17, 2024
23ff5a4
Merge branch 'ethereum:master' into master
nerolation Jun 3, 2024
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: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ Some clarifications were enabled without protocol releases:
|-----|-----------|
| [EIP-2681](https://eips.ethereum.org/EIPS/eip-2681) | 0 |
| [EIP-3607](https://eips.ethereum.org/EIPS/eip-3607) | 0 |
| [EIP-7523](https://eips.ethereum.org/EIPS/eip-7523) | 15537394 |
| [EIP-7610](https://github.com/ethereum/EIPs/pull/8161) | 0 |


## Execution Specification (work-in-progress)

Expand Down
2 changes: 1 addition & 1 deletion lists/signature-types/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Reserved or Tentative.
| 0x00 | Reserved: indicates legacy (untyped) trancactions |
| 0x01 | Reserved: [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930) *(available in Berlin)* |
| 0x02 | Reserved: [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) *(available in London)* |
| 0x02 | Reserved: [EIP-4844](https://eips.ethereum.org/EIPS/eip-1559) *(available in Cancun)* |
| 0x03 | Reserved: [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) *(available in Cancun)* |
| 0x04 | Reserved: [EIP-3074](https://eips.ethereum.org/EIPS/eip-3074) |
| 0x19 | Reserved: prevents collision with [EIP-191](https://eips.ethereum.org/EIPS/eip-191) |
| 0xc0 - 0xff | Invalid; collides with the initial byte of valid RLP encoded transactions |
Expand Down
2 changes: 1 addition & 1 deletion network-upgrades/retrospectives/london.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ Solution: Do a `debug.setHead{X-1)` to jump to before the fork. This internally

##### Syncing in the presence of a wrong higher-td chain

You are syncing a `geth`-node, and a fork has occurred at block `X`. Since the fork has already happened, and the erroneous chain has higher TD, you will most likely wind up on the 'wrong' side of the chain, with a pivot block `X+M`. If this happens, you _do not have_ any state for blocks `<X+M`, so you _cannot_ do `debug.setHead` to to resolve the situation.
You are syncing a `geth`-node, and a fork has occurred at block `X`. Since the fork has already happened, and the erroneous chain has higher TD, you will most likely wind up on the 'wrong' side of the chain, with a pivot block `X+M`. If this happens, you _do not have_ any state for blocks `<X+M`, so you _cannot_ do `debug.setHead` to resolve the situation.

In this case, a resync is required. However, you need to prevent geth from winding up on the wrong side of the fork. This can be done with the `whitelist` command line parameter.
```
Expand Down
3 changes: 2 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,11 @@ extend-ignore =

extend-exclude =
setup.py
tests/
doc/
tests/fixtures/

per-file-ignores =
src/ethereum/base_types.py:D105,SC100,SC200
tests/*:D100,D101,D103,D104,E501,SC100,SC200

# vim: set ft=dosini:
147 changes: 72 additions & 75 deletions src/ethereum/arrow_glacier/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
from ethereum.crypto.hash import Hash32, keccak256
from ethereum.ethash import dataset_size, generate_cache, hashimoto_light
from ethereum.exceptions import InvalidBlock
from ethereum.utils.ensure import ensure

from .. import rlp
from ..base_types import U64, U256, U256_CEIL_VALUE, Bytes, Uint
Expand Down Expand Up @@ -178,23 +177,16 @@ def state_transition(chain: BlockChain, block: Block) -> None:
block.ommers,
chain.chain_id,
)
ensure(
apply_body_output.block_gas_used == block.header.gas_used, InvalidBlock
)
ensure(
apply_body_output.transactions_root == block.header.transactions_root,
InvalidBlock,
)
ensure(
apply_body_output.state_root == block.header.state_root, InvalidBlock
)
ensure(
apply_body_output.receipt_root == block.header.receipt_root,
InvalidBlock,
)
ensure(
apply_body_output.block_logs_bloom == block.header.bloom, InvalidBlock
)
if apply_body_output.block_gas_used != block.header.gas_used:
raise InvalidBlock
if apply_body_output.transactions_root != block.header.transactions_root:
raise InvalidBlock
if apply_body_output.state_root != block.header.state_root:
raise InvalidBlock
if apply_body_output.receipt_root != block.header.receipt_root:
raise InvalidBlock
if apply_body_output.block_logs_bloom != block.header.bloom:
raise InvalidBlock

chain.blocks.append(block)
if len(chain.blocks) > 255:
Expand Down Expand Up @@ -229,11 +221,8 @@ def calculate_base_fee_per_gas(
Base fee per gas for the block.
"""
parent_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER

ensure(
check_gas_limit(block_gas_limit, parent_gas_limit),
InvalidBlock,
)
if not check_gas_limit(block_gas_limit, parent_gas_limit):
raise InvalidBlock

if parent_gas_used == parent_gas_target:
expected_base_fee_per_gas = parent_base_fee_per_gas
Expand Down Expand Up @@ -286,21 +275,25 @@ def validate_header(header: Header, parent_header: Header) -> None:
parent_header :
Parent Header of the header to check for correctness
"""
ensure(header.gas_used <= header.gas_limit, InvalidBlock)
if header.gas_used > header.gas_limit:
raise InvalidBlock

expected_base_fee_per_gas = calculate_base_fee_per_gas(
header.gas_limit,
parent_header.gas_limit,
parent_header.gas_used,
parent_header.base_fee_per_gas,
)

ensure(expected_base_fee_per_gas == header.base_fee_per_gas, InvalidBlock)
if expected_base_fee_per_gas != header.base_fee_per_gas:
raise InvalidBlock

parent_has_ommers = parent_header.ommers_hash != EMPTY_OMMER_HASH
ensure(header.timestamp > parent_header.timestamp, InvalidBlock)
ensure(header.number == parent_header.number + 1, InvalidBlock)
ensure(len(header.extra_data) <= 32, InvalidBlock)
if header.timestamp <= parent_header.timestamp:
raise InvalidBlock
if header.number != parent_header.number + 1:
raise InvalidBlock
if len(header.extra_data) > 32:
raise InvalidBlock

block_difficulty = calculate_block_difficulty(
header.number,
Expand All @@ -309,10 +302,12 @@ def validate_header(header: Header, parent_header: Header) -> None:
parent_header.difficulty,
parent_has_ommers,
)
ensure(header.difficulty == block_difficulty, InvalidBlock)
if header.difficulty != block_difficulty:
raise InvalidBlock

block_parent_hash = keccak256(rlp.encode(parent_header))
ensure(header.parent_hash == block_parent_hash, InvalidBlock)
if header.parent_hash != block_parent_hash:
raise InvalidBlock

validate_proof_of_work(header)

Expand Down Expand Up @@ -382,12 +377,10 @@ def validate_proof_of_work(header: Header) -> None:
mix_digest, result = hashimoto_light(
header_hash, header.nonce, cache, dataset_size(header.number)
)

ensure(mix_digest == header.mix_digest, InvalidBlock)
ensure(
Uint.from_be_bytes(result) <= (U256_CEIL_VALUE // header.difficulty),
InvalidBlock,
)
if mix_digest != header.mix_digest:
raise InvalidBlock
if Uint.from_be_bytes(result) > (U256_CEIL_VALUE // header.difficulty):
raise InvalidBlock


def check_transaction(
Expand Down Expand Up @@ -422,20 +415,24 @@ def check_transaction(
InvalidBlock :
If the transaction is not includable.
"""
ensure(tx.gas <= gas_available, InvalidBlock)
if tx.gas > gas_available:
raise InvalidBlock
sender_address = recover_sender(chain_id, tx)

if isinstance(tx, FeeMarketTransaction):
ensure(tx.max_fee_per_gas >= tx.max_priority_fee_per_gas, InvalidBlock)
ensure(tx.max_fee_per_gas >= base_fee_per_gas, InvalidBlock)
if tx.max_fee_per_gas < tx.max_priority_fee_per_gas:
raise InvalidBlock
if tx.max_fee_per_gas < base_fee_per_gas:
raise InvalidBlock

priority_fee_per_gas = min(
tx.max_priority_fee_per_gas,
tx.max_fee_per_gas - base_fee_per_gas,
)
effective_gas_price = priority_fee_per_gas + base_fee_per_gas
else:
ensure(tx.gas_price >= base_fee_per_gas, InvalidBlock)
if tx.gas_price < base_fee_per_gas:
raise InvalidBlock
effective_gas_price = tx.gas_price

return sender_address, effective_gas_price
Expand Down Expand Up @@ -653,27 +650,27 @@ def validate_ommers(
History and current state.
"""
block_hash = rlp.rlp_hash(block_header)

ensure(rlp.rlp_hash(ommers) == block_header.ommers_hash, InvalidBlock)
if rlp.rlp_hash(ommers) != block_header.ommers_hash:
raise InvalidBlock

if len(ommers) == 0:
# Nothing to validate
return

# Check that each ommer satisfies the constraints of a header
for ommer in ommers:
ensure(1 <= ommer.number < block_header.number, InvalidBlock)
if 1 > ommer.number or ommer.number >= block_header.number:
raise InvalidBlock
ommer_parent_header = chain.blocks[
-(block_header.number - ommer.number) - 1
].header
validate_header(ommer, ommer_parent_header)

# Check that there can be only at most 2 ommers for a block.
ensure(len(ommers) <= 2, InvalidBlock)
if len(ommers) > 2:
raise InvalidBlock

ommers_hashes = [rlp.rlp_hash(ommer) for ommer in ommers]
# Check that there are no duplicates in the ommers of current block
ensure(len(ommers_hashes) == len(set(ommers_hashes)), InvalidBlock)
if len(ommers_hashes) != len(set(ommers_hashes)):
raise InvalidBlock

recent_canonical_blocks = chain.blocks[-(MAX_OMMER_DEPTH + 1) :]
recent_canonical_block_hashes = {
Expand All @@ -687,25 +684,22 @@ def validate_ommers(

for ommer_index, ommer in enumerate(ommers):
ommer_hash = ommers_hashes[ommer_index]
# The current block shouldn't be the ommer
ensure(ommer_hash != block_hash, InvalidBlock)

# Ommer shouldn't be one of the recent canonical blocks
ensure(ommer_hash not in recent_canonical_block_hashes, InvalidBlock)

# Ommer shouldn't be one of the uncles mentioned in the recent
# canonical blocks
ensure(ommer_hash not in recent_ommers_hashes, InvalidBlock)
if ommer_hash == block_hash:
raise InvalidBlock
if ommer_hash in recent_canonical_block_hashes:
raise InvalidBlock
if ommer_hash in recent_ommers_hashes:
raise InvalidBlock

# Ommer age with respect to the current block. For example, an age of
# 1 indicates that the ommer is a sibling of previous block.
ommer_age = block_header.number - ommer.number
ensure(1 <= ommer_age <= MAX_OMMER_DEPTH, InvalidBlock)

ensure(
ommer.parent_hash in recent_canonical_block_hashes, InvalidBlock
)
ensure(ommer.parent_hash != block_header.parent_hash, InvalidBlock)
if 1 > ommer_age or ommer_age > MAX_OMMER_DEPTH:
raise InvalidBlock
if ommer.parent_hash not in recent_canonical_block_hashes:
raise InvalidBlock
if ommer.parent_hash == block_header.parent_hash:
raise InvalidBlock


def pay_rewards(
Expand Down Expand Up @@ -778,7 +772,8 @@ def process_transaction(
logs : `Tuple[ethereum.blocks.Log, ...]`
Logs generated during execution.
"""
ensure(validate_transaction(tx), InvalidBlock)
if not validate_transaction(tx):
raise InvalidBlock

sender = env.origin
sender_account = get_account(env.state, sender)
Expand All @@ -787,10 +782,12 @@ def process_transaction(
max_gas_fee = tx.gas * tx.max_fee_per_gas
else:
max_gas_fee = tx.gas * tx.gas_price

ensure(sender_account.nonce == tx.nonce, InvalidBlock)
ensure(sender_account.balance >= max_gas_fee + tx.value, InvalidBlock)
ensure(sender_account.code == bytearray(), InvalidBlock)
if sender_account.nonce != tx.nonce:
raise InvalidBlock
if sender_account.balance < max_gas_fee + tx.value:
raise InvalidBlock
if sender_account.code != bytearray():
raise InvalidBlock

effective_gas_fee = tx.gas * env.gas_price

Expand Down Expand Up @@ -956,9 +953,10 @@ def recover_sender(chain_id: U64, tx: Transaction) -> Address:
The address of the account that signed the transaction.
"""
r, s = tx.r, tx.s

ensure(0 < r and r < SECP256K1N, InvalidBlock)
ensure(0 < s and s <= SECP256K1N // 2, InvalidBlock)
if 0 >= r or r >= SECP256K1N:
raise InvalidBlock
if 0 >= s or s > SECP256K1N // 2:
raise InvalidBlock

if isinstance(tx, LegacyTransaction):
v = tx.v
Expand All @@ -967,9 +965,8 @@ def recover_sender(chain_id: U64, tx: Transaction) -> Address:
r, s, v - 27, signing_hash_pre155(tx)
)
else:
ensure(
v == 35 + chain_id * 2 or v == 36 + chain_id * 2, InvalidBlock
)
if v != 35 + chain_id * 2 and v != 36 + chain_id * 2:
raise InvalidBlock
public_key = secp256k1_recover(
r, s, v - 35 - chain_id * 2, signing_hash_155(tx, chain_id)
)
Expand Down
4 changes: 2 additions & 2 deletions src/ethereum/arrow_glacier/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
from typing import Callable, Dict, List, Optional, Set, Tuple

from ethereum.base_types import U256, Bytes, Uint, modify
from ethereum.utils.ensure import ensure

from .fork_types import EMPTY_ACCOUNT, Account, Address, Root
from .trie import EMPTY_TRIE_ROOT, Trie, copy_trie, root, trie_get, trie_set
Expand Down Expand Up @@ -469,7 +468,8 @@ def move_ether(
"""

def reduce_sender_balance(sender: Account) -> None:
ensure(sender.balance >= amount, AssertionError)
if sender.balance < amount:
raise AssertionError
sender.balance -= amount

def increase_recipient_balance(recipient: Account) -> None:
Expand Down
5 changes: 2 additions & 3 deletions src/ethereum/arrow_glacier/trie.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@

from ethereum.crypto.hash import keccak256
from ethereum.london import trie as previous_trie
from ethereum.utils.ensure import ensure
from ethereum.utils.hexadecimal import hex_to_bytes

from .. import rlp
Expand Down Expand Up @@ -346,8 +345,8 @@ def _prepare_trie(
encoded_value = encode_node(value, get_storage_root(address))
else:
encoded_value = encode_node(value)
# Empty values are represented by their absence
ensure(encoded_value != b"", AssertionError)
if encoded_value == b"":
raise AssertionError
key: Bytes
if trie.secured:
# "secure" tries hash keys once before construction
Expand Down
9 changes: 2 additions & 7 deletions src/ethereum/arrow_glacier/vm/instructions/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

from ethereum.base_types import U256, Uint
from ethereum.crypto.hash import keccak256
from ethereum.utils.ensure import ensure
from ethereum.utils.numeric import ceil32

from ...fork_types import EMPTY_ACCOUNT
Expand Down Expand Up @@ -439,12 +438,8 @@ def returndatacopy(evm: Evm) -> None:
evm.memory, [(memory_start_index, size)]
)
charge_gas(evm, GAS_VERY_LOW + copy_gas_cost + extend_memory.cost)

# OPERATION
ensure(
Uint(return_data_start_position) + Uint(size) <= len(evm.return_data),
OutOfBoundsRead,
)
if Uint(return_data_start_position) + Uint(size) > len(evm.return_data):
raise OutOfBoundsRead

evm.memory += b"\x00" * extend_memory.expand_by
value = evm.return_data[
Expand Down
4 changes: 2 additions & 2 deletions src/ethereum/arrow_glacier/vm/instructions/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
from functools import partial

from ethereum.base_types import U256
from ethereum.utils.ensure import ensure

from ...blocks import Log
from .. import Evm
Expand Down Expand Up @@ -68,7 +67,8 @@ def log_n(evm: Evm, num_topics: U256) -> None:

# OPERATION
evm.memory += b"\x00" * extend_memory.expand_by
ensure(not evm.message.is_static, WriteInStaticContext)
if evm.message.is_static:
raise WriteInStaticContext
log_entry = Log(
address=evm.message.current_target,
topics=tuple(topics),
Expand Down
Loading