nemtus-symbol-sdk 3.3.2__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 (58) hide show
  1. nemtus_symbol_sdk-3.3.2/LICENSE +27 -0
  2. nemtus_symbol_sdk-3.3.2/PKG-INFO +145 -0
  3. nemtus_symbol_sdk-3.3.2/README.md +113 -0
  4. nemtus_symbol_sdk-3.3.2/pyproject.toml +29 -0
  5. nemtus_symbol_sdk-3.3.2/symbolchain/AccountDescriptorRepository.py +59 -0
  6. nemtus_symbol_sdk-3.3.2/symbolchain/ArrayHelpers.py +116 -0
  7. nemtus_symbol_sdk-3.3.2/symbolchain/BaseValue.py +55 -0
  8. nemtus_symbol_sdk-3.3.2/symbolchain/Bip32.py +56 -0
  9. nemtus_symbol_sdk-3.3.2/symbolchain/BlockchainSettings.py +21 -0
  10. nemtus_symbol_sdk-3.3.2/symbolchain/BufferReader.py +34 -0
  11. nemtus_symbol_sdk-3.3.2/symbolchain/BufferWriter.py +26 -0
  12. nemtus_symbol_sdk-3.3.2/symbolchain/ByteArray.py +43 -0
  13. nemtus_symbol_sdk-3.3.2/symbolchain/Cipher.py +66 -0
  14. nemtus_symbol_sdk-3.3.2/symbolchain/CodeWordsEncoder.py +47 -0
  15. nemtus_symbol_sdk-3.3.2/symbolchain/CryptoTypes.py +83 -0
  16. nemtus_symbol_sdk-3.3.2/symbolchain/DiceMnemonicGenerator.py +56 -0
  17. nemtus_symbol_sdk-3.3.2/symbolchain/Network.py +100 -0
  18. nemtus_symbol_sdk-3.3.2/symbolchain/NetworkTimestamp.py +53 -0
  19. nemtus_symbol_sdk-3.3.2/symbolchain/NodeDescriptorRepository.py +27 -0
  20. nemtus_symbol_sdk-3.3.2/symbolchain/Ordered.py +20 -0
  21. nemtus_symbol_sdk-3.3.2/symbolchain/PrivateKeyStorage.py +41 -0
  22. nemtus_symbol_sdk-3.3.2/symbolchain/QrSignatureStorage.py +37 -0
  23. nemtus_symbol_sdk-3.3.2/symbolchain/QrStorage.py +42 -0
  24. nemtus_symbol_sdk-3.3.2/symbolchain/RuleBasedTransactionFactory.py +177 -0
  25. nemtus_symbol_sdk-3.3.2/symbolchain/SharedKey.py +16 -0
  26. nemtus_symbol_sdk-3.3.2/symbolchain/TransactionDescriptorProcessor.py +50 -0
  27. nemtus_symbol_sdk-3.3.2/symbolchain/Transforms.py +9 -0
  28. nemtus_symbol_sdk-3.3.2/symbolchain/__init__.py +0 -0
  29. nemtus_symbol_sdk-3.3.2/symbolchain/external/__init__.py +0 -0
  30. nemtus_symbol_sdk-3.3.2/symbolchain/external/ed25519.py +313 -0
  31. nemtus_symbol_sdk-3.3.2/symbolchain/facade/BatchOperations.py +66 -0
  32. nemtus_symbol_sdk-3.3.2/symbolchain/facade/NemFacade.py +117 -0
  33. nemtus_symbol_sdk-3.3.2/symbolchain/facade/SymbolFacade.py +191 -0
  34. nemtus_symbol_sdk-3.3.2/symbolchain/facade/__init__.py +0 -0
  35. nemtus_symbol_sdk-3.3.2/symbolchain/impl/CipherHelpers.py +57 -0
  36. nemtus_symbol_sdk-3.3.2/symbolchain/impl/__init__.py +0 -0
  37. nemtus_symbol_sdk-3.3.2/symbolchain/nc/__init__.py +3842 -0
  38. nemtus_symbol_sdk-3.3.2/symbolchain/nem/FeeCalculator.py +94 -0
  39. nemtus_symbol_sdk-3.3.2/symbolchain/nem/KeyPair.py +137 -0
  40. nemtus_symbol_sdk-3.3.2/symbolchain/nem/MessageEncoder.py +78 -0
  41. nemtus_symbol_sdk-3.3.2/symbolchain/nem/Network.py +58 -0
  42. nemtus_symbol_sdk-3.3.2/symbolchain/nem/SharedKey.py +26 -0
  43. nemtus_symbol_sdk-3.3.2/symbolchain/nem/TransactionFactory.py +110 -0
  44. nemtus_symbol_sdk-3.3.2/symbolchain/nem/__init__.py +0 -0
  45. nemtus_symbol_sdk-3.3.2/symbolchain/ripemd160.py +17 -0
  46. nemtus_symbol_sdk-3.3.2/symbolchain/sc/__init__.py +9989 -0
  47. nemtus_symbol_sdk-3.3.2/symbolchain/symbol/FeeCalculator.py +7 -0
  48. nemtus_symbol_sdk-3.3.2/symbolchain/symbol/IdGenerator.py +63 -0
  49. nemtus_symbol_sdk-3.3.2/symbolchain/symbol/KeyPair.py +49 -0
  50. nemtus_symbol_sdk-3.3.2/symbolchain/symbol/Merkle.py +266 -0
  51. nemtus_symbol_sdk-3.3.2/symbolchain/symbol/MessageEncoder.py +103 -0
  52. nemtus_symbol_sdk-3.3.2/symbolchain/symbol/Metadata.py +32 -0
  53. nemtus_symbol_sdk-3.3.2/symbolchain/symbol/Network.py +98 -0
  54. nemtus_symbol_sdk-3.3.2/symbolchain/symbol/Restriction.py +10 -0
  55. nemtus_symbol_sdk-3.3.2/symbolchain/symbol/SharedKey.py +9 -0
  56. nemtus_symbol_sdk-3.3.2/symbolchain/symbol/TransactionFactory.py +109 -0
  57. nemtus_symbol_sdk-3.3.2/symbolchain/symbol/VotingKeysGenerator.py +38 -0
  58. nemtus_symbol_sdk-3.3.2/symbolchain/symbol/__init__.py +0 -0
@@ -0,0 +1,27 @@
1
+ MIT License
2
+
3
+ This package is a redistribution of `symbol-sdk-python` (import module
4
+ `symbolchain`) from the upstream project symbol/symbol
5
+ (https://github.com/symbol/symbol), published by its authors — identified in the
6
+ upstream package metadata as "Symbol Contributors <contributors@symbol.dev>" —
7
+ under the MIT License. The nemtus mirror asserts no copyright over this software
8
+ and changes nothing but the published PyPI distribution name (to
9
+ `nemtus-symbol-sdk`). The authoritative copyright and license are upstream's.
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
@@ -0,0 +1,145 @@
1
+ Metadata-Version: 2.4
2
+ Name: nemtus-symbol-sdk
3
+ Version: 3.3.2
4
+ Summary: Symbol Python SDK (nemtus mirror of upstream symbol-sdk-python; import module: symbolchain)
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Keywords: symbol,sdk,Symbol SDK
8
+ Author: Symbol Contributors
9
+ Author-email: contributors@symbol.dev
10
+ Maintainer: Symbol Contributors
11
+ Maintainer-email: contributors@symbol.dev
12
+ Requires-Python: >=3.10,<4.0
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Requires-Dist: cryptography (>=49.0.0,<49.1.0)
21
+ Requires-Dist: mnemonic (>=0.20,<1.0)
22
+ Requires-Dist: pillow (>=12.2.0,<12.3.0)
23
+ Requires-Dist: pynacl (>=1.6.0,<1.7.0)
24
+ Requires-Dist: pyyaml (>=6.0.1,<6.1.0)
25
+ Requires-Dist: pyzbar (>=0.1.9,<0.2.0)
26
+ Requires-Dist: qrcode (>=8.0,<9.0)
27
+ Requires-Dist: ripemd-hash (>=1.0.1,<1.1.0)
28
+ Requires-Dist: safe-pysha3 (>=1.0.4,<1.1.0)
29
+ Project-URL: Repository, https://github.com/nemtus/symbol/tree/dev/sdk/python
30
+ Description-Content-Type: text/markdown
31
+
32
+ <!-- nemtus-mirror-notice -->
33
+ > **Note (nemtus mirror):** This package is a content mirror of the **Python** SDK
34
+ > from [`symbol/symbol`](https://github.com/symbol/symbol) (`sdk/python`, upstream
35
+ > PyPI name `symbol-sdk-python`). nemtus republishes it on PyPI as
36
+ > [`nemtus-symbol-sdk`](https://pypi.org/project/nemtus-symbol-sdk/); the only
37
+ > change from upstream is the published distribution name. The import module name
38
+ > is unchanged — you still `import symbolchain`. This is distinct from
39
+ > `@nemtus/symbol-sdk` on npm, which mirrors the JavaScript SDK. For the canonical
40
+ > project, see [symbol/symbol](https://github.com/symbol/symbol).
41
+ <!-- /nemtus-mirror-notice -->
42
+
43
+ # Symbol-SDK
44
+
45
+ [![lint][sdk-python-lint]][sdk-python-job] [![test][sdk-python-test]][sdk-python-job] [![vectors][sdk-python-vectors]][sdk-python-job] [![][sdk-python-cov]][sdk-python-cov-link] [![][sdk-python-package]][sdk-python-package-link]
46
+
47
+ [sdk-python-job]: https://jenkins.symbolsyndicate.us/blue/organizations/jenkins/Symbol%2Fgenerated%2Fsymbol%2Fpython/activity?branch=dev
48
+ [sdk-python-lint]: https://jenkins.symbolsyndicate.us/buildStatus/icon?job=Symbol%2Fgenerated%2Fsymbol%2Fpython%2Fdev%2F&config=sdk-python-lint
49
+ [sdk-python-build]: https://jenkins.symbolsyndicate.us/buildStatus/icon?job=Symbol%2Fgenerated%2Fsymbol%2Fpython%2Fdev%2F&config=sdk-python-build
50
+ [sdk-python-test]: https://jenkins.symbolsyndicate.us/buildStatus/icon?job=Symbol%2Fgenerated%2Fsymbol%2Fpython%2Fdev%2F&config=sdk-python-test
51
+ [sdk-python-examples]: https://jenkins.symbolsyndicate.us/buildStatus/icon?job=Symbol%2Fgenerated%2Fsymbol%2Fpython%2Fdev%2F&config=sdk-python-examples
52
+ [sdk-python-vectors]: https://jenkins.symbolsyndicate.us/buildStatus/icon?job=Symbol%2Fgenerated%2Fsymbol%2Fpython%2Fdev%2F&config=sdk-python-vectors
53
+ [sdk-python-cov]: https://codecov.io/gh/symbol/symbol/branch/dev/graph/badge.svg?token=SSYYBMK0M7&flag=sdk-python
54
+ [sdk-python-cov-link]: https://codecov.io/gh/symbol/symbol/tree/dev/sdk/python
55
+ [sdk-python-package]: https://img.shields.io/pypi/v/symbol-sdk-python
56
+ [sdk-python-package-link]: https://pypi.org/project/symbol-sdk-python
57
+
58
+ Python SDK for interacting with the Symbol and NEM blockchains.
59
+
60
+ Most common functionality is grouped under facades so that the same programming paradigm can be used for interacting with both Symbol and NEM.
61
+
62
+ ## Sending a Transaction
63
+
64
+ To send a transaction, first create a facade for the desired network:
65
+
66
+ _Symbol_
67
+ ```python
68
+ from symbolchain.CryptoTypes import PrivateKey
69
+ from symbolchain.facade.SymbolFacade import SymbolFacade
70
+
71
+
72
+ facade = SymbolFacade('testnet')
73
+ ```
74
+
75
+ _NEM_
76
+ ```python
77
+ from symbolchain.CryptoTypes import PrivateKey
78
+ from symbolchain.facade.NemFacade import NemFacade
79
+
80
+ facade = NemFacade('testnet')
81
+ ````
82
+
83
+ Second, describe the transaction using a Python dictionary. For example, a transfer transaction can be described as follows:
84
+
85
+ _Symbol_
86
+ ```python
87
+ transaction = facade.transaction_factory.create({
88
+ 'type': 'transfer_transaction_v1',
89
+ 'signer_public_key': '87DA603E7BE5656C45692D5FC7F6D0EF8F24BB7A5C10ED5FDA8C5CFBC49FCBC8',
90
+ 'fee': 1000000,
91
+ 'deadline': 41998024783,
92
+ 'recipient_address': 'TCHBDENCLKEBILBPWP3JPB2XNY64OE7PYHHE32I',
93
+ 'mosaics': [
94
+ {'mosaic_id': 0x7CDF3B117A3C40CC, 'amount': 1000000}
95
+ ]
96
+ })
97
+ ```
98
+
99
+ _NEM_
100
+ ```python
101
+ transaction = facade.transaction_factory.create({
102
+ 'type': 'transfer_transaction_v1',
103
+ 'signer_public_key': 'A59277D56E9F4FA46854F5EFAAA253B09F8AE69A473565E01FD9E6A738E4AB74',
104
+ 'fee': 0x186A0,
105
+ 'timestamp': 191205516,
106
+ 'deadline': 191291916,
107
+ 'recipient_address': 'TALICE5VF6J5FYMTCB7A3QG6OIRDRUXDWJGFVXNW',
108
+ 'amount': 5100000
109
+ })
110
+ ````
111
+
112
+ Third, sign the transaction and attach the signature:
113
+
114
+
115
+ ```python
116
+ private_key = PrivateKey('EDB671EB741BD676969D8A035271D1EE5E75DF33278083D877F23615EB839FEC')
117
+ signature = facade.sign_transaction(facade.KeyPair(private_key), transaction)
118
+
119
+ json_payload = facade.transaction_factory.attach_signature(transaction, signature)
120
+ ```
121
+
122
+ Finally, send the payload to the desired network using the specified node endpoint:
123
+
124
+ _Symbol_: PUT `/transactions`
125
+ <br>
126
+ _NEM_: POST `/transaction/announce`
127
+
128
+
129
+ ## NEM Cheat Sheet
130
+
131
+ In order to simplify the learning curve for NEM and Symbol usage, the SDK uses Symbol terminology for shared Symbol and NEM concepts.
132
+ Where appropriate, NEM terminology is replaced with Symbol terminology, including the names of many of the NEM transactions.
133
+ The mapping of NEM transactions to SDK descriptors can be found in the following table:
134
+
135
+ | NEM name (used in docs) | SDK descriptor name|
136
+ |--- |--- |
137
+ | ImportanceTransfer transaction | `account_key_link_transaction_v1` |
138
+ | MosaicDefinitionCreation transaction | `mosaic_definition_transaction_v1` |
139
+ | MosaicSupplyChange transaction | `mosaic_supply_change_transaction_v1` |
140
+ | MultisigAggregateModification transaction | `multisig_account_modification_transaction_v1`<br>`multisig_account_modification_transaction_v2` |
141
+ | MultisigSignature transaction or Cosignature transaction | `cosignature_v1` |
142
+ | Multisig transaction | `multisig_transaction_v1` |
143
+ | ProvisionNamespace transaction | `namespace_registration_transaction_v1` |
144
+ | Transfer transaction | `transfer_transaction_v1`<br>`transfer_transaction_v2` |
145
+
@@ -0,0 +1,113 @@
1
+ <!-- nemtus-mirror-notice -->
2
+ > **Note (nemtus mirror):** This package is a content mirror of the **Python** SDK
3
+ > from [`symbol/symbol`](https://github.com/symbol/symbol) (`sdk/python`, upstream
4
+ > PyPI name `symbol-sdk-python`). nemtus republishes it on PyPI as
5
+ > [`nemtus-symbol-sdk`](https://pypi.org/project/nemtus-symbol-sdk/); the only
6
+ > change from upstream is the published distribution name. The import module name
7
+ > is unchanged — you still `import symbolchain`. This is distinct from
8
+ > `@nemtus/symbol-sdk` on npm, which mirrors the JavaScript SDK. For the canonical
9
+ > project, see [symbol/symbol](https://github.com/symbol/symbol).
10
+ <!-- /nemtus-mirror-notice -->
11
+
12
+ # Symbol-SDK
13
+
14
+ [![lint][sdk-python-lint]][sdk-python-job] [![test][sdk-python-test]][sdk-python-job] [![vectors][sdk-python-vectors]][sdk-python-job] [![][sdk-python-cov]][sdk-python-cov-link] [![][sdk-python-package]][sdk-python-package-link]
15
+
16
+ [sdk-python-job]: https://jenkins.symbolsyndicate.us/blue/organizations/jenkins/Symbol%2Fgenerated%2Fsymbol%2Fpython/activity?branch=dev
17
+ [sdk-python-lint]: https://jenkins.symbolsyndicate.us/buildStatus/icon?job=Symbol%2Fgenerated%2Fsymbol%2Fpython%2Fdev%2F&config=sdk-python-lint
18
+ [sdk-python-build]: https://jenkins.symbolsyndicate.us/buildStatus/icon?job=Symbol%2Fgenerated%2Fsymbol%2Fpython%2Fdev%2F&config=sdk-python-build
19
+ [sdk-python-test]: https://jenkins.symbolsyndicate.us/buildStatus/icon?job=Symbol%2Fgenerated%2Fsymbol%2Fpython%2Fdev%2F&config=sdk-python-test
20
+ [sdk-python-examples]: https://jenkins.symbolsyndicate.us/buildStatus/icon?job=Symbol%2Fgenerated%2Fsymbol%2Fpython%2Fdev%2F&config=sdk-python-examples
21
+ [sdk-python-vectors]: https://jenkins.symbolsyndicate.us/buildStatus/icon?job=Symbol%2Fgenerated%2Fsymbol%2Fpython%2Fdev%2F&config=sdk-python-vectors
22
+ [sdk-python-cov]: https://codecov.io/gh/symbol/symbol/branch/dev/graph/badge.svg?token=SSYYBMK0M7&flag=sdk-python
23
+ [sdk-python-cov-link]: https://codecov.io/gh/symbol/symbol/tree/dev/sdk/python
24
+ [sdk-python-package]: https://img.shields.io/pypi/v/symbol-sdk-python
25
+ [sdk-python-package-link]: https://pypi.org/project/symbol-sdk-python
26
+
27
+ Python SDK for interacting with the Symbol and NEM blockchains.
28
+
29
+ Most common functionality is grouped under facades so that the same programming paradigm can be used for interacting with both Symbol and NEM.
30
+
31
+ ## Sending a Transaction
32
+
33
+ To send a transaction, first create a facade for the desired network:
34
+
35
+ _Symbol_
36
+ ```python
37
+ from symbolchain.CryptoTypes import PrivateKey
38
+ from symbolchain.facade.SymbolFacade import SymbolFacade
39
+
40
+
41
+ facade = SymbolFacade('testnet')
42
+ ```
43
+
44
+ _NEM_
45
+ ```python
46
+ from symbolchain.CryptoTypes import PrivateKey
47
+ from symbolchain.facade.NemFacade import NemFacade
48
+
49
+ facade = NemFacade('testnet')
50
+ ````
51
+
52
+ Second, describe the transaction using a Python dictionary. For example, a transfer transaction can be described as follows:
53
+
54
+ _Symbol_
55
+ ```python
56
+ transaction = facade.transaction_factory.create({
57
+ 'type': 'transfer_transaction_v1',
58
+ 'signer_public_key': '87DA603E7BE5656C45692D5FC7F6D0EF8F24BB7A5C10ED5FDA8C5CFBC49FCBC8',
59
+ 'fee': 1000000,
60
+ 'deadline': 41998024783,
61
+ 'recipient_address': 'TCHBDENCLKEBILBPWP3JPB2XNY64OE7PYHHE32I',
62
+ 'mosaics': [
63
+ {'mosaic_id': 0x7CDF3B117A3C40CC, 'amount': 1000000}
64
+ ]
65
+ })
66
+ ```
67
+
68
+ _NEM_
69
+ ```python
70
+ transaction = facade.transaction_factory.create({
71
+ 'type': 'transfer_transaction_v1',
72
+ 'signer_public_key': 'A59277D56E9F4FA46854F5EFAAA253B09F8AE69A473565E01FD9E6A738E4AB74',
73
+ 'fee': 0x186A0,
74
+ 'timestamp': 191205516,
75
+ 'deadline': 191291916,
76
+ 'recipient_address': 'TALICE5VF6J5FYMTCB7A3QG6OIRDRUXDWJGFVXNW',
77
+ 'amount': 5100000
78
+ })
79
+ ````
80
+
81
+ Third, sign the transaction and attach the signature:
82
+
83
+
84
+ ```python
85
+ private_key = PrivateKey('EDB671EB741BD676969D8A035271D1EE5E75DF33278083D877F23615EB839FEC')
86
+ signature = facade.sign_transaction(facade.KeyPair(private_key), transaction)
87
+
88
+ json_payload = facade.transaction_factory.attach_signature(transaction, signature)
89
+ ```
90
+
91
+ Finally, send the payload to the desired network using the specified node endpoint:
92
+
93
+ _Symbol_: PUT `/transactions`
94
+ <br>
95
+ _NEM_: POST `/transaction/announce`
96
+
97
+
98
+ ## NEM Cheat Sheet
99
+
100
+ In order to simplify the learning curve for NEM and Symbol usage, the SDK uses Symbol terminology for shared Symbol and NEM concepts.
101
+ Where appropriate, NEM terminology is replaced with Symbol terminology, including the names of many of the NEM transactions.
102
+ The mapping of NEM transactions to SDK descriptors can be found in the following table:
103
+
104
+ | NEM name (used in docs) | SDK descriptor name|
105
+ |--- |--- |
106
+ | ImportanceTransfer transaction | `account_key_link_transaction_v1` |
107
+ | MosaicDefinitionCreation transaction | `mosaic_definition_transaction_v1` |
108
+ | MosaicSupplyChange transaction | `mosaic_supply_change_transaction_v1` |
109
+ | MultisigAggregateModification transaction | `multisig_account_modification_transaction_v1`<br>`multisig_account_modification_transaction_v2` |
110
+ | MultisigSignature transaction or Cosignature transaction | `cosignature_v1` |
111
+ | Multisig transaction | `multisig_transaction_v1` |
112
+ | ProvisionNamespace transaction | `namespace_registration_transaction_v1` |
113
+ | Transfer transaction | `transfer_transaction_v1`<br>`transfer_transaction_v2` |
@@ -0,0 +1,29 @@
1
+ [tool.poetry]
2
+ name = 'nemtus-symbol-sdk'
3
+ version = '3.3.2'
4
+ description = 'Symbol Python SDK (nemtus mirror of upstream symbol-sdk-python; import module: symbolchain)'
5
+ authors = ['Symbol Contributors <contributors@symbol.dev>']
6
+ maintainers = ['Symbol Contributors <contributors@symbol.dev>']
7
+ license = 'MIT'
8
+
9
+ readme = 'README.md'
10
+
11
+ packages = [{ include = 'symbolchain' }]
12
+
13
+ repository = 'https://github.com/nemtus/symbol/tree/dev/sdk/python'
14
+
15
+ keywords = ['symbol', 'sdk', 'Symbol SDK']
16
+
17
+ classifiers = ['Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: 3.13', 'Programming Language :: Python :: 3.14']
18
+
19
+ [tool.poetry.dependencies]
20
+ python = "^3.10"
21
+ cryptography = ">=49.0.0,<49.1.0"
22
+ mnemonic = ">=0.20,<1.0"
23
+ pillow = ">=12.2.0,<12.3.0"
24
+ pynacl = ">=1.6.0,<1.7.0"
25
+ pyyaml = ">=6.0.1,<6.1.0"
26
+ pyzbar = ">=0.1.9,<0.2.0"
27
+ qrcode = ">=8.0,<9.0"
28
+ ripemd-hash = ">=1.0.1,<1.1.0"
29
+ safe-pysha3 = ">=1.0.4,<1.1.0"
@@ -0,0 +1,59 @@
1
+ import yaml
2
+
3
+ from .CryptoTypes import PublicKey
4
+
5
+
6
+ class AccountDescriptor:
7
+ """Represents an account."""
8
+
9
+ def __init__(self, descriptor_yaml):
10
+ """Creates a descriptor from a yaml container."""
11
+ self.public_key = descriptor_yaml.get('public_key')
12
+ if self.public_key:
13
+ self.public_key = PublicKey(self.public_key)
14
+
15
+ self.address = descriptor_yaml.get('address')
16
+ self.name = descriptor_yaml.get('name')
17
+ self.roles = descriptor_yaml.get('roles') or []
18
+
19
+
20
+ class AccountDescriptorRepository:
21
+ """Loads read-only account descriptors from YAML."""
22
+
23
+ def __init__(self, yaml_input):
24
+ """Loads account descriptors from the specified input."""
25
+ descriptors_yaml = yaml_input if isinstance(yaml_input, list) else yaml.load(yaml_input, Loader=yaml.SafeLoader)
26
+ self.descriptors = [AccountDescriptor(descriptor_yaml) for descriptor_yaml in descriptors_yaml]
27
+
28
+ def try_find_by_name(self, name):
29
+ """Finds the account descriptor with a matching name or None if no matching descriptors are found."""
30
+ return next((descriptor for descriptor in self.descriptors if name == descriptor.name), None)
31
+
32
+ def find_by_public_key(self, public_key):
33
+ """Finds the account descriptor with a matching public key."""
34
+ return next(descriptor for descriptor in self.descriptors if descriptor.public_key and public_key == descriptor.public_key)
35
+
36
+ def find_by_address(self, address):
37
+ """Finds the account descriptor with a matching address."""
38
+ return next(descriptor for descriptor in self.descriptors if descriptor.address and str(address) == descriptor.address)
39
+
40
+ def find_all_by_role(self, role):
41
+ """Finds all account descriptors with a matching role."""
42
+ return [descriptor for descriptor in self.descriptors if not role or role in descriptor.roles]
43
+
44
+ def _lookup_account_descriptor_field(self, value, property_name, target_class):
45
+ account_descriptor = self.try_find_by_name(value)
46
+ if account_descriptor and hasattr(account_descriptor, property_name):
47
+ return target_class(getattr(account_descriptor, property_name))
48
+
49
+ return target_class(value)
50
+
51
+ def _bind_lookup_account_descriptor_field(self, property_name, target_class):
52
+ return lambda value: self._lookup_account_descriptor_field(value, property_name, target_class)
53
+
54
+ def to_type_parsing_rules_map(self, type_to_property_mapping):
55
+ """Builds a type to parsing rule map."""
56
+ return {
57
+ target_class: self._bind_lookup_account_descriptor_field(property_name, target_class)
58
+ for target_class, property_name in type_to_property_mapping.items()
59
+ }
@@ -0,0 +1,116 @@
1
+ def read_array_impl(view, factory_class, accessor, should_continue):
2
+ elements = []
3
+ previous_element = None
4
+
5
+ i = 0
6
+ while should_continue(i, view):
7
+ element = factory_class.deserialize(view)
8
+
9
+ if element.size <= 0:
10
+ raise ValueError('element size has invalid size')
11
+
12
+ if accessor and previous_element and accessor(previous_element) >= accessor(element):
13
+ raise ValueError('elements in array are not sorted')
14
+
15
+ elements.append(element)
16
+ view = view[element.size:]
17
+
18
+ previous_element = element
19
+ i += 1
20
+
21
+ return elements
22
+
23
+
24
+ def write_array_impl(elements, count, accessor):
25
+ output_buffer = bytes()
26
+ for i in range(0, count):
27
+ element = elements[i]
28
+ if accessor and i > 0 and accessor(elements[i - 1]) >= accessor(element):
29
+ raise ValueError('array passed to write array is not sorted')
30
+
31
+ output_buffer += element.serialize()
32
+
33
+ return output_buffer
34
+
35
+
36
+ class ArrayHelpers:
37
+ @staticmethod
38
+ def get_bytes(view, size):
39
+ """Returns first size bytes of view."""
40
+ if size > len(view):
41
+ raise ValueError(f'size should not exceed {len(view)}. The value of size was: {size}.')
42
+
43
+ return view[:size].tobytes()
44
+
45
+ @staticmethod
46
+ def align_up(size, alignment):
47
+ """Calculates aligned size."""
48
+ return (size + alignment - 1) // alignment * alignment
49
+
50
+ @staticmethod
51
+ def size(elements, alignment=0, skip_last_element_padding=False):
52
+ """Calculates size of variable size objects."""
53
+ if not alignment:
54
+ return sum(map(lambda e: e.size, elements))
55
+
56
+ if not skip_last_element_padding:
57
+ return sum(map(lambda e: ArrayHelpers.align_up(e.size, alignment), elements))
58
+
59
+ return sum(map(lambda e: ArrayHelpers.align_up(e.size, alignment), elements[:-1])) + sum(map(lambda e: e.size, elements[-1:]))
60
+
61
+ @staticmethod
62
+ def read_array(view, factory_class, accessor=None):
63
+ """Reads array of objects."""
64
+ return read_array_impl(view, factory_class, accessor, lambda _, view: len(view) > 0)
65
+
66
+ @staticmethod
67
+ def read_array_count(view, factory_class, count, accessor=None):
68
+ """Reads array of deterministic number of objects."""
69
+ return read_array_impl(view, factory_class, accessor, lambda index, _: count > index)
70
+
71
+ @staticmethod
72
+ def read_variable_size_elements(view, factory_class, alignment, skip_last_element_padding=False):
73
+ """Reads array of variable size objects."""
74
+ elements = []
75
+ while len(view) > 0:
76
+ element = factory_class.deserialize(view)
77
+
78
+ if element.size <= 0:
79
+ raise ValueError('element size has invalid size')
80
+
81
+ elements.append(element)
82
+
83
+ aligned_size = ArrayHelpers.align_up(element.size, alignment)
84
+ if skip_last_element_padding and element.size >= len(view):
85
+ aligned_size = element.size
86
+
87
+ if aligned_size > len(view):
88
+ raise ValueError('unexpected buffer length')
89
+
90
+ view = view[aligned_size:]
91
+
92
+ return elements
93
+
94
+ @staticmethod
95
+ def write_array(elements, accessor=None):
96
+ """Writes array of objects."""
97
+ return write_array_impl(elements, len(elements), accessor)
98
+
99
+ @staticmethod
100
+ def write_array_count(elements, count, accessor=None):
101
+ """Writes array of deterministic number of objects."""
102
+ return write_array_impl(elements, count, accessor)
103
+
104
+ @staticmethod
105
+ def write_variable_size_elements(elements, alignment, skip_last_element_padding=False):
106
+ """Writes array of variable size objects."""
107
+ output_buffer = bytes()
108
+ for index, element in enumerate(elements):
109
+ output_buffer += element.serialize()
110
+
111
+ if not skip_last_element_padding or len(elements) - 1 != index:
112
+ aligned_size = ArrayHelpers.align_up(element.size, alignment)
113
+ if aligned_size != element.size:
114
+ output_buffer += bytes(aligned_size - element.size)
115
+
116
+ return output_buffer
@@ -0,0 +1,55 @@
1
+ from .Ordered import Ordered
2
+
3
+
4
+ class BaseValue(Ordered):
5
+ """Represents a base int."""
6
+
7
+ def __init__(self, size, value, tag=None, signed=False):
8
+ """Creates a base value."""
9
+ self.size = size
10
+ self.value = value
11
+ self.__tag = (tag, signed)
12
+
13
+ # check bounds
14
+ bit_size = self.size * 8
15
+ if signed:
16
+ upper_bound = (1 << (bit_size - 1)) - 1
17
+ lower_bound = -upper_bound - 1
18
+ else:
19
+ upper_bound = (1 << bit_size) - 1
20
+ lower_bound = 0
21
+
22
+ if self.value < lower_bound or self.value > upper_bound:
23
+ signed_description = 'signed' if signed else 'unsigned'
24
+ value_range_message = f'{value} must be in range [{lower_bound}, {upper_bound}]'
25
+ raise ValueError(f'{value_range_message} for {self.size} bytes ({signed_description})')
26
+
27
+ def _cmp(self, other, operation):
28
+ if not isinstance(other, BaseValue):
29
+ return NotImplemented
30
+
31
+ # pylint: disable=protected-access
32
+ return operation(self.value, other.value) and self.__tag == other.__tag
33
+
34
+ def __eq__(self, other):
35
+ # pylint: disable=protected-access
36
+ return isinstance(other, BaseValue) and self.value == other.value and self.__tag == other.__tag
37
+
38
+ def __ne__(self, other):
39
+ return not self == other
40
+
41
+ def __hash__(self):
42
+ return hash((self.value, self.__tag))
43
+
44
+ def __str__(self):
45
+ if not self.__tag[1] or self.value >= 0:
46
+ unsigned_value = self.value
47
+ else:
48
+ upper_bound_plus_one = 1 << (self.size * 8)
49
+ unsigned_value = self.value + upper_bound_plus_one
50
+
51
+ return f'0x{unsigned_value:0{self.size * 2}X}'
52
+
53
+ def to_json(self):
54
+ """Returns representation of this object that can be stored in JSON."""
55
+ return str(self.value) if 8 <= self.size else self.value
@@ -0,0 +1,56 @@
1
+ import hashlib
2
+ import hmac
3
+ import secrets
4
+
5
+ from mnemonic import Mnemonic
6
+
7
+ from .BufferWriter import BufferWriter
8
+ from .CryptoTypes import PrivateKey
9
+
10
+
11
+ class Bip32Node:
12
+ """Representation of a BIP32 node."""
13
+
14
+ def __init__(self, hmac_key, data):
15
+ """Creates a BIP32 node around a key and data."""
16
+ hmac_result = hmac.new(hmac_key, data, hashlib.sha512).digest()
17
+
18
+ self.private_key = PrivateKey(hmac_result[0:PrivateKey.SIZE])
19
+ self.chain_code = hmac_result[PrivateKey.SIZE:]
20
+
21
+ def derive_one(self, identifier):
22
+ """Derives a direct child node with specified identifier."""
23
+ hmac_data_writer = BufferWriter('big')
24
+ hmac_data_writer.write_int(0, 1)
25
+ hmac_data_writer.write_bytes(self.private_key.bytes)
26
+ hmac_data_writer.write_int(0x80000000 | identifier, 4)
27
+ return Bip32Node(self.chain_code, hmac_data_writer.buffer)
28
+
29
+ def derive_path(self, path):
30
+ """Derives a descendent node with specified path."""
31
+ next_node = self
32
+ for identifier in path:
33
+ next_node = next_node.derive_one(identifier)
34
+
35
+ return next_node
36
+
37
+
38
+ class Bip32:
39
+ """Factory of BIP32 root nodes """
40
+
41
+ def __init__(self, curve_name='ed25519', mnemonic_language='english'):
42
+ """Creates a BIP32 root node factory."""
43
+ self.root_hmac_key = (curve_name + ' seed').encode('utf8')
44
+ self.mnemonic_language = mnemonic_language
45
+
46
+ def from_seed(self, seed):
47
+ """Creates a BIP32 root node from a seed."""
48
+ return Bip32Node(self.root_hmac_key, seed)
49
+
50
+ def from_mnemonic(self, mnemonic, password):
51
+ """Creates a BIP32 root node from a BIP39 mnemonic and password."""
52
+ return self.from_seed(Mnemonic(self.mnemonic_language).to_seed(mnemonic, password))
53
+
54
+ def random(self, seed_length=32):
55
+ """Creates a random BIP32 mnemonic."""
56
+ return Mnemonic(self.mnemonic_language).to_mnemonic(secrets.token_bytes(seed_length))
@@ -0,0 +1,21 @@
1
+ import yaml
2
+
3
+ from .AccountDescriptorRepository import AccountDescriptorRepository
4
+ from .NodeDescriptorRepository import NodeDescriptorRepository
5
+
6
+
7
+ class BlockchainSettings:
8
+ """Settings describing a blockchain."""
9
+
10
+ def __init__(self, settings_dict):
11
+ """Creates blockchain settings from a dictionary."""
12
+ self.blockchain = settings_dict['blockchain']
13
+ self.network = settings_dict['network']
14
+ self.nodes = NodeDescriptorRepository(settings_dict['nodes'])
15
+ self.accounts = AccountDescriptorRepository(settings_dict['accounts'])
16
+
17
+ @staticmethod
18
+ def load_from_yaml(yaml_input):
19
+ """Loads settings from YAML."""
20
+ settings_yaml = yaml.load(yaml_input, Loader=yaml.SafeLoader)
21
+ return BlockchainSettings(settings_yaml)
@@ -0,0 +1,34 @@
1
+ from binascii import hexlify
2
+
3
+
4
+ class BufferReader:
5
+ """Reads data from an in memory buffer."""
6
+
7
+ def __init__(self, buffer, byte_order='little'):
8
+ """Creates a reader with specified byte order."""
9
+ self.buffer = buffer
10
+ self.byte_order = byte_order
11
+ self.offset = 0
12
+
13
+ @property
14
+ def eof(self):
15
+ """Returns true if the reader is at eof."""
16
+ return self.offset == len(self.buffer)
17
+
18
+ def read_int(self, count):
19
+ """Reads an integer."""
20
+ return int.from_bytes(self.read_bytes(count), self.byte_order)
21
+
22
+ def read_string(self, count):
23
+ """Reads a string."""
24
+ return self.read_bytes(count).decode('utf8')
25
+
26
+ def read_hex_string(self, count):
27
+ """Reads a hex string."""
28
+ return hexlify(self.read_bytes(count)).decode('utf8').upper()
29
+
30
+ def read_bytes(self, count):
31
+ """Reads bytes."""
32
+ buffer = self.buffer[self.offset:self.offset + count]
33
+ self.offset += count
34
+ return buffer