distributed-state-network 0.4.1__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.
Files changed (32) hide show
  1. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/PKG-INFO +1 -6
  2. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/README.md +0 -5
  3. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/pyproject.toml +1 -1
  4. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/src/distributed_state_network/create_key.py +2 -2
  5. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/src/distributed_state_network/dsnode.py +13 -8
  6. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/src/distributed_state_network/handler.py +11 -9
  7. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/src/distributed_state_network/objects/config.py +2 -2
  8. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2/tests}/test.py +2 -2
  9. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/.github/workflows/publish.yml +0 -0
  10. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/.gitignore +0 -0
  11. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/LICENSE +0 -0
  12. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/coverage.sh +0 -0
  13. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/documentation/ds-node-config.md +0 -0
  14. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/documentation/ds-node-server.md +0 -0
  15. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/documentation/ds-node.md +0 -0
  16. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/documentation/protocol.md +0 -0
  17. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/documentation/usage.md +0 -0
  18. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/src/distributed_state_network/__init__.py +0 -0
  19. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/src/distributed_state_network/objects/endpoint.py +0 -0
  20. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/src/distributed_state_network/objects/hello_packet.py +0 -0
  21. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/src/distributed_state_network/objects/msg_types.py +0 -0
  22. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/src/distributed_state_network/objects/peers_packet.py +0 -0
  23. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/src/distributed_state_network/objects/signed_packet.py +0 -0
  24. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/src/distributed_state_network/objects/state_packet.py +0 -0
  25. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/src/distributed_state_network/util/__init__.py +0 -0
  26. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/src/distributed_state_network/util/aes.py +0 -0
  27. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/src/distributed_state_network/util/byte_helper.py +0 -0
  28. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/src/distributed_state_network/util/ecdsa.py +0 -0
  29. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/src/distributed_state_network/util/https.py +0 -0
  30. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/src/distributed_state_network/util/key_manager.py +0 -0
  31. {distributed_state_network-0.4.1 → distributed_state_network-0.4.2}/technical.md +0 -0
  32. {distributed_state_network-0.4.1 → 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.1
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.1"
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"]
@@ -6,5 +6,5 @@ if len(sys.argv) < 2:
6
6
 
7
7
  location = sys.argv[1]
8
8
 
9
- with open(location, 'wb') as f:
10
- f.write(generate_aes_key())
9
+ with open(location, 'w') as f:
10
+ f.write(generate_aes_key().hex())
@@ -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,6 +75,8 @@ class DSNode:
75
75
  self.server = server
76
76
 
77
77
  def get_aes_key(self):
78
+ if not os.path.exists(self.config.aes_key_file):
79
+ raise Exception("Could not find aes key file")
78
80
  with open(self.config.aes_key_file, 'r') as f:
79
81
  key_hex = f.read()
80
82
  return bytes.fromhex(key_hex)
@@ -110,8 +112,9 @@ class DSNode:
110
112
  """Send HTTP request and wait for response"""
111
113
  try:
112
114
  # Prepend message type to payload
113
- data_with_type = bytes([msg_type]) + payload
114
- encrypted_data = self.encrypt_data(data_with_type)
115
+ data = bytes([msg_type]) + payload
116
+ if self.config.aes_key_file is not None:
117
+ data = self.encrypt_data(data)
115
118
 
116
119
  # Determine the URL path based on message type
117
120
  path = MSG_TYPE_TO_PATH.get(msg_type, '/unknown')
@@ -120,7 +123,7 @@ class DSNode:
120
123
  # Send HTTP POST request
121
124
  response = requests.post(
122
125
  url,
123
- data=encrypted_data,
126
+ data=data,
124
127
  headers={'Content-Type': 'application/octet-stream'},
125
128
  timeout=HTTP_TIMEOUT
126
129
  )
@@ -132,19 +135,21 @@ class DSNode:
132
135
  elif response.status_code != 200:
133
136
  raise Exception(f"HTTP error: {response.status_code}")
134
137
 
138
+ response_data = response.content
135
139
  # Decrypt the response
136
- decrypted = self.decrypt_data(response.content)
140
+ if self.config.aes_key_file is not None:
141
+ response_data = self.decrypt_data(response_data)
137
142
 
138
- if len(decrypted) < 1:
143
+ if len(response_data) < 1:
139
144
  raise Exception("Empty response")
140
145
 
141
146
  # First byte is message type
142
- response_msg_type = decrypted[0]
147
+ response_msg_type = response_data[0]
143
148
  if response_msg_type != msg_type:
144
149
  raise Exception(f"Response message type mismatch: expected {msg_type}, got {response_msg_type}")
145
150
 
146
151
  # Return the body (everything after the message type byte)
147
- return decrypted[1:]
152
+ return response_data[1:]
148
153
 
149
154
  except requests.exceptions.Timeout:
150
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.1"
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
- decrypted = self.node.decrypt_data(data)
69
+ if self.config.aes_key_file is not None:
70
+ data = self.node.decrypt_data(data)
70
71
 
71
- if len(decrypted) < 1:
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 = decrypted[0]
76
- body = decrypted[1:]
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
- encrypted_response = self.node.encrypt_data(response_with_type)
102
- return Response(encrypted_response, status=200, mimetype='application/octet-stream')
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, 'wb') as f:
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__), './src'))
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(32, len(key))
314
+ self.assertEqual(64, len(key))
315
315
  os.remove(test_key_file)
316
316
 
317
317
  def test_write_cert(self):