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
@@ -0,0 +1,575 @@
1
+ #!/usr/bin/env python
2
+ #
3
+ # Copyright 2014 Corgan Labs
4
+ # See LICENSE.txt for distribution terms
5
+ # https://github.com/namuyan/bip32nem/blob/master/bip32nem/BIP32Key.py
6
+
7
+ import codecs
8
+ import hashlib
9
+ import hmac
10
+ import os
11
+ import struct
12
+ from binascii import hexlify, unhexlify
13
+ from hashlib import sha256
14
+ from typing import List, Optional, Tuple, Union
15
+
16
+ try:
17
+ import ecdsa
18
+ from ecdsa.curves import SECP256k1
19
+ from ecdsa.ellipticcurve import INFINITY as INFINITY_CONST
20
+ from ecdsa.ellipticcurve import Point as PointClass
21
+ from ecdsa.numbertheory import square_root_mod_prime as sqrt_mod
22
+
23
+ _HAS_ELLIPTIC_CURVE = True
24
+ except ImportError:
25
+ # Fallback for older versions
26
+ import ecdsa
27
+ from ecdsa.curves import SECP256k1
28
+ from ecdsa.numbertheory import square_root_mod_prime as sqrt_mod
29
+
30
+ PointClass = None # type: ignore
31
+ INFINITY_CONST = None # type: ignore
32
+ _HAS_ELLIPTIC_CURVE = False
33
+
34
+ from nectargraphenebase.base58 import base58CheckDecode, base58CheckEncode
35
+
36
+ VerifyKey = ecdsa.VerifyingKey.from_public_point
37
+ SigningKey = ecdsa.SigningKey.from_string
38
+ if _HAS_ELLIPTIC_CURVE and PointClass is not None:
39
+ PointObject = PointClass
40
+ else:
41
+ PointObject = ecdsa.ellipticcurve.Point # type: ignore
42
+ CURVE_GEN = ecdsa.SECP256k1.generator # PointJacobi class
43
+ CURVE_ORDER = CURVE_GEN.order() # int
44
+ FIELD_ORDER = SECP256k1.curve.p() # int
45
+ if _HAS_ELLIPTIC_CURVE and INFINITY_CONST is not None:
46
+ INFINITY = INFINITY_CONST
47
+ else:
48
+ INFINITY = ecdsa.ellipticcurve.INFINITY # type: ignore
49
+
50
+ MIN_ENTROPY_LEN = 128 # bits
51
+ BIP32_HARDEN = 0x80000000 # choose from hardened set of child keys
52
+ EX_MAIN_PRIVATE = [
53
+ codecs.decode("0488ade4", "hex")
54
+ ] # Version strings for mainnet extended private keys
55
+ EX_MAIN_PUBLIC = [
56
+ codecs.decode("0488b21e", "hex"),
57
+ codecs.decode("049d7cb2", "hex"),
58
+ ] # Version strings for mainnet extended public keys
59
+ EX_TEST_PRIVATE = [
60
+ codecs.decode("04358394", "hex")
61
+ ] # Version strings for testnet extended private keys
62
+ EX_TEST_PUBLIC = [
63
+ codecs.decode("043587CF", "hex")
64
+ ] # Version strings for testnet extended public keys
65
+
66
+
67
+ def int_to_hex(x: int) -> bytes:
68
+ return bytes(hex(x)[2:], encoding="utf-8")
69
+
70
+
71
+ def parse_path(nstr: str, as_bytes: bool = False) -> Union[List[int], bytes]:
72
+ """Parse a derivation path like \"m/0'/1/2\" into a list of indexes or bytes."""
73
+ r = list()
74
+ for s in nstr.split("/"):
75
+ if s == "m":
76
+ continue
77
+ elif s.endswith("'") or s.endswith("h"):
78
+ r.append(int(s[:-1]) + BIP32_HARDEN)
79
+ else:
80
+ r.append(int(s))
81
+ if not as_bytes:
82
+ return r
83
+ path = b""
84
+ for p in r:
85
+ path += int_to_hex(p)
86
+ return path
87
+
88
+
89
+ class BIP32Key:
90
+ # Static initializers to create from entropy or external formats
91
+ #
92
+ @classmethod
93
+ def fromEntropy(
94
+ cls, entropy: Optional[bytes] = None, public: bool = False, testnet: bool = False
95
+ ) -> "BIP32Key":
96
+ """Create a BIP32Key using supplied entropy >= MIN_ENTROPY_LEN"""
97
+ if entropy is None:
98
+ entropy = os.urandom(MIN_ENTROPY_LEN // 8) # Python doesn't have os.random()
99
+ if not len(entropy) >= MIN_ENTROPY_LEN // 8:
100
+ raise ValueError(
101
+ "Initial entropy %i must be at least %i bits" % (len(entropy), MIN_ENTROPY_LEN)
102
+ )
103
+ i64 = hmac.new(b"Bitcoin seed", entropy, hashlib.sha512).digest()
104
+ il, ir = i64[:32], i64[32:]
105
+ # FIXME test Il for 0 or less than SECP256k1 prime field order
106
+ key = BIP32Key(
107
+ secret=il, chain=ir, depth=0, index=0, fpr=b"\0\0\0\0", public=False, testnet=testnet
108
+ )
109
+ if public:
110
+ key.SetPublic()
111
+ return key
112
+
113
+ @staticmethod
114
+ def fromExtendedKey(xkey: str, public: bool = False) -> "BIP32Key":
115
+ """
116
+ Create a BIP32Key by importing from extended private or public key string
117
+
118
+ If public is True, return a public-only key regardless of input type.
119
+ """
120
+ # Sanity checks
121
+ # raw = check_decode(xkey)
122
+ raw = unhexlify(base58CheckDecode(xkey, skip_first_bytes=False))
123
+
124
+ if len(raw) != 78:
125
+ raise ValueError("extended key format wrong length")
126
+
127
+ # Verify address version/type
128
+ version = raw[:4]
129
+ if version in EX_MAIN_PRIVATE:
130
+ is_testnet = False
131
+ is_pubkey = False
132
+ elif version in EX_TEST_PRIVATE:
133
+ is_testnet = True
134
+ is_pubkey = False
135
+ elif version in EX_MAIN_PUBLIC:
136
+ is_testnet = False
137
+ is_pubkey = True
138
+ elif version in EX_TEST_PUBLIC:
139
+ is_testnet = True
140
+ is_pubkey = True
141
+ else:
142
+ raise ValueError("unknown extended key version")
143
+
144
+ # Extract remaining fields
145
+ depth = raw[4]
146
+ fpr = raw[5:9]
147
+ child = struct.unpack(">L", raw[9:13])[0]
148
+ chain = raw[13:45]
149
+ secret = raw[45:78]
150
+
151
+ # Extract private key or public key point
152
+ if not is_pubkey:
153
+ secret = secret[1:]
154
+ else:
155
+ # Recover public curve point from compressed key
156
+ lsb = secret[0] & 1
157
+ x = int.from_bytes(secret[1:], "big")
158
+ ys = (x**3 + 7) % FIELD_ORDER # y^2 = x^3 + 7 mod p
159
+ y = sqrt_mod(ys, FIELD_ORDER)
160
+ if y & 1 != lsb:
161
+ y = FIELD_ORDER - y
162
+ point = PointObject(SECP256k1.curve, x, y)
163
+ secret = VerifyKey(point, curve=SECP256k1)
164
+
165
+ key = BIP32Key(
166
+ secret=secret,
167
+ chain=chain,
168
+ depth=depth,
169
+ index=child,
170
+ fpr=fpr,
171
+ public=is_pubkey,
172
+ testnet=is_testnet,
173
+ )
174
+ if not is_pubkey and public:
175
+ key.SetPublic()
176
+ return key
177
+
178
+ # Normal class initializer
179
+ def __init__(
180
+ self,
181
+ secret: Union[bytes, ecdsa.VerifyingKey],
182
+ chain: bytes,
183
+ depth: int,
184
+ index: int,
185
+ fpr: bytes,
186
+ public: bool = False,
187
+ testnet: bool = False,
188
+ ) -> None:
189
+ """
190
+ Create a public or private BIP32Key using key material and chain code.
191
+
192
+ secret This is the source material to generate the keypair, either a
193
+ 32-byte str representation of a private key, or the ECDSA
194
+ library object representing a public key.
195
+
196
+ chain This is a 32-byte str representation of the chain code
197
+
198
+ depth Child depth; parent increments its own by one when assigning this
199
+
200
+ index Child index
201
+
202
+ fpr Parent fingerprint
203
+
204
+ public If true, this keypair will only contain a public key and can only create
205
+ a public key chain.
206
+ """
207
+
208
+ self.public = public
209
+ if public is False:
210
+ self.k = SigningKey(secret, curve=SECP256k1)
211
+ self.K = self.k.get_verifying_key()
212
+ else:
213
+ self.k = None
214
+ self.K = secret
215
+
216
+ self.C = chain
217
+ self.depth = depth
218
+ self.index = index
219
+ self.parent_fpr = fpr
220
+ self.testnet = testnet
221
+
222
+ # Internal methods not intended to be called externally
223
+ #
224
+ def hmac(self, data: bytes) -> Tuple[bytes, bytes]:
225
+ """
226
+ Calculate the HMAC-SHA512 of input data using the chain code as key.
227
+
228
+ Returns a tuple of the left and right halves of the HMAC
229
+ """
230
+ i64 = hmac.new(self.C, data, hashlib.sha512).digest()
231
+ return i64[:32], i64[32:]
232
+
233
+ def CKDpriv(self, i: int) -> Optional["BIP32Key"]:
234
+ """
235
+ Create a child key of index 'i'.
236
+
237
+ If the most significant bit of 'i' is set, then select from the
238
+ hardened key set, otherwise, select a regular child key.
239
+
240
+ Returns a BIP32Key constructed with the child key parameters,
241
+ or None if i index would result in an invalid key.
242
+ """
243
+ # Index as bytes, BE
244
+ i_str = struct.pack(">L", i)
245
+
246
+ # Data to HMAC
247
+ if i & BIP32_HARDEN:
248
+ if self.k is None:
249
+ raise Exception("No private key available for hardened derivation")
250
+ data = b"\0" + self.k.to_string() + i_str
251
+ else:
252
+ data = self.PublicKey() + i_str
253
+ # Get HMAC of data
254
+ (Il, Ir) = self.hmac(data)
255
+
256
+ # Construct new key material from Il and current private key
257
+ Il_int = int.from_bytes(Il, "big")
258
+ if Il_int > CURVE_ORDER:
259
+ return None
260
+ if self.k is None:
261
+ return None
262
+ pvt_int = int.from_bytes(self.k.to_string(), "big")
263
+ k_int = (Il_int + pvt_int) % CURVE_ORDER
264
+ if k_int == 0:
265
+ return None
266
+ secret = k_int.to_bytes(32, "big")
267
+
268
+ # Construct and return a new BIP32Key
269
+ return BIP32Key(
270
+ secret=secret,
271
+ chain=Ir,
272
+ depth=self.depth + 1,
273
+ index=i,
274
+ fpr=self.Fingerprint(),
275
+ public=False,
276
+ testnet=self.testnet,
277
+ )
278
+
279
+ def CKDpub(self, i: int) -> Optional["BIP32Key"]:
280
+ """
281
+ Create a publicly derived child key of index 'i'.
282
+
283
+ If the most significant bit of 'i' is set, this is
284
+ an error.
285
+
286
+ Returns a BIP32Key constructed with the child key parameters,
287
+ or None if index would result in invalid key.
288
+ """
289
+
290
+ if i & BIP32_HARDEN:
291
+ raise Exception("Cannot create a hardened child key using public child derivation")
292
+
293
+ # Data to HMAC. Same as CKDpriv() for public child key.
294
+ data = self.PublicKey() + struct.pack(">L", i)
295
+
296
+ # Get HMAC of data
297
+ (Il, Ir) = self.hmac(data)
298
+
299
+ # Construct curve point Il*G+K
300
+ Il_int = int.from_bytes(Il, "big")
301
+ if Il_int >= CURVE_ORDER:
302
+ return None
303
+ if self.K is None:
304
+ return None
305
+ # Get the curve point from VerifyingKey
306
+ try:
307
+ # Try newer ecdsa version approach
308
+ point = self.K.point # type: ignore[attr-defined]
309
+ except AttributeError:
310
+ # Fallback for older versions
311
+ point = self.K.pubkey.point # type: ignore[attr-defined]
312
+ curve_point = Il_int * CURVE_GEN + point
313
+ if curve_point == INFINITY:
314
+ return None
315
+
316
+ # Retrieve public key based on curve point
317
+ K_i = VerifyKey(curve_point, curve=SECP256k1)
318
+
319
+ # Construct and return a new BIP32Key
320
+ return BIP32Key(
321
+ secret=K_i,
322
+ chain=Ir,
323
+ depth=self.depth + 1,
324
+ index=i,
325
+ fpr=self.Fingerprint(),
326
+ public=True,
327
+ testnet=self.testnet,
328
+ )
329
+
330
+ # Public methods
331
+ #
332
+ def ChildKey(self, i: int) -> Optional["BIP32Key"]:
333
+ """
334
+ Create and return a child key of this one at index 'i'.
335
+
336
+ The index 'i' should be summed with BIP32_HARDEN to indicate
337
+ to use the private derivation algorithm.
338
+ """
339
+ if self.public is False:
340
+ return self.CKDpriv(i)
341
+ else:
342
+ return self.CKDpub(i)
343
+
344
+ def SetPublic(self) -> None:
345
+ """Convert a private BIP32Key into a public one"""
346
+ self.k = None
347
+ self.public = True
348
+
349
+ def PrivateKey(self) -> bytes:
350
+ """Return private key as string"""
351
+ if self.public:
352
+ raise Exception("Publicly derived deterministic keys have no private half")
353
+ if self.k is None:
354
+ raise Exception("No private key available")
355
+ return self.k.to_string()
356
+
357
+ def PublicKey(self) -> bytes:
358
+ """Return compressed public key encoding"""
359
+ if self.K is None:
360
+ raise Exception("No public key available")
361
+ # Get the curve point from VerifyingKey
362
+ try:
363
+ # Try newer ecdsa version approach
364
+ point = self.K.point # type: ignore[attr-defined]
365
+ except AttributeError:
366
+ # Fallback for older versions
367
+ point = self.K.pubkey.point # type: ignore[attr-defined]
368
+ padx = int(point.x()).to_bytes(32, "big")
369
+ if int(point.y()) & 1:
370
+ ck = b"\3" + padx
371
+ else:
372
+ ck = b"\2" + padx
373
+ return ck
374
+
375
+ def ChainCode(self) -> bytes:
376
+ """Return chain code as string"""
377
+ return self.C
378
+
379
+ def Identifier(self) -> bytes:
380
+ """Return key identifier as string"""
381
+ cK = self.PublicKey()
382
+ return hashlib.new("ripemd160", sha256(cK).digest()).digest()
383
+
384
+ def Fingerprint(self) -> bytes:
385
+ """Return key fingerprint as string"""
386
+ return self.Identifier()[:4]
387
+
388
+ def Address(self) -> str:
389
+ """Return compressed public key address"""
390
+ addressversion = b"\x00" if not self.testnet else b"\x6f"
391
+ # vh160 = addressversion + self.Identifier()
392
+ # return check_encode(vh160)
393
+ payload = hexlify(self.Identifier()).decode("ascii")
394
+ return base58CheckEncode(int.from_bytes(addressversion, "big"), payload)
395
+
396
+ def P2WPKHoP2SHAddress(self) -> str:
397
+ """Return P2WPKH over P2SH segwit address"""
398
+ pk_bytes = self.PublicKey()
399
+ assert len(pk_bytes) == 33 and (
400
+ pk_bytes.startswith(b"\x02") or pk_bytes.startswith(b"\x03")
401
+ ), (
402
+ "Only compressed public keys are compatible with p2sh-p2wpkh addresses. "
403
+ "See https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki."
404
+ )
405
+ pk_hash = hashlib.new("ripemd160", sha256(pk_bytes).digest()).digest()
406
+ push_20 = bytes.fromhex("0014")
407
+ script_sig = push_20 + pk_hash
408
+ address_bytes = hashlib.new("ripemd160", sha256(script_sig).digest()).digest()
409
+ prefix = b"\xc4" if self.testnet else b"\x05"
410
+ # return check_encode(prefix + address_bytes)
411
+ payload = hexlify(address_bytes).decode("ascii")
412
+ return base58CheckEncode(int.from_bytes(prefix, "big"), payload)
413
+
414
+ def WalletImportFormat(self) -> str:
415
+ """Returns private key encoded for wallet import"""
416
+ if self.public:
417
+ raise Exception("Publicly derived deterministic keys have no private half")
418
+ if self.k is None:
419
+ raise Exception("No private key available")
420
+ addressversion = b"\x80" if not self.testnet else b"\xef"
421
+ raw = self.k.to_string() + b"\x01" # Always compressed
422
+ # return check_encode(addressversion + raw)
423
+ payload = hexlify(raw).decode("ascii")
424
+ return base58CheckEncode(int.from_bytes(addressversion, "big"), payload)
425
+
426
+ def ExtendedKey(self, private: bool = True, encoded: bool = True) -> Union[str, bytes]:
427
+ """Return extended private or public key as string, optionally base58 encoded"""
428
+ if self.public is True and private is True:
429
+ raise Exception(
430
+ "Cannot export an extended private key from a public-only deterministic key"
431
+ )
432
+ if not self.testnet:
433
+ version = EX_MAIN_PRIVATE[0] if private else EX_MAIN_PUBLIC[0]
434
+ else:
435
+ version = EX_TEST_PRIVATE[0] if private else EX_TEST_PUBLIC[0]
436
+ depth = bytes(bytearray([self.depth]))
437
+ fpr = self.parent_fpr
438
+ child = struct.pack(">L", self.index)
439
+ chain = self.C
440
+ if self.public is True or private is False:
441
+ data = self.PublicKey()
442
+ else:
443
+ data = b"\x00" + self.PrivateKey()
444
+ raw = version + depth + fpr + child + chain + data
445
+ if not encoded:
446
+ return raw
447
+ else:
448
+ # return check_encode(raw)
449
+ payload = hexlify(chain + data).decode("ascii")
450
+ version_int = int.from_bytes(version + depth + fpr + child, "big")
451
+ return base58CheckEncode(version_int, payload)
452
+
453
+ # Debugging methods
454
+ #
455
+ def dump(self):
456
+ """Dump key fields mimicking the BIP0032 test vector format"""
457
+ print(" * Identifier")
458
+ print(" * (hex): ", self.Identifier().hex())
459
+ print(" * (fpr): ", self.Fingerprint().hex())
460
+ print(" * (main addr):", self.Address())
461
+ if self.public is False:
462
+ print(" * Secret key")
463
+ print(" * (hex): ", self.PrivateKey().hex())
464
+ print(" * (wif): ", self.WalletImportFormat())
465
+ print(" * Public key")
466
+ print(" * (hex): ", self.PublicKey().hex())
467
+ print(" * Chain code")
468
+ print(" * (hex): ", self.C.hex())
469
+ print(" * Serialized")
470
+ pub_hex = self.ExtendedKey(private=False, encoded=False)
471
+ prv_hex = self.ExtendedKey(private=True, encoded=False) if not self.public else None
472
+ print(" * (pub hex): ", pub_hex.hex() if isinstance(pub_hex, bytes) else str(pub_hex))
473
+ print(" * (pub b58): ", self.ExtendedKey(private=False, encoded=True))
474
+ if self.public is False:
475
+ print(
476
+ " * (prv hex): ", prv_hex.hex() if isinstance(prv_hex, bytes) else str(prv_hex)
477
+ )
478
+ print(" * (prv b58): ", self.ExtendedKey(private=True, encoded=True))
479
+
480
+
481
+ def test():
482
+ from binascii import a2b_hex
483
+
484
+ # BIP0032 Test vector 1
485
+ entropy = a2b_hex("000102030405060708090A0B0C0D0E0F")
486
+ m = BIP32Key.fromEntropy(entropy)
487
+ print("Test vector 1:")
488
+ print("Master (hex):", entropy.hex())
489
+ print("* [Chain m]")
490
+ m.dump()
491
+
492
+ print("* [Chain m/0h]")
493
+ m_child = m.ChildKey(0 + BIP32_HARDEN)
494
+ if m_child:
495
+ m_child.dump()
496
+ else:
497
+ print("Failed to create child key")
498
+
499
+ print("* [Chain m/0h/1]")
500
+ m_child2 = m_child.ChildKey(1) if m_child else None
501
+ if m_child2:
502
+ m_child2.dump()
503
+ else:
504
+ print("Failed to create grandchild key")
505
+
506
+ print("* [Chain m/0h/1/2h]")
507
+ m_child3 = m_child2.ChildKey(2 + BIP32_HARDEN) if m_child2 else None
508
+ if m_child3:
509
+ m_child3.dump()
510
+ else:
511
+ print("Failed to create great-grandchild key")
512
+
513
+ print("* [Chain m/0h/1/2h/2]")
514
+ m_child4 = m_child3.ChildKey(2) if m_child3 else None
515
+ if m_child4:
516
+ m_child4.dump()
517
+ else:
518
+ print("Failed to create 4th level key")
519
+
520
+ print("* [Chain m/0h/1/2h/2/1000000000]")
521
+ m_child5 = m_child4.ChildKey(1000000000) if m_child4 else None
522
+ if m_child5:
523
+ m_child5.dump()
524
+ else:
525
+ print("Failed to create 5th level key")
526
+
527
+ # BIP0032 Test vector 2
528
+ entropy = a2b_hex(
529
+ "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a878481"
530
+ "7e7b7875726f6c696663605d5a5754514e4b484542"
531
+ )
532
+ m = BIP32Key.fromEntropy(entropy)
533
+ print("Test vector 2:")
534
+ print("Master (hex):", entropy.hex())
535
+ print("* [Chain m]")
536
+ m.dump()
537
+
538
+ print("* [Chain m/0]")
539
+ m2_child = m.ChildKey(0)
540
+ if m2_child:
541
+ m2_child.dump()
542
+ else:
543
+ print("Failed to create child key")
544
+
545
+ print("* [Chain m/0/2147483647h]")
546
+ m2_child2 = m2_child.ChildKey(2147483647 + BIP32_HARDEN) if m2_child else None
547
+ if m2_child2:
548
+ m2_child2.dump()
549
+ else:
550
+ print("Failed to create grandchild key")
551
+
552
+ print("* [Chain m/0/2147483647h/1]")
553
+ m2_child3 = m2_child2.ChildKey(1) if m2_child2 else None
554
+ if m2_child3:
555
+ m2_child3.dump()
556
+ else:
557
+ print("Failed to create great-grandchild key")
558
+
559
+ print("* [Chain m/0/2147483647h/1/2147483646h]")
560
+ m2_child4 = m2_child3.ChildKey(2147483646 + BIP32_HARDEN) if m2_child3 else None
561
+ if m2_child4:
562
+ m2_child4.dump()
563
+ else:
564
+ print("Failed to create 4th level key")
565
+
566
+ print("* [Chain m/0/2147483647h/1/2147483646h/2]")
567
+ m2_child5 = m2_child4.ChildKey(2) if m2_child4 else None
568
+ if m2_child5:
569
+ m2_child5.dump()
570
+ else:
571
+ print("Failed to create 5th level key")
572
+
573
+
574
+ if __name__ == "__main__":
575
+ test()
@@ -0,0 +1,110 @@
1
+ import hashlib
2
+ import logging
3
+ import sys
4
+ from binascii import hexlify, unhexlify
5
+ from typing import Any
6
+
7
+ import scrypt
8
+ from Cryptodome.Cipher import AES
9
+
10
+ from .account import PrivateKey
11
+ from .base58 import Base58, base58decode
12
+
13
+ log = logging.getLogger(__name__)
14
+
15
+
16
+ class SaltException(Exception):
17
+ pass
18
+
19
+
20
+ def _encrypt_xor(a: Any, b: bytes, aes: Any) -> bytes:
21
+ """Returns encrypt(a ^ b)."""
22
+ a = unhexlify("%0.32x" % (int((a), 16) ^ int(hexlify(b), 16)))
23
+ return aes.encrypt(a)
24
+
25
+
26
+ def encrypt(privkey: Any, passphrase: str) -> Base58:
27
+ """BIP0038 non-ec-multiply encryption. Returns BIP0038 encrypted privkey.
28
+
29
+ :param privkey: Private key
30
+ :type privkey: Base58
31
+ :param str passphrase: UTF-8 encoded passphrase for encryption
32
+ :return: BIP0038 non-ec-multiply encrypted wif key
33
+ :rtype: Base58
34
+
35
+ """
36
+ if isinstance(privkey, str):
37
+ privkey = PrivateKey(privkey)
38
+ else:
39
+ privkey = PrivateKey(repr(privkey))
40
+
41
+ privkeyhex = repr(privkey) # hex
42
+ addr = format(privkey.bitcoin.address, "BTC")
43
+ a = bytes(addr, "ascii")
44
+ salt = hashlib.sha256(hashlib.sha256(a).digest()).digest()[0:4]
45
+ if sys.version < "3":
46
+ if isinstance(passphrase, str):
47
+ passphrase_bytes = passphrase.encode("utf-8")
48
+ else:
49
+ passphrase_bytes = passphrase
50
+ else:
51
+ passphrase_bytes = passphrase.encode("utf-8") if isinstance(passphrase, str) else passphrase
52
+
53
+ key = scrypt.hash(passphrase_bytes, salt, 16384, 8, 8)
54
+ (derived_half1, derived_half2) = (key[:32], key[32:])
55
+ aes = AES.new(derived_half2, AES.MODE_ECB)
56
+ encrypted_half1 = _encrypt_xor(privkeyhex[:32], derived_half1[:16], aes)
57
+ encrypted_half2 = _encrypt_xor(privkeyhex[32:], derived_half1[16:], aes)
58
+ " flag byte is forced 0xc0 because Graphene only uses compressed keys "
59
+ payload = b"\x01" + b"\x42" + b"\xc0" + salt + encrypted_half1 + encrypted_half2
60
+ " Checksum "
61
+ checksum = hashlib.sha256(hashlib.sha256(payload).digest()).digest()[:4]
62
+ privatkey = hexlify(payload + checksum).decode("ascii")
63
+ return Base58(privatkey)
64
+
65
+
66
+ def decrypt(encrypted_privkey: Any, passphrase: str) -> Base58:
67
+ """BIP0038 non-ec-multiply decryption. Returns WIF privkey.
68
+
69
+ :param Base58 encrypted_privkey: Private key
70
+ :param str passphrase: UTF-8 encoded passphrase for decryption
71
+ :return: BIP0038 non-ec-multiply decrypted key
72
+ :rtype: Base58
73
+ :raises SaltException: if checksum verification failed (e.g. wrong password)
74
+
75
+ """
76
+
77
+ d = unhexlify(base58decode(encrypted_privkey))
78
+ d = d[2:] # remove trailing 0x01 and 0x42
79
+ flagbyte = d[0:1] # get flag byte
80
+ d = d[1:] # get payload
81
+ if not flagbyte == b"\xc0":
82
+ raise AssertionError("Flagbyte has to be 0xc0")
83
+ salt = d[0:4]
84
+ d = d[4:-4]
85
+ if sys.version < "3":
86
+ if isinstance(passphrase, str):
87
+ passphrase_bytes = passphrase.encode("utf-8")
88
+ else:
89
+ passphrase_bytes = passphrase
90
+ else:
91
+ passphrase_bytes = passphrase.encode("utf-8") if isinstance(passphrase, str) else passphrase
92
+ key = scrypt.hash(passphrase_bytes, salt, 16384, 8, 8)
93
+ derivedhalf1 = key[0:32]
94
+ derivedhalf2 = key[32:64]
95
+ encryptedhalf1 = d[0:16]
96
+ encryptedhalf2 = d[16:32]
97
+ aes = AES.new(derivedhalf2, AES.MODE_ECB)
98
+ decryptedhalf2 = aes.decrypt(encryptedhalf2)
99
+ decryptedhalf1 = aes.decrypt(encryptedhalf1)
100
+ privraw = decryptedhalf1 + decryptedhalf2
101
+ privraw = "%064x" % (int(hexlify(privraw), 16) ^ int(hexlify(derivedhalf1), 16))
102
+ wif = Base58(privraw)
103
+ """ Verify Salt """
104
+ privkey = PrivateKey(format(wif, "wif"))
105
+ addr = format(privkey.bitcoin.address, "BTC")
106
+ a = bytes(addr, "ascii")
107
+ saltverify = hashlib.sha256(hashlib.sha256(a).digest()).digest()[0:4]
108
+ if saltverify != salt:
109
+ raise SaltException("checksum verification failed! Password may be incorrect.")
110
+ return wif
@@ -0,0 +1,15 @@
1
+ from typing import Any, Dict
2
+
3
+ default_prefix: str = "STM"
4
+ known_chains: Dict[str, Dict[str, Any]] = {
5
+ "HIVE": {
6
+ "chain_id": "beeab0de00000000000000000000000000000000000000000000000000000000",
7
+ "min_version": "0.24.0",
8
+ "prefix": "STM",
9
+ "chain_assets": [
10
+ {"asset": "@@000000013", "symbol": "HBD", "precision": 3, "id": 0},
11
+ {"asset": "@@000000021", "symbol": "HIVE", "precision": 3, "id": 1},
12
+ {"asset": "@@000000037", "symbol": "VESTS", "precision": 6, "id": 2},
13
+ ],
14
+ },
15
+ }