Как я могу создать SwarmEvent::Behavior из производного NetworkBehaviour?

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

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

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

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

Вот как выглядит мое 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>,
}

а какая моя кадемля 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 себя?

1 ответ

Немного поздно ответить на ваш вопрос, но, возможно, это поможет:

Передача права собственности на структуру данных. Я хочу обновить структуру ClientBehavior, чтобы я мог просто self.my_data_structure.add(result); из метода KademliaEvent inject_event

  • #[derive(NetworkBehaviour)] это вообще НЕ нравится
  • График - это просто структура и не генерирует никаких событий / реализует NetworkBehaviour

В документации по этому поводу говорится следующее:

#[поведение (игнорировать)] можно добавить в поле структуры, чтобы отключить создание делегирования для полей, которые не реализуют NetworkBehaviour.

Поскольку в противном случае для реализации свойства NetworkBehaviour требуются все поля. Это должно сделать это жизнеспособным решением.

В любом случае, если вы пытаетесь изменить структуру, которая выходит за рамки вашего Mdns, я считаю, что наиболее идиоматическим решением для всего этого было бы генерирование настраиваемого события через функцию опроса (вернутьPoll::Ready(GenerateEvent(TOut))), Которые затем можно прослушивать через другую NetworkBehaviourEventProcess осущий для соответствующей структуры, которая должна затем модифицировать структуры. Подобно тому, как реализация libp2p-ping использует очередь событий VecDeque, которая затем распространяется во времяpoll() у вас может быть похожая очередь (отмечена behaviour(ignore)]) в вашем ClientBehaviour, добавьте к нему, испустите событие вpoll() и как-то поймать это конкретное событие в вашем вызывающем коде / цикле ожидания?

Изменить: вы также должны иметь возможность расширить метод опроса, чтобы таким образом можно было возвращать дополнительные события.

¹: Я далеко не достаточно разбираюсь в rust-libp2p и далеко не достаточно опытен, чтобы приводить неопровержимые факты, на самом деле ваш вопрос был для меня своего рода руководством

Другие вопросы по тегам