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
16 changes: 1 addition & 15 deletions hyperdrive/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1162,21 +1162,7 @@ async fn login_with_password(
None => (0, tcp_networking.1),
};

// Try each provider until one succeeds
let mut last_error = None;
for provider in providers.iter() {
match register::assign_routing(&mut our, provider, ws_port, tcp_port).await {
Ok(()) => {
last_error = None;
break;
}
Err(e) => {
last_error = Some(e);
continue;
}
}
}
if let Some(e) = last_error {
if let Err(e) = register::assign_routing(&mut our, &providers, ws_port, tcp_port).await {
panic!("information used to boot does not match information onchain: {e}");
}

Expand Down
174 changes: 108 additions & 66 deletions hyperdrive/src/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ const DEFAULT_RPC_URLS: &[&str] = &[
"wss://base.gateway.tenderly.co",
];

/// Check if a hypermap entry is empty (stale provider data).
/// Returns true if all fields are zero/empty.
fn is_hypermap_entry_empty(entry: &getReturn) -> bool {
entry.tba == EthAddress::ZERO && entry.owner == EthAddress::ZERO && entry.data.is_empty()
}

type RegistrationSender = mpsc::Sender<(Identity, Keyfile, Vec<u8>, Vec<String>, Vec<String>)>;

/// Serve the registration page and receive POSTs and PUTs from it
Expand Down Expand Up @@ -470,18 +476,18 @@ async fn handle_boot(
)
.into_response());
};
let owner = node_info.owner;

// If owner is zero address, the provider likely has stale data.
// If all fields are zero/empty, the provider likely has stale data.
// Try the next provider.
if owner == EthAddress::ZERO {
if is_hypermap_entry_empty(&node_info) {
println!(
"Provider {} returned zero address for owner, trying next provider...\r",
"Provider {} returned empty hypermap entry, trying next provider...\r",
provider_index
);
continue;
}

let owner = node_info.owner;

let chain_id: u64 = 8453; // base

let domain = eip712_domain! {
Expand Down Expand Up @@ -619,10 +625,9 @@ async fn handle_import_keyfile(
}
};

// Use the first provider for assign_routing
if let Err(e) = assign_routing(
&mut our,
&providers[0],
&providers,
ws_networking_port,
tcp_networking_port,
)
Expand Down Expand Up @@ -717,10 +722,9 @@ async fn handle_login(
Vec::new()
};

// Use the first provider for assign_routing
if let Err(e) = assign_routing(
&mut our,
&providers[0],
&providers,
ws_networking_port,
tcp_networking_port,
)
Expand All @@ -745,7 +749,7 @@ async fn handle_login(

pub async fn assign_routing(
our: &mut Identity,
provider: &RootProvider<PubSubFrontend>,
providers: &[RootProvider<PubSubFrontend>],
ws_networking_port: (u16, bool),
tcp_networking_port: (u16, bool),
) -> anyhow::Result<()> {
Expand Down Expand Up @@ -788,72 +792,110 @@ pub async fn assign_routing(
let tx_input = TransactionInput::new(Bytes::from(multicall_call));
let tx = TransactionRequest::default().to(multicall).input(tx_input);

let multicall_return = match provider.call(&tx).await {
Ok(multicall_return) => multicall_return,
Err(e) => {
return Err(anyhow::anyhow!(
"Failed to fetch node IP data from hypermap: {e}"
))
}
};
let mut last_error = None;

for (provider_index, provider) in providers.iter().enumerate() {
let multicall_return = match provider.call(&tx).await {
Ok(multicall_return) => multicall_return,
Err(e) => {
println!(
"Provider {} failed to fetch node IP data, trying next provider...\r",
provider_index
);
last_error = Some(anyhow::anyhow!(
"Failed to fetch node IP data from hypermap: {e}"
));
continue;
}
};

let Ok(results) = aggregateCall::abi_decode_returns(&multicall_return, false) else {
return Err(anyhow::anyhow!("Failed to decode hypermap multicall data"));
};
let Ok(results) = aggregateCall::abi_decode_returns(&multicall_return, false) else {
println!(
"Provider {} returned invalid multicall data, trying next provider...\r",
provider_index
);
last_error = Some(anyhow::anyhow!("Failed to decode hypermap multicall data"));
continue;
};

let Ok(netkey) = getCall::abi_decode_returns(&results.returnData[0], false) else {
println!(
"Provider {} returned invalid netkey data, trying next provider...\r",
provider_index
);
last_error = Some(anyhow::anyhow!("Failed to decode netkey data"));
continue;
};

let netkey = getCall::abi_decode_returns(&results.returnData[0], false)?;
let ws = getCall::abi_decode_returns(&results.returnData[1], false)?;
let tcp = getCall::abi_decode_returns(&results.returnData[2], false)?;
let ip = getCall::abi_decode_returns(&results.returnData[3], false)?;
// If all fields are zero/empty, the provider likely has stale data.
// Try the next provider.
if is_hypermap_entry_empty(&netkey) {
println!(
"Provider {} returned empty netkey entry, trying next provider...\r",
provider_index
);
last_error = Some(anyhow::anyhow!(
"Provider returned empty hypermap entry (stale data)"
));
continue;
}

let ip = keygen::bytes_to_ip(&ip.data);
let ws = keygen::bytes_to_port(&ws.data);
let tcp = keygen::bytes_to_port(&tcp.data);
if netkey.data.to_string() != our.networking_key {
return Err(anyhow::anyhow!(
"Networking key from PKI ({}) does not match our saved networking key ({})",
netkey.data.to_string(),
our.networking_key
));
}

if netkey.data.to_string() != our.networking_key {
return Err(anyhow::anyhow!(
"Networking key from PKI ({}) does not match our saved networking key ({})",
netkey.data.to_string(),
our.networking_key
));
}
// Successfully validated netkey, now decode the rest
let ws = getCall::abi_decode_returns(&results.returnData[1], false)?;
let tcp = getCall::abi_decode_returns(&results.returnData[2], false)?;
let ip = getCall::abi_decode_returns(&results.returnData[3], false)?;

if !our.is_direct() {
// indirect node
return Ok(());
}
let ip = keygen::bytes_to_ip(&ip.data);
let ws = keygen::bytes_to_port(&ws.data);
let tcp = keygen::bytes_to_port(&tcp.data);

if ip.is_ok() && (ws.is_ok() || tcp.is_ok()) {
// direct node
let mut ports = std::collections::BTreeMap::new();
if let Ok(ws) = ws {
if ws_networking_port.1 && ws != ws_networking_port.0 {
return Err(anyhow::anyhow!(
"Binary used --ws-port flag to set port to {}, but node is using port {} onchain.",
ws_networking_port.0,
ws
));
}
ports.insert("ws".to_string(), ws);
if !our.is_direct() {
// indirect node
return Ok(());
}
if let Ok(tcp) = tcp {
if tcp_networking_port.1 && tcp != tcp_networking_port.0 {
return Err(anyhow::anyhow!(
"Binary used --tcp-port flag to set port to {}, but node is using port {} onchain.",
tcp_networking_port.0,
tcp
));

if ip.is_ok() && (ws.is_ok() || tcp.is_ok()) {
// direct node
let mut ports = std::collections::BTreeMap::new();
if let Ok(ws) = ws {
if ws_networking_port.1 && ws != ws_networking_port.0 {
return Err(anyhow::anyhow!(
"Binary used --ws-port flag to set port to {}, but node is using port {} onchain.",
ws_networking_port.0,
ws
));
}
ports.insert("ws".to_string(), ws);
}
if let Ok(tcp) = tcp {
if tcp_networking_port.1 && tcp != tcp_networking_port.0 {
return Err(anyhow::anyhow!(
"Binary used --tcp-port flag to set port to {}, but node is using port {} onchain.",
tcp_networking_port.0,
tcp
));
}
ports.insert("tcp".to_string(), tcp);
}
ports.insert("tcp".to_string(), tcp);
our.routing = NodeRouting::Direct {
ip: ip.unwrap().to_string(),
ports,
};
}
our.routing = NodeRouting::Direct {
ip: ip.unwrap().to_string(),
ports,
};
} else {
// indirect node
// Success - return early
return Ok(());
}
Ok(())

// All providers failed
Err(last_error.unwrap_or_else(|| anyhow::anyhow!("No providers available")))
}

async fn success_response(
Expand Down