Skip to content

Commit ba099df

Browse files
committed
feat: don’t set up the TCP-over-WebSocket tunnels if running on the same machine
1 parent 2c1148f commit ba099df

File tree

6 files changed

+140
-13
lines changed

6 files changed

+140
-13
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ anyhow.workspace = true
6262
bech32.workspace = true
6363
cardano-serialization-lib.workspace = true
6464
ntest = "0.9.3"
65+
sysinfo.workspace = true
66+
machine-uid = "0.5"
67+
blake3 = "1"
68+
getrandom = "0.3"
6569

6670
[target.'cfg(unix)'.dependencies]
6771
nix = { version = "0.30", default-features = false, features = ["signal"] }
@@ -126,6 +130,7 @@ reqwest = { version = "0.12.12", features = ["blocking"] }
126130
rstest = "0.24.0"
127131
serde = { version = "1.0.219", features = ["derive"] }
128132
serde_json = "1.0.140"
133+
sysinfo = "0.33.1"
129134
thiserror = "2.0.12"
130135
tokio = { version = "1.43.0", features = [
131136
"rt",

crates/node/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ serde_json.workspace = true
1818
tokio.workspace = true
1919
pallas-network.workspace = true
2020
deadpool = "0.12.1"
21-
sysinfo = "0.33.1"
21+
sysinfo.workspace = true
2222
num_cpus = "1"
2323
metrics.workspace = true
2424
pallas-hardano.workspace = true

src/hydra.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ pub struct HydraController {
1919

2020
#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq, Eq, Clone)]
2121
pub struct KeyExchangeRequest {
22+
pub machine_id: String,
2223
pub platform_cardano_vkey: serde_json::Value,
2324
pub platform_hydra_vkey: serde_json::Value,
2425
pub accepted_platform_h2h_port: Option<u16>,
2526
}
2627

2728
#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq, Eq, Clone)]
2829
pub struct KeyExchangeResponse {
30+
pub machine_id: String,
2931
pub gateway_cardano_vkey: serde_json::Value,
3032
pub gateway_hydra_vkey: serde_json::Value,
3133
pub hydra_scripts_tx_id: String,
@@ -255,6 +257,7 @@ impl State {
255257

256258
self.kex_requests
257259
.send(KeyExchangeRequest {
260+
machine_id: verifications::hashed_machine_id(),
258261
platform_cardano_vkey: self.platform_cardano_vkey.clone(),
259262
platform_hydra_vkey: verifications::read_json_file(
260263
&self.config_dir.join("hydra.vk"),
@@ -291,6 +294,7 @@ impl State {
291294
} else {
292295
self.kex_requests
293296
.send(KeyExchangeRequest {
297+
machine_id: verifications::hashed_machine_id(),
294298
platform_cardano_vkey: self.platform_cardano_vkey.clone(),
295299
platform_hydra_vkey: verifications::read_json_file(
296300
&self.config_dir.join("hydra.vk"),

src/hydra/verifications.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,3 +383,29 @@ pub fn sigterm(pid: u32) -> Result<()> {
383383
pub fn sigterm(_pid: u32) -> Result<()> {
384384
unreachable!()
385385
}
386+
387+
/// We use it for `localhost` tests, to detect if the Gateway and Platform are
388+
/// running on the same host. Then we cannot set up a
389+
/// `[crate::hydra::tunnel2::Tunnel]`, because the ports are already taken.
390+
pub fn hashed_machine_id() -> String {
391+
const MACHINE_ID_NAMESPACE: &str = "blockfrost.machine-id.v1";
392+
393+
let mut hasher = blake3::Hasher::new();
394+
hasher.update(MACHINE_ID_NAMESPACE.as_bytes());
395+
hasher.update(b":");
396+
397+
match machine_uid::get() {
398+
Ok(id) => {
399+
hasher.update(id.as_bytes());
400+
},
401+
Err(e) => {
402+
tracing::warn!(error = ?e, "machine_uid::get() failed; falling back to random bytes");
403+
let mut fallback = [0u8; 32];
404+
getrandom::fill(&mut fallback)
405+
.expect("getrandom::fill shouldn’t fail in normal circumstances");
406+
hasher.update(&fallback);
407+
},
408+
}
409+
410+
hasher.finalize().to_hex().to_string()
411+
}

src/load_balancer.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,17 +253,19 @@ mod event_loop {
253253

254254
LBEvent::NewLoadBalancerMessage(LoadBalancerMessage::HydraKExResponse(resp)) => {
255255
if let Some(hydra_kex) = &hydra_kex {
256-
if resp.kex_done {
256+
// Only start the TCP-over-WebSocket tunnels if we’re running
257+
// on different machines:
258+
if resp.machine_id != hydra::verifications::hashed_machine_id() {
257259
let (tunnel_ctl, mut tunnel_rx) = hydra::tunnel2::Tunnel::new(
258260
hydra::tunnel2::TunnelConfig {
259-
expose_port: resp.gateway_h2h_port,
261+
expose_port: resp.proposed_platform_h2h_port,
260262
id_prefix_bit: true,
261263
..(hydra::tunnel2::TunnelConfig::default())
262264
},
263265
tunnel_cancellation.clone(),
264266
);
265267

266-
tunnel_ctl.spawn_listener(resp.proposed_platform_h2h_port).await.expect("FIXME: this really shouldn’t fail, unless we hit the TOCTOU race condition…");
268+
tunnel_ctl.spawn_listener(resp.gateway_h2h_port).await.expect("FIXME: this really shouldn’t fail, unless we hit the TOCTOU race condition…");
267269

268270
let socket_tx_ = socket_tx.clone();
269271
let config_ = config.clone();

0 commit comments

Comments
 (0)