hive-nectar 0.0.2__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.

Potentially problematic release.


This version of hive-nectar might be problematic. Click here for more details.

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