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

Files changed (45) hide show
  1. astreum/node.py +10 -0
  2. {astreum-0.2.3.dist-info → astreum-0.2.5.dist-info}/METADATA +1 -1
  3. astreum-0.2.5.dist-info/RECORD +18 -0
  4. astreum/_node/relay/__init__.py +0 -371
  5. astreum/_node/relay/bucket.py +0 -90
  6. astreum/_node/relay/envelope.py +0 -280
  7. astreum/_node/relay/message.py +0 -110
  8. astreum/_node/relay/peer.py +0 -174
  9. astreum/_node/relay/route.py +0 -161
  10. astreum/_node/storage/storage.py +0 -253
  11. astreum/_node/storage/utils.py +0 -137
  12. astreum/_node/utils.py +0 -34
  13. astreum/_node/validation/__init__.py +0 -0
  14. astreum/_node/validation/_block/__init__.py +0 -0
  15. astreum/_node/validation/_block/create.py +0 -98
  16. astreum/_node/validation/_block/model.py +0 -81
  17. astreum/_node/validation/_block/validate.py +0 -196
  18. astreum/_node/validation/account.py +0 -99
  19. astreum/_node/validation/block.py +0 -21
  20. astreum/_node/validation/constants.py +0 -15
  21. astreum/_node/validation/stake.py +0 -229
  22. astreum/_node/validation/transaction.py +0 -146
  23. astreum/_node/validation/vdf.py +0 -80
  24. astreum/lispeum/expression.py +0 -95
  25. astreum/lispeum/special/__init__.py +0 -0
  26. astreum/lispeum/special/definition.py +0 -27
  27. astreum/lispeum/special/list/__init__.py +0 -0
  28. astreum/lispeum/special/list/all.py +0 -32
  29. astreum/lispeum/special/list/any.py +0 -32
  30. astreum/lispeum/special/list/fold.py +0 -29
  31. astreum/lispeum/special/list/get.py +0 -20
  32. astreum/lispeum/special/list/insert.py +0 -23
  33. astreum/lispeum/special/list/map.py +0 -30
  34. astreum/lispeum/special/list/position.py +0 -33
  35. astreum/lispeum/special/list/remove.py +0 -22
  36. astreum/lispeum/special/number/__init__.py +0 -0
  37. astreum/lispeum/special/number/addition.py +0 -0
  38. astreum/lispeum/storage.py +0 -410
  39. astreum/machine/__init__.py +0 -352
  40. astreum/machine/environment.py +0 -4
  41. astreum/machine/error.py +0 -0
  42. astreum-0.2.3.dist-info/RECORD +0 -56
  43. {astreum-0.2.3.dist-info → astreum-0.2.5.dist-info}/WHEEL +0 -0
  44. {astreum-0.2.3.dist-info → astreum-0.2.5.dist-info}/licenses/LICENSE +0 -0
  45. {astreum-0.2.3.dist-info → astreum-0.2.5.dist-info}/top_level.txt +0 -0
@@ -1,81 +0,0 @@
1
- """
2
- Block model for the Astreum blockchain.
3
- """
4
-
5
- import time
6
- import threading
7
- from typing import Dict, List, Optional, Tuple, Set
8
- from dataclasses import dataclass, field
9
-
10
- from ...node.storage.merkle import MerkleTree
11
- from ...utils import hash_data
12
-
13
- class Block:
14
- def __init__(self, number, validator, previous, transactions, timestamp, vdf, signature, proof, receipts, data, delay, difficulty, accounts=None, chain=None):
15
- self.accounts = accounts
16
- self.chain = chain
17
- self.data = data
18
- self.delay = delay
19
- self.difficulty = difficulty
20
- self.number = number
21
- self.validator = validator
22
- self.vdf = vdf
23
- self.previous = previous
24
- self.transactions = transactions
25
- self.timestamp = timestamp
26
- self.signature = signature
27
- self.proof = proof
28
- self.receipts = receipts
29
-
30
- def get_details_hash(self):
31
- detail_fields = [
32
- 'accounts',
33
- 'chain',
34
- 'data',
35
- 'delay',
36
- 'difficulty',
37
- 'number',
38
- 'previous',
39
- 'proof',
40
- 'receipts',
41
- 'timestamp',
42
- 'transactions',
43
- 'validator',
44
- 'vdf',
45
- ]
46
-
47
- # Prepare serialized entries in the exact order of detail_fields
48
- serialized_entries = []
49
- for field in detail_fields:
50
- value = getattr(self, field, None) # Use None if attribute doesn't exist
51
-
52
- # Convert value to bytes directly based on type
53
- if isinstance(value, (bytes, bytearray)):
54
- # Already bytes, use as is
55
- entry = value
56
- elif isinstance(value, (int, float)):
57
- # Convert numbers to bytes
58
- entry = str(value).encode('utf-8')
59
- elif isinstance(value, str):
60
- # Convert strings to bytes
61
- entry = value.encode('utf-8')
62
- elif value is None:
63
- # Handle None values
64
- entry = b''
65
- else:
66
- # For complex types, use their string representation
67
- entry = str(value).encode('utf-8')
68
-
69
- serialized_entries.append(entry)
70
-
71
- # Use MerkleTree from storage module
72
- merkle_tree = MerkleTree()
73
- merkle_tree.add(serialized_entries)
74
-
75
- return merkle_tree.get_root_hash()
76
-
77
-
78
- def get_hash(self):
79
- merkle_tree = MerkleTree()
80
- merkle_tree.add([self.get_details_hash(), self.signature])
81
- return merkle_tree.get_root_hash()
@@ -1,196 +0,0 @@
1
- # Cached verified blocks
2
- verified_blocks = set()
3
-
4
- def validate_block(block: Block, accounts: Dict[bytes, Account],
5
- known_blocks: Dict[bytes, Block]) -> bool:
6
- """
7
- Validate a block.
8
-
9
- Args:
10
- block: Block to validate
11
- accounts: Dictionary of accounts
12
- known_blocks: Dictionary of known blocks
13
-
14
- Returns:
15
- True if block is valid, False otherwise
16
- """
17
- # Skip validation if already verified
18
- block_hash = block.get_hash()
19
- if block_hash in verified_blocks:
20
- return True
21
-
22
- # Check block structure
23
- if not _validate_block_structure(block):
24
- return False
25
-
26
- # Check if previous block exists, except for genesis block
27
- if block.number > 0 and block.previous not in known_blocks:
28
- return False
29
-
30
- # Check if validator is a validator
31
- if not _is_valid_producer(block, accounts):
32
- return False
33
-
34
- # Validate VDF proof
35
- if not _validate_vdf_proof(block):
36
- return False
37
-
38
- # Validate transactions
39
- if not _validate_transactions(block, accounts):
40
- return False
41
-
42
- # All checks passed, mark as verified
43
- verified_blocks.add(block_hash)
44
- return True
45
-
46
- def _validate_block_structure(block: Block) -> bool:
47
- """
48
- Validate basic block structure.
49
-
50
- Args:
51
- block: Block to validate
52
-
53
- Returns:
54
- True if structure is valid, False otherwise
55
- """
56
- # Check for required fields
57
- if (block.number is None or block.time is None or
58
- not hasattr(block, 'validator') or not block.validator):
59
- return False
60
-
61
- # Check that genesis block has no previous
62
- if block.number == 0 and block.previous is not None:
63
- return False
64
-
65
- # Check that non-genesis block has previous
66
- if block.number > 0 and block.previous is None:
67
- return False
68
-
69
- # Check timestamp is reasonable
70
- current_time = int(time.time())
71
- if block.time > current_time + 60: # Allow 1 minute clock drift
72
- return False
73
-
74
- return True
75
-
76
- def _is_valid_producer(block: Block, accounts: Dict[bytes, Account]) -> bool:
77
- """
78
- Check if block validator is a valid validator.
79
-
80
- Args:
81
- block: Block to validate
82
- accounts: Dictionary of accounts
83
-
84
- Returns:
85
- True if validator is valid, False otherwise
86
- """
87
- # Anyone can produce the genesis block
88
- if block.number == 0:
89
- return True
90
-
91
- # Check if validator is a validator with stake
92
- if not block.validator or not hasattr(block.validator, 'public_key'):
93
- return False
94
-
95
- validator_address = block.validator.public_key
96
- stake = get_validator_stake(accounts, validator_address)
97
- return stake >= MIN_STAKE_AMOUNT
98
-
99
- def _validate_vdf_proof(block: Block) -> bool:
100
- """
101
- Validate the VDF proof in the block.
102
-
103
- Args:
104
- block: Block to validate
105
-
106
- Returns:
107
- True if VDF proof is valid, False otherwise
108
- """
109
- # Skip VDF validation for genesis block
110
- if block.number == 0:
111
- return True
112
-
113
- # In a real implementation, this would verify the VDF proof
114
- # For our purposes, we'll assume all blocks have valid VDF proofs
115
- if not block.vdf_proof:
116
- return False
117
-
118
- return validate_block_vdf(block.number, block.previous, block.vdf_proof)
119
-
120
- def _validate_transactions(block: Block, accounts: Dict[bytes, Account]) -> bool:
121
- """
122
- Validate transactions in a block.
123
-
124
- Args:
125
- block: Block to validate
126
- accounts: Dictionary of accounts
127
-
128
- Returns:
129
- True if all transactions are valid, False otherwise
130
- """
131
- # In a real implementation, this would verify each transaction
132
- # For our purposes, we'll assume all transactions in a block are valid
133
- return True
134
-
135
- def select_validator(accounts: Dict[bytes, Account], random_seed: bytes) -> Optional[bytes]:
136
- """
137
- Select a validator based on stake using a random seed.
138
-
139
- Args:
140
- accounts: Dictionary of accounts
141
- random_seed: Random seed for selection
142
-
143
- Returns:
144
- Selected validator address or None if no validators
145
- """
146
- # Get validators (accounts with stake)
147
- validators = {addr: acc for addr, acc in accounts.items()
148
- if get_validator_stake(accounts, addr) >= MIN_STAKE_AMOUNT}
149
-
150
- # Calculate total stake
151
- total_stake = sum(get_validator_stake(accounts, addr) for addr in validators)
152
-
153
- if total_stake <= 0:
154
- return None
155
-
156
- # Convert random seed to a number between 0 and total_stake
157
- seed_hash = hash_data(random_seed)
158
- random_value = int.from_bytes(seed_hash, byteorder='big') % total_stake
159
-
160
- # Build cumulative stake mapping
161
- cumulative_stake = 0
162
- stake_map = {}
163
-
164
- # Sort validators by address for determinism
165
- for addr in sorted(validators.keys()):
166
- cumulative_stake += get_validator_stake(accounts, addr)
167
- stake_map[addr] = cumulative_stake
168
-
169
- # Find the validator whose cumulative stake covers the random value
170
- for addr, cum_stake in stake_map.items():
171
- if random_value < cum_stake:
172
- return addr
173
-
174
- # If no validator found (should not happen), return the last one
175
- if stake_map:
176
- return list(stake_map.keys())[-1]
177
- return None
178
-
179
- def select_validator_for_slot(accounts: Dict[bytes, Account], slot: int,
180
- previous_vdf: bytes) -> Optional[bytes]:
181
- """
182
- Select validator for a specific slot.
183
-
184
- Args:
185
- accounts: Dictionary of accounts
186
- slot: Slot number
187
- previous_vdf: VDF output from previous block
188
-
189
- Returns:
190
- Selected validator address or None if no validators
191
- """
192
- # Generate random seed based on slot and previous VDF
193
- seed = hash_data(previous_vdf + slot.to_bytes(8, byteorder='big'))
194
-
195
- # Select validator based on stake
196
- return select_validator(accounts, seed)
@@ -1,99 +0,0 @@
1
- from typing import Optional
2
- from ..storage.patricia import PatriciaTrie
3
-
4
- import astreum.format as format
5
- class Account:
6
- def __init__(self, public_key: bytes, balance: int, code: bytes, counter: int, data: bytes, secret_key: Optional[bytes] = None):
7
- """
8
- Initialize an Account.
9
-
10
- :param public_key: The public key used as the account identifier (used as trie key).
11
- :param balance: The account balance.
12
- :param code: The associated code (for example, smart contract code).
13
- :param counter: A transaction counter (nonce).
14
- :param data: Additional account data.
15
- :param secret_key: (Optional) The account’s secret key.
16
- """
17
- self.public_key = public_key
18
- self.secret_key = secret_key # Optional private key.
19
- self.balance = balance
20
- self.code = code
21
- self.counter = counter
22
- self.data = data
23
-
24
- @classmethod
25
- def from_bytes(cls, public_key: bytes, data: bytes, secret_key: Optional[bytes] = None) -> 'Account':
26
- """
27
- Deserialize an Account from its byte representation.
28
-
29
- Expected format: [balance, code, counter, data]
30
-
31
- The public_key (and optional secret_key) must be provided separately.
32
- """
33
- decoded = format.decode(data)
34
- balance, code, counter, account_data = decoded
35
- return cls(public_key, balance, code, counter, account_data, secret_key=secret_key)
36
-
37
- def to_bytes(self) -> bytes:
38
- """
39
- Serialize the Account into bytes.
40
-
41
- Format: [balance, code, counter, data]
42
- """
43
- return format.encode([
44
- self.balance,
45
- self.code,
46
- self.counter,
47
- self.data
48
- ])
49
-
50
-
51
- class Accounts(PatriciaTrie):
52
- """
53
- Accounts is a PatriciaTrie storing Account objects.
54
-
55
- The trie key is the account’s public key (bytes), while the value is the account data
56
- (serialized with balance, code, counter, and data only).
57
-
58
- You can instantiate Accounts with a storage instance and, if available, a root hash
59
- representing an existing trie.
60
- """
61
- def get_account(self, public_key: bytes) -> Optional[Account]:
62
- """
63
- Retrieve an account using its public key.
64
-
65
- :param public_key: The public key (bytes) of the account.
66
- :return: The Account instance if found, else None.
67
- """
68
- raw = self.get(public_key)
69
- if raw is None:
70
- return None
71
- return Account.from_bytes(public_key, raw)
72
-
73
- def put_account(self, account: Account) -> None:
74
- """
75
- Insert or update an account in the trie.
76
-
77
- :param account: The Account instance to store.
78
- """
79
- self.put(account.public_key, account.to_bytes())
80
-
81
- def get_account_from_storage(public_key: bytes, accounts: Accounts, storage: Storage) -> Optional[Account]:
82
- # 1. get account details hash
83
- # 2. resolve storage objects to get account details
84
- # 3. return account
85
- return None
86
-
87
- # Example of instantiation:
88
- #
89
- # Assuming you have an instance of Storage (e.g., from your storage module), you can create
90
- # an Accounts instance either as an empty trie or using an existing root hash:
91
- #
92
- # storage = Storage()
93
- #
94
- # # To instantiate an empty Accounts trie:
95
- # accounts = Accounts(storage)
96
- #
97
- # # To instantiate with an existing trie root hash:
98
- # existing_root_hash = b'...' # This would be loaded from persistent storage.
99
- # accounts = Accounts(storage, root_hash=existing_root_hash)
@@ -1,21 +0,0 @@
1
- class Block:
2
-
3
- def __init__(self):
4
- pass
5
-
6
- @classmethod
7
- def from_bytes(cls) -> 'Block':
8
- """
9
- Deserialize a block from its byte representation.
10
- """
11
- return cls()
12
-
13
- def to_bytes(self) -> bytes:
14
- """
15
- Serialize the block into bytes.
16
- """
17
- return b""
18
-
19
- class Chain:
20
- def __init__(self, latest_block: Block):
21
- self.latest_block = latest_block
@@ -1,15 +0,0 @@
1
- """
2
- Constants for the validation module.
3
- """
4
-
5
- import time
6
- from ..utils import hash_data
7
-
8
- # Special addresses
9
- VALIDATION_ADDRESS = b'\xFF' * 32 # Address for staking (all F's)
10
- BURN_ADDRESS = b'\x00' * 32 # Address for burning tokens (all 0's)
11
-
12
- # Validation parameters
13
- MIN_STAKE_AMOUNT = 1 # Minimum stake amount in Aster
14
- SLOT_DURATION = 1 # Duration of each slot in seconds
15
- VDF_DIFFICULTY = 1 # Default VDF difficulty
@@ -1,229 +0,0 @@
1
- """
2
- Stake management for validators.
3
- """
4
-
5
- import time
6
- import random
7
- import json
8
- from typing import Dict, List, Optional, Tuple
9
-
10
- from ...crypto import hash_data
11
- from ..models import Transaction, TransactionType
12
- from ..storage import Storage, MerkleNode
13
- from .constants import SLOT_DURATION, MIN_STAKE_AMOUNT, VALIDATION_ADDRESS
14
- from .account import Account
15
-
16
-
17
- def get_validator_stake(storage: Storage, stake_root: bytes, validator_address: bytes) -> int:
18
- """
19
- Get the stake amount for a specific validator.
20
-
21
- Args:
22
- storage: Storage instance
23
- stake_root: Root hash of the stake Merkle tree
24
- validator_address: Address of the validator to check
25
-
26
- Returns:
27
- Stake amount for the validator, or 0 if not a validator
28
- """
29
- from ..storage.merkle import find_first
30
-
31
- # Find the validator node containing the stake information
32
- def match_validator(node_data: bytes) -> bool:
33
- try:
34
- stake_data = json.loads(node_data.decode('utf-8'))
35
- return stake_data.get('address') == validator_address.hex()
36
- except:
37
- return False
38
-
39
- validator_node = find_first(storage, stake_root, match_validator)
40
-
41
- if validator_node:
42
- try:
43
- stake_data = json.loads(validator_node.decode('utf-8'))
44
- return stake_data.get('stake', 0)
45
- except:
46
- return 0
47
-
48
- return 0
49
-
50
-
51
- def is_validator(storage: Storage, stake_root: bytes, address: bytes) -> bool:
52
- """
53
- Check if an address is registered as a validator.
54
-
55
- Args:
56
- storage: Storage instance
57
- stake_root: Root hash of the stake Merkle tree
58
- address: Address to check
59
-
60
- Returns:
61
- True if the address is a validator, False otherwise
62
- """
63
- stake = get_validator_stake(storage, stake_root, address)
64
- return stake >= MIN_STAKE_AMOUNT
65
-
66
-
67
- def get_all_validators(storage: Storage, stake_root: bytes) -> Dict[bytes, int]:
68
- """
69
- Get all validators and their stakes.
70
-
71
- Args:
72
- storage: Storage instance
73
- stake_root: Root hash of the stake Merkle tree
74
-
75
- Returns:
76
- Dictionary mapping validator addresses to their stakes
77
- """
78
- from ..storage.merkle import find_all
79
-
80
- validators = {}
81
-
82
- # Find all validator nodes containing stake information
83
- def match_all_validators(node_data: bytes) -> bool:
84
- return True # Match all nodes
85
-
86
- validator_nodes = find_all(storage, stake_root, match_all_validators)
87
-
88
- for node_data in validator_nodes:
89
- try:
90
- stake_data = json.loads(node_data.decode('utf-8'))
91
- address = bytes.fromhex(stake_data.get('address', ''))
92
- stake = stake_data.get('stake', 0)
93
-
94
- if stake >= MIN_STAKE_AMOUNT:
95
- validators[address] = stake
96
- except:
97
- continue
98
-
99
- return validators
100
-
101
-
102
- def process_stake_transaction(
103
- storage: Storage,
104
- stake_root: bytes,
105
- validator_address: bytes,
106
- amount: int
107
- ) -> bytes:
108
- """
109
- Process a staking transaction and update the stake tree.
110
-
111
- Args:
112
- storage: Storage instance
113
- stake_root: Current root hash of the stake Merkle tree
114
- validator_address: Address of the validator staking tokens
115
- amount: Amount being staked
116
-
117
- Returns:
118
- New root hash of the stake Merkle tree after the update
119
- """
120
- from ..storage.merkle import find_first, MerkleTree
121
-
122
- # Find the validator node if it exists
123
- def match_validator(node_data: bytes) -> bool:
124
- try:
125
- stake_data = json.loads(node_data.decode('utf-8'))
126
- return stake_data.get('address') == validator_address.hex()
127
- except:
128
- return False
129
-
130
- validator_node = find_first(storage, stake_root, match_validator)
131
-
132
- # Create or update the validator stake
133
- if validator_node:
134
- # Update existing validator
135
- try:
136
- stake_data = json.loads(validator_node.decode('utf-8'))
137
- current_stake = stake_data.get('stake', 0)
138
- stake_data['stake'] = current_stake + amount
139
- updated_node = json.dumps(stake_data).encode('utf-8')
140
-
141
- # Replace the node in the tree
142
- merkle_tree = MerkleTree(storage)
143
- merkle_tree.root_hash = stake_root
144
- new_root = merkle_tree.add(updated_node)
145
- except Exception as e:
146
- print(f"Error updating validator stake: {e}")
147
- return stake_root
148
- else:
149
- # Create new validator
150
- stake_data = {
151
- 'address': validator_address.hex(),
152
- 'stake': amount,
153
- 'last_update': int(time.time())
154
- }
155
- new_node = json.dumps(stake_data).encode('utf-8')
156
-
157
- # Add the node to the tree
158
- merkle_tree = MerkleTree(storage)
159
- if stake_root:
160
- merkle_tree.root_hash = stake_root
161
- new_root = merkle_tree.add(new_node)
162
- else:
163
- new_root = merkle_tree.add([new_node])
164
-
165
- return new_root
166
-
167
-
168
- class SlotManager:
169
- """
170
- Manager for tracking slots and selecting validators.
171
- """
172
-
173
- def __init__(self, storage: Storage, stake_root: bytes = None):
174
- """
175
- Initialize slot manager.
176
-
177
- Args:
178
- storage: Storage instance
179
- stake_root: Root hash of the stake Merkle tree
180
- """
181
- self.storage = storage
182
- self.stake_root = stake_root
183
-
184
- def get_current_slot(self) -> int:
185
- """
186
- Get the current slot based on current time.
187
-
188
- Returns:
189
- Current slot number
190
- """
191
- return int(time.time() // SLOT_DURATION)
192
-
193
- def select_validator_for_slot(self, slot: int, previous_vdf: bytes) -> Optional[bytes]:
194
- """
195
- Select a validator for the given slot.
196
-
197
- Args:
198
- slot: Slot number
199
- previous_vdf: VDF output from previous block
200
-
201
- Returns:
202
- Selected validator address, or None if no validators available
203
- """
204
- # Get all validators
205
- validators = get_all_validators(self.storage, self.stake_root)
206
-
207
- if not validators:
208
- return None
209
-
210
- # Generate random seed based on slot and previous VDF
211
- seed = hash_data(previous_vdf + slot.to_bytes(8, byteorder='big'))
212
-
213
- # Select validator based on stake and random seed
214
- total_stake = sum(validators.values())
215
- if total_stake == 0:
216
- return None
217
-
218
- # Convert seed to a random number between 0 and 1
219
- random_value = int.from_bytes(seed, byteorder='big') / (2**(len(seed) * 8) - 1)
220
-
221
- # Select validator based on stake weight
222
- cumulative_prob = 0
223
- for validator, stake in validators.items():
224
- cumulative_prob += stake / total_stake
225
- if random_value <= cumulative_prob:
226
- return validator
227
-
228
- # Should never reach here unless there's a rounding error
229
- return list(validators.keys())[0]