Smart contract integration - Half Done

This commit is contained in:
Vivek Teega 2019-03-18 14:44:24 +05:30
parent 38a00c9f2e
commit 11a4aa09c6
11 changed files with 351 additions and 22 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
tree.db tree.db
.idea/ .idea/
flo_addresses.txt flo_addresses.txt
__pycache__/
*.swp

Binary file not shown.

Binary file not shown.

View File

@ -1,12 +1,6 @@
[DEFAULT] [DEFAULT]
NET = testnet NET = testnet
DB_NAME = tree.db
ROOT_ADDRESS = FHQ5rPqqMZT4r3oqpecM54E7tieQoJwZTK
INIT_TOKEN_NO = 21000000
FLO_CLI_PATH = /usr/local/bin/flo-cli FLO_CLI_PATH = /usr/local/bin/flo-cli
START_BLOCK = 461275 START_BLOCK = 489130
;if 'ranchimalltest#100' is on blockchain, set value to 'ranchimalltest'
PREFIX = ranchimall

Binary file not shown.

View File

@ -2,6 +2,7 @@ from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, Float, String, ForeignKey from sqlalchemy import Column, Integer, Float, String, ForeignKey
Base = declarative_base() Base = declarative_base()
ContractBase = declarative_base()
class Extra(Base): class Extra(Base):
__tablename__ = "extra" __tablename__ = "extra"
@ -43,4 +44,12 @@ class Webtable(Base):
transferDescription = Column('transferDescription', String) transferDescription = Column('transferDescription', String)
blockchainReference = Column('blockchainReference', String) blockchainReference = Column('blockchainReference', String)
class ContractStructure(ContractBase):
__tablename__ = "contractstructure"
id = Column('id', Integer, primary_key=True)
attribute = Column('attribute', String)
index = Column('index', Integer)
value = Column('value', String)

View File

@ -5,6 +5,7 @@ operation = None
address = None address = None
amount = None amount = None
def isTransfer(text): def isTransfer(text):
wordlist = ['transfer','send','give'] #keep everything lowercase wordlist = ['transfer','send','give'] #keep everything lowercase
textList = text.split(' ') textList = text.split(' ')
@ -22,6 +23,31 @@ def isIncorp(text):
return True return True
return False return False
def isSmartContract(text):
textList = text.split(' ')
for word in textList:
if word[-1] == '@':
return word
return False
def isSmartContractPay(text):
wordlist = text.split(' ')
if len(wordlist) != 2:
return False
smartContractTrigger = re.findall(r"smartContractTrigger:'.*'", text)[0].split('smartContractTrigger:')[1]
smartContractTrigger = smartContractTrigger[1:-1]
smartContractName = re.findall(r"smartContractName:.*@", text)[0].split('smartContractName:')[1]
smartContractName = smartContractName[:-1]
if smartContractTrigger and smartContractName:
contractconditions = { 'smartContractTrigger':smartContractTrigger, 'smartContractName':smartContractName }
return contractconditions
else:
return False
def extractOperation(text): def extractOperation(text):
operationList = ['send', 'transfer', 'give'] # keep everything lowercase operationList = ['send', 'transfer', 'give'] # keep everything lowercase
count = 0 count = 0
@ -35,6 +61,7 @@ def extractOperation(text):
return returnval return returnval
# todo pass marker to the function and support all types
def extractAmount(text): def extractAmount(text):
count = 0 count = 0
returnval = None returnval = None
@ -61,6 +88,7 @@ def extractMarker(text):
return word return word
return False return False
def extractInitTokens(text): def extractInitTokens(text):
base_units = {'thousand':10**3 , 'million':10**6 ,'billion':10**9, 'trillion':10**12} base_units = {'thousand':10**3 , 'million':10**6 ,'billion':10**9, 'trillion':10**12}
textList = text.split(' ') textList = text.split(' ')
@ -74,27 +102,106 @@ def extractInitTokens(text):
continue continue
def extractAddress(text):
textList = text.split(' ')
for word in textList:
if word[-1] == '$':
return word
return False
def extractContractType(text):
operationList = ['betting*'] # keep everything lowercase
count = 0
returnval = None
for operation in operationList:
count = count + text.count(operation)
if count > 1:
return 'Too many'
if count == 1 and (returnval is None):
returnval = operation
return returnval
def extractContractCondition(text):
return text.split("contractcondition:")[1][1:-1]
def extractContractConditions(text, contracttype, marker):
rulestext = re.split('contractconditions:\s*', text)[-1]
rulelist = re.split('\d\.\s*', rulestext)
if contracttype == 'betting*':
extractedRules = {}
for rule in rulelist:
if rule=='':
continue
elif rule[:19]=='userassetcommitment':
pattern = re.compile('[^userassetcommitment="].*[^"]')
searchResult = pattern.search(rule).group(0)
extractedRules['userassetcommitment'] = searchResult.split(marker)[0]
elif rule[:17]=='smartcontractpays':
conditions = rule.split('smartcontractpays=')[1]
if conditions[0]=='"' or conditions[0]=="'" or conditions[-1]=='"' or conditions[-1]=="'":
conditionlist = conditions[1:-1].split('|')
extractedRules['smartcontractpays'] = {}
for idx, condition in enumerate(conditionlist):
extractedRules['smartcontractpays'][idx] = condition.strip()
else:
print("something is wrong with smartcontractpays conditions")
return extractedRules
return False
# Combine test # Combine test
def parse_flodata(string): def parse_flodata(string):
if string[0:5] == 'text:': if string[0:5] == 'text:':
string = string.split('text:')[1] string = string.split('text:')[1]
cleanstring = re.sub(' +', ' ', string) nospacestring = re.sub(' +', ' ', string)
cleanstring = cleanstring.lower() cleanstring = nospacestring.lower()
if isTransfer(cleanstring): if isTransfer(cleanstring):
if isSmartContract(cleanstring):
contractname = isSmartContract(cleanstring)
marker = extractMarker(cleanstring)
operation = extractOperation(cleanstring)
amount = extractAmount(cleanstring)
contractcondition = extractContractCondition(cleanstring)
parsed_data = { 'type': 'transfer', 'transferType': 'smartContract', 'flodata': string, 'tokenIdentification': marker[:-1],
'operation': operation, 'tokenAmount': amount, 'contractName': contractname, 'contractCondition': contractcondition}
else:
marker = extractMarker(cleanstring)
operation = extractOperation(cleanstring)
amount = extractAmount(cleanstring)
address = extractAddress(nospacestring)
parsed_data = {'type': 'transfer', 'transferType': 'token', 'flodata': string, 'tokenIdentification': marker[:-1], 'operation': operation,
'amount': amount, 'address': address}
elif isSmartContractPay(nospacestring):
contractConditions = isSmartContractPay(nospacestring)
parsed_data = {'type': 'smartContractPays', 'flodata': string, 'smartContractTrigger':contractConditions['smartContractTrigger'], 'smartContractName':contractConditions['smartContractName']}
elif isSmartContract(cleanstring):
contractname = isSmartContract(cleanstring)
marker = extractMarker(cleanstring) marker = extractMarker(cleanstring)
operation = extractOperation(cleanstring) contracttype = extractContractType(cleanstring)
amount = extractAmount(cleanstring) contractaddress = extractAddress(nospacestring)
parsed_data = {'type': 'transfer', 'flodata': string, 'marker': marker, 'operation': operation, contractconditions = extractContractConditions(cleanstring, 'betting*', marker)
'amount': amount} parsed_data = {'type': 'smartContractIncorporation', 'contractType': contracttype[:-1], 'tokenIdentification': marker[:-1], 'contractName': contractname[:-1], 'contractAddress':contractaddress[:-1], 'flodata': string, 'contractConditions': contractconditions}
elif isIncorp(cleanstring): elif isIncorp(cleanstring):
incMarker = extractMarker(cleanstring) incMarker = extractMarker(cleanstring)
initTokens = extractInitTokens(cleanstring) initTokens = extractInitTokens(cleanstring)
parsed_data = {'type': 'incorporation', 'flodata': string, 'marker': incMarker, 'initTokens': initTokens} parsed_data = {'type': 'tokenIncorporation', 'flodata': string, 'tokenIdentification': incMarker[:-1], 'tokenAmount': initTokens}
else: else:
parsed_data = {'type': 'noise'} parsed_data = {'type': 'noise'}
return parsed_data
return parsed_data result = parse_flodata('create electionbetting@ at address oM4pCYsbT5xg7bqLNCTXmoADUs6zBwLfXi$ of type betting* using the token rmt# with contractconditions: 1. userAssetCommitment="1rmt" 2. smartContractPays="NAMO=WIN | NAMO=LOOSE 3. expirydate=1553040000"')
print(result)

Binary file not shown.

View File

@ -8,7 +8,7 @@ import sys
import parsing import parsing
from sqlalchemy.orm import sessionmaker, relationship from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy import create_engine, func, desc from sqlalchemy import create_engine, func, desc
from models import Extra, TransactionHistory, TransactionTable, TransferLogs, Webtable, Base from models import Extra, TransactionHistory, TransactionTable, TransferLogs, Webtable, Base, ContractStructure
def startWorking(transaction_data, parsed_data): def startWorking(transaction_data, parsed_data):
@ -74,9 +74,27 @@ def startWorking(transaction_data, parsed_data):
print("This transaction will be discarded") print("This transaction will be discarded")
continue continue
# Check if the transaction is of the type 'incorporation' # Check if the transaction is of the type 'incorporation' or 'smartcontract'
if parsed_data['type'] == 'incorporation': if parsed_data['type'] == 'smartcontract':
print("I JUST FOUND THE INCORPORATION OF SMART CONTRACT \n" + str(parsed_data))
if parsed_data['contracttype'] == 'betting*':
print("Smart contract is of the type betting")
if parsed_data['contractname'] is not None and parsed_data['contractaddress'][:-1] == outputlist[0][0]:
print("Hey I have passed the first test for smart contract")
#check if a db with contractname exists
scengine = create_engine('sqlite:///smartContracts/{}.db'.format(parsed_data['contractname'][:-1]), echo=True)
Base.metadata.create_all(bind=scengine)
scsession = sessionmaker(bind=scengine)()
scsession.add(ContractStructure(attribute='contracttype', index=0, value=parsed_data['contracttype'][:-1]))
scsession.add(ContractStructure(attribute='contractname', index=0, value=parsed_data['contractname'][:-1]))
scsession.add(
ContractStructure(attribute='marker', index=0, value=parsed_data['marker'][:-1]))
scsession.commit()
scsession.close()
sys.exit(0)
elif parsed_data['type'] == 'incorporation':
session.add(TransactionTable(address=outputlist[0][0], parentid=0, transferBalance=parsed_data['initTokens'])) session.add(TransactionTable(address=outputlist[0][0], parentid=0, transferBalance=parsed_data['initTokens']))
session.commit() session.commit()
#transferDescription = "Root address = " + str(root_address) + " has been initialized with " + str(root_init_value) + " tokens" #transferDescription = "Root address = " + str(root_address) + " has been initialized with " + str(root_init_value) + " tokens"
@ -93,8 +111,7 @@ def startWorking(transaction_data, parsed_data):
print("The input address dosen't exist in our database ") print("The input address dosen't exist in our database ")
elif availableTokens < commentTransferAmount: elif availableTokens < commentTransferAmount:
print( print("\nThe transfer amount passed in the comments is more than the user owns\nThis transaction will be discarded\n")
"\nThe transfer amount passed in the comments is more than the user owns\nThis transaction will be discarded\n")
continue continue
elif availableTokens >= commentTransferAmount: elif availableTokens >= commentTransferAmount:
@ -216,6 +233,8 @@ def startWorking(transaction_data, parsed_data):
session.commit() session.commit()
session.close() session.close()
# Read configuration # Read configuration
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read('config.ini') config.read('config.ini')
@ -255,6 +274,7 @@ response = subprocess.check_output(string, shell=True)
current_index = json.loads(response.decode("utf-8")) current_index = json.loads(response.decode("utf-8"))
print("current_block_height : " + str(current_index)) print("current_block_height : " + str(current_index))
for blockindex in range( startblock, current_index ): for blockindex in range( startblock, current_index ):
print(blockindex) print(blockindex)
@ -279,5 +299,4 @@ for blockindex in range( startblock, current_index ):
if parsed_data['type'] != 'noise': if parsed_data['type'] != 'noise':
print(blockindex) print(blockindex)
print(parsed_data['type']) print(parsed_data['type'])
startWorking(transaction_data, parsed_data) startWorking(transaction_data, parsed_data)

View File

@ -0,0 +1,198 @@
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
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'] == 'smartContract':
print('do something')
elif parsed_data['transferType'] == 'token':
engine = create_engine('sqlite:///tokens/{}.db'.format(parsed_data['tokenIdentification']), echo=True)
Base.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
availableTokens = session.query(func.sum(TransactionTable.transferBalance)).filter_by(address=inputlist[0][0]).all()[0][0]
commentTransferAmount = parsed_data['amount']
if availableTokens is None:
print("The input address dosen't exist in our database ")
elif availableTokens < commentTransferAmount:
print(
"\nThe transfer amount passed in the comments is more than the user owns\nThis transaction will be discarded\n")
return
elif availableTokens >= commentTransferAmount:
print("well i've reached here")
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'][:-1]))
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()
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')
# 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 ")
# 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)
# 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"]
parsed_data = parsing.parse_flodata(text)
if parsed_data['type'] != 'noise':
print(blockindex)
print(parsed_data['type'])
startWorking(transaction_data, parsed_data)