+ get_detailed_accounts

This commit is contained in:
Lex Berezhny 2018-08-30 11:50:11 -04:00
parent 00cf8131ec
commit 9e55694512
4 changed files with 75 additions and 42 deletions

View file

@ -5,6 +5,7 @@ from twisted.internet import defer
from torba.mnemonic import Mnemonic
from torba.bip32 import PrivateKey, PubKey, from_extended_key_string
from torba.hash import double_sha256, aes_encrypt, aes_decrypt
from torba.constants import COIN
if typing.TYPE_CHECKING:
from torba import baseledger
@ -65,15 +66,15 @@ class AddressManager:
@defer.inlineCallbacks
def get_addresses(self, limit: int = None, only_usable: bool = False) -> defer.Deferred:
records = yield self.get_address_records(limit=limit, only_usable=only_usable)
defer.returnValue([r['address'] for r in records])
return [r['address'] for r in records]
@defer.inlineCallbacks
def get_or_create_usable_address(self) -> defer.Deferred:
addresses = yield self.get_addresses(limit=1, only_usable=True)
if addresses:
defer.returnValue(addresses[0])
return addresses[0]
addresses = yield self.ensure_address_gap()
defer.returnValue(addresses[0])
return addresses[0]
class HierarchicalDeterministic(AddressManager):
@ -109,7 +110,7 @@ class HierarchicalDeterministic(AddressManager):
yield self.db.add_keys(
self.account, self.chain_number, new_keys
)
defer.returnValue([key[1].address for key in new_keys])
return [key[1].address for key in new_keys]
@defer.inlineCallbacks
def get_max_gap(self) -> defer.Deferred:
@ -122,7 +123,7 @@ class HierarchicalDeterministic(AddressManager):
else:
max_gap = max(max_gap, current_gap)
current_gap = 0
defer.returnValue(max_gap)
return max_gap
@defer.inlineCallbacks
def ensure_address_gap(self) -> defer.Deferred:
@ -136,12 +137,12 @@ class HierarchicalDeterministic(AddressManager):
break
if existing_gap == self.gap:
defer.returnValue([])
return []
start = addresses[0]['position']+1 if addresses else 0
end = start + (self.gap - existing_gap)
new_keys = yield self.generate_keys(start, end-1)
defer.returnValue(new_keys)
return new_keys
def get_address_records(self, limit: int = None, only_usable: bool = False):
return self._query_addresses(
@ -179,8 +180,8 @@ class SingleKey(AddressManager):
yield self.db.add_keys(
self.account, self.chain_number, [(0, self.public_key)]
)
defer.returnValue([self.public_key.address])
defer.returnValue([])
return [self.public_key.address]
return []
def get_address_records(self, limit: int = None, only_usable: bool = False) -> defer.Deferred:
return self._query_addresses()
@ -201,6 +202,7 @@ class BaseAccount:
address_generator: dict) -> None:
self.ledger = ledger
self.wallet = wallet
self.id = public_key.address
self.name = name
self.seed = seed
self.encrypted = encrypted
@ -271,6 +273,22 @@ class BaseAccount:
'address_generator': self.address_generator.to_dict(self.receiving, self.change)
}
@defer.inlineCallbacks
def get_details(self, show_seed=False, **kwargs):
satoshis = yield self.get_balance(**kwargs)
details = {
'id': self.id,
'name': self.name,
'coins': round(satoshis/COIN, 2),
'satoshis': satoshis,
'encrypted': self.encrypted,
'public_key': self.public_key.extended_key_string(),
'address_generator': self.address_generator.to_dict(self.receiving, self.change)
}
if show_seed:
details['seed'] = self.seed
return details
def decrypt(self, password):
assert self.encrypted, "Key is not encrypted."
secret = double_sha256(password)
@ -291,12 +309,12 @@ class BaseAccount:
for address_manager in self.address_managers:
new_addresses = yield address_manager.ensure_address_gap()
addresses.extend(new_addresses)
defer.returnValue(addresses)
return addresses
@defer.inlineCallbacks
def get_addresses(self, limit: int = None, max_used_times: int = None) -> defer.Deferred:
records = yield self.get_address_records(limit, max_used_times)
defer.returnValue([r['address'] for r in records])
return [r['address'] for r in records]
def get_address_records(self, limit: int = None, max_used_times: int = None) -> defer.Deferred:
return self.ledger.db.get_addresses(self, None, limit, max_used_times)
@ -316,14 +334,17 @@ class BaseAccount:
def get_max_gap(self):
change_gap = yield self.change.get_max_gap()
receiving_gap = yield self.receiving.get_max_gap()
defer.returnValue({
return {
'max_change_gap': change_gap,
'max_receiving_gap': receiving_gap,
})
}
def get_unspent_outputs(self, **constraints):
return self.ledger.db.get_utxos_for_account(self, **constraints)
def get_inputs_outputs(self, **constraints):
return self.ledger.db.get_txios_for_account(self, **constraints)
@defer.inlineCallbacks
def fund(self, to_account, amount=None, everything=False,
outputs=1, broadcast=False, **constraints):
@ -360,4 +381,4 @@ class BaseAccount:
[txi.txo_ref.txo for txi in tx.inputs]
)
defer.returnValue(tx)
return tx

View file

@ -86,25 +86,25 @@ class SQLiteMixin:
def query_one_value(self, query, params=None, default=None):
result = yield self.run_query(query, params)
if result:
defer.returnValue(result[0][0] or default)
return result[0][0] or default
else:
defer.returnValue(default)
return default
@defer.inlineCallbacks
def query_dict_value_list(self, query, fields, params=None):
result = yield self.run_query(query.format(', '.join(fields)), params)
if result:
defer.returnValue([dict(zip(fields, r)) for r in result])
return [dict(zip(fields, r)) for r in result]
else:
defer.returnValue([])
return []
@defer.inlineCallbacks
def query_dict_value(self, query, fields, params=None, default=None):
result = yield self.query_dict_value_list(query, fields, params)
if result:
defer.returnValue(result[0])
return result[0]
else:
defer.returnValue(default)
return default
@staticmethod
def execute(t, sql, values):
@ -262,9 +262,9 @@ class BaseDatabase(SQLiteMixin):
"SELECT raw, height, is_verified FROM tx WHERE txid = ?", (txid,)
)
if result:
defer.returnValue(result[0])
return result[0]
else:
defer.returnValue((None, None, False))
return None, None, False
def get_balance_for_account(self, account, include_reserved=False, **constraints):
if not include_reserved:
@ -294,14 +294,34 @@ class BaseDatabase(SQLiteMixin):
"""+constraints_to_sql(constraints), constraints
)
output_class = account.ledger.transaction_class.output_class
defer.returnValue([
return [
output_class(
values[0],
output_class.script_class(values[1]),
TXRefImmutable.from_id(values[2]),
position=values[3]
) for values in utxos
])
]
@defer.inlineCallbacks
def get_txios_for_account(self, account, **constraints):
constraints['account'] = account.public_key.address
utxos = yield self.run_query(
"""
SELECT amount, script, txid, txo.position
FROM txo JOIN pubkey_address ON pubkey_address.address=txo.address
WHERE account=:account AND txo.is_reserved=0 AND txoid NOT IN (SELECT txoid FROM txi)
"""+constraints_to_sql(constraints), constraints
)
output_class = account.ledger.transaction_class.output_class
return [
output_class(
values[0],
output_class.script_class(values[1]),
TXRefImmutable.from_id(values[2]),
position=values[3]
) for values in utxos
]
def add_keys(self, account, chain, keys):
sql = (

View file

@ -129,11 +129,8 @@ class BaseLedger(metaclass=LedgerRegistry):
def path(self):
return os.path.join(self.config['data_path'], self.get_id())
@defer.inlineCallbacks
def add_account(self, account: baseaccount.BaseAccount) -> defer.Deferred:
def add_account(self, account: baseaccount.BaseAccount):
self.accounts.append(account)
if self.network.is_connected:
yield self.update_account(account)
@defer.inlineCallbacks
def get_transaction(self, txhash):

View file

@ -43,20 +43,15 @@ class BaseWalletManager:
return wallet
@defer.inlineCallbacks
def get_balances(self, confirmations=6):
balances = {}
for i, ledger in enumerate(self.ledgers.values()):
ledger_balances = balances[ledger.get_id()] = []
for j, account in enumerate(ledger.accounts):
satoshis = yield account.get_balance(confirmations)
ledger_balances.append({
'account': account.name,
'coins': round(satoshis/COIN, 2),
'satoshis': satoshis,
'is_default_account': i == j == 0,
'id': account.public_key.address
})
defer.returnValue(balances)
def get_detailed_accounts(self, confirmations=6, show_seed=False):
ledgers = {}
for i, account in enumerate(self.accounts):
details = yield account.get_details(confirmations=confirmations, show_seed=True)
details['is_default_account'] = i == 0
ledger_id = account.ledger.get_id()
ledgers.setdefault(ledger_id, [])
ledgers[ledger_id].append(details)
return ledgers
@property
def default_wallet(self):