ReticulumTelemetryHub 0.1.0__py3-none-any.whl → 0.143.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. reticulum_telemetry_hub/api/__init__.py +23 -0
  2. reticulum_telemetry_hub/api/models.py +323 -0
  3. reticulum_telemetry_hub/api/service.py +836 -0
  4. reticulum_telemetry_hub/api/storage.py +528 -0
  5. reticulum_telemetry_hub/api/storage_base.py +156 -0
  6. reticulum_telemetry_hub/api/storage_models.py +118 -0
  7. reticulum_telemetry_hub/atak_cot/__init__.py +49 -0
  8. reticulum_telemetry_hub/atak_cot/base.py +277 -0
  9. reticulum_telemetry_hub/atak_cot/chat.py +506 -0
  10. reticulum_telemetry_hub/atak_cot/detail.py +235 -0
  11. reticulum_telemetry_hub/atak_cot/event.py +181 -0
  12. reticulum_telemetry_hub/atak_cot/pytak_client.py +569 -0
  13. reticulum_telemetry_hub/atak_cot/tak_connector.py +848 -0
  14. reticulum_telemetry_hub/config/__init__.py +25 -0
  15. reticulum_telemetry_hub/config/constants.py +7 -0
  16. reticulum_telemetry_hub/config/manager.py +515 -0
  17. reticulum_telemetry_hub/config/models.py +215 -0
  18. reticulum_telemetry_hub/embedded_lxmd/__init__.py +5 -0
  19. reticulum_telemetry_hub/embedded_lxmd/embedded.py +418 -0
  20. reticulum_telemetry_hub/internal_api/__init__.py +21 -0
  21. reticulum_telemetry_hub/internal_api/bus.py +344 -0
  22. reticulum_telemetry_hub/internal_api/core.py +690 -0
  23. reticulum_telemetry_hub/internal_api/v1/__init__.py +74 -0
  24. reticulum_telemetry_hub/internal_api/v1/enums.py +109 -0
  25. reticulum_telemetry_hub/internal_api/v1/manifest.json +8 -0
  26. reticulum_telemetry_hub/internal_api/v1/schemas.py +478 -0
  27. reticulum_telemetry_hub/internal_api/versioning.py +63 -0
  28. reticulum_telemetry_hub/lxmf_daemon/Handlers.py +122 -0
  29. reticulum_telemetry_hub/lxmf_daemon/LXMF.py +252 -0
  30. reticulum_telemetry_hub/lxmf_daemon/LXMPeer.py +898 -0
  31. reticulum_telemetry_hub/lxmf_daemon/LXMRouter.py +4227 -0
  32. reticulum_telemetry_hub/lxmf_daemon/LXMessage.py +1006 -0
  33. reticulum_telemetry_hub/lxmf_daemon/LXStamper.py +490 -0
  34. reticulum_telemetry_hub/lxmf_daemon/__init__.py +10 -0
  35. reticulum_telemetry_hub/lxmf_daemon/_version.py +1 -0
  36. reticulum_telemetry_hub/lxmf_daemon/lxmd.py +1655 -0
  37. reticulum_telemetry_hub/lxmf_telemetry/model/fields/field_telemetry_stream.py +6 -0
  38. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/__init__.py +3 -0
  39. {lxmf_telemetry → reticulum_telemetry_hub/lxmf_telemetry}/model/persistance/appearance.py +19 -19
  40. {lxmf_telemetry → reticulum_telemetry_hub/lxmf_telemetry}/model/persistance/peer.py +17 -13
  41. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/__init__.py +65 -0
  42. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/acceleration.py +68 -0
  43. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/ambient_light.py +37 -0
  44. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/angular_velocity.py +68 -0
  45. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/battery.py +68 -0
  46. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/connection_map.py +258 -0
  47. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/generic.py +841 -0
  48. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/gravity.py +68 -0
  49. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/humidity.py +37 -0
  50. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/information.py +42 -0
  51. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/location.py +110 -0
  52. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/lxmf_propagation.py +429 -0
  53. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/magnetic_field.py +68 -0
  54. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/physical_link.py +53 -0
  55. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/pressure.py +37 -0
  56. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/proximity.py +37 -0
  57. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/received.py +75 -0
  58. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/rns_transport.py +209 -0
  59. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/sensor.py +65 -0
  60. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/sensor_enum.py +27 -0
  61. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/sensor_mapping.py +58 -0
  62. reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/temperature.py +37 -0
  63. {lxmf_telemetry → reticulum_telemetry_hub/lxmf_telemetry}/model/persistance/sensors/time.py +36 -32
  64. {lxmf_telemetry → reticulum_telemetry_hub/lxmf_telemetry}/model/persistance/telemeter.py +26 -23
  65. reticulum_telemetry_hub/lxmf_telemetry/sampler.py +229 -0
  66. reticulum_telemetry_hub/lxmf_telemetry/telemeter_manager.py +409 -0
  67. reticulum_telemetry_hub/lxmf_telemetry/telemetry_controller.py +804 -0
  68. reticulum_telemetry_hub/northbound/__init__.py +5 -0
  69. reticulum_telemetry_hub/northbound/app.py +195 -0
  70. reticulum_telemetry_hub/northbound/auth.py +119 -0
  71. reticulum_telemetry_hub/northbound/gateway.py +310 -0
  72. reticulum_telemetry_hub/northbound/internal_adapter.py +302 -0
  73. reticulum_telemetry_hub/northbound/models.py +213 -0
  74. reticulum_telemetry_hub/northbound/routes_chat.py +123 -0
  75. reticulum_telemetry_hub/northbound/routes_files.py +119 -0
  76. reticulum_telemetry_hub/northbound/routes_rest.py +345 -0
  77. reticulum_telemetry_hub/northbound/routes_subscribers.py +150 -0
  78. reticulum_telemetry_hub/northbound/routes_topics.py +178 -0
  79. reticulum_telemetry_hub/northbound/routes_ws.py +107 -0
  80. reticulum_telemetry_hub/northbound/serializers.py +72 -0
  81. reticulum_telemetry_hub/northbound/services.py +373 -0
  82. reticulum_telemetry_hub/northbound/websocket.py +855 -0
  83. reticulum_telemetry_hub/reticulum_server/__main__.py +2237 -0
  84. reticulum_telemetry_hub/reticulum_server/command_manager.py +1268 -0
  85. reticulum_telemetry_hub/reticulum_server/command_text.py +399 -0
  86. reticulum_telemetry_hub/reticulum_server/constants.py +1 -0
  87. reticulum_telemetry_hub/reticulum_server/event_log.py +357 -0
  88. reticulum_telemetry_hub/reticulum_server/internal_adapter.py +358 -0
  89. reticulum_telemetry_hub/reticulum_server/outbound_queue.py +312 -0
  90. reticulum_telemetry_hub/reticulum_server/services.py +422 -0
  91. reticulumtelemetryhub-0.143.0.dist-info/METADATA +181 -0
  92. reticulumtelemetryhub-0.143.0.dist-info/RECORD +97 -0
  93. {reticulumtelemetryhub-0.1.0.dist-info → reticulumtelemetryhub-0.143.0.dist-info}/WHEEL +1 -1
  94. reticulumtelemetryhub-0.143.0.dist-info/licenses/LICENSE +277 -0
  95. lxmf_telemetry/model/fields/field_telemetry_stream.py +0 -7
  96. lxmf_telemetry/model/persistance/__init__.py +0 -3
  97. lxmf_telemetry/model/persistance/sensors/location.py +0 -69
  98. lxmf_telemetry/model/persistance/sensors/magnetic_field.py +0 -36
  99. lxmf_telemetry/model/persistance/sensors/sensor.py +0 -44
  100. lxmf_telemetry/model/persistance/sensors/sensor_enum.py +0 -24
  101. lxmf_telemetry/model/persistance/sensors/sensor_mapping.py +0 -9
  102. lxmf_telemetry/telemetry_controller.py +0 -124
  103. reticulum_server/main.py +0 -182
  104. reticulumtelemetryhub-0.1.0.dist-info/METADATA +0 -15
  105. reticulumtelemetryhub-0.1.0.dist-info/RECORD +0 -19
  106. {lxmf_telemetry → reticulum_telemetry_hub}/__init__.py +0 -0
  107. {lxmf_telemetry/model/persistance/sensors → reticulum_telemetry_hub/lxmf_telemetry}/__init__.py +0 -0
  108. {reticulum_server → reticulum_telemetry_hub/reticulum_server}/__init__.py +0 -0
@@ -0,0 +1,490 @@
1
+ import RNS
2
+ import RNS.vendor.umsgpack as msgpack
3
+
4
+ import os
5
+ import time
6
+ import math
7
+ import itertools
8
+ import multiprocessing
9
+
10
+ WORKBLOCK_EXPAND_ROUNDS = 3000
11
+ WORKBLOCK_EXPAND_ROUNDS_PN = 1000
12
+ WORKBLOCK_EXPAND_ROUNDS_PEERING = 25
13
+ STAMP_SIZE = RNS.Identity.HASHLENGTH // 8
14
+ PN_VALIDATION_POOL_MIN_SIZE = 256
15
+
16
+ active_jobs = {}
17
+
18
+ if RNS.vendor.platformutils.is_linux():
19
+ try:
20
+ multiprocessing.set_start_method("fork")
21
+ except RuntimeError:
22
+ pass
23
+
24
+
25
+ def stamp_workblock(material, expand_rounds=WORKBLOCK_EXPAND_ROUNDS):
26
+ wb_st = time.time()
27
+ workblock = b""
28
+ for n in range(expand_rounds):
29
+ workblock += RNS.Cryptography.hkdf(
30
+ length=256,
31
+ derive_from=material,
32
+ salt=RNS.Identity.full_hash(material + msgpack.packb(n)),
33
+ context=None,
34
+ )
35
+ wb_time = time.time() - wb_st
36
+ # RNS.log(f"Stamp workblock size {RNS.prettysize(len(workblock))}, generated in {round(wb_time*1000,2)}ms", RNS.LOG_DEBUG)
37
+
38
+ return workblock
39
+
40
+
41
+ def stamp_value(workblock, stamp):
42
+ value = 0
43
+ bits = 256
44
+ material = RNS.Identity.full_hash(workblock + stamp)
45
+ i = int.from_bytes(material, byteorder="big")
46
+ while (i & (1 << (bits - 1))) == 0:
47
+ i = i << 1
48
+ value += 1
49
+
50
+ return value
51
+
52
+
53
+ def stamp_valid(stamp, target_cost, workblock):
54
+ target = 0b1 << 256 - target_cost
55
+ result = RNS.Identity.full_hash(workblock + stamp)
56
+ if int.from_bytes(result, byteorder="big") > target:
57
+ return False
58
+ else:
59
+ return True
60
+
61
+
62
+ def validate_peering_key(peering_id, peering_key, target_cost):
63
+ workblock = stamp_workblock(
64
+ peering_id, expand_rounds=WORKBLOCK_EXPAND_ROUNDS_PEERING
65
+ )
66
+ if not stamp_valid(peering_key, target_cost, workblock):
67
+ return False
68
+ else:
69
+ return True
70
+
71
+
72
+ def validate_pn_stamp(transient_data, target_cost):
73
+ from .LXMessage import LXMessage
74
+
75
+ if len(transient_data) <= LXMessage.LXMF_OVERHEAD + STAMP_SIZE:
76
+ return None, None, None, None
77
+ else:
78
+ lxm_data = transient_data[:-STAMP_SIZE]
79
+ stamp = transient_data[-STAMP_SIZE:]
80
+ transient_id = RNS.Identity.full_hash(lxm_data)
81
+ workblock = stamp_workblock(
82
+ transient_id, expand_rounds=WORKBLOCK_EXPAND_ROUNDS_PN
83
+ )
84
+
85
+ if not stamp_valid(stamp, target_cost, workblock):
86
+ return None, None, None, None
87
+ else:
88
+ value = stamp_value(workblock, stamp)
89
+ return transient_id, lxm_data, value, stamp
90
+
91
+
92
+ def validate_pn_stamps_job_simple(transient_list, target_cost):
93
+ validated_messages = []
94
+ for transient_data in transient_list:
95
+ transient_id, lxm_data, value, stamp_data = validate_pn_stamp(
96
+ transient_data, target_cost
97
+ )
98
+ if transient_id:
99
+ validated_messages.append([transient_id, lxm_data, value, stamp_data])
100
+
101
+ return validated_messages
102
+
103
+
104
+ def validate_pn_stamps_job_multip(transient_list, target_cost):
105
+ cores = multiprocessing.cpu_count()
106
+ pool_count = min(
107
+ cores, math.ceil(len(transient_list) / PN_VALIDATION_POOL_MIN_SIZE)
108
+ )
109
+
110
+ RNS.log(
111
+ f"Validating {len(transient_list)} stamps using {pool_count} processes...",
112
+ RNS.LOG_VERBOSE,
113
+ )
114
+ with multiprocessing.Pool(pool_count) as p:
115
+ validated_entries = p.starmap(
116
+ validate_pn_stamp, zip(transient_list, itertools.repeat(target_cost))
117
+ )
118
+
119
+ return [e for e in validated_entries if e[0] != None]
120
+
121
+
122
+ def validate_pn_stamps(transient_list, target_cost):
123
+ non_mp_platform = RNS.vendor.platformutils.is_android()
124
+ if len(transient_list) <= PN_VALIDATION_POOL_MIN_SIZE or non_mp_platform:
125
+ return validate_pn_stamps_job_simple(transient_list, target_cost)
126
+ else:
127
+ return validate_pn_stamps_job_multip(transient_list, target_cost)
128
+
129
+
130
+ def generate_stamp(message_id, stamp_cost, expand_rounds=WORKBLOCK_EXPAND_ROUNDS):
131
+ RNS.log(
132
+ f"Generating stamp with cost {stamp_cost} for {RNS.prettyhexrep(message_id)}...",
133
+ RNS.LOG_DEBUG,
134
+ )
135
+ workblock = stamp_workblock(message_id, expand_rounds=expand_rounds)
136
+
137
+ start_time = time.time()
138
+ stamp = None
139
+ rounds = 0
140
+ value = 0
141
+
142
+ if RNS.vendor.platformutils.is_windows() or RNS.vendor.platformutils.is_darwin():
143
+ stamp, rounds = job_simple(stamp_cost, workblock, message_id)
144
+ elif RNS.vendor.platformutils.is_android():
145
+ stamp, rounds = job_android(stamp_cost, workblock, message_id)
146
+ else:
147
+ stamp, rounds = job_linux(stamp_cost, workblock, message_id)
148
+
149
+ duration = time.time() - start_time
150
+ speed = rounds / duration
151
+ if stamp != None:
152
+ value = stamp_value(workblock, stamp)
153
+
154
+ RNS.log(
155
+ f"Stamp with value {value} generated in {RNS.prettytime(duration)}, {rounds} rounds, {int(speed)} rounds per second",
156
+ RNS.LOG_DEBUG,
157
+ )
158
+
159
+ return stamp, value
160
+
161
+
162
+ def cancel_work(message_id):
163
+ if RNS.vendor.platformutils.is_windows() or RNS.vendor.platformutils.is_darwin():
164
+ try:
165
+ if message_id in active_jobs:
166
+ active_jobs[message_id] = True
167
+
168
+ except Exception as e:
169
+ RNS.log(
170
+ "Error while terminating stamp generation workers: {e}", RNS.LOG_ERROR
171
+ )
172
+ RNS.trace_exception(e)
173
+
174
+ elif RNS.vendor.platformutils.is_android():
175
+ try:
176
+ if message_id in active_jobs:
177
+ active_jobs[message_id] = True
178
+
179
+ except Exception as e:
180
+ RNS.log(
181
+ "Error while terminating stamp generation workers: {e}", RNS.LOG_ERROR
182
+ )
183
+ RNS.trace_exception(e)
184
+
185
+ else:
186
+ try:
187
+ if message_id in active_jobs:
188
+ stop_event = active_jobs[message_id][0]
189
+ result_queue = active_jobs[message_id][1]
190
+ stop_event.set()
191
+ result_queue.put(None)
192
+ active_jobs.pop(message_id)
193
+
194
+ except Exception as e:
195
+ RNS.log(
196
+ "Error while terminating stamp generation workers: {e}", RNS.LOG_ERROR
197
+ )
198
+ RNS.trace_exception(e)
199
+
200
+
201
+ def job_simple(stamp_cost, workblock, message_id):
202
+ # A simple, single-process stamp generator.
203
+ # should work on any platform, and is used
204
+ # as a fall-back, in case of limited multi-
205
+ # processing and/or acceleration support.
206
+
207
+ platform = RNS.vendor.platformutils.get_platform()
208
+ RNS.log(
209
+ f"Running stamp generation on {platform}, work limited to single CPU core. This will be slower than ideal.",
210
+ RNS.LOG_WARNING,
211
+ )
212
+
213
+ rounds = 0
214
+ pstamp = os.urandom(256 // 8)
215
+ st = time.time()
216
+
217
+ active_jobs[message_id] = False
218
+
219
+ def sv(s, c, w):
220
+ target = 0b1 << 256 - c
221
+ m = w + s
222
+ result = RNS.Identity.full_hash(m)
223
+ if int.from_bytes(result, byteorder="big") > target:
224
+ return False
225
+ else:
226
+ return True
227
+
228
+ while not sv(pstamp, stamp_cost, workblock) and not active_jobs[message_id]:
229
+ pstamp = os.urandom(256 // 8)
230
+ rounds += 1
231
+ if rounds % 2500 == 0:
232
+ speed = rounds / (time.time() - st)
233
+ RNS.log(
234
+ f"Stamp generation running. {rounds} rounds completed so far, {int(speed)} rounds per second",
235
+ RNS.LOG_DEBUG,
236
+ )
237
+
238
+ if active_jobs[message_id] == True:
239
+ pstamp = None
240
+
241
+ active_jobs.pop(message_id)
242
+
243
+ return pstamp, rounds
244
+
245
+
246
+ def job_linux(stamp_cost, workblock, message_id):
247
+ allow_kill = True
248
+ stamp = None
249
+ total_rounds = 0
250
+ cores = multiprocessing.cpu_count()
251
+ jobs = cores if cores <= 12 else int(cores / 2)
252
+ stop_event = multiprocessing.Event()
253
+ result_queue = multiprocessing.Queue(1)
254
+ rounds_queue = multiprocessing.Queue()
255
+
256
+ def job(stop_event, pn, sc, wb):
257
+ terminated = False
258
+ rounds = 0
259
+ pstamp = os.urandom(256 // 8)
260
+
261
+ def sv(s, c, w):
262
+ target = 0b1 << 256 - c
263
+ m = w + s
264
+ result = RNS.Identity.full_hash(m)
265
+ if int.from_bytes(result, byteorder="big") > target:
266
+ return False
267
+ else:
268
+ return True
269
+
270
+ while not stop_event.is_set() and not sv(pstamp, sc, wb):
271
+ pstamp = os.urandom(256 // 8)
272
+ rounds += 1
273
+
274
+ if not stop_event.is_set():
275
+ stop_event.set()
276
+ result_queue.put(pstamp)
277
+ rounds_queue.put(rounds)
278
+
279
+ job_procs = []
280
+ RNS.log(f"Starting {jobs} stamp generation workers", RNS.LOG_DEBUG)
281
+ for jpn in range(jobs):
282
+ process = multiprocessing.Process(
283
+ target=job,
284
+ kwargs={
285
+ "stop_event": stop_event,
286
+ "pn": jpn,
287
+ "sc": stamp_cost,
288
+ "wb": workblock,
289
+ },
290
+ daemon=True,
291
+ )
292
+ job_procs.append(process)
293
+ process.start()
294
+
295
+ active_jobs[message_id] = [stop_event, result_queue]
296
+
297
+ stamp = result_queue.get()
298
+ RNS.log("Got stamp result from worker", RNS.LOG_DEBUG) # TODO: Remove
299
+
300
+ # Collect any potential spurious
301
+ # results from worker queue.
302
+ try:
303
+ while True:
304
+ result_queue.get_nowait()
305
+ except:
306
+ pass
307
+
308
+ for j in range(jobs):
309
+ nrounds = 0
310
+ try:
311
+ nrounds = rounds_queue.get(timeout=2)
312
+ except Exception as e:
313
+ RNS.log(f"Failed to get round stats part {j}: {e}", RNS.LOG_ERROR)
314
+ total_rounds += nrounds
315
+
316
+ all_exited = False
317
+ exit_timeout = time.time() + 5
318
+ while time.time() < exit_timeout:
319
+ if not any(p.is_alive() for p in job_procs):
320
+ all_exited = True
321
+ break
322
+ time.sleep(0.1)
323
+
324
+ if not all_exited:
325
+ RNS.log(
326
+ "Stamp generation IPC timeout, possible worker deadlock. Terminating remaining processes.",
327
+ RNS.LOG_ERROR,
328
+ )
329
+ if allow_kill:
330
+ for j in range(jobs):
331
+ process = job_procs[j]
332
+ process.kill()
333
+ else:
334
+ return None
335
+
336
+ else:
337
+ for j in range(jobs):
338
+ process = job_procs[j]
339
+ process.join()
340
+ # RNS.log(f"Joined {j} / {process}", RNS.LOG_DEBUG) # TODO: Remove
341
+
342
+ return stamp, total_rounds
343
+
344
+
345
+ def job_android(stamp_cost, workblock, message_id):
346
+ # Semaphore support is flaky to non-existent on
347
+ # Android, so we need to manually dispatch and
348
+ # manage workloads here, while periodically
349
+ # checking in on the progress.
350
+
351
+ stamp = None
352
+ start_time = time.time()
353
+ total_rounds = 0
354
+ rounds_per_worker = 1000
355
+
356
+ use_nacl = False
357
+ try:
358
+ import nacl.encoding
359
+ import nacl.hash
360
+
361
+ use_nacl = True
362
+ except:
363
+ pass
364
+
365
+ if use_nacl:
366
+
367
+ def full_hash(m):
368
+ return nacl.hash.sha256(m, encoder=nacl.encoding.RawEncoder)
369
+
370
+ else:
371
+
372
+ def full_hash(m):
373
+ return RNS.Identity.full_hash(m)
374
+
375
+ def sv(s, c, w):
376
+ target = 0b1 << 256 - c
377
+ m = w + s
378
+ result = full_hash(m)
379
+ if int.from_bytes(result, byteorder="big") > target:
380
+ return False
381
+ else:
382
+ return True
383
+
384
+ wm = multiprocessing.Manager()
385
+ jobs = multiprocessing.cpu_count()
386
+
387
+ def job(procnum=None, results_dict=None, wb=None, sc=None, jr=None):
388
+ # RNS.log(f"Worker {procnum} starting for {jr} rounds...") # TODO: Remove
389
+ try:
390
+ rounds = 0
391
+ found_stamp = None
392
+
393
+ while True:
394
+ pstamp = os.urandom(256 // 8)
395
+ rounds += 1
396
+ if sv(pstamp, sc, wb):
397
+ found_stamp = pstamp
398
+ break
399
+
400
+ if rounds >= jr:
401
+ # RNS.log(f"Worker {procnum} found no result in {rounds} rounds") # TODO: Remove
402
+ break
403
+
404
+ results_dict[procnum] = [found_stamp, rounds]
405
+ except Exception as e:
406
+ RNS.log(f"Stamp generation worker error: {e}", RNS.LOG_ERROR)
407
+ RNS.trace_exception(e)
408
+
409
+ active_jobs[message_id] = False
410
+
411
+ RNS.log(
412
+ f"Dispatching {jobs} workers for stamp generation...", RNS.LOG_DEBUG
413
+ ) # TODO: Remove
414
+
415
+ results_dict = wm.dict()
416
+ while stamp == None and active_jobs[message_id] == False:
417
+ job_procs = []
418
+ try:
419
+ for pnum in range(jobs):
420
+ pargs = {
421
+ "procnum": pnum,
422
+ "results_dict": results_dict,
423
+ "wb": workblock,
424
+ "sc": stamp_cost,
425
+ "jr": rounds_per_worker,
426
+ }
427
+ process = multiprocessing.Process(target=job, kwargs=pargs)
428
+ job_procs.append(process)
429
+ process.start()
430
+
431
+ for process in job_procs:
432
+ process.join()
433
+
434
+ for j in results_dict:
435
+ r = results_dict[j]
436
+ total_rounds += r[1]
437
+ if r[0] != None:
438
+ stamp = r[0]
439
+
440
+ if stamp == None:
441
+ elapsed = time.time() - start_time
442
+ speed = total_rounds / elapsed
443
+ RNS.log(
444
+ f"Stamp generation running. {total_rounds} rounds completed so far, {int(speed)} rounds per second",
445
+ RNS.LOG_DEBUG,
446
+ )
447
+
448
+ except Exception as e:
449
+ RNS.log(f"Stamp generation job error: {e}")
450
+ RNS.trace_exception(e)
451
+
452
+ active_jobs.pop(message_id)
453
+
454
+ return stamp, total_rounds
455
+
456
+
457
+ # def stamp_value_linear(workblock, stamp):
458
+ # value = 0
459
+ # bits = 256
460
+ # material = RNS.Identity.full_hash(workblock+stamp)
461
+ # s = int.from_bytes(material, byteorder="big")
462
+ # return s.bit_count()
463
+
464
+ if __name__ == "__main__":
465
+ import sys
466
+
467
+ if len(sys.argv) < 2:
468
+ RNS.log("No cost argument provided", RNS.LOG_ERROR)
469
+ exit(1)
470
+ else:
471
+ try:
472
+ cost = int(sys.argv[1])
473
+ except Exception as e:
474
+ RNS.log(f"Invalid cost argument provided: {e}", RNS.LOG_ERROR)
475
+ exit(1)
476
+
477
+ RNS.loglevel = RNS.LOG_DEBUG
478
+ RNS.log("Testing LXMF stamp generation", RNS.LOG_DEBUG)
479
+ message_id = os.urandom(32)
480
+ generate_stamp(message_id, cost)
481
+
482
+ RNS.log("", RNS.LOG_DEBUG)
483
+ RNS.log("Testing propagation stamp generation", RNS.LOG_DEBUG)
484
+ message_id = os.urandom(32)
485
+ generate_stamp(message_id, cost, expand_rounds=WORKBLOCK_EXPAND_ROUNDS_PN)
486
+
487
+ RNS.log("", RNS.LOG_DEBUG)
488
+ RNS.log("Testing peering key generation", RNS.LOG_DEBUG)
489
+ message_id = os.urandom(32)
490
+ generate_stamp(message_id, cost, expand_rounds=WORKBLOCK_EXPAND_ROUNDS_PEERING)
@@ -0,0 +1,10 @@
1
+ import os
2
+ import glob
3
+
4
+ from .LXMessage import LXMessage
5
+ from .LXMRouter import LXMRouter
6
+ from .LXMF import * # noqa: F401,F403
7
+ from ._version import __version__
8
+
9
+ modules = glob.glob(os.path.join(os.path.dirname(__file__), "*.py"))
10
+ __all__ = [os.path.basename(f)[:-3] for f in modules if not f.endswith("__init__.py")]
@@ -0,0 +1 @@
1
+ __version__ = "0.9.2"