astreum 0.2.53__tar.gz → 0.3.5__tar.gz

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 (95) hide show
  1. {astreum-0.2.53/src/astreum.egg-info → astreum-0.3.5}/PKG-INFO +25 -24
  2. {astreum-0.2.53 → astreum-0.3.5}/README.md +139 -138
  3. {astreum-0.2.53 → astreum-0.3.5}/pyproject.toml +4 -4
  4. astreum-0.3.5/src/astreum/__init__.py +18 -0
  5. {astreum-0.2.53/src/astreum/_communication → astreum-0.3.5/src/astreum/communication}/__init__.py +3 -3
  6. astreum-0.3.5/src/astreum/communication/handlers/handshake.py +81 -0
  7. astreum-0.3.5/src/astreum/communication/handlers/object_request.py +153 -0
  8. astreum-0.3.5/src/astreum/communication/handlers/object_response.py +37 -0
  9. astreum-0.3.5/src/astreum/communication/handlers/ping.py +48 -0
  10. astreum-0.3.5/src/astreum/communication/handlers/route_request.py +78 -0
  11. astreum-0.3.5/src/astreum/communication/handlers/route_response.py +52 -0
  12. astreum-0.3.5/src/astreum/communication/models/message.py +105 -0
  13. {astreum-0.2.53/src/astreum/_communication → astreum-0.3.5/src/astreum/communication/models}/route.py +7 -12
  14. astreum-0.3.5/src/astreum/communication/setup.py +262 -0
  15. astreum-0.3.5/src/astreum/communication/start.py +38 -0
  16. {astreum-0.2.53/src/astreum/_communication → astreum-0.3.5/src/astreum/communication}/util.py +7 -0
  17. astreum-0.3.5/src/astreum/consensus/__init__.py +20 -0
  18. astreum-0.3.5/src/astreum/consensus/genesis.py +66 -0
  19. astreum-0.3.5/src/astreum/consensus/models/account.py +84 -0
  20. astreum-0.3.5/src/astreum/consensus/models/accounts.py +72 -0
  21. astreum-0.3.5/src/astreum/consensus/models/block.py +364 -0
  22. {astreum-0.2.53/src/astreum/_consensus → astreum-0.3.5/src/astreum/consensus/models}/chain.py +7 -7
  23. {astreum-0.2.53/src/astreum/_consensus → astreum-0.3.5/src/astreum/consensus/models}/fork.py +8 -8
  24. astreum-0.3.5/src/astreum/consensus/models/receipt.py +98 -0
  25. {astreum-0.2.53/src/astreum/_consensus → astreum-0.3.5/src/astreum/consensus/models}/transaction.py +76 -78
  26. {astreum-0.2.53/src/astreum/_consensus → astreum-0.3.5/src/astreum/consensus}/setup.py +18 -50
  27. astreum-0.3.5/src/astreum/consensus/start.py +68 -0
  28. astreum-0.3.5/src/astreum/consensus/validator.py +95 -0
  29. {astreum-0.2.53/src/astreum/_consensus → astreum-0.3.5/src/astreum/consensus}/workers/discovery.py +20 -1
  30. astreum-0.3.5/src/astreum/consensus/workers/validation.py +292 -0
  31. {astreum-0.2.53/src/astreum/_consensus → astreum-0.3.5/src/astreum/consensus}/workers/verify.py +31 -2
  32. astreum-0.3.5/src/astreum/crypto/__init__.py +0 -0
  33. astreum-0.3.5/src/astreum/machine/__init__.py +20 -0
  34. astreum-0.3.5/src/astreum/machine/evaluations/__init__.py +0 -0
  35. {astreum-0.2.53/src/astreum/_lispeum → astreum-0.3.5/src/astreum/machine/evaluations}/high_evaluation.py +230 -183
  36. astreum-0.3.5/src/astreum/machine/evaluations/low_evaluation.py +281 -0
  37. astreum-0.3.5/src/astreum/machine/evaluations/script_evaluation.py +27 -0
  38. astreum-0.3.5/src/astreum/machine/models/__init__.py +0 -0
  39. astreum-0.3.5/src/astreum/machine/models/environment.py +31 -0
  40. {astreum-0.2.53/src/astreum/_lispeum → astreum-0.3.5/src/astreum/machine/models}/expression.py +45 -8
  41. astreum-0.3.5/src/astreum/machine/tokenizer.py +90 -0
  42. astreum-0.3.5/src/astreum/node.py +75 -0
  43. {astreum-0.2.53/src/astreum/_storage → astreum-0.3.5/src/astreum/storage}/__init__.py +1 -1
  44. astreum-0.3.5/src/astreum/storage/actions/get.py +85 -0
  45. astreum-0.3.5/src/astreum/storage/actions/set.py +138 -0
  46. {astreum-0.2.53/src/astreum/_storage → astreum-0.3.5/src/astreum/storage/models}/atom.py +55 -57
  47. astreum-0.2.53/src/astreum/_storage/patricia.py → astreum-0.3.5/src/astreum/storage/models/trie.py +236 -177
  48. astreum-0.3.5/src/astreum/storage/setup.py +22 -0
  49. astreum-0.3.5/src/astreum/utils/config.py +48 -0
  50. {astreum-0.2.53 → astreum-0.3.5/src/astreum.egg-info}/PKG-INFO +25 -24
  51. astreum-0.3.5/src/astreum.egg-info/SOURCES.txt +69 -0
  52. astreum-0.2.53/src/astreum/__init__.py +0 -9
  53. astreum-0.2.53/src/astreum/_communication/message.py +0 -101
  54. astreum-0.2.53/src/astreum/_communication/setup.py +0 -322
  55. astreum-0.2.53/src/astreum/_consensus/__init__.py +0 -20
  56. astreum-0.2.53/src/astreum/_consensus/account.py +0 -95
  57. astreum-0.2.53/src/astreum/_consensus/accounts.py +0 -38
  58. astreum-0.2.53/src/astreum/_consensus/block.py +0 -311
  59. astreum-0.2.53/src/astreum/_consensus/genesis.py +0 -72
  60. astreum-0.2.53/src/astreum/_consensus/receipt.py +0 -177
  61. astreum-0.2.53/src/astreum/_consensus/workers/validation.py +0 -125
  62. astreum-0.2.53/src/astreum/_lispeum/__init__.py +0 -16
  63. astreum-0.2.53/src/astreum/_lispeum/environment.py +0 -13
  64. astreum-0.2.53/src/astreum/_lispeum/low_evaluation.py +0 -123
  65. astreum-0.2.53/src/astreum/_lispeum/tokenizer.py +0 -22
  66. astreum-0.2.53/src/astreum/_node.py +0 -163
  67. astreum-0.2.53/src/astreum/_storage/setup.py +0 -35
  68. astreum-0.2.53/src/astreum/format.py +0 -75
  69. astreum-0.2.53/src/astreum/models/block.py +0 -441
  70. astreum-0.2.53/src/astreum/models/merkle.py +0 -205
  71. astreum-0.2.53/src/astreum/models/patricia.py +0 -393
  72. astreum-0.2.53/src/astreum/node.py +0 -781
  73. astreum-0.2.53/src/astreum/storage/object.py +0 -68
  74. astreum-0.2.53/src/astreum/storage/setup.py +0 -15
  75. astreum-0.2.53/src/astreum.egg-info/SOURCES.txt +0 -60
  76. {astreum-0.2.53 → astreum-0.3.5}/LICENSE +0 -0
  77. {astreum-0.2.53 → astreum-0.3.5}/setup.cfg +0 -0
  78. {astreum-0.2.53/src/astreum/crypto → astreum-0.3.5/src/astreum/communication/handlers}/__init__.py +0 -0
  79. {astreum-0.2.53/src/astreum → astreum-0.3.5/src/astreum/communication}/models/__init__.py +0 -0
  80. {astreum-0.2.53/src/astreum/_communication → astreum-0.3.5/src/astreum/communication/models}/peer.py +0 -0
  81. {astreum-0.2.53/src/astreum/_communication → astreum-0.3.5/src/astreum/communication/models}/ping.py +0 -0
  82. {astreum-0.2.53/src/astreum/storage → astreum-0.3.5/src/astreum/consensus/models}/__init__.py +0 -0
  83. {astreum-0.2.53/src/astreum/_consensus → astreum-0.3.5/src/astreum/consensus}/workers/__init__.py +0 -0
  84. {astreum-0.2.53 → astreum-0.3.5}/src/astreum/crypto/ed25519.py +0 -0
  85. {astreum-0.2.53 → astreum-0.3.5}/src/astreum/crypto/quadratic_form.py +0 -0
  86. {astreum-0.2.53 → astreum-0.3.5}/src/astreum/crypto/wesolowski.py +0 -0
  87. {astreum-0.2.53 → astreum-0.3.5}/src/astreum/crypto/x25519.py +0 -0
  88. {astreum-0.2.53/src/astreum/_lispeum → astreum-0.3.5/src/astreum/machine/models}/meter.py +0 -0
  89. {astreum-0.2.53/src/astreum/_lispeum → astreum-0.3.5/src/astreum/machine}/parser.py +0 -0
  90. {astreum-0.2.53 → astreum-0.3.5}/src/astreum/utils/bytes.py +0 -0
  91. {astreum-0.2.53 → astreum-0.3.5}/src/astreum/utils/integer.py +0 -0
  92. {astreum-0.2.53 → astreum-0.3.5}/src/astreum/utils/logging.py +0 -0
  93. {astreum-0.2.53 → astreum-0.3.5}/src/astreum.egg-info/dependency_links.txt +0 -0
  94. {astreum-0.2.53 → astreum-0.3.5}/src/astreum.egg-info/requires.txt +0 -0
  95. {astreum-0.2.53 → astreum-0.3.5}/src/astreum.egg-info/top_level.txt +0 -0
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: astreum
3
- Version: 0.2.53
4
- Summary: Python library to interact with the Astreum blockchain and its Lispeum virtual machine.
3
+ Version: 0.3.5
4
+ Summary: Python library to interact with the Astreum blockchain and its virtual machine.
5
5
  Author-email: "Roy R. O. Okello" <roy@stelar.xyz>
6
- Project-URL: Homepage, https://github.com/astreum/lib
7
- Project-URL: Issues, https://github.com/astreum/lib/issues
6
+ Project-URL: Homepage, https://github.com/astreum/lib-py
7
+ Project-URL: Issues, https://github.com/astreum/lib-py/issues
8
8
  Classifier: Programming Language :: Python :: 3
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Operating System :: OS Independent
@@ -18,7 +18,7 @@ Dynamic: license-file
18
18
 
19
19
  # lib
20
20
 
21
- Python library to interact with the Astreum blockchain and its Lispeum virtual machine.
21
+ Python library to interact with the Astreum blockchain and its virtual machine.
22
22
 
23
23
  [View on PyPI](https://pypi.org/project/astreum/)
24
24
 
@@ -30,21 +30,21 @@ When initializing an `astreum.Node`, pass a dictionary with any of the options b
30
30
 
31
31
  | Parameter | Type | Default | Description |
32
32
  | --------------------------- | ---------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
33
- | `machine-only` | bool | `True` | When **True** the node starts in *machine‑only* mode: no storage subsystem and no relay networking – only the Lispeum VM. Set to **False** to enable storage and relay features. |
34
- | `relay_secret_key` | hex string | Auto‑generated | Ed25519 private key that identifies the node on the network. If omitted, a fresh keypair is generated and kept in‑memory. |
35
- | `validation_secret_key` | hex string | `None` | X25519 private key that lets the node participate in the validation route. Leave unset for a non‑validator node. |
36
- | `storage_path` | string | `None` | Directory where objects are persisted. If *None*, the node uses an in‑memory store. |
37
- | `storage_get_relay_timeout` | float | `5` | Seconds to wait for an object requested from peers before timing‑out. |
33
+ | `hot_storage_limit` | int | `1073741824` | Maximum bytes kept in the hot cache before new atoms are skipped (1 GiB). |
34
+ | `cold_storage_limit` | int | `10737418240` | Cold storage write threshold (10 GiB by default); set to `0` to skip the limit. |
35
+ | `cold_storage_path` | string | `None` | Directory where persisted atoms live; Astreum creates it on startup and skips cold storage when unset. |
38
36
  | `logging_retention` | int | `90` | Number of days to keep rotated log files (daily gzip). |
39
37
  | `verbose` | bool | `False` | When **True**, also mirror JSON logs to stdout with a human-readable format. |
40
38
 
41
39
  ### Networking
42
40
 
43
- | Parameter | Type | Default | Description |
44
- | --------------- | ----------------------- | ------- | ----------------------------------------------------------------------------------- |
45
- | `use_ipv6` | bool | `False` | Listen on IPv6 as well as IPv4. |
46
- | `incoming_port` | int | `7373` | UDP port the relay binds to. |
47
- | `bootstrap` | list\[tuple\[str, int]] | `[]` | Initial peers used to join the network, e.g. `[ ("bootstrap.astreum.org", 7373) ]`. |
41
+ | Parameter | Type | Default | Description |
42
+ | ------------------------ | ----------- | --------------------- | ------------------------------------------------------------------------------------------------------- |
43
+ | `relay_secret_key` | hex string | Auto-generated | X25519 private key used for the relay route; a new keypair is created when this field is omitted. |
44
+ | `validation_secret_key` | hex string | `None` | Optional Ed25519 key that lets the node join the validation route; leave blank to opt out of validation. |
45
+ | `use_ipv6` | bool | `False` | Bind the incoming/outgoing sockets on IPv6 (the OS still listens on IPv4 if a peer speaks both). |
46
+ | `incoming_port` | int | `7373` | UDP port the relay binds to; pass `0` or omit to let the OS pick an ephemeral port. |
47
+ | `bootstrap` | list\[str\] | `[]` | Addresses to ping with a handshake before joining; each must look like `host:port` or `[ipv6]:port`. |
48
48
 
49
49
  > **Note**
50
50
  > The peer‑to‑peer *route* used for object discovery is always enabled.
@@ -56,16 +56,16 @@ When initializing an `astreum.Node`, pass a dictionary with any of the options b
56
56
  from astreum.node import Node
57
57
 
58
58
  config = {
59
- "machine-only": False, # run full node
60
59
  "relay_secret_key": "ab…cd", # optional – hex encoded
61
60
  "validation_secret_key": "12…34", # optional – validator
62
- "storage_path": "./data/node1",
63
- "storage_get_relay_timeout": 5,
61
+ "hot_storage_limit": 1073741824, # cap hot cache at 1 GiB
62
+ "cold_storage_limit": 10737418240, # cap cold storage at 10 GiB
63
+ "cold_storage_path": "./data/node1",
64
64
  "incoming_port": 7373,
65
65
  "use_ipv6": False,
66
66
  "bootstrap": [
67
- ("bootstrap.astreum.org", 7373),
68
- ("127.0.0.1", 7374)
67
+ "bootstrap.astreum.org:7373",
68
+ "127.0.0.1:7374"
69
69
  ]
70
70
  }
71
71
 
@@ -73,9 +73,10 @@ node = Node(config)
73
73
  # … your code …
74
74
  ```
75
75
 
76
- ## Lispeum Machine Quickstart
77
76
 
78
- The Lispeum virtual machine (VM) is embedded inside `astreum.Node`. You feed it Lispeum source text, and the node tokenizes, parses, and **evaluates** the resulting AST inside an isolated environment.
77
+ ## Astreum Machine Quickstart
78
+
79
+ The Astreum virtual machine (VM) is embedded inside `astreum.Node`. You feed it Astreum script, and the node tokenizes, parses, and evaluates.
79
80
 
80
81
  ```python
81
82
  # Define a named function int.add (stack body) and call it with bytes 1 and 2
@@ -108,10 +109,10 @@ params = Expr.ListExpr([Expr.Symbol("a"), Expr.Symbol("b")])
108
109
  int_add_fn = Expr.ListExpr([fn_body, params, Expr.Symbol("fn")])
109
110
 
110
111
  # 4) Store under the name "int.add"
111
- node.env_set(env_id, b"int.add", int_add_fn)
112
+ node.env_set(env_id, "int.add", int_add_fn)
112
113
 
113
114
  # 5) Retrieve the function and call it with bytes 1 and 2
114
- bound = node.env_get(env_id, b"int.add")
115
+ bound = node.env_get(env_id, "int.add")
115
116
  call = Expr.ListExpr([Expr.Byte(1), Expr.Byte(2), bound])
116
117
  res = node.high_eval(env_id, call)
117
118
 
@@ -1,141 +1,142 @@
1
- # lib
2
-
3
- Python library to interact with the Astreum blockchain and its Lispeum virtual machine.
4
-
5
- [View on PyPI](https://pypi.org/project/astreum/)
6
-
7
- ## Configuration
8
-
9
- When initializing an `astreum.Node`, pass a dictionary with any of the options below. Only the parameters you want to override need to be present – everything else falls back to its default.
10
-
11
- ### Core Configuration
12
-
13
- | Parameter | Type | Default | Description |
14
- | --------------------------- | ---------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
15
- | `machine-only` | bool | `True` | When **True** the node starts in *machine‑only* mode: no storage subsystem and no relay networking – only the Lispeum VM. Set to **False** to enable storage and relay features. |
16
- | `relay_secret_key` | hex string | Auto‑generated | Ed25519 private key that identifies the node on the network. If omitted, a fresh keypair is generated and kept in‑memory. |
17
- | `validation_secret_key` | hex string | `None` | X25519 private key that lets the node participate in the validation route. Leave unset for a non‑validator node. |
18
- | `storage_path` | string | `None` | Directory where objects are persisted. If *None*, the node uses an in‑memory store. |
19
- | `storage_get_relay_timeout` | float | `5` | Seconds to wait for an object requested from peers before timing‑out. |
20
- | `logging_retention` | int | `90` | Number of days to keep rotated log files (daily gzip). |
21
- | `verbose` | bool | `False` | When **True**, also mirror JSON logs to stdout with a human-readable format. |
22
-
23
- ### Networking
24
-
25
- | Parameter | Type | Default | Description |
26
- | --------------- | ----------------------- | ------- | ----------------------------------------------------------------------------------- |
27
- | `use_ipv6` | bool | `False` | Listen on IPv6 as well as IPv4. |
28
- | `incoming_port` | int | `7373` | UDP port the relay binds to. |
29
- | `bootstrap` | list\[tuple\[str, int]] | `[]` | Initial peers used to join the network, e.g. `[ ("bootstrap.astreum.org", 7373) ]`. |
30
-
31
- > **Note**
32
- > The peer‑to‑peer *route* used for object discovery is always enabled.
33
- > If `validation_secret_key` is provided the node automatically joins the validation route too.
34
-
35
- ### Example
36
-
37
- ```python
38
- from astreum.node import Node
39
-
40
- config = {
41
- "machine-only": False, # run full node
42
- "relay_secret_key": "abcd", # optional – hex encoded
43
- "validation_secret_key": "12…34", # optional validator
44
- "storage_path": "./data/node1",
45
- "storage_get_relay_timeout": 5,
46
- "incoming_port": 7373,
47
- "use_ipv6": False,
48
- "bootstrap": [
49
- ("bootstrap.astreum.org", 7373),
50
- ("127.0.0.1", 7374)
51
- ]
52
- }
53
-
54
- node = Node(config)
55
- # … your code …
56
- ```
57
-
58
- ## Lispeum Machine Quickstart
59
-
60
- The Lispeum virtual machine (VM) is embedded inside `astreum.Node`. You feed it Lispeum source text, and the node tokenizes, parses, and **evaluates** the resulting AST inside an isolated environment.
61
-
62
- ```python
63
- # Define a named function int.add (stack body) and call it with bytes 1 and 2
64
-
65
- import uuid
66
- from astreum import Node, Env, Expr
67
-
68
- # 1) Spin‑up a stand‑alone VM
69
- node = Node()
70
-
71
- # 2) Create an environment (simple manual setup)
72
- env_id = uuid.uuid4()
73
- node.environments[env_id] = Env()
74
-
75
- # 3) Build a function value using a low‑level stack body via `sk`.
76
- # Body does: $0 $1 add (i.e., a + b)
77
- low_body = Expr.ListExpr([
78
- Expr.Symbol("$0"), # a (first arg)
79
- Expr.Symbol("$1"), # b (second arg)
80
- Expr.Symbol("add"),
81
- ])
82
-
83
- fn_body = Expr.ListExpr([
84
- Expr.Symbol("a"),
85
- Expr.Symbol("b"),
86
- Expr.ListExpr([low_body, Expr.Symbol("sk")]),
87
- ])
88
-
89
- params = Expr.ListExpr([Expr.Symbol("a"), Expr.Symbol("b")])
90
- int_add_fn = Expr.ListExpr([fn_body, params, Expr.Symbol("fn")])
91
-
1
+ # lib
2
+
3
+ Python library to interact with the Astreum blockchain and its virtual machine.
4
+
5
+ [View on PyPI](https://pypi.org/project/astreum/)
6
+
7
+ ## Configuration
8
+
9
+ When initializing an `astreum.Node`, pass a dictionary with any of the options below. Only the parameters you want to override need to be present – everything else falls back to its default.
10
+
11
+ ### Core Configuration
12
+
13
+ | Parameter | Type | Default | Description |
14
+ | --------------------------- | ---------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
15
+ | `hot_storage_limit` | int | `1073741824` | Maximum bytes kept in the hot cache before new atoms are skipped (1 GiB). |
16
+ | `cold_storage_limit` | int | `10737418240` | Cold storage write threshold (10 GiB by default); set to `0` to skip the limit. |
17
+ | `cold_storage_path` | string | `None` | Directory where persisted atoms live; Astreum creates it on startup and skips cold storage when unset. |
18
+ | `logging_retention` | int | `90` | Number of days to keep rotated log files (daily gzip). |
19
+ | `verbose` | bool | `False` | When **True**, also mirror JSON logs to stdout with a human-readable format. |
20
+
21
+ ### Networking
22
+
23
+ | Parameter | Type | Default | Description |
24
+ | ------------------------ | ----------- | --------------------- | ------------------------------------------------------------------------------------------------------- |
25
+ | `relay_secret_key` | hex string | Auto-generated | X25519 private key used for the relay route; a new keypair is created when this field is omitted. |
26
+ | `validation_secret_key` | hex string | `None` | Optional Ed25519 key that lets the node join the validation route; leave blank to opt out of validation. |
27
+ | `use_ipv6` | bool | `False` | Bind the incoming/outgoing sockets on IPv6 (the OS still listens on IPv4 if a peer speaks both). |
28
+ | `incoming_port` | int | `7373` | UDP port the relay binds to; pass `0` or omit to let the OS pick an ephemeral port. |
29
+ | `bootstrap` | list\[str\] | `[]` | Addresses to ping with a handshake before joining; each must look like `host:port` or `[ipv6]:port`. |
30
+
31
+ > **Note**
32
+ > The peer‑to‑peer *route* used for object discovery is always enabled.
33
+ > If `validation_secret_key` is provided the node automatically joins the validation route too.
34
+
35
+ ### Example
36
+
37
+ ```python
38
+ from astreum.node import Node
39
+
40
+ config = {
41
+ "relay_secret_key": "ab…cd", # optional hex encoded
42
+ "validation_secret_key": "1234", # optional – validator
43
+ "hot_storage_limit": 1073741824, # cap hot cache at 1 GiB
44
+ "cold_storage_limit": 10737418240, # cap cold storage at 10 GiB
45
+ "cold_storage_path": "./data/node1",
46
+ "incoming_port": 7373,
47
+ "use_ipv6": False,
48
+ "bootstrap": [
49
+ "bootstrap.astreum.org:7373",
50
+ "127.0.0.1:7374"
51
+ ]
52
+ }
53
+
54
+ node = Node(config)
55
+ # … your code …
56
+ ```
57
+
58
+
59
+ ## Astreum Machine Quickstart
60
+
61
+ The Astreum virtual machine (VM) is embedded inside `astreum.Node`. You feed it Astreum script, and the node tokenizes, parses, and evaluates.
62
+
63
+ ```python
64
+ # Define a named function int.add (stack body) and call it with bytes 1 and 2
65
+
66
+ import uuid
67
+ from astreum import Node, Env, Expr
68
+
69
+ # 1) Spin‑up a stand‑alone VM
70
+ node = Node()
71
+
72
+ # 2) Create an environment (simple manual setup)
73
+ env_id = uuid.uuid4()
74
+ node.environments[env_id] = Env()
75
+
76
+ # 3) Build a function value using a low‑level stack body via `sk`.
77
+ # Body does: $0 $1 add (i.e., a + b)
78
+ low_body = Expr.ListExpr([
79
+ Expr.Symbol("$0"), # a (first arg)
80
+ Expr.Symbol("$1"), # b (second arg)
81
+ Expr.Symbol("add"),
82
+ ])
83
+
84
+ fn_body = Expr.ListExpr([
85
+ Expr.Symbol("a"),
86
+ Expr.Symbol("b"),
87
+ Expr.ListExpr([low_body, Expr.Symbol("sk")]),
88
+ ])
89
+
90
+ params = Expr.ListExpr([Expr.Symbol("a"), Expr.Symbol("b")])
91
+ int_add_fn = Expr.ListExpr([fn_body, params, Expr.Symbol("fn")])
92
+
92
93
  # 4) Store under the name "int.add"
93
- node.env_set(env_id, b"int.add", int_add_fn)
94
+ node.env_set(env_id, "int.add", int_add_fn)
94
95
 
95
96
  # 5) Retrieve the function and call it with bytes 1 and 2
96
- bound = node.env_get(env_id, b"int.add")
97
- call = Expr.ListExpr([Expr.Byte(1), Expr.Byte(2), bound])
98
- res = node.high_eval(env_id, call)
99
-
100
- # sk returns a list of bytes; for 1+2 expect a single byte with value 3
101
- print([b.value for b in res.elements]) # [3]
102
- ```
103
-
104
- ### Handling errors
105
-
106
- Both helpers raise `ParseError` (from `astreum.machine.error`) when something goes wrong:
107
-
108
- * Unterminated string literals are caught by `tokenize`.
109
- * Unexpected or missing parentheses are caught by `parse`.
110
-
111
- Catch the exception to provide developer‑friendly diagnostics:
112
-
113
- ```python
114
- try:
115
- tokens = tokenize(bad_source)
116
- expr, _ = parse(tokens)
117
- except ParseError as e:
118
- print("Parse failed:", e)
119
- ```
120
-
121
- ---
122
-
123
-
124
- ## Logging
125
-
126
- Every `Node` instance wires up structured logging automatically:
127
-
128
- - Logs land in per-instance files named `node.log` under `%LOCALAPPDATA%\Astreum\lib-py\logs/<instance_id>` on Windows and `$XDG_STATE_HOME` (or `~/.local/state`)/`Astreum/lib-py/logs/<instance_id>` on other platforms. The `<instance_id>` is the first 16 hex characters of a BLAKE3 hash of the caller's file path, so running the node from different entry points keeps their logs isolated.
129
- - Files rotate at midnight UTC with gzip compression (`node-YYYY-MM-DD.log.gz`) and retain 90 days by default. Override via `config["logging_retention"]`.
130
- - Each event is a single JSON line containing timestamp, level, logger, message, process/thread info, module/function, and the derived `instance_id`.
131
- - Set `config["verbose"] = True` to mirror logs to stdout in a human-friendly format like `[2025-04-13-42-59] [info] Starting Astreum Node`.
132
- - The very first entry emitted is the banner `Starting Astreum Node`, signalling that the logging pipeline is live before other subsystems spin up.
133
-
134
- ## Testing
135
-
136
- ```bash
137
- python3 -m venv venv
138
- source venv/bin/activate
139
- pip install -e .
140
- python3 -m unittest discover -s tests
141
- ```
97
+ bound = node.env_get(env_id, "int.add")
98
+ call = Expr.ListExpr([Expr.Byte(1), Expr.Byte(2), bound])
99
+ res = node.high_eval(env_id, call)
100
+
101
+ # sk returns a list of bytes; for 1+2 expect a single byte with value 3
102
+ print([b.value for b in res.elements]) # [3]
103
+ ```
104
+
105
+ ### Handling errors
106
+
107
+ Both helpers raise `ParseError` (from `astreum.machine.error`) when something goes wrong:
108
+
109
+ * Unterminated string literals are caught by `tokenize`.
110
+ * Unexpected or missing parentheses are caught by `parse`.
111
+
112
+ Catch the exception to provide developer‑friendly diagnostics:
113
+
114
+ ```python
115
+ try:
116
+ tokens = tokenize(bad_source)
117
+ expr, _ = parse(tokens)
118
+ except ParseError as e:
119
+ print("Parse failed:", e)
120
+ ```
121
+
122
+ ---
123
+
124
+
125
+ ## Logging
126
+
127
+ Every `Node` instance wires up structured logging automatically:
128
+
129
+ - Logs land in per-instance files named `node.log` under `%LOCALAPPDATA%\Astreum\lib-py\logs/<instance_id>` on Windows and `$XDG_STATE_HOME` (or `~/.local/state`)/`Astreum/lib-py/logs/<instance_id>` on other platforms. The `<instance_id>` is the first 16 hex characters of a BLAKE3 hash of the caller's file path, so running the node from different entry points keeps their logs isolated.
130
+ - Files rotate at midnight UTC with gzip compression (`node-YYYY-MM-DD.log.gz`) and retain 90 days by default. Override via `config["logging_retention"]`.
131
+ - Each event is a single JSON line containing timestamp, level, logger, message, process/thread info, module/function, and the derived `instance_id`.
132
+ - Set `config["verbose"] = True` to mirror logs to stdout in a human-friendly format like `[2025-04-13-42-59] [info] Starting Astreum Node`.
133
+ - The very first entry emitted is the banner `Starting Astreum Node`, signalling that the logging pipeline is live before other subsystems spin up.
134
+
135
+ ## Testing
136
+
137
+ ```bash
138
+ python3 -m venv venv
139
+ source venv/bin/activate
140
+ pip install -e .
141
+ python3 -m unittest discover -s tests
142
+ ```
@@ -1,10 +1,10 @@
1
1
  [project]
2
2
  name = "astreum"
3
- version = "0.2.53"
3
+ version = "0.3.5"
4
4
  authors = [
5
5
  { name="Roy R. O. Okello", email="roy@stelar.xyz" },
6
6
  ]
7
- description = "Python library to interact with the Astreum blockchain and its Lispeum virtual machine."
7
+ description = "Python library to interact with the Astreum blockchain and its virtual machine."
8
8
  readme = "README.md"
9
9
  requires-python = ">=3.8"
10
10
  classifiers = [
@@ -19,5 +19,5 @@ dependencies = [
19
19
  ]
20
20
 
21
21
  [project.urls]
22
- Homepage = "https://github.com/astreum/lib"
23
- Issues = "https://github.com/astreum/lib/issues"
22
+ Homepage = "https://github.com/astreum/lib-py"
23
+ Issues = "https://github.com/astreum/lib-py/issues"
@@ -0,0 +1,18 @@
1
+
2
+ from astreum.consensus import Account, Accounts, Block, Chain, Fork, Receipt, Transaction
3
+ from astreum.machine import Env, Expr
4
+ from astreum.node import Node
5
+
6
+
7
+ __all__: list[str] = [
8
+ "Node",
9
+ "Env",
10
+ "Expr",
11
+ "Block",
12
+ "Chain",
13
+ "Fork",
14
+ "Receipt",
15
+ "Transaction",
16
+ "Account",
17
+ "Accounts",
18
+ ]
@@ -1,6 +1,6 @@
1
- from .message import Message
2
- from .peer import Peer
3
- from .route import Route
1
+ from .models.message import Message
2
+ from .models.peer import Peer
3
+ from .models.route import Route
4
4
  from .setup import communication_setup
5
5
 
6
6
  __all__ = [
@@ -0,0 +1,81 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Sequence
4
+
5
+ from cryptography.hazmat.primitives import serialization
6
+ from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PublicKey
7
+
8
+ from ..models.peer import Peer
9
+ from ..models.message import Message
10
+
11
+ if TYPE_CHECKING:
12
+ from .... import Node
13
+
14
+
15
+ def handle_handshake(node: "Node", addr: Sequence[object], message: Message) -> bool:
16
+ """Handle incoming handshake messages.
17
+
18
+ Returns True if the outer loop should `continue`, False otherwise.
19
+ """
20
+ logger = node.logger
21
+
22
+ sender_public_key_bytes = message.sender_bytes
23
+ try:
24
+ sender_key = X25519PublicKey.from_public_bytes(sender_public_key_bytes)
25
+ except Exception as exc:
26
+ logger.warning("Error extracting sender key bytes: %s", exc)
27
+ return True
28
+
29
+ try:
30
+ host, port = addr[0], int(addr[1])
31
+ except Exception:
32
+ return True
33
+ address_key = (host, port)
34
+
35
+ old_key_bytes = node.addresses.get(address_key)
36
+ node.addresses[address_key] = sender_public_key_bytes
37
+
38
+ if old_key_bytes is None:
39
+ try:
40
+ peer = Peer(node.relay_secret_key, sender_key)
41
+ except Exception:
42
+ return True
43
+ peer.address = address_key
44
+
45
+ node.peers[sender_public_key_bytes] = peer
46
+ node.peer_route.add_peer(sender_public_key_bytes, peer)
47
+
48
+ logger.info(
49
+ "Handshake accepted from %s:%s; peer added",
50
+ address_key[0],
51
+ address_key[1],
52
+ )
53
+ response = Message(handshake=True, sender=node.relay_public_key)
54
+ node.outgoing_queue.put((response.to_bytes(), address_key))
55
+ return True
56
+
57
+ if old_key_bytes == sender_public_key_bytes:
58
+ peer = node.peers.get(sender_public_key_bytes)
59
+ if peer is not None:
60
+ peer.address = address_key
61
+ return False
62
+
63
+ node.peers.pop(old_key_bytes, None)
64
+ try:
65
+ node.peer_route.remove_peer(old_key_bytes)
66
+ except Exception:
67
+ pass
68
+ try:
69
+ peer = Peer(node.relay_secret_key, sender_key)
70
+ except Exception:
71
+ return True
72
+ peer.address = address_key
73
+
74
+ node.peers[sender_public_key_bytes] = peer
75
+ node.peer_route.add_peer(sender_public_key_bytes, peer)
76
+ logger.info(
77
+ "Peer at %s:%s replaced due to key change",
78
+ address_key[0],
79
+ address_key[1],
80
+ )
81
+ return False