Skip to content

Commit 92e575b

Browse files
committed
Improve performance when extending the chain head
Also make an unrelated change which makes circleci happy, it uses an updated version of eth-utils.
1 parent 10df2a6 commit 92e575b

File tree

4 files changed

+61
-46
lines changed

4 files changed

+61
-46
lines changed

eth/db/chain.py

+11-24
Original file line numberDiff line numberDiff line change
@@ -90,21 +90,11 @@ def get_block_uncles(self, uncles_hash: Hash32) -> Tuple[BlockHeaderAPI, ...]:
9090
return tuple(rlp.decode(encoded_uncles, sedes=rlp.sedes.CountableList(BlockHeader)))
9191

9292
@classmethod
93-
def _set_as_canonical_chain_head(
94-
cls,
95-
db: DatabaseAPI,
96-
block_hash: Hash32,
97-
genesis_parent_hash: Hash32,
98-
) -> Tuple[Tuple[BlockHeaderAPI, ...], Tuple[BlockHeaderAPI, ...]]:
99-
try:
100-
header = cls._get_block_header_by_hash(db, block_hash)
101-
except HeaderNotFound:
102-
raise ValueError(
103-
f"Cannot use unknown block hash as canonical head: {header.hash}"
104-
)
105-
106-
new_canonical_headers = tuple(reversed(
107-
cls._find_new_ancestors(db, header, genesis_parent_hash)))
93+
def _decanonicalize_old_headers(
94+
cls,
95+
db: DatabaseAPI,
96+
new_canonical_headers: Tuple[BlockHeaderAPI, ...]
97+
) -> Tuple[BlockHeaderAPI, ...]:
10898
old_canonical_headers = []
10999

110100
# remove transaction lookups for blocks that are no longer canonical
@@ -118,19 +108,16 @@ def _set_as_canonical_chain_head(
118108
old_header = cls._get_block_header_by_hash(db, old_hash)
119109
old_canonical_headers.append(old_header)
120110
try:
121-
for transaction_hash in cls._get_block_transaction_hashes(db, old_header):
111+
transaction_hashes = cls._get_block_transaction_hashes(db, old_header)
112+
for transaction_hash in transaction_hashes:
122113
cls._remove_transaction_from_canonical_chain(db, transaction_hash)
123114
except MissingTrieNode:
124-
# If the transactions were never stored for the (now) non-canonical chain,
125-
# then you don't need to remove them from the canonical chain lookup.
115+
# If the transactions were never stored for the (now) non-canonical
116+
# chain, then you don't need to remove them from the canonical chain
117+
# lookup.
126118
pass
127119

128-
for h in new_canonical_headers:
129-
cls._add_block_number_to_hash_lookup(db, h)
130-
131-
db.set(SchemaV1.make_canonical_head_hash_lookup_key(), header.hash)
132-
133-
return new_canonical_headers, tuple(old_canonical_headers)
120+
return tuple(old_canonical_headers)
134121

135122
#
136123
# Block API

eth/db/header.py

+46-20
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import functools
2-
from typing import Iterable, Tuple
2+
from typing import cast, Iterable, Tuple
33

44
import rlp
55

@@ -83,11 +83,15 @@ def get_canonical_head(self) -> BlockHeaderAPI:
8383

8484
@classmethod
8585
def _get_canonical_head(cls, db: DatabaseAPI) -> BlockHeaderAPI:
86+
canonical_head_hash = cls._get_canonical_head_hash(db)
87+
return cls._get_block_header_by_hash(db, canonical_head_hash)
88+
89+
@classmethod
90+
def _get_canonical_head_hash(cls, db: DatabaseAPI) -> Hash32:
8691
try:
87-
canonical_head_hash = db[SchemaV1.make_canonical_head_hash_lookup_key()]
92+
return Hash32(db[SchemaV1.make_canonical_head_hash_lookup_key()])
8893
except KeyError:
8994
raise CanonicalHeadNotFound("No canonical head set for this chain")
90-
return cls._get_block_header_by_hash(db, Hash32(canonical_head_hash))
9195

9296
#
9397
# Header API
@@ -173,7 +177,7 @@ def _persist_checkpoint_header(
173177
)
174178
previous_score = score - header.difficulty
175179
cls._set_hash_scores_to_db(db, header, previous_score)
176-
cls._set_as_canonical_chain_head(db, header.hash, header.parent_hash)
180+
cls._set_as_canonical_chain_head(db, header, header.parent_hash)
177181

178182
@classmethod
179183
def _persist_header_chain(
@@ -226,21 +230,21 @@ def _persist_header_chain(
226230
score = cls._set_hash_scores_to_db(db, curr_chain_head, score)
227231

228232
try:
229-
previous_canonical_head = cls._get_canonical_head(db).hash
233+
previous_canonical_head = cls._get_canonical_head_hash(db)
230234
head_score = cls._get_score(db, previous_canonical_head)
231235
except CanonicalHeadNotFound:
232-
return cls._set_as_canonical_chain_head(db, curr_chain_head.hash, genesis_parent_hash)
236+
return cls._set_as_canonical_chain_head(db, curr_chain_head, genesis_parent_hash)
233237

234238
if score > head_score:
235-
return cls._set_as_canonical_chain_head(db, curr_chain_head.hash, genesis_parent_hash)
239+
return cls._set_as_canonical_chain_head(db, curr_chain_head, genesis_parent_hash)
236240

237241
return tuple(), tuple()
238242

239243
@classmethod
240244
def _set_as_canonical_chain_head(
241245
cls,
242246
db: DatabaseAPI,
243-
block_hash: Hash32,
247+
header: BlockHeaderAPI,
244248
genesis_parent_hash: Hash32,
245249
) -> Tuple[Tuple[BlockHeaderAPI, ...], Tuple[BlockHeaderAPI, ...]]:
246250
"""
@@ -251,14 +255,41 @@ def _set_as_canonical_chain_head(
251255
are no longer in the canonical chain
252256
"""
253257
try:
254-
header = cls._get_block_header_by_hash(db, block_hash)
255-
except HeaderNotFound:
256-
raise ValueError(
257-
f"Cannot use unknown block hash as canonical head: {block_hash}"
258+
current_canonical_head = cls._get_canonical_head_hash(db)
259+
except CanonicalHeadNotFound:
260+
current_canonical_head = None
261+
262+
new_canonical_headers: Tuple[BlockHeaderAPI, ...]
263+
old_canonical_headers: Tuple[BlockHeaderAPI, ...]
264+
265+
if current_canonical_head and header.parent_hash == current_canonical_head:
266+
# the calls to _find_new_ancestors and _decanonicalize_old_headers are
267+
# relatively expensive, it's better to skip them in this case, where we're
268+
# extending the canonical chain by a header
269+
new_canonical_headers = (header,)
270+
old_canonical_headers = ()
271+
else:
272+
new_canonical_headers = cast(
273+
Tuple[BlockHeaderAPI, ...],
274+
tuple(reversed(cls._find_new_ancestors(db, header, genesis_parent_hash)))
275+
)
276+
old_canonical_headers = cls._decanonicalize_old_headers(
277+
db, new_canonical_headers
258278
)
259279

260-
new_canonical_headers = tuple(reversed(
261-
cls._find_new_ancestors(db, header, genesis_parent_hash)))
280+
for h in new_canonical_headers:
281+
cls._add_block_number_to_hash_lookup(db, h)
282+
283+
db.set(SchemaV1.make_canonical_head_hash_lookup_key(), header.hash)
284+
285+
return new_canonical_headers, old_canonical_headers
286+
287+
@classmethod
288+
def _decanonicalize_old_headers(
289+
cls,
290+
db: DatabaseAPI,
291+
new_canonical_headers: Tuple[BlockHeaderAPI, ...]
292+
) -> Tuple[BlockHeaderAPI, ...]:
262293
old_canonical_headers = []
263294

264295
for h in new_canonical_headers:
@@ -271,12 +302,7 @@ def _set_as_canonical_chain_head(
271302
old_canonical_header = cls._get_block_header_by_hash(db, old_canonical_hash)
272303
old_canonical_headers.append(old_canonical_header)
273304

274-
for h in new_canonical_headers:
275-
cls._add_block_number_to_hash_lookup(db, h)
276-
277-
db.set(SchemaV1.make_canonical_head_hash_lookup_key(), header.hash)
278-
279-
return new_canonical_headers, tuple(old_canonical_headers)
305+
return tuple(old_canonical_headers)
280306

281307
@classmethod
282308
@to_tuple

eth/tools/_utils/normalization.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ def normalize_to_address(value: AnyStr) -> Address:
128128
return CREATE_CONTRACT_ADDRESS
129129

130130

131-
robust_decode_hex = eth_utils.curried.hexstr_if_str(to_bytes) # type: ignore # https://github.com/ethereum/eth-utils/issues/156 # noqa: E501
131+
robust_decode_hex = eth_utils.curried.hexstr_if_str(to_bytes)
132132

133133

134134
#
@@ -247,7 +247,7 @@ def state_definition_to_dict(state_definition: GeneralState) -> AccountState:
247247
"nonce": normalize_int,
248248
"storage": normalize_storage
249249
}, required=[])),
250-
eth_utils.curried.apply_formatter_if( # type: ignore # https://github.com/ethereum/eth-utils/issues/156 # noqa: E501
250+
eth_utils.curried.apply_formatter_if(
251251
lambda s: isinstance(s, Iterable) and not isinstance(s, Mapping),
252252
state_definition_to_dict
253253
),

newsfragments/1891.performance.rst

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Improve performance when importing a header which is a child of the current canonical
2+
chain tip.

0 commit comments

Comments
 (0)