2 * API Reference: https://ethereum.github.io/beacon-APIs/
5 #![cfg_attr(debug_assertions, allow(unused_variables, unused_imports, dead_code))]
9 net
::{IpAddr
, Ipv4Addr
},
11 time
::{self, Duration
},
14 use anyhow
::{Context
, Result
};
15 use reqwest
::StatusCode
;
16 use serde
::Deserialize
;
17 use serde_json
::{json
, Value
};
19 use crate::config
::Config
;
24 const FILE_CONF
: &str = "config.ron";
25 const CHECK_PERIOD
: Duration
= Duration
::from_secs(5); // 5s.
26 const EMAIL_RESEND_PERIOD
: Duration
= Duration
::from_secs(12 * 60 * 60); // 12h.
27 const BASE_URI
: &str = "http://localhost:5052/eth/v1/";
29 fn main() -> Result
<()> {
30 println!("Staking Watchdog");
32 let config
= Config
::read(FILE_CONF
)?
;
34 println!("Configuration: {:?}", config
);
36 let mut time_last_email_send
= time
::Instant
::now() - EMAIL_RESEND_PERIOD
;
39 let time_beginning_loop
= time
::Instant
::now();
41 if let Err(error
) = check_validators(&config
.pub_keys
) {
42 println!("Error: {:?}", error
);
43 if time
::Instant
::now() - time_last_email_send
>= EMAIL_RESEND_PERIOD
{
45 println!("Sending email...");
47 time_last_email_send
= time
::Instant
::now();
51 let elapsed
= time
::Instant
::now() - time_beginning_loop
;
53 if elapsed
< CHECK_PERIOD
{
54 let to_wait
= CHECK_PERIOD
- elapsed
;
55 thread
::sleep(to_wait
);
66 UnknownCodeFromHealthCheck(u16),
67 ReqwestError(reqwest
::Error
),
68 ValidatorError
{ pub_key
: String
, message
: String
},
69 ValidatorStatusError
{ pub_key
: String
, message
: String
},
72 impl From
<reqwest
::Error
> for CheckError
{
73 fn from(value
: reqwest
::Error
) -> Self {
74 CheckError
::ReqwestError(value
)
78 #[derive(Deserialize, Debug)]
79 struct JsonValidatorState
{
80 data
: JsonValidatorStateData
,
83 #[derive(Deserialize, Debug)]
84 struct JsonValidatorStateData
{
88 #[derive(Deserialize, Debug)]
94 fn check_validators(pub_keys
: &[String
]) -> std
::result
::Result
<(), CheckError
> {
96 let client
= reqwest
::blocking
::Client
::new();
98 let request_health
= client
99 .get(format!("{url}node/health"))
100 .header("accept", "application/json");
101 match request_health
.send() {
103 println!("{resp:?}");
104 match resp
.status().as_u16() {
106 206 => return Err(CheckError
::NotSync
),
107 400 => return Err(CheckError
::InvalidSyncStatus
),
108 503 => return Err(CheckError
::NodeHavingIssues
),
109 code
=> return Err(CheckError
::UnknownCodeFromHealthCheck(code
)),
113 println!("{error:?}");
114 return Err(CheckError
::HttpError(error
.to_string()));
118 return Err(CheckError
::NotSync
);
120 for pub_key
in pub_keys
{
122 .get(format!("{url}beacon/states/head/validators/0x{pub_key}"))
123 .header("accept", "application/json");
124 match request
.send() {
126 println!("{resp:?}");
127 match resp
.status().as_u16() {
129 let json
: JsonValidatorState
= resp
.json()?
;
130 // println!("JSON:\n{:?}", json); // For Debug.
131 if json
.data
.status
!= "active_ongoing" {
132 return Err(CheckError
::ValidatorStatusError
{
133 pub_key
: pub_key
.clone(),
134 message
: format!("Status: {}", json
.data
.status
),
139 let json
: JsonError
= resp
.json()?
;
140 // println!("JSON:\n{:?}", json); // For Debug.
141 return Err(CheckError
::ValidatorError
{
142 pub_key
: pub_key
.clone(),
144 "Http error code: {}, message: {}",
152 println!("{error:?}");
153 return Err(CheckError
::ValidatorError
{
154 pub_key
: pub_key
.clone(),
155 message
: error
.to_string(),
161 // match request_builder
162 // .header("Authorization", format!("Apikey {}", api_key))
166 // if resp.status().is_success() {
167 // let content = resp.text().unwrap();
168 // Ok(serde_json::from_str(&content).unwrap())
170 // Err(Box::new(Error {
171 // message: format!("Request unsuccessful to {}: {:#?}", &url, resp),
175 // Err(error) => Err(Box::new(Error {
176 // message: format!("Error during request to {}: {:?}", &url, error),
182 // 2) Check each validators.