diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py
index 513086e..0e04956 100644
--- a/basicswap/basicswap.py
+++ b/basicswap/basicswap.py
@@ -5258,60 +5258,82 @@ class BasicSwap(BaseApp):
}
return rv
- def getWalletInfo(self, coin):
-
+ def getBlockchainInfo(self, coin):
ci = self.ci(coin)
- blockchaininfo = ci.getBlockchainInfo()
- walletinfo = ci.getWalletInfo()
- scale = chainparams[coin]['decimal_places']
- rv = {
- 'version': self.coin_clients[coin]['core_version'],
- 'deposit_address': self.getCachedAddressForCoin(coin),
- 'name': ci.coin_name(),
- 'blocks': blockchaininfo['blocks'],
- 'balance': format_amount(make_int(walletinfo['balance'], scale), scale),
- 'unconfirmed': format_amount(make_int(walletinfo.get('unconfirmed_balance'), scale), scale),
- 'synced': '{0:.2f}'.format(round(blockchaininfo['verificationprogress'], 2)),
- 'expected_seed': ci.knownWalletSeed(),
- }
+ try:
+ blockchaininfo = ci.getBlockchainInfo()
- if coin == Coins.PART:
- rv['stealth_address'] = self.getCachedStealthAddressForCoin(Coins.PART)
- rv['anon_balance'] = walletinfo['anon_balance']
- rv['anon_pending'] = walletinfo['unconfirmed_anon'] + walletinfo['immature_anon_balance']
- rv['blind_balance'] = walletinfo['blind_balance']
- rv['blind_unconfirmed'] = walletinfo['unconfirmed_blind']
- elif coin == Coins.XMR:
- rv['main_address'] = self.getCachedMainWalletAddress(ci)
+ rv = {
+ 'version': self.coin_clients[coin]['core_version'],
+ 'name': ci.coin_name(),
+ 'blocks': blockchaininfo['blocks'],
+ 'synced': '{0:.2f}'.format(round(blockchaininfo['verificationprogress'], 2)),
+ }
- return rv
+ return rv
+ except Exception as e:
+ self.log.warning('getWalletInfo failed with: %s', str(e))
- def updateWalletInfo(self, coin):
- wi = self.getWalletInfo(coin)
+ def getWalletInfo(self, coin):
+ ci = self.ci(coin)
- # Store wallet info to db so it's available after startup
+ try:
+ walletinfo = ci.getWalletInfo()
+ scale = chainparams[coin]['decimal_places']
+ rv = {
+ 'deposit_address': self.getCachedAddressForCoin(coin),
+ 'balance': format_amount(make_int(walletinfo['balance'], scale), scale),
+ 'unconfirmed': format_amount(make_int(walletinfo.get('unconfirmed_balance'), scale), scale),
+ 'expected_seed': ci.knownWalletSeed(),
+ }
+
+ if coin == Coins.PART:
+ rv['stealth_address'] = self.getCachedStealthAddressForCoin(Coins.PART)
+ rv['anon_balance'] = walletinfo['anon_balance']
+ rv['anon_pending'] = walletinfo['unconfirmed_anon'] + walletinfo['immature_anon_balance']
+ rv['blind_balance'] = walletinfo['blind_balance']
+ rv['blind_unconfirmed'] = walletinfo['unconfirmed_blind']
+ elif coin == Coins.XMR:
+ rv['main_address'] = self.getCachedMainWalletAddress(ci)
+
+ return rv
+ except Exception as e:
+ self.log.warning('getWalletInfo failed with: %s', str(e))
+
+ def addWalletInfoRecord(self, coin, info_type, wi):
+ coin_id = int(coin)
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.add(Wallets(coin_id=coin, balance_type=info_type, wallet_data=json.dumps(wi), created_at=now))
+ query_str = f'DELETE FROM wallets WHERE (coin_id = {coin_id} AND balance_type = {info_type}) AND record_id NOT IN (SELECT record_id FROM wallets WHERE coin_id = {coin_id} AND balance_type = {info_type} ORDER BY created_at DESC LIMIT 3 )'
session.execute(query_str)
session.commit()
except Exception as e:
- self.log.error(f'updateWalletInfo {e}')
-
+ self.log.error(f'addWalletInfoRecord {e}')
finally:
session.close()
session.remove()
- self._updating_wallets_info[int(coin)] = False
self.mxDB.release()
+ def updateWalletInfo(self, coin):
+ # Store wallet info to db so it's available after startup
+ try:
+ bi = self.getBlockchainInfo(coin)
+ if bi:
+ self.addWalletInfoRecord(coin, 0, bi)
+
+ # monero-wallet-rpc is slow/unresponsive while syncing
+ wi = self.getWalletInfo(coin)
+ if wi:
+ self.addWalletInfoRecord(coin, 1, wi)
+ except Exception as e:
+ self.log.error(f'updateWalletInfo {e}')
+ finally:
+ self._updating_wallets_info[int(coin)] = False
+
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:
@@ -5335,6 +5357,7 @@ class BasicSwap(BaseApp):
if self.coin_clients[c]['connection_type'] == 'rpc':
try:
rv[c] = self.getWalletInfo(c)
+ rv[c].update(self.getBlockchainInfo(c))
except Exception as ex:
rv[c] = {'name': chainparams[c]['name'].capitalize(), 'error': str(ex)}
return rv
@@ -5347,8 +5370,8 @@ class BasicSwap(BaseApp):
where_str = ''
if opts is not None and 'coin_id' in opts:
where_str = 'WHERE coin_id = {}'.format(opts['coin_id'])
- inner_str = f'SELECT coin_id, MAX(created_at) as max_created_at FROM wallets {where_str} 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)
+ inner_str = f'SELECT coin_id, balance_type, MAX(created_at) as max_created_at FROM wallets {where_str} GROUP BY coin_id, balance_type'
+ query_str = 'SELECT a.coin_id, a.balance_type, wallet_data, created_at FROM wallets a, ({}) b WHERE a.coin_id = b.coin_id AND a.balance_type = b.balance_type AND a.created_at = b.max_created_at'.format(inner_str)
q = session.execute(query_str)
for row in q:
@@ -5358,16 +5381,20 @@ class BasicSwap(BaseApp):
# Skip cached info if coin was disabled
continue
- wallet_data = json.loads(row[1])
- wallet_data['lastupdated'] = row[2]
- wallet_data['updating'] = self._updating_wallets_info.get(coin_id, False)
+ wallet_data = json.loads(row[2])
+ if row[1] == 1:
+ wallet_data['lastupdated'] = row[3]
+ 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]
+ # 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
+ if coin_id in rv:
+ rv[coin_id].update(wallet_data)
+ else:
+ rv[coin_id] = wallet_data
finally:
session.close()
session.remove()
@@ -5383,6 +5410,7 @@ class BasicSwap(BaseApp):
if coin_id not in rv:
rv[coin_id] = {
'name': chainparams[c]['name'].capitalize(),
+ 'no_data': True,
'updating': self._updating_wallets_info.get(coin_id, False),
}
@@ -5602,7 +5630,6 @@ class BasicSwap(BaseApp):
def getAutomationStrategy(self, strategy_id):
self.mxDB.acquire()
try:
- rv = []
session = scoped_session(self.session_factory)
return session.query(AutomationStrategy).filter_by(record_id=strategy_id).first()
finally:
@@ -5613,7 +5640,6 @@ class BasicSwap(BaseApp):
def getLinkedStrategy(self, linked_type, linked_id):
self.mxDB.acquire()
try:
- rv = []
session = scoped_session(self.session_factory)
query_str = 'SELECT links.strategy_id, strats.label FROM automationlinks links' + \
' LEFT JOIN automationstrategies strats ON strats.record_id = links.strategy_id' + \
diff --git a/basicswap/db.py b/basicswap/db.py
index e5bf6f7..f0ce860 100644
--- a/basicswap/db.py
+++ b/basicswap/db.py
@@ -391,8 +391,6 @@ class Wallets(Base):
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)
created_at = sa.Column(sa.BigInteger)
diff --git a/basicswap/db_upgrades.py b/basicswap/db_upgrades.py
index 6e1784f..18e16ba 100644
--- a/basicswap/db_upgrades.py
+++ b/basicswap/db_upgrades.py
@@ -102,9 +102,8 @@ def upgradeDatabase(self, db_version):
record_id INTEGER NOT NULL,
coin_id INTEGER,
wallet_name VARCHAR,
+ wallet_data VARCHAR,
balance_type INTEGER,
- amount BIGINT,
- updated_at BIGINT,
created_at BIGINT,
PRIMARY KEY (record_id))''')
db_version += 1
@@ -216,6 +215,7 @@ def upgradeDatabase(self, db_version):
db_version += 1
session.execute('ALTER TABLE xmr_swaps ADD COLUMN coin_a_lock_release_msg_id BLOB')
session.execute('ALTER TABLE xmr_swaps RENAME COLUMN coin_a_lock_refund_spend_tx_msg_id TO coin_a_lock_spend_tx_msg_id')
+
if current_version != db_version:
self.db_version = db_version
self.setIntKVInSession('db_version', db_version, session)
diff --git a/basicswap/http_server.py b/basicswap/http_server.py
index a53a9a0..500183d 100644
--- a/basicswap/http_server.py
+++ b/basicswap/http_server.py
@@ -23,7 +23,6 @@ from .util import (
from .chainparams import (
Coins,
chainparams,
- getCoinIdFromTicker,
)
from .basicswap_util import (
BidStates,
@@ -61,6 +60,7 @@ from .ui.util import (
)
from .ui.page_tor import page_tor
from .ui.page_offers import page_offers, page_offer, page_newoffer
+from .ui.page_wallet import page_wallets, page_wallet
from .ui.page_automation import (
page_automation_strategies,
page_automation_strategy,
@@ -257,304 +257,6 @@ class HttpHandler(BaseHTTPRequestHandler):
active_swaps=[(s[0].hex(), s[1], strBidState(s[2]), strTxState(s[3]), strTxState(s[4])) for s in active_swaps],
), 'UTF-8')
- def page_wallets(self, url_split, post_string):
- swap_client = self.server.swap_client
-
- page_data = {}
- messages = []
- form_data = self.checkForm(post_string, 'wallets', messages)
- if form_data:
- for c in Coins:
- if c not in chainparams:
- continue
- cid = str(int(c))
-
- if bytes('newaddr_' + cid, 'utf-8') in form_data:
- swap_client.cacheNewAddressForCoin(c)
- elif bytes('reseed_' + cid, 'utf-8') in form_data:
- try:
- swap_client.reseedWallet(c)
- messages.append('Reseed complete ' + str(c))
- except Exception as ex:
- messages.append('Reseed failed ' + str(ex))
- swap_client.updateWalletsInfo(True, c)
- elif bytes('withdraw_' + cid, 'utf-8') in form_data:
- try:
- value = form_data[bytes('amt_' + cid, 'utf-8')][0].decode('utf-8')
- page_data['wd_value_' + cid] = value
- except Exception as e:
- messages.append('Error: Missing value')
- try:
- address = form_data[bytes('to_' + cid, 'utf-8')][0].decode('utf-8')
- page_data['wd_address_' + cid] = address
- except Exception as e:
- messages.append('Error: Missing address')
-
- subfee = True if bytes('subfee_' + cid, 'utf-8') in form_data else False
- page_data['wd_subfee_' + cid] = subfee
-
- if c == Coins.PART:
- try:
- type_from = form_data[bytes('withdraw_type_from_' + cid, 'utf-8')][0].decode('utf-8')
- type_to = form_data[bytes('withdraw_type_to_' + cid, 'utf-8')][0].decode('utf-8')
- page_data['wd_type_from_' + cid] = type_from
- page_data['wd_type_to_' + cid] = type_to
- except Exception as e:
- messages.append('Error: Missing type')
-
- if len(messages) == 0:
- ci = swap_client.ci(c)
- ticker = ci.ticker()
- if c == Coins.PART:
- try:
- txid = swap_client.withdrawParticl(type_from, type_to, value, address, subfee)
- messages.append('Withdrew {} {} ({} to {}) to address {}
In txid: {}'.format(value, ticker, type_from, type_to, address, txid))
- except Exception as e:
- messages.append('Error: {}'.format(str(e)))
- else:
- try:
- txid = swap_client.withdrawCoin(c, value, address, subfee)
- messages.append('Withdrew {} {} to address {}
In txid: {}'.format(value, ticker, address, txid))
- except Exception as e:
- messages.append('Error: {}'.format(str(e)))
- swap_client.updateWalletsInfo(True, c)
-
- swap_client.updateWalletsInfo()
- wallets = swap_client.getCachedWalletsInfo()
-
- wallets_formatted = []
- sk = sorted(wallets.keys())
-
- for k in sk:
- w = wallets[k]
- if 'error' in w:
- wallets_formatted.append({
- 'cid': str(int(k)),
- 'error': w['error']
- })
- continue
-
- if 'balance' not in w:
- wallets_formatted.append({
- 'name': w['name'],
- 'havedata': False,
- 'updating': w['updating'],
- })
- continue
-
- ci = swap_client.ci(k)
- cid = str(int(k))
- wf = {
- 'name': w['name'],
- 'version': w['version'],
- 'ticker': ci.ticker_mainnet(),
- 'cid': cid,
- 'balance': w['balance'],
- 'blocks': w['blocks'],
- 'synced': w['synced'],
- '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']
- wf['anon_balance'] = w['anon_balance']
- if float(w['anon_pending']) > 0.0:
- wf['anon_pending'] = w['anon_pending']
-
- wallets_formatted.append(wf)
-
- template = env.get_template('wallets.html')
- return bytes(template.render(
- title=self.server.title,
- h2=self.server.title,
- messages=messages,
- wallets=wallets_formatted,
- form_id=os.urandom(8).hex(),
- ), 'UTF-8')
-
- def page_wallet(self, url_split, post_string):
- ensure(len(url_split) > 2, 'Wallet not specified')
- wallet_ticker = url_split[2]
- swap_client = self.server.swap_client
-
- coin_id = getCoinIdFromTicker(wallet_ticker)
-
- page_data = {}
- messages = []
- form_data = self.checkForm(post_string, 'settings', messages)
- show_utxo_groups = False
- if form_data:
- cid = str(int(coin_id))
-
- if bytes('newaddr_' + cid, 'utf-8') in form_data:
- swap_client.cacheNewAddressForCoin(coin_id)
- elif bytes('reseed_' + cid, 'utf-8') in form_data:
- try:
- swap_client.reseedWallet(coin_id)
- messages.append('Reseed complete ' + str(coin_id))
- except Exception as ex:
- messages.append('Reseed failed ' + str(ex))
- swap_client.updateWalletsInfo(True, coin_id)
- elif bytes('withdraw_' + cid, 'utf-8') in form_data:
- try:
- value = form_data[bytes('amt_' + cid, 'utf-8')][0].decode('utf-8')
- page_data['wd_value_' + cid] = value
- except Exception as e:
- messages.append('Error: Missing value')
- try:
- address = form_data[bytes('to_' + cid, 'utf-8')][0].decode('utf-8')
- page_data['wd_address_' + cid] = address
- except Exception as e:
- messages.append('Error: Missing address')
-
- subfee = True if bytes('subfee_' + cid, 'utf-8') in form_data else False
- page_data['wd_subfee_' + cid] = subfee
-
- if coin_id == Coins.PART:
- try:
- type_from = form_data[bytes('withdraw_type_from_' + cid, 'utf-8')][0].decode('utf-8')
- type_to = form_data[bytes('withdraw_type_to_' + cid, 'utf-8')][0].decode('utf-8')
- page_data['wd_type_from_' + cid] = type_from
- page_data['wd_type_to_' + cid] = type_to
- except Exception as e:
- messages.append('Error: Missing type')
-
- if len(messages) == 0:
- ci = swap_client.ci(coin_id)
- ticker = ci.ticker()
- if coin_id == Coins.PART:
- try:
- txid = swap_client.withdrawParticl(type_from, type_to, value, address, subfee)
- messages.append('Withdrew {} {} ({} to {}) to address {}
In txid: {}'.format(value, ticker, type_from, type_to, address, txid))
- except Exception as e:
- messages.append('Error: {}'.format(str(e)))
- else:
- try:
- txid = swap_client.withdrawCoin(coin_id, value, address, subfee)
- messages.append('Withdrew {} {} to address {}
In txid: {}'.format(value, ticker, address, txid))
- except Exception as e:
- messages.append('Error: {}'.format(str(e)))
- swap_client.updateWalletsInfo(True, coin_id)
- elif have_data_entry(form_data, 'showutxogroups'):
- show_utxo_groups = True
- elif have_data_entry(form_data, 'create_utxo'):
- show_utxo_groups = True
- try:
- value = get_data_entry(form_data, 'utxo_value')
- page_data['utxo_value'] = value
-
- ci = swap_client.ci(coin_id)
-
- value_sats = ci.make_int(value)
-
- txid, address = ci.createUTXO(value_sats)
- messages.append('Created new utxo of value {} and address {}
In txid: {}'.format(value, address, txid))
- except Exception as e:
- messages.append('Error: {}'.format(str(e)))
- if swap_client.debug is True:
- swap_client.log.error(traceback.format_exc())
-
- swap_client.updateWalletsInfo()
- wallets = swap_client.getCachedWalletsInfo({'coin_id': coin_id})
- for k in wallets.keys():
- w = wallets[k]
- if 'error' in w:
- wallet_data = {
- 'cid': str(int(k)),
- 'error': w['error']
- }
- continue
-
- if 'balance' not in w:
- wallet_data = {
- '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)
- cid = str(int(k))
- wallet_data = {
- 'name': w['name'],
- 'version': w['version'],
- 'ticker': ci.ticker_mainnet(),
- 'cid': cid,
- 'fee_rate': ci.format_amount(int(fee_rate * ci.COIN())),
- 'fee_rate_src': fee_src,
- 'est_fee': 'Unknown' if est_fee is None else ci.format_amount(int(est_fee * ci.COIN())),
- 'balance': w['balance'],
- 'blocks': w['blocks'],
- 'synced': w['synced'],
- '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:
- wallet_data['unconfirmed'] = w['unconfirmed']
-
- if k == Coins.PART:
- wallet_data['stealth_address'] = w['stealth_address']
- wallet_data['blind_balance'] = w['blind_balance']
- if float(w['blind_unconfirmed']) > 0.0:
- wallet_data['blind_unconfirmed'] = w['blind_unconfirmed']
- wallet_data['anon_balance'] = w['anon_balance']
- if float(w['anon_pending']) > 0.0:
- wallet_data['anon_pending'] = w['anon_pending']
-
- elif k == Coins.XMR:
- wallet_data['main_address'] = w.get('main_address', 'Refresh necessary')
-
- if 'wd_type_from_' + cid in page_data:
- wallet_data['wd_type_from'] = page_data['wd_type_from_' + cid]
- if 'wd_type_to_' + cid in page_data:
- wallet_data['wd_type_to'] = page_data['wd_type_to_' + cid]
-
- if 'wd_value_' + cid in page_data:
- wallet_data['wd_value'] = page_data['wd_value_' + cid]
- if 'wd_address_' + cid in page_data:
- wallet_data['wd_address'] = page_data['wd_address_' + cid]
- if 'wd_subfee_' + cid in page_data:
- wallet_data['wd_subfee'] = page_data['wd_subfee_' + cid]
- if 'utxo_value' in page_data:
- wallet_data['utxo_value'] = page_data['utxo_value']
-
- if show_utxo_groups:
- utxo_groups = ''
-
- unspent_by_addr = swap_client.getUnspentsByAddr(k)
-
- sorted_unspent_by_addr = sorted(unspent_by_addr.items(), key=lambda x: x[1], reverse=True)
- for kv in sorted_unspent_by_addr:
- utxo_groups += kv[0] + ' ' + ci.format_amount(kv[1]) + '\n'
-
- wallet_data['show_utxo_groups'] = True
- wallet_data['utxo_groups'] = utxo_groups
-
- template = env.get_template('wallet.html')
- return bytes(template.render(
- title=self.server.title,
- h2=self.server.title,
- messages=messages,
- w=wallet_data,
- form_id=os.urandom(8).hex(),
- ), 'UTF-8')
-
def page_settings(self, url_split, post_string):
swap_client = self.server.swap_client
@@ -1000,9 +702,9 @@ class HttpHandler(BaseHTTPRequestHandler):
if url_split[1] == 'active':
return self.page_active(url_split, post_string)
if url_split[1] == 'wallets':
- return self.page_wallets(url_split, post_string)
+ return page_wallets(self, url_split, post_string)
if url_split[1] == 'wallet':
- return self.page_wallet(url_split, post_string)
+ return page_wallet(self, url_split, post_string)
if url_split[1] == 'settings':
return self.page_settings(url_split, post_string)
if url_split[1] == 'rpc':
diff --git a/basicswap/interface_xmr.py b/basicswap/interface_xmr.py
index 5364f64..6952e02 100644
--- a/basicswap/interface_xmr.py
+++ b/basicswap/interface_xmr.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-# Copyright (c) 2020-2021 tecnovert
+# Copyright (c) 2020-2022 tecnovert
# Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
@@ -117,22 +117,34 @@ class XMRInterface(CoinInterface):
return self.rpc_wallet_cb('get_version')['version']
def getBlockchainInfo(self):
- rv = {}
-
- # get_block_count returns "Internal error" if bootstrap-daemon is active
- # rv['blocks'] = self.rpc_cb('get_block_count')['count']
- rv['blocks'] = self.rpc_cb2('get_height', timeout=30)['height']
-
- # sync_info = self.rpc_cb('sync_info', timeout=30)
- # rv['verificationprogress'] = 0.0 if 'spans' in sync_info else 1.0
- rv['verificationprogress'] = 0.0
+ get_height = self.rpc_cb2('get_height', timeout=30)
+ rv = {
+ 'blocks': get_height['height'],
+ 'verificationprogress': 0.0,
+ }
+
+ try:
+ # get_block_count.block_count is how many blocks are in the longest chain known to the node.
+ # get_block_count returns "Internal error" if bootstrap-daemon is active
+ if get_height['untrusted'] is True:
+ rv['untrusted'] = True
+ get_info = self.rpc_cb2('get_info', timeout=30)
+ if 'height_without_bootstrap' in get_info:
+ rv['blocks'] = get_info['height_without_bootstrap']
+
+ rv['block_count'] = get_info['height']
+ if rv['block_count'] > rv['blocks']:
+ rv['verificationprogress'] = rv['blocks'] / rv['block_count']
+ else:
+ rv['block_count'] = self.rpc_cb('get_block_count', timeout=30)['count']
+ rv['verificationprogress'] = rv['blocks'] / rv['block_count']
+ except Exception as e:
+ self._log.warning('XMR get_block_count failed with: %s', str(e))
+ rv['verificationprogress'] = 0.0
return rv
def getChainHeight(self):
- # get_block_count returns "Internal error" if bootstrap-daemon is active
- # return self.rpc_cb('get_info')['height']
- # return self.rpc_cb('get_block_count')['count']
return self.rpc_cb2('get_height', timeout=30)['height']
def getWalletInfo(self):
diff --git a/basicswap/js_server.py b/basicswap/js_server.py
index fb80149..39d22d0 100644
--- a/basicswap/js_server.py
+++ b/basicswap/js_server.py
@@ -62,6 +62,7 @@ def withdraw_coin(swap_client, coin_type, post_string, is_json):
def js_wallets(self, url_split, post_string, is_json):
+ swap_client = self.server.swap_client
if len(url_split) > 3:
ticker_str = url_split[3]
coin_type = tickerToCoinId(ticker_str)
@@ -69,9 +70,11 @@ def js_wallets(self, url_split, post_string, is_json):
if len(url_split) > 4:
cmd = url_split[4]
if cmd == 'withdraw':
- return bytes(json.dumps(withdraw_coin(self.server.swap_client, coin_type, post_string, is_json)), 'UTF-8')
+ return bytes(json.dumps(withdraw_coin(swap_client, coin_type, post_string, is_json)), 'UTF-8')
raise ValueError('Unknown command')
- return bytes(json.dumps(self.server.swap_client.getWalletInfo(coin_type)), 'UTF-8')
+
+ rv = swap_client.getWalletInfo(coin_type).update(swap_client.getBlockchainInfo(coin_type))
+ return bytes(json.dumps(rv), 'UTF-8')
return bytes(json.dumps(self.server.swap_client.getWalletsInfo()), 'UTF-8')
diff --git a/basicswap/ui/page_wallet.py b/basicswap/ui/page_wallet.py
new file mode 100644
index 0000000..5cd5f04
--- /dev/null
+++ b/basicswap/ui/page_wallet.py
@@ -0,0 +1,327 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2022 tecnovert
+# Distributed under the MIT software license, see the accompanying
+# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
+
+import os
+import traceback
+
+from .util import (
+ get_data_entry,
+ have_data_entry,
+)
+from basicswap.util import (
+ ensure,
+ format_timestamp,
+)
+from basicswap.chainparams import (
+ Coins,
+ chainparams,
+ getCoinIdFromTicker,
+)
+
+
+def page_wallets(self, url_split, post_string):
+ server = self.server
+ swap_client = server.swap_client
+
+ page_data = {}
+ messages = []
+ form_data = self.checkForm(post_string, 'wallets', messages)
+ if form_data:
+ for c in Coins:
+ if c not in chainparams:
+ continue
+ cid = str(int(c))
+
+ if bytes('newaddr_' + cid, 'utf-8') in form_data:
+ swap_client.cacheNewAddressForCoin(c)
+ elif bytes('reseed_' + cid, 'utf-8') in form_data:
+ try:
+ swap_client.reseedWallet(c)
+ messages.append('Reseed complete ' + str(c))
+ except Exception as ex:
+ messages.append('Reseed failed ' + str(ex))
+ swap_client.updateWalletsInfo(True, c)
+ elif bytes('withdraw_' + cid, 'utf-8') in form_data:
+ try:
+ value = form_data[bytes('amt_' + cid, 'utf-8')][0].decode('utf-8')
+ page_data['wd_value_' + cid] = value
+ except Exception as e:
+ messages.append('Error: Missing value')
+ try:
+ address = form_data[bytes('to_' + cid, 'utf-8')][0].decode('utf-8')
+ page_data['wd_address_' + cid] = address
+ except Exception as e:
+ messages.append('Error: Missing address')
+
+ subfee = True if bytes('subfee_' + cid, 'utf-8') in form_data else False
+ page_data['wd_subfee_' + cid] = subfee
+
+ if c == Coins.PART:
+ try:
+ type_from = form_data[bytes('withdraw_type_from_' + cid, 'utf-8')][0].decode('utf-8')
+ type_to = form_data[bytes('withdraw_type_to_' + cid, 'utf-8')][0].decode('utf-8')
+ page_data['wd_type_from_' + cid] = type_from
+ page_data['wd_type_to_' + cid] = type_to
+ except Exception as e:
+ messages.append('Error: Missing type')
+
+ if len(messages) == 0:
+ ci = swap_client.ci(c)
+ ticker = ci.ticker()
+ if c == Coins.PART:
+ try:
+ txid = swap_client.withdrawParticl(type_from, type_to, value, address, subfee)
+ messages.append('Withdrew {} {} ({} to {}) to address {}
In txid: {}'.format(value, ticker, type_from, type_to, address, txid))
+ except Exception as e:
+ messages.append('Error: {}'.format(str(e)))
+ else:
+ try:
+ txid = swap_client.withdrawCoin(c, value, address, subfee)
+ messages.append('Withdrew {} {} to address {}
In txid: {}'.format(value, ticker, address, txid))
+ except Exception as e:
+ messages.append('Error: {}'.format(str(e)))
+ swap_client.updateWalletsInfo(True, c)
+
+ swap_client.updateWalletsInfo()
+ wallets = swap_client.getCachedWalletsInfo()
+
+ wallets_formatted = []
+ sk = sorted(wallets.keys())
+
+ for k in sk:
+ w = wallets[k]
+ if 'error' in w:
+ wallets_formatted.append({
+ 'cid': str(int(k)),
+ 'error': w['error']
+ })
+ continue
+
+ if 'no_data' in w:
+ wallets_formatted.append({
+ 'name': w['name'],
+ 'havedata': False,
+ 'updating': w['updating'],
+ })
+ continue
+
+ ci = swap_client.ci(k)
+ cid = str(int(k))
+ wf = {
+ 'name': ci.coin_name(),
+ 'version': w.get('version', '?'),
+ 'ticker': ci.ticker_mainnet(),
+ 'cid': cid,
+ 'balance': w.get('balance', '?'),
+ 'blocks': w.get('blocks', '?'),
+ 'synced': w.get('synced', '?'),
+ 'deposit_address': w.get('deposit_address', '?'),
+ 'expected_seed': w.get('expected_seed', '?'),
+ 'updating': w.get('updating', '?'),
+ 'havedata': True,
+ }
+
+ if 'balance' in w and 'unconfirmed' in w:
+ wf['balance_all'] = float(w['balance']) + float(w['unconfirmed'])
+ if 'lastupdated' in w:
+ wf['lastupdated'] = format_timestamp(w['lastupdated'])
+ if 'unconfirmed' in w and float(w['unconfirmed']) > 0.0:
+ wf['unconfirmed'] = w['unconfirmed']
+
+ if k == Coins.PART:
+ wf['stealth_address'] = w.get('stealth_address', '?')
+ wf['blind_balance'] = w.get('blind_balance', '?')
+ if 'blind_unconfirmed' in w and float(w['blind_unconfirmed']) > 0.0:
+ wf['blind_unconfirmed'] = w['blind_unconfirmed']
+ wf['anon_balance'] = w.get('anon_balance', '?')
+ if 'anon_pending' in w and float(w['anon_pending']) > 0.0:
+ wf['anon_pending'] = w['anon_pending']
+
+ wallets_formatted.append(wf)
+
+ template = server.env.get_template('wallets.html')
+ return bytes(template.render(
+ title=server.title,
+ h2=server.title,
+ messages=messages,
+ wallets=wallets_formatted,
+ form_id=os.urandom(8).hex(),
+ ), 'UTF-8')
+
+
+def page_wallet(self, url_split, post_string):
+ ensure(len(url_split) > 2, 'Wallet not specified')
+ wallet_ticker = url_split[2]
+ server = self.server
+ swap_client = server.swap_client
+
+ coin_id = getCoinIdFromTicker(wallet_ticker)
+
+ page_data = {}
+ messages = []
+ form_data = self.checkForm(post_string, 'settings', messages)
+ show_utxo_groups = False
+ if form_data:
+ cid = str(int(coin_id))
+
+ if bytes('newaddr_' + cid, 'utf-8') in form_data:
+ swap_client.cacheNewAddressForCoin(coin_id)
+ elif bytes('reseed_' + cid, 'utf-8') in form_data:
+ try:
+ swap_client.reseedWallet(coin_id)
+ messages.append('Reseed complete ' + str(coin_id))
+ except Exception as ex:
+ messages.append('Reseed failed ' + str(ex))
+ swap_client.updateWalletsInfo(True, coin_id)
+ elif bytes('withdraw_' + cid, 'utf-8') in form_data:
+ try:
+ value = form_data[bytes('amt_' + cid, 'utf-8')][0].decode('utf-8')
+ page_data['wd_value_' + cid] = value
+ except Exception as e:
+ messages.append('Error: Missing value')
+ try:
+ address = form_data[bytes('to_' + cid, 'utf-8')][0].decode('utf-8')
+ page_data['wd_address_' + cid] = address
+ except Exception as e:
+ messages.append('Error: Missing address')
+
+ subfee = True if bytes('subfee_' + cid, 'utf-8') in form_data else False
+ page_data['wd_subfee_' + cid] = subfee
+
+ if coin_id == Coins.PART:
+ try:
+ type_from = form_data[bytes('withdraw_type_from_' + cid, 'utf-8')][0].decode('utf-8')
+ type_to = form_data[bytes('withdraw_type_to_' + cid, 'utf-8')][0].decode('utf-8')
+ page_data['wd_type_from_' + cid] = type_from
+ page_data['wd_type_to_' + cid] = type_to
+ except Exception as e:
+ messages.append('Error: Missing type')
+
+ if len(messages) == 0:
+ ci = swap_client.ci(coin_id)
+ ticker = ci.ticker()
+ if coin_id == Coins.PART:
+ try:
+ txid = swap_client.withdrawParticl(type_from, type_to, value, address, subfee)
+ messages.append('Withdrew {} {} ({} to {}) to address {}
In txid: {}'.format(value, ticker, type_from, type_to, address, txid))
+ except Exception as e:
+ messages.append('Error: {}'.format(str(e)))
+ else:
+ try:
+ txid = swap_client.withdrawCoin(coin_id, value, address, subfee)
+ messages.append('Withdrew {} {} to address {}
In txid: {}'.format(value, ticker, address, txid))
+ except Exception as e:
+ messages.append('Error: {}'.format(str(e)))
+ swap_client.updateWalletsInfo(True, coin_id)
+ elif have_data_entry(form_data, 'showutxogroups'):
+ show_utxo_groups = True
+ elif have_data_entry(form_data, 'create_utxo'):
+ show_utxo_groups = True
+ try:
+ value = get_data_entry(form_data, 'utxo_value')
+ page_data['utxo_value'] = value
+
+ ci = swap_client.ci(coin_id)
+
+ value_sats = ci.make_int(value)
+
+ txid, address = ci.createUTXO(value_sats)
+ messages.append('Created new utxo of value {} and address {}
In txid: {}'.format(value, address, txid))
+ except Exception as e:
+ messages.append('Error: {}'.format(str(e)))
+ if swap_client.debug is True:
+ swap_client.log.error(traceback.format_exc())
+
+ swap_client.updateWalletsInfo()
+ wallets = swap_client.getCachedWalletsInfo({'coin_id': coin_id})
+ for k in wallets.keys():
+ w = wallets[k]
+ if 'error' in w:
+ wallet_data = {
+ 'cid': str(int(k)),
+ 'error': w['error']
+ }
+ continue
+
+ if 'balance' not in w:
+ wallet_data = {
+ '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)
+ cid = str(int(k))
+ wallet_data = {
+ 'name': w['name'],
+ 'version': w['version'],
+ 'ticker': ci.ticker_mainnet(),
+ 'cid': cid,
+ 'fee_rate': ci.format_amount(int(fee_rate * ci.COIN())),
+ 'fee_rate_src': fee_src,
+ 'est_fee': 'Unknown' if est_fee is None else ci.format_amount(int(est_fee * ci.COIN())),
+ 'balance': w['balance'],
+ 'blocks': w['blocks'],
+ 'synced': w['synced'],
+ '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:
+ wallet_data['unconfirmed'] = w['unconfirmed']
+
+ if k == Coins.PART:
+ wallet_data['stealth_address'] = w['stealth_address']
+ wallet_data['blind_balance'] = w['blind_balance']
+ if float(w['blind_unconfirmed']) > 0.0:
+ wallet_data['blind_unconfirmed'] = w['blind_unconfirmed']
+ wallet_data['anon_balance'] = w['anon_balance']
+ if float(w['anon_pending']) > 0.0:
+ wallet_data['anon_pending'] = w['anon_pending']
+
+ elif k == Coins.XMR:
+ wallet_data['main_address'] = w.get('main_address', 'Refresh necessary')
+
+ if 'wd_type_from_' + cid in page_data:
+ wallet_data['wd_type_from'] = page_data['wd_type_from_' + cid]
+ if 'wd_type_to_' + cid in page_data:
+ wallet_data['wd_type_to'] = page_data['wd_type_to_' + cid]
+
+ if 'wd_value_' + cid in page_data:
+ wallet_data['wd_value'] = page_data['wd_value_' + cid]
+ if 'wd_address_' + cid in page_data:
+ wallet_data['wd_address'] = page_data['wd_address_' + cid]
+ if 'wd_subfee_' + cid in page_data:
+ wallet_data['wd_subfee'] = page_data['wd_subfee_' + cid]
+ if 'utxo_value' in page_data:
+ wallet_data['utxo_value'] = page_data['utxo_value']
+
+ if show_utxo_groups:
+ utxo_groups = ''
+
+ unspent_by_addr = swap_client.getUnspentsByAddr(k)
+
+ sorted_unspent_by_addr = sorted(unspent_by_addr.items(), key=lambda x: x[1], reverse=True)
+ for kv in sorted_unspent_by_addr:
+ utxo_groups += kv[0] + ' ' + ci.format_amount(kv[1]) + '\n'
+
+ wallet_data['show_utxo_groups'] = True
+ wallet_data['utxo_groups'] = utxo_groups
+
+ template = server.env.get_template('wallet.html')
+ return bytes(template.render(
+ title=server.title,
+ h2=server.title,
+ messages=messages,
+ w=wallet_data,
+ form_id=os.urandom(8).hex(),
+ ), 'UTF-8')