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

payjoin / rust-payjoin / 15303313837

28 May 2025 02:49PM UTC coverage: 83.805% (-0.7%) from 84.553%
15303313837

Pull #705

github

web-flow
Merge c7652d355 into 87042266d
Pull Request #705: Adjust _danger-local-https to be the default to prevent dangerous cert vulnerability when building with --all-features

11 of 27 new or added lines in 3 files covered. (40.74%)

65 existing lines in 3 files now uncovered.

6520 of 7780 relevant lines covered (83.8%)

595.35 hits per line

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

18.81
/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::Url>,
10
    failed_relays: Vec<payjoin::Url>,
11
}
12

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

UNCOV
16
    pub fn set_selected_relay(&mut self, relay: payjoin::Url) { self.selected_relay = Some(relay); }
×
17

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

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

UNCOV
22
    pub fn get_failed_relays(&self) -> Vec<payjoin::Url> { self.failed_relays.clone() }
×
23
}
24

25
pub(crate) async fn unwrap_ohttp_keys_or_else_fetch(
1✔
26
    config: &Config,
1✔
27
    relay_manager: Arc<Mutex<RelayManager>>,
1✔
28
) -> Result<payjoin::OhttpKeys> {
1✔
29
    if let Some(keys) = config.v2()?.ohttp_keys.clone() {
1✔
30
        println!("Using OHTTP Keys from config");
1✔
31
        Ok(keys)
1✔
32
    } else {
33
        println!("Bootstrapping private network transport over Oblivious HTTP");
×
34

×
NEW
35
        fetch_keys(config, relay_manager.clone())
×
36
            .await
×
37
            .and_then(|keys| keys.ok_or_else(|| anyhow::anyhow!("No OHTTP keys found")))
×
38
    }
39
}
1✔
40

UNCOV
41
async fn fetch_keys(
×
UNCOV
42
    config: &Config,
×
NEW
43
    relay_manager: Arc<Mutex<RelayManager>>,
×
UNCOV
44
) -> Result<Option<payjoin::OhttpKeys>> {
×
45
    use payjoin::bitcoin::secp256k1::rand::prelude::SliceRandom;
UNCOV
46
    let payjoin_directory = config.v2()?.pj_directory.clone();
×
UNCOV
47
    let relays = config.v2()?.ohttp_relays.clone();
×
48

UNCOV
49
    loop {
×
UNCOV
50
        let failed_relays =
×
NEW
51
            relay_manager.lock().expect("Lock should not be poisoned").get_failed_relays();
×
UNCOV
52

×
UNCOV
53
        let remaining_relays: Vec<_> =
×
UNCOV
54
            relays.iter().filter(|r| !failed_relays.contains(r)).cloned().collect();
×
UNCOV
55

×
UNCOV
56
        if remaining_relays.is_empty() {
×
UNCOV
57
            return Err(anyhow!("No valid relays available"));
×
UNCOV
58
        }
×
59

UNCOV
60
        let selected_relay =
×
UNCOV
61
            match remaining_relays.choose(&mut payjoin::bitcoin::key::rand::thread_rng()) {
×
UNCOV
62
                Some(relay) => relay.clone(),
×
UNCOV
63
                None => return Err(anyhow!("Failed to select from remaining relays")),
×
64
            };
65

NEW
66
        relay_manager
×
UNCOV
67
            .lock()
×
UNCOV
68
            .expect("Lock should not be poisoned")
×
UNCOV
69
            .set_selected_relay(selected_relay.clone());
×
70

UNCOV
71
        let ohttp_keys = {
×
72
            #[cfg(not(feature = "pki-https"))]
73
            {
NEW
74
                let cert_der = crate::app::read_local_cert()?;
×
NEW
75
                payjoin::io::fetch_ohttp_keys_with_cert(
×
NEW
76
                    selected_relay.clone(),
×
NEW
77
                    payjoin_directory.clone(),
×
NEW
78
                    cert_der,
×
NEW
79
                )
×
NEW
80
                .await
×
81
            }
82
            #[cfg(feature = "pki-https")]
83
            {
84
                payjoin::io::fetch_ohttp_keys(selected_relay.clone(), payjoin_directory.clone())
85
                    .await
86
            }
87
        };
88

UNCOV
89
        match ohttp_keys {
×
UNCOV
90
            Ok(keys) => return Ok(Some(keys)),
×
UNCOV
91
            Err(payjoin::io::Error::UnexpectedStatusCode(e)) => {
×
UNCOV
92
                return Err(payjoin::io::Error::UnexpectedStatusCode(e).into());
×
93
            }
UNCOV
94
            Err(e) => {
×
UNCOV
95
                log::debug!("Failed to connect to relay: {selected_relay}, {e:?}");
×
NEW
96
                relay_manager
×
UNCOV
97
                    .lock()
×
UNCOV
98
                    .expect("Lock should not be poisoned")
×
UNCOV
99
                    .add_failed_relay(selected_relay);
×
100
            }
101
        }
102
    }
UNCOV
103
}
×
104

105
pub(crate) async fn validate_relay(
6✔
106
    config: &Config,
6✔
107
    relay_manager: Arc<Mutex<RelayManager>>,
6✔
108
) -> Result<payjoin::Url> {
6✔
109
    use payjoin::bitcoin::secp256k1::rand::prelude::SliceRandom;
110
    let payjoin_directory = config.v2()?.pj_directory.clone();
6✔
111
    let relays = config.v2()?.ohttp_relays.clone();
6✔
112

113
    if let Some(relay) = config.v2()?.ohttp_relays.first().cloned() {
6✔
114
        return Ok(relay);
6✔
NEW
115
    }
×
116

UNCOV
117
    loop {
×
UNCOV
118
        let failed_relays =
×
NEW
119
            relay_manager.lock().expect("Lock should not be poisoned").get_failed_relays();
×
UNCOV
120

×
UNCOV
121
        let remaining_relays: Vec<_> =
×
UNCOV
122
            relays.iter().filter(|r| !failed_relays.contains(r)).cloned().collect();
×
UNCOV
123

×
UNCOV
124
        if remaining_relays.is_empty() {
×
UNCOV
125
            return Err(anyhow!("No valid relays available"));
×
UNCOV
126
        }
×
127

UNCOV
128
        let selected_relay =
×
UNCOV
129
            match remaining_relays.choose(&mut payjoin::bitcoin::key::rand::thread_rng()) {
×
UNCOV
130
                Some(relay) => relay.clone(),
×
UNCOV
131
                None => return Err(anyhow!("Failed to select from remaining relays")),
×
132
            };
133

NEW
134
        relay_manager
×
UNCOV
135
            .lock()
×
UNCOV
136
            .expect("Lock should not be poisoned")
×
UNCOV
137
            .set_selected_relay(selected_relay.clone());
×
138

UNCOV
139
        let ohttp_keys =
×
UNCOV
140
            payjoin::io::fetch_ohttp_keys(selected_relay.clone(), payjoin_directory.clone()).await;
×
141

UNCOV
142
        match ohttp_keys {
×
UNCOV
143
            Ok(_) => return Ok(selected_relay),
×
UNCOV
144
            Err(payjoin::io::Error::UnexpectedStatusCode(e)) => {
×
UNCOV
145
                return Err(payjoin::io::Error::UnexpectedStatusCode(e).into());
×
146
            }
UNCOV
147
            Err(e) => {
×
UNCOV
148
                log::debug!("Failed to connect to relay: {selected_relay}, {e:?}");
×
NEW
149
                relay_manager
×
UNCOV
150
                    .lock()
×
UNCOV
151
                    .expect("Lock should not be poisoned")
×
UNCOV
152
                    .add_failed_relay(selected_relay);
×
153
            }
154
        }
155
    }
156
}
6✔
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