def setup_listeners(self): """Setup subsystems that scan for incoming events from geth.""" wallet_contract = HostedWallet.contract_class(self.web3) token_contract = Token.contract_class(self.web3) self.eth_wallet_listener = EthWalletListener(self.web3, wallet_contract, self.dbsession, self.asset_network_id, registry=self.registry) self.eth_token_listener = EthTokenListener(self.web3, token_contract, self.dbsession, self.asset_network_id, registry=self.registry) self.confirmation_updater = DatabaseConfirmationUpdater(self.web3, self.dbsession, self.asset_network_id, self.registry) self.op_queue_manager = OperationQueueManager(self.web3, self.dbsession, self.asset_network_id, self.registry)
class EthereumService: """Ethereum service takes care of synchronizing operations between internal database and Ethereum daemon. We take a simple approach where we have one service / db running one single thread which does all operations serial manner. When testing, we call functions directly. """ def __init__(self, web3: Web3, asset_network_id: UUID, dbsession: Session, registry: Registry): assert isinstance(web3, Web3) assert isinstance(registry, Registry) self.web3 = web3 self.asset_network_id = asset_network_id self.dbsession = dbsession self.registry = registry self.setup_listeners() def get_withdraw_required_confirmation_count(self): """How many confirmations we check on withdraw ops until we mark them confirmed.""" return 1 def setup_listeners(self): """Setup subsystems that scan for incoming events from geth.""" wallet_contract = HostedWallet.contract_class(self.web3) token_contract = Token.contract_class(self.web3) self.eth_wallet_listener = EthWalletListener(self.web3, wallet_contract, self.dbsession, self.asset_network_id, registry=self.registry) self.eth_token_listener = EthTokenListener(self.web3, token_contract, self.dbsession, self.asset_network_id, registry=self.registry) self.confirmation_updater = DatabaseConfirmationUpdater(self.web3, self.dbsession, self.asset_network_id, self.registry) self.op_queue_manager = OperationQueueManager(self.web3, self.dbsession, self.asset_network_id, self.registry) def run_listener_operations(self) -> Tuple[int, int]: """Return number of operations events read and handled.""" total_success = total_failure = 0 for func in (self.eth_wallet_listener.poll, self.eth_token_listener.poll): success, failure = func() total_success += success total_failure += failure return total_success, total_failure def run_confirmation_updates(self) -> Tuple[int, int]: return self.confirmation_updater.poll() def run_waiting_operations(self) -> Tuple[int, int]: """Run all operations that are waiting to be executed. :return: Number of operations (performed successfully, failed) """ return self.op_queue_manager.run_waiting_operations() def update_heartbeat(self): # Tell web interface we are still alive block_number = self.web3.eth.blockNumber block = self.web3.eth.getBlock(block_number) block_time = int(block["timestamp"]) update_heart_beat(self.dbsession, self.asset_network_id, block_number, block_time) def run_event_cycle(self, cycle_num=None) -> Tuple[int, int]: """Run full event cycle for all operations.""" total_success = total_failure = 0 for func in (self.run_waiting_operations, self.run_listener_operations, self.run_confirmation_updates): # Make sure all transactions are closed before and after running ops # logger.info("Running %s", func) ensure_transactionless("TX management Error. Starting to process {} in event cycle {}".format(func, cycle_num)) success, failure = func() ensure_transactionless() total_success += success total_failure += failure self.update_heartbeat() return total_success, total_failure