sgx.py 0.12.dev1__tar.gz → 0.12.dev3__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.
Files changed (25) hide show
  1. {sgx_py-0.12.dev1/sgx.py.egg-info → sgx_py-0.12.dev3}/PKG-INFO +1 -1
  2. sgx_py-0.12.dev3/README.md +112 -0
  3. {sgx_py-0.12.dev1 → sgx_py-0.12.dev3}/setup.py +1 -1
  4. sgx_py-0.12.dev3/sgx/__init__.py +5 -0
  5. sgx_py-0.12.dev3/sgx/certificates.py +134 -0
  6. {sgx_py-0.12.dev1 → sgx_py-0.12.dev3}/sgx/http.py +9 -3
  7. sgx_py-0.12.dev3/sgx/options.py +104 -0
  8. {sgx_py-0.12.dev1 → sgx_py-0.12.dev3}/sgx/sgx.py +13 -2
  9. {sgx_py-0.12.dev1 → sgx_py-0.12.dev3}/sgx/sgx_rpc_handler.py +34 -6
  10. {sgx_py-0.12.dev1 → sgx_py-0.12.dev3/sgx.py.egg-info}/PKG-INFO +1 -1
  11. {sgx_py-0.12.dev1 → sgx_py-0.12.dev3}/sgx.py.egg-info/SOURCES.txt +2 -0
  12. {sgx_py-0.12.dev1 → sgx_py-0.12.dev3}/tests/test_core.py +21 -1
  13. {sgx_py-0.12.dev1 → sgx_py-0.12.dev3}/tests/test_http.py +12 -0
  14. sgx_py-0.12.dev1/README.md +0 -75
  15. sgx_py-0.12.dev1/sgx/__init__.py +0 -2
  16. {sgx_py-0.12.dev1 → sgx_py-0.12.dev3}/LICENSE +0 -0
  17. {sgx_py-0.12.dev1 → sgx_py-0.12.dev3}/setup.cfg +0 -0
  18. {sgx_py-0.12.dev1 → sgx_py-0.12.dev3}/sgx/constants.py +0 -0
  19. {sgx_py-0.12.dev1 → sgx_py-0.12.dev3}/sgx/generate.sh +0 -0
  20. {sgx_py-0.12.dev1 → sgx_py-0.12.dev3}/sgx/sgx_zmq.py +0 -0
  21. {sgx_py-0.12.dev1 → sgx_py-0.12.dev3}/sgx/utils.py +0 -0
  22. {sgx_py-0.12.dev1 → sgx_py-0.12.dev3}/sgx.py.egg-info/dependency_links.txt +0 -0
  23. {sgx_py-0.12.dev1 → sgx_py-0.12.dev3}/sgx.py.egg-info/requires.txt +0 -0
  24. {sgx_py-0.12.dev1 → sgx_py-0.12.dev3}/sgx.py.egg-info/top_level.txt +0 -0
  25. {sgx_py-0.12.dev1 → sgx_py-0.12.dev3}/tests/test_dkg.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sgx.py
3
- Version: 0.12.dev1
3
+ Version: 0.12.dev3
4
4
  Summary: SGX
5
5
  Home-page: http://github.com/skalenetwork/sgx.py
6
6
  Author: SKALE Labs
@@ -0,0 +1,112 @@
1
+ # sgx.py
2
+ [![Discord](https://img.shields.io/discord/534485763354787851.svg)](https://discord.gg/vvUtWJB)
3
+
4
+ ## Requirements
5
+
6
+ |**Tool**|**Minimum Supported Version**|
7
+ |--------|-----------|
8
+ | Python | 3.11 |
9
+
10
+ ---
11
+
12
+ ## Installation
13
+
14
+ ### Install Python 3.11
15
+
16
+ ```bash
17
+ # confirm prerequisites are isntalled
18
+ sudo apt update
19
+ sudo apt install software-properties-common
20
+ # Add deadsnakes PPA - widely used for Python versions
21
+ sudo add-apt-repository ppa:deadsnakes/ppa
22
+ # update again & install python 3.11
23
+ sudo apt update
24
+ sudo apt install python3.11
25
+ # Check if it was installed
26
+ python3.11 --version
27
+
28
+ pip install sgx.py
29
+ ```
30
+ ---
31
+
32
+ ## Developers
33
+ ### Install all python libraries
34
+ ```bash
35
+ sudo apt-get update --fix-missing
36
+ sudo apt-get install libudev-dev
37
+ sudo apt-get install swig
38
+
39
+ # create virtual environment - install dependencies locally
40
+ python3.11 -m venv venv
41
+ source venv/bin/activate
42
+ pip install -e .
43
+ pip install -e .[dev]
44
+ ```
45
+
46
+ ### Running Tests
47
+
48
+ Create a `.env` file in source directory, like the following:
49
+ ```bash
50
+ # SGXWallet listening point (1026 uses https w/ certificates)
51
+ SERVER=https://127.0.0.1:1026
52
+ # path where you want tests to store the auto-generated certificates
53
+ CERT_PATH=.
54
+ # private key for the funded test account used by transaction tests
55
+ ETH_PRIVATE_KEY=
56
+ # Ethereum JSON-RPC endpoint used by transaction tests
57
+ GETH=
58
+ # 1 when sgxwallet runs with -e (ZMQ key ownership)
59
+ SGX_EXPECT_ZMQ_OWNERSHIP=0
60
+ ```
61
+ You may alter the fields as needed.
62
+
63
+ Run the tests:
64
+ ```bash
65
+ # Run all tests
66
+ pytest
67
+
68
+ # Run specific test file
69
+ pytest ./tests/<test-file-name>.py
70
+
71
+ ```
72
+
73
+ ## Checking the wallet
74
+
75
+ Available from sgxwallet 1.11.0. Older servers raise `SgxMethodNotFoundError`, a subclass of `SgxServerError`.
76
+
77
+ ### Client certificate
78
+
79
+ ```python
80
+ from sgx import CertificateMatch, SgxClient
81
+
82
+ client = SgxClient(endpoint, cert_dir, allow_registration=False)
83
+ check = client.check_local_certificate(expected_number=1)
84
+ if check.outcome is not CertificateMatch.MATCH:
85
+ print(check.outcome, check.newer_issued_at)
86
+ ```
87
+
88
+ | `check.outcome` | Meaning |
89
+ |---|---|
90
+ | `MATCH` | `sgx.crt` is the newest certificate the wallet issued, and the wallet issued `expected_number` client certificates |
91
+ | `UNEXPECTED_NUMBER` | `sgx.crt` is the newest, but the number of issued certificates differs |
92
+ | `NOT_LATEST` | the wallet issued a newer certificate, at `check.newer_issued_at` |
93
+ | `NOT_ISSUED` | unknown to the wallet's CA: wrong endpoint, or its CA database was recreated |
94
+
95
+ `check.info` holds both counts and the newest certificate with its status: `V`, `R` or `E`. The check relies on the server verifying client certificates; otherwise `NOT_LATEST` can also mean a certificate from another CA.
96
+
97
+ Unless `allow_registration=False` is passed, any request registers a new certificate when `sgx.crt` or `sgx.key` is missing, and the constructor makes such a request when the directory does not hold exactly three files. `check_local_certificate` never registers.
98
+
99
+ ### Server options
100
+
101
+ ```python
102
+ options = client.get_server_options()
103
+ options.effective.zmq_key_ownership_enforced # -e is in effect; applies to ZMQ only
104
+ ```
105
+
106
+ The server reports these values itself; they are not attested. `options.build` is `None` unless the server verifies the caller's certificate. While sgxwallet starts or stops, the call raises `SgxServerError`; retry.
107
+
108
+ ## License
109
+
110
+ [![License](https://img.shields.io/github/license/skalenetwork/sgx.py.svg)](LICENSE)
111
+
112
+ Copyright (C) 2019-present SKALE Labs
@@ -22,7 +22,7 @@ extras_require['dev'] = extras_require['linter'] + extras_require['dev']
22
22
 
23
23
  setup(
24
24
  name='sgx.py',
25
- version='0.12dev1',
25
+ version='0.12dev3',
26
26
  description='SGX',
27
27
  url='http://github.com/skalenetwork/sgx.py',
28
28
  author='SKALE Labs',
@@ -0,0 +1,5 @@
1
+ # flake8: noqa: F401
2
+ from sgx.certificates import CertificateMatch, IssuedCertificatesInfo, SgxCertificateError
3
+ from sgx.options import LogLevel, ServerOptions, SgxServerOptionsError
4
+ from sgx.sgx import SgxClient
5
+ from sgx.sgx_rpc_handler import SgxMethodNotFoundError
@@ -0,0 +1,134 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # This file is part of sgx.py
4
+ #
5
+ # Copyright (C) 2026 SKALE Labs
6
+ #
7
+ # sgx.py is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Affero General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # sgx.py is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Affero General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Affero General Public License
18
+ # along with sgx.py. If not, see <https://www.gnu.org/licenses/>.
19
+
20
+ import os
21
+ import re
22
+ from dataclasses import dataclass
23
+ from datetime import datetime, timezone
24
+ from enum import Enum
25
+
26
+ from cryptography import x509
27
+ from cryptography.hazmat.primitives import hashes
28
+
29
+ from sgx.constants import CRT_FILENAME
30
+ from sgx.utils import SgxError
31
+
32
+
33
+ class SgxCertificateError(SgxError):
34
+ pass
35
+
36
+
37
+ class CertificateMatch(Enum):
38
+ MATCH = 'match' # the local certificate is the newest issued
39
+ UNEXPECTED_NUMBER = 'unexpected_number' # as MATCH, but the count differs from the expected
40
+ NOT_LATEST = 'not_latest' # a newer certificate was issued after it
41
+ NOT_ISSUED = 'not_issued' # unknown to this wallet's CA database
42
+
43
+
44
+ @dataclass(frozen=True)
45
+ class NewestCertificate:
46
+ serial: int
47
+ sha256: bytes
48
+ not_before: datetime
49
+ not_after: datetime
50
+ status: str # V valid, R revoked, E expired
51
+
52
+
53
+ @dataclass(frozen=True)
54
+ class IssuedCertificatesInfo:
55
+ """Client certificates issued by the wallet's CA, from getIssuedCertificatesInfo."""
56
+
57
+ certificates_number: int
58
+ server_certificates_number: int
59
+ newest_certificate: NewestCertificate | None
60
+
61
+ @classmethod
62
+ def from_result(cls, result):
63
+ """Parses a result in the format sgxwallet documents and rejects anything else."""
64
+ try:
65
+ number = _count(result['certificatesNumber'])
66
+ newest = result['newestCertificate']
67
+ # sgxwallet sends a newest certificate exactly when it has issued any
68
+ if (newest is None) != (number == 0):
69
+ raise ValueError('newestCertificate does not match certificatesNumber')
70
+ if newest is not None:
71
+ newest = NewestCertificate(
72
+ serial=int(_text('[0-9A-F]+', newest['serial']), 16),
73
+ sha256=bytes.fromhex(_text('[0-9a-f]{64}', newest['sha256'])),
74
+ not_before=_utc(newest['notBefore']),
75
+ not_after=_utc(newest['notAfter']),
76
+ status=_text('[VRE]', newest['status']),
77
+ )
78
+ return cls(number, _count(result['serverCertificatesNumber']), newest)
79
+ except (KeyError, TypeError, ValueError) as err:
80
+ raise SgxCertificateError(
81
+ f'Malformed getIssuedCertificatesInfo result: {result!r}'
82
+ ) from err
83
+
84
+ def check(self, certificate, expected_number=None):
85
+ """Compares an x509.Certificate with the newest certificate the wallet issued. If the
86
+ server does not verify client certificates, NOT_LATEST may be another CA's certificate."""
87
+ newest = self.newest_certificate
88
+ if newest is not None and newest.sha256 == certificate.fingerprint(hashes.SHA256()):
89
+ if expected_number is None or expected_number == self.certificates_number:
90
+ outcome = CertificateMatch.MATCH
91
+ else:
92
+ outcome = CertificateMatch.UNEXPECTED_NUMBER
93
+ elif newest is not None and newest.serial > certificate.serial_number:
94
+ outcome = CertificateMatch.NOT_LATEST
95
+ else:
96
+ outcome = CertificateMatch.NOT_ISSUED
97
+ return CertificateCheck(outcome, self)
98
+
99
+
100
+ @dataclass(frozen=True)
101
+ class CertificateCheck:
102
+ outcome: CertificateMatch
103
+ info: IssuedCertificatesInfo
104
+
105
+ @property
106
+ def newer_issued_at(self):
107
+ if self.outcome is CertificateMatch.NOT_LATEST:
108
+ return self.info.newest_certificate.not_before
109
+ return None
110
+
111
+
112
+ def load_local_certificate(cert_dir):
113
+ try:
114
+ with open(os.path.join(cert_dir, CRT_FILENAME), 'rb') as crt_file:
115
+ return x509.load_pem_x509_certificate(crt_file.read())
116
+ except (OSError, TypeError, ValueError) as err: # TypeError: no cert_dir
117
+ raise SgxCertificateError(f'Cannot load {CRT_FILENAME} from {cert_dir!r}') from err
118
+
119
+
120
+ def _count(value):
121
+ # Exact type, because bool is a subclass of int
122
+ if type(value) is not int or value < 0:
123
+ raise ValueError(f'Invalid count {value!r}')
124
+ return value
125
+
126
+
127
+ def _text(pattern, value):
128
+ if not re.fullmatch(pattern, value):
129
+ raise ValueError(f'Invalid value {value!r}')
130
+ return value
131
+
132
+
133
+ def _utc(value):
134
+ return datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=timezone.utc)
@@ -11,6 +11,7 @@ import requests
11
11
  import urllib3
12
12
  from requests.packages.urllib3.exceptions import InsecureRequestWarning
13
13
 
14
+ from sgx.certificates import SgxCertificateError
14
15
  from sgx.constants import (
15
16
  CRT_FILENAME,
16
17
  CSR_FILENAME,
@@ -56,11 +57,15 @@ def enrollment_lock(crt_dir_path: str):
56
57
  os.close(fd)
57
58
 
58
59
 
59
- def get_certificate_credentials(crt_dir_path, csr_server):
60
+ def get_certificate_credentials(crt_dir_path, csr_server, allow_registration=True):
60
61
  key_path = os.path.join(crt_dir_path, KEY_FILENAME)
61
62
  crt_path = os.path.join(crt_dir_path, CRT_FILENAME)
62
63
  if certificate_exists(crt_dir_path):
63
64
  return crt_path, key_path
65
+ if not allow_registration:
66
+ raise SgxCertificateError(
67
+ f'{crt_path} or {key_path} is missing and registration is disabled'
68
+ )
64
69
  with enrollment_lock(crt_dir_path):
65
70
  if not certificate_exists(crt_dir_path):
66
71
  csr_path = os.path.join(crt_dir_path, CSR_FILENAME)
@@ -104,7 +109,7 @@ def sign_certificate(csr_server, csr_path):
104
109
  return csr_hash
105
110
 
106
111
 
107
- def send_request(url, method, params, path_to_cert=None):
112
+ def send_request(url, method, params, path_to_cert=None, allow_registration=True):
108
113
  headers = {'content-type': 'application/json'}
109
114
  call_data = {
110
115
  "id": 0,
@@ -117,7 +122,8 @@ def send_request(url, method, params, path_to_cert=None):
117
122
  if path_to_cert:
118
123
  cert = get_certificate_credentials(
119
124
  path_to_cert,
120
- get_cert_provider(url)
125
+ get_cert_provider(url),
126
+ allow_registration
121
127
  )
122
128
 
123
129
  try:
@@ -0,0 +1,104 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # This file is part of sgx.py
4
+ #
5
+ # Copyright (C) 2026 SKALE Labs
6
+ #
7
+ # sgx.py is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Affero General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # sgx.py is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Affero General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Affero General Public License
18
+ # along with sgx.py. If not, see <https://www.gnu.org/licenses/>.
19
+
20
+ from dataclasses import dataclass, fields
21
+ from enum import IntEnum
22
+
23
+ from sgx.utils import SgxError
24
+
25
+
26
+ class SgxServerOptionsError(SgxError):
27
+ pass
28
+
29
+
30
+ class LogLevel(IntEnum):
31
+ TRACE = 0
32
+ DEBUG = 1
33
+ INFO = 2
34
+ WARNING = 3
35
+ ERROR = 4
36
+
37
+
38
+ @dataclass(frozen=True)
39
+ class ServerFlags:
40
+ log_level: LogLevel
41
+ enclave_log_level: LogLevel
42
+ use_https: bool
43
+ autoconfirm: bool
44
+ enter_backup_key: bool
45
+ reencrypt_database_with_new_sek: bool
46
+ check_cert: bool
47
+ check_zmq_sig: bool
48
+ auto_sign: bool
49
+ generate_test_keys: bool
50
+ check_key_ownership: bool
51
+ thread_pool_size: int
52
+
53
+
54
+ @dataclass(frozen=True)
55
+ class EffectiveOptions:
56
+ rpc_port: int # as bound inside the server, before any port mapping
57
+ rpc_client_certificate_required: bool
58
+ zmq_key_ownership_enforced: bool # -e is in effect; applies to ZMQ only
59
+ json_rpc_key_ownership_enforced: bool
60
+ sgx_thread_pool_size: int
61
+
62
+
63
+ @dataclass(frozen=True)
64
+ class BuildInfo:
65
+ sgx_simulation: bool
66
+ sgx_debug_launch: bool
67
+
68
+
69
+ @dataclass(frozen=True)
70
+ class ServerOptions:
71
+ """Options sgxwallet reports about itself, from getServerOptions; they are not attested.
72
+ build is sent only to authenticated callers: HTTPS with the client certificate check."""
73
+
74
+ flags: ServerFlags
75
+ effective: EffectiveOptions
76
+ build: BuildInfo | None = None
77
+
78
+ @classmethod
79
+ def from_result(cls, result):
80
+ if not isinstance(result, dict):
81
+ raise SgxServerOptionsError(f'Malformed getServerOptions result: {result!r}')
82
+ build = result.get('build')
83
+ return cls(
84
+ _load(ServerFlags, result.get('flags')),
85
+ _load(EffectiveOptions, result.get('effective')),
86
+ None if build is None else _load(BuildInfo, build),
87
+ )
88
+
89
+
90
+ def _load(cls, data):
91
+ # Fields match the camelCase keys ignoring case and underscores: use_https is useHTTPS
92
+ try:
93
+ values = {key.lower(): value for key, value in data.items()}
94
+ kwargs = {f.name: _typed(f.type, values[f.name.replace('_', '')]) for f in fields(cls)}
95
+ except (AttributeError, KeyError, TypeError, ValueError) as err:
96
+ raise SgxServerOptionsError(f'Malformed {cls.__name__}: {data!r}') from err
97
+ return cls(**kwargs)
98
+
99
+
100
+ def _typed(kind, value):
101
+ # Exact types, because bool is a subclass of int
102
+ if type(value) is not (int if kind is LogLevel else kind):
103
+ raise TypeError(f'{value!r} is not {kind.__name__}')
104
+ return kind(value)
@@ -53,9 +53,11 @@ class ComplaintResponse:
53
53
 
54
54
 
55
55
  class SgxClient:
56
- def __init__(self, sgx_endpoint, path_to_cert=None, n=None, t=None, zmq=False):
56
+ def __init__(
57
+ self, sgx_endpoint, path_to_cert=None, n=None, t=None, zmq=False, allow_registration=True
58
+ ):
57
59
  self.sgx_endpoint = sgx_endpoint
58
- self.sgx_rpc_server = SgxRPCHandler(sgx_endpoint, path_to_cert)
60
+ self.sgx_rpc_server = SgxRPCHandler(sgx_endpoint, path_to_cert, allow_registration)
59
61
  if zmq:
60
62
  self.zmq = SgxZmq(sgx_endpoint, path_to_cert, n, t)
61
63
  if not path_to_cert:
@@ -151,6 +153,15 @@ class SgxClient:
151
153
  def get_server_version(self):
152
154
  return self.sgx_rpc_server.get_server_version()
153
155
 
156
+ def get_server_options(self):
157
+ return self.sgx_rpc_server.get_server_options()
158
+
159
+ def get_issued_certificates_info(self):
160
+ return self.sgx_rpc_server.get_issued_certificates_info()
161
+
162
+ def check_local_certificate(self, expected_number=None):
163
+ return self.sgx_rpc_server.check_local_certificate(expected_number)
164
+
154
165
  def verify_secret_share(self, public_shares, eth_key_name, secret_share, index):
155
166
  return self.sgx_rpc_server.verify_secret_share(
156
167
  public_shares, eth_key_name, secret_share, self.n, self.t, index
@@ -21,16 +21,24 @@ import logging
21
21
  from enum import Enum
22
22
  from urllib.parse import urlparse
23
23
 
24
+ from sgx.certificates import IssuedCertificatesInfo, load_local_certificate
24
25
  from sgx.http import certificate_exists, send_request
26
+ from sgx.options import ServerOptions
25
27
  from sgx.utils import SgxError
26
28
 
27
29
  logger = logging.getLogger(__name__)
28
30
 
31
+ METHOD_NOT_FOUND = -32601
32
+
29
33
 
30
34
  class SgxServerError(SgxError):
31
35
  pass
32
36
 
33
37
 
38
+ class SgxMethodNotFoundError(SgxServerError):
39
+ """The server does not implement the method, e.g. an sgxwallet older than 1.10.4."""
40
+
41
+
34
42
  class DkgPolyStatus(Enum):
35
43
  FAIL = 0
36
44
  NEW_GENERATED = 1
@@ -38,10 +46,11 @@ class DkgPolyStatus(Enum):
38
46
 
39
47
 
40
48
  class SgxRPCHandler:
41
- def __init__(self, sgx_endpoint, path_to_cert):
49
+ def __init__(self, sgx_endpoint, path_to_cert, allow_registration=True):
42
50
  self.sgx_endpoint = check_provider(sgx_endpoint)
43
51
  self.path_to_cert = path_to_cert
44
- if path_to_cert and not certificate_exists(path_to_cert):
52
+ self.allow_registration = allow_registration
53
+ if allow_registration and path_to_cert and not certificate_exists(path_to_cert):
45
54
  self.get_server_status()
46
55
 
47
56
  def ecdsa_sign(self, key_name, transaction_hash):
@@ -130,6 +139,21 @@ class SgxRPCHandler:
130
139
  response = self.__send_request("getServerVersion")
131
140
  return response['result']['version']
132
141
 
142
+ def get_server_options(self):
143
+ response = self.__send_request('getServerOptions')
144
+ return ServerOptions.from_result(response['result'])
145
+
146
+ def get_issued_certificates_info(self):
147
+ response = self.__send_request('getIssuedCertificatesInfo')
148
+ return IssuedCertificatesInfo.from_result(response['result'])
149
+
150
+ def check_local_certificate(self, expected_number=None):
151
+ certificate = load_local_certificate(self.path_to_cert)
152
+ # Never registers: a new certificate would replace the one being checked
153
+ response = self.__send_request('getIssuedCertificatesInfo', allow_registration=False)
154
+ info = IssuedCertificatesInfo.from_result(response['result'])
155
+ return info.check(certificate, expected_number)
156
+
133
157
  def verify_secret_share(self, public_shares, eth_key_name, secret_share, n, t, index):
134
158
  params = dict()
135
159
  params['publicShares'] = public_shares
@@ -267,10 +291,14 @@ class SgxRPCHandler:
267
291
 
268
292
  return result
269
293
 
270
- def __send_request(self, method, params=None):
271
- response = send_request(self.sgx_endpoint, method, params, self.path_to_cert)
272
- if response.get('error') is not None:
273
- raise SgxServerError(response['error']['message'])
294
+ def __send_request(self, method, params=None, allow_registration=True):
295
+ registration = self.allow_registration and allow_registration
296
+ response = send_request(self.sgx_endpoint, method, params, self.path_to_cert, registration)
297
+ error = response.get('error')
298
+ if error is not None:
299
+ if error.get('code') == METHOD_NOT_FOUND:
300
+ raise SgxMethodNotFoundError(error['message'])
301
+ raise SgxServerError(error['message'])
274
302
  if response['result']['status']:
275
303
  raise SgxServerError(response['result']['errorMessage'])
276
304
  return response
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sgx.py
3
- Version: 0.12.dev1
3
+ Version: 0.12.dev3
4
4
  Summary: SGX
5
5
  Home-page: http://github.com/skalenetwork/sgx.py
6
6
  Author: SKALE Labs
@@ -2,9 +2,11 @@ LICENSE
2
2
  README.md
3
3
  setup.py
4
4
  sgx/__init__.py
5
+ sgx/certificates.py
5
6
  sgx/constants.py
6
7
  sgx/generate.sh
7
8
  sgx/http.py
9
+ sgx/options.py
8
10
  sgx/sgx.py
9
11
  sgx/sgx_rpc_handler.py
10
12
  sgx/sgx_zmq.py
@@ -11,13 +11,14 @@ from eth_keys import keys
11
11
  from hexbytes import HexBytes
12
12
  from web3 import Web3
13
13
 
14
- from sgx import SgxClient
14
+ from sgx import CertificateMatch, SgxClient
15
15
 
16
16
  load_dotenv()
17
17
 
18
18
  SGX_URL = os.getenv('SERVER')
19
19
  GETH_URL = os.getenv('GETH')
20
20
  ETH_PRIVATE_KEY = os.getenv('ETH_PRIVATE_KEY')
21
+ EXPECT_ZMQ_OWNERSHIP = os.getenv('SGX_EXPECT_ZMQ_OWNERSHIP') == '1'
21
22
 
22
23
  ETH_VALUE_FOR_TESTS = 5 * 10**18
23
24
 
@@ -147,6 +148,25 @@ def test_get_server_version(sgx):
147
148
  assert isinstance(sgx.get_server_version(), str)
148
149
 
149
150
 
151
+ def test_get_server_options(sgx):
152
+ url = urllib.parse.urlparse(SGX_URL)
153
+ options = sgx.get_server_options()
154
+ assert options.effective.rpc_port == url.port
155
+ assert options.flags.use_https == (url.scheme == 'https')
156
+ assert options.flags.auto_sign and options.flags.autoconfirm
157
+ assert options.effective.zmq_key_ownership_enforced == EXPECT_ZMQ_OWNERSHIP
158
+ assert (options.build is not None) == options.effective.rpc_client_certificate_required
159
+ assert options.build is None or options.build.sgx_simulation
160
+
161
+
162
+ def test_check_local_certificate(sgx):
163
+ # A fresh simulator, and every test shares CERT_PATH: one client certificate
164
+ check = sgx.check_local_certificate(expected_number=1)
165
+ assert check.outcome is CertificateMatch.MATCH
166
+ assert check.info.server_certificates_number == 1
167
+ assert check.info.newest_certificate.status == 'V'
168
+
169
+
150
170
  def test_sign_message(sgx, account, w3):
151
171
  key, address = account
152
172
  txn = generate_tx(w3, tx_type=None, no_fee=True)
@@ -1,3 +1,5 @@
1
+ import logging
2
+
1
3
  import mock
2
4
  import pytest
3
5
  import requests
@@ -31,3 +33,13 @@ def test_send_request_failed_sgx_up():
31
33
  with mock.patch('requests.post', post_mock):
32
34
  with pytest.raises(SgxUnreachableError):
33
35
  send_request(URL, method, params)
36
+
37
+
38
+ def test_send_request_logs_redacted_params(caplog):
39
+ response = mock.Mock(**{'json.return_value': {'result': {'status': 0}}})
40
+ with mock.patch('requests.post', return_value=response), caplog.at_level(logging.INFO):
41
+ send_request(URL, 'importBLSKeyShare', {'keyShare': 'a' * 64, 'keyShareName': 'b' * 64})
42
+ assert "'keyShare': '<redacted>'" in caplog.text
43
+ assert 'a' * 50 not in caplog.text
44
+ assert 'b' * 50 + '...' in caplog.text
45
+ assert 'b' * 51 not in caplog.text
@@ -1,75 +0,0 @@
1
- # sgx.py
2
- [![Discord](https://img.shields.io/discord/534485763354787851.svg)](https://discord.gg/vvUtWJB)
3
-
4
- ## Requirements
5
-
6
- |**Tool**|**Minimum Supported Version**|
7
- |--------|-----------|
8
- | Python | 3.11 |
9
-
10
- ---
11
-
12
- ## Installation
13
-
14
- ### Install Python 3.11
15
-
16
- ```bash
17
- # confirm prerequisites are isntalled
18
- sudo apt update
19
- sudo apt install software-properties-common
20
- # Add deadsnakes PPA - widely used for Python versions
21
- sudo add-apt-repository ppa:deadsnakes/ppa
22
- # update again & install python 3.11
23
- sudo apt update
24
- sudo apt install python3.11
25
- # Check if it was installed
26
- python3.11 --version
27
-
28
- pip install sgx.py
29
- ```
30
- ---
31
-
32
- ## Developers
33
- ### Install all python libraries
34
- ```bash
35
- sudo apt-get update --fix-missing
36
- sudo apt-get install libudev-dev
37
- sudo apt-get install swig
38
-
39
- # create virtual environment - install dependencies locally
40
- python3.11 -m venv venv
41
- source venv/bin/activate
42
- pip install -e .
43
- pip install -e .[dev]
44
- ```
45
-
46
- ### Running Tests
47
-
48
- Create a `.env` file in source directory, like the following:
49
- ```bash
50
- # SGXWallet listening point (1026 uses https w/ certificates)
51
- SERVER=https://127.0.0.1:1026
52
- # path where you want tests to store the auto-generated certificates
53
- CERT_PATH=.
54
- # private key for the funded test account used by transaction tests
55
- ETH_PRIVATE_KEY=
56
- # Ethereum JSON-RPC endpoint used by transaction tests
57
- GETH=
58
- ```
59
- You may alter the fields as needed.
60
-
61
- Run the tests:
62
- ```bash
63
- # Run all tests
64
- pytest
65
-
66
- # Run specific test file
67
- pytest ./tests/<test-file-name>.py
68
-
69
- ```
70
-
71
- ## License
72
-
73
- [![License](https://img.shields.io/github/license/skalenetwork/sgx.py.svg)](LICENSE)
74
-
75
- Copyright (C) 2019-present SKALE Labs
@@ -1,2 +0,0 @@
1
- # flake8: noqa: F401
2
- from sgx.sgx import SgxClient
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes