• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

payjoin / rust-payjoin / 24517350856

16 Apr 2026 02:56PM UTC coverage: 84.74% (+0.4%) from 84.38%
24517350856

Pull #1377

github

web-flow
Merge 27e916f06 into 7b9057d1f
Pull Request #1377: Use internal Url struct in favor of url::Url to minimize the url dep in payjoin

534 of 574 new or added lines in 16 files covered. (93.03%)

3 existing lines in 2 files now uncovered.

11267 of 13296 relevant lines covered (84.74%)

402.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.24
/payjoin-cli/src/app/v2/ohttp.rs
1
use std::sync::{Arc, Mutex};
2

3
use anyhow::{anyhow, Result};
4
use payjoin::Url;
5

6
use super::Config;
7

8
#[derive(Debug, Clone)]
9
pub struct RelayManager {
10
    selected_relay: Option<Url>,
11
    failed_relays: Vec<Url>,
12
}
13

14
impl RelayManager {
15
    pub fn new() -> Self { RelayManager { selected_relay: None, failed_relays: Vec::new() } }
8✔
16

17
    pub fn set_selected_relay(&mut self, relay: Url) { self.selected_relay = Some(relay); }
4✔
18

19
    pub fn get_selected_relay(&self) -> Option<Url> { self.selected_relay.clone() }
6✔
20

NEW
21
    pub fn add_failed_relay(&mut self, relay: Url) { self.failed_relays.push(relay); }
×
22

23
    pub fn get_failed_relays(&self) -> Vec<Url> { self.failed_relays.clone() }
4✔
24
}
25

26
pub(crate) struct ValidatedOhttpKeys {
27
    pub(crate) ohttp_keys: payjoin::OhttpKeys,
28
    pub(crate) relay_url: Url,
29
}
30

31
pub(crate) async fn unwrap_ohttp_keys_or_else_fetch(
4✔
32
    config: &Config,
4✔
33
    directory: Option<Url>,
4✔
34
    relay_manager: Arc<Mutex<RelayManager>>,
4✔
35
) -> Result<ValidatedOhttpKeys> {
4✔
36
    if let Some(ohttp_keys) = config.v2()?.ohttp_keys.clone() {
4✔
37
        println!("Using OHTTP Keys from config");
1✔
38
        let validated = fetch_ohttp_keys(config, directory, relay_manager).await?;
1✔
39
        Ok(ValidatedOhttpKeys { ohttp_keys, relay_url: validated.relay_url })
1✔
40
    } else {
41
        println!("Bootstrapping private network transport over Oblivious HTTP");
3✔
42
        let fetched_keys = fetch_ohttp_keys(config, directory, relay_manager).await?;
3✔
43

44
        Ok(fetched_keys)
3✔
45
    }
46
}
4✔
47

48
async fn fetch_ohttp_keys(
4✔
49
    config: &Config,
4✔
50
    directory: Option<Url>,
4✔
51
    relay_manager: Arc<Mutex<RelayManager>>,
4✔
52
) -> Result<ValidatedOhttpKeys> {
4✔
53
    use payjoin::bitcoin::secp256k1::rand::prelude::SliceRandom;
54
    let payjoin_directory = directory.unwrap_or(config.v2()?.pj_directory.clone());
4✔
55
    let relays = config.v2()?.ohttp_relays.clone();
4✔
56

57
    loop {
58
        let failed_relays =
4✔
59
            relay_manager.lock().expect("Lock should not be poisoned").get_failed_relays();
4✔
60

61
        let remaining_relays: Vec<_> =
4✔
62
            relays.iter().filter(|r| !failed_relays.contains(r)).cloned().collect();
4✔
63

64
        if remaining_relays.is_empty() {
4✔
65
            return Err(anyhow!("No valid relays available"));
×
66
        }
4✔
67

68
        let selected_relay =
4✔
69
            match remaining_relays.choose(&mut payjoin::bitcoin::key::rand::thread_rng()) {
4✔
70
                Some(relay) => relay.clone(),
4✔
71
                None => return Err(anyhow!("Failed to select from remaining relays")),
×
72
            };
73

74
        relay_manager
4✔
75
            .lock()
4✔
76
            .expect("Lock should not be poisoned")
4✔
77
            .set_selected_relay(selected_relay.clone());
4✔
78

79
        let ohttp_keys = {
4✔
80
            #[cfg(feature = "_manual-tls")]
81
            {
82
                if let Some(cert_path) = config.root_certificate.as_ref() {
4✔
83
                    let cert_der = std::fs::read(cert_path)?;
4✔
84
                    payjoin::io::fetch_ohttp_keys_with_cert(
4✔
85
                        selected_relay.as_str(),
4✔
86
                        payjoin_directory.as_str(),
4✔
87
                        &cert_der,
4✔
88
                    )
4✔
89
                    .await
4✔
90
                } else {
91
                    payjoin::io::fetch_ohttp_keys(
×
92
                        selected_relay.as_str(),
×
93
                        payjoin_directory.as_str(),
×
94
                    )
×
95
                    .await
×
96
                }
97
            }
98
            #[cfg(not(feature = "_manual-tls"))]
99
            payjoin::io::fetch_ohttp_keys(selected_relay.as_str(), payjoin_directory.as_str()).await
100
        };
101

102
        match ohttp_keys {
×
103
            Ok(keys) =>
4✔
104
                return Ok(ValidatedOhttpKeys { ohttp_keys: keys, relay_url: selected_relay }),
4✔
105
            Err(payjoin::io::Error::UnexpectedStatusCode(e)) => {
×
106
                return Err(payjoin::io::Error::UnexpectedStatusCode(e).into());
×
107
            }
108
            Err(e) => {
×
109
                tracing::debug!("Failed to connect to relay: {selected_relay}, {e:?}");
×
110
                relay_manager
×
111
                    .lock()
×
112
                    .expect("Lock should not be poisoned")
×
113
                    .add_failed_relay(selected_relay);
×
114
            }
115
        }
116
    }
117
}
4✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc