hive-nectar 0.2.9__py3-none-any.whl

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 (87) hide show
  1. hive_nectar-0.2.9.dist-info/METADATA +194 -0
  2. hive_nectar-0.2.9.dist-info/RECORD +87 -0
  3. hive_nectar-0.2.9.dist-info/WHEEL +4 -0
  4. hive_nectar-0.2.9.dist-info/entry_points.txt +2 -0
  5. hive_nectar-0.2.9.dist-info/licenses/LICENSE.txt +23 -0
  6. nectar/__init__.py +37 -0
  7. nectar/account.py +5076 -0
  8. nectar/amount.py +553 -0
  9. nectar/asciichart.py +303 -0
  10. nectar/asset.py +122 -0
  11. nectar/block.py +574 -0
  12. nectar/blockchain.py +1242 -0
  13. nectar/blockchaininstance.py +2590 -0
  14. nectar/blockchainobject.py +263 -0
  15. nectar/cli.py +5937 -0
  16. nectar/comment.py +1552 -0
  17. nectar/community.py +854 -0
  18. nectar/constants.py +95 -0
  19. nectar/discussions.py +1437 -0
  20. nectar/exceptions.py +152 -0
  21. nectar/haf.py +381 -0
  22. nectar/hive.py +630 -0
  23. nectar/imageuploader.py +114 -0
  24. nectar/instance.py +113 -0
  25. nectar/market.py +876 -0
  26. nectar/memo.py +542 -0
  27. nectar/message.py +379 -0
  28. nectar/nodelist.py +309 -0
  29. nectar/price.py +603 -0
  30. nectar/profile.py +74 -0
  31. nectar/py.typed +0 -0
  32. nectar/rc.py +333 -0
  33. nectar/snapshot.py +1024 -0
  34. nectar/storage.py +62 -0
  35. nectar/transactionbuilder.py +659 -0
  36. nectar/utils.py +630 -0
  37. nectar/version.py +3 -0
  38. nectar/vote.py +722 -0
  39. nectar/wallet.py +472 -0
  40. nectar/witness.py +728 -0
  41. nectarapi/__init__.py +12 -0
  42. nectarapi/exceptions.py +126 -0
  43. nectarapi/graphenerpc.py +596 -0
  44. nectarapi/node.py +194 -0
  45. nectarapi/noderpc.py +79 -0
  46. nectarapi/openapi.py +107 -0
  47. nectarapi/py.typed +0 -0
  48. nectarapi/rpcutils.py +98 -0
  49. nectarapi/version.py +3 -0
  50. nectarbase/__init__.py +15 -0
  51. nectarbase/ledgertransactions.py +106 -0
  52. nectarbase/memo.py +242 -0
  53. nectarbase/objects.py +521 -0
  54. nectarbase/objecttypes.py +21 -0
  55. nectarbase/operationids.py +102 -0
  56. nectarbase/operations.py +1357 -0
  57. nectarbase/py.typed +0 -0
  58. nectarbase/signedtransactions.py +89 -0
  59. nectarbase/transactions.py +11 -0
  60. nectarbase/version.py +3 -0
  61. nectargraphenebase/__init__.py +27 -0
  62. nectargraphenebase/account.py +1121 -0
  63. nectargraphenebase/aes.py +49 -0
  64. nectargraphenebase/base58.py +197 -0
  65. nectargraphenebase/bip32.py +575 -0
  66. nectargraphenebase/bip38.py +110 -0
  67. nectargraphenebase/chains.py +15 -0
  68. nectargraphenebase/dictionary.py +2 -0
  69. nectargraphenebase/ecdsasig.py +309 -0
  70. nectargraphenebase/objects.py +130 -0
  71. nectargraphenebase/objecttypes.py +8 -0
  72. nectargraphenebase/operationids.py +5 -0
  73. nectargraphenebase/operations.py +25 -0
  74. nectargraphenebase/prefix.py +13 -0
  75. nectargraphenebase/py.typed +0 -0
  76. nectargraphenebase/signedtransactions.py +221 -0
  77. nectargraphenebase/types.py +557 -0
  78. nectargraphenebase/unsignedtransactions.py +288 -0
  79. nectargraphenebase/version.py +3 -0
  80. nectarstorage/__init__.py +57 -0
  81. nectarstorage/base.py +317 -0
  82. nectarstorage/exceptions.py +15 -0
  83. nectarstorage/interfaces.py +244 -0
  84. nectarstorage/masterpassword.py +237 -0
  85. nectarstorage/py.typed +0 -0
  86. nectarstorage/ram.py +27 -0
  87. nectarstorage/sqlite.py +343 -0
@@ -0,0 +1,244 @@
1
+ # Inspired by https://raw.githubusercontent.com/xeroc/python-graphenelib/master/graphenestorage/interfaces.py
2
+ from collections.abc import MutableMapping
3
+ from typing import Any, Iterator
4
+
5
+
6
+ class StoreInterface(MutableMapping):
7
+ """The store interface is the most general store that we can have.
8
+
9
+ It behaves like a dictionary but allows returning None for missing keys and
10
+ keeps a `defaults` mapping that can supply fallback values.
11
+
12
+ .. note:: This class defines ``defaults`` that are used to return
13
+ reasonable defaults for the library.
14
+
15
+ .. warning:: If you are trying to obtain a value for a key that does
16
+ **not** exist in the store, the library will **NOT** raise but
17
+ return a ``None`` value. This represents the biggest difference to
18
+ a regular ``dict`` class.
19
+
20
+ Methods that need to be implemented:
21
+
22
+ * ``def setdefault(cls, key, value)``
23
+ * ``def __init__(self, *args, **kwargs)``
24
+ * ``def __setitem__(self, key, value)``
25
+ * ``def __getitem__(self, key)``
26
+ * ``def __iter__(self)``
27
+ * ``def __len__(self)``
28
+ * ``def __contains__(self, key)``
29
+
30
+
31
+ .. note:: Configuration and Key classes are subclasses of this to allow
32
+ storing keys separate from configuration.
33
+
34
+ """
35
+
36
+ defaults = {}
37
+
38
+ def setdefault(self, key, value=None):
39
+ """Allows to define default values on this store instance."""
40
+ if value is None and key in self.defaults:
41
+ return self.defaults[key]
42
+ if value is not None:
43
+ self.defaults[key] = value
44
+ return self._data.setdefault(key, value)
45
+
46
+ def __init__(self, *_args, **_kwargs):
47
+ self._data: dict[Any, Any] = {}
48
+
49
+ def __setitem__(self, key, value):
50
+ """Sets an item in the store"""
51
+ self._data[key] = value
52
+
53
+ def __getitem__(self, key):
54
+ """Gets an item from the store as if it was a dictionary
55
+
56
+ .. note:: Returns the value from the store or from defaults if
57
+ the key is found there. Raises ``KeyError`` if not found
58
+ in either.
59
+ """
60
+ if key in self._data:
61
+ return self._data[key]
62
+ if key in self.defaults:
63
+ return self.defaults[key]
64
+ raise KeyError(key)
65
+
66
+ def __iter__(self) -> Iterator[Any]:
67
+ """Iterates through the store"""
68
+ return iter(self._data)
69
+
70
+ def __len__(self) -> int:
71
+ """return length of store"""
72
+ return len(self._data)
73
+
74
+ def __contains__(self, key) -> bool:
75
+ """Tests if a key is contained in the store."""
76
+ return key in self._data
77
+
78
+ def __delitem__(self, key: Any) -> None:
79
+ if key not in self._data:
80
+ raise KeyError(key)
81
+ self._data.pop(key)
82
+
83
+ def items(self):
84
+ """Returns all items off the store as tuples"""
85
+ return self._data.items()
86
+
87
+ def get(self, key, default=None):
88
+ """Return the key if exists or a default value"""
89
+ return self._data.get(key, self.defaults.get(key, default))
90
+
91
+ # Specific for this library
92
+ def delete(self, key):
93
+ """Delete a key from the store"""
94
+ raise NotImplementedError
95
+
96
+ def wipe(self):
97
+ """Wipe the store"""
98
+ raise NotImplementedError
99
+
100
+
101
+ class KeyInterface(StoreInterface):
102
+ """The KeyInterface defines the interface for key storage.
103
+
104
+ .. note:: This class inherits
105
+ :class:`nectarstorage.interfaces.StoreInterface` and defines
106
+ additional key-specific methods.
107
+ """
108
+
109
+ def is_encrypted(self):
110
+ """Returns True/False to indicate required use of unlock"""
111
+ return False
112
+
113
+ # Interface to deal with encrypted keys
114
+ def getPublicKeys(self):
115
+ """Returns the public keys stored in the database"""
116
+ raise NotImplementedError
117
+
118
+ def getPrivateKeyForPublicKey(self, pub):
119
+ """Returns the (possibly encrypted) private key that
120
+ corresponds to a public key
121
+
122
+ :param str pub: Public key
123
+
124
+ The encryption scheme is BIP38
125
+ """
126
+ raise NotImplementedError
127
+
128
+ def add(self, wif, pub=None):
129
+ """Add a new public/private key pair (correspondence has to be checked elsewhere!)
130
+
131
+ :param str pub: Public key
132
+ :param str wif: Private key
133
+ """
134
+ raise NotImplementedError
135
+
136
+ def delete(self, key):
137
+ """Delete a pubkey/privatekey pair from the store
138
+
139
+ :param str key: Public key
140
+ """
141
+ raise NotImplementedError
142
+
143
+
144
+ class EncryptedKeyInterface(KeyInterface):
145
+ """The EncryptedKeyInterface extends KeyInterface to work with encrypted
146
+ keys
147
+ """
148
+
149
+ def is_encrypted(self):
150
+ """Returns True/False to indicate required use of unlock"""
151
+ return True
152
+
153
+ def unlock(self, password):
154
+ """Tries to unlock the wallet if required
155
+
156
+ :param str password: Plain password
157
+ """
158
+ raise NotImplementedError
159
+
160
+ def locked(self):
161
+ """is the wallet locked?"""
162
+ return False
163
+
164
+ def lock(self):
165
+ """Lock the wallet again"""
166
+ raise NotImplementedError
167
+
168
+
169
+ class ConfigInterface(StoreInterface):
170
+ """The BaseKeyStore defines the interface for key storage
171
+
172
+ .. note:: This class inherits
173
+ :class:`nectarstorage.interfaces.StoreInterface` and defines
174
+ **no** additional configuration-specific methods.
175
+ """
176
+
177
+ pass
178
+
179
+
180
+ class TokenInterface(StoreInterface):
181
+ """The TokenInterface defines the interface for token storage.
182
+
183
+ .. note:: This class inherits
184
+ :class:`nectarstorage.interfaces.StoreInterface` and defines
185
+ additional key-specific methods.
186
+ """
187
+
188
+ def is_encrypted(self):
189
+ """Returns True/False to indicate required use of unlock"""
190
+ return False
191
+
192
+ # Interface to deal with tokens
193
+ def getPublicNames(self):
194
+ """Returns the public token names stored in the database"""
195
+ raise NotImplementedError
196
+
197
+ def getPrivateKeyForPublicKey(self, pub):
198
+ """Returns the (possibly encrypted) token that corresponds to a name
199
+
200
+ :param str pub: Public key
201
+
202
+ The encryption scheme is BIP38
203
+ """
204
+ raise NotImplementedError
205
+
206
+ def add(self, token, name=None):
207
+ """Add a new token entry (correspondence has to be checked elsewhere!)
208
+
209
+ :param str name: Public identifier
210
+ :param str token: Token value
211
+ """
212
+ raise NotImplementedError
213
+
214
+ def delete(self, key):
215
+ """Delete a token entry from the store
216
+
217
+ :param str key: Public identifier
218
+ """
219
+ raise NotImplementedError
220
+
221
+
222
+ class EncryptedTokenInterface(TokenInterface):
223
+ """The EncryptedKeyInterface extends KeyInterface to work with encrypted
224
+ tokens
225
+ """
226
+
227
+ def is_encrypted(self):
228
+ """Returns True/False to indicate required use of unlock"""
229
+ return True
230
+
231
+ def unlock(self, password):
232
+ """Tries to unlock the wallet if required
233
+
234
+ :param str password: Plain password
235
+ """
236
+ raise NotImplementedError
237
+
238
+ def locked(self):
239
+ """is the wallet locked?"""
240
+ return False
241
+
242
+ def lock(self):
243
+ """Lock the wallet again"""
244
+ raise NotImplementedError
@@ -0,0 +1,237 @@
1
+ # Inspired by https://raw.githubusercontent.com/xeroc/python-graphenelib/master/graphenestorage/masterpassword.py
2
+ import hashlib
3
+ import logging
4
+ import os
5
+ import warnings
6
+ from binascii import hexlify
7
+
8
+ from nectargraphenebase import bip38
9
+ from nectargraphenebase.aes import AESCipher
10
+
11
+ from .exceptions import WalletLocked, WrongMasterPasswordException
12
+
13
+ log = logging.getLogger(__name__)
14
+
15
+
16
+ class MasterPassword:
17
+ """The keys are encrypted with a Masterpassword that is stored in
18
+ the configurationStore. It has a checksum to verify correctness
19
+ of the password
20
+ The encrypted private keys in `keys` are encrypted with a random
21
+ **masterkey/masterpassword** that is stored in the configuration
22
+ encrypted by the user-provided password.
23
+
24
+ :param ConfigStore config: Configuration store to get access to the
25
+ encrypted master password
26
+ """
27
+
28
+ def __init__(self, config=None, **kwargs):
29
+ if config is None:
30
+ raise ValueError("If using encrypted store, a config store is required!")
31
+ self.config = config
32
+ self.password = None
33
+ self.decrypted_master = None
34
+ self.config_key = "encrypted_master_password"
35
+
36
+ @property
37
+ def masterkey(self):
38
+ """Contains the **decrypted** master key"""
39
+ return self.decrypted_master
40
+
41
+ def has_masterpassword(self):
42
+ """Tells us if the config store knows an encrypted masterpassword"""
43
+ return self.config_key in self.config
44
+
45
+ def locked(self):
46
+ """Is the store locked. E.g. Is a valid password known that can be
47
+ used to decrypt the master key?
48
+ """
49
+ return not self.unlocked()
50
+
51
+ def unlocked(self):
52
+ """Is the store unlocked so that I can decrypt the content?"""
53
+ if self.password is not None:
54
+ return bool(self.password)
55
+ else:
56
+ password_storage = self.config["password_storage"]
57
+ KEYRING_AVAILABLE = False
58
+ if password_storage == "keyring":
59
+ try:
60
+ import keyring # type: ignore[import-not-found]
61
+
62
+ if not isinstance(keyring.get_keyring(), keyring.backends.fail.Keyring):
63
+ KEYRING_AVAILABLE = True
64
+ else:
65
+ KEYRING_AVAILABLE = False
66
+ except ImportError:
67
+ KEYRING_AVAILABLE = False
68
+ if (
69
+ "UNLOCK" in os.environ
70
+ and os.environ["UNLOCK"]
71
+ and self.config_key in self.config
72
+ and self.config[self.config_key]
73
+ and password_storage == "environment"
74
+ ):
75
+ log.debug("Trying to use environmental variable to unlock wallet")
76
+ self.unlock(os.environ.get("UNLOCK"))
77
+ return bool(self.password)
78
+ elif (
79
+ password_storage == "keyring"
80
+ and KEYRING_AVAILABLE
81
+ and self.config_key in self.config
82
+ and self.config[self.config_key]
83
+ ):
84
+ log.debug("Trying to use keyring to unlock wallet")
85
+ pwd = keyring.get_password("nectar", "wallet")
86
+ self.unlock(pwd)
87
+ return bool(self.password)
88
+ return False
89
+
90
+ def lock(self):
91
+ """Lock the store so that we can no longer decrypt the content of the
92
+ store
93
+ """
94
+ self.password = None
95
+ self.decrypted_master = None
96
+
97
+ def unlock(self, password):
98
+ """The password is used to encrypt this masterpassword. To
99
+ decrypt the keys stored in the keys database, one must use
100
+ BIP38, decrypt the masterpassword from the configuration
101
+ store with the user password, and use the decrypted
102
+ masterpassword to decrypt the BIP38 encrypted private keys
103
+ from the keys storage!
104
+
105
+ :param str password: Password to use for en-/de-cryption
106
+ """
107
+ self.password = password
108
+ if self.config_key in self.config and self.config[self.config_key]:
109
+ self._decrypt_masterpassword()
110
+ else:
111
+ self._new_masterpassword(password)
112
+ self._save_encrypted_masterpassword()
113
+
114
+ def wipe_masterpassword(self):
115
+ """Removes the encrypted masterpassword from config storage"""
116
+ if self.config_key in self.config and self.config[self.config_key]:
117
+ self.config[self.config_key] = None
118
+
119
+ def _decrypt_masterpassword(self):
120
+ """Decrypt the encrypted masterkey"""
121
+ aes = AESCipher(self.password)
122
+ checksum, encrypted_master = self.config[self.config_key].split("$")
123
+ try:
124
+ decrypted_master = aes.decrypt(encrypted_master)
125
+ except Exception:
126
+ self._raise_wrongmasterpassexception()
127
+ if checksum != self._derive_checksum(decrypted_master):
128
+ self._raise_wrongmasterpassexception()
129
+ self.decrypted_master = decrypted_master
130
+
131
+ def _raise_wrongmasterpassexception(self):
132
+ self.password = None
133
+ raise WrongMasterPasswordException
134
+
135
+ def _save_encrypted_masterpassword(self):
136
+ self.config[self.config_key] = self._get_encrypted_masterpassword()
137
+
138
+ def _new_masterpassword(self, password):
139
+ """Generate a new random masterkey, encrypt it with the password and
140
+ store it in the store.
141
+
142
+ :param str password: Password to use for en-/de-cryption
143
+ """
144
+ # make sure to not overwrite an existing key
145
+ if self.config_key in self.config and self.config[self.config_key]:
146
+ raise Exception("Storage already has a masterpassword!")
147
+
148
+ self.decrypted_master = hexlify(os.urandom(32)).decode("ascii")
149
+
150
+ # Encrypt and save master
151
+ self.password = password
152
+ self._save_encrypted_masterpassword()
153
+ return self.masterkey
154
+
155
+ def _derive_checksum(self, s):
156
+ """Derive the checksum
157
+
158
+ :param str s: Random string for which to derive the checksum
159
+ """
160
+ checksum = hashlib.sha256(bytes(s, "ascii")).hexdigest()
161
+ return checksum[:4]
162
+
163
+ def _get_encrypted_masterpassword(self):
164
+ """Obtain the encrypted masterkey
165
+
166
+ .. note:: The encrypted masterkey is checksummed, so that we can
167
+ figure out that a provided password is correct or not. The
168
+ checksum is only 4 bytes long!
169
+ """
170
+ if not self.unlocked():
171
+ raise WalletLocked
172
+ aes = AESCipher(self.password)
173
+ return "{}${}".format(self._derive_checksum(self.masterkey), aes.encrypt(self.masterkey))
174
+
175
+ def change_password(self, newpassword):
176
+ """Change the password that allows to decrypt the master key"""
177
+ if not self.unlocked():
178
+ raise WalletLocked
179
+ self.password = newpassword
180
+ self._save_encrypted_masterpassword()
181
+
182
+ def decrypt(self, wif):
183
+ """Decrypt the content according to BIP38
184
+
185
+ :param str wif: Encrypted key
186
+ """
187
+ if not self.unlocked():
188
+ raise WalletLocked
189
+ return format(bip38.decrypt(wif, self.masterkey), "wif")
190
+
191
+ def deriveChecksum(self, s):
192
+ """Derive the checksum"""
193
+ checksum = hashlib.sha256(bytes(s, "ascii")).hexdigest()
194
+ return checksum[:4]
195
+
196
+ def encrypt_text(self, txt):
197
+ """Encrypt the content according to BIP38
198
+
199
+ :param str wif: Unencrypted key
200
+ """
201
+ if not self.unlocked():
202
+ raise WalletLocked
203
+ aes = AESCipher(self.masterkey)
204
+ return "{}${}".format(self.deriveChecksum(txt), aes.encrypt(txt))
205
+
206
+ def decrypt_text(self, enctxt):
207
+ """Decrypt the content according to BIP38
208
+
209
+ :param str wif: Encrypted key
210
+ """
211
+ if not self.unlocked():
212
+ raise WalletLocked
213
+ aes = AESCipher(self.masterkey)
214
+ checksum, encrypted_text = enctxt.split("$")
215
+ try:
216
+ decrypted_text = aes.decrypt(encrypted_text)
217
+ except Exception:
218
+ raise WrongMasterPasswordException
219
+ if checksum != self.deriveChecksum(decrypted_text):
220
+ raise WrongMasterPasswordException
221
+ return decrypted_text
222
+
223
+ def encrypt(self, wif):
224
+ """Encrypt the content according to BIP38
225
+
226
+ :param str wif: Unencrypted key
227
+ """
228
+ if not self.unlocked():
229
+ raise WalletLocked
230
+ return format(bip38.encrypt(str(wif), self.masterkey), "encwif")
231
+
232
+ def changePassword(self, newpassword): # pragma: no cover
233
+ warnings.warn(
234
+ "changePassword will be replaced by change_password in the future",
235
+ PendingDeprecationWarning,
236
+ )
237
+ return self.change_password(newpassword)
nectarstorage/py.typed ADDED
File without changes
nectarstorage/ram.py ADDED
@@ -0,0 +1,27 @@
1
+ # Inspired by https://raw.githubusercontent.com/xeroc/python-graphenelib/master/graphenestorage/ram.py
2
+ from .interfaces import StoreInterface
3
+
4
+
5
+ class InRamStore(StoreInterface):
6
+ """The InRamStore inherits
7
+ :class:`nectarstorage.interfaces.StoreInterface` and extends it by two
8
+ further calls for wipe and delete.
9
+
10
+ The store is syntactically equivalent to a regular dictionary.
11
+
12
+ .. warning:: If you are trying to obtain a value for a key that does
13
+ **not** exist in the store, the library will **NOT** raise but
14
+ return a ``None`` value. This represents the biggest difference to
15
+ a regular ``dict`` class.
16
+ """
17
+
18
+ # Specific for this library
19
+ def delete(self, key):
20
+ """Delete a key from the store"""
21
+ self._data.pop(key, None)
22
+
23
+ def wipe(self):
24
+ """Wipe the store"""
25
+ keys = list(self.keys()).copy()
26
+ for key in keys:
27
+ self.delete(key)