#!/usr/bin/env python3 from future.utils import iteritems from past.builtins import cmp from functools import cmp_to_key import http.server import base64 import io import json import threading import time import hashlib import os import sys from future.moves.urllib.parse import parse_qs from decimal import Decimal from optparse import OptionParser from twisted.internet import reactor from datetime import datetime, timedelta if sys.version_info < (3, 7): print("ERROR: this script requires at least python 3.7") exit(1) from jmbase.support import EXIT_FAILURE from jmbase import bintohex from jmclient import FidelityBondMixin, get_interest_rate, check_and_start_tor from jmclient.fidelity_bond import FidelityBondProof import sybil_attack_calculations as sybil from jmbase import get_log log = get_log() try: import matplotlib except: log.warning("matplotlib not found, charts will not be available. " "Do `pip install matplotlib` in the joinmarket virtualenv.") if 'matplotlib' in sys.modules: # https://stackoverflow.com/questions/2801882/generating-a-png-with-matplotlib-when-display-is-undefined matplotlib.use('Agg') import matplotlib.pyplot as plt from jmclient import jm_single, load_program_config, calc_cj_fee, \ get_irc_mchannels, add_base_options from jmdaemon import OrderbookWatch, MessageChannelCollection, IRCMessageChannel #TODO this is only for base58, find a solution for a client without jmbitcoin import jmbitcoin as btc from jmdaemon.protocol import * #Initial state: allow only SW offer types sw0offers = list(filter(lambda x: x[0:3] == 'sw0', offername_list)) swoffers = list(filter(lambda x: x[0:3] == 'swa' or x[0:3] == 'swr', offername_list)) filtered_offername_list = sw0offers rotateObform = '
' refresh_orderbook_form = '
' sorted_units = ('BTC', 'mBTC', 'μBTC', 'satoshi') unit_to_power = {'BTC': 8, 'mBTC': 5, 'μBTC': 2, 'satoshi': 0} sorted_rel_units = ('%', '‱', 'ppm') rel_unit_to_factor = {'%': 100, '‱': 1e4, 'ppm': 1e6} def calc_depth_data(db, value): pass def get_graph_html(fig): imbuf = io.BytesIO() fig.savefig(imbuf, format='png') b64 = base64.b64encode(imbuf.getvalue()).decode('utf-8') return '' # callback functions for displaying order data def do_nothing(arg, order, btc_unit, rel_unit): return arg def ordertype_display(ordertype, order, btc_unit, rel_unit): ordertypes = {'sw0absoffer': 'Native SW Absolute Fee', 'sw0reloffer': 'Native SW Relative Fee', 'swabsoffer': 'SW Absolute Fee', 'swreloffer': 'SW Relative Fee'} return ordertypes[ordertype] def cjfee_display(cjfee, order, btc_unit, rel_unit): if order['ordertype'] in ['swabsoffer', 'sw0absoffer']: return satoshi_to_unit(cjfee, order, btc_unit, rel_unit) elif order['ordertype'] in ['reloffer', 'swreloffer', 'sw0reloffer']: return str(Decimal(cjfee) * Decimal(rel_unit_to_factor[rel_unit])) + rel_unit def satoshi_to_unit_power(sat, power): return ("%." + str(power) + "f") % float( Decimal(sat) / Decimal(10 ** power)) def satoshi_to_unit(sat, order, btc_unit, rel_unit): return satoshi_to_unit_power(sat, unit_to_power[btc_unit]) def order_str(s, order, btc_unit, rel_unit): return str(s) def create_offerbook_table_heading(btc_unit, rel_unit): col = ' {1}\n' # .format(field,label) tableheading = '\n ' + ''.join( [ col.format('ordertype', 'Type'), col.format('counterparty', 'Counterparty'), col.format('oid', 'Order ID'), col.format('cjfee', 'Fee'), col.format('txfee', 'Miner Fee Contribution / ' + btc_unit), col.format('minsize', 'Minimum Size / ' + btc_unit), col.format('maxsize', 'Maximum Size / ' + btc_unit), col.format('bondvalue', 'Bond value / ' + btc_unit + '²') ]) + ' ' return tableheading def create_bonds_table_heading(btc_unit): tableheading = ('
' + '' + '' + '' + '' + '' + '' + '' + '' + '' ) return tableheading def create_choose_units_form(selected_btc, selected_rel): choose_units_form = ( '' + '') choose_units_form = choose_units_form.replace( '" bondtable = "" for (bond_data, utxo_data), bond_value, conf_time in zip( fidelity_bond_data, fidelity_bond_values, bond_outpoint_conf_times): if bond_value == -1 or conf_time == -1 or utxo_data == None: bond_value_str = "No data" conf_time_str = "No data" utxo_value_str = "No data" else: bond_value_str = satoshi_to_unit_power(bond_value, 2*unit_to_power[btc_unit]) conf_time_str = str(datetime.utcfromtimestamp(0) + timedelta(seconds=conf_time)) utxo_value_str = satoshi_to_unit(utxo_data["value"], None, btc_unit, 0) bondtable += ("" + elem(bond_data.maker_nick) + elem(bintohex(bond_data.utxo[0]) + ":" + str(bond_data.utxo[1])) + elem(bond_value_str) + elem((datetime.utcfromtimestamp(0) + timedelta(seconds=bond_data.locktime)).strftime("%Y-%m-%d")) + elem(utxo_value_str) + elem(conf_time_str) + elem(str(bond_data.cert_expiry*RETARGET_INTERVAL)) + elem(bintohex(btc.mk_freeze_script(bond_data.utxo_pub, bond_data.locktime))) + "" ) heading2 = (str(len(fidelity_bond_data)) + " fidelity bonds found with " + total_btc_committed_str + " " + btc_unit + " total locked up") choose_units_form = ( '' + '') choose_units_form = choose_units_form.replace( '
CounterpartyUTXOBond value / ' + btc_unit + '²LocktimeLocked coins / ' + btc_unit + 'Confirmation timeSignature expiry heightRedeem script
" + e + "
" + decodescript_tip) def create_sybil_resistance_page(self, btc_unit): if jm_single().bc_interface == None: return "", "Calculations unavailable, requires configured bitcoin node." (fidelity_bond_data, fidelity_bond_values, bond_outpoint_conf_times) =\ get_fidelity_bond_data(self.taker) choose_units_form = ( '
' + '
') choose_units_form = choose_units_form.replace( '