Skip to content

Commit

Permalink
New ImmutableSerializable with a memoizable .hash property
Browse files Browse the repository at this point in the history
  • Loading branch information
gsalgado committed Apr 12, 2018
1 parent 1fd7394 commit 28b9f69
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 14 deletions.
7 changes: 2 additions & 5 deletions evm/rlp/headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
ValidationError,
)

from evm.rlp.immutable import ImmutableSerializable
from evm.utils.hexadecimal import (
encode_hex,
)
Expand Down Expand Up @@ -58,7 +59,7 @@
)


class BlockHeader(rlp.Serializable):
class BlockHeader(ImmutableSerializable):
fields = [
('parent_hash', hash32),
('uncles_hash', hash32),
Expand Down Expand Up @@ -119,10 +120,6 @@ def __repr__(self) -> str:
encode_hex(self.hash)[2:10],
)

@property
def hash(self) -> bytes:
return keccak(rlp.encode(self))

@property
def mining_hash(self) -> bytes:
return keccak(
Expand Down
39 changes: 39 additions & 0 deletions evm/rlp/immutable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import functools

import rlp

from eth_utils import keccak


# TODO: Document, and explain that it must only be used on methods with no arguments as well as
# subclasses of ImmutableSerializable. Maybe instead of a decorator it should actually be a method
# on ImmutableSerializable?
def memoize(method):
attr_name = '_memoized_' + method.__name__

@functools.wraps(method)
def memoized_method(obj):
if not hasattr(obj, attr_name):
setattr(obj, attr_name, method(obj))
return getattr(obj, attr_name)

return memoized_method


class ImmutableSerializable(rlp.Serializable):

_hash = None

@classmethod
def deserialize(cls, serial, exclude=None, mutable=False, **kwargs):
obj = super().deserialize(serial, exclude=exclude, mutable=True, **kwargs)
obj._cached_rlp = rlp.codec.encode_raw(serial)
return rlp.sedes.make_immutable(obj)

@property
@memoize
def hash(self) -> bytes:
if self._cached_rlp:
return keccak(self._cached_rlp)
else:
return keccak(rlp.encode(self))
11 changes: 2 additions & 9 deletions evm/rlp/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,11 @@
binary,
)

from eth_utils import (
keccak,
)

from evm.exceptions import (
ValidationError,
)

from evm.rlp.immutable import ImmutableSerializable
from evm.rlp.sedes import (
address,
)
Expand All @@ -29,7 +26,7 @@
)


class BaseTransaction(rlp.Serializable, metaclass=ABCMeta):
class BaseTransaction(ImmutableSerializable, metaclass=ABCMeta):
fields = [
('nonce', big_endian_int),
('gas_price', big_endian_int),
Expand All @@ -46,10 +43,6 @@ class BaseTransaction(rlp.Serializable, metaclass=ABCMeta):
def from_base_transaction(cls, transaction: 'BaseTransaction') -> 'BaseTransaction':
return rlp.decode(rlp.encode(transaction), sedes=cls)

@property
def hash(self) -> bytes:
return keccak(rlp.encode(self))

@property
def sender(self) -> bytes:
"""
Expand Down
2 changes: 2 additions & 0 deletions evm/vm/forks/frontier/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
validate_canonical_address,
)

from evm.rlp.immutable import memoize
from evm.rlp.transactions import (
BaseTransaction,
BaseUnsignedTransaction,
Expand Down Expand Up @@ -65,6 +66,7 @@ def get_sender(self):
def get_intrinsic_gas(self):
return _get_frontier_intrinsic_gas(self.data)

@memoize
def get_message_for_signing(self):
return rlp.encode(FrontierUnsignedTransaction(
nonce=self.nonce,
Expand Down
2 changes: 2 additions & 0 deletions evm/vm/forks/homestead/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
GAS_TXDATANONZERO,
CREATE_CONTRACT_ADDRESS,
)
from evm.rlp.immutable import memoize
from evm.validation import (
validate_lt_secpk1n2,
)
Expand All @@ -29,6 +30,7 @@ def validate(self):
def get_intrinsic_gas(self):
return _get_homestead_intrinsic_gas(self)

@memoize
def get_message_for_signing(self):
return rlp.encode(HomesteadUnsignedTransaction(
nonce=self.nonce,
Expand Down
3 changes: 3 additions & 0 deletions evm/vm/forks/spurious_dragon/transactions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import rlp

from evm.rlp.immutable import memoize
from evm.vm.forks.homestead.transactions import (
HomesteadTransaction,
HomesteadUnsignedTransaction,
Expand All @@ -16,6 +17,8 @@


class SpuriousDragonTransaction(HomesteadTransaction):

@memoize
def get_message_for_signing(self):
if is_eip_155_signed_transaction(self):
txn_parts = rlp.decode(rlp.encode(self))
Expand Down

0 comments on commit 28b9f69

Please sign in to comment.