Skip to content
This repository was archived by the owner on Sep 8, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions evm/chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,6 @@ def configure(cls, name, vm_configuration, **overrides):
#
# Convenience and Helpers
#
def get_state_db(self):
"""
Passthrough helper to the current VM class.
"""
return self.get_vm().state_db

def get_block(self):
"""
Passthrough helper to the current VM class.
Expand Down
17 changes: 9 additions & 8 deletions evm/logic/call.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,8 @@ def __call__(self, computation):
computation.gas_meter.consume_gas(child_msg_gas_fee, reason=self.mnemonic)

# Pre-call checks
sender_balance = computation.state_db.get_balance(
computation.msg.storage_address,
)
with computation.vm.state_db(read_only=True) as state_db:
sender_balance = state_db.get_balance(computation.msg.storage_address)
insufficient_funds = should_transfer_value and sender_balance < value
stack_too_deep = computation.msg.depth + 1 > constants.STACK_DEPTH_LIMIT

Expand All @@ -82,10 +81,11 @@ def __call__(self, computation):
computation.gas_meter.return_gas(child_msg_gas)
computation.stack.push(0)
else:
if code_address:
code = computation.state_db.get_code(code_address)
else:
code = computation.state_db.get_code(to)
with computation.vm.state_db(read_only=True) as state_db:
if code_address:
code = state_db.get_code(code_address)
else:
code = state_db.get_code(to)

child_msg_kwargs = {
'gas': child_msg_gas,
Expand Down Expand Up @@ -123,7 +123,8 @@ def __call__(self, computation):

class Call(BaseCall):
def compute_msg_extra_gas(self, computation, gas, to, value):
account_exists = computation.state_db.account_exists(to)
with computation.vm.state_db(read_only=True) as state_db:
account_exists = state_db.account_exists(to)
transfer_gas_fee = constants.GAS_CALLVALUE if value else 0
create_gas_fee = constants.GAS_NEWACCOUNT if not account_exists else 0
return transfer_gas_fee + create_gas_fee
Expand Down
9 changes: 6 additions & 3 deletions evm/logic/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@

def balance(computation):
addr = force_bytes_to_address(computation.stack.pop(type_hint=constants.BYTES))
balance = computation.state_db.get_balance(addr)
with computation.vm.state_db(read_only=True) as state_db:
balance = state_db.get_balance(addr)
computation.stack.push(balance)


Expand Down Expand Up @@ -107,7 +108,8 @@ def gasprice(computation):

def extcodesize(computation):
account = force_bytes_to_address(computation.stack.pop(type_hint=constants.BYTES))
code_size = len(computation.state_db.get_code(account))
with computation.vm.state_db(read_only=True) as state_db:
code_size = len(state_db.get_code(account))

computation.stack.push(code_size)

Expand All @@ -130,7 +132,8 @@ def extcodecopy(computation):
reason='EXTCODECOPY: word gas cost',
)

code = computation.state_db.get_code(account)
with computation.vm.state_db(read_only=True) as state_db:
code = state_db.get_code(account)
code_bytes = code[code_start_position:code_start_position + size]
padded_code_bytes = pad_right(code_bytes, size, b'\x00')

Expand Down
29 changes: 16 additions & 13 deletions evm/logic/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
def sstore(computation):
slot, value = computation.stack.pop(num_items=2, type_hint=constants.UINT256)

current_value = computation.state_db.get_storage(
address=computation.msg.storage_address,
slot=slot,
)
with computation.vm.state_db(read_only=True) as state_db:
current_value = state_db.get_storage(
address=computation.msg.storage_address,
slot=slot,
)

is_currently_empty = not bool(current_value)
is_going_to_be_empty = not bool(value)
Expand Down Expand Up @@ -42,18 +43,20 @@ def sstore(computation):
if gas_refund:
computation.gas_meter.refund_gas(gas_refund)

computation.state_db.set_storage(
address=computation.msg.storage_address,
slot=slot,
value=value,
)
with computation.vm.state_db() as state_db:
state_db.set_storage(
address=computation.msg.storage_address,
slot=slot,
value=value,
)


def sload(computation):
slot = computation.stack.pop(type_hint=constants.UINT256)

value = computation.state_db.get_storage(
address=computation.msg.storage_address,
slot=slot,
)
with computation.vm.state_db(read_only=True) as state_db:
value = state_db.get_storage(
address=computation.msg.storage_address,
slot=slot,
)
computation.stack.push(value)
38 changes: 19 additions & 19 deletions evm/logic/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,24 @@ def suicide(computation):

def suicide_eip150(computation):
beneficiary = force_bytes_to_address(computation.stack.pop(type_hint=constants.BYTES))
if not computation.state_db.account_exists(beneficiary):
computation.gas_meter.consume_gas(
constants.GAS_SUICIDE_NEWACCOUNT, reason=mnemonics.SUICIDE)
with computation.vm.state_db(read_only=True) as state_db:
if not state_db.account_exists(beneficiary):
computation.gas_meter.consume_gas(
constants.GAS_SUICIDE_NEWACCOUNT, reason=mnemonics.SUICIDE)
_suicide(computation, beneficiary)


def _suicide(computation, beneficiary):
local_balance = computation.state_db.get_balance(computation.msg.storage_address)
beneficiary_balance = computation.state_db.get_balance(beneficiary)

# 1st: Transfer to beneficiary
computation.state_db.set_balance(
beneficiary,
local_balance + beneficiary_balance,
)
# 2nd: Zero the balance of the address being deleted (must come after
# sending to beneficiary in case the contract named itself as the
# beneficiary.
computation.state_db.set_balance(computation.msg.storage_address, 0)
with computation.vm.state_db() as state_db:
local_balance = state_db.get_balance(computation.msg.storage_address)
beneficiary_balance = state_db.get_balance(beneficiary)

# 1st: Transfer to beneficiary
state_db.set_balance(beneficiary, local_balance + beneficiary_balance)
# 2nd: Zero the balance of the address being deleted (must come after
# sending to beneficiary in case the contract named itself as the
# beneficiary.
state_db.set_balance(computation.msg.storage_address, 0)

# 3rd: Register the account to be deleted
computation.register_account_for_deletion(computation.msg.storage_address)
Expand All @@ -66,8 +65,9 @@ def __call__(self, computation):

computation.extend_memory(start_position, size)

insufficient_funds = computation.state_db.get_balance(
computation.msg.storage_address) < value
with computation.vm.state_db(read_only=True) as state_db:
insufficient_funds = state_db.get_balance(
computation.msg.storage_address) < value
stack_too_deep = computation.msg.depth + 1 > constants.STACK_DEPTH_LIMIT

if insufficient_funds or stack_too_deep:
Expand All @@ -80,8 +80,8 @@ def __call__(self, computation):
computation.gas_meter.gas_remaining)
computation.gas_meter.consume_gas(create_msg_gas, reason="CREATE")

creation_nonce = computation.state_db.get_nonce(
computation.msg.storage_address)
with computation.vm.state_db(read_only=True) as state_db:
creation_nonce = state_db.get_nonce(computation.msg.storage_address)
contract_address = generate_contract_address(
computation.msg.storage_address, creation_nonce)

Expand Down
1 change: 0 additions & 1 deletion evm/utils/fixture_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,6 @@ def setup_state_db(desired_state, state_db):
state_db.set_nonce(account, nonce)
state_db.set_code(account, code)
state_db.set_balance(account, balance)
return state_db


def verify_state_db(expected_state, state_db):
Expand Down
57 changes: 26 additions & 31 deletions evm/vm/base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import absolute_import

from contextlib import contextmanager
import logging

from evm.constants import (
Expand Down Expand Up @@ -56,19 +57,17 @@ def configure(cls,
)
return type(name, (cls,), overrides)

_block = None
state_db = None

@property
def block(self):
if self._block is None:
raise AttributeError("No block property set")
return self._block

@block.setter
def block(self, value):
self._block = value
self.state_db = State(db=self.db, root_hash=value.header.state_root)
@contextmanager
def state_db(self, read_only=False):
state = State(db=self.db, root_hash=self.block.header.state_root)
yield state
if read_only:
# TODO: This is a bit of a hack; ideally we should raise an error whenever the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I like the idea of implementing this as a wrapper around the self.db which raises errors on any write operations. Might be a good easy pickings ticket to open.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#80

# callsite tries to call a State method that modifies it.
assert state.root_hash == self.block.header.state_root
elif self.block.header.state_root != state.root_hash:
self.logger.debug("Updating block's state_root to %s", state.root_hash)
self.block.header.state_root = state.root_hash

#
# Logging
Expand All @@ -85,12 +84,7 @@ def apply_transaction(self, transaction):
Apply the transaction to the vm in the current block.
"""
computation = self.execute_transaction(transaction)
# NOTE: mutation. Needed in order to update self.state_db, so we should be able to get rid
# of this once we fix https://github.com/pipermerriam/py-evm/issues/67
self.block = self.block.add_transaction(
transaction=transaction,
computation=computation,
)
self.block.add_transaction(transaction, computation)
return computation

def execute_transaction(self, transaction):
Expand Down Expand Up @@ -151,12 +145,15 @@ def mine_block(self, *args, **kwargs):
block = self.block
block.mine(*args, **kwargs)

if block.number > 0:
block_reward = self.get_block_reward(block.number) + (
len(block.uncles) * self.get_nephew_reward(block.number)
)
if block.number == 0:
return block

block_reward = self.get_block_reward(block.number) + (
len(block.uncles) * self.get_nephew_reward(block.number)
)

self.state_db.delta_balance(block.header.coinbase, block_reward)
with self.state_db() as state_db:
state_db.delta_balance(block.header.coinbase, block_reward)
self.logger.debug(
"BLOCK REWARD: %s -> %s",
block_reward,
Expand All @@ -167,17 +164,13 @@ def mine_block(self, *args, **kwargs):
uncle_reward = BLOCK_REWARD * (
UNCLE_DEPTH_PENALTY_FACTOR + uncle.block_number - block.number
) // UNCLE_DEPTH_PENALTY_FACTOR
self.state_db.delta_balance(uncle.coinbase, uncle_reward)
state_db.delta_balance(uncle.coinbase, uncle_reward)
self.logger.debug(
"UNCLE REWARD REWARD: %s -> %s",
uncle_reward,
uncle.coinbase,
)

self.logger.debug('BEFORE ROOT: %s', block.header.state_root)
block.header.state_root = self.state_db.root_hash
self.logger.debug('STATE_ROOT: %s', block.header.state_root)

return block

#
Expand Down Expand Up @@ -264,15 +257,17 @@ def snapshot(self):

TODO: This needs to do more than just snapshot the state_db but this is a start.
"""
return self.state_db.snapshot()
with self.state_db(read_only=True) as state_db:
return state_db.snapshot()

def revert(self, snapshot):
"""
Revert the VM to the state

TODO: This needs to do more than just snapshot the state_db but this is a start.
"""
return self.state_db.revert(snapshot)
with self.state_db() as state_db:
return state_db.revert(snapshot)

#
# Opcode API
Expand Down
7 changes: 0 additions & 7 deletions evm/vm/computation.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,6 @@ def is_origin_computation(self):
"""
return self.msg.is_origin

@property
def state_db(self):
"""
Convenience access to the state database
"""
return self.vm.state_db

#
# Execution
#
Expand Down
Loading