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.
- astreum/__init__.py +1 -2
- astreum/communication/__init__.py +15 -11
- astreum/communication/difficulty.py +39 -0
- astreum/communication/disconnect.py +57 -0
- astreum/communication/handlers/handshake.py +105 -62
- astreum/communication/handlers/object_request.py +179 -149
- astreum/communication/handlers/object_response.py +7 -1
- astreum/communication/handlers/ping.py +9 -0
- astreum/communication/handlers/route_request.py +7 -1
- astreum/communication/handlers/route_response.py +7 -1
- astreum/communication/incoming_queue.py +96 -0
- astreum/communication/message_pow.py +36 -0
- astreum/communication/models/peer.py +4 -0
- astreum/communication/models/ping.py +27 -6
- astreum/communication/models/route.py +4 -0
- astreum/communication/{start.py → node.py} +10 -11
- astreum/communication/outgoing_queue.py +108 -0
- astreum/communication/processors/incoming.py +110 -37
- astreum/communication/processors/outgoing.py +35 -2
- astreum/communication/processors/peer.py +133 -58
- astreum/communication/setup.py +272 -113
- astreum/communication/util.py +14 -0
- astreum/node.py +99 -92
- astreum/storage/actions/get.py +79 -48
- astreum/storage/actions/set.py +171 -156
- astreum/storage/providers.py +24 -0
- astreum/storage/setup.py +23 -22
- astreum/utils/config.py +234 -45
- astreum/utils/logging.py +1 -1
- astreum/{consensus → validation}/__init__.py +0 -4
- astreum/validation/constants.py +2 -0
- astreum/{consensus → validation}/genesis.py +4 -6
- astreum/validation/models/block.py +544 -0
- astreum/validation/models/fork.py +511 -0
- astreum/{consensus → validation}/models/receipt.py +17 -4
- astreum/{consensus → validation}/models/transaction.py +45 -3
- astreum/validation/node.py +190 -0
- astreum/{consensus → validation}/validator.py +1 -1
- astreum/validation/workers/__init__.py +8 -0
- astreum/{consensus → validation}/workers/validation.py +360 -333
- astreum/verification/__init__.py +4 -0
- astreum/{consensus/workers/discovery.py → verification/discover.py} +1 -1
- astreum/verification/node.py +61 -0
- astreum/verification/worker.py +183 -0
- {astreum-0.3.16.dist-info → astreum-0.3.46.dist-info}/METADATA +43 -9
- astreum-0.3.46.dist-info/RECORD +79 -0
- astreum/consensus/models/block.py +0 -364
- astreum/consensus/models/chain.py +0 -66
- astreum/consensus/models/fork.py +0 -100
- astreum/consensus/setup.py +0 -83
- astreum/consensus/start.py +0 -67
- astreum/consensus/workers/__init__.py +0 -9
- astreum/consensus/workers/verify.py +0 -90
- astreum-0.3.16.dist-info/RECORD +0 -72
- /astreum/{consensus → validation}/models/__init__.py +0 -0
- /astreum/{consensus → validation}/models/account.py +0 -0
- /astreum/{consensus → validation}/models/accounts.py +0 -0
- {astreum-0.3.16.dist-info → astreum-0.3.46.dist-info}/WHEEL +0 -0
- {astreum-0.3.16.dist-info → astreum-0.3.46.dist-info}/licenses/LICENSE +0 -0
- {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.
|
|
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")
|
|
@@ -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 ..
|
|
9
|
-
from ..models.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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 ..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
|
-
|
|
11
|
-
|
|
12
|
-
from ..
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
self.
|
|
28
|
-
self.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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)
|