def test_channel_deposit(raiden_chain, deposit, retry_timeout, token_addresses): app0, app1 = raiden_chain token_address = token_addresses[0] registry_address = app0.raiden.default_registry.address 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 channel0 = views.get_channelstate_by_token_network_and_partner( views.state_from_app(app0), token_network_address, app1.raiden.address) channel1 = views.get_channelstate_by_token_network_and_partner( views.state_from_app(app0), token_network_address, app1.raiden.address) assert channel0 is None assert channel1 is None RaidenAPI(app0.raiden).channel_open(registry_address, token_address, app1.raiden.address) timeout_seconds = 15 exception = RuntimeError( f"Did not see the channels open within {timeout_seconds} seconds") with gevent.Timeout(seconds=timeout_seconds, exception=exception): wait_both_channel_open(app0, app1, registry_address, token_address, retry_timeout) assert_synced_channel_state(token_network_address, app0, Balance(0), [], app1, Balance(0), []) RaidenAPI(app0.raiden).set_total_channel_deposit(registry_address, token_address, app1.raiden.address, deposit) exception = RuntimeError( f"Did not see the channel deposit within {timeout_seconds} seconds") with gevent.Timeout(seconds=timeout_seconds, exception=exception): waiting.wait_single_channel_deposit(app0, app1, registry_address, token_address, deposit, retry_timeout) assert_synced_channel_state(token_network_address, app0, deposit, [], app1, Balance(0), []) RaidenAPI(app1.raiden).set_total_channel_deposit(registry_address, token_address, app0.raiden.address, deposit) with gevent.Timeout(seconds=timeout_seconds, exception=exception): waiting.wait_single_channel_deposit(app1, app0, registry_address, token_address, deposit, retry_timeout) assert_synced_channel_state(token_network_address, app0, deposit, [], app1, deposit, [])
def check_channel( app1: App, app2: App, token_network_address: TokenNetworkAddress, settle_timeout: BlockTimeout, deposit_amount: TokenAmount, ) -> None: channel_state1 = get_channelstate_by_token_network_and_partner( chain_state=state_from_raiden(app1.raiden), token_network_address=token_network_address, partner_address=app2.raiden.address, ) assert channel_state1, "app1 does not have a channel with app2." netcontract1 = app1.raiden.proxy_manager.payment_channel( channel_state=channel_state1, block_identifier=BLOCK_ID_LATEST) channel_state2 = get_channelstate_by_token_network_and_partner( chain_state=state_from_raiden(app2.raiden), token_network_address=token_network_address, partner_address=app1.raiden.address, ) assert channel_state2, "app2 does not have a channel with app1." netcontract2 = app2.raiden.proxy_manager.payment_channel( channel_state=channel_state2, block_identifier=BLOCK_ID_LATEST) # Check a valid settle timeout was used, the netting contract has an # enforced minimum and maximum assert settle_timeout == netcontract1.settle_timeout() assert settle_timeout == netcontract2.settle_timeout() if deposit_amount > 0: assert netcontract1.can_transfer(BLOCK_ID_LATEST) assert netcontract2.can_transfer(BLOCK_ID_LATEST) app1_details = netcontract1.detail(BLOCK_ID_LATEST) app2_details = netcontract2.detail(BLOCK_ID_LATEST) assert (app1_details.participants_data.our_details.address == app2_details.participants_data.partner_details.address) assert (app1_details.participants_data.partner_details.address == app2_details.participants_data.our_details.address) assert (app1_details.participants_data.our_details.deposit == app2_details.participants_data.partner_details.deposit) assert (app1_details.participants_data.partner_details.deposit == app2_details.participants_data.our_details.deposit) assert app1_details.chain_id == app2_details.chain_id assert app1_details.participants_data.our_details.deposit == deposit_amount assert app1_details.participants_data.partner_details.deposit == deposit_amount assert app2_details.participants_data.our_details.deposit == deposit_amount assert app2_details.participants_data.partner_details.deposit == deposit_amount assert app2_details.chain_id == UNIT_CHAIN_ID
def handle_processed( chain_state: ChainState, state_change: ReceiveProcessed, ) -> TransitionResult: events = list() for queue in chain_state.queueids_to_queues.values(): for message in queue: message_found = ( message.message_identifier == state_change.message_identifier and message.recipient == state_change.sender ) if message_found: if type(message) == SendDirectTransfer: channel_state = views.get_channelstate_by_token_network_and_partner( chain_state, message.balance_proof.token_network_identifier, message.recipient, ) events.append(EventPaymentSentSuccess( channel_state.payment_network_identifier, channel_state.token_network_identifier, message.payment_identifier, message.balance_proof.transferred_amount, message.recipient, )) # Clean up message queue for queueid in list(chain_state.queueids_to_queues.keys()): inplace_delete_message_queue(chain_state, state_change, queueid) return TransitionResult(chain_state, events)
def replay_wal(storage, token_network_identifier, partner_address): all_state_changes = storage.get_statechanges_by_identifier( from_identifier=0, to_identifier='latest', ) state_manager = StateManager(state_transition=node.state_transition, current_state=None) wal = WriteAheadLog(state_manager, storage) for _, state_change in enumerate(all_state_changes): events = wal.state_manager.dispatch(state_change) chain_state = wal.state_manager.current_state channel_state = views.get_channelstate_by_token_network_and_partner( chain_state, to_canonical_address(token_network_identifier), to_canonical_address(partner_address), ) if not channel_state: continue ### # Customize this to filter things further somewhere around here. # An example would be to add `import pdb; pdb.set_trace()` # and inspect the state. ### print_state_change(state_change) print_events(events)
def run_test_create_monitoring_request(raiden_network, token_addresses): app0, app1 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state=chain_state, payment_network_id=payment_network_id, token_address=token_address, ) payment_identifier = create_default_identifier() transfer( initiator_app=app1, target_app=app0, token_address=token_address, amount=1, identifier=payment_identifier, ) chain_state = views.state_from_raiden(app0.raiden) channel_state = views.get_channelstate_by_token_network_and_partner( chain_state, token_network_identifier, app1.raiden.address, ) balance_proof = channel_state.partner_state.balance_proof api = RaidenAPI(app0.raiden) request = api.create_monitoring_request( balance_proof=balance_proof, reward_amount=1, ) assert request as_dict = request.to_dict() from_dict = RequestMonitoring.from_dict(as_dict) assert from_dict.to_dict() == as_dict
def direct_transfer( initiator_app, target_app, token_network_identifier, amount, identifier=None, timeout=5, ): """ Nice to read shortcut to make a DirectTransfer. """ channel_state = views.get_channelstate_by_token_network_and_partner( views.state_from_app(initiator_app), token_network_identifier, target_app.raiden.address, ) assert channel_state, 'there is not a direct channel' initiator_app.raiden.direct_transfer_async( token_network_identifier, amount, target_app.raiden.address, identifier, ) # direct transfers don't have confirmation gevent.sleep(timeout)
def get_channelstate(app0, app1, token_network_identifier) -> NettingChannelState: channel_state = views.get_channelstate_by_token_network_and_partner( views.state_from_app(app0), token_network_identifier, app1.raiden.address, ) return channel_state
def replay_wal(storage, token_network_identifier, partner_address, translator=None): all_state_changes = storage.get_statechanges_by_identifier( from_identifier=0, to_identifier='latest', ) state_manager = StateManager(state_transition=node.state_transition, current_state=None) wal = WriteAheadLog(state_manager, storage) for _, state_change in enumerate(all_state_changes): events = wal.state_manager.dispatch(state_change) chain_state = wal.state_manager.current_state channel_state = views.get_channelstate_by_token_network_and_partner( chain_state, to_canonical_address(token_network_identifier), to_canonical_address(partner_address), ) if not channel_state: continue ### # Customize this to filter things further somewhere around here. # An example would be to add `import pdb; pdb.set_trace()` # and inspect the state. ### print_state_change(state_change, translator=translator) print_events(events, translator=translator)
def direct_transfer( initiator_app, target_app, token_network_identifier, amount, identifier=None, timeout=5, ): """ Nice to read shortcut to make a DirectTransfer. """ channel_state = views.get_channelstate_by_token_network_and_partner( views.state_from_app(initiator_app), token_network_identifier, target_app.raiden.address, ) assert channel_state, 'there is not a direct channel' initiator_app.raiden.direct_transfer_async( token_network_identifier, amount, target_app.raiden.address, identifier, ) # direct transfers don't have confirmation gevent.sleep(timeout)
def resolve_routes( routes: List[RouteMetadata], token_network_address: TokenNetworkAddress, chain_state: ChainState, ) -> List[RouteState]: """resolve the forward_channel_id for a given route TODO: We don't have ``forward_channel_id``, anymore. Does this function still make sense? """ resolvable = [] for route_metadata in routes: if len(route_metadata.route) < 2: continue channel_state = views.get_channelstate_by_token_network_and_partner( chain_state=chain_state, token_network_address=token_network_address, partner_address=route_metadata.route[1], ) if channel_state is not None: resolvable.append( RouteState( route=route_metadata.route, # This is only used in the mediator, so fees are set to 0 estimated_fee=FeeAmount(0), )) return resolvable
def pending_mediated_transfer(app_chain, token_network_identifier, amount, identifier): """ Nice to read shortcut to make a LockedTransfer where the secret is _not_ revealed. While the secret is not revealed all apps will be synchronized, meaning they are all going to receive the LockedTransfer message. Returns: The secret used to generate the LockedTransfer """ # pylint: disable=too-many-locals if len(app_chain) < 2: raise ValueError('Cannot make a LockedTransfer with less than two apps') target = app_chain[-1].raiden.address # Generate a secret initiator_channel = views.get_channelstate_by_token_network_and_partner( views.state_from_app(app_chain[0]), token_network_identifier, app_chain[1].raiden.address, ) address = initiator_channel.identifier nonce_int = channel.get_next_nonce(initiator_channel.our_state) nonce_bytes = nonce_int.to_bytes(2, 'big') secret = sha3(address + nonce_bytes) initiator_app = app_chain[0] init_initiator_statechange = initiator_init( initiator_app.raiden, identifier, amount, secret, token_network_identifier, target, ) events = initiator_app.raiden.wal.log_and_dispatch( init_initiator_statechange, initiator_app.raiden.get_block_number(), ) send_transfermessage = must_contain_entry(events, SendLockedTransfer, {}) transfermessage = LockedTransfer.from_event(send_transfermessage) initiator_app.raiden.sign(transfermessage) for mediator_app in app_chain[1:-1]: mediator_init_statechange = mediator_init(mediator_app.raiden, transfermessage) events = mediator_app.raiden.wal.log_and_dispatch( mediator_init_statechange, mediator_app.raiden.get_block_number(), ) send_transfermessage = must_contain_entry(events, SendLockedTransfer, {}) transfermessage = LockedTransfer.from_event(send_transfermessage) mediator_app.raiden.sign(transfermessage) target_app = app_chain[-1] mediator_init_statechange = target_init(transfermessage) events = target_app.raiden.wal.log_and_dispatch( mediator_init_statechange, target_app.raiden.get_block_number(), ) return secret
def get_channelstate(app0, app1, token_network_identifier) -> NettingChannelState: channel_state = views.get_channelstate_by_token_network_and_partner( views.state_from_app(app0), token_network_identifier, app1.raiden.address, ) return channel_state
def resolve_routes( routes: List[RouteMetadata], token_network_address: TokenNetworkAddress, chain_state: ChainState, ) -> List[RouteState]: """ resolve the forward_channel_id for a given route """ resolvable = [] for route_metadata in routes: if len(route_metadata.route) < 2: continue channel_state = views.get_channelstate_by_token_network_and_partner( chain_state=chain_state, token_network_address=token_network_address, partner_address=route_metadata.route[1], ) if channel_state is not None: resolvable.append( RouteState( route=route_metadata.route, forward_channel_id=channel_state.canonical_identifier. channel_identifier, # This is only used in the mediator, so fees are set to 0 estimated_fee=FeeAmount(0), )) return resolvable
def handle_processed( chain_state: ChainState, state_change: ReceiveProcessed, ) -> TransitionResult: # TODO: improve the complexity of this algorithm events = list() for queue in chain_state.queueids_to_queues.values(): remove = [] # TODO: ensure Processed message came from the correct peer for pos, message in enumerate(queue): if message.message_identifier == state_change.message_identifier: if type(message) == SendDirectTransfer: channel_state = views.get_channelstate_by_token_network_and_partner( chain_state, message.balance_proof.token_network_identifier, message.recipient, ) events.append( EventPaymentSentSuccess( channel_state.payment_network_identifier, channel_state.token_network_identifier, message.payment_identifier, message.balance_proof.transferred_amount, message.recipient, )) remove.append(pos) for removepos in reversed(remove): queue.pop(removepos) return TransitionResult(chain_state, events)
def get_channelstate( app0: App, app1: App, token_network_address: TokenNetworkAddress) -> NettingChannelState: channel_state = views.get_channelstate_by_token_network_and_partner( views.state_from_app(app0), token_network_address, app1.raiden.address) assert channel_state return channel_state
def pending_mediated_transfer(app_chain, token_network_identifier, amount, identifier): """ Nice to read shortcut to make a LockedTransfer where the secret is _not_ revealed. While the secret is not revealed all apps will be synchronized, meaning they are all going to receive the LockedTransfer message. Returns: The secret used to generate the LockedTransfer """ # pylint: disable=too-many-locals if len(app_chain) < 2: raise ValueError('Cannot make a LockedTransfer with less than two apps') target = app_chain[-1].raiden.address # Generate a secret initiator_channel = views.get_channelstate_by_token_network_and_partner( views.state_from_app(app_chain[0]), token_network_identifier, app_chain[1].raiden.address, ) address = initiator_channel.identifier nonce_int = channel.get_next_nonce(initiator_channel.our_state) nonce_bytes = nonce_int.to_bytes(2, 'big') secret = sha3(address + nonce_bytes) initiator_app = app_chain[0] init_initiator_statechange = initiator_init( initiator_app.raiden, identifier, amount, secret, token_network_identifier, target, ) events = initiator_app.raiden.wal.log_and_dispatch( init_initiator_statechange, initiator_app.raiden.get_block_number(), ) send_transfermessage = must_contain_entry(events, SendLockedTransfer, {}) transfermessage = LockedTransfer.from_event(send_transfermessage) initiator_app.raiden.sign(transfermessage) for mediator_app in app_chain[1:-1]: mediator_init_statechange = mediator_init(mediator_app.raiden, transfermessage) events = mediator_app.raiden.wal.log_and_dispatch( mediator_init_statechange, mediator_app.raiden.get_block_number(), ) send_transfermessage = must_contain_entry(events, SendLockedTransfer, {}) transfermessage = LockedTransfer.from_event(send_transfermessage) mediator_app.raiden.sign(transfermessage) target_app = app_chain[-1] mediator_init_statechange = target_init(transfermessage) events = target_app.raiden.wal.log_and_dispatch( mediator_init_statechange, target_app.raiden.get_block_number(), ) return secret
def test_create_monitoring_request(raiden_network, token_addresses): app0, app1 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) token_network_registry_address = app0.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( chain_state=chain_state, token_network_registry_address=token_network_registry_address, token_address=token_address, ) assert token_network_address payment_identifier = create_default_identifier() transfer( initiator_app=app1, target_app=app0, token_address=token_address, amount=PaymentAmount(1), identifier=payment_identifier, ) chain_state = views.state_from_raiden(app0.raiden) channel_state = views.get_channelstate_by_token_network_and_partner( chain_state, token_network_address, app1.raiden.address) assert channel_state balance_proof = cast(BalanceProofSignedState, channel_state.partner_state.balance_proof) api = RaidenAPI(app0.raiden) request = api.create_monitoring_request(balance_proof=balance_proof, reward_amount=TokenAmount(1)) assert request as_dict = DictSerializer.serialize(request) from_dict = DictSerializer.deserialize(as_dict) assert DictSerializer.serialize(from_dict) == as_dict
def assert_synced_channel_state( token_network_address: TokenNetworkAddress, app0: App, balance0: Balance, pending_locks0: List[HashTimeLockState], app1: App, balance1: Balance, pending_locks1: List[HashTimeLockState], ) -> None: """Compare channel's state from both nodes. Note: This assert does not work for an intermediate state, where one message hasn't been delivered yet or has been completely lost. """ assert app0.raiden.wal assert app1.raiden.wal saved_state0 = app0.raiden.wal.saved_state saved_state1 = app1.raiden.wal.saved_state assert_deposit(token_network_address, app0, app1, saved_state0, saved_state1) assert_deposit(token_network_address, app1, app0, saved_state1, saved_state0) assert_balance_proof(token_network_address, app1, app0, saved_state1, saved_state0) assert_balance_proof(token_network_address, app0, app1, saved_state0, saved_state1) channel0 = views.get_channelstate_by_token_network_and_partner( saved_state0.state, token_network_address, app1.raiden.address) channel1 = views.get_channelstate_by_token_network_and_partner( saved_state1.state, token_network_address, app0.raiden.address) assert channel0 assert channel1 assert_channel_values( channel0=channel0, balance0=balance0, pending_locks0=pending_locks0, channel1=channel1, balance1=balance1, pending_locks1=pending_locks1, )
def test_api_channel_set_reveal_timeout( api_server_test_instance: APIServer, raiden_network: List[RaidenService], token_addresses, settle_timeout, ): app0, app1 = raiden_network token_address = token_addresses[0] partner_address = app1.address request = grequests.patch( api_url_for( api_server_test_instance, "channelsresourcebytokenandpartneraddress", token_address=token_address, partner_address=partner_address, ), json=dict(reveal_timeout=0), ) response = request.send().response assert_response_with_error(response, HTTPStatus.CONFLICT) request = grequests.patch( api_url_for( api_server_test_instance, "channelsresourcebytokenandpartneraddress", token_address=token_address, partner_address=partner_address, ), json=dict(reveal_timeout=settle_timeout + 1), ) response = request.send().response assert_response_with_error(response, HTTPStatus.CONFLICT) reveal_timeout = int(settle_timeout / 2) request = grequests.patch( api_url_for( api_server_test_instance, "channelsresourcebytokenandpartneraddress", token_address=token_address, partner_address=partner_address, ), json=dict(reveal_timeout=reveal_timeout), ) response = request.send().response assert_response_with_code(response, HTTPStatus.OK) token_network_address = views.get_token_network_address_by_token_address( views.state_from_raiden(app0), app0.default_registry.address, token_address) assert token_network_address channel_state = views.get_channelstate_by_token_network_and_partner( chain_state=views.state_from_raiden(app0), token_network_address=token_network_address, partner_address=app1.address, ) assert channel_state assert channel_state.reveal_timeout == reveal_timeout
def test_get_channelstate_by_token_network_and_partner(): test_state = factories.make_chain_state(number_of_channels=3) partner_address = test_state.channels[1].partner_state.address assert (views.get_channelstate_by_token_network_and_partner( chain_state=test_state.chain_state, token_network_address=test_state.token_network_address, partner_address=partner_address, ) == test_state.channels[1])
def set_fee_schedule(app: App, other_app: App, fee_schedule: FeeScheduleState): channel_state = views.get_channelstate_by_token_network_and_partner( # type: ignore chain_state=views.state_from_raiden(app.raiden), token_network_address=token_network_address, partner_address=other_app.raiden.address, ) assert channel_state channel_state.fee_schedule = fee_schedule
def get_channel_state(app) -> NettingChannelState: chain_state = views.state_from_app(app) token_network_registry_address = app.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( chain_state, token_network_registry_address, token_address) assert token_network_address channel_state = views.get_channelstate_by_token_network_and_partner( chain_state, token_network_address, app1.raiden.address) assert channel_state return channel_state
def replay_wal( storage: SerializedSQLiteStorage, token_network_address: TokenNetworkAddress, partner_address: Address, translator: Optional[Translator] = None, ) -> None: snapshot = storage.get_snapshot_before_state_change( state_change_identifier=LOW_STATECHANGE_ULID) assert snapshot is not None, "No snapshot found" wal = WriteAheadLog(snapshot.data, storage, node.state_transition) state = wal.get_current_state() all_state_changes = storage.get_statechanges_by_range( RANGE_ALL_STATE_CHANGES) for state_change in all_state_changes: # Dispatching the state changes one-by-one to easy debugging state, events = dispatch( state=state, state_change=state_change, state_transition=wal.state_transition, ) msg = "Chain state must never be cleared up." assert state, msg channel_state = views.get_channelstate_by_token_network_and_partner( state, to_canonical_address(token_network_address), to_canonical_address(partner_address), ) if channel_state is None: continue ### # Customize this to filter things further somewhere around here. # An example would be to add `breakpoint()` # and inspect the state. ### print_state_change(state_change, translator=translator) print_events(events, translator=translator) # Enable to print color coded presence state of channel partners # print_presence_view(chain_state, translator) # Enable to print balances & balance sum with all channel partners # print_node_balances(chain_state, token_network_address, translator) print_nl()
def replay_wal( storage: SerializedSQLiteStorage, token_network_address: TokenNetworkAddress, partner_address: Address, translator: Optional[Translator] = None, ) -> None: all_state_changes = storage.get_statechanges_by_range( RANGE_ALL_STATE_CHANGES) state_manager = StateManager(state_transition=node.state_transition, current_state=None) wal = WriteAheadLog(state_manager, storage) for _, state_change in enumerate(all_state_changes): # Dispatching the state changes one-by-one to easy debugging _, events = wal.state_manager.dispatch([state_change]) chain_state = wal.state_manager.current_state msg = "Chain state must never be cleared up." assert chain_state, msg channel_state = views.get_channelstate_by_token_network_and_partner( chain_state, to_canonical_address(token_network_address), to_canonical_address(partner_address), ) if channel_state is None: continue ### # Customize this to filter things further somewhere around here. # An example would be to add `breakpoint()` # and inspect the state. ### print_state_change(state_change, translator=translator) print_events(chain.from_iterable(events), translator=translator) # Enable to print color coded presence state of channel partners # print_presence_view(chain_state, translator) # Enable to print balances & balance sum with all channel partners # print_node_balances(chain_state, token_network_address, translator) print_nl()
def update_pfs(raiden: RaidenService, event: Event): channel_state = get_channelstate_by_token_network_and_partner( chain_state=state_from_raiden(raiden), token_network_id=to_canonical_address( event.balance_proof.token_network_identifier, ), partner_address=to_canonical_address(event.recipient), ) error_msg = 'tried to send a balance proof in non-existant channel ' f'token_network_address: {pex(event.balance_proof.token_network_identifier)} ' f'recipient: {pex(event.recipient)}' assert channel_state is not None, error_msg msg = UpdatePFS.from_balance_proof( balance_proof=event.balance_proof, reveal_timeout=channel_state.reveal_timeout, ) msg.sign(raiden.signer) raiden.transport.send_global(PATH_FINDING_BROADCASTING_ROOM, msg) log.debug( 'sent a PFS Update', balance_proof=event.balance_proof, recipient=event.recipient, )
def get_best_routes_pfs( chain_state: ChainState, token_network_address: TokenNetworkAddress, one_to_n_address: OneToNAddress, from_address: InitiatorAddress, to_address: TargetAddress, amount: PaymentAmount, previous_address: Optional[Address], pfs_config: PFSConfig, privkey: bytes, pfs_wait_for_block: BlockNumber, ) -> Tuple[Optional[str], List[RouteState], Optional[UUID]]: try: pfs_routes, feedback_token = query_paths( pfs_config=pfs_config, our_address=chain_state.our_address, privkey=privkey, current_block_number=chain_state.block_number, token_network_address=token_network_address, one_to_n_address=one_to_n_address, chain_id=chain_state.chain_id, route_from=from_address, route_to=to_address, value=amount, pfs_wait_for_block=pfs_wait_for_block, ) except ServiceRequestFailed as e: log_message = ("PFS: " + e.args[0]) if e.args[0] else None log_info = e.args[1] if len(e.args) > 1 else {} log.warning("An error with the path request occurred", log_message=log_message, **log_info) return log_message, [], None paths = [] for path_object in pfs_routes: path = path_object["path"] estimated_fee = path_object["estimated_fee"] canonical_path = [to_canonical_address(node) for node in path] # get the second entry, as the first one is the node itself # also needs to be converted to canonical representation partner_address = canonical_path[1] # don't route back if partner_address == previous_address: continue channel_state = views.get_channelstate_by_token_network_and_partner( chain_state=chain_state, token_network_address=token_network_address, partner_address=partner_address, ) if not channel_state: continue # check channel state 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="Pathfinding Service", ) continue paths.append( RouteState( route=canonical_path, forward_channel_id=channel_state.identifier, estimated_fee=estimated_fee, )) return None, paths, feedback_token
def get_best_routes_internal( chain_state: ChainState, token_network_id: typing.TokenNetworkID, from_address: typing.InitiatorAddress, to_address: typing.TargetAddress, amount: int, previous_address: typing.Optional[typing.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_identifier( chain_state, token_network_id, ) network_statuses = views.get_networkstatuses(chain_state) neighbors_heap = 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_id, partner_address, ) channel_constraints_fulfilled = check_channel_constraints( channel_state=channel_state, from_address=from_address, partner_address=partner_address, amount=amount, network_statuses=network_statuses, routing_module='Internal Routing', ) if not channel_constraints_fulfilled: continue nonrefundable = amount > channel.get_distributable( channel_state.partner_state, channel_state.our_state, ) try: length = networkx.shortest_path_length( token_network.network_graph.network, partner_address, to_address, ) heappush( neighbors_heap, (length, nonrefundable, partner_address, channel_state.identifier), ) except (networkx.NetworkXNoPath, networkx.NodeNotFound): pass if not neighbors_heap: log.warning( 'No routes available', from_address=pex(from_address), to_address=pex(to_address), ) return list() while neighbors_heap: *_, partner_address, channel_state_id = heappop(neighbors_heap) route_state = RouteState(partner_address, channel_state_id) available_routes.append(route_state) return available_routes
def handle_contract_send_channelunlock( raiden: "RaidenService", chain_state: ChainState, channel_unlock_event: ContractSendChannelBatchUnlock, ): assert raiden.wal, "The Raiden Service must be initialize to handle events" canonical_identifier = channel_unlock_event.canonical_identifier token_network_identifier = canonical_identifier.token_network_address channel_identifier = canonical_identifier.channel_identifier participant = channel_unlock_event.participant payment_channel: PaymentChannel = raiden.chain.payment_channel( canonical_identifier=canonical_identifier ) channel_state = get_channelstate_by_token_network_and_partner( chain_state=chain_state, token_network_id=TokenNetworkID(token_network_identifier), 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)}, token_network:pex(token_network_identifier)" ) 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 != EMPTY_HASH # we want to unlock, because there are unlocked/unclaimed locks search_state_changes = partner_locksroot != EMPTY_HASH 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, ) state_change_identifier = state_change_record.state_change_identifier if not state_change_identifier: raise RaidenUnrecoverableError( f"Failed to find state that matches the current channel locksroots. " f"chain_id:{raiden.chain.network_id} " f"token_network:{to_checksum_address(token_network_identifier)} " f"channel:{channel_identifier} " f"participant:{to_checksum_address(participant)} " f"our_locksroot:{to_hex(our_locksroot)} " f"partner_locksroot:{to_hex(partner_locksroot)} " ) restored_channel_state = channel_state_until_state_change( raiden=raiden, canonical_identifier=canonical_identifier, state_change_identifier=state_change_identifier, ) assert restored_channel_state is not None 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( raiden=raiden, payment_channel=payment_channel, end_state=restored_channel_state.partner_state, participant=our_address, partner=partner_address, ) 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, ) state_change_identifier = event_record.state_change_identifier if not state_change_identifier: raise RaidenUnrecoverableError( f"Failed to find event that match current channel locksroots. " f"chain_id:{raiden.chain.network_id} " f"token_network:{to_checksum_address(token_network_identifier)} " f"channel:{channel_identifier} " f"participant:{to_checksum_address(participant)} " f"our_locksroot:{to_hex(our_locksroot)} " f"partner_locksroot:{to_hex(partner_locksroot)} " ) restored_channel_state = channel_state_until_state_change( raiden=raiden, canonical_identifier=canonical_identifier, state_change_identifier=state_change_identifier, ) assert restored_channel_state is not None 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: unlock( raiden=raiden, payment_channel=payment_channel, end_state=restored_channel_state.our_state, participant=partner_address, partner=our_address, )
def test_handle_contract_send_channelunlock_already_unlocked(): """This is a test for the scenario where the onchain unlock has already happened when we get to handle our own send unlock transaction. Regression test for https://github.com/raiden-network/raiden/issues/3152 """ channel_identifier = 1 payment_network_identifier = make_address() token_network_identifier = make_address() participant = make_address() raiden = make_raiden_service_mock( payment_network_identifier=payment_network_identifier, token_network_identifier=token_network_identifier, channel_identifier=channel_identifier, partner=participant, ) channel_state = get_channelstate_by_token_network_and_partner( chain_state=state_from_raiden(raiden), token_network_id=token_network_identifier, partner_address=participant, ) channel_state.our_state.onchain_locksroot = EMPTY_MERKLE_ROOT channel_state.partner_state.onchain_locksroot = EMPTY_MERKLE_ROOT def detail_participants( # pylint: disable=unused-argument participant1, participant2, block_identifier, channel_identifier): transferred_amount = 1 locked_amount = 1 locksroot = make_32bytes() balance_hash = hash_balance_data(transferred_amount, locked_amount, locksroot) our_details = ParticipantDetails( address=raiden.address, deposit=5, withdrawn=0, is_closer=False, balance_hash=balance_hash, nonce=1, locksroot=locksroot, locked_amount=locked_amount, ) transferred_amount = 1 locked_amount = 1 # Let's mock here that partner locksroot is 0x0 balance_hash = hash_balance_data(transferred_amount, locked_amount, locksroot) partner_details = ParticipantDetails( address=participant, deposit=5, withdrawn=0, is_closer=True, balance_hash=balance_hash, nonce=1, locksroot=EMPTY_HASH, locked_amount=locked_amount, ) return ParticipantsDetails(our_details, partner_details) # make sure detail_participants returns partner data with a locksroot of 0x0 raiden.chain.token_network.detail_participants = detail_participants event = ContractSendChannelBatchUnlock( canonical_identifier=make_canonical_identifier( token_network_address=token_network_identifier, channel_identifier=channel_identifier), participant=participant, triggered_by_block_hash=make_block_hash(), ) # This should not throw an unrecoverable error RaidenEventHandler().on_raiden_event( raiden=raiden, chain_state=raiden.wal.state_manager.current_state, event=event)
def get_best_routes_pfs( chain_state: ChainState, token_network_id: typing.TokenNetworkID, from_address: typing.InitiatorAddress, to_address: typing.TargetAddress, amount: int, previous_address: typing.Optional[typing.Address], config: Dict[str, Any], ) -> Tuple[bool, List[RouteState]]: pfs_path = '{}/api/v1/{}/paths'.format( config['pathfinding_service_address'], to_checksum_address(token_network_id), ) payload = { 'from': to_checksum_address(from_address), 'to': to_checksum_address(to_address), 'value': amount, 'max_paths': config['pathfinding_max_paths'], } # check that the response is successful try: response = requests.get(pfs_path, params=payload, timeout=DEFAULT_HTTP_REQUEST_TIMEOUT) except requests.RequestException: log.warning( 'Could not connect to Pathfinding Service', request=pfs_path, parameters=payload, exc_info=True, ) return False, [] # check that the response contains valid json try: response_json = response.json() except ValueError: log.warning( 'Pathfinding Service returned invalid JSON', response_text=response.text, exc_info=True, ) return False, [] if response.status_code != 200: log_info = { 'error_code': response.status_code, } error = response_json.get('errors') if error is not None: log_info['pfs_error'] = error log.info( 'Pathfinding Service returned error code', **log_info, ) return False, [] if response_json.get('result') is None: log.info( 'Pathfinding Service returned unexpected result', result=response_json, ) return False, [] paths = [] network_statuses = views.get_networkstatuses(chain_state) for path_object in response_json['result']: path = path_object['path'] # get the second entry, as the first one is the node itself # also needs to be converted to canonical representation partner_address = to_canonical_address(path[1]) # don't route back if partner_address == previous_address: continue channel_state = views.get_channelstate_by_token_network_and_partner( chain_state=chain_state, token_network_id=token_network_id, partner_address=partner_address, ) channel_constraints_fulfilled = check_channel_constraints( channel_state=channel_state, from_address=from_address, partner_address=partner_address, amount=amount, network_statuses=network_statuses, routing_module='Pathfinding Service', ) if not channel_constraints_fulfilled: continue paths.append(RouteState( node_address=partner_address, channel_identifier=channel_state.identifier, )) return True, paths
def get_best_routes_internal( chain_state: ChainState, token_network_id: typing.TokenNetworkID, from_address: typing.InitiatorAddress, to_address: typing.TargetAddress, amount: int, previous_address: typing.Optional[typing.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_identifier( chain_state, token_network_id, ) neighbors_heap = 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_id, partner_address, ) if channel.get_status(channel_state) != CHANNEL_STATE_OPENED: log.info( 'Channel is not opened, ignoring', from_address=pex(from_address), partner_address=pex(partner_address), routing_source='Internal Routing', ) continue nonrefundable = amount > channel.get_distributable( channel_state.partner_state, channel_state.our_state, ) try: length = networkx.shortest_path_length( token_network.network_graph.network, partner_address, to_address, ) heappush( neighbors_heap, (length, nonrefundable, partner_address, channel_state.identifier), ) except (networkx.NetworkXNoPath, networkx.NodeNotFound): pass if not neighbors_heap: log.warning( 'No routes available', from_address=pex(from_address), to_address=pex(to_address), ) return list() while neighbors_heap: *_, partner_address, channel_state_id = heappop(neighbors_heap) route_state = RouteState(partner_address, channel_state_id) available_routes.append(route_state) return available_routes
def get_best_routes_pfs( chain_state: ChainState, token_network_id: typing.TokenNetworkID, from_address: typing.InitiatorAddress, to_address: typing.TargetAddress, amount: int, previous_address: typing.Optional[typing.Address], config: Dict[str, Any], ) -> Tuple[bool, List[RouteState]]: pfs_path = '{}/api/v1/{}/paths'.format( config['pathfinding_service_address'], to_checksum_address(token_network_id), ) payload = { 'from': to_checksum_address(from_address), 'to': to_checksum_address(to_address), 'value': amount, 'max_paths': config['pathfinding_max_paths'], } # check that the response is successful try: response = requests.get(pfs_path, params=payload, timeout=DEFAULT_HTTP_REQUEST_TIMEOUT) except requests.RequestException: log.warning( 'Could not connect to Pathfinding Service', request=pfs_path, parameters=payload, exc_info=True, ) return False, [] # check that the response contains valid json try: response_json = response.json() except ValueError: log.warning( 'Pathfinding Service returned invalid JSON', response_text=response.text, exc_info=True, ) return False, [] if response.status_code != 200: log_info = { 'error_code': response.status_code, } error = response_json.get('errors') if error is not None: log_info['pfs_error'] = error log.info( 'Pathfinding Service returned error code', **log_info, ) return False, [] if response_json.get('result') is None: log.info( 'Pathfinding Service returned unexpected result', result=response_json, ) return False, [] paths = [] for path_object in response_json['result']: path = path_object['path'] # get the second entry, as the first one is the node itself # also needs to be converted to canonical representation partner_address = to_canonical_address(path[1]) # don't route back if partner_address == previous_address: continue channel_state = views.get_channelstate_by_token_network_and_partner( chain_state=chain_state, token_network_id=token_network_id, partner_address=partner_address, ) # check channel state if channel.get_status(channel_state) != CHANNEL_STATE_OPENED: log.info( 'Channel is not opened, ignoring', from_address=pex(from_address), partner_address=pex(partner_address), routing_source='Pathfinding Service', ) continue paths.append(RouteState( node_address=partner_address, channel_identifier=channel_state.identifier, )) return True, paths
def is_transaction_effect_satisfied( chain_state: ChainState, transaction: ContractSendEvent, state_change: StateChange, ) -> bool: """ True if the side-effect of `transaction` is satisfied by `state_change`. This predicate is used to clear the transaction queue. This should only be done once the expected side effect of a transaction is achieved. This doesn't necessarily mean that the transaction sent by *this* node was mined, but only that *some* transaction which achieves the same side-effect was successfully executed and mined. This distinction is important for restarts and to reduce the number of state changes. On restarts: The state of the on-chain channel could have changed while the node was offline. Once the node learns about the change (e.g. the channel was settled), new transactions can be dispatched by Raiden as a side effect for the on-chain *event* (e.g. do the batch unlock with the latest merkle tree), but the dispatched transaction could have been completed by another agent (e.g. the partner node). For these cases, the transaction from a different address which achieves the same side-effect is sufficient, otherwise unnecessary transactions would be sent by the node. NOTE: The above is not important for transactions sent as a side-effect for a new *block*. On restart the node first synchronizes its state by querying for new events, only after the off-chain state is up-to-date, a Block state change is dispatched. At this point some transactions are not required anymore and therefore are not dispatched. On the number of state changes: Accepting a transaction from another address removes the need for clearing state changes, e.g. when our the node's close transaction fails but its partner's close transaction succeeds. """ # These transactions are not made atomic through the WAL, they are sent # exclusively through the external APIs. # # - ContractReceiveChannelNew # - ContractReceiveChannelNewBalance # - ContractReceiveNewPaymentNetwork # - ContractReceiveNewTokenNetwork # - ContractReceiveRouteNew # # Note: Deposits and Withdraws must consider a transaction with a higher # value as sufficient, because the values are monotonically increasing and # the transaction with a lower value will never be executed. # Transactions are used to change the on-chain state of a channel. It # doesn't matter if the sender of the transaction is the local node or # another node authorized to perform the operation. So, for the following # transactions, as long as the side-effects are the same, the local # transaction can be removed from the queue. # # - An update transfer can be done by a trusted third party (i.e. monitoring service) # - A close transaction can be sent by our partner # - A settle transaction can be sent by anyone # - A secret reveal can be done by anyone # - A lower nonce is not a valid replacement, since that is an older balance # proof # - A larger raiden state change nonce is impossible. # That would require the partner node to produce an invalid balance proof, # and this node to accept the invalid balance proof and sign it is_valid_update_transfer = ( isinstance(state_change, ContractReceiveUpdateTransfer) and isinstance(transaction, ContractSendChannelUpdateTransfer) and state_change.token_network_identifier == transaction.token_network_identifier and state_change.channel_identifier == transaction.channel_identifier and state_change.nonce == transaction.balance_proof.nonce ) if is_valid_update_transfer: return True # The balance proof data cannot be verified, the local close could have # lost a race against a remote close, and the balance proof data would be # the one provided by this node's partner is_valid_close = ( isinstance(state_change, ContractReceiveChannelClosed) and isinstance(transaction, ContractSendChannelClose) and state_change.token_network_identifier == transaction.token_network_identifier and state_change.channel_identifier == transaction.channel_identifier ) if is_valid_close: return True is_valid_settle = ( isinstance(state_change, ContractReceiveChannelSettled) and isinstance(transaction, ContractSendChannelSettle) and state_change.token_network_identifier == transaction.token_network_identifier and state_change.channel_identifier == transaction.channel_identifier ) if is_valid_settle: return True is_valid_secret_reveal = ( isinstance(state_change, ContractReceiveSecretReveal) and isinstance(transaction, ContractSendSecretReveal) and state_change.secret == transaction.secret ) if is_valid_secret_reveal: return True is_batch_unlock = ( isinstance(state_change, ContractReceiveChannelBatchUnlock) and isinstance(transaction, ContractSendChannelBatchUnlock) ) if is_batch_unlock: our_address = chain_state.our_address # Don't assume that because we sent the transaction, we are a # participant partner_address = None if state_change.participant == our_address: partner_address = state_change.partner elif state_change.partner == our_address: partner_address = state_change.participant # Use the second address as the partner address, but check that a # channel exists for our_address and partner_address if partner_address: channel_state = views.get_channelstate_by_token_network_and_partner( chain_state, state_change.token_network_identifier, partner_address, ) if channel_state: is_our_batch_unlock = ( state_change.participant == our_address and state_change.token_network_identifier == transaction.token_network_identifier ) if is_our_batch_unlock: return True return False
def is_transaction_effect_satisfied( chain_state: ChainState, transaction: ContractSendEvent, state_change: StateChange, ) -> bool: """ True if the side-effect of `transaction` is satisfied by `state_change`. This predicate is used to clear the transaction queue. This should only be done once the expected side effect of a transaction is achieved. This doesn't necessarily mean that the transaction sent by *this* node was mined, but only that *some* transaction which achieves the same side-effect was successfully executed and mined. This distinction is important for restarts and to reduce the number of state changes. On restarts: The state of the on-chain channel could have changed while the node was offline. Once the node learns about the change (e.g. the channel was settled), new transactions can be dispatched by Raiden as a side effect for the on-chain *event* (e.g. do the batch unlock with the latest merkle tree), but the dispatched transaction could have been completed by another agent (e.g. the partner node). For these cases, the transaction from a different address which achieves the same side-effect is sufficient, otherwise unnecessary transactions would be sent by the node. NOTE: The above is not important for transactions sent as a side-effect for a new *block*. On restart the node first synchronizes its state by querying for new events, only after the off-chain state is up-to-date, a Block state change is dispatched. At this point some transactions are not required anymore and therefore are not dispatched. On the number of state changes: Accepting a transaction from another address removes the need for clearing state changes, e.g. when our the node's close transaction fails but its partner's close transaction succeeds. """ # These transactions are not made atomic through the WAL, they are sent # exclusively through the external APIs. # # - ContractReceiveChannelNew # - ContractReceiveChannelNewBalance # - ContractReceiveNewPaymentNetwork # - ContractReceiveNewTokenNetwork # - ContractReceiveRouteNew # # Note: Deposits and Withdraws must consider a transaction with a higher # value as sufficient, because the values are monotonically increasing and # the transaction with a lower value will never be executed. # Transactions are used to change the on-chain state of a channel. It # doesn't matter if the sender of the transaction is the local node or # another node authorized to perform the operation. So, for the following # transactions, as long as the side-effects are the same, the local # transaction can be removed from the queue. # # - An update transfer can be done by a trusted third party (i.e. monitoring service) # - A close transaction can be sent by our partner # - A settle transaction can be sent by anyone # - A secret reveal can be done by anyone # - A lower nonce is not a valid replacement, since that is an older balance # proof # - A larger raiden state change nonce is impossible. # That would require the partner node to produce an invalid balance proof, # and this node to accept the invalid balance proof and sign it is_valid_update_transfer = ( isinstance(state_change, ContractReceiveUpdateTransfer) and isinstance(transaction, ContractSendChannelUpdateTransfer) and state_change.token_network_identifier == transaction.token_network_identifier and state_change.channel_identifier == transaction.channel_identifier and state_change.nonce == transaction.balance_proof.nonce ) if is_valid_update_transfer: return True # The balance proof data cannot be verified, the local close could have # lost a race against a remote close, and the balance proof data would be # the one provided by this node's partner is_valid_close = ( isinstance(state_change, ContractReceiveChannelClosed) and isinstance(transaction, ContractSendChannelClose) and state_change.token_network_identifier == transaction.token_network_identifier and state_change.channel_identifier == transaction.channel_identifier ) if is_valid_close: return True is_valid_settle = ( isinstance(state_change, ContractReceiveChannelSettled) and isinstance(transaction, ContractSendChannelSettle) and state_change.token_network_identifier == transaction.token_network_identifier and state_change.channel_identifier == transaction.channel_identifier ) if is_valid_settle: return True is_valid_secret_reveal = ( isinstance(state_change, ContractReceiveSecretReveal) and isinstance(transaction, ContractSendSecretReveal) and state_change.secret == transaction.secret ) if is_valid_secret_reveal: return True is_batch_unlock = ( isinstance(state_change, ContractReceiveChannelBatchUnlock) and isinstance(transaction, ContractSendChannelBatchUnlock) ) if is_batch_unlock: our_address = chain_state.our_address # Don't assume that because we sent the transaction, we are a # participant partner_address = None if state_change.participant == our_address: partner_address = state_change.partner elif state_change.partner == our_address: partner_address = state_change.participant # Use the second address as the partner address, but check that a # channel exists for our_address and partner_address if partner_address: channel_state = views.get_channelstate_by_token_network_and_partner( chain_state, state_change.token_network_identifier, partner_address, ) if channel_state: is_our_batch_unlock = ( state_change.participant == our_address and state_change.token_network_identifier == transaction.token_network_identifier ) if is_our_batch_unlock: return True return False
def test_mediated_transfer_with_node_consuming_more_than_allocated_fee( raiden_network, number_of_nodes, deposit, token_addresses, network_wait): """ Tests a mediator node consuming more fees than allocated. Which means that the initiator will not reveal the secret to the target. """ app0, app1, app2 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) token_network_registry_address = app0.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( chain_state, token_network_registry_address, token_address) assert token_network_address amount = PaymentAmount(100) fee = FeeAmount(5) fee_margin = calculate_fee_margin(amount, fee) app1_app2_channel_state = views.get_channelstate_by_token_network_and_partner( chain_state=views.state_from_raiden(app1.raiden), token_network_address=token_network_address, partner_address=app2.raiden.address, ) assert app1_app2_channel_state # Let app1 consume all of the allocated mediation fee app1_app2_channel_state.fee_schedule = FeeScheduleState( flat=FeeAmount(fee * 2)) secret = factories.make_secret(0) secrethash = sha256_secrethash(secret) wait_message_handler = WaitForMessage() app0.raiden.message_handler = wait_message_handler secret_request_received = wait_message_handler.wait_for_message( SecretRequest, {"secrethash": secrethash}) def get_best_routes_with_fees(*args, **kwargs): routes = get_best_routes_internal(*args, **kwargs) for r in routes: r.estimated_fee = fee return routes with patch("raiden.routing.get_best_routes_internal", get_best_routes_with_fees): app0.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=amount, target=app2.raiden.address, identifier=1, secret=secret, ) app0_app1_channel_state = views.get_channelstate_by_token_network_and_partner( chain_state=views.state_from_raiden(app0.raiden), token_network_address=token_network_address, partner_address=app1.raiden.address, ) assert app0_app1_channel_state msg = "App0 should have the transfer in secrethashes_to_lockedlocks" assert secrethash in app0_app1_channel_state.our_state.secrethashes_to_lockedlocks, msg msg = "App0 should have locked the amount + fee" lock_amount = app0_app1_channel_state.our_state.secrethashes_to_lockedlocks[ secrethash].amount assert lock_amount == amount + fee + fee_margin, msg secret_request_received.wait() app0_chain_state = views.state_from_app(app0) initiator_task = cast( InitiatorTask, app0_chain_state.payment_mapping.secrethashes_to_task[secrethash]) msg = "App0 should have never revealed the secret" transfer_state = initiator_task.manager_state.initiator_transfers[ secrethash].transfer_state assert transfer_state != "transfer_secret_revealed", msg
def get_best_routes( chain_state: ChainState, token_network_id: TokenNetworkID, one_to_n_address: Optional[Address], from_address: InitiatorAddress, to_address: TargetAddress, amount: PaymentAmount, previous_address: Optional[Address], config: Dict[str, Any], privkey: bytes, ) -> Tuple[List[RouteState], Optional[UUID]]: services_config = config.get("services", None) # the pfs should not be requested when the target is linked via a direct channel if to_address in views.all_neighbour_nodes(chain_state): neighbours = get_best_routes_internal( chain_state=chain_state, token_network_id=token_network_id, from_address=from_address, to_address=to_address, amount=amount, previous_address=previous_address, ) channel_state = views.get_channelstate_by_token_network_and_partner( chain_state=chain_state, token_network_id=token_network_id, partner_address=Address(to_address), ) for route_state in neighbours: if to_address == route_state.node_address and ( channel_state # other conditions about e.g. channel state are checked in best routes internal and channel.get_distributable( sender=channel_state.our_state, receiver=channel_state.partner_state) >= amount): return [route_state], None if (services_config and services_config["pathfinding_service_address"] is not None and one_to_n_address is not None): pfs_answer_ok, pfs_routes, pfs_feedback_token = get_best_routes_pfs( chain_state=chain_state, token_network_id=token_network_id, one_to_n_address=one_to_n_address, from_address=from_address, to_address=to_address, amount=amount, previous_address=previous_address, config=services_config, privkey=privkey, ) if pfs_answer_ok: log.info("Received route(s) from PFS", routes=pfs_routes, feedback_token=pfs_feedback_token) return pfs_routes, pfs_feedback_token else: log.warning("Request to Pathfinding Service was not successful, " "falling back to internal routing.") return ( get_best_routes_internal( chain_state=chain_state, token_network_id=token_network_id, from_address=from_address, to_address=to_address, amount=amount, previous_address=previous_address, ), None, )
def get_best_routes( node_state: NodeState, token_network_id: typing.Address, from_address: typing.Address, to_address: typing.Address, amount: int, previous_address: typing.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_identifier( node_state, token_network_id, ) network_statuses = views.get_networkstatuses(node_state) neighbors_heap = get_ordered_partners( token_network.network_graph.network, from_address, to_address, ) if not neighbors_heap: log.warning( 'No routes available from %s to %s' % (pex(from_address), pex(to_address)), ) while neighbors_heap: _, partner_address = heappop(neighbors_heap) channel_state = views.get_channelstate_by_token_network_and_partner( node_state, token_network_id, partner_address, ) # don't send the message backwards if partner_address == previous_address: continue if channel.get_status(channel_state) != CHANNEL_STATE_OPENED: log.info( 'channel %s - %s is not opened, ignoring' % (pex(from_address), pex(partner_address)), ) continue distributable = channel.get_distributable( channel_state.our_state, channel_state.partner_state, ) if amount > distributable: log.info( 'channel %s - %s doesnt have enough funds [%s], ignoring' % (pex(from_address), pex(partner_address), amount), ) continue network_state = network_statuses.get(partner_address, NODE_NETWORK_UNKNOWN) if network_state != NODE_NETWORK_REACHABLE: log.info( 'partner for channel %s - %s is not %s, ignoring' % (pex(from_address), pex(partner_address), NODE_NETWORK_REACHABLE), ) continue route_state = RouteState(partner_address, channel_state.identifier) available_routes.append(route_state) return available_routes
def get_best_routes( chain_state: ChainState, token_network_id: typing.Address, from_address: typing.Address, to_address: typing.Address, amount: int, previous_address: typing.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_identifier( chain_state, token_network_id, ) network_statuses = views.get_networkstatuses(chain_state) neighbors_heap = get_ordered_partners( token_network.network_graph.network, from_address, to_address, ) if not neighbors_heap: log.warning( 'No routes available from %s to %s' % (pex(from_address), pex(to_address)), ) while neighbors_heap: _, partner_address = heappop(neighbors_heap) channel_state = views.get_channelstate_by_token_network_and_partner( chain_state, token_network_id, partner_address, ) # don't send the message backwards if partner_address == previous_address: continue if channel.get_status(channel_state) != CHANNEL_STATE_OPENED: log.info( 'channel %s - %s is not opened, ignoring' % (pex(from_address), pex(partner_address)), ) continue distributable = channel.get_distributable( channel_state.our_state, channel_state.partner_state, ) if amount > distributable: log.info( 'channel %s - %s doesnt have enough funds [%s], ignoring' % (pex(from_address), pex(partner_address), amount), ) continue network_state = network_statuses.get(partner_address, NODE_NETWORK_UNKNOWN) if network_state != NODE_NETWORK_REACHABLE: log.info( 'partner for channel %s - %s is not %s, ignoring' % (pex(from_address), pex(partner_address), NODE_NETWORK_REACHABLE), ) continue route_state = RouteState(partner_address, channel_state.identifier) available_routes.append(route_state) return available_routes