Exemple #1
0
    def refund_gas(self, amount: int) -> None:
        if amount < 0:
            raise ValidationError("Gas refund amount must be positive")

        self.gas_refunded += amount

        self.logger.trace(
            'GAS REFUND: %s + %s -> %s',
            self.gas_refunded - amount,
            amount,
            self.gas_refunded,
        )
Exemple #2
0
    def validate_uncle(self, 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))
Exemple #3
0
def validate_frontier_transaction(vm_state, transaction):
    gas_cost = transaction.gas * transaction.gas_price
    with vm_state.state_db(read_only=True) as state_db:
        sender_balance = state_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 vm_state.gas_used + transaction.gas > vm_state.gas_limit:
        raise ValidationError("Transaction exceeds gas limit")

    with vm_state.state_db(read_only=True) as state_db:
        if state_db.get_nonce(transaction.sender) != transaction.nonce:
            raise ValidationError("Invalid transaction nonce")
Exemple #4
0
    async def _validate_header(self, header):
        if header.is_genesis:
            raise ValidationError("Peer sent a genesis header {} that we didn't ask for".format(
                header,
            ))
        else:
            async_header = self.headerdb.coro_get_block_header_by_hash(header.parent_hash)
            parent_header = await self.wait(async_header)

            VM = self.chain_class.get_vm_class_for_block_number(header.block_number)
            # TODO push validation into process pool executor
            VM.validate_header(header, parent_header)
Exemple #5
0
    def return_gas(self, amount: int) -> None:
        if amount < 0:
            raise ValidationError("Gas return amount must be positive")

        self.gas_remaining += amount

        self.logger.trace(
            'GAS RETURNED: %s + %s -> %s',
            self.gas_remaining - amount,
            amount,
            self.gas_remaining,
        )
Exemple #6
0
def check_pow(block_number, mining_hash, mix_hash, nonce, difficulty):
    validate_length(mix_hash, 32, title="Mix Hash")
    validate_length(mining_hash, 32, title="Mining Hash")
    validate_length(nonce, 8, title="POW Nonce")
    cache = get_cache(block_number)
    mining_output = hashimoto_light(
        block_number, cache, mining_hash, big_endian_to_int(nonce))
    if mining_output[b'mix digest'] != mix_hash:
        raise ValidationError("mix hash mismatch; {0} != {1}".format(
            encode_hex(mining_output[b'mix digest']), encode_hex(mix_hash)))
    result = big_endian_to_int(mining_output[b'result'])
    validate_lte(result, 2**256 // difficulty, title="POW Difficulty")
Exemple #7
0
def _process_point(data_buffer, exponent):
    x1, y1, x2_i, x2_r, y2_i, y2_r = _extract_point(data_buffer)
    p1 = validate_point(x1, y1)

    for v in (x2_i, x2_r, y2_i, y2_r):
        if v >= bn128.field_modulus:
            raise ValidationError("value greater than field modulus")

    fq2_x = bn128.FQ2([x2_r, x2_i])
    fq2_y = bn128.FQ2([y2_r, y2_i])

    if (fq2_x, fq2_y) != (bn128.FQ2.zero(), bn128.FQ2.zero()):
        p2 = (fq2_x, fq2_y, bn128.FQ2.one())
        if not bn128.is_on_curve(p2, bn128.b2):
            raise ValidationError("point is not on curve")
    else:
        p2 = ZERO

    if bn128.multiply(p2, bn128.curve_order)[-1] != bn128.FQ2.zero():
        raise ValidationError("TODO: what case is this?????")

    return exponent * bn128.pairing(p2, p1, final_exponentiate=False)
Exemple #8
0
    def validate_uncles(self, block: BaseBlock) -> None:
        """
        Validate the uncles for the given block.
        """
        # Check for duplicates
        uncle_groups = groupby(operator.attrgetter('hash'), block.uncles)
        duplicate_uncles = tuple(sorted(
            hash for hash, twins in uncle_groups.items() if len(twins) > 1
        ))
        if duplicate_uncles:
            raise ValidationError(
                "Block contains duplicate uncles:\n"
                " - {0}".format(' - '.join(duplicate_uncles))
            )

        recent_ancestors = tuple(
            ancestor
            for ancestor
            in self.get_ancestors(MAX_UNCLE_DEPTH + 1, header=block.header)
        )
        recent_ancestor_hashes = {ancestor.hash for ancestor in recent_ancestors}
        recent_uncle_hashes = _extract_uncle_hashes(recent_ancestors)

        for uncle in block.uncles:
            if uncle.hash == block.hash:
                raise ValidationError("Uncle has same hash as block")

            # ensure the uncle has not already been included.
            if uncle.hash in recent_uncle_hashes:
                raise ValidationError(
                    "Duplicate uncle: {0}".format(encode_hex(uncle.hash))
                )

            # ensure that the uncle is not one of the canonical chain blocks.
            if uncle.hash in recent_ancestor_hashes:
                raise ValidationError(
                    "Uncle {0} cannot be an ancestor of {1}".format(
                        encode_hex(uncle.hash), encode_hex(block.hash)))

            # ensure that the uncle was built off of one of the canonical chain
            # blocks.
            if uncle.parent_hash not in recent_ancestor_hashes or (
               uncle.parent_hash == block.header.parent_hash):
                raise ValidationError(
                    "Uncle's parent {0} is not an ancestor of {1}".format(
                        encode_hex(uncle.parent_hash), encode_hex(block.hash)))

            # Now perform VM level validation of the uncle
            self.validate_seal(uncle)

            try:
                uncle_parent = self.get_block_header_by_hash(uncle.parent_hash)
            except HeaderNotFound:
                raise ValidationError(
                    "Uncle ancestor not found: {0}".format(uncle.parent_hash)
                )

            uncle_vm_class = self.get_vm_class_for_block_number(uncle.block_number)
            uncle_vm_class.validate_uncle(block, uncle, uncle_parent)
Exemple #9
0
    def get_chain_at_block_parent(self, block: BaseBlock) -> BaseChain:
        """
        Returns a `Chain` instance with the given block's parent at the chain head.
        """
        try:
            parent_header = self.get_block_header_by_hash(
                block.header.parent_hash)
        except HeaderNotFound:
            raise ValidationError("Parent ({0}) of block {1} not found".format(
                block.header.parent_hash, block.header.hash))

        init_header = self.create_header_from_parent(parent_header)
        return type(self)(self.chaindb.db, init_header)
Exemple #10
0
 def validate_uncle(self, block, uncle):
     if uncle.block_number >= block.number:
         raise ValidationError(
             "Uncle number ({0}) is higher than block number ({1})".format(
                 uncle.block_number, block.number))
     try:
         parent_header = get_block_header_by_hash(uncle.parent_hash, self.chaindb)
     except BlockNotFound:
         raise ValidationError(
             "Uncle ancestor not found: {0}".format(uncle.parent_hash))
     if uncle.block_number != parent_header.block_number + 1:
         raise ValidationError(
             "Uncle number ({0}) is not one above ancestor's number ({1})".format(
                 uncle.block_number, parent_header.block_number))
     if uncle.timestamp < parent_header.timestamp:
         raise ValidationError(
             "Uncle timestamp ({0}) is before ancestor's timestamp ({1})".format(
                 uncle.timestamp, parent_header.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))
Exemple #11
0
def validate_unique(values):
    if not isdistinct(values):
        duplicates = pipe(
            values,
            frequencies,  # get the frequencies
            partial(valfilter,
                    lambda v: v > 1),  # filter to ones that occure > 1
            sorted,  # sort them
            tuple,  # cast them to an immutiable form
        )
        raise ValidationError(
            "The values provided are not unique.  Duplicates: {0}".format(
                ', '.join((str(value) for value in duplicates))))
Exemple #12
0
 def validate_uncle(self, uncle):
     if uncle.block_number >= self.number:
         raise ValidationError(
             "Uncle number ({0}) is higher than block number ({1})".format(
                 uncle.block_number, self.number))
     try:
         uncle_parent = self.db.get(uncle.parent_hash)
     except KeyError:
         raise ValidationError("Uncle ancestor not found: {0}".format(
             uncle.parent_hash))
     parent_header = rlp.decode(uncle_parent, sedes=BlockHeader)
     if uncle.block_number != parent_header.block_number + 1:
         raise ValidationError(
             "Uncle number ({0}) is not one above ancestor's number ({1})".
             format(uncle.block_number, parent_header.block_number))
     if uncle.timestamp < parent_header.timestamp:
         raise ValidationError(
             "Uncle timestamp ({0}) is before ancestor's timestamp ({1})".
             format(uncle.timestamp, parent_header.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))
Exemple #13
0
    async def fetch_headers(self, start_block: int, peer: LESPeer) -> List[BlockHeader]:
        if start_block == GENESIS_BLOCK_NUMBER:
            raise ValidationError("Must not attempt to download genesis header")

        for i in range(self.max_consecutive_timeouts):
            try:
                return await self._fetch_headers_starting_at(peer, start_block)
            except TimeoutError:
                self.logger.info(
                    "Timeout when fetching headers from %s (attempt %d of %d)",
                    peer, i + 1, self.max_consecutive_timeouts)
                # TODO: Figure out what's a good value to use here.
                await asyncio.sleep(0.5)
        raise TooManyTimeouts()
Exemple #14
0
def get_merkle_proof(tree: MerkleTree, item_index: int) -> Sequence[Hash32]:
    """Read off the Merkle proof for an item from a Merkle tree."""
    if item_index < 0 or item_index >= len(tree[-1]):
        raise ValidationError("Item index out of range")

    # special case of tree consisting of only root
    if len(tree) == 1:
        return ()

    branch_indices = get_branch_indices(item_index, len(tree))
    proof_indices = [i ^ 1 for i in branch_indices
                     ][:-1]  # get sibling by flipping rightmost bit
    return tuple(layer[proof_index]
                 for layer, proof_index in zip(reversed(tree), proof_indices))
Exemple #15
0
    def __init__(self, state_root, gas_used, logs, bloom=None):
        if bloom is None:
            bloomables = itertools.chain.from_iterable(log.bloomables
                                                       for log in logs)
            bloom = int(BloomFilter.from_iterable(bloomables))

        super(Receipt, self).__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))
Exemple #16
0
    def validate_block(self, block):
        if not block.is_genesis:
            parent_header = get_parent_header(block.header, self.chaindb)

            validate_gas_limit(block.header.gas_limit, parent_header.gas_limit)
            validate_length_lte(block.header.extra_data,
                                32,
                                title="BlockHeader.extra_data")

            # timestamp
            if block.header.timestamp < parent_header.timestamp:
                raise ValidationError(
                    "`timestamp` is before the parent block's timestamp.\n"
                    "- block  : {0}\n"
                    "- parent : {1}. ".format(
                        block.header.timestamp,
                        parent_header.timestamp,
                    ))
            elif block.header.timestamp == parent_header.timestamp:
                raise ValidationError(
                    "`timestamp` is equal to the parent block's timestamp\n"
                    "- block : {0}\n"
                    "- parent: {1}. ".format(
                        block.header.timestamp,
                        parent_header.timestamp,
                    ))

        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)))

        for uncle in block.uncles:
            self.validate_uncle(block, uncle)

        if not self.state.is_key_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,
                ))
Exemple #17
0
    def validate_header(cls, header, previous_header):
        # ignore mypy warnings, because super's validate_header is defined by mixing w/ other class
        super().validate_header(header, previous_header)  # 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),
                    ))
Exemple #18
0
def validate_unique(values, title="Value"):
    if not isdistinct(values):
        duplicates = pipe(
            values,
            frequencies,  # get the frequencies
            partial(valfilter,
                    lambda v: v > 1),  # filter to ones that occure > 1
            sorted,  # sort them
            tuple,  # cast them to an immutiable form
        )
        raise ValidationError(
            "{title} does not contain unique items.  Duplicates: {0}".format(
                ', '.join((str(value) for value in duplicates)),
                title=title,
            ))
Exemple #19
0
    def validate_header(cls, header: BlockHeader,
                        parent_header: BlockHeader) -> None:
        """
        :raise evm.exceptions.ValidationError: if the header is not valid
        """
        if parent_header is None:
            # to validate genesis header, check if it equals canonical header at block number 0
            raise ValidationError(
                "Must have access to parent header to validate current header")
        else:
            validate_length_lte(header.extra_data,
                                32,
                                title="BlockHeader.extra_data")

            validate_gas_limit(header.gas_limit, parent_header.gas_limit)

            if header.block_number != parent_header.block_number + 1:
                raise ValidationError(
                    "Blocks must be numbered consecutively. Block number #{} has parent #{}"
                    .format(
                        header.block_number,
                        parent_header.block_number,
                    ))

            # timestamp
            if header.timestamp <= parent_header.timestamp:
                raise ValidationError(
                    "timestamp must be strictly later than parent, but is {} seconds before.\n"
                    "- child  : {}\n"
                    "- parent : {}. ".format(
                        parent_header.timestamp - header.timestamp,
                        header.timestamp,
                        parent_header.timestamp,
                    ))

            cls.validate_seal(header)
Exemple #20
0
    def validate_block(self, block: BaseBlock) -> None:
        """
        Performs validation on a block that is either being mined or imported.

        Since block validation (specifically the uncle validation) must have
        access to the ancestor blocks, this validation must occur at the Chain
        level.

        Cannot be used to validate genesis block.
        """
        if block.is_genesis:
            raise ValidationError("Cannot validate genesis block this way")
        VM = self.get_vm_class_for_block_number(BlockNumber(block.number))
        parent_block = self.get_block_by_hash(block.header.parent_hash)
        VM.validate_header(block.header, parent_block.header)
        self.validate_uncles(block)
        self.validate_gaslimit(block.header)
Exemple #21
0
    def import_block(self, block):
        """
        Import the given block to the chain.
        """
        if self.block.number != block.number:
            raise ValidationError(
                "This VM can only import blocks at number #{}, the attempted block was #{}"
                .format(
                    self.block.number,
                    block.number,
                ))

        self.block = self.block.copy(
            header=self.configure_header(
                coinbase=block.header.coinbase,
                gas_limit=block.header.gas_limit,
                timestamp=block.header.timestamp,
                extra_data=block.header.extra_data,
                mix_hash=block.header.mix_hash,
                nonce=block.header.nonce,
                uncles_hash=keccak(rlp.encode(block.uncles)),
            ),
            uncles=block.uncles,
        )
        # we need to re-initialize the `state` to update the execution context.
        self._state = self.get_state_class()(
            db=self.chaindb.db,
            execution_context=self.block.header.create_execution_context(
                self.previous_hashes),
            state_root=self.block.header.state_root,
        )

        # run all of the transactions.
        new_header, receipts, _ = self.apply_all_transactions(
            block.transactions, self.block.header)

        self.block = self.set_block_transactions(
            self.block,
            new_header,
            block.transactions,
            receipts,
        )

        return self.mine_block()
Exemple #22
0
    def from_genesis(cls,
                     chaindb,
                     genesis_params,
                     genesis_state=None):
        """
        Initialize the Chain from a genesis state.
        """
        if chaindb.trie_class is HexaryTrie:
            root_hash = BLANK_ROOT_HASH
        else:
            root_hash = EMPTY_SHA3
        state_db = chaindb.get_state_db(root_hash, read_only=False)

        if genesis_state is None:
            genesis_state = {}

        for account, account_data in genesis_state.items():
            state_db.set_balance(account, account_data['balance'])
            state_db.set_nonce(account, account_data['nonce'])
            state_db.set_code(account, account_data['code'])

            for slot, value in account_data['storage'].items():
                state_db.set_storage(account, slot, value)

        if 'state_root' not in genesis_params:
            # If the genesis state_root was not specified, use the value
            # computed from the initialized state database.
            genesis_params = assoc(genesis_params, 'state_root', state_db.root_hash)
        elif genesis_params['state_root'] != state_db.root_hash:
            # If the genesis state_root was specified, validate that it matches
            # the computed state from the initialized state database.
            raise ValidationError(
                "The provided genesis state root does not match the computed "
                "genesis state root.  Got {0}.  Expected {1}".format(
                    state_db.root_hash,
                    genesis_params['state_root'],
                )
            )

        genesis_header = BlockHeader(**genesis_params)
        genesis_chain = cls(chaindb, genesis_header)
        chaindb.persist_block_to_db(genesis_chain.get_block())
        return cls.from_genesis_header(chaindb, genesis_header)
Exemple #23
0
def ensure_rlp_objects_are_equal(obj_a, obj_b, obj_a_name, obj_b_name):
    if obj_a == obj_b:
        return
    diff = diff_rlp_object(obj_a, obj_b)
    longest_field_name = max(len(field_name) for field_name, _, _ in diff)
    error_message = (
        "Mismatch between {obj_a_name} and {obj_b_name} on {0} fields:\n - {1}"
        .format(
            len(diff),
            "\n - ".join(
                tuple("{0}:\n    (actual)  : {1}\n    (expected): {2}".format(
                    pad_right(field_name, longest_field_name, ' '),
                    actual,
                    expected,
                ) for field_name, actual, expected in diff)),
            obj_a_name=obj_a_name,
            obj_b_name=obj_b_name,
        ))
    raise ValidationError(error_message)
Exemple #24
0
 def ensure_blocks_are_equal(self, block1, block2):
     if block1 == block2:
         return
     diff = diff_rlp_object(block1, block2)
     longest_field_name = max(len(field_name) for field_name, _, _ in diff)
     error_message = (
         "Mismatch between block and imported block on {0} fields:\n - {1}".format(
             len(diff),
             "\n - ".join(tuple(
                 "{0}:\n    (actual)  : {1}\n    (expected): {2}".format(
                     pad_right(field_name, longest_field_name, ' '),
                     actual,
                     expected,
                 )
                 for field_name, actual, expected
                 in diff
             )),
         )
     )
     raise ValidationError(error_message)
Exemple #25
0
    def consume_gas(self, amount, reason):
        if amount < 0:
            raise ValidationError("Gas consumption amount must be positive")

        if amount > self.gas_remaining:
            raise OutOfGas(
                "Out of gas: Needed {0} - Remaining {1} - Reason: {2}".format(
                    amount,
                    self.gas_remaining,
                    reason,
                ))

        self.gas_remaining -= amount

        self.logger.trace(
            'GAS CONSUMPTION: %s - %s -> %s (%s)',
            self.gas_remaining + amount,
            amount,
            self.gas_remaining,
            reason,
        )
Exemple #26
0
    def from_genesis(cls,
                     chaindb,
                     genesis_params,
                     genesis_state=None):
        """
        Initializes the Chain from a genesis state.
        """
        genesis_vm_class = cls.get_vm_class_for_block_number(0)
        account_db = genesis_vm_class.get_state_class().get_account_db_class()(
            chaindb.db,
            BLANK_ROOT_HASH,
        )

        if genesis_state is None:
            genesis_state = {}

        # mutation
        apply_state_dict(account_db, genesis_state)
        account_db.persist()

        if 'state_root' not in genesis_params:
            # If the genesis state_root was not specified, use the value
            # computed from the initialized state database.
            genesis_params = assoc(genesis_params, 'state_root', account_db.state_root)
        elif genesis_params['state_root'] != account_db.state_root:
            # If the genesis state_root was specified, validate that it matches
            # the computed state from the initialized state database.
            raise ValidationError(
                "The provided genesis state root does not match the computed "
                "genesis state root.  Got {0}.  Expected {1}".format(
                    account_db.state_root,
                    genesis_params['state_root'],
                )
            )

        genesis_header = BlockHeader(**genesis_params)
        genesis_chain = cls(chaindb, genesis_header)
        chaindb.persist_block(genesis_chain.get_block())
        return cls.from_genesis_header(chaindb, genesis_header)
Exemple #27
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 (start_index, end_index), (field_name, field_type) in zip(field_bounds, cls.fields):
            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)
Exemple #28
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:
            for index, transaction_hash in enumerate(
                    self.get_block_transaction_hashes(header)):
                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)
Exemple #29
0
 def _deserialize_header_bytes_to_dict(cls, header_bytes: bytes) -> Iterator[Tuple[str, Any]]:
     # assume all fields are padded to 32 bytes
     obj_size = 32
     if len(header_bytes) != obj_size * len(cls.fields):
         raise ValidationError(
             "Expected header bytes to be of length: {0}. Got length {1} instead.\n- {2}".format(
                 obj_size * len(cls.fields),
                 len(header_bytes),
                 encode_hex(header_bytes),
             )
         )
     for idx, field in enumerate(cls.fields):
         field_name, field_type = field
         start_index = idx * obj_size
         field_bytes = header_bytes[start_index:(start_index + obj_size)]
         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)
Exemple #30
0
    def import_block(self, block):
        self.configure_header(
            coinbase=block.header.coinbase,
            gas_limit=block.header.gas_limit,
            timestamp=block.header.timestamp,
            extra_data=block.header.extra_data,
            mix_hash=block.header.mix_hash,
            nonce=block.header.nonce,
        )

        for transaction in block.transactions:
            self.apply_transaction(transaction)

        for uncle in block.uncles:
            self.block.add_uncle(uncle)

        mined_block = self.mine_block()
        if mined_block != block:
            diff = diff_rlp_object(mined_block, block)
            longest_field_name = max(len(field_name) for field_name, _, _ in diff)
            error_message = (
                "Mismatch between block and imported block on {0} fields:\n - {1}".format(
                    len(diff),
                    "\n - ".join(tuple(
                        "{0}:\n    (actual)  : {1}\n    (expected): {2}".format(
                            pad_right(field_name, longest_field_name, ' '),
                            actual,
                            expected,
                        )
                        for field_name, actual, expected
                        in diff
                    )),
                )
            )
            raise ValidationError(error_message)

        return mined_block