flo-token-tracking/tracktokens-smartcontracts.py
2021-06-26 13:46:40 +00:00

3262 lines
191 KiB
Python
Executable File

import argparse
import configparser
import json
import logging
import os
import shutil
import sqlite3
import sys
import pybtc
import requests
import socketio
from sqlalchemy import create_engine, func
from sqlalchemy.orm import sessionmaker
import time
import parsing
from config import *
from datetime import datetime
import pdb
from models import SystemData, ActiveTable, ConsumedTable, TransferLogs, TransactionHistory, RejectedTransactionHistory, \
Base, ContractStructure, ContractBase, ContractParticipants, SystemBase, ActiveContracts, ContractAddressMapping, \
LatestCacheBase, ContractTransactionHistory, RejectedContractTransactionHistory, TokenContractAssociation
goodblockset = {3387923,
3387978,
3396172,
3396192,
3396199,
3396201,
3396205,
3396206,
3396208,
3429342,
3443480,
3443669,
3443675,
3443677,
3443679,
3443680,
3443682,
3443684,
3443702,
3443718,
3443723,
3443733,
3443764,
3443895,
3444345,
3444499,
3444502,
3444503,
3444505,
3444513,
3444522,
3444595,
3444691,
3444698,
3444732,
3444756,
3444828,
3445284,
3445287,
3445463,
3445501,
3446215,
3446465,
3446877,
3446884,
3446888,
3446943,
3446980,
3447073,
3447257,
3447278,
3454503,
3503146,
3609700,
3935975,
3937128,
3937241,
3937242,
3937338,
3937525,
3939941,
3939999,
3940023,
3941146,
3941147,
3956547,
3961676,
3969778,
3970781,
3981168,
3990334,
4000081,
4000252,
4000341,
4000359,
4000456,
4001475,
4002492,
4008981,
4008986,
4008999,
4009033,
4009096,
4010850,
4010910,
4010919,
4010957,
4010980,
4030789,
4030795,
4034239,
4173382,
4174529,
4176306,
4176341,
4176352,
4176383,
4177433,
4177436,
4177705,
4179318,
4184367,
4184544,
4184706,
4186183,
4190429,
4191992,
4203044,
4204961,
4206876,
4210582,
4212515,
4212538,
4212587,
4212634,
4212648,
4213909,
4213921,
4213939,
4213967,
4221370,
4235291,
4243310,
4243333,
4248901,
4256675,
4256730,
4280238,
4281352,
4291219,
4293808,
4313846,
4313887,
4315165,
4315166,
4315223,
4315314,
4315388,
4315411,
4316891,
4316964,
4317202,
4317879,
4317887,
4317962,
4318675,
4318747,
4318796,
4318799,
4318811,
4318848,
4318881,
4318885,
4318889,
4318908,
4318927,
4318928,
4318942,
4318979,
4318987,
4318992,
4319012,
4319028,
4319426,
4319563,
4320551,
4320553,
4320554,
4320569,
4320575,
4320583,
4321197,
4321248,
4321289,
4322379,
4322494,
4323991,
4324082,
4324099,
4324108,
4324111,
4324160,
4324282,
4324285,
4324286,
4324438,
4324446,
4324450,
4324451,
4324456,
4324459,
4324537,
4324562,
4325899,
4326104,
4326179,
4328292,
4328296,
4328309,
4328402,
4338538,
4338608,
4338622,
4340833,
4341514,
4353757,
4353777,
4353779,
4353808,
4353825,
4355736,
4355799,
4355819,
4356783,
4356788,
4356793,
4356901,
4356933,
4356952,
4356970,
4357082,
4357970,
4358021,
4358201,
4358221,
4358222,
4358307,
4358308,
4358324,
4358344,
4358355,
4358379,
4358391,
4360056,
4360211,
4360576,
4360906,
4360955,
4361030,
4362187,
4362192,
4362213,
4362728,
4363547,
4363668,
4364856,
4364858,
4364868,
4364875,
4364876,
4365014,
4365152,
4365327,
4366550,
4366621,
4366675,
4366700,
4366747,
4368196,
4369662,
4369683,
4369756,
4376861,
4377318,
4378253,
4378295,
4378297,
4384839,
4385456,
4385457,
4385498,
4386139,
4389212,
4390807,
4410901,
4410906,
4410912,
4410940,
4411239,
4411273,
4411288,
4411315,
4411330,
4411339,
4411346,
4411348,
4411350,
4411353,
4411355,
4411661,
4411683,
4411740,
4411742,
4411743,
4411745,
4411746,
4411757,
4413288,
4413289,
4413291,
4413418,
4413420,
4413430,
4413457,
4413465,
4413470,
4413474,
4418348,
4420905,
4420912,
4420920,
4420940,
4420952,
4420958,
4421020,
4421091,
4421093,
4421100,
4421147,
4431335,
4433179,
4433620,
4433637,
4434828,
4434873,
4434888,
4434891,
4434898,
4434903,
4437014,
4437066,
4447198,
4447202,
4447204,
4447261,
4447314,
4448107,
4449169,
4449323,
4449488,
4473417,
4473420,
4473503,
4473517,
4473583,
4473804,
4484210,
4484247,
4508994,
4509004,
4509300,
4509950,
4528359,
4528622,
4541278,
4541295,
4552508,
4552522,
4561033,
4562937,
4576480,
4576482,
4577665,
4577712,
4597585,
4597674,
4598087,
4598090,
4599578,
4599724,
4599771,
4606282,
4608453,
4608750,
4612976,
4621941,
4621968,
4629357,
4629457,
4643323,
4643325,
4643544,
4643635,
4658051,
4658053,
4658163,
4658174,
4658778,
4661954,
4664069,
4664075,
4665838,
4665898,
4674594,
4674633,
4684574,
4684602,
4695346,
4695451,
4695529,
4695552,
3961676,
4323991,
4324562,
4361030,
4362187,
4362192,
4364858,
4364868,
4364875,
4364876}
goodtxset = {'a74a03ec1e77fa50e0b586b1e9745225ad4f78ce96ca59d6ac025f8057dd095c',
'fdcb3ff273deb2a03c19333d4f4f3a22b1fedd22e17fef576559319c40450f50',
'8425c6d4171f8b1634fa1aa339b59e18ae3a585ed3387749cd2fa30d81e0f95e',
'cde8728abca696af417d2f69dda6f55defdf1f5a4bd4625df768ca863d620fa7',
'9ce989dec4ad8a39ca7461812232514c59a3618c38eb6a590ab1e693b17baf7d',
'8c3559b0ba78d1143d3ab4e30c3611842fc57988751be67d761e5c087a8d07c5',
'3803668250105fca8a90e691e07916ece655611afc07b2c6a01b028f35f59352',
'5c180a2961189dd40d5af35a17fbed7865443f6c4ef263924630d3c815606ee9',
'22330e8b2c98ef0d3dc1b0d7961aaad9b2a7bd5afbb4c71d6d4bf20c02595cce',
'c6eb7adc731a60b2ffa0c48d0d72d33b2ec3a33e666156e729a63b25f6c5cd56',
'7c84e9ac1a3d9a81868ca801966c75c4ab261bbc2d0e07050c0f25275cf8a5e1',
'd8436c30c3493dcf723672d337093bbe9466afa88235051c7981c39fa30c1326',
'b463b2b447433b7044bdd3022e271db02b30a3307f25e9c13e18eab1bd681d93',
'cf6de70c9e9e3d45d37bc6e4112851655d82dbd5aef48aedcee466fe4799b950',
'26f08763cd177e2d55080041637527a7769eb3507b023a25bc9edbe8649c2fe2',
'3faa8479657839e6ff9869b49fe5b20441e45e780403c3ab7e33eb70787a785d',
'511f16a69c5f62ad1cce70a2f9bfba133589e3ddc560d406c4fbf3920eae8469',
'e92bf6a8bddf177a5e2d793fa86c7ad059c89157f683f90f02b3590c0e4282c5',
'21976fe4a9a99eb31bd18a496328f7cb91bd4f708bb848771fd638949a5eaba3',
'd8a8b87bd2dd98ac3e5a6996f7bbb30612ecf13756c0f860968c2cb7c8cf01e5',
'dd050ac2ec951eaad4ee897c8348b5a9314a6e7d13debbc319757ec121c5a013',
'dfbc183b45762d7db565eeb0eddce74232acbc8a2c00a68a6ef628c16bb95a93',
'bb6cef5e9612363ed263291e8d3b39533661b3ba1b3ce8c2e9500158124266b8',
'edcd710fa6176cee9c738188a053967ffb4c5dca1a1aa721bee7e22b7f3b7e11',
'dd5512594a0402b914e8d852c918a3b0017e888deea1324512c62f62420a27e0',
'78f6e1dbb2eb8de746395d5c35b47ab91f51076bf3a134afa5fe5b0a44eba5e0',
'd07676c59c90b2363db301af4158d7f384dc0d6e84bb0619639ac358e6f311f1',
'38db5c9c78e6b6c97e622cc1576ebf3b824280f728c157beb56b974d5d802fa7',
'259974b79c94bba0481ad5e3b2f468380df9ac31da7dfdd0da8c3af78bcea561',
'4258e8a7530d4d1e3f1d7524cc927d0c9c335a779a032bbb2d83725680a47ee9',
'21b0b9564ae62d32eed3c9911686c20dea24e1a94c255d7b3de4ecaa720e3d17',
'5e67fee05ed5f6598a85e8fb12e207183bc4441c8f81d54b89ce6bfd196c5fdf',
'eec02efef5a505e141f5551604d401a872e33837edfcbb7e5895a9951c50aa2a',
'97b051261a91dfb4b9d591c44d9ab6192efc2f82d85777f5bf33861952d30cb4',
'3e52f39f785191b693d99859a9823eed281c2ff85669803ef85b55ea924bf790',
'3b8c7d37e16a2f7446411feebe9d141fda6edd4886998033bb3405a08c6026a7',
'0727f232cee6f4c1e10f7b281c8c5c7bb2e90c2c0fc50c8b1160f383840a8ae5',
'0623bea90559e1bb59f4134f6164544ef305fbeaa792e51f09e03706cfe35b79',
'67783a84997fd6b6099267afd158494a971c3aea6ff6856583d778ff5bcc2a0c',
'441c222e9953c6e4278e38362c691835a3c7b6f09f036bf6918b619d0e16cd3c',
'3c0c391d1ec5db8c6636996b6479c25a4a939fb7e9f14d899d41431c5d0a6aad',
'd5e61452a5fc98cac6a0c2c8eb5dc45c345513f2bb27240d66ef3d47a11c46bc',
'3a53a7b61f0ff1c09a25f2ffcb3a97ae0ec0e3b0eccb5e82fec4966dbc0eddcf',
'a433e8f2e7d332cd9574d7325b33720c75f459870d5e060b506a7c7c17650b37',
'2cbc28088cfb983b9b8fd7ec7ee5abcac5cdef07e706413d4094c2caa7020753',
'7edcc427a565a458ae81dcc9a9717d2592120bedd632a17d4ca3fa863e08ca81',
'27ee5479706aba9d37eb458e1a51657b418dc1c9574ed37af763d7d7d7178937',
'f38996915cd00f7bb6ab33ca522168739e4af41ac718ec77b9e7974687f48ec6',
'4db4f56b711720b1769a70d834ef3138b599143d0dcf82c1d3e22e44201cc1b4',
'ad50d8bffe7214473b6f8f454f55749d23a26ab3fc854d63e1ccc808045d98ba',
'5a36fce4646358c751b5403ec5c7465f1b11c8dca6d86e8a9cd4e26184e07b1a',
'3520cda2de65249f7629683c4b8f3efb15479076fe9521250aba2f1abe3f2ce9',
'b57cf412c8cb16e473d04bae44214705c64d2c25146be22695bf1ac36e166ee0',
'ebc99f41c4ccaea32bd9dbc251515838ba47660b7422c7cae39ee3e2ae0107fe',
'e6338071b4247003e16aae50e2696d42f42df116b928122bd94c3c718fbd70bc',
'cf54e0e1b12065ad5ac307400e19b9097f2759f4ef003753e0938eb6c1c36f00',
'ebc015c8c57acdca1020863537eaa25cd00c9ff71c4dc3975b3d4104aa456488',
'b2b4e8bb8cb463e0295f9b639c29aae443eb8e7911551220e7dd45648a4e17c6',
'0e6ec23a1755217b64a6bae24ac14a326a427bcea88c9e4a25a6a89ab6b5e1f6',
'55f144c92556075e6980de0febf5ab41ebf34af4b15eb60fa115384d2507c460',
'af9ca8eb285437a637ddc03cb25e06f15ac650b9f4b8529205380410b11697b1',
'a59a7a3f24d374679747256790908e9eec841a2220e51e479f545122e2181fdb',
'16a3a51868c0e4461958fbd725dca6c2c8d8e9ffedc52a948fea21f4e6ecbf55',
'dec6a4cf1d2ecfd1b36eae0139b8e4fe6307a44af1100f805511860d43508e48',
'f4bea61cc8831a51945fdd240ed3bc198ef3205f4dd409d13d3a36f050a808a0',
'44e17058bb67046e391027d0695cd2e494242406d0b7cec0748645ecaeb97ec1',
'cb36f02e2eb97984a5de6a22123e989ba08dcb13d8c38b101f743dc3200d7490',
'322f60f380573876b5391e77b1296640d97de56d6efcb21f83d6d010e0820165',
'ba8e3784503f5b728764ca7853fb3f07efdd68a86152b126ce8a9ccc0a85f30c',
'ac6e826fb63a62b17f4603202c2ba43cd20aa9e362ed5f7419b71844ecaadc65',
'1e6de7e90286a211335e3fbcab1022c3561d57ffcfe4036a556fa4c58b032ec0',
'cd4539baedd99a5aaa743a2ed2babc55d2d0042cb95d71761f54e918571a57ee',
'6635af4bf7b82f280a57b9696227e79280b17442ad01a2e0902b513f1ef7c017',
'd219431684852035ac19137bda6a1904461d11fc8d3a66aff3fa21139f4ffde0',
'7e4b1e283602645584b24321417dad922bb8f65b3b983c924a047e22b9ef1c8a',
'55acaa10d9cdb37636b2b5f97054fc6a10ceb1cce310009b2f9ff5e8bb274e84',
'13e8242ee6c8c90a57e902051ce38f3c87376a6672b5be132d931363ecc4ca75',
'3a9118d83177f6e9fb8d46ee97bf1e1390cac2ba24ce1e3982086c910053ade0',
'e6f3effa2b1d885e1bacec3e7ff8256c4b55b4e0bda0514508ba4fc44ce13250',
'074acad64d28f63746616ffe39dd9e1b03c56b80809bf35b02b545f9907564ae',
'01f560d06ead33b2dabba9b9fdb44ab9ff693e63c0033bb422c991a4ccaa3a9a',
'46c5734b45d45276b69364ffac6cceb789ea667853760750be1875847d226f37',
'a1f6f8834ec597b23041c44761c93af3f595941dd23eac1357f80c886c601a8f',
'4476e804db749a21d5d5ac809a487b4f31b9f4f561531879e4b507abafa18953',
'65c1a4cb0cfc2fe51107abcb77277f1ab0e16f985c78369599d98f684fb45144',
'3f71fd1649ee36cf66ffcab38bc59dc18aab2995d37ae1e3dcafe9f63598c4bb',
'61bf9bfb99417054862fcd5da7cfc8c68dcc477272ecd3e821fa07bc7d2e7a70',
'535c996c16aedb8df875dcf48e38242e79b22784a9297d663bf271644a747951',
'1ab70a9de6a8deeb0ba8f6c4ca015d60659c90953a7c8a1f3215276fe57b1fb2',
'b93810c8ab06676a42907e2b116cf8799e08c4c0f0829d77b1cc9284f308a9f0',
'612ad2b43229a5b02714ac86d8d119269e291872885312269b4d9f98c0bd4ed5',
'c1ab82c2a996103dcdb9895e453b4a8d991a14ffed79505c752ae02ae657076b',
'9bf4006ec5f4f0af4512318dd90cbd24f3af45a6280a444164a67d5af155d85a',
'89e3dc0c351d5554caa013515be89865c128a5424fea363cc6d43b53981a4290',
'911d634423646a12c7a0825b3d2dbabd6a34231835318e546142295300705254',
'fbae58129af3c09590fbf1781d7b8d331e521f8bf8e51cf22d91fecf34d68f79',
'3a2b547fb501a376c0d04fd883c0e0fc8f226413ac175ec9e345eac7611a16fd',
'db5c739bcdb88f6ba8532e66ca344dd517c527a3a38793529b53ae133c917433',
'536bb47cf8cc0499588d25ff4185e92c4edd77610a6aeb92cf551c2f74607af0',
'5783bf578fcf8cb7b193d82d961ac2b82cf3767e578f67326c96373a4feab8f7',
'd0b4e46302d55e5e08369e595df24f58726eedde6004dbd99d04f740b9e41132',
'7a51b3e2301ae30402770284c008a5905e56e534efe6dec9dee93aeb9381905f',
'db4407a6f08f08e8fe375cce5aa72c1ef1dc03522b7bf5dd625230ab18485675',
'951e7e2bf6d42f690acbcb7a0cef8a3c5f58994148c0461bcd8daf417ea8bfda',
'22e7156182f9dd6695a1f23108da144534ac783b42188c84913d99df0c0cfee3',
'ab67fb7852361fcc05e7d5550fe7a30bd1211718dbca5a66ab26892a7650c4e0',
'57caac52230ad49aa6fbb83e6d2fc84f48909ee65197034f6a8064d397066ae9',
'47c41cc4de57abb4e9fa7b1347d14bd62bc7401bfd9dce6caf11ce4b73fd94ec',
'3addd5b84ea121bbf6b4044452a806bd94adfbf35d591932229846509fe616eb',
'466e7827eb7034bdf6a1ebba4c1b385e2faa1dc21e418e66b535dcf5a73d88c7',
'4a2640abbd6921cb7ef8fdc8a6cadd27da44539aa10c5e6ad364432824bea419',
'1fd1e25e703124d294c69c67852cc1fb2017e51f37aa4b733d423badfcf90a52',
'e3ad816a90a4320d74d52324c5c657041ef29219fd7be971f51917e59ececbf8',
'641d218c815f689a461565789920d7b86ecabd1a324611f039450be91b632a4b',
'9818246afc1b90b717bb202259ba9595a82f999e3310f4369c0c50b06c9de15f',
'b8628b077b4c472e4a6ab18a7f99f7006317e7abe909f8dbe43312780cd8b0a0',
'3788c20cc073baff9b9505380d7522f2dfdbedabd31fa1326347be49adbbdcb4',
'e65c8cca158865575529159c023fdd83001fd7492e0a854191f8cbf93475c494',
'd7e160ef8119f91070f3e95426911c927b87a03cf5374d3bd75ceecebd60073b',
'6db604b62b1be5fdfcd3fb51611f7042666060c65a5217deac5c91a9b5b10453',
'e7b24ae11da6590c9d296eaeeb26c080cc97b20c73793ec205b91ed43f3816cc',
'458b8494dccf3cb91aaa49131537b5008f699004fa3ac4157f9e48420bdd9662',
'bacaea9cb7e3bb62d9e54506e038c9daf7850a7342df047858fe1ff48597a3a9',
'41d5913ca22c3ced4c08c6f8637b9e067f2d7758d0d0275dc704e8f8b64b9f7c',
'3be4222924e4ff05a6bc121e069ed51b335ed2f6cb95402593c8a27417c505f0',
'72ecc2b914745cdb0d6421b1422dfbdba96c0256e56d6d4c1d84ece56d0e3e18',
'3620e5beb80ea8e4aded4796072399a8f17fb782e5736c8d5577493ec3794702',
'2ca7477d0425c676d4e28e5b4e1c71f4b12a16ebd840d0595c3937f23c3d70b0',
'12215cf6c84e1137cfca91c2c1db822eb1caacda9befd008c2adb556cadff0f6',
'40fd93136637c31f9772c3f846cb857988fa2ca59a77282ff1c54dc754f95051',
'3d011143188a0a931bfebe76e77c11272bfb543f71286690072423bb376ee872',
'50ee03d685ad9bc06d07b049d041ca523afd46439c631b08cd684bb8b2fd5929',
'5c4ea2a61857a1179e861274ae292ad22d778eba1eb04d5b02b8901461480baf',
'86f157bb40c266caa5b7b6212fab35f32d6d059251e47e799405457bb00720b9',
'cd27fbfec5083a79986aec7d772ba9695fd6c23263c2252a6eb84dafc7fb9ced',
'e51d497ae3961ea7bca697f7347fd7156c17310be5cb9b956e6fcc7438755f57',
'392fdd08ebaed00373cd6496cde779f976a7928f02cd9cfa7aaf96bea619f6e4',
'd3a8cf88f949bb068c9ae6118afb0373a2929713a165ffcd7113be4ec9004945',
'7127bffb2681dadc8090087261676135f2a063923d19f4683816bf3a8061dbee',
'7669a68489ced1aed50ba44d8be50c6c2513481ae27a464d6c29d0df66029b4b',
'1a9077b6b2ad7c13442b0e5751b26b5ad50e64c37eed2d782d10c72b260cb46d',
'59e75e41cd2966a5ce39b8a7bbb1e89ccee203fffaade7b440c216bdb692895e',
'93638c3080f030a1c169680f542fde5c5fbee9f119a2bb78ae6f28fc4616be48',
'0080c6a7155016d55f9717851aa2ad5a9571d3b03fa3ed872095190ca7ab4136',
'260ebd1d7f40c9126466c728d8504c1fb9492bb59f9186adc91c5ac16ca5daf9',
'f4a9131c8af63f8e59c96745019d621d04c24beaeec018a49051890f869e1588',
'8fb08532dfe79c7fa5537ef95a0d1c9c806c134707a05453e62c92c59c45cb0b',
'4cf0bbfe84e73bb22ccbe802a5f90301dbb24573ce707061db0e4c995551fad9',
'5b1b2127115f71e2a10d32da321b5b76b9f6ad0cd6ca1c1dbff454a6e783e826',
'13822b25f5e92870c9fb5a6ad09ebbe0ba3ba2c909a381fc41fcab25e3d2be7f',
'4a26e4bafcd460e38ce082c44ec49725dfe26e5362dd67fc060ff21e273f549d',
'e6b7422316f7ff5d7538a3e48d5860edfabac2d475365addda2f822c45ba56ec',
'7ed1d7e63b852626dd29be2553e9c88566734282220d67703a329660776a1a3a',
'8309340f71736574268497665ab8f31948478988d2f7a9c26014c0ba7fd521fb',
'061c7f252198e284d5993fc1798050f56f27c6b46aaf90fac4df0df326b28e4c',
'ce2685d884fe5c885fb0279e7745dacee38348c5bb8cc629c72bba589943dc3e',
'9d2a9d6f3fff5645eda3e1cdf8df56411d4a92ddf7f6563d745cbd36ac79405c',
'622e602433049439913c9507bc26f45830055f5119b8926e63af6d7f81ce5138',
'60d072af2202693a2467a097b2bc74383508759c36a6fbb94e857538a9e618f7',
'2483f05eade500e24d34f7dc5f1443dca078c2c8c04fd6dd0b87da7c5f19a3e2',
'bec8026fd4a3d606970f37a3c049b9610613637c35eeee15854b8744867b0735',
'092b2ee21b233f81113aa0a0af38879b90b16021ca215b4ec6aa53b070305b49',
'600db50af61adc64812e9866cf789b67535bfa48fba3a82aea023dbb5a6df170',
'78ba189416344f22c50ad5006f5e612115bc903efc97757889fd8d706bb81f5e',
'2dcbb87dae48f4cef02342ac1caa6fe9e293e9ef8b0e109a640430dee8a474ea',
'dd8813ca9b05201b7463a1cf337a177749542eba1cbaa8fb8f7a02185dbf8932',
'ebab14d8926ff9b00a5a5d8f16747fb1008fabf0a91f202adf6d379a77d0318c',
'9219e49b8a402a185dc96bbde1d6e5988b34dca4c7da28c028cbdf0caacb3755',
'2efe394a7af45d0d631b855a44a90a37d0fbf457301e3f02ba70f7dfb069c3e9',
'8992e7200bb05a89f6b6141d82b69d9bf74b30bcc709d06c33b475902ea59e99',
'93779565a4462e6b912deaee052f26b092b11f669dcbabfb6de1b43cedbc1946',
'492d0595a3048bd9742d632e2a2643e22a3e0dff16985ab678a64d5403beb1ee',
'45a24ca98a644b889f65b31e27316bbdf5aeee3d4408ea8ec247235ed044e0a6',
'296002fedfb70ca513bedbd5fe0e72d5ceeca5c9ac633ad1d98879887146a5eb',
'4c7bd839fd63731012feb08f4f219370fe6febf56e4a709c8b8cc1afa3bac65e',
'0a10adf58dfc0a84fa6f093bb933f4bc777001c7add52b60637a18a3779e5d54',
'98981e00f3d5c7be41a8dae6e062d42555f2bd8672e9c9a8dd9375803fd4267a',
'fab5f3e8c9849fc19105f05f28f31d297a04d70ef3d85a6cd147d53bda684c0e',
'2790937711173f0d6e6095308850945408218fd5823345e2b3a2107aa65f4dae',
'ae5fb75ab5b05ea54d6089023e00ae797d09449e7e1b69f8250ebf9bdc02bf4a',
'4f84f7296875f76ab5790e176ce4284e36558e4d46591c26cd66f7daca412ff6',
'bbe2e76c54ed38930a1940988a68abde0c20a313b8d40e29cb0984ac77f9f971',
'45d44e43a04b4025456e2e6f265c5fca05245350e817b79b28fd97e3212f832c',
'172a7646479dcc6482f88e7f8fc5fad88522a4c5aadf72cda35ae84c3f4fe2d6',
'4fa284945a06ee78943203be7833711b6fd22505f87efc7ae334c4897da57c80',
'2795f8838e10ad379a639063c17b72b323f2cb790975198bc8ac0ede416477b5',
'db857c67829c6026e1bbe6dc015db074d14eda24a576b1746781c3bc5d14c0ce',
'9709ed076fef74da5ef153337404d88c25b54b6a657b72c59d8325149ddc93de',
'0dcf380356f6d2d7bcba56930e898b83126048af6020904d6196a81885f95b9c',
'2540e7376a10dd67d07f5cc2423b05a7e1641a5631a03e783701940e820fffc2',
'3975271761b85a03064b115de71fd27b1bcaa226337a35e7f7bc229a055d9c3b',
'c98477c4016c5e5efa9740158747ecc599713304d770dab5841c182bff5aa62d',
'b3b9cb17bc96663a956fadf1a8cd1d09a323eefd1a5c887153a5e05ade95283a',
'7a1fbd32552fb1fa6f30e55e17ac880affbdfba7420ec4153c90801d89dadcbc',
'49b479660361c526944d3bd4edd7babdf04c47e0b9d413a2d42e5bda0554e053',
'25e901bbf4c3a95b499ec6db269c7685c3fafcb2294ad23430ff9a38fa732d6a',
'8827a9b2baf1e4b6bc3a8f8bd987cba04064613aa035a10c8df17c87762dba7d',
'd04fa3810fd0275502d93628c6399d1f6ee1e891f4285a3bb99d3f83cda6211d',
'4266ae72d5ee29b527b283d137f9f7552b926b21aec867158fc8256df08c6b54',
'9776901f56b85ab35f50615603157893daf337bfa3af97d42d9c6d5cbe98c61d',
'50cf0f4dc221c801ca62bf524309249b5dcc9fbaa8621c20d3d152348b3974ac',
'7382fa53b6d64b269fd9b326ec46104cd30480c8e7b193ca441c70b735621a46',
'ef4430106dbfc1bfee425f4a1288fafcedae17636e3e44483ab868b729a7cdb0',
'e16f57915e4fcbc535a0eacb7c46d39decd8594b6092d435e66f85ce4091b600',
'adc15fe031ca56ac63e317a838ac99972a19cd63c7294f9c53960080be0ec289',
'dafb9bffabee8044994e9b32d98278f8a0e74647dc80519d5f113014f2142b46',
'763b6ec4637f426f6b41fce6af7f23a3537925aaf3b23cbfdbae3227f6930a4e',
'db779d26caf9a11881630256cecf4cede992c616253af77958b706835e4f71a6',
'51d4d8ec4a8536041a80ae5899635162347e316525271659c99e0f79389ffc20',
'e118f346408aba460c5acc78d416ac4d78c93cd0f3f8edab2bfd1d4777e18ced',
'f12664c4ce0206a6b7307ecdc824aaaeec0ab00bd31608ffef628e8a35d2ee8a',
'8dba8a73696e6d0d61cfe7d6b3d1baf9d17a4b336edb5ce100f8fdcc9015f1f1',
'090b2240badddc79406930aefe159d2dce96998b0c4926f829fd28052cdba11f',
'ce75b076f7956692976b3dc2080ebbab7a1062b908bf1c4a071bd404726e175a',
'18d4d6bbebe8430b4c94e2e3c17ba24cce6a3c1a2ab7e46d103e97190aa3db36',
'7cbcc0ce87a0a9f725ee7200a7e86c9bc7d6247da2a96aef37b7ca6652563d88',
'640e2365b73d4c43d84d594b226d017ff520ed8059b889d295c0f900e735b29b',
'9684451f0c396ea9adbcc86fb4abc7306cc408301752d0a0209cc0dbfbb2f4fa',
'a32334d2e357e04f6aa41feaf23e4cad58e0ee7a718dcd6b1ea004d78708a672',
'15e9df0ab69520069311a5a8c20a1e0eead8d97547e4a848404d887803af1968',
'9f6f0a29256e77c8972c3da1a24b3f765dac2fcdb93820cbd88b7f82c54da3d9',
'320feb73d8c766dd6f86b95a7420469a2ad9a56e92c88598b7c2838db5c90ac9',
'1e3e464c911bbaea8c1b2ddc1b6e7630fc773efe77cca7792b9745f67a13ff96',
'988cbcf1afe194c59c04ee381787d28fca9fa5049c18873ee001f084acc6e448',
'0d36855b7571bb8cb95b715a042fe329da3a8e4b778f9d75823e936a0e549312',
'429cc35d04c057bb691eedb0b792bc829b6fd88cd2eecf72a728d7d496d8c30f',
'604c30546332ca9fd30b048303643dde9c7946615fe6869b730d16fdb7b02878',
'6146fe7c79189d69df963819d90ec4932552dae2db32dade683c6c63c76dad84',
'5e2a58a43ff166e931d08a540685d7f039017b4573682e03dd96543a7eacb73b',
'af40d8ec09b302be543caea9729c43042fe84faa0a66d9841055081d913c2093',
'afb884d94a0fdcd789e30a11e0e2d99361a624955570560c53e925f5bc3c637e',
'b389de9d41637bb5917fbecae7b8086d5e2a7e97e32be8f3372dea99bcba498d',
'd4f1d0593d2c452839ecea58caab35f01ee260ee24327a97fa1da71523ab825d',
'6b307a5ec87cb3f56b35717963070e00c60b6da250e422f6805956cd18820d5a',
'f926a5ca4f2d6dd6d47123ad512fe52f20547b5067765c68b1c51c038e22607e',
'7e083b829d991ee91486d7fee10276c289cb47465cf9b5113fb6ffcf9ee8d509',
'5d411870089af67c73b83251ad7fa198e26963684ec1fd6f53ecc05d0d5a3fa3',
'073a7a3001d559d0777ff559ff4aebe48dcc7d53c1876271b9cec534c8502846',
'e3c3a7a9cb9c83940f864be276ff6307934a1a23680269e654d00fd520621802',
'7bf2d61f2c128435cea56eaa869dffc5feb705ddd6b1b0752092caa0ecf2cf5a',
'9fd3974b313b0e7b66802259e39e493f77976fd403a02fc006680b5e310e6587',
'33513a8fd099520b3d7797153bb1f0cfa3afaf95c3ba12ec8c8d9936d3851776',
'932208d88197c89a6a819eff1d17515f9a00ef68140e7dbd8711d59fd3344ad0',
'0594fff94b6c5eef1aa3418f89a2b9a815b0b78cd37a9f5bf5e6c7527e341938',
'f0e8db89dc4bb6f1d0afd375a7ce62a1d6a8d30bead1a9e7fc2bf0a307402209',
'3cd8260017004591c8f66263f266f969ced86b858f2e3b43533d349f5f6d4167',
'e79b02d8eb1e72278d5d23db1ae4dd779bd834e4de15119a05f37fa849662277',
'c128256462490241ae90861f5ab5648f37c3579828588996f4eafd090209bc19',
'ecbbb79b86d4e5c55bc97273290c0b1989d6fe867d219fc3ab2837981a586671',
'b22772b8fce88d52a4280bb991411b25de1c6aa64da8889cf03548d95614900b',
'01f5c6de4661bda50e99434e37fff96f6d113e1dd2907f0f768816dca57a75f3',
'ee3ced889ded3e7484b61f8a591bff2e67e3b8b486f398e10db9f3fe74445a82',
'8a90dc410bbd5404421af830def2b74e2baf8af566524ef018f0a6f45443538d',
'f47160e6901aa364a49a122c37591bd6bfc3091931931c66a87cd8002857c120',
'41284ecc6d052cb2e2674a9e78058a402a201290d8e5a0912cea571f59289ad8',
'4d328859872d13930f86cd367c2c4879efe0d13114aa233cf8fce3e42274ff92',
'a48e6af747934c4e7441f35c616cb70f4793e4cc353c30cd99eae68fad8eb178',
'3de5562c2b42023dd09d83a53dd49c3d3f15dd82034602fa694fa9f197fb0fb8',
'70742e4750d2d41fa9249ec293cfee40eb98ec96f5dd9b3566180c2ce491e59b',
'9c18f5f2f520793a670fd7d13a81ed0b5b2b2353b80c1df9a26457ada2070aef',
'41dff580778dc0d6913456134c3c616714e913d049363a6ef2320984a900d0ea',
'33212388c36ef30a4a2344fb0ba69de51024ae6e9437d5a7da62bd0273231f9e',
'ab57f854266f4d74fe9e44b59153947a7d87719574c50bce7e987294d759acce',
'f55ab35d960b8d8f3b9157e8ad35307d478b3f35f1a78e4d48046b3cf1a58704',
'1153dece5a0fed4ba5b5946578df9cddcdedfedeefd26b0e9d403c89774c7e63',
'aac7e91aece0703ee8229d0347deb38dc6c5194fe19d65566594ebca627c2426',
'4f0d12abad3bd347134db8b6c7ca9d4db2490bff589ad63c4a431ac52b77cfed',
'ac4cbb2c073b66eb1e002e0d009a209b7b407e416f361ebf3b07df8f32f2f84c',
'270bce14bbb30b331ea04bb785118ba2460e780eca07410c019596199cee6f4b',
'4d1ec4d8e74b2cd7211e8effaf24979172bfc345e3ef10248c67980feb17969a',
'df25f18d610e8cdeded67016f8d0b48cda71691627a66681dab788a4cd92460a',
'3d5e468a3f1aef9b259eef7105e8eb5a0ce4c8d3a2230cf8fc036ff0fe61b4e3',
'e62f0d358a697f8863bd7ce8d29442661a5fea1e20d3cee08b8441919d01c8f1',
'68c424677691726381d4b80b664718609f0d7e96ff21ee204dc6afa3c7eafb26',
'386ab80b0dc9609063101c4408abe43f3edba36b51f5dbed4e4349a201a9310c',
'62ae5f6a96afe8b2fb971777e3fbf8daf1dbae26ba049dedc38e137ea1c592dd',
'74baa5e9183f820df28cac718fdf1081333eba1463be6b43be8534c757aab558',
'f81031a1c3919c0f04f467531b51da84cbe92cf846629456f6d837cefdbe4b18',
'6b3c0b47bfeaecfa277e2cef7ca9833e62abdc751c4fe0c3841d35185e09e000',
'a74e8a63c64f0f93dde2eb8e4080e280b911abd3587e1e94d6cb310eff4d5c3b',
'923ca648c0358452d2d320f76435b0991782a9810fcebe113ec1bbd90676e051',
'8fd5989e62ea9db5a22c3321b559eed0f71c9113659c4c898a2f6bd1cdd177e2',
'3944bcc8ac1eeefe8ca42b164887eedf09d7831560ba3b348cafd2d8a688f9aa',
'b7f1beed64f8c3b50d7ed2aa40ef81069a1435f2070e608ed79d6227561ebdf1',
'c96a0993941fc28892a5bab0566b7efe5282239f7dad2e9bf32eca4cd7a2e341',
'297fc73b91a9727ac6c68c2301868d1d18474638dad1939e37e92308584f72a5',
'6f62cbbaca8a3c1801bed061dfb25127e9ba5180d1fd62259c62be763bf1e060',
'5c25c6297cc55a73a85b9fd05b5e7f062bdcdad2638d938cc106a74635328a48',
'0f870c311083f9066cc1536355eb9311dd4d4ebb2eb3f4be59a68f82cf101ce2',
'b1bd3c1a5148bde436c6b5fee1c6aba715e90c9c796f7052737c1d9271c11304',
'f01eca1701776198dcd54facb4ea6d0430eac1f606ef3cb60e681d6866619dae',
'254bb1d8c26bb655db414eb8da2c818e5b722102de88533e39710e5a8fa189fc',
'd643b37250556456341f1e2ea3e92917e8979029207881d6b5523185d3f982cf',
'3094eeb53e4561f924207347c7eaacbcba33c87baa805b9e861002b48bc80198',
'8e122930dbe5f9d7e6b2ddbdc6077e42c6575b0513b7dc56723b74845b31f394',
'465796280843bd23899514250aeaa4604d5f93d0de32732d906c26356c4c0272',
'ec73e56997ffe21a8989a80b7b1b004ea81c4f9209295ae4ba58c20ff1490f6f',
'a8bfd689e82a1683ef217ec4a365c3723a327f82a23748d2524a6c9f38874deb',
'352b7579bbfee90dce0a78092e5af97b6c0e9f7722106859b675b103ac70ea61',
'4e217dc342815642c42a85e80017879d1fec77e97c55fd8982b498398a4c8141',
'677ef5637eb541fb9491f420dcc146b3b184dc9e232aa57ad36ddf1529f69622',
'16a8dd1a35c2711c330fa3e4ce108b48581d7f50bd39d52774a82d6404289e1d',
'caef4776c79795a8f5907c99d7d8a64ab2ba3d2484298b6fca6247f6c92deb15',
'a50f3d1859240d306d97b0bb9fee5be3e61dbd36744b57051a4b01713c0b018f',
'b8a4a6b56ff5ccf1c29a1270b760f799825cc9f875921457132080cff07a7ccf',
'90b7c60f1a910fa507273cb1c9e9688fb3371c501fa666ebee35fe0fd6876864',
'a6e68957ec18f9424be3c35854e8b510ff69fd2fcc245625c19fe756f8a6ba22',
'42622508d48ba6652c8c5e057a9ca0d4dca52df1c2c8f2e205ec5af84a4ae034',
'd6e330a0fdaa09122efb29026e6b29ae01c67dfa97152134699e5727ce8f1a8c',
'3ef8229136529cd0097b28f71a3473f82d37d77bf30e466d1a87ee990243b926',
'42141524318ce10cd680465d66d24839cc56f92b8c8511ce237e2f8c698b8acb',
'53aa67d564e8cb18c24be81b694cd6796af777616d91a563297610bf866664ed',
'66202c052c27c4947a892be7160829134f92e15e520d995b0649c398e5ab6c97',
'e37d3e48ab530054cc6001b96f6ff65d7fac342a945453bced9e7d0e93d2ce72',
'0ac8508da892c66f210fbd271fbd105659900df11d49d55fd70ab629543332d8',
'3f86a17910c56e2038a0bd4a9429754921c3db3b231981fa7777a0716de322d4',
'd1f6f3f6aa4aab63a93df3329719f0f17ce0abf6de44f91c74189fdf79397f03',
'7160c44006ac68d56b2d3d8ee9762aef613c988dc2bd118244edd6e5fd2888f1',
'cc7e88c6ad07001d77771f8aa8b1df8240344112d4a33b0df6c201d0dfce52e0',
'c0bc8fadc1aa383211db4d8ffc33fb42b1a0c4c6a8e41b31bf17cbeef6ce256d',
'ea8028180dff361f39a1f342a5df70a29102da2808415b7c4ad91fde82a8b50d',
'd53845f444884348f2853af02f61f4026f9cb587f934a821666263a07ce4bde2',
'4da0c42cd50b30f0225399ce606df889afbcee48a476704fed5d5cd06df6903f',
'7b49f85631277f0eb805c0d6c1339c2d06e1f7245147bd848595681e26be1c16',
'c25449daa3a5a20c432ffbd7224951942eac76cc752b8d40a5b8c0b4aebc643a',
'e04e97ea87675d30569ad0f4b87c468a3f5e2d4bdd8a57d194d9dc2d694c81a7',
'16ce4899882e7903d066f561944c4eb93757f91a5656b7950452db40102d7fef',
'a576f5dcafb9b39fcc8ef7f306b2282c60d5d25edf46002e14a26fc23ff9cf6e',
'801bb9ae6e63614aa8b18d7672501952352c8e7bcb4d435287bfa358b1a6ceaf',
'c66af0d4b38d60db6839e96dd5e51c39bd66f0eabada130456011aabaa82fb03',
'8c84643fffbd3b78330eac851e77b6b90b3787996c8909f276a01ad0e4bfe42e',
'7f31a398614e8dc5aa6e4c00a84684641a07c920f0769ab1456b0fd99988c586',
'4cc5cf690d91d4b0f17482d9b3cf37a8cc3cfbd9a80031c7d0ad83009e3eae4f',
'62aa36f441c3641b07a7e8bc517920f88dd866d75326f8969ea759067bb83519',
'2b3c96cae11cf115ce41ad3ad432a1be964525ecae064b297a03b1ffc8445c26',
'c1d6efb5b64f4411884f42c2222767f808f3e02bdf288ab308669e45f453ab0d',
'3144b230521841fbd05a4c007073ada4c01ba0db361dd269ff67619741ed01a7',
'c699166ed9dd58eaa1815fd9b940109974286b5f827c9ebf7f4cadcd235e0fc6',
'8dda730c35ebfd18471cfd22a9d82c61f43972b0f5cc22046277646cf5547954',
'9bb296afaefcf0af647acfacbb8b823f8d3a15e5655f139471a84909a88eb94d',
'4e93768d652cdc5ee161efc9f16318e100de93b995cbe0c1badba2dbc140b0c1',
'92f9f687c84d3c76b9b584fb974369c81fb412f2040aa1b728c6cc5db20f2810',
'1c1458a772198d641f5e23bcabfd11c8e45881762e419c262a5b2fbfd0ca5a24',
'1c0a012649db8561b4002a417897418e7127fb756b025a542552eb63d842c80a',
'c80d5f78a14cee0be8eaae8eecd93c79e9341aaa77b86e81c931710389072179',
'aa0227a4df7ba5ccd73828333cb9d9e50cdcf138aafdf23102e95654a691dd7a',
'1c40b7e8db498b8776b70e41a5e364e8e734c9fba441cce58bfa6ca11fa16742',
'e9ec6e6c690da07b38ba005b26ba97dc8ae201b5fc70978501a1ad973fd3145e',
'3dde18eafa3d4fb8deb7a6314e3448dec3ae4ee240ed9ad018f6a3f5c22fccbe',
'd51972193c1ab27686784fbc6bdf57f2ab3512db10d087fa7daa9b223b4dcd25',
'93ed06b90fd50dab61dcf147ce69c352730fd73ca0c9d2afd0b4da3ed8a01e1c',
'ecd834cd2b93485c58d01cfe47ebf3de8e9e56b395ef5bf29480e030b5d317da',
'235e0fee34ba0972c51b174a975fa97deb0855949962a63faa227e6c89827d9b',
'e3a0472f06f169a06bb26b2e3f9f5cd0d91051f324eeb8518f39aa989a013fa1',
'8d12689f78116526aa0d6eb24450fcc52cd1417cfba960f8e49ec2ce7e0ae146',
'2adba391227f919132be08d4063328bcec9edf8f7d3f3ffbb9552cb117eaf9df',
'4938f163678066ef336a0089ed40fbb329d29168fdb54a4417128796a7f28677',
'b4f00c50d3a326448724f0199c41f9f6b375b1fb09d3f08dee06aff50cd443a6',
'814d4127765aead557be45a54bf08fd1880a8c8d5cb69de2cd54ecf0b0ba9f2c',
'44d4882a9c0a793ea5e77931109d52e943485894718c37d2190a76078b1b0f71',
'14503fc26e9699b768dc90f32d316cff443f19f5dda3d264454eb61674bd973f',
'2f8c794eac8254b4048935104bb6cafede8ce24543b3ed5cae556d2d0a7a4581',
'dabc14dd8539ae18a5a9243fb6c78b6dfdb348bf99c1ad1db57080d9b01ebfc2',
'514cd93b8d3ee13b807dee6f81071a5afa7c6bf8e6398b1f8a2670b8641410b7',
'1ce47158d3d4b55d637bffc72ec4300646477682a4b07954bd0297b6891c725f',
'e3086334be719c20df144302013188714ab70a9e712957aa397b65e197d5a260',
'9f64264fe373658c52703429a35a8ef451e78e280ebd215110acacf41c692062',
'9be7ab557421e8164e2e890bc9e9c393b911e2e7346bca586ea8ea746db5ef82',
'77de61802eeab102a1c8f835a550d3158d5f19c3cfcb0e2c341ff3370fa0cf78',
'5bef4e0dbe537b32135689ca199df434255e7914fe695454832a7ba58924aa4c',
'4cbb8510cbc98a9576a743a6848ed2987b3f1c5701152dabbcf2587ada7e11cf',
'4d9a2a4df387574c5d1df5b6c95dc72d3976778ab4b440ce665723962470414b',
'2d182aac179091eb1a452ca88b928d65b353d06b018ce76dc405382cd3e7b32c',
'1df4967e9866867aa6c108861b41c60efa4b430aeaf352274dcd1716a86e22e6',
'2136ad0d7378afea9d74fee86f193ee321a5f6b76cb3d4f00ae6c3fca0b73468',
'146a3f30fafa3bac8eb2cba7488e37523a127dfa9d5db5e62231ed073d4db98d',
'dc1d5351986f79b13fcb55dbebc14b58a760a2cc78e493782f363039031dc0a2',
'988d61985965f6319786edfacffc7ea6c60d22482257e816d0ca32c53d12a2d9',
'a04ea0bb0a58aa953ec51e3e52cbfe1bb140551f46c05d1211e01c43675d59f0',
'2b31f8378b9a1d8dddb5738490e88aabc447c3325fb093ef2675d888c40bb5e7',
'87c37a2e48a74655540eaafe311c6c83ae606611a78f5d3eb0dca7dcf0eab7f5',
'1e00f056d6bda9518676e6f30c6da5abd766ee36c79ef3fab11d3afa01854eb5',
'354dd562a379f18f6182d377ff1d73a15b059e55d230e26b381d2a8cfd00d56b',
'0b114de869ee0665af1b2b3a8732457bbdc404ef009cce95b5cec8c2603f9ff8',
'f9cc18d4cd894d1b61d9de374d9d1665af3d10d6f3342156b85c0f0ae0c73cbc',
'4a5ca92a505ba9a09c970d746c33ddaa206584ddd34e2e485b68dffbcf621cfc',
'b9a05501f19a137bcfefba702aad7628aff3fe6ce2cb8be03796373dec279907',
'f1ebd02e1cdf382cf75344a6289b8bf132227350fb46147961bf74650d38b0c5',
'466acf103d5e3fca2f406e97496af96dbdbba99ba5204e2ef18584574c12fe07',
'a03f2217b1781f1e0fa0a5eb5ca11a43c6c88f0374bc54f3930eab57247ae089',
'e6bfc52d2e4f5fe47c5d06746b6ecd28bb083a9ae7338746578e9b6c512f3878',
'f4c2aa16e5a355b5839502848b519ccf28014c7e185bbc5ab3bf787517f247b4',
'9552ceac803aac6bd88f3b95de8dce4f314730962b2d1a598cbd0bb61017d8d8',
'c9f0eac85cdbf0ce00ed19197f938084f36f81a45ed6470931287c0471e64272',
'fc8c9b7720e4671de42c4473a603c4ff7ef49e579b70bde01431b1a1e905cbd0',
'f92eda195323a5938bc3514116fa4f05dac695c785a20be8f8afa8e967901769',
'36671b6c0a92eec53a0bbf98c1e7f1e9734ea2e9185d0d5038a8c4a157ba876f',
'ce6b04307b81dacce56cb3c87972ecdd28bba40e52b9d1efeea5651f52fe691f',
'ea4a8c27e68bbb4f70121a2c7b1877cdff8737e014e9bad1abbe83b0c3997b1c',
'5ef8953f395c841e27ddc1fe3ca0d79331c8189807cec5ed0ff724da5e08cefe',
'bcd37032ad64ce978586cfb1a65b3ec5bb7446eefada4149ce9d7a88b669ed7a',
'ff1e4deba577be3a1e9c9c31c61d305bc0aa13c10bc2ec6064057adc4a36c5f1',
'a8b67d1442fd65d4d05d3bcb6da74c7a59739787df08e120fd4d733e5f966aa7',
'c9eba770b629fd32d2180553cb96d8352dc5cb0bb652f900ffc1e9e29038f98f',
'ed1da7bbd10476dd0e84d0cefa6a457230c84bb4b31190b4ff4b58e25c398210',
'3aa16d6479d93b8bd0c4642856190ea0dc8777723b74089af58bd61b4700d8f2',
'bb47dea8df3aab2f16960e5ab33fa54c9c1bc239833f69fabba64a7b099fc688',
'f2ee6e2778650b556ec5e5e6fc6127e31fe7976efd7b2401e7b36cc7ec260fd4',
'a2604f43af7a5b9f600d34e0c9a1b3829360d2ccbbe20de96a0c32f4a7d70e18',
'b55b22f981a3ffe3c8ccf0ef320f72cd4f228b74bd4bda73236a0c8f67478999',
'322f60f380573876b5391e77b1296640d97de56d6efcb21f83d6d010e0820165',
'45d44e43a04b4025456e2e6f265c5fca05245350e817b79b28fd97e3212f832c',
'4266ae72d5ee29b527b283d137f9f7552b926b21aec867158fc8256df08c6b54',
'f0e8db89dc4bb6f1d0afd375a7ce62a1d6a8d30bead1a9e7fc2bf0a307402209',
'3cd8260017004591c8f66263f266f969ced86b858f2e3b43533d349f5f6d4167',
'e79b02d8eb1e72278d5d23db1ae4dd779bd834e4de15119a05f37fa849662277',
'8a9i0dc410bbd5404421af830def2b74e2baf8af566524ef018f0a6f45443538d',
'f47160e6901aa364a49a122c37591bd6bfc3091931931c66a87cd8002857c120',
'41284ecc6d052cb2e2674a9e78058a402a201290d8e5a0912cea571f59289ad8',
'4d328859872d13930f86cd367c2c4879efe0d13114aa233cf8fce3e42274ff92' }
def retryRequest(tempserverlist, apicall):
while len(tempserverlist) != 0:
try:
response = requests.get('{}api/{}'.format(tempserverlist[0], apicall))
except:
tempserverlist.pop(0)
else:
if response.status_code == 200:
return json.loads(response.content)
else:
tempserverlist.pop(0)
if len(tempserverlist) == 0:
logger.error("None of the APIs are responding for the call {}".format(apicall))
return 0
def multiRequest(apicall, net):
testserverlist = ['http://0.0.0.0:9000/', 'https://testnet-flosight.duckdns.org/', 'https://testnet.flocha.in/']
mainserverlist = ['https://flosight.duckdns.org/','http://0.0.0.0:9495/']
if net == 'mainnet':
return retryRequest(mainserverlist, apicall)
elif net == 'testnet':
return retryRequest(testserverlist, apicall)
def pushData_SSEapi(message):
signature = pybtc.sign_message(message.encode(), privKey)
headers = {'Accept': 'application/json', 'Content-Type': 'application/json', 'Signature': signature}
'''try:
r = requests.post(sseAPI_url, json={'message': '{}'.format(message)}, headers=headers)
except:
logger.error("couldn't push the following message to SSE api {}".format(message))'''
print('')
def processBlock(blockindex):
logger.info(f'Processing block {blockindex}')
logger.info('Processing block ' + str(blockindex))
# Get block details
response = multiRequest(f"block-index/{blockindex}", config['DEFAULT']['NET'])
blockhash = response['blockHash']
blockinfo = multiRequest(f"block/{blockhash}", config['DEFAULT']['NET'])
# todo Rule 8 - read every transaction from every block to find and parse flodata
counter = 0
acceptedTxList = []
# Scan every transaction
logger.info("Before tx loop")
#pdb.set_trace()
counter = 0
#pdb.set_trace()
for transaction in blockinfo["tx"]:
if blockindex < 4365011 and (transaction not in goodtxset):
continue
counter = counter + 1
logger.info(f"Transaction {counter} {transaction}")
#transaction_data = multiRequest(f"tx/{transaction}", config['DEFAULT']['NET'])
#try:
# text = transaction_data["floData"]
# text = text.replace("\n", " \n ")
#except:
# logger.info("The API has passed the Block height test but failed transaction_data['floData'] test. transaction_data response from the API is logged below")
# logger.info(f"{transaction_data}")
#if current_index == -1:
current_index = -1
while(current_index == -1):
transaction_data = multiRequest(f"tx/{transaction}", config['DEFAULT']['NET'])
try:
text = transaction_data["floData"]
text = text.replace("\n", " \n ")
current_index = 2
except:
logger.info("The API has passed the Block height test but failed transaction_data['floData'] test")
logger.info(f"Block Height : {blockindex}")
logger.info(f"Transaction {transaction} data : ")
logger.info(transaction_data)
logger.info('Program will wait for 1 seconds and try to reconnect')
time.sleep(1)
# todo Rule 9 - Reject all noise transactions. Further rules are in parsing.py
returnval = None
parsed_data = parsing.parse_flodata(text, blockinfo, config['DEFAULT']['NET'])
if parsed_data['type'] != 'noise':
logger.info(f"Processing transaction {transaction}")
logger.info(f"flodata {text} is parsed to {parsed_data}")
returnval = processTransaction(transaction_data, parsed_data)
if returnval == 1:
acceptedTxList.append(transaction)
elif returnval == 0:
logger.info("Transfer for the transaction %s is illegitimate. Moving on" % transaction)
if len(acceptedTxList) > 0:
tempinfo = blockinfo['tx'].copy()
for tx in blockinfo['tx']:
if tx not in acceptedTxList:
tempinfo.remove(tx)
blockinfo['tx'] = tempinfo
updateLatestBlock(blockinfo)
engine = create_engine('sqlite:///system.db')
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
entry = session.query(SystemData).filter(SystemData.attribute == 'lastblockscanned').all()[0]
entry.value = str(blockindex)
session.commit()
session.close()
# Check smartContracts which will be triggered locally, and not by the contract committee
checkLocaltriggerContracts(blockinfo)
def processApiBlock(blockhash):
logger.info(config['DEFAULT']['NET'])
blockinfo = multiRequest('block/{}'.format(str(blockhash)), config['DEFAULT']['NET'])
# todo Rule 8 - read every transaction from every block to find and parse flodata
counter = 0
acceptedTxList = []
# Scan every transaction
for transaction in blockinfo["tx"]:
transaction_data = multiRequest(f"tx/{transaction}", config['DEFAULT']['NET'])
text = transaction_data["floData"]
text = text.replace("\n", " \n ")
# todo Rule 9 - Reject all noise transactions. Further rules are in parsing.py
returnval = None
parsed_data = parsing.parse_flodata(text, blockinfo, config['DEFAULT']['NET'])
if parsed_data['type'] != 'noise':
logger.info(f"Processing transaction {transaction}")
logger.info(f"flodata {text} is parsed to {parsed_data}")
returnval = processTransaction(transaction_data, parsed_data)
if returnval == 1:
acceptedTxList.append(transaction)
elif returnval == 0:
logger.info("Transfer for the transaction %s is illegitimate. Moving on" % transaction)
if len(acceptedTxList) > 0:
tempinfo = blockinfo['tx'].copy()
for tx in blockinfo['tx']:
if tx not in acceptedTxList:
tempinfo.remove(tx)
blockinfo['tx'] = tempinfo
updateLatestBlock(blockinfo)
engine = create_engine('sqlite:///system.db')
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
entry = session.query(SystemData).filter(SystemData.attribute == 'lastblockscanned').all()[0]
entry.value = str(blockinfo['height'])
logger.info('Last scanned block value should be '+ str(entry.value))
session.commit()
session.close()
# Check smartContracts which will be triggered locally, and not by the contract committee
checkLocaltriggerContracts(blockinfo)
def updateLatestTransaction(transactionData, parsed_data):
# connect to latest transaction db
conn = sqlite3.connect('latestCache.db')
conn.execute(
"INSERT INTO latestTransactions(transactionHash, blockNumber, jsonData, transactionType, parsedFloData) VALUES (?,?,?,?,?)",
(transactionData['txid'], transactionData['blockheight'], json.dumps(transactionData), parsed_data['type'],
json.dumps(parsed_data)))
conn.commit()
conn.close()
def updateLatestBlock(blockData):
# connect to latest block db
conn = sqlite3.connect('latestCache.db')
conn.execute('INSERT INTO latestBlocks(blockNumber, blockHash, jsonData) VALUES (?,?,?)',
(blockData['height'], blockData['hash'], json.dumps(blockData)))
conn.commit()
conn.close()
def transferToken(tokenIdentification, tokenAmount, inputAddress, outputAddress, transaction_data=None, parsed_data=None):
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(ActiveTable.transferBalance)).filter_by(address=inputAddress).all()[0][0]
commentTransferAmount = float(tokenAmount)
if availableTokens is None:
logger.info(f"The sender address {inputAddress} doesn't own any {tokenIdentification.upper()} tokens")
session.close()
return 0
elif availableTokens < commentTransferAmount:
logger.info(
"The transfer amount passed in the comments is more than the user owns\nThis transaction will be discarded\n")
session.close()
return 0
elif availableTokens >= commentTransferAmount:
table = session.query(ActiveTable).filter(ActiveTable.address == inputAddress).all()
block_data = multiRequest('block/{}'.format(transaction_data['blockhash']), config['DEFAULT']['NET'])
pidlst = []
checksum = 0
for row in table:
if checksum >= commentTransferAmount:
break
pidlst.append([row.id, row.transferBalance])
checksum = checksum + row.transferBalance
if checksum == commentTransferAmount:
consumedpid_string = ''
# Update all pids in pidlist's transferBalance to 0
lastid = session.query(ActiveTable)[-1].id
for piditem in pidlst:
entry = session.query(ActiveTable).filter(ActiveTable.id == piditem[0]).all()
consumedpid_string = consumedpid_string + '{},'.format(piditem[0])
session.add(TransferLogs(sourceFloAddress=inputAddress, destFloAddress=outputAddress,
transferAmount=entry[0].transferBalance, sourceId=piditem[0],
destinationId=lastid + 1,
blockNumber=block_data['height'], time=block_data['time'],
transactionHash=transaction_data['txid']))
entry[0].transferBalance = 0
if len(consumedpid_string) > 1:
consumedpid_string = consumedpid_string[:-1]
# Make new entry
session.add(ActiveTable(address=outputAddress, consumedpid=consumedpid_string,
transferBalance=commentTransferAmount))
# Migration
# shift pid of used utxos from active to consumed
for piditem in pidlst:
# move the parentids consumed to consumedpid column in both activeTable and consumedTable
entries = session.query(ActiveTable).filter(ActiveTable.parentid == piditem[0]).all()
for entry in entries:
entry.consumedpid = entry.consumedpid + ',{}'.format(piditem[0])
entry.parentid = None
entries = session.query(ConsumedTable).filter(ConsumedTable.parentid == piditem[0]).all()
for entry in entries:
entry.consumedpid = entry.consumedpid + ',{}'.format(piditem[0])
entry.parentid = None
# move the pids consumed in the transaction to consumedTable and delete them from activeTable
session.execute(
'INSERT INTO consumedTable (id, address, parentid, consumedpid, transferBalance) SELECT id, address, parentid, consumedpid, transferBalance FROM activeTable WHERE id={}'.format(
piditem[0]))
session.execute('DELETE FROM activeTable WHERE id={}'.format(piditem[0]))
session.commit()
session.commit()
elif checksum > commentTransferAmount:
consumedpid_string = ''
# Update all pids in pidlist's transferBalance
lastid = session.query(ActiveTable)[-1].id
for idx, piditem in enumerate(pidlst):
entry = session.query(ActiveTable).filter(ActiveTable.id == piditem[0]).all()
if idx != len(pidlst) - 1:
session.add(TransferLogs(sourceFloAddress=inputAddress, destFloAddress=outputAddress,
transferAmount=entry[0].transferBalance, sourceId=piditem[0],
destinationId=lastid + 1,
blockNumber=block_data['height'], time=block_data['time'],
transactionHash=transaction_data['txid']))
entry[0].transferBalance = 0
consumedpid_string = consumedpid_string + '{},'.format(piditem[0])
else:
session.add(TransferLogs(sourceFloAddress=inputAddress, destFloAddress=outputAddress,
transferAmount=piditem[1] - (checksum - commentTransferAmount),
sourceId=piditem[0],
destinationId=lastid + 1,
blockNumber=block_data['height'], time=block_data['time'],
transactionHash=transaction_data['txid']))
entry[0].transferBalance = checksum - commentTransferAmount
if len(consumedpid_string) > 1:
consumedpid_string = consumedpid_string[:-1]
# Make new entry
session.add(ActiveTable(address=outputAddress, parentid=pidlst[-1][0], consumedpid=consumedpid_string,
transferBalance=commentTransferAmount))
# Migration
# shift pid of used utxos from active to consumed
for piditem in pidlst[:-1]:
# move the parentids consumed to consumedpid column in both activeTable and consumedTable
entries = session.query(ActiveTable).filter(ActiveTable.parentid == piditem[0]).all()
for entry in entries:
entry.consumedpid = entry.consumedpid + ',{}'.format(piditem[0])
entry.parentid = None
entries = session.query(ConsumedTable).filter(ConsumedTable.parentid == piditem[0]).all()
for entry in entries:
entry.consumedpid = entry.consumedpid + ',{}'.format(piditem[0])
entry.parentid = None
# move the pids consumed in the transaction to consumedTable and delete them from activeTable
session.execute(
'INSERT INTO consumedTable (id, address, parentid, consumedpid, transferBalance) SELECT id, address, parentid, consumedpid, transferBalance FROM activeTable WHERE id={}'.format(
piditem[0]))
session.execute('DELETE FROM activeTable WHERE id={}'.format(piditem[0]))
session.commit()
session.commit()
block_data = multiRequest('block/{}'.format(transaction_data['blockhash']), config['DEFAULT']['NET'])
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(TransactionHistory(sourceFloAddress=inputAddress, destFloAddress=outputAddress,
transferAmount=tokenAmount, blockNumber=block_data['height'],
blockHash=block_data['hash'], time=block_data['time'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference, jsonData=json.dumps(transaction_data),
transactionType=parsed_data['type'],
parsedFloData=json.dumps(parsed_data)))
session.commit()
session.close()
return 1
def checkLocaltriggerContracts(blockinfo):
engine = create_engine('sqlite:///system.db', echo=False)
connection = engine.connect()
# todo : filter activeContracts which only have local triggers
activeContracts = connection.execute(
'select contractName, contractAddress from activecontracts where status=="active" ').fetchall()
connection.close()
for contract in activeContracts:
# pull out the contract structure into a dictionary
engine = create_engine('sqlite:///smartContracts/{}-{}.db'.format(contract[0], contract[1]), echo=True)
connection = engine.connect()
# todo : filter activeContracts which only have local triggers
attributevaluepair = connection.execute(
"select attribute, value from contractstructure where attribute != 'contractName' and attribute != 'flodata' and attribute != 'contractAddress'").fetchall()
contractStructure = {}
conditionDict = {}
counter = 0
for item in attributevaluepair:
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'] == 'one-time-event':
# Check if the contract has blockchain trigger or committee trigger
if 'exitconditions' in contractStructure:
# This is a committee trigger contract
expiryTime = contractStructure['expiryTime']
expirytime_split = expiryTime.split(' ')
parse_string = '{}/{}/{} {}'.format(expirytime_split[3], parsing.months[expirytime_split[1]],
expirytime_split[2], expirytime_split[4])
expirytime_object = parsing.arrow.get(parse_string, 'YYYY/M/D HH:mm:ss').replace(
tzinfo=expirytime_split[5][3:])
blocktime_object = parsing.arrow.get(blockinfo['time']).to('Asia/Kolkata')
if blocktime_object > expirytime_object:
if 'minimumsubscriptionamount' in contractStructure:
minimumsubscriptionamount = contractStructure['minimumsubscriptionamount']
tokenAmount_sum = \
connection.execute('select sum(tokenAmount) from contractparticipants').fetchall()[0][0]
if tokenAmount_sum < minimumsubscriptionamount:
# Initialize payback to contract participants
contractParticipants = connection.execute(
'select participantAddress, tokenAmount, transactionHash from contractparticipants').fetchall()[
0][0]
for participant in contractParticipants:
tokenIdentification = contractStructure['tokenIdentification']
contractAddress = connection.execute(
'select * from contractstructure where attribute="contractAddress"').fetchall()[0][
0]
returnval = transferToken(tokenIdentification, participant[1], contractAddress,
participant[0])
if returnval is None:
logger.critical(
"Something went wrong in the token transfer method while doing local Smart Contract Trigger. THIS IS CRITICAL ERROR")
return
connection.execute(
'update contractparticipants set winningAmount="{}" where participantAddress="{}" and transactionHash="{}"'.format(
(participant[1], participant[0], participant[2])))
# add transaction to ContractTransactionHistory
engine = create_engine(
'sqlite:///smartContracts/{}-{}.db'.format(contract[0],
contract[1]),
echo=True)
ContractBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
session.add(ContractTransactionHistory(transactionType='trigger',
transactionSubType='minimumsubscriptionamount-payback',
transferAmount=None,
blockNumber=blockinfo['height'],
blockHash=blockinfo['hash'],
time=blockinfo['time']))
session.commit()
session.close()
engine = create_engine('sqlite:///system.db', echo=True)
connection = engine.connect()
connection.execute(
'update activecontracts set status="closed" where contractName="{}" and contractAddress="{}"'.format(
contract[0], contract[1]))
connection.execute(
'update activecontracts set closeDate="{}" where contractName="{}" and contractAddress="{}"'.format(
blockinfo['time'],
contract[0], contract[1]))
connection.close()
engine = create_engine('sqlite:///system.db', echo=True)
connection = engine.connect()
connection.execute(
'update activecontracts set status="expired" where contractName="{}" and contractAddress="{}"'.format(
contract[0], contract[1]))
connection.execute(
'update activecontracts set expirydate="{}" where contractName="{}" and contractAddress="{}"'.format(
blockinfo['time'],
contract[0], contract[1]))
connection.close()
elif 'payeeAddress' in contractStructure:
# This is a local trigger contract
if 'maximumsubscriptionamount' in contractStructure:
maximumsubscriptionamount = connection.execute(
'select value from contractstructure where attribute=="maximumsubscriptionamount"').fetchall()[
0][0]
tokenAmount_sum = \
connection.execute('select sum(tokenAmount) from contractparticipants').fetchall()[0][0]
if tokenAmount_sum >= maximumsubscriptionamount:
# Trigger the contract
payeeAddress = contractStructure['payeeAddress']
tokenIdentification = contractStructure['tokenIdentification']
contractAddress = contractStructure['contractAddress']
returnval = transferToken(tokenIdentification, tokenAmount_sum, contractAddress, payeeAddress)
if returnval is None:
logger.critical(
"Something went wrong in the token transfer method while doing local Smart Contract Trigger")
return
connection.execute(
'update contractparticipants set winningAmount="{}"'.format(
(0)))
# add transaction to ContractTransactionHistory
engine = create_engine(
'sqlite:///smartContracts/{}-{}.db'.format(contract[0],
contract[1]),
echo=True)
ContractBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
session.add(ContractTransactionHistory(transactionType='trigger',
transactionSubType='maximumsubscriptionamount',
sourceFloAddress=contractAddress,
destFloAddress=payeeAddress,
transferAmount=tokenAmount_sum,
blockNumber=blockinfo['height'],
blockHash=blockinfo['hash'],
time=blockinfo['time']))
session.commit()
session.close()
engine = create_engine('sqlite:///system.db', echo=False)
connection = engine.connect()
connection.execute(
'update activecontracts set status="closed" where contractName="{}" and contractAddress="{}"'.format(
contract[0], contract[1]))
connection.execute(
'update activecontracts set closeDate="{}" where contractName="{}" and contractAddress="{}"'.format(
blockinfo['time'], contract[0], contract[1]))
connection.execute(
'update activecontracts set expiryDate="{}" where contractName="{}" and contractAddress="{}"'.format(
blockinfo['time'], contract[0], contract[1]))
connection.close()
return
expiryTime = contractStructure['expiryTime']
expirytime_split = expiryTime.split(' ')
parse_string = '{}/{}/{} {}'.format(expirytime_split[3], parsing.months[expirytime_split[1]],
expirytime_split[2], expirytime_split[4])
expirytime_object = parsing.arrow.get(parse_string, 'YYYY/M/D HH:mm:ss').replace(
tzinfo=expirytime_split[5][3:])
blocktime_object = parsing.arrow.get(blockinfo['time']).to('Asia/Kolkata')
if blocktime_object > expirytime_object:
if 'minimumsubscriptionamount' in contractStructure:
minimumsubscriptionamount = contractStructure['minimumsubscriptionamount']
tokenAmount_sum = \
connection.execute('select sum(tokenAmount) from contractparticipants').fetchall()[0][0]
if tokenAmount_sum < minimumsubscriptionamount:
# Initialize payback to contract participants
contractParticipants = connection.execute(
'select participantAddress, tokenAmount, transactionHash from contractparticipants').fetchall()[
0][0]
for participant in contractParticipants:
tokenIdentification = connection.execute(
'select * from contractstructure where attribute="tokenIdentification"').fetchall()[
0][
0]
contractAddress = connection.execute(
'select * from contractstructure where attribute="contractAddress"').fetchall()[0][
0]
returnval = transferToken(tokenIdentification, participant[1], contractAddress,
participant[0])
if returnval is None:
logger.critical(
"Something went wrong in the token transfer method while doing local Smart Contract Trigger")
return
connection.execute(
'update contractparticipants set winningAmount="{}" where participantAddress="{}" and transactionHash="{}"'.format(
(participant[1], participant[0], participant[2])))
# add transaction to ContractTransactionHistory
engine = create_engine(
'sqlite:///smartContracts/{}-{}.db'.format(contract[0],
contract[1]),
echo=True)
ContractBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
session.add(ContractTransactionHistory(transactionType='trigger',
transactionSubType='minimumsubscriptionamount-payback',
transferAmount=None,
blockNumber=blockinfo['height'],
blockHash=blockinfo['hash'],
time=blockinfo['time']))
session.commit()
session.close()
engine = create_engine('sqlite:///system.db', echo=False)
connection = engine.connect()
connection.execute(
'update activecontracts set status="closed" where contractName="{}" and contractAddress="{}"'.format(
contract[0], contract[1]))
connection.execute(
'update activecontracts set closeDate="{}" where contractName="{}" and contractAddress="{}"'.format(
blockinfo['time'], contract[0], contract[1]))
connection.execute(
'update activecontracts set expiryDate="{}" where contractName="{}" and contractAddress="{}"'.format(
blockinfo['time'], contract[0], contract[1]))
connection.close()
return
# Trigger the contract
payeeAddress = contractStructure['payeeAddress']
tokenIdentification = contractStructure['tokenIdentification']
contractAddress = contractStructure['contractAddress']
engine = create_engine('sqlite:///smartContracts/{}-{}.db'.format(contract[0], contract[1]),
echo=True)
connection = engine.connect()
tokenAmount_sum = \
connection.execute('select sum(tokenAmount) from contractparticipants').fetchall()[0][0]
returnval = transferToken(tokenIdentification, tokenAmount_sum, contractAddress, payeeAddress)
if returnval is None:
logger.critical(
"Something went wrong in the token transfer method while doing local Smart Contract Trigger")
return
connection.execute('update contractparticipants set winningAmount="{}"'.format(0))
# add transaction to ContractTransactionHistory
engine = create_engine(
'sqlite:///smartContracts/{}-{}.db'.format(contract[0],
contract[1]),
echo=True)
ContractBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
session.add(ContractTransactionHistory(transactionType='trigger',
transactionSubType='expiryTime',
sourceFloAddress=contractAddress,
destFloAddress=payeeAddress,
transferAmount=tokenAmount_sum,
blockNumber=blockinfo['height'],
blockHash=blockinfo['hash'],
time=blockinfo['time']))
session.commit()
session.close()
engine = create_engine('sqlite:///system.db', echo=False)
connection = engine.connect()
connection.execute(
'update activecontracts set status="closed" where contractName="{}" and contractAddress="{}"'.format(
contract[0], contract[1]))
connection.execute(
'update activecontracts set closeDate="{}" where contractName="{}" and contractAddress="{}"'.format(
blockinfo['time'], contract[0], contract[1]))
connection.execute(
'update activecontracts set expiryDate="{}" where contractName="{}" and contractAddress="{}"'.format(
blockinfo['time'], contract[0], contract[1]))
connection.close()
return
def processTransaction(transaction_data, parsed_data):
# Do the necessary checks for the inputs and outputs
# todo Rule 38 - Here we are doing FLO processing. We attach asset amounts to a FLO address, so every FLO address
# will have multiple feed ins of the asset. Each of those feedins will be an input to the address.
# an address can also spend the asset. Each of those spends is an output of that address feeding the asset into some
# other address an as input
# Rule 38 reframe - For checking any asset transfer on the flo blockchain it is possible that some transactions may use more than one
# vins. However in any single transaction the system considers valid, they can be only one source address from which the flodata is
# originting. To ensure consistency, we will have to check that even if there are more than one vins in a transaction, there should be
# excatly one FLO address on the originating side and that FLO address should be the owner of the asset tokens being transferred
# Create vinlist and outputlist
vinlist = []
querylist = []
# todo Rule 39 - Create a list of vins for a given transaction id
for obj in transaction_data["vin"]:
querylist.append([obj["txid"], obj["vout"]])
totalinputval = 0
inputadd = ''
# todo Rule 40 - For each vin, find the feeding address and the fed value. Make an inputlist containing [inputaddress, n value]
for query in querylist:
content = multiRequest('tx/{}'.format(str(query[0])), config['DEFAULT']['NET'])
for objec in content["vout"]:
if objec["n"] == query[1]:
inputadd = objec["scriptPubKey"]["addresses"][0]
totalinputval = totalinputval + float(objec["value"])
vinlist.append([inputadd, objec["value"]])
# todo Rule 41 - Check if all the addresses in a transaction on the input side are the same
for idx, item in enumerate(vinlist):
if idx == 0:
temp = item[0]
continue
if item[0] != temp:
logger.info(
f"System has found more than one address as part of vin. Transaction {transaction_data['txid']} is rejected")
return 0
inputlist = [vinlist[0][0], totalinputval]
# todo Rule 42 - If the number of vout is more than 2, reject the transaction
if len(transaction_data["vout"]) > 2:
logger.info(
f"System has found more than 2 address as part of vout. Transaction {transaction_data['txid']} is rejected")
return 0
# todo Rule 43 - A transaction accepted by the system has two vouts, 1. The FLO address of the receiver
# 2. Flo address of the sender as change address. If the vout address is change address, then the other adddress
# is the recevier address
outputlist = []
addresscounter = 0
inputcounter = 0
for obj in transaction_data["vout"]:
if obj["scriptPubKey"]["type"] == "pubkeyhash":
addresscounter = addresscounter + 1
if inputlist[0] == obj["scriptPubKey"]["addresses"][0]:
inputcounter = inputcounter + 1
continue
outputlist.append([obj["scriptPubKey"]["addresses"][0], obj["value"]])
if addresscounter == inputcounter:
outputlist = [inputlist[0]]
elif len(outputlist) != 1:
logger.info(
f"Transaction's change is not coming back to the input address. Transaction {transaction_data['txid']} is rejected")
return 0
else:
outputlist = outputlist[0]
logger.info(
f"Input address list : {inputlist}")
logger.info(
f"Output address list : {outputlist}")
# All FLO checks completed at this point.
# Semantic rules for parsed data begins
# todo Rule 44 - Process as per the type of transaction
if parsed_data['type'] == 'transfer':
logger.info(f"Transaction {transaction_data['txid']} is of the type transfer")
# todo Rule 45 - If the transfer type is token, then call the function transferToken to adjust the balances
if parsed_data['transferType'] == 'token':
# check if the token exists in the database
if os.path.isfile(f"./tokens/{parsed_data['tokenIdentification']}.db"):
# Check if the transaction hash already exists in the token db
engine = create_engine(f"sqlite:///tokens/{parsed_data['tokenIdentification']}.db", echo=True)
connection = engine.connect()
blockno_txhash = connection.execute(
'select blockNumber, transactionHash from transactionHistory').fetchall()
connection.close()
blockno_txhash_T = list(zip(*blockno_txhash))
if transaction_data['txid'] in list(blockno_txhash_T[1]):
logger.warning(
f"Transaction {transaction_data['txid']} already exists in the token db. This is unusual, please check your code")
pushData_SSEapi(
f"Error | Transaction {transaction_data['txid']} already exists in the token db. This is unusual, please check your code")
return 0
returnval = transferToken(parsed_data['tokenIdentification'], parsed_data['tokenAmount'], inputlist[0],
outputlist[0], transaction_data, parsed_data)
if returnval is None:
logger.info("Something went wrong in the token transfer method")
pushData_SSEapi(
f"Error | Something went wrong while doing the internal db transactions for {transaction_data['txid']}")
return 0
else:
updateLatestTransaction(transaction_data, parsed_data)
# If this is the first interaction of the outputlist's address with the given token name, add it to token mapping
engine = create_engine('sqlite:///system.db', echo=True)
connection = engine.connect()
firstInteractionCheck = connection.execute(
f"select * from tokenAddressMapping where tokenAddress='{outputlist[0]}' and token='{parsed_data['tokenIdentification']}'").fetchall()
if len(firstInteractionCheck) == 0:
connection.execute(
f"INSERT INTO tokenAddressMapping (tokenAddress, token, transactionHash, blockNumber, blockHash) VALUES ('{outputlist[0]}', '{parsed_data['tokenIdentification']}', '{transaction_data['txid']}', '{transaction_data['blockheight']}', '{transaction_data['blockhash']}')")
connection.close()
# Pass information to SSE channel
url = 'https://ranchimallflo.duckdns.org/'
headers = {'Accept': 'application/json', 'Content-Type': 'application/json'}
# r = requests.post(url, json={f"message': 'Token Transfer | name:{parsed_data['tokenIdentification']} | transactionHash:{transaction_data['txid']}"}, headers=headers)
return 1
else:
logger.info(
f"Token transfer at transaction {transaction_data['txid']} rejected as a token with the name {parsed_data['tokenIdentification']} doesnt not exist")
engine = create_engine(f"sqlite:///system.db", echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(RejectedTransactionHistory(tokenIdentification=parsed_data['tokenIdentification'],
sourceFloAddress=inputadd, destFloAddress=outputlist[0],
transferAmount=parsed_data['tokenAmount'],
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Token transfer at transaction {transaction_data['txid']} rejected as a token with the name {parsed_data['tokenIdentification']} doesnt not exist",
transactionType=parsed_data['type'],
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
pushData_SSEapi(
f"Error | Token transfer at transaction {transaction_data['txid']} rejected as a token with the name {parsed_data['tokenIdentification']} doesnt not exist")
return 0
# todo Rule 46 - If the transfer type is smart contract, then call the function transferToken to do sanity checks & lock the balance
elif parsed_data['transferType'] == 'smartContract':
if os.path.isfile(f"./smartContracts/{parsed_data['contractName']}-{outputlist[0]}.db"):
# Check if the transaction hash already exists in the contract db (Safety check)
engine = create_engine(
'sqlite:///smartContracts/{}-{}.db'.format(parsed_data['contractName'], outputlist[0]), echo=True)
connection = engine.connect()
participantAdd_txhash = connection.execute(
'select participantAddress, transactionHash from contractparticipants').fetchall()
participantAdd_txhash_T = list(zip(*participantAdd_txhash))
if len(participantAdd_txhash) != 0 and transaction_data['txid'] in list(participantAdd_txhash_T[1]):
logger.warning(
f"Transaction {transaction_data['txid']} rejected as it already exists in the Smart Contract db. This is unusual, please check your code")
pushData_SSEapi(
f"Error | Transaction {transaction_data['txid']} rejected as it already exists in the Smart Contract db. This is unusual, please check your code")
return 0
# if contractAddress was passed, then check if it matches the output address of this contract
if 'contractAddress' in parsed_data:
if parsed_data['contractAddress'] != outputlist[0]:
logger.info(
f"Contract participation at transaction {transaction_data['txid']} rejected as contractAddress specified in flodata, {parsed_data['contractAddress']}, doesnt not match with transaction's output address {outputlist[0]}")
# Store transfer as part of RejectedContractTransactionHistory
engine = create_engine(
f"sqlite:///system.db",
echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(
RejectedContractTransactionHistory(transactionType='participation',
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Contract participation at transaction {transaction_data['txid']} rejected as contractAddress specified in flodata, {parsed_data['contractAddress']}, doesnt not match with transaction's output address {outputlist[0]}",
parsedFloData=json.dumps(parsed_data)))
session.commit()
session.close()
url = 'https://ranchimallflo.duckdns.org/'
headers = {'Accept': 'application/json', 'Content-Type': 'application/json'}
'''r = requests.post(url, json={
'message': f"Error | Contract participation at transaction {transaction_data['txid']} rejected as contractAddress specified in flodata, {parsed_data['contractAddress']}, doesnt not match with transaction's output address {outputlist[0]}"},
headers=headers)'''
# Pass information to SSE channel
pushData_SSEapi(
'Error| Mismatch in contract address specified in flodata and the output address of the transaction {}'.format(
transaction_data['txid']))
return 0
# check the status of the contract
engine = create_engine('sqlite:///system.db', echo=True)
connection = engine.connect()
contractStatus = connection.execute(
f"select status from activecontracts where contractName=='{parsed_data['contractName']}' and contractAddress='{outputlist[0]}'").fetchall()[
0][0]
connection.close()
contractList = []
if contractStatus == 'closed':
logger.info(
f"Transaction {transaction_data['txid']} closed as Smart contract {parsed_data['contractName']} at the {outputlist[0]} is closed")
# Store transfer as part of RejectedContractTransactionHistory
engine = create_engine(
f"sqlite:///system.db",
echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(
RejectedContractTransactionHistory(transactionType='participation',
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Transaction {transaction_data['txid']} closed as Smart contract {parsed_data['contractName']} at the {outputlist[0]} is closed",
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
url = 'https://ranchimallflo.duckdns.org/'
headers = {'Accept': 'application/json', 'Content-Type': 'application/json'}
'''r = requests.post(url, json={
'message': f"Error | Transaction {transaction_data['txid']} closed as Smart contract {parsed_data['contractName']} at the {outputlist[0]} is closed"},
headers=headers)'''
return 0
else:
engine = create_engine(
'sqlite:///smartContracts/{}-{}.db'.format(parsed_data['contractName'], outputlist[0]),
echo=True)
ContractBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
result = session.query(ContractStructure).filter_by(attribute='expiryTime').all()
session.close()
if result:
# now parse the expiry time in python
expirytime = result[0].value.strip()
expirytime_split = expirytime.split(' ')
parse_string = '{}/{}/{} {}'.format(expirytime_split[3], parsing.months[expirytime_split[1]],
expirytime_split[2], expirytime_split[4])
expirytime_object = parsing.arrow.get(parse_string, 'YYYY/M/D HH:mm:ss').replace(
tzinfo=expirytime_split[5][3:])
blocktime_object = parsing.arrow.get(transaction_data['blocktime']).to('Asia/Kolkata')
if blocktime_object > expirytime_object:
logger.info(
f"Transaction {transaction_data['txid']} rejected as Smart contract {parsed_data['contractName']}-{outputlist[0]} has expired and will not accept any user participation")
# Store transfer as part of RejectedContractTransactionHistory
engine = create_engine(
f"sqlite:///system.db",
echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(
RejectedContractTransactionHistory(transactionType='participation',
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Transaction {transaction_data['txid']} rejected as Smart contract {parsed_data['contractName']}-{outputlist[0]} has expired and will not accept any user participation",
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
pushData_SSEapi(
f"Error| Transaction {transaction_data['txid']} rejected as Smart contract {parsed_data['contractName']}-{outputlist[0]} has expired and will not accept any user participation")
return 0
# pull out the contract structure into a dictionary
engine = create_engine(
'sqlite:///smartContracts/{}-{}.db'.format(parsed_data['contractName'], outputlist[0]), echo=True)
connection = engine.connect()
attributevaluepair = connection.execute(
"select attribute, value from contractstructure where attribute != 'contractName' and attribute != 'flodata' and attribute != 'contractAddress'").fetchall()
contractStructure = {}
conditionDict = {}
counter = 0
for item in attributevaluepair:
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
# check if user choice has been passed, to the wrong contract type
if 'userChoice' in parsed_data and 'exitconditions' not in contractStructure:
logger.info(
f"Transaction {transaction_data['txid']} rejected as userChoice, {parsed_data['userChoice']}, has been passed to Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]} which doesn't accept any userChoice")
# Store transfer as part of RejectedContractTransactionHistory
engine = create_engine(
f"sqlite:///system.db",
echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(
RejectedContractTransactionHistory(transactionType='participation',
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Transaction {transaction_data['txid']} rejected as userChoice, {parsed_data['userChoice']}, has been passed to Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]} which doesn't accept any userChoice",
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
pushData_SSEapi(
f"Error | Transaction {transaction_data['txid']} rejected as userChoice, {parsed_data['userChoice']}, has been passed to Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]} which doesn't accept any userChoice")
return 0
# check if the right token is being sent for participation
if parsed_data['tokenIdentification'] != contractStructure['tokenIdentification']:
logger.info(
f"Transaction {transaction_data['txid']} rejected as the token being transferred, {parsed_data['tokenIdentidication'].upper()}, is not part of the structure of Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]}")
# Store transfer as part of RejectedContractTransactionHistory
engine = create_engine(
f"sqlite:///system.db",
echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(
RejectedContractTransactionHistory(transactionType='participation',
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Transaction {transaction_data['txid']} rejected as the token being transferred, {parsed_data['tokenIdentidication'].upper()}, is not part of the structure of Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]}",
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
pushData_SSEapi(
f"Error| Transaction {transaction_data['txid']} rejected as the token being transferred, {parsed_data['tokenIdentidication'].upper()}, is not part of the structure of Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]}")
return 0
# Check if the contract is of the type one-time-event
if contractStructure['contractType'] == 'one-time-event':
# Check if contractAmount is part of the contract structure, and enforce it if it is
if 'contractAmount' in contractStructure:
if float(contractStructure['contractAmount']) != float(parsed_data['tokenAmount']):
logger.info(
f"Transaction {transaction_data['txid']} rejected as contractAmount being transferred is not part of the structure of Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]}")
# Store transfer as part of RejectedContractTransactionHistory
engine = create_engine(
f"sqlite:///system.db",
echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(
RejectedContractTransactionHistory(transactionType='participation',
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Transaction {transaction_data['txid']} rejected as contractAmount being transferred is not part of the structure of Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]}",
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
pushData_SSEapi(
f"Error| Transaction {transaction_data['txid']} rejected as contractAmount being transferred is not part of the structure of Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]}")
return 0
partialTransferCounter = 0
# Check if maximum subscription amount has reached
if 'maximumsubscriptionamount' in contractStructure:
# now parse the expiry time in python
maximumsubscriptionamount = float(contractStructure['maximumsubscriptionamount'])
engine = create_engine(
'sqlite:///smartContracts/{}-{}.db'.format(parsed_data['contractName'], outputlist[0]),
echo=True)
ContractBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
amountDeposited = session.query(func.sum(ContractParticipants.tokenAmount)).all()[0][0]
session.close()
if amountDeposited is None:
amountDeposited = 0
if amountDeposited >= maximumsubscriptionamount:
logger.info(
f"Transaction {transaction_data['txid']} rejected as maximum subscription amount has been reached for the Smart contract named {parsed_data['contractName']} at the address {outputlist[0]}")
# Store transfer as part of RejectedContractTransactionHistory
engine = create_engine(
f"sqlite:///system.db",
echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(
RejectedContractTransactionHistory(transactionType='participation',
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Transaction {transaction_data['txid']} rejected as maximum subscription amount has been reached for the Smart contract named {parsed_data['contractName']} at the address {outputlist[0]}",
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
pushData_SSEapi(
f"Error | Transaction {transaction_data['txid']} rejected as maximum subscription amount has been reached for the Smart contract named {parsed_data['contractName']} at the address {outputlist[0]}")
return 0
elif ((float(amountDeposited) + float(parsed_data[
'tokenAmount'])) > maximumsubscriptionamount) and 'contractAmount' in contractStructure:
logger.info(
f"Transaction {transaction_data['txid']} rejected as the contractAmount surpasses the maximum subscription amount, {contractStructure['maximumsubscriptionamount']} {contractStructure['tokenIdentification'].upper()}, for the Smart contract named {parsed_data['contractName']} at the address {outputlist[0]}")
# Store transfer as part of RejectedContractTransactionHistory
engine = create_engine(
f"sqlite:///system.db",
echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(
RejectedContractTransactionHistory(transactionType='participation',
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Transaction {transaction_data['txid']} rejected as the contractAmount surpasses the maximum subscription amount, {contractStructure['maximumsubscriptionamount']} {contractStructure['tokenIdentification'].upper()}, for the Smart contract named {parsed_data['contractName']} at the address {outputlist[0]}",
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
pushData_SSEapi(
f"Error | Transaction {transaction_data['txid']} rejected as the contractAmount surpasses the maximum subscription amount, {contractStructure['maximumsubscriptionamount']} {contractStructure['tokenIdentification'].upper()}, for the Smart contract named {parsed_data['contractName']} at the address {outputlist[0]}")
return 0
else:
partialTransferCounter = 1
# Check if exitcondition exists as part of contractstructure and is given in right format
if 'exitconditions' in contractStructure:
# This means the contract has an external trigger, ie. trigger coming from the contract committee
exitconditionsList = []
for condition in contractStructure['exitconditions']:
exitconditionsList.append(contractStructure['exitconditions'][condition])
if parsed_data['userChoice'] in exitconditionsList:
if partialTransferCounter == 0:
# Check if the tokenAmount being transferred exists in the address & do the token transfer
returnval = transferToken(parsed_data['tokenIdentification'],
parsed_data['tokenAmount'], inputlist[0], outputlist[0],
transaction_data, parsed_data)
if returnval is not None:
# Store participant details in the smart contract's db
session.add(ContractParticipants(participantAddress=inputadd,
tokenAmount=parsed_data['tokenAmount'],
userChoice=parsed_data['userChoice'],
transactionHash=transaction_data['txid'],
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash']))
session.commit()
# Store transfer as part of ContractTransactionHistory
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(ContractTransactionHistory(transactionType='participation',
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=parsed_data['tokenAmount'],
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
# Store a mapping of participant address -> Contract participated in
engine = create_engine('sqlite:///system.db', echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
session.add(ContractAddressMapping(address=inputadd, addressType='participant',
tokenAmount=parsed_data['tokenAmount'],
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
transactionHash=transaction_data['txid'],
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash']))
session.commit()
# If this is the first interaction of the outputlist's address with the given token name, add it to token mapping
engine = create_engine('sqlite:///system.db', echo=True)
connection = engine.connect()
firstInteractionCheck = connection.execute(
f"select * from tokenAddressMapping where tokenAddress='{outputlist[0]}' and token='{parsed_data['tokenIdentification']}'").fetchall()
if len(firstInteractionCheck) == 0:
connection.execute(
f"INSERT INTO tokenAddressMapping (tokenAddress, token, transactionHash, blockNumber, blockHash) VALUES ('{outputlist[0]}', '{parsed_data['tokenIdentification']}', '{transaction_data['txid']}', '{transaction_data['blockheight']}', '{transaction_data['blockhash']}')")
connection.close()
updateLatestTransaction(transaction_data, parsed_data)
return 1
else:
logger.info("Something went wrong in the smartcontract token transfer method")
return 0
elif partialTransferCounter == 1:
# Transfer only part of the tokens users specified, till the time it reaches maximumamount
returnval = transferToken(parsed_data['tokenIdentification'],
maximumsubscriptionamount - amountDeposited,
inputlist[0], outputlist[0], transaction_data, parsed_data)
if returnval is not None:
# Store participant details in the smart contract's db
session.add(ContractParticipants(participantAddress=inputadd,
tokenAmount=maximumsubscriptionamount - amountDeposited,
userChoice=parsed_data['userChoice'],
transactionHash=transaction_data['txid'],
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash']))
session.commit()
session.close()
# Store a mapping of participant address -> Contract participated in
engine = create_engine('sqlite:///system.db', echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
session.add(ContractAddressMapping(address=inputadd, addressType='participant',
tokenAmount=maximumsubscriptionamount - amountDeposited,
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
transactionHash=transaction_data['txid'],
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash']))
session.commit()
session.close()
updateLatestTransaction(transaction_data, parsed_data)
return 1
else:
logger.info("Something went wrong in the smartcontract token transfer method")
return 0
else:
logger.info(
f"Transaction {transaction_data['txid']} rejected as wrong userchoice entered for the Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]}")
# Store transfer as part of RejectedContractTransactionHistory
engine = create_engine(
f"sqlite:///system.db",
echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(
RejectedContractTransactionHistory(transactionType='participation',
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Transaction {transaction_data['txid']} rejected as wrong userchoice entered for the Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]}",
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
pushData_SSEapi(
f"Error| Transaction {transaction_data['txid']} rejected as wrong userchoice entered for the Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]}")
return 0
elif 'payeeAddress' in contractStructure:
# this means the contract if of the type internal trigger
if partialTransferCounter == 0:
# Check if the tokenAmount being transferred exists in the address & do the token transfer
returnval = transferToken(parsed_data['tokenIdentification'],
parsed_data['tokenAmount'], inputlist[0], outputlist[0],
transaction_data, parsed_data)
if returnval is not None:
# Store participant details in the smart contract's db
session.add(ContractParticipants(participantAddress=inputadd,
tokenAmount=parsed_data['tokenAmount'],
userChoice='-',
transactionHash=transaction_data['txid'],
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash']))
session.commit()
# Store transfer as part of ContractTransactionHistory
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(ContractTransactionHistory(transactionType='participation',
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=parsed_data['tokenAmount'],
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
# Store a mapping of participant address -> Contract participated in
engine = create_engine('sqlite:///system.db', echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
session.add(ContractAddressMapping(address=inputadd, addressType='participant',
tokenAmount=parsed_data['tokenAmount'],
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
transactionHash=transaction_data['txid'],
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash']))
session.commit()
updateLatestTransaction(transaction_data, parsed_data)
return 1
else:
logger.info("Something went wrong in the smartcontract token transfer method")
return 0
elif partialTransferCounter == 1:
# Transfer only part of the tokens users specified, till the time it reaches maximumamount
returnval = transferToken(parsed_data['tokenIdentification'],
maximumsubscriptionamount - amountDeposited,
inputlist[0], outputlist[0], transaction_data, parsed_data)
if returnval is not None:
# Store participant details in the smart contract's db
session.add(ContractParticipants(participantAddress=inputadd,
tokenAmount=maximumsubscriptionamount - amountDeposited,
userChoice='-',
transactionHash=transaction_data['txid'],
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash']))
session.commit()
session.close()
# Store a mapping of participant address -> Contract participated in
engine = create_engine('sqlite:///system.db', echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
session.add(ContractAddressMapping(address=inputadd, addressType='participant',
tokenAmount=maximumsubscriptionamount - amountDeposited,
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
transactionHash=transaction_data['txid'],
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash']))
session.commit()
session.close()
updateLatestTransaction(transaction_data, parsed_data)
return 1
else:
logger.info("Something went wrong in the smartcontract token transfer method")
return 0
else:
logger.info(
f"Transaction {transaction_data['txid']} rejected as the participation doesn't belong to any valid contract type")
# Store transfer as part of RejectedContractTransactionHistory
engine = create_engine(
f"sqlite:///system.db",
echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(
RejectedContractTransactionHistory(transactionType='participation',
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Transaction {transaction_data['txid']} rejected as the participation doesn't belong to any valid contract type",
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
url = 'https://ranchimallflo.duckdns.org/'
headers = {'Accept': 'application/json', 'Content-Type': 'application/json'}
'''r = requests.post(url, json={
'message': f"Error | Transaction {transaction_data['txid']} rejected as the participation doesn't belong to any valid contract type"},
headers=headers)'''
return 0
else:
logger.info(
f"Transaction {transaction_data['txid']} rejected as a Smart Contract with the name {parsed_data['contractName']} at address {outputlist[0]} doesnt exist")
# Store transfer as part of RejectedContractTransactionHistory
engine = create_engine(
f"sqlite:///system.db",
echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(
RejectedContractTransactionHistory(transactionType='participation',
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Transaction {transaction_data['txid']} rejected as a Smart Contract with the name {parsed_data['contractName']} at address {outputlist[0]} doesnt exist",
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
url = 'https://ranchimallflo.duckdns.org/'
headers = {'Accept': 'application/json', 'Content-Type': 'application/json'}
'''r = requests.post(url, json={
'message': f"Error | Contract transaction {transaction_data['txid']} rejected as a smartcontract with same name {parsed_data['contractName']}-{parsed_data['contractAddress']} dosent exist "},
headers=headers)'''
return 0
# todo Rule 47 - If the parsed data type is token incorporation, then check if the name hasn't been taken already
# if it has been taken then reject the incorporation. Else incorporate it
elif parsed_data['type'] == 'tokenIncorporation':
if not os.path.isfile(f"./tokens/{parsed_data['tokenIdentification']}.db"):
engine = create_engine(f"sqlite:///tokens/{parsed_data['tokenIdentification']}.db", echo=True)
Base.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
session.add(ActiveTable(address=inputlist[0], parentid=0, transferBalance=parsed_data['tokenAmount']))
session.add(TransferLogs(sourceFloAddress=inputadd, destFloAddress=outputlist[0],
transferAmount=parsed_data['tokenAmount'], sourceId=0, destinationId=1,
blockNumber=transaction_data['blockheight'], time=transaction_data['blocktime'],
transactionHash=transaction_data['txid']))
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(TransactionHistory(sourceFloAddress=inputadd, destFloAddress=outputlist[0],
transferAmount=parsed_data['tokenAmount'],
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data), transactionType=parsed_data['type'],
parsedFloData=json.dumps(parsed_data)))
session.commit()
session.close()
# add it to token address to token mapping db table
engine = create_engine('sqlite:///system.db'.format(parsed_data['tokenIdentification']), echo=True)
connection = engine.connect()
connection.execute(
f"INSERT INTO tokenAddressMapping (tokenAddress, token, transactionHash, blockNumber, blockHash) VALUES ('{inputadd}', '{parsed_data['tokenIdentification']}', '{transaction_data['txid']}', '{transaction_data['blockheight']}', '{transaction_data['blockhash']}');")
connection.close()
updateLatestTransaction(transaction_data, parsed_data)
pushData_SSEapi(
f"Token | Succesfully incorporated token {parsed_data['tokenIdentification']} at transaction {transaction_data['txid']}")
return 1
else:
logger.info(
f"Transaction {transaction_data['txid']} rejected as a token with the name {parsed_data['tokenIdentification']} has already been incorporated")
engine = create_engine(f"sqlite:///system.db", echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(RejectedTransactionHistory(tokenIdentification=parsed_data['tokenIdentification'],
sourceFloAddress=inputadd, destFloAddress=outputlist[0],
transferAmount=parsed_data['tokenAmount'],
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Transaction {transaction_data['txid']} rejected as a token with the name {parsed_data['tokenIdentification']} has already been incorporated",
transactionType=parsed_data['type'],
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
pushData_SSEapi(
f"Error | Token incorporation rejected at transaction {transaction_data['txid']} as token {parsed_data['tokenIdentification']} already exists")
return 0
# todo Rule 48 - If the parsed data type if smart contract incorporation, then check if the name hasn't been taken already
# if it has been taken then reject the incorporation.
elif parsed_data['type'] == 'smartContractIncorporation':
if not os.path.isfile(f"./smartContracts/{parsed_data['contractName']}-{parsed_data['contractAddress']}.db"):
# todo Rule 49 - If the contract name hasn't been taken before, check if the contract type is an authorized type by the system
if parsed_data['contractType'] == 'one-time-event':
logger.info("Smart contract is of the type one-time-event")
# either userchoice or payeeAddress condition should be present. Check for it
if 'userchoices' not in parsed_data['contractConditions'] and 'payeeAddress' not in parsed_data[
'contractConditions']:
logger.info(
f"Either userchoice or payeeAddress should be part of the Contract conditions.\nSmart contract incorporation on transaction {transaction_data['txid']} rejected")
# Store transfer as part of RejectedContractTransactionHistory
engine = create_engine(
f"sqlite:///system.db",
echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(
RejectedContractTransactionHistory(transactionType='incorporation',
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Either userchoice or payeeAddress should be part of the Contract conditions.\nSmart contract incorporation on transaction {transaction_data['txid']} rejected",
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
return 0
# userchoice and payeeAddress conditions cannot come together. Check for it
if 'userchoices' in parsed_data['contractConditions'] and 'payeeAddress' in parsed_data[
'contractConditions']:
logger.info(
f"Both userchoice and payeeAddress provided as part of the Contract conditions.\nSmart contract incorporation on transaction {transaction_data['txid']} rejected")
# Store transfer as part of RejectedContractTransactionHistory
engine = create_engine(
f"sqlite:///system.db",
echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(
RejectedContractTransactionHistory(transactionType='incorporation',
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Both userchoice and payeeAddress provided as part of the Contract conditions.\nSmart contract incorporation on transaction {transaction_data['txid']} rejected",
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
return 0
# todo Rule 50 - Contract address mentioned in flodata field should be same as the receiver FLO address on the output side
# henceforth we will not consider any flo private key initiated comment as valid from this address
# Unlocking can only be done through smart contract system address
if parsed_data['contractAddress'] == inputadd:
dbName = '{}-{}'.format(parsed_data['contractName'], parsed_data['contractAddress'])
engine = create_engine('sqlite:///smartContracts/{}.db'.format(dbName), 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='expiryTime', index=0,
value=parsed_data['contractConditions']['expiryTime']))
if 'contractAmount' in parsed_data['contractConditions']:
session.add(
ContractStructure(attribute='contractAmount', index=0,
value=parsed_data['contractConditions']['contractAmount']))
if 'minimumsubscriptionamount' in parsed_data['contractConditions']:
session.add(
ContractStructure(attribute='minimumsubscriptionamount', index=0,
value=parsed_data['contractConditions']['minimumsubscriptionamount']))
if 'maximumsubscriptionamount' in parsed_data['contractConditions']:
session.add(
ContractStructure(attribute='maximumsubscriptionamount', index=0,
value=parsed_data['contractConditions']['maximumsubscriptionamount']))
if 'userchoices' in parsed_data['contractConditions']:
for key, value in parsed_data['contractConditions']['userchoices'].items():
session.add(ContractStructure(attribute='exitconditions', index=key, value=value))
if 'payeeAddress' in parsed_data['contractConditions']:
# in this case, expirydate( or maximumamount) is the trigger internally. Keep a track of expiry dates
session.add(
ContractStructure(attribute='payeeAddress', index=0,
value=parsed_data['contractConditions']['payeeAddress']))
session.commit()
# Store transfer as part of ContractTransactionHistory
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(ContractTransactionHistory(transactionType='incorporation', sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
# add Smart Contract name in token contract association
engine = create_engine(f"sqlite:///tokens/{parsed_data['tokenIdentification']}.db", echo=True)
Base.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
session.add(TokenContractAssociation(tokenIdentification=parsed_data['tokenIdentification'],
contractName=parsed_data['contractName'],
contractAddress=parsed_data['contractAddress'],
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
transactionType=parsed_data['type'],
parsedFloData=json.dumps(parsed_data)))
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'], status='active',
tokenIdentification=parsed_data['tokenIdentification'],
contractType=parsed_data['contractType'],
transactionHash=transaction_data['txid'],
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
incorporationDate=transaction_data['blocktime']))
session.commit()
session.add(ContractAddressMapping(address=inputadd, addressType='incorporation',
tokenAmount=None,
contractName=parsed_data['contractName'],
contractAddress=inputadd,
transactionHash=transaction_data['txid'],
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash']))
session.commit()
session.close()
updateLatestTransaction(transaction_data, parsed_data)
pushData_SSEapi('Contract | Contract incorporated at transaction {} with name {}-{}'.format(
transaction_data['txid'], parsed_data['contractName'], parsed_data['contractAddress']))
return 1
else:
logger.info(
f"Contract Incorporation on transaction {transaction_data['txid']} rejected as contract address in Flodata and input address are different")
# Store transfer as part of RejectedContractTransactionHistory
engine = create_engine(
f"sqlite:///system.db",
echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(
RejectedContractTransactionHistory(transactionType='incorporation',
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Contract Incorporation on transaction {transaction_data['txid']} rejected as contract address in flodata and input address are different",
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
pushData_SSEapi(
'Error | Contract Incorporation rejected as address in Flodata and input address are different at transaction {}'.format(
transaction_data['txid']))
return 0
else:
logger.info(
f"Transaction {transaction_data['txid']} rejected as a Smart Contract with the name {parsed_data['contractName']} at address {parsed_data['contractAddress']} already exists")
# Store transfer as part of RejectedContractTransactionHistory
engine = create_engine(f"sqlite:///system.db", echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(RejectedContractTransactionHistory(transactionType='incorporation',
contractName=parsed_data['contractName'],
contractAddress=outputlist[0], sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Transaction {transaction_data['txid']} rejected as a Smart Contract with the name {parsed_data['contractName']} at address {parsed_data['contractAddress']} already exists",
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
url = 'https://ranchimallflo.duckdns.org/'
headers = {'Accept': 'application/json', 'Content-Type': 'application/json'}
'''r = requests.post(url, json={
'message': 'Error | Contract Incorporation rejected as a smartcontract with same name {}-{} is active currentlyt at transaction {}'.format(parsed_data['contractName'], parsed_data['contractAddress'], transaction_data['txid'])}, headers=headers)
'''
return 0
elif parsed_data['type'] == 'smartContractPays':
logger.info(f"Transaction {transaction_data['txid']} is of the type smartContractPays")
# Check if input address is a committee address
if inputlist[0] in committeeAddressList:
# check if the contract exists
if os.path.isfile(f"./smartContracts/{parsed_data['contractName']}-{outputlist[0]}.db"):
# Check if the transaction hash already exists in the contract db (Safety check)
engine = create_engine(
'sqlite:///smartContracts/{}-{}.db'.format(parsed_data['contractName'], outputlist[0]), echo=True)
connection = engine.connect()
participantAdd_txhash = connection.execute(
f"select sourceFloAddress, transactionHash from contractTransactionHistory where transactionType != 'incorporation'").fetchall()
participantAdd_txhash_T = list(zip(*participantAdd_txhash))
if len(participantAdd_txhash) != 0 and transaction_data['txid'] in list(participantAdd_txhash_T[1]):
logger.warning(
f"Transaction {transaction_data['txid']} rejected as it already exists in the Smart Contract db. This is unusual, please check your code")
pushData_SSEapi(
f"Error | Transaction {transaction_data['txid']} rejected as it already exists in the Smart Contract db. This is unusual, please check your code")
return 0
# pull out the contract structure into a dictionary
engine = create_engine(
'sqlite:///smartContracts/{}-{}.db'.format(parsed_data['contractName'], outputlist[0]), echo=True)
connection = engine.connect()
attributevaluepair = connection.execute(
"select attribute, value from contractstructure where attribute != 'contractName' and attribute != 'flodata' and attribute != 'contractAddress'").fetchall()
contractStructure = {}
conditionDict = {}
counter = 0
for item in attributevaluepair:
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 contractAddress has been passed, check if output address is contract Incorporation address
if 'contractAddress' in contractStructure:
if outputlist[0] != contractStructure['contractAddress']:
logger.warning(
f"Transaction {transaction_data['txid']} rejected as Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]} hasn't expired yet")
# Store transfer as part of RejectedContractTransactionHistory
engine = create_engine(
f"sqlite:///system.db",
echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(
RejectedContractTransactionHistory(transactionType='trigger',
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Transaction {transaction_data['txid']} rejected as Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]} hasn't expired yet",
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
pushData_SSEapi(
f"Error | Transaction {transaction_data['txid']} rejected as Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]} hasn't expired yet")
return 0
# check the type of smart contract ie. external trigger or internal trigger
if 'payeeAddress' in contractStructure:
logger.warning(
f"Transaction {transaction_data['txid']} rejected as Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]} has an internal trigger")
# Store transfer as part of RejectedContractTransactionHistory
engine = create_engine(
f"sqlite:///system.db",
echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(
RejectedContractTransactionHistory(transactionType='trigger',
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Transaction {transaction_data['txid']} rejected as Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]} has an internal trigger",
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
pushData_SSEapi(
f"Error | Transaction {transaction_data['txid']} rejected as Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]} has an internal trigger")
return 0
# check the status of the contract
engine = create_engine('sqlite:///system.db', echo=True)
connection = engine.connect()
contractStatus = connection.execute(
f"select status from activecontracts where contractName=='{parsed_data['contractName']}' and contractAddress='{outputlist[0]}'").fetchall()[
0][0]
connection.close()
contractList = []
if contractStatus == 'closed':
logger.info(
f"Transaction {transaction_data['txid']} closed as Smart contract {parsed_data['contractName']} at the {outputlist[0]} is closed")
# Store transfer as part of RejectedContractTransactionHistory
engine = create_engine(
f"sqlite:///system.db",
echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(
RejectedContractTransactionHistory(transactionType='trigger',
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Transaction {transaction_data['txid']} closed as Smart contract {parsed_data['contractName']} at the {outputlist[0]} is closed",
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
url = 'https://ranchimallflo.duckdns.org/'
headers = {'Accept': 'application/json', 'Content-Type': 'application/json'}
'''r = requests.post(url, json={
'message': f"Error | Transaction {transaction_data['txid']} closed as Smart contract {parsed_data['contractName']} at the {outputlist[0]} is closed"},
headers=headers)'''
return 0
else:
engine = create_engine(
'sqlite:///smartContracts/{}-{}.db'.format(parsed_data['contractName'], outputlist[0]),
echo=True)
ContractBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
result = session.query(ContractStructure).filter_by(attribute='expiryTime').all()
session.close()
if result:
# now parse the expiry time in python
expirytime = result[0].value.strip()
expirytime_split = expirytime.split(' ')
parse_string = '{}/{}/{} {}'.format(expirytime_split[3],
parsing.months[expirytime_split[1]],
expirytime_split[2], expirytime_split[4])
expirytime_object = parsing.arrow.get(parse_string, 'YYYY/M/D HH:mm:ss').replace(
tzinfo=expirytime_split[5][3:])
blocktime_object = parsing.arrow.get(transaction_data['blocktime']).to('Asia/Kolkata')
if blocktime_object <= expirytime_object:
logger.info(
f"Transaction {transaction_data['txid']} rejected as Smart contract {parsed_data['contractName']}-{outputlist[0]} has not expired and will not trigger")
# Store transfer as part of RejectedContractTransactionHistory
engine = create_engine(
f"sqlite:///system.db",
echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(
RejectedContractTransactionHistory(transactionType='trigger',
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Transaction {transaction_data['txid']} rejected as Smart contract {parsed_data['contractName']}-{outputlist[0]} has not expired and will not trigger",
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
pushData_SSEapi(
f"Error| Transaction {transaction_data['txid']} rejected as Smart contract {parsed_data['contractName']}-{outputlist[0]} has not expired and will not trigger")
return 0
# check if the user choice passed is part of the contract structure
tempchoiceList = []
for item in contractStructure['exitconditions']:
tempchoiceList.append(contractStructure['exitconditions'][item])
if parsed_data['triggerCondition'] not in tempchoiceList:
logger.info(
f"Transaction {transaction_data['txid']} rejected as triggerCondition, {parsed_data['triggerCondition']}, has been passed to Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]} which doesn't accept any userChoice of the given name")
# Store transfer as part of RejectedContractTransactionHistory
engine = create_engine(
f"sqlite:///system.db",
echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(
RejectedContractTransactionHistory(transactionType='trigger',
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Transaction {transaction_data['txid']} rejected as triggerCondition, {parsed_data['triggerCondition']}, has been passed to Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]} which doesn't accept any userChoice of the given name",
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
pushData_SSEapi(
f"Error | Transaction {transaction_data['txid']} rejected as triggerCondition, {parsed_data['triggerCondition']}, has been passed to Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]} which doesn't accept any userChoice of the given name")
return 0
# check if minimumsubscriptionamount exists as part of the contract structure
if 'minimumsubscriptionamount' in contractStructure:
# if it has not been reached, close the contract and return money
minimumsubscriptionamount = float(contractStructure['minimumsubscriptionamount'])
engine = create_engine(
'sqlite:///smartContracts/{}-{}.db'.format(parsed_data['contractName'], outputlist[0]),
echo=True)
ContractBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
amountDeposited = session.query(func.sum(ContractParticipants.tokenAmount)).all()[0][0]
session.close()
if amountDeposited is None:
amountDeposited = 0
if amountDeposited < minimumsubscriptionamount:
# close the contract and return the money
logger.info(
'Minimum subscription amount hasn\'t been reached\n The token will be returned back')
# Initialize payback to contract participants
engine = create_engine(
'sqlite:///smartContracts/{}-{}.db'.format(parsed_data['contractName'], outputlist[0]),
echo=True)
connection = engine.connect()
contractParticipants = connection.execute(
'select participantAddress, tokenAmount, transactionHash from contractparticipants').fetchall()[
0][0]
for participant in contractParticipants:
tokenIdentification = connection.execute(
'select * from contractstructure where attribute="tokenIdentification"').fetchall()[0][
0]
contractAddress = connection.execute(
'select * from contractstructure where attribute="contractAddress"').fetchall()[0][0]
returnval = transferToken(tokenIdentification, participant[1], contractAddress,
participant[0], transaction_data, parsed_data)
if returnval is None:
logger.info(
"CRITICAL ERROR | Something went wrong in the token transfer method while doing local Smart Contract Trigger")
return 0
connection.execute(
'update contractparticipants set winningAmount="{}" where participantAddress="{}" and transactionHash="{}"'.format(
(participant[1], participant[0], participant[4])))
# add transaction to ContractTransactionHistory
blockchainReference = neturl + 'tx/' + transaction_data['txid']
engine = create_engine(
'sqlite:///smartContracts/{}-{}.db'.format(parsed_data['contractName'], outputlist[0]),
echo=True)
ContractBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
session.add(ContractTransactionHistory(transactionType='trigger',
transactionSubType='minimumsubscriptionamount-payback',
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
engine = create_engine('sqlite:///system.db', echo=True)
connection = engine.connect()
connection.execute(
'update activecontracts set status="closed" where contractName="{}" and contractAddress="{}"'.format(
parsed_data['contractName'], outputlist[0]))
connection.execute(
'update activecontracts set status="{}" where contractName="{}" and contractAddress="{}"'.format(
transaction_data['blocktime'],
parsed_data['contractName'], outputlist[0]))
connection.close()
updateLatestTransaction(transaction_data, parsed_data)
pushData_SSEapi(
'Trigger | Minimum subscription amount not reached at contract {}-{} at transaction {}. Tokens will be refunded'.format(
parsed_data['contractName'], outputlist[0], transaction_data['txid']))
return 1
# Trigger the contract
engine = create_engine(
'sqlite:///smartContracts/{}-{}.db'.format(parsed_data['contractName'], outputlist[0]),
echo=True)
connection = engine.connect()
contractWinners = connection.execute(
'select * from contractparticipants where userChoice="{}"'.format(
parsed_data['triggerCondition'])).fetchall()
tokenSum = connection.execute('select sum(tokenAmount) from contractparticipants').fetchall()[0][0]
winnerSum = connection.execute(
'select sum(tokenAmount) from contractparticipants where userChoice="{}"'.format(
parsed_data['triggerCondition'])).fetchall()[0][0]
tokenIdentification = connection.execute(
'select value from contractstructure where attribute="tokenIdentification"').fetchall()[0][0]
for winner in contractWinners:
winnerAmount = "%.8f" % ((winner[2] / winnerSum) * tokenSum)
returnval = transferToken(tokenIdentification, winnerAmount,
outputlist[0], winner[1], transaction_data, parsed_data)
if returnval is None:
logger.critical(
"Something went wrong in the token transfer method while doing local Smart Contract Trigger")
return 0
connection.execute(
f"update contractparticipants set winningAmount='{winnerAmount}' where participantAddress='{winner[1]}' and transactionHash='{winner[4]}'")
# add transaction to ContractTransactionHistory
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(ContractTransactionHistory(transactionType='trigger',
transactionSubType='committee',
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
engine = create_engine('sqlite:///system.db', echo=True)
connection = engine.connect()
connection.execute(
'update activecontracts set status="closed" where contractName="{}" and contractAddress="{}"'.format(
parsed_data['contractName'], outputlist[0]))
connection.execute(
'update activecontracts set closeDate="{}" where contractName="{}" and contractAddress="{}"'.format(
transaction_data['blocktime'],
parsed_data['contractName'], outputlist[0]))
connection.close()
updateLatestTransaction(transaction_data, parsed_data)
pushData_SSEapi(
'Trigger | Contract triggered of the name {}-{} is active currently at transaction {}'.format(
parsed_data['contractName'], outputlist[0], transaction_data['txid']))
return 1
else:
logger.info(
f"Transaction {transaction_data['txid']} rejected as Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]} doesn't exist")
# Store transfer as part of RejectedContractTransactionHistory
engine = create_engine(
f"sqlite:///system.db",
echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(
RejectedContractTransactionHistory(transactionType='trigger',
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Transaction {transaction_data['txid']} rejected as Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]} doesn't exist",
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
pushData_SSEapi(
f"Error | Transaction {transaction_data['txid']} rejected as Smart Contract named {parsed_data['contractName']} at the address {outputlist[0]} doesn't exist")
return 0
else:
logger.info(
f"Transaction {transaction_data['txid']} rejected as input address, {inputlist[0]}, is not part of the committee address list")
# Store transfer as part of RejectedContractTransactionHistory
engine = create_engine(
f"sqlite:///system.db",
echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
blockchainReference = neturl + 'tx/' + transaction_data['txid']
session.add(RejectedContractTransactionHistory(transactionType='trigger',
contractName=parsed_data['contractName'],
contractAddress=outputlist[0],
sourceFloAddress=inputadd,
destFloAddress=outputlist[0],
transferAmount=None,
blockNumber=transaction_data['blockheight'],
blockHash=transaction_data['blockhash'],
time=transaction_data['blocktime'],
transactionHash=transaction_data['txid'],
blockchainReference=blockchainReference,
jsonData=json.dumps(transaction_data),
rejectComment=f"Transaction {transaction_data['txid']} rejected as input address, {inputlist[0]}, is not part of the committee address list",
parsedFloData=json.dumps(parsed_data)
))
session.commit()
session.close()
pushData_SSEapi(
f"Transaction {transaction_data['txid']} rejected as input address, {inputlist[0]}, is not part of the committee address list")
return 0
def scanBlockchain():
# Read start block no
engine = create_engine('sqlite:///system.db', echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
startblock = int(session.query(SystemData).filter_by(attribute='lastblockscanned').all()[0].value) + 1
session.commit()
session.close()
# todo Rule 6 - Find current block height
# Rule 7 - Start analysing the block contents from starting block to current height
# Find current block height
current_index = -1
while(current_index == -1):
response = multiRequest('blocks?limit=1', config['DEFAULT']['NET'])
try:
current_index = response['blocks'][0]['height']
except:
logger.info('Latest block count response from multiRequest() is not in the right format. Displaying the data received in the log below')
logger.info(response)
logger.info('Program will wait for 1 seconds and try to reconnect')
time.sleep(1)
else:
logger.info("Current block height is %s" % str(current_index))
break
for blockindex in range(startblock, current_index):
if blockindex < 4365011:
if blockindex in goodblockset:
processBlock(blockindex)
else:
logger.info(f"Skipping block {blockindex}")
else:
processBlock(blockindex)
# At this point the script has updated to the latest block
# Now we connect to flosight's websocket API to get information about the latest blocks
# Configuration of required variables
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s:%(name)s:%(message)s')
file_handler = logging.FileHandler('tracking.log')
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(formatter)
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(formatter)
logger.addHandler(file_handler)
logger.addHandler(stream_handler)
# todo Rule 1 - Read command line arguments to reset the databases as blank
# Rule 2 - Read config to set testnet/mainnet
# Rule 3 - Set flo blockexplorer location depending on testnet or mainnet
# Rule 4 - Set the local flo-cli path depending on testnet or mainnet
# Rule 5 - Set the block number to scan from
# 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()
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)
# Read configuration
config = configparser.ConfigParser()
config.read('config.ini')
# Assignment the flo-cli command
if config['DEFAULT']['NET'] == 'mainnet':
neturl = 'http://0.0.0.0:9495/'
localapi = config['DEFAULT']['FLO_CLI_PATH']
elif config['DEFAULT']['NET'] == 'testnet':
neturl = 'https://testnet-flosight.duckdns.org/'
localapi = '{} --testnet'.format(config['DEFAULT']['FLO_CLI_PATH'])
else:
logger.error(
"NET parameter in config.ini invalid. Options are either 'mainnet' or 'testnet'. Script is exiting now")
# Delete database and smartcontract directory if reset is set to 1
if args.reset == 1:
logger.info("Resetting the database. ")
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)
dirpath = os.path.join(apppath, 'latestCache.db')
if os.path.exists(dirpath):
os.remove(dirpath)
# Read start block no
startblock = int(config['DEFAULT']['START_BLOCK'])
engine = create_engine('sqlite:///system.db', echo=True)
SystemBase.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
session.add(SystemData(attribute='lastblockscanned', value=startblock - 1))
session.commit()
session.close()
# Initialize latest cache DB
engine = create_engine('sqlite:///latestCache.db', echo=True)
LatestCacheBase.metadata.create_all(bind=engine)
session.commit()
session.close()
def switchNeturl(currentneturl):
mainserverlist = ['http://0.0.0.0:9495/']
neturlindex = mainserverlist.index(currentneturl)
if neturlindex+1 >= len(mainserverlist):
return mainserverlist[neturlindex+1 - len(mainserverlist)]
else:
return mainserverlist[neturlindex+1]
def reconnectWebsocket(socket_variable):
# Switch a to different flosight
# neturl = switchNeturl(neturl)
# Connect to Flosight websocket to get data on new incoming blocks
i=0
newurl = neturl
while(not socket_variable.connected):
logger.info(f"While loop {i}")
logger.info(f"Sleeping for 3 seconds before attempting reconnect to {newurl}")
time.sleep(3)
try:
""" neturl = temp
logger.info(f"neturl: {neturl}") """
scanBlockchain()
logger.info(f"Websocket endpoint which is being connected to {newurl}socket.io/socket.io.js")
socket_variable.connect(f"{newurl}socket.io/socket.io.js")
i=i+1
except:
logger.info(f"disconnect block: Failed reconnect attempt to {newurl}")
newurl = switchNeturl(newurl)
i=i+1
# MAIN LOGIC
# scan from the latest block saved locally to latest network block
scanBlockchain()
# At this point the script has updated to the latest block
# Now we connect to flosight's websocket API to get information about the latest blocks
# Neturl is the URL for Flosight API whose websocket endpoint is being connected to
neturl = 'https://flosight.duckdns.org/'
sio = socketio.Client()
# Connect to a websocket endpoint and wait for further events
reconnectWebsocket(sio)
#sio.connect(f"{neturl}socket.io/socket.io.js")
@sio.on('connect')
def token_connect():
current_time=datetime.now().strftime('%H:%M:%S')
logger.info(f"Token Tracker has connected to websocket endpoint. Time : {current_time}")
sio.emit('subscribe', 'inv')
@sio.on('disconnect')
def token_disconnect():
current_time = datetime.now().strftime('%H:%M:%S')
logger.info(f"disconnect block: Token Tracker disconnected from websocket endpoint. Time : {current_time}")
logger.info('disconnect block: Triggering client disconnect')
sio.disconnect()
logger.info('disconnect block: Finished triggering client disconnect')
reconnectWebsocket(sio)
@sio.on('connect_error')
def connect_error():
current_time = datetime.now().strftime('%H:%M:%S')
logger.info(f"connection error block: Token Tracker disconnected from websocket endpoint. Time : {current_time}")
logger.info('connection error block: Triggering client disconnect')
sio.disconnect()
logger.info('connection error block: Finished triggering client disconnect')
reconnectWebsocket(sio)
@sio.on('block')
def on_block(data):
logger.info('New block received')
logger.info(str(data))
processApiBlock(data)