astreum 0.1.14__py3-none-any.whl → 0.1.15__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.

@@ -0,0 +1,874 @@
1
+ """
2
+ Account validation and stake management for the Astreum blockchain.
3
+ """
4
+
5
+ import time
6
+ import json
7
+ from typing import Dict, Optional, Tuple, List, Set, Any
8
+ from dataclasses import dataclass, field
9
+ from ..utils import hash_data
10
+ from ..storage import Storage
11
+ from ..storage.merkle import MerkleNode, MerkleTree
12
+ from .stake import process_stake_transaction
13
+
14
+ from .constants import VALIDATION_ADDRESS, MIN_STAKE_AMOUNT
15
+
16
+
17
+ @dataclass
18
+ class Account:
19
+ """
20
+ Account class for tracking balance and account state.
21
+ """
22
+ address: bytes
23
+ balance: int = 0
24
+ counter: int = 0
25
+ data: bytes = field(default_factory=lambda: b'')
26
+
27
+ def to_dict(self) -> Dict[str, Any]:
28
+ """Convert account to dictionary representation"""
29
+ return {
30
+ 'address': self.address.hex(),
31
+ 'balance': self.balance,
32
+ 'counter': self.counter,
33
+ 'data': self.data.hex() if self.data else ''
34
+ }
35
+
36
+ @classmethod
37
+ def from_dict(cls, data: Dict[str, Any]) -> 'Account':
38
+ """Create account from dictionary representation"""
39
+ return cls(
40
+ address=bytes.fromhex(data['address']),
41
+ balance=data['balance'],
42
+ counter=data['counter'],
43
+ data=bytes.fromhex(data['data']) if data['data'] else b''
44
+ )
45
+
46
+
47
+ def get_account_details(storage: Storage, details_hash: bytes) -> Optional[Account]:
48
+ """
49
+ Retrieve account details from storage using a details hash.
50
+
51
+ Args:
52
+ storage: Storage instance to retrieve data from
53
+ details_hash: Hash of the account details to retrieve
54
+
55
+ Returns:
56
+ Account object if found, None otherwise
57
+ """
58
+ # Retrieve the account node from storage
59
+ node_data = storage.get(details_hash)
60
+ if not node_data:
61
+ return None
62
+
63
+ # Deserialize the node
64
+ node = MerkleNode.deserialize(node_data)
65
+ if not node or not node.data:
66
+ return None
67
+
68
+ # Try to parse the account data
69
+ try:
70
+ # Assuming the node.data contains a serialized JSON or similar format
71
+ # that can be converted to a dictionary and then to an Account
72
+ import json
73
+ account_dict = json.loads(node.data.decode('utf-8'))
74
+ return Account.from_dict(account_dict)
75
+ except Exception as e:
76
+ print(f"Error parsing account data: {e}")
77
+ return None
78
+
79
+
80
+ def get_validator_stake(accounts: Dict[bytes, Account], validator_address: bytes) -> int:
81
+ """
82
+ Get the stake amount for a specific validator.
83
+
84
+ Args:
85
+ accounts: Dictionary mapping addresses to Account objects
86
+ validator_address: Address of the validator to check
87
+
88
+ Returns:
89
+ Stake amount for the validator, or 0 if not a validator
90
+ """
91
+ # This function would need to be updated to use a separate stake lookup
92
+ # For now, returning 0 as a placeholder
93
+ return 0
94
+
95
+
96
+ def is_validator(accounts: Dict[bytes, Account], address: bytes) -> bool:
97
+ """
98
+ Check if an address is registered as a validator.
99
+
100
+ Args:
101
+ accounts: Dictionary mapping addresses to Account objects
102
+ address: Address to check
103
+
104
+ Returns:
105
+ True if the address is a validator, False otherwise
106
+ """
107
+ # This function would need to be updated to use a separate stake lookup
108
+ # For now, returning False as a placeholder
109
+ return False
110
+
111
+
112
+ def create_genesis_account(address: bytes, balance: int = 1000) -> Account:
113
+ """
114
+ Create a genesis account with initial balance.
115
+
116
+ Args:
117
+ address: Address of the genesis account
118
+ balance: Initial balance (default: 1000)
119
+
120
+ Returns:
121
+ New Account object
122
+ """
123
+ return Account(
124
+ address=address,
125
+ balance=balance,
126
+ counter=0,
127
+ data=b''
128
+ )
129
+
130
+
131
+ def apply_transaction_to_account(
132
+ accounts: Dict[bytes, Account],
133
+ sender: bytes,
134
+ recipient: bytes,
135
+ amount: int,
136
+ data: bytes = b'',
137
+ storage: Optional[Storage] = None,
138
+ stake_root: Optional[bytes] = None
139
+ ) -> Tuple[bool, Optional[bytes]]:
140
+ """
141
+ Apply a transaction to update account balances.
142
+
143
+ Args:
144
+ accounts: Dictionary mapping addresses to Account objects
145
+ sender: Sender address
146
+ recipient: Recipient address
147
+ amount: Amount to transfer
148
+ data: Transaction data
149
+ storage: Storage instance (required for staking transactions)
150
+ stake_root: Root hash of the stake Merkle tree (required for staking transactions)
151
+
152
+ Returns:
153
+ Tuple containing:
154
+ - Boolean indicating if transaction was successfully applied
155
+ - New stake root hash if transaction was a staking transaction, otherwise None
156
+ """
157
+ # Ensure accounts exist
158
+ if sender not in accounts:
159
+ return False, None
160
+
161
+ # Get sender account
162
+ sender_account = accounts[sender]
163
+
164
+ # Check if sender has sufficient balance
165
+ if sender_account.balance < amount:
166
+ return False, None
167
+
168
+ # Handle special case for staking (sending to validation address)
169
+ if recipient == VALIDATION_ADDRESS:
170
+ # This is a staking transaction
171
+ if not storage or stake_root is None:
172
+ # Cannot process staking without storage and stake root
173
+ return False, None
174
+
175
+ # Update account balance
176
+ sender_account.balance -= amount
177
+ sender_account.counter += 1
178
+
179
+ # Update stake in the Merkle tree
180
+ new_stake_root = process_stake_transaction(
181
+ storage,
182
+ stake_root,
183
+ sender,
184
+ amount
185
+ )
186
+
187
+ return True, new_stake_root
188
+
189
+ # Create recipient account if it doesn't exist
190
+ if recipient not in accounts:
191
+ accounts[recipient] = Account(address=recipient)
192
+
193
+ # Get recipient account
194
+ recipient_account = accounts[recipient]
195
+
196
+ # Handle regular transaction
197
+ sender_account.balance -= amount
198
+ recipient_account.balance += amount
199
+ sender_account.counter += 1
200
+
201
+ # Update data if provided
202
+ if data:
203
+ recipient_account.data = data
204
+
205
+ return True, None
206
+
207
+
208
+ def store_account(storage: Storage, account: Account) -> bytes:
209
+ """
210
+ Stores an account in the hierarchical Merkle tree structure and returns the account hash.
211
+
212
+ Structure:
213
+ - Account tree
214
+ - Address tree (addresses as leaves)
215
+ - Details tree
216
+ - Balance
217
+ - Code
218
+ - Counter
219
+ - Storage
220
+
221
+ Args:
222
+ storage: Storage instance
223
+ account: Account to store
224
+
225
+ Returns:
226
+ Root hash of the account tree
227
+ """
228
+ from ..storage.merkle import MerkleTree
229
+ import json
230
+
231
+ # First, build the detail leaves
232
+ detail_leaves = []
233
+
234
+ # Balance leaf
235
+ balance_data = {'type': 'balance', 'value': account.balance}
236
+ balance_leaf = json.dumps(balance_data).encode('utf-8')
237
+ detail_leaves.append(balance_leaf)
238
+
239
+ # Code/data leaf (account data)
240
+ code_data = {'type': 'code', 'value': account.data.hex() if account.data else ''}
241
+ code_leaf = json.dumps(code_data).encode('utf-8')
242
+ detail_leaves.append(code_leaf)
243
+
244
+ # Counter leaf
245
+ counter_data = {'type': 'counter', 'value': account.counter}
246
+ counter_leaf = json.dumps(counter_data).encode('utf-8')
247
+ detail_leaves.append(counter_leaf)
248
+
249
+ # Storage leaf (placeholder for now)
250
+ storage_data = {'type': 'storage', 'value': ''}
251
+ storage_leaf = json.dumps(storage_data).encode('utf-8')
252
+ detail_leaves.append(storage_leaf)
253
+
254
+ # Build the details Merkle tree
255
+ details_tree = MerkleTree(storage)
256
+ details_root = details_tree.add(detail_leaves)
257
+
258
+ # Create address and details nodes
259
+ address_data = {'type': 'address', 'value': account.address.hex()}
260
+ address_leaf = json.dumps(address_data).encode('utf-8')
261
+
262
+ details_data = {'type': 'details', 'root': details_root.hex()}
263
+ details_leaf = json.dumps(details_data).encode('utf-8')
264
+
265
+ # Build the account Merkle tree
266
+ account_tree = MerkleTree(storage)
267
+ account_root = account_tree.add([address_leaf, details_leaf])
268
+
269
+ return account_root
270
+
271
+
272
+ def update_account(storage: Storage, account_root: bytes, account: Account) -> bytes:
273
+ """
274
+ Updates an existing account in the Merkle tree structure.
275
+
276
+ Args:
277
+ storage: Storage instance
278
+ account_root: Existing account root hash
279
+ account: Updated account data
280
+
281
+ Returns:
282
+ New root hash of the account tree
283
+ """
284
+ from ..storage.merkle import MerkleTree, find_first
285
+ import json
286
+
287
+ # First, find the details node
288
+ def is_details_node(node_data: bytes) -> bool:
289
+ try:
290
+ data = json.loads(node_data.decode('utf-8'))
291
+ return data.get('type') == 'details'
292
+ except:
293
+ return False
294
+
295
+ details_node = find_first(storage, account_root, is_details_node)
296
+ if not details_node:
297
+ # If no details node found, create a new account
298
+ return store_account(storage, account)
299
+
300
+ # Get the details root
301
+ details_data = json.loads(details_node.decode('utf-8'))
302
+ details_root = bytes.fromhex(details_data.get('root', ''))
303
+
304
+ # Update each leaf in the details tree
305
+ detail_types = ['balance', 'code', 'counter', 'storage']
306
+ new_leaves = []
307
+
308
+ for detail_type in detail_types:
309
+ # Find the existing leaf
310
+ def is_matching_leaf(node_data: bytes) -> bool:
311
+ try:
312
+ data = json.loads(node_data.decode('utf-8'))
313
+ return data.get('type') == detail_type
314
+ except:
315
+ return False
316
+
317
+ leaf_node = find_first(storage, details_root, is_matching_leaf)
318
+
319
+ # Create updated leaf data
320
+ if detail_type == 'balance':
321
+ leaf_data = {'type': 'balance', 'value': account.balance}
322
+ elif detail_type == 'code':
323
+ leaf_data = {'type': 'code', 'value': account.data.hex() if account.data else ''}
324
+ elif detail_type == 'counter':
325
+ leaf_data = {'type': 'counter', 'value': account.counter}
326
+ elif detail_type == 'storage':
327
+ # Get existing storage value or use empty if not found
328
+ if leaf_node:
329
+ try:
330
+ existing_data = json.loads(leaf_node.decode('utf-8'))
331
+ leaf_data = {'type': 'storage', 'value': existing_data.get('value', '')}
332
+ except:
333
+ leaf_data = {'type': 'storage', 'value': ''}
334
+ else:
335
+ leaf_data = {'type': 'storage', 'value': ''}
336
+
337
+ new_leaves.append(json.dumps(leaf_data).encode('utf-8'))
338
+
339
+ # Build new details tree
340
+ details_tree = MerkleTree(storage)
341
+ new_details_root = details_tree.add(new_leaves)
342
+
343
+ # Update details node
344
+ new_details_data = {'type': 'details', 'root': new_details_root.hex()}
345
+ new_details_leaf = json.dumps(new_details_data).encode('utf-8')
346
+
347
+ # Find address node
348
+ def is_address_node(node_data: bytes) -> bool:
349
+ try:
350
+ data = json.loads(node_data.decode('utf-8'))
351
+ return data.get('type') == 'address'
352
+ except:
353
+ return False
354
+
355
+ address_node = find_first(storage, account_root, is_address_node)
356
+
357
+ # Build new account tree
358
+ account_tree = MerkleTree(storage)
359
+ new_account_root = account_tree.add([address_node, new_details_leaf])
360
+
361
+ return new_account_root
362
+
363
+
364
+ def get_account_from_tree(storage: Storage, account_root: bytes, address: bytes) -> Optional[Account]:
365
+ """
366
+ Retrieve an account from the account Merkle tree based on the address.
367
+
368
+ Args:
369
+ storage: Storage instance
370
+ account_root: Root hash of the account tree
371
+ address: Address to find
372
+
373
+ Returns:
374
+ Account if found, None otherwise
375
+ """
376
+ from ..storage.merkle import find_first
377
+
378
+ # First find the address node to verify it matches
379
+ def is_matching_address(node_data: bytes) -> bool:
380
+ return node_data == address # Direct binary comparison
381
+
382
+ address_node = find_first(storage, account_root, is_matching_address)
383
+ if not address_node:
384
+ return None
385
+
386
+ # Extract balance, code, and counter from the tree
387
+ balance = 0
388
+ code = b''
389
+ counter = 0
390
+
391
+ # Instead of using inefficient search, get the account directly
392
+ account = get_account_from_tree_direct(storage, account_root)
393
+ if not account or account.address != address:
394
+ return None
395
+
396
+ return account
397
+
398
+
399
+ def build_accounts_state_tree(storage: Storage, accounts: Dict[bytes, Account]) -> bytes:
400
+ """
401
+ Build a complete state tree containing multiple accounts.
402
+
403
+ Args:
404
+ storage: Storage instance
405
+ accounts: Dictionary mapping addresses to Account objects
406
+
407
+ Returns:
408
+ Root hash of the state tree
409
+ """
410
+ from ..storage.merkle import MerkleTree
411
+ import json
412
+
413
+ # Store each account and collect their root hashes
414
+ account_nodes = []
415
+
416
+ for address, account in accounts.items():
417
+ # Store the account
418
+ account_root = store_account(storage, account)
419
+
420
+ # Create a state node reference to this account
421
+ state_node_data = {
422
+ 'address': address.hex(),
423
+ 'account_root': account_root.hex()
424
+ }
425
+ state_node = json.dumps(state_node_data).encode('utf-8')
426
+ account_nodes.append(state_node)
427
+
428
+ # Build the state tree
429
+ state_tree = MerkleTree(storage)
430
+ state_root = state_tree.add(account_nodes)
431
+
432
+ return state_root
433
+
434
+
435
+ def get_account_from_state(storage: Storage, state_root: bytes, address: bytes) -> Optional[Account]:
436
+ """
437
+ Retrieve an account from the state tree by address.
438
+
439
+ Args:
440
+ storage: Storage instance
441
+ state_root: Root hash of the state tree
442
+ address: Address to find
443
+
444
+ Returns:
445
+ Account if found, None otherwise
446
+ """
447
+ from ..storage.merkle import find_first
448
+ import json
449
+
450
+ # Find the state node for this address
451
+ def is_matching_state_node(node_data: bytes) -> bool:
452
+ try:
453
+ data = json.loads(node_data.decode('utf-8'))
454
+ return data.get('address') == address.hex()
455
+ except:
456
+ return False
457
+
458
+ state_node = find_first(storage, state_root, is_matching_state_node)
459
+ if not state_node:
460
+ return None
461
+
462
+ # Get the account root
463
+ try:
464
+ state_data = json.loads(state_node.decode('utf-8'))
465
+ account_root = bytes.fromhex(state_data.get('account_root', ''))
466
+ except:
467
+ return None
468
+
469
+ # Get the account from its tree
470
+ return get_account_from_tree(storage, account_root, address)
471
+
472
+
473
+ def update_account_in_state(storage: Storage, state_root: bytes, account: Account) -> bytes:
474
+ """
475
+ Update an account in the state tree or add it if it doesn't exist.
476
+
477
+ Args:
478
+ storage: Storage instance
479
+ state_root: Root hash of the state tree
480
+ account: Account to update
481
+
482
+ Returns:
483
+ New root hash of the state tree
484
+ """
485
+ from ..storage.merkle import MerkleTree, find_first, find_all
486
+ import json
487
+
488
+ # Find all existing state nodes
489
+ def match_all_nodes(node_data: bytes) -> bool:
490
+ return True
491
+
492
+ all_state_nodes = find_all(storage, state_root, match_all_nodes)
493
+
494
+ # Check if this account already exists in state
495
+ account_root = None
496
+ existing_node = None
497
+
498
+ for node in all_state_nodes:
499
+ try:
500
+ data = json.loads(node.decode('utf-8'))
501
+ if data.get('address') == account.address.hex():
502
+ account_root = bytes.fromhex(data.get('account_root', ''))
503
+ existing_node = node
504
+ break
505
+ except:
506
+ continue
507
+
508
+ # Store or update the account
509
+ if account_root:
510
+ # Update existing account
511
+ new_account_root = update_account(storage, account_root, account)
512
+ else:
513
+ # Store new account
514
+ new_account_root = store_account(storage, account)
515
+
516
+ # Create or update state node
517
+ state_node_data = {
518
+ 'address': account.address.hex(),
519
+ 'account_root': new_account_root.hex()
520
+ }
521
+ new_state_node = json.dumps(state_node_data).encode('utf-8')
522
+
523
+ # If we're updating an existing node
524
+ if existing_node:
525
+ # Replace the node in the list
526
+ new_nodes = []
527
+ for node in all_state_nodes:
528
+ if node == existing_node:
529
+ new_nodes.append(new_state_node)
530
+ else:
531
+ new_nodes.append(node)
532
+ else:
533
+ # Add the new node to the list
534
+ new_nodes = list(all_state_nodes)
535
+ new_nodes.append(new_state_node)
536
+
537
+ # Build new state tree
538
+ state_tree = MerkleTree(storage)
539
+ new_state_root = state_tree.add(new_nodes)
540
+
541
+ return new_state_root
542
+
543
+
544
+ def create_account_details_objects(account: Account) -> Dict[bytes, MerkleNode]:
545
+ """
546
+ Creates MerkleNode objects for account details that can be directly stored
547
+ in the storage system.
548
+
549
+ Args:
550
+ account: The account to create objects for
551
+
552
+ Returns:
553
+ Dictionary mapping node hashes to MerkleNode objects
554
+ """
555
+ from ..storage.merkle import MerkleNodeType
556
+
557
+ # Dictionary to store all nodes
558
+ nodes = {}
559
+
560
+ # Create leaf nodes for each detail
561
+ # Balance leaf - directly use 8-byte representation of balance
562
+ balance_bytes = account.balance.to_bytes(8, 'big')
563
+ balance_node = MerkleNode(
564
+ node_type=MerkleNodeType.LEAF,
565
+ hash=hash_data(b'\x00' + balance_bytes),
566
+ data=balance_bytes
567
+ )
568
+ nodes[balance_node.hash] = balance_node
569
+
570
+ # Code/data leaf - directly use raw bytes
571
+ code_bytes = account.data if account.data else b''
572
+ code_node = MerkleNode(
573
+ node_type=MerkleNodeType.LEAF,
574
+ hash=hash_data(b'\x00' + code_bytes),
575
+ data=code_bytes
576
+ )
577
+ nodes[code_node.hash] = code_node
578
+
579
+ # Counter leaf - directly use 8-byte representation of counter
580
+ counter_bytes = account.counter.to_bytes(8, 'big')
581
+ counter_node = MerkleNode(
582
+ node_type=MerkleNodeType.LEAF,
583
+ hash=hash_data(b'\x00' + counter_bytes),
584
+ data=counter_bytes
585
+ )
586
+ nodes[counter_node.hash] = counter_node
587
+
588
+ # Storage leaf - placeholder for account storage data
589
+ storage_bytes = b''
590
+ storage_node = MerkleNode(
591
+ node_type=MerkleNodeType.LEAF,
592
+ hash=hash_data(b'\x00' + storage_bytes),
593
+ data=storage_bytes
594
+ )
595
+ nodes[storage_node.hash] = storage_node
596
+
597
+ # Create branch nodes for the details tree
598
+ # First level: balance+code branch
599
+ balance_code_branch = MerkleNode(
600
+ node_type=MerkleNodeType.BRANCH,
601
+ hash=hash_data(b'\x01' + balance_node.hash + code_node.hash),
602
+ left_child=balance_node.hash,
603
+ right_child=code_node.hash
604
+ )
605
+ nodes[balance_code_branch.hash] = balance_code_branch
606
+
607
+ # First level: counter+storage branch
608
+ counter_storage_branch = MerkleNode(
609
+ node_type=MerkleNodeType.BRANCH,
610
+ hash=hash_data(b'\x01' + counter_node.hash + storage_node.hash),
611
+ left_child=counter_node.hash,
612
+ right_child=storage_node.hash
613
+ )
614
+ nodes[counter_storage_branch.hash] = counter_storage_branch
615
+
616
+ # Second level: details root
617
+ details_root = MerkleNode(
618
+ node_type=MerkleNodeType.BRANCH,
619
+ hash=hash_data(b'\x01' + balance_code_branch.hash + counter_storage_branch.hash),
620
+ left_child=balance_code_branch.hash,
621
+ right_child=counter_storage_branch.hash
622
+ )
623
+ nodes[details_root.hash] = details_root
624
+
625
+ # Address leaf node - directly use raw address bytes
626
+ address_bytes = account.address
627
+ address_node = MerkleNode(
628
+ node_type=MerkleNodeType.LEAF,
629
+ hash=hash_data(b'\x00' + address_bytes),
630
+ data=address_bytes
631
+ )
632
+ nodes[address_node.hash] = address_node
633
+
634
+ # Create the account root node
635
+ account_root = MerkleNode(
636
+ node_type=MerkleNodeType.BRANCH,
637
+ hash=hash_data(b'\x01' + address_node.hash + details_root.hash),
638
+ left_child=address_node.hash,
639
+ right_child=details_root.hash
640
+ )
641
+ nodes[account_root.hash] = account_root
642
+
643
+ return nodes
644
+
645
+
646
+ def store_account_direct(storage: Storage, account: Account) -> bytes:
647
+ """
648
+ Stores an account directly in storage as MerkleNode objects.
649
+
650
+ Args:
651
+ storage: Storage instance
652
+ account: Account to store
653
+
654
+ Returns:
655
+ Root hash of the account tree
656
+ """
657
+ # Create all the nodes
658
+ nodes = create_account_details_objects(account)
659
+
660
+ # Store all nodes in storage
661
+ for node_hash, node in nodes.items():
662
+ storage.put(node_hash, node.serialize())
663
+
664
+ # Return the root hash (the last node is the root)
665
+ account_root_hash = None
666
+ for node_hash, node in nodes.items():
667
+ account_root_hash = node_hash # The last one will be the root
668
+
669
+ return account_root_hash
670
+
671
+
672
+ def update_account_direct(storage: Storage, account_root_hash: bytes, account: Account) -> bytes:
673
+ """
674
+ Updates an account directly in storage using MerkleNode objects.
675
+
676
+ Args:
677
+ storage: Storage instance
678
+ account_root_hash: Current root hash of the account
679
+ account: Updated account data
680
+
681
+ Returns:
682
+ New root hash of the account tree
683
+ """
684
+ from ..storage.merkle import MerkleNodeType
685
+
686
+ # Get the account root node
687
+ root_data = storage.get(account_root_hash)
688
+ if not root_data:
689
+ # Account doesn't exist, create it
690
+ return store_account_direct(storage, account)
691
+
692
+ root_node = MerkleNode.deserialize(root_data)
693
+ if root_node.node_type != MerkleNodeType.BRANCH:
694
+ # Invalid root node, create new account
695
+ return store_account_direct(storage, account)
696
+
697
+ # Create new nodes
698
+ new_nodes = create_account_details_objects(account)
699
+
700
+ # Store all nodes
701
+ for node_hash, node in new_nodes.items():
702
+ storage.put(node_hash, node.serialize())
703
+
704
+ # Return the new root hash
705
+ account_root_hash = None
706
+ for node_hash, node in new_nodes.items():
707
+ account_root_hash = node_hash # The last one will be the root
708
+
709
+ return account_root_hash
710
+
711
+
712
+ def build_state_tree_direct(storage: Storage, accounts: Dict[bytes, Account]) -> bytes:
713
+ """
714
+ Builds a state tree directly from account objects without using MerkleTree.
715
+
716
+ Args:
717
+ storage: Storage instance
718
+ accounts: Dictionary mapping addresses to accounts
719
+
720
+ Returns:
721
+ Root hash of the state tree
722
+ """
723
+ from ..storage.merkle import MerkleNodeType
724
+ import json
725
+
726
+ # Create state leaf nodes for each account
727
+ state_leaves = []
728
+
729
+ for address, account in accounts.items():
730
+ # Store the account
731
+ account_root_hash = store_account_direct(storage, account)
732
+
733
+ # Create state leaf
734
+ state_data = {
735
+ 'address': address.hex(),
736
+ 'account_root': account_root_hash.hex()
737
+ }
738
+ state_bytes = json.dumps(state_data).encode('utf-8')
739
+ state_leaf = MerkleNode(
740
+ node_type=MerkleNodeType.LEAF,
741
+ hash=hash_data(b'\x00' + state_bytes),
742
+ data=state_bytes
743
+ )
744
+ storage.put(state_leaf.hash, state_leaf.serialize())
745
+ state_leaves.append(state_leaf)
746
+
747
+ # If no accounts, return None
748
+ if not state_leaves:
749
+ return None
750
+
751
+ # Build a balanced tree from state leaves
752
+ current_level = state_leaves
753
+
754
+ while len(current_level) > 1:
755
+ next_level = []
756
+
757
+ # Process pairs of nodes
758
+ for i in range(0, len(current_level), 2):
759
+ # If we have a pair, create a branch node
760
+ if i + 1 < len(current_level):
761
+ branch = MerkleNode(
762
+ node_type=MerkleNodeType.BRANCH,
763
+ hash=hash_data(b'\x01' + current_level[i].hash + current_level[i+1].hash),
764
+ left_child=current_level[i].hash,
765
+ right_child=current_level[i+1].hash
766
+ )
767
+ # If we have an odd node left, duplicate it
768
+ else:
769
+ branch = MerkleNode(
770
+ node_type=MerkleNodeType.BRANCH,
771
+ hash=hash_data(b'\x01' + current_level[i].hash + current_level[i].hash),
772
+ left_child=current_level[i].hash,
773
+ right_child=current_level[i].hash
774
+ )
775
+
776
+ storage.put(branch.hash, branch.serialize())
777
+ next_level.append(branch)
778
+
779
+ current_level = next_level
780
+
781
+ # The last remaining node is the root
782
+ return current_level[0].hash if current_level else None
783
+
784
+
785
+ def get_account_from_tree_direct(storage: Storage, account_root_hash: bytes) -> Optional[Account]:
786
+ """
787
+ Retrieves an account from storage using its root hash, working directly with MerkleNodes.
788
+
789
+ Args:
790
+ storage: Storage instance
791
+ account_root_hash: Root hash of the account tree
792
+
793
+ Returns:
794
+ The account if found, None otherwise
795
+ """
796
+ from ..storage.merkle import MerkleNodeType
797
+
798
+ # Get the account root node
799
+ root_data = storage.get(account_root_hash)
800
+ if not root_data:
801
+ return None
802
+
803
+ root_node = MerkleNode.deserialize(root_data)
804
+ if root_node.node_type != MerkleNodeType.BRANCH:
805
+ return None
806
+
807
+ # Get the address node
808
+ address_hash = root_node.left_child
809
+ address_data = storage.get(address_hash)
810
+ if not address_data:
811
+ return None
812
+
813
+ address_node = MerkleNode.deserialize(address_data)
814
+ if address_node.node_type != MerkleNodeType.LEAF:
815
+ return None
816
+
817
+ # Address is directly stored as bytes
818
+ address = address_node.data
819
+
820
+ # Get the details node
821
+ details_hash = root_node.right_child
822
+ details_data = storage.get(details_hash)
823
+ if not details_data:
824
+ return None
825
+
826
+ details_node = MerkleNode.deserialize(details_data)
827
+
828
+ # Get values from the details subtree
829
+ balance = 0
830
+ code = b''
831
+ counter = 0
832
+
833
+ # We need to traverse the details tree to get all values
834
+ if details_node.node_type == MerkleNodeType.BRANCH:
835
+ # Get balance+code branch
836
+ balance_code_hash = details_node.left_child
837
+ balance_code_data = storage.get(balance_code_hash)
838
+ if balance_code_data:
839
+ balance_code_node = MerkleNode.deserialize(balance_code_data)
840
+
841
+ # Get balance node
842
+ balance_hash = balance_code_node.left_child
843
+ balance_data = storage.get(balance_hash)
844
+ if balance_data:
845
+ balance_node = MerkleNode.deserialize(balance_data)
846
+ balance = int.from_bytes(balance_node.data, 'big')
847
+
848
+ # Get code node
849
+ code_hash = balance_code_node.right_child
850
+ code_data = storage.get(code_hash)
851
+ if code_data:
852
+ code_node = MerkleNode.deserialize(code_data)
853
+ code = code_node.data
854
+
855
+ # Get counter+storage branch
856
+ counter_storage_hash = details_node.right_child
857
+ counter_storage_data = storage.get(counter_storage_hash)
858
+ if counter_storage_data:
859
+ counter_storage_node = MerkleNode.deserialize(counter_storage_data)
860
+
861
+ # Get counter node
862
+ counter_hash = counter_storage_node.left_child
863
+ counter_data = storage.get(counter_hash)
864
+ if counter_data:
865
+ counter_node = MerkleNode.deserialize(counter_data)
866
+ counter = int.from_bytes(counter_node.data, 'big')
867
+
868
+ # Create the account with the retrieved values
869
+ return Account(
870
+ address=address,
871
+ balance=balance,
872
+ counter=counter,
873
+ data=code
874
+ )