Dynamic fee selection.

Display xmr offer fees.
Display bid events.
html create offer uses correct coin amount scales.
This commit is contained in:
tecnovert 2020-12-08 20:23:00 +02:00
parent 2346858145
commit 3c4c2c528f
No known key found for this signature in database
GPG key ID: 8ED6D8750C4E3F93
7 changed files with 93 additions and 23 deletions

View file

@ -183,7 +183,7 @@ class EventTypes(IntEnum):
class EventLogTypes(IntEnum): class EventLogTypes(IntEnum):
FAILED_TX_B_PUBLISH = auto() FAILED_TX_B_LOCK_PUBLISH = auto()
LOCK_TX_A_PUBLISHED = auto() LOCK_TX_A_PUBLISHED = auto()
LOCK_TX_B_PUBLISHED = auto() LOCK_TX_B_PUBLISHED = auto()
FAILED_TX_B_SPEND = auto() FAILED_TX_B_SPEND = auto()
@ -308,6 +308,19 @@ def getLockName(lock_type):
return 'time' return 'time'
def describeEventEntry(event_type, event_msg):
if event_type == EventLogTypes.FAILED_TX_B_LOCK_PUBLISH:
return 'Failed to publish lock tx b'
if event_type == EventLogTypes.FAILED_TX_B_LOCK_PUBLISH:
return 'Failed to publish lock tx b'
if event_type == EventLogTypes.LOCK_TX_A_PUBLISHED:
return 'Lock tx a published'
if event_type == EventLogTypes.LOCK_TX_B_PUBLISHED:
return 'Lock tx b published'
if event_type == EventLogTypes.FAILED_TX_B_SPEND:
return 'Failed to publish lock tx b spend'
def getExpectedSequence(lockType, lockVal, coin_type): def getExpectedSequence(lockType, lockVal, coin_type):
assert(lockVal >= 1), 'Bad lockVal' assert(lockVal >= 1), 'Bad lockVal'
if lockType == SEQUENCE_LOCK_BLOCKS: if lockType == SEQUENCE_LOCK_BLOCKS:
@ -995,8 +1008,13 @@ class BasicSwap(BaseApp):
# Delay before the follower can spend from the chain a lock refund tx # Delay before the follower can spend from the chain a lock refund tx
xmr_offer.lock_time_2 = getExpectedSequence(lock_type, lock_value, coin_from) xmr_offer.lock_time_2 = getExpectedSequence(lock_type, lock_value, coin_from)
# TODO: Dynamic fee selection # TODO: max fee warning?
xmr_offer.a_fee_rate = make_int(0.00032595, self.ci(coin_from).exp()) chain_client_settings = self.getChainClientSettings(coin_from)
lock_tx_fee_premium = chain_client_settings.get('lock_tx_fee_premium', 0.0)
xmr_offer.a_fee_rate = make_int(self.getFeeRateForCoin(coin_from) + lock_tx_fee_premium, self.ci(coin_from).exp())
# Unused: TODO - Set priority?
# xmr_offer.b_fee_rate = make_int(0.0012595, self.ci(coin_to).exp()) # xmr_offer.b_fee_rate = make_int(0.0012595, self.ci(coin_to).exp())
xmr_offer.b_fee_rate = make_int(0.00002, self.ci(coin_to).exp()) # abs fee xmr_offer.b_fee_rate = make_int(0.00002, self.ci(coin_to).exp()) # abs fee
@ -1210,9 +1228,9 @@ class BasicSwap(BaseApp):
return self.callcoinrpc(coin_type, 'getnetworkinfo')['relayfee'] return self.callcoinrpc(coin_type, 'getnetworkinfo')['relayfee']
def getFeeRateForCoin(self, coin_type): def getFeeRateForCoin(self, coin_type):
# TODO: Per coin settings to override feerate
override_feerate = self.coin_clients[coin_type].get('override_feerate', None) override_feerate = self.coin_clients[coin_type].get('override_feerate', None)
if override_feerate: if override_feerate:
self.log.debug('Fee rate override used for %s: %f', str(coin_type), override_feerate)
return override_feerate return override_feerate
return self.ci(coin_type).get_fee_rate() return self.ci(coin_type).get_fee_rate()
@ -1559,6 +1577,23 @@ class BasicSwap(BaseApp):
session.remove() session.remove()
self.mxDB.release() self.mxDB.release()
def list_bid_events(self, bid_id):
self.mxDB.acquire()
events = []
try:
session = scoped_session(self.session_factory)
query_str = 'SELECT created_at, event_type, event_msg FROM eventlog ' + \
'WHERE linked_type = {} AND linked_id = x\'{}\' '.format(TableTypes.BID, bid_id.hex())
q = self.engine.execute(query_str)
for row in q:
events.append({'at': row[0], 'desc': describeEventEntry(row[1], row[2])})
return events
finally:
session.close()
session.remove()
self.mxDB.release()
def acceptBid(self, bid_id): def acceptBid(self, bid_id):
self.log.info('Accepting bid %s', bid_id.hex()) self.log.info('Accepting bid %s', bid_id.hex())
@ -3822,6 +3857,8 @@ class BasicSwap(BaseApp):
bid.setState(BidStates.XMR_SWAP_HAVE_SCRIPT_COIN_SPEND_TX) bid.setState(BidStates.XMR_SWAP_HAVE_SCRIPT_COIN_SPEND_TX)
self.watchXmrSwap(bid, offer, xmr_swap) self.watchXmrSwap(bid, offer, xmr_swap)
self.logBidEvent(bid, EventLogTypes.LOCK_TX_A_PUBLISHED, '', session)
self.saveBidInSession(bid_id, bid, session, xmr_swap) self.saveBidInSession(bid_id, bid, session, xmr_swap)
def sendXmrBidCoinBLockTx(self, bid_id, session): def sendXmrBidCoinBLockTx(self, bid_id, session):
@ -3852,7 +3889,7 @@ class BasicSwap(BaseApp):
b_lock_tx_id = ci_to.publishBLockTx(xmr_swap.pkbv, xmr_swap.pkbs, bid.amount_to, xmr_offer.b_fee_rate) b_lock_tx_id = ci_to.publishBLockTx(xmr_swap.pkbv, xmr_swap.pkbs, bid.amount_to, xmr_offer.b_fee_rate)
except Exception as ex: except Exception as ex:
error_msg = 'publishBLockTx failed for bid {} with error {}'.format(bid_id.hex(), str(ex)) error_msg = 'publishBLockTx failed for bid {} with error {}'.format(bid_id.hex(), str(ex))
num_retries = self.countBidEvents(bid, EventLogTypes.FAILED_TX_B_PUBLISH, session) num_retries = self.countBidEvents(bid, EventLogTypes.FAILED_TX_B_LOCK_PUBLISH, session)
if num_retries > 0: if num_retries > 0:
error_msg += ', retry no. {}'.format(num_retries) error_msg += ', retry no. {}'.format(num_retries)
self.log.error(error_msg) self.log.error(error_msg)
@ -3866,7 +3903,7 @@ class BasicSwap(BaseApp):
self.setBidError(bid_id, bid, 'publishBLockTx failed: ' + str(ex), save_bid=False) self.setBidError(bid_id, bid, 'publishBLockTx failed: ' + str(ex), save_bid=False)
self.saveBidInSession(bid_id, bid, session, xmr_swap, save_in_progress=offer) self.saveBidInSession(bid_id, bid, session, xmr_swap, save_in_progress=offer)
self.logBidEvent(bid, EventLogTypes.FAILED_TX_B_PUBLISH, str_error, session) self.logBidEvent(bid, EventLogTypes.FAILED_TX_B_LOCK_PUBLISH, str_error, session)
return return
self.log.debug('Submitted lock txn %s to %s chain for bid %s', b_lock_tx_id.hex(), chainparams[coin_to]['name'], bid_id.hex()) self.log.debug('Submitted lock txn %s to %s chain for bid %s', b_lock_tx_id.hex(), chainparams[coin_to]['name'], bid_id.hex())
@ -3876,6 +3913,7 @@ class BasicSwap(BaseApp):
txid=b_lock_tx_id, txid=b_lock_tx_id,
) )
bid.xmr_b_lock_tx.setState(TxStates.TX_NONE) bid.xmr_b_lock_tx.setState(TxStates.TX_NONE)
self.logBidEvent(bid, EventLogTypes.LOCK_TX_B_PUBLISHED, '', session)
self.saveBidInSession(bid_id, bid, session, xmr_swap, save_in_progress=offer) self.saveBidInSession(bid_id, bid, session, xmr_swap, save_in_progress=offer)

View file

@ -59,7 +59,9 @@ class Offer(Base):
from_feerate = sa.Column(sa.BigInteger) from_feerate = sa.Column(sa.BigInteger)
to_feerate = sa.Column(sa.BigInteger) to_feerate = sa.Column(sa.BigInteger)
# Local fields
auto_accept_bids = sa.Column(sa.Boolean) auto_accept_bids = sa.Column(sa.Boolean)
withdraw_to_addr = sa.Column(sa.String) # Address to spend lock tx to - address from wallet if empty TODO
state = sa.Column(sa.Integer) state = sa.Column(sa.Integer)
states = sa.Column(sa.LargeBinary) # Packed states and times states = sa.Column(sa.LargeBinary) # Packed states and times
@ -87,6 +89,7 @@ class Bid(Base):
expire_at = sa.Column(sa.BigInteger) expire_at = sa.Column(sa.BigInteger)
bid_addr = sa.Column(sa.String) bid_addr = sa.Column(sa.String)
proof_address = sa.Column(sa.String) proof_address = sa.Column(sa.String)
withdraw_to_addr = sa.Column(sa.String) # Address to spend lock tx to - address from wallet if empty TODO
recovered_secret = sa.Column(sa.LargeBinary) recovered_secret = sa.Column(sa.LargeBinary)
amount_to = sa.Column(sa.BigInteger) # amount * offer.rate amount_to = sa.Column(sa.BigInteger) # amount * offer.rate

View file

@ -17,6 +17,7 @@ from jinja2 import Environment, PackageLoader
from . import __version__ from . import __version__
from .util import ( from .util import (
dumpj, dumpj,
make_int,
) )
from .chainparams import ( from .chainparams import (
chainparams, chainparams,
@ -300,6 +301,7 @@ class HttpHandler(BaseHTTPRequestHandler):
try: try:
coin_from = Coins(int(form_data[b'coin_from'][0])) coin_from = Coins(int(form_data[b'coin_from'][0]))
ci_from = swap_client.ci(coin_from)
except Exception: except Exception:
raise ValueError('Unknown Coin From') raise ValueError('Unknown Coin From')
try: try:
@ -308,11 +310,11 @@ class HttpHandler(BaseHTTPRequestHandler):
except Exception: except Exception:
raise ValueError('Unknown Coin To') raise ValueError('Unknown Coin To')
value_from = inputAmount(form_data[b'amt_from'][0].decode('utf-8')) value_from = inputAmount(form_data[b'amt_from'][0].decode('utf-8'), ci_from)
value_to = inputAmount(form_data[b'amt_to'][0].decode('utf-8')) value_to = inputAmount(form_data[b'amt_to'][0].decode('utf-8'), ci_to)
min_bid = int(value_from) min_bid = int(value_from)
rate = int((value_to / value_from) * ci_to.COIN()) rate = int((value_to / value_from) * ci_from.COIN())
autoaccept = True if b'autoaccept' in form_data else False autoaccept = True if b'autoaccept' in form_data else False
lock_seconds = int(form_data[b'lockhrs'][0]) * 60 * 60 lock_seconds = int(form_data[b'lockhrs'][0]) * 60 * 60
# TODO: More accurate rate # TODO: More accurate rate
@ -357,7 +359,7 @@ class HttpHandler(BaseHTTPRequestHandler):
except Exception: except Exception:
raise ValueError('Bad offer ID') raise ValueError('Bad offer ID')
swap_client = self.server.swap_client swap_client = self.server.swap_client
offer = swap_client.getOffer(offer_id) offer, xmr_offer = swap_client.getXmrOffer(offer_id)
assert(offer), 'Unknown offer ID' assert(offer), 'Unknown offer ID'
messages = [] messages = []
@ -374,16 +376,12 @@ class HttpHandler(BaseHTTPRequestHandler):
sent_bid_id = swap_client.postBid(offer_id, offer.amount_from, addr_send_from=addr_from).hex() sent_bid_id = swap_client.postBid(offer_id, offer.amount_from, addr_send_from=addr_from).hex()
coin_from = Coins(offer.coin_from) ci_from = swap_client.ci(Coins(offer.coin_from))
coin_to = Coins(offer.coin_to) ci_to = swap_client.ci(Coins(offer.coin_to))
ci_from = swap_client.ci(coin_from)
ci_to = swap_client.ci(coin_to)
ticker_from = swap_client.getTicker(coin_from)
ticker_to = swap_client.getTicker(coin_to)
data = { data = {
'tla_from': swap_client.getTicker(coin_from), 'tla_from': ci_from.ticker(),
'tla_to': swap_client.getTicker(coin_to), 'tla_to': ci_to.ticker(),
'state': strOfferState(offer.state), 'state': strOfferState(offer.state),
'coin_from': ci_from.coin_name(), 'coin_from': ci_from.coin_name(),
'coin_to': ci_to.coin_name(), 'coin_to': ci_to.coin_name(),
@ -399,6 +397,13 @@ class HttpHandler(BaseHTTPRequestHandler):
'show_bid_form': show_bid_form, 'show_bid_form': show_bid_form,
} }
if xmr_offer:
int_fee_rate_now = make_int(ci_from.get_fee_rate(), ci_from.exp())
data['xmr_type'] = True
data['a_fee_rate'] = ci_from.format_amount(xmr_offer.a_fee_rate)
data['a_fee_rate_verify'] = ci_from.format_amount(int_fee_rate_now)
data['a_fee_warn'] = xmr_offer.a_fee_rate < int_fee_rate_now
if offer.was_sent: if offer.was_sent:
data['auto_accept'] = 'True' if offer.auto_accept_bids else 'False' data['auto_accept'] = 'True' if offer.auto_accept_bids else 'False'

View file

@ -68,5 +68,13 @@
{% endfor %} {% endfor %}
</table> </table>
<h4>Events</h4>
<table>
<tr><th>At</th><th>Event</th></tr>
{% for e in data.events %}
<tr><td>{{ e.at | formatts }}</td><td>{{ e.desc }}</td></tr>
{% endfor %}
</table>
<p><a href="/">home</a></p> <p><a href="/">home</a></p>
</body></html> </body></html>

View file

@ -29,8 +29,15 @@
{% if data.sent == 'True' %} {% if data.sent == 'True' %}
<tr><td>Auto Accept Bids</td><td>{{ data.auto_accept }}</td></tr> <tr><td>Auto Accept Bids</td><td>{{ data.auto_accept }}</td></tr>
{% endif %} {% endif %}
{% if data.xmr_type == true %}
<tr><td>Chain A offer fee rate</td><td>{{ data.a_fee_rate }}</td></tr>
<tr><td>Chain A local fee rate</td><td>{{ data.a_fee_rate_verify }} {% if data.a_fee_warn == true %} WARNING {% endif %}</td></tr>
{% endif %}
</table> </table>
<form method="post"> <form method="post">
{% if data.show_bid_form %} {% if data.show_bid_form %}
<br/><h4>New Bid</h4> <br/><h4>New Bid</h4>

View file

@ -25,17 +25,17 @@ from .basicswap import (
PAGE_LIMIT = 50 PAGE_LIMIT = 50
def validateAmountString(amount): def validateAmountString(amount, ci):
if type(amount) != str: if type(amount) != str:
return return
ar = amount.split('.') ar = amount.split('.')
if len(ar) > 1 and len(ar[1]) > 8: if len(ar) > 1 and len(ar[1]) > ci.exp():
raise ValueError('Too many decimal places in amount {}'.format(amount)) raise ValueError('Too many decimal places in amount {}'.format(amount))
def inputAmount(amount_str): def inputAmount(amount_str, ci):
validateAmountString(amount_str) validateAmountString(amount_str, ci)
return make_int(amount_str) return make_int(amount_str, ci.exp())
def setCoinFilter(form_data, field_name): def setCoinFilter(form_data, field_name):
@ -169,4 +169,7 @@ def describeBid(swap_client, bid, offer, edit_bid, show_txns):
data['initiate_tx_spend'] = getTxSpendHex(bid, TxTypes.ITX) data['initiate_tx_spend'] = getTxSpendHex(bid, TxTypes.ITX)
data['participate_tx_spend'] = getTxSpendHex(bid, TxTypes.PTX) data['participate_tx_spend'] = getTxSpendHex(bid, TxTypes.PTX)
if offer.swap_type == SwapTypes.XMR_SWAP:
data['events'] = swap_client.list_bid_events(bid.bid_id)
return data return data

View file

@ -200,6 +200,9 @@ class Test(unittest.TestCase):
assert('100.00000000' == format_amount(amount_from, scale_from)) assert('100.00000000' == format_amount(amount_from, scale_from))
assert('10.000000000000' == format_amount(amount_to, scale_to)) assert('10.000000000000' == format_amount(amount_to, scale_to))
rate_check = int((amount_to / amount_from) * (10 ** scale_from))
assert(rate == rate_check)
scale_from = 12 scale_from = 12
scale_to = 8 scale_to = 8
amount_from = 1 * (10 ** scale_from) amount_from = 1 * (10 ** scale_from)
@ -209,6 +212,9 @@ class Test(unittest.TestCase):
assert('1.000000000000' == format_amount(amount_from, scale_from)) assert('1.000000000000' == format_amount(amount_from, scale_from))
assert('12.00000000' == format_amount(amount_to, scale_to)) assert('12.00000000' == format_amount(amount_to, scale_to))
rate_check = int((amount_to / amount_from) * (10 ** scale_from))
assert(rate == rate_check)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()