def validate_mining_nonce(self, block, enable_logging=False): parent_metadata = self.state.get_block_metadata(block.prev_headerhash) parent_block = self.state.get_block(block.prev_headerhash) measurement = self.state.get_measurement(block.timestamp, block.prev_headerhash, parent_metadata) diff, target = DifficultyTracker.get( measurement=measurement, parent_difficulty=parent_metadata.block_difficulty) if enable_logging: logger.debug('-----------------START--------------------') logger.debug('Validate #%s', block.block_number) logger.debug('block.timestamp %s', block.timestamp) logger.debug('parent_block.timestamp %s', parent_block.timestamp) logger.debug('parent_block.difficulty %s', UInt256ToString(parent_metadata.block_difficulty)) logger.debug('diff : %s | target : %s', UInt256ToString(diff), target) logger.debug('-------------------END--------------------') if not self.verify_input_cached(block.mining_blob, target): if enable_logging: logger.warning("PoW verification failed") qn = Qryptonight() tmp_hash = qn.hash(block.mining_blob) logger.warning("{}".format(tmp_hash)) logger.debug('%s', block.to_json()) return False return True
def __init__(self, chain_manager, pre_block_logic, mining_address: bytes, mining_thread_count): self.lock = threading.RLock() self.qryptonight_7_miner = CNv1Miner(pre_block_logic, mining_address, mining_thread_count, self.lock) self.qrandomx_miner = QRandomXMiner(chain_manager, pre_block_logic, mining_address, mining_thread_count, self.lock) self._qn = Qryptonight() self._chain_manager = chain_manager self._pre_block_logic = pre_block_logic self._mining_block = None self._current_difficulty = None self._current_target = None self._measurement = None self._current_miner = self.qrandomx_miner
def validate_mining_nonce(self, blockheader: BlockHeader, enable_logging=True): with self.lock: parent_metadata = self.get_block_metadata( blockheader.prev_headerhash) parent_block = self._state.get_block(blockheader.prev_headerhash) measurement = self.get_measurement(blockheader.timestamp, blockheader.prev_headerhash, parent_metadata) diff, target = DifficultyTracker.get( measurement=measurement, parent_difficulty=parent_metadata.block_difficulty) if enable_logging: logger.debug('-----------------START--------------------') logger.debug('Validate #%s', blockheader.block_number) logger.debug('block.timestamp %s', blockheader.timestamp) logger.debug('parent_block.timestamp %s', parent_block.timestamp) logger.debug('parent_block.difficulty %s', UInt256ToString(parent_metadata.block_difficulty)) logger.debug('diff %s', UInt256ToString(diff)) logger.debug('target %s', bin2hstr(target)) logger.debug('-------------------END--------------------') if not PoWValidator().verify_input(blockheader.mining_blob, target): if enable_logging: logger.warning("PoW verification failed") qn = Qryptonight() tmp_hash = qn.hash(blockheader.mining_blob) logger.warning("{}".format(bin2hstr(tmp_hash))) logger.debug('%s', blockheader.to_json()) return False return True
def _get_qryptonight_hash(self, blob): qn = Qryptonight() return bytes(qn.hash(blob))
def validate(self, chain_manager, future_blocks: OrderedDict) -> bool: if chain_manager.get_block_is_duplicate(self): logger.warning('Duplicate Block #%s %s', self.block_number, bin2hstr(self.headerhash)) return False parent_block = chain_manager.get_block(self.prev_headerhash) dev_config = chain_manager.get_config_by_block_number(self.block_number) # If parent block not found in state, then check if its in the future block list if not parent_block: try: parent_block = future_blocks[self.prev_headerhash] except KeyError: logger.warning('Parent block not found') logger.warning('Parent block headerhash %s', bin2hstr(self.prev_headerhash)) return False if not self._validate_parent_child_relation(parent_block): logger.warning('Failed to validate blocks parent child relation') return False if not chain_manager.validate_mining_nonce(self.blockheader, dev_config): logger.warning('Failed PoW Validation') return False if len(self.transactions) == 0: return False try: state_container = chain_manager.new_state_container(set(), self.block_number, False, None) coinbase_txn = Transaction.from_pbdata(self.transactions[0]) coinbase_amount = coinbase_txn.amount if not coinbase_txn.validate_all(state_container): return False if self.block_number != 2078158: for proto_tx in self.transactions[1:]: if proto_tx.WhichOneof('transactionType') == 'coinbase': logger.warning("Multiple coinbase transaction found") return False except Exception as e: logger.warning('Exception %s', e) return False # Build transaction merkle tree, calculate fee reward, and then see if BlockHeader also agrees. hashedtransactions = [] for tx in self.transactions: tx = Transaction.from_pbdata(tx) hashedtransactions.append(tx.txhash) fee_reward = 0 for index in range(1, len(self.transactions)): fee_reward += self.transactions[index].fee qn = Qryptonight() seed_block = chain_manager.get_block_by_number(qn.get_seed_height(self.block_number)) self.blockheader._seed_height = seed_block.block_number self.blockheader._seed_hash = seed_block.headerhash if not self.blockheader.validate(fee_reward, coinbase_amount, merkle_tx_hash(hashedtransactions), dev_config): return False return True
class Miner: def __init__(self, chain_manager, pre_block_logic, mining_address: bytes, mining_thread_count): self.lock = threading.RLock() self.qryptonight_7_miner = CNv1Miner(pre_block_logic, mining_address, mining_thread_count, self.lock) self.qrandomx_miner = QRandomXMiner(chain_manager, pre_block_logic, mining_address, mining_thread_count, self.lock) self._qn = Qryptonight() self._chain_manager = chain_manager self._pre_block_logic = pre_block_logic self._mining_block = None self._current_difficulty = None self._current_target = None self._measurement = None self._current_miner = self.qrandomx_miner def isRunning(self): return self._current_miner.isRunning def get_miner(self, height, dev_config: DevConfig): if height < dev_config.hard_fork_heights[0]: self._current_miner = self.qryptonight_7_miner return self.qryptonight_7_miner else: self._current_miner = self.qrandomx_miner return self.qrandomx_miner def solutionAvailable(self): return self._current_miner.solutionAvailable() def prepare_next_unmined_block_template(self, mining_address, tx_pool, parent_block: Block, parent_difficulty, dev_config: DevConfig): miner = self.get_miner(parent_block.block_number + 1, dev_config) try: logger.debug('Miner-Try - prepare_next_unmined_block_template') with self.lock: logger.debug('Miner-Locked - prepare_next_unmined_block_template') logger.debug('Miner-TryCancel - prepare_next_unmined_block_template') miner.cancel() logger.debug('Miner-Cancel - prepare_next_unmined_block_template') self._mining_block = self.create_block(last_block=parent_block, mining_nonce=0, tx_pool=tx_pool, miner_address=mining_address) parent_metadata = self._chain_manager.get_block_metadata(parent_block.headerhash) self._measurement = self._chain_manager.get_measurement(dev_config, self._mining_block.timestamp, self._mining_block.prev_headerhash, parent_metadata) self._current_difficulty, self._current_target = DifficultyTracker.get( measurement=self._measurement, parent_difficulty=parent_difficulty, dev_config=dev_config) except Exception as e: logger.warning("Exception in start_mining") logger.exception(e) def start_mining(self, parent_block: Block, parent_difficulty, dev_config: DevConfig): logger.debug('!!! Mine #{} | {} ({}) | {} -> {} | {} '.format( self._mining_block.block_number, self._measurement, self._mining_block.timestamp - parent_block.timestamp, UInt256ToString(parent_difficulty), UInt256ToString(self._current_difficulty), bin2hstr(bytearray(self._current_target)) )) logger.debug('!!! Mine #{} | blob: {}'.format( self._mining_block.block_number, bin2hstr(bytearray(self._mining_block.mining_blob(dev_config))) )) miner = self.get_miner(parent_block.block_number + 1, dev_config) miner.start_mining(self._mining_block, self._current_target, dev_config) def create_block(self, last_block, mining_nonce, tx_pool: TransactionPool, miner_address) -> Optional[Block]: seed_block = self._chain_manager.get_block_by_number(self._qn.get_seed_height(last_block.block_number + 1)) dev_config = self._chain_manager.get_config_by_block_number(block_number=last_block.block_number + 1) dummy_block = Block.create(dev_config=dev_config, block_number=last_block.block_number + 1, prev_headerhash=last_block.headerhash, prev_timestamp=last_block.timestamp, transactions=[], miner_address=miner_address, seed_height=seed_block.block_number, seed_hash=seed_block.headerhash) dummy_block.set_nonces(dev_config, mining_nonce, 0) t_pool2 = tx_pool.transactions block_size = dummy_block.size block_size_limit = self._chain_manager.get_block_size_limit(last_block, dev_config) transactions = [] state_container = self._chain_manager.new_state_container(set(), last_block.block_number, True, None) for tx_set in t_pool2: tx = tx_set[1].transaction # Skip Transactions for later, which doesn't fit into block if block_size + tx.size + dev_config.tx_extra_overhead > block_size_limit: continue if not self._chain_manager.update_state_container(tx, state_container): logger.error("[create_block] Error updating state_container") return None if not tx.validate_all(state_container, check_nonce=False): if not state_container.revert_update(): return None tx_pool.remove_tx_from_pool(tx) continue if not self._chain_manager.apply_txn(tx, state_container): logger.error("[create_block] Failed to apply txn") if not state_container.revert_update(): return None continue addr_from_pk_state = state_container.addresses_state[tx.addr_from] addr_from_pk = Transaction.get_slave(tx) if addr_from_pk: addr_from_pk_state = state_container.addresses_state[addr_from_pk] tx._data.nonce = addr_from_pk_state.nonce block_size += tx.size + dev_config.tx_extra_overhead transactions.append(tx) block = Block.create(dev_config=dev_config, block_number=last_block.block_number + 1, prev_headerhash=last_block.headerhash, prev_timestamp=last_block.timestamp, transactions=transactions, miner_address=miner_address, seed_height=seed_block.block_number, seed_hash=seed_block.headerhash) return block def get_block_to_mine(self, wallet_address, tx_pool, last_block, last_block_difficulty) -> list: dev_config = self._chain_manager.get_config_by_block_number(last_block.block_number + 1) try: mining_address = bytes(hstr2bin(wallet_address[1:].decode())) if not OptimizedAddressState.address_is_valid(mining_address): raise ValueError("[get_block_to_mine] Invalid Wallet Address %s", wallet_address) except Exception as e: raise ValueError("Error while decoding wallet address %s", e) if self._mining_block: if last_block.headerhash == self._mining_block.prev_headerhash: if self._mining_block.transactions[0].coinbase.addr_to == mining_address: return [bin2hstr(self._mining_block.mining_blob(dev_config)), int(bin2hstr(self._current_difficulty), 16)] else: self._mining_block.update_mining_address(dev_config, mining_address) # Updates only Miner Address self.prepare_next_unmined_block_template(mining_address, tx_pool, last_block, last_block_difficulty, dev_config=dev_config) return [bin2hstr(self._mining_block.mining_blob(dev_config)), int(bin2hstr(self._current_difficulty), 16)] def submit_mined_block(self, blob: bytes) -> bool: dev_config = self._chain_manager.get_config_by_block_number(self._mining_block.block_number - 1) if not self._mining_block.verify_blob(blob, dev_config): return False blockheader = copy.deepcopy(self._mining_block.blockheader) blockheader.set_mining_nonce_from_blob(blob, dev_config) dev_config = self._chain_manager.get_config_by_block_number(blockheader.block_number) if not self._chain_manager.validate_mining_nonce(blockheader, dev_config): return False self._mining_block.set_nonces(dev_config, blockheader.mining_nonce, blockheader.extra_nonce) cloned_block = copy.deepcopy(self._mining_block) return self._pre_block_logic(cloned_block) def cancel(self): self.qryptonight_7_miner.cancel() self.qrandomx_miner.cancel()
def __init__(self, qrlnode: QRLNode): self.qrlnode = qrlnode self._qn = Qryptonight()
class MiningAPIService(MiningAPIServicer): MAX_REQUEST_QUANTITY = 100 def __init__(self, qrlnode: QRLNode): self.qrlnode = qrlnode self._qn = Qryptonight() @GrpcExceptionWrapper(qrlmining_pb2.GetBlockMiningCompatibleResp, StatusCode.UNKNOWN) def GetBlockMiningCompatible( self, request: qrlmining_pb2.GetBlockMiningCompatibleReq, context) -> qrlmining_pb2.GetBlockMiningCompatibleResp: blockheader, block_metadata = self.qrlnode.get_blockheader_and_metadata( request.height) response = qrlmining_pb2.GetBlockMiningCompatibleResp() if blockheader is not None and block_metadata is not None: response = qrlmining_pb2.GetBlockMiningCompatibleResp( blockheader=blockheader.pbdata, blockmetadata=block_metadata.pbdata) return response @GrpcExceptionWrapper(qrlmining_pb2.GetLastBlockHeaderResp, StatusCode.UNKNOWN) def GetLastBlockHeader(self, request: qrlmining_pb2.GetLastBlockHeaderReq, context) -> qrlmining_pb2.GetLastBlockHeaderResp: response = qrlmining_pb2.GetLastBlockHeaderResp() blockheader, block_metadata = self.qrlnode.get_blockheader_and_metadata( request.height) response.difficulty = int(bin2hstr(block_metadata.block_difficulty), 16) response.height = self.qrlnode.block_height response.timestamp = blockheader.timestamp response.reward = blockheader.block_reward + blockheader.fee_reward response.hash = bin2hstr(blockheader.headerhash) response.depth = self.qrlnode.block_height - blockheader.block_number return response @GrpcExceptionWrapper(qrlmining_pb2.GetBlockToMineResp, StatusCode.UNKNOWN) def GetBlockToMine(self, request: qrlmining_pb2.GetBlockToMineReq, context) -> qrlmining_pb2.GetBlockToMineResp: response = qrlmining_pb2.GetBlockToMineResp() blocktemplate_blob_and_difficulty = self.qrlnode.get_block_to_mine( request.wallet_address) if blocktemplate_blob_and_difficulty: response.blocktemplate_blob = blocktemplate_blob_and_difficulty[0] response.difficulty = blocktemplate_blob_and_difficulty[1] response.height = self.qrlnode.block_height + 1 response.reserved_offset = config.dev.extra_nonce_offset seed_block_number = self._qn.get_seed_height(response.height) response.seed_hash = bin2hstr( self.qrlnode.get_block_header_hash_by_number( seed_block_number)) return response @GrpcExceptionWrapper(qrlmining_pb2.GetBlockToMineResp, StatusCode.UNKNOWN) def SubmitMinedBlock(self, request: qrlmining_pb2.SubmitMinedBlockReq, context) -> qrlmining_pb2.SubmitMinedBlockResp: response = qrlmining_pb2.SubmitMinedBlockResp() response.error = not self.qrlnode.submit_mined_block(request.blob) return response
def _get_qryptonight_hash(blob): qn = Qryptonight() qnhash = bytes(qn.hash(blob)) return qnhash
def _get_qryptonight_hash(block_number, seed_height, seed_hash, blob): qn = Qryptonight() qnhash = bytes(qn.hash(block_number, seed_height, seed_hash, blob)) return qnhash