Skip to content

Commit

Permalink
Merge pull request #244 from hwwhww/state_obj2
Browse files Browse the repository at this point in the history
Implemented `VMState` and Refactored `Computation`
  • Loading branch information
hwwhww authored Dec 29, 2017
2 parents 0281dd7 + 6ecad32 commit 362266c
Show file tree
Hide file tree
Showing 30 changed files with 669 additions and 428 deletions.
135 changes: 116 additions & 19 deletions evm/vm/computation.py → evm/computation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,36 @@
GAS_MEMORY_QUADRATIC_DENOMINATOR,
)
from evm.exceptions import (
Halt,
VMError,
)
from evm.validation import (
validate_canonical_address,
validate_uint256,
validate_is_bytes,
from evm.logic.invalid import (
InvalidOpcode,
)

from evm.utils.hexadecimal import (
encode_hex,
)
from evm.utils.numeric import (
ceil32,
)

from .code_stream import (
from evm.validation import (
validate_canonical_address,
validate_is_bytes,
validate_uint256,
)
from evm.vm.code_stream import (
CodeStream,
)
from .gas_meter import (
from evm.vm.gas_meter import (
GasMeter,
)
from .memory import (
from evm.vm.memory import (
Memory,
)
from .message import (
from evm.vm.message import (
Message,
)
from .stack import (
from evm.vm.stack import (
Stack,
)

Expand All @@ -47,11 +49,11 @@ def memory_gas_cost(size_in_bytes):
return total_cost


class Computation(object):
class BaseComputation(object):
"""
The execution computation
"""
vm = None
vm_state = None
msg = None

memory = None
Expand All @@ -69,10 +71,13 @@ class Computation(object):
logs = None
accounts_to_delete = None

opcodes = None
precompiles = None

logger = logging.getLogger('evm.vm.computation.Computation')

def __init__(self, vm, message):
self.vm = vm
def __init__(self, vm_state, message, opcodes, precompiles):
self.vm_state = vm_state
self.msg = message

self.memory = Memory()
Expand All @@ -86,6 +91,9 @@ def __init__(self, vm, message):
code = message.code
self.code = CodeStream(code)

self.opcodes = opcodes
self.precompiles = precompiles

#
# Convenience
#
Expand Down Expand Up @@ -192,12 +200,31 @@ def output(self, value):
# Runtime operations
#
def apply_child_computation(self, child_msg):
child_computation = self.generate_child_computation(
self.vm_state,
child_msg,
self.opcodes,
self.precompiles,
)
self.add_child_computation(child_computation)
return child_computation

@classmethod
def generate_child_computation(cls, vm_state, child_msg, opcodes, precompiles):
if child_msg.is_create:
child_computation = self.vm.apply_create_message(child_msg)
child_computation = cls(
vm_state,
child_msg,
opcodes,
precompiles,
).apply_create_message()
else:
child_computation = self.vm.apply_message(child_msg)

self.add_child_computation(child_computation)
child_computation = cls(
vm_state,
child_msg,
opcodes,
precompiles,
).apply_message()
return child_computation

def add_child_computation(self, child_computation):
Expand Down Expand Up @@ -334,3 +361,73 @@ def __exit__(self, exc_type, exc_value, traceback):
self.msg.gas - self.gas_meter.gas_remaining,
self.gas_meter.gas_remaining,
)

#
# State Transition
#
def apply_message(self):
"""
Execution of an VM message.
"""
raise NotImplementedError("Must be implemented by subclasses")

def apply_create_message(self):
"""
Execution of an VM message to create a new contract.
"""
raise NotImplementedError("Must be implemented by subclasses")

@classmethod
def apply_computation(cls, vm_state, message, opcodes, precompiles):
"""
Perform the computation that would be triggered by the VM message.
"""
with cls(vm_state, message, opcodes, precompiles) as computation:
# Early exit on pre-compiles
if message.code_address in computation.precompiles:
computation.precompiles[message.code_address](computation)
return computation

for opcode in computation.code:
opcode_fn = computation.get_opcode_fn(computation.opcodes, opcode)

computation.logger.trace(
"OPCODE: 0x%x (%s) | pc: %s",
opcode,
opcode_fn.mnemonic,
max(0, computation.code.pc - 1),
)

try:
opcode_fn(computation=computation)
except Halt:
break
return computation

#
# Opcode API
#
def get_opcode_fn(self, opcodes, opcode):
try:
return opcodes[opcode]
except KeyError:
return InvalidOpcode(opcode)

#
# classmethod
#
@classmethod
def configure(cls,
name,
**overrides):
"""
Class factory method for simple inline subclassing.
"""
for key in overrides:
if not hasattr(cls, key):
raise TypeError(
"The Computation.configure cannot set attributes that are not "
"already present on the base class. The attribute `{0}` was "
"not found on the base class `{1}`".format(key, cls)
)
return type(name, (cls,), overrides)
4 changes: 2 additions & 2 deletions evm/db/chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
JournalDB,
)
from evm.db.state import (
State,
AccountStateDB,
)
from evm.rlp.headers import (
BlockHeader,
Expand Down Expand Up @@ -243,4 +243,4 @@ def clear(self):
self.db.clear()

def get_state_db(self, state_root, read_only):
return State(db=self.db, root_hash=state_root, read_only=read_only)
return AccountStateDB(db=self.db, root_hash=state_root, read_only=read_only)
2 changes: 1 addition & 1 deletion evm/db/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
from .hash_trie import HashTrie


class State:
class AccountStateDB:
"""
High level API around account storage.
"""
Expand Down
12 changes: 6 additions & 6 deletions evm/logic/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,26 @@
def blockhash(computation):
block_number = computation.stack.pop(type_hint=constants.UINT256)

block_hash = computation.vm.get_ancestor_hash(block_number)
block_hash = computation.vm_state.get_ancestor_hash(block_number)

computation.stack.push(block_hash)


def coinbase(computation):
computation.stack.push(computation.vm.block.header.coinbase)
computation.stack.push(computation.vm_state.coinbase)


def timestamp(computation):
computation.stack.push(computation.vm.block.header.timestamp)
computation.stack.push(computation.vm_state.timestamp)


def number(computation):
computation.stack.push(computation.vm.block.header.block_number)
computation.stack.push(computation.vm_state.block_number)


def difficulty(computation):
computation.stack.push(computation.vm.block.header.difficulty)
computation.stack.push(computation.vm_state.difficulty)


def gaslimit(computation):
computation.stack.push(computation.vm.block.header.gas_limit)
computation.stack.push(computation.vm_state.gas_limit)
8 changes: 4 additions & 4 deletions evm/logic/call.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def __call__(self, computation):
computation.gas_meter.consume_gas(child_msg_gas_fee, reason=self.mnemonic)

# Pre-call checks
with computation.vm.state_db(read_only=True) as state_db:
with computation.vm_state.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 @@ -83,7 +83,7 @@ def __call__(self, computation):
computation.gas_meter.return_gas(child_msg_gas)
computation.stack.push(0)
else:
with computation.vm.state_db(read_only=True) as state_db:
with computation.vm_state.state_db(read_only=True) as state_db:
if code_address:
code = state_db.get_code(code_address)
else:
Expand Down Expand Up @@ -125,7 +125,7 @@ def __call__(self, computation):

class Call(BaseCall):
def compute_msg_extra_gas(self, computation, gas, to, value):
with computation.vm.state_db(read_only=True) as state_db:
with computation.vm_state.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
Expand Down Expand Up @@ -282,7 +282,7 @@ def compute_eip150_msg_gas(computation, gas, extra_gas, value, mnemonic, callsti
#
class CallEIP161(CallEIP150):
def compute_msg_extra_gas(self, computation, gas, to, value):
with computation.vm.state_db(read_only=True) as state_db:
with computation.vm_state.state_db(read_only=True) as state_db:
account_is_dead = (
not state_db.account_exists(to) or
state_db.account_is_empty(to)
Expand Down
6 changes: 3 additions & 3 deletions evm/logic/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

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

Expand Down Expand Up @@ -111,7 +111,7 @@ def gasprice(computation):

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

computation.stack.push(code_size)
Expand All @@ -135,7 +135,7 @@ def extcodecopy(computation):
reason='EXTCODECOPY: word gas cost',
)

with computation.vm.state_db(read_only=True) as state_db:
with computation.vm_state.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
6 changes: 3 additions & 3 deletions evm/logic/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
def sstore(computation):
slot, value = computation.stack.pop(num_items=2, type_hint=constants.UINT256)

with computation.vm.state_db(read_only=True) as state_db:
with computation.vm_state.state_db(read_only=True) as state_db:
current_value = state_db.get_storage(
address=computation.msg.storage_address,
slot=slot,
Expand Down Expand Up @@ -43,7 +43,7 @@ def sstore(computation):
if gas_refund:
computation.gas_meter.refund_gas(gas_refund)

with computation.vm.state_db() as state_db:
with computation.vm_state.state_db() as state_db:
state_db.set_storage(
address=computation.msg.storage_address,
slot=slot,
Expand All @@ -54,7 +54,7 @@ def sstore(computation):
def sload(computation):
slot = computation.stack.pop(type_hint=constants.UINT256)

with computation.vm.state_db(read_only=True) as state_db:
with computation.vm_state.state_db(read_only=True) as state_db:
value = state_db.get_storage(
address=computation.msg.storage_address,
slot=slot,
Expand Down
Loading

0 comments on commit 362266c

Please sign in to comment.