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)
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, )
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
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)
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)
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
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, )
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
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
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
def hex_hash(self): return encode_hex(self.hash)
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