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.
- hive_nectar-0.0.2.dist-info/METADATA +182 -0
- hive_nectar-0.0.2.dist-info/RECORD +86 -0
- hive_nectar-0.0.2.dist-info/WHEEL +4 -0
- hive_nectar-0.0.2.dist-info/entry_points.txt +2 -0
- hive_nectar-0.0.2.dist-info/licenses/LICENSE.txt +23 -0
- nectar/__init__.py +32 -0
- nectar/account.py +4371 -0
- nectar/amount.py +475 -0
- nectar/asciichart.py +270 -0
- nectar/asset.py +82 -0
- nectar/block.py +446 -0
- nectar/blockchain.py +1178 -0
- nectar/blockchaininstance.py +2284 -0
- nectar/blockchainobject.py +221 -0
- nectar/blurt.py +563 -0
- nectar/cli.py +6285 -0
- nectar/comment.py +1217 -0
- nectar/community.py +513 -0
- nectar/constants.py +111 -0
- nectar/conveyor.py +309 -0
- nectar/discussions.py +1709 -0
- nectar/exceptions.py +149 -0
- nectar/hive.py +546 -0
- nectar/hivesigner.py +420 -0
- nectar/imageuploader.py +72 -0
- nectar/instance.py +129 -0
- nectar/market.py +1013 -0
- nectar/memo.py +449 -0
- nectar/message.py +357 -0
- nectar/nodelist.py +444 -0
- nectar/price.py +557 -0
- nectar/profile.py +65 -0
- nectar/rc.py +308 -0
- nectar/snapshot.py +726 -0
- nectar/steem.py +582 -0
- nectar/storage.py +53 -0
- nectar/transactionbuilder.py +622 -0
- nectar/utils.py +545 -0
- nectar/version.py +2 -0
- nectar/vote.py +557 -0
- nectar/wallet.py +472 -0
- nectar/witness.py +617 -0
- nectarapi/__init__.py +11 -0
- nectarapi/exceptions.py +123 -0
- nectarapi/graphenerpc.py +589 -0
- nectarapi/node.py +178 -0
- nectarapi/noderpc.py +229 -0
- nectarapi/rpcutils.py +97 -0
- nectarapi/version.py +2 -0
- nectarbase/__init__.py +14 -0
- nectarbase/ledgertransactions.py +75 -0
- nectarbase/memo.py +243 -0
- nectarbase/objects.py +429 -0
- nectarbase/objecttypes.py +22 -0
- nectarbase/operationids.py +102 -0
- nectarbase/operations.py +1297 -0
- nectarbase/signedtransactions.py +48 -0
- nectarbase/transactions.py +11 -0
- nectarbase/version.py +2 -0
- nectargrapheneapi/__init__.py +6 -0
- nectargraphenebase/__init__.py +27 -0
- nectargraphenebase/account.py +846 -0
- nectargraphenebase/aes.py +52 -0
- nectargraphenebase/base58.py +192 -0
- nectargraphenebase/bip32.py +494 -0
- nectargraphenebase/bip38.py +134 -0
- nectargraphenebase/chains.py +149 -0
- nectargraphenebase/dictionary.py +3 -0
- nectargraphenebase/ecdsasig.py +326 -0
- nectargraphenebase/objects.py +123 -0
- nectargraphenebase/objecttypes.py +6 -0
- nectargraphenebase/operationids.py +3 -0
- nectargraphenebase/operations.py +23 -0
- nectargraphenebase/prefix.py +11 -0
- nectargraphenebase/py23.py +38 -0
- nectargraphenebase/signedtransactions.py +201 -0
- nectargraphenebase/types.py +419 -0
- nectargraphenebase/unsignedtransactions.py +283 -0
- nectargraphenebase/version.py +2 -0
- nectarstorage/__init__.py +38 -0
- nectarstorage/base.py +306 -0
- nectarstorage/exceptions.py +16 -0
- nectarstorage/interfaces.py +237 -0
- nectarstorage/masterpassword.py +239 -0
- nectarstorage/ram.py +30 -0
- nectarstorage/sqlite.py +334 -0
|
@@ -0,0 +1,494 @@
|
|
|
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
|
+
|
|
15
|
+
import ecdsa
|
|
16
|
+
from ecdsa.curves import SECP256k1
|
|
17
|
+
from ecdsa.numbertheory import square_root_mod_prime as sqrt_mod
|
|
18
|
+
|
|
19
|
+
from nectargraphenebase.base58 import base58CheckDecode, base58CheckEncode
|
|
20
|
+
from nectargraphenebase.py23 import py23_bytes
|
|
21
|
+
|
|
22
|
+
VerifyKey = ecdsa.VerifyingKey.from_public_point
|
|
23
|
+
SigningKey = ecdsa.SigningKey.from_string
|
|
24
|
+
PointObject = ecdsa.ellipticcurve.Point # Point class
|
|
25
|
+
CURVE_GEN = ecdsa.ecdsa.generator_secp256k1 # Point class
|
|
26
|
+
CURVE_ORDER = CURVE_GEN.order() # int
|
|
27
|
+
FIELD_ORDER = SECP256k1.curve.p() # int
|
|
28
|
+
INFINITY = ecdsa.ellipticcurve.INFINITY # Point
|
|
29
|
+
|
|
30
|
+
MIN_ENTROPY_LEN = 128 # bits
|
|
31
|
+
BIP32_HARDEN = 0x80000000 # choose from hardened set of child keys
|
|
32
|
+
EX_MAIN_PRIVATE = [
|
|
33
|
+
codecs.decode("0488ade4", "hex")
|
|
34
|
+
] # Version strings for mainnet extended private keys
|
|
35
|
+
EX_MAIN_PUBLIC = [
|
|
36
|
+
codecs.decode("0488b21e", "hex"),
|
|
37
|
+
codecs.decode("049d7cb2", "hex"),
|
|
38
|
+
] # Version strings for mainnet extended public keys
|
|
39
|
+
EX_TEST_PRIVATE = [
|
|
40
|
+
codecs.decode("04358394", "hex")
|
|
41
|
+
] # Version strings for testnet extended private keys
|
|
42
|
+
EX_TEST_PUBLIC = [
|
|
43
|
+
codecs.decode("043587CF", "hex")
|
|
44
|
+
] # Version strings for testnet extended public keys
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def int_to_hex(x):
|
|
48
|
+
return py23_bytes(hex(x)[2:], encoding="utf-8")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def parse_path(nstr, as_bytes=False):
|
|
52
|
+
""""""
|
|
53
|
+
r = list()
|
|
54
|
+
for s in nstr.split("/"):
|
|
55
|
+
if s == "m":
|
|
56
|
+
continue
|
|
57
|
+
elif s.endswith("'") or s.endswith("h"):
|
|
58
|
+
r.append(int(s[:-1]) + BIP32_HARDEN)
|
|
59
|
+
else:
|
|
60
|
+
r.append(int(s))
|
|
61
|
+
if not as_bytes:
|
|
62
|
+
return r
|
|
63
|
+
path = None
|
|
64
|
+
for p in r:
|
|
65
|
+
if path is None:
|
|
66
|
+
path = int_to_hex(p)
|
|
67
|
+
else:
|
|
68
|
+
path += int_to_hex(p)
|
|
69
|
+
return path
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class BIP32Key(object):
|
|
73
|
+
# Static initializers to create from entropy or external formats
|
|
74
|
+
#
|
|
75
|
+
@staticmethod
|
|
76
|
+
def fromEntropy(entropy, public=False, testnet=False):
|
|
77
|
+
"""Create a BIP32Key using supplied entropy >= MIN_ENTROPY_LEN"""
|
|
78
|
+
if entropy is None:
|
|
79
|
+
entropy = os.urandom(MIN_ENTROPY_LEN // 8) # Python doesn't have os.random()
|
|
80
|
+
if not len(entropy) >= MIN_ENTROPY_LEN // 8:
|
|
81
|
+
raise ValueError(
|
|
82
|
+
"Initial entropy %i must be at least %i bits" % (len(entropy), MIN_ENTROPY_LEN)
|
|
83
|
+
)
|
|
84
|
+
i64 = hmac.new(b"Bitcoin seed", entropy, hashlib.sha512).digest()
|
|
85
|
+
il, ir = i64[:32], i64[32:]
|
|
86
|
+
# FIXME test Il for 0 or less than SECP256k1 prime field order
|
|
87
|
+
key = BIP32Key(
|
|
88
|
+
secret=il, chain=ir, depth=0, index=0, fpr=b"\0\0\0\0", public=False, testnet=testnet
|
|
89
|
+
)
|
|
90
|
+
if public:
|
|
91
|
+
key.SetPublic()
|
|
92
|
+
return key
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def fromExtendedKey(xkey, public=False):
|
|
96
|
+
"""
|
|
97
|
+
Create a BIP32Key by importing from extended private or public key string
|
|
98
|
+
|
|
99
|
+
If public is True, return a public-only key regardless of input type.
|
|
100
|
+
"""
|
|
101
|
+
# Sanity checks
|
|
102
|
+
# raw = check_decode(xkey)
|
|
103
|
+
raw = unhexlify(base58CheckDecode(xkey, skip_first_bytes=False))
|
|
104
|
+
|
|
105
|
+
if len(raw) != 78:
|
|
106
|
+
raise ValueError("extended key format wrong length")
|
|
107
|
+
|
|
108
|
+
# Verify address version/type
|
|
109
|
+
version = raw[:4]
|
|
110
|
+
if version in EX_MAIN_PRIVATE:
|
|
111
|
+
is_testnet = False
|
|
112
|
+
is_pubkey = False
|
|
113
|
+
elif version in EX_TEST_PRIVATE:
|
|
114
|
+
is_testnet = True
|
|
115
|
+
is_pubkey = False
|
|
116
|
+
elif version in EX_MAIN_PUBLIC:
|
|
117
|
+
is_testnet = False
|
|
118
|
+
is_pubkey = True
|
|
119
|
+
elif version in EX_TEST_PUBLIC:
|
|
120
|
+
is_testnet = True
|
|
121
|
+
is_pubkey = True
|
|
122
|
+
else:
|
|
123
|
+
raise ValueError("unknown extended key version")
|
|
124
|
+
|
|
125
|
+
# Extract remaining fields
|
|
126
|
+
# Python 2.x compatibility
|
|
127
|
+
if type(raw[4]) == int:
|
|
128
|
+
depth = raw[4]
|
|
129
|
+
else:
|
|
130
|
+
depth = ord(raw[4])
|
|
131
|
+
fpr = raw[5:9]
|
|
132
|
+
child = struct.unpack(">L", raw[9:13])[0]
|
|
133
|
+
chain = raw[13:45]
|
|
134
|
+
secret = raw[45:78]
|
|
135
|
+
|
|
136
|
+
# Extract private key or public key point
|
|
137
|
+
if not is_pubkey:
|
|
138
|
+
secret = secret[1:]
|
|
139
|
+
else:
|
|
140
|
+
# Recover public curve point from compressed key
|
|
141
|
+
# Python3 FIX
|
|
142
|
+
lsb = secret[0] & 1 if type(secret[0]) == int else ord(secret[0]) & 1
|
|
143
|
+
x = int.from_bytes(secret[1:], "big")
|
|
144
|
+
ys = (x**3 + 7) % FIELD_ORDER # y^2 = x^3 + 7 mod p
|
|
145
|
+
y = sqrt_mod(ys, FIELD_ORDER)
|
|
146
|
+
if y & 1 != lsb:
|
|
147
|
+
y = FIELD_ORDER - y
|
|
148
|
+
point = PointObject(SECP256k1.curve, x, y)
|
|
149
|
+
secret = VerifyKey(point, curve=SECP256k1)
|
|
150
|
+
|
|
151
|
+
key = BIP32Key(
|
|
152
|
+
secret=secret,
|
|
153
|
+
chain=chain,
|
|
154
|
+
depth=depth,
|
|
155
|
+
index=child,
|
|
156
|
+
fpr=fpr,
|
|
157
|
+
public=is_pubkey,
|
|
158
|
+
testnet=is_testnet,
|
|
159
|
+
)
|
|
160
|
+
if not is_pubkey and public:
|
|
161
|
+
key.SetPublic()
|
|
162
|
+
return key
|
|
163
|
+
|
|
164
|
+
# Normal class initializer
|
|
165
|
+
def __init__(self, secret, chain, depth, index, fpr, public=False, testnet=False):
|
|
166
|
+
"""
|
|
167
|
+
Create a public or private BIP32Key using key material and chain code.
|
|
168
|
+
|
|
169
|
+
secret This is the source material to generate the keypair, either a
|
|
170
|
+
32-byte string representation of a private key, or the ECDSA
|
|
171
|
+
library object representing a public key.
|
|
172
|
+
|
|
173
|
+
chain This is a 32-byte string representation of the chain code
|
|
174
|
+
|
|
175
|
+
depth Child depth; parent increments its own by one when assigning this
|
|
176
|
+
|
|
177
|
+
index Child index
|
|
178
|
+
|
|
179
|
+
fpr Parent fingerprint
|
|
180
|
+
|
|
181
|
+
public If true, this keypair will only contain a public key and can only create
|
|
182
|
+
a public key chain.
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
self.public = public
|
|
186
|
+
if public is False:
|
|
187
|
+
self.k = SigningKey(secret, curve=SECP256k1)
|
|
188
|
+
self.K = self.k.get_verifying_key()
|
|
189
|
+
else:
|
|
190
|
+
self.k = None
|
|
191
|
+
self.K = secret
|
|
192
|
+
|
|
193
|
+
self.C = chain
|
|
194
|
+
self.depth = depth
|
|
195
|
+
self.index = index
|
|
196
|
+
self.parent_fpr = fpr
|
|
197
|
+
self.testnet = testnet
|
|
198
|
+
|
|
199
|
+
# Internal methods not intended to be called externally
|
|
200
|
+
#
|
|
201
|
+
def hmac(self, data):
|
|
202
|
+
"""
|
|
203
|
+
Calculate the HMAC-SHA512 of input data using the chain code as key.
|
|
204
|
+
|
|
205
|
+
Returns a tuple of the left and right halves of the HMAC
|
|
206
|
+
"""
|
|
207
|
+
i64 = hmac.new(self.C, data, hashlib.sha512).digest()
|
|
208
|
+
return i64[:32], i64[32:]
|
|
209
|
+
|
|
210
|
+
def CKDpriv(self, i):
|
|
211
|
+
"""
|
|
212
|
+
Create a child key of index 'i'.
|
|
213
|
+
|
|
214
|
+
If the most significant bit of 'i' is set, then select from the
|
|
215
|
+
hardened key set, otherwise, select a regular child key.
|
|
216
|
+
|
|
217
|
+
Returns a BIP32Key constructed with the child key parameters,
|
|
218
|
+
or None if i index would result in an invalid key.
|
|
219
|
+
"""
|
|
220
|
+
# Index as bytes, BE
|
|
221
|
+
i_str = struct.pack(">L", i)
|
|
222
|
+
|
|
223
|
+
# Data to HMAC
|
|
224
|
+
if i & BIP32_HARDEN:
|
|
225
|
+
data = b"\0" + self.k.to_string() + i_str
|
|
226
|
+
else:
|
|
227
|
+
data = self.PublicKey() + i_str
|
|
228
|
+
# Get HMAC of data
|
|
229
|
+
(Il, Ir) = self.hmac(data)
|
|
230
|
+
|
|
231
|
+
# Construct new key material from Il and current private key
|
|
232
|
+
Il_int = int.from_bytes(Il, "big")
|
|
233
|
+
if Il_int > CURVE_ORDER:
|
|
234
|
+
return None
|
|
235
|
+
pvt_int = int.from_bytes(self.k.to_string(), "big")
|
|
236
|
+
k_int = (Il_int + pvt_int) % CURVE_ORDER
|
|
237
|
+
if k_int == 0:
|
|
238
|
+
return None
|
|
239
|
+
secret = k_int.to_bytes(32, "big")
|
|
240
|
+
|
|
241
|
+
# Construct and return a new BIP32Key
|
|
242
|
+
return BIP32Key(
|
|
243
|
+
secret=secret,
|
|
244
|
+
chain=Ir,
|
|
245
|
+
depth=self.depth + 1,
|
|
246
|
+
index=i,
|
|
247
|
+
fpr=self.Fingerprint(),
|
|
248
|
+
public=False,
|
|
249
|
+
testnet=self.testnet,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
def CKDpub(self, i):
|
|
253
|
+
"""
|
|
254
|
+
Create a publicly derived child key of index 'i'.
|
|
255
|
+
|
|
256
|
+
If the most significant bit of 'i' is set, this is
|
|
257
|
+
an error.
|
|
258
|
+
|
|
259
|
+
Returns a BIP32Key constructed with the child key parameters,
|
|
260
|
+
or None if index would result in invalid key.
|
|
261
|
+
"""
|
|
262
|
+
|
|
263
|
+
if i & BIP32_HARDEN:
|
|
264
|
+
raise Exception("Cannot create a hardened child key using public child derivation")
|
|
265
|
+
|
|
266
|
+
# Data to HMAC. Same as CKDpriv() for public child key.
|
|
267
|
+
data = self.PublicKey() + struct.pack(">L", i)
|
|
268
|
+
|
|
269
|
+
# Get HMAC of data
|
|
270
|
+
(Il, Ir) = self.hmac(data)
|
|
271
|
+
|
|
272
|
+
# Construct curve point Il*G+K
|
|
273
|
+
Il_int = int.from_bytes(Il, "big")
|
|
274
|
+
if Il_int >= CURVE_ORDER:
|
|
275
|
+
return None
|
|
276
|
+
point = Il_int * CURVE_GEN + self.K.pubkey.point
|
|
277
|
+
if point == INFINITY:
|
|
278
|
+
return None
|
|
279
|
+
|
|
280
|
+
# Retrieve public key based on curve point
|
|
281
|
+
K_i = VerifyKey(point, curve=SECP256k1)
|
|
282
|
+
|
|
283
|
+
# Construct and return a new BIP32Key
|
|
284
|
+
return BIP32Key(
|
|
285
|
+
secret=K_i,
|
|
286
|
+
chain=Ir,
|
|
287
|
+
depth=self.depth + 1,
|
|
288
|
+
index=i,
|
|
289
|
+
fpr=self.Fingerprint(),
|
|
290
|
+
public=True,
|
|
291
|
+
testnet=self.testnet,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
# Public methods
|
|
295
|
+
#
|
|
296
|
+
def ChildKey(self, i):
|
|
297
|
+
"""
|
|
298
|
+
Create and return a child key of this one at index 'i'.
|
|
299
|
+
|
|
300
|
+
The index 'i' should be summed with BIP32_HARDEN to indicate
|
|
301
|
+
to use the private derivation algorithm.
|
|
302
|
+
"""
|
|
303
|
+
if self.public is False:
|
|
304
|
+
return self.CKDpriv(i)
|
|
305
|
+
else:
|
|
306
|
+
return self.CKDpub(i)
|
|
307
|
+
|
|
308
|
+
def SetPublic(self):
|
|
309
|
+
"""Convert a private BIP32Key into a public one"""
|
|
310
|
+
self.k = None
|
|
311
|
+
self.public = True
|
|
312
|
+
|
|
313
|
+
def PrivateKey(self):
|
|
314
|
+
"""Return private key as string"""
|
|
315
|
+
if self.public:
|
|
316
|
+
raise Exception("Publicly derived deterministic keys have no private half")
|
|
317
|
+
else:
|
|
318
|
+
return self.k.to_string()
|
|
319
|
+
|
|
320
|
+
def PublicKey(self):
|
|
321
|
+
"""Return compressed public key encoding"""
|
|
322
|
+
padx = self.K.pubkey.point.x().to_bytes(32, "big")
|
|
323
|
+
if self.K.pubkey.point.y() & 1:
|
|
324
|
+
ck = b"\3" + padx
|
|
325
|
+
else:
|
|
326
|
+
ck = b"\2" + padx
|
|
327
|
+
return ck
|
|
328
|
+
|
|
329
|
+
def ChainCode(self):
|
|
330
|
+
"""Return chain code as string"""
|
|
331
|
+
return self.C
|
|
332
|
+
|
|
333
|
+
def Identifier(self):
|
|
334
|
+
"""Return key identifier as string"""
|
|
335
|
+
cK = self.PublicKey()
|
|
336
|
+
return hashlib.new("ripemd160", sha256(cK).digest()).digest()
|
|
337
|
+
|
|
338
|
+
def Fingerprint(self):
|
|
339
|
+
"""Return key fingerprint as string"""
|
|
340
|
+
return self.Identifier()[:4]
|
|
341
|
+
|
|
342
|
+
def Address(self):
|
|
343
|
+
"""Return compressed public key address"""
|
|
344
|
+
addressversion = b"\x00" if not self.testnet else b"\x6f"
|
|
345
|
+
# vh160 = addressversion + self.Identifier()
|
|
346
|
+
# return check_encode(vh160)
|
|
347
|
+
payload = hexlify(self.Identifier()).decode("ascii")
|
|
348
|
+
return base58CheckEncode(hexlify(addressversion).decode("ascii"), payload)
|
|
349
|
+
|
|
350
|
+
def P2WPKHoP2SHAddress(self):
|
|
351
|
+
"""Return P2WPKH over P2SH segwit address"""
|
|
352
|
+
pk_bytes = self.PublicKey()
|
|
353
|
+
assert len(pk_bytes) == 33 and (
|
|
354
|
+
pk_bytes.startswith(b"\x02") or pk_bytes.startswith(b"\x03")
|
|
355
|
+
), (
|
|
356
|
+
"Only compressed public keys are compatible with p2sh-p2wpkh addresses. "
|
|
357
|
+
"See https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki."
|
|
358
|
+
)
|
|
359
|
+
pk_hash = hashlib.new("ripemd160", sha256(pk_bytes).digest()).digest()
|
|
360
|
+
push_20 = bytes.fromhex("0014")
|
|
361
|
+
script_sig = push_20 + pk_hash
|
|
362
|
+
address_bytes = hashlib.new("ripemd160", sha256(script_sig).digest()).digest()
|
|
363
|
+
prefix = b"\xc4" if self.testnet else b"\x05"
|
|
364
|
+
# return check_encode(prefix + address_bytes)
|
|
365
|
+
payload = hexlify(address_bytes).decode("ascii")
|
|
366
|
+
return base58CheckEncode(hexlify(prefix).decode("ascii"), payload)
|
|
367
|
+
|
|
368
|
+
def WalletImportFormat(self):
|
|
369
|
+
"""Returns private key encoded for wallet import"""
|
|
370
|
+
if self.public:
|
|
371
|
+
raise Exception("Publicly derived deterministic keys have no private half")
|
|
372
|
+
addressversion = b"\x80" if not self.testnet else b"\xef"
|
|
373
|
+
raw = self.k.to_string() + b"\x01" # Always compressed
|
|
374
|
+
# return check_encode(addressversion + raw)
|
|
375
|
+
payload = hexlify(raw).decode("ascii")
|
|
376
|
+
return base58CheckEncode(hexlify(addressversion).decode("ascii"), payload)
|
|
377
|
+
|
|
378
|
+
def ExtendedKey(self, private=True, encoded=True):
|
|
379
|
+
"""Return extended private or public key as string, optionally base58 encoded"""
|
|
380
|
+
if self.public is True and private is True:
|
|
381
|
+
raise Exception(
|
|
382
|
+
"Cannot export an extended private key from a public-only deterministic key"
|
|
383
|
+
)
|
|
384
|
+
if not self.testnet:
|
|
385
|
+
version = EX_MAIN_PRIVATE[0] if private else EX_MAIN_PUBLIC[0]
|
|
386
|
+
else:
|
|
387
|
+
version = EX_TEST_PRIVATE[0] if private else EX_TEST_PUBLIC[0]
|
|
388
|
+
depth = bytes(bytearray([self.depth]))
|
|
389
|
+
fpr = self.parent_fpr
|
|
390
|
+
child = struct.pack(">L", self.index)
|
|
391
|
+
chain = self.C
|
|
392
|
+
if self.public is True or private is False:
|
|
393
|
+
data = self.PublicKey()
|
|
394
|
+
else:
|
|
395
|
+
data = b"\x00" + self.PrivateKey()
|
|
396
|
+
raw = version + depth + fpr + child + chain + data
|
|
397
|
+
if not encoded:
|
|
398
|
+
return raw
|
|
399
|
+
else:
|
|
400
|
+
# return check_encode(raw)
|
|
401
|
+
payload = hexlify(chain + data).decode("ascii")
|
|
402
|
+
return base58CheckEncode(
|
|
403
|
+
hexlify(version + depth + fpr + child).decode("ascii"), payload
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
# Debugging methods
|
|
407
|
+
#
|
|
408
|
+
def dump(self):
|
|
409
|
+
"""Dump key fields mimicking the BIP0032 test vector format"""
|
|
410
|
+
print(" * Identifier")
|
|
411
|
+
print(" * (hex): ", self.Identifier().hex())
|
|
412
|
+
print(" * (fpr): ", self.Fingerprint().hex())
|
|
413
|
+
print(" * (main addr):", self.Address())
|
|
414
|
+
if self.public is False:
|
|
415
|
+
print(" * Secret key")
|
|
416
|
+
print(" * (hex): ", self.PrivateKey().hex())
|
|
417
|
+
print(" * (wif): ", self.WalletImportFormat())
|
|
418
|
+
print(" * Public key")
|
|
419
|
+
print(" * (hex): ", self.PublicKey().hex())
|
|
420
|
+
print(" * Chain code")
|
|
421
|
+
print(" * (hex): ", self.C.hex())
|
|
422
|
+
print(" * Serialized")
|
|
423
|
+
print(" * (pub hex): ", self.ExtendedKey(private=False, encoded=False).hex())
|
|
424
|
+
print(" * (pub b58): ", self.ExtendedKey(private=False, encoded=True))
|
|
425
|
+
if self.public is False:
|
|
426
|
+
print(" * (prv hex): ", self.ExtendedKey(private=True, encoded=False).hex())
|
|
427
|
+
print(" * (prv b58): ", self.ExtendedKey(private=True, encoded=True))
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def test():
|
|
431
|
+
from binascii import a2b_hex
|
|
432
|
+
|
|
433
|
+
# BIP0032 Test vector 1
|
|
434
|
+
entropy = a2b_hex("000102030405060708090A0B0C0D0E0F")
|
|
435
|
+
m = BIP32Key.fromEntropy(entropy)
|
|
436
|
+
print("Test vector 1:")
|
|
437
|
+
print("Master (hex):", entropy.hex())
|
|
438
|
+
print("* [Chain m]")
|
|
439
|
+
m.dump()
|
|
440
|
+
|
|
441
|
+
print("* [Chain m/0h]")
|
|
442
|
+
m = m.ChildKey(0 + BIP32_HARDEN)
|
|
443
|
+
m.dump()
|
|
444
|
+
|
|
445
|
+
print("* [Chain m/0h/1]")
|
|
446
|
+
m = m.ChildKey(1)
|
|
447
|
+
m.dump()
|
|
448
|
+
|
|
449
|
+
print("* [Chain m/0h/1/2h]")
|
|
450
|
+
m = m.ChildKey(2 + BIP32_HARDEN)
|
|
451
|
+
m.dump()
|
|
452
|
+
|
|
453
|
+
print("* [Chain m/0h/1/2h/2]")
|
|
454
|
+
m = m.ChildKey(2)
|
|
455
|
+
m.dump()
|
|
456
|
+
|
|
457
|
+
print("* [Chain m/0h/1/2h/2/1000000000]")
|
|
458
|
+
m = m.ChildKey(1000000000)
|
|
459
|
+
m.dump()
|
|
460
|
+
|
|
461
|
+
# BIP0032 Test vector 2
|
|
462
|
+
entropy = a2b_hex(
|
|
463
|
+
"fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a878481"
|
|
464
|
+
"7e7b7875726f6c696663605d5a5754514e4b484542"
|
|
465
|
+
)
|
|
466
|
+
m = BIP32Key.fromEntropy(entropy)
|
|
467
|
+
print("Test vector 2:")
|
|
468
|
+
print("Master (hex):", entropy.hex())
|
|
469
|
+
print("* [Chain m]")
|
|
470
|
+
m.dump()
|
|
471
|
+
|
|
472
|
+
print("* [Chain m/0]")
|
|
473
|
+
m = m.ChildKey(0)
|
|
474
|
+
m.dump()
|
|
475
|
+
|
|
476
|
+
print("* [Chain m/0/2147483647h]")
|
|
477
|
+
m = m.ChildKey(2147483647 + BIP32_HARDEN)
|
|
478
|
+
m.dump()
|
|
479
|
+
|
|
480
|
+
print("* [Chain m/0/2147483647h/1]")
|
|
481
|
+
m = m.ChildKey(1)
|
|
482
|
+
m.dump()
|
|
483
|
+
|
|
484
|
+
print("* [Chain m/0/2147483647h/1/2147483646h]")
|
|
485
|
+
m = m.ChildKey(2147483646 + BIP32_HARDEN)
|
|
486
|
+
m.dump()
|
|
487
|
+
|
|
488
|
+
print("* [Chain m/0/2147483647h/1/2147483646h/2]")
|
|
489
|
+
m = m.ChildKey(2)
|
|
490
|
+
m.dump()
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
if __name__ == "__main__":
|
|
494
|
+
test()
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import hashlib
|
|
3
|
+
import logging
|
|
4
|
+
import sys
|
|
5
|
+
from binascii import hexlify, unhexlify
|
|
6
|
+
|
|
7
|
+
from .account import PrivateKey
|
|
8
|
+
from .base58 import Base58, base58decode
|
|
9
|
+
from .py23 import py23_bytes, text_type
|
|
10
|
+
|
|
11
|
+
log = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
from Cryptodome.Cipher import AES
|
|
15
|
+
except ImportError:
|
|
16
|
+
try:
|
|
17
|
+
from Crypto.Cipher import AES
|
|
18
|
+
except ImportError:
|
|
19
|
+
raise ImportError("Missing dependency: pyCryptodome")
|
|
20
|
+
|
|
21
|
+
SCRYPT_MODULE = None
|
|
22
|
+
if not SCRYPT_MODULE:
|
|
23
|
+
try:
|
|
24
|
+
import scrypt
|
|
25
|
+
|
|
26
|
+
SCRYPT_MODULE = "scrypt"
|
|
27
|
+
except ImportError:
|
|
28
|
+
try:
|
|
29
|
+
import pylibscrypt as scrypt
|
|
30
|
+
|
|
31
|
+
SCRYPT_MODULE = "pylibscrypt"
|
|
32
|
+
except ImportError:
|
|
33
|
+
raise ImportError("Missing dependency: scrypt or pylibscrypt")
|
|
34
|
+
|
|
35
|
+
log.debug("Using scrypt module: %s" % SCRYPT_MODULE)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class SaltException(Exception):
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _encrypt_xor(a, b, aes):
|
|
43
|
+
"""Returns encrypt(a ^ b)."""
|
|
44
|
+
a = unhexlify("%0.32x" % (int((a), 16) ^ int(hexlify(b), 16)))
|
|
45
|
+
return aes.encrypt(a)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def encrypt(privkey, passphrase):
|
|
49
|
+
"""BIP0038 non-ec-multiply encryption. Returns BIP0038 encrypted privkey.
|
|
50
|
+
|
|
51
|
+
:param privkey: Private key
|
|
52
|
+
:type privkey: Base58
|
|
53
|
+
:param str passphrase: UTF-8 encoded passphrase for encryption
|
|
54
|
+
:return: BIP0038 non-ec-multiply encrypted wif key
|
|
55
|
+
:rtype: Base58
|
|
56
|
+
|
|
57
|
+
"""
|
|
58
|
+
if isinstance(privkey, str):
|
|
59
|
+
privkey = PrivateKey(privkey)
|
|
60
|
+
else:
|
|
61
|
+
privkey = PrivateKey(repr(privkey))
|
|
62
|
+
|
|
63
|
+
privkeyhex = repr(privkey) # hex
|
|
64
|
+
addr = format(privkey.bitcoin.address, "BTC")
|
|
65
|
+
a = py23_bytes(addr, "ascii")
|
|
66
|
+
salt = hashlib.sha256(hashlib.sha256(a).digest()).digest()[0:4]
|
|
67
|
+
if sys.version < "3":
|
|
68
|
+
if isinstance(passphrase, text_type):
|
|
69
|
+
passphrase = passphrase.encode("utf-8")
|
|
70
|
+
|
|
71
|
+
if SCRYPT_MODULE == "scrypt":
|
|
72
|
+
key = scrypt.hash(passphrase, salt, 16384, 8, 8)
|
|
73
|
+
elif SCRYPT_MODULE == "pylibscrypt":
|
|
74
|
+
key = scrypt.scrypt(py23_bytes(passphrase, "utf-8"), salt, 16384, 8, 8)
|
|
75
|
+
else:
|
|
76
|
+
raise ValueError("No scrypt module loaded")
|
|
77
|
+
(derived_half1, derived_half2) = (key[:32], key[32:])
|
|
78
|
+
aes = AES.new(derived_half2, AES.MODE_ECB)
|
|
79
|
+
encrypted_half1 = _encrypt_xor(privkeyhex[:32], derived_half1[:16], aes)
|
|
80
|
+
encrypted_half2 = _encrypt_xor(privkeyhex[32:], derived_half1[16:], aes)
|
|
81
|
+
" flag byte is forced 0xc0 because Graphene only uses compressed keys "
|
|
82
|
+
payload = b"\x01" + b"\x42" + b"\xc0" + salt + encrypted_half1 + encrypted_half2
|
|
83
|
+
" Checksum "
|
|
84
|
+
checksum = hashlib.sha256(hashlib.sha256(payload).digest()).digest()[:4]
|
|
85
|
+
privatkey = hexlify(payload + checksum).decode("ascii")
|
|
86
|
+
return Base58(privatkey)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def decrypt(encrypted_privkey, passphrase):
|
|
90
|
+
"""BIP0038 non-ec-multiply decryption. Returns WIF privkey.
|
|
91
|
+
|
|
92
|
+
:param Base58 encrypted_privkey: Private key
|
|
93
|
+
:param str passphrase: UTF-8 encoded passphrase for decryption
|
|
94
|
+
:return: BIP0038 non-ec-multiply decrypted key
|
|
95
|
+
:rtype: Base58
|
|
96
|
+
:raises SaltException: if checksum verification failed (e.g. wrong password)
|
|
97
|
+
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
d = unhexlify(base58decode(encrypted_privkey))
|
|
101
|
+
d = d[2:] # remove trailing 0x01 and 0x42
|
|
102
|
+
flagbyte = d[0:1] # get flag byte
|
|
103
|
+
d = d[1:] # get payload
|
|
104
|
+
if not flagbyte == b"\xc0":
|
|
105
|
+
raise AssertionError("Flagbyte has to be 0xc0")
|
|
106
|
+
salt = d[0:4]
|
|
107
|
+
d = d[4:-4]
|
|
108
|
+
if sys.version < "3":
|
|
109
|
+
if isinstance(passphrase, text_type):
|
|
110
|
+
passphrase = passphrase.encode("utf-8")
|
|
111
|
+
if SCRYPT_MODULE == "scrypt":
|
|
112
|
+
key = scrypt.hash(passphrase, salt, 16384, 8, 8)
|
|
113
|
+
elif SCRYPT_MODULE == "pylibscrypt":
|
|
114
|
+
key = scrypt.scrypt(py23_bytes(passphrase, "utf-8"), salt, 16384, 8, 8)
|
|
115
|
+
else:
|
|
116
|
+
raise ValueError("No scrypt module loaded")
|
|
117
|
+
derivedhalf1 = key[0:32]
|
|
118
|
+
derivedhalf2 = key[32:64]
|
|
119
|
+
encryptedhalf1 = d[0:16]
|
|
120
|
+
encryptedhalf2 = d[16:32]
|
|
121
|
+
aes = AES.new(derivedhalf2, AES.MODE_ECB)
|
|
122
|
+
decryptedhalf2 = aes.decrypt(encryptedhalf2)
|
|
123
|
+
decryptedhalf1 = aes.decrypt(encryptedhalf1)
|
|
124
|
+
privraw = decryptedhalf1 + decryptedhalf2
|
|
125
|
+
privraw = "%064x" % (int(hexlify(privraw), 16) ^ int(hexlify(derivedhalf1), 16))
|
|
126
|
+
wif = Base58(privraw)
|
|
127
|
+
""" Verify Salt """
|
|
128
|
+
privkey = PrivateKey(format(wif, "wif"))
|
|
129
|
+
addr = format(privkey.bitcoin.address, "BTC")
|
|
130
|
+
a = py23_bytes(addr, "ascii")
|
|
131
|
+
saltverify = hashlib.sha256(hashlib.sha256(a).digest()).digest()[0:4]
|
|
132
|
+
if saltverify != salt:
|
|
133
|
+
raise SaltException("checksum verification failed! Password may be incorrect.")
|
|
134
|
+
return wif
|