lbry-sdk/lbry/wallet/manager.py
Lex Berezhny 3ef83febc0 progress
2020-06-05 00:35:22 -04:00

116 lines
4.5 KiB
Python

import os
import asyncio
import logging
from typing import Optional, Dict
from lbry.db import Database
from lbry.blockchain.ledger import Ledger
from .wallet import Wallet
log = logging.getLogger(__name__)
class WalletManager:
def __init__(self, ledger: Ledger, db: Database):
self.ledger = ledger
self.db = db
self.wallets: Dict[str, Wallet] = {}
def __getitem__(self, wallet_id: str) -> Wallet:
try:
return self.wallets[wallet_id]
except KeyError:
raise ValueError(f"Couldn't find wallet: {wallet_id}.")
@property
def default(self) -> Optional[Wallet]:
for wallet in self.wallets.values():
return wallet
def get_or_default(self, wallet_id: Optional[str]) -> Wallet:
if wallet_id:
return self[wallet_id]
wallet = self.default
if not wallet:
raise ValueError("No wallets available.")
return wallet
def get_or_default_for_spending(self, wallet_id: Optional[str]) -> Wallet:
wallet = self.get_or_default(wallet_id)
if wallet.is_locked:
raise ValueError("Cannot spend funds with locked wallet, unlock first.")
return wallet
@property
def path(self):
return os.path.join(self.ledger.conf.wallet_dir, 'wallets')
def sync_ensure_path_exists(self):
if not os.path.exists(self.path):
os.mkdir(self.path)
async def ensure_path_exists(self):
await asyncio.get_running_loop().run_in_executor(
None, self.sync_ensure_path_exists
)
async def load(self):
wallets_directory = self.path
for wallet_id in self.ledger.conf.wallets:
if wallet_id in self.wallets:
log.warning("Ignoring duplicate wallet_id in config: %s", wallet_id)
continue
wallet_path = os.path.join(wallets_directory, wallet_id)
if not os.path.exists(wallet_path):
if not wallet_id == "default_wallet": # we'll probably generate this wallet, don't show error
log.error("Could not load wallet, file does not exist: %s", wallet_path)
continue
wallet = await Wallet.from_path(self.ledger, self.db, wallet_path)
self.add(wallet)
default_wallet = self.default
if default_wallet is None:
if self.ledger.conf.create_default_wallet:
assert self.ledger.conf.wallets[0] == "default_wallet", (
"Requesting to generate the default wallet but the 'wallets' "
"config setting does not include 'default_wallet' as the first wallet."
)
await self.create(
self.ledger.conf.wallets[0], 'Wallet',
create_account=self.ledger.conf.create_default_account
)
elif not default_wallet.has_accounts and self.ledger.conf.create_default_account:
await default_wallet.accounts.generate()
def add(self, wallet: Wallet) -> Wallet:
self.wallets[wallet.id] = wallet
return wallet
async def add_from_path(self, wallet_path) -> Wallet:
wallet_id = os.path.basename(wallet_path)
if wallet_id in self.wallets:
existing = self.wallets.get(wallet_id)
if existing.storage.path == wallet_path:
raise Exception(f"Wallet '{wallet_id}' is already loaded.")
raise Exception(
f"Wallet '{wallet_id}' is already loaded from '{existing.storage.path}'"
f" and cannot be loaded from '{wallet_path}'. Consider changing the wallet"
f" filename to be unique in order to avoid conflicts."
)
wallet = await Wallet.from_path(self.ledger, self.db, wallet_path)
return self.add(wallet)
async def create(
self, wallet_id: str, name: str,
create_account=False, language='en', single_key=False) -> Wallet:
if wallet_id in self.wallets:
raise Exception(f"Wallet with id '{wallet_id}' is already loaded and cannot be created.")
wallet_path = os.path.join(self.path, wallet_id)
if os.path.exists(wallet_path):
raise Exception(f"Wallet at path '{wallet_path}' already exists, use 'wallet_add' to load wallet.")
wallet = await Wallet.create(
self.ledger, self.db, wallet_path, name,
create_account, language, single_key
)
return self.add(wallet)