Exemple #1
0
def queue_transaction(bitcoin_rpc, redis, tx_id):
    """
    Transaction notification received for an address that belongs to us. Used
    by Bitcoind's `walletnotify=` hook through `bin/walletnotify.py`.

    :param redis:
        Instance of the Redis client connection.

    :param tx_id:
        Relevant transaction ID that we just learned about.
    """

    t = bitcoin_rpc.gettransaction(tx_id)
    if not t:
        # TODO: Log invalid transaction
        return

    transaction = t.__dict__
    log.debug("Relevant transaction received: %s", transaction['txid'])

    # FIXME: We don't need to store the full transaction, just the txid. (Regression)
    value = json.dumps(transaction)
    redis.rpush(KEY_CONFIRMATION_QUEUE, value)

    return process_transaction(bitcoin_rpc, redis, transaction)
Exemple #2
0
def create_wallet(bitcoin_rpc, redis, payout_address=None, callback_url=None, account=''):
    """
    Return a fresh wallet address.

    :param bitcoin_rpc:
        Instance of the Bitcoin JSONRPC client connection.

    :param redis:
        Instance of the Redis client connection.

    :param payout_address:
        If supplied, then payments to the new wallet are automatically relayed
        to the given payout_address.

    :param callback_url:
        If supplied, then a webhook is executed onto callback_url for each
        transaction.
    """
    new_wallet = redis.spop(KEY_WALLETS_SET) or bitcoin_rpc.getnewaddress(account)

    # TODO: Add support for min_amount?
    redis.set(
        KEY_PREFIX_PENDING_WALLET(new_wallet),
        json.dumps([payout_address, callback_url])
    )

    return new_wallet
Exemple #3
0
def process_transaction(bitcoin_rpc, redis, transaction, min_confirmations=5):
    log.debug("Processing transaction: %s", transaction['txid'])

    receive_details = next(d for d in transaction['details'] if d['category'] == u'receive')
    address = receive_details['address']
    amount = receive_details['amount']
    is_confirmed =  int(transaction['confirmations']) >= min_confirmations

    key = KEY_PREFIX_PENDING_WALLET(address)
    value = redis.get(key)
    if not value:
        # Wallet is discarded.
        return

    payout_address, callback_url = json.loads(value)

    if is_confirmed and payout_address:
        # FIXME: Is bitcoind ready to sendtoaddress at this point?
        # TODO: Log receipt.
        # TODO: Take fee.
        bitcoin_rpc.sendtoaddress(payout_address, amount)

    if callback_url:
        transaction_str = json.dumps(transaction)
        payload = {
            # TODO: Add nonce and signing?
            'state': 'confirmed' if is_confirmed else 'unconfirmed',
            'transaction': transaction_str,
        }

        process_callback(redis, callback_url, payload)

    return transaction
Exemple #4
0
def queue_callback(redis, callback_url, payload, num_attempts=0):
    retry_value = json.dumps({
        'num_attempts': num_attempts + 1,
        'time_attempted': int(time.time()),
        'callback_url': callback_url,
        'payload': payload,
    })
    redis.rpush(KEY_CALLBACK_QUEUE, retry_value)

    return retry_value
Exemple #5
0
def send_queued(redis, callback_url, payload, num_attempts=10):
    request_id = json.dumps([callback_url, payload])
    is_unique = redis.sadd(KEY_CALLBACK_DIRTY, request_id)
    if not is_unique:
        # Already in-progress
        return
Exemple #6
0
        if r is not None:
            data['result'] = r

    except (APIControllerError, LoginRequired), e:
        data['messages'] += [e.message]
        data['code'] = e.code
        data['status'] = 'error'

        if isinstance(e, LoginRequired):
            query = {'next': e.next or next}
            next = request.route_url('account_login', _query=query)

    # FIXME: data['messages'] += request.pop_flash()

    if format == 'redirect':
        for message in data['messages']:
            # Copy request-level messages to session storage to be displayed on
            # redirect.
            request.session.flash(message)
        return httpexceptions.HTTPSeeOther(next or '/')

    body = json.dumps(data, **encode_settings)
    return Response(body, content_type='application/json', status=data['code'])


# Exposed APIs:

@expose_api('ping', check_csrf=False)
def ping(request):
    return {'ping': 'pong'}