Skip to content

Commit 1322c5b

Browse files
committed
Even more features
1 parent 8653097 commit 1322c5b

File tree

22 files changed

+594
-145
lines changed

22 files changed

+594
-145
lines changed

Cargo.lock

Lines changed: 77 additions & 76 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

watch/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
config.yaml

watch/README.md

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,12 @@ will be able to sync to the beacon node.
4747

4848
1. Run the updater daemon:
4949
```
50-
cargo run -- start-daemon
50+
cargo run --release -- start-daemon
5151
```
5252

5353
1. Start the HTTP API server:
5454
```
55-
cargo run -- serve
55+
cargo run --release -- serve
5656
```
5757

5858
1. Ensure connectivity:
@@ -115,12 +115,6 @@ As beacon.watch continues to develop, more endpoints will be added.
115115
- More API endpoints
116116
- E.g. `/v1/proposer_info?start_epoch={}&end_epoch={}`
117117
- Use new API design.
118-
- Finish API for validators/attestations
119-
120-
121-
- Fix validator updating.
122-
- Only update new validators rather than reloading the whole validator set every update.
123-
- Implement exit epochs (and check for updates)
124118

125119

126120
- Store the config in the database on first run so that we can warn against unexpected config changes.

watch/config.yaml.default

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
---
12
database:
23
user: "postgres"
34
password: "postgres"
@@ -28,6 +29,16 @@ updater:
2829
# Whether to sync the block_packing table.
2930
block_packing: true
3031

32+
blockprint:
33+
# Whether to sync client information from blockprint.
34+
enabled: false
35+
# The URL of the blockprint server.
36+
url: "https://api.blockprint.sigp.io"
37+
# The username used to authenticate to the blockprint server.
38+
username: ""
39+
# The password used to authenticate to the blockprint server.
40+
password: ""
41+
3142
# Slots per Epoch of the target network.
3243
slots_per_epoch: 32
3344
# Log level.

watch/migrations/2022-01-01-000002_validators/up.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ CREATE TABLE validators (
22
index integer PRIMARY KEY,
33
public_key bytea NOT NULL,
44
status text NOT NULL,
5-
balance bigint NOT NULL,
6-
activation_epoch integer NOT NULL,
5+
client text,
6+
activation_epoch integer,
77
exit_epoch integer
88
)

watch/migrations/2022-01-01-000013_suboptimal_attestations/up.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
CREATE TABLE suboptimal_attestations (
2-
epoch_start_slot integer REFERENCES canonical_slots(slot) ON DELETE CASCADE,
2+
epoch_start_slot integer CHECK (epoch_start_slot % 32 = 0) REFERENCES canonical_slots(slot) ON DELETE CASCADE,
33
index integer NOT NULL REFERENCES validators(index) ON DELETE CASCADE,
44
source boolean NOT NULL,
55
head boolean NOT NULL,

watch/src/blockprint/config.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use serde::{Deserialize, Serialize};
2+
3+
pub const fn enabled() -> bool {
4+
false
5+
}
6+
7+
pub const fn url() -> Option<String> {
8+
None
9+
}
10+
11+
pub const fn username() -> Option<String> {
12+
None
13+
}
14+
15+
pub const fn password() -> Option<String> {
16+
None
17+
}
18+
19+
#[derive(Debug, Clone, Serialize, Deserialize)]
20+
pub struct Config {
21+
#[serde(default = "enabled")]
22+
pub enabled: bool,
23+
#[serde(default = "url")]
24+
pub url: Option<String>,
25+
#[serde(default = "username")]
26+
pub username: Option<String>,
27+
#[serde(default = "password")]
28+
pub password: Option<String>,
29+
}
30+
31+
impl Default for Config {
32+
fn default() -> Self {
33+
Config {
34+
enabled: enabled(),
35+
url: url(),
36+
username: username(),
37+
password: password(),
38+
}
39+
}
40+
}

watch/src/blockprint/error.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

watch/src/blockprint/mod.rs

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
use eth2::SensitiveUrl;
2+
use reqwest::{Client, Response, Url};
3+
use serde::{Deserialize, Serialize};
4+
use std::collections::HashMap;
5+
use std::time::Duration;
6+
use types::Slot;
7+
8+
pub use config::Config;
9+
10+
mod config;
11+
mod error;
12+
13+
const TIMEOUT: Duration = Duration::from_secs(50);
14+
15+
#[derive(Debug)]
16+
pub enum Error {
17+
Reqwest(reqwest::Error),
18+
Url(url::ParseError),
19+
BlockprintNotSynced,
20+
Other(String),
21+
}
22+
23+
impl From<reqwest::Error> for Error {
24+
fn from(e: reqwest::Error) -> Self {
25+
Error::Reqwest(e)
26+
}
27+
}
28+
29+
impl From<url::ParseError> for Error {
30+
fn from(e: url::ParseError) -> Self {
31+
Error::Url(e)
32+
}
33+
}
34+
35+
pub struct WatchBlockprintClient {
36+
pub client: Client,
37+
pub server: SensitiveUrl,
38+
pub username: Option<String>,
39+
pub password: Option<String>,
40+
}
41+
42+
#[derive(Debug, Deserialize, Serialize)]
43+
pub struct BlockprintSyncingResponse {
44+
pub greatest_block_slot: Slot,
45+
pub synced: bool,
46+
}
47+
48+
#[derive(Debug, Deserialize, Serialize)]
49+
pub struct BlockprintResponse {
50+
pub proposer_index: i32,
51+
pub slot: Slot,
52+
pub best_guess_single: String,
53+
}
54+
55+
impl WatchBlockprintClient {
56+
async fn get(&self, url: Url) -> Result<Response, Error> {
57+
let mut builder = self.client.get(url).timeout(TIMEOUT);
58+
if let Some(username) = &self.username {
59+
builder = builder.basic_auth(username, self.password.as_ref());
60+
}
61+
let response = builder.send().await.map_err(Error::Reqwest)?;
62+
63+
if !response.status().is_success() {
64+
return Err(Error::Other(response.text().await?));
65+
}
66+
67+
Ok(response)
68+
}
69+
70+
// Returns the `greatest_block_slot` as reported by the Blockprint server.
71+
// Will error if the Blockprint server is not synced.
72+
pub async fn ensure_synced(&self) -> Result<Slot, Error> {
73+
let url = self.server.full.join("sync/")?.join("status")?;
74+
75+
let response = self.get(url).await?;
76+
77+
let result = response.json::<BlockprintSyncingResponse>().await?;
78+
if !result.synced {
79+
return Err(Error::BlockprintNotSynced);
80+
}
81+
82+
Ok(result.greatest_block_slot)
83+
}
84+
85+
pub async fn blockprint_all_validators(
86+
&self,
87+
highest_validator: i32,
88+
) -> Result<HashMap<i32, String>, Error> {
89+
let url = self
90+
.server
91+
.full
92+
.join("validator/")?
93+
.join("blocks/")?
94+
.join("latest")?;
95+
96+
let response = self.get(url).await?;
97+
98+
let mut result = response.json::<Vec<BlockprintResponse>>().await?;
99+
result.retain(|print| print.proposer_index <= highest_validator);
100+
101+
let mut map: HashMap<i32, String> = HashMap::with_capacity(result.len());
102+
for print in result {
103+
map.insert(print.proposer_index, print.best_guess_single);
104+
}
105+
106+
Ok(map)
107+
}
108+
109+
pub async fn blockprint_proposers_between(
110+
&self,
111+
start_slot: Slot,
112+
end_slot: Slot,
113+
) -> Result<HashMap<i32, String>, Error> {
114+
let url = self
115+
.server
116+
.full
117+
.join("blocks/")?
118+
.join(&format!("{start_slot}/{end_slot}"))?;
119+
120+
let response = self.get(url).await?;
121+
122+
let result = response.json::<Vec<BlockprintResponse>>().await?;
123+
124+
let mut map: HashMap<i32, String> = HashMap::with_capacity(result.len());
125+
for print in result {
126+
map.insert(print.proposer_index, print.best_guess_single);
127+
}
128+
129+
Ok(map)
130+
}
131+
}

watch/src/config.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::blockprint::Config as BlockprintConfig;
12
use crate::database::Config as DatabaseConfig;
23
use crate::server::Config as ServerConfig;
34
use crate::updater::Config as UpdaterConfig;
@@ -16,6 +17,8 @@ fn log_level() -> String {
1617

1718
#[derive(Debug, Clone, Serialize, Deserialize)]
1819
pub struct Config {
20+
#[serde(default)]
21+
pub blockprint: BlockprintConfig,
1922
#[serde(default)]
2023
pub database: DatabaseConfig,
2124
#[serde(default)]
@@ -33,6 +36,7 @@ pub struct Config {
3336
impl Default for Config {
3437
fn default() -> Self {
3538
Self {
39+
blockprint: BlockprintConfig::default(),
3640
database: DatabaseConfig::default(),
3741
server: ServerConfig::default(),
3842
updater: UpdaterConfig::default(),

0 commit comments

Comments
 (0)