ethereum.forks.berlin.vm.interpreterethereum.forks.london.vm.interpreter
Ethereum Virtual Machine (EVM) Interpreter.
.. contents:: Table of Contents :backlinks: none :local:
Introduction
A straightforward interpreter that executes EVM code.
STACK_DEPTH_LIMIT
64 | STACK_DEPTH_LIMIT = Uint(1024) |
---|
MAX_CODE_SIZE
65 | MAX_CODE_SIZE = 0x6000 |
---|
MessageCallOutput
Output of a particular message call.
Contains the following:
1. `gas_left`: remaining gas after execution.
2. `refund_counter`: gas to refund after execution.
3. `logs`: list of `Log` generated during execution.
4. `accounts_to_delete`: Contracts which have self-destructed.
5. `touched_accounts`: Accounts that have been touched.
6. `error`: The error from the execution if any.
68 | @dataclass |
---|
class MessageCallOutput:
gas_left
83 | gas_left: Uint |
---|
refund_counter
84 | refund_counter: U256 |
---|
logs
85 | logs: Tuple[Log, ...] |
---|
accounts_to_delete
86 | accounts_to_delete: Set[Address] |
---|
touched_accounts
87 | touched_accounts: Set[Address] |
---|
error
88 | error: Optional[EthereumException] |
---|
process_message_call
If message.target
is empty then it creates a smart contract
else it executes a call from the message.caller
to the message.target
.
Parameters
message : Transaction specific items.
Returns
output : MessageCallOutput
Output of the message call
def process_message_call(message: Message) -> MessageCallOutput:
92 | """ |
---|---|
93 | If `message.target` is empty then it creates a smart contract |
94 | else it executes a call from the `message.caller` to the `message.target`. |
95 |
|
96 | Parameters |
97 | ---------- |
98 | message : |
99 | Transaction specific items. |
100 |
|
101 | Returns |
102 | ------- |
103 | output : `MessageCallOutput` |
104 | Output of the message call |
105 |
|
106 | """ |
107 | block_env = message.block_env |
108 | refund_counter = U256(0) |
109 | if message.target == Bytes0(b""): |
110 | is_collision = account_has_code_or_nonce( |
111 | block_env.state, message.current_target |
112 | ) or account_has_storage(block_env.state, message.current_target) |
113 | if is_collision: |
114 | return MessageCallOutput( |
115 | Uint(0), U256(0), tuple(), set(), set(), AddressCollision() |
116 | ) |
117 | else: |
118 | evm = process_create_message(message) |
119 | else: |
120 | evm = process_message(message) |
121 | if account_exists_and_is_empty( |
122 | block_env.state, Address(message.target) |
123 | ): |
124 | evm.touched_accounts.add(Address(message.target)) |
125 | |
126 | if evm.error: |
127 | logs: Tuple[Log, ...] = () |
128 | accounts_to_delete = set() |
129 | touched_accounts = set() |
130 | else: |
131 | logs = evm.logs |
132 | accounts_to_delete = evm.accounts_to_delete |
133 | touched_accounts = evm.touched_accounts |
134 | refund_counter += U256(evm.refund_counter) |
135 | |
136 | tx_end = TransactionEnd( |
137 | int(message.gas) - int(evm.gas_left), evm.output, evm.error |
138 | ) |
139 | evm_trace(evm, tx_end) |
140 | |
141 | return MessageCallOutput( |
142 | gas_left=evm.gas_left, |
143 | refund_counter=refund_counter, |
144 | logs=logs, |
145 | accounts_to_delete=accounts_to_delete, |
146 | touched_accounts=touched_accounts, |
147 | error=evm.error, |
148 | ) |
process_create_message
Executes a call to create a smart contract.
Parameters
message : Transaction specific items.
Returns
evm: :py:class:
Items containing execution specific objects.~ethereum.forks.berlin.vm.Evm~ethereum.forks.london.vm.Evm
def process_create_message(message: Message) -> Evm:
152 | """ |
---|---|
153 | Executes a call to create a smart contract. |
154 |
|
155 | Parameters |
156 | ---------- |
157 | message : |
158 | Transaction specific items. |
159 |
|
160 | Returns |
161 | ------- |
161 | evm: :py:class:`~ethereum.forks.berlin.vm.Evm` |
162 | evm: :py:class:`~ethereum.forks.london.vm.Evm` |
163 | Items containing execution specific objects. |
164 |
|
165 | """ |
166 | state = message.block_env.state |
167 | # take snapshot of state before processing the message |
168 | begin_transaction(state) |
169 | |
170 | # If the address where the account is being created has storage, it is |
171 | # destroyed. This can only happen in the following highly unlikely |
172 | # circumstances: |
173 | # * The address created by a `CREATE` call collides with a subsequent |
174 | # `CREATE` or `CREATE2` call. |
175 | # * The first `CREATE` happened before Spurious Dragon and left empty |
176 | # code. |
177 | destroy_storage(state, message.current_target) |
178 | |
179 | # In the previously mentioned edge case the preexisting storage is ignored |
180 | # for gas refund purposes. In order to do this we must track created |
181 | # accounts. |
182 | mark_account_created(state, message.current_target) |
183 | |
184 | increment_nonce(state, message.current_target) |
185 | evm = process_message(message) |
186 | if not evm.error: |
187 | contract_code = evm.output |
188 | contract_code_gas = Uint(len(contract_code)) * GAS_CODE_DEPOSIT |
189 | try: |
189 | charge_gas(evm, contract_code_gas) |
190 | if len(contract_code) > 0: |
191 | if contract_code[0] == 0xEF: |
192 | raise InvalidContractPrefix |
193 | charge_gas(evm, contract_code_gas) |
194 | if len(contract_code) > MAX_CODE_SIZE: |
195 | raise OutOfGasError |
196 | except ExceptionalHalt as error: |
197 | rollback_transaction(state) |
198 | evm.gas_left = Uint(0) |
199 | evm.output = b"" |
200 | evm.error = error |
201 | else: |
202 | set_code(state, message.current_target, contract_code) |
203 | commit_transaction(state) |
204 | else: |
205 | rollback_transaction(state) |
206 | return evm |
process_message
Move ether and execute the relevant code.
Parameters
message : Transaction specific items.
Returns
evm: :py:class:
Items containing execution specific objects~ethereum.forks.berlin.vm.Evm~ethereum.forks.london.vm.Evm
def process_message(message: Message) -> Evm:
210 | """ |
---|---|
211 | Move ether and execute the relevant code. |
212 |
|
213 | Parameters |
214 | ---------- |
215 | message : |
216 | Transaction specific items. |
217 |
|
218 | Returns |
219 | ------- |
216 | evm: :py:class:`~ethereum.forks.berlin.vm.Evm` |
220 | evm: :py:class:`~ethereum.forks.london.vm.Evm` |
221 | Items containing execution specific objects |
222 |
|
223 | """ |
224 | state = message.block_env.state |
225 | if message.depth > STACK_DEPTH_LIMIT: |
226 | raise StackDepthLimitError("Stack depth limit reached") |
227 | |
228 | # take snapshot of state before processing the message |
229 | begin_transaction(state) |
230 | |
231 | touch_account(state, message.current_target) |
232 | |
233 | if message.should_transfer_value and message.value != 0: |
234 | move_ether( |
235 | state, message.caller, message.current_target, message.value |
236 | ) |
237 | |
238 | evm = execute_code(message) |
239 | if evm.error: |
240 | # revert state to the last saved checkpoint |
241 | # since the message call resulted in an error |
242 | rollback_transaction(state) |
243 | else: |
244 | commit_transaction(state) |
245 | return evm |
execute_code
Executes bytecode present in the message
.
Parameters
message : Transaction specific items.
Returns
evm: ethereum.vm.EVM
Items containing execution specific objects
def execute_code(message: Message) -> Evm:
249 | """ |
---|---|
250 | Executes bytecode present in the `message`. |
251 |
|
252 | Parameters |
253 | ---------- |
254 | message : |
255 | Transaction specific items. |
256 |
|
257 | Returns |
258 | ------- |
259 | evm: `ethereum.vm.EVM` |
260 | Items containing execution specific objects |
261 |
|
262 | """ |
263 | code = message.code |
264 | valid_jump_destinations = get_valid_jump_destinations(code) |
265 | |
266 | evm = Evm( |
267 | pc=Uint(0), |
268 | stack=[], |
269 | memory=bytearray(), |
270 | code=code, |
271 | gas_left=message.gas, |
272 | valid_jump_destinations=valid_jump_destinations, |
273 | logs=(), |
274 | refund_counter=0, |
275 | running=True, |
276 | message=message, |
277 | output=b"", |
278 | accounts_to_delete=set(), |
279 | touched_accounts=set(), |
280 | return_data=b"", |
281 | error=None, |
282 | accessed_addresses=message.accessed_addresses, |
283 | accessed_storage_keys=message.accessed_storage_keys, |
284 | ) |
285 | try: |
286 | if evm.message.code_address in PRE_COMPILED_CONTRACTS: |
287 | evm_trace(evm, PrecompileStart(evm.message.code_address)) |
288 | PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) |
289 | evm_trace(evm, PrecompileEnd()) |
290 | return evm |
291 |
|
292 | while evm.running and evm.pc < ulen(evm.code): |
293 | try: |
294 | op = Ops(evm.code[evm.pc]) |
295 | except ValueError as e: |
296 | raise InvalidOpcode(evm.code[evm.pc]) from e |
297 |
|
298 | evm_trace(evm, OpStart(op)) |
299 | op_implementation[op](evm) |
300 | evm_trace(evm, OpEnd()) |
301 |
|
302 | evm_trace(evm, EvmStop(Ops.STOP)) |
303 |
|
304 | except ExceptionalHalt as error: |
305 | evm_trace(evm, OpException(error)) |
306 | evm.gas_left = Uint(0) |
307 | evm.output = b"" |
308 | evm.error = error |
309 | except Revert as error: |
310 | evm_trace(evm, OpException(error)) |
311 | evm.error = error |
312 | return evm |