refactored get_transactions and get_txos
This commit is contained in:
parent
0960762694
commit
356ab9666f
3 changed files with 64 additions and 65 deletions
|
@ -7,6 +7,7 @@ from twisted.enterprise import adbapi
|
||||||
|
|
||||||
from torba.hash import TXRefImmutable
|
from torba.hash import TXRefImmutable
|
||||||
from torba.basetransaction import BaseTransaction, TXORefResolvable
|
from torba.basetransaction import BaseTransaction, TXORefResolvable
|
||||||
|
from torba.baseaccount import BaseAccount
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -15,6 +16,13 @@ def clean_arg_name(arg):
|
||||||
return arg.replace('.', '_')
|
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=''):
|
def constraints_to_sql(constraints, joiner=' AND ', prepend_sql=' AND ', prepend_key=''):
|
||||||
if not constraints:
|
if not constraints:
|
||||||
return ''
|
return ''
|
||||||
|
@ -36,7 +44,7 @@ def constraints_to_sql(constraints, joiner=' AND ', prepend_sql=' AND ', prepend
|
||||||
col, op = key[:-len('__in')], 'IN'
|
col, op = key[:-len('__in')], 'IN'
|
||||||
else:
|
else:
|
||||||
col, op = key[:-len('__not_in')], 'NOT IN'
|
col, op = key[:-len('__not_in')], 'NOT IN'
|
||||||
if isinstance(constraint, list):
|
if isinstance(constraint, (list, set)):
|
||||||
placeholders = []
|
placeholders = []
|
||||||
for item_no, item in enumerate(constraint, 1):
|
for item_no, item in enumerate(constraint, 1):
|
||||||
constraints['{}_{}'.format(clean_arg_name(col), item_no)] = item
|
constraints['{}_{}'.format(clean_arg_name(col), item_no)] = item
|
||||||
|
@ -288,38 +296,34 @@ class BaseDatabase(SQLiteMixin):
|
||||||
return txs[0]
|
return txs[0]
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@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 = ""
|
if 'txid' not in constraints and account is not None:
|
||||||
account_id = account.public_key.address if account is not None else 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:
|
where = constraints_to_sql(constraints, prepend_sql='WHERE ')
|
||||||
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
|
|
||||||
)
|
|
||||||
"""
|
|
||||||
|
|
||||||
tx_rows = yield self.run_query(
|
tx_rows = yield self.run_query(
|
||||||
"""
|
"""
|
||||||
SELECT txid, raw, height, position, is_verified FROM tx {}
|
SELECT txid, raw, height, position, is_verified FROM tx {}
|
||||||
ORDER BY height DESC, position DESC LIMIT :offset, :limit
|
ORDER BY height DESC, position DESC LIMIT :offset, :limit
|
||||||
""".format(tx_where), {
|
""".format(where), {
|
||||||
'account': account_id,
|
**constraints,
|
||||||
'txid': txid,
|
'account': account,
|
||||||
'offset': min(offset, 0),
|
'offset': max(offset, 0),
|
||||||
'limit': max(limit, 100)
|
'limit': max(limit, 1)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
txids, txs = [], []
|
txids, txs = [], []
|
||||||
for row in tx_rows:
|
for row in tx_rows:
|
||||||
txids.append(row[0])
|
txids.append(row[0])
|
||||||
|
@ -327,49 +331,39 @@ class BaseDatabase(SQLiteMixin):
|
||||||
raw=row[1], height=row[2], position=row[3], is_verified=row[4]
|
raw=row[1], height=row[2], position=row[3], is_verified=row[4]
|
||||||
))
|
))
|
||||||
|
|
||||||
txo_rows = yield self.run_query(
|
annotated_txos = {
|
||||||
"""
|
txo.id: txo for txo in
|
||||||
SELECT txoid, chain, account
|
(yield self.get_txos(
|
||||||
FROM txo JOIN pubkey_address USING (address)
|
my_account=my_account,
|
||||||
WHERE txid IN ({})
|
txid__in=txids
|
||||||
""".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
|
|
||||||
}
|
|
||||||
|
|
||||||
referenced_txos = yield self.get_txos(
|
referenced_txos = {
|
||||||
account=account,
|
txo.id: txo for txo in
|
||||||
txoid__in="SELECT txoid FROM txi WHERE txi.txid IN ({})".format(
|
(yield self.get_txos(
|
||||||
','.join("'{}'".format(txid) for txid in txids)
|
my_account=my_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}
|
)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
for tx in txs:
|
for tx in txs:
|
||||||
for txi in tx.inputs:
|
for txi in tx.inputs:
|
||||||
if txi.txo_ref.id in referenced_txos_map:
|
txo = referenced_txos.get(txi.txo_ref.id)
|
||||||
txi.txo_ref = TXORefResolvable(referenced_txos_map[txi.txo_ref.id])
|
if txo:
|
||||||
|
txi.txo_ref = txo.ref
|
||||||
for txo in tx.outputs:
|
for txo in tx.outputs:
|
||||||
txo_meta = txos.get(txo.id)
|
_txo = annotated_txos.get(txo.id)
|
||||||
if txo_meta is not None:
|
if _txo:
|
||||||
txo.is_change = txo_meta['is_change']
|
txo.update_annotations(_txo)
|
||||||
txo.is_my_account = txo_meta['is_my_account']
|
|
||||||
else:
|
|
||||||
txo.is_change = False
|
|
||||||
txo.is_my_account = False
|
|
||||||
|
|
||||||
return txs
|
return txs
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def get_txos(self, account=None, **constraints):
|
def get_txos(self, **constraints):
|
||||||
account_id = None
|
my_account = prepare_constraints(constraints)
|
||||||
if account is not None:
|
|
||||||
account_id = account.public_key.address
|
|
||||||
constraints['account'] = account_id
|
|
||||||
rows = yield self.run_query(
|
rows = yield self.run_query(
|
||||||
"""
|
"""
|
||||||
SELECT amount, script, txid, txo.position, chain, account
|
SELECT amount, script, txid, txo.position, chain, account
|
||||||
|
@ -384,18 +378,18 @@ class BaseDatabase(SQLiteMixin):
|
||||||
tx_ref=TXRefImmutable.from_id(row[2]),
|
tx_ref=TXRefImmutable.from_id(row[2]),
|
||||||
position=row[3],
|
position=row[3],
|
||||||
is_change=row[4] == 1,
|
is_change=row[4] == 1,
|
||||||
is_my_account=row[5] == account_id
|
is_my_account=row[5] == my_account
|
||||||
) for row in rows
|
) for row in rows
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_utxos(self, **constraints):
|
def get_utxos(self, **constraints):
|
||||||
constraints['txoid__not_in'] = 'SELECT txoid FROM txi'
|
constraints['txoid__not_in'] = 'SELECT txoid FROM txi'
|
||||||
constraints['is_reserved'] = 0
|
constraints['is_reserved'] = False
|
||||||
return self.get_txos(**constraints)
|
return self.get_txos(**constraints)
|
||||||
|
|
||||||
def get_balance_for_account(self, account, include_reserved=False, **constraints):
|
def get_balance_for_account(self, account, include_reserved=False, **constraints):
|
||||||
if not include_reserved:
|
if not include_reserved:
|
||||||
constraints['is_reserved'] = 0
|
constraints['is_reserved'] = False
|
||||||
values = {'account': account.public_key.address}
|
values = {'account': account.public_key.address}
|
||||||
values.update(constraints)
|
values.update(constraints)
|
||||||
return self.query_one_value(
|
return self.query_one_value(
|
||||||
|
|
|
@ -133,11 +133,8 @@ class BaseLedger(metaclass=LedgerRegistry):
|
||||||
def add_account(self, account: baseaccount.BaseAccount):
|
def add_account(self, account: baseaccount.BaseAccount):
|
||||||
self.accounts.append(account)
|
self.accounts.append(account)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
def get_transaction(self, txhash):
|
def get_transaction(self, txhash):
|
||||||
raw, _, _, _ = yield self.db.get_transaction(txhash)
|
return self.db.get_transaction(txhash)
|
||||||
if raw is not None:
|
|
||||||
return self.transaction_class(raw)
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def get_private_key_for_address(self, address):
|
def get_private_key_for_address(self, address):
|
||||||
|
|
|
@ -200,6 +200,14 @@ class BaseOutput(InputOutput):
|
||||||
self.is_change = is_change
|
self.is_change = is_change
|
||||||
self.is_my_account = is_my_account
|
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
|
@property
|
||||||
def ref(self):
|
def ref(self):
|
||||||
return TXORefResolvable(self)
|
return TXORefResolvable(self)
|
||||||
|
|
Loading…
Reference in a new issue