From 83bb589ea63173029b36a88f87f6e113ceb9f6c7 Mon Sep 17 00:00:00 2001 From: archipelago Date: Wed, 17 Jun 2026 19:50:46 -0400 Subject: [PATCH] style: cargo fmt for v1.7.99-alpha release gate Co-Authored-By: Claude Opus 4.8 (1M context) --- core/archipelago/src/api/handler/content.rs | 10 +- core/archipelago/src/api/rpc/content.rs | 116 ++++++++++-------- .../src/api/rpc/federation/handlers.rs | 7 +- core/archipelago/src/api/rpc/fedimint.rs | 27 ++-- core/archipelago/src/api/rpc/lnd/wallet.rs | 11 +- .../archipelago/src/api/rpc/mesh/assistant.rs | 23 ++-- .../archipelago/src/api/rpc/mesh/messaging.rs | 5 +- core/archipelago/src/api/rpc/mesh/status.rs | 10 +- .../archipelago/src/api/rpc/package/stacks.rs | 16 +-- core/archipelago/src/api/rpc/seed_rpc.rs | 3 +- core/archipelago/src/ceremony.rs | 9 +- core/archipelago/src/container/app_catalog.rs | 10 +- .../src/container/prod_orchestrator.rs | 28 ++++- core/archipelago/src/mesh/listener/assist.rs | 67 ++++++---- core/archipelago/src/mesh/listener/frames.rs | 4 +- core/archipelago/src/mesh/listener/session.rs | 5 +- core/archipelago/src/mesh/scheduler.rs | 6 +- core/archipelago/src/port_allocator.rs | 2 +- core/archipelago/src/server.rs | 6 +- core/archipelago/src/swarm/iroh_provider.rs | 3 +- core/archipelago/src/swarm/mod.rs | 47 ++++--- core/archipelago/src/swarm/paid_alpn.rs | 16 ++- core/archipelago/src/swarm/payment.rs | 3 +- core/archipelago/src/swarm/seed_advert.rs | 22 +++- core/archipelago/src/trust/did.rs | 4 +- core/archipelago/src/trust/signed_doc.rs | 13 +- core/archipelago/src/wallet/ecash.rs | 107 ++++++++++++---- .../archipelago/src/wallet/fedimint_client.rs | 6 +- 28 files changed, 384 insertions(+), 202 deletions(-) diff --git a/core/archipelago/src/api/handler/content.rs b/core/archipelago/src/api/handler/content.rs index 4ab0f11c..2074a68e 100644 --- a/core/archipelago/src/api/handler/content.rs +++ b/core/archipelago/src/api/handler/content.rs @@ -159,10 +159,7 @@ impl ApiHandler { /// Seller side (#46): mint a Lightning invoice for a paid catalog item so a /// buyer can pay from any external wallet. Path: GET /content/{id}/invoice. /// Records a pending entitlement keyed by the invoice's payment hash. - pub(super) async fn handle_content_invoice( - &self, - path: &str, - ) -> Result> { + pub(super) async fn handle_content_invoice(&self, path: &str) -> Result> { let content_id = path .strip_prefix("/content/") .and_then(|s| s.strip_suffix("/invoice")) @@ -295,10 +292,7 @@ impl ApiHandler { /// Seller side (#46): issue a fresh on-chain address for a paid catalog item /// so a buyer can pay on-chain. Path: GET /content/{id}/onchain. Records a /// pending entitlement keyed by the address; price doubles as expected amount. - pub(super) async fn handle_content_onchain( - &self, - path: &str, - ) -> Result> { + pub(super) async fn handle_content_onchain(&self, path: &str) -> Result> { let content_id = path .strip_prefix("/content/") .and_then(|s| s.strip_suffix("/onchain")) diff --git a/core/archipelago/src/api/rpc/content.rs b/core/archipelago/src/api/rpc/content.rs index 903b2b86..ea66666c 100644 --- a/core/archipelago/src/api/rpc/content.rs +++ b/core/archipelago/src/api/rpc/content.rs @@ -382,23 +382,26 @@ impl RpcHandler { // Surface a real reason instead of the generic sanitized error (#30): // the dial already tries FIPS/mesh then falls back to Tor, so a failure // here means the peer is genuinely unreachable on both transports. - let (response, transport) = - match crate::fips::dial::PeerRequest::new(fips_npub.as_deref(), onion, &path) - .service(crate::settings::transport::PeerService::PeerFiles) - .header("X-Federation-DID", local_did) - .header("X-Payment-Token", token_str) - .timeout(std::time::Duration::from_secs(900)) - .send_get() - .await - { - Ok(v) => v, - Err(e) => { - tracing::warn!("paid peer download dial failed for {}: {:#}", onion, e); - return Ok(serde_json::json!({ - "error": "Could not reach the peer over mesh or Tor — it may be offline. Please try again." - })); - } - }; + let (response, transport) = match crate::fips::dial::PeerRequest::new( + fips_npub.as_deref(), + onion, + &path, + ) + .service(crate::settings::transport::PeerService::PeerFiles) + .header("X-Federation-DID", local_did) + .header("X-Payment-Token", token_str) + .timeout(std::time::Duration::from_secs(900)) + .send_get() + .await + { + Ok(v) => v, + Err(e) => { + tracing::warn!("paid peer download dial failed for {}: {:#}", onion, e); + return Ok(serde_json::json!({ + "error": "Could not reach the peer over mesh or Tor — it may be offline. Please try again." + })); + } + }; // Record which transport actually reached the peer (B14). let _ = crate::federation::record_peer_transport( &self.config.data_dir, @@ -576,23 +579,26 @@ impl RpcHandler { let fips_npub = crate::federation::fips_npub_for_onion(&self.config.data_dir, onion).await; let path = format!("/content/{}", content_id); - let (response, transport) = - match crate::fips::dial::PeerRequest::new(fips_npub.as_deref(), onion, &path) - .service(crate::settings::transport::PeerService::PeerFiles) - .header("X-Federation-DID", local_did) - .header("X-Invoice-Hash", payment_hash.to_string()) - .timeout(std::time::Duration::from_secs(900)) - .send_get() - .await - { - Ok(v) => v, - Err(e) => { - tracing::warn!("invoice download dial failed for {}: {:#}", onion, e); - return Ok(serde_json::json!({ - "error": "Could not reach the peer over mesh or Tor — it may be offline. Please try again." - })); - } - }; + let (response, transport) = match crate::fips::dial::PeerRequest::new( + fips_npub.as_deref(), + onion, + &path, + ) + .service(crate::settings::transport::PeerService::PeerFiles) + .header("X-Federation-DID", local_did) + .header("X-Invoice-Hash", payment_hash.to_string()) + .timeout(std::time::Duration::from_secs(900)) + .send_get() + .await + { + Ok(v) => v, + Err(e) => { + tracing::warn!("invoice download dial failed for {}: {:#}", onion, e); + return Ok(serde_json::json!({ + "error": "Could not reach the peer over mesh or Tor — it may be offline. Please try again." + })); + } + }; let _ = crate::federation::record_peer_transport( &self.config.data_dir, None, @@ -697,7 +703,10 @@ impl RpcHandler { return Err(anyhow::anyhow!("Invalid v3 onion address")); } // Bitcoin addresses are alphanumeric; keep strictly so for safe path use. - if address.is_empty() || address.len() > 100 || !address.chars().all(|c| c.is_ascii_alphanumeric()) { + if address.is_empty() + || address.len() > 100 + || !address.chars().all(|c| c.is_ascii_alphanumeric()) + { return Err(anyhow::anyhow!("Invalid address")); } @@ -754,23 +763,26 @@ impl RpcHandler { let fips_npub = crate::federation::fips_npub_for_onion(&self.config.data_dir, onion).await; let path = format!("/content/{}", content_id); - let (response, transport) = - match crate::fips::dial::PeerRequest::new(fips_npub.as_deref(), onion, &path) - .service(crate::settings::transport::PeerService::PeerFiles) - .header("X-Federation-DID", local_did) - .header("X-Onchain-Address", address.to_string()) - .timeout(std::time::Duration::from_secs(900)) - .send_get() - .await - { - Ok(v) => v, - Err(e) => { - tracing::warn!("onchain download dial failed for {}: {:#}", onion, e); - return Ok(serde_json::json!({ - "error": "Could not reach the peer over mesh or Tor — it may be offline. Please try again." - })); - } - }; + let (response, transport) = match crate::fips::dial::PeerRequest::new( + fips_npub.as_deref(), + onion, + &path, + ) + .service(crate::settings::transport::PeerService::PeerFiles) + .header("X-Federation-DID", local_did) + .header("X-Onchain-Address", address.to_string()) + .timeout(std::time::Duration::from_secs(900)) + .send_get() + .await + { + Ok(v) => v, + Err(e) => { + tracing::warn!("onchain download dial failed for {}: {:#}", onion, e); + return Ok(serde_json::json!({ + "error": "Could not reach the peer over mesh or Tor — it may be offline. Please try again." + })); + } + }; let _ = crate::federation::record_peer_transport( &self.config.data_dir, None, diff --git a/core/archipelago/src/api/rpc/federation/handlers.rs b/core/archipelago/src/api/rpc/federation/handlers.rs index 5a63e9bf..3088e8ac 100644 --- a/core/archipelago/src/api/rpc/federation/handlers.rs +++ b/core/archipelago/src/api/rpc/federation/handlers.rs @@ -268,12 +268,7 @@ impl RpcHandler { let removed_pubkey = federation::load_nodes(&self.config.data_dir) .await .ok() - .and_then(|nodes| { - nodes - .into_iter() - .find(|n| n.did == did) - .map(|n| n.pubkey) - }); + .and_then(|nodes| nodes.into_iter().find(|n| n.did == did).map(|n| n.pubkey)); let nodes = federation::remove_node(&self.config.data_dir, did).await?; info!(did = %did, "Removed node from federation"); diff --git a/core/archipelago/src/api/rpc/fedimint.rs b/core/archipelago/src/api/rpc/fedimint.rs index 1be5a2a7..6e11dcec 100644 --- a/core/archipelago/src/api/rpc/fedimint.rs +++ b/core/archipelago/src/api/rpc/fedimint.rs @@ -69,20 +69,23 @@ impl RpcHandler { let federation_id = client.join(invite_code).await?; // Try to label it from the federation meta (best-effort). - let name = client - .info() - .await - .ok() - .and_then(|i| { - i.get(&federation_id) - .and_then(|e| e.get("meta")) - .and_then(|m| m.get("federation_name").or_else(|| m.get("federation_expiry_timestamp"))) - .and_then(|v| v.as_str()) - .map(|s| s.to_string()) - }); + let name = client.info().await.ok().and_then(|i| { + i.get(&federation_id) + .and_then(|e| e.get("meta")) + .and_then(|m| { + m.get("federation_name") + .or_else(|| m.get("federation_expiry_timestamp")) + }) + .and_then(|v| v.as_str()) + .map(|s| s.to_string()) + }); let mut reg = fedimint_client::load_registry(&self.config.data_dir).await?; - if !reg.federations.iter().any(|f| f.federation_id == federation_id) { + if !reg + .federations + .iter() + .any(|f| f.federation_id == federation_id) + { reg.federations.push(JoinedFederation { federation_id: federation_id.clone(), name, diff --git a/core/archipelago/src/api/rpc/lnd/wallet.rs b/core/archipelago/src/api/rpc/lnd/wallet.rs index 6fed4286..b479467c 100644 --- a/core/archipelago/src/api/rpc/lnd/wallet.rs +++ b/core/archipelago/src/api/rpc/lnd/wallet.rs @@ -205,11 +205,7 @@ impl RpcHandler { let payment_hash_hex = body .get("r_hash") .and_then(|v| v.as_str()) - .and_then(|b64| { - base64::engine::general_purpose::STANDARD - .decode(b64) - .ok() - }) + .and_then(|b64| base64::engine::general_purpose::STANDARD.decode(b64).ok()) .map(hex::encode) .unwrap_or_default(); @@ -317,9 +313,8 @@ impl RpcHandler { .get("output_details") .and_then(|v| v.as_array()) .map(|arr| { - arr.iter().any(|o| { - o.get("address").and_then(|a| a.as_str()) == Some(address) - }) + arr.iter() + .any(|o| o.get("address").and_then(|a| a.as_str()) == Some(address)) }) .unwrap_or(false); if pays_addr { diff --git a/core/archipelago/src/api/rpc/mesh/assistant.rs b/core/archipelago/src/api/rpc/mesh/assistant.rs index f9f3e72d..0995298d 100644 --- a/core/archipelago/src/api/rpc/mesh/assistant.rs +++ b/core/archipelago/src/api/rpc/mesh/assistant.rs @@ -23,11 +23,10 @@ impl RpcHandler { }; let (ollama_detected, models) = detect_ollama().await; - let claude_available = tokio::fs::metadata( - self.config.data_dir.join("secrets/claude-api-key"), - ) - .await - .is_ok(); + let claude_available = + tokio::fs::metadata(self.config.data_dir.join("secrets/claude-api-key")) + .await + .is_ok(); Ok(serde_json::json!({ "enabled": cfg.enabled, "model": cfg.model, @@ -94,7 +93,10 @@ impl RpcHandler { .get("fire_at") .and_then(|v| v.as_i64()) .ok_or_else(|| anyhow::anyhow!("fire_at (unix seconds) is required"))?; - let contact_id = p.get("contact_id").and_then(|v| v.as_u64()).map(|v| v as u32); + let contact_id = p + .get("contact_id") + .and_then(|v| v.as_u64()) + .map(|v| v as u32); let channel = p.get("channel").and_then(|v| v.as_u64()).map(|v| v as u8); if contact_id.is_none() && channel.is_none() { anyhow::bail!("either contact_id or channel is required"); @@ -104,7 +106,10 @@ impl RpcHandler { let svc = service .as_ref() .ok_or_else(|| anyhow::anyhow!("Mesh service not running"))?; - let msg = svc.scheduler.add(contact_id, channel, body, fire_at).await?; + let msg = svc + .scheduler + .add(contact_id, channel, body, fire_at) + .await?; Ok(serde_json::to_value(msg)?) } @@ -157,7 +162,9 @@ async fn detect_ollama() -> (bool, Vec) { .map(|arr| { arr.iter() .filter_map(|m| { - m.get("name").and_then(|n| n.as_str()).map(|s| s.to_string()) + m.get("name") + .and_then(|n| n.as_str()) + .map(|s| s.to_string()) }) .collect() }) diff --git a/core/archipelago/src/api/rpc/mesh/messaging.rs b/core/archipelago/src/api/rpc/mesh/messaging.rs index 2aa64c25..62dd9011 100644 --- a/core/archipelago/src/api/rpc/mesh/messaging.rs +++ b/core/archipelago/src/api/rpc/mesh/messaging.rs @@ -116,7 +116,10 @@ impl RpcHandler { { config.announce_block_headers = announce; } - if let Some(receive) = params.get("receive_block_headers").and_then(|v| v.as_bool()) { + if let Some(receive) = params + .get("receive_block_headers") + .and_then(|v| v.as_bool()) + { config.receive_block_headers = receive; } diff --git a/core/archipelago/src/api/rpc/mesh/status.rs b/core/archipelago/src/api/rpc/mesh/status.rs index 3f41cf43..59f61a98 100644 --- a/core/archipelago/src/api/rpc/mesh/status.rs +++ b/core/archipelago/src/api/rpc/mesh/status.rs @@ -28,8 +28,14 @@ impl RpcHandler { }) }; if let Some(obj) = value.as_object_mut() { - obj.insert("announce_block_headers".into(), config.announce_block_headers.into()); - obj.insert("receive_block_headers".into(), config.receive_block_headers.into()); + obj.insert( + "announce_block_headers".into(), + config.announce_block_headers.into(), + ); + obj.insert( + "receive_block_headers".into(), + config.receive_block_headers.into(), + ); } Ok(value) } diff --git a/core/archipelago/src/api/rpc/package/stacks.rs b/core/archipelago/src/api/rpc/package/stacks.rs index b0459735..8155afd9 100644 --- a/core/archipelago/src/api/rpc/package/stacks.rs +++ b/core/archipelago/src/api/rpc/package/stacks.rs @@ -469,7 +469,10 @@ async fn wait_for_stack_containers( PODMAN_STACK_PROBE_TIMEOUT, ) .await; - pending.push(format!("{}=restarting({}/{})", container, *attempts, MAX_RESTARTS)); + pending.push(format!( + "{}=restarting({}/{})", + container, *attempts, MAX_RESTARTS + )); } else { let logs = stack_container_logs(container, 40).await; install_log(&format!( @@ -1997,11 +2000,7 @@ async fn ensure_netbird_tls_cert(host_ip: &str) -> Result<()> { Ok(()) } -async fn write_netbird_config_files( - host_ip: &str, - lan_ip: &str, - resolver_ip: &str, -) -> Result<()> { +async fn write_netbird_config_files(host_ip: &str, lan_ip: &str, resolver_ip: &str) -> Result<()> { // netbird's dashboard uses window.crypto.subtle (OIDC PKCE), which browsers // only expose in a SECURE context — so the proxy serves HTTPS and every // origin here is https (issue #15: over plain http the dashboard threw @@ -2187,7 +2186,10 @@ async fn detect_netbird_public_host_ip() -> Option { .await .ok()?; let stdout = String::from_utf8_lossy(&output.stdout); - let ips: Vec<&str> = stdout.split_whitespace().filter(|s| s.contains('.')).collect(); + let ips: Vec<&str> = stdout + .split_whitespace() + .filter(|s| s.contains('.')) + .collect(); // Prefer the LAN address as the canonical origin — that's what users browse // to on the local network. Baking the Tailscale 100.x address here broke diff --git a/core/archipelago/src/api/rpc/seed_rpc.rs b/core/archipelago/src/api/rpc/seed_rpc.rs index 22e5057a..13c6870d 100644 --- a/core/archipelago/src/api/rpc/seed_rpc.rs +++ b/core/archipelago/src/api/rpc/seed_rpc.rs @@ -398,8 +398,7 @@ impl RpcHandler { .and_then(|v| v.as_str()) .map(|s| s.to_string()); let secret_phrase = passphrase.unwrap_or_else(|| password.clone()); - let reveal = - crate::seed::load_seed_encrypted(&self.config.data_dir, &secret_phrase).await; + let reveal = crate::seed::load_seed_encrypted(&self.config.data_dir, &secret_phrase).await; password.zeroize(); let mnemonic = reveal.map_err(|_| { anyhow::anyhow!( diff --git a/core/archipelago/src/ceremony.rs b/core/archipelago/src/ceremony.rs index b491639c..1036cc4c 100644 --- a/core/archipelago/src/ceremony.rs +++ b/core/archipelago/src/ceremony.rs @@ -92,7 +92,10 @@ fn cmd_sign(path: &str) -> Result<()> { let obj = value.as_object_mut().expect("checked above"); obj.insert("signature".into(), serde_json::Value::String(signature)); - obj.insert("signed_by".into(), serde_json::Value::String(signed_by.clone())); + obj.insert( + "signed_by".into(), + serde_json::Value::String(signed_by.clone()), + ); let pretty = serde_json::to_string_pretty(&value).context("serialize signed document")?; let tmp = format!("{path}.tmp"); @@ -107,8 +110,8 @@ fn cmd_sign(path: &str) -> Result<()> { /// Derive the release-root signing key from the mnemonic in env/stdin. fn load_release_root_key() -> Result { let phrase = read_mnemonic()?; - let (_mnemonic, seed) = - MasterSeed::from_mnemonic_words(phrase.trim()).context("invalid release master mnemonic")?; + let (_mnemonic, seed) = MasterSeed::from_mnemonic_words(phrase.trim()) + .context("invalid release master mnemonic")?; seed::derive_release_root_ed25519(&seed).context("derive release-root") } diff --git a/core/archipelago/src/container/app_catalog.rs b/core/archipelago/src/container/app_catalog.rs index 912c3b8f..b32980d0 100644 --- a/core/archipelago/src/container/app_catalog.rs +++ b/core/archipelago/src/container/app_catalog.rs @@ -281,9 +281,15 @@ async fn fetch_one(client: &reqwest::Client, url: &str) -> anyhow::Result { debug!("app-catalog: unsigned (accepted during migration window)"); } - crate::trust::SignatureStatus::Verified { signer_did, anchored } => { + crate::trust::SignatureStatus::Verified { + signer_did, + anchored, + } => { if anchored { - info!("app-catalog: release-root signature verified ({})", signer_did); + info!( + "app-catalog: release-root signature verified ({})", + signer_did + ); } else { warn!( "app-catalog: signature self-consistent but release-root anchor \ diff --git a/core/archipelago/src/container/prod_orchestrator.rs b/core/archipelago/src/container/prod_orchestrator.rs index f83f609b..fd544b21 100644 --- a/core/archipelago/src/container/prod_orchestrator.rs +++ b/core/archipelago/src/container/prod_orchestrator.rs @@ -235,7 +235,9 @@ fn build_fingerprint_stamp_path(data_dir: &Path, tag: &str) -> PathBuf { .chars() .map(|c| if c.is_ascii_alphanumeric() { c } else { '_' }) .collect(); - data_dir.join(".image-build").join(format!("{safe}.fingerprint")) + data_dir + .join(".image-build") + .join(format!("{safe}.fingerprint")) } async fn chown_for_rootless_container(uid_gid: &str, path: &str) -> Result<()> { @@ -362,7 +364,16 @@ async fn ensure_running_container_ownership(name: &str) -> bool { // Repair inside the container's userns — podman maps to the right host uid. let chown = tokio::process::Command::new("podman") - .args(["exec", "-u", "0", name, "chown", "-R", &format!("{uid}:{gid}"), dest]) + .args([ + "exec", + "-u", + "0", + name, + "chown", + "-R", + &format!("{uid}:{gid}"), + dest, + ]) .output() .await; match chown { @@ -378,7 +389,9 @@ async fn ensure_running_container_ownership(name: &str) -> bool { "volume ownership repair failed: {}", String::from_utf8_lossy(&o.stderr).trim() ), - Err(e) => tracing::warn!(container = %name, dest, "volume ownership repair errored: {e}"), + Err(e) => { + tracing::warn!(container = %name, dest, "volume ownership repair errored: {e}") + } } } repaired @@ -4634,10 +4647,15 @@ app: #[test] fn build_fingerprint_stamp_path_sanitizes_tag() { - let p = build_fingerprint_stamp_path(Path::new("/var/lib/archipelago"), "localhost/bitcoin-ui:local"); + let p = build_fingerprint_stamp_path( + Path::new("/var/lib/archipelago"), + "localhost/bitcoin-ui:local", + ); assert_eq!( p, - PathBuf::from("/var/lib/archipelago/.image-build/localhost_bitcoin_ui_local.fingerprint") + PathBuf::from( + "/var/lib/archipelago/.image-build/localhost_bitcoin_ui_local.fingerprint" + ) ); } } diff --git a/core/archipelago/src/mesh/listener/assist.rs b/core/archipelago/src/mesh/listener/assist.rs index f53125ff..0e54a46e 100644 --- a/core/archipelago/src/mesh/listener/assist.rs +++ b/core/archipelago/src/mesh/listener/assist.rs @@ -65,11 +65,13 @@ pub(super) async fn run_assist( "AssistQuery denied — sender not permitted by assistant policy" ); // Silent on the wire (no airtime spent on denials); surface to the UI. - let _ = state.event_tx.send(super::super::types::MeshEvent::AssistResponseReady { - req_id, - to_contact_id: asker, - error: Some("denied".to_string()), - }); + let _ = state + .event_tx + .send(super::super::types::MeshEvent::AssistResponseReady { + req_id, + to_contact_id: asker, + error: Some("denied".to_string()), + }); return; } @@ -77,22 +79,31 @@ pub(super) async fn run_assist( { let mut inflight = state.assist_inflight.write().await; if !inflight.insert(asker) { - warn!(from = asker, "AssistQuery dropped — asker already has one in flight"); + warn!( + from = asker, + "AssistQuery dropped — asker already has one in flight" + ); return; } } - let _ = state.event_tx.send(super::super::types::MeshEvent::AssistQueryReceived { - from_contact_id: asker, - prompt: prompt.clone(), - }); + let _ = state + .event_tx + .send(super::super::types::MeshEvent::AssistQueryReceived { + from_contact_id: asker, + prompt: prompt.clone(), + }); let (backend, configured_model) = { let a = state.assistant.read().await; (a.backend.clone(), a.model.clone()) }; let is_claude = backend == "claude"; - let default_model = if is_claude { CLAUDE_DEFAULT_MODEL } else { DEFAULT_MODEL }; + let default_model = if is_claude { + CLAUDE_DEFAULT_MODEL + } else { + DEFAULT_MODEL + }; let model = model_override .or(configured_model) .unwrap_or_else(|| default_model.to_string()); @@ -108,20 +119,24 @@ pub(super) async fn run_assist( match result { Ok(answer) => { send_reply(&state, &reply, req_id, &answer).await; - let _ = state.event_tx.send(super::super::types::MeshEvent::AssistResponseReady { - req_id, - to_contact_id: asker, - error: None, - }); + let _ = state + .event_tx + .send(super::super::types::MeshEvent::AssistResponseReady { + req_id, + to_contact_id: asker, + error: None, + }); } Err(e) => { warn!(req_id, "AI query failed: {}", e); send_failure(&state, &reply, req_id, "AI unavailable").await; - let _ = state.event_tx.send(super::super::types::MeshEvent::AssistResponseReady { - req_id, - to_contact_id: asker, - error: Some(e.to_string()), - }); + let _ = state + .event_tx + .send(super::super::types::MeshEvent::AssistResponseReady { + req_id, + to_contact_id: asker, + error: Some(e.to_string()), + }); } } @@ -219,7 +234,10 @@ async fn send_typed_chunks(state: &Arc, dest_contact_id: u32, req_id: let chunks: Vec = if chars.is_empty() { vec![String::new()] } else { - chars.chunks(CHUNK_CHARS).map(|c| c.iter().collect()).collect() + chars + .chunks(CHUNK_CHARS) + .map(|c| c.iter().collect()) + .collect() }; let last = chunks.len().saturating_sub(1); for (i, chunk) in chunks.into_iter().enumerate() { @@ -337,7 +355,10 @@ async fn call_claude(data_dir: &Path, model: &str, prompt: &str) -> anyhow::Resu let text = json .get("content") .and_then(|c| c.as_array()) - .and_then(|arr| arr.iter().find_map(|b| b.get("text").and_then(|t| t.as_str()))) + .and_then(|arr| { + arr.iter() + .find_map(|b| b.get("text").and_then(|t| t.as_str())) + }) .unwrap_or("") .to_string(); if text.trim().is_empty() { diff --git a/core/archipelago/src/mesh/listener/frames.rs b/core/archipelago/src/mesh/listener/frames.rs index 78fbba36..1d0b60b9 100644 --- a/core/archipelago/src/mesh/listener/frames.rs +++ b/core/archipelago/src/mesh/listener/frames.rs @@ -340,9 +340,7 @@ async fn handle_channel_payload( // seeded peer instead of creating a duplicate chat thread. Not stored as // a chat message. if let Ok(text) = std::str::from_utf8(payload) { - if let Some((did, ed_hex, x_hex)) = - super::super::protocol::parse_identity_broadcast(text) - { + if let Some((did, ed_hex, x_hex)) = super::super::protocol::parse_identity_broadcast(text) { // Ignore our own identity echoed back by the radio/channel. if ed_hex.eq_ignore_ascii_case(&state.our_ed_pubkey_hex) { return; diff --git a/core/archipelago/src/mesh/listener/session.rs b/core/archipelago/src/mesh/listener/session.rs index 6e5448d4..3f816467 100644 --- a/core/archipelago/src/mesh/listener/session.rs +++ b/core/archipelago/src/mesh/listener/session.rs @@ -433,7 +433,10 @@ pub(super) async fn run_mesh_session( our_ed_pubkey_hex, our_x25519_pubkey_hex, ); - if let Err(e) = device.send_channel_text(0, identity_advert.as_bytes()).await { + if let Err(e) = device + .send_channel_text(0, identity_advert.as_bytes()) + .await + { warn!("Failed to broadcast archipelago identity: {}", e); } diff --git a/core/archipelago/src/mesh/scheduler.rs b/core/archipelago/src/mesh/scheduler.rs index 0778749b..6c9cc45d 100644 --- a/core/archipelago/src/mesh/scheduler.rs +++ b/core/archipelago/src/mesh/scheduler.rs @@ -165,7 +165,11 @@ async fn fire_due(scheduler: &Arc, state: &Arc) { if failed.contains(&m.id) { m.attempts += 1; if m.attempts >= MAX_ATTEMPTS { - warn!(id = m.id, attempts = m.attempts, "Dropping undeliverable scheduled message"); + warn!( + id = m.id, + attempts = m.attempts, + "Dropping undeliverable scheduled message" + ); to_remove.push(m.id); } } diff --git a/core/archipelago/src/port_allocator.rs b/core/archipelago/src/port_allocator.rs index 98e85329..5075ff3e 100644 --- a/core/archipelago/src/port_allocator.rs +++ b/core/archipelago/src/port_allocator.rs @@ -18,7 +18,7 @@ const RESERVED_PORTS: &[u16] = &[ 4080, 8999, 50001, // Mempool stack 23000, // BTCPay 8173, 8174, 8175, // Fedimint - 8178, // Fedimint client daemon (fedimint-clientd REST) + 8178, // Fedimint client daemon (fedimint-clientd REST) 8123, // Home Assistant 3000, // Grafana 11434, // Ollama diff --git a/core/archipelago/src/server.rs b/core/archipelago/src/server.rs index 6c093938..b8a4d822 100644 --- a/core/archipelago/src/server.rs +++ b/core/archipelago/src/server.rs @@ -292,9 +292,9 @@ impl Server { sm.update_data(data).await; } Ok(_) => {} - Err(tokio::sync::broadcast::error::RecvError::Lagged(_)) => { - continue - } + Err(tokio::sync::broadcast::error::RecvError::Lagged( + _, + )) => continue, Err(_) => break, // sender dropped → mesh stopped } } diff --git a/core/archipelago/src/swarm/iroh_provider.rs b/core/archipelago/src/swarm/iroh_provider.rs index afc57cc3..c21f12cc 100644 --- a/core/archipelago/src/swarm/iroh_provider.rs +++ b/core/archipelago/src/swarm/iroh_provider.rs @@ -120,7 +120,8 @@ impl IrohProvider { // The event sender gates each request through the ecash `streaming` layer // — free by default, paid only if the operator priced `content-download` // (Networking Profits → Settings). It also hard-disables peer writes. - let event_sender = super::paid::gated_event_sender(data_dir.to_path_buf(), (*store).clone()); + let event_sender = + super::paid::gated_event_sender(data_dir.to_path_buf(), (*store).clone()); let blobs = BlobsProtocol::new(&store, Some(event_sender)); // Shape-A paid negotiation rides a second ALPN on the same endpoint so a // downloader can pay (open a session) before the blob-GET above serves it. diff --git a/core/archipelago/src/swarm/mod.rs b/core/archipelago/src/swarm/mod.rs index f675e6b0..d60164a8 100644 --- a/core/archipelago/src/swarm/mod.rs +++ b/core/archipelago/src/swarm/mod.rs @@ -108,7 +108,9 @@ pub async fn init( if enabled { warn!("swarm: swarm_enabled set but binary built without the `iroh-swarm` feature — staying origin-only"); } - let _ = RUNTIME.set(SwarmRuntime { providers: Vec::new() }); + let _ = RUNTIME.set(SwarmRuntime { + providers: Vec::new(), + }); return Ok(()); } @@ -123,13 +125,10 @@ pub async fn init( return Ok(()); } - let discovery: Arc = - Arc::new(iroh_provider::NostrSeedDiscovery::new( - relays.to_vec(), - tor_proxy.map(str::to_string), - )); - let provider = - Arc::new(iroh_provider::IrohProvider::new(data_dir, Some(discovery)).await?); + let discovery: Arc = Arc::new( + iroh_provider::NostrSeedDiscovery::new(relays.to_vec(), tor_proxy.map(str::to_string)), + ); + let provider = Arc::new(iroh_provider::IrohProvider::new(data_dir, Some(discovery)).await?); info!( "swarm: iroh provider active (endpoint {}) — swarm-assist enabled, origin always wins", provider.endpoint_id() @@ -231,7 +230,10 @@ where } } - debug!("swarm: no provider served {} — falling back to origin", digest); + debug!( + "swarm: no provider served {} — falling back to origin", + digest + ); origin().await?; Ok(FetchSource::Origin) } @@ -248,7 +250,11 @@ mod tests { use std::sync::atomic::{AtomicBool, Ordering}; fn digest_of(bytes: &[u8]) -> ContentDigest { - ContentDigest::parse(&format!("blake3:{}", crate::content_hash::blake3_hex(bytes))).unwrap() + ContentDigest::parse(&format!( + "blake3:{}", + crate::content_hash::blake3_hex(bytes) + )) + .unwrap() } /// Provider that writes a fixed payload (which may or may not match). @@ -295,7 +301,10 @@ mod tests { .await .unwrap(); assert_eq!(src, FetchSource::Swarm); - assert!(!origin_ran.load(Ordering::SeqCst), "origin must not run on swarm hit"); + assert!( + !origin_ran.load(Ordering::SeqCst), + "origin must not run on swarm hit" + ); assert_eq!(tokio::fs::read(&dest).await.unwrap(), content); } @@ -316,7 +325,11 @@ mod tests { }) .await .unwrap(); - assert_eq!(src, FetchSource::Origin, "tampered swarm bytes must not be accepted"); + assert_eq!( + src, + FetchSource::Origin, + "tampered swarm bytes must not be accepted" + ); assert_eq!(tokio::fs::read(&dest).await.unwrap(), content); } @@ -343,8 +356,14 @@ mod tests { let content = b"second wins".to_vec(); let digest = digest_of(&content); let providers = vec![ - arc(FixedProvider { name: "miss", payload: None }), - arc(FixedProvider { name: "hit", payload: Some(content.clone()) }), + arc(FixedProvider { + name: "miss", + payload: None, + }), + arc(FixedProvider { + name: "hit", + payload: Some(content.clone()), + }), ]; let src = fetch_content_addressed(&digest, &providers, &dest, || async { tokio::fs::write(&dest, b"origin").await?; diff --git a/core/archipelago/src/swarm/paid_alpn.rs b/core/archipelago/src/swarm/paid_alpn.rs index 8a1fca5c..48b7c407 100644 --- a/core/archipelago/src/swarm/paid_alpn.rs +++ b/core/archipelago/src/swarm/paid_alpn.rs @@ -166,7 +166,9 @@ impl ProtocolHandler for PaidBlobsProtocol { }, }; let bytes = serde_json::to_vec(&response).map_err(AcceptError::from_err)?; - send.write_all(&bytes).await.map_err(AcceptError::from_err)?; + send.write_all(&bytes) + .await + .map_err(AcceptError::from_err)?; send.finish().map_err(AcceptError::from_err)?; } Ok(()) @@ -192,7 +194,9 @@ pub async fn negotiate_access( match negotiate_inner(endpoint, data_dir, peer, blake3_hex, policy).await { Ok(proceed) => proceed, Err(e) => { - tracing::debug!("paid-alpn: negotiation with {peer} failed ({e}) — proceeding (gate decides)"); + tracing::debug!( + "paid-alpn: negotiation with {peer} failed ({e}) — proceeding (gate decides)" + ); true } } @@ -265,7 +269,10 @@ mod tests { token: None, }; let json = serde_json::to_string(&req).unwrap(); - assert!(!json.contains("token"), "absent token must be omitted: {json}"); + assert!( + !json.contains("token"), + "absent token must be omitted: {json}" + ); let back: PaidRequest = serde_json::from_str(&json).unwrap(); assert_eq!(back.want, "abcd"); assert!(back.token.is_none()); @@ -277,7 +284,8 @@ mod tests { want: "ff".into(), token: Some("cashuAbc".into()), }; - let back: PaidRequest = serde_json::from_str(&serde_json::to_string(&req).unwrap()).unwrap(); + let back: PaidRequest = + serde_json::from_str(&serde_json::to_string(&req).unwrap()).unwrap(); assert_eq!(back.token.as_deref(), Some("cashuAbc")); } diff --git a/core/archipelago/src/swarm/payment.rs b/core/archipelago/src/swarm/payment.rs index 3e681678..8c2888e7 100644 --- a/core/archipelago/src/swarm/payment.rs +++ b/core/archipelago/src/swarm/payment.rs @@ -88,7 +88,8 @@ pub async fn auto_pay_token( return Ok(None); } - match ecash::build_payment_token(data_dir, accepted_mints, price_sats, policy.max_fee_sats).await + match ecash::build_payment_token(data_dir, accepted_mints, price_sats, policy.max_fee_sats) + .await { Ok(token) => Ok(Some(token)), Err(e) => { diff --git a/core/archipelago/src/swarm/seed_advert.rs b/core/archipelago/src/swarm/seed_advert.rs index 9f0c9d5a..b12e9e29 100644 --- a/core/archipelago/src/swarm/seed_advert.rs +++ b/core/archipelago/src/swarm/seed_advert.rs @@ -191,7 +191,10 @@ mod tests { let hash = "b".repeat(64); let json = serde_json::to_string(&advertisement_filter(&hash)).unwrap(); assert!(json.contains(&hash), "filter must target the hash d-tag"); - assert!(json.contains("30081"), "filter must constrain the seed kind"); + assert!( + json.contains("30081"), + "filter must constrain the seed kind" + ); } #[test] @@ -212,10 +215,19 @@ mod tests { let a = Keys::generate(); let b = Keys::generate(); let hash = "d".repeat(64); - let e1 = advertisement_builder(&hash, "endpoint-A").sign_with_keys(&a).unwrap(); - let e2 = advertisement_builder(&hash, "endpoint-A").sign_with_keys(&b).unwrap(); - let e3 = advertisement_builder(&hash, "endpoint-B").sign_with_keys(&b).unwrap(); + let e1 = advertisement_builder(&hash, "endpoint-A") + .sign_with_keys(&a) + .unwrap(); + let e2 = advertisement_builder(&hash, "endpoint-A") + .sign_with_keys(&b) + .unwrap(); + let e3 = advertisement_builder(&hash, "endpoint-B") + .sign_with_keys(&b) + .unwrap(); let ids = endpoint_ids_from_events([&e1, &e2, &e3]); - assert_eq!(ids, vec!["endpoint-A".to_string(), "endpoint-B".to_string()]); + assert_eq!( + ids, + vec!["endpoint-A".to_string(), "endpoint-B".to_string()] + ); } } diff --git a/core/archipelago/src/trust/did.rs b/core/archipelago/src/trust/did.rs index a7e174a1..7789c8cd 100644 --- a/core/archipelago/src/trust/did.rs +++ b/core/archipelago/src/trust/did.rs @@ -28,9 +28,7 @@ pub fn ed25519_pubkey_from_did_key(did: &str) -> Result { if decoded.len() != 34 || decoded[0..2] != ED25519_MULTICODEC { return Err(anyhow!("not an Ed25519 did:key (bad multicodec prefix)")); } - let arr: [u8; 32] = decoded[2..] - .try_into() - .expect("length checked above"); + let arr: [u8; 32] = decoded[2..].try_into().expect("length checked above"); VerifyingKey::from_bytes(&arr).context("invalid Ed25519 public key in did:key") } diff --git a/core/archipelago/src/trust/signed_doc.rs b/core/archipelago/src/trust/signed_doc.rs index adb4d70a..a0147894 100644 --- a/core/archipelago/src/trust/signed_doc.rs +++ b/core/archipelago/src/trust/signed_doc.rs @@ -60,7 +60,13 @@ pub fn verify_detached(doc: &Value) -> Result { let signed_by = obj .get(SIGNED_BY_FIELD) .and_then(Value::as_str) - .ok_or_else(|| anyhow!("signed document has `{}` but no `{}`", SIGNATURE_FIELD, SIGNED_BY_FIELD))?; + .ok_or_else(|| { + anyhow!( + "signed document has `{}` but no `{}`", + SIGNATURE_FIELD, + SIGNED_BY_FIELD + ) + })?; let signer = did::ed25519_pubkey_from_did_key(signed_by) .with_context(|| format!("invalid `{}` did:key", SIGNED_BY_FIELD))?; @@ -174,7 +180,10 @@ mod tests { #[test] fn tampered_payload_is_rejected() { let mut signed = sign_into(&test_key(), json!({"schema": 1, "n": 42})); - signed.as_object_mut().unwrap().insert("n".into(), json!(43)); + signed + .as_object_mut() + .unwrap() + .insert("n".into(), json!(43)); assert!(verify_detached(&signed).is_err()); } diff --git a/core/archipelago/src/wallet/ecash.rs b/core/archipelago/src/wallet/ecash.rs index 537f1269..81ce5a43 100644 --- a/core/archipelago/src/wallet/ecash.rs +++ b/core/archipelago/src/wallet/ecash.rs @@ -489,8 +489,12 @@ pub async fn swap_between_mints( // The pay leg never completed — record the route failure so future // payments can prefer a route with a track record. record_swap_failure(data_dir, from_mint, to_mint).await; - return Err(e) - .with_context(|| format!("melting source proofs at {} to pay target invoice", from_mint)); + return Err(e).with_context(|| { + format!( + "melting source proofs at {} to pay target invoice", + from_mint + ) + }); } // Persist the spend BEFORE claiming so a crash can't double-spend, and @@ -533,7 +537,10 @@ pub async fn swap_between_mints( wallet.record_tx( TransactionType::Mint, minted, - &format!("Cross-mint swap {}→{}: claimed {} sats", from_mint, to_mint, minted), + &format!( + "Cross-mint swap {}→{}: claimed {} sats", + from_mint, to_mint, minted + ), to_mint, from_mint, ); @@ -680,7 +687,11 @@ pub enum PaymentPlan { /// Pure and synchronous so it can be unit-tested without a live mint. It does /// not know swap fees; `swap_between_mints` enforces the fee cap and bails (→ /// origin fallback) if the chosen source can't cover amount + fee. -fn plan_payment(holdings: &[(String, u64)], accepted: &[(String, bool)], amount: u64) -> PaymentPlan { +fn plan_payment( + holdings: &[(String, u64)], + accepted: &[(String, bool)], + amount: u64, +) -> PaymentPlan { let norm = |s: &str| s.trim_end_matches('/').to_string(); let home = norm(&default_mint_url()); let held = |mint: &str| -> u64 { @@ -692,10 +703,8 @@ fn plan_payment(holdings: &[(String, u64)], accepted: &[(String, bool)], amount: }; // 1. Direct: any accepted mint we already hold enough on. Prefer home. - let mut direct: Vec<&(String, bool)> = accepted - .iter() - .filter(|(m, _)| held(m) >= amount) - .collect(); + let mut direct: Vec<&(String, bool)> = + accepted.iter().filter(|(m, _)| held(m) >= amount).collect(); direct.sort_by_key(|(m, _)| norm(m) != home); // home (false) sorts first if let Some((mint, _)) = direct.first() { return PaymentPlan::Direct { @@ -761,7 +770,10 @@ pub async fn build_payment_token( match plan_payment(&holdings, &accepted, amount_sats) { PaymentPlan::Direct { mint_url } => { - debug!("Payment plan: direct from {} for {} sats", mint_url, amount_sats); + debug!( + "Payment plan: direct from {} for {} sats", + mint_url, amount_sats + ); send_token_at(data_dir, &mint_url, amount_sats).await } PaymentPlan::Swap { from_mint, to_mint } => { @@ -844,7 +856,10 @@ pub async fn resume_pending_swaps(data_dir: &Path) -> Result { let to = match MintClient::new(&swap.to_mint) { Ok(c) => c, Err(e) => { - warn!("resume_pending_swaps: bad target mint {}: {}", swap.to_mint, e); + warn!( + "resume_pending_swaps: bad target mint {}: {}", + swap.to_mint, e + ); continue; } }; @@ -883,7 +898,10 @@ pub async fn resume_pending_swaps(data_dir: &Path) -> Result { swap.from_mint, swap.to_mint, minted ); } - Err(e) => warn!("resume_pending_swaps: claim failed for {}: {}", swap.mint_quote_id, e), + Err(e) => warn!( + "resume_pending_swaps: claim failed for {}: {}", + swap.mint_quote_id, e + ), }, "ISSUED" => { // Already claimed on a previous run — drop the journal entry. @@ -940,14 +958,20 @@ async fn save_swap_liquidity(data_dir: &Path, liq: &SwapLiquidity) { /// Record that a swap route succeeded (best-effort; never fails the caller). async fn record_swap_success(data_dir: &Path, from_mint: &str, to_mint: &str) { let mut liq = load_swap_liquidity(data_dir).await; - liq.routes.entry(route_key(from_mint, to_mint)).or_default().successes += 1; + liq.routes + .entry(route_key(from_mint, to_mint)) + .or_default() + .successes += 1; save_swap_liquidity(data_dir, &liq).await; } /// Record that a swap route failed (best-effort; never fails the caller). async fn record_swap_failure(data_dir: &Path, from_mint: &str, to_mint: &str) { let mut liq = load_swap_liquidity(data_dir).await; - liq.routes.entry(route_key(from_mint, to_mint)).or_default().failures += 1; + liq.routes + .entry(route_key(from_mint, to_mint)) + .or_default() + .failures += 1; save_swap_liquidity(data_dir, &liq).await; } @@ -1574,17 +1598,38 @@ mod tests { wallet.add_proofs( "http://mint-a", vec![ - Proof { amount: 10, id: "k".into(), secret: "s1".into(), c: "c".into() }, - Proof { amount: 5, id: "k".into(), secret: "s2".into(), c: "c".into() }, + Proof { + amount: 10, + id: "k".into(), + secret: "s1".into(), + c: "c".into(), + }, + Proof { + amount: 5, + id: "k".into(), + secret: "s2".into(), + c: "c".into(), + }, ], ); wallet.add_proofs( "http://mint-b", - vec![Proof { amount: 7, id: "k".into(), secret: "s3".into(), c: "c".into() }], + vec![Proof { + amount: 7, + id: "k".into(), + secret: "s3".into(), + c: "c".into(), + }], ); wallet.proofs[1].spent = true; // exclude the 5 on mint-a let by_mint = wallet.spendable_by_mint(); - assert_eq!(by_mint, vec![("http://mint-a".to_string(), 10), ("http://mint-b".to_string(), 7)]); + assert_eq!( + by_mint, + vec![ + ("http://mint-a".to_string(), 10), + ("http://mint-b".to_string(), 7) + ] + ); } #[test] @@ -1606,7 +1651,9 @@ mod tests { let accepted = vec![("https://b".into(), true)]; assert_eq!( plan_payment(&holdings, &accepted, 50), - PaymentPlan::Direct { mint_url: "https://b".into() } + PaymentPlan::Direct { + mint_url: "https://b".into() + } ); } @@ -1617,7 +1664,10 @@ mod tests { let accepted = vec![("https://b".into(), true)]; assert_eq!( plan_payment(&holdings, &accepted, 50), - PaymentPlan::Swap { from_mint: "https://a".into(), to_mint: "https://b".into() } + PaymentPlan::Swap { + from_mint: "https://a".into(), + to_mint: "https://b".into() + } ); } @@ -1626,7 +1676,10 @@ mod tests { // Seeder accepts only B, but B is not trusted → no swap, insufficient. let holdings = vec![("https://a".into(), 100)]; let accepted = vec![("https://b".into(), false)]; - assert_eq!(plan_payment(&holdings, &accepted, 50), PaymentPlan::Insufficient); + assert_eq!( + plan_payment(&holdings, &accepted, 50), + PaymentPlan::Insufficient + ); } #[test] @@ -1635,7 +1688,10 @@ mod tests { // we hold neither accepted mint directly. let holdings = vec![("https://a".into(), 30), ("https://c".into(), 30)]; let accepted = vec![("https://b".into(), true)]; - assert_eq!(plan_payment(&holdings, &accepted, 50), PaymentPlan::Insufficient); + assert_eq!( + plan_payment(&holdings, &accepted, 50), + PaymentPlan::Insufficient + ); } #[test] @@ -1646,7 +1702,9 @@ mod tests { let accepted = vec![("https://b".into(), true)]; assert_eq!( plan_payment(&holdings, &accepted, 50), - PaymentPlan::Direct { mint_url: "https://b".into() } + PaymentPlan::Direct { + mint_url: "https://b".into() + } ); } @@ -1744,7 +1802,10 @@ mod tests { record_swap_success(tmp.path(), "https://src", "https://liquid").await; // Seeder accepts both non-home mints; we only hold "https://src". - let accepted = vec![("https://dry".into(), true), ("https://liquid".into(), true)]; + let accepted = vec![ + ("https://dry".into(), true), + ("https://liquid".into(), true), + ]; let holdings = vec![("https://src".to_string(), 1000u64)]; // Mirror build_payment_token's ordering step, then plan. diff --git a/core/archipelago/src/wallet/fedimint_client.rs b/core/archipelago/src/wallet/fedimint_client.rs index 2c6682d0..b644f24c 100644 --- a/core/archipelago/src/wallet/fedimint_client.rs +++ b/core/archipelago/src/wallet/fedimint_client.rs @@ -88,7 +88,11 @@ pub async fn ensure_default_federation(data_dir: &Path) -> Result<()> { } }; let mut reg = load_registry(data_dir).await?; - if !reg.federations.iter().any(|f| f.federation_id == federation_id) { + if !reg + .federations + .iter() + .any(|f| f.federation_id == federation_id) + { reg.federations.push(JoinedFederation { federation_id, name: None,