docstr: Exchange Route With Another Node
docstr_tok: ["Exchange","Route","With","Another","Node"]
code: def exchange_credentials_with(
      self,
      context: AuthedServiceContext,
      self_node_route: NodeRoute,
      remote_node_route: NodeRoute,
      remote_node_verify_key: SyftVerifyKey,
  ) -> Union[SyftSuccess, SyftError]:
      """Exchange Route With Another Node"""
  
      # Step 1: Validate the Route
      self_node_peer = self_node_route.validate_with_context(
          context=context
      )
  
      if isinstance(self_node_peer, SyftError):
          return self_node_peer
  
      # Step 2: Send the Node Peer to the remote node
      # Also give them their own to validate that it belongs to them
      # random challenge prevents replay attacks
      remote_client: SyftClient = remote_node_route.client_with_context(
          context=context
      )
      random_challenge = secrets.token_bytes(16)
  
      remote_res = remote_client.api.services.network.add_peer(
          peer=self_node_peer,
          challenge=random_challenge,
          self_node_route=remote_node_route,
          verify_key=remote_node_verify_key,
      )
  
      if isinstance(remote_res, SyftError):
          return remote_res
  
      challenge_signature, remote_node_peer = remote_res
  
      # Verifying if the challenge is valid
  
      try:
          remote_node_verify_key.verify_key.verify(
              random_challenge, challenge_signature
          )
      except Exception as e:
          return SyftError(message=str(e))
  
      # save the remote peer for later
      result = self.stash.update_peer(
          context.node.verify_key, remote_node_peer
      )
      if result.is_err():
          return SyftError(message=str(result.err()))
  
      return SyftSuccess(message="Routes Exchanged")
  
imports: import secrets
  from typing import Any
  from typing import Callable
  from typing import List
  from typing import Optional
  from typing import Union
  from result import Result
  from ...abstract_node import NodeType
  from ...client.client import HTTPConnection
  from ...client.client import PythonConnection
  from ...client.client import SyftClient
  from ...node.credentials import SyftVerifyKey
  from ...node.worker_settings import WorkerSettings
  from ...serde.serializable import serializable
  from ...service.settings.settings import NodeSettingsV2
  from ...store.document_store import BaseUIDStoreStash
  from ...store.document_store import DocumentStore
  from ...store.document_store import PartitionKey
  from ...store.document_store import PartitionSettings
  from ...store.document_store import QueryKeys
  from ...types.grid_url import GridURL
  from ...types.transforms import TransformContext
  from ...types.transforms import keep
  from ...types.transforms import transform
  from ...types.transforms import transform_method
  from ...util.telemetry import instrument
  from ..context import AuthedServiceContext
  from ..data_subject.data_subject import NamePartitionKey
  from ..metadata.node_metadata import NodeMetadataV3
  from ..response import SyftError
  from ..response import SyftSuccess
  from ..service import AbstractService
  from ..service import SERVICE_TO_TYPES
  from ..service import TYPE_TO_SERVICE
  from ..service import service_method
  from ..user.user_roles import GUEST_ROLE_LEVEL
  from ..warnings import CRUDWarning
  from .node_peer import NodePeer
  from .routes import HTTPNodeRoute
  from .routes import NodeRoute
  from .routes import PythonNodeRoute
label_desc: Use image node_id 2 for calling the NetworkService obj's underlying member method code with example usage: obj.exchange_credentials_with(context, _node_route, remote_node_route, remote_node_verify_key) and returns: SyftSuccess, self_node_peer, remote_res, SyftError, SyftError