ethereum.shanghai.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, init_code_gas: Uint) -> None:
66 | """ |
---|---|
67 | Core logic used by the `CREATE*` family of opcodes. |
68 | """ |
69 | # This import causes a circular import error |
70 | # if it's not moved inside this method |
71 | from ...vm.interpreter import ( |
72 | MAX_CODE_SIZE, |
73 | STACK_DEPTH_LIMIT, |
74 | process_create_message, |
75 | ) |
76 | |
77 | call_data = memory_read_bytes( |
78 | evm.memory, memory_start_position, memory_size |
79 | ) |
80 | if len(call_data) > 2 * MAX_CODE_SIZE: |
81 | raise OutOfGasError |
82 | |
83 | evm.accessed_addresses.add(contract_address) |
84 | |
85 | create_message_gas = max_message_call_gas(Uint(evm.gas_left)) |
86 | evm.gas_left -= create_message_gas |
87 | if evm.message.is_static: |
88 | raise WriteInStaticContext |
89 | evm.return_data = b"" |
90 | |
91 | sender_address = evm.message.current_target |
92 | sender = get_account(evm.env.state, sender_address) |
93 | |
94 | if ( |
95 | sender.balance < endowment |
96 | or sender.nonce == Uint(2**64 - 1) |
97 | or evm.message.depth + 1 > STACK_DEPTH_LIMIT |
98 | ): |
99 | evm.gas_left += create_message_gas |
100 | push(evm.stack, U256(0)) |
101 | return |
102 | |
103 | if account_has_code_or_nonce(evm.env.state, contract_address): |
104 | increment_nonce(evm.env.state, evm.message.current_target) |
105 | push(evm.stack, U256(0)) |
106 | return |
107 | |
108 | increment_nonce(evm.env.state, evm.message.current_target) |
109 | |
110 | child_message = Message( |
111 | caller=evm.message.current_target, |
112 | target=Bytes0(), |
113 | gas=create_message_gas, |
114 | value=endowment, |
115 | data=b"", |
116 | code=call_data, |
117 | current_target=contract_address, |
118 | depth=evm.message.depth + 1, |
119 | code_address=None, |
120 | should_transfer_value=True, |
121 | is_static=False, |
122 | accessed_addresses=evm.accessed_addresses.copy(), |
123 | accessed_storage_keys=evm.accessed_storage_keys.copy(), |
124 | parent_evm=evm, |
125 | ) |
126 | child_evm = process_create_message(child_message, evm.env) |
127 | |
128 | if child_evm.error: |
129 | incorporate_child_on_error(evm, child_evm) |
130 | evm.return_data = child_evm.output |
131 | push(evm.stack, U256(0)) |
132 | else: |
133 | incorporate_child_on_success(evm, child_evm) |
134 | evm.return_data = b"" |
135 | 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:
139 | """ |
---|---|
140 | Creates a new account with associated code. |
141 |
|
142 | Parameters |
143 | ---------- |
144 | evm : |
145 | The current EVM frame. |
146 | """ |
147 | # STACK |
148 | endowment = pop(evm.stack) |
149 | memory_start_position = pop(evm.stack) |
150 | memory_size = pop(evm.stack) |
151 | |
152 | # GAS |
153 | extend_memory = calculate_gas_extend_memory( |
154 | evm.memory, [(memory_start_position, memory_size)] |
155 | ) |
156 | init_code_gas = init_code_cost(Uint(memory_size)) |
157 | |
158 | charge_gas(evm, GAS_CREATE + extend_memory.cost + init_code_gas) |
159 | |
160 | # OPERATION |
161 | evm.memory += b"\x00" * extend_memory.expand_by |
162 | contract_address = compute_contract_address( |
163 | evm.message.current_target, |
164 | get_account(evm.env.state, evm.message.current_target).nonce, |
165 | ) |
166 | |
167 | generic_create( |
168 | evm, |
169 | endowment, |
170 | contract_address, |
171 | memory_start_position, |
172 | memory_size, |
173 | init_code_gas, |
174 | ) |
175 | |
176 | # PROGRAM COUNTER |
177 | evm.pc += 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:
181 | """ |
---|---|
182 | Creates a new account with associated code. |
183 |
|
184 | It's similar to CREATE opcode except that the address of new account |
185 | depends on the init_code instead of the nonce of sender. |
186 |
|
187 | Parameters |
188 | ---------- |
189 | evm : |
190 | The current EVM frame. |
191 | """ |
192 | # STACK |
193 | endowment = pop(evm.stack) |
194 | memory_start_position = pop(evm.stack) |
195 | memory_size = pop(evm.stack) |
196 | salt = pop(evm.stack).to_be_bytes32() |
197 | |
198 | # GAS |
199 | extend_memory = calculate_gas_extend_memory( |
200 | evm.memory, [(memory_start_position, memory_size)] |
201 | ) |
202 | call_data_words = ceil32(Uint(memory_size)) // 32 |
203 | init_code_gas = init_code_cost(Uint(memory_size)) |
204 | charge_gas( |
205 | evm, |
206 | GAS_CREATE |
207 | + GAS_KECCAK256_WORD * call_data_words |
208 | + extend_memory.cost |
209 | + init_code_gas, |
210 | ) |
211 | |
212 | # OPERATION |
213 | evm.memory += b"\x00" * extend_memory.expand_by |
214 | contract_address = compute_create2_contract_address( |
215 | evm.message.current_target, |
216 | salt, |
217 | memory_read_bytes(evm.memory, memory_start_position, memory_size), |
218 | ) |
219 | |
220 | generic_create( |
221 | evm, |
222 | endowment, |
223 | contract_address, |
224 | memory_start_position, |
225 | memory_size, |
226 | init_code_gas, |
227 | ) |
228 | |
229 | # PROGRAM COUNTER |
230 | evm.pc += 1 |
return_
Halts execution returning output data.
Parameters
evm : The current EVM frame.
def return_(evm: Evm) -> None:
234 | """ |
---|---|
235 | Halts execution returning output data. |
236 |
|
237 | Parameters |
238 | ---------- |
239 | evm : |
240 | The current EVM frame. |
241 | """ |
242 | # STACK |
243 | memory_start_position = pop(evm.stack) |
244 | memory_size = pop(evm.stack) |
245 | |
246 | # GAS |
247 | extend_memory = calculate_gas_extend_memory( |
248 | evm.memory, [(memory_start_position, memory_size)] |
249 | ) |
250 | |
251 | charge_gas(evm, GAS_ZERO + extend_memory.cost) |
252 | |
253 | # OPERATION |
254 | evm.memory += b"\x00" * extend_memory.expand_by |
255 | evm.output = memory_read_bytes( |
256 | evm.memory, memory_start_position, memory_size |
257 | ) |
258 | |
259 | evm.running = False |
260 | |
261 | # PROGRAM COUNTER |
262 | 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:
279 | """ |
---|---|
280 | Perform the core logic of the `CALL*` family of opcodes. |
281 | """ |
282 | from ...vm.interpreter import STACK_DEPTH_LIMIT, process_message |
283 | |
284 | evm.return_data = b"" |
285 | |
286 | if evm.message.depth + 1 > STACK_DEPTH_LIMIT: |
287 | evm.gas_left += gas |
288 | push(evm.stack, U256(0)) |
289 | return |
290 | |
291 | call_data = memory_read_bytes( |
292 | evm.memory, memory_input_start_position, memory_input_size |
293 | ) |
294 | code = get_account(evm.env.state, code_address).code |
295 | child_message = Message( |
296 | caller=caller, |
297 | target=to, |
298 | gas=gas, |
299 | value=value, |
300 | data=call_data, |
301 | code=code, |
302 | current_target=to, |
303 | depth=evm.message.depth + 1, |
304 | code_address=code_address, |
305 | should_transfer_value=should_transfer_value, |
306 | is_static=True if is_staticcall else evm.message.is_static, |
307 | accessed_addresses=evm.accessed_addresses.copy(), |
308 | accessed_storage_keys=evm.accessed_storage_keys.copy(), |
309 | parent_evm=evm, |
310 | ) |
311 | child_evm = process_message(child_message, evm.env) |
312 | |
313 | if child_evm.error: |
314 | incorporate_child_on_error(evm, child_evm) |
315 | evm.return_data = child_evm.output |
316 | push(evm.stack, U256(0)) |
317 | else: |
318 | incorporate_child_on_success(evm, child_evm) |
319 | evm.return_data = child_evm.output |
320 | push(evm.stack, U256(1)) |
321 | |
322 | actual_output_size = min(memory_output_size, U256(len(child_evm.output))) |
323 | memory_write( |
324 | evm.memory, |
325 | memory_output_start_position, |
326 | child_evm.output[:actual_output_size], |
327 | ) |
call
Message-call into an account.
Parameters
evm : The current EVM frame.
def call(evm: Evm) -> None:
331 | """ |
---|---|
332 | Message-call into an account. |
333 |
|
334 | Parameters |
335 | ---------- |
336 | evm : |
337 | The current EVM frame. |
338 | """ |
339 | # STACK |
340 | gas = Uint(pop(evm.stack)) |
341 | to = to_address(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 = GAS_WARM_ACCESS |
359 | else: |
360 | evm.accessed_addresses.add(to) |
361 | access_gas_cost = GAS_COLD_ACCOUNT_ACCESS |
362 | |
363 | create_gas_cost = ( |
364 | Uint(0) |
365 | if is_account_alive(evm.env.state, to) or value == 0 |
366 | else GAS_NEW_ACCOUNT |
367 | ) |
368 | transfer_gas_cost = Uint(0) if value == 0 else GAS_CALL_VALUE |
369 | message_call_gas = calculate_message_call_gas( |
370 | value, |
371 | gas, |
372 | Uint(evm.gas_left), |
373 | extend_memory.cost, |
374 | 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.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.stipend |
387 | else: |
388 | generic_call( |
389 | evm, |
390 | message_call_gas.stipend, |
391 | value, |
392 | evm.message.current_target, |
393 | to, |
394 | to, |
395 | True, |
396 | False, |
397 | memory_input_start_position, |
398 | memory_input_size, |
399 | memory_output_start_position, |
400 | memory_output_size, |
401 | ) |
402 | |
403 | # PROGRAM COUNTER |
404 | evm.pc += 1 |
callcode
Message-call into this account with alternative account’s code.
Parameters
evm : The current EVM frame.
def callcode(evm: Evm) -> None:
408 | """ |
---|---|
409 | Message-call into this account with alternative account’s code. |
410 |
|
411 | Parameters |
412 | ---------- |
413 | evm : |
414 | The current EVM frame. |
415 | """ |
416 | # STACK |
417 | gas = Uint(pop(evm.stack)) |
418 | code_address = to_address(pop(evm.stack)) |
419 | value = pop(evm.stack) |
420 | memory_input_start_position = pop(evm.stack) |
421 | memory_input_size = pop(evm.stack) |
422 | memory_output_start_position = pop(evm.stack) |
423 | memory_output_size = pop(evm.stack) |
424 | |
425 | # GAS |
426 | to = evm.message.current_target |
427 | |
428 | extend_memory = calculate_gas_extend_memory( |
429 | evm.memory, |
430 | [ |
431 | (memory_input_start_position, memory_input_size), |
432 | (memory_output_start_position, memory_output_size), |
433 | ], |
434 | ) |
435 | |
436 | if code_address in evm.accessed_addresses: |
437 | access_gas_cost = GAS_WARM_ACCESS |
438 | else: |
439 | evm.accessed_addresses.add(code_address) |
440 | access_gas_cost = GAS_COLD_ACCOUNT_ACCESS |
441 | |
442 | transfer_gas_cost = Uint(0) if value == 0 else GAS_CALL_VALUE |
443 | message_call_gas = calculate_message_call_gas( |
444 | value, |
445 | gas, |
446 | Uint(evm.gas_left), |
447 | extend_memory.cost, |
448 | access_gas_cost + transfer_gas_cost, |
449 | ) |
450 | charge_gas(evm, message_call_gas.cost + extend_memory.cost) |
451 | |
452 | # OPERATION |
453 | evm.memory += b"\x00" * extend_memory.expand_by |
454 | sender_balance = get_account( |
455 | evm.env.state, evm.message.current_target |
456 | ).balance |
457 | if sender_balance < value: |
458 | push(evm.stack, U256(0)) |
459 | evm.return_data = b"" |
460 | evm.gas_left += message_call_gas.stipend |
461 | else: |
462 | generic_call( |
463 | evm, |
464 | message_call_gas.stipend, |
465 | value, |
466 | evm.message.current_target, |
467 | to, |
468 | code_address, |
469 | True, |
470 | False, |
471 | memory_input_start_position, |
472 | memory_input_size, |
473 | memory_output_start_position, |
474 | memory_output_size, |
475 | ) |
476 | |
477 | # PROGRAM COUNTER |
478 | evm.pc += 1 |
selfdestruct
Halt execution and register account for later deletion.
Parameters
evm : The current EVM frame.
def selfdestruct(evm: Evm) -> None:
482 | """ |
---|---|
483 | Halt execution and register account for later deletion. |
484 |
|
485 | Parameters |
486 | ---------- |
487 | evm : |
488 | The current EVM frame. |
489 | """ |
490 | # STACK |
491 | beneficiary = to_address(pop(evm.stack)) |
492 | |
493 | # GAS |
494 | gas_cost = GAS_SELF_DESTRUCT |
495 | if beneficiary not in evm.accessed_addresses: |
496 | evm.accessed_addresses.add(beneficiary) |
497 | gas_cost += GAS_COLD_ACCOUNT_ACCESS |
498 | |
499 | if ( |
500 | not is_account_alive(evm.env.state, beneficiary) |
501 | and get_account(evm.env.state, evm.message.current_target).balance != 0 |
502 | ): |
503 | gas_cost += GAS_SELF_DESTRUCT_NEW_ACCOUNT |
504 | |
505 | charge_gas(evm, gas_cost) |
506 | if evm.message.is_static: |
507 | raise WriteInStaticContext |
508 | |
509 | originator = evm.message.current_target |
510 | beneficiary_balance = get_account(evm.env.state, beneficiary).balance |
511 | originator_balance = get_account(evm.env.state, originator).balance |
512 | |
513 | # First Transfer to beneficiary |
514 | set_account_balance( |
515 | evm.env.state, beneficiary, 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.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.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(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.stipend, |
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 += 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(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 | message_call_gas = calculate_message_call_gas( |
627 | U256(0), |
628 | gas, |
629 | Uint(evm.gas_left), |
630 | extend_memory.cost, |
631 | access_gas_cost, |
632 | ) |
633 | charge_gas(evm, message_call_gas.cost + extend_memory.cost) |
634 | |
635 | # OPERATION |
636 | evm.memory += b"\x00" * extend_memory.expand_by |
637 | generic_call( |
638 | evm, |
639 | message_call_gas.stipend, |
640 | U256(0), |
641 | evm.message.current_target, |
642 | to, |
643 | to, |
644 | True, |
645 | True, |
646 | memory_input_start_position, |
647 | memory_input_size, |
648 | memory_output_start_position, |
649 | memory_output_size, |
650 | ) |
651 | |
652 | # PROGRAM COUNTER |
653 | evm.pc += 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:
657 | """ |
---|---|
658 | Stop execution and revert state changes, without consuming all provided gas |
659 | and also has the ability to return a reason |
660 | Parameters |
661 | ---------- |
662 | evm : |
663 | The current EVM frame. |
664 | """ |
665 | # STACK |
666 | memory_start_index = pop(evm.stack) |
667 | size = pop(evm.stack) |
668 | |
669 | # GAS |
670 | extend_memory = calculate_gas_extend_memory( |
671 | evm.memory, [(memory_start_index, size)] |
672 | ) |
673 | |
674 | charge_gas(evm, extend_memory.cost) |
675 | |
676 | # OPERATION |
677 | evm.memory += b"\x00" * extend_memory.expand_by |
678 | output = memory_read_bytes(evm.memory, memory_start_index, size) |
679 | evm.output = bytes(output) |
680 | raise Revert |
681 | |
682 | # PROGRAM COUNTER |
683 | pass |