Beispiel #1
0
def serialize_blobs(blobs: Iterable[bytes]) -> Iterator[bytes]:
    """Serialize a sequence of blobs and return a collation body."""
    for i, blob in enumerate(blobs):
        if len(blob) == 0:
            raise ValidationError("Cannot serialize blob {} of length 0".format(i))
        if len(blob) > MAX_BLOB_SIZE:
            raise ValidationError("Cannot serialize blob {} of size {}".format(i, len(blob)))

        for blob_index in range(0, len(blob), CHUNK_DATA_SIZE):
            remaining_blob_bytes = len(blob) - blob_index

            if remaining_blob_bytes <= CHUNK_DATA_SIZE:
                length_bits = remaining_blob_bytes
            else:
                length_bits = 0

            flag_bits = 0  # TODO: second parameter? blobs as tuple `(flag, blob)`?
            indicator_byte = int_to_big_endian(length_bits | (flag_bits << 5))
            if len(indicator_byte) != 1:
                raise Exception("Invariant: indicator is not a single byte")

            yield indicator_byte
            yield blob[blob_index:blob_index + CHUNK_DATA_SIZE]

        # end of range(0, N, k) is given by the largest multiple of k smaller than N, i.e.,
        # (ceil(N / k) - 1) * k where ceil(N / k) == -(-N // k)
        last_blob_index = (-(-len(blob) // CHUNK_DATA_SIZE) - 1) * CHUNK_DATA_SIZE
        if last_blob_index != blob_index:
            raise Exception("Invariant: last blob index calculation failed")
        chunk_filler = b"\x00" * (CHUNK_DATA_SIZE - (len(blob) - last_blob_index))
        yield chunk_filler
Beispiel #2
0
    def validate_header(cls, header, previous_header, check_seal=True):
        # ignore mypy warnings, because super's validate_header is defined by mixing w/ other class
        super().validate_header(header, previous_header,
                                check_seal)  # type: ignore

        # The special extra_data is set on the ten headers starting at the fork
        extra_data_block_nums = range(cls.dao_fork_block_number,
                                      cls.dao_fork_block_number + 10)

        if header.block_number in extra_data_block_nums:
            if cls.support_dao_fork and header.extra_data != DAO_FORK_MAINNET_EXTRA_DATA:
                raise ValidationError(
                    "Block {!r} must have extra data {} not {} when supporting DAO fork"
                    .format(
                        header,
                        encode_hex(DAO_FORK_MAINNET_EXTRA_DATA),
                        encode_hex(header.extra_data),
                    ))
            elif not cls.support_dao_fork and header.extra_data == DAO_FORK_MAINNET_EXTRA_DATA:
                raise ValidationError(
                    "Block {!r} must not have extra data {} when declining the DAO fork"
                    .format(
                        header,
                        encode_hex(DAO_FORK_MAINNET_EXTRA_DATA),
                    ))
Beispiel #3
0
    def __init__(self,
                 state_root: bytes,
                 gas_used: int,
                 logs: Iterable[Log],
                 bloom: int=None) -> None:

        if bloom is None:
            bloomables = itertools.chain.from_iterable(log.bloomables for log in logs)
            bloom = int(BloomFilter.from_iterable(bloomables))

        super().__init__(
            state_root=state_root,
            gas_used=gas_used,
            bloom=bloom,
            logs=logs,
        )

        for log_idx, log in enumerate(self.logs):
            if log.address not in self.bloom_filter:
                raise ValidationError(
                    "The address from the log entry at position {0} is not "
                    "present in the provided bloom filter.".format(log_idx)
                )
            for topic_idx, topic in enumerate(log.topics):
                if int32.serialize(topic) not in self.bloom_filter:
                    raise ValidationError(
                        "The topic at position {0} from the log entry at "
                        "position {1} is not present in the provided bloom "
                        "filter.".format(topic_idx, log_idx)
                    )
Beispiel #4
0
def validate_gas_limit(gas_limit, parent_gas_limit):
    if gas_limit < GAS_LIMIT_MINIMUM:
        raise ValidationError("Gas limit {0} is below minimum {1}".format(
            gas_limit, GAS_LIMIT_MINIMUM))
    if gas_limit > GAS_LIMIT_MAXIMUM:
        raise ValidationError("Gas limit {0} is above maximum {1}".format(
            gas_limit, GAS_LIMIT_MAXIMUM))
    diff = gas_limit - parent_gas_limit
    if diff > (parent_gas_limit // GAS_LIMIT_ADJUSTMENT_FACTOR):
        raise ValidationError(
            "Gas limit {0} difference to parent {1} is too big {2}".format(
                gas_limit, parent_gas_limit, diff))
Beispiel #5
0
 def validate_gaslimit(self, header: BlockHeader) -> None:
     """
     Validate the gas limit on the given header.
     """
     parent_header = self.get_block_header_by_hash(header.parent_hash)
     low_bound, high_bound = compute_gas_limit_bounds(parent_header)
     if header.gas_limit < low_bound:
         raise ValidationError(
             "The gas limit on block {0} is too low: {1}. It must be at least {2}".format(
                 encode_hex(header.hash), header.gas_limit, low_bound))
     elif header.gas_limit > high_bound:
         raise ValidationError(
             "The gas limit on block {0} is too high: {1}. It must be at most {2}".format(
                 encode_hex(header.hash), header.gas_limit, high_bound))
Beispiel #6
0
def validate_word(value, title="Value"):
    if not isinstance(value, bytes):
        raise ValidationError(
            "{title} is not a valid word. Must be of bytes type: Got: {0}".format(
                type(value),
                title=title,
            )
        )
    elif not len(value) == 32:
        raise ValidationError(
            "{title} is not a valid word. Must be 32 bytes in length: Got: {0}".format(
                len(value),
                title=title,
            )
        )
Beispiel #7
0
def validate_header_response(start_number: int, count: int, reverse: bool,
                             headers: List[BlockHeader]) -> None:
    if len(headers) != count:
        raise ValidationError("Expected {} headers, got: {}".format(
            count, headers))
    if reverse:
        headers = list(reversed(headers))
    if headers[0].block_number != start_number:
        raise ValidationError(
            "Expected {} as first header's number, got: {}".format(
                start_number, headers[0].block_number))
    for i, header in enumerate(headers):
        if header.block_number != start_number + i:
            raise ValidationError(
                "Header numbers are not sequential: {}".format(headers))
Beispiel #8
0
    def _decode_header_to_dict(
            cls, encoded_header: bytes) -> Iterator[Tuple[str, Any]]:
        if len(encoded_header) != cls.smc_encoded_size:
            raise ValidationError(
                "Expected encoded header to be of size: {0}. Got size {1} instead.\n- {2}"
                .format(
                    cls.smc_encoded_size,
                    len(encoded_header),
                    encode_hex(encoded_header),
                ))

        start_indices = accumulate(lambda i, field: i + field[2],
                                   cls.fields_with_sizes, 0)
        field_bounds = sliding_window(2, start_indices)
        for byte_range, field in zip(field_bounds, cls._meta.fields):
            start_index, end_index = byte_range
            field_name, field_type = field

            field_bytes = encoded_header[start_index:end_index]
            if field_type == rlp.sedes.big_endian_int:
                # remove the leading zeros, to avoid `not minimal length` error in deserialization
                formatted_field_bytes = field_bytes.lstrip(b'\x00')
            elif field_type == address:
                formatted_field_bytes = field_bytes[-20:]
            else:
                formatted_field_bytes = field_bytes
            yield field_name, field_type.deserialize(formatted_field_bytes)
Beispiel #9
0
def calc_merkle_tree(items: Sequence[Hashable]) -> MerkleTree:
    """Calculate the Merkle tree corresponding to a list of items."""
    if len(items) == 0:
        raise ValidationError("No items given")
    n_layers = math.log2(len(items)) + 1
    if not n_layers.is_integer():
        raise ValidationError("Item number is not a power of two")
    n_layers = int(n_layers)

    leaves = tuple(keccak(item) for item in items)
    tree = cast(MerkleTree,
                tuple(take(n_layers, iterate(_hash_layer, leaves)))[::-1])
    if len(tree[0]) != 1:
        raise Exception("Invariant: There must only be one root")

    return tree
Beispiel #10
0
 def _validate_changeset(self, changeset_id: uuid.UUID) -> None:
     """
     Checks to be sure the changeset is known by the journal
     """
     if not self.journal.has_changeset(changeset_id):
         raise ValidationError("Changeset not found in journal: {0}".format(
             str(changeset_id)))
Beispiel #11
0
    def persist_block(self, block: 'BaseBlock') -> None:
        '''
        Persist the given block's header and uncles.

        Assumes all block transactions have been persisted already.
        '''
        new_canonical_headers = self.persist_header(block.header)

        for header in new_canonical_headers:
            if header.hash == block.hash:
                # Most of the time this is called to persist a block whose parent is the current
                # head, so we optimize for that and read the tx hashes from the block itself. This
                # is specially important during a fast sync.
                tx_hashes = [tx.hash for tx in block.transactions]
            else:
                tx_hashes = self.get_block_transaction_hashes(header)

            for index, transaction_hash in enumerate(tx_hashes):
                self._add_transaction_to_canonical_chain(
                    transaction_hash, header, index)

        if block.uncles:
            uncles_hash = self.persist_uncles(block.uncles)
        else:
            uncles_hash = EMPTY_UNCLE_HASH
        if uncles_hash != block.header.uncles_hash:
            raise ValidationError(
                "Block's uncles_hash (%s) does not match actual uncles' hash (%s)",
                block.header.uncles_hash, uncles_hash)
Beispiel #12
0
    def import_block(self, block: BaseBlock, perform_validation: bool=True) -> BaseBlock:
        """
        Imports a complete block.
        """
        try:
            parent_header = self.get_block_header_by_hash(block.header.parent_hash)
        except HeaderNotFound:
            raise ValidationError(
                "Attempt to import block #{}.  Cannot import block {} before importing "
                "its parent block at {}".format(
                    block.number,
                    block.hash,
                    block.header.parent_hash,
                )
            )

        base_header_for_import = self.create_header_from_parent(parent_header)
        imported_block = self.get_vm(base_header_for_import).import_block(block)

        # Validate the imported block.
        if perform_validation:
            ensure_imported_block_unchanged(imported_block, block)
            self.validate_block(imported_block)

        self.chaindb.persist_block(imported_block)
        self.logger.debug(
            'IMPORTED_BLOCK: number %s | hash %s',
            imported_block.number,
            encode_hex(imported_block.hash),
        )
        return imported_block
Beispiel #13
0
    def apply_all_transactions(self, transactions, base_header):
        """
        Determine the results of applying all transactions to the base header.
        This does *not* update the current block or header of the VM.

        :param transactions: an iterable of all transactions to apply
        :param base_header: the starting header to apply transactions to
        :return: the final header, the receipts of each transaction, and the computations
        """
        if base_header.block_number != self.block.number:
            raise ValidationError(
                "This VM instance must only work on block #{}, "
                "but the target header has block #{}".format(
                    self.block.number,
                    base_header.block_number,
                ))

        receipts = []
        computations = []
        previous_header = base_header
        result_header = base_header

        for transaction in transactions:
            result_header, receipt, computation = self.apply_transaction(
                previous_header,
                transaction,
            )

            previous_header = result_header
            receipts.append(receipt)
            computations.append(computation)

        return result_header, receipts, computations
Beispiel #14
0
def validate_point(x: int, y: int) -> Tuple[bn128.FQ, bn128.FQ, bn128.FQ]:
    FQ = bn128.FQ

    if x >= bn128.field_modulus:
        raise ValidationError("Point x value is greater than field modulus")
    elif y >= bn128.field_modulus:
        raise ValidationError("Point y value is greater than field modulus")

    if (x, y) != (0, 0):
        p1 = (FQ(x), FQ(y), FQ(1))
        if not bn128.is_on_curve(p1, bn128.b):
            raise ValidationError("Point is not on the curve")
    else:
        p1 = (FQ(1), FQ(1), FQ(0))

    return p1
Beispiel #15
0
def validate_frontier_transaction(account_db, transaction):
    gas_cost = transaction.gas * transaction.gas_price
    sender_balance = account_db.get_balance(transaction.sender)

    if sender_balance < gas_cost:
        raise ValidationError(
            "Sender account balance cannot afford txn gas: `{0}`".format(transaction.sender)
        )

    total_cost = transaction.value + gas_cost

    if sender_balance < total_cost:
        raise ValidationError("Sender account balance cannot afford txn")

    if account_db.get_nonce(transaction.sender) != transaction.nonce:
        raise ValidationError("Invalid transaction nonce")
Beispiel #16
0
def validate_transaction_signature(transaction: BaseTransaction) -> None:
    if is_eip_155_signed_transaction(transaction):
        v = extract_signature_v(transaction.v)
    else:
        v = transaction.v

    canonical_v = v - 27
    vrs = (canonical_v, transaction.r, transaction.s)
    signature = keys.Signature(vrs=vrs)
    message = transaction.get_message_for_signing()
    try:
        public_key = signature.recover_public_key_from_msg(message)
    except BadSignature as e:
        raise ValidationError("Bad Signature: {0}".format(str(e)))

    if not signature.verify_msg(message, public_key):
        raise ValidationError("Invalid Signature")
Beispiel #17
0
 def validate(self) -> None:
     """
     Hook called during instantiation to ensure that all transaction
     parameters pass validation rules.
     """
     if self.gas < self.intrinsic_gas:
         raise ValidationError("Insufficient gas")
     self.check_signature_validity()
Beispiel #18
0
    def validate_block(self, block):
        """
        Validate the the given block.
        """
        if not isinstance(block, self.get_block_class()):
            raise ValidationError(
                "This vm ({0!r}) is not equipped to validate a block of type {1!r}"
                .format(
                    self,
                    block,
                ))

        if block.is_genesis:
            validate_length_lte(block.header.extra_data,
                                32,
                                title="BlockHeader.extra_data")
        else:
            parent_header = get_parent_header(block.header, self.chaindb)
            self.validate_header(block.header, parent_header)

        tx_root_hash, _ = make_trie_root_and_nodes(block.transactions)
        if tx_root_hash != block.header.transaction_root:
            raise ValidationError(
                "Block's transaction_root ({0}) does not match expected value: {1}"
                .format(block.header.transaction_root, tx_root_hash))

        if len(block.uncles) > MAX_UNCLES:
            raise ValidationError(
                "Blocks may have a maximum of {0} uncles.  Found "
                "{1}.".format(MAX_UNCLES, len(block.uncles)))

        if not self.chaindb.exists(block.header.state_root):
            raise ValidationError("`state_root` was not found in the db.\n"
                                  "- state_root: {0}".format(
                                      block.header.state_root, ))
        local_uncle_hash = keccak(rlp.encode(block.uncles))
        if local_uncle_hash != block.header.uncles_hash:
            raise ValidationError(
                "`uncles_hash` and block `uncles` do not match.\n"
                " - num_uncles       : {0}\n"
                " - block uncle_hash : {1}\n"
                " - header uncle_hash: {2}".format(
                    len(block.uncles),
                    local_uncle_hash,
                    block.header.uncle_hash,
                ))
Beispiel #19
0
def validate_stack_item(value):
    if isinstance(value, bytes) and len(value) <= 32:
        return
    elif isinstance(value, int) and 0 <= value <= UINT_256_MAX:
        return
    raise ValidationError(
        "Invalid Stack Item: Must be either a length 32 byte "
        "string or a 256 bit integer. Got {0}".format(value)
    )
Beispiel #20
0
def validate_frontier_transaction_against_header(_vm, base_header, transaction):
    if base_header.gas_used + transaction.gas > base_header.gas_limit:
        raise ValidationError(
            "Transaction exceeds gas limit: using {}, bringing total to {}, but limit is {}".format(
                transaction.gas,
                base_header.gas_used + transaction.gas,
                base_header.gas_limit,
            )
        )
Beispiel #21
0
 def validate_chain(self, chain: Tuple[BlockHeader, ...]) -> None:
     parent = self.chaindb.get_block_header_by_hash(chain[0].parent_hash)
     for header in chain:
         if header.parent_hash != parent.hash:
             raise ValidationError(
                 "Invalid header chain; {} has parent {}, but expected {}".format(
                     header, header.parent_hash, parent.hash))
         vm_class = self.get_vm_class_for_block_number(header.block_number)
         vm_class.validate_header(header, parent)
         parent = header
Beispiel #22
0
 def complete_work(self, work_completed: Union[int, float]) -> None:
     if self._last_start is None:
         raise ValidationError(
             "Cannot end the ThroughputTracker without starting it")
     time_elapsed = time.perf_counter() - self._last_start
     last_throughput = work_completed / time_elapsed
     self._throughput = (self._throughput *
                         (1 - self._alpha)) + (last_throughput *
                                               self._alpha)
     self._last_start = None
Beispiel #23
0
 def __init__(self, default_throughput: float,
              smoothing_factor: float) -> None:
     self._last_start: float = None
     self._throughput = default_throughput
     if 0 < smoothing_factor < 1:
         self._alpha = smoothing_factor
     else:
         raise ValidationError(
             "Smoothing factor of ThroughputTracker must be between 0 and 1"
         )
Beispiel #24
0
def check_shard_id(
        shard: Shard, header_or_collation: Union[CollationHeader,
                                                 Collation]) -> None:
    if header_or_collation.shard_id != shard.shard_id:
        raise ValidationError(
            "Header or collation belongs to shard {} instead of shard {}".
            format(
                header_or_collation.shard_id,
                shard.shard_id,
            ))
Beispiel #25
0
def validate_header_params_for_configuration(header_params):
    extra_fields = set(header_params.keys()).difference(ALLOWED_HEADER_FIELDS)
    if extra_fields:
        raise ValidationError(
            "The `configure_header` method may only be used with the fields ({0}). "
            "The provided fields ({1}) are not supported".format(
                ", ".join(tuple(sorted(ALLOWED_HEADER_FIELDS))),
                ", ".join(tuple(sorted(extra_fields))),
            )
        )
Beispiel #26
0
def validate_length(value, length, title="Value"):
    if not len(value) == length:
        raise ValidationError(
            "{title} must be of length {0}.  Got {1} of length {2}".format(
                length,
                value,
                len(value),
                title=title,
            )
        )
Beispiel #27
0
def validate_lte(value, maximum, title="Value"):
    if value > maximum:
        raise ValidationError(
            "{title} {0} is not less than or equal to {1}".format(
                value,
                maximum,
                title=title,
            )
        )
    validate_is_integer(value, title=title)
Beispiel #28
0
def validate_gte(value, minimum, title="Value"):
    if value < minimum:
        raise ValidationError(
            "{title} {0} is not greater than or equal to {1}".format(
                value,
                minimum,
                title=title,
            )
        )
    validate_is_integer(value)
Beispiel #29
0
def validate_length_lte(value, maximum_length, title="Value"):
    if len(value) > maximum_length:
        raise ValidationError(
            "{title} must be of length less than or equal to {0}.  "
            "Got {1} of length {2}".format(
                maximum_length,
                value,
                len(value),
                title=title,
            )
        )
Beispiel #30
0
    def validate_uncle(cls, block, uncle, uncle_parent):
        """
        Validate the given uncle in the context of the given block.
        """
        if uncle.block_number >= block.number:
            raise ValidationError(
                "Uncle number ({0}) is higher than block number ({1})".format(
                    uncle.block_number, block.number))

        if uncle.block_number != uncle_parent.block_number + 1:
            raise ValidationError(
                "Uncle number ({0}) is not one above ancestor's number ({1})".
                format(uncle.block_number, uncle_parent.block_number))
        if uncle.timestamp < uncle_parent.timestamp:
            raise ValidationError(
                "Uncle timestamp ({0}) is before ancestor's timestamp ({1})".
                format(uncle.timestamp, uncle_parent.timestamp))
        if uncle.gas_used > uncle.gas_limit:
            raise ValidationError(
                "Uncle's gas usage ({0}) is above the limit ({1})".format(
                    uncle.gas_used, uncle.gas_limit))