distributed-state-network 0.4.0__tar.gz → 0.4.2__tar.gz
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.
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/PKG-INFO +1 -6
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/README.md +0 -5
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/pyproject.toml +1 -1
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/src/distributed_state_network/create_key.py +2 -2
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/src/distributed_state_network/dsnode.py +16 -10
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/src/distributed_state_network/handler.py +11 -9
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/src/distributed_state_network/objects/config.py +2 -2
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2/tests}/test.py +2 -2
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/.github/workflows/publish.yml +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/.gitignore +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/LICENSE +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/coverage.sh +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/documentation/ds-node-config.md +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/documentation/ds-node-server.md +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/documentation/ds-node.md +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/documentation/protocol.md +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/documentation/usage.md +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/src/distributed_state_network/__init__.py +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/src/distributed_state_network/objects/endpoint.py +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/src/distributed_state_network/objects/hello_packet.py +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/src/distributed_state_network/objects/msg_types.py +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/src/distributed_state_network/objects/peers_packet.py +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/src/distributed_state_network/objects/signed_packet.py +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/src/distributed_state_network/objects/state_packet.py +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/src/distributed_state_network/util/__init__.py +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/src/distributed_state_network/util/aes.py +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/src/distributed_state_network/util/byte_helper.py +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/src/distributed_state_network/util/ecdsa.py +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/src/distributed_state_network/util/https.py +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/src/distributed_state_network/util/key_manager.py +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/technical.md +0 -0
- {distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/tests/connections.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: distributed-state-network
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: A tool to distribute the state of a network device to other devices on the network
|
|
5
5
|
Project-URL: Homepage, https://github.com/erinclemmer/distributed_state_network
|
|
6
6
|
Project-URL: Issues, https://github.com/erinclemmer/distributed_state_network/issues
|
|
@@ -54,14 +54,10 @@ The simplest DSN network is a single node:
|
|
|
54
54
|
```python
|
|
55
55
|
from distributed_state_network import DSNodeServer, DSNodeConfig
|
|
56
56
|
|
|
57
|
-
# Generate a network key (do this once for your entire network)
|
|
58
|
-
DSNodeServer.generate_key("network.key")
|
|
59
|
-
|
|
60
57
|
# Start a node
|
|
61
58
|
node = DSNodeServer.start(DSNodeConfig(
|
|
62
59
|
node_id="my_first_node",
|
|
63
60
|
port=8000,
|
|
64
|
-
aes_key_file="network.key",
|
|
65
61
|
bootstrap_nodes=[] # Empty for the first node
|
|
66
62
|
))
|
|
67
63
|
|
|
@@ -89,7 +85,6 @@ Create a network of temperature sensors that share readings:
|
|
|
89
85
|
sensor_node = DSNodeServer.start(DSNodeConfig(
|
|
90
86
|
node_id=f"sensor_{location}",
|
|
91
87
|
port=8000,
|
|
92
|
-
aes_key_file="network.key",
|
|
93
88
|
bootstrap_nodes=[{"address": "coordinator.local", "port": 8000}]
|
|
94
89
|
))
|
|
95
90
|
|
|
@@ -28,14 +28,10 @@ The simplest DSN network is a single node:
|
|
|
28
28
|
```python
|
|
29
29
|
from distributed_state_network import DSNodeServer, DSNodeConfig
|
|
30
30
|
|
|
31
|
-
# Generate a network key (do this once for your entire network)
|
|
32
|
-
DSNodeServer.generate_key("network.key")
|
|
33
|
-
|
|
34
31
|
# Start a node
|
|
35
32
|
node = DSNodeServer.start(DSNodeConfig(
|
|
36
33
|
node_id="my_first_node",
|
|
37
34
|
port=8000,
|
|
38
|
-
aes_key_file="network.key",
|
|
39
35
|
bootstrap_nodes=[] # Empty for the first node
|
|
40
36
|
))
|
|
41
37
|
|
|
@@ -63,7 +59,6 @@ Create a network of temperature sensors that share readings:
|
|
|
63
59
|
sensor_node = DSNodeServer.start(DSNodeConfig(
|
|
64
60
|
node_id=f"sensor_{location}",
|
|
65
61
|
port=8000,
|
|
66
|
-
aes_key_file="network.key",
|
|
67
62
|
bootstrap_nodes=[{"address": "coordinator.local", "port": 8000}]
|
|
68
63
|
))
|
|
69
64
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "distributed-state-network"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.2"
|
|
8
8
|
authors = [{name="Erin Clemmer", email="erin.c.clemmer@gmail.com"}]
|
|
9
9
|
description = "A tool to distribute the state of a network device to other devices on the network"
|
|
10
10
|
keywords = ["distributed", "networking"]
|
|
@@ -65,7 +65,7 @@ class DSNode:
|
|
|
65
65
|
self.logger = logging.getLogger("DSN: " + config.node_id)
|
|
66
66
|
self.disconnect_cb = disconnect_callback
|
|
67
67
|
self.update_cb = update_callback
|
|
68
|
-
if not os.path.exists(config.aes_key_file):
|
|
68
|
+
if config.aes_key_file is not None and not os.path.exists(config.aes_key_file):
|
|
69
69
|
raise Exception(f"Could not find aes key file in {config.aes_key_file}")
|
|
70
70
|
|
|
71
71
|
threading.Thread(target=self.network_tick, daemon=True).start()
|
|
@@ -75,8 +75,11 @@ class DSNode:
|
|
|
75
75
|
self.server = server
|
|
76
76
|
|
|
77
77
|
def get_aes_key(self):
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
if not os.path.exists(self.config.aes_key_file):
|
|
79
|
+
raise Exception("Could not find aes key file")
|
|
80
|
+
with open(self.config.aes_key_file, 'r') as f:
|
|
81
|
+
key_hex = f.read()
|
|
82
|
+
return bytes.fromhex(key_hex)
|
|
80
83
|
|
|
81
84
|
def network_tick(self):
|
|
82
85
|
time.sleep(TICK_INTERVAL)
|
|
@@ -109,8 +112,9 @@ class DSNode:
|
|
|
109
112
|
"""Send HTTP request and wait for response"""
|
|
110
113
|
try:
|
|
111
114
|
# Prepend message type to payload
|
|
112
|
-
|
|
113
|
-
|
|
115
|
+
data = bytes([msg_type]) + payload
|
|
116
|
+
if self.config.aes_key_file is not None:
|
|
117
|
+
data = self.encrypt_data(data)
|
|
114
118
|
|
|
115
119
|
# Determine the URL path based on message type
|
|
116
120
|
path = MSG_TYPE_TO_PATH.get(msg_type, '/unknown')
|
|
@@ -119,7 +123,7 @@ class DSNode:
|
|
|
119
123
|
# Send HTTP POST request
|
|
120
124
|
response = requests.post(
|
|
121
125
|
url,
|
|
122
|
-
data=
|
|
126
|
+
data=data,
|
|
123
127
|
headers={'Content-Type': 'application/octet-stream'},
|
|
124
128
|
timeout=HTTP_TIMEOUT
|
|
125
129
|
)
|
|
@@ -131,19 +135,21 @@ class DSNode:
|
|
|
131
135
|
elif response.status_code != 200:
|
|
132
136
|
raise Exception(f"HTTP error: {response.status_code}")
|
|
133
137
|
|
|
138
|
+
response_data = response.content
|
|
134
139
|
# Decrypt the response
|
|
135
|
-
|
|
140
|
+
if self.config.aes_key_file is not None:
|
|
141
|
+
response_data = self.decrypt_data(response_data)
|
|
136
142
|
|
|
137
|
-
if len(
|
|
143
|
+
if len(response_data) < 1:
|
|
138
144
|
raise Exception("Empty response")
|
|
139
145
|
|
|
140
146
|
# First byte is message type
|
|
141
|
-
response_msg_type =
|
|
147
|
+
response_msg_type = response_data[0]
|
|
142
148
|
if response_msg_type != msg_type:
|
|
143
149
|
raise Exception(f"Response message type mismatch: expected {msg_type}, got {response_msg_type}")
|
|
144
150
|
|
|
145
151
|
# Return the body (everything after the message type byte)
|
|
146
|
-
return
|
|
152
|
+
return response_data[1:]
|
|
147
153
|
|
|
148
154
|
except requests.exceptions.Timeout:
|
|
149
155
|
if retries < 2:
|
|
@@ -8,7 +8,7 @@ from distributed_state_network.objects.config import DSNodeConfig
|
|
|
8
8
|
from distributed_state_network.util.aes import generate_aes_key
|
|
9
9
|
from distributed_state_network.util import stop_thread
|
|
10
10
|
|
|
11
|
-
VERSION = "0.4.
|
|
11
|
+
VERSION = "0.4.2"
|
|
12
12
|
logging.basicConfig(level=logging.INFO)
|
|
13
13
|
|
|
14
14
|
# Silence Flask and Werkzeug logging
|
|
@@ -66,14 +66,15 @@ class DSNodeServer:
|
|
|
66
66
|
"""Handle incoming HTTP request"""
|
|
67
67
|
try:
|
|
68
68
|
# Decrypt the data
|
|
69
|
-
|
|
69
|
+
if self.config.aes_key_file is not None:
|
|
70
|
+
data = self.node.decrypt_data(data)
|
|
70
71
|
|
|
71
|
-
if len(
|
|
72
|
+
if len(data) < 1:
|
|
72
73
|
return Response(status=400)
|
|
73
74
|
|
|
74
75
|
# First byte should be message type (for verification)
|
|
75
|
-
received_msg_type =
|
|
76
|
-
body =
|
|
76
|
+
received_msg_type = data[0]
|
|
77
|
+
body = data[1:]
|
|
77
78
|
|
|
78
79
|
if received_msg_type != msg_type:
|
|
79
80
|
self.node.logger.error(f"Message type mismatch: expected {msg_type}, got {received_msg_type}")
|
|
@@ -98,8 +99,9 @@ class DSNodeServer:
|
|
|
98
99
|
if response_data is not None:
|
|
99
100
|
# Prepend message type to response
|
|
100
101
|
response_with_type = bytes([msg_type]) + response_data
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
if self.config.aes_key_file is not None:
|
|
103
|
+
response_with_type = self.node.encrypt_data(response_with_type)
|
|
104
|
+
return Response(response_with_type, status=200, mimetype='application/octet-stream')
|
|
103
105
|
else:
|
|
104
106
|
return Response(status=204) # No content
|
|
105
107
|
|
|
@@ -132,8 +134,8 @@ class DSNodeServer:
|
|
|
132
134
|
@staticmethod
|
|
133
135
|
def generate_key(out_file_path: str):
|
|
134
136
|
key = generate_aes_key()
|
|
135
|
-
with open(out_file_path, '
|
|
136
|
-
f.write(key)
|
|
137
|
+
with open(out_file_path, 'w', encoding='utf-8') as f:
|
|
138
|
+
f.write(key.hex())
|
|
137
139
|
|
|
138
140
|
@staticmethod
|
|
139
141
|
def start(
|
|
@@ -7,7 +7,7 @@ from distributed_state_network.objects.endpoint import Endpoint
|
|
|
7
7
|
class DSNodeConfig:
|
|
8
8
|
node_id: str
|
|
9
9
|
port: int
|
|
10
|
-
aes_key_file: str
|
|
10
|
+
aes_key_file: str | None
|
|
11
11
|
bootstrap_nodes: List[Endpoint]
|
|
12
12
|
|
|
13
13
|
@staticmethod
|
|
@@ -15,6 +15,6 @@ class DSNodeConfig:
|
|
|
15
15
|
return DSNodeConfig(
|
|
16
16
|
data["node_id"],
|
|
17
17
|
data["port"],
|
|
18
|
-
data["aes_key_file"],
|
|
18
|
+
data["aes_key_file"] if "aes_key_file" in data else None,
|
|
19
19
|
[Endpoint.from_json(e) for e in data["bootstrap_nodes"]]
|
|
20
20
|
)
|
|
@@ -7,7 +7,7 @@ import unittest
|
|
|
7
7
|
import requests
|
|
8
8
|
from typing import List, Dict
|
|
9
9
|
|
|
10
|
-
sys.path.append(os.path.join(os.path.dirname(__file__), '
|
|
10
|
+
sys.path.append(os.path.join(os.path.dirname(__file__), '../src'))
|
|
11
11
|
|
|
12
12
|
from distributed_state_network import DSNodeServer, Endpoint, DSNodeConfig
|
|
13
13
|
|
|
@@ -311,7 +311,7 @@ class TestNode(unittest.TestCase):
|
|
|
311
311
|
DSNodeServer.generate_key(test_key_file)
|
|
312
312
|
with open(test_key_file, 'rb') as f:
|
|
313
313
|
key = f.read()
|
|
314
|
-
self.assertEqual(
|
|
314
|
+
self.assertEqual(64, len(key))
|
|
315
315
|
os.remove(test_key_file)
|
|
316
316
|
|
|
317
317
|
def test_write_cert(self):
|
{distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/.github/workflows/publish.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/documentation/ds-node-config.md
RENAMED
|
File without changes
|
{distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/documentation/ds-node-server.md
RENAMED
|
File without changes
|
{distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/documentation/ds-node.md
RENAMED
|
File without changes
|
{distributed_state_network-0.4.0 → distributed_state_network-0.4.2}/documentation/protocol.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|