Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ hyperdrive/packages/settings/pkg/ui/*
hyperdrive/src/register-ui/build/
hyperdrive/src/register-ui/dist/
hyperdrive/packages/docs/pkg/ui
/.idea
184 changes: 152 additions & 32 deletions hyperdrive/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use lib::types::core::{
NetworkErrorSender, NodeRouting, PrintReceiver, PrintSender, ProcessId, ProcessVerbosity,
Request, KERNEL_PROCESS_ID,
};
use lib::types::eth::RpcUrlConfigInput;
#[cfg(feature = "simulation-mode")]
use ring::{rand::SystemRandom, signature, signature::KeyPair};
use std::collections::HashMap;
Expand Down Expand Up @@ -88,6 +87,12 @@ async fn main() {
let rpc_config = matches.get_one::<String>("rpc-config").map(|p| {
std::fs::canonicalize(&p).expect(&format!("specified rpc-config path {p} not found"))
});

// Prevent using both --rpc and --rpc-config flags simultaneously
if rpc.is_some() && rpc_config.is_some() {
panic!("Cannot use both --rpc and --rpc-config flags simultaneously. Please use either --rpc for a single RPC URL or --rpc-config for multiple URLs and/or advanced configs (e.g., auth), but not both.");
}

let password = matches.get_one::<String>("password");

// logging mode is toggled at runtime by CTRL+L
Expand Down Expand Up @@ -129,38 +134,114 @@ async fn main() {
is_eth_provider_config_updated = true;
serde_json::from_str(DEFAULT_ETH_PROVIDERS).unwrap()
};

// TODO: Remove debug logging before merging - before modification
eprintln!(
"[DEBUG-AUTH] eth_provider_config before --rpc/--rpc-config processing: {:#?}",
eth_provider_config
);

if let Some(rpc) = rpc {
eth_provider_config.insert(
0,
lib::eth::ProviderConfig {
chain_id: CHAIN_ID,
trusted: true,
provider: lib::eth::NodeOrRpcUrl::RpcUrl {
url: rpc.to_string(),
auth: None,
},
// TODO: Remove debug logging before merging
eprintln!("[DEBUG-AUTH] Processing --rpc flag with URL: {}", rpc);

let new_provider = lib::eth::ProviderConfig {
chain_id: CHAIN_ID,
trusted: true,
provider: lib::eth::NodeOrRpcUrl::RpcUrl {
url: rpc.to_string(),
auth: None,
},
);
};

add_provider_to_config(&mut eth_provider_config, new_provider);
is_eth_provider_config_updated = true;

// TODO: Remove debug logging before merging
eprintln!("[DEBUG-AUTH] Added --rpc provider (with deduplication)");
}
if let Some(rpc_config) = rpc_config {
let rpc_config = tokio::fs::read_to_string(rpc_config)
.await
.expect("cant read rpc-config");
let rpc_config: Vec<RpcUrlConfigInput> =
serde_json::from_str(&rpc_config).expect("rpc-config had invalid format");
for RpcUrlConfigInput { url, auth } in rpc_config {
eth_provider_config.insert(
0,
lib::eth::ProviderConfig {
chain_id: CHAIN_ID,
trusted: true,
provider: lib::eth::NodeOrRpcUrl::RpcUrl { url, auth },
},
);
// TODO: Remove debug logging before merging
eprintln!(
"[DEBUG-AUTH] Processing --rpc-config flag with file: {}",
rpc_config.display()
);

match std::fs::read_to_string(&rpc_config) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

match is a great construct, but style-wise it should be used sparingly when other control flow is available because of the double-indentation it causes. Especially when there are two main outcomes we care about, we have two good alternatives:

alternative 1:

if let Ok(contents) = std::fs::read_to_string(&rpc_config) {
    ...
}
...

or (if we care about the non-Ok case, but don't need to unpack it)

if let Ok(...) ... {
} else {
}
...

alternative 2 (my preference when it works (e.g. when you want to exit early in a non-Ok case):

let Ok(contents) = std::fs::read_to_string(&rpc_config) else {
    ...
};
...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, and because there were nested matches it avoided the dreaded quadruple indentation.

Ok(contents) => {
match serde_json::from_str::<Vec<lib::eth::RpcUrlConfigInput>>(&contents) {
Ok(rpc_configs) => {
// TODO: Remove debug logging before merging
eprintln!(
"[DEBUG-AUTH] Loaded {} providers from config file",
rpc_configs.len()
);

// Store the length before consuming the vector
let total_configs = rpc_configs.len();

// Process in reverse order so the first entry in the file becomes highest priority
for (reverse_index, rpc_url_config) in
rpc_configs.into_iter().rev().enumerate()
{
let original_index = total_configs - 1 - reverse_index;

// TODO: Remove debug logging before merging
eprintln!("[DEBUG-AUTH] Processing config provider {} (original position {}): {}",
reverse_index + 1, original_index + 1, rpc_url_config.url);
if let Some(ref auth) = rpc_url_config.auth {
match auth {
lib::eth::Authorization::Basic(creds) => {
eprintln!("[DEBUG-AUTH] Config provider has Basic auth (length: {})", creds.len());
}
lib::eth::Authorization::Bearer(token) => {
eprintln!("[DEBUG-AUTH] Config provider has Bearer auth (length: {})", token.len());
}
lib::eth::Authorization::Raw(raw) => {
eprintln!("[DEBUG-AUTH] Config provider has Raw auth (length: {})", raw.len());
}
}
} else {
eprintln!("[DEBUG-AUTH] Config provider has no auth");
}

let new_provider = lib::eth::ProviderConfig {
chain_id: CHAIN_ID,
trusted: true,
provider: lib::eth::NodeOrRpcUrl::RpcUrl {
url: rpc_url_config.url,
auth: rpc_url_config.auth,
},
};

add_provider_to_config(&mut eth_provider_config, new_provider);
}
is_eth_provider_config_updated = true;

// TODO: Remove debug logging before merging
eprintln!("[DEBUG-AUTH] Added all --rpc-config providers (with deduplication, order preserved)");
}
Err(e) => {
// TODO: Remove debug logging before merging
eprintln!("[DEBUG-AUTH] Failed to parse RPC config file: {}", e);
eprintln!("Failed to parse RPC config file: {e}");
}
}
}
Err(e) => {
// TODO: Remove debug logging before merging
eprintln!("[DEBUG-AUTH] Failed to read RPC config file: {}", e);
eprintln!("Failed to read RPC config file: {e}");
}
}
is_eth_provider_config_updated = true;
}

// TODO: Remove debug logging before merging - after modification
eprintln!(
"[DEBUG-AUTH] eth_provider_config after --rpc/--rpc-config processing: {:#?}",
eth_provider_config
);

if is_eth_provider_config_updated {
// save the new provider config
tokio::fs::write(
Expand Down Expand Up @@ -274,7 +355,7 @@ async fn main() {
(ws_tcp_handle, ws_flag_used),
(tcp_tcp_handle, tcp_flag_used),
http_server_port,
rpc.cloned(),
eth_provider_config.clone(),
detached,
)
.await
Expand All @@ -285,7 +366,7 @@ async fn main() {
our_ip.to_string(),
(ws_tcp_handle, ws_flag_used),
(tcp_tcp_handle, tcp_flag_used),
rpc.cloned(),
eth_provider_config.clone(),
password,
)
.await
Expand Down Expand Up @@ -842,7 +923,7 @@ async fn serve_register_fe(
ws_networking: (Option<tokio::net::TcpListener>, bool),
tcp_networking: (Option<tokio::net::TcpListener>, bool),
http_server_port: u16,
maybe_rpc: Option<String>,
eth_provider_config: lib::eth::SavedConfigs,
detached: bool,
) -> (Identity, Vec<u8>, Keyfile) {
let (kill_tx, kill_rx) = tokio::sync::oneshot::channel::<bool>();
Expand All @@ -861,7 +942,7 @@ async fn serve_register_fe(
(tcp_networking.0.as_ref(), tcp_networking.1),
http_server_port,
disk_keyfile,
maybe_rpc,
eth_provider_config,
detached) => {
panic!("registration failed")
}
Expand All @@ -888,7 +969,7 @@ async fn login_with_password(
our_ip: String,
ws_networking: (Option<tokio::net::TcpListener>, bool),
tcp_networking: (Option<tokio::net::TcpListener>, bool),
maybe_rpc: Option<String>,
eth_provider_config: lib::eth::SavedConfigs,
password: &str,
) -> (Identity, Vec<u8>, Keyfile) {
use argon2::Argon2;
Expand Down Expand Up @@ -962,7 +1043,7 @@ async fn login_with_password(
},
};

let provider = Arc::new(register::connect_to_provider(maybe_rpc).await);
let provider = Arc::new(register::connect_to_provider_from_config(&eth_provider_config).await);

register::assign_routing(
&mut our,
Expand All @@ -986,6 +1067,45 @@ async fn login_with_password(
(our, disk_keyfile, k)
}

/// Add a provider config with deduplication logic (same as runtime system)
fn add_provider_to_config(
eth_provider_config: &mut lib::eth::SavedConfigs,
new_provider: lib::eth::ProviderConfig,
) {
match &new_provider.provider {
lib::eth::NodeOrRpcUrl::RpcUrl { url, .. } => {
// Remove any existing provider with this URL
eth_provider_config.0.retain(|config| {
if let lib::eth::NodeOrRpcUrl::RpcUrl {
url: existing_url, ..
} = &config.provider
{
existing_url != url
} else {
true
}
});
}
lib::eth::NodeOrRpcUrl::Node { hns_update, .. } => {
// Remove any existing provider with this node name
eth_provider_config.0.retain(|config| {
if let lib::eth::NodeOrRpcUrl::Node {
hns_update: existing_update,
..
} = &config.provider
{
existing_update.name != hns_update.name
} else {
true
}
});
}
}

// Insert the new provider at the front (position 0)
eth_provider_config.0.insert(0, new_provider);
}

fn make_remote_link(url: &str, text: &str) -> String {
format!("\x1B]8;;{}\x1B\\{}\x1B]8;;\x1B\\", url, text)
}
Loading