def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.database = load_plugin("chain.plugins.database") self.process_queue = load_plugin("chain.plugins.process_queue") self.peers = load_plugin("chain.plugins.peers") self.peers.setup() self.transaction_pool = load_plugin("chain.plugins.transaction_pool")
def _verify_peer_blocks(peer, start_height, peer_height): database = load_plugin("chain.plugins.database") current_round, _, max_delegates = calculate_round(start_height) last_height_in_round = current_round * max_delegates first_height_in_round = (current_round - 1) * max_delegates + 1 delegates = database.get_active_delegates(first_height_in_round) delegate_keys = [d.public_key for d in delegates] end_height = min(peer_height, last_height_in_round) height_block_map = {} for height in range(start_height, end_height + 1): if height not in height_block_map: # Height does not exist in our map yet, so get it from the peer # To get the block with current height, we need to fetch for blocks from # previous height blocks = peer.fetch_blocks_from_height(height - 1) for block in blocks: height_block_map[block.height] = block if height in height_block_map: is_valid = _is_valid_block(height_block_map[height], height, current_round, delegate_keys) if not is_valid: return False else: logger.info( "Could not find block with height %s in mapping %s", height, height_block_map, ) return False return True
def discover_peers(): """ Fetch peers of your existing peers to increase the number of peers. """ # TODO: Disable this function if peer discoverability is disabled in config peer_manager = load_plugin("chain.plugins.peers") peers = peer_manager.peers() # Shuffle peers so we always get the peers from the different peers at the start random.shuffle(peers) for index, peer in enumerate(peers): his_peers = peer.fetch_peers() for his_peer in his_peers: add_peer( ip=his_peer.ip, port=his_peer.port, chain_version=his_peer.chain_version, nethash=his_peer.nethash, os=his_peer.os, ) # Always get peers from at least 4 sources. As add_peer is async, # `has_minimum_peers` might actually return wrong result, but that will only # increase the number of peers we have. if index >= 4 and peer_manager.has_minimum_peers(): break reverify_all_peers()
def migrate(): database = load_plugin("chain.plugins.database") migrate_dir = os.path.join(os.getcwd(), "chain", "plugins", "database", "migrations") router = Router(database.db, migrate_dir=migrate_dir) router.run()
def create_migrations(name): database = load_plugin("chain.plugins.database") router = Router(database.db, migrate_dir="chain/plugins/database/migrations") # Create migration router.create(name, auto="chain.plugins.database.models")
def __init__(self): super().__init__() self.database = load_plugin("chain.plugins.database") self.redis = Redis( host=os.environ.get("REDIS_HOST", "localhost"), port=os.environ.get("REDIS_PORT", 6379), db=os.environ.get("REDIS_DB", 0), )
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.database = load_plugin("chain.plugins.database") # TODO: check DNS and NTP connectivitiy? self.redis = Redis( host=os.environ.get("REDIS_HOST", "localhost"), port=os.environ.get("REDIS_PORT", 6379), db=os.environ.get("REDIS_DB", 0), )
def __init__(self): super().__init__() # Load the database plugin to correctly setup PoolTransaction model and to # use it for getting the last block self.database = load_plugin("chain.plugins.database") self.redis = Redis( host=os.environ.get("REDIS_HOST", "localhost"), port=os.environ.get("REDIS_PORT", 6379), db=os.environ.get("REDIS_DB", 0), ) self.wallets = PoolWalletManager()
def reverify_peer(ip): peer_manager = load_plugin("chain.plugins.peers") peer = peer_manager.get_peer_by_ip(ip) logger.info("Reverifying peer %s:%s", peer.ip, peer.port) if peer: try: peer.verify_peer() except Exception as e: # TODO: be more specific logger.error("Peer %s:%s failed verification: %s", peer.ip, peer.port, e) peer_manager.suspend_peer(peer) else: logger.info("Peer %s:%s successfully reverified", peer.ip, peer.port) peer_manager.redis.set(peer_manager.key_active.format(peer.ip), peer.to_json()) else: logger.warning("Couldn't find a peer to reverify")
def add_peer(ip, port, chain_version, nethash, os): # TODO: Disable this function if peer discoverability is disabled in config peer_manager = load_plugin("chain.plugins.peers") peer = Peer(ip=ip, port=port, chain_version=chain_version, nethash=nethash, os=os) if not peer.is_valid() or peer_manager.is_peer_suspended(peer): logger.warning("Peer %s:%s is invalid or suspended.", peer.ip, peer.port) return if peer_manager.peer_with_ip_exists(peer.ip): logger.warning("Peer %s:%s already exists.", peer.ip, peer.port) return # Wait for a bit more than 3s for response in case the node you're pinging is # the official node and is trying to add you as their peer and your p2p is not # responding. When they ping you, they wait for 3 seconds for response and if they # don't get any, they reject your response even though the request to them was # valid # try: try: peer.verify_peer() except Exception as e: # TODO: Be more specific logger.exception("Suspended peer because %s", str(e)) peer_manager.suspend_peer(peer) else: logger.info( "Accepting peer %s:%s. Vefification: %s", peer.ip, peer.port, peer.verification, ) peer_manager.redis.set(peer_manager.key_active.format(peer.ip), peer.to_json())
def _find_highest_common_between_heights(peer, heights): database = load_plugin("chain.plugins.database") logger.info( ("Checking for the highest common block height. Currently checking for " "heights %s"), heights, ) our_blocks = database.get_blocks_by_heights(heights) if len(our_blocks) != len(heights): raise Exception( "Did not fetch all blocks with heights {} from the db".format( heights)) # TODO: something something deadline heights_by_id = {} for block in our_blocks: heights_by_id[block.id] = block.height common = peer.fetch_common_block_by_ids(list(heights_by_id.keys())) if not common: logger.info( "Couldn't find a common block for peer %s:%s for block heights %s", peer.ip, peer.port, heights, ) return None if heights_by_id.get(common["id"]) != common["height"]: logger.info( ("Our block height %s does not match with peer height %s for block with " "id %s"), heights_by_id.get(common["id"]), common["height"], common["id"], ) return None return common["height"]
def verify_peer_status(peer, state): """ Verify the peer's blockchain (claimed state). Confirm that the peer's chain is either: - the same as ours or - different than ours but legit. Legit chain would have blocks signed/forged by the appropriate delegate(s). We distinguish 6 different cases with respect to our chain and peer's chain: Case1. Peer height > our height and our highest block is part of the peer's chain. This means the peer is ahead of us, on the same chain. No fork. We verify: his blocks that have height > our height (up to the round end). Case2. Peer height > our height and our highest block is not part of the peer's chain This means that the peer is on a different, higher chain. It has forked before our latest block. We verify: the first few of the peer's blocks after the fork (up to the round end) Case3. Peer height == our height and our latest blocks are the same. This means that our chains are the same. We verify: nothing. Case4. Peer height == our height and our latest blocks differ. This means that we are on a different chains with equal height. A fork has occurred We verify: the first few of the peer's blocks after the fork (up to the round end) Case5. Peer height < our height and peer's latest block is part of our chain. This means that the peer is on the same chain as us, just lagging behind. Case6. Peer height < our height and peer's latest block is not part of our chain. This means that we have forked and the peer's chain is lower. We verify: the first few of the peer's blocks after the fork (up to the round end) """ database = load_plugin("chain.plugins.database") last_block = database.get_last_block() peer_height = int(state["header"]["height"]) peer_id = state["header"]["id"] """ Case3. Peer height == our height and our latest blocks are the same. This means that our chains are the same. """ if last_block.height == peer_height and last_block.id == peer_id: logger.info( ("Peer's latest block is the same as our latest block " "(height=%s, id=%s). Identical chains."), last_block.height, last_block.id, ) return { "my_height": last_block.height, "his_height": peer_height, "highest_common_height": peer_height, } """ Case5. Peer height < our height and peer's latest block is part of our chain. This means that the peer is on the same chain as us, just lagging behind. """ if peer_height < last_block.height: blocks = database.get_blocks_by_heights([peer_height]) block = blocks[0] if block.id == peer_id: return { "my_height": last_block.height, "his_height": peer_height, "highest_common_height": peer_height, } highest_common_height = _find_highest_common_block_height( peer, 1, min(peer_height, last_block.height)) if not highest_common_height: return None valid_peer_blocks = _verify_peer_blocks(peer, highest_common_height + 1, peer_height) if not valid_peer_blocks: return None return { "my_height": last_block.height, "his_height": peer_height, "highest_common_height": highest_common_height, }
def reverify_all_peers(): peer_manager = load_plugin("chain.plugins.peers") peers = peer_manager.peers() logger.info("Reverifying all %s peers", len(peers)) for peer in peers: reverify_peer(peer.ip)
def rollback_to_round(round): database = load_plugin("chain.plugins.database") database.rollback_to_round(int(round))
def __init__(self): super().__init__() self.db = load_plugin("chain.plugins.database") self.process_queue = load_plugin("chain.plugins.process_queue") self.transaction_pool = load_plugin("chain.plugins.transaction_pool")