Пример #1
0
    def apply_create_message(self) -> BaseComputation:
        computation = self.apply_message()

        if computation.is_error:
            return computation
        else:
            contract_code = computation.output

            if contract_code:
                contract_code_gas_fee = len(contract_code) * GAS_CODEDEPOSIT
                try:
                    computation.consume_gas(
                        contract_code_gas_fee,
                        reason="Write contract code for CREATE",
                    )
                except OutOfGas:
                    computation.output = b''
                else:
                    self.logger.trace(
                        "SETTING CODE: %s -> length: %s | hash: %s",
                        encode_hex(self.msg.storage_address),
                        len(contract_code), encode_hex(keccak(contract_code)))
                    self.state.account_db.set_code(self.msg.storage_address,
                                                   contract_code)
            return computation
Пример #2
0
    def build_computation(self, message: Message,
                          transaction: BaseTransaction) -> BaseComputation:
        """Apply the message to the VM."""
        transaction_context = self.vm_state.get_transaction_context(
            transaction)
        if message.is_create:
            is_collision = self.vm_state.account_db.account_has_code_or_nonce(
                message.storage_address)

            if is_collision:
                # The address of the newly created contract has *somehow* collided
                # with an existing contract address.
                computation = self.vm_state.get_computation(
                    message, transaction_context)
                computation._error = ContractCreationCollision(
                    "Address collision while creating contract: {0}".format(
                        encode_hex(message.storage_address), ))
                self.vm_state.logger.trace(
                    "Address collision while creating contract: %s",
                    encode_hex(message.storage_address),
                )
            else:
                computation = self.vm_state.get_computation(
                    message,
                    transaction_context,
                ).apply_create_message()
        else:
            computation = self.vm_state.get_computation(
                message, transaction_context).apply_message()

        return computation
Пример #3
0
    def get_canonical_transaction(self, transaction_hash: Hash32) -> BaseTransaction:
        """
        Returns the requested transaction as specified by the transaction hash
        from the canonical chain.

        Raises TransactionNotFound if no transaction with the specified hash is
        found in the main chain.
        """
        (block_num, index) = self.chaindb.get_transaction_index(transaction_hash)
        VM = self.get_vm_class_for_block_number(block_num)

        transaction = self.chaindb.get_transaction_by_index(
            block_num,
            index,
            VM.get_transaction_class(),
        )

        if transaction.hash == transaction_hash:
            return transaction
        else:
            raise TransactionNotFound("Found transaction {} instead of {} in block {} at {}".format(
                encode_hex(transaction.hash),
                encode_hex(transaction_hash),
                block_num,
                index,
            ))
Пример #4
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)
Пример #5
0
    def __enter__(self) -> 'BaseComputation':
        self.logger.trace(
            ("COMPUTATION STARTING: gas: %s | from: %s | to: %s | value: %s "
             "| depth %s | static: %s"),
            self.msg.gas,
            encode_hex(self.msg.sender),
            encode_hex(self.msg.to),
            self.msg.value,
            self.msg.depth,
            "y" if self.msg.is_static else "n",
        )

        return self
Пример #6
0
Файл: pow.py Проект: sjyi/py-evm
def check_pow(block_number: int, mining_hash: Hash32, mix_hash: Hash32,
              nonce: bytes, difficulty: int) -> None:
    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")
Пример #7
0
    def build_evm_message(self, transaction):

        transaction_context = self.get_transaction_context(transaction)
        gas_fee = transaction.gas * transaction_context.gas_price

        # Buy Gas
        self.vm_state.account_db.delta_balance(transaction.sender,
                                               -1 * gas_fee)

        # Increment Nonce
        self.vm_state.account_db.increment_nonce(transaction.sender)

        # Setup VM Message
        message_gas = transaction.gas - transaction.intrinsic_gas

        if transaction.to == CREATE_CONTRACT_ADDRESS:
            contract_address = generate_contract_address(
                transaction.sender,
                self.vm_state.account_db.get_nonce(transaction.sender) - 1,
            )
            data = b''
            code = transaction.data
        else:
            contract_address = None
            data = transaction.data
            code = self.vm_state.account_db.get_code(transaction.to)

        self.vm_state.logger.trace(
            ("TRANSACTION: sender: %s | to: %s | value: %s | gas: %s | "
             "gas-price: %s | s: %s | r: %s | v: %s | data-hash: %s"),
            encode_hex(transaction.sender),
            encode_hex(transaction.to),
            transaction.value,
            transaction.gas,
            transaction.gas_price,
            transaction.s,
            transaction.r,
            transaction.v,
            encode_hex(keccak(transaction.data)),
        )

        message = Message(
            gas=message_gas,
            to=transaction.to,
            sender=transaction.sender,
            value=transaction.value,
            data=data,
            code=code,
            create_address=contract_address,
        )
        return message
Пример #8
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))
Пример #9
0
    def apply_create_message(self):
        snapshot = self.state.snapshot()

        # EIP161 nonce incrementation
        self.state.account_db.increment_nonce(self.msg.storage_address)

        computation = self.apply_message()

        if computation.is_error:
            self.state.revert(snapshot)
            return computation
        else:
            contract_code = computation.output

            if contract_code and len(contract_code) >= EIP170_CODE_SIZE_LIMIT:
                computation._error = OutOfGas(
                    "Contract code size exceeds EIP170 limit of {0}.  Got code of "
                    "size: {1}".format(
                        EIP170_CODE_SIZE_LIMIT,
                        len(contract_code),
                    ))
                self.state.revert(snapshot)
            elif contract_code:
                contract_code_gas_cost = len(
                    contract_code) * constants.GAS_CODEDEPOSIT
                try:
                    computation.consume_gas(
                        contract_code_gas_cost,
                        reason="Write contract code for CREATE",
                    )
                except OutOfGas as err:
                    # Different from Frontier: reverts state on gas failure while
                    # writing contract code.
                    computation._error = err
                    self.state.revert(snapshot)
                else:
                    if self.logger:
                        self.logger.trace(
                            "SETTING CODE: %s -> length: %s | hash: %s",
                            encode_hex(self.msg.storage_address),
                            len(contract_code),
                            encode_hex(keccak(contract_code)))

                    self.state.account_db.set_code(self.msg.storage_address,
                                                   contract_code)
                    self.state.commit(snapshot)
            else:
                self.state.commit(snapshot)
            return computation
Пример #10
0
    def finalize_computation(self, transaction: BaseTransaction,
                             computation: BaseComputation) -> BaseComputation:
        # Self Destruct Refunds
        num_deletions = len(computation.get_accounts_for_deletion())
        if num_deletions:
            computation.refund_gas(REFUND_SELFDESTRUCT * num_deletions)

        # Gas Refunds
        gas_remaining = computation.get_gas_remaining()
        gas_refunded = computation.get_gas_refund()
        gas_used = transaction.gas - gas_remaining
        gas_refund = min(gas_refunded, gas_used // 2)
        gas_refund_amount = (gas_refund +
                             gas_remaining) * transaction.gas_price

        if gas_refund_amount:
            self.vm_state.logger.trace(
                'TRANSACTION REFUND: %s -> %s',
                gas_refund_amount,
                encode_hex(computation.msg.sender),
            )

            self.vm_state.account_db.delta_balance(computation.msg.sender,
                                                   gas_refund_amount)

        # Miner Fees
        transaction_fee = \
            (transaction.gas - gas_remaining - gas_refund) * transaction.gas_price
        self.vm_state.logger.trace(
            'TRANSACTION FEE: %s -> %s',
            transaction_fee,
            encode_hex(self.vm_state.coinbase),
        )
        self.vm_state.account_db.delta_balance(self.vm_state.coinbase,
                                               transaction_fee)

        # Process Self Destructs
        for account, beneficiary in computation.get_accounts_for_deletion():
            # TODO: need to figure out how we prevent multiple selfdestructs from
            # the same account and if this is the right place to put this.
            self.vm_state.logger.trace('DELETING ACCOUNT: %s',
                                       encode_hex(account))

            # TODO: this balance setting is likely superflous and can be
            # removed since `delete_account` does this.
            self.vm_state.account_db.set_balance(account, 0)
            self.vm_state.account_db.delete_account(account)

        return computation
Пример #11
0
    def __exit__(self, exc_type: None, exc_value: None,
                 traceback: None) -> None:
        if exc_value and isinstance(exc_value, VMError):
            self.logger.trace(
                ("COMPUTATION ERROR: gas: %s | from: %s | to: %s | value: %s | "
                 "depth: %s | static: %s | error: %s"),
                self.msg.gas,
                encode_hex(self.msg.sender),
                encode_hex(self.msg.to),
                self.msg.value,
                self.msg.depth,
                "y" if self.msg.is_static else "n",
                exc_value,
            )
            # print("COMPUTATION ERROR:\"{}\", gasUsed:\"{}\"".format(exc_value, ('%x' % (self.msg.gas - self._gas_meter.gas_remaining))))
            import binascii
            print("{{\"output\": \"{}\", \"gasUsed\": \"0x{}\"}}".format(
                str(binascii.hexlify(self.output), encoding="utf8"),
                '%x' % (self.msg.gas - self._gas_meter.gas_remaining)))

            self._error = exc_value
            if self.should_burn_gas:
                self.consume_gas(
                    self._gas_meter.gas_remaining,
                    reason=" ".join((
                        "Zeroing gas due to VM Exception:",
                        str(exc_value),
                    )),
                )

            # suppress VM exceptions
            return True
        elif exc_type is None:
            self.logger.trace(
                ("COMPUTATION SUCCESS: from: %s | to: %s | value: %s | "
                 "depth: %s | static: %s | gas-used: %s | gas-remaining: %s"),
                encode_hex(self.msg.sender),
                encode_hex(self.msg.to),
                self.msg.value,
                self.msg.depth,
                "y" if self.msg.is_static else "n",
                self.msg.gas - self._gas_meter.gas_remaining,
                self._gas_meter.gas_remaining,
            )
            import binascii
            print("{{\"output\": \"{}\", \"gasUsed\": \"0x{}\"}}".format(
                str(binascii.hexlify(self.output), encoding="utf8"),
                '%x' % (self.msg.gas - self._gas_meter.gas_remaining)))
Пример #12
0
def format_block(block: BaseBlock) -> str:
    return (
        "\n\n"
        "------------------------Block------------------------------------\n"
        "Number #{b.number:>12} Hash {hash}\n"
        "-----------------------------------------------------------------\n"
    ).format(b=block, hash=encode_hex(block.hash))
Пример #13
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)
Пример #14
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
Пример #15
0
def sstore_eip1283(computation):
    slot, value = computation.stack_pop(num_items=2, type_hint=UINT256)

    current_value = computation.state.account_db.get_storage(
        address=computation.msg.storage_address,
        slot=slot,
    )

    original_value = computation.state.account_db.get_storage(
        address=computation.msg.storage_address, slot=slot, from_journal=False)

    gas_refund = 0

    if current_value == value:
        gas_cost = constants.GAS_SSTORE_EIP1283_NOOP
    else:
        if original_value == current_value:
            if original_value == 0:
                gas_cost = constants.GAS_SSTORE_EIP1283_INIT
            else:
                gas_cost = constants.GAS_SSTORE_EIP1283_CLEAN

                if value == 0:
                    gas_refund += constants.GAS_SSTORE_EIP1283_CLEAR_REFUND
        else:
            gas_cost = constants.GAS_SSTORE_EIP1283_NOOP

            if original_value != 0:
                if current_value == 0:
                    gas_refund -= constants.GAS_SSTORE_EIP1283_CLEAR_REFUND
                if value == 0:
                    gas_refund += constants.GAS_SSTORE_EIP1283_CLEAR_REFUND

            if original_value == value:
                if original_value == 0:
                    gas_refund += constants.GAS_SSTORE_EIP1283_RESET_CLEAR_REFUND
                else:
                    gas_refund += constants.GAS_SSTORE_EIP1283_RESET_REFUND

    computation.consume_gas(
        gas_cost,
        reason="SSTORE: {0}[{1}] -> {2} (current: {3} / original: {4})".format(
            encode_hex(computation.msg.storage_address),
            slot,
            value,
            current_value,
            original_value,
        ))

    if gas_refund:
        computation.refund_gas(gas_refund)

    computation.state.account_db.set_storage(
        address=computation.msg.storage_address,
        slot=slot,
        value=value,
    )
Пример #16
0
    def __call__(self, computation: BaseComputation) -> None:

        stack_data = self.get_stack_data(computation)

        gas_cost = self.get_gas_cost(stack_data)
        computation.consume_gas(gas_cost, reason=self.mnemonic)

        computation.extend_memory(stack_data.memory_start,
                                  stack_data.memory_length)

        insufficient_funds = computation.state.account_db.get_balance(
            computation.msg.storage_address) < stack_data.endowment
        stack_too_deep = computation.msg.depth + 1 > constants.STACK_DEPTH_LIMIT

        if insufficient_funds or stack_too_deep:
            computation.stack_push(0)
            return

        call_data = computation.memory_read(stack_data.memory_start,
                                            stack_data.memory_length)

        create_msg_gas = self.max_child_gas_modifier(
            computation.get_gas_remaining())
        computation.consume_gas(create_msg_gas, reason=self.mnemonic)

        contract_address = self.generate_contract_address(
            stack_data, call_data, computation)

        is_collision = computation.state.account_db.account_has_code_or_nonce(
            contract_address)

        if is_collision:
            self.logger.trace(
                "Address collision while creating contract: %s",
                encode_hex(contract_address),
            )
            computation.stack_push(0)
            return

        child_msg = computation.prepare_child_message(
            gas=create_msg_gas,
            to=constants.CREATE_CONTRACT_ADDRESS,
            value=stack_data.endowment,
            data=b'',
            code=call_data,
            create_address=contract_address,
        )

        child_computation = computation.apply_child_computation(child_msg)

        if child_computation.is_error:
            computation.stack_push(0)
        else:
            computation.stack_push(contract_address)
        computation.return_gas(child_computation.get_gas_remaining())
Пример #17
0
    def import_block(self,
                     block: BaseBlock,
                     perform_validation: bool=True
                     ) -> Tuple[BaseBlock, Tuple[BaseBlock, ...], Tuple[BaseBlock, ...]]:
        """
        Imports a complete block and returns a 3-tuple

        - the imported block
        - a tuple of blocks which are now part of the canonical chain.
        - a tuple of blocks which are were canonical and now are no longer canonical.
        """

        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:
            validate_imported_block_unchanged(imported_block, block)
            self.validate_block(imported_block)

        (
            new_canonical_hashes,
            old_canonical_hashes,
        ) = self.chaindb.persist_block(imported_block)

        self.logger.debug(
            'IMPORTED_BLOCK: number %s | hash %s',
            imported_block.number,
            encode_hex(imported_block.hash),
        )

        new_canonical_blocks = tuple(
            self.get_block_by_hash(header_hash)
            for header_hash
            in new_canonical_hashes
        )
        old_canonical_blocks = tuple(
            self.get_block_by_hash(header_hash)
            for header_hash
            in old_canonical_hashes
        )

        return imported_block, new_canonical_blocks, old_canonical_blocks
Пример #18
0
    def __exit__(self, exc_type: None, exc_value: None, traceback: None) -> None:
        if exc_value and isinstance(exc_value, VMError):
            self.logger.trace(
                (
                    "COMPUTATION ERROR: gas: %s | from: %s | to: %s | value: %s | "
                    "depth: %s | static: %s | error: %s"
                ),
                self.msg.gas,
                encode_hex(self.msg.sender),
                encode_hex(self.msg.to),
                self.msg.value,
                self.msg.depth,
                "y" if self.msg.is_static else "n",
                exc_value,
            )
            self._error = exc_value
            if self.should_burn_gas:
                self.consume_gas(
                    self._gas_meter.gas_remaining,
                    reason=" ".join((
                        "Zeroing gas due to VM Exception:",
                        str(exc_value),
                    )),
                )

            # suppress VM exceptions
            return True
        elif exc_type is None:
            self.logger.trace(
                (
                    "COMPUTATION SUCCESS: from: %s | to: %s | value: %s | "
                    "depth: %s | static: %s | gas-used: %s | gas-remaining: %s"
                ),
                encode_hex(self.msg.sender),
                encode_hex(self.msg.to),
                self.msg.value,
                self.msg.depth,
                "y" if self.msg.is_static else "n",
                self.msg.gas - self._gas_meter.gas_remaining,
                self._gas_meter.gas_remaining,
            )
Пример #19
0
    def apply_message(self) -> BaseComputation:
        snapshot = self.state.snapshot()

        if self.msg.depth > STACK_DEPTH_LIMIT:
            raise StackDepthLimit("Stack depth limit reached")

        if self.msg.should_transfer_value and self.msg.value:
            sender_balance = self.state.account_db.get_balance(self.msg.sender)

            if sender_balance < self.msg.value:
                raise InsufficientFunds("Insufficient funds: {0} < {1}".format(
                    sender_balance, self.msg.value))

            self.state.account_db.delta_balance(self.msg.sender,
                                                -1 * self.msg.value)
            self.state.account_db.delta_balance(self.msg.storage_address,
                                                self.msg.value)

            self.logger.trace(
                "TRANSFERRED: %s from %s -> %s",
                self.msg.value,
                encode_hex(self.msg.sender),
                encode_hex(self.msg.storage_address),
            )

        self.state.account_db.touch_account(self.msg.storage_address)

        computation = self.apply_computation(
            self.state,
            self.msg,
            self.transaction_context,
        )

        if computation.is_error:
            self.state.revert(snapshot)
        else:
            self.state.commit(snapshot)

        return computation
Пример #20
0
    def apply_create_message(self):
        snapshot = self.state.snapshot()

        computation = self.apply_message()

        if computation.is_error:
            self.state.revert(snapshot)
            return computation
        else:
            contract_code = computation.output

            if contract_code:
                contract_code_gas_cost = len(
                    contract_code) * constants.GAS_CODEDEPOSIT
                try:
                    computation.consume_gas(
                        contract_code_gas_cost,
                        reason="Write contract code for CREATE",
                    )
                except OutOfGas as err:
                    # Different from Frontier: reverts state on gas failure while
                    # writing contract code.
                    computation._error = err
                    self.state.revert(snapshot)
                else:
                    if self.logger:
                        self.logger.trace(
                            "SETTING CODE: %s -> length: %s | hash: %s",
                            encode_hex(self.msg.storage_address),
                            len(contract_code),
                            encode_hex(keccak(contract_code)))

                    self.state.account_db.set_code(self.msg.storage_address,
                                                   contract_code)
                    self.state.commit(snapshot)
            else:
                self.state.commit(snapshot)
            return computation
Пример #21
0
    def persist_header(self, header: BlockHeader) -> Tuple[BlockHeader, ...]:
        """
        Returns iterable of headers newly on the canonical chain
        """
        is_genesis = header.parent_hash == GENESIS_PARENT_HASH
        if not is_genesis and not self.header_exists(header.parent_hash):
            raise ParentNotFound(
                "Cannot persist block header ({}) with unknown parent ({})".
                format(encode_hex(header.hash),
                       encode_hex(header.parent_hash)))

        self.db.set(
            header.hash,
            rlp.encode(header),
        )

        if is_genesis:
            score = header.difficulty
        else:
            score = self.get_score(header.parent_hash) + header.difficulty

        self.db.set(
            SchemaV1.make_block_hash_to_score_lookup_key(header.hash),
            rlp.encode(score, sedes=rlp.sedes.big_endian_int),
        )

        try:
            head_score = self.get_score(self.get_canonical_head().hash)
        except CanonicalHeadNotFound:
            new_headers = self._set_as_canonical_chain_head(header)
        else:
            if score > head_score:
                new_headers = self._set_as_canonical_chain_head(header)
            else:
                new_headers = tuple()

        return new_headers
Пример #22
0
def _selfdestruct(computation, beneficiary):
    local_balance = computation.state.account_db.get_balance(
        computation.msg.storage_address)
    beneficiary_balance = computation.state.account_db.get_balance(beneficiary)

    # 1st: Transfer to beneficiary
    computation.state.account_db.set_balance(
        beneficiary, local_balance + beneficiary_balance)
    # 2nd: Zero the balance of the address being deleted (must come after
    # sending to beneficiary in case the contract named itself as the
    # beneficiary.
    computation.state.account_db.set_balance(computation.msg.storage_address,
                                             0)

    computation.logger.trace(
        "SELFDESTRUCT: %s (%s) -> %s",
        encode_hex(computation.msg.storage_address),
        local_balance,
        encode_hex(beneficiary),
    )

    # 3rd: Register the account to be deleted
    computation.register_account_for_deletion(beneficiary)
    raise Halt('SELFDESTRUCT')
Пример #23
0
    def get_transaction_index(self, transaction_hash: Hash32) -> Tuple[BlockNumber, int]:
        """
        Returns a 2-tuple of (block_number, transaction_index) indicating which
        block the given transaction can be found in and at what index in the
        block transactions.

        Raises TransactionNotFound if the transaction_hash is not found in the
        canonical chain.
        """
        key = SchemaV1.make_transaction_hash_to_block_lookup_key(transaction_hash)
        try:
            encoded_key = self.db[key]
        except KeyError:
            raise TransactionNotFound(
                "Transaction {} not found in canonical chain".format(encode_hex(transaction_hash)))

        transaction_key = rlp.decode(encoded_key, sedes=TransactionKey)
        return (transaction_key.block_number, transaction_key.index)
Пример #24
0
def sstore(computation: BaseComputation) -> None:
    slot, value = computation.stack_pop(num_items=2,
                                        type_hint=constants.UINT256)

    current_value = computation.state.account_db.get_storage(
        address=computation.msg.storage_address,
        slot=slot,
    )

    is_currently_empty = not bool(current_value)
    is_going_to_be_empty = not bool(value)

    if is_currently_empty:
        gas_refund = 0
    elif is_going_to_be_empty:
        gas_refund = constants.REFUND_SCLEAR
    else:
        gas_refund = 0

    if is_currently_empty and is_going_to_be_empty:
        gas_cost = constants.GAS_SRESET
    elif is_currently_empty:
        gas_cost = constants.GAS_SSET
    elif is_going_to_be_empty:
        gas_cost = constants.GAS_SRESET
    else:
        gas_cost = constants.GAS_SRESET

    computation.consume_gas(gas_cost,
                            reason="SSTORE: {0}[{1}] -> {2} ({3})".format(
                                encode_hex(computation.msg.storage_address),
                                slot,
                                value,
                                current_value,
                            ))

    if gas_refund:
        computation.refund_gas(gas_refund)

    computation.state.account_db.set_storage(
        address=computation.msg.storage_address,
        slot=slot,
        value=value,
    )
Пример #25
0
    def finalize_computation(self, transaction, computation):
        computation = super().finalize_computation(transaction, computation)

        #
        # EIP161 state clearing
        #
        touched_accounts = collect_touched_accounts(computation)

        for account in touched_accounts:
            should_delete = (
                self.vm_state.account_db.account_exists(account) and
                self.vm_state.account_db.account_is_empty(account)
            )
            if should_delete:
                self.vm_state.logger.trace(
                    "CLEARING EMPTY ACCOUNT: %s",
                    encode_hex(account),
                )
                self.vm_state.account_db.delete_account(account)

        return computation
Пример #26
0

if __name__ == "__main__":
    for (filler_dir, test_dir), test_groups in DIR_STRUCTURE.items():
        for test_group, tests in test_groups.items():
            for filler, filler_kwargs in tests:
                test_name = get_test_name(filler)
                filename = test_name + ".json"

                filler_src_path = os.path.join(filler_dir, test_group, filename)
                filler_path = os.path.join(FILLER_PARENT_DIR, filler_src_path)
                test_path = os.path.join(TEST_PARENT_DIR, test_dir, test_group, filename)

                for path in [filler_path, test_path]:
                    os.makedirs(os.path.dirname(path), exist_ok=True)

                formatted_filler = filler_formatter(filler)
                filler_string = json.dumps(formatted_filler, indent=4, sort_keys=True)
                with open(filler_path, "w") as filler_file:
                    filler_file.write(filler_string)

                filler_hash = keccak(filler_string.encode("ascii"))
                info = {
                    "source": filler_src_path,
                    "sourceHash": encode_hex(filler_hash),
                }

                test = fill_test(filler, info=info, **filler_kwargs or {})
                with open(test_path, "w") as test_file:
                    json.dump(test, test_file, indent=4, sort_keys=True)
Пример #27
0
    def __call__(self, computation):
        computation.consume_gas(self.gas_cost, reason=self.mnemonic)

        value, start_position, size = computation.stack_pop(
            num_items=3,
            type_hint=constants.UINT256,
        )

        computation.extend_memory(start_position, size)

        insufficient_funds = computation.state.account_db.get_balance(
            computation.msg.storage_address) < value
        stack_too_deep = computation.msg.depth + 1 > constants.STACK_DEPTH_LIMIT

        if insufficient_funds or stack_too_deep:
            computation.stack_push(0)
            return

        call_data = computation.memory_read(start_position, size)

        create_msg_gas = self.max_child_gas_modifier(
            computation.get_gas_remaining())
        computation.consume_gas(create_msg_gas, reason="CREATE")

        creation_nonce = computation.state.account_db.get_nonce(
            computation.msg.storage_address)
        computation.state.account_db.increment_nonce(
            computation.msg.storage_address)

        contract_address = generate_contract_address(
            computation.msg.storage_address,
            creation_nonce,
        )

        is_collision = computation.state.account_db.account_has_code_or_nonce(
            contract_address)

        if is_collision:
            self.logger.trace(
                "Address collision while creating contract: %s",
                encode_hex(contract_address),
            )
            computation.stack_push(0)
            return

        child_msg = computation.prepare_child_message(
            gas=create_msg_gas,
            to=constants.CREATE_CONTRACT_ADDRESS,
            value=value,
            data=b'',
            code=call_data,
            create_address=contract_address,
        )

        child_computation = computation.apply_child_computation(child_msg)

        if child_computation.is_error:
            computation.stack_push(0)
        else:
            computation.stack_push(contract_address)
        computation.return_gas(child_computation.get_gas_remaining())
Пример #28
0
 def __repr__(self) -> str:
     return '<Block #{0} {1}>'.format(
         self.slot_number,
         encode_hex(self.hash)[2:10],
     )
 def __repr__(self) -> str:
     return '<BlockHeader #{0} {1}>'.format(
         self.block_number,
         encode_hex(self.hash)[2:10],
     )
 def hex_hash(self):
     return encode_hex(self.hash)