nemtus-symbol-lightapi 0.0.9__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 (52) hide show
  1. nemtus_symbol_lightapi-0.0.9/PKG-INFO +28 -0
  2. nemtus_symbol_lightapi-0.0.9/README.md +7 -0
  3. nemtus_symbol_lightapi-0.0.9/cffi_src/__init__.py +0 -0
  4. nemtus_symbol_lightapi-0.0.9/cffi_src/openssl_build.py +125 -0
  5. nemtus_symbol_lightapi-0.0.9/nemtus_symbol_lightapi.egg-info/PKG-INFO +28 -0
  6. nemtus_symbol_lightapi-0.0.9/nemtus_symbol_lightapi.egg-info/SOURCES.txt +51 -0
  7. nemtus_symbol_lightapi-0.0.9/nemtus_symbol_lightapi.egg-info/dependency_links.txt +1 -0
  8. nemtus_symbol_lightapi-0.0.9/nemtus_symbol_lightapi.egg-info/not-zip-safe +1 -0
  9. nemtus_symbol_lightapi-0.0.9/nemtus_symbol_lightapi.egg-info/requires.txt +4 -0
  10. nemtus_symbol_lightapi-0.0.9/nemtus_symbol_lightapi.egg-info/top_level.txt +4 -0
  11. nemtus_symbol_lightapi-0.0.9/pyproject.toml +12 -0
  12. nemtus_symbol_lightapi-0.0.9/setup.cfg +33 -0
  13. nemtus_symbol_lightapi-0.0.9/setup.py +34 -0
  14. nemtus_symbol_lightapi-0.0.9/symbollightapi/__init__.py +0 -0
  15. nemtus_symbol_lightapi-0.0.9/symbollightapi/bindings/__init__.py +0 -0
  16. nemtus_symbol_lightapi-0.0.9/symbollightapi/bindings/openssl.py +7 -0
  17. nemtus_symbol_lightapi-0.0.9/symbollightapi/connector/BasicConnector.py +62 -0
  18. nemtus_symbol_lightapi-0.0.9/symbollightapi/connector/CatapultCertificateProcessor.py +76 -0
  19. nemtus_symbol_lightapi-0.0.9/symbollightapi/connector/CertificateUtils.py +66 -0
  20. nemtus_symbol_lightapi-0.0.9/symbollightapi/connector/ConnectorExtensions.py +65 -0
  21. nemtus_symbol_lightapi-0.0.9/symbollightapi/connector/NemBlockCalculator.py +274 -0
  22. nemtus_symbol_lightapi-0.0.9/symbollightapi/connector/NemConnector.py +429 -0
  23. nemtus_symbol_lightapi-0.0.9/symbollightapi/connector/SymbolConnector.py +339 -0
  24. nemtus_symbol_lightapi-0.0.9/symbollightapi/connector/SymbolPeerConnector.py +170 -0
  25. nemtus_symbol_lightapi-0.0.9/symbollightapi/connector/__init__.py +0 -0
  26. nemtus_symbol_lightapi-0.0.9/symbollightapi/model/Block.py +32 -0
  27. nemtus_symbol_lightapi-0.0.9/symbollightapi/model/Constants.py +13 -0
  28. nemtus_symbol_lightapi-0.0.9/symbollightapi/model/Endpoint.py +21 -0
  29. nemtus_symbol_lightapi-0.0.9/symbollightapi/model/Exceptions.py +28 -0
  30. nemtus_symbol_lightapi-0.0.9/symbollightapi/model/NodeInfo.py +66 -0
  31. nemtus_symbol_lightapi-0.0.9/symbollightapi/model/PacketHeader.py +62 -0
  32. nemtus_symbol_lightapi-0.0.9/symbollightapi/model/Transaction.py +564 -0
  33. nemtus_symbol_lightapi-0.0.9/symbollightapi/model/__init__.py +0 -0
  34. nemtus_symbol_lightapi-0.0.9/tests/__init__.py +0 -0
  35. nemtus_symbol_lightapi-0.0.9/tests/connector/__init__.py +0 -0
  36. nemtus_symbol_lightapi-0.0.9/tests/connector/test_BasicConnector.py +407 -0
  37. nemtus_symbol_lightapi-0.0.9/tests/connector/test_CatapultCertificateProcessor.py +286 -0
  38. nemtus_symbol_lightapi-0.0.9/tests/connector/test_CertificateUtils.py +168 -0
  39. nemtus_symbol_lightapi-0.0.9/tests/connector/test_ConnectorExtensions.py +236 -0
  40. nemtus_symbol_lightapi-0.0.9/tests/connector/test_NemBlockCalculator.py +279 -0
  41. nemtus_symbol_lightapi-0.0.9/tests/connector/test_NemConnector.py +1445 -0
  42. nemtus_symbol_lightapi-0.0.9/tests/connector/test_SymbolConnector.py +927 -0
  43. nemtus_symbol_lightapi-0.0.9/tests/connector/test_SymbolPeerConnector.py +294 -0
  44. nemtus_symbol_lightapi-0.0.9/tests/model/__init__.py +0 -0
  45. nemtus_symbol_lightapi-0.0.9/tests/model/test_Block.py +70 -0
  46. nemtus_symbol_lightapi-0.0.9/tests/model/test_Endpoint.py +57 -0
  47. nemtus_symbol_lightapi-0.0.9/tests/model/test_NodeInfo.py +177 -0
  48. nemtus_symbol_lightapi-0.0.9/tests/model/test_PacketHeader.py +43 -0
  49. nemtus_symbol_lightapi-0.0.9/tests/model/test_Transaction.py +401 -0
  50. nemtus_symbol_lightapi-0.0.9/tests/test/CertificateTestUtils.py +114 -0
  51. nemtus_symbol_lightapi-0.0.9/tests/test/LightApiTestUtils.py +24 -0
  52. nemtus_symbol_lightapi-0.0.9/tests/test/__init__.py +0 -0
@@ -0,0 +1,28 @@
1
+ Metadata-Version: 2.4
2
+ Name: nemtus-symbol-lightapi
3
+ Version: 0.0.9
4
+ Summary: Symbol Light API (NEMTUS mirror of upstream symbol-lightapi; import module: symbollightapi)
5
+ Home-page: https://github.com/nemtus/symbol-product/tree/dev/lightapi/python
6
+ Author: NEMTUS
7
+ Author-email: info@nemtus.com
8
+ License: MIT
9
+ Keywords: symbol,symbollightapi,lightapi,Symbol Light API,nemtus
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ Requires-Dist: aiohttp~=3.14.0
18
+ Requires-Dist: aiolimiter~=1.2.1
19
+ Requires-Dist: nemtus-symbol-sdk~=3.3.2
20
+ Requires-Dist: zenlog~=1.1
21
+
22
+ # Python Light API
23
+
24
+ Provides partial Python wrappers for calling NEM and Symbol API functions.
25
+
26
+ Only a subset of APIs are currently supported.
27
+
28
+ PRs are accepted for extending the set of APIs.
@@ -0,0 +1,7 @@
1
+ # Python Light API
2
+
3
+ Provides partial Python wrappers for calling NEM and Symbol API functions.
4
+
5
+ Only a subset of APIs are currently supported.
6
+
7
+ PRs are accepted for extending the set of APIs.
File without changes
@@ -0,0 +1,125 @@
1
+ import os
2
+ from pathlib import Path
3
+
4
+ from cffi import FFI
5
+
6
+ ffi_builder = FFI()
7
+
8
+ # if specified, add openssl to the include path
9
+ include_dirs = []
10
+ library_dirs = []
11
+ openssl_root_dir = os.environ.get('OPENSSL_ROOT_DIR', None)
12
+ if openssl_root_dir:
13
+ include_dirs += [Path(openssl_root_dir) / 'include']
14
+ library_dirs += [str(Path(openssl_root_dir) / 'lib')]
15
+
16
+ # build '_openssl_symbol' module and include openssl headers
17
+ ffi_builder.set_source(
18
+ '_openssl_symbol',
19
+ r'''
20
+ #include <openssl/ssl.h>
21
+ typedef STACK_OF(X509) Cryptography_STACK_OF_X509; // replacement for STACK_OF(X509)
22
+ ''',
23
+ include_dirs=include_dirs,
24
+ library_dirs=library_dirs,
25
+ libraries=['libcrypto', 'libssl'] if os.name == 'nt' else ['crypto', 'ssl'])
26
+
27
+ # add all openssl constants, types and functions being used
28
+ ffi_builder.cdef('''
29
+ // constants
30
+ static const int MBSTRING_ASC;
31
+ static const int XN_FLAG_RFC2253;
32
+
33
+ static const int SSL_VERIFY_PEER;
34
+ static const int SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
35
+
36
+ static const int EVP_PKEY_ED25519;
37
+ static const int EVP_PKEY_X25519;
38
+
39
+ static const int X509_V_ERR_APPLICATION_VERIFICATION;
40
+ static const int X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
41
+
42
+ static const int X509_V_FLAG_CHECK_SS_SIGNATURE;
43
+
44
+ // types
45
+ typedef ... SSL_CTX;
46
+ typedef ... BIO;
47
+ typedef ... BIO_METHOD;
48
+ typedef ... ENGINE;
49
+ typedef ... EVP_MD;
50
+ typedef ... EVP_PKEY;
51
+ typedef ... X509;
52
+ typedef ... Cryptography_STACK_OF_X509;
53
+ typedef ... X509_STORE;
54
+ typedef ... X509_STORE_CTX;
55
+ typedef ... X509_NAME;
56
+ typedef ... ASN1_INTEGER;
57
+ typedef ... ASN1_TIME;
58
+
59
+ // SSL_CTX
60
+ void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *));
61
+
62
+ // BIO_METHOD
63
+ const BIO_METHOD * BIO_s_mem(void);
64
+
65
+ // BIO
66
+ BIO* BIO_new(const BIO_METHOD *type);
67
+ int BIO_free(BIO *a);
68
+ long BIO_get_mem_data(BIO *b, char **pp);
69
+
70
+ // EVP_PKEY
71
+ EVP_PKEY *EVP_PKEY_new_raw_private_key(int type, ENGINE *e, const unsigned char *key, size_t keylen);
72
+ void EVP_PKEY_free(EVP_PKEY *key);
73
+ int EVP_PKEY_id(const EVP_PKEY *pkey);
74
+ int EVP_PKEY_get_raw_public_key(const EVP_PKEY *pkey, unsigned char *pub, size_t *len);
75
+
76
+ // X509
77
+ X509 *X509_new(void);
78
+ void X509_free(X509 *a);
79
+ EVP_PKEY *X509_get0_pubkey(const X509 *x);
80
+ X509_NAME *X509_get_subject_name(const X509 *x);
81
+ X509_NAME *X509_get_issuer_name(const X509 *x);
82
+ ASN1_INTEGER *X509_get_serialNumber(X509 *x);
83
+ ASN1_TIME *X509_getm_notBefore(const X509 *x);
84
+ ASN1_TIME *X509_getm_notAfter(const X509 *x);
85
+ int X509_set_pubkey(X509 *x, EVP_PKEY *pkey);
86
+ int X509_set_version(X509 *x, long version);
87
+ int X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md);
88
+
89
+ // STACK_OF(X509)
90
+ Cryptography_STACK_OF_X509 *sk_X509_new_null(void);
91
+ void sk_X509_free(Cryptography_STACK_OF_X509 *sk);
92
+ int sk_X509_num(const Cryptography_STACK_OF_X509 *sk);
93
+ int sk_X509_push(Cryptography_STACK_OF_X509 *sk, X509 *ptr);
94
+
95
+ // X509_STORE
96
+ X509_STORE *X509_STORE_new(void);
97
+ void X509_STORE_free(X509_STORE *v);
98
+ int X509_STORE_add_cert(X509_STORE *ctx, X509 *x);
99
+
100
+ // X509_STORE_CTX
101
+ X509_STORE_CTX *X509_STORE_CTX_new(void);
102
+ void X509_STORE_CTX_free(X509_STORE_CTX *ctx);
103
+ int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *trust_store, X509 *target, Cryptography_STACK_OF_X509 *untrusted);
104
+ Cryptography_STACK_OF_X509 *X509_STORE_CTX_get0_chain(X509_STORE_CTX *ctx);
105
+ X509 *X509_STORE_CTX_get_current_cert(X509_STORE_CTX *ctx);
106
+ int X509_STORE_CTX_get_error(X509_STORE_CTX *ctx);
107
+ void X509_STORE_CTX_set_error(X509_STORE_CTX *ctx, int s);
108
+ void X509_STORE_CTX_set_current_cert(X509_STORE_CTX *ctx, X509 *x);
109
+ void X509_STORE_CTX_set0_verified_chain(X509_STORE_CTX *ctx, Cryptography_STACK_OF_X509 *chain);
110
+ void X509_STORE_CTX_set_flags(X509_STORE_CTX *ctx, unsigned long flags);
111
+ int X509_verify_cert(X509_STORE_CTX *ctx);
112
+
113
+ // X509_NAME
114
+ int X509_NAME_add_entry_by_txt(X509_NAME *name, const char *field, int type, const unsigned char *bytes, int len, int loc, int set);
115
+ int X509_NAME_print_ex(BIO *out, const X509_NAME *nm, int indent, unsigned long flags);
116
+
117
+ // ASN1_INTEGER
118
+ int ASN1_INTEGER_set(ASN1_INTEGER *a, long v);
119
+
120
+ // ASN1_TIME
121
+ ASN1_TIME *X509_gmtime_adj(ASN1_TIME *asn1_time, long offset_sec);
122
+ ''')
123
+
124
+ if '__main__' == __name__:
125
+ ffi_builder.compile(verbose=True)
@@ -0,0 +1,28 @@
1
+ Metadata-Version: 2.4
2
+ Name: nemtus-symbol-lightapi
3
+ Version: 0.0.9
4
+ Summary: Symbol Light API (NEMTUS mirror of upstream symbol-lightapi; import module: symbollightapi)
5
+ Home-page: https://github.com/nemtus/symbol-product/tree/dev/lightapi/python
6
+ Author: NEMTUS
7
+ Author-email: info@nemtus.com
8
+ License: MIT
9
+ Keywords: symbol,symbollightapi,lightapi,Symbol Light API,nemtus
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ Requires-Dist: aiohttp~=3.14.0
18
+ Requires-Dist: aiolimiter~=1.2.1
19
+ Requires-Dist: nemtus-symbol-sdk~=3.3.2
20
+ Requires-Dist: zenlog~=1.1
21
+
22
+ # Python Light API
23
+
24
+ Provides partial Python wrappers for calling NEM and Symbol API functions.
25
+
26
+ Only a subset of APIs are currently supported.
27
+
28
+ PRs are accepted for extending the set of APIs.
@@ -0,0 +1,51 @@
1
+ README.md
2
+ pyproject.toml
3
+ setup.cfg
4
+ setup.py
5
+ cffi_src/__init__.py
6
+ cffi_src/openssl_build.py
7
+ nemtus_symbol_lightapi.egg-info/PKG-INFO
8
+ nemtus_symbol_lightapi.egg-info/SOURCES.txt
9
+ nemtus_symbol_lightapi.egg-info/dependency_links.txt
10
+ nemtus_symbol_lightapi.egg-info/not-zip-safe
11
+ nemtus_symbol_lightapi.egg-info/requires.txt
12
+ nemtus_symbol_lightapi.egg-info/top_level.txt
13
+ symbollightapi/__init__.py
14
+ symbollightapi/bindings/__init__.py
15
+ symbollightapi/bindings/openssl.py
16
+ symbollightapi/connector/BasicConnector.py
17
+ symbollightapi/connector/CatapultCertificateProcessor.py
18
+ symbollightapi/connector/CertificateUtils.py
19
+ symbollightapi/connector/ConnectorExtensions.py
20
+ symbollightapi/connector/NemBlockCalculator.py
21
+ symbollightapi/connector/NemConnector.py
22
+ symbollightapi/connector/SymbolConnector.py
23
+ symbollightapi/connector/SymbolPeerConnector.py
24
+ symbollightapi/connector/__init__.py
25
+ symbollightapi/model/Block.py
26
+ symbollightapi/model/Constants.py
27
+ symbollightapi/model/Endpoint.py
28
+ symbollightapi/model/Exceptions.py
29
+ symbollightapi/model/NodeInfo.py
30
+ symbollightapi/model/PacketHeader.py
31
+ symbollightapi/model/Transaction.py
32
+ symbollightapi/model/__init__.py
33
+ tests/__init__.py
34
+ tests/connector/__init__.py
35
+ tests/connector/test_BasicConnector.py
36
+ tests/connector/test_CatapultCertificateProcessor.py
37
+ tests/connector/test_CertificateUtils.py
38
+ tests/connector/test_ConnectorExtensions.py
39
+ tests/connector/test_NemBlockCalculator.py
40
+ tests/connector/test_NemConnector.py
41
+ tests/connector/test_SymbolConnector.py
42
+ tests/connector/test_SymbolPeerConnector.py
43
+ tests/model/__init__.py
44
+ tests/model/test_Block.py
45
+ tests/model/test_Endpoint.py
46
+ tests/model/test_NodeInfo.py
47
+ tests/model/test_PacketHeader.py
48
+ tests/model/test_Transaction.py
49
+ tests/test/CertificateTestUtils.py
50
+ tests/test/LightApiTestUtils.py
51
+ tests/test/__init__.py
@@ -0,0 +1,4 @@
1
+ aiohttp~=3.14.0
2
+ aiolimiter~=1.2.1
3
+ nemtus-symbol-sdk~=3.3.2
4
+ zenlog~=1.1
@@ -0,0 +1,4 @@
1
+ _openssl_symbol
2
+ cffi_src
3
+ symbollightapi
4
+ tests
@@ -0,0 +1,12 @@
1
+ [build-system]
2
+ requires = [
3
+ "setuptools>=61",
4
+ "wheel",
5
+ "cffi>=1.15.0",
6
+ ]
7
+ build-backend = "setuptools.build_meta"
8
+
9
+ [tool.pytest.ini_options]
10
+ filterwarnings = [
11
+ "ignore:The 'warn' method is deprecated:DeprecationWarning"
12
+ ]
@@ -0,0 +1,33 @@
1
+ [metadata]
2
+ name = nemtus-symbol-lightapi
3
+ version = 0.0.9
4
+ author = NEMTUS
5
+ author_email = info@nemtus.com
6
+ description = Symbol Light API (NEMTUS mirror of upstream symbol-lightapi; import module: symbollightapi)
7
+ long_description = file: README.md
8
+ long_description_content_type = text/markdown
9
+ keywords = symbol, symbollightapi, lightapi, Symbol Light API, nemtus
10
+ license = MIT
11
+ classifiers =
12
+ Programming Language :: Python :: 3.10
13
+ Programming Language :: Python :: 3.11
14
+ Programming Language :: Python :: 3.12
15
+ Programming Language :: Python :: 3.13
16
+ Programming Language :: Python :: 3.14
17
+ url = https://github.com/nemtus/symbol-product/tree/dev/lightapi/python
18
+
19
+ [options]
20
+ zip_safe = False
21
+ include_package_data = True
22
+ packages = find:
23
+ python_requires = >=3.10
24
+ install_requires =
25
+ aiohttp~=3.14.0
26
+ aiolimiter~=1.2.1
27
+ nemtus-symbol-sdk~=3.3.2
28
+ zenlog~=1.1
29
+
30
+ [egg_info]
31
+ tag_build =
32
+ tag_date = 0
33
+
@@ -0,0 +1,34 @@
1
+ import platform
2
+ import sys
3
+
4
+ from setuptools import setup
5
+
6
+ cmdclass = {}
7
+
8
+ if 'CPython' == platform.python_implementation():
9
+ try:
10
+ import wheel.bdist_wheel
11
+
12
+ class BDistWheel(wheel.bdist_wheel.bdist_wheel):
13
+ def __init__(self, dist, **kw):
14
+ super().__init__(dist, **kw)
15
+ self.py_limited_api = None
16
+
17
+ def finalize_options(self):
18
+ self.py_limited_api = f'cp3{sys.version_info[1]}'
19
+ wheel.bdist_wheel.bdist_wheel.finalize_options(self)
20
+
21
+ cmdclass['bdist_wheel'] = BDistWheel
22
+ except ImportError:
23
+ pass
24
+
25
+
26
+ if '__main__' == __name__:
27
+ setup(
28
+ # Ensure limited API is set on CPython
29
+ cmdclass=cmdclass,
30
+ # CFFI
31
+ zip_safe=False,
32
+ ext_package='symbollightapi/bindings',
33
+ cffi_modules=['cffi_src/openssl_build.py:ffi_builder'],
34
+ )
@@ -0,0 +1,7 @@
1
+ from cryptography.hazmat.bindings.openssl.binding import Binding as OpensslBinding
2
+
3
+ from ._openssl_symbol import ffi, lib # pylint: disable=no-name-in-module, unused-import
4
+
5
+ # initialize openssl
6
+ if not OpensslBinding.lib:
7
+ OpensslBinding()
@@ -0,0 +1,62 @@
1
+ import asyncio
2
+ import json
3
+
4
+ from aiohttp import ClientSession, ClientTimeout, client_exceptions
5
+
6
+ from ..model.Exceptions import HttpException, NodeException
7
+
8
+
9
+ class BasicConnector:
10
+ """Async connector for interacting with a node."""
11
+
12
+ def __init__(self, endpoint):
13
+ """Creates a connector around an endpoint."""
14
+
15
+ self.endpoint = endpoint
16
+ self.timeout_seconds = None
17
+
18
+ async def _dispatch(self, action, url_path, property_name, not_found_as_error, **kwargs):
19
+ try:
20
+ timeout = ClientTimeout(total=self.timeout_seconds)
21
+ async with ClientSession(timeout=timeout) as session:
22
+ async with getattr(session, action)(f'{self.endpoint}/{url_path}', **kwargs) as response:
23
+ try:
24
+ response_json = await response.json()
25
+ except (client_exceptions.ContentTypeError, json.decoder.JSONDecodeError) as ex:
26
+ raise NodeException from ex
27
+
28
+ if 400 <= response.status and (404 != response.status or not_found_as_error):
29
+ error_message = f'HTTP request failed with code {response.status}'
30
+ for key in ('code', 'message'):
31
+ if key in response_json:
32
+ error_message += f'\n{response_json[key]}'
33
+
34
+ raise HttpException(error_message, response.status)
35
+
36
+ return response_json if property_name is None else response_json[property_name]
37
+ except (asyncio.TimeoutError, client_exceptions.ClientConnectorError) as ex:
38
+ raise NodeException from ex
39
+
40
+ async def get(self, url_path, property_name=None, not_found_as_error=True):
41
+ """
42
+ Initiates a GET to the specified path and returns the desired property.
43
+ Raises NodeException on connection or content failure.
44
+ """
45
+
46
+ return await self._dispatch('get', url_path, property_name, not_found_as_error)
47
+
48
+ async def post(self, url_path, request_payload, property_name=None, not_found_as_error=True):
49
+ """
50
+ Initiates a POST to the specified path and returns the desired property.
51
+ Raises NodeException on connection or content failure.
52
+ """
53
+
54
+ return await self._dispatch('post', url_path, property_name, not_found_as_error, json=request_payload)
55
+
56
+ async def put(self, url_path, request_payload, property_name=None, not_found_as_error=True):
57
+ """
58
+ Initiates a PUT to the specified path and returns the desired property.
59
+ Raises NodeException on connection or content failure.
60
+ """
61
+
62
+ return await self._dispatch('put', url_path, property_name, not_found_as_error, json=request_payload)
@@ -0,0 +1,76 @@
1
+ from zenlog import log
2
+
3
+ from ..bindings.openssl import lib
4
+ from .CertificateUtils import try_parse_certificate, verify_self_signed
5
+
6
+
7
+ class CatapultCertificateProcessor:
8
+ """Catapult-specific certificate processor."""
9
+
10
+ def __init__(self):
11
+ """Creates a new certificate processor."""
12
+
13
+ self.certificate_infos = []
14
+
15
+ @property
16
+ def size(self):
17
+ """Gets the number of certificates in the chain."""
18
+
19
+ return len(self.certificate_infos)
20
+
21
+ def certificate(self, depth):
22
+ """
23
+ Gets the parsed certificate information at depth.
24
+ Depth is 0-based starting with the root certificate.
25
+ """
26
+
27
+ return self.certificate_infos[depth]
28
+
29
+ def verify(self, preverified, certificate_store_context):
30
+ """Verifies the current certificate in certificate_store_context given preverified result."""
31
+
32
+ # reject all certificate chains that are not composed of two certificates
33
+ chain = lib.X509_STORE_CTX_get0_chain(certificate_store_context)
34
+ chain_size = lib.sk_X509_num(chain)
35
+ if 2 != chain_size:
36
+ log.warning(f'rejecting certificate chain with size {chain_size}')
37
+ return False
38
+
39
+ certificate = lib.X509_STORE_CTX_get_current_cert(certificate_store_context)
40
+ if not certificate:
41
+ raise RuntimeError('rejecting certificate chain with no active certificate')
42
+
43
+ if preverified:
44
+ if self._push(certificate):
45
+ return True
46
+
47
+ lib.X509_STORE_CTX_set_error(certificate_store_context, lib.X509_V_ERR_APPLICATION_VERIFICATION)
48
+ return False
49
+
50
+ error_code = lib.X509_STORE_CTX_get_error(certificate_store_context)
51
+ return self.verify_unverified_root(certificate, error_code)
52
+
53
+ def verify_unverified_root(self, certificate, error_code):
54
+ if self.certificate_infos:
55
+ log.warning('rejecting certificate chain with unverified non-root certificate')
56
+ return False
57
+
58
+ # only verify self signed certificates
59
+ if lib.X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN != error_code:
60
+ log.warning(f'rejecting certificate chain with unverified unexpected error {error_code}')
61
+ return False
62
+
63
+ if not verify_self_signed(certificate):
64
+ log.warning('rejecting certificate chain with improperly self-signed root certificate')
65
+ return False
66
+
67
+ return True
68
+
69
+ def _push(self, certificate):
70
+ certificate_info = try_parse_certificate(certificate)
71
+ if not certificate_info:
72
+ log.warning('rejecting certificate chain due to certificate parse failure')
73
+ return False
74
+
75
+ self.certificate_infos.append(certificate_info)
76
+ return True
@@ -0,0 +1,66 @@
1
+ from collections import namedtuple
2
+
3
+ from symbolchain.CryptoTypes import PublicKey
4
+
5
+ from symbollightapi.bindings.openssl import ffi, lib
6
+
7
+ CertificateInfo = namedtuple('CertificateInfo', ['subject', 'public_key'])
8
+
9
+ # region try_parse_certificate
10
+
11
+
12
+ def _extract_one_line(name):
13
+ bio = ffi.gc(lib.BIO_new(lib.BIO_s_mem()), lib.BIO_free)
14
+
15
+ if -1 == lib.X509_NAME_print_ex(bio, name, 0, lib.XN_FLAG_RFC2253):
16
+ return None
17
+
18
+ raw_data = ffi.new('char **')
19
+ data_size = lib.BIO_get_mem_data(bio, raw_data)
20
+ data = ffi.buffer(raw_data[0], data_size)[:]
21
+ return data.decode('utf8')
22
+
23
+
24
+ def try_parse_certificate(certificate):
25
+ """Tries to extract information about certificate."""
26
+
27
+ subject_x509_name = lib.X509_get_subject_name(certificate)
28
+ subject = _extract_one_line(subject_x509_name)
29
+
30
+ certificate_public_key = lib.X509_get0_pubkey(certificate)
31
+ if not certificate_public_key:
32
+ return None
33
+
34
+ if lib.EVP_PKEY_ED25519 != lib.EVP_PKEY_id(ffi.cast('EVP_PKEY *', certificate_public_key)):
35
+ return None
36
+
37
+ public_key = PublicKey(bytes(PublicKey.SIZE))
38
+ key_size_pointer = ffi.new('size_t *', PublicKey.SIZE)
39
+ if not lib.EVP_PKEY_get_raw_public_key(certificate_public_key, public_key.bytes, key_size_pointer):
40
+ return None
41
+
42
+ if PublicKey.SIZE != key_size_pointer[0]:
43
+ return None
44
+
45
+ return CertificateInfo(subject, public_key)
46
+
47
+ # endregion
48
+
49
+
50
+ # region verify_self_signed
51
+
52
+ def verify_self_signed(certificate):
53
+ """Returns True if self-signed certificate signature is correct."""
54
+
55
+ certificate_store = ffi.gc(lib.X509_STORE_new(), lib.X509_STORE_free)
56
+ if not lib.X509_STORE_add_cert(certificate_store, certificate):
57
+ return False
58
+
59
+ certificate_store_context = ffi.gc(lib.X509_STORE_CTX_new(), lib.X509_STORE_CTX_free)
60
+ if not lib.X509_STORE_CTX_init(certificate_store_context, certificate_store, certificate, ffi.cast('Cryptography_STACK_OF_X509 *', 0)):
61
+ return False
62
+
63
+ lib.X509_STORE_CTX_set_flags(certificate_store_context, lib.X509_V_FLAG_CHECK_SS_SIGNATURE)
64
+ return 1 == lib.X509_verify_cert(certificate_store_context)
65
+
66
+ # endregion
@@ -0,0 +1,65 @@
1
+ import asyncio
2
+
3
+ from aiolimiter import AsyncLimiter
4
+
5
+ from ..model.Constants import DEFAULT_ASYNC_LIMITER_ARGUMENTS
6
+
7
+ # region get_incoming_transactions_from
8
+
9
+
10
+ async def get_incoming_transactions_from(connector, address, start_height=None, end_height=None):
11
+ """Uses the specified connector to retrieve all transactions sent to an account in the range [start_height, end_height)."""
12
+
13
+ start_id = None
14
+ while True:
15
+ transactions_json = await connector.incoming_transactions(address, start_id)
16
+ if not transactions_json:
17
+ return
18
+
19
+ for transaction_json in transactions_json:
20
+ transaction_height = int(transaction_json['meta']['height'])
21
+ if start_height and transaction_height < start_height:
22
+ return
23
+
24
+ if end_height and transaction_height >= end_height:
25
+ continue
26
+
27
+ yield transaction_json
28
+
29
+ start_id = connector.extract_transaction_id(transactions_json[-1])
30
+
31
+ # endregion
32
+
33
+
34
+ # region filter_finalized_transactions
35
+
36
+ async def filter_finalized_transactions(connector, transaction_hashes):
37
+ """Filters transaction hashes and returns only finalized ones with heights."""
38
+
39
+ finalized_chain_height = await connector.finalized_chain_height()
40
+ transaction_hash_height_pairs = await connector.filter_confirmed_transactions(transaction_hashes)
41
+ return list(filter(
42
+ lambda transaction_hash_height_pair: transaction_hash_height_pair[1] <= finalized_chain_height,
43
+ transaction_hash_height_pairs))
44
+
45
+ # endregion
46
+
47
+
48
+ # region query_block_timestamps
49
+
50
+ async def query_block_timestamps(connector, heights, async_limiter_arguments=DEFAULT_ASYNC_LIMITER_ARGUMENTS):
51
+ """Finds the timestamps for all blocks with the specified heights."""
52
+
53
+ limiter = AsyncLimiter(*async_limiter_arguments)
54
+
55
+ async def get_block_height_timestamp_pair(height):
56
+ async with limiter:
57
+ block_json = await connector.block_headers(height)
58
+ timestamp = connector.extract_block_timestamp(block_json)
59
+ return (height, timestamp)
60
+
61
+ tasks = [get_block_height_timestamp_pair(height) for height in heights]
62
+ block_height_timestamp_pairs = await asyncio.gather(*tasks)
63
+ return block_height_timestamp_pairs
64
+
65
+ # endregion