def htlc_unlock_payment(cls, invoker, channel_id, founder, partner, lock_period, lock_amount, lock_hash, founder_signature, partner_signature, lock_secret, invoker_key, gwei_coef=1): try: result = cls._eth_interface.withdraw(invoker, channel_id, founder, partner, int(lock_period), int(lock_amount), lock_hash, founder_signature, partner_signature, lock_secret, invoker_key, gwei_coef=gwei_coef) LOG.debug('htlc_unlock_payment result: {}'.format(result)) return result except Exception as error: LOG.exception('htlc_unlock_payment error: {}'.format(error)) return None
def approve(cls, address, deposit, private_key, gwei_coef=1): """ :param address: :param deposit: :param private_key: :param gwei_coef: :return: """ deposit = int(deposit) # approved_asset = cls.get_approved_asset(address) # # if approved_asset >= deposit: # LOG.info('Has been approved asset count: {}'.format(approved_asset)) # return True try: # return tx_id tx_id = cls._eth_interface.approve(address, deposit, private_key, gwei_coef=gwei_coef) LOG.debug( 'ContractEventInterface::approve: txId: {}'.format(tx_id)) return tx_id except Exception as error: LOG.exception('authorized deposit error: {}'.format(error)) return False
def handle(self): """ :return: """ old_block = get_block_count() - 1 _event_coroutine = self.timer_event() next(_event_coroutine) self.prepare_handle_event() while WebSocketConnection._websocket_listening: try: response = self.receive() block_height = get_block_count() # handle response self.handle_event(block_height, response) # handle timer event if old_block != block_height: for block in range(old_block + 1, block_height + 1): ucoro_event(_event_coroutine, block) # set new value to old_block old_block = block_height time.sleep(0.5) except Exception as error: LOG.exception('websocket handle: {}'.format(error)) time.sleep(1) ucoro_event(_event_coroutine, None)
def ucoro_event(coro, iter_data): try: coro.send(iter_data) except StopIteration as error: LOG.debug('Co-routine has been killed.') except Exception as error: LOG.exception('Error occurred during using co-routine. error: {}'.format(error))
def approve_deposit(cls, address, channel_id, nonce, founder, founder_amount, partner, partner_amount, founder_sign, partner_sign, private_key, gwei_coef=1): try: return cls._eth_interface.deposit(address, channel_id, nonce, founder, int(founder_amount), partner, int(partner_amount), founder_sign, partner_sign, private_key, gwei_coef=gwei_coef) except Exception as error: LOG.exception('approve_deposit error: {}'.format(error)) return None
def register_quick_close_event(self, is_founder=True): """ :param is_founder: :return: """ try: channel_event = ChannelQuickSettleEvent(self.channel_name, self.wallet.address, is_founder, self.asset_type) if is_founder: settle_trade = Channel.query_trade(self.channel_name, SettleBase._SETTLE_NONCE) # register arguments for execution action channel_event.register_args( EnumEventAction.EVENT_EXECUTE, self.receiver_address, self.channel_name, self.nonce, self.receiver_address, settle_trade.balance, self.sender_address, settle_trade.peer_balance, settle_trade.commitment, self.commitment, self.wallet._key.private_key_string) # register arguments for termination action channel_event.register_args(EnumEventAction.EVENT_TERMINATE, asset_type=self.asset_type) except Exception as error: LOG.exception( 'Failed to regiser quick close channel event since {}'.format( error)) else: # register and trigger the event event_machine.register_event(self.channel_name, channel_event) event_machine.trigger_start_event(self.channel_name)
def execute(self, block_height, channel_name='', hashcode=''): """ :param block_height: :param channel_name: :param kwargs: :return: """ super(ChannelHtlcUnlockedEvent, self).execute(block_height) # if monitor this event, update the htlc trade state to confirmed if channel_name and hashcode: try: htlc_trade = Channel.batch_query_trade( channel_name, filters={ 'type': EnumTradeType.TRADE_TYPE_HTLC.name, 'hashcode': hashcode })[0] except Exception as error: LOG.exception('Htlc trade with HashR<{}> not found for channel<{}>. Exception: {}'\ .format(hashcode, channel_name, error)) else: Channel.update_trade( channel_name, htlc_trade.nonce, state=EnumTradeState.confirmed_onchain.name) else: LOG.error( 'Error input parameters: channel <{}>, hashcode<{}>.'.format( channel_name, hashcode))
def get_transaction_receipt(cls, tx_id): try: result = cls._eth_interface.get_transaction_receipt(tx_id) if result: print(result) return result except Exception as error: LOG.exception('get_approved_asset error: {}'.format(error)) return None
def handle(self): super(RsmcResponsesMessage, self).handle() status = EnumResponseStatus.RESPONSE_OK try: # check the response status if not self.check_response_status(self.status): self.rollback_resource(self.channel_name, self.nonce, self.payment, self.status) return # common check self.check_channel_state(self.channel_name) self.verify() self.check_role(self.role_index) if 0 == self.role_index: need_update_balance, rsmc_sign_body = self.handle_partner_response() elif 1 == self.role_index: need_update_balance, rsmc_sign_body = self.handle_founder_response() else: raise GoTo(EnumResponseStatus.RESPONSE_FAIL, 'Invalid Role<{}> of RsmcResponse'.format(self.role_index)) # send RsmcSign to peer if rsmc_sign_body: response_message = self.create_message_header(self.receiver, self.sender, self._message_name, self.channel_name, self.asset_type, self.nego_nonce or self.nonce) response_message.update({'MessageBody': rsmc_sign_body}) response_message.update({'Status': status.name}) self.send(response_message) is_htlc_to_rsmc = self.is_hlock_to_rsmc(self.hashcode) if is_htlc_to_rsmc: # update the htlc transaction state. Channel.confirm_payment(self.channel_name, self.hashcode, is_htlc_to_rsmc) # update the channel balance if need_update_balance: APIStatistics.update_statistics(self.wallet.address, payment=self.payment, payer=(0==self.role_index)) self.update_balance_for_channel(self.channel_name, self.asset_type, self.payer_address, self.payee_address, self.payment, is_htlc_to_rsmc) except GoTo as error: LOG.exception(error) status = error.reason except Exception as error: status = EnumResponseStatus.RESPONSE_EXCEPTION_HAPPENED LOG.exception('Failed to handle RsmcSign for channel<{}> nonce<{}>, role_index<{}>.Exception: {}' \ .format(self.channel_name, self.nonce, self.role_index, error)) finally: # send error response if EnumResponseStatus.RESPONSE_OK != status: if 0 == self.role_index: self.send_error_response(self.sender, self.receiver, self.channel_name, self.asset_type, self.nonce, status, kwargs={'RoleIndex': '1'}) # need rollback some resources self.rollback_resource(self.channel_name, self.nonce, self.payment, status=self.status) return
def monitorCloseChannel(self, message): """ :param message: :return: message like below format: { 'playload': '0xabf328663edff39bfa3f157556afa52bdcb14fda32d35c70e6e3386e954e7995', 'txId': '0xc0461c9a92a295eec4dd1060e4abd34bbb1c8139e2b9bc5e35e7c2daa1dccfc5', 'channelId': '0xabf328663edff39bfa3f157556afa52bdcb14fda32d35c70e6e3386e954e7995', 'invoker': '0x23cca051bfedb5e17d3aad2038ba0a5155d1b1b7', 'nonce': 5, 'blockNumber': 3924231, 'messageType': 'monitorCloseChannel' } """ if not message: LOG.error('Invalid message: {}'.format(message)) return try: invoker = message.get('invoker').strip() channel_name = message.get('channelId') nonce = message.get('nonce') end_time = int(message.get('blockNumber')) except Exception as error: LOG.exception('Invalid message: {}. Exception: {}'.format( message, error)) else: if not (self.wallet_address and invoker): LOG.error( 'Wallet address<{}> or invoker<{}> should not be none'. format(self.wallet_address, invoker)) return if invoker != self.wallet_address.lower(): channel_event = ChannelUpdateSettleEvent( channel_name, self.wallet_address) channel_event.register_args( EnumEventAction.EVENT_EXECUTE, self.wallet.url, channel_name, self.wallet._key.private_key_string, nonce) event_machine.register_event(channel_name, channel_event) event_machine.trigger_start_event(channel_name) else: LOG.debug('register ChannelEndSettleEvent at block<{}>'.format( end_time)) channel_event = ChannelEndSettleEvent(channel_name, self.wallet_address) channel_event.register_args( EnumEventAction.EVENT_EXECUTE, invoker, channel_name, self.wallet._key.private_key_string) self.register_event(channel_event, end_time) return
def register_event(self, name, event): try: self.event_lock.acquire() self.__event_queue.update({name: event}) except Exception as error: LOG.exception( 'event machine register_event<{}> exception: {}'.format( name, error)) finally: self.event_lock.release()
def register_deposit_event(self, is_founder=True): """ :param is_founder: :return: """ try: channel_event = ChannelDepositEvent(self.channel_name, self.wallet.address, is_founder) # get the transaction record of founder message founder_trade = Channel.query_trade(self.channel_name, FounderBase._FOUNDER_NONCE) # set some arguments by the role type self_address = self.receiver_address self_deposit = founder_trade.balance if is_founder: founder_address = self.receiver_address founder_deposit = founder_trade.balance founder_commitment = founder_trade.commitment partner_address = self.sender_address partner_deposit = founder_trade.peer_balance partner_commitment = founder_trade.peer_commitment else: founder_address = self.sender_address founder_deposit = founder_trade.peer_balance founder_commitment = founder_trade.peer_commitment partner_address = self.receiver_address partner_deposit = founder_trade.balance partner_commitment = founder_trade.commitment # register the preparation action for deposit event channel_event.register_args(EnumEventAction.EVENT_PREPARE, self_address, self_deposit, self.wallet._key.private_key_string) # add execution action for deposit event channel_event.register_args( EnumEventAction.EVENT_EXECUTE, self_address, self.channel_name, FounderBase._FOUNDER_NONCE, founder_address, founder_deposit, partner_address, partner_deposit, founder_commitment, partner_commitment, self.wallet._key.private_key_string) channel_event.register_args(EnumEventAction.EVENT_TERMINATE, asset_type=self.asset_type) except Exception as error: LOG.exception( 'Failed to register deposit event since {}'.format(error)) else: # register and trigger event event_machine.register_event(self.channel_name, channel_event) event_machine.trigger_start_event(self.channel_name) return
def trigger_start_event(self, name): try: self.event_lock.acquire() if name not in self.__event_ordered_list: self.__event_ordered_list.append(name) except Exception as error: LOG.exception( 'event machine trigger_start_event<{}> exception: {}'.format( name, error)) finally: self.event_lock.release()
def end_close_channel(cls, invoker, channel_id, invoker_key, gwei_coef=1): try: result = cls._eth_interface.settle_transaction(invoker, channel_id, invoker_key, gwei_coef=gwei_coef) LOG.debug('end_close_channel result: {}'.format(result)) return result except Exception as error: LOG.exception('end force_settle error: {}'.format(error)) return None
def get_event(self, key): try: self.event_lock.acquire() event = self.__monitor_queue.pop(key, []) except Exception as error: event = None LOG.exception('websocket get_event exception: {}'.format(error)) finally: self.event_lock.release() return event
def send(self, payload): try: self._conn.send(payload) except WebSocketConnectionClosedException as error: LOG.exception('send: Websocket was closed: {}'.format(error)) self.reconnect() # re-send this payload if self._conn: self._conn.send(payload) except Exception as error: LOG.exception('send: Websocket exception: {}'.format(error))
def get_approved_asset(cls, address): try: result = cls._eth_interface.get_approved_asset( settings.TNC, settings.TNC_abi, address, settings.ETH_Data_Contract_address) if result: result = TrinityNumber(str(result)).number return result except Exception as error: LOG.exception('get_approved_asset error: {}'.format(error)) return 0
def unregister_event(self, name): try: self.event_lock.acquire() if name in self.__event_ordered_list: self.__event_ordered_list.remove(name) if name in self.__event_queue.keys(): self.__event_queue.pop(name) except Exception as error: LOG.exception( 'event machine unregister_event<{}> exception: {}'.format( name, error)) finally: self.event_lock.release()
def insert_event_back_into_queue(self, name, event): try: self.event_lock.acquire() if not self.has_event(name): self.__event_queue.update({name: event}) self.__event_ordered_list.append(name) except Exception as error: LOG.exception( 'event machine insert_event_back_into_queue<{}> exception: {}'. format(name, error)) finally: self.event_lock.release() return
def execute(self, block_height, address='', channel_id='', nonce='', founder='', deposit=0, partner='', partner_deposit=0, founder_sign='', partner_sign='', private_key=''): super(ChannelDepositEvent, self).execute(block_height) # update the founder and partner deposit self.deposit = int(deposit) self.partner_deposit = int(partner_deposit) # execute stage of channel event try: # if this wallet is not the event founder, go to next step directly. if not self.is_event_founder: self.next_stage() return True # below codes is just executed by the founder # check whether deposit is success or not if self.deposit_tx_id: checked = self.check_transaction_success(self.deposit_tx_id) if checked: # go to next stage self.next_stage() return True elif checked is False: return False else: # new transaction is pushed to chain self.deposit_tx_id = None # get approved asset of both partners peer_approved_deposit = self.contract_event_api.get_approved_asset(partner) approved_deposit = self.contract_event_api.get_approved_asset(founder) LOG.debug('Approved asset in wallet<{}>: founder<{}:{}>, partner<{}:{}>' \ .format(address, founder, approved_deposit, partner, peer_approved_deposit)) # check approved asset of both side for event founder. if not (approved_deposit >= self.deposit and peer_approved_deposit >= self.partner_deposit): return False # Trigger deposit action if is_event_founder is True result = self.deposit_tx_id = self.contract_event_api.approve_deposit( address, channel_id, nonce, founder, deposit, partner, partner_deposit, founder_sign, partner_sign, private_key, gwei_coef=self.gwei_coef) if result: self.deposit_tx_id = '0x'+result.strip() except Exception as error: LOG.exception('Failed to approve deposit of Channel<{}>. Exception: {}'.format(self.channel_name, error)) return False
def get_event(self): try: self.event_lock.acquire() name = self.__event_ordered_list.pop(0) event = self.__event_queue.pop(name) except Exception as error: name = None event = None LOG.exception( 'event machine get_event exception: {}'.format(error)) finally: self.event_lock.release() return name, event
def monitorWithdrawUpdate(self, message): if not message: LOG.error('Invalid message: {}'.format(message)) return try: invoker = message.get('invoker').strip() channel_name = message.get('channelId') except Exception as error: LOG.exception('Invalid message: {}. Exception: {}'.format( message, error)) else: if invoker.lower() != self.wallet_address.lower() and channel_name: ChannelStateManageEvent(channel_name).execute( get_block_count(), channel_name)
def receive(self): try: return self._conn.recv() except WebSocketConnectionClosedException as error: LOG.exception('receive: Websocket was closed: {}'.format(error)) self.reconnect() return self._conn.recv() if self._conn else None except WebSocketTimeoutException as error: pass except Exception as error: if not self._conn: self.reconnect() LOG.exception('receive: Websocket exception: {}'.format(error)) return None
def create_client(self, retry=False): try: self._conn = create_connection(self.__ws_url, sockopt=( (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1), (socket.IPPROTO_TCP, socket.TCP_NODELAY, 1), ), timeout=self.timeout) except Exception as error: LOG.exception('Connect to server error. {}'.format(error)) else: if self.wallet_address and retry: self.notify_wallet_info() pass
def quick_settle(cls, invoker, channel_id, nonce, founder, founder_balance, partner, partner_balance, founder_signature, partner_signature, invoker_key, gwei_coef=1): """ :param invoker: :param channel_id: :param nonce: :param founder: :param founder_balance: :param partner: :param partner_balance: :param founder_signature: :param partner_signature: :param invoker_key: :return: """ try: return cls._eth_interface.quick_close_channel(invoker, channel_id, nonce, founder, int(founder_balance), partner, int(partner_balance), founder_signature, partner_signature, invoker_key, gwei_coef=gwei_coef) except Exception as error: LOG.exception('quick_settle error: {}'.format(error)) LOG.error('quick_settle parameters: ' 'channel<{}>, nonce<{}>, ' 'founder<{}>, founder_balance<{}>, founder_signature<{}>, ' 'partner<{}>, partner_balance<{}>, partner_signature<{}>' \ .format(channel_id, nonce, founder, founder_balance, founder_signature, partner, partner_balance, partner_signature)) return None
def handle(self): super(SettleMessage, self).handle() status = EnumResponseStatus.RESPONSE_OK nonce = self.nonce try: # common check arguments self.verify() self.check_nonce(self.nonce) self.check_balance(self.channel_name, self.asset_type, self.sender_address, self.sender_balance, self.receiver_address, self.receiver_balance) self.check_signature(self.wallet, self.sender_address, type_list=self._sign_type_list, value_list=[ self.channel_name, self.nonce, self.sender_address, int(self.sender_balance), self.receiver_address, int(self.receiver_balance) ], signature=self.commitment) # To create settle response message SettleResponseMessage.create(self.wallet, self.channel_name, self.asset_type, self.nonce, self.sender, self.sender_balance, self.receiver, self.receiver_balance, self.commitment) except GoTo as error: LOG.exception(error) status = error.reason except Exception as error: LOG.exception(error) status = EnumResponseStatus.RESPONSE_EXCEPTION_HAPPENED finally: if EnumResponseStatus.RESPONSE_OK != status: SettleResponseMessage.send_error_response( self.sender, self.receiver, self.channel_name, self.asset_type, nonce, status) else: # register the settle event self.register_quick_close_event(False) return
def monitorWithdraw(self, message): """ :param message: :return: """ if not message: LOG.error('Invalid message: {}'.format(message)) return try: invoker = message.get('invoker').strip() channel_name = message.get('channelId') hashcode = message.get('lockHash') rcode = message.get('secret') end_time = int(message.get('blockNumber')) except Exception as error: LOG.exception('Invalid message: {}. Exception: {}'.format( message, error)) else: if not (self.wallet_address and invoker): LOG.error('monitorWithdraw: Wallet address<{}> or invoker<{}> should not be none' \ .format(self.wallet_address, invoker)) return if invoker != self.wallet_address.lower(): channel_event = ChannelPunishHtlcUnlockEvent(channel_name) channel_event.register_args( EnumEventAction.EVENT_EXECUTE, self.wallet.url, channel_name, hashcode, rcode, self.wallet._key.private_key_string) event_machine.register_event(channel_name, channel_event) event_machine.trigger_start_event(channel_name) else: LOG.debug( 'monitorWithdraw: register ChannelEndSettleEvent at next block' ) channel_event = ChannelSettleHtlcUnlockEvent(channel_name) channel_event.register_args( EnumEventAction.EVENT_EXECUTE, invoker, channel_name, hashcode, self.wallet._key.private_key_string) self.register_event(channel_event, end_time) return
def wrapper(*args, **kwargs): received = None # use such mode to modulate blocking-mode to received while True: try: received = yield if received in ['exit', None]: break kwargs.update({'received': received}) callback(*args, **kwargs) except Exception as error: LOG.exception('Co-routine received<{}>, error: {}'.format(received, error)) finally: # only run once time if once: break time.sleep(timeout)
def register_event(self, event, end_block=None): if not end_block: block_height = get_block_count() + 1 else: block_height = end_block try: self.event_lock.acquire() event_list = self.__monitor_queue.get(block_height, []) event_list.append(event) self.__monitor_queue.update({block_height: event_list}) except Exception as error: LOG.exception( 'websocket register_event exception: {}'.format(error)) finally: self.event_lock.release() return
def monitorWithdrawSettle(self, message): """ :param message: :return: """ try: channel_name = message.get('channelId') hashcode = message.get('lockHash') # update the channel to settled state if channel_name: channel_event = ChannelHtlcUnlockedEvent(channel_name) channel_event.execute(get_block_count(), channel_name, hashcode) except Exception as error: LOG.exception('Invalid message: {}. Exception: {}'.format( message, error)) return