mirror of
https://github.com/basicswap/basicswap.git
synced 2025-01-24 03:15:51 +00:00
ui: Improved wallets page.
This commit is contained in:
parent
4475e5b643
commit
062283c31a
6 changed files with 130 additions and 4 deletions
|
@ -1,3 +1,3 @@
|
||||||
name = "basicswap"
|
name = "basicswap"
|
||||||
|
|
||||||
__version__ = "0.0.21"
|
__version__ = "0.0.22"
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
import zmq
|
import zmq
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
|
@ -20,6 +21,7 @@ import threading
|
||||||
import traceback
|
import traceback
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
import collections
|
import collections
|
||||||
|
import concurrent.futures
|
||||||
|
|
||||||
from enum import IntEnum, auto
|
from enum import IntEnum, auto
|
||||||
from sqlalchemy.orm import sessionmaker, scoped_session
|
from sqlalchemy.orm import sessionmaker, scoped_session
|
||||||
|
@ -83,6 +85,7 @@ from .db import (
|
||||||
XmrOffer,
|
XmrOffer,
|
||||||
XmrSwap,
|
XmrSwap,
|
||||||
XmrSplitData,
|
XmrSplitData,
|
||||||
|
Wallets,
|
||||||
)
|
)
|
||||||
from .base import BaseApp
|
from .base import BaseApp
|
||||||
from .explorers import (
|
from .explorers import (
|
||||||
|
@ -462,6 +465,8 @@ class BasicSwap(BaseApp):
|
||||||
self._last_checked_events = 0
|
self._last_checked_events = 0
|
||||||
self._last_checked_xmr_swaps = 0
|
self._last_checked_xmr_swaps = 0
|
||||||
self._possibly_revoked_offers = collections.deque([], maxlen=48) # TODO: improve
|
self._possibly_revoked_offers = collections.deque([], maxlen=48) # TODO: improve
|
||||||
|
self._updating_wallets_info = {}
|
||||||
|
self._last_updated_wallets_info = 0
|
||||||
|
|
||||||
# TODO: Adjust ranges
|
# TODO: Adjust ranges
|
||||||
self.min_delay_event = self.settings.get('min_delay_event', 10)
|
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.SMSG_SECONDS_IN_HOUR = 60 * 60 # Note: Set smsgsregtestadjust=0 for regtest
|
||||||
|
|
||||||
self.threads = []
|
self.threads = []
|
||||||
|
self.thread_pool = concurrent.futures.ThreadPoolExecutor(max_workers=4, thread_name_prefix='bsp')
|
||||||
|
|
||||||
# Encode key to match network
|
# Encode key to match network
|
||||||
wif_prefix = chainparams[Coins.PART][self.chain]['key_prefix']
|
wif_prefix = chainparams[Coins.PART][self.chain]['key_prefix']
|
||||||
|
@ -573,6 +579,11 @@ class BasicSwap(BaseApp):
|
||||||
for t in self.threads:
|
for t in self.threads:
|
||||||
t.join()
|
t.join()
|
||||||
|
|
||||||
|
if sys.version_info[1] >= 9:
|
||||||
|
self.thread_pool.shutdown(cancel_futures=True)
|
||||||
|
else:
|
||||||
|
self.thread_pool.shutdown()
|
||||||
|
|
||||||
close_all_sessions()
|
close_all_sessions()
|
||||||
self.engine.dispose()
|
self.engine.dispose()
|
||||||
|
|
||||||
|
@ -828,6 +839,9 @@ class BasicSwap(BaseApp):
|
||||||
created_at BIGINT,
|
created_at BIGINT,
|
||||||
PRIMARY KEY (record_id))''')
|
PRIMARY KEY (record_id))''')
|
||||||
db_version += 1
|
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:
|
if current_version != db_version:
|
||||||
self.db_version = db_version
|
self.db_version = db_version
|
||||||
|
@ -5093,6 +5107,46 @@ class BasicSwap(BaseApp):
|
||||||
|
|
||||||
return rv
|
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):
|
def getWalletsInfo(self, opts=None):
|
||||||
rv = {}
|
rv = {}
|
||||||
for c in Coins:
|
for c in Coins:
|
||||||
|
@ -5105,6 +5159,44 @@ class BasicSwap(BaseApp):
|
||||||
rv[c] = {'name': chainparams[c]['name'].capitalize(), 'error': str(ex)}
|
rv[c] = {'name': chainparams[c]['name'].capitalize(), 'error': str(ex)}
|
||||||
return rv
|
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):
|
def countAcceptedBids(self, offer_id=None):
|
||||||
self.mxDB.acquire()
|
self.mxDB.acquire()
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -12,7 +12,7 @@ from enum import IntEnum, auto
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
|
||||||
|
|
||||||
CURRENT_DB_VERSION = 9
|
CURRENT_DB_VERSION = 10
|
||||||
Base = declarative_base()
|
Base = declarative_base()
|
||||||
|
|
||||||
|
|
||||||
|
@ -360,6 +360,7 @@ class Wallets(Base):
|
||||||
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||||
coin_id = sa.Column(sa.Integer)
|
coin_id = sa.Column(sa.Integer)
|
||||||
wallet_name = sa.Column(sa.String)
|
wallet_name = sa.Column(sa.String)
|
||||||
|
wallet_data = sa.Column(sa.String)
|
||||||
balance_type = sa.Column(sa.Integer)
|
balance_type = sa.Column(sa.Integer)
|
||||||
amount = sa.Column(sa.BigInteger)
|
amount = sa.Column(sa.BigInteger)
|
||||||
updated_at = sa.Column(sa.BigInteger)
|
updated_at = sa.Column(sa.BigInteger)
|
||||||
|
|
|
@ -279,11 +279,16 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||||
messages.append('Withdrew {} {} to address {}<br/>In txid: {}'.format(value, ticker, address, txid))
|
messages.append('Withdrew {} {} to address {}<br/>In txid: {}'.format(value, ticker, address, txid))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
messages.append('Error: {}'.format(str(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 = []
|
wallets_formatted = []
|
||||||
for k, w in wallets.items():
|
sk = sorted(wallets.keys())
|
||||||
|
|
||||||
|
for k in sk:
|
||||||
|
w = wallets[k]
|
||||||
if 'error' in w:
|
if 'error' in w:
|
||||||
wallets_formatted.append({
|
wallets_formatted.append({
|
||||||
'cid': str(int(k)),
|
'cid': str(int(k)),
|
||||||
|
@ -291,6 +296,14 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if 'balance' not in w:
|
||||||
|
wallets_formatted.append({
|
||||||
|
'name': w['name'],
|
||||||
|
'havedata': False,
|
||||||
|
'updating': w['updating'],
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
|
||||||
ci = swap_client.ci(k)
|
ci = swap_client.ci(k)
|
||||||
fee_rate, fee_src = swap_client.getFeeRateForCoin(k)
|
fee_rate, fee_src = swap_client.getFeeRateForCoin(k)
|
||||||
est_fee = swap_client.estimateWithdrawFee(k, fee_rate)
|
est_fee = swap_client.estimateWithdrawFee(k, fee_rate)
|
||||||
|
@ -308,11 +321,16 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||||
'deposit_address': w['deposit_address'],
|
'deposit_address': w['deposit_address'],
|
||||||
'expected_seed': w['expected_seed'],
|
'expected_seed': w['expected_seed'],
|
||||||
'balance_all': float(w['balance']) + float(w['unconfirmed']),
|
'balance_all': float(w['balance']) + float(w['unconfirmed']),
|
||||||
|
'updating': w['updating'],
|
||||||
|
'lastupdated': format_timestamp(w['lastupdated']),
|
||||||
|
'havedata': True,
|
||||||
}
|
}
|
||||||
if float(w['unconfirmed']) > 0.0:
|
if float(w['unconfirmed']) > 0.0:
|
||||||
wf['unconfirmed'] = w['unconfirmed']
|
wf['unconfirmed'] = w['unconfirmed']
|
||||||
|
|
||||||
if k == Coins.PART:
|
if k == Coins.PART:
|
||||||
|
|
||||||
|
wf['stealth_address'] = w['stealth_address']
|
||||||
wf['blind_balance'] = w['blind_balance']
|
wf['blind_balance'] = w['blind_balance']
|
||||||
if float(w['blind_unconfirmed']) > 0.0:
|
if float(w['blind_unconfirmed']) > 0.0:
|
||||||
wf['blind_unconfirmed'] = w['blind_unconfirmed']
|
wf['blind_unconfirmed'] = w['blind_unconfirmed']
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
{% include 'header.html' %}
|
{% include 'header.html' %}
|
||||||
|
|
||||||
|
<p><a href="/wallets">refresh</a></p>
|
||||||
|
|
||||||
<h3>Wallets</h3>
|
<h3>Wallets</h3>
|
||||||
{% if refresh %}
|
{% if refresh %}
|
||||||
<p>Page Refresh: {{ refresh }} seconds</p>
|
<p>Page Refresh: {{ refresh }} seconds</p>
|
||||||
|
@ -13,10 +15,15 @@
|
||||||
|
|
||||||
{% for w in wallets %}
|
{% for w in wallets %}
|
||||||
<h4>{{ w.name }} {{ w.version }}</h4>
|
<h4>{{ w.name }} {{ w.version }}</h4>
|
||||||
|
{% if w.updating %}
|
||||||
|
<h5>Updating</h5>
|
||||||
|
{% endif %}
|
||||||
|
{% if w.havedata %}
|
||||||
{% if w.error %}
|
{% if w.error %}
|
||||||
<p>Error: {{ w.error }}</p>
|
<p>Error: {{ w.error }}</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<table>
|
<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>
|
<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>
|
<tr><td>Fee Rate:</td><td>{{ w.fee_rate }}</td><td>Est Fee:</td><td>{{ w.est_fee }}</td></tr>
|
||||||
</table>
|
</table>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endif %} <!-- havedata -->
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
0.0.22
|
||||||
|
==============
|
||||||
|
- Improved wallets page
|
||||||
|
- Consistent wallet order
|
||||||
|
- Separated RPC calls into threads.
|
||||||
|
|
||||||
|
|
||||||
0.0.21
|
0.0.21
|
||||||
==============
|
==============
|
||||||
- Raised Particl and Monero daemon versions.
|
- Raised Particl and Monero daemon versions.
|
||||||
|
|
Loading…
Reference in a new issue