astreum 0.1.19__tar.gz → 0.2.0__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.
Potentially problematic release.
This version of astreum might be problematic. Click here for more details.
- astreum-0.2.0/PKG-INFO +144 -0
- astreum-0.2.0/README.md +126 -0
- {astreum-0.1.19 → astreum-0.2.0}/pyproject.toml +1 -1
- astreum-0.2.0/src/astreum/__init__.py +1 -0
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum/_node}/__init__.py +1 -1
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum/_node}/relay/envelope.py +1 -1
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum/_node}/relay/message.py +1 -1
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum/_node}/storage/merkle.py +65 -65
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum/_node}/storage/patricia.py +3 -3
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum/_node}/storage/storage.py +2 -0
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum/_node}/utils.py +1 -1
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum/_node}/validation/account.py +3 -3
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum/_node}/validation/transaction.py +3 -3
- astreum-0.2.0/src/astreum/lispeum/__init__.py +2 -0
- {astreum-0.1.19 → astreum-0.2.0}/src/astreum/lispeum/parser.py +1 -1
- astreum-0.2.0/src/astreum/machine/environment.py +4 -0
- astreum-0.2.0/src/astreum/node.py +1021 -0
- astreum-0.2.0/src/astreum.egg-info/PKG-INFO +144 -0
- astreum-0.2.0/src/astreum.egg-info/SOURCES.txt +60 -0
- astreum-0.1.19/PKG-INFO +0 -90
- astreum-0.1.19/README.md +0 -72
- astreum-0.1.19/src/astreum/__init__.py +0 -2
- astreum-0.1.19/src/astreum/machine/environment.py +0 -29
- astreum-0.1.19/src/astreum/node/storage/__init__.py +0 -13
- astreum-0.1.19/src/astreum/utils/__init__.py +0 -0
- astreum-0.1.19/src/astreum.egg-info/PKG-INFO +0 -90
- astreum-0.1.19/src/astreum.egg-info/SOURCES.txt +0 -60
- {astreum-0.1.19 → astreum-0.2.0}/LICENSE +0 -0
- {astreum-0.1.19 → astreum-0.2.0}/setup.cfg +0 -0
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum/_node}/relay/__init__.py +0 -0
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum/_node}/relay/bucket.py +0 -0
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum/_node}/relay/peer.py +0 -0
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum/_node}/relay/route.py +0 -0
- {astreum-0.1.19/src/astreum/lispeum → astreum-0.2.0/src/astreum/_node/storage}/__init__.py +0 -0
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum/_node}/storage/utils.py +0 -0
- {astreum-0.1.19/src/astreum/lispeum/special → astreum-0.2.0/src/astreum/_node/validation}/__init__.py +0 -0
- {astreum-0.1.19/src/astreum/lispeum/special/list → astreum-0.2.0/src/astreum/_node/validation/_block}/__init__.py +0 -0
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum/_node}/validation/_block/create.py +0 -0
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum/_node}/validation/_block/model.py +0 -0
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum/_node}/validation/_block/validate.py +0 -0
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum/_node}/validation/block.py +0 -0
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum/_node}/validation/constants.py +0 -0
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum/_node}/validation/stake.py +0 -0
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum/_node}/validation/vdf.py +0 -0
- {astreum-0.1.19/src/astreum/lispeum/special/number → astreum-0.2.0/src/astreum/crypto}/__init__.py +0 -0
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum}/crypto/ed25519.py +0 -0
- {astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum}/crypto/x25519.py +0 -0
- /astreum-0.1.19/src/astreum/utils/bytes_format.py → /astreum-0.2.0/src/astreum/format.py +0 -0
- {astreum-0.1.19 → astreum-0.2.0}/src/astreum/lispeum/expression.py +0 -0
- {astreum-0.1.19/src/astreum/node/crypto → astreum-0.2.0/src/astreum/lispeum/special}/__init__.py +0 -0
- {astreum-0.1.19 → astreum-0.2.0}/src/astreum/lispeum/special/definition.py +0 -0
- {astreum-0.1.19/src/astreum/node/validation → astreum-0.2.0/src/astreum/lispeum/special/list}/__init__.py +0 -0
- {astreum-0.1.19 → astreum-0.2.0}/src/astreum/lispeum/special/list/all.py +0 -0
- {astreum-0.1.19 → astreum-0.2.0}/src/astreum/lispeum/special/list/any.py +0 -0
- {astreum-0.1.19 → astreum-0.2.0}/src/astreum/lispeum/special/list/fold.py +0 -0
- {astreum-0.1.19 → astreum-0.2.0}/src/astreum/lispeum/special/list/get.py +0 -0
- {astreum-0.1.19 → astreum-0.2.0}/src/astreum/lispeum/special/list/insert.py +0 -0
- {astreum-0.1.19 → astreum-0.2.0}/src/astreum/lispeum/special/list/map.py +0 -0
- {astreum-0.1.19 → astreum-0.2.0}/src/astreum/lispeum/special/list/position.py +0 -0
- {astreum-0.1.19 → astreum-0.2.0}/src/astreum/lispeum/special/list/remove.py +0 -0
- {astreum-0.1.19/src/astreum/node/validation/_block → astreum-0.2.0/src/astreum/lispeum/special/number}/__init__.py +0 -0
- {astreum-0.1.19 → astreum-0.2.0}/src/astreum/lispeum/special/number/addition.py +0 -0
- {astreum-0.1.19 → astreum-0.2.0}/src/astreum/lispeum/storage.py +0 -0
- {astreum-0.1.19 → astreum-0.2.0}/src/astreum/lispeum/tokenizer.py +0 -0
- {astreum-0.1.19 → astreum-0.2.0}/src/astreum/lispeum/utils.py +0 -0
- {astreum-0.1.19 → astreum-0.2.0}/src/astreum/machine/__init__.py +0 -0
- {astreum-0.1.19 → astreum-0.2.0}/src/astreum/machine/error.py +0 -0
- {astreum-0.1.19 → astreum-0.2.0}/src/astreum.egg-info/dependency_links.txt +0 -0
- {astreum-0.1.19 → astreum-0.2.0}/src/astreum.egg-info/requires.txt +0 -0
- {astreum-0.1.19 → astreum-0.2.0}/src/astreum.egg-info/top_level.txt +0 -0
astreum-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: astreum
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Python library to interact with the Astreum blockchain and its Lispeum virtual machine.
|
|
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
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: pycryptodomex==3.21.0
|
|
15
|
+
Requires-Dist: cryptography==44.0.2
|
|
16
|
+
Requires-Dist: blake3==1.0.4
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# Astreum Python Library
|
|
20
|
+
|
|
21
|
+
Python library to interact with the Astreum blockchain and its Lispeum virtual machine.
|
|
22
|
+
|
|
23
|
+
[View on PyPI](https://pypi.org/project/astreum/)
|
|
24
|
+
|
|
25
|
+
## Configuration
|
|
26
|
+
|
|
27
|
+
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.
|
|
28
|
+
|
|
29
|
+
### Core Configuration
|
|
30
|
+
|
|
31
|
+
| Parameter | Type | Default | Description |
|
|
32
|
+
| -------------- | ---- | ------- | ----------- |
|
|
33
|
+
| `machine-only` | bool | `True` | When |
|
|
34
|
+
|
|
35
|
+
| |
|
|
36
|
+
| - |
|
|
37
|
+
|
|
38
|
+
| **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. | | | |
|
|
39
|
+
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | -------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
40
|
+
| `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. |
|
|
41
|
+
| `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. |
|
|
42
|
+
| `storage_path` | string | `None` | Directory where objects are persisted. If *None* the node uses an in‑memory store. |
|
|
43
|
+
| `storage_get_relay_timeout` | float | `5` | Seconds to wait for an object requested from peers before timing‑out. |
|
|
44
|
+
|
|
45
|
+
### Networking
|
|
46
|
+
|
|
47
|
+
| Parameter | Type | Default | Description |
|
|
48
|
+
| --------------- | ----------------------- | ------- | ----------------------------------------------------------------------------------- |
|
|
49
|
+
| `use_ipv6` | bool | `False` | Listen on IPv6 as well as IPv4. |
|
|
50
|
+
| `incoming_port` | int | `7373` | UDP port the relay binds to. |
|
|
51
|
+
| `bootstrap` | list\[tuple\[str, int]] | `[]` | Initial peers used to join the network, e.g. `[ ("bootstrap.astreum.org", 7373) ]`. |
|
|
52
|
+
|
|
53
|
+
> **Note**
|
|
54
|
+
> The peer‑to‑peer *route* used for object discovery is always enabled.
|
|
55
|
+
> If `validation_secret_key` is provided the node automatically joins the validation route too.
|
|
56
|
+
|
|
57
|
+
### Example
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from astreum.node import Node
|
|
61
|
+
|
|
62
|
+
config = {
|
|
63
|
+
"machine-only": False, # run full node
|
|
64
|
+
"relay_secret_key": "ab…cd", # optional – hex encoded
|
|
65
|
+
"validation_secret_key": "12…34", # optional – validator
|
|
66
|
+
"storage_path": "./data/node1",
|
|
67
|
+
"storage_get_relay_timeout": 5,
|
|
68
|
+
"incoming_port": 7373,
|
|
69
|
+
"use_ipv6": False,
|
|
70
|
+
"bootstrap": [
|
|
71
|
+
("bootstrap.astreum.org", 7373),
|
|
72
|
+
("127.0.0.1", 7374)
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
node = Node(config)
|
|
77
|
+
# … your code …
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Lispeum Machine Quickstart
|
|
81
|
+
|
|
82
|
+
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 *session* (lexical environment).
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from astreum.node import Node
|
|
86
|
+
from astreum.machine.tokenizer import tokenize
|
|
87
|
+
from astreum.machine.parser import parse
|
|
88
|
+
|
|
89
|
+
# 1. Spin‑up a stand‑alone VM (machine‑only node).
|
|
90
|
+
node = Node({"machine-only": True})
|
|
91
|
+
|
|
92
|
+
# 2. Create a fresh session (environment).
|
|
93
|
+
session_id = node.machine_session_create()
|
|
94
|
+
|
|
95
|
+
# 3. Convert Lispeum source → Expr AST.
|
|
96
|
+
source = '(+ 1 (* 2 3))'
|
|
97
|
+
expr, _ = parse(tokenize(source))
|
|
98
|
+
|
|
99
|
+
# 4. Evaluate inside that session.
|
|
100
|
+
env = node.sessions[session_id] # fetch the Env
|
|
101
|
+
result = node.machine_expr_eval(env, expr) # -> Expr.Integer(7)
|
|
102
|
+
|
|
103
|
+
print(result.value) # 7
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Handling errors
|
|
107
|
+
|
|
108
|
+
Both helpers raise `ParseError` (from `astreum.machine.error`) when something goes wrong:
|
|
109
|
+
|
|
110
|
+
* Unterminated string literals are caught by `tokenize` fileciteturn1file1
|
|
111
|
+
* Unexpected or missing parentheses are caught by `parse` fileciteturn1file0
|
|
112
|
+
|
|
113
|
+
Catch the exception to provide developer‑friendly diagnostics:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
try:
|
|
117
|
+
tokens = tokenize(bad_source)
|
|
118
|
+
expr, _ = parse(tokens)
|
|
119
|
+
except ParseError as e:
|
|
120
|
+
print("Parse failed:", e)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Both helpers raise `ParseError` (from `astreum.machine.error`) when something goes wrong:
|
|
124
|
+
|
|
125
|
+
* Unterminated string literals are caught by `tokenize` fileciteturn1file1
|
|
126
|
+
* Unexpected or missing parentheses are caught by `parse` fileciteturn1file0
|
|
127
|
+
|
|
128
|
+
Catch the exception to provide developer‑friendly diagnostics:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
try:
|
|
132
|
+
tokens = tokenize(bad_source)
|
|
133
|
+
expr, _ = parse(tokens)
|
|
134
|
+
except ParseError as e:
|
|
135
|
+
print("Parse failed:", e)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Testing
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
python3 -m unittest discover -s tests
|
|
144
|
+
```
|
astreum-0.2.0/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Astreum Python Library
|
|
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 |
|
|
16
|
+
|
|
17
|
+
| |
|
|
18
|
+
| - |
|
|
19
|
+
|
|
20
|
+
| **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. | | | |
|
|
21
|
+
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | -------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
22
|
+
| `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. |
|
|
23
|
+
| `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. |
|
|
24
|
+
| `storage_path` | string | `None` | Directory where objects are persisted. If *None* the node uses an in‑memory store. |
|
|
25
|
+
| `storage_get_relay_timeout` | float | `5` | Seconds to wait for an object requested from peers before timing‑out. |
|
|
26
|
+
|
|
27
|
+
### Networking
|
|
28
|
+
|
|
29
|
+
| Parameter | Type | Default | Description |
|
|
30
|
+
| --------------- | ----------------------- | ------- | ----------------------------------------------------------------------------------- |
|
|
31
|
+
| `use_ipv6` | bool | `False` | Listen on IPv6 as well as IPv4. |
|
|
32
|
+
| `incoming_port` | int | `7373` | UDP port the relay binds to. |
|
|
33
|
+
| `bootstrap` | list\[tuple\[str, int]] | `[]` | Initial peers used to join the network, e.g. `[ ("bootstrap.astreum.org", 7373) ]`. |
|
|
34
|
+
|
|
35
|
+
> **Note**
|
|
36
|
+
> The peer‑to‑peer *route* used for object discovery is always enabled.
|
|
37
|
+
> If `validation_secret_key` is provided the node automatically joins the validation route too.
|
|
38
|
+
|
|
39
|
+
### Example
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from astreum.node import Node
|
|
43
|
+
|
|
44
|
+
config = {
|
|
45
|
+
"machine-only": False, # run full node
|
|
46
|
+
"relay_secret_key": "ab…cd", # optional – hex encoded
|
|
47
|
+
"validation_secret_key": "12…34", # optional – validator
|
|
48
|
+
"storage_path": "./data/node1",
|
|
49
|
+
"storage_get_relay_timeout": 5,
|
|
50
|
+
"incoming_port": 7373,
|
|
51
|
+
"use_ipv6": False,
|
|
52
|
+
"bootstrap": [
|
|
53
|
+
("bootstrap.astreum.org", 7373),
|
|
54
|
+
("127.0.0.1", 7374)
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
node = Node(config)
|
|
59
|
+
# … your code …
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Lispeum Machine Quickstart
|
|
63
|
+
|
|
64
|
+
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 *session* (lexical environment).
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from astreum.node import Node
|
|
68
|
+
from astreum.machine.tokenizer import tokenize
|
|
69
|
+
from astreum.machine.parser import parse
|
|
70
|
+
|
|
71
|
+
# 1. Spin‑up a stand‑alone VM (machine‑only node).
|
|
72
|
+
node = Node({"machine-only": True})
|
|
73
|
+
|
|
74
|
+
# 2. Create a fresh session (environment).
|
|
75
|
+
session_id = node.machine_session_create()
|
|
76
|
+
|
|
77
|
+
# 3. Convert Lispeum source → Expr AST.
|
|
78
|
+
source = '(+ 1 (* 2 3))'
|
|
79
|
+
expr, _ = parse(tokenize(source))
|
|
80
|
+
|
|
81
|
+
# 4. Evaluate inside that session.
|
|
82
|
+
env = node.sessions[session_id] # fetch the Env
|
|
83
|
+
result = node.machine_expr_eval(env, expr) # -> Expr.Integer(7)
|
|
84
|
+
|
|
85
|
+
print(result.value) # 7
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Handling errors
|
|
89
|
+
|
|
90
|
+
Both helpers raise `ParseError` (from `astreum.machine.error`) when something goes wrong:
|
|
91
|
+
|
|
92
|
+
* Unterminated string literals are caught by `tokenize` fileciteturn1file1
|
|
93
|
+
* Unexpected or missing parentheses are caught by `parse` fileciteturn1file0
|
|
94
|
+
|
|
95
|
+
Catch the exception to provide developer‑friendly diagnostics:
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
try:
|
|
99
|
+
tokens = tokenize(bad_source)
|
|
100
|
+
expr, _ = parse(tokens)
|
|
101
|
+
except ParseError as e:
|
|
102
|
+
print("Parse failed:", e)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Both helpers raise `ParseError` (from `astreum.machine.error`) when something goes wrong:
|
|
106
|
+
|
|
107
|
+
* Unterminated string literals are caught by `tokenize` fileciteturn1file1
|
|
108
|
+
* Unexpected or missing parentheses are caught by `parse` fileciteturn1file0
|
|
109
|
+
|
|
110
|
+
Catch the exception to provide developer‑friendly diagnostics:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
try:
|
|
114
|
+
tokens = tokenize(bad_source)
|
|
115
|
+
expr, _ = parse(tokens)
|
|
116
|
+
except ParseError as e:
|
|
117
|
+
print("Parse failed:", e)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Testing
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
python3 -m unittest discover -s tests
|
|
126
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from node import Node
|
|
@@ -8,7 +8,7 @@ from .relay import Relay, Topic
|
|
|
8
8
|
from ..machine import AstreumMachine
|
|
9
9
|
from .utils import hash_data
|
|
10
10
|
from .validation.block import Block
|
|
11
|
-
from .storage import Storage
|
|
11
|
+
from .storage.storage import Storage
|
|
12
12
|
|
|
13
13
|
class Node:
|
|
14
14
|
def __init__(self, config: dict):
|
|
@@ -34,7 +34,7 @@ import os
|
|
|
34
34
|
from dataclasses import dataclass
|
|
35
35
|
from typing import Optional, Tuple, List
|
|
36
36
|
from .message import Message, Topic
|
|
37
|
-
from astreum.
|
|
37
|
+
from astreum.format import encode, decode
|
|
38
38
|
from ..utils import hash_data
|
|
39
39
|
|
|
40
40
|
@dataclass
|
|
@@ -1,7 +1,71 @@
|
|
|
1
1
|
import blake3
|
|
2
2
|
from .storage import Storage
|
|
3
|
-
|
|
3
|
+
from astreum import format
|
|
4
4
|
|
|
5
|
+
class MerkleNode:
|
|
6
|
+
def __init__(self, leaf: bool, data: bytes):
|
|
7
|
+
"""
|
|
8
|
+
Initialize a Merkle node.
|
|
9
|
+
|
|
10
|
+
For a leaf node, `data` is the actual content to be stored.
|
|
11
|
+
For an internal node, `data` should be the concatenation of the two child hashes.
|
|
12
|
+
|
|
13
|
+
:param leaf: A boolean flag indicating whether this node is a leaf node (True) or an internal node (False).
|
|
14
|
+
:param data: The node's data. For leaves, the stored data; for internal nodes, concatenated child hashes.
|
|
15
|
+
"""
|
|
16
|
+
self.leaf = leaf
|
|
17
|
+
self.data = data
|
|
18
|
+
self._hash = None # Cached hash value to avoid recomputation.
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def from_bytes(cls, data: bytes) -> 'MerkleNode':
|
|
22
|
+
"""
|
|
23
|
+
Deserialize a MerkleNode from its byte representation.
|
|
24
|
+
|
|
25
|
+
The input bytes are expected to be in the Astreum format, containing a leaf flag and node data.
|
|
26
|
+
|
|
27
|
+
:param data: The serialized node data.
|
|
28
|
+
:return: A new MerkleNode instance.
|
|
29
|
+
"""
|
|
30
|
+
leaf_flag, node_data = format.decode(data)
|
|
31
|
+
return cls(True if leaf_flag == 1 else False, node_data)
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def from_storage(cls, storage: Storage, hash_value: bytes) -> 'MerkleNode' or None:
|
|
35
|
+
"""
|
|
36
|
+
Retrieve and deserialize a MerkleNode from storage using its hash.
|
|
37
|
+
|
|
38
|
+
:param storage: The Storage instance used to retrieve the node.
|
|
39
|
+
:param hash_value: The hash key under which the node is stored.
|
|
40
|
+
:return: A MerkleNode instance if found, otherwise None.
|
|
41
|
+
"""
|
|
42
|
+
node_bytes = storage.get(hash_value)
|
|
43
|
+
if node_bytes is None:
|
|
44
|
+
return None
|
|
45
|
+
return cls.from_bytes(node_bytes)
|
|
46
|
+
|
|
47
|
+
def to_bytes(self) -> bytes:
|
|
48
|
+
"""
|
|
49
|
+
Serialize the MerkleNode into bytes using the Astreum format.
|
|
50
|
+
|
|
51
|
+
The format encodes a list containing the leaf flag and the node data.
|
|
52
|
+
|
|
53
|
+
:return: The serialized bytes representing the node.
|
|
54
|
+
"""
|
|
55
|
+
return format.encode([1 if self.leaf else 0, self.data])
|
|
56
|
+
|
|
57
|
+
def hash(self) -> bytes:
|
|
58
|
+
"""
|
|
59
|
+
Compute (or retrieve a cached) hash of the node using the Blake3 algorithm.
|
|
60
|
+
|
|
61
|
+
For leaf nodes, the hash is computed over the actual data.
|
|
62
|
+
For internal nodes, the hash is computed over the concatenated child hashes.
|
|
63
|
+
|
|
64
|
+
:return: The Blake3 digest of the node's data.
|
|
65
|
+
"""
|
|
66
|
+
if self._hash is None:
|
|
67
|
+
self._hash = blake3.blake3(self.data).digest()
|
|
68
|
+
return self._hash
|
|
5
69
|
|
|
6
70
|
|
|
7
71
|
class MerkleTree:
|
|
@@ -158,67 +222,3 @@ class MerkleTree:
|
|
|
158
222
|
return new_node_hash
|
|
159
223
|
|
|
160
224
|
|
|
161
|
-
class MerkleNode:
|
|
162
|
-
def __init__(self, leaf: bool, data: bytes):
|
|
163
|
-
"""
|
|
164
|
-
Initialize a Merkle node.
|
|
165
|
-
|
|
166
|
-
For a leaf node, `data` is the actual content to be stored.
|
|
167
|
-
For an internal node, `data` should be the concatenation of the two child hashes.
|
|
168
|
-
|
|
169
|
-
:param leaf: A boolean flag indicating whether this node is a leaf node (True) or an internal node (False).
|
|
170
|
-
:param data: The node's data. For leaves, the stored data; for internal nodes, concatenated child hashes.
|
|
171
|
-
"""
|
|
172
|
-
self.leaf = leaf
|
|
173
|
-
self.data = data
|
|
174
|
-
self._hash = None # Cached hash value to avoid recomputation.
|
|
175
|
-
|
|
176
|
-
@classmethod
|
|
177
|
-
def from_bytes(cls, data: bytes) -> 'MerkleNode':
|
|
178
|
-
"""
|
|
179
|
-
Deserialize a MerkleNode from its byte representation.
|
|
180
|
-
|
|
181
|
-
The input bytes are expected to be in the Astreum format, containing a leaf flag and node data.
|
|
182
|
-
|
|
183
|
-
:param data: The serialized node data.
|
|
184
|
-
:return: A new MerkleNode instance.
|
|
185
|
-
"""
|
|
186
|
-
leaf_flag, node_data = bytes_format.decode(data)
|
|
187
|
-
return cls(True if leaf_flag == 1 else False, node_data)
|
|
188
|
-
|
|
189
|
-
@classmethod
|
|
190
|
-
def from_storage(cls, storage: Storage, hash_value: bytes) -> 'MerkleNode' or None:
|
|
191
|
-
"""
|
|
192
|
-
Retrieve and deserialize a MerkleNode from storage using its hash.
|
|
193
|
-
|
|
194
|
-
:param storage: The Storage instance used to retrieve the node.
|
|
195
|
-
:param hash_value: The hash key under which the node is stored.
|
|
196
|
-
:return: A MerkleNode instance if found, otherwise None.
|
|
197
|
-
"""
|
|
198
|
-
node_bytes = storage.get(hash_value)
|
|
199
|
-
if node_bytes is None:
|
|
200
|
-
return None
|
|
201
|
-
return cls.from_bytes(node_bytes)
|
|
202
|
-
|
|
203
|
-
def to_bytes(self) -> bytes:
|
|
204
|
-
"""
|
|
205
|
-
Serialize the MerkleNode into bytes using the Astreum format.
|
|
206
|
-
|
|
207
|
-
The format encodes a list containing the leaf flag and the node data.
|
|
208
|
-
|
|
209
|
-
:return: The serialized bytes representing the node.
|
|
210
|
-
"""
|
|
211
|
-
return bytes_format.encode([1 if self.leaf else 0, self.data])
|
|
212
|
-
|
|
213
|
-
def hash(self) -> bytes:
|
|
214
|
-
"""
|
|
215
|
-
Compute (or retrieve a cached) hash of the node using the Blake3 algorithm.
|
|
216
|
-
|
|
217
|
-
For leaf nodes, the hash is computed over the actual data.
|
|
218
|
-
For internal nodes, the hash is computed over the concatenated child hashes.
|
|
219
|
-
|
|
220
|
-
:return: The Blake3 digest of the node's data.
|
|
221
|
-
"""
|
|
222
|
-
if self._hash is None:
|
|
223
|
-
self._hash = blake3.blake3(self.data).digest()
|
|
224
|
-
return self._hash
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import blake3
|
|
2
2
|
from typing import Optional, List
|
|
3
3
|
from .storage import Storage
|
|
4
|
-
import astreum.
|
|
4
|
+
import astreum.format as format format.decode, format.encode
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
def common_prefix_length(a: bytes, b: bytes) -> int:
|
|
@@ -35,7 +35,7 @@ class PatriciaNode:
|
|
|
35
35
|
Expected format: [key, value, children]
|
|
36
36
|
where children is a list of child node hashes (bytes).
|
|
37
37
|
"""
|
|
38
|
-
decoded =
|
|
38
|
+
decoded = format.decode(data)
|
|
39
39
|
key, value, children = decoded
|
|
40
40
|
return cls(key, value, children)
|
|
41
41
|
|
|
@@ -59,7 +59,7 @@ class PatriciaNode:
|
|
|
59
59
|
|
|
60
60
|
Structure: [key, value, children]
|
|
61
61
|
"""
|
|
62
|
-
return
|
|
62
|
+
return format.encode([self.key, self.value, self.children])
|
|
63
63
|
|
|
64
64
|
def hash(self) -> bytes:
|
|
65
65
|
"""
|
|
@@ -39,6 +39,8 @@ class Storage:
|
|
|
39
39
|
self.pending_requests = {} # hash -> (start_time, event)
|
|
40
40
|
self.request_lock = threading.Lock()
|
|
41
41
|
|
|
42
|
+
|
|
43
|
+
|
|
42
44
|
def put(self, data_hash: bytes, data: bytes) -> bool:
|
|
43
45
|
"""Store data with its hash. Returns True if successful, False if space limit exceeded."""
|
|
44
46
|
data_size = len(data)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import Optional
|
|
2
2
|
from ..storage.patricia import PatriciaTrie
|
|
3
3
|
|
|
4
|
-
import astreum.
|
|
4
|
+
import astreum.format as format
|
|
5
5
|
class Account:
|
|
6
6
|
def __init__(self, public_key: bytes, balance: int, code: bytes, counter: int, data: bytes, secret_key: Optional[bytes] = None):
|
|
7
7
|
"""
|
|
@@ -30,7 +30,7 @@ class Account:
|
|
|
30
30
|
|
|
31
31
|
The public_key (and optional secret_key) must be provided separately.
|
|
32
32
|
"""
|
|
33
|
-
decoded =
|
|
33
|
+
decoded = format.decode(data)
|
|
34
34
|
balance, code, counter, account_data = decoded
|
|
35
35
|
return cls(public_key, balance, code, counter, account_data, secret_key=secret_key)
|
|
36
36
|
|
|
@@ -40,7 +40,7 @@ class Account:
|
|
|
40
40
|
|
|
41
41
|
Format: [balance, code, counter, data]
|
|
42
42
|
"""
|
|
43
|
-
return
|
|
43
|
+
return format.encode([
|
|
44
44
|
self.balance,
|
|
45
45
|
self.code,
|
|
46
46
|
self.counter,
|
{astreum-0.1.19/src/astreum/node → astreum-0.2.0/src/astreum/_node}/validation/transaction.py
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import Optional
|
|
2
2
|
import time
|
|
3
3
|
from .account import Account, get_account_from_storage
|
|
4
|
-
import astreum.
|
|
4
|
+
import astreum.format as format
|
|
5
5
|
|
|
6
6
|
class Transaction:
|
|
7
7
|
def __init__(
|
|
@@ -36,7 +36,7 @@ class Transaction:
|
|
|
36
36
|
Returns:
|
|
37
37
|
Transaction object
|
|
38
38
|
"""
|
|
39
|
-
decoded =
|
|
39
|
+
decoded = format.decode(data)
|
|
40
40
|
sender_public_key, recipient_public_key, amount, tx_data, counter, timestamp, signature = decoded
|
|
41
41
|
|
|
42
42
|
sender_account = None
|
|
@@ -63,7 +63,7 @@ class Transaction:
|
|
|
63
63
|
|
|
64
64
|
Format: [sender_hash, recipient_hash, amount, data, counter, timestamp, signature]
|
|
65
65
|
"""
|
|
66
|
-
return
|
|
66
|
+
return format.encode([
|
|
67
67
|
self.sender.public_key,
|
|
68
68
|
self.recipient.public_key,
|
|
69
69
|
self.amount,
|