astreum 0.2.39__py3-none-any.whl → 0.2.41__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.
Files changed (42) hide show
  1. astreum/_communication/__init__.py +2 -0
  2. astreum/{models → _communication}/message.py +100 -64
  3. astreum/_communication/ping.py +33 -0
  4. astreum/_communication/route.py +53 -20
  5. astreum/_communication/setup.py +240 -99
  6. astreum/_communication/util.py +42 -0
  7. astreum/_consensus/__init__.py +6 -0
  8. astreum/_consensus/account.py +170 -0
  9. astreum/_consensus/accounts.py +67 -0
  10. astreum/_consensus/block.py +84 -52
  11. astreum/_consensus/chain.py +65 -62
  12. astreum/_consensus/fork.py +99 -97
  13. astreum/_consensus/genesis.py +141 -0
  14. astreum/_consensus/receipt.py +177 -0
  15. astreum/_consensus/setup.py +21 -162
  16. astreum/_consensus/transaction.py +43 -23
  17. astreum/_consensus/workers/__init__.py +9 -0
  18. astreum/_consensus/workers/discovery.py +48 -0
  19. astreum/_consensus/workers/validation.py +122 -0
  20. astreum/_consensus/workers/verify.py +63 -0
  21. astreum/_storage/atom.py +24 -7
  22. astreum/_storage/patricia.py +443 -0
  23. astreum/models/block.py +10 -10
  24. astreum/node.py +755 -753
  25. {astreum-0.2.39.dist-info → astreum-0.2.41.dist-info}/METADATA +1 -1
  26. astreum-0.2.41.dist-info/RECORD +53 -0
  27. astreum/lispeum/__init__.py +0 -0
  28. astreum/lispeum/environment.py +0 -40
  29. astreum/lispeum/expression.py +0 -86
  30. astreum/lispeum/parser.py +0 -41
  31. astreum/lispeum/tokenizer.py +0 -52
  32. astreum/models/account.py +0 -91
  33. astreum/models/accounts.py +0 -34
  34. astreum/models/transaction.py +0 -106
  35. astreum/relay/__init__.py +0 -0
  36. astreum/relay/peer.py +0 -9
  37. astreum/relay/route.py +0 -25
  38. astreum/relay/setup.py +0 -58
  39. astreum-0.2.39.dist-info/RECORD +0 -55
  40. {astreum-0.2.39.dist-info → astreum-0.2.41.dist-info}/WHEEL +0 -0
  41. {astreum-0.2.39.dist-info → astreum-0.2.41.dist-info}/licenses/LICENSE +0 -0
  42. {astreum-0.2.39.dist-info → astreum-0.2.41.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: astreum
3
- Version: 0.2.39
3
+ Version: 0.2.41
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
@@ -0,0 +1,53 @@
1
+ astreum/__init__.py,sha256=9tzA27B_eG5wRF1SAWJIV7xTmCcR1QFc123b_cvFOa4,345
2
+ astreum/_node.py,sha256=3fpfULVs3MrPBR-ymlvPyuZMi8lv0a8JBnxPb2oFOTU,2214
3
+ astreum/format.py,sha256=X4tG5GGPweNCE54bHYkLFiuLTbmpy5upO_s1Cef-MGA,2711
4
+ astreum/node.py,sha256=MmlK3jaANTMB3ZAxR8IaSc82OS9meJmVawYIVURADbg,39689
5
+ astreum/_communication/__init__.py,sha256=XJui0yOcfAur4HKt-8sSRlwB-MSU1rchkuOAY-nKDOE,207
6
+ astreum/_communication/message.py,sha256=zNjUnt1roJRHMEoiLkcA7VK_TsHXok8PYEF7KTCqKnQ,3303
7
+ astreum/_communication/peer.py,sha256=DT2vJOzeLyyOj7vTDQ1u1lF5Vc7Epj0Hhie-YfDrzfA,425
8
+ astreum/_communication/ping.py,sha256=u_DQTZJsbMdYiDDqjdZDsLaN5na2m9WZjVeEM3zq9_Y,955
9
+ astreum/_communication/route.py,sha256=fbVXY0xF3O-k7dY9TuCsr6_XxD3m7Cb9ugVacQZ6GSk,2490
10
+ astreum/_communication/setup.py,sha256=TzXpc8dajV31BJhHQMMcb5UdaFrxb1xlYSj58vc_5mg,9072
11
+ astreum/_communication/util.py,sha256=bJ3td3naDzmCelAJQpLwiDMoRBkijQl9YLROjsWyOrI,1256
12
+ astreum/_consensus/__init__.py,sha256=gOCpvnIeO17CGjUGr0odaKNvGEggmDRXfT5IuyrYtcM,376
13
+ astreum/_consensus/account.py,sha256=d95h5c8kvmLD4CCyGRvaTJRQK7AESKrqDG8bCdbZOKg,5614
14
+ astreum/_consensus/accounts.py,sha256=NaLKDafsfq9SgFMtrYQpvAO7o0XbEwAIRhFl0dkY_Dk,2277
15
+ astreum/_consensus/block.py,sha256=v6U2y8br9PVKGn0Q8fLVOtEODlXayPtEd_Wqprem_TU,12358
16
+ astreum/_consensus/chain.py,sha256=WwUeLrdg7uj--ZsUxse6xFlzO2QeQQggyKSL49KhfU0,2768
17
+ astreum/_consensus/fork.py,sha256=dK5DtT9hWCj_rQs6MS1c1bcGBbkgVTBPIOudYbqS9vw,3825
18
+ astreum/_consensus/genesis.py,sha256=Xe9LA58cBz_IoutSJrtjeGDuFo0W-MVrWMkSdEwAVog,4760
19
+ astreum/_consensus/receipt.py,sha256=GPLspqVJHnyBr1cKmBoClsJyeEUecYmg3_acz4L4rRo,6031
20
+ astreum/_consensus/setup.py,sha256=agwqfOemdLqE0Z1zrPV2XHVmZ9sAxp8Bh4k6e2u-t8w,2385
21
+ astreum/_consensus/transaction.py,sha256=LK4oDe9EepOfzLxeXc19fEFDBoxY-aj_v-nsuRyET8g,6490
22
+ astreum/_consensus/workers/__init__.py,sha256=bS5FjbevbIR5FHbVGnT4Jli17VIld_5auemRw4CaHFU,278
23
+ astreum/_consensus/workers/discovery.py,sha256=X1yjKGjLSApMJ9mgWbnc7N21ALD09khDf-in-M45Mis,1683
24
+ astreum/_consensus/workers/validation.py,sha256=WcHLOs2NLMTrJpErghBXWeW2aqtX3-xGPo-HcySdEZ8,4814
25
+ astreum/_consensus/workers/verify.py,sha256=uqmf2UICj_eBFeGDpGyzKQLtz9qeCbOtz5euwNqkZmw,1924
26
+ astreum/_lispeum/__init__.py,sha256=LAy2Z-gBBQlByBHRGUKaaQrOB7QzFEEyGRtInmwTpfU,304
27
+ astreum/_lispeum/environment.py,sha256=pJ0rjp9GoQxHhDiPIVei0jP7dZ_Pznso2O_tpp94-Ik,328
28
+ astreum/_lispeum/expression.py,sha256=io8tbCer_1TJee77yRbcNI5q-DPFGa8xZiC80tGvRRQ,1063
29
+ astreum/_lispeum/high_evaluation.py,sha256=7MwIeVZMPumYvKXn6Lkn-GrZhRNB6MjnUUWdiYT7Ei0,7952
30
+ astreum/_lispeum/low_evaluation.py,sha256=HgyCSAmL5K5SKq3Xw_BtbTZZUJbMg4-bVZW-A12glQ0,4373
31
+ astreum/_lispeum/meter.py,sha256=5q2PFW7_jmgKVM1-vwE4RRjMfPEthUA4iu1CwR-Axws,505
32
+ astreum/_lispeum/parser.py,sha256=WOW3sSZWkIzPEy3fYTyl0lrtkMxHL9zRrNSYztPDemQ,2271
33
+ astreum/_lispeum/tokenizer.py,sha256=P68uIj4aPKzjuCJ85jfzRi67QztpuXIOC1vvLQueBI4,552
34
+ astreum/_storage/__init__.py,sha256=EmKZNAZmo3UVE3ekOOuckwFnBVjpa0Sy8Oxg72Lgdxc,53
35
+ astreum/_storage/atom.py,sha256=YFjvfMhniInch13iaKGpw4CCxxgyWonniryb-Rfse4A,4177
36
+ astreum/_storage/patricia.py,sha256=Eh8AO2_J8WCai4kyuML_0Jb_zntgGlq-VPljnSjss10,14851
37
+ astreum/crypto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
+ astreum/crypto/ed25519.py,sha256=FRnvlN0kZlxn4j-sJKl-C9tqiz_0z4LZyXLj3KIj1TQ,1760
39
+ astreum/crypto/quadratic_form.py,sha256=pJgbORey2NTWbQNhdyvrjy_6yjORudQ67jBz2ScHptg,4037
40
+ astreum/crypto/wesolowski.py,sha256=SUgGXW3Id07dJtWzDcs4dluIhjqbRWQ8YWjn_mK78AQ,4092
41
+ astreum/crypto/x25519.py,sha256=i29v4BmwKRcbz9E7NKqFDQyxzFtJUqN0St9jd7GS1uA,1137
42
+ astreum/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
+ astreum/models/block.py,sha256=7qKUhOpL26NiiLDq_ff7mJCNrbxWb_9jPTtMtjemy3Y,18126
44
+ astreum/models/merkle.py,sha256=lvWJa9nmrBL0n_2h_uNqpB_9a5s5Hn1FceRLx0IZIVQ,6778
45
+ astreum/models/patricia.py,sha256=ohmXrcaz7Ae561tyC4u4iPOkQPkKr8N0IWJek4upFIg,13392
46
+ astreum/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
+ astreum/storage/object.py,sha256=knFlvw_tpcC4twSu1DGNpHX31wlANN8E5dgEqIfU--Q,2041
48
+ astreum/storage/setup.py,sha256=1-9ztEFI_BvRDvAA0lAn4mFya8iq65THTArlj--M3Hg,626
49
+ astreum-0.2.41.dist-info/licenses/LICENSE,sha256=gYBvRDP-cPLmTyJhvZ346QkrYW_eleke4Z2Yyyu43eQ,1089
50
+ astreum-0.2.41.dist-info/METADATA,sha256=03v5FuUofHssnDaviuCFvqWB1p3hz6lqvhM1t9HFhas,6181
51
+ astreum-0.2.41.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
52
+ astreum-0.2.41.dist-info/top_level.txt,sha256=1EG1GmkOk3NPmUA98FZNdKouhRyget-KiFiMk0i2Uz0,8
53
+ astreum-0.2.41.dist-info/RECORD,,
File without changes
@@ -1,40 +0,0 @@
1
- from typing import Dict, Optional
2
- import uuid
3
-
4
- from astreum.lispeum.expression import Expr
5
-
6
-
7
- class Env:
8
- def __init__(
9
- self,
10
- data: Optional[Dict[str, Expr]] = None,
11
- parent_id: Optional[uuid.UUID] = None,
12
- max_exprs: Optional[int] = 8,
13
- ):
14
- self.data: Dict[str, Expr] = data if data is not None else {}
15
- self.parent_id: Optional[uuid.UUID] = parent_id
16
- self.max_exprs: Optional[int] = max_exprs
17
-
18
- def put(self, name: str, value: Expr) -> None:
19
- if (
20
- self.max_exprs is not None
21
- and name not in self.data
22
- and len(self.data) >= self.max_exprs
23
- ):
24
- raise RuntimeError(
25
- f"environment full: {len(self.data)} ≥ max_exprs={self.max_exprs}"
26
- )
27
- self.data[name] = value
28
-
29
- def get(self, name: str) -> Optional[Expr]:
30
- return self.data.get(name)
31
-
32
- def pop(self, name: str) -> Optional[Expr]:
33
- return self.data.pop(name, None)
34
-
35
- def __repr__(self) -> str:
36
- return (
37
- f"Env(size={len(self.data)}, "
38
- f"max_exprs={self.max_exprs}, "
39
- f"parent_id={self.parent_id})"
40
- )
@@ -1,86 +0,0 @@
1
-
2
- from typing import List, Optional, Union
3
-
4
-
5
- class Expr:
6
- class ListExpr:
7
- def __init__(self, elements: List['Expr']):
8
- self.elements = elements
9
-
10
- def __eq__(self, other):
11
- if not isinstance(other, Expr.ListExpr):
12
- return NotImplemented
13
- return self.elements == other.elements
14
-
15
- def __ne__(self, other):
16
- return not self.__eq__(other)
17
-
18
- @property
19
- def value(self):
20
- inner = " ".join(str(e) for e in self.elements)
21
- return f"({inner})"
22
-
23
-
24
- def __repr__(self):
25
- if not self.elements:
26
- return "()"
27
-
28
- inner = " ".join(str(e) for e in self.elements)
29
- return f"({inner})"
30
-
31
- def __iter__(self):
32
- return iter(self.elements)
33
-
34
- def __getitem__(self, index: Union[int, slice]):
35
- return self.elements[index]
36
-
37
- def __len__(self):
38
- return len(self.elements)
39
-
40
- class Symbol:
41
- def __init__(self, value: str):
42
- self.value = value
43
-
44
- def __repr__(self):
45
- return self.value
46
-
47
- class Integer:
48
- def __init__(self, value: int):
49
- self.value = value
50
-
51
- def __repr__(self):
52
- return str(self.value)
53
-
54
- class String:
55
- def __init__(self, value: str):
56
- self.value = value
57
-
58
- def __repr__(self):
59
- return f'"{self.value}"'
60
-
61
- class Boolean:
62
- def __init__(self, value: bool):
63
- self.value = value
64
-
65
- def __repr__(self):
66
- return "true" if self.value else "false"
67
-
68
- class Function:
69
- def __init__(self, params: List[str], body: 'Expr'):
70
- self.params = params
71
- self.body = body
72
-
73
- def __repr__(self):
74
- params_str = " ".join(self.params)
75
- body_str = str(self.body)
76
- return f"(fn ({params_str}) {body_str})"
77
-
78
- class Error:
79
- def __init__(self, message: str, origin: Optional['Expr'] = None):
80
- self.message = message
81
- self.origin = origin
82
-
83
- def __repr__(self):
84
- if self.origin is None:
85
- return f'(error "{self.message}")'
86
- return f'(error "{self.message}" in {self.origin})'
astreum/lispeum/parser.py DELETED
@@ -1,41 +0,0 @@
1
- from typing import List, Tuple
2
- from ..node import Expr
3
-
4
- class ParseError(Exception):
5
- pass
6
-
7
- def parse(tokens: List[str]) -> Tuple[Expr, List[str]]:
8
- if not tokens:
9
- raise ParseError("Unexpected end of input")
10
-
11
- first_token, *rest = tokens
12
-
13
- if first_token == '(':
14
- if not rest:
15
- raise ParseError("Expected token after '('")
16
-
17
- list_items = []
18
- inner_tokens = rest
19
-
20
- while inner_tokens:
21
- if inner_tokens[0] == ')':
22
- return Expr.ListExpr(list_items), inner_tokens[1:]
23
-
24
- expr, inner_tokens = parse(inner_tokens)
25
- list_items.append(expr)
26
-
27
- raise ParseError("Expected closing ')'")
28
-
29
- elif first_token == ')':
30
- raise ParseError("Unexpected closing parenthesis")
31
-
32
- elif first_token.startswith('"') and first_token.endswith('"'):
33
- string_content = first_token[1:-1]
34
- return Expr.String(string_content), rest
35
-
36
- else:
37
- try:
38
- number = int(first_token)
39
- return Expr.Integer(number), rest
40
- except ValueError:
41
- return Expr.Symbol(first_token), rest
@@ -1,52 +0,0 @@
1
-
2
-
3
- # Tokenizer function
4
- from typing import List
5
-
6
- class ParseError(Exception):
7
- pass
8
-
9
- def tokenize(input: str) -> List[str]:
10
- tokens = []
11
- current_token = ""
12
- in_string = False
13
- escape = False
14
-
15
- for char in input:
16
- if in_string:
17
- if escape:
18
- current_token += char
19
- escape = False
20
- elif char == '\\':
21
- escape = True
22
- elif char == '"':
23
- in_string = False
24
- tokens.append(f'"{current_token}"')
25
- current_token = ""
26
- else:
27
- current_token += char
28
- else:
29
- if char.isspace():
30
- if current_token:
31
- tokens.append(current_token)
32
- current_token = ""
33
- elif char == '(' or char == ')':
34
- if current_token:
35
- tokens.append(current_token)
36
- current_token = ""
37
- tokens.append(char)
38
- elif char == '"':
39
- if current_token:
40
- tokens.append(current_token)
41
- current_token = ""
42
- in_string = True
43
- else:
44
- current_token += char
45
-
46
- if in_string:
47
- raise ParseError("Unterminated string literal")
48
-
49
- if current_token:
50
- tokens.append(current_token)
51
-
52
- return tokens
astreum/models/account.py DELETED
@@ -1,91 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import Optional, Callable
4
-
5
- from .merkle import MerkleTree
6
-
7
- _FIELD_ORDER = ["balance", "data", "nonce"]
8
- _INT_FIELDS = {"balance", "nonce"}
9
-
10
- def _int_to_min_bytes(i: int) -> bytes:
11
- length = (i.bit_length() + 7) // 8 or 1
12
- return i.to_bytes(length, "big")
13
-
14
- class Account:
15
- def __init__(
16
- self,
17
- body_hash: bytes,
18
- *,
19
- body_tree: Optional[MerkleTree] = None,
20
- get_node_fn: Optional[Callable[[bytes], Optional[bytes]]] = None,
21
- ) -> None:
22
- self._body_hash = body_hash
23
- self._body_tree = body_tree
24
- self._balance: Optional[int] = None
25
- self._data: Optional[bytes] = None
26
- self._nonce: Optional[int] = None
27
-
28
- if self._body_tree and get_node_fn:
29
- self._body_tree._node_get = get_node_fn
30
-
31
- @classmethod
32
- def create(
33
- cls,
34
- balance: int,
35
- data: bytes,
36
- nonce: int,
37
- ) -> Account:
38
- """Build an Account body from explicit fields in alphabetical order."""
39
- # prepare values dict
40
- values = {"balance": balance, "data": data, "nonce": nonce}
41
-
42
- # build leaves in alphabetical order
43
- leaves: list[bytes] = []
44
- for name in _FIELD_ORDER:
45
- v = values[name]
46
- if name in _INT_FIELDS:
47
- leaves.append(_int_to_min_bytes(v)) # type: ignore[arg-type]
48
- else:
49
- leaves.append(v)
50
-
51
- tree = MerkleTree.from_leaves(leaves)
52
- return cls(tree.root_hash, body_tree=tree)
53
-
54
- def body_hash(self) -> bytes:
55
- """Return the Merkle root of the account body."""
56
- return self._body_hash
57
-
58
- def _require_tree(self) -> MerkleTree:
59
- if not self._body_tree:
60
- raise ValueError("Body tree unavailable for this Account")
61
- return self._body_tree
62
-
63
- def balance(self) -> int:
64
- """Fetch & cache the `balance` field (leaf 0)."""
65
- if self._balance is not None:
66
- return self._balance
67
- raw = self._require_tree().get(0)
68
- if raw is None:
69
- raise ValueError("Merkle leaf 0 (balance) missing")
70
- self._balance = int.from_bytes(raw, "big")
71
- return self._balance
72
-
73
- def data(self) -> bytes:
74
- """Fetch & cache the `data` field (leaf 1)."""
75
- if self._data is not None:
76
- return self._data
77
- raw = self._require_tree().get(1)
78
- if raw is None:
79
- raise ValueError("Merkle leaf 1 (data) missing")
80
- self._data = raw
81
- return self._data
82
-
83
- def nonce(self) -> int:
84
- """Fetch & cache the `nonce` field (leaf 2)."""
85
- if self._nonce is not None:
86
- return self._nonce
87
- raw = self._require_tree().get(2)
88
- if raw is None:
89
- raise ValueError("Merkle leaf 2 (nonce) missing")
90
- self._nonce = int.from_bytes(raw, "big")
91
- return self._nonce
@@ -1,34 +0,0 @@
1
- from __future__ import annotations
2
- from typing import Dict, Optional, Callable
3
- from .patricia import PatriciaTrie
4
- from .account import Account
5
-
6
- class Accounts:
7
- def __init__(
8
- self,
9
- root_hash: Optional[bytes] = None,
10
- global_get_fn: Optional[Callable[[bytes], Optional[bytes]]] = None,
11
- ) -> None:
12
- self._global_get_fn = global_get_fn
13
- self._trie = PatriciaTrie(node_get=global_get_fn, root_hash=root_hash)
14
- self._cache: Dict[bytes, Account] = {}
15
-
16
- @property
17
- def root_hash(self) -> Optional[bytes]:
18
- return self._trie.root_hash
19
-
20
- def get_account(self, address: bytes) -> Optional[Account]:
21
- if address in self._cache:
22
- return self._cache[address]
23
-
24
- body_hash: Optional[bytes] = self._trie.get(address)
25
- if body_hash is None:
26
- return None
27
-
28
- acc = Account(body_hash, get_node_fn=self._global_get_fn)
29
- self._cache[address] = acc
30
- return acc
31
-
32
- def set_account(self, address: bytes, account: Account) -> None:
33
- self._cache[address] = account
34
- self._trie.put(address, account.body_hash())
@@ -1,106 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import Dict, List, Optional, Union, Any, Callable
4
-
5
- from .merkle import MerkleTree
6
- from ..crypto import ed25519
7
-
8
- _FIELD_ORDER: List[str] = [
9
- "amount",
10
- "balance",
11
- "fee",
12
- "nonce",
13
- "recipient_pk",
14
- "sender_pk",
15
- ]
16
-
17
- _INT_FIELDS = {"amount", "balance", "fee", "nonce"}
18
-
19
- def _int_to_min_bytes(i: int) -> bytes:
20
- length = (i.bit_length() + 7) // 8 or 1
21
- return i.to_bytes(length, "big")
22
-
23
- class Transaction:
24
- # init
25
- def __init__(
26
- self,
27
- tx_hash: bytes,
28
- *,
29
- tree: Optional[MerkleTree] = None,
30
- global_get_fn: Optional[Callable[[bytes], Optional[bytes]]] = None,
31
- ) -> None:
32
- self._hash = tx_hash
33
- self._tree = tree
34
- self._field_cache: Dict[str, Union[int, bytes]] = {}
35
-
36
- if self._tree and global_get_fn:
37
- self._tree.global_get_fn = global_get_fn
38
-
39
- @classmethod
40
- def create(
41
- cls,
42
- *,
43
- amount: int,
44
- balance: int,
45
- fee: int,
46
- nonce: int,
47
- recipient_pk: bytes,
48
- sender_pk: bytes,
49
- ) -> "Transaction":
50
- vals: Dict[str, Any] = locals().copy()
51
- leaves = [
52
- vals[name] if isinstance(vals[name], bytes) else _int_to_min_bytes(vals[name])
53
- for name in _FIELD_ORDER
54
- ]
55
-
56
- tree = MerkleTree.from_leaves(leaves)
57
- return cls(tx_hash=tree.root_hash, tree=tree)
58
-
59
- @property
60
- def hash(self) -> bytes:
61
- return self._hash
62
-
63
- def _require_tree(self) -> MerkleTree:
64
- if not self._tree:
65
- raise ValueError("Merkle tree unavailable for this Transaction")
66
- return self._tree
67
-
68
- def _field(self, idx: int, name: str) -> Union[int, bytes]:
69
- if name in self._field_cache:
70
- return self._field_cache[name]
71
-
72
- raw = self._require_tree().get(idx)
73
- if raw is None:
74
- raise ValueError(f"Leaf {idx} (‘{name}’) missing from Merkle tree")
75
-
76
- value = int.from_bytes(raw, "big") if name in _INT_FIELDS else raw
77
- self._field_cache[name] = value
78
- return value
79
-
80
- def get_amount(self) -> int:
81
- return self._field(0, "amount")
82
-
83
- def get_balance(self) -> int:
84
- return self._field(1, "balance")
85
-
86
- def get_fee(self) -> int:
87
- return self._field(2, "fee")
88
-
89
- def get_nonce(self) -> int:
90
- return self._field(3, "nonce")
91
-
92
- def get_recipient_pk(self) -> bytes:
93
- return self._field(4, "recipient_pk")
94
-
95
- def get_sender_pk(self) -> bytes:
96
- return self._field(5, "sender_pk")
97
-
98
- def sign(self, priv: ed25519.Ed25519PrivateKey) -> bytes:
99
- return priv.sign(self.hash)
100
-
101
- def verify_signature(self, sig: bytes, sender_pk: bytes) -> bool:
102
- try:
103
- ed25519.Ed25519PublicKey.from_public_bytes(sender_pk).verify(sig, self.hash)
104
- return True
105
- except Exception:
106
- return False
astreum/relay/__init__.py DELETED
File without changes
astreum/relay/peer.py DELETED
@@ -1,9 +0,0 @@
1
- from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey, X25519PublicKey
2
- from datetime import datetime, timezone
3
-
4
- class Peer:
5
- shared_key: bytes
6
- timestamp: datetime
7
- def __init__(self, my_sec_key: X25519PrivateKey, peer_pub_key: X25519PublicKey):
8
- self.shared_key = my_sec_key.exchange(peer_pub_key)
9
- self.timestamp = datetime.now(timezone.utc)
astreum/relay/route.py DELETED
@@ -1,25 +0,0 @@
1
- from typing import Dict, List
2
- from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey, X25519PublicKey
3
-
4
- class Route:
5
- def __init__(self, relay_public_key: X25519PublicKey, bucket_size: int = 16):
6
- self.relay_public_key_bytes = relay_public_key.public_bytes(encoding=Encoding.Raw, format=PublicFormat.Raw)
7
- self.bucket_size = bucket_size
8
- self.buckets: Dict[int, List[X25519PublicKey]] = {
9
- i: [] for i in range(len(self.relay_public_key_bytes) * 8)
10
- }
11
- self.peers = {}
12
-
13
- @staticmethod
14
- def _matching_leading_bits(a: bytes, b: bytes) -> int:
15
- for byte_index, (ba, bb) in enumerate(zip(a, b)):
16
- diff = ba ^ bb
17
- if diff:
18
- return byte_index * 8 + (8 - diff.bit_length())
19
- return len(a) * 8
20
-
21
- def add_peer(self, peer_public_key: X25519PublicKey):
22
- peer_public_key_bytes = peer_public_key.public_bytes(encoding=Encoding.Raw, format=PublicFormat.Raw)
23
- bucket_idx = self._matching_leading_bits(self.relay_public_key_bytes, peer_public_key_bytes)
24
- if len(self.buckets[bucket_idx]) < self.bucket_size:
25
- self.buckets[bucket_idx].append(peer_public_key)
astreum/relay/setup.py DELETED
@@ -1,58 +0,0 @@
1
- import socket, threading
2
- from queue import Queue
3
- from typing import Tuple, Optional
4
- from cryptography.hazmat.primitives.asymmetric import ed25519
5
- from cryptography.hazmat.primitives.asymmetric.x25519 import (
6
- X25519PrivateKey,
7
- X25519PublicKey,
8
- )
9
- from yourproject.routes import Route
10
-
11
- def load_x25519(hex_key: Optional[str]) -> X25519PrivateKey:
12
- """DH key for relaying (always X25519)."""
13
- return
14
-
15
- def load_ed25519(hex_key: Optional[str]) -> Optional[ed25519.Ed25519PrivateKey]:
16
- """Signing key for validation (Ed25519), or None if absent."""
17
- return ed25519.Ed25519PrivateKey.from_private_bytes(bytes.fromhex(hex_key)) \
18
- if hex_key else None
19
-
20
- def make_routes(
21
- relay_pk: X25519PublicKey,
22
- val_sk: Optional[ed25519.Ed25519PrivateKey]
23
- ) -> Tuple[Route, Optional[Route]]:
24
- """Peer route (DH pubkey) + optional validation route (ed pubkey)."""
25
- peer_rt = Route(relay_pk)
26
- val_rt = Route(val_sk.public_key()) if val_sk else None
27
- return peer_rt, val_rt
28
-
29
- def setup_udp(
30
- bind_port: int,
31
- use_ipv6: bool
32
- ) -> Tuple[socket.socket, int, Queue, threading.Thread, threading.Thread]:
33
- fam = socket.AF_INET6 if use_ipv6 else socket.AF_INET
34
- sock = socket.socket(fam, socket.SOCK_DGRAM)
35
- if use_ipv6:
36
- sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
37
- sock.bind(("::" if use_ipv6 else "0.0.0.0", bind_port or 0))
38
- port = sock.getsockname()[1]
39
-
40
- q = Queue()
41
- pop = threading.Thread(target=lambda: None, daemon=True)
42
- proc = threading.Thread(target=lambda: None, daemon=True)
43
- pop.start(); proc.start()
44
- return sock, port, q, pop, proc
45
-
46
- def setup_outgoing(
47
- use_ipv6: bool
48
- ) -> Tuple[socket.socket, Queue, threading.Thread]:
49
- fam = socket.AF_INET6 if use_ipv6 else socket.AF_INET
50
- sock = socket.socket(fam, socket.SOCK_DGRAM)
51
- q = Queue()
52
- thr = threading.Thread(target=lambda: None, daemon=True)
53
- thr.start()
54
- return sock, q, thr
55
-
56
- def make_maps():
57
- """Empty lookup maps: peers and addresses."""
58
- return
@@ -1,55 +0,0 @@
1
- astreum/__init__.py,sha256=9tzA27B_eG5wRF1SAWJIV7xTmCcR1QFc123b_cvFOa4,345
2
- astreum/_node.py,sha256=3fpfULVs3MrPBR-ymlvPyuZMi8lv0a8JBnxPb2oFOTU,2214
3
- astreum/format.py,sha256=X4tG5GGPweNCE54bHYkLFiuLTbmpy5upO_s1Cef-MGA,2711
4
- astreum/node.py,sha256=SuVm1b0QWl1FpDUaLRH1fiFYnXCrPs6qYeUQlPDae8w,38358
5
- astreum/_communication/__init__.py,sha256=I6TVFC1KXtwwdCjKUkGjOhsLWfC7cECubjzqmm-SwRs,161
6
- astreum/_communication/peer.py,sha256=DT2vJOzeLyyOj7vTDQ1u1lF5Vc7Epj0Hhie-YfDrzfA,425
7
- astreum/_communication/route.py,sha256=enWT_1260LJq-L-zK-jtacQ8LbZGquNO9yj-9IglSXE,1232
8
- astreum/_communication/setup.py,sha256=6KH144iaHNec9g4cvC8fcLIBY0dvx5OCjV5WVYKNcZo,3865
9
- astreum/_consensus/__init__.py,sha256=EDwIjOABPYv1c8jL0ib1hd8CuNCovTVrdcwxIy_qhaU,241
10
- astreum/_consensus/block.py,sha256=4ImViawnG0bhN-TiCk3F0byTlPm9U0l6PdSL53u3bsc,11171
11
- astreum/_consensus/chain.py,sha256=lOFbz5z0AOeMLchQ2YPRSbIZIdQlnHqDyMJvgdwJ-Iw,2532
12
- astreum/_consensus/fork.py,sha256=ZGTOVEhSiYJvvLvlC6FV8Zqe62uLTM-cSS3yKrKrLvA,3602
13
- astreum/_consensus/genesis.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- astreum/_consensus/setup.py,sha256=ao9Vcuv8vUUn5yDAFBpjA_ffsmAv0pBeED0eYn997r0,8215
15
- astreum/_consensus/transaction.py,sha256=LhMGQgXR4RxcEGu6m4aYsKAVhSX_bNt17jMtWaagnHc,5992
16
- astreum/_lispeum/__init__.py,sha256=LAy2Z-gBBQlByBHRGUKaaQrOB7QzFEEyGRtInmwTpfU,304
17
- astreum/_lispeum/environment.py,sha256=pJ0rjp9GoQxHhDiPIVei0jP7dZ_Pznso2O_tpp94-Ik,328
18
- astreum/_lispeum/expression.py,sha256=io8tbCer_1TJee77yRbcNI5q-DPFGa8xZiC80tGvRRQ,1063
19
- astreum/_lispeum/high_evaluation.py,sha256=7MwIeVZMPumYvKXn6Lkn-GrZhRNB6MjnUUWdiYT7Ei0,7952
20
- astreum/_lispeum/low_evaluation.py,sha256=HgyCSAmL5K5SKq3Xw_BtbTZZUJbMg4-bVZW-A12glQ0,4373
21
- astreum/_lispeum/meter.py,sha256=5q2PFW7_jmgKVM1-vwE4RRjMfPEthUA4iu1CwR-Axws,505
22
- astreum/_lispeum/parser.py,sha256=WOW3sSZWkIzPEy3fYTyl0lrtkMxHL9zRrNSYztPDemQ,2271
23
- astreum/_lispeum/tokenizer.py,sha256=P68uIj4aPKzjuCJ85jfzRi67QztpuXIOC1vvLQueBI4,552
24
- astreum/_storage/__init__.py,sha256=EmKZNAZmo3UVE3ekOOuckwFnBVjpa0Sy8Oxg72Lgdxc,53
25
- astreum/_storage/atom.py,sha256=w0O_jw74nGhO0hgxFHk0RmkCIgHXHweBEOVULi7_dhQ,3652
26
- astreum/crypto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
- astreum/crypto/ed25519.py,sha256=FRnvlN0kZlxn4j-sJKl-C9tqiz_0z4LZyXLj3KIj1TQ,1760
28
- astreum/crypto/quadratic_form.py,sha256=pJgbORey2NTWbQNhdyvrjy_6yjORudQ67jBz2ScHptg,4037
29
- astreum/crypto/wesolowski.py,sha256=SUgGXW3Id07dJtWzDcs4dluIhjqbRWQ8YWjn_mK78AQ,4092
30
- astreum/crypto/x25519.py,sha256=i29v4BmwKRcbz9E7NKqFDQyxzFtJUqN0St9jd7GS1uA,1137
31
- astreum/lispeum/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
- astreum/lispeum/environment.py,sha256=wolwt9psDl62scgjaVG0G59xlBs1AM4NPgryUbxzG_4,1220
33
- astreum/lispeum/expression.py,sha256=K8gFifDaHu394bs9qnpvP8tjeiymFGQpnDC_iW9nU4E,2379
34
- astreum/lispeum/parser.py,sha256=jQRzZYvBuSg8t_bxsbt1-WcHaR_LPveHNX7Qlxhaw-M,1165
35
- astreum/lispeum/tokenizer.py,sha256=J-I7MEd0r2ZoVqxvRPlu-Afe2ZdM0tKXXhf1R4SxYTo,1429
36
- astreum/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
- astreum/models/account.py,sha256=sHujGSwtV13rvOGJ5LZXuMrJ4F9XUdvyuWKz-zJ9lkE,2986
38
- astreum/models/accounts.py,sha256=aFSEWlq6zRf65-KGAdNGqEJyNVY3fpKhx8y1vU6sgSc,1164
39
- astreum/models/block.py,sha256=Zke4yxZ-OvmjBdGnZsnU6qDQAVFJ2B1Op8ayaz1JPSo,18163
40
- astreum/models/merkle.py,sha256=lvWJa9nmrBL0n_2h_uNqpB_9a5s5Hn1FceRLx0IZIVQ,6778
41
- astreum/models/message.py,sha256=vv8yx-ndVYjCmPM4gXRVMToCTlKY_mflPu0uKsb9iiE,2117
42
- astreum/models/patricia.py,sha256=ohmXrcaz7Ae561tyC4u4iPOkQPkKr8N0IWJek4upFIg,13392
43
- astreum/models/transaction.py,sha256=MkLL5YX18kIf9-O4LBaZ4eWjkXDAaYIrDcDehbDZoqg,3038
44
- astreum/relay/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
- astreum/relay/peer.py,sha256=94rNkHfsvYfq-ijLDR9QQkEJZ1meMr17HsaNYDBB8kg,398
46
- astreum/relay/route.py,sha256=enWT_1260LJq-L-zK-jtacQ8LbZGquNO9yj-9IglSXE,1232
47
- astreum/relay/setup.py,sha256=ynvGaJdlDtw_f5LLiow2Wo7IRzUjvgk8eSr1Sv4_zTg,2090
48
- astreum/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
- astreum/storage/object.py,sha256=knFlvw_tpcC4twSu1DGNpHX31wlANN8E5dgEqIfU--Q,2041
50
- astreum/storage/setup.py,sha256=1-9ztEFI_BvRDvAA0lAn4mFya8iq65THTArlj--M3Hg,626
51
- astreum-0.2.39.dist-info/licenses/LICENSE,sha256=gYBvRDP-cPLmTyJhvZ346QkrYW_eleke4Z2Yyyu43eQ,1089
52
- astreum-0.2.39.dist-info/METADATA,sha256=htQXaHM7_zULw39odLaHu6wtU-W_sa5IXt0GlxEejGU,6181
53
- astreum-0.2.39.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
54
- astreum-0.2.39.dist-info/top_level.txt,sha256=1EG1GmkOk3NPmUA98FZNdKouhRyget-KiFiMk0i2Uz0,8
55
- astreum-0.2.39.dist-info/RECORD,,