@@ -39,8 +39,8 @@ pub struct Signer {
3939}
4040
4141impl Signer {
42- /// Creates a new `Signer` instance.
43- pub async fn spawn ( signer : impl alloy_signer:: Signer + Send + Sync + ' static ) -> SignerHandle {
42+ /// Creates a new [ `Signer`] instance and [`SignerHandle`] with the provided signer .
43+ fn new ( signer : impl alloy_signer:: Signer + Send + Sync + ' static ) -> ( Self , SignerHandle ) {
4444 let ( req_tx, req_rx) = tokio:: sync:: mpsc:: unbounded_channel ( ) ;
4545 let ( event_tx, event_rx) = tokio:: sync:: mpsc:: unbounded_channel ( ) ;
4646 let signer = Self {
@@ -49,8 +49,14 @@ impl Signer {
4949 in_progress : FuturesOrdered :: new ( ) ,
5050 sender : event_tx,
5151 } ;
52+ ( signer, SignerHandle :: new ( req_tx, event_rx. into ( ) ) )
53+ }
54+
55+ /// Spawns a new `Signer` instance onto the tokio runtime.
56+ pub fn spawn ( signer : impl alloy_signer:: Signer + Send + Sync + ' static ) -> SignerHandle {
57+ let ( signer, handle) = Self :: new ( signer) ;
5258 tokio:: spawn ( signer. run ( ) ) ;
53- SignerHandle :: new ( req_tx , event_rx . into ( ) )
59+ handle
5460 }
5561
5662 /// Execution loop for the signer.
@@ -67,16 +73,24 @@ impl Signer {
6773
6874 }
6975 }
70- Some ( result) = self . in_progress. next( ) => {
76+ Some ( result) = self . in_progress. next( ) , if ! self . in_progress . is_empty ( ) => {
7177 match result {
72- Ok ( event) => self . sender. send( event) . expect( "The event channel is closed" ) ,
78+ Ok ( event) => {
79+ if self . sender. send( event) . is_err( ) {
80+ tracing:: info!( target: "scroll::signer" , "The event channel has been closed - shutting down." ) ;
81+ break ;
82+ }
83+ } ,
7384 Err ( err) => {
74- tracing:: error!( target: "rollup_node ::signer" , ?err, "An error occurred while signing" ) ;
85+ tracing:: error!( target: "scroll ::signer" , ?err, "An error occurred while signing" ) ;
7586 }
7687 }
77-
7888 }
79- else => ( )
89+ else => {
90+ // The request channel is closed, exit the loop.
91+ tracing:: info!( target: "scroll::signer" , "Signer request channel has been closed - shutting down." ) ;
92+ break ;
93+ }
8094 }
8195 }
8296 }
@@ -102,8 +116,9 @@ mod tests {
102116
103117 #[ tokio:: test]
104118 async fn test_signer_local ( ) {
119+ reth_tracing:: init_test_tracing ( ) ;
105120 let signer = PrivateKeySigner :: random ( ) ;
106- let mut handle = Signer :: spawn ( Box :: new ( signer. clone ( ) ) ) . await ;
121+ let mut handle = Signer :: spawn ( Box :: new ( signer. clone ( ) ) ) ;
107122
108123 // Test sending a request
109124 let block = ScrollBlock :: default ( ) ;
@@ -120,4 +135,69 @@ mod tests {
120135 assert_eq ! ( event_block, block) ;
121136 assert_eq ! ( recovered_address, signer. address( ) ) ;
122137 }
138+
139+ // The following tests do not have any assertions, they are just to ensure that the
140+ // shutdown logic works correctly and does not panic. You can observer the logs to see if the
141+ // shutdown logic is executed correctly.
142+
143+ #[ tokio:: test]
144+ async fn test_drop_signer_handle ( ) {
145+ reth_tracing:: init_test_tracing ( ) ;
146+
147+ // Create a local signer and the signer service
148+ let key = PrivateKeySigner :: random ( ) ;
149+ let ( signer, handle) = Signer :: new ( Box :: new ( key. clone ( ) ) ) ;
150+
151+ // Spawn the signer task and capture the JoinHandle
152+ let task = tokio:: spawn ( signer. run ( ) ) ;
153+
154+ // Drop the handle to simulate shutdown
155+ drop ( handle) ;
156+
157+ // Wait for the signer task to complete
158+ task. await . expect ( "Signer task panicked" ) ;
159+ }
160+
161+ #[ tokio:: test]
162+ async fn test_drop_signer_handle_with_wait ( ) {
163+ reth_tracing:: init_test_tracing ( ) ;
164+
165+ // Create a local signer and the signer service
166+ let key = PrivateKeySigner :: random ( ) ;
167+ let ( signer, handle) = Signer :: new ( Box :: new ( key. clone ( ) ) ) ;
168+
169+ // Spawn the signer task and capture the JoinHandle
170+ let task = tokio:: spawn ( signer. run ( ) ) ;
171+
172+ // wait to observe if the else block will be executed.
173+ tokio:: time:: sleep ( tokio:: time:: Duration :: from_millis ( 100 ) ) . await ;
174+
175+ // Drop the handle to simulate shutdown
176+ drop ( handle) ;
177+
178+ // Wait for the signer task to complete
179+ task. await . expect ( "signer task panicked" ) ;
180+ }
181+
182+ #[ tokio:: test]
183+ async fn test_drop_signer_handle_with_request ( ) {
184+ reth_tracing:: init_test_tracing ( ) ;
185+
186+ // Create a local signer and the signer service
187+ let key = PrivateKeySigner :: random ( ) ;
188+ let ( signer, handle) = Signer :: new ( Box :: new ( key. clone ( ) ) ) ;
189+
190+ // Spawn the signer task and capture the JoinHandle
191+ let task = tokio:: spawn ( signer. run ( ) ) ;
192+
193+ // Send a signing request through the handle
194+ let block = ScrollBlock :: default ( ) ;
195+ handle. sign_block ( block. clone ( ) ) . unwrap ( ) ;
196+
197+ // Drop the handle to simulate shutdown
198+ drop ( handle) ;
199+
200+ // Wait for the signer task to complete
201+ task. await . expect ( "Signer task panicked" ) ;
202+ }
123203}
0 commit comments