astreum 0.3.14__tar.gz → 0.3.46__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.
- {astreum-0.3.14/src/astreum.egg-info → astreum-0.3.46}/PKG-INFO +43 -9
- {astreum-0.3.14 → astreum-0.3.46}/README.md +42 -8
- {astreum-0.3.14 → astreum-0.3.46}/pyproject.toml +1 -1
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/__init__.py +1 -2
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/communication/__init__.py +15 -11
- astreum-0.3.46/src/astreum/communication/difficulty.py +39 -0
- astreum-0.3.46/src/astreum/communication/disconnect.py +57 -0
- astreum-0.3.46/src/astreum/communication/handlers/handshake.py +105 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/communication/handlers/object_request.py +179 -149
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/communication/handlers/object_response.py +7 -1
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/communication/handlers/ping.py +9 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/communication/handlers/route_request.py +7 -1
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/communication/handlers/route_response.py +7 -1
- astreum-0.3.46/src/astreum/communication/incoming_queue.py +96 -0
- astreum-0.3.46/src/astreum/communication/message_pow.py +36 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/communication/models/peer.py +4 -0
- astreum-0.3.46/src/astreum/communication/models/ping.py +54 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/communication/models/route.py +4 -0
- astreum-0.3.14/src/astreum/communication/start.py → astreum-0.3.46/src/astreum/communication/node.py +10 -11
- astreum-0.3.46/src/astreum/communication/outgoing_queue.py +108 -0
- astreum-0.3.46/src/astreum/communication/processors/incoming.py +171 -0
- astreum-0.3.46/src/astreum/communication/processors/outgoing.py +53 -0
- astreum-0.3.46/src/astreum/communication/processors/peer.py +134 -0
- astreum-0.3.46/src/astreum/communication/setup.py +327 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/communication/util.py +14 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/node.py +99 -92
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/storage/actions/get.py +79 -48
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/storage/actions/set.py +171 -156
- astreum-0.3.46/src/astreum/storage/providers.py +24 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/storage/setup.py +23 -22
- astreum-0.3.46/src/astreum/utils/config.py +265 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/utils/logging.py +1 -1
- {astreum-0.3.14/src/astreum/consensus → astreum-0.3.46/src/astreum/validation}/__init__.py +0 -4
- astreum-0.3.46/src/astreum/validation/constants.py +2 -0
- {astreum-0.3.14/src/astreum/consensus → astreum-0.3.46/src/astreum/validation}/genesis.py +4 -6
- astreum-0.3.46/src/astreum/validation/models/block.py +544 -0
- astreum-0.3.46/src/astreum/validation/models/fork.py +511 -0
- {astreum-0.3.14/src/astreum/consensus → astreum-0.3.46/src/astreum/validation}/models/receipt.py +17 -4
- {astreum-0.3.14/src/astreum/consensus → astreum-0.3.46/src/astreum/validation}/models/transaction.py +45 -3
- astreum-0.3.46/src/astreum/validation/node.py +190 -0
- {astreum-0.3.14/src/astreum/consensus → astreum-0.3.46/src/astreum/validation}/validator.py +3 -1
- astreum-0.3.46/src/astreum/validation/workers/__init__.py +8 -0
- {astreum-0.3.14/src/astreum/consensus → astreum-0.3.46/src/astreum/validation}/workers/validation.py +361 -334
- astreum-0.3.46/src/astreum/verification/__init__.py +4 -0
- astreum-0.3.14/src/astreum/consensus/workers/discovery.py → astreum-0.3.46/src/astreum/verification/discover.py +1 -1
- astreum-0.3.46/src/astreum/verification/node.py +61 -0
- astreum-0.3.46/src/astreum/verification/worker.py +183 -0
- {astreum-0.3.14 → astreum-0.3.46/src/astreum.egg-info}/PKG-INFO +43 -9
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum.egg-info/SOURCES.txt +26 -19
- astreum-0.3.14/src/astreum/communication/handlers/handshake.py +0 -62
- astreum-0.3.14/src/astreum/communication/models/ping.py +0 -33
- astreum-0.3.14/src/astreum/communication/processors/incoming.py +0 -98
- astreum-0.3.14/src/astreum/communication/processors/outgoing.py +0 -20
- astreum-0.3.14/src/astreum/communication/processors/peer.py +0 -59
- astreum-0.3.14/src/astreum/communication/setup.py +0 -168
- astreum-0.3.14/src/astreum/consensus/models/block.py +0 -364
- astreum-0.3.14/src/astreum/consensus/models/chain.py +0 -66
- astreum-0.3.14/src/astreum/consensus/models/fork.py +0 -100
- astreum-0.3.14/src/astreum/consensus/setup.py +0 -83
- astreum-0.3.14/src/astreum/consensus/start.py +0 -67
- astreum-0.3.14/src/astreum/consensus/workers/__init__.py +0 -9
- astreum-0.3.14/src/astreum/consensus/workers/verify.py +0 -90
- astreum-0.3.14/src/astreum/utils/config.py +0 -76
- {astreum-0.3.14 → astreum-0.3.46}/LICENSE +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/setup.cfg +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/communication/handlers/__init__.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/communication/models/__init__.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/communication/models/message.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/communication/processors/__init__.py +0 -0
- {astreum-0.3.14/src/astreum/consensus/models → astreum-0.3.46/src/astreum/crypto}/__init__.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/crypto/chacha20poly1305.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/crypto/ed25519.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/crypto/quadratic_form.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/crypto/wesolowski.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/crypto/x25519.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/machine/__init__.py +0 -0
- {astreum-0.3.14/src/astreum/crypto → astreum-0.3.46/src/astreum/machine/evaluations}/__init__.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/machine/evaluations/high_evaluation.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/machine/evaluations/low_evaluation.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/machine/evaluations/script_evaluation.py +0 -0
- {astreum-0.3.14/src/astreum/machine/evaluations → astreum-0.3.46/src/astreum/machine/models}/__init__.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/machine/models/environment.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/machine/models/expression.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/machine/models/meter.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/machine/parser.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/machine/tokenizer.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/storage/__init__.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/storage/models/atom.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/storage/models/trie.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/storage/requests.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/utils/bytes.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum/utils/integer.py +0 -0
- {astreum-0.3.14/src/astreum/machine → astreum-0.3.46/src/astreum/validation}/models/__init__.py +0 -0
- {astreum-0.3.14/src/astreum/consensus → astreum-0.3.46/src/astreum/validation}/models/account.py +0 -0
- {astreum-0.3.14/src/astreum/consensus → astreum-0.3.46/src/astreum/validation}/models/accounts.py +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum.egg-info/dependency_links.txt +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum.egg-info/requires.txt +0 -0
- {astreum-0.3.14 → astreum-0.3.46}/src/astreum.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: astreum
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.46
|
|
4
4
|
Summary: Python library to interact with the Astreum blockchain and its virtual machine.
|
|
5
5
|
Author-email: "Roy R. O. Okello" <roy@stelar.xyz>
|
|
6
6
|
Project-URL: Homepage, https://github.com/astreum/lib-py
|
|
@@ -33,18 +33,29 @@ When initializing an `astreum.Node`, pass a dictionary with any of the options b
|
|
|
33
33
|
| `hot_storage_limit` | int | `1073741824` | Maximum bytes kept in the hot cache before new atoms are skipped (1 GiB). |
|
|
34
34
|
| `cold_storage_limit` | int | `10737418240` | Cold storage write threshold (10 GiB by default); set to `0` to skip the limit. |
|
|
35
35
|
| `cold_storage_path` | string | `None` | Directory where persisted atoms live; Astreum creates it on startup and skips cold storage when unset. |
|
|
36
|
-
| `
|
|
36
|
+
| `logging_retention_days` | int | `90` | Number of days to keep rotated log files (daily gzip). |
|
|
37
|
+
| `chain_id` | int | `0` | Chain identifier used for validation (0 = test, 1 = main). |
|
|
37
38
|
| `verbose` | bool | `False` | When **True**, also mirror JSON logs to stdout with a human-readable format. |
|
|
38
39
|
|
|
39
|
-
###
|
|
40
|
+
### Communication
|
|
40
41
|
|
|
41
42
|
| Parameter | Type | Default | Description |
|
|
42
43
|
| ------------------------ | ----------- | --------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
43
44
|
| `relay_secret_key` | hex string | Auto-generated | X25519 private key used for the relay route; a new keypair is created when this field is omitted. |
|
|
44
45
|
| `validation_secret_key` | hex string | `None` | Optional Ed25519 key that lets the node join the validation route; leave blank to opt out of validation. |
|
|
45
46
|
| `use_ipv6` | bool | `False` | Bind the incoming/outgoing sockets on IPv6 (the OS still listens on IPv4 if a peer speaks both). |
|
|
46
|
-
| `incoming_port` | int | `
|
|
47
|
-
| `
|
|
47
|
+
| `incoming_port` | int | `52780` | UDP port the relay binds to; pass `0` or omit to let the OS pick an ephemeral port. |
|
|
48
|
+
| `default_seed` | string | `"bootstrap.astreum.org:52780"` | Default address to ping before joining; set to `None` to disable the built-in default. |
|
|
49
|
+
| `additional_seeds` | list\[str\] | `[]` | Extra addresses appended to the bootstrap list; each must look like `host:port` or `[ipv6]:port`. |
|
|
50
|
+
| `peer_timeout` | int | `900` | Evict peers that have not been seen within this many seconds (15 minutes). |
|
|
51
|
+
| `peer_timeout_interval` | int | `10` | How often (seconds) the peer manager checks for stale peers. |
|
|
52
|
+
| `bootstrap_retry_interval` | int | `30` | How often (seconds) to retry bootstrapping when the peer list is empty. |
|
|
53
|
+
| `storage_index_interval` | int | `600` | How often (seconds) to re-advertise cold storage atoms to the closest known peer. |
|
|
54
|
+
| `cold_storage_advertise_limit` | int | `1000` | Max cold storage atoms advertised per cycle using last-modified time; `-1` unlimited, `0` disable. |
|
|
55
|
+
| `incoming_queue_size_limit` | int | `67108864` | Soft cap (bytes) for inbound queue usage tracked by `enqueue_incoming`; set to `0` to disable. |
|
|
56
|
+
| `incoming_queue_timeout` | float | `1.0` | When > 0, `enqueue_incoming` waits up to this many seconds for space before dropping the payload. |
|
|
57
|
+
| `outgoing_queue_size_limit` | int | `67108864` | Soft cap (bytes) for `enqueue_outgoing`-tracked outgoing queue usage; set to `0` to disable. |
|
|
58
|
+
| `outgoing_queue_timeout` | float | `1.0` | When > 0, `enqueue_outgoing` waits up to this many seconds for space before dropping the payload. |
|
|
48
59
|
|
|
49
60
|
> **Note**
|
|
50
61
|
> The peer‑to‑peer *route* used for object discovery is always enabled.
|
|
@@ -61,10 +72,11 @@ config = {
|
|
|
61
72
|
"hot_storage_limit": 1073741824, # cap hot cache at 1 GiB
|
|
62
73
|
"cold_storage_limit": 10737418240, # cap cold storage at 10 GiB
|
|
63
74
|
"cold_storage_path": "./data/node1",
|
|
64
|
-
"
|
|
75
|
+
"cold_storage_advertise_limit": 1000, # -1 unlimited, 0 disable, >0 limit
|
|
76
|
+
"incoming_port": 52780,
|
|
65
77
|
"use_ipv6": False,
|
|
66
|
-
"
|
|
67
|
-
|
|
78
|
+
"default_seed": None,
|
|
79
|
+
"additional_seeds": [
|
|
68
80
|
"127.0.0.1:7374"
|
|
69
81
|
]
|
|
70
82
|
}
|
|
@@ -145,7 +157,7 @@ except ParseError as e:
|
|
|
145
157
|
Every `Node` instance wires up structured logging automatically:
|
|
146
158
|
|
|
147
159
|
- Logs land in per-instance files named `node.log` under `%LOCALAPPDATA%\Astreum\lib-py\logs/<instance_id>` on Windows and `$XDG_STATE_HOME` (or `~/.local/state`)/`Astreum/lib-py/logs/<instance_id>` on other platforms. The `<instance_id>` is the first 16 hex characters of a BLAKE3 hash of the caller's file path, so running the node from different entry points keeps their logs isolated.
|
|
148
|
-
- Files rotate at midnight UTC with gzip compression (`node-YYYY-MM-DD.log.gz`) and retain 90 days by default. Override via `config["
|
|
160
|
+
- Files rotate at midnight UTC with gzip compression (`node-YYYY-MM-DD.log.gz`) and retain 90 days by default. Override via `config["logging_retention_days"]`.
|
|
149
161
|
- Each event is a single JSON line containing timestamp, level, logger, message, process/thread info, module/function, and the derived `instance_id`.
|
|
150
162
|
- Set `config["verbose"] = True` to mirror logs to stdout in a human-friendly format like `[2025-04-13-42-59] [info] Starting Astreum Node`.
|
|
151
163
|
- The very first entry emitted is the banner `Starting Astreum Node`, signalling that the logging pipeline is live before other subsystems spin up.
|
|
@@ -156,5 +168,27 @@ Every `Node` instance wires up structured logging automatically:
|
|
|
156
168
|
python3 -m venv venv
|
|
157
169
|
source venv/bin/activate
|
|
158
170
|
pip install -e .
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
for all tests
|
|
174
|
+
```
|
|
159
175
|
python3 -m unittest discover -s tests
|
|
160
176
|
```
|
|
177
|
+
|
|
178
|
+
for individual tests
|
|
179
|
+
```
|
|
180
|
+
python3 -m unittest tests.node.test_atom_get
|
|
181
|
+
python3 -m unittest tests.node.test_current_validator
|
|
182
|
+
python3 -m unittest tests.node.test_node_connection
|
|
183
|
+
python3 -m unittest tests.node.test_node_init
|
|
184
|
+
python3 -m unittest tests.node.test_node_validation
|
|
185
|
+
python3 -m unittest tests.node.config.default_seed
|
|
186
|
+
python3 -m unittest tests.node.tokenize
|
|
187
|
+
python3 -m unittest tests.node.parse
|
|
188
|
+
python3 -m unittest tests.node.function
|
|
189
|
+
python3 -m unittest tests.node.stack
|
|
190
|
+
python3 -m unittest tests.models.test_merkle
|
|
191
|
+
python3 -m unittest tests.models.test_patricia
|
|
192
|
+
python3 -m unittest tests.block.atom
|
|
193
|
+
python3 -m unittest tests.block.nonce
|
|
194
|
+
```
|
|
@@ -15,18 +15,29 @@ When initializing an `astreum.Node`, pass a dictionary with any of the options b
|
|
|
15
15
|
| `hot_storage_limit` | int | `1073741824` | Maximum bytes kept in the hot cache before new atoms are skipped (1 GiB). |
|
|
16
16
|
| `cold_storage_limit` | int | `10737418240` | Cold storage write threshold (10 GiB by default); set to `0` to skip the limit. |
|
|
17
17
|
| `cold_storage_path` | string | `None` | Directory where persisted atoms live; Astreum creates it on startup and skips cold storage when unset. |
|
|
18
|
-
| `
|
|
18
|
+
| `logging_retention_days` | int | `90` | Number of days to keep rotated log files (daily gzip). |
|
|
19
|
+
| `chain_id` | int | `0` | Chain identifier used for validation (0 = test, 1 = main). |
|
|
19
20
|
| `verbose` | bool | `False` | When **True**, also mirror JSON logs to stdout with a human-readable format. |
|
|
20
21
|
|
|
21
|
-
###
|
|
22
|
+
### Communication
|
|
22
23
|
|
|
23
24
|
| Parameter | Type | Default | Description |
|
|
24
25
|
| ------------------------ | ----------- | --------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
25
26
|
| `relay_secret_key` | hex string | Auto-generated | X25519 private key used for the relay route; a new keypair is created when this field is omitted. |
|
|
26
27
|
| `validation_secret_key` | hex string | `None` | Optional Ed25519 key that lets the node join the validation route; leave blank to opt out of validation. |
|
|
27
28
|
| `use_ipv6` | bool | `False` | Bind the incoming/outgoing sockets on IPv6 (the OS still listens on IPv4 if a peer speaks both). |
|
|
28
|
-
| `incoming_port` | int | `
|
|
29
|
-
| `
|
|
29
|
+
| `incoming_port` | int | `52780` | UDP port the relay binds to; pass `0` or omit to let the OS pick an ephemeral port. |
|
|
30
|
+
| `default_seed` | string | `"bootstrap.astreum.org:52780"` | Default address to ping before joining; set to `None` to disable the built-in default. |
|
|
31
|
+
| `additional_seeds` | list\[str\] | `[]` | Extra addresses appended to the bootstrap list; each must look like `host:port` or `[ipv6]:port`. |
|
|
32
|
+
| `peer_timeout` | int | `900` | Evict peers that have not been seen within this many seconds (15 minutes). |
|
|
33
|
+
| `peer_timeout_interval` | int | `10` | How often (seconds) the peer manager checks for stale peers. |
|
|
34
|
+
| `bootstrap_retry_interval` | int | `30` | How often (seconds) to retry bootstrapping when the peer list is empty. |
|
|
35
|
+
| `storage_index_interval` | int | `600` | How often (seconds) to re-advertise cold storage atoms to the closest known peer. |
|
|
36
|
+
| `cold_storage_advertise_limit` | int | `1000` | Max cold storage atoms advertised per cycle using last-modified time; `-1` unlimited, `0` disable. |
|
|
37
|
+
| `incoming_queue_size_limit` | int | `67108864` | Soft cap (bytes) for inbound queue usage tracked by `enqueue_incoming`; set to `0` to disable. |
|
|
38
|
+
| `incoming_queue_timeout` | float | `1.0` | When > 0, `enqueue_incoming` waits up to this many seconds for space before dropping the payload. |
|
|
39
|
+
| `outgoing_queue_size_limit` | int | `67108864` | Soft cap (bytes) for `enqueue_outgoing`-tracked outgoing queue usage; set to `0` to disable. |
|
|
40
|
+
| `outgoing_queue_timeout` | float | `1.0` | When > 0, `enqueue_outgoing` waits up to this many seconds for space before dropping the payload. |
|
|
30
41
|
|
|
31
42
|
> **Note**
|
|
32
43
|
> The peer‑to‑peer *route* used for object discovery is always enabled.
|
|
@@ -43,10 +54,11 @@ config = {
|
|
|
43
54
|
"hot_storage_limit": 1073741824, # cap hot cache at 1 GiB
|
|
44
55
|
"cold_storage_limit": 10737418240, # cap cold storage at 10 GiB
|
|
45
56
|
"cold_storage_path": "./data/node1",
|
|
46
|
-
"
|
|
57
|
+
"cold_storage_advertise_limit": 1000, # -1 unlimited, 0 disable, >0 limit
|
|
58
|
+
"incoming_port": 52780,
|
|
47
59
|
"use_ipv6": False,
|
|
48
|
-
"
|
|
49
|
-
|
|
60
|
+
"default_seed": None,
|
|
61
|
+
"additional_seeds": [
|
|
50
62
|
"127.0.0.1:7374"
|
|
51
63
|
]
|
|
52
64
|
}
|
|
@@ -127,7 +139,7 @@ except ParseError as e:
|
|
|
127
139
|
Every `Node` instance wires up structured logging automatically:
|
|
128
140
|
|
|
129
141
|
- Logs land in per-instance files named `node.log` under `%LOCALAPPDATA%\Astreum\lib-py\logs/<instance_id>` on Windows and `$XDG_STATE_HOME` (or `~/.local/state`)/`Astreum/lib-py/logs/<instance_id>` on other platforms. The `<instance_id>` is the first 16 hex characters of a BLAKE3 hash of the caller's file path, so running the node from different entry points keeps their logs isolated.
|
|
130
|
-
- Files rotate at midnight UTC with gzip compression (`node-YYYY-MM-DD.log.gz`) and retain 90 days by default. Override via `config["
|
|
142
|
+
- Files rotate at midnight UTC with gzip compression (`node-YYYY-MM-DD.log.gz`) and retain 90 days by default. Override via `config["logging_retention_days"]`.
|
|
131
143
|
- Each event is a single JSON line containing timestamp, level, logger, message, process/thread info, module/function, and the derived `instance_id`.
|
|
132
144
|
- Set `config["verbose"] = True` to mirror logs to stdout in a human-friendly format like `[2025-04-13-42-59] [info] Starting Astreum Node`.
|
|
133
145
|
- The very first entry emitted is the banner `Starting Astreum Node`, signalling that the logging pipeline is live before other subsystems spin up.
|
|
@@ -138,5 +150,27 @@ Every `Node` instance wires up structured logging automatically:
|
|
|
138
150
|
python3 -m venv venv
|
|
139
151
|
source venv/bin/activate
|
|
140
152
|
pip install -e .
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
for all tests
|
|
156
|
+
```
|
|
141
157
|
python3 -m unittest discover -s tests
|
|
142
158
|
```
|
|
159
|
+
|
|
160
|
+
for individual tests
|
|
161
|
+
```
|
|
162
|
+
python3 -m unittest tests.node.test_atom_get
|
|
163
|
+
python3 -m unittest tests.node.test_current_validator
|
|
164
|
+
python3 -m unittest tests.node.test_node_connection
|
|
165
|
+
python3 -m unittest tests.node.test_node_init
|
|
166
|
+
python3 -m unittest tests.node.test_node_validation
|
|
167
|
+
python3 -m unittest tests.node.config.default_seed
|
|
168
|
+
python3 -m unittest tests.node.tokenize
|
|
169
|
+
python3 -m unittest tests.node.parse
|
|
170
|
+
python3 -m unittest tests.node.function
|
|
171
|
+
python3 -m unittest tests.node.stack
|
|
172
|
+
python3 -m unittest tests.models.test_merkle
|
|
173
|
+
python3 -m unittest tests.models.test_patricia
|
|
174
|
+
python3 -m unittest tests.block.atom
|
|
175
|
+
python3 -m unittest tests.block.nonce
|
|
176
|
+
```
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
from astreum.
|
|
2
|
+
from astreum.validation import Account, Accounts, Block, Fork, Receipt, Transaction
|
|
3
3
|
from astreum.machine import Env, Expr, parse, tokenize
|
|
4
4
|
from astreum.node import Node
|
|
5
5
|
|
|
@@ -9,7 +9,6 @@ __all__: list[str] = [
|
|
|
9
9
|
"Env",
|
|
10
10
|
"Expr",
|
|
11
11
|
"Block",
|
|
12
|
-
"Chain",
|
|
13
12
|
"Fork",
|
|
14
13
|
"Receipt",
|
|
15
14
|
"Transaction",
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
from .models.message import Message
|
|
2
|
-
from .models.peer import Peer
|
|
3
|
-
from .models.route import Route
|
|
4
|
-
from .
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
|
|
1
|
+
from .models.message import Message
|
|
2
|
+
from .models.peer import Peer
|
|
3
|
+
from .models.route import Route
|
|
4
|
+
from .incoming_queue import enqueue_incoming
|
|
5
|
+
from .outgoing_queue import enqueue_outgoing
|
|
6
|
+
from .setup import communication_setup
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"Message",
|
|
10
|
+
"Peer",
|
|
11
|
+
"Route",
|
|
12
|
+
"enqueue_incoming",
|
|
13
|
+
"enqueue_outgoing",
|
|
14
|
+
"communication_setup",
|
|
15
|
+
]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from .. import Node
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def message_difficulty(node: "Node") -> int:
|
|
10
|
+
"""Compute current message difficulty based on incoming queue pressure."""
|
|
11
|
+
size = node.incoming_queue_size
|
|
12
|
+
limit = node.incoming_queue_size_limit
|
|
13
|
+
|
|
14
|
+
if limit <= 0:
|
|
15
|
+
return 1
|
|
16
|
+
|
|
17
|
+
pressure = size / limit
|
|
18
|
+
if pressure < 0.70:
|
|
19
|
+
value = 1
|
|
20
|
+
elif pressure < 0.75:
|
|
21
|
+
value = 3
|
|
22
|
+
elif pressure < 0.80:
|
|
23
|
+
value = 5
|
|
24
|
+
elif pressure < 0.85:
|
|
25
|
+
value = 8
|
|
26
|
+
elif pressure < 0.90:
|
|
27
|
+
value = 12
|
|
28
|
+
elif pressure < 0.93:
|
|
29
|
+
value = 16
|
|
30
|
+
elif pressure < 0.95:
|
|
31
|
+
value = 19
|
|
32
|
+
elif pressure < 0.97:
|
|
33
|
+
value = 22
|
|
34
|
+
elif pressure < 0.98:
|
|
35
|
+
value = 24
|
|
36
|
+
else:
|
|
37
|
+
value = 26
|
|
38
|
+
|
|
39
|
+
return max(1, min(255, value))
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Helpers related to disconnecting communication components."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from astreum.node import Node
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
_SOCKET_ATTRS: tuple[str, ...] = ("incoming_socket", "outgoing_socket")
|
|
12
|
+
_THREAD_ATTRS: tuple[str, ...] = (
|
|
13
|
+
"incoming_populate_thread",
|
|
14
|
+
"incoming_process_thread",
|
|
15
|
+
"outgoing_thread",
|
|
16
|
+
"peer_manager_thread",
|
|
17
|
+
"latest_block_discovery_thread",
|
|
18
|
+
"verify_thread",
|
|
19
|
+
"consensus_validation_thread",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _set_event(node: "Node", attr_name: str) -> None:
|
|
24
|
+
event = getattr(node, attr_name, None)
|
|
25
|
+
if event is not None:
|
|
26
|
+
event.set()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _close_socket(node: "Node", attr_name: str) -> None:
|
|
30
|
+
sock = getattr(node, attr_name, None)
|
|
31
|
+
if sock is None:
|
|
32
|
+
return
|
|
33
|
+
try:
|
|
34
|
+
sock.close()
|
|
35
|
+
except Exception as exc: # pragma: no cover - defensive logging path
|
|
36
|
+
node.logger.warning("Error closing %s: %s", attr_name, exc)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def disconnect_node(node: "Node") -> None:
|
|
40
|
+
"""Gracefully stop worker threads and close communication sockets."""
|
|
41
|
+
node.logger.info("Disconnecting Astreum Node")
|
|
42
|
+
|
|
43
|
+
_set_event(node, "communication_stop_event")
|
|
44
|
+
_set_event(node, "_validation_stop_event")
|
|
45
|
+
_set_event(node, "_verify_stop_event")
|
|
46
|
+
|
|
47
|
+
for sock_name in _SOCKET_ATTRS:
|
|
48
|
+
_close_socket(node, sock_name)
|
|
49
|
+
|
|
50
|
+
for thread_name in _THREAD_ATTRS:
|
|
51
|
+
thread = getattr(node, thread_name, None)
|
|
52
|
+
if thread is None or not thread.is_alive():
|
|
53
|
+
continue
|
|
54
|
+
thread.join(timeout=1.0)
|
|
55
|
+
|
|
56
|
+
node.is_connected = False
|
|
57
|
+
node.logger.info("Node disconnected")
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Sequence
|
|
4
|
+
|
|
5
|
+
from cryptography.hazmat.primitives import serialization
|
|
6
|
+
from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PublicKey
|
|
7
|
+
|
|
8
|
+
from ..outgoing_queue import enqueue_outgoing
|
|
9
|
+
from ..models.peer import Peer
|
|
10
|
+
from ..models.message import Message, MessageTopic
|
|
11
|
+
from ..models.ping import Ping
|
|
12
|
+
from ..difficulty import message_difficulty
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from .... import Node
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def handle_handshake(node: "Node", addr: Sequence[object], message: Message) -> bool:
|
|
19
|
+
"""Handle incoming handshake messages.
|
|
20
|
+
|
|
21
|
+
Returns True if the outer loop should `continue`, False otherwise.
|
|
22
|
+
"""
|
|
23
|
+
def _queue_handshake_ping(peer: Peer, peer_address: tuple[str, int]) -> None:
|
|
24
|
+
latest_block = getattr(node, "latest_block_hash", None)
|
|
25
|
+
if not isinstance(latest_block, (bytes, bytearray)) or len(latest_block) != 32:
|
|
26
|
+
latest_block = None
|
|
27
|
+
try:
|
|
28
|
+
ping_payload = Ping(
|
|
29
|
+
is_validator=bool(getattr(node, "validation_public_key", None)),
|
|
30
|
+
difficulty=message_difficulty(node),
|
|
31
|
+
latest_block=latest_block,
|
|
32
|
+
).to_bytes()
|
|
33
|
+
ping_msg = Message(
|
|
34
|
+
topic=MessageTopic.PING,
|
|
35
|
+
content=ping_payload,
|
|
36
|
+
sender=node.relay_public_key,
|
|
37
|
+
)
|
|
38
|
+
ping_msg.encrypt(peer.shared_key_bytes)
|
|
39
|
+
enqueue_outgoing(
|
|
40
|
+
node,
|
|
41
|
+
peer_address,
|
|
42
|
+
message=ping_msg,
|
|
43
|
+
difficulty=peer.difficulty,
|
|
44
|
+
)
|
|
45
|
+
except Exception as exc:
|
|
46
|
+
node.logger.debug(
|
|
47
|
+
"Failed sending handshake ping to %s:%s: %s",
|
|
48
|
+
peer_address[0],
|
|
49
|
+
peer_address[1],
|
|
50
|
+
exc,
|
|
51
|
+
)
|
|
52
|
+
sender_public_key_bytes = message.sender_bytes
|
|
53
|
+
try:
|
|
54
|
+
sender_key = X25519PublicKey.from_public_bytes(sender_public_key_bytes)
|
|
55
|
+
except Exception as exc:
|
|
56
|
+
node.logger.warning("Error extracting sender key bytes: %s", exc)
|
|
57
|
+
return True
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
host = addr[0]
|
|
61
|
+
port = int.from_bytes(message.content[:2], "big", signed=False)
|
|
62
|
+
except Exception:
|
|
63
|
+
return True
|
|
64
|
+
peer_address = (host, port)
|
|
65
|
+
default_seed_ips = getattr(node, "default_seed_ips", None)
|
|
66
|
+
is_default_seed = bool(default_seed_ips) and host in default_seed_ips
|
|
67
|
+
|
|
68
|
+
existing_peer = node.get_peer(sender_public_key_bytes)
|
|
69
|
+
if existing_peer is not None:
|
|
70
|
+
existing_peer.address = peer_address
|
|
71
|
+
existing_peer.is_default_seed = is_default_seed
|
|
72
|
+
_queue_handshake_ping(existing_peer, peer_address)
|
|
73
|
+
return False
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
peer = Peer(
|
|
77
|
+
node_secret_key=node.relay_secret_key,
|
|
78
|
+
peer_public_key=sender_key,
|
|
79
|
+
address=peer_address,
|
|
80
|
+
is_default_seed=is_default_seed,
|
|
81
|
+
)
|
|
82
|
+
except Exception:
|
|
83
|
+
return True
|
|
84
|
+
|
|
85
|
+
node.add_peer(sender_public_key_bytes, peer)
|
|
86
|
+
node.peer_route.add_peer(sender_public_key_bytes, peer)
|
|
87
|
+
|
|
88
|
+
node.logger.info(
|
|
89
|
+
"Handshake accepted from %s:%s; peer added",
|
|
90
|
+
peer_address[0],
|
|
91
|
+
peer_address[1],
|
|
92
|
+
)
|
|
93
|
+
response = Message(
|
|
94
|
+
handshake=True,
|
|
95
|
+
sender=node.relay_public_key,
|
|
96
|
+
content=int(node.config["incoming_port"]).to_bytes(2, "big", signed=False),
|
|
97
|
+
)
|
|
98
|
+
enqueue_outgoing(
|
|
99
|
+
node,
|
|
100
|
+
peer_address,
|
|
101
|
+
message=response,
|
|
102
|
+
difficulty=peer.difficulty,
|
|
103
|
+
)
|
|
104
|
+
_queue_handshake_ping(peer, peer_address)
|
|
105
|
+
return True
|