Exemple #1
0
    def validate_chain(cls,
                       root: BlockHeader,
                       descendants: Tuple[BlockHeader, ...],
                       seal_check_random_sample_rate: int = 1) -> None:
        """
        Validate that all of the descendents are valid, given that the root header is valid.

        By default, check the seal validity (Proof-of-Work on Ethereum 1.x mainnet) of all headers.
        This can be expensive. Instead, check a random sample of seals using
        seal_check_random_sample_rate.
        """

        all_indices = range(len(descendants))
        if seal_check_random_sample_rate == 1:
            indices_to_check_seal = set(all_indices)
        else:
            sample_size = len(all_indices) // seal_check_random_sample_rate
            indices_to_check_seal = set(random.sample(all_indices,
                                                      sample_size))

        header_pairs = sliding_window(2, concatv([root], descendants))

        for index, (parent, child) in enumerate(header_pairs):
            if child.parent_hash != parent.hash:
                raise ValidationError(
                    "Invalid header chain; {} has parent {}, but expected {}".
                    format(child, child.parent_hash, parent.hash))
            should_check_seal = index in indices_to_check_seal
            vm_class = cls.get_vm_class_for_block_number(child.block_number)
            vm_class.validate_header(child,
                                     parent,
                                     check_seal=should_check_seal)
Exemple #2
0
    def validate_chain(
            self,
            root: BlockHeaderAPI,
            descendants: Tuple[BlockHeaderAPI, ...],
            seal_check_random_sample_rate: int = 1) -> None:

        all_indices = range(len(descendants))
        if seal_check_random_sample_rate == 1:
            indices_to_check_seal = set(all_indices)
        elif seal_check_random_sample_rate == 0:
            indices_to_check_seal = set()
        else:
            sample_size = len(all_indices) // seal_check_random_sample_rate
            indices_to_check_seal = set(random.sample(all_indices, sample_size))

        header_pairs = sliding_window(2, concatv([root], descendants))

        for index, (parent, child) in enumerate(header_pairs):
            if child.parent_hash != parent.hash:
                raise ValidationError(
                    f"Invalid header chain; {child} has parent {encode_hex(child.parent_hash)},"
                    f" but expected {encode_hex(parent.hash)}"
                )
            should_check_seal = index in indices_to_check_seal
            vm = self.get_vm(child)
            try:
                vm.validate_header(child, parent)
            except ValidationError as exc:
                raise ValidationError(
                    f"{child} is not a valid child of {parent}: {exc}"
                ) from exc

            if should_check_seal:
                vm.validate_seal(child)
Exemple #3
0
    def serialize(self, value: TSerializable) -> bytes:
        self._validate_serializable(value)

        if not len(value):
            return b""

        pairs = self._get_item_sedes_pairs(value)  # slow
        element_sedes = tuple(sedes for element, sedes in pairs)

        has_fixed_size_section_length_cache = hasattr(
            value, "_fixed_size_section_length_cache")
        if has_fixed_size_section_length_cache:
            if value._fixed_size_section_length_cache is None:
                fixed_size_section_length = _compute_fixed_size_section_length(
                    element_sedes)
                value._fixed_size_section_length_cache = fixed_size_section_length
            else:
                fixed_size_section_length = value._fixed_size_section_length_cache
        else:
            fixed_size_section_length = _compute_fixed_size_section_length(
                element_sedes)

        variable_size_section_parts = tuple(
            sedes.serialize(item)  # slow
            for item, sedes in pairs if not sedes.is_fixed_sized)

        if variable_size_section_parts:
            offsets = tuple(
                accumulate(
                    operator.add,
                    map(len, variable_size_section_parts[:-1]),
                    fixed_size_section_length,
                ))
        else:
            offsets = ()

        offsets_iter = iter(offsets)

        fixed_size_section_parts = tuple(
            sedes.serialize(item)  # slow
            if sedes.is_fixed_sized else encode_offset(next(offsets_iter))
            for item, sedes in pairs)

        try:
            next(offsets_iter)
        except StopIteration:
            pass
        else:
            raise DeserializationError(
                "Did not consume all offsets while decoding value")

        return b"".join(
            concatv(fixed_size_section_parts, variable_size_section_parts))
Exemple #4
0
    async def _fill_in_gap(
            self, peer: TChainPeer, pairs: Tuple[Tuple[BlockHeader, ...], ...],
            gap_index: int) -> Tuple[Tuple[BlockHeader, ...], ...]:
        """
        Fill headers into the specified gap in the middle of the header pairs using supplied peer.
        Validate the returned segment of headers against the surrounding header pairs.
        :param peer: to make the request to
        :param pairs: header pairs with gaps in between
        :param gap_index: 0-indexed gap number that should be filled in
        :return: segments just like the pairs input, but with one long segment that was filled in

        For example, if four pairs were input, and the gap_index set to 1, then the
        returned value would have three segments, like:

        ::

            segment 0: (parent, child)
            --formerly gap 0--
            segment 1: (parent, child, ... all headers between ..., parent, child)
            --formerly gap 2--
            segment 2: (parent, child)
        """
        # validate gap value
        if not (0 <= gap_index < len(pairs) - 1):
            raise ValidationError(
                f"Tried to fill gap #{gap_index} in skeleton, with only {len(pairs) - 1} gaps"
            )

        # find the headers just before and after the gap
        gap_parent = pairs[gap_index][-1]
        gap_child = pairs[gap_index + 1][0]
        # request the gap's headers from the skeleton peer
        start_num = gap_parent.block_number + 1
        max_headers = gap_child.block_number - gap_parent.block_number - 1
        gap_headers = await self._fetch_headers_from(peer,
                                                     start_num,
                                                     max_headers,
                                                     skip=0)

        if len(gap_headers) == 0:
            self.logger.warning(
                "Skeleton %s could not fill header gap with headers at %s",
                peer,
                start_num,
            )
            raise ValidationError(
                f"Skeleton {peer} could not return headers at {start_num}")

        # validate the filled headers
        filled_gap_children = tuple(concatv(gap_headers, pairs[gap_index + 1]))
        try:
            await self.wait(
                self._chain.coro_validate_chain(
                    gap_parent,
                    filled_gap_children,
                    SEAL_CHECK_RANDOM_SAMPLE_RATE,
                ))
        except ValidationError:
            self.logger.warning(
                "%s returned an invalid gap for index %s, with pairs %s, filler %s",
                peer,
                gap_index,
                pairs,
                gap_headers,
            )
            raise
        else:
            return tuple(
                concatv(
                    # include all the leading pairs, through the pair that marks the start of the gap
                    pairs[:gap_index + 1],
                    # include the gap that has been filled in, which includes the pair after the gap
                    # must convert to tuple of tuple of headers to match the other types
                    (
                        filled_gap_children, ),
                    # skip the pair following the gap, include all the following pairs
                    pairs[gap_index + 2:],
                ))