astreum 0.3.16__py3-none-any.whl → 0.3.48__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- astreum/__init__.py +1 -2
- astreum/communication/__init__.py +15 -11
- astreum/communication/difficulty.py +39 -0
- astreum/communication/disconnect.py +57 -0
- astreum/communication/handlers/handshake.py +105 -62
- astreum/communication/handlers/object_request.py +226 -138
- astreum/communication/handlers/object_response.py +118 -10
- astreum/communication/handlers/ping.py +9 -0
- astreum/communication/handlers/route_request.py +7 -1
- astreum/communication/handlers/route_response.py +7 -1
- astreum/communication/incoming_queue.py +96 -0
- astreum/communication/message_pow.py +36 -0
- astreum/communication/models/peer.py +4 -0
- astreum/communication/models/ping.py +27 -6
- astreum/communication/models/route.py +4 -0
- astreum/communication/{start.py → node.py} +10 -11
- astreum/communication/outgoing_queue.py +108 -0
- astreum/communication/processors/incoming.py +110 -37
- astreum/communication/processors/outgoing.py +35 -2
- astreum/communication/processors/peer.py +133 -58
- astreum/communication/setup.py +272 -113
- astreum/communication/util.py +14 -0
- astreum/machine/evaluations/low_evaluation.py +5 -5
- astreum/machine/models/expression.py +5 -5
- astreum/node.py +96 -87
- astreum/storage/actions/get.py +285 -183
- astreum/storage/actions/set.py +171 -156
- astreum/storage/models/atom.py +0 -14
- astreum/storage/models/trie.py +2 -2
- astreum/storage/providers.py +24 -0
- astreum/storage/requests.py +13 -10
- astreum/storage/setup.py +20 -15
- astreum/utils/config.py +260 -43
- astreum/utils/logging.py +1 -1
- astreum/{consensus → validation}/__init__.py +0 -4
- astreum/validation/constants.py +2 -0
- astreum/{consensus → validation}/genesis.py +4 -6
- astreum/{consensus → validation}/models/account.py +1 -1
- astreum/validation/models/block.py +544 -0
- astreum/validation/models/fork.py +511 -0
- astreum/{consensus → validation}/models/receipt.py +18 -5
- astreum/{consensus → validation}/models/transaction.py +50 -8
- astreum/validation/node.py +190 -0
- astreum/{consensus → validation}/validator.py +1 -1
- astreum/validation/workers/__init__.py +8 -0
- astreum/{consensus → validation}/workers/validation.py +360 -333
- astreum/verification/__init__.py +4 -0
- astreum/{consensus/workers/discovery.py → verification/discover.py} +1 -1
- astreum/verification/node.py +61 -0
- astreum/verification/worker.py +183 -0
- {astreum-0.3.16.dist-info → astreum-0.3.48.dist-info}/METADATA +45 -9
- astreum-0.3.48.dist-info/RECORD +79 -0
- astreum/consensus/models/block.py +0 -364
- astreum/consensus/models/chain.py +0 -66
- astreum/consensus/models/fork.py +0 -100
- astreum/consensus/setup.py +0 -83
- astreum/consensus/start.py +0 -67
- astreum/consensus/workers/__init__.py +0 -9
- astreum/consensus/workers/verify.py +0 -90
- astreum-0.3.16.dist-info/RECORD +0 -72
- /astreum/{consensus → validation}/models/__init__.py +0 -0
- /astreum/{consensus → validation}/models/accounts.py +0 -0
- {astreum-0.3.16.dist-info → astreum-0.3.48.dist-info}/WHEEL +0 -0
- {astreum-0.3.16.dist-info → astreum-0.3.48.dist-info}/licenses/LICENSE +0 -0
- {astreum-0.3.16.dist-info → astreum-0.3.48.dist-info}/top_level.txt +0 -0
|
@@ -1,334 +1,361 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import math
|
|
4
|
-
import time
|
|
5
|
-
from queue import Empty
|
|
6
|
-
from typing import Any, Callable
|
|
7
|
-
|
|
8
|
-
from ..models.account import Account
|
|
9
|
-
from ..models.accounts import Accounts
|
|
10
|
-
from ..models.block import Block
|
|
11
|
-
from ..models.transaction import apply_transaction
|
|
12
|
-
from ..validator import current_validator
|
|
13
|
-
from ...storage.models.atom import bytes_list_to_atoms
|
|
14
|
-
from ...communication.models.message import Message, MessageTopic
|
|
15
|
-
from ...communication.models.ping import Ping
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
validator_account
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
accounts_snapshot =
|
|
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
|
-
|
|
137
|
-
|
|
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
|
-
current_hash =
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
node.logger.debug(
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
import time
|
|
5
|
+
from queue import Empty
|
|
6
|
+
from typing import Any, Callable
|
|
7
|
+
|
|
8
|
+
from ..models.account import Account
|
|
9
|
+
from ..models.accounts import Accounts
|
|
10
|
+
from ..models.block import Block
|
|
11
|
+
from ..models.transaction import Transaction, apply_transaction
|
|
12
|
+
from ..validator import current_validator
|
|
13
|
+
from ...storage.models.atom import bytes_list_to_atoms
|
|
14
|
+
from ...communication.models.message import Message, MessageTopic
|
|
15
|
+
from ...communication.models.ping import Ping
|
|
16
|
+
from ...communication.difficulty import message_difficulty
|
|
17
|
+
from ...communication.outgoing_queue import enqueue_outgoing
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def make_validation_worker(
|
|
21
|
+
node: Any,
|
|
22
|
+
) -> Callable[[], None]:
|
|
23
|
+
"""Build the validation worker bound to the given node."""
|
|
24
|
+
|
|
25
|
+
def _validation_worker() -> None:
|
|
26
|
+
node.logger.info("Validation worker started")
|
|
27
|
+
stop = node._validation_stop_event
|
|
28
|
+
|
|
29
|
+
def _award_validator_reward(block: Block, reward_amount: int) -> None:
|
|
30
|
+
"""Credit the validator account with the provided reward."""
|
|
31
|
+
if reward_amount <= 0:
|
|
32
|
+
return
|
|
33
|
+
accounts = getattr(block, "accounts", None)
|
|
34
|
+
validator_key = getattr(block, "validator_public_key_bytes", None)
|
|
35
|
+
if accounts is None or not validator_key:
|
|
36
|
+
node.logger.debug(
|
|
37
|
+
"Skipping validator reward; accounts snapshot or key missing"
|
|
38
|
+
)
|
|
39
|
+
return
|
|
40
|
+
try:
|
|
41
|
+
validator_account = accounts.get_account(
|
|
42
|
+
address=validator_key, node=node
|
|
43
|
+
)
|
|
44
|
+
except Exception:
|
|
45
|
+
node.logger.exception("Unable to load validator account for reward")
|
|
46
|
+
return
|
|
47
|
+
if validator_account is None:
|
|
48
|
+
validator_account = Account.create()
|
|
49
|
+
validator_account.balance += reward_amount
|
|
50
|
+
accounts.set_account(validator_key, validator_account)
|
|
51
|
+
|
|
52
|
+
while not stop.is_set():
|
|
53
|
+
validation_public_key = getattr(node, "validation_public_key", None)
|
|
54
|
+
if not validation_public_key:
|
|
55
|
+
node.logger.debug("Validation public key unavailable; sleeping")
|
|
56
|
+
time.sleep(0.5)
|
|
57
|
+
continue
|
|
58
|
+
|
|
59
|
+
latest_block_hash = getattr(node, "latest_block_hash", None)
|
|
60
|
+
if not isinstance(latest_block_hash, (bytes, bytearray)):
|
|
61
|
+
node.logger.warning("Missing latest_block_hash; retrying")
|
|
62
|
+
time.sleep(0.5)
|
|
63
|
+
continue
|
|
64
|
+
|
|
65
|
+
node.logger.debug(
|
|
66
|
+
"Querying current validator for block %s",
|
|
67
|
+
latest_block_hash.hex()
|
|
68
|
+
if isinstance(latest_block_hash, (bytes, bytearray))
|
|
69
|
+
else latest_block_hash,
|
|
70
|
+
)
|
|
71
|
+
try:
|
|
72
|
+
scheduled_validator, _ = current_validator(node, latest_block_hash)
|
|
73
|
+
except Exception as exc:
|
|
74
|
+
node.logger.exception("Unable to determine current validator: %s", exc)
|
|
75
|
+
time.sleep(0.5)
|
|
76
|
+
continue
|
|
77
|
+
|
|
78
|
+
if scheduled_validator != validation_public_key:
|
|
79
|
+
expected_hex = (
|
|
80
|
+
scheduled_validator.hex()
|
|
81
|
+
if isinstance(scheduled_validator, (bytes, bytearray))
|
|
82
|
+
else scheduled_validator
|
|
83
|
+
)
|
|
84
|
+
node.logger.debug("Current validator mismatch; expected %s", expected_hex)
|
|
85
|
+
time.sleep(0.5)
|
|
86
|
+
continue
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
previous_block = Block.from_atom(node, latest_block_hash)
|
|
90
|
+
except Exception:
|
|
91
|
+
node.logger.exception("Unable to load previous block for validation")
|
|
92
|
+
time.sleep(0.5)
|
|
93
|
+
continue
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
current_hash = node._validation_transaction_queue.get_nowait()
|
|
97
|
+
queue_empty = False
|
|
98
|
+
except Empty:
|
|
99
|
+
current_hash = None
|
|
100
|
+
queue_empty = True
|
|
101
|
+
node.logger.debug(
|
|
102
|
+
"No pending validation transactions; generating empty block"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
try:
|
|
106
|
+
accounts_snapshot = Accounts(root_hash=previous_block.accounts_hash)
|
|
107
|
+
except Exception:
|
|
108
|
+
accounts_snapshot = None
|
|
109
|
+
node.logger.warning("Unable to initialise accounts snapshot for block")
|
|
110
|
+
|
|
111
|
+
new_block = Block(
|
|
112
|
+
chain_id=getattr(node, "chain", 0),
|
|
113
|
+
previous_block_hash=latest_block_hash,
|
|
114
|
+
previous_block=previous_block,
|
|
115
|
+
number=(previous_block.number or 0) + 1,
|
|
116
|
+
timestamp=None,
|
|
117
|
+
accounts_hash=previous_block.accounts_hash,
|
|
118
|
+
transactions_total_fees=0,
|
|
119
|
+
transactions_hash=None,
|
|
120
|
+
receipts_hash=None,
|
|
121
|
+
delay_difficulty=None,
|
|
122
|
+
validator_public_key_bytes=validation_public_key,
|
|
123
|
+
nonce=0,
|
|
124
|
+
signature=None,
|
|
125
|
+
accounts=accounts_snapshot,
|
|
126
|
+
transactions=[],
|
|
127
|
+
receipts=[],
|
|
128
|
+
)
|
|
129
|
+
node.logger.debug(
|
|
130
|
+
"Creating block #%s extending %s",
|
|
131
|
+
new_block.number,
|
|
132
|
+
(
|
|
133
|
+
node.latest_block_hash.hex()
|
|
134
|
+
if isinstance(node.latest_block_hash, (bytes, bytearray))
|
|
135
|
+
else node.latest_block_hash
|
|
136
|
+
),
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# we may want to add a timer to process part of the txs only on a slow computer
|
|
140
|
+
total_fees = 0
|
|
141
|
+
while current_hash is not None:
|
|
142
|
+
try:
|
|
143
|
+
total_fees += apply_transaction(node, new_block, current_hash)
|
|
144
|
+
except NotImplementedError:
|
|
145
|
+
tx_hex = (
|
|
146
|
+
current_hash.hex()
|
|
147
|
+
if isinstance(current_hash, (bytes, bytearray))
|
|
148
|
+
else current_hash
|
|
149
|
+
)
|
|
150
|
+
node.logger.warning("Transaction %s unsupported; re-queued", tx_hex)
|
|
151
|
+
node._validation_transaction_queue.put(current_hash)
|
|
152
|
+
time.sleep(0.5)
|
|
153
|
+
break
|
|
154
|
+
except Exception:
|
|
155
|
+
tx_hex = (
|
|
156
|
+
current_hash.hex()
|
|
157
|
+
if isinstance(current_hash, (bytes, bytearray))
|
|
158
|
+
else current_hash
|
|
159
|
+
)
|
|
160
|
+
node.logger.exception("Failed applying transaction %s", tx_hex)
|
|
161
|
+
|
|
162
|
+
try:
|
|
163
|
+
current_hash = node._validation_transaction_queue.get_nowait()
|
|
164
|
+
except Empty:
|
|
165
|
+
current_hash = None
|
|
166
|
+
|
|
167
|
+
new_block.transactions_total_fees = total_fees
|
|
168
|
+
reward_amount = total_fees if total_fees > 0 else 1
|
|
169
|
+
if total_fees == 0 and queue_empty:
|
|
170
|
+
node.logger.debug("Awarding base validator reward of 1 aster")
|
|
171
|
+
elif total_fees > 0:
|
|
172
|
+
node.logger.debug(
|
|
173
|
+
"Collected %d aster in transaction fees for this block", total_fees
|
|
174
|
+
)
|
|
175
|
+
_award_validator_reward(new_block, reward_amount)
|
|
176
|
+
|
|
177
|
+
# create an atom list of transactions, save the list head hash as the block's transactions_hash
|
|
178
|
+
transactions = new_block.transactions or []
|
|
179
|
+
tx_hashes = [bytes(tx.hash) for tx in transactions if tx.hash]
|
|
180
|
+
head_hash, _ = bytes_list_to_atoms(tx_hashes)
|
|
181
|
+
new_block.transactions_hash = head_hash
|
|
182
|
+
node.logger.debug("Block includes %d transactions", len(transactions))
|
|
183
|
+
transaction_atoms = []
|
|
184
|
+
for tx in transactions:
|
|
185
|
+
if not tx.hash:
|
|
186
|
+
continue
|
|
187
|
+
atoms = Transaction.get_atoms(node, tx.hash)
|
|
188
|
+
if atoms is None:
|
|
189
|
+
node.logger.debug(
|
|
190
|
+
"Unable to load transaction atoms for %s",
|
|
191
|
+
tx.hash.hex(),
|
|
192
|
+
)
|
|
193
|
+
continue
|
|
194
|
+
transaction_atoms.extend(atoms)
|
|
195
|
+
|
|
196
|
+
receipts = new_block.receipts or []
|
|
197
|
+
receipt_atoms = []
|
|
198
|
+
receipt_hashes = []
|
|
199
|
+
for rcpt in receipts:
|
|
200
|
+
receipt_id, atoms = rcpt.to_atom()
|
|
201
|
+
receipt_atoms.extend(atoms)
|
|
202
|
+
receipt_hashes.append(bytes(receipt_id))
|
|
203
|
+
receipts_head, _ = bytes_list_to_atoms(receipt_hashes)
|
|
204
|
+
new_block.receipts_hash = receipts_head
|
|
205
|
+
node.logger.debug("Block includes %d receipts", len(receipts))
|
|
206
|
+
|
|
207
|
+
account_atoms = []
|
|
208
|
+
if new_block.accounts is not None:
|
|
209
|
+
try:
|
|
210
|
+
account_atoms = new_block.accounts.update_trie(node)
|
|
211
|
+
new_block.accounts_hash = new_block.accounts.root_hash
|
|
212
|
+
node.logger.debug(
|
|
213
|
+
"Updated trie for %d cached accounts",
|
|
214
|
+
len(new_block.accounts._cache),
|
|
215
|
+
)
|
|
216
|
+
except Exception:
|
|
217
|
+
node.logger.exception("Failed to update accounts trie for block")
|
|
218
|
+
|
|
219
|
+
now = time.time()
|
|
220
|
+
min_allowed = new_block.previous_block.timestamp + 1
|
|
221
|
+
nonce_time_seconds = node.nonce_time_ms / 1000.0
|
|
222
|
+
expected_blocktime = now + nonce_time_seconds
|
|
223
|
+
new_block.timestamp = max(int(math.ceil(expected_blocktime)), min_allowed)
|
|
224
|
+
|
|
225
|
+
new_block.delay_difficulty = Block.calculate_delay_difficulty(
|
|
226
|
+
previous_timestamp=previous_block.timestamp,
|
|
227
|
+
current_timestamp=new_block.timestamp,
|
|
228
|
+
previous_difficulty=previous_block.delay_difficulty,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
try:
|
|
232
|
+
nonce_started = time.perf_counter()
|
|
233
|
+
new_block.generate_nonce(difficulty=previous_block.delay_difficulty)
|
|
234
|
+
elapsed_ms = int((time.perf_counter() - nonce_started) * 1000)
|
|
235
|
+
setattr(node, "nonce_time_ms", elapsed_ms)
|
|
236
|
+
node.logger.debug(
|
|
237
|
+
"Found nonce %s for block #%s at difficulty %s",
|
|
238
|
+
new_block.nonce,
|
|
239
|
+
new_block.number,
|
|
240
|
+
new_block.delay_difficulty,
|
|
241
|
+
)
|
|
242
|
+
except Exception:
|
|
243
|
+
node.logger.exception("Failed while searching for block nonce")
|
|
244
|
+
time.sleep(0.5)
|
|
245
|
+
continue
|
|
246
|
+
|
|
247
|
+
# wait until the block timestamp is reached before propagating
|
|
248
|
+
now = time.time()
|
|
235
249
|
if now > (new_block.timestamp + 2):
|
|
236
|
-
node.logger.warning(
|
|
237
|
-
"Skipping block #%s propagation; timestamp %s already elapsed (now=%s)",
|
|
238
|
-
new_block.number,
|
|
239
|
-
new_block.timestamp,
|
|
240
|
-
now,
|
|
241
|
-
)
|
|
242
|
-
continue
|
|
243
|
-
|
|
244
|
-
spread_delay = new_block.timestamp - now
|
|
245
|
-
if spread_delay > 0:
|
|
246
|
-
node.logger.debug(
|
|
247
|
-
"Delaying distribution for %.3fs to reach block timestamp %s",
|
|
248
|
-
spread_delay,
|
|
249
|
-
new_block.timestamp,
|
|
250
|
-
)
|
|
251
|
-
time.sleep(spread_delay)
|
|
252
|
-
|
|
253
|
-
# atomize block
|
|
254
|
-
new_block_hash, new_block_atoms = new_block.to_atom()
|
|
255
|
-
# put as own latest block hash
|
|
256
|
-
node.latest_block_hash = new_block_hash
|
|
257
|
-
node.latest_block = new_block
|
|
258
|
-
node.logger.info(
|
|
259
|
-
"Created block #%s with hash %s (%d atoms)",
|
|
260
|
-
new_block.number,
|
|
261
|
-
new_block_hash.hex(),
|
|
262
|
-
len(new_block_atoms),
|
|
263
|
-
)
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
# ping peers
|
|
267
|
-
if node.
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
ping_msg
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
250
|
+
node.logger.warning(
|
|
251
|
+
"Skipping block #%s propagation; timestamp %s already elapsed (now=%s)",
|
|
252
|
+
new_block.number,
|
|
253
|
+
new_block.timestamp,
|
|
254
|
+
now,
|
|
255
|
+
)
|
|
256
|
+
continue
|
|
257
|
+
|
|
258
|
+
spread_delay = new_block.timestamp - now
|
|
259
|
+
if spread_delay > 0:
|
|
260
|
+
node.logger.debug(
|
|
261
|
+
"Delaying distribution for %.3fs to reach block timestamp %s",
|
|
262
|
+
spread_delay,
|
|
263
|
+
new_block.timestamp,
|
|
264
|
+
)
|
|
265
|
+
time.sleep(spread_delay)
|
|
266
|
+
|
|
267
|
+
# atomize block
|
|
268
|
+
new_block_hash, new_block_atoms = new_block.to_atom()
|
|
269
|
+
# put as own latest block hash
|
|
270
|
+
node.latest_block_hash = new_block_hash
|
|
271
|
+
node.latest_block = new_block
|
|
272
|
+
node.logger.info(
|
|
273
|
+
"Created block #%s with hash %s (%d atoms)",
|
|
274
|
+
new_block.number,
|
|
275
|
+
new_block_hash.hex(),
|
|
276
|
+
len(new_block_atoms),
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
# ping all peers to update their records
|
|
281
|
+
if node.outgoing_queue and node.peers:
|
|
282
|
+
try:
|
|
283
|
+
with node.peers_lock:
|
|
284
|
+
peers = list(node.peers.items())
|
|
285
|
+
except Exception:
|
|
286
|
+
peers = list(getattr(node, "peers", {}).items())
|
|
287
|
+
|
|
288
|
+
if peers:
|
|
289
|
+
ping_payload = Ping(
|
|
290
|
+
is_validator=True,
|
|
291
|
+
difficulty=message_difficulty(node),
|
|
292
|
+
latest_block=new_block_hash,
|
|
293
|
+
).to_bytes()
|
|
294
|
+
|
|
295
|
+
for peer_key, peer in peers:
|
|
296
|
+
peer_hex = (
|
|
297
|
+
peer_key.hex()
|
|
298
|
+
if isinstance(peer_key, (bytes, bytearray))
|
|
299
|
+
else peer_key
|
|
300
|
+
)
|
|
301
|
+
address = getattr(peer, "address", None)
|
|
302
|
+
if not address:
|
|
303
|
+
node.logger.debug(
|
|
304
|
+
"Skipping validator ping to %s; address missing",
|
|
305
|
+
peer_hex,
|
|
306
|
+
)
|
|
307
|
+
continue
|
|
308
|
+
try:
|
|
309
|
+
ping_msg = Message(
|
|
310
|
+
topic=MessageTopic.PING,
|
|
311
|
+
content=ping_payload,
|
|
312
|
+
sender=node.relay_public_key,
|
|
313
|
+
)
|
|
314
|
+
ping_msg.encrypt(peer.shared_key_bytes)
|
|
315
|
+
if enqueue_outgoing(
|
|
316
|
+
node,
|
|
317
|
+
address,
|
|
318
|
+
message=ping_msg,
|
|
319
|
+
difficulty=peer.difficulty,
|
|
320
|
+
):
|
|
321
|
+
node.logger.debug(
|
|
322
|
+
"Queued validator ping to %s (%s)",
|
|
323
|
+
address,
|
|
324
|
+
peer_key.hex()
|
|
325
|
+
if isinstance(peer_key, (bytes, bytearray))
|
|
326
|
+
else peer_key,
|
|
327
|
+
)
|
|
328
|
+
else:
|
|
329
|
+
node.logger.debug(
|
|
330
|
+
"Dropped validator ping to %s (%s); outgoing queue full",
|
|
331
|
+
address,
|
|
332
|
+
peer_key.hex()
|
|
333
|
+
if isinstance(peer_key, (bytes, bytearray))
|
|
334
|
+
else peer_key,
|
|
335
|
+
)
|
|
336
|
+
except Exception:
|
|
337
|
+
node.logger.exception("Failed queueing validator ping to %s", address)
|
|
338
|
+
|
|
339
|
+
# upload block atoms
|
|
340
|
+
for block_atom in new_block_atoms:
|
|
341
|
+
atom_id = block_atom.object_id()
|
|
342
|
+
node._cold_storage_set(atom_id, block_atom)
|
|
343
|
+
|
|
344
|
+
# upload receipt atoms
|
|
345
|
+
for receipt_atom in receipt_atoms:
|
|
346
|
+
atom_id = receipt_atom.object_id()
|
|
347
|
+
node._cold_storage_set(atom_id, receipt_atom)
|
|
348
|
+
|
|
349
|
+
# upload transaction atoms
|
|
350
|
+
for transaction_atom in transaction_atoms:
|
|
351
|
+
atom_id = transaction_atom.object_id()
|
|
352
|
+
node._cold_storage_set(atom_id, transaction_atom)
|
|
353
|
+
|
|
354
|
+
# upload account atoms
|
|
355
|
+
for account_atom in account_atoms:
|
|
356
|
+
atom_id = account_atom.object_id()
|
|
357
|
+
node._cold_storage_set(atom_id, account_atom)
|
|
358
|
+
|
|
359
|
+
node.logger.info("Validation worker stopped")
|
|
360
|
+
|
|
361
|
+
return _validation_worker
|