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 |