Exemplo n.º 1
0
Arquivo: chain.py Projeto: ofek/py-evm
    def validate_uncles(self, block):
        recent_ancestors = dict(
            (ancestor.hash, ancestor)
            for ancestor in self.get_ancestors(MAX_UNCLE_DEPTH + 1),
        )
        recent_uncles = []
        for ancestor in recent_ancestors.values():
            recent_uncles.extend([uncle.hash for uncle in ancestor.uncles])
        recent_ancestors[block.hash] = block
        recent_uncles.append(block.hash)

        for uncle in block.uncles:
            if uncle.hash in recent_ancestors:
                raise ValidationError(
                    "Duplicate uncle: {0}".format(encode_hex(uncle.hash)))
            recent_uncles.append(uncle.hash)

            if uncle.hash in recent_ancestors:
                raise ValidationError(
                    "Uncle {0} cannot be an ancestor of {1}".format(
                        encode_hex(uncle.hash), encode_hex(block.hash)))

            if uncle.parent_hash not in recent_ancestors 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)))

            self.validate_seal(uncle)
Exemplo n.º 2
0
    def __exit__(self, exc_type, exc_value, traceback):
        if exc_value and isinstance(exc_value, VMError):
            if self.logger is not None:
                self.logger.debug(
                    "COMPUTATION ERROR: gas: %s | from: %s | to: %s | value: %s | error: %s",
                    self.msg.gas,
                    encode_hex(self.msg.sender),
                    encode_hex(self.msg.to),
                    self.msg.value,
                    exc_value,
                )
            self.error = exc_value
            self.gas_meter.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:
            if self.logger is not None:
                self.logger.debug(
                    ("COMPUTATION SUCCESS: from: %s | to: %s | value: %s | "
                     "gas-used: %s | gas-remaining: %s"),
                    encode_hex(self.msg.sender),
                    encode_hex(self.msg.to),
                    self.msg.value,
                    self.msg.gas - self.gas_meter.gas_remaining,
                    self.gas_meter.gas_remaining,
                )
Exemplo n.º 3
0
    def __enter__(self):
        if self.logger is not None:
            self.logger.debug(
                "COMPUTATION STARTING: gas: %s | from: %s | to: %s | value: %s",
                self.msg.gas,
                encode_hex(self.msg.sender),
                encode_hex(self.msg.to),
                self.msg.value,
            )

        return self
Exemplo n.º 4
0
def check_pow(block_number, mining_hash, mix_hash, nonce, difficulty):
    validate_length(mix_hash, 32)
    validate_length(mining_hash, 32)
    validate_length(nonce, 8)
    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 mistmatch; {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)
Exemplo n.º 5
0
def get_block_header_by_hash(db, block_hash):
    """
    Returns the requested block header as specified by block hash.

    Raises BlockNotFound if it is not present in the db.
    """
    validate_word(block_hash)
    try:
        block = db.get(block_hash)
    except KeyError:
        raise BlockNotFound("No block with hash {0} found".format(
            encode_hex(block_hash)))
    return rlp.decode(block, sedes=BlockHeader)
Exemplo n.º 6
0
def _apply_frontier_message(vm, message):
    snapshot = vm.snapshot()

    if message.depth > constants.STACK_DEPTH_LIMIT:
        raise StackDepthLimit("Stack depth limit reached")

    if message.should_transfer_value and message.value:
        with vm.state_db() as state_db:
            sender_balance = state_db.get_balance(message.sender)

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

            sender_balance -= message.value
            state_db.set_balance(message.sender, sender_balance)

            recipient_balance = state_db.get_balance(message.storage_address)
            recipient_balance += message.value
            state_db.set_balance(message.storage_address, recipient_balance)

        if vm.logger is not None:
            vm.logger.debug(
                "TRANSFERRED: %s from %s -> %s",
                message.value,
                encode_hex(message.sender),
                encode_hex(message.storage_address),
            )

    with vm.state_db() as state_db:
        if not state_db.account_exists(message.storage_address):
            state_db.touch_account(message.storage_address)

    computation = vm.apply_computation(message)

    if computation.error:
        vm.revert(snapshot)
    return computation
Exemplo n.º 7
0
def sstore(computation):
    slot, value = computation.stack.pop(num_items=2,
                                        type_hint=constants.UINT256)

    with computation.vm.state_db(read_only=True) as state_db:
        current_value = state_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.gas_meter.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.gas_meter.refund_gas(gas_refund)

    with computation.vm.state_db() as state_db:
        state_db.set_storage(
            address=computation.msg.storage_address,
            slot=slot,
            value=value,
        )
Exemplo n.º 8
0
def _apply_homestead_create_message(evm, message):
    if evm.state_db.account_exists(message.storage_address):
        evm.state_db.set_nonce(message.storage_address, 0)
        evm.state_db.set_code(message.storage_address, b'')
        evm.state_db.delete_storage(message.storage_address)

    if message.sender != message.origin:
        evm.state_db.increment_nonce(message.sender)

    snapshot = evm.snapshot()

    computation = evm.apply_message(message)

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

        if contract_code:
            contract_code_gas_cost = len(
                contract_code) * constants.GAS_CODEDEPOSIT
            try:
                computation.gas_meter.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
                evm.revert(snapshot)
            else:
                if evm.logger:
                    evm.logger.debug(
                        "SETTING CODE: %s -> %s",
                        encode_hex(message.storage_address),
                        contract_code,
                    )
                computation.state_db.set_code(message.storage_address,
                                              contract_code)
        return computation
Exemplo n.º 9
0
def _apply_frontier_create_message(vm, message):
    with vm.state_db() as state_db:
        if state_db.get_balance(message.storage_address) > 0:
            state_db.set_nonce(message.storage_address, 0)
            state_db.set_code(message.storage_address, b'')
            # TODO: figure out whether the following line is correct.
            state_db.delete_storage(message.storage_address)

        if message.sender != message.origin:
            state_db.increment_nonce(message.sender)

    computation = vm.apply_message(message)

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

        if contract_code:
            contract_code_gas_cost = len(
                contract_code) * constants.GAS_CODEDEPOSIT
            try:
                computation.gas_meter.consume_gas(
                    contract_code_gas_cost,
                    reason="Write contract code for CREATE",
                )
            except OutOfGas:
                computation.output = b''
            else:
                if vm.logger:
                    vm.logger.debug(
                        "SETTING CODE: %s -> %s",
                        encode_hex(message.storage_address),
                        contract_code,
                    )
                with vm.state_db() as state_db:
                    state_db.set_code(message.storage_address, contract_code)
        return computation
Exemplo n.º 10
0
def _apply_frontier_create_message(evm, message):
    if evm.block.state_db.account_exists(message.storage_address):
        evm.block.state_db.set_nonce(message.storage_address, 0)
        evm.block.state_db.set_code(message.storage_address, b'')
        evm.block.state_db.delete_storage(message.storage_address)

    if message.sender != message.origin:
        evm.block.state_db.increment_nonce(message.sender)

    computation = evm.apply_message(message)

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

        if contract_code:
            contract_code_gas_cost = len(
                contract_code) * constants.GAS_CODEDEPOSIT
            try:
                computation.gas_meter.consume_gas(
                    contract_code_gas_cost,
                    reason="Write contract code for CREATE",
                )
            except OutOfGas as err:
                computation.output = b''
            else:
                if evm.logger:
                    evm.logger.debug(
                        "SETTING CODE: %s -> %s",
                        encode_hex(message.storage_address),
                        contract_code,
                    )
                computation.evm.block.state_db.set_code(
                    message.storage_address, contract_code)
        return computation
Exemplo n.º 11
0
 def hex_hash(self):
     return encode_hex(self.hash)
Exemplo n.º 12
0
def _apply_frontier_transaction(evm, transaction):
    #
    # 1) Pre Computation
    #

    # Validate the transaction
    try:
        transaction.validate()
    except ValidationError as err:
        raise InvalidTransaction(str(err))

    evm.validate_transaction(transaction)

    gas_cost = transaction.gas * transaction.gas_price
    sender_balance = evm.state_db.get_balance(transaction.sender)

    # Buy Gas
    evm.state_db.set_balance(transaction.sender, sender_balance - gas_cost)

    # Increment Nonce
    evm.state_db.increment_nonce(transaction.sender)

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

    if transaction.to == constants.CREATE_CONTRACT_ADDRESS:
        contract_address = generate_contract_address(
            transaction.sender,
            evm.state_db.get_nonce(transaction.sender) - 1,
        )
        data = b''
        code = transaction.data
    else:
        contract_address = None
        data = transaction.data
        code = evm.state_db.get_code(transaction.to)

    if evm.logger:
        evm.logger.info(
            ("TRANSACTION: sender: %s | to: %s | value: %s | gas: %s | "
             "gas-price: %s | s: %s | r: %s | v: %s | data: %s"),
            encode_hex(transaction.sender),
            encode_hex(transaction.to),
            transaction.value,
            transaction.gas,
            transaction.gas_price,
            transaction.s,
            transaction.r,
            transaction.v,
            encode_hex(transaction.data),
        )

    message = Message(
        gas=message_gas,
        gas_price=transaction.gas_price,
        to=transaction.to,
        sender=transaction.sender,
        value=transaction.value,
        data=data,
        code=code,
        create_address=contract_address,
    )

    #
    # 2) Apply the message to the EVM.
    #
    if message.is_create:
        computation = evm.apply_create_message(message)
    else:
        computation = evm.apply_message(message)

    #
    # 2) Post Computation
    #
    if computation.error:
        # Miner Fees
        transaction_fee = transaction.gas * transaction.gas_price
        if evm.logger:
            evm.logger.debug('TRANSACTION FEE: %s', transaction_fee)
        coinbase_balance = evm.state_db.get_balance(evm.block.header.coinbase)
        evm.state_db.set_balance(
            evm.block.header.coinbase,
            coinbase_balance + transaction_fee,
        )
    else:
        # Suicide Refunds
        num_deletions = len(computation.get_accounts_for_deletion())
        if num_deletions:
            computation.gas_meter.refund_gas(constants.REFUND_SUICIDE *
                                             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:
            if evm.logger:
                evm.logger.debug(
                    'TRANSACTION REFUND: %s -> %s',
                    gas_refund_amount,
                    encode_hex(message.sender),
                )

            sender_balance = evm.state_db.get_balance(message.sender)
            evm.state_db.set_balance(message.sender,
                                     sender_balance + gas_refund_amount)

        # Miner Fees
        transaction_fee = (transaction.gas - gas_remaining -
                           gas_refund) * transaction.gas_price
        if evm.logger:
            evm.logger.debug(
                'TRANSACTION FEE: %s -> %s',
                transaction_fee,
                encode_hex(evm.block.header.coinbase),
            )
        coinbase_balance = evm.state_db.get_balance(evm.block.header.coinbase)
        evm.state_db.set_balance(
            evm.block.header.coinbase,
            coinbase_balance + transaction_fee,
        )

    # Suicides
    for account, beneficiary in computation.get_accounts_for_deletion():
        # TODO: need to figure out how we prevent multiple suicides from
        # the same account and if this is the right place to put this.
        if evm.logger is not None:
            evm.logger.debug('DELETING ACCOUNT: %s', encode_hex(account))

        evm.state_db.set_balance(account, 0)
        evm.state_db.delete_account(account)

    return computation