from collections import defaultdict import sqlite3 import json import os import requests import sys import time from datetime import datetime from quart import jsonify, make_response, Quart, render_template, request, flash, redirect, url_for, send_file from quart_cors import cors import asyncio from typing import Optional from config import * import parsing import subprocess from apscheduler.schedulers.background import BackgroundScheduler import atexit import pyflo import pdb app = Quart(__name__) app.clients = set() app = cors(app, allow_origin="*") if net == 'mainnet': is_testnet = False elif net == 'testnet': is_testnet = True # Validation functionss def check_flo_address(floaddress, is_testnet=False): return pyflo.is_address_valid(floaddress, testnet=is_testnet) def check_integer(value): return str.isdigit(value) # Helper functions def retryRequest(tempserverlist, apicall): if len(tempserverlist) != 0: try: response = requests.get( '{}api/{}'.format(tempserverlist[0], apicall)) except: tempserverlist.pop(0) return retryRequest(tempserverlist, apicall) else: if response.status_code == 200: return json.loads(response.content) else: tempserverlist.pop(0) return retryRequest(tempserverlist, apicall) else: print("None of the APIs are responding for the call {}".format(apicall)) sys.exit(0) def multiRequest(apicall, net): testserverlist = ['http://0.0.0.0:9000/', 'https://testnet.flocha.in/', 'https://testnet-flosight.duckdns.org/'] mainserverlist = ['http://0.0.0.0:9001/', 'https://livenet.flocha.in/', 'https://testnet-flosight.duckdns.org/'] if net == 'mainnet': return retryRequest(mainserverlist, apicall) elif net == 'testnet': return retryRequest(testserverlist, apicall) def blockdetailhelper(blockdetail): if blockdetail.isdigit(): blockHash = None blockHeight = int(blockdetail) else: blockHash = str(blockdetail) blockHeight = None # open the latest block database conn = sqlite3.connect(os.path.join(dbfolder, 'latestCache.db')) c = conn.cursor() if blockHash: c.execute(f"select jsonData from latestBlocks where blockHash='{blockHash}'") elif blockHeight: c.execute(f"select jsonData from latestBlocks where blockNumber='{blockHeight}'") blockJson = c.fetchall() return blockJson def transactiondetailhelper(transactionHash): # open the latest block database conn = sqlite3.connect(os.path.join(dbfolder, 'latestCache.db')) c = conn.cursor() c.execute(f"select jsonData, parsedFloData, transactionType, db_reference from latestTransactions where transactionHash='{transactionHash}'") transactionJsonData = c.fetchall() return transactionJsonData def smartContractInfo_output(contractName, contractAddress, contractType, subtype): if contractType == 'continuos-event' and contractType == 'tokenswap': pass elif contractType == 'one-time-event' and contractType == 'userchoice': pass elif contractType == 'one-time-event' and contractType == 'timetrigger': pass def return_smart_contracts(connection, contractName=None, contractAddress=None): # find all the contracts details if contractName and contractAddress: connection.execute("SELECT * FROM activecontracts WHERE id IN (SELECT max(id) FROM activecontracts GROUP BY contractName, contractAddress) AND contractName=? AND contractAddress=?", (contractName, contractAddress)) elif contractName and not contractAddress: connection.execute("SELECT * FROM activecontracts WHERE id IN (SELECT max(id) FROM activecontracts GROUP BY contractName, contractAddress) AND contractName=?", (contractName,)) elif not contractName and contractAddress: connection.execute("SELECT * FROM activecontracts WHERE id IN (SELECT max(id) FROM activecontracts GROUP BY contractName, contractAddress) AND contractAddress=?", (contractAddress,)) else: connection.execute("SELECT * FROM activecontracts WHERE id IN (SELECT max(id) FROM activecontracts GROUP BY contractName, contractAddress)") smart_contracts = connection.fetchall() return smart_contracts def create_database_connection(type, parameters=None): if type == 'token': filelocation = os.path.join(dbfolder, 'tokens', parameters['token_name']) elif type == 'smart_contract': contractDbName = '{}-{}.db'.format(parameters['contract_name'].strip(), parameters['contract_address'].strip()) filelocation = os.path.join(dbfolder, 'smartContracts', contractDbName) elif type == 'system_dbs': filelocation = os.path.join(dbfolder, 'system.db') elif type == 'latest_cache': filelocation = os.path.join(dbfolder, 'latestCache.db') conn = sqlite3.connect(filelocation) c = conn.cursor() return [conn, c] def fetchContractStructure(contractName, contractAddress): # Make connection to contract database contractDbName = '{}-{}.db'.format(contractName.strip(),contractAddress.strip()) filelocation = os.path.join(dbfolder, 'smartContracts', contractDbName) if os.path.isfile(filelocation): # fetch from contractStructure conn = sqlite3.connect(filelocation) c = conn.cursor() c.execute('SELECT attribute,value FROM contractstructure') result = c.fetchall() contractStructure = {} conditionDict = {} counter = 0 for item in result: if list(item)[0] == 'exitconditions': conditionDict[counter] = list(item)[1] counter = counter + 1 else: contractStructure[list(item)[0]] = list(item)[1] if len(conditionDict) > 0: contractStructure['exitconditions'] = conditionDict del counter, conditionDict, c conn.close() if 'contractAmount' in contractStructure: contractStructure['contractAmount'] = float(contractStructure['contractAmount']) if 'payeeAddress' in contractStructure: contractStructure['payeeAddress'] = json.loads(contractStructure['payeeAddress']) if 'maximumsubscriptionamount' in contractStructure: contractStructure['maximumsubscriptionamount'] = float(contractStructure['maximumsubscriptionamount']) if 'minimumsubscriptionamount' in contractStructure: contractStructure['minimumsubscriptionamount'] = float(contractStructure['minimumsubscriptionamount']) if 'price' in contractStructure: contractStructure['price'] = float(contractStructure['price']) return contractStructure else: return 0 def fetchContractStatus(contractName, contractAddress): conn, c = create_database_connection('system_dbs') # select status from the last instance of activecontracts where match contractName and contractAddress c.execute(f'SELECT status FROM activecontracts WHERE contractName="{contractName}" AND contractAddress="{contractAddress}" ORDER BY id DESC LIMIT 1') status = c.fetchall() if len(status)==0: return None else: return status[0][0] def extract_ip_op_addresses(transactionJson): sender_address = transactionJson['vin'][0]['addr'] receiver_address = None for utxo in transactionJson['vout']: if utxo['scriptPubKey']['addresses'][0] == sender_address: continue receiver_address = utxo['scriptPubKey']['addresses'][0] return sender_address, receiver_address def updatePrices(): prices = {} # USD -> INR response = requests.get(f"https://api.exchangerate-api.com/v4/latest/usd") price = response.json() prices['USDINR'] = price['rates']['INR'] # Blockchain stuff : BTC,FLO -> USD,INR # BTC->USD | BTC->INR response = requests.get(f"https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,flo&vs_currencies=usd,inr") price = response.json() prices['BTCUSD'] = price['bitcoin']['usd'] prices['BTCINR'] = price['bitcoin']['inr'] # FLO->USD | FLO->INR response = requests.get(f"https://api.coinlore.net/api/ticker/?id=67") price = response.json() prices["FLOUSD"] = float(price[0]['price_usd']) prices["FLOINR"] = float(prices["FLOUSD"]) * float(prices['USDINR']) # 3. update latest price data print('Prices updated at time: %s' % datetime.now()) print(prices) conn = sqlite3.connect('system.db') c = conn.cursor() for pair in list(prices.items()): pair = list(pair) c.execute(f"UPDATE ratepairs SET price={pair[1]} WHERE ratepair='{pair[0]}'") conn.commit() @app.route('/') async def welcome_msg(): return jsonify('Welcome to RanchiMall FLO Api v2') @app.route('/api/v1.0/getSystemData', methods=['GET']) async def systemData(): # query for the number of flo addresses in tokenAddress mapping conn = sqlite3.connect(os.path.join(dbfolder, 'system.db')) c = conn.cursor() tokenAddressCount = c.execute('select count(distinct tokenAddress) from tokenAddressMapping').fetchall()[0][0] tokenCount = c.execute('select count(distinct token) from tokenAddressMapping').fetchall()[0][0] contractCount = c.execute('select count(distinct contractName) from contractAddressMapping').fetchall()[0][0] lastscannedblock = int(c.execute("select value from systemData where attribute=='lastblockscanned'").fetchall()[0][0]) conn.close() # query for total number of validated blocks conn = sqlite3.connect(os.path.join(dbfolder, 'latestCache.db')) c = conn.cursor() validatedBlockCount = c.execute('select count(distinct blockNumber) from latestBlocks').fetchall()[0][0] validatedTransactionCount = c.execute('select count(distinct transactionHash) from latestTransactions').fetchall()[0][0] conn.close() return jsonify(systemAddressCount=tokenAddressCount, systemBlockCount=validatedBlockCount, systemTransactionCount=validatedTransactionCount, systemSmartContractCount=contractCount, systemTokenCount=tokenCount, lastscannedblock=lastscannedblock, result='ok') @app.route('/api/v1.0/broadcastTx/') async def broadcastTx(raw_transaction_hash): p1 = subprocess.run(['flo-cli',f"-datadir={FLO_DATA_DIR}",'sendrawtransaction',raw_transaction_hash], capture_output=True) return jsonify(args=p1.args,returncode=p1.returncode,stdout=p1.stdout.decode(),stderr=p1.stderr.decode()) # FLO TOKEN APIs @app.route('/api/v1.0/getTokenList', methods=['GET']) async def getTokenList(): filelist = [] for item in os.listdir(os.path.join(dbfolder, 'tokens')): if os.path.isfile(os.path.join(dbfolder, 'tokens', item)): filelist.append(item[:-3]) return jsonify(tokens=filelist, result='ok') @app.route('/api/v1.0/getTokenInfo', methods=['GET']) async def getTokenInfo(): token = request.args.get('token') if token is None: return jsonify(result='error', description='token name hasnt been passed') dblocation = dbfolder + '/tokens/' + str(token) + '.db' if os.path.exists(dblocation): conn = sqlite3.connect(dblocation) c = conn.cursor() else: return jsonify(result='error', description='token doesn\'t exist') c.execute('SELECT * FROM transactionHistory WHERE id=1') incorporationRow = c.fetchall()[0] c.execute('SELECT COUNT (DISTINCT address) FROM activeTable') numberOf_distinctAddresses = c.fetchall()[0][0] c.execute('select max(id) from transactionHistory') numberOf_transactions = c.fetchall()[0][0] c.execute('select contractName, contractAddress, blockNumber, blockHash, transactionHash from tokenContractAssociation') associatedContracts = c.fetchall() conn.close() associatedContractList = [] for item in associatedContracts: tempdict = {} item = list(item) tempdict['contractName'] = item[0] tempdict['contractAddress'] = item[1] tempdict['blockNumber'] = item[2] tempdict['blockHash'] = item[3] tempdict['transactionHash'] = item[4] associatedContractList.append(tempdict) return jsonify(result='ok', token=token, incorporationAddress=incorporationRow[1], tokenSupply=incorporationRow[3], time=incorporationRow[6], blockchainReference=incorporationRow[7], activeAddress_no=numberOf_distinctAddresses, totalTransactions=numberOf_transactions, associatedSmartContracts=associatedContractList) @app.route('/api/v1.0/getTokenTransactions', methods=['GET']) async def getTokenTransactions(): token = request.args.get('token') senderFloAddress = request.args.get('senderFloAddress') destFloAddress = request.args.get('destFloAddress') limit = request.args.get('limit') if token is None: return jsonify(result='error', description='token name hasnt been passed') dblocation = dbfolder + '/tokens/' + str(token) + '.db' if os.path.exists(dblocation): conn = sqlite3.connect(dblocation) conn.row_factory = sqlite3.Row c = conn.cursor() else: return jsonify(result='error', description='token doesn\'t exist') if senderFloAddress and not destFloAddress: if limit is None: c.execute('SELECT jsonData, parsedFloData FROM transactionHistory WHERE sourceFloAddress="{}" ORDER BY id DESC LIMIT 100'.format(senderFloAddress)) else: c.execute('SELECT jsonData, parsedFloData FROM transactionHistory WHERE sourceFloAddress="{}" ORDER BY id DESC LIMIT {}'.format(senderFloAddress, limit)) elif not senderFloAddress and destFloAddress: if limit is None: c.execute('SELECT jsonData, parsedFloData FROM transactionHistory WHERE destFloAddress="{}" ORDER BY id DESC LIMIT 100'.format(destFloAddress)) else: c.execute('SELECT jsonData, parsedFloData FROM transactionHistory WHERE destFloAddress="{}" ORDER BY id DESC LIMIT {}'.format(destFloAddress, limit)) elif senderFloAddress and destFloAddress: if limit is None: c.execute('SELECT jsonData, parsedFloData FROM transactionHistory WHERE sourceFloAddress="{}" AND destFloAddress="{}" ORDER BY id DESC LIMIT 100'.format(senderFloAddress, destFloAddress)) else: c.execute('SELECT jsonData, parsedFloData FROM transactionHistory WHERE sourceFloAddress="{}" AND destFloAddress="{}" ORDER BY id DESC LIMIT {}'.format(senderFloAddress, destFloAddress, limit)) else: if limit is None: c.execute( 'SELECT jsonData, parsedFloData FROM transactionHistory ORDER BY id DESC LIMIT 100') else: c.execute( 'SELECT jsonData, parsedFloData FROM transactionHistory ORDER BY id DESC LIMIT {}'.format(limit)) transactionJsonData = c.fetchall() conn.close() rowarray_list = {} for row in transactionJsonData: temp = {} temp['transactionDetails'] = json.loads(row[0]) temp['parsedFloData'] = json.loads(row[1]) rowarray_list[temp['transactionDetails']['txid']] = temp return jsonify(result='ok', token=token, transactions=rowarray_list) @app.route('/api/v1.0/getTokenBalances', methods=['GET']) async def getTokenBalances(): token = request.args.get('token') if token is None: return jsonify(result='error', description='token name hasnt been passed') dblocation = dbfolder + '/tokens/' + str(token) + '.db' if os.path.exists(dblocation): conn = sqlite3.connect(dblocation) c = conn.cursor() else: return jsonify(result='error', description='token doesn\'t exist') c.execute( 'SELECT address,SUM(transferBalance) FROM activeTable GROUP BY address') addressBalances = c.fetchall() returnList = {} for address in addressBalances: returnList[address[0]] = address[1] return jsonify(result='ok', token=token, balances=returnList) # FLO Address APIs @app.route('/api/v1.0/getFloAddressInfo', methods=['GET']) async def getFloAddressInfo(): floAddress = request.args.get('floAddress') if floAddress is None: return jsonify(description='floAddress hasn\'t been passed'), 400 dblocation = dbfolder + '/system.db' if os.path.exists(dblocation): conn = sqlite3.connect(dblocation) c = conn.cursor() c.execute( 'select token from tokenAddressMapping where tokenAddress="{}"'.format(floAddress)) tokenNames = c.fetchall() c.execute( f"select contractName, status, tokenIdentification, contractType, transactionHash, blockNumber, blockHash from activecontracts where contractAddress='{floAddress}'") incorporatedContracts = c.fetchall() if len(tokenNames) != 0: detailList = {} for token in tokenNames: token = token[0] dblocation = dbfolder + '/tokens/' + str(token) + '.db' if os.path.exists(dblocation): tempdict = {} conn = sqlite3.connect(dblocation) c = conn.cursor() c.execute( 'SELECT SUM(transferBalance) FROM activeTable WHERE address="{}"'.format(floAddress)) balance = c.fetchall()[0][0] tempdict['balance'] = balance tempdict['token'] = token detailList[token] = tempdict else: # Address is not associated with any token return jsonify(result='error', description='FLO address is not associated with any tokens') if len(incorporatedContracts) != 0: incorporatedSmartContracts = [] for contract in incorporatedContracts: tempdict = {} tempdict['contractName'] = contract[0] tempdict['contractAddress'] = floAddress tempdict['status'] = contract[1] tempdict['tokenIdentification'] = contract[2] tempdict['contractType'] = contract[3] tempdict['transactionHash'] = contract[4] tempdict['blockNumber'] = contract[5] tempdict['blockHash'] = contract[6] incorporatedSmartContracts.append(tempdict) return jsonify(result='ok', floAddress=floAddress, floAddressBalances=detailList, incorporatedSmartContracts=incorporatedContracts) else: return jsonify(result='ok', floAddress=floAddress, floAddressBalances=detailList, incorporatedSmartContracts=None) @app.route('/api/v1.0/getFloAddressBalance', methods=['GET']) async def getAddressBalance(): floAddress = request.args.get('floAddress') token = request.args.get('token') if floAddress is None: return jsonify(result='error', description='floAddress hasn\'t been passed') if token is None: dblocation = dbfolder + '/system.db' if os.path.exists(dblocation): conn = sqlite3.connect(dblocation) c = conn.cursor() c.execute( 'select token from tokenAddressMapping where tokenAddress="{}"'.format(floAddress)) tokenNames = c.fetchall() if len(tokenNames) != 0: detailList = {} for token in tokenNames: token = token[0] dblocation = dbfolder + '/tokens/' + str(token) + '.db' if os.path.exists(dblocation): tempdict = {} conn = sqlite3.connect(dblocation) c = conn.cursor() c.execute( 'SELECT SUM(transferBalance) FROM activeTable WHERE address="{}"'.format(floAddress)) balance = c.fetchall()[0][0] tempdict['balance'] = balance tempdict['token'] = token detailList[token] = tempdict return jsonify(result='ok', floAddress=floAddress, floAddressBalances=detailList) else: # Address is not associated with any token return jsonify(result='error', description='FLO address is not associated with any tokens') else: dblocation = dbfolder + '/tokens/' + str(token) + '.db' if os.path.exists(dblocation): conn = sqlite3.connect(dblocation) c = conn.cursor() else: return jsonify(result='error', description='token doesn\'t exist') c.execute( 'SELECT SUM(transferBalance) FROM activeTable WHERE address="{}"'.format(floAddress)) balance = c.fetchall()[0][0] conn.close() return jsonify(result='ok', token=token, floAddress=floAddress, balance=balance) @app.route('/api/v1.0/getFloAddressTransactions', methods=['GET']) async def getFloAddressTransactions(): floAddress = request.args.get('floAddress') token = request.args.get('token') limit = request.args.get('limit') if floAddress is None: return jsonify(result='error', description='floAddress has not been passed') if token is None: dblocation = dbfolder + '/system.db' if os.path.exists(dblocation): conn = sqlite3.connect(dblocation) c = conn.cursor() c.execute('select token from tokenAddressMapping where tokenAddress="{}"'.format(floAddress)) tokenNames = c.fetchall() else: dblocation = dbfolder + '/tokens/' + str(token) + '.db' if os.path.exists(dblocation): tokenNames = [[str(token), ]] else: return jsonify(result='error', description='token doesn\'t exist') if len(tokenNames) != 0: allTransactionList = {} for tokenname in tokenNames: tokenname = tokenname[0] dblocation = dbfolder + '/tokens/' + str(tokenname) + '.db' if os.path.exists(dblocation): tempdict = {} conn = sqlite3.connect(dblocation) c = conn.cursor() if limit is None: c.execute('SELECT jsonData, parsedFloData FROM transactionHistory WHERE sourceFloAddress="{}" OR destFloAddress="{}" ORDER BY id DESC LIMIT 100'.format( floAddress, floAddress)) else: c.execute('SELECT jsonData, parsedFloData FROM transactionHistory WHERE sourceFloAddress="{}" OR destFloAddress="{}" ORDER BY id DESC LIMIT {}'.format( floAddress, floAddress, limit)) transactionJsonData = c.fetchall() conn.close() for row in transactionJsonData: temp = {} temp['transactionDetails'] = json.loads(row[0]) temp['parsedFloData'] = json.loads(row[1]) allTransactionList[temp['transactionDetails'] ['txid']] = temp if token is None: return jsonify(result='ok', floAddress=floAddress, transactions=allTransactionList) else: return jsonify(result='ok', floAddress=floAddress, transactions=allTransactionList, token=token) else: return jsonify(result='error', description='No token transactions present present on this address') # SMART CONTRACT APIs @app.route('/api/v1.0/getSmartContractList', methods=['GET']) async def getContractList(): contractName = request.args.get('contractName') contractAddress = request.args.get('contractAddress') conn = sqlite3.connect(os.path.join(dbfolder, 'system.db')) c = conn.cursor() contractList = [] if contractName and contractAddress: c.execute('select * from activecontracts where contractName="{}" and contractAddress="{}"'.format(contractName, contractAddress)) allcontractsDetailList = c.fetchall() for idx, contract in enumerate(allcontractsDetailList): contractDict = {} contractDict['contractName'] = contract[1] contractDict['contractAddress'] = contract[2] contractDict['status'] = contract[3] contractDict['tokenIdentification'] = contract[4] contractDict['contractType'] = contract[5] contractDict['transactionHash'] = contract[6] contractDict['blockNumber'] = contract[7] contractDict['incorporationDate'] = contract[8] if contract[9]: contractDict['expiryDate'] = contract[9] if contract[10]: contractDict['closeDate'] = contract[10] contractList.append(contractDict) elif contractName and not contractAddress: c.execute('select * from activecontracts where contractName="{}"'.format(contractName)) allcontractsDetailList = c.fetchall() for idx, contract in enumerate(allcontractsDetailList): contractDict = {} contractDict['contractName'] = contract[1] contractDict['contractAddress'] = contract[2] contractDict['status'] = contract[3] contractDict['tokenIdentification'] = contract[4] contractDict['contractType'] = contract[5] contractDict['transactionHash'] = contract[6] contractDict['blockNumber'] = contract[7] contractDict['incorporationDate'] = contract[8] if contract[9]: contractDict['expiryDate'] = contract[9] if contract[10]: contractDict['closeDate'] = contract[10] contractList.append(contractDict) elif not contractName and contractAddress: c.execute('select * from activecontracts where contractAddress="{}"'.format(contractAddress)) allcontractsDetailList = c.fetchall() for idx, contract in enumerate(allcontractsDetailList): contractDict = {} contractDict['contractName'] = contract[1] contractDict['contractAddress'] = contract[2] contractDict['status'] = contract[3] contractDict['tokenIdentification'] = contract[4] contractDict['contractType'] = contract[5] contractDict['transactionHash'] = contract[6] contractDict['blockNumber'] = contract[7] contractDict['incorporationDate'] = contract[8] if contract[9]: contractDict['expiryDate'] = contract[9] if contract[10]: contractDict['closeDate'] = contract[10] contractList.append(contractDict) else: c.execute('select * from activecontracts') allcontractsDetailList = c.fetchall() for idx, contract in enumerate(allcontractsDetailList): contractDict = {} contractDict['contractName'] = contract[1] contractDict['contractAddress'] = contract[2] contractDict['status'] = contract[3] contractDict['tokenIdentification'] = contract[4] contractDict['contractType'] = contract[5] contractDict['transactionHash'] = contract[6] contractDict['blockNumber'] = contract[7] contractDict['incorporationDate'] = contract[8] if contract[9]: contractDict['expiryDate'] = contract[9] if contract[10]: contractDict['closeDate'] = contract[10] contractList.append(contractDict) return jsonify(smartContracts=contractList, result='ok') @app.route('/api/v1.0/getSmartContractInfo', methods=['GET']) async def getContractInfo(): contractName = request.args.get('contractName') contractAddress = request.args.get('contractAddress') if contractName is None: return jsonify(result='error', description='Smart Contract\'s name hasn\'t been passed') if contractAddress is None: return jsonify(result='error', description='Smart Contract\'s address hasn\'t been passed') contractDbName = '{}-{}.db'.format(contractName.strip(),contractAddress.strip()) filelocation = os.path.join(dbfolder, 'smartContracts', contractDbName) if os.path.isfile(filelocation): # Make db connection and fetch data conn = sqlite3.connect(filelocation) c = conn.cursor() c.execute('SELECT attribute,value FROM contractstructure') result = c.fetchall() contractStructure = {} conditionDict = {} counter = 0 for item in result: if list(item)[0] == 'exitconditions': conditionDict[counter] = list(item)[1] counter = counter + 1 else: contractStructure[list(item)[0]] = list(item)[1] if len(conditionDict) > 0: contractStructure['exitconditions'] = conditionDict del counter, conditionDict returnval = contractStructure returnval['userChoice'] = contractStructure['exitconditions'] returnval.pop('exitconditions') c.execute('select count(participantAddress) from contractparticipants') noOfParticipants = c.fetchall()[0][0] returnval['numberOfParticipants'] = noOfParticipants c.execute('select sum(tokenAmount) from contractparticipants') totalAmount = c.fetchall()[0][0] returnval['tokenAmountDeposited'] = totalAmount conn.close() conn = sqlite3.connect(os.path.join(dbfolder, 'system.db')) c = conn.cursor() c.execute('select status, incorporationDate, expiryDate, closeDate from activecontracts where contractName=="{}" and contractAddress=="{}"'.format(contractName.strip(), contractAddress.strip())) results = c.fetchall() if len(results) == 1: for result in results: returnval['status'] = result[0] returnval['incorporationDate'] = result[1] if result[2]: returnval['expiryDate'] = result[2] if result[3]: returnval['closeDate'] = result[3] if returnval['status'] == 'closed': conn = sqlite3.connect(filelocation) c = conn.cursor() if returnval['contractType'] == 'one-time-event': # pull out trigger information # check if the trigger was succesful or failed c.execute( f"select transactionType, transactionSubType from contractTransactionHistory where transactionType='trigger'") triggerntype = c.fetchall() if len(triggerntype) == 1: triggerntype = list(triggerntype[0]) returnval['triggerType'] = triggerntype[1] if 'userChoice' in returnval: # Contract is of the type external trigger if triggerntype[0] == 'trigger' and triggerntype[1] is None: # this is a normal trigger # find the winning condition c.execute( 'select userchoice from contractparticipants where winningAmount is not null limit 1') returnval['winningChoice'] = c.fetchall()[0][0] # find the winning participants c.execute( 'select participantAddress, winningAmount from contractparticipants where winningAmount is not null') winnerparticipants = c.fetchall() returnval['numberOfWinners'] = len(winnerparticipants) else: return jsonify(result='error', description='There is more than 1 trigger in the database for the smart contract. Please check your code, this shouldnt happen') return jsonify(result='ok', contractName=contractName, contractAddress=contractAddress, contractInfo=returnval) else: return jsonify(result='error', details='Smart Contract with the given name doesn\'t exist') @app.route('/api/v1.0/getSmartContractParticipants', methods=['GET']) async def getcontractparticipants(): contractName = request.args.get('contractName') contractAddress = request.args.get('contractAddress') if contractName is None: return jsonify(result='error', description='Smart Contract\'s name hasn\'t been passed') if contractAddress is None: return jsonify(result='error', description='Smart Contract\'s address hasn\'t been passed') contractName = contractName.strip() contractAddress = contractAddress.strip() filelocation = os.path.join(dbfolder, 'smartContracts', '{}-{}.db'.format(contractName, contractAddress)) if os.path.isfile(filelocation): # Make db connection and fetch data contractStructure = fetchContractStructure(contractName, contractAddress) conn, c = create_database_connection('smart_contract', {'contract_name': contractName, 'contract_address': contractAddress}) if 'exitconditions' in contractStructure: # contract is of the type external trigger # check if the contract has been closed c.execute('select * from contractTransactionHistory where transactionType="trigger"') trigger = c.fetchall() if len(trigger) == 1: c.execute('select value from contractstructure where attribute="tokenIdentification"') token = c.fetchall() token = token[0][0] c.execute('SELECT id,participantAddress, tokenAmount, userChoice, transactionHash, winningAmount FROM contractparticipants') result = c.fetchall() conn.close() returnval = {} for row in result: returnval[row[1]] = {'participantFloAddress': row[1], 'tokenAmount': row[2], 'userChoice': row[3], 'transactionHash': row[4], 'winningAmount': row[5], 'tokenIdentification': token} elif len(trigger) == 0: c.execute('SELECT id,participantAddress, tokenAmount, userChoice, transactionHash FROM contractparticipants') result = c.fetchall() conn.close() returnval = {} for row in result: returnval[row[1]] = {'participantFloAddress': row[1], 'tokenAmount': row[2], 'userChoice': row[3], 'transactionHash': row[4]} else: return jsonify(result='error', description='More than 1 trigger present. This is unusual, please check your code') elif 'payeeAddress' in contractStructure: # contract is of the type internal trigger c.execute( 'SELECT id,participantAddress, tokenAmount, userChoice, transactionHash FROM contractparticipants') result = c.fetchall() conn.close() returnval = {} for row in result: returnval[row[1]] = {'participantFloAddress': row[1], 'tokenAmount': row[2], 'userChoice': row[3], 'transactionHash': row[4]} elif contractStructure['contractType'] == 'continuos-event' and contractStructure['subtype'] == 'tokenswap': c.execute('SELECT * FROM contractparticipants') contract_participants = c.fetchall() returnval = {} for row in contract_participants: returnval[row[1]] = { 'participantFloAddress': row[1], 'participationAmount': row[2], 'swapPrice': float(row[3]), 'transactionHash': row[4], 'blockNumber': row[5], 'blockHash': row[6], 'swapAmount': row[7] } conn.close() return jsonify(result='ok', contractName=contractName, contractAddress=contractAddress, participantInfo=returnval) else: return jsonify(result='error', description='Smart Contract with the given name doesn\'t exist') @app.route('/api/v1.0/getParticipantDetails', methods=['GET']) async def getParticipantDetails(): floAddress = request.args.get('floAddress') contractName = request.args.get('contractName') contractAddress = request.args.get('contractAddress') if floAddress is None: return jsonify(result='error', description='FLO address hasn\'t been passed') dblocation = os.path.join(dbfolder, 'system.db') if (contractName and contractAddress is None) or (contractName is None and contractAddress): return jsonify(result='error', description='pass both, contractName and contractAddress as url parameters') #if os.path.isfile(dblocation) and os.path.isfile(contract_db): if os.path.isfile(dblocation): # Make db connection and fetch data conn = sqlite3.connect(dblocation) c = conn.cursor() if contractName is not None: c.execute(f'SELECT * FROM contractAddressMapping WHERE address="{floAddress}" AND addressType="participant" AND contractName="{contractName}" AND contractAddress="{contractAddress}"') else: c.execute(f'SELECT * FROM contractAddressMapping WHERE address="{floAddress}" AND addressType="participant"') participant_address_contracts = c.fetchall() if len(participant_address_contracts) != 0: participationDetailsList = [] for contract in participant_address_contracts: detailsDict = {} contract_db = os.path.join(dbfolder, 'smartContracts', f"{contract[3]}-{contract[4]}.db") # Make db connection and fetch contract structure conn = sqlite3.connect(contract_db) c = conn.cursor() # Get details of the type of Smart Contract c.execute('SELECT attribute,value FROM contractstructure') result = c.fetchall() contractStructure = {} conditionDict = {} counter = 0 for item in result: if list(item)[0] == 'exitconditions': conditionDict[counter] = list(item)[1] counter = counter + 1 else: contractStructure[list(item)[0]] = list(item)[1] if len(conditionDict) > 0: contractStructure['exitconditions'] = conditionDict del counter, conditionDict if contractStructure['contractType']=='continuos-event' and contractStructure['subtype']=='tokenswap': # normal result + swap details # what is a api detail c.execute('SELECT * FROM contractparticipants WHERE participantAddress=?',(floAddress,)) participant_details = c.fetchall() if len(participant_details) > 0: participationList = [] for participation in participant_details: c.execute("SELECT value FROM contractstructure WHERE attribute='selling_token'") structure = c.fetchall() detailsDict['participationAddress'] = floAddress detailsDict['participationAmount'] = participation[2] detailsDict['receivedAmount'] = float(participation[3]) detailsDict['participationToken'] = contractStructure['accepting_token'] detailsDict['receivedToken'] = contractStructure['selling_token'] detailsDict['swapPrice_received_to_participation'] = float(participation[7]) detailsDict['transactionHash'] = participation[4] detailsDict['blockNumber'] = participation[5] detailsDict['blockHash'] = participation[6] participationList.append(detailsDict) participationDetailsList.append(participationList) elif contractStructure['contractType']=='one-time-event' and 'payeeAddress' in contractStructure.keys(): # normal results conn = sqlite3.connect(dblocation) c = conn.cursor() detailsDict = {} detailsDict['contractName'] = contract[3] detailsDict['contractAddress'] = contract[4] detailsDict['tokenAmount'] = contract[5] detailsDict['transactionHash'] = contract[6] c.execute(f"select status, tokenIdentification, contractType, blockNumber, blockHash, incorporationDate, expiryDate, closeDate from activecontracts where contractName='{detailsDict['contractName']}' and contractAddress='{detailsDict['contractAddress']}'") temp = c.fetchall() detailsDict['status'] = temp[0][0] detailsDict['tokenIdentification'] = temp[0][1] detailsDict['contractType'] = temp[0][2] detailsDict['blockNumber'] = temp[0][3] detailsDict['blockHash'] = temp[0][4] detailsDict['incorporationDate'] = temp[0][5] if temp[0][6]: detailsDict['expiryDate'] = temp[0][6] if temp[0][7]: detailsDict['closeDate'] = temp[0][7] # check if the contract has been closed contractDbName = '{}-{}.db'.format(detailsDict['contractName'].strip(), detailsDict['contractAddress'].strip()) filelocation = os.path.join(dbfolder, 'smartContracts', contractDbName) if os.path.isfile(filelocation): # Make db connection and fetch data conn = sqlite3.connect(filelocation) c = conn.cursor() c.execute('SELECT attribute,value FROM contractstructure') result = c.fetchall() contractStructure = {} conditionDict = {} counter = 0 for item in result: if list(item)[0] == 'exitconditions': conditionDict[counter] = list(item)[1] counter = counter + 1 else: contractStructure[list(item)[0]] = list(item)[1] if len(conditionDict) > 0: contractStructure['exitconditions'] = conditionDict del counter, conditionDict if 'payeeAddress' in contractStructure: # contract is of the type external trigger # check if the contract has been closed c.execute(f"SELECT tokenAmount FROM contractparticipants where participantAddress='{floAddress}'") result = c.fetchall() conn.close() detailsDict['tokenAmount'] = result[0][0] elif contractStructure['contractType']=='one-time-event' and 'exitconditions' in contractStructure.keys(): # normal results + winning/losing details conn = sqlite3.connect(dblocation) c = conn.cursor() detailsDict = {} detailsDict['contractName'] = contract[3] detailsDict['contractAddress'] = contract[4] detailsDict['tokenAmount'] = contract[5] detailsDict['transactionHash'] = contract[6] c.execute(f"select status, tokenIdentification, contractType, blockNumber, blockHash, incorporationDate, expiryDate, closeDate from activecontracts where contractName='{detailsDict['contractName']}' and contractAddress='{detailsDict['contractAddress']}'") temp = c.fetchall() detailsDict['status'] = temp[0][0] detailsDict['tokenIdentification'] = temp[0][1] detailsDict['contractType'] = temp[0][2] detailsDict['blockNumber'] = temp[0][3] detailsDict['blockHash'] = temp[0][4] detailsDict['incorporationDate'] = temp[0][5] if temp[0][6]: detailsDict['expiryDate'] = temp[0][6] if temp[0][7]: detailsDict['closeDate'] = temp[0][7] # check if the contract has been closed contractDbName = '{}-{}.db'.format(detailsDict['contractName'].strip(), detailsDict['contractAddress'].strip()) filelocation = os.path.join(dbfolder, 'smartContracts', contractDbName) if os.path.isfile(filelocation): # Make db connection and fetch data conn = sqlite3.connect(filelocation) c = conn.cursor() c.execute('SELECT attribute,value FROM contractstructure') result = c.fetchall() contractStructure = {} conditionDict = {} counter = 0 for item in result: if list(item)[0] == 'exitconditions': conditionDict[counter] = list(item)[1] counter = counter + 1 else: contractStructure[list(item)[0]] = list(item)[1] if len(conditionDict) > 0: contractStructure['exitconditions'] = conditionDict del counter, conditionDict if 'exitconditions' in contractStructure: # contract is of the type external trigger # check if the contract has been closed c.execute('select * from contractTransactionHistory where transactionType="trigger"') trigger = c.fetchall() if detailsDict['status'] == 'closed': c.execute(f"SELECT userChoice, winningAmount FROM contractparticipants where participantAddress='{floAddress}'") result = c.fetchall() conn.close() detailsDict['userChoice'] = result[0][0] detailsDict['winningAmount'] = result[0][1] else: c.execute(f"SELECT userChoice FROM contractparticipants where participantAddress='{floAddress}'") result = c.fetchall() conn.close() detailsDict['userChoice'] = result[0][0] participationDetailsList.append(detailsDict) return jsonify(result='ok', floAddress=floAddress, type='participant', participatedContracts=participationDetailsList) else: return jsonify(result='error', description='Address hasn\'t participated in any other contract') else: return jsonify(result='error', description='System error. System db is missing') @app.route('/api/v1.0/getSmartContractTransactions', methods=['GET']) async def getsmartcontracttransactions(): contractName = request.args.get('contractName') contractAddress = request.args.get('contractAddress') if contractName is None: return jsonify(result='error', description='Smart Contract\'s name hasn\'t been passed') if contractAddress is None: return jsonify(result='error', description='Smart Contract\'s address hasn\'t been passed') contractDbName = '{}-{}.db'.format(contractName.strip(), contractAddress.strip()) filelocation = os.path.join(dbfolder, 'smartContracts', contractDbName) if os.path.isfile(filelocation): # Make db connection and fetch data conn = sqlite3.connect(filelocation) c = conn.cursor() c.execute('select jsonData, parsedFloData from contractTransactionHistory') result = c.fetchall() conn.close() returnval = {} for item in result: temp = {} temp['transactionDetails'] = json.loads(item[0]) temp['parsedFloData'] = json.loads(item[1]) returnval[temp['transactionDetails']['txid']] = temp return jsonify(result='ok', contractName=contractName, contractAddress=contractAddress, contractTransactions=returnval) else: return jsonify(result='error', description='Smart Contract with the given name doesn\'t exist') @app.route('/api/v1.0/getBlockDetails/', methods=['GET']) async def getblockdetails(blockdetail): blockJson = blockdetailhelper(blockdetail) if len(blockJson) != 0: blockJson = json.loads(blockJson[0][0]) return jsonify(result='ok', blockDetails=blockJson) else: return jsonify(result='error', description='Block doesn\'t exist in database') @app.route('/api/v1.0/getTransactionDetails/', methods=['GET']) async def gettransactiondetails(transactionHash): transactionJsonData = transactiondetailhelper(transactionHash) if len(transactionJsonData) != 0: transactionJson = json.loads(transactionJsonData[0][0]) parseResult = json.loads(transactionJsonData[0][1]) return jsonify(parsedFloData=parseResult, transactionDetails=transactionJson, transactionHash=transactionHash, result='ok') else: return jsonify(result='error', description='Transaction doesn\'t exist in database') @app.route('/api/v1.0/getLatestTransactionDetails', methods=['GET']) async def getLatestTransactionDetails(): numberOfLatestBlocks = request.args.get('numberOfLatestBlocks') dblocation = dbfolder + '/latestCache.db' if os.path.exists(dblocation): conn = sqlite3.connect(dblocation) c = conn.cursor() else: return 'Latest transactions db doesn\'t exist. This is unusual, please report on https://github.com/ranchimall/ranchimallflo-api' if numberOfLatestBlocks is not None: c.execute('SELECT * FROM latestTransactions WHERE blockNumber IN (SELECT DISTINCT blockNumber FROM latestTransactions ORDER BY blockNumber DESC LIMIT {}) ORDER BY id ASC;'.format(int(numberOfLatestBlocks))) latestTransactions = c.fetchall() c.close() tempdict = {} for idx, item in enumerate(latestTransactions): item = list(item) tx_parsed_details = {} tx_parsed_details['transactionDetails'] = json.loads(item[3]) tx_parsed_details['parsedFloData'] = json.loads(item[5]) tx_parsed_details['parsedFloData']['transactionType'] = item[4] tx_parsed_details['transactionDetails']['blockheight'] = int(item[2]) tempdict[json.loads(item[3])['txid']] = tx_parsed_details else: c.execute('''SELECT * FROM latestTransactions WHERE blockNumber IN (SELECT DISTINCT blockNumber FROM latestTransactions ORDER BY blockNumber DESC LIMIT 100) ORDER BY id ASC;''') latestTransactions = c.fetchall() c.close() tempdict = {} for idx, item in enumerate(latestTransactions): item = list(item) tx_parsed_details = {} tx_parsed_details['transactionDetails'] = json.loads(item[3]) tx_parsed_details['parsedFloData'] = json.loads(item[5]) tx_parsed_details['parsedFloData']['transactionType'] = item[4] tx_parsed_details['transactionDetails']['blockheight'] = int(item[2]) tempdict[json.loads(item[3])['txid']] = tx_parsed_details return jsonify(result='ok', latestTransactions=tempdict) @app.route('/api/v1.0/getLatestBlockDetails', methods=['GET']) async def getLatestBlockDetails(): limit = request.args.get('limit') dblocation = dbfolder + '/latestCache.db' if os.path.exists(dblocation): conn = sqlite3.connect(dblocation) c = conn.cursor() else: return 'Latest transactions db doesn\'t exist. This is unusual, please report on https://github.com/ranchimall/ranchimallflo-api' if limit is None: c.execute('''SELECT * FROM ( SELECT * FROM latestBlocks ORDER BY blockNumber DESC LIMIT 4) ORDER BY id ASC;''') else: int(limit) c.execute('SELECT * FROM ( SELECT * FROM latestBlocks ORDER BY blockNumber DESC LIMIT {}) ORDER BY id ASC;'.format(limit)) latestBlocks = c.fetchall() c.close() tempdict = {} for idx, item in enumerate(latestBlocks): tempdict[json.loads(item[3])['hash']] = json.loads(item[3]) return jsonify(result='ok', latestBlocks=tempdict) @app.route('/api/v1.0/getBlockTransactions/', methods=['GET']) async def getblocktransactions(blockdetail): blockJson = blockdetailhelper(blockdetail) if len(blockJson) != 0: blockJson = json.loads(blockJson[0][0]) blocktxlist = blockJson['tx'] blocktxs = {} for i in range(len(blocktxlist)): temptx = transactiondetailhelper(blocktxlist[i]) transactionJson = json.loads(temptx[0][0]) parseResult = json.loads(temptx[0][1]) blocktxs[blocktxlist[i]] = { "parsedFloData" : parseResult, "transactionDetails" : transactionJson } return jsonify(result='ok', transactions=blocktxs, blockKeyword=blockdetail) else: return jsonify(result='error', description='Block doesn\'t exist in database') @app.route('/api/v1.0/categoriseString/') async def categoriseString(urlstring): # check if the hash is of a transaction response = requests.get('{}tx/{}'.format(apiUrl, urlstring)) if response.status_code == 200: return jsonify(type='transaction') else: response = requests.get('{}block/{}'.format(apiUrl, urlstring)) if response.status_code == 200: return jsonify(type='block') else: # check urlstring is a token name tokenfolder = os.path.join(dbfolder, 'tokens') onlyfiles = [f[:-3] for f in os.listdir(tokenfolder) if os.path.isfile(os.path.join(tokenfolder, f))] if urlstring.lower() in onlyfiles: return jsonify(type='token') else: contractfolder = os.path.join(dbfolder, 'system.db') conn = sqlite3.connect(contractfolder) conn.row_factory = lambda cursor, row: row[0] c = conn.cursor() contractList = c.execute( 'select contractname from activeContracts').fetchall() if urlstring.lower() in contractList: return jsonify(type='smartContract') else: return jsonify(type='noise') @app.route('/api/v1.0/getTokenSmartContractList', methods=['GET']) async def getTokenSmartContractList(): # list of tokens filelist = [] for item in os.listdir(os.path.join(dbfolder, 'tokens')): if os.path.isfile(os.path.join(dbfolder, 'tokens', item)): filelist.append(item[:-3]) # list of smart contracts conn = sqlite3.connect(os.path.join(dbfolder, 'system.db')) c = conn.cursor() contractList = [] c.execute('select * from activecontracts') allcontractsDetailList = c.fetchall() for idx, contract in enumerate(allcontractsDetailList): contractDict = {} contractDict['contractName'] = contract[1] contractDict['contractAddress'] = contract[2] contractDict['status'] = contract[3] contractDict['tokenIdentification'] = contract[4] contractDict['contractType'] = contract[5] contractDict['transactionHash'] = contract[6] contractDict['blockNumber'] = contract[7] contractDict['blockHash'] = contract[8] contractDict['incorporationDate'] = contract[9] if contract[10]: contractDict['expiryDate'] = contract[10] if contract[11]: contractDict['closeDate'] = contract[11] contractList.append(contractDict) return jsonify(tokens=filelist, smartContracts=contractList, result='ok') ################### ### VERSION 2 ### ################### @app.route('/api/v2/info', methods=['GET']) async def info(): # query for the number of flo addresses in tokenAddress mapping conn = sqlite3.connect(os.path.join(dbfolder, 'system.db')) c = conn.cursor() tokenAddressCount = c.execute('select count(distinct tokenAddress) from tokenAddressMapping').fetchall()[0][0] tokenCount = c.execute('select count(distinct token) from tokenAddressMapping').fetchall()[0][0] contractCount = c.execute('select count(distinct contractName) from contractAddressMapping').fetchall()[0][0] lastscannedblock = int(c.execute("select value from systemData where attribute=='lastblockscanned'").fetchall()[0][0]) conn.close() # query for total number of validated blocks conn = sqlite3.connect(os.path.join(dbfolder, 'latestCache.db')) c = conn.cursor() validatedBlockCount = c.execute('select count(distinct blockNumber) from latestBlocks').fetchall()[0][0] validatedTransactionCount = c.execute('select count(distinct transactionHash) from latestTransactions').fetchall()[0][0] conn.close() return jsonify(systemAddressCount=tokenAddressCount, systemBlockCount=validatedBlockCount, systemTransactionCount=validatedTransactionCount, systemSmartContractCount=contractCount, systemTokenCount=tokenCount, lastscannedblock=lastscannedblock), 200 @app.route('/api/v2/broadcastTx/') async def broadcastTx_v2(raw_transaction_hash): p1 = subprocess.run(['flo-cli',f"-datadir={FLO_DATA_DIR}",'sendrawtransaction',raw_transaction_hash], capture_output=True) return jsonify(args=p1.args,returncode=p1.returncode,stdout=p1.stdout.decode(),stderr=p1.stderr.decode()), 200 # FLO TOKEN APIs @app.route('/api/v2/tokenList', methods=['GET']) async def tokenList(): filelist = [] for item in os.listdir(os.path.join(dbfolder, 'tokens')): if os.path.isfile(os.path.join(dbfolder, 'tokens', item)): filelist.append(item[:-3]) return jsonify(tokens=filelist), 200 @app.route('/api/v2/tokenInfo/', methods=['GET']) async def tokenInfo(token): if token is None: return jsonify(description='token name hasnt been passed'), 400 # todo : input validation dblocation = dbfolder + '/tokens/' + str(token) + '.db' if os.path.exists(dblocation): conn = sqlite3.connect(dblocation) c = conn.cursor() else: return jsonify(description='token doesn\'t exist'), 404 c.execute('SELECT * FROM transactionHistory WHERE id=1') incorporationRow = c.fetchall()[0] c.execute('SELECT COUNT (DISTINCT address) FROM activeTable') numberOf_distinctAddresses = c.fetchall()[0][0] c.execute('select max(id) from transactionHistory') numberOf_transactions = c.fetchall()[0][0] c.execute('select contractName, contractAddress, blockNumber, blockHash, transactionHash from tokenContractAssociation') associatedContracts = c.fetchall() conn.close() associatedContractList = [] for item in associatedContracts: tempdict = {} item = list(item) tempdict['contractName'] = item[0] tempdict['contractAddress'] = item[1] tempdict['blockNumber'] = item[2] tempdict['blockHash'] = item[3] tempdict['transactionHash'] = item[4] associatedContractList.append(tempdict) return jsonify(token=token, incorporationAddress=incorporationRow[1], tokenSupply=incorporationRow[3], time=incorporationRow[6], blockchainReference=incorporationRow[7], activeAddress_no=numberOf_distinctAddresses, totalTransactions=numberOf_transactions, associatedSmartContracts=associatedContractList), 200 @app.route('/api/v2/tokenTransactions/', methods=['GET']) async def tokenTransactions(token): if token is None: return jsonify(description='token name hasnt been passed'), 400 # Input validations senderFloAddress = request.args.get('senderFloAddress') if senderFloAddress is not None and not check_flo_address(senderFloAddress, is_testnet): return jsonify(description='senderFloAddress validation failed'), 400 destFloAddress = request.args.get('destFloAddress') if destFloAddress is not None and not check_flo_address(destFloAddress, is_testnet): return jsonify(description='destFloAddress validation failed'), 400 limit = request.args.get('limit') if limit is not None and not check_integer(limit): return jsonify(description='limit validation failed'), 400 dblocation = dbfolder + '/tokens/' + str(token) + '.db' if os.path.exists(dblocation): conn = sqlite3.connect(dblocation) conn.row_factory = sqlite3.Row c = conn.cursor() else: return jsonify(description='token doesn\'t exist'), 404 if senderFloAddress and not destFloAddress: if limit is None: c.execute('SELECT jsonData, parsedFloData FROM transactionHistory WHERE sourceFloAddress="{}" ORDER BY id DESC LIMIT 100'.format(senderFloAddress)) else: c.execute('SELECT jsonData, parsedFloData FROM transactionHistory WHERE sourceFloAddress="{}" ORDER BY id DESC LIMIT {}'.format(senderFloAddress, limit)) elif not senderFloAddress and destFloAddress: if limit is None: c.execute('SELECT jsonData, parsedFloData FROM transactionHistory WHERE destFloAddress="{}" ORDER BY id DESC LIMIT 100'.format(destFloAddress)) else: c.execute('SELECT jsonData, parsedFloData FROM transactionHistory WHERE destFloAddress="{}" ORDER BY id DESC LIMIT {}'.format(destFloAddress, limit)) elif senderFloAddress and destFloAddress: if limit is None: c.execute('SELECT jsonData, parsedFloData FROM transactionHistory WHERE sourceFloAddress="{}" AND destFloAddress="{}" ORDER BY id DESC LIMIT 100'.format(senderFloAddress, destFloAddress)) else: c.execute('SELECT jsonData, parsedFloData FROM transactionHistory WHERE sourceFloAddress="{}" AND destFloAddress="{}" ORDER BY id DESC LIMIT {}'.format(senderFloAddress, destFloAddress, limit)) else: if limit is None: c.execute('SELECT jsonData, parsedFloData FROM transactionHistory ORDER BY id DESC LIMIT 100') else: c.execute('SELECT jsonData, parsedFloData FROM transactionHistory ORDER BY id DESC LIMIT {}'.format(limit)) transactionJsonData = c.fetchall() conn.close() rowarray_list = {} for row in transactionJsonData: temp = {} temp['transactionDetails'] = json.loads(row[0]) temp['parsedFloData'] = json.loads(row[1]) rowarray_list[temp['transactionDetails']['txid']] = temp return jsonify(token=token, transactions=rowarray_list), 200 @app.route('/api/v2/tokenBalances/', methods=['GET']) async def tokenBalances(token): if token is None: return jsonify(description='token name hasnt been passed'), 400 dblocation = dbfolder + '/tokens/' + str(token) + '.db' if os.path.exists(dblocation): conn = sqlite3.connect(dblocation) c = conn.cursor() else: return jsonify(description='token doesn\'t exist'), 404 c.execute('SELECT address,SUM(transferBalance) FROM activeTable GROUP BY address') addressBalances = c.fetchall() returnList = {} for address in addressBalances: returnList[address[0]] = address[1] return jsonify(token=token, balances=returnList), 200 # FLO Address APIs @app.route('/api/v2/floAddressInfo/', methods=['GET']) async def floAddressInfo(floAddress): if floAddress is None: return jsonify(description='floAddress hasn\'t been passed'), 400 # input validation if not check_flo_address(floAddress, is_testnet): return jsonify(description='floAddress validation failed'), 400 dblocation = dbfolder + '/system.db' if os.path.exists(dblocation): conn = sqlite3.connect(dblocation) c = conn.cursor() c.execute(f'SELECT token FROM tokenAddressMapping WHERE tokenAddress="{floAddress}"') tokenNames = c.fetchall() c.execute(f"SELECT contractName, status, tokenIdentification, contractType, transactionHash, blockNumber, blockHash FROM activecontracts WHERE contractAddress='{floAddress}'") incorporatedContracts = c.fetchall() if len(tokenNames) != 0: detailList = {} for token in tokenNames: token = token[0] dblocation = dbfolder + '/tokens/' + str(token) + '.db' if os.path.exists(dblocation): tempdict = {} conn = sqlite3.connect(dblocation) c = conn.cursor() c.execute(f'SELECT SUM(transferBalance) FROM activeTable WHERE address="{floAddress}"') balance = c.fetchall()[0][0] tempdict['balance'] = balance tempdict['token'] = token detailList[token] = tempdict else: # Address is not associated with any token return jsonify(description='FLO address is not associated with any tokens'), 404 if len(incorporatedContracts) != 0: incorporatedSmartContracts = [] for contract in incorporatedContracts: tempdict = {} tempdict['contractName'] = contract[0] tempdict['contractAddress'] = floAddress tempdict['status'] = contract[1] tempdict['tokenIdentification'] = contract[2] tempdict['contractType'] = contract[3] tempdict['transactionHash'] = contract[4] tempdict['blockNumber'] = contract[5] tempdict['blockHash'] = contract[6] incorporatedSmartContracts.append(tempdict) else: incorporatedContracts=None return jsonify(floAddress=floAddress, floAddressBalances=detailList, incorporatedSmartContracts=None), 200 @app.route('/api/v2/floAddressBalance/', methods=['GET']) async def floAddressBalance(floAddress): if floAddress is None: return jsonify(description='floAddress hasn\'t been passed'), 400 # input validation if not check_flo_address(floAddress, is_testnet): return jsonify(description='floAddress validation failed'), 400 token = request.args.get('token') if token is None: dblocation = dbfolder + '/system.db' if os.path.exists(dblocation): conn = sqlite3.connect(dblocation) c = conn.cursor() c.execute(f'SELECT token FROM tokenAddressMapping WHERE tokenAddress="{floAddress}"') tokenNames = c.fetchall() if len(tokenNames) != 0: detailList = {} for token in tokenNames: token = token[0] dblocation = dbfolder + '/tokens/' + str(token) + '.db' if os.path.exists(dblocation): tempdict = {} conn = sqlite3.connect(dblocation) c = conn.cursor() c.execute(f'SELECT SUM(transferBalance) FROM activeTable WHERE address="{floAddress}"') balance = c.fetchall()[0][0] tempdict['balance'] = balance tempdict['token'] = token detailList[token] = tempdict return jsonify(floAddress=floAddress, floAddressBalances=detailList), 200 else: # Address is not associated with any token return jsonify(floAddress=floAddress, floAddressBalances={}), 200 else: dblocation = dbfolder + '/tokens/' + str(token) + '.db' if os.path.exists(dblocation): conn = sqlite3.connect(dblocation) c = conn.cursor() else: return jsonify(description="token doesn't exist"), 404 c.execute(f'SELECT SUM(transferBalance) FROM activeTable WHERE address="{floAddress}"') balance = c.fetchall()[0][0] conn.close() return jsonify(token=token, floAddress=floAddress, balance=balance), 200 @app.route('/api/v2/floAddressTransactions/', methods=['GET']) async def floAddressTransactions(floAddress): if floAddress is None: return jsonify(description='floAddress has not been passed'), 400 if not check_flo_address(floAddress, is_testnet): return jsonify(description='floAddress validation failed'), 400 limit = request.args.get('limit') if limit is not None and not check_integer(limit): return jsonify(description='limit validation failed'), 400 token = request.args.get('token') if token is None: dblocation = dbfolder + '/system.db' if os.path.exists(dblocation): conn = sqlite3.connect(dblocation) c = conn.cursor() c.execute('select token from tokenAddressMapping where tokenAddress="{}"'.format(floAddress)) tokenNames = c.fetchall() else: dblocation = dbfolder + '/tokens/' + str(token) + '.db' if os.path.exists(dblocation): tokenNames = [[str(token), ]] else: return jsonify(description='token doesn\'t exist'), 404 if len(tokenNames) != 0: allTransactionList = {} for tokenname in tokenNames: tokenname = tokenname[0] dblocation = dbfolder + '/tokens/' + str(tokenname) + '.db' if os.path.exists(dblocation): tempdict = {} conn = sqlite3.connect(dblocation) c = conn.cursor() if limit is None: c.execute(f'SELECT jsonData, parsedFloData FROM transactionHistory WHERE sourceFloAddress="{floAddress}" OR destFloAddress="{floAddress}" ORDER BY id DESC LIMIT 100') else: c.execute(f'SELECT jsonData, parsedFloData FROM transactionHistory WHERE sourceFloAddress="{floAddress}" OR destFloAddress="{floAddress}" ORDER BY id DESC LIMIT {limit}') transactionJsonData = c.fetchall() conn.close() for row in transactionJsonData: temp = {} temp['transactionDetails'] = json.loads(row[0]) temp['parsedFloData'] = json.loads(row[1]) allTransactionList[temp['transactionDetails']['txid']] = temp if token is None: return jsonify(floAddress=floAddress, transactions=allTransactionList), 200 else: return jsonify(floAddress=floAddress, transactions=allTransactionList, token=token), 200 else: return jsonify(floAddress=floAddress, transactions={}, token=token), 200 # SMART CONTRACT APIs @app.route('/api/v2/smartContractList', methods=['GET']) async def getContractList_v2(): contractName = request.args.get('contractName') contractAddress = request.args.get('contractAddress') # todo - Add validation for contractAddress and contractName to prevent SQL injection attacks if contractAddress is not None and not check_flo_address(contractAddress, is_testnet): return jsonify(description='contractAddress validation failed'), 400 contractList = [] conn = sqlite3.connect(os.path.join(dbfolder, 'system.db')) c = conn.cursor() smart_contracts = return_smart_contracts(c, contractName, contractAddress) for idx, contract in enumerate(smart_contracts): contractDict = {} contractDict['contractName'] = contract[1] contractDict['contractAddress'] = contract[2] contractDict['status'] = contract[3] contractDict['tokenIdentification'] = contract[4] contractDict['contractType'] = contract[5] contractDict['transactionHash'] = contract[6] contractDict['blockNumber'] = contract[7] contractDict['incorporationDate'] = contract[8] contractDict['expiryDate'] = contract[9] contractDict['closeDate'] = contract[10] contractList.append(contractDict) conn.close() return jsonify(smartContracts=contractList, result='ok') @app.route('/api/v2/smartContractInfo', methods=['GET']) async def getContractInfo_v2(): contractName = request.args.get('contractName') contractAddress = request.args.get('contractAddress') if contractName is None: return jsonify(description='Smart Contract\'s name hasn\'t been passed'), 400 contractName = contractName.strip() if contractAddress is None: return jsonify(description='Smart Contract\'s address hasn\'t been passed'), 400 contractAddress = contractAddress.strip() if not check_flo_address(contractAddress, is_testnet): return jsonify(description='contractAddress validation failed'), 400 contractStructure = fetchContractStructure(contractName, contractAddress) if contractStructure: returnval = contractStructure # Categorize into what type of contract it is right now if contractStructure['contractType'] == 'continuos-event' and contractStructure['subtype'] == 'tokenswap': conn, c = create_database_connection('smart_contract', {'contract_name': contractName, 'contract_address': contractAddress}) c.execute('select count(participantAddress), sum(tokenAmount), sum(winningAmount) from contractparticipants') participation_details = c.fetchall() returnval['numberOfParticipants'] = participation_details[0][0] returnval['totalParticipationAmount'] = participation_details[0][1] returnval['totalHonorAmount'] = participation_details[0][2] c.execute('SELECT COUNT(DISTINCT transactionHash) FROM contractdeposits') returnval['numberOfDeposits'] = c.fetchall()[0][0] elif contractStructure['contractType'] == 'one-time-event' and 'exitconditions' in contractStructure.keys(): returnval['userChoice'] = contractStructure['exitconditions'] returnval.pop('exitconditions') conn, c = create_database_connection('system_dbs') c.execute('select status, incorporationDate, expiryDate, closeDate from activecontracts where contractName=="{}" and contractAddress=="{}"'.format(contractName.strip(), contractAddress.strip())) results = c.fetchall() if len(results) == 1: for result in results: returnval['status'] = result[0] returnval['incorporationDate'] = result[1] if result[2]: returnval['expiryDate'] = result[2] if result[3]: returnval['closeDate'] = result[3] elif contractStructure['contractType'] == 'one-time-event' and 'payeeAddress' in contractStructure.keys(): pass return jsonify(contractName=contractName, contractAddress=contractAddress, contractInfo=returnval), 200 else: return jsonify(details='Smart Contract with the given name doesn\'t exist'), 404 @app.route('/api/v2/smartContractParticipants', methods=['GET']) async def getcontractparticipants_v2(): contractName = request.args.get('contractName') contractName = contractName.strip() if contractName is None: return jsonify(description='Smart Contract\'s name hasn\'t been passed'), 400 contractAddress = request.args.get('contractAddress') contractAddress = contractAddress.strip() if contractAddress is None: return jsonify(description='Smart Contract\'s address hasn\'t been passed'), 400 if not check_flo_address(contractAddress, is_testnet): return jsonify(description='contractAddress validation failed'), 400 filelocation = os.path.join(dbfolder, 'smartContracts', '{}-{}.db'.format(contractName, contractAddress)) if os.path.isfile(filelocation): # Make db connection and fetch data contractStructure = fetchContractStructure(contractName, contractAddress) contractStatus = fetchContractStatus(contractName, contractAddress) conn, c = create_database_connection('smart_contract', {'contract_name': contractName, 'contract_address': contractAddress}) if 'exitconditions' in contractStructure: # contract is of the type external trigger # check if the contract has been closed if contractStatus == 'closed': token = contractStructure['tokenIdentification'] c.execute('SELECT id, participantAddress, tokenAmount, userChoice, transactionHash, winningAmount FROM contractparticipants') result = c.fetchall() returnval = {} for row in result: returnval[row[1]] = {'participantFloAddress': row[1], 'tokenAmount': row[2], 'userChoice': row[3], 'transactionHash': row[4], 'winningAmount': row[5], 'tokenIdentification': token} else: c.execute('SELECT id, participantAddress, tokenAmount, userChoice, transactionHash FROM contractparticipants') result = c.fetchall() conn.close() returnval = {} for row in result: returnval[row[1]] = {'participantFloAddress': row[1], 'tokenAmount': row[2], 'userChoice': row[3], 'transactionHash': row[4]} elif 'payeeAddress' in contractStructure: # contract is of the type internal trigger c.execute('SELECT id, participantAddress, tokenAmount, userChoice, transactionHash FROM contractparticipants') result = c.fetchall() conn.close() returnval = {} for row in result: returnval[row[1]] = {'participantFloAddress': row[1], 'tokenAmount': row[2], 'transactionHash': row[4]} elif contractStructure['contractType'] == 'continuos-event' and contractStructure['subtype'] == 'tokenswap': c.execute('SELECT * FROM contractparticipants') contract_participants = c.fetchall() returnval = {} for row in contract_participants: returnval[row[1]] = { 'participantFloAddress': row[1], 'participationAmount': row[2], 'swapPrice': float(row[3]), 'transactionHash': row[4], 'blockNumber': row[5], 'blockHash': row[6], 'swapAmount': row[7] } conn.close() return jsonify(contractName=contractName, contractAddress=contractAddress, participantInfo=returnval), 200 else: return jsonify(description='Smart Contract with the given name doesn\'t exist'), 404 @app.route('/api/v2/participantDetails/', methods=['GET']) async def participantDetails(): if floAddress is None: return jsonify(description='FLO address hasn\'t been passed'), 400 if not check_flo_address(floAddress, is_testnet): return jsonify(description='floAddress validation failed'), 400 contractName = request.args.get('contractName') contractName = contractName.strip() contractAddress = request.args.get('contractAddress') contractAddress = contractAddress.strip() if contractAddress is not None and not check_flo_address(contractAddress, is_testnet): return jsonify(description='contractAddress validation failed'), 400 if (contractName and contractAddress is None) or (contractName is None and contractAddress): return jsonify(description='pass both, contractName and contractAddress as url parameters'), 400 dblocation = os.path.join(dbfolder, 'system.db') if os.path.isfile(dblocation): # Make db connection and fetch data conn = sqlite3.connect(dblocation) c = conn.cursor() if contractName is not None: c.execute(f'SELECT * FROM contractAddressMapping WHERE address="{floAddress}" AND addressType="participant" AND contractName="{contractName}" AND contractAddress="{contractAddress}"') else: c.execute(f'SELECT * FROM contractAddressMapping WHERE address="{floAddress}" AND addressType="participant"') participant_address_contracts = c.fetchall() if len(participant_address_contracts) != 0: participationDetailsList = [] for contract in participant_address_contracts: detailsDict = {} contract_db = os.path.join(dbfolder, 'smartContracts', f"{contract[3]}-{contract[4]}.db") # Make db connection and fetch contract structure conn = sqlite3.connect(contract_db) c = conn.cursor() # Get details of the type of Smart Contract c.execute('SELECT attribute,value FROM contractstructure') result = c.fetchall() contractStructure = {} conditionDict = {} counter = 0 for item in result: if list(item)[0] == 'exitconditions': conditionDict[counter] = list(item)[1] counter = counter + 1 else: contractStructure[list(item)[0]] = list(item)[1] if len(conditionDict) > 0: contractStructure['exitconditions'] = conditionDict del counter, conditionDict if contractStructure['contractType']=='continuos-event' and contractStructure['subtype']=='tokenswap': # normal result + swap details # what is a api detail c.execute('SELECT * FROM contractparticipants WHERE participantAddress=?',(floAddress,)) participant_details = c.fetchall() if len(participant_details) > 0: participationList = [] for participation in participant_details: c.execute("SELECT value FROM contractstructure WHERE attribute='selling_token'") structure = c.fetchall() detailsDict['participationAddress'] = floAddress detailsDict['participationAmount'] = participation[2] detailsDict['receivedAmount'] = float(participation[3]) detailsDict['participationToken'] = contractStructure['accepting_token'] detailsDict['receivedToken'] = contractStructure['selling_token'] detailsDict['swapPrice_received_to_participation'] = float(participation[7]) detailsDict['transactionHash'] = participation[4] detailsDict['blockNumber'] = participation[5] detailsDict['blockHash'] = participation[6] participationList.append(detailsDict) participationDetailsList.append(participationList) elif contractStructure['contractType']=='one-time-event' and 'payeeAddress' in contractStructure.keys(): # normal results conn = sqlite3.connect(dblocation) c = conn.cursor() detailsDict = {} detailsDict['contractName'] = contract[3] detailsDict['contractAddress'] = contract[4] detailsDict['tokenAmount'] = contract[5] detailsDict['transactionHash'] = contract[6] c.execute(f"select status, tokenIdentification, contractType, blockNumber, blockHash, incorporationDate, expiryDate, closeDate from activecontracts where contractName='{detailsDict['contractName']}' and contractAddress='{detailsDict['contractAddress']}'") temp = c.fetchall() detailsDict['status'] = temp[0][0] detailsDict['tokenIdentification'] = temp[0][1] detailsDict['contractType'] = temp[0][2] detailsDict['blockNumber'] = temp[0][3] detailsDict['blockHash'] = temp[0][4] detailsDict['incorporationDate'] = temp[0][5] if temp[0][6]: detailsDict['expiryDate'] = temp[0][6] if temp[0][7]: detailsDict['closeDate'] = temp[0][7] # check if the contract has been closed contractDbName = '{}-{}.db'.format(detailsDict['contractName'].strip(), detailsDict['contractAddress'].strip()) filelocation = os.path.join(dbfolder, 'smartContracts', contractDbName) if os.path.isfile(filelocation): # Make db connection and fetch data conn = sqlite3.connect(filelocation) c = conn.cursor() c.execute('SELECT attribute,value FROM contractstructure') result = c.fetchall() contractStructure = {} conditionDict = {} counter = 0 for item in result: if list(item)[0] == 'exitconditions': conditionDict[counter] = list(item)[1] counter = counter + 1 else: contractStructure[list(item)[0]] = list(item)[1] if len(conditionDict) > 0: contractStructure['exitconditions'] = conditionDict del counter, conditionDict if 'payeeAddress' in contractStructure: # contract is of the type external trigger # check if the contract has been closed c.execute(f"SELECT tokenAmount FROM contractparticipants where participantAddress='{floAddress}'") result = c.fetchall() conn.close() detailsDict['tokenAmount'] = result[0][0] elif contractStructure['contractType']=='one-time-event' and 'exitconditions' in contractStructure.keys(): # normal results + winning/losing details conn = sqlite3.connect(dblocation) c = conn.cursor() detailsDict = {} detailsDict['contractName'] = contract[3] detailsDict['contractAddress'] = contract[4] detailsDict['tokenAmount'] = contract[5] detailsDict['transactionHash'] = contract[6] c.execute(f"select status, tokenIdentification, contractType, blockNumber, blockHash, incorporationDate, expiryDate, closeDate from activecontracts where contractName='{detailsDict['contractName']}' and contractAddress='{detailsDict['contractAddress']}'") temp = c.fetchall() detailsDict['status'] = temp[0][0] detailsDict['tokenIdentification'] = temp[0][1] detailsDict['contractType'] = temp[0][2] detailsDict['blockNumber'] = temp[0][3] detailsDict['blockHash'] = temp[0][4] detailsDict['incorporationDate'] = temp[0][5] if temp[0][6]: detailsDict['expiryDate'] = temp[0][6] if temp[0][7]: detailsDict['closeDate'] = temp[0][7] # check if the contract has been closed contractDbName = '{}-{}.db'.format(detailsDict['contractName'].strip(), detailsDict['contractAddress'].strip()) filelocation = os.path.join(dbfolder, 'smartContracts', contractDbName) if os.path.isfile(filelocation): # Make db connection and fetch data conn = sqlite3.connect(filelocation) c = conn.cursor() c.execute('SELECT attribute,value FROM contractstructure') result = c.fetchall() contractStructure = {} conditionDict = {} counter = 0 for item in result: if list(item)[0] == 'exitconditions': conditionDict[counter] = list(item)[1] counter = counter + 1 else: contractStructure[list(item)[0]] = list(item)[1] if len(conditionDict) > 0: contractStructure['exitconditions'] = conditionDict del counter, conditionDict if 'exitconditions' in contractStructure: # contract is of the type external trigger # check if the contract has been closed c.execute('select * from contractTransactionHistory where transactionType="trigger"') trigger = c.fetchall() if detailsDict['status'] == 'closed': c.execute(f"SELECT userChoice, winningAmount FROM contractparticipants where participantAddress='{floAddress}'") result = c.fetchall() conn.close() detailsDict['userChoice'] = result[0][0] detailsDict['winningAmount'] = result[0][1] else: c.execute(f"SELECT userChoice FROM contractparticipants where participantAddress='{floAddress}'") result = c.fetchall() conn.close() detailsDict['userChoice'] = result[0][0] participationDetailsList.append(detailsDict) return jsonify(floAddress=floAddress, type='participant', participatedContracts=participationDetailsList), 200 else: return jsonify(description="Address hasn't participated in any other contract"), 404 else: return jsonify(description='System error. System db is missing'), 404 @app.route('/api/v2/smartContractTransactions', methods=['GET']) async def smartcontracttransactions(): contractName = request.args.get('contractName') contractName = contractName.strip() if contractName is None: return jsonify(description='Smart Contract\'s name hasn\'t been passed'), 400 contractAddress = request.args.get('contractAddress') contractAddress = contractAddress.strip() if contractAddress is None: return jsonify(description='Smart Contract\'s address hasn\'t been passed'), 400 if not check_flo_address(contractAddress, is_testnet): return jsonify(description='contractAddress validation failed'), 400 contractDbName = '{}-{}.db'.format(contractName.strip(), contractAddress.strip()) filelocation = os.path.join(dbfolder, 'smartContracts', contractDbName) if os.path.isfile(filelocation): # Make db connection and fetch data conn = sqlite3.connect(filelocation) c = conn.cursor() c.execute('select jsonData, parsedFloData from contractTransactionHistory') result = c.fetchall() conn.close() returnval = {} for item in result: temp = {} temp['transactionDetails'] = json.loads(item[0]) temp['parsedFloData'] = json.loads(item[1]) returnval[temp['transactionDetails']['txid']] = temp return jsonify(contractName=contractName, contractAddress=contractAddress, contractTransactions=returnval), 200 else: return jsonify(description='Smart Contract with the given name doesn\'t exist'), 404 @app.route('/api/v2/blockDetails/', methods=['GET']) async def blockdetails(blockHash): # todo - validate blockHash blockJson = blockdetailhelper(blockHash) if len(blockJson) != 0: blockJson = json.loads(blockJson[0][0]) return jsonify(blockDetails=blockJson), 200 else: return jsonify(description='Block doesn\'t exist in database'), 404 @app.route('/api/v2/transactionDetails/', methods=['GET']) async def transactiondetails1(transactionHash): # todo - validate transactionHash transactionJsonData = transactiondetailhelper(transactionHash) if len(transactionJsonData) != 0: transactionJson = json.loads(transactionJsonData[0][0]) parseResult = json.loads(transactionJsonData[0][1]) operation = transactionJsonData[0][2] db_reference = transactionJsonData[0][3] sender_address, receiver_address = extract_ip_op_addresses(transactionJson) operationDetails = {} if operation == 'smartContractDeposit': # open the db reference and check if there is a deposit return conn = sqlite3.connect(f"{dbfolder}/smartContracts/{db_reference}.db") c = conn.cursor() c.execute("SELECT depositAmount, blockNumber FROM contractdeposits WHERE status='deposit-return' AND transactionHash=?",(transactionJson['txid'],)) returned_deposit_tx = c.fetchall() if len(returned_deposit_tx) == 1: operationDetails['returned_depositAmount'] = returned_deposit_tx[0][0] operationDetails['returned_blockNumber'] = returned_deposit_tx[0][1] c.execute("SELECT depositAmount, blockNumber FROM contractdeposits WHERE status='deposit-honor' AND transactionHash=?",(transactionJson['txid'],)) deposit_honors = c.fetchall() operationDetails['depositHonors'] = {} operationDetails['depositHonors']['list'] = [] operationDetails['depositHonors']['count'] = len(deposit_honors) for deposit_honor in deposit_honors: operationDetails['depositHonors']['list'].append({'honor_amount':deposit_honor[0],'blockNumber':deposit_honor[1]}) c.execute("SELECT depositBalance FROM contractdeposits WHERE id=(SELECT max(id) FROM contractdeposits WHERE transactionHash=?)",(transactionJson['txid'],)) depositBalance = c.fetchall() operationDetails['depositBalance'] = depositBalance[0][0] operationDetails['consumedAmount'] = parseResult['depositAmount'] - operationDetails['depositBalance'] elif operation == 'tokenswap-participation': conn = sqlite3.connect(f"{dbfolder}/smartContracts/{db_reference}.db") c = conn.cursor() c.execute('SELECT tokenAmount, winningAmount, userChoice FROM contractparticipants WHERE transactionHash=?',(transactionJson['txid'],)) swap_amounts = c.fetchall() c.execute("SELECT value FROM contractstructure WHERE attribute='selling_token'") structure = c.fetchall() operationDetails['participationAmount'] = swap_amounts[0][0] operationDetails['receivedAmount'] = swap_amounts[0][1] operationDetails['participationToken'] = parseResult['tokenIdentification'] operationDetails['receivedToken'] = structure[0][0] operationDetails['swapPrice_received_to_participation'] = float(swap_amounts[0][2]) elif operation == 'smartContractPays': # Find what happened because of the trigger # Find who conn = sqlite3.connect(f"{dbfolder}/smartContracts/{db_reference}.db") c = conn.cursor() c.execute('SELECT participantAddress, tokenAmount, userChoice, winningAmount FROM contractparticipants WHERE winningAmount IS NOT NULL') winner_participants = c.fetchall() if len(winner_participants) != 0: operationDetails['total_winners'] = len(winner_participants) operationDetails['winning_choice'] = winner_participants[0][2] operationDetails['winner_list'] = [] for participant in winner_participants: winner_details = {} winner_details['participantAddress'] = participant[0] winner_details['participationAmount'] = participant[1] winner_details['winningAmount'] = participant[3] operationDetails['winner_list'].append(winner_details) elif operation == 'ote-externaltrigger-participation': # Find if this guy has won conn = sqlite3.connect(f"{dbfolder}/smartContracts/{db_reference}.db") c = conn.cursor() c.execute('SELECT winningAmount FROM contractparticipants WHERE transactionHash=?',(transactionHash,)) winningAmount = c.fetchall() if winningAmount[0][0] is not None: operationDetails['winningAmount'] = winningAmount[0][0] return jsonify(parsedFloData=parseResult, transactionDetails=transactionJson, transactionHash=transactionHash, operation=operation, operationDetails=operationDetails, senderAddress=sender_address, receiverAddress=receiver_address), 200 else: return jsonify(description='Transaction doesn\'t exist in database'), 404 @app.route('/api/v2/latestTransactionDetails', methods=['GET']) async def latestTransactionDetails(): numberOfLatestBlocks = request.args.get('numberOfLatestBlocks') if numberOfLatestBlocks is not None and not check_integer(numberOfLatestBlocks): return jsonify(description='numberOfLatestBlocks validation failed'), 400 dblocation = dbfolder + '/latestCache.db' if os.path.exists(dblocation): conn = sqlite3.connect(dblocation) c = conn.cursor() else: return jsonify(description='Latest transactions db doesn\'t exist. This is unusual, please report on https://github.com/ranchimall/ranchimallflo-api'), 500 if numberOfLatestBlocks is not None: c.execute('SELECT * FROM latestTransactions WHERE blockNumber IN (SELECT DISTINCT blockNumber FROM latestTransactions ORDER BY blockNumber DESC LIMIT {}) ORDER BY id ASC;'.format(int(numberOfLatestBlocks))) latestTransactions = c.fetchall() c.close() tempdict = {} for idx, item in enumerate(latestTransactions): item = list(item) tx_parsed_details = {} tx_parsed_details['transactionDetails'] = json.loads(item[3]) tx_parsed_details['parsedFloData'] = json.loads(item[5]) tx_parsed_details['parsedFloData']['transactionType'] = item[4] tx_parsed_details['transactionDetails']['blockheight'] = int(item[2]) tempdict[json.loads(item[3])['txid']] = tx_parsed_details else: c.execute('''SELECT * FROM latestTransactions WHERE blockNumber IN (SELECT DISTINCT blockNumber FROM latestTransactions ORDER BY blockNumber DESC LIMIT 100) ORDER BY id ASC;''') latestTransactions = c.fetchall() c.close() tempdict = {} for idx, item in enumerate(latestTransactions): item = list(item) tx_parsed_details = {} tx_parsed_details['transactionDetails'] = json.loads(item[3]) tx_parsed_details['parsedFloData'] = json.loads(item[5]) tx_parsed_details['parsedFloData']['transactionType'] = item[4] tx_parsed_details['transactionDetails']['blockheight'] = int(item[2]) tempdict[json.loads(item[3])['txid']] = tx_parsed_details return jsonify(latestTransactions=tempdict), 200 @app.route('/api/v2/latestBlockDetails', methods=['GET']) async def latestBlockDetails(): limit = request.args.get('limit') if limit is not None and not check_integer(limit): return jsonify(description='limit validation failed'), 400 dblocation = dbfolder + '/latestCache.db' if os.path.exists(dblocation): conn = sqlite3.connect(dblocation) c = conn.cursor() else: return jsonify(description='Latest transactions db doesn\'t exist. This is unusual, please report on https://github.com/ranchimall/ranchimallflo-api'), 404 if limit is None: c.execute('''SELECT * FROM ( SELECT * FROM latestBlocks ORDER BY blockNumber DESC LIMIT 4) ORDER BY id ASC;''') else: c.execute(f'SELECT * FROM ( SELECT * FROM latestBlocks ORDER BY blockNumber DESC LIMIT {limit}) ORDER BY id ASC;') latestBlocks = c.fetchall() c.close() tempdict = {} for idx, item in enumerate(latestBlocks): tempdict[json.loads(item[3])['hash']] = json.loads(item[3]) return jsonify(result='ok', latestBlocks=tempdict) @app.route('/api/v2/blockTransactions/', methods=['GET']) async def blocktransactions(blockHash): blockJson = blockdetailhelper(blockHash) if len(blockJson) != 0: blockJson = json.loads(blockJson[0][0]) blocktxlist = blockJson['tx'] blocktxs = {} for i in range(len(blocktxlist)): temptx = transactiondetailhelper(blocktxlist[i]) transactionJson = json.loads(temptx[0][0]) parseResult = json.loads(temptx[0][1]) blocktxs[blocktxlist[i]] = { "parsedFloData" : parseResult, "transactionDetails" : transactionJson } return jsonify(transactions=blocktxs, blockKeyword=blockHash), 200 else: return jsonify(description='Block doesn\'t exist in database'), 404 @app.route('/api/v2/categoriseString/') async def categoriseString_v2(urlstring): # check if the hash is of a transaction response = requests.get('{}tx/{}'.format(apiUrl, urlstring)) if response.status_code == 200: return jsonify(type='transaction'), 200 else: response = requests.get('{}block/{}'.format(apiUrl, urlstring)) if response.status_code == 200: return jsonify(type='block'), 200 else: # check urlstring is a token name tokenfolder = os.path.join(dbfolder, 'tokens') onlyfiles = [f[:-3] for f in os.listdir(tokenfolder) if os.path.isfile(os.path.join(tokenfolder, f))] if urlstring.lower() in onlyfiles: return jsonify(type='token'), 200 else: contractfolder = os.path.join(dbfolder, 'system.db') conn = sqlite3.connect(contractfolder) conn.row_factory = lambda cursor, row: row[0] c = conn.cursor() contractList = c.execute('select contractname from activeContracts').fetchall() if urlstring.lower() in contractList: return jsonify(type='smartContract'), 200 else: return jsonify(type='noise'), 200 @app.route('/api/v2/tokenSmartContractList', methods=['GET']) async def tokenSmartContractList(): # list of tokens filelist = [] for item in os.listdir(os.path.join(dbfolder, 'tokens')): if os.path.isfile(os.path.join(dbfolder, 'tokens', item)): filelist.append(item[:-3]) # list of smart contracts conn = sqlite3.connect(os.path.join(dbfolder, 'system.db')) c = conn.cursor() contractList = [] c.execute('select * from activecontracts') allcontractsDetailList = c.fetchall() for idx, contract in enumerate(allcontractsDetailList): contractDict = {} contractDict['contractName'] = contract[1] contractDict['contractAddress'] = contract[2] contractDict['status'] = contract[3] contractDict['tokenIdentification'] = contract[4] contractDict['contractType'] = contract[5] contractDict['transactionHash'] = contract[6] contractDict['blockNumber'] = contract[7] contractDict['blockHash'] = contract[8] contractDict['incorporationDate'] = contract[9] if contract[10]: contractDict['expiryDate'] = contract[10] if contract[11]: contractDict['closeDate'] = contract[11] contractList.append(contractDict) return jsonify(tokens=filelist, smartContracts=contractList), 200 class ServerSentEvent: def __init__( self, data: str, *, event: Optional[str] = None, id: Optional[int] = None, retry: Optional[int] = None, ) -> None: self.data = data self.event = event self.id = id self.retry = retry def encode(self) -> bytes: message = f"data: {self.data}" if self.event is not None: message = f"{message}\nevent: {self.event}" if self.id is not None: message = f"{message}\nid: {self.id}" if self.retry is not None: message = f"{message}\nretry: {self.retry}" message = f"{message}\r\n\r\n" return message.encode('utf-8') @app.route('/sse') async def sse(): queue = asyncio.Queue() app.clients.add(queue) async def send_events(): while True: try: data = await queue.get() event = ServerSentEvent(data) yield event.encode() except asyncio.CancelledError as error: app.clients.remove(queue) response = await make_response( send_events(), { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Transfer-Encoding': 'chunked', }, ) response.timeout = None return response @app.route('/api/v2/prices', methods=['GET']) async def priceData(): # read system.db for price data conn = sqlite3.connect('system.db') c = conn.cursor() ratepairs = c.execute('select ratepair, price from ratepairs') ratepairs = ratepairs.fetchall() prices = {} for ratepair in ratepairs: ratepair = list(ratepair) prices[ratepair[0]] = ratepair[1] return jsonify(prices=prices), 200 ####################### ####################### # if system.db isn't present, initialize it if not os.path.isfile(f"system.db"): # create an empty db conn = sqlite3.connect('system.db') c = conn.cursor() c.execute('''CREATE TABLE ratepairs (id integer primary key, ratepair text, price real)''') c.execute("INSERT INTO ratepairs(ratepair, price) VALUES ('BTCBTC', 1)") c.execute("INSERT INTO ratepairs(ratepair, price) VALUES ('BTCUSD', -1)") c.execute("INSERT INTO ratepairs(ratepair, price) VALUES ('BTCINR', -1)") c.execute("INSERT INTO ratepairs(ratepair, price) VALUES ('FLOUSD', -1)") c.execute("INSERT INTO ratepairs(ratepair, price) VALUES ('FLOINR', -1)") c.execute("INSERT INTO ratepairs(ratepair, price) VALUES ('USDINR', -1)") conn.commit() conn.close() # update the prices once updatePrices() # assign a scheduler for updating prices in the background scheduler = BackgroundScheduler() scheduler.add_job(func=updatePrices, trigger="interval", seconds=600) scheduler.start() # Shut down the scheduler when exiting the app atexit.register(lambda: scheduler.shutdown()) if __name__ == "__main__": app.run(debug=debug_status, host='0.0.0.0', port=5009)