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.
- hive_nectar-0.2.9.dist-info/METADATA +194 -0
- hive_nectar-0.2.9.dist-info/RECORD +87 -0
- hive_nectar-0.2.9.dist-info/WHEEL +4 -0
- hive_nectar-0.2.9.dist-info/entry_points.txt +2 -0
- hive_nectar-0.2.9.dist-info/licenses/LICENSE.txt +23 -0
- nectar/__init__.py +37 -0
- nectar/account.py +5076 -0
- nectar/amount.py +553 -0
- nectar/asciichart.py +303 -0
- nectar/asset.py +122 -0
- nectar/block.py +574 -0
- nectar/blockchain.py +1242 -0
- nectar/blockchaininstance.py +2590 -0
- nectar/blockchainobject.py +263 -0
- nectar/cli.py +5937 -0
- nectar/comment.py +1552 -0
- nectar/community.py +854 -0
- nectar/constants.py +95 -0
- nectar/discussions.py +1437 -0
- nectar/exceptions.py +152 -0
- nectar/haf.py +381 -0
- nectar/hive.py +630 -0
- nectar/imageuploader.py +114 -0
- nectar/instance.py +113 -0
- nectar/market.py +876 -0
- nectar/memo.py +542 -0
- nectar/message.py +379 -0
- nectar/nodelist.py +309 -0
- nectar/price.py +603 -0
- nectar/profile.py +74 -0
- nectar/py.typed +0 -0
- nectar/rc.py +333 -0
- nectar/snapshot.py +1024 -0
- nectar/storage.py +62 -0
- nectar/transactionbuilder.py +659 -0
- nectar/utils.py +630 -0
- nectar/version.py +3 -0
- nectar/vote.py +722 -0
- nectar/wallet.py +472 -0
- nectar/witness.py +728 -0
- nectarapi/__init__.py +12 -0
- nectarapi/exceptions.py +126 -0
- nectarapi/graphenerpc.py +596 -0
- nectarapi/node.py +194 -0
- nectarapi/noderpc.py +79 -0
- nectarapi/openapi.py +107 -0
- nectarapi/py.typed +0 -0
- nectarapi/rpcutils.py +98 -0
- nectarapi/version.py +3 -0
- nectarbase/__init__.py +15 -0
- nectarbase/ledgertransactions.py +106 -0
- nectarbase/memo.py +242 -0
- nectarbase/objects.py +521 -0
- nectarbase/objecttypes.py +21 -0
- nectarbase/operationids.py +102 -0
- nectarbase/operations.py +1357 -0
- nectarbase/py.typed +0 -0
- nectarbase/signedtransactions.py +89 -0
- nectarbase/transactions.py +11 -0
- nectarbase/version.py +3 -0
- nectargraphenebase/__init__.py +27 -0
- nectargraphenebase/account.py +1121 -0
- nectargraphenebase/aes.py +49 -0
- nectargraphenebase/base58.py +197 -0
- nectargraphenebase/bip32.py +575 -0
- nectargraphenebase/bip38.py +110 -0
- nectargraphenebase/chains.py +15 -0
- nectargraphenebase/dictionary.py +2 -0
- nectargraphenebase/ecdsasig.py +309 -0
- nectargraphenebase/objects.py +130 -0
- nectargraphenebase/objecttypes.py +8 -0
- nectargraphenebase/operationids.py +5 -0
- nectargraphenebase/operations.py +25 -0
- nectargraphenebase/prefix.py +13 -0
- nectargraphenebase/py.typed +0 -0
- nectargraphenebase/signedtransactions.py +221 -0
- nectargraphenebase/types.py +557 -0
- nectargraphenebase/unsignedtransactions.py +288 -0
- nectargraphenebase/version.py +3 -0
- nectarstorage/__init__.py +57 -0
- nectarstorage/base.py +317 -0
- nectarstorage/exceptions.py +15 -0
- nectarstorage/interfaces.py +244 -0
- nectarstorage/masterpassword.py +237 -0
- nectarstorage/py.typed +0 -0
- nectarstorage/ram.py +27 -0
- 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
|
+
}
|