mirror of
https://github.com/basicswap/basicswap.git
synced 2025-03-21 06:38:54 +00:00
Adding settings page.
This commit is contained in:
parent
4405a130f5
commit
3e542e6bd0
7 changed files with 138 additions and 11 deletions
|
@ -15,6 +15,8 @@ import hashlib
|
||||||
import subprocess
|
import subprocess
|
||||||
import logging
|
import logging
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
import shutil
|
||||||
|
import json
|
||||||
from sqlalchemy.orm import sessionmaker, scoped_session
|
from sqlalchemy.orm import sessionmaker, scoped_session
|
||||||
from enum import IntEnum, auto
|
from enum import IntEnum, auto
|
||||||
|
|
||||||
|
@ -453,6 +455,7 @@ class BasicSwap():
|
||||||
'pid': None,
|
'pid': None,
|
||||||
'core_version': None,
|
'core_version': None,
|
||||||
'explorers': [],
|
'explorers': [],
|
||||||
|
'chain_lookups': chain_client_settings.get('chain_lookups', 'local'),
|
||||||
}
|
}
|
||||||
|
|
||||||
def setDaemonPID(self, name, pid):
|
def setDaemonPID(self, name, pid):
|
||||||
|
@ -637,13 +640,13 @@ class BasicSwap():
|
||||||
if bid.state == BidStates.BID_ABANDONED or bid.state == BidStates.SWAP_COMPLETED:
|
if bid.state == BidStates.BID_ABANDONED or bid.state == BidStates.SWAP_COMPLETED:
|
||||||
# Return unused addrs to pool
|
# Return unused addrs to pool
|
||||||
if bid.getITxState() != TxStates.TX_REDEEMED:
|
if bid.getITxState() != TxStates.TX_REDEEMED:
|
||||||
self.returnAddressToPool(bid_id, TxTypes.ITX_REDEEM)
|
self.returnAddressToPool(bid.bid_id, TxTypes.ITX_REDEEM)
|
||||||
if bid.getITxState() != TxStates.TX_REFUNDED:
|
if bid.getITxState() != TxStates.TX_REFUNDED:
|
||||||
self.returnAddressToPool(bid_id, TxTypes.ITX_REFUND)
|
self.returnAddressToPool(bid.bid_id, TxTypes.ITX_REFUND)
|
||||||
if bid.getPTxState() != TxStates.TX_REDEEMED:
|
if bid.getPTxState() != TxStates.TX_REDEEMED:
|
||||||
self.returnAddressToPool(bid_id, TxTypes.PTX_REDEEM)
|
self.returnAddressToPool(bid.bid_id, TxTypes.PTX_REDEEM)
|
||||||
if bid.getPTxState() != TxStates.TX_REFUNDED:
|
if bid.getPTxState() != TxStates.TX_REFUNDED:
|
||||||
self.returnAddressToPool(bid_id, TxTypes.PTX_REFUND)
|
self.returnAddressToPool(bid.bid_id, TxTypes.PTX_REFUND)
|
||||||
|
|
||||||
def loadFromDB(self):
|
def loadFromDB(self):
|
||||||
self.log.info('Loading data from db')
|
self.log.info('Loading data from db')
|
||||||
|
@ -1700,12 +1703,40 @@ class BasicSwap():
|
||||||
|
|
||||||
# bid saved in checkBidState
|
# bid saved in checkBidState
|
||||||
|
|
||||||
|
def getAddressBalance(self, coin_type, address):
|
||||||
|
if self.coin_clients[coin_type]['chain_lookups'] == 'explorer':
|
||||||
|
explorers = self.coin_clients[coin_type]['explorers']
|
||||||
|
|
||||||
|
# TODO: random offset into explorers, try blocks
|
||||||
|
for exp in explorers:
|
||||||
|
return exp.getBalance(address)
|
||||||
|
return self.lookupUnspentByAddress(coin_type, address, sum_output=True)
|
||||||
|
|
||||||
def lookupChainHeight(self, coin_type):
|
def lookupChainHeight(self, coin_type):
|
||||||
return self.callcoinrpc(coin_type, 'getblockchaininfo')['blocks']
|
return self.callcoinrpc(coin_type, 'getblockchaininfo')['blocks']
|
||||||
|
|
||||||
def lookupUnspentByAddress(self, coin_type, address, sum_output=False, assert_amount=None, assert_txid=None):
|
def lookupUnspentByAddress(self, coin_type, address, sum_output=False, assert_amount=None, assert_txid=None):
|
||||||
|
|
||||||
# TODO: Lookup from explorers
|
if self.coin_clients[coin_type]['chain_lookups'] == 'explorer':
|
||||||
|
explorers = self.coin_clients[coin_type]['explorers']
|
||||||
|
|
||||||
|
# TODO: random offset into explorers, try blocks
|
||||||
|
for exp in explorers:
|
||||||
|
|
||||||
|
# TODO: ExplorerBitAps use only gettransaction if assert_txid is set
|
||||||
|
rv = exp.lookupUnspentByAddress(address)
|
||||||
|
|
||||||
|
if assert_amount is not None:
|
||||||
|
assert(rv['value'] == int(assert_amount)), 'Incorrect output amount in txn {}: {} != {}.'.format(assert_txid, rv['value'], int(assert_amount))
|
||||||
|
if assert_txid is not None:
|
||||||
|
assert(rv['txid)'] == assert_txid), 'Incorrect txid'
|
||||||
|
|
||||||
|
return rv
|
||||||
|
|
||||||
|
raise ValueError('No explorer for lookupUnspentByAddress {}'.format(str(coin_type)))
|
||||||
|
|
||||||
|
if self.coin_clients[coin_type]['connection_type'] != 'rpc':
|
||||||
|
raise ValueError('No RPC connection for lookupUnspentByAddress {}'.format(str(coin_type)))
|
||||||
|
|
||||||
if assert_txid is not None:
|
if assert_txid is not None:
|
||||||
try:
|
try:
|
||||||
|
@ -1739,6 +1770,7 @@ class BasicSwap():
|
||||||
'index': o['vout'],
|
'index': o['vout'],
|
||||||
'height': o['height'],
|
'height': o['height'],
|
||||||
'n_conf': n_conf,
|
'n_conf': n_conf,
|
||||||
|
'value': makeInt(o['amount']),
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
sum_unspent += o['amount'] * COIN
|
sum_unspent += o['amount'] * COIN
|
||||||
|
@ -2153,7 +2185,7 @@ class BasicSwap():
|
||||||
else:
|
else:
|
||||||
addr_search = bid_data.proof_address
|
addr_search = bid_data.proof_address
|
||||||
|
|
||||||
sum_unspent = self.lookupUnspentByAddress(coin_to, addr_search, sum_output=True)
|
sum_unspent = self.getAddressBalance(coin_to, addr_search)
|
||||||
self.log.debug('Proof of funds %s %s', bid_data.proof_address, format8(sum_unspent))
|
self.log.debug('Proof of funds %s %s', bid_data.proof_address, format8(sum_unspent))
|
||||||
assert(sum_unspent >= bid_data.amount), 'Proof of funds failed'
|
assert(sum_unspent >= bid_data.amount), 'Proof of funds failed'
|
||||||
|
|
||||||
|
@ -2366,7 +2398,7 @@ class BasicSwap():
|
||||||
if has_changed:
|
if has_changed:
|
||||||
session = scoped_session(self.session_factory)
|
session = scoped_session(self.session_factory)
|
||||||
try:
|
try:
|
||||||
self.saveBidInSession(session, bid)
|
self.saveBidInSession(bid_id, bid, session)
|
||||||
session.commit()
|
session.commit()
|
||||||
if bid.state and bid.state > BidStates.BID_RECEIVED and bid.state < BidStates.SWAP_COMPLETED:
|
if bid.state and bid.state > BidStates.BID_RECEIVED and bid.state < BidStates.SWAP_COMPLETED:
|
||||||
self.activateBid(session, bid)
|
self.activateBid(session, bid)
|
||||||
|
@ -2380,6 +2412,23 @@ class BasicSwap():
|
||||||
finally:
|
finally:
|
||||||
self.mxDB.release()
|
self.mxDB.release()
|
||||||
|
|
||||||
|
def editSettings(self, coin_name, data):
|
||||||
|
self.log.info('Updating settings %s', coin_name)
|
||||||
|
self.mxDB.acquire()
|
||||||
|
try:
|
||||||
|
if 'lookups' in data:
|
||||||
|
self.settings['chainclients'][coin_name]['chain_lookups'] = data['lookups']
|
||||||
|
settings_path = os.path.join(self.data_dir, 'basicswap.json')
|
||||||
|
shutil.copyfile(settings_path, settings_path + '.last')
|
||||||
|
with open(settings_path, 'w') as fp:
|
||||||
|
json.dump(self.settings, fp, indent=4)
|
||||||
|
|
||||||
|
for c in self.coin_clients:
|
||||||
|
if c['name'] == coin_name:
|
||||||
|
c['chain_lookups'] = data['lookups']
|
||||||
|
finally:
|
||||||
|
self.mxDB.release()
|
||||||
|
|
||||||
def getSummary(self, opts=None):
|
def getSummary(self, opts=None):
|
||||||
num_watched_outputs = 0
|
num_watched_outputs = 0
|
||||||
for c, v in self.coin_clients.items():
|
for c, v in self.coin_clients.items():
|
||||||
|
|
|
@ -190,4 +190,3 @@ class EventQueue(Base):
|
||||||
trigger_at = sa.Column(sa.BigInteger)
|
trigger_at = sa.Column(sa.BigInteger)
|
||||||
linked_id = sa.Column(sa.LargeBinary)
|
linked_id = sa.Column(sa.LargeBinary)
|
||||||
event_type = sa.Column(sa.Integer)
|
event_type = sa.Column(sa.Integer)
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,7 @@ class ExplorerInsight(Explorer):
|
||||||
'index': utxo['vout'],
|
'index': utxo['vout'],
|
||||||
'height': utxo['height'],
|
'height': utxo['height'],
|
||||||
'n_conf': utxo['confirmations'],
|
'n_conf': utxo['confirmations'],
|
||||||
|
'value': utxo['satoshis'],
|
||||||
})
|
})
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
|
@ -66,10 +67,28 @@ class ExplorerBitAps(Explorer):
|
||||||
|
|
||||||
def getBalance(self, address):
|
def getBalance(self, address):
|
||||||
data = json.loads(self.readURL(self.base_url + '/address/state/' + address))
|
data = json.loads(self.readURL(self.base_url + '/address/state/' + address))
|
||||||
return data
|
return data['data']['balance']
|
||||||
|
|
||||||
def lookupUnspentByAddress(self, address):
|
def lookupUnspentByAddress(self, address):
|
||||||
return json.loads(self.readURL(self.base_url + '/address/transactions/' + address))
|
# Can't get unspents return only if exactly one transaction exists
|
||||||
|
data = json.loads(self.readURL(self.base_url + '/address/transactions/' + address))
|
||||||
|
try:
|
||||||
|
assert(data['data']['list'] == 1)
|
||||||
|
except Exception as ex:
|
||||||
|
self.log.debug('Explorer error: {}'.format(str(ex)))
|
||||||
|
return None
|
||||||
|
tx = data['data']['list'][0]
|
||||||
|
tx_data = json.loads(self.readURL(self.base_url + '/transaction/{}'.format(tx['txId'])))['data']
|
||||||
|
|
||||||
|
for i, vout in tx_data['vOut'].items():
|
||||||
|
if vout['address'] == address:
|
||||||
|
return [{
|
||||||
|
'txid': tx_data['txId'],
|
||||||
|
'index': int(i),
|
||||||
|
'height': tx_data['blockHeight'],
|
||||||
|
'n_conf': tx_data['confirmations'],
|
||||||
|
'value': vout['value'],
|
||||||
|
}]
|
||||||
|
|
||||||
|
|
||||||
class ExplorerChainz(Explorer):
|
class ExplorerChainz(Explorer):
|
||||||
|
|
|
@ -204,7 +204,7 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||||
explorer = form_data[b'explorer'][0].decode('utf-8')
|
explorer = form_data[b'explorer'][0].decode('utf-8')
|
||||||
action = form_data[b'action'][0].decode('utf-8')
|
action = form_data[b'action'][0].decode('utf-8')
|
||||||
|
|
||||||
args = '' if not b'args' in form_data else form_data[b'args'][0].decode('utf-8')
|
args = '' if b'args' not in form_data else form_data[b'args'][0].decode('utf-8')
|
||||||
try:
|
try:
|
||||||
c, e = explorer.split('_')
|
c, e = explorer.split('_')
|
||||||
exp = swap_client.coin_clients[Coins(int(c))]['explorers'][int(e)]
|
exp = swap_client.coin_clients[Coins(int(c))]['explorers'][int(e)]
|
||||||
|
@ -331,6 +331,32 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||||
form_id=os.urandom(8).hex(),
|
form_id=os.urandom(8).hex(),
|
||||||
), 'UTF-8')
|
), 'UTF-8')
|
||||||
|
|
||||||
|
def page_settings(self, url_split, post_string):
|
||||||
|
swap_client = self.server.swap_client
|
||||||
|
|
||||||
|
messages = []
|
||||||
|
form_data = self.checkForm(post_string, 'settings', messages)
|
||||||
|
if form_data:
|
||||||
|
for name, c in swap_client.settings['chainclients'].items():
|
||||||
|
if bytes('apply_' + name, 'utf-8') in form_data:
|
||||||
|
data = {'lookups': form_data[bytes('lookups_' + name, 'utf-8')][0].decode('utf-8')}
|
||||||
|
swap_client.editSettings(name, data)
|
||||||
|
chains_formatted = []
|
||||||
|
|
||||||
|
for name, c in swap_client.settings['chainclients'].items():
|
||||||
|
chains_formatted.append({
|
||||||
|
'name': name,
|
||||||
|
'lookups': c.get('chain_lookups', 'local')
|
||||||
|
})
|
||||||
|
|
||||||
|
template = env.get_template('settings.html')
|
||||||
|
return bytes(template.render(
|
||||||
|
title=self.server.title,
|
||||||
|
h2=self.server.title,
|
||||||
|
chains=chains_formatted,
|
||||||
|
form_id=os.urandom(8).hex(),
|
||||||
|
), 'UTF-8')
|
||||||
|
|
||||||
def page_newoffer(self, url_split, post_string):
|
def page_newoffer(self, url_split, post_string):
|
||||||
swap_client = self.server.swap_client
|
swap_client = self.server.swap_client
|
||||||
|
|
||||||
|
@ -739,6 +765,8 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||||
return self.page_active(url_split, post_string)
|
return self.page_active(url_split, post_string)
|
||||||
if url_split[1] == 'wallets':
|
if url_split[1] == 'wallets':
|
||||||
return self.page_wallets(url_split, post_string)
|
return self.page_wallets(url_split, post_string)
|
||||||
|
if url_split[1] == 'settings':
|
||||||
|
return self.page_settings(url_split, post_string)
|
||||||
if url_split[1] == 'rpc':
|
if url_split[1] == 'rpc':
|
||||||
return self.page_rpc(url_split, post_string)
|
return self.page_rpc(url_split, post_string)
|
||||||
if url_split[1] == 'explorers':
|
if url_split[1] == 'explorers':
|
||||||
|
|
|
@ -8,6 +8,7 @@ Version: {{ version }}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="/wallets">View Wallets</a><br/>
|
<a href="/wallets">View Wallets</a><br/>
|
||||||
|
<a href="/settings">Settings</a><br/>
|
||||||
<a href="/rpc">RPC Console</a><br/>
|
<a href="/rpc">RPC Console</a><br/>
|
||||||
<a href="/explorers">Explorers</a><br/>
|
<a href="/explorers">Explorers</a><br/>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
27
basicswap/templates/settings.html
Normal file
27
basicswap/templates/settings.html
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{% include 'header.html' %}
|
||||||
|
|
||||||
|
<h3>Settings</h3>
|
||||||
|
|
||||||
|
{% for m in messages %}
|
||||||
|
<p>{{ m }}</p>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<form method="post">
|
||||||
|
|
||||||
|
{% for c in chains %}
|
||||||
|
<h4>{{ c.name }}</h4>
|
||||||
|
<table>
|
||||||
|
<tr><td>Chain Lookups</td><td>
|
||||||
|
<select name="lookups_{{ c.name }}">
|
||||||
|
<option value="local"{% if c.lookups=='local' %} selected{% endif %}>Local Node</option>
|
||||||
|
<option value="explorer"{% if c.lookups=='explorer' %} selected{% endif %}>Explorer</option>
|
||||||
|
</select></td></tr>
|
||||||
|
<tr><td><input type="submit" name="apply_{{ c.name }}" value="Apply"></td></tr>
|
||||||
|
</table>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<p><a href="/">home</a></p>
|
||||||
|
</body></html>
|
|
@ -383,6 +383,7 @@ def main():
|
||||||
'override_feerate': 0.002,
|
'override_feerate': 0.002,
|
||||||
'conf_target': 2,
|
'conf_target': 2,
|
||||||
'core_version_group': 18,
|
'core_version_group': 18,
|
||||||
|
'chain_lookups': 'local',
|
||||||
},
|
},
|
||||||
'litecoin': {
|
'litecoin': {
|
||||||
'connection_type': 'rpc' if 'litecoin' in with_coins else 'none',
|
'connection_type': 'rpc' if 'litecoin' in with_coins else 'none',
|
||||||
|
@ -394,6 +395,7 @@ def main():
|
||||||
'blocks_confirmed': 2,
|
'blocks_confirmed': 2,
|
||||||
'conf_target': 2,
|
'conf_target': 2,
|
||||||
'core_version_group': 17,
|
'core_version_group': 17,
|
||||||
|
'chain_lookups': 'local',
|
||||||
},
|
},
|
||||||
'bitcoin': {
|
'bitcoin': {
|
||||||
'connection_type': 'rpc' if 'bitcoin' in with_coins else 'none',
|
'connection_type': 'rpc' if 'bitcoin' in with_coins else 'none',
|
||||||
|
@ -405,6 +407,7 @@ def main():
|
||||||
'blocks_confirmed': 1,
|
'blocks_confirmed': 1,
|
||||||
'conf_target': 2,
|
'conf_target': 2,
|
||||||
'core_version_group': 18,
|
'core_version_group': 18,
|
||||||
|
'chain_lookups': 'local',
|
||||||
},
|
},
|
||||||
'namecoin': {
|
'namecoin': {
|
||||||
'connection_type': 'rpc' if 'namecoin' in with_coins else 'none',
|
'connection_type': 'rpc' if 'namecoin' in with_coins else 'none',
|
||||||
|
@ -417,6 +420,7 @@ def main():
|
||||||
'blocks_confirmed': 1,
|
'blocks_confirmed': 1,
|
||||||
'conf_target': 2,
|
'conf_target': 2,
|
||||||
'core_version_group': 18,
|
'core_version_group': 18,
|
||||||
|
'chain_lookups': 'local',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue