Ejemplo n.º 1
0
    def run(self):

        # Loop through the list of peers and send heartbeat messages
        while True and not self.stop.is_set():

            for target, last_beat in list(daemon.peers.items()):

                if (last_beat + timedelta(seconds=settings.MSG_HB_TTL)
                    ) < datetime.now():
                    # Check for dead peers
                    with lock:
                        utils.log_message(
                            "Removing dead peer {0}".format(target),
                            utils.Level.MEDIUM)

                    del daemon.peers[target]
                else:
                    # Send heartbeat with root id and tail id to peers
                    s = socket(AF_INET, SOCK_DGRAM)

                    message_body = {
                        "ledger": daemon.ledger.id,
                        "tail": daemon.ledger.tail.hash
                    }

                    message = Message(settings.MSG_TYPE_HB,
                                      message_body).__repr__()

                    s.sendto(message.encode(), (target, settings.BIND_PORT))
                    utils.log_message("Heartbeat sent to {0}".format(target),
                                      utils.Level.LOW)
                    s.close()

            # Sleep the required time between heartbeats
            time.sleep(settings.MSG_HB_FREQ)
Ejemplo n.º 2
0
 def __init__(self, ip, port):
     super(UDPListener, self).__init__()
     with lock:
         utils.log_message("Starting UDP Listener Thread")
     self.daemon = True
     self._port = port
     self._ip = ip
     self.stop = threading.Event()
Ejemplo n.º 3
0
 def _respond(self, message):
     with lock:
         utils.log_message("Responded with message to {}".format(
             self._socket.getsockname()))
         utils.log_message(message, utils.Level.MEDIUM)
     self._socket.sendall(message.encode())
     self._socket.shutdown(SHUT_WR)
     self._socket.recv(4096)
     self._socket.close()
Ejemplo n.º 4
0
    def __init__(self, target, message, timeout=5):
        super(TCPMessageThread, self).__init__()
        with lock:
            utils.log_message(
                "Sending Message to {0} {1}: {2}{3}".format(
                    target[0], target[1], message[:10], '...'),
                utils.Level.MEDIUM)
        self._target = target

        self.message = message
        self._timeout = timeout
Ejemplo n.º 5
0
    def __init__(self, ip=settings.BIND_IP, port=settings.BIND_PORT):
        super(TCPListener, self).__init__()
        with lock:
            utils.log_message("Starting TCP Listener Thread")
        self.daemon = True
        self._port = port
        self._ip = ip
        self.stop = threading.Event()
        self.stop.clear()

        self.tcp_server_socket = socket(AF_INET, SOCK_STREAM)
Ejemplo n.º 6
0
def discover(ip='<broadcast>',
             port=settings.BIND_PORT,
             timeout=settings.DISCOVERY_TIMEOUT):

    utils.log_message("Starting Discovery for {} seconds".format(timeout))

    results = dict()

    # Get our IP address - I don't like this hack but it works
    # https://stackoverflow.com/questions/24196932/how-can-i-get-the-ip-address-of-eth0-in-python/24196955
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    s.connect((ip, port))
    ip_self = s.getsockname()[0]
    s.close()

    # Send out discovery query
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    message = messaging.Message(settings.MSG_TYPE_DISCOVER).__repr__()
    s.sendto(message.encode(), (ip, port))

    try:
        # Listen for responses for 10 seconds
        s.settimeout(timeout)
        while True:
            data, address = s.recvfrom(4096)

            try:
                message = json.loads(data.decode(),
                                     object_hook=utils.message_decoder)

                if message.msg_type == settings.MSG_TYPE_SUCCESS:
                    utils.log_message(
                        "Discovered ledger {0} at {1}".format(
                            message.msg, address), utils.Level.MEDIUM)

                    # Received response
                    # Is the response our own ledger?
                    if address[0] == ip_self:
                        continue
                    # Is the hash already in our list?
                    if message.msg not in results:
                        # If hash isn't in the list, create a new set and add address to it
                        results[message.msg] = set()
                    # Since there's already a set for our hash, we add to it
                    results[message.msg].add(address)

            except:
                utils.log_message("Malformed response from {0}: {1}".format(
                    data, address))

    except OSError as e:
        utils.log_message("Exception: {0}".format(e))
    finally:
        s.close()

    return results
Ejemplo n.º 7
0
    def do_init(self, args):
        """Initialize the ledger with a provided Root of Trust (RSA Public Key)

        Arguments:
        gen: Generate an RSA key locally - to save to disk, follow with save path
        private_key: Provide a PEM private key string
        path: Path to PEM private key
        """

        # Give error with no key, use default key, or provide
        if len(args) == 0:
            print("Please provide an RSA key as your new Root of Trust.")
            return
        else:
            args_list = args.split()
            if args_list[0].lower() == "gen":
                # Generate an RSA key

                if len(args_list) == 1:
                    # Generate a RSA key in memory
                    privkey = utils.gen_privkey()
                else:
                    # Generate and save RSA key
                    privkey = utils.gen_privkey(True, args_list[0])

            else:
                # Try to import provided key
                privkey = utils.get_key(args)

                if privkey is None:
                    print("Could not import the provided key")
                    return

        # If we made it this far we have a valid key
        # Store generated key in our daemon for now
        daemon.create_ledger(privkey)
        hash = daemon.ledger.id

        print("\nPublic Key Hash: {0}".format(hash))
        utils.log_message(
            "Added key ({0}) as a new Root of Trust".format(hash),
            utils.Level.FORCE)

        self.update_prompt()
Ejemplo n.º 8
0
def peer_sync(target):
    utils.log_message("Requesting peers from {0}".format(target),
                      utils.Level.MEDIUM)

    ledger_message = Message(settings.MSG_TYPE_PEER, None).prep_tcp()
    thread = TCPMessageThread(target, ledger_message)
    thread.start()
    thread.join()

    message = json.loads(thread.message, object_hook=utils.message_decoder)

    for peer in message.msg:
        daemon.peers[peer] = datetime.now()

    daemon.peers[target[0]] = datetime.now()

    utils.log_message(
        "Successfully synchronized {} peer(s) from {}".format(
            len(message.msg), target), utils.Level.MEDIUM)
Ejemplo n.º 9
0
    def run(self):
        # Listen for ledger client connection requests
        with lock:
            utils.log_message(
                "Listening for ledger messages on port {0}".format(self._port))

        try:
            self.tcp_server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
            self.tcp_server_socket.setblocking(False)
            self.tcp_server_socket.bind((self._ip, self._port))
            self.tcp_server_socket.listen(5)

            # List for managing spawned threads
            socket_threads = []

            # Non-blocking socket loop that can be interrupted with a signal/event
            while True and not self.stop.is_set():
                try:
                    client_socket, address = self.tcp_server_socket.accept()

                    # Spawn thread
                    client_thread = TCPConnectionThread(client_socket)
                    client_thread.start()
                    socket_threads.append(client_thread)

                except Exception as e:
                    continue

            # Clean up all the threads
            for thread in socket_threads:
                thread.join()

        except Exception as e:
            print("Could not bind to port: {0}".format(e))
        finally:
            self.tcp_server_socket.close()
Ejemplo n.º 10
0
def join_ledger(public_key_hash, member):
    global ledger

    # Check to make sure we aren't part of a ledger yet
    if joined():
        print("You are already a member of a ledger")
        return

    utils.log_message("Spawning TCP Connection Thread to {0}".format(member))
    join_message = messaging.Message(settings.MSG_TYPE_JOIN,
                                     public_key_hash).prep_tcp()
    thread = messaging.TCPMessageThread(member, join_message)
    thread.start()
    thread.join()

    # If the message is a success, import the key
    try:

        message = json.loads(thread.message, object_hook=utils.message_decoder)

        if message.msg_type == settings.MSG_TYPE_SUCCESS:
            key = utils.get_key(message.msg)
            key_hash = utils.gen_hash(utils.encode_key(key))

            if public_key_hash == key_hash:
                # Hooray! We have a match
                utils.log_message("Joined ledger {}".format(public_key_hash),
                                  utils.Level.FORCE)

                # Sync Ledger
                messaging.block_sync(member)

                # Request peers
                messaging.peer_sync(member)

                # Start Listeners
                ledger_listeners(True)

            else:
                raise ValueError(
                    'Public key returned does not match requested hash: {0}'.
                    format(key_hash))

        else:
            raise ValueError('Response was not as expected: {0}'.format(
                message.msg_type))

    except (ValueError, TypeError) as e:
        utils.log_message("Not a valid response from {0}: {1}".format(
            member, e))
Ejemplo n.º 11
0
    def run(self):
        tcp_message_socket = socket(AF_INET, SOCK_STREAM)
        tcp_message_socket.settimeout(self._timeout)

        try:
            tcp_message_socket.connect(self._target)
            tcp_message_socket.sendall(self.message.encode())

            # Get response
            self.message = ''
            message_size = None

            while True:
                if message_size is None:
                    data = tcp_message_socket.recv(settings.MSG_SIZE_BYTES)
                    # Convert first message size to an integer
                    message_size = int(data.decode())
                elif len(self.message) < message_size:
                    data = tcp_message_socket.recv(4096)
                    self.message += data.decode()
                else:
                    break

        except ValueError as e:
            with lock:
                utils.log_message('Received invalid response from {0}'.format(
                    tcp_message_socket.getsockname()))

        except Exception as e:
            with lock:
                utils.log_message(
                    'Could not send or receive message to or from the ledger at {0}:\n{1}\n{2}'
                    .format(tcp_message_socket.getsockname()[0], self.message,
                            e))

        else:
            with lock:
                utils.log_message(
                    "Received Response from {0} {1}: {2}{3}".format(
                        self._target[0], self._target[1], self.message[:10],
                        '...'), utils.Level.MEDIUM)

        finally:
            tcp_message_socket.close()
Ejemplo n.º 12
0
def ledger_listeners(start):
    global _udp_thread, _udp_hb_thread, _tcp_thread

    if start:
        # Spawn UDP Persistent Listener thread
        _udp_thread = messaging.UDPListener(settings.BIND_IP,
                                            settings.BIND_PORT)
        _udp_thread.start()

        # Spawn TCP Listener thread
        _tcp_thread = messaging.TCPListener(settings.BIND_IP,
                                            settings.BIND_PORT)
        _tcp_thread.start()

        # Spawn UDP Heartbeat thread
        _udp_hb_thread = messaging.UDPHeartbeat()
        _udp_hb_thread.start()

    else:
        # Kill udp listener thread
        if _udp_thread is not None:
            utils.log_message("Killing UDP Listening Thread...")
            _udp_thread.stop.set()
            _udp_thread.join()
            _udp_thread = None

        # Kill tcp listener thread
        if _tcp_thread is not None:
            utils.log_message("Killing TCP Listening Thread...")
            _tcp_thread.stop.set()
            _tcp_thread.join()
            _tcp_thread = None

        # Kill udp hb thread
        if _udp_hb_thread is not None:
            utils.log_message("Killing Heartbeat Thread...")
            _udp_hb_thread.stop.set()
            _udp_hb_thread.join()
            _udp_hb_thread = None
Ejemplo n.º 13
0
def block_sync(target, block_hash=None):
    utils.log_message("Requesting blocks from {0}".format(target),
                      utils.Level.MEDIUM)

    ledger_message = Message(settings.MSG_TYPE_LEDGER, block_hash).prep_tcp()
    thread = TCPMessageThread(target, ledger_message)
    thread.start()
    thread.join()

    message = json.loads(thread.message, object_hook=utils.message_decoder)

    # Add received blocks to our ledger
    if daemon.ledger is None:
        daemon.ledger = ledger.Ledger()

    try:
        for block in message.msg:
            daemon.ledger.append(block)
    except ValueError as e:
        utils.log_message(e)

    utils.log_message(
        "Successfully synchronized {} block(s) from {}".format(
            len(message.msg), target), utils.Level.HIGH)
Ejemplo n.º 14
0
    def do_discover(self, args):
        """Attempt to discover other ledgers.

        Arguments:
        peers: include to discover peers on the same ledger and add them to your peer list
        cached: include to utilize the cache
        ip: provide an ip address otherwise the local broadcast will be used.
        """

        args = args.lower().strip().split()

        peers = False
        cached = False
        ip = '<broadcast>'

        # Parse the arguments
        if 'peers' in args:
            # Check that we're even a member of a ledger
            if daemon.ledger is None or daemon.ledger.id is None:
                print(
                    "You may not search for peers without being a member of a ledger."
                )
                return
            peers = True
            args.remove('peers')
        if 'cached' in args:
            cached = len(daemon.disc_peers) > 0 if peers else len(
                daemon.disc_ledgers)
            args.remove('cached')
            utils.log_message("Using cached results" if cached else
                              "Bypassing cache because no results in cache.")
        if len(args) > 0:
            # If we still have arguments, we assume it's an IP address
            ip = args[0]

            # Check for a valid IP
            try:
                socket.inet_pton(socket.AF_INET, ip)
            except socket.error:
                print("You entered an invalid IP address")
                return

        # Get results of discovery and process
        if not cached:
            daemon.disc_ledgers = daemon.discover(ip)

            if peers:
                # If we're looking for peers, we're only looking for ledger ids that match ours
                daemon.disc_peers = daemon.disc_ledgers.get(
                    daemon.ledger.id, set())

        # Display the results
        if peers:
            print("Found {} peers".format(str(len(daemon.disc_peers))))
            if len(daemon.disc_peers) > 0:
                added_peer_count = 0
                for idx, addr in enumerate(daemon.disc_peers):
                    is_peer = addr[0] in daemon.peers
                    print("{} | {}{}".format(idx + 1,
                                             '(peer) ' if is_peer else '',
                                             addr[0]))

                    # Add non-peers to peer list
                    if not is_peer:
                        daemon.peers[addr[0]] = datetime.now()
                        added_peer_count += 1

                    print(
                        "Added {} peers to peer list".format(added_peer_count))

        else:
            print("Found {} available ledgers".format(
                str(len(daemon.disc_ledgers))))
            if len(daemon.disc_ledgers) > 0:
                member = ''
                for idx, ledger in enumerate(daemon.disc_ledgers):
                    if daemon.ledger is not None and daemon.ledger.id == ledger.strip(
                    ):
                        member = '(peer)'
                    else:
                        member = ''
                    print("{0} | {4}: ({1} members) {2} {3}".format(
                        idx + 1, len(daemon.disc_ledgers[ledger]), ledger,
                        member,
                        list(daemon.disc_ledgers[ledger])[0][0]))
Ejemplo n.º 15
0
 def __init__(self):
     super(UDPHeartbeat, self).__init__()
     with lock:
         utils.log_message("Starting UDP Heartbeat Thread")
     self.daemon = True
     self.stop = threading.Event()
Ejemplo n.º 16
0
    def run(self):
        # Listen for ledger client connection requests
        with lock:
            utils.log_message(
                "Listening for ledger discovery queries on port {0}".format(
                    self._port), utils.Level.MEDIUM)

        discovery_socket = socket(AF_INET, SOCK_DGRAM)
        discovery_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        discovery_socket.bind((self._ip, self._port))
        discovery_socket.setblocking(False)

        # Non-blocking socket loop that can be interrupted with a signal/event
        while True and not self.stop.is_set():
            try:
                data, addr = discovery_socket.recvfrom(1024)
            except OSError as e:
                continue
            else:
                message = json.loads(data.decode(),
                                     object_hook=utils.message_decoder)
                # Decode Message Type
                if message.msg_type == settings.MSG_TYPE_DISCOVER:
                    # Discovery Message
                    with lock:
                        utils.log_message(
                            "Received discovery inquiry from {0}, responding..."
                            .format(addr), utils.Level.MEDIUM)
                    response = Message(settings.MSG_TYPE_SUCCESS,
                                       daemon.ledger.id).__repr__()
                    discovery_socket.sendto(response.encode(), addr)

                elif message.msg_type == settings.MSG_TYPE_HB:
                    # Heartbeat Message
                    if "ledger" in message.msg and message.msg[
                            "ledger"] == daemon.ledger.id:

                        # Add the source address and port to our list of peers and update the date
                        daemon.peers[addr[0]] = datetime.now()

                        # Possible Scenarios:
                        # Heartbeat tail is same as local tail: Do nothing (in sync)
                        # Heartbeat tail is in our ledger: Do nothing (out of sync)
                        # Heartbeat tail is not in our ledger: Synchronize with peer (out of sync)
                        if "tail" in message.msg:
                            tail = message.msg["tail"]

                            # Check for presence of heartbeat tail in our ledger
                            idx, blocks = daemon.ledger.search(tail)

                            # If heartbeat tail is in our ledger, do nothing
                            # If heartbeat tail isn't in our ledger, synchronize with peer
                            if len(idx) <= 0:
                                block_sync((addr[0], settings.BIND_PORT),
                                           daemon.ledger.tail.hash)

                        with lock:
                            utils.log_message(
                                "Received heartbeat from {0}".format(addr),
                                utils.Level.LOW)

        discovery_socket.close()
Ejemplo n.º 17
0
 def __init__(self, socket):
     super(TCPConnectionThread, self).__init__()
     with lock:
         utils.log_message("Spawning TCP Connection Thread from {0}".format(
             socket.getsockname()))
     self._socket = socket
Ejemplo n.º 18
0
    def run(self):

        # Get message
        message = ''
        message_size = None
        try:
            while True:
                if message_size is None:
                    data = self._socket.recv(settings.MSG_SIZE_BYTES)
                    # Convert first message size to an integer
                    message_size = int(data.decode())
                elif len(message) < message_size:
                    data = self._socket.recv(4096)
                    message += data.decode()
                else:
                    break
        except ValueError as e:
            utils.log_message('Received invalid packet from {0}'.format(
                self._socket.getsockname()))
            return

        with lock:
            utils.log_message(
                "Received message from {0}:\n{1}".format(
                    self._socket.getsockname(), message), utils.Level.MEDIUM)

        message = json.loads(message, object_hook=utils.message_decoder)

        # JOIN LEDGER
        if message.msg_type == settings.MSG_TYPE_JOIN:
            if message.msg == daemon.ledger.id:
                # Respond with success and the root key
                response = Message(settings.MSG_TYPE_SUCCESS,
                                   daemon.ledger.root.message).prep_tcp()
                self._respond(response)
                return
            else:
                self._respond_error()
                return
        elif message.msg_type == settings.MSG_TYPE_PEER:
            # Respond with list of peers
            peer_list = list(daemon.peers.keys())

            target = self._socket.getsockname()
            if target in peer_list:
                peer_list.remove(target)

            response = Message(settings.MSG_TYPE_SUCCESS, peer_list).prep_tcp()
            self._respond(response)
            return

        elif message.msg_type == settings.MSG_TYPE_LEDGER:
            # Respond with the ledger
            ledger_list = daemon.ledger.slice_ledger(message.msg)

            if ledger_list is None:
                self._respond_error()
                return

            response = Message(settings.MSG_TYPE_SUCCESS,
                               ledger_list).prep_tcp()
            self._respond(response)
            return

        # No response, send error status
        else:
            self._respond_error()
            return