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.
- package/README.md +12 -5
- package/package.json +1 -1
- package/src/__init__.py +7 -0
- package/src/zexus/__init__.py +1 -1
- package/src/zexus/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/capability_system.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/debug_sanitizer.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/environment.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/error_reporter.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/input_validation.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/lexer.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/module_cache.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/module_manager.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/object.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/security.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/security_enforcement.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/syntax_validator.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/zexus_ast.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/zexus_token.cpython-312.pyc +0 -0
- package/src/zexus/access_control_system/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/access_control_system/__pycache__/access_control.cpython-312.pyc +0 -0
- package/src/zexus/advanced_types.py +17 -2
- package/src/zexus/blockchain/__init__.py +411 -0
- package/src/zexus/blockchain/accelerator.py +1160 -0
- package/src/zexus/blockchain/chain.py +660 -0
- package/src/zexus/blockchain/consensus.py +821 -0
- package/src/zexus/blockchain/contract_vm.py +1019 -0
- package/src/zexus/blockchain/crypto.py +79 -14
- package/src/zexus/blockchain/events.py +526 -0
- package/src/zexus/blockchain/loadtest.py +721 -0
- package/src/zexus/blockchain/monitoring.py +350 -0
- package/src/zexus/blockchain/mpt.py +716 -0
- package/src/zexus/blockchain/multichain.py +951 -0
- package/src/zexus/blockchain/multiprocess_executor.py +338 -0
- package/src/zexus/blockchain/network.py +886 -0
- package/src/zexus/blockchain/node.py +666 -0
- package/src/zexus/blockchain/rpc.py +1203 -0
- package/src/zexus/blockchain/rust_bridge.py +421 -0
- package/src/zexus/blockchain/storage.py +423 -0
- package/src/zexus/blockchain/tokens.py +750 -0
- package/src/zexus/blockchain/upgradeable.py +1004 -0
- package/src/zexus/blockchain/verification.py +1602 -0
- package/src/zexus/blockchain/wallet.py +621 -0
- package/src/zexus/capability_system.py +184 -9
- package/src/zexus/cli/__pycache__/main.cpython-312.pyc +0 -0
- package/src/zexus/cli/main.py +383 -34
- package/src/zexus/cli/zpm.py +1 -1
- package/src/zexus/compiler/__pycache__/bytecode.cpython-312.pyc +0 -0
- package/src/zexus/compiler/__pycache__/lexer.cpython-312.pyc +0 -0
- package/src/zexus/compiler/__pycache__/parser.cpython-312.pyc +0 -0
- package/src/zexus/compiler/__pycache__/semantic.cpython-312.pyc +0 -0
- package/src/zexus/compiler/__pycache__/zexus_ast.cpython-312.pyc +0 -0
- package/src/zexus/compiler/bytecode.py +124 -7
- package/src/zexus/compiler/compat_runtime.py +6 -2
- package/src/zexus/compiler/lexer.py +16 -5
- package/src/zexus/compiler/parser.py +108 -7
- package/src/zexus/compiler/semantic.py +18 -19
- package/src/zexus/compiler/zexus_ast.py +26 -1
- package/src/zexus/concurrency_system.py +79 -0
- package/src/zexus/config.py +54 -0
- package/src/zexus/crypto_bridge.py +244 -8
- package/src/zexus/dap/__init__.py +10 -0
- package/src/zexus/dap/__main__.py +4 -0
- package/src/zexus/dap/dap_server.py +391 -0
- package/src/zexus/dap/debug_engine.py +298 -0
- package/src/zexus/environment.py +112 -9
- package/src/zexus/evaluator/__pycache__/bytecode_compiler.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/core.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/expressions.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/functions.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/resource_limiter.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/statements.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/unified_execution.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/utils.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/bytecode_compiler.py +457 -37
- package/src/zexus/evaluator/core.py +644 -50
- package/src/zexus/evaluator/expressions.py +358 -62
- package/src/zexus/evaluator/functions.py +458 -20
- package/src/zexus/evaluator/resource_limiter.py +4 -4
- package/src/zexus/evaluator/statements.py +774 -122
- package/src/zexus/evaluator/unified_execution.py +573 -72
- package/src/zexus/evaluator/utils.py +14 -2
- package/src/zexus/evaluator_original.py +1 -1
- package/src/zexus/event_loop.py +186 -0
- package/src/zexus/lexer.py +742 -458
- package/src/zexus/lsp/__init__.py +1 -1
- package/src/zexus/lsp/definition_provider.py +163 -9
- package/src/zexus/lsp/server.py +22 -8
- package/src/zexus/lsp/symbol_provider.py +182 -9
- package/src/zexus/module_cache.py +239 -9
- package/src/zexus/module_manager.py +129 -1
- package/src/zexus/object.py +76 -6
- package/src/zexus/parser/__pycache__/parser.cpython-312.pyc +0 -0
- package/src/zexus/parser/__pycache__/strategy_context.cpython-312.pyc +0 -0
- package/src/zexus/parser/__pycache__/strategy_structural.cpython-312.pyc +0 -0
- package/src/zexus/parser/parser.py +1349 -408
- package/src/zexus/parser/strategy_context.py +755 -58
- package/src/zexus/parser/strategy_structural.py +121 -21
- package/src/zexus/persistence.py +15 -1
- package/src/zexus/renderer/__init__.py +61 -0
- package/src/zexus/renderer/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/backend.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/canvas.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/color_system.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/layout.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/main_renderer.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/painter.cpython-312.pyc +0 -0
- package/src/zexus/renderer/backend.py +261 -0
- package/src/zexus/renderer/canvas.py +78 -0
- package/src/zexus/renderer/color_system.py +201 -0
- package/src/zexus/renderer/graphics.py +31 -0
- package/src/zexus/renderer/layout.py +222 -0
- package/src/zexus/renderer/main_renderer.py +66 -0
- package/src/zexus/renderer/painter.py +30 -0
- package/src/zexus/renderer/tk_backend.py +208 -0
- package/src/zexus/renderer/web_backend.py +260 -0
- package/src/zexus/runtime/__init__.py +10 -2
- package/src/zexus/runtime/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/runtime/__pycache__/async_runtime.cpython-312.pyc +0 -0
- package/src/zexus/runtime/__pycache__/load_manager.cpython-312.pyc +0 -0
- package/src/zexus/runtime/file_flags.py +137 -0
- package/src/zexus/runtime/load_manager.py +368 -0
- package/src/zexus/safety/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/safety/__pycache__/memory_safety.cpython-312.pyc +0 -0
- package/src/zexus/security.py +424 -34
- package/src/zexus/stdlib/fs.py +23 -18
- package/src/zexus/stdlib/http.py +289 -186
- package/src/zexus/stdlib/sockets.py +207 -163
- package/src/zexus/stdlib/websockets.py +282 -0
- package/src/zexus/stdlib_integration.py +369 -2
- package/src/zexus/strategy_recovery.py +6 -3
- package/src/zexus/type_checker.py +423 -0
- package/src/zexus/virtual_filesystem.py +189 -2
- package/src/zexus/vm/__init__.py +113 -3
- package/src/zexus/vm/__pycache__/async_optimizer.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/bytecode.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/bytecode_converter.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/cache.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/compiler.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/gas_metering.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/jit.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/parallel_vm.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/vm.cpython-312.pyc +0 -0
- package/src/zexus/vm/async_optimizer.py +80 -6
- package/src/zexus/vm/binary_bytecode.py +659 -0
- package/src/zexus/vm/bytecode.py +59 -11
- package/src/zexus/vm/bytecode_converter.py +26 -12
- package/src/zexus/vm/cabi.c +1985 -0
- package/src/zexus/vm/cabi.cpython-312-x86_64-linux-gnu.so +0 -0
- package/src/zexus/vm/cabi.h +127 -0
- package/src/zexus/vm/cache.py +561 -17
- package/src/zexus/vm/compiler.py +818 -51
- package/src/zexus/vm/fastops.c +15743 -0
- package/src/zexus/vm/fastops.cpython-312-x86_64-linux-gnu.so +0 -0
- package/src/zexus/vm/fastops.pyx +288 -0
- package/src/zexus/vm/gas_metering.py +50 -9
- package/src/zexus/vm/jit.py +364 -20
- package/src/zexus/vm/native_jit_backend.py +1816 -0
- package/src/zexus/vm/native_runtime.cpp +1388 -0
- package/src/zexus/vm/native_runtime.cpython-312-x86_64-linux-gnu.so +0 -0
- package/src/zexus/vm/optimizer.py +161 -11
- package/src/zexus/vm/parallel_vm.py +140 -45
- package/src/zexus/vm/peephole_optimizer.py +82 -4
- package/src/zexus/vm/profiler.py +38 -18
- package/src/zexus/vm/register_allocator.py +16 -5
- package/src/zexus/vm/register_vm.py +8 -5
- package/src/zexus/vm/vm.py +3581 -531
- package/src/zexus/vm/wasm_compiler.py +658 -0
- package/src/zexus/zexus_ast.py +137 -11
- package/src/zexus/zexus_token.py +16 -5
- package/src/zexus/zpm/installer.py +55 -15
- package/src/zexus/zpm/package_manager.py +1 -1
- package/src/zexus/zpm/registry.py +257 -28
- package/src/zexus.egg-info/PKG-INFO +16 -6
- package/src/zexus.egg-info/SOURCES.txt +129 -17
- package/src/zexus.egg-info/entry_points.txt +1 -0
- package/src/zexus.egg-info/requires.txt +4 -0
package/src/zexus/security.py
CHANGED
|
@@ -13,6 +13,8 @@ import uuid
|
|
|
13
13
|
import sqlite3
|
|
14
14
|
import time
|
|
15
15
|
import hashlib
|
|
16
|
+
import threading
|
|
17
|
+
from zexus.config import config as zexus_config
|
|
16
18
|
|
|
17
19
|
# Try importing advanced database drivers
|
|
18
20
|
try:
|
|
@@ -27,16 +29,49 @@ try:
|
|
|
27
29
|
except ImportError:
|
|
28
30
|
_ROCKSDB_AVAILABLE = False
|
|
29
31
|
|
|
30
|
-
from .object import (
|
|
32
|
+
from zexus.object import (
|
|
31
33
|
Environment, Map, String, Integer, Float, Boolean as BooleanObj,
|
|
32
34
|
Builtin, List, Null, EvaluationError as ObjectEvaluationError, NULL
|
|
33
35
|
)
|
|
34
36
|
|
|
35
37
|
try:
|
|
36
|
-
from .object import ContractReference
|
|
38
|
+
from zexus.object import ContractReference
|
|
37
39
|
except ImportError: # Fallback if optional type missing
|
|
38
40
|
ContractReference = None
|
|
39
41
|
|
|
42
|
+
# =============================================
|
|
43
|
+
# Shared Evaluator Cache for Performance
|
|
44
|
+
# =============================================
|
|
45
|
+
# Creating a new Evaluator() for every contract method call is expensive
|
|
46
|
+
# This cache provides thread-local evaluators that can be reused
|
|
47
|
+
|
|
48
|
+
_evaluator_cache = threading.local()
|
|
49
|
+
_vm_action_context = threading.local()
|
|
50
|
+
|
|
51
|
+
def _get_vm_action_context() -> bool:
|
|
52
|
+
return bool(getattr(_vm_action_context, 'disable_vm', False))
|
|
53
|
+
|
|
54
|
+
def _set_vm_action_context(flag: bool) -> None:
|
|
55
|
+
_vm_action_context.disable_vm = bool(flag)
|
|
56
|
+
|
|
57
|
+
def _get_cached_evaluator():
|
|
58
|
+
"""Get a cached evaluator instance for the current thread.
|
|
59
|
+
|
|
60
|
+
This significantly improves performance for contract method calls
|
|
61
|
+
by avoiding repeated Evaluator() initialization overhead.
|
|
62
|
+
"""
|
|
63
|
+
if not hasattr(_evaluator_cache, 'evaluator'):
|
|
64
|
+
from zexus.evaluator.core import Evaluator
|
|
65
|
+
_evaluator_cache.evaluator = Evaluator()
|
|
66
|
+
return _evaluator_cache.evaluator
|
|
67
|
+
|
|
68
|
+
def _clear_evaluator_cache():
|
|
69
|
+
"""Clear the evaluator cache (useful for testing)."""
|
|
70
|
+
if hasattr(_evaluator_cache, 'evaluator'):
|
|
71
|
+
del _evaluator_cache.evaluator
|
|
72
|
+
|
|
73
|
+
# =============================================
|
|
74
|
+
|
|
40
75
|
# Ensure storage directory exists
|
|
41
76
|
STORAGE_DIR = "chain_data"
|
|
42
77
|
if not os.path.exists(STORAGE_DIR):
|
|
@@ -664,10 +699,8 @@ class EntityInstance:
|
|
|
664
699
|
param_name = str(param)
|
|
665
700
|
method_env.set(param_name, args[i])
|
|
666
701
|
|
|
667
|
-
#
|
|
668
|
-
|
|
669
|
-
from zexus.evaluator.core import Evaluator
|
|
670
|
-
evaluator = Evaluator()
|
|
702
|
+
# Use cached evaluator for performance (avoids repeated Evaluator() initialization)
|
|
703
|
+
evaluator = _get_cached_evaluator()
|
|
671
704
|
|
|
672
705
|
# Execute the method body with stack trace
|
|
673
706
|
result = evaluator.eval_node(method.body, method_env, stack_trace=[])
|
|
@@ -881,6 +914,66 @@ class RocksDBBackend(StorageBackend):
|
|
|
881
914
|
# CONTRACT SYSTEM - Blockchain State & Logic
|
|
882
915
|
# ===============================================
|
|
883
916
|
|
|
917
|
+
class TrackingList(list):
|
|
918
|
+
def __init__(self, owner, iterable=None):
|
|
919
|
+
super().__init__(iterable or [])
|
|
920
|
+
self.owner = owner
|
|
921
|
+
|
|
922
|
+
def __setitem__(self, key, value):
|
|
923
|
+
super().__setitem__(key, value)
|
|
924
|
+
self.owner.mark_dirty(key)
|
|
925
|
+
|
|
926
|
+
def append(self, item):
|
|
927
|
+
idx = len(self)
|
|
928
|
+
super().append(item)
|
|
929
|
+
self.owner.mark_dirty(idx)
|
|
930
|
+
|
|
931
|
+
def extend(self, iterable):
|
|
932
|
+
start_idx = len(self)
|
|
933
|
+
super().extend(iterable)
|
|
934
|
+
end_idx = len(self)
|
|
935
|
+
for i in range(start_idx, end_idx):
|
|
936
|
+
self.owner.mark_dirty(i)
|
|
937
|
+
|
|
938
|
+
class StorageList(List):
|
|
939
|
+
"""List wrapper for persistent storage"""
|
|
940
|
+
__slots__ = ('_dirty_indices', '_var_name', '_orig_elements')
|
|
941
|
+
|
|
942
|
+
def __init__(self, elements, var_name):
|
|
943
|
+
# We wrap elements in TrackingList
|
|
944
|
+
tracked = TrackingList(self, elements)
|
|
945
|
+
super().__init__(tracked)
|
|
946
|
+
self._dirty_indices = set()
|
|
947
|
+
self._var_name = var_name
|
|
948
|
+
|
|
949
|
+
def mark_dirty(self, index):
|
|
950
|
+
self._dirty_indices.add(index)
|
|
951
|
+
|
|
952
|
+
def mark_all_dirty(self):
|
|
953
|
+
# Mark all current indices as dirty
|
|
954
|
+
self._dirty_indices = set(range(len(self.elements)))
|
|
955
|
+
|
|
956
|
+
def mark_clean(self):
|
|
957
|
+
self._dirty_indices.clear()
|
|
958
|
+
|
|
959
|
+
@property
|
|
960
|
+
def dirty_indices(self):
|
|
961
|
+
return self._dirty_indices
|
|
962
|
+
|
|
963
|
+
def set(self, index, value):
|
|
964
|
+
"""Set value at index, mirroring list item assignment."""
|
|
965
|
+
try:
|
|
966
|
+
idx = index.value if hasattr(index, 'value') else int(index)
|
|
967
|
+
except Exception:
|
|
968
|
+
raise ObjectEvaluationError(f"Invalid index for assignment: {index}")
|
|
969
|
+
|
|
970
|
+
if idx < 0 or idx >= len(self.elements):
|
|
971
|
+
raise ObjectEvaluationError(f"Index out of range: {idx}")
|
|
972
|
+
|
|
973
|
+
self.elements[idx] = value
|
|
974
|
+
self.mark_dirty(idx)
|
|
975
|
+
return value
|
|
976
|
+
|
|
884
977
|
class StorageMap(Map):
|
|
885
978
|
"""Map wrapper that tracks dirty entries for persistent storage"""
|
|
886
979
|
|
|
@@ -924,25 +1017,39 @@ class StorageMap(Map):
|
|
|
924
1017
|
return self._var_name
|
|
925
1018
|
|
|
926
1019
|
class ContractStorage:
|
|
927
|
-
"""Persistent storage for contract state with DB selection
|
|
1020
|
+
"""Persistent storage for contract state with DB selection
|
|
1021
|
+
|
|
1022
|
+
Storage Engine Priority:
|
|
1023
|
+
1. Explicit db_type parameter
|
|
1024
|
+
2. ZEXUS_STORAGE_ENGINE environment variable
|
|
1025
|
+
3. Default: 'memory' for maximum performance
|
|
1026
|
+
|
|
1027
|
+
Set ZEXUS_STORAGE_ENGINE=sqlite for persistence in production.
|
|
1028
|
+
"""
|
|
928
1029
|
|
|
929
|
-
def __init__(self, contract_id, db_type=
|
|
1030
|
+
def __init__(self, contract_id, db_type=None):
|
|
930
1031
|
self.transaction_log = []
|
|
1032
|
+
|
|
1033
|
+
# Determine storage type: explicit > env var > default (memory)
|
|
1034
|
+
if db_type is None:
|
|
1035
|
+
db_type = os.environ.get("ZEXUS_STORAGE_ENGINE", "memory")
|
|
931
1036
|
self.db_type = db_type
|
|
932
1037
|
self._map_meta_cache = {}
|
|
933
1038
|
|
|
934
1039
|
# Determine strict path
|
|
935
1040
|
base_path = os.path.join(STORAGE_DIR, f"{contract_id}")
|
|
936
1041
|
|
|
937
|
-
# Initialize Backend
|
|
938
|
-
if db_type == "
|
|
1042
|
+
# Initialize Backend - memory is now first for performance
|
|
1043
|
+
if db_type == "memory":
|
|
1044
|
+
self.backend = InMemoryBackend()
|
|
1045
|
+
elif db_type == "leveldb" and _LEVELDB_AVAILABLE:
|
|
939
1046
|
self.backend = LevelDBBackend(base_path)
|
|
940
1047
|
elif db_type == "rocksdb" and _ROCKSDB_AVAILABLE:
|
|
941
1048
|
self.backend = RocksDBBackend(f"{base_path}.rdb")
|
|
942
1049
|
elif db_type == "sqlite":
|
|
943
1050
|
self.backend = SQLiteBackend(f"{base_path}.sqlite")
|
|
944
1051
|
else:
|
|
945
|
-
|
|
1052
|
+
# Unknown type, fall back to memory
|
|
946
1053
|
self.backend = InMemoryBackend()
|
|
947
1054
|
|
|
948
1055
|
self._cache_enabled = False
|
|
@@ -983,6 +1090,36 @@ class ContractStorage:
|
|
|
983
1090
|
self._action_cache[key] = storage_map
|
|
984
1091
|
self._persistent_cache[key] = storage_map
|
|
985
1092
|
return storage_map
|
|
1093
|
+
|
|
1094
|
+
if isinstance(meta, dict) and meta.get("__kind") == "list":
|
|
1095
|
+
entries = self.backend.scan(self._list_entry_prefix(key))
|
|
1096
|
+
items = {}
|
|
1097
|
+
max_idx = -1
|
|
1098
|
+
prefix_len = len(self._list_entry_prefix(key))
|
|
1099
|
+
|
|
1100
|
+
for entry_key, payload in entries:
|
|
1101
|
+
idx_str = entry_key[prefix_len:]
|
|
1102
|
+
try:
|
|
1103
|
+
idx = int(idx_str)
|
|
1104
|
+
val = self._deserialize_recursive(json.loads(payload))
|
|
1105
|
+
items[idx] = val
|
|
1106
|
+
if idx > max_idx: max_idx = idx
|
|
1107
|
+
except ValueError:
|
|
1108
|
+
continue
|
|
1109
|
+
|
|
1110
|
+
length = meta.get("length", max_idx + 1)
|
|
1111
|
+
final_list = []
|
|
1112
|
+
for i in range(length):
|
|
1113
|
+
# We use NULL for missing items (should not happen in valid state)
|
|
1114
|
+
final_list.append(items.get(i, NULL))
|
|
1115
|
+
|
|
1116
|
+
storage_list = StorageList(final_list, key)
|
|
1117
|
+
storage_list.mark_clean()
|
|
1118
|
+
|
|
1119
|
+
if self._cache_enabled and self._action_cache is not None:
|
|
1120
|
+
self._action_cache[key] = storage_list
|
|
1121
|
+
self._persistent_cache[key] = storage_list
|
|
1122
|
+
return storage_list
|
|
986
1123
|
except json.JSONDecodeError:
|
|
987
1124
|
pass
|
|
988
1125
|
|
|
@@ -1011,6 +1148,19 @@ class ContractStorage:
|
|
|
1011
1148
|
self._store_map(key, storage_map)
|
|
1012
1149
|
return
|
|
1013
1150
|
|
|
1151
|
+
if isinstance(value, List):
|
|
1152
|
+
if isinstance(value, StorageList):
|
|
1153
|
+
storage_list = value
|
|
1154
|
+
else:
|
|
1155
|
+
storage_list = StorageList(value.elements, key)
|
|
1156
|
+
storage_list.mark_all_dirty()
|
|
1157
|
+
|
|
1158
|
+
if self._cache_enabled and self._action_cache is not None:
|
|
1159
|
+
self._action_cache[key] = storage_list
|
|
1160
|
+
self._persistent_cache[key] = storage_list
|
|
1161
|
+
self._store_list(key, storage_list)
|
|
1162
|
+
return
|
|
1163
|
+
|
|
1014
1164
|
serialized = self._serialize(value)
|
|
1015
1165
|
self.backend.set(key, serialized)
|
|
1016
1166
|
if self._cache_enabled and self._action_cache is not None:
|
|
@@ -1102,6 +1252,39 @@ class ContractStorage:
|
|
|
1102
1252
|
if isinstance(map_obj, StorageMap):
|
|
1103
1253
|
map_obj.mark_clean()
|
|
1104
1254
|
|
|
1255
|
+
def _list_entry_prefix(self, storage_key):
|
|
1256
|
+
return f"{storage_key}:L:"
|
|
1257
|
+
|
|
1258
|
+
def _list_entry_key(self, storage_key, index):
|
|
1259
|
+
return f"{self._list_entry_prefix(storage_key)}{index}"
|
|
1260
|
+
|
|
1261
|
+
def _store_list(self, storage_key, list_obj):
|
|
1262
|
+
meta = {"__kind": "list", "version": 1, "length": len(list_obj.elements)}
|
|
1263
|
+
self.backend.set(storage_key, json.dumps(meta))
|
|
1264
|
+
|
|
1265
|
+
is_storage_list = isinstance(list_obj, StorageList)
|
|
1266
|
+
|
|
1267
|
+
if is_storage_list:
|
|
1268
|
+
indices_to_write = list_obj.dirty_indices
|
|
1269
|
+
else:
|
|
1270
|
+
# Full rewrite
|
|
1271
|
+
self._delete_list_entries(storage_key)
|
|
1272
|
+
indices_to_write = range(len(list_obj.elements))
|
|
1273
|
+
|
|
1274
|
+
for i in indices_to_write:
|
|
1275
|
+
if i < len(list_obj.elements):
|
|
1276
|
+
val = list_obj.elements[i]
|
|
1277
|
+
entry_payload = json.dumps(self._serialize_val_recursive(val))
|
|
1278
|
+
entry_key = self._list_entry_key(storage_key, i)
|
|
1279
|
+
self.backend.set(entry_key, entry_payload)
|
|
1280
|
+
|
|
1281
|
+
if is_storage_list:
|
|
1282
|
+
list_obj.mark_clean()
|
|
1283
|
+
|
|
1284
|
+
def _delete_list_entries(self, storage_key):
|
|
1285
|
+
for entry_key, _ in self.backend.scan(self._list_entry_prefix(storage_key)):
|
|
1286
|
+
self.backend.delete(entry_key)
|
|
1287
|
+
|
|
1105
1288
|
summary = {
|
|
1106
1289
|
"dirty": len(dirty_keys),
|
|
1107
1290
|
"deleted": len(deleted_keys)
|
|
@@ -1112,6 +1295,19 @@ class ContractStorage:
|
|
|
1112
1295
|
for entry_key, _ in self.backend.scan(self._map_entry_prefix(storage_key)):
|
|
1113
1296
|
self.backend.delete(entry_key)
|
|
1114
1297
|
|
|
1298
|
+
def _key_to_str(self, key):
|
|
1299
|
+
"""Convert a Zexus object key to a Python string for JSON serialization."""
|
|
1300
|
+
if key.__class__ is String:
|
|
1301
|
+
return key.value
|
|
1302
|
+
if key.__class__ is Integer:
|
|
1303
|
+
return str(key.value)
|
|
1304
|
+
if key.__class__ is Float:
|
|
1305
|
+
return str(key.value)
|
|
1306
|
+
if key.__class__ is BooleanObj:
|
|
1307
|
+
return str(key.value)
|
|
1308
|
+
# Fallback for other types
|
|
1309
|
+
return str(key)
|
|
1310
|
+
|
|
1115
1311
|
def _serialize(self, obj):
|
|
1116
1312
|
"""Convert Zexus Object -> JSON String"""
|
|
1117
1313
|
cls = obj.__class__
|
|
@@ -1134,7 +1330,8 @@ class ContractStorage:
|
|
|
1134
1330
|
serialized_list = [self._serialize_val_recursive(e) for e in obj.elements]
|
|
1135
1331
|
return json.dumps({"type": "list", "val": serialized_list})
|
|
1136
1332
|
if cls is Map or cls is StorageMap:
|
|
1137
|
-
|
|
1333
|
+
# Convert Zexus String keys to Python strings for JSON compatibility
|
|
1334
|
+
serialized_map = {self._key_to_str(k): self._serialize_val_recursive(v) for k, v in obj.pairs.items()}
|
|
1138
1335
|
return json.dumps({"type": "map", "val": serialized_map})
|
|
1139
1336
|
|
|
1140
1337
|
if obj is NULL or obj is None:
|
|
@@ -1161,7 +1358,8 @@ class ContractStorage:
|
|
|
1161
1358
|
if cls is List:
|
|
1162
1359
|
return {"type": "list", "val": [self._serialize_val_recursive(e) for e in obj.elements]}
|
|
1163
1360
|
if cls is Map or cls is StorageMap:
|
|
1164
|
-
|
|
1361
|
+
# Convert Zexus String keys to Python strings for JSON compatibility
|
|
1362
|
+
return {"type": "map", "val": {self._key_to_str(k): self._serialize_val_recursive(v) for k, v in obj.pairs.items()}}
|
|
1165
1363
|
if obj is NULL:
|
|
1166
1364
|
return {"type": "null", "val": None}
|
|
1167
1365
|
|
|
@@ -1253,6 +1451,66 @@ def lookup_contract(address):
|
|
|
1253
1451
|
class SmartContract:
|
|
1254
1452
|
"""Represents a smart contract with persistent storage"""
|
|
1255
1453
|
|
|
1454
|
+
@staticmethod
|
|
1455
|
+
def _clone_storage_value(value, var_name=None):
|
|
1456
|
+
"""Deep clone storage values to keep instances isolated."""
|
|
1457
|
+
from zexus.object import (
|
|
1458
|
+
Integer as ZInteger,
|
|
1459
|
+
Float as ZFloat,
|
|
1460
|
+
Boolean as ZBoolean,
|
|
1461
|
+
String as ZString,
|
|
1462
|
+
List as ZList,
|
|
1463
|
+
Map as ZMap,
|
|
1464
|
+
)
|
|
1465
|
+
|
|
1466
|
+
if isinstance(value, StorageList):
|
|
1467
|
+
target_name = var_name or getattr(value, "_var_name", "")
|
|
1468
|
+
cloned_elements = [SmartContract._clone_storage_value(item) for item in value.elements]
|
|
1469
|
+
cloned_list = StorageList(cloned_elements, target_name)
|
|
1470
|
+
cloned_list.mark_clean()
|
|
1471
|
+
return cloned_list
|
|
1472
|
+
|
|
1473
|
+
if isinstance(value, ZList):
|
|
1474
|
+
return ZList([SmartContract._clone_storage_value(item) for item in value.elements])
|
|
1475
|
+
|
|
1476
|
+
if isinstance(value, StorageMap):
|
|
1477
|
+
target_name = var_name or getattr(value, "_var_name", "")
|
|
1478
|
+
cloned_pairs = {}
|
|
1479
|
+
for key, stored_val in value.pairs.items():
|
|
1480
|
+
cloned_pairs[key] = SmartContract._clone_storage_value(stored_val)
|
|
1481
|
+
cloned_map = StorageMap(cloned_pairs, target_name)
|
|
1482
|
+
cloned_map.mark_clean()
|
|
1483
|
+
return cloned_map
|
|
1484
|
+
|
|
1485
|
+
if isinstance(value, ZMap):
|
|
1486
|
+
cloned_pairs = {}
|
|
1487
|
+
for key, stored_val in value.pairs.items():
|
|
1488
|
+
cloned_pairs[key] = SmartContract._clone_storage_value(stored_val)
|
|
1489
|
+
return ZMap(cloned_pairs)
|
|
1490
|
+
|
|
1491
|
+
if isinstance(value, ZString):
|
|
1492
|
+
return ZString(value.value, sanitized_for=value.sanitized_for, is_trusted=value.is_trusted)
|
|
1493
|
+
|
|
1494
|
+
if isinstance(value, ZInteger):
|
|
1495
|
+
return ZInteger(value.value)
|
|
1496
|
+
|
|
1497
|
+
if isinstance(value, ZFloat):
|
|
1498
|
+
return ZFloat(value.value)
|
|
1499
|
+
|
|
1500
|
+
if isinstance(value, ZBoolean):
|
|
1501
|
+
return ZBoolean(value.value)
|
|
1502
|
+
|
|
1503
|
+
if isinstance(value, list):
|
|
1504
|
+
return [SmartContract._clone_storage_value(item) for item in value]
|
|
1505
|
+
|
|
1506
|
+
if isinstance(value, tuple):
|
|
1507
|
+
return tuple(SmartContract._clone_storage_value(item) for item in value)
|
|
1508
|
+
|
|
1509
|
+
if isinstance(value, dict):
|
|
1510
|
+
return {key: SmartContract._clone_storage_value(val) for key, val in value.items()}
|
|
1511
|
+
|
|
1512
|
+
return value
|
|
1513
|
+
|
|
1256
1514
|
def __init__(self, name, storage_vars, actions, blockchain_config=None, address=None):
|
|
1257
1515
|
self.name = name
|
|
1258
1516
|
self.storage_vars = storage_vars or []
|
|
@@ -1262,8 +1520,10 @@ class SmartContract:
|
|
|
1262
1520
|
# Generate a unique address/ID for this specific instance if not provided
|
|
1263
1521
|
self.address = address or str(uuid.uuid4())[:8]
|
|
1264
1522
|
|
|
1265
|
-
#
|
|
1266
|
-
db_pref = (blockchain_config or {}).get("storage_engine"
|
|
1523
|
+
# Storage engine priority: blockchain_config > env var > default (memory)
|
|
1524
|
+
db_pref = (blockchain_config or {}).get("storage_engine")
|
|
1525
|
+
if db_pref is None:
|
|
1526
|
+
db_pref = os.environ.get("ZEXUS_STORAGE_ENGINE", "memory")
|
|
1267
1527
|
|
|
1268
1528
|
# Initialize storage linked to unique address
|
|
1269
1529
|
# The unique ID ensures multiple "ZiverWallet()" calls don't overwrite each other
|
|
@@ -1287,7 +1547,8 @@ class SmartContract:
|
|
|
1287
1547
|
|
|
1288
1548
|
def instantiate(self, args=None):
|
|
1289
1549
|
"""Create a new instance of this contract when called like ZiverWallet()."""
|
|
1290
|
-
|
|
1550
|
+
if zexus_config.should_log('debug'):
|
|
1551
|
+
print(f"📄 SmartContract.instantiate() called for: {self.name}")
|
|
1291
1552
|
|
|
1292
1553
|
# Generate new unique address for the instance
|
|
1293
1554
|
new_address = str(uuid.uuid4())[:16]
|
|
@@ -1301,7 +1562,8 @@ class SmartContract:
|
|
|
1301
1562
|
address=new_address
|
|
1302
1563
|
)
|
|
1303
1564
|
|
|
1304
|
-
|
|
1565
|
+
if zexus_config.should_log('debug'):
|
|
1566
|
+
print(f" 🔗 Contract Address: {new_address}")
|
|
1305
1567
|
|
|
1306
1568
|
# Copy initial storage values from the template contract
|
|
1307
1569
|
# This ensures instances get the evaluated initial values
|
|
@@ -1317,13 +1579,12 @@ class SmartContract:
|
|
|
1317
1579
|
# Get the initial value from the template contract's storage
|
|
1318
1580
|
value = self.storage.get(var_name)
|
|
1319
1581
|
if value is not None:
|
|
1320
|
-
initial_storage[var_name] = value
|
|
1582
|
+
initial_storage[var_name] = self._clone_storage_value(value, var_name=var_name)
|
|
1321
1583
|
|
|
1322
1584
|
# Deploy the instance with the copied initial values
|
|
1323
1585
|
instance.deploy(evaluated_storage_values=initial_storage)
|
|
1324
1586
|
instance.parent_contract = self
|
|
1325
1587
|
|
|
1326
|
-
print(f" Available actions: {list(self.actions.keys())}")
|
|
1327
1588
|
return instance
|
|
1328
1589
|
|
|
1329
1590
|
def __call__(self, *args):
|
|
@@ -1343,7 +1604,8 @@ class SmartContract:
|
|
|
1343
1604
|
if evaluated_storage_values:
|
|
1344
1605
|
for var_name, value in evaluated_storage_values.items():
|
|
1345
1606
|
if self.storage.get(var_name) is None:
|
|
1346
|
-
self.
|
|
1607
|
+
cloned_value = self._clone_storage_value(value, var_name=var_name)
|
|
1608
|
+
self.storage.set(var_name, cloned_value)
|
|
1347
1609
|
else:
|
|
1348
1610
|
# Fallback: Initialize storage with NULL for declared variables
|
|
1349
1611
|
for var_node in self.storage_vars:
|
|
@@ -1366,24 +1628,50 @@ class SmartContract:
|
|
|
1366
1628
|
from zexus.object import EvaluationError
|
|
1367
1629
|
return EvaluationError(f"Action '{action_name}' not found in contract {self.name}")
|
|
1368
1630
|
|
|
1631
|
+
trace_calls = os.environ.get("ZEXUS_VM_TRACE_CALLS")
|
|
1632
|
+
if trace_calls:
|
|
1633
|
+
try:
|
|
1634
|
+
interval = int(trace_calls) if trace_calls.isdigit() else 1000
|
|
1635
|
+
except Exception:
|
|
1636
|
+
interval = 1000
|
|
1637
|
+
count = getattr(self, "_trace_call_count", 0) + 1
|
|
1638
|
+
self._trace_call_count = count
|
|
1639
|
+
if interval > 0 and count % interval == 0:
|
|
1640
|
+
print(f"[VM TRACE] CONTRACT action={action_name} total={count}")
|
|
1641
|
+
|
|
1369
1642
|
# Get the action (Action object)
|
|
1370
1643
|
action = self.actions[action_name]
|
|
1371
1644
|
|
|
1372
|
-
#
|
|
1645
|
+
# FAST PATH: Cache environments per action for reuse (big perf win)
|
|
1646
|
+
if not hasattr(self, '_action_env_cache'):
|
|
1647
|
+
self._action_env_cache = {}
|
|
1648
|
+
|
|
1373
1649
|
from zexus.environment import Environment
|
|
1374
|
-
|
|
1650
|
+
cached_env = self._action_env_cache.get(action_name)
|
|
1651
|
+
if cached_env is not None:
|
|
1652
|
+
action_env = cached_env
|
|
1653
|
+
# Clear only local store, keep outer linkage intact
|
|
1654
|
+
action_env.store.clear()
|
|
1655
|
+
else:
|
|
1656
|
+
action_env = Environment(outer=action.env if hasattr(action, 'env') else None)
|
|
1657
|
+
self._action_env_cache[action_name] = action_env
|
|
1658
|
+
|
|
1375
1659
|
|
|
1376
1660
|
# Bind 'this' to the current contract instance in the action environment
|
|
1377
1661
|
action_env.set('this', self)
|
|
1378
1662
|
|
|
1379
|
-
#
|
|
1380
|
-
# In a real blockchain, this would be the transaction sender's address
|
|
1663
|
+
# FAST PATH: Reuse msg object
|
|
1381
1664
|
msg_sender = getattr(self, '_current_sender', self.deployer if hasattr(self, 'deployer') else "0x0000000000000000")
|
|
1665
|
+
cached_msg = getattr(self, '_cached_msg_obj', None)
|
|
1382
1666
|
from zexus.object import String as ZexusString, Map as ZexusMap
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1667
|
+
if cached_msg is not None and getattr(self, '_cached_msg_sender', None) == msg_sender:
|
|
1668
|
+
msg_obj = cached_msg
|
|
1669
|
+
else:
|
|
1670
|
+
msg_obj = ZexusMap({
|
|
1671
|
+
ZexusString("sender"): ZexusString(msg_sender)
|
|
1672
|
+
})
|
|
1673
|
+
self._cached_msg_obj = msg_obj
|
|
1674
|
+
self._cached_msg_sender = msg_sender
|
|
1387
1675
|
action_env.set('msg', msg_obj)
|
|
1388
1676
|
|
|
1389
1677
|
# Make contract storage accessible in the action environment
|
|
@@ -1401,6 +1689,9 @@ class SmartContract:
|
|
|
1401
1689
|
stored_value = self.storage.get(var_name)
|
|
1402
1690
|
if stored_value is not None:
|
|
1403
1691
|
action_env.set(var_name, stored_value)
|
|
1692
|
+
else:
|
|
1693
|
+
# Ensure state variables always exist in the action env
|
|
1694
|
+
action_env.set(var_name, Null)
|
|
1404
1695
|
|
|
1405
1696
|
# Bind action parameters to arguments
|
|
1406
1697
|
if hasattr(action, 'parameters'):
|
|
@@ -1421,16 +1712,105 @@ class SmartContract:
|
|
|
1421
1712
|
from zexus.object import Null
|
|
1422
1713
|
action_env.set(param_name, Null)
|
|
1423
1714
|
|
|
1424
|
-
# Execute the action body
|
|
1425
|
-
|
|
1426
|
-
|
|
1715
|
+
# Execute the action body - use cached evaluator for performance
|
|
1716
|
+
evaluator = _get_cached_evaluator()
|
|
1717
|
+
disable_vm = _get_vm_action_context()
|
|
1718
|
+
saved_use_vm = None
|
|
1719
|
+
saved_vm_enabled = None
|
|
1720
|
+
if disable_vm:
|
|
1721
|
+
saved_use_vm = evaluator.use_vm
|
|
1722
|
+
evaluator.use_vm = False
|
|
1723
|
+
if evaluator.unified_executor is not None:
|
|
1724
|
+
saved_vm_enabled = evaluator.unified_executor.vm_enabled
|
|
1725
|
+
evaluator.unified_executor.vm_enabled = False
|
|
1726
|
+
|
|
1727
|
+
# Optional: VM cached action execution for performance mode
|
|
1728
|
+
vm_result = None
|
|
1729
|
+
use_vm_actions = False
|
|
1730
|
+
try:
|
|
1731
|
+
if evaluator.unified_executor and evaluator.unified_executor.vm_enabled:
|
|
1732
|
+
if evaluator.unified_executor.vm_config.get("vm_action_cache", False):
|
|
1733
|
+
use_vm_actions = True
|
|
1734
|
+
else:
|
|
1735
|
+
perf_mode_flag = action_env.get("performance_mode")
|
|
1736
|
+
perf_mode = getattr(perf_mode_flag, "value", perf_mode_flag)
|
|
1737
|
+
config_obj = action_env.get("config")
|
|
1738
|
+
perf_use_vm_actions = None
|
|
1739
|
+
if isinstance(config_obj, Map):
|
|
1740
|
+
key = String("perf_use_vm_actions")
|
|
1741
|
+
if key in config_obj.pairs:
|
|
1742
|
+
perf_use_vm_actions = config_obj.pairs.get(key)
|
|
1743
|
+
elif "perf_use_vm_actions" in config_obj.pairs:
|
|
1744
|
+
perf_use_vm_actions = config_obj.pairs.get("perf_use_vm_actions")
|
|
1745
|
+
if isinstance(perf_use_vm_actions, BooleanObj):
|
|
1746
|
+
perf_use_vm_actions = perf_use_vm_actions.value
|
|
1747
|
+
if perf_use_vm_actions is True and perf_mode is True:
|
|
1748
|
+
use_vm_actions = True
|
|
1749
|
+
except Exception:
|
|
1750
|
+
use_vm_actions = False
|
|
1751
|
+
|
|
1752
|
+
if disable_vm:
|
|
1753
|
+
use_vm_actions = False
|
|
1427
1754
|
|
|
1428
1755
|
# Start batching storage writes for performance
|
|
1429
1756
|
self.storage.begin_batch()
|
|
1430
1757
|
self.storage.enable_action_cache()
|
|
1431
1758
|
|
|
1432
1759
|
try:
|
|
1433
|
-
|
|
1760
|
+
if use_vm_actions:
|
|
1761
|
+
try:
|
|
1762
|
+
from zexus.evaluator.bytecode_compiler import EvaluatorBytecodeCompiler, should_use_vm_for_node
|
|
1763
|
+
from zexus.evaluator.utils import is_error
|
|
1764
|
+
|
|
1765
|
+
if should_use_vm_for_node(action.body):
|
|
1766
|
+
cache = getattr(self, "_vm_action_cache", None)
|
|
1767
|
+
if cache is None:
|
|
1768
|
+
cache = {}
|
|
1769
|
+
self._vm_action_cache = cache
|
|
1770
|
+
|
|
1771
|
+
bytecode = cache.get(action_name)
|
|
1772
|
+
if bytecode is None:
|
|
1773
|
+
compiler = EvaluatorBytecodeCompiler(use_cache=True)
|
|
1774
|
+
bytecode = compiler.compile(action.body, optimize=True)
|
|
1775
|
+
if bytecode and not compiler.errors:
|
|
1776
|
+
cache[action_name] = bytecode
|
|
1777
|
+
sync_cache = getattr(self, "_vm_action_sync_keys", None)
|
|
1778
|
+
if sync_cache is None:
|
|
1779
|
+
sync_cache = {}
|
|
1780
|
+
self._vm_action_sync_keys = sync_cache
|
|
1781
|
+
try:
|
|
1782
|
+
sync_cache[action_name] = evaluator.unified_executor._collect_assigned_names(action.body)
|
|
1783
|
+
except Exception:
|
|
1784
|
+
sync_cache[action_name] = None
|
|
1785
|
+
|
|
1786
|
+
if bytecode is not None:
|
|
1787
|
+
instr_count = 0
|
|
1788
|
+
try:
|
|
1789
|
+
instr_count = len(getattr(bytecode, "instructions", []) or [])
|
|
1790
|
+
except Exception:
|
|
1791
|
+
instr_count = 0
|
|
1792
|
+
min_instr = 32
|
|
1793
|
+
try:
|
|
1794
|
+
min_instr = int(evaluator.unified_executor.vm_config.get("vm_action_min_instructions", 32))
|
|
1795
|
+
except Exception:
|
|
1796
|
+
min_instr = 32
|
|
1797
|
+
|
|
1798
|
+
if instr_count >= min_instr:
|
|
1799
|
+
sync_keys = None
|
|
1800
|
+
try:
|
|
1801
|
+
sync_keys = self._vm_action_sync_keys.get(action_name)
|
|
1802
|
+
except Exception:
|
|
1803
|
+
sync_keys = None
|
|
1804
|
+
vm_result = evaluator.unified_executor.execute_action_bytecode(bytecode, action_env, sync_keys=sync_keys)
|
|
1805
|
+
if is_error(vm_result):
|
|
1806
|
+
vm_result = None
|
|
1807
|
+
except Exception:
|
|
1808
|
+
vm_result = None
|
|
1809
|
+
|
|
1810
|
+
if vm_result is not None:
|
|
1811
|
+
result = vm_result
|
|
1812
|
+
else:
|
|
1813
|
+
result = evaluator.eval_node(action.body, action_env, stack_trace=[])
|
|
1434
1814
|
|
|
1435
1815
|
# Save any modified state variables back to storage
|
|
1436
1816
|
# SKIP variables that were set via this.property (direct storage updates)
|
|
@@ -1450,6 +1830,13 @@ class SmartContract:
|
|
|
1450
1830
|
continue
|
|
1451
1831
|
|
|
1452
1832
|
current_value = action_env.get(var_name)
|
|
1833
|
+
if var_name == 'chain' and zexus_config.should_log('debug'):
|
|
1834
|
+
size = None
|
|
1835
|
+
try:
|
|
1836
|
+
size = len(current_value.elements)
|
|
1837
|
+
except Exception:
|
|
1838
|
+
size = 'n/a'
|
|
1839
|
+
print(f"DEBUG storage sync chain -> {current_value} (len={size})")
|
|
1453
1840
|
if current_value is not None:
|
|
1454
1841
|
self.storage.set(var_name, current_value)
|
|
1455
1842
|
|
|
@@ -1467,6 +1854,11 @@ class SmartContract:
|
|
|
1467
1854
|
finally:
|
|
1468
1855
|
self.storage.disable_action_cache()
|
|
1469
1856
|
|
|
1857
|
+
if disable_vm:
|
|
1858
|
+
evaluator.use_vm = saved_use_vm
|
|
1859
|
+
if evaluator.unified_executor is not None and saved_vm_enabled is not None:
|
|
1860
|
+
evaluator.unified_executor.vm_enabled = saved_vm_enabled
|
|
1861
|
+
|
|
1470
1862
|
|
|
1471
1863
|
return result
|
|
1472
1864
|
|
|
@@ -1502,12 +1894,10 @@ class SmartContract:
|
|
|
1502
1894
|
attr = getattr(self, property_name)
|
|
1503
1895
|
# Wrap primitive Python types
|
|
1504
1896
|
if isinstance(attr, str):
|
|
1505
|
-
from ..object import String
|
|
1506
1897
|
return String(attr)
|
|
1507
1898
|
return attr
|
|
1508
1899
|
|
|
1509
1900
|
# Return NULL if not found
|
|
1510
|
-
from ..object import NULL
|
|
1511
1901
|
return NULL
|
|
1512
1902
|
|
|
1513
1903
|
def set(self, property_name, value):
|