GitHub Action commited on
Commit
e9a81ea
·
1 Parent(s): 18163dc

Sync from GitHub with Git LFS

Browse files
agents/bootstrap.txt CHANGED
@@ -1,2 +1,3 @@
1
- http://node1.mesh.local:8000
2
- http://node2.mesh.local:8000
 
 
1
+ tcp://node1.mesh.local:8000
2
+ udp://node2.mesh.local:8000
3
+ any://node3.mesh.local:8000
agents/peer_sync.py CHANGED
@@ -2,12 +2,15 @@
2
 
3
  """
4
  peer_sync.py — фоновый процесс синхронизации пиров в сети HMP
 
5
  """
6
 
7
  import socket
8
  import threading
9
  import time
10
  import json
 
 
11
  from tools.storage import Storage
12
 
13
  storage = Storage()
@@ -17,7 +20,6 @@ storage = Storage()
17
  # ======================
18
 
19
  def udp_discovery_listener(udp_port: int):
20
- """Слушаем UDP-пакеты"""
21
  sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
22
  sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
23
  sock.bind(("", udp_port))
@@ -29,13 +31,25 @@ def udp_discovery_listener(udp_port: int):
29
  peer_id = msg.get("id")
30
  name = msg.get("name", "unknown")
31
  addresses = msg.get("addresses", [f"{addr[0]}:{msg.get('tcp_port', udp_port)}"])
32
- storage.add_or_update_peer(peer_id, name, addresses, source="discovery", status="online")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  except Exception as e:
34
  print("[PeerSync] Ошибка при обработке UDP пакета:", e)
35
 
36
-
37
  def udp_discovery_sender(agent_id: str, agent_name: str, udp_port: int, tcp_port: int):
38
- """Рассылаем UDP multicast/broadcast пакеты"""
39
  sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
40
  sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
41
 
@@ -52,42 +66,46 @@ def udp_discovery_sender(agent_id: str, agent_name: str, udp_port: int, tcp_port
52
 
53
  while True:
54
  now = time.time()
55
- # Multicast раз в минуту
56
  if int(now) % DISCOVERY_INTERVAL == 0:
57
  sock.sendto(json.dumps(msg).encode("utf-8"), ("239.255.0.1", udp_port))
58
- # Broadcast раз в 10 минут
59
  if now - last_broadcast > BROADCAST_INTERVAL:
60
  sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
61
  sock.sendto(json.dumps(msg).encode("utf-8"), ("255.255.255.255", udp_port))
62
  last_broadcast = now
63
  time.sleep(1)
64
 
65
-
66
  # ======================
67
  # Peer Exchange (TCP)
68
  # ======================
69
 
70
  def peer_exchange():
71
- """Периодический обмен списками пиров"""
72
  PEER_EXCHANGE_INTERVAL = 120
73
  while True:
74
  peers = storage.get_online_peers(limit=50)
75
  for peer in peers:
76
  peer_id, addresses = peer["id"], peer["addresses"]
77
  try:
78
- addr = json.loads(addresses)[0]
79
- host, port = addr.split(":")
80
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
81
- s.settimeout(2)
82
- s.connect((host, int(port)))
83
- # пока только "каркас"
84
- s.sendall(b"PEER_EXCHANGE_REQUEST")
85
- s.close()
 
 
 
 
 
 
 
 
 
86
  except Exception as e:
87
- print(f"[PeerSync] Не удалось подключиться к {peer_id} ({addresses}): {e}")
88
  time.sleep(PEER_EXCHANGE_INTERVAL)
89
 
90
-
91
  # ======================
92
  # Основной запуск
93
  # ======================
@@ -95,22 +113,16 @@ def peer_exchange():
95
  def start_sync():
96
  print("[PeerSync] Запуск фоновой синхронизации")
97
 
98
- # 1. Конфиг
99
  udp_port = int(storage.get_config_value("udp_port", 4000))
100
  tcp_port = int(storage.get_config_value("tcp_port", 5000))
101
  agent_id = storage.get_config_value("agent_id", "unknown-agent")
102
  agent_name = storage.get_config_value("agent_name", "HMP-Agent")
103
 
104
- # 2. Bootstrap загрузка
105
  storage.load_bootstrap()
106
 
107
- # 3. UDP discovery
108
  threading.Thread(target=udp_discovery_listener, args=(udp_port,), daemon=True).start()
109
  threading.Thread(target=udp_discovery_sender, args=(agent_id, agent_name, udp_port, tcp_port), daemon=True).start()
110
-
111
- # 4. Peer exchange (TCP)
112
  threading.Thread(target=peer_exchange, daemon=True).start()
113
 
114
- # вечный цикл (чтобы поток не завершался)
115
  while True:
116
  time.sleep(60)
 
2
 
3
  """
4
  peer_sync.py — фоновый процесс синхронизации пиров в сети HMP
5
+ с поддержкой tcp://, udp:// и any:// адресов
6
  """
7
 
8
  import socket
9
  import threading
10
  import time
11
  import json
12
+ import uuid
13
+ import requests
14
  from tools.storage import Storage
15
 
16
  storage = Storage()
 
20
  # ======================
21
 
22
  def udp_discovery_listener(udp_port: int):
 
23
  sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
24
  sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
25
  sock.bind(("", udp_port))
 
31
  peer_id = msg.get("id")
32
  name = msg.get("name", "unknown")
33
  addresses = msg.get("addresses", [f"{addr[0]}:{msg.get('tcp_port', udp_port)}"])
34
+
35
+ # Обрабатываем все адреса через normalize_address
36
+ normalized_addresses = []
37
+ for a in addresses:
38
+ norm = storage.normalize_address(a)
39
+ if norm is None:
40
+ continue
41
+ proto, hostport = norm.split("://")
42
+ # UDP и any учитываются для UDP discovery
43
+ if proto in ["udp", "any"]:
44
+ normalized_addresses.append(norm)
45
+
46
+ if normalized_addresses:
47
+ storage.add_or_update_peer(peer_id, name, normalized_addresses, source="discovery", status="online")
48
+
49
  except Exception as e:
50
  print("[PeerSync] Ошибка при обработке UDP пакета:", e)
51
 
 
52
  def udp_discovery_sender(agent_id: str, agent_name: str, udp_port: int, tcp_port: int):
 
53
  sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
54
  sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
55
 
 
66
 
67
  while True:
68
  now = time.time()
 
69
  if int(now) % DISCOVERY_INTERVAL == 0:
70
  sock.sendto(json.dumps(msg).encode("utf-8"), ("239.255.0.1", udp_port))
 
71
  if now - last_broadcast > BROADCAST_INTERVAL:
72
  sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
73
  sock.sendto(json.dumps(msg).encode("utf-8"), ("255.255.255.255", udp_port))
74
  last_broadcast = now
75
  time.sleep(1)
76
 
 
77
  # ======================
78
  # Peer Exchange (TCP)
79
  # ======================
80
 
81
  def peer_exchange():
 
82
  PEER_EXCHANGE_INTERVAL = 120
83
  while True:
84
  peers = storage.get_online_peers(limit=50)
85
  for peer in peers:
86
  peer_id, addresses = peer["id"], peer["addresses"]
87
  try:
88
+ addr_list = json.loads(addresses)
89
+ for addr in addr_list:
90
+ proto, hostport = addr.split("://") if "://" in addr else ("any", addr)
91
+ if proto not in ["tcp", "any"]:
92
+ continue # пропускаем UDP
93
+
94
+ host, port = hostport.split(":")
95
+ try:
96
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
97
+ s.settimeout(2)
98
+ s.connect((host, int(port)))
99
+ s.sendall(b"PEER_EXCHANGE_REQUEST")
100
+ s.close()
101
+ print(f"[PeerSync] Успешно подключились к {peer_id} ({addr})")
102
+ break # успешное подключение — можно не пробовать остальные адреса
103
+ except Exception as e:
104
+ print(f"[PeerSync] Не удалось подключиться к {peer_id} ({addr}): {e}")
105
  except Exception as e:
106
+ print(f"[PeerSync] Ошибка обработки адресов {peer_id} ({addresses}): {e}")
107
  time.sleep(PEER_EXCHANGE_INTERVAL)
108
 
 
109
  # ======================
110
  # Основной запуск
111
  # ======================
 
113
  def start_sync():
114
  print("[PeerSync] Запуск фоновой синхронизации")
115
 
 
116
  udp_port = int(storage.get_config_value("udp_port", 4000))
117
  tcp_port = int(storage.get_config_value("tcp_port", 5000))
118
  agent_id = storage.get_config_value("agent_id", "unknown-agent")
119
  agent_name = storage.get_config_value("agent_name", "HMP-Agent")
120
 
 
121
  storage.load_bootstrap()
122
 
 
123
  threading.Thread(target=udp_discovery_listener, args=(udp_port,), daemon=True).start()
124
  threading.Thread(target=udp_discovery_sender, args=(agent_id, agent_name, udp_port, tcp_port), daemon=True).start()
 
 
125
  threading.Thread(target=peer_exchange, daemon=True).start()
126
 
 
127
  while True:
128
  time.sleep(60)
agents/tools/storage.py CHANGED
@@ -896,24 +896,92 @@ class Storage:
896
  c.execute("SELECT id, addresses FROM agent_peers WHERE status='online' LIMIT ?", (limit,))
897
  return c.fetchall()
898
 
 
 
 
 
 
 
 
 
 
 
 
 
899
  def load_bootstrap(self, bootstrap_file="bootstrap.txt"):
900
- if not os.path.exists(bootstrap_file):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
901
  return
902
- with open(bootstrap_file, "r", encoding="utf-8") as f:
903
- for line in f:
904
- line = line.strip()
905
- if not line or line.startswith("#"):
 
 
 
 
 
906
  continue
907
- # Пример: http://node1.mesh.local:8000
908
- try:
909
- peer_url = line
910
- peer_id = str(uuid.uuid4()) # временно генерим ID, потом можно подтянуть DID
911
- name = peer_url.split("://")[1].split(":")[0]
912
- self.add_or_update_peer(peer_id, name, [peer_url], source="bootstrap")
913
- except Exception as e:
914
- print(f"[Storage] Ошибка парсинга bootstrap: {line} -> {e}")
915
 
916
- # Утилиты
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
917
 
 
918
  def close(self):
919
  self.conn.close()
 
896
  c.execute("SELECT id, addresses FROM agent_peers WHERE status='online' LIMIT ?", (limit,))
897
  return c.fetchall()
898
 
899
+ def normalize_address(self, addr: str) -> str:
900
+ """Нормализует адрес в формате tcp://, udp:// или any://"""
901
+ addr = addr.strip()
902
+ if not addr:
903
+ return None
904
+
905
+ if "://" not in addr:
906
+ # Универсальная запись → считаем, что оба порта используются
907
+ return f"any://{addr}"
908
+
909
+ return addr
910
+
911
  def load_bootstrap(self, bootstrap_file="bootstrap.txt"):
912
+ """
913
+ Загружает узлы из bootstrap.txt.
914
+ Поддерживаются адреса:
915
+ tcp://host:port
916
+ udp://host:port
917
+ any://host:port
918
+ TCP-узлы проверяются запросом /identity.
919
+ UDP/any регистрируются без проверки (any учитывается как TCP+UDP).
920
+ """
921
+ import requests
922
+
923
+ # Абсолютный путь к bootstrap.txt в корне проекта
924
+ base_path = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
925
+ bootstrap_path = os.path.join(base_path, bootstrap_file)
926
+
927
+ if not os.path.exists(bootstrap_path):
928
+ print(f"[Bootstrap] Файл {bootstrap_file} не найден по пути {bootstrap_path}")
929
  return
930
+
931
+ for line in open(bootstrap_path, encoding="utf-8"):
932
+ line = line.strip()
933
+ if not line or line.startswith("#"):
934
+ continue
935
+
936
+ try:
937
+ addr = self.normalize_address(line)
938
+ if addr is None:
939
  continue
 
 
 
 
 
 
 
 
940
 
941
+ proto, hostport = addr.split("://")
942
+
943
+ # TCP или any → проверяем identity
944
+ if proto in ["tcp", "any"]:
945
+ try:
946
+ url = f"http://{hostport}/identity"
947
+ r = requests.get(url, timeout=3)
948
+ if r.status_code == 200:
949
+ info = r.json()
950
+ peer_id = info.get("id")
951
+ name = info.get("name", "unknown")
952
+ pubkey = info.get("pubkey")
953
+ capabilities = info.get("capabilities", {})
954
+
955
+ self.add_or_update_peer(
956
+ peer_id=peer_id,
957
+ name=name,
958
+ addresses=[addr],
959
+ source="bootstrap",
960
+ status="online",
961
+ pubkey=pubkey,
962
+ capabilities=capabilities,
963
+ )
964
+ print(f"[Bootstrap] Добавлен узел {peer_id} ({addr})")
965
+ else:
966
+ print(f"[Bootstrap] {addr} недоступен (HTTP {r.status_code})")
967
+ except Exception as e:
968
+ print(f"[Bootstrap] Ошибка при подключении к {addr}: {e}")
969
+
970
+ # UDP или any → просто регистрируем
971
+ if proto in ["udp", "any"]:
972
+ peer_id = str(uuid.uuid4())
973
+ self.add_or_update_peer(
974
+ peer_id=peer_id,
975
+ name="unknown",
976
+ addresses=[addr],
977
+ source="bootstrap",
978
+ status="unknown"
979
+ )
980
+ print(f"[Bootstrap] Добавлен адрес (без проверки): {addr}")
981
+
982
+ except Exception as e:
983
+ print(f"[Bootstrap] Ошибка парсинга {line}: {e}")
984
 
985
+ # Утилиты
986
  def close(self):
987
  self.conn.close()