@@ -26,10 +26,11 @@ use crate::{
2626} ;
2727use codec:: { Decode , Encode } ;
2828use frame_election_provider_support:: NposSolution ;
29+ use jsonrpsee:: { core:: Error as JsonRpseeError , types:: error:: CallError } ;
2930use pallet_election_provider_multi_phase:: { RawSolution , SolutionOf } ;
3031use sp_runtime:: Perbill ;
3132use std:: sync:: Arc ;
32- use subxt:: { error:: RpcError , rpc:: Subscription , tx:: TxStatus } ;
33+ use subxt:: { error:: RpcError , rpc:: Subscription , tx:: TxStatus , Error as SubxtError } ;
3334use tokio:: sync:: Mutex ;
3435
3536pub async fn monitor_cmd < T > ( api : SubxtClient , config : MonitorConfig ) -> Result < ( ) , Error >
4950
5051 log:: info!( target: LOG_TARGET , "Loaded account {}, {:?}" , signer, account_info) ;
5152
53+ if config. dry_run {
54+ // if we want to try-run, ensure the node supports it.
55+ dry_run_works ( & api) . await ?;
56+ }
57+
5258 let mut subscription = heads_subscription ( & api, config. listen ) . await ?;
5359 let ( tx, mut rx) = tokio:: sync:: mpsc:: unbounded_channel :: < Error > ( ) ;
5460 let submit_lock = Arc :: new ( Mutex :: new ( ( ) ) ) ;
@@ -101,9 +107,6 @@ where
101107}
102108
103109fn kill_main_task_if_critical_err ( tx : & tokio:: sync:: mpsc:: UnboundedSender < Error > , err : Error ) {
104- use jsonrpsee:: { core:: Error as JsonRpseeError , types:: error:: CallError } ;
105- use subxt:: Error as SubxtError ;
106-
107110 match err {
108111 Error :: AlreadySubmitted |
109112 Error :: BetterScoreExist |
@@ -130,11 +133,15 @@ fn kill_main_task_if_critical_err(tx: &tokio::sync::mpsc::UnboundedSender<Error>
130133 JsonRpseeError :: Call ( CallError :: Custom ( e) ) => {
131134 const BAD_EXTRINSIC_FORMAT : i32 = 1001 ;
132135 const VERIFICATION_ERROR : i32 = 1002 ;
136+ use jsonrpsee:: types:: error:: ErrorCode ;
133137
134138 // Check if the transaction gets fatal errors from the `author` RPC.
135139 // It's possible to get other errors such as outdated nonce and similar
136140 // but then it should be possible to try again in the next block or round.
137- if e. code ( ) == BAD_EXTRINSIC_FORMAT || e. code ( ) == VERIFICATION_ERROR {
141+ if e. code ( ) == BAD_EXTRINSIC_FORMAT ||
142+ e. code ( ) == VERIFICATION_ERROR || e. code ( ) ==
143+ ErrorCode :: MethodNotFound . code ( )
144+ {
138145 let _ = tx. send ( Error :: Subxt ( SubxtError :: Rpc (
139146 RpcError :: ClientError ( Box :: new ( CallError :: Custom ( e) ) ) ,
140147 ) ) ) ;
@@ -215,8 +222,6 @@ async fn mine_and_submit_solution<T>(
215222 } ,
216223 } ;
217224
218- let _lock = submit_lock. lock ( ) . await ;
219-
220225 if let Err ( e) =
221226 ensure_no_previous_solution :: < T :: Solution > ( & api, hash, signer. account_id ( ) ) . await
222227 {
@@ -231,6 +236,8 @@ async fn mine_and_submit_solution<T>(
231236 return
232237 }
233238
239+ let _lock = submit_lock. lock ( ) . await ;
240+
234241 let ( solution, score) =
235242 match epm:: fetch_snapshot_and_mine_solution :: < T > ( & api, Some ( hash) , config. solver )
236243 . timed ( )
@@ -292,6 +299,20 @@ async fn mine_and_submit_solution<T>(
292299 return
293300 }
294301
302+ if let Err ( e) =
303+ ensure_no_previous_solution :: < T :: Solution > ( & api, best_head, signer. account_id ( ) ) . await
304+ {
305+ log:: debug!(
306+ target: LOG_TARGET ,
307+ "ensure_no_previous_solution failed: {:?}; skipping block: {:?}" ,
308+ e,
309+ best_head,
310+ ) ;
311+
312+ kill_main_task_if_critical_err ( & tx, e) ;
313+ return
314+ }
315+
295316 match ensure_solution_passes_strategy ( & api, best_head, score, config. submission_strategy )
296317 . timed ( )
297318 . await
@@ -323,6 +344,7 @@ async fn mine_and_submit_solution<T>(
323344 hash,
324345 nonce,
325346 config. listen ,
347+ config. dry_run ,
326348 )
327349 . timed ( )
328350 . await
@@ -419,13 +441,22 @@ async fn submit_and_watch_solution<T: MinerConfig + Send + Sync + 'static>(
419441 hash : Hash ,
420442 nonce : u32 ,
421443 listen : Listen ,
444+ dry_run : bool ,
422445) -> Result < ( ) , Error > {
423446 let tx = epm:: signed_solution ( RawSolution { solution, score, round } ) ?;
424447
425448 let xt = api
426449 . tx ( )
427450 . create_signed_with_nonce ( & tx, & * signer, nonce, ExtrinsicParams :: default ( ) ) ?;
428451
452+ if dry_run {
453+ match api. rpc ( ) . dry_run ( xt. encoded ( ) , None ) . await ? {
454+ Ok ( Ok ( ( ) ) ) => ( ) ,
455+ Ok ( Err ( e) ) => return Err ( Error :: TransactionRejected ( format ! ( "{:?}" , e) ) ) ,
456+ Err ( e) => return Err ( Error :: TransactionRejected ( e. to_string ( ) ) ) ,
457+ } ;
458+ }
459+
429460 let mut status_sub = xt. submit_and_watch ( ) . await . map_err ( |e| {
430461 log:: warn!( target: LOG_TARGET , "submit solution failed: {:?}" , e) ;
431462 e
@@ -520,6 +551,29 @@ pub(crate) fn score_passes_strategy(
520551 }
521552}
522553
554+ async fn dry_run_works ( api : & SubxtClient ) -> Result < ( ) , Error > {
555+ if let Err ( SubxtError :: Rpc ( RpcError :: ClientError ( e) ) ) = api. rpc ( ) . dry_run ( & [ ] , None ) . await {
556+ let rpc_err = match e. downcast :: < JsonRpseeError > ( ) {
557+ Ok ( e) => * e,
558+ Err ( _) =>
559+ return Err ( Error :: Other (
560+ "Failed to downcast RPC error; this is a bug please file an issue" . to_string ( ) ,
561+ ) ) ,
562+ } ;
563+
564+ if let JsonRpseeError :: Call ( CallError :: Custom ( e) ) = rpc_err {
565+ if e. message ( ) == "RPC call is unsafe to be called externally" {
566+ return Err ( Error :: Other (
567+ "dry-run requires a RPC endpoint with `--rpc-methods unsafe`; \
568+ either connect to another RPC endpoint or disable dry-run"
569+ . to_string ( ) ,
570+ ) )
571+ }
572+ }
573+ }
574+ Ok ( ( ) )
575+ }
576+
523577#[ cfg( test) ]
524578mod tests {
525579 use super :: * ;
0 commit comments