Как я могу испустить SwarmEvent :: Behavior из производного NetworkBehaviour? - PullRequest
0 голосов
/ 19 января 2020

Я собираюсь написать поведение сети, использующее mDNS, floodsub и kademlia DHT. До сих пор я заставил каждую из этих служб работать, но не смог использовать ответы этих служб для чего-либо значимого.

В идеале, я бы смог передать данные, которые приходят из события процесса поведения, подобного тому, которое будет реализовано для Kad DHT, в основной Swarm poll l oop. Например, в моем случае у меня есть ссылка на структуру, представляющую граф, который сохраняется на диске через базу данных sled. Право собственности на эту структуру существует в методе, который опрашивает рой. Как мне go обновить этот график (например, добавить запись) при получении KademliaEvent?

Решения, которые я пробовал:

  • Перенос права собственности на данные структуру, которую я хочу обновить до структуры ClientBehavior, чтобы я мог просто self.my_data_structure.add(result); из KademliaEvent inject_event метода
    • #[derive(NetworkBehaviour)] НЕ нравится это вообще
    • Graph является просто структурой и не генерирует никаких событий / реализаций NetworkBehaviour
  • Создание Context структуры, которая derive s NetworkBehaviour и может использоваться для передачи ответ туда и обратно между методом опроса и соответствующим inject_event методом
    • #[derive(NetworkBehaviour)] не помещается приятно с Arc s / Mutex es

Вот как выглядит мой NetworkBehavior:

/// A network behavior describing a client connected to a pub-sub compatible,
/// optionally mDNS-compatible network. Such a "behavior" may be implemented for
/// any libp2p transport, but any transport used with this behavior must implement
/// asynchronous reading & writing capabilities.
#[derive(NetworkBehaviour)]
pub struct ClientBehavior<TSubstream: AsyncRead + AsyncWrite + Send + Unpin + 'static> {
    /// Some pubsub mechanism bound to the above transport
    pub floodsub: Floodsub<TSubstream>,

    /// Some mDNS service bound to the above transport
    pub mdns: Mdns<TSubstream>,

    /// Allow for the client to do some external discovery on the global network through a KAD DHT
    pub kad_dht: Kademlia<TSubstream, MemoryStore>,
}

и как выглядит моя реализация Kademlia NetworkBehaviourEventProceess:

impl<TSubstream: AsyncRead + AsyncWrite + Send + Unpin + 'static>
    NetworkBehaviourEventProcess<KademliaEvent> for ClientBehavior<TSubstream>
{
    fn inject_event(&mut self, event: KademliaEvent) {
        // Some behavior logic, nothing special, really, just a bunch of matches
        // NOTE: this is when I'd want to update the `Graph`, since KademliaEvent will contain data that I need to put in the `Graph` instance
    }
}

и как я порождаю свой Swarm и мой ClientBehavior:

// Initialize a new behavior for a client that we will generate in the not-so-distant future with the given peerId, alongside
// an mDNS service handler as well as a floodsub instance targeted at the given peer
let mut behavior = ClientBehavior {
    floodsub: Floodsub::new(self.peer_id.clone()),
    mdns: Mdns::new().await?,
    kad_dht: Kademlia::new(self.peer_id.clone(), store),
};

// Iterate through bootstrap addresses
for bootstrap_peer in bootstrap_addresses {
    // NOTE: add_address is a method that isn't provided by #[derive(NetworkBehaviour)].
    // It's just a helper method I wrote that adds the peer to the Floodsub & DHT views.
    behavior.add_address(&bootstrap_peer.0, bootstrap_peer.1); // Add the bootstrap peer to the DHT
}

// Bootstrap the behavior's DHT
behavior.kad_dht.bootstrap();

// Note: here, `self` is a reference to a configuration struct holding a peer ID, and a keypair
let mut swarm = Swarm::new(
    libp2p::build_tcp_ws_secio_mplex_yamux(self.keypair.clone())?,
    behavior,
    self.peer_id.clone(),
); // Initialize a swarm

и как я опрашиваю рой:

// NOTE: Here, `self` has ownership of the aforementioned `Graph` instance. I'd like to update this
// instance from this block, or from the ClientBehavior itself--as long as I'm able to update it once a `KademliaEvent` is received.

// Try to get the address we'll listen on
if let Ok(addr) = format!("/ip4/0.0.0.0/tcp/{}", port).parse::<Multiaddr>() {
    // Try to tell the swarm to listen on this address, return an error if this doesn't work
    if let Err(e) = Swarm::listen_on(&mut swarm, addr.clone()) {
        // Convert the addr err into an io error
        let e: std::io::Error = io::ErrorKind::AddrNotAvailable.into();

        // Return an error
        return Err(e.into());
    };

    // Fetch the hash of the first transaction in the DAG from the network
    swarm
        .kad_dht
        .get_record(&Key::new(&sync::ROOT_TRANSACTION_KEY), Quorum::Majority);

    loop {
        // Poll the swarm
        match swarm.next_event().await {
            // NOTE: Initially, I was under the impression that I would be able to intercept
            // events from the ClientBehavior here. Yet, the below info! call is never reached.
            // This remains the case in libp2p example code that I have experimented with.
            SwarmEvent::Behaviour(e) => info!("idk: {:?}", e),

            _ => debug!("Some other event; this is handled specifically in the actual codebase, but for this question, all I really care about is the above behavior event."),
        };
    }
}

Нужно ли мне просто реализовать NetworkBehaviour сам?

...