def _connect_to_peers(self):
        min_peer_count = self.Config.get("InitialConnectivity", 1)
        current_peer_count = len(self.Ledger.peer_list())

        logger.debug("peer count is %d of %d",
                     current_peer_count, min_peer_count)

        if current_peer_count < min_peer_count:
            peerset = self._get_candidate_peers()

            # Add the candidate nodes to the gossip object so we can send
            # connect requests to them
            for peername in peerset:
                peer = self.NodeMap.get(peername)
                if peer:
                    logger.info('add peer %s with identifier %s', peername,
                                peer.Identifier)
                    connect_message.send_connection_request(self.Ledger, peer)
                    self.Ledger.add_node(peer)
                else:
                    logger.info('requested connection to unknown peer %s',
                                peername)

            return False
        else:
            return True
    def initialize_ledger_connection(self):
        """
        Connect the ledger to the rest of the network.
        """

        assert self.Ledger

        self.status = 'waiting for initial connections'

        min_peer_count = self.Config.get("InitialConnectivity", 1)
        current_peer_count = len(self.Ledger.peer_list())

        logger.debug("initial peer count is %d of %d",
                     current_peer_count, min_peer_count)

        if current_peer_count < min_peer_count:
            peerset = self._get_candidate_peers()

            # Add the candidate nodes to the gossip object so we can send
            # connect requests to them
            for peername in peerset:
                peer = self.NodeMap.get(peername)
                if peer:
                    logger.info('add peer %s with identifier %s', peername,
                                peer.Identifier)
                    connect_message.send_connection_request(self.Ledger, peer)
                    self.Ledger.add_node(peer)
                else:
                    logger.info('requested connection to unknown peer %s',
                                peername)

            reactor.callLater(2.0, self.initialize_ledger_connection)
        else:
            reactor.callLater(2.0, self.initialize_ledger_topology,
                              self.start_journal_transfer)
示例#3
0
    def _connect_to_peers(self):
        min_peer_count = self.config.get("InitialConnectivity", 1)
        current_peer_count = len(self.gossip.peer_list())

        logger.debug("peer count is %d of %d", current_peer_count,
                     min_peer_count)

        if current_peer_count < min_peer_count:
            peerset = self._get_candidate_peers()

            # Add the candidate nodes to the gossip object so we can send
            # connect requests to them
            for peername in peerset:
                peer = self.NodeMap.get(peername)
                if peer:
                    logger.info('add peer %s with identifier %s', peername,
                                peer.Identifier)
                    connect_message.send_connection_request(self.gossip, peer)
                    self.gossip.add_node(peer)
                else:
                    logger.info('requested connection to unknown peer %s',
                                peername)

            return False
        else:
            return True
示例#4
0
def update_connections(gossiper, topology, oncomplete):
    """Connects the node to the network by building a Barabasi-Albert graph.

    Note:
        For more information see
        http://en.wikipedia.org/wiki/Barab%C3%A1si%E2%80%93Albert_model

    Args:
        gossiper (Node): The local node.
        topology (dict): Map of nodes to connections.
        oncomplete (function): The function to call once the topology
            update has completed.
    """
    logger.info("update connections from topology probe")

    for peer, connections in topology.iteritems():
        logger.debug("node %s --> %s", peer.Name, len(connections))

    # First pass through the topology information that was collected, compute
    # the total number of connections per node which will give us a
    # distribution for connections, Bara
    total = 0
    candidates = {}
    for peer, connections in topology.iteritems():
        if peer.Identifier == gossiper.LocalNode.Identifier:
            continue

        if peer.Identifier in gossiper.NodeMap:
            continue

        # this is strictly NOT part of the Barabasi graph construction because
        # it forces a limit on connectivity, however it removes some of the
        # worst hotspots without fundamentally changing the graph structure
        count = len(connections)
        if count > MaximumConnectivity:
            continue

        candidates[peer] = count
        total += count

    # Second pass selects some subset of nodes based on the number of existing
    # connections and sends out a connection request to each
    if total > 0:
        for peer, count in candidates.iteritems():

            # the FudgeFactor is used to increase the chance that we'll connect
            # to a node, strictly speaking the fudge factor should be 0
            if random.randint(0, total - 1) < count + ConnectivityFudgeFactor:
                connect_message.send_connection_request(gossiper, peer)

    # call the final handler
    oncomplete(gossiper)
示例#5
0
def update_connections(gossiper, topology, oncomplete):
    """Connects the node to the network by building a Barabasi-Albert graph.

    Note:
        For more information see
        http://en.wikipedia.org/wiki/Barab%C3%A1si%E2%80%93Albert_model

    Args:
        gossiper (Node): The local node.
        topology (dict): Map of nodes to connections.
        oncomplete (function): The function to call once the topology
            update has completed.
    """
    logger.info("update connections from topology probe")

    for peer, connections in topology.iteritems():
        logger.debug("node %s --> %s", peer.Name, len(connections))

    # First pass through the topology information that was collected, compute
    # the total number of connections per node which will give us a
    # distribution for connections, Bara
    total = 0
    candidates = {}
    for peer, connections in topology.iteritems():
        if peer.Identifier == gossiper.LocalNode.Identifier:
            continue

        if peer.Identifier in gossiper.NodeMap:
            continue

        # this is strictly NOT part of the Barabasi graph construction because
        # it forces a limit on connectivity, however it removes some of the
        # worst hotspots without fundamentally changing the graph structure
        count = len(connections)
        if count > MaximumConnectivity:
            continue

        candidates[peer] = count
        total += count

    # Second pass selects some subset of nodes based on the number of existing
    # connections and sends out a connection request to each
    if total > 0:
        for peer, count in candidates.iteritems():

            # the FudgeFactor is used to increase the chance that we'll connect
            # to a node, strictly speaking the fudge factor should be 0
            if random.randint(0, total - 1) < count + ConnectivityFudgeFactor:
                connect_message.send_connection_request(gossiper, peer)

    # call the final handler
    oncomplete(gossiper)
示例#6
0
def random_walk_handler(msg, gossiper):
    """Function called when the gossiper receives a RandomWalkMessage
    from one of its peers.

    Args:
        msg (message.Message): The received random walk message.
        gossiper (Node): The local node.
    """

    if msg.OriginatorID == gossiper.LocalNode.Identifier:
        logger.debug('node %s received its own random walk request, ignore',
                     gossiper.LocalNode)
        return

    logger.debug('random walk request %s from %s with ttl %d',
                 msg.Identifier[:8], msg.Name, msg.TimeToLive)

    peers = gossiper.peer_id_list()

    # if the source is not already one of our peers, then check to see if we
    # should add it to our list
    if msg.OriginatorID not in peers:
        if len(peers) < random_connections():
            logger.debug(
                'add connection to node %s based on random walk request %s',
                msg.Name, msg.Identifier[:8])
            onode = node.Node(address=msg.NetAddress,
                              identifier=msg.NodeIdentifier,
                              name=msg.Name)
            onode.enable()

            send_connection_request(gossiper, onode)
            return

    # if there is still life in the message, then see if we should forward it
    # to another node

    if msg.TimeToLive > 0:
        # see if we can find a peer other than the peer who forwarded the
        # message to us, if not then we'll just drop the request

        try:
            peers.remove(msg.SenderID)
            peers.remove(msg.OriginatorID)
        except:
            pass

        if len(peers) > 0:
            peerid = random.choice(peers)
            gossiper.send_message(msg, peerid, initialize=False)
    def initialize_ledger_topology(self, callback):
        """
        Make certain that there is at least one connected peer and then
        kick off the configured topology generation protocol.
        """

        logger.debug('initialize ledger topology')

        # make sure there is at least one connection already confirmed
        if len(self.Ledger.peer_list()) == 0:
            self._connectionattempts -= 1
            if self._connectionattempts > 0:
                logger.info(
                    'initial connection attempts failed, '
                    'try again [%s]', self._connectionattempts)
                for peer in self.Ledger.peer_list(allflag=True):
                    connect_message.send_connection_request(self.Ledger, peer)
                reactor.callLater(2.0, self.initialize_ledger_topology,
                                  callback)
                return
            else:
                logger.critical('failed to connect to selected peers, '
                                'shutting down')
                self.shutdown()
                return

        self._connectionattempts = 0

        # and now its time to pick the topology protocol
        topology = self.Config.get("TopologyAlgorithm", "RandomWalk")
        if topology == "RandomWalk":
            if 'TargetConnectivity' in self.Config:
                random_walk.TargetConnectivity = self.Config[
                    'TargetConnectivity']
            self.random_walk_initialization(callback)

        elif topology == "BarabasiAlbert":
            if 'MaximumConnectivity' in self.Config:
                barabasi_albert.MaximumConnectivity = self.Config[
                    'MaximumConnectivity']
            if 'MinimumConnectivity' in self.Config:
                barabasi_albert.MinimumConnectivity = self.Config[
                    'MinimumConnectivity']
            self.barabasi_initialization(callback)

        else:
            logger.error("unknown topology protocol %s", topology)
            self.shutdown()
            return
    def initialize_ledger_topology(self, callback):
        """
        Make certain that there is at least one connected peer and then
        kick off the configured topology generation protocol.
        """

        logger.debug('initialize ledger topology')

        # make sure there is at least one connection already confirmed
        if len(self.Ledger.peer_list()) == 0:
            self._connectionattempts -= 1
            if self._connectionattempts > 0:
                logger.info('initial connection attempts failed, '
                            'try again [%s]', self._connectionattempts)
                for peer in self.Ledger.peer_list(allflag=True):
                    connect_message.send_connection_request(self.Ledger, peer)
                reactor.callLater(2.0, self.initialize_ledger_topology,
                                  callback)
                return
            else:
                logger.critical('failed to connect to selected peers, '
                                'shutting down')
                self.shutdown()
                return

        self._connectionattempts = 0

        # and now its time to pick the topology protocol
        topology = self.Config.get("TopologyAlgorithm", "RandomWalk")
        if topology == "RandomWalk":
            if 'TargetConnectivity' in self.Config:
                random_walk.TargetConnectivity = self.Config[
                    'TargetConnectivity']
            self.random_walk_initialization(callback)

        elif topology == "BarabasiAlbert":
            if 'MaximumConnectivity' in self.Config:
                barabasi_albert.MaximumConnectivity = self.Config[
                    'MaximumConnectivity']
            if 'MinimumConnectivity' in self.Config:
                barabasi_albert.MinimumConnectivity = self.Config[
                    'MinimumConnectivity']
            self.barabasi_initialization(callback)

        else:
            logger.error("unknown topology protocol %s", topology)
            self.shutdown()
            return
示例#9
0
def _get_quorum(gossiper, callback):
    """Attempts to connect gossiper to new available quorum nodes
    Args:
        gossiper (Node): The local node.
        callback (function): The function to call once the quorum topology
            update has completed.
    """
    # find out how many we have and how many nodes we still need need
    count = max(0, TargetConnectivity - len(gossiper.VotingQuorum.keys()))
    # we have all the nodes we need; do next operation (the callback)
    if count <= 0:
        logger.debug('sufficiently connected via %s',
                     [str(x.Name) for x in gossiper.VotingQuorum.itervalues()])
        callback()
        return
    # add nodes we don't have already, in random order
    candidates = [
        x for x in gossiper.quorum_list()
        if gossiper.VotingQuorum.get(x.Identifier, None) is None
    ]
    random.shuffle(candidates)
    logger.debug('trying to increase working quorum by %d from candidates %s',
                 count, [str(x.Name) for x in candidates])
    for nd in candidates:
        lwc = LedgerWebClient('http://{0}:{1}'.format(nd.NetHost, nd.HttpPort))
        try:
            status = lwc.get_status(verbose=False, timeout=2)
        except MessageException as e:
            logger.debug(e.message)
            continue
        status = status.get('Status', '')
        if status in [
                'started', "transferring ledger",
                "waiting for initial connections"
        ]:
            # candidate node is live; add it
            logger.debug('adding %s to quorum', nd.Name)
            gossiper.add_quorum_node(nd)
            if nd.Identifier not in gossiper.peer_id_list():
                send_connection_request(gossiper, nd)
            count -= 1
            if count == 0:
                logger.debug('now sufficiently connected')
                break
    # try again (or possibly execute the piggybacked callback)
    reactor.callLater(TimeBetweenProbes, _get_quorum, gossiper, callback)
示例#10
0
def _get_quorum(gossiper, callback):
    """Attempts to connect gossiper to new available quorum nodes
    Args:
        gossiper (Node): The local node.
        callback (function): The function to call once the quorum topology
            update has completed.
    """
    # find out how many we have and how many nodes we still need need
    count = max(0, TargetConnectivity - len(gossiper.VotingQuorum.keys()))
    # we have all the nodes we need; do next operation (the callback)
    if count <= 0:
        logger.debug('sufficiently connected via %s',
                     [str(x.Name) for x in gossiper.VotingQuorum.itervalues()])
        callback()
        return
    # add nodes we don't have already, in random order
    candidates = [x for x in gossiper.quorum_list()
                  if gossiper.VotingQuorum.get(x.Identifier, None) is None]
    random.shuffle(candidates)
    logger.debug('trying to increase working quorum by %d from candidates %s',
                 count, [str(x.Name) for x in candidates])
    for nd in candidates:
        client = SawtoothClient('http://{0}:{1}'.format(nd.NetHost,
                                                        nd.HttpPort))
        try:
            status = client.get_status(timeout=2)
        except MessageException as e:
            logger.debug(e.message)
            continue
        status = status.get('Status', '')
        if status in ['started', "transferring ledger",
                      "waiting for initial connections"]:
            # candidate node is live; add it
            logger.debug('adding %s to quorum', nd.Name)
            gossiper.add_quorum_node(nd)
            if nd.Identifier not in gossiper.peer_id_list():
                send_connection_request(gossiper, nd)
            count -= 1
            if count == 0:
                logger.debug('now sufficiently connected')
                break
    # try again (or possibly execute the piggybacked callback)
    reactor.callLater(TimeBetweenProbes, _get_quorum, gossiper, callback)
示例#11
0
    def initialize_ledger_connection(self):
        """
        Connect the ledger to the rest of the network; in addition to the
        list of nodes directly specified in the configuration file, pull
        a list from the LedgerURL. Once the list of potential peers is
        constructed, pick from it those specified in the Peers configuration
        variable. If that is not enough, then pick more at random from the
        list.
        """

        assert self.Ledger

        url = self.Config.get('LedgerURL', '**none**')
        if url != '**none**':
            logger.info('load peers using url %s', self.Config['LedgerURL'])

            try:
                peers = self.get_endpoints(0, self.EndpointDomain)
                for peer in peers:
                    self.NodeMap[peer.Name] = peer
            except ledger_web_client.MessageException as e:
                logger.error("Unable to get endpoints from LedgerURL: %s",
                             str(e))

        # Build a list of nodes that we can use for the initial connection
        minpeercount = self.Config.get("InitialConnectivity", 1)
        peerset = set(self.Config.get('Peers', []))
        nodeset = set(self.NodeMap.keys())
        if len(peerset) < minpeercount and len(nodeset) > 0:
            nodeset.discard(self.Ledger.LocalNode.Name)
            nodeset = nodeset.difference(peerset)
            peerset = peerset.union(
                random.sample(list(nodeset),
                              min(minpeercount - len(peerset), len(nodeset))))

        # Add the candidate nodes to the gossip object so we can send connect
        # requests to them
        for peername in peerset:
            peer = self.NodeMap.get(peername)
            if peer:
                logger.info('add peer %s with identifier %s', peername,
                            peer.Identifier)
                self.Ledger.add_node(peer)
            else:
                logger.info('requested connection to unknown peer %s',
                            peername)

        # the pathological case is that there was nothing specified and since
        # we already know we aren't the genesis block, we can just shut down
        if len(self.Ledger.peer_list(allflag=True)) == 0:
            logger.critical('unable to find a valid peer')
            self.shutdown()
            return

        # and send all the connection requests, must use allflag because we
        # added the nodes disabled, the connect response will mark them
        # enabled
        for peer in self.Ledger.peer_list(allflag=True):
            connect_message.send_connection_request(self.Ledger, peer)

        logger.debug("initial ledger connection requests sent")

        # Wait for the connection message to be processed before jumping to the
        # next state a better technique would be to add an event in sawtooth
        # when a new node is connected
        self._connectionattempts = 3
        reactor.callLater(2.0, self.initialize_ledger_topology,
                          self.start_journal_transfer)
    def initialize_ledger_connection(self):
        """
        Connect the ledger to the rest of the network; in addition to the
        list of nodes directly specified in the configuration file, pull
        a list from the LedgerURL. Once the list of potential peers is
        constructed, pick from it those specified in the Peers configuration
        variable. If that is not enough, then pick more at random from the
        list.
        """

        assert self.Ledger

        url = self.Config.get('LedgerURL', '**none**')
        if url != '**none**':
            logger.info('load peers using url %s', self.Config['LedgerURL'])

            try:
                peers = self.get_endpoints(0, self.EndpointDomain)
                for peer in peers:
                    self.NodeMap[peer.Name] = peer
            except ledger_web_client.MessageException as e:
                logger.error("Unable to get endpoints from LedgerURL: %s",
                             str(e))

        # Build a list of nodes that we can use for the initial connection
        minpeercount = self.Config.get("InitialConnectivity", 1)
        peerset = set(self.Config.get('Peers', []))
        nodeset = set(self.NodeMap.keys())
        if len(peerset) < minpeercount and len(nodeset) > 0:
            nodeset.discard(self.Ledger.LocalNode.Name)
            nodeset = nodeset.difference(peerset)
            peerset = peerset.union(random.sample(list(nodeset), min(
                minpeercount - len(peerset), len(nodeset))))

        # Add the candidate nodes to the gossip object so we can send connect
        # requests to them
        for peername in peerset:
            peer = self.NodeMap.get(peername)
            if peer:
                logger.info('add peer %s with identifier %s', peername,
                            peer.Identifier)
                self.Ledger.add_node(peer)
            else:
                logger.info('requested connection to unknown peer %s',
                            peername)

        # the pathological case is that there was nothing specified and since
        # we already know we aren't the genesis block, we can just shut down
        if len(self.Ledger.peer_list(allflag=True)) == 0:
            logger.critical('unable to find a valid peer')
            self.shutdown()
            return

        # and send all the connection requests, must use allflag because we
        # added the nodes disabled, the connect response will mark them
        # enabled
        for peer in self.Ledger.peer_list(allflag=True):
            connect_message.send_connection_request(self.Ledger, peer)

        logger.debug("initial ledger connection requests sent")

        # Wait for the connection message to be processed before jumping to the
        # next state a better technique would be to add an event in sawtooth
        # when a new node is connected
        self._connectionattempts = 3
        reactor.callLater(2.0, self.initialize_ledger_topology,
                          self.start_journal_transfer)