# -*- coding: utf-8 -*- # Copyright (c) 2019 tecnovert # Distributed under the MIT software license, see the accompanying # file LICENSE or http://www.opensource.org/licenses/mit-license.php. import os import json import time import struct import traceback import threading import http.client import urllib.parse from http.server import BaseHTTPRequestHandler, HTTPServer from jinja2 import Environment, PackageLoader from . import __version__ from .util import ( COIN, format8, makeInt, dumpj, ) from .chainparams import ( chainparams, Coins, ) from .basicswap import ( SwapTypes, BidStates, TxStates, TxTypes, strOfferState, strBidState, strTxState, getLockName, SEQUENCE_LOCK_TIME, ABS_LOCK_TIME, ) def format_timestamp(value): return time.strftime('%Y-%m-%d %H:%M', time.localtime(value)) env = Environment(loader=PackageLoader('basicswap', 'templates')) env.filters['formatts'] = format_timestamp PAGE_LIMIT = 50 def getCoinName(c): return chainparams[c]['name'].capitalize() def listAvailableCoins(swap_client): coins = [] for k, v in swap_client.coin_clients.items(): if v['connection_type'] == 'rpc': coins.append((int(k), getCoinName(k))) return coins def extractDomain(url): return url.split('://', 1)[1].split('/', 1)[0] def listAvailableExplorers(swap_client): explorers = [] for c in Coins: for i, e in enumerate(swap_client.coin_clients[c]['explorers']): explorers.append(('{}_{}'.format(int(c), i), swap_client.coin_clients[c]['name'].capitalize() + ' - ' + extractDomain(e.base_url))) return explorers def listExplorerActions(swap_client): actions = [('height', 'Chain Height'), ('block', 'Get Block'), ('tx', 'Get Transaction'), ('balance', 'Address Balance'), ('unspent', 'List Unspent')] return actions def listBidStates(): rv = [] for s in BidStates: rv.append((int(s), strBidState(s))) return rv def getTxIdHex(bid, tx_type, prefix): 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() + prefix 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 validateAmountString(amount): if type(amount) != str: return ar = amount.split('.') if len(ar) > 1 and len(ar[1]) > 8: raise ValueError('Too many decimal places in amount {}'.format(amount)) def inputAmount(amount_str): validateAmountString(amount_str) return makeInt(amount_str) def setCoinFilter(form_data, field_name): if field_name not in form_data: return -1 coin_type = int(form_data[field_name][0]) if coin_type == -1: return -1 try: return Coins(coin_type) except Exception: raise ValueError('Unknown Coin Type {}'.format(str(field_name))) def describeBid(swap_client, bid, offer, edit_bid, show_txns): coin_from = Coins(offer.coin_from) coin_to = Coins(offer.coin_to) ticker_from = swap_client.getTicker(coin_from) ticker_to = swap_client.getTicker(coin_to) 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: 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 else: state_description = '' data = { 'amt_from': format8(bid.amount), 'amt_to': format8((bid.amount * offer.rate) // 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': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(bid.created_at)), 'expired_at': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(bid.expire_at)), '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, } if edit_bid: data['bid_state_ind'] = int(bid.state) data['bid_states'] = listBidStates() if show_txns: 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) return data def html_content_start(title, h2=None, refresh=None): content = '\n
' \ + '' \ + ('' if not refresh else ''.format(refresh)) \ + 'Info: ' + info_str + '
' \ + '' return bytes(content, 'UTF-8') def page_error(self, error_str): content = html_content_start('BasicSwap Error') \ + 'Error: ' + error_str + '
' \ + '