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