ethereum.london.vm.instructions.systemethereum.arrow_glacier.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:
68
    """
69
    Core logic used by the `CREATE*` family of opcodes.
70
    """
71
    # This import causes a circular import error
72
    # if it's not moved inside this method
73
    from ...vm.interpreter import STACK_DEPTH_LIMIT, process_create_message
74
75
    call_data = memory_read_bytes(
76
        evm.memory, memory_start_position, memory_size
77
    )
78
79
    create_message_gas = max_message_call_gas(Uint(evm.gas_left))
80
    evm.gas_left -= create_message_gas
81
    if evm.message.is_static:
82
        raise WriteInStaticContext
83
    evm.return_data = b""
84
85
    sender_address = evm.message.current_target
86
    sender = get_account(evm.message.block_env.state, sender_address)
87
88
    if (
89
        sender.balance < endowment
90
        or sender.nonce == Uint(2**64 - 1)
91
        or evm.message.depth + Uint(1) > STACK_DEPTH_LIMIT
92
    ):
93
        evm.gas_left += create_message_gas
94
        push(evm.stack, U256(0))
95
        return
96
97
    evm.accessed_addresses.add(contract_address)
98
99
    if account_has_code_or_nonce(
100
        evm.message.block_env.state, contract_address
101
    ) or account_has_storage(evm.message.block_env.state, contract_address):
102
        increment_nonce(
103
            evm.message.block_env.state, evm.message.current_target
104
        )
105
        push(evm.stack, U256(0))
106
        return
107
108
    increment_nonce(evm.message.block_env.state, evm.message.current_target)
109
110
    child_message = Message(
111
        block_env=evm.message.block_env,
112
        tx_env=evm.message.tx_env,
113
        caller=evm.message.current_target,
114
        target=Bytes0(),
115
        gas=create_message_gas,
116
        value=endowment,
117
        data=b"",
118
        code=call_data,
119
        current_target=contract_address,
120
        depth=evm.message.depth + Uint(1),
121
        code_address=None,
122
        should_transfer_value=True,
123
        is_static=False,
124
        accessed_addresses=evm.accessed_addresses.copy(),
125
        accessed_storage_keys=evm.accessed_storage_keys.copy(),
126
        parent_evm=evm,
127
    )
128
    child_evm = process_create_message(child_message)
129
130
    if child_evm.error:
131
        incorporate_child_on_error(evm, child_evm)
132
        evm.return_data = child_evm.output
133
        push(evm.stack, U256(0))
134
    else:
135
        incorporate_child_on_success(evm, child_evm)
136
        evm.return_data = b""
137
        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:
141
    """
142
    Creates a new account with associated code.
143
144
    Parameters
145
    ----------
146
    evm :
147
        The current EVM frame.
148
    """
149
    # STACK
150
    endowment = pop(evm.stack)
151
    memory_start_position = pop(evm.stack)
152
    memory_size = pop(evm.stack)
153
154
    # GAS
155
    extend_memory = calculate_gas_extend_memory(
156
        evm.memory, [(memory_start_position, memory_size)]
157
    )
158
159
    charge_gas(evm, GAS_CREATE + extend_memory.cost)
160
161
    # OPERATION
162
    evm.memory += b"\x00" * extend_memory.expand_by
163
    contract_address = compute_contract_address(
164
        evm.message.current_target,
165
        get_account(
166
            evm.message.block_env.state, evm.message.current_target
167
        ).nonce,
168
    )
169
170
    generic_create(
171
        evm, endowment, contract_address, memory_start_position, memory_size
172
    )
173
174
    # PROGRAM COUNTER
175
    evm.pc += Uint(1)

create2

Creates a new account with associated code.

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

Parameters

evm : The current EVM frame.

def create2(evm: Evm) -> None:
179
    """
180
    Creates a new account with associated code.
181
182
    It's similar to CREATE opcode except that the address of new account
183
    depends on the init_code instead of the nonce of sender.
184
185
    Parameters
186
    ----------
187
    evm :
188
        The current EVM frame.
189
    """
190
    # STACK
191
    endowment = pop(evm.stack)
192
    memory_start_position = pop(evm.stack)
193
    memory_size = pop(evm.stack)
194
    salt = pop(evm.stack).to_be_bytes32()
195
196
    # GAS
197
    extend_memory = calculate_gas_extend_memory(
198
        evm.memory, [(memory_start_position, memory_size)]
199
    )
200
    call_data_words = ceil32(Uint(memory_size)) // Uint(32)
201
    charge_gas(
202
        evm,
203
        GAS_CREATE + GAS_KECCAK256_WORD * call_data_words + extend_memory.cost,
204
    )
205
206
    # OPERATION
207
    evm.memory += b"\x00" * extend_memory.expand_by
208
    contract_address = compute_create2_contract_address(
209
        evm.message.current_target,
210
        salt,
211
        memory_read_bytes(evm.memory, memory_start_position, memory_size),
212
    )
213
214
    generic_create(
215
        evm, endowment, contract_address, memory_start_position, memory_size
216
    )
217
218
    # PROGRAM COUNTER
219
    evm.pc += Uint(1)

return_

Halts execution returning output data.

Parameters

evm : The current EVM frame.

def return_(evm: Evm) -> None:
223
    """
224
    Halts execution returning output data.
225
226
    Parameters
227
    ----------
228
    evm :
229
        The current EVM frame.
230
    """
231
    # STACK
232
    memory_start_position = pop(evm.stack)
233
    memory_size = pop(evm.stack)
234
235
    # GAS
236
    extend_memory = calculate_gas_extend_memory(
237
        evm.memory, [(memory_start_position, memory_size)]
238
    )
239
240
    charge_gas(evm, GAS_ZERO + extend_memory.cost)
241
242
    # OPERATION
243
    evm.memory += b"\x00" * extend_memory.expand_by
244
    evm.output = memory_read_bytes(
245
        evm.memory, memory_start_position, memory_size
246
    )
247
248
    evm.running = False
249
250
    # PROGRAM COUNTER
251
    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, ​​is_staticcall: bool, ​​memory_input_start_position: U256, ​​memory_input_size: U256, ​​memory_output_start_position: U256, ​​memory_output_size: U256) -> None:
268
    """
269
    Perform the core logic of the `CALL*` family of opcodes.
270
    """
271
    from ...vm.interpreter import STACK_DEPTH_LIMIT, process_message
272
273
    evm.return_data = b""
274
275
    if evm.message.depth + Uint(1) > STACK_DEPTH_LIMIT:
276
        evm.gas_left += gas
277
        push(evm.stack, U256(0))
278
        return
279
280
    call_data = memory_read_bytes(
281
        evm.memory, memory_input_start_position, memory_input_size
282
    )
283
    code = get_account(evm.message.block_env.state, code_address).code
284
    child_message = Message(
285
        block_env=evm.message.block_env,
286
        tx_env=evm.message.tx_env,
287
        caller=caller,
288
        target=to,
289
        gas=gas,
290
        value=value,
291
        data=call_data,
292
        code=code,
293
        current_target=to,
294
        depth=evm.message.depth + Uint(1),
295
        code_address=code_address,
296
        should_transfer_value=should_transfer_value,
297
        is_static=True if is_staticcall else evm.message.is_static,
298
        accessed_addresses=evm.accessed_addresses.copy(),
299
        accessed_storage_keys=evm.accessed_storage_keys.copy(),
300
        parent_evm=evm,
301
    )
302
    child_evm = process_message(child_message)
303
304
    if child_evm.error:
305
        incorporate_child_on_error(evm, child_evm)
306
        evm.return_data = child_evm.output
307
        push(evm.stack, U256(0))
308
    else:
309
        incorporate_child_on_success(evm, child_evm)
310
        evm.return_data = child_evm.output
311
        push(evm.stack, U256(1))
312
313
    actual_output_size = min(memory_output_size, U256(len(child_evm.output)))
314
    memory_write(
315
        evm.memory,
316
        memory_output_start_position,
317
        child_evm.output[:actual_output_size],
318
    )

call

Message-call into an account.

Parameters

evm : The current EVM frame.

def call(evm: Evm) -> None:
322
    """
323
    Message-call into an account.
324
325
    Parameters
326
    ----------
327
    evm :
328
        The current EVM frame.
329
    """
330
    # STACK
331
    gas = Uint(pop(evm.stack))
332
    to = to_address_masked(pop(evm.stack))
333
    value = pop(evm.stack)
334
    memory_input_start_position = pop(evm.stack)
335
    memory_input_size = pop(evm.stack)
336
    memory_output_start_position = pop(evm.stack)
337
    memory_output_size = pop(evm.stack)
338
339
    # GAS
340
    extend_memory = calculate_gas_extend_memory(
341
        evm.memory,
342
        [
343
            (memory_input_start_position, memory_input_size),
344
            (memory_output_start_position, memory_output_size),
345
        ],
346
    )
347
348
    if to in evm.accessed_addresses:
349
        access_gas_cost = GAS_WARM_ACCESS
350
    else:
351
        evm.accessed_addresses.add(to)
352
        access_gas_cost = GAS_COLD_ACCOUNT_ACCESS
353
354
    code_address = to
355
356
    create_gas_cost = GAS_NEW_ACCOUNT
357
    if value == 0 or is_account_alive(evm.message.block_env.state, to):
358
        create_gas_cost = Uint(0)
359
    transfer_gas_cost = Uint(0) if value == 0 else GAS_CALL_VALUE
360
    message_call_gas = calculate_message_call_gas(
361
        value,
362
        gas,
363
        Uint(evm.gas_left),
364
        extend_memory.cost,
365
        access_gas_cost + create_gas_cost + transfer_gas_cost,
366
    )
367
    charge_gas(evm, message_call_gas.cost + extend_memory.cost)
368
    if evm.message.is_static and value != U256(0):
369
        raise WriteInStaticContext
370
    evm.memory += b"\x00" * extend_memory.expand_by
371
    sender_balance = get_account(
372
        evm.message.block_env.state, evm.message.current_target
373
    ).balance
374
    if sender_balance < value:
375
        push(evm.stack, U256(0))
376
        evm.return_data = b""
377
        evm.gas_left += message_call_gas.sub_call
378
    else:
379
        generic_call(
380
            evm,
381
            message_call_gas.sub_call,
382
            value,
383
            evm.message.current_target,
384
            to,
385
            code_address,
386
            True,
387
            False,
388
            memory_input_start_position,
389
            memory_input_size,
390
            memory_output_start_position,
391
            memory_output_size,
392
        )
393
394
    # PROGRAM COUNTER
395
    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:
399
    """
400
    Message-call into this account with alternative account’s code.
401
402
    Parameters
403
    ----------
404
    evm :
405
        The current EVM frame.
406
    """
407
    # STACK
408
    gas = Uint(pop(evm.stack))
409
    code_address = to_address_masked(pop(evm.stack))
410
    value = pop(evm.stack)
411
    memory_input_start_position = pop(evm.stack)
412
    memory_input_size = pop(evm.stack)
413
    memory_output_start_position = pop(evm.stack)
414
    memory_output_size = pop(evm.stack)
415
416
    # GAS
417
    to = evm.message.current_target
418
419
    extend_memory = calculate_gas_extend_memory(
420
        evm.memory,
421
        [
422
            (memory_input_start_position, memory_input_size),
423
            (memory_output_start_position, memory_output_size),
424
        ],
425
    )
426
427
    if code_address in evm.accessed_addresses:
428
        access_gas_cost = GAS_WARM_ACCESS
429
    else:
430
        evm.accessed_addresses.add(code_address)
431
        access_gas_cost = GAS_COLD_ACCOUNT_ACCESS
432
433
    transfer_gas_cost = Uint(0) if value == 0 else GAS_CALL_VALUE
434
    message_call_gas = calculate_message_call_gas(
435
        value,
436
        gas,
437
        Uint(evm.gas_left),
438
        extend_memory.cost,
439
        access_gas_cost + transfer_gas_cost,
440
    )
441
    charge_gas(evm, message_call_gas.cost + extend_memory.cost)
442
443
    # OPERATION
444
    evm.memory += b"\x00" * extend_memory.expand_by
445
    sender_balance = get_account(
446
        evm.message.block_env.state, evm.message.current_target
447
    ).balance
448
    if sender_balance < value:
449
        push(evm.stack, U256(0))
450
        evm.return_data = b""
451
        evm.gas_left += message_call_gas.sub_call
452
    else:
453
        generic_call(
454
            evm,
455
            message_call_gas.sub_call,
456
            value,
457
            evm.message.current_target,
458
            to,
459
            code_address,
460
            True,
461
            False,
462
            memory_input_start_position,
463
            memory_input_size,
464
            memory_output_start_position,
465
            memory_output_size,
466
        )
467
468
    # PROGRAM COUNTER
469
    evm.pc += Uint(1)

selfdestruct

Halt execution and register account for later deletion.

Parameters

evm : The current EVM frame.

def selfdestruct(evm: Evm) -> None:
473
    """
474
    Halt execution and register account for later deletion.
475
476
    Parameters
477
    ----------
478
    evm :
479
        The current EVM frame.
480
    """
481
    # STACK
482
    beneficiary = to_address_masked(pop(evm.stack))
483
484
    # GAS
485
    gas_cost = GAS_SELF_DESTRUCT
486
    if beneficiary not in evm.accessed_addresses:
487
        evm.accessed_addresses.add(beneficiary)
488
        gas_cost += GAS_COLD_ACCOUNT_ACCESS
489
490
    if (
491
        not is_account_alive(evm.message.block_env.state, beneficiary)
492
        and get_account(
493
            evm.message.block_env.state, evm.message.current_target
494
        ).balance
495
        != 0
496
    ):
497
        gas_cost += GAS_SELF_DESTRUCT_NEW_ACCOUNT
498
499
    charge_gas(evm, gas_cost)
500
    if evm.message.is_static:
501
        raise WriteInStaticContext
502
503
    originator = evm.message.current_target
504
    beneficiary_balance = get_account(
505
        evm.message.block_env.state, beneficiary
506
    ).balance
507
    originator_balance = get_account(
508
        evm.message.block_env.state, originator
509
    ).balance
510
511
    # First Transfer to beneficiary
512
    set_account_balance(
513
        evm.message.block_env.state,
514
        beneficiary,
515
        beneficiary_balance + originator_balance,
516
    )
517
    # Next, Zero the balance of the address being deleted (must come after
518
    # sending to beneficiary in case the contract named itself as the
519
    # beneficiary).
520
    set_account_balance(evm.message.block_env.state, originator, U256(0))
521
522
    # register account for deletion
523
    evm.accounts_to_delete.add(originator)
524
525
    # mark beneficiary as touched
526
    if account_exists_and_is_empty(evm.message.block_env.state, beneficiary):
527
        evm.touched_accounts.add(beneficiary)
528
529
    # HALT the execution
530
    evm.running = False
531
532
    # PROGRAM COUNTER
533
    pass

delegatecall

Message-call into an account.

Parameters

evm : The current EVM frame.

def delegatecall(evm: Evm) -> None:
537
    """
538
    Message-call into an account.
539
540
    Parameters
541
    ----------
542
    evm :
543
        The current EVM frame.
544
    """
545
    # STACK
546
    gas = Uint(pop(evm.stack))
547
    code_address = to_address_masked(pop(evm.stack))
548
    memory_input_start_position = pop(evm.stack)
549
    memory_input_size = pop(evm.stack)
550
    memory_output_start_position = pop(evm.stack)
551
    memory_output_size = pop(evm.stack)
552
553
    # GAS
554
    extend_memory = calculate_gas_extend_memory(
555
        evm.memory,
556
        [
557
            (memory_input_start_position, memory_input_size),
558
            (memory_output_start_position, memory_output_size),
559
        ],
560
    )
561
562
    if code_address in evm.accessed_addresses:
563
        access_gas_cost = GAS_WARM_ACCESS
564
    else:
565
        evm.accessed_addresses.add(code_address)
566
        access_gas_cost = GAS_COLD_ACCOUNT_ACCESS
567
568
    message_call_gas = calculate_message_call_gas(
569
        U256(0), gas, Uint(evm.gas_left), extend_memory.cost, access_gas_cost
570
    )
571
    charge_gas(evm, message_call_gas.cost + extend_memory.cost)
572
573
    # OPERATION
574
    evm.memory += b"\x00" * extend_memory.expand_by
575
    generic_call(
576
        evm,
577
        message_call_gas.sub_call,
578
        evm.message.value,
579
        evm.message.caller,
580
        evm.message.current_target,
581
        code_address,
582
        False,
583
        False,
584
        memory_input_start_position,
585
        memory_input_size,
586
        memory_output_start_position,
587
        memory_output_size,
588
    )
589
590
    # PROGRAM COUNTER
591
    evm.pc += Uint(1)

staticcall

Message-call into an account.

Parameters

evm : The current EVM frame.

def staticcall(evm: Evm) -> None:
595
    """
596
    Message-call into an account.
597
598
    Parameters
599
    ----------
600
    evm :
601
        The current EVM frame.
602
    """
603
    # STACK
604
    gas = Uint(pop(evm.stack))
605
    to = to_address_masked(pop(evm.stack))
606
    memory_input_start_position = pop(evm.stack)
607
    memory_input_size = pop(evm.stack)
608
    memory_output_start_position = pop(evm.stack)
609
    memory_output_size = pop(evm.stack)
610
611
    # GAS
612
    extend_memory = calculate_gas_extend_memory(
613
        evm.memory,
614
        [
615
            (memory_input_start_position, memory_input_size),
616
            (memory_output_start_position, memory_output_size),
617
        ],
618
    )
619
620
    if to in evm.accessed_addresses:
621
        access_gas_cost = GAS_WARM_ACCESS
622
    else:
623
        evm.accessed_addresses.add(to)
624
        access_gas_cost = GAS_COLD_ACCOUNT_ACCESS
625
626
    code_address = to
627
628
    message_call_gas = calculate_message_call_gas(
629
        U256(0),
630
        gas,
631
        Uint(evm.gas_left),
632
        extend_memory.cost,
633
        access_gas_cost,
634
    )
635
    charge_gas(evm, message_call_gas.cost + extend_memory.cost)
636
637
    # OPERATION
638
    evm.memory += b"\x00" * extend_memory.expand_by
639
    generic_call(
640
        evm,
641
        message_call_gas.sub_call,
642
        U256(0),
643
        evm.message.current_target,
644
        to,
645
        code_address,
646
        True,
647
        True,
648
        memory_input_start_position,
649
        memory_input_size,
650
        memory_output_start_position,
651
        memory_output_size,
652
    )
653
654
    # PROGRAM COUNTER
655
    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:
659
    """
660
    Stop execution and revert state changes, without consuming all provided gas
661
    and also has the ability to return a reason
662
    Parameters
663
    ----------
664
    evm :
665
        The current EVM frame.
666
    """
667
    # STACK
668
    memory_start_index = pop(evm.stack)
669
    size = pop(evm.stack)
670
671
    # GAS
672
    extend_memory = calculate_gas_extend_memory(
673
        evm.memory, [(memory_start_index, size)]
674
    )
675
676
    charge_gas(evm, extend_memory.cost)
677
678
    # OPERATION
679
    evm.memory += b"\x00" * extend_memory.expand_by
680
    output = memory_read_bytes(evm.memory, memory_start_index, size)
681
    evm.output = Bytes(output)
682
    raise Revert