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

return_

Halts execution returning output data.

Parameters

evm : The current EVM frame.

def return_(evm: Evm) -> None:
137
    <snip>
146
    # STACK
147
    memory_start_position = pop(evm.stack)
148
    memory_size = pop(evm.stack)
149
150
    # GAS
151
    extend_memory = calculate_gas_extend_memory(
152
        evm.memory, [(memory_start_position, memory_size)]
153
    )
154
155
    charge_gas(evm, GasCosts.ZERO + extend_memory.cost)
156
157
    # OPERATION
158
    evm.memory += b"\x00" * extend_memory.expand_by
159
    evm.output = memory_read_bytes(
160
        evm.memory, memory_start_position, memory_size
161
    )
162
163
    evm.running = False
164
165
    # PROGRAM COUNTER
166
    pass

GenericCall

Parameters for the core logic of the CALL* family of opcodes.

169
@final
170
@dataclass
class GenericCall:

gas

176
    gas: Uint

value

177
    value: U256

caller

178
    caller: Address

to

179
    to: Address

code_address

180
    code_address: Address

should_transfer_value

181
    should_transfer_value: bool

memory_input_start_position

182
    memory_input_start_position: U256

memory_input_size

183
    memory_input_size: U256

memory_output_start_position

184
    memory_output_start_position: U256

memory_output_size

185
    memory_output_size: U256

generic_call

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

def generic_call(evm: Evm, ​​params: GenericCall) -> None:
189
    <snip>
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 += params.gas
196
        push(evm.stack, U256(0))
197
        return
198
199
    call_data = memory_read_bytes(
200
        evm.memory,
201
        params.memory_input_start_position,
202
        params.memory_input_size,
203
    )
204
    account = get_account(evm.message.tx_env.state, params.code_address)
205
    code = get_code(evm.message.tx_env.state, account.code_hash)
206
    child_message = Message(
207
        block_env=evm.message.block_env,
208
        tx_env=evm.message.tx_env,
209
        caller=params.caller,
210
        target=params.to,
211
        gas=params.gas,
212
        value=params.value,
213
        data=call_data,
214
        code=code,
215
        current_target=params.to,
216
        depth=evm.message.depth + Uint(1),
217
        code_address=params.code_address,
218
        should_transfer_value=params.should_transfer_value,
219
        parent_evm=evm,
220
    )
221
    child_evm = process_message(child_message)
222
223
    if child_evm.error:
224
        incorporate_child_on_error(evm, child_evm)
225
        push(evm.stack, U256(0))
226
    else:
227
        incorporate_child_on_success(evm, child_evm)
228
        push(evm.stack, U256(1))
229
230
    actual_output_size = min(
231
        params.memory_output_size, U256(len(child_evm.output))
232
    )
233
    memory_write(
234
        evm.memory,
235
        params.memory_output_start_position,
236
        child_evm.output[:actual_output_size],
237
    )

call

Message-call into an account.

Parameters

evm : The current EVM frame.

def call(evm: Evm) -> None:
241
    <snip>
250
    # STACK
251
    gas = Uint(pop(evm.stack))
252
    to = to_address_masked(pop(evm.stack))
253
    value = pop(evm.stack)
254
    memory_input_start_position = pop(evm.stack)
255
    memory_input_size = pop(evm.stack)
256
    memory_output_start_position = pop(evm.stack)
257
    memory_output_size = pop(evm.stack)
258
259
    # GAS
260
    extend_memory = calculate_gas_extend_memory(
261
        evm.memory,
262
        [
263
            (memory_input_start_position, memory_input_size),
264
            (memory_output_start_position, memory_output_size),
265
        ],
266
    )
267
268
    code_address = to
269
270
    _account_exists = account_exists(evm.message.tx_env.state, to)
271
    create_gas_cost = Uint(0) if _account_exists else GasCosts.NEW_ACCOUNT
272
    transfer_gas_cost = Uint(0) if value == 0 else GasCosts.CALL_VALUE
273
    message_call_gas = calculate_message_call_gas(
274
        value,
275
        gas,
276
        Uint(evm.gas_left),
277
        memory_cost=extend_memory.cost,
278
        extra_gas=GasCosts.OPCODE_CALL_BASE
279
        + create_gas_cost
280
        + transfer_gas_cost,
281
    )
282
    charge_gas(evm, message_call_gas.cost + extend_memory.cost)
283
284
    # OPERATION
285
    evm.memory += b"\x00" * extend_memory.expand_by
286
    sender_balance = get_account(
287
        evm.message.tx_env.state, evm.message.current_target
288
    ).balance
289
    if sender_balance < value:
290
        push(evm.stack, U256(0))
291
        evm.gas_left += message_call_gas.sub_call
292
    else:
293
        generic_call(
294
            evm,
295
            GenericCall(
296
                gas=message_call_gas.sub_call,
297
                value=value,
298
                caller=evm.message.current_target,
299
                to=to,
300
                code_address=code_address,
301
                should_transfer_value=True,
302
                memory_input_start_position=memory_input_start_position,
303
                memory_input_size=memory_input_size,
304
                memory_output_start_position=memory_output_start_position,
305
                memory_output_size=memory_output_size,
306
            ),
307
        )
308
309
    # PROGRAM COUNTER
310
    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:
314
    <snip>
323
    # STACK
324
    gas = Uint(pop(evm.stack))
325
    code_address = to_address_masked(pop(evm.stack))
326
    value = pop(evm.stack)
327
    memory_input_start_position = pop(evm.stack)
328
    memory_input_size = pop(evm.stack)
329
    memory_output_start_position = pop(evm.stack)
330
    memory_output_size = pop(evm.stack)
331
332
    # GAS
333
    to = evm.message.current_target
334
335
    extend_memory = calculate_gas_extend_memory(
336
        evm.memory,
337
        [
338
            (memory_input_start_position, memory_input_size),
339
            (memory_output_start_position, memory_output_size),
340
        ],
341
    )
342
    transfer_gas_cost = Uint(0) if value == 0 else GasCosts.CALL_VALUE
343
    message_call_gas = calculate_message_call_gas(
344
        value,
345
        gas,
346
        Uint(evm.gas_left),
347
        extend_memory.cost,
348
        GasCosts.OPCODE_CALL_BASE + transfer_gas_cost,
349
    )
350
    charge_gas(evm, message_call_gas.cost + extend_memory.cost)
351
352
    # OPERATION
353
    evm.memory += b"\x00" * extend_memory.expand_by
354
    sender_balance = get_account(
355
        evm.message.tx_env.state, evm.message.current_target
356
    ).balance
357
    if sender_balance < value:
358
        push(evm.stack, U256(0))
359
        evm.gas_left += message_call_gas.sub_call
360
    else:
361
        generic_call(
362
            evm,
363
            GenericCall(
364
                gas=message_call_gas.sub_call,
365
                value=value,
366
                caller=evm.message.current_target,
367
                to=to,
368
                code_address=code_address,
369
                should_transfer_value=True,
370
                memory_input_start_position=memory_input_start_position,
371
                memory_input_size=memory_input_size,
372
                memory_output_start_position=memory_output_start_position,
373
                memory_output_size=memory_output_size,
374
            ),
375
        )
376
377
    # PROGRAM COUNTER
378
    evm.pc += Uint(1)

selfdestruct

Halt execution and register account for later deletion.

Parameters

evm : The current EVM frame.

def selfdestruct(evm: Evm) -> None:
382
    <snip>
391
    # STACK
392
    beneficiary = to_address_masked(pop(evm.stack))
393
394
    # GAS
395
    gas_cost = GasCosts.OPCODE_SELFDESTRUCT_BASE
396
    if not account_exists(evm.message.tx_env.state, beneficiary):
397
        gas_cost += GasCosts.OPCODE_SELFDESTRUCT_NEW_ACCOUNT
398
399
    originator = evm.message.current_target
400
401
    refunded_accounts = evm.accounts_to_delete
402
    parent_evm = evm.message.parent_evm
403
    while parent_evm is not None:
404
        refunded_accounts.update(parent_evm.accounts_to_delete)
405
        parent_evm = parent_evm.message.parent_evm
406
407
    if originator not in refunded_accounts:
408
        evm.refund_counter += GasCosts.REFUND_SELF_DESTRUCT
409
410
    charge_gas(evm, gas_cost)
411
412
    # OPERATION
413
    beneficiary_balance = get_account(
414
        evm.message.tx_env.state, beneficiary
415
    ).balance
416
    originator_balance = get_account(
417
        evm.message.tx_env.state, originator
418
    ).balance
419
420
    # First Transfer to beneficiary
421
    set_account_balance(
422
        evm.message.tx_env.state,
423
        beneficiary,
424
        beneficiary_balance + originator_balance,
425
    )
426
    # Next, Zero the balance of the address being deleted (must come after
427
    # sending to beneficiary in case the contract named itself as the
428
    # beneficiary).
429
    set_account_balance(evm.message.tx_env.state, originator, U256(0))
430
431
    # register account for deletion
432
    evm.accounts_to_delete.add(originator)
433
434
    # HALT the execution
435
    evm.running = False
436
437
    # PROGRAM COUNTER
438
    pass

delegatecall

Message-call into an account.

Parameters

evm : The current EVM frame.

def delegatecall(evm: Evm) -> None:
442
    <snip>
451
    # STACK
452
    gas = Uint(pop(evm.stack))
453
    code_address = to_address_masked(pop(evm.stack))
454
    memory_input_start_position = pop(evm.stack)
455
    memory_input_size = pop(evm.stack)
456
    memory_output_start_position = pop(evm.stack)
457
    memory_output_size = pop(evm.stack)
458
459
    # GAS
460
    extend_memory = calculate_gas_extend_memory(
461
        evm.memory,
462
        [
463
            (memory_input_start_position, memory_input_size),
464
            (memory_output_start_position, memory_output_size),
465
        ],
466
    )
467
    message_call_gas = calculate_message_call_gas(
468
        U256(0),
469
        gas,
470
        Uint(evm.gas_left),
471
        extend_memory.cost,
472
        GasCosts.OPCODE_CALL_BASE,
473
    )
474
    charge_gas(evm, message_call_gas.cost + extend_memory.cost)
475
476
    # OPERATION
477
    evm.memory += b"\x00" * extend_memory.expand_by
478
    generic_call(
479
        evm,
480
        GenericCall(
481
            gas=message_call_gas.sub_call,
482
            value=evm.message.value,
483
            caller=evm.message.caller,
484
            to=evm.message.current_target,
485
            code_address=code_address,
486
            should_transfer_value=False,
487
            memory_input_start_position=memory_input_start_position,
488
            memory_input_size=memory_input_size,
489
            memory_output_start_position=memory_output_start_position,
490
            memory_output_size=memory_output_size,
491
        ),
492
    )
493
494
    # PROGRAM COUNTER
495
    evm.pc += Uint(1)