def add_user(): """ Register a new User. Create a User and a UserKey based on the JWS header and payload. --- operationId: addUser parameters: - name: user in: body description: A new User to add required: true schema: $ref: '#/definitions/User' responses: '200': description: "user's new key" schema: $ref: '#/definitions/UserKey' default: description: unexpected error schema: $ref: '#/definitions/errorModel' security: - kid: [] - typ: [] - alg: [] """ load_jws_from_request(request) if not hasattr(request, 'jws_header') or request.jws_header is None: return "Invalid Payload", 401 username = request.jws_payload['data'].get('username') address = request.jws_header['kid'] user = SLM_User(username=username) ses.add(user) try: ses.commit() except Exception as ie: current_app.logger.exception(ie) ses.rollback() ses.flush() return 'username taken', 400 userkey = UserKey(key=address, keytype='public', user_id=user.id, last_nonce=request.jws_payload['iat']*1000) ses.add(userkey) for plug in ps: ses.add(models.Balance(total=0, available=0, currency=ps[plug].CURRENCY, reference='open account', user_id=user.id)) try: ses.commit() except Exception as ie: current_app.logger.exception(ie) ses.rollback() ses.flush() #ses.delete(user) #ses.commit() return 'username taken', 400 jresult = jsonify2(userkey, 'UserKey') current_app.logger.info("registered user %s with key %s" % (user.id, userkey.key)) return current_app.bitjws.create_response(jresult)
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 __init__(self): for cur in json.loads(CFG.get('internal', 'CURRENCIES')): #TODO set this to maximum transaction sizes for each currency hwb = models.HWBalance(100000000000, 100000000000, cur, 'internal') ses.add(hwb) try: ses.commit() except Exception as ie: ses.rollback() ses.flush()
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_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 create_address(): """ Create a new address owned by your user. --- parameters: - name: address in: body description: The pseudo-address you would like to create. i.e. currency and network required: true schema: $ref: '#/definitions/Address' responses: '200': description: Your new address schema: $ref: '#/definitions/Address' default: description: unexpected error schema: $ref: '#/definitions/errorModel' security: - kid: [] - typ: [] - alg: [] operationId: createAddress """ currency = request.jws_payload['data'].get('currency') network = request.jws_payload['data'].get('network') state = 'active' if network.lower() in ps: try: addy = ps[network.lower()].get_new_address() except Exception as e: print type(e) print e current_app.logger.error(e) return 'wallet temporarily unavailable', 500 else: return 'Invalid network', 400 address = wm.Address(addy, currency, network, state, current_user.id) ses.add(address) try: ses.commit() except Exception as ie: ses.rollback() ses.flush() return 'Could not save address', 500 newaddy = json.loads(jsonify2(address, 'Address')) current_app.logger.info("created new address %s" % newaddy) ses.close() return current_app.bitjws.create_response(newaddy)
def create_address(): """ Create a new address owned by your user. --- parameters: - name: address in: body description: The pseudo-address you would like to create. i.e. currency and network required: true schema: $ref: '#/definitions/Address' responses: '200': description: Your new address schema: $ref: '#/definitions/Address' default: description: unexpected error schema: $ref: '#/definitions/errorModel' security: - kid: [] - typ: [] - alg: [] operationId: createAddress """ currency = request.jws_payload['data'].get('currency') network = request.jws_payload['data'].get('network') state = 'active' if network.lower() in ps: try: addy = ps[network.lower()].get_new_address() except Exception as e: print type(e) print e current_app.logger.error(e) return 'wallet temporarily unavailable', 500 else: return 'Invalid network', 400 address = models.Address(addy, currency, network, state, current_user.id) ses.add(address) try: ses.commit() except Exception as ie: ses.rollback() ses.flush() return 'Could not save address', 500 newaddy = jsonify2(address, 'Address') current_app.logger.info("created new address %s" % newaddy) return current_app.bitjws.create_response(newaddy)
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 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 test_create_debit_no_funds(): 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() try: create_debit(user, Amount("0.001 BTC"), "BTC", caddress2.address, 'Internal', "", session=ses) assert not "debit created even though not enough funds were available" except ValueError: pass
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 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 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_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 create_user(): privkey = bitjws.PrivateKey() my_pubkey = privkey.pubkey.serialize() my_address = bitjws.pubkey_to_addr(my_pubkey) username = str(my_address)[0:8] user = models.User(username=username) ses.add(user) try: ses.commit() except Exception as ie: print ie ses.rollback() ses.flush() userkey = models.UserKey(key=my_address, keytype='public', user_id=user.id, last_nonce=0) ses.add(userkey) ses.add(models.Balance(total=0, available=0, currency=CURRENCY, reference='open account', user_id=user.id)) try: ses.commit() except Exception as ie: print ie ses.rollback() ses.flush() return user
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_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")
from desw import ses from sqlalchemy_models import wallet as wm hwb = wm.HWBalance(0, 0, 'DASH', 'dash') ses.add(hwb) try: ses.commit() except Exception as ie: ses.rollback() ses.flush()
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 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_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 add_user(): """ Register a new User. Create a User and a UserKey based on the JWS header and payload. --- operationId: addUser parameters: - name: user in: body description: A new User to add required: true schema: $ref: '#/definitions/User' responses: '200': description: "user's new key" schema: $ref: '#/definitions/UserKey' default: description: unexpected error schema: $ref: '#/definitions/errorModel' security: - kid: [] - typ: [] - alg: [] """ load_jws_from_request(request) if not hasattr(request, 'jws_header') or request.jws_header is None: return "Invalid Payload", 401 username = request.jws_payload['data'].get('username') address = request.jws_header['kid'] user = SLM_User(username=username) ses.add(user) try: ses.commit() except Exception as ie: current_app.logger.exception(ie) ses.rollback() ses.flush() return 'username taken', 400 userkey = UserKey(key=address, keytype='public', user_id=user.id, last_nonce=request.jws_payload['iat'] * 1000) ses.add(userkey) for cur in json.loads(CFG.get('internal', 'CURRENCIES')): ses.add( models.Balance(total=0, available=0, currency=cur, reference='open account', user_id=user.id)) try: ses.commit() except Exception as ie: current_app.logger.exception(ie) ses.rollback() ses.flush() #ses.delete(user) #ses.commit() return 'username taken', 400 jresult = jsonify2(userkey, 'UserKey') current_app.logger.info("registered user %s with key %s" % (user.id, userkey.key)) return current_app.bitjws.create_response(jresult)
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 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()
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