예제 #1
0
    def add_transaction(self, transaction, computation, block):
        """
        Add a transaction to the given block and
        return `trie_data` to store the transaction data in chaindb in VM layer.

        Update the bloom_filter, transaction trie and receipt trie roots, bloom_filter,
        bloom, and used_gas of the block.

        :param transaction: the executed transaction
        :param computation: the Computation object with executed result
        :param block: the Block which the transaction is added in
        :type transaction: Transaction
        :type computation: Computation
        :type block: Block

        :return: the block and the trie_data
        :rtype: (Block, dict[bytes, bytes])
        """
        receipt = self.make_receipt(transaction, computation)
        self.add_receipt(receipt)

        # Create a new Block object
        block_header = block.header.clone()
        transactions = list(block.transactions)
        block = self.block_class(block_header, transactions)

        block.transactions.append(transaction)

        # Calculate transaction fee
        transaction_fee, _ = computation.compute_transaction_fee_and_refund()
        # Bookkeep this transaction fee
        block.transaction_fee_sum += transaction_fee

        # Get trie roots and changed key-values.
        tx_root_hash, tx_kv_nodes = make_trie_root_and_nodes(
            block.transactions,
            trie_class=BinaryTrie,
        )
        receipt_root_hash, receipt_kv_nodes = make_trie_root_and_nodes(
            self.receipts,
            trie_class=BinaryTrie,
        )

        trie_data = merge(tx_kv_nodes, receipt_kv_nodes)

        block.header.transaction_root = tx_root_hash
        block.header.receipt_root = receipt_root_hash
        block.header.gas_used = receipt.gas_used

        return block, trie_data
예제 #2
0
    def set_block_transactions(self, base_block, new_header, transactions, receipts):

        tx_root_hash, tx_kv_nodes = make_trie_root_and_nodes(transactions)
        self.chaindb.persist_trie_data_dict(tx_kv_nodes)

        receipt_root_hash, receipt_kv_nodes = make_trie_root_and_nodes(receipts)
        self.chaindb.persist_trie_data_dict(receipt_kv_nodes)

        return base_block.copy(
            transactions=transactions,
            header=new_header.copy(
                transaction_root=tx_root_hash,
                receipt_root=receipt_root_hash,
            ),
        )
예제 #3
0
 async def handle_msg(self, peer: ETHPeer, cmd: protocol.Command,
                      msg: protocol._DecodedMsgType) -> None:
     if isinstance(cmd, eth.BlockHeaders):
         msg = cast(List[BlockHeader], msg)
         self.logger.debug("Got BlockHeaders from %d to %d",
                           msg[0].block_number, msg[-1].block_number)
         self._new_headers.put_nowait(msg)
     elif isinstance(cmd, eth.BlockBodies):
         msg = cast(List[eth.BlockBody], msg)
         self.logger.debug("Got %d BlockBodies", len(msg))
         for body in msg:
             tx_root, trie_dict_data = make_trie_root_and_nodes(
                 body.transactions)
             await self.chaindb.coro_persist_trie_data_dict(trie_dict_data)
             # TODO: Add transactions to canonical chain; blocked by
             # https://github.com/ethereum/py-evm/issues/337
             uncles_hash = await self.chaindb.coro_persist_uncles(
                 body.uncles)
             self._pending_bodies.pop((tx_root, uncles_hash), None)
     elif isinstance(cmd, eth.Receipts):
         msg = cast(List[List[eth.Receipt]], msg)
         self.logger.debug("Got Receipts for %d blocks", len(msg))
         for block_receipts in msg:
             receipt_root, trie_dict_data = make_trie_root_and_nodes(
                 block_receipts)
             await self.chaindb.coro_persist_trie_data_dict(trie_dict_data)
             self._pending_receipts.pop(receipt_root, None)
     elif isinstance(cmd, eth.NewBlock):
         msg = cast(Dict[str, Any], msg)
         header = msg['block'][0]
         actual_head = header.parent_hash
         actual_td = msg['total_difficulty'] - header.difficulty
         if actual_td > peer.head_td:
             peer.head_hash = actual_head
             peer.head_td = actual_td
             self._sync_requests.put_nowait(peer)
     elif isinstance(cmd, eth.Transactions):
         # TODO: Figure out what to do with those.
         pass
     elif isinstance(cmd, eth.NewBlockHashes):
         # TODO: Figure out what to do with those.
         pass
     else:
         # TODO: There are other msg types we'll want to handle here, but for now just log them
         # as a warning so we don't forget about it.
         self.logger.warn("Got unexpected msg: %s (%s)", cmd, msg)
예제 #4
0
파일: base.py 프로젝트: 9nix00/py-evm
    def import_block(self, block):
        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,
            gas_used=self.block.header.gas_used,
        )

        # run all of the transactions.
        execution_data = [
            self.apply_transaction(transaction)
            for transaction
            in block.transactions
        ]
        if execution_data:
            _, receipts, _ = zip(*execution_data)
        else:
            receipts = tuple()

        tx_root_hash, tx_kv_nodes = make_trie_root_and_nodes(block.transactions)
        receipt_root_hash, receipt_kv_nodes = make_trie_root_and_nodes(receipts)

        self.chaindb.persist_trie_data_dict(tx_kv_nodes)
        self.chaindb.persist_trie_data_dict(receipt_kv_nodes)

        self.block = self.block.copy(
            header=self.block.header.copy(
                transaction_root=tx_root_hash,
                receipt_root=receipt_root_hash,
            ),
        )

        return self.mine_block()
예제 #5
0
파일: base.py 프로젝트: rizplate/py-evm
    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,
                ))
예제 #6
0
파일: base.py 프로젝트: 9nix00/py-evm
    def apply_transaction(self, transaction):
        """
        Applies the transaction to the current tip block.

        WARNING: Receipt and Transaction trie generation is computationally
        heavy and incurs significant perferomance overhead.
        """
        vm = self.get_vm()
        block, receipt, computation = vm.apply_transaction(transaction)
        receipts = block.get_receipts(self.chaindb) + [receipt]

        tx_root_hash, tx_kv_nodes = make_trie_root_and_nodes(block.transactions)
        receipt_root_hash, receipt_kv_nodes = make_trie_root_and_nodes(receipts)
        self.chaindb.persist_trie_data_dict(tx_kv_nodes)
        self.chaindb.persist_trie_data_dict(receipt_kv_nodes)

        self.header = block.header.copy(
            transaction_root=tx_root_hash,
            receipt_root=receipt_root_hash,
        )

        return block.copy(header=self.header), receipt, computation
예제 #7
0
    def mine_block(self, *args, **kwargs):
        """
        Mine the current block. Proxies to self.pack_block method.
        """
        block = self.block
        tx_root_hash, tx_kv_nodes = make_trie_root_and_nodes(
            block.transactions)
        receipt_root_hash, receipt_kv_nodes = make_trie_root_and_nodes(
            self.receipts)
        self.chaindb.persist_trie_data_dict(tx_kv_nodes)
        self.chaindb.persist_trie_data_dict(receipt_kv_nodes)
        self.block.header.transaction_root = tx_root_hash
        self.block.header.receipt_root = receipt_root_hash

        self.pack_block(block, *args, **kwargs)

        if block.number == 0:
            return block

        block = self.state.finalize_block(block)

        return block
예제 #8
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,
                )
            )