@@ -2,6 +2,7 @@ use crate::{
22 api:: stats:: StatsSender ,
33 proxy_state:: { DownstreamType , ProxyState } ,
44 shared:: utils:: AbortOnDrop ,
5+ shares_monitor:: { RejectionReason , ShareInfo , SharesMonitor } ,
56 translator:: { error:: Error , utils:: validate_share} ,
67} ;
78
@@ -115,6 +116,7 @@ pub struct Downstream {
115116 pub ( super ) stats_sender : StatsSender ,
116117 pub recent_jobs : RecentJobs ,
117118 pub first_job : Notify < ' static > ,
119+ pub share_monitor : SharesMonitor ,
118120}
119121
120122impl Downstream {
@@ -194,6 +196,7 @@ impl Downstream {
194196 stats_sender,
195197 recent_jobs : RecentJobs :: new ( ) ,
196198 first_job : last_notify. expect ( "we have an assertion at the beginning of this function" ) ,
199+ share_monitor : SharesMonitor :: new ( ) ,
197200 } ) ) ;
198201
199202 if let Err ( e) = start_receive_downstream (
@@ -233,6 +236,31 @@ impl Downstream {
233236 error ! ( "Failed to start notify task: {e}" ) ;
234237 ProxyState :: update_downstream_state ( DownstreamType :: TranslatorDownstream ) ;
235238 } ;
239+
240+ if let Err ( e) = Self :: start_share_monitor ( task_manager. clone ( ) , downstream. clone ( ) ) . await {
241+ error ! ( "Failed to start share monitor task: {e}" ) ;
242+ ProxyState :: update_downstream_state ( DownstreamType :: TranslatorDownstream ) ;
243+ }
244+ }
245+
246+ /// Starts the shares monitor task.
247+ async fn start_share_monitor (
248+ task_manager : Arc < Mutex < TaskManager > > ,
249+ downstream : Arc < Mutex < Self > > ,
250+ ) -> Result < ( ) , Error < ' static > > {
251+ let ( share_monitor, connection_id) =
252+ downstream. safe_lock ( |s| ( s. share_monitor . clone ( ) , s. connection_id ) ) ?;
253+
254+ // Create an abortable task for the shares monitor
255+ let abortable = tokio:: spawn ( async move {
256+ info ! ( "Starting shares monitor for downstream: {}" , connection_id) ;
257+ share_monitor. clone ( ) . monitor ( ) . await ;
258+ } ) ;
259+
260+ // Register the task with the task manager so it can be aborted when needed
261+ TaskManager :: add_shares_monitor ( task_manager, abortable. into ( ) )
262+ . await
263+ . map_err ( |_| Error :: TranslatorTaskManagerFailed )
236264 }
237265
238266 /// Accept connections from one or more SV1 Downstream roles (SV1 Mining Devices) and create a
@@ -353,6 +381,8 @@ impl Downstream {
353381 stats_sender : StatsSender ,
354382 first_job : Notify < ' static > ,
355383 ) -> Self {
384+ use crate :: shares_monitor:: SharesMonitor ;
385+
356386 Downstream {
357387 connection_id,
358388 authorized_names,
@@ -368,6 +398,7 @@ impl Downstream {
368398 first_job,
369399 stats_sender,
370400 recent_jobs : RecentJobs :: new ( ) ,
401+ share_monitor : SharesMonitor :: new ( ) ,
371402 }
372403 }
373404}
@@ -445,9 +476,19 @@ impl IsServer<'static> for Downstream {
445476 "Share rejected: can not convert v1 job id to number. v1 id: {}" ,
446477 request. job_id
447478 ) ;
479+
480+ let share = ShareInfo :: new (
481+ request. user_name . clone ( ) ,
482+ None ,
483+ job_id_as_number. expect ( "checked above" ) as i64 ,
484+ Some ( RejectionReason :: InvalidJobIdFormat ) ,
485+ ) ;
486+ self . share_monitor . insert_share ( share) ;
487+
448488 self . stats_sender . update_rejected_shares ( self . connection_id ) ;
449489 return false ;
450490 }
491+ let job_id = job_id_as_number. clone ( ) . expect ( "checked above" ) as i64 ;
451492 crate :: translator:: utils:: update_share_count ( self . connection_id ) ; // update share count
452493 if let Some ( job) = self
453494 . recent_jobs
@@ -481,6 +522,23 @@ impl IsServer<'static> for Downstream {
481522 // Return false because submit was not properly handled
482523 return false ;
483524 }
525+ // Share is accepted here
526+ let share = ShareInfo :: new (
527+ request. user_name . clone ( ) ,
528+ Some ( met_difficulty) ,
529+ job_id,
530+ None ,
531+ ) ;
532+ self . share_monitor . insert_share ( share) ;
533+ } else {
534+ // met_difficulty is not latest difficulty, so we mark it as rejected
535+ let share = ShareInfo :: new (
536+ request. user_name . clone ( ) ,
537+ None ,
538+ job_id, // rejected because it was not sent upstream
539+ Some ( RejectionReason :: DifficultyMismatch ) ,
540+ ) ;
541+ self . share_monitor . insert_share ( share) ;
484542 }
485543 }
486544 self . stats_sender . update_accepted_shares ( self . connection_id ) ;
@@ -490,11 +548,25 @@ impl IsServer<'static> for Downstream {
490548 ) ;
491549 true
492550 } else {
551+ let share = ShareInfo :: new (
552+ request. user_name . clone ( ) ,
553+ None ,
554+ job_id,
555+ Some ( RejectionReason :: InvalidShare ) ,
556+ ) ;
557+ self . share_monitor . insert_share ( share) ;
493558 error ! ( "Share rejected: Invalid share" ) ;
494559 self . stats_sender . update_rejected_shares ( self . connection_id ) ;
495560 false
496561 }
497562 } else {
563+ let event = ShareInfo :: new (
564+ request. user_name . clone ( ) ,
565+ None ,
566+ job_id,
567+ Some ( RejectionReason :: JobIdNotFound ) ,
568+ ) ;
569+ self . share_monitor . insert_share ( event) ;
498570 error ! (
499571 "Share rejected: can not find job with id {}" ,
500572 request. job_id
0 commit comments