From cbd6408609e0cbcac0f328eafc0449c0e3f9e0a4 Mon Sep 17 00:00:00 2001 From: Pranay Valson Date: Mon, 25 Apr 2022 15:25:10 -0700 Subject: [PATCH 1/7] amirs finalize scripts Signed-off-by: Pranay Valson --- abi/ProofChainContractABI | 1023 +++++++++++++++++++++++++++++++++++++ contract.py | 127 +++++ dbmanager.py | 145 ++++++ finalizationrequest.py | 72 +++ finalizer.py | 66 +++ logformat.py | 43 ++ main.py | 49 ++ requirements.txt | 43 ++ 8 files changed, 1568 insertions(+) create mode 100644 abi/ProofChainContractABI create mode 100644 contract.py create mode 100644 dbmanager.py create mode 100644 finalizationrequest.py create mode 100644 finalizer.py create mode 100644 logformat.py create mode 100644 main.py create mode 100644 requirements.txt diff --git a/abi/ProofChainContractABI b/abi/ProofChainContractABI new file mode 100644 index 0000000..4a9df1d --- /dev/null +++ b/abi/ProofChainContractABI @@ -0,0 +1,1023 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "chainId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "newMaxNumberOfHashesPer24H", + "type": "uint64" + } + ], + "name": "BlockSpecimenMaxNumberOfHashesPer24HChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint128", + "name": "newBlockSpecimenMinSubmissionsRequired", + "type": "uint128" + } + ], + "name": "BlockSpecimenMinSubmissionRequiredChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "chainId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "blockHeight", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "blockHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "specimenHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "string", + "name": "storageURL", + "type": "string" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "submittedStake", + "type": "uint128" + } + ], + "name": "BlockSpecimenProductionProofSubmitted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "chainId", + "type": "uint64" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "blockHeight", + "type": "uint64" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "blockhash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "specimenhash", + "type": "bytes32" + } + ], + "name": "BlockSpecimenRewardAwarded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint128", + "name": "newBlockSpecimenRewardAllocation", + "type": "uint128" + } + ], + "name": "BlockSpecimenRewardChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "maxSubmissions", + "type": "uint256" + } + ], + "name": "MaxSubmissionsPerBlockHeightChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint128", + "name": "newStakeRequirement", + "type": "uint128" + } + ], + "name": "MinimumRequiredStakeChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "chainId", + "type": "uint64" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "nthBlock", + "type": "uint64" + } + ], + "name": "NthBlockChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "numberOfBlocks", + "type": "uint64" + } + ], + "name": "NumberOfBlocksPer24HChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "validatorId", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "OperatorAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "OperatorDisabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "OperatorEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "OperatorRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "chainId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "blockHeight", + "type": "uint64" + } + ], + "name": "QuorumNotReached", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "chainId", + "type": "uint64" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "blockHeight", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "deadline", + "type": "uint64" + } + ], + "name": "SessionStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "newSessionDuration", + "type": "uint64" + } + ], + "name": "SpecimenSessionDurationChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "minSubmissions", + "type": "uint64" + } + ], + "name": "SpecimenSessionMinSubmissionChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "newQuorumThreshold", + "type": "uint256" + } + ], + "name": "SpecimenSessionQuorumChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newInterfaceAddress", + "type": "address" + } + ], + "name": "StakingInterfaceChanged", + "type": "event" + }, + { + "inputs": [], + "name": "AUDITOR_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "BLOCK_SPECIMEN_PRODUCER_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "GOVERNANCE_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "auditor", + "type": "address" + } + ], + "name": "addAuditor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "uint128", + "name": "validatorId", + "type": "uint128" + } + ], + "name": "addBSPOperator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "governer", + "type": "address" + } + ], + "name": "addGoverner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "uint128", + "name": "commissionRate", + "type": "uint128" + } + ], + "name": "addValidator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "chainId", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "blockHeight", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "blockHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "definitiveSpecimenHash", + "type": "bytes32" + } + ], + "name": "arbitrateBlockSpecimenSession", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "disableBSPOperator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint128", + "name": "validatorId", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "disableValidator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "enableBSPOperator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "chainId", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "blockHeight", + "type": "uint64" + } + ], + "name": "finalizeAndRewardSpecimenSession", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getAllOperators", + "outputs": [ + { + "internalType": "address[]", + "name": "_bsps", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "__governers", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "__auditors", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getBSPRoleData", + "outputs": [ + { + "internalType": "uint128", + "name": "requiredStake", + "type": "uint128" + }, + { + "internalType": "address[]", + "name": "activeMembers", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getMetadata", + "outputs": [ + { + "internalType": "address", + "name": "stakingInterface", + "type": "address" + }, + { + "internalType": "uint64", + "name": "numberOfBlocksPer24H", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "maxSubmissionsPerBlockHeight", + "type": "uint64" + }, + { + "internalType": "uint128", + "name": "blockSpecimenRewardAllocation", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "blockSpecimenSessionDuration", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "minSubmissionsRequired", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "blockSpecimenQuorum", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint128", + "name": "validatorId", + "type": "uint128" + } + ], + "name": "getOperators", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "initialOwner", + "type": "address" + }, + { + "internalType": "address", + "name": "stakingContract", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "name": "maxNumberOfHashesPer24H", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "name": "nthBlock", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "operatorRoles", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "auditor", + "type": "address" + } + ], + "name": "removeAuditor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "removeBSPOperator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "governer", + "type": "address" + } + ], + "name": "removeGoverner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint128", + "name": "newStakeAmount", + "type": "uint128" + } + ], + "name": "setBSPRequiredStake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "minSubmissions", + "type": "uint64" + } + ], + "name": "setBlockSpecimenMinSubmissionRequired", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint128", + "name": "newBlockSpecimenReward", + "type": "uint128" + } + ], + "name": "setBlockSpecimenReward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "newSessionDuration", + "type": "uint64" + } + ], + "name": "setBlockSpecimenSessionDuration", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "chainId", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "maxSubmissions", + "type": "uint64" + } + ], + "name": "setMaxNumberOfHashesPer24H", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "maxSubmissions", + "type": "uint64" + } + ], + "name": "setMaxSubmissionsPerBlockHeight", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "minSubmissions", + "type": "uint64" + } + ], + "name": "setMinSubmissionsRequired", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "chainId", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "n", + "type": "uint64" + } + ], + "name": "setNthBlock", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "numberOfBlocks", + "type": "uint64" + } + ], + "name": "setNumberOfBlocksPer24H", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "quorum", + "type": "uint256" + } + ], + "name": "setQuorumThreshold", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "stakingContractAddress", + "type": "address" + } + ], + "name": "setStakingInterface", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "chainId", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "blockHeight", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "blockHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "specimenHash", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "storageURL", + "type": "string" + } + ], + "name": "submitBlockSpecimenProof", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "validatorIDs", + "outputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/contract.py b/contract.py new file mode 100644 index 0000000..1727995 --- /dev/null +++ b/contract.py @@ -0,0 +1,127 @@ +import asyncio +import logging +import pprint +import statistics +import traceback + +from web3.exceptions import TimeExhausted +from web3.middleware import geth_poa_middleware +import web3.auto +from web3 import Web3 + +import logformat + + +class ProofChainContract: + def __init__(self, rpc_endpoint, finalizer_address, finalizer_prvkey, proofchain_address): + self.counter = 0 + self.finalizer_address = finalizer_address + self.finalizer_prvkey = finalizer_prvkey + self.provider: Web3.HTTPProvider = Web3.HTTPProvider(rpc_endpoint) + self.w3: Web3 = Web3(self.provider) + self.gas = 2100000 + self.gasPrice = web3.auto.w3.toWei('1', 'gwei') + self.w3.middleware_onion.inject(geth_poa_middleware, layer=0) + self.contractAddress: str = proofchain_address # "0x8243AF52B91649547DC80814670Dd1683F360E4c" + with open("abi/ProofChainContractABI", "r") as f: + self.contract = self.w3.eth.contract( + address=self.contractAddress, + abi=f.read() + ) + + self.logger = logformat.LogFormat.init_logger("Contract") + + # asynchronous defined function to loop + # this loop sets up an event filter and is looking for new entires for the "PairCreated" event + # this loop runs on a poll interval + # async def _log_loop(self, event_filter, poll_interval, cb): + # while True: + # try: + # for PairCreated in event_filter.get_new_entries(): + # cb(PairCreated) + # await asyncio.sleep(poll_interval) + # except Exception as e: + # print(e) + # self.subscribe_on_event(cb) + + def send_finalize(self, chainId, blockHeight, wait_for_confirming=None): + self.nonce = self.w3.eth.get_transaction_count(self.finalizer_address) + transaction = self.contract.functions.finalizeAndRewardSpecimenSession( + chainId, + blockHeight).buildTransaction({ + 'gas': self.gas, + 'gasPrice': self.gasPrice, + 'from': self.finalizer_address, + 'nonce': self.nonce + }) + signed_txn = self.w3.eth.account.signTransaction(transaction, private_key=self.finalizer_prvkey) + tx_hash = self.w3.eth.sendRawTransaction(signed_txn.rawTransaction) + self.logger.info("sent a transaction for {} {} nonce: {} balance: {}" + .format(chainId, blockHeight, self.nonce, self.w3.eth.get_balance(self.finalizer_address))) + if wait_for_confirming is not None: + try: + self.w3.eth.wait_for_transaction_receipt(tx_hash, wait_for_confirming) + receipt = self.w3.eth.get_transaction_receipt(tx_hash) + + if receipt['status'] == 0: + self.logger.warning( + "transaction failed with blockNumber {} comGasUsed {} gasUsed {} status {} trxHash {} trxIndex {} " + .format(receipt['blockNumber'], + receipt['cumulativeGasUsed'], + receipt['gasUsed'], + receipt['status'], + receipt['transactionHash'].hex(), + receipt['transactionIndex'], + ) + ) + else: + self.logger.info( + "transaction mined with blockNumber {} comGasUsed {} gasUsed {} status {} trxHash {} trxIndex {} " + .format(receipt['blockNumber'], + receipt['cumulativeGasUsed'], + receipt['gasUsed'], + receipt['status'], + receipt['transactionHash'].hex(), + receipt['transactionIndex'], + ) + ) + except TimeExhausted as ex: + # TODO what can we do? + self.increase_gas_price(self.gas) + self.logger.critical(''.join( + traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__))) + + def block_number(self): + return self.w3.eth.get_block('latest').number + + # def subscribe_on_event(self, cb, from_block=1): + # event_filter = self.contract.events.SessionStarted.createFilter(fromBlock=from_block) + # loop = asyncio.get_event_loop() + # try: + # loop.run_until_complete( + # asyncio.gather( + # self._log_loop(event_filter, 2, cb))) + # # log_loop(block_filter, 2), + # # log_loop(tx_filter, 2))) + # finally: + # # close loop to free up system resources + # loop.close() + def estimate_gas_price(self): + pending_transactions = self.provider.make_request("parity_futureTransactions", []) + gas_prices = [] + gases = [] + print(pending_transactions) + for tx in pending_transactions["result"[:10]]: + gas_prices.append(int((tx["gasPrice"]),16)) + gases.append(int((tx["gas"]),16)) + print("Average:") + print("-"*80) + print("gasPrice: ", statistics.mean(gas_prices)) + print(" ") + print("Median:") + print("-"*80) + print("gasPrice: ", statistics.median(gas_prices)) + + def increase_gas_price(self, gasPrice): + # try to replace the unmined trx next time in emergency cases + self.gasPrice = gasPrice * 1.15 diff --git a/dbmanager.py b/dbmanager.py new file mode 100644 index 0000000..184a368 --- /dev/null +++ b/dbmanager.py @@ -0,0 +1,145 @@ +import logging +import threading +import time +import traceback +import logformat + +import psycopg2 + +from finalizationrequest import FinalizationRequest + + +class DBManager(threading.Thread): + caught_up: bool = False + last_block_id: int + starting_point: int + logger: logging.Logger + + def __init__(self, user, password, database, host, staring_point): + super().__init__() + self.host = host + self.database = database + self.password = password + self.user = user + + self.logger = logformat.LogFormat.init_logger("DB") + DBManager.starting_point = staring_point + + def _process_outputs(self, outputs): + fl = 0 + c = 0 + for output in outputs: + block_id = output[1] + blockHeight = output[4] + chainId = output[3] + deadline = output[5] + finalizationHash = output[6] + fr = FinalizationRequest(chainId=chainId, blockHeight=blockHeight, deadline=deadline, block_id=block_id) + + if finalizationHash is None: + if not fr.waiting_for_confirm() and not fr.waiting_for_finalize(): + fr.finalize_later() + fl += 1 + else: + if fr.waiting_for_confirm(): + fr.confirm_request() + DBManager.__update_cursor(fr.session_started_block_id) + c += 1 + if fl > 0: + self.logger.info("{} entries were added for finalizing.".format(fl)) + if c > 0: + self.logger.info("{} entries were confirmed.".format(c)) + + def __connect(self): + return psycopg2.connect( + host=self.host, + database=self.database, + user=self.user, + password=self.password + ) + + def __main_loop(self): + conn = None + try: + self.logger.info('Connecting to the database...') + conn = self.__connect() + cur = conn.cursor() + if not DBManager.caught_up: + self.logger.info("Started catching up with db.") + # we are catching up. So we only need to grab what we need to attempt for finalizing + cur.execute( + 'select * from reports.proof_chain_dbt where finalization_hash IS NULL and block_id >= %s ORDER BY block_id;', + (DBManager.last_block_id,)) + self.logger.info("Processing {} records from db.".format(cur.rowcount)) + outputs = [cur.fetchone()] + if outputs[0] is not None: + self._process_outputs(outputs) + while len(outputs) != 0: + outputs = cur.fetchmany(1000) + self._process_outputs(outputs) + time.sleep(5) + self.caught_up = True + self.logger.info("Caught up with db.") + while True: + self.logger.info("attempting to get more data from {}".format(DBManager.last_block_id)) + # we need everything after last max block number + cur.execute( + 'select * from reports.proof_chain_dbt where block_id >= %s ORDER BY block_id;', + (DBManager.last_block_id,)) + outputs = cur.fetchall() + self._process_outputs(outputs) + conn.close() + self.logger.info('Database connection closed.') + time.sleep(40) + conn = self.__connect() + cur = conn.cursor() + + except (Exception, psycopg2.DatabaseError) as ex: + self.logger.warning(''.join(traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__))) + if conn is not None: + conn.close() + finally: + if conn is not None: + conn.close() + self.logger.info('Database connection closed.') + + def run(self): + # we need to avoid recursion in order to avoid stack depth exceeded exception + if DBManager.starting_point != -1: + DBManager.last_block_id = DBManager.starting_point + else: + self.__fetch_last_block() + while True: + try: + self.__main_loop() + time.sleep(60) + except (Exception, psycopg2.DatabaseError) as ex: + self.logger.warning(''.join(traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__))) + # this should never happen + self.__main_loop() + + def __fetch_last_block(self): + try: + conn = self.__connect() + cur = conn.cursor() + cur.execute('select min(block_id) from reports.proof_chain_dbt where finalization_hash is null') + block_id = cur.fetchone() + if block_id is not None: + DBManager.last_block_id = block_id[0] + self.logger.info("starting from block id " + str(DBManager.last_block_id)) + else: + DBManager.last_block_id = 1 + except Exception as ex: + self.logger.warning(''.join(traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__))) + + @staticmethod + def __update_cursor(block_id): + for fr in FinalizationRequest.get_requests_to_be_confirmed(): + if fr.session_started_block_id < block_id: + return + for fr in FinalizationRequest.get_requests_to_be_finalized(): + if fr.session_started_block_id < block_id: + return + with open("last_block_id", "w") as f: + f.write(str(block_id)) + DBManager.last_block_id = block_id diff --git a/finalizationrequest.py b/finalizationrequest.py new file mode 100644 index 0000000..a11cbef --- /dev/null +++ b/finalizationrequest.py @@ -0,0 +1,72 @@ +import time + + +class FinalizationRequest: + requests_to_be_finalized = dict() + requests_to_be_confirmed = dict() + + @staticmethod + def get_requests_to_be_finalized() -> []: + values = list(FinalizationRequest.requests_to_be_finalized.values()) + frs = [] + for v in values: + for fr in v.values(): + frs.append(fr) + return frs + + @staticmethod + def get_requests_to_be_confirmed() -> []: + values = list(FinalizationRequest.requests_to_be_confirmed.values()) + frs = [] + for v in values: + for fr in v.values(): + frs.append(fr) + return frs + + def __init__(self, chainId, blockHeight, deadline, block_id): + self.deadline = deadline + self.chainId = chainId + self.blockHeight = blockHeight + self.session_started_block_id = block_id + self.finalized_time = None + + def update_block_id(self, bid): + self.block_id = bid + + def confirm_request(self): + if self.chainId in FinalizationRequest.requests_to_be_confirmed.keys(): + FinalizationRequest.requests_to_be_confirmed[self.chainId].pop(self.blockHeight, None) + + def finalize_request(self): + if self.chainId in FinalizationRequest.requests_to_be_finalized.keys(): + FinalizationRequest.requests_to_be_finalized[self.chainId].pop(self.blockHeight, None) + + self.finalized_time = time.time() + + def finalize_later(self): + if self.chainId in FinalizationRequest.requests_to_be_finalized.keys(): + FinalizationRequest.requests_to_be_finalized[self.chainId][self.blockHeight] = self + else: + d = dict() + d[self.blockHeight] = self + FinalizationRequest.requests_to_be_finalized[self.chainId] = d + + def confirm_later(self): + if self.chainId in FinalizationRequest.requests_to_be_confirmed.keys(): + FinalizationRequest.requests_to_be_confirmed[self.chainId][self.blockHeight] = self + else: + d = dict() + d[self.blockHeight] = self + FinalizationRequest.requests_to_be_confirmed[self.chainId] = d + + def waiting_for_confirm(self): + if self.chainId in FinalizationRequest.requests_to_be_confirmed.keys(): + if self.blockHeight in FinalizationRequest.requests_to_be_confirmed[self.chainId].keys(): + return True + return False + + def waiting_for_finalize(self): + if self.chainId in FinalizationRequest.requests_to_be_finalized.keys(): + if self.blockHeight in FinalizationRequest.requests_to_be_finalized[self.chainId].keys(): + return True + return False diff --git a/finalizer.py b/finalizer.py new file mode 100644 index 0000000..f1beedb --- /dev/null +++ b/finalizer.py @@ -0,0 +1,66 @@ +import threading +import time +import logging +import traceback + +import logformat +from contract import ProofChainContract +from finalizationrequest import FinalizationRequest + + +class Finalizer(threading.Thread): + def __init__(self, cn: ProofChainContract): + super().__init__() + self.contract = cn + self.logger = logformat.LogFormat.init_logger("Finalizer") + + def __main_loop(self): + # self.contract.estimate_gas_price() + self.refinalize_rejected_requests() + frs = FinalizationRequest.get_requests_to_be_finalized() + # while len(frs) > 0: + ready_to_finalize = [] + bn = self.contract.block_number() + for fr in frs: + if fr.deadline < bn: + ready_to_finalize.append(fr) + if len(ready_to_finalize) == 0: + return + self.logger.info("Finalizing {} sessions.".format(len(ready_to_finalize))) + for fr in ready_to_finalize: + self._attempt_to_finalize(fr) + self.logger.info("{} sessions have been finalized.".format(len(ready_to_finalize))) + + def run(self) -> None: + # we need to avoid recursion in order to avoid stack depth exceeded exception + while True: + time.sleep(12) + try: + self.__main_loop() + except: + # this should never happen + self.__main_loop() + + def refinalize_rejected_requests(self): + to_send = [] + for fr in FinalizationRequest.get_requests_to_be_confirmed(): + if fr.finalized_time < time.time() - 200: + to_send.append(fr) + if len(to_send) == 0: + return + self.logger.info("Refinalizing {} sessions.".format(len(to_send))) + while len(to_send) > 0: + i = 0 + for fr in to_send[:1000]: + self._attempt_to_finalize(fr) + i += 1 + to_send = to_send[1000:] + self.logger.info("{} sessions have been refinalized.".format(i)) + + def _attempt_to_finalize(self, fr): + try: + self.contract.send_finalize(int(fr.chainId), int(fr.blockHeight), 60) + except Exception as ex: + self.logger.critical(''.join(traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__))) + fr.finalize_request() + fr.confirm_later() diff --git a/logformat.py b/logformat.py new file mode 100644 index 0000000..4f18f44 --- /dev/null +++ b/logformat.py @@ -0,0 +1,43 @@ +import logging +from logging.handlers import TimedRotatingFileHandler + + +class LogFormat(logging.Formatter): + + grey = "\x1b[38;20m" + yellow = "\x1b[33;20m" + red = "\x1b[31;20m" + bold_red = "\x1b[31;1m" + reset = "\x1b[0m" + format = "%(asctime)s - %(levelname)s - %(name)s - (%(filename)s:%(lineno)d) %(message)s " + + FORMATS = { + logging.DEBUG: grey + format + reset, + logging.INFO: grey + format + reset, + logging.WARNING: yellow + format + reset, + logging.ERROR: red + format + reset, + logging.CRITICAL: bold_red + format + reset + } + + def format(self, record): + log_fmt = self.FORMATS.get(record.levelno) + formatter = logging.Formatter(log_fmt) + return formatter.format(record) + + @staticmethod + def init_logger(class_name): + logger = logging.getLogger(class_name) + logger.setLevel(logging.DEBUG) + + logname = "logs/{}/log".format(class_name) + ch = TimedRotatingFileHandler(logname, when="D", interval=1) + ch.suffix = "%Y-%m-%d_%H-%M-%S.log" + # ch = logging.FileHandler("logs.txt") + ch.setLevel(logging.DEBUG) + + ch.setFormatter(LogFormat()) + + logger.addHandler(ch) + logger.propagate = False + + return logger \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..edadda4 --- /dev/null +++ b/main.py @@ -0,0 +1,49 @@ +import logging + +from contract import ProofChainContract +from dbmanager import DBManager +from finalizer import Finalizer +import os +from dotenv import load_dotenv + +if __name__ == "__main__": + + load_dotenv() + + BLOCK_ID_START = os.getenv("BLOCK_ID_START") + PROOFCHAIN_ADDRESS = os.getenv("PROOFCHAIN_ADDRESS") + FINALIZER_PRIVATE_KEY = os.getenv("FINALIZER_PRIVATE_KEY") + FINALIZER_ADDRESS = os.getenv("FINALIZER_ADDRESS") + RPC_ENDPOINT = os.getenv("RPC_ENDPOINT") + DB_USER = os.getenv("DB_USER") + DB_PASSWORD = os.getenv("DB_PASSWORD") + DB_HOST = os.getenv("DB_HOST") + DB_DATABASE = os.getenv("DB_DATABASE") + + logging.basicConfig(format='%(asctime)s - %(message)s', level=logging.INFO) + contract = ProofChainContract( + rpc_endpoint=RPC_ENDPOINT, + proofchain_address=PROOFCHAIN_ADDRESS, + finalizer_prvkey=FINALIZER_PRIVATE_KEY, + finalizer_address=FINALIZER_ADDRESS + ) + dbm = DBManager( + staring_point=int(BLOCK_ID_START), + user=DB_USER, + password=DB_PASSWORD, + database=DB_DATABASE, + host=DB_HOST + ) + + dbm.start() + finalizer = Finalizer(contract) + finalizer.start() + + dbm.join() + finalizer.join() + # + # contract.send_finalize(4, 10430382) + # contract.subscribe_on_event(handle_event) + + # dbm.join() + # finalizer.join() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3e0ef92 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,43 @@ +aiohttp==3.8.1 +aiosignal==1.2.0 +async-timeout==4.0.2 +attrs==21.4.0 +base58==2.1.1 +bitarray==1.2.2 +certifi==2021.10.8 +charset-normalizer==2.0.12 +cytoolz==0.11.2 +eth-abi==2.1.1 +eth-account==0.5.7 +eth-hash==0.3.2 +eth-keyfile==0.5.1 +eth-keys==0.3.4 +eth-rlp==0.2.1 +eth-typing==2.3.0 +eth-utils==1.10.0 +frozenlist==1.3.0 +hexbytes==0.2.2 +idna==3.3 +importlib-resources==5.7.0 +ipfshttpclient==0.8.0a2 +jsonschema==4.4.0 +lru-dict==1.1.7 +multiaddr==0.0.9 +multidict==6.0.2 +netaddr==0.8.0 +parsimonious==0.8.1 +protobuf==3.20.0 +psycopg2==2.9.3 +pycryptodome==3.14.1 +pyrsistent==0.18.1 +python-dotenv==0.20.0 +requests==2.27.1 +rlp==2.0.1 +six==1.16.0 +toolz==0.11.2 +urllib3==1.26.9 +varint==1.0.2 +web3==5.29.0 +websockets==9.1 +yarl==1.7.2 +zipp==3.8.0 From ea972b52d6a1b0e29b75b12b93f3f1f45595a07d Mon Sep 17 00:00:00 2001 From: Pranay Valson Date: Mon, 25 Apr 2022 15:25:10 -0700 Subject: [PATCH 2/7] updates gitignore for venv Signed-off-by: Pranay Valson --- .gitignore | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..85b607b --- /dev/null +++ b/.gitignore @@ -0,0 +1,126 @@ +# Editors +.vscode/ +.idea/ + +# Vagrant +.vagrant/ + +# Mac/OSX +.DS_Store + +# Windows +Thumbs.db + +# Source for the following rules: https://raw.githubusercontent.com/github/gitignore/master/Python.gitignore +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json +rewards From f0631b69daaf18120d705235061c56eeaec4af60 Mon Sep 17 00:00:00 2001 From: amirhossein Date: Mon, 25 Apr 2022 20:37:41 -0700 Subject: [PATCH 3/7] gigignore --- .gitignore | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 From 1329394d0597621f803dda7985fd03c20d215f90 Mon Sep 17 00:00:00 2001 From: amirhossein Date: Mon, 25 Apr 2022 20:37:58 -0700 Subject: [PATCH 4/7] gitignore --- .gitignore | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/.gitignore b/.gitignore index e69de29..8be5b0f 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1,151 @@ +# Created by .ignore support plugin (hsz.mobi) +### Python template +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# IPython Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# dotenv +.env + +# virtualenv +venv/ +ENV/ + +# Spyder project settings +.spyderproject + +# Rope project settings +.ropeproject +### VirtualEnv template +# Virtualenv +# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ +.Python +[Bb]in +[Ii]nclude +[Ll]ib +[Ll]ib64 +[Ll]ocal +[Ss]cripts +pyvenv.cfg +.venv +pip-selfcheck.json +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/workspace.xml +.idea/tasks.xml +.idea/dictionaries +.idea/vcs.xml +.idea/jsLibraryMappings.xml + +# Sensitive or high-churn files: +.idea/dataSources.ids +.idea/dataSources.xml +.idea/dataSources.local.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries + +# Mongo Explorer plugin: +.idea/mongoSettings.xml + +.idea/ + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties From 91d0bbcfa9abe418853cd361b089f640614e7b98 Mon Sep 17 00:00:00 2001 From: amirhossein Date: Mon, 25 Apr 2022 20:45:55 -0700 Subject: [PATCH 5/7] moved codes in the src folder. terminal log added. abi update. readme update. --- .gitignore | 2 + README.md | 43 ++++++++++++++++++- abi/ProofChainContractABI | 2 +- requirements.txt | 1 - src/LICENSE | 21 +++++++++ contract.py => src/contract.py | 5 +-- dbmanager.py => src/dbmanager.py | 0 .../finalizationrequest.py | 0 finalizer.py => src/finalizer.py | 3 +- logformat.py => src/logformat.py | 11 +++-- main.py => src/main.py | 16 +++++-- 11 files changed, 89 insertions(+), 15 deletions(-) create mode 100644 src/LICENSE rename contract.py => src/contract.py (99%) rename dbmanager.py => src/dbmanager.py (100%) rename finalizationrequest.py => src/finalizationrequest.py (100%) rename finalizer.py => src/finalizer.py (99%) rename logformat.py => src/logformat.py (79%) rename main.py => src/main.py (85%) diff --git a/.gitignore b/.gitignore index 8be5b0f..08298b9 100644 --- a/.gitignore +++ b/.gitignore @@ -149,3 +149,5 @@ com_crashlytics_export_strings.xml crashlytics.properties crashlytics-build.properties fabric.properties + +logs/ \ No newline at end of file diff --git a/README.md b/README.md index 3663b0d..80848fa 100644 --- a/README.md +++ b/README.md @@ -1 +1,42 @@ -# bsp-finalizer \ No newline at end of file +# bsp-finalizer + +# Running + +Put appropriate values on the `.env` file: + +``` +RPC_ENDPOINT="" +BLOCK_ID_START="1606150927003422917" # The block ID from which DB thread starts reading off +PROOFCHAIN_ADDRESS="0x48bb0d9653D30b977439c71B3F6C4557137dD0ad" +FINALIZER_PRIVATE_KEY="" +FINALIZER_ADDRESS="" +DB_USER="" +DB_PASSWORD="" +DB_HOST="master.datamodel.db.covalenthq.com" +DB_DATABASE="blockchains" +``` + +Load environment variables: +``` +direnv allow +``` + +Create directories for logging: + +``` +mkdir -p logs/{Contract,Finalizer,DB} +``` + +Install python packages (venv): + +``` +python3 -m venv ./.venv +source .venv/bin/activate +brew install openssl # In case you don't already installed it +export LDFLAGS="-L/opt/homebrew/opt/openssl@3/lib"; pip install -r requirements.txt +``` + +Run the script: +``` +python3 src/main.py +``` \ No newline at end of file diff --git a/abi/ProofChainContractABI b/abi/ProofChainContractABI index 4a9df1d..51907b0 100644 --- a/abi/ProofChainContractABI +++ b/abi/ProofChainContractABI @@ -1020,4 +1020,4 @@ "stateMutability": "view", "type": "function" } -] +] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 3e0ef92..48df758 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,7 +19,6 @@ frozenlist==1.3.0 hexbytes==0.2.2 idna==3.3 importlib-resources==5.7.0 -ipfshttpclient==0.8.0a2 jsonschema==4.4.0 lru-dict==1.1.7 multiaddr==0.0.9 diff --git a/src/LICENSE b/src/LICENSE new file mode 100644 index 0000000..e8b8ba9 --- /dev/null +++ b/src/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Covalent + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/contract.py b/src/contract.py similarity index 99% rename from contract.py rename to src/contract.py index 1727995..aa95643 100644 --- a/contract.py +++ b/src/contract.py @@ -1,6 +1,3 @@ -import asyncio -import logging -import pprint import statistics import traceback @@ -29,7 +26,7 @@ def __init__(self, rpc_endpoint, finalizer_address, finalizer_prvkey, proofchain abi=f.read() ) - self.logger = logformat.LogFormat.init_logger("Contract") + self.logger = logformat.LogFormat.init_logger("Contract", console_mode=True) # asynchronous defined function to loop # this loop sets up an event filter and is looking for new entires for the "PairCreated" event diff --git a/dbmanager.py b/src/dbmanager.py similarity index 100% rename from dbmanager.py rename to src/dbmanager.py diff --git a/finalizationrequest.py b/src/finalizationrequest.py similarity index 100% rename from finalizationrequest.py rename to src/finalizationrequest.py diff --git a/finalizer.py b/src/finalizer.py similarity index 99% rename from finalizer.py rename to src/finalizer.py index f1beedb..68dbcc0 100644 --- a/finalizer.py +++ b/src/finalizer.py @@ -1,6 +1,5 @@ import threading import time -import logging import traceback import logformat @@ -12,7 +11,7 @@ class Finalizer(threading.Thread): def __init__(self, cn: ProofChainContract): super().__init__() self.contract = cn - self.logger = logformat.LogFormat.init_logger("Finalizer") + self.logger = logformat.LogFormat.init_logger("Finalizer", console_mode=True) def __main_loop(self): # self.contract.estimate_gas_price() diff --git a/logformat.py b/src/logformat.py similarity index 79% rename from logformat.py rename to src/logformat.py index 4f18f44..917edd5 100644 --- a/logformat.py +++ b/src/logformat.py @@ -25,7 +25,7 @@ def format(self, record): return formatter.format(record) @staticmethod - def init_logger(class_name): + def init_logger(class_name, console_mode=False): logger = logging.getLogger(class_name) logger.setLevel(logging.DEBUG) @@ -34,10 +34,15 @@ def init_logger(class_name): ch.suffix = "%Y-%m-%d_%H-%M-%S.log" # ch = logging.FileHandler("logs.txt") ch.setLevel(logging.DEBUG) + format = LogFormat() - ch.setFormatter(LogFormat()) + ch.setFormatter(format) logger.addHandler(ch) + if console_mode: + console_handler = logging.StreamHandler() + console_handler.setFormatter(format) + logger.addHandler(console_handler) logger.propagate = False - return logger \ No newline at end of file + return logger diff --git a/main.py b/src/main.py similarity index 85% rename from main.py rename to src/main.py index edadda4..bef09cb 100644 --- a/main.py +++ b/src/main.py @@ -1,4 +1,5 @@ import logging +import time from contract import ProofChainContract from dbmanager import DBManager @@ -6,6 +7,11 @@ import os from dotenv import load_dotenv + +def is_any_thread_alive(threads): + return True in [t.is_alive() for t in threads] + + if __name__ == "__main__": load_dotenv() @@ -34,13 +40,17 @@ database=DB_DATABASE, host=DB_HOST ) + dbm.daemon = True - dbm.start() finalizer = Finalizer(contract) + finalizer.daemon = True + dbm.start() + finalizer.start() - dbm.join() - finalizer.join() + while is_any_thread_alive([finalizer, dbm]): + time.sleep(0.3) + # # contract.send_finalize(4, 10430382) # contract.subscribe_on_event(handle_event) From a6cb9b61cba516d053ada6c74285db73b6627861 Mon Sep 17 00:00:00 2001 From: Pranay Valson Date: Tue, 26 Apr 2022 10:46:50 -0700 Subject: [PATCH 6/7] corrects path required to abi directory Signed-off-by: Pranay Valson --- src/contract.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/contract.py b/src/contract.py index aa95643..9eb5d38 100644 --- a/src/contract.py +++ b/src/contract.py @@ -20,7 +20,7 @@ def __init__(self, rpc_endpoint, finalizer_address, finalizer_prvkey, proofchain self.gasPrice = web3.auto.w3.toWei('1', 'gwei') self.w3.middleware_onion.inject(geth_poa_middleware, layer=0) self.contractAddress: str = proofchain_address # "0x8243AF52B91649547DC80814670Dd1683F360E4c" - with open("abi/ProofChainContractABI", "r") as f: + with open("../abi/ProofChainContractABI", "r") as f: self.contract = self.w3.eth.contract( address=self.contractAddress, abi=f.read() From 08490d7df6447b2857af2c3f36d60e0dddc61bff Mon Sep 17 00:00:00 2001 From: Pranay Valson Date: Tue, 26 Apr 2022 14:46:34 -0700 Subject: [PATCH 7/7] moves back to old abi path Signed-off-by: Pranay Valson --- src/contract.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/contract.py b/src/contract.py index 9eb5d38..aa95643 100644 --- a/src/contract.py +++ b/src/contract.py @@ -20,7 +20,7 @@ def __init__(self, rpc_endpoint, finalizer_address, finalizer_prvkey, proofchain self.gasPrice = web3.auto.w3.toWei('1', 'gwei') self.w3.middleware_onion.inject(geth_poa_middleware, layer=0) self.contractAddress: str = proofchain_address # "0x8243AF52B91649547DC80814670Dd1683F360E4c" - with open("../abi/ProofChainContractABI", "r") as f: + with open("abi/ProofChainContractABI", "r") as f: self.contract = self.w3.eth.contract( address=self.contractAddress, abi=f.read()