ethereum.tangerine_whistle.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

54
STACK_DEPTH_LIMIT = U256(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.
57
@dataclass
class MessageCallOutput:

gas_left

71
    gas_left: Uint

refund_counter

72
    refund_counter: U256

logs

73
    logs: Tuple[Log, ...]

accounts_to_delete

74
    accounts_to_delete: Set[Address]

error

75
    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:
81
    """
82
    If `message.current` is empty then it creates a smart contract
83
    else it executes a call from the `message.caller` to the `message.target`.
84
85
    Parameters
86
    ----------
87
    message :
88
        Transaction specific items.
89
90
    env :
91
        External items required for EVM execution.
92
93
    Returns
94
    -------
95
    output : `MessageCallOutput`
96
        Output of the message call
97
    """
98
    if message.target == Bytes0(b""):
99
        is_collision = account_has_code_or_nonce(
100
            env.state, message.current_target
101
        )
102
        if is_collision:
103
            return MessageCallOutput(
104
                Uint(0), U256(0), tuple(), set(), AddressCollision()
105
            )
106
        else:
107
            evm = process_create_message(message, env)
108
    else:
109
        evm = process_message(message, env)
110
111
    if evm.error:
112
        logs: Tuple[Log, ...] = ()
113
        accounts_to_delete = set()
114
        refund_counter = U256(0)
115
    else:
116
        logs = evm.logs
117
        accounts_to_delete = evm.accounts_to_delete
118
        refund_counter = U256(evm.refund_counter)
119
120
    tx_end = TransactionEnd(message.gas - evm.gas_left, evm.output, evm.error)
121
    evm_trace(evm, tx_end)
122
123
    return MessageCallOutput(
124
        gas_left=evm.gas_left,
125
        refund_counter=refund_counter,
126
        logs=logs,
127
        accounts_to_delete=accounts_to_delete,
128
        error=evm.error,
129
    )

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.tangerine_whistle.vm.Evm Items containing execution specific objects.

def process_create_message(message: Message, ​​env: Environment) -> Evm:
133
    """
134
    Executes a call to create a smart contract.
135
136
    Parameters
137
    ----------
138
    message :
139
        Transaction specific items.
140
    env :
141
        External items required for EVM execution.
142
143
    Returns
144
    -------
145
    evm: :py:class:`~ethereum.tangerine_whistle.vm.Evm`
146
        Items containing execution specific objects.
147
    """
148
    # take snapshot of state before processing the message
149
    begin_transaction(env.state)
150
151
    # If the address where the account is being created has storage, it is
152
    # destroyed. This can only happen in the following highly unlikely
153
    # circumstances:
154
    # * The address created by two `CREATE` calls collide.
155
    # * The first `CREATE` left empty code.
156
    destroy_storage(env.state, message.current_target)
157
158
    evm = process_message(message, env)
159
    if not evm.error:
160
        contract_code = evm.output
161
        contract_code_gas = len(contract_code) * GAS_CODE_DEPOSIT
162
        try:
163
            charge_gas(evm, contract_code_gas)
164
        except ExceptionalHalt as error:
165
            rollback_transaction(env.state)
166
            evm.gas_left = Uint(0)
167
            evm.error = error
168
        else:
169
            set_code(env.state, message.current_target, contract_code)
170
            commit_transaction(env.state)
171
    else:
172
        rollback_transaction(env.state)
173
    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.tangerine_whistle.vm.Evm Items containing execution specific objects

def process_message(message: Message, ​​env: Environment) -> Evm:
177
    """
178
    Executes a call to create a smart contract.
179
180
    Parameters
181
    ----------
182
    message :
183
        Transaction specific items.
184
    env :
185
        External items required for EVM execution.
186
187
    Returns
188
    -------
189
    evm: :py:class:`~ethereum.tangerine_whistle.vm.Evm`
190
        Items containing execution specific objects
191
    """
192
    if message.depth > STACK_DEPTH_LIMIT:
193
        raise StackDepthLimitError("Stack depth limit reached")
194
195
    # take snapshot of state before processing the message
196
    begin_transaction(env.state)
197
198
    touch_account(env.state, message.current_target)
199
200
    if message.should_transfer_value and message.value != 0:
201
        move_ether(
202
            env.state, message.caller, message.current_target, message.value
203
        )
204
205
    evm = execute_code(message, env)
206
    if evm.error:
207
        # revert state to the last saved checkpoint
208
        # since the message call resulted in an error
209
        rollback_transaction(env.state)
210
    else:
211
        commit_transaction(env.state)
212
    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:
216
    """
217
    Executes bytecode present in the `message`.
218
219
    Parameters
220
    ----------
221
    message :
222
        Transaction specific items.
223
    env :
224
        External items required for EVM execution.
225
226
    Returns
227
    -------
228
    evm: `ethereum.vm.EVM`
229
        Items containing execution specific objects
230
    """
231
    code = message.code
232
    valid_jump_destinations = get_valid_jump_destinations(code)
233
234
    evm = Evm(
235
        pc=Uint(0),
236
        stack=[],
237
        memory=bytearray(),
238
        code=code,
239
        gas_left=message.gas,
240
        env=env,
241
        valid_jump_destinations=valid_jump_destinations,
242
        logs=(),
243
        refund_counter=U256(0),
244
        running=True,
245
        message=message,
246
        output=b"",
247
        accounts_to_delete=set(),
248
        error=None,
249
    )
250
    try:
251
        if evm.message.code_address in PRE_COMPILED_CONTRACTS:
252
            evm_trace(evm, PrecompileStart(evm.message.code_address))
253
            PRE_COMPILED_CONTRACTS[evm.message.code_address](evm)
254
            evm_trace(evm, PrecompileEnd())
255
            return evm
256
257
        while evm.running and evm.pc < len(evm.code):
258
            try:
259
                op = Ops(evm.code[evm.pc])
260
            except ValueError:
261
                raise InvalidOpcode(evm.code[evm.pc])
262
263
            evm_trace(evm, OpStart(op))
264
            op_implementation[op](evm)
265
            evm_trace(evm, OpEnd())
266
267
        evm_trace(evm, EvmStop(Ops.STOP))
268
269
    except ExceptionalHalt as error:
270
        evm_trace(evm, OpException(error))
271
        evm.gas_left = Uint(0)
272
        evm.error = error
273
    return evm