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/message.py ADDED
@@ -0,0 +1,357 @@
1
+ # -*- coding: utf-8 -*-
2
+ import json
3
+ import logging
4
+ import re
5
+ from binascii import hexlify, unhexlify
6
+ from datetime import datetime, timezone
7
+
8
+ from nectar.account import Account
9
+ from nectar.instance import shared_blockchain_instance
10
+ from nectargraphenebase.account import PublicKey
11
+ from nectargraphenebase.ecdsasig import sign_message, verify_message
12
+
13
+ from .exceptions import (
14
+ AccountDoesNotExistsException,
15
+ InvalidMemoKeyException,
16
+ InvalidMessageSignature,
17
+ WrongMemoKey,
18
+ )
19
+
20
+ log = logging.getLogger(__name__)
21
+
22
+
23
+ class MessageV1(object):
24
+ """Allow to sign and verify Messages that are sigend with a private key"""
25
+
26
+ MESSAGE_SPLIT = (
27
+ "-----BEGIN HIVE SIGNED MESSAGE-----",
28
+ "-----BEGIN META-----",
29
+ "-----BEGIN SIGNATURE-----",
30
+ "-----END HIVE SIGNED MESSAGE-----",
31
+ )
32
+
33
+ # This is the message that is actually signed
34
+ SIGNED_MESSAGE_META = """{message}
35
+ account={meta[account]}
36
+ memokey={meta[memokey]}
37
+ block={meta[block]}
38
+ timestamp={meta[timestamp]}"""
39
+
40
+ SIGNED_MESSAGE_ENCAPSULATED = """
41
+ {MESSAGE_SPLIT[0]}
42
+ {message}
43
+ {MESSAGE_SPLIT[1]}
44
+ account={meta[account]}
45
+ memokey={meta[memokey]}
46
+ block={meta[block]}
47
+ timestamp={meta[timestamp]}
48
+ {MESSAGE_SPLIT[2]}
49
+ {signature}
50
+ {MESSAGE_SPLIT[3]}"""
51
+
52
+ def __init__(self, message, blockchain_instance=None, *args, **kwargs):
53
+ if blockchain_instance is None:
54
+ if kwargs.get("steem_instance"):
55
+ blockchain_instance = kwargs["steem_instance"]
56
+ elif kwargs.get("hive_instance"):
57
+ blockchain_instance = kwargs["hive_instance"]
58
+ self.blockchain = blockchain_instance or shared_blockchain_instance()
59
+ self.message = message.replace("\r\n", "\n")
60
+ self.signed_by_account = None
61
+ self.signed_by_name = None
62
+ self.meta = None
63
+ self.plain_message = None
64
+
65
+ def sign(self, account=None, **kwargs):
66
+ """Sign a message with an account's memo key
67
+ :param str account: (optional) the account that owns the bet
68
+ (defaults to ``default_account``)
69
+ :raises ValueError: If not account for signing is provided
70
+ :returns: the signed message encapsulated in a known format
71
+ """
72
+ if not account:
73
+ if "default_account" in self.blockchain.config:
74
+ account = self.blockchain.config["default_account"]
75
+ if not account:
76
+ raise ValueError("You need to provide an account")
77
+
78
+ # Data for message
79
+ account = Account(account, blockchain_instance=self.blockchain)
80
+ info = self.blockchain.info()
81
+ meta = dict(
82
+ timestamp=info["time"],
83
+ block=info["head_block_number"],
84
+ memokey=account["memo_key"],
85
+ account=account["name"],
86
+ )
87
+
88
+ # wif key
89
+ wif = self.blockchain.wallet.getPrivateKeyForPublicKey(account["memo_key"])
90
+
91
+ # We strip the message here so we know for sure there are no trailing
92
+ # whitespaces or returns
93
+ message = self.message.strip()
94
+
95
+ enc_message = self.SIGNED_MESSAGE_META.format(**locals())
96
+
97
+ # signature
98
+ signature = hexlify(sign_message(enc_message, wif)).decode("ascii")
99
+
100
+ self.signed_by_account = account
101
+ self.signed_by_name = account["name"]
102
+ self.meta = meta
103
+ self.plain_message = message
104
+
105
+ return self.SIGNED_MESSAGE_ENCAPSULATED.format(MESSAGE_SPLIT=self.MESSAGE_SPLIT, **locals())
106
+
107
+ def verify(self, **kwargs):
108
+ """Verify a message with an account's memo key
109
+ :param str account: (optional) the account that owns the bet
110
+ (defaults to ``default_account``)
111
+ :returns: True if the message is verified successfully
112
+ :raises InvalidMessageSignature if the signature is not ok
113
+ """
114
+ # Split message into its parts
115
+ parts = re.split("|".join(self.MESSAGE_SPLIT), self.message)
116
+ parts = [x for x in parts if x.strip()]
117
+
118
+ assert len(parts) > 2, "Incorrect number of message parts"
119
+
120
+ # Strip away all whitespaces before and after the message
121
+ message = parts[0].strip()
122
+ signature = parts[2].strip()
123
+ # Parse the meta data
124
+ meta = dict(re.findall(r"(\S+)=(.*)", parts[1]))
125
+
126
+ log.info("Message is: {}".format(message))
127
+ log.info("Meta is: {}".format(json.dumps(meta)))
128
+ log.info("Signature is: {}".format(signature))
129
+
130
+ # Ensure we have all the data in meta
131
+ assert "account" in meta, "No 'account' could be found in meta data"
132
+ assert "memokey" in meta, "No 'memokey' could be found in meta data"
133
+ assert "block" in meta, "No 'block' could be found in meta data"
134
+ assert "timestamp" in meta, "No 'timestamp' could be found in meta data"
135
+
136
+ account_name = meta.get("account").strip()
137
+ memo_key = meta["memokey"].strip()
138
+
139
+ try:
140
+ PublicKey(memo_key, prefix=self.blockchain.prefix)
141
+ except Exception:
142
+ raise InvalidMemoKeyException("The memo key in the message is invalid")
143
+
144
+ # Load account from blockchain
145
+ try:
146
+ account = Account(account_name, blockchain_instance=self.blockchain)
147
+ except AccountDoesNotExistsException:
148
+ raise AccountDoesNotExistsException(
149
+ "Could not find account {}. Are you connected to the right chain?".format(
150
+ account_name
151
+ )
152
+ )
153
+
154
+ # Test if memo key is the same as on the blockchain
155
+ if not account["memo_key"] == memo_key:
156
+ raise WrongMemoKey(
157
+ "Memo Key of account {} on the Blockchain ".format(account["name"])
158
+ + "differs from memo key in the message: {} != {}".format(
159
+ account["memo_key"], memo_key
160
+ )
161
+ )
162
+
163
+ # Reformat message
164
+ enc_message = self.SIGNED_MESSAGE_META.format(**locals())
165
+
166
+ # Verify Signature
167
+ pubkey = verify_message(enc_message, unhexlify(signature))
168
+
169
+ # Verify pubky
170
+ pk = PublicKey(hexlify(pubkey).decode("ascii"), prefix=self.blockchain.prefix)
171
+ if format(pk, self.blockchain.prefix) != memo_key:
172
+ raise InvalidMessageSignature("The signature doesn't match the memo key")
173
+
174
+ self.signed_by_account = account
175
+ self.signed_by_name = account["name"]
176
+ self.meta = meta
177
+ self.plain_message = message
178
+
179
+ return True
180
+
181
+
182
+ class MessageV2(object):
183
+ """Allow to sign and verify Messages that are sigend with a private key"""
184
+
185
+ def __init__(self, message, blockchain_instance=None, *args, **kwargs):
186
+ if blockchain_instance is None:
187
+ if kwargs.get("steem_instance"):
188
+ blockchain_instance = kwargs["steem_instance"]
189
+ elif kwargs.get("hive_instance"):
190
+ blockchain_instance = kwargs["hive_instance"]
191
+ self.blockchain = blockchain_instance or shared_blockchain_instance()
192
+
193
+ self.message = message
194
+ self.signed_by_account = None
195
+ self.signed_by_name = None
196
+ self.meta = None
197
+ self.plain_message = None
198
+
199
+ def sign(self, account=None, **kwargs):
200
+ """Sign a message with an account's memo key
201
+ :param str account: (optional) the account that owns the bet
202
+ (defaults to ``default_account``)
203
+ :raises ValueError: If not account for signing is provided
204
+ :returns: the signed message encapsulated in a known format
205
+ """
206
+ if not account:
207
+ if "default_account" in self.blockchain.config:
208
+ account = self.blockchain.config["default_account"]
209
+ if not account:
210
+ raise ValueError("You need to provide an account")
211
+
212
+ # Data for message
213
+ account = Account(account, blockchain_instance=self.blockchain)
214
+
215
+ # wif key
216
+ wif = self.blockchain.wallet.getPrivateKeyForPublicKey(account["memo_key"])
217
+
218
+ payload = [
219
+ "from",
220
+ account["name"],
221
+ "key",
222
+ account["memo_key"],
223
+ "time",
224
+ str(datetime.now(timezone.utc)),
225
+ "text",
226
+ self.message,
227
+ ]
228
+ enc_message = json.dumps(payload, separators=(",", ":"))
229
+
230
+ # signature
231
+ signature = hexlify(sign_message(enc_message, wif)).decode("ascii")
232
+
233
+ return dict(signed=enc_message, payload=payload, signature=signature)
234
+
235
+ def verify(self, **kwargs):
236
+ """Verify a message with an account's memo key
237
+ :param str account: (optional) the account that owns the bet
238
+ (defaults to ``default_account``)
239
+ :returns: True if the message is verified successfully
240
+ :raises InvalidMessageSignature if the signature is not ok
241
+ """
242
+ if not isinstance(self.message, dict):
243
+ try:
244
+ self.message = json.loads(self.message)
245
+ except Exception:
246
+ raise ValueError("Message must be valid JSON")
247
+
248
+ payload = self.message.get("payload")
249
+ assert payload, "Missing payload"
250
+ payload_dict = {k[0]: k[1] for k in zip(payload[::2], payload[1::2])}
251
+ signature = self.message.get("signature")
252
+
253
+ account_name = payload_dict.get("from").strip()
254
+ memo_key = payload_dict.get("key").strip()
255
+
256
+ assert account_name, "Missing account name 'from'"
257
+ assert memo_key, "missing 'key'"
258
+
259
+ try:
260
+ Account(memo_key, prefix=self.blockchain.prefix)
261
+ except Exception:
262
+ raise InvalidMemoKeyException("The memo key in the message is invalid")
263
+
264
+ # Load account from blockchain
265
+ try:
266
+ account = Account(account_name, blockchain_instance=self.blockchain)
267
+ except AccountDoesNotExistsException:
268
+ raise AccountDoesNotExistsException(
269
+ "Could not find account {}. Are you connected to the right chain?".format(
270
+ account_name
271
+ )
272
+ )
273
+
274
+ # Test if memo key is the same as on the blockchain
275
+ if not account["memo_key"] == memo_key:
276
+ raise WrongMemoKey(
277
+ "Memo Key of account {} on the Blockchain ".format(account["name"])
278
+ + "differs from memo key in the message: {} != {}".format(
279
+ account["memo_key"], memo_key
280
+ )
281
+ )
282
+
283
+ # Ensure payload and signed match
284
+ signed_target = json.dumps(self.message.get("payload"), separators=(",", ":"))
285
+ signed_actual = self.message.get("signed")
286
+ assert signed_target == signed_actual, (
287
+ "payload doesn't match signed message: \n{}\n{}".format(signed_target, signed_actual)
288
+ )
289
+
290
+ # Reformat message
291
+ enc_message = self.message.get("signed")
292
+
293
+ # Verify Signature
294
+ pubkey = verify_message(enc_message, unhexlify(signature))
295
+
296
+ # Verify pubky
297
+ pk = PublicKey(hexlify(pubkey).decode("ascii"), prefix=self.blockchain.prefix)
298
+ if format(pk, self.blockchain.prefix) != memo_key:
299
+ raise InvalidMessageSignature("The signature doesn't match the memo key")
300
+
301
+ self.signed_by_account = account
302
+ self.signed_by_name = account["name"]
303
+ self.plain_message = payload_dict.get("text")
304
+
305
+ return True
306
+
307
+
308
+ class Message(MessageV1, MessageV2):
309
+ supported_formats = (MessageV1, MessageV2)
310
+ valid_exceptions = (
311
+ AccountDoesNotExistsException,
312
+ InvalidMessageSignature,
313
+ WrongMemoKey,
314
+ InvalidMemoKeyException,
315
+ )
316
+
317
+ def __init__(self, *args, **kwargs):
318
+ for _format in self.supported_formats:
319
+ try:
320
+ _format.__init__(self, *args, **kwargs)
321
+ return
322
+ except self.valid_exceptions as e:
323
+ raise e
324
+ except Exception as e:
325
+ log.warning(
326
+ "{}: Couldn't init: {}: {}".format(
327
+ _format.__name__, e.__class__.__name__, str(e)
328
+ )
329
+ )
330
+
331
+ def verify(self, **kwargs):
332
+ for _format in self.supported_formats:
333
+ try:
334
+ return _format.verify(self, **kwargs)
335
+ except self.valid_exceptions as e:
336
+ raise e
337
+ except Exception as e:
338
+ log.warning(
339
+ "{}: Couldn't verify: {}: {}".format(
340
+ _format.__name__, e.__class__.__name__, str(e)
341
+ )
342
+ )
343
+ raise ValueError("No Decoder accepted the message")
344
+
345
+ def sign(self, *args, **kwargs):
346
+ for _format in self.supported_formats:
347
+ try:
348
+ return _format.sign(self, *args, **kwargs)
349
+ except self.valid_exceptions as e:
350
+ raise e
351
+ except Exception as e:
352
+ log.warning(
353
+ "{}: Couldn't sign: {}: {}".format(
354
+ _format.__name__, e.__class__.__name__, str(e)
355
+ )
356
+ )
357
+ raise ValueError("No Decoder accepted the message")