neuronum 7.0.4__py3-none-any.whl → 8.1.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.
Potentially problematic release.
This version of neuronum might be problematic. Click here for more details.
- cli/main.py +154 -719
- neuronum/__init__.py +1 -2
- neuronum/neuronum.py +268 -447
- neuronum-8.1.0.dist-info/METADATA +124 -0
- neuronum-8.1.0.dist-info/RECORD +10 -0
- neuronum-7.0.4.dist-info/METADATA +0 -171
- neuronum-7.0.4.dist-info/RECORD +0 -10
- {neuronum-7.0.4.dist-info → neuronum-8.1.0.dist-info}/WHEEL +0 -0
- {neuronum-7.0.4.dist-info → neuronum-8.1.0.dist-info}/entry_points.txt +0 -0
- {neuronum-7.0.4.dist-info → neuronum-8.1.0.dist-info}/licenses/LICENSE.md +0 -0
- {neuronum-7.0.4.dist-info → neuronum-8.1.0.dist-info}/top_level.txt +0 -0
neuronum/neuronum.py
CHANGED
|
@@ -1,17 +1,31 @@
|
|
|
1
1
|
import aiohttp
|
|
2
|
-
from typing import
|
|
3
|
-
import ssl
|
|
2
|
+
from typing import AsyncGenerator
|
|
4
3
|
import websockets
|
|
5
4
|
import json
|
|
6
5
|
import asyncio
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
import base64
|
|
7
|
+
import os
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from websockets.exceptions import ConnectionClosed
|
|
10
|
+
from cryptography.hazmat.primitives.asymmetric import ec
|
|
11
|
+
from cryptography.hazmat.primitives import serialization, hashes
|
|
12
|
+
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
13
|
+
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
|
|
14
|
+
from cryptography.hazmat.backends import default_backend
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Node:
|
|
18
|
+
def __init__(self, id: str, private_key: str, public_key: str):
|
|
19
|
+
self.node_id = id
|
|
20
|
+
self.private_key_path = private_key
|
|
21
|
+
self.public_key_path = public_key
|
|
14
22
|
self.queue = asyncio.Queue()
|
|
23
|
+
self.host = self._load_host()
|
|
24
|
+
self.network = self._load_network()
|
|
25
|
+
self.synapse = self._load_synapse()
|
|
26
|
+
self.password = self._load_password()
|
|
27
|
+
self._private_key = self._load_private_key()
|
|
28
|
+
self._public_key = self._load_public_key()
|
|
15
29
|
|
|
16
30
|
|
|
17
31
|
def to_dict(self) -> dict:
|
|
@@ -21,186 +35,298 @@ class Cell:
|
|
|
21
35
|
"synapse": self.synapse
|
|
22
36
|
}
|
|
23
37
|
|
|
38
|
+
def _load_private_key(self):
|
|
39
|
+
try:
|
|
40
|
+
with open(self.private_key_path, "rb") as f:
|
|
41
|
+
private_key = serialization.load_pem_private_key(
|
|
42
|
+
f.read(),
|
|
43
|
+
password=None,
|
|
44
|
+
backend=default_backend()
|
|
45
|
+
)
|
|
46
|
+
print("Private key loaded successfully.")
|
|
47
|
+
return private_key
|
|
48
|
+
except FileNotFoundError:
|
|
49
|
+
print(f"Private key file not found at {self.private_key_path}.")
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
def _load_host(self):
|
|
53
|
+
credentials_folder_path = Path.home() / ".neuronum"
|
|
54
|
+
env_path = credentials_folder_path / ".env"
|
|
55
|
+
|
|
56
|
+
env_data = {}
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
with open(env_path, "r") as f:
|
|
60
|
+
for line in f:
|
|
61
|
+
key, value = line.strip().split("=")
|
|
62
|
+
env_data[key] = value
|
|
63
|
+
|
|
64
|
+
host = env_data.get("HOST", "")
|
|
65
|
+
return host
|
|
66
|
+
except FileNotFoundError:
|
|
67
|
+
print(f"Cell Host not found")
|
|
68
|
+
return None
|
|
69
|
+
|
|
24
70
|
|
|
25
|
-
def
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
async def stream(self, label: str, data: dict, stx: Optional[str] = None, retry_delay: int = 3):
|
|
30
|
-
context = ssl.create_default_context()
|
|
31
|
-
context.check_hostname = True
|
|
32
|
-
context.verify_mode = ssl.CERT_REQUIRED
|
|
33
|
-
|
|
34
|
-
while True:
|
|
35
|
-
try:
|
|
36
|
-
reader, writer = await asyncio.open_connection(self.network, 55555, ssl=context, server_hostname=self.network)
|
|
37
|
-
|
|
38
|
-
credentials = f"{self.host}\n{self.password}\n{self.synapse}\n{stx}\n"
|
|
39
|
-
writer.write(credentials.encode("utf-8"))
|
|
40
|
-
await writer.drain()
|
|
41
|
-
|
|
42
|
-
response = await reader.read(1024)
|
|
43
|
-
response_text = response.decode("utf-8").strip()
|
|
44
|
-
|
|
45
|
-
if "Authentication successful" not in response_text:
|
|
46
|
-
print("Authentication failed, retrying...")
|
|
47
|
-
writer.close()
|
|
48
|
-
await writer.wait_closed()
|
|
49
|
-
await asyncio.sleep(retry_delay)
|
|
50
|
-
continue
|
|
51
|
-
|
|
52
|
-
stream_payload = {
|
|
53
|
-
"label": label,
|
|
54
|
-
"data": data,
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
writer.write(json.dumps(stream_payload).encode("utf-8"))
|
|
58
|
-
await writer.drain()
|
|
59
|
-
|
|
60
|
-
response = await reader.read(1024)
|
|
61
|
-
response_text = response.decode("utf-8").strip()
|
|
62
|
-
|
|
63
|
-
if response_text == "Sent":
|
|
64
|
-
print(f"Success: {response_text} - {stream_payload}")
|
|
65
|
-
break
|
|
66
|
-
else:
|
|
67
|
-
print(f"Error sending: {stream_payload}")
|
|
68
|
-
|
|
69
|
-
except (ssl.SSLError, ConnectionError) as e:
|
|
70
|
-
print(f"Connection error: {e}, retrying...")
|
|
71
|
-
await asyncio.sleep(retry_delay)
|
|
72
|
-
|
|
73
|
-
except Exception as e:
|
|
74
|
-
print(f"Unexpected error: {e}, retrying...")
|
|
75
|
-
await asyncio.sleep(retry_delay)
|
|
71
|
+
def _load_password(self):
|
|
72
|
+
credentials_folder_path = Path.home() / ".neuronum"
|
|
73
|
+
env_path = credentials_folder_path / ".env"
|
|
76
74
|
|
|
77
|
-
|
|
78
|
-
if 'writer' in locals():
|
|
79
|
-
writer.close()
|
|
80
|
-
await writer.wait_closed()
|
|
75
|
+
env_data = {}
|
|
81
76
|
|
|
77
|
+
try:
|
|
78
|
+
with open(env_path, "r") as f:
|
|
79
|
+
for line in f:
|
|
80
|
+
key, value = line.strip().split("=")
|
|
81
|
+
env_data[key] = value
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
host = env_data.get("PASSWORD", "")
|
|
84
|
+
return host
|
|
85
|
+
except FileNotFoundError:
|
|
86
|
+
print(f"Cell Password not found")
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
def _load_synapse(self):
|
|
90
|
+
credentials_folder_path = Path.home() / ".neuronum"
|
|
91
|
+
env_path = credentials_folder_path / ".env"
|
|
92
|
+
|
|
93
|
+
env_data = {}
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
with open(env_path, "r") as f:
|
|
97
|
+
for line in f:
|
|
98
|
+
key, value = line.strip().split("=")
|
|
99
|
+
env_data[key] = value
|
|
100
|
+
|
|
101
|
+
host = env_data.get("SYNAPSE", "")
|
|
102
|
+
return host
|
|
103
|
+
except FileNotFoundError:
|
|
104
|
+
print(f"Cell Synapse not found")
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
def _load_network(self):
|
|
108
|
+
credentials_folder_path = Path.home() / ".neuronum"
|
|
109
|
+
env_path = credentials_folder_path / ".env"
|
|
110
|
+
|
|
111
|
+
env_data = {}
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
with open(env_path, "r") as f:
|
|
115
|
+
for line in f:
|
|
116
|
+
key, value = line.strip().split("=")
|
|
117
|
+
env_data[key] = value
|
|
118
|
+
|
|
119
|
+
host = env_data.get("NETWORK", "")
|
|
120
|
+
return host
|
|
121
|
+
except FileNotFoundError:
|
|
122
|
+
print(f"Cell Network not found")
|
|
123
|
+
return None
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _load_public_key(self):
|
|
127
|
+
try:
|
|
128
|
+
with open(self.public_key_path, "rb") as f:
|
|
129
|
+
public_key = serialization.load_pem_public_key(
|
|
130
|
+
f.read(),
|
|
131
|
+
backend=default_backend()
|
|
132
|
+
)
|
|
133
|
+
print("Public key loaded successfully.")
|
|
134
|
+
print(public_key)
|
|
135
|
+
return public_key
|
|
136
|
+
except FileNotFoundError:
|
|
137
|
+
print(f"Public key file not found at {self.public_key_path}. Deriving from private key.")
|
|
138
|
+
if self._private_key:
|
|
139
|
+
return self._private_key.public_key()
|
|
140
|
+
else:
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def get_public_key_jwk(self):
|
|
145
|
+
public_key = self._load_public_key()
|
|
146
|
+
if not public_key:
|
|
147
|
+
print("Public key not loaded. Cannot generate JWK.")
|
|
148
|
+
return None
|
|
149
|
+
|
|
150
|
+
print("Public key loaded successfully.")
|
|
151
|
+
public_numbers = public_key.public_numbers()
|
|
152
|
+
|
|
153
|
+
x_bytes = public_numbers.x.to_bytes((public_numbers.x.bit_length() + 7) // 8, 'big')
|
|
154
|
+
y_bytes = public_numbers.y.to_bytes((public_numbers.y.bit_length() + 7) // 8, 'big')
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
"kty": "EC",
|
|
158
|
+
"crv": "P-256",
|
|
159
|
+
"x": base64.urlsafe_b64encode(x_bytes).rstrip(b'=').decode('utf-8'),
|
|
160
|
+
"y": base64.urlsafe_b64encode(y_bytes).rstrip(b'=').decode('utf-8')
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _decrypt_with_ecdh_aesgcm(self, ephemeral_public_key_bytes, nonce, ciphertext):
|
|
165
|
+
try:
|
|
166
|
+
ephemeral_public_key = ec.EllipticCurvePublicKey.from_encoded_point(
|
|
167
|
+
ec.SECP256R1(), ephemeral_public_key_bytes
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
shared_secret = self._private_key.exchange(ec.ECDH(), ephemeral_public_key)
|
|
171
|
+
|
|
172
|
+
derived_key = HKDF(
|
|
173
|
+
algorithm=hashes.SHA256(),
|
|
174
|
+
length=32,
|
|
175
|
+
salt=None,
|
|
176
|
+
info=b'handshake data'
|
|
177
|
+
).derive(shared_secret)
|
|
178
|
+
|
|
179
|
+
aesgcm = AESGCM(derived_key)
|
|
180
|
+
plaintext_bytes = aesgcm.decrypt(nonce, ciphertext, None)
|
|
181
|
+
return json.loads(plaintext_bytes.decode())
|
|
182
|
+
|
|
183
|
+
except Exception as e:
|
|
184
|
+
print(f"Decryption failed: {e}")
|
|
185
|
+
return None
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
async def sync(self) -> AsyncGenerator[str, None]:
|
|
189
|
+
full_url = f"wss://{self.network}/sync/{self.node_id}"
|
|
85
190
|
auth_payload = {
|
|
86
191
|
"host": self.host,
|
|
87
192
|
"password": self.password,
|
|
88
193
|
"synapse": self.synapse,
|
|
89
194
|
}
|
|
90
|
-
|
|
91
195
|
while True:
|
|
92
196
|
try:
|
|
93
197
|
async with websockets.connect(full_url) as ws:
|
|
94
198
|
await ws.send(json.dumps(auth_payload))
|
|
95
199
|
print("Connected to WebSocket.")
|
|
96
|
-
|
|
97
200
|
while True:
|
|
98
201
|
try:
|
|
99
202
|
raw_operation = await ws.recv()
|
|
100
203
|
operation = json.loads(raw_operation)
|
|
101
|
-
|
|
102
|
-
|
|
204
|
+
|
|
205
|
+
if "encrypted" in operation.get("data", {}):
|
|
206
|
+
encrypted_data = operation["data"]["encrypted"]
|
|
207
|
+
|
|
208
|
+
ephemeral_public_key_b64 = encrypted_data["ephemeralPublicKey"]
|
|
209
|
+
ephemeral_public_key_b64 += '=' * ((4 - len(ephemeral_public_key_b64) % 4) % 4)
|
|
210
|
+
ephemeral_public_key_bytes = base64.urlsafe_b64decode(ephemeral_public_key_b64)
|
|
211
|
+
|
|
212
|
+
nonce_b64 = encrypted_data["nonce"]
|
|
213
|
+
nonce_b64 += '=' * ((4 - len(nonce_b64) % 4) % 4)
|
|
214
|
+
nonce = base64.urlsafe_b64decode(nonce_b64)
|
|
215
|
+
|
|
216
|
+
ciphertext_b64 = encrypted_data["ciphertext"]
|
|
217
|
+
ciphertext_b64 += '=' * ((4 - len(ciphertext_b64) % 4) % 4)
|
|
218
|
+
ciphertext = base64.urlsafe_b64decode(ciphertext_b64)
|
|
219
|
+
|
|
220
|
+
decrypted_data = self._decrypt_with_ecdh_aesgcm(
|
|
221
|
+
ephemeral_public_key_bytes, nonce, ciphertext
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
if decrypted_data:
|
|
225
|
+
operation["data"].update(decrypted_data)
|
|
226
|
+
operation["data"].pop("encrypted")
|
|
227
|
+
yield operation
|
|
228
|
+
else:
|
|
229
|
+
print("Failed to decrypt incoming data. Skipping...")
|
|
230
|
+
else:
|
|
231
|
+
yield operation
|
|
103
232
|
except asyncio.TimeoutError:
|
|
104
233
|
print("No data received. Continuing...")
|
|
105
|
-
except
|
|
106
|
-
|
|
234
|
+
except ConnectionClosed as e:
|
|
235
|
+
if e.code == 1000:
|
|
236
|
+
print(f"WebSocket closed cleanly (code 1000). Reconnecting...")
|
|
237
|
+
else:
|
|
238
|
+
print(f"Connection closed with error code {e.code}: {e.reason}. Reconnecting...")
|
|
107
239
|
break
|
|
108
240
|
except Exception as e:
|
|
109
241
|
print(f"Unexpected error in recv loop: {e}")
|
|
110
242
|
break
|
|
111
|
-
|
|
112
243
|
except websockets.exceptions.WebSocketException as e:
|
|
113
244
|
print(f"WebSocket error occurred: {e}. Retrying in 5 seconds...")
|
|
114
245
|
except Exception as e:
|
|
115
246
|
print(f"General error occurred: {e}. Retrying in 5 seconds...")
|
|
116
|
-
|
|
117
247
|
await asyncio.sleep(3)
|
|
118
|
-
|
|
119
248
|
|
|
120
|
-
async def create_tx(self, descr: str, key_values: dict, stx: str, label: str, partners: list):
|
|
121
|
-
url = f"https://{self.network}/api/create_tx"
|
|
122
249
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
"stx": stx,
|
|
127
|
-
"label": label,
|
|
128
|
-
"partners": partners,
|
|
250
|
+
async def list_nodes(self):
|
|
251
|
+
full_url = f"https://{self.network}/api/list_nodes"
|
|
252
|
+
list_nodes_payload = {
|
|
129
253
|
"cell": self.to_dict()
|
|
130
254
|
}
|
|
131
|
-
|
|
132
255
|
async with aiohttp.ClientSession() as session:
|
|
133
256
|
try:
|
|
134
|
-
async with session.post(
|
|
257
|
+
async with session.post(full_url, json=list_nodes_payload) as response:
|
|
135
258
|
response.raise_for_status()
|
|
136
259
|
data = await response.json()
|
|
137
|
-
return data
|
|
138
|
-
|
|
260
|
+
return data.get("Nodes", [])
|
|
139
261
|
except aiohttp.ClientError as e:
|
|
140
262
|
print(f"Error sending request: {e}")
|
|
141
263
|
except Exception as e:
|
|
142
264
|
print(f"Unexpected error: {e}")
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _load_public_key_from_jwk(self, jwk):
|
|
268
|
+
try:
|
|
269
|
+
print(jwk)
|
|
270
|
+
x = base64.urlsafe_b64decode(jwk['x'] + '==')
|
|
271
|
+
y = base64.urlsafe_b64decode(jwk['y'] + '==')
|
|
272
|
+
public_numbers = ec.EllipticCurvePublicNumbers(
|
|
273
|
+
int.from_bytes(x, 'big'),
|
|
274
|
+
int.from_bytes(y, 'big'),
|
|
275
|
+
ec.SECP256R1()
|
|
276
|
+
)
|
|
277
|
+
return public_numbers.public_key(default_backend())
|
|
278
|
+
except (KeyError, ValueError, TypeError) as e:
|
|
279
|
+
print(f"Error loading public key from JWK string: {e}")
|
|
280
|
+
return None
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def _encrypt_with_ecdh_aesgcm(self, public_key, plaintext_dict):
|
|
284
|
+
ephemeral_private = ec.generate_private_key(ec.SECP256R1())
|
|
285
|
+
ephemeral_public = ephemeral_private.public_key()
|
|
286
|
+
shared_secret = ephemeral_private.exchange(ec.ECDH(), public_key)
|
|
287
|
+
derived_key = HKDF(
|
|
288
|
+
algorithm=hashes.SHA256(),
|
|
289
|
+
length=32,
|
|
290
|
+
salt=None,
|
|
291
|
+
info=b'handshake data'
|
|
292
|
+
).derive(shared_secret)
|
|
293
|
+
aesgcm = AESGCM(derived_key)
|
|
294
|
+
nonce = os.urandom(12)
|
|
295
|
+
plaintext_bytes = json.dumps(plaintext_dict).encode()
|
|
296
|
+
ciphertext = aesgcm.encrypt(nonce, plaintext_bytes, None)
|
|
297
|
+
ephemeral_public_bytes = ephemeral_public.public_bytes(
|
|
298
|
+
serialization.Encoding.X962,
|
|
299
|
+
serialization.PublicFormat.UncompressedPoint
|
|
300
|
+
)
|
|
301
|
+
return {
|
|
302
|
+
'ciphertext': base64.b64encode(ciphertext).decode(),
|
|
303
|
+
'nonce': base64.b64encode(nonce).decode(),
|
|
304
|
+
'ephemeralPublicKey': base64.b64encode(ephemeral_public_bytes).decode()
|
|
151
305
|
}
|
|
152
|
-
|
|
153
|
-
async with aiohttp.ClientSession() as session:
|
|
154
|
-
try:
|
|
155
|
-
async with session.post(url, json=TX) as response:
|
|
156
|
-
response.raise_for_status()
|
|
157
|
-
data = await response.json()
|
|
158
|
-
print(f"Response from Neuronum: {data}")
|
|
159
|
-
return data
|
|
160
|
-
|
|
161
|
-
except aiohttp.ClientError as e:
|
|
162
|
-
print(f"Error sending request: {e}")
|
|
163
|
-
except Exception as e:
|
|
164
|
-
print(f"Unexpected error: {e}")
|
|
165
306
|
|
|
166
307
|
|
|
167
|
-
async def
|
|
168
|
-
|
|
169
|
-
TX = {
|
|
170
|
-
"data": data,
|
|
171
|
-
"cell": self.to_dict()
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async with aiohttp.ClientSession() as session:
|
|
308
|
+
async def tx_response(self, transmitter_id: str, data: dict, client_public_key_str):
|
|
309
|
+
if isinstance(client_public_key_str, str):
|
|
175
310
|
try:
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
except Exception as e:
|
|
192
|
-
print(f"Unexpected error: {e}")
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
async def tx_response(self, txID: str, client: str, data: dict):
|
|
196
|
-
url = f"https://{self.network}/api/tx_response/{txID}"
|
|
197
|
-
|
|
311
|
+
client_public_key_jwk = json.loads(client_public_key_str)
|
|
312
|
+
except json.JSONDecodeError:
|
|
313
|
+
print("Failed to decode client public key from string. Aborting response.")
|
|
314
|
+
return
|
|
315
|
+
elif isinstance(client_public_key_str, dict):
|
|
316
|
+
client_public_key_jwk = client_public_key_str
|
|
317
|
+
else:
|
|
318
|
+
print("Invalid type for client public key. Expected str or dict. Aborting response.")
|
|
319
|
+
return
|
|
320
|
+
public_key = self._load_public_key_from_jwk(client_public_key_jwk)
|
|
321
|
+
if not public_key:
|
|
322
|
+
print("Failed to load public key. Aborting response.")
|
|
323
|
+
return
|
|
324
|
+
encrypted_payload = self._encrypt_with_ecdh_aesgcm(public_key, data)
|
|
325
|
+
url = f"https://{self.network}/api/tx_response/{transmitter_id}"
|
|
198
326
|
tx_response = {
|
|
199
|
-
"
|
|
200
|
-
"data": data,
|
|
327
|
+
"data": encrypted_payload,
|
|
201
328
|
"cell": self.to_dict()
|
|
202
329
|
}
|
|
203
|
-
|
|
204
330
|
async with aiohttp.ClientSession() as session:
|
|
205
331
|
try:
|
|
206
332
|
for _ in range(2):
|
|
@@ -208,312 +334,7 @@ class Cell:
|
|
|
208
334
|
response.raise_for_status()
|
|
209
335
|
data = await response.json()
|
|
210
336
|
print(data["message"])
|
|
211
|
-
|
|
212
|
-
except aiohttp.ClientError as e:
|
|
213
|
-
print(f"Error sending request: {e}")
|
|
214
|
-
except Exception as e:
|
|
215
|
-
print(f"Unexpected error: {e}")
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
async def create_ctx(self, descr: str, partners: list):
|
|
219
|
-
url = f"https://{self.network}/api/create_ctx"
|
|
220
|
-
|
|
221
|
-
CTX = {
|
|
222
|
-
"descr": descr,
|
|
223
|
-
"partners": partners,
|
|
224
|
-
"cell": self.to_dict()
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
async with aiohttp.ClientSession() as session:
|
|
228
|
-
try:
|
|
229
|
-
async with session.post(url, json=CTX) as response:
|
|
230
|
-
response.raise_for_status()
|
|
231
|
-
data = await response.json()
|
|
232
|
-
return data["ctxID"]
|
|
233
|
-
|
|
234
|
-
except aiohttp.ClientError as e:
|
|
235
|
-
print(f"Error sending request: {e}")
|
|
236
|
-
except Exception as e:
|
|
237
|
-
print(f"Unexpected error: {e}")
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
async def delete_ctx(self, ctxID: str):
|
|
241
|
-
url = f"https://{self.network}/api/delete_ctx"
|
|
242
|
-
|
|
243
|
-
CTX = {
|
|
244
|
-
"ctxID": ctxID,
|
|
245
|
-
"cell": self.to_dict()
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
async with aiohttp.ClientSession() as session:
|
|
249
|
-
try:
|
|
250
|
-
async with session.post(url, json=CTX) as response:
|
|
251
|
-
response.raise_for_status()
|
|
252
|
-
data = await response.json()
|
|
253
|
-
print(f"Response from Neuronum: {data}")
|
|
254
|
-
return data
|
|
255
|
-
|
|
256
|
-
except aiohttp.ClientError as e:
|
|
257
|
-
print(f"Error sending request: {e}")
|
|
258
|
-
except Exception as e:
|
|
259
|
-
print(f"Unexpected error: {e}")
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
async def create_stx(self, descr: str, partners: list):
|
|
263
|
-
url = f"https://{self.network}/api/create_stx"
|
|
264
|
-
|
|
265
|
-
STX = {
|
|
266
|
-
"descr": descr,
|
|
267
|
-
"partners": partners,
|
|
268
|
-
"cell": self.to_dict()
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
async with aiohttp.ClientSession() as session:
|
|
272
|
-
try:
|
|
273
|
-
async with session.post(url, json=STX) as response:
|
|
274
|
-
response.raise_for_status()
|
|
275
|
-
data = await response.json()
|
|
276
|
-
return data["stxID"]
|
|
277
|
-
|
|
278
|
-
except aiohttp.ClientError as e:
|
|
279
|
-
print(f"Error sending request: {e}")
|
|
280
|
-
except Exception as e:
|
|
281
|
-
print(f"Unexpected error: {e}")
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
async def delete_stx(self, stxID: str):
|
|
285
|
-
url = f"https://{self.network}/api/delete_stx"
|
|
286
|
-
|
|
287
|
-
STX = {
|
|
288
|
-
"stxID": stxID,
|
|
289
|
-
"cell": self.to_dict()
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
async with aiohttp.ClientSession() as session:
|
|
293
|
-
try:
|
|
294
|
-
async with session.post(url, json=STX) as response:
|
|
295
|
-
response.raise_for_status()
|
|
296
|
-
data = await response.json()
|
|
297
|
-
print(f"Response from Neuronum: {data}")
|
|
298
|
-
return data
|
|
299
|
-
|
|
300
|
-
except aiohttp.ClientError as e:
|
|
301
|
-
print(f"Error sending request: {e}")
|
|
302
|
-
except Exception as e:
|
|
303
|
-
print(f"Unexpected error: {e}")
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
async def list_cells(self):
|
|
307
|
-
full_url = f"https://{self.network}/api/list_cells"
|
|
308
|
-
|
|
309
|
-
list_cells_payload = {
|
|
310
|
-
"cell": self.to_dict()
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
async with aiohttp.ClientSession() as session:
|
|
314
|
-
try:
|
|
315
|
-
async with session.get(full_url, json=list_cells_payload) as response:
|
|
316
|
-
response.raise_for_status()
|
|
317
|
-
data = await response.json()
|
|
318
|
-
return data.get("Cells", [])
|
|
319
|
-
|
|
320
|
-
except aiohttp.ClientError as e:
|
|
321
|
-
print(f"Error sending request: {e}")
|
|
322
|
-
except Exception as e:
|
|
323
|
-
print(f"Unexpected error: {e}")
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
async def list_tx(self):
|
|
327
|
-
full_url = f"https://{self.network}/api/list_tx"
|
|
328
|
-
|
|
329
|
-
list_tx_payload = {
|
|
330
|
-
"cell": self.to_dict()
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
async with aiohttp.ClientSession() as session:
|
|
334
|
-
try:
|
|
335
|
-
async with session.get(full_url, json=list_tx_payload) as response:
|
|
336
|
-
response.raise_for_status()
|
|
337
|
-
data = await response.json()
|
|
338
|
-
return data.get("Transmitters", [])
|
|
339
|
-
|
|
340
|
-
except aiohttp.ClientError as e:
|
|
341
|
-
print(f"Error sending request: {e}")
|
|
342
|
-
except Exception as e:
|
|
343
|
-
print(f"Unexpected error: {e}")
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
async def list_ctx(self):
|
|
347
|
-
full_url = f"https://{self.network}/api/list_ctx"
|
|
348
|
-
|
|
349
|
-
list_ctx_payload = {
|
|
350
|
-
"cell": self.to_dict()
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
async with aiohttp.ClientSession() as session:
|
|
354
|
-
try:
|
|
355
|
-
async with session.get(full_url, json=list_ctx_payload) as response:
|
|
356
|
-
response.raise_for_status()
|
|
357
|
-
data = await response.json()
|
|
358
|
-
return data.get("Circuits", [])
|
|
359
|
-
|
|
360
|
-
except aiohttp.ClientError as e:
|
|
361
|
-
print(f"Error sending request: {e}")
|
|
362
|
-
except Exception as e:
|
|
363
|
-
print(f"Unexpected error: {e}")
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
async def list_stx(self):
|
|
367
|
-
full_url = f"https://{self.network}/api/list_stx"
|
|
368
|
-
|
|
369
|
-
list_stx_payload = {
|
|
370
|
-
"cell": self.to_dict()
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
async with aiohttp.ClientSession() as session:
|
|
374
|
-
try:
|
|
375
|
-
async with session.get(full_url, json=list_stx_payload) as response:
|
|
376
|
-
response.raise_for_status()
|
|
377
|
-
data = await response.json()
|
|
378
|
-
return data.get("Streams", [])
|
|
379
|
-
|
|
380
|
-
except aiohttp.ClientError as e:
|
|
381
|
-
print(f"Error sending request: {e}")
|
|
382
|
-
except Exception as e:
|
|
383
|
-
print(f"Unexpected error: {e}")
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
async def list_nodes(self):
|
|
387
|
-
full_url = f"https://{self.network}/api/list_nodes"
|
|
388
|
-
|
|
389
|
-
list_nodes_payload = {
|
|
390
|
-
"cell": self.to_dict()
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
async with aiohttp.ClientSession() as session:
|
|
394
|
-
try:
|
|
395
|
-
async with session.get(full_url, json=list_nodes_payload) as response:
|
|
396
|
-
response.raise_for_status()
|
|
397
|
-
data = await response.json()
|
|
398
|
-
return data.get("Nodes", [])
|
|
399
|
-
|
|
400
337
|
except aiohttp.ClientError as e:
|
|
401
338
|
print(f"Error sending request: {e}")
|
|
402
339
|
except Exception as e:
|
|
403
|
-
print(f"Unexpected error: {e}")
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
async def store(self, label: str, data: dict, ctx: Optional[str] = None):
|
|
407
|
-
full_url = f"https://{self.network}/api/store_in_ctx/{ctx}" if ctx else f"https://{self.network}/api/store"
|
|
408
|
-
|
|
409
|
-
store_payload = {
|
|
410
|
-
"label": label,
|
|
411
|
-
"data": data,
|
|
412
|
-
"cell": self.to_dict()
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
async with aiohttp.ClientSession() as session:
|
|
416
|
-
try:
|
|
417
|
-
async with session.post(full_url, json=store_payload) as response:
|
|
418
|
-
response.raise_for_status()
|
|
419
|
-
data = await response.json()
|
|
420
|
-
print(f"Response from Neuronum: {data}")
|
|
421
|
-
return data
|
|
422
|
-
|
|
423
|
-
except aiohttp.ClientError as e:
|
|
424
|
-
print(f"Error sending request: {e}")
|
|
425
|
-
except Exception as e:
|
|
426
|
-
print(f"Unexpected error: {e}")
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
async def load(self, label: str, ctx: Optional[str] = None):
|
|
430
|
-
full_url = f"https://{self.network}/api/load_from_ctx/{ctx}" if ctx else f"https://{self.network}/api/load"
|
|
431
|
-
|
|
432
|
-
load_payload = {
|
|
433
|
-
"label": label,
|
|
434
|
-
"cell": self.to_dict()
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
async with aiohttp.ClientSession() as session:
|
|
438
|
-
try:
|
|
439
|
-
async with session.post(full_url, json=load_payload) as response:
|
|
440
|
-
response.raise_for_status()
|
|
441
|
-
data = await response.json()
|
|
442
|
-
return data
|
|
443
|
-
|
|
444
|
-
except aiohttp.ClientError as e:
|
|
445
|
-
print(f"Error sending request: {e}")
|
|
446
|
-
except Exception as e:
|
|
447
|
-
print(f"Unexpected error: {e}")
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
async def delete(self, label: str, ctx: Optional[str] = None):
|
|
451
|
-
full_url = f"https://{self.network}/api/delete_from_ctx/{ctx}" if ctx else f"https://{self.network}/api/delete"
|
|
452
|
-
|
|
453
|
-
delete_payload = {
|
|
454
|
-
"label": label,
|
|
455
|
-
"cell": self.to_dict()
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
async with aiohttp.ClientSession() as session:
|
|
459
|
-
try:
|
|
460
|
-
async with session.post(full_url, json=delete_payload) as response:
|
|
461
|
-
response.raise_for_status()
|
|
462
|
-
data = await response.json()
|
|
463
|
-
print(f"Response from Neuronum: {data}")
|
|
464
|
-
return data
|
|
465
|
-
|
|
466
|
-
except aiohttp.ClientError as e:
|
|
467
|
-
print(f"Error sending request: {e}")
|
|
468
|
-
except Exception as e:
|
|
469
|
-
print(f"Unexpected error: {e}")
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
async def clear(self, ctx: Optional[str] = None):
|
|
473
|
-
full_url = f"https://{self.network}/api/clear_ctx/{ctx}" if ctx else f"https://{self.network}/api/clear"
|
|
474
|
-
|
|
475
|
-
clear_payload = {
|
|
476
|
-
"cell": self.to_dict()
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
async with aiohttp.ClientSession() as session:
|
|
480
|
-
try:
|
|
481
|
-
async with session.post(full_url, json=clear_payload) as response:
|
|
482
|
-
response.raise_for_status()
|
|
483
|
-
data = await response.json()
|
|
484
|
-
print(f"Response from Neuronum: {data}")
|
|
485
|
-
return data
|
|
486
|
-
|
|
487
|
-
except aiohttp.ClientError as e:
|
|
488
|
-
print(f"Error sending request: {e}")
|
|
489
|
-
except Exception as e:
|
|
490
|
-
print(f"Unexpected error: {e}")
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
async def notify(self, receiver: str, title: str, message: str):
|
|
494
|
-
full_url = f"https://{self.network}/api/notify"
|
|
495
|
-
|
|
496
|
-
notify_payload = {
|
|
497
|
-
"receiver": receiver,
|
|
498
|
-
"notification": {
|
|
499
|
-
"title": title,
|
|
500
|
-
"message": message
|
|
501
|
-
},
|
|
502
|
-
"cell": self.to_dict()
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
async with aiohttp.ClientSession() as session:
|
|
506
|
-
try:
|
|
507
|
-
async with session.post(full_url, json=notify_payload) as response:
|
|
508
|
-
response.raise_for_status()
|
|
509
|
-
data = await response.json()
|
|
510
|
-
print(f"Notification sent successfully: {data}")
|
|
511
|
-
return data
|
|
512
|
-
|
|
513
|
-
except aiohttp.ClientError as e:
|
|
514
|
-
print(f"HTTP error while sending notification: {e}")
|
|
515
|
-
except Exception as e:
|
|
516
|
-
print(f"Unexpected error: {e}")
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
__all__ = ['Cell']
|
|
340
|
+
print(f"Unexpected error: {e}")
|