forked from LBRYCommunity/lbry-sdk
working sync
This commit is contained in:
parent
3a1cd8985c
commit
b07ca20e0e
3 changed files with 62 additions and 10 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
import json
|
||||||
import asyncio
|
import asyncio
|
||||||
import random
|
import random
|
||||||
import typing
|
import typing
|
||||||
|
@ -5,7 +6,7 @@ from typing import Dict, Tuple, Type, Optional, Any, List
|
||||||
|
|
||||||
from torba.client.mnemonic import Mnemonic
|
from torba.client.mnemonic import Mnemonic
|
||||||
from torba.client.bip32 import PrivateKey, PubKey, from_extended_key_string
|
from torba.client.bip32 import PrivateKey, PubKey, from_extended_key_string
|
||||||
from torba.client.hash import aes_encrypt, aes_decrypt
|
from torba.client.hash import aes_encrypt, aes_decrypt, sha256
|
||||||
from torba.client.constants import COIN
|
from torba.client.constants import COIN
|
||||||
|
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
|
@ -235,7 +236,8 @@ class BaseAccount:
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, ledger: 'baseledger.BaseLedger', wallet: 'basewallet.Wallet', d: dict):
|
def keys_from_dict(cls, ledger: 'baseledger.BaseLedger', d: dict) \
|
||||||
|
-> Tuple[str, Optional[PrivateKey], PubKey]:
|
||||||
seed = d.get('seed', '')
|
seed = d.get('seed', '')
|
||||||
private_key_string = d.get('private_key', '')
|
private_key_string = d.get('private_key', '')
|
||||||
private_key = None
|
private_key = None
|
||||||
|
@ -250,6 +252,11 @@ class BaseAccount:
|
||||||
public_key = private_key.public_key
|
public_key = private_key.public_key
|
||||||
if public_key is None:
|
if public_key is None:
|
||||||
public_key = from_extended_key_string(ledger, d['public_key'])
|
public_key = from_extended_key_string(ledger, d['public_key'])
|
||||||
|
return seed, private_key, public_key
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, ledger: 'baseledger.BaseLedger', wallet: 'basewallet.Wallet', d: dict):
|
||||||
|
seed, private_key, public_key = cls.keys_from_dict(ledger, d)
|
||||||
name = d.get('name')
|
name = d.get('name')
|
||||||
if not name:
|
if not name:
|
||||||
name = 'Account #{}'.format(public_key.address)
|
name = 'Account #{}'.format(public_key.address)
|
||||||
|
@ -258,8 +265,8 @@ class BaseAccount:
|
||||||
wallet=wallet,
|
wallet=wallet,
|
||||||
name=name,
|
name=name,
|
||||||
seed=seed,
|
seed=seed,
|
||||||
private_key_string=private_key_string,
|
private_key_string=d.get('private_key', ''),
|
||||||
encrypted=encrypted,
|
encrypted=d.get('encrypted', False),
|
||||||
private_key=private_key,
|
private_key=private_key,
|
||||||
public_key=public_key,
|
public_key=public_key,
|
||||||
address_generator=d.get('address_generator', {})
|
address_generator=d.get('address_generator', {})
|
||||||
|
@ -273,8 +280,8 @@ class BaseAccount:
|
||||||
assert None not in [self.seed_encryption_init_vector, self.private_key_encryption_init_vector]
|
assert None not in [self.seed_encryption_init_vector, self.private_key_encryption_init_vector]
|
||||||
private_key_string = aes_encrypt(
|
private_key_string = aes_encrypt(
|
||||||
self.password, private_key_string, self.private_key_encryption_init_vector
|
self.password, private_key_string, self.private_key_encryption_init_vector
|
||||||
)
|
)[0]
|
||||||
seed = aes_encrypt(self.password, self.seed, self.seed_encryption_init_vector)
|
seed = aes_encrypt(self.password, self.seed, self.seed_encryption_init_vector)[0]
|
||||||
return {
|
return {
|
||||||
'ledger': self.ledger.get_id(),
|
'ledger': self.ledger.get_id(),
|
||||||
'name': self.name,
|
'name': self.name,
|
||||||
|
@ -285,6 +292,10 @@ class BaseAccount:
|
||||||
'address_generator': self.address_generator.to_dict(self.receiving, self.change)
|
'address_generator': self.address_generator.to_dict(self.receiving, self.change)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hash(self) -> bytes:
|
||||||
|
return sha256(json.dumps(self.to_dict()).encode())
|
||||||
|
|
||||||
async def get_details(self, show_seed=False, **kwargs):
|
async def get_details(self, show_seed=False, **kwargs):
|
||||||
satoshis = await self.get_balance(**kwargs)
|
satoshis = await self.get_balance(**kwargs)
|
||||||
details = {
|
details = {
|
||||||
|
@ -329,10 +340,10 @@ class BaseAccount:
|
||||||
assert not self.encrypted, "Key is already encrypted."
|
assert not self.encrypted, "Key is already encrypted."
|
||||||
assert isinstance(self.private_key, PrivateKey)
|
assert isinstance(self.private_key, PrivateKey)
|
||||||
|
|
||||||
self.seed = aes_encrypt(password, self.seed, self.seed_encryption_init_vector)
|
self.seed = aes_encrypt(password, self.seed, self.seed_encryption_init_vector)[0]
|
||||||
self.private_key_string = aes_encrypt(
|
self.private_key_string = aes_encrypt(
|
||||||
password, self.private_key.extended_key_string(), self.private_key_encryption_init_vector
|
password, self.private_key.extended_key_string(), self.private_key_encryption_init_vector
|
||||||
)
|
)[0]
|
||||||
self.private_key = None
|
self.private_key = None
|
||||||
self.password = None
|
self.password = None
|
||||||
self.encrypted = True
|
self.encrypted = True
|
||||||
|
|
|
@ -121,7 +121,7 @@ def hex_str_to_hash(x):
|
||||||
return reversed(unhexlify(x))
|
return reversed(unhexlify(x))
|
||||||
|
|
||||||
|
|
||||||
def aes_encrypt(secret: str, value: str, init_vector: bytes = None) -> str:
|
def aes_encrypt(secret: str, value: str, init_vector: bytes = None) -> typing.Tuple[str, bytes]:
|
||||||
if init_vector is not None:
|
if init_vector is not None:
|
||||||
assert len(init_vector) == 16
|
assert len(init_vector) == 16
|
||||||
else:
|
else:
|
||||||
|
@ -144,6 +144,25 @@ def aes_decrypt(secret: str, value: str) -> typing.Tuple[str, bytes]:
|
||||||
return result.decode(), init_vector
|
return result.decode(), init_vector
|
||||||
|
|
||||||
|
|
||||||
|
def better_aes_encrypt(secret: str, value: bytes) -> bytes:
|
||||||
|
init_vector = os.urandom(16)
|
||||||
|
key = double_sha256(secret.encode())
|
||||||
|
encryptor = Cipher(AES(key), modes.CBC(init_vector), default_backend()).encryptor()
|
||||||
|
padder = PKCS7(AES.block_size).padder()
|
||||||
|
padded_data = padder.update(value) + padder.finalize()
|
||||||
|
encrypted_data = encryptor.update(padded_data) + encryptor.finalize()
|
||||||
|
return base64.b64encode(init_vector + encrypted_data)
|
||||||
|
|
||||||
|
|
||||||
|
def better_aes_decrypt(secret: str, value: bytes) -> bytes:
|
||||||
|
data = base64.b64decode(value)
|
||||||
|
key = double_sha256(secret.encode())
|
||||||
|
init_vector, data = data[:16], data[16:]
|
||||||
|
decryptor = Cipher(AES(key), modes.CBC(init_vector), default_backend()).decryptor()
|
||||||
|
unpadder = PKCS7(AES.block_size).unpadder()
|
||||||
|
return unpadder.update(decryptor.update(data)) + unpadder.finalize()
|
||||||
|
|
||||||
|
|
||||||
class Base58Error(Exception):
|
class Base58Error(Exception):
|
||||||
""" Exception used for Base58 errors. """
|
""" Exception used for Base58 errors. """
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
|
import os
|
||||||
import stat
|
import stat
|
||||||
import json
|
import json
|
||||||
import os
|
import zlib
|
||||||
import typing
|
import typing
|
||||||
|
from hashlib import sha256
|
||||||
from typing import Sequence, MutableSequence
|
from typing import Sequence, MutableSequence
|
||||||
|
from torba.client.hash import better_aes_encrypt, better_aes_decrypt
|
||||||
|
from operator import attrgetter
|
||||||
|
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
from torba.client import basemanager, baseaccount, baseledger
|
from torba.client import basemanager, baseaccount, baseledger
|
||||||
|
@ -55,6 +59,24 @@ class Wallet:
|
||||||
for account in self.accounts:
|
for account in self.accounts:
|
||||||
return account
|
return account
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hash(self) -> str:
|
||||||
|
h = sha256()
|
||||||
|
for account in sorted(self.accounts, key=attrgetter('id')):
|
||||||
|
h.update(account.hash)
|
||||||
|
return h.digest()
|
||||||
|
|
||||||
|
def pack(self, password):
|
||||||
|
new_data = json.dumps(self.to_dict())
|
||||||
|
new_data_compressed = zlib.compress(new_data.encode())
|
||||||
|
return better_aes_encrypt(password, new_data_compressed)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def unpack(cls, password, encrypted):
|
||||||
|
decrypted = better_aes_decrypt(password, encrypted)
|
||||||
|
decompressed = zlib.decompress(decrypted)
|
||||||
|
return json.loads(decompressed)
|
||||||
|
|
||||||
|
|
||||||
class WalletStorage:
|
class WalletStorage:
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue