astreum 0.1.17__py3-none-any.whl → 0.1.18__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.
Potentially problematic release.
This version of astreum might be problematic. Click here for more details.
- astreum/node/__init__.py +417 -422
- {astreum-0.1.17.dist-info → astreum-0.1.18.dist-info}/METADATA +1 -1
- {astreum-0.1.17.dist-info → astreum-0.1.18.dist-info}/RECORD +6 -6
- {astreum-0.1.17.dist-info → astreum-0.1.18.dist-info}/WHEEL +0 -0
- {astreum-0.1.17.dist-info → astreum-0.1.18.dist-info}/licenses/LICENSE +0 -0
- {astreum-0.1.17.dist-info → astreum-0.1.18.dist-info}/top_level.txt +0 -0
astreum/node/__init__.py
CHANGED
|
@@ -1,480 +1,475 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
from
|
|
6
|
-
import json
|
|
7
|
-
from cryptography.hazmat.primitives.asymmetric import ed25519
|
|
8
|
-
from cryptography.hazmat.primitives import serialization
|
|
1
|
+
# import time
|
|
2
|
+
# import threading
|
|
3
|
+
# from typing import List
|
|
4
|
+
# from cryptography.hazmat.primitives.asymmetric import ed25519
|
|
5
|
+
# from cryptography.hazmat.primitives import serialization
|
|
9
6
|
|
|
10
|
-
from .relay import Relay, Topic
|
|
11
|
-
from .
|
|
12
|
-
from .
|
|
13
|
-
from .
|
|
14
|
-
from .
|
|
15
|
-
from astreum.lispeum.storage import store_expr, get_expr_from_storage
|
|
7
|
+
# from .relay import Relay, Topic
|
|
8
|
+
# from .machine import AstreumMachine
|
|
9
|
+
# from .utils import hash_data
|
|
10
|
+
# from .validation.block import Block
|
|
11
|
+
# from .storage import Storage
|
|
16
12
|
|
|
17
|
-
# Import our validation components using the new functional approach
|
|
18
|
-
from .validation import (
|
|
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
|
-
from .validation.adapter import BlockAdapter, TransactionAdapter, AccountAdapter
|
|
13
|
+
# # Import our validation components using the new functional approach
|
|
14
|
+
# from .validation import (
|
|
15
|
+
# validate_block,
|
|
16
|
+
# create_block,
|
|
17
|
+
# create_genesis_block,
|
|
18
|
+
# compute_vdf,
|
|
19
|
+
# verify_vdf,
|
|
20
|
+
# select_validator,
|
|
21
|
+
# select_validator_for_slot,
|
|
22
|
+
# get_validator_stake,
|
|
23
|
+
# is_validator,
|
|
24
|
+
# VALIDATION_ADDRESS,
|
|
25
|
+
# BURN_ADDRESS,
|
|
26
|
+
# MIN_STAKE_AMOUNT,
|
|
27
|
+
# SLOT_DURATION,
|
|
28
|
+
# VDF_DIFFICULTY
|
|
29
|
+
# )
|
|
30
|
+
# from .validation.state import (
|
|
31
|
+
# add_block_to_state,
|
|
32
|
+
# validate_and_apply_block,
|
|
33
|
+
# create_account_state,
|
|
34
|
+
# get_validator_for_slot,
|
|
35
|
+
# select_best_chain,
|
|
36
|
+
# compare_chains,
|
|
37
|
+
# get_validator_set
|
|
38
|
+
# )
|
|
39
|
+
# from .validation.adapter import BlockAdapter, TransactionAdapter
|
|
45
40
|
|
|
46
|
-
class Node:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
41
|
+
# class Node:
|
|
42
|
+
# def __init__(self, config: dict):
|
|
43
|
+
# # Ensure config is a dictionary, but allow it to be None
|
|
44
|
+
# self.config = config if config is not None else {}
|
|
45
|
+
|
|
46
|
+
# # Handle validation key if provided
|
|
47
|
+
# self.validation_private_key = None
|
|
48
|
+
# self.validation_public_key = None
|
|
49
|
+
# self.is_validator = False
|
|
50
|
+
|
|
51
|
+
# # Extract validation private key from config
|
|
52
|
+
# if 'validation_private_key' in self.config:
|
|
53
|
+
# try:
|
|
54
|
+
# key_bytes = bytes.fromhex(self.config['validation_private_key'])
|
|
55
|
+
# self.validation_private_key = ed25519.Ed25519PrivateKey.from_private_bytes(key_bytes)
|
|
56
|
+
# self.validation_public_key = self.validation_private_key.public_key()
|
|
57
|
+
# self.is_validator = True
|
|
63
58
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
59
|
+
# # Set validation_route to True in config so relay will join validation route
|
|
60
|
+
# self.config['validation_route'] = True
|
|
61
|
+
# print(f"Node is configured as a validator with validation key")
|
|
62
|
+
# except Exception as e:
|
|
63
|
+
# print(f"Error loading validation private key: {e}")
|
|
64
|
+
|
|
65
|
+
# # Initialize relay with our config
|
|
66
|
+
# self.relay = Relay(self.config)
|
|
67
|
+
|
|
68
|
+
# # Get the node_id from relay
|
|
69
|
+
# self.node_id = self.relay.node_id
|
|
70
|
+
|
|
71
|
+
# # Initialize storage
|
|
72
|
+
# self.storage = Storage(self.config)
|
|
73
|
+
# self.storage.node = self # Set the storage node reference to self
|
|
74
|
+
|
|
75
|
+
# # Initialize blockchain state
|
|
76
|
+
# self.blockchain = create_account_state(self.config)
|
|
77
|
+
|
|
78
|
+
# # Store our validator info if we're a validator
|
|
79
|
+
# if self.is_validator and self.validation_public_key:
|
|
80
|
+
# self.validator_address = self.validation_public_key.public_bytes(
|
|
81
|
+
# encoding=serialization.Encoding.Raw,
|
|
82
|
+
# format=serialization.PublicFormat.Raw
|
|
83
|
+
# )
|
|
84
|
+
# self.validator_private_bytes = self.validation_private_key.private_bytes(
|
|
85
|
+
# encoding=serialization.Encoding.Raw,
|
|
86
|
+
# format=serialization.PrivateFormat.Raw,
|
|
87
|
+
# encryption_algorithm=serialization.NoEncryption()
|
|
88
|
+
# )
|
|
89
|
+
# print(f"Registered validator with address: {self.validator_address.hex()}")
|
|
90
|
+
# else:
|
|
91
|
+
# self.validator_address = None
|
|
92
|
+
# self.validator_private_bytes = None
|
|
93
|
+
|
|
94
|
+
# # Latest block of the chain this node is following
|
|
95
|
+
# self.latest_block = None
|
|
96
|
+
# self.followed_chain_id = self.config.get('followed_chain_id', None)
|
|
97
|
+
|
|
98
|
+
# # Initialize machine
|
|
99
|
+
# self.machine = AstreumMachine(node=self)
|
|
100
|
+
|
|
101
|
+
# # Register message handlers
|
|
102
|
+
# self.relay.message_handlers[Topic.PEER_ROUTE] = self._handle_peer_route
|
|
103
|
+
# self.relay.message_handlers[Topic.PING] = self._handle_ping
|
|
104
|
+
# self.relay.message_handlers[Topic.PONG] = self._handle_pong
|
|
105
|
+
# self.relay.message_handlers[Topic.OBJECT_REQUEST] = self._handle_object_request
|
|
106
|
+
# self.relay.message_handlers[Topic.OBJECT_RESPONSE] = self._handle_object_response
|
|
107
|
+
# self.relay.message_handlers[Topic.ROUTE_REQUEST] = self._handle_route_request
|
|
108
|
+
# self.relay.message_handlers[Topic.ROUTE] = self._handle_route
|
|
109
|
+
# self.relay.message_handlers[Topic.LATEST_BLOCK_REQUEST] = self._handle_latest_block_request
|
|
110
|
+
# self.relay.message_handlers[Topic.LATEST_BLOCK] = self._handle_latest_block
|
|
111
|
+
# self.relay.message_handlers[Topic.TRANSACTION] = self._handle_transaction
|
|
112
|
+
# self.relay.message_handlers[Topic.BLOCK_REQUEST] = self._handle_block_request
|
|
113
|
+
# self.relay.message_handlers[Topic.BLOCK_RESPONSE] = self._handle_block_response
|
|
114
|
+
|
|
115
|
+
# # Initialize latest block from storage if available
|
|
116
|
+
# self._initialize_latest_block()
|
|
117
|
+
|
|
118
|
+
# # Candidate chains that might be adopted
|
|
119
|
+
# self.candidate_chains = {} # chain_id -> {'latest_block': block, 'timestamp': time.time()}
|
|
120
|
+
# self.pending_blocks = {} # block_hash -> {'block': block, 'timestamp': time.time()}
|
|
121
|
+
|
|
122
|
+
# # Threads for validation and chain monitoring
|
|
123
|
+
# self.running = False
|
|
124
|
+
# self.main_chain_validation_thread = None
|
|
125
|
+
# self.candidate_chain_validation_thread = None
|
|
126
|
+
|
|
127
|
+
# # Pending transactions for a block
|
|
128
|
+
# self.pending_transactions = {} # tx_hash -> {'transaction': tx, 'timestamp': time.time()}
|
|
129
|
+
|
|
130
|
+
# # Last block production attempt time
|
|
131
|
+
# self.last_block_attempt_time = 0
|
|
137
132
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
133
|
+
# def start(self):
|
|
134
|
+
# """Start the node."""
|
|
135
|
+
# self.running = True
|
|
136
|
+
|
|
137
|
+
# # Start relay
|
|
138
|
+
# self.relay.start()
|
|
139
|
+
|
|
140
|
+
# # Start chain monitoring thread
|
|
141
|
+
# self.main_chain_validation_thread = threading.Thread(
|
|
142
|
+
# target=self._main_chain_validation_loop,
|
|
143
|
+
# name="MainChainValidation"
|
|
144
|
+
# )
|
|
145
|
+
# self.main_chain_validation_thread.daemon = True
|
|
146
|
+
# self.main_chain_validation_thread.start()
|
|
147
|
+
|
|
148
|
+
# self.candidate_chain_validation_thread = threading.Thread(
|
|
149
|
+
# target=self._candidate_chain_validation_loop,
|
|
150
|
+
# name="CandidateChainValidation"
|
|
151
|
+
# )
|
|
152
|
+
# self.candidate_chain_validation_thread.daemon = True
|
|
153
|
+
# self.candidate_chain_validation_thread.start()
|
|
154
|
+
|
|
155
|
+
# # Set up recurring block query tasks
|
|
156
|
+
# main_query_thread = threading.Thread(
|
|
157
|
+
# target=self._block_query_loop,
|
|
158
|
+
# args=('main',),
|
|
159
|
+
# daemon=True
|
|
160
|
+
# )
|
|
161
|
+
# main_query_thread.start()
|
|
162
|
+
|
|
163
|
+
# validation_query_thread = threading.Thread(
|
|
164
|
+
# target=self._block_query_loop,
|
|
165
|
+
# args=('validation',),
|
|
166
|
+
# daemon=True
|
|
167
|
+
# )
|
|
168
|
+
# validation_query_thread.start()
|
|
169
|
+
|
|
170
|
+
# print(f"Node started with ID {self.node_id.hex()}")
|
|
176
171
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
172
|
+
# def stop(self):
|
|
173
|
+
# """Stop the node and all its services."""
|
|
174
|
+
# self.running = False
|
|
180
175
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
176
|
+
# # Stop all threads
|
|
177
|
+
# if self.main_chain_validation_thread and self.main_chain_validation_thread.is_alive():
|
|
178
|
+
# self.main_chain_validation_thread.join(timeout=1.0)
|
|
184
179
|
|
|
185
|
-
|
|
186
|
-
|
|
180
|
+
# if self.candidate_chain_validation_thread and self.candidate_chain_validation_thread.is_alive():
|
|
181
|
+
# self.candidate_chain_validation_thread.join(timeout=1.0)
|
|
187
182
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
183
|
+
# # Stop relay last
|
|
184
|
+
# if self.relay:
|
|
185
|
+
# self.relay.stop()
|
|
191
186
|
|
|
192
|
-
|
|
187
|
+
# print("Node stopped")
|
|
193
188
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
189
|
+
# def _main_chain_validation_loop(self):
|
|
190
|
+
# """
|
|
191
|
+
# Main validation loop for the primary blockchain.
|
|
192
|
+
# This thread prioritizes validating blocks on the main chain we're following.
|
|
193
|
+
# """
|
|
194
|
+
# while self.running:
|
|
195
|
+
# try:
|
|
196
|
+
# # Update latest block if we don't have one yet
|
|
197
|
+
# if not self.latest_block and hasattr(self.blockchain, 'get_latest_block'):
|
|
198
|
+
# self.latest_block = self.blockchain.get_latest_block()
|
|
204
199
|
|
|
205
|
-
|
|
206
|
-
|
|
200
|
+
# # Process any blocks that extend our main chain immediately
|
|
201
|
+
# self._process_main_chain_blocks()
|
|
207
202
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
203
|
+
# # Attempt block production if we are a validator
|
|
204
|
+
# if self.is_validator and self.validator_address:
|
|
205
|
+
# self._attempt_block_production()
|
|
211
206
|
|
|
212
|
-
|
|
213
|
-
|
|
207
|
+
# # Cleanup old items
|
|
208
|
+
# self._prune_pending_items()
|
|
214
209
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
210
|
+
# # Sleep to prevent high CPU usage
|
|
211
|
+
# time.sleep(0.1) # Short sleep for main chain validation
|
|
212
|
+
# except Exception as e:
|
|
213
|
+
# print(f"Error in main chain validation loop: {e}")
|
|
214
|
+
# time.sleep(1) # Longer sleep on error
|
|
220
215
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
216
|
+
# def _candidate_chain_validation_loop(self):
|
|
217
|
+
# """
|
|
218
|
+
# Validation loop for candidate chains (potential forks).
|
|
219
|
+
# This thread handles validation of blocks from alternate chains
|
|
220
|
+
# without slowing down the main chain processing.
|
|
221
|
+
# """
|
|
222
|
+
# while self.running:
|
|
223
|
+
# try:
|
|
224
|
+
# # Process candidate chains
|
|
225
|
+
# self._evaluate_candidate_chains()
|
|
231
226
|
|
|
232
|
-
|
|
233
|
-
|
|
227
|
+
# # Prune old candidate chains
|
|
228
|
+
# self._prune_candidate_chains()
|
|
234
229
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
230
|
+
# # Sleep longer for candidate chain validation (lower priority)
|
|
231
|
+
# time.sleep(1) # Longer sleep for candidate chain validation
|
|
232
|
+
# except Exception as e:
|
|
233
|
+
# print(f"Error in candidate chain validation loop: {e}")
|
|
234
|
+
# time.sleep(2) # Even longer sleep on error
|
|
240
235
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
236
|
+
# def _prune_pending_items(self):
|
|
237
|
+
# """Remove old pending blocks and transactions."""
|
|
238
|
+
# current_time = time.time()
|
|
239
|
+
|
|
240
|
+
# # Prune old pending blocks (older than 1 hour)
|
|
241
|
+
# blocks_to_remove = [
|
|
242
|
+
# block_hash for block_hash, data in self.pending_blocks.items()
|
|
243
|
+
# if current_time - data['timestamp'] > 3600 # 1 hour
|
|
244
|
+
# ]
|
|
245
|
+
# for block_hash in blocks_to_remove:
|
|
246
|
+
# del self.pending_blocks[block_hash]
|
|
252
247
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
248
|
+
# # Prune old pending transactions (older than 30 minutes)
|
|
249
|
+
# txs_to_remove = [
|
|
250
|
+
# tx_hash for tx_hash, data in self.pending_transactions.items()
|
|
251
|
+
# if current_time - data['timestamp'] > 1800 # 30 minutes
|
|
252
|
+
# ]
|
|
253
|
+
# for tx_hash in txs_to_remove:
|
|
254
|
+
# del self.pending_transactions[tx_hash]
|
|
260
255
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
256
|
+
# def _process_main_chain_blocks(self):
|
|
257
|
+
# """
|
|
258
|
+
# Process blocks that extend our current main chain.
|
|
259
|
+
# Prioritizes blocks that build on our latest block.
|
|
260
|
+
# """
|
|
261
|
+
# # Skip if we don't have a latest block yet
|
|
262
|
+
# if not self.latest_block:
|
|
263
|
+
# return
|
|
269
264
|
|
|
270
|
-
|
|
271
|
-
|
|
265
|
+
# # Get the hash of our latest block
|
|
266
|
+
# latest_hash = self.latest_block.get_hash()
|
|
272
267
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
268
|
+
# # Find any pending blocks that build on our latest block
|
|
269
|
+
# main_chain_blocks = []
|
|
270
|
+
# for block_hash, data in list(self.pending_blocks.items()):
|
|
271
|
+
# block = data['block']
|
|
277
272
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
273
|
+
# # Check if this block extends our latest block
|
|
274
|
+
# if block.previous == latest_hash:
|
|
275
|
+
# main_chain_blocks.append(block)
|
|
281
276
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
277
|
+
# # Process found blocks
|
|
278
|
+
# for block in main_chain_blocks:
|
|
279
|
+
# self._validate_and_process_main_chain_block(block)
|
|
285
280
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
281
|
+
# def _validate_and_process_main_chain_block(self, block: Block):
|
|
282
|
+
# """
|
|
283
|
+
# Validate and process a block that extends our main chain.
|
|
284
|
+
|
|
285
|
+
# Args:
|
|
286
|
+
# block: Block to validate and process
|
|
287
|
+
# """
|
|
288
|
+
# try:
|
|
289
|
+
# # Validate block
|
|
290
|
+
# is_valid = validate_block(block, self.blockchain.get_accounts_at_block(block.previous), self.blockchain.get_blocks())
|
|
296
291
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
292
|
+
# if is_valid:
|
|
293
|
+
# # Apply block to our state
|
|
294
|
+
# success = validate_and_apply_block(self.blockchain, block)
|
|
295
|
+
# if success:
|
|
296
|
+
# print(f"Applied valid block {block.number} to blockchain state")
|
|
297
|
+
# self._update_latest_block(block)
|
|
298
|
+
# blocks_to_remove = [block.get_hash()]
|
|
299
|
+
# for block_hash in blocks_to_remove:
|
|
300
|
+
# if block_hash in self.pending_blocks:
|
|
301
|
+
# del self.pending_blocks[block_hash]
|
|
302
|
+
# print(f"Added block {block.number} to blockchain")
|
|
303
|
+
# return True
|
|
304
|
+
# except Exception as e:
|
|
305
|
+
# print(f"Error validating main chain block {block.number}: {e}")
|
|
311
306
|
|
|
312
|
-
|
|
307
|
+
# return False
|
|
313
308
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
309
|
+
# def _evaluate_candidate_chains(self):
|
|
310
|
+
# """
|
|
311
|
+
# Evaluate candidate chains to determine if any should become our main chain.
|
|
312
|
+
# This will validate pending blocks and look for chains with higher cumulative difficulty.
|
|
313
|
+
# """
|
|
314
|
+
# # Skip if no candidate chains
|
|
315
|
+
# if not self.candidate_chains:
|
|
316
|
+
# return
|
|
322
317
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
318
|
+
# # For each candidate chain, validate blocks and calculate metrics
|
|
319
|
+
# for chain_id, data in list(self.candidate_chains.items()):
|
|
320
|
+
# latest_candidate_block = data['latest_block']
|
|
326
321
|
|
|
327
|
-
|
|
328
|
-
|
|
322
|
+
# # Build the chain backwards
|
|
323
|
+
# chain_blocks = self._build_chain_from_latest(latest_candidate_block)
|
|
329
324
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
325
|
+
# # Skip if we couldn't build a complete chain
|
|
326
|
+
# if not chain_blocks:
|
|
327
|
+
# continue
|
|
333
328
|
|
|
334
|
-
|
|
335
|
-
|
|
329
|
+
# # Validate the entire chain
|
|
330
|
+
# valid_chain = self._validate_candidate_chain(chain_blocks)
|
|
336
331
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
332
|
+
# # If valid and better than our current chain, switch to it
|
|
333
|
+
# if valid_chain and self._is_better_chain(chain_blocks):
|
|
334
|
+
# self._switch_to_new_chain(chain_blocks)
|
|
340
335
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
336
|
+
# def _build_chain_from_latest(self, latest_block: Block) -> List[Block]:
|
|
337
|
+
# """
|
|
338
|
+
# Build a chain from the latest block back to a known point in our blockchain.
|
|
344
339
|
|
|
345
|
-
|
|
346
|
-
|
|
340
|
+
# Args:
|
|
341
|
+
# latest_block: Latest block in the candidate chain
|
|
347
342
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
343
|
+
# Returns:
|
|
344
|
+
# List of blocks in the chain, ordered from oldest to newest
|
|
345
|
+
# """
|
|
346
|
+
# chain_blocks = [latest_block]
|
|
347
|
+
# current_block = latest_block
|
|
348
|
+
|
|
349
|
+
# # Track visited blocks to avoid cycles
|
|
350
|
+
# visited = {current_block.get_hash()}
|
|
351
|
+
|
|
352
|
+
# # Build chain backwards until we either:
|
|
353
|
+
# # 1. Find a block in our main chain
|
|
354
|
+
# # 2. Run out of blocks
|
|
355
|
+
# # 3. Detect a cycle
|
|
356
|
+
# while current_block.number > 0:
|
|
357
|
+
# previous_hash = current_block.previous
|
|
363
358
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
359
|
+
# # Check if we have this block in our blockchain
|
|
360
|
+
# if hasattr(self.blockchain, 'has_block') and self.blockchain.has_block(previous_hash):
|
|
361
|
+
# # Found connection to our main chain
|
|
362
|
+
# previous_block = self.blockchain.get_block(previous_hash)
|
|
363
|
+
# chain_blocks.insert(0, previous_block)
|
|
364
|
+
# break
|
|
370
365
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
366
|
+
# # Check if block is in pending blocks
|
|
367
|
+
# elif previous_hash in self.pending_blocks:
|
|
368
|
+
# previous_block = self.pending_blocks[previous_hash]['block']
|
|
374
369
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
370
|
+
# # Check for cycles
|
|
371
|
+
# if previous_hash in visited:
|
|
372
|
+
# print(f"Cycle detected in candidate chain at block {previous_block.number}")
|
|
373
|
+
# return []
|
|
379
374
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
375
|
+
# visited.add(previous_hash)
|
|
376
|
+
# chain_blocks.insert(0, previous_block)
|
|
377
|
+
# current_block = previous_block
|
|
378
|
+
# else:
|
|
379
|
+
# # Missing block, cannot validate the chain
|
|
380
|
+
# print(f"Missing block {previous_hash.hex()} in candidate chain")
|
|
381
|
+
# return []
|
|
387
382
|
|
|
388
|
-
|
|
383
|
+
# return chain_blocks
|
|
389
384
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
385
|
+
# def _validate_candidate_chain(self, chain_blocks: List[Block]) -> bool:
|
|
386
|
+
# """
|
|
387
|
+
# Validate a candidate chain of blocks.
|
|
393
388
|
|
|
394
|
-
|
|
395
|
-
|
|
389
|
+
# Args:
|
|
390
|
+
# chain_blocks: List of blocks in the chain (oldest to newest)
|
|
396
391
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
392
|
+
# Returns:
|
|
393
|
+
# True if the chain is valid, False otherwise
|
|
394
|
+
# """
|
|
395
|
+
# # Validate each block in the chain
|
|
396
|
+
# for i, block in enumerate(chain_blocks):
|
|
397
|
+
# # Skip first block, it's either genesis or a block we already have
|
|
398
|
+
# if i == 0:
|
|
399
|
+
# continue
|
|
405
400
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
401
|
+
# # Validate block connections
|
|
402
|
+
# if block.previous != chain_blocks[i-1].get_hash():
|
|
403
|
+
# print(f"Invalid chain: block {block.number} does not reference previous block")
|
|
404
|
+
# return False
|
|
410
405
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
406
|
+
# # Validate block
|
|
407
|
+
# is_valid = validate_block(block, self.blockchain.get_accounts_at_block(block.previous), self.blockchain.get_blocks())
|
|
408
|
+
# if not is_valid:
|
|
409
|
+
# print(f"Invalid chain: block {block.number} is invalid")
|
|
410
|
+
# return False
|
|
416
411
|
|
|
417
|
-
|
|
412
|
+
# return True
|
|
418
413
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
414
|
+
# def _is_better_chain(self, chain_blocks: List[Block]) -> bool:
|
|
415
|
+
# """
|
|
416
|
+
# Determine if a candidate chain is better than our current chain.
|
|
422
417
|
|
|
423
|
-
|
|
424
|
-
|
|
418
|
+
# Args:
|
|
419
|
+
# chain_blocks: List of blocks in the candidate chain
|
|
425
420
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
421
|
+
# Returns:
|
|
422
|
+
# True if the candidate chain is better, False otherwise
|
|
423
|
+
# """
|
|
424
|
+
# # Get the latest block from the candidate chain
|
|
425
|
+
# candidate_latest = chain_blocks[-1]
|
|
426
|
+
|
|
427
|
+
# # If we don't have a latest block, any valid chain is better
|
|
428
|
+
# if not self.latest_block:
|
|
429
|
+
# return True
|
|
435
430
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
431
|
+
# # Compare block numbers (longest chain rule)
|
|
432
|
+
# if candidate_latest.number > self.latest_block.number:
|
|
433
|
+
# print(f"Candidate chain is longer: {candidate_latest.number} vs {self.latest_block.number}")
|
|
434
|
+
# return True
|
|
440
435
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
436
|
+
# return False
|
|
437
|
+
|
|
438
|
+
# def _switch_to_new_chain(self, chain_blocks: List[Block]):
|
|
439
|
+
# """
|
|
440
|
+
# Switch to a new chain by adding all blocks to our blockchain.
|
|
441
|
+
|
|
442
|
+
# Args:
|
|
443
|
+
# chain_blocks: List of blocks in the chain (oldest to newest)
|
|
444
|
+
# """
|
|
445
|
+
# # Find the point where the chains diverge
|
|
446
|
+
# divergence_point = 0
|
|
447
|
+
# for i, block in enumerate(chain_blocks):
|
|
448
|
+
# # Check if we have this block in our blockchain
|
|
449
|
+
# if hasattr(self.blockchain, 'has_block') and self.blockchain.has_block(block.get_hash()):
|
|
450
|
+
# divergence_point = i + 1
|
|
451
|
+
# else:
|
|
452
|
+
# break
|
|
458
453
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
454
|
+
# # Add all blocks after the divergence point
|
|
455
|
+
# for i in range(divergence_point, len(chain_blocks)):
|
|
456
|
+
# block = chain_blocks[i]
|
|
462
457
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
458
|
+
# # Add block to blockchain
|
|
459
|
+
# if hasattr(self.blockchain, 'add_block'):
|
|
460
|
+
# try:
|
|
461
|
+
# self.blockchain.add_block(block)
|
|
467
462
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
463
|
+
# # Remove from pending blocks
|
|
464
|
+
# block_hash = block.get_hash()
|
|
465
|
+
# if block_hash in self.pending_blocks:
|
|
466
|
+
# del self.pending_blocks[block_hash]
|
|
472
467
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
468
|
+
# print(f"Added block {block.number} to blockchain")
|
|
469
|
+
# except Exception as e:
|
|
470
|
+
# print(f"Error adding block {block.number} to blockchain: {e}")
|
|
471
|
+
# return
|
|
477
472
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
473
|
+
# # Update latest block
|
|
474
|
+
# self._update_latest_block(chain_blocks[-1])
|
|
475
|
+
# print(f"Switched to new chain, latest block: {self.latest_block.number}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: astreum
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.18
|
|
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
|
|
@@ -21,7 +21,7 @@ astreum/lispeum/special/number/addition.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5
|
|
|
21
21
|
astreum/machine/__init__.py,sha256=GOdZl1tS9uIJHbq5WVcplifMDPDLQroX7CVew-K2YbA,15262
|
|
22
22
|
astreum/machine/environment.py,sha256=K0084U6B7wwjrDZ9b2_7cEcbBzsB7UOy_Zpbrr7B3GY,834
|
|
23
23
|
astreum/machine/error.py,sha256=MvqBaZZt33rNELNhUJ2lER3TE3aS8WVqsWF2hz2AwoA,38
|
|
24
|
-
astreum/node/__init__.py,sha256=
|
|
24
|
+
astreum/node/__init__.py,sha256=zY4rD4knr8zPOmXX3eL-VENCQ-SGDccUQlTLEzzQaAA,20073
|
|
25
25
|
astreum/node/utils.py,sha256=amGhNYHVMjvAO-9vBRAcim-S5LlLSRudqooBN-XPdm4,702
|
|
26
26
|
astreum/node/crypto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
27
|
astreum/node/crypto/ed25519.py,sha256=FRnvlN0kZlxn4j-sJKl-C9tqiz_0z4LZyXLj3KIj1TQ,1760
|
|
@@ -51,8 +51,8 @@ astreum/node/validation/_block/model.py,sha256=d7x3_tX2MPLnGODL_5_vNjQfLdYa405bl
|
|
|
51
51
|
astreum/node/validation/_block/validate.py,sha256=niLexCNhEwUJLclyrdNZSvHcVa_J6jlu7J3FiWY7XBU,6232
|
|
52
52
|
astreum/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
53
53
|
astreum/utils/bytes_format.py,sha256=X4tG5GGPweNCE54bHYkLFiuLTbmpy5upO_s1Cef-MGA,2711
|
|
54
|
-
astreum-0.1.
|
|
55
|
-
astreum-0.1.
|
|
56
|
-
astreum-0.1.
|
|
57
|
-
astreum-0.1.
|
|
58
|
-
astreum-0.1.
|
|
54
|
+
astreum-0.1.18.dist-info/licenses/LICENSE,sha256=gYBvRDP-cPLmTyJhvZ346QkrYW_eleke4Z2Yyyu43eQ,1089
|
|
55
|
+
astreum-0.1.18.dist-info/METADATA,sha256=k1Eptg-mbsR5Ddo2ky6JCIgkKlXO0WqovKe5zKDSZ3c,3312
|
|
56
|
+
astreum-0.1.18.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
|
|
57
|
+
astreum-0.1.18.dist-info/top_level.txt,sha256=1EG1GmkOk3NPmUA98FZNdKouhRyget-KiFiMk0i2Uz0,8
|
|
58
|
+
astreum-0.1.18.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|