369 lines
17 KiB
Python
369 lines
17 KiB
Python
import requests
|
|
import json
|
|
import sqlite3
|
|
import argparse
|
|
import configparser
|
|
import subprocess
|
|
import sys
|
|
import parsing
|
|
import os
|
|
import shutil
|
|
from sqlalchemy.orm import sessionmaker, relationship
|
|
from sqlalchemy import create_engine, func, desc
|
|
from models import Extra, TransactionHistory, TransactionTable, TransferLogs, Webtable, Base, ContractStructure, ContractBase, ContractParticipants, SystemBase, ActiveContracts
|
|
|
|
|
|
committeeAddressList = ['oUc4dVvxwK7w5MHUHtev8UawN3eDjiZnNx']
|
|
|
|
def transferToken(tokenIdentification, tokenAmount, inputAddress, outputAddress):
|
|
engine = create_engine('sqlite:///tokens/{}.db'.format(tokenIdentification), echo=True)
|
|
Base.metadata.create_all(bind=engine)
|
|
session = sessionmaker(bind=engine)()
|
|
availableTokens = session.query(func.sum(TransactionTable.transferBalance)).filter_by(address=inputAddress).all()[0][0]
|
|
commentTransferAmount = tokenAmount
|
|
if availableTokens is None:
|
|
print("The input address dosen't exist in our database ")
|
|
session.close()
|
|
return None
|
|
|
|
elif availableTokens < commentTransferAmount:
|
|
print(
|
|
"\nThe transfer amount passed in the comments is more than the user owns\nThis transaction will be discarded\n")
|
|
session.close()
|
|
return None
|
|
|
|
elif availableTokens >= commentTransferAmount:
|
|
table = session.query(TransactionTable).filter(TransactionTable.address==inputAddress, TransactionTable.transferBalance>0).all()
|
|
pidlst = []
|
|
checksum = 0
|
|
for row in table:
|
|
if checksum >= commentTransferAmount:
|
|
break
|
|
pidlst.append(row.id)
|
|
checksum = checksum + row.transferBalance
|
|
|
|
balance = commentTransferAmount
|
|
opbalance = session.query(func.sum(TransactionTable.transferBalance)).filter_by(address=outputAddress).all()[0][0]
|
|
|
|
if opbalance is None:
|
|
opbalance = 0
|
|
|
|
ipbalance = availableTokens
|
|
|
|
for pid in pidlst:
|
|
temp = session.query(TransactionTable.transferBalance).filter_by(id=pid).all()[0][0]
|
|
|
|
if balance <= temp:
|
|
|
|
session.add(TransactionTable(address=outputAddress, parentid=pid, transferBalance=balance))
|
|
entry = session.query(TransactionTable).filter(TransactionTable.id == pid).all()
|
|
entry[0].transferBalance = temp - balance
|
|
session.commit()
|
|
|
|
## transaction logs section ##
|
|
result = session.query(TransactionTable.id).order_by(desc(TransactionTable.id)).all()
|
|
lastid = result[-1].id
|
|
transferDescription = str(balance) + " tokens transferred to " + str(
|
|
outputAddress) + " from " + str(inputAddress)
|
|
blockchainReference = '{}tx/{}'.format(neturl, str(transaction))
|
|
session.add(TransferLogs(primaryIDReference=lastid, transferDescription=transferDescription,
|
|
transferIDConsumed=pid, blockchainReference=blockchainReference))
|
|
transferDescription = str(inputAddress) + " balance UPDATED from " + str(
|
|
temp) + " to " + str(temp - balance)
|
|
blockchainReference = '{}tx/{}'.format(neturl, str(transaction))
|
|
session.add(TransferLogs(primaryIDReference=pid, transferDescription=transferDescription,
|
|
blockchainReference=blockchainReference))
|
|
|
|
## transaction history table ##
|
|
session.add(TransactionHistory(blockno=blockindex, fromAddress=inputAddress,
|
|
toAddress=outputAddress, amount=str(balance),
|
|
blockchainReference=blockchainReference))
|
|
|
|
##webpage table section ##
|
|
transferDescription = str(commentTransferAmount) + " tokens transferred from " + str(
|
|
inputAddress) + " to " + str(outputAddress)
|
|
session.add(
|
|
Webtable(transferDescription=transferDescription, blockchainReference=blockchainReference))
|
|
|
|
transferDescription = "UPDATE " + str(outputAddress) + " balance from " + str(
|
|
opbalance) + " to " + str(opbalance + commentTransferAmount)
|
|
session.add(
|
|
Webtable(transferDescription=transferDescription, blockchainReference=blockchainReference))
|
|
|
|
transferDescription = "UPDATE " + str(inputAddress) + " balance from " + str(
|
|
ipbalance) + " to " + str(ipbalance - commentTransferAmount)
|
|
session.add(
|
|
Webtable(transferDescription=transferDescription, blockchainReference=blockchainReference))
|
|
|
|
balance = 0
|
|
session.commit()
|
|
|
|
elif balance > temp:
|
|
session.add(TransactionTable(address=outputAddress, parentid=pid, transferBalance=temp))
|
|
entry = session.query(TransactionTable).filter(TransactionTable.id == pid).all()
|
|
entry[0].transferBalance = 0
|
|
session.commit()
|
|
|
|
##transaction logs section ##
|
|
result = session.query(TransactionTable.id).order_by(desc(TransactionTable.id)).all()
|
|
lastid = result[-1].id
|
|
transferDescription = str(temp) + " tokens transferred to " + str(outputAddress) + " from " + str(inputAddress)
|
|
blockchainReference = '{}tx/{}'.format(neturl, str(transaction))
|
|
session.add(TransferLogs(primaryIDReference=lastid, transferDescription=transferDescription,
|
|
transferIDConsumed=pid, blockchainReference=blockchainReference))
|
|
|
|
transferDescription = str() + " balance UPDATED from " + str(temp) + " to " + str(0)
|
|
blockchainReference = '{}tx/{}'.format(neturl, str(transaction))
|
|
session.add(TransferLogs(primaryIDReference=pid, transferDescription=transferDescription,
|
|
blockchainReference=blockchainReference))
|
|
|
|
## transaction history table ##
|
|
session.add(TransactionHistory(blockno=blockindex, fromAddress=inputAddress,
|
|
toAddress=outputAddress, amount=str(balance),
|
|
blockchainReference=blockchainReference))
|
|
balance = balance - temp
|
|
session.commit()
|
|
session.close()
|
|
return 1
|
|
|
|
|
|
def startWorking(transaction_data, parsed_data):
|
|
|
|
# Do the necessary checks for the inputs and outputs
|
|
|
|
# Create inputlist and outputlist
|
|
inputlist = []
|
|
querylist = []
|
|
|
|
for obj in transaction_data["vin"]:
|
|
querylist.append([obj["txid"], obj["vout"]])
|
|
|
|
if len(querylist) > 1:
|
|
print("Program has detected more than one input address ")
|
|
print("This transaction will be discarded")
|
|
return
|
|
|
|
inputval = 0
|
|
inputadd = ''
|
|
|
|
for query in querylist:
|
|
string = "{} getrawtransaction {} 1".format(localapi, str(query[0]))
|
|
response = subprocess.check_output(string, shell=True)
|
|
content = json.loads(response.decode("utf-8"))
|
|
|
|
for objec in content["vout"]:
|
|
if objec["n"] == query[1]:
|
|
inputadd = objec["scriptPubKey"]["addresses"][0]
|
|
inputval = inputval + objec["value"]
|
|
|
|
inputlist = [[inputadd, inputval]]
|
|
|
|
if len(transaction_data["vout"]) > 2:
|
|
print("Program has detected more than one output address ")
|
|
print("This transaction will be discarded")
|
|
return
|
|
|
|
outputlist = []
|
|
for obj in transaction_data["vout"]:
|
|
if obj["scriptPubKey"]["type"] == "pubkeyhash":
|
|
if inputlist[0][0] == obj["scriptPubKey"]["addresses"][0]:
|
|
continue
|
|
|
|
outputlist.append([obj["scriptPubKey"]["addresses"][0], obj["value"]])
|
|
|
|
print("\n\nInput List")
|
|
print(inputlist)
|
|
print("\nOutput List")
|
|
print(outputlist)
|
|
|
|
|
|
# Do operations as per the type of transaction
|
|
if parsed_data['type'] == 'transfer':
|
|
print('Found a transaction of the type transfer')
|
|
|
|
if parsed_data['transferType'] == 'token':
|
|
|
|
returnval = transferToken(parsed_data['tokenIdentification'], parsed_data['tokenAmount'], inputlist[0][0], outputlist[0][0])
|
|
if returnval is None:
|
|
print("Something went wrong in the token transfer method")
|
|
|
|
elif parsed_data['transferType'] == 'smartContract':
|
|
|
|
# Check if the tokenAmount being transferred exists in the address & do the token transfer
|
|
returnval = transferToken(parsed_data['tokenIdentification'], parsed_data['tokenAmount'], inputlist[0][0], outputlist[0][0])
|
|
if returnval is not None:
|
|
# Store participant details in the smart contract's db
|
|
engine = create_engine('sqlite:///smartContracts/{}.db'.format(parsed_data['contractName']), echo=True)
|
|
Base.metadata.create_all(bind=engine)
|
|
session = sessionmaker(bind=engine)()
|
|
session.add(ContractParticipants(participantAddress=inputadd, tokenAmount=parsed_data['tokenAmount'],
|
|
contractCondition=parsed_data['contractCondition']))
|
|
session.commit()
|
|
session.close()
|
|
|
|
|
|
else:
|
|
print("Something went wrong in the smartcontract token transfer method")
|
|
|
|
|
|
elif parsed_data['type'] == 'tokenIncorporation':
|
|
if not os.path.isfile('./tokens/{}.db'.format(parsed_data['tokenIdentification'])):
|
|
engine = create_engine('sqlite:///tokens/{}.db'.format(parsed_data['tokenIdentification']), echo=True)
|
|
Base.metadata.create_all(bind=engine)
|
|
session = sessionmaker(bind=engine)()
|
|
session.add(TransactionTable(address=outputlist[0][0], parentid=0, transferBalance=parsed_data['tokenAmount']))
|
|
session.commit()
|
|
session.close()
|
|
else:
|
|
print('Transaction rejected as the token with same name has already been incorporated')
|
|
|
|
elif parsed_data['type'] == 'smartContractIncorporation':
|
|
if not os.path.isfile('./smartContracts/{}.db'.format(parsed_data['contractName'])):
|
|
if parsed_data['contractType'] == 'betting':
|
|
print("Smart contract is of the type betting")
|
|
if parsed_data['contractName'] is not None and parsed_data['contractAddress'] == outputlist[0][0]:
|
|
print("Hey I have passed the first test for smart contract")
|
|
engine = create_engine('sqlite:///smartContracts/{}.db'.format(parsed_data['contractName']), echo=True)
|
|
ContractBase.metadata.create_all(bind=engine)
|
|
session = sessionmaker(bind=engine)()
|
|
session.add(ContractStructure(attribute='contractType', index=0, value=parsed_data['contractType']))
|
|
session.add(ContractStructure(attribute='contractName', index=0, value=parsed_data['contractName']))
|
|
session.add(
|
|
ContractStructure(attribute='tokenIdentification', index=0, value=parsed_data['tokenIdentification']))
|
|
session.add(
|
|
ContractStructure(attribute='contractAddress', index=0, value=parsed_data['contractAddress']))
|
|
session.add(
|
|
ContractStructure(attribute='flodata', index=0,
|
|
value=parsed_data['flodata']))
|
|
session.add(
|
|
ContractStructure(attribute='userassetcommitment', index=0,
|
|
value=parsed_data['contractConditions']['userassetcommitment'].split(parsed_data['tokenIdentification'][:-1])[0]))
|
|
for key, value in parsed_data['contractConditions']['smartcontractpays'].items():
|
|
session.add(ContractStructure(attribute='exitconditions', index=key, value=value))
|
|
session.commit()
|
|
session.close()
|
|
|
|
# Store smart contract address in system's db, to be ignored during future transfers
|
|
engine = create_engine('sqlite:///system.db',
|
|
echo=True)
|
|
SystemBase.metadata.create_all(bind=engine)
|
|
session = sessionmaker(bind=engine)()
|
|
session.add(
|
|
ActiveContracts(contractName=parsed_data['contractName'], contractAddress=parsed_data['contractAddress']))
|
|
session.commit()
|
|
session.close()
|
|
else:
|
|
print('Transaction rejected as a smartcontract with same name has already been incorporated')
|
|
elif parsed_data['type'] == 'smartContractPays':
|
|
print('Found a transaction of the type smartContractPays')
|
|
|
|
# Check if input address is a committee address
|
|
if inputlist[0][0] in committeeAddressList:
|
|
|
|
# Check if the output address is an active Smart contract address
|
|
engine = create_engine('sqlite:///system.db', echo=True)
|
|
connection = engine.connect()
|
|
activeContracts = connection.execute('select * from activecontracts').fetchall()
|
|
connection.close()
|
|
|
|
# Change columns into rows - https://stackoverflow.com/questions/44360162/how-to-access-a-column-in-a-list-of-lists-in-python
|
|
activeContracts = list(zip(*activeContracts))
|
|
if outputlist[0][0] in activeContracts[2] and parsed_data['contractName'] in activeContracts[1]:
|
|
|
|
engine = create_engine('sqlite:///smartContracts/{}.db'.format(parsed_data['contractName']), echo=True)
|
|
connection = engine.connect()
|
|
contractWinners = connection.execute('select * from contractparticipants where contractCondition="{}"'.format(parsed_data['triggerCondition'])).fetchall()
|
|
tokenSum = connection.execute('select sum(tokenAmount) from contractparticipants').fetchall()[0][0]
|
|
tokenIdentification = connection.execute('select value from contractstructure where attribute="tokenIdentification"').fetchall()[0][0]
|
|
connection.close()
|
|
|
|
contractWinners = list(zip(*contractWinners))
|
|
for address in contractWinners[1]:
|
|
transferToken(tokenIdentification, tokenSum/len(contractWinners[1]), outputlist[0][0], address)
|
|
|
|
else:
|
|
print('This trigger doesn\'t apply to an active contract. It will be discarded')
|
|
|
|
else:
|
|
print('Input address is not part of the committee address list. This trigger is rejected')
|
|
|
|
|
|
|
|
# Read configuration
|
|
config = configparser.ConfigParser()
|
|
config.read('config.ini')
|
|
|
|
# Read command line arguments
|
|
parser = argparse.ArgumentParser(description='Script tracks RMT using FLO data on the FLO blockchain - https://flo.cash')
|
|
parser.add_argument('-r', '--reset', nargs='?', const=1, type=int, help='Purge existing db and rebuild it')
|
|
args = parser.parse_args()
|
|
|
|
# Assignment the flo-cli command
|
|
if config['DEFAULT']['NET'] == 'mainnet':
|
|
neturl = 'https://florincoin.info/'
|
|
localapi = config['DEFAULT']['FLO_CLI_PATH']
|
|
elif config['DEFAULT']['NET'] == 'testnet':
|
|
neturl = 'https://testnet.florincoin.info/'
|
|
localapi = '{} --testnet'.format(config['DEFAULT']['FLO_CLI_PATH'])
|
|
else:
|
|
print("NET parameter is wrong\nThe script will exit now ")
|
|
|
|
apppath = os.path.dirname(os.path.realpath(__file__))
|
|
dirpath = os.path.join(apppath, 'tokens')
|
|
if not os.path.isdir(dirpath):
|
|
os.mkdir(dirpath)
|
|
dirpath = os.path.join(apppath, 'smartContracts')
|
|
if not os.path.isdir(dirpath):
|
|
os.mkdir(dirpath)
|
|
|
|
# Delete database and smartcontract directory if reset is set to 1
|
|
if args.reset == 1:
|
|
apppath = os.path.dirname(os.path.realpath(__file__))
|
|
dirpath = os.path.join(apppath, 'tokens')
|
|
shutil.rmtree(dirpath)
|
|
os.mkdir(dirpath)
|
|
dirpath = os.path.join(apppath, 'smartContracts')
|
|
shutil.rmtree(dirpath)
|
|
os.mkdir(dirpath)
|
|
dirpath = os.path.join(apppath, 'system.db')
|
|
if os.path.exists(dirpath):
|
|
os.remove(dirpath)
|
|
|
|
|
|
# Read start block no
|
|
startblock = int(config['DEFAULT']['START_BLOCK'])
|
|
|
|
# Find current block height
|
|
string = "{} getblockcount".format(localapi)
|
|
response = subprocess.check_output(string, shell=True)
|
|
current_index = json.loads(response.decode("utf-8"))
|
|
print("current_block_height : " + str(current_index))
|
|
|
|
|
|
for blockindex in range( startblock, current_index ):
|
|
print(blockindex)
|
|
|
|
# Scan every block
|
|
string = "{} getblockhash {}".format(localapi, str(blockindex))
|
|
response = subprocess.check_output(string, shell=True)
|
|
blockhash = response.decode("utf-8")
|
|
|
|
string = "{} getblock {}".format(localapi, str(blockhash))
|
|
response = subprocess.check_output(string, shell=True)
|
|
blockinfo = json.loads(response.decode("utf-8"))
|
|
|
|
# Scan every transaction
|
|
for transaction in blockinfo["tx"]:
|
|
string = "{} getrawtransaction {} 1".format(localapi, str(transaction))
|
|
response = subprocess.check_output(string, shell=True)
|
|
transaction_data = json.loads(response.decode("utf-8"))
|
|
text = transaction_data["floData"]
|
|
|
|
if blockindex == 498385:
|
|
print('debug point')
|
|
|
|
parsed_data = parsing.parse_flodata(text)
|
|
if parsed_data['type'] != 'noise':
|
|
print(blockindex)
|
|
print(parsed_data['type'])
|
|
startWorking(transaction_data, parsed_data) |