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

payjoin / rust-payjoin / 17950773689

23 Sep 2025 03:16PM UTC coverage: 84.527% (-0.009%) from 84.536%
17950773689

Pull #1077

github

web-flow
Merge c31244f76 into e8bb1a7c3
Pull Request #1077: Remove url::Url from return values in public api

106 of 112 new or added lines in 15 files covered. (94.64%)

1 existing line in 1 file now uncovered.

8615 of 10192 relevant lines covered (84.53%)

480.14 hits per line

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

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

3
use anyhow::{anyhow, Result};
4

5
use super::Config;
6

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

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

16
    pub fn set_selected_relay(&mut self, relay: payjoin::into_url::Url) {
3✔
17
        self.selected_relay = Some(relay);
3✔
18
    }
3✔
19

20
    pub fn get_selected_relay(&self) -> Option<payjoin::into_url::Url> {
8✔
21
        self.selected_relay.clone()
8✔
22
    }
8✔
23

NEW
24
    pub fn add_failed_relay(&mut self, relay: payjoin::into_url::Url) {
×
NEW
25
        self.failed_relays.push(relay);
×
NEW
26
    }
×
27

28
    pub fn get_failed_relays(&self) -> Vec<payjoin::into_url::Url> { self.failed_relays.clone() }
3✔
29
}
30

31
pub(crate) struct ValidatedOhttpKeys {
32
    pub(crate) ohttp_keys: payjoin::OhttpKeys,
33
    pub(crate) relay_url: payjoin::into_url::Url,
34
}
35

36
pub(crate) async fn unwrap_ohttp_keys_or_else_fetch(
6✔
37
    config: &Config,
6✔
38
    directory: Option<payjoin::into_url::Url>,
6✔
39
    relay_manager: Arc<Mutex<RelayManager>>,
6✔
40
) -> Result<ValidatedOhttpKeys> {
6✔
41
    if let Some(ohttp_keys) = config.v2()?.ohttp_keys.clone() {
6✔
42
        println!("Using OHTTP Keys from config");
3✔
43
        return Ok(ValidatedOhttpKeys {
44
            ohttp_keys,
3✔
45
            relay_url: config.v2()?.ohttp_relays[0].clone(),
3✔
46
        });
47
    } else {
48
        println!("Bootstrapping private network transport over Oblivious HTTP");
3✔
49
        let fetched_keys = fetch_ohttp_keys(config, directory, relay_manager).await?;
3✔
50

51
        Ok(fetched_keys)
3✔
52
    }
53
}
6✔
54

55
async fn fetch_ohttp_keys(
3✔
56
    config: &Config,
3✔
57
    directory: Option<payjoin::into_url::Url>,
3✔
58
    relay_manager: Arc<Mutex<RelayManager>>,
3✔
59
) -> Result<ValidatedOhttpKeys> {
3✔
60
    use payjoin::bitcoin::secp256k1::rand::prelude::SliceRandom;
61
    let payjoin_directory = directory.unwrap_or(config.v2()?.pj_directory.clone());
3✔
62
    let relays = config.v2()?.ohttp_relays.clone();
3✔
63

64
    loop {
65
        let failed_relays =
3✔
66
            relay_manager.lock().expect("Lock should not be poisoned").get_failed_relays();
3✔
67

68
        let remaining_relays: Vec<_> =
3✔
69
            relays.iter().filter(|r| !failed_relays.contains(r)).cloned().collect();
3✔
70

71
        if remaining_relays.is_empty() {
3✔
72
            return Err(anyhow!("No valid relays available"));
×
73
        }
3✔
74

75
        let selected_relay =
3✔
76
            match remaining_relays.choose(&mut payjoin::bitcoin::key::rand::thread_rng()) {
3✔
77
                Some(relay) => relay.clone(),
3✔
78
                None => return Err(anyhow!("Failed to select from remaining relays")),
×
79
            };
80

81
        relay_manager
3✔
82
            .lock()
3✔
83
            .expect("Lock should not be poisoned")
3✔
84
            .set_selected_relay(selected_relay.clone());
3✔
85

86
        let ohttp_keys = {
3✔
87
            #[cfg(feature = "_manual-tls")]
88
            {
89
                if let Some(cert_path) = config.root_certificate.as_ref() {
3✔
90
                    let cert_der = std::fs::read(cert_path)?;
3✔
91
                    payjoin::io::fetch_ohttp_keys_with_cert(
3✔
92
                        selected_relay.as_str(),
3✔
93
                        payjoin_directory.as_str(),
3✔
94
                        cert_der,
3✔
95
                    )
3✔
96
                    .await
3✔
97
                } else {
98
                    payjoin::io::fetch_ohttp_keys(
×
99
                        selected_relay.as_str(),
×
100
                        payjoin_directory.as_str(),
×
101
                    )
×
102
                    .await
×
103
                }
104
            }
105
            #[cfg(not(feature = "_manual-tls"))]
106
            payjoin::io::fetch_ohttp_keys(selected_relay.as_str(), payjoin_directory.as_str()).await
107
        };
108

109
        match ohttp_keys {
×
110
            Ok(keys) =>
3✔
111
                return Ok(ValidatedOhttpKeys { ohttp_keys: keys, relay_url: selected_relay }),
3✔
112
            Err(payjoin::io::Error::UnexpectedStatusCode(e)) => {
×
113
                return Err(payjoin::io::Error::UnexpectedStatusCode(e).into());
×
114
            }
115
            Err(e) => {
×
116
                tracing::debug!("Failed to connect to relay: {selected_relay}, {e:?}");
×
117
                relay_manager
×
118
                    .lock()
×
119
                    .expect("Lock should not be poisoned")
×
120
                    .add_failed_relay(selected_relay);
×
121
            }
122
        }
123
    }
124
}
3✔
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