Esempio n. 1
0
 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)
Esempio n. 2
0
 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)
Esempio n. 3
0
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)
Esempio n. 4
0
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())