Skip to content

Commit a7b731d

Browse files
committed
Lazily evaluate vm.previous_hashes
1 parent 6657dfe commit a7b731d

File tree

4 files changed

+35
-15
lines changed

4 files changed

+35
-15
lines changed

eth/rlp/headers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import time
22
from typing import (
3+
Iterable,
34
Optional,
4-
Tuple,
55
Union,
66
overload,
77
)
@@ -203,7 +203,7 @@ def from_parent(cls,
203203
return header
204204

205205
def create_execution_context(
206-
self, prev_hashes: Tuple[Hash32, ...]) -> ExecutionContext:
206+
self, prev_hashes: Iterable[Hash32]) -> ExecutionContext:
207207

208208
return ExecutionContext(
209209
coinbase=self.coinbase,

eth/vm/base.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
abstractmethod,
55
)
66
import contextlib
7-
import functools
7+
import itertools
88
import logging
99
from typing import (
1010
Any,
@@ -28,7 +28,6 @@
2828
)
2929

3030
from eth_utils import (
31-
to_tuple,
3231
ValidationError,
3332
)
3433

@@ -258,7 +257,6 @@ def get_nephew_reward(cls) -> int:
258257

259258
@classmethod
260259
@abstractmethod
261-
@to_tuple
262260
def get_prev_hashes(cls,
263261
last_block_hash: Hash32,
264262
chaindb: BaseChainDB) -> Optional[Iterable[Hash32]]:
@@ -691,8 +689,6 @@ def get_block_class(cls) -> Type[BaseBlock]:
691689
return cls.block_class
692690

693691
@classmethod
694-
@functools.lru_cache(maxsize=32)
695-
@to_tuple
696692
def get_prev_hashes(cls,
697693
last_block_hash: Hash32,
698694
chaindb: BaseChainDB) -> Optional[Iterable[Hash32]]:
@@ -709,7 +705,7 @@ def get_prev_hashes(cls,
709705
break
710706

711707
@property
712-
def previous_hashes(self) -> Optional[Tuple[Hash32, ...]]:
708+
def previous_hashes(self) -> Optional[Iterable[Hash32]]:
713709
"""
714710
Convenience API for accessing the previous 255 block hashes.
715711
"""
@@ -919,7 +915,7 @@ def get_state_class(cls) -> Type[BaseState]:
919915
def state_in_temp_block(self) -> Iterator[BaseState]:
920916
header = self.block.header
921917
temp_block = self.generate_block_from_parent_header_and_coinbase(header, header.coinbase)
922-
prev_hashes = (header.hash, ) + self.previous_hashes
918+
prev_hashes = itertools.chain((header.hash, ), self.previous_hashes)
923919

924920
state = self.get_state_class()(
925921
db=self.chaindb.db,

eth/vm/execution_context.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
from typing import Tuple
1+
from typing import (
2+
Iterable,
3+
)
24

35
from eth_typing import (
46
Address,
@@ -22,7 +24,7 @@ def __init__(
2224
block_number: int,
2325
difficulty: int,
2426
gas_limit: int,
25-
prev_hashes: Tuple[Hash32, ...]) -> None:
27+
prev_hashes: Iterable[Hash32]) -> None:
2628
self._coinbase = coinbase
2729
self._timestamp = timestamp
2830
self._block_number = block_number
@@ -51,5 +53,5 @@ def gas_limit(self) -> int:
5153
return self._gas_limit
5254

5355
@property
54-
def prev_hashes(self) -> Tuple[Hash32, ...]:
56+
def prev_hashes(self) -> Iterable[Hash32]:
5557
return self._prev_hashes

eth/vm/state.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
abstractmethod
44
)
55
import contextlib
6+
import itertools
67
import logging
78
from typing import ( # noqa: F401
89
cast,
@@ -84,11 +85,14 @@ class for vm execution.
8485
transaction_context_class = None # type: Type[BaseTransactionContext]
8586
account_db_class = None # type: Type[BaseAccountDB]
8687
transaction_executor = None # type: Type[BaseTransactionExecutor]
88+
prev_hashes_generator_backup = None
8789

8890
def __init__(self, db: BaseDB, execution_context: ExecutionContext, state_root: bytes) -> None:
8991
self._db = db
9092
self.execution_context = execution_context
9193
self.account_db = self.get_account_db_class()(self._db, state_root)
94+
if execution_context:
95+
self.prev_hashes_generator_backup = execution_context.prev_hashes
9296

9397
#
9498
# Logging
@@ -197,16 +201,34 @@ def get_ancestor_hash(self, block_number: int) -> Hash32:
197201
Return the empty bytestring ``b''`` if the block number is outside of the
198202
range of available block numbers (typically the last 255 blocks).
199203
"""
204+
# Create a copy of the generator so that traversing it doesn't change the
205+
# original generator
206+
self.prev_hashes_generator_backup, temp_prev_hashes_generator = itertools.tee(
207+
self.prev_hashes_generator_backup
208+
)
209+
200210
ancestor_depth = self.block_number - block_number - 1
201211
is_ancestor_depth_out_of_range = (
202212
ancestor_depth >= MAX_PREV_HEADER_DEPTH or
203213
ancestor_depth < 0 or
204-
ancestor_depth >= len(self.execution_context.prev_hashes)
214+
block_number < 0
205215
)
206216
if is_ancestor_depth_out_of_range:
207217
return Hash32(b'')
208-
ancestor_hash = self.execution_context.prev_hashes[ancestor_depth]
209-
return ancestor_hash
218+
219+
current_ancestor_depth = 0
220+
current_ancestor_hash = None
221+
while True:
222+
try:
223+
current_ancestor_hash = next(temp_prev_hashes_generator)
224+
except StopIteration:
225+
# Ancestor with specified depth not present
226+
return Hash32(b'')
227+
if current_ancestor_depth == ancestor_depth:
228+
return current_ancestor_hash
229+
current_ancestor_depth += 1
230+
231+
raise Exception("Invariant: unreachable code path")
210232

211233
#
212234
# Computation

0 commit comments

Comments
 (0)