refactored get_transactions and get_txos

This commit is contained in:
Lex Berezhny 2018-10-04 19:27:39 -04:00
parent 0960762694
commit 356ab9666f
3 changed files with 64 additions and 65 deletions

View file

@ -7,6 +7,7 @@ from twisted.enterprise import adbapi
from torba.hash import TXRefImmutable
from torba.basetransaction import BaseTransaction, TXORefResolvable
from torba.baseaccount import BaseAccount
log = logging.getLogger(__name__)
@ -15,6 +16,13 @@ def clean_arg_name(arg):
return arg.replace('.', '_')
def prepare_constraints(constraints):
if 'account' in constraints:
if isinstance(constraints['account'], BaseAccount):
constraints['account'] = constraints['account'].public_key.address
return constraints.pop('my_account', constraints.get('account'))
def constraints_to_sql(constraints, joiner=' AND ', prepend_sql=' AND ', prepend_key=''):
if not constraints:
return ''
@ -36,7 +44,7 @@ def constraints_to_sql(constraints, joiner=' AND ', prepend_sql=' AND ', prepend
col, op = key[:-len('__in')], 'IN'
else:
col, op = key[:-len('__not_in')], 'NOT IN'
if isinstance(constraint, list):
if isinstance(constraint, (list, set)):
placeholders = []
for item_no, item in enumerate(constraint, 1):
constraints['{}_{}'.format(clean_arg_name(col), item_no)] = item
@ -288,38 +296,34 @@ class BaseDatabase(SQLiteMixin):
return txs[0]
@defer.inlineCallbacks
def get_transactions(self, account=None, txid=None, offset=0, limit=1000):
def get_transactions(self, offset=0, limit=1000000, **constraints):
my_account = prepare_constraints(constraints)
account = constraints.pop('account', None)
tx_where = ""
account_id = account.public_key.address if account is not None else None
if 'txid' not in constraints and account is not None:
constraints['txid__in'] = """
SELECT txo.txid FROM txo
JOIN pubkey_address USING (address) WHERE pubkey_address.account = :account
UNION
SELECT txi.txid FROM txi
JOIN txo USING (txoid)
JOIN pubkey_address USING (address) WHERE pubkey_address.account = :account
"""
if txid is not None:
tx_where = """
WHERE txid = :txid
"""
elif account is not None:
tx_where = """
WHERE txid IN (
SELECT txo.txid FROM txo
JOIN pubkey_address USING (address) WHERE pubkey_address.account = :account
UNION
SELECT txi.txid FROM txi
JOIN txo USING (txoid)
JOIN pubkey_address USING (address) WHERE pubkey_address.account = :account
)
"""
where = constraints_to_sql(constraints, prepend_sql='WHERE ')
tx_rows = yield self.run_query(
"""
SELECT txid, raw, height, position, is_verified FROM tx {}
ORDER BY height DESC, position DESC LIMIT :offset, :limit
""".format(tx_where), {
'account': account_id,
'txid': txid,
'offset': min(offset, 0),
'limit': max(limit, 100)
""".format(where), {
**constraints,
'account': account,
'offset': max(offset, 0),
'limit': max(limit, 1)
}
)
txids, txs = [], []
for row in tx_rows:
txids.append(row[0])
@ -327,49 +331,39 @@ class BaseDatabase(SQLiteMixin):
raw=row[1], height=row[2], position=row[3], is_verified=row[4]
))
txo_rows = yield self.run_query(
"""
SELECT txoid, chain, account
FROM txo JOIN pubkey_address USING (address)
WHERE txid IN ({})
""".format(', '.join(['?']*len(txids))), txids
)
txos = {}
for row in txo_rows:
txos[row[0]] = {
'is_change': row[1] == 1,
'is_my_account': row[2] == account_id
}
annotated_txos = {
txo.id: txo for txo in
(yield self.get_txos(
my_account=my_account,
txid__in=txids
))
}
referenced_txos = yield self.get_txos(
account=account,
txoid__in="SELECT txoid FROM txi WHERE txi.txid IN ({})".format(
','.join("'{}'".format(txid) for txid in txids)
)
)
referenced_txos_map = {txo.id: txo for txo in referenced_txos}
referenced_txos = {
txo.id: txo for txo in
(yield self.get_txos(
my_account=my_account,
txoid__in="SELECT txoid FROM txi WHERE txi.txid IN ({})".format(
','.join("'{}'".format(txid) for txid in txids)
)
))
}
for tx in txs:
for txi in tx.inputs:
if txi.txo_ref.id in referenced_txos_map:
txi.txo_ref = TXORefResolvable(referenced_txos_map[txi.txo_ref.id])
txo = referenced_txos.get(txi.txo_ref.id)
if txo:
txi.txo_ref = txo.ref
for txo in tx.outputs:
txo_meta = txos.get(txo.id)
if txo_meta is not None:
txo.is_change = txo_meta['is_change']
txo.is_my_account = txo_meta['is_my_account']
else:
txo.is_change = False
txo.is_my_account = False
_txo = annotated_txos.get(txo.id)
if _txo:
txo.update_annotations(_txo)
return txs
@defer.inlineCallbacks
def get_txos(self, account=None, **constraints):
account_id = None
if account is not None:
account_id = account.public_key.address
constraints['account'] = account_id
def get_txos(self, **constraints):
my_account = prepare_constraints(constraints)
rows = yield self.run_query(
"""
SELECT amount, script, txid, txo.position, chain, account
@ -384,18 +378,18 @@ class BaseDatabase(SQLiteMixin):
tx_ref=TXRefImmutable.from_id(row[2]),
position=row[3],
is_change=row[4] == 1,
is_my_account=row[5] == account_id
is_my_account=row[5] == my_account
) for row in rows
]
def get_utxos(self, **constraints):
constraints['txoid__not_in'] = 'SELECT txoid FROM txi'
constraints['is_reserved'] = 0
constraints['is_reserved'] = False
return self.get_txos(**constraints)
def get_balance_for_account(self, account, include_reserved=False, **constraints):
if not include_reserved:
constraints['is_reserved'] = 0
constraints['is_reserved'] = False
values = {'account': account.public_key.address}
values.update(constraints)
return self.query_one_value(

View file

@ -133,11 +133,8 @@ class BaseLedger(metaclass=LedgerRegistry):
def add_account(self, account: baseaccount.BaseAccount):
self.accounts.append(account)
@defer.inlineCallbacks
def get_transaction(self, txhash):
raw, _, _, _ = yield self.db.get_transaction(txhash)
if raw is not None:
return self.transaction_class(raw)
return self.db.get_transaction(txhash)
@defer.inlineCallbacks
def get_private_key_for_address(self, address):

View file

@ -200,6 +200,14 @@ class BaseOutput(InputOutput):
self.is_change = is_change
self.is_my_account = is_my_account
def update_annotations(self, annotated):
if annotated is None:
self.is_change = False
self.is_my_account = False
else:
self.is_change = annotated.is_change
self.is_my_account = annotated.is_my_account
@property
def ref(self):
return TXORefResolvable(self)