ui: Improved wallets page.

This commit is contained in:
tecnovert 2021-10-14 22:17:37 +02:00
parent 4475e5b643
commit 062283c31a
No known key found for this signature in database
GPG key ID: 8ED6D8750C4E3F93
6 changed files with 130 additions and 4 deletions

View file

@ -1,3 +1,3 @@
name = "basicswap"
__version__ = "0.0.21"
__version__ = "0.0.22"

View file

@ -6,6 +6,7 @@
import os
import re
import sys
import zmq
import json
import time
@ -20,6 +21,7 @@ import threading
import traceback
import sqlalchemy as sa
import collections
import concurrent.futures
from enum import IntEnum, auto
from sqlalchemy.orm import sessionmaker, scoped_session
@ -83,6 +85,7 @@ from .db import (
XmrOffer,
XmrSwap,
XmrSplitData,
Wallets,
)
from .base import BaseApp
from .explorers import (
@ -462,6 +465,8 @@ class BasicSwap(BaseApp):
self._last_checked_events = 0
self._last_checked_xmr_swaps = 0
self._possibly_revoked_offers = collections.deque([], maxlen=48) # TODO: improve
self._updating_wallets_info = {}
self._last_updated_wallets_info = 0
# TODO: Adjust ranges
self.min_delay_event = self.settings.get('min_delay_event', 10)
@ -480,6 +485,7 @@ class BasicSwap(BaseApp):
self.SMSG_SECONDS_IN_HOUR = 60 * 60 # Note: Set smsgsregtestadjust=0 for regtest
self.threads = []
self.thread_pool = concurrent.futures.ThreadPoolExecutor(max_workers=4, thread_name_prefix='bsp')
# Encode key to match network
wif_prefix = chainparams[Coins.PART][self.chain]['key_prefix']
@ -573,6 +579,11 @@ class BasicSwap(BaseApp):
for t in self.threads:
t.join()
if sys.version_info[1] >= 9:
self.thread_pool.shutdown(cancel_futures=True)
else:
self.thread_pool.shutdown()
close_all_sessions()
self.engine.dispose()
@ -828,6 +839,9 @@ class BasicSwap(BaseApp):
created_at BIGINT,
PRIMARY KEY (record_id))''')
db_version += 1
elif current_version == 9:
session.execute('ALTER TABLE wallets ADD COLUMN wallet_data VARCHAR')
db_version += 1
if current_version != db_version:
self.db_version = db_version
@ -5093,6 +5107,46 @@ class BasicSwap(BaseApp):
return rv
def updateWalletInfo(self, coin):
wi = self.getWalletInfo(coin)
# Store wallet info to db so it's available after startup
self.mxDB.acquire()
try:
rv = []
now = int(time.time())
session = scoped_session(self.session_factory)
session.add(Wallets(coin_id=coin, wallet_data=json.dumps(wi), created_at=now))
coin_id = int(coin)
query_str = f'DELETE FROM wallets WHERE coin_id = {coin_id} AND record_id NOT IN (SELECT record_id FROM wallets WHERE coin_id = {coin_id} ORDER BY created_at DESC LIMIT 3 )'
session.execute(query_str)
session.commit()
except Exception as e:
self.log.error(f'updateWalletInfo {e}')
finally:
session.close()
session.remove()
self._updating_wallets_info[int(coin)] = False
self.mxDB.release()
def updateWalletsInfo(self, force_update=False, only_coin=None):
now = int(time.time())
if not force_update and now - self._last_updated_wallets_info < 30:
return
for c in Coins:
if only_coin is not None and c != only_coin:
continue
if c not in chainparams:
continue
if self.coin_clients[c]['connection_type'] == 'rpc':
self._updating_wallets_info[int(c)] = True
self.thread_pool.submit(self.updateWalletInfo, c)
if only_coin is None:
self._last_updated_wallets_info = int(time.time())
def getWalletsInfo(self, opts=None):
rv = {}
for c in Coins:
@ -5105,6 +5159,44 @@ class BasicSwap(BaseApp):
rv[c] = {'name': chainparams[c]['name'].capitalize(), 'error': str(ex)}
return rv
def getCachedWalletsInfo(self, opts=None):
rv = {}
# Requires? self.mxDB.acquire()
try:
session = scoped_session(self.session_factory)
inner_str = 'SELECT coin_id, MAX(created_at) as max_created_at FROM wallets GROUP BY coin_id'
query_str = 'SELECT a.coin_id, wallet_data, created_at FROM wallets a, ({}) b WHERE a.coin_id = b.coin_id AND a.created_at = b.max_created_at'.format(inner_str)
q = session.execute(query_str)
for row in q:
coin_id = row[0]
wallet_data = json.loads(row[1])
wallet_data['lastupdated'] = row[2]
wallet_data['updating'] = self._updating_wallets_info.get(coin_id, False)
# Ensure the latest deposit address is displayed
q = session.execute('SELECT value FROM kv_string WHERE key = "receive_addr_{}"'.format(chainparams[coin_id]['name']))
for row in q:
wallet_data['deposit_address'] = row[0]
rv[coin_id] = wallet_data
finally:
session.close()
session.remove()
for c in Coins:
if c not in chainparams:
continue
if self.coin_clients[c]['connection_type'] == 'rpc':
coin_id = int(c)
if coin_id not in rv:
rv[coin_id] = {
'name': chainparams[c]['name'].capitalize(),
'updating': self._updating_wallets_info.get(coin_id, False),
}
return rv
def countAcceptedBids(self, offer_id=None):
self.mxDB.acquire()
try:

View file

@ -12,7 +12,7 @@ from enum import IntEnum, auto
from sqlalchemy.ext.declarative import declarative_base
CURRENT_DB_VERSION = 9
CURRENT_DB_VERSION = 10
Base = declarative_base()
@ -360,6 +360,7 @@ class Wallets(Base):
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
coin_id = sa.Column(sa.Integer)
wallet_name = sa.Column(sa.String)
wallet_data = sa.Column(sa.String)
balance_type = sa.Column(sa.Integer)
amount = sa.Column(sa.BigInteger)
updated_at = sa.Column(sa.BigInteger)

View file

@ -279,11 +279,16 @@ class HttpHandler(BaseHTTPRequestHandler):
messages.append('Withdrew {} {} to address {}<br/>In txid: {}'.format(value, ticker, address, txid))
except Exception as e:
messages.append('Error: {}'.format(str(e)))
swap_client.updateWalletsInfo(True, c)
wallets = swap_client.getWalletsInfo()
swap_client.updateWalletsInfo()
wallets = swap_client.getCachedWalletsInfo()
wallets_formatted = []
for k, w in wallets.items():
sk = sorted(wallets.keys())
for k in sk:
w = wallets[k]
if 'error' in w:
wallets_formatted.append({
'cid': str(int(k)),
@ -291,6 +296,14 @@ class HttpHandler(BaseHTTPRequestHandler):
})
continue
if 'balance' not in w:
wallets_formatted.append({
'name': w['name'],
'havedata': False,
'updating': w['updating'],
})
continue
ci = swap_client.ci(k)
fee_rate, fee_src = swap_client.getFeeRateForCoin(k)
est_fee = swap_client.estimateWithdrawFee(k, fee_rate)
@ -308,11 +321,16 @@ class HttpHandler(BaseHTTPRequestHandler):
'deposit_address': w['deposit_address'],
'expected_seed': w['expected_seed'],
'balance_all': float(w['balance']) + float(w['unconfirmed']),
'updating': w['updating'],
'lastupdated': format_timestamp(w['lastupdated']),
'havedata': True,
}
if float(w['unconfirmed']) > 0.0:
wf['unconfirmed'] = w['unconfirmed']
if k == Coins.PART:
wf['stealth_address'] = w['stealth_address']
wf['blind_balance'] = w['blind_balance']
if float(w['blind_unconfirmed']) > 0.0:
wf['blind_unconfirmed'] = w['blind_unconfirmed']

View file

@ -1,5 +1,7 @@
{% include 'header.html' %}
<p><a href="/wallets">refresh</a></p>
<h3>Wallets</h3>
{% if refresh %}
<p>Page Refresh: {{ refresh }} seconds</p>
@ -13,10 +15,15 @@
{% for w in wallets %}
<h4>{{ w.name }} {{ w.version }}</h4>
{% if w.updating %}
<h5>Updating</h5>
{% endif %}
{% if w.havedata %}
{% if w.error %}
<p>Error: {{ w.error }}</p>
{% else %}
<table>
<tr><td>Last updated:</td><td>{{ w.lastupdated }}</td></tr>
<tr><td>Balance:</td><td>{{ w.balance }}</td>{% if w.unconfirmed %}<td>Unconfirmed:</td><td>{{ w.unconfirmed }}</td>{% endif %}</tr>
@ -50,6 +57,7 @@
<tr><td>Fee Rate:</td><td>{{ w.fee_rate }}</td><td>Est Fee:</td><td>{{ w.est_fee }}</td></tr>
</table>
{% endif %}
{% endif %} <!-- havedata -->
{% endfor %}
<input type="hidden" name="formid" value="{{ form_id }}">

View file

@ -1,3 +1,10 @@
0.0.22
==============
- Improved wallets page
- Consistent wallet order
- Separated RPC calls into threads.
0.0.21
==============
- Raised Particl and Monero daemon versions.