use axum::{ extract::{Path, State}, routing::get, Json, Router, }; use crate::errors::AppResult; use crate::state::AppState; /// Convert a fingerprint hex string to a stable i64 ID (for Telegram compatibility). /// Uses first 8 bytes of the fingerprint as a positive i64. pub fn fp_to_numeric_id(fp: &str) -> i64 { let clean: String = fp.chars().filter(|c| c.is_ascii_hexdigit()).take(16).collect(); let bytes = hex::decode(&clean).unwrap_or_default(); if bytes.len() >= 8 { let mut arr = [0u8; 8]; arr.copy_from_slice(&bytes[..8]); i64::from_be_bytes(arr) & 0x7FFFFFFFFFFFFFFF // ensure positive } else { 0 } } pub fn routes() -> Router { Router::new().route("/resolve/:address", get(resolve_address)) } /// Resolve an address to a fingerprint. /// /// Accepts: ETH address (`0x...`), alias (`@name`), or raw fingerprint. async fn resolve_address( State(state): State, Path(address): Path, ) -> AppResult> { let addr = address.trim().to_lowercase(); // ETH address: 0x... if addr.starts_with("0x") { if let Some(fp_bytes) = state.db.eth_addresses.get(addr.as_bytes())? { let fp = String::from_utf8_lossy(&fp_bytes).to_string(); return Ok(Json(serde_json::json!({ "address": address, "fingerprint": fp, "numeric_id": fp_to_numeric_id(&fp), "type": "eth", }))); } // Try federation if let Some(ref federation) = state.federation { let url = format!("{}/v1/resolve/{}", federation.config.peer.url, addr); if let Ok(resp) = federation.client.get(&url).send().await { if resp.status().is_success() { if let Ok(data) = resp.json::().await { if let Some(fp) = data.get("fingerprint").and_then(|v| v.as_str()) { return Ok(Json(serde_json::json!({ "address": address, "fingerprint": fp, "numeric_id": fp_to_numeric_id(fp), "type": "eth", "federated": true, }))); } } } } } return Ok(Json(serde_json::json!({ "error": "address not found" }))); } // Alias: @name if addr.starts_with('@') { let alias = &addr[1..]; // Try local alias resolution let alias_key = format!("a:{}", alias); if let Some(fp_bytes) = state.db.aliases.get(alias_key.as_bytes())? { let fp = String::from_utf8_lossy(&fp_bytes).to_string(); return Ok(Json(serde_json::json!({ "address": address, "fingerprint": fp, "numeric_id": fp_to_numeric_id(&fp), "type": "alias", }))); } // Try federation if let Some(ref federation) = state.federation { if let Some(fp) = federation.resolve_remote_alias(alias).await { return Ok(Json(serde_json::json!({ "address": address, "fingerprint": fp, "numeric_id": fp_to_numeric_id(&fp), "type": "alias", "federated": true, }))); } } return Ok(Json(serde_json::json!({ "error": "alias not found" }))); } // Raw fingerprint: just echo back with optional reverse ETH lookup let fp = addr .chars() .filter(|c| c.is_ascii_hexdigit()) .collect::(); if fp.len() == 32 { let rev_key = format!("rev:{}", fp); let eth = state .db .eth_addresses .get(rev_key.as_bytes())? .map(|v| String::from_utf8_lossy(&v).to_string()); return Ok(Json(serde_json::json!({ "address": address, "fingerprint": fp, "numeric_id": fp_to_numeric_id(&fp), "eth_address": eth, "type": "fingerprint", }))); } Ok(Json(serde_json::json!({ "error": "unrecognized address format" }))) }