ethereum.gray_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:
67
    """
68
    Core logic used by the `CREATE*` family of opcodes.
69
    """
70
    # This import causes a circular import error
71
    # if it's not moved inside this method
72
    from ...vm.interpreter import STACK_DEPTH_LIMIT, process_create_message
73
74
    evm.accessed_addresses.add(contract_address)
75
76
    create_message_gas = max_message_call_gas(Uint(evm.gas_left))
77
    evm.gas_left -= create_message_gas
78
    if evm.message.is_static:
79
        raise WriteInStaticContext
80
    evm.return_data = b""
81
82
    sender_address = evm.message.current_target
83
    sender = get_account(evm.env.state, sender_address)
84
85
    if (
86
        sender.balance < endowment
87
        or sender.nonce == Uint(2**64 - 1)
88
        or evm.message.depth + Uint(1) > STACK_DEPTH_LIMIT
89
    ):
90
        evm.gas_left += create_message_gas
91
        push(evm.stack, U256(0))
92
        return
93
94
    if account_has_code_or_nonce(
95
        evm.env.state, contract_address
96
    ) or account_has_storage(evm.env.state, contract_address):
97
        increment_nonce(evm.env.state, evm.message.current_target)
98
        push(evm.stack, U256(0))
99
        return
100
101
    call_data = memory_read_bytes(
102
        evm.memory, memory_start_position, memory_size
103
    )
104
105
    increment_nonce(evm.env.state, evm.message.current_target)
106
107
    child_message = Message(
108
        caller=evm.message.current_target,
109
        target=Bytes0(),
110
        gas=create_message_gas,
111
        value=endowment,
112
        data=b"",
113
        code=call_data,
114
        current_target=contract_address,
115
        depth=evm.message.depth + Uint(1),
116
        code_address=None,
117
        should_transfer_value=True,
118
        is_static=False,
119
        accessed_addresses=evm.accessed_addresses.copy(),
120
        accessed_storage_keys=evm.accessed_storage_keys.copy(),
121
        parent_evm=evm,
122
    )
123
    child_evm = process_create_message(child_message, evm.env)
124
125
    if child_evm.error:
126
        incorporate_child_on_error(evm, child_evm)
127
        evm.return_data = child_evm.output
128
        push(evm.stack, U256(0))
129
    else:
130
        incorporate_child_on_success(evm, child_evm)
131
        evm.return_data = b""
132
        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:
136
    """
137
    Creates a new account with associated code.
138
139
    Parameters
140
    ----------
141
    evm :
142
        The current EVM frame.
143
    """
144
    # STACK
145
    endowment = pop(evm.stack)
146
    memory_start_position = pop(evm.stack)
147
    memory_size = pop(evm.stack)
148
149
    # GAS
150
    extend_memory = calculate_gas_extend_memory(
151
        evm.memory, [(memory_start_position, memory_size)]
152
    )
153
154
    charge_gas(evm, GAS_CREATE + extend_memory.cost)
155
156
    # OPERATION
157
    evm.memory += b"\x00" * extend_memory.expand_by
158
    contract_address = compute_contract_address(
159
        evm.message.current_target,
160
        get_account(evm.env.state, evm.message.current_target).nonce,
161
    )
162
163
    generic_create(
164
        evm, endowment, contract_address, memory_start_position, memory_size
165
    )
166
167
    # PROGRAM COUNTER
168
    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:
172
    """
173
    Creates a new account with associated code.
174
175
    It's similar to CREATE opcode except that the address of new account
176
    depends on the init_code instead of the nonce of sender.
177
178
    Parameters
179
    ----------
180
    evm :
181
        The current EVM frame.
182
    """
183
    # STACK
184
    endowment = pop(evm.stack)
185
    memory_start_position = pop(evm.stack)
186
    memory_size = pop(evm.stack)
187
    salt = pop(evm.stack).to_be_bytes32()
188
189
    # GAS
190
    extend_memory = calculate_gas_extend_memory(
191
        evm.memory, [(memory_start_position, memory_size)]
192
    )
193
    call_data_words = ceil32(Uint(memory_size)) // Uint(32)
194
    charge_gas(
195
        evm,
196
        GAS_CREATE + GAS_KECCAK256_WORD * call_data_words + 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
    """
217
    Halts execution returning output data.
218
219
    Parameters
220
    ----------
221
    evm :
222
        The current EVM frame.
223
    """
224
    # STACK
225
    memory_start_position = pop(evm.stack)
226
    memory_size = pop(evm.stack)
227
228
    # GAS
229
    extend_memory = calculate_gas_extend_memory(
230
        evm.memory, [(memory_start_position, memory_size)]
231
    )
232
233
    charge_gas(evm, GAS_ZERO + extend_memory.cost)
234
235
    # OPERATION
236
    evm.memory += b"\x00" * extend_memory.expand_by
237
    evm.output = memory_read_bytes(
238
        evm.memory, memory_start_position, memory_size
239
    )
240
241
    evm.running = False
242
243
    # PROGRAM COUNTER
244
    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:
261
    """
262
    Perform the core logic of the `CALL*` family of opcodes.
263
    """
264
    from ...vm.interpreter import STACK_DEPTH_LIMIT, process_message
265
266
    evm.return_data = b""
267
268
    if evm.message.depth + Uint(1) > STACK_DEPTH_LIMIT:
269
        evm.gas_left += gas
270
        push(evm.stack, U256(0))
271
        return
272
273
    call_data = memory_read_bytes(
274
        evm.memory, memory_input_start_position, memory_input_size
275
    )
276
    code = get_account(evm.env.state, code_address).code
277
    child_message = Message(
278
        caller=caller,
279
        target=to,
280
        gas=gas,
281
        value=value,
282
        data=call_data,
283
        code=code,
284
        current_target=to,
285
        depth=evm.message.depth + Uint(1),
286
        code_address=code_address,
287
        should_transfer_value=should_transfer_value,
288
        is_static=True if is_staticcall else evm.message.is_static,
289
        accessed_addresses=evm.accessed_addresses.copy(),
290
        accessed_storage_keys=evm.accessed_storage_keys.copy(),
291
        parent_evm=evm,
292
    )
293
    child_evm = process_message(child_message, evm.env)
294
295
    if child_evm.error:
296
        incorporate_child_on_error(evm, child_evm)
297
        evm.return_data = child_evm.output
298
        push(evm.stack, U256(0))
299
    else:
300
        incorporate_child_on_success(evm, child_evm)
301
        evm.return_data = child_evm.output
302
        push(evm.stack, U256(1))
303
304
    actual_output_size = min(memory_output_size, U256(len(child_evm.output)))
305
    memory_write(
306
        evm.memory,
307
        memory_output_start_position,
308
        child_evm.output[:actual_output_size],
309
    )

call

Message-call into an account.

Parameters

evm : The current EVM frame.

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

selfdestruct

Halt execution and register account for later deletion.

Parameters

evm : The current EVM frame.

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

delegatecall

Message-call into an account.

Parameters

evm : The current EVM frame.

def delegatecall(evm: Evm) -> None:
519
    """
520
    Message-call into an account.
521
522
    Parameters
523
    ----------
524
    evm :
525
        The current EVM frame.
526
    """
527
    # STACK
528
    gas = Uint(pop(evm.stack))
529
    code_address = to_address(pop(evm.stack))
530
    memory_input_start_position = pop(evm.stack)
531
    memory_input_size = pop(evm.stack)
532
    memory_output_start_position = pop(evm.stack)
533
    memory_output_size = pop(evm.stack)
534
535
    # GAS
536
    extend_memory = calculate_gas_extend_memory(
537
        evm.memory,
538
        [
539
            (memory_input_start_position, memory_input_size),
540
            (memory_output_start_position, memory_output_size),
541
        ],
542
    )
543
544
    if code_address in evm.accessed_addresses:
545
        access_gas_cost = GAS_WARM_ACCESS
546
    else:
547
        evm.accessed_addresses.add(code_address)
548
        access_gas_cost = GAS_COLD_ACCOUNT_ACCESS
549
550
    message_call_gas = calculate_message_call_gas(
551
        U256(0), gas, Uint(evm.gas_left), extend_memory.cost, access_gas_cost
552
    )
553
    charge_gas(evm, message_call_gas.cost + extend_memory.cost)
554
555
    # OPERATION
556
    evm.memory += b"\x00" * extend_memory.expand_by
557
    generic_call(
558
        evm,
559
        message_call_gas.stipend,
560
        evm.message.value,
561
        evm.message.caller,
562
        evm.message.current_target,
563
        code_address,
564
        False,
565
        False,
566
        memory_input_start_position,
567
        memory_input_size,
568
        memory_output_start_position,
569
        memory_output_size,
570
    )
571
572
    # PROGRAM COUNTER
573
    evm.pc += Uint(1)

staticcall

Message-call into an account.

Parameters

evm : The current EVM frame.

def staticcall(evm: Evm) -> None:
577
    """
578
    Message-call into an account.
579
580
    Parameters
581
    ----------
582
    evm :
583
        The current EVM frame.
584
    """
585
    # STACK
586
    gas = Uint(pop(evm.stack))
587
    to = to_address(pop(evm.stack))
588
    memory_input_start_position = pop(evm.stack)
589
    memory_input_size = pop(evm.stack)
590
    memory_output_start_position = pop(evm.stack)
591
    memory_output_size = pop(evm.stack)
592
593
    # GAS
594
    extend_memory = calculate_gas_extend_memory(
595
        evm.memory,
596
        [
597
            (memory_input_start_position, memory_input_size),
598
            (memory_output_start_position, memory_output_size),
599
        ],
600
    )
601
602
    if to in evm.accessed_addresses:
603
        access_gas_cost = GAS_WARM_ACCESS
604
    else:
605
        evm.accessed_addresses.add(to)
606
        access_gas_cost = GAS_COLD_ACCOUNT_ACCESS
607
608
    message_call_gas = calculate_message_call_gas(
609
        U256(0),
610
        gas,
611
        Uint(evm.gas_left),
612
        extend_memory.cost,
613
        access_gas_cost,
614
    )
615
    charge_gas(evm, message_call_gas.cost + extend_memory.cost)
616
617
    # OPERATION
618
    evm.memory += b"\x00" * extend_memory.expand_by
619
    generic_call(
620
        evm,
621
        message_call_gas.stipend,
622
        U256(0),
623
        evm.message.current_target,
624
        to,
625
        to,
626
        True,
627
        True,
628
        memory_input_start_position,
629
        memory_input_size,
630
        memory_output_start_position,
631
        memory_output_size,
632
    )
633
634
    # PROGRAM COUNTER
635
    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:
639
    """
640
    Stop execution and revert state changes, without consuming all provided gas
641
    and also has the ability to return a reason
642
    Parameters
643
    ----------
644
    evm :
645
        The current EVM frame.
646
    """
647
    # STACK
648
    memory_start_index = pop(evm.stack)
649
    size = pop(evm.stack)
650
651
    # GAS
652
    extend_memory = calculate_gas_extend_memory(
653
        evm.memory, [(memory_start_index, size)]
654
    )
655
656
    charge_gas(evm, extend_memory.cost)
657
658
    # OPERATION
659
    evm.memory += b"\x00" * extend_memory.expand_by
660
    output = memory_read_bytes(evm.memory, memory_start_index, size)
661
    evm.output = bytes(output)
662
    raise Revert
663
664
    # PROGRAM COUNTER
665
    pass