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
nectar/wallet.py ADDED
@@ -0,0 +1,472 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from typing import Any, Dict, Generator, List, Optional, Union
5
+
6
+ from nectar.instance import shared_blockchain_instance
7
+ from nectargraphenebase.account import PrivateKey
8
+ from nectarstorage.exceptions import KeyAlreadyInStoreException
9
+
10
+ from .account import Account
11
+ from .exceptions import (
12
+ AccountDoesNotExistsException,
13
+ InvalidWifError,
14
+ MissingKeyError,
15
+ OfflineHasNoRPCException,
16
+ WalletExists,
17
+ )
18
+
19
+ log = logging.getLogger(__name__)
20
+
21
+
22
+ class Wallet:
23
+ """The wallet is meant to maintain access to private keys for
24
+ your accounts. It either uses manually provided private keys
25
+ or uses a SQLite database managed by storage.py.
26
+
27
+ :param Rpc rpc: RPC connection to a Hive node
28
+ :param keys: Predefine the wif keys to shortcut the
29
+ wallet database
30
+ :type keys: array, dict, str
31
+
32
+ Three wallet operation modes are possible:
33
+
34
+ * **Wallet Database**: Here, nectar loads the keys from the
35
+ locally stored wallet SQLite database (see ``storage.py``).
36
+ To use this mode, simply call :class:`nectar.hive.Hive` without the
37
+ ``keys`` parameter
38
+ * **Providing Keys**: Here, you can provide the keys for
39
+ your accounts manually. All you need to do is add the wif
40
+ keys for the accounts you want to use as a simple array
41
+ using the ``keys`` parameter to :class:`nectar.hive.Hive`.
42
+ * **Force keys**: This more is for advanced users and
43
+ requires that you know what you are doing. Here, the
44
+ ``keys`` parameter is a dictionary that overwrite the
45
+ ``active``, ``owner``, ``posting`` or ``memo`` keys for
46
+ any account. This mode is only used for *foreign*
47
+ signatures!
48
+
49
+ A new wallet can be created by using:
50
+
51
+ .. code-block:: python
52
+
53
+ from nectar import Hive
54
+ hive = Hive()
55
+ hive.wallet.wipe(True)
56
+ hive.wallet.create("supersecret-passphrase")
57
+
58
+ This will raise :class:`nectar.exceptions.WalletExists` if you already have a wallet installed.
59
+
60
+
61
+ The wallet can be unlocked for signing using
62
+
63
+ .. code-block:: python
64
+
65
+ from nectar import Hive
66
+ hive = Hive()
67
+ hive.wallet.unlock("supersecret-passphrase")
68
+
69
+ A private key can be added by using the
70
+ :func:`addPrivateKey` method that is available
71
+ **after** unlocking the wallet with the correct passphrase:
72
+
73
+ .. code-block:: python
74
+
75
+ from nectar import Hive
76
+ hive = Hive()
77
+ hive.wallet.unlock("supersecret-passphrase")
78
+ hive.wallet.addPrivateKey("5xxxxxxxxxxxxxxxxxxxx")
79
+
80
+ .. note:: The private key has to be either in hexadecimal or in wallet
81
+ import format (wif) (starting with a ``5``).
82
+
83
+ """
84
+
85
+ def __init__(
86
+ self, blockchain_instance: Optional[Any] = None, *args: Any, **kwargs: Any
87
+ ) -> None:
88
+ """
89
+ Initialize the Wallet, binding it to a blockchain instance and setting up the underlying key store.
90
+
91
+ If a blockchain_instance is provided, it is used; otherwise the shared blockchain instance is used. Accepts legacy "wif" argument (aliased to "keys"). If non-empty "keys" are supplied, an in-memory plain key store is created and populated; otherwise a SQLite-encrypted key store is instantiated (can be overridden via the `key_store` kwarg).
92
+
93
+ Parameters:
94
+ blockchain_instance (optional): Explicit blockchain/RPC wrapper to use; if omitted the module's shared blockchain instance is used.
95
+
96
+ Side effects:
97
+ - Creates and assigns self.store to either an in-memory or persistent key store.
98
+ - Calls setKeys when an in-memory store is selected.
99
+ """
100
+ self.blockchain = blockchain_instance or shared_blockchain_instance()
101
+
102
+ # Compatibility after name change from wif->keys
103
+ if "wif" in kwargs and "keys" not in kwargs:
104
+ kwargs["keys"] = kwargs["wif"]
105
+
106
+ self.store: Any
107
+ if "keys" in kwargs and len(kwargs["keys"]) > 0:
108
+ from nectarstorage import InRamPlainKeyStore
109
+
110
+ self.store = InRamPlainKeyStore()
111
+ self.setKeys(kwargs["keys"])
112
+ else:
113
+ """ If no keys are provided manually we load the SQLite
114
+ keyStorage
115
+ """
116
+ from nectarstorage import SqliteEncryptedKeyStore
117
+
118
+ self.store = kwargs.get(
119
+ "key_store",
120
+ SqliteEncryptedKeyStore(config=self.blockchain.config, **kwargs),
121
+ )
122
+
123
+ @property
124
+ def prefix(self) -> str:
125
+ if self.blockchain.is_connected():
126
+ prefix = self.blockchain.prefix
127
+ else:
128
+ # If not connected, load prefix from config
129
+ prefix = self.blockchain.config["prefix"]
130
+ return prefix or "STM" # default prefix is STM
131
+
132
+ @property
133
+ def rpc(self) -> Any:
134
+ if not self.blockchain.is_connected():
135
+ raise OfflineHasNoRPCException("No RPC available in offline mode!")
136
+ return self.blockchain.rpc
137
+
138
+ def setKeys(self, loadkeys: Union[Dict[str, Any], List[Any], set, str]) -> None:
139
+ """This method is strictly only for in memory keys that are
140
+ passed to Wallet with the ``keys`` argument
141
+ """
142
+ log.debug("Force setting of private keys. Not using the wallet database!")
143
+ if isinstance(loadkeys, dict):
144
+ loadkeys = list(loadkeys.values())
145
+ elif not isinstance(loadkeys, (list, set)):
146
+ loadkeys = [loadkeys]
147
+ for wif in loadkeys:
148
+ pub = self.publickey_from_wif(wif)
149
+ self.store.add(str(wif), pub)
150
+
151
+ def is_encrypted(self) -> bool:
152
+ """Is the key store encrypted?"""
153
+ return self.store.is_encrypted()
154
+
155
+ def unlock(self, pwd: str) -> Optional[bool]:
156
+ """Unlock the wallet database"""
157
+ unlock_ok = None
158
+ if self.store.is_encrypted():
159
+ unlock_ok = self.store.unlock(pwd)
160
+ return unlock_ok
161
+
162
+ def lock(self) -> bool:
163
+ """Lock the wallet database"""
164
+ lock_ok = False
165
+ if self.store.is_encrypted():
166
+ lock_ok = self.store.lock()
167
+ return lock_ok
168
+
169
+ def unlocked(self) -> bool:
170
+ """Is the wallet database unlocked?"""
171
+ unlocked = True
172
+ if self.store.is_encrypted():
173
+ unlocked = not self.store.locked()
174
+ return unlocked
175
+
176
+ def locked(self) -> bool:
177
+ """Is the wallet database locked?"""
178
+ if self.store.is_encrypted():
179
+ return self.store.locked()
180
+ else:
181
+ return False
182
+
183
+ def changePassphrase(self, new_pwd: str) -> None:
184
+ """Change the passphrase for the wallet database"""
185
+ self.store.change_password(new_pwd)
186
+
187
+ def created(self) -> bool:
188
+ """Do we have a wallet database already?"""
189
+ if len(self.store.getPublicKeys()):
190
+ # Already keys installed
191
+ return True
192
+ else:
193
+ return False
194
+
195
+ def create(self, pwd: str) -> None:
196
+ """Alias for :func:`newWallet`
197
+
198
+ :param str pwd: Passphrase for the created wallet
199
+ """
200
+ self.newWallet(pwd)
201
+
202
+ def newWallet(self, pwd: str) -> None:
203
+ """Create a new wallet database
204
+
205
+ :param str pwd: Passphrase for the created wallet
206
+ """
207
+ if self.created():
208
+ raise WalletExists("You already have created a wallet!")
209
+ self.store.unlock(pwd)
210
+
211
+ def privatekey(self, key: str) -> PrivateKey:
212
+ return PrivateKey(key, prefix=self.prefix)
213
+
214
+ def publickey_from_wif(self, wif: str) -> str:
215
+ return str(self.privatekey(str(wif)).pubkey)
216
+
217
+ def addPrivateKey(self, wif: str) -> None:
218
+ """Add a private key to the wallet database
219
+
220
+ :param str wif: Private key
221
+ """
222
+ try:
223
+ pub = self.publickey_from_wif(wif)
224
+ except Exception:
225
+ raise InvalidWifError("Invalid Key format!")
226
+ if str(pub) in self.store:
227
+ raise KeyAlreadyInStoreException("Key already in the store")
228
+ self.store.add(str(wif), str(pub))
229
+
230
+ def getPrivateKeyForPublicKey(self, pub: str) -> Optional[str]:
231
+ """Obtain the private key for a given public key
232
+
233
+ :param str pub: Public Key
234
+ """
235
+ if str(pub) not in self.store:
236
+ raise MissingKeyError
237
+ return self.store.getPrivateKeyForPublicKey(str(pub))
238
+
239
+ def removePrivateKeyFromPublicKey(self, pub: str) -> None:
240
+ """Remove a key from the wallet database
241
+
242
+ :param str pub: Public key
243
+ """
244
+ self.store.delete(str(pub))
245
+
246
+ def removeAccount(self, account: str) -> None:
247
+ """Remove all keys associated with a given account
248
+
249
+ :param str account: name of account to be removed
250
+ """
251
+ accounts = self.getAccounts()
252
+ for a in accounts:
253
+ if a["name"] == account:
254
+ self.store.delete(a["pubkey"])
255
+
256
+ def getKeyForAccount(self, name: str, key_type: str) -> Optional[str]:
257
+ """Obtain `key_type` Private Key for an account from the wallet database
258
+
259
+ :param str name: Account name
260
+ :param str key_type: key type, has to be one of "owner", "active",
261
+ "posting" or "memo"
262
+ """
263
+ if key_type not in ["owner", "active", "posting", "memo"]:
264
+ raise AssertionError("Wrong key type")
265
+
266
+ account = self.rpc.find_accounts({"accounts": [name]})["accounts"]
267
+ if not account:
268
+ return
269
+ if len(account) == 0:
270
+ return
271
+ if key_type == "memo":
272
+ key = self.getPrivateKeyForPublicKey(account[0]["memo_key"])
273
+ if key:
274
+ return key
275
+ else:
276
+ key = None
277
+ for authority in account[0][key_type]["key_auths"]:
278
+ try:
279
+ key = self.getPrivateKeyForPublicKey(authority[0])
280
+ if key:
281
+ return key
282
+ except MissingKeyError:
283
+ key = None
284
+ if key is None:
285
+ raise MissingKeyError("No private key for {} found".format(name))
286
+ return
287
+
288
+ def getKeysForAccount(self, name: str, key_type: str) -> Optional[List[str]]:
289
+ """Obtain a List of `key_type` Private Keys for an account from the wallet database
290
+
291
+ :param str name: Account name
292
+ :param str key_type: key type, has to be one of "owner", "active",
293
+ "posting" or "memo"
294
+ """
295
+ if key_type not in ["owner", "active", "posting", "memo"]:
296
+ raise AssertionError("Wrong key type")
297
+
298
+ account = self.rpc.find_accounts({"accounts": [name]})["accounts"]
299
+ if not account:
300
+ return
301
+ if len(account) == 0:
302
+ return
303
+ if key_type == "memo":
304
+ key = self.getPrivateKeyForPublicKey(account[0]["memo_key"])
305
+ if key:
306
+ return [key]
307
+ else:
308
+ keys = []
309
+ key = None
310
+ for authority in account[0][key_type]["key_auths"]:
311
+ try:
312
+ key = self.getPrivateKeyForPublicKey(authority[0])
313
+ if key:
314
+ keys.append(key)
315
+ except MissingKeyError:
316
+ key = None
317
+ if not keys:
318
+ raise MissingKeyError("No private key for {} found".format(name))
319
+ return keys
320
+ return
321
+
322
+ def getOwnerKeyForAccount(self, name: str) -> Optional[str]:
323
+ """Obtain owner Private Key for an account from the wallet database"""
324
+ return self.getKeyForAccount(name, "owner")
325
+
326
+ def getMemoKeyForAccount(self, name: str) -> Optional[str]:
327
+ """Obtain owner Memo Key for an account from the wallet database"""
328
+ return self.getKeyForAccount(name, "memo")
329
+
330
+ def getActiveKeyForAccount(self, name: str) -> Optional[str]:
331
+ """Obtain owner Active Key for an account from the wallet database"""
332
+ return self.getKeyForAccount(name, "active")
333
+
334
+ def getPostingKeyForAccount(self, name: str) -> Optional[str]:
335
+ """Obtain owner Posting Key for an account from the wallet database"""
336
+ return self.getKeyForAccount(name, "posting")
337
+
338
+ def getOwnerKeysForAccount(self, name: str) -> Optional[List[str]]:
339
+ """Obtain list of all owner Private Keys for an account from the wallet database"""
340
+ return self.getKeysForAccount(name, "owner")
341
+
342
+ def getActiveKeysForAccount(self, name: str) -> Optional[List[str]]:
343
+ """Obtain list of all owner Active Keys for an account from the wallet database"""
344
+ return self.getKeysForAccount(name, "active")
345
+
346
+ def getPostingKeysForAccount(self, name: str) -> Optional[List[str]]:
347
+ """Obtain list of all owner Posting Keys for an account from the wallet database"""
348
+ return self.getKeysForAccount(name, "posting")
349
+
350
+ def getAccountFromPrivateKey(self, wif: str) -> Optional[str]:
351
+ """Obtain account name from private key"""
352
+ pub = self.publickey_from_wif(wif)
353
+ return self.getAccountFromPublicKey(pub)
354
+
355
+ def getAccountsFromPublicKey(self, pub: str) -> Generator[str, None, None]:
356
+ """Obtain all account names associated with a public key
357
+
358
+ :param str pub: Public key
359
+ """
360
+ if not self.blockchain.is_connected():
361
+ raise OfflineHasNoRPCException("No RPC available in offline mode!")
362
+ self.blockchain.rpc.set_next_node_on_empty_reply(False)
363
+ names = self.blockchain.rpc.get_key_references({"keys": [pub]})["accounts"]
364
+ for name in names:
365
+ yield from name
366
+
367
+ def getAccountFromPublicKey(self, pub: str) -> Optional[str]:
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: str) -> Generator[Dict[str, Any], None, None]:
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: str) -> Optional[Dict[str, Any]]:
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 Exception:
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: Union[Account, Dict[str, Any]], pub: str) -> Optional[str]:
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) -> List[Dict[str, Any]]:
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: bool = False) -> List[str]:
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: bool = False) -> None:
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()