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