xian-tech-contracting 1.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- xian_tech_contracting-1.0.1/LICENSE +7 -0
- xian_tech_contracting-1.0.1/PKG-INFO +125 -0
- xian_tech_contracting-1.0.1/README.md +99 -0
- xian_tech_contracting-1.0.1/pyproject.toml +81 -0
- xian_tech_contracting-1.0.1/setup.cfg +4 -0
- xian_tech_contracting-1.0.1/src/contracting/__init__.py +0 -0
- xian_tech_contracting-1.0.1/src/contracting/client.py +389 -0
- xian_tech_contracting-1.0.1/src/contracting/compilation/__init__.py +0 -0
- xian_tech_contracting-1.0.1/src/contracting/compilation/compiler.py +155 -0
- xian_tech_contracting-1.0.1/src/contracting/compilation/linter.py +419 -0
- xian_tech_contracting-1.0.1/src/contracting/compilation/parser.py +55 -0
- xian_tech_contracting-1.0.1/src/contracting/compilation/whitelists.py +145 -0
- xian_tech_contracting-1.0.1/src/contracting/constants.py +54 -0
- xian_tech_contracting-1.0.1/src/contracting/contracts/__init__.py +0 -0
- xian_tech_contracting-1.0.1/src/contracting/contracts/proxythis.py +11 -0
- xian_tech_contracting-1.0.1/src/contracting/contracts/submission.s.py +108 -0
- xian_tech_contracting-1.0.1/src/contracting/contracts/thistest2.py +11 -0
- xian_tech_contracting-1.0.1/src/contracting/contracts/zk_registry.s.py +153 -0
- xian_tech_contracting-1.0.1/src/contracting/execution/__init__.py +0 -0
- xian_tech_contracting-1.0.1/src/contracting/execution/executor.py +235 -0
- xian_tech_contracting-1.0.1/src/contracting/execution/module.py +188 -0
- xian_tech_contracting-1.0.1/src/contracting/execution/native_tracer.py +139 -0
- xian_tech_contracting-1.0.1/src/contracting/execution/python_tracer.py +188 -0
- xian_tech_contracting-1.0.1/src/contracting/execution/runtime.py +428 -0
- xian_tech_contracting-1.0.1/src/contracting/execution/tracer.py +57 -0
- xian_tech_contracting-1.0.1/src/contracting/execution/tracer_common.py +282 -0
- xian_tech_contracting-1.0.1/src/contracting/names.py +26 -0
- xian_tech_contracting-1.0.1/src/contracting/stdlib/__init__.py +0 -0
- xian_tech_contracting-1.0.1/src/contracting/stdlib/bridge/__init__.py +0 -0
- xian_tech_contracting-1.0.1/src/contracting/stdlib/bridge/access.py +41 -0
- xian_tech_contracting-1.0.1/src/contracting/stdlib/bridge/crypto.py +35 -0
- xian_tech_contracting-1.0.1/src/contracting/stdlib/bridge/hashing.py +43 -0
- xian_tech_contracting-1.0.1/src/contracting/stdlib/bridge/imports.py +337 -0
- xian_tech_contracting-1.0.1/src/contracting/stdlib/bridge/orm.py +62 -0
- xian_tech_contracting-1.0.1/src/contracting/stdlib/bridge/random.py +128 -0
- xian_tech_contracting-1.0.1/src/contracting/stdlib/bridge/zk.py +274 -0
- xian_tech_contracting-1.0.1/src/contracting/stdlib/env.py +33 -0
- xian_tech_contracting-1.0.1/src/contracting/storage/__init__.py +0 -0
- xian_tech_contracting-1.0.1/src/contracting/storage/contract.py +115 -0
- xian_tech_contracting-1.0.1/src/contracting/storage/driver.py +390 -0
- xian_tech_contracting-1.0.1/src/contracting/storage/lmdb_store.py +137 -0
- xian_tech_contracting-1.0.1/src/contracting/storage/orm.py +428 -0
- xian_tech_contracting-1.0.1/src/xian_tech_contracting.egg-info/PKG-INFO +125 -0
- xian_tech_contracting-1.0.1/src/xian_tech_contracting.egg-info/SOURCES.txt +45 -0
- xian_tech_contracting-1.0.1/src/xian_tech_contracting.egg-info/dependency_links.txt +1 -0
- xian_tech_contracting-1.0.1/src/xian_tech_contracting.egg-info/requires.txt +11 -0
- xian_tech_contracting-1.0.1/src/xian_tech_contracting.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Creative Commons Attribution‑NonCommercial 4.0 International
|
|
2
|
+
|
|
3
|
+
Copyright © 2025 XIAN.org
|
|
4
|
+
|
|
5
|
+
This work is licensed under CC BY‑NC 4.0.
|
|
6
|
+
You may copy, modify, and share it **for non‑commercial purposes only**, provided that you give appropriate credit.
|
|
7
|
+
Full legal text: https://creativecommons.org/licenses/by-nc/4.0/legalcode
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: xian-tech-contracting
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: Xian Network Python Contracting Engine
|
|
5
|
+
Author-email: Xian Network <info@xian.org>
|
|
6
|
+
License-Expression: GPL-3.0-only
|
|
7
|
+
Project-URL: Repository, https://github.com/xian-technology/xian-contracting
|
|
8
|
+
Keywords: blockchain,xian,contracting,python
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Requires-Python: >=3.12
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Requires-Dist: autopep8<2,>=1.5.7
|
|
17
|
+
Requires-Dist: cachetools<8,>=5
|
|
18
|
+
Requires-Dist: lmdb<2,>=1.6
|
|
19
|
+
Requires-Dist: pynacl<2,>=1.5.0
|
|
20
|
+
Requires-Dist: xian-tech-runtime-types<0.2,>=0.1.0
|
|
21
|
+
Provides-Extra: native
|
|
22
|
+
Requires-Dist: xian-tech-native-tracer<0.2,>=0.1.0; extra == "native"
|
|
23
|
+
Provides-Extra: zk
|
|
24
|
+
Requires-Dist: xian-tech-zk<0.2,>=0.1.0; extra == "zk"
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
# xian-contracting
|
|
28
|
+
|
|
29
|
+
`xian-contracting` is the Python contract runtime for Xian. It owns contract
|
|
30
|
+
compilation, secure execution, storage semantics, metering, and the runtime
|
|
31
|
+
rules that contracts must obey.
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
Install the runtime:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install xian-tech-contracting
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
If you want contract-side zk proof verification, install the optional zk
|
|
42
|
+
backend too:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install 'xian-tech-contracting[zk]'
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The published PyPI package name is `xian-tech-contracting`. The repo name
|
|
49
|
+
remains `xian-contracting`, and the import surface remains `contracting`.
|
|
50
|
+
|
|
51
|
+
Submit and call a contract:
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from contracting.client import ContractingClient
|
|
55
|
+
|
|
56
|
+
client = ContractingClient()
|
|
57
|
+
client.submit(name="con_token", code=contract_source)
|
|
58
|
+
token = client.get_contract("con_token")
|
|
59
|
+
token.transfer(amount=100, to="bob")
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Access storage directly:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from contracting.storage.driver import Driver
|
|
66
|
+
|
|
67
|
+
driver = Driver()
|
|
68
|
+
driver.set("example.key", "value")
|
|
69
|
+
print(driver.get("example.key"))
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Principles
|
|
73
|
+
|
|
74
|
+
- Contracts use Python syntax, but execution rules are consensus-sensitive and
|
|
75
|
+
intentionally narrower than general Python.
|
|
76
|
+
- Metering, storage encoding, import restrictions, and runtime helpers must
|
|
77
|
+
stay version-aligned across validators.
|
|
78
|
+
- Optional native tracing is an implementation detail. The contract model and
|
|
79
|
+
runtime rules should remain understandable without it.
|
|
80
|
+
- Built-in helpers should serve the execution model, not grow into a general
|
|
81
|
+
convenience framework.
|
|
82
|
+
|
|
83
|
+
## Key Directories
|
|
84
|
+
|
|
85
|
+
- `src/contracting/`: runtime, storage, compilation, and stdlib bridge code
|
|
86
|
+
- `packages/`: shared packages such as `xian-runtime-types`, `xian-accounts`,
|
|
87
|
+
the native tracer, and `xian-zk`
|
|
88
|
+
- `tests/`: unit, integration, and security coverage
|
|
89
|
+
- `docs/`: architecture, backlog, and execution notes
|
|
90
|
+
|
|
91
|
+
## What It Covers
|
|
92
|
+
|
|
93
|
+
- compilation and linting
|
|
94
|
+
- runtime execution and metering
|
|
95
|
+
- storage drivers and encoding
|
|
96
|
+
- contract-side runtime helpers
|
|
97
|
+
- optional native tracing backend
|
|
98
|
+
- native zero-knowledge verifier building blocks
|
|
99
|
+
|
|
100
|
+
## Validation
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
uv sync --group dev
|
|
104
|
+
uv run ruff check .
|
|
105
|
+
uv run ruff format --check .
|
|
106
|
+
uv run pytest
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
If you change the zk runtime surface or the native verifier package, run:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
uv sync --group dev --extra zk
|
|
113
|
+
uv run --extra zk pytest -q tests/unit/test_zk_stdlib.py tests/integration/test_zk_bridge.py
|
|
114
|
+
cd packages/xian-zk && uv sync --group dev && uv run maturin develop && uv run pytest -q
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
If you change metering, tracing, storage encoding, or import restrictions, run
|
|
118
|
+
the relevant `tests/security/` and `tests/integration/` paths explicitly too.
|
|
119
|
+
|
|
120
|
+
## Related Docs
|
|
121
|
+
|
|
122
|
+
- [AGENTS.md](AGENTS.md)
|
|
123
|
+
- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)
|
|
124
|
+
- [docs/BACKLOG.md](docs/BACKLOG.md)
|
|
125
|
+
- [docs/README.md](docs/README.md)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# xian-contracting
|
|
2
|
+
|
|
3
|
+
`xian-contracting` is the Python contract runtime for Xian. It owns contract
|
|
4
|
+
compilation, secure execution, storage semantics, metering, and the runtime
|
|
5
|
+
rules that contracts must obey.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
Install the runtime:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install xian-tech-contracting
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
If you want contract-side zk proof verification, install the optional zk
|
|
16
|
+
backend too:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install 'xian-tech-contracting[zk]'
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The published PyPI package name is `xian-tech-contracting`. The repo name
|
|
23
|
+
remains `xian-contracting`, and the import surface remains `contracting`.
|
|
24
|
+
|
|
25
|
+
Submit and call a contract:
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from contracting.client import ContractingClient
|
|
29
|
+
|
|
30
|
+
client = ContractingClient()
|
|
31
|
+
client.submit(name="con_token", code=contract_source)
|
|
32
|
+
token = client.get_contract("con_token")
|
|
33
|
+
token.transfer(amount=100, to="bob")
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Access storage directly:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from contracting.storage.driver import Driver
|
|
40
|
+
|
|
41
|
+
driver = Driver()
|
|
42
|
+
driver.set("example.key", "value")
|
|
43
|
+
print(driver.get("example.key"))
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Principles
|
|
47
|
+
|
|
48
|
+
- Contracts use Python syntax, but execution rules are consensus-sensitive and
|
|
49
|
+
intentionally narrower than general Python.
|
|
50
|
+
- Metering, storage encoding, import restrictions, and runtime helpers must
|
|
51
|
+
stay version-aligned across validators.
|
|
52
|
+
- Optional native tracing is an implementation detail. The contract model and
|
|
53
|
+
runtime rules should remain understandable without it.
|
|
54
|
+
- Built-in helpers should serve the execution model, not grow into a general
|
|
55
|
+
convenience framework.
|
|
56
|
+
|
|
57
|
+
## Key Directories
|
|
58
|
+
|
|
59
|
+
- `src/contracting/`: runtime, storage, compilation, and stdlib bridge code
|
|
60
|
+
- `packages/`: shared packages such as `xian-runtime-types`, `xian-accounts`,
|
|
61
|
+
the native tracer, and `xian-zk`
|
|
62
|
+
- `tests/`: unit, integration, and security coverage
|
|
63
|
+
- `docs/`: architecture, backlog, and execution notes
|
|
64
|
+
|
|
65
|
+
## What It Covers
|
|
66
|
+
|
|
67
|
+
- compilation and linting
|
|
68
|
+
- runtime execution and metering
|
|
69
|
+
- storage drivers and encoding
|
|
70
|
+
- contract-side runtime helpers
|
|
71
|
+
- optional native tracing backend
|
|
72
|
+
- native zero-knowledge verifier building blocks
|
|
73
|
+
|
|
74
|
+
## Validation
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
uv sync --group dev
|
|
78
|
+
uv run ruff check .
|
|
79
|
+
uv run ruff format --check .
|
|
80
|
+
uv run pytest
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
If you change the zk runtime surface or the native verifier package, run:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
uv sync --group dev --extra zk
|
|
87
|
+
uv run --extra zk pytest -q tests/unit/test_zk_stdlib.py tests/integration/test_zk_bridge.py
|
|
88
|
+
cd packages/xian-zk && uv sync --group dev && uv run maturin develop && uv run pytest -q
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
If you change metering, tracing, storage encoding, or import restrictions, run
|
|
92
|
+
the relevant `tests/security/` and `tests/integration/` paths explicitly too.
|
|
93
|
+
|
|
94
|
+
## Related Docs
|
|
95
|
+
|
|
96
|
+
- [AGENTS.md](AGENTS.md)
|
|
97
|
+
- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)
|
|
98
|
+
- [docs/BACKLOG.md](docs/BACKLOG.md)
|
|
99
|
+
- [docs/README.md](docs/README.md)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "xian-tech-contracting"
|
|
7
|
+
version = "1.0.1"
|
|
8
|
+
description = "Xian Network Python Contracting Engine"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "GPL-3.0-only"
|
|
11
|
+
authors = [{ name = "Xian Network", email = "info@xian.org" }]
|
|
12
|
+
requires-python = ">=3.12"
|
|
13
|
+
dependencies = [
|
|
14
|
+
"autopep8>=1.5.7,<2",
|
|
15
|
+
"cachetools>=5,<8",
|
|
16
|
+
"lmdb>=1.6,<2",
|
|
17
|
+
"pynacl>=1.5.0,<2",
|
|
18
|
+
"xian-tech-runtime-types>=0.1.0,<0.2",
|
|
19
|
+
]
|
|
20
|
+
keywords = ["blockchain", "xian", "contracting", "python"]
|
|
21
|
+
classifiers = [
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Programming Language :: Python :: 3.14",
|
|
25
|
+
"Development Status :: 5 - Production/Stable",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Repository = "https://github.com/xian-technology/xian-contracting"
|
|
30
|
+
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
native = [
|
|
33
|
+
"xian-tech-native-tracer>=0.1.0,<0.2",
|
|
34
|
+
]
|
|
35
|
+
zk = [
|
|
36
|
+
"xian-tech-zk>=0.1.0,<0.2",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[dependency-groups]
|
|
40
|
+
dev = [
|
|
41
|
+
"pytest>=9.0.2,<10",
|
|
42
|
+
"pytest-cov>=7.0.0,<8",
|
|
43
|
+
"ruff>=0.15.6,<0.16",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[tool.setuptools]
|
|
47
|
+
package-dir = {"" = "src"}
|
|
48
|
+
include-package-data = true
|
|
49
|
+
|
|
50
|
+
[tool.setuptools.packages.find]
|
|
51
|
+
where = ["src"]
|
|
52
|
+
|
|
53
|
+
[tool.setuptools.package-data]
|
|
54
|
+
contracting = ["contracts/*.s.py"]
|
|
55
|
+
|
|
56
|
+
[tool.pytest.ini_options]
|
|
57
|
+
testpaths = ["tests"]
|
|
58
|
+
|
|
59
|
+
[tool.ruff]
|
|
60
|
+
line-length = 80
|
|
61
|
+
target-version = "py312"
|
|
62
|
+
exclude = [
|
|
63
|
+
"examples",
|
|
64
|
+
"tests/integration/test_contracts",
|
|
65
|
+
"tests",
|
|
66
|
+
"tests/performance/test_contracts",
|
|
67
|
+
"tests/security/contracts",
|
|
68
|
+
"tests/unit/contracts",
|
|
69
|
+
"tests/unit/precompiled",
|
|
70
|
+
"tests/unit/test_sys_contracts",
|
|
71
|
+
"src/contracting/contracts",
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
[tool.ruff.lint]
|
|
75
|
+
select = ["E", "F", "I"]
|
|
76
|
+
ignore = ["E501"]
|
|
77
|
+
|
|
78
|
+
[tool.uv.sources]
|
|
79
|
+
xian-tech-runtime-types = { path = "packages/xian-runtime-types", editable = true }
|
|
80
|
+
xian-tech-native-tracer = { path = "packages/xian-native-tracer", editable = true }
|
|
81
|
+
xian-tech-zk = { path = "packages/xian-zk", editable = true }
|
|
File without changes
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import inspect
|
|
3
|
+
import os
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from functools import partial
|
|
6
|
+
from types import FunctionType
|
|
7
|
+
|
|
8
|
+
import autopep8
|
|
9
|
+
from xian_runtime_types.time import Datetime
|
|
10
|
+
|
|
11
|
+
from contracting.compilation.compiler import ContractingCompiler
|
|
12
|
+
from contracting.execution import runtime
|
|
13
|
+
from contracting.execution.executor import Executor
|
|
14
|
+
from contracting.storage.driver import Driver
|
|
15
|
+
|
|
16
|
+
from . import constants
|
|
17
|
+
from .storage.orm import Hash, Variable
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AbstractContract:
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
name,
|
|
24
|
+
signer,
|
|
25
|
+
environment,
|
|
26
|
+
executor: Executor,
|
|
27
|
+
funcs,
|
|
28
|
+
return_full_output=False,
|
|
29
|
+
):
|
|
30
|
+
self.name = name
|
|
31
|
+
self.signer = signer
|
|
32
|
+
self.environment = environment
|
|
33
|
+
self.executor = executor
|
|
34
|
+
self.functions = funcs
|
|
35
|
+
|
|
36
|
+
# set up virtual functions
|
|
37
|
+
for f in funcs:
|
|
38
|
+
# unpack tuple packed in SenecaClient
|
|
39
|
+
func, kwargs = f
|
|
40
|
+
|
|
41
|
+
# set the kwargs to None. these will fail if they are not provided
|
|
42
|
+
default_kwargs = {}
|
|
43
|
+
for kwarg in kwargs:
|
|
44
|
+
default_kwargs[kwarg] = None
|
|
45
|
+
|
|
46
|
+
# each function is a partial that allows kwarg overloading and overriding
|
|
47
|
+
setattr(
|
|
48
|
+
self,
|
|
49
|
+
func,
|
|
50
|
+
partial(
|
|
51
|
+
self._abstract_function_call,
|
|
52
|
+
signer=self.signer,
|
|
53
|
+
contract_name=self.name,
|
|
54
|
+
executor=self.executor,
|
|
55
|
+
func=func,
|
|
56
|
+
return_full_output=return_full_output,
|
|
57
|
+
# environment=self.environment,
|
|
58
|
+
**default_kwargs,
|
|
59
|
+
),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def keys(self):
|
|
63
|
+
# Scope strictly to this contract's namespace
|
|
64
|
+
return self.executor.driver.keys(f"{self.name}.")
|
|
65
|
+
|
|
66
|
+
# a variable contains a DOT, but no __, and no :
|
|
67
|
+
# a hash contains a DOT, no __, and a :
|
|
68
|
+
# a constant contains __, a DOT, and :
|
|
69
|
+
|
|
70
|
+
def quick_read(self, variable, key=None, args=None):
|
|
71
|
+
a = []
|
|
72
|
+
|
|
73
|
+
if key is not None:
|
|
74
|
+
a.append(key)
|
|
75
|
+
|
|
76
|
+
if args is not None and isinstance(args, list):
|
|
77
|
+
for arg in args:
|
|
78
|
+
a.append(arg)
|
|
79
|
+
|
|
80
|
+
k = self.executor.driver.make_key(
|
|
81
|
+
contract=self.name, variable=variable, args=a
|
|
82
|
+
)
|
|
83
|
+
return self.executor.driver.get(k)
|
|
84
|
+
|
|
85
|
+
def quick_write(self, variable, key=None, value=None, args=None):
|
|
86
|
+
if key is not None:
|
|
87
|
+
a = [key]
|
|
88
|
+
else:
|
|
89
|
+
a = []
|
|
90
|
+
|
|
91
|
+
if args is not None and isinstance(args, list):
|
|
92
|
+
for arg in args:
|
|
93
|
+
a.append(arg)
|
|
94
|
+
|
|
95
|
+
k = self.executor.driver.make_key(
|
|
96
|
+
contract=self.name, variable=variable, args=a
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
self.executor.driver.set(k, value)
|
|
100
|
+
self.executor.driver.commit()
|
|
101
|
+
|
|
102
|
+
def run_private_function(self, f, signer=None, environment=None, **kwargs):
|
|
103
|
+
# Override kwargs if provided
|
|
104
|
+
signer = signer or self.signer
|
|
105
|
+
environment = environment or self.environment
|
|
106
|
+
|
|
107
|
+
# Let executor access private functions
|
|
108
|
+
self.executor.bypass_privates = True
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
# Append private method prefix to function name if it isn't there already
|
|
112
|
+
if not f.startswith(constants.PRIVATE_METHOD_PREFIX):
|
|
113
|
+
f = "{}{}".format(constants.PRIVATE_METHOD_PREFIX, f)
|
|
114
|
+
|
|
115
|
+
return self._abstract_function_call(
|
|
116
|
+
signer=signer,
|
|
117
|
+
executor=self.executor,
|
|
118
|
+
contract_name=self.name,
|
|
119
|
+
environment=environment,
|
|
120
|
+
func=f,
|
|
121
|
+
metering=None,
|
|
122
|
+
now=None,
|
|
123
|
+
**kwargs,
|
|
124
|
+
)
|
|
125
|
+
finally:
|
|
126
|
+
# Always restore restricted mode, even if the private call fails.
|
|
127
|
+
self.executor.bypass_privates = False
|
|
128
|
+
|
|
129
|
+
def __getattr__(self, item):
|
|
130
|
+
try:
|
|
131
|
+
# return the attribute if it exists on the instance
|
|
132
|
+
return self.__getattribute__(item)
|
|
133
|
+
except AttributeError as e:
|
|
134
|
+
# otherwise, attempt to resolve it. full name is contract.item
|
|
135
|
+
fullname = "{}.{}".format(self.name, item)
|
|
136
|
+
|
|
137
|
+
# if the raw name exists, it is a __protected__ or a variable, so prepare for those
|
|
138
|
+
if fullname in self.keys():
|
|
139
|
+
variable = Variable(
|
|
140
|
+
contract=self.name, name=item, driver=self.executor.driver
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# return just the value if it is __protected__ to prevent sets
|
|
144
|
+
if item.startswith("__"):
|
|
145
|
+
return variable.get()
|
|
146
|
+
|
|
147
|
+
# otherwise, return the variable object with allows sets
|
|
148
|
+
return variable
|
|
149
|
+
|
|
150
|
+
# otherwise, see if contract.items: has more than one entry
|
|
151
|
+
if (
|
|
152
|
+
len(
|
|
153
|
+
self.executor.driver.values(
|
|
154
|
+
prefix=self.name + "." + item + ":"
|
|
155
|
+
)
|
|
156
|
+
)
|
|
157
|
+
> 0
|
|
158
|
+
):
|
|
159
|
+
# if so, it is a hash. return the hash object
|
|
160
|
+
return Hash(
|
|
161
|
+
contract=self.name, name=item, driver=self.executor.driver
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# otherwise, the attribut does not exist, so throw the error.
|
|
165
|
+
raise e
|
|
166
|
+
|
|
167
|
+
def now(self):
|
|
168
|
+
d = datetime.today()
|
|
169
|
+
return Datetime(d.year, d.month, d.day, hour=d.hour, minute=d.minute)
|
|
170
|
+
|
|
171
|
+
def _abstract_function_call(
|
|
172
|
+
self,
|
|
173
|
+
signer,
|
|
174
|
+
executor,
|
|
175
|
+
contract_name,
|
|
176
|
+
func,
|
|
177
|
+
environment=None,
|
|
178
|
+
stamps=constants.DEFAULT_STAMPS,
|
|
179
|
+
metering=None,
|
|
180
|
+
now=None,
|
|
181
|
+
return_full_output=False,
|
|
182
|
+
**kwargs,
|
|
183
|
+
):
|
|
184
|
+
|
|
185
|
+
# for k, v in kwargs.items():
|
|
186
|
+
# assert v is not None, 'Keyword "{}" not provided. Must not be None.'.format(k)
|
|
187
|
+
environment = environment or self.environment
|
|
188
|
+
|
|
189
|
+
if now is None:
|
|
190
|
+
now = self.now()
|
|
191
|
+
|
|
192
|
+
if environment.get("now") is None:
|
|
193
|
+
environment.update({"now": now})
|
|
194
|
+
|
|
195
|
+
output = executor.execute(
|
|
196
|
+
sender=signer,
|
|
197
|
+
contract_name=contract_name,
|
|
198
|
+
function_name=func,
|
|
199
|
+
kwargs=kwargs,
|
|
200
|
+
stamps=stamps,
|
|
201
|
+
environment=environment,
|
|
202
|
+
metering=metering,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
if executor.production:
|
|
206
|
+
executor.sandbox.terminate()
|
|
207
|
+
|
|
208
|
+
if output["status_code"] == 1:
|
|
209
|
+
raise output["result"] if not return_full_output else output
|
|
210
|
+
return output["result"] if not return_full_output else output
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class ContractingClient:
|
|
214
|
+
def __init__(
|
|
215
|
+
self,
|
|
216
|
+
signer="sys",
|
|
217
|
+
submission_filename=os.path.join(
|
|
218
|
+
os.path.dirname(__file__), "contracts/submission.s.py"
|
|
219
|
+
),
|
|
220
|
+
storage_home=constants.STORAGE_HOME,
|
|
221
|
+
driver: Driver = None,
|
|
222
|
+
metering=False,
|
|
223
|
+
compiler=ContractingCompiler(),
|
|
224
|
+
environment={},
|
|
225
|
+
tracer_mode: str | None = None,
|
|
226
|
+
):
|
|
227
|
+
if tracer_mode is not None:
|
|
228
|
+
runtime.rt.set_tracer_mode(tracer_mode)
|
|
229
|
+
driver = (
|
|
230
|
+
driver if driver is not None else Driver(storage_home=storage_home)
|
|
231
|
+
)
|
|
232
|
+
self.executor = Executor(metering=metering, driver=driver)
|
|
233
|
+
self.raw_driver = driver
|
|
234
|
+
self.signer = signer
|
|
235
|
+
self.compiler = compiler
|
|
236
|
+
self.submission_filename = submission_filename
|
|
237
|
+
self.environment = environment
|
|
238
|
+
# Get submission contract from file
|
|
239
|
+
if submission_filename is not None:
|
|
240
|
+
# Seed the genesis contracts into the instance
|
|
241
|
+
with open(self.submission_filename) as f:
|
|
242
|
+
contract = f.read()
|
|
243
|
+
|
|
244
|
+
self.raw_driver.set_contract(name="submission", code=contract)
|
|
245
|
+
self.raw_driver.commit()
|
|
246
|
+
|
|
247
|
+
# Get submission contract from state
|
|
248
|
+
self.submission_contract = self.get_contract("submission")
|
|
249
|
+
|
|
250
|
+
def set_submission_contract(self, filename=None, commit=True):
|
|
251
|
+
state_contract = self.get_contract("submission")
|
|
252
|
+
|
|
253
|
+
if filename is None:
|
|
254
|
+
filename = self.submission_filename
|
|
255
|
+
|
|
256
|
+
if filename is None and state_contract is None:
|
|
257
|
+
raise AssertionError(
|
|
258
|
+
"No submission contract provided or found in state."
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
if filename is not None:
|
|
262
|
+
with open(filename) as f:
|
|
263
|
+
contract = f.read()
|
|
264
|
+
|
|
265
|
+
self.raw_driver.delete_contract(name="submission")
|
|
266
|
+
self.raw_driver.set_contract(name="submission", code=contract)
|
|
267
|
+
|
|
268
|
+
if commit:
|
|
269
|
+
self.raw_driver.commit()
|
|
270
|
+
|
|
271
|
+
self.submission_contract = self.get_contract("submission")
|
|
272
|
+
|
|
273
|
+
def flush(self):
|
|
274
|
+
# flushes storage and resubmits genesis contracts
|
|
275
|
+
self.raw_driver.flush_full()
|
|
276
|
+
self.raw_driver.flush_cache()
|
|
277
|
+
|
|
278
|
+
if self.submission_filename is not None:
|
|
279
|
+
self.set_submission_contract()
|
|
280
|
+
|
|
281
|
+
# Returns abstract contract which has partial methods mapped to each exported function.
|
|
282
|
+
def get_contract(self, name):
|
|
283
|
+
contract = self.raw_driver.get_contract(name)
|
|
284
|
+
|
|
285
|
+
if contract is None:
|
|
286
|
+
return None
|
|
287
|
+
|
|
288
|
+
tree = ast.parse(contract)
|
|
289
|
+
|
|
290
|
+
function_defs = [
|
|
291
|
+
n for n in ast.walk(tree) if isinstance(n, ast.FunctionDef)
|
|
292
|
+
]
|
|
293
|
+
|
|
294
|
+
funcs = []
|
|
295
|
+
for definition in function_defs:
|
|
296
|
+
func_name = definition.name
|
|
297
|
+
kwargs = [arg.arg for arg in definition.args.args]
|
|
298
|
+
|
|
299
|
+
funcs.append((func_name, kwargs))
|
|
300
|
+
|
|
301
|
+
return AbstractContract(
|
|
302
|
+
name=name,
|
|
303
|
+
signer=self.signer,
|
|
304
|
+
environment=self.environment,
|
|
305
|
+
executor=self.executor,
|
|
306
|
+
funcs=funcs,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
def closure_to_code_string(self, f):
|
|
310
|
+
closure_code = inspect.getsource(f)
|
|
311
|
+
closure_code = autopep8.fix_code(closure_code)
|
|
312
|
+
closure_tree = ast.parse(closure_code)
|
|
313
|
+
|
|
314
|
+
# Remove the enclosing function by swapping out the function def node with its children
|
|
315
|
+
assert len(closure_tree.body) == 1, "Module has multiple body nodes."
|
|
316
|
+
assert isinstance(closure_tree.body[0], ast.FunctionDef), (
|
|
317
|
+
"Function definition not found at root."
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
func_def_body = closure_tree.body[0]
|
|
321
|
+
closure_tree.body = func_def_body.body
|
|
322
|
+
|
|
323
|
+
contract_code = ast.unparse(closure_tree)
|
|
324
|
+
name = func_def_body.name
|
|
325
|
+
|
|
326
|
+
return contract_code, name
|
|
327
|
+
|
|
328
|
+
def lint(self, f, raise_errors=False):
|
|
329
|
+
if isinstance(f, FunctionType):
|
|
330
|
+
f, _ = self.closure_to_code_string(f)
|
|
331
|
+
|
|
332
|
+
if raise_errors:
|
|
333
|
+
self.compiler.linter.check_raise(f)
|
|
334
|
+
return None
|
|
335
|
+
|
|
336
|
+
return self.compiler.linter.check(f)
|
|
337
|
+
|
|
338
|
+
def compile(self, f):
|
|
339
|
+
if isinstance(f, FunctionType):
|
|
340
|
+
f, _ = self.closure_to_code_string(f)
|
|
341
|
+
|
|
342
|
+
code = self.compiler.parse_to_code(f)
|
|
343
|
+
return code
|
|
344
|
+
|
|
345
|
+
def submit(
|
|
346
|
+
self,
|
|
347
|
+
f,
|
|
348
|
+
name=None,
|
|
349
|
+
metering=None,
|
|
350
|
+
owner=None,
|
|
351
|
+
constructor_args={},
|
|
352
|
+
signer=None,
|
|
353
|
+
):
|
|
354
|
+
|
|
355
|
+
assert self.submission_contract is not None, (
|
|
356
|
+
"No submission contract set. Try set_submission_contract first."
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
if isinstance(f, FunctionType):
|
|
360
|
+
f, n = self.closure_to_code_string(f)
|
|
361
|
+
if name is None:
|
|
362
|
+
name = n
|
|
363
|
+
|
|
364
|
+
assert name is not None, "No name provided."
|
|
365
|
+
|
|
366
|
+
if signer is None:
|
|
367
|
+
signer = self.signer
|
|
368
|
+
|
|
369
|
+
self.submission_contract.submit_contract(
|
|
370
|
+
name=name,
|
|
371
|
+
code=f,
|
|
372
|
+
owner=owner,
|
|
373
|
+
constructor_args=constructor_args,
|
|
374
|
+
metering=metering,
|
|
375
|
+
signer=signer,
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
def get_contracts(self):
|
|
379
|
+
contracts = []
|
|
380
|
+
for key in self.raw_driver.keys():
|
|
381
|
+
if key.endswith(".__code__"):
|
|
382
|
+
contracts.append(key.replace(".__code__", ""))
|
|
383
|
+
return contracts
|
|
384
|
+
|
|
385
|
+
def get_var(self, contract, variable, arguments=[], mark=False):
|
|
386
|
+
return self.raw_driver.get_var(contract, variable, arguments, mark)
|
|
387
|
+
|
|
388
|
+
def set_var(self, contract, variable, arguments=[], value=None, mark=False):
|
|
389
|
+
self.raw_driver.set_var(contract, variable, arguments, value, mark)
|