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
nectar/wallet.py ADDED
@@ -0,0 +1,472 @@
1
+ # -*- coding: utf-8 -*-
2
+ import logging
3
+
4
+ from nectar.instance import shared_blockchain_instance
5
+ from nectargraphenebase.account import PrivateKey
6
+ from nectarstorage.exceptions import KeyAlreadyInStoreException
7
+
8
+ from .account import Account
9
+ from .exceptions import (
10
+ AccountDoesNotExistsException,
11
+ InvalidWifError,
12
+ MissingKeyError,
13
+ OfflineHasNoRPCException,
14
+ WalletExists,
15
+ )
16
+
17
+ log = logging.getLogger(__name__)
18
+
19
+
20
+ class Wallet(object):
21
+ """The wallet is meant to maintain access to private keys for
22
+ your accounts. It either uses manually provided private keys
23
+ or uses a SQLite database managed by storage.py.
24
+
25
+ :param SteemNodeRPC rpc: RPC connection to a Steem node
26
+ :param keys: Predefine the wif keys to shortcut the
27
+ wallet database
28
+ :type keys: array, dict, str
29
+
30
+ Three wallet operation modes are possible:
31
+
32
+ * **Wallet Database**: Here, nectar loads the keys from the
33
+ locally stored wallet SQLite database (see ``storage.py``).
34
+ To use this mode, simply call :class:`nectar.steem.Steem` without the
35
+ ``keys`` parameter
36
+ * **Providing Keys**: Here, you can provide the keys for
37
+ your accounts manually. All you need to do is add the wif
38
+ keys for the accounts you want to use as a simple array
39
+ using the ``keys`` parameter to :class:`nectar.steem.Steem`.
40
+ * **Force keys**: This more is for advanced users and
41
+ requires that you know what you are doing. Here, the
42
+ ``keys`` parameter is a dictionary that overwrite the
43
+ ``active``, ``owner``, ``posting`` or ``memo`` keys for
44
+ any account. This mode is only used for *foreign*
45
+ signatures!
46
+
47
+ A new wallet can be created by using:
48
+
49
+ .. code-block:: python
50
+
51
+ from nectar import Steem
52
+ steem = Steem()
53
+ steem.wallet.wipe(True)
54
+ steem.wallet.create("supersecret-passphrase")
55
+
56
+ This will raise :class:`nectar.exceptions.WalletExists` if you already have a wallet installed.
57
+
58
+
59
+ The wallet can be unlocked for signing using
60
+
61
+ .. code-block:: python
62
+
63
+ from nectar import Steem
64
+ steem = Steem()
65
+ steem.wallet.unlock("supersecret-passphrase")
66
+
67
+ A private key can be added by using the
68
+ :func:`addPrivateKey` method that is available
69
+ **after** unlocking the wallet with the correct passphrase:
70
+
71
+ .. code-block:: python
72
+
73
+ from nectar import Steem
74
+ steem = Steem()
75
+ steem.wallet.unlock("supersecret-passphrase")
76
+ steem.wallet.addPrivateKey("5xxxxxxxxxxxxxxxxxxxx")
77
+
78
+ .. note:: The private key has to be either in hexadecimal or in wallet
79
+ import format (wif) (starting with a ``5``).
80
+
81
+ """
82
+
83
+ def __init__(self, blockchain_instance=None, *args, **kwargs):
84
+ if blockchain_instance is None:
85
+ if kwargs.get("steem_instance"):
86
+ blockchain_instance = kwargs["steem_instance"]
87
+ elif kwargs.get("hive_instance"):
88
+ blockchain_instance = kwargs["hive_instance"]
89
+ self.blockchain = blockchain_instance or shared_blockchain_instance()
90
+
91
+ # Compatibility after name change from wif->keys
92
+ if "wif" in kwargs and "keys" not in kwargs:
93
+ kwargs["keys"] = kwargs["wif"]
94
+
95
+ if "keys" in kwargs and len(kwargs["keys"]) > 0:
96
+ from nectarstorage import InRamPlainKeyStore
97
+
98
+ self.store = InRamPlainKeyStore()
99
+ self.setKeys(kwargs["keys"])
100
+ else:
101
+ """ If no keys are provided manually we load the SQLite
102
+ keyStorage
103
+ """
104
+ from nectarstorage import SqliteEncryptedKeyStore
105
+
106
+ self.store = kwargs.get(
107
+ "key_store",
108
+ SqliteEncryptedKeyStore(config=self.blockchain.config, **kwargs),
109
+ )
110
+
111
+ @property
112
+ def prefix(self):
113
+ if self.blockchain.is_connected():
114
+ prefix = self.blockchain.prefix
115
+ else:
116
+ # If not connected, load prefix from config
117
+ prefix = self.blockchain.config["prefix"]
118
+ return prefix or "STM" # default prefix is STM
119
+
120
+ @property
121
+ def rpc(self):
122
+ if not self.blockchain.is_connected():
123
+ raise OfflineHasNoRPCException("No RPC available in offline mode!")
124
+ return self.blockchain.rpc
125
+
126
+ def setKeys(self, loadkeys):
127
+ """This method is strictly only for in memory keys that are
128
+ passed to Wallet with the ``keys`` argument
129
+ """
130
+ log.debug("Force setting of private keys. Not using the wallet database!")
131
+ if isinstance(loadkeys, dict):
132
+ loadkeys = list(loadkeys.values())
133
+ elif not isinstance(loadkeys, (list, set)):
134
+ loadkeys = [loadkeys]
135
+ for wif in loadkeys:
136
+ pub = self.publickey_from_wif(wif)
137
+ self.store.add(str(wif), pub)
138
+
139
+ def is_encrypted(self):
140
+ """Is the key store encrypted?"""
141
+ return self.store.is_encrypted()
142
+
143
+ def unlock(self, pwd):
144
+ """Unlock the wallet database"""
145
+ unlock_ok = None
146
+ if self.store.is_encrypted():
147
+ unlock_ok = self.store.unlock(pwd)
148
+ return unlock_ok
149
+
150
+ def lock(self):
151
+ """Lock the wallet database"""
152
+ lock_ok = False
153
+ if self.store.is_encrypted():
154
+ lock_ok = self.store.lock()
155
+ return lock_ok
156
+
157
+ def unlocked(self):
158
+ """Is the wallet database unlocked?"""
159
+ unlocked = True
160
+ if self.store.is_encrypted():
161
+ unlocked = not self.store.locked()
162
+ return unlocked
163
+
164
+ def locked(self):
165
+ """Is the wallet database locked?"""
166
+ if self.store.is_encrypted():
167
+ return self.store.locked()
168
+ else:
169
+ return False
170
+
171
+ def changePassphrase(self, new_pwd):
172
+ """Change the passphrase for the wallet database"""
173
+ self.store.change_password(new_pwd)
174
+
175
+ def created(self):
176
+ """Do we have a wallet database already?"""
177
+ if len(self.store.getPublicKeys()):
178
+ # Already keys installed
179
+ return True
180
+ else:
181
+ return False
182
+
183
+ def create(self, pwd):
184
+ """Alias for :func:`newWallet`
185
+
186
+ :param str pwd: Passphrase for the created wallet
187
+ """
188
+ self.newWallet(pwd)
189
+
190
+ def newWallet(self, pwd):
191
+ """Create a new wallet database
192
+
193
+ :param str pwd: Passphrase for the created wallet
194
+ """
195
+ if self.created():
196
+ raise WalletExists("You already have created a wallet!")
197
+ self.store.unlock(pwd)
198
+
199
+ def privatekey(self, key):
200
+ return PrivateKey(key, prefix=self.prefix)
201
+
202
+ def publickey_from_wif(self, wif):
203
+ return str(self.privatekey(str(wif)).pubkey)
204
+
205
+ def addPrivateKey(self, wif):
206
+ """Add a private key to the wallet database
207
+
208
+ :param str wif: Private key
209
+ """
210
+ try:
211
+ pub = self.publickey_from_wif(wif)
212
+ except Exception:
213
+ raise InvalidWifError("Invalid Key format!")
214
+ if str(pub) in self.store:
215
+ raise KeyAlreadyInStoreException("Key already in the store")
216
+ self.store.add(str(wif), str(pub))
217
+
218
+ def getPrivateKeyForPublicKey(self, pub):
219
+ """Obtain the private key for a given public key
220
+
221
+ :param str pub: Public Key
222
+ """
223
+ if str(pub) not in self.store:
224
+ raise MissingKeyError
225
+ return self.store.getPrivateKeyForPublicKey(str(pub))
226
+
227
+ def removePrivateKeyFromPublicKey(self, pub):
228
+ """Remove a key from the wallet database
229
+
230
+ :param str pub: Public key
231
+ """
232
+ self.store.delete(str(pub))
233
+
234
+ def removeAccount(self, account):
235
+ """Remove all keys associated with a given account
236
+
237
+ :param str account: name of account to be removed
238
+ """
239
+ accounts = self.getAccounts()
240
+ for a in accounts:
241
+ if a["name"] == account:
242
+ self.store.delete(a["pubkey"])
243
+
244
+ def getKeyForAccount(self, name, key_type):
245
+ """Obtain `key_type` Private Key for an account from the wallet database
246
+
247
+ :param str name: Account name
248
+ :param str key_type: key type, has to be one of "owner", "active",
249
+ "posting" or "memo"
250
+ """
251
+ if key_type not in ["owner", "active", "posting", "memo"]:
252
+ raise AssertionError("Wrong key type")
253
+
254
+ if self.rpc.get_use_appbase():
255
+ account = self.rpc.find_accounts({"accounts": [name]}, api="database")["accounts"]
256
+ else:
257
+ account = self.rpc.get_account(name)
258
+ if not account:
259
+ return
260
+ if len(account) == 0:
261
+ return
262
+ if key_type == "memo":
263
+ key = self.getPrivateKeyForPublicKey(account[0]["memo_key"])
264
+ if key:
265
+ return key
266
+ else:
267
+ key = None
268
+ for authority in account[0][key_type]["key_auths"]:
269
+ try:
270
+ key = self.getPrivateKeyForPublicKey(authority[0])
271
+ if key:
272
+ return key
273
+ except MissingKeyError:
274
+ key = None
275
+ if key is None:
276
+ raise MissingKeyError("No private key for {} found".format(name))
277
+ return
278
+
279
+ def getKeysForAccount(self, name, key_type):
280
+ """Obtain a List of `key_type` Private Keys for an account from the wallet database
281
+
282
+ :param str name: Account name
283
+ :param str key_type: key type, has to be one of "owner", "active",
284
+ "posting" or "memo"
285
+ """
286
+ if key_type not in ["owner", "active", "posting", "memo"]:
287
+ raise AssertionError("Wrong key type")
288
+
289
+ if self.rpc.get_use_appbase():
290
+ account = self.rpc.find_accounts({"accounts": [name]}, api="database")["accounts"]
291
+ else:
292
+ account = self.rpc.get_account(name)
293
+ if not account:
294
+ return
295
+ if len(account) == 0:
296
+ return
297
+ if key_type == "memo":
298
+ key = self.getPrivateKeyForPublicKey(account[0]["memo_key"])
299
+ if key:
300
+ return [key]
301
+ else:
302
+ keys = []
303
+ key = None
304
+ for authority in account[0][key_type]["key_auths"]:
305
+ try:
306
+ key = self.getPrivateKeyForPublicKey(authority[0])
307
+ if key:
308
+ keys.append(key)
309
+ except MissingKeyError:
310
+ key = None
311
+ if key is None:
312
+ raise MissingKeyError("No private key for {} found".format(name))
313
+ return keys
314
+ return
315
+
316
+ def getOwnerKeyForAccount(self, name):
317
+ """Obtain owner Private Key for an account from the wallet database"""
318
+ return self.getKeyForAccount(name, "owner")
319
+
320
+ def getMemoKeyForAccount(self, name):
321
+ """Obtain owner Memo Key for an account from the wallet database"""
322
+ return self.getKeyForAccount(name, "memo")
323
+
324
+ def getActiveKeyForAccount(self, name):
325
+ """Obtain owner Active Key for an account from the wallet database"""
326
+ return self.getKeyForAccount(name, "active")
327
+
328
+ def getPostingKeyForAccount(self, name):
329
+ """Obtain owner Posting Key for an account from the wallet database"""
330
+ return self.getKeyForAccount(name, "posting")
331
+
332
+ def getOwnerKeysForAccount(self, name):
333
+ """Obtain list of all owner Private Keys for an account from the wallet database"""
334
+ return self.getKeysForAccount(name, "owner")
335
+
336
+ def getActiveKeysForAccount(self, name):
337
+ """Obtain list of all owner Active Keys for an account from the wallet database"""
338
+ return self.getKeysForAccount(name, "active")
339
+
340
+ def getPostingKeysForAccount(self, name):
341
+ """Obtain list of all owner Posting Keys for an account from the wallet database"""
342
+ return self.getKeysForAccount(name, "posting")
343
+
344
+ def getAccountFromPrivateKey(self, wif):
345
+ """Obtain account name from private key"""
346
+ pub = self.publickey_from_wif(wif)
347
+ return self.getAccountFromPublicKey(pub)
348
+
349
+ def getAccountsFromPublicKey(self, pub):
350
+ """Obtain all account names associated with a public key
351
+
352
+ :param str pub: Public key
353
+ """
354
+ if not self.blockchain.is_connected():
355
+ raise OfflineHasNoRPCException("No RPC available in offline mode!")
356
+ self.blockchain.rpc.set_next_node_on_empty_reply(False)
357
+ if self.blockchain.rpc.get_use_appbase():
358
+ names = self.blockchain.rpc.get_key_references({"keys": [pub]}, api="account_by_key")[
359
+ "accounts"
360
+ ]
361
+ else:
362
+ names = self.blockchain.rpc.get_key_references([pub], api="account_by_key")
363
+ for name in names:
364
+ for i in name:
365
+ yield i
366
+
367
+ def getAccountFromPublicKey(self, pub):
368
+ """Obtain the first account name from public key
369
+
370
+ :param str pub: Public key
371
+
372
+ Note: this returns only the first account with the given key. To
373
+ get all accounts associated with a given public key, use
374
+ :func:`getAccountsFromPublicKey`.
375
+ """
376
+ names = list(self.getAccountsFromPublicKey(pub))
377
+ if not names:
378
+ return None
379
+ else:
380
+ return names[0]
381
+
382
+ def getAllAccounts(self, pub):
383
+ """Get the account data for a public key (all accounts found for this
384
+ public key)
385
+
386
+ :param str pub: Public key
387
+ """
388
+ for name in self.getAccountsFromPublicKey(pub):
389
+ try:
390
+ account = Account(name, blockchain_instance=self.blockchain)
391
+ except AccountDoesNotExistsException:
392
+ continue
393
+ yield {
394
+ "name": account["name"],
395
+ "account": account,
396
+ "type": self.getKeyType(account, pub),
397
+ "pubkey": pub,
398
+ }
399
+
400
+ def getAccount(self, pub):
401
+ """Get the account data for a public key (first account found for this
402
+ public key)
403
+
404
+ :param str pub: Public key
405
+ """
406
+ name = self.getAccountFromPublicKey(pub)
407
+ if not name:
408
+ return {"name": None, "type": None, "pubkey": pub}
409
+ else:
410
+ try:
411
+ account = Account(name, blockchain_instance=self.blockchain)
412
+ except:
413
+ return
414
+ return {
415
+ "name": account["name"],
416
+ "account": account,
417
+ "type": self.getKeyType(account, pub),
418
+ "pubkey": pub,
419
+ }
420
+
421
+ def getKeyType(self, account, pub):
422
+ """Get key type
423
+
424
+ :param nectar.account.Account/dict account: Account data
425
+ :type account: Account, dict
426
+ :param str pub: Public key
427
+
428
+ """
429
+ for authority in ["owner", "active", "posting"]:
430
+ for key in account[authority]["key_auths"]:
431
+ if str(pub) == key[0]:
432
+ return authority
433
+ if str(pub) == account["memo_key"]:
434
+ return "memo"
435
+ return None
436
+
437
+ def getAccounts(self):
438
+ """Return all accounts installed in the wallet database"""
439
+ pubkeys = self.getPublicKeys()
440
+ accounts = []
441
+ for pubkey in pubkeys:
442
+ # Filter those keys not for our network
443
+ if pubkey[: len(self.prefix)] == self.prefix:
444
+ accounts.extend(self.getAllAccounts(pubkey))
445
+ return accounts
446
+
447
+ def getPublicKeys(self, current=False):
448
+ """Return all installed public keys
449
+ :param bool current: If true, returns only keys for currently
450
+ connected blockchain
451
+ """
452
+ pubkeys = self.store.getPublicKeys()
453
+ if not current:
454
+ return pubkeys
455
+ pubs = []
456
+ for pubkey in pubkeys:
457
+ # Filter those keys not for our network
458
+ if pubkey[: len(self.prefix)] == self.prefix:
459
+ pubs.append(pubkey)
460
+ return pubs
461
+
462
+ def wipe(self, sure=False):
463
+ if not sure:
464
+ log.error(
465
+ "You need to confirm that you are sure "
466
+ "and understand the implications of "
467
+ "wiping your wallet!"
468
+ )
469
+ return
470
+ else:
471
+ self.store.wipe()
472
+ self.store.wipe_masterpassword()