diff --git a/validator_client/src/beacon_node_fallback.rs b/validator_client/src/beacon_node_fallback.rs index 25c430c022..1e4d901651 100644 --- a/validator_client/src/beacon_node_fallback.rs +++ b/validator_client/src/beacon_node_fallback.rs @@ -230,7 +230,6 @@ impl CandidateBeaconNode { if let Some(slot_clock) = slot_clock { match check_node_health(&self.beacon_node, log).await { Ok((head, is_optimistic, el_offline)) => { - println!("{} {} {}", head, is_optimistic, el_offline); let Some(slot_clock_head) = slot_clock.now() else { let e = match slot_clock.is_prior_to_genesis() { Some(true) => CandidateError::PreGenesis, @@ -275,7 +274,6 @@ impl CandidateBeaconNode { Err(e) => { // Set the health as `Err` which is sorted last in the list. *self.health.write().await = Err(e); - println!("{:?}", e); Err(e) } } @@ -703,24 +701,22 @@ impl ApiTopic { #[cfg(test)] mod tests { - use std::str::FromStr; - use logging::test_logger; + use std::str::FromStr; use strum::VariantNames; + use types::EmptyBlock; use eth2::Timeouts; use slot_clock::TestingSlotClock; - use types::EmptyBlock; use types::{BeaconBlockDeneb, BlindedBeaconBlock, MainnetEthSpec, Slot}; + use super::*; use crate::beacon_node_health::BeaconNodeHealthTier; use crate::block_service::{BlockService, BlockServiceBuilder, UnsignedBlock}; use crate::testing::mock_beacon_node::MockBeaconNode; use crate::testing::validator_test_rig::ValidatorTestRig; use crate::SensitiveUrl; - use super::*; - type E = MainnetEthSpec; #[test] @@ -846,6 +842,7 @@ mod tests { #[tokio::test] async fn update_all_candidates_should_update_sync_status() { + let spec = Arc::new(MainnetEthSpec::default_spec()); let mut mock_beacon_node_one = MockBeaconNode::::new().await; let mut mock_beacon_node_two = MockBeaconNode::::new().await; let mut mock_beacon_node_three = MockBeaconNode::::new().await; @@ -857,18 +854,29 @@ mod tests { let beacon_node_three = CandidateBeaconNode::::new(mock_beacon_node_three.beacon_api_client.clone(), 2); - let beacon_node_fallback: BeaconNodeFallback = BeaconNodeFallback::new( - // Put this out of order to be sorted later - vec![ - beacon_node_two.clone(), - beacon_node_three.clone(), - beacon_node_one.clone(), - ], - Config::default(), - vec![], // to broadcast blocks to both bns - Arc::new(MainnetEthSpec::default_spec()), - test_logger(), - ); + let mut beacon_node_fallback: BeaconNodeFallback = + BeaconNodeFallback::new( + // Put this out of order to be sorted later + vec![ + beacon_node_two.clone(), + beacon_node_three.clone(), + beacon_node_one.clone(), + ], + Config::default(), + vec![], // to broadcast blocks to both bns + spec.clone(), + test_logger(), + ); + + beacon_node_fallback.set_slot_clock(TestingSlotClock::new( + Slot::new(1), + Duration::from_secs(0), + Duration::from_secs(12), + )); + + mock_beacon_node_one.mock_config_spec(&spec); + mock_beacon_node_two.mock_config_spec(&spec); + mock_beacon_node_three.mock_config_spec(&spec); // BeaconNodeHealthTier 1 mock_beacon_node_one.mock_get_node_syncing(eth2::types::SyncingData { @@ -897,100 +905,75 @@ mod tests { beacon_node_fallback.update_all_candidates().await; - // logging to check health - { - let candidates = beacon_node_fallback.candidates.read().await; - for (i, candidate) in candidates.iter().enumerate() { - println!( - "Candidate {} health: {:?}", - i, - candidate.health.read().await - ); - } - - assert_eq!( - vec![beacon_node_one, beacon_node_two, beacon_node_three], - *candidates - ); - } + let candidates = beacon_node_fallback.candidates.read().await; + assert_eq!( + vec![beacon_node_one, beacon_node_two, beacon_node_three], + *candidates + ); } - // #[tokio::test] - // async fn check_fallback_broadcast_with_bn_delay() { - // let test_rig = ValidatorTestRig::new().await; - // let mut mock_beacon_node_one = MockBeaconNode::::new().await; - // let mut mock_beacon_node_two = MockBeaconNode::::new().await; - - // let beacon_node_one = - // CandidateBeaconNode::new(mock_beacon_node_one.beacon_api_client.clone(), 0); - // let beacon_node_two = - // CandidateBeaconNode::new(mock_beacon_node_two.beacon_api_client.clone(), 1); - - // let beacon_node_fallback = BeaconNodeFallback::new( - // vec![beacon_node_one, beacon_node_two], - // Config::default(), - // vec![ApiTopic::Blocks], // to broadcast blocks to both bns - // test_rig.spec.clone(), - // test_rig.logger.clone(), - // ); - - // let mock1 = mock_beacon_node_one.mock_post_beacon_blinded_blocks_v1(Duration::from_secs(0)); - // let mock2 = mock_beacon_node_two.mock_post_beacon_blinded_blocks_v1(Duration::from_secs(0)); - - // // logging to check health - // { - // let candidates = beacon_node_fallback.candidates.read().await; - // for (i, candidate) in candidates.iter().enumerate() { - // println!( - // "Candidate {} health: {:?}", - // i, - // candidate.health.read().await - // ); - // } - // } - - // let block_service: BlockService = - // BlockServiceBuilder::new() - // .slot_clock(test_rig.slot_clock) - // .validator_store(test_rig.validator_store.clone()) - // .beacon_nodes(Arc::new(beacon_node_fallback)) - // .runtime_context(test_rig.runtime_context) - // .build() - // .unwrap(); - - // // Update all beacon nodes health - // beacon_node_fallback.update_all_candidates().await; - // let first_beacon_node = { - // let candidates = beacon_node_fallback.candidates.read().await; - // candidates.first().cloned() - // }; - - // let validators = test_rig.validator_store.initialized_validators(); - // let validators = validators.read(); - // let first_validator = validators.validator_definitions().first().unwrap(); - // let unsigned_block = UnsignedBlock::Blinded(BlindedBeaconBlock::Deneb( - // BeaconBlockDeneb::empty(&test_rig.spec), - // )); - // let voting_pubkey = first_validator.voting_public_key.clone(); - // let result = block_service - // .publish_block_for_testing(Slot::new(1), &voting_pubkey.into(), unsigned_block) - // .await; - - // mock1.expect(1).assert(); - // mock2.expect(1).assert(); - - // let received_blocks_one = mock_beacon_node_one.received_blocks.lock().unwrap(); - // let received_blocks_two = mock_beacon_node_two.received_blocks.lock().unwrap(); - - // assert_eq!(received_blocks_one.len(), 1); - // assert_eq!(received_blocks_two.len(), 0); - - // // first success fn - // // --broadcast - // // request - - // if let Err(e) = result { - // panic!("Expected block to be broadcasted to BN, but failed with error: {e:?}"); - // } - // } + #[tokio::test] + async fn broadcast_should_send_to_all_bns() { + let test_rig = ValidatorTestRig::new().await; + let mut mock_beacon_node_one = MockBeaconNode::::new().await; + let mut mock_beacon_node_two = MockBeaconNode::::new().await; + + let beacon_node_one = + CandidateBeaconNode::new(mock_beacon_node_one.beacon_api_client.clone(), 0); + let beacon_node_two = + CandidateBeaconNode::new(mock_beacon_node_two.beacon_api_client.clone(), 1); + + let mut beacon_node_fallback = BeaconNodeFallback::new( + vec![beacon_node_one, beacon_node_two], + Config::default(), + vec![ApiTopic::Blocks], // to broadcast blocks to both bns + test_rig.spec.clone(), + test_rig.logger.clone(), + ); + + beacon_node_fallback.set_slot_clock(TestingSlotClock::new( + Slot::new(1), + Duration::from_secs(0), + Duration::from_secs(12), + )); + + mock_beacon_node_one.mock_config_spec(&test_rig.spec); + mock_beacon_node_two.mock_config_spec(&test_rig.spec); + + let mock1 = mock_beacon_node_one.mock_post_beacon_blinded_blocks_v1(Duration::from_secs(0)); + let mock2 = mock_beacon_node_two.mock_post_beacon_blinded_blocks_v1(Duration::from_secs(0)); + + let beacon_node_fallback = Arc::new(beacon_node_fallback); + let block_service: BlockService = + BlockServiceBuilder::new() + .slot_clock(test_rig.slot_clock) + .validator_store(test_rig.validator_store.clone()) + .beacon_nodes(beacon_node_fallback.clone()) + .runtime_context(test_rig.runtime_context) + .build() + .unwrap(); + + beacon_node_fallback.update_all_candidates().await; + + let validators = test_rig.validator_store.initialized_validators(); + let validators = validators.read(); + let first_validator = validators.validator_definitions().first().unwrap(); + let unsigned_block = UnsignedBlock::Blinded(BlindedBeaconBlock::Deneb( + BeaconBlockDeneb::empty(&test_rig.spec), + )); + let voting_pubkey = first_validator.voting_public_key.clone(); + let result = block_service + .publish_block_for_testing(Slot::new(1), &voting_pubkey.into(), unsigned_block) + .await; + assert!(result.is_ok()); + + mock1.expect(1).assert(); + mock2.expect(1).assert(); + + let received_blocks_one = mock_beacon_node_one.received_blocks.lock().unwrap(); + let received_blocks_two = mock_beacon_node_two.received_blocks.lock().unwrap(); + + assert_eq!(received_blocks_one.len(), 1); + assert_eq!(received_blocks_two.len(), 1); + } } diff --git a/validator_client/src/testing/mock_beacon_node.rs b/validator_client/src/testing/mock_beacon_node.rs index 39ea25227b..f6d3a5985c 100644 --- a/validator_client/src/testing/mock_beacon_node.rs +++ b/validator_client/src/testing/mock_beacon_node.rs @@ -15,8 +15,8 @@ use eth2::{ use logging::test_logger; use sensitive_url::SensitiveUrl; use types::{ - BeaconBlock, BlobsList, EthSpec, ForkName, ForkVersionedResponse, KzgProofs, - SignedBlindedBeaconBlock, Slot, Uint256, + BeaconBlock, BlobsList, ChainSpec, ConfigAndPreset, EthSpec, ForkName, ForkVersionedResponse, + KzgProofs, SignedBlindedBeaconBlock, Slot, Uint256, }; pub struct MockBeaconNode { @@ -105,6 +105,17 @@ impl MockBeaconNode { self } + pub fn mock_config_spec(&mut self, spec: &ChainSpec) { + let path_pattern = Regex::new(r"^/eth/v1/config/spec$").unwrap(); + let config_and_preset = ConfigAndPreset::from_chain_spec::(&spec, None); + let data = GenericResponse::from(config_and_preset); + self.server + .mock("GET", Matcher::Regex(path_pattern.to_string())) + .with_status(200) + .with_body(serde_json::to_string(&data).unwrap()) + .create(); + } + pub fn mock_get_node_syncing(&mut self, response: SyncingData) { let path_pattern = Regex::new(r"^/eth/v1/node/syncing$").unwrap();