basicswap_miserver/basicswap/ui.py
tecnovert deb71856e8
Poll chainstates.
Litecoin download link changed.
Fix fee comparison tx weight difference.
Remove format8.
New stalled for test bid state.
Moved sequence code to coin interfaces.
Display estimated time lock refund tx will be valid.
2021-02-03 16:01:27 +02:00

242 lines
9.5 KiB
Python

# -*- coding: utf-8 -*-
# Copyright (c) 2020-2021 tecnovert
# Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
from .util import (
make_int,
format_timestamp,
)
from .chainparams import (
Coins,
)
from .basicswap import (
SwapTypes,
BidStates,
TxStates,
TxTypes,
strTxType,
strBidState,
strTxState,
)
from .types import (
SEQUENCE_LOCK_TIME,
)
PAGE_LIMIT = 50
def tickerToCoinId(ticker):
search_str = ticker.upper()
for c in Coins:
if c.name == search_str:
return c.value
raise ValueError('Unknown coin')
def getCoinType(coin_type_ind):
# coin_type_ind can be int id or str ticker
try:
return int(coin_type_ind)
except Exception:
return tickerToCoinId(coin_type_ind)
def validateAmountString(amount, ci):
if type(amount) != str:
return
ar = amount.split('.')
if len(ar) > 1 and len(ar[1]) > ci.exp():
raise ValueError('Too many decimal places in amount {}'.format(amount))
def inputAmount(amount_str, ci):
validateAmountString(amount_str, ci)
return make_int(amount_str, ci.exp())
def get_data_entry(post_data, name):
if 'is_json' in post_data:
return post_data[name]
return post_data[name.encode('utf-8')][0].decode('utf-8')
def have_data_entry(post_data, name):
if 'is_json' in post_data:
return name in post_data
return name.encode('utf-8') in post_data
def setCoinFilter(form_data, field_name):
try:
coin_type = getCoinType(get_data_entry(form_data, field_name))
except Exception:
return -1
if coin_type == -1:
return -1
try:
return Coins(coin_type)
except Exception:
raise ValueError('Unknown Coin Type {}'.format(str(field_name)))
def getTxIdHex(bid, tx_type, suffix):
if tx_type == TxTypes.ITX:
obj = bid.initiate_tx
elif tx_type == TxTypes.PTX:
obj = bid.participate_tx
else:
return 'Unknown Type'
if not obj:
return 'None'
if not obj.txid:
return 'None'
return obj.txid.hex() + suffix
def getTxSpendHex(bid, tx_type):
if tx_type == TxTypes.ITX:
obj = bid.initiate_tx
elif tx_type == TxTypes.PTX:
obj = bid.participate_tx
else:
return 'Unknown Type'
if not obj:
return 'None'
if not obj.spend_txid:
return 'None'
return obj.spend_txid.hex() + ' {}'.format(obj.spend_n)
def listBidStates():
rv = []
for s in BidStates:
rv.append((int(s), strBidState(s)))
return rv
def describeBid(swap_client, bid, xmr_swap, offer, xmr_offer, bid_events, edit_bid, show_txns, view_tx_ind=None):
ci_from = swap_client.ci(Coins(offer.coin_from))
ci_to = swap_client.ci(Coins(offer.coin_to))
ticker_from = ci_from.ticker()
ticker_to = ci_to.ticker()
state_description = ''
if offer.swap_type == SwapTypes.SELLER_FIRST:
if bid.state == BidStates.BID_SENT:
state_description = 'Waiting for seller to accept.'
elif bid.state == BidStates.BID_RECEIVED:
state_description = 'Waiting for seller to accept.'
elif bid.state == BidStates.BID_ACCEPTED:
if not bid.initiate_tx:
state_description = 'Waiting for seller to send initiate tx.'
else:
state_description = 'Waiting for initiate tx to confirm.'
elif bid.state == BidStates.SWAP_INITIATED:
state_description = 'Waiting for participate txn to be confirmed in {} chain'.format(ticker_to)
elif bid.state == BidStates.SWAP_PARTICIPATING:
if bid.was_sent:
state_description = 'Waiting for participate txn to be spent in {} chain'.format(ticker_to)
else:
state_description = 'Waiting for initiate txn to be spent in {} chain'.format(ticker_from)
elif bid.state == BidStates.SWAP_COMPLETED:
state_description = 'Swap completed'
if bid.getITxState() == TxStates.TX_REDEEMED and bid.getPTxState() == TxStates.TX_REDEEMED:
state_description += ' successfully'
else:
state_description += ', ITX ' + strTxState(bid.getITxState()) + ', PTX ' + strTxState(bid.getPTxState())
elif bid.state == BidStates.SWAP_TIMEDOUT:
state_description = 'Timed out waiting for initiate txn'
elif bid.state == BidStates.BID_ABANDONED:
state_description = 'Bid abandoned'
elif bid.state == BidStates.BID_ERROR:
state_description = bid.state_note
data = {
'amt_from': ci_from.format_amount(bid.amount),
'amt_to': ci_to.format_amount((bid.amount * offer.rate) // ci_from.COIN()),
'ticker_from': ticker_from,
'ticker_to': ticker_to,
'bid_state': strBidState(bid.state),
'state_description': state_description,
'itx_state': strTxState(bid.getITxState()),
'ptx_state': strTxState(bid.getPTxState()),
'offer_id': bid.offer_id.hex(),
'addr_from': bid.bid_addr,
'addr_fund_proof': bid.proof_address,
'created_at': format_timestamp(bid.created_at, with_seconds=True),
'expired_at': format_timestamp(bid.expire_at, with_seconds=True),
'was_sent': 'True' if bid.was_sent else 'False',
'was_received': 'True' if bid.was_received else 'False',
'initiate_tx': getTxIdHex(bid, TxTypes.ITX, ' ' + ticker_from),
'initiate_conf': 'None' if (not bid.initiate_tx or not bid.initiate_tx.conf) else bid.initiate_tx.conf,
'participate_tx': getTxIdHex(bid, TxTypes.PTX, ' ' + ticker_to),
'participate_conf': 'None' if (not bid.participate_tx or not bid.participate_tx.conf) else bid.participate_tx.conf,
'show_txns': show_txns,
'can_abandon': True if bid.state not in (BidStates.BID_ABANDONED, BidStates.SWAP_COMPLETED) else False,
'events': bid_events,
}
if edit_bid:
data['bid_state_ind'] = int(bid.state)
data['bid_states'] = listBidStates()
if show_txns:
if offer.swap_type == SwapTypes.XMR_SWAP:
txns = []
if bid.xmr_a_lock_tx:
confirms = None
if swap_client.coin_clients[ci_from.coin_type()]['chain_height'] and bid.xmr_a_lock_tx.chain_height:
confirms = (swap_client.coin_clients[ci_from.coin_type()]['chain_height'] - bid.xmr_a_lock_tx.chain_height) + 1
txns.append({'type': 'Chain A Lock', 'txid': bid.xmr_a_lock_tx.txid.hex(), 'confirms': confirms})
if bid.xmr_a_lock_spend_tx:
txns.append({'type': 'Chain A Lock Spend', 'txid': bid.xmr_a_lock_spend_tx.txid.hex()})
if bid.xmr_b_lock_tx:
confirms = None
if swap_client.coin_clients[ci_to.coin_type()]['chain_height'] and bid.xmr_b_lock_tx.chain_height:
confirms = (swap_client.coin_clients[ci_to.coin_type()]['chain_height'] - bid.xmr_b_lock_tx.chain_height) + 1
txns.append({'type': 'Chain B Lock', 'txid': bid.xmr_b_lock_tx.txid.hex(), 'confirms': confirms})
if bid.xmr_b_lock_tx and bid.xmr_b_lock_tx.spend_txid:
txns.append({'type': 'Chain B Lock Spend', 'txid': bid.xmr_b_lock_tx.spend_txid.hex()})
if xmr_swap.a_lock_refund_tx:
txns.append({'type': strTxType(TxTypes.XMR_SWAP_A_LOCK_REFUND), 'txid': xmr_swap.a_lock_refund_tx_id.hex()})
if xmr_swap.a_lock_refund_spend_tx:
txns.append({'type': strTxType(TxTypes.XMR_SWAP_A_LOCK_REFUND_SPEND), 'txid': xmr_swap.a_lock_refund_spend_tx_id.hex()})
for tx_type, tx in bid.txns.items():
if tx_type in (TxTypes.XMR_SWAP_A_LOCK_REFUND, TxTypes.XMR_SWAP_A_LOCK_REFUND_SPEND):
continue
txns.append({'type': strTxType(tx_type), 'txid': tx.txid.hex()})
data['txns'] = txns
else:
data['initiate_tx_refund'] = 'None' if not bid.initiate_txn_refund else bid.initiate_txn_refund.hex()
data['participate_tx_refund'] = 'None' if not bid.participate_txn_refund else bid.participate_txn_refund.hex()
data['initiate_tx_spend'] = getTxSpendHex(bid, TxTypes.ITX)
data['participate_tx_spend'] = getTxSpendHex(bid, TxTypes.PTX)
if offer.swap_type == SwapTypes.XMR_SWAP:
data['coin_a_lock_refund_tx_est_final'] = 'None'
if bid.xmr_a_lock_tx and bid.xmr_a_lock_tx.block_time:
if offer.lock_type == SEQUENCE_LOCK_TIME:
raw_sequence = ci_from.getExpectedSequence(offer.lock_type, offer.lock_value)
seconds_locked = ci_from.decodeSequence(raw_sequence)
data['coin_a_lock_refund_tx_est_final'] = bid.xmr_a_lock_tx.block_time + seconds_locked
data['coin_a_last_median_time'] = swap_client.coin_clients[offer.coin_from]['chain_median_time']
if view_tx_ind:
data['view_tx_ind'] = view_tx_ind
view_tx_id = bytes.fromhex(view_tx_ind)
if xmr_swap:
if view_tx_id == xmr_swap.a_lock_tx_id and xmr_swap.a_lock_tx:
data['view_tx_hex'] = xmr_swap.a_lock_tx.hex()
if view_tx_id == xmr_swap.a_lock_refund_tx_id and xmr_swap.a_lock_refund_tx:
data['view_tx_hex'] = xmr_swap.a_lock_refund_tx.hex()
if view_tx_id == xmr_swap.a_lock_refund_spend_tx_id and xmr_swap.a_lock_refund_spend_tx:
data['view_tx_hex'] = xmr_swap.a_lock_refund_spend_tx.hex()
return data