Skip to content
This repository was archived by the owner on Sep 8, 2025. It is now read-only.

Commit 0013997

Browse files
authored
Merge pull request #1921 from carver/static-apply-message
Convert apply_message to a classmethod
2 parents 086e070 + fae67de commit 0013997

File tree

12 files changed

+177
-91
lines changed

12 files changed

+177
-91
lines changed

eth/abc.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,17 +1542,28 @@ def get_log_entries(self) -> Tuple[Tuple[bytes, Tuple[int, ...], bytes], ...]:
15421542
#
15431543
# State Transition
15441544
#
1545+
@classmethod
15451546
@abstractmethod
1546-
def apply_message(self) -> 'ComputationAPI':
1547+
def apply_message(
1548+
cls,
1549+
state: 'StateAPI',
1550+
message: MessageAPI,
1551+
transaction_context: TransactionContextAPI) -> 'ComputationAPI':
15471552
"""
1548-
Execution of a VM message.
1553+
Execute a VM message. This is where the VM-specific call logic exists.
15491554
"""
15501555
...
15511556

1557+
@classmethod
15521558
@abstractmethod
1553-
def apply_create_message(self) -> 'ComputationAPI':
1559+
def apply_create_message(
1560+
cls,
1561+
state: 'StateAPI',
1562+
message: MessageAPI,
1563+
transaction_context: TransactionContextAPI) -> 'ComputationAPI':
15541564
"""
1555-
Execution of a VM message to create a new contract.
1565+
Execute a VM message to create a new contract. This is where the VM-specific
1566+
create logic exists.
15561567
"""
15571568
...
15581569

@@ -1563,7 +1574,14 @@ def apply_computation(cls,
15631574
message: MessageAPI,
15641575
transaction_context: TransactionContextAPI) -> 'ComputationAPI':
15651576
"""
1566-
Perform the computation that would be triggered by the VM message.
1577+
Execute the logic within the message: Either run the precompile, or
1578+
step through each opcode. Generally, the only VM-specific logic is for
1579+
each opcode as it executes.
1580+
1581+
This should rarely be called directly, because it will skip over other important
1582+
VM-specific logic that happens before or after the execution.
1583+
1584+
Instead, prefer :meth:`~apply_message` or :meth:`~apply_create_message`.
15671585
"""
15681586
...
15691587

@@ -2499,7 +2517,17 @@ def execute_bytecode(self,
24992517
code_address: Address = None) -> ComputationAPI:
25002518
"""
25012519
Execute raw bytecode in the context of the current state of
2502-
the virtual machine.
2520+
the virtual machine. Note that this skips over some of the logic
2521+
that would normally happen during a call. Watch out for:
2522+
2523+
- value (ether) is *not* transferred
2524+
- state is *not* rolled back in case of an error
2525+
- The target account is *not* necessarily created
2526+
- others...
2527+
2528+
For other potential surprises, check the implementation differences
2529+
between :meth:`ComputationAPI.apply_computation` and
2530+
:meth:`ComputationAPI.apply_message`. (depending on the VM fork)
25032531
"""
25042532
...
25052533

eth/vm/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ def execute_bytecode(self,
216216
)
217217

218218
# Execute it in the VM
219-
return self.state.get_computation(message, transaction_context).apply_computation(
219+
return self.state.computation_class.apply_computation(
220220
self.state,
221221
message,
222222
transaction_context,

eth/vm/computation.py

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
from abc import (
2-
abstractmethod,
3-
)
41
import itertools
52
from types import TracebackType
63
from typing import (
@@ -369,17 +366,17 @@ def apply_child_computation(self, child_msg: MessageAPI) -> ComputationAPI:
369366

370367
def generate_child_computation(self, child_msg: MessageAPI) -> ComputationAPI:
371368
if child_msg.is_create:
372-
child_computation = self.__class__(
369+
child_computation = self.apply_create_message(
373370
self.state,
374371
child_msg,
375372
self.transaction_context,
376-
).apply_create_message()
373+
)
377374
else:
378-
child_computation = self.__class__(
375+
child_computation = self.apply_message(
379376
self.state,
380377
child_msg,
381378
self.transaction_context,
382-
).apply_message()
379+
)
383380
return child_computation
384381

385382
def add_child_computation(self, child_computation: ComputationAPI) -> None:
@@ -513,14 +510,6 @@ def __exit__(self,
513510
#
514511
# State Transition
515512
#
516-
@abstractmethod
517-
def apply_message(self) -> ComputationAPI:
518-
raise NotImplementedError("Must be implemented by subclasses")
519-
520-
@abstractmethod
521-
def apply_create_message(self) -> ComputationAPI:
522-
raise NotImplementedError("Must be implemented by subclasses")
523-
524513
@classmethod
525514
def apply_computation(cls,
526515
state: StateAPI,

eth/vm/forks/frontier/computation.py

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
)
1616
from eth.abc import (
1717
ComputationAPI,
18+
MessageAPI,
19+
StateAPI,
20+
TransactionContextAPI,
1821
)
1922
from eth.exceptions import (
2023
OutOfGas,
@@ -45,47 +48,59 @@ class FrontierComputation(BaseComputation):
4548
opcodes = FRONTIER_OPCODES
4649
_precompiles = FRONTIER_PRECOMPILES # type: ignore # https://github.com/python/mypy/issues/708 # noqa: E501
4750

48-
def apply_message(self) -> ComputationAPI:
49-
snapshot = self.state.snapshot()
51+
@classmethod
52+
def apply_message(
53+
cls,
54+
state: StateAPI,
55+
message: MessageAPI,
56+
transaction_context: TransactionContextAPI) -> ComputationAPI:
5057

51-
if self.msg.depth > STACK_DEPTH_LIMIT:
58+
snapshot = state.snapshot()
59+
60+
if message.depth > STACK_DEPTH_LIMIT:
5261
raise StackDepthLimit("Stack depth limit reached")
5362

54-
if self.msg.should_transfer_value and self.msg.value:
55-
sender_balance = self.state.get_balance(self.msg.sender)
63+
if message.should_transfer_value and message.value:
64+
sender_balance = state.get_balance(message.sender)
5665

57-
if sender_balance < self.msg.value:
66+
if sender_balance < message.value:
5867
raise InsufficientFunds(
59-
f"Insufficient funds: {sender_balance} < {self.msg.value}"
68+
f"Insufficient funds: {sender_balance} < {message.value}"
6069
)
6170

62-
self.state.delta_balance(self.msg.sender, -1 * self.msg.value)
63-
self.state.delta_balance(self.msg.storage_address, self.msg.value)
71+
state.delta_balance(message.sender, -1 * message.value)
72+
state.delta_balance(message.storage_address, message.value)
6473

65-
self.logger.debug2(
74+
cls.logger.debug2(
6675
"TRANSFERRED: %s from %s -> %s",
67-
self.msg.value,
68-
encode_hex(self.msg.sender),
69-
encode_hex(self.msg.storage_address),
76+
message.value,
77+
encode_hex(message.sender),
78+
encode_hex(message.storage_address),
7079
)
7180

72-
self.state.touch_account(self.msg.storage_address)
81+
state.touch_account(message.storage_address)
7382

74-
computation = self.apply_computation(
75-
self.state,
76-
self.msg,
77-
self.transaction_context,
83+
computation = cls.apply_computation(
84+
state,
85+
message,
86+
transaction_context,
7887
)
7988

8089
if computation.is_error:
81-
self.state.revert(snapshot)
90+
state.revert(snapshot)
8291
else:
83-
self.state.commit(snapshot)
92+
state.commit(snapshot)
8493

8594
return computation
8695

87-
def apply_create_message(self) -> ComputationAPI:
88-
computation = self.apply_message()
96+
@classmethod
97+
def apply_create_message(
98+
cls,
99+
state: StateAPI,
100+
message: MessageAPI,
101+
transaction_context: TransactionContextAPI) -> ComputationAPI:
102+
103+
computation = cls.apply_message(state, message, transaction_context)
89104

90105
if computation.is_error:
91106
return computation
@@ -102,11 +117,11 @@ def apply_create_message(self) -> ComputationAPI:
102117
except OutOfGas:
103118
computation.output = b''
104119
else:
105-
self.logger.debug2(
120+
cls.logger.debug2(
106121
"SETTING CODE: %s -> length: %s | hash: %s",
107-
encode_hex(self.msg.storage_address),
122+
encode_hex(message.storage_address),
108123
len(contract_code),
109124
encode_hex(keccak(contract_code))
110125
)
111-
self.state.set_code(self.msg.storage_address, contract_code)
126+
state.set_code(message.storage_address, contract_code)
112127
return computation

eth/vm/forks/frontier/state.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,14 +123,17 @@ def build_computation(self,
123123
encode_hex(message.storage_address),
124124
)
125125
else:
126-
computation = self.vm_state.get_computation(
126+
computation = self.vm_state.computation_class.apply_create_message(
127+
self.vm_state,
127128
message,
128129
transaction_context,
129-
).apply_create_message()
130+
)
130131
else:
131-
computation = self.vm_state.get_computation(
132+
computation = self.vm_state.computation_class.apply_message(
133+
self.vm_state,
132134
message,
133-
transaction_context).apply_message()
135+
transaction_context,
136+
)
134137

135138
return computation
136139

eth/vm/forks/homestead/computation.py

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@
88
encode_hex,
99
)
1010

11-
from eth.abc import ComputationAPI
11+
from eth.abc import (
12+
ComputationAPI,
13+
MessageAPI,
14+
StateAPI,
15+
TransactionContextAPI,
16+
)
1217
from eth.vm.forks.frontier.computation import (
1318
FrontierComputation,
1419
)
@@ -24,13 +29,18 @@ class HomesteadComputation(FrontierComputation):
2429
# Override
2530
opcodes = HOMESTEAD_OPCODES
2631

27-
def apply_create_message(self) -> ComputationAPI:
28-
snapshot = self.state.snapshot()
32+
@classmethod
33+
def apply_create_message(
34+
cls,
35+
state: StateAPI,
36+
message: MessageAPI,
37+
transaction_context: TransactionContextAPI) -> ComputationAPI:
38+
snapshot = state.snapshot()
2939

30-
computation = self.apply_message()
40+
computation = cls.apply_message(state, message, transaction_context)
3141

3242
if computation.is_error:
33-
self.state.revert(snapshot)
43+
state.revert(snapshot)
3444
return computation
3545
else:
3646
contract_code = computation.output
@@ -46,18 +56,18 @@ def apply_create_message(self) -> ComputationAPI:
4656
# Different from Frontier: reverts state on gas failure while
4757
# writing contract code.
4858
computation.error = err
49-
self.state.revert(snapshot)
59+
state.revert(snapshot)
5060
else:
51-
if self.logger:
52-
self.logger.debug2(
61+
if cls.logger:
62+
cls.logger.debug2(
5363
"SETTING CODE: %s -> length: %s | hash: %s",
54-
encode_hex(self.msg.storage_address),
64+
encode_hex(message.storage_address),
5565
len(contract_code),
5666
encode_hex(keccak(contract_code))
5767
)
5868

59-
self.state.set_code(self.msg.storage_address, contract_code)
60-
self.state.commit(snapshot)
69+
state.set_code(message.storage_address, contract_code)
70+
state.commit(snapshot)
6171
else:
62-
self.state.commit(snapshot)
72+
state.commit(snapshot)
6373
return computation

eth/vm/forks/spurious_dragon/computation.py

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@
44
)
55

66
from eth import constants
7-
from eth.abc import ComputationAPI
7+
from eth.abc import (
8+
ComputationAPI,
9+
MessageAPI,
10+
StateAPI,
11+
TransactionContextAPI,
12+
)
813
from eth.exceptions import (
914
OutOfGas,
1015
)
@@ -24,16 +29,22 @@ class SpuriousDragonComputation(HomesteadComputation):
2429
# Override
2530
opcodes = SPURIOUS_DRAGON_OPCODES
2631

27-
def apply_create_message(self) -> ComputationAPI:
28-
snapshot = self.state.snapshot()
32+
@classmethod
33+
def apply_create_message(
34+
cls,
35+
state: StateAPI,
36+
message: MessageAPI,
37+
transaction_context: TransactionContextAPI) -> ComputationAPI:
38+
39+
snapshot = state.snapshot()
2940

3041
# EIP161 nonce incrementation
31-
self.state.increment_nonce(self.msg.storage_address)
42+
state.increment_nonce(message.storage_address)
3243

33-
computation = self.apply_message()
44+
computation = cls.apply_message(state, message, transaction_context)
3445

3546
if computation.is_error:
36-
self.state.revert(snapshot)
47+
state.revert(snapshot)
3748
return computation
3849
else:
3950
contract_code = computation.output
@@ -43,7 +54,7 @@ def apply_create_message(self) -> ComputationAPI:
4354
f"Contract code size exceeds EIP170 limit of {EIP170_CODE_SIZE_LIMIT}."
4455
f" Got code of size: {len(contract_code)}"
4556
)
46-
self.state.revert(snapshot)
57+
state.revert(snapshot)
4758
elif contract_code:
4859
contract_code_gas_cost = len(contract_code) * constants.GAS_CODEDEPOSIT
4960
try:
@@ -55,18 +66,18 @@ def apply_create_message(self) -> ComputationAPI:
5566
# Different from Frontier: reverts state on gas failure while
5667
# writing contract code.
5768
computation.error = err
58-
self.state.revert(snapshot)
69+
state.revert(snapshot)
5970
else:
60-
if self.logger:
61-
self.logger.debug2(
71+
if cls.logger:
72+
cls.logger.debug2(
6273
"SETTING CODE: %s -> length: %s | hash: %s",
63-
encode_hex(self.msg.storage_address),
74+
encode_hex(message.storage_address),
6475
len(contract_code),
6576
encode_hex(keccak(contract_code))
6677
)
6778

68-
self.state.set_code(self.msg.storage_address, contract_code)
69-
self.state.commit(snapshot)
79+
state.set_code(message.storage_address, contract_code)
80+
state.commit(snapshot)
7081
else:
71-
self.state.commit(snapshot)
82+
state.commit(snapshot)
7283
return computation

newsfragments/1921.internal.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fix for creating a duplicate "ghost" Computation that was never used. It didn't
2+
break anything, but was ineligant and surprising to get extra objects created
3+
that were mostly useless. This was achieved by changing
4+
:meth:`ComputationAPI.apply_message` and
5+
:meth:`ComputationAPI.apply_create_message` to be class methods.

0 commit comments

Comments
 (0)