Split the VC into crates making it more modular (#6453)

* Starting to modularize the VC

* Revert changes to eth2

* More progress

* More progress

* Compiles

* Merge latest unstable and make it compile

* Fix some lints

* Tests compile

* Merge latest unstable

* Remove unnecessary deps

* Merge latest unstable

* Correct release tests

* Merge latest unstable

* Merge remote-tracking branch 'origin/unstable' into modularize-vc

* Merge branch 'unstable' into modularize-vc

* Revert unnecessary cargo lock changes

* Update validator_client/beacon_node_fallback/Cargo.toml

* Update validator_client/http_metrics/Cargo.toml

* Update validator_client/http_metrics/src/lib.rs

* Update validator_client/initialized_validators/Cargo.toml

* Update validator_client/signing_method/Cargo.toml

* Update validator_client/validator_metrics/Cargo.toml

* Update validator_client/validator_services/Cargo.toml

* Update validator_client/validator_store/Cargo.toml

* Update validator_client/validator_store/src/lib.rs

* Merge remote-tracking branch 'origin/unstable' into modularize-vc

* Fix format string

* Rename doppelganger trait

* Don't drop the tempdir

* Cargo fmt
This commit is contained in:
Age Manning
2024-11-08 12:01:46 +11:00
committed by GitHub
parent ae160ebf07
commit 8e95024945
59 changed files with 1021 additions and 554 deletions

242
Cargo.lock generated
View File

@@ -857,6 +857,23 @@ dependencies = [
"unused_port",
]
[[package]]
name = "beacon_node_fallback"
version = "0.1.0"
dependencies = [
"environment",
"eth2",
"futures",
"itertools 0.10.5",
"serde",
"slog",
"slot_clock",
"strum",
"tokio",
"types",
"validator_metrics",
]
[[package]]
name = "beacon_processor"
version = "0.1.0"
@@ -2208,6 +2225,23 @@ dependencies = [
"syn 2.0.77",
]
[[package]]
name = "doppelganger_service"
version = "0.1.0"
dependencies = [
"beacon_node_fallback",
"environment",
"eth2",
"futures",
"logging",
"parking_lot 0.12.3",
"slog",
"slot_clock",
"task_executor",
"tokio",
"types",
]
[[package]]
name = "dsl_auto_type"
version = "0.1.2"
@@ -3498,6 +3532,18 @@ dependencies = [
"web-time",
]
[[package]]
name = "graffiti_file"
version = "0.1.0"
dependencies = [
"bls",
"hex",
"serde",
"slog",
"tempfile",
"types",
]
[[package]]
name = "group"
version = "0.12.1"
@@ -4200,6 +4246,31 @@ dependencies = [
"hashbrown 0.14.5",
]
[[package]]
name = "initialized_validators"
version = "0.1.0"
dependencies = [
"account_utils",
"bincode",
"bls",
"eth2_keystore",
"filesystem",
"lockfile",
"metrics",
"parking_lot 0.12.3",
"rand",
"reqwest",
"serde",
"serde_json",
"signing_method",
"slog",
"tokio",
"types",
"url",
"validator_dir",
"validator_metrics",
]
[[package]]
name = "inout"
version = "0.1.3"
@@ -5019,6 +5090,7 @@ dependencies = [
"account_manager",
"account_utils",
"beacon_node",
"beacon_node_fallback",
"beacon_processor",
"bls",
"boot_node",
@@ -5032,6 +5104,7 @@ dependencies = [
"eth2_network_config",
"ethereum_hashing",
"futures",
"initialized_validators",
"lighthouse_network",
"lighthouse_version",
"logging",
@@ -5697,6 +5770,7 @@ name = "node_test_rig"
version = "0.2.0"
dependencies = [
"beacon_node",
"beacon_node_fallback",
"environment",
"eth2",
"execution_layer",
@@ -7712,6 +7786,22 @@ dependencies = [
"rand_core",
]
[[package]]
name = "signing_method"
version = "0.1.0"
dependencies = [
"eth2_keystore",
"ethereum_serde_utils",
"lockfile",
"parking_lot 0.12.3",
"reqwest",
"serde",
"task_executor",
"types",
"url",
"validator_metrics",
]
[[package]]
name = "simple_asn1"
version = "0.6.2"
@@ -9147,54 +9237,34 @@ name = "validator_client"
version = "0.3.5"
dependencies = [
"account_utils",
"bincode",
"bls",
"beacon_node_fallback",
"clap",
"clap_utils",
"deposit_contract",
"directory",
"dirs",
"doppelganger_service",
"environment",
"eth2",
"eth2_keystore",
"ethereum_serde_utils",
"fdlimit",
"filesystem",
"futures",
"hex",
"graffiti_file",
"hyper 1.4.1",
"itertools 0.10.5",
"libsecp256k1",
"lighthouse_version",
"lockfile",
"logging",
"malloc_utils",
"initialized_validators",
"metrics",
"monitoring_api",
"parking_lot 0.12.3",
"rand",
"reqwest",
"ring 0.16.20",
"safe_arith",
"sensitive_url",
"serde",
"serde_json",
"slashing_protection",
"slog",
"slot_clock",
"strum",
"sysinfo",
"system_health",
"task_executor",
"tempfile",
"tokio",
"tokio-stream",
"tree_hash",
"types",
"url",
"validator_dir",
"warp",
"warp_utils",
"validator_http_api",
"validator_http_metrics",
"validator_metrics",
"validator_services",
"validator_store",
]
[[package]]
@@ -9215,6 +9285,67 @@ dependencies = [
"types",
]
[[package]]
name = "validator_http_api"
version = "0.1.0"
dependencies = [
"account_utils",
"beacon_node_fallback",
"bls",
"deposit_contract",
"doppelganger_service",
"eth2",
"eth2_keystore",
"ethereum_serde_utils",
"filesystem",
"futures",
"graffiti_file",
"initialized_validators",
"itertools 0.10.5",
"lighthouse_version",
"logging",
"parking_lot 0.12.3",
"rand",
"sensitive_url",
"serde",
"signing_method",
"slashing_protection",
"slog",
"slot_clock",
"sysinfo",
"system_health",
"task_executor",
"tempfile",
"tokio",
"tokio-stream",
"types",
"url",
"validator_dir",
"validator_services",
"validator_store",
"warp",
"warp_utils",
]
[[package]]
name = "validator_http_metrics"
version = "0.1.0"
dependencies = [
"lighthouse_version",
"malloc_utils",
"metrics",
"parking_lot 0.12.3",
"serde",
"slog",
"slot_clock",
"types",
"validator_metrics",
"validator_services",
"validator_store",
"warp",
"warp_utils",
]
[[package]]
name = "validator_manager"
version = "0.1.0"
@@ -9236,7 +9367,54 @@ dependencies = [
"tokio",
"tree_hash",
"types",
"validator_client",
"validator_http_api",
]
[[package]]
name = "validator_metrics"
version = "0.1.0"
dependencies = [
"metrics",
]
[[package]]
name = "validator_services"
version = "0.1.0"
dependencies = [
"beacon_node_fallback",
"bls",
"doppelganger_service",
"environment",
"eth2",
"futures",
"graffiti_file",
"parking_lot 0.12.3",
"safe_arith",
"slog",
"slot_clock",
"tokio",
"tree_hash",
"types",
"validator_metrics",
"validator_store",
]
[[package]]
name = "validator_store"
version = "0.1.0"
dependencies = [
"account_utils",
"doppelganger_service",
"initialized_validators",
"parking_lot 0.12.3",
"serde",
"signing_method",
"slashing_protection",
"slog",
"slot_clock",
"task_executor",
"types",
"validator_metrics",
]
[[package]]
@@ -9516,19 +9694,21 @@ dependencies = [
"eth2_keystore",
"eth2_network_config",
"futures",
"initialized_validators",
"logging",
"parking_lot 0.12.3",
"reqwest",
"serde",
"serde_json",
"serde_yaml",
"slashing_protection",
"slot_clock",
"task_executor",
"tempfile",
"tokio",
"types",
"url",
"validator_client",
"validator_store",
"zip",
]

View File

@@ -83,7 +83,17 @@ members = [
"testing/web3signer_tests",
"validator_client",
"validator_client/beacon_node_fallback",
"validator_client/doppelganger_service",
"validator_client/graffiti_file",
"validator_client/http_api",
"validator_client/http_metrics",
"validator_client/initialized_validators",
"validator_client/signing_method",
"validator_client/slashing_protection",
"validator_client/validator_metrics",
"validator_client/validator_services",
"validator_client/validator_store",
"validator_manager",
@@ -101,6 +111,7 @@ alloy-consensus = "0.3.0"
anyhow = "1"
arbitrary = { version = "1", features = ["derive"] }
async-channel = "1.9.0"
axum = "0.7.7"
bincode = "1"
bitvec = "1"
byteorder = "1"
@@ -129,6 +140,7 @@ exit-future = "0.2"
fnv = "1"
fs2 = "0.4"
futures = "0.3"
graffiti_file = { path = "validator_client/graffiti_file" }
hex = "0.4"
hashlink = "0.9.0"
hyper = "1"
@@ -170,7 +182,7 @@ superstruct = "0.8"
syn = "1"
sysinfo = "0.26"
tempfile = "3"
tokio = { version = "1", features = ["rt-multi-thread", "sync", "signal"] }
tokio = { version = "1", features = ["rt-multi-thread", "sync", "signal", "macros"] }
tokio-stream = { version = "0.1", features = ["sync"] }
tokio-util = { version = "0.7", features = ["codec", "compat", "time"] }
tracing = "0.1.40"
@@ -190,12 +202,15 @@ zip = "0.6"
account_utils = { path = "common/account_utils" }
beacon_chain = { path = "beacon_node/beacon_chain" }
beacon_node = { path = "beacon_node" }
beacon_node_fallback = { path = "validator_client/beacon_node_fallback" }
beacon_processor = { path = "beacon_node/beacon_processor" }
bls = { path = "crypto/bls" }
clap_utils = { path = "common/clap_utils" }
compare_fields = { path = "common/compare_fields" }
deposit_contract = { path = "common/deposit_contract" }
directory = { path = "common/directory" }
doppelganger_service = { path = "validator_client/doppelganger_service" }
validator_services = { path = "validator_client/validator_services" }
environment = { path = "lighthouse/environment" }
eth1 = { path = "beacon_node/eth1" }
eth1_test_rig = { path = "testing/eth1_test_rig" }
@@ -212,6 +227,7 @@ fork_choice = { path = "consensus/fork_choice" }
genesis = { path = "beacon_node/genesis" }
gossipsub = { path = "beacon_node/lighthouse_network/gossipsub/" }
http_api = { path = "beacon_node/http_api" }
initialized_validators = { path = "validator_client/initialized_validators" }
int_to_bytes = { path = "consensus/int_to_bytes" }
kzg = { path = "crypto/kzg" }
metrics = { path = "common/metrics" }
@@ -229,17 +245,23 @@ pretty_reqwest_error = { path = "common/pretty_reqwest_error" }
proto_array = { path = "consensus/proto_array" }
safe_arith = { path = "consensus/safe_arith" }
sensitive_url = { path = "common/sensitive_url" }
signing_method = { path = "validator_client/signing_method" }
slasher = { path = "slasher", default-features = false }
slashing_protection = { path = "validator_client/slashing_protection" }
slot_clock = { path = "common/slot_clock" }
state_processing = { path = "consensus/state_processing" }
store = { path = "beacon_node/store" }
swap_or_not_shuffle = { path = "consensus/swap_or_not_shuffle" }
system_health = { path = "common/system_health" }
task_executor = { path = "common/task_executor" }
types = { path = "consensus/types" }
unused_port = { path = "common/unused_port" }
validator_client = { path = "validator_client" }
validator_dir = { path = "common/validator_dir" }
validator_http_api = { path = "validator_client/http_api" }
validator_http_metrics = { path = "validator_client/http_metrics" }
validator_metrics = { path = "validator_client/validator_metrics" }
validator_store= { path = "validator_client/validator_store" }
warp_utils = { path = "common/warp_utils" }
[profile.maxperf]

View File

@@ -71,6 +71,9 @@ sensitive_url = { workspace = true }
eth1 = { workspace = true }
eth2 = { workspace = true }
beacon_processor = { workspace = true }
beacon_node_fallback = { workspace = true }
initialized_validators = { workspace = true }
[[test]]
name = "lighthouse_tests"

View File

@@ -1,9 +1,8 @@
use validator_client::{
config::DEFAULT_WEB3SIGNER_KEEP_ALIVE, ApiTopic, BeaconNodeSyncDistanceTiers, Config,
};
use beacon_node_fallback::{beacon_node_health::BeaconNodeSyncDistanceTiers, ApiTopic};
use crate::exec::CommandLineTestExec;
use bls::{Keypair, PublicKeyBytes};
use initialized_validators::DEFAULT_WEB3SIGNER_KEEP_ALIVE;
use sensitive_url::SensitiveUrl;
use std::fs::File;
use std::io::Write;
@@ -15,6 +14,7 @@ use std::string::ToString;
use std::time::Duration;
use tempfile::TempDir;
use types::{Address, Slot};
use validator_client::Config;
/// Returns the `lighthouse validator_client` command.
fn base_cmd() -> Command {
@@ -240,7 +240,7 @@ fn fee_recipient_flag() {
.run()
.with_config(|config| {
assert_eq!(
config.fee_recipient,
config.validator_store.fee_recipient,
Some(Address::from_str("0x00000000219ab540356cbb839cbe05303d7705fa").unwrap())
)
});
@@ -430,7 +430,7 @@ fn no_doppelganger_protection_flag() {
fn no_gas_limit_flag() {
CommandLineTest::new()
.run()
.with_config(|config| assert!(config.gas_limit.is_none()));
.with_config(|config| assert!(config.validator_store.gas_limit.is_none()));
}
#[test]
fn gas_limit_flag() {
@@ -438,46 +438,46 @@ fn gas_limit_flag() {
.flag("gas-limit", Some("600"))
.flag("builder-proposals", None)
.run()
.with_config(|config| assert_eq!(config.gas_limit, Some(600)));
.with_config(|config| assert_eq!(config.validator_store.gas_limit, Some(600)));
}
#[test]
fn no_builder_proposals_flag() {
CommandLineTest::new()
.run()
.with_config(|config| assert!(!config.builder_proposals));
.with_config(|config| assert!(!config.validator_store.builder_proposals));
}
#[test]
fn builder_proposals_flag() {
CommandLineTest::new()
.flag("builder-proposals", None)
.run()
.with_config(|config| assert!(config.builder_proposals));
.with_config(|config| assert!(config.validator_store.builder_proposals));
}
#[test]
fn builder_boost_factor_flag() {
CommandLineTest::new()
.flag("builder-boost-factor", Some("150"))
.run()
.with_config(|config| assert_eq!(config.builder_boost_factor, Some(150)));
.with_config(|config| assert_eq!(config.validator_store.builder_boost_factor, Some(150)));
}
#[test]
fn no_builder_boost_factor_flag() {
CommandLineTest::new()
.run()
.with_config(|config| assert_eq!(config.builder_boost_factor, None));
.with_config(|config| assert_eq!(config.validator_store.builder_boost_factor, None));
}
#[test]
fn prefer_builder_proposals_flag() {
CommandLineTest::new()
.flag("prefer-builder-proposals", None)
.run()
.with_config(|config| assert!(config.prefer_builder_proposals));
.with_config(|config| assert!(config.validator_store.prefer_builder_proposals));
}
#[test]
fn no_prefer_builder_proposals_flag() {
CommandLineTest::new()
.run()
.with_config(|config| assert!(!config.prefer_builder_proposals));
.with_config(|config| assert!(!config.validator_store.prefer_builder_proposals));
}
#[test]
fn no_builder_registration_timestamp_override_flag() {
@@ -624,7 +624,7 @@ fn validator_registration_batch_size_zero_value() {
#[test]
fn validator_disable_web3_signer_slashing_protection_default() {
CommandLineTest::new().run().with_config(|config| {
assert!(config.enable_web3signer_slashing_protection);
assert!(config.validator_store.enable_web3signer_slashing_protection);
});
}
@@ -634,7 +634,7 @@ fn validator_disable_web3_signer_slashing_protection() {
.flag("disable-slashing-protection-web3signer", None)
.run()
.with_config(|config| {
assert!(!config.enable_web3signer_slashing_protection);
assert!(!config.validator_store.enable_web3signer_slashing_protection);
});
}
@@ -642,7 +642,7 @@ fn validator_disable_web3_signer_slashing_protection() {
fn validator_web3_signer_keep_alive_default() {
CommandLineTest::new().run().with_config(|config| {
assert_eq!(
config.web3_signer_keep_alive_timeout,
config.initialized_validators.web3_signer_keep_alive_timeout,
DEFAULT_WEB3SIGNER_KEEP_ALIVE
);
});
@@ -655,7 +655,7 @@ fn validator_web3_signer_keep_alive_override() {
.run()
.with_config(|config| {
assert_eq!(
config.web3_signer_keep_alive_timeout,
config.initialized_validators.web3_signer_keep_alive_timeout,
Some(Duration::from_secs(1))
);
});

View File

@@ -11,6 +11,7 @@ types = { workspace = true }
tempfile = { workspace = true }
eth2 = { workspace = true }
validator_client = { workspace = true }
beacon_node_fallback = { workspace = true }
validator_dir = { workspace = true, features = ["insecure_keys"] }
sensitive_url = { workspace = true }
execution_layer = { workspace = true }

View File

@@ -16,12 +16,13 @@ use validator_client::ProductionValidatorClient;
use validator_dir::insecure_keys::build_deterministic_validator_dirs;
pub use beacon_node::{ClientConfig, ClientGenesis, ProductionClient};
pub use beacon_node_fallback::ApiTopic;
pub use environment;
pub use eth2;
pub use execution_layer::test_utils::{
Config as MockServerConfig, MockExecutionConfig, MockServer,
};
pub use validator_client::{ApiTopic, Config as ValidatorConfig};
pub use validator_client::Config as ValidatorConfig;
/// The global timeout for HTTP requests to the beacon node.
const HTTP_TIMEOUT: Duration = Duration::from_secs(8);

View File

@@ -175,7 +175,8 @@ pub fn run_basic_sim(matches: &ArgMatches) -> Result<(), String> {
executor.spawn(
async move {
let mut validator_config = testing_validator_config();
validator_config.fee_recipient = Some(SUGGESTED_FEE_RECIPIENT.into());
validator_config.validator_store.fee_recipient =
Some(SUGGESTED_FEE_RECIPIENT.into());
println!("Adding validator client {}", i);
// Enable broadcast on every 4th node.

View File

@@ -178,7 +178,8 @@ pub fn run_fallback_sim(matches: &ArgMatches) -> Result<(), String> {
executor.spawn(
async move {
let mut validator_config = testing_validator_config();
validator_config.fee_recipient = Some(SUGGESTED_FEE_RECIPIENT.into());
validator_config.validator_store.fee_recipient =
Some(SUGGESTED_FEE_RECIPIENT.into());
println!("Adding validator client {}", i);
network_1
.add_validator_client_with_fallbacks(

View File

@@ -15,7 +15,6 @@ tempfile = { workspace = true }
tokio = { workspace = true }
reqwest = { workspace = true }
url = { workspace = true }
validator_client = { workspace = true }
slot_clock = { workspace = true }
futures = { workspace = true }
task_executor = { workspace = true }
@@ -28,3 +27,6 @@ serde_json = { workspace = true }
zip = { workspace = true }
parking_lot = { workspace = true }
logging = { workspace = true }
initialized_validators = { workspace = true }
slashing_protection = { workspace = true }
validator_store = { workspace = true }

View File

@@ -22,10 +22,14 @@ mod tests {
};
use eth2_keystore::KeystoreBuilder;
use eth2_network_config::Eth2NetworkConfig;
use initialized_validators::{
load_pem_certificate, load_pkcs12_identity, InitializedValidators,
};
use logging::test_logger;
use parking_lot::Mutex;
use reqwest::Client;
use serde::Serialize;
use slashing_protection::{SlashingDatabase, SLASHING_PROTECTION_FILENAME};
use slot_clock::{SlotClock, TestingSlotClock};
use std::env;
use std::fmt::Debug;
@@ -41,13 +45,7 @@ mod tests {
use tokio::time::sleep;
use types::{attestation::AttestationBase, *};
use url::Url;
use validator_client::{
initialized_validators::{
load_pem_certificate, load_pkcs12_identity, InitializedValidators,
},
validator_store::{Error as ValidatorStoreError, ValidatorStore},
SlashingDatabase, SLASHING_PROTECTION_FILENAME,
};
use validator_store::{Error as ValidatorStoreError, ValidatorStore};
/// If the we are unable to reach the Web3Signer HTTP API within this time out then we will
/// assume it failed to start.
@@ -322,7 +320,7 @@ mod tests {
let log = test_logger();
let validator_dir = TempDir::new().unwrap();
let config = validator_client::Config::default();
let config = initialized_validators::Config::default();
let validator_definitions = ValidatorDefinitions::from(validator_definitions);
let initialized_validators = InitializedValidators::from_definitions(
validator_definitions,
@@ -354,7 +352,7 @@ mod tests {
let slot_clock =
TestingSlotClock::new(Slot::new(0), Duration::from_secs(0), Duration::from_secs(1));
let config = validator_client::Config {
let config = validator_store::Config {
enable_web3signer_slashing_protection: slashing_protection_config.local,
..Default::default()
};

View File

@@ -1,7 +1,7 @@
[package]
name = "validator_client"
version = "0.3.5"
authors = ["Paul Hauner <paul@paulhauner.com>", "Age Manning <Age@AgeManning.com>", "Luke Anderson <luke@lukeanderson.com.au>"]
authors = ["Sigma Prime <contact@sigmaprime.io>"]
edition = { workspace = true }
[lib]
@@ -12,52 +12,32 @@ path = "src/lib.rs"
tokio = { workspace = true }
[dependencies]
tree_hash = { workspace = true }
clap = { workspace = true }
slashing_protection = { workspace = true }
slot_clock = { workspace = true }
types = { workspace = true }
safe_arith = { workspace = true }
serde = { workspace = true }
bincode = { workspace = true }
serde_json = { workspace = true }
slog = { workspace = true }
tokio = { workspace = true }
tokio-stream = { workspace = true }
futures = { workspace = true }
dirs = { workspace = true }
directory = { workspace = true }
lockfile = { workspace = true }
environment = { workspace = true }
parking_lot = { workspace = true }
filesystem = { workspace = true }
hex = { workspace = true }
deposit_contract = { workspace = true }
bls = { workspace = true }
eth2 = { workspace = true }
tempfile = { workspace = true }
validator_dir = { workspace = true }
clap_utils = { workspace = true }
eth2_keystore = { workspace = true }
account_utils = { workspace = true }
lighthouse_version = { workspace = true }
warp_utils = { workspace = true }
warp = { workspace = true }
beacon_node_fallback = { workspace = true }
clap = { workspace = true }
clap_utils = { workspace = true }
directory = { workspace = true }
doppelganger_service = { workspace = true }
dirs = { workspace = true }
eth2 = { workspace = true }
environment = { workspace = true }
graffiti_file = { workspace = true }
hyper = { workspace = true }
ethereum_serde_utils = { workspace = true }
libsecp256k1 = { workspace = true }
ring = { workspace = true }
rand = { workspace = true, features = ["small_rng"] }
initialized_validators = { workspace = true }
metrics = { workspace = true }
monitoring_api = { workspace = true }
parking_lot = { workspace = true }
reqwest = { workspace = true }
sensitive_url = { workspace = true }
task_executor = { workspace = true }
reqwest = { workspace = true, features = ["native-tls"] }
url = { workspace = true }
malloc_utils = { workspace = true }
sysinfo = { workspace = true }
system_health = { path = "../common/system_health" }
logging = { workspace = true }
strum = { workspace = true }
itertools = { workspace = true }
slashing_protection = { workspace = true }
serde = { workspace = true }
slog = { workspace = true }
slot_clock = { workspace = true }
types = { workspace = true }
validator_http_api = { workspace = true }
validator_http_metrics = { workspace = true }
validator_metrics = { workspace = true }
validator_services = { workspace = true }
validator_store = { workspace = true }
tokio = { workspace = true }
fdlimit = "0.3.0"

View File

@@ -0,0 +1,22 @@
[package]
name = "beacon_node_fallback"
version = "0.1.0"
edition = { workspace = true }
authors = ["Sigma Prime <contact@sigmaprime.io>"]
[lib]
name = "beacon_node_fallback"
path = "src/lib.rs"
[dependencies]
environment = { workspace = true }
eth2 = { workspace = true }
futures = { workspace = true }
itertools = { workspace = true }
serde = { workspace = true }
slog = { workspace = true }
slot_clock = { workspace = true }
strum = { workspace = true }
tokio = { workspace = true }
types = { workspace = true }
validator_metrics = { workspace = true }

View File

@@ -1,5 +1,8 @@
use super::CandidateError;
use eth2::BeaconNodeHttpClient;
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use slog::{warn, Logger};
use std::cmp::Ordering;
use std::fmt::{Debug, Display, Formatter};
use std::str::FromStr;
@@ -285,6 +288,30 @@ impl BeaconNodeHealth {
}
}
pub async fn check_node_health(
beacon_node: &BeaconNodeHttpClient,
log: &Logger,
) -> Result<(Slot, bool, bool), CandidateError> {
let resp = match beacon_node.get_node_syncing().await {
Ok(resp) => resp,
Err(e) => {
warn!(
log,
"Unable connect to beacon node";
"error" => %e
);
return Err(CandidateError::Offline);
}
};
Ok((
resp.data.head_slot,
resp.data.is_optimistic,
resp.data.el_offline,
))
}
#[cfg(test)]
mod tests {
use super::ExecutionEngineHealth::{Healthy, Unhealthy};
@@ -292,7 +319,7 @@ mod tests {
BeaconNodeHealth, BeaconNodeHealthTier, BeaconNodeSyncDistanceTiers, IsOptimistic,
SyncDistanceTier,
};
use crate::beacon_node_fallback::Config;
use crate::Config;
use std::str::FromStr;
use types::Slot;

View File

@@ -2,12 +2,11 @@
//! "fallback" behaviour; it will try a request on all of the nodes until one or none of them
//! succeed.
use crate::beacon_node_health::{
BeaconNodeHealth, BeaconNodeSyncDistanceTiers, ExecutionEngineHealth, IsOptimistic,
SyncDistanceTier,
pub mod beacon_node_health;
use beacon_node_health::{
check_node_health, BeaconNodeHealth, BeaconNodeSyncDistanceTiers, ExecutionEngineHealth,
IsOptimistic, SyncDistanceTier,
};
use crate::check_synced::check_node_health;
use crate::http_metrics::metrics::{inc_counter_vec, ENDPOINT_ERRORS, ENDPOINT_REQUESTS};
use environment::RuntimeContext;
use eth2::BeaconNodeHttpClient;
use futures::future;
@@ -24,6 +23,7 @@ use std::time::{Duration, Instant};
use strum::{EnumString, EnumVariantNames};
use tokio::{sync::RwLock, time::sleep};
use types::{ChainSpec, Config as ConfigSpec, EthSpec, Slot};
use validator_metrics::{inc_counter_vec, ENDPOINT_ERRORS, ENDPOINT_REQUESTS};
/// Message emitted when the VC detects the BN is using a different spec.
const UPDATE_REQUIRED_LOG_HINT: &str = "this VC or the remote BN may need updating";
@@ -739,7 +739,7 @@ impl ApiTopic {
mod tests {
use super::*;
use crate::beacon_node_health::BeaconNodeHealthTier;
use crate::SensitiveUrl;
use eth2::SensitiveUrl;
use eth2::Timeouts;
use std::str::FromStr;
use strum::VariantNames;

View File

@@ -0,0 +1,20 @@
[package]
name = "doppelganger_service"
version = "0.1.0"
edition = { workspace = true }
authors = ["Sigma Prime <contact@sigmaprime.io>"]
[dependencies]
beacon_node_fallback = { workspace = true }
environment = { workspace = true }
eth2 = { workspace = true }
parking_lot = { workspace = true }
slog = { workspace = true }
slot_clock = { workspace = true }
task_executor = { workspace = true }
tokio = { workspace = true }
types = { workspace = true }
[dev-dependencies]
futures = { workspace = true }
logging = {workspace = true }

View File

@@ -29,8 +29,7 @@
//!
//! Doppelganger protection is a best-effort, last-line-of-defence mitigation. Do not rely upon it.
use crate::beacon_node_fallback::BeaconNodeFallback;
use crate::validator_store::ValidatorStore;
use beacon_node_fallback::BeaconNodeFallback;
use environment::RuntimeContext;
use eth2::types::LivenessResponseData;
use parking_lot::RwLock;
@@ -114,6 +113,13 @@ struct LivenessResponses {
/// validators on the network.
pub const DEFAULT_REMAINING_DETECTION_EPOCHS: u64 = 1;
/// This crate cannot depend on ValidatorStore as validator_store depends on this crate and
/// initialises the doppelganger protection. For this reason, we abstract the validator store
/// functions this service needs through the following trait
pub trait DoppelgangerValidatorStore {
fn get_validator_index(&self, pubkey: &PublicKeyBytes) -> Option<u64>;
}
/// Store the per-validator status of doppelganger checking.
#[derive(Debug, PartialEq)]
pub struct DoppelgangerState {
@@ -280,15 +286,20 @@ impl DoppelgangerService {
/// Starts a reoccurring future which will try to keep the doppelganger service updated each
/// slot.
pub fn start_update_service<E: EthSpec, T: 'static + SlotClock>(
pub fn start_update_service<E, T, V>(
service: Arc<Self>,
context: RuntimeContext<E>,
validator_store: Arc<ValidatorStore<T, E>>,
validator_store: Arc<V>,
beacon_nodes: Arc<BeaconNodeFallback<T, E>>,
slot_clock: T,
) -> Result<(), String> {
) -> Result<(), String>
where
E: EthSpec,
T: 'static + SlotClock,
V: DoppelgangerValidatorStore + Send + Sync + 'static,
{
// Define the `get_index` function as one that uses the validator store.
let get_index = move |pubkey| validator_store.validator_index(&pubkey);
let get_index = move |pubkey| validator_store.get_validator_index(&pubkey);
// Define the `get_liveness` function as one that queries the beacon node API.
let log = service.log.clone();

View File

@@ -0,0 +1,19 @@
[package]
name = "graffiti_file"
version = "0.1.0"
edition = { workspace = true }
authors = ["Sigma Prime <contact@sigmaprime.io>"]
[lib]
name = "graffiti_file"
path = "src/lib.rs"
[dependencies]
serde = { workspace = true }
bls = { workspace = true }
types = { workspace = true }
slog = { workspace = true }
[dev-dependencies]
tempfile = { workspace = true }
hex = { workspace = true }

View File

@@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize};
use slog::warn;
use std::collections::HashMap;
use std::fs::File;
use std::io::{prelude::*, BufReader};
@@ -100,6 +101,27 @@ fn read_line(line: &str) -> Result<(Option<PublicKeyBytes>, Graffiti), Error> {
}
}
// Given the various graffiti control methods, determine the graffiti that will be used for
// the next block produced by the validator with the given public key.
pub fn determine_graffiti(
validator_pubkey: &PublicKeyBytes,
log: &slog::Logger,
graffiti_file: Option<GraffitiFile>,
validator_definition_graffiti: Option<Graffiti>,
graffiti_flag: Option<Graffiti>,
) -> Option<Graffiti> {
graffiti_file
.and_then(|mut g| match g.load_graffiti(validator_pubkey) {
Ok(g) => g,
Err(e) => {
warn!(log, "Failed to read graffiti file"; "error" => ?e);
None
}
})
.or(validator_definition_graffiti)
.or(graffiti_flag)
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -0,0 +1,50 @@
[package]
name = "validator_http_api"
version = "0.1.0"
edition = { workspace = true }
authors = ["Sigma Prime <contact@sigmaprime.io>"]
[lib]
name = "validator_http_api"
path = "src/lib.rs"
[dependencies]
account_utils = { workspace = true }
bls = { workspace = true }
beacon_node_fallback = { workspace = true }
deposit_contract = { workspace = true }
doppelganger_service = { workspace = true }
graffiti_file = { workspace = true }
eth2 = { workspace = true }
eth2_keystore = { workspace = true }
ethereum_serde_utils = { workspace = true }
initialized_validators = { workspace = true }
lighthouse_version = { workspace = true }
logging = { workspace = true }
parking_lot = { workspace = true }
filesystem = { workspace = true }
rand = { workspace = true }
serde = { workspace = true }
signing_method = { workspace = true }
sensitive_url = { workspace = true }
slashing_protection = { workspace = true }
slog = { workspace = true }
slot_clock = { workspace = true }
sysinfo = { workspace = true }
system_health = { workspace = true }
task_executor = { workspace = true }
tempfile = { workspace = true }
tokio = { workspace = true }
tokio-stream = { workspace = true }
types = { workspace = true }
validator_dir = { workspace = true }
validator_store = { workspace = true }
validator_services = { workspace = true }
url = { workspace = true }
warp_utils = { workspace = true }
warp = { workspace = true }
[dev-dependencies]
itertools = { workspace = true }
futures = { workspace = true }
rand = { workspace = true, features = ["small_rng"] }

View File

@@ -1,10 +1,10 @@
use crate::validator_store::ValidatorStore;
use bls::{PublicKey, PublicKeyBytes};
use eth2::types::GenericResponse;
use slog::{info, Logger};
use slot_clock::SlotClock;
use std::sync::Arc;
use types::{Epoch, EthSpec, SignedVoluntaryExit, VoluntaryExit};
use validator_store::ValidatorStore;
pub async fn create_signed_voluntary_exit<T: 'static + SlotClock + Clone, E: EthSpec>(
pubkey: PublicKey,

View File

@@ -1,4 +1,3 @@
use crate::ValidatorStore;
use account_utils::validator_definitions::{PasswordStorage, ValidatorDefinition};
use account_utils::{
eth2_keystore::Keystore,
@@ -11,6 +10,7 @@ use std::path::{Path, PathBuf};
use types::ChainSpec;
use types::EthSpec;
use validator_dir::{keystore_password_path, Builder as ValidatorDirBuilder};
use validator_store::ValidatorStore;
/// Create some validator EIP-2335 keystores and store them on disk. Then, enroll the validators in
/// this validator client.

View File

@@ -1,8 +1,8 @@
use crate::validator_store::ValidatorStore;
use bls::PublicKey;
use slot_clock::SlotClock;
use std::sync::Arc;
use types::{graffiti::GraffitiString, EthSpec, Graffiti};
use validator_store::ValidatorStore;
pub fn get_graffiti<T: 'static + SlotClock + Clone, E: EthSpec>(
validator_pubkey: PublicKey,

View File

@@ -1,8 +1,4 @@
//! Implementation of the standard keystore management API.
use crate::{
initialized_validators::Error, signing_method::SigningMethod, InitializedValidators,
ValidatorStore,
};
use account_utils::{validator_definitions::PasswordStorage, ZeroizeString};
use eth2::lighthouse_vc::{
std_types::{
@@ -13,6 +9,8 @@ use eth2::lighthouse_vc::{
types::{ExportKeystoresResponse, SingleExportKeystoresResponse},
};
use eth2_keystore::Keystore;
use initialized_validators::{Error, InitializedValidators};
use signing_method::SigningMethod;
use slog::{info, warn, Logger};
use slot_clock::SlotClock;
use std::path::PathBuf;
@@ -21,6 +19,7 @@ use task_executor::TaskExecutor;
use tokio::runtime::Handle;
use types::{EthSpec, PublicKeyBytes};
use validator_dir::{keystore_password_path, Builder as ValidatorDirBuilder};
use validator_store::ValidatorStore;
use warp::Rejection;
use warp_utils::reject::{custom_bad_request, custom_server_error};

View File

@@ -8,16 +8,18 @@ mod tests;
pub mod test_utils;
use crate::beacon_node_fallback::CandidateInfo;
use crate::http_api::graffiti::{delete_graffiti, get_graffiti, set_graffiti};
use graffiti::{delete_graffiti, get_graffiti, set_graffiti};
use create_signed_voluntary_exit::create_signed_voluntary_exit;
use graffiti_file::{determine_graffiti, GraffitiFile};
use validator_store::ValidatorStore;
use crate::http_api::create_signed_voluntary_exit::create_signed_voluntary_exit;
use crate::{determine_graffiti, BlockService, GraffitiFile, ValidatorStore};
use account_utils::{
mnemonic_from_phrase,
validator_definitions::{SigningDefinition, ValidatorDefinition, Web3SignerDefinition},
};
pub use api_secret::ApiSecret;
use beacon_node_fallback::CandidateInfo;
use create_validator::{
create_validators_mnemonic, create_validators_web3signer, get_voting_password_storage,
};
@@ -46,6 +48,7 @@ use task_executor::TaskExecutor;
use tokio_stream::{wrappers::BroadcastStream, StreamExt};
use types::{ChainSpec, ConfigAndPreset, EthSpec};
use validator_dir::Builder as ValidatorDirBuilder;
use validator_services::block_service::BlockService;
use warp::{sse::Event, Filter};
use warp_utils::task::blocking_json_task;

View File

@@ -1,5 +1,4 @@
//! Implementation of the standard remotekey management API.
use crate::{initialized_validators::Error, InitializedValidators, ValidatorStore};
use account_utils::validator_definitions::{
SigningDefinition, ValidatorDefinition, Web3SignerDefinition,
};
@@ -8,6 +7,7 @@ use eth2::lighthouse_vc::std_types::{
ImportRemotekeyStatus, ImportRemotekeysRequest, ImportRemotekeysResponse,
ListRemotekeysResponse, SingleListRemotekeysResponse, Status,
};
use initialized_validators::{Error, InitializedValidators};
use slog::{info, warn, Logger};
use slot_clock::SlotClock;
use std::sync::Arc;
@@ -15,6 +15,7 @@ use task_executor::TaskExecutor;
use tokio::runtime::Handle;
use types::{EthSpec, PublicKeyBytes};
use url::Url;
use validator_store::ValidatorStore;
use warp::Rejection;
use warp_utils::reject::custom_server_error;

View File

@@ -1,21 +1,19 @@
use crate::doppelganger_service::DoppelgangerService;
use crate::key_cache::{KeyCache, CACHE_FILENAME};
use crate::{
http_api::{ApiSecret, Config as HttpConfig, Context},
initialized_validators::{InitializedValidators, OnDecryptFailure},
Config, ValidatorDefinitions, ValidatorStore,
};
use crate::{ApiSecret, Config as HttpConfig, Context};
use account_utils::validator_definitions::ValidatorDefinitions;
use account_utils::{
eth2_wallet::WalletBuilder, mnemonic_from_phrase, random_mnemonic, random_password,
ZeroizeString,
};
use deposit_contract::decode_eth1_tx_data;
use doppelganger_service::DoppelgangerService;
use eth2::{
lighthouse_vc::{http_client::ValidatorClientHttpClient, types::*},
types::ErrorMessage as ApiErrorMessage,
Error as ApiError,
};
use eth2_keystore::KeystoreBuilder;
use initialized_validators::key_cache::{KeyCache, CACHE_FILENAME};
use initialized_validators::{InitializedValidators, OnDecryptFailure};
use logging::test_logger;
use parking_lot::RwLock;
use sensitive_url::SensitiveUrl;
@@ -29,6 +27,7 @@ use std::time::Duration;
use task_executor::test_utils::TestRuntime;
use tempfile::{tempdir, TempDir};
use tokio::sync::oneshot;
use validator_store::{Config as ValidatorStoreConfig, ValidatorStore};
pub const PASSWORD_BYTES: &[u8] = &[42, 50, 37];
pub const TEST_DEFAULT_FEE_RECIPIENT: Address = Address::repeat_byte(42);
@@ -89,16 +88,14 @@ impl ApiTester {
let api_secret = ApiSecret::create_or_open(validator_dir.path()).unwrap();
let api_pubkey = api_secret.api_token();
let config = Config {
validator_dir: validator_dir.path().into(),
secrets_dir: secrets_dir.path().into(),
let config = ValidatorStoreConfig {
fee_recipient: Some(TEST_DEFAULT_FEE_RECIPIENT),
..Default::default()
};
let spec = Arc::new(E::default_spec());
let slashing_db_path = config.validator_dir.join(SLASHING_PROTECTION_FILENAME);
let slashing_db_path = validator_dir.path().join(SLASHING_PROTECTION_FILENAME);
let slashing_protection = SlashingDatabase::open_or_create(&slashing_db_path).unwrap();
let slot_clock =

View File

@@ -3,15 +3,13 @@
mod keystores;
use crate::doppelganger_service::DoppelgangerService;
use crate::{
http_api::{ApiSecret, Config as HttpConfig, Context},
initialized_validators::InitializedValidators,
Config, ValidatorDefinitions, ValidatorStore,
};
use doppelganger_service::DoppelgangerService;
use initialized_validators::{Config as InitializedValidatorsConfig, InitializedValidators};
use crate::{ApiSecret, Config as HttpConfig, Context};
use account_utils::{
eth2_wallet::WalletBuilder, mnemonic_from_phrase, random_mnemonic, random_password,
random_password_string, ZeroizeString,
random_password_string, validator_definitions::ValidatorDefinitions, ZeroizeString,
};
use deposit_contract::decode_eth1_tx_data;
use eth2::{
@@ -34,6 +32,7 @@ use std::time::Duration;
use task_executor::test_utils::TestRuntime;
use tempfile::{tempdir, TempDir};
use types::graffiti::GraffitiString;
use validator_store::{Config as ValidatorStoreConfig, ValidatorStore};
const PASSWORD_BYTES: &[u8] = &[42, 50, 37];
pub const TEST_DEFAULT_FEE_RECIPIENT: Address = Address::repeat_byte(42);
@@ -47,17 +46,18 @@ struct ApiTester {
url: SensitiveUrl,
slot_clock: TestingSlotClock,
_validator_dir: TempDir,
_secrets_dir: TempDir,
_test_runtime: TestRuntime,
}
impl ApiTester {
pub async fn new() -> Self {
let mut config = Config::default();
let mut config = ValidatorStoreConfig::default();
config.fee_recipient = Some(TEST_DEFAULT_FEE_RECIPIENT);
Self::new_with_config(config).await
}
pub async fn new_with_config(mut config: Config) -> Self {
pub async fn new_with_config(config: ValidatorStoreConfig) -> Self {
let log = test_logger();
let validator_dir = tempdir().unwrap();
@@ -68,7 +68,7 @@ impl ApiTester {
let initialized_validators = InitializedValidators::from_definitions(
validator_defs,
validator_dir.path().into(),
Config::default(),
InitializedValidatorsConfig::default(),
log.clone(),
)
.await
@@ -77,12 +77,9 @@ impl ApiTester {
let api_secret = ApiSecret::create_or_open(validator_dir.path()).unwrap();
let api_pubkey = api_secret.api_token();
config.validator_dir = validator_dir.path().into();
config.secrets_dir = secrets_dir.path().into();
let spec = Arc::new(E::default_spec());
let slashing_db_path = config.validator_dir.join(SLASHING_PROTECTION_FILENAME);
let slashing_db_path = validator_dir.path().join(SLASHING_PROTECTION_FILENAME);
let slashing_protection = SlashingDatabase::open_or_create(&slashing_db_path).unwrap();
let genesis_time: u64 = 0;
@@ -157,6 +154,7 @@ impl ApiTester {
url,
slot_clock,
_validator_dir: validator_dir,
_secrets_dir: secrets_dir,
_test_runtime: test_runtime,
}
}
@@ -1147,11 +1145,11 @@ async fn validator_builder_boost_factor() {
/// `prefer_builder_proposals` and `builder_boost_factor` values.
#[tokio::test]
async fn validator_derived_builder_boost_factor_with_process_defaults() {
let config = Config {
let config = ValidatorStoreConfig {
builder_proposals: true,
prefer_builder_proposals: false,
builder_boost_factor: Some(80),
..Config::default()
..ValidatorStoreConfig::default()
};
ApiTester::new_with_config(config)
.await
@@ -1181,11 +1179,11 @@ async fn validator_derived_builder_boost_factor_with_process_defaults() {
#[tokio::test]
async fn validator_builder_boost_factor_global_builder_proposals_true() {
let config = Config {
let config = ValidatorStoreConfig {
builder_proposals: true,
prefer_builder_proposals: false,
builder_boost_factor: None,
..Config::default()
..ValidatorStoreConfig::default()
};
ApiTester::new_with_config(config)
.await
@@ -1194,11 +1192,11 @@ async fn validator_builder_boost_factor_global_builder_proposals_true() {
#[tokio::test]
async fn validator_builder_boost_factor_global_builder_proposals_false() {
let config = Config {
let config = ValidatorStoreConfig {
builder_proposals: false,
prefer_builder_proposals: false,
builder_boost_factor: None,
..Config::default()
..ValidatorStoreConfig::default()
};
ApiTester::new_with_config(config)
.await
@@ -1207,11 +1205,11 @@ async fn validator_builder_boost_factor_global_builder_proposals_false() {
#[tokio::test]
async fn validator_builder_boost_factor_global_prefer_builder_proposals_true() {
let config = Config {
let config = ValidatorStoreConfig {
builder_proposals: true,
prefer_builder_proposals: true,
builder_boost_factor: None,
..Config::default()
..ValidatorStoreConfig::default()
};
ApiTester::new_with_config(config)
.await
@@ -1220,11 +1218,11 @@ async fn validator_builder_boost_factor_global_prefer_builder_proposals_true() {
#[tokio::test]
async fn validator_builder_boost_factor_global_prefer_builder_proposals_true_override() {
let config = Config {
let config = ValidatorStoreConfig {
builder_proposals: false,
prefer_builder_proposals: true,
builder_boost_factor: None,
..Config::default()
..ValidatorStoreConfig::default()
};
ApiTester::new_with_config(config)
.await

View File

@@ -1,4 +1,3 @@
use super::super::super::validator_store::DEFAULT_GAS_LIMIT;
use super::*;
use account_utils::random_password_string;
use bls::PublicKeyBytes;
@@ -14,6 +13,7 @@ use slashing_protection::interchange::{Interchange, InterchangeMetadata};
use std::{collections::HashMap, path::Path};
use tokio::runtime::Handle;
use types::{attestation::AttestationBase, Address};
use validator_store::DEFAULT_GAS_LIMIT;
fn new_keystore(password: ZeroizeString) -> Keystore {
let keypair = Keypair::random();

View File

@@ -0,0 +1,20 @@
[package]
name = "validator_http_metrics"
version = "0.1.0"
edition = { workspace = true }
authors = ["Sigma Prime <contact@sigmaprime.io>"]
[dependencies]
malloc_utils = { workspace = true }
slot_clock = { workspace = true }
metrics = { workspace = true }
parking_lot = { workspace = true }
serde = { workspace = true }
slog = { workspace = true }
warp_utils = { workspace = true }
warp = { workspace = true }
lighthouse_version = { workspace = true }
validator_services = { workspace = true }
validator_store = { workspace = true }
validator_metrics = { workspace = true }
types = { workspace = true }

View File

@@ -1,18 +1,20 @@
//! This crate provides a HTTP server that is solely dedicated to serving the `/metrics` endpoint.
//!
//! For other endpoints, see the `http_api` crate.
pub mod metrics;
use crate::{DutiesService, ValidatorStore};
use lighthouse_version::version_with_platform;
use malloc_utils::scrape_allocator_metrics;
use parking_lot::RwLock;
use serde::{Deserialize, Serialize};
use slog::{crit, info, Logger};
use slot_clock::SystemTimeSlotClock;
use slot_clock::{SlotClock, SystemTimeSlotClock};
use std::future::Future;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use types::EthSpec;
use validator_services::duties_service::DutiesService;
use validator_store::ValidatorStore;
use warp::{http::Response, Filter};
#[derive(Debug)]
@@ -120,7 +122,7 @@ pub fn serve<E: EthSpec>(
.map(move || inner_ctx.clone())
.and_then(|ctx: Arc<Context<E>>| async move {
Ok::<_, warp::Rejection>(
metrics::gather_prometheus_metrics(&ctx)
gather_prometheus_metrics(&ctx)
.map(|body| {
Response::builder()
.status(200)
@@ -156,3 +158,59 @@ pub fn serve<E: EthSpec>(
Ok((listening_socket, server))
}
pub fn gather_prometheus_metrics<E: EthSpec>(
ctx: &Context<E>,
) -> std::result::Result<String, String> {
use validator_metrics::*;
let mut buffer = vec![];
let encoder = TextEncoder::new();
{
let shared = ctx.shared.read();
if let Some(genesis_time) = shared.genesis_time {
if let Ok(now) = SystemTime::now().duration_since(UNIX_EPOCH) {
let distance = now.as_secs() as i64 - genesis_time as i64;
set_gauge(&GENESIS_DISTANCE, distance);
}
}
if let Some(duties_service) = &shared.duties_service {
if let Some(slot) = duties_service.slot_clock.now() {
let current_epoch = slot.epoch(E::slots_per_epoch());
let next_epoch = current_epoch + 1;
set_int_gauge(
&PROPOSER_COUNT,
&[CURRENT_EPOCH],
duties_service.proposer_count(current_epoch) as i64,
);
set_int_gauge(
&ATTESTER_COUNT,
&[CURRENT_EPOCH],
duties_service.attester_count(current_epoch) as i64,
);
set_int_gauge(
&ATTESTER_COUNT,
&[NEXT_EPOCH],
duties_service.attester_count(next_epoch) as i64,
);
}
}
}
// It's important to ensure these metrics are explicitly enabled in the case that users aren't
// using glibc and this function causes panics.
if ctx.config.allocator_metrics_enabled {
scrape_allocator_metrics();
}
warp_utils::metrics::scrape_health_metrics();
encoder
.encode(&metrics::gather(), &mut buffer)
.map_err(|e| format!("{e:?}"))?;
String::from_utf8(buffer).map_err(|e| format!("Failed to encode prometheus info: {:?}", e))
}

View File

@@ -0,0 +1,26 @@
[package]
name = "initialized_validators"
version = "0.1.0"
edition = { workspace = true }
authors = ["Sigma Prime <contact@sigmaprime.io>"]
[dependencies]
signing_method = { workspace = true }
account_utils = { workspace = true }
eth2_keystore = { workspace = true }
metrics = { workspace = true }
lockfile = { workspace = true }
parking_lot = { workspace = true }
reqwest = { workspace = true }
slog = { workspace = true }
types = { workspace = true }
url = { workspace = true }
validator_dir = { workspace = true }
rand = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
bls = { workspace = true }
tokio = { workspace = true }
bincode = { workspace = true }
filesystem = { workspace = true }
validator_metrics = { workspace = true }

View File

@@ -6,7 +6,8 @@
//! The `InitializedValidators` struct in this file serves as the source-of-truth of which
//! validators are managed by this validator client.
use crate::signing_method::SigningMethod;
pub mod key_cache;
use account_utils::{
read_password, read_password_from_user, read_password_string,
validator_definitions::{
@@ -20,6 +21,8 @@ use lockfile::{Lockfile, LockfileError};
use metrics::set_gauge;
use parking_lot::{MappedMutexGuard, Mutex, MutexGuard};
use reqwest::{Certificate, Client, Error as ReqwestError, Identity};
use serde::{Deserialize, Serialize};
use signing_method::SigningMethod;
use slog::{debug, error, info, warn, Logger};
use std::collections::{HashMap, HashSet};
use std::fs::{self, File};
@@ -32,9 +35,7 @@ use types::{Address, Graffiti, Keypair, PublicKey, PublicKeyBytes};
use url::{ParseError, Url};
use validator_dir::Builder as ValidatorDirBuilder;
use crate::key_cache;
use crate::key_cache::KeyCache;
use crate::Config;
use key_cache::KeyCache;
/// Default timeout for a request to a remote signer for a signature.
///
@@ -45,6 +46,24 @@ const DEFAULT_REMOTE_SIGNER_REQUEST_TIMEOUT: Duration = Duration::from_secs(12);
// Use TTY instead of stdin to capture passwords from users.
const USE_STDIN: bool = false;
pub const DEFAULT_WEB3SIGNER_KEEP_ALIVE: Option<Duration> = Some(Duration::from_secs(20));
// The configuration for initialised validators.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Config {
pub web3_signer_keep_alive_timeout: Option<Duration>,
pub web3_signer_max_idle_connections: Option<usize>,
}
impl Default for Config {
fn default() -> Self {
Config {
web3_signer_keep_alive_timeout: DEFAULT_WEB3SIGNER_KEEP_ALIVE,
web3_signer_max_idle_connections: None,
}
}
}
pub enum OnDecryptFailure {
/// If the key cache fails to decrypt, create a new cache.
CreateNew,
@@ -1194,7 +1213,7 @@ impl InitializedValidators {
/// A validator is considered "already known" and skipped if the public key is already known.
/// I.e., if there are two different definitions with the same public key then the second will
/// be ignored.
pub(crate) async fn update_validators(&mut self) -> Result<(), Error> {
pub async fn update_validators(&mut self) -> Result<(), Error> {
//use key cache if available
let mut key_stores = HashMap::new();
@@ -1380,11 +1399,11 @@ impl InitializedValidators {
// Update the enabled and total validator counts
set_gauge(
&crate::http_metrics::metrics::ENABLED_VALIDATORS_COUNT,
&validator_metrics::ENABLED_VALIDATORS_COUNT,
self.num_enabled() as i64,
);
set_gauge(
&crate::http_metrics::metrics::TOTAL_VALIDATORS_COUNT,
&validator_metrics::TOTAL_VALIDATORS_COUNT,
self.num_total() as i64,
);
Ok(())

View File

@@ -0,0 +1,17 @@
[package]
name = "signing_method"
version = "0.1.0"
edition = { workspace = true }
authors = ["Sigma Prime <contact@sigmaprime.io>"]
[dependencies]
eth2_keystore = { workspace = true }
lockfile = { workspace = true }
parking_lot = { workspace = true }
reqwest = { workspace = true }
task_executor = { workspace = true }
types = { workspace = true }
url = { workspace = true }
validator_metrics = { workspace = true }
serde = { workspace = true }
ethereum_serde_utils = { workspace = true }

View File

@@ -3,7 +3,6 @@
//! - Via a local `Keypair`.
//! - Via a remote signer (Web3Signer)
use crate::http_metrics::metrics;
use eth2_keystore::Keystore;
use lockfile::Lockfile;
use parking_lot::Mutex;
@@ -166,8 +165,10 @@ impl SigningMethod {
) -> Result<Signature, Error> {
match self {
SigningMethod::LocalKeystore { voting_keypair, .. } => {
let _timer =
metrics::start_timer_vec(&metrics::SIGNING_TIMES, &[metrics::LOCAL_KEYSTORE]);
let _timer = validator_metrics::start_timer_vec(
&validator_metrics::SIGNING_TIMES,
&[validator_metrics::LOCAL_KEYSTORE],
);
let voting_keypair = voting_keypair.clone();
// Spawn a blocking task to produce the signature. This avoids blocking the core
@@ -187,8 +188,10 @@ impl SigningMethod {
http_client,
..
} => {
let _timer =
metrics::start_timer_vec(&metrics::SIGNING_TIMES, &[metrics::WEB3SIGNER]);
let _timer = validator_metrics::start_timer_vec(
&validator_metrics::SIGNING_TIMES,
&[validator_metrics::WEB3SIGNER],
);
// Map the message into a Web3Signer type.
let object = match signable_message {

View File

@@ -1,27 +0,0 @@
use crate::beacon_node_fallback::CandidateError;
use eth2::{types::Slot, BeaconNodeHttpClient};
use slog::{warn, Logger};
pub async fn check_node_health(
beacon_node: &BeaconNodeHttpClient,
log: &Logger,
) -> Result<(Slot, bool, bool), CandidateError> {
let resp = match beacon_node.get_node_syncing().await {
Ok(resp) => resp,
Err(e) => {
warn!(
log,
"Unable connect to beacon node";
"error" => %e
);
return Err(CandidateError::Offline);
}
};
Ok((
resp.data.head_slot,
resp.data.is_optimistic,
resp.data.el_offline,
))
}

View File

@@ -1,8 +1,4 @@
use crate::beacon_node_fallback::ApiTopic;
use crate::graffiti_file::GraffitiFile;
use crate::{
beacon_node_fallback, beacon_node_health::BeaconNodeSyncDistanceTiers, http_api, http_metrics,
};
use beacon_node_fallback::{beacon_node_health::BeaconNodeSyncDistanceTiers, ApiTopic};
use clap::ArgMatches;
use clap_utils::{flags::DISABLE_MALLOC_TUNING_FLAG, parse_optional, parse_required};
use directory::{
@@ -10,6 +6,8 @@ use directory::{
DEFAULT_VALIDATOR_DIR,
};
use eth2::types::Graffiti;
use graffiti_file::GraffitiFile;
use initialized_validators::Config as InitializedValidatorsConfig;
use sensitive_url::SensitiveUrl;
use serde::{Deserialize, Serialize};
use slog::{info, warn, Logger};
@@ -19,13 +17,18 @@ use std::path::PathBuf;
use std::str::FromStr;
use std::time::Duration;
use types::{Address, GRAFFITI_BYTES_LEN};
use validator_http_api;
use validator_http_metrics;
use validator_store::Config as ValidatorStoreConfig;
pub const DEFAULT_BEACON_NODE: &str = "http://localhost:5052/";
pub const DEFAULT_WEB3SIGNER_KEEP_ALIVE: Option<Duration> = Some(Duration::from_secs(20));
/// Stores the core configuration for this validator instance.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Config {
/// Configuration parameters for the validator store.
#[serde(flatten)]
pub validator_store: ValidatorStoreConfig,
/// The data directory, which stores all validator databases
pub validator_dir: PathBuf,
/// The directory containing the passwords to unlock validator keystores.
@@ -49,12 +52,10 @@ pub struct Config {
pub graffiti: Option<Graffiti>,
/// Graffiti file to load per validator graffitis.
pub graffiti_file: Option<GraffitiFile>,
/// Fallback fallback address.
pub fee_recipient: Option<Address>,
/// Configuration for the HTTP REST API.
pub http_api: http_api::Config,
pub http_api: validator_http_api::Config,
/// Configuration for the HTTP REST API.
pub http_metrics: http_metrics::Config,
pub http_metrics: validator_http_metrics::Config,
/// Configuration for the Beacon Node fallback.
pub beacon_node_fallback: beacon_node_fallback::Config,
/// Configuration for sending metrics to a remote explorer endpoint.
@@ -68,11 +69,7 @@ pub struct Config {
/// (<= 64 validators)
pub enable_high_validator_count_metrics: bool,
/// Enable use of the blinded block endpoints during proposals.
pub builder_proposals: bool,
/// Overrides the timestamp field in builder api ValidatorRegistrationV1
pub builder_registration_timestamp_override: Option<u64>,
/// Fallback gas limit.
pub gas_limit: Option<u64>,
/// A list of custom certificates that the validator client will additionally use when
/// connecting to a beacon node over SSL/TLS.
pub beacon_nodes_tls_certs: Option<Vec<PathBuf>>,
@@ -82,16 +79,11 @@ pub struct Config {
pub enable_latency_measurement_service: bool,
/// Defines the number of validators per `validator/register_validator` request sent to the BN.
pub validator_registration_batch_size: usize,
/// Enable slashing protection even while using web3signer keys.
pub enable_web3signer_slashing_protection: bool,
/// Specifies the boost factor, a percentage multiplier to apply to the builder's payload value.
pub builder_boost_factor: Option<u64>,
/// If true, Lighthouse will prefer builder proposals, if available.
pub prefer_builder_proposals: bool,
/// Whether we are running with distributed network support.
pub distributed: bool,
pub web3_signer_keep_alive_timeout: Option<Duration>,
pub web3_signer_max_idle_connections: Option<usize>,
/// Configuration for the initialized validators
#[serde(flatten)]
pub initialized_validators: InitializedValidatorsConfig,
}
impl Default for Config {
@@ -109,6 +101,7 @@ impl Default for Config {
let beacon_nodes = vec![SensitiveUrl::parse(DEFAULT_BEACON_NODE)
.expect("beacon_nodes must always be a valid url.")];
Self {
validator_store: ValidatorStoreConfig::default(),
validator_dir,
secrets_dir,
beacon_nodes,
@@ -119,7 +112,6 @@ impl Default for Config {
use_long_timeouts: false,
graffiti: None,
graffiti_file: None,
fee_recipient: None,
http_api: <_>::default(),
http_metrics: <_>::default(),
beacon_node_fallback: <_>::default(),
@@ -127,18 +119,12 @@ impl Default for Config {
enable_doppelganger_protection: false,
enable_high_validator_count_metrics: false,
beacon_nodes_tls_certs: None,
builder_proposals: false,
builder_registration_timestamp_override: None,
gas_limit: None,
broadcast_topics: vec![ApiTopic::Subscriptions],
enable_latency_measurement_service: true,
validator_registration_batch_size: 500,
enable_web3signer_slashing_protection: true,
builder_boost_factor: None,
prefer_builder_proposals: false,
distributed: false,
web3_signer_keep_alive_timeout: DEFAULT_WEB3SIGNER_KEEP_ALIVE,
web3_signer_max_idle_connections: None,
initialized_validators: <_>::default(),
}
}
}
@@ -233,7 +219,7 @@ impl Config {
if let Some(input_fee_recipient) =
parse_optional::<Address>(cli_args, "suggested-fee-recipient")?
{
config.fee_recipient = Some(input_fee_recipient);
config.validator_store.fee_recipient = Some(input_fee_recipient);
}
if let Some(tls_certs) = parse_optional::<String>(cli_args, "beacon-nodes-tls-certs")? {
@@ -270,7 +256,7 @@ impl Config {
* Web3 signer
*/
if let Some(s) = parse_optional::<String>(cli_args, "web3-signer-keep-alive-timeout")? {
config.web3_signer_keep_alive_timeout = if s == "null" {
config.initialized_validators.web3_signer_keep_alive_timeout = if s == "null" {
None
} else {
Some(Duration::from_millis(
@@ -279,7 +265,9 @@ impl Config {
}
}
if let Some(n) = parse_optional::<usize>(cli_args, "web3-signer-max-idle-connections")? {
config.web3_signer_max_idle_connections = Some(n);
config
.initialized_validators
.web3_signer_max_idle_connections = Some(n);
}
/*
@@ -382,14 +370,14 @@ impl Config {
}
if cli_args.get_flag("builder-proposals") {
config.builder_proposals = true;
config.validator_store.builder_proposals = true;
}
if cli_args.get_flag("prefer-builder-proposals") {
config.prefer_builder_proposals = true;
config.validator_store.prefer_builder_proposals = true;
}
config.gas_limit = cli_args
config.validator_store.gas_limit = cli_args
.get_one::<String>("gas-limit")
.map(|gas_limit| {
gas_limit
@@ -408,7 +396,8 @@ impl Config {
);
}
config.builder_boost_factor = parse_optional(cli_args, "builder-boost-factor")?;
config.validator_store.builder_boost_factor =
parse_optional(cli_args, "builder-boost-factor")?;
config.enable_latency_measurement_service =
!cli_args.get_flag("disable-latency-measurement-service");
@@ -419,7 +408,7 @@ impl Config {
return Err("validator-registration-batch-size cannot be 0".to_string());
}
config.enable_web3signer_slashing_protection =
config.validator_store.enable_web3signer_slashing_protection =
if cli_args.get_flag("disable-slashing-protection-web3signer") {
warn!(
log,

View File

@@ -1,4 +1,4 @@
use crate::{http_metrics::metrics, BeaconNodeFallback};
use beacon_node_fallback::BeaconNodeFallback;
use environment::RuntimeContext;
use slog::debug;
use slot_clock::SlotClock;
@@ -44,14 +44,14 @@ pub fn start_latency_service<T: SlotClock + 'static, E: EthSpec>(
"node" => &measurement.beacon_node_id,
"latency" => latency.as_millis(),
);
metrics::observe_timer_vec(
&metrics::VC_BEACON_NODE_LATENCY,
validator_metrics::observe_timer_vec(
&validator_metrics::VC_BEACON_NODE_LATENCY,
&[&measurement.beacon_node_id],
latency,
);
if i == 0 {
metrics::observe_duration(
&metrics::VC_BEACON_NODE_LATENCY_PRIMARY_ENDPOINT,
validator_metrics::observe_duration(
&validator_metrics::VC_BEACON_NODE_LATENCY_PRIMARY_ENDPOINT,
latency,
);
}

View File

@@ -1,52 +1,28 @@
mod attestation_service;
mod beacon_node_fallback;
mod beacon_node_health;
mod block_service;
mod check_synced;
mod cli;
mod duties_service;
mod graffiti_file;
mod http_metrics;
mod key_cache;
pub mod config;
mod latency;
mod notifier;
mod preparation_service;
mod signing_method;
mod sync_committee_service;
pub mod config;
mod doppelganger_service;
pub mod http_api;
pub mod initialized_validators;
pub mod validator_store;
pub use beacon_node_fallback::ApiTopic;
pub use beacon_node_health::BeaconNodeSyncDistanceTiers;
pub use cli::cli_app;
pub use config::Config;
use initialized_validators::InitializedValidators;
use metrics::set_gauge;
use monitoring_api::{MonitoringHttpClient, ProcessType};
use sensitive_url::SensitiveUrl;
pub use slashing_protection::{SlashingDatabase, SLASHING_PROTECTION_FILENAME};
use slashing_protection::{SlashingDatabase, SLASHING_PROTECTION_FILENAME};
use crate::beacon_node_fallback::{
use beacon_node_fallback::{
start_fallback_updater_service, BeaconNodeFallback, CandidateBeaconNode,
};
use crate::doppelganger_service::DoppelgangerService;
use crate::graffiti_file::GraffitiFile;
use crate::initialized_validators::Error::UnableToOpenVotingKeystore;
use account_utils::validator_definitions::ValidatorDefinitions;
use attestation_service::{AttestationService, AttestationServiceBuilder};
use block_service::{BlockService, BlockServiceBuilder};
use clap::ArgMatches;
use duties_service::{sync::SyncDutiesMap, DutiesService};
use doppelganger_service::DoppelgangerService;
use environment::RuntimeContext;
use eth2::{reqwest::ClientBuilder, types::Graffiti, BeaconNodeHttpClient, StatusCode, Timeouts};
use http_api::ApiSecret;
use eth2::{reqwest::ClientBuilder, BeaconNodeHttpClient, StatusCode, Timeouts};
use initialized_validators::Error::UnableToOpenVotingKeystore;
use notifier::spawn_notifier;
use parking_lot::RwLock;
use preparation_service::{PreparationService, PreparationServiceBuilder};
use reqwest::Certificate;
use slog::{debug, error, info, warn, Logger};
use slot_clock::SlotClock;
@@ -58,12 +34,20 @@ use std::net::SocketAddr;
use std::path::Path;
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use sync_committee_service::SyncCommitteeService;
use tokio::{
sync::mpsc,
time::{sleep, Duration},
};
use types::{EthSpec, Hash256, PublicKeyBytes};
use types::{EthSpec, Hash256};
use validator_http_api::ApiSecret;
use validator_services::{
attestation_service::{AttestationService, AttestationServiceBuilder},
block_service::{BlockService, BlockServiceBuilder},
duties_service::{self, DutiesService},
preparation_service::{PreparationService, PreparationServiceBuilder},
sync::SyncDutiesMap,
sync_committee_service::SyncCommitteeService,
};
use validator_store::ValidatorStore;
/// The interval between attempts to contact the beacon node during startup.
@@ -152,22 +136,23 @@ impl<E: EthSpec> ProductionValidatorClient<E> {
);
// Optionally start the metrics server.
let http_metrics_ctx = if config.http_metrics.enabled {
let shared = http_metrics::Shared {
let validator_metrics_ctx = if config.http_metrics.enabled {
let shared = validator_http_metrics::Shared {
validator_store: None,
genesis_time: None,
duties_service: None,
};
let ctx: Arc<http_metrics::Context<E>> = Arc::new(http_metrics::Context {
config: config.http_metrics.clone(),
shared: RwLock::new(shared),
log: log.clone(),
});
let ctx: Arc<validator_http_metrics::Context<E>> =
Arc::new(validator_http_metrics::Context {
config: config.http_metrics.clone(),
shared: RwLock::new(shared),
log: log.clone(),
});
let exit = context.executor.exit();
let (_listen_addr, server) = http_metrics::serve(ctx.clone(), exit)
let (_listen_addr, server) = validator_http_metrics::serve(ctx.clone(), exit)
.map_err(|e| format!("Unable to start metrics API server: {:?}", e))?;
context
@@ -215,7 +200,7 @@ impl<E: EthSpec> ProductionValidatorClient<E> {
let validators = InitializedValidators::from_definitions(
validator_defs,
config.validator_dir.clone(),
config.clone(),
config.initialized_validators.clone(),
log.clone(),
)
.await
@@ -384,20 +369,20 @@ impl<E: EthSpec> ProductionValidatorClient<E> {
// Set the count for beacon node fallbacks excluding the primary beacon node.
set_gauge(
&http_metrics::metrics::ETH2_FALLBACK_CONFIGURED,
&validator_metrics::ETH2_FALLBACK_CONFIGURED,
num_nodes.saturating_sub(1) as i64,
);
// Set the total beacon node count.
set_gauge(
&http_metrics::metrics::TOTAL_BEACON_NODES_COUNT,
&validator_metrics::TOTAL_BEACON_NODES_COUNT,
num_nodes as i64,
);
// Initialize the number of connected, synced beacon nodes to 0.
set_gauge(&http_metrics::metrics::ETH2_FALLBACK_CONNECTED, 0);
set_gauge(&http_metrics::metrics::SYNCED_BEACON_NODES_COUNT, 0);
set_gauge(&validator_metrics::ETH2_FALLBACK_CONNECTED, 0);
set_gauge(&validator_metrics::SYNCED_BEACON_NODES_COUNT, 0);
// Initialize the number of connected, avaliable beacon nodes to 0.
set_gauge(&http_metrics::metrics::AVAILABLE_BEACON_NODES_COUNT, 0);
set_gauge(&validator_metrics::AVAILABLE_BEACON_NODES_COUNT, 0);
let mut beacon_nodes: BeaconNodeFallback<_, E> = BeaconNodeFallback::new(
candidates,
@@ -422,7 +407,7 @@ impl<E: EthSpec> ProductionValidatorClient<E> {
};
// Update the metrics server.
if let Some(ctx) = &http_metrics_ctx {
if let Some(ctx) = &validator_metrics_ctx {
ctx.shared.write().genesis_time = Some(genesis_time);
}
@@ -459,7 +444,7 @@ impl<E: EthSpec> ProductionValidatorClient<E> {
context.eth2_config.spec.clone(),
doppelganger_service.clone(),
slot_clock.clone(),
&config,
&config.validator_store,
context.executor.clone(),
log.clone(),
));
@@ -496,7 +481,7 @@ impl<E: EthSpec> ProductionValidatorClient<E> {
});
// Update the metrics server.
if let Some(ctx) = &http_metrics_ctx {
if let Some(ctx) = &validator_metrics_ctx {
ctx.shared.write().validator_store = Some(validator_store.clone());
ctx.shared.write().duties_service = Some(duties_service.clone());
}
@@ -569,7 +554,7 @@ impl<E: EthSpec> ProductionValidatorClient<E> {
let api_secret = ApiSecret::create_or_open(&self.config.validator_dir)?;
self.http_api_listen_addr = if self.config.http_api.enabled {
let ctx = Arc::new(http_api::Context {
let ctx = Arc::new(validator_http_api::Context {
task_executor: self.context.executor.clone(),
api_secret,
block_service: Some(self.block_service.clone()),
@@ -588,7 +573,7 @@ impl<E: EthSpec> ProductionValidatorClient<E> {
let exit = self.context.executor.exit();
let (listen_addr, server) = http_api::serve(ctx, exit)
let (listen_addr, server) = validator_http_api::serve(ctx, exit)
.map_err(|e| format!("Unable to start HTTP API server: {:?}", e))?;
self.context
@@ -850,24 +835,3 @@ pub fn load_pem_certificate<P: AsRef<Path>>(pem_path: P) -> Result<Certificate,
.map_err(|e| format!("Unable to read certificate file: {}", e))?;
Certificate::from_pem(&buf).map_err(|e| format!("Unable to parse certificate: {}", e))
}
// Given the various graffiti control methods, determine the graffiti that will be used for
// the next block produced by the validator with the given public key.
pub fn determine_graffiti(
validator_pubkey: &PublicKeyBytes,
log: &Logger,
graffiti_file: Option<GraffitiFile>,
validator_definition_graffiti: Option<Graffiti>,
graffiti_flag: Option<Graffiti>,
) -> Option<Graffiti> {
graffiti_file
.and_then(|mut g| match g.load_graffiti(validator_pubkey) {
Ok(g) => g,
Err(e) => {
warn!(log, "Failed to read graffiti file"; "error" => ?e);
None
}
})
.or(validator_definition_graffiti)
.or(graffiti_flag)
}

View File

@@ -1,4 +1,3 @@
use crate::http_metrics;
use crate::{DutiesService, ProductionValidatorClient};
use metrics::set_gauge;
use slog::{debug, error, info, Logger};
@@ -45,15 +44,15 @@ async fn notify<T: SlotClock + 'static, E: EthSpec>(
let num_synced_fallback = num_synced.saturating_sub(1);
set_gauge(
&http_metrics::metrics::AVAILABLE_BEACON_NODES_COUNT,
&validator_metrics::AVAILABLE_BEACON_NODES_COUNT,
num_available as i64,
);
set_gauge(
&http_metrics::metrics::SYNCED_BEACON_NODES_COUNT,
&validator_metrics::SYNCED_BEACON_NODES_COUNT,
num_synced as i64,
);
set_gauge(
&http_metrics::metrics::TOTAL_BEACON_NODES_COUNT,
&validator_metrics::TOTAL_BEACON_NODES_COUNT,
num_total as i64,
);
if num_synced > 0 {
@@ -79,9 +78,9 @@ async fn notify<T: SlotClock + 'static, E: EthSpec>(
)
}
if num_synced_fallback > 0 {
set_gauge(&http_metrics::metrics::ETH2_FALLBACK_CONNECTED, 1);
set_gauge(&validator_metrics::ETH2_FALLBACK_CONNECTED, 1);
} else {
set_gauge(&http_metrics::metrics::ETH2_FALLBACK_CONNECTED, 0);
set_gauge(&validator_metrics::ETH2_FALLBACK_CONNECTED, 0);
}
for info in candidate_info {

View File

@@ -0,0 +1,12 @@
[package]
name = "validator_metrics"
version = "0.1.0"
edition = { workspace = true }
authors = ["Sigma Prime <contact@sigmaprime.io>"]
[lib]
name = "validator_metrics"
path = "src/lib.rs"
[dependencies]
metrics = { workspace = true }

View File

@@ -1,9 +1,4 @@
use super::Context;
use malloc_utils::scrape_allocator_metrics;
use slot_clock::SlotClock;
use std::sync::LazyLock;
use std::time::{SystemTime, UNIX_EPOCH};
use types::EthSpec;
pub const SUCCESS: &str = "success";
pub const SLASHABLE: &str = "slashable";
@@ -267,56 +262,3 @@ pub static VC_BEACON_NODE_LATENCY_PRIMARY_ENDPOINT: LazyLock<Result<Histogram>>
"Round-trip latency for the primary BN endpoint",
)
});
pub fn gather_prometheus_metrics<E: EthSpec>(
ctx: &Context<E>,
) -> std::result::Result<String, String> {
let mut buffer = vec![];
let encoder = TextEncoder::new();
{
let shared = ctx.shared.read();
if let Some(genesis_time) = shared.genesis_time {
if let Ok(now) = SystemTime::now().duration_since(UNIX_EPOCH) {
let distance = now.as_secs() as i64 - genesis_time as i64;
set_gauge(&GENESIS_DISTANCE, distance);
}
}
if let Some(duties_service) = &shared.duties_service {
if let Some(slot) = duties_service.slot_clock.now() {
let current_epoch = slot.epoch(E::slots_per_epoch());
let next_epoch = current_epoch + 1;
set_int_gauge(
&PROPOSER_COUNT,
&[CURRENT_EPOCH],
duties_service.proposer_count(current_epoch) as i64,
);
set_int_gauge(
&ATTESTER_COUNT,
&[CURRENT_EPOCH],
duties_service.attester_count(current_epoch) as i64,
);
set_int_gauge(
&ATTESTER_COUNT,
&[NEXT_EPOCH],
duties_service.attester_count(next_epoch) as i64,
);
}
}
}
// It's important to ensure these metrics are explicitly enabled in the case that users aren't
// using glibc and this function causes panics.
if ctx.config.allocator_metrics_enabled {
scrape_allocator_metrics();
}
warp_utils::metrics::scrape_health_metrics();
encoder.encode(&metrics::gather(), &mut buffer).unwrap();
String::from_utf8(buffer).map_err(|e| format!("Failed to encode prometheus info: {:?}", e))
}

View File

@@ -0,0 +1,23 @@
[package]
name = "validator_services"
version = "0.1.0"
edition = { workspace = true }
authors = ["Sigma Prime <contact@sigmaprime.io>"]
[dependencies]
beacon_node_fallback = { workspace = true }
validator_metrics = { workspace = true }
validator_store = { workspace = true }
graffiti_file = { workspace = true }
doppelganger_service = { workspace = true }
environment = { workspace = true }
eth2 = { workspace = true }
futures = { workspace = true }
parking_lot = { workspace = true }
safe_arith = { workspace = true }
slog = { workspace = true }
slot_clock = { workspace = true }
tokio = { workspace = true }
types = { workspace = true }
tree_hash = { workspace = true }
bls = { workspace = true }

View File

@@ -1,9 +1,5 @@
use crate::beacon_node_fallback::{ApiTopic, BeaconNodeFallback};
use crate::{
duties_service::{DutiesService, DutyAndProof},
http_metrics::metrics,
validator_store::{Error as ValidatorStoreError, ValidatorStore},
};
use crate::duties_service::{DutiesService, DutyAndProof};
use beacon_node_fallback::{ApiTopic, BeaconNodeFallback};
use environment::RuntimeContext;
use futures::future::join_all;
use slog::{crit, debug, error, info, trace, warn};
@@ -14,8 +10,10 @@ use std::sync::Arc;
use tokio::time::{sleep, sleep_until, Duration, Instant};
use tree_hash::TreeHash;
use types::{Attestation, AttestationData, ChainSpec, CommitteeIndex, EthSpec, Slot};
use validator_store::{Error as ValidatorStoreError, ValidatorStore};
/// Builds an `AttestationService`.
#[derive(Default)]
pub struct AttestationServiceBuilder<T: SlotClock + 'static, E: EthSpec> {
duties_service: Option<Arc<DutiesService<T, E>>>,
validator_store: Option<Arc<ValidatorStore<T, E>>>,
@@ -238,9 +236,9 @@ impl<T: SlotClock + 'static, E: EthSpec> AttestationService<T, E> {
aggregate_production_instant: Instant,
) -> Result<(), ()> {
let log = self.context.log();
let attestations_timer = metrics::start_timer_vec(
&metrics::ATTESTATION_SERVICE_TIMES,
&[metrics::ATTESTATIONS],
let attestations_timer = validator_metrics::start_timer_vec(
&validator_metrics::ATTESTATION_SERVICE_TIMES,
&[validator_metrics::ATTESTATIONS],
);
// There's not need to produce `Attestation` or `SignedAggregateAndProof` if we do not have
@@ -278,9 +276,9 @@ impl<T: SlotClock + 'static, E: EthSpec> AttestationService<T, E> {
sleep_until(aggregate_production_instant).await;
// Start the metrics timer *after* we've done the delay.
let _aggregates_timer = metrics::start_timer_vec(
&metrics::ATTESTATION_SERVICE_TIMES,
&[metrics::AGGREGATES],
let _aggregates_timer = validator_metrics::start_timer_vec(
&validator_metrics::ATTESTATION_SERVICE_TIMES,
&[validator_metrics::AGGREGATES],
);
// Then download, sign and publish a `SignedAggregateAndProof` for each
@@ -339,9 +337,9 @@ impl<T: SlotClock + 'static, E: EthSpec> AttestationService<T, E> {
let attestation_data = self
.beacon_nodes
.first_success(|beacon_node| async move {
let _timer = metrics::start_timer_vec(
&metrics::ATTESTATION_SERVICE_TIMES,
&[metrics::ATTESTATIONS_HTTP_GET],
let _timer = validator_metrics::start_timer_vec(
&validator_metrics::ATTESTATION_SERVICE_TIMES,
&[validator_metrics::ATTESTATIONS_HTTP_GET],
);
beacon_node
.get_validator_attestation_data(slot, committee_index)
@@ -454,9 +452,9 @@ impl<T: SlotClock + 'static, E: EthSpec> AttestationService<T, E> {
match self
.beacon_nodes
.request(ApiTopic::Attestations, |beacon_node| async move {
let _timer = metrics::start_timer_vec(
&metrics::ATTESTATION_SERVICE_TIMES,
&[metrics::ATTESTATIONS_HTTP_POST],
let _timer = validator_metrics::start_timer_vec(
&validator_metrics::ATTESTATION_SERVICE_TIMES,
&[validator_metrics::ATTESTATIONS_HTTP_POST],
);
if fork_name.electra_enabled() {
beacon_node
@@ -531,9 +529,9 @@ impl<T: SlotClock + 'static, E: EthSpec> AttestationService<T, E> {
let aggregated_attestation = &self
.beacon_nodes
.first_success(|beacon_node| async move {
let _timer = metrics::start_timer_vec(
&metrics::ATTESTATION_SERVICE_TIMES,
&[metrics::AGGREGATES_HTTP_GET],
let _timer = validator_metrics::start_timer_vec(
&validator_metrics::ATTESTATION_SERVICE_TIMES,
&[validator_metrics::AGGREGATES_HTTP_GET],
);
if fork_name.electra_enabled() {
beacon_node
@@ -620,9 +618,9 @@ impl<T: SlotClock + 'static, E: EthSpec> AttestationService<T, E> {
match self
.beacon_nodes
.first_success(|beacon_node| async move {
let _timer = metrics::start_timer_vec(
&metrics::ATTESTATION_SERVICE_TIMES,
&[metrics::AGGREGATES_HTTP_POST],
let _timer = validator_metrics::start_timer_vec(
&validator_metrics::ATTESTATION_SERVICE_TIMES,
&[validator_metrics::AGGREGATES_HTTP_POST],
);
if fork_name.electra_enabled() {
beacon_node

View File

@@ -1,17 +1,9 @@
use crate::beacon_node_fallback::{Error as FallbackError, Errors};
use crate::{
beacon_node_fallback::{ApiTopic, BeaconNodeFallback},
determine_graffiti,
graffiti_file::GraffitiFile,
};
use crate::{
http_metrics::metrics,
validator_store::{Error as ValidatorStoreError, ValidatorStore},
};
use beacon_node_fallback::{ApiTopic, BeaconNodeFallback, Error as FallbackError, Errors};
use bls::SignatureBytes;
use environment::RuntimeContext;
use eth2::types::{FullBlockContents, PublishBlockRequest};
use eth2::{BeaconNodeHttpClient, StatusCode};
use graffiti_file::{determine_graffiti, GraffitiFile};
use slog::{crit, debug, error, info, trace, warn, Logger};
use slot_clock::SlotClock;
use std::fmt::Debug;
@@ -24,6 +16,7 @@ use types::{
BlindedBeaconBlock, BlockType, EthSpec, Graffiti, PublicKeyBytes, SignedBlindedBeaconBlock,
Slot,
};
use validator_store::{Error as ValidatorStoreError, ValidatorStore};
#[derive(Debug)]
pub enum BlockError {
@@ -50,6 +43,7 @@ impl From<Errors<BlockError>> for BlockError {
}
/// Builds a `BlockService`.
#[derive(Default)]
pub struct BlockServiceBuilder<T, E: EthSpec> {
validator_store: Option<Arc<ValidatorStore<T, E>>>,
slot_clock: Option<Arc<T>>,
@@ -186,8 +180,8 @@ impl<T: SlotClock, E: EthSpec> ProposerFallback<T, E> {
pub struct Inner<T, E: EthSpec> {
validator_store: Arc<ValidatorStore<T, E>>,
slot_clock: Arc<T>,
pub(crate) beacon_nodes: Arc<BeaconNodeFallback<T, E>>,
pub(crate) proposer_nodes: Option<Arc<BeaconNodeFallback<T, E>>>,
pub beacon_nodes: Arc<BeaconNodeFallback<T, E>>,
pub proposer_nodes: Option<Arc<BeaconNodeFallback<T, E>>>,
context: RuntimeContext<E>,
graffiti: Option<Graffiti>,
graffiti_file: Option<GraffitiFile>,
@@ -247,8 +241,10 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
/// Attempt to produce a block for any block producers in the `ValidatorStore`.
async fn do_update(&self, notification: BlockServiceNotification) -> Result<(), ()> {
let log = self.context.log();
let _timer =
metrics::start_timer_vec(&metrics::BLOCK_SERVICE_TIMES, &[metrics::FULL_UPDATE]);
let _timer = validator_metrics::start_timer_vec(
&validator_metrics::BLOCK_SERVICE_TIMES,
&[validator_metrics::FULL_UPDATE],
);
let slot = self.slot_clock.now().ok_or_else(move || {
crit!(log, "Duties manager failed to read slot clock");
@@ -337,7 +333,7 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
unsigned_block: UnsignedBlock<E>,
) -> Result<(), BlockError> {
let log = self.context.log();
let signing_timer = metrics::start_timer(&metrics::BLOCK_SIGNING_TIMES);
let signing_timer = validator_metrics::start_timer(&validator_metrics::BLOCK_SIGNING_TIMES);
let res = match unsigned_block {
UnsignedBlock::Full(block_contents) => {
@@ -418,8 +414,10 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
builder_boost_factor: Option<u64>,
) -> Result<(), BlockError> {
let log = self.context.log();
let _timer =
metrics::start_timer_vec(&metrics::BLOCK_SERVICE_TIMES, &[metrics::BEACON_BLOCK]);
let _timer = validator_metrics::start_timer_vec(
&validator_metrics::BLOCK_SERVICE_TIMES,
&[validator_metrics::BEACON_BLOCK],
);
let randao_reveal = match self
.validator_store
@@ -475,9 +473,9 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
// great view of attestations on the network.
let unsigned_block = proposer_fallback
.request_proposers_last(|beacon_node| async move {
let _get_timer = metrics::start_timer_vec(
&metrics::BLOCK_SERVICE_TIMES,
&[metrics::BEACON_BLOCK_HTTP_GET],
let _get_timer = validator_metrics::start_timer_vec(
&validator_metrics::BLOCK_SERVICE_TIMES,
&[validator_metrics::BEACON_BLOCK_HTTP_GET],
);
Self::get_validator_block(
&beacon_node,
@@ -520,9 +518,9 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
let slot = signed_block.slot();
match signed_block {
SignedBlock::Full(signed_block) => {
let _post_timer = metrics::start_timer_vec(
&metrics::BLOCK_SERVICE_TIMES,
&[metrics::BEACON_BLOCK_HTTP_POST],
let _post_timer = validator_metrics::start_timer_vec(
&validator_metrics::BLOCK_SERVICE_TIMES,
&[validator_metrics::BEACON_BLOCK_HTTP_POST],
);
beacon_node
.post_beacon_blocks_v2_ssz(signed_block, None)
@@ -530,9 +528,9 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
.or_else(|e| handle_block_post_error(e, slot, log))?
}
SignedBlock::Blinded(signed_block) => {
let _post_timer = metrics::start_timer_vec(
&metrics::BLOCK_SERVICE_TIMES,
&[metrics::BLINDED_BEACON_BLOCK_HTTP_POST],
let _post_timer = validator_metrics::start_timer_vec(
&validator_metrics::BLOCK_SERVICE_TIMES,
&[validator_metrics::BLINDED_BEACON_BLOCK_HTTP_POST],
);
beacon_node
.post_beacon_blinded_blocks_v2_ssz(signed_block, None)

View File

@@ -6,15 +6,11 @@
//! The `DutiesService` is also responsible for sending events to the `BlockService` which trigger
//! block production.
pub mod sync;
use crate::beacon_node_fallback::{ApiTopic, BeaconNodeFallback};
use crate::http_metrics::metrics::{get_int_gauge, set_int_gauge, ATTESTATION_DUTY};
use crate::{
block_service::BlockServiceNotification,
http_metrics::metrics,
validator_store::{DoppelgangerStatus, Error as ValidatorStoreError, ValidatorStore},
};
use crate::block_service::BlockServiceNotification;
use crate::sync::poll_sync_committee_duties;
use crate::sync::SyncDutiesMap;
use beacon_node_fallback::{ApiTopic, BeaconNodeFallback};
use doppelganger_service::DoppelgangerStatus;
use environment::RuntimeContext;
use eth2::types::{
AttesterData, BeaconCommitteeSubscription, DutiesResponse, ProposerData, StateId, ValidatorId,
@@ -29,10 +25,10 @@ use std::collections::{hash_map, BTreeMap, HashMap, HashSet};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;
use sync::poll_sync_committee_duties;
use sync::SyncDutiesMap;
use tokio::{sync::mpsc::Sender, time::sleep};
use types::{ChainSpec, Epoch, EthSpec, Hash256, PublicKeyBytes, SelectionProof, Slot};
use validator_metrics::{get_int_gauge, set_int_gauge, ATTESTATION_DUTY};
use validator_store::{Error as ValidatorStoreError, ValidatorStore};
/// Only retain `HISTORICAL_DUTIES_EPOCHS` duties prior to the current epoch.
const HISTORICAL_DUTIES_EPOCHS: u64 = 2;
@@ -473,8 +469,10 @@ pub fn start_update_service<T: SlotClock + 'static, E: EthSpec>(
async fn poll_validator_indices<T: SlotClock + 'static, E: EthSpec>(
duties_service: &DutiesService<T, E>,
) {
let _timer =
metrics::start_timer_vec(&metrics::DUTIES_SERVICE_TIMES, &[metrics::UPDATE_INDICES]);
let _timer = validator_metrics::start_timer_vec(
&validator_metrics::DUTIES_SERVICE_TIMES,
&[validator_metrics::UPDATE_INDICES],
);
let log = duties_service.context.log();
@@ -518,9 +516,9 @@ async fn poll_validator_indices<T: SlotClock + 'static, E: EthSpec>(
let download_result = duties_service
.beacon_nodes
.first_success(|beacon_node| async move {
let _timer = metrics::start_timer_vec(
&metrics::DUTIES_SERVICE_TIMES,
&[metrics::VALIDATOR_ID_HTTP_GET],
let _timer = validator_metrics::start_timer_vec(
&validator_metrics::DUTIES_SERVICE_TIMES,
&[validator_metrics::VALIDATOR_ID_HTTP_GET],
);
beacon_node
.get_beacon_states_validator_id(
@@ -604,9 +602,9 @@ async fn poll_validator_indices<T: SlotClock + 'static, E: EthSpec>(
async fn poll_beacon_attesters<T: SlotClock + 'static, E: EthSpec>(
duties_service: &Arc<DutiesService<T, E>>,
) -> Result<(), Error> {
let current_epoch_timer = metrics::start_timer_vec(
&metrics::DUTIES_SERVICE_TIMES,
&[metrics::UPDATE_ATTESTERS_CURRENT_EPOCH],
let current_epoch_timer = validator_metrics::start_timer_vec(
&validator_metrics::DUTIES_SERVICE_TIMES,
&[validator_metrics::UPDATE_ATTESTERS_CURRENT_EPOCH],
);
let log = duties_service.context.log();
@@ -660,9 +658,9 @@ async fn poll_beacon_attesters<T: SlotClock + 'static, E: EthSpec>(
update_per_validator_duty_metrics::<T, E>(duties_service, current_epoch, current_slot);
drop(current_epoch_timer);
let next_epoch_timer = metrics::start_timer_vec(
&metrics::DUTIES_SERVICE_TIMES,
&[metrics::UPDATE_ATTESTERS_NEXT_EPOCH],
let next_epoch_timer = validator_metrics::start_timer_vec(
&validator_metrics::DUTIES_SERVICE_TIMES,
&[validator_metrics::UPDATE_ATTESTERS_NEXT_EPOCH],
);
// Download the duties and update the duties for the next epoch.
@@ -682,8 +680,10 @@ async fn poll_beacon_attesters<T: SlotClock + 'static, E: EthSpec>(
update_per_validator_duty_metrics::<T, E>(duties_service, next_epoch, current_slot);
drop(next_epoch_timer);
let subscriptions_timer =
metrics::start_timer_vec(&metrics::DUTIES_SERVICE_TIMES, &[metrics::SUBSCRIPTIONS]);
let subscriptions_timer = validator_metrics::start_timer_vec(
&validator_metrics::DUTIES_SERVICE_TIMES,
&[validator_metrics::SUBSCRIPTIONS],
);
// This vector is intentionally oversized by 10% so that it won't reallocate.
// Each validator has 2 attestation duties occuring in the current and next epoch, for which
@@ -741,9 +741,9 @@ async fn poll_beacon_attesters<T: SlotClock + 'static, E: EthSpec>(
let subscription_result = duties_service
.beacon_nodes
.request(ApiTopic::Subscriptions, |beacon_node| async move {
let _timer = metrics::start_timer_vec(
&metrics::DUTIES_SERVICE_TIMES,
&[metrics::SUBSCRIPTIONS_HTTP_POST],
let _timer = validator_metrics::start_timer_vec(
&validator_metrics::DUTIES_SERVICE_TIMES,
&[validator_metrics::SUBSCRIPTIONS_HTTP_POST],
);
beacon_node
.post_validator_beacon_committee_subscriptions(subscriptions_ref)
@@ -815,9 +815,9 @@ async fn poll_beacon_attesters_for_epoch<T: SlotClock + 'static, E: EthSpec>(
return Ok(());
}
let fetch_timer = metrics::start_timer_vec(
&metrics::DUTIES_SERVICE_TIMES,
&[metrics::UPDATE_ATTESTERS_FETCH],
let fetch_timer = validator_metrics::start_timer_vec(
&validator_metrics::DUTIES_SERVICE_TIMES,
&[validator_metrics::UPDATE_ATTESTERS_FETCH],
);
// Request duties for all uninitialized validators. If there isn't any, we will just request for
@@ -883,9 +883,9 @@ async fn poll_beacon_attesters_for_epoch<T: SlotClock + 'static, E: EthSpec>(
drop(fetch_timer);
let _store_timer = metrics::start_timer_vec(
&metrics::DUTIES_SERVICE_TIMES,
&[metrics::UPDATE_ATTESTERS_STORE],
let _store_timer = validator_metrics::start_timer_vec(
&validator_metrics::DUTIES_SERVICE_TIMES,
&[validator_metrics::UPDATE_ATTESTERS_STORE],
);
debug!(
@@ -1029,9 +1029,9 @@ async fn post_validator_duties_attester<T: SlotClock + 'static, E: EthSpec>(
duties_service
.beacon_nodes
.first_success(|beacon_node| async move {
let _timer = metrics::start_timer_vec(
&metrics::DUTIES_SERVICE_TIMES,
&[metrics::ATTESTER_DUTIES_HTTP_POST],
let _timer = validator_metrics::start_timer_vec(
&validator_metrics::DUTIES_SERVICE_TIMES,
&[validator_metrics::ATTESTER_DUTIES_HTTP_POST],
);
beacon_node
.post_validator_duties_attester(epoch, validator_indices)
@@ -1089,9 +1089,9 @@ async fn fill_in_selection_proofs<T: SlotClock + 'static, E: EthSpec>(
continue;
}
let timer = metrics::start_timer_vec(
&metrics::DUTIES_SERVICE_TIMES,
&[metrics::ATTESTATION_SELECTION_PROOFS],
let timer = validator_metrics::start_timer_vec(
&validator_metrics::DUTIES_SERVICE_TIMES,
&[validator_metrics::ATTESTATION_SELECTION_PROOFS],
);
// Sign selection proofs (serially).
@@ -1223,8 +1223,10 @@ async fn poll_beacon_proposers<T: SlotClock + 'static, E: EthSpec>(
duties_service: &DutiesService<T, E>,
block_service_tx: &mut Sender<BlockServiceNotification>,
) -> Result<(), Error> {
let _timer =
metrics::start_timer_vec(&metrics::DUTIES_SERVICE_TIMES, &[metrics::UPDATE_PROPOSERS]);
let _timer = validator_metrics::start_timer_vec(
&validator_metrics::DUTIES_SERVICE_TIMES,
&[validator_metrics::UPDATE_PROPOSERS],
);
let log = duties_service.context.log();
@@ -1261,9 +1263,9 @@ async fn poll_beacon_proposers<T: SlotClock + 'static, E: EthSpec>(
let download_result = duties_service
.beacon_nodes
.first_success(|beacon_node| async move {
let _timer = metrics::start_timer_vec(
&metrics::DUTIES_SERVICE_TIMES,
&[metrics::PROPOSER_DUTIES_HTTP_GET],
let _timer = validator_metrics::start_timer_vec(
&validator_metrics::DUTIES_SERVICE_TIMES,
&[validator_metrics::PROPOSER_DUTIES_HTTP_GET],
);
beacon_node
.get_validator_duties_proposer(current_epoch)
@@ -1341,7 +1343,7 @@ async fn poll_beacon_proposers<T: SlotClock + 'static, E: EthSpec>(
"Detected new block proposer";
"current_slot" => current_slot,
);
metrics::inc_counter(&metrics::PROPOSAL_CHANGED);
validator_metrics::inc_counter(&validator_metrics::PROPOSAL_CHANGED);
}
}

View File

@@ -0,0 +1,6 @@
pub mod attestation_service;
pub mod block_service;
pub mod duties_service;
pub mod preparation_service;
pub mod sync;
pub mod sync_committee_service;

View File

@@ -1,6 +1,6 @@
use crate::beacon_node_fallback::{ApiTopic, BeaconNodeFallback};
use crate::validator_store::{DoppelgangerStatus, Error as ValidatorStoreError, ValidatorStore};
use beacon_node_fallback::{ApiTopic, BeaconNodeFallback};
use bls::PublicKeyBytes;
use doppelganger_service::DoppelgangerStatus;
use environment::RuntimeContext;
use parking_lot::RwLock;
use slog::{debug, error, info, warn};
@@ -15,6 +15,7 @@ use types::{
Address, ChainSpec, EthSpec, ProposerPreparationData, SignedValidatorRegistrationData,
ValidatorRegistrationData,
};
use validator_store::{Error as ValidatorStoreError, ProposalData, ValidatorStore};
/// Number of epochs before the Bellatrix hard fork to begin posting proposer preparations.
const PROPOSER_PREPARATION_LOOKAHEAD_EPOCHS: u64 = 2;
@@ -23,6 +24,7 @@ const PROPOSER_PREPARATION_LOOKAHEAD_EPOCHS: u64 = 2;
const EPOCHS_PER_VALIDATOR_REGISTRATION_SUBMISSION: u64 = 1;
/// Builds an `PreparationService`.
#[derive(Default)]
pub struct PreparationServiceBuilder<T: SlotClock + 'static, E: EthSpec> {
validator_store: Option<Arc<ValidatorStore<T, E>>>,
slot_clock: Option<T>,
@@ -492,11 +494,3 @@ impl<T: SlotClock + 'static, E: EthSpec> PreparationService<T, E> {
Ok(())
}
}
/// A helper struct, used for passing data from the validator store to services.
pub struct ProposalData {
pub(crate) validator_index: Option<u64>,
pub(crate) fee_recipient: Option<Address>,
pub(crate) gas_limit: u64,
pub(crate) builder_proposals: bool,
}

View File

@@ -1,10 +1,5 @@
use crate::{
doppelganger_service::DoppelgangerStatus,
duties_service::{DutiesService, Error},
http_metrics::metrics,
validator_store::Error as ValidatorStoreError,
};
use crate::duties_service::{DutiesService, Error};
use doppelganger_service::DoppelgangerStatus;
use futures::future::join_all;
use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};
use slog::{crit, debug, info, warn};
@@ -13,6 +8,7 @@ use std::collections::{HashMap, HashSet};
use std::marker::PhantomData;
use std::sync::Arc;
use types::{ChainSpec, EthSpec, PublicKeyBytes, Slot, SyncDuty, SyncSelectionProof, SyncSubnetId};
use validator_store::Error as ValidatorStoreError;
/// Number of epochs in advance to compute selection proofs when not in `distributed` mode.
pub const AGGREGATION_PRE_COMPUTE_EPOCHS: u64 = 2;
@@ -442,9 +438,9 @@ pub async fn poll_sync_committee_duties_for_period<T: SlotClock + 'static, E: Et
let duties_response = duties_service
.beacon_nodes
.first_success(|beacon_node| async move {
let _timer = metrics::start_timer_vec(
&metrics::DUTIES_SERVICE_TIMES,
&[metrics::VALIDATOR_DUTIES_SYNC_HTTP_POST],
let _timer = validator_metrics::start_timer_vec(
&validator_metrics::DUTIES_SERVICE_TIMES,
&[validator_metrics::VALIDATOR_DUTIES_SYNC_HTTP_POST],
);
beacon_node
.post_validator_duties_sync(period_start_epoch, local_indices)

View File

@@ -1,8 +1,5 @@
use crate::beacon_node_fallback::{ApiTopic, BeaconNodeFallback};
use crate::{
duties_service::DutiesService,
validator_store::{Error as ValidatorStoreError, ValidatorStore},
};
use crate::duties_service::DutiesService;
use beacon_node_fallback::{ApiTopic, BeaconNodeFallback};
use environment::RuntimeContext;
use eth2::types::BlockId;
use futures::future::join_all;
@@ -18,6 +15,7 @@ use types::{
ChainSpec, EthSpec, Hash256, PublicKeyBytes, Slot, SyncCommitteeSubscription,
SyncContributionData, SyncDuty, SyncSelectionProof, SyncSubnetId,
};
use validator_store::{Error as ValidatorStoreError, ValidatorStore};
pub const SUBSCRIPTION_LOOKAHEAD_EPOCHS: u64 = 4;

View File

@@ -0,0 +1,23 @@
[package]
name = "validator_store"
version = "0.1.0"
edition = { workspace = true }
authors = ["Sigma Prime <contact@sigmaprime.io>"]
[lib]
name = "validator_store"
path = "src/lib.rs"
[dependencies]
account_utils = { workspace = true }
doppelganger_service = { workspace = true }
initialized_validators = { workspace = true }
parking_lot = { workspace = true }
serde = { workspace = true }
signing_method = { workspace = true }
slashing_protection = { workspace = true }
slog = { workspace = true }
slot_clock = { workspace = true }
task_executor = { workspace = true }
types = { workspace = true }
validator_metrics = { workspace = true }

View File

@@ -1,12 +1,9 @@
use crate::{
doppelganger_service::DoppelgangerService,
http_metrics::metrics,
initialized_validators::InitializedValidators,
signing_method::{Error as SigningError, SignableMessage, SigningContext, SigningMethod},
Config,
};
use account_utils::validator_definitions::{PasswordStorage, ValidatorDefinition};
use doppelganger_service::{DoppelgangerService, DoppelgangerStatus, DoppelgangerValidatorStore};
use initialized_validators::InitializedValidators;
use parking_lot::{Mutex, RwLock};
use serde::{Deserialize, Serialize};
use signing_method::{Error as SigningError, SignableMessage, SigningContext, SigningMethod};
use slashing_protection::{
interchange::Interchange, InterchangeError, NotSafe, Safe, SlashingDatabase,
};
@@ -26,9 +23,6 @@ use types::{
ValidatorRegistrationData, VoluntaryExit,
};
pub use crate::doppelganger_service::DoppelgangerStatus;
use crate::preparation_service::ProposalData;
#[derive(Debug, PartialEq)]
pub enum Error {
DoppelgangerProtected(PublicKeyBytes),
@@ -48,6 +42,30 @@ impl From<SigningError> for Error {
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct Config {
/// Fallback fee recipient address.
pub fee_recipient: Option<Address>,
/// Fallback gas limit.
pub gas_limit: Option<u64>,
/// Enable use of the blinded block endpoints during proposals.
pub builder_proposals: bool,
/// Enable slashing protection even while using web3signer keys.
pub enable_web3signer_slashing_protection: bool,
/// If true, Lighthouse will prefer builder proposals, if available.
pub prefer_builder_proposals: bool,
/// Specifies the boost factor, a percentage multiplier to apply to the builder's payload value.
pub builder_boost_factor: Option<u64>,
}
/// A helper struct, used for passing data from the validator store to services.
pub struct ProposalData {
pub validator_index: Option<u64>,
pub fee_recipient: Option<Address>,
pub gas_limit: u64,
pub builder_proposals: bool,
}
/// Number of epochs of slashing protection history to keep.
///
/// This acts as a maximum safe-guard against clock drift.
@@ -77,6 +95,12 @@ pub struct ValidatorStore<T, E: EthSpec> {
_phantom: PhantomData<E>,
}
impl<T: SlotClock + 'static, E: EthSpec> DoppelgangerValidatorStore for ValidatorStore<T, E> {
fn get_validator_index(&self, pubkey: &PublicKeyBytes) -> Option<u64> {
self.validator_index(pubkey)
}
}
impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
// All arguments are different types. Making the fields `pub` is undesired. A builder seems
// unnecessary.
@@ -590,7 +614,10 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
match slashing_status {
// We can safely sign this block without slashing.
Ok(Safe::Valid) => {
metrics::inc_counter_vec(&metrics::SIGNED_BLOCKS_TOTAL, &[metrics::SUCCESS]);
validator_metrics::inc_counter_vec(
&validator_metrics::SIGNED_BLOCKS_TOTAL,
&[validator_metrics::SUCCESS],
);
let signature = signing_method
.get_signature::<E, Payload>(
@@ -607,7 +634,10 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
self.log,
"Skipping signing of previously signed block";
);
metrics::inc_counter_vec(&metrics::SIGNED_BLOCKS_TOTAL, &[metrics::SAME_DATA]);
validator_metrics::inc_counter_vec(
&validator_metrics::SIGNED_BLOCKS_TOTAL,
&[validator_metrics::SAME_DATA],
);
Err(Error::SameData)
}
Err(NotSafe::UnregisteredValidator(pk)) => {
@@ -617,7 +647,10 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
"msg" => "Carefully consider running with --init-slashing-protection (see --help)",
"public_key" => format!("{:?}", pk)
);
metrics::inc_counter_vec(&metrics::SIGNED_BLOCKS_TOTAL, &[metrics::UNREGISTERED]);
validator_metrics::inc_counter_vec(
&validator_metrics::SIGNED_BLOCKS_TOTAL,
&[validator_metrics::UNREGISTERED],
);
Err(Error::Slashable(NotSafe::UnregisteredValidator(pk)))
}
Err(e) => {
@@ -626,7 +659,10 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
"Not signing slashable block";
"error" => format!("{:?}", e)
);
metrics::inc_counter_vec(&metrics::SIGNED_BLOCKS_TOTAL, &[metrics::SLASHABLE]);
validator_metrics::inc_counter_vec(
&validator_metrics::SIGNED_BLOCKS_TOTAL,
&[validator_metrics::SLASHABLE],
);
Err(Error::Slashable(e))
}
}
@@ -681,7 +717,10 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
.add_signature(&signature, validator_committee_position)
.map_err(Error::UnableToSignAttestation)?;
metrics::inc_counter_vec(&metrics::SIGNED_ATTESTATIONS_TOTAL, &[metrics::SUCCESS]);
validator_metrics::inc_counter_vec(
&validator_metrics::SIGNED_ATTESTATIONS_TOTAL,
&[validator_metrics::SUCCESS],
);
Ok(())
}
@@ -690,9 +729,9 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
self.log,
"Skipping signing of previously signed attestation"
);
metrics::inc_counter_vec(
&metrics::SIGNED_ATTESTATIONS_TOTAL,
&[metrics::SAME_DATA],
validator_metrics::inc_counter_vec(
&validator_metrics::SIGNED_ATTESTATIONS_TOTAL,
&[validator_metrics::SAME_DATA],
);
Err(Error::SameData)
}
@@ -703,9 +742,9 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
"msg" => "Carefully consider running with --init-slashing-protection (see --help)",
"public_key" => format!("{:?}", pk)
);
metrics::inc_counter_vec(
&metrics::SIGNED_ATTESTATIONS_TOTAL,
&[metrics::UNREGISTERED],
validator_metrics::inc_counter_vec(
&validator_metrics::SIGNED_ATTESTATIONS_TOTAL,
&[validator_metrics::UNREGISTERED],
);
Err(Error::Slashable(NotSafe::UnregisteredValidator(pk)))
}
@@ -716,9 +755,9 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
"attestation" => format!("{:?}", attestation.data()),
"error" => format!("{:?}", e)
);
metrics::inc_counter_vec(
&metrics::SIGNED_ATTESTATIONS_TOTAL,
&[metrics::SLASHABLE],
validator_metrics::inc_counter_vec(
&validator_metrics::SIGNED_ATTESTATIONS_TOTAL,
&[validator_metrics::SLASHABLE],
);
Err(Error::Slashable(e))
}
@@ -743,7 +782,10 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
)
.await?;
metrics::inc_counter_vec(&metrics::SIGNED_VOLUNTARY_EXITS_TOTAL, &[metrics::SUCCESS]);
validator_metrics::inc_counter_vec(
&validator_metrics::SIGNED_VOLUNTARY_EXITS_TOTAL,
&[validator_metrics::SUCCESS],
);
Ok(SignedVoluntaryExit {
message: voluntary_exit,
@@ -769,9 +811,9 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
)
.await?;
metrics::inc_counter_vec(
&metrics::SIGNED_VALIDATOR_REGISTRATIONS_TOTAL,
&[metrics::SUCCESS],
validator_metrics::inc_counter_vec(
&validator_metrics::SIGNED_VALIDATOR_REGISTRATIONS_TOTAL,
&[validator_metrics::SUCCESS],
);
Ok(SignedValidatorRegistrationData {
@@ -807,7 +849,10 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
)
.await?;
metrics::inc_counter_vec(&metrics::SIGNED_AGGREGATES_TOTAL, &[metrics::SUCCESS]);
validator_metrics::inc_counter_vec(
&validator_metrics::SIGNED_AGGREGATES_TOTAL,
&[validator_metrics::SUCCESS],
);
Ok(SignedAggregateAndProof::from_aggregate_and_proof(
message, signature,
@@ -843,7 +888,10 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
.await
.map_err(Error::UnableToSign)?;
metrics::inc_counter_vec(&metrics::SIGNED_SELECTION_PROOFS_TOTAL, &[metrics::SUCCESS]);
validator_metrics::inc_counter_vec(
&validator_metrics::SIGNED_SELECTION_PROOFS_TOTAL,
&[validator_metrics::SUCCESS],
);
Ok(signature.into())
}
@@ -862,9 +910,9 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
// Bypass `with_validator_signing_method`: sync committee messages are not slashable.
let signing_method = self.doppelganger_bypassed_signing_method(*validator_pubkey)?;
metrics::inc_counter_vec(
&metrics::SIGNED_SYNC_SELECTION_PROOFS_TOTAL,
&[metrics::SUCCESS],
validator_metrics::inc_counter_vec(
&validator_metrics::SIGNED_SYNC_SELECTION_PROOFS_TOTAL,
&[validator_metrics::SUCCESS],
);
let message = SyncAggregatorSelectionData {
@@ -911,9 +959,9 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
.await
.map_err(Error::UnableToSign)?;
metrics::inc_counter_vec(
&metrics::SIGNED_SYNC_COMMITTEE_MESSAGES_TOTAL,
&[metrics::SUCCESS],
validator_metrics::inc_counter_vec(
&validator_metrics::SIGNED_SYNC_COMMITTEE_MESSAGES_TOTAL,
&[validator_metrics::SUCCESS],
);
Ok(SyncCommitteeMessage {
@@ -953,9 +1001,9 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
.await
.map_err(Error::UnableToSign)?;
metrics::inc_counter_vec(
&metrics::SIGNED_SYNC_COMMITTEE_CONTRIBUTIONS_TOTAL,
&[metrics::SUCCESS],
validator_metrics::inc_counter_vec(
&validator_metrics::SIGNED_SYNC_COMMITTEE_CONTRIBUTIONS_TOTAL,
&[validator_metrics::SUCCESS],
);
Ok(SignedContributionAndProof { message, signature })
@@ -1029,7 +1077,8 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
info!(self.log, "Pruning slashing protection DB"; "epoch" => current_epoch);
}
let _timer = metrics::start_timer(&metrics::SLASHING_PROTECTION_PRUNE_TIMES);
let _timer =
validator_metrics::start_timer(&validator_metrics::SLASHING_PROTECTION_PRUNE_TIMES);
let new_min_target_epoch = current_epoch.saturating_sub(SLASHING_PROTECTION_HISTORY_EPOCHS);
let new_min_slot = new_min_target_epoch.start_slot(E::slots_per_epoch());

View File

@@ -25,4 +25,4 @@ derivative = { workspace = true }
[dev-dependencies]
tempfile = { workspace = true }
regex = { workspace = true }
validator_client = { workspace = true }
validator_http_api = { workspace = true }

View File

@@ -148,7 +148,7 @@ mod test {
use crate::{
common::ValidatorSpecification, import_validators::tests::TestBuilder as ImportTestBuilder,
};
use validator_client::http_api::{test_utils::ApiTester, Config as HttpConfig};
use validator_http_api::{test_utils::ApiTester, Config as HttpConfig};
struct TestBuilder {
delete_config: Option<DeleteConfig>,

View File

@@ -387,7 +387,7 @@ pub mod tests {
str::FromStr,
};
use tempfile::{tempdir, TempDir};
use validator_client::http_api::{test_utils::ApiTester, Config as HttpConfig};
use validator_http_api::{test_utils::ApiTester, Config as HttpConfig};
const VC_TOKEN_FILE_NAME: &str = "vc_token.json";

View File

@@ -87,7 +87,7 @@ mod test {
use crate::{
common::ValidatorSpecification, import_validators::tests::TestBuilder as ImportTestBuilder,
};
use validator_client::http_api::{test_utils::ApiTester, Config as HttpConfig};
use validator_http_api::{test_utils::ApiTester, Config as HttpConfig};
struct TestBuilder {
list_config: Option<ListConfig>,

View File

@@ -668,7 +668,7 @@ mod test {
use account_utils::validator_definitions::SigningDefinition;
use std::fs;
use tempfile::{tempdir, TempDir};
use validator_client::http_api::{test_utils::ApiTester, Config as HttpConfig};
use validator_http_api::{test_utils::ApiTester, Config as HttpConfig};
const SRC_VC_TOKEN_FILE_NAME: &str = "src_vc_token.json";
const DEST_VC_TOKEN_FILE_NAME: &str = "dest_vc_token.json";