Skip to content

Commit 9724a4c

Browse files
lrettigpipermerriam
authored andcommitted
Add VM function to execute raw bytecode (#315)
* Minimal proof of concept for exec bytecode Construct a message and pass it through apply_computation * Add missing origin param * Add transaction_context Rebase and update to work with #327 changes. Adds code_address to allow execution of arbitrary code in the context of an existing contract per #306 (comment). * Allow code_address to be None * Bugfix: pass tx_context to apply_computation * Parametrize the computation_getter Per @pipermerriam's suggestion
1 parent 9df7a3d commit 9724a4c

File tree

3 files changed

+88
-22
lines changed

3 files changed

+88
-22
lines changed

evm/vm/base.py

+44-3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
validate_length_lte,
4040
validate_gas_limit,
4141
)
42+
from evm.vm.message import (
43+
Message,
44+
)
4245

4346
from .execution_context import (
4447
ExecutionContext,
@@ -48,8 +51,8 @@
4851
class VM(Configurable):
4952
"""
5053
The VM class represents the Chain rules for a specific protocol definition
51-
such as the Frontier or Homestead network. Defining an Chain defining
52-
individual VM classes for each fork of the protocol rules within that
54+
such as the Frontier or Homestead network. Define a Chain which specifies
55+
the individual VM classes for each fork of the protocol rules within that
5356
network.
5457
"""
5558
chaindb = None
@@ -89,6 +92,44 @@ def apply_transaction(self, transaction):
8992

9093
return computation, self.block
9194

95+
def execute_bytecode(self,
96+
origin,
97+
gas_price,
98+
gas,
99+
to,
100+
sender,
101+
value,
102+
data,
103+
code,
104+
code_address=None,
105+
):
106+
if origin is None:
107+
origin = sender
108+
109+
# Construct a message
110+
message = Message(
111+
gas=gas,
112+
to=to,
113+
sender=sender,
114+
value=value,
115+
data=data,
116+
code=code,
117+
code_address=code_address,
118+
)
119+
120+
# Construction a tx context
121+
transaction_context = self.state.get_transaction_context_class()(
122+
gas_price=gas_price,
123+
origin=origin,
124+
)
125+
126+
# Execute it in the VM
127+
return self.state.get_computation(message, transaction_context).apply_computation(
128+
self.state,
129+
message,
130+
transaction_context,
131+
)
132+
92133
#
93134
# Mining
94135
#
@@ -433,7 +474,7 @@ def compute_difficulty(cls, parent_header, timestamp):
433474
#
434475
def clear_journal(self):
435476
"""
436-
Cleare the journal. This should be called at any point of VM execution
477+
Clear the journal. This should be called at any point of VM execution
437478
where the statedb is being committed, such as after a transaction has
438479
been applied to a block.
439480
"""

evm/vm_state.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ def apply_transaction(
201201
"""
202202
Apply transaction to the given block
203203
204-
:param transaction: the transaction need to be applied
204+
:param transaction: the transaction to apply
205205
:param block: the block which the transaction applies on
206206
:type transaction: Transaction
207207
:type block: Block

tests/json-fixtures/test_virtual_machine.py

+43-18
Original file line numberDiff line numberDiff line change
@@ -135,23 +135,7 @@ def vm_class(request):
135135
assert False, "Unsupported VM: {0}".format(request.param)
136136

137137

138-
def test_vm_fixtures(fixture, vm_class):
139-
chaindb = ChainDB(get_db_backend())
140-
header = BlockHeader(
141-
coinbase=fixture['env']['currentCoinbase'],
142-
difficulty=fixture['env']['currentDifficulty'],
143-
block_number=fixture['env']['currentNumber'],
144-
gas_limit=fixture['env']['currentGasLimit'],
145-
timestamp=fixture['env']['currentTimestamp'],
146-
)
147-
vm = vm_class(header=header, chaindb=chaindb)
148-
vm_state = vm.state
149-
with vm_state.state_db() as state_db:
150-
setup_state_db(fixture['pre'], state_db)
151-
code = state_db.get_code(fixture['exec']['address'])
152-
# Update state_root manually
153-
vm.block.header.state_root = vm_state.state_root
154-
138+
def fixture_to_computation(fixture, code, vm):
155139
message = Message(
156140
to=fixture['exec']['address'],
157141
sender=fixture['exec']['caller'],
@@ -164,11 +148,52 @@ def test_vm_fixtures(fixture, vm_class):
164148
origin=fixture['exec']['origin'],
165149
gas_price=fixture['exec']['gasPrice'],
166150
)
167-
computation = vm.state.get_computation(message, transaction_context).apply_computation(
151+
return vm.state.get_computation(message, transaction_context).apply_computation(
168152
vm.state,
169153
message,
170154
transaction_context,
171155
)
156+
157+
158+
def fixture_to_bytecode_computation(fixture, code, vm):
159+
return vm.execute_bytecode(
160+
origin=fixture['exec']['origin'],
161+
gas_price=fixture['exec']['gasPrice'],
162+
gas=fixture['exec']['gas'],
163+
to=fixture['exec']['address'],
164+
sender=fixture['exec']['caller'],
165+
value=fixture['exec']['value'],
166+
data=fixture['exec']['data'],
167+
code=code,
168+
)
169+
170+
171+
@pytest.mark.parametrize(
172+
'computation_getter',
173+
(
174+
fixture_to_bytecode_computation,
175+
fixture_to_computation,
176+
),
177+
)
178+
def test_vm_fixtures(fixture, vm_class, computation_getter):
179+
chaindb = ChainDB(get_db_backend())
180+
header = BlockHeader(
181+
coinbase=fixture['env']['currentCoinbase'],
182+
difficulty=fixture['env']['currentDifficulty'],
183+
block_number=fixture['env']['currentNumber'],
184+
gas_limit=fixture['env']['currentGasLimit'],
185+
timestamp=fixture['env']['currentTimestamp'],
186+
)
187+
vm = vm_class(header=header, chaindb=chaindb)
188+
vm_state = vm.state
189+
with vm_state.state_db() as state_db:
190+
setup_state_db(fixture['pre'], state_db)
191+
code = state_db.get_code(fixture['exec']['address'])
192+
# Update state_root manually
193+
vm.block.header.state_root = vm_state.state_root
194+
195+
computation = computation_getter(fixture, code, vm)
196+
172197
# Update state_root manually
173198
vm.block.header.state_root = computation.vm_state.state_root
174199

0 commit comments

Comments
 (0)