astreum 0.2.41__py3-none-any.whl → 0.3.1__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.
- astreum/__init__.py +16 -7
- astreum/{_communication → communication}/__init__.py +3 -3
- astreum/communication/handlers/handshake.py +83 -0
- astreum/communication/handlers/ping.py +48 -0
- astreum/communication/handlers/storage_request.py +81 -0
- astreum/communication/models/__init__.py +0 -0
- astreum/{_communication → communication/models}/message.py +1 -0
- astreum/communication/models/peer.py +23 -0
- astreum/{_communication → communication/models}/route.py +45 -8
- astreum/{_communication → communication}/setup.py +46 -95
- astreum/communication/start.py +38 -0
- astreum/consensus/__init__.py +20 -0
- astreum/consensus/genesis.py +66 -0
- astreum/consensus/models/__init__.py +0 -0
- astreum/consensus/models/account.py +84 -0
- astreum/consensus/models/accounts.py +72 -0
- astreum/consensus/models/block.py +364 -0
- astreum/{_consensus → consensus/models}/chain.py +7 -7
- astreum/{_consensus → consensus/models}/fork.py +8 -8
- astreum/consensus/models/receipt.py +98 -0
- astreum/consensus/models/transaction.py +213 -0
- astreum/{_consensus → consensus}/setup.py +26 -11
- astreum/consensus/start.py +68 -0
- astreum/consensus/validator.py +95 -0
- astreum/{_consensus → consensus}/workers/discovery.py +20 -1
- astreum/consensus/workers/validation.py +291 -0
- astreum/{_consensus → consensus}/workers/verify.py +32 -3
- astreum/machine/__init__.py +20 -0
- astreum/machine/evaluations/__init__.py +0 -0
- astreum/machine/evaluations/high_evaluation.py +237 -0
- astreum/machine/evaluations/low_evaluation.py +281 -0
- astreum/machine/evaluations/script_evaluation.py +27 -0
- astreum/machine/models/__init__.py +0 -0
- astreum/machine/models/environment.py +31 -0
- astreum/machine/models/expression.py +218 -0
- astreum/{_lispeum → machine}/parser.py +26 -31
- astreum/machine/tokenizer.py +90 -0
- astreum/node.py +73 -781
- astreum/storage/__init__.py +7 -0
- astreum/storage/actions/get.py +69 -0
- astreum/storage/actions/set.py +132 -0
- astreum/storage/models/atom.py +107 -0
- astreum/{_storage/patricia.py → storage/models/trie.py} +236 -177
- astreum/storage/setup.py +44 -15
- astreum/utils/bytes.py +24 -0
- astreum/utils/integer.py +25 -0
- astreum/utils/logging.py +219 -0
- astreum-0.3.1.dist-info/METADATA +160 -0
- astreum-0.3.1.dist-info/RECORD +62 -0
- astreum/_communication/peer.py +0 -11
- astreum/_consensus/__init__.py +0 -20
- astreum/_consensus/account.py +0 -170
- astreum/_consensus/accounts.py +0 -67
- astreum/_consensus/block.py +0 -328
- astreum/_consensus/genesis.py +0 -141
- astreum/_consensus/receipt.py +0 -177
- astreum/_consensus/transaction.py +0 -192
- astreum/_consensus/workers/validation.py +0 -122
- astreum/_lispeum/__init__.py +0 -16
- astreum/_lispeum/environment.py +0 -13
- astreum/_lispeum/expression.py +0 -37
- astreum/_lispeum/high_evaluation.py +0 -177
- astreum/_lispeum/low_evaluation.py +0 -123
- astreum/_lispeum/tokenizer.py +0 -22
- astreum/_node.py +0 -58
- astreum/_storage/__init__.py +0 -5
- astreum/_storage/atom.py +0 -117
- astreum/format.py +0 -75
- astreum/models/block.py +0 -441
- astreum/models/merkle.py +0 -205
- astreum/models/patricia.py +0 -393
- astreum/storage/object.py +0 -68
- astreum-0.2.41.dist-info/METADATA +0 -146
- astreum-0.2.41.dist-info/RECORD +0 -53
- /astreum/{models → communication/handlers}/__init__.py +0 -0
- /astreum/{_communication → communication/models}/ping.py +0 -0
- /astreum/{_communication → communication}/util.py +0 -0
- /astreum/{_consensus → consensus}/workers/__init__.py +0 -0
- /astreum/{_lispeum → machine/models}/meter.py +0 -0
- {astreum-0.2.41.dist-info → astreum-0.3.1.dist-info}/WHEEL +0 -0
- {astreum-0.2.41.dist-info → astreum-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {astreum-0.2.41.dist-info → astreum-0.3.1.dist-info}/top_level.txt +0 -0
astreum/_consensus/receipt.py
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from dataclasses import dataclass, field
|
|
4
|
-
from typing import Callable, List, Optional, Tuple
|
|
5
|
-
|
|
6
|
-
from .._storage.atom import Atom, ZERO32
|
|
7
|
-
|
|
8
|
-
STATUS_SUCCESS = 0
|
|
9
|
-
STATUS_FAILED = 1
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def _int_to_be_bytes(value: Optional[int]) -> bytes:
|
|
13
|
-
if value is None:
|
|
14
|
-
return b""
|
|
15
|
-
value = int(value)
|
|
16
|
-
if value == 0:
|
|
17
|
-
return b"\x00"
|
|
18
|
-
size = (value.bit_length() + 7) // 8
|
|
19
|
-
return value.to_bytes(size, "big")
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def _be_bytes_to_int(data: Optional[bytes]) -> int:
|
|
23
|
-
if not data:
|
|
24
|
-
return 0
|
|
25
|
-
return int.from_bytes(data, "big")
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def _make_list(child_ids: List[bytes]) -> Tuple[bytes, List[Atom]]:
|
|
29
|
-
atoms: List[Atom] = []
|
|
30
|
-
next_hash = ZERO32
|
|
31
|
-
elements: List[Atom] = []
|
|
32
|
-
for child_id in reversed(child_ids):
|
|
33
|
-
elem = Atom.from_data(data=child_id, next_hash=next_hash)
|
|
34
|
-
next_hash = elem.object_id()
|
|
35
|
-
elements.append(elem)
|
|
36
|
-
elements.reverse()
|
|
37
|
-
list_value = Atom.from_data(data=len(child_ids).to_bytes(8, "little"), next_hash=next_hash)
|
|
38
|
-
list_type = Atom.from_data(data=b"list", next_hash=list_value.object_id())
|
|
39
|
-
atoms.extend(elements)
|
|
40
|
-
atoms.append(list_value)
|
|
41
|
-
atoms.append(list_type)
|
|
42
|
-
return list_type.object_id(), atoms
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def _read_list_entries(
|
|
46
|
-
storage_get: Callable[[bytes], Optional[Atom]], start: bytes
|
|
47
|
-
) -> List[bytes]:
|
|
48
|
-
entries: List[bytes] = []
|
|
49
|
-
current = start if start and start != ZERO32 else b""
|
|
50
|
-
while current:
|
|
51
|
-
elem = storage_get(current)
|
|
52
|
-
if elem is None:
|
|
53
|
-
break
|
|
54
|
-
entries.append(elem.data)
|
|
55
|
-
nxt = elem.next
|
|
56
|
-
current = nxt if nxt and nxt != ZERO32 else b""
|
|
57
|
-
return entries
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def _read_payload_bytes(
|
|
61
|
-
storage_get: Callable[[bytes], Optional[Atom]], object_id: bytes
|
|
62
|
-
) -> bytes:
|
|
63
|
-
if not object_id or object_id == ZERO32:
|
|
64
|
-
return b""
|
|
65
|
-
atom = storage_get(object_id)
|
|
66
|
-
if atom is None:
|
|
67
|
-
return b""
|
|
68
|
-
if atom.data == b"bytes":
|
|
69
|
-
value_atom = storage_get(atom.next)
|
|
70
|
-
return value_atom.data if value_atom is not None else b""
|
|
71
|
-
return atom.data
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
@dataclass
|
|
75
|
-
class Receipt:
|
|
76
|
-
transaction_hash: bytes = ZERO32
|
|
77
|
-
cost: int = 0
|
|
78
|
-
logs: bytes = b""
|
|
79
|
-
status: int = 0
|
|
80
|
-
hash: bytes = ZERO32
|
|
81
|
-
atoms: List[Atom] = field(default_factory=list)
|
|
82
|
-
|
|
83
|
-
def to_atom(self) -> Tuple[bytes, List[Atom]]:
|
|
84
|
-
"""Serialise the receipt into Atom storage."""
|
|
85
|
-
if self.status not in (STATUS_SUCCESS, STATUS_FAILED):
|
|
86
|
-
raise ValueError("unsupported receipt status")
|
|
87
|
-
|
|
88
|
-
atoms: List[Atom] = []
|
|
89
|
-
|
|
90
|
-
tx_atom = Atom.from_data(data=bytes(self.transaction_hash))
|
|
91
|
-
status_atom = Atom.from_data(data=_int_to_be_bytes(self.status))
|
|
92
|
-
cost_atom = Atom.from_data(data=_int_to_be_bytes(self.cost))
|
|
93
|
-
logs_atom = Atom.from_data(data=bytes(self.logs))
|
|
94
|
-
|
|
95
|
-
atoms.extend([tx_atom, status_atom, cost_atom, logs_atom])
|
|
96
|
-
|
|
97
|
-
body_child_ids = [
|
|
98
|
-
tx_atom.object_id(),
|
|
99
|
-
status_atom.object_id(),
|
|
100
|
-
cost_atom.object_id(),
|
|
101
|
-
logs_atom.object_id(),
|
|
102
|
-
]
|
|
103
|
-
body_id, body_atoms = _make_list(body_child_ids)
|
|
104
|
-
atoms.extend(body_atoms)
|
|
105
|
-
|
|
106
|
-
type_atom = Atom.from_data(data=b"receipt", next_hash=body_id)
|
|
107
|
-
atoms.append(type_atom)
|
|
108
|
-
|
|
109
|
-
top_list_id, top_atoms = _make_list([type_atom.object_id(), body_id])
|
|
110
|
-
atoms.extend(top_atoms)
|
|
111
|
-
|
|
112
|
-
return top_list_id, atoms
|
|
113
|
-
|
|
114
|
-
def atomize(self) -> Tuple[bytes, List[Atom]]:
|
|
115
|
-
"""Generate atoms for this receipt and cache them."""
|
|
116
|
-
receipt_id, atoms = self.to_atom()
|
|
117
|
-
self.hash = receipt_id
|
|
118
|
-
self.atoms = atoms
|
|
119
|
-
return receipt_id, atoms
|
|
120
|
-
|
|
121
|
-
@classmethod
|
|
122
|
-
def from_atom(
|
|
123
|
-
cls,
|
|
124
|
-
storage_get: Callable[[bytes], Optional[Atom]],
|
|
125
|
-
receipt_id: bytes,
|
|
126
|
-
) -> Receipt:
|
|
127
|
-
"""Materialise a Receipt from Atom storage."""
|
|
128
|
-
top_type_atom = storage_get(receipt_id)
|
|
129
|
-
if top_type_atom is None or top_type_atom.data != b"list":
|
|
130
|
-
raise ValueError("not a receipt (outer list missing)")
|
|
131
|
-
|
|
132
|
-
top_value_atom = storage_get(top_type_atom.next)
|
|
133
|
-
if top_value_atom is None:
|
|
134
|
-
raise ValueError("malformed receipt (outer value missing)")
|
|
135
|
-
|
|
136
|
-
head = top_value_atom.next
|
|
137
|
-
first_elem = storage_get(head) if head else None
|
|
138
|
-
if first_elem is None:
|
|
139
|
-
raise ValueError("malformed receipt (type element missing)")
|
|
140
|
-
|
|
141
|
-
type_atom_id = first_elem.data
|
|
142
|
-
type_atom = storage_get(type_atom_id)
|
|
143
|
-
if type_atom is None or type_atom.data != b"receipt":
|
|
144
|
-
raise ValueError("not a receipt (type mismatch)")
|
|
145
|
-
|
|
146
|
-
remainder_entries = _read_list_entries(storage_get, first_elem.next)
|
|
147
|
-
if not remainder_entries:
|
|
148
|
-
raise ValueError("malformed receipt (body missing)")
|
|
149
|
-
body_id = remainder_entries[0]
|
|
150
|
-
|
|
151
|
-
body_type_atom = storage_get(body_id)
|
|
152
|
-
if body_type_atom is None or body_type_atom.data != b"list":
|
|
153
|
-
raise ValueError("malformed receipt body (type)")
|
|
154
|
-
|
|
155
|
-
body_value_atom = storage_get(body_type_atom.next)
|
|
156
|
-
if body_value_atom is None:
|
|
157
|
-
raise ValueError("malformed receipt body (value)")
|
|
158
|
-
|
|
159
|
-
body_entries = _read_list_entries(storage_get, body_value_atom.next)
|
|
160
|
-
if len(body_entries) < 4:
|
|
161
|
-
body_entries.extend([ZERO32] * (4 - len(body_entries)))
|
|
162
|
-
|
|
163
|
-
transaction_hash_bytes = _read_payload_bytes(storage_get, body_entries[0])
|
|
164
|
-
status_bytes = _read_payload_bytes(storage_get, body_entries[1])
|
|
165
|
-
cost_bytes = _read_payload_bytes(storage_get, body_entries[2])
|
|
166
|
-
logs_bytes = _read_payload_bytes(storage_get, body_entries[3])
|
|
167
|
-
status_value = _be_bytes_to_int(status_bytes)
|
|
168
|
-
if status_value not in (STATUS_SUCCESS, STATUS_FAILED):
|
|
169
|
-
raise ValueError("unsupported receipt status")
|
|
170
|
-
|
|
171
|
-
return cls(
|
|
172
|
-
transaction_hash=transaction_hash_bytes or ZERO32,
|
|
173
|
-
cost=_be_bytes_to_int(cost_bytes),
|
|
174
|
-
logs=logs_bytes,
|
|
175
|
-
status=status_value,
|
|
176
|
-
hash=bytes(receipt_id),
|
|
177
|
-
)
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
from typing import Any, List, Optional, Tuple
|
|
5
|
-
|
|
6
|
-
from .._storage.atom import Atom, ZERO32
|
|
7
|
-
from .receipt import Receipt, STATUS_SUCCESS
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def _int_to_be_bytes(value: Optional[int]) -> bytes:
|
|
11
|
-
if value is None:
|
|
12
|
-
return b""
|
|
13
|
-
value = int(value)
|
|
14
|
-
if value == 0:
|
|
15
|
-
return b"\x00"
|
|
16
|
-
size = (value.bit_length() + 7) // 8
|
|
17
|
-
return value.to_bytes(size, "big")
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def _be_bytes_to_int(data: Optional[bytes]) -> int:
|
|
21
|
-
if not data:
|
|
22
|
-
return 0
|
|
23
|
-
return int.from_bytes(data, "big")
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def _make_list(child_ids: List[bytes]) -> Tuple[bytes, List[Atom]]:
|
|
27
|
-
atoms: List[Atom] = []
|
|
28
|
-
next_hash = ZERO32
|
|
29
|
-
chain: List[Atom] = []
|
|
30
|
-
for child_id in reversed(child_ids):
|
|
31
|
-
elem = Atom.from_data(data=child_id, next_hash=next_hash)
|
|
32
|
-
next_hash = elem.object_id()
|
|
33
|
-
chain.append(elem)
|
|
34
|
-
|
|
35
|
-
chain.reverse()
|
|
36
|
-
list_value = Atom.from_data(data=len(child_ids).to_bytes(8, "little"), next_hash=next_hash)
|
|
37
|
-
list_type = Atom.from_data(data=b"list", next_hash=list_value.object_id())
|
|
38
|
-
atoms.extend(chain)
|
|
39
|
-
atoms.append(list_value)
|
|
40
|
-
atoms.append(list_type)
|
|
41
|
-
return list_type.object_id(), atoms
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
@dataclass
|
|
45
|
-
class Transaction:
|
|
46
|
-
amount: int
|
|
47
|
-
counter: int
|
|
48
|
-
data: bytes = b""
|
|
49
|
-
recipient: bytes = b""
|
|
50
|
-
sender: bytes = b""
|
|
51
|
-
signature: bytes = b""
|
|
52
|
-
hash: bytes = ZERO32
|
|
53
|
-
|
|
54
|
-
def to_atom(self) -> Tuple[bytes, List[Atom]]:
|
|
55
|
-
"""Serialise the transaction, returning (object_id, atoms)."""
|
|
56
|
-
body_child_ids: List[bytes] = []
|
|
57
|
-
acc: List[Atom] = []
|
|
58
|
-
|
|
59
|
-
def emit(payload: bytes) -> None:
|
|
60
|
-
atom = Atom.from_data(data=payload)
|
|
61
|
-
body_child_ids.append(atom.object_id())
|
|
62
|
-
acc.append(atom)
|
|
63
|
-
|
|
64
|
-
emit(_int_to_be_bytes(self.amount))
|
|
65
|
-
emit(_int_to_be_bytes(self.counter))
|
|
66
|
-
emit(bytes(self.data))
|
|
67
|
-
emit(bytes(self.recipient))
|
|
68
|
-
emit(bytes(self.sender))
|
|
69
|
-
|
|
70
|
-
body_id, body_atoms = _make_list(body_child_ids)
|
|
71
|
-
acc.extend(body_atoms)
|
|
72
|
-
|
|
73
|
-
type_atom = Atom.from_data(data=b"transaction", next_hash=body_id)
|
|
74
|
-
signature_atom = Atom.from_data(data=bytes(self.signature))
|
|
75
|
-
|
|
76
|
-
top_list_id, top_atoms = _make_list(
|
|
77
|
-
[type_atom.object_id(), body_id, signature_atom.object_id()]
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
atoms: List[Atom] = acc
|
|
81
|
-
atoms.append(type_atom)
|
|
82
|
-
atoms.append(signature_atom)
|
|
83
|
-
atoms.extend(top_atoms)
|
|
84
|
-
return top_list_id, atoms
|
|
85
|
-
|
|
86
|
-
@classmethod
|
|
87
|
-
def from_atom(
|
|
88
|
-
cls,
|
|
89
|
-
node: Any,
|
|
90
|
-
transaction_id: bytes,
|
|
91
|
-
) -> Transaction:
|
|
92
|
-
storage_get = node._local_get
|
|
93
|
-
if not callable(storage_get):
|
|
94
|
-
raise NotImplementedError("node does not expose a storage getter")
|
|
95
|
-
|
|
96
|
-
top_type_atom = storage_get(transaction_id)
|
|
97
|
-
if top_type_atom is None or top_type_atom.data != b"list":
|
|
98
|
-
raise ValueError("not a transaction (outer list missing)")
|
|
99
|
-
|
|
100
|
-
top_value_atom = storage_get(top_type_atom.next)
|
|
101
|
-
if top_value_atom is None:
|
|
102
|
-
raise ValueError("malformed transaction (outer value missing)")
|
|
103
|
-
|
|
104
|
-
head = top_value_atom.next
|
|
105
|
-
first_elem = storage_get(head)
|
|
106
|
-
if first_elem is None:
|
|
107
|
-
raise ValueError("malformed transaction (type element missing)")
|
|
108
|
-
|
|
109
|
-
type_atom_id = first_elem.data
|
|
110
|
-
type_atom = storage_get(type_atom_id)
|
|
111
|
-
if type_atom is None or type_atom.data != b"transaction":
|
|
112
|
-
raise ValueError("not a transaction (type mismatch)")
|
|
113
|
-
|
|
114
|
-
def read_list_entries(start: bytes) -> List[bytes]:
|
|
115
|
-
entries: List[bytes] = []
|
|
116
|
-
current = start if start != ZERO32 else b""
|
|
117
|
-
while current:
|
|
118
|
-
elem = storage_get(current)
|
|
119
|
-
if elem is None:
|
|
120
|
-
break
|
|
121
|
-
entries.append(elem.data)
|
|
122
|
-
nxt = elem.next
|
|
123
|
-
current = nxt if nxt != ZERO32 else b""
|
|
124
|
-
return entries
|
|
125
|
-
|
|
126
|
-
remainder_entries = read_list_entries(first_elem.next)
|
|
127
|
-
if len(remainder_entries) < 2:
|
|
128
|
-
raise ValueError("malformed transaction (body/signature missing)")
|
|
129
|
-
|
|
130
|
-
body_id, signature_atom_id = remainder_entries[0], remainder_entries[1]
|
|
131
|
-
|
|
132
|
-
body_type_atom = storage_get(body_id)
|
|
133
|
-
if body_type_atom is None or body_type_atom.data != b"list":
|
|
134
|
-
raise ValueError("malformed transaction body (type)")
|
|
135
|
-
|
|
136
|
-
body_value_atom = storage_get(body_type_atom.next)
|
|
137
|
-
if body_value_atom is None:
|
|
138
|
-
raise ValueError("malformed transaction body (value)")
|
|
139
|
-
|
|
140
|
-
body_entries = read_list_entries(body_value_atom.next)
|
|
141
|
-
if len(body_entries) < 5:
|
|
142
|
-
body_entries.extend([ZERO32] * (5 - len(body_entries)))
|
|
143
|
-
|
|
144
|
-
def read_detail_bytes(entry_id: bytes) -> bytes:
|
|
145
|
-
if entry_id == ZERO32:
|
|
146
|
-
return b""
|
|
147
|
-
elem = storage_get(entry_id)
|
|
148
|
-
if elem is None:
|
|
149
|
-
return b""
|
|
150
|
-
detail_atom = storage_get(elem.data)
|
|
151
|
-
return detail_atom.data if detail_atom is not None else b""
|
|
152
|
-
|
|
153
|
-
amount_bytes = read_detail_bytes(body_entries[0])
|
|
154
|
-
counter_bytes = read_detail_bytes(body_entries[1])
|
|
155
|
-
data_bytes = read_detail_bytes(body_entries[2])
|
|
156
|
-
recipient_bytes = read_detail_bytes(body_entries[3])
|
|
157
|
-
sender_bytes = read_detail_bytes(body_entries[4])
|
|
158
|
-
|
|
159
|
-
signature_atom = storage_get(signature_atom_id)
|
|
160
|
-
signature_bytes = signature_atom.data if signature_atom is not None else b""
|
|
161
|
-
|
|
162
|
-
return cls(
|
|
163
|
-
amount=_be_bytes_to_int(amount_bytes),
|
|
164
|
-
counter=_be_bytes_to_int(counter_bytes),
|
|
165
|
-
data=data_bytes,
|
|
166
|
-
recipient=recipient_bytes,
|
|
167
|
-
sender=sender_bytes,
|
|
168
|
-
signature=signature_bytes,
|
|
169
|
-
hash=bytes(transaction_id),
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
def apply_transaction(node: Any, block: object, transaction_hash: bytes) -> None:
|
|
174
|
-
"""Apply transaction to the candidate block. Override downstream."""
|
|
175
|
-
transaction = Transaction.from_atom(node, transaction_hash)
|
|
176
|
-
|
|
177
|
-
if block.transactions is None:
|
|
178
|
-
block.transactions = []
|
|
179
|
-
block.transactions.append(transaction)
|
|
180
|
-
|
|
181
|
-
receipt = Receipt(
|
|
182
|
-
transaction_hash=bytes(transaction_hash),
|
|
183
|
-
cost=0,
|
|
184
|
-
logs=b"",
|
|
185
|
-
status=STATUS_SUCCESS,
|
|
186
|
-
)
|
|
187
|
-
receipt.atomize()
|
|
188
|
-
if block.receipts is None:
|
|
189
|
-
block.receipts = []
|
|
190
|
-
block.receipts.append(receipt)
|
|
191
|
-
|
|
192
|
-
# Downstream implementations can extend this to apply state changes.
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import time
|
|
4
|
-
from queue import Empty
|
|
5
|
-
from typing import Any, Callable
|
|
6
|
-
|
|
7
|
-
from ..block import Block
|
|
8
|
-
from ..transaction import apply_transaction
|
|
9
|
-
from ..._storage.atom import bytes_list_to_atoms
|
|
10
|
-
from ..._storage.patricia import PatriciaTrie
|
|
11
|
-
from ..._communication.message import Message, MessageTopic
|
|
12
|
-
from ..._communication.ping import Ping
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def make_validation_worker(
|
|
16
|
-
node: Any,
|
|
17
|
-
*,
|
|
18
|
-
current_validator: Callable[[Any], bytes],
|
|
19
|
-
) -> Callable[[], None]:
|
|
20
|
-
"""Build the validation worker bound to the given node."""
|
|
21
|
-
|
|
22
|
-
def _validation_worker() -> None:
|
|
23
|
-
stop = node._validation_stop_event
|
|
24
|
-
while not stop.is_set():
|
|
25
|
-
validation_public_key = getattr(node, "validation_public_key", None)
|
|
26
|
-
if not validation_public_key:
|
|
27
|
-
time.sleep(0.5)
|
|
28
|
-
continue
|
|
29
|
-
|
|
30
|
-
scheduled_validator = current_validator(node)
|
|
31
|
-
|
|
32
|
-
if scheduled_validator != validation_public_key:
|
|
33
|
-
time.sleep(0.5)
|
|
34
|
-
continue
|
|
35
|
-
|
|
36
|
-
try:
|
|
37
|
-
current_hash = node._validation_transaction_queue.get_nowait()
|
|
38
|
-
except Empty:
|
|
39
|
-
time.sleep(0.1)
|
|
40
|
-
continue
|
|
41
|
-
|
|
42
|
-
# create thread to perform vdf
|
|
43
|
-
|
|
44
|
-
new_block = Block()
|
|
45
|
-
new_block.validator_public_key = validation_public_key
|
|
46
|
-
new_block.previous_block_hash = node.latest_block_hash
|
|
47
|
-
try:
|
|
48
|
-
new_block.previous_block = Block.from_atom(node, new_block.previous_block_hash)
|
|
49
|
-
except Exception:
|
|
50
|
-
continue
|
|
51
|
-
new_block.accounts = PatriciaTrie(root_hash=new_block.previous_block.accounts_hash)
|
|
52
|
-
|
|
53
|
-
# we may want to add a timer to process part of the txs only on a slow computer
|
|
54
|
-
while True:
|
|
55
|
-
try:
|
|
56
|
-
apply_transaction(node, new_block, current_hash)
|
|
57
|
-
except NotImplementedError:
|
|
58
|
-
node._validation_transaction_queue.put(current_hash)
|
|
59
|
-
time.sleep(0.5)
|
|
60
|
-
break
|
|
61
|
-
except Exception:
|
|
62
|
-
pass
|
|
63
|
-
|
|
64
|
-
try:
|
|
65
|
-
current_hash = node._validation_transaction_queue.get_nowait()
|
|
66
|
-
except Empty:
|
|
67
|
-
break
|
|
68
|
-
|
|
69
|
-
# create an atom list of transactions, save the list head hash as the block's transactions_hash
|
|
70
|
-
transactions = new_block.transactions or []
|
|
71
|
-
tx_hashes = [bytes(tx.hash) for tx in transactions if tx.hash]
|
|
72
|
-
head_hash, _ = bytes_list_to_atoms(tx_hashes)
|
|
73
|
-
new_block.transactions_hash = head_hash
|
|
74
|
-
|
|
75
|
-
receipts = new_block.receipts or []
|
|
76
|
-
receipt_hashes = [bytes(rcpt.hash) for rcpt in receipts if rcpt.hash]
|
|
77
|
-
receipts_head, _ = bytes_list_to_atoms(receipt_hashes)
|
|
78
|
-
new_block.receipts_hash = receipts_head
|
|
79
|
-
|
|
80
|
-
# get vdf result, default to 0 for now
|
|
81
|
-
|
|
82
|
-
# get timestamp or wait for a the next second from the previous block, rule is the next block must be atleast 1 second after the previous
|
|
83
|
-
now = time.time()
|
|
84
|
-
min_allowed = new_block.previous_block.timestamp + 1
|
|
85
|
-
if now < min_allowed:
|
|
86
|
-
time.sleep(max(0.0, min_allowed - now))
|
|
87
|
-
now = time.time()
|
|
88
|
-
new_block.timestamp = max(int(now), min_allowed)
|
|
89
|
-
|
|
90
|
-
# atomize block
|
|
91
|
-
new_block_hash, _ = new_block.to_atom()
|
|
92
|
-
# put as own latest block hash
|
|
93
|
-
node.latest_block_hash = new_block_hash
|
|
94
|
-
|
|
95
|
-
# ping peers in the validation route to update there records
|
|
96
|
-
if node.validation_route and node.outgoing_queue and node.addresses:
|
|
97
|
-
route_peers = {
|
|
98
|
-
peer_key
|
|
99
|
-
for bucket in getattr(node.validation_route, "buckets", {}).values()
|
|
100
|
-
for peer_key in bucket
|
|
101
|
-
}
|
|
102
|
-
if route_peers:
|
|
103
|
-
ping_payload = Ping(
|
|
104
|
-
is_validator=True,
|
|
105
|
-
latest_block=new_block_hash,
|
|
106
|
-
).to_bytes()
|
|
107
|
-
|
|
108
|
-
message_bytes = Message(
|
|
109
|
-
topic=MessageTopic.PING,
|
|
110
|
-
content=ping_payload,
|
|
111
|
-
).to_bytes()
|
|
112
|
-
|
|
113
|
-
for address, peer_key in node.addresses.items():
|
|
114
|
-
if peer_key in route_peers:
|
|
115
|
-
try:
|
|
116
|
-
node.outgoing_queue.put((message_bytes, address))
|
|
117
|
-
except Exception:
|
|
118
|
-
pass
|
|
119
|
-
|
|
120
|
-
# store the new block and receipts
|
|
121
|
-
|
|
122
|
-
return _validation_worker
|
astreum/_lispeum/__init__.py
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
from .expression import Expr
|
|
2
|
-
from .environment import Env
|
|
3
|
-
from .low_evaluation import low_eval
|
|
4
|
-
from .meter import Meter
|
|
5
|
-
from .parser import parse, ParseError
|
|
6
|
-
from .tokenizer import tokenize
|
|
7
|
-
|
|
8
|
-
__all__ = [
|
|
9
|
-
"Env",
|
|
10
|
-
"Expr",
|
|
11
|
-
"low_eval",
|
|
12
|
-
"Meter",
|
|
13
|
-
"parse",
|
|
14
|
-
"tokenize",
|
|
15
|
-
"ParseError",
|
|
16
|
-
]
|
astreum/_lispeum/environment.py
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
from ast import Expr
|
|
2
|
-
from typing import Dict, Optional
|
|
3
|
-
import uuid
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class Env:
|
|
7
|
-
def __init__(
|
|
8
|
-
self,
|
|
9
|
-
data: Optional[Dict[str, Expr]] = None,
|
|
10
|
-
parent_id: Optional[uuid.UUID] = None,
|
|
11
|
-
):
|
|
12
|
-
self.data: Dict[bytes, Expr] = {} if data is None else data
|
|
13
|
-
self.parent_id = parent_id
|
astreum/_lispeum/expression.py
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
from typing import List, Optional
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class Expr:
|
|
5
|
-
class ListExpr:
|
|
6
|
-
def __init__(self, elements: List['Expr']):
|
|
7
|
-
self.elements = elements
|
|
8
|
-
|
|
9
|
-
def __repr__(self):
|
|
10
|
-
if not self.elements:
|
|
11
|
-
return "()"
|
|
12
|
-
inner = " ".join(str(e) for e in self.elements)
|
|
13
|
-
return f"({inner} list)"
|
|
14
|
-
|
|
15
|
-
class Symbol:
|
|
16
|
-
def __init__(self, value: str):
|
|
17
|
-
self.value = value
|
|
18
|
-
|
|
19
|
-
def __repr__(self):
|
|
20
|
-
return f"({self.value} symbol)"
|
|
21
|
-
|
|
22
|
-
class Bytes:
|
|
23
|
-
def __init__(self, value: bytes):
|
|
24
|
-
self.value = value
|
|
25
|
-
|
|
26
|
-
def __repr__(self):
|
|
27
|
-
return f"({self.value} bytes)"
|
|
28
|
-
|
|
29
|
-
class Error:
|
|
30
|
-
def __init__(self, topic: str, origin: Optional['Expr'] = None):
|
|
31
|
-
self.topic = topic
|
|
32
|
-
self.origin = origin
|
|
33
|
-
|
|
34
|
-
def __repr__(self):
|
|
35
|
-
if self.origin is None:
|
|
36
|
-
return f'({self.topic} error)'
|
|
37
|
-
return f'({self.origin} {self.topic} error)'
|