1
1
use alloy:: primitives:: U256 ;
2
2
use anyhow:: { Context , Result } ;
3
+ use directories:: ProjectDirs ;
4
+ use log:: debug;
3
5
use log:: { error, info} ;
6
+ use serde:: { Deserialize , Serialize } ;
7
+ use std:: fs;
8
+ use std:: path:: Path ;
9
+ use std:: path:: PathBuf ;
10
+ use toml;
4
11
5
12
use crate :: validators:: Validator ;
6
13
use shared:: web3:: contracts:: implementations:: work_validators:: synthetic_data_validator:: SyntheticDataWorkValidator ;
7
14
15
+ fn get_default_state_dir ( ) -> Option < String > {
16
+ ProjectDirs :: from ( "com" , "prime" , "validator" )
17
+ . map ( |proj_dirs| proj_dirs. data_local_dir ( ) . to_string_lossy ( ) . into_owned ( ) )
18
+ }
19
+
20
+ fn state_filename ( pool_id : & str ) -> String {
21
+ format ! ( "work_state_{}.toml" , pool_id)
22
+ }
23
+
24
+ #[ derive( Debug , Clone , Serialize , Deserialize ) ]
25
+ struct PersistedWorkState {
26
+ pool_id : U256 ,
27
+ last_validation_timestamp : U256 ,
28
+ }
29
+
8
30
pub struct SyntheticDataValidator {
9
31
pool_id : U256 ,
10
32
validator : SyntheticDataWorkValidator ,
11
33
last_validation_timestamp : U256 ,
34
+ state_dir : Option < PathBuf > ,
12
35
}
13
36
14
37
impl Validator for SyntheticDataValidator {
@@ -20,16 +43,84 @@ impl Validator for SyntheticDataValidator {
20
43
}
21
44
22
45
impl SyntheticDataValidator {
23
- pub fn new ( pool_id_str : String , validator : SyntheticDataWorkValidator ) -> Self {
46
+ pub fn new (
47
+ state_dir : Option < String > ,
48
+ pool_id_str : String ,
49
+ validator : SyntheticDataWorkValidator ,
50
+ ) -> Self {
24
51
let pool_id = pool_id_str. parse :: < U256 > ( ) . expect ( "Invalid pool ID" ) ;
52
+ let default_state_dir = get_default_state_dir ( ) ;
53
+ debug ! ( "Default state dir: {:?}" , default_state_dir) ;
54
+ let state_path = state_dir
55
+ . map ( PathBuf :: from)
56
+ . or_else ( || default_state_dir. map ( PathBuf :: from) ) ;
57
+ debug ! ( "State path: {:?}" , state_path) ;
58
+ let mut last_validation_timestamp: Option < _ > = None ;
59
+
60
+ // Try to load state, log info if creating new file
61
+ if let Some ( path) = & state_path {
62
+ let state_file = path. join ( state_filename ( & pool_id. to_string ( ) ) ) ;
63
+ if !state_file. exists ( ) {
64
+ debug ! (
65
+ "No state file found at {:?}, will create on first state change" ,
66
+ state_file
67
+ ) ;
68
+ } else if let Ok ( Some ( loaded_state) ) =
69
+ SyntheticDataValidator :: load_state ( path, & pool_id. to_string ( ) )
70
+ {
71
+ debug ! ( "Loaded previous state from {:?}" , state_file) ;
72
+ last_validation_timestamp = Some ( loaded_state. last_validation_timestamp ) ;
73
+ } else {
74
+ debug ! ( "Failed to load state from {:?}" , state_file) ;
75
+ }
76
+ }
77
+
78
+ // if no last time, set it to 24 hours ago, as nothing before that can be invalidated
79
+ if last_validation_timestamp. is_none ( ) {
80
+ last_validation_timestamp = Some ( U256 :: from (
81
+ std:: time:: SystemTime :: now ( )
82
+ . duration_since ( std:: time:: UNIX_EPOCH )
83
+ . expect ( "Failed to get current timestamp" )
84
+ . as_secs ( )
85
+ . saturating_sub ( 24 * 60 * 60 ) ,
86
+ ) ) ;
87
+ }
25
88
26
89
Self {
27
90
pool_id,
28
91
validator,
29
- last_validation_timestamp : U256 :: from ( 0 ) ,
92
+ last_validation_timestamp : last_validation_timestamp. unwrap ( ) ,
93
+ state_dir : state_path. clone ( ) ,
30
94
}
31
95
}
32
96
97
+ fn save_state ( & self ) -> Result < ( ) > {
98
+ // Get values without block_on
99
+ let state = PersistedWorkState {
100
+ pool_id : self . pool_id ,
101
+ last_validation_timestamp : self . last_validation_timestamp ,
102
+ } ;
103
+
104
+ if let Some ( ref state_dir) = self . state_dir {
105
+ fs:: create_dir_all ( state_dir) ?;
106
+ let state_path = state_dir. join ( state_filename ( & self . pool_id . to_string ( ) ) ) ;
107
+ let toml = toml:: to_string_pretty ( & state) ?;
108
+ fs:: write ( & state_path, toml) ?;
109
+ debug ! ( "Saved state to {:?}" , state_path) ;
110
+ }
111
+ Ok ( ( ) )
112
+ }
113
+
114
+ fn load_state ( state_dir : & Path , pool_id : & str ) -> Result < Option < PersistedWorkState > > {
115
+ let state_path = state_dir. join ( state_filename ( pool_id) ) ;
116
+ if state_path. exists ( ) {
117
+ let contents = fs:: read_to_string ( state_path) ?;
118
+ let state: PersistedWorkState = toml:: from_str ( & contents) ?;
119
+ return Ok ( Some ( state) ) ;
120
+ }
121
+ Ok ( None )
122
+ }
123
+
33
124
pub async fn validate_work ( & mut self ) -> Result < ( ) > {
34
125
info ! ( "Validating work for pool ID: {:?}" , self . pool_id) ;
35
126
@@ -70,6 +161,8 @@ impl SyntheticDataValidator {
70
161
. as_secs ( ) ,
71
162
) ;
72
163
164
+ self . save_state ( ) ?;
165
+
73
166
Ok ( ( ) )
74
167
}
75
168
}
0 commit comments