def test_get_token_network_by_address(): test_state = factories.make_chain_state(number_of_channels=3) assert (views.get_token_network_by_address( test_state.chain_state, test_state.token_network_address).address == test_state.token_network_address) unknown_token_network_address = factories.make_address() assert (views.get_token_network_by_address( test_state.chain_state, unknown_token_network_address) is None)
def get_contractreceivechannelnew_data_from_event( chain_state: ChainState, event: DecodedEvent) -> Optional[NewChannelDetails]: token_network_address = TokenNetworkAddress(event.originating_contract) data = event.event_data args = data["args"] participant1 = args["participant1"] participant2 = args["participant2"] our_address = chain_state.our_address if our_address == participant1: partner_address = participant2 elif our_address == participant2: partner_address = participant1 else: # Not a channel which this node is a participant return None token_network_registry = views.get_token_network_registry_by_token_network_address( chain_state, token_network_address) assert token_network_registry is not None, "Token network registry missing" token_network = views.get_token_network_by_address( chain_state=chain_state, token_network_address=token_network_address) assert token_network is not None, "Token network missing" return NewChannelDetails( chain_id=event.chain_id, token_network_registry_address=token_network_registry.address, token_address=token_network.token_address, token_network_address=token_network_address, our_address=our_address, partner_address=partner_address, )
def __init__(self, raiden: "RaidenService", token_network_address: TokenNetworkAddress): self.raiden = raiden chain_state = views.state_from_raiden(raiden) token_network_state = views.get_token_network_by_address( chain_state, token_network_address) token_network_registry = views.get_token_network_registry_by_token_network_address( chain_state, token_network_address) msg = f"Token network for address {to_checksum_address(token_network_address)} not found." assert token_network_state, msg msg = (f"Token network registry for token network address " f"{to_checksum_address(token_network_address)} not found.") assert token_network_registry, msg # TODO: # - Add timeout for transaction polling, used to overwrite the RaidenAPI # defaults # - Add a proper selection strategy (#576) self.funds = 0 self.initial_channel_target = 0 self.joinable_funds_target = 0.0 self.raiden = raiden self.registry_address = token_network_registry.address self.token_network_address = token_network_address self.token_address = token_network_state.token_address self.lock = Semaphore( ) #: protects self.funds and self.initial_channel_target self._retry_greenlet: Optional[Greenlet] = None self.api = RaidenAPI(raiden)
def event_filter_for_payments( event: Event, chain_state: Optional[ChainState] = None, partner_address: Optional[Address] = None, token_address: Optional[TokenAddress] = None, ) -> bool: """Filters payment history related events depending on given arguments - If no other args are given, all payment related events match. - If a token network identifier is given then only payment events for that match. - If a partner is also given then if the event is a payment sent event and the target matches it's returned. If it's a payment received and the initiator matches. then it's returned. - If a token address is given then all events are filtered to be about that token. """ sent_and_target_matches = isinstance( event, (EventPaymentSentFailed, EventPaymentSentSuccess) ) and (partner_address is None or event.target == TargetAddress(partner_address)) received_and_initiator_matches = isinstance(event, EventPaymentReceivedSuccess) and ( partner_address is None or event.initiator == InitiatorAddress(partner_address) ) token_address_matches = True if token_address: assert chain_state, "Filtering for token_address without a chain state is an error" token_network = get_token_network_by_address( chain_state=chain_state, token_network_address=event.token_network_address, # type: ignore ) if not token_network: token_address_matches = False else: token_address_matches = token_address == token_network.token_address return token_address_matches and (sent_and_target_matches or received_and_initiator_matches)
def __init__(self, raiden: "RaidenService", token_network_address: TokenNetworkAddress): self.raiden = raiden chain_state = views.state_from_raiden(raiden) token_network_state = views.get_token_network_by_address( chain_state, token_network_address ) token_network_registry = views.get_token_network_registry_by_token_network_address( chain_state, token_network_address ) assert token_network_state assert token_network_registry # TODO: # - Add timeout for transaction polling, used to overwrite the RaidenAPI # defaults # - Add a proper selection strategy (#576) self.funds = 0 self.initial_channel_target = 0 self.joinable_funds_target = 0.0 self.raiden = raiden self.registry_address = token_network_registry.address self.token_network_address = token_network_address self.token_address = token_network_state.token_address self.lock = Semaphore() #: protects self.funds and self.initial_channel_target self.api = RaidenAPI(raiden)
def serialize(self, chain_state: ChainState, event: TimestampedEvent) -> Dict[str, Any]: serialized_event = self.dump(event) token_network = get_token_network_by_address( chain_state=chain_state, token_network_address=event.wrapped_event.token_network_address, ) assert token_network, "Token network object should be registered if we got events with it" serialized_event["token_address"] = to_checksum_address(token_network.token_address) return serialized_event
def is_channel_registered(node_app: App, partner_app: App, canonical_identifier: CanonicalIdentifier) -> bool: """True if the `node_app` has a channel with `partner_app` in its state.""" token_network = views.get_token_network_by_address( chain_state=views.state_from_app(node_app), token_network_address=canonical_identifier.token_network_address, ) assert token_network is_in_channelid_map = (canonical_identifier.channel_identifier in token_network.channelidentifiers_to_channels) is_in_partner_map = ( canonical_identifier.channel_identifier in token_network.partneraddresses_to_channelidentifiers[ partner_app.raiden.address]) return is_in_channelid_map and is_in_partner_map
def get_best_routes( chain_state: ChainState, token_network_address: TokenNetworkAddress, one_to_n_address: Optional[OneToNAddress], from_address: InitiatorAddress, to_address: TargetAddress, amount: PaymentAmount, previous_address: Optional[Address], pfs_config: Optional[PFSConfig], privkey: PrivateKey, ) -> Tuple[Optional[str], List[RouteState], Optional[UUID]]: token_network = views.get_token_network_by_address(chain_state, token_network_address) assert token_network, "The token network must be validated and exist." # Always use a direct channel if available: # - There are no race conditions and the capacity is guaranteed to be # available. # - There will be no mediation fees # - The transfer will be faster if Address( to_address ) in token_network.partneraddresses_to_channelidentifiers.keys(): for channel_id in token_network.partneraddresses_to_channelidentifiers[ Address(to_address)]: channel_state = token_network.channelidentifiers_to_channels[ channel_id] # direct channels don't have fees payment_with_fee_amount = PaymentWithFeeAmount(amount) is_usable = channel.is_channel_usable_for_new_transfer( channel_state, payment_with_fee_amount, None) if is_usable is channel.ChannelUsability.USABLE: direct_route = RouteState( route=[Address(from_address), Address(to_address)], estimated_fee=FeeAmount(0)) return None, [direct_route], None if pfs_config is None or one_to_n_address is None: log.warning("Pathfinding Service could not be used.") return "Pathfinding Service could not be used.", list(), None # Does any channel have sufficient capacity for the payment? channels = [ token_network.channelidentifiers_to_channels[channel_id] for channels_to_partner in token_network.partneraddresses_to_channelidentifiers.values() for channel_id in channels_to_partner ] for channel_state in channels: payment_with_fee_amount = PaymentWithFeeAmount(amount) is_usable = channel.is_channel_usable_for_new_transfer( channel_state, payment_with_fee_amount, None) if is_usable is channel.ChannelUsability.USABLE: break else: return ("You have no suitable channel to initiate this payment.", list(), None) # Make sure that the PFS knows about the last channel we opened latest_channel_opened_at = 0 for channel_state in token_network.channelidentifiers_to_channels.values(): latest_channel_opened_at = max( latest_channel_opened_at, channel_state.open_transaction.finished_block_number) pfs_error_msg, pfs_routes, pfs_feedback_token = get_best_routes_pfs( chain_state=chain_state, token_network_address=token_network_address, one_to_n_address=one_to_n_address, from_address=from_address, to_address=to_address, amount=amount, previous_address=previous_address, pfs_config=pfs_config, privkey=privkey, pfs_wait_for_block=BlockNumber(latest_channel_opened_at), ) if pfs_error_msg: log.warning( "Request to Pathfinding Service was not successful. " "No routes to the target were found.", pfs_message=pfs_error_msg, ) return pfs_error_msg, list(), None if not pfs_routes: # As of version 0.5 it is possible for the PFS to return an empty # list of routes without an error message. return "PFS could not find any routes", list(), None log.info("Received route(s) from PFS", routes=pfs_routes, feedback_token=pfs_feedback_token) return pfs_error_msg, pfs_routes, pfs_feedback_token
def get_contractreceivechannelbatchunlock_data_from_event( chain_state: ChainState, storage: SerializedSQLiteStorage, event: DecodedEvent) -> Optional[CanonicalIdentifier]: token_network_address = TokenNetworkAddress(event.originating_contract) data = event.event_data args = data["args"] participant1 = args["receiver"] participant2 = args["sender"] locksroot = args["locksroot"] token_network_state = views.get_token_network_by_address( chain_state, token_network_address) msg = f"Could not find token network for address {to_checksum_address(token_network_address)}" assert token_network_state is not None, msg if participant1 == chain_state.our_address: partner = participant2 elif participant2 == chain_state.our_address: partner = participant1 else: return None channel_identifiers = token_network_state.partneraddresses_to_channelidentifiers[ partner] canonical_identifier = None for channel_identifier in channel_identifiers: if partner == args["sender"]: state_change_record = get_state_change_with_balance_proof_by_locksroot( storage=storage, canonical_identifier=CanonicalIdentifier( chain_identifier=chain_state.chain_id, token_network_address=token_network_address, channel_identifier=channel_identifier, ), locksroot=locksroot, sender=partner, ) if state_change_record is not None: canonical_identifier = ( state_change_record.data.balance_proof. canonical_identifier # type: ignore ) break elif partner == args["receiver"]: event_record = get_event_with_balance_proof_by_locksroot( storage=storage, canonical_identifier=CanonicalIdentifier( chain_identifier=chain_state.chain_id, token_network_address=token_network_address, channel_identifier=channel_identifier, ), locksroot=locksroot, recipient=partner, ) if event_record is not None: canonical_identifier = ( event_record.data.balance_proof. canonical_identifier # type: ignore ) break msg = ( f"Can not resolve channel_id for unlock with locksroot {to_hex(locksroot)} and " f"partner {to_checksum_address(partner)}.") if canonical_identifier is None: raise RaidenUnrecoverableError(msg) return canonical_identifier
def test_clear_closed_queue(raiden_network: List[App], token_addresses, network_wait): """ Closing a channel clears the respective message queue. """ app0, app1 = raiden_network hold_event_handler = app1.raiden.raiden_event_handler assert isinstance(hold_event_handler, HoldRaidenEventHandler) registry_address = app0.raiden.default_registry.address token_address = token_addresses[0] chain_state0 = views.state_from_app(app0) token_network_address = views.get_token_network_address_by_token_address( chain_state0, app0.raiden.default_registry.address, token_address) assert token_network_address token_network = views.get_token_network_by_address(chain_state0, token_network_address) assert token_network channel_identifier = get_channelstate(app0, app1, token_network_address).identifier assert (channel_identifier in token_network.partneraddresses_to_channelidentifiers[ app1.raiden.address]) target = app1.raiden.address secret = Secret(keccak(target)) secrethash = sha256_secrethash(secret) hold_event_handler.hold_secretrequest_for(secrethash=secrethash) # make an unconfirmed transfer to ensure the nodes have communicated amount = PaymentAmount(10) payment_identifier = PaymentID(1337) app0.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=amount, target=TargetAddress(target), identifier=payment_identifier, secret=secret, ) app1.raiden.transport.stop() app1.raiden.transport.greenlet.get() # make sure to wait until the queue is created def has_initiator_events(): assert app0.raiden.wal, "raiden server must have been started" initiator_events = app0.raiden.wal.storage.get_events() return search_for_item(initiator_events, SendLockedTransfer, {}) assert wait_until(has_initiator_events, network_wait) # assert the specific queue is present chain_state0 = views.state_from_app(app0) queues0 = views.get_all_messagequeues(chain_state=chain_state0) assert [ (queue_id, queue) for queue_id, queue in queues0.items() if queue_id.recipient == app1.raiden.address and queue_id. canonical_identifier.channel_identifier == channel_identifier and queue ] # A ChannelClose event will be generated, this will be polled by both apps RaidenAPI(app0.raiden).channel_close(registry_address, token_address, app1.raiden.address) with block_offset_timeout(app0.raiden, "Could not get close event"): waiting.wait_for_close( app0.raiden, registry_address, token_address, [channel_identifier], app0.raiden.alarm.sleep_time, ) # assert all queues with this partner are gone or empty chain_state0 = views.state_from_app(app0) queues0 = views.get_all_messagequeues(chain_state=chain_state0) assert not [(queue_id, queue) for queue_id, queue in queues0.items() if queue_id.recipient == app1.raiden.address and queue] chain_state1 = views.state_from_app(app1) queues1 = views.get_all_messagequeues(chain_state=chain_state1) assert not [(queue_id, queue) for queue_id, queue in queues1.items() if queue_id.recipient == app0.raiden.address and queue]
def test_batch_unlock_after_restart(raiden_network, token_addresses, deposit): """Simulate the case where: - A sends B a transfer - B sends A a transfer - Secrets were never revealed - B closes channel - A crashes - Wait for settle - Wait for unlock from B - Restart A At this point, the current unlock logic will try to unlock iff the node gains from unlocking. Which means that the node will try to unlock either side. In the above scenario, each node will unlock its side. This test makes sure that we do NOT invalidate A's unlock transaction based on the ContractReceiveChannelBatchUnlock caused by B's unlock. """ alice_app, bob_app = raiden_network registry_address = alice_app.raiden.default_registry.address token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( chain_state=views.state_from_app(alice_app), token_network_registry_address=alice_app.raiden.default_registry.address, token_address=token_address, ) assert token_network_address timeout = 10 token_network = views.get_token_network_by_address( chain_state=views.state_from_app(alice_app), token_network_address=token_network_address ) assert token_network channel_identifier = get_channelstate(alice_app, bob_app, token_network_address).identifier assert ( channel_identifier in token_network.partneraddresses_to_channelidentifiers[bob_app.raiden.address] ) alice_to_bob_amount = 10 identifier = 1 alice_transfer_secret = Secret(sha3(alice_app.raiden.address)) alice_transfer_secrethash = sha256_secrethash(alice_transfer_secret) bob_transfer_secret = Secret(sha3(bob_app.raiden.address)) bob_transfer_secrethash = sha256_secrethash(bob_transfer_secret) alice_transfer_hold = bob_app.raiden.raiden_event_handler.hold_secretrequest_for( secrethash=alice_transfer_secrethash ) bob_transfer_hold = alice_app.raiden.raiden_event_handler.hold_secretrequest_for( secrethash=bob_transfer_secrethash ) alice_app.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=alice_to_bob_amount, target=bob_app.raiden.address, identifier=identifier, secret=alice_transfer_secret, ) bob_app.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=alice_to_bob_amount, target=alice_app.raiden.address, identifier=identifier + 1, secret=bob_transfer_secret, ) alice_transfer_hold.wait(timeout=timeout) bob_transfer_hold.wait(timeout=timeout) alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_address) alice_lock = channel.get_lock(alice_bob_channel_state.our_state, alice_transfer_secrethash) bob_lock = channel.get_lock(alice_bob_channel_state.partner_state, bob_transfer_secrethash) assert alice_lock assert bob_lock # This is the current state of protocol: # # A -> B LockedTransfer # - protocol didn't continue assert_synced_channel_state( token_network_address=token_network_address, app0=alice_app, balance0=deposit, pending_locks0=[alice_lock], app1=bob_app, balance1=deposit, pending_locks1=[bob_lock], ) # A ChannelClose event will be generated, this will be polled by both apps # and each must start a task for calling settle RaidenAPI(bob_app.raiden).channel_close( registry_address=registry_address, token_address=token_address, partner_address=alice_app.raiden.address, ) # wait for the close transaction to be mined, this is necessary to compute # the timeout for the settle with gevent.Timeout(timeout): waiting.wait_for_close( raiden=alice_app.raiden, token_network_registry_address=registry_address, token_address=token_address, channel_ids=[alice_bob_channel_state.identifier], retry_timeout=alice_app.raiden.alarm.sleep_time, ) channel_closed = raiden_state_changes_search_for_item( bob_app.raiden, ContractReceiveChannelClosed, { "canonical_identifier": { "token_network_address": token_network_address, "channel_identifier": alice_bob_channel_state.identifier, } }, ) assert isinstance(channel_closed, ContractReceiveChannelClosed) settle_max_wait_block = BlockNumber( channel_closed.block_number + alice_bob_channel_state.settle_timeout * 2 ) settle_timeout = BlockTimeout( RuntimeError("settle did not happen"), bob_app.raiden, settle_max_wait_block, alice_app.raiden.alarm.sleep_time, ) with settle_timeout: waiting.wait_for_settle( raiden=alice_app.raiden, token_network_registry_address=registry_address, token_address=token_address, channel_ids=[alice_bob_channel_state.identifier], retry_timeout=alice_app.raiden.alarm.sleep_time, ) with gevent.Timeout(timeout): wait_for_batch_unlock( app=bob_app, token_network_address=token_network_address, receiver=alice_bob_channel_state.partner_state.address, sender=alice_bob_channel_state.our_state.address, ) alice_app.start() with gevent.Timeout(timeout): wait_for_batch_unlock( app=alice_app, token_network_address=token_network_address, receiver=alice_bob_channel_state.partner_state.address, sender=alice_bob_channel_state.our_state.address, )
def test_settle_is_automatically_called(raiden_network, token_addresses): """Settle is automatically called by one of the nodes.""" app0, app1 = raiden_network registry_address = app0.raiden.default_registry.address token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address ) assert token_network_address token_network = views.get_token_network_by_address( views.state_from_app(app0), token_network_address ) assert token_network channel_identifier = get_channelstate(app0, app1, token_network_address).identifier assert ( channel_identifier in token_network.partneraddresses_to_channelidentifiers[app1.raiden.address] ) # A ChannelClose event will be generated, this will be polled by both apps # and each must start a task for calling settle RaidenAPI(app1.raiden).channel_close(registry_address, token_address, app0.raiden.address) waiting.wait_for_close( app0.raiden, registry_address, token_address, [channel_identifier], app0.raiden.alarm.sleep_time, ) channel_state = views.get_channelstate_for( views.state_from_raiden(app0.raiden), registry_address, token_address, app1.raiden.address ) assert channel_state assert channel_state.close_transaction assert channel_state.close_transaction.finished_block_number waiting.wait_for_settle( app0.raiden, registry_address, token_address, [channel_identifier], app0.raiden.alarm.sleep_time, ) token_network = views.get_token_network_by_address( views.state_from_app(app0), token_network_address ) assert token_network assert ( channel_identifier not in token_network.partneraddresses_to_channelidentifiers[app1.raiden.address] ) state_changes = app0.raiden.wal.storage.get_statechanges_by_range(RANGE_ALL_STATE_CHANGES) assert search_for_item( state_changes, ContractReceiveChannelClosed, { "token_network_address": token_network_address, "channel_identifier": channel_identifier, "transaction_from": app1.raiden.address, "block_number": channel_state.close_transaction.finished_block_number, }, ) assert search_for_item( state_changes, ContractReceiveChannelSettled, {"token_network_address": token_network_address, "channel_identifier": channel_identifier}, )
def test_mediator_clear_pairs_after_batch_unlock(chain_state, token_network_state, our_address, channel_properties): """ Regression test for https://github.com/raiden-network/raiden/issues/2932 The mediator must also clear the transfer pairs once a ReceiveBatchUnlock where he is a participant is received. """ open_block_number = 10 open_block_hash = factories.make_block_hash() pseudo_random_generator = random.Random() properties, pkey = channel_properties address = properties.partner_state.address channel_state = factories.create(properties) channel_new_state_change = ContractReceiveChannelNew( transaction_hash=factories.make_transaction_hash(), channel_state=channel_state, block_number=open_block_number, block_hash=open_block_hash, ) channel_new_iteration = token_network.state_transition( token_network_state=token_network_state, state_change=channel_new_state_change, block_number=open_block_number, block_hash=open_block_hash, pseudo_random_generator=pseudo_random_generator, ) lock_amount = 30 lock_expiration = 20 lock_secret = keccak(b"test_end_state") lock_secrethash = sha256(lock_secret).digest() lock = HashTimeLockState(lock_amount, lock_expiration, lock_secrethash) mediated_transfer = make_receive_transfer_mediated( channel_state=channel_state, privkey=pkey, nonce=1, transferred_amount=0, lock=lock) route_state = RouteState( route=[ channel_state.our_state.address, channel_state.partner_state.address ], forward_channel_id=channel_state.canonical_identifier. channel_identifier, ) from_hop = factories.make_hop_from_channel(channel_state) init_mediator = ActionInitMediator( route_states=[route_state], from_hop=from_hop, from_transfer=mediated_transfer, balance_proof=mediated_transfer.balance_proof, sender=mediated_transfer.balance_proof.sender, # pylint: disable=no-member ) node.state_transition(chain_state, init_mediator) closed_block_number = open_block_number + 10 closed_block_hash = factories.make_block_hash() channel_close_state_change = ContractReceiveChannelClosed( transaction_hash=factories.make_transaction_hash(), transaction_from=channel_state.partner_state.address, canonical_identifier=channel_state.canonical_identifier, block_number=closed_block_number, block_hash=closed_block_hash, ) channel_closed_iteration = token_network.state_transition( token_network_state=channel_new_iteration.new_state, state_change=channel_close_state_change, block_number=closed_block_number, block_hash=closed_block_hash, pseudo_random_generator=pseudo_random_generator, ) settle_block_number = closed_block_number + channel_state.settle_timeout + 1 channel_settled_state_change = ContractReceiveChannelSettled( transaction_hash=factories.make_transaction_hash(), canonical_identifier=channel_state.canonical_identifier, block_number=settle_block_number, block_hash=factories.make_block_hash(), our_onchain_locksroot=factories.make_32bytes(), partner_onchain_locksroot=LOCKSROOT_OF_NO_LOCKS, ) channel_settled_iteration = token_network.state_transition( token_network_state=channel_closed_iteration.new_state, state_change=channel_settled_state_change, block_number=closed_block_number, block_hash=closed_block_hash, pseudo_random_generator=pseudo_random_generator, ) token_network_state_after_settle = channel_settled_iteration.new_state ids_to_channels = token_network_state_after_settle.channelidentifiers_to_channels assert len(ids_to_channels) == 1 assert channel_state.identifier in ids_to_channels block_number = closed_block_number + 1 channel_batch_unlock_state_change = ContractReceiveChannelBatchUnlock( transaction_hash=factories.make_transaction_hash(), canonical_identifier=channel_state.canonical_identifier, receiver=address, sender=our_address, locksroot=compute_locksroot(PendingLocksState([bytes(lock.encoded)])), unlocked_amount=lock_amount, returned_tokens=0, block_number=block_number, block_hash=factories.make_block_hash(), ) channel_unlock_iteration = node.state_transition( chain_state=chain_state, state_change=channel_batch_unlock_state_change) chain_state = channel_unlock_iteration.new_state token_network_state = views.get_token_network_by_address( chain_state=chain_state, token_network_address=token_network_state.address) ids_to_channels = token_network_state.channelidentifiers_to_channels assert len(ids_to_channels) == 0 # Make sure that all is fine in the next block block = Block(block_number=block_number + 1, gas_limit=1, block_hash=factories.make_transaction_hash()) iteration = node.state_transition(chain_state=chain_state, state_change=block) assert iteration.new_state # Make sure that mediator task was cleared during the next block processing # since the channel was removed mediator_task = chain_state.payment_mapping.secrethashes_to_task.get( lock_secrethash) assert not mediator_task
def get_best_routes_internal( chain_state: ChainState, token_network_address: TokenNetworkAddress, from_address: InitiatorAddress, to_address: TargetAddress, amount: int, previous_address: Optional[Address], ) -> List[RouteState]: """ Returns a list of channels that can be used to make a transfer. This will filter out channels that are not open and don't have enough capacity. """ # TODO: Route ranking. # Rate each route to optimize the fee price/quality of each route and add a # rate from in the range [0.0,1.0]. available_routes = list() token_network = views.get_token_network_by_address(chain_state, token_network_address) if not token_network: return list() neighbors_heap: List[Neighbour] = list() try: all_neighbors = networkx.all_neighbors( token_network.network_graph.network, from_address) except networkx.NetworkXError: # If `our_address` is not in the graph, no channels opened with the # address return list() for partner_address in all_neighbors: # don't send the message backwards if partner_address == previous_address: continue channel_state = views.get_channelstate_by_token_network_and_partner( chain_state, token_network_address, partner_address) if not channel_state: continue if channel.get_status(channel_state) != ChannelState.STATE_OPENED: log.info( "Channel is not opened, ignoring", from_address=to_checksum_address(from_address), partner_address=to_checksum_address(partner_address), routing_source="Internal Routing", ) continue nonrefundable = amount > channel.get_distributable( channel_state.partner_state, channel_state.our_state) try: route = networkx.shortest_path(token_network.network_graph.network, partner_address, to_address) neighbour = Neighbour( length=len(route), nonrefundable=nonrefundable, partner_address=partner_address, channelid=channel_state.identifier, route=route, ) heappush(neighbors_heap, neighbour) except (networkx.NetworkXNoPath, networkx.NodeNotFound): pass if not neighbors_heap: log.warning( "No routes available", from_address=to_checksum_address(from_address), to_address=to_checksum_address(to_address), ) return list() while neighbors_heap: neighbour = heappop(neighbors_heap) # The complete route includes the initiator, add it to the beginning complete_route = [Address(from_address)] + neighbour.route # https://github.com/raiden-network/raiden/issues/4751 # Internal routing doesn't know how much fees the initiator will be charged, # so it should set a percentage on top of the original amount # for the whole route. estimated_fee = FeeAmount( round(INTERNAL_ROUTING_DEFAULT_FEE_PERC * amount)) if neighbour.length == 1: # Target is our direct neighbour, pay no fees. estimated_fee = FeeAmount(0) available_routes.append( RouteState( route=complete_route, forward_channel_id=neighbour.channelid, estimated_fee=estimated_fee, )) return available_routes
def test_clear_closed_queue(raiden_network, token_addresses, network_wait): """ Closing a channel clears the respective message queue. """ app0, app1 = raiden_network hold_event_handler = app1.raiden.raiden_event_handler registry_address = app0.raiden.default_registry.address token_address = token_addresses[0] chain_state0 = views.state_from_app(app0) token_network_address = views.get_token_network_address_by_token_address( chain_state0, app0.raiden.default_registry.address, token_address) assert token_network_address token_network = views.get_token_network_by_address(chain_state0, token_network_address) assert token_network channel_identifier = get_channelstate(app0, app1, token_network_address).identifier assert (channel_identifier in token_network.partneraddresses_to_channelidentifiers[ app1.raiden.address]) target = app1.raiden.address secret = sha3(target) secrethash = sha256(secret).digest() hold_event_handler.hold_secretrequest_for(secrethash=secrethash) # make an unconfirmed transfer to ensure the nodes have communicated amount = 10 payment_identifier = 1337 app0.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=amount, target=target, identifier=payment_identifier, secret=secret, ) app1.raiden.transport.stop() app1.raiden.transport.get() # make sure to wait until the queue is created def has_initiator_events(): initiator_events = app0.raiden.wal.storage.get_events() return search_for_item(initiator_events, SendLockedTransfer, {}) assert wait_until(has_initiator_events, network_wait) # assert the specific queue is present chain_state0 = views.state_from_app(app0) queues0 = views.get_all_messagequeues(chain_state=chain_state0) assert [ (queue_id, queue) for queue_id, queue in queues0.items() if queue_id.recipient == app1.raiden.address and queue_id. canonical_identifier.channel_identifier == channel_identifier and queue ] # A ChannelClose event will be generated, this will be polled by both apps RaidenAPI(app0.raiden).channel_close(registry_address, token_address, app1.raiden.address) exception = ValueError("Could not get close event") with gevent.Timeout(seconds=30, exception=exception): waiting.wait_for_close( app0.raiden, registry_address, token_address, [channel_identifier], app0.raiden.alarm.sleep_time, ) # assert all queues with this partner are gone or empty chain_state0 = views.state_from_app(app0) queues0 = views.get_all_messagequeues(chain_state=chain_state0) assert not [(queue_id, queue) for queue_id, queue in queues0.items() if queue_id.recipient == app1.raiden.address and queue] chain_state1 = views.state_from_app(app1) queues1 = views.get_all_messagequeues(chain_state=chain_state1) assert not [(queue_id, queue) for queue_id, queue in queues1.items() if queue_id.recipient == app0.raiden.address and queue]
def test_lock_expiry(raiden_network, token_addresses, deposit): """Test lock expiry and removal.""" alice_app, bob_app = raiden_network token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(alice_app), alice_app.raiden.default_registry.address, token_address ) assert token_network_address hold_event_handler = bob_app.raiden.raiden_event_handler wait_message_handler = bob_app.raiden.message_handler token_network = views.get_token_network_by_address( views.state_from_app(alice_app), token_network_address ) assert token_network channel_state = get_channelstate(alice_app, bob_app, token_network_address) channel_identifier = channel_state.identifier assert ( channel_identifier in token_network.partneraddresses_to_channelidentifiers[bob_app.raiden.address] ) alice_to_bob_amount = 10 identifier = 1 target = bob_app.raiden.address transfer_1_secret = factories.make_secret(0) transfer_1_secrethash = sha256_secrethash(transfer_1_secret) transfer_2_secret = factories.make_secret(1) transfer_2_secrethash = sha256_secrethash(transfer_2_secret) hold_event_handler.hold_secretrequest_for(secrethash=transfer_1_secrethash) transfer1_received = wait_message_handler.wait_for_message( LockedTransfer, {"lock": {"secrethash": transfer_1_secrethash}} ) transfer2_received = wait_message_handler.wait_for_message( LockedTransfer, {"lock": {"secrethash": transfer_2_secrethash}} ) remove_expired_lock_received = wait_message_handler.wait_for_message( LockExpired, {"secrethash": transfer_1_secrethash} ) alice_app.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=alice_to_bob_amount, target=target, identifier=identifier, secret=transfer_1_secret, ) transfer1_received.wait() alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_address) lock = channel.get_lock(alice_bob_channel_state.our_state, transfer_1_secrethash) assert lock # This is the current state of the protocol: # # A -> B LockedTransfer # B -> A SecretRequest # - protocol didn't continue assert_synced_channel_state( token_network_address, alice_app, deposit, [lock], bob_app, deposit, [] ) # Verify lock is registered in both channel states alice_channel_state = get_channelstate(alice_app, bob_app, token_network_address) assert transfer_1_secrethash in alice_channel_state.our_state.secrethashes_to_lockedlocks bob_channel_state = get_channelstate(bob_app, alice_app, token_network_address) assert transfer_1_secrethash in bob_channel_state.partner_state.secrethashes_to_lockedlocks alice_chain_state = views.state_from_raiden(alice_app.raiden) assert transfer_1_secrethash in alice_chain_state.payment_mapping.secrethashes_to_task remove_expired_lock_received.wait() alice_channel_state = get_channelstate(alice_app, bob_app, token_network_address) assert transfer_1_secrethash not in alice_channel_state.our_state.secrethashes_to_lockedlocks # Verify Bob received the message and processed the LockExpired message bob_channel_state = get_channelstate(bob_app, alice_app, token_network_address) assert transfer_1_secrethash not in bob_channel_state.partner_state.secrethashes_to_lockedlocks alice_chain_state = views.state_from_raiden(alice_app.raiden) assert transfer_1_secrethash not in alice_chain_state.payment_mapping.secrethashes_to_task # Make another transfer alice_to_bob_amount = 10 identifier = 2 hold_event_handler.hold_secretrequest_for(secrethash=transfer_2_secrethash) alice_app.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=alice_to_bob_amount, target=target, identifier=identifier, secret=transfer_2_secret, ) transfer2_received.wait() # Make sure the other transfer still exists alice_chain_state = views.state_from_raiden(alice_app.raiden) assert transfer_2_secrethash in alice_chain_state.payment_mapping.secrethashes_to_task bob_channel_state = get_channelstate(bob_app, alice_app, token_network_address) assert transfer_2_secrethash in bob_channel_state.partner_state.secrethashes_to_lockedlocks
def get_best_routes( chain_state: ChainState, token_network_address: TokenNetworkAddress, one_to_n_address: Optional[OneToNAddress], from_address: InitiatorAddress, to_address: TargetAddress, amount: PaymentAmount, previous_address: Optional[Address], pfs_config: Optional[PFSConfig], privkey: bytes, ) -> Tuple[Optional[str], List[RouteState], Optional[UUID]]: token_network = views.get_token_network_by_address(chain_state, token_network_address) assert token_network, "The token network must be validated and exist." try: # networkx returns a generator, consume the result since it will be # iterated over multiple times. all_neighbors = list( networkx.all_neighbors(token_network.network_graph.network, from_address)) except networkx.NetworkXError: # If `our_address` is not in the graph, no channels opened with the # address. log.debug( "Node does not have a channel in the requested token network.", source=to_checksum_address(from_address), target=to_checksum_address(to_address), amount=amount, ) return ("Node does not have a channel in the requested token network.", list(), None) error_closed = 0 error_no_route = 0 error_no_capacity = 0 error_not_online = 0 error_direct = None shortest_routes: List[Neighbour] = list() # Always use a direct channel if available: # - There are no race conditions and the capacity is guaranteed to be # available. # - There will be no mediation fees # - The transfer will be faster if to_address in all_neighbors: for channel_id in token_network.partneraddresses_to_channelidentifiers[ Address(to_address)]: channel_state = token_network.channelidentifiers_to_channels[ channel_id] # direct channels don't have fees payment_with_fee_amount = PaymentWithFeeAmount(amount) is_usable = channel.is_channel_usable_for_new_transfer( channel_state, payment_with_fee_amount, None) if is_usable is channel.ChannelUsability.USABLE: direct_route = RouteState( route=[Address(from_address), Address(to_address)], forward_channel_id=channel_state.canonical_identifier. channel_identifier, estimated_fee=FeeAmount(0), ) return (None, [direct_route], None) error_direct = is_usable latest_channel_opened_at = BlockNumber(0) for partner_address in all_neighbors: for channel_id in token_network.partneraddresses_to_channelidentifiers[ partner_address]: channel_state = token_network.channelidentifiers_to_channels[ channel_id] if channel.get_status(channel_state) != ChannelState.STATE_OPENED: error_closed += 1 continue latest_channel_opened_at = max( latest_channel_opened_at, channel_state.open_transaction.finished_block_number) try: route = networkx.shortest_path( token_network.network_graph.network, partner_address, to_address) except (networkx.NetworkXNoPath, networkx.NodeNotFound): error_no_route += 1 else: distributable = channel.get_distributable( channel_state.our_state, channel_state.partner_state) network_status = views.get_node_network_status( chain_state, channel_state.partner_state.address) if distributable < amount: error_no_capacity += 1 elif network_status != NetworkState.REACHABLE: error_not_online += 1 else: nonrefundable = amount > channel.get_distributable( channel_state.partner_state, channel_state.our_state) # The complete route includes the initiator, add it to the beginning complete_route = [Address(from_address)] + route neighbour = Neighbour( length=len(route), nonrefundable=nonrefundable, partner_address=partner_address, channelid=channel_state.identifier, route=complete_route, ) heappush(shortest_routes, neighbour) if not shortest_routes: qty_channels = sum( len(token_network. partneraddresses_to_channelidentifiers[partner_address]) for partner_address in all_neighbors) error_msg = ( f"None of the existing channels could be used to complete the " f"transfer. From the {qty_channels} existing channels. " f"{error_closed} are closed. {error_not_online} are not online. " f"{error_no_route} don't have a route to the target in the given " f"token network. {error_no_capacity} don't have enough capacity for " f"the requested transfer.") if error_direct is not None: error_msg += f"direct channel {error_direct}." log.warning( "None of the existing channels could be used to complete the transfer", from_address=to_checksum_address(from_address), to_address=to_checksum_address(to_address), error_closed=error_closed, error_no_route=error_no_route, error_no_capacity=error_no_capacity, error_direct=error_direct, error_not_online=error_not_online, ) return (error_msg, list(), None) if pfs_config is not None and one_to_n_address is not None: pfs_error_msg, pfs_routes, pfs_feedback_token = get_best_routes_pfs( chain_state=chain_state, token_network_address=token_network_address, one_to_n_address=one_to_n_address, from_address=from_address, to_address=to_address, amount=amount, previous_address=previous_address, pfs_config=pfs_config, privkey=privkey, pfs_wait_for_block=latest_channel_opened_at, ) if not pfs_error_msg: # As of version 0.5 it is possible for the PFS to return an empty # list of routes without an error message. if not pfs_routes: return ("PFS could not find any routes", list(), None) log.info("Received route(s) from PFS", routes=pfs_routes, feedback_token=pfs_feedback_token) return (pfs_error_msg, pfs_routes, pfs_feedback_token) log.warning( "Request to Pathfinding Service was not successful. " "No routes to the target are found.", pfs_message=pfs_error_msg, ) return (pfs_error_msg, list(), None) else: available_routes = list() while shortest_routes: neighbour = heappop(shortest_routes) # https://github.com/raiden-network/raiden/issues/4751 # Internal routing doesn't know how much fees the initiator will be charged, # so it should set a percentage on top of the original amount # for the whole route. estimated_fee = FeeAmount( round(INTERNAL_ROUTING_DEFAULT_FEE_PERC * amount)) if neighbour.length == 1: # Target is our direct neighbour, pay no fees. estimated_fee = FeeAmount(0) available_routes.append( RouteState( route=neighbour.route, forward_channel_id=neighbour.channelid, estimated_fee=estimated_fee, )) return (None, available_routes, None)
def test_lock_expiry( raiden_network: List[RaidenService], token_addresses: List[TokenAddress], deposit: TokenAmount ) -> None: """Test lock expiry and removal.""" alice_app, bob_app = raiden_network token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_raiden(alice_app), alice_app.default_registry.address, token_address, ) assert token_network_address hold_event_handler = bob_app.raiden_event_handler wait_message_handler = bob_app.message_handler msg = "hold event handler necessary to control messages" assert isinstance(hold_event_handler, HoldRaidenEventHandler), msg assert isinstance(wait_message_handler, WaitForMessage), msg token_network = views.get_token_network_by_address( views.state_from_raiden(alice_app), token_network_address ) assert token_network channel_state = get_channelstate(alice_app, bob_app, token_network_address) channel_identifier = channel_state.identifier assert ( channel_identifier in token_network.partneraddresses_to_channelidentifiers[bob_app.address] ) alice_to_bob_amount = PaymentAmount(10) identifier = factories.make_payment_id() target = TargetAddress(bob_app.address) transfer_1_secret = factories.make_secret(0) transfer_1_secrethash = sha256_secrethash(transfer_1_secret) transfer_2_secret = factories.make_secret(1) transfer_2_secrethash = sha256_secrethash(transfer_2_secret) hold_event_handler.hold_secretrequest_for(secrethash=transfer_1_secrethash) transfer1_received = wait_message_handler.wait_for_message( LockedTransfer, {"lock": {"secrethash": transfer_1_secrethash}} ) transfer2_received = wait_message_handler.wait_for_message( LockedTransfer, {"lock": {"secrethash": transfer_2_secrethash}} ) remove_expired_lock_received = wait_message_handler.wait_for_message( LockExpired, {"secrethash": transfer_1_secrethash} ) alice_app.mediated_transfer_async( token_network_address=token_network_address, amount=alice_to_bob_amount, target=target, identifier=identifier, secret=transfer_1_secret, ) transfer1_received.wait() alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_address) lock = channel.get_lock(alice_bob_channel_state.our_state, transfer_1_secrethash) assert lock # This is the current state of the protocol: # # A -> B LockedTransfer # B -> A SecretRequest # - protocol didn't continue assert_synced_channel_state( token_network_address, alice_app, Balance(deposit), [lock], bob_app, Balance(deposit), [] ) # Verify lock is registered in both channel states alice_channel_state = get_channelstate(alice_app, bob_app, token_network_address) assert transfer_1_secrethash in alice_channel_state.our_state.secrethashes_to_lockedlocks bob_channel_state = get_channelstate(bob_app, alice_app, token_network_address) assert transfer_1_secrethash in bob_channel_state.partner_state.secrethashes_to_lockedlocks alice_chain_state = views.state_from_raiden(alice_app) assert transfer_1_secrethash in alice_chain_state.payment_mapping.secrethashes_to_task remove_expired_lock_received.wait() alice_channel_state = get_channelstate(alice_app, bob_app, token_network_address) assert transfer_1_secrethash not in alice_channel_state.our_state.secrethashes_to_lockedlocks # Verify Bob received the message and processed the LockExpired message bob_channel_state = get_channelstate(bob_app, alice_app, token_network_address) assert transfer_1_secrethash not in bob_channel_state.partner_state.secrethashes_to_lockedlocks alice_chain_state = views.state_from_raiden(alice_app) assert transfer_1_secrethash not in alice_chain_state.payment_mapping.secrethashes_to_task # Make another transfer alice_to_bob_amount = PaymentAmount(10) identifier = factories.make_payment_id() hold_event_handler.hold_secretrequest_for(secrethash=transfer_2_secrethash) alice_app.mediated_transfer_async( token_network_address=token_network_address, amount=alice_to_bob_amount, target=target, identifier=identifier, secret=transfer_2_secret, ) transfer2_received.wait() # Make sure the other transfer still exists alice_chain_state = views.state_from_raiden(alice_app) assert transfer_2_secrethash in alice_chain_state.payment_mapping.secrethashes_to_task bob_channel_state = get_channelstate(bob_app, alice_app, token_network_address) assert transfer_2_secrethash in bob_channel_state.partner_state.secrethashes_to_lockedlocks