def update_counters(self): time_diff = ntp.getTime() - self.last_rate_limit_update if time_diff > 60: self.out_counter = 0 self.in_counter = 0 self.last_rate_limit_update = ntp.getTime() return
def block_received(self, source, block: Block): self.pow.last_pb_time = ntp.getTime() logger.info('>>> Received Block #%d %s', block.block_number, bin2hstr(block.headerhash)) if source != self._target_channel: if self._target_channel is None: logger.warning('Received block and target channel is None') else: logger.warning('Received block from unexpected peer') logger.warning('Expected peer: %s', self._target_channel.peer) logger.warning('Found peer: %s', source.peer) return if block.block_number != self._last_requested_block_number: logger.warning('Did not match %s', self._last_requested_block_number) self._qrl_node.peer_manager.ban_channel(source) return target_start_blocknumber = self._target_node_header_hash.block_number expected_headerhash = self._target_node_header_hash.headerhashes[ block.block_number - target_start_blocknumber] if block.headerhash != expected_headerhash: logger.warning('Did not match headerhash') logger.warning('Expected headerhash %s', expected_headerhash) logger.warning('Found headerhash %s', block.headerhash) self._qrl_node.peer_manager.ban_channel(source) return if not block.validate(self._chain_manager, self.pow.future_blocks): logger.warning('Syncing Failed: Block Validation Failed') self._qrl_node.peer_manager.ban_channel(source) return if self._chain_manager.add_block(block, check_stale=False): if self._chain_manager.last_block.headerhash == block.headerhash: self.pow.suspend_mining_timestamp = ntp.getTime( ) + config.dev.sync_delay_mining else: logger.warning('Failed to Add Block') self._qrl_node.peer_manager.ban_channel(source) return try: reactor.download_monitor.cancel() except Exception as e: logger.warning("PB: %s", e) if self.is_syncing_finished(): return self._last_requested_block_number += 1 self.peer_fetch_block()
def create(blocknumber: int, prev_blockheaderhash: bytes, hashedtransactions: bytes, fee_reward: int): """ Create a block header based on the parameters >>> BlockHeader.create(blocknumber=1, ... prev_blockheaderhash=b'headerhash', ... hashedtransactions=b'some_data', fee_reward=1) is not None True >>> b=BlockHeader.create(blocknumber=1, ... prev_blockheaderhash=b'headerhash', ... hashedtransactions=b'some_data', fee_reward=1) >>> b.epoch 0 """ bh = BlockHeader() bh._data.block_number = blocknumber if bh._data.block_number != 0: bh._data.timestamp_seconds = int(ntp.getTime()) if bh._data.timestamp_seconds == 0: logger.warning('Failed to get NTP timestamp') return bh._data.hash_header_prev = prev_blockheaderhash bh._data.merkle_root = hashedtransactions bh._data.reward_fee = fee_reward bh._data.reward_block = bh.block_reward_calc(blocknumber) bh.set_mining_nonce(0) return bh
def pre_block_logic(self, block: Block): logger.debug('LOCK - TRY - pre_block_logic') with self.miner.lock: logger.debug('LOCK - LOCKED - pre_block_logic') if not block.validate(self.chain_manager, self.future_blocks): logger.warning('Block Validation failed for #%s %s', block.block_number, bin2hstr(block.headerhash)) return False dev_config = self.chain_manager.get_config_by_block_number(block.block_number) if block.is_future_block(dev_config): delay = abs(block.timestamp - ntp.getTime()) + 1 reactor.callLater(delay, self.process_future_blocks) self.add_future_block(block) return True logger.debug('Inside add_block') result = self.chain_manager.add_block(block) logger.debug('trigger_miner %s', self.chain_manager.trigger_miner) if self.chain_manager.trigger_miner: logger.debug('try last block') last_block = self.chain_manager.last_block logger.debug('got last block') self._mine_next(last_block) if not result: logger.debug('Block Rejected %s %s', block.block_number, bin2hstr(block.headerhash)) return False reactor.callLater(0, self.broadcast_block, block) logger.debug('LOCK - RELEASE - pre_block_logic') return result
def __init__(self, db_state: State, mining_address: bytes): self.start_time = ntp.getTime() self.db_state = db_state self._sync_state = SyncState() self.peer_manager = P2PPeerManager() self.peer_manager.load_peer_addresses() self.peer_manager.register(P2PPeerManager.EventType.NO_PEERS, self.connect_peers) self.p2pchain_manager = P2PChainManager() self.tx_manager = P2PTxManagement() self._chain_manager = None # FIXME: REMOVE. This is temporary self._p2pfactory = None # FIXME: REMOVE. This is temporary self._pow = None self.mining_address = mining_address banned_peers_filename = os.path.join(config.user.wallet_dir, config.dev.banned_peers_filename) self._banned_peers = ExpiringSet( expiration_time=config.user.ban_minutes * 60, filename=banned_peers_filename) reactor.callLater(10, self.monitor_chain_state)
def validate(self): if self.ttl < self.ttr: return False if ntp.getTime() > self.ttl: return False return True
def pre_block_logic(self, block: Block): logger.debug('Checking miner lock') with self._miner_lock: if not block.validate(self.chain_manager.state, self.future_blocks): logger.warning('Block Validation failed for #%s %s', block.block_number, bin2hstr(block.headerhash)) return if block.is_future_block(): delay = abs(block.timestamp - ntp.getTime()) + 1 reactor.callLater(delay, self.process_future_blocks) self.add_future_block(block) return logger.debug('Inside add_block') result = self.chain_manager.add_block(block) logger.debug('trigger_miner %s', self.chain_manager.trigger_miner) if self.chain_manager.trigger_miner: self.mine_next(self.chain_manager.last_block) if not result: logger.debug('Block Rejected %s %s', block.block_number, bin2hstr(block.headerhash)) return reactor.callLater(0, self.broadcast_block, block)
def create(blocknumber: int, prev_headerhash: bytes, prev_timestamp: int, hashedtransactions: bytes, fee_reward: int): bh = BlockHeader() bh._data.block_number = blocknumber if bh._data.block_number != 0: bh._data.timestamp_seconds = int(ntp.getTime()) # If current block timestamp is less than or equals to the previous block timestamp # then set current block timestamp 1 sec higher than prev_timestamp if bh._data.timestamp_seconds <= prev_timestamp: bh._data.timestamp_seconds = prev_timestamp + 1 if bh._data.timestamp_seconds == 0: logger.warning('Failed to get NTP timestamp') return else: bh._data.timestamp_seconds = prev_timestamp # Set timestamp for genesis block bh._data.hash_header_prev = prev_headerhash bh._data.merkle_root = hashedtransactions bh._data.reward_fee = fee_reward bh._data.reward_block = bh.block_reward_calc(blocknumber) bh.set_nonces(0, 0) return bh
def validate(self): if self.ttl < self.ttr: return False if ntp.getTime() > self.ttl: return False return True
def create(peer_ip: bytes, port: int): peer = PeerInfo() peer._data.peer_ip = peer_ip peer._data.port = port peer._data.credibility = 0 # TODO: To be used later, to assign higher points to peer with good behavior peer._data.extend([ntp.getTime()]) return peer
def handle_message_received(source, message: qrllegacy_pb2.LegacyMessage): """ Message Receipt This function accepts message receipt from peer, checks if the message hash already been received or not. In case its a already received message, it is ignored. Otherwise the request is made to get the full message. :return: """ mr_data = message.mrData msg_hash = mr_data.hash # FIXME: Separate into respective message handlers if mr_data.type not in MessageReceipt.allowed_types: return if mr_data.type == qrllegacy_pb2.LegacyMessage.TX and source.factory.sync_state.state != ESyncState.synced: return if mr_data.type == qrllegacy_pb2.LegacyMessage.TX: if ntp.getTime() < source.factory.pow.suspend_mining_timestamp: return if source.factory._chain_manager.tx_pool.is_full_pending_transaction_pool( ): logger.warning( 'TX pool size full, incoming tx dropped. mr hash: %s', bin2hstr(msg_hash)) return if mr_data.type == qrllegacy_pb2.LegacyMessage.BK: if mr_data.block_number > source.factory.chain_height + config.dev.max_margin_block_number: logger.debug('Skipping block #%s as beyond lead limit', mr_data.block_number) return if mr_data.block_number < source.factory.chain_height - config.dev.min_margin_block_number: logger.debug('Skipping block #%s as beyond the limit', mr_data.block_number) return if not source.factory.is_block_present(mr_data.prev_headerhash): logger.debug('Skipping block #%s as prev_headerhash not found', mr_data.block_number) return if source.factory.master_mr.contains(msg_hash, mr_data.type): return source.factory.master_mr.add_peer(msg_hash, mr_data.type, source, mr_data) if source.factory.master_mr.is_callLater_active( msg_hash): # Ignore if already requested return source.factory.request_full_message(mr_data)
def handle_chain_state(self, source, message: qrllegacy_pb2.LegacyMessage): P2PBaseObserver._validate_message(message, qrllegacy_pb2.LegacyMessage.CHAINSTATE) message.chainStateData.timestamp = ntp.getTime() # Receiving time if len(message.chainStateData.cumulative_difficulty) != 32: logger.warning('Invalid Cumulative Difficulty sent by peer') source.loseConnection() return self._peer_node_status[source] = message.chainStateData
def ban_peer(self, peer_obj): ip = peer_obj.peer_ip ban_time = ntp.getTime() + (config.user.ban_minutes * 60) banned_peers = self._get_banned_peers() banned_peers[ip] = ban_time self._update_banned_peers(banned_peers) logger.warning('Banned %s', peer_obj.peer_ip) peer_obj.loseConnection()
def _update_banned_peers(self, banned_peers): current_time = ntp.getTime() ip_list = list(banned_peers.keys()) for ip in ip_list: if current_time > banned_peers[ip]: del banned_peers[ip] self._put_banned_peers(banned_peers)
def __init__(self, tx: Transaction, block_number: int, timestamp: int = None): self._transaction = tx self._block_number = block_number self._timestamp = timestamp if not self._timestamp: self._timestamp = ntp.getTime()
def update(self): total_len = len(self.encrypted_ephemeral_message_list) for index in range(total_len): encrypted_ephemeral = self._data.encrypted_ephemeral_message_list[index] if ntp.getTime() > encrypted_ephemeral.ttl: del self._data.encrypted_ephemeral_message_list[index] index -= 1 continue
def _update_banned_peers(self, banned_peers): # FIXME: Move to another class. Group peer banning there current_time = ntp.getTime() ip_list = list(banned_peers.keys()) for ip in ip_list: if current_time > banned_peers[ip]: del banned_peers[ip] self._put_banned_peers(banned_peers)
def ban_peer(self, peer_obj): # FIXME: Move to another class. Group peer banning there ip = peer_obj.peer_ip ban_time = ntp.getTime() + (config.user.ban_minutes * 60) banned_peers = self._get_banned_peers() banned_peers[ip] = ban_time self._update_banned_peers(banned_peers) logger.warning('Banned %s', peer_obj.peer_ip) peer_obj.loseConnection()
def monitor_bk(self): # FIXME: Too many magic numbers / timing constants # FIXME: This is obsolete time_diff1 = ntp.getTime() - self.last_pow_cycle if 90 < time_diff1: if self.sync_state.state == ESyncState.unsynced: if ntp.getTime() - self.last_bk_time > 120: self.last_pow_cycle = ntp.getTime() logger.info(' POW cycle activated by monitor_bk() ') self.update_node_state(ESyncState.synced) reactor.monitor_bk = reactor.callLater(60, self.monitor_bk) return time_diff2 = ntp.getTime() - self.last_pb_time if self.sync_state.state == ESyncState.syncing and time_diff2 > 60: self.update_node_state(ESyncState.unsynced) self.epoch_diff = -1 reactor.monitor_bk = reactor.callLater(60, self.monitor_bk)
def monitor_chain_state(self): current_timestamp = ntp.getTime() for channel in self._channels: if channel not in self._peer_node_status: channel.loseConnection() continue delta = current_timestamp - self._peer_node_status[channel].timestamp if delta > config.user.chain_state_timeout: del self._peer_node_status[channel] logger.debug('>>>> No State Update [%18s] %2.2f (TIMEOUT)', channel.addr_remote, delta) channel.loseConnection()
def new_channel(self, channel): self._channels.append(channel) self._peer_node_status[channel] = qrl_pb2.NodeChainState(block_number=0, header_hash=b'', cumulative_difficulty=b'\x00' * 32, timestamp=ntp.getTime()) channel.register(qrllegacy_pb2.LegacyMessage.VE, self.handle_version) channel.register(qrllegacy_pb2.LegacyMessage.PL, self.handle_peer_list) channel.register(qrllegacy_pb2.LegacyMessage.CHAINSTATE, self.handle_chain_state) channel.register(qrllegacy_pb2.LegacyMessage.SYNC, self.handle_sync) channel.register(qrllegacy_pb2.LegacyMessage.P2P_ACK, self.handle_p2p_acknowledgement)
def monitor_chain_state(self): # FIXME: Not sure this belongs to peer management current_timestamp = ntp.getTime() for channel in self._channels: if channel not in self._peer_node_status: channel.loseConnection() continue delta = current_timestamp - self._peer_node_status[channel].timestamp if delta > config.user.chain_state_timeout: del self._peer_node_status[channel] logger.debug('>>>> No State Update [%18s] %2.2f (TIMEOUT)', channel.peer, delta) channel.loseConnection()
def _refresh(self): # TODO: refactored from banned peers. Rework to use a priority queue instead current_time = ntp.getTime() len_before = len(self._data) self._data = {k: v for k, v in self._data.items() if v > current_time} len_after = len(self._data) # FIXME: Drop peers beyond configuration limit if len_before != len_after: self._store()
def mine_next(self, parent_block): if ntp.getTime() < self.suspend_mining_timestamp: return if config.user.mining_enabled: parent_metadata = self.chain_manager.state.get_block_metadata( parent_block.headerhash) self.miner.prepare_next_unmined_block_template( mining_address=self.mining_address, tx_pool=self.chain_manager.tx_pool, parent_block=parent_block, parent_difficulty=parent_metadata.block_difficulty) logger.info('Mining Block #%s', parent_block.block_number + 1) self.miner.start_mining(parent_block, parent_metadata.block_difficulty)
def validate(self, fee_reward, coinbase_amount, tx_merkle_root): current_time = ntp.getTime() allowed_timestamp = current_time + config.dev.block_lead_timestamp if self.timestamp > allowed_timestamp: logger.warning( 'BLOCK timestamp is more than the allowed block lead timestamp' ) logger.warning('Block timestamp %s ', self.timestamp) logger.warning('threshold timestamp %s', allowed_timestamp) return False if self.timestamp < config.user.genesis_timestamp: logger.warning('Timestamp lower than genesis timestamp') logger.warning('Genesis Timestamp %s', config.user.genesis_timestamp) logger.warning('Block Timestamp %s', self.timestamp) return False generated_hash = self.generate_headerhash() if generated_hash != self.headerhash: logger.warning('received: {}'.format(bin2hstr(self.headerhash))) logger.warning('calculated: {}'.format(bin2hstr(generated_hash))) logger.warning('Headerhash false for block: failed validation') return False if self.block_reward != self.block_reward_calc(self.block_number): logger.warning( 'Block reward incorrect for block: failed validation') return False if self.fee_reward != fee_reward: logger.warning( 'Block Fee reward incorrect for block: failed validation') return False if self.block_reward + self.fee_reward != coinbase_amount: logger.warning( 'Block_reward + fee_reward doesnt sums up to coinbase_amount') return False if self.timestamp == 0 and self.block_number > 0: logger.warning('Invalid block timestamp ') return False if self.tx_merkle_root != tx_merkle_root: logger.warning('Invalid TX Merkle Root') return False return True
def handle_chain_state(self, source, message: qrllegacy_pb2.LegacyMessage): # FIXME: Not sure this belongs to peer management P2PBaseObserver._validate_message( message, qrllegacy_pb2.LegacyMessage.CHAINSTATE) message.chainStateData.timestamp = ntp.getTime() # Receiving time try: UInt256ToString(message.chainStateData.cumulative_difficulty) except ValueError: logger.warning('Invalid Cumulative Difficulty sent by peer') source.loseConnection() return self._peer_node_status[source] = message.chainStateData
def __init__(self): self._buffer = bytes() # Need to use composition instead of inheritance here self._observable = P2PObservable(self) self.last_rate_limit_update = 0 self.rate_limit = config.user.peer_rate_limit self.in_counter = 0 self.out_counter = 0 self.bytes_sent = 0 self.outgoing_queue = PriorityQueue(maxsize=config.user.p2p_q_size) self._connected_at = ntp.getTime() self._valid_message_count = 0
def monitor_chain_state(self): self.peer_manager.monitor_chain_state() last_block = self._chain_manager.last_block block_metadata = self._chain_manager.get_block_metadata(last_block.headerhash) node_chain_state = qrl_pb2.NodeChainState(block_number=last_block.block_number, header_hash=last_block.headerhash, cumulative_difficulty=bytes(block_metadata.cumulative_difficulty), version=config.dev.version, timestamp=ntp.getTime()) self.peer_manager.broadcast_chain_state(node_chain_state=node_chain_state) channel = self.peer_manager.get_better_difficulty(block_metadata.cumulative_difficulty) logger.debug('Got better difficulty %s', channel) if channel: logger.debug('Connection id >> %s', channel.peer) channel.send_get_headerhash_list(self._chain_manager.height) reactor.callLater(config.user.chain_state_broadcast_period, self.monitor_chain_state)
def __init__(self, mining_address: bytes): self.start_time = ntp.getTime() self._sync_state = SyncState() self.peer_manager = P2PPeerManager() self.peer_manager.load_peer_addresses() self.p2pchain_manager = P2PChainManager() self.tx_manager = P2PTxManagement() self._chain_manager = None # FIXME: REMOVE. This is temporary self._p2pfactory = None # FIXME: REMOVE. This is temporary self._pow = None self.mining_address = mining_address reactor.callLater(10, self.monitor_chain_state)
def create(blocknumber: int, mining_nonce: int, PK: bytes, prev_blockheaderhash: bytes, hashedtransactions: bytes, fee_reward: int): """ Create a block header based on the parameters >>> BlockHeader.create(blocknumber=1, mining_nonce=1, PK=b'publickey', ... prev_blockheaderhash=b'headerhash', ... hashedtransactions=b'some_data', fee_reward=1) is not None True >>> b=BlockHeader.create(blocknumber=1, mining_nonce=1, PK=b'publickey', ... prev_blockheaderhash=b'headerhash', ... hashedtransactions=b'some_data', fee_reward=1) >>> b.epoch 0 """ bh = BlockHeader() bh._data.block_number = blocknumber bh._data.epoch = bh._data.block_number // config.dev.blocks_per_epoch if bh._data.block_number != 0: bh._data.timestamp.seconds = int(ntp.getTime()) if bh._data.timestamp == 0: logger.warning('Failed to get NTP timestamp') return bh._data.hash_header_prev = prev_blockheaderhash bh._data.merkle_root = hashedtransactions bh._data.PK = PK bh._data.reward_fee = fee_reward bh._data.reward_block = 0 if bh._data.block_number != 0: bh._data.reward_block = bh.block_reward_calc() bh.set_mining_nonce(mining_nonce) return bh
def validate(self, fee_reward, coinbase_amount): current_time = ntp.getTime() allowed_timestamp = current_time + config.dev.block_lead_timestamp if self.timestamp > allowed_timestamp: logger.warning( 'BLOCK timestamp is more than the allowed block lead timestamp' ) logger.warning('Block timestamp %s ', self.timestamp) logger.warning('threshold timestamp %s', allowed_timestamp) return False if self.timestamp < config.dev.genesis_timestamp: logger.warning('Timestamp lower than genesis timestamp') logger.warning('Genesis Timestamp %s', config.dev.genesis_timestamp) logger.warning('Block Timestamp %s', self.timestamp) return False if self.generate_headerhash() != self.headerhash: logger.warning('Headerhash false for block: failed validation') return False if self.block_reward != self.block_reward_calc(): logger.warning( 'Block reward incorrect for block: failed validation') return False if self.fee_reward != fee_reward: logger.warning( 'Block Fee reward incorrect for block: failed validation') return False if self.block_reward + self.fee_reward != coinbase_amount: logger.warning( 'Block_reward + fee_reward doesnt sums up to coinbase_amount') return False if self.timestamp == 0 and self.block_number > 0: logger.warning('Invalid block timestamp ') return False return True
def create(blocknumber: int, prev_block_headerhash: bytes, prev_block_timestamp: int, hashedtransactions: bytes, fee_reward: int): """ Create a block header based on the parameters >>> BlockHeader.create(blocknumber=1, ... prev_block_headerhash=b'headerhash', ... prev_block_timestamp=10, ... hashedtransactions=b'some_data', fee_reward=1) is not None True >>> b=BlockHeader.create(blocknumber=1, ... prev_block_headerhash=b'headerhash', ... prev_block_timestamp=10, ... hashedtransactions=b'some_data', fee_reward=1) >>> b.epoch 0 """ bh = BlockHeader() bh._data.block_number = blocknumber if bh._data.block_number != 0: bh._data.timestamp_seconds = int(ntp.getTime()) # If current block timestamp is less than or equals to the previous block timestamp # then set current block timestamp 1 sec higher than prev_block_timestamp if bh._data.timestamp_seconds <= prev_block_timestamp: bh._data.timestamp_seconds = prev_block_timestamp + 1 if bh._data.timestamp_seconds == 0: logger.warning('Failed to get NTP timestamp') return bh._data.hash_header_prev = prev_block_headerhash bh._data.merkle_root = hashedtransactions bh._data.reward_fee = fee_reward bh._data.reward_block = bh.block_reward_calc(blocknumber) bh.set_nonces(0, 0) return bh
def block_received(self, source, block: Block): self.pow.last_pb_time = time.time() logger.info('>>> Received Block #%d %s', block.block_number, bin2hstr(block.headerhash)) if source != self._target_peer: logger.warning('Received block from unexpected peer') logger.warning('Expected peer: %s', self._target_peer.addr_remote) logger.warning('Found peer: %s', source.addr_remote) return if block.block_number != self._last_requested_block_idx: logger.warning('Did not match %s', self._last_requested_block_idx) return target_start_blocknumber = self._target_node_header_hash.block_number expected_headerhash = self._target_node_header_hash.headerhashes[block.block_number - target_start_blocknumber] if block.headerhash != expected_headerhash: logger.warning('Did not match headerhash') logger.warning('Expected headerhash %s', expected_headerhash) logger.warning('Found headerhash %s', block.headerhash) return if self._chain_manager.add_block(block): if self._chain_manager.last_block.headerhash == block.headerhash: self.pow.suspend_mining_timestamp = ntp.getTime() + config.dev.sync_delay_mining else: logger.warning('Failed to Add Block') return try: reactor.download_monitor.cancel() except Exception as e: logger.warning("PB: %s", e) if self.is_syncing_finished(): return self._last_requested_block_idx += 1 if self.is_syncing_finished(): return self.peer_fetch_block()
def handle_chain_state(self, source, message: qrllegacy_pb2.LegacyMessage): P2PBaseObserver._validate_message(message, qrllegacy_pb2.LegacyMessage.CHAINSTATE) message.chainStateData.timestamp = ntp.getTime() # Receiving time try: UInt256ToString(message.chainStateData.cumulative_difficulty) except ValueError: logger.warning('Invalid Cumulative Difficulty sent by peer') source.loseConnection() return self._peer_node_status[source] = message.chainStateData if not self._get_version_compatibility(message.chainStateData.version): logger.warning("Disconnecting from Peer %s running incompatible node version %s", source.peer.ip, message.veData.version) source.loseConnection() return
def __init__(self, priority, message): self.priority = priority self.timestamp = int(ntp.getTime()) self.message = message
def test_getTime(self): setDrift() time = getTime() self.assertIsNotNone(time)
def is_expired(self): return self.timestamp - ntp.getTime() > config.user.outgoing_message_expiry