ethereum.forks.dao_fork.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
58 | STACK_DEPTH_LIMIT = Uint(1024) |
---|
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.
61 | @dataclass |
---|
class MessageCallOutput:
gas_left
75 | gas_left: Uint |
---|
refund_counter
76 | refund_counter: U256 |
---|
logs
77 | logs: Tuple[Log, ...] |
---|
accounts_to_delete
78 | accounts_to_delete: Set[Address] |
---|
error
79 | 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:
83 | """ |
---|---|
84 | If `message.target` is empty then it creates a smart contract |
85 | else it executes a call from the `message.caller` to the `message.target`. |
86 |
|
87 | Parameters |
88 | ---------- |
89 | message : |
90 | Transaction specific items. |
91 |
|
92 | Returns |
93 | ------- |
94 | output : `MessageCallOutput` |
95 | Output of the message call |
96 |
|
97 | """ |
98 | block_env = message.block_env |
99 | refund_counter = U256(0) |
100 | if message.target == Bytes0(b""): |
101 | is_collision = account_has_code_or_nonce( |
102 | block_env.state, message.current_target |
103 | ) or account_has_storage(block_env.state, message.current_target) |
104 | if is_collision: |
105 | return MessageCallOutput( |
106 | Uint(0), U256(0), tuple(), set(), AddressCollision() |
107 | ) |
108 | else: |
109 | evm = process_create_message(message) |
110 | else: |
111 | evm = process_message(message) |
112 | |
113 | if evm.error: |
114 | logs: Tuple[Log, ...] = () |
115 | accounts_to_delete = set() |
116 | else: |
117 | logs = evm.logs |
118 | accounts_to_delete = evm.accounts_to_delete |
119 | refund_counter += U256(evm.refund_counter) |
120 | |
121 | tx_end = TransactionEnd( |
122 | int(message.gas) - int(evm.gas_left), evm.output, evm.error |
123 | ) |
124 | evm_trace(evm, tx_end) |
125 | |
126 | return MessageCallOutput( |
127 | gas_left=evm.gas_left, |
128 | refund_counter=refund_counter, |
129 | logs=logs, |
130 | accounts_to_delete=accounts_to_delete, |
131 | error=evm.error, |
132 | ) |
process_create_message
Executes a call to create a smart contract.
Parameters
message : Transaction specific items.
Returns
evm: :py:class:~ethereum.forks.dao_fork.vm.Evm
Items containing execution specific objects.
def process_create_message(message: Message) -> Evm:
136 | """ |
---|---|
137 | Executes a call to create a smart contract. |
138 |
|
139 | Parameters |
140 | ---------- |
141 | message : |
142 | Transaction specific items. |
143 |
|
144 | Returns |
145 | ------- |
146 | evm: :py:class:`~ethereum.forks.dao_fork.vm.Evm` |
147 | Items containing execution specific objects. |
148 |
|
149 | """ |
150 | state = message.block_env.state |
151 | # take snapshot of state before processing the message |
152 | begin_transaction(state) |
153 | |
154 | # If the address where the account is being created has storage, it is |
155 | # destroyed. This can only happen in the following highly unlikely |
156 | # circumstances: |
157 | # * The address created by two `CREATE` calls collide. |
158 | # * The first `CREATE` left empty code. |
159 | destroy_storage(state, message.current_target) |
160 | |
161 | evm = process_message(message) |
162 | if not evm.error: |
163 | contract_code = evm.output |
164 | contract_code_gas = Uint(len(contract_code)) * GAS_CODE_DEPOSIT |
165 | try: |
166 | charge_gas(evm, contract_code_gas) |
167 | except ExceptionalHalt as error: |
168 | rollback_transaction(state) |
169 | evm.gas_left = Uint(0) |
170 | evm.error = error |
171 | else: |
172 | set_code(state, message.current_target, contract_code) |
173 | commit_transaction(state) |
174 | else: |
175 | rollback_transaction(state) |
176 | return evm |
process_message
Move ether and execute the relevant code.
Parameters
message : Transaction specific items.
Returns
evm: :py:class:~ethereum.forks.dao_fork.vm.Evm
Items containing execution specific objects
def process_message(message: Message) -> Evm:
180 | """ |
---|---|
181 | Move ether and execute the relevant code. |
182 |
|
183 | Parameters |
184 | ---------- |
185 | message : |
186 | Transaction specific items. |
187 |
|
188 | Returns |
189 | ------- |
190 | evm: :py:class:`~ethereum.forks.dao_fork.vm.Evm` |
191 | Items containing execution specific objects |
192 |
|
193 | """ |
194 | state = message.block_env.state |
195 | if message.depth > STACK_DEPTH_LIMIT: |
196 | raise StackDepthLimitError("Stack depth limit reached") |
197 | |
198 | # take snapshot of state before processing the message |
199 | begin_transaction(state) |
200 | |
201 | touch_account(state, message.current_target) |
202 | |
203 | if message.should_transfer_value and message.value != 0: |
204 | move_ether( |
205 | state, message.caller, message.current_target, message.value |
206 | ) |
207 | |
208 | evm = execute_code(message) |
209 | if evm.error: |
210 | # revert state to the last saved checkpoint |
211 | # since the message call resulted in an error |
212 | rollback_transaction(state) |
213 | else: |
214 | commit_transaction(state) |
215 | 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:
219 | """ |
---|---|
220 | Executes bytecode present in the `message`. |
221 |
|
222 | Parameters |
223 | ---------- |
224 | message : |
225 | Transaction specific items. |
226 |
|
227 | Returns |
228 | ------- |
229 | evm: `ethereum.vm.EVM` |
230 | Items containing execution specific objects |
231 |
|
232 | """ |
233 | code = message.code |
234 | valid_jump_destinations = get_valid_jump_destinations(code) |
235 | |
236 | evm = Evm( |
237 | pc=Uint(0), |
238 | stack=[], |
239 | memory=bytearray(), |
240 | code=code, |
241 | gas_left=message.gas, |
242 | valid_jump_destinations=valid_jump_destinations, |
243 | logs=(), |
244 | refund_counter=0, |
245 | running=True, |
246 | message=message, |
247 | output=b"", |
248 | accounts_to_delete=set(), |
249 | error=None, |
250 | ) |
251 | try: |
252 | if evm.message.code_address in PRE_COMPILED_CONTRACTS: |
253 | evm_trace(evm, PrecompileStart(evm.message.code_address)) |
254 | PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) |
255 | evm_trace(evm, PrecompileEnd()) |
256 | return evm |
257 |
|
258 | while evm.running and evm.pc < ulen(evm.code): |
259 | try: |
260 | op = Ops(evm.code[evm.pc]) |
261 | except ValueError as e: |
262 | raise InvalidOpcode(evm.code[evm.pc]) from e |
263 |
|
264 | evm_trace(evm, OpStart(op)) |
265 | op_implementation[op](evm) |
266 | evm_trace(evm, OpEnd()) |
267 |
|
268 | evm_trace(evm, EvmStop(Ops.STOP)) |
269 |
|
270 | except ExceptionalHalt as error: |
271 | evm_trace(evm, OpException(error)) |
272 | evm.gas_left = Uint(0) |
273 | evm.error = error |
274 | return evm |