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
nectar/memo.py
ADDED
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import os
|
|
3
|
+
import random
|
|
4
|
+
import struct
|
|
5
|
+
from binascii import hexlify, unhexlify
|
|
6
|
+
|
|
7
|
+
from nectar.instance import shared_blockchain_instance
|
|
8
|
+
from nectar.version import version as __version__
|
|
9
|
+
from nectarbase import memo as BtsMemo
|
|
10
|
+
from nectargraphenebase.account import PrivateKey, PublicKey
|
|
11
|
+
from nectargraphenebase.base58 import base58decode, base58encode
|
|
12
|
+
|
|
13
|
+
from .account import Account
|
|
14
|
+
from .exceptions import MissingKeyError
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Memo(object):
|
|
18
|
+
"""Deals with Memos that are attached to a transfer
|
|
19
|
+
|
|
20
|
+
:param Account from_account: Account that has sent the memo
|
|
21
|
+
:param Account to_account: Account that has received the memo
|
|
22
|
+
:param Steem blockchain_instance: Steem instance
|
|
23
|
+
|
|
24
|
+
A memo is encrypted with a shared secret derived from a private key of
|
|
25
|
+
the sender and a public key of the receiver. Due to the underlying
|
|
26
|
+
mathematics, the same shared secret can be derived by the private key
|
|
27
|
+
of the receiver and the public key of the sender. The encrypted message
|
|
28
|
+
is perturbed by a nonce that is part of the transmitted message.
|
|
29
|
+
|
|
30
|
+
.. code-block:: python
|
|
31
|
+
|
|
32
|
+
from nectar.memo import Memo
|
|
33
|
+
m = Memo("thecrazygm", "hive-nectar")
|
|
34
|
+
m.unlock_wallet("secret")
|
|
35
|
+
enc = (m.encrypt("test"))
|
|
36
|
+
print(enc)
|
|
37
|
+
>> {'message': '#DTpKcbxWqsETCRfjYGk9feERFa5nVBF8FaHfWPwUjyHBTgNhXGh4mN5TTG41nLhUcHtXfu7Hy3AwLrtWvo1ERUyAZaJjaEZNhHyoeDnrHdWChrzbccbANQmazgwjyxzEL', 'from': 'STM6MQBLaX9Q15CK3prXoWK4C6EqtsL7C4rqq1h6BQjxvfk9tuT3N', 'to': 'STM6sRudsxWpTZWxnpRkCDVD51RteiJnvJYCt5LiZAbVLfM1hJCQC'}
|
|
38
|
+
print(m.decrypt(enc))
|
|
39
|
+
>> foobar
|
|
40
|
+
|
|
41
|
+
To decrypt a memo, simply use
|
|
42
|
+
|
|
43
|
+
.. code-block:: python
|
|
44
|
+
|
|
45
|
+
from nectar.memo import Memo
|
|
46
|
+
m = Memo()
|
|
47
|
+
m.unlock_wallet("secret")
|
|
48
|
+
print(m.decrypt(op_data["memo"]))
|
|
49
|
+
|
|
50
|
+
if ``op_data`` being the payload of a transfer operation.
|
|
51
|
+
|
|
52
|
+
Memo Keys
|
|
53
|
+
|
|
54
|
+
In Steem, memos are AES-256 encrypted with a shared secret between sender and
|
|
55
|
+
receiver. It is derived from the memo private key of the sender and the memo
|
|
56
|
+
public key of the receiver.
|
|
57
|
+
|
|
58
|
+
In order for the receiver to decode the memo, the shared secret has to be
|
|
59
|
+
derived from the receiver's private key and the senders public key.
|
|
60
|
+
|
|
61
|
+
The memo public key is part of the account and can be retrieved with the
|
|
62
|
+
`get_account` call:
|
|
63
|
+
|
|
64
|
+
.. code-block:: js
|
|
65
|
+
|
|
66
|
+
get_account <accountname>
|
|
67
|
+
{
|
|
68
|
+
[...]
|
|
69
|
+
"options": {
|
|
70
|
+
"memo_key": "GPH5TPTziKkLexhVKsQKtSpo4bAv5RnB8oXcG4sMHEwCcTf3r7dqE",
|
|
71
|
+
[...]
|
|
72
|
+
},
|
|
73
|
+
[...]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
while the memo private key can be dumped with `dump_private_keys`
|
|
77
|
+
|
|
78
|
+
Memo Message
|
|
79
|
+
|
|
80
|
+
The take the following form:
|
|
81
|
+
|
|
82
|
+
.. code-block:: js
|
|
83
|
+
|
|
84
|
+
{
|
|
85
|
+
"from": "GPH5mgup8evDqMnT86L7scVebRYDC2fwAWmygPEUL43LjstQegYCC",
|
|
86
|
+
"to": "GPH5Ar4j53kFWuEZQ9XhxbAja4YXMPJ2EnUg5QcrdeMFYUNMMNJbe",
|
|
87
|
+
"nonce": "13043867485137706821",
|
|
88
|
+
"message": "d55524c37320920844ca83bb20c8d008"
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
The fields `from` and `to` contain the memo public key of sender and receiver.
|
|
92
|
+
The `nonce` is a random integer that is used for the seed of the AES encryption
|
|
93
|
+
of the message.
|
|
94
|
+
|
|
95
|
+
Encrypting a memo
|
|
96
|
+
|
|
97
|
+
The high level memo class makes use of the nectar wallet to obtain keys
|
|
98
|
+
for the corresponding accounts.
|
|
99
|
+
|
|
100
|
+
.. code-block:: python
|
|
101
|
+
|
|
102
|
+
from nectar.memo import Memo
|
|
103
|
+
from nectar.account import Account
|
|
104
|
+
|
|
105
|
+
memoObj = Memo(
|
|
106
|
+
from_account=Account(from_account),
|
|
107
|
+
to_account=Account(to_account)
|
|
108
|
+
)
|
|
109
|
+
encrypted_memo = memoObj.encrypt(memo)
|
|
110
|
+
|
|
111
|
+
Decoding of a received memo
|
|
112
|
+
|
|
113
|
+
.. code-block:: python
|
|
114
|
+
|
|
115
|
+
from getpass import getpass
|
|
116
|
+
from nectar.block import Block
|
|
117
|
+
from nectar.memo import Memo
|
|
118
|
+
|
|
119
|
+
# Obtain a transfer from the blockchain
|
|
120
|
+
block = Block(23755086) # block
|
|
121
|
+
transaction = block["transactions"][3] # transactions
|
|
122
|
+
op = transaction["operations"][0] # operation
|
|
123
|
+
op_id = op[0] # operation type
|
|
124
|
+
op_data = op[1] # operation payload
|
|
125
|
+
|
|
126
|
+
# Instantiate Memo for decoding
|
|
127
|
+
memo = Memo()
|
|
128
|
+
|
|
129
|
+
# Unlock wallet
|
|
130
|
+
memo.unlock_wallet(getpass())
|
|
131
|
+
|
|
132
|
+
# Decode memo
|
|
133
|
+
# Raises exception if required keys not available in the wallet
|
|
134
|
+
print(memo.decrypt(op_data["transfer"]))
|
|
135
|
+
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
def __init__(self, from_account=None, to_account=None, blockchain_instance=None, **kwargs):
|
|
139
|
+
if blockchain_instance is None:
|
|
140
|
+
if kwargs.get("steem_instance"):
|
|
141
|
+
blockchain_instance = kwargs["steem_instance"]
|
|
142
|
+
elif kwargs.get("hive_instance"):
|
|
143
|
+
blockchain_instance = kwargs["hive_instance"]
|
|
144
|
+
self.blockchain = blockchain_instance or shared_blockchain_instance()
|
|
145
|
+
|
|
146
|
+
if to_account and len(to_account) < 51:
|
|
147
|
+
self.to_account = Account(to_account, blockchain_instance=self.blockchain)
|
|
148
|
+
elif to_account and len(to_account) >= 51:
|
|
149
|
+
self.to_account = PublicKey(to_account)
|
|
150
|
+
else:
|
|
151
|
+
self.to_account = None
|
|
152
|
+
if from_account and len(from_account) < 51:
|
|
153
|
+
self.from_account = Account(from_account, blockchain_instance=self.blockchain)
|
|
154
|
+
elif from_account and len(from_account) >= 51:
|
|
155
|
+
self.from_account = PrivateKey(from_account)
|
|
156
|
+
else:
|
|
157
|
+
self.from_account = None
|
|
158
|
+
|
|
159
|
+
def unlock_wallet(self, *args, **kwargs):
|
|
160
|
+
"""Unlock the library internal wallet"""
|
|
161
|
+
self.blockchain.wallet.unlock(*args, **kwargs)
|
|
162
|
+
|
|
163
|
+
def encrypt(self, memo, bts_encrypt=False, return_enc_memo_only=False, nonce=None):
|
|
164
|
+
"""Encrypt a memo
|
|
165
|
+
|
|
166
|
+
:param str memo: clear text memo message
|
|
167
|
+
:param bool return_enc_memo_only: When True, only the encoded memo is returned
|
|
168
|
+
:param str nonce: when not set, a random string is generated and used
|
|
169
|
+
:returns: encrypted memo
|
|
170
|
+
:rtype: dict
|
|
171
|
+
"""
|
|
172
|
+
if not memo:
|
|
173
|
+
return None
|
|
174
|
+
if nonce is None:
|
|
175
|
+
nonce = str(random.getrandbits(64))
|
|
176
|
+
if isinstance(self.from_account, Account):
|
|
177
|
+
memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(
|
|
178
|
+
self.from_account["memo_key"]
|
|
179
|
+
)
|
|
180
|
+
memo_wif = PrivateKey(memo_wif)
|
|
181
|
+
else:
|
|
182
|
+
memo_wif = self.from_account
|
|
183
|
+
if isinstance(self.to_account, Account):
|
|
184
|
+
pubkey = self.to_account["memo_key"]
|
|
185
|
+
else:
|
|
186
|
+
pubkey = self.to_account
|
|
187
|
+
if not memo_wif:
|
|
188
|
+
raise MissingKeyError("Memo key for %s missing!" % self.from_account["name"])
|
|
189
|
+
|
|
190
|
+
if not hasattr(self, "chain_prefix"):
|
|
191
|
+
self.chain_prefix = self.blockchain.prefix
|
|
192
|
+
|
|
193
|
+
if bts_encrypt:
|
|
194
|
+
enc = BtsMemo.encode_memo_bts(
|
|
195
|
+
PrivateKey(memo_wif), PublicKey(pubkey, prefix=self.chain_prefix), nonce, memo
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
"message": enc,
|
|
200
|
+
"nonce": nonce,
|
|
201
|
+
"from": str(PrivateKey(memo_wif).pubkey),
|
|
202
|
+
"to": str(pubkey),
|
|
203
|
+
}
|
|
204
|
+
else:
|
|
205
|
+
enc = BtsMemo.encode_memo(
|
|
206
|
+
PrivateKey(memo_wif),
|
|
207
|
+
PublicKey(pubkey, prefix=self.chain_prefix),
|
|
208
|
+
nonce,
|
|
209
|
+
memo,
|
|
210
|
+
prefix=self.chain_prefix,
|
|
211
|
+
)
|
|
212
|
+
if return_enc_memo_only:
|
|
213
|
+
return enc
|
|
214
|
+
return {"message": enc, "from": str(PrivateKey(memo_wif).pubkey), "to": str(pubkey)}
|
|
215
|
+
|
|
216
|
+
def encrypt_binary(self, infile, outfile, buffer_size=2048, nonce=None):
|
|
217
|
+
"""Encrypt a binary file
|
|
218
|
+
|
|
219
|
+
:param str infile: input file name
|
|
220
|
+
:param str outfile: output file name
|
|
221
|
+
:param int buffer_size: write buffer size
|
|
222
|
+
:param str nonce: when not set, a random string is generated and used
|
|
223
|
+
"""
|
|
224
|
+
if not os.path.exists(infile):
|
|
225
|
+
raise ValueError("%s does not exists!" % infile)
|
|
226
|
+
|
|
227
|
+
if nonce is None:
|
|
228
|
+
nonce = str(random.getrandbits(64))
|
|
229
|
+
if isinstance(self.from_account, Account):
|
|
230
|
+
memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(
|
|
231
|
+
self.from_account["memo_key"]
|
|
232
|
+
)
|
|
233
|
+
else:
|
|
234
|
+
memo_wif = self.from_account
|
|
235
|
+
if isinstance(self.to_account, Account):
|
|
236
|
+
pubkey = self.to_account["memo_key"]
|
|
237
|
+
else:
|
|
238
|
+
pubkey = self.to_account
|
|
239
|
+
if not memo_wif:
|
|
240
|
+
raise MissingKeyError("Memo key for %s missing!" % self.from_account["name"])
|
|
241
|
+
|
|
242
|
+
if not hasattr(self, "chain_prefix"):
|
|
243
|
+
self.chain_prefix = self.blockchain.prefix
|
|
244
|
+
|
|
245
|
+
file_size = os.path.getsize(infile)
|
|
246
|
+
priv = PrivateKey(memo_wif)
|
|
247
|
+
pub = PublicKey(pubkey, prefix=self.chain_prefix)
|
|
248
|
+
enc = BtsMemo.encode_memo(
|
|
249
|
+
priv, pub, nonce, "nectar/%s" % __version__, prefix=self.chain_prefix
|
|
250
|
+
)
|
|
251
|
+
enc = unhexlify(base58decode(enc[1:]))
|
|
252
|
+
shared_secret = BtsMemo.get_shared_secret(priv, pub)
|
|
253
|
+
aes, check = BtsMemo.init_aes(shared_secret, nonce)
|
|
254
|
+
with open(outfile, "wb") as fout:
|
|
255
|
+
fout.write(struct.pack("<Q", len(enc)))
|
|
256
|
+
fout.write(enc)
|
|
257
|
+
fout.write(struct.pack("<Q", file_size))
|
|
258
|
+
with open(infile, "rb") as fin:
|
|
259
|
+
while True:
|
|
260
|
+
data = fin.read(buffer_size)
|
|
261
|
+
n = len(data)
|
|
262
|
+
if n == 0:
|
|
263
|
+
break
|
|
264
|
+
elif n % 16 != 0:
|
|
265
|
+
data += b" " * (16 - n % 16) # <- padded with spaces
|
|
266
|
+
encd = aes.encrypt(data)
|
|
267
|
+
fout.write(encd)
|
|
268
|
+
|
|
269
|
+
def extract_decrypt_memo_data(self, memo):
|
|
270
|
+
"""Returns information about an encrypted memo"""
|
|
271
|
+
from_key, to_key, nonce, check, cipher = BtsMemo.extract_memo_data(memo)
|
|
272
|
+
return from_key, to_key, nonce
|
|
273
|
+
|
|
274
|
+
def decrypt(self, memo):
|
|
275
|
+
"""Decrypt a memo
|
|
276
|
+
|
|
277
|
+
:param str memo: encrypted memo message
|
|
278
|
+
:returns: encrypted memo
|
|
279
|
+
:rtype: str
|
|
280
|
+
"""
|
|
281
|
+
if not memo:
|
|
282
|
+
return None
|
|
283
|
+
memo_wif = None
|
|
284
|
+
# We first try to decode assuming we received the memo
|
|
285
|
+
if isinstance(memo, dict) and "to" in memo and "from" in memo and "memo" in memo:
|
|
286
|
+
memo_to = Account(memo["to"], blockchain_instance=self.blockchain)
|
|
287
|
+
memo_from = Account(memo["from"], blockchain_instance=self.blockchain)
|
|
288
|
+
message = memo["memo"]
|
|
289
|
+
else:
|
|
290
|
+
memo_to = self.to_account
|
|
291
|
+
memo_from = self.from_account
|
|
292
|
+
message = memo
|
|
293
|
+
if isinstance(memo, dict) and "nonce" in memo:
|
|
294
|
+
nonce = memo.get("nonce")
|
|
295
|
+
else:
|
|
296
|
+
nonce = ""
|
|
297
|
+
|
|
298
|
+
if memo_to is None or memo_from is None:
|
|
299
|
+
from_key, to_key, nonce, check, cipher = BtsMemo.extract_memo_data(message)
|
|
300
|
+
try:
|
|
301
|
+
memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(str(to_key))
|
|
302
|
+
pubkey = from_key
|
|
303
|
+
except MissingKeyError:
|
|
304
|
+
try:
|
|
305
|
+
# if that failed, we assume that we have sent the memo
|
|
306
|
+
memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(str(from_key))
|
|
307
|
+
pubkey = to_key
|
|
308
|
+
except MissingKeyError:
|
|
309
|
+
# if all fails, raise exception
|
|
310
|
+
raise MissingKeyError(
|
|
311
|
+
"Non of the required memo keys are installed!Need any of {}".format(
|
|
312
|
+
[str(to_key), str(from_key)]
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
elif memo_to is not None and memo_from is not None and isinstance(memo_from, PrivateKey):
|
|
316
|
+
memo_wif = memo_from
|
|
317
|
+
pubkey = memo_to
|
|
318
|
+
elif memo_to is not None and memo_from is not None and isinstance(memo_to, PrivateKey):
|
|
319
|
+
memo_wif = memo_to
|
|
320
|
+
pubkey = memo_from
|
|
321
|
+
else:
|
|
322
|
+
try:
|
|
323
|
+
memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(memo_to["memo_key"])
|
|
324
|
+
pubkey = memo_from["memo_key"]
|
|
325
|
+
except MissingKeyError:
|
|
326
|
+
try:
|
|
327
|
+
# if that failed, we assume that we have sent the memo
|
|
328
|
+
memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(
|
|
329
|
+
memo_from["memo_key"]
|
|
330
|
+
)
|
|
331
|
+
pubkey = memo_to["memo_key"]
|
|
332
|
+
except MissingKeyError:
|
|
333
|
+
# if all fails, raise exception
|
|
334
|
+
raise MissingKeyError(
|
|
335
|
+
"Non of the required memo keys are installed!Need any of {}".format(
|
|
336
|
+
[memo_to["name"], memo_from["name"]]
|
|
337
|
+
)
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
if not hasattr(self, "chain_prefix"):
|
|
341
|
+
self.chain_prefix = self.blockchain.prefix
|
|
342
|
+
|
|
343
|
+
if message[0] == "#" or memo_to is None or memo_from is None:
|
|
344
|
+
return BtsMemo.decode_memo(PrivateKey(memo_wif), message)
|
|
345
|
+
else:
|
|
346
|
+
return BtsMemo.decode_memo_bts(
|
|
347
|
+
PrivateKey(memo_wif), PublicKey(pubkey, prefix=self.chain_prefix), nonce, message
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
def decrypt_binary(self, infile, outfile, buffer_size=2048):
|
|
351
|
+
"""Decrypt a binary file
|
|
352
|
+
|
|
353
|
+
:param str infile: encrypted binary file
|
|
354
|
+
:param str outfile: output file name
|
|
355
|
+
:param int buffer_size: read buffer size
|
|
356
|
+
:returns: encrypted memo information
|
|
357
|
+
:rtype: dict
|
|
358
|
+
"""
|
|
359
|
+
if not os.path.exists(infile):
|
|
360
|
+
raise ValueError("%s does not exists!" % infile)
|
|
361
|
+
if buffer_size % 16 != 0:
|
|
362
|
+
raise ValueError("buffer_size must be dividable by 16")
|
|
363
|
+
with open(infile, "rb") as fin:
|
|
364
|
+
memo_size = struct.unpack("<Q", fin.read(struct.calcsize("<Q")))[0]
|
|
365
|
+
memo = fin.read(memo_size)
|
|
366
|
+
orig_file_size = struct.unpack("<Q", fin.read(struct.calcsize("<Q")))[0]
|
|
367
|
+
memo = "#" + base58encode(hexlify(memo).decode("ascii"))
|
|
368
|
+
memo_to = self.to_account
|
|
369
|
+
memo_from = self.from_account
|
|
370
|
+
from_key, to_key, nonce, check, cipher = BtsMemo.extract_memo_data(memo)
|
|
371
|
+
if memo_to is None and memo_from is None:
|
|
372
|
+
try:
|
|
373
|
+
memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(str(to_key))
|
|
374
|
+
pubkey = from_key
|
|
375
|
+
except MissingKeyError:
|
|
376
|
+
try:
|
|
377
|
+
# if that failed, we assume that we have sent the memo
|
|
378
|
+
memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(str(from_key))
|
|
379
|
+
pubkey = to_key
|
|
380
|
+
except MissingKeyError:
|
|
381
|
+
# if all fails, raise exception
|
|
382
|
+
raise MissingKeyError(
|
|
383
|
+
"Non of the required memo keys are installed!Need any of {}".format(
|
|
384
|
+
[str(to_key), str(from_key)]
|
|
385
|
+
)
|
|
386
|
+
)
|
|
387
|
+
elif memo_to is not None and memo_from is not None and isinstance(memo_from, PrivateKey):
|
|
388
|
+
memo_wif = memo_from
|
|
389
|
+
if isinstance(memo_to, Account):
|
|
390
|
+
pubkey = memo_to["memo_key"]
|
|
391
|
+
else:
|
|
392
|
+
pubkey = memo_to
|
|
393
|
+
elif memo_to is not None and memo_from is not None and isinstance(memo_to, PrivateKey):
|
|
394
|
+
memo_wif = memo_to
|
|
395
|
+
if isinstance(memo_from, Account):
|
|
396
|
+
pubkey = memo_from["memo_key"]
|
|
397
|
+
else:
|
|
398
|
+
pubkey = memo_from
|
|
399
|
+
else:
|
|
400
|
+
try:
|
|
401
|
+
memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(memo_to["memo_key"])
|
|
402
|
+
pubkey = memo_from["memo_key"]
|
|
403
|
+
except MissingKeyError:
|
|
404
|
+
try:
|
|
405
|
+
# if that failed, we assume that we have sent the memo
|
|
406
|
+
memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(
|
|
407
|
+
memo_from["memo_key"]
|
|
408
|
+
)
|
|
409
|
+
pubkey = memo_to["memo_key"]
|
|
410
|
+
except MissingKeyError:
|
|
411
|
+
# if all fails, raise exception
|
|
412
|
+
raise MissingKeyError(
|
|
413
|
+
"Non of the required memo keys are installed!Need any of {}".format(
|
|
414
|
+
[memo_to["name"], memo_from["name"]]
|
|
415
|
+
)
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
if not hasattr(self, "chain_prefix"):
|
|
419
|
+
self.chain_prefix = self.blockchain.prefix
|
|
420
|
+
priv = PrivateKey(memo_wif)
|
|
421
|
+
pubkey = PublicKey(pubkey, prefix=self.chain_prefix)
|
|
422
|
+
nectar_version = BtsMemo.decode_memo(priv, memo)
|
|
423
|
+
shared_secret = BtsMemo.get_shared_secret(priv, pubkey)
|
|
424
|
+
# Init encryption
|
|
425
|
+
aes, checksum = BtsMemo.init_aes(shared_secret, nonce)
|
|
426
|
+
with open(infile, "rb") as fin:
|
|
427
|
+
memo_size = struct.unpack("<Q", fin.read(struct.calcsize("<Q")))[0]
|
|
428
|
+
memo = fin.read(memo_size)
|
|
429
|
+
file_size = struct.unpack("<Q", fin.read(struct.calcsize("<Q")))[0]
|
|
430
|
+
with open(outfile, "wb") as fout:
|
|
431
|
+
while True:
|
|
432
|
+
data = fin.read(buffer_size)
|
|
433
|
+
n = len(data)
|
|
434
|
+
if n == 0:
|
|
435
|
+
break
|
|
436
|
+
decd = aes.decrypt(data)
|
|
437
|
+
n = len(decd)
|
|
438
|
+
if file_size > n:
|
|
439
|
+
fout.write(decd)
|
|
440
|
+
else:
|
|
441
|
+
fout.write(decd[:file_size]) # <- remove padding on last block
|
|
442
|
+
file_size -= n
|
|
443
|
+
return {
|
|
444
|
+
"file_size": orig_file_size,
|
|
445
|
+
"from_key": str(from_key),
|
|
446
|
+
"to_key": str(to_key),
|
|
447
|
+
"nonce": nonce,
|
|
448
|
+
"nectar_version": nectar_version,
|
|
449
|
+
}
|