astreum 0.2.61__py3-none-any.whl → 0.3.9__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 (86) hide show
  1. astreum/__init__.py +16 -7
  2. astreum/{_communication → communication}/__init__.py +3 -3
  3. astreum/communication/handlers/handshake.py +89 -0
  4. astreum/communication/handlers/object_request.py +176 -0
  5. astreum/communication/handlers/object_response.py +115 -0
  6. astreum/communication/handlers/ping.py +34 -0
  7. astreum/communication/handlers/route_request.py +76 -0
  8. astreum/communication/handlers/route_response.py +53 -0
  9. astreum/communication/models/__init__.py +0 -0
  10. astreum/communication/models/message.py +124 -0
  11. astreum/communication/models/peer.py +51 -0
  12. astreum/{_communication → communication/models}/route.py +7 -12
  13. astreum/communication/processors/__init__.py +0 -0
  14. astreum/communication/processors/incoming.py +98 -0
  15. astreum/communication/processors/outgoing.py +20 -0
  16. astreum/communication/setup.py +166 -0
  17. astreum/communication/start.py +37 -0
  18. astreum/{_communication → communication}/util.py +7 -0
  19. astreum/consensus/__init__.py +20 -0
  20. astreum/consensus/genesis.py +66 -0
  21. astreum/consensus/models/__init__.py +0 -0
  22. astreum/consensus/models/account.py +84 -0
  23. astreum/consensus/models/accounts.py +72 -0
  24. astreum/consensus/models/block.py +364 -0
  25. astreum/{_consensus → consensus/models}/chain.py +7 -7
  26. astreum/{_consensus → consensus/models}/fork.py +8 -8
  27. astreum/consensus/models/receipt.py +98 -0
  28. astreum/{_consensus → consensus/models}/transaction.py +76 -78
  29. astreum/{_consensus → consensus}/setup.py +18 -50
  30. astreum/consensus/start.py +67 -0
  31. astreum/consensus/validator.py +95 -0
  32. astreum/{_consensus → consensus}/workers/discovery.py +19 -1
  33. astreum/consensus/workers/validation.py +307 -0
  34. astreum/{_consensus → consensus}/workers/verify.py +29 -2
  35. astreum/crypto/chacha20poly1305.py +74 -0
  36. astreum/machine/__init__.py +20 -0
  37. astreum/machine/evaluations/__init__.py +0 -0
  38. astreum/{_lispeum → machine/evaluations}/high_evaluation.py +237 -236
  39. astreum/machine/evaluations/low_evaluation.py +281 -0
  40. astreum/machine/evaluations/script_evaluation.py +27 -0
  41. astreum/machine/models/__init__.py +0 -0
  42. astreum/machine/models/environment.py +31 -0
  43. astreum/{_lispeum → machine/models}/expression.py +36 -8
  44. astreum/machine/tokenizer.py +90 -0
  45. astreum/node.py +78 -767
  46. astreum/storage/__init__.py +7 -0
  47. astreum/storage/actions/get.py +183 -0
  48. astreum/storage/actions/set.py +178 -0
  49. astreum/{_storage → storage/models}/atom.py +55 -57
  50. astreum/{_storage/patricia.py → storage/models/trie.py} +227 -203
  51. astreum/storage/requests.py +28 -0
  52. astreum/storage/setup.py +22 -15
  53. astreum/utils/config.py +48 -0
  54. {astreum-0.2.61.dist-info → astreum-0.3.9.dist-info}/METADATA +27 -26
  55. astreum-0.3.9.dist-info/RECORD +71 -0
  56. astreum/_communication/message.py +0 -101
  57. astreum/_communication/peer.py +0 -23
  58. astreum/_communication/setup.py +0 -322
  59. astreum/_consensus/__init__.py +0 -20
  60. astreum/_consensus/account.py +0 -95
  61. astreum/_consensus/accounts.py +0 -38
  62. astreum/_consensus/block.py +0 -311
  63. astreum/_consensus/genesis.py +0 -72
  64. astreum/_consensus/receipt.py +0 -136
  65. astreum/_consensus/workers/validation.py +0 -125
  66. astreum/_lispeum/__init__.py +0 -16
  67. astreum/_lispeum/environment.py +0 -13
  68. astreum/_lispeum/low_evaluation.py +0 -123
  69. astreum/_lispeum/tokenizer.py +0 -22
  70. astreum/_node.py +0 -198
  71. astreum/_storage/__init__.py +0 -7
  72. astreum/_storage/setup.py +0 -35
  73. astreum/format.py +0 -75
  74. astreum/models/block.py +0 -441
  75. astreum/models/merkle.py +0 -205
  76. astreum/models/patricia.py +0 -393
  77. astreum/storage/object.py +0 -68
  78. astreum-0.2.61.dist-info/RECORD +0 -57
  79. /astreum/{models → communication/handlers}/__init__.py +0 -0
  80. /astreum/{_communication → communication/models}/ping.py +0 -0
  81. /astreum/{_consensus → consensus}/workers/__init__.py +0 -0
  82. /astreum/{_lispeum → machine/models}/meter.py +0 -0
  83. /astreum/{_lispeum → machine}/parser.py +0 -0
  84. {astreum-0.2.61.dist-info → astreum-0.3.9.dist-info}/WHEEL +0 -0
  85. {astreum-0.2.61.dist-info → astreum-0.3.9.dist-info}/licenses/LICENSE +0 -0
  86. {astreum-0.2.61.dist-info → astreum-0.3.9.dist-info}/top_level.txt +0 -0
@@ -1,236 +1,237 @@
1
- from typing import List, Optional, Union
2
- import uuid
3
-
4
- from .environment import Env
5
- from .expression import Expr, error_expr, ERROR_SYMBOL
6
- from .meter import Meter
7
-
8
-
9
- def _is_error(expr: Expr) -> bool:
10
- return (
11
- isinstance(expr, Expr.ListExpr)
12
- and bool(expr.elements)
13
- and isinstance(expr.elements[0], Expr.Symbol)
14
- and expr.elements[0].value == ERROR_SYMBOL
15
- )
16
-
17
-
18
- def _hex_symbol_to_bytes(value: Optional[str]) -> Optional[bytes]:
19
- if not value:
20
- return None
21
- data = value.strip()
22
- if data.startswith(("0x", "0X")):
23
- data = data[2:]
24
- if len(data) % 2:
25
- data = "0" + data
26
- try:
27
- return bytes.fromhex(data)
28
- except ValueError:
29
- return None
30
-
31
-
32
- def _expr_to_bytes(expr: Expr) -> Optional[bytes]:
33
- if isinstance(expr, Expr.Bytes):
34
- return expr.value
35
- if isinstance(expr, Expr.Symbol):
36
- return _hex_symbol_to_bytes(expr.value)
37
- return None
38
-
39
-
40
- def high_eval(self, env_id: uuid.UUID, expr: Expr, meter = None) -> Expr:
41
- if meter is None:
42
- meter = Meter()
43
-
44
- call_env_id = uuid.uuid4()
45
- self.environments[call_env_id] = Env(parent_id=env_id)
46
- env_id = call_env_id
47
-
48
- try:
49
- # ---------- atoms ----------
50
- if _is_error(expr):
51
- return expr
52
-
53
- if isinstance(expr, Expr.Symbol):
54
- bound = self.env_get(env_id, expr.value.encode())
55
- if bound is None:
56
- return error_expr("eval", f"unbound symbol '{expr.value}'")
57
- return bound
58
-
59
- if not isinstance(expr, Expr.ListExpr):
60
- return expr # Expr.Byte or other literals passthrough
61
-
62
- # ---------- empty / single ----------
63
- if len(expr.elements) == 0:
64
- return expr
65
- if len(expr.elements) == 1:
66
- return self.high_eval(env_id, expr.elements[0], meter)
67
-
68
- tail = expr.elements[-1]
69
-
70
- # ---------- (value name def) ----------
71
- if isinstance(tail, Expr.Symbol) and tail.value == "def":
72
- if len(expr.elements) < 3:
73
- return error_expr("eval", "def expects (value name def)")
74
- name_e = expr.elements[-2]
75
- if not isinstance(name_e, Expr.Symbol):
76
- return error_expr("eval", "def name must be symbol")
77
- value_e = expr.elements[-3]
78
- value_res = self.high_eval(env_id, value_e, meter)
79
- if _is_error(value_res):
80
- return value_res
81
- self.env_set(env_id, name_e.value.encode(), value_res)
82
- return value_res
83
-
84
- # Reference Call
85
- # (atom_id ref)
86
- if isinstance(tail, Expr.Symbol) and tail.value == "ref":
87
- if len(expr.elements) != 2:
88
- return error_expr("eval", "ref expects (atom_id ref)")
89
- key_bytes = _expr_to_bytes(expr.elements[0])
90
- if not key_bytes:
91
- return error_expr("eval", "ref expects (atom_id ref)")
92
- stored_list = self.get_expr_list_from_storage(key_bytes)
93
- if stored_list is None:
94
- return error_expr("eval", "ref target not found")
95
- return stored_list
96
-
97
- # Low Level Call
98
- # (arg1 arg2 ... ((body) sk))
99
- if isinstance(tail, Expr.ListExpr):
100
- inner = tail.elements
101
- if len(inner) >= 2 and isinstance(inner[-1], Expr.Symbol) and inner[-1].value == "sk":
102
- body_expr = inner[-2]
103
- if not isinstance(body_expr, Expr.ListExpr):
104
- return error_expr("eval", "sk body must be list")
105
-
106
- # helper: turn an Expr into a contiguous bytes buffer
107
- def to_bytes(v: Expr) -> Union[bytes, Expr]:
108
- if isinstance(v, Expr.Byte):
109
- return bytes([v.value & 0xFF])
110
- if isinstance(v, Expr.ListExpr):
111
- # expect a list of Expr.Byte
112
- out: bytearray = bytearray()
113
- for el in v.elements:
114
- if isinstance(el, Expr.Byte):
115
- out.append(el.value & 0xFF)
116
- else:
117
- return error_expr("eval", "byte list must contain only Byte elements")
118
- return bytes(out)
119
- if _is_error(v):
120
- return v
121
- return error_expr("eval", "argument must resolve to Byte or (Byte ...)")
122
-
123
- # resolve ALL preceding args into bytes (can be Byte or List[Byte])
124
- args_exprs = expr.elements[:-1]
125
- arg_bytes: List[bytes] = []
126
- for a in args_exprs:
127
- v = self.high_eval(env_id, a, meter)
128
- if _is_error(v):
129
- return v
130
- vb = to_bytes(v)
131
- if not isinstance(vb, bytes):
132
- if _is_error(vb):
133
- return vb
134
- return error_expr("eval", "unexpected expression while coercing to bytes")
135
- arg_bytes.append(vb)
136
-
137
- # build low-level code with $0-based placeholders ($0 = first arg)
138
- code: List[bytes] = []
139
-
140
- def emit(tok: Expr) -> Union[None, Expr]:
141
- if isinstance(tok, Expr.Symbol):
142
- name = tok.value
143
- if name.startswith("$"):
144
- idx_s = name[1:]
145
- if not idx_s.isdigit():
146
- return error_expr("eval", "invalid sk placeholder")
147
- idx = int(idx_s) # $0 is first
148
- if idx < 0 or idx >= len(arg_bytes):
149
- return error_expr("eval", "arity mismatch in sk placeholder")
150
- code.append(arg_bytes[idx])
151
- return None
152
- code.append(name.encode())
153
- return None
154
-
155
- if isinstance(tok, Expr.Byte):
156
- code.append(bytes([tok.value & 0xFF]))
157
- return None
158
-
159
- if isinstance(tok, Expr.ListExpr):
160
- rv = self.high_eval(env_id, tok, meter)
161
- if _is_error(rv):
162
- return rv
163
- rb = to_bytes(rv)
164
- if not isinstance(rb, bytes):
165
- if _is_error(rb):
166
- return rb
167
- return error_expr("eval", "unexpected expression while coercing list token to bytes")
168
- code.append(rb)
169
- return None
170
-
171
- if _is_error(tok):
172
- return tok
173
-
174
- return error_expr("eval", "invalid token in sk body")
175
-
176
- for t in body_expr.elements:
177
- err = emit(t)
178
- if err is not None and _is_error(err):
179
- return err
180
-
181
- # Execute low-level code built from sk-body using the caller's meter
182
- res = self.low_eval(code, meter=meter)
183
- return res
184
-
185
- # High Level Call
186
- # (arg1 arg2 ... ((body) (params) fn))
187
- if isinstance(tail, Expr.ListExpr):
188
- fn_form = tail
189
- if (len(fn_form.elements) >= 3
190
- and isinstance(fn_form.elements[-1], Expr.Symbol)
191
- and fn_form.elements[-1].value == "fn"):
192
-
193
- body_expr = fn_form.elements[-3]
194
- params_expr = fn_form.elements[-2]
195
-
196
- if not isinstance(body_expr, Expr.ListExpr):
197
- return error_expr("eval", "fn body must be list")
198
- if not isinstance(params_expr, Expr.ListExpr):
199
- return error_expr("eval", "fn params must be list")
200
-
201
- params: List[bytes] = []
202
- for p in params_expr.elements:
203
- if not isinstance(p, Expr.Symbol):
204
- return error_expr("eval", "fn param must be symbol")
205
- params.append(p.value.encode())
206
-
207
- args_exprs = expr.elements[:-1]
208
- if len(args_exprs) != len(params):
209
- return error_expr("eval", "arity mismatch")
210
-
211
- arg_bytes: List[bytes] = []
212
- for a in args_exprs:
213
- v = self.high_eval(env_id, a, meter)
214
- if _is_error(v):
215
- return v
216
- if not isinstance(v, Expr.Byte):
217
- return error_expr("eval", "argument must resolve to Byte")
218
- arg_bytes.append(bytes([v.value & 0xFF]))
219
-
220
- # child env, bind params -> Expr.Byte
221
- child_env = uuid.uuid4()
222
- self.environments[child_env] = Env(parent_id=env_id)
223
- try:
224
- for name_b, val_b in zip(params, arg_bytes):
225
- self.env_set(child_env, name_b, Expr.Byte(val_b[0]))
226
-
227
- # evaluate HL body, metered from the top
228
- return self.high_eval(child_env, body_expr, meter)
229
- finally:
230
- self.environments.pop(child_env, None)
231
-
232
- # ---------- default: resolve each element and return list ----------
233
- resolved: List[Expr] = [self.high_eval(env_id, e, meter) for e in expr.elements]
234
- return Expr.ListExpr(resolved)
235
- finally:
236
- self.environments.pop(call_env_id, None)
1
+ from typing import List, Optional, Union
2
+ import uuid
3
+
4
+ from ..models.environment import Env
5
+ from ..models.expression import Expr, error_expr, ERROR_SYMBOL
6
+ from ..models.meter import Meter
7
+
8
+
9
+ def _is_error(expr: Expr) -> bool:
10
+ return (
11
+ isinstance(expr, Expr.ListExpr)
12
+ and bool(expr.elements)
13
+ and isinstance(expr.elements[0], Expr.Symbol)
14
+ and expr.elements[0].value == ERROR_SYMBOL
15
+ )
16
+
17
+
18
+ def _hex_symbol_to_bytes(value: Optional[str]) -> Optional[bytes]:
19
+ if not value:
20
+ return None
21
+ data = value.strip()
22
+ if data.startswith(("0x", "0X")):
23
+ data = data[2:]
24
+ if len(data) % 2:
25
+ data = "0" + data
26
+ try:
27
+ return bytes.fromhex(data)
28
+ except ValueError:
29
+ return None
30
+
31
+
32
+ def _expr_to_bytes(expr: Expr) -> Optional[bytes]:
33
+ if isinstance(expr, Expr.Bytes):
34
+ return expr.value
35
+ if isinstance(expr, Expr.Symbol):
36
+ return _hex_symbol_to_bytes(expr.value)
37
+ return None
38
+
39
+
40
+ def high_eval(self, expr: Expr, env_id: Optional[uuid.UUID] = None, meter = None) -> Expr:
41
+ """Evaluate high-level expressions with scoped environments and metering."""
42
+ if meter is None:
43
+ meter = Meter()
44
+
45
+ call_env_id = uuid.uuid4()
46
+ self.environments[call_env_id] = Env(parent_id=env_id)
47
+ env_id = call_env_id
48
+
49
+ try:
50
+ # ---------- atoms ----------
51
+ if _is_error(expr):
52
+ return expr
53
+
54
+ if isinstance(expr, Expr.Symbol):
55
+ bound = self.env_get(env_id, expr.value)
56
+ if bound is None:
57
+ return error_expr("eval", f"unbound symbol '{expr.value}'")
58
+ return bound
59
+
60
+ if not isinstance(expr, Expr.ListExpr):
61
+ return expr # Expr.Bytes or other literals passthrough
62
+
63
+ # ---------- empty / single ----------
64
+ if len(expr.elements) == 0:
65
+ return expr
66
+ if len(expr.elements) == 1:
67
+ return self.high_eval(expr=expr.elements[0], env_id=env_id, meter=meter)
68
+
69
+ tail = expr.elements[-1]
70
+
71
+ # ---------- (value name def) ----------
72
+ if isinstance(tail, Expr.Symbol) and tail.value == "def":
73
+ if len(expr.elements) < 3:
74
+ return error_expr("eval", "def expects (value name def)")
75
+ name_e = expr.elements[-2]
76
+ if not isinstance(name_e, Expr.Symbol):
77
+ return error_expr("eval", "def name must be symbol")
78
+ value_e = expr.elements[-3]
79
+ value_res = self.high_eval(expr=value_e, env_id=env_id, meter=meter)
80
+ if _is_error(value_res):
81
+ return value_res
82
+ self.env_set(call_env_id, name_e.value, value_res)
83
+ return value_res
84
+
85
+ # Reference Call
86
+ # (atom_id ref)
87
+ if isinstance(tail, Expr.Symbol) and tail.value == "ref":
88
+ if len(expr.elements) != 2:
89
+ return error_expr("eval", "ref expects (atom_id ref)")
90
+ key_bytes = _expr_to_bytes(expr.elements[0])
91
+ if not key_bytes:
92
+ return error_expr("eval", "ref expects (atom_id ref)")
93
+ stored_list = self.get_expr_list_from_storage(key_bytes)
94
+ if stored_list is None:
95
+ return error_expr("eval", "ref target not found")
96
+ return stored_list
97
+
98
+ # Low Level Call
99
+ # (arg1 arg2 ... ((body) sk))
100
+ if isinstance(tail, Expr.ListExpr):
101
+ inner = tail.elements
102
+ if len(inner) >= 2 and isinstance(inner[-1], Expr.Symbol) and inner[-1].value == "sk":
103
+ body_expr = inner[-2]
104
+ if not isinstance(body_expr, Expr.ListExpr):
105
+ return error_expr("eval", "sk body must be list")
106
+
107
+ # helper: turn an Expr into a contiguous bytes buffer
108
+ def to_bytes(v: Expr) -> Union[bytes, Expr]:
109
+ if isinstance(v, Expr.Bytes):
110
+ return v.value
111
+ if isinstance(v, Expr.ListExpr):
112
+ # expect a list of Expr.Bytes
113
+ out: bytearray = bytearray()
114
+ for el in v.elements:
115
+ if isinstance(el, Expr.Bytes):
116
+ out.extend(el.value)
117
+ else:
118
+ return error_expr("eval", "byte list must contain only Bytes elements")
119
+ return bytes(out)
120
+ if _is_error(v):
121
+ return v
122
+ return error_expr("eval", "argument must resolve to Bytes or (Bytes ...)")
123
+
124
+ # resolve ALL preceding args into bytes (can be Bytes or List[Bytes])
125
+ args_exprs = expr.elements[:-1]
126
+ arg_bytes: List[bytes] = []
127
+ for a in args_exprs:
128
+ v = self.high_eval(expr=a, env_id=env_id, meter=meter)
129
+ if _is_error(v):
130
+ return v
131
+ vb = to_bytes(v)
132
+ if not isinstance(vb, bytes):
133
+ if _is_error(vb):
134
+ return vb
135
+ return error_expr("eval", "unexpected expression while coercing to bytes")
136
+ arg_bytes.append(vb)
137
+
138
+ # build low-level code with $0-based placeholders ($0 = first arg)
139
+ code: List[bytes] = []
140
+
141
+ def emit(tok: Expr) -> Union[None, Expr]:
142
+ if isinstance(tok, Expr.Symbol):
143
+ name = tok.value
144
+ if name.startswith("$"):
145
+ idx_s = name[1:]
146
+ if not idx_s.isdigit():
147
+ return error_expr("eval", "invalid sk placeholder")
148
+ idx = int(idx_s) # $0 is first
149
+ if idx < 0 or idx >= len(arg_bytes):
150
+ return error_expr("eval", "arity mismatch in sk placeholder")
151
+ code.append(arg_bytes[idx])
152
+ return None
153
+ code.append(name.encode())
154
+ return None
155
+
156
+ if isinstance(tok, Expr.Bytes):
157
+ code.append(tok.value)
158
+ return None
159
+
160
+ if isinstance(tok, Expr.ListExpr):
161
+ rv = self.high_eval(expr=tok, env_id=env_id, meter=meter)
162
+ if _is_error(rv):
163
+ return rv
164
+ rb = to_bytes(rv)
165
+ if not isinstance(rb, bytes):
166
+ if _is_error(rb):
167
+ return rb
168
+ return error_expr("eval", "unexpected expression while coercing list token to bytes")
169
+ code.append(rb)
170
+ return None
171
+
172
+ if _is_error(tok):
173
+ return tok
174
+
175
+ return error_expr("eval", "invalid token in sk body")
176
+
177
+ for t in body_expr.elements:
178
+ err = emit(t)
179
+ if err is not None and _is_error(err):
180
+ return err
181
+
182
+ # Execute low-level code built from sk-body using the caller's meter
183
+ res = self.low_eval(code, meter=meter)
184
+ return res
185
+
186
+ # High Level Call
187
+ # (arg1 arg2 ... ((body) (params) fn))
188
+ if isinstance(tail, Expr.ListExpr):
189
+ fn_form = tail
190
+ if (len(fn_form.elements) >= 3
191
+ and isinstance(fn_form.elements[-1], Expr.Symbol)
192
+ and fn_form.elements[-1].value == "fn"):
193
+
194
+ body_expr = fn_form.elements[-3]
195
+ params_expr = fn_form.elements[-2]
196
+
197
+ if not isinstance(body_expr, Expr.ListExpr):
198
+ return error_expr("eval", "fn body must be list")
199
+ if not isinstance(params_expr, Expr.ListExpr):
200
+ return error_expr("eval", "fn params must be list")
201
+
202
+ params: List[str] = []
203
+ for p in params_expr.elements:
204
+ if not isinstance(p, Expr.Symbol):
205
+ return error_expr("eval", "fn param must be symbol")
206
+ params.append(p.value)
207
+
208
+ args_exprs = expr.elements[:-1]
209
+ if len(args_exprs) != len(params):
210
+ return error_expr("eval", "arity mismatch")
211
+
212
+ arg_bytes: List[bytes] = []
213
+ for a in args_exprs:
214
+ v = self.high_eval(expr=a, env_id=env_id, meter=meter)
215
+ if _is_error(v):
216
+ return v
217
+ if not isinstance(v, Expr.Bytes):
218
+ return error_expr("eval", "argument must resolve to Bytes")
219
+ arg_bytes.append(v.value)
220
+
221
+ # child env, bind params -> Expr.Bytes
222
+ child_env = uuid.uuid4()
223
+ self.environments[child_env] = Env(parent_id=env_id)
224
+ try:
225
+ for name_b, val_b in zip(params, arg_bytes):
226
+ self.env_set(child_env, name_b, Expr.Bytes(val_b))
227
+
228
+ # evaluate HL body, metered from the top
229
+ return self.high_eval(expr=body_expr, env_id=child_env, meter=meter)
230
+ finally:
231
+ self.environments.pop(child_env, None)
232
+
233
+ # ---------- default: resolve each element and return list ----------
234
+ resolved: List[Expr] = [self.high_eval(expr=e, env_id=env_id, meter=meter) for e in expr.elements]
235
+ return Expr.ListExpr(resolved)
236
+ finally:
237
+ self.environments.pop(call_env_id, None)