GitHub Action commited on
Commit
1e1ee41
·
1 Parent(s): e6c82a2

Sync from GitHub with Git LFS

Browse files
agents/bootstrap.txt CHANGED
@@ -1,4 +1,3 @@
1
- DID: "did:hmp:87836dac-a93c-4203-8710-5e4711b07e02"; NAME: "Agent_108"; KEY: "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAzqHBnykJSRs6PsvpsEXdZNSPAs+voBarQYDAR0B664I=\n-----END PUBLIC KEY-----\n"; ADDRESS: [{"addr": "tcp://95.72.96.108:4000", "nonce": 67802, "pow_hash": "00002dbeb3936beffc746b25bdbeff0e20c7c164281f9ebe4d03e76a28592211", "difficulty": 4, "datetime": "2025-08-28T15:44:51.608882+00:00"}, {"addr": "udp://95.72.96.108:4000", "nonce": 8072, "pow_hash": "0000e54c7d2a5e0dabd811e1b1ac3f8ec94029b29382d8bf8a9fe3fc12985dc6", "difficulty": 4, "datetime": "2025-08-28T15:44:51.773206+00:00"}, {"addr": "tcp://195.172.229.108:4000", "nonce": 368, "pow_hash": "000089b642d389d1c5e36f93e0983158c676e2cae21b981fe978c8488ed679e0", "difficulty": 4, "datetime": "2025-08-28T15:44:51.790767+00:00"}, {"addr": "udp://195.172.229.108:4000", "nonce": 24998, "pow_hash": "00004791d6e3042a876d465e63b5d6104ded6e492cd23300b6455eeed2b36701", "difficulty": 4, "datetime": "2025-08-28T15:44:51.791505+00:00"}];
2
- DID: "did:hmp:377e75d2-9eaf-4bfa-a02c-e9cc063b72d1"; NAME: "Agent_112"; KEY: "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAuvk/l8iJIQ3DcFJg+Jn5SFg+9xc7VRZ7p5SwsKyIwt4=\n-----END PUBLIC KEY-----\n"; ADDRESS: [{"addr": "tcp://95.72.96.112:4000", "nonce": 11491, "pow_hash": "0000b2f6b325bdf17b0da5ba1a3e66ed7cd49ba45ba681477af11e89ccd01ca8", "difficulty": 4, "datetime": "2025-08-28T15:47:03.319703+00:00"}, {"addr": "udp://95.72.96.112:4000", "nonce": 18356, "pow_hash": "0000d2c1e2b48141a15bad3f2be0ad091972be28f8e4e4fba5004bbfbc12f1ef", "difficulty": 4, "datetime": "2025-08-28T15:47:03.362438+00:00"}, {"addr": "tcp://195.172.229.112:4000", "nonce": 171, "pow_hash": "0000c5b7b126e83b93d3e809b022d7bf38225766d194052d15f6a750c465b515", "difficulty": 4, "datetime": "2025-08-28T15:47:03.404411+00:00"}, {"addr": "udp://195.172.229.112:4000", "nonce": 48621, "pow_hash": "000007acb2d37c02f0e3f2ce9bb4c1fd29bbbd2953a3f3218ee1313a18e662d9", "difficulty": 4, "datetime": "2025-08-28T15:47:03.404820+00:00"}];
3
- DID: "did:hmp:25e6c264-8e16-4bb7-8cce-5930f757df98"; NAME: "Agent_116"; KEY: "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAFgW23nQgQ89si67ZcRbsoZXqSS2rrWFnMAYKib29orA=\n-----END PUBLIC KEY-----\n"; ADDRESS: [{"addr": "tcp://95.72.96.116:4000", "nonce": 19471, "pow_hash": "000049397d5e7f91f09d6aa3da1537fedb95a1ef3456006a739cb4e81d37ee20", "difficulty": 4, "datetime": "2025-08-28T15:48:44.898372+00:00"}, {"addr": "udp://95.72.96.116:4000", "nonce": 67946, "pow_hash": "00002fb896fe39cf18408c5d87ee83a5e8eb76326205e5254e862b70bd336864", "difficulty": 4, "datetime": "2025-08-28T15:48:44.945521+00:00"}, {"addr": "tcp://195.172.229.116:4000", "nonce": 106038, "pow_hash": "000032d57ddc691502e9aea72fc2f3fd9018d6c5127f6c971af6aae2d964d5e9", "difficulty": 4, "datetime": "2025-08-28T15:48:45.097886+00:00"}, {"addr": "udp://195.172.229.116:4000", "nonce": 20754, "pow_hash": "00008df5e84f7d98b3e756784cad7a6518d202273ac5b9fcc6918803da0a815e", "difficulty": 4, "datetime": "2025-08-28T15:48:45.374646+00:00"}];
4
- DID: "did:hmp:3a30112e-6b14-4161-8ce9-189a6f4bdd1e"; NAME: "Agent_120"; KEY: "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAPIYEVWz9lBfxBI1MCHMoyVqAJpWH5wwLjAAFKV7Q6UM=\n-----END PUBLIC KEY-----\n"; ADDRESS: [{"addr": "tcp://95.72.96.120:4000", "nonce": 48308, "pow_hash": "0000bbf1c4880c752362fe6100d1eddb98530e9b36f7dda366dfbd3e8860a680", "difficulty": 4, "datetime": "2025-08-28T15:50:17.822119+00:00"}, {"addr": "udp://95.72.96.120:4000", "nonce": 277204, "pow_hash": "0000e71c10d990fb4af04a3a171f9735291ab21a8807acf9284fc39817e028a3", "difficulty": 4, "datetime": "2025-08-28T15:50:17.935199+00:00"}, {"addr": "tcp://195.172.229.120:4000", "nonce": 74353, "pow_hash": "0000c16fcdbbc456b93680fdf48e3a844a477d038c362dd01d70219d566defb0", "difficulty": 4, "datetime": "2025-08-28T15:50:18.661732+00:00"}, {"addr": "udp://195.172.229.120:4000", "nonce": 9937, "pow_hash": "000019a037ff2c15c8b88e45d63152dee141a779628370692973626bf0c343f7", "difficulty": 4, "datetime": "2025-08-28T15:50:18.849814+00:00"}];
 
1
+ DID: "did:hmp:300d25eb-25e2-4f8a-a1a9-818f09f67be4"; NAME: "Agent_203"; KEY: "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAAvaR44j54884fTvTX4qQ1o7bvpaT8ZnP/+FHmhl4xp4=\n-----END PUBLIC KEY-----\n"; ADDRESS: [{"addr": "tcp://97.203.96.27:4010", "nonce": 7364, "pow_hash": "00005fa2053a7dbc7e06269d427540668b7832982be8504189ae60822f17b613", "difficulty": 4, "datetime": "2025-08-28T21:01:16+00:00"}, {"addr": "udp://97.203.96.27:4010", "nonce": 46359, "pow_hash": "00008c063b6964de0bc509f412fc4bb13f6b61a55e96ed7f16ff5f1d2b8615b8", "difficulty": 4, "datetime": "2025-08-28T21:01:16+00:00"}];
2
+ DID: "did:hmp:424e79ff-6d44-4b43-9c04-883f76bce800"; NAME: "Agent_207"; KEY: "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAQ4p/UgxONBwF1Zo7dj0IqseesMpydBmqrWCZZrNnqAw=\n-----END PUBLIC KEY-----\n"; ADDRESS: [{"addr": "tcp://97.207.96.27:4010", "nonce": 90667, "pow_hash": "000078c10f5bf921115e57f73270ce36135ed2d92584bd7477afcd6951db55da", "difficulty": 4, "datetime": "2025-08-28T21:04:08+00:00"}, {"addr": "udp://97.207.96.27:4010", "nonce": 9245, "pow_hash": "000050bb4afd13fec665d289e52df1a71001f47e9cda29665d43a60ac260bb9d", "difficulty": 4, "datetime": "2025-08-28T21:04:08+00:00"}];
3
+ DID: "did:hmp:5f8bd63d-f31e-46aa-b95a-f95775945549"; NAME: "Agent_209"; KEY: "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA5IZRyvQZDqEo+/W4sHRVyoSh1yRElqmIYKAyO54y8sY=\n-----END PUBLIC KEY-----\n"; ADDRESS: [{"addr": "tcp://97.209.96.27:4010", "nonce": 20846, "pow_hash": "0000736894e8b9a06edaba964cabc5341f18102c4f20248aa27bb13fb4d13d71", "difficulty": 4, "datetime": "2025-08-28T21:07:58+00:00"}, {"addr": "udp://97.209.96.27:4010", "nonce": 117913, "pow_hash": "0000f5b2b30244f7851ccf9473fa89583034201e4d9b26b3261cb6ea70034051", "difficulty": 4, "datetime": "2025-08-28T21:07:58+00:00"}];
 
agents/examples/bootstrap.txt CHANGED
@@ -1,4 +1,3 @@
1
- DID: "did:hmp:87836dac-a93c-4203-8710-5e4711b07e02"; NAME: "Agent_108"; KEY: "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAzqHBnykJSRs6PsvpsEXdZNSPAs+voBarQYDAR0B664I=\n-----END PUBLIC KEY-----\n"; ADDRESS: [{"addr": "tcp://95.72.96.108:4000", "nonce": 67802, "pow_hash": "00002dbeb3936beffc746b25bdbeff0e20c7c164281f9ebe4d03e76a28592211", "difficulty": 4, "datetime": "2025-08-28T15:44:51.608882+00:00"}, {"addr": "udp://95.72.96.108:4000", "nonce": 8072, "pow_hash": "0000e54c7d2a5e0dabd811e1b1ac3f8ec94029b29382d8bf8a9fe3fc12985dc6", "difficulty": 4, "datetime": "2025-08-28T15:44:51.773206+00:00"}, {"addr": "tcp://195.172.229.108:4000", "nonce": 368, "pow_hash": "000089b642d389d1c5e36f93e0983158c676e2cae21b981fe978c8488ed679e0", "difficulty": 4, "datetime": "2025-08-28T15:44:51.790767+00:00"}, {"addr": "udp://195.172.229.108:4000", "nonce": 24998, "pow_hash": "00004791d6e3042a876d465e63b5d6104ded6e492cd23300b6455eeed2b36701", "difficulty": 4, "datetime": "2025-08-28T15:44:51.791505+00:00"}];
2
- DID: "did:hmp:377e75d2-9eaf-4bfa-a02c-e9cc063b72d1"; NAME: "Agent_112"; KEY: "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAuvk/l8iJIQ3DcFJg+Jn5SFg+9xc7VRZ7p5SwsKyIwt4=\n-----END PUBLIC KEY-----\n"; ADDRESS: [{"addr": "tcp://95.72.96.112:4000", "nonce": 11491, "pow_hash": "0000b2f6b325bdf17b0da5ba1a3e66ed7cd49ba45ba681477af11e89ccd01ca8", "difficulty": 4, "datetime": "2025-08-28T15:47:03.319703+00:00"}, {"addr": "udp://95.72.96.112:4000", "nonce": 18356, "pow_hash": "0000d2c1e2b48141a15bad3f2be0ad091972be28f8e4e4fba5004bbfbc12f1ef", "difficulty": 4, "datetime": "2025-08-28T15:47:03.362438+00:00"}, {"addr": "tcp://195.172.229.112:4000", "nonce": 171, "pow_hash": "0000c5b7b126e83b93d3e809b022d7bf38225766d194052d15f6a750c465b515", "difficulty": 4, "datetime": "2025-08-28T15:47:03.404411+00:00"}, {"addr": "udp://195.172.229.112:4000", "nonce": 48621, "pow_hash": "000007acb2d37c02f0e3f2ce9bb4c1fd29bbbd2953a3f3218ee1313a18e662d9", "difficulty": 4, "datetime": "2025-08-28T15:47:03.404820+00:00"}];
3
- DID: "did:hmp:25e6c264-8e16-4bb7-8cce-5930f757df98"; NAME: "Agent_116"; KEY: "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAFgW23nQgQ89si67ZcRbsoZXqSS2rrWFnMAYKib29orA=\n-----END PUBLIC KEY-----\n"; ADDRESS: [{"addr": "tcp://95.72.96.116:4000", "nonce": 19471, "pow_hash": "000049397d5e7f91f09d6aa3da1537fedb95a1ef3456006a739cb4e81d37ee20", "difficulty": 4, "datetime": "2025-08-28T15:48:44.898372+00:00"}, {"addr": "udp://95.72.96.116:4000", "nonce": 67946, "pow_hash": "00002fb896fe39cf18408c5d87ee83a5e8eb76326205e5254e862b70bd336864", "difficulty": 4, "datetime": "2025-08-28T15:48:44.945521+00:00"}, {"addr": "tcp://195.172.229.116:4000", "nonce": 106038, "pow_hash": "000032d57ddc691502e9aea72fc2f3fd9018d6c5127f6c971af6aae2d964d5e9", "difficulty": 4, "datetime": "2025-08-28T15:48:45.097886+00:00"}, {"addr": "udp://195.172.229.116:4000", "nonce": 20754, "pow_hash": "00008df5e84f7d98b3e756784cad7a6518d202273ac5b9fcc6918803da0a815e", "difficulty": 4, "datetime": "2025-08-28T15:48:45.374646+00:00"}];
4
- DID: "did:hmp:3a30112e-6b14-4161-8ce9-189a6f4bdd1e"; NAME: "Agent_120"; KEY: "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAPIYEVWz9lBfxBI1MCHMoyVqAJpWH5wwLjAAFKV7Q6UM=\n-----END PUBLIC KEY-----\n"; ADDRESS: [{"addr": "tcp://95.72.96.120:4000", "nonce": 48308, "pow_hash": "0000bbf1c4880c752362fe6100d1eddb98530e9b36f7dda366dfbd3e8860a680", "difficulty": 4, "datetime": "2025-08-28T15:50:17.822119+00:00"}, {"addr": "udp://95.72.96.120:4000", "nonce": 277204, "pow_hash": "0000e71c10d990fb4af04a3a171f9735291ab21a8807acf9284fc39817e028a3", "difficulty": 4, "datetime": "2025-08-28T15:50:17.935199+00:00"}, {"addr": "tcp://195.172.229.120:4000", "nonce": 74353, "pow_hash": "0000c16fcdbbc456b93680fdf48e3a844a477d038c362dd01d70219d566defb0", "difficulty": 4, "datetime": "2025-08-28T15:50:18.661732+00:00"}, {"addr": "udp://195.172.229.120:4000", "nonce": 9937, "pow_hash": "000019a037ff2c15c8b88e45d63152dee141a779628370692973626bf0c343f7", "difficulty": 4, "datetime": "2025-08-28T15:50:18.849814+00:00"}];
 
1
+ DID: "did:hmp:300d25eb-25e2-4f8a-a1a9-818f09f67be4"; NAME: "Agent_203"; KEY: "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAAvaR44j54884fTvTX4qQ1o7bvpaT8ZnP/+FHmhl4xp4=\n-----END PUBLIC KEY-----\n"; ADDRESS: [{"addr": "tcp://97.203.96.27:4010", "nonce": 7364, "pow_hash": "00005fa2053a7dbc7e06269d427540668b7832982be8504189ae60822f17b613", "difficulty": 4, "datetime": "2025-08-28T21:01:16+00:00"}, {"addr": "udp://97.203.96.27:4010", "nonce": 46359, "pow_hash": "00008c063b6964de0bc509f412fc4bb13f6b61a55e96ed7f16ff5f1d2b8615b8", "difficulty": 4, "datetime": "2025-08-28T21:01:16+00:00"}];
2
+ DID: "did:hmp:424e79ff-6d44-4b43-9c04-883f76bce800"; NAME: "Agent_207"; KEY: "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAQ4p/UgxONBwF1Zo7dj0IqseesMpydBmqrWCZZrNnqAw=\n-----END PUBLIC KEY-----\n"; ADDRESS: [{"addr": "tcp://97.207.96.27:4010", "nonce": 90667, "pow_hash": "000078c10f5bf921115e57f73270ce36135ed2d92584bd7477afcd6951db55da", "difficulty": 4, "datetime": "2025-08-28T21:04:08+00:00"}, {"addr": "udp://97.207.96.27:4010", "nonce": 9245, "pow_hash": "000050bb4afd13fec665d289e52df1a71001f47e9cda29665d43a60ac260bb9d", "difficulty": 4, "datetime": "2025-08-28T21:04:08+00:00"}];
3
+ DID: "did:hmp:5f8bd63d-f31e-46aa-b95a-f95775945549"; NAME: "Agent_209"; KEY: "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA5IZRyvQZDqEo+/W4sHRVyoSh1yRElqmIYKAyO54y8sY=\n-----END PUBLIC KEY-----\n"; ADDRESS: [{"addr": "tcp://97.209.96.27:4010", "nonce": 20846, "pow_hash": "0000736894e8b9a06edaba964cabc5341f18102c4f20248aa27bb13fb4d13d71", "difficulty": 4, "datetime": "2025-08-28T21:07:58+00:00"}, {"addr": "udp://97.209.96.27:4010", "nonce": 117913, "pow_hash": "0000f5b2b30244f7851ccf9473fa89583034201e4d9b26b3261cb6ea70034051", "difficulty": 4, "datetime": "2025-08-28T21:07:58+00:00"}];
 
agents/init.py CHANGED
@@ -55,8 +55,8 @@ def init_identity(storage, config):
55
  "pubkey": pubkey,
56
  "privkey": privkey,
57
  "metadata": json.dumps({"role": config.get("agent_role", "core")}),
58
- "created_at": datetime.now(UTC).isoformat(),
59
- "updated_at": datetime.now(UTC).isoformat()
60
  }
61
  storage.add_identity(identity)
62
 
@@ -109,7 +109,7 @@ def init_llm_backends(storage, config):
109
  "name": backend["name"],
110
  "endpoint": desc,
111
  "metadata": json.dumps(backend),
112
- "created_at": datetime.now(UTC).isoformat()
113
  }
114
  storage.add_llm(llm)
115
  print(f"[+] Зарегистрирован LLM: {backend['name']}")
@@ -140,7 +140,7 @@ def update_pow_for_addresses(storage, difficulty=4):
140
  "nonce": 108834,
141
  "pow_hash": "0000665ea1440781356d7a9b899fc03a01a4f8342d3cfa3d75bb2619b66b4cfb",
142
  "difficulty": 4,
143
- "datetime": "2025-08-28T11:00:00+03:00"
144
  }
145
  """
146
  raw_id = storage.get_config_value("agent_id")
@@ -171,7 +171,7 @@ def update_pow_for_addresses(storage, difficulty=4):
171
 
172
  enriched = []
173
  for addr in addresses:
174
- dt_now = datetime.now(UTC).isoformat()
175
 
176
  if isinstance(addr, dict) and "addr" in addr and "pow_hash" in addr:
177
  # уже в новом формате → оставляем как есть
@@ -180,7 +180,7 @@ def update_pow_for_addresses(storage, difficulty=4):
180
 
181
  # строка → нужно сгенерировать PoW
182
  addr_str = addr if isinstance(addr, str) else addr.get("address")
183
- dt_now = datetime.now(UTC).isoformat()
184
  if addr_str:
185
  nonce, hash_value, dt_now = storage.generate_pow(
186
  peer_id=agent_id,
@@ -227,7 +227,7 @@ def init_prompts_and_ethics():
227
  ON CONFLICT(id) DO UPDATE SET
228
  content=excluded.content,
229
  updated_at=excluded.updated_at
230
- """, (pid, fname, ptype, "1.0", "local", content, datetime.now(UTC).isoformat()))
231
  print(f"[+] Загружен промпт: {fname} ({ptype})")
232
 
233
  # Загружаем ethics.yml
@@ -273,7 +273,7 @@ def init_prompts_and_ethics():
273
  json.dumps(ethics_data.get("evaluation"), ensure_ascii=False),
274
  json.dumps(ethics_data.get("violation_policy"), ensure_ascii=False),
275
  json.dumps(ethics_data.get("audit"), ensure_ascii=False),
276
- datetime.now(UTC).isoformat()
277
  ))
278
  print(f"[+] Загружена этическая политика: {eid}")
279
  else:
 
55
  "pubkey": pubkey,
56
  "privkey": privkey,
57
  "metadata": json.dumps({"role": config.get("agent_role", "core")}),
58
+ "created_at": datetime.now(UTC).replace(microsecond=0).isoformat(),
59
+ "updated_at": datetime.now(UTC).replace(microsecond=0).isoformat()
60
  }
61
  storage.add_identity(identity)
62
 
 
109
  "name": backend["name"],
110
  "endpoint": desc,
111
  "metadata": json.dumps(backend),
112
+ "created_at": datetime.now(UTC).replace(microsecond=0).isoformat()
113
  }
114
  storage.add_llm(llm)
115
  print(f"[+] Зарегистрирован LLM: {backend['name']}")
 
140
  "nonce": 108834,
141
  "pow_hash": "0000665ea1440781356d7a9b899fc03a01a4f8342d3cfa3d75bb2619b66b4cfb",
142
  "difficulty": 4,
143
+ "datetime": "2025-08-28T11:00:00+00:00"
144
  }
145
  """
146
  raw_id = storage.get_config_value("agent_id")
 
171
 
172
  enriched = []
173
  for addr in addresses:
174
+ dt_now = datetime.now(UTC).replace(microsecond=0).isoformat()
175
 
176
  if isinstance(addr, dict) and "addr" in addr and "pow_hash" in addr:
177
  # уже в новом формате → оставляем как есть
 
180
 
181
  # строка → нужно сгенерировать PoW
182
  addr_str = addr if isinstance(addr, str) else addr.get("address")
183
+ dt_now = datetime.now(UTC).replace(microsecond=0).isoformat()
184
  if addr_str:
185
  nonce, hash_value, dt_now = storage.generate_pow(
186
  peer_id=agent_id,
 
227
  ON CONFLICT(id) DO UPDATE SET
228
  content=excluded.content,
229
  updated_at=excluded.updated_at
230
+ """, (pid, fname, ptype, "1.0", "local", content, datetime.now(UTC).replace(microsecond=0).isoformat()))
231
  print(f"[+] Загружен промпт: {fname} ({ptype})")
232
 
233
  # Загружаем ethics.yml
 
273
  json.dumps(ethics_data.get("evaluation"), ensure_ascii=False),
274
  json.dumps(ethics_data.get("violation_policy"), ensure_ascii=False),
275
  json.dumps(ethics_data.get("audit"), ensure_ascii=False),
276
+ datetime.now(UTC).replace(microsecond=0).isoformat()
277
  ))
278
  print(f"[+] Загружена этическая политика: {eid}")
279
  else:
agents/peer_sync.py CHANGED
@@ -8,6 +8,9 @@ import select
8
  import netifaces
9
  import re
10
  import ipaddress
 
 
 
11
 
12
  from datetime import datetime, timezone as UTC
13
  from tools.storage import Storage
@@ -134,67 +137,76 @@ def load_bootstrap_peers(filename="bootstrap.txt"):
134
  # UDP Discovery
135
  # ---------------------------
136
  def udp_discovery():
 
137
  DISCOVERY_INTERVAL = 30
138
 
139
- while True:
140
- try:
141
- # Получаем локальные адреса из storage
142
- local_addresses = storage.get_config_value("local_addresses", [])
143
- msg_data = json.dumps({
144
- "id": my_id,
145
- "name": agent_name,
146
- "addresses": local_addresses
147
- }).encode("utf-8")
 
148
 
149
- # Создаём UDP сокеты для прослушки
150
- listen_sockets = []
151
- for entry in local_addresses:
152
- addr_str = entry.get("addr") if isinstance(entry, dict) else entry
153
- pubkey = entry.get("pubkey") if isinstance(entry, dict) else None
154
- if not addr_str:
155
- continue
156
 
157
- proto, hostport = addr_str.split("://", 1)
158
- host, port = storage.parse_hostport(hostport)
159
- if not port or proto.lower() != "udp":
160
- continue
 
 
 
 
 
 
161
 
162
- # IPv4
163
- if not host.startswith("["):
164
- try:
165
- sock4 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
166
- sock4.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
167
- sock4.bind(("", port))
168
- listen_sockets.append(sock4)
169
- print(f"[UDP Discovery] Listening IPv4 on *:{port}")
170
- except Exception as e:
171
- print(f"[UDP Discovery] IPv4 bind failed on port {port}: {e}")
172
 
173
- # IPv6
174
- else:
175
- try:
176
- sock6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
177
- sock6.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
178
- sock6.bind(("::", port))
179
- listen_sockets.append(sock6)
180
- print(f"[UDP Discovery] Listening IPv6 on [::]:{port}")
181
- except Exception as e:
182
- print(f"[UDP Discovery] IPv6 bind failed on port {port}: {e}")
183
 
184
- # Приём сообщений
 
 
185
  if listen_sockets:
186
  rlist, _, _ = select.select(listen_sockets, [], [], 0.5)
187
  for sock in rlist:
188
  try:
189
  data, addr = sock.recvfrom(2048)
190
- msg = json.loads(data.decode("utf-8"))
 
 
 
 
 
 
 
191
  peer_id = msg.get("id")
192
  if peer_id == my_id:
193
  continue
194
  name = msg.get("name", "unknown")
195
  addresses = msg.get("addresses", [])
196
 
197
- # Фильтруем адреса по PoW и datetime
198
  valid_addresses = []
199
  for a in addresses:
200
  addr_str = a.get("addr")
@@ -204,20 +216,28 @@ def udp_discovery():
204
  dt = a.get("datetime")
205
  pubkey = a.get("pubkey")
206
 
207
- if not addr_str:
208
  continue
209
 
210
  # Проверка PoW
211
- if nonce is not None and pow_hash and difficulty is not None and pubkey:
212
- if not storage.verify_pow(peer_id, pubkey, addr_str, nonce, pow_hash, dt, difficulty):
 
 
213
  continue
214
 
215
- # Проверяем datetime
216
  existing = storage.get_peer_address(peer_id, addr_str)
217
- if existing:
218
- existing_dt = existing.get("datetime")
219
- if existing_dt and existing_dt >= dt:
220
- continue
 
 
 
 
 
 
221
 
222
  valid_addresses.append(a)
223
 
@@ -229,27 +249,31 @@ def udp_discovery():
229
  source="discovery",
230
  status="online"
231
  )
232
- print(f"[UDP Discovery] peer={peer_id} from {addr}")
233
 
234
  except Exception as e:
235
  print(f"[UDP Discovery] receive error: {e}")
236
 
237
- # Отправка broadcast/multicast с фильтрацией по PoW и datetime
 
238
  valid_local_addresses = []
 
239
  for a in local_addresses:
240
  addr_str = a.get("addr") if isinstance(a, dict) else a
241
  nonce = a.get("nonce")
242
  pow_hash = a.get("pow_hash")
243
  difficulty = a.get("difficulty")
244
  dt = a.get("datetime")
245
- pubkey = a.get("pubkey") if isinstance(a, dict) else None
246
 
247
- if not addr_str or not pubkey:
248
  continue
249
 
250
- # Проверка PoW
251
  if nonce is not None and pow_hash and difficulty is not None:
252
- if not storage.verify_pow(my_id, pubkey, addr_str, nonce, pow_hash, dt, difficulty):
 
 
253
  continue
254
 
255
  valid_local_addresses.append(a)
@@ -260,6 +284,8 @@ def udp_discovery():
260
  "addresses": valid_local_addresses
261
  }).encode("utf-8")
262
 
 
 
263
  for entry in valid_local_addresses:
264
  addr_str = entry.get("addr")
265
  proto, hostport = addr_str.split("://", 1)
@@ -276,10 +302,11 @@ def udp_discovery():
276
  try:
277
  sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
278
  sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
 
279
  sock.sendto(msg_data, (a["broadcast"], port))
280
  sock.close()
281
- except Exception:
282
- continue
283
 
284
  # IPv6 multicast ff02::1
285
  else:
@@ -294,10 +321,11 @@ def udp_discovery():
294
  sock6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
295
  sock6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, socket.if_nametoindex(iface))
296
  sock6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, 1)
 
297
  sock6.sendto(msg_data, (multicast_addr, port))
298
  sock6.close()
299
- except Exception:
300
- continue
301
 
302
  time.sleep(DISCOVERY_INTERVAL)
303
 
@@ -309,6 +337,7 @@ def udp_discovery():
309
  # TCP Peer Exchange (исходящие)
310
  # ---------------------------
311
  def tcp_peer_exchange():
 
312
  PEER_EXCHANGE_INTERVAL = 20 # секунды для отладки
313
 
314
  while True:
@@ -336,23 +365,29 @@ def tcp_peer_exchange():
336
  dt = addr_entry.get("datetime")
337
  pubkey = addr_entry.get("pubkey")
338
 
339
- # Нормализация адреса
340
  norm = storage.normalize_address(addr_str)
341
  if not norm:
342
  continue
343
 
344
  # Проверка PoW
345
  if nonce is not None and pow_hash and difficulty is not None and pubkey:
346
- if not storage.verify_pow(peer_id, pubkey, addr_str, nonce, pow_hash, dt, difficulty):
347
- print(f"[PeerExchange] PoW check failed for {addr_str}")
 
348
  continue
349
 
350
- # Проверяем datetime
351
  existing = storage.get_peer_address(peer_id, addr_str)
352
- if existing:
353
- existing_dt = existing.get("datetime")
354
- if existing_dt and existing_dt >= dt:
355
- continue # старый адрес, пропускаем
 
 
 
 
 
 
356
 
357
  # Парсим host и port
358
  proto, hostport = norm.split("://", 1)
@@ -380,7 +415,7 @@ def tcp_peer_exchange():
380
  sock.settimeout(3)
381
  sock.connect((host, port))
382
 
383
- # Отправка своих адресов
384
  if storage.is_private(host):
385
  send_addresses = all_addresses
386
  else:
@@ -395,8 +430,11 @@ def tcp_peer_exchange():
395
  "name": agent_name,
396
  "addresses": send_addresses,
397
  }
398
- sock.sendall(json.dumps(handshake).encode("utf-8"))
 
 
399
 
 
400
  data = sock.recv(64 * 1024)
401
  sock.close()
402
 
@@ -404,15 +442,25 @@ def tcp_peer_exchange():
404
  print(f"[PeerExchange] No data from {host}:{port}")
405
  continue
406
 
 
 
407
  try:
408
  peers_recv = json.loads(data.decode("utf-8"))
 
409
  for p in peers_recv:
410
- # Сохраняем только новые адреса или более новые datetime
411
  new_addrs = []
412
  for a in p.get("addresses", []):
413
- existing_addr = storage.get_peer_address(p["id"], a.get("addr"))
414
- if existing_addr is None or existing_addr.get("datetime", "") < a.get("datetime", ""):
415
- new_addrs.append(a)
 
 
 
 
 
 
 
 
416
 
417
  if new_addrs:
418
  storage.add_or_update_peer(
@@ -422,9 +470,10 @@ def tcp_peer_exchange():
422
  source="peer_exchange",
423
  status="online"
424
  )
 
425
  print(f"[PeerExchange] Received {len(peers_recv)} peers from {host}:{port}")
426
  except Exception as e:
427
- print(f"[PeerExchange] Decode error from {host}:{port} -> {e}")
428
  continue
429
 
430
  break
@@ -463,11 +512,15 @@ def tcp_listener():
463
  conn, addr = s.accept()
464
  data = conn.recv(64 * 1024)
465
  if not data:
 
466
  conn.close()
467
  continue
468
 
 
 
469
  try:
470
  msg = json.loads(data.decode("utf-8"))
 
471
  except Exception as e:
472
  print(f"[TCP Listener] JSON decode error from {addr}: {e}")
473
  conn.close()
@@ -478,7 +531,6 @@ def tcp_listener():
478
  peer_name = msg.get("name", "unknown")
479
  peer_addrs = msg.get("addresses", [])
480
 
481
- # Добавляем/обновляем пира только если PoW валидный и datetime новее
482
  valid_addrs = []
483
  for a in peer_addrs:
484
  addr_value = a.get("addr")
@@ -489,16 +541,25 @@ def tcp_listener():
489
  pubkey = a.get("pubkey")
490
 
491
  if not addr_value or nonce is None or not pow_hash or not pubkey:
 
492
  continue
493
 
494
- if not storage.verify_pow(peer_id, pubkey, addr_value, nonce, pow_hash, dt, difficulty):
 
 
495
  continue
496
 
497
  existing = storage.get_peer_address(peer_id, addr_value)
498
- if existing:
499
- existing_dt = existing.get("datetime")
500
- if existing_dt and existing_dt >= dt:
501
- continue # старый адрес
 
 
 
 
 
 
502
 
503
  valid_addrs.append(a)
504
 
@@ -510,10 +571,13 @@ def tcp_listener():
510
  source="incoming",
511
  status="online"
512
  )
 
 
 
513
 
514
- print(f"[TCP Listener] Handshake from {peer_id} ({addr})")
515
 
516
- # Отправляем актуальные адреса собеседнику
517
  is_lan = storage.is_private(addr[0])
518
  peers_list = []
519
 
@@ -526,24 +590,34 @@ def tcp_listener():
526
 
527
  updated_addresses = []
528
  for a in addresses:
529
- proto, hostport = a["addr"].split("://")
530
- host, port = storage.parse_hostport(hostport)
 
 
 
531
 
532
- # Фильтруем по LAN/Internet
533
- if not is_lan and not is_public(host):
534
- continue
535
 
536
- # IPv6 link-local
537
- if storage.is_ipv6(host) and host.startswith("fe80:"):
538
- scope_id = storage.get_ipv6_scope(host)
539
- if scope_id:
540
- host = f"{host}%{scope_id}"
541
 
542
- updated_addresses.append(f"{proto}://{host}:{port}")
 
 
 
 
543
 
544
- peers_list.append({"id": peer_id_local, "addresses": updated_addresses})
 
 
 
545
 
 
546
  conn.sendall(json.dumps(peers_list).encode("utf-8"))
 
547
  conn.close()
548
  except Exception as e:
549
  print(f"[TCP Listener] Connection handling error: {e}")
 
8
  import netifaces
9
  import re
10
  import ipaddress
11
+ import asyncio
12
+ import dateutil.parser
13
+
14
 
15
  from datetime import datetime, timezone as UTC
16
  from tools.storage import Storage
 
137
  # UDP Discovery
138
  # ---------------------------
139
  def udp_discovery():
140
+ import dateutil.parser # для парсинга ISO datetime
141
  DISCOVERY_INTERVAL = 30
142
 
143
+ try:
144
+ # --- Создаём слушающие сокеты один раз ---
145
+ listen_sockets = []
146
+ local_addresses = storage.get_config_value("local_addresses", [])
147
+ print(f"[UDP Discovery] Local addresses (init): {local_addresses}")
148
+
149
+ for entry in local_addresses:
150
+ addr_str = entry.get("addr") if isinstance(entry, dict) else entry
151
+ if not addr_str:
152
+ continue
153
 
154
+ proto, hostport = addr_str.split("://", 1)
155
+ host, port = storage.parse_hostport(hostport)
156
+ if not port or proto.lower() != "udp":
157
+ continue
 
 
 
158
 
159
+ # IPv4
160
+ if not host.startswith("["):
161
+ try:
162
+ sock4 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
163
+ sock4.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
164
+ sock4.bind(("", port))
165
+ listen_sockets.append(sock4)
166
+ print(f"[UDP Discovery] Listening IPv4 on *:{port}")
167
+ except Exception as e:
168
+ print(f"[UDP Discovery] IPv4 bind failed on port {port}: {e}")
169
 
170
+ # IPv6
171
+ else:
172
+ try:
173
+ sock6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
174
+ sock6.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
175
+ sock6.bind(("::", port))
176
+ listen_sockets.append(sock6)
177
+ print(f"[UDP Discovery] Listening IPv6 on [::]:{port}")
178
+ except Exception as e:
179
+ print(f"[UDP Discovery] IPv6 bind failed on port {port}: {e}")
180
 
181
+ except Exception as init_e:
182
+ print(f"[UDP Discovery] init error: {init_e}")
183
+ return
184
+
185
+ # --- Основной цикл ---
186
+ agent_pubkey = storage.get_config_value("agent_pubkey")
 
 
 
 
187
 
188
+ while True:
189
+ try:
190
+ # Приём входящих пакетов
191
  if listen_sockets:
192
  rlist, _, _ = select.select(listen_sockets, [], [], 0.5)
193
  for sock in rlist:
194
  try:
195
  data, addr = sock.recvfrom(2048)
196
+ print(f"[UDP Discovery] RAW from {addr}: {data!r}")
197
+
198
+ try:
199
+ msg = json.loads(data.decode("utf-8"))
200
+ except Exception as e:
201
+ print(f"[UDP Discovery] JSON decode error from {addr}: {e}")
202
+ continue
203
+
204
  peer_id = msg.get("id")
205
  if peer_id == my_id:
206
  continue
207
  name = msg.get("name", "unknown")
208
  addresses = msg.get("addresses", [])
209
 
 
210
  valid_addresses = []
211
  for a in addresses:
212
  addr_str = a.get("addr")
 
216
  dt = a.get("datetime")
217
  pubkey = a.get("pubkey")
218
 
219
+ if not addr_str or not pubkey:
220
  continue
221
 
222
  # Проверка PoW
223
+ if nonce is not None and pow_hash and difficulty is not None:
224
+ ok = storage.verify_pow(peer_id, pubkey, addr_str, nonce, pow_hash, dt, difficulty)
225
+ print(f"[UDP Discovery] Verify PoW for {addr_str} = {ok}")
226
+ if not ok:
227
  continue
228
 
229
+ # Проверка datetime
230
  existing = storage.get_peer_address(peer_id, addr_str)
231
+ try:
232
+ existing_dt = dateutil.parser.isoparse(existing.get("datetime")) if existing else None
233
+ dt_obj = dateutil.parser.isoparse(dt) if dt else None
234
+ except Exception as e:
235
+ print(f"[UDP Discovery] datetime parse error: {e}")
236
+ continue
237
+
238
+ if existing_dt and dt_obj and existing_dt >= dt_obj:
239
+ print(f"[UDP Discovery] Skip {addr_str}: old datetime {dt}")
240
+ continue
241
 
242
  valid_addresses.append(a)
243
 
 
249
  source="discovery",
250
  status="online"
251
  )
252
+ print(f"[UDP Discovery] Accepted peer {peer_id} ({addr}), {len(valid_addresses)} addresses")
253
 
254
  except Exception as e:
255
  print(f"[UDP Discovery] receive error: {e}")
256
 
257
+ # --- Отправка broadcast/multicast ---
258
+ local_addresses = storage.get_config_value("local_addresses", [])
259
  valid_local_addresses = []
260
+
261
  for a in local_addresses:
262
  addr_str = a.get("addr") if isinstance(a, dict) else a
263
  nonce = a.get("nonce")
264
  pow_hash = a.get("pow_hash")
265
  difficulty = a.get("difficulty")
266
  dt = a.get("datetime")
267
+ pubkey = a.get("pubkey") if isinstance(a, dict) else agent_pubkey # self-check
268
 
269
+ if not addr_str:
270
  continue
271
 
272
+ # Проверка PoW только если есть необходимые поля
273
  if nonce is not None and pow_hash and difficulty is not None:
274
+ ok = storage.verify_pow(my_id, pubkey, addr_str, nonce, pow_hash, dt, difficulty)
275
+ print(f"[UDP Discovery] Self-check PoW for {addr_str} = {ok}")
276
+ if not ok:
277
  continue
278
 
279
  valid_local_addresses.append(a)
 
284
  "addresses": valid_local_addresses
285
  }).encode("utf-8")
286
 
287
+ print(f"[UDP Discovery] Broadcasting: {msg_data}")
288
+
289
  for entry in valid_local_addresses:
290
  addr_str = entry.get("addr")
291
  proto, hostport = addr_str.split("://", 1)
 
302
  try:
303
  sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
304
  sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
305
+ print(f"[UDP Discovery] Sending broadcast -> {a['broadcast']}:{port}")
306
  sock.sendto(msg_data, (a["broadcast"], port))
307
  sock.close()
308
+ except Exception as e:
309
+ print(f"[UDP Discovery] Broadcast error {a['broadcast']}:{port}: {e}")
310
 
311
  # IPv6 multicast ff02::1
312
  else:
 
321
  sock6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
322
  sock6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, socket.if_nametoindex(iface))
323
  sock6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, 1)
324
+ print(f"[UDP Discovery] Sending multicast -> {multicast_addr}:{port}")
325
  sock6.sendto(msg_data, (multicast_addr, port))
326
  sock6.close()
327
+ except Exception as e:
328
+ print(f"[UDP Discovery] Multicast error {multicast_addr}:{port}: {e}")
329
 
330
  time.sleep(DISCOVERY_INTERVAL)
331
 
 
337
  # TCP Peer Exchange (исходящие)
338
  # ---------------------------
339
  def tcp_peer_exchange():
340
+ import dateutil.parser # для корректного парсинга ISO datetime
341
  PEER_EXCHANGE_INTERVAL = 20 # секунды для отладки
342
 
343
  while True:
 
365
  dt = addr_entry.get("datetime")
366
  pubkey = addr_entry.get("pubkey")
367
 
 
368
  norm = storage.normalize_address(addr_str)
369
  if not norm:
370
  continue
371
 
372
  # Проверка PoW
373
  if nonce is not None and pow_hash and difficulty is not None and pubkey:
374
+ ok = storage.verify_pow(peer_id, pubkey, addr_str, nonce, pow_hash, dt, difficulty)
375
+ print(f"[PeerExchange] Verify PoW for {peer_id}@{addr_str} = {ok}")
376
+ if not ok:
377
  continue
378
 
379
+ # Проверка datetime с использованием dateutil
380
  existing = storage.get_peer_address(peer_id, addr_str)
381
+ try:
382
+ existing_dt = dateutil.parser.isoparse(existing.get("datetime")) if existing else None
383
+ dt_obj = dateutil.parser.isoparse(dt) if dt else None
384
+ except Exception as e:
385
+ print(f"[PeerExchange] datetime parse error for {addr_str}: {e}")
386
+ continue
387
+
388
+ if existing_dt and dt_obj and existing_dt >= dt_obj:
389
+ print(f"[PeerExchange] Skip {addr_str}: old datetime {dt}")
390
+ continue
391
 
392
  # Парсим host и port
393
  proto, hostport = norm.split("://", 1)
 
415
  sock.settimeout(3)
416
  sock.connect((host, port))
417
 
418
+ # Отправляем handshake
419
  if storage.is_private(host):
420
  send_addresses = all_addresses
421
  else:
 
430
  "name": agent_name,
431
  "addresses": send_addresses,
432
  }
433
+ raw_handshake = json.dumps(handshake).encode("utf-8")
434
+ print(f"[PeerExchange] Sending handshake -> {host}:{port}: {raw_handshake}")
435
+ sock.sendall(raw_handshake)
436
 
437
+ # Читаем ответ
438
  data = sock.recv(64 * 1024)
439
  sock.close()
440
 
 
442
  print(f"[PeerExchange] No data from {host}:{port}")
443
  continue
444
 
445
+ print(f"[PeerExchange] RAW recv from {host}:{port}: {data!r}")
446
+
447
  try:
448
  peers_recv = json.loads(data.decode("utf-8"))
449
+ print(f"[PeerExchange] Parsed recv from {host}:{port}: {peers_recv}")
450
  for p in peers_recv:
 
451
  new_addrs = []
452
  for a in p.get("addresses", []):
453
+ try:
454
+ existing_addr = storage.get_peer_address(p["id"], a.get("addr"))
455
+ existing_dt = dateutil.parser.isoparse(existing_addr.get("datetime")) if existing_addr else None
456
+ dt_obj = dateutil.parser.isoparse(a.get("datetime")) if a.get("datetime") else None
457
+ if existing_addr is None or (existing_dt and dt_obj and existing_dt < dt_obj) or existing_dt is None:
458
+ new_addrs.append(a)
459
+ else:
460
+ print(f"[PeerExchange] Ignored old {a.get('addr')} from {p['id']}")
461
+ except Exception as e:
462
+ print(f"[PeerExchange] Error parsing datetime for {a.get('addr')}: {e}")
463
+ continue
464
 
465
  if new_addrs:
466
  storage.add_or_update_peer(
 
470
  source="peer_exchange",
471
  status="online"
472
  )
473
+ print(f"[PeerExchange] Stored {len(new_addrs)} new addrs for peer {p['id']}")
474
  print(f"[PeerExchange] Received {len(peers_recv)} peers from {host}:{port}")
475
  except Exception as e:
476
+ print(f"[PeerExchange] Decode error from {host}:{port}: {e}")
477
  continue
478
 
479
  break
 
512
  conn, addr = s.accept()
513
  data = conn.recv(64 * 1024)
514
  if not data:
515
+ print(f"[TCP Listener] Empty data from {addr}, closing")
516
  conn.close()
517
  continue
518
 
519
+ print(f"[TCP Listener] RAW recv from {addr}: {data!r}")
520
+
521
  try:
522
  msg = json.loads(data.decode("utf-8"))
523
+ print(f"[TCP Listener] Decoded JSON from {addr}: {msg}")
524
  except Exception as e:
525
  print(f"[TCP Listener] JSON decode error from {addr}: {e}")
526
  conn.close()
 
531
  peer_name = msg.get("name", "unknown")
532
  peer_addrs = msg.get("addresses", [])
533
 
 
534
  valid_addrs = []
535
  for a in peer_addrs:
536
  addr_value = a.get("addr")
 
541
  pubkey = a.get("pubkey")
542
 
543
  if not addr_value or nonce is None or not pow_hash or not pubkey:
544
+ print(f"[TCP Listener] Skip addr (incomplete): {a}")
545
  continue
546
 
547
+ ok = storage.verify_pow(peer_id, pubkey, addr_value, nonce, pow_hash, dt, difficulty)
548
+ print(f"[TCP Listener] Verify PoW for {addr_value} = {ok}")
549
+ if not ok:
550
  continue
551
 
552
  existing = storage.get_peer_address(peer_id, addr_value)
553
+ try:
554
+ existing_dt = dateutil.parser.isoparse(existing.get("datetime")) if existing else None
555
+ dt_obj = dateutil.parser.isoparse(dt) if dt else None
556
+ except Exception as e:
557
+ print(f"[TCP Listener] datetime parse error for {addr_value}: {e}")
558
+ continue
559
+
560
+ if existing_dt and dt_obj and existing_dt >= dt_obj:
561
+ print(f"[TCP Listener] Skip old addr {addr_value} (dt={dt})")
562
+ continue
563
 
564
  valid_addrs.append(a)
565
 
 
571
  source="incoming",
572
  status="online"
573
  )
574
+ print(f"[TCP Listener] Stored {len(valid_addrs)} addrs for peer {peer_id}")
575
+ else:
576
+ print(f"[TCP Listener] No valid addrs from {peer_id}")
577
 
578
+ print(f"[TCP Listener] Handshake from {peer_id} ({addr}) -> name={peer_name}")
579
 
580
+ # Готовим список пиров для ответа
581
  is_lan = storage.is_private(addr[0])
582
  peers_list = []
583
 
 
590
 
591
  updated_addresses = []
592
  for a in addresses:
593
+ try:
594
+ proto, hostport = a["addr"].split("://", 1)
595
+ host, port = storage.parse_hostport(hostport)
596
+ if not host or not port:
597
+ continue
598
 
599
+ if not is_lan and not is_public(host):
600
+ continue
 
601
 
602
+ if storage.is_ipv6(host) and host.startswith("fe80:"):
603
+ scope_id = storage.get_ipv6_scope(host)
604
+ if scope_id:
605
+ host = f"{host}%{scope_id}"
 
606
 
607
+ updated_addresses.append({
608
+ "addr": f"{proto}://{host}:{port}"
609
+ })
610
+ except Exception:
611
+ continue
612
 
613
+ peers_list.append({
614
+ "id": peer_id_local,
615
+ "addresses": updated_addresses
616
+ })
617
 
618
+ print(f"[TCP Listener] Sending {len(peers_list)} peers back to {peer_id}")
619
  conn.sendall(json.dumps(peers_list).encode("utf-8"))
620
+
621
  conn.close()
622
  except Exception as e:
623
  print(f"[TCP Listener] Connection handling error: {e}")
agents/requirements.txt CHANGED
@@ -14,4 +14,5 @@ passlib[bcrypt]
14
  werkzeug
15
  itsdangerous
16
  bleach
17
- netifaces
 
 
14
  werkzeug
15
  itsdangerous
16
  bleach
17
+ netifaces
18
+ python-dateutil
agents/tools/storage.py CHANGED
@@ -677,12 +677,14 @@ class Storage:
677
  self.conn.commit()
678
  return cursor.lastrowid
679
 
 
680
  def generate_pow(self, peer_id, pubkey, address, dt=None, difficulty=4):
681
  """
682
  Генерирует PoW для (peer_id + pubkey + address + datetime).
 
683
  """
684
  if dt is None:
685
- dt = datetime.now(UTC).isoformat()
686
 
687
  nonce = 0
688
  prefix = "0" * difficulty
@@ -1008,10 +1010,12 @@ class Storage:
1008
  return did.strip().strip('"').strip("'")
1009
 
1010
  # Работа с пирам (agent_peers)
 
1011
  @staticmethod
1012
  def verify_pow(peer_id, pubkey, address, nonce, pow_hash, dt, difficulty=4):
1013
  """
1014
  Проверяет PoW (peer_id + pubkey + address + datetime).
 
1015
  """
1016
  base = f"{peer_id}{pubkey}{address}{dt}{nonce}".encode()
1017
  h = hashlib.sha256(base).hexdigest()
@@ -1029,7 +1033,18 @@ class Storage:
1029
  norm_addresses = []
1030
  for a in (addresses or []):
1031
  if isinstance(a, dict) and "addr" in a:
1032
- dt = a.get("datetime") or datetime.now(UTC).isoformat()
 
 
 
 
 
 
 
 
 
 
 
1033
  norm_addresses.append({
1034
  "addr": self.normalize_address(a["addr"]),
1035
  "nonce": a.get("nonce"),
@@ -1041,7 +1056,7 @@ class Storage:
1041
  "addr": self.normalize_address(a),
1042
  "nonce": None,
1043
  "pow_hash": None,
1044
- "datetime": datetime.now(UTC).isoformat()
1045
  })
1046
 
1047
  # получаем существующую запись
@@ -1120,7 +1135,7 @@ class Storage:
1120
  json.dumps(combined_addresses),
1121
  source,
1122
  status,
1123
- datetime.now(UTC).isoformat(),
1124
  final_pubkey,
1125
  json.dumps(final_capabilities),
1126
  json.dumps(combined_heard_from)
 
677
  self.conn.commit()
678
  return cursor.lastrowid
679
 
680
+ # --- Генерация PoW ---
681
  def generate_pow(self, peer_id, pubkey, address, dt=None, difficulty=4):
682
  """
683
  Генерирует PoW для (peer_id + pubkey + address + datetime).
684
+ Используется ISO 8601 без микросекунд, UTC.
685
  """
686
  if dt is None:
687
+ dt = datetime.now(timezone.utc).replace(microsecond=0).isoformat()
688
 
689
  nonce = 0
690
  prefix = "0" * difficulty
 
1010
  return did.strip().strip('"').strip("'")
1011
 
1012
  # Работа с пирам (agent_peers)
1013
+ # --- Проверка PoW ---
1014
  @staticmethod
1015
  def verify_pow(peer_id, pubkey, address, nonce, pow_hash, dt, difficulty=4):
1016
  """
1017
  Проверяет PoW (peer_id + pubkey + address + datetime).
1018
+ dt ожидается в формате ISO 8601 без микросекунд, UTC.
1019
  """
1020
  base = f"{peer_id}{pubkey}{address}{dt}{nonce}".encode()
1021
  h = hashlib.sha256(base).hexdigest()
 
1033
  norm_addresses = []
1034
  for a in (addresses or []):
1035
  if isinstance(a, dict) and "addr" in a:
1036
+ # нормализация datetime: ISO 8601 без микросекунд
1037
+ dt_raw = a.get("datetime")
1038
+ if dt_raw:
1039
+ try:
1040
+ dt_obj = datetime.fromisoformat(dt_raw)
1041
+ dt_obj = dt_obj.astimezone(timezone.utc).replace(microsecond=0)
1042
+ dt = dt_obj.isoformat()
1043
+ except Exception:
1044
+ dt = datetime.now(timezone.utc).replace(microsecond=0).isoformat()
1045
+ else:
1046
+ dt = datetime.now(timezone.utc).replace(microsecond=0).isoformat()
1047
+
1048
  norm_addresses.append({
1049
  "addr": self.normalize_address(a["addr"]),
1050
  "nonce": a.get("nonce"),
 
1056
  "addr": self.normalize_address(a),
1057
  "nonce": None,
1058
  "pow_hash": None,
1059
+ "datetime": datetime.now(timezone.utc).replace(microsecond=0).isoformat()
1060
  })
1061
 
1062
  # получаем существующую запись
 
1135
  json.dumps(combined_addresses),
1136
  source,
1137
  status,
1138
+ datetime.now(timezone.utc).replace(microsecond=0).isoformat(),
1139
  final_pubkey,
1140
  json.dumps(final_capabilities),
1141
  json.dumps(combined_heard_from)