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,846 @@
1
+ # -*- coding: utf-8 -*-
2
+ import binascii
3
+ import bisect
4
+ import codecs
5
+ import hashlib
6
+ import itertools
7
+ import os
8
+ import re
9
+ import sys
10
+ import unicodedata
11
+ from binascii import hexlify, unhexlify
12
+
13
+ import ecdsa
14
+
15
+ from .base58 import Base58, doublesha256, ripemd160
16
+ from .bip32 import BIP32Key, parse_path
17
+ from .dictionary import words as BrainKeyDictionary
18
+ from .dictionary import words_bip39 as MnemonicDictionary
19
+ from .prefix import Prefix
20
+ from .py23 import PY2, py23_bytes
21
+
22
+ PBKDF2_ROUNDS = 2048
23
+
24
+
25
+ # From <https://stackoverflow.com/questions/212358/binary-search-bisection-in-python/2233940#2233940>
26
+ def binary_search(a, x, lo=0, hi=None): # can't use a to specify default for hi
27
+ hi = hi if hi is not None else len(a) # hi defaults to len(a)
28
+ pos = bisect.bisect_left(a, x, lo, hi) # find insertion position
29
+ return pos if pos != hi and a[pos] == x else -1 # don't walk off the end
30
+
31
+
32
+ class PasswordKey(Prefix):
33
+ """This class derives a private key given the account name, the
34
+ role and a password. It leverages the technology of Brainkeys
35
+ and allows people to have a secure private key by providing a
36
+ passphrase only.
37
+ """
38
+
39
+ def __init__(self, account, password, role="active", prefix=None):
40
+ self.set_prefix(prefix)
41
+ self.account = account
42
+ self.role = role
43
+ self.password = password
44
+
45
+ def normalize(self, seed):
46
+ """Correct formating with single whitespace syntax and no trailing space"""
47
+ return " ".join(re.compile("[\t\n\v\f\r ]+").split(seed))
48
+
49
+ def get_private(self):
50
+ """Derive private key from the account, the role and the password"""
51
+ if self.account is None and self.role is None:
52
+ seed = self.password
53
+ elif self.account == "" and self.role == "":
54
+ seed = self.password
55
+ else:
56
+ seed = self.account + self.role + self.password
57
+ seed = self.normalize(seed)
58
+ a = py23_bytes(seed, "utf8")
59
+ s = hashlib.sha256(a).digest()
60
+ return PrivateKey(hexlify(s).decode("ascii"), prefix=self.prefix)
61
+
62
+ def get_public(self):
63
+ return self.get_private().pubkey
64
+
65
+ def get_private_key(self):
66
+ return self.get_private()
67
+
68
+ def get_public_key(self):
69
+ return self.get_public()
70
+
71
+
72
+ class BrainKey(Prefix):
73
+ """Brainkey implementation similar to the graphene-ui web-wallet.
74
+
75
+ :param str brainkey: Brain Key
76
+ :param int sequence: Sequence number for consecutive keys
77
+
78
+ Keys in Graphene are derived from a seed brain key which is a string of
79
+ 16 words out of a predefined dictionary with 49744 words. It is a
80
+ simple single-chain key derivation scheme that is not compatible with
81
+ BIP44 but easy to use.
82
+
83
+ Given the brain key, a private key is derived as::
84
+
85
+ privkey = SHA256(SHA512(brainkey + " " + sequence))
86
+
87
+ Incrementing the sequence number yields a new key that can be
88
+ regenerated given the brain key.
89
+
90
+ """
91
+
92
+ def __init__(self, brainkey=None, sequence=0, prefix=None):
93
+ self.set_prefix(prefix)
94
+ if not brainkey:
95
+ self.brainkey = self.suggest()
96
+ else:
97
+ self.brainkey = self.normalize(brainkey).strip()
98
+ self.sequence = sequence
99
+
100
+ def __next__(self):
101
+ """Get the next private key (sequence number increment) for
102
+ iterators
103
+ """
104
+ return self.next_sequence()
105
+
106
+ def next_sequence(self):
107
+ """Increment the sequence number by 1"""
108
+ self.sequence += 1
109
+ return self
110
+
111
+ def normalize(self, brainkey):
112
+ """Correct formating with single whitespace syntax and no trailing space"""
113
+ return " ".join(re.compile("[\t\n\v\f\r ]+").split(brainkey))
114
+
115
+ def get_brainkey(self):
116
+ """Return brain key of this instance"""
117
+ return self.normalize(self.brainkey)
118
+
119
+ def get_private(self):
120
+ """Derive private key from the brain key and the current sequence
121
+ number
122
+ """
123
+ encoded = "%s %d" % (self.brainkey, self.sequence)
124
+ a = py23_bytes(encoded, "ascii")
125
+ s = hashlib.sha256(hashlib.sha512(a).digest()).digest()
126
+ return PrivateKey(hexlify(s).decode("ascii"), prefix=self.prefix)
127
+
128
+ def get_blind_private(self):
129
+ """Derive private key from the brain key (and no sequence number)"""
130
+ a = py23_bytes(self.brainkey, "ascii")
131
+ return PrivateKey(hashlib.sha256(a).hexdigest(), prefix=self.prefix)
132
+
133
+ def get_public(self):
134
+ return self.get_private().pubkey
135
+
136
+ def get_private_key(self):
137
+ return self.get_private()
138
+
139
+ def get_public_key(self):
140
+ return self.get_public()
141
+
142
+ def suggest(self, word_count=16):
143
+ """Suggest a new random brain key. Randomness is provided by the
144
+ operating system using ``os.urandom()``.
145
+ """
146
+ brainkey = [None] * word_count
147
+ dict_lines = BrainKeyDictionary.split(",")
148
+ if not len(dict_lines) == 49744:
149
+ raise AssertionError()
150
+ for j in range(0, word_count):
151
+ urand = os.urandom(2)
152
+ if isinstance(urand, str):
153
+ urand = py23_bytes(urand, "ascii")
154
+ if PY2:
155
+ num = int(codecs.encode(urand[::-1], "hex"), 16)
156
+ else:
157
+ num = int.from_bytes(urand, byteorder="little")
158
+ rndMult = num / 2**16 # returns float between 0..1 (inclusive)
159
+ wIdx = int(round(len(dict_lines) * rndMult))
160
+ brainkey[j] = dict_lines[wIdx]
161
+ return " ".join(brainkey).upper()
162
+
163
+
164
+ # From https://github.com/trezor/python-mnemonic/blob/master/mnemonic/mnemonic.py
165
+ #
166
+ # Copyright (c) 2013 Pavol Rusnak
167
+ # Copyright (c) 2017 mruddy
168
+ class Mnemonic(object):
169
+ """BIP39 mnemoric implementation"""
170
+
171
+ def __init__(self):
172
+ self.wordlist = MnemonicDictionary.split(",")
173
+ self.radix = 2048
174
+
175
+ def generate(self, strength=128):
176
+ """Generates a word list based on the given strength
177
+
178
+ :param int strength: initial entropy strength, must be one of [128, 160, 192, 224, 256]
179
+
180
+ """
181
+ if strength not in [128, 160, 192, 224, 256]:
182
+ raise ValueError(
183
+ "Strength should be one of the following [128, 160, 192, 224, 256], but it is not (%d)."
184
+ % strength
185
+ )
186
+ return self.to_mnemonic(os.urandom(strength // 8))
187
+
188
+ # Adapted from <http://tinyurl.com/oxmn476>
189
+ def to_entropy(self, words):
190
+ if not isinstance(words, list):
191
+ words = words.split(" ")
192
+ if len(words) not in [12, 15, 18, 21, 24]:
193
+ raise ValueError(
194
+ "Number of words must be one of the following: [12, 15, 18, 21, 24], but it is not (%d)."
195
+ % len(words)
196
+ )
197
+ # Look up all the words in the list and construct the
198
+ # concatenation of the original entropy and the checksum.
199
+ concatLenBits = len(words) * 11
200
+ concatBits = [False] * concatLenBits
201
+ wordindex = 0
202
+ use_binary_search = True
203
+ for word in words:
204
+ # Find the words index in the wordlist
205
+ ndx = (
206
+ binary_search(self.wordlist, word)
207
+ if use_binary_search
208
+ else self.wordlist.index(word)
209
+ )
210
+ if ndx < 0:
211
+ raise LookupError('Unable to find "%s" in word list.' % word)
212
+ # Set the next 11 bits to the value of the index.
213
+ for ii in range(11):
214
+ concatBits[(wordindex * 11) + ii] = (ndx & (1 << (10 - ii))) != 0
215
+ wordindex += 1
216
+ checksumLengthBits = concatLenBits // 33
217
+ entropyLengthBits = concatLenBits - checksumLengthBits
218
+ # Extract original entropy as bytes.
219
+ entropy = bytearray(entropyLengthBits // 8)
220
+ for ii in range(len(entropy)):
221
+ for jj in range(8):
222
+ if concatBits[(ii * 8) + jj]:
223
+ entropy[ii] |= 1 << (7 - jj)
224
+ # Take the digest of the entropy.
225
+ hashBytes = hashlib.sha256(entropy).digest()
226
+ if sys.version < "3":
227
+ hashBits = list(
228
+ itertools.chain.from_iterable(
229
+ ([ord(c) & (1 << (7 - i)) != 0 for i in range(8)] for c in hashBytes)
230
+ )
231
+ )
232
+ else:
233
+ hashBits = list(
234
+ itertools.chain.from_iterable(
235
+ ([c & (1 << (7 - i)) != 0 for i in range(8)] for c in hashBytes)
236
+ )
237
+ )
238
+ # Check all the checksum bits.
239
+ for i in range(checksumLengthBits):
240
+ if concatBits[entropyLengthBits + i] != hashBits[i]:
241
+ raise ValueError("Failed checksum.")
242
+ return entropy
243
+
244
+ def to_mnemonic(self, data):
245
+ if len(data) not in [16, 20, 24, 28, 32]:
246
+ raise ValueError(
247
+ "Data length should be one of the following: [16, 20, 24, 28, 32], but it is not (%d)."
248
+ % len(data)
249
+ )
250
+ h = hashlib.sha256(data).hexdigest()
251
+ b = (
252
+ bin(int(binascii.hexlify(data), 16))[2:].zfill(len(data) * 8)
253
+ + bin(int(h, 16))[2:].zfill(256)[: len(data) * 8 // 32]
254
+ )
255
+ result = []
256
+ for i in range(len(b) // 11):
257
+ idx = int(b[i * 11 : (i + 1) * 11], 2)
258
+ result.append(self.wordlist[idx])
259
+
260
+ result_phrase = " ".join(result)
261
+ return result_phrase
262
+
263
+ def check(self, mnemonic):
264
+ """Checks the mnemonic word list is valid
265
+ :param list mnemonic: mnemonic word list with lenght of 12, 15, 18, 21, 24
266
+ :returns: True, when valid
267
+ """
268
+ mnemonic = self.normalize_string(mnemonic).split(" ")
269
+ # list of valid mnemonic lengths
270
+ if len(mnemonic) not in [12, 15, 18, 21, 24]:
271
+ return False
272
+ try:
273
+ idx = map(lambda x: bin(self.wordlist.index(x))[2:].zfill(11), mnemonic)
274
+ b = "".join(idx)
275
+ except ValueError:
276
+ return False
277
+ l = len(b) # noqa: E741
278
+ d = b[: l // 33 * 32]
279
+ h = b[-l // 33 :]
280
+ nd = binascii.unhexlify(hex(int(d, 2))[2:].rstrip("L").zfill(l // 33 * 8))
281
+ nh = bin(int(hashlib.sha256(nd).hexdigest(), 16))[2:].zfill(256)[: l // 33]
282
+ return h == nh
283
+
284
+ def check_word(self, word):
285
+ return word in self.wordlist
286
+
287
+ def expand_word(self, prefix):
288
+ """Expands a word when sufficient chars are given
289
+
290
+ :param str prefix: first chars of a valid dict word
291
+
292
+ """
293
+ if prefix in self.wordlist:
294
+ return prefix
295
+ else:
296
+ matches = [word for word in self.wordlist if word.startswith(prefix)]
297
+ if len(matches) == 1: # matched exactly one word in the wordlist
298
+ return matches[0]
299
+ else:
300
+ # exact match not found.
301
+ # this is not a validation routine, just return the input
302
+ return prefix
303
+
304
+ def expand(self, mnemonic):
305
+ """Expands all words given in a list"""
306
+ return " ".join(map(self.expand_word, mnemonic.split(" ")))
307
+
308
+ @classmethod
309
+ def normalize_string(cls, txt):
310
+ """Normalizes strings"""
311
+ if isinstance(txt, str if sys.version < "3" else bytes):
312
+ utxt = txt.decode("utf8")
313
+ elif isinstance(txt, unicode if sys.version < "3" else str): # noqa: F821
314
+ utxt = txt
315
+ else:
316
+ raise TypeError("String value expected")
317
+
318
+ return unicodedata.normalize("NFKD", utxt)
319
+
320
+ @classmethod
321
+ def to_seed(cls, mnemonic, passphrase=""):
322
+ """Returns a seed based on bip39
323
+
324
+ :param str mnemonic: string containing a valid mnemonic word list
325
+ :param str passphrase: optional, passphrase can be set to modify the returned seed.
326
+
327
+ """
328
+ mnemonic = cls.normalize_string(mnemonic)
329
+ passphrase = cls.normalize_string(passphrase)
330
+ passphrase = "mnemonic" + passphrase
331
+ mnemonic = mnemonic.encode("utf-8")
332
+ passphrase = passphrase.encode("utf-8")
333
+ stretched = hashlib.pbkdf2_hmac("sha512", mnemonic, passphrase, PBKDF2_ROUNDS)
334
+ return stretched[:64]
335
+
336
+
337
+ class MnemonicKey(Prefix):
338
+ """This class derives a private key from a BIP39 mnemoric implementation"""
339
+
340
+ def __init__(
341
+ self, word_list=None, passphrase="", account_sequence=0, key_sequence=0, prefix=None
342
+ ):
343
+ self.set_prefix(prefix)
344
+ if word_list is not None:
345
+ self.set_mnemonic(word_list, passphrase=passphrase)
346
+ else:
347
+ self.seed = None
348
+ self.account_sequence = account_sequence
349
+ self.key_sequence = key_sequence
350
+ self.prefix = prefix
351
+ self.path = "m/48'/13'/0'/%d'/%d'" % (self.account_sequence, self.key_sequence)
352
+
353
+ def set_mnemonic(self, word_list, passphrase=""):
354
+ mnemonic = Mnemonic()
355
+ if not mnemonic.check(word_list):
356
+ raise ValueError("Word list is not valid!")
357
+ self.seed = mnemonic.to_seed(word_list, passphrase=passphrase)
358
+
359
+ def generate_mnemonic(self, passphrase="", strength=256):
360
+ mnemonic = Mnemonic()
361
+ word_list = mnemonic.generate(strength=strength)
362
+ self.seed = mnemonic.to_seed(word_list, passphrase=passphrase)
363
+ return word_list
364
+
365
+ def set_path_BIP32(self, path):
366
+ self.path = path
367
+
368
+ def set_path_BIP44(
369
+ self, account_sequence=0, chain_sequence=0, key_sequence=0, hardened_address=True
370
+ ):
371
+ if account_sequence < 0:
372
+ raise ValueError("account_sequence must be >= 0")
373
+ if key_sequence < 0:
374
+ raise ValueError("key_sequence must be >= 0")
375
+ if chain_sequence < 0:
376
+ raise ValueError("chain_sequence must be >= 0")
377
+ self.account_sequence = account_sequence
378
+ self.key_sequence = key_sequence
379
+ if hardened_address:
380
+ self.path = "m/44'/0'/%d'/%d/%d'" % (
381
+ self.account_sequence,
382
+ chain_sequence,
383
+ self.key_sequence,
384
+ )
385
+ else:
386
+ self.path = "m/44'/0'/%d'/%d/%d" % (
387
+ self.account_sequence,
388
+ chain_sequence,
389
+ self.key_sequence,
390
+ )
391
+
392
+ def set_path_BIP48(self, network_index=13, role="owner", account_sequence=0, key_sequence=0):
393
+ if account_sequence < 0:
394
+ raise ValueError("account_sequence must be >= 0")
395
+ if key_sequence < 0:
396
+ raise ValueError("key_sequence must be >= 0")
397
+ if network_index < 0:
398
+ raise ValueError("network_index must be >= 0")
399
+ if isinstance(role, str) and role not in ["owner", "active", "posting", "memo"]:
400
+ raise ValueError("Wrong role!")
401
+ elif isinstance(role, int) and role < 0:
402
+ raise ValueError("role must be >= 0")
403
+ if role == "owner":
404
+ role = 0
405
+ elif role == "active":
406
+ role = 1
407
+ elif role == "posting":
408
+ role = 4
409
+ elif role == "memo":
410
+ role = 3
411
+
412
+ self.account_sequence = account_sequence
413
+ self.key_sequence = key_sequence
414
+ self.path = "m/48'/%d'/%d'/%d'/%d'" % (
415
+ network_index,
416
+ role,
417
+ self.account_sequence,
418
+ self.key_sequence,
419
+ )
420
+
421
+ def next_account_sequence(self):
422
+ """Increment the account sequence number by 1"""
423
+ self.account_sequence += 1
424
+ return self
425
+
426
+ def next_sequence(self):
427
+ """Increment the key sequence number by 1"""
428
+ self.key_sequence += 1
429
+ return self
430
+
431
+ def set_path(self, path):
432
+ self.path = path
433
+
434
+ def get_path(self):
435
+ return self.path
436
+
437
+ def get_private(self):
438
+ """Derive private key from the account_sequence, the role and the key_sequence"""
439
+ if self.seed is None:
440
+ raise ValueError("seed is None, set or generate a mnemnoric first")
441
+ key = BIP32Key.fromEntropy(self.seed)
442
+ for n in parse_path(self.get_path()):
443
+ key = key.ChildKey(n)
444
+ return PrivateKey(key.WalletImportFormat(), prefix=self.prefix)
445
+
446
+ def get_public(self):
447
+ return self.get_private().pubkey
448
+
449
+ def get_private_key(self):
450
+ return self.get_private()
451
+
452
+ def get_public_key(self):
453
+ return self.get_public()
454
+
455
+
456
+ class Address(Prefix):
457
+ """Address class
458
+
459
+ This class serves as an address representation for Public Keys.
460
+
461
+ :param str address: Base58 encoded address (defaults to ``None``)
462
+ :param str prefix: Network prefix (defaults to ``STM``)
463
+
464
+ Example::
465
+
466
+ Address("STMFN9r6VYzBK8EKtMewfNbfiGCr56pHDBFi")
467
+
468
+ """
469
+
470
+ def __init__(self, address, prefix=None):
471
+ self.set_prefix(prefix)
472
+ self._address = Base58(address, prefix=self.prefix)
473
+
474
+ @classmethod
475
+ def from_pubkey(cls, pubkey, compressed=True, version=56, prefix=None):
476
+ """Load an address provided by the public key.
477
+ Version: 56 => PTS
478
+ """
479
+ # Ensure this is a public key
480
+ pubkey = PublicKey(pubkey, prefix=prefix or Prefix.prefix)
481
+ if compressed:
482
+ pubkey_plain = pubkey.compressed()
483
+ else:
484
+ pubkey_plain = pubkey.uncompressed()
485
+ sha = hashlib.sha256(unhexlify(pubkey_plain)).hexdigest()
486
+ rep = hexlify(ripemd160(sha)).decode("ascii")
487
+ s = ("%.2x" % version) + rep
488
+ result = s + hexlify(doublesha256(s)[:4]).decode("ascii")
489
+ result = hexlify(ripemd160(result)).decode("ascii")
490
+ return cls(result, prefix=pubkey.prefix)
491
+
492
+ @classmethod
493
+ def derivesha256address(cls, pubkey, compressed=True, prefix=None):
494
+ """Derive address using ``RIPEMD160(SHA256(x))``"""
495
+ pubkey = PublicKey(pubkey, prefix=prefix or Prefix.prefix)
496
+ if compressed:
497
+ pubkey_plain = pubkey.compressed()
498
+ else:
499
+ pubkey_plain = pubkey.uncompressed()
500
+ pkbin = unhexlify(repr(pubkey_plain))
501
+ result = hexlify(hashlib.sha256(pkbin).digest())
502
+ result = hexlify(ripemd160(result)).decode("ascii")
503
+ return cls(result, prefix=pubkey.prefix)
504
+
505
+ @classmethod
506
+ def derivesha512address(cls, pubkey, compressed=True, prefix=None):
507
+ """Derive address using ``RIPEMD160(SHA512(x))``"""
508
+ pubkey = PublicKey(pubkey, prefix=prefix or Prefix.prefix)
509
+ if compressed:
510
+ pubkey_plain = pubkey.compressed()
511
+ else:
512
+ pubkey_plain = pubkey.uncompressed()
513
+ pkbin = unhexlify(repr(pubkey_plain))
514
+ result = hexlify(hashlib.sha512(pkbin).digest())
515
+ result = hexlify(ripemd160(result)).decode("ascii")
516
+ return cls(result, prefix=pubkey.prefix)
517
+
518
+ def __repr__(self):
519
+ """Gives the hex representation of the ``GrapheneBase58CheckEncoded``
520
+ Graphene address.
521
+ """
522
+ return repr(self._address)
523
+
524
+ def __str__(self):
525
+ """Returns the readable Graphene address. This call is equivalent to
526
+ ``format(Address, "STM")``
527
+ """
528
+ return format(self._address, self.prefix)
529
+
530
+ def __format__(self, _format):
531
+ """May be issued to get valid "MUSE", "PLAY" or any other Graphene compatible
532
+ address with corresponding prefix.
533
+ """
534
+ return format(self._address, _format)
535
+
536
+ def __bytes__(self):
537
+ """Returns the raw content of the ``Base58CheckEncoded`` address"""
538
+ return py23_bytes(self._address)
539
+
540
+
541
+ class GrapheneAddress(Address):
542
+ """Graphene Addresses are different. Hence we have a different class"""
543
+
544
+ @classmethod
545
+ def from_pubkey(cls, pubkey, compressed=True, version=56, prefix=None):
546
+ # Ensure this is a public key
547
+ pubkey = PublicKey(pubkey, prefix=prefix or Prefix.prefix)
548
+ if compressed:
549
+ pubkey_plain = pubkey.compressed()
550
+ else:
551
+ pubkey_plain = pubkey.uncompressed()
552
+
553
+ """ Derive address using ``RIPEMD160(SHA512(x))`` """
554
+ addressbin = ripemd160(hashlib.sha512(unhexlify(pubkey_plain)).hexdigest())
555
+ result = Base58(hexlify(addressbin).decode("ascii"))
556
+ return cls(result, prefix=pubkey.prefix)
557
+
558
+
559
+ class PublicKey(Prefix):
560
+ """This class deals with Public Keys and inherits ``Address``.
561
+
562
+ :param str pk: Base58 encoded public key
563
+ :param str prefix: Network prefix (defaults to ``STM``)
564
+
565
+ Example::
566
+
567
+ PublicKey("STM6UtYWWs3rkZGV8JA86qrgkG6tyFksgECefKE1MiH4HkLD8PFGL")
568
+
569
+ .. note:: By default, graphene-based networks deal with **compressed**
570
+ public keys. If an **uncompressed** key is required, the
571
+ method :func:`unCompressed` can be used::
572
+
573
+ PublicKey("xxxxx").unCompressed()
574
+
575
+ """
576
+
577
+ def __init__(self, pk, prefix=None):
578
+ """Init PublicKey
579
+ :param str pk: Base58 encoded public key
580
+ :param str prefix: Network prefix (defaults to ``STM``)
581
+ """
582
+ self.set_prefix(prefix)
583
+ if isinstance(pk, PublicKey):
584
+ pk = format(pk, self.prefix)
585
+
586
+ if str(pk).startswith("04"):
587
+ # We only ever deal with compressed keys, so let's make it
588
+ # compressed
589
+ order = ecdsa.SECP256k1.order
590
+ p = ecdsa.VerifyingKey.from_string(
591
+ unhexlify(pk[2:]), curve=ecdsa.SECP256k1
592
+ ).pubkey.point
593
+ x_str = ecdsa.util.number_to_string(p.x(), order)
594
+ pk = hexlify(chr(2 + (p.y() & 1)).encode("ascii") + x_str).decode("ascii")
595
+
596
+ self._pk = Base58(pk, prefix=self.prefix)
597
+
598
+ @property
599
+ def pubkey(self):
600
+ return self._pk
601
+
602
+ def get_public_key(self):
603
+ """Returns the pubkey"""
604
+ return self.pubkey
605
+
606
+ @property
607
+ def compressed_key(self):
608
+ return PublicKey(self.compressed())
609
+
610
+ def _derive_y_from_x(self, x, is_even):
611
+ """Derive y point from x point"""
612
+ curve = ecdsa.SECP256k1.curve
613
+ # The curve equation over F_p is:
614
+ # y^2 = x^3 + ax + b
615
+ a, b, p = curve.a(), curve.b(), curve.p()
616
+ alpha = (pow(x, 3, p) + a * x + b) % p
617
+ beta = ecdsa.numbertheory.square_root_mod_prime(alpha, p)
618
+ if (beta % 2) == is_even:
619
+ beta = p - beta
620
+ return beta
621
+
622
+ def compressed(self):
623
+ """Derive compressed public key"""
624
+ return repr(self._pk)
625
+
626
+ def uncompressed(self):
627
+ """Derive uncompressed key"""
628
+ public_key = repr(self._pk)
629
+ prefix = public_key[0:2]
630
+ if prefix == "04":
631
+ return public_key
632
+ if not (prefix == "02" or prefix == "03"):
633
+ raise AssertionError()
634
+ x = int(public_key[2:], 16)
635
+ y = self._derive_y_from_x(x, (prefix == "02"))
636
+ key = "04" + "%064x" % x + "%064x" % y
637
+ return key
638
+
639
+ def point(self):
640
+ """Return the point for the public key"""
641
+ string = unhexlify(self.uncompressed())
642
+ return ecdsa.VerifyingKey.from_string(string[1:], curve=ecdsa.SECP256k1).pubkey.point
643
+
644
+ def child(self, offset256):
645
+ """Derive new public key from this key and a sha256 "offset" """
646
+ a = bytes(self) + offset256
647
+ s = hashlib.sha256(a).digest()
648
+ return self.add(s)
649
+
650
+ def add(self, digest256):
651
+ """Derive new public key from this key and a sha256 "digest" """
652
+ from .ecdsa import tweakaddPubkey
653
+
654
+ return tweakaddPubkey(self, digest256)
655
+
656
+ @classmethod
657
+ def from_privkey(cls, privkey, prefix=None):
658
+ """Derive uncompressed public key"""
659
+ privkey = PrivateKey(privkey, prefix=prefix or Prefix.prefix)
660
+ secret = unhexlify(repr(privkey))
661
+ order = ecdsa.SigningKey.from_string(secret, curve=ecdsa.SECP256k1).curve.generator.order()
662
+ p = ecdsa.SigningKey.from_string(secret, curve=ecdsa.SECP256k1).verifying_key.pubkey.point
663
+ x_str = ecdsa.util.number_to_string(p.x(), order)
664
+ # y_str = ecdsa.util.number_to_string(p.y(), order)
665
+ compressed = hexlify(chr(2 + (p.y() & 1)).encode("ascii") + x_str).decode("ascii")
666
+ # uncompressed = hexlify(
667
+ # chr(4).encode('ascii') + x_str + y_str).decode('ascii')
668
+ return cls(compressed, prefix=prefix or Prefix.prefix)
669
+
670
+ def __repr__(self):
671
+ """Gives the hex representation of the Graphene public key."""
672
+ return repr(self._pk)
673
+
674
+ def __str__(self):
675
+ """Returns the readable Graphene public key. This call is equivalent to
676
+ ``format(PublicKey, "STM")``
677
+ """
678
+ return format(self._pk, self.prefix)
679
+
680
+ def __format__(self, _format):
681
+ """Formats the instance of:doc:`Base58 <base58>` according to ``_format``"""
682
+ return format(self._pk, _format)
683
+
684
+ def __bytes__(self):
685
+ """Returns the raw public key (has length 33)"""
686
+ return py23_bytes(self._pk)
687
+
688
+ def __lt__(self, other):
689
+ """For sorting of public keys (due to graphene),
690
+ we actually sort according to addresses
691
+ """
692
+ assert isinstance(other, PublicKey)
693
+ return repr(self.address) < repr(other.address)
694
+
695
+ def unCompressed(self):
696
+ """Alias for self.uncompressed() - LEGACY"""
697
+ return self.uncompressed()
698
+
699
+ @property
700
+ def address(self):
701
+ """Obtain a GrapheneAddress from a public key"""
702
+ return GrapheneAddress.from_pubkey(repr(self), prefix=self.prefix)
703
+
704
+
705
+ class PrivateKey(Prefix):
706
+ """Derives the compressed and uncompressed public keys and
707
+ constructs two instances of :class:`PublicKey`:
708
+
709
+ :param str wif: Base58check-encoded wif key
710
+ :param str prefix: Network prefix (defaults to ``STM``)
711
+
712
+ Example::
713
+
714
+ PrivateKey("5HqUkGuo62BfcJU5vNhTXKJRXuUi9QSE6jp8C3uBJ2BVHtB8WSd")
715
+
716
+ Compressed vs. Uncompressed:
717
+
718
+ * ``PrivateKey("w-i-f").pubkey``:
719
+ Instance of :class:`PublicKey` using compressed key.
720
+ * ``PrivateKey("w-i-f").pubkey.address``:
721
+ Instance of :class:`Address` using compressed key.
722
+ * ``PrivateKey("w-i-f").uncompressed``:
723
+ Instance of :class:`PublicKey` using uncompressed key.
724
+ * ``PrivateKey("w-i-f").uncompressed.address``:
725
+ Instance of :class:`Address` using uncompressed key.
726
+
727
+ """
728
+
729
+ def __init__(self, wif=None, prefix=None):
730
+ self.set_prefix(prefix)
731
+ if wif is None:
732
+ import os
733
+
734
+ self._wif = Base58(hexlify(os.urandom(32)).decode("ascii"))
735
+ elif isinstance(wif, PrivateKey):
736
+ self._wif = wif._wif
737
+ elif isinstance(wif, Base58):
738
+ self._wif = wif
739
+ else:
740
+ self._wif = Base58(wif)
741
+
742
+ assert len(repr(self._wif)) == 64
743
+
744
+ @property
745
+ def bitcoin(self):
746
+ return BitcoinPublicKey.from_privkey(self)
747
+
748
+ @property
749
+ def address(self):
750
+ return Address.from_pubkey(self.pubkey, prefix=self.prefix)
751
+
752
+ @property
753
+ def pubkey(self):
754
+ return self.compressed
755
+
756
+ def get_public_key(self):
757
+ """Legacy: Returns the pubkey"""
758
+ return self.pubkey
759
+
760
+ @property
761
+ def compressed(self):
762
+ return PublicKey.from_privkey(self, prefix=self.prefix)
763
+
764
+ @property
765
+ def uncompressed(self):
766
+ return PublicKey(self.pubkey.uncompressed(), prefix=self.prefix)
767
+
768
+ def get_secret(self):
769
+ """Get sha256 digest of the wif key."""
770
+ return hashlib.sha256(bytes(self)).digest()
771
+
772
+ def derive_private_key(self, sequence):
773
+ """Derive new private key from this private key and an arbitrary
774
+ sequence number
775
+ """
776
+ encoded = "%s %d" % (str(self), sequence)
777
+ a = py23_bytes(encoded, "ascii")
778
+ s = hashlib.sha256(hashlib.sha512(a).digest()).digest()
779
+ return PrivateKey(hexlify(s).decode("ascii"), prefix=self.pubkey.prefix)
780
+
781
+ def child(self, offset256):
782
+ """Derive new private key from this key and a sha256 "offset" """
783
+ a = py23_bytes(self.pubkey) + offset256
784
+ s = hashlib.sha256(a).digest()
785
+ return self.derive_from_seed(s)
786
+
787
+ def derive_from_seed(self, offset):
788
+ """Derive private key using "generate_from_seed" method.
789
+ Here, the key itself serves as a `seed`, and `offset`
790
+ is expected to be a sha256 digest.
791
+ """
792
+ seed = int(hexlify(py23_bytes(self)).decode("ascii"), 16)
793
+ z = int(hexlify(offset).decode("ascii"), 16)
794
+ order = ecdsa.SECP256k1.order
795
+ secexp = (seed + z) % order
796
+ secret = "%0x" % secexp
797
+ if len(secret) < 64: # left-pad with zeroes
798
+ secret = ("0" * (64 - len(secret))) + secret
799
+ return PrivateKey(secret, prefix=self.pubkey.prefix)
800
+
801
+ def __format__(self, _format):
802
+ """Formats the instance of:doc:`Base58 <base58>` according to
803
+ ``_format``
804
+ """
805
+ return format(self._wif, _format)
806
+
807
+ def __repr__(self):
808
+ """Gives the hex representation of the Graphene private key."""
809
+ return repr(self._wif)
810
+
811
+ def __str__(self):
812
+ """Returns the readable (uncompressed wif format) Graphene private key. This
813
+ call is equivalent to ``format(PrivateKey, "WIF")``
814
+ """
815
+ return format(self._wif, "WIF")
816
+
817
+ def __bytes__(self):
818
+ """Returns the raw private key"""
819
+ return py23_bytes(self._wif)
820
+
821
+
822
+ class BitcoinAddress(Address):
823
+ @classmethod
824
+ def from_pubkey(cls, pubkey, compressed=False, version=56, prefix=None):
825
+ # Ensure this is a public key
826
+ pubkey = PublicKey(pubkey)
827
+ if compressed:
828
+ pubkey = pubkey.compressed()
829
+ else:
830
+ pubkey = pubkey.uncompressed()
831
+
832
+ """ Derive address using ``RIPEMD160(SHA256(x))`` """
833
+ addressbin = ripemd160(hexlify(hashlib.sha256(unhexlify(pubkey)).digest()))
834
+ return cls(hexlify(addressbin).decode("ascii"))
835
+
836
+ def __str__(self):
837
+ """Returns the readable Graphene address. This call is equivalent to
838
+ ``format(Address, "GPH")``
839
+ """
840
+ return format(self._address, "BTC")
841
+
842
+
843
+ class BitcoinPublicKey(PublicKey):
844
+ @property
845
+ def address(self):
846
+ return BitcoinAddress.from_pubkey(repr(self))