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.
- reticulum_telemetry_hub/api/__init__.py +23 -0
- reticulum_telemetry_hub/api/models.py +323 -0
- reticulum_telemetry_hub/api/service.py +836 -0
- reticulum_telemetry_hub/api/storage.py +528 -0
- reticulum_telemetry_hub/api/storage_base.py +156 -0
- reticulum_telemetry_hub/api/storage_models.py +118 -0
- reticulum_telemetry_hub/atak_cot/__init__.py +49 -0
- reticulum_telemetry_hub/atak_cot/base.py +277 -0
- reticulum_telemetry_hub/atak_cot/chat.py +506 -0
- reticulum_telemetry_hub/atak_cot/detail.py +235 -0
- reticulum_telemetry_hub/atak_cot/event.py +181 -0
- reticulum_telemetry_hub/atak_cot/pytak_client.py +569 -0
- reticulum_telemetry_hub/atak_cot/tak_connector.py +848 -0
- reticulum_telemetry_hub/config/__init__.py +25 -0
- reticulum_telemetry_hub/config/constants.py +7 -0
- reticulum_telemetry_hub/config/manager.py +515 -0
- reticulum_telemetry_hub/config/models.py +215 -0
- reticulum_telemetry_hub/embedded_lxmd/__init__.py +5 -0
- reticulum_telemetry_hub/embedded_lxmd/embedded.py +418 -0
- reticulum_telemetry_hub/internal_api/__init__.py +21 -0
- reticulum_telemetry_hub/internal_api/bus.py +344 -0
- reticulum_telemetry_hub/internal_api/core.py +690 -0
- reticulum_telemetry_hub/internal_api/v1/__init__.py +74 -0
- reticulum_telemetry_hub/internal_api/v1/enums.py +109 -0
- reticulum_telemetry_hub/internal_api/v1/manifest.json +8 -0
- reticulum_telemetry_hub/internal_api/v1/schemas.py +478 -0
- reticulum_telemetry_hub/internal_api/versioning.py +63 -0
- reticulum_telemetry_hub/lxmf_daemon/Handlers.py +122 -0
- reticulum_telemetry_hub/lxmf_daemon/LXMF.py +252 -0
- reticulum_telemetry_hub/lxmf_daemon/LXMPeer.py +898 -0
- reticulum_telemetry_hub/lxmf_daemon/LXMRouter.py +4227 -0
- reticulum_telemetry_hub/lxmf_daemon/LXMessage.py +1006 -0
- reticulum_telemetry_hub/lxmf_daemon/LXStamper.py +490 -0
- reticulum_telemetry_hub/lxmf_daemon/__init__.py +10 -0
- reticulum_telemetry_hub/lxmf_daemon/_version.py +1 -0
- reticulum_telemetry_hub/lxmf_daemon/lxmd.py +1655 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/fields/field_telemetry_stream.py +6 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/__init__.py +3 -0
- {lxmf_telemetry → reticulum_telemetry_hub/lxmf_telemetry}/model/persistance/appearance.py +19 -19
- {lxmf_telemetry → reticulum_telemetry_hub/lxmf_telemetry}/model/persistance/peer.py +17 -13
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/__init__.py +65 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/acceleration.py +68 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/ambient_light.py +37 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/angular_velocity.py +68 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/battery.py +68 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/connection_map.py +258 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/generic.py +841 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/gravity.py +68 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/humidity.py +37 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/information.py +42 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/location.py +110 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/lxmf_propagation.py +429 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/magnetic_field.py +68 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/physical_link.py +53 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/pressure.py +37 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/proximity.py +37 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/received.py +75 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/rns_transport.py +209 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/sensor.py +65 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/sensor_enum.py +27 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/sensor_mapping.py +58 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/temperature.py +37 -0
- {lxmf_telemetry → reticulum_telemetry_hub/lxmf_telemetry}/model/persistance/sensors/time.py +36 -32
- {lxmf_telemetry → reticulum_telemetry_hub/lxmf_telemetry}/model/persistance/telemeter.py +26 -23
- reticulum_telemetry_hub/lxmf_telemetry/sampler.py +229 -0
- reticulum_telemetry_hub/lxmf_telemetry/telemeter_manager.py +409 -0
- reticulum_telemetry_hub/lxmf_telemetry/telemetry_controller.py +804 -0
- reticulum_telemetry_hub/northbound/__init__.py +5 -0
- reticulum_telemetry_hub/northbound/app.py +195 -0
- reticulum_telemetry_hub/northbound/auth.py +119 -0
- reticulum_telemetry_hub/northbound/gateway.py +310 -0
- reticulum_telemetry_hub/northbound/internal_adapter.py +302 -0
- reticulum_telemetry_hub/northbound/models.py +213 -0
- reticulum_telemetry_hub/northbound/routes_chat.py +123 -0
- reticulum_telemetry_hub/northbound/routes_files.py +119 -0
- reticulum_telemetry_hub/northbound/routes_rest.py +345 -0
- reticulum_telemetry_hub/northbound/routes_subscribers.py +150 -0
- reticulum_telemetry_hub/northbound/routes_topics.py +178 -0
- reticulum_telemetry_hub/northbound/routes_ws.py +107 -0
- reticulum_telemetry_hub/northbound/serializers.py +72 -0
- reticulum_telemetry_hub/northbound/services.py +373 -0
- reticulum_telemetry_hub/northbound/websocket.py +855 -0
- reticulum_telemetry_hub/reticulum_server/__main__.py +2237 -0
- reticulum_telemetry_hub/reticulum_server/command_manager.py +1268 -0
- reticulum_telemetry_hub/reticulum_server/command_text.py +399 -0
- reticulum_telemetry_hub/reticulum_server/constants.py +1 -0
- reticulum_telemetry_hub/reticulum_server/event_log.py +357 -0
- reticulum_telemetry_hub/reticulum_server/internal_adapter.py +358 -0
- reticulum_telemetry_hub/reticulum_server/outbound_queue.py +312 -0
- reticulum_telemetry_hub/reticulum_server/services.py +422 -0
- reticulumtelemetryhub-0.143.0.dist-info/METADATA +181 -0
- reticulumtelemetryhub-0.143.0.dist-info/RECORD +97 -0
- {reticulumtelemetryhub-0.1.0.dist-info → reticulumtelemetryhub-0.143.0.dist-info}/WHEEL +1 -1
- reticulumtelemetryhub-0.143.0.dist-info/licenses/LICENSE +277 -0
- lxmf_telemetry/model/fields/field_telemetry_stream.py +0 -7
- lxmf_telemetry/model/persistance/__init__.py +0 -3
- lxmf_telemetry/model/persistance/sensors/location.py +0 -69
- lxmf_telemetry/model/persistance/sensors/magnetic_field.py +0 -36
- lxmf_telemetry/model/persistance/sensors/sensor.py +0 -44
- lxmf_telemetry/model/persistance/sensors/sensor_enum.py +0 -24
- lxmf_telemetry/model/persistance/sensors/sensor_mapping.py +0 -9
- lxmf_telemetry/telemetry_controller.py +0 -124
- reticulum_server/main.py +0 -182
- reticulumtelemetryhub-0.1.0.dist-info/METADATA +0 -15
- reticulumtelemetryhub-0.1.0.dist-info/RECORD +0 -19
- {lxmf_telemetry → reticulum_telemetry_hub}/__init__.py +0 -0
- {lxmf_telemetry/model/persistance/sensors → reticulum_telemetry_hub/lxmf_telemetry}/__init__.py +0 -0
- {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"
|