Proof of Concept
diff --git a/miner-apps/translator/src/lib/sv2/upstream/mod.rs b/miner-apps/translator/src/lib/sv2/upstream/mod.rs
--- a/miner-apps/translator/src/lib/sv2/upstream/mod.rs
+++ b/miner-apps/translator/src/lib/sv2/upstream/mod.rs
@@ -577,3 +577,56 @@ impl Upstream {
})
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use async_channel::unbounded;
+ use std::{sync::Arc, time::Duration};
+
+ #[tokio::test]
+ async fn silent_upstream_does_not_hold_setup_forever() {
+ let (upstream_sender, upstream_outbound_receiver) = unbounded::<Sv2Frame>();
+ let (_upstream_inbound_sender, upstream_receiver) = unbounded::<Sv2Frame>();
+ let (channel_manager_sender, _channel_manager_receiver) = unbounded::<Sv2Frame>();
+ let (_channel_manager_sender, channel_manager_input_receiver) =
+ unbounded::<Sv2Frame>();
+
+ let upstream = Upstream {
+ upstream_io: UpstreamIo::new(
+ upstream_receiver,
+ upstream_sender,
+ channel_manager_sender,
+ channel_manager_input_receiver,
+ ),
+ required_extensions: vec![],
+ address: "127.0.0.1:1234".parse().unwrap(),
+ };
+
+ let cancellation_token = CancellationToken::new();
+ let fallback_coordinator = FallbackCoordinator::new();
+ let task_manager = Arc::new(TaskManager::new());
+ let start_task = tokio::spawn(upstream.start(
+ cancellation_token.clone(),
+ fallback_coordinator,
+ task_manager,
+ ));
+
+ tokio::time::timeout(Duration::from_secs(1), upstream_outbound_receiver.recv())
+ .await
+ .expect("translator never sent SetupConnection")
+ .expect("translator outbound channel closed before SetupConnection");
+
+ tokio::time::sleep(Duration::from_secs(12)).await;
+ let setup_timed_out = start_task.is_finished();
+
+ if !setup_timed_out {
+ cancellation_token.cancel();
+ }
+ let _ = tokio::time::timeout(Duration::from_secs(1), start_task).await;
+
+ assert!(
+ setup_timed_out,
+ "translator kept waiting forever for a silent upstream SetupConnection response"
+ );
+ }
+}
Proof of Concept