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/memo.py ADDED
@@ -0,0 +1,449 @@
1
+ # -*- coding: utf-8 -*-
2
+ import os
3
+ import random
4
+ import struct
5
+ from binascii import hexlify, unhexlify
6
+
7
+ from nectar.instance import shared_blockchain_instance
8
+ from nectar.version import version as __version__
9
+ from nectarbase import memo as BtsMemo
10
+ from nectargraphenebase.account import PrivateKey, PublicKey
11
+ from nectargraphenebase.base58 import base58decode, base58encode
12
+
13
+ from .account import Account
14
+ from .exceptions import MissingKeyError
15
+
16
+
17
+ class Memo(object):
18
+ """Deals with Memos that are attached to a transfer
19
+
20
+ :param Account from_account: Account that has sent the memo
21
+ :param Account to_account: Account that has received the memo
22
+ :param Steem blockchain_instance: Steem instance
23
+
24
+ A memo is encrypted with a shared secret derived from a private key of
25
+ the sender and a public key of the receiver. Due to the underlying
26
+ mathematics, the same shared secret can be derived by the private key
27
+ of the receiver and the public key of the sender. The encrypted message
28
+ is perturbed by a nonce that is part of the transmitted message.
29
+
30
+ .. code-block:: python
31
+
32
+ from nectar.memo import Memo
33
+ m = Memo("thecrazygm", "hive-nectar")
34
+ m.unlock_wallet("secret")
35
+ enc = (m.encrypt("test"))
36
+ print(enc)
37
+ >> {'message': '#DTpKcbxWqsETCRfjYGk9feERFa5nVBF8FaHfWPwUjyHBTgNhXGh4mN5TTG41nLhUcHtXfu7Hy3AwLrtWvo1ERUyAZaJjaEZNhHyoeDnrHdWChrzbccbANQmazgwjyxzEL', 'from': 'STM6MQBLaX9Q15CK3prXoWK4C6EqtsL7C4rqq1h6BQjxvfk9tuT3N', 'to': 'STM6sRudsxWpTZWxnpRkCDVD51RteiJnvJYCt5LiZAbVLfM1hJCQC'}
38
+ print(m.decrypt(enc))
39
+ >> foobar
40
+
41
+ To decrypt a memo, simply use
42
+
43
+ .. code-block:: python
44
+
45
+ from nectar.memo import Memo
46
+ m = Memo()
47
+ m.unlock_wallet("secret")
48
+ print(m.decrypt(op_data["memo"]))
49
+
50
+ if ``op_data`` being the payload of a transfer operation.
51
+
52
+ Memo Keys
53
+
54
+ In Steem, memos are AES-256 encrypted with a shared secret between sender and
55
+ receiver. It is derived from the memo private key of the sender and the memo
56
+ public key of the receiver.
57
+
58
+ In order for the receiver to decode the memo, the shared secret has to be
59
+ derived from the receiver's private key and the senders public key.
60
+
61
+ The memo public key is part of the account and can be retrieved with the
62
+ `get_account` call:
63
+
64
+ .. code-block:: js
65
+
66
+ get_account <accountname>
67
+ {
68
+ [...]
69
+ "options": {
70
+ "memo_key": "GPH5TPTziKkLexhVKsQKtSpo4bAv5RnB8oXcG4sMHEwCcTf3r7dqE",
71
+ [...]
72
+ },
73
+ [...]
74
+ }
75
+
76
+ while the memo private key can be dumped with `dump_private_keys`
77
+
78
+ Memo Message
79
+
80
+ The take the following form:
81
+
82
+ .. code-block:: js
83
+
84
+ {
85
+ "from": "GPH5mgup8evDqMnT86L7scVebRYDC2fwAWmygPEUL43LjstQegYCC",
86
+ "to": "GPH5Ar4j53kFWuEZQ9XhxbAja4YXMPJ2EnUg5QcrdeMFYUNMMNJbe",
87
+ "nonce": "13043867485137706821",
88
+ "message": "d55524c37320920844ca83bb20c8d008"
89
+ }
90
+
91
+ The fields `from` and `to` contain the memo public key of sender and receiver.
92
+ The `nonce` is a random integer that is used for the seed of the AES encryption
93
+ of the message.
94
+
95
+ Encrypting a memo
96
+
97
+ The high level memo class makes use of the nectar wallet to obtain keys
98
+ for the corresponding accounts.
99
+
100
+ .. code-block:: python
101
+
102
+ from nectar.memo import Memo
103
+ from nectar.account import Account
104
+
105
+ memoObj = Memo(
106
+ from_account=Account(from_account),
107
+ to_account=Account(to_account)
108
+ )
109
+ encrypted_memo = memoObj.encrypt(memo)
110
+
111
+ Decoding of a received memo
112
+
113
+ .. code-block:: python
114
+
115
+ from getpass import getpass
116
+ from nectar.block import Block
117
+ from nectar.memo import Memo
118
+
119
+ # Obtain a transfer from the blockchain
120
+ block = Block(23755086) # block
121
+ transaction = block["transactions"][3] # transactions
122
+ op = transaction["operations"][0] # operation
123
+ op_id = op[0] # operation type
124
+ op_data = op[1] # operation payload
125
+
126
+ # Instantiate Memo for decoding
127
+ memo = Memo()
128
+
129
+ # Unlock wallet
130
+ memo.unlock_wallet(getpass())
131
+
132
+ # Decode memo
133
+ # Raises exception if required keys not available in the wallet
134
+ print(memo.decrypt(op_data["transfer"]))
135
+
136
+ """
137
+
138
+ def __init__(self, from_account=None, to_account=None, blockchain_instance=None, **kwargs):
139
+ if blockchain_instance is None:
140
+ if kwargs.get("steem_instance"):
141
+ blockchain_instance = kwargs["steem_instance"]
142
+ elif kwargs.get("hive_instance"):
143
+ blockchain_instance = kwargs["hive_instance"]
144
+ self.blockchain = blockchain_instance or shared_blockchain_instance()
145
+
146
+ if to_account and len(to_account) < 51:
147
+ self.to_account = Account(to_account, blockchain_instance=self.blockchain)
148
+ elif to_account and len(to_account) >= 51:
149
+ self.to_account = PublicKey(to_account)
150
+ else:
151
+ self.to_account = None
152
+ if from_account and len(from_account) < 51:
153
+ self.from_account = Account(from_account, blockchain_instance=self.blockchain)
154
+ elif from_account and len(from_account) >= 51:
155
+ self.from_account = PrivateKey(from_account)
156
+ else:
157
+ self.from_account = None
158
+
159
+ def unlock_wallet(self, *args, **kwargs):
160
+ """Unlock the library internal wallet"""
161
+ self.blockchain.wallet.unlock(*args, **kwargs)
162
+
163
+ def encrypt(self, memo, bts_encrypt=False, return_enc_memo_only=False, nonce=None):
164
+ """Encrypt a memo
165
+
166
+ :param str memo: clear text memo message
167
+ :param bool return_enc_memo_only: When True, only the encoded memo is returned
168
+ :param str nonce: when not set, a random string is generated and used
169
+ :returns: encrypted memo
170
+ :rtype: dict
171
+ """
172
+ if not memo:
173
+ return None
174
+ if nonce is None:
175
+ nonce = str(random.getrandbits(64))
176
+ if isinstance(self.from_account, Account):
177
+ memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(
178
+ self.from_account["memo_key"]
179
+ )
180
+ memo_wif = PrivateKey(memo_wif)
181
+ else:
182
+ memo_wif = self.from_account
183
+ if isinstance(self.to_account, Account):
184
+ pubkey = self.to_account["memo_key"]
185
+ else:
186
+ pubkey = self.to_account
187
+ if not memo_wif:
188
+ raise MissingKeyError("Memo key for %s missing!" % self.from_account["name"])
189
+
190
+ if not hasattr(self, "chain_prefix"):
191
+ self.chain_prefix = self.blockchain.prefix
192
+
193
+ if bts_encrypt:
194
+ enc = BtsMemo.encode_memo_bts(
195
+ PrivateKey(memo_wif), PublicKey(pubkey, prefix=self.chain_prefix), nonce, memo
196
+ )
197
+
198
+ return {
199
+ "message": enc,
200
+ "nonce": nonce,
201
+ "from": str(PrivateKey(memo_wif).pubkey),
202
+ "to": str(pubkey),
203
+ }
204
+ else:
205
+ enc = BtsMemo.encode_memo(
206
+ PrivateKey(memo_wif),
207
+ PublicKey(pubkey, prefix=self.chain_prefix),
208
+ nonce,
209
+ memo,
210
+ prefix=self.chain_prefix,
211
+ )
212
+ if return_enc_memo_only:
213
+ return enc
214
+ return {"message": enc, "from": str(PrivateKey(memo_wif).pubkey), "to": str(pubkey)}
215
+
216
+ def encrypt_binary(self, infile, outfile, buffer_size=2048, nonce=None):
217
+ """Encrypt a binary file
218
+
219
+ :param str infile: input file name
220
+ :param str outfile: output file name
221
+ :param int buffer_size: write buffer size
222
+ :param str nonce: when not set, a random string is generated and used
223
+ """
224
+ if not os.path.exists(infile):
225
+ raise ValueError("%s does not exists!" % infile)
226
+
227
+ if nonce is None:
228
+ nonce = str(random.getrandbits(64))
229
+ if isinstance(self.from_account, Account):
230
+ memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(
231
+ self.from_account["memo_key"]
232
+ )
233
+ else:
234
+ memo_wif = self.from_account
235
+ if isinstance(self.to_account, Account):
236
+ pubkey = self.to_account["memo_key"]
237
+ else:
238
+ pubkey = self.to_account
239
+ if not memo_wif:
240
+ raise MissingKeyError("Memo key for %s missing!" % self.from_account["name"])
241
+
242
+ if not hasattr(self, "chain_prefix"):
243
+ self.chain_prefix = self.blockchain.prefix
244
+
245
+ file_size = os.path.getsize(infile)
246
+ priv = PrivateKey(memo_wif)
247
+ pub = PublicKey(pubkey, prefix=self.chain_prefix)
248
+ enc = BtsMemo.encode_memo(
249
+ priv, pub, nonce, "nectar/%s" % __version__, prefix=self.chain_prefix
250
+ )
251
+ enc = unhexlify(base58decode(enc[1:]))
252
+ shared_secret = BtsMemo.get_shared_secret(priv, pub)
253
+ aes, check = BtsMemo.init_aes(shared_secret, nonce)
254
+ with open(outfile, "wb") as fout:
255
+ fout.write(struct.pack("<Q", len(enc)))
256
+ fout.write(enc)
257
+ fout.write(struct.pack("<Q", file_size))
258
+ with open(infile, "rb") as fin:
259
+ while True:
260
+ data = fin.read(buffer_size)
261
+ n = len(data)
262
+ if n == 0:
263
+ break
264
+ elif n % 16 != 0:
265
+ data += b" " * (16 - n % 16) # <- padded with spaces
266
+ encd = aes.encrypt(data)
267
+ fout.write(encd)
268
+
269
+ def extract_decrypt_memo_data(self, memo):
270
+ """Returns information about an encrypted memo"""
271
+ from_key, to_key, nonce, check, cipher = BtsMemo.extract_memo_data(memo)
272
+ return from_key, to_key, nonce
273
+
274
+ def decrypt(self, memo):
275
+ """Decrypt a memo
276
+
277
+ :param str memo: encrypted memo message
278
+ :returns: encrypted memo
279
+ :rtype: str
280
+ """
281
+ if not memo:
282
+ return None
283
+ memo_wif = None
284
+ # We first try to decode assuming we received the memo
285
+ if isinstance(memo, dict) and "to" in memo and "from" in memo and "memo" in memo:
286
+ memo_to = Account(memo["to"], blockchain_instance=self.blockchain)
287
+ memo_from = Account(memo["from"], blockchain_instance=self.blockchain)
288
+ message = memo["memo"]
289
+ else:
290
+ memo_to = self.to_account
291
+ memo_from = self.from_account
292
+ message = memo
293
+ if isinstance(memo, dict) and "nonce" in memo:
294
+ nonce = memo.get("nonce")
295
+ else:
296
+ nonce = ""
297
+
298
+ if memo_to is None or memo_from is None:
299
+ from_key, to_key, nonce, check, cipher = BtsMemo.extract_memo_data(message)
300
+ try:
301
+ memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(str(to_key))
302
+ pubkey = from_key
303
+ except MissingKeyError:
304
+ try:
305
+ # if that failed, we assume that we have sent the memo
306
+ memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(str(from_key))
307
+ pubkey = to_key
308
+ except MissingKeyError:
309
+ # if all fails, raise exception
310
+ raise MissingKeyError(
311
+ "Non of the required memo keys are installed!Need any of {}".format(
312
+ [str(to_key), str(from_key)]
313
+ )
314
+ )
315
+ elif memo_to is not None and memo_from is not None and isinstance(memo_from, PrivateKey):
316
+ memo_wif = memo_from
317
+ pubkey = memo_to
318
+ elif memo_to is not None and memo_from is not None and isinstance(memo_to, PrivateKey):
319
+ memo_wif = memo_to
320
+ pubkey = memo_from
321
+ else:
322
+ try:
323
+ memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(memo_to["memo_key"])
324
+ pubkey = memo_from["memo_key"]
325
+ except MissingKeyError:
326
+ try:
327
+ # if that failed, we assume that we have sent the memo
328
+ memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(
329
+ memo_from["memo_key"]
330
+ )
331
+ pubkey = memo_to["memo_key"]
332
+ except MissingKeyError:
333
+ # if all fails, raise exception
334
+ raise MissingKeyError(
335
+ "Non of the required memo keys are installed!Need any of {}".format(
336
+ [memo_to["name"], memo_from["name"]]
337
+ )
338
+ )
339
+
340
+ if not hasattr(self, "chain_prefix"):
341
+ self.chain_prefix = self.blockchain.prefix
342
+
343
+ if message[0] == "#" or memo_to is None or memo_from is None:
344
+ return BtsMemo.decode_memo(PrivateKey(memo_wif), message)
345
+ else:
346
+ return BtsMemo.decode_memo_bts(
347
+ PrivateKey(memo_wif), PublicKey(pubkey, prefix=self.chain_prefix), nonce, message
348
+ )
349
+
350
+ def decrypt_binary(self, infile, outfile, buffer_size=2048):
351
+ """Decrypt a binary file
352
+
353
+ :param str infile: encrypted binary file
354
+ :param str outfile: output file name
355
+ :param int buffer_size: read buffer size
356
+ :returns: encrypted memo information
357
+ :rtype: dict
358
+ """
359
+ if not os.path.exists(infile):
360
+ raise ValueError("%s does not exists!" % infile)
361
+ if buffer_size % 16 != 0:
362
+ raise ValueError("buffer_size must be dividable by 16")
363
+ with open(infile, "rb") as fin:
364
+ memo_size = struct.unpack("<Q", fin.read(struct.calcsize("<Q")))[0]
365
+ memo = fin.read(memo_size)
366
+ orig_file_size = struct.unpack("<Q", fin.read(struct.calcsize("<Q")))[0]
367
+ memo = "#" + base58encode(hexlify(memo).decode("ascii"))
368
+ memo_to = self.to_account
369
+ memo_from = self.from_account
370
+ from_key, to_key, nonce, check, cipher = BtsMemo.extract_memo_data(memo)
371
+ if memo_to is None and memo_from is None:
372
+ try:
373
+ memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(str(to_key))
374
+ pubkey = from_key
375
+ except MissingKeyError:
376
+ try:
377
+ # if that failed, we assume that we have sent the memo
378
+ memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(str(from_key))
379
+ pubkey = to_key
380
+ except MissingKeyError:
381
+ # if all fails, raise exception
382
+ raise MissingKeyError(
383
+ "Non of the required memo keys are installed!Need any of {}".format(
384
+ [str(to_key), str(from_key)]
385
+ )
386
+ )
387
+ elif memo_to is not None and memo_from is not None and isinstance(memo_from, PrivateKey):
388
+ memo_wif = memo_from
389
+ if isinstance(memo_to, Account):
390
+ pubkey = memo_to["memo_key"]
391
+ else:
392
+ pubkey = memo_to
393
+ elif memo_to is not None and memo_from is not None and isinstance(memo_to, PrivateKey):
394
+ memo_wif = memo_to
395
+ if isinstance(memo_from, Account):
396
+ pubkey = memo_from["memo_key"]
397
+ else:
398
+ pubkey = memo_from
399
+ else:
400
+ try:
401
+ memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(memo_to["memo_key"])
402
+ pubkey = memo_from["memo_key"]
403
+ except MissingKeyError:
404
+ try:
405
+ # if that failed, we assume that we have sent the memo
406
+ memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(
407
+ memo_from["memo_key"]
408
+ )
409
+ pubkey = memo_to["memo_key"]
410
+ except MissingKeyError:
411
+ # if all fails, raise exception
412
+ raise MissingKeyError(
413
+ "Non of the required memo keys are installed!Need any of {}".format(
414
+ [memo_to["name"], memo_from["name"]]
415
+ )
416
+ )
417
+
418
+ if not hasattr(self, "chain_prefix"):
419
+ self.chain_prefix = self.blockchain.prefix
420
+ priv = PrivateKey(memo_wif)
421
+ pubkey = PublicKey(pubkey, prefix=self.chain_prefix)
422
+ nectar_version = BtsMemo.decode_memo(priv, memo)
423
+ shared_secret = BtsMemo.get_shared_secret(priv, pubkey)
424
+ # Init encryption
425
+ aes, checksum = BtsMemo.init_aes(shared_secret, nonce)
426
+ with open(infile, "rb") as fin:
427
+ memo_size = struct.unpack("<Q", fin.read(struct.calcsize("<Q")))[0]
428
+ memo = fin.read(memo_size)
429
+ file_size = struct.unpack("<Q", fin.read(struct.calcsize("<Q")))[0]
430
+ with open(outfile, "wb") as fout:
431
+ while True:
432
+ data = fin.read(buffer_size)
433
+ n = len(data)
434
+ if n == 0:
435
+ break
436
+ decd = aes.decrypt(data)
437
+ n = len(decd)
438
+ if file_size > n:
439
+ fout.write(decd)
440
+ else:
441
+ fout.write(decd[:file_size]) # <- remove padding on last block
442
+ file_size -= n
443
+ return {
444
+ "file_size": orig_file_size,
445
+ "from_key": str(from_key),
446
+ "to_key": str(to_key),
447
+ "nonce": nonce,
448
+ "nectar_version": nectar_version,
449
+ }