Skip to content

tProxy: channel is not cleaned up when a Sv1 client disconnects while waiting for the OpenExtendedMiningChannel.Success #620

Description

@GitGab19

Proof of Concept

diff --git a/miner-apps/translator/src/lib/sv1/sv1_server/mod.rs b/miner-apps/translator/src/lib/sv1/sv1_server/mod.rs
--- a/miner-apps/translator/src/lib/sv1/sv1_server/mod.rs
+++ b/miner-apps/translator/src/lib/sv1/sv1_server/mod.rs
@@ -1630,4 +1630,40 @@ mod tests {
         assert_eq!(seq_id, 1);
         assert_eq!(server.sequence_counter.load(Ordering::SeqCst), 2);
     }
+
+    #[tokio::test]
+    async fn test_open_success_for_disconnected_downstream_closes_orphaned_channel() {
+        use stratum_apps::stratum_core::mining_sv2::OpenExtendedMiningChannelSuccess;
+
+        let (channel_manager_sender, server_to_channel_manager_receiver) = unbounded();
+        let (channel_manager_to_server_sender, channel_manager_receiver) = unbounded();
+        let config = create_test_config();
+        let addr = "127.0.0.1:3333".parse().unwrap();
+        let tproxy_mode = TproxyMode::from(config.aggregate_channels);
+        let server = Sv1Server::new(addr, channel_manager_receiver, channel_manager_sender, config, tproxy_mode);
+        let target = hash_rate_to_target(200.0, 5.0).unwrap();
+
+        server.request_id_to_downstream_id.insert(42, 7);
+        channel_manager_to_server_sender
+            .send((
+                Mining::OpenExtendedMiningChannelSuccess(OpenExtendedMiningChannelSuccess {
+                    request_id: 42,
+                    channel_id: 9,
+                    target: target.to_le_bytes().into(),
+                    extranonce_size: 4,
+                    extranonce_prefix: vec![0u8; 4].try_into().unwrap(),
+                    group_channel_id: 0,
+                }),
+                None,
+            ))
+            .await
+            .unwrap();
+
+        server.handle_upstream_message(target).await.unwrap();
+
+        let (message, _) = server_to_channel_manager_receiver
+            .try_recv()
+            .expect("orphaned channel should be closed when its downstream vanished");
+        assert!(matches!(message, Mining::CloseChannel(close) if close.channel_id == 9));
+    }
 }

Suggested Fix

diff --git a/miner-apps/translator/src/lib/sv1/sv1_server/mod.rs b/miner-apps/translator/src/lib/sv1/sv1_server/mod.rs
--- a/miner-apps/translator/src/lib/sv1/sv1_server/mod.rs
+++ b/miner-apps/translator/src/lib/sv1/sv1_server/mod.rs
@@ -854,6 +854,19 @@
                     }
                 } else {
                     error!("Downstream not found for downstream_id: {}", downstream_id);
+                    let reason_code =
+                        Str0255::try_from("downstream disconnected".to_string()).unwrap();
+                    _ = self
+                        .sv1_server_io
+                        .channel_manager_sender
+                        .send((
+                            Mining::CloseChannel(CloseChannel {
+                                channel_id: m.channel_id,
+                                reason_code,
+                            }),
+                            None,
+                        ))
+                        .await;
                 }
             }
 

Metadata

Metadata

Assignees

Type

No type

Projects

Status
Todo 📝
Status
Todo 📝

Relationships

None yet

Development

No branches or pull requests

Issue actions