diff --git a/node.py b/node.py new file mode 100755 index 0000000..fa410ef --- /dev/null +++ b/node.py @@ -0,0 +1,90 @@ +#! /usr/bin/env python3 + +import hashlib, socket, sys, threading, time + +DEFAULT_PORT = 62039 + +class Peer: + def __init__(self, ipv6, port, lifetime_counter): + self.ipv6 = ipv6 + self.port = port + self.lifetime_counter = lifetime_counter + +class Node: + def __init__(self, node_socket, peers): + self.node_socket = node_socket + self.peers = peers + +def parse_address(addr_str): + if addr_str.startswith('['): + closing = addr_str.find(']:') + if closing == -1: + raise Exception(f'Not a valid address: {addr_str}') + ipv6 = addr_str[1:closing] + port = int(addr_str[closing+2:]) + else: + ipv6 = addr_str + port = DEFAULT_PORT + return Peer(ipv6, port, 10) + +def wait_until(target_time): + duration = target_time - time.time() + if duration > 0: + time.sleep(duration) + +def empty_transaction_list_hash(): + current_hash = 32 * b"\0" + for _ in range(1024): + entry = current_hash + 44 * b"\0" + current_hash = hashlib.sha256(entry).digest() + return current_hash + +def send_heartbeat(node, peer): + protocol_version = 2 * b"\0" + capable_version = 2 * b"\0" + msg_type = b"\0" + difficulty_sum = 32 * b"\0" + hash_value = empty_transaction_list_hash() + partner_ipv6 = 16 * b"\0" + partner_port = 2 * b"\0" + + heartbeat_msg = protocol_version + capable_version + msg_type + \ + difficulty_sum + hash_value + partner_ipv6 + partner_port + node.node_socket.sendto(heartbeat_msg, (peer.ipv6, peer.port)) + +def heartbeat(node): + while True: + peer_count = len(node.peers) + start_time = time.time() + for i, peer in enumerate(node.peers): + wait_until(start_time + 60 * (i+1) / peer_count) + send_heartbeat(node, peer) + +def receiver(node): + pass + +def main(): + address_arguments = sys.argv[1:] + peers = [parse_address(argument) for argument in address_arguments] + + node_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) + try: + node_socket.bind(("::", DEFAULT_PORT)) + except OSError as e: + if e.errno == 98: + node_socket.bind(("::", 0)) + port = node_socket.getsockname()[1] + print(f"Default port {DEFAULT_PORT} is in use, listening on port {port} instead.") + else: + raise e + + node = Node(node_socket, peers) + heartbeat_thread = threading.Thread(target = heartbeat, args = (node,)) + receiving_thread = threading.Thread(target = receiver, args = (node,)) + heartbeat_thread.start() + receiving_thread.start() + heartbeat_thread.join() + receiving_thread.join() + +if __name__ == '__main__': + main()