def save_json_data(json_data, tag0): """ This method saves json_data given and returns saved location. :param json_string: json data to save for contract. :param tag0: should be con_id of the contract. :return: filename of newly saved qrcode. """ try: file_type = 'json' new_file_name = "{}_{}.{}".format(tag0, uuid1(), file_type) # Generate the full path of where to save the file. file_save_path = os.path.join(*[ current_app.root_path, 'PICTURES', Folders.METADATA.value, new_file_name ]) with open(file_save_path, 'w') as outfile: outfile.write(json_data) return new_file_name except Exception as e: log_kv(LOG_ERROR, { "info": "Error saving json meta data", "error": str(e) }, exception=True) return None
def post(self, data): # Get needed issuer, and contract data. collector = GetCollectorByCID().execute_n_fetchone( {'c_id': g.collector_info['c_id']}) contract = GetContractByConID().execute_n_fetchone( {'con_id': data['con_id']}, schema_out=False) try: g.geth.perform_transfer(contract['con_addr'], contract['con_abi'], data['t_id'], src_acct=collector['c_hash'], dest_acct=data['destination_wallet_hash']) UpdateTokenStatus().execute({ 'new_status': TokenStatus.EXTERNAL.value, 'this_id': data['t_id'] }) g.sesh.commit() except Exception as e: g.sesh.rollback() log_kv(LOG_ERROR, { 'error': str(e), 'message': "Could not perform external token transfer!" }, exception=True) return error_response( 'Unable to perform transfer. Please check the given wallet address', status_code=423) return success_response("Went through.")
def process_time_constraints(time_constraints, con_id): """ This method inserts all of the time constraints into the database associated to given con_id. :param time_constraints: time constraints to add. :param con_id: con_id of contract to associate constraints to. :return: """ for tc in time_constraints: try: InsertTimeConstraint().execute({ 'con_id': con_id, 'start': tc['start'].strftime(CONSTRAINT_DATETIME_FORMAT), 'end': tc['end'].strftime(CONSTRAINT_DATETIME_FORMAT) }) except SQLAlchemyError as e: log_kv( LOG_ERROR, { 'message': 'Exception trying to add time constraint.', 'contract_con_id': con_id, 'exception': str(e), 'constraints': time_constraints }) continue
def get(self): collector = GetCollectorByCID().execute_n_fetchone( {'c_id': g.collector_info['c_id']}, close_connection=True) if collector: # Try and get out the eth balance. try: balance = g.geth.get_eth_balance(collector['c_hash']) except GethException as e: log_kv(LOG_ERROR, { 'error': "Couldn't retrieve eth balance", 'c_id': g.collector_info['c_id'], 'exception': e.exception, 'geth_message': e.message }, exception=True) balance = 'Not available at this time.' collector.update({'eth_balance': balance}) return success_response(collector) else: log_kv( LOG_WARNING, { 'warning': 'could not get collector account', 'collector_id': g.collector_info['c_id'] }) return error_response(status="Couldn't retrieve collector info.", status_code=-1, http_code=200)
def delete(self, data): # Get trade specified by requester. trade = GetTradeByTRID().execute_n_fetchone(data, schema_out=False) # Make sure got a trade. if trade is None: return error_response('No trade request specified for that tr_id.') # Make sure trade is in correct state to delete. if trade['status'] != TradeStatus.REQUESTED.value: return error_response('Trade is not in a valid state to delete.') # Ensure requester has access to cancel this trade. if trade['trader_c_id'] != g.collector_info['c_id']: log_kv(LOG_INFO, {'info': "Attempted deletion of trade request not owned by authorized collector", 'c_id': g.collector_info['c_id']}) return error_response('Authorized collector does not have ownership over this trade.') # If identity has be verified update the status. data.update({'new_status': TradeStatus.CANCELED.value}) if UpdateTradeStatus().execute(data): g.sesh.commit() return success_response(status='Trade request canceled.') else: g.sesh.rollback() return error_response(status='Unable to cancel trade request.')
def get(self): """ Method to use for get requests to the /contract method. :return: """ contracts = GetContractsByIssuerID().execute_n_fetchall( {'i_id': g.issuer_info['i_id']}) if contracts is not None: log_kv( LOG_INFO, { 'message': 'succesfully retrieved issuer\'s contracts', 'issuer_id': g.issuer_info['i_id'] }) # Add the constraints to the contract object. for contract in contracts: contract.update( {'constraints': get_all_constraints(contract['con_id'])}) return success_response({'contracts': contracts}) else: log_kv( LOG_WARNING, { 'warning': 'could not get contract for issuer', 'issuer_id': g.issuer_info['i_id'] }) return error_response( status="Couldn't retrieve contract with that con_id", status_code=-1, http_code=200)
def save_qrcode(file, tag0, tag1): """ This method saves a qr code. :param file: PIL IMAGE. :param tag0: should be con_id of the contract. :param tag1: should be t_id of the contract. :return: filename of newly saved qrcode. """ try: file_type = 'png' new_file_name = "{}-{}_{}.{}".format(tag0, tag1, uuid1(), file_type) # Generate the full path of where to save the file. file_save_path = os.path.join(*[ current_app.root_path, 'PICTURES', Folders.QR_CODES.value, new_file_name ]) file.save(file_save_path) return new_file_name except Exception as e: log_kv(LOG_ERROR, { "info": "Error saving qr code", "error": str(e) }, exception=True) return None
def get_all_tradable(keyword=None): contracts = GetAllTradableContracts(keyword).execute_n_fetchall({}, close_connection=True, load_out=True) if contracts is not None: log_kv(LOG_DEBUG, {'debug': 'succesfully got all contracts'}) return success_response({'contracts': contracts}) else: log_kv(LOG_ERROR, {'warning': 'could not pull all contracts!!!'}) return error_response(status="Couldn't retrieve contracts")
def get_collection(): # Get collection for user. collection = GetCollection().execute_n_fetchall( {'c_id': g.collector_info['c_id']}, close_connection=True) if collection is not None: return success_response({'collection': collection}) else: log_kv( LOG_ERROR, { 'error': 'could not get a user\'s collection', 'collector_id': g.collector_info['c_id'] }) return error_response("Couldn't retrieve collectors collection.")
def wrapper(*args, **kwargs): schema = schema_cls(**schema_kwargs) json = request.get_json(force=True) try: if dump: data = schema.dump(json) else: data = schema.load(json) return f(data=data, *args, **kwargs) except ValidationError as err: log_kv(LOG_ERROR, { 'exception': str(err), 'error': "failed to validate schema." }, exception=True) return error_response("Validation Failed", errors=err.messages)
def get_contract_by_name(name): contracts_by_name = GetContractByName().execute_n_fetchall( {'name': '%' + name + '%'}, close_connection=True) if contracts_by_name is not None: log_kv(LOG_DEBUG, { 'debug': 'found contract by name', 'contract_name': name }) return success_response({'contracts': contracts_by_name}) else: log_kv(LOG_WARNING, { 'warning': 'could not find contract by name', 'contract_name': name }) return error_response( status="Couldn't retrieve contract with that con_id", status_code=-1, http_code=200)
def get_collector_by_username(username): collector = GetCollectorByUsername().execute_n_fetchone( {'username': username}, close_connection=True) if collector: log_kv(LOG_INFO, { 'message': 'successfully created collector', 'username': username }) return success_response(collector) else: log_kv(LOG_WARNING, { 'warning': 'could not create collector', 'username': username }) return error_response( status="Couldn't retrieve collector with that username", status_code=-1, http_code=200)
def get_contract_by_con_id(con_id): contract = GetContractByConID().execute_n_fetchone({'con_id': con_id}) constraints = get_all_constraints(con_id) g.sesh.close() if contract: log_kv(LOG_DEBUG, { 'debug': 'successfully retrieved contract', 'contract_id': con_id }) contract.update({'constraints': constraints}) return success_response({'contract': contract}) else: log_kv(LOG_WARNING, { 'warning': 'could not find contract', 'contract_id': con_id }) return error_response( status="Couldn't retrieve contract with that con_id", status_code=-1, http_code=200)
def get_issuer_jwt(data): # Gather up login details. login_deets = GetIssuerLoginDetails().execute_n_fetchone( binds=data, close_connection=True) if login_deets is None: log_kv( LOG_WARNING, { 'warning': 'could not find login details for issuer', 'username': data['username'] }) return error_response("Authorization Failed for Issuer Login.") # If we got some deets back then check if passwords match. if data['password'] == login_deets['password']: del login_deets['password'] log_kv(LOG_DEBUG, { 'debug': 'successfully logged in issuer', 'username': data['username'] }) return success_response({'jwt': generate_jwt(login_deets)}) else: log_kv( LOG_INFO, { 'message': 'authorization failed for issuer', 'username': data['username'] }) return error_response("Authorization Failed for Issuer Login.")
def post(self, data): try: data['i_hash'], data['i_priv_key'] = g.geth.create_account() issuer = create_issuer(data) g.sesh.commit() log_kv(LOG_INFO, {'message': 'successfully created issuer account'}) return success_response({'jwt': generate_jwt(issuer)}, http_code=201) except GethException as ge: g.sesh.rollback() log_kv( LOG_ERROR, { 'error': 'raised exception from geth_keeper while creating issuer account', 'exception': ge.exception, 'exc_message': ge.message }) return error_response(ge.message) except Exception as err: g.sesh.rollback() log_kv( LOG_ERROR, { 'error': 'an exception occurred while creating issuer account', 'exception': str(err) }) return error_response("Couldn't create issuer", http_code=200)
def process_unique_code_constraints(uc_constraints, con_id): """ This method inserts all of the code constraints into the database associated to given con_id. :param uc_constraints: codes to add :param con_id: con_id of contract to associate constraints to. :return: None """ for uc in uc_constraints: try: InsertUniqueCodeConstraint().execute({ 'con_id': con_id, 'code': uc['code'] }) except SQLAlchemyError as e: log_kv( LOG_ERROR, { 'message': 'Exception trying to add code constraint.', 'contract_con_id': con_id, 'exception': str(e), 'constraints': uc_constraints }) continue
def perform_ownership_transfer(trade, trade_items, sess): # Go through and transfer ownership over trader_c_id, tradee_c_id = trade['trader_c_id'], trade['tradee_c_id'] for trade_item in trade_items: try: # Figure out new ownership. if trade_item['owner'] == trader_c_id: prev_owner, new_owner = trader_c_id, tradee_c_id else: prev_owner, new_owner = tradee_c_id, trader_c_id # Update the ownership. upt_cnt = UpdateOwnership().execute( { 'con_id': trade_item['con_id'], 't_id': trade_item['t_id'], 'new_owner': new_owner, 'prev_owner': prev_owner }, sesh=sess) # Ensure that the ownership was actually updated. if upt_cnt != 1: log_kv( LOG_ERROR, { 'error': 'error transferring token ownership', 'con_id': trade_item['con_id'], 't_id': trade_item['t_id'] }) except Exception as e: log_kv(LOG_ERROR, { 'error': 'error transferring ownership of token.', 'con_id': trade_item['con_id'], 't_id': trade_item['t_id'], 'owner_c_id': trade_item['owner'], 'exception': str(e) }, exception=True) sess.rollback()
def get_issuer_by_username(username): """ This method retrieves issuer data for the given username. :param username: username of issuer to retrieve. :return: """ issuer = GetIssuerByUsername().execute_n_fetchone({'username': username}, close_connection=True) if issuer: log_kv(LOG_INFO, { 'message': 'Successfully found issuer', 'issuer_username': username }) return success_response(issuer) else: log_kv(LOG_WARNING, { 'warning': 'Could not find account', 'issuer_username': username }) return error_response( status="Couldn't retrieve issuer with that username", status_code=-1, http_code=200)
def is_valid_trade_items(trade_items): """ Go through each trade item and ensure that the owners of the tokens are the same when the initial trade was invoked. :param trade_items: list of trade_item db records. :return: Boolean representing validity of trade. """ # Go through each token and ensure it is owned by the correct collector. for trade_item in trade_items: # If we find a token in which the owner has changed then we have found an invalid trade request. if not check_trade_item_ownership( trade_item['owner'], trade_item['con_id'], trade_item['t_id']): log_kv( LOG_INFO, { 'info': 'Owner of token has changed', 'previous_owner_c_id': trade_item['owner'], 'con_id': trade_item['con_id'], 't_id': trade_item['t_id'] }) return False return True
def process_location_constraints(location_constraints, con_id): """ This method inserts all of the location constraints into the database associated to given con_id. :param location_constraints: location constraints to add. :param con_id: con_id of contract to associate constraints to. :return: """ for lc in location_constraints: try: InsertLocationConstraint().execute({ 'con_id': con_id, 'latitude': lc['latitude'], 'longitude': lc['longitude'], 'radius': lc['radius'] }) except SQLAlchemyError as e: log_kv( LOG_ERROR, { 'message': 'Exception trying to add location constraint.', 'contract_con_id': con_id, 'exception': str(e), 'constraints': location_constraints }) continue
def post(self, data): try: # If the user already exists, return a jwt to them collector = GetCollectorByUsername().execute_n_fetchone( {'username': data['username']}) if collector: log_kv( LOG_INFO, { 'message': 'the given user already exists', 'username': data['username'] }) return success_response({'jwt': generate_jwt(collector)}, http_code=201) # Create the collector account and bind the hash and private key data['c_hash'], data['c_priv_key'] = g.geth.create_account() collector = create_collector(data) g.sesh.commit() log_kv(LOG_INFO, {'message': 'successfully created collector'}) return success_response({'jwt': generate_jwt(collector)}, http_code=201) except GethException as ge: log_kv( LOG_ERROR, { 'error': 'a geth_exception occurred while creating collector account', 'exception': ge.exception, 'exc_message': ge.message }) return error_response(ge.message) except Exception as e: log_kv( LOG_ERROR, { 'error': 'an exception occurred while creating collector account', 'exception': str(e) }) return error_response(str(e), http_code=200)
def handle_bad_request(e): log_kv(LOG_ERROR, {'error': 'exception while processing request', 'exception': str(e)}, exception=True) if isinstance(e, HTTPException): return error_response(str(e), http_code=e.code) else: return error_response('Unknown Error!', http_code=500)
def collector_a_ping(): log_kv(LOG_INFO, {'message': 'authorized collector ping called', 'collector_id': g.collector_info['c_id']}) return success_response("pong for c_id:{}".format(g.collector_info['c_id']))
def post(self): """ Method to use for post requests to the /contract method. :return: HTTP response """ try: # Parse out the data from the contract request. data = ContractRequest().loads(request.form['json_data']) except ValidationError as er: return error_response('Validation Failed', errors=er.messages) # Check to ensure we are not over the max token limit. if data['num_created'] > MAX_TOKEN_LIMIT: log_kv( LOG_WARNING, { 'warning': 'issuer tried to create contract over limit', 'issuer_id': g.issuer_info['i_id'], 'num_tokens': data['num_created'] }) return error_response( "Could not create a token contract with that many individual token. Max is {}" .format(MAX_TOKEN_LIMIT), status_code=89) # Update the original data given after validation for contract creation binds. data.update({'i_id': g.issuer_info['i_id']}) # If we have an image save it. file_location = 'default.png' if 'token_image' in request.files: file_location = save_file(request.files['token_image'], Folders.CONTRACTS.value, g.issuer_info['i_id']) data.update({'pic_location': file_location}) # If meta data is persistent save it. if 'meta_json_data' in request.form: data['metadata_location'] = save_json_data( request.form['meta_json_data'], g.issuer_info['i_id']) else: img_location = "project-token.com/contract/image=" + data[ 'pic_location'] data['metadata_location'] = save_json_data( DEFAULT_JSON_METADATA.format(name=data['name'], description=data['description'], img_loc=img_location), g.issuer_info['i_id']) try: # Get the received constraints in array format for the smart contract code_constraints, date_constraints, loc_constraints = self.get_constraints( data) # Issue the contract on the ETH network issuer = GetIssuerInfo().execute_n_fetchone( binds={'i_id': g.issuer_info['i_id']}) # Ensure we retrieved an issuer. if issuer is None: return error_response('Failed to retrieve issuer specified.', status_code=45) data['con_tx'], data['con_abi'], data[ 'gas_price'] = g.geth.issue_contract( issuer['i_hash'], issuer_name=issuer['username'], name=data['name'], desc=data['description'], img_url=data['pic_location'], num_tokes=data['num_created'], code_reqs=code_constraints, date_reqs=date_constraints, loc_reqs=loc_constraints, tradable=data['tradable'], metadata_uri=data['metadata_location']) # Insert into the database con_id, t_ids = insert_bulk_tokens(data['num_created'], data, g.sesh) # It is either qr_codes or other contstraints it cannot be both. if data['qr_code_claimable']: # Get all tokens to associate qr code with. for t_id in t_ids: # Generate the data to place in qr code. json_data_dict = dumps({ 'con_id': con_id, 't_id': t_id, 'jwt': generate_jwt({ 'con_id': con_id, 't_id': t_id }) }) # Make qr_code and save it. qrc = qrcode.make(json_data_dict) saved_location = save_qrcode(qrc, con_id, t_id) # If we successfully saved the image persist it into database. if saved_location is None: log_kv(LOG_ERROR, {'error': 'failed to make qrcode.'}) else: UpdateQRCODE().execute({ 'qr_code_location': saved_location, 'con_id': con_id, 't_id': t_id }) elif 'constraints' in data: # If constraints were passed in we need to process them. process_constraints(data['constraints'], con_id) g.sesh.commit() log_kv( LOG_INFO, { 'message': 'succesfully issued contract!', 'issuer_id': g.issuer_info['i_id'] }) return success_response('Success in issuing token!', http_code=201) except GethException as e: g.sesh.rollback() log_kv(LOG_ERROR, { 'error': 'a geth_exception occurred while issuing contract', 'issuer_id': g.issuer_info['i_id'], 'exception': e.exception, 'exc_message': e.message }, exception=True) return error_response(e.message) except Exception as e: g.sesh.rollback() log_kv(LOG_ERROR, { 'error': 'an exception occurred while issuing contract', 'issuer_id': g.issuer_info['i_id'], 'exception': str(e) }, exception=True) return error_response( "Couldn't create new contract. Exception {}".format(str(e)))
def log_request(): g.log_id = str(uuid1())[:8] log_kv(LOG_DEBUG, {'message': 'received request', 'method': request.method, 'headers': request.headers, 'url': request.url, 'data': request.get_data()})
def pings(): log_kv(LOG_INFO, {'message': 'ping called pong respond'}) return success_response("pong")
def claim_qr_code(con_id, t_id, c_id, lat, long): """ Method that is very similar to the claim_token_for_user method but only attempts to claim token by given t_id. :param con_id: The contract_id of the token :param t_id: the t_id of token to claim. :param c_id: The collector_id of the collecting user :param lat: latitude of user during claim attempt. :param long: longitude of user during claim attempt. :return: True if the claim request was successful, False if otherwise """ try: # No constraint logic is done as its only a qr_code token. # Make sure a token is available and that it has info avail_token = GetSingleAvailToken().execute_n_fetchone({ 'con_id': con_id, 't_id': t_id }) token_info = GetTokenInfo().execute_n_fetchone({ 'con_id': con_id, 'c_id': c_id }) if not avail_token or not token_info: log_kv( LOG_INFO, { 'message': 'no tokens are available', 'contract_id': con_id, 'collector_id': c_id }) return False, 'Token not available.', 78 # Claim the token and update the database log_kv( LOG_INFO, { 'message': 'claiming ethereum token', 'token_id': avail_token['t_id'], 'collector_id': c_id }) # Attempt to claim the token on the eth network. tx_hash, gas_price = g.geth.claim_token(token_info['con_addr'], token_info['con_abi'], token_info['c_hash'], avail_token['t_id']) # Persist the changes in the database. rows_updated = SetToken().execute({ 'con_id': con_id, 'latitude': lat, 'longitude': long, 'gas_price': gas_price, 't_hash': tx_hash, 't_id': avail_token['t_id'], 'c_id': c_id, 'new_status': TokenStatus.CLAIMED.value }) # Make sure a row was updated if rows_updated == 1: log_kv( LOG_INFO, { 'message': 'successfully claimed token', 'contract_id': con_id, 'collector_id': c_id }) return True, 'Token has been claimed!', 0 except GethException as e: log_kv(LOG_ERROR, { 'error': 'a geth_exception occurred while claiming token', 'exception': e.exception, 'exc_message': e.message, 'contract_id': con_id, 'collector_id': c_id }, exception=True) return False, 'Geth Error:' + e.exception, -1 except Exception as e: log_kv(LOG_ERROR, { 'error': 'an exception occurred while claiming token', 'exception': str(e), 'contract_id': con_id, 'collector_id': c_id }, exception=True) return False, str(e) + traceback.format_exc(), -1
def claim_token_for_user(con_id, c_id, lat, long, constraints, sesh): """ Attempts to claim a token for the given user :param con_id: The contract_id of the token :param c_id: The collector_id of the collecting user :param lat: latitude of user during claim attempt. :param long: longitude of user during claim attempt. :param sesh: The database session to use :param constraints: Contraint information given by user. :return: True if the claim request was successful, False if otherwise """ try: # Enforcing claim constraints. if not validate_uni_code_constraints(con_id, constraints.get('code', None)): return False, "Constraint Failed: Code provided does not match any codes required to claim this token.", 3 if not validate_time_constraints(con_id, constraints.get('time', None)): return False, 'Constraint Failed: This token is unclaimable at this time.', 4 if not validate_location_constraints( con_id, constraints.get('location', None)): return False, 'Constraint Failed: Not within the appropriate location to obtain this token.', 5 # Make sure a token is available and that it has info avail_token = GetAvailableToken().execute_n_fetchone( {'con_id': con_id}, sesh=sesh) token_info = GetTokenInfo().execute_n_fetchone( { 'con_id': con_id, 'c_id': c_id }, sesh=sesh) if not avail_token or not token_info: log_kv( LOG_INFO, { 'message': 'no tokens are available', 'contract_id': con_id, 'collector_id': c_id }) return False, 'No available tokens', 6 # Claim the token and update the database log_kv( LOG_INFO, { 'message': 'claiming ethereum token', 'token_id': avail_token['t_id'], 'collector_id': c_id }) # Attempt to claim the token on the eth network. tx_hash, gas_price = g.geth.claim_token(token_info['con_addr'], token_info['con_abi'], token_info['c_hash'], avail_token['t_id'], code=constraints.get( 'code', None)) # Persist the changes in the database. rows_updated = SetToken().execute( { 'con_id': con_id, 'latitude': lat, 'longitude': long, 'gas_price': gas_price, 't_hash': tx_hash, 't_id': avail_token['t_id'], 'c_id': c_id, 'new_status': TokenStatus.CLAIMED.value }, sesh=sesh) # Make sure a row was updated if rows_updated == 1: log_kv( LOG_INFO, { 'message': 'successfully claimed token', 'contract_id': con_id, 'collector_id': c_id }) return True, 'Token has been claimed!', 0 except GethException as e: log_kv(LOG_ERROR, { 'error': 'a geth_exception occurred while claiming token', 'exception': e.exception, 'exc_message': e.message, 'contract_id': con_id, 'collector_id': c_id }, exception=True) return False, 'Geth Error:' + e.exception, -1 except Exception as e: log_kv(LOG_ERROR, { 'error': 'an exception occurred while claiming token', 'exception': str(e), 'contract_id': con_id, 'collector_id': c_id }, exception=True) return False, str(e) + traceback.format_exc(), -1
def __init__(self, exception, message='', exception_number=0): self.exception = exception self.message = message self.exception_number = exception_number log_kv(LOG_ERROR, {'error': self.message, 'exception': self.exception}, exception=True)
def issuer_a_ping(): log_kv(LOG_INFO, {'message': 'authorized issuer ping called', 'issuer_id': g.issuer_info['i_id']}) return success_response("pong for i_id:{}".format(g.issuer_info['i_id']))