zexus 1.6.8 → 1.7.2

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 (177) hide show
  1. package/README.md +12 -5
  2. package/package.json +1 -1
  3. package/src/__init__.py +7 -0
  4. package/src/zexus/__init__.py +1 -1
  5. package/src/zexus/__pycache__/__init__.cpython-312.pyc +0 -0
  6. package/src/zexus/__pycache__/capability_system.cpython-312.pyc +0 -0
  7. package/src/zexus/__pycache__/debug_sanitizer.cpython-312.pyc +0 -0
  8. package/src/zexus/__pycache__/environment.cpython-312.pyc +0 -0
  9. package/src/zexus/__pycache__/error_reporter.cpython-312.pyc +0 -0
  10. package/src/zexus/__pycache__/input_validation.cpython-312.pyc +0 -0
  11. package/src/zexus/__pycache__/lexer.cpython-312.pyc +0 -0
  12. package/src/zexus/__pycache__/module_cache.cpython-312.pyc +0 -0
  13. package/src/zexus/__pycache__/module_manager.cpython-312.pyc +0 -0
  14. package/src/zexus/__pycache__/object.cpython-312.pyc +0 -0
  15. package/src/zexus/__pycache__/security.cpython-312.pyc +0 -0
  16. package/src/zexus/__pycache__/security_enforcement.cpython-312.pyc +0 -0
  17. package/src/zexus/__pycache__/syntax_validator.cpython-312.pyc +0 -0
  18. package/src/zexus/__pycache__/zexus_ast.cpython-312.pyc +0 -0
  19. package/src/zexus/__pycache__/zexus_token.cpython-312.pyc +0 -0
  20. package/src/zexus/access_control_system/__pycache__/__init__.cpython-312.pyc +0 -0
  21. package/src/zexus/access_control_system/__pycache__/access_control.cpython-312.pyc +0 -0
  22. package/src/zexus/advanced_types.py +17 -2
  23. package/src/zexus/blockchain/__init__.py +411 -0
  24. package/src/zexus/blockchain/accelerator.py +1160 -0
  25. package/src/zexus/blockchain/chain.py +660 -0
  26. package/src/zexus/blockchain/consensus.py +821 -0
  27. package/src/zexus/blockchain/contract_vm.py +1019 -0
  28. package/src/zexus/blockchain/crypto.py +79 -14
  29. package/src/zexus/blockchain/events.py +526 -0
  30. package/src/zexus/blockchain/loadtest.py +721 -0
  31. package/src/zexus/blockchain/monitoring.py +350 -0
  32. package/src/zexus/blockchain/mpt.py +716 -0
  33. package/src/zexus/blockchain/multichain.py +951 -0
  34. package/src/zexus/blockchain/multiprocess_executor.py +338 -0
  35. package/src/zexus/blockchain/network.py +886 -0
  36. package/src/zexus/blockchain/node.py +666 -0
  37. package/src/zexus/blockchain/rpc.py +1203 -0
  38. package/src/zexus/blockchain/rust_bridge.py +421 -0
  39. package/src/zexus/blockchain/storage.py +423 -0
  40. package/src/zexus/blockchain/tokens.py +750 -0
  41. package/src/zexus/blockchain/upgradeable.py +1004 -0
  42. package/src/zexus/blockchain/verification.py +1602 -0
  43. package/src/zexus/blockchain/wallet.py +621 -0
  44. package/src/zexus/capability_system.py +184 -9
  45. package/src/zexus/cli/__pycache__/main.cpython-312.pyc +0 -0
  46. package/src/zexus/cli/main.py +383 -34
  47. package/src/zexus/cli/zpm.py +1 -1
  48. package/src/zexus/compiler/__pycache__/bytecode.cpython-312.pyc +0 -0
  49. package/src/zexus/compiler/__pycache__/lexer.cpython-312.pyc +0 -0
  50. package/src/zexus/compiler/__pycache__/parser.cpython-312.pyc +0 -0
  51. package/src/zexus/compiler/__pycache__/semantic.cpython-312.pyc +0 -0
  52. package/src/zexus/compiler/__pycache__/zexus_ast.cpython-312.pyc +0 -0
  53. package/src/zexus/compiler/bytecode.py +124 -7
  54. package/src/zexus/compiler/compat_runtime.py +6 -2
  55. package/src/zexus/compiler/lexer.py +16 -5
  56. package/src/zexus/compiler/parser.py +108 -7
  57. package/src/zexus/compiler/semantic.py +18 -19
  58. package/src/zexus/compiler/zexus_ast.py +26 -1
  59. package/src/zexus/concurrency_system.py +79 -0
  60. package/src/zexus/config.py +54 -0
  61. package/src/zexus/crypto_bridge.py +244 -8
  62. package/src/zexus/dap/__init__.py +10 -0
  63. package/src/zexus/dap/__main__.py +4 -0
  64. package/src/zexus/dap/dap_server.py +391 -0
  65. package/src/zexus/dap/debug_engine.py +298 -0
  66. package/src/zexus/environment.py +112 -9
  67. package/src/zexus/evaluator/__pycache__/bytecode_compiler.cpython-312.pyc +0 -0
  68. package/src/zexus/evaluator/__pycache__/core.cpython-312.pyc +0 -0
  69. package/src/zexus/evaluator/__pycache__/expressions.cpython-312.pyc +0 -0
  70. package/src/zexus/evaluator/__pycache__/functions.cpython-312.pyc +0 -0
  71. package/src/zexus/evaluator/__pycache__/resource_limiter.cpython-312.pyc +0 -0
  72. package/src/zexus/evaluator/__pycache__/statements.cpython-312.pyc +0 -0
  73. package/src/zexus/evaluator/__pycache__/unified_execution.cpython-312.pyc +0 -0
  74. package/src/zexus/evaluator/__pycache__/utils.cpython-312.pyc +0 -0
  75. package/src/zexus/evaluator/bytecode_compiler.py +457 -37
  76. package/src/zexus/evaluator/core.py +644 -50
  77. package/src/zexus/evaluator/expressions.py +358 -62
  78. package/src/zexus/evaluator/functions.py +458 -20
  79. package/src/zexus/evaluator/resource_limiter.py +4 -4
  80. package/src/zexus/evaluator/statements.py +774 -122
  81. package/src/zexus/evaluator/unified_execution.py +573 -72
  82. package/src/zexus/evaluator/utils.py +14 -2
  83. package/src/zexus/evaluator_original.py +1 -1
  84. package/src/zexus/event_loop.py +186 -0
  85. package/src/zexus/lexer.py +742 -458
  86. package/src/zexus/lsp/__init__.py +1 -1
  87. package/src/zexus/lsp/definition_provider.py +163 -9
  88. package/src/zexus/lsp/server.py +22 -8
  89. package/src/zexus/lsp/symbol_provider.py +182 -9
  90. package/src/zexus/module_cache.py +239 -9
  91. package/src/zexus/module_manager.py +129 -1
  92. package/src/zexus/object.py +76 -6
  93. package/src/zexus/parser/__pycache__/parser.cpython-312.pyc +0 -0
  94. package/src/zexus/parser/__pycache__/strategy_context.cpython-312.pyc +0 -0
  95. package/src/zexus/parser/__pycache__/strategy_structural.cpython-312.pyc +0 -0
  96. package/src/zexus/parser/parser.py +1349 -408
  97. package/src/zexus/parser/strategy_context.py +755 -58
  98. package/src/zexus/parser/strategy_structural.py +121 -21
  99. package/src/zexus/persistence.py +15 -1
  100. package/src/zexus/renderer/__init__.py +61 -0
  101. package/src/zexus/renderer/__pycache__/__init__.cpython-312.pyc +0 -0
  102. package/src/zexus/renderer/__pycache__/backend.cpython-312.pyc +0 -0
  103. package/src/zexus/renderer/__pycache__/canvas.cpython-312.pyc +0 -0
  104. package/src/zexus/renderer/__pycache__/color_system.cpython-312.pyc +0 -0
  105. package/src/zexus/renderer/__pycache__/layout.cpython-312.pyc +0 -0
  106. package/src/zexus/renderer/__pycache__/main_renderer.cpython-312.pyc +0 -0
  107. package/src/zexus/renderer/__pycache__/painter.cpython-312.pyc +0 -0
  108. package/src/zexus/renderer/backend.py +261 -0
  109. package/src/zexus/renderer/canvas.py +78 -0
  110. package/src/zexus/renderer/color_system.py +201 -0
  111. package/src/zexus/renderer/graphics.py +31 -0
  112. package/src/zexus/renderer/layout.py +222 -0
  113. package/src/zexus/renderer/main_renderer.py +66 -0
  114. package/src/zexus/renderer/painter.py +30 -0
  115. package/src/zexus/renderer/tk_backend.py +208 -0
  116. package/src/zexus/renderer/web_backend.py +260 -0
  117. package/src/zexus/runtime/__init__.py +10 -2
  118. package/src/zexus/runtime/__pycache__/__init__.cpython-312.pyc +0 -0
  119. package/src/zexus/runtime/__pycache__/async_runtime.cpython-312.pyc +0 -0
  120. package/src/zexus/runtime/__pycache__/load_manager.cpython-312.pyc +0 -0
  121. package/src/zexus/runtime/file_flags.py +137 -0
  122. package/src/zexus/runtime/load_manager.py +368 -0
  123. package/src/zexus/safety/__pycache__/__init__.cpython-312.pyc +0 -0
  124. package/src/zexus/safety/__pycache__/memory_safety.cpython-312.pyc +0 -0
  125. package/src/zexus/security.py +424 -34
  126. package/src/zexus/stdlib/fs.py +23 -18
  127. package/src/zexus/stdlib/http.py +289 -186
  128. package/src/zexus/stdlib/sockets.py +207 -163
  129. package/src/zexus/stdlib/websockets.py +282 -0
  130. package/src/zexus/stdlib_integration.py +369 -2
  131. package/src/zexus/strategy_recovery.py +6 -3
  132. package/src/zexus/type_checker.py +423 -0
  133. package/src/zexus/virtual_filesystem.py +189 -2
  134. package/src/zexus/vm/__init__.py +113 -3
  135. package/src/zexus/vm/__pycache__/async_optimizer.cpython-312.pyc +0 -0
  136. package/src/zexus/vm/__pycache__/bytecode.cpython-312.pyc +0 -0
  137. package/src/zexus/vm/__pycache__/bytecode_converter.cpython-312.pyc +0 -0
  138. package/src/zexus/vm/__pycache__/cache.cpython-312.pyc +0 -0
  139. package/src/zexus/vm/__pycache__/compiler.cpython-312.pyc +0 -0
  140. package/src/zexus/vm/__pycache__/gas_metering.cpython-312.pyc +0 -0
  141. package/src/zexus/vm/__pycache__/jit.cpython-312.pyc +0 -0
  142. package/src/zexus/vm/__pycache__/parallel_vm.cpython-312.pyc +0 -0
  143. package/src/zexus/vm/__pycache__/vm.cpython-312.pyc +0 -0
  144. package/src/zexus/vm/async_optimizer.py +80 -6
  145. package/src/zexus/vm/binary_bytecode.py +659 -0
  146. package/src/zexus/vm/bytecode.py +59 -11
  147. package/src/zexus/vm/bytecode_converter.py +26 -12
  148. package/src/zexus/vm/cabi.c +1985 -0
  149. package/src/zexus/vm/cabi.cpython-312-x86_64-linux-gnu.so +0 -0
  150. package/src/zexus/vm/cabi.h +127 -0
  151. package/src/zexus/vm/cache.py +561 -17
  152. package/src/zexus/vm/compiler.py +818 -51
  153. package/src/zexus/vm/fastops.c +15743 -0
  154. package/src/zexus/vm/fastops.cpython-312-x86_64-linux-gnu.so +0 -0
  155. package/src/zexus/vm/fastops.pyx +288 -0
  156. package/src/zexus/vm/gas_metering.py +50 -9
  157. package/src/zexus/vm/jit.py +364 -20
  158. package/src/zexus/vm/native_jit_backend.py +1816 -0
  159. package/src/zexus/vm/native_runtime.cpp +1388 -0
  160. package/src/zexus/vm/native_runtime.cpython-312-x86_64-linux-gnu.so +0 -0
  161. package/src/zexus/vm/optimizer.py +161 -11
  162. package/src/zexus/vm/parallel_vm.py +140 -45
  163. package/src/zexus/vm/peephole_optimizer.py +82 -4
  164. package/src/zexus/vm/profiler.py +38 -18
  165. package/src/zexus/vm/register_allocator.py +16 -5
  166. package/src/zexus/vm/register_vm.py +8 -5
  167. package/src/zexus/vm/vm.py +3581 -531
  168. package/src/zexus/vm/wasm_compiler.py +658 -0
  169. package/src/zexus/zexus_ast.py +137 -11
  170. package/src/zexus/zexus_token.py +16 -5
  171. package/src/zexus/zpm/installer.py +55 -15
  172. package/src/zexus/zpm/package_manager.py +1 -1
  173. package/src/zexus/zpm/registry.py +257 -28
  174. package/src/zexus.egg-info/PKG-INFO +16 -6
  175. package/src/zexus.egg-info/SOURCES.txt +129 -17
  176. package/src/zexus.egg-info/entry_points.txt +1 -0
  177. package/src/zexus.egg-info/requires.txt +4 -0
@@ -0,0 +1,338 @@
1
+ """
2
+ Zexus Blockchain — Multiprocessing Batch Executor
3
+ ===================================================
4
+
5
+ Dispatches contract-group transaction batches to **separate OS processes**
6
+ via ``multiprocessing.Pool``, giving each group its own Python GIL for
7
+ true CPU-level parallelism.
8
+
9
+ Architecture::
10
+
11
+ Main process (orchestrator)
12
+
13
+ ├── Worker-0 (own GIL) ← contract group A
14
+ ├── Worker-1 (own GIL) ← contract group B
15
+ ├── Worker-2 (own GIL) ← contract group C
16
+ └── ...
17
+
18
+ Each worker process re-instantiates the ``ContractVM`` (or a lightweight
19
+ mock) so that contract state is process-local. Results are aggregated
20
+ in the main process.
21
+
22
+ When combined with the Rust batched-GIL executor (Option 2) inside each
23
+ worker, the two optimisations stack: Rayon parallelises within a process
24
+ while multiprocessing parallelises across processes.
25
+
26
+ Usage::
27
+
28
+ from zexus.blockchain.multiprocess_executor import MultiProcessBatchExecutor
29
+
30
+ executor = MultiProcessBatchExecutor(
31
+ vm_factory=lambda: MyContractVM(),
32
+ workers=4,
33
+ )
34
+ result = executor.execute_batch(transactions)
35
+ print(result.throughput) # >> 10,000+ tx/s
36
+ """
37
+
38
+ from __future__ import annotations
39
+
40
+ import json
41
+ import logging
42
+ import multiprocessing
43
+ import os
44
+ import time
45
+ from collections import defaultdict
46
+ from concurrent.futures import ProcessPoolExecutor, as_completed
47
+ from typing import Any, Callable, Dict, List, Optional, Tuple
48
+
49
+ logger = logging.getLogger("zexus.blockchain.multiprocess_executor")
50
+
51
+
52
+ # ── Results ────────────────────────────────────────────────────────────
53
+
54
+ class MPBatchResult:
55
+ """Aggregated result from a multiprocess batch execution."""
56
+
57
+ __slots__ = (
58
+ "total", "succeeded", "failed", "gas_used",
59
+ "elapsed", "receipts",
60
+ )
61
+
62
+ def __init__(
63
+ self,
64
+ total: int = 0,
65
+ succeeded: int = 0,
66
+ failed: int = 0,
67
+ gas_used: int = 0,
68
+ elapsed: float = 0.0,
69
+ receipts: Optional[List[Dict[str, Any]]] = None,
70
+ ):
71
+ self.total = total
72
+ self.succeeded = succeeded
73
+ self.failed = failed
74
+ self.gas_used = gas_used
75
+ self.elapsed = elapsed
76
+ self.receipts = receipts or []
77
+
78
+ @property
79
+ def throughput(self) -> float:
80
+ return self.total / self.elapsed if self.elapsed > 0 else 0.0
81
+
82
+ def __repr__(self) -> str:
83
+ return (
84
+ f"MPBatchResult(total={self.total}, ok={self.succeeded}, "
85
+ f"fail={self.failed}, gas={self.gas_used}, "
86
+ f"{self.throughput:.1f} tx/s)"
87
+ )
88
+
89
+
90
+ # ── Worker function (runs in child process) ───────────────────────────
91
+
92
+ # Module-level VM holder for the child process.
93
+ _worker_vm: Any = None
94
+
95
+
96
+ def _init_worker(vm_factory_pickle: bytes) -> None:
97
+ """Process initialiser — creates a VM instance per worker."""
98
+ import pickle
99
+ global _worker_vm
100
+ vm_factory = pickle.loads(vm_factory_pickle)
101
+ _worker_vm = vm_factory()
102
+
103
+
104
+ def _execute_group(
105
+ contract_addr: str,
106
+ txs_json: str,
107
+ ) -> str:
108
+ """Execute a group of transactions in the worker's own GIL.
109
+
110
+ Receives and returns JSON to avoid pickle overhead on complex objects.
111
+ """
112
+ global _worker_vm
113
+ import json as _json
114
+ import hashlib as _hashlib
115
+
116
+ txs = _json.loads(txs_json)
117
+ results: List[Dict[str, Any]] = []
118
+ succeeded = 0
119
+ failed = 0
120
+ gas_used = 0
121
+
122
+ for tx in txs:
123
+ try:
124
+ if _worker_vm is not None and hasattr(_worker_vm, "execute_action"):
125
+ receipt = _worker_vm.execute_action(
126
+ contract=tx.get("contract", contract_addr),
127
+ action=tx.get("action", ""),
128
+ args=tx.get("args", {}),
129
+ caller=tx.get("caller", ""),
130
+ gas_limit=tx.get("gas_limit", 100_000),
131
+ )
132
+ else:
133
+ # Minimal fallback — hash-based mock
134
+ data = (
135
+ f"{contract_addr}:{tx.get('action', '')}:"
136
+ f"{tx.get('caller', '')}:{_json.dumps(tx.get('args', {}), sort_keys=True)}"
137
+ )
138
+ receipt = {
139
+ "success": True,
140
+ "gas_used": max(21_000, len(data) * 8),
141
+ "result": _hashlib.sha256(data.encode()).hexdigest()[:16],
142
+ }
143
+
144
+ if isinstance(receipt, dict) and receipt.get("success"):
145
+ succeeded += 1
146
+ gas_used += receipt.get("gas_used", 0)
147
+ else:
148
+ failed += 1
149
+ results.append(receipt if isinstance(receipt, dict) else {"success": False})
150
+ except Exception as e:
151
+ failed += 1
152
+ results.append({"success": False, "error": str(e)})
153
+
154
+ return _json.dumps({
155
+ "contract": contract_addr,
156
+ "succeeded": succeeded,
157
+ "failed": failed,
158
+ "gas_used": gas_used,
159
+ "receipts": results,
160
+ })
161
+
162
+
163
+ # ── Try Rust-accelerated group execution ──────────────────────────────
164
+
165
+ def _execute_group_rust(
166
+ contract_addr: str,
167
+ txs_json: str,
168
+ ) -> str:
169
+ """Execute a group using the Rust batched-GIL executor within the worker."""
170
+ global _worker_vm
171
+ import json as _json
172
+
173
+ txs = _json.loads(txs_json)
174
+
175
+ try:
176
+ import zexus_core # type: ignore[import-untyped]
177
+
178
+ # Build the vm_callback for Rust
179
+ def _callback(contract, action, args_json_str, caller, gas_limit):
180
+ if _worker_vm is not None and hasattr(_worker_vm, "execute_action"):
181
+ import json as _j
182
+ args = _j.loads(args_json_str) if isinstance(args_json_str, str) else args_json_str
183
+ gas = int(gas_limit) if isinstance(gas_limit, str) else gas_limit
184
+ return _worker_vm.execute_action(
185
+ contract=contract, action=action,
186
+ args=args, caller=caller, gas_limit=gas,
187
+ )
188
+ # Mock fallback
189
+ import hashlib as _h
190
+ data = f"{contract}:{action}:{caller}:{args_json_str}"
191
+ return {
192
+ "success": True,
193
+ "gas_used": max(21_000, len(data) * 8),
194
+ "result": _h.sha256(data.encode()).hexdigest()[:16],
195
+ }
196
+
197
+ # Serialise txs for Rust
198
+ serialized = []
199
+ for tx in txs:
200
+ serialized.append({
201
+ "contract": str(tx.get("contract", contract_addr)),
202
+ "action": str(tx.get("action", "")),
203
+ "args": _json.dumps(tx.get("args", {})) if not isinstance(tx.get("args"), str) else tx["args"],
204
+ "caller": str(tx.get("caller", "")),
205
+ "gas_limit": str(tx.get("gas_limit", "0")),
206
+ })
207
+
208
+ executor = zexus_core.RustBatchExecutor(max_workers=1)
209
+ result = executor.execute_batch(serialized, _callback)
210
+
211
+ receipts = []
212
+ for r_json in result.receipts:
213
+ try:
214
+ receipts.append(_json.loads(r_json))
215
+ except _json.JSONDecodeError:
216
+ receipts.append({"success": False, "error": r_json})
217
+
218
+ return _json.dumps({
219
+ "contract": contract_addr,
220
+ "succeeded": result.succeeded,
221
+ "failed": result.failed,
222
+ "gas_used": result.gas_used,
223
+ "receipts": receipts,
224
+ })
225
+ except ImportError:
226
+ # Rust not available in worker — fall back to pure Python
227
+ return _execute_group(contract_addr, txs_json)
228
+
229
+
230
+ def _default_vm_factory():
231
+ """Default VM factory that creates no VM (uses mock fallback)."""
232
+ return None
233
+
234
+
235
+ # ── Main executor class ───────────────────────────────────────────────
236
+
237
+ class MultiProcessBatchExecutor:
238
+ """Multi-process batch executor — true GIL-free parallelism.
239
+
240
+ Parameters
241
+ ----------
242
+ vm_factory : callable
243
+ A zero-argument callable that returns a ContractVM instance.
244
+ Must be picklable (e.g. a module-level function or lambda
245
+ with no closures over unpicklable objects).
246
+ workers : int
247
+ Number of worker processes (default: CPU count).
248
+ use_rust_in_workers : bool
249
+ If True, each worker uses the Rust batched-GIL executor for
250
+ its group — stacking Rayon parallelism (within-process) with
251
+ multiprocessing parallelism (across processes).
252
+ """
253
+
254
+ def __init__(
255
+ self,
256
+ vm_factory: Optional[Callable[[], Any]] = None,
257
+ workers: int = 0,
258
+ use_rust_in_workers: bool = True,
259
+ ):
260
+ self._vm_factory = vm_factory
261
+ self._workers = workers or min(multiprocessing.cpu_count(), 16)
262
+ self._use_rust = use_rust_in_workers
263
+ self._pool: Optional[ProcessPoolExecutor] = None
264
+ self._pool_mp: Optional[multiprocessing.pool.Pool] = None
265
+
266
+ def _ensure_pool(self) -> None:
267
+ """Lazily create the process pool."""
268
+ if self._pool_mp is not None:
269
+ return
270
+ import pickle
271
+ factory = self._vm_factory if self._vm_factory else _default_vm_factory
272
+ factory_bytes = pickle.dumps(factory)
273
+ self._pool_mp = multiprocessing.Pool(
274
+ processes=self._workers,
275
+ initializer=_init_worker,
276
+ initargs=(factory_bytes,),
277
+ )
278
+
279
+ def execute_batch(
280
+ self,
281
+ transactions: List[Dict[str, Any]],
282
+ ) -> MPBatchResult:
283
+ """Execute a batch of transactions across multiple processes.
284
+
285
+ Transactions are grouped by contract address. Each group is
286
+ dispatched to a worker process that has its own Python GIL.
287
+ """
288
+ start = time.perf_counter()
289
+ self._ensure_pool()
290
+
291
+ # Group by contract
292
+ groups: Dict[str, List[Dict[str, Any]]] = defaultdict(list)
293
+ for tx in transactions:
294
+ groups[tx.get("contract", "unknown")].append(tx)
295
+
296
+ # Choose worker function
297
+ worker_fn = _execute_group_rust if self._use_rust else _execute_group
298
+
299
+ # Dispatch groups to workers
300
+ async_results = []
301
+ for contract_addr, txs in groups.items():
302
+ txs_json = json.dumps(txs)
303
+ async_results.append(
304
+ self._pool_mp.apply_async(worker_fn, (contract_addr, txs_json)) # type: ignore
305
+ )
306
+
307
+ # Collect results
308
+ result = MPBatchResult(total=len(transactions))
309
+ for ar in async_results:
310
+ try:
311
+ group_json = ar.get(timeout=30)
312
+ group_data = json.loads(group_json)
313
+ result.succeeded += group_data.get("succeeded", 0)
314
+ result.failed += group_data.get("failed", 0)
315
+ result.gas_used += group_data.get("gas_used", 0)
316
+ result.receipts.extend(group_data.get("receipts", []))
317
+ except Exception as e:
318
+ logger.error("Worker group failed: %s", e)
319
+ result.failed += 1
320
+
321
+ result.elapsed = time.perf_counter() - start
322
+ return result
323
+
324
+ def shutdown(self) -> None:
325
+ """Terminate the process pool."""
326
+ if self._pool_mp:
327
+ self._pool_mp.terminate()
328
+ self._pool_mp.join()
329
+ self._pool_mp = None
330
+
331
+ def __del__(self) -> None:
332
+ self.shutdown()
333
+
334
+ def __repr__(self) -> str:
335
+ return (
336
+ f"MultiProcessBatchExecutor(workers={self._workers}, "
337
+ f"rust_in_workers={self._use_rust})"
338
+ )