distributed-state-network 0.2.0__tar.gz → 0.3.0__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.2.0 → distributed_state_network-0.3.0}/PKG-INFO +1 -1
- distributed_state_network-0.3.0/documentation/ds-node-server.md +192 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/documentation/protocol.md +1 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/pyproject.toml +1 -1
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/src/distributed_state_network/dsnode.py +6 -5
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/src/distributed_state_network/handler.py +13 -11
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/tests/connections.py +4 -12
- distributed_state_network-0.2.0/documentation/ds-node-server.md +0 -58
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/.github/workflows/publish.yml +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/.gitignore +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/LICENSE +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/README.md +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/coverage.sh +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/documentation/ds-node-config.md +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/documentation/ds-node.md +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/documentation/usage.md +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/src/distributed_state_network/__init__.py +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/src/distributed_state_network/create_key.py +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/src/distributed_state_network/objects/config.py +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/src/distributed_state_network/objects/endpoint.py +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/src/distributed_state_network/objects/hello_packet.py +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/src/distributed_state_network/objects/msg_types.py +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/src/distributed_state_network/objects/peers_packet.py +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/src/distributed_state_network/objects/signed_packet.py +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/src/distributed_state_network/objects/state_packet.py +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/src/distributed_state_network/util/__init__.py +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/src/distributed_state_network/util/aes.py +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/src/distributed_state_network/util/byte_helper.py +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/src/distributed_state_network/util/ecdsa.py +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/src/distributed_state_network/util/https.py +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/src/distributed_state_network/util/key_manager.py +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/technical.md +0 -0
- {distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/test.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: distributed-state-network
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
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
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
## DSNodeServer
|
|
2
|
+
|
|
3
|
+
UDP server wrapper for DSNode that handles incoming network requests.
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
from distributed_state_network import DSNodeServer
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
### Class Definition
|
|
10
|
+
```python
|
|
11
|
+
class DSNodeServer:
|
|
12
|
+
config: DSNodeConfig
|
|
13
|
+
node: DSNode
|
|
14
|
+
socket: socket.socket
|
|
15
|
+
running: bool
|
|
16
|
+
thread: threading.Thread
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Constructor
|
|
20
|
+
|
|
21
|
+
**Parameters:**
|
|
22
|
+
- `config` (`DSNodeConfig`): Node configuration
|
|
23
|
+
- `sock` (`Optional[socket.socket]`): Custom UDP socket to use (optional, creates new socket if not provided)
|
|
24
|
+
- `disconnect_callback` (`Optional[Callable]`): Callback for disconnect events
|
|
25
|
+
- `update_callback` (`Optional[Callable]`): Callback for state update events
|
|
26
|
+
|
|
27
|
+
**Example:**
|
|
28
|
+
```python
|
|
29
|
+
# Create with default socket
|
|
30
|
+
server = DSNodeServer(config)
|
|
31
|
+
|
|
32
|
+
# Or with custom socket
|
|
33
|
+
custom_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
34
|
+
custom_socket.bind(("0.0.0.0", 8000))
|
|
35
|
+
server = DSNodeServer(config, sock=custom_socket)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Static Methods
|
|
39
|
+
|
|
40
|
+
### `start(config: DSNodeConfig, sock: Optional[socket.socket] = None, disconnect_callback: Optional[Callable] = None, update_callback: Optional[Callable] = None) -> DSNodeServer`
|
|
41
|
+
Creates and starts a new DSNodeServer instance with UDP socket.
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
server = DSNodeServer.start(config)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Parameters:**
|
|
48
|
+
- `config` (`DSNodeConfig`): Node configuration
|
|
49
|
+
- `sock` (`Optional[socket.socket]`): Custom UDP socket to use (optional)
|
|
50
|
+
- `disconnect_callback` (`Optional[Callable]`): Callback for disconnect events (no parameters)
|
|
51
|
+
- `update_callback` (`Optional[Callable]`): Callback for state update events (no parameters)
|
|
52
|
+
|
|
53
|
+
**Returns:**
|
|
54
|
+
- `DSNodeServer`: Running server instance
|
|
55
|
+
|
|
56
|
+
**Example with bootstrap:**
|
|
57
|
+
```python
|
|
58
|
+
# Bootstrap node (first node in network)
|
|
59
|
+
bootstrap_config = DSNodeConfig(
|
|
60
|
+
node_id="bootstrap",
|
|
61
|
+
port=8000,
|
|
62
|
+
aes_key_file="network.key",
|
|
63
|
+
bootstrap_nodes=[]
|
|
64
|
+
)
|
|
65
|
+
bootstrap = DSNodeServer.start(bootstrap_config)
|
|
66
|
+
|
|
67
|
+
# Connector node (joins existing network)
|
|
68
|
+
connector_config = DSNodeConfig(
|
|
69
|
+
node_id="connector",
|
|
70
|
+
port=8001,
|
|
71
|
+
aes_key_file="network.key",
|
|
72
|
+
bootstrap_nodes=[Endpoint("127.0.0.1", 8000)]
|
|
73
|
+
)
|
|
74
|
+
connector = DSNodeServer.start(connector_config)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### `generate_key(out_file_path: str) -> None`
|
|
78
|
+
Generates a new AES key file for network encryption. All nodes in the same network must share the same AES key.
|
|
79
|
+
|
|
80
|
+
**Parameters:**
|
|
81
|
+
- `out_file_path` (`str`): Path where the key file will be saved
|
|
82
|
+
|
|
83
|
+
**Example:**
|
|
84
|
+
```python
|
|
85
|
+
DSNodeServer.generate_key("/path/to/network.key")
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Instance Methods
|
|
89
|
+
|
|
90
|
+
### `stop() -> None`
|
|
91
|
+
Gracefully shuts down the server and cleans up resources.
|
|
92
|
+
|
|
93
|
+
**Example:**
|
|
94
|
+
```python
|
|
95
|
+
server.stop()
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### `serve_forever() -> None`
|
|
99
|
+
Main UDP server loop. Continuously listens for incoming packets and routes them to appropriate handlers. This method is typically called automatically by `start()` in a daemon thread.
|
|
100
|
+
|
|
101
|
+
**Note:** This method blocks until the server is stopped.
|
|
102
|
+
|
|
103
|
+
## Network Protocol
|
|
104
|
+
|
|
105
|
+
The server uses UDP sockets with the following characteristics:
|
|
106
|
+
|
|
107
|
+
- **Port**: Configurable via DSNodeConfig
|
|
108
|
+
- **Encryption**: All packets encrypted with AES
|
|
109
|
+
- **Authentication**: ECDSA signatures for message verification
|
|
110
|
+
- **Max Packet Size**: 65507 bytes (UDP limit)
|
|
111
|
+
- **Timeout**: 2 seconds with automatic retry (up to 3 attempts)
|
|
112
|
+
|
|
113
|
+
## Message Types
|
|
114
|
+
|
|
115
|
+
The server handles four types of messages:
|
|
116
|
+
|
|
117
|
+
1. **HELLO (1)**: Node introduction and public key exchange
|
|
118
|
+
2. **PEERS (2)**: Request/response for peer list
|
|
119
|
+
3. **UPDATE (3)**: State synchronization
|
|
120
|
+
4. **PING (4)**: Connection health check
|
|
121
|
+
|
|
122
|
+
## Automatic IP Detection
|
|
123
|
+
|
|
124
|
+
When a client connects to a bootstrap server:
|
|
125
|
+
|
|
126
|
+
1. Server extracts client's IP from UDP packet source address
|
|
127
|
+
2. Server includes detected IP in HELLO response via `detected_address` field
|
|
128
|
+
3. Client updates its own address book with the detected IP
|
|
129
|
+
4. Correct IP propagates to other nodes through peer discovery
|
|
130
|
+
|
|
131
|
+
This eliminates the need for manual IP configuration and works correctly behind NAT.
|
|
132
|
+
|
|
133
|
+
## Thread Safety
|
|
134
|
+
|
|
135
|
+
The server spawns separate threads for:
|
|
136
|
+
- Main UDP receive loop (`serve_forever`)
|
|
137
|
+
- Packet handling (one thread per packet)
|
|
138
|
+
- Node network tick (periodic health checks)
|
|
139
|
+
|
|
140
|
+
All shared state is protected with appropriate locks.
|
|
141
|
+
|
|
142
|
+
## Example: Multi-Node Setup
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from distributed_state_network import DSNodeServer, DSNodeConfig, Endpoint
|
|
146
|
+
|
|
147
|
+
# Generate shared AES key (only once)
|
|
148
|
+
DSNodeServer.generate_key("shared.key")
|
|
149
|
+
|
|
150
|
+
# Start bootstrap node
|
|
151
|
+
bootstrap = DSNodeServer.start(DSNodeConfig(
|
|
152
|
+
node_id="bootstrap",
|
|
153
|
+
port=8000,
|
|
154
|
+
aes_key_file="shared.key",
|
|
155
|
+
bootstrap_nodes=[]
|
|
156
|
+
))
|
|
157
|
+
|
|
158
|
+
# Start additional nodes
|
|
159
|
+
node1 = DSNodeServer.start(DSNodeConfig(
|
|
160
|
+
node_id="node1",
|
|
161
|
+
port=8001,
|
|
162
|
+
aes_key_file="shared.key",
|
|
163
|
+
bootstrap_nodes=[Endpoint("192.168.1.100", 8000)]
|
|
164
|
+
))
|
|
165
|
+
|
|
166
|
+
node2 = DSNodeServer.start(DSNodeConfig(
|
|
167
|
+
node_id="node2",
|
|
168
|
+
port=8002,
|
|
169
|
+
aes_key_file="shared.key",
|
|
170
|
+
bootstrap_nodes=[Endpoint("192.168.1.100", 8000)]
|
|
171
|
+
))
|
|
172
|
+
|
|
173
|
+
# Nodes automatically discover each other through bootstrap
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Troubleshooting
|
|
177
|
+
|
|
178
|
+
### Server won't start
|
|
179
|
+
- Check if port is already in use
|
|
180
|
+
- Verify AES key file exists and is readable
|
|
181
|
+
- Ensure firewall allows UDP traffic on specified port
|
|
182
|
+
|
|
183
|
+
### Nodes can't connect
|
|
184
|
+
- Verify all nodes use the same AES key
|
|
185
|
+
- Check bootstrap node is running and reachable
|
|
186
|
+
- Verify network connectivity (ping bootstrap address)
|
|
187
|
+
- Check firewall rules for UDP ports
|
|
188
|
+
|
|
189
|
+
### Connection drops
|
|
190
|
+
- Increase timeout if on slow networks
|
|
191
|
+
- Check network stability
|
|
192
|
+
- Verify nodes aren't being stopped unexpectedly
|
{distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/documentation/protocol.md
RENAMED
|
@@ -19,6 +19,7 @@ All UDP packets use a message type byte prefix to identify the packet type:
|
|
|
19
19
|
Each UDP packet follows this structure:
|
|
20
20
|
1. **AES Encrypted Payload** containing:
|
|
21
21
|
- Message type (1 byte)
|
|
22
|
+
- Response bit (1 byte): 0 for requests, 1 for responses
|
|
22
23
|
- Message payload (variable length)
|
|
23
24
|
|
|
24
25
|
### Security
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "distributed-state-network"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
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"]
|
|
@@ -108,8 +108,8 @@ class DSNode:
|
|
|
108
108
|
raise Exception("Socket not set. Cannot send UDP request.")
|
|
109
109
|
|
|
110
110
|
try:
|
|
111
|
-
# Prepend message type to payload
|
|
112
|
-
data_with_type = bytes([msg_type]) + payload
|
|
111
|
+
# Prepend message type and response bit (0 for request) to payload
|
|
112
|
+
data_with_type = bytes([msg_type, 0]) + payload
|
|
113
113
|
encrypted_data = self.encrypt_data(data_with_type)
|
|
114
114
|
|
|
115
115
|
# Create a unique ID for this request based on endpoint and msg type
|
|
@@ -159,12 +159,13 @@ class DSNode:
|
|
|
159
159
|
# Decrypt the data
|
|
160
160
|
decrypted = self.decrypt_data(data)
|
|
161
161
|
|
|
162
|
-
if len(decrypted)
|
|
162
|
+
if len(decrypted) < 2:
|
|
163
163
|
return
|
|
164
164
|
|
|
165
|
-
# First byte is message type
|
|
165
|
+
# First byte is message type, second byte is response bit
|
|
166
166
|
msg_type = decrypted[0]
|
|
167
|
-
|
|
167
|
+
is_response = decrypted[1]
|
|
168
|
+
body = decrypted[2:]
|
|
168
169
|
|
|
169
170
|
# Find matching pending request
|
|
170
171
|
request_id = (addr[0], addr[1], msg_type)
|
|
@@ -7,7 +7,7 @@ from distributed_state_network.objects.config import DSNodeConfig
|
|
|
7
7
|
from distributed_state_network.util.aes import generate_aes_key
|
|
8
8
|
from distributed_state_network.util import stop_thread
|
|
9
9
|
|
|
10
|
-
VERSION = "0.
|
|
10
|
+
VERSION = "0.3.0"
|
|
11
11
|
logging.basicConfig(level=logging.INFO)
|
|
12
12
|
|
|
13
13
|
# Message type constants
|
|
@@ -53,19 +53,20 @@ class DSNodeServer:
|
|
|
53
53
|
# Decrypt the data
|
|
54
54
|
decrypted = self.node.decrypt_data(data)
|
|
55
55
|
|
|
56
|
-
if len(decrypted)
|
|
56
|
+
if len(decrypted) < 2:
|
|
57
57
|
return
|
|
58
58
|
|
|
59
|
-
# First byte is message type
|
|
59
|
+
# First byte is message type, second byte is response bit
|
|
60
60
|
msg_type = decrypted[0]
|
|
61
|
-
|
|
61
|
+
is_response = decrypted[1]
|
|
62
|
+
body = decrypted[2:]
|
|
62
63
|
|
|
63
|
-
# Check if this is a response
|
|
64
|
+
# Check if this is a response (either by bit flag or by pending request)
|
|
64
65
|
request_id = (addr[0], addr[1], msg_type)
|
|
65
66
|
with self.node.response_lock:
|
|
66
|
-
|
|
67
|
+
has_pending_request = request_id in self.node.pending_responses
|
|
67
68
|
|
|
68
|
-
if is_response:
|
|
69
|
+
if is_response == 1 or has_pending_request:
|
|
69
70
|
# This is a response to our request - route to response handler
|
|
70
71
|
self.node.handle_response(data, addr)
|
|
71
72
|
else:
|
|
@@ -87,8 +88,8 @@ class DSNodeServer:
|
|
|
87
88
|
|
|
88
89
|
# Send response if handler returned data
|
|
89
90
|
if response is not None:
|
|
90
|
-
# Prepend message type to response
|
|
91
|
-
response_with_type = bytes([msg_type]) + response
|
|
91
|
+
# Prepend message type and response bit (1 for response) to response
|
|
92
|
+
response_with_type = bytes([msg_type, 1]) + response
|
|
92
93
|
encrypted_response = self.node.encrypt_data(response_with_type)
|
|
93
94
|
self.socket.sendto(encrypted_response, addr)
|
|
94
95
|
|
|
@@ -104,8 +105,9 @@ class DSNodeServer:
|
|
|
104
105
|
try:
|
|
105
106
|
# Receive UDP packet (max 65507 bytes for UDP)
|
|
106
107
|
data, addr = self.socket.recvfrom(65507)
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
if self.running:
|
|
109
|
+
# Handle packet in separate thread to avoid blocking
|
|
110
|
+
threading.Thread(target=self.handle_packet, args=(data, addr), daemon=True).start()
|
|
109
111
|
except OSError:
|
|
110
112
|
# Socket was closed
|
|
111
113
|
if not self.running:
|
|
@@ -17,14 +17,12 @@ cb_test = 0
|
|
|
17
17
|
|
|
18
18
|
current_port = 5000
|
|
19
19
|
|
|
20
|
-
def start_node(node_id: str, bootstrap_port: int = None, disconnect_cb: Optional[Callable] = None, update_cb: Optional[Callable] = None
|
|
20
|
+
def start_node(node_id: str, bootstrap_port: int = None, disconnect_cb: Optional[Callable] = None, update_cb: Optional[Callable] = None):
|
|
21
21
|
global current_port
|
|
22
22
|
args = {
|
|
23
23
|
"node_id": node_id,
|
|
24
24
|
"port": current_port,
|
|
25
|
-
"aes_key_file": KEY_FILE
|
|
26
|
-
"https": https,
|
|
27
|
-
"network_ip": "127.0.0.1"
|
|
25
|
+
"aes_key_file": KEY_FILE
|
|
28
26
|
}
|
|
29
27
|
current_port += 1
|
|
30
28
|
if bootstrap_port is not None:
|
|
@@ -35,7 +33,7 @@ def start_node(node_id: str, bootstrap_port: int = None, disconnect_cb: Optional
|
|
|
35
33
|
else:
|
|
36
34
|
args["bootstrap_nodes"] = []
|
|
37
35
|
|
|
38
|
-
return DSNodeServer.start(DSNodeConfig(**args), disconnect_cb, update_cb)
|
|
36
|
+
return DSNodeServer.start(DSNodeConfig(**args), None, disconnect_cb, update_cb)
|
|
39
37
|
|
|
40
38
|
class ConnectionsTest(unittest.TestCase):
|
|
41
39
|
def test_one(self):
|
|
@@ -48,12 +46,6 @@ class ConnectionsTest(unittest.TestCase):
|
|
|
48
46
|
self.assertEqual(["node-1", "node-2"], sorted(node1.node.peers()))
|
|
49
47
|
self.assertEqual(["node-1", "node-2"], sorted(node2.node.peers()))
|
|
50
48
|
|
|
51
|
-
def test_https(self):
|
|
52
|
-
node1 = start_node("node-1", https=True)
|
|
53
|
-
node2 = start_node("node-2", node1.config.port, https=True)
|
|
54
|
-
self.assertEqual(["node-1", "node-2"], sorted(node1.node.peers()))
|
|
55
|
-
self.assertEqual(["node-1", "node-2"], sorted(node2.node.peers()))
|
|
56
|
-
|
|
57
49
|
def test_many(self):
|
|
58
50
|
bootstrap_node = start_node("bootstrap")
|
|
59
51
|
nodes = []
|
|
@@ -92,7 +84,7 @@ class ConnectionsTest(unittest.TestCase):
|
|
|
92
84
|
node2 = start_node("node-2", node1.config.port)
|
|
93
85
|
time.sleep(1)
|
|
94
86
|
node2.stop()
|
|
95
|
-
time.sleep(
|
|
87
|
+
time.sleep(10)
|
|
96
88
|
self.assertEqual(cb_test, 1)
|
|
97
89
|
|
|
98
90
|
def test_update_cb(self):
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
## DSNodeServer
|
|
2
|
-
|
|
3
|
-
HTTPS server wrapper for DSNode that handles incoming network requests.
|
|
4
|
-
|
|
5
|
-
```python
|
|
6
|
-
from distributed_state_network import DSNodeServer
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
### Class Definition
|
|
10
|
-
```python
|
|
11
|
-
class DSNodeServer(HTTPServer):
|
|
12
|
-
config: DSNodeConfig
|
|
13
|
-
node: DSNode
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
### Constructor
|
|
17
|
-
|
|
18
|
-
**Parameters:**
|
|
19
|
-
- `config` (`DSNodeConfig`): Node configuration
|
|
20
|
-
- `disconnect_callback` (`Optional[Callable]`): Callback for disconnect events
|
|
21
|
-
- `update_callback` (`Optional[Callable]`): Callback for state update events
|
|
22
|
-
|
|
23
|
-
### Static Methods
|
|
24
|
-
|
|
25
|
-
### `start(config: DSNodeConfig, disconnect_callback: Optional[Callable] = None, update_callback: Optional[Callable] = None) -> DSNodeServer`
|
|
26
|
-
Creates and starts a new DSNodeServer instance with SSL configuration.
|
|
27
|
-
```python
|
|
28
|
-
server = DSNodeServer.start(config)
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
**Parameters:**
|
|
32
|
-
- `config` (`DSNodeConfig`): Node configuration
|
|
33
|
-
- `disconnect_callback` (`Optional[Callable]`): Callback for disconnect events (no parameters)
|
|
34
|
-
- `update_callback` (`Optional[Callable]`): Callback for state update events (no parameters)
|
|
35
|
-
|
|
36
|
-
**Returns:**
|
|
37
|
-
- `DSNodeServer`: Running server instance
|
|
38
|
-
|
|
39
|
-
### `generate_key(out_file_path: str) -> None`
|
|
40
|
-
Generates a new AES key file for network encryption.
|
|
41
|
-
|
|
42
|
-
**Parameters:**
|
|
43
|
-
- `out_file_path` (`str`): Path where the key file will be saved
|
|
44
|
-
|
|
45
|
-
**Example:**
|
|
46
|
-
```python
|
|
47
|
-
DSNodeServer.generate_key("/path/to/network.key")
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### Instance Methods
|
|
51
|
-
|
|
52
|
-
### `stop() -> None`
|
|
53
|
-
Gracefully shuts down the server and cleans up resources.
|
|
54
|
-
|
|
55
|
-
**Example:**
|
|
56
|
-
```python
|
|
57
|
-
server.stop()
|
|
58
|
-
```
|
{distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/.github/workflows/publish.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/documentation/ds-node-config.md
RENAMED
|
File without changes
|
{distributed_state_network-0.2.0 → distributed_state_network-0.3.0}/documentation/ds-node.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
|
|
File without changes
|
|
File without changes
|