astreum 0.3.16__py3-none-any.whl → 0.3.46__py3-none-any.whl

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 (60) hide show
  1. astreum/__init__.py +1 -2
  2. astreum/communication/__init__.py +15 -11
  3. astreum/communication/difficulty.py +39 -0
  4. astreum/communication/disconnect.py +57 -0
  5. astreum/communication/handlers/handshake.py +105 -62
  6. astreum/communication/handlers/object_request.py +179 -149
  7. astreum/communication/handlers/object_response.py +7 -1
  8. astreum/communication/handlers/ping.py +9 -0
  9. astreum/communication/handlers/route_request.py +7 -1
  10. astreum/communication/handlers/route_response.py +7 -1
  11. astreum/communication/incoming_queue.py +96 -0
  12. astreum/communication/message_pow.py +36 -0
  13. astreum/communication/models/peer.py +4 -0
  14. astreum/communication/models/ping.py +27 -6
  15. astreum/communication/models/route.py +4 -0
  16. astreum/communication/{start.py → node.py} +10 -11
  17. astreum/communication/outgoing_queue.py +108 -0
  18. astreum/communication/processors/incoming.py +110 -37
  19. astreum/communication/processors/outgoing.py +35 -2
  20. astreum/communication/processors/peer.py +133 -58
  21. astreum/communication/setup.py +272 -113
  22. astreum/communication/util.py +14 -0
  23. astreum/node.py +99 -92
  24. astreum/storage/actions/get.py +79 -48
  25. astreum/storage/actions/set.py +171 -156
  26. astreum/storage/providers.py +24 -0
  27. astreum/storage/setup.py +23 -22
  28. astreum/utils/config.py +234 -45
  29. astreum/utils/logging.py +1 -1
  30. astreum/{consensus → validation}/__init__.py +0 -4
  31. astreum/validation/constants.py +2 -0
  32. astreum/{consensus → validation}/genesis.py +4 -6
  33. astreum/validation/models/block.py +544 -0
  34. astreum/validation/models/fork.py +511 -0
  35. astreum/{consensus → validation}/models/receipt.py +17 -4
  36. astreum/{consensus → validation}/models/transaction.py +45 -3
  37. astreum/validation/node.py +190 -0
  38. astreum/{consensus → validation}/validator.py +1 -1
  39. astreum/validation/workers/__init__.py +8 -0
  40. astreum/{consensus → validation}/workers/validation.py +360 -333
  41. astreum/verification/__init__.py +4 -0
  42. astreum/{consensus/workers/discovery.py → verification/discover.py} +1 -1
  43. astreum/verification/node.py +61 -0
  44. astreum/verification/worker.py +183 -0
  45. {astreum-0.3.16.dist-info → astreum-0.3.46.dist-info}/METADATA +43 -9
  46. astreum-0.3.46.dist-info/RECORD +79 -0
  47. astreum/consensus/models/block.py +0 -364
  48. astreum/consensus/models/chain.py +0 -66
  49. astreum/consensus/models/fork.py +0 -100
  50. astreum/consensus/setup.py +0 -83
  51. astreum/consensus/start.py +0 -67
  52. astreum/consensus/workers/__init__.py +0 -9
  53. astreum/consensus/workers/verify.py +0 -90
  54. astreum-0.3.16.dist-info/RECORD +0 -72
  55. /astreum/{consensus → validation}/models/__init__.py +0 -0
  56. /astreum/{consensus → validation}/models/account.py +0 -0
  57. /astreum/{consensus → validation}/models/accounts.py +0 -0
  58. {astreum-0.3.16.dist-info → astreum-0.3.46.dist-info}/WHEEL +0 -0
  59. {astreum-0.3.16.dist-info → astreum-0.3.46.dist-info}/licenses/LICENSE +0 -0
  60. {astreum-0.3.16.dist-info → astreum-0.3.46.dist-info}/top_level.txt +0 -0
astreum/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- from astreum.consensus import Account, Accounts, Block, Chain, Fork, Receipt, Transaction
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 .setup import communication_setup
5
-
6
- __all__ = [
7
- "Message",
8
- "Peer",
9
- "Route",
10
- "communication_setup",
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")
@@ -1,62 +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 ..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
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
@@ -1,176 +1,206 @@
1
- import logging
2
- import socket
1
+ import logging
2
+ import socket
3
3
  from enum import IntEnum
4
4
  from typing import TYPE_CHECKING, Tuple
5
5
 
6
6
  from .object_response import ObjectResponse, ObjectResponseType
7
+ from ..outgoing_queue import enqueue_outgoing
7
8
  from ..models.message import Message, MessageTopic
8
9
  from ..util import xor_distance
9
-
10
- if TYPE_CHECKING:
11
- from .. import Node
12
- from ..models.peer import Peer
13
-
14
-
15
- class ObjectRequestType(IntEnum):
16
- OBJECT_GET = 0
17
- OBJECT_PUT = 1
18
-
19
-
20
- class ObjectRequest:
21
- type: ObjectRequestType
22
- data: bytes
23
- atom_id: bytes
24
-
25
- def __init__(self, type: ObjectRequestType, data: bytes, atom_id: bytes = None):
26
- self.type = type
27
- self.data = data
28
- self.atom_id = atom_id
29
-
30
- def to_bytes(self):
31
- return bytes([self.type.value]) + self.atom_id + self.data
32
-
33
- @classmethod
34
- def from_bytes(cls, data: bytes) -> "ObjectRequest":
35
- # need at least 1 byte for type + 32 bytes for hash
36
- if len(data) < 1 + 32:
37
- raise ValueError(f"Too short for ObjectRequest ({len(data)} bytes)")
38
-
39
- type_val = data[0]
40
- try:
41
- req_type = ObjectRequestType(type_val)
42
- except ValueError:
43
- raise ValueError(f"Unknown ObjectRequestType: {type_val!r}")
44
-
45
- atom_id_bytes = data[1:33]
46
- payload = data[33:]
47
- return cls(req_type, payload, atom_id_bytes)
48
-
49
-
50
- def encode_peer_contact_bytes(peer: "Peer") -> bytes:
51
- """Return a fixed-width peer contact payload (32-byte key + IPv4 + port)."""
52
- host, port = peer.address
53
- key_bytes = peer.public_key_bytes
54
- try:
55
- ip_bytes = socket.inet_aton(host)
56
- except OSError as exc: # pragma: no cover - inet_aton raises for invalid hosts
57
- raise ValueError(f"invalid IPv4 address: {host}") from exc
58
- if not (0 <= port <= 0xFFFF):
59
- raise ValueError(f"port out of range (0-65535): {port}")
60
- port_bytes = int(port).to_bytes(2, "big", signed=False)
61
- return key_bytes + ip_bytes + port_bytes
62
-
63
-
64
- def handle_object_request(node: "Node", peer: "Peer", message: Message) -> None:
65
- if message.content is None:
66
- node.logger.warning("OBJECT_REQUEST from %s missing content", peer.address)
67
- return
68
-
69
- try:
70
- object_request = ObjectRequest.from_bytes(message.content)
71
- except Exception as exc:
72
- node.logger.warning("Error decoding OBJECT_REQUEST from %s: %s", peer.address, exc)
73
- return
74
-
75
- match object_request.type:
76
- case ObjectRequestType.OBJECT_GET:
77
- atom_id = object_request.atom_id
78
- node.logger.debug("Handling OBJECT_GET for %s from %s", atom_id.hex(), peer.address)
79
-
80
- local_atom = node.local_get(atom_id)
81
- if local_atom is not None:
82
- node.logger.debug("Object %s found locally; returning to %s", atom_id.hex(), peer.address)
83
- resp = ObjectResponse(
84
- type=ObjectResponseType.OBJECT_FOUND,
85
- data=local_atom.to_bytes(),
86
- atom_id=atom_id
87
- )
10
+ from ...storage.providers import provider_id_for_payload, provider_payload_for_id
11
+
12
+ if TYPE_CHECKING:
13
+ from .. import Node
14
+ from ..models.peer import Peer
15
+
16
+
17
+ class ObjectRequestType(IntEnum):
18
+ OBJECT_GET = 0
19
+ OBJECT_PUT = 1
20
+
21
+
22
+ class ObjectRequest:
23
+ type: ObjectRequestType
24
+ data: bytes
25
+ atom_id: bytes
26
+
27
+ def __init__(self, type: ObjectRequestType, data: bytes, atom_id: bytes = None):
28
+ self.type = type
29
+ self.data = data
30
+ self.atom_id = atom_id
31
+
32
+ def to_bytes(self):
33
+ return bytes([self.type.value]) + self.atom_id + self.data
34
+
35
+ @classmethod
36
+ def from_bytes(cls, data: bytes) -> "ObjectRequest":
37
+ # need at least 1 byte for type + 32 bytes for hash
38
+ if len(data) < 1 + 32:
39
+ raise ValueError(f"Too short for ObjectRequest ({len(data)} bytes)")
40
+
41
+ type_val = data[0]
42
+ try:
43
+ req_type = ObjectRequestType(type_val)
44
+ except ValueError:
45
+ raise ValueError(f"Unknown ObjectRequestType: {type_val!r}")
46
+
47
+ atom_id_bytes = data[1:33]
48
+ payload = data[33:]
49
+ return cls(req_type, payload, atom_id_bytes)
50
+
51
+
52
+ def encode_peer_contact_bytes(peer: "Peer") -> bytes:
53
+ """Return a fixed-width peer contact payload (32-byte key + IPv4 + port)."""
54
+ host, port = peer.address
55
+ key_bytes = peer.public_key_bytes
56
+ try:
57
+ ip_bytes = socket.inet_aton(host)
58
+ except OSError as exc: # pragma: no cover - inet_aton raises for invalid hosts
59
+ raise ValueError(f"invalid IPv4 address: {host}") from exc
60
+ if not (0 <= port <= 0xFFFF):
61
+ raise ValueError(f"port out of range (0-65535): {port}")
62
+ port_bytes = int(port).to_bytes(2, "big", signed=False)
63
+ return key_bytes + ip_bytes + port_bytes
64
+
65
+
66
+ def handle_object_request(node: "Node", peer: "Peer", message: Message) -> None:
67
+ if message.content is None:
68
+ node.logger.warning("OBJECT_REQUEST from %s missing content", peer.address)
69
+ return
70
+
71
+ try:
72
+ object_request = ObjectRequest.from_bytes(message.content)
73
+ except Exception as exc:
74
+ node.logger.warning("Error decoding OBJECT_REQUEST from %s: %s", peer.address, exc)
75
+ return
76
+
77
+ match object_request.type:
78
+ case ObjectRequestType.OBJECT_GET:
79
+ atom_id = object_request.atom_id
80
+ node.logger.debug("Handling OBJECT_GET for %s from %s", atom_id.hex(), peer.address)
81
+
82
+ local_atom = node.local_get(atom_id)
83
+ if local_atom is not None:
84
+ node.logger.debug("Object %s found locally; returning to %s", atom_id.hex(), peer.address)
85
+ resp = ObjectResponse(
86
+ type=ObjectResponseType.OBJECT_FOUND,
87
+ data=local_atom.to_bytes(),
88
+ atom_id=atom_id
89
+ )
88
90
  obj_res_msg = Message(
89
91
  topic=MessageTopic.OBJECT_RESPONSE,
90
92
  body=resp.to_bytes(),
91
93
  sender=node.relay_public_key,
92
94
  )
93
95
  obj_res_msg.encrypt(peer.shared_key_bytes)
94
- node.outgoing_queue.put((obj_res_msg.to_bytes(), peer.address))
95
- return
96
-
97
- if atom_id in node.storage_index:
98
- node.logger.debug("Known provider for %s; informing %s", atom_id.hex(), peer.address)
99
- provider_bytes = node.storage_index[atom_id]
100
- resp = ObjectResponse(
101
- type=ObjectResponseType.OBJECT_PROVIDER,
102
- data=provider_bytes,
103
- atom_id=atom_id
96
+ enqueue_outgoing(
97
+ node,
98
+ peer.address,
99
+ message=obj_res_msg,
100
+ difficulty=peer.difficulty,
104
101
  )
105
- obj_res_msg = Message(
106
- topic=MessageTopic.OBJECT_RESPONSE,
107
- body=resp.to_bytes(),
108
- sender=node.relay_public_key,
109
- )
110
- obj_res_msg.encrypt(peer.shared_key_bytes)
111
- node.outgoing_queue.put((obj_res_msg.to_bytes(), peer.address))
112
102
  return
113
-
114
- nearest_peer = node.peer_route.closest_peer_for_hash(atom_id)
115
- if nearest_peer:
116
- node.logger.debug("Forwarding requester %s to nearest peer for %s", peer.address, atom_id.hex())
117
- peer_info = encode_peer_contact_bytes(nearest_peer)
118
- resp = ObjectResponse(
119
- type=ObjectResponseType.OBJECT_PROVIDER,
120
- # type=ObjectResponseType.OBJECT_NEAREST_PEER,
121
- data=peer_info,
122
- atom_id=atom_id
123
- )
103
+
104
+ if atom_id in node.storage_index:
105
+ provider_id = node.storage_index[atom_id]
106
+ provider_bytes = provider_payload_for_id(node, provider_id)
107
+ if provider_bytes is not None:
108
+ node.logger.debug("Known provider for %s; informing %s", atom_id.hex(), peer.address)
109
+ resp = ObjectResponse(
110
+ type=ObjectResponseType.OBJECT_PROVIDER,
111
+ data=provider_bytes,
112
+ atom_id=atom_id
113
+ )
114
+ obj_res_msg = Message(
115
+ topic=MessageTopic.OBJECT_RESPONSE,
116
+ body=resp.to_bytes(),
117
+ sender=node.relay_public_key,
118
+ )
119
+ obj_res_msg.encrypt(peer.shared_key_bytes)
120
+ enqueue_outgoing(
121
+ node,
122
+ peer.address,
123
+ message=obj_res_msg,
124
+ difficulty=peer.difficulty,
125
+ )
126
+ return
127
+ node.logger.warning(
128
+ "Unknown provider id %s for %s",
129
+ provider_id,
130
+ atom_id.hex(),
131
+ )
132
+
133
+ nearest_peer = node.peer_route.closest_peer_for_hash(atom_id)
134
+ if nearest_peer:
135
+ node.logger.debug("Forwarding requester %s to nearest peer for %s", peer.address, atom_id.hex())
136
+ peer_info = encode_peer_contact_bytes(nearest_peer)
137
+ resp = ObjectResponse(
138
+ type=ObjectResponseType.OBJECT_PROVIDER,
139
+ # type=ObjectResponseType.OBJECT_NEAREST_PEER,
140
+ data=peer_info,
141
+ atom_id=atom_id
142
+ )
124
143
  obj_res_msg = Message(
125
144
  topic=MessageTopic.OBJECT_RESPONSE,
126
145
  body=resp.to_bytes(),
127
146
  sender=node.relay_public_key,
128
147
  )
129
148
  obj_res_msg.encrypt(nearest_peer.shared_key_bytes)
130
- node.outgoing_queue.put((obj_res_msg.to_bytes(), peer.address))
131
-
132
- case ObjectRequestType.OBJECT_PUT:
133
- node.logger.debug("Handling OBJECT_PUT for %s from %s", object_request.atom_id.hex(), peer.address)
134
-
135
- nearest_peer = node.peer_route.closest_peer_for_hash(object_request.atom_id)
136
- is_self_closest = False
137
- if nearest_peer is None or nearest_peer.address is None:
138
- is_self_closest = True
139
- else:
140
- try:
141
- self_distance = xor_distance(object_request.atom_id, node.relay_public_key_bytes)
142
- peer_distance = xor_distance(object_request.atom_id, nearest_peer.public_key_bytes)
143
- except Exception as exc:
144
- node.logger.warning(
145
- "Failed distance comparison for OBJECT_PUT %s: %s",
146
- object_request.atom_id.hex(),
147
- exc,
148
- )
149
- is_self_closest = True
150
- else:
151
- is_self_closest = self_distance <= peer_distance
152
-
153
- if is_self_closest:
154
- node.logger.debug("Storing provider info for %s locally", object_request.atom_id.hex())
155
- node.storage_index[object_request.atom_id] = object_request.data
156
- else:
157
- node.logger.debug(
158
- "Forwarding OBJECT_PUT for %s to nearer peer %s",
159
- object_request.atom_id.hex(),
160
- nearest_peer.address,
161
- )
162
- fwd_req = ObjectRequest(
163
- type=ObjectRequestType.OBJECT_PUT,
164
- data=object_request.data,
165
- atom_id=object_request.atom_id,
149
+ enqueue_outgoing(
150
+ node,
151
+ peer.address,
152
+ message=obj_res_msg,
153
+ difficulty=peer.difficulty,
166
154
  )
155
+
156
+ case ObjectRequestType.OBJECT_PUT:
157
+ node.logger.debug("Handling OBJECT_PUT for %s from %s", object_request.atom_id.hex(), peer.address)
158
+
159
+ nearest_peer = node.peer_route.closest_peer_for_hash(object_request.atom_id)
160
+ is_self_closest = False
161
+ if nearest_peer is None or nearest_peer.address is None:
162
+ is_self_closest = True
163
+ else:
164
+ try:
165
+ self_distance = xor_distance(object_request.atom_id, node.relay_public_key_bytes)
166
+ peer_distance = xor_distance(object_request.atom_id, nearest_peer.public_key_bytes)
167
+ except Exception as exc:
168
+ node.logger.warning(
169
+ "Failed distance comparison for OBJECT_PUT %s: %s",
170
+ object_request.atom_id.hex(),
171
+ exc,
172
+ )
173
+ is_self_closest = True
174
+ else:
175
+ is_self_closest = self_distance <= peer_distance
176
+
177
+ if is_self_closest:
178
+ node.logger.debug("Storing provider info for %s locally", object_request.atom_id.hex())
179
+ provider_id = provider_id_for_payload(node, object_request.data)
180
+ node.storage_index[object_request.atom_id] = provider_id
181
+ else:
182
+ node.logger.debug(
183
+ "Forwarding OBJECT_PUT for %s to nearer peer %s",
184
+ object_request.atom_id.hex(),
185
+ nearest_peer.address,
186
+ )
187
+ fwd_req = ObjectRequest(
188
+ type=ObjectRequestType.OBJECT_PUT,
189
+ data=object_request.data,
190
+ atom_id=object_request.atom_id,
191
+ )
167
192
  obj_req_msg = Message(
168
193
  topic=MessageTopic.OBJECT_REQUEST,
169
194
  body=fwd_req.to_bytes(),
170
195
  sender=node.relay_public_key,
171
196
  )
172
197
  obj_req_msg.encrypt(nearest_peer.shared_key_bytes)
173
- node.outgoing_queue.put((obj_req_msg.to_bytes(), nearest_peer.address))
174
-
175
- case _:
176
- node.logger.warning("Unknown ObjectRequestType %s from %s", object_request.type, peer.address)
198
+ enqueue_outgoing(
199
+ node,
200
+ nearest_peer.address,
201
+ message=obj_req_msg,
202
+ difficulty=nearest_peer.difficulty,
203
+ )
204
+
205
+ case _:
206
+ node.logger.warning("Unknown ObjectRequestType %s from %s", object_request.type, peer.address)