def test_get_channelstate_by_canonical_identifier(): test_state = factories.make_chain_state(number_of_channels=3) canonical_identifier = test_state.channels[1].canonical_identifier assert (views.get_channelstate_by_canonical_identifier( chain_state=test_state.chain_state, canonical_identifier=canonical_identifier) == test_state.channels[1])
def handle_contract_send_channelwithdraw( raiden: "RaidenService", channel_withdraw_event: ContractSendChannelWithdraw ) -> None: withdraw_confirmation_data = pack_withdraw( canonical_identifier=channel_withdraw_event.canonical_identifier, participant=raiden.address, total_withdraw=channel_withdraw_event.total_withdraw, expiration_block=channel_withdraw_event.expiration, ) our_signature = raiden.signer.sign(data=withdraw_confirmation_data) chain_state = state_from_raiden(raiden) confirmed_block_identifier = chain_state.block_hash channel_state = get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=channel_withdraw_event.canonical_identifier, ) if channel_state is None: raise RaidenUnrecoverableError("ContractSendChannelWithdraw for non-existing channel.") channel_proxy = raiden.proxy_manager.payment_channel( channel_state=channel_state, block_identifier=confirmed_block_identifier ) try: channel_proxy.set_total_withdraw( total_withdraw=channel_withdraw_event.total_withdraw, expiration_block=channel_withdraw_event.expiration, participant_signature=our_signature, partner_signature=channel_withdraw_event.partner_signature, block_identifier=channel_withdraw_event.triggered_by_block_hash, ) except InsufficientEth as e: raise RaidenUnrecoverableError(str(e)) from e
def channel_state_until_state_change( raiden: "RaidenService", canonical_identifier: CanonicalIdentifier, state_change_identifier: StateChangeID, ) -> Optional[NettingChannelState]: # pragma: no unittest """ Go through WAL state changes until a certain balance hash is found. """ assert raiden.wal, "Raiden has not been started yet" wal = restore_to_state_change( transition_function=node.state_transition, storage=raiden.wal.storage, state_change_identifier=state_change_identifier, node_address=raiden.address, ) msg = "There is a state change, therefore the state must not be None" assert wal.state_manager.current_state is not None, msg chain_state = wal.state_manager.current_state channel_state = views.get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=canonical_identifier) if not channel_state: raise RaidenUnrecoverableError( f"Channel was not found before state_change {state_change_identifier}" ) return channel_state
def update_path_finding_service_from_balance_proof( raiden: 'RaidenService', chain_state: 'ChainState', new_balance_proof: Union[BalanceProofSignedState, BalanceProofUnsignedState], ) -> None: channel_state = views.get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=new_balance_proof.canonical_identifier, ) network_address = new_balance_proof.canonical_identifier.token_network_address error_msg = (f'tried to send a balance proof in non-existant channel ' f'token_network_address: {pex(network_address)} ') assert channel_state is not None, error_msg if isinstance(new_balance_proof, BalanceProofSignedState): assert channel_state.partner_state.balance_proof == new_balance_proof else: # BalanceProofUnsignedState assert channel_state.our_state.balance_proof == new_balance_proof msg = UpdatePFS.from_channel_state(channel_state) msg.sign(raiden.signer) raiden.transport.send_global(constants.PATH_FINDING_BROADCASTING_ROOM, msg) log.debug( 'Sent a PFS Update', message=msg, balance_proof=new_balance_proof, )
def send_pfs_update( raiden: "RaidenService", canonical_identifier: CanonicalIdentifier, update_fee_schedule: bool = False, ) -> None: if raiden.routing_mode == RoutingMode.PRIVATE: return channel_state = views.get_channelstate_by_canonical_identifier( chain_state=views.state_from_raiden(raiden), canonical_identifier=canonical_identifier ) if channel_state is None: return capacity_msg = PFSCapacityUpdate.from_channel_state(channel_state) capacity_msg.sign(raiden.signer) raiden.transport.send_global(constants.PATH_FINDING_BROADCASTING_ROOM, capacity_msg) log.debug("Sent a PFS Capacity Update", message=capacity_msg, channel_state=channel_state) if update_fee_schedule: fee_msg = PFSFeeUpdate.from_channel_state(channel_state) fee_msg.sign(raiden.signer) raiden.transport.send_global(constants.PATH_FINDING_BROADCASTING_ROOM, fee_msg) log.debug("Sent a PFS Fee Update", message=fee_msg, channel_state=channel_state)
def handle_channel_update_transfer(raiden: "RaidenService", event: Event): token_network_identifier = event.originating_contract data = event.event_data args = data["args"] channel_identifier = args["channel_identifier"] transaction_hash = data["transaction_hash"] block_number = data["block_number"] block_hash = data["block_hash"] chain_state = views.state_from_raiden(raiden) channel_state = views.get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=CanonicalIdentifier( chain_identifier=chain_state.chain_id, token_network_address=token_network_identifier, channel_identifier=channel_identifier, ), ) if channel_state: channel_transfer_updated = ContractReceiveUpdateTransfer( transaction_hash=transaction_hash, canonical_identifier=channel_state.canonical_identifier, nonce=args["nonce"], block_number=block_number, block_hash=block_hash, ) raiden.handle_and_track_state_change(channel_transfer_updated)
def subdispatch_targettask( chain_state: ChainState, state_change: StateChange, token_network_identifier: TokenNetworkID, channel_identifier: ChannelID, secrethash: SecretHash, ) -> TransitionResult[ChainState]: block_number = chain_state.block_number sub_task = chain_state.payment_mapping.secrethashes_to_task.get(secrethash) if not sub_task: is_valid_subtask = True target_state = None elif sub_task and isinstance(sub_task, TargetTask): is_valid_subtask = ( token_network_identifier == sub_task.token_network_identifier) target_state = sub_task.target_state else: is_valid_subtask = False events = list() channel_state = None if is_valid_subtask: channel_state = views.get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=CanonicalIdentifier( chain_identifier=chain_state.chain_id, token_network_address=token_network_identifier, channel_identifier=channel_identifier, ), ) if channel_state: pseudo_random_generator = chain_state.pseudo_random_generator iteration = target.state_transition( target_state, state_change, channel_state, pseudo_random_generator, block_number, ) events = iteration.events if iteration.new_state: sub_task = TargetTask( token_network_identifier, channel_identifier, iteration.new_state, ) chain_state.payment_mapping.secrethashes_to_task[ secrethash] = sub_task elif secrethash in chain_state.payment_mapping.secrethashes_to_task: del chain_state.payment_mapping.secrethashes_to_task[secrethash] return TransitionResult(chain_state, events)
def update_monitoring_service_from_balance_proof( raiden: 'RaidenService', chain_state: 'ChainState', new_balance_proof: BalanceProofSignedState, ) -> None: if raiden.config['services']['monitoring_enabled'] is False: return channel_state = views.get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=new_balance_proof.canonical_identifier, ) msg = ( f'Failed to update monitoring service due to inability to find ' f'channel: {new_balance_proof.channel_identifier} ' f'token_network_address: {pex(new_balance_proof.token_network_identifier)}.' ) assert channel_state, msg balance = channel.get_balance( sender=channel_state.our_state, receiver=channel_state.partner_state, ) if balance < MONITORING_MIN_CAPACITY: log.warn( f'Skipping update to Monitoring service. ' f'Available balance of {balance} is less than configured ' f'minimum capacity of {MONITORING_MIN_CAPACITY}', ) return rei_balance = raiden.user_deposit.effective_balance( raiden.address, 'latest') if rei_balance < MONITORING_REWARD: rdn_balance = to_rdn(rei_balance) rdn_reward = to_rdn(MONITORING_REWARD) log.warn( f'Skipping update to Monitoring service. ' f'Your deposit balance {rdn_balance} is less than ' f'the required monitoring service reward of {rdn_reward}', ) return log.info( 'Received new balance proof, creating message for Monitoring Service.', balance_proof=new_balance_proof, ) monitoring_message = RequestMonitoring.from_balance_proof_signed_state( new_balance_proof, MONITORING_REWARD, ) monitoring_message.sign(raiden.signer) raiden.transport.send_global( constants.MONITORING_BROADCASTING_ROOM, monitoring_message, )
def wait_for_channel_in_states( raiden: "RaidenService", payment_network_id: PaymentNetworkID, token_address: TokenAddress, channel_ids: List[ChannelID], retry_timeout: float, target_states: Sequence[str], ) -> None: """Wait until all channels are in `target_states`. Raises: ValueError: If the token_address is not registered in the payment_network. Note: This does not time out, use gevent.Timeout. """ chain_state = views.state_from_raiden(raiden) token_network = views.get_token_network_by_token_address( chain_state=chain_state, payment_network_id=payment_network_id, token_address=token_address) if token_network is None: raise ValueError( f"The token {token_address} is not registered on the network {payment_network_id}." ) token_network_address = token_network.address list_cannonical_ids = [ CanonicalIdentifier( chain_identifier=chain_state.chain_id, token_network_address=token_network_address, channel_identifier=channel_identifier, ) for channel_identifier in channel_ids ] while list_cannonical_ids: assert raiden, ALARM_TASK_ERROR_MSG assert raiden.alarm, ALARM_TASK_ERROR_MSG canonical_id = list_cannonical_ids[-1] chain_state = views.state_from_raiden(raiden) channel_state = views.get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=canonical_id) channel_is_settled = (channel_state is None or channel.get_status(channel_state) in target_states) if channel_is_settled: list_cannonical_ids.pop() else: gevent.sleep(retry_timeout)
def handle_contract_send_channelclose( raiden: "RaidenService", chain_state: ChainState, channel_close_event: ContractSendChannelClose, ) -> None: balance_proof = channel_close_event.balance_proof if balance_proof: nonce = balance_proof.nonce balance_hash = balance_proof.balance_hash signature_in_proof = balance_proof.signature message_hash = balance_proof.message_hash canonical_identifier = balance_proof.canonical_identifier else: nonce = Nonce(0) balance_hash = EMPTY_BALANCE_HASH signature_in_proof = EMPTY_SIGNATURE message_hash = EMPTY_MESSAGE_HASH canonical_identifier = channel_close_event.canonical_identifier closing_data = pack_signed_balance_proof( msg_type=MessageTypeId.BALANCE_PROOF, nonce=nonce, balance_hash=balance_hash, additional_hash=message_hash, canonical_identifier=canonical_identifier, partner_signature=signature_in_proof, ) our_signature = raiden.signer.sign(data=closing_data) confirmed_block_identifier = state_from_raiden(raiden).block_hash channel_state = get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=channel_close_event.canonical_identifier) if channel_state is None: raise RaidenUnrecoverableError( "ContractSendChannelClose for inexesting channel.") channel_proxy = raiden.proxy_manager.payment_channel( channel_state=channel_state, block_identifier=confirmed_block_identifier) channel_proxy.close( nonce=nonce, balance_hash=balance_hash, additional_hash=message_hash, non_closing_signature=signature_in_proof, closing_signature=our_signature, block_identifier=channel_close_event.triggered_by_block_hash, )
def handle_channel_new_balance(raiden: 'RaidenService', event: Event): data = event.event_data args = data['args'] block_number = data['block_number'] block_hash = data['block_hash'] channel_identifier = args['channel_identifier'] token_network_identifier = event.originating_contract participant_address = args['participant'] total_deposit = args['total_deposit'] transaction_hash = data['transaction_hash'] chain_state = views.state_from_raiden(raiden) previous_channel_state = views.get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=CanonicalIdentifier( chain_identifier=chain_state.chain_id, token_network_address=token_network_identifier, channel_identifier=channel_identifier, ), ) # Channels will only be registered if this node is a participant if previous_channel_state is not None: previous_balance = previous_channel_state.our_state.contract_balance balance_was_zero = previous_balance == 0 deposit_transaction = TransactionChannelNewBalance( participant_address, total_deposit, block_number, ) newbalance_statechange = ContractReceiveChannelNewBalance( transaction_hash=transaction_hash, canonical_identifier=previous_channel_state.canonical_identifier, deposit_transaction=deposit_transaction, block_number=block_number, block_hash=block_hash, ) raiden.handle_and_track_state_change(newbalance_statechange) if balance_was_zero and participant_address != raiden.address: connection_manager = raiden.connection_manager_for_token_network( token_network_identifier, ) join_channel = gevent.spawn( connection_manager.join_channel, participant_address, total_deposit, ) raiden.add_pending_greenlet(join_channel)
def update_monitoring_service_from_balance_proof( raiden: "RaidenService", chain_state: ChainState, new_balance_proof: BalanceProofSignedState, non_closing_participant: Address, ) -> None: if raiden.config.services.monitoring_enabled is False: return msg = f"Monitoring is enabled but the default monitoring service address is None." assert raiden.default_msc_address is not None, msg channel_state = views.get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=new_balance_proof.canonical_identifier) msg = ( f"Failed to update monitoring service due to inability to find " f"channel: {new_balance_proof.channel_identifier} " f"token_network_address: {to_checksum_address(new_balance_proof.token_network_address)}." ) assert channel_state, msg msg = f"Monitoring is enabled but the `UserDeposit` contract is None." assert raiden.user_deposit is not None, msg rei_balance = raiden.user_deposit.effective_balance( raiden.address, BLOCK_ID_LATEST) if rei_balance < MONITORING_REWARD: rdn_balance = to_rdn(rei_balance) rdn_reward = to_rdn(MONITORING_REWARD) log.warning(f"Skipping update to Monitoring service. " f"Your deposit balance {rdn_balance} is less than " f"the required monitoring service reward of {rdn_reward}") return log.info( "Received new balance proof, creating message for Monitoring Service.", node=to_checksum_address(raiden.address), balance_proof=new_balance_proof, ) monitoring_message = RequestMonitoring.from_balance_proof_signed_state( balance_proof=new_balance_proof, non_closing_participant=non_closing_participant, reward_amount=MONITORING_REWARD, monitoring_service_contract_address=raiden.default_msc_address, ) monitoring_message.sign(raiden.signer) raiden.transport.broadcast(constants.MONITORING_BROADCASTING_ROOM, monitoring_message)
def handle_contract_send_channelupdate( raiden: "RaidenService", channel_update_event: ContractSendChannelUpdateTransfer) -> None: balance_proof = channel_update_event.balance_proof if balance_proof: canonical_identifier = balance_proof.canonical_identifier chain_state = state_from_raiden(raiden) confirmed_block_identifier = chain_state.block_hash channel_state = get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=canonical_identifier) if channel_state is None: raise RaidenUnrecoverableError( "ContractSendChannelUpdateTransfer for inexesting channel." ) channel = raiden.proxy_manager.payment_channel( channel_state=channel_state, block_identifier=confirmed_block_identifier) non_closing_data = pack_signed_balance_proof( msg_type=MessageTypeId.BALANCE_PROOF_UPDATE, nonce=balance_proof.nonce, balance_hash=balance_proof.balance_hash, additional_hash=balance_proof.message_hash, canonical_identifier=canonical_identifier, partner_signature=balance_proof.signature, ) our_signature = raiden.signer.sign(data=non_closing_data) try: channel.update_transfer( nonce=balance_proof.nonce, balance_hash=balance_proof.balance_hash, additional_hash=balance_proof.message_hash, partner_signature=balance_proof.signature, signature=our_signature, block_identifier=channel_update_event. triggered_by_block_hash, ) except InsufficientEth as e: raise RaidenUnrecoverableError( f"{str(e)}\n" "CAUTION: This happened when updating our side of the channel " "during a channel settlement. You are in immediate danger of " "losing funds in this channel.") from e
def get_contractreceiveupdatetransfer_data_from_event( chain_state: ChainState, event: DecodedEvent ) -> Optional[NettingChannelState]: data = event.event_data args = data["args"] channel_identifier = args["channel_identifier"] channel_state = views.get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=CanonicalIdentifier( chain_identifier=chain_state.chain_id, token_network_address=TokenNetworkAddress(event.originating_contract), channel_identifier=channel_identifier, ), ) return channel_state
def channel_state_until_state_change( raiden, payment_network_identifier: typing.PaymentNetworkID, token_address: typing.TokenAddress, channel_identifier: typing.ChannelID, state_change_identifier: int, ) -> typing.Optional[NettingChannelState]: """ Go through WAL state changes until a certain balance hash is found. """ wal = restore_to_state_change( transition_function=node.state_transition, storage=raiden.wal.storage, state_change_identifier=state_change_identifier, ) msg = 'There is a state change, therefore the state must not be None' assert wal.state_manager.current_state is not None, msg chain_state = wal.state_manager.current_state token_network = views.get_token_network_by_token_address( chain_state=chain_state, payment_network_id=payment_network_identifier, token_address=token_address, ) if not token_network: return None token_network_address = token_network.address canonical_identifier = CanonicalIdentifier( chain_identifier=chain_state.chain_id, token_network_address=token_network_address, channel_identifier=channel_identifier, ) channel_state = views.get_channelstate_by_canonical_identifier( chain_state=wal.state_manager.current_state, canonical_identifier=canonical_identifier, ) if not channel_state: raise RaidenUnrecoverableError( f"Channel was not found before state_change {state_change_identifier}", ) return channel_state
def handle_channel_closed(raiden: "RaidenService", event: Event): token_network_identifier = event.originating_contract data = event.event_data block_number = data["block_number"] args = data["args"] channel_identifier = args["channel_identifier"] transaction_hash = data["transaction_hash"] block_hash = data["block_hash"] chain_state = views.state_from_raiden(raiden) channel_state = views.get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=CanonicalIdentifier( chain_identifier=chain_state.chain_id, token_network_address=token_network_identifier, channel_identifier=channel_identifier, ), ) channel_closed: StateChange if channel_state: # The from address is included in the ChannelClosed event as the # closing_participant field channel_closed = ContractReceiveChannelClosed( transaction_hash=transaction_hash, transaction_from=args["closing_participant"], canonical_identifier=channel_state.canonical_identifier, block_number=block_number, block_hash=block_hash, ) raiden.handle_and_track_state_change(channel_closed) else: # This is a channel close event of a channel we're not a participant of route_closed = ContractReceiveRouteClosed( transaction_hash=transaction_hash, canonical_identifier=CanonicalIdentifier( chain_identifier=chain_state.chain_id, token_network_address=token_network_identifier, channel_identifier=channel_identifier, ), block_number=block_number, block_hash=block_hash, ) raiden.handle_and_track_state_change(route_closed)
def get_contractreceivechannelclosed_data_from_event( chain_state: "ChainState", event: DecodedEvent) -> Optional[CanonicalIdentifier]: token_network_address = TokenNetworkAddress(event.originating_contract) data = event.event_data args = data["args"] channel_identifier = args["channel_identifier"] channel_state = views.get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=CanonicalIdentifier( chain_identifier=chain_state.chain_id, token_network_address=token_network_address, channel_identifier=channel_identifier, ), ) if channel_state: return channel_state.canonical_identifier return None
def handle_contract_receive_channel_closed( chain_state: ChainState, state_change: ContractReceiveChannelClosed ) -> TransitionResult[ChainState]: # cleanup queue for channel canonical_identifier = CanonicalIdentifier( chain_identifier=chain_state.chain_id, token_network_address=state_change.token_network_address, channel_identifier=state_change.channel_identifier, ) channel_state = views.get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=canonical_identifier ) if channel_state: queue_id = QueueIdentifier( recipient=channel_state.partner_state.address, canonical_identifier=canonical_identifier, ) if queue_id in chain_state.queueids_to_queues: chain_state.queueids_to_queues.pop(queue_id) return handle_token_network_action(chain_state=chain_state, state_change=state_change)
def channel_state_until_state_change( raiden, canonical_identifier: CanonicalIdentifier, state_change_identifier: int) -> typing.Optional[NettingChannelState]: """ Go through WAL state changes until a certain balance hash is found. """ wal = restore_to_state_change( transition_function=node.state_transition, storage=raiden.wal.storage, state_change_identifier=state_change_identifier, ) msg = "There is a state change, therefore the state must not be None" assert wal.state_manager.current_state is not None, msg chain_state = wal.state_manager.current_state channel_state = views.get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=canonical_identifier) if not channel_state: raise RaidenUnrecoverableError( f"Channel was not found before state_change {state_change_identifier}" ) return channel_state
def payment_channel_open_and_deposit( app0: App, app1: App, token_address: TokenAddress, deposit: TokenAmount, settle_timeout: BlockTimeout, ) -> None: """ Open a new channel with app0 and app1 as participants """ assert token_address block_identifier: BlockIdentifier if app0.raiden.wal: block_identifier = views.get_confirmed_blockhash(app0.raiden) else: block_identifier = BLOCK_ID_LATEST token_network_address = app0.raiden.default_registry.get_token_network( token_address=token_address, block_identifier=block_identifier) assert token_network_address, "request a channel for an unregistered token" token_network_proxy = app0.raiden.proxy_manager.token_network( token_network_address, block_identifier=BLOCK_ID_LATEST) channel_identifier, _, _ = token_network_proxy.new_netting_channel( partner=app1.raiden.address, settle_timeout=settle_timeout, given_block_identifier=block_identifier, ) assert channel_identifier if deposit != 0: for app, partner in [(app0, app1), (app1, app0)]: waiting.wait_for_newchannel( raiden=app.raiden, token_network_registry_address=app.raiden.default_registry. address, token_address=token_address, partner_address=partner.raiden.address, retry_timeout=0.5, ) chain_state = state_from_raiden(app.raiden) canonical_identifier = CanonicalIdentifier( chain_identifier=chain_state.chain_id, token_network_address=token_network_proxy.address, channel_identifier=channel_identifier, ) channel_state = get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=canonical_identifier) assert channel_state, "nodes dont share a channel" # Use each app's own chain because of the private key / local signing token = app.raiden.proxy_manager.token(token_address, BLOCK_ID_LATEST) payment_channel_proxy = app.raiden.proxy_manager.payment_channel( channel_state=channel_state, block_identifier=BLOCK_ID_LATEST) # This check can succeed and the deposit still fail, if channels are # openned in parallel previous_balance = token.balance_of(app.raiden.address) assert previous_balance >= deposit # the payment channel proxy will call approve # token.approve(token_network_proxy.address, deposit) payment_channel_proxy.approve_and_set_total_deposit( total_deposit=deposit, block_identifier=BLOCK_ID_LATEST) # Balance must decrease by at least but not exactly `deposit` amount, # because channels can be openned in parallel new_balance = token.balance_of(app.raiden.address) assert new_balance <= previous_balance - deposit check_channel(app0, app1, token_network_proxy.address, settle_timeout, deposit)
def handle_contract_send_channelsettle( raiden: "RaidenService", channel_settle_event: ContractSendChannelSettle) -> None: assert raiden.wal, "The Raiden Service must be initialize to handle events" canonical_identifier = CanonicalIdentifier( chain_identifier=raiden.rpc_client.chain_id, token_network_address=channel_settle_event.token_network_address, channel_identifier=channel_settle_event.channel_identifier, ) triggered_by_block_hash = channel_settle_event.triggered_by_block_hash chain_state = state_from_raiden(raiden) channel_state = get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=canonical_identifier) if channel_state is None: raise RaidenUnrecoverableError( "ContractSendChannelSettle for inexesting channel.") confirmed_block_identifier = chain_state.block_hash payment_channel: PaymentChannel = raiden.proxy_manager.payment_channel( channel_state=channel_state, block_identifier=confirmed_block_identifier) token_network_proxy: TokenNetwork = payment_channel.token_network try: participants_details = token_network_proxy.detail_participants( participant1=payment_channel.participant1, participant2=payment_channel.participant2, block_identifier=triggered_by_block_hash, channel_identifier=channel_settle_event.channel_identifier, ) except ValueError: # The triggered_by_block_hash block was pruned. participants_details = token_network_proxy.detail_participants( participant1=payment_channel.participant1, participant2=payment_channel.participant2, block_identifier=BLOCK_ID_LATEST, channel_identifier=channel_settle_event.channel_identifier, ) our_details = participants_details.our_details partner_details = participants_details.partner_details log_details = { "chain_id": canonical_identifier.chain_identifier, "token_network_address": canonical_identifier.token_network_address, "channel_identifier": canonical_identifier.channel_identifier, "node": to_checksum_address(raiden.address), "partner": to_checksum_address(partner_details.address), "our_deposit": our_details.deposit, "our_withdrawn": our_details.withdrawn, "our_is_closer": our_details.is_closer, "our_balance_hash": to_hex(our_details.balance_hash), "our_nonce": our_details.nonce, "our_locksroot": to_hex(our_details.locksroot), "our_locked_amount": our_details.locked_amount, "partner_deposit": partner_details.deposit, "partner_withdrawn": partner_details.withdrawn, "partner_is_closer": partner_details.is_closer, "partner_balance_hash": to_hex(partner_details.balance_hash), "partner_nonce": partner_details.nonce, "partner_locksroot": to_hex(partner_details.locksroot), "partner_locked_amount": partner_details.locked_amount, } if our_details.balance_hash != EMPTY_BALANCE_HASH: event_record = get_event_with_balance_proof_by_balance_hash( storage=raiden.wal.storage, canonical_identifier=canonical_identifier, balance_hash=our_details.balance_hash, recipient=participants_details.partner_details.address, ) if event_record is None: log.critical("our balance proof not found", **log_details) raise RaidenUnrecoverableError( "Our balance proof could not be found in the database") our_balance_proof = event_record.data.balance_proof # type: ignore our_transferred_amount = our_balance_proof.transferred_amount our_locked_amount = our_balance_proof.locked_amount our_locksroot = our_balance_proof.locksroot else: our_transferred_amount = 0 our_locked_amount = 0 our_locksroot = LOCKSROOT_OF_NO_LOCKS if partner_details.balance_hash != EMPTY_BALANCE_HASH: state_change_record = get_state_change_with_balance_proof_by_balance_hash( storage=raiden.wal.storage, canonical_identifier=canonical_identifier, balance_hash=partner_details.balance_hash, sender=participants_details.partner_details.address, ) if state_change_record is None: log.critical("partner balance proof not found", **log_details) raise RaidenUnrecoverableError( "Partner balance proof could not be found in the database") partner_balance_proof = state_change_record.data.balance_proof # type: ignore partner_transferred_amount = partner_balance_proof.transferred_amount partner_locked_amount = partner_balance_proof.locked_amount partner_locksroot = partner_balance_proof.locksroot else: partner_transferred_amount = 0 partner_locked_amount = 0 partner_locksroot = LOCKSROOT_OF_NO_LOCKS try: payment_channel.settle( transferred_amount=our_transferred_amount, locked_amount=our_locked_amount, locksroot=our_locksroot, partner_transferred_amount=partner_transferred_amount, partner_locked_amount=partner_locked_amount, partner_locksroot=partner_locksroot, block_identifier=triggered_by_block_hash, ) except InsufficientEth as e: raise RaidenUnrecoverableError(str(e)) from e
def handle_channel_settled(raiden: 'RaidenService', event: Event): data = event.event_data token_network_identifier = event.originating_contract channel_identifier = data['args']['channel_identifier'] block_number = data['block_number'] block_hash = data['block_hash'] transaction_hash = data['transaction_hash'] chain_state = views.state_from_raiden(raiden) channel_state = views.get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=CanonicalIdentifier( chain_identifier=chain_state.chain_id, token_network_address=token_network_identifier, channel_identifier=channel_identifier, ), ) # This may happen for two reasons: # - This node is not a participant for the given channel (normal operation, # the event should be ignored). # - Something went wrong in our code and the channel state was cleared # before settle (a bug, this should raise an exception on development # mode). # Because we cannot distinguish the two cases, assume the channel is not of # interest and ignore the event. if not channel_state: return """ This is resolving a corner case where the current node view of the channel state does not reflect what the blockchain contains. The corner case goes as follows in a setup of nodes: A -> B: - A sends out a LockedTransfer to B - B sends a refund to A - B goes offline - A sends LockExpired to B Here: (1) the lock is removed from A's state (2) B never received the message - A closes the channel with B's refund - B comes back online and calls updateNonClosingBalanceProof with A's LockedTransfer (LockExpired was never processed). - When channel is settled, B unlocks it's refund transfer lock provided that it gains from doing so. - A does NOT try to unlock its lock because its side of the channel state is empty (lock expired and was removed). The above is resolved by providing the state machine with the onchain locksroots for both participants in the channel so that the channel state is updated to store these locksroots. In `raiden_event_handler:handle_contract_send_channelunlock`, those values are used to restore the channel state back to where the locksroots values existed and this channel state is used to calculate the gain and potentially perform unlocks in case there is value to be gained. """ canonical_identifier = CanonicalIdentifier( chain_identifier=CHAIN_ID_UNSPECIFIED, token_network_address=token_network_identifier, channel_identifier=channel_identifier, ) our_locksroot, partner_locksroot = get_onchain_locksroots( chain=raiden.chain, canonical_identifier=canonical_identifier, participant1=channel_state.our_state.address, participant2=channel_state.partner_state.address, block_identifier=block_hash, ) channel_settled = ContractReceiveChannelSettled( transaction_hash=transaction_hash, canonical_identifier=channel_state.canonical_identifier, our_onchain_locksroot=our_locksroot, partner_onchain_locksroot=partner_locksroot, block_number=block_number, block_hash=block_hash, ) raiden.handle_and_track_state_change(channel_settled)
def wait_for_channel_in_states( raiden: "RaidenService", token_network_registry_address: TokenNetworkRegistryAddress, token_address: TokenAddress, channel_ids: List[ChannelID], retry_timeout: float, target_states: Sequence[ChannelState], ) -> None: """Wait until all channels are in `target_states`. Raises: ValueError: If the token_address is not registered in the token_network_registry. Note: This does not time out, use gevent.Timeout. """ chain_state = views.state_from_raiden(raiden) token_network = views.get_token_network_by_token_address( chain_state=chain_state, token_network_registry_address=token_network_registry_address, token_address=token_address, ) if token_network is None: raise ValueError( f"The token {to_checksum_address(token_address)} is not registered on " f"the network {to_checksum_address(token_network_registry_address)}." ) token_network_address = token_network.address list_cannonical_ids = [ CanonicalIdentifier( chain_identifier=chain_state.chain_id, token_network_address=token_network_address, channel_identifier=channel_identifier, ) for channel_identifier in channel_ids ] log_details = { "token_network_registry_address": to_checksum_address(token_network_registry_address), "token_address": to_checksum_address(token_address), "list_cannonical_ids": list_cannonical_ids, "target_states": target_states, } while list_cannonical_ids: assert raiden, ALARM_TASK_ERROR_MSG assert raiden.alarm, ALARM_TASK_ERROR_MSG canonical_id = list_cannonical_ids[-1] chain_state = views.state_from_raiden(raiden) channel_state = views.get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=canonical_id) channel_is_settled = (channel_state is None or channel.get_status(channel_state) in target_states) if channel_is_settled: list_cannonical_ids.pop() else: log.debug("wait_for_channel_in_states", **log_details) gevent.sleep(retry_timeout)
def get_contractreceivechannelsettled_data_from_event( proxy_manager: ProxyManager, chain_state: ChainState, event: DecodedEvent, current_confirmed_head: BlockNumber, ) -> Optional[ChannelSettleState]: data = event.event_data token_network_address = TokenNetworkAddress(event.originating_contract) channel_identifier = data["args"]["channel_identifier"] block_hash = data["block_hash"] canonical_identifier = CanonicalIdentifier( chain_identifier=chain_state.chain_id, token_network_address=token_network_address, channel_identifier=channel_identifier, ) channel_state = views.get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=canonical_identifier) # This may happen for two reasons: # - This node is not a participant for the given channel (normal operation, # the event should be ignored). # - Something went wrong in our code and the channel state was cleared # before settle (a bug, this should raise an exception on development # mode). # Because we cannot distinguish the two cases, assume the channel is not of # interest and ignore the event. if not channel_state: return None # Recover the locksroot from the blockchain to fix data races. Check # get_onchain_locksroots for details. try: # First try to query the unblinded state. This way the # ContractReceiveChannelSettled's locksroots will match the values # provided during settle. our_locksroot, partner_locksroot = get_onchain_locksroots( proxy_manager=proxy_manager, channel_state=channel_state, participant1=channel_state.our_state.address, participant2=channel_state.partner_state.address, block_identifier=block_hash, ) except ValueError: # State pruning handling. The block which generate the # ChannelSettled event may have been pruned, because of this the # RPC call raised ValueError. # # The solution is to query the channel's state from the latest # *confirmed* block, this /may/ create a ContractReceiveChannelSettled # with the wrong locksroot (i.e. not the locksroot used during the call # to settle). However this is fine, because at this point the channel # is settled, it is known that the locksroot can not be reverted # without an unlock, and because the unlocks are fair it doesn't matter # who called it, only if there are tokens locked in the settled # channel. our_locksroot, partner_locksroot = get_onchain_locksroots( proxy_manager=proxy_manager, channel_state=channel_state, participant1=channel_state.our_state.address, participant2=channel_state.partner_state.address, block_identifier=current_confirmed_head, ) return ChannelSettleState(canonical_identifier, our_locksroot, partner_locksroot)
def handle_contract_send_channelunlock( raiden: "RaidenService", chain_state: ChainState, channel_unlock_event: ContractSendChannelBatchUnlock, ) -> None: assert raiden.wal, "The Raiden Service must be initialize to handle events" canonical_identifier = channel_unlock_event.canonical_identifier token_network_address = canonical_identifier.token_network_address channel_identifier = canonical_identifier.channel_identifier participant = channel_unlock_event.sender channel_state = get_channelstate_by_canonical_identifier( chain_state=state_from_raiden(raiden), canonical_identifier=canonical_identifier) if channel_state is None: raise RaidenUnrecoverableError( "ContractSendChannelBatchUnlock for inexesting channel.") confirmed_block_identifier = state_from_raiden(raiden).block_hash payment_channel: PaymentChannel = raiden.proxy_manager.payment_channel( channel_state=channel_state, block_identifier=confirmed_block_identifier) channel_state = get_channelstate_by_token_network_and_partner( chain_state=chain_state, token_network_address=token_network_address, partner_address=participant, ) if not channel_state: # channel was cleaned up already due to an unlock raise RaidenUnrecoverableError( f"Failed to find channel state with partner:" f"{to_checksum_address(participant)}, " f"token_network:{to_checksum_address(token_network_address)}") our_address = channel_state.our_state.address our_locksroot = channel_state.our_state.onchain_locksroot partner_address = channel_state.partner_state.address partner_locksroot = channel_state.partner_state.onchain_locksroot # we want to unlock because there are on-chain unlocked locks search_events = our_locksroot != LOCKSROOT_OF_NO_LOCKS # we want to unlock, because there are unlocked/unclaimed locks search_state_changes = partner_locksroot != LOCKSROOT_OF_NO_LOCKS if not search_events and not search_state_changes: # In the case that someone else sent the unlock we do nothing # Check https://github.com/raiden-network/raiden/issues/3152 # for more details log.warning( "Onchain unlock already mined", canonical_identifier=canonical_identifier, channel_identifier=canonical_identifier.channel_identifier, participant=to_checksum_address(participant), ) return if search_state_changes: state_change_record = get_state_change_with_balance_proof_by_locksroot( storage=raiden.wal.storage, canonical_identifier=canonical_identifier, locksroot=partner_locksroot, sender=partner_address, ) if state_change_record is None: raise RaidenUnrecoverableError( f"Failed to find state that matches the current channel locksroots. " f"chain_id:{raiden.rpc_client.chain_id} " f"token_network:{to_checksum_address(token_network_address)} " f"channel:{channel_identifier} " f"participant:{to_checksum_address(participant)} " f"our_locksroot:{to_hex(our_locksroot)} " f"partner_locksroot:{to_hex(partner_locksroot)} ") state_change_identifier = state_change_record.state_change_identifier restored_channel_state = channel_state_until_state_change( raiden=raiden, canonical_identifier=canonical_identifier, state_change_identifier=state_change_identifier, ) gain = get_batch_unlock_gain(restored_channel_state) skip_unlock = (restored_channel_state.partner_state.address == participant and gain.from_partner_locks == 0) if not skip_unlock: unlock( payment_channel=payment_channel, end_state=restored_channel_state.partner_state, sender=partner_address, receiver=our_address, given_block_identifier=channel_unlock_event. triggered_by_block_hash, ) if search_events: event_record = get_event_with_balance_proof_by_locksroot( storage=raiden.wal.storage, canonical_identifier=canonical_identifier, locksroot=our_locksroot, recipient=partner_address, ) if event_record is None: raise RaidenUnrecoverableError( f"Failed to find event that match current channel locksroots. " f"chain_id:{raiden.rpc_client.chain_id} " f"token_network:{to_checksum_address(token_network_address)} " f"channel:{channel_identifier} " f"participant:{to_checksum_address(participant)} " f"our_locksroot:{to_hex(our_locksroot)} " f"partner_locksroot:{to_hex(partner_locksroot)} ") state_change_identifier = event_record.state_change_identifier restored_channel_state = channel_state_until_state_change( raiden=raiden, canonical_identifier=canonical_identifier, state_change_identifier=state_change_identifier, ) gain = get_batch_unlock_gain(restored_channel_state) skip_unlock = (restored_channel_state.our_state.address == participant and gain.from_our_locks == 0) if not skip_unlock: try: unlock( payment_channel=payment_channel, end_state=restored_channel_state.our_state, sender=our_address, receiver=partner_address, given_block_identifier=channel_unlock_event. triggered_by_block_hash, ) except InsufficientEth as e: raise RaidenUnrecoverableError(str(e)) from e
def handle_channel_settled(raiden: "RaidenService", event: Event): data = event.event_data token_network_identifier = event.originating_contract channel_identifier = data["args"]["channel_identifier"] block_number = data["block_number"] block_hash = data["block_hash"] transaction_hash = data["transaction_hash"] chain_state = views.state_from_raiden(raiden) channel_state = views.get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=CanonicalIdentifier( chain_identifier=chain_state.chain_id, token_network_address=token_network_identifier, channel_identifier=channel_identifier, ), ) # This may happen for two reasons: # - This node is not a participant for the given channel (normal operation, # the event should be ignored). # - Something went wrong in our code and the channel state was cleared # before settle (a bug, this should raise an exception on development # mode). # Because we cannot distinguish the two cases, assume the channel is not of # interest and ignore the event. if not channel_state: return # Recover the locksroot from the blockchain to fix data races. Check # get_onchain_locksroots for details. try: # First try to query the unblinded state. This way the # ContractReceiveChannelSettled's locksroots will match the values # provided during settle. our_locksroot, partner_locksroot = get_onchain_locksroots( chain=raiden.chain, canonical_identifier=channel_state.canonical_identifier, participant1=channel_state.our_state.address, participant2=channel_state.partner_state.address, block_identifier=block_hash, ) except ValueError: # State pruning handling. The block which generate the ChannelSettled # event may have been pruned, because of this the RPC call will raises # a ValueError. # # The solution is to query the channel's state from the latest block, # this /may/ create a ContractReceiveChannelSettled with the wrong # locksroot (i.e. not the locksroot used during the call to settle). # However this is fine, because at this point the channel is settled, # it is known that the locksroot can not be reverted without an unlock, # and because the unlocks are fare it doesn't matter who called it, # only if there are tokens locked in the settled channel. our_locksroot, partner_locksroot = get_onchain_locksroots( chain=raiden.chain, canonical_identifier=channel_state.canonical_identifier, participant1=channel_state.our_state.address, participant2=channel_state.partner_state.address, block_identifier="latest", ) channel_settled = ContractReceiveChannelSettled( transaction_hash=transaction_hash, canonical_identifier=channel_state.canonical_identifier, our_onchain_locksroot=our_locksroot, partner_onchain_locksroot=partner_locksroot, block_number=block_number, block_hash=block_hash, ) raiden.handle_and_track_state_change(channel_settled)
def update_monitoring_service_from_balance_proof( raiden: "RaidenService", chain_state: ChainState, new_balance_proof: BalanceProofSignedState, non_closing_participant: Address, ) -> None: if raiden.config.services.monitoring_enabled is False: return msg = "Monitoring is enabled but the default monitoring service address is None." assert raiden.default_msc_address is not None, msg channel_state = views.get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=new_balance_proof.canonical_identifier) msg = ( f"Failed to update monitoring service due to inability to find " f"channel: {new_balance_proof.channel_identifier} " f"token_network_address: {to_checksum_address(new_balance_proof.token_network_address)}." ) assert channel_state, msg msg = "Monitoring is enabled but the `UserDeposit` contract is None." assert raiden.default_user_deposit is not None, msg rei_balance = raiden.default_user_deposit.effective_balance( raiden.address, BLOCK_ID_LATEST) if rei_balance < MONITORING_REWARD: rdn_balance = to_rdn(rei_balance) rdn_reward = to_rdn(MONITORING_REWARD) log.warning(f"Skipping update to Monitoring service. " f"Your deposit balance {rdn_balance} is less than " f"the required monitoring service reward of {rdn_reward}") return # In production there should be no MonitoringRequest if # channel balance is below a certain threshold. This is # a naive approach that needs to be worked on in the future if raiden.config.environment_type == Environment.PRODUCTION: message = ("Skipping update to Monitoring service. " "Your channel balance {channel_balance} is less than " "the required minimum balance of {min_balance} ") dai_token_network_address = views.get_token_network_address_by_token_address( chain_state=chain_state, token_network_registry_address=raiden.default_registry.address, token_address=DAI_TOKEN_ADDRESS, ) weth_token_network_address = views.get_token_network_address_by_token_address( chain_state=chain_state, token_network_registry_address=raiden.default_registry.address, token_address=WETH_TOKEN_ADDRESS, ) channel_balance = get_balance( sender=channel_state.our_state, receiver=channel_state.partner_state, ) if channel_state.canonical_identifier.token_network_address == dai_token_network_address: if channel_balance < MIN_MONITORING_AMOUNT_DAI: data = dict( channel_balance=channel_balance, min_balance=MIN_MONITORING_AMOUNT_DAI, channel_id=channel_state.canonical_identifier. channel_identifier, token_address=to_checksum_address(DAI_TOKEN_ADDRESS), ) log.warning(message.format(**data), **data) return if channel_state.canonical_identifier.token_network_address == weth_token_network_address: if channel_balance < MIN_MONITORING_AMOUNT_WETH: data = dict( channel_balance=channel_balance, min_balance=MIN_MONITORING_AMOUNT_WETH, channel_id=channel_state.canonical_identifier. channel_identifier, token_address=to_checksum_address(WETH_TOKEN_ADDRESS), ) log.warning(message.format(**data), **data) return log.info( "Received new balance proof, creating message for Monitoring Service.", node=to_checksum_address(raiden.address), balance_proof=new_balance_proof, ) monitoring_message = RequestMonitoring.from_balance_proof_signed_state( balance_proof=new_balance_proof, non_closing_participant=non_closing_participant, reward_amount=MONITORING_REWARD, monitoring_service_contract_address=raiden.default_msc_address, ) monitoring_message.sign(raiden.signer) raiden.transport.broadcast(constants.MONITORING_BROADCASTING_ROOM, monitoring_message)
def subdispatch_to_paymenttask( chain_state: ChainState, state_change: StateChange, secrethash: SecretHash) -> TransitionResult[ChainState]: block_number = chain_state.block_number block_hash = chain_state.block_hash sub_task = chain_state.payment_mapping.secrethashes_to_task.get(secrethash) events: List[Event] = list() if sub_task: pseudo_random_generator = chain_state.pseudo_random_generator sub_iteration: Union[TransitionResult[InitiatorPaymentState], TransitionResult[MediatorTransferState], TransitionResult[TargetTransferState], ] if isinstance(sub_task, InitiatorTask): token_network_identifier = sub_task.token_network_identifier token_network_state = get_token_network_by_address( chain_state, token_network_identifier) if token_network_state: sub_iteration = initiator_manager.state_transition( sub_task.manager_state, state_change, token_network_state.channelidentifiers_to_channels, pseudo_random_generator, block_number, ) events = sub_iteration.events if sub_iteration.new_state is None: del chain_state.payment_mapping.secrethashes_to_task[ secrethash] elif isinstance(sub_task, MediatorTask): token_network_identifier = sub_task.token_network_identifier token_network_state = get_token_network_by_address( chain_state, token_network_identifier) if token_network_state: channelids_to_channels = token_network_state.channelidentifiers_to_channels sub_iteration = mediator.state_transition( mediator_state=sub_task.mediator_state, state_change=state_change, channelidentifiers_to_channels=channelids_to_channels, nodeaddresses_to_networkstates=chain_state. nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=block_number, block_hash=block_hash, ) events = sub_iteration.events if sub_iteration.new_state is None: del chain_state.payment_mapping.secrethashes_to_task[ secrethash] elif isinstance(sub_task, TargetTask): token_network_identifier = sub_task.token_network_identifier channel_identifier = sub_task.channel_identifier channel_state = views.get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=CanonicalIdentifier( chain_identifier=chain_state.chain_id, token_network_address=token_network_identifier, channel_identifier=channel_identifier, ), ) if channel_state: sub_iteration = target.state_transition( target_state=sub_task.target_state, state_change=state_change, channel_state=channel_state, pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) events = sub_iteration.events if sub_iteration.new_state is None: del chain_state.payment_mapping.secrethashes_to_task[ secrethash] return TransitionResult(chain_state, events)