astreum 0.2.61__py3-none-any.whl → 0.3.9__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 +16 -7
- astreum/{_communication → communication}/__init__.py +3 -3
- astreum/communication/handlers/handshake.py +89 -0
- astreum/communication/handlers/object_request.py +176 -0
- astreum/communication/handlers/object_response.py +115 -0
- astreum/communication/handlers/ping.py +34 -0
- astreum/communication/handlers/route_request.py +76 -0
- astreum/communication/handlers/route_response.py +53 -0
- astreum/communication/models/__init__.py +0 -0
- astreum/communication/models/message.py +124 -0
- astreum/communication/models/peer.py +51 -0
- astreum/{_communication → communication/models}/route.py +7 -12
- astreum/communication/processors/__init__.py +0 -0
- astreum/communication/processors/incoming.py +98 -0
- astreum/communication/processors/outgoing.py +20 -0
- astreum/communication/setup.py +166 -0
- astreum/communication/start.py +37 -0
- astreum/{_communication → communication}/util.py +7 -0
- astreum/consensus/__init__.py +20 -0
- astreum/consensus/genesis.py +66 -0
- astreum/consensus/models/__init__.py +0 -0
- astreum/consensus/models/account.py +84 -0
- astreum/consensus/models/accounts.py +72 -0
- astreum/consensus/models/block.py +364 -0
- astreum/{_consensus → consensus/models}/chain.py +7 -7
- astreum/{_consensus → consensus/models}/fork.py +8 -8
- astreum/consensus/models/receipt.py +98 -0
- astreum/{_consensus → consensus/models}/transaction.py +76 -78
- astreum/{_consensus → consensus}/setup.py +18 -50
- astreum/consensus/start.py +67 -0
- astreum/consensus/validator.py +95 -0
- astreum/{_consensus → consensus}/workers/discovery.py +19 -1
- astreum/consensus/workers/validation.py +307 -0
- astreum/{_consensus → consensus}/workers/verify.py +29 -2
- astreum/crypto/chacha20poly1305.py +74 -0
- astreum/machine/__init__.py +20 -0
- astreum/machine/evaluations/__init__.py +0 -0
- astreum/{_lispeum → machine/evaluations}/high_evaluation.py +237 -236
- astreum/machine/evaluations/low_evaluation.py +281 -0
- astreum/machine/evaluations/script_evaluation.py +27 -0
- astreum/machine/models/__init__.py +0 -0
- astreum/machine/models/environment.py +31 -0
- astreum/{_lispeum → machine/models}/expression.py +36 -8
- astreum/machine/tokenizer.py +90 -0
- astreum/node.py +78 -767
- astreum/storage/__init__.py +7 -0
- astreum/storage/actions/get.py +183 -0
- astreum/storage/actions/set.py +178 -0
- astreum/{_storage → storage/models}/atom.py +55 -57
- astreum/{_storage/patricia.py → storage/models/trie.py} +227 -203
- astreum/storage/requests.py +28 -0
- astreum/storage/setup.py +22 -15
- astreum/utils/config.py +48 -0
- {astreum-0.2.61.dist-info → astreum-0.3.9.dist-info}/METADATA +27 -26
- astreum-0.3.9.dist-info/RECORD +71 -0
- astreum/_communication/message.py +0 -101
- astreum/_communication/peer.py +0 -23
- astreum/_communication/setup.py +0 -322
- astreum/_consensus/__init__.py +0 -20
- astreum/_consensus/account.py +0 -95
- astreum/_consensus/accounts.py +0 -38
- astreum/_consensus/block.py +0 -311
- astreum/_consensus/genesis.py +0 -72
- astreum/_consensus/receipt.py +0 -136
- astreum/_consensus/workers/validation.py +0 -125
- astreum/_lispeum/__init__.py +0 -16
- astreum/_lispeum/environment.py +0 -13
- astreum/_lispeum/low_evaluation.py +0 -123
- astreum/_lispeum/tokenizer.py +0 -22
- astreum/_node.py +0 -198
- astreum/_storage/__init__.py +0 -7
- astreum/_storage/setup.py +0 -35
- astreum/format.py +0 -75
- astreum/models/block.py +0 -441
- astreum/models/merkle.py +0 -205
- astreum/models/patricia.py +0 -393
- astreum/storage/object.py +0 -68
- astreum-0.2.61.dist-info/RECORD +0 -57
- /astreum/{models → communication/handlers}/__init__.py +0 -0
- /astreum/{_communication → communication/models}/ping.py +0 -0
- /astreum/{_consensus → consensus}/workers/__init__.py +0 -0
- /astreum/{_lispeum → machine/models}/meter.py +0 -0
- /astreum/{_lispeum → machine}/parser.py +0 -0
- {astreum-0.2.61.dist-info → astreum-0.3.9.dist-info}/WHEEL +0 -0
- {astreum-0.2.61.dist-info → astreum-0.3.9.dist-info}/licenses/LICENSE +0 -0
- {astreum-0.2.61.dist-info → astreum-0.3.9.dist-info}/top_level.txt +0 -0
astreum/node.py
CHANGED
|
@@ -1,781 +1,92 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
# raise ValueError("Invalid address byte format")
|
|
43
|
-
# return ip, port
|
|
44
|
-
|
|
45
|
-
# class Node:
|
|
46
|
-
# def __init__(self, config: dict = {}):
|
|
47
|
-
# self._machine_setup()
|
|
48
|
-
# machine_only = bool(config.get('machine-only', True))
|
|
49
|
-
# if not machine_only:
|
|
50
|
-
# (
|
|
51
|
-
# self.storage_path,
|
|
52
|
-
# self.memory_storage,
|
|
53
|
-
# self.storage_get_relay_timeout,
|
|
54
|
-
# self.storage_index
|
|
55
|
-
# ) = storage_setup(config)
|
|
56
|
-
|
|
57
|
-
# self._relay_setup(config=config)
|
|
58
|
-
# self._validation_setup(config=config)
|
|
59
|
-
|
|
60
|
-
# def _validation_setup(self, config: dict):
|
|
61
|
-
# if True:
|
|
62
|
-
# self.validator_transactions: Dict[bytes, Transaction] = {}
|
|
63
|
-
# # validator thread
|
|
64
|
-
# pass
|
|
65
|
-
|
|
66
|
-
# def _create_block(self):
|
|
67
|
-
# pass
|
|
68
|
-
|
|
69
|
-
# def _relay_setup(self, config: dict):
|
|
70
|
-
# self.use_ipv6 = config.get('use_ipv6', False)
|
|
71
|
-
|
|
72
|
-
# # key loading
|
|
73
|
-
# self.relay_secret_key = load_x25519(config.get('relay_secret_key'))
|
|
74
|
-
# self.validation_secret_key = load_ed25519(config.get('validation_secret_key'))
|
|
75
|
-
|
|
76
|
-
# # derive pubs + routes
|
|
77
|
-
# self.relay_public_key = self.relay_secret_key.public_key()
|
|
78
|
-
# self.peer_route, self.validation_route = make_routes(
|
|
79
|
-
# self.relay_public_key,
|
|
80
|
-
# self.validation_secret_key
|
|
81
|
-
# )
|
|
82
|
-
|
|
83
|
-
# # sockets + queues + threads
|
|
84
|
-
# (self.incoming_socket,
|
|
85
|
-
# self.incoming_port,
|
|
86
|
-
# self.incoming_queue,
|
|
87
|
-
# self.incoming_populate_thread,
|
|
88
|
-
# self.incoming_process_thread
|
|
89
|
-
# ) = setup_udp(config.get('incoming_port', 7373), self.use_ipv6)
|
|
90
|
-
|
|
91
|
-
# (self.outgoing_socket,
|
|
92
|
-
# self.outgoing_queue,
|
|
93
|
-
# self.outgoing_thread
|
|
94
|
-
# ) = setup_outgoing(self.use_ipv6)
|
|
95
|
-
|
|
96
|
-
# # other workers & maps
|
|
97
|
-
# self.object_request_queue = Queue()
|
|
98
|
-
# self.peer_manager_thread = threading.Thread(
|
|
99
|
-
# target=self._relay_peer_manager,
|
|
100
|
-
# daemon=True
|
|
101
|
-
# )
|
|
102
|
-
# self.peer_manager_thread.start()
|
|
103
|
-
|
|
104
|
-
# self.peers, self.addresses = {}, {} # peers: Dict[X25519PublicKey,Peer], addresses: Dict[(str,int),X25519PublicKey]
|
|
105
|
-
|
|
106
|
-
# # bootstrap pings
|
|
107
|
-
# for addr in config.get('bootstrap', []):
|
|
108
|
-
# self._send_ping(addr)
|
|
109
|
-
|
|
110
|
-
# def _local_object_get(self, data_hash: bytes) -> Optional[bytes]:
|
|
111
|
-
# if self.memory_storage is not None:
|
|
112
|
-
# return self.memory_storage.get(data_hash)
|
|
113
|
-
|
|
114
|
-
# file_path = self.storage_path / data_hash.hex()
|
|
115
|
-
# if file_path.exists():
|
|
116
|
-
# return file_path.read_bytes()
|
|
117
|
-
# return None
|
|
118
|
-
|
|
119
|
-
# def _local_object_put(self, hash: bytes, data: bytes) -> bool:
|
|
120
|
-
# if self.memory_storage is not None:
|
|
121
|
-
# self.memory_storage[hash] = data
|
|
122
|
-
# return True
|
|
123
|
-
|
|
124
|
-
# file_path = self.storage_path / hash.hex()
|
|
125
|
-
# file_path.write_bytes(data)
|
|
126
|
-
# return True
|
|
127
|
-
|
|
128
|
-
# def _object_get(self, hash: bytes) -> Optional[bytes]:
|
|
129
|
-
# local_data = self._local_object_get(hash)
|
|
130
|
-
# if local_data:
|
|
131
|
-
# return local_data
|
|
132
|
-
|
|
133
|
-
# # find the nearest peer route node to the hash and send an object request
|
|
134
|
-
# closest_peer = self._get_closest_local_peer(hash)
|
|
135
|
-
# if closest_peer:
|
|
136
|
-
# object_request_message = Message(topic=MessageTopic.OBJECT_REQUEST, content=hash)
|
|
137
|
-
# self.outgoing_queue.put((object_request_message.to_bytes(), self.peers[closest_peer].address))
|
|
138
|
-
|
|
139
|
-
# # wait for upto self.storage_get_relay_timeout seconds for the object to be stored/until local_object_get returns something
|
|
140
|
-
# start_time = time.time()
|
|
141
|
-
# while time.time() - start_time < self.storage_get_relay_timeout:
|
|
142
|
-
# # Check if the object has been stored locally
|
|
143
|
-
# local_data = self._local_object_get(hash)
|
|
144
|
-
# if local_data:
|
|
145
|
-
# return local_data
|
|
146
|
-
# # Sleep briefly to avoid hammering the local storage
|
|
147
|
-
# time.sleep(0.1)
|
|
148
|
-
|
|
149
|
-
# # If we reach here, the object was not received within the timeout period
|
|
150
|
-
# return None
|
|
151
|
-
|
|
152
|
-
# # RELAY METHODS
|
|
153
|
-
# def _relay_incoming_queue_populating(self):
|
|
154
|
-
# while True:
|
|
155
|
-
# try:
|
|
156
|
-
# data, addr = self.incoming_socket.recvfrom(4096)
|
|
157
|
-
# self.incoming_queue.put((data, addr))
|
|
158
|
-
# except Exception as e:
|
|
159
|
-
# print(f"Error in _relay_populate_incoming_queue: {e}")
|
|
160
|
-
|
|
161
|
-
# def _relay_incoming_queue_processing(self):
|
|
162
|
-
# while True:
|
|
163
|
-
# try:
|
|
164
|
-
# data, addr = self.incoming_queue.get()
|
|
165
|
-
# message = Message.from_bytes(data)
|
|
166
|
-
# match message.topic:
|
|
167
|
-
# case MessageTopic.PING:
|
|
168
|
-
# peer_pub_key = self.addresses.get(addr)
|
|
169
|
-
# if peer_pub_key in self.peers:
|
|
170
|
-
# self.peers[peer_pub_key].timestamp = datetime.now(timezone.utc)
|
|
171
|
-
# continue
|
|
172
|
-
|
|
173
|
-
# is_validator_flag = decode(message.body)
|
|
174
|
-
|
|
175
|
-
# if peer_pub_key not in self.peers:
|
|
176
|
-
# self._send_ping(addr)
|
|
177
|
-
|
|
178
|
-
# peer = Peer(my_sec_key=self.relay_secret_key, peer_pub_key=peer_pub_key)
|
|
179
|
-
# self.peers[peer.sender] = peer
|
|
180
|
-
# self.peer_route.add_peer(peer_pub_key)
|
|
181
|
-
# if is_validator_flag == [1]:
|
|
182
|
-
# self.validation_route.add_peer(peer_pub_key)
|
|
183
|
-
|
|
184
|
-
# if peer.timestamp < datetime.now(timezone.utc) - timedelta(minutes=5.0):
|
|
185
|
-
# self._send_ping(addr)
|
|
186
|
-
|
|
187
|
-
# case MessageTopic.OBJECT_REQUEST:
|
|
188
|
-
# try:
|
|
189
|
-
# object_request = ObjectRequest.from_bytes(message.body)
|
|
190
|
-
|
|
191
|
-
# match object_request.type:
|
|
192
|
-
# # -------------- OBJECT_GET --------------
|
|
193
|
-
# case ObjectRequestType.OBJECT_GET:
|
|
194
|
-
# object_hash = object_request.hash
|
|
195
|
-
|
|
196
|
-
# # 1. If we already have the object, return it.
|
|
197
|
-
# local_data = self._local_object_get(object_hash)
|
|
198
|
-
# if local_data is not None:
|
|
199
|
-
# resp = ObjectResponse(
|
|
200
|
-
# type=ObjectResponseType.OBJECT_FOUND,
|
|
201
|
-
# data=local_data,
|
|
202
|
-
# hash=object_hash
|
|
203
|
-
# )
|
|
204
|
-
# obj_res_msg = Message(topic=MessageTopic.OBJECT_RESPONSE, body=resp.to_bytes())
|
|
205
|
-
# self.outgoing_queue.put((obj_res_msg.to_bytes(), addr))
|
|
206
|
-
# return # done
|
|
207
|
-
|
|
208
|
-
# # 2. If we know a provider, tell the requester.
|
|
209
|
-
# if not hasattr(self, "storage_index") or not isinstance(self.storage_index, dict):
|
|
210
|
-
# self.storage_index = {}
|
|
211
|
-
# if object_hash in self.storage_index:
|
|
212
|
-
# provider_bytes = self.storage_index[object_hash]
|
|
213
|
-
# resp = ObjectResponse(
|
|
214
|
-
# type=ObjectResponseType.OBJECT_PROVIDER,
|
|
215
|
-
# data=provider_bytes,
|
|
216
|
-
# hash=object_hash
|
|
217
|
-
# )
|
|
218
|
-
# obj_res_msg = Message(topic=MessageTopic.OBJECT_RESPONSE, body=resp.to_bytes())
|
|
219
|
-
# self.outgoing_queue.put((obj_res_msg.to_bytes(), addr))
|
|
220
|
-
# return # done
|
|
221
|
-
|
|
222
|
-
# # 3. Otherwise, direct the requester to a peer nearer to the hash.
|
|
223
|
-
# nearest = self._get_closest_local_peer(object_hash)
|
|
224
|
-
# if nearest:
|
|
225
|
-
# nearest_key, nearest_peer = nearest
|
|
226
|
-
# peer_info = encode([
|
|
227
|
-
# nearest_key.public_bytes(
|
|
228
|
-
# encoding=serialization.Encoding.Raw,
|
|
229
|
-
# format=serialization.PublicFormat.Raw
|
|
230
|
-
# ),
|
|
231
|
-
# encode_ip_address(*nearest_peer.address)
|
|
232
|
-
# ])
|
|
233
|
-
# resp = ObjectResponse(
|
|
234
|
-
# type=ObjectResponseType.OBJECT_NEAREST_PEER,
|
|
235
|
-
# data=peer_info,
|
|
236
|
-
# hash=object_hash
|
|
237
|
-
# )
|
|
238
|
-
# obj_res_msg = Message(topic=MessageTopic.OBJECT_RESPONSE, body=resp.to_bytes())
|
|
239
|
-
# self.outgoing_queue.put((obj_res_msg.to_bytes(), addr))
|
|
240
|
-
|
|
241
|
-
# # -------------- OBJECT_PUT --------------
|
|
242
|
-
# case ObjectRequestType.OBJECT_PUT:
|
|
243
|
-
# # Ensure the hash is present / correct.
|
|
244
|
-
# obj_hash = object_request.hash or blake3.blake3(object_request.data).digest()
|
|
245
|
-
|
|
246
|
-
# nearest = self._get_closest_local_peer(obj_hash)
|
|
247
|
-
# # If a strictly nearer peer exists, forward the PUT.
|
|
248
|
-
# if nearest and self._is_closer_than_local_peers(obj_hash, nearest[0]):
|
|
249
|
-
# fwd_req = ObjectRequest(
|
|
250
|
-
# type=ObjectRequestType.OBJECT_PUT,
|
|
251
|
-
# data=object_request.data,
|
|
252
|
-
# hash=obj_hash
|
|
253
|
-
# )
|
|
254
|
-
# obj_req_msg = Message(topic=MessageTopic.OBJECT_REQUEST, body=fwd_req.to_bytes())
|
|
255
|
-
# self.outgoing_queue.put((obj_req_msg.to_bytes(), nearest[1].address))
|
|
256
|
-
# else:
|
|
257
|
-
# # We are closest → remember who can provide the object.
|
|
258
|
-
# peer_pub_key = self.addresses.get(addr)
|
|
259
|
-
# provider_record = encode([
|
|
260
|
-
# peer_pub_key.public_bytes(),
|
|
261
|
-
# encode_ip_address(*addr)
|
|
262
|
-
# ])
|
|
263
|
-
# if not hasattr(self, "storage_index") or not isinstance(self.storage_index, dict):
|
|
264
|
-
# self.storage_index = {}
|
|
265
|
-
# self.storage_index[obj_hash] = provider_record
|
|
266
|
-
|
|
267
|
-
# except Exception as e:
|
|
268
|
-
# print(f"Error processing OBJECT_REQUEST: {e}")
|
|
269
|
-
|
|
270
|
-
# case MessageTopic.OBJECT_RESPONSE:
|
|
271
|
-
# try:
|
|
272
|
-
# object_response = ObjectResponse.from_bytes(message.body)
|
|
273
|
-
# if object_response.hash not in self.object_request_queue:
|
|
274
|
-
# continue
|
|
275
|
-
|
|
276
|
-
# match object_response.type:
|
|
277
|
-
# case ObjectResponseType.OBJECT_FOUND:
|
|
278
|
-
# if object_response.hash != blake3.blake3(object_response.data).digest():
|
|
279
|
-
# continue
|
|
280
|
-
# self.object_request_queue.remove(object_response.hash)
|
|
281
|
-
# self._local_object_put(object_response.hash, object_response.data)
|
|
282
|
-
|
|
283
|
-
# case ObjectResponseType.OBJECT_PROVIDER:
|
|
284
|
-
# _provider_public_key, provider_address = decode(object_response.data)
|
|
285
|
-
# provider_ip, provider_port = decode_ip_address(provider_address)
|
|
286
|
-
# obj_req_msg = Message(topic=MessageTopic.OBJECT_REQUEST, body=object_hash)
|
|
287
|
-
# self.outgoing_queue.put((obj_req_msg.to_bytes(), (provider_ip, provider_port)))
|
|
288
|
-
|
|
289
|
-
# case ObjectResponseType.OBJECT_NEAREST_PEER:
|
|
290
|
-
# # -- decode the peer info sent back
|
|
291
|
-
# nearest_peer_public_key_bytes, nearest_peer_address = (
|
|
292
|
-
# decode(object_response.data)
|
|
293
|
-
# )
|
|
294
|
-
# nearest_peer_public_key = X25519PublicKey.from_public_bytes(
|
|
295
|
-
# nearest_peer_public_key_bytes
|
|
296
|
-
# )
|
|
297
|
-
|
|
298
|
-
# # -- XOR-distance between the object hash and the candidate peer
|
|
299
|
-
# peer_bytes = nearest_peer_public_key.public_bytes(
|
|
300
|
-
# encoding=serialization.Encoding.Raw,
|
|
301
|
-
# format=serialization.PublicFormat.Raw,
|
|
302
|
-
# )
|
|
303
|
-
# object_response_xor = sum(
|
|
304
|
-
# a ^ b for a, b in zip(object_response.hash, peer_bytes)
|
|
305
|
-
# )
|
|
306
|
-
|
|
307
|
-
# # -- forward only if that peer is strictly nearer than any local peer
|
|
308
|
-
# if self._is_closer_than_local_peers(
|
|
309
|
-
# object_response.hash, nearest_peer_public_key
|
|
310
|
-
# ):
|
|
311
|
-
# nearest_peer_ip, nearest_peer_port = decode_ip_address(nearest_peer_address)
|
|
312
|
-
# obj_req_msg = Message(topic=MessageTopic.OBJECT_REQUEST, content=object_response.hash)
|
|
313
|
-
# self.outgoing_queue.put((obj_req_msg.to_bytes(), (nearest_peer_ip, nearest_peer_port),)
|
|
314
|
-
# )
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
# except Exception as e:
|
|
318
|
-
# print(f"Error processing OBJECT_RESPONSE: {e}")
|
|
319
|
-
|
|
320
|
-
# except Exception as e:
|
|
321
|
-
# print(f"Error processing message: {e}")
|
|
322
|
-
|
|
323
|
-
# def _relay_outgoing_queue_processor(self):
|
|
324
|
-
# while True:
|
|
325
|
-
# try:
|
|
326
|
-
# data, addr = self.outgoing_queue.get()
|
|
327
|
-
# self.outgoing_socket.sendto(data, addr)
|
|
328
|
-
# except Exception as e:
|
|
329
|
-
# print(f"Error sending message: {e}")
|
|
330
|
-
|
|
331
|
-
# def _relay_peer_manager(self):
|
|
332
|
-
# while True:
|
|
333
|
-
# try:
|
|
334
|
-
# time.sleep(60)
|
|
335
|
-
# for peer in self.peers.values():
|
|
336
|
-
# if (datetime.now(timezone.utc) - peer.timestamp).total_seconds() > 900:
|
|
337
|
-
# del self.peers[peer.sender]
|
|
338
|
-
# self.peer_route.remove_peer(peer.sender)
|
|
339
|
-
# if peer.sender in self.validation_route.buckets:
|
|
340
|
-
# self.validation_route.remove_peer(peer.sender)
|
|
341
|
-
# except Exception as e:
|
|
342
|
-
# print(f"Error in _peer_manager_thread: {e}")
|
|
343
|
-
|
|
344
|
-
# def _send_ping(self, addr: Tuple[str, int]):
|
|
345
|
-
# is_validator_flag = encode([1] if self.validation_secret_key else [0])
|
|
346
|
-
# ping_message = Message(topic=MessageTopic.PING, content=is_validator_flag)
|
|
347
|
-
# self.outgoing_queue.put((ping_message.to_bytes(), addr))
|
|
348
|
-
|
|
349
|
-
# def _get_closest_local_peer(self, hash: bytes) -> Optional[Tuple[X25519PublicKey, Peer]]:
|
|
350
|
-
# # Find the globally closest peer using XOR distance
|
|
351
|
-
# closest_peer = None
|
|
352
|
-
# closest_distance = None
|
|
1
|
+
"""Core Astreum Node implementation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import threading
|
|
6
|
+
import uuid
|
|
7
|
+
from typing import Dict
|
|
8
|
+
|
|
9
|
+
from astreum.communication.start import connect_to_network_and_verify
|
|
10
|
+
from astreum.communication.models.peer import (
|
|
11
|
+
add_peer as peers_add_peer,
|
|
12
|
+
replace_peer as peers_replace_peer,
|
|
13
|
+
get_peer as peers_get_peer,
|
|
14
|
+
remove_peer as peers_remove_peer,
|
|
15
|
+
)
|
|
16
|
+
from astreum.consensus.start import process_blocks_and_transactions
|
|
17
|
+
from astreum.machine import Expr, high_eval, low_eval, script_eval
|
|
18
|
+
from astreum.machine.models.environment import Env, env_get, env_set
|
|
19
|
+
from astreum.machine.models.expression import get_expr_list_from_storage
|
|
20
|
+
from astreum.storage.models.atom import get_atom_list_from_storage
|
|
21
|
+
from astreum.storage.actions.get import (
|
|
22
|
+
_hot_storage_get,
|
|
23
|
+
_cold_storage_get,
|
|
24
|
+
_network_get,
|
|
25
|
+
storage_get,
|
|
26
|
+
local_get,
|
|
27
|
+
)
|
|
28
|
+
from astreum.storage.actions.set import (
|
|
29
|
+
_hot_storage_set,
|
|
30
|
+
_cold_storage_set,
|
|
31
|
+
_network_set,
|
|
32
|
+
)
|
|
33
|
+
from astreum.storage.requests import add_atom_req, has_atom_req, pop_atom_req
|
|
34
|
+
from astreum.storage.setup import storage_setup
|
|
35
|
+
from astreum.utils.config import config_setup
|
|
36
|
+
from astreum.utils.logging import logging_setup
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Node:
|
|
40
|
+
def __init__(self, config: dict = {}):
|
|
41
|
+
self.config = config_setup(config=config)
|
|
353
42
|
|
|
354
|
-
|
|
355
|
-
# for peer_key, peer in self.peers.items():
|
|
356
|
-
# # Calculate XOR distance between hash and peer's public key
|
|
357
|
-
# peer_bytes = peer_key.public_bytes(encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw)
|
|
358
|
-
# # XOR each byte and sum them to get a total distance
|
|
359
|
-
# distance = sum(a ^ b for a, b in zip(hash, peer_bytes))
|
|
360
|
-
# # Update the closest peer if the distance is smaller
|
|
361
|
-
# if closest_distance is None or distance < closest_distance:
|
|
362
|
-
# closest_distance = distance
|
|
363
|
-
# closest_peer = (peer_key, peer)
|
|
364
|
-
|
|
365
|
-
# return closest_peer
|
|
43
|
+
self.logger = logging_setup(self.config)
|
|
366
44
|
|
|
367
|
-
|
|
45
|
+
self.logger.info("Starting Astreum Node")
|
|
368
46
|
|
|
369
|
-
#
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
# # If we have no local peers, the foreign peer is closer by default
|
|
373
|
-
# if closest_local_peer is None:
|
|
374
|
-
# return True
|
|
375
|
-
|
|
376
|
-
# # Calculate XOR distance for the foreign peer
|
|
377
|
-
# foreign_peer_bytes = foreign_peer_public_key.public_bytes(encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw)
|
|
378
|
-
# foreign_distance = sum(a ^ b for a, b in zip(hash, foreign_peer_bytes))
|
|
379
|
-
|
|
380
|
-
# # Get the closest local peer key and calculate its distance
|
|
381
|
-
# closest_peer_key, _ = closest_local_peer
|
|
382
|
-
# closest_peer_bytes = closest_peer_key.public_bytes(encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw)
|
|
383
|
-
# local_distance = sum(a ^ b for a, b in zip(hash, closest_peer_bytes))
|
|
384
|
-
|
|
385
|
-
# # Return True if the foreign peer is closer (has smaller XOR distance)
|
|
386
|
-
# return foreign_distance < local_distance
|
|
387
|
-
|
|
388
|
-
# # MACHINE
|
|
389
|
-
# def _machine_setup(self):
|
|
390
|
-
# self.environments: Dict[uuid.UUID, Env] = {}
|
|
391
|
-
# self.machine_environments_lock = threading.Lock()
|
|
392
|
-
|
|
393
|
-
# def machine_create_environment(self, parent_id: Optional[uuid.UUID] = None) -> uuid.UUID:
|
|
394
|
-
# env_id = uuid.uuid4()
|
|
395
|
-
# with self.machine_environments_lock:
|
|
396
|
-
# while env_id in self.environments:
|
|
397
|
-
# env_id = uuid.uuid4()
|
|
398
|
-
# self.environments[env_id] = Env(parent_id=parent_id)
|
|
399
|
-
# return env_id
|
|
400
|
-
|
|
401
|
-
# def machine_get_or_create_environment(
|
|
402
|
-
# self,
|
|
403
|
-
# env_id: Optional[uuid.UUID] = None,
|
|
404
|
-
# parent_id: Optional[uuid.UUID] = None,
|
|
405
|
-
# max_exprs: Optional[int] = None
|
|
406
|
-
# ) -> uuid.UUID:
|
|
407
|
-
# with self.machine_environments_lock:
|
|
408
|
-
# if env_id is not None and env_id in self.environments:
|
|
409
|
-
# return env_id
|
|
410
|
-
# new_id = env_id if env_id is not None else uuid.uuid4()
|
|
411
|
-
# while new_id in self.environments:
|
|
412
|
-
# new_id = uuid.uuid4()
|
|
413
|
-
# self.environments[new_id] = Env(parent_id=parent_id, max_exprs=max_exprs)
|
|
414
|
-
# return new_id
|
|
415
|
-
|
|
416
|
-
# def machine_delete_environment(self, env_id: uuid.UUID) -> bool:
|
|
417
|
-
# with self.machine_environments_lock:
|
|
418
|
-
# removed = self.environments.pop(env_id, None)
|
|
419
|
-
# return removed is not None
|
|
420
|
-
|
|
421
|
-
# def machine_expr_get(self, env_id: uuid.UUID, name: str) -> Optional[Expr]:
|
|
422
|
-
# with self.machine_environments_lock:
|
|
423
|
-
# cur = self.environments.get(env_id)
|
|
424
|
-
# while cur is not None:
|
|
425
|
-
# if name in cur.data:
|
|
426
|
-
# return cur.data[name]
|
|
427
|
-
# if cur.parent_id:
|
|
428
|
-
# cur = self.environments.get(cur.parent_id)
|
|
429
|
-
# else:
|
|
430
|
-
# cur = None
|
|
431
|
-
# return None
|
|
432
|
-
|
|
433
|
-
# def machine_expr_put(self, env_id: uuid.UUID, name: str, expr: Expr):
|
|
434
|
-
# with self.machine_environments_lock:
|
|
435
|
-
# env = self.environments.get(env_id)
|
|
436
|
-
# if env is None:
|
|
437
|
-
# return False
|
|
438
|
-
# env.put(name, expr)
|
|
439
|
-
# return True
|
|
440
|
-
|
|
441
|
-
# def machine_expr_eval(self, env_id: uuid.UUID, expr: Expr) -> Expr:
|
|
442
|
-
# if isinstance(expr, Expr.Boolean) or isinstance(expr, Expr.Integer) or isinstance(expr, Expr.String) or isinstance(expr, Expr.Error):
|
|
443
|
-
# return expr
|
|
444
|
-
|
|
445
|
-
# elif isinstance(expr, Expr.Symbol):
|
|
446
|
-
# value = self.machine_expr_get(env_id=env_id, name=expr.value)
|
|
447
|
-
# if value:
|
|
448
|
-
# return value
|
|
449
|
-
# else:
|
|
450
|
-
# return Expr.Error(message=f"unbound symbol '{expr.value}'", origin=expr)
|
|
451
|
-
|
|
452
|
-
# elif isinstance(expr, Expr.ListExpr):
|
|
453
|
-
# if len(expr.elements) == 0:
|
|
454
|
-
# return expr
|
|
455
|
-
# if len(expr.elements) == 1:
|
|
456
|
-
# return self.machine_expr_eval(expr=expr.elements[0], env_id=env_id)
|
|
457
|
-
# first = expr.elements[0]
|
|
458
|
-
# if isinstance(first, Expr.Symbol):
|
|
459
|
-
# first_symbol_value = self.machine_expr_get(env_id=env_id, name=first.value)
|
|
460
|
-
|
|
461
|
-
# if first_symbol_value and not isinstance(first_symbol_value, Expr.Function):
|
|
462
|
-
# evaluated_elements = [self.machine_expr_eval(env_id=env_id, expr=e) for e in expr.elements]
|
|
463
|
-
# return Expr.ListExpr(evaluated_elements)
|
|
464
|
-
|
|
465
|
-
# elif first.value == "def":
|
|
466
|
-
# args = expr.elements[1:]
|
|
467
|
-
# if len(args) != 2:
|
|
468
|
-
# return Expr.Error("def expects key value", origin=expr)
|
|
469
|
-
# if not isinstance(args[0], Expr.Symbol):
|
|
470
|
-
# return Expr.Error(message="first argument to 'def' must be a symbol", origin=args[0])
|
|
471
|
-
# result = self.machine_expr_eval(env_id=env_id, expr=args[1])
|
|
472
|
-
# if isinstance(result, Expr.Error):
|
|
473
|
-
# return result
|
|
474
|
-
|
|
475
|
-
# self.machine_expr_put(env_id=env_id, name=args[0].value, expr=result)
|
|
476
|
-
# return result
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
# ## DEF: (def x 1) -> ()
|
|
480
|
-
|
|
481
|
-
# ## GET: (x) -> 1
|
|
482
|
-
|
|
483
|
-
# ## ADD: (+ 1 2) -> (+ 3) -> 3
|
|
484
|
-
|
|
485
|
-
# ## NAND: (~& 1 1) -> (~& 0) -> 0
|
|
486
|
-
|
|
487
|
-
# ##
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
# ## List: ints -> (1 2)
|
|
491
|
-
# # push: (list.push 3 ints) -> (1 2 3) / (list.push 0 0 ints) -> (0 1 2)
|
|
492
|
-
# elif first.value == "list.push":
|
|
493
|
-
# args = expr.elements[1:]
|
|
494
|
-
# if len(args) == 2:
|
|
495
|
-
# val_expr, list_expr = args
|
|
496
|
-
# idx = None
|
|
497
|
-
# elif len(args) == 3:
|
|
498
|
-
# idx_expr, val_expr, list_expr = args
|
|
499
|
-
# idx = self.machine_expr_eval(env_id, idx_expr)
|
|
500
|
-
# if isinstance(idx, Expr.Error): return idx
|
|
501
|
-
# if not isinstance(idx, Expr.IntExpr):
|
|
502
|
-
# return Expr.Error("index must be int", origin=idx_expr)
|
|
503
|
-
# idx = idx.value
|
|
504
|
-
# else:
|
|
505
|
-
# return Expr.Error("list.push expects (value list) or (index value list)", origin=expr)
|
|
506
|
-
|
|
507
|
-
# lst = self.machine_expr_eval(env_id, list_expr)
|
|
508
|
-
# if isinstance(lst, Expr.Error): return lst
|
|
509
|
-
# if not isinstance(lst, Expr.ListExpr):
|
|
510
|
-
# return Expr.Error("last arg to list.push must be a list", origin=list_expr)
|
|
511
|
-
|
|
512
|
-
# val = self.machine_expr_eval(env_id, val_expr)
|
|
513
|
-
# if isinstance(val, Expr.Error): return val
|
|
514
|
-
|
|
515
|
-
# elems = list(lst.elements)
|
|
516
|
-
# if idx is None:
|
|
517
|
-
# elems.append(val)
|
|
518
|
-
# else:
|
|
519
|
-
# if idx < 0 or idx > len(elems):
|
|
520
|
-
# return Expr.Error("index out of range", origin=idx_expr)
|
|
521
|
-
# elems.insert(idx, val)
|
|
522
|
-
# return Expr.ListExpr(elems)
|
|
523
|
-
|
|
524
|
-
# # pop: (list.pop 1 ints) -> 2
|
|
525
|
-
# elif first.value == "list.pop":
|
|
526
|
-
# if len(expr.elements) < 3:
|
|
527
|
-
# return Expr.Error("list.pop expects index list", origin=expr)
|
|
528
|
-
|
|
529
|
-
# idx_expr, list_expr = expr.elements[1], expr.elements[2]
|
|
530
|
-
# idx = self.machine_expr_eval(env_id, idx_expr)
|
|
531
|
-
# if isinstance(idx, Expr.Error): return idx
|
|
532
|
-
# if not isinstance(idx, Expr.IntExpr):
|
|
533
|
-
# return Expr.Error("index must be int", origin=idx_expr)
|
|
534
|
-
# idx = idx.value
|
|
535
|
-
|
|
536
|
-
# lst = self.machine_expr_eval(env_id, list_expr)
|
|
537
|
-
# if isinstance(lst, Expr.Error): return lst
|
|
538
|
-
# if not isinstance(lst, Expr.ListExpr):
|
|
539
|
-
# return Expr.Error("second arg to list.pop must be a list", origin=list_expr)
|
|
47
|
+
# Chain Configuration
|
|
48
|
+
self.logger.info(f"Chain configured as: {self.config["chain"]} ({self.config["chain_id"]})")
|
|
540
49
|
|
|
541
|
-
#
|
|
542
|
-
|
|
543
|
-
# return Expr.Error("index out of range", origin=idx_expr)
|
|
544
|
-
# del elems[idx]
|
|
545
|
-
# return Expr.ListExpr(elems)
|
|
50
|
+
# Storage Setup
|
|
51
|
+
storage_setup(self, config=self.config)
|
|
546
52
|
|
|
547
|
-
#
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
53
|
+
# Machine Setup
|
|
54
|
+
self.environments: Dict[uuid.UUID, Env] = {}
|
|
55
|
+
self.machine_environments_lock = threading.RLock()
|
|
56
|
+
self.is_connected = False
|
|
551
57
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
# if isinstance(idx, Expr.Error): return idx
|
|
555
|
-
# if not isinstance(idx, Expr.IntExpr):
|
|
556
|
-
# return Expr.Error("index must be int", origin=idx_expr)
|
|
557
|
-
# idx = idx.value
|
|
58
|
+
connect = connect_to_network_and_verify
|
|
59
|
+
validate = process_blocks_and_transactions
|
|
558
60
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
# return Expr.Error("second arg to list.get must be a list", origin=list_expr)
|
|
61
|
+
low_eval = low_eval
|
|
62
|
+
high_eval = high_eval
|
|
63
|
+
script_eval = script_eval
|
|
563
64
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
# return lst.elements[idx]
|
|
65
|
+
env_get = env_get
|
|
66
|
+
env_set = env_set
|
|
567
67
|
|
|
568
|
-
#
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
# idx = self.machine_expr_eval(env_id, idx_expr)
|
|
574
|
-
# if isinstance(idx, Expr.Error): return idx
|
|
575
|
-
# if not isinstance(idx, Expr.IntExpr):
|
|
576
|
-
# return Expr.Error("index must be int", origin=idx_expr)
|
|
577
|
-
# idx = idx.value
|
|
68
|
+
# Storage
|
|
69
|
+
## Get
|
|
70
|
+
_hot_storage_get = _hot_storage_get
|
|
71
|
+
_cold_storage_get = _cold_storage_get
|
|
72
|
+
_network_get = _network_get
|
|
578
73
|
|
|
579
|
-
|
|
580
|
-
|
|
74
|
+
## Set
|
|
75
|
+
_hot_storage_set = _hot_storage_set
|
|
76
|
+
_cold_storage_set = _cold_storage_set
|
|
77
|
+
_network_set = _network_set
|
|
581
78
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
# if not isinstance(lst, Expr.ListExpr):
|
|
585
|
-
# return Expr.Error("third arg to list.set must be a list", origin=list_expr)
|
|
79
|
+
storage_get = storage_get
|
|
80
|
+
local_get = local_get
|
|
586
81
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
# return Expr.Error("index out of range", origin=idx_expr)
|
|
590
|
-
# elems[idx] = val
|
|
591
|
-
# return Expr.ListExpr(elems)
|
|
82
|
+
get_expr_list_from_storage = get_expr_list_from_storage
|
|
83
|
+
get_atom_list_from_storage = get_atom_list_from_storage
|
|
592
84
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
# return Expr.Error("list.each expects fn list", origin=expr)
|
|
597
|
-
# fn_expr, list_expr = expr.elements[1], expr.elements[2]
|
|
598
|
-
# lst = self.machine_expr_eval(env_id, list_expr)
|
|
599
|
-
# if isinstance(lst, Expr.Error):
|
|
600
|
-
# return lst
|
|
601
|
-
# if not isinstance(lst, Expr.ListExpr):
|
|
602
|
-
# return Expr.Error("second arg to list.each must be a list", origin=list_expr)
|
|
85
|
+
add_atom_req = add_atom_req
|
|
86
|
+
has_atom_req = has_atom_req
|
|
87
|
+
pop_atom_req = pop_atom_req
|
|
603
88
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
# return Expr.ListExpr([])
|
|
609
|
-
|
|
610
|
-
# ### fold: (list.fold fn init list) / (list.fold + 0 ints) -> 3
|
|
611
|
-
# elif first.value == "list.fold":
|
|
612
|
-
# fn_expr, init_expr, list_expr = expr.elements[1], expr.elements[2], expr.elements[3]
|
|
613
|
-
# acc = self.machine_expr_eval(env_id, init_expr)
|
|
614
|
-
# if isinstance(acc, Expr.Error):
|
|
615
|
-
# return acc
|
|
616
|
-
|
|
617
|
-
# lst = self.machine_expr_eval(env_id, list_expr)
|
|
618
|
-
# if isinstance(lst, Expr.Error):
|
|
619
|
-
# return lst
|
|
620
|
-
# if not isinstance(lst, Expr.ListExpr):
|
|
621
|
-
# return Expr.Error("third arg to list.fold must be a list", origin=list_expr)
|
|
622
|
-
|
|
623
|
-
# for el in lst.elements:
|
|
624
|
-
# call = Expr.ListExpr([fn_expr, acc, el])
|
|
625
|
-
# res = self.machine_expr_eval(env_id, call)
|
|
626
|
-
# if isinstance(res, Expr.Error):
|
|
627
|
-
# return res
|
|
628
|
-
# acc = res
|
|
629
|
-
|
|
630
|
-
# return acc
|
|
631
|
-
|
|
632
|
-
# ### sort: (list.sort fn list) / (list.sort (fn (a b) (a < b)) ints) -> (2 1)
|
|
633
|
-
# elif first.value == "list.sort":
|
|
634
|
-
# if len(expr.elements) < 3:
|
|
635
|
-
# return Expr.Error("list.sort fn list", origin=expr)
|
|
636
|
-
# fn_e, lst_e = expr.elements[1], expr.elements[2]
|
|
637
|
-
|
|
638
|
-
# lst = self.machine_expr_eval(env_id, lst_e)
|
|
639
|
-
# if isinstance(lst, Expr.Error): return lst
|
|
640
|
-
# if not isinstance(lst, Expr.ListExpr):
|
|
641
|
-
# return Expr.Error("second arg must be list", origin=lst_e)
|
|
642
|
-
|
|
643
|
-
# elems = list(lst.elements)
|
|
644
|
-
# for i in range(1, len(elems)):
|
|
645
|
-
# j = i
|
|
646
|
-
# while j > 0:
|
|
647
|
-
# cmp_res = self.machine_expr_eval(
|
|
648
|
-
# env_id,
|
|
649
|
-
# Expr.ListExpr([fn_e, elems[j-1], elems[j]])
|
|
650
|
-
# )
|
|
651
|
-
# if isinstance(cmp_res, Expr.Error): return cmp_res
|
|
652
|
-
# if not isinstance(cmp_res, Expr.BoolExpr):
|
|
653
|
-
# return Expr.Error("comparator must return bool", origin=fn_e)
|
|
654
|
-
|
|
655
|
-
# if cmp_res.value:
|
|
656
|
-
# elems[j-1], elems[j] = elems[j], elems[j-1]
|
|
657
|
-
# j -= 1
|
|
658
|
-
# else:
|
|
659
|
-
# break
|
|
660
|
-
# return Expr.ListExpr(elems)
|
|
661
|
-
|
|
662
|
-
# ### len: (list.len list) -> Int / (list.len ints) -> Integer(2)
|
|
663
|
-
# elif first.value == "list.len":
|
|
664
|
-
# if len(expr.elements) < 2:
|
|
665
|
-
# return Expr.Error("list.len list", origin=expr)
|
|
666
|
-
# lst_e = expr.elements[1]
|
|
667
|
-
# lst = self.machine_expr_eval(env_id, lst_e)
|
|
668
|
-
# if isinstance(lst, Expr.Error): return lst
|
|
669
|
-
# if not isinstance(lst, Expr.ListExpr):
|
|
670
|
-
# return Expr.Error("arg must be list", origin=lst_e)
|
|
671
|
-
# return Expr.Integer(len(lst.elements))
|
|
672
|
-
|
|
673
|
-
# ## Integer
|
|
674
|
-
# ### add
|
|
675
|
-
# elif first.value == "+":
|
|
676
|
-
# args = expr.elements[1:]
|
|
677
|
-
# if not args:
|
|
678
|
-
# return Expr.Error("'+' expects at least 1 argument", origin=expr)
|
|
679
|
-
# vals = [self.machine_expr_eval(env_id=env_id, expr=a) for a in args]
|
|
680
|
-
# for v in vals:
|
|
681
|
-
# if isinstance(v, Expr.Error): return v
|
|
682
|
-
# if not isinstance(v, Expr.Integer):
|
|
683
|
-
# return Expr.Error("'+' only accepts integer operands", origin=v)
|
|
684
|
-
# return Expr.Integer(abs(vals[0].value) if len(vals) == 1
|
|
685
|
-
# else sum(v.value for v in vals))
|
|
686
|
-
|
|
687
|
-
# elif first.value == "-":
|
|
688
|
-
# args = expr.elements[1:]
|
|
689
|
-
# if not args:
|
|
690
|
-
# return Expr.Error("'-' expects at least 1 argument", origin=expr)
|
|
691
|
-
# vals = [self.machine_expr_eval(env_id=env_id, expr=a) for a in args]
|
|
692
|
-
# for v in vals:
|
|
693
|
-
# if isinstance(v, Expr.Error): return v
|
|
694
|
-
# if not isinstance(v, Expr.Integer):
|
|
695
|
-
# return Expr.Error("'-' only accepts integer operands", origin=v)
|
|
696
|
-
# if len(vals) == 1:
|
|
697
|
-
# return Expr.Integer(-vals[0].value)
|
|
698
|
-
# result = vals[0].value
|
|
699
|
-
# for v in vals[1:]:
|
|
700
|
-
# result -= v.value
|
|
701
|
-
# return Expr.Integer(result)
|
|
702
|
-
|
|
703
|
-
# elif first.value == "/":
|
|
704
|
-
# args = expr.elements[1:]
|
|
705
|
-
# if len(args) < 2:
|
|
706
|
-
# return Expr.Error("'/' expects at least 2 arguments", origin=expr)
|
|
707
|
-
# vals = [self.machine_expr_eval(env_id=env_id, expr=a) for a in args]
|
|
708
|
-
# for v in vals:
|
|
709
|
-
# if isinstance(v, Expr.Error): return v
|
|
710
|
-
# if not isinstance(v, Expr.Integer):
|
|
711
|
-
# return Expr.Error("'/' only accepts integer operands", origin=v)
|
|
712
|
-
# result = vals[0].value
|
|
713
|
-
# for v in vals[1:]:
|
|
714
|
-
# if v.value == 0:
|
|
715
|
-
# return Expr.Error("division by zero", origin=v)
|
|
716
|
-
# if result % v.value:
|
|
717
|
-
# return Expr.Error("non-exact division", origin=expr)
|
|
718
|
-
# result //= v.value
|
|
719
|
-
# return Expr.Integer(result)
|
|
720
|
-
|
|
721
|
-
# elif first.value == "%":
|
|
722
|
-
# if len(expr.elements) != 3:
|
|
723
|
-
# return Expr.Error("'%' expects exactly 2 arguments", origin=expr)
|
|
724
|
-
# a = self.machine_expr_eval(env_id=env_id, expr=expr.elements[1])
|
|
725
|
-
# b = self.machine_expr_eval(env_id=env_id, expr=expr.elements[2])
|
|
726
|
-
# for v in (a, b):
|
|
727
|
-
# if isinstance(v, Expr.Error): return v
|
|
728
|
-
# if not isinstance(v, Expr.Integer):
|
|
729
|
-
# return Expr.Error("'%' only accepts integer operands", origin=v)
|
|
730
|
-
# if b.value == 0:
|
|
731
|
-
# return Expr.Error("division by zero", origin=expr.elements[2])
|
|
732
|
-
# return Expr.Integer(a.value % b.value)
|
|
733
|
-
|
|
734
|
-
# elif first.value in ("=", "!=", ">", "<", ">=", "<="):
|
|
735
|
-
# args = expr.elements[1:]
|
|
736
|
-
# if len(args) != 2:
|
|
737
|
-
# return Expr.Error(f"'{first.value}' expects exactly 2 arguments", origin=expr)
|
|
738
|
-
|
|
739
|
-
# left = self.machine_expr_eval(env_id=env_id, expr=args[0])
|
|
740
|
-
# right = self.machine_expr_eval(env_id=env_id, expr=args[1])
|
|
741
|
-
|
|
742
|
-
# for v in (left, right):
|
|
743
|
-
# if isinstance(v, Expr.Error):
|
|
744
|
-
# return v
|
|
745
|
-
# if not isinstance(v, Expr.Integer):
|
|
746
|
-
# return Expr.Error(f"'{first.value}' only accepts integer operands", origin=v)
|
|
747
|
-
|
|
748
|
-
# a, b = left.value, right.value
|
|
749
|
-
# match first.value:
|
|
750
|
-
# case "=": res = a == b
|
|
751
|
-
# case "!=": res = a != b
|
|
752
|
-
# case ">": res = a > b
|
|
753
|
-
# case "<": res = a < b
|
|
754
|
-
# case ">=": res = a >= b
|
|
755
|
-
# case "<=": res = a <= b
|
|
756
|
-
|
|
757
|
-
# return Expr.Boolean(res)
|
|
758
|
-
|
|
759
|
-
# if isinstance(first, Expr.Function):
|
|
760
|
-
# arg_exprs = expr.elements[1:]
|
|
761
|
-
# if len(arg_exprs) != len(first.params):
|
|
762
|
-
# return Expr.Error(f"arity mismatch: expected {len(first.params)}, got {len(arg_exprs)}", origin=expr)
|
|
763
|
-
|
|
764
|
-
# call_env = self.machine_create_environment(parent_id=env_id)
|
|
765
|
-
# for name, aexpr in zip(first.params, arg_exprs):
|
|
766
|
-
# val = self.machine_expr_eval(env_id, aexpr)
|
|
767
|
-
# if isinstance(val, Expr.Error): return val
|
|
768
|
-
# self.machine_expr_put(call_env, name, val)
|
|
769
|
-
|
|
770
|
-
# return self.machine_expr_eval(env_id=call_env, expr=first.body)
|
|
771
|
-
|
|
772
|
-
# else:
|
|
773
|
-
# evaluated_elements = [self.machine_expr_eval(env_id=env_id, expr=e) for e in expr.elements]
|
|
774
|
-
# return Expr.ListExpr(evaluated_elements)
|
|
775
|
-
|
|
776
|
-
# elif isinstance(expr, Expr.Function):
|
|
777
|
-
# return expr
|
|
778
|
-
|
|
779
|
-
# else:
|
|
780
|
-
# raise ValueError(f"Unknown expression type: {type(expr)}")
|
|
781
|
-
|
|
89
|
+
add_peer = peers_add_peer
|
|
90
|
+
replace_peer = peers_replace_peer
|
|
91
|
+
get_peer = peers_get_peer
|
|
92
|
+
remove_peer = peers_remove_peer
|