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)
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
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
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
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
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'}