Skip to content

Commit 51f2d6a

Browse files
authored
Refactor Environment (#1131)
* refactor blob gas logic There are parallels between how the regular block gas is handled and how the blob gas is handled. This commit refactors blob gas canculation to bring them in line with block gas * create BlockEnvironment and TransactionEnvironment Create the BlockEnvironment and TransactionEnvironment dataclasses BlockEnvironment holds data that is chain or block scoped TransactionEnvironment holds data that is transaction scoped Message holds data that is specific to a message call * refactor validate_header * update t8n * update evm tracing * backport changes to cancun * port to shanghai * port changes to paris * port to london * port berlin * port istanbul * port constantinople * port byzantium * port spurious_dragon * port older forks * fix t8n receipts * update testing * process_withdrawals update * fixes for doc * remove execution_spec_tests entry The tests from execution_spec_tests should now be a part of EEST * use default_factory in BlockOutput * minor fixes 1 * define current_block_number in block_hash opcode * rename tx_index to index_in_block * fix vm test runs
1 parent dc76275 commit 51f2d6a

File tree

203 files changed

+7818
-8360
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

203 files changed

+7818
-8360
lines changed

src/ethereum/arrow_glacier/fork.py

Lines changed: 170 additions & 224 deletions
Large diffs are not rendered by default.

src/ethereum/arrow_glacier/state.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
`EMPTY_ACCOUNT`.
1818
"""
1919
from dataclasses import dataclass, field
20-
from typing import Callable, Dict, List, Optional, Set, Tuple
20+
from typing import Callable, Dict, Iterable, List, Optional, Set, Tuple
2121

2222
from ethereum_types.bytes import Bytes
2323
from ethereum_types.frozen import modify
@@ -630,3 +630,20 @@ def get_storage_original(state: State, address: Address, key: Bytes) -> U256:
630630
assert isinstance(original_value, U256)
631631

632632
return original_value
633+
634+
635+
def destroy_touched_empty_accounts(
636+
state: State, touched_accounts: Iterable[Address]
637+
) -> None:
638+
"""
639+
Destroy all touched accounts that are empty.
640+
Parameters
641+
----------
642+
state: `State`
643+
The current state.
644+
touched_accounts: `Iterable[Address]`
645+
All the accounts that have been touched in the current transaction.
646+
"""
647+
for address in touched_accounts:
648+
if account_exists_and_is_empty(state, address):
649+
destroy_account(state, address)

src/ethereum/arrow_glacier/transactions.py

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from ethereum_rlp import rlp
1010
from ethereum_types.bytes import Bytes, Bytes0, Bytes32
1111
from ethereum_types.frozen import slotted_freezable
12-
from ethereum_types.numeric import U64, U256, Uint
12+
from ethereum_types.numeric import U64, U256, Uint, ulen
1313

1414
from ethereum.crypto.elliptic_curve import SECP256K1N, secp256k1_recover
1515
from ethereum.crypto.hash import Hash32, keccak256
@@ -18,12 +18,12 @@
1818
from .exceptions import TransactionTypeError
1919
from .fork_types import Address
2020

21-
TX_BASE_COST = 21000
22-
TX_DATA_COST_PER_NON_ZERO = 16
23-
TX_DATA_COST_PER_ZERO = 4
24-
TX_CREATE_COST = 32000
25-
TX_ACCESS_LIST_ADDRESS_COST = 2400
26-
TX_ACCESS_LIST_STORAGE_KEY_COST = 1900
21+
TX_BASE_COST = Uint(21000)
22+
TX_DATA_COST_PER_NON_ZERO = Uint(16)
23+
TX_DATA_COST_PER_ZERO = Uint(4)
24+
TX_CREATE_COST = Uint(32000)
25+
TX_ACCESS_LIST_ADDRESS_COST = Uint(2400)
26+
TX_ACCESS_LIST_STORAGE_KEY_COST = Uint(1900)
2727

2828

2929
@slotted_freezable
@@ -177,10 +177,10 @@ def calculate_intrinsic_cost(tx: Transaction) -> Uint:
177177
178178
Returns
179179
-------
180-
verified : `ethereum.base_types.Uint`
180+
intrinsic_gas : `ethereum.base_types.Uint`
181181
The intrinsic cost of the transaction.
182182
"""
183-
data_cost = 0
183+
data_cost = Uint(0)
184184

185185
for byte in tx.data:
186186
if byte == 0:
@@ -191,15 +191,15 @@ def calculate_intrinsic_cost(tx: Transaction) -> Uint:
191191
if tx.to == Bytes0(b""):
192192
create_cost = TX_CREATE_COST
193193
else:
194-
create_cost = 0
194+
create_cost = Uint(0)
195195

196-
access_list_cost = 0
196+
access_list_cost = Uint(0)
197197
if isinstance(tx, (AccessListTransaction, FeeMarketTransaction)):
198198
for _address, keys in tx.access_list:
199199
access_list_cost += TX_ACCESS_LIST_ADDRESS_COST
200-
access_list_cost += len(keys) * TX_ACCESS_LIST_STORAGE_KEY_COST
200+
access_list_cost += ulen(keys) * TX_ACCESS_LIST_STORAGE_KEY_COST
201201

202-
return Uint(TX_BASE_COST + data_cost + create_cost + access_list_cost)
202+
return TX_BASE_COST + data_cost + create_cost + access_list_cost
203203

204204

205205
def recover_sender(chain_id: U64, tx: Transaction) -> Address:
@@ -380,3 +380,22 @@ def signing_hash_1559(tx: FeeMarketTransaction) -> Hash32:
380380
)
381381
)
382382
)
383+
384+
385+
def get_transaction_hash(tx: Union[Bytes, LegacyTransaction]) -> Hash32:
386+
"""
387+
Parameters
388+
----------
389+
tx :
390+
Transaction of interest.
391+
392+
Returns
393+
-------
394+
hash : `ethereum.crypto.hash.Hash32`
395+
Hash of the transaction.
396+
"""
397+
assert isinstance(tx, (LegacyTransaction, Bytes))
398+
if isinstance(tx, LegacyTransaction):
399+
return keccak256(rlp.encode(tx))
400+
else:
401+
return keccak256(tx)

src/ethereum/arrow_glacier/utils/message.py

Lines changed: 35 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -12,105 +12,78 @@
1212
Message specific functions used in this arrow_glacier version of
1313
specification.
1414
"""
15-
from typing import FrozenSet, Optional, Tuple, Union
16-
17-
from ethereum_types.bytes import Bytes, Bytes0, Bytes32
18-
from ethereum_types.numeric import U256, Uint
15+
from ethereum_types.bytes import Bytes, Bytes0
16+
from ethereum_types.numeric import Uint
1917

2018
from ..fork_types import Address
2119
from ..state import get_account
22-
from ..vm import Environment, Message
20+
from ..transactions import Transaction
21+
from ..vm import BlockEnvironment, Message, TransactionEnvironment
2322
from ..vm.precompiled_contracts.mapping import PRE_COMPILED_CONTRACTS
2423
from .address import compute_contract_address
2524

2625

2726
def prepare_message(
28-
caller: Address,
29-
target: Union[Bytes0, Address],
30-
value: U256,
31-
data: Bytes,
32-
gas: Uint,
33-
env: Environment,
34-
code_address: Optional[Address] = None,
35-
should_transfer_value: bool = True,
36-
is_static: bool = False,
37-
preaccessed_addresses: FrozenSet[Address] = frozenset(),
38-
preaccessed_storage_keys: FrozenSet[
39-
Tuple[(Address, Bytes32)]
40-
] = frozenset(),
27+
block_env: BlockEnvironment,
28+
tx_env: TransactionEnvironment,
29+
tx: Transaction,
4130
) -> Message:
4231
"""
4332
Execute a transaction against the provided environment.
4433
4534
Parameters
4635
----------
47-
caller :
48-
Address which initiated the transaction
49-
target :
50-
Address whose code will be executed
51-
value :
52-
Value to be transferred.
53-
data :
54-
Array of bytes provided to the code in `target`.
55-
gas :
56-
Gas provided for the code in `target`.
57-
env :
36+
block_env :
5837
Environment for the Ethereum Virtual Machine.
59-
code_address :
60-
This is usually same as the `target` address except when an alternative
61-
accounts code needs to be executed.
62-
eg. `CALLCODE` calling a precompile.
63-
should_transfer_value :
64-
if True ETH should be transferred while executing a message call.
65-
is_static:
66-
if True then it prevents all state-changing operations from being
67-
executed.
68-
preaccessed_addresses:
69-
Addresses that should be marked as accessed prior to the message call
70-
preaccessed_storage_keys:
71-
Storage keys that should be marked as accessed prior to the message
72-
call
38+
tx_env :
39+
Environment for the transaction.
40+
tx :
41+
Transaction to be executed.
7342
7443
Returns
7544
-------
7645
message: `ethereum.arrow_glacier.vm.Message`
7746
Items containing contract creation or message call specific data.
7847
"""
7948
accessed_addresses = set()
80-
accessed_addresses.add(caller)
49+
accessed_addresses.add(tx_env.origin)
8150
accessed_addresses.update(PRE_COMPILED_CONTRACTS.keys())
82-
accessed_addresses.update(preaccessed_addresses)
83-
if isinstance(target, Bytes0):
51+
accessed_addresses.update(tx_env.access_list_addresses)
52+
53+
if isinstance(tx.to, Bytes0):
8454
current_target = compute_contract_address(
85-
caller,
86-
get_account(env.state, caller).nonce - Uint(1),
55+
tx_env.origin,
56+
get_account(block_env.state, tx_env.origin).nonce - Uint(1),
8757
)
8858
msg_data = Bytes(b"")
89-
code = data
90-
elif isinstance(target, Address):
91-
current_target = target
92-
msg_data = data
93-
code = get_account(env.state, target).code
94-
if code_address is None:
95-
code_address = target
59+
code = tx.data
60+
code_address = None
61+
elif isinstance(tx.to, Address):
62+
current_target = tx.to
63+
msg_data = tx.data
64+
code = get_account(block_env.state, tx.to).code
65+
66+
code_address = tx.to
9667
else:
9768
raise AssertionError("Target must be address or empty bytes")
9869

9970
accessed_addresses.add(current_target)
10071

10172
return Message(
102-
caller=caller,
103-
target=target,
104-
gas=gas,
105-
value=value,
73+
block_env=block_env,
74+
tx_env=tx_env,
75+
caller=tx_env.origin,
76+
target=tx.to,
77+
gas=tx_env.gas,
78+
value=tx.value,
10679
data=msg_data,
10780
code=code,
10881
depth=Uint(0),
10982
current_target=current_target,
11083
code_address=code_address,
111-
should_transfer_value=should_transfer_value,
112-
is_static=is_static,
84+
should_transfer_value=True,
85+
is_static=False,
11386
accessed_addresses=accessed_addresses,
114-
accessed_storage_keys=set(preaccessed_storage_keys),
87+
accessed_storage_keys=set(tx_env.access_list_storage_keys),
11588
parent_evm=None,
11689
)

src/ethereum/arrow_glacier/vm/__init__.py

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,40 +13,83 @@
1313
`.fork_types.Account`.
1414
"""
1515

16-
from dataclasses import dataclass
16+
from dataclasses import dataclass, field
1717
from typing import List, Optional, Set, Tuple, Union
1818

1919
from ethereum_types.bytes import Bytes, Bytes0, Bytes32
2020
from ethereum_types.numeric import U64, U256, Uint
2121

2222
from ethereum.crypto.hash import Hash32
23+
from ethereum.exceptions import EthereumException
2324

24-
from ..blocks import Log
25+
from ..blocks import Log, Receipt
2526
from ..fork_types import Address
2627
from ..state import State, account_exists_and_is_empty
28+
from ..transactions import LegacyTransaction
29+
from ..trie import Trie
2730
from .precompiled_contracts import RIPEMD160_ADDRESS
2831

2932
__all__ = ("Environment", "Evm", "Message")
3033

3134

3235
@dataclass
33-
class Environment:
36+
class BlockEnvironment:
3437
"""
3538
Items external to the virtual machine itself, provided by the environment.
3639
"""
3740

38-
caller: Address
41+
chain_id: U64
42+
state: State
43+
block_gas_limit: Uint
3944
block_hashes: List[Hash32]
40-
origin: Address
4145
coinbase: Address
4246
number: Uint
4347
base_fee_per_gas: Uint
44-
gas_limit: Uint
45-
gas_price: Uint
4648
time: U256
4749
difficulty: Uint
48-
state: State
49-
chain_id: U64
50+
51+
52+
@dataclass
53+
class BlockOutput:
54+
"""
55+
Output from applying the block body to the present state.
56+
57+
Contains the following:
58+
59+
block_gas_used : `ethereum.base_types.Uint`
60+
Gas used for executing all transactions.
61+
transactions_trie : `ethereum.fork_types.Root`
62+
Trie of all the transactions in the block.
63+
receipts_trie : `ethereum.fork_types.Root`
64+
Trie root of all the receipts in the block.
65+
block_logs : `Bloom`
66+
Logs bloom of all the logs included in all the transactions of the
67+
block.
68+
"""
69+
70+
block_gas_used: Uint = Uint(0)
71+
transactions_trie: Trie[
72+
Bytes, Optional[Union[Bytes, LegacyTransaction]]
73+
] = field(default_factory=lambda: Trie(secured=False, default=None))
74+
receipts_trie: Trie[Bytes, Optional[Union[Bytes, Receipt]]] = field(
75+
default_factory=lambda: Trie(secured=False, default=None)
76+
)
77+
block_logs: Tuple[Log, ...] = field(default_factory=tuple)
78+
79+
80+
@dataclass
81+
class TransactionEnvironment:
82+
"""
83+
Items that are used by contract creation or message call.
84+
"""
85+
86+
origin: Address
87+
gas_price: Uint
88+
gas: Uint
89+
access_list_addresses: Set[Address]
90+
access_list_storage_keys: Set[Tuple[Address, Bytes32]]
91+
index_in_block: Optional[Uint]
92+
tx_hash: Optional[Hash32]
5093
traces: List[dict]
5194

5295

@@ -56,6 +99,8 @@ class Message:
5699
Items that are used by contract creation or message call.
57100
"""
58101

102+
block_env: BlockEnvironment
103+
tx_env: TransactionEnvironment
59104
caller: Address
60105
target: Union[Bytes0, Address]
61106
current_target: Address
@@ -81,7 +126,6 @@ class Evm:
81126
memory: bytearray
82127
code: Bytes
83128
gas_left: Uint
84-
env: Environment
85129
valid_jump_destinations: Set[Uint]
86130
logs: Tuple[Log, ...]
87131
refund_counter: int
@@ -91,7 +135,7 @@ class Evm:
91135
accounts_to_delete: Set[Address]
92136
touched_accounts: Set[Address]
93137
return_data: Bytes
94-
error: Optional[Exception]
138+
error: Optional[EthereumException]
95139
accessed_addresses: Set[Address]
96140
accessed_storage_keys: Set[Tuple[Address, Bytes32]]
97141

@@ -113,7 +157,7 @@ def incorporate_child_on_success(evm: Evm, child_evm: Evm) -> None:
113157
evm.accounts_to_delete.update(child_evm.accounts_to_delete)
114158
evm.touched_accounts.update(child_evm.touched_accounts)
115159
if account_exists_and_is_empty(
116-
evm.env.state, child_evm.message.current_target
160+
evm.message.block_env.state, child_evm.message.current_target
117161
):
118162
evm.touched_accounts.add(child_evm.message.current_target)
119163
evm.accessed_addresses.update(child_evm.accessed_addresses)
@@ -142,7 +186,7 @@ def incorporate_child_on_error(evm: Evm, child_evm: Evm) -> None:
142186
evm.touched_accounts.add(RIPEMD160_ADDRESS)
143187
if child_evm.message.current_target == RIPEMD160_ADDRESS:
144188
if account_exists_and_is_empty(
145-
evm.env.state, child_evm.message.current_target
189+
evm.message.block_env.state, child_evm.message.current_target
146190
):
147191
evm.touched_accounts.add(RIPEMD160_ADDRESS)
148192
evm.gas_left += child_evm.gas_left

0 commit comments

Comments
 (0)