def home(request):
    return TemplateResponse(request, "home.html", {
        'supported_currencies': get_balance_currencies,
        'block_info_currencies': block_info_currencies,
    }) @csrf_exempt
def perform_lookup(request, service_mode, service_id):
    """
    Passes on this request to the API, then return their response normalized to
    a single API. service_mode == (address_balance, historical_transactions)
    """
    include_raw = request.GET.get('include_raw', False)

    extended_fetch = (
        request.GET.get('extended_fetch', 'false').lower() == 'true' and
        service_mode == 'historical_transactions'
    )

    paranoid = service_id.startswith("paranoid")
    average_mode = service_id.startswith("average")
    private_mode = service_id.startswith("private")
    random_mode = False

    if service_id == "random":
        service_id = "fallback"
        random_mode = True

    if service_id == "fallback" or paranoid or average_mode or private_mode:
        Service = None
    else:
        try:
            Service = services_by_id[int(service_id)]
        except IndexError:
            return http.JsonResponse({
                'error': "Unknown Service ID: %s. This service may have been decomissioned." % service_id
            }, status=400)

    address = request.GET.get('address', None)
    addresses = request.GET.get('addresses', [])
    xpub = request.GET.get('xpub', None)
    fiat = request.GET.get('fiat', None)
    txid = request.GET.get('txid', None)

    block_args = {
        'latest': request.GET.get('latest', ''),
        'block_hash': request.GET.get("block_hash", ''),
        'block_number': request.GET.get("block_number", '')
    }

    currency = request.GET.get("currency", None)
    if service_mode == 'push_tx':
        currency = request.POST['currency']

    if not currency:
        try:
            guess_currency_result = guess_currency_from_address(address)
        except ValueError as exc:
            return http.JsonResponse({'error': str(exc)}, status=400)

        if len(guess_currency_result) == 1:
            currency, currency_name = guess_currency_result[0]
        else:
            msg = "Address may be one of %s, please specify which one by using the `currency` parameter" % (
                ', '.join([x[0] for x in guess_currency_result])
            )
            return http.JsonResponse({'error': msg}, status=400)
    else:
        currency_name = crypto_data[currency]['name']
        currency = currency.lower()

    if not currency:
        return http.JsonResponse({
            'error': "Currency Not Recognized: %s" % currency_name
        }, status=400)

    if service_mode == 'push_tx': # don't go inside cache function
        try:
            result = push_tx(currency, request.POST['tx'])
            return http.JsonResponse({
                'txid': result
            })
        except Exception as exc:
            return http.JsonResponse({
                'error': "%s: %s" % (exc.__class__.__name__, str(exc))
            }, status=500)

    errors, response_dict = _cached_fetch(**locals())
    if errors:
        return http.JsonResponse(response_dict, status=500)

    return http.JsonResponse(response_dict) def single_address(request, address):
    currency = request.GET.get('currency', None)

    if not currency:
        try:
            guess_currency_result = guess_currency_from_address(address)
        except Exception as exc:
            return TemplateResponse(request, "single_address.html", {
                'crypto_data_json': crypto_data_json,
                'service_info_json': service_info_json,
                'address': address,
                'currency': None,
                'transactions': [],
                'currency_name': None,
                'currency_icon': None,
            })

        if len(guess_currency_result) == 1:
            currency, currency_name = guess_currency_result[0]
        else:
            return http.HttpResponseRedirect(
                reverse("address_disambiguation", kwargs={'address': address})
            )
    else:
        currency = currency.lower()
        crypto_name = currency_name = crypto_data[currency]['name']

    error, response = _cached_fetch(
        currency=currency, currency_name=currency_name, service_id="fallback",
        address=address, Service=None, service_mode="historical_transactions"
    )

    if not error:
        txs = response['transactions']
    else:
        txs = []

    return TemplateResponse(request, "single_address.html", {
        'crypto_data_json': crypto_data_json,
        'service_info_json': service_info_json,
        'address': address,
        'currency': currency,
        'transactions': txs,
        'currency_name': currency_name,
        'currency_icon': "logos/" + currency.lower() + "-logo.png",
    }) def block_lookup(request):
    return TemplateResponse(request, "block_lookup.html", {
        'crypto_data_json': crypto_data_json,
        'service_info_json': service_info_json,
        'block_info_currencies': block_info_currencies,
        'currency': request.GET.get('currency', None),
        'block_hash': request.GET.get("block_hash", None),
        'block_number': request.GET.get("block_number", None)
    }) def api_docs(request):
    return TemplateResponse(request, 'api_docs.html', {
        'service_table': service_table_html,
        'domain': settings.ALLOWED_HOSTS[0],
    }) def address_disambiguation(request, address):
    """
    When the user tried to lookup a currency that has a duplicate address byte
    take them to this page where they can choose which currency they meant.
    """
    guess_currency_result = guess_currency_from_address(address)
    balances = {}
    for crypto, name in guess_currency_result:
        try:
            balance = get_address_balance(crypto, address)
        except Exception as exc:
            balance = str(exc)

        balances[crypto.upper()] = {'balance': balance, 'name': name}

    return TemplateResponse(request, "address_disambiguation.html", {
        'balances': balances,
        'address': address,
    }) def onchain_exchange_rates(request):
    """
    Returns a list of exchange pairs that are supported by all onchain exchange services.
    (currently only shapeshift.io is supported). After a second onchain exchange is defined,
    this code will be moved to moneywagon.
    """
    cache_key = 'shapeshift_marketinfo'
    hit = cache.get(cache_key)

    if not hit:
        pairs = requests.get("https://shapeshift.io/marketinfo/").json()
        cache.set(cache_key, pairs)
    else:
        pairs = hit

    deposit_currency = request.GET.get('deposit_currency', '').upper()
    final_pairs = []

    for pair in pairs:
        if deposit_currency and not pair['pair'].startswith(deposit_currency):
            continue

        deposit_code = pair['pair'].split("_")[0]
        try:
            deposit_name = crypto_data[deposit_code.lower()]['name']
        except (KeyError, TypeError):
            continue

        withdraw_code = pair['pair'].split("_")[1]

        try:
            withdraw_name = crypto_data[withdraw_code.lower()]['name']
        except (KeyError, TypeError):
            continue

        if pair['maxLimit'] == 0:
            continue

        final_pairs.append({
            'deposit_currency': {'code': deposit_code, 'name': deposit_name},
            'withdraw_currency': {'code': withdraw_code, 'name': withdraw_name},
            'rate': pair['rate'],
            'max_amount': pair['maxLimit'],
            'min_amount': pair['min'],
            'withdraw_fee': pair['minerFee'],
            'provider': 'ShapeShift.io'
        })


    return http.JsonResponse({'pairs': final_pairs}) def onchain_status(request):
    deposit_crypto = request.GET['deposit_currency']
    address = request.GET['address']

    response = requests.get("https://shapeshift.io/txStat/" + address).json()
    return http.JsonResponse(response) def logout(request):
    dj_logout(request)
    return http.HttpResponseRedirect("/") def single_tx(request, crypto, txid):
    full_tx = CachedTransaction.fetch_full_tx(crypto, txid=txid, fiat='usd')
    hist_price = full_tx['historical_price'] or 0
    if hist_price:
        hist_price = hist_price['price']
    fee = full_tx['fee'] / 1e8
    full_tx['fee'] = "%.8f %s (%.2f USD)" % (fee, crypto.upper(), fee * hist_price)
    s = full_tx['size']
    full_tx['size'] = "%.2f KB" % (s / 1024.0) if s > 1024 else "%d bytes" % s

    ins = []
    for x in full_tx['inputs']:
        amount = x['amount'] / 1e8
        ins.append({
            'txid': x['txid'],
            'amount': "%.8f %s (%.2f USD)" % (amount, crypto.upper(), hist_price * amount)
        })

    full_tx['inputs'] = ins

    outs = []
    for x in full_tx['outputs']:
        amount = x['amount'] / 1e8
        outs.append({
            'address': x['address'],
            'amount': "%.8f %s (%.2f USD)" % (amount, crypto.upper(), hist_price * amount)
        })

    full_tx['outputs'] = outs

    return TemplateResponse(request, "single_transaction.html", {
        'tx': full_tx,
        'crypto': crypto,
        'crypto_name': crypto_data[crypto]['name']
    }) @csrf_exempt
def handle_memo(request):
    if request.POST:
        return save_memo(request)
    if request.GET:
        return get_memo(request) def serve_memo_pull(request):
    """
    There are two ways to use the "Memo Pull" method: by eithr pasing in a "since timestamp",
    or by passing in a page number.

    Page number is used when performing a "full memo sync", and does not include
    records of deleted memos.

    On the other hand, "since timestamp" is used when performing a periodical
    pull. The response will include all new memos (and "Please Delete" memos)
    made since the timestamp.
    """
    if settings.MEMO_SERVER_PRIVATE_MODE:
        return http.HttpResponse("Memo Server is in Private Mode", status=401)

    if request.GET.get('since'):
        since = arrow.get(request.GET['since'])
        memos = Memo.objects.filter(created__gte=since.datetime).order_by('created')

    if request.GET.get('page'):
        start = (int(request.GET.get('page', 1)) - 1) * 100
        end = start + 100
        memos = Memo.objects.exclude(encrypted_text="Please Delete").order_by('created')[start:end]

    return http.JsonResponse({'memos': [{
        't': m.encrypted_text,
        'p': m.pubkey,
        'x': m.txid,
        'c': m.crypto,
        } for m in memos
    ]}) def historical_price(request):
    fiat = request.GET['fiat'].upper()
    crypto = request.GET['currency'].upper()
    try:
        time = arrow.get(request.GET['time']).datetime
    except:
        return http.JsonResponse({'error': "Invalid Time argument"}, status=400)

    try:
        price = PriceTick.nearest(crypto, fiat, time)
    except PriceTick.DoesNotExist:
        return http.JsonResponse(
            {'error': "Can't get historical price for %s->%s" % (fiat, crypto)},
            status=400
        )

    try:
        naive_time = time.replace(tzinfo=None)
        price['estimated_supply'] = SupplyEstimator(crypto).calculate_supply(at_time=naive_time)
    except NotImplementedError:
        pass

    price['currency'] = crypto
    return http.JsonResponse(price) def plot_supply(request):
    from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
    from matplotlib.figure import Figure
    from matplotlib.dates import DateFormatter
    currency = request.GET['currency']
    genesis = crypto_data[currency.lower()]['genesis_date']
    currency_name = crypto_data[currency.lower()]['name']
    s = SupplyEstimator(currency)

    try:
        max_block = crypto_data[currency.lower()]['supply_data']['reward_ends_at_block']
        end = s.estimate_date_from_height(max_block)
    except KeyError:
        end = genesis + datetime.timedelta(days=365 * 50)

    x = list(date_generator(genesis, end))
    y = [s.calculate_supply(at_time=z)/1e6 for z in x]

    fig = Figure()
    ax = fig.add_subplot(111)
    ax.plot_date(x, y, '-')

    ax.set_title("%s Supply" % currency_name)
    ax.grid(True)
    ax.xaxis.set_label_text("Date")
    ax.yaxis.set_label_text("%s Units (In millions)" % currency.upper())

    ax.xaxis.set_major_formatter(DateFormatter('%Y-%m-%d'))
    fig.autofmt_xdate()

    canvas = FigureCanvas(fig)
    response = http.HttpResponse(content_type='image/png')
    canvas.print_png(response)
    return response def test(request):
    return TemplateResponse(request, "test_template.html") def paper_wallet(request):
    paper_wallet_currencies = sorted(get_paper_wallet_currencies(), key=lambda x: x['name'])
    paper_wallet_currencies_json = json.dumps(paper_wallet_currencies)
    return TemplateResponse(request, "paper_wallet.html", locals()) def crypto_data_page(request, path1=None, path2=None):
    if path1 and path2:
        full_path = "%s/%s" % (path1, path2)
        hit = cache.get(full_path)
        if hit:
            return http.JsonResponse(hit)

        data = extract_crypto_data(full_path)
        cache.set(full_path, data)
        return http.JsonResponse(data)

    return TemplateResponse(request, "crypto_data.html", locals()) @csrf_exempt
def replay_attack(request, fork_coin=None, block_to_replay=None):
    if request.method == 'POST':
        errors, response_dict = _cached_fetch(
            "get_block", "fallback", currency=fork_coin,
            block_args={'block_number': block_to_replay}, random_mode=True
        )

        block = response_dict['block']

        def block_fetcher(currency, txid):
            errors, response_dict = _cached_fetch(
                "single_transaction", "fallback", currency=currency,
                txid=txid, random_mode=True
            )
            return response_dict['transaction']

        results = []
        for r in replay_block(fork_coin, block, verbose=False, block_fetcher=block_fetcher, limit=3):
            print(r)
            results.append(r)

        return http.JsonResponse({'results': results})

    first_pass = [[x, d['forked_from']] for x,d in crypto_data.items() if 'forked_from' in d]
    attack_candidates = [["%s ⇆ %s" % (x[0], x[1][0]), x[1][1]] for x in first_pass]
    return TemplateResponse(request, "replay_attack.html", locals())
from django.conf.urls import include, url
from django.contrib import admin
from django.views.generic import TemplateView
from .views import (
    home, perform_lookup, single_address, block_lookup, api_docs, address_disambiguation,
    onchain_exchange_rates, onchain_status, logout, single_tx, handle_memo, serve_memo_pull,
    historical_price, plot_supply, test, paper_wallet, crypto_data_page, replay_attack
)
from django.conf import settings


admin.site.site_header = 'MultiExplorer Administration'

urlpatterns = [
    # Examples:
    # url(r'^$', 'multiexplorer.views.home', name='home'),
    # url(r'^blog/', include('blog.urls')),

    url(r'^admin/', admin.site.urls),
    url(r'^$', home, name="home"),

    url(r'^address/(?P<address>\w+)/$', single_address, name="single_address"),

    url(r'^block', block_lookup, name="block_lookup"),