code: def simultaneous_challenge(self, node_ip, node_port, proto):
          
  
          parts = self.sequential_connect()
          if parts is None:
              log.debug("Sequential connect failed")
              return None
          con, mappings, predictions = parts
  
          # Tell server to list ourselves as a candidate for node.
          msg = "CANDIDATE %s %s %s" % (node_ip, str(proto), predictions)
          con.send_line(msg)
          reply = con.recv_line(timeout=10)
          log.debug(reply)
          if "PREDICTION SET" not in reply:
              log.debug("Prediction set failed")
              return None
  
          # Wait for node to accept and give us fight time.
          # FIGHT 192.168.0.1 4552 345 34235 TCP 123123123.1\
          reply = con.recv_line(timeout=10)
          log.debug(reply)
          con.s.close()
          p = "^FIGHT ([0-9]+[.][0-9]+[.][0-9]+[.][0-9]+) ((?:[0-9]+\s?)+)"
          p += " (TCP|UDP) ([0-9]+(?:[.][0-9]+)?)$"
          parts = re.findall(p, reply)
          if not len(parts):
              log.debug("Invalid parts length")
              return None
          node_ip, predictions, proto, ntp = parts[0]
  
          log.debug("Received fight details")
          log.debug(str(parts[0]))
          log.debug("Attending fight now")
          return self.attend_fight(mappings, node_ip, predictions, ntp)
docs: Used by active simultaneous nodes to attempt to initiate
          a simultaneous open to a compatible node after retrieving
          its details from bootstrapping. The function advertises
          itself as a potential candidate to the server for the
          designated node_ip. It also waits for a response from the
          node (if any) and attends any arranged fights.