forked from LBRYCommunity/lbry-sdk
147 lines
4.9 KiB
Python
147 lines
4.9 KiB
Python
import os
|
|
import json
|
|
import typing
|
|
import logging
|
|
import asyncio
|
|
from binascii import unhexlify
|
|
from decimal import Decimal
|
|
from typing import List, Type, MutableSequence, MutableMapping, Optional
|
|
|
|
from lbry.error import KeyFeeAboveMaxAllowedError
|
|
from lbry.conf import Config
|
|
|
|
from .account import Account
|
|
from lbry.blockchain.dewies import dewies_to_lbc
|
|
from lbry.blockchain.ledger import Ledger
|
|
from lbry.db import Database
|
|
from lbry.blockchain.ledger import Ledger
|
|
from lbry.blockchain.transaction import Transaction, Output
|
|
|
|
from .wallet import Wallet, WalletStorage, ENCRYPT_ON_DISK
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
class WalletManager:
|
|
|
|
def __init__(self, ledger: Ledger, db: Database,
|
|
wallets: MutableSequence[Wallet] = None,
|
|
ledgers: MutableMapping[Type[Ledger], Ledger] = None) -> None:
|
|
self.ledger = ledger
|
|
self.db = db
|
|
self.wallets = wallets or []
|
|
self.ledgers = ledgers or {}
|
|
self.running = False
|
|
self.config: Optional[Config] = None
|
|
|
|
async def open(self):
|
|
conf = self.ledger.conf
|
|
|
|
wallets_directory = os.path.join(conf.wallet_dir, 'wallets')
|
|
if not os.path.exists(wallets_directory):
|
|
os.mkdir(wallets_directory)
|
|
|
|
for wallet_file in conf.wallets:
|
|
wallet_path = os.path.join(wallets_directory, wallet_file)
|
|
wallet_storage = WalletStorage(wallet_path)
|
|
wallet = Wallet.from_storage(self.ledger, self.db, wallet_storage)
|
|
self.wallets.append(wallet)
|
|
|
|
self.ledger.coin_selection_strategy = self.ledger.conf.coin_selection_strategy
|
|
default_wallet = self.default_wallet
|
|
if default_wallet.default_account is None:
|
|
log.info('Wallet at %s is empty, generating a default account.', default_wallet.id)
|
|
default_wallet.generate_account()
|
|
default_wallet.save()
|
|
if default_wallet.is_locked and default_wallet.preferences.get(ENCRYPT_ON_DISK) is None:
|
|
default_wallet.preferences[ENCRYPT_ON_DISK] = True
|
|
default_wallet.save()
|
|
|
|
def import_wallet(self, path):
|
|
storage = WalletStorage(path)
|
|
wallet = Wallet.from_storage(self.ledger, self.db, storage)
|
|
self.wallets.append(wallet)
|
|
return wallet
|
|
|
|
@property
|
|
def default_wallet(self):
|
|
for wallet in self.wallets:
|
|
return wallet
|
|
|
|
@property
|
|
def default_account(self):
|
|
for wallet in self.wallets:
|
|
return wallet.default_account
|
|
|
|
@property
|
|
def accounts(self):
|
|
for wallet in self.wallets:
|
|
yield from wallet.accounts
|
|
|
|
async def start(self):
|
|
self.running = True
|
|
await asyncio.gather(*(
|
|
l.start() for l in self.ledgers.values()
|
|
))
|
|
|
|
async def stop(self):
|
|
await asyncio.gather(*(
|
|
l.stop() for l in self.ledgers.values()
|
|
))
|
|
self.running = False
|
|
|
|
def get_wallet_or_default(self, wallet_id: Optional[str]) -> Wallet:
|
|
if wallet_id is None:
|
|
return self.default_wallet
|
|
return self.get_wallet_or_error(wallet_id)
|
|
|
|
def get_wallet_or_error(self, wallet_id: str) -> Wallet:
|
|
for wallet in self.wallets:
|
|
if wallet.id == wallet_id:
|
|
return wallet
|
|
raise ValueError(f"Couldn't find wallet: {wallet_id}.")
|
|
|
|
@staticmethod
|
|
def get_balance(wallet):
|
|
accounts = wallet.accounts
|
|
if not accounts:
|
|
return 0
|
|
return accounts[0].ledger.db.get_balance(wallet=wallet, accounts=accounts)
|
|
|
|
def check_locked(self):
|
|
return self.default_wallet.is_locked
|
|
|
|
async def reset(self):
|
|
self.ledger.config = {
|
|
'auto_connect': True,
|
|
'default_servers': self.config.lbryum_servers,
|
|
'data_path': self.config.wallet_dir,
|
|
}
|
|
await self.ledger.stop()
|
|
await self.ledger.start()
|
|
|
|
async def get_best_blockhash(self):
|
|
if len(self.ledger.headers) <= 0:
|
|
return self.ledger.genesis_hash
|
|
return (await self.ledger.headers.hash(self.ledger.headers.height)).decode()
|
|
|
|
def get_unused_address(self):
|
|
return self.default_account.receiving.get_or_create_usable_address()
|
|
|
|
async def get_transaction(self, tx_hash: bytes):
|
|
tx = await self.db.get_transaction(tx_hash=tx_hash)
|
|
if tx:
|
|
return tx
|
|
try:
|
|
raw, merkle = await self.ledger.network.get_transaction_and_merkle(tx_hash)
|
|
except CodeMessageError as e:
|
|
if 'No such mempool or blockchain transaction.' in e.message:
|
|
return {'success': False, 'code': 404, 'message': 'transaction not found'}
|
|
return {'success': False, 'code': e.code, 'message': e.message}
|
|
height = merkle.get('block_height')
|
|
tx = Transaction(unhexlify(raw), height=height)
|
|
if height and height > 0:
|
|
await self.ledger.maybe_verify_transaction(tx, height, merkle)
|
|
return tx
|
|
|