astreum 0.2.28__tar.gz → 0.2.29__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.
Potentially problematic release.
This version of astreum might be problematic. Click here for more details.
- {astreum-0.2.28/src/astreum.egg-info → astreum-0.2.29}/PKG-INFO +1 -1
- {astreum-0.2.28 → astreum-0.2.29}/pyproject.toml +1 -1
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/node.py +55 -225
- astreum-0.2.29/src/astreum/relay/peer.py +9 -0
- astreum-0.2.29/src/astreum/relay/route.py +25 -0
- astreum-0.2.29/src/astreum/relay/setup.py +58 -0
- astreum-0.2.29/src/astreum/storage/__init__.py +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/storage/setup.py +1 -2
- {astreum-0.2.28 → astreum-0.2.29/src/astreum.egg-info}/PKG-INFO +1 -1
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum.egg-info/SOURCES.txt +4 -0
- {astreum-0.2.28 → astreum-0.2.29}/LICENSE +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/README.md +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/setup.cfg +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/__init__.py +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/crypto/__init__.py +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/crypto/ed25519.py +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/crypto/quadratic_form.py +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/crypto/wesolowski.py +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/crypto/x25519.py +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/format.py +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/lispeum/__init__.py +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/lispeum/environment.py +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/lispeum/expression.py +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/lispeum/parser.py +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/lispeum/tokenizer.py +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/models/__init__.py +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/models/account.py +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/models/accounts.py +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/models/block.py +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/models/merkle.py +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/models/message.py +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/models/patricia.py +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/models/transaction.py +0 -0
- {astreum-0.2.28/src/astreum/storage → astreum-0.2.29/src/astreum/relay}/__init__.py +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum/storage/object.py +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum.egg-info/dependency_links.txt +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum.egg-info/requires.txt +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/src/astreum.egg-info/top_level.txt +0 -0
- {astreum-0.2.28 → astreum-0.2.29}/tests/test_node_machine.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: astreum
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.29
|
|
4
4
|
Summary: Python library to interact with the Astreum blockchain and its Lispeum virtual machine.
|
|
5
5
|
Author-email: "Roy R. O. Okello" <roy@stelar.xyz>
|
|
6
6
|
Project-URL: Homepage, https://github.com/astreum/lib
|
|
@@ -9,6 +9,9 @@ import uuid
|
|
|
9
9
|
|
|
10
10
|
from astreum.lispeum.environment import Env
|
|
11
11
|
from astreum.lispeum.expression import Expr
|
|
12
|
+
from astreum.relay.peer import Peer
|
|
13
|
+
from astreum.relay.route import Route
|
|
14
|
+
from astreum.relay.setup import load_ed25519, load_x25519, make_routes, setup_outgoing, setup_udp
|
|
12
15
|
from astreum.storage.object import ObjectRequest, ObjectRequestType, ObjectResponse, ObjectResponseType
|
|
13
16
|
from astreum.storage.setup import storage_setup
|
|
14
17
|
|
|
@@ -21,39 +24,6 @@ import blake3
|
|
|
21
24
|
import struct
|
|
22
25
|
from .models.message import Message, MessageTopic
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class Peer:
|
|
27
|
-
shared_key: bytes
|
|
28
|
-
timestamp: datetime
|
|
29
|
-
def __init__(self, my_sec_key: X25519PrivateKey, peer_pub_key: X25519PublicKey):
|
|
30
|
-
self.shared_key = my_sec_key.exchange(peer_pub_key)
|
|
31
|
-
self.timestamp = datetime.now(timezone.utc)
|
|
32
|
-
|
|
33
|
-
class Route:
|
|
34
|
-
def __init__(self, relay_public_key: X25519PublicKey, bucket_size: int = 16):
|
|
35
|
-
self.relay_public_key_bytes = relay_public_key.public_bytes(encoding=Encoding.Raw, format=PublicFormat.Raw)
|
|
36
|
-
self.bucket_size = bucket_size
|
|
37
|
-
self.buckets: Dict[int, List[X25519PublicKey]] = {
|
|
38
|
-
i: [] for i in range(len(self.relay_public_key_bytes) * 8)
|
|
39
|
-
}
|
|
40
|
-
self.peers = {}
|
|
41
|
-
|
|
42
|
-
@staticmethod
|
|
43
|
-
def _matching_leading_bits(a: bytes, b: bytes) -> int:
|
|
44
|
-
for byte_index, (ba, bb) in enumerate(zip(a, b)):
|
|
45
|
-
diff = ba ^ bb
|
|
46
|
-
if diff:
|
|
47
|
-
return byte_index * 8 + (8 - diff.bit_length())
|
|
48
|
-
return len(a) * 8
|
|
49
|
-
|
|
50
|
-
def add_peer(self, peer_public_key: X25519PublicKey):
|
|
51
|
-
peer_public_key_bytes = peer_public_key.public_bytes(encoding=Encoding.Raw, format=PublicFormat.Raw)
|
|
52
|
-
bucket_idx = self._matching_leading_bits(self.relay_public_key_bytes, peer_public_key_bytes)
|
|
53
|
-
if len(self.buckets[bucket_idx]) < self.bucket_size:
|
|
54
|
-
self.buckets[bucket_idx].append(peer_public_key)
|
|
55
|
-
|
|
56
|
-
|
|
57
27
|
def encode_ip_address(host: str, port: int) -> bytes:
|
|
58
28
|
ip_bytes = socket.inet_pton(socket.AF_INET6 if ':' in host else socket.AF_INET, host)
|
|
59
29
|
port_bytes = struct.pack("!H", port)
|
|
@@ -95,71 +65,45 @@ class Node:
|
|
|
95
65
|
pass
|
|
96
66
|
|
|
97
67
|
def _relay_setup(self, config: dict):
|
|
98
|
-
self.use_ipv6
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
self.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
#
|
|
125
|
-
family = socket.AF_INET6 if self.use_ipv6 else socket.AF_INET
|
|
126
|
-
|
|
127
|
-
self.incoming_socket = socket.socket(family, socket.SOCK_DGRAM)
|
|
128
|
-
if self.use_ipv6:
|
|
129
|
-
self.incoming_socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
|
|
130
|
-
bind_address = "::" if self.use_ipv6 else "0.0.0.0"
|
|
131
|
-
self.incoming_socket.bind((bind_address, incoming_port or 0))
|
|
132
|
-
self.incoming_port = self.incoming_socket.getsockname()[1]
|
|
133
|
-
self.incoming_queue = Queue()
|
|
134
|
-
|
|
135
|
-
self.incoming_populate_thread = threading.Thread(target=self._relay_incoming_queue_populating)
|
|
136
|
-
self.incoming_populate_thread.daemon = True
|
|
137
|
-
self.incoming_populate_thread.start()
|
|
138
|
-
|
|
139
|
-
self.incoming_process_thread = threading.Thread(target=self._relay_incoming_queue_processing)
|
|
140
|
-
self.incoming_process_thread.daemon = True
|
|
141
|
-
self.incoming_process_thread.start()
|
|
142
|
-
|
|
143
|
-
# outgoing thread
|
|
144
|
-
self.outgoing_socket = socket.socket(family, socket.SOCK_DGRAM)
|
|
145
|
-
self.outgoing_queue = Queue()
|
|
146
|
-
self.outgoing_thread = threading.Thread(target=self._relay_outgoing_queue_processor)
|
|
147
|
-
self.outgoing_thread.daemon = True
|
|
148
|
-
self.outgoing_thread.start()
|
|
149
|
-
|
|
68
|
+
self.use_ipv6 = config.get('use_ipv6', False)
|
|
69
|
+
|
|
70
|
+
# key loading
|
|
71
|
+
self.relay_secret_key = load_x25519(config.get('relay_secret_key'))
|
|
72
|
+
self.validation_secret_key = load_ed25519(config.get('validation_secret_key'))
|
|
73
|
+
|
|
74
|
+
# derive pubs + routes
|
|
75
|
+
self.relay_public_key = self.relay_secret_key.public_key()
|
|
76
|
+
self.peer_route, self.validation_route = make_routes(
|
|
77
|
+
self.relay_public_key,
|
|
78
|
+
self.validation_secret_key
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# sockets + queues + threads
|
|
82
|
+
(self.incoming_socket,
|
|
83
|
+
self.incoming_port,
|
|
84
|
+
self.incoming_queue,
|
|
85
|
+
self.incoming_populate_thread,
|
|
86
|
+
self.incoming_process_thread
|
|
87
|
+
) = setup_udp(config.get('incoming_port', 7373), self.use_ipv6)
|
|
88
|
+
|
|
89
|
+
(self.outgoing_socket,
|
|
90
|
+
self.outgoing_queue,
|
|
91
|
+
self.outgoing_thread
|
|
92
|
+
) = setup_outgoing(self.use_ipv6)
|
|
93
|
+
|
|
94
|
+
# other workers & maps
|
|
150
95
|
self.object_request_queue = Queue()
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
96
|
+
self.peer_manager_thread = threading.Thread(
|
|
97
|
+
target=self._relay_peer_manager,
|
|
98
|
+
daemon=True
|
|
99
|
+
)
|
|
154
100
|
self.peer_manager_thread.start()
|
|
155
101
|
|
|
156
|
-
self.peers = Dict[X25519PublicKey,
|
|
157
|
-
self.addresses = Dict[Tuple[str, int], X25519PublicKey]
|
|
158
|
-
|
|
159
|
-
if 'bootstrap' in config:
|
|
160
|
-
for addr in config['bootstrap']:
|
|
161
|
-
self._send_ping(addr)
|
|
102
|
+
self.peers, self.addresses = {}, {} # peers: Dict[X25519PublicKey,Peer], addresses: Dict[(str,int),X25519PublicKey]
|
|
162
103
|
|
|
104
|
+
# bootstrap pings
|
|
105
|
+
for addr in config.get('bootstrap', []):
|
|
106
|
+
self._send_ping(addr)
|
|
163
107
|
|
|
164
108
|
def _local_object_get(self, data_hash: bytes) -> Optional[bytes]:
|
|
165
109
|
if self.memory_storage is not None:
|
|
@@ -530,137 +474,10 @@ class Node:
|
|
|
530
474
|
return result
|
|
531
475
|
|
|
532
476
|
# # List
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
# args = expr.elements[1:]
|
|
538
|
-
# if len(args) != 2:
|
|
539
|
-
# return Expr.Error(
|
|
540
|
-
# category="SyntaxError",
|
|
541
|
-
# message="list.get expects exactly two arguments: a list and an index"
|
|
542
|
-
# )
|
|
543
|
-
# list_obj = self.evaluate_expression(args[0], env)
|
|
544
|
-
# index = self.evaluate_expression(args[1], env)
|
|
545
|
-
# return handle_list_get(self, list_obj, index, env)
|
|
546
|
-
|
|
547
|
-
# elif first.value == "list.insert":
|
|
548
|
-
# args = expr.elements[1:]
|
|
549
|
-
# if len(args) != 3:
|
|
550
|
-
# return Expr.ListExpr([
|
|
551
|
-
# Expr.ListExpr([]),
|
|
552
|
-
# Expr.String("list.insert expects exactly three arguments: a list, an index, and a value")
|
|
553
|
-
# ])
|
|
554
|
-
|
|
555
|
-
# return handle_list_insert(
|
|
556
|
-
# list=self.evaluate_expression(args[0], env),
|
|
557
|
-
# index=self.evaluate_expression(args[1], env),
|
|
558
|
-
# value=self.evaluate_expression(args[2], env),
|
|
559
|
-
# )
|
|
560
|
-
|
|
561
|
-
# elif first.value == "list.remove":
|
|
562
|
-
# args = expr.elements[1:]
|
|
563
|
-
# if len(args) != 2:
|
|
564
|
-
# return Expr.ListExpr([
|
|
565
|
-
# Expr.ListExpr([]),
|
|
566
|
-
# Expr.String("list.remove expects exactly two arguments: a list and an index")
|
|
567
|
-
# ])
|
|
568
|
-
|
|
569
|
-
# return handle_list_remove(
|
|
570
|
-
# list=self.evaluate_expression(args[0], env),
|
|
571
|
-
# index=self.evaluate_expression(args[1], env),
|
|
572
|
-
# )
|
|
573
|
-
|
|
574
|
-
# elif first.value == "list.length":
|
|
575
|
-
# args = expr.elements[1:]
|
|
576
|
-
# if len(args) != 1:
|
|
577
|
-
# return Expr.ListExpr([
|
|
578
|
-
# Expr.ListExpr([]),
|
|
579
|
-
# Expr.String("list.length expects exactly one argument: a list")
|
|
580
|
-
# ])
|
|
581
|
-
|
|
582
|
-
# list_obj = self.evaluate_expression(args[0], env)
|
|
583
|
-
# if not isinstance(list_obj, Expr.ListExpr):
|
|
584
|
-
# return Expr.ListExpr([
|
|
585
|
-
# Expr.ListExpr([]),
|
|
586
|
-
# Expr.String("Argument must be a list")
|
|
587
|
-
# ])
|
|
588
|
-
|
|
589
|
-
# return Expr.ListExpr([
|
|
590
|
-
# Expr.Integer(len(list_obj.elements)),
|
|
591
|
-
# Expr.ListExpr([])
|
|
592
|
-
# ])
|
|
593
|
-
|
|
594
|
-
# elif first.value == "list.fold":
|
|
595
|
-
# if len(args) != 3:
|
|
596
|
-
# return Expr.ListExpr([
|
|
597
|
-
# Expr.ListExpr([]),
|
|
598
|
-
# Expr.String("list.fold expects exactly three arguments: a list, an initial value, and a function")
|
|
599
|
-
# ])
|
|
600
|
-
|
|
601
|
-
# return handle_list_fold(
|
|
602
|
-
# machine=self,
|
|
603
|
-
# list=self.evaluate_expression(args[0], env),
|
|
604
|
-
# initial=self.evaluate_expression(args[1], env),
|
|
605
|
-
# func=self.evaluate_expression(args[2], env),
|
|
606
|
-
# env=env,
|
|
607
|
-
# )
|
|
608
|
-
|
|
609
|
-
# elif first.value == "list.map":
|
|
610
|
-
# if len(args) != 2:
|
|
611
|
-
# return Expr.ListExpr([
|
|
612
|
-
# Expr.ListExpr([]),
|
|
613
|
-
# Expr.String("list.map expects exactly two arguments: a list and a function")
|
|
614
|
-
# ])
|
|
615
|
-
|
|
616
|
-
# return handle_list_map(
|
|
617
|
-
# machine=self,
|
|
618
|
-
# list=self.evaluate_expression(args[0], env),
|
|
619
|
-
# func=self.evaluate_expression(args[1], env),
|
|
620
|
-
# env=env,
|
|
621
|
-
# )
|
|
622
|
-
|
|
623
|
-
# elif first.value == "list.position":
|
|
624
|
-
# if len(args) != 2:
|
|
625
|
-
# return Expr.ListExpr([
|
|
626
|
-
# Expr.ListExpr([]),
|
|
627
|
-
# Expr.String("list.position expects exactly two arguments: a list and a function")
|
|
628
|
-
# ])
|
|
629
|
-
|
|
630
|
-
# return handle_list_position(
|
|
631
|
-
# machine=self,
|
|
632
|
-
# list=self.evaluate_expression(args[0], env),
|
|
633
|
-
# predicate=self.evaluate_expression(args[1], env),
|
|
634
|
-
# env=env,
|
|
635
|
-
# )
|
|
636
|
-
|
|
637
|
-
# elif first.value == "list.any":
|
|
638
|
-
# if len(args) != 2:
|
|
639
|
-
# return Expr.ListExpr([
|
|
640
|
-
# Expr.ListExpr([]),
|
|
641
|
-
# Expr.String("list.any expects exactly two arguments: a list and a function")
|
|
642
|
-
# ])
|
|
643
|
-
|
|
644
|
-
# return handle_list_any(
|
|
645
|
-
# machine=self,
|
|
646
|
-
# list=self.evaluate_expression(args[0], env),
|
|
647
|
-
# predicate=self.evaluate_expression(args[1], env),
|
|
648
|
-
# env=env,
|
|
649
|
-
# )
|
|
650
|
-
|
|
651
|
-
# elif first.value == "list.all":
|
|
652
|
-
# if len(args) != 2:
|
|
653
|
-
# return Expr.ListExpr([
|
|
654
|
-
# Expr.ListExpr([]),
|
|
655
|
-
# Expr.String("list.all expects exactly two arguments: a list and a function")
|
|
656
|
-
# ])
|
|
657
|
-
|
|
658
|
-
# return handle_list_all(
|
|
659
|
-
# machine=self,
|
|
660
|
-
# list=self.evaluate_expression(args[0], env),
|
|
661
|
-
# predicate=self.evaluate_expression(args[1], env),
|
|
662
|
-
# env=env,
|
|
663
|
-
# )
|
|
477
|
+
elif first.value == "list.each":
|
|
478
|
+
internal_function = expr.elements[1]
|
|
479
|
+
|
|
480
|
+
|
|
664
481
|
|
|
665
482
|
# Integer arithmetic primitives
|
|
666
483
|
elif first.value == "+":
|
|
@@ -747,6 +564,19 @@ class Node:
|
|
|
747
564
|
|
|
748
565
|
return Expr.Boolean(res)
|
|
749
566
|
|
|
567
|
+
if isinstance(first, Expr.Function):
|
|
568
|
+
arg_exprs = expr.elements[1:]
|
|
569
|
+
if len(arg_exprs) != len(first.params):
|
|
570
|
+
return Expr.Error(f"arity mismatch: expected {len(first.params)}, got {len(arg_exprs)}", origin=expr)
|
|
571
|
+
|
|
572
|
+
call_env = self.machine_create_environment(parent_id=env_id)
|
|
573
|
+
for name, aexpr in zip(first.params, arg_exprs):
|
|
574
|
+
val = self.machine_expr_eval(env_id, aexpr)
|
|
575
|
+
if isinstance(val, Expr.Error): return val
|
|
576
|
+
self.machine_expr_put(call_env, name, val)
|
|
577
|
+
|
|
578
|
+
return self.machine_expr_eval(env_id=call_env, expr=first.body)
|
|
579
|
+
|
|
750
580
|
else:
|
|
751
581
|
evaluated_elements = [self.machine_expr_eval(env_id=env_id, expr=e) for e in expr.elements]
|
|
752
582
|
return Expr.ListExpr(evaluated_elements)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey, X25519PublicKey
|
|
2
|
+
from datetime import datetime, timezone
|
|
3
|
+
|
|
4
|
+
class Peer:
|
|
5
|
+
shared_key: bytes
|
|
6
|
+
timestamp: datetime
|
|
7
|
+
def __init__(self, my_sec_key: X25519PrivateKey, peer_pub_key: X25519PublicKey):
|
|
8
|
+
self.shared_key = my_sec_key.exchange(peer_pub_key)
|
|
9
|
+
self.timestamp = datetime.now(timezone.utc)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from typing import Dict, List
|
|
2
|
+
from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey, X25519PublicKey
|
|
3
|
+
|
|
4
|
+
class Route:
|
|
5
|
+
def __init__(self, relay_public_key: X25519PublicKey, bucket_size: int = 16):
|
|
6
|
+
self.relay_public_key_bytes = relay_public_key.public_bytes(encoding=Encoding.Raw, format=PublicFormat.Raw)
|
|
7
|
+
self.bucket_size = bucket_size
|
|
8
|
+
self.buckets: Dict[int, List[X25519PublicKey]] = {
|
|
9
|
+
i: [] for i in range(len(self.relay_public_key_bytes) * 8)
|
|
10
|
+
}
|
|
11
|
+
self.peers = {}
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
def _matching_leading_bits(a: bytes, b: bytes) -> int:
|
|
15
|
+
for byte_index, (ba, bb) in enumerate(zip(a, b)):
|
|
16
|
+
diff = ba ^ bb
|
|
17
|
+
if diff:
|
|
18
|
+
return byte_index * 8 + (8 - diff.bit_length())
|
|
19
|
+
return len(a) * 8
|
|
20
|
+
|
|
21
|
+
def add_peer(self, peer_public_key: X25519PublicKey):
|
|
22
|
+
peer_public_key_bytes = peer_public_key.public_bytes(encoding=Encoding.Raw, format=PublicFormat.Raw)
|
|
23
|
+
bucket_idx = self._matching_leading_bits(self.relay_public_key_bytes, peer_public_key_bytes)
|
|
24
|
+
if len(self.buckets[bucket_idx]) < self.bucket_size:
|
|
25
|
+
self.buckets[bucket_idx].append(peer_public_key)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import socket, threading
|
|
2
|
+
from queue import Queue
|
|
3
|
+
from typing import Tuple, Optional
|
|
4
|
+
from cryptography.hazmat.primitives.asymmetric import ed25519
|
|
5
|
+
from cryptography.hazmat.primitives.asymmetric.x25519 import (
|
|
6
|
+
X25519PrivateKey,
|
|
7
|
+
X25519PublicKey,
|
|
8
|
+
)
|
|
9
|
+
from yourproject.routes import Route
|
|
10
|
+
|
|
11
|
+
def load_x25519(hex_key: Optional[str]) -> X25519PrivateKey:
|
|
12
|
+
"""DH key for relaying (always X25519)."""
|
|
13
|
+
return
|
|
14
|
+
|
|
15
|
+
def load_ed25519(hex_key: Optional[str]) -> Optional[ed25519.Ed25519PrivateKey]:
|
|
16
|
+
"""Signing key for validation (Ed25519), or None if absent."""
|
|
17
|
+
return ed25519.Ed25519PrivateKey.from_private_bytes(bytes.fromhex(hex_key)) \
|
|
18
|
+
if hex_key else None
|
|
19
|
+
|
|
20
|
+
def make_routes(
|
|
21
|
+
relay_pk: X25519PublicKey,
|
|
22
|
+
val_sk: Optional[ed25519.Ed25519PrivateKey]
|
|
23
|
+
) -> Tuple[Route, Optional[Route]]:
|
|
24
|
+
"""Peer route (DH pubkey) + optional validation route (ed pubkey)."""
|
|
25
|
+
peer_rt = Route(relay_pk)
|
|
26
|
+
val_rt = Route(val_sk.public_key()) if val_sk else None
|
|
27
|
+
return peer_rt, val_rt
|
|
28
|
+
|
|
29
|
+
def setup_udp(
|
|
30
|
+
bind_port: int,
|
|
31
|
+
use_ipv6: bool
|
|
32
|
+
) -> Tuple[socket.socket, int, Queue, threading.Thread, threading.Thread]:
|
|
33
|
+
fam = socket.AF_INET6 if use_ipv6 else socket.AF_INET
|
|
34
|
+
sock = socket.socket(fam, socket.SOCK_DGRAM)
|
|
35
|
+
if use_ipv6:
|
|
36
|
+
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
|
|
37
|
+
sock.bind(("::" if use_ipv6 else "0.0.0.0", bind_port or 0))
|
|
38
|
+
port = sock.getsockname()[1]
|
|
39
|
+
|
|
40
|
+
q = Queue()
|
|
41
|
+
pop = threading.Thread(target=lambda: None, daemon=True)
|
|
42
|
+
proc = threading.Thread(target=lambda: None, daemon=True)
|
|
43
|
+
pop.start(); proc.start()
|
|
44
|
+
return sock, port, q, pop, proc
|
|
45
|
+
|
|
46
|
+
def setup_outgoing(
|
|
47
|
+
use_ipv6: bool
|
|
48
|
+
) -> Tuple[socket.socket, Queue, threading.Thread]:
|
|
49
|
+
fam = socket.AF_INET6 if use_ipv6 else socket.AF_INET
|
|
50
|
+
sock = socket.socket(fam, socket.SOCK_DGRAM)
|
|
51
|
+
q = Queue()
|
|
52
|
+
thr = threading.Thread(target=lambda: None, daemon=True)
|
|
53
|
+
thr.start()
|
|
54
|
+
return sock, q, thr
|
|
55
|
+
|
|
56
|
+
def make_maps():
|
|
57
|
+
"""Empty lookup maps: peers and addresses."""
|
|
58
|
+
return
|
|
File without changes
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
from typing import Optional, Dict, Tuple, Any
|
|
3
3
|
|
|
4
|
-
def storage_setup(config: dict
|
|
5
|
-
) -> Tuple[Optional[Path], Dict[bytes, Any], int, Dict[bytes, bytes]]:
|
|
4
|
+
def storage_setup(config: dict) -> Tuple[Optional[Path], Dict[bytes, Any], int, Dict[bytes, bytes]]:
|
|
6
5
|
storage_path_str = config.get('storage_path')
|
|
7
6
|
if storage_path_str is None:
|
|
8
7
|
storage_path, memory_storage = None, {}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: astreum
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.29
|
|
4
4
|
Summary: Python library to interact with the Astreum blockchain and its Lispeum virtual machine.
|
|
5
5
|
Author-email: "Roy R. O. Okello" <roy@stelar.xyz>
|
|
6
6
|
Project-URL: Homepage, https://github.com/astreum/lib
|
|
@@ -27,6 +27,10 @@ src/astreum/models/merkle.py
|
|
|
27
27
|
src/astreum/models/message.py
|
|
28
28
|
src/astreum/models/patricia.py
|
|
29
29
|
src/astreum/models/transaction.py
|
|
30
|
+
src/astreum/relay/__init__.py
|
|
31
|
+
src/astreum/relay/peer.py
|
|
32
|
+
src/astreum/relay/route.py
|
|
33
|
+
src/astreum/relay/setup.py
|
|
30
34
|
src/astreum/storage/__init__.py
|
|
31
35
|
src/astreum/storage/object.py
|
|
32
36
|
src/astreum/storage/setup.py
|
|
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
|