zexus 1.7.2 → 1.8.0
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.
- package/README.md +26 -3
- package/package.json +1 -1
- package/src/zexus/__init__.py +1 -1
- package/src/zexus/blockchain/accelerator.py +27 -0
- package/src/zexus/blockchain/contract_vm.py +409 -3
- package/src/zexus/blockchain/rust_bridge.py +64 -0
- package/src/zexus/cli/main.py +1 -1
- package/src/zexus/cli/zpm.py +1 -1
- package/src/zexus/vm/fastops.c +1093 -2975
- package/src/zexus/vm/gas_metering.py +2 -2
- package/src/zexus/vm/vm.py +163 -0
- package/src/zexus/zpm/package_manager.py +1 -1
- package/src/zexus.egg-info/PKG-INFO +27 -4
- package/src/zexus.egg-info/SOURCES.txt +17 -0
|
@@ -59,7 +59,7 @@ class GasCost(IntEnum):
|
|
|
59
59
|
CALL_TOP = 10
|
|
60
60
|
CALL_METHOD = 10
|
|
61
61
|
CALL_BUILTIN = 8
|
|
62
|
-
STORE_FUNC =
|
|
62
|
+
STORE_FUNC = 3 # Aligned with Rust — function storage is a cheap memory op
|
|
63
63
|
BUILD_LAMBDA = 5
|
|
64
64
|
|
|
65
65
|
# Async operations
|
|
@@ -71,7 +71,7 @@ class GasCost(IntEnum):
|
|
|
71
71
|
VERIFY_SIGNATURE = 100
|
|
72
72
|
MERKLE_ROOT = 30 # Base cost, + per leaf
|
|
73
73
|
STATE_READ = 20
|
|
74
|
-
STATE_WRITE =
|
|
74
|
+
STATE_WRITE = 30 # Reduced: RustStateAdapter caching makes writes memory-local
|
|
75
75
|
TX_BEGIN = 20
|
|
76
76
|
TX_COMMIT = 30
|
|
77
77
|
TX_REVERT = 20
|
package/src/zexus/vm/vm.py
CHANGED
|
@@ -136,6 +136,17 @@ except Exception:
|
|
|
136
136
|
_FASTOPS_AVAILABLE = False
|
|
137
137
|
_fastops = None
|
|
138
138
|
|
|
139
|
+
# Rust VM (Phase 3 — adaptive execution)
|
|
140
|
+
try:
|
|
141
|
+
from zexus_core import RustVMExecutor as _RustVMExecutor
|
|
142
|
+
_RUST_VM_AVAILABLE = True
|
|
143
|
+
except Exception:
|
|
144
|
+
_RUST_VM_AVAILABLE = False
|
|
145
|
+
_RustVMExecutor = None
|
|
146
|
+
|
|
147
|
+
# Sentinel returned when Rust VM signals needs_fallback
|
|
148
|
+
_RUST_VM_FALLBACK_SENTINEL = object()
|
|
149
|
+
|
|
139
150
|
# Async Optimizer (Phase 8)
|
|
140
151
|
try:
|
|
141
152
|
from .async_optimizer import AsyncOptimizer, AsyncOptimizationLevel
|
|
@@ -425,6 +436,25 @@ class VM:
|
|
|
425
436
|
self._in_execution = 0
|
|
426
437
|
self._native_jit_auto_enabled = False
|
|
427
438
|
self._native_jit_auto_threshold = 700
|
|
439
|
+
|
|
440
|
+
# --- Rust VM Adaptive Routing (Phase 3 + Phase 6) ---
|
|
441
|
+
self._rust_vm_available = _RUST_VM_AVAILABLE
|
|
442
|
+
self._rust_vm_executor = _RustVMExecutor() if _RUST_VM_AVAILABLE else None
|
|
443
|
+
self._rust_vm_threshold = 0 # Phase 6: route ALL programs through Rust by default
|
|
444
|
+
try:
|
|
445
|
+
_env_thresh = os.environ.get("ZEXUS_RUST_VM_THRESHOLD")
|
|
446
|
+
if _env_thresh is not None:
|
|
447
|
+
self._rust_vm_threshold = int(_env_thresh)
|
|
448
|
+
except (ValueError, TypeError):
|
|
449
|
+
pass
|
|
450
|
+
self._rust_vm_enabled = self._rust_vm_available # Can be disabled at runtime
|
|
451
|
+
self._rust_vm_stats = {
|
|
452
|
+
"rust_executions": 0,
|
|
453
|
+
"rust_fallbacks": 0,
|
|
454
|
+
"python_executions": 0,
|
|
455
|
+
"total_rust_ops": 0,
|
|
456
|
+
}
|
|
457
|
+
|
|
428
458
|
self._env_version = 0
|
|
429
459
|
self._name_cache: Dict[str, Tuple[Any, int]] = {}
|
|
430
460
|
self._method_cache: Dict[Tuple[type, str], Any] = {}
|
|
@@ -1704,6 +1734,126 @@ class VM:
|
|
|
1704
1734
|
if tag == "LIST": return [self._eval_hl_op(e) for e in op[1]]
|
|
1705
1735
|
return None
|
|
1706
1736
|
|
|
1737
|
+
# ==================== Rust VM Adaptive Routing (Phase 3) ====================
|
|
1738
|
+
|
|
1739
|
+
def _execute_via_rust_vm(self, bytecode, debug=False):
|
|
1740
|
+
"""Serialize bytecode to .zxc and execute via the Rust VM.
|
|
1741
|
+
|
|
1742
|
+
Returns the result value on success, or ``_RUST_VM_FALLBACK_SENTINEL``
|
|
1743
|
+
if the Rust VM signals it needs a Python fallback (e.g. for
|
|
1744
|
+
CALL_NAME / CALL_METHOD that need Python interop).
|
|
1745
|
+
"""
|
|
1746
|
+
from .binary_bytecode import serialize as _serialize_zxc
|
|
1747
|
+
|
|
1748
|
+
# Serialize bytecode → .zxc binary
|
|
1749
|
+
zxc_data = _serialize_zxc(bytecode, include_checksum=True)
|
|
1750
|
+
|
|
1751
|
+
# Build env dict for Rust (only simple serializable values)
|
|
1752
|
+
rust_env = {}
|
|
1753
|
+
for k, v in self.env.items():
|
|
1754
|
+
if isinstance(v, (int, float, str, bool, type(None))):
|
|
1755
|
+
rust_env[k] = v
|
|
1756
|
+
elif isinstance(v, ZInteger):
|
|
1757
|
+
rust_env[k] = v.value
|
|
1758
|
+
elif isinstance(v, ZFloat):
|
|
1759
|
+
rust_env[k] = v.value
|
|
1760
|
+
elif isinstance(v, ZString):
|
|
1761
|
+
rust_env[k] = v.value
|
|
1762
|
+
elif isinstance(v, ZBoolean):
|
|
1763
|
+
rust_env[k] = v.value
|
|
1764
|
+
elif isinstance(v, (ZNull, type(None))):
|
|
1765
|
+
rust_env[k] = None
|
|
1766
|
+
# Skip non-serializable values (callables, AST nodes, etc.)
|
|
1767
|
+
|
|
1768
|
+
# Build state dict (if blockchain state exists)
|
|
1769
|
+
rust_state = {}
|
|
1770
|
+
bc_state = self.env.get("_blockchain_state")
|
|
1771
|
+
if bc_state and isinstance(bc_state, dict):
|
|
1772
|
+
for k, v in bc_state.items():
|
|
1773
|
+
if isinstance(v, (int, float, str, bool, type(None))):
|
|
1774
|
+
rust_state[k] = v
|
|
1775
|
+
|
|
1776
|
+
# Gas limit
|
|
1777
|
+
gas_limit = 0
|
|
1778
|
+
if self.gas_metering:
|
|
1779
|
+
remaining_fn = getattr(self.gas_metering, "remaining", None)
|
|
1780
|
+
if callable(remaining_fn):
|
|
1781
|
+
try:
|
|
1782
|
+
rem = remaining_fn()
|
|
1783
|
+
if isinstance(rem, (int, float)) and rem > 0:
|
|
1784
|
+
gas_limit = int(rem)
|
|
1785
|
+
except Exception:
|
|
1786
|
+
pass
|
|
1787
|
+
if gas_limit == 0:
|
|
1788
|
+
gas_limit_val = getattr(self.gas_metering, "gas_limit", 0) or 0
|
|
1789
|
+
gas_used_val = getattr(self.gas_metering, "gas_used", 0) or 0
|
|
1790
|
+
if isinstance(gas_limit_val, (int, float)) and isinstance(gas_used_val, (int, float)):
|
|
1791
|
+
if gas_limit_val > gas_used_val:
|
|
1792
|
+
gas_limit = int(gas_limit_val - gas_used_val)
|
|
1793
|
+
|
|
1794
|
+
# Execute via Rust VM
|
|
1795
|
+
result_dict = self._rust_vm_executor.execute(
|
|
1796
|
+
zxc_data,
|
|
1797
|
+
env=rust_env or None,
|
|
1798
|
+
state=rust_state or None,
|
|
1799
|
+
gas_limit=gas_limit,
|
|
1800
|
+
)
|
|
1801
|
+
|
|
1802
|
+
# Check for fallback
|
|
1803
|
+
if result_dict.get("needs_fallback", False):
|
|
1804
|
+
self._rust_vm_stats["rust_fallbacks"] += 1
|
|
1805
|
+
if debug:
|
|
1806
|
+
print("[VM] Rust VM needs Python fallback — delegating to Python VM")
|
|
1807
|
+
return _RUST_VM_FALLBACK_SENTINEL
|
|
1808
|
+
|
|
1809
|
+
# Check for errors
|
|
1810
|
+
error = result_dict.get("error")
|
|
1811
|
+
if error:
|
|
1812
|
+
if "OutOfGas" in str(error):
|
|
1813
|
+
if self.gas_metering:
|
|
1814
|
+
raise OutOfGasError(str(error))
|
|
1815
|
+
raise RuntimeError(str(error))
|
|
1816
|
+
if "RequireFailed" in str(error):
|
|
1817
|
+
raise RuntimeError(str(error))
|
|
1818
|
+
raise RuntimeError(f"Rust VM error: {error}")
|
|
1819
|
+
|
|
1820
|
+
# Success — update stats
|
|
1821
|
+
self._rust_vm_stats["rust_executions"] += 1
|
|
1822
|
+
self._rust_vm_stats["total_rust_ops"] += result_dict.get("instructions_executed", 0)
|
|
1823
|
+
|
|
1824
|
+
# Bridge gas usage back to Python metering
|
|
1825
|
+
if self.gas_metering:
|
|
1826
|
+
rust_gas = result_dict.get("gas_used", 0)
|
|
1827
|
+
if rust_gas > 0:
|
|
1828
|
+
gas_used_attr = getattr(self.gas_metering, "gas_used", None)
|
|
1829
|
+
if gas_used_attr is not None:
|
|
1830
|
+
self.gas_metering.gas_used = gas_used_attr + rust_gas
|
|
1831
|
+
add_gas = getattr(self.gas_metering, "add_gas", None)
|
|
1832
|
+
if add_gas:
|
|
1833
|
+
add_gas(rust_gas)
|
|
1834
|
+
|
|
1835
|
+
# Merge Rust env back into Python env
|
|
1836
|
+
rust_env_out = result_dict.get("env", {})
|
|
1837
|
+
if rust_env_out:
|
|
1838
|
+
for k, v in rust_env_out.items():
|
|
1839
|
+
self.env[k] = v
|
|
1840
|
+
|
|
1841
|
+
# Merge blockchain state back
|
|
1842
|
+
rust_state_out = result_dict.get("state", {})
|
|
1843
|
+
if rust_state_out and bc_state is not None:
|
|
1844
|
+
for k, v in rust_state_out.items():
|
|
1845
|
+
bc_state[k] = v
|
|
1846
|
+
|
|
1847
|
+
if debug:
|
|
1848
|
+
print(f"[VM] Rust VM executed: ops={result_dict.get('instructions_executed', 0)} "
|
|
1849
|
+
f"gas={result_dict.get('gas_used', 0)}")
|
|
1850
|
+
|
|
1851
|
+
return result_dict.get("result")
|
|
1852
|
+
|
|
1853
|
+
def get_rust_vm_stats(self):
|
|
1854
|
+
"""Return statistics about Rust VM usage."""
|
|
1855
|
+
return dict(self._rust_vm_stats)
|
|
1856
|
+
|
|
1707
1857
|
# ==================== Fast Synchronous Dispatch (Performance Mode) ====================
|
|
1708
1858
|
|
|
1709
1859
|
def _run_stack_bytecode_sync(self, bytecode, debug=False):
|
|
@@ -1736,6 +1886,19 @@ class VM:
|
|
|
1736
1886
|
except Exception:
|
|
1737
1887
|
pass
|
|
1738
1888
|
|
|
1889
|
+
# Rust VM adaptive routing (Phase 3) — delegate to Rust when
|
|
1890
|
+
# the program is large enough to amortise serialisation overhead.
|
|
1891
|
+
if (self._rust_vm_enabled
|
|
1892
|
+
and self._rust_vm_executor is not None
|
|
1893
|
+
and len(instrs) >= self._rust_vm_threshold):
|
|
1894
|
+
try:
|
|
1895
|
+
rust_result = self._execute_via_rust_vm(bytecode, debug)
|
|
1896
|
+
if rust_result is not _RUST_VM_FALLBACK_SENTINEL:
|
|
1897
|
+
return rust_result
|
|
1898
|
+
# Rust signalled needs_fallback — continue to Python VM
|
|
1899
|
+
except Exception:
|
|
1900
|
+
self._rust_vm_stats["rust_fallbacks"] += 1
|
|
1901
|
+
|
|
1739
1902
|
# Fast stack implementation
|
|
1740
1903
|
stack: List[Any] = []
|
|
1741
1904
|
stack_append = stack.append
|
|
@@ -23,7 +23,7 @@ class PackageManager:
|
|
|
23
23
|
self.installer = PackageInstaller(self.zpm_dir)
|
|
24
24
|
self.publisher = PackagePublisher(self.registry)
|
|
25
25
|
|
|
26
|
-
def init(self, name: str = None, version: str = "1.
|
|
26
|
+
def init(self, name: str = None, version: str = "1.8.0") -> Dict:
|
|
27
27
|
"""Initialize a new Zexus project with package.json"""
|
|
28
28
|
if self.config_file.exists():
|
|
29
29
|
print(f"⚠️ {self.config_file} already exists")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: zexus
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.0
|
|
4
4
|
Summary: A modern, security-first programming language with blockchain support
|
|
5
5
|
Home-page: https://github.com/Zaidux/zexus-interpreter
|
|
6
6
|
Author: Zaidux
|
|
@@ -53,7 +53,7 @@ Dynamic: requires-python
|
|
|
53
53
|
|
|
54
54
|
<div align="center">
|
|
55
55
|
|
|
56
|
-

|
|
57
57
|
[](LICENSE)
|
|
58
58
|
[](https://python.org)
|
|
59
59
|
[](https://github.com/Zaidux/zexus-interpreter)
|
|
@@ -118,9 +118,9 @@ Zexus is a next-generation, general-purpose programming language designed for se
|
|
|
118
118
|
|
|
119
119
|
---
|
|
120
120
|
|
|
121
|
-
## 🎉 What's New in v1.
|
|
121
|
+
## 🎉 What's New in v1.8.0
|
|
122
122
|
|
|
123
|
-
### Latest Features (v1.
|
|
123
|
+
### Latest Features (v1.8.0)
|
|
124
124
|
|
|
125
125
|
✅ **FIND Keyword** - Declarative project search that resolves exact module paths with scope filtering and smart suggestions
|
|
126
126
|
✅ **LOAD Keyword & Manager** - Provider-aware configuration loader with built-in ENV, JSON, and YAML support plus caching
|
|
@@ -2643,6 +2643,29 @@ See [Ecosystem Strategy](docs/ECOSYSTEM_STRATEGY.md) for detailed roadmap.
|
|
|
2643
2643
|
- Consider using `native` keyword for C/C++ FFI
|
|
2644
2644
|
- Profile with `memory_stats()` to check for leaks
|
|
2645
2645
|
|
|
2646
|
+
#### PyPI upload fails on Termux with: "unsupported platform tag 'linux_aarch64'"
|
|
2647
|
+
- PyPI rejects wheels with generic `linux_*` platform tags (common when building on Termux).
|
|
2648
|
+
- **Fix**: upload an sdist (source distribution) and/or a pure-Python wheel:
|
|
2649
|
+
- `python -m pip install --upgrade build twine`
|
|
2650
|
+
- `python -m build` # creates `dist/*.tar.gz` and (by default) a `py3-none-any.whl`
|
|
2651
|
+
- `python -m twine upload dist/*.tar.gz dist/*-py3-none-any.whl`
|
|
2652
|
+
- `ZEXUS_BUILD_EXTENSIONS=1 python -m build` means: **build the optional native extensions** (Cython/C/C++) *in addition* to the Python code (it does not skip Python). This produces a **platform-specific** wheel.
|
|
2653
|
+
- If you want “everything compiled” locally, use:
|
|
2654
|
+
- `ZEXUS_BUILD_EXTENSIONS=1 python -m build`
|
|
2655
|
+
- or `ZEXUS_BUILD_EXTENSIONS=1 python -m pip install .`
|
|
2656
|
+
- For publishing native wheels to PyPI, build them in a manylinux environment (e.g. GitHub Actions + cibuildwheel). Wheels built on Termux are usually **not** PyPI-uploadable.
|
|
2657
|
+
- This repo includes a workflow you can run: `.github/workflows/wheels.yml` (builds manylinux `aarch64` + `x86_64`, plus macOS/Windows wheels).
|
|
2658
|
+
- To publish from GitHub Actions without API tokens, use **PyPI Trusted Publishing**:
|
|
2659
|
+
- PyPI → your project → **Settings** → **Publishing** → **Trusted publishers** → **Add a new trusted publisher**
|
|
2660
|
+
- Provider: **GitHub Actions**
|
|
2661
|
+
- Owner: `Zaidux` (or your GitHub org/user)
|
|
2662
|
+
- Repository: `zexus-interpreter`
|
|
2663
|
+
- Workflow: `.github/workflows/wheels.yml`
|
|
2664
|
+
- Environment: `pypi` (and optionally also add another trusted publisher entry for `testpypi`)
|
|
2665
|
+
- Then either:
|
|
2666
|
+
- Run **Actions → “Build wheels (cibuildwheel)” → Run workflow** and choose `testpypi` first, or
|
|
2667
|
+
- Push a release tag like `v1.7.3` to auto-build + publish
|
|
2668
|
+
|
|
2646
2669
|
#### Blockchain/Contract issues
|
|
2647
2670
|
- Remember `TX` is a global context object (uppercase)
|
|
2648
2671
|
- Use `persistent storage` for contract state
|
|
@@ -28,6 +28,13 @@ VULNERABILITY_FINDINGS.md
|
|
|
28
28
|
VULNERABILITY_TEST_RESULTS.md
|
|
29
29
|
ZEXUS_BLOCKCHAIN_REFERENCE.md
|
|
30
30
|
any.zx
|
|
31
|
+
bench_compare.py
|
|
32
|
+
bench_gas_stress.py
|
|
33
|
+
bench_phase3.py
|
|
34
|
+
bench_phase6.py
|
|
35
|
+
bench_quick.py
|
|
36
|
+
bench_vs.py
|
|
37
|
+
benchmark_rust_vm.py
|
|
31
38
|
check_action_tokens.py
|
|
32
39
|
check_tokens.py
|
|
33
40
|
check_verify_ast.py
|
|
@@ -73,6 +80,7 @@ zx-dev
|
|
|
73
80
|
zx-run
|
|
74
81
|
zx.bin
|
|
75
82
|
.github/linguist.yml
|
|
83
|
+
.github/workflows/wheels.yml
|
|
76
84
|
.vscode/extensions.json
|
|
77
85
|
.vscode/settings.json
|
|
78
86
|
.vscode/extensions/zexus-language/README.md
|
|
@@ -264,6 +272,7 @@ docs/PERFORMANCE_FEATURES.md
|
|
|
264
272
|
docs/PHILOSOPHY.md
|
|
265
273
|
docs/PLUGIN_QUICK_REFERENCE.md
|
|
266
274
|
docs/PLUGIN_SYSTEM.md
|
|
275
|
+
docs/PYPI_TRUSTED_PUBLISHING.md
|
|
267
276
|
docs/QUICK_REFERENCE.md
|
|
268
277
|
docs/QUICK_START.md
|
|
269
278
|
docs/README.md
|
|
@@ -477,11 +486,14 @@ linguist-submission/samples/security.zx
|
|
|
477
486
|
rust_core/Cargo.lock
|
|
478
487
|
rust_core/Cargo.toml
|
|
479
488
|
rust_core/src/binary_bytecode.rs
|
|
489
|
+
rust_core/src/contract_vm.rs
|
|
480
490
|
rust_core/src/executor.rs
|
|
481
491
|
rust_core/src/hasher.rs
|
|
482
492
|
rust_core/src/lib.rs
|
|
483
493
|
rust_core/src/merkle.rs
|
|
494
|
+
rust_core/src/rust_vm.rs
|
|
484
495
|
rust_core/src/signature.rs
|
|
496
|
+
rust_core/src/state_adapter.rs
|
|
485
497
|
rust_core/src/validator.rs
|
|
486
498
|
samples/basic.zx
|
|
487
499
|
samples/blockchain.zx
|
|
@@ -1088,9 +1100,14 @@ tests/vm/test_memory_pool.py
|
|
|
1088
1100
|
tests/vm/test_optimizer.py
|
|
1089
1101
|
tests/vm/test_parallel_vm.py
|
|
1090
1102
|
tests/vm/test_peephole_optimizer.py
|
|
1103
|
+
tests/vm/test_phase3_adaptive.py
|
|
1104
|
+
tests/vm/test_phase4_contract_vm.py
|
|
1105
|
+
tests/vm/test_phase5_gil_free.py
|
|
1106
|
+
tests/vm/test_phase6_builtins.py
|
|
1091
1107
|
tests/vm/test_profiler.py
|
|
1092
1108
|
tests/vm/test_register_allocator.py
|
|
1093
1109
|
tests/vm/test_register_vm.py
|
|
1110
|
+
tests/vm/test_rust_vm.py
|
|
1094
1111
|
tests/vm/test_ssa_converter.py
|
|
1095
1112
|
tests/vm/test_vm_async_integration.py
|
|
1096
1113
|
tests/vm/test_vm_memory_pool_integration.py
|