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