0
# Fork Management
1
2
Comprehensive support for all Ethereum hard forks with proper opcode evolution, gas cost updates, and utilities for fork selection and block number conversion. PyEVMAsm accurately handles the historical development of the EVM instruction set across all major Ethereum upgrades.
3
4
## Capabilities
5
6
### Fork Constants and Configuration
7
8
Default fork configuration and comprehensive fork support information.
9
10
```python { .api }
11
DEFAULT_FORK: str # Current default fork name ("istanbul")
12
```
13
14
**Usage Examples:**
15
16
```python
17
from pyevmasm import DEFAULT_FORK
18
19
print(f"Default fork: {DEFAULT_FORK}") # istanbul
20
21
# Supported forks can be accessed via instruction_tables.keys()
22
from pyevmasm import instruction_tables
23
supported_forks = tuple(instruction_tables.keys())
24
print(f"Supported forks: {supported_forks}")
25
# ('frontier', 'homestead', 'tangerine_whistle', 'spurious_dragon',
26
# 'byzantium', 'constantinople', 'petersburg', 'serenity', 'istanbul')
27
28
# Check if a fork is supported
29
fork_name = "byzantium"
30
if fork_name in instruction_tables:
31
print(f"{fork_name} is supported")
32
```
33
34
### Instruction Tables
35
36
Fork-specific instruction lookup tables providing accurate opcode definitions for each Ethereum hard fork.
37
38
```python { .api }
39
instruction_tables: dict # Mapping of fork names to InstructionTable instances
40
41
# Individual fork instruction tables
42
frontier_instruction_table: InstructionTable
43
homestead_instruction_table: InstructionTable
44
tangerine_whistle_instruction_table: InstructionTable
45
spurious_dragon_instruction_table: InstructionTable
46
byzantium_instruction_table: InstructionTable
47
constantinople_instruction_table: InstructionTable
48
serenity_instruction_table: InstructionTable
49
istanbul_instruction_table: InstructionTable
50
```
51
52
**Usage Examples:**
53
54
```python
55
from pyevmasm import instruction_tables
56
57
# Access fork-specific instruction table
58
byzantium_table = instruction_tables["byzantium"]
59
frontier_table = instruction_tables["frontier"]
60
61
# Look up instructions by opcode
62
returndatasize = byzantium_table[0x3d] # RETURNDATASIZE
63
print(f"Byzantium 0x3d: {returndatasize.name}") # RETURNDATASIZE
64
65
# Same opcode in frontier (before RETURNDATASIZE existed)
66
try:
67
frontier_returndatasize = frontier_table[0x3d]
68
print(f"Frontier 0x3d: {frontier_returndatasize.name}") # INVALID
69
except KeyError:
70
print("0x3d not defined in frontier")
71
72
# Look up by mnemonic
73
push1 = byzantium_table["PUSH1"]
74
print(f"PUSH1 opcode: 0x{push1.opcode:02x}") # 0x60
75
```
76
77
### InstructionTable Class
78
79
Fork-specific instruction lookup and iteration capabilities.
80
81
```python { .api }
82
class InstructionTable:
83
"""
84
EVM Instruction factory providing fork-specific instruction definitions.
85
Implements an immutable, iterable instruction lookup table indexed by
86
both mnemonic and opcode.
87
"""
88
89
def __getitem__(self, key) -> Instruction:
90
"""
91
Get instruction by opcode (int) or mnemonic (str).
92
93
Parameters:
94
- key (int | str): Opcode value (0x00-0xFF) or mnemonic name
95
96
Returns:
97
Instruction: New Instruction instance for the specified opcode/mnemonic
98
99
Raises:
100
KeyError: If opcode/mnemonic not found in this fork
101
"""
102
103
def get(self, key, default=None) -> Instruction:
104
"""
105
Get instruction with default fallback.
106
107
Parameters:
108
- key (int | str): Opcode value or mnemonic name
109
- default: Default value if key not found
110
111
Returns:
112
Instruction | default: Instruction instance or default value
113
"""
114
115
def __contains__(self, key) -> bool:
116
"""Check if opcode or mnemonic exists in this fork."""
117
118
def __iter__(self):
119
"""Iterate over all instructions in opcode order."""
120
121
def keys(self):
122
"""Get sorted list of all opcodes in this fork."""
123
```
124
125
**Usage Examples:**
126
127
```python
128
from pyevmasm import instruction_tables
129
130
# Get instruction table for specific fork
131
istanbul_table = instruction_tables["istanbul"]
132
133
# Multiple ways to access instructions
134
push1_by_opcode = istanbul_table[0x60] # By opcode
135
push1_by_name = istanbul_table["PUSH1"] # By mnemonic
136
print(f"Same instruction: {push1_by_opcode.name == push1_by_name.name}") # True
137
138
# Safe access with default
139
unknown = istanbul_table.get(0xff, None) # SELFDESTRUCT or None depending on context
140
valid_instr = istanbul_table.get("ADD") # Returns ADD instruction
141
142
# Check existence
143
has_returndatasize = "RETURNDATASIZE" in istanbul_table # True
144
has_returndatasize_frontier = "RETURNDATASIZE" in instruction_tables["frontier"] # False
145
146
# Iterate all instructions
147
for instruction in istanbul_table:
148
if instruction.is_arithmetic:
149
print(f"Arithmetic: {instruction.name}")
150
151
# Get all opcodes
152
all_opcodes = istanbul_table.keys()
153
print(f"Total opcodes in Istanbul: {len(all_opcodes)}")
154
```
155
156
### Block Number to Fork Conversion
157
158
Utility function to determine the appropriate fork for a given Ethereum block number.
159
160
```python { .api }
161
def block_to_fork(block_number: int) -> str:
162
"""
163
Convert Ethereum block number to appropriate fork name.
164
165
Parameters:
166
- block_number (int): Ethereum block number
167
168
Returns:
169
str: Fork name appropriate for the given block number
170
171
Example:
172
>>> block_to_fork(0)
173
'frontier'
174
>>> block_to_fork(4370000)
175
'byzantium'
176
>>> block_to_fork(7280000)
177
'petersburg'
178
"""
179
```
180
181
**Usage Examples:**
182
183
```python
184
from pyevmasm import block_to_fork
185
186
# Historical block analysis
187
print(f"Block 0: {block_to_fork(0)}") # frontier
188
print(f"Block 1150000: {block_to_fork(1150000)}") # homestead
189
print(f"Block 4370000: {block_to_fork(4370000)}") # byzantium
190
print(f"Block 7280000: {block_to_fork(7280000)}") # petersburg
191
print(f"Recent block: {block_to_fork(15000000)}") # petersburg (latest)
192
193
# Use in analysis
194
def analyze_transaction_bytecode(bytecode, block_number):
195
fork = block_to_fork(block_number)
196
return disassemble_hex(bytecode, fork=fork)
197
```
198
199
## Fork Evolution and Differences
200
201
### Frontier (Block 0)
202
- Original EVM instruction set
203
- Basic arithmetic, comparison, bitwise, and system operations
204
- All core opcodes: STOP, ADD, MUL, PUSH, POP, MLOAD, SSTORE, CALL, etc.
205
206
### Homestead (Block 1,150,000)
207
- Added DELEGATECALL (0xf4)
208
- Enhanced contract creation security
209
210
### Tangerine Whistle (Block 2,463,000)
211
- Gas cost increases for several opcodes
212
- EXTCODESIZE, EXTCODECOPY, BALANCE, SLOAD, CALL operations became more expensive
213
- SELFDESTRUCT gas cost increased
214
215
### Spurious Dragon (Block 2,675,000)
216
- No new opcodes
217
- Additional gas cost and state tree adjustments
218
219
### Byzantium (Block 4,370,000)
220
- Added RETURNDATASIZE (0x3d), RETURNDATACOPY (0x3e)
221
- Added STATICCALL (0xfa) for state-reading calls
222
- Added REVERT (0xfd) for better error handling
223
224
### Constantinople/Petersburg (Block 7,280,000)
225
- Added shift operations: SHL (0x1b), SHR (0x1c), SAR (0x1d)
226
- Added EXTCODEHASH (0x3f) for code hash retrieval
227
- Added CREATE2 (0xf5) for deterministic contract addresses
228
229
### Istanbul (Latest Default)
230
- Updated gas costs for BALANCE, EXTCODEHASH, SLOAD
231
- Added CHAINID (0x46) and SELFBALANCE (0x47)
232
233
**Fork Compatibility Examples:**
234
235
```python
236
from pyevmasm import assemble_hex, disassemble_one
237
238
# Instructions available in different forks
239
def test_fork_compatibility():
240
# This works in all forks
241
basic_code = assemble_hex("PUSH1 0x01\nADD", fork="frontier")
242
243
# DELEGATECALL introduced in homestead
244
try:
245
homestead_code = assemble_hex("DELEGATECALL", fork="homestead") # Works
246
frontier_code = assemble_hex("DELEGATECALL", fork="frontier") # Fails
247
except AssembleError:
248
print("DELEGATECALL not available in frontier")
249
250
# RETURNDATASIZE introduced in byzantium
251
byzantium_instr = disassemble_one(b'\x3d', fork="byzantium") # RETURNDATASIZE
252
frontier_instr = disassemble_one(b'\x3d', fork="frontier") # INVALID
253
254
# Gas costs change between forks
255
balance_frontier = disassemble_one(b'\x31', fork="frontier") # fee: 20
256
balance_istanbul = disassemble_one(b'\x31', fork="istanbul") # fee: 700
257
258
print(f"BALANCE gas: frontier={balance_frontier.fee}, istanbul={balance_istanbul.fee}")
259
260
# Choose appropriate fork for analysis
261
def analyze_historical_transaction(bytecode, block_number):
262
fork = block_to_fork(block_number)
263
instructions = list(disassemble_all(bytecode, fork=fork))
264
265
# Analysis with correct historical opcodes
266
for instr in instructions:
267
if instr.name == "INVALID":
268
print(f"Invalid opcode 0x{instr.opcode:02x} at block {block_number} (fork: {fork})")
269
```
270
271
## Best Practices
272
273
### Fork Selection
274
- Use `DEFAULT_FORK` for current/modern bytecode analysis
275
- Use `block_to_fork()` for historical transaction analysis
276
- Specify explicit fork when analyzing specific deployment contexts
277
- Test compatibility when working with cross-fork smart contracts
278
279
### Instruction Table Caching
280
- Instruction tables are pre-computed and immutable
281
- Safe to cache table references for performance
282
- Each table lookup creates new Instruction instances
283
284
### Version-Aware Analysis
285
```python
286
from pyevmasm import instruction_tables, block_to_fork
287
288
def version_aware_analysis(bytecode, context_block=None):
289
if context_block:
290
fork = block_to_fork(context_block)
291
else:
292
fork = DEFAULT_FORK
293
294
table = instruction_tables[fork]
295
# Proceed with fork-appropriate analysis
296
return disassemble_all(bytecode, fork=fork)
297
```