Skip to content

Commit

Permalink
continue work on blockchain tests
Browse files Browse the repository at this point in the history
  • Loading branch information
pipermerriam committed Jun 13, 2017
1 parent 6d06ab5 commit cc7ef8d
Show file tree
Hide file tree
Showing 15 changed files with 273 additions and 150 deletions.
33 changes: 33 additions & 0 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Block Workflow

The following workflows are supported for blocks.

## 1. Block Building

Incremental creation

1. Initialize Block - `Header.from_parent(...)`:
- `coinbase`
- `parent_hash`
- `difficulty`
- `block_number`
- `gas_limit`
- `timestamp`
2. Apply Transaction(s) - `Block.apply_transaction(...)`:
3. Mine Block - `Block.mine(...)`:
- `uncles_hash`
- `state_root`
- `transaction_root`
- `receipts_root`
- `bloom`
- `gas_used`
- `extra_data`
- `mix_hash`
- `nonce`


## 2. Block Ingestion

> (This is actually just a special case of use case #1.)
Full ingestion of a complete block.
4 changes: 2 additions & 2 deletions evm/logic/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ def log_XX(computation, topic_count):
if not topic_count:
topics = []
elif topic_count > 1:
topics = computation.stack.pop(num_items=topic_count, type_hint=constants.BYTES)
topics = computation.stack.pop(num_items=topic_count, type_hint=constants.UINT256)
else:
topics = [computation.stack.pop(num_items=topic_count, type_hint=constants.BYTES)]
topics = [computation.stack.pop(num_items=topic_count, type_hint=constants.UINT256)]

data_gas_cost = constants.GAS_LOGDATA * size
topic_gas_cost = constants.GAS_LOGTOPIC * topic_count
Expand Down
32 changes: 13 additions & 19 deletions evm/rlp/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,6 @@


class BaseBlock(rlp.Serializable):
@property
def is_sealed(self):
return not self.is_open


class BaseOpenBlock(BaseBlock):
is_open = True

transaction_class = None

@classmethod
Expand All @@ -18,14 +10,16 @@ def get_transaction_class(cls):
raise AttributeError("OpenBlock subclasses must declare a transaction_class")
return cls.transaction_class

sealed_block_class = None

@classmethod
def get_sealed_block_class(cls):
if cls.sealed_block_class is None:
raise AttributeError("OpenBlock subclasses must declare a sealed_block_class")
return cls.sealed_block_class


class BaseSealedBlock(BaseBlock):
is_open = False
def apply_transaction(self, evm, transaction):
"""
Applies the given transaction to the current block.
"""
raise NotImplementedError(
"The `Block.apply_transaction` method must be implemented by subclasses"
)

def mine(self, *args, **kwargs):
"""
Mines the block.
"""
raise NotImplementedError("The `Block.mine` method must be implemented by subclasses")
8 changes: 8 additions & 0 deletions evm/rlp/logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,11 @@ class Log(rlp.Serializable):

def __init__(self, address, topics, data):
super(Log, self).__init__(address, topics, data)

@property
def bloomables(self):
return (
self.address,
) + tuple(
int32.serialize(topic) for topic in self.topics
)
42 changes: 40 additions & 2 deletions evm/rlp/receipts.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import itertools

import rlp
from rlp.sedes import (
big_endian_int,
CountableList,
)

from eth_bloom import BloomFilter

from evm.exceptions import ValidationError

from .sedes import (
trie_root,
int256,
int32,
)

from .logs import Log
Expand All @@ -21,5 +28,36 @@ class Receipt(rlp.Serializable):
('logs', CountableList(Log))
]

def __init__(self, state_root, gas_used, logs, bloom):
super(Receipt, self).__init__(state_root, gas_used, logs, bloom)
def __init__(self, state_root, gas_used, logs, bloom=None):
if bloom is None:
bloomables = itertools.chain.from_iterable(log.bloomables for log in logs)
bloom = int(BloomFilter.from_iterable(bloomables))

super(Receipt, self).__init__(
state_root=state_root,
gas_used=gas_used,
bloom=bloom,
logs=logs,
)

for log_idx, log in enumerate(self.logs):
if log.address not in self.bloom_filter:
raise ValidationError(
"The address from the log entry at position {0} is not "
"present in the provided bloom filter.".format(log_idx)
)
for topic_idx, topic in enumerate(log.topics):
if int32.serialize(topic) not in self.bloom_filter:
raise ValidationError(
"The topic at position {0} from the log entry at "
"position {1} is not present in the provided bloom "
"filter.".format(topic_idx, log_idx)
)

@property
def bloom_filter(self):
return BloomFilter(self.bloom)

@bloom_filter.setter
def bloom_filter(self, value):
self.bloom = int(value)
54 changes: 27 additions & 27 deletions evm/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,64 +30,64 @@
)


class StateTrie(object):
trie = None
class HashTrie(object):
_trie = None

logger = logging.getLogger('evm.state.StateTrie')
logger = logging.getLogger('evm.state.HashTrie')

def __init__(self, trie):
self.trie = trie
self._trie = trie

def __setitem__(self, key, value):
self._set(key, value)

def _set(self, key, value):
# TODO: remove, for debug purposes.
self.trie[keccak(key)] = value
self._trie[keccak(key)] = value

def __getitem__(self, key):
return self.trie[keccak(key)]
return self._trie[keccak(key)]

def __delitem__(self, key):
del self.trie[keccak(key)]
del self._trie[keccak(key)]

def __contains__(self, key):
return keccak(key) in self.trie
return keccak(key) in self._trie

@property
def root_hash(self):
return self.trie.root_hash
return self._trie.root_hash

def snapshot(self):
return self.trie.snapshot()
return self._trie.snapshot()

def revert(self, snapshot):
return self.trie.revert(snapshot)
return self._trie.revert(snapshot)


class State(object):
"""
High level API around account storage.
"""
db = None
state = None
_trie = None

logger = logging.getLogger('evm.state.State')

def __init__(self, db, root_hash):
def __init__(self, db, root_hash=BLANK_ROOT_HASH):
self.db = db
self.state = StateTrie(Trie(db, root_hash))
self._trie = HashTrie(Trie(db, root_hash))

#
# Base API
#
@property
def state_root(self):
return self._trie.root_hash

def set_storage(self, address, slot, value):
validate_uint256(value)
validate_uint256(slot)
validate_canonical_address(address)

account = self._get_account(address)
storage = StateTrie(Trie(self.db, account.storage_root))
storage = HashTrie(Trie(self.db, account.storage_root))

slot_as_key = pad32(int_to_big_endian(slot))

Expand All @@ -105,7 +105,7 @@ def get_storage(self, address, slot):
validate_uint256(slot)

account = self._get_account(address)
storage = StateTrie(Trie(self.db, account.storage_root))
storage = HashTrie(Trie(self.db, account.storage_root))

slot_as_key = pad32(int_to_big_endian(slot))

Expand Down Expand Up @@ -181,11 +181,11 @@ def delete_code(self, address):
# Account Methods
#
def delete_account(self, address):
del self.state[address]
del self._trie[address]

def account_exists(self, address):
validate_canonical_address(address)
return bool(self.state[address])
return bool(self._trie[address])

def touch_account(self, address):
account = self._get_account(address)
Expand All @@ -199,21 +199,21 @@ def increment_nonce(self, address):
# Internal
#
def snapshot(self):
return self.state.snapshot()
return self._trie.snapshot()

def revert(self, snapshot):
return self.state.revert(snapshot)
return self._trie.revert(snapshot)

#
# Internal
#
def _get_account(self, address):
if address in self.state:
account = rlp.decode(self.state[address], sedes=Account)
if address in self._trie:
account = rlp.decode(self._trie[address], sedes=Account)
account._mutable = True
else:
account = Account()
return account

def _set_account(self, address, account):
self.state[address] = rlp.encode(account, sedes=Account)
self._trie[address] = rlp.encode(account, sedes=Account)
2 changes: 1 addition & 1 deletion evm/utils/fixture_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def normalize_logs(logs):
return [
{
'address': to_canonical_address(log_entry['address']),
'topics': [decode_hex(topic) for topic in log_entry['topics']],
'topics': [to_int(topic) for topic in log_entry['topics']],
'data': decode_hex(log_entry['data']),
'bloom': decode_hex(log_entry['bloom']),
} for log_entry in logs
Expand Down
11 changes: 11 additions & 0 deletions evm/vm/computation.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from evm.validation import (
validate_canonical_address,
validate_uint256,
validate_is_bytes,
)

from evm.utils.hexidecimal import (
Expand Down Expand Up @@ -197,6 +198,10 @@ def get_accounts_for_deletion(self):
)).items())

def add_log_entry(self, account, topics, data):
validate_canonical_address(account)
for topic in topics:
validate_uint256(topic)
validate_is_bytes(data)
self.log_entries.append((account, topics, data))

def get_log_entries(self):
Expand All @@ -214,6 +219,12 @@ def get_gas_refund(self):
else:
return self.gas_meter.gas_refunded + sum(c.get_gas_refund() for c in self.children)

def get_gas_used(self):
if self.error:
return self.msg.gas
else:
return self.msg.gas - self.gas_meter.gas_remaining

#
# Context Manager API
#
Expand Down
Loading

0 comments on commit cc7ef8d

Please sign in to comment.