Пример #1
0
    def _update_header_chain_gaps(cls,
                                  db: DatabaseAPI,
                                  persisting_header: BlockHeaderAPI,
                                  base_gaps: ChainGaps = None) -> GapInfo:
        # The only reason we overwrite this here is to be able to detect when the HeaderDB
        # de-canonicalizes an uncle that should cause us to re-open a block gap.
        gap_change, gaps = super()._update_header_chain_gaps(
            db, persisting_header, base_gaps)

        if gap_change is not GapChange.NoChange or persisting_header.block_number == 0:
            return gap_change, gaps

        # We have written a header for which block number we've already had a header.
        # This might be a sign of a de-canonicalized uncle.
        current_gaps = cls._get_chain_gaps(db)
        if not is_block_number_in_gap(persisting_header.block_number,
                                      current_gaps):
            # ChainDB believes we have that block. If the header has changed, we need to re-open
            # a gap for the corresponding block.
            old_canonical_header = cls._get_canonical_block_header_by_number(
                db, persisting_header.block_number)
            if old_canonical_header != persisting_header:
                updated_gaps = reopen_gap(persisting_header.block_number,
                                          current_gaps)
                db.set(SchemaV1.make_chain_gaps_lookup_key(),
                       rlp.encode(updated_gaps, sedes=chain_gaps))

        return gap_change, gaps
Пример #2
0
def test_gap_continuity(changes):
    MAX_BLOCK_NUM = 21

    # method to get all the block numbers that are in a gap right now
    _all_missing = compose(
        set, partial(_all_gap_numbers, highest_block_number=MAX_BLOCK_NUM))

    @to_set
    def _all_inserted(chain_gaps):
        """List all the inserted headers, the block numbers not in gaps"""
        missing = _all_missing(chain_gaps)
        for block_num in range(MAX_BLOCK_NUM + 1):
            if block_num not in missing:
                yield block_num

    chain_gaps = GENESIS_CHAIN_GAPS

    for do_insert, block_num in changes:
        starts_inserted = _all_inserted(chain_gaps)
        starts_missing = _all_missing(chain_gaps)

        if do_insert:
            to_insert = block_num
            _, chain_gaps = fill_gap(to_insert, chain_gaps)
            assert not is_block_number_in_gap(to_insert, chain_gaps)

            # Make sure that at most this one block number was filled
            finished_inserted = _all_inserted(chain_gaps)
            assert to_insert in finished_inserted
            new_inserts = finished_inserted - starts_inserted
            if block_num in starts_inserted:
                assert new_inserts == set()
            else:
                assert new_inserts == {block_num}

            # Make sure that no new gaps were created
            finished_missing = _all_missing(chain_gaps)
            assert to_insert not in finished_missing
            new_missing = finished_missing - starts_missing
            assert new_missing == set()
        else:
            to_remove = block_num
            # Note that removing a header is inserting a gap
            chain_gaps = reopen_gap(to_remove, chain_gaps)
            assert is_block_number_in_gap(to_remove, chain_gaps)

            # Make sure that no gaps were filled
            finished_inserted = _all_inserted(chain_gaps)
            new_inserts = finished_inserted - starts_inserted
            assert new_inserts == set()

            # Make sure that at most this one block number gap was reopened
            finished_missing = _all_missing(chain_gaps)
            new_missing = finished_missing - starts_missing
            if block_num in starts_missing:
                assert new_missing == set()
            else:
                assert new_missing == {block_num}

        _validate_gap_invariants(chain_gaps)
Пример #3
0
    def _decanonicalize_descendant_orphans(
            cls,
            db: DatabaseAPI,
            header: BlockHeaderAPI,
            checkpoints: Tuple[Hash32, ...]) -> None:

        # Determine if any children need to be de-canonicalized because they are not children of
        #   the new chain head
        new_gaps = starting_gaps = cls._get_header_chain_gaps(db)

        child_number = BlockNumber(header.block_number + 1)
        try:
            child = cls._get_canonical_block_header_by_number(db, child_number)
        except HeaderNotFound:
            # There is no canonical block here
            next_invalid_child = None
        else:
            if child.parent_hash != header.hash:
                if child.hash in checkpoints:
                    raise CheckpointsMustBeCanonical(
                        f"Trying to decanonicalize {child} while making {header} the chain tip"
                    )
                else:
                    next_invalid_child = child
            else:
                next_invalid_child = None

        while next_invalid_child:
            # decanonicalize, and add gap for tracking
            db.delete(SchemaV1.make_block_number_to_hash_lookup_key(child_number))
            new_gaps = reopen_gap(child_number, new_gaps)

            # find next child
            child_number = BlockNumber(child_number + 1)
            try:
                # All contiguous children must now be made invalid
                next_invalid_child = cls._get_canonical_block_header_by_number(db, child_number)
            except HeaderNotFound:
                # Found the end of this streak of canonical blocks
                break
            else:
                if next_invalid_child.hash in checkpoints:
                    raise CheckpointsMustBeCanonical(
                        f"Trying to decanonicalize {next_invalid_child} while making {header} the"
                        " chain tip"
                    )

        if new_gaps != starting_gaps:
            db.set(
                SchemaV1.make_header_chain_gaps_lookup_key(),
                rlp.encode(new_gaps, sedes=chain_gaps)
            )
Пример #4
0
    def _decanonicalize_single(cls, db: DatabaseAPI, block_num: BlockNumber,
                               base_gaps: ChainGaps) -> ChainGaps:
        """
        A single block number was found to no longer be canonical. At doc-time,
        this only happens because it does not link up with a checkpoint header.
        So de-canonicalize this block number and insert a gap in the tracked
        chain gaps.
        """

        db.delete(SchemaV1.make_block_number_to_hash_lookup_key(block_num))

        new_gaps = reopen_gap(block_num, base_gaps)
        if new_gaps != base_gaps:
            db.set(SchemaV1.make_header_chain_gaps_lookup_key(),
                   rlp.encode(new_gaps, sedes=chain_gaps))
        return new_gaps