def from_headerdb(cls, headerdb: HeaderDB, **kwargs: Any) -> ETHHandshakeParams: head = headerdb.get_canonical_head() head_score = headerdb.get_score(head.hash) genesis = headerdb.get_canonical_block_header_by_number( GENESIS_BLOCK_NUMBER) return cls(head_hash=head.hash, genesis_hash=genesis.hash, total_difficulty=head_score, **kwargs)
def from_headerdb(cls, headerdb: HeaderDB, **kwargs: Any) -> ETHHandshakeParams: head = headerdb.get_canonical_head() head_score = headerdb.get_score(head.hash) # TODO: https://github.com/ethereum/py-evm/issues/1847 genesis = headerdb.get_canonical_block_header_by_number( BlockNumber(GENESIS_BLOCK_NUMBER)) return cls(head_hash=head.hash, genesis_hash=genesis.hash, total_difficulty=head_score, **kwargs)
def test_header_chain_initialization_header_already_persisted(base_db, genesis_header): headerdb = HeaderDB(base_db) headerdb.persist_header(genesis_header) # sanity check that the header is persisted assert_headers_eq(headerdb.get_canonical_head(), genesis_header) header_chain = HeaderChain.from_genesis_header(base_db, genesis_header) head = header_chain.get_canonical_head() assert_headers_eq(head, genesis_header)
def test_true_chain_canonicalized_regardless_of_order(genesis_header, data): class ActionEnum(enum.Enum): PERSIST_CHAIN = enum.auto() CHECKPOINT = enum.auto() # Setup headerdb = HeaderDB(AtomicDB()) CHAIN_A, CHAIN_B = 0, 1 chain_length = data.draw(st.integers(min_value=2, max_value=20)) headerdb.persist_header(genesis_header) chain_a_headers = mk_header_chain(genesis_header, length=chain_length) headers = [ # chain A: eventually canonical; the only source for checkpoints chain_a_headers, # chain B: non-canonical mk_header_chain(genesis_header, length=chain_length), ] missing_indices = [ set(range(chain_length)), # chain A set(range(chain_length)), # chain B ] @to_tuple def _get_valid_extensions(of_missing_indices): """ Find the headers that have available parents, and so are valid to persist """ # For each chain for chain_index, header_indices in enumerate(of_missing_indices): # yield each header number that has a parent that's not missing # Sorting header_indices is important for hypothesis consistency for header_index in sorted(header_indices): if header_index == 0 or header_index - 1 not in header_indices: yield (chain_index, header_index) last_checkpoint = None while len(missing_indices[CHAIN_A]): _validate_consecutive_canonical_links(headerdb, chain_length) _validate_gap_invariants(headerdb.get_header_chain_gaps()) action = data.draw(st.sampled_from(ActionEnum)) if action == ActionEnum.CHECKPOINT: checkpoint_index = data.draw( st.sampled_from(list(sorted(missing_indices[CHAIN_A])))) checkpoint = chain_a_headers[checkpoint_index] checkpoint_score = get_score( genesis_header, chain_a_headers[:checkpoint_index + 1]) headerdb.persist_checkpoint_header(checkpoint, checkpoint_score) # keep track of whether any checkpoints were added, so we eventually # guarantee A as canonical last_checkpoint = checkpoint_index missing_indices[CHAIN_A].discard(checkpoint_index) elif action == ActionEnum.PERSIST_CHAIN: # choose the series of headers to add valid_extensions = _get_valid_extensions(missing_indices) chain_idx, next_chain_start = data.draw( st.sampled_from(valid_extensions)) next_chain_len = data.draw( st.integers(min_value=1, max_value=chain_length)) chain_range_end = next_chain_start + next_chain_len next_headers = headers[chain_idx][next_chain_start:chain_range_end] # persist them to chain try: headerdb.persist_header_chain(next_headers) except CheckpointsMustBeCanonical: assert chain_idx == CHAIN_B, "Only chain B should fail to decanonize checkpoint" # Persist failed, so retry different action continue # mark persisted headers as not missing for inserted_index in range(next_chain_start, chain_range_end): missing_indices[chain_idx].discard(inserted_index) _validate_consecutive_canonical_links(headerdb, chain_length) _validate_gap_invariants(headerdb.get_header_chain_gaps()) if last_checkpoint is None: # Force canonicalization of chain a by adding a bonus header at the end of # all the chain A headers. (subsequent_header, ) = mk_header_chain(chain_a_headers[-1], length=1) headerdb.persist_checkpoint_header( subsequent_header, get_score(genesis_header, chain_a_headers + (subsequent_header, )), ) assert_is_canonical_chain(headerdb, chain_a_headers + (subsequent_header, )) else: if headerdb.get_canonical_head() != chain_a_headers[-1]: child_headers = mk_header_chain(chain_a_headers[-1], length=1) headerdb.persist_header_chain(child_headers) assert_is_canonical_chain(headerdb, chain_a_headers + child_headers) else: assert_is_canonical_chain(headerdb, chain_a_headers) _validate_consecutive_canonical_links(headerdb, chain_length) _validate_gap_invariants(headerdb.get_header_chain_gaps())