@@ -4,9 +4,11 @@ use crate::accountant::db_access_objects::utils::{
44} ;
55use crate :: accountant:: db_big_integer:: big_int_divider:: BigIntDivider ;
66use crate :: accountant:: { checked_conversion, comma_joined_stringifiable} ;
7+ use crate :: blockchain:: errors:: AppRpcError ;
78use crate :: database:: rusqlite_wrappers:: ConnectionWrapper ;
89use itertools:: Itertools ;
910use masq_lib:: utils:: ExpectValue ;
11+ use serde_derive:: { Deserialize , Serialize } ;
1012use std:: collections:: { HashMap , HashSet } ;
1113use std:: fmt:: { Display , Formatter } ;
1214use std:: str:: FromStr ;
@@ -21,11 +23,29 @@ pub enum FailedPayableDaoError {
2123 SqlExecutionFailed ( String ) ,
2224}
2325
24- #[ derive( Clone , Debug , PartialEq , Eq ) ]
26+ #[ derive( Debug , Clone , PartialEq , Eq , Serialize , Deserialize ) ]
2527pub enum FailureReason {
28+ Submission ( AppRpcError ) ,
29+ Validation ( AppRpcError ) ,
30+ Reverted ,
2631 PendingTooLong ,
27- NonceIssue ,
28- General ,
32+ }
33+
34+ impl Display for FailureReason {
35+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
36+ match serde_json:: to_string ( self ) {
37+ Ok ( json) => write ! ( f, "{}" , json) ,
38+ Err ( _) => write ! ( f, "<invalid FailureReason>" ) ,
39+ }
40+ }
41+ }
42+
43+ impl FromStr for FailureReason {
44+ type Err = String ;
45+
46+ fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
47+ serde_json:: from_str ( s) . map_err ( |e| e. to_string ( ) )
48+ }
2949}
3050
3151#[ derive( Clone , Debug , PartialEq , Eq ) ]
@@ -47,19 +67,6 @@ impl FromStr for FailureStatus {
4767 }
4868}
4969
50- impl FromStr for FailureReason {
51- type Err = String ;
52-
53- fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
54- match s {
55- "PendingTooLong" => Ok ( FailureReason :: PendingTooLong ) ,
56- "NonceIssue" => Ok ( FailureReason :: NonceIssue ) ,
57- "General" => Ok ( FailureReason :: General ) ,
58- _ => Err ( format ! ( "Invalid FailureReason: {}" , s) ) ,
59- }
60- }
61- }
62-
6370#[ derive( Clone , Debug , PartialEq , Eq ) ]
6471pub struct FailedTx {
6572 pub hash : TxHash ,
@@ -174,7 +181,7 @@ impl FailedPayableDao for FailedPayableDaoReal<'_> {
174181 let ( gas_price_wei_high_b, gas_price_wei_low_b) =
175182 BigIntDivider :: deconstruct( gas_price_wei_checked) ;
176183 format!(
177- "('{:?}', '{:?}', {}, {}, {}, {}, {}, {}, '{:? }', '{:?}')" ,
184+ "('{:?}', '{:?}', {}, {}, {}, {}, {}, {}, '{}', '{:?}')" ,
178185 tx. hash,
179186 tx. receiver_address,
180187 amount_high_b,
@@ -350,7 +357,7 @@ impl FailedPayableDaoFactory for DaoFactoryReal {
350357#[ cfg( test) ]
351358mod tests {
352359 use crate :: accountant:: db_access_objects:: failed_payable_dao:: FailureReason :: {
353- General , NonceIssue , PendingTooLong ,
360+ PendingTooLong , Reverted ,
354361 } ;
355362 use crate :: accountant:: db_access_objects:: failed_payable_dao:: FailureStatus :: {
356363 Concluded , RecheckRequired , RetryRequired ,
@@ -363,6 +370,7 @@ mod tests {
363370 make_read_only_db_connection, FailedTxBuilder ,
364371 } ;
365372 use crate :: accountant:: db_access_objects:: utils:: current_unix_timestamp;
373+ use crate :: blockchain:: errors:: { AppRpcError , LocalError , RemoteError } ;
366374 use crate :: blockchain:: test_utils:: make_tx_hash;
367375 use crate :: database:: db_initializer:: {
368376 DbInitializationConfig , DbInitializer , DbInitializerReal ,
@@ -382,7 +390,7 @@ mod tests {
382390 . unwrap ( ) ;
383391 let tx1 = FailedTxBuilder :: default ( )
384392 . hash ( make_tx_hash ( 1 ) )
385- . reason ( NonceIssue )
393+ . reason ( Reverted )
386394 . build ( ) ;
387395 let tx2 = FailedTxBuilder :: default ( )
388396 . hash ( make_tx_hash ( 2 ) )
@@ -563,15 +571,49 @@ mod tests {
563571
564572 #[ test]
565573 fn failure_reason_from_str_works ( ) {
574+ // Submission error
566575 assert_eq ! (
567- FailureReason :: from_str( "PendingTooLong" ) ,
568- Ok ( PendingTooLong )
576+ FailureReason :: from_str( r#"{"Submission":{"Local":{"Decoder":"Test decoder error"}}}"# )
577+ . unwrap( ) ,
578+ FailureReason :: Submission ( AppRpcError :: Local ( LocalError :: Decoder (
579+ "Test decoder error" . to_string( )
580+ ) ) )
569581 ) ;
570- assert_eq ! ( FailureReason :: from_str( "NonceIssue" ) , Ok ( NonceIssue ) ) ;
571- assert_eq ! ( FailureReason :: from_str( "General" ) , Ok ( General ) ) ;
582+
583+ // Validation error
584+ assert_eq ! (
585+ FailureReason :: from_str( r#"{"Validation":{"Remote":{"Web3RpcError":{"code":42,"message":"Test RPC error"}}}}"# ) . unwrap( ) ,
586+ FailureReason :: Validation ( AppRpcError :: Remote ( RemoteError :: Web3RpcError {
587+ code: 42 ,
588+ message: "Test RPC error" . to_string( )
589+ } ) )
590+ ) ;
591+
592+ // Reverted
593+ assert_eq ! (
594+ FailureReason :: from_str( r#"{"Reverted":null}"# ) . unwrap( ) ,
595+ FailureReason :: Reverted
596+ ) ;
597+
598+ // PendingTooLong
599+ assert_eq ! (
600+ FailureReason :: from_str( r#"{"PendingTooLong":null}"# ) . unwrap( ) ,
601+ FailureReason :: PendingTooLong
602+ ) ;
603+
604+ // Invalid Variant
605+ assert_eq ! (
606+ FailureReason :: from_str( r#"{"UnknownReason":null}"# ) . unwrap_err( ) ,
607+ "unknown variant `UnknownReason`, \
608+ expected one of `Submission`, `Validation`, `Reverted`, `PendingTooLong` \
609+ at line 1 column 16"
610+ . to_string( )
611+ ) ;
612+
613+ // Invalid Input
572614 assert_eq ! (
573- FailureReason :: from_str( "InvalidReason" ) ,
574- Err ( "Invalid FailureReason: InvalidReason ". to_string( ) )
615+ FailureReason :: from_str( "random string" ) . unwrap_err ( ) ,
616+ "expected value at line 1 column 1 ". to_string( )
575617 ) ;
576618 }
577619
@@ -640,7 +682,7 @@ mod tests {
640682 . build ( ) ;
641683 let tx2 = FailedTxBuilder :: default ( )
642684 . hash ( make_tx_hash ( 2 ) )
643- . reason ( NonceIssue )
685+ . reason ( Reverted )
644686 . timestamp ( now - 3600 )
645687 . status ( RetryRequired )
646688 . build ( ) ;
@@ -674,7 +716,7 @@ mod tests {
674716 let subject = FailedPayableDaoReal :: new ( wrapped_conn) ;
675717 let tx1 = FailedTxBuilder :: default ( )
676718 . hash ( make_tx_hash ( 1 ) )
677- . reason ( NonceIssue )
719+ . reason ( Reverted )
678720 . status ( RetryRequired )
679721 . build ( ) ;
680722 let tx2 = FailedTxBuilder :: default ( )
0 commit comments