#! /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()