symbol-sdk-python 3.3.0__tar.gz → 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 (57) hide show
  1. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/PKG-INFO +5 -5
  2. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/README.md +102 -102
  3. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/pyproject.toml +5 -5
  4. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/ArrayHelpers.py +116 -116
  5. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/RuleBasedTransactionFactory.py +12 -1
  6. symbol_sdk_python-3.3.2/symbolchain/nem/FeeCalculator.py +94 -0
  7. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/nem/TransactionFactory.py +5 -5
  8. symbol_sdk_python-3.3.2/symbolchain/symbol/FeeCalculator.py +7 -0
  9. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/symbol/IdGenerator.py +8 -0
  10. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/symbol/Network.py +6 -1
  11. symbol_sdk_python-3.3.2/symbolchain/symbol/Restriction.py +10 -0
  12. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/symbol/TransactionFactory.py +4 -0
  13. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/AccountDescriptorRepository.py +0 -0
  14. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/BaseValue.py +0 -0
  15. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/Bip32.py +0 -0
  16. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/BlockchainSettings.py +0 -0
  17. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/BufferReader.py +0 -0
  18. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/BufferWriter.py +0 -0
  19. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/ByteArray.py +0 -0
  20. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/Cipher.py +0 -0
  21. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/CodeWordsEncoder.py +0 -0
  22. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/CryptoTypes.py +0 -0
  23. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/DiceMnemonicGenerator.py +0 -0
  24. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/Network.py +0 -0
  25. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/NetworkTimestamp.py +0 -0
  26. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/NodeDescriptorRepository.py +0 -0
  27. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/Ordered.py +0 -0
  28. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/PrivateKeyStorage.py +0 -0
  29. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/QrSignatureStorage.py +0 -0
  30. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/QrStorage.py +0 -0
  31. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/SharedKey.py +0 -0
  32. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/TransactionDescriptorProcessor.py +0 -0
  33. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/Transforms.py +0 -0
  34. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/__init__.py +0 -0
  35. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/external/__init__.py +0 -0
  36. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/external/ed25519.py +0 -0
  37. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/facade/BatchOperations.py +0 -0
  38. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/facade/NemFacade.py +0 -0
  39. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/facade/SymbolFacade.py +0 -0
  40. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/facade/__init__.py +0 -0
  41. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/impl/CipherHelpers.py +0 -0
  42. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/impl/__init__.py +0 -0
  43. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/nc/__init__.py +0 -0
  44. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/nem/KeyPair.py +0 -0
  45. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/nem/MessageEncoder.py +0 -0
  46. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/nem/Network.py +0 -0
  47. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/nem/SharedKey.py +0 -0
  48. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/nem/__init__.py +0 -0
  49. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/ripemd160.py +0 -0
  50. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/sc/__init__.py +0 -0
  51. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/symbol/KeyPair.py +0 -0
  52. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/symbol/Merkle.py +0 -0
  53. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/symbol/MessageEncoder.py +0 -0
  54. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/symbol/Metadata.py +0 -0
  55. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/symbol/SharedKey.py +0 -0
  56. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/symbol/VotingKeysGenerator.py +0 -0
  57. {symbol_sdk_python-3.3.0 → symbol_sdk_python-3.3.2}/symbolchain/symbol/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: symbol-sdk-python
3
- Version: 3.3.0
3
+ Version: 3.3.2
4
4
  Summary: Symbol SDK
5
5
  Home-page: https://github.com/symbol/symbol/tree/main/sdk/python
6
6
  License: MIT
@@ -9,17 +9,17 @@ Author: Symbol Contributors
9
9
  Author-email: contributors@symbol.dev
10
10
  Maintainer: Symbol Contributors
11
11
  Maintainer-email: contributors@symbol.dev
12
- Requires-Python: >=3.9.2,<4.0.0
12
+ Requires-Python: >=3.10,<4.0
13
13
  Classifier: License :: OSI Approved :: MIT License
14
14
  Classifier: Programming Language :: Python :: 3
15
15
  Classifier: Programming Language :: Python :: 3.10
16
16
  Classifier: Programming Language :: Python :: 3.11
17
17
  Classifier: Programming Language :: Python :: 3.12
18
18
  Classifier: Programming Language :: Python :: 3.13
19
- Classifier: Programming Language :: Python :: 3.9
20
- Requires-Dist: cryptography (>=46.0.1,<46.1.0)
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Requires-Dist: cryptography (>=49.0.0,<49.1.0)
21
21
  Requires-Dist: mnemonic (>=0.20,<1.0)
22
- Requires-Dist: pillow (>=11.3.0,<11.4.0)
22
+ Requires-Dist: pillow (>=12.2.0,<12.3.0)
23
23
  Requires-Dist: pynacl (>=1.6.0,<1.7.0)
24
24
  Requires-Dist: pyyaml (>=6.0.1,<6.1.0)
25
25
  Requires-Dist: pyzbar (>=0.1.9,<0.2.0)
@@ -1,102 +1,102 @@
1
- # Symbol-SDK
2
-
3
- [![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]
4
-
5
- [sdk-python-job]: https://jenkins.symbolsyndicate.us/blue/organizations/jenkins/Symbol%2Fgenerated%2Fsymbol%2Fpython/activity?branch=dev
6
- [sdk-python-lint]: https://jenkins.symbolsyndicate.us/buildStatus/icon?job=Symbol%2Fgenerated%2Fsymbol%2Fpython%2Fdev%2F&config=sdk-python-lint
7
- [sdk-python-build]: https://jenkins.symbolsyndicate.us/buildStatus/icon?job=Symbol%2Fgenerated%2Fsymbol%2Fpython%2Fdev%2F&config=sdk-python-build
8
- [sdk-python-test]: https://jenkins.symbolsyndicate.us/buildStatus/icon?job=Symbol%2Fgenerated%2Fsymbol%2Fpython%2Fdev%2F&config=sdk-python-test
9
- [sdk-python-examples]: https://jenkins.symbolsyndicate.us/buildStatus/icon?job=Symbol%2Fgenerated%2Fsymbol%2Fpython%2Fdev%2F&config=sdk-python-examples
10
- [sdk-python-vectors]: https://jenkins.symbolsyndicate.us/buildStatus/icon?job=Symbol%2Fgenerated%2Fsymbol%2Fpython%2Fdev%2F&config=sdk-python-vectors
11
- [sdk-python-cov]: https://codecov.io/gh/symbol/symbol/branch/dev/graph/badge.svg?token=SSYYBMK0M7&flag=sdk-python
12
- [sdk-python-cov-link]: https://codecov.io/gh/symbol/symbol/tree/dev/sdk/python
13
- [sdk-python-package]: https://img.shields.io/pypi/v/symbol-sdk-python
14
- [sdk-python-package-link]: https://pypi.org/project/symbol-sdk-python
15
-
16
- Python SDK for interacting with the Symbol and NEM blockchains.
17
-
18
- Most common functionality is grouped under facades so that the same programming paradigm can be used for interacting with both Symbol and NEM.
19
-
20
- ## Sending a Transaction
21
-
22
- To send a transaction, first create a facade for the desired network:
23
-
24
- _Symbol_
25
- ```python
26
- from symbolchain.CryptoTypes import PrivateKey
27
- from symbolchain.facade.SymbolFacade import SymbolFacade
28
-
29
-
30
- facade = SymbolFacade('testnet')
31
- ```
32
-
33
- _NEM_
34
- ```python
35
- from symbolchain.CryptoTypes import PrivateKey
36
- from symbolchain.facade.NemFacade import NemFacade
37
-
38
- facade = NemFacade('testnet')
39
- ````
40
-
41
- Second, describe the transaction using a Python dictionary. For example, a transfer transaction can be described as follows:
42
-
43
- _Symbol_
44
- ```python
45
- transaction = facade.transaction_factory.create({
46
- 'type': 'transfer_transaction_v1',
47
- 'signer_public_key': '87DA603E7BE5656C45692D5FC7F6D0EF8F24BB7A5C10ED5FDA8C5CFBC49FCBC8',
48
- 'fee': 1000000,
49
- 'deadline': 41998024783,
50
- 'recipient_address': 'TCHBDENCLKEBILBPWP3JPB2XNY64OE7PYHHE32I',
51
- 'mosaics': [
52
- {'mosaic_id': 0x7CDF3B117A3C40CC, 'amount': 1000000}
53
- ]
54
- })
55
- ```
56
-
57
- _NEM_
58
- ```python
59
- transaction = facade.transaction_factory.create({
60
- 'type': 'transfer_transaction_v1',
61
- 'signer_public_key': 'A59277D56E9F4FA46854F5EFAAA253B09F8AE69A473565E01FD9E6A738E4AB74',
62
- 'fee': 0x186A0,
63
- 'timestamp': 191205516,
64
- 'deadline': 191291916,
65
- 'recipient_address': 'TALICE5VF6J5FYMTCB7A3QG6OIRDRUXDWJGFVXNW',
66
- 'amount': 5100000
67
- })
68
- ````
69
-
70
- Third, sign the transaction and attach the signature:
71
-
72
-
73
- ```python
74
- private_key = PrivateKey('EDB671EB741BD676969D8A035271D1EE5E75DF33278083D877F23615EB839FEC')
75
- signature = facade.sign_transaction(facade.KeyPair(private_key), transaction)
76
-
77
- json_payload = facade.transaction_factory.attach_signature(transaction, signature)
78
- ```
79
-
80
- Finally, send the payload to the desired network using the specified node endpoint:
81
-
82
- _Symbol_: PUT `/transactions`
83
- <br>
84
- _NEM_: POST `/transaction/announce`
85
-
86
-
87
- ## NEM Cheat Sheet
88
-
89
- In order to simplify the learning curve for NEM and Symbol usage, the SDK uses Symbol terminology for shared Symbol and NEM concepts.
90
- Where appropriate, NEM terminology is replaced with Symbol terminology, including the names of many of the NEM transactions.
91
- The mapping of NEM transactions to SDK descriptors can be found in the following table:
92
-
93
- | NEM name (used in docs) | SDK descriptor name|
94
- |--- |--- |
95
- | ImportanceTransfer transaction | `account_key_link_transaction_v1` |
96
- | MosaicDefinitionCreation transaction | `mosaic_definition_transaction_v1` |
97
- | MosaicSupplyChange transaction | `mosaic_supply_change_transaction_v1` |
98
- | MultisigAggregateModification transaction | `multisig_account_modification_transaction_v1`<br>`multisig_account_modification_transaction_v2` |
99
- | MultisigSignature transaction or Cosignature transaction | `cosignature_v1` |
100
- | Multisig transaction | `multisig_transaction_v1` |
101
- | ProvisionNamespace transaction | `namespace_registration_transaction_v1` |
102
- | Transfer transaction | `transfer_transaction_v1`<br>`transfer_transaction_v2` |
1
+ # Symbol-SDK
2
+
3
+ [![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]
4
+
5
+ [sdk-python-job]: https://jenkins.symbolsyndicate.us/blue/organizations/jenkins/Symbol%2Fgenerated%2Fsymbol%2Fpython/activity?branch=dev
6
+ [sdk-python-lint]: https://jenkins.symbolsyndicate.us/buildStatus/icon?job=Symbol%2Fgenerated%2Fsymbol%2Fpython%2Fdev%2F&config=sdk-python-lint
7
+ [sdk-python-build]: https://jenkins.symbolsyndicate.us/buildStatus/icon?job=Symbol%2Fgenerated%2Fsymbol%2Fpython%2Fdev%2F&config=sdk-python-build
8
+ [sdk-python-test]: https://jenkins.symbolsyndicate.us/buildStatus/icon?job=Symbol%2Fgenerated%2Fsymbol%2Fpython%2Fdev%2F&config=sdk-python-test
9
+ [sdk-python-examples]: https://jenkins.symbolsyndicate.us/buildStatus/icon?job=Symbol%2Fgenerated%2Fsymbol%2Fpython%2Fdev%2F&config=sdk-python-examples
10
+ [sdk-python-vectors]: https://jenkins.symbolsyndicate.us/buildStatus/icon?job=Symbol%2Fgenerated%2Fsymbol%2Fpython%2Fdev%2F&config=sdk-python-vectors
11
+ [sdk-python-cov]: https://codecov.io/gh/symbol/symbol/branch/dev/graph/badge.svg?token=SSYYBMK0M7&flag=sdk-python
12
+ [sdk-python-cov-link]: https://codecov.io/gh/symbol/symbol/tree/dev/sdk/python
13
+ [sdk-python-package]: https://img.shields.io/pypi/v/symbol-sdk-python
14
+ [sdk-python-package-link]: https://pypi.org/project/symbol-sdk-python
15
+
16
+ Python SDK for interacting with the Symbol and NEM blockchains.
17
+
18
+ Most common functionality is grouped under facades so that the same programming paradigm can be used for interacting with both Symbol and NEM.
19
+
20
+ ## Sending a Transaction
21
+
22
+ To send a transaction, first create a facade for the desired network:
23
+
24
+ _Symbol_
25
+ ```python
26
+ from symbolchain.CryptoTypes import PrivateKey
27
+ from symbolchain.facade.SymbolFacade import SymbolFacade
28
+
29
+
30
+ facade = SymbolFacade('testnet')
31
+ ```
32
+
33
+ _NEM_
34
+ ```python
35
+ from symbolchain.CryptoTypes import PrivateKey
36
+ from symbolchain.facade.NemFacade import NemFacade
37
+
38
+ facade = NemFacade('testnet')
39
+ ````
40
+
41
+ Second, describe the transaction using a Python dictionary. For example, a transfer transaction can be described as follows:
42
+
43
+ _Symbol_
44
+ ```python
45
+ transaction = facade.transaction_factory.create({
46
+ 'type': 'transfer_transaction_v1',
47
+ 'signer_public_key': '87DA603E7BE5656C45692D5FC7F6D0EF8F24BB7A5C10ED5FDA8C5CFBC49FCBC8',
48
+ 'fee': 1000000,
49
+ 'deadline': 41998024783,
50
+ 'recipient_address': 'TCHBDENCLKEBILBPWP3JPB2XNY64OE7PYHHE32I',
51
+ 'mosaics': [
52
+ {'mosaic_id': 0x7CDF3B117A3C40CC, 'amount': 1000000}
53
+ ]
54
+ })
55
+ ```
56
+
57
+ _NEM_
58
+ ```python
59
+ transaction = facade.transaction_factory.create({
60
+ 'type': 'transfer_transaction_v1',
61
+ 'signer_public_key': 'A59277D56E9F4FA46854F5EFAAA253B09F8AE69A473565E01FD9E6A738E4AB74',
62
+ 'fee': 0x186A0,
63
+ 'timestamp': 191205516,
64
+ 'deadline': 191291916,
65
+ 'recipient_address': 'TALICE5VF6J5FYMTCB7A3QG6OIRDRUXDWJGFVXNW',
66
+ 'amount': 5100000
67
+ })
68
+ ````
69
+
70
+ Third, sign the transaction and attach the signature:
71
+
72
+
73
+ ```python
74
+ private_key = PrivateKey('EDB671EB741BD676969D8A035271D1EE5E75DF33278083D877F23615EB839FEC')
75
+ signature = facade.sign_transaction(facade.KeyPair(private_key), transaction)
76
+
77
+ json_payload = facade.transaction_factory.attach_signature(transaction, signature)
78
+ ```
79
+
80
+ Finally, send the payload to the desired network using the specified node endpoint:
81
+
82
+ _Symbol_: PUT `/transactions`
83
+ <br>
84
+ _NEM_: POST `/transaction/announce`
85
+
86
+
87
+ ## NEM Cheat Sheet
88
+
89
+ In order to simplify the learning curve for NEM and Symbol usage, the SDK uses Symbol terminology for shared Symbol and NEM concepts.
90
+ Where appropriate, NEM terminology is replaced with Symbol terminology, including the names of many of the NEM transactions.
91
+ The mapping of NEM transactions to SDK descriptors can be found in the following table:
92
+
93
+ | NEM name (used in docs) | SDK descriptor name|
94
+ |--- |--- |
95
+ | ImportanceTransfer transaction | `account_key_link_transaction_v1` |
96
+ | MosaicDefinitionCreation transaction | `mosaic_definition_transaction_v1` |
97
+ | MosaicSupplyChange transaction | `mosaic_supply_change_transaction_v1` |
98
+ | MultisigAggregateModification transaction | `multisig_account_modification_transaction_v1`<br>`multisig_account_modification_transaction_v2` |
99
+ | MultisigSignature transaction or Cosignature transaction | `cosignature_v1` |
100
+ | Multisig transaction | `multisig_transaction_v1` |
101
+ | ProvisionNamespace transaction | `namespace_registration_transaction_v1` |
102
+ | Transfer transaction | `transfer_transaction_v1`<br>`transfer_transaction_v2` |
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = 'symbol-sdk-python'
3
- version = '3.3.0'
3
+ version = '3.3.2'
4
4
  description = 'Symbol SDK'
5
5
  authors = ['Symbol Contributors <contributors@symbol.dev>']
6
6
  maintainers = ['Symbol Contributors <contributors@symbol.dev>']
@@ -14,13 +14,13 @@ repository = 'https://github.com/symbol/symbol/tree/main/sdk/python'
14
14
 
15
15
  keywords = ['symbol', 'sdk', 'Symbol SDK']
16
16
 
17
- classifiers = ['Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11']
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
18
 
19
19
  [tool.poetry.dependencies]
20
- python = "^3.9.2"
21
- cryptography = ">=46.0.1,<46.1.0"
20
+ python = "^3.10"
21
+ cryptography = ">=49.0.0,<49.1.0"
22
22
  mnemonic = ">=0.20,<1.0"
23
- pillow = ">=11.3.0,<11.4.0"
23
+ pillow = ">=12.2.0,<12.3.0"
24
24
  pynacl = ">=1.6.0,<1.7.0"
25
25
  pyyaml = ">=6.0.1,<6.1.0"
26
26
  pyzbar = ">=0.1.9,<0.2.0"
@@ -1,116 +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
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
@@ -161,6 +161,17 @@ class RuleBasedTransactionFactory:
161
161
 
162
162
  @staticmethod
163
163
  def _auto_encode_strings(entity):
164
+ if not hasattr(entity, '__dict__'): # skip non-objects
165
+ return
166
+
164
167
  for key, value in vars(entity).items():
168
+ if '_' == key[0] and '_' == key[-1]: # skip system properties
169
+ continue
170
+
165
171
  if isinstance(value, str):
166
- setattr(entity, key, value.encode('utf8'))
172
+ setattr(entity, key, value.encode('utf8')) # convert string literals to byte array
173
+ elif hasattr(value, '__len__'): # process list items
174
+ for item in value:
175
+ RuleBasedTransactionFactory._auto_encode_strings(item)
176
+ else:
177
+ RuleBasedTransactionFactory._auto_encode_strings(value) # process nested objects
@@ -0,0 +1,94 @@
1
+ import math
2
+
3
+ from symbolchain.nc import TransactionType
4
+
5
+
6
+ def calculate_mosaic_rental_fee():
7
+ """Calculates the minimum required mosaic rental fee."""
8
+
9
+ return 10 * 1_000_000
10
+
11
+
12
+ def calculate_namespace_rental_fee(is_root):
13
+ """
14
+ Calculates the minimum required namespace rental fee.
15
+ When is_root is True, calculates the root namespace fee.
16
+ Otherwise, calculates the child namespace fee.
17
+ """
18
+
19
+ return (100 if is_root else 10) * 1_000_000
20
+
21
+
22
+ def _decode_mosaic_id(mosaic_id):
23
+ return {'namespace_id': {'name': mosaic_id.namespace_id.name.decode('utf8')}, 'name': mosaic_id.name.decode('utf8')}
24
+
25
+
26
+ def _calculate_unweighted_transfer_fee(transaction, mosaic_information_lookup):
27
+ xem_supply = 8_999_999_999
28
+ max_mosaic_units = 9_000_000_000_000_000
29
+
30
+ def _calculate_xem_transfer_fee(amount):
31
+ return int(min(25, max(1, amount // 10000)))
32
+
33
+ def _calculate_mosaic_total_quantity(mosaic_information):
34
+ return mosaic_information['supply'] * (10 ** mosaic_information['divisibility'])
35
+
36
+ def _calculate_xem_equivalent(amount, mosaic_amount, mosaic_information):
37
+ if 0 == mosaic_information['supply']:
38
+ return 0
39
+
40
+ # amount XEM whole units
41
+ # mosaic_amount mosaic atomic units
42
+ # xem_supply / _calculate_mosaic_total_quantity(mosaic_information) convert mosaic_amount from mosaic units to XEM equivalent units
43
+ return amount * mosaic_amount * xem_supply / _calculate_mosaic_total_quantity(mosaic_information)
44
+
45
+ def _calculate_mosaic_transfer_fee(amount, mosaic, mosaic_information):
46
+ if 0 == mosaic_information['divisibility'] and 10_000 >= mosaic_information['supply']:
47
+ return 1
48
+
49
+ xem_equivalent = _calculate_xem_equivalent(amount, mosaic.mosaic.amount.value, mosaic_information)
50
+ xem_fee = _calculate_xem_transfer_fee(xem_equivalent)
51
+ mosaic_total_quantity = _calculate_mosaic_total_quantity(mosaic_information)
52
+ supply_related_adjustment = 0 if 0 == mosaic_total_quantity else int(0.8 * math.log(max_mosaic_units / mosaic_total_quantity))
53
+ return max(1, xem_fee - supply_related_adjustment)
54
+
55
+ message_fee = 0 if not transaction.message else len(transaction.message.message) // 32 + 1
56
+ amount = transaction.amount.value // 1_000_000 # convert to XEM whole units
57
+ if not transaction.mosaics:
58
+ transfer_fee = _calculate_xem_transfer_fee(amount)
59
+ return message_fee + transfer_fee
60
+
61
+ def _lookup_and_calculate_mosaic_transfer_fee(mosaic):
62
+ mosaic_id = _decode_mosaic_id(mosaic.mosaic.mosaic_id)
63
+ mosaic_information = mosaic_information_lookup(mosaic_id)
64
+ if not mosaic_information:
65
+ raise ValueError(f'unable to find fee information for {mosaic_id["namespace_id"]["name"]}:{mosaic_id["name"]}')
66
+
67
+ return _calculate_mosaic_transfer_fee(amount, mosaic, mosaic_information)
68
+
69
+ transfer_fee = sum(_lookup_and_calculate_mosaic_transfer_fee(mosaic) for mosaic in transaction.mosaics)
70
+ return message_fee + transfer_fee
71
+
72
+
73
+ def calculate_transaction_fee(transaction, mosaic_information_lookup=None):
74
+ """
75
+ Calculates the minimum required transaction fee for a transaction.
76
+ mosaic_information_lookup looks up mosaic information ({supply, divisibility}) given mosaic identifier.
77
+ When a function, mosaic identifier will be passed as parameter.
78
+ When an object map, fully qualified mosaic identifier will be used as an index.
79
+ When None, this function will be unable to calculate fees for custom mosaic transfers.
80
+ """
81
+
82
+ def _weight_with_fee_unit(amount):
83
+ return amount * 50_000
84
+
85
+ if TransactionType.TRANSFER != transaction.type_:
86
+ return _weight_with_fee_unit(10 if TransactionType.MULTISIG_ACCOUNT_MODIFICATION == transaction.type_ else 3)
87
+
88
+ def _make_lookup_function(lookup):
89
+ if callable(lookup):
90
+ return lookup
91
+
92
+ return lambda mosaic_id: lookup[f'{mosaic_id["namespace_id"]["name"]}:{mosaic_id["name"]}']
93
+
94
+ return _weight_with_fee_unit(_calculate_unweighted_transfer_fee(transaction, _make_lookup_function(mosaic_information_lookup)))
@@ -33,10 +33,6 @@ class TransactionFactory:
33
33
  if autosort:
34
34
  transaction.sort()
35
35
 
36
- # hack: explicitly translate transfer message
37
- if nc.TransactionType.TRANSFER == transaction.type_ and transaction.message and isinstance(transaction.message.message, str):
38
- transaction.message.message = transaction.message.message.encode('utf8')
39
-
40
36
  return transaction
41
37
 
42
38
  @staticmethod
@@ -66,9 +62,13 @@ class TransactionFactory:
66
62
  def attach_signature(transaction, signature):
67
63
  """Attaches a signature to a transaction."""
68
64
  transaction.signature = nc.Signature(signature.bytes)
65
+ return TransactionFactory.to_json(transaction)
69
66
 
67
+ @staticmethod
68
+ def to_json(transaction):
69
+ """Generates a JSON representation of transaction that can be sent to a node."""
70
70
  transaction_hex = hexlify(TransactionFactory.to_non_verifiable_transaction(transaction).serialize()).decode('utf8').upper()
71
- signature_hex = str(signature)
71
+ signature_hex = str(transaction.signature)
72
72
  json_payload = f'{{"data":"{transaction_hex}", "signature":"{signature_hex}"}}'
73
73
  return json_payload
74
74
 
@@ -0,0 +1,7 @@
1
+ from symbolchain.sc import Cosignature
2
+
3
+
4
+ def calculate_transaction_fee(transaction, fee_multiplier, cosignature_count=0):
5
+ """Calculates the minimum required transaction fee for a transaction."""
6
+
7
+ return transaction.size * fee_multiplier + Cosignature().size * cosignature_count
@@ -19,6 +19,9 @@ def generate_mosaic_id(owner_address, nonce):
19
19
 
20
20
  def generate_namespace_id(name, parent_namespace_id=0):
21
21
  """Generates a namespace id from a name and an optional parent namespace id."""
22
+ if '.' in name:
23
+ raise ValueError(f'\'name\' cannot contain \'.\'; if {name} is a namepace path, consider using generate_namespace_path')
24
+
22
25
  hasher = hashlib.sha3_256()
23
26
  hasher.update(parent_namespace_id.to_bytes(8, 'little'))
24
27
  hasher.update(name.encode('utf8'))
@@ -33,6 +36,11 @@ def generate_mosaic_alias_id(fully_qualified_name):
33
36
  return generate_namespace_path(fully_qualified_name)[-1]
34
37
 
35
38
 
39
+ def is_mosaic_alias(mosaic_id):
40
+ """Determines if mosaic_id is an alias."""
41
+ return 0 != (mosaic_id & NAMESPACE_FLAG)
42
+
43
+
36
44
  def is_valid_namespace_name(name):
37
45
  """Returns true if a name is a valid namespace name."""
38
46
  def is_alphanum(character):
@@ -41,11 +41,16 @@ class Address(ByteArray):
41
41
  def to_namespace_id(self):
42
42
  """Attempts to convert this address into a namespace id."""
43
43
 
44
- if not self.bytes[0] & 0x01:
44
+ if not self.is_alias():
45
45
  return None
46
46
 
47
47
  return NamespaceId(int.from_bytes(self.bytes[1:9], 'little'))
48
48
 
49
+ def is_alias(self):
50
+ """Determines if this address is an alias."""
51
+
52
+ return 0 != (self.bytes[0] & 0x01)
53
+
49
54
  def __str__(self):
50
55
  return base64.b32encode(self.bytes + bytes(0)).decode('utf8')[0:-1]
51
56
 
@@ -0,0 +1,10 @@
1
+ import hashlib
2
+
3
+
4
+ def mosaic_restriction_generate_key(seed):
5
+ """Generates a mosaic restriction key from a string."""
6
+
7
+ hash_result = hashlib.sha3_256(seed.encode('utf8')).digest()
8
+ key_bytes = bytearray(hash_result[:8])
9
+
10
+ return int.from_bytes(key_bytes, 'little')
@@ -69,7 +69,11 @@ class TransactionFactory:
69
69
  def attach_signature(transaction, signature):
70
70
  """Attaches a signature to a transaction."""
71
71
  transaction.signature = sc.Signature(signature.bytes)
72
+ return TransactionFactory.to_json(transaction)
72
73
 
74
+ @staticmethod
75
+ def to_json(transaction):
76
+ """Generates a JSON representation of transaction that can be sent to a node."""
73
77
  transaction_buffer = transaction.serialize()
74
78
  hex_payload = hexlify(transaction_buffer).decode('utf8').upper()
75
79
  json_payload = f'{{"payload": "{hex_payload}"}}'