Browse Source

wallet.get_full_history: more consistent sort order

before:
```
>>> [print(wallet.get_tx_status(txid, wallet.adb.get_tx_height(txid))) for txid in list(wallet.get_full_history())[-10:]]
(7, '2023-04-04 16:13')
(7, '2023-04-04 16:13')
(7, '2023-04-04 16:13')
(7, '2023-04-04 16:13')
(0, 'Unconfirmed [20. sat/b, 0.00 MB]')
(2, 'in 2 blocks')
(3, 'Local [180.4 sat/b]')
(3, 'Local [180.2 sat/b]')
(2, 'in 2016 blocks')
(0, 'Unconfirmed [180. sat/b, 0.00 MB]')
```

after:
```
>>> [print(wallet.get_tx_status(txid, wallet.adb.get_tx_height(txid))) for txid in list(wallet.get_full_history())[-10:]]
(7, '2023-04-04 16:13')
(7, '2023-04-04 16:13')
(7, '2023-04-04 16:13')
(7, '2023-04-04 16:13')
(0, 'Unconfirmed [20. sat/b, 0.00 MB]')
(0, 'Unconfirmed [180. sat/b, 0.00 MB]')
(2, 'in 2016 blocks')
(2, 'in 2 blocks')
(3, 'Local [180.4 sat/b]')
(3, 'Local [180.2 sat/b]')
```
master
SomberNight 3 years ago
parent
commit
db4943ff86
No known key found for this signature in database
GPG Key ID: B33B5F232C6271E9
  1. 48
      electrum/address_synchronizer.py
  2. 1
      electrum/gui/qml/qetransactionlistmodel.py
  3. 4
      electrum/lnworker.py
  4. 12
      electrum/wallet.py

48
electrum/address_synchronizer.py

@ -50,6 +50,9 @@ TX_HEIGHT_LOCAL = -2
TX_HEIGHT_UNCONF_PARENT = -1
TX_HEIGHT_UNCONFIRMED = 0
TX_TIMESTAMP_INF = 999_999_999_999
TX_HEIGHT_INF = 10 ** 9
class HistoryItem(NamedTuple):
txid: str
@ -476,28 +479,29 @@ class AddressSynchronizer(Logger, EventListener):
self._history_local.clear()
self._get_balance_cache.clear() # invalidate cache
def _get_txpos(self, tx_hash: str) -> Tuple[int, int]:
"""Returns (height, txpos) tuple, even if the tx is unverified.
If txpos is -1, height should only be used for sorting purposes.
"""
def _get_tx_sort_key(self, tx_hash: str) -> Tuple[int, int]:
"""Returns a key to be used for sorting txs."""
with self.lock:
verified_tx_mined_info = self.db.get_verified_tx(tx_hash)
if verified_tx_mined_info:
height = verified_tx_mined_info.height
txpos = verified_tx_mined_info.txpos
assert height > 0, height
assert txpos is not None
return height, txpos
elif tx_hash in self.unverified_tx:
height = self.unverified_tx[tx_hash]
assert height > 0, height
return height, -1
elif tx_hash in self.unconfirmed_tx:
height = self.unconfirmed_tx[tx_hash]
assert height <= 0, height
return (10**9 - height), -1
else:
return (10**9 + 1), -1
tx_mined_info = self.get_tx_height(tx_hash)
height = self.tx_height_to_sort_height(tx_mined_info.height)
txpos = tx_mined_info.txpos or -1
return height, txpos
@classmethod
def tx_height_to_sort_height(cls, height: int = None):
"""Return a height-like value to be used for sorting txs."""
if height is not None:
if height > 0:
return height
if height == TX_HEIGHT_UNCONFIRMED:
return TX_HEIGHT_INF
if height == TX_HEIGHT_UNCONF_PARENT:
return TX_HEIGHT_INF + 1
if height == TX_HEIGHT_FUTURE:
return TX_HEIGHT_INF + 2
if height == TX_HEIGHT_LOCAL:
return TX_HEIGHT_INF + 3
return TX_HEIGHT_INF + 100
def with_local_height_cached(func):
# get local height only once, as it's relatively expensive.
@ -530,7 +534,7 @@ class AddressSynchronizer(Logger, EventListener):
tx_mined_status = self.get_tx_height(tx_hash)
fee = self.get_tx_fee(tx_hash)
history.append((tx_hash, tx_mined_status, delta, fee))
history.sort(key = lambda x: self._get_txpos(x[0]))
history.sort(key = lambda x: self._get_tx_sort_key(x[0]))
# 3. add balance
h2 = []
balance = 0

1
electrum/gui/qml/qetransactionlistmodel.py

@ -219,6 +219,7 @@ class QETransactionListModel(QAbstractListModel, QtEventListener):
txinfo = self.wallet.get_tx_info(tx)
status, status_str = self.wallet.get_tx_status(txid, txinfo.tx_mined_status)
tx_item['date'] = status_str
# note: if the height changes, that might affect the history order, but we won't re-sort now.
tx_item['height'] = self.wallet.adb.get_tx_height(txid).height
index = self.index(tx_item_idx, 0)
roles = [self._ROLE_RMAP[x] for x in ['height', 'date']]

4
electrum/lnworker.py

@ -73,7 +73,7 @@ from .lnmsg import decode_msg
from .i18n import _
from .lnrouter import (RouteEdge, LNPaymentRoute, LNPaymentPath, is_route_sane_to_use,
NoChannelPolicy, LNPathInconsistent)
from .address_synchronizer import TX_HEIGHT_LOCAL
from .address_synchronizer import TX_HEIGHT_LOCAL, TX_TIMESTAMP_INF
from . import lnsweep
from .lnwatcher import LNWalletWatcher
from .crypto import pw_encode_with_version_and_mac, pw_decode_with_version_and_mac
@ -900,6 +900,7 @@ class LNWallet(LNWorker):
'amount_msat': chan.balance(LOCAL, ctn=0),
'direction': PaymentDirection.RECEIVED,
'timestamp': tx_height.timestamp,
'monotonic_timestamp': tx_height.timestamp or TX_TIMESTAMP_INF,
'date': timestamp_to_datetime(tx_height.timestamp),
'fee_sat': None,
'fee_msat': None,
@ -922,6 +923,7 @@ class LNWallet(LNWorker):
'amount_msat': -chan.balance_minus_outgoing_htlcs(LOCAL),
'direction': PaymentDirection.SENT,
'timestamp': tx_height.timestamp,
'monotonic_timestamp': tx_height.timestamp or TX_TIMESTAMP_INF,
'date': timestamp_to_datetime(tx_height.timestamp),
'fee_sat': None,
'fee_msat': None,

12
electrum/wallet.py

@ -73,7 +73,7 @@ from .transaction import (Transaction, TxInput, UnknownTxinType, TxOutput,
PartialTransaction, PartialTxInput, PartialTxOutput, TxOutpoint)
from .plugin import run_hook
from .address_synchronizer import (AddressSynchronizer, TX_HEIGHT_LOCAL,
TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_FUTURE)
TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_FUTURE, TX_TIMESTAMP_INF)
from .invoices import BaseInvoice, Invoice, Request
from .invoices import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED, PR_UNCONFIRMED, PR_INFLIGHT
from .contacts import Contacts
@ -1011,7 +1011,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
domain = self.get_addresses()
monotonic_timestamp = 0
for hist_item in self.adb.get_history(domain=domain):
monotonic_timestamp = max(monotonic_timestamp, (hist_item.tx_mined_status.timestamp or 999_999_999_999))
monotonic_timestamp = max(monotonic_timestamp, (hist_item.tx_mined_status.timestamp or TX_TIMESTAMP_INF))
d = {
'txid': hist_item.txid,
'fee_sat': hist_item.fee,
@ -1241,9 +1241,13 @@ class Abstract_Wallet(ABC, Logger, EventListener):
transactions_tmp[key] = tx_item
# sort on-chain and LN stuff into new dict, by timestamp
# (we rely on this being a *stable* sort)
def sort_key(x):
txid, tx_item = x
ts = tx_item.get('monotonic_timestamp') or tx_item.get('timestamp') or float('inf')
height = self.adb.tx_height_to_sort_height(tx_item.get('height'))
return ts, height
transactions = OrderedDictWithIndex()
for k, v in sorted(list(transactions_tmp.items()),
key=lambda x: x[1].get('monotonic_timestamp') or x[1].get('timestamp') or float('inf')):
for k, v in sorted(list(transactions_tmp.items()), key=sort_key):
transactions[k] = v
now = time.time()
balance = 0

Loading…
Cancel
Save