Skip to content

Commit dab298e

Browse files
authored
cleanup from changes to import_block (#1200)
* cleanup from changes to import_block * Rename orphaned to old
1 parent 9b80f45 commit dab298e

File tree

10 files changed

+197
-159
lines changed

10 files changed

+197
-159
lines changed

eth/chains/base.py

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ def estimate_gas(
269269
def import_block(self,
270270
block: BaseBlock,
271271
perform_validation: bool=True,
272-
) -> Tuple[Tuple[BaseBlock, ...], Tuple[BaseBlock, ...]]:
272+
) -> Tuple[BaseBlock, Tuple[BaseBlock, ...], Tuple[BaseBlock, ...]]:
273273
raise NotImplementedError("Chain classes must implement this method")
274274

275275
#
@@ -591,9 +591,13 @@ def estimate_gas(
591591
def import_block(self,
592592
block: BaseBlock,
593593
perform_validation: bool=True
594-
) -> Tuple[Tuple[BaseBlock, ...], Tuple[BaseBlock, ...]]:
594+
) -> Tuple[BaseBlock, Tuple[BaseBlock, ...], Tuple[BaseBlock, ...]]:
595595
"""
596-
Imports a complete block and returns new_blocks and orphaned_canonical_blocks.
596+
Imports a complete block and returns a 3-tuple
597+
598+
- the imported block
599+
- a tuple of blocks which are now part of the canonical chain.
600+
- a tuple of blocks which are were canonical and now are no longer canonical.
597601
"""
598602

599603
try:
@@ -617,8 +621,8 @@ def import_block(self,
617621
self.validate_block(imported_block)
618622

619623
(
620-
new_canonical_header_hashes,
621-
orphaned_header_hashes,
624+
new_canonical_hashes,
625+
old_canonical_hashes,
622626
) = self.chaindb.persist_block(imported_block)
623627

624628
self.logger.debug(
@@ -627,15 +631,18 @@ def import_block(self,
627631
encode_hex(imported_block.hash),
628632
)
629633

630-
new_blocks = tuple(
631-
self.get_block_by_hash(header_hash) for header_hash in new_canonical_header_hashes)
632-
orphaned_canonical_blocks = tuple(
633-
self.get_block_by_hash(header_hash) for header_hash in orphaned_header_hashes)
634-
# add imported block to new block when no chain re-org
635-
if not new_blocks:
636-
new_blocks = (imported_block, )
634+
new_canonical_blocks = tuple(
635+
self.get_block_by_hash(header_hash)
636+
for header_hash
637+
in new_canonical_hashes
638+
)
639+
old_canonical_blocks = tuple(
640+
self.get_block_by_hash(header_hash)
641+
for header_hash
642+
in old_canonical_hashes
643+
)
637644

638-
return new_blocks, orphaned_canonical_blocks
645+
return imported_block, new_canonical_blocks, old_canonical_blocks
639646

640647
#
641648
# Validation API
@@ -804,12 +811,12 @@ def apply_transaction(self, transaction):
804811
def import_block(self,
805812
block: BaseBlock,
806813
perform_validation: bool=True
807-
) -> Tuple[Tuple[BaseBlock, ...], Tuple[BaseBlock, ...]]:
808-
new_canonical_blocks, orphaned_canonical_blocks = super().import_block(
814+
) -> Tuple[BaseBlock, Tuple[BaseBlock, ...], Tuple[BaseBlock, ...]]:
815+
imported_block, new_canonical_blocks, old_canonical_blocks = super().import_block(
809816
block, perform_validation)
810817

811818
self.header = self.ensure_header()
812-
return new_canonical_blocks, orphaned_canonical_blocks
819+
return imported_block, new_canonical_blocks, old_canonical_blocks
813820

814821
def mine_block(self, *args: Any, **kwargs: Any) -> BaseBlock:
815822
"""
@@ -834,10 +841,12 @@ def get_vm(self, at_header: BlockHeader=None) -> 'BaseVM':
834841
# This class is a work in progress; its main purpose is to define the API of an asyncio-compatible
835842
# Chain implementation.
836843
class AsyncChain(Chain):
844+
# TODO: this really belongs in the `trinity` module.
837845

838846
async def coro_import_block(self,
839847
block: BlockHeader,
840-
perform_validation: bool=True) -> BaseBlock:
848+
perform_validation: bool=True,
849+
) -> Tuple[BaseBlock, Tuple[BaseBlock, ...], Tuple[BaseBlock, ...]]:
841850
raise NotImplementedError()
842851

843852
async def coro_validate_chain(

eth/db/chain.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -220,19 +220,19 @@ def persist_header(self,
220220
except CanonicalHeadNotFound:
221221
(
222222
new_canonical_headers,
223-
orphaned_canonical_headers
223+
old_canonical_headers
224224
) = self._set_as_canonical_chain_head(header)
225225
else:
226226
if score > head_score:
227227
(
228228
new_canonical_headers,
229-
orphaned_canonical_headers
229+
old_canonical_headers
230230
) = self._set_as_canonical_chain_head(header)
231231
else:
232232
new_canonical_headers = tuple()
233-
orphaned_canonical_headers = tuple()
233+
old_canonical_headers = tuple()
234234

235-
return new_canonical_headers, orphaned_canonical_headers
235+
return new_canonical_headers, old_canonical_headers
236236

237237
# TODO: update this to take a `hash` rather than a full header object.
238238
def _set_as_canonical_chain_head(self,
@@ -248,7 +248,7 @@ def _set_as_canonical_chain_head(self,
248248
header.hash))
249249

250250
new_canonical_headers = tuple(reversed(self._find_new_ancestors(header)))
251-
orphaned_canonical_headers = []
251+
old_canonical_headers = []
252252
# remove transaction lookups for blocks that are no longer canonical
253253
for h in new_canonical_headers:
254254
try:
@@ -257,17 +257,17 @@ def _set_as_canonical_chain_head(self,
257257
# no old block, and no more possible
258258
break
259259
else:
260-
orphaned_header = self.get_block_header_by_hash(old_hash)
261-
orphaned_canonical_headers.append(orphaned_header)
262-
for transaction_hash in self.get_block_transaction_hashes(orphaned_header):
260+
old_header = self.get_block_header_by_hash(old_hash)
261+
old_canonical_headers.append(old_header)
262+
for transaction_hash in self.get_block_transaction_hashes(old_header):
263263
self._remove_transaction_from_canonical_chain(transaction_hash)
264264

265265
for h in new_canonical_headers:
266266
self._add_block_number_to_hash_lookup(h)
267267

268268
self.db.set(SchemaV1.make_canonical_head_hash_lookup_key(), header.hash)
269269

270-
return new_canonical_headers, tuple(orphaned_canonical_headers)
270+
return new_canonical_headers, tuple(old_canonical_headers)
271271

272272
#
273273
# Block API
@@ -280,7 +280,7 @@ def persist_block(self,
280280
281281
Assumes all block transactions have been persisted already.
282282
'''
283-
new_canonical_headers, orphaned_canonical_headers = self.persist_header(block.header)
283+
new_canonical_headers, old_canonical_headers = self.persist_header(block.header)
284284

285285
for header in new_canonical_headers:
286286
if header.hash == block.hash:
@@ -302,11 +302,11 @@ def persist_block(self,
302302
raise ValidationError(
303303
"Block's uncles_hash (%s) does not match actual uncles' hash (%s)",
304304
block.header.uncles_hash, uncles_hash)
305-
new_canonical_header_hashes = tuple(header.hash for header in new_canonical_headers)
306-
orphaned_canonical_header_hashes = tuple(
307-
header.hash for header in orphaned_canonical_headers)
305+
new_canonical_hashes = tuple(header.hash for header in new_canonical_headers)
306+
old_canonical_hashes = tuple(
307+
header.hash for header in old_canonical_headers)
308308

309-
return new_canonical_header_hashes, orphaned_canonical_header_hashes
309+
return new_canonical_hashes, old_canonical_hashes
310310

311311
def persist_uncles(self, uncles: Tuple[BlockHeader]) -> Hash32:
312312
"""

eth/db/header.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -178,15 +178,19 @@ def persist_header(self,
178178
try:
179179
head_score = self.get_score(self.get_canonical_head().hash)
180180
except CanonicalHeadNotFound:
181-
new_canonical_headers, old_headers = self._set_as_canonical_chain_head(header.hash)
181+
new_canonical_headers, old_canonical_headers = self._set_as_canonical_chain_head(
182+
header.hash,
183+
)
182184
else:
183185
if score > head_score:
184-
new_canonical_headers, old_headers = self._set_as_canonical_chain_head(header.hash)
186+
new_canonical_headers, old_canonical_headers = self._set_as_canonical_chain_head(
187+
header.hash
188+
)
185189
else:
186190
new_canonical_headers = tuple()
187-
old_headers = tuple()
191+
old_canonical_headers = tuple()
188192

189-
return new_canonical_headers, old_headers
193+
return new_canonical_headers, old_canonical_headers
190194

191195
def _set_as_canonical_chain_head(self, block_hash: Hash32
192196
) -> Tuple[Tuple[BlockHeader, ...], Tuple[BlockHeader, ...]]:
@@ -204,24 +208,24 @@ def _set_as_canonical_chain_head(self, block_hash: Hash32
204208
)
205209

206210
new_canonical_headers = tuple(reversed(self._find_new_ancestors(header)))
207-
orphaned_headers = []
211+
old_canonical_headers = []
208212

209213
for h in new_canonical_headers:
210214
try:
211-
orphaned_hash = self.get_canonical_block_hash(h.block_number)
215+
old_canonical_hash = self.get_canonical_block_hash(h.block_number)
212216
except HeaderNotFound:
213-
# no orphaned block, and no more possible
217+
# no old_canonical block, and no more possible
214218
break
215219
else:
216-
orphaned_header = self.get_block_header_by_hash(orphaned_hash)
217-
orphaned_headers.append(orphaned_header)
220+
old_canonical_header = self.get_block_header_by_hash(old_canonical_hash)
221+
old_canonical_headers.append(old_canonical_header)
218222

219223
for h in new_canonical_headers:
220224
self._add_block_number_to_hash_lookup(h)
221225

222226
self.db.set(SchemaV1.make_canonical_head_hash_lookup_key(), header.hash)
223227

224-
return new_canonical_headers, tuple(orphaned_headers)
228+
return new_canonical_headers, tuple(old_canonical_headers)
225229

226230
@to_tuple
227231
def _find_new_ancestors(self, header: BlockHeader) -> Iterable[BlockHeader]:

eth/tools/fixture_tests.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -641,8 +641,7 @@ def apply_fixture_block_to_chain(block_fixture, chain):
641641

642642
block = rlp.decode(block_fixture['rlp'], sedes=block_class)
643643

644-
imported_blocks, _ = chain.import_block(block)
645-
mined_block = imported_blocks[-1]
644+
mined_block, _, _ = chain.import_block(block)
646645

647646
rlp_encoded_mined_block = rlp.encode(mined_block, sedes=block_class)
648647

scripts/benchmark/checks/import_empty_blocks.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,7 @@ def import_empty_blocks(self, chain: Chain, number_blocks: int) -> int:
4747

4848
total_gas_used = 0
4949
for _ in range(1, number_blocks + 1):
50-
imported_blocks, _ = chain.import_block(chain.get_vm().block, False)
51-
block = imported_blocks[-1]
50+
block, _, _ = chain.import_block(chain.get_vm().block, False)
5251

5352
total_gas_used = total_gas_used + block.header.gas_used
5453
logging.debug(format_block(block))

tests/core/chain-object/test_chain.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@ def tx(chain, funded_address, funded_address_private_key):
4141
@pytest.mark.xfail(reason="modification to initial allocation made the block fixture invalid")
4242
def test_import_block_validation(valid_chain, funded_address, funded_address_initial_balance):
4343
block = rlp.decode(valid_block_rlp, sedes=FrontierBlock)
44-
imported_blocks, _ = valid_chain.import_block(block)
45-
imported_block = imported_blocks[-1]
44+
imported_block, _, _ = valid_chain.import_block(block)
4645

4746
assert len(imported_block.transactions) == 1
4847
tx = imported_block.transactions[0]
@@ -66,8 +65,7 @@ def test_import_block(chain, tx):
6665
new_block, receipts, computations = chain.build_block_with_transactions([tx])
6766
assert computations[0].is_success
6867

69-
imported_blocks, _ = chain.import_block(new_block)
70-
block = imported_blocks[-1]
68+
block, _, _ = chain.import_block(new_block)
7169

7270
assert block.transactions == (tx,)
7371
assert chain.get_block_by_hash(block.hash) == block
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import pytest
2+
3+
from eth import constants
4+
from eth.chains.base import MiningChain
5+
from eth.chains.tester import MAINNET_VMS
6+
from eth.db.backends.memory import MemoryDB
7+
8+
9+
VM_CLASSES = tuple(
10+
VMClass
11+
for _, VMClass
12+
in MAINNET_VMS.items()
13+
)
14+
15+
16+
@pytest.fixture(params=VM_CLASSES)
17+
def chain(request, genesis_state):
18+
VMClass = request.param.configure(validate_seal=lambda block: None)
19+
20+
class ChainForTest(MiningChain):
21+
vm_configuration = ((0, VMClass),)
22+
network_id = 1337
23+
24+
genesis_params = {
25+
'block_number': constants.GENESIS_BLOCK_NUMBER,
26+
'difficulty': constants.GENESIS_DIFFICULTY,
27+
'gas_limit': constants.GENESIS_GAS_LIMIT,
28+
}
29+
chain = ChainForTest.from_genesis(MemoryDB(), genesis_params, genesis_state)
30+
return chain
31+
32+
33+
ZERO_ADDRESS = b'\x00' * 20
34+
35+
36+
def test_import_block_with_reorg(chain, funded_address_private_key):
37+
# mine 3 "common blocks"
38+
block_0 = chain.get_canonical_block_by_number(0)
39+
assert block_0.number == 0
40+
block_1 = chain.mine_block()
41+
assert block_1.number == 1
42+
block_2 = chain.mine_block()
43+
assert block_2.number == 2
44+
block_3 = chain.mine_block()
45+
assert block_3.number == 3
46+
47+
# make a duplicate chain with no shared state
48+
fork_db = MemoryDB(chain.chaindb.db.kv_store.copy())
49+
fork_chain = type(chain)(fork_db, chain.header)
50+
51+
# sanity check to verify that the two chains are the same.
52+
assert chain.header == fork_chain.header
53+
54+
assert chain.header.block_number == 4
55+
assert fork_chain.header.block_number == 4
56+
57+
assert fork_chain.get_canonical_head() == chain.get_canonical_head()
58+
59+
assert fork_chain.get_canonical_block_by_number(0) == block_0
60+
assert fork_chain.get_canonical_block_by_number(1) == block_1
61+
assert fork_chain.get_canonical_block_by_number(2) == block_2
62+
assert fork_chain.get_canonical_block_by_number(3) == block_3
63+
64+
# now cause the fork chain to diverge from the main chain
65+
tx = fork_chain.create_unsigned_transaction(
66+
nonce=0,
67+
gas_price=1,
68+
gas=21000,
69+
to=ZERO_ADDRESS,
70+
value=0,
71+
data=b'',
72+
)
73+
fork_chain.apply_transaction(tx.as_signed_transaction(funded_address_private_key))
74+
75+
# Mine 3 blocks, ensuring that the difficulty of the main chain remains
76+
# equal or greater than the fork chain
77+
block_4 = chain.mine_block()
78+
f_block_4 = fork_chain.mine_block()
79+
assert f_block_4 != block_4
80+
assert block_4.number == 4
81+
assert f_block_4.number == 4
82+
assert f_block_4.header.difficulty <= block_4.header.difficulty
83+
84+
f_block_5, block_5 = fork_chain.mine_block(), chain.mine_block()
85+
assert f_block_5 != block_5
86+
assert block_5.number == 5
87+
assert f_block_5.number == 5
88+
assert f_block_5.header.difficulty <= block_5.header.difficulty
89+
90+
f_block_6, block_6 = fork_chain.mine_block(), chain.mine_block()
91+
assert f_block_6 != block_6
92+
assert block_6.number == 6
93+
assert f_block_6.number == 6
94+
assert f_block_6.header.difficulty <= block_6.header.difficulty
95+
96+
# now mine the 7th block which will outpace the main chain difficulty.
97+
f_block_7 = fork_chain.mine_block()
98+
99+
pre_fork_chain_head = chain.header
100+
101+
# now we proceed to import the blocks from the fork chain into the main
102+
# chain. Blocks 4, 5, and 6 should import resulting in no re-organization.
103+
for block in (f_block_4, f_block_5, f_block_6):
104+
_, new_canonical_blocks, old_canonical_blocks = chain.import_block(block)
105+
assert not new_canonical_blocks
106+
assert not old_canonical_blocks
107+
assert chain.header == pre_fork_chain_head
108+
109+
# now we import block 7 from the fork chain. This should cause a re-org.
110+
_, new_canonical_blocks, old_canonical_blocks = chain.import_block(f_block_7)
111+
assert new_canonical_blocks == (f_block_4, f_block_5, f_block_6, f_block_7)
112+
assert old_canonical_blocks == (block_4, block_5, block_6)
113+
114+
assert chain.get_canonical_head() == f_block_7.header

0 commit comments

Comments
 (0)