astreum 0.3.10__tar.gz → 0.3.14__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.10/src/astreum.egg-info → astreum-0.3.14}/PKG-INFO +1 -1
- {astreum-0.3.10 → astreum-0.3.14}/pyproject.toml +1 -1
- astreum-0.3.14/src/astreum/communication/handlers/handshake.py +62 -0
- astreum-0.3.14/src/astreum/communication/processors/peer.py +59 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/communication/setup.py +4 -2
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/consensus/validator.py +15 -8
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/consensus/workers/validation.py +29 -2
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/node.py +2 -1
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/utils/config.py +29 -1
- {astreum-0.3.10 → astreum-0.3.14/src/astreum.egg-info}/PKG-INFO +1 -1
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum.egg-info/SOURCES.txt +1 -0
- astreum-0.3.10/src/astreum/communication/handlers/handshake.py +0 -89
- {astreum-0.3.10 → astreum-0.3.14}/LICENSE +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/README.md +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/setup.cfg +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/__init__.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/communication/__init__.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/communication/handlers/__init__.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/communication/handlers/object_request.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/communication/handlers/object_response.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/communication/handlers/ping.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/communication/handlers/route_request.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/communication/handlers/route_response.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/communication/models/__init__.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/communication/models/message.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/communication/models/peer.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/communication/models/ping.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/communication/models/route.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/communication/processors/__init__.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/communication/processors/incoming.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/communication/processors/outgoing.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/communication/start.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/communication/util.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/consensus/__init__.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/consensus/genesis.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/consensus/models/__init__.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/consensus/models/account.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/consensus/models/accounts.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/consensus/models/block.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/consensus/models/chain.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/consensus/models/fork.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/consensus/models/receipt.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/consensus/models/transaction.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/consensus/setup.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/consensus/start.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/consensus/workers/__init__.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/consensus/workers/discovery.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/consensus/workers/verify.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/crypto/__init__.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/crypto/chacha20poly1305.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/crypto/ed25519.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/crypto/quadratic_form.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/crypto/wesolowski.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/crypto/x25519.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/machine/__init__.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/machine/evaluations/__init__.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/machine/evaluations/high_evaluation.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/machine/evaluations/low_evaluation.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/machine/evaluations/script_evaluation.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/machine/models/__init__.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/machine/models/environment.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/machine/models/expression.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/machine/models/meter.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/machine/parser.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/machine/tokenizer.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/storage/__init__.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/storage/actions/get.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/storage/actions/set.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/storage/models/atom.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/storage/models/trie.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/storage/requests.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/storage/setup.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/utils/bytes.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/utils/integer.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum/utils/logging.py +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum.egg-info/dependency_links.txt +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/src/astreum.egg-info/requires.txt +0 -0
- {astreum-0.3.10 → astreum-0.3.14}/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.14
|
|
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
|
|
@@ -0,0 +1,62 @@
|
|
|
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 ..models.peer import Peer
|
|
9
|
+
from ..models.message import Message
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from .... import Node
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def handle_handshake(node: "Node", addr: Sequence[object], message: Message) -> bool:
|
|
16
|
+
"""Handle incoming handshake messages.
|
|
17
|
+
|
|
18
|
+
Returns True if the outer loop should `continue`, False otherwise.
|
|
19
|
+
"""
|
|
20
|
+
sender_public_key_bytes = message.sender_bytes
|
|
21
|
+
try:
|
|
22
|
+
sender_key = X25519PublicKey.from_public_bytes(sender_public_key_bytes)
|
|
23
|
+
except Exception as exc:
|
|
24
|
+
node.logger.warning("Error extracting sender key bytes: %s", exc)
|
|
25
|
+
return True
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
host = addr[0]
|
|
29
|
+
port = int.from_bytes(message.content[:2], "big", signed=False)
|
|
30
|
+
except Exception:
|
|
31
|
+
return True
|
|
32
|
+
peer_address = (host, port)
|
|
33
|
+
|
|
34
|
+
existing_peer = node.get_peer(sender_public_key_bytes)
|
|
35
|
+
if existing_peer is not None:
|
|
36
|
+
existing_peer.address = peer_address
|
|
37
|
+
return False
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
peer = Peer(
|
|
41
|
+
node_secret_key=node.relay_secret_key,
|
|
42
|
+
peer_public_key=sender_key,
|
|
43
|
+
address=peer_address,
|
|
44
|
+
)
|
|
45
|
+
except Exception:
|
|
46
|
+
return True
|
|
47
|
+
|
|
48
|
+
node.add_peer(sender_public_key_bytes, peer)
|
|
49
|
+
node.peer_route.add_peer(sender_public_key_bytes, peer)
|
|
50
|
+
|
|
51
|
+
node.logger.info(
|
|
52
|
+
"Handshake accepted from %s:%s; peer added",
|
|
53
|
+
peer_address[0],
|
|
54
|
+
peer_address[1],
|
|
55
|
+
)
|
|
56
|
+
response = Message(
|
|
57
|
+
handshake=True,
|
|
58
|
+
sender=node.relay_public_key,
|
|
59
|
+
content=int(node.config["incoming_port"]).to_bytes(2, "big", signed=False),
|
|
60
|
+
)
|
|
61
|
+
node.outgoing_queue.put((response.to_bytes(), peer_address))
|
|
62
|
+
return True
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
from datetime import datetime, timedelta, timezone
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from .. import Node
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def manage_peer(node: "Node") -> None:
|
|
12
|
+
"""Continuously evict peers whose timestamps exceed the configured timeout."""
|
|
13
|
+
node.logger.info(
|
|
14
|
+
"Peer manager started (timeout=%3ds, interval=%3ds)",
|
|
15
|
+
node.config["peer_timeout"],
|
|
16
|
+
node.config["peer_timeout_interval"],
|
|
17
|
+
)
|
|
18
|
+
while True:
|
|
19
|
+
timeout_seconds = node.config["peer_timeout"]
|
|
20
|
+
interval_seconds = node.config["peer_timeout_interval"]
|
|
21
|
+
try:
|
|
22
|
+
peers = getattr(node, "peers", None)
|
|
23
|
+
peer_route = getattr(node, "peer_route", None)
|
|
24
|
+
if not isinstance(peers, dict) or peer_route is None:
|
|
25
|
+
time.sleep(interval_seconds)
|
|
26
|
+
continue
|
|
27
|
+
|
|
28
|
+
cutoff = datetime.now(timezone.utc) - timedelta(seconds=timeout_seconds)
|
|
29
|
+
stale_keys = []
|
|
30
|
+
with node.peers_lock:
|
|
31
|
+
for peer_key, peer in list(peers.items()):
|
|
32
|
+
if peer.timestamp < cutoff:
|
|
33
|
+
stale_keys.append(peer_key)
|
|
34
|
+
|
|
35
|
+
removed_count = 0
|
|
36
|
+
for peer_key in stale_keys:
|
|
37
|
+
removed = node.remove_peer(peer_key)
|
|
38
|
+
if removed is None:
|
|
39
|
+
continue
|
|
40
|
+
removed_count += 1
|
|
41
|
+
try:
|
|
42
|
+
peer_route.remove_peer(peer_key)
|
|
43
|
+
except Exception:
|
|
44
|
+
node.logger.debug(
|
|
45
|
+
"Unable to remove peer %s from route",
|
|
46
|
+
peer_key.hex(),
|
|
47
|
+
)
|
|
48
|
+
node.logger.debug(
|
|
49
|
+
"Evicted stale peer %s last seen at %s",
|
|
50
|
+
peer_key.hex(),
|
|
51
|
+
getattr(removed, "timestamp", None),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
if removed_count:
|
|
55
|
+
node.logger.info("Peer manager removed %s stale peer(s)", removed_count)
|
|
56
|
+
except Exception:
|
|
57
|
+
node.logger.exception("Peer manager iteration failed")
|
|
58
|
+
|
|
59
|
+
time.sleep(interval_seconds)
|
|
@@ -18,6 +18,7 @@ from .processors.incoming import (
|
|
|
18
18
|
populate_incoming_messages,
|
|
19
19
|
)
|
|
20
20
|
from .processors.outgoing import process_outgoing_messages
|
|
21
|
+
from .processors.peer import manage_peer
|
|
21
22
|
from .util import address_str_to_host_and_port
|
|
22
23
|
from ..utils.bytes import hex_to_bytes
|
|
23
24
|
|
|
@@ -122,13 +123,14 @@ def communication_setup(node: "Node", config: dict):
|
|
|
122
123
|
# other workers & maps
|
|
123
124
|
# track atom requests we initiated; guarded by atom_requests_lock on the node
|
|
124
125
|
node.peer_manager_thread = threading.Thread(
|
|
125
|
-
target=
|
|
126
|
+
target=manage_peer,
|
|
127
|
+
args=(node,),
|
|
126
128
|
daemon=True
|
|
127
129
|
)
|
|
128
130
|
node.peer_manager_thread.start()
|
|
129
131
|
|
|
130
132
|
with node.peers_lock:
|
|
131
|
-
node.peers
|
|
133
|
+
node.peers = {} # Dict[bytes,Peer]
|
|
132
134
|
|
|
133
135
|
latest_block_hex = config.get("latest_block_hash")
|
|
134
136
|
if latest_block_hex:
|
|
@@ -11,15 +11,19 @@ from ..storage.models.atom import ZERO32
|
|
|
11
11
|
from ..utils.integer import bytes_to_int, int_to_bytes
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
SLOT_DURATION_SECONDS = 2
|
|
15
|
+
|
|
16
|
+
|
|
14
17
|
def current_validator(
|
|
15
18
|
node: Any,
|
|
16
19
|
block_hash: bytes,
|
|
17
20
|
target_time: Optional[int] = None,
|
|
18
21
|
) -> Tuple[bytes, Accounts]:
|
|
19
22
|
"""
|
|
20
|
-
Determine the validator for the requested target_time, halving stakes
|
|
21
|
-
between the referenced block and the target time.
|
|
22
|
-
the updated accounts snapshot reflecting stake and
|
|
23
|
+
Determine the validator for the requested target_time, halving stakes once per
|
|
24
|
+
slot (currently 2 seconds) between the referenced block and the target time.
|
|
25
|
+
Returns the validator key and the updated accounts snapshot reflecting stake and
|
|
26
|
+
balance adjustments.
|
|
23
27
|
"""
|
|
24
28
|
|
|
25
29
|
block = Block.from_atom(node, block_hash)
|
|
@@ -86,10 +90,13 @@ def current_validator(
|
|
|
86
90
|
accounts.set_account(validator_key, validator_account)
|
|
87
91
|
accounts.set_account(TREASURY_ADDRESS, treasury_account)
|
|
88
92
|
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
delta = target_timestamp - block_timestamp
|
|
94
|
+
slots_to_process = max(1, (delta + SLOT_DURATION_SECONDS - 1) // SLOT_DURATION_SECONDS)
|
|
95
|
+
|
|
96
|
+
selected_validator = pick_validator()
|
|
97
|
+
halve_stake(selected_validator)
|
|
98
|
+
for _ in range(1, slots_to_process):
|
|
91
99
|
selected_validator = pick_validator()
|
|
92
100
|
halve_stake(selected_validator)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
iteration_target += 1
|
|
101
|
+
|
|
102
|
+
return selected_validator, accounts
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import math
|
|
3
4
|
import time
|
|
4
5
|
from queue import Empty
|
|
5
6
|
from typing import Any, Callable
|
|
@@ -203,7 +204,9 @@ def make_validation_worker(
|
|
|
203
204
|
|
|
204
205
|
now = time.time()
|
|
205
206
|
min_allowed = new_block.previous_block.timestamp + 1
|
|
206
|
-
|
|
207
|
+
nonce_time_seconds = node.nonce_time_ms / 1000.0
|
|
208
|
+
expected_blocktime = now + nonce_time_seconds
|
|
209
|
+
new_block.timestamp = max(int(math.ceil(expected_blocktime)), min_allowed)
|
|
207
210
|
|
|
208
211
|
new_block.delay_difficulty = Block.calculate_delay_difficulty(
|
|
209
212
|
previous_timestamp=previous_block.timestamp,
|
|
@@ -212,7 +215,10 @@ def make_validation_worker(
|
|
|
212
215
|
)
|
|
213
216
|
|
|
214
217
|
try:
|
|
218
|
+
nonce_started = time.perf_counter()
|
|
215
219
|
new_block.generate_nonce(difficulty=previous_block.delay_difficulty)
|
|
220
|
+
elapsed_ms = int((time.perf_counter() - nonce_started) * 1000)
|
|
221
|
+
setattr(node, "nonce_time_ms", elapsed_ms)
|
|
216
222
|
node.logger.debug(
|
|
217
223
|
"Found nonce %s for block #%s at difficulty %s",
|
|
218
224
|
new_block.nonce,
|
|
@@ -223,18 +229,39 @@ def make_validation_worker(
|
|
|
223
229
|
node.logger.exception("Failed while searching for block nonce")
|
|
224
230
|
time.sleep(0.5)
|
|
225
231
|
continue
|
|
232
|
+
|
|
233
|
+
# wait until the block timestamp is reached before propagating
|
|
234
|
+
now = time.time()
|
|
235
|
+
if now > new_block.timestamp:
|
|
236
|
+
node.logger.warning(
|
|
237
|
+
"Skipping block #%s propagation; timestamp %s already elapsed (now=%s)",
|
|
238
|
+
new_block.number,
|
|
239
|
+
new_block.timestamp,
|
|
240
|
+
now,
|
|
241
|
+
)
|
|
242
|
+
continue
|
|
226
243
|
|
|
244
|
+
spread_delay = new_block.timestamp - now
|
|
245
|
+
if spread_delay > 0:
|
|
246
|
+
node.logger.debug(
|
|
247
|
+
"Delaying distribution for %.3fs to reach block timestamp %s",
|
|
248
|
+
spread_delay,
|
|
249
|
+
new_block.timestamp,
|
|
250
|
+
)
|
|
251
|
+
time.sleep(spread_delay)
|
|
252
|
+
|
|
227
253
|
# atomize block
|
|
228
254
|
new_block_hash, new_block_atoms = new_block.to_atom()
|
|
229
255
|
# put as own latest block hash
|
|
230
256
|
node.latest_block_hash = new_block_hash
|
|
231
257
|
node.latest_block = new_block
|
|
232
258
|
node.logger.info(
|
|
233
|
-
"
|
|
259
|
+
"Created block #%s with hash %s (%d atoms)",
|
|
234
260
|
new_block.number,
|
|
235
261
|
new_block_hash.hex(),
|
|
236
262
|
len(new_block_atoms),
|
|
237
263
|
)
|
|
264
|
+
|
|
238
265
|
|
|
239
266
|
# ping peers in the validation route to update their records
|
|
240
267
|
if node.validation_route and node.outgoing_queue and node.peers:
|
|
@@ -56,7 +56,8 @@ class Node:
|
|
|
56
56
|
self.is_connected = False
|
|
57
57
|
self.latest_block_hash = None
|
|
58
58
|
self.latest_block = None
|
|
59
|
-
|
|
59
|
+
self.nonce_time_ms = 0 # rolling measurement of last nonce search duration
|
|
60
|
+
|
|
60
61
|
connect = connect_to_network_and_verify
|
|
61
62
|
validate = process_blocks_and_transactions
|
|
62
63
|
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from typing import Dict
|
|
4
4
|
|
|
5
5
|
DEFAULT_HOT_STORAGE_LIMIT = 1 << 30 # 1 GiB
|
|
6
6
|
DEFAULT_COLD_STORAGE_LIMIT = 10 << 30 # 10 GiB
|
|
7
|
+
DEFAULT_PEER_TIMEOUT_SECONDS = 15 * 60 # 15 minutes
|
|
8
|
+
DEFAULT_PEER_TIMEOUT_INTERVAL_SECONDS = 10 # 10 seconds
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
def config_setup(config: Dict = {}):
|
|
@@ -45,4 +47,30 @@ def config_setup(config: Dict = {}):
|
|
|
45
47
|
else:
|
|
46
48
|
config["cold_storage_path"] = None
|
|
47
49
|
|
|
50
|
+
peer_timeout_raw = config.get("peer_timeout", DEFAULT_PEER_TIMEOUT_SECONDS)
|
|
51
|
+
try:
|
|
52
|
+
peer_timeout = int(peer_timeout_raw)
|
|
53
|
+
except (TypeError, ValueError) as exc:
|
|
54
|
+
raise ValueError(
|
|
55
|
+
f"peer_timeout must be an integer: {peer_timeout_raw!r}"
|
|
56
|
+
) from exc
|
|
57
|
+
|
|
58
|
+
if peer_timeout <= 0:
|
|
59
|
+
raise ValueError("peer_timeout must be a positive integer")
|
|
60
|
+
|
|
61
|
+
config["peer_timeout"] = peer_timeout
|
|
62
|
+
|
|
63
|
+
interval_raw = config.get("peer_timeout_interval", DEFAULT_PEER_TIMEOUT_INTERVAL_SECONDS)
|
|
64
|
+
try:
|
|
65
|
+
interval = int(interval_raw)
|
|
66
|
+
except (TypeError, ValueError) as exc:
|
|
67
|
+
raise ValueError(
|
|
68
|
+
f"peer_timeout_interval must be an integer: {interval_raw!r}"
|
|
69
|
+
) from exc
|
|
70
|
+
|
|
71
|
+
if interval <= 0:
|
|
72
|
+
raise ValueError("peer_timeout_interval must be a positive integer")
|
|
73
|
+
|
|
74
|
+
config["peer_timeout_interval"] = interval
|
|
75
|
+
|
|
48
76
|
return config
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: astreum
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.14
|
|
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
|
|
@@ -27,6 +27,7 @@ src/astreum/communication/models/route.py
|
|
|
27
27
|
src/astreum/communication/processors/__init__.py
|
|
28
28
|
src/astreum/communication/processors/incoming.py
|
|
29
29
|
src/astreum/communication/processors/outgoing.py
|
|
30
|
+
src/astreum/communication/processors/peer.py
|
|
30
31
|
src/astreum/consensus/__init__.py
|
|
31
32
|
src/astreum/consensus/genesis.py
|
|
32
33
|
src/astreum/consensus/setup.py
|
|
@@ -1,89 +0,0 @@
|
|
|
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 ..models.peer import Peer
|
|
9
|
-
from ..models.message import Message
|
|
10
|
-
|
|
11
|
-
if TYPE_CHECKING:
|
|
12
|
-
from .... import Node
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def handle_handshake(node: "Node", addr: Sequence[object], message: Message) -> bool:
|
|
16
|
-
"""Handle incoming handshake messages.
|
|
17
|
-
|
|
18
|
-
Returns True if the outer loop should `continue`, False otherwise.
|
|
19
|
-
"""
|
|
20
|
-
sender_public_key_bytes = message.sender_bytes
|
|
21
|
-
try:
|
|
22
|
-
sender_key = X25519PublicKey.from_public_bytes(sender_public_key_bytes)
|
|
23
|
-
except Exception as exc:
|
|
24
|
-
node.logger.warning("Error extracting sender key bytes: %s", exc)
|
|
25
|
-
return True
|
|
26
|
-
|
|
27
|
-
try:
|
|
28
|
-
host = addr[0]
|
|
29
|
-
port = int.from_bytes(message.content[:2], "big", signed=False)
|
|
30
|
-
except Exception:
|
|
31
|
-
return True
|
|
32
|
-
peer_address = (host, port)
|
|
33
|
-
|
|
34
|
-
old_key_bytes = node.addresses.get(peer_address)
|
|
35
|
-
node.addresses[peer_address] = sender_public_key_bytes
|
|
36
|
-
|
|
37
|
-
if old_key_bytes is None:
|
|
38
|
-
try:
|
|
39
|
-
peer = Peer(
|
|
40
|
-
node_secret_key=node.relay_secret_key,
|
|
41
|
-
peer_public_key=sender_key,
|
|
42
|
-
address=peer_address,
|
|
43
|
-
)
|
|
44
|
-
except Exception:
|
|
45
|
-
return True
|
|
46
|
-
|
|
47
|
-
node.add_peer(sender_public_key_bytes, peer)
|
|
48
|
-
node.peer_route.add_peer(sender_public_key_bytes, peer)
|
|
49
|
-
|
|
50
|
-
node.logger.info(
|
|
51
|
-
"Handshake accepted from %s:%s; peer added",
|
|
52
|
-
peer_address[0],
|
|
53
|
-
peer_address[1],
|
|
54
|
-
)
|
|
55
|
-
response = Message(
|
|
56
|
-
handshake=True,
|
|
57
|
-
sender=node.relay_public_key,
|
|
58
|
-
content=int(node.config["incoming_port"]).to_bytes(2, "big", signed=False),
|
|
59
|
-
)
|
|
60
|
-
node.outgoing_queue.put((response.to_bytes(), peer_address))
|
|
61
|
-
return True
|
|
62
|
-
|
|
63
|
-
if old_key_bytes == sender_public_key_bytes:
|
|
64
|
-
peer = node.get_peer(sender_public_key_bytes)
|
|
65
|
-
if peer is not None:
|
|
66
|
-
peer.address = peer_address
|
|
67
|
-
return False
|
|
68
|
-
|
|
69
|
-
try:
|
|
70
|
-
node.peer_route.remove_peer(old_key_bytes)
|
|
71
|
-
except Exception:
|
|
72
|
-
pass
|
|
73
|
-
try:
|
|
74
|
-
peer = Peer(
|
|
75
|
-
node_secret_key=node.relay_secret_key,
|
|
76
|
-
peer_public_key=sender_key,
|
|
77
|
-
address=peer_address,
|
|
78
|
-
)
|
|
79
|
-
except Exception:
|
|
80
|
-
return True
|
|
81
|
-
|
|
82
|
-
node.replace_peer(old_key_bytes, sender_public_key_bytes, peer)
|
|
83
|
-
node.peer_route.add_peer(sender_public_key_bytes, peer)
|
|
84
|
-
node.logger.info(
|
|
85
|
-
"Peer at %s:%s replaced due to key change",
|
|
86
|
-
peer_address[0],
|
|
87
|
-
peer_address[1],
|
|
88
|
-
)
|
|
89
|
-
return False
|
|
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
|
|
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
|
|
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
|
|
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
|