def test_receive_already_confirmed(): txs = create_client().listtransactions("*", 100) tx = None for t in txs: if t['confirmations'] >= float(CFG.get('bitcoin', 'CONFS')) and t['category'] == 'receive': tx = t break if not tx: "skipping test_receive_already_confirmed" return user = create_user() assign_address(tx['address'], user) main(['transaction', tx['txid']]) for i in range(0, 50): c = ses.query(models.Credit).filter(models.Credit.address == tx['address']).first() if c is not None: break else: time.sleep(0.1) assert c is not None assert c.address == tx['address'] assert c.amount == int(float(tx['amount']) * 1e8) assert c.currency == CURRENCY assert c.network == NETWORK assert c.state == 'complete' assert c.user_id == user.id assert tx['txid'] in c.ref_id bal = ses.query(models.Balance).filter(models.Balance.user_id == user.id).filter(models.Balance.currency == CURRENCY).first() assert bal.total == int(float(tx['amount']) * 1e8) assert bal.available == int(float(tx['amount']) * 1e8)
def process_receive(txid, details, confirmed=False): """ Process an incoming transaction with the given txid and details. If valid and new, create a Credit and update the corresponding Balance. :param str txid: The txid for the transaction in question :param dict details: The transaction details as returned by rpc client. :param bool confirmed: Has this transaction received enough confirmations? """ creds = ses.query(wm.Credit).filter(wm.Credit.ref_id == txid) if creds.count() > 0: logger.info("txid already known. returning.") return transaction_state = 'complete' if confirmed else 'unconfirmed' addy = ses.query(wm.Address)\ .filter(wm.Address.address == details['address']).first() if not addy: logger.warning("address not known. returning.") return amount = Amount("%s %s" % (float(details['amount']), CURRENCIES[0])) logger.info("crediting txid %s" % txid) process_credit(amount=amount, address=details['address'], currency=CURRENCIES[0], network=NETWORK, transaction_state=transaction_state, reference='tx received', ref_id=txid, user_id=addy.user_id) adjust_hw_balance(CURRENCIES[0], NETWORK, available=None, total=amount)
def search_debit(): """ Get one to ten debit(s) for a single User. --- parameters: - name: searchcd in: body description: The Debit(s) you'd like to get. required: false schema: $ref: '#/definitions/SearchCD' responses: '200': description: the User's debit(s) schema: items: $ref: '#/definitions/Debit' type: array default: description: unexpected error schema: $ref: '#/definitions/errorModel' security: - kid: [] - typ: [] - alg: [] operationId: searchDebits """ sid = request.jws_payload['data'].get('id') address = request.jws_payload['data'].get('address') currency = request.jws_payload['data'].get('currency') network = request.jws_payload['data'].get('network') #reference = request.jws_payload['data'].get('reference') ref_id = request.jws_payload['data'].get('ref_id') page = request.jws_payload['data'].get('page') or 0 debsq = ses.query( models.Debit).filter(models.Debit.user_id == current_user.id) if not debsq: return None if sid: debsq = debsq.filter(models.Debit.id == sid) if address: debsq = debsq.filter(models.Debit.address == address) if currency: debsq = debsq.filter(models.Debit.currency == currency) if network: debsq = debsq.filter(models.Debit.network == network) #if reference: # debsq = debsq.filter(models.Debit.reference == reference) if ref_id: debsq = debsq.filter(models.Debit.ref_id == ref_id) debsq = debsq.order_by(models.Debit.time.desc()).limit(10) if page and isinstance(page, int): debsq = debsq.offset(page * 10) debits = [jsonify2(d, 'Debit') for d in debsq] response = current_app.bitjws.create_response(debits) return response
def get_balance(): """ Get the latest balance(s) for a single User. Currently no search parameters are supported. All balances returned. --- responses: '200': description: the User's balance(s) schema: items: $ref: '#/definitions/Balance' type: array default: description: unexpected error schema: $ref: '#/definitions/errorModel' security: - kid: [] - typ: [] - alg: [] operationId: getBalance """ balsq = ses.query( models.Balance).filter(models.Balance.user_id == current_user.id) if not balsq: return None bals = [jsonify2(b, 'Balance') for b in balsq] response = current_app.bitjws.create_response(bals) return response
def search_debit(): """ Get one to ten debit(s) for a single User. --- parameters: - name: searchcd in: body description: The Debit(s) you'd like to get. required: false schema: $ref: '#/definitions/SearchCD' responses: '200': description: the User's debit(s) schema: items: $ref: '#/definitions/Debit' type: array default: description: unexpected error schema: $ref: '#/definitions/errorModel' security: - kid: [] - typ: [] - alg: [] operationId: searchDebits """ sid = request.jws_payload['data'].get('id') address = request.jws_payload['data'].get('address') currency = request.jws_payload['data'].get('currency') network = request.jws_payload['data'].get('network') #reference = request.jws_payload['data'].get('reference') ref_id = request.jws_payload['data'].get('ref_id') page = request.jws_payload['data'].get('page') or 0 debsq = ses.query(wm.Debit).filter(wm.Debit.user_id == current_user.id) if not debsq: return None if sid: debsq = debsq.filter(wm.Debit.id == sid) if address: debsq = debsq.filter(wm.Debit.address == address) if currency: debsq = debsq.filter(wm.Debit.currency == currency) if network: debsq = debsq.filter(wm.Debit.network == network) #if reference: # debsq = debsq.filter(wm.Debit.reference == reference) if ref_id: debsq = debsq.filter(wm.Debit.ref_id == ref_id) debsq = debsq.order_by(wm.Debit.time.desc()).limit(10) if page and isinstance(page, int): debsq = debsq.offset(page * 10) debits = [json.loads(jsonify2(d, 'Debit')) for d in debsq] response = current_app.bitjws.create_response(debits) ses.close() return response
def get_balance(): """ Get the latest balance(s) for a single User. Currently no search parameters are supported. All balances returned. --- responses: '200': description: the User's balance(s) schema: items: $ref: '#/definitions/Balance' type: array default: description: unexpected error schema: $ref: '#/definitions/errorModel' security: - kid: [] - typ: [] - alg: [] operationId: getBalance """ balsq = ses.query(wm.Balance).filter(wm.Balance.user_id == current_user.id) if not balsq: return None bals = [json.loads(jsonify2(b, 'Balance')) for b in balsq] print "returning bals %s" % bals response = current_app.bitjws.create_response(bals) ses.close() return response
def mock_credit(address, amount): addyq = ses.query(models.Address) addy = addyq.filter(models.Address.address == address).first() if not addy: logger.warning("address not known. returning.") return return process_credit(amount, address, 'MCK', 'Mock', 'unconfirmed', 'mock credit', _gen_txid(), addy.user_id)
def internal_credit(address, amount, currency='BTC'): addyq = ses.query(models.Address) addy = addyq.filter(models.Address.address == address).first() if not addy: logger.warning("address not known. returning.") return return process_credit(amount, address, currency, 'Internal', 'unconfirmed', 'internal credit', _gen_txid(), addy.user_id)
def get_user_by_key(app, key): """ An SQLAlchemy User getting function. Get a user by public key. :param str key: the public key the user belongs to """ user = ses.query(um.User).join(um.UserKey).filter(um.UserKey.key==key).first() return user
def internal_confirm_credit(txid): credq = ses.query(models.Credit) credit = credq.filter(models.Credit.ref_id == txid).first() if not credit: logger.warning("credit not known. returning.") return credit.state = 'complete' credit.ref_id = "%s:0" % txid
def get_user_by_key(app, key): """ An SQLAlchemy User getting function. Get a user by public key. :param str key: the public key the user belongs to """ user = ses.query(SLM_User).join(UserKey).filter(UserKey.key == key).first() return user
def internal_confirm_credit(txid): credq = ses.query(models.Credit) credit = credq.filter(models.Credit.ref_id == txid).first() if not credit: logger.warning("credit not known. returning.") return credit.state = 'complete' credit.ref_id = "%s:0" % txid
def internal_credit(address, amount, currency='BTC'): addyq = ses.query(models.Address) addy = addyq.filter(models.Address.address == address).first() if not addy: logger.warning("address not known. returning.") return return process_credit(amount, address, currency, 'Internal', 'unconfirmed', 'internal credit', _gen_txid(), addy.user_id)
def assign_address(address, user): dbaddy = ses.query(models.Address).filter(models.Address.address == address).first() if dbaddy is None: ses.add(models.Address(address, CURRENCY, NETWORK, 'active', user.id)) elif dbaddy.user_id != user.id: dbaddy.user_id = user.id ses.add(dbaddy) else: return creds = ses.query(models.Credit).filter(models.Credit.address == address) for c in creds: ses.delete(c) try: ses.commit() except Exception as ie: ses.rollback() ses.flush()
def main(sys_args=sys.argv[1:]): """ The main CLI entry point. Reads the command line arguments which should be filled in by the calling wallet node. Handler for walletnotify and blocknotify. """ global lastblock client = create_client() parser = argparse.ArgumentParser() parser.add_argument("type") parser.add_argument("data") args = parser.parse_args(sys_args) typ = args.type if typ == 'transaction' and args.data is not None: txid = args.data txd = client.gettransaction(txid) confirmed = txd['confirmations'] >= CONFS for p, put in enumerate(txd['details']): if put['category'] == 'send': confirm_send(put['address'], put['amount'], ref_id="%s:%s" % (txid, p)) elif put['category'] == 'receive': process_receive("%s:%s" % (txid, p), put, confirmed) elif typ == 'block': info = client.getinfo() if info['blocks'] <= lastblock: return lastblock = info['blocks'] creds = ses.query(models.Credit)\ .filter(models.Credit.state == 'unconfirmed')\ .filter(models.Credit.network == NETWORK) for cred in creds: txid = cred.ref_id.split(':')[0] or cred.ref_id txd = client.gettransaction(txid) if txd['confirmations'] >= CONFS: cred.state = 'complete' for p, put in enumerate(txd['details']): cred.ref_id = "%s:%s" % (txd['txid'], p) ses.add(cred) try: ses.commit() except Exception as e: logger.exception(e) ses.rollback() ses.flush() # update balances total = int(float(client.getbalance("*", 0)) * 1e8) avail = int(float(info['balance']) * 1e8) hwb = models.HWBalance(avail, total, CURRENCIES[0], NETWORK.lower()) ses.add(hwb) try: ses.commit() except Exception as ie: ses.rollback() ses.flush()
def get_balance(): """ Get the wallet's balance. Returns a dict with 'available' and 'total' balances, indicating what can be spent right now, and what is the total including unconfirmed funds. :rtype: dict """ hwb = ses.query(wm.HWBalance).filter(wm.HWBalance.network == NETWORK).order_by(wm.HWBalance.time.desc()).first() return {'total': hwb.total, 'available': hwb.available}
def test_confirm_credit_by_txid(): username, address = create_username_address() user, userkey = create_user_and_key(username=username, address=address, last_nonce=time.time() * 1000, session=ses) caddress = wm.Address(_gen_txid(), "BTC", "Internal", "active", user.id) ses.add(caddress) ses.commit() credit = internal_credit(caddress.address, Amount("0.01 BTC"), session=ses) confirm_credit(txid=credit.ref_id, session=ses) bal2 = ses.query(wm.Balance).filter(wm.Balance.user_id == user.id).filter(wm.Balance.currency == 'BTC').first() assert bal2.available == Amount("0.01 BTC") assert bal2.total == Amount("0.01 BTC")
def test_process_internal_debit(): username, address = create_username_address() user, userkey = create_user_and_key(username=username, address=address, last_nonce=time.time() * 1000, session=ses) caddress = wm.Address(_gen_txid(), "BTC", "Internal", "active", user.id) ses.add(caddress) username2, address2 = create_username_address() user2, userkey2 = create_user_and_key(username=username2, address=address2, last_nonce=time.time() * 1000, session=ses) caddress2 = wm.Address(_gen_txid(), "BTC", "Internal", "active", user2.id) ses.add(caddress2) ses.commit() internal_credit(caddress.address, Amount("0.01 BTC"), state='complete', session=ses) debit = create_debit(user, Amount("0.001 BTC"), "BTC", caddress2.address, 'Internal', "", session=ses) process_debit(debit, session=ses) credit = ses.query(wm.Credit).filter(wm.Credit.ref_id == str(debit.id)).first() assert credit.amount == Amount("0.001 BTC") assert credit.transaction_state == 'complete' dbdebit = ses.query(wm.Debit).filter(wm.Debit.id == debit.id).first() assert dbdebit.transaction_state == 'complete' assert dbdebit.ref_id == str(credit.id)
def test_create_user_used_address(): username, address = create_username_address() create_user_and_key(username=username, address=address, last_nonce=time.time() * 1000, session=ses) username2, address2 = create_username_address() try: create_user_and_key(username=username2, address=address, last_nonce=time.time() * 1000, session=ses) assert not "address was already used, but did not throw IOError as expected" except IOError: pass dbuser = ses.query(um.User).filter(um.User.username == username2).first() assert dbuser is None
def test_process_unconfirmed_credit(): username, address = create_username_address() user, userkey = create_user_and_key(username=username, address=address, last_nonce=time.time() * 1000, session=ses) caddress = wm.Address(_gen_txid(), "BTC", "Internal", "active", user.id) ses.add(caddress) ses.commit() # process_credit(Amount("0.01 BTC"), address, "BTC", 'Internal', 'unconfirmed', '', ) credit = internal_credit(caddress.address, Amount("0.01 BTC"), session=ses) assert isinstance(credit, wm.Credit) bal = ses.query(wm.Balance).filter(wm.Balance.user_id == user.id).filter(wm.Balance.currency == 'BTC').first() assert bal.available == Amount("0 BTC") assert bal.total == Amount("0.01 BTC")
def test_receive_then_confirm(): txs = create_client().listtransactions("*", 100) tx = None for t in txs: if t['confirmations'] >= CONFS and t['category'] == 'receive': tx = t break if not tx: "skipping test_receive_already_confirmed" return user = create_user() assign_address(tx['address'], user) print "confirmed tx to credit: %s" % tx['txid'] main(['transaction', tx['txid']]) for i in range(0, 50): c = ses.query(models.Credit).filter(models.Credit.address == tx['address']).first() if c is not None: break else: time.sleep(0.1) assert c is not None c.state = 'unconfirmed' ses.add(c) ses.commit() main(['block', ""]) assert c.address == tx['address'] assert c.amount == int(float(tx['amount']) * 1e8) assert c.currency == CURRENCY assert c.network == NETWORK assert c.state == 'complete' assert c.user_id == user.id assert tx['txid'] in c.ref_id assert len(c.ref_id) > len(tx['txid']) bal = ses.query(models.Balance).filter(models.Balance.user_id == user.id).filter(models.Balance.currency == CURRENCY).first() assert bal.total == int(float(tx['amount']) * 1e8) assert bal.available == int(float(tx['amount']) * 1e8)
def test_adjust_user_balance(): username, address = create_username_address() user, userkey = create_user_and_key(username=username, address=address, last_nonce=time.time() * 1000, session=ses) adjust_user_balance(user.id, 'BTC', available=Amount("0 BTC"), total=Amount("0.01 BTC"), session=ses) ses.commit() bal2 = ses.query(wm.Balance).filter(wm.Balance.user_id == user.id).filter(wm.Balance.currency == 'BTC').first() assert bal2.available == Amount("0 BTC") assert bal2.total == Amount("0.01 BTC") adjust_user_balance(user.id, 'BTC', available=Amount("0.01 BTC"), total=Amount("0 BTC"), session=ses) ses.commit() bal3 = ses.query(wm.Balance).filter(wm.Balance.user_id == user.id).filter(wm.Balance.currency == 'BTC').first() bal3.load_commodities() assert bal3.available == Amount("0.01 BTC") assert bal3.total == Amount("0.01 BTC") adjust_user_balance(user.id, 'BTC', available=Amount("-0.01 BTC"), total=Amount("-0.01 BTC"), session=ses) ses.commit() bal4 = ses.query(wm.Balance).filter(wm.Balance.user_id == user.id).filter(wm.Balance.currency == 'BTC').first() bal4.load_commodities() assert bal4.available == Amount("0 BTC") assert bal4.total == Amount("0 BTC")
def test_receive(): user = create_user() address = get_new_address() assign_address(address, user) txid = testclient.sendtoaddress(address, 0.01) for i in range(0, 600): c = ses.query(models.Credit).filter(models.Credit.address == address).first() if c is not None: break else: time.sleep(0.1) assert c is not None assert c.address == address assert c.amount == int(0.01 * 1e8) assert c.currency == CURRENCY assert c.network == NETWORK assert c.state == 'unconfirmed' assert c.user_id == user.id assert txid in c.ref_id bal = ses.query(models.Balance).filter(models.Balance.user_id == user.id).filter(models.Balance.currency == CURRENCY).first() assert bal.total == int(0.01 * 1e8) assert bal.available == 0
def adjust_hwbalance(available=None, total=None): if available is None and total is None: return hwb = ses.query(models.HWBalance).filter(models.HWBalance.network == NETWORK.lower()).order_by(models.HWBalance.time.desc()).first() if available is not None: hwb.available += available if total is not None: hwb.total += total ses.add(hwb) try: ses.commit() except Exception as e: logger.exception(e) ses.rollback() ses.flush()
def get_address(): """ Get one or more existing address(es) owned by your user. --- parameters: - name: address in: body description: The address you'd like to get info about. required: false schema: $ref: '#/definitions/Address' responses: '200': description: Your new address schema: items: $ref: '#/definitions/Address' type: array default: description: unexpected error schema: $ref: '#/definitions/errorModel' security: - kid: [] - typ: [] - alg: [] operationId: getAddress """ address = request.jws_payload['data'].get('address') currency = request.jws_payload['data'].get('currency') network = request.jws_payload['data'].get('network') addysq = ses.query(wm.Address).filter(wm.Address.user_id == current_user.id) if address: addysq = addysq.filter(wm.Address.address == address) elif currency: addysq = addysq.filter(wm.Address.currency == currency) elif network: addysq = addysq.filter(wm.Address.network == network) if addysq.count() == 0: return "Invalid Request", 400 addys = [json.loads(jsonify2(a, 'Address')) for a in addysq] response = current_app.bitjws.create_response(addys) ses.close() return response
def test_process_credit(): username, address = create_username_address() user, userkey = create_user_and_key(username=username, address=address, last_nonce=time.time() * 1000, session=ses) caddress = wm.Address(_gen_txid(), "BTC", "Internal", "active", user.id) ses.add(caddress) ses.commit() # process_credit(Amount("0.01 BTC"), address, "BTC", 'Internal', 'unconfirmed', '', ) credit = internal_credit(caddress.address, Amount("0.01 BTC"), state='complete', session=ses) assert isinstance(credit, wm.Credit) bal = ses.query(wm.Balance).filter(wm.Balance.user_id == user.id).filter(wm.Balance.currency == 'BTC').first() assert bal.available == Amount("0.01 BTC") assert bal.total == Amount("0.01 BTC") try: confirm_credit(credit=credit, session=ses) assert not "confirming complete credit, but did not throw ValueError as expected" except ValueError: pass
def get_address(): """ Get one or more existing address(es) owned by your user. --- parameters: - name: address in: body description: The address you'd like to get info about. required: false schema: $ref: '#/definitions/Address' responses: '200': description: Your new address schema: items: $ref: '#/definitions/Address' type: array default: description: unexpected error schema: $ref: '#/definitions/errorModel' security: - kid: [] - typ: [] - alg: [] operationId: getAddress """ address = request.jws_payload['data'].get('address') currency = request.jws_payload['data'].get('currency') network = request.jws_payload['data'].get('network') addysq = ses.query( models.Address).filter(models.Address.user_id == current_user.id) if address: addysq = addysq.filter(models.Address.address == address) elif currency: addysq = addysq.filter(models.Address.currency == currency) elif network: addysq = addysq.filter(models.Address.network == network) if not addysq: return None addys = [jsonify2(a, 'Address') for a in addysq] response = current_app.bitjws.create_response(addys) return response
def get_last_nonce(app, key, nonce): """ Get the last_nonce used by the given key from the SQLAlchemy database. Update the last_nonce to nonce at the same time. :param str key: the public key the nonce belongs to :param int nonce: the last nonce used by this key """ uk = ses.query(UserKey).filter(UserKey.key==key)\ .filter(UserKey.last_nonce<nonce * 1000).first() if not uk: return None lastnonce = copy.copy(uk.last_nonce) # TODO Update DB record in same query as above, if possible uk.last_nonce = nonce * 1000 try: ses.commit() except Exception as e: current_app.logger.exception(e) ses.rollback() ses.flush() return lastnonce
def get_last_nonce(app, key, nonce): """ Get the last_nonce used by the given key from the SQLAlchemy database. Update the last_nonce to nonce at the same time. :param str key: the public key the nonce belongs to :param int nonce: the last nonce used by this key """ uk = ses.query(um.UserKey).filter(um.UserKey.key==key)\ .filter(um.UserKey.last_nonce<nonce * 1000).first() if not uk: return None lastnonce = copy.copy(uk.last_nonce) # TODO Update DB record in same query as above, if possible uk.last_nonce = nonce * 1000 try: ses.commit() except Exception as e: current_app.logger.exception(e) ses.rollback() ses.flush() return lastnonce
def test_create_debit(): username, address = create_username_address() user, userkey = create_user_and_key(username=username, address=address, last_nonce=time.time() * 1000, session=ses) caddress = wm.Address(_gen_txid(), "BTC", "Internal", "active", user.id) ses.add(caddress) username2, address2 = create_username_address() user2, userkey2 = create_user_and_key(username=username2, address=address2, last_nonce=time.time() * 1000, session=ses) caddress2 = wm.Address(_gen_txid(), "BTC", "Internal", "active", user2.id) ses.add(caddress2) ses.commit() internal_credit(caddress.address, Amount("0.01 BTC"), state='complete', session=ses) debit = create_debit(user, Amount("0.001 BTC"), "BTC", caddress2.address, 'Internal', "", session=ses) assert isinstance(debit, wm.Debit) assert debit.network == 'internal' assert debit.transaction_state == 'unconfirmed' txid = _gen_txid() cdebit = confirm_send(caddress2.address, 0.001, ref_id=txid, session=ses) assert cdebit.transaction_state == 'complete' assert cdebit.ref_id == txid dbdebit = ses.query(wm.Debit).filter(wm.Debit.id == debit.id).first() assert dbdebit.transaction_state == 'complete' assert dbdebit.ref_id == txid
def test_get_debits(): # generate a big credit addy = client.get_model('Address')(currency='BTC', network='Internal') address = client.address.createAddress(address=addy).result() c = internal_credit(address.address, Amount("1 BTC")) bal = ses.query(wm.Balance).filter(wm.Balance.user_id == user.id).filter(wm.Balance.currency == 'BTC').first() bal.available = bal.available + c.amount ses.add(bal) try: ses.commit() except Exception as e: ses.rollback() print "skipping test" return # send lots of debits by_id = None by_address = None for i in range(30): addy = client2.get_model('Address')(currency='BTC', network='Internal') address = client2.address.createAddress(address=addy).result() debit = client.debit.sendMoney(debit={'amount': 0.01, 'fee': CFG.get('internal', 'FEE'), 'address': address.address, 'currency': 'BTC', 'network': 'Internal', 'state': 'unconfirmed', 'reference': 'test get debits', 'ref_id': ''}).result() if i == 1: by_id = debit.id elif i == 2: by_address = address.address time.sleep(0.2) # db write time... should really check to avoid race # find all debs = client.search.searchDebits().result() assert len(debs) >= 10 # find second page debs2 = client.search.searchDebits(searchcd={'page': 1}).result() assert len(debs2) == 10 # assure that there is no overlap for d2 in debs2: for d in debs: assert d.id != d2.id # find third page debs3 = client.search.searchDebits(searchcd={'page': 2}).result() assert len(debs3) == 10 # assure that there is no overlap for d3 in debs3: for d in debs: assert d.id != d3.id for d2 in debs2: assert d2.id != d3.id # find by address debs = client.search.searchDebits(searchcd={'address': by_address}).result() assert len(debs) == 1 assert debs[0].address == by_address # find by id debs = client.search.searchDebits(searchcd={'id': by_id}).result() assert len(debs) == 1 assert debs[0].id == by_id
def create_debit(): """ Create a new debit, sending tokens out of your User's account. --- parameters: - name: debit in: body description: The debit you would like to create. required: true schema: $ref: '#/definitions/Debit' responses: '200': description: The Debit record schema: $ref: '#/definitions/Debit' default: description: unexpected error schema: $ref: '#/definitions/errorModel' security: - kid: [] - typ: [] - alg: [] operationId: sendMoney """ amount = request.jws_payload['data'].get('amount') address = request.jws_payload['data'].get('address') currency = request.jws_payload['data'].get('currency') network = request.jws_payload['data'].get('network') reference = request.jws_payload['data'].get('reference') state = 'unconfirmed' if network.lower() not in ps: return 'Invalid network', 400 dbaddy = ses.query(models.Address)\ .filter(models.Address.address == address)\ .filter(models.Address.currency == currency).first() if dbaddy is not None and dbaddy.address == address: network = 'internal' elif network == 'internal' and dbaddy is None: return "internal address not found", 400 txid = 'TBD' debit = models.Debit(amount, address, currency, network, state, reference, txid, current_user.id) ses.add(debit) bal = ses.query(models.Balance)\ .filter(models.Balance.user_id == current_user.id)\ .filter(models.Balance.currency == currency)\ .order_by(models.Balance.time.desc()).first() if not bal or bal.available < amount: return "not enough funds", 400 else: bal.total -= amount bal.available -= amount ses.add(bal) current_app.logger.info("updating balance %s" % jsonify2(bal, 'Balance')) try: ses.commit() except Exception as ie: current_app.logger.exception(ie) ses.rollback() ses.flush() return "unable to send funds", 500 if network == 'internal': bal2 = ses.query(models.Balance)\ .filter(models.Balance.user_id == dbaddy.user_id)\ .filter(models.Balance.currency == currency)\ .order_by(models.Balance.time.desc()).first() bal2.available += amount bal2.total += amount credit = models.Credit(amount, address, currency, network, 'complete', reference, debit.id, dbaddy.user_id) ses.add(bal2) ses.add(credit) current_app.logger.info("updating balance %s" % jsonify2(bal2, 'Balance')) current_app.logger.info("created new credit %s" % jsonify2(credit, 'Credit')) try: ses.commit() debit.ref_id = str(credit.id) except Exception as ie: ses.rollback() ses.flush() return "unable to send funds", 500 else: try: debit.ref_id = ps[network.lower()].send_to_address(address, float(amount) / 1e8) except Exception as e: print type(e) print e current_app.logger.error(e) return 'wallet temporarily unavailable', 500 debit.state = 'complete' try: ses.commit() except Exception as ie: current_app.logger.exception(ie) ses.rollback() ses.flush() return "Sent but unconfirmed... check again soon", 200 result = jsonify2(debit, 'Debit') current_app.logger.info("created new debit %s" % result) return current_app.bitjws.create_response(result)
def get_balance(self): hwb = ses.query(models.HWBalance).filter(models.HWBalance.network == self.NETWORK.lower()).order_by(models.HWBalance.time.desc()).first() return {'total': hwb.total, 'available': hwb.available}
def create_debit(): """ Create a new debit, sending tokens out of your User's account. --- parameters: - name: debit in: body description: The debit you would like to create. required: true schema: $ref: '#/definitions/Debit' responses: '200': description: The Debit record schema: $ref: '#/definitions/Debit' default: description: unexpected error schema: $ref: '#/definitions/errorModel' security: - kid: [] - typ: [] - alg: [] operationId: sendMoney """ amount = request.jws_payload['data'].get('amount') address = request.jws_payload['data'].get('address') currency = request.jws_payload['data'].get('currency') network = request.jws_payload['data'].get('network') reference = request.jws_payload['data'].get('reference') state = 'unconfirmed' if network.lower() not in ps: return 'Invalid network', 400 dbaddy = ses.query(models.Address)\ .filter(models.Address.address == address)\ .filter(models.Address.currency == currency).first() if dbaddy is not None and dbaddy.address == address: network = 'internal' elif network == 'internal' and dbaddy is None: return "internal address not found", 400 fee = int(CFG.get(network.lower(), 'FEE')) txid = 'TBD' debit = models.Debit(amount, fee, address, currency, network, state, reference, txid, current_user.id) ses.add(debit) bal = ses.query(models.Balance)\ .filter(models.Balance.user_id == current_user.id)\ .filter(models.Balance.currency == currency)\ .order_by(models.Balance.time.desc()).first() if not bal or bal.available < amount + fee: return "not enough funds", 400 else: bal.total -= amount + fee bal.available -= amount + fee ses.add(bal) current_app.logger.info("updating balance %s" % jsonify2(bal, 'Balance')) try: ses.commit() except Exception as ie: current_app.logger.exception(ie) ses.rollback() ses.flush() return "unable to send funds", 500 if network == 'internal': bal2 = ses.query(models.Balance)\ .filter(models.Balance.user_id == dbaddy.user_id)\ .filter(models.Balance.currency == currency)\ .order_by(models.Balance.time.desc()).first() bal2.available += amount bal2.total += amount credit = models.Credit(amount, address, currency, network, 'complete', reference, debit.id, dbaddy.user_id) ses.add(bal2) ses.add(credit) current_app.logger.info("updating balance %s" % jsonify2(bal2, 'Balance')) current_app.logger.info("created new credit %s" % jsonify2(credit, 'Credit')) try: ses.commit() debit.ref_id = str(credit.id) except Exception as ie: ses.rollback() ses.flush() return "unable to send funds", 500 else: try: debit.ref_id = ps[network.lower()].send_to_address( address, float(amount) / 1e8) except Exception as e: print type(e) print e current_app.logger.error(e) return 'wallet temporarily unavailable', 500 debit.state = 'complete' try: ses.commit() except Exception as ie: current_app.logger.exception(ie) ses.rollback() ses.flush() return "Sent but unconfirmed... check again soon", 200 result = jsonify2(debit, 'Debit') current_app.logger.info("created new debit %s" % result) return current_app.bitjws.create_response(result)
def get_balance(self): hwb = ses.query(models.HWBalance).filter( models.HWBalance.network == self.NETWORK.lower()).order_by( models.HWBalance.time.desc()).first() return {'total': hwb.total, 'available': hwb.available}
def test_money_cycle(): fee = int(CFG.get('internal', 'FEE')) # Receive Internal to user addy = client.get_model('Address')(currency='BTC', network='Internal') address = client.address.createAddress(address=addy).result() internal_credit(address.address, int(0.01 * 1e8) + fee) for i in range(0, 60): c = ses.query(models.Credit).filter( models.Credit.address == address.address).first() if c is not None: break else: time.sleep(1) assert c is not None assert c.address == address.address assert c.amount == int(0.01 * 1e8) + fee assert c.currency == 'BTC' assert c.network == 'Internal' assert len(c.ref_id) > 0 bal = ses.query( models.Balance).filter(models.Balance.user_id == user.id).filter( models.Balance.currency == 'BTC').first() assert bal.total > 0 assert bal.available == 0 bal.available += c.amount ses.add(bal) try: ses.commit() except Exception as e: ses.rollback() print "skipping test" return # send Internal internally to user 2 addy = client2.get_model('Address')(currency='BTC', network='Internal') address = client2.address.createAddress(address=addy).result() debit = client.debit.sendMoney( debit={ 'amount': int(0.01 * 1e8), 'fee': fee, 'address': address.address, 'currency': 'BTC', 'network': 'Internal', 'state': 'unconfirmed', 'reference': 'test send money internal internal', 'ref_id': '' }).result() assert debit.state == 'complete' assert debit.amount == int(0.01 * 1e8) assert debit.reference == 'test send money internal internal' assert debit.network == 'internal' for i in range(0, 60): c = ses.query(models.Credit).filter( models.Credit.address == address.address).first() if c is not None: break else: time.sleep(1) assert c is not None assert c.state == 'complete' assert c.amount == int(0.01 * 1e8) assert c.reference == 'test send money internal internal' assert c.network == 'internal' assert int(debit.ref_id) == c.id assert int(c.ref_id) == debit.id bal = ses.query( models.Balance).filter(models.Balance.user_id == user.id).filter( models.Balance.currency == 'BTC').first() assert bal.total == 0 assert bal.available == 0 bal = ses.query( models.Balance).filter(models.Balance.user_id == user2.id).filter( models.Balance.currency == 'BTC').first() assert bal.total == int(0.01 * 1e8) assert bal.available == int(0.01 * 1e8) # send BTC internally to user 2 addy = client2.get_model('Address')(currency='BTC', network='Internal') address = client2.address.createAddress(address=addy).result() debit = client2.debit.sendMoney( debit={ 'amount': int(0.0099 * 1e8), 'fee': CFG.get('internal', 'FEE'), 'address': address.address, 'currency': 'BTC', 'network': 'Internal', 'state': 'unconfirmed', 'reference': 'test send money internal internal', 'ref_id': '' }).result() assert debit.state == 'complete' assert debit.amount == int(0.0099 * 1e8) assert debit.reference == 'test send money internal internal' assert debit.network == 'internal' for i in range(0, 60): c = ses.query(models.Credit).filter( models.Credit.address == address.address).first() if c is not None: break else: time.sleep(1) assert c is not None assert c.state == 'complete' assert c.amount == int(0.0099 * 1e8) assert c.reference == 'test send money internal internal' assert c.network == 'internal' assert int(debit.ref_id) == c.id assert int(c.ref_id) == debit.id bal = ses.query( models.Balance).filter(models.Balance.user_id == user.id).filter( models.Balance.currency == 'BTC').first() assert bal.total == 0 assert bal.available == 0 bal = ses.query( models.Balance).filter(models.Balance.user_id == user2.id).filter( models.Balance.currency == 'BTC').first() assert bal.total == int(0.0099 * 1e8) assert bal.available == int(0.0099 * 1e8) # Send Internal from user2 addy = internal_address() debit = client2.debit.sendMoney( debit={ 'amount': int(0.0098 * 1e8), 'fee': CFG.get('internal', 'FEE'), 'address': addy, 'currency': 'BTC', 'network': 'Internal', 'state': 'unconfirmed', 'reference': 'test send money internal', 'ref_id': '' }).result() time.sleep(0.1) for i in range(0, 60): d = ses.query( models.Debit).filter(models.Debit.address == addy).first() if d is not None: break else: time.sleep(1) assert d is not None assert d.address == addy assert d.amount == int(0.0098 * 1e8) bal = ses.query( models.Balance).filter(models.Balance.user_id == user2.id).filter( models.Balance.currency == 'BTC') assert bal.first().total == 0 assert bal.first().available == 0
def test_get_debits(): # generate a big credit addy = client.get_model('Address')(currency='BTC', network='Internal') address = client.address.createAddress(address=addy).result() c = internal_credit(address.address, int(1 * 1e8)) bal = ses.query( models.Balance).filter(models.Balance.user_id == user.id).filter( models.Balance.currency == 'BTC').first() bal.available += c.amount ses.add(bal) try: ses.commit() except Exception as e: ses.rollback() print "skipping test" return # send lots of debits by_id = None by_address = None for i in range(30): addy = client2.get_model('Address')(currency='BTC', network='Internal') address = client2.address.createAddress(address=addy).result() debit = client.debit.sendMoney( debit={ 'amount': int(0.01 * 1e8), 'fee': CFG.get('internal', 'FEE'), 'address': address.address, 'currency': 'BTC', 'network': 'Internal', 'state': 'unconfirmed', 'reference': 'test get debits', 'ref_id': '' }).result() if i == 1: by_id = debit.id elif i == 2: by_address = address.address time.sleep(0.2) # db write time... should really check to avoid race # find all debs = client.search.searchDebits().result() assert len(debs) >= 10 # find second page debs2 = client.search.searchDebits(searchcd={'page': 1}).result() assert len(debs2) == 10 # assure that there is no overlap for d2 in debs2: for d in debs: assert d.id != d2.id # find third page debs3 = client.search.searchDebits(searchcd={'page': 2}).result() assert len(debs3) == 10 # assure that there is no overlap for d3 in debs3: for d in debs: assert d.id != d3.id for d2 in debs2: assert d2.id != d3.id # find by address debs = client.search.searchDebits(searchcd={ 'address': by_address }).result() assert len(debs) == 1 assert debs[0].address == by_address # find by id debs = client.search.searchDebits(searchcd={'id': by_id}).result() assert len(debs) == 1 assert debs[0].id == by_id
def test_money_cycle(): fee = Amount("%s BTC" % CFG.get('internal', 'FEE')) amount = Amount("%s BTC" % 0.01) # Receive Internal to user addy = client.get_model('Address')(currency='BTC', network='Internal') address = client.address.createAddress(address=addy).result() internal_credit(address.address, amount + fee, session=ses) for i in range(0, 60): c = ses.query(wm.Credit).filter(wm.Credit.address == address.address).first() if c is not None: break else: time.sleep(1) assert c is not None assert c.address == address.address assert c.amount == amount + fee assert c.currency == 'BTC' assert c.network == 'Internal' assert len(c.ref_id) > 0 # ses.close() bal = ses.query(wm.Balance).filter(wm.Balance.user_id == user.id).filter(wm.Balance.currency == 'BTC').first() assert bal.total > Amount("0 BTC") assert bal.available == Amount("0 BTC") bal.available = bal.available + c.amount ses.add(bal) try: ses.commit() except Exception as e: ses.rollback() print "skipping test" return ses.close() # send Internal internally to user 2 addy = client2.get_model('Address')(currency='BTC', network='Internal') address = client2.address.createAddress(address=addy).result() debit = client.debit.sendMoney(debit={'amount': 0.01, 'fee': fee.to_double(), 'address': address.address, 'currency': 'BTC', 'network': 'Internal', 'state': 'unconfirmed', 'reference': 'test send money internal internal', 'ref_id': ''}).result() assert debit.transaction_state == 'complete' assert debit.amount == 0.01 assert debit.reference == 'test send money internal internal' assert debit.network == 'internal' for i in range(0, 60): c = ses.query(wm.Credit).filter(wm.Credit.address == address.address).first() if c is not None: break else: time.sleep(1) assert c is not None assert c.transaction_state == 'complete' assert c.amount == Amount("0.01 BTC") assert c.reference == 'test send money internal internal' assert c.network == 'internal' assert int(debit.ref_id) == c.id assert int(c.ref_id) == debit.id bal = ses.query(wm.Balance).filter(wm.Balance.user_id == user.id).filter(wm.Balance.currency == 'BTC').first() assert bal.total == Amount("0 BTC") assert bal.available == Amount("0 BTC") bal = ses.query(wm.Balance).filter(wm.Balance.user_id == user2.id).filter(wm.Balance.currency == 'BTC').first() assert bal.total == Amount("0.01 BTC") assert bal.available == Amount("0.01 BTC") ses.close() # send BTC internally to user 2 addy = client2.get_model('Address')(currency='BTC', network='Internal') address = client2.address.createAddress(address=addy).result() debit = client2.debit.sendMoney(debit={'amount': 0.0099, 'fee': CFG.get('internal', 'FEE'), 'address': address.address, 'currency': 'BTC', 'network': 'Internal', 'state': 'unconfirmed', 'reference': 'test send money internal internal', 'ref_id': ''}).result() assert debit.transaction_state == 'complete' assert debit.amount == 0.0099 assert debit.reference == 'test send money internal internal' assert debit.network == 'internal' for i in range(0, 60): c = ses.query(wm.Credit).filter(wm.Credit.address == address.address).first() if c is not None: break else: time.sleep(1) assert c is not None assert c.transaction_state == 'complete' assert c.amount == Amount("0.0099 BTC") assert c.reference == 'test send money internal internal' assert c.network == 'internal' assert int(debit.ref_id) == c.id assert int(c.ref_id) == debit.id bal = ses.query(wm.Balance).filter(wm.Balance.user_id == user.id).filter(wm.Balance.currency == 'BTC').first() assert bal.total == Amount("0 BTC") assert bal.available == Amount("0 BTC") bal = ses.query(wm.Balance).filter(wm.Balance.user_id == user2.id).filter(wm.Balance.currency == 'BTC').first() assert bal.total == Amount("0.0099 BTC") assert bal.available == Amount("0.0099 BTC") ses.close() # Send Internal from user2 addy = internal_address() debit = client2.debit.sendMoney(debit={'amount': 0.0098, 'fee': CFG.get('internal', 'FEE'), 'address': addy, 'currency': 'BTC', 'network': 'Internal', 'state': 'unconfirmed', 'reference': 'test send money internal', 'ref_id': ''}).result() time.sleep(0.1) for i in range(0, 60): d = ses.query(wm.Debit).filter(wm.Debit.address == addy).first() if d is not None: break else: time.sleep(1) assert d is not None assert d.address == addy assert d.amount == Amount("0.0098 BTC") bal = ses.query(wm.Balance).filter(wm.Balance.user_id == user2.id).filter(wm.Balance.currency == 'BTC') assert bal.first().total == Amount("0 BTC") assert bal.first().available == Amount("0 BTC")
def test_money_cycle(): fee = int(CFG.get('internal', 'FEE')) # Receive Internal to user addy = client.get_model('Address')(currency='BTC', network='Internal') address = client.address.createAddress(address=addy).result() internal_credit(address.address, int(0.01 * 1e8) + fee) for i in range(0, 60): c = ses.query(models.Credit).filter(models.Credit.address == address.address).first() if c is not None: break else: time.sleep(1) assert c is not None assert c.address == address.address assert c.amount == int(0.01 * 1e8) + fee assert c.currency == 'BTC' assert c.network == 'Internal' assert len(c.ref_id) > 0 bal = ses.query(models.Balance).filter(models.Balance.user_id == user.id).filter(models.Balance.currency == 'BTC').first() assert bal.total > 0 assert bal.available == 0 bal.available += c.amount ses.add(bal) try: ses.commit() except Exception as e: ses.rollback() print "skipping test" return # send Internal internally to user 2 addy = client2.get_model('Address')(currency='BTC', network='Internal') address = client2.address.createAddress(address=addy).result() debit = client.debit.sendMoney(debit={'amount': int(0.01 * 1e8), 'fee': fee, 'address': address.address, 'currency': 'BTC', 'network': 'Internal', 'state': 'unconfirmed', 'reference': 'test send money internal internal', 'ref_id': ''}).result() assert debit.state == 'complete' assert debit.amount == int(0.01 * 1e8) assert debit.reference == 'test send money internal internal' assert debit.network == 'internal' for i in range(0, 60): c = ses.query(models.Credit).filter(models.Credit.address == address.address).first() if c is not None: break else: time.sleep(1) assert c is not None assert c.state == 'complete' assert c.amount == int(0.01 * 1e8) assert c.reference == 'test send money internal internal' assert c.network == 'internal' assert int(debit.ref_id) == c.id assert int(c.ref_id) == debit.id bal = ses.query(models.Balance).filter(models.Balance.user_id == user.id).filter(models.Balance.currency == 'BTC').first() assert bal.total == 0 assert bal.available == 0 bal = ses.query(models.Balance).filter(models.Balance.user_id == user2.id).filter(models.Balance.currency == 'BTC').first() assert bal.total == int(0.01 * 1e8) assert bal.available == int(0.01 * 1e8) # send BTC internally to user 2 addy = client2.get_model('Address')(currency='BTC', network='Internal') address = client2.address.createAddress(address=addy).result() debit = client2.debit.sendMoney(debit={'amount': int(0.0099 * 1e8), 'fee': CFG.get('internal', 'FEE'), 'address': address.address, 'currency': 'BTC', 'network': 'Internal', 'state': 'unconfirmed', 'reference': 'test send money internal internal', 'ref_id': ''}).result() assert debit.state == 'complete' assert debit.amount == int(0.0099 * 1e8) assert debit.reference == 'test send money internal internal' assert debit.network == 'internal' for i in range(0, 60): c = ses.query(models.Credit).filter(models.Credit.address == address.address).first() if c is not None: break else: time.sleep(1) assert c is not None assert c.state == 'complete' assert c.amount == int(0.0099 * 1e8) assert c.reference == 'test send money internal internal' assert c.network == 'internal' assert int(debit.ref_id) == c.id assert int(c.ref_id) == debit.id bal = ses.query(models.Balance).filter(models.Balance.user_id == user.id).filter(models.Balance.currency == 'BTC').first() assert bal.total == 0 assert bal.available == 0 bal = ses.query(models.Balance).filter(models.Balance.user_id == user2.id).filter(models.Balance.currency == 'BTC').first() assert bal.total == int(0.0099 * 1e8) assert bal.available == int(0.0099 * 1e8) # Send Internal from user2 addy = internal_address() debit = client2.debit.sendMoney(debit={'amount': int(0.0098 * 1e8), 'fee': CFG.get('internal', 'FEE'), 'address': addy, 'currency': 'BTC', 'network': 'Internal', 'state': 'unconfirmed', 'reference': 'test send money internal', 'ref_id': ''}).result() time.sleep(0.1) for i in range(0, 60): d = ses.query(models.Debit).filter(models.Debit.address == addy).first() if d is not None: break else: time.sleep(1) assert d is not None assert d.address == addy assert d.amount == int(0.0098 * 1e8) bal = ses.query(models.Balance).filter(models.Balance.user_id == user2.id).filter(models.Balance.currency == 'BTC') assert bal.first().total == 0 assert bal.first().available == 0
def main(sys_args=sys.argv[1:]): """ The main CLI entry point. Reads the command line arguments which should be filled in by the calling wallet node. Handler for walletnotify and blocknotify. """ global lastblock client = create_client() parser = argparse.ArgumentParser() parser.add_argument("type") parser.add_argument("data") args = parser.parse_args(sys_args) typ = args.type if typ == 'transaction' and args.data is not None: txid = args.data txd = client.gettransaction(txid) confirmed = txd['confirmations'] >= CONFS for p, put in enumerate(txd['details']): if put['category'] == 'send': try: confirm_send(put['address'], put['amount'], ref_id=txid) except ValueError as ve: logger.info(str(ve)) elif put['category'] == 'receive': try: process_receive(txid, put, confirmed) except ValueError as ve: logger.info(str(ve)) elif typ == 'block': info = client.getinfo() if info['blocks'] <= lastblock: return lastblock = info['blocks'] creds = ses.query(wm.Credit)\ .filter(wm.Credit.transaction_state == 'unconfirmed')\ .filter(wm.Credit.network == NETWORK) modified = False for cred in creds: txid = cred.ref_id.split(':')[0] or cred.ref_id txd = client.gettransaction(txid) if txd['confirmations'] >= CONFS or \ txd['bcconfirmations'] >= CONFS: cred.load_commodities() confirm_credit(credit=cred, txid=txd['txid'], session=ses) # cred.transaction_state = 'complete' # for p, put in enumerate(txd['details']): # cred.ref_id = "%s:%s" % (txd['txid'], p) # ses.add(cred) modified = True if modified: try: ses.commit() except Exception as e: logger.exception(e) ses.rollback() ses.flush() # update balances total = Amount("%s %s" % (client.getbalance("*", 0), CURRENCIES[0])) avail = Amount("%s %s" % (info['balance'], CURRENCIES[0])) hwb = wm.HWBalance(avail, total, CURRENCIES[0], NETWORK) ses.add(hwb) try: ses.commit() except Exception as ie: ses.rollback() ses.flush() ses.close()