diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index 904236d..e088d03 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -183,7 +183,7 @@ class EventTypes(IntEnum): class EventLogTypes(IntEnum): - FAILED_TX_B_PUBLISH = auto() + FAILED_TX_B_LOCK_PUBLISH = auto() LOCK_TX_A_PUBLISHED = auto() LOCK_TX_B_PUBLISHED = auto() FAILED_TX_B_SPEND = auto() @@ -308,6 +308,19 @@ def getLockName(lock_type): 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): assert(lockVal >= 1), 'Bad lockVal' 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 xmr_offer.lock_time_2 = getExpectedSequence(lock_type, lock_value, coin_from) - # TODO: Dynamic fee selection - xmr_offer.a_fee_rate = make_int(0.00032595, self.ci(coin_from).exp()) + # TODO: max fee warning? + 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.00002, self.ci(coin_to).exp()) # abs fee @@ -1210,9 +1228,9 @@ class BasicSwap(BaseApp): return self.callcoinrpc(coin_type, 'getnetworkinfo')['relayfee'] def getFeeRateForCoin(self, coin_type): - # TODO: Per coin settings to override feerate override_feerate = self.coin_clients[coin_type].get('override_feerate', None) if override_feerate: + self.log.debug('Fee rate override used for %s: %f', str(coin_type), override_feerate) return override_feerate return self.ci(coin_type).get_fee_rate() @@ -1559,6 +1577,23 @@ class BasicSwap(BaseApp): session.remove() 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): 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) self.watchXmrSwap(bid, offer, xmr_swap) + self.logBidEvent(bid, EventLogTypes.LOCK_TX_A_PUBLISHED, '', session) + self.saveBidInSession(bid_id, bid, session, xmr_swap) 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) except Exception as 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: error_msg += ', retry no. {}'.format(num_retries) 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.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 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, ) 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) diff --git a/basicswap/db.py b/basicswap/db.py index 093a7d7..49a62a3 100644 --- a/basicswap/db.py +++ b/basicswap/db.py @@ -59,7 +59,9 @@ class Offer(Base): from_feerate = sa.Column(sa.BigInteger) to_feerate = sa.Column(sa.BigInteger) + # Local fields 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) states = sa.Column(sa.LargeBinary) # Packed states and times @@ -87,6 +89,7 @@ class Bid(Base): expire_at = sa.Column(sa.BigInteger) bid_addr = 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) amount_to = sa.Column(sa.BigInteger) # amount * offer.rate diff --git a/basicswap/http_server.py b/basicswap/http_server.py index 9d26395..55130ec 100644 --- a/basicswap/http_server.py +++ b/basicswap/http_server.py @@ -17,6 +17,7 @@ from jinja2 import Environment, PackageLoader from . import __version__ from .util import ( dumpj, + make_int, ) from .chainparams import ( chainparams, @@ -300,6 +301,7 @@ class HttpHandler(BaseHTTPRequestHandler): try: coin_from = Coins(int(form_data[b'coin_from'][0])) + ci_from = swap_client.ci(coin_from) except Exception: raise ValueError('Unknown Coin From') try: @@ -308,11 +310,11 @@ class HttpHandler(BaseHTTPRequestHandler): except Exception: raise ValueError('Unknown Coin To') - value_from = inputAmount(form_data[b'amt_from'][0].decode('utf-8')) - value_to = inputAmount(form_data[b'amt_to'][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'), ci_to) 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 lock_seconds = int(form_data[b'lockhrs'][0]) * 60 * 60 # TODO: More accurate rate @@ -357,7 +359,7 @@ class HttpHandler(BaseHTTPRequestHandler): except Exception: raise ValueError('Bad offer ID') 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' 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() - coin_from = Coins(offer.coin_from) - coin_to = 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) + ci_from = swap_client.ci(Coins(offer.coin_from)) + ci_to = swap_client.ci(Coins(offer.coin_to)) data = { - 'tla_from': swap_client.getTicker(coin_from), - 'tla_to': swap_client.getTicker(coin_to), + 'tla_from': ci_from.ticker(), + 'tla_to': ci_to.ticker(), 'state': strOfferState(offer.state), 'coin_from': ci_from.coin_name(), 'coin_to': ci_to.coin_name(), @@ -399,6 +397,13 @@ class HttpHandler(BaseHTTPRequestHandler): '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: data['auto_accept'] = 'True' if offer.auto_accept_bids else 'False' diff --git a/basicswap/templates/bid_xmr.html b/basicswap/templates/bid_xmr.html index d486cd7..d32016c 100644 --- a/basicswap/templates/bid_xmr.html +++ b/basicswap/templates/bid_xmr.html @@ -68,5 +68,13 @@ {% endfor %} +

Events

+ + +{% for e in data.events %} + +{% endfor %} +
AtEvent
{{ e.at | formatts }}{{ e.desc }}
+

home

diff --git a/basicswap/templates/offer.html b/basicswap/templates/offer.html index 7f32b31..4ad1638 100644 --- a/basicswap/templates/offer.html +++ b/basicswap/templates/offer.html @@ -29,8 +29,15 @@ {% if data.sent == 'True' %} Auto Accept Bids{{ data.auto_accept }} {% endif %} + +{% if data.xmr_type == true %} +Chain A offer fee rate{{ data.a_fee_rate }} +Chain A local fee rate{{ data.a_fee_rate_verify }} {% if data.a_fee_warn == true %} WARNING {% endif %} +{% endif %} + +
{% if data.show_bid_form %}

New Bid

diff --git a/basicswap/ui.py b/basicswap/ui.py index eeb2100..79dd50e 100644 --- a/basicswap/ui.py +++ b/basicswap/ui.py @@ -25,17 +25,17 @@ from .basicswap import ( PAGE_LIMIT = 50 -def validateAmountString(amount): +def validateAmountString(amount, ci): if type(amount) != str: return 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)) -def inputAmount(amount_str): - validateAmountString(amount_str) - return make_int(amount_str) +def inputAmount(amount_str, ci): + validateAmountString(amount_str, ci) + return make_int(amount_str, ci.exp()) 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['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 diff --git a/tests/basicswap/test_other.py b/tests/basicswap/test_other.py index b179723..ec1550b 100644 --- a/tests/basicswap/test_other.py +++ b/tests/basicswap/test_other.py @@ -200,6 +200,9 @@ class Test(unittest.TestCase): assert('100.00000000' == format_amount(amount_from, scale_from)) 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_to = 8 amount_from = 1 * (10 ** scale_from) @@ -209,6 +212,9 @@ class Test(unittest.TestCase): assert('1.000000000000' == format_amount(amount_from, scale_from)) 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__': unittest.main()