ethereum.forks.paris.vm.instructions.system

Ethereum Virtual Machine (EVM) System Instructions.

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

Introduction

Implementations of the EVM system related instructions.

generic_create

Core logic used by the CREATE* family of opcodes.

def generic_create(evm: Evm, ​​endowment: U256, ​​contract_address: Address, ​​memory_start_position: U256, ​​memory_size: U256) -> None:
61
    <snip>
64
    # This import causes a circular import error
65
    # if it's not moved inside this method
66
    from ...vm.interpreter import STACK_DEPTH_LIMIT, process_create_message
67
68
    call_data = memory_read_bytes(
69
        evm.memory, memory_start_position, memory_size
70
    )
71
72
    create_message_gas = max_message_call_gas(Uint(evm.gas_left))
73
    evm.gas_left -= create_message_gas
74
    if evm.message.is_static:
75
        raise WriteInStaticContext
76
    evm.return_data = b""
77
78
    sender_address = evm.message.current_target
79
    sender = get_account(evm.message.tx_env.state, sender_address)
80
81
    if (
82
        sender.balance < endowment
83
        or sender.nonce == Uint(2**64 - 1)
84
        or evm.message.depth + Uint(1) > STACK_DEPTH_LIMIT
85
    ):
86
        evm.gas_left += create_message_gas
87
        push(evm.stack, U256(0))
88
        return
89
90
    evm.accessed_addresses.add(contract_address)
91
92
    if not account_deployable(evm.message.tx_env.state, contract_address):
93
        increment_nonce(evm.message.tx_env.state, evm.message.current_target)
94
        push(evm.stack, U256(0))
95
        return
96
97
    increment_nonce(evm.message.tx_env.state, evm.message.current_target)
98
99
    child_message = Message(
100
        block_env=evm.message.block_env,
101
        tx_env=evm.message.tx_env,
102
        caller=evm.message.current_target,
103
        target=Bytes0(),
104
        gas=create_message_gas,
105
        value=endowment,
106
        data=b"",
107
        code=call_data,
108
        current_target=contract_address,
109
        depth=evm.message.depth + Uint(1),
110
        code_address=None,
111
        should_transfer_value=True,
112
        is_static=False,
113
        accessed_addresses=evm.accessed_addresses.copy(),
114
        accessed_storage_keys=evm.accessed_storage_keys.copy(),
115
        parent_evm=evm,
116
    )
117
    child_evm = process_create_message(child_message)
118
119
    if child_evm.error:
120
        incorporate_child_on_error(evm, child_evm)
121
        evm.return_data = child_evm.output
122
        push(evm.stack, U256(0))
123
    else:
124
        incorporate_child_on_success(evm, child_evm)
125
        evm.return_data = b""
126
        push(evm.stack, U256.from_be_bytes(child_evm.message.current_target))

create

Creates a new account with associated code.

Parameters

evm : The current EVM frame.

def create(evm: Evm) -> None:
130
    <snip>
139
    # STACK
140
    endowment = pop(evm.stack)
141
    memory_start_position = pop(evm.stack)
142
    memory_size = pop(evm.stack)
143
144
    # GAS
145
    extend_memory = calculate_gas_extend_memory(
146
        evm.memory, [(memory_start_position, memory_size)]
147
    )
148
149
    charge_gas(evm, GasCosts.OPCODE_CREATE_BASE + extend_memory.cost)
150
151
    # OPERATION
152
    evm.memory += b"\x00" * extend_memory.expand_by
153
    contract_address = compute_contract_address(
154
        evm.message.current_target,
155
        get_account(
156
            evm.message.tx_env.state, evm.message.current_target
157
        ).nonce,
158
    )
159
160
    generic_create(
161
        evm, endowment, contract_address, memory_start_position, memory_size
162
    )
163
164
    # PROGRAM COUNTER
165
    evm.pc += Uint(1)

create2

Creates a new account with associated code.

It's similar to the CREATE opcode except that the address of the new account depends on the init_code instead of the nonce of sender.

Parameters

evm : The current EVM frame.

def create2(evm: Evm) -> None:
169
    <snip>
181
    # STACK
182
    endowment = pop(evm.stack)
183
    memory_start_position = pop(evm.stack)
184
    memory_size = pop(evm.stack)
185
    salt = pop(evm.stack).to_be_bytes32()
186
187
    # GAS
188
    extend_memory = calculate_gas_extend_memory(
189
        evm.memory, [(memory_start_position, memory_size)]
190
    )
191
    call_data_words = ceil32(Uint(memory_size)) // Uint(32)
192
    charge_gas(
193
        evm,
194
        GasCosts.OPCODE_CREATE_BASE
195
        + GasCosts.OPCODE_KECCAK256_PER_WORD * call_data_words
196
        + extend_memory.cost,
197
    )
198
199
    # OPERATION
200
    evm.memory += b"\x00" * extend_memory.expand_by
201
    contract_address = compute_create2_contract_address(
202
        evm.message.current_target,
203
        salt,
204
        memory_read_bytes(evm.memory, memory_start_position, memory_size),
205
    )
206
207
    generic_create(
208
        evm, endowment, contract_address, memory_start_position, memory_size
209
    )
210
211
    # PROGRAM COUNTER
212
    evm.pc += Uint(1)

return_

Halts execution returning output data.

Parameters

evm : The current EVM frame.

def return_(evm: Evm) -> None:
216
    <snip>
225
    # STACK
226
    memory_start_position = pop(evm.stack)
227
    memory_size = pop(evm.stack)
228
229
    # GAS
230
    extend_memory = calculate_gas_extend_memory(
231
        evm.memory, [(memory_start_position, memory_size)]
232
    )
233
234
    charge_gas(evm, GasCosts.ZERO + extend_memory.cost)
235
236
    # OPERATION
237
    evm.memory += b"\x00" * extend_memory.expand_by
238
    evm.output = memory_read_bytes(
239
        evm.memory, memory_start_position, memory_size
240
    )
241
242
    evm.running = False
243
244
    # PROGRAM COUNTER
245
    pass

GenericCall

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

248
@final
249
@dataclass
class GenericCall:

gas

255
    gas: Uint

value

256
    value: U256

caller

257
    caller: Address

to

258
    to: Address

code_address

259
    code_address: Address

should_transfer_value

260
    should_transfer_value: bool

is_staticcall

261
    is_staticcall: bool

memory_input_start_position

262
    memory_input_start_position: U256

memory_input_size

263
    memory_input_size: U256

memory_output_start_position

264
    memory_output_start_position: U256

memory_output_size

265
    memory_output_size: U256

generic_call

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

def generic_call(evm: Evm, ​​params: GenericCall) -> None:
269
    <snip>
272
    from ...vm.interpreter import STACK_DEPTH_LIMIT, process_message
273
274
    evm.return_data = b""
275
276
    if evm.message.depth + Uint(1) > STACK_DEPTH_LIMIT:
277
        evm.gas_left += params.gas
278
        push(evm.stack, U256(0))
279
        return
280
281
    call_data = memory_read_bytes(
282
        evm.memory,
283
        params.memory_input_start_position,
284
        params.memory_input_size,
285
    )
286
    tx_state = evm.message.tx_env.state
287
    code = get_code(
288
        tx_state, get_account(tx_state, params.code_address).code_hash
289
    )
290
    child_message = Message(
291
        block_env=evm.message.block_env,
292
        tx_env=evm.message.tx_env,
293
        caller=params.caller,
294
        target=params.to,
295
        gas=params.gas,
296
        value=params.value,
297
        data=call_data,
298
        code=code,
299
        current_target=params.to,
300
        depth=evm.message.depth + Uint(1),
301
        code_address=params.code_address,
302
        should_transfer_value=params.should_transfer_value,
303
        is_static=params.is_staticcall or evm.message.is_static,
304
        accessed_addresses=evm.accessed_addresses.copy(),
305
        accessed_storage_keys=evm.accessed_storage_keys.copy(),
306
        parent_evm=evm,
307
    )
308
    child_evm = process_message(child_message)
309
310
    if child_evm.error:
311
        incorporate_child_on_error(evm, child_evm)
312
        evm.return_data = child_evm.output
313
        push(evm.stack, U256(0))
314
    else:
315
        incorporate_child_on_success(evm, child_evm)
316
        evm.return_data = child_evm.output
317
        push(evm.stack, U256(1))
318
319
    actual_output_size = min(
320
        params.memory_output_size, U256(len(child_evm.output))
321
    )
322
    memory_write(
323
        evm.memory,
324
        params.memory_output_start_position,
325
        child_evm.output[:actual_output_size],
326
    )

call

Message-call into an account.

Parameters

evm : The current EVM frame.

def call(evm: Evm) -> None:
330
    <snip>
339
    # STACK
340
    gas = Uint(pop(evm.stack))
341
    to = to_address_masked(pop(evm.stack))
342
    value = pop(evm.stack)
343
    memory_input_start_position = pop(evm.stack)
344
    memory_input_size = pop(evm.stack)
345
    memory_output_start_position = pop(evm.stack)
346
    memory_output_size = pop(evm.stack)
347
348
    # GAS
349
    extend_memory = calculate_gas_extend_memory(
350
        evm.memory,
351
        [
352
            (memory_input_start_position, memory_input_size),
353
            (memory_output_start_position, memory_output_size),
354
        ],
355
    )
356
357
    if to in evm.accessed_addresses:
358
        access_gas_cost = GasCosts.WARM_ACCESS
359
    else:
360
        evm.accessed_addresses.add(to)
361
        access_gas_cost = GasCosts.COLD_ACCOUNT_ACCESS
362
363
    code_address = to
364
365
    create_gas_cost = GasCosts.NEW_ACCOUNT
366
    if value == 0 or is_account_alive(evm.message.tx_env.state, to):
367
        create_gas_cost = Uint(0)
368
    transfer_gas_cost = Uint(0) if value == 0 else GasCosts.CALL_VALUE
369
    message_call_gas = calculate_message_call_gas(
370
        value,
371
        gas,
372
        Uint(evm.gas_left),
373
        memory_cost=extend_memory.cost,
374
        extra_gas=access_gas_cost + create_gas_cost + transfer_gas_cost,
375
    )
376
    charge_gas(evm, message_call_gas.cost + extend_memory.cost)
377
    if evm.message.is_static and value != U256(0):
378
        raise WriteInStaticContext
379
    evm.memory += b"\x00" * extend_memory.expand_by
380
    sender_balance = get_account(
381
        evm.message.tx_env.state, evm.message.current_target
382
    ).balance
383
    if sender_balance < value:
384
        push(evm.stack, U256(0))
385
        evm.return_data = b""
386
        evm.gas_left += message_call_gas.sub_call
387
    else:
388
        generic_call(
389
            evm,
390
            GenericCall(
391
                gas=message_call_gas.sub_call,
392
                value=value,
393
                caller=evm.message.current_target,
394
                to=to,
395
                code_address=code_address,
396
                should_transfer_value=True,
397
                is_staticcall=False,
398
                memory_input_start_position=memory_input_start_position,
399
                memory_input_size=memory_input_size,
400
                memory_output_start_position=memory_output_start_position,
401
                memory_output_size=memory_output_size,
402
            ),
403
        )
404
405
    # PROGRAM COUNTER
406
    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:
410
    <snip>
419
    # STACK
420
    gas = Uint(pop(evm.stack))
421
    code_address = to_address_masked(pop(evm.stack))
422
    value = pop(evm.stack)
423
    memory_input_start_position = pop(evm.stack)
424
    memory_input_size = pop(evm.stack)
425
    memory_output_start_position = pop(evm.stack)
426
    memory_output_size = pop(evm.stack)
427
428
    # GAS
429
    to = evm.message.current_target
430
431
    extend_memory = calculate_gas_extend_memory(
432
        evm.memory,
433
        [
434
            (memory_input_start_position, memory_input_size),
435
            (memory_output_start_position, memory_output_size),
436
        ],
437
    )
438
439
    if code_address in evm.accessed_addresses:
440
        access_gas_cost = GasCosts.WARM_ACCESS
441
    else:
442
        evm.accessed_addresses.add(code_address)
443
        access_gas_cost = GasCosts.COLD_ACCOUNT_ACCESS
444
445
    transfer_gas_cost = Uint(0) if value == 0 else GasCosts.CALL_VALUE
446
    message_call_gas = calculate_message_call_gas(
447
        value,
448
        gas,
449
        Uint(evm.gas_left),
450
        extend_memory.cost,
451
        access_gas_cost + transfer_gas_cost,
452
    )
453
    charge_gas(evm, message_call_gas.cost + extend_memory.cost)
454
455
    # OPERATION
456
    evm.memory += b"\x00" * extend_memory.expand_by
457
    sender_balance = get_account(
458
        evm.message.tx_env.state, evm.message.current_target
459
    ).balance
460
    if sender_balance < value:
461
        push(evm.stack, U256(0))
462
        evm.return_data = b""
463
        evm.gas_left += message_call_gas.sub_call
464
    else:
465
        generic_call(
466
            evm,
467
            GenericCall(
468
                gas=message_call_gas.sub_call,
469
                value=value,
470
                caller=evm.message.current_target,
471
                to=to,
472
                code_address=code_address,
473
                should_transfer_value=True,
474
                is_staticcall=False,
475
                memory_input_start_position=memory_input_start_position,
476
                memory_input_size=memory_input_size,
477
                memory_output_start_position=memory_output_start_position,
478
                memory_output_size=memory_output_size,
479
            ),
480
        )
481
482
    # PROGRAM COUNTER
483
    evm.pc += Uint(1)

selfdestruct

Halt execution and register account for later deletion.

Parameters

evm : The current EVM frame.

def selfdestruct(evm: Evm) -> None:
487
    <snip>
496
    # STACK
497
    beneficiary = to_address_masked(pop(evm.stack))
498
499
    # GAS
500
    gas_cost = GasCosts.OPCODE_SELFDESTRUCT_BASE
501
    if beneficiary not in evm.accessed_addresses:
502
        evm.accessed_addresses.add(beneficiary)
503
        gas_cost += GasCosts.COLD_ACCOUNT_ACCESS
504
505
    if (
506
        not is_account_alive(evm.message.tx_env.state, beneficiary)
507
        and get_account(
508
            evm.message.tx_env.state, evm.message.current_target
509
        ).balance
510
        != 0
511
    ):
512
        gas_cost += GasCosts.OPCODE_SELFDESTRUCT_NEW_ACCOUNT
513
514
    charge_gas(evm, gas_cost)
515
    if evm.message.is_static:
516
        raise WriteInStaticContext
517
518
    originator = evm.message.current_target
519
    beneficiary_balance = get_account(
520
        evm.message.tx_env.state, beneficiary
521
    ).balance
522
    originator_balance = get_account(
523
        evm.message.tx_env.state, originator
524
    ).balance
525
526
    # First Transfer to beneficiary
527
    set_account_balance(
528
        evm.message.tx_env.state,
529
        beneficiary,
530
        beneficiary_balance + originator_balance,
531
    )
532
    # Next, Zero the balance of the address being deleted (must come after
533
    # sending to beneficiary in case the contract named itself as the
534
    # beneficiary).
535
    set_account_balance(evm.message.tx_env.state, originator, U256(0))
536
537
    # register account for deletion
538
    evm.accounts_to_delete.add(originator)
539
540
    # HALT the execution
541
    evm.running = False
542
543
    # PROGRAM COUNTER
544
    pass

delegatecall

Message-call into an account.

Parameters

evm : The current EVM frame.

def delegatecall(evm: Evm) -> None:
548
    <snip>
557
    # STACK
558
    gas = Uint(pop(evm.stack))
559
    code_address = to_address_masked(pop(evm.stack))
560
    memory_input_start_position = pop(evm.stack)
561
    memory_input_size = pop(evm.stack)
562
    memory_output_start_position = pop(evm.stack)
563
    memory_output_size = pop(evm.stack)
564
565
    # GAS
566
    extend_memory = calculate_gas_extend_memory(
567
        evm.memory,
568
        [
569
            (memory_input_start_position, memory_input_size),
570
            (memory_output_start_position, memory_output_size),
571
        ],
572
    )
573
574
    if code_address in evm.accessed_addresses:
575
        access_gas_cost = GasCosts.WARM_ACCESS
576
    else:
577
        evm.accessed_addresses.add(code_address)
578
        access_gas_cost = GasCosts.COLD_ACCOUNT_ACCESS
579
580
    message_call_gas = calculate_message_call_gas(
581
        U256(0), gas, Uint(evm.gas_left), extend_memory.cost, access_gas_cost
582
    )
583
    charge_gas(evm, message_call_gas.cost + extend_memory.cost)
584
585
    # OPERATION
586
    evm.memory += b"\x00" * extend_memory.expand_by
587
    generic_call(
588
        evm,
589
        GenericCall(
590
            gas=message_call_gas.sub_call,
591
            value=evm.message.value,
592
            caller=evm.message.caller,
593
            to=evm.message.current_target,
594
            code_address=code_address,
595
            should_transfer_value=False,
596
            is_staticcall=False,
597
            memory_input_start_position=memory_input_start_position,
598
            memory_input_size=memory_input_size,
599
            memory_output_start_position=memory_output_start_position,
600
            memory_output_size=memory_output_size,
601
        ),
602
    )
603
604
    # PROGRAM COUNTER
605
    evm.pc += Uint(1)

staticcall

Message-call into an account.

Parameters

evm : The current EVM frame.

def staticcall(evm: Evm) -> None:
609
    <snip>
618
    # STACK
619
    gas = Uint(pop(evm.stack))
620
    to = to_address_masked(pop(evm.stack))
621
    memory_input_start_position = pop(evm.stack)
622
    memory_input_size = pop(evm.stack)
623
    memory_output_start_position = pop(evm.stack)
624
    memory_output_size = pop(evm.stack)
625
626
    # GAS
627
    extend_memory = calculate_gas_extend_memory(
628
        evm.memory,
629
        [
630
            (memory_input_start_position, memory_input_size),
631
            (memory_output_start_position, memory_output_size),
632
        ],
633
    )
634
635
    if to in evm.accessed_addresses:
636
        access_gas_cost = GasCosts.WARM_ACCESS
637
    else:
638
        evm.accessed_addresses.add(to)
639
        access_gas_cost = GasCosts.COLD_ACCOUNT_ACCESS
640
641
    code_address = to
642
643
    message_call_gas = calculate_message_call_gas(
644
        U256(0),
645
        gas,
646
        Uint(evm.gas_left),
647
        extend_memory.cost,
648
        access_gas_cost,
649
    )
650
    charge_gas(evm, message_call_gas.cost + extend_memory.cost)
651
652
    # OPERATION
653
    evm.memory += b"\x00" * extend_memory.expand_by
654
    generic_call(
655
        evm,
656
        GenericCall(
657
            gas=message_call_gas.sub_call,
658
            value=U256(0),
659
            caller=evm.message.current_target,
660
            to=to,
661
            code_address=code_address,
662
            should_transfer_value=True,
663
            is_staticcall=True,
664
            memory_input_start_position=memory_input_start_position,
665
            memory_input_size=memory_input_size,
666
            memory_output_start_position=memory_output_start_position,
667
            memory_output_size=memory_output_size,
668
        ),
669
    )
670
671
    # PROGRAM COUNTER
672
    evm.pc += Uint(1)

revert

Stop execution and revert state changes, without consuming all provided gas and also has the ability to return a reason.

Parameters

evm : The current EVM frame.

def revert(evm: Evm) -> None:
676
    <snip>
686
    # STACK
687
    memory_start_index = pop(evm.stack)
688
    size = pop(evm.stack)
689
690
    # GAS
691
    extend_memory = calculate_gas_extend_memory(
692
        evm.memory, [(memory_start_index, size)]
693
    )
694
695
    charge_gas(evm, extend_memory.cost)
696
697
    # OPERATION
698
    evm.memory += b"\x00" * extend_memory.expand_by
699
    output = memory_read_bytes(evm.memory, memory_start_index, size)
700
    evm.output = Bytes(output)
701
    raise Revert
702
703
    # PROGRAM COUNTER
704
    # no-op