diff --git a/nodes/nomos-node/src/config.rs b/nodes/nomos-node/src/config.rs index 7ad35aad..80477700 100644 --- a/nodes/nomos-node/src/config.rs +++ b/nodes/nomos-node/src/config.rs @@ -279,8 +279,8 @@ pub fn update_mix( mix.backend.peering_degree = peering_degree; } - if let Some(num_mix_layers) = mix_num_mix_layers { - mix.backend.num_mix_layers = num_mix_layers; + if let Some(_num_mix_layers) = mix_num_mix_layers { + // TODO: Set num_mix_layers to the proper module setting } Ok(()) diff --git a/nomos-mix/message/src/lib.rs b/nomos-mix/message/src/lib.rs index a7d81584..79b6a922 100644 --- a/nomos-mix/message/src/lib.rs +++ b/nomos-mix/message/src/lib.rs @@ -5,7 +5,7 @@ pub use error::Error; use sha2::{Digest, Sha256}; pub const MSG_SIZE: usize = 2048; -pub const NOISE: [u8; MSG_SIZE] = [0; MSG_SIZE]; +pub const DROP_MESSAGE: [u8; MSG_SIZE] = [0; MSG_SIZE]; /// A mock implementation of the Sphinx encoding. /// @@ -55,7 +55,7 @@ pub fn unwrap_message(message: &[u8]) -> Result<(Vec, bool), Error> { } } -/// Check if the message is a noise message. -pub fn is_noise(message: &[u8]) -> bool { - message == NOISE +/// Check if the message is a drop message. +pub fn is_drop_message(message: &[u8]) -> bool { + message == DROP_MESSAGE } diff --git a/nomos-mix/network/src/behaviour.rs b/nomos-mix/network/src/behaviour.rs index a83e5d38..93f55f42 100644 --- a/nomos-mix/network/src/behaviour.rs +++ b/nomos-mix/network/src/behaviour.rs @@ -12,14 +12,16 @@ use libp2p::{ }, Multiaddr, PeerId, }; -use nomos_mix_message::{message_id, unwrap_message}; +use nomos_mix_message::{is_drop_message, message_id}; use crate::{ error::Error, handler::{FromBehaviour, MixConnectionHandler, ToBehaviour}, }; -/// A [`NetworkBehaviour`] that forwards messages between mix nodes. +/// A [`NetworkBehaviour`]: +/// - forwards messages to all connected peers with deduplication. +/// - receives messages from all connected peers. pub struct Behaviour { config: Config, /// Peers that support the mix protocol, and their connection IDs @@ -35,14 +37,13 @@ pub struct Behaviour { #[derive(Debug)] pub struct Config { - pub transmission_rate: f64, pub duplicate_cache_lifespan: u64, } #[derive(Debug)] pub enum Event { - /// A fully unwrapped message received from one of the peers. - FullyUnwrappedMessage(Vec), + /// A message received from one of the peers. + Message(Vec), Error(Error), } @@ -58,39 +59,39 @@ impl Behaviour { } } - /// Publishs a message through the mix network. - /// - /// This function expects that the message was already encoded for the cryptographic mixing - /// (e.g. Sphinx encoding). - /// - /// The message is forward to all connected peers, - /// so that it can arrive in the mix node who can unwrap it one layer. - /// Fully unwrapped messages are returned as the [`MixBehaviourEvent::FullyUnwrappedMessage`]. + /// Publish a message (data or drop) to all connected peers pub fn publish(&mut self, message: Vec) -> Result<(), Error> { - self.duplicate_cache.cache_set(message_id(&message), ()); - self.forward_message(message, None) + if is_drop_message(&message) { + // Bypass deduplication for the drop message + return self.forward_message(message, None); + } + + let msg_id = message_id(&message); + // If the message was already seen, don't forward it again + if self.duplicate_cache.cache_get(&msg_id).is_some() { + return Ok(()); + } + + let result = self.forward_message(message, None); + // Add the message to the cache only if the forwarding was successfully triggered + if result.is_ok() { + self.duplicate_cache.cache_set(msg_id, ()); + } + result } - /// Forwards a message to all connected peers except the one that was received from. + /// Forwards a message to all connected peers except the excluded peer. /// /// Returns [`Error::NoPeers`] if there are no connected peers that support the mix protocol. fn forward_message( &mut self, message: Vec, - propagation_source: Option, + excluded_peer: Option, ) -> Result<(), Error> { - let peer_ids = self - .negotiated_peers - .keys() - .filter(|&peer_id| { - if let Some(propagation_source) = propagation_source { - *peer_id != propagation_source - } else { - true - } - }) - .cloned() - .collect::>(); + let mut peer_ids: HashSet<_> = self.negotiated_peers.keys().collect(); + if let Some(peer) = &excluded_peer { + peer_ids.remove(peer); + } if peer_ids.is_empty() { return Err(Error::NoPeers); @@ -99,7 +100,7 @@ impl Behaviour { for peer_id in peer_ids.into_iter() { tracing::debug!("Registering event for peer {:?} to send msg", peer_id); self.events.push_back(ToSwarm::NotifyHandler { - peer_id, + peer_id: *peer_id, handler: NotifyHandler::Any, event: FromBehaviour::Message(message.clone()), }); @@ -189,6 +190,12 @@ impl NetworkBehaviour for Behaviour { match event { // A message was forwarded from the peer. ToBehaviour::Message(message) => { + // Ignore drop message + if is_drop_message(&message) { + return; + } + + // Add the message to the cache. If it was already seen, ignore it. if self .duplicate_cache .cache_set(message_id(&message), ()) @@ -203,25 +210,10 @@ impl NetworkBehaviour for Behaviour { tracing::error!("Failed to forward message: {e:?}"); } - // Try to unwrap the message. - // TODO: Abstract as Tier 2: Cryptographic Processor & Temporal Processor - match unwrap_message(&message) { - Ok((unwrapped_msg, fully_unwrapped)) => { - if fully_unwrapped { - self.events.push_back(ToSwarm::GenerateEvent( - Event::FullyUnwrappedMessage(unwrapped_msg), - )); - } else if let Err(e) = self.forward_message(unwrapped_msg, None) { - tracing::error!("Failed to forward message: {:?}", e); - } - } - Err(nomos_mix_message::Error::MsgUnwrapNotAllowed) => { - tracing::debug!("Message cannot be unwrapped by this node"); - } - Err(e) => { - tracing::error!("Failed to unwrap message: {:?}", e); - } - } + // Notify the swarm about the received message, + // so that it can be processed by the core protocol module. + self.events + .push_back(ToSwarm::GenerateEvent(Event::Message(message))); } // The connection was fully negotiated by the peer, // which means that the peer supports the mix protocol. diff --git a/nomos-mix/network/src/handler.rs b/nomos-mix/network/src/handler.rs index 7271bdd7..09f45415 100644 --- a/nomos-mix/network/src/handler.rs +++ b/nomos-mix/network/src/handler.rs @@ -2,11 +2,9 @@ use std::{ collections::VecDeque, io, task::{Context, Poll, Waker}, - time::Duration, }; use futures::{future::BoxFuture, AsyncReadExt, AsyncWriteExt, FutureExt}; -use futures_timer::Delay; use libp2p::{ core::upgrade::ReadyUpgrade, swarm::{ @@ -15,20 +13,19 @@ use libp2p::{ }, Stream, StreamProtocol, }; -use nomos_mix_message::{is_noise, MSG_SIZE, NOISE}; -use nomos_mix_queue::{NonMixQueue, Queue}; +use nomos_mix_message::MSG_SIZE; use crate::behaviour::Config; const PROTOCOL_NAME: StreamProtocol = StreamProtocol::new("/nomos/mix/0.1.0"); +// TODO: Consider replacing this struct with libp2p_stream ConnectionHandler +// because we don't implement persistent emission in the per-connection level anymore. /// A [`ConnectionHandler`] that handles the mix protocol. pub struct MixConnectionHandler { inbound_substream: Option, outbound_substream: Option, - interval: Duration, // TODO: use absolute time - timer: Delay, - queue: Box> + Send>, + outbound_msgs: VecDeque>, pending_events_to_behaviour: VecDeque, waker: Option, } @@ -46,15 +43,11 @@ enum OutboundSubstreamState { } impl MixConnectionHandler { - pub fn new(config: &Config) -> Self { - let interval_sec = 1.0 / config.transmission_rate; - let interval = Duration::from_millis((interval_sec * 1000.0) as u64); + pub fn new(_config: &Config) -> Self { Self { inbound_substream: None, outbound_substream: None, - interval, - timer: Delay::new(interval), - queue: Box::new(NonMixQueue::new(NOISE.to_vec())), + outbound_msgs: VecDeque::new(), pending_events_to_behaviour: VecDeque::new(), waker: None, } @@ -109,17 +102,17 @@ impl ConnectionHandler for MixConnectionHandler { } // Process inbound stream + // TODO: Measure message frequencies and compare them to the desired frequencies + // for connection maintenance defined in the Tier 1 spec. tracing::debug!("Processing inbound stream"); if let Some(msg_recv_fut) = self.inbound_substream.as_mut() { match msg_recv_fut.poll_unpin(cx) { Poll::Ready(Ok((stream, msg))) => { tracing::debug!("Received message from inbound stream. Notifying behaviour..."); self.inbound_substream = Some(recv_msg(stream).boxed()); - if !is_noise(&msg) { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - ToBehaviour::Message(msg), - )); - } + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + ToBehaviour::Message(msg), + )); } Poll::Ready(Err(e)) => { tracing::error!("Failed to receive message from inbound stream: {:?}", e); @@ -140,21 +133,22 @@ impl ConnectionHandler for MixConnectionHandler { return Poll::Pending; } // If the substream is idle, and if it's time to send a message, send it. - Some(OutboundSubstreamState::Idle(stream)) => match self.timer.poll_unpin(cx) { - Poll::Ready(_) => { - let msg = self.queue.pop(); - tracing::debug!("Sending message to outbound stream: {:?}", msg); - self.outbound_substream = Some(OutboundSubstreamState::PendingSend( - send_msg(stream, msg).boxed(), - )); - self.timer.reset(self.interval); + Some(OutboundSubstreamState::Idle(stream)) => { + match self.outbound_msgs.pop_front() { + Some(msg) => { + tracing::debug!("Sending message to outbound stream: {:?}", msg); + self.outbound_substream = Some(OutboundSubstreamState::PendingSend( + send_msg(stream, msg).boxed(), + )); + } + None => { + tracing::debug!("Nothing to send to outbound stream"); + self.outbound_substream = Some(OutboundSubstreamState::Idle(stream)); + self.waker = Some(cx.waker().clone()); + return Poll::Pending; + } } - Poll::Pending => { - self.outbound_substream = Some(OutboundSubstreamState::Idle(stream)); - self.waker = Some(cx.waker().clone()); - return Poll::Pending; - } - }, + } // If a message is being sent, check if it's done. Some(OutboundSubstreamState::PendingSend(mut msg_send_fut)) => { match msg_send_fut.poll_unpin(cx) { @@ -191,7 +185,7 @@ impl ConnectionHandler for MixConnectionHandler { fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { match event { FromBehaviour::Message(msg) => { - self.queue.push(msg); + self.outbound_msgs.push_back(msg); } } } diff --git a/nomos-mix/network/src/lib.rs b/nomos-mix/network/src/lib.rs index 4a5e3c94..1de9838c 100644 --- a/nomos-mix/network/src/lib.rs +++ b/nomos-mix/network/src/lib.rs @@ -14,12 +14,12 @@ mod test { swarm::{dummy, NetworkBehaviour, SwarmEvent}, Multiaddr, PeerId, Swarm, SwarmBuilder, }; - use nomos_mix_message::{new_message, MSG_SIZE}; + use nomos_mix_message::MSG_SIZE; use tokio::select; use crate::{behaviour::Config, error::Error, Behaviour, Event}; - /// Check that an wrapped message is forwarded through mix nodes and unwrapped successfully. + /// Check that a published messsage arrives in the peers successfully. #[tokio::test] async fn behaviour() { let k1 = libp2p::identity::Keypair::generate_ed25519(); @@ -33,23 +33,17 @@ mod test { let addr: Multiaddr = "/ip4/127.0.0.1/udp/5073/quic-v1".parse().unwrap(); let addr_with_peer_id = addr.clone().with_p2p(peer_id1).unwrap(); - // Spawn swarm1 - tokio::spawn(async move { - swarm1.listen_on(addr).unwrap(); - loop { - swarm1.select_next_some().await; - } - }); + // Swarm1 listens on the address. + swarm1.listen_on(addr).unwrap(); // Dial to swarm1 from swarm2 tokio::time::sleep(Duration::from_secs(1)).await; swarm2.dial(addr_with_peer_id).unwrap(); tokio::time::sleep(Duration::from_secs(1)).await; - // Prepare a task for swarm2 to publish a two-layer wrapped message, - // receive an one-layer unwrapped message from swarm1, - // and return a fully unwrapped message. + // Swamr2 publishes a message. let task = async { + let msg = vec![1; MSG_SIZE]; let mut msg_published = false; let mut publish_try_interval = tokio::time::interval(Duration::from_secs(1)); loop { @@ -58,18 +52,18 @@ mod test { // (It will fail until swarm2 is connected to swarm1 successfully.) _ = publish_try_interval.tick() => { if !msg_published { - // Prepare a message wrapped in two layers - let msg = new_message(&[1; MSG_SIZE - 1], 2).unwrap(); - msg_published = swarm2.behaviour_mut().publish(msg).is_ok(); + msg_published = swarm2.behaviour_mut().publish(msg.clone()).is_ok(); } } - // Proceed swarm2 - event = swarm2.select_next_some() => { - if let SwarmEvent::Behaviour(Event::FullyUnwrappedMessage(message)) = event { - println!("SWARM2 FULLY_UNWRAPPED_MESSAGE: {:?}", message); + // Proceed swarm1 + event = swarm1.select_next_some() => { + if let SwarmEvent::Behaviour(Event::Message(received_msg)) = event { + assert_eq!(received_msg, msg); break; }; } + // Proceed swarm2 + _ = swarm2.select_next_some() => {} } } }; @@ -94,13 +88,8 @@ mod test { let addr: Multiaddr = "/ip4/127.0.0.1/udp/5074/quic-v1".parse().unwrap(); let addr_with_peer_id = addr.clone().with_p2p(peer_id1).unwrap(); - // Spawn swarm1 - tokio::spawn(async move { - swarm1.listen_on(addr).unwrap(); - loop { - swarm1.select_next_some().await; - } - }); + // Swarm1 listens on the address. + swarm1.listen_on(addr).unwrap(); // Dial to swarm1 from swarm2 tokio::time::sleep(Duration::from_secs(1)).await; @@ -109,18 +98,19 @@ mod test { // Expect all publish attempts to fail with [`Error::NoPeers`] // because swarm2 doesn't have any peers that support the mix protocol. + let msg = vec![1; MSG_SIZE]; let mut publish_try_interval = tokio::time::interval(Duration::from_secs(1)); let mut publish_try_count = 0; loop { select! { _ = publish_try_interval.tick() => { - let msg = new_message(&[10; MSG_SIZE - 1], 1).unwrap(); - assert!(matches!(swarm2.behaviour_mut().publish(msg), Err(Error::NoPeers))); + assert!(matches!(swarm2.behaviour_mut().publish(msg.clone()), Err(Error::NoPeers))); publish_try_count += 1; if publish_try_count >= 10 { break; } } + _ = swarm1.select_next_some() => {} _ = swarm2.select_next_some() => {} } } @@ -130,7 +120,6 @@ mod test { new_swarm_with_behaviour( key, Behaviour::new(Config { - transmission_rate: 1.0, duplicate_cache_lifespan: 60, }), ) diff --git a/nomos-services/data-availability/tests/src/common.rs b/nomos-services/data-availability/tests/src/common.rs index 67716096..8ab1b94e 100644 --- a/nomos-services/data-availability/tests/src/common.rs +++ b/nomos-services/data-availability/tests/src/common.rs @@ -305,7 +305,6 @@ pub fn new_mix_configs(listening_addresses: Vec) -> Vec>(); diff --git a/nomos-services/mix/src/backends/libp2p.rs b/nomos-services/mix/src/backends/libp2p.rs index 65d1448d..7a891d0b 100644 --- a/nomos-services/mix/src/backends/libp2p.rs +++ b/nomos-services/mix/src/backends/libp2p.rs @@ -25,7 +25,7 @@ pub struct Libp2pMixBackend { #[allow(dead_code)] task: JoinHandle<()>, swarm_message_sender: mpsc::Sender, - fully_unwrapped_message_sender: broadcast::Sender>, + incoming_message_sender: broadcast::Sender>, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -36,7 +36,6 @@ pub struct Libp2pMixBackendSettings { pub node_key: ed25519::SecretKey, pub membership: Vec, pub peering_degree: usize, - pub num_mix_layers: usize, } const CHANNEL_SIZE: usize = 64; @@ -47,15 +46,14 @@ impl MixBackend for Libp2pMixBackend { fn new(config: Self::Settings, overwatch_handle: OverwatchHandle) -> Self { let (swarm_message_sender, swarm_message_receiver) = mpsc::channel(CHANNEL_SIZE); - let (fully_unwrapped_message_sender, _) = broadcast::channel(CHANNEL_SIZE); + let (incoming_message_sender, _) = broadcast::channel(CHANNEL_SIZE); let keypair = Keypair::from(ed25519::Keypair::from(config.node_key.clone())); let local_peer_id = keypair.public().to_peer_id(); let mut swarm = MixSwarm::new( keypair, - config.num_mix_layers, swarm_message_receiver, - fully_unwrapped_message_sender.clone(), + incoming_message_sender.clone(), ); swarm @@ -89,25 +87,23 @@ impl MixBackend for Libp2pMixBackend { Self { task, swarm_message_sender, - fully_unwrapped_message_sender, + incoming_message_sender, } } - async fn mix(&self, msg: Vec) { + async fn publish(&self, msg: Vec) { if let Err(e) = self .swarm_message_sender - .send(MixSwarmMessage::Mix(msg)) + .send(MixSwarmMessage::Publish(msg)) .await { tracing::error!("Failed to send message to MixSwarm: {e}"); } } - fn listen_to_fully_unwrapped_messages( - &mut self, - ) -> Pin> + Send>> { + fn listen_to_incoming_messages(&mut self) -> Pin> + Send>> { Box::pin( - BroadcastStream::new(self.fully_unwrapped_message_sender.subscribe()) + BroadcastStream::new(self.incoming_message_sender.subscribe()) .filter_map(|event| async { event.ok() }), ) } @@ -115,29 +111,26 @@ impl MixBackend for Libp2pMixBackend { struct MixSwarm { swarm: Swarm, - num_mix_layers: usize, swarm_messages_receiver: mpsc::Receiver, - fully_unwrapped_messages_sender: broadcast::Sender>, + incoming_message_sender: broadcast::Sender>, } #[derive(Debug)] pub enum MixSwarmMessage { - Mix(Vec), + Publish(Vec), } impl MixSwarm { fn new( keypair: Keypair, - num_mix_layers: usize, swarm_messages_receiver: mpsc::Receiver, - fully_unwrapped_messages_sender: broadcast::Sender>, + incoming_message_sender: broadcast::Sender>, ) -> Self { let swarm = SwarmBuilder::with_existing_identity(keypair) .with_tokio() .with_quic() .with_behaviour(|_| { nomos_mix_network::Behaviour::new(nomos_mix_network::Config { - transmission_rate: 1.0, duplicate_cache_lifespan: 60, }) }) @@ -149,9 +142,8 @@ impl MixSwarm { Self { swarm, - num_mix_layers, swarm_messages_receiver, - fully_unwrapped_messages_sender, + incoming_message_sender, } } @@ -178,18 +170,9 @@ impl MixSwarm { async fn handle_swarm_message(&mut self, msg: MixSwarmMessage) { match msg { - MixSwarmMessage::Mix(msg) => { - tracing::debug!("Wrap msg and send it to mix network: {msg:?}"); - match nomos_mix_message::new_message(&msg, self.num_mix_layers.try_into().unwrap()) - { - Ok(wrapped_msg) => { - if let Err(e) = self.swarm.behaviour_mut().publish(wrapped_msg) { - tracing::error!("Failed to publish message to mix network: {e:?}"); - } - } - Err(e) => { - tracing::error!("Failed to wrap message: {e:?}"); - } + MixSwarmMessage::Publish(msg) => { + if let Err(e) = self.swarm.behaviour_mut().publish(msg) { + tracing::error!("Failed to publish message to mix network: {e:?}"); } } } @@ -197,9 +180,9 @@ impl MixSwarm { fn handle_event(&mut self, event: SwarmEvent) { match event { - SwarmEvent::Behaviour(nomos_mix_network::Event::FullyUnwrappedMessage(msg)) => { - tracing::debug!("Received fully unwrapped message: {msg:?}"); - self.fully_unwrapped_messages_sender.send(msg).unwrap(); + SwarmEvent::Behaviour(nomos_mix_network::Event::Message(msg)) => { + tracing::debug!("Received message from a peer: {msg:?}"); + self.incoming_message_sender.send(msg).unwrap(); } SwarmEvent::Behaviour(nomos_mix_network::Event::Error(e)) => { tracing::error!("Received error from mix network: {e:?}"); diff --git a/nomos-services/mix/src/backends/mod.rs b/nomos-services/mix/src/backends/mod.rs index 24a8e960..f983b2f9 100644 --- a/nomos-services/mix/src/backends/mod.rs +++ b/nomos-services/mix/src/backends/mod.rs @@ -12,9 +12,8 @@ pub trait MixBackend { type Settings: Clone + Debug + Send + Sync + 'static; fn new(config: Self::Settings, overwatch_handle: OverwatchHandle) -> Self; - async fn mix(&self, msg: Vec); - /// Listen to fully unwrapped messages returned from the mix network - /// if this node is the last node that can unwrap the data message. - fn listen_to_fully_unwrapped_messages(&mut self) - -> Pin> + Send>>; + /// Publish a message to the mix network. + async fn publish(&self, msg: Vec); + /// Listen to messages received from the mix network. + fn listen_to_incoming_messages(&mut self) -> Pin> + Send>>; } diff --git a/nomos-services/mix/src/lib.rs b/nomos-services/mix/src/lib.rs index 225fbad7..d937b6c4 100644 --- a/nomos-services/mix/src/lib.rs +++ b/nomos-services/mix/src/lib.rs @@ -8,6 +8,7 @@ use backends::MixBackend; use futures::StreamExt; use network::NetworkAdapter; use nomos_core::wire; +use nomos_mix_message::{new_message, unwrap_message}; use nomos_network::NetworkService; use overwatch_rs::services::{ handle::ServiceStateHandle, @@ -80,16 +81,21 @@ where let network_relay = network_relay.connect().await?; let network_adapter = Network::new(network_relay); - // A channel to listen to fully unwrapped messages returned from the [`MixBackend`] - // if this node is the last node that can unwrap the data message. - let mut fully_unwrapped_messages_receiver = backend.listen_to_fully_unwrapped_messages(); + // TODO: Spawn PersistentTransmission (Tier 1) + // TODO: Spawn Processor (Tier 2) and connect it to PersistentTransmission + + // A channel to listen to messages received from the [`MixBackend`] + let mut incoming_message_stream = backend.listen_to_incoming_messages(); let mut lifecycle_stream = service_state.lifecycle_handle.message_stream(); loop { tokio::select! { - Some(msg) = fully_unwrapped_messages_receiver.next() => { - tracing::debug!("Received fully unwrapped message from mix backend. Broadcasting it to the network service.."); - // Deserialize the received message to get the topic for broadcasting + Some(msg) = incoming_message_stream.next() => { + // TODO: The following logic is wrong and temporary. + // Here we're unwrapping the message and broadcasting it, + // but the message should be handled by Processor and PersistentTransmission. + let (msg, fully_unwrapped) = unwrap_message(&msg).unwrap(); + assert!(fully_unwrapped); match wire::deserialize::>(&msg) { Ok(msg) => { network_adapter.broadcast(msg.message, msg.broadcast_settings).await; @@ -127,7 +133,11 @@ where ServiceMessage::Mix(msg) => { // split sending in two steps to help the compiler understand we do not // need to hold an instance of &I (which is not send) across an await point - let _send = backend.mix(wire::serialize(&msg).unwrap()); + // TODO: The following logic is wrong and temporary. + // Here we're wrapping the message here and publishing the message immediately, + // but the message should be handled by Processor and PersistentTransmission. + let wrapped_msg = new_message(&wire::serialize(&msg).unwrap(), 1).unwrap(); + let _send = backend.publish(wrapped_msg); _send.await } } diff --git a/tests/src/topology/configs/mix.rs b/tests/src/topology/configs/mix.rs index abc396c9..318f2e85 100644 --- a/tests/src/topology/configs/mix.rs +++ b/tests/src/topology/configs/mix.rs @@ -28,7 +28,6 @@ pub fn create_mix_configs(ids: &[[u8; 32]]) -> Vec { node_key, membership: Vec::new(), peering_degree: 1, - num_mix_layers: 1, }, } })