def calculate_hash_difficulty(self): block = self.get_latest_block() current_difficulty = difficulty.decompress(block.difficulty) if block.index > 0 and block.index % config.DIFFICULTY_ADJUSTMENT_SPAN == 0: block_delta = self.get_block(block.index - config.DIFFICULTY_ADJUSTMENT_SPAN) timestamp_delta = (block.timestamp - block_delta.timestamp ) // config.DIFFICULTY_ADJUSTMENT_SPAN numerator, denominator = (timestamp_delta, config.TARGET_TIME_PER_BLOCK) if denominator > config.DIFFICULTY_CHANGE_RATIO_LIMIT * numerator: numerator, denominator = (1, config.DIFFICULTY_CHANGE_RATIO_LIMIT) try: new_difficulty = difficulty.normalize( difficulty.multiply(current_difficulty, numerator, denominator)) if difficulty.less_or_equal(config.MINIMUM_HASH_DIFFICULTY, new_difficulty): new_difficulty = config.MINIMUM_HASH_DIFFICULTY except OverflowError: new_difficulty = config.MINIMUM_HASH_DIFFICULTY return new_difficulty else: return current_difficulty
async def mine_next_block(self): if self.miner is not None and not self.miner.closed: block = self.get_next_block() await self.miner.send_json({ 'id': block.index, 'data': block.serialize(header_only = True).hex(), 'nonceOffset': 76, 'nonceSize': 4, 'timestampOffset': 72, 'timestampSize': 4, 'target': difficulty.decompress(block.difficulty).hex() })
def is_valid_block(self, block): # Check block size if len(block.serialize()) > config.MAX_BLOCK_SIZE: raise BlockTooLarge() # Check block validity. if not block.valid(): raise InvalidBlock() # Check if it isn't an old block if block.index < self.get_height(): raise BlockOld() # Check if minimum PoW has been done on it block_hash = block.calculate_hash() claimed_difficulty = difficulty.decompress(block.difficulty) if not difficulty.less_or_equal(block_hash, claimed_difficulty) or \ not difficulty.less_or_equal(claimed_difficulty, config.MINIMUM_HASH_DIFFICULTY): raise InvalidDifficulty() return True
def is_next_block(self, block): # Check genesis block if self.get_height() == 0 and block == genesis.genesis_block(): return # Check if it is a valid block self.is_valid_block(block) # Check if it is the next block if block.index != self.get_height(): raise InvalidIndex() # Check if it points to the previous block if block.previous_hash != self.get_latest_block().calculate_hash(): raise InvalidPreviousHash() # Check if timestamp is logical if block.index >= config.BLOCKS_CLOCK_CHECK: previous_blocks = self.get_block_range( block.index - config.BLOCKS_CLOCK_CHECK, block.index) timestamps = [b.timestamp for b in previous_blocks] med = misc.median(timestamps) if block.timestamp <= med: raise InvalidTimestamp() # Check if work has been done on the block claimed_difficulty = difficulty.decompress(block.difficulty) if claimed_difficulty != self.calculate_hash_difficulty(): raise InvalidDifficulty() # Check if all transactions are valid payers = dict() hashes = set() fees = 0 for transaction in block.transactions[:-2]: # Check if the transaction has minimum validity in this blockchain. self.is_valid_transaction(transaction) # Check if transaction is targeting this block if transaction.target != block.index: raise InvalidTransactionTarget() # Check if it is not a duplicate transaction transaction_hash = transaction.calculate_hash() if transaction_hash not in hashes: hashes.add(transaction_hash) else: raise DuplicatedTransactionsFound() source = self.resolve(transaction.source) destination = self.resolve(transaction.destination) if source not in payers: payers[source] = 0 if destination not in payers: payers[destination] = 0 payers[source] += transaction.amount + transaction.fee payers[destination] -= transaction.amount fees += transaction.fee # Check if payers have enough balance for payer in payers: balance = self.get_balance(payer) if payers[payer] > balance: raise BalanceNotEnough() # Check fee transaction fee_transaction = block.transactions[-2] if not fee_transaction.valid( ) or fee_transaction.source != config.NOWHERE_NAME or fee_transaction.amount != fees or self.resolve( fee_transaction.destination) is None: raise InvalidFeeTransaction() # Check reward transaction reward_transaction = block.transactions[-1] if not reward_transaction.valid( ) or reward_transaction.source != config.SUPPLY_NAME or reward_transaction.amount != self.calculate_reward( ) or self.resolve(reward_transaction.destination) is None: raise InvalidRewardTransaction()
def mine_block(block): diff = difficulty.decompress(block.difficulty) block.nonce = 0 while not difficulty.less_or_equal(block.calculate_hash(), diff): block.nonce += 1
def test_compress_decompress(self): for i in range(32): diff = b'\0' * i + b'\x2e' + b'\0' * (31 - i) self.assertEquals(diff, difficulty.decompress(difficulty.compress(diff)))