Skip to content

Commit 3628204

Browse files
committed
First working SSV dynamic refresh test
1 parent 2d2bc7f commit 3628204

File tree

4 files changed

+129
-39
lines changed

4 files changed

+129
-39
lines changed

crates/pbs/src/service.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ impl PbsService {
138138
if new_pubkeys.is_empty() {
139139
return;
140140
}
141+
// Log the new pubkeys
142+
for (pubkey, runtime_config) in new_pubkeys.iter() {
143+
info!("adding new pubkey {pubkey} to mux {}", runtime_config.id);
144+
}
141145
{
142146
// Since config isn't an RwLock, the option with the least amount of code churn
143147
// is to just clone the whole config and replace the mux_lookup

tests/src/mock_ssv.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub struct SsvMockState {
2626

2727
/// Creates a simple mock server to simulate the SSV API endpoint under
2828
/// various conditions for testing. Note this ignores
29-
pub async fn create_mock_server(
29+
pub async fn create_mock_ssv_server(
3030
port: u16,
3131
state: Option<SsvMockState>,
3232
) -> Result<JoinHandle<()>, axum::Error> {

tests/tests/pbs_mux.rs

Lines changed: 9 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use cb_common::{
1010
use cb_pbs::{DefaultBuilderApi, PbsService, PbsState};
1111
use cb_tests::{
1212
mock_relay::{MockRelayState, start_mock_relay_service},
13-
mock_ssv::{SsvMockState, TEST_HTTP_TIMEOUT, create_mock_server},
13+
mock_ssv::{SsvMockState, TEST_HTTP_TIMEOUT, create_mock_ssv_server},
1414
mock_validator::MockValidator,
1515
utils::{
1616
bls_pubkey_from_hex_unchecked, generate_mock_relay, get_pbs_static_config, setup_test_env,
@@ -25,10 +25,10 @@ use url::Url;
2525

2626
#[tokio::test]
2727
/// Tests that a successful SSV network fetch is handled and parsed properly
28-
async fn test_ssv_network_fetch() -> eyre::Result<()> {
28+
async fn test_ssv_network_fetch() -> Result<()> {
2929
// Start the mock server
3030
let port = 30100;
31-
let _server_handle = create_mock_server(port, None).await?;
31+
let _server_handle = create_mock_ssv_server(port, None).await?;
3232
let url = Url::parse(&format!("http://localhost:{port}/test_chain/validators/in_operator/1"))
3333
.unwrap();
3434
let response =
@@ -61,10 +61,10 @@ async fn test_ssv_network_fetch() -> eyre::Result<()> {
6161
#[tokio::test]
6262
/// Tests that the SSV network fetch is handled properly when the response's
6363
/// body is too large
64-
async fn test_ssv_network_fetch_big_data() -> eyre::Result<()> {
64+
async fn test_ssv_network_fetch_big_data() -> Result<()> {
6565
// Start the mock server
6666
let port = 30101;
67-
let server_handle = cb_tests::mock_ssv::create_mock_server(port, None).await?;
67+
let server_handle = cb_tests::mock_ssv::create_mock_ssv_server(port, None).await?;
6868
let url = Url::parse(&format!("http://localhost:{port}/big_data")).unwrap();
6969
let response = fetch_ssv_pubkeys_from_url(url, Duration::from_secs(120)).await;
7070

@@ -92,14 +92,14 @@ async fn test_ssv_network_fetch_big_data() -> eyre::Result<()> {
9292
#[tokio::test]
9393
/// Tests that the SSV network fetch is handled properly when the request
9494
/// times out
95-
async fn test_ssv_network_fetch_timeout() -> eyre::Result<()> {
95+
async fn test_ssv_network_fetch_timeout() -> Result<()> {
9696
// Start the mock server
9797
let port = 30102;
9898
let state = SsvMockState {
9999
validators: Arc::new(RwLock::new(vec![])),
100100
force_timeout: Arc::new(RwLock::new(true)),
101101
};
102-
let server_handle = create_mock_server(port, Some(state)).await?;
102+
let server_handle = create_mock_ssv_server(port, Some(state)).await?;
103103
let url = Url::parse(&format!("http://localhost:{port}/test_chain/validators/in_operator/1"))
104104
.unwrap();
105105
let response = fetch_ssv_pubkeys_from_url(url, Duration::from_secs(TEST_HTTP_TIMEOUT)).await;
@@ -119,11 +119,11 @@ async fn test_ssv_network_fetch_timeout() -> eyre::Result<()> {
119119
#[tokio::test]
120120
/// Tests that the SSV network fetch is handled properly when the response's
121121
/// content-length header is missing
122-
async fn test_ssv_network_fetch_big_data_without_content_length() -> eyre::Result<()> {
122+
async fn test_ssv_network_fetch_big_data_without_content_length() -> Result<()> {
123123
// Start the mock server
124124
let port = 30103;
125125
set_ignore_content_length(true);
126-
let server_handle = create_mock_server(port, None).await?;
126+
let server_handle = create_mock_ssv_server(port, None).await?;
127127
let url = Url::parse(&format!("http://localhost:{port}/big_data")).unwrap();
128128
let response = fetch_ssv_pubkeys_from_url(url, Duration::from_secs(120)).await;
129129

@@ -226,32 +226,3 @@ async fn test_mux() -> Result<()> {
226226

227227
Ok(())
228228
}
229-
230-
/*
231-
#[tokio::test]
232-
#[tracing_test::traced_test]
233-
async fn test_auto_refresh() -> Result<()> {
234-
// This test reads the log files to verify behavior, so we can't attach a global
235-
// trace listener setup_test_env();
236-
237-
let signer = random_secret();
238-
let pubkey = signer.public_key();
239-
240-
let chain = Chain::Hoodi;
241-
let pbs_port = 3710;
242-
243-
// Start the mock SSV API server
244-
let ssv_api_port = 3711;
245-
let ssv_api_url = format!("http://localhost:{ssv_api_port}");
246-
let mock_ssv_state = SsvMockState {
247-
validators: Arc::new(RwLock::new(vec![pubkey])),
248-
force_timeout: Arc::new(RwLock::new(false)),
249-
};
250-
251-
// Run PBS service
252-
let state = PbsState::new(config);
253-
tokio::spawn(PbsService::run::<(), DefaultBuilderApi>(state));
254-
255-
Ok(())
256-
}
257-
*/

tests/tests/pbs_mux_refresh.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
use std::{sync::Arc, time::Duration};
2+
3+
use cb_common::{
4+
config::{MuxConfig, MuxKeysLoader, PbsMuxes},
5+
interop::ssv::types::SSVValidator,
6+
signer::random_secret,
7+
types::Chain,
8+
};
9+
use cb_pbs::{DefaultBuilderApi, PbsService, PbsState};
10+
use cb_tests::{
11+
mock_relay::{MockRelayState, start_mock_relay_service},
12+
mock_ssv::{SsvMockState, create_mock_ssv_server},
13+
utils::{generate_mock_relay, get_pbs_static_config, to_pbs_config},
14+
};
15+
use eyre::Result;
16+
use tokio::sync::RwLock;
17+
use tracing::info;
18+
use url::Url;
19+
20+
#[tokio::test]
21+
#[allow(unused_assignments)]
22+
#[tracing_test::traced_test]
23+
async fn test_auto_refresh() -> Result<()> {
24+
// This test reads the log files to verify behavior, so we can't attach a global
25+
// trace listener setup_test_env();
26+
27+
let signer = random_secret();
28+
let pubkey = signer.public_key();
29+
30+
let chain = Chain::Hoodi;
31+
let pbs_port = 3710;
32+
33+
// Start the mock SSV API server
34+
let mut next_port = pbs_port + 1;
35+
let ssv_api_port = next_port;
36+
next_port += 1;
37+
let ssv_api_url = Url::parse(&format!("http://localhost:{ssv_api_port}"))?;
38+
let mock_ssv_state = SsvMockState {
39+
validators: Arc::new(RwLock::new(vec![SSVValidator { pubkey: pubkey.clone() }])),
40+
force_timeout: Arc::new(RwLock::new(false)),
41+
};
42+
let ssv_server_handle =
43+
create_mock_ssv_server(ssv_api_port, Some(mock_ssv_state.clone())).await?;
44+
45+
// Start a mock relay to be used by the mux
46+
let default_relay = generate_mock_relay(next_port, pubkey.clone())?;
47+
let relay_id = default_relay.id.clone().to_string();
48+
let mock_state = Arc::new(MockRelayState::new(chain, signer));
49+
tokio::spawn(start_mock_relay_service(mock_state.clone(), next_port));
50+
next_port += 1;
51+
52+
// Create the registry mux
53+
let loader = MuxKeysLoader::Registry {
54+
enable_refreshing: true,
55+
node_operator_id: 1,
56+
registry: cb_common::config::NORegistry::SSV,
57+
};
58+
let muxes = PbsMuxes {
59+
muxes: vec![MuxConfig {
60+
id: relay_id.clone(),
61+
loader: Some(loader),
62+
late_in_slot_time_ms: Some(2000),
63+
relays: vec![(*default_relay.config).clone()],
64+
timeout_get_header_ms: Some(750),
65+
validator_pubkeys: vec![],
66+
}],
67+
};
68+
69+
// Set up the PBS config
70+
let mut pbs_config = get_pbs_static_config(pbs_port);
71+
pbs_config.ssv_api_url = Some(ssv_api_url.clone());
72+
pbs_config.mux_registry_refresh_interval_seconds = 1; // Refresh the mux every second
73+
let (mux_lookup, registry_muxes) = muxes.validate_and_fill(chain, &pbs_config).await?;
74+
let relays = vec![default_relay.clone()];
75+
let mut config = to_pbs_config(chain, pbs_config, relays);
76+
config.all_relays = vec![default_relay.clone()];
77+
config.mux_lookup = Some(mux_lookup);
78+
config.registry_muxes = Some(registry_muxes);
79+
80+
// Run PBS service
81+
let state = PbsState::new(config);
82+
let pbs_server = tokio::spawn(PbsService::run::<(), DefaultBuilderApi>(state));
83+
info!("Started PBS server with pubkey {pubkey}");
84+
85+
// Wait for the first refresh to complete
86+
let wait_for_refresh_time = Duration::from_secs(2);
87+
tokio::time::sleep(wait_for_refresh_time).await;
88+
89+
// Check the logs to ensure a refresh happened
90+
assert!(logs_contain(&format!("fetched 1 pubkeys for registry mux {relay_id}")));
91+
assert!(!logs_contain(&format!("fetched 2 pubkeys for registry mux {relay_id}")));
92+
assert!(!logs_contain("adding new pubkey"));
93+
94+
// Add another validator
95+
let new_secret = random_secret();
96+
let new_pubkey = new_secret.public_key();
97+
{
98+
let mut validators = mock_ssv_state.validators.write().await;
99+
validators.push(SSVValidator { pubkey: new_pubkey.clone() });
100+
info!("Added new validator {new_pubkey} to the SSV mock server");
101+
}
102+
103+
// Wait for the next refresh to complete
104+
tokio::time::sleep(wait_for_refresh_time).await;
105+
106+
// Check the logs to ensure the new pubkey was added
107+
assert!(logs_contain(&format!("adding new pubkey {new_pubkey} to mux {relay_id}")));
108+
assert!(logs_contain(&format!("fetched 2 pubkeys for registry mux {relay_id}")));
109+
110+
// Shut down the server handles
111+
pbs_server.abort();
112+
ssv_server_handle.abort();
113+
114+
Ok(())
115+
}

0 commit comments

Comments
 (0)