ethereum.tangerine_whistle.vm.instructions.system

Ethereum Virtual Machine (EVM) System Instructions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. contents:: Table of Contents :backlinks: none :local:

Introduction

Implementations of the EVM system related instructions.

create

Creates a new account with associated code.

Parameters

evm : The current EVM frame.

def create(evm: Evm) -> None:
52
    """
53
    Creates a new account with associated code.
54
55
    Parameters
56
    ----------
57
    evm :
58
        The current EVM frame.
59
    """
60
    # This import causes a circular import error
61
    # if it's not moved inside this method
62
    from ...vm.interpreter import STACK_DEPTH_LIMIT, process_create_message
63
64
    # STACK
65
    endowment = pop(evm.stack)
66
    memory_start_position = pop(evm.stack)
67
    memory_size = pop(evm.stack)
68
69
    # GAS
70
    extend_memory = calculate_gas_extend_memory(
71
        evm.memory, [(memory_start_position, memory_size)]
72
    )
73
74
    charge_gas(evm, GAS_CREATE + extend_memory.cost)
75
76
    create_message_gas = max_message_call_gas(Uint(evm.gas_left))
77
    evm.gas_left -= create_message_gas
78
79
    # OPERATION
80
    evm.memory += b"\x00" * extend_memory.expand_by
81
    sender_address = evm.message.current_target
82
    sender = get_account(evm.message.block_env.state, sender_address)
83
84
    contract_address = compute_contract_address(
85
        evm.message.current_target,
86
        get_account(
87
            evm.message.block_env.state, evm.message.current_target
88
        ).nonce,
89
    )
90
91
    if (
92
        sender.balance < endowment
93
        or sender.nonce == Uint(2**64 - 1)
94
        or evm.message.depth + Uint(1) > STACK_DEPTH_LIMIT
95
    ):
96
        push(evm.stack, U256(0))
97
        evm.gas_left += create_message_gas
98
    elif account_has_code_or_nonce(
99
        evm.message.block_env.state, contract_address
100
    ) or account_has_storage(evm.message.block_env.state, contract_address):
101
        increment_nonce(
102
            evm.message.block_env.state, evm.message.current_target
103
        )
104
        push(evm.stack, U256(0))
105
    else:
106
        call_data = memory_read_bytes(
107
            evm.memory, memory_start_position, memory_size
108
        )
109
110
        increment_nonce(
111
            evm.message.block_env.state, evm.message.current_target
112
        )
113
114
        child_message = Message(
115
            block_env=evm.message.block_env,
116
            tx_env=evm.message.tx_env,
117
            caller=evm.message.current_target,
118
            target=Bytes0(),
119
            gas=create_message_gas,
120
            value=endowment,
121
            data=b"",
122
            code=call_data,
123
            current_target=contract_address,
124
            depth=evm.message.depth + Uint(1),
125
            code_address=None,
126
            should_transfer_value=True,
127
            parent_evm=evm,
128
        )
129
        child_evm = process_create_message(child_message)
130
131
        if child_evm.error:
132
            incorporate_child_on_error(evm, child_evm)
133
            push(evm.stack, U256(0))
134
        else:
135
            incorporate_child_on_success(evm, child_evm)
136
            push(
137
                evm.stack, U256.from_be_bytes(child_evm.message.current_target)
138
            )
139
140
    # PROGRAM COUNTER
141
    evm.pc += Uint(1)

return_

Halts execution returning output data.

Parameters

evm : The current EVM frame.

def return_(evm: Evm) -> None:
145
    """
146
    Halts execution returning output data.
147
148
    Parameters
149
    ----------
150
    evm :
151
        The current EVM frame.
152
    """
153
    # STACK
154
    memory_start_position = pop(evm.stack)
155
    memory_size = pop(evm.stack)
156
157
    # GAS
158
    extend_memory = calculate_gas_extend_memory(
159
        evm.memory, [(memory_start_position, memory_size)]
160
    )
161
162
    charge_gas(evm, GAS_ZERO + extend_memory.cost)
163
164
    # OPERATION
165
    evm.memory += b"\x00" * extend_memory.expand_by
166
    evm.output = memory_read_bytes(
167
        evm.memory, memory_start_position, memory_size
168
    )
169
170
    evm.running = False
171
172
    # PROGRAM COUNTER
173
    pass

generic_call

Perform the core logic of the CALL* family of opcodes.

def generic_call(evm: Evm, ​​gas: Uint, ​​value: U256, ​​caller: Address, ​​to: Address, ​​code_address: Address, ​​should_transfer_value: bool, ​​memory_input_start_position: U256, ​​memory_input_size: U256, ​​memory_output_start_position: U256, ​​memory_output_size: U256) -> None:
189
    """
190
    Perform the core logic of the `CALL*` family of opcodes.
191
    """
192
    from ...vm.interpreter import STACK_DEPTH_LIMIT, process_message
193
194
    if evm.message.depth + Uint(1) > STACK_DEPTH_LIMIT:
195
        evm.gas_left += gas
196
        push(evm.stack, U256(0))
197
        return
198
199
    call_data = memory_read_bytes(
200
        evm.memory, memory_input_start_position, memory_input_size
201
    )
202
    code = get_account(evm.message.block_env.state, code_address).code
203
    child_message = Message(
204
        block_env=evm.message.block_env,
205
        tx_env=evm.message.tx_env,
206
        caller=caller,
207
        target=to,
208
        gas=gas,
209
        value=value,
210
        data=call_data,
211
        code=code,
212
        current_target=to,
213
        depth=evm.message.depth + Uint(1),
214
        code_address=code_address,
215
        should_transfer_value=should_transfer_value,
216
        parent_evm=evm,
217
    )
218
    child_evm = process_message(child_message)
219
220
    if child_evm.error:
221
        incorporate_child_on_error(evm, child_evm)
222
        push(evm.stack, U256(0))
223
    else:
224
        incorporate_child_on_success(evm, child_evm)
225
        push(evm.stack, U256(1))
226
227
    actual_output_size = min(memory_output_size, U256(len(child_evm.output)))
228
    memory_write(
229
        evm.memory,
230
        memory_output_start_position,
231
        child_evm.output[:actual_output_size],
232
    )

call

Message-call into an account.

Parameters

evm : The current EVM frame.

def call(evm: Evm) -> None:
236
    """
237
    Message-call into an account.
238
239
    Parameters
240
    ----------
241
    evm :
242
        The current EVM frame.
243
    """
244
    # STACK
245
    gas = Uint(pop(evm.stack))
246
    to = to_address(pop(evm.stack))
247
    value = pop(evm.stack)
248
    memory_input_start_position = pop(evm.stack)
249
    memory_input_size = pop(evm.stack)
250
    memory_output_start_position = pop(evm.stack)
251
    memory_output_size = pop(evm.stack)
252
253
    # GAS
254
    extend_memory = calculate_gas_extend_memory(
255
        evm.memory,
256
        [
257
            (memory_input_start_position, memory_input_size),
258
            (memory_output_start_position, memory_output_size),
259
        ],
260
    )
261
262
    code_address = to
263
264
    _account_exists = account_exists(evm.message.block_env.state, to)
265
    create_gas_cost = Uint(0) if _account_exists else GAS_NEW_ACCOUNT
266
    transfer_gas_cost = Uint(0) if value == 0 else GAS_CALL_VALUE
267
    message_call_gas = calculate_message_call_gas(
268
        value,
269
        gas,
270
        Uint(evm.gas_left),
271
        extend_memory.cost,
272
        GAS_CALL + create_gas_cost + transfer_gas_cost,
273
    )
274
    charge_gas(evm, message_call_gas.cost + extend_memory.cost)
275
276
    # OPERATION
277
    evm.memory += b"\x00" * extend_memory.expand_by
278
    sender_balance = get_account(
279
        evm.message.block_env.state, evm.message.current_target
280
    ).balance
281
    if sender_balance < value:
282
        push(evm.stack, U256(0))
283
        evm.gas_left += message_call_gas.sub_call
284
    else:
285
        generic_call(
286
            evm,
287
            message_call_gas.sub_call,
288
            value,
289
            evm.message.current_target,
290
            to,
291
            code_address,
292
            True,
293
            memory_input_start_position,
294
            memory_input_size,
295
            memory_output_start_position,
296
            memory_output_size,
297
        )
298
299
    # PROGRAM COUNTER
300
    evm.pc += Uint(1)

callcode

Message-call into this account with alternative account’s code.

Parameters

evm : The current EVM frame.

def callcode(evm: Evm) -> None:
304
    """
305
    Message-call into this account with alternative account’s code.
306
307
    Parameters
308
    ----------
309
    evm :
310
        The current EVM frame.
311
    """
312
    # STACK
313
    gas = Uint(pop(evm.stack))
314
    code_address = to_address(pop(evm.stack))
315
    value = pop(evm.stack)
316
    memory_input_start_position = pop(evm.stack)
317
    memory_input_size = pop(evm.stack)
318
    memory_output_start_position = pop(evm.stack)
319
    memory_output_size = pop(evm.stack)
320
321
    # GAS
322
    to = evm.message.current_target
323
324
    extend_memory = calculate_gas_extend_memory(
325
        evm.memory,
326
        [
327
            (memory_input_start_position, memory_input_size),
328
            (memory_output_start_position, memory_output_size),
329
        ],
330
    )
331
    transfer_gas_cost = Uint(0) if value == 0 else GAS_CALL_VALUE
332
    message_call_gas = calculate_message_call_gas(
333
        value,
334
        gas,
335
        Uint(evm.gas_left),
336
        extend_memory.cost,
337
        GAS_CALL + transfer_gas_cost,
338
    )
339
    charge_gas(evm, message_call_gas.cost + extend_memory.cost)
340
341
    # OPERATION
342
    evm.memory += b"\x00" * extend_memory.expand_by
343
    sender_balance = get_account(
344
        evm.message.block_env.state, evm.message.current_target
345
    ).balance
346
    if sender_balance < value:
347
        push(evm.stack, U256(0))
348
        evm.gas_left += message_call_gas.sub_call
349
    else:
350
        generic_call(
351
            evm,
352
            message_call_gas.sub_call,
353
            value,
354
            evm.message.current_target,
355
            to,
356
            code_address,
357
            True,
358
            memory_input_start_position,
359
            memory_input_size,
360
            memory_output_start_position,
361
            memory_output_size,
362
        )
363
364
    # PROGRAM COUNTER
365
    evm.pc += Uint(1)

selfdestruct

Halt execution and register account for later deletion.

Parameters

evm : The current EVM frame.

def selfdestruct(evm: Evm) -> None:
369
    """
370
    Halt execution and register account for later deletion.
371
372
    Parameters
373
    ----------
374
    evm :
375
        The current EVM frame.
376
    """
377
    # STACK
378
    beneficiary = to_address(pop(evm.stack))
379
380
    # GAS
381
    gas_cost = GAS_SELF_DESTRUCT
382
    if not account_exists(evm.message.block_env.state, beneficiary):
383
        gas_cost += GAS_SELF_DESTRUCT_NEW_ACCOUNT
384
385
    originator = evm.message.current_target
386
387
    refunded_accounts = evm.accounts_to_delete
388
    parent_evm = evm.message.parent_evm
389
    while parent_evm is not None:
390
        refunded_accounts.update(parent_evm.accounts_to_delete)
391
        parent_evm = parent_evm.message.parent_evm
392
393
    if originator not in refunded_accounts:
394
        evm.refund_counter += REFUND_SELF_DESTRUCT
395
396
    charge_gas(evm, gas_cost)
397
398
    # OPERATION
399
    beneficiary_balance = get_account(
400
        evm.message.block_env.state, beneficiary
401
    ).balance
402
    originator_balance = get_account(
403
        evm.message.block_env.state, originator
404
    ).balance
405
406
    # First Transfer to beneficiary
407
    set_account_balance(
408
        evm.message.block_env.state,
409
        beneficiary,
410
        beneficiary_balance + originator_balance,
411
    )
412
    # Next, Zero the balance of the address being deleted (must come after
413
    # sending to beneficiary in case the contract named itself as the
414
    # beneficiary).
415
    set_account_balance(evm.message.block_env.state, originator, U256(0))
416
417
    # register account for deletion
418
    evm.accounts_to_delete.add(originator)
419
420
    # HALT the execution
421
    evm.running = False
422
423
    # PROGRAM COUNTER
424
    pass

delegatecall

Message-call into an account.

Parameters

evm : The current EVM frame.

def delegatecall(evm: Evm) -> None:
428
    """
429
    Message-call into an account.
430
431
    Parameters
432
    ----------
433
    evm :
434
        The current EVM frame.
435
    """
436
    # STACK
437
    gas = Uint(pop(evm.stack))
438
    code_address = to_address(pop(evm.stack))
439
    memory_input_start_position = pop(evm.stack)
440
    memory_input_size = pop(evm.stack)
441
    memory_output_start_position = pop(evm.stack)
442
    memory_output_size = pop(evm.stack)
443
444
    # GAS
445
    extend_memory = calculate_gas_extend_memory(
446
        evm.memory,
447
        [
448
            (memory_input_start_position, memory_input_size),
449
            (memory_output_start_position, memory_output_size),
450
        ],
451
    )
452
    message_call_gas = calculate_message_call_gas(
453
        U256(0), gas, Uint(evm.gas_left), extend_memory.cost, GAS_CALL
454
    )
455
    charge_gas(evm, message_call_gas.cost + extend_memory.cost)
456
457
    # OPERATION
458
    evm.memory += b"\x00" * extend_memory.expand_by
459
    generic_call(
460
        evm,
461
        message_call_gas.sub_call,
462
        evm.message.value,
463
        evm.message.caller,
464
        evm.message.current_target,
465
        code_address,
466
        False,
467
        memory_input_start_position,
468
        memory_input_size,
469
        memory_output_start_position,
470
        memory_output_size,
471
    )
472
473
    # PROGRAM COUNTER
474
    evm.pc += Uint(1)