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

payjoin / rust-payjoin / 13477293934

22 Feb 2025 10:56PM UTC coverage: 79.558% (+0.5%) from 79.045%
13477293934

Pull #434

github

web-flow
Merge de285a2f1 into 3b95ff295
Pull Request #434: Multiparty Senders: NS1R

423 of 493 new or added lines in 9 files covered. (85.8%)

1 existing line in 1 file now uncovered.

4429 of 5567 relevant lines covered (79.56%)

828.91 hits per line

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

72.86
/payjoin/src/receive/optional_parameters.rs
1
use std::borrow::Borrow;
2
use std::fmt;
3

4
use bitcoin::FeeRate;
5
use log::warn;
6

7
#[derive(Debug, Clone)]
8
pub(crate) struct Params {
9
    // version
10
    pub v: usize,
11
    // disableoutputsubstitution
12
    pub disable_output_substitution: bool,
13
    // maxadditionalfeecontribution, additionalfeeoutputindex
14
    pub additional_fee_contribution: Option<(bitcoin::Amount, usize)>,
15
    // minfeerate
16
    pub min_fee_rate: FeeRate,
17
    #[cfg(feature = "multiparty")]
18
    /// Opt in to optimistic psbt merge
19
    pub optimistic_merge: bool,
20
}
21

22
impl Default for Params {
23
    fn default() -> Self {
28✔
24
        Params {
28✔
25
            v: 1,
28✔
26
            disable_output_substitution: false,
28✔
27
            additional_fee_contribution: None,
28✔
28
            min_fee_rate: FeeRate::BROADCAST_MIN,
28✔
29
            #[cfg(feature = "multiparty")]
28✔
30
            optimistic_merge: false,
28✔
31
        }
28✔
32
    }
28✔
33
}
34

35
impl Params {
36
    pub fn from_query_pairs<K, V, I>(
24✔
37
        pairs: I,
24✔
38
        supported_versions: &'static [usize],
24✔
39
    ) -> Result<Self, Error>
24✔
40
    where
24✔
41
        I: Iterator<Item = (K, V)>,
24✔
42
        K: Borrow<str> + Into<String>,
24✔
43
        V: Borrow<str> + Into<String>,
24✔
44
    {
24✔
45
        let mut params = Params::default();
24✔
46

24✔
47
        let mut additional_fee_output_index = None;
24✔
48
        let mut max_additional_fee_contribution = None;
24✔
49

50
        for (key, v) in pairs {
93✔
51
            match (key.borrow(), v.borrow()) {
69✔
52
                ("v", version) =>
69✔
53
                    params.v = match version.parse::<usize>() {
19✔
54
                        Ok(version) if supported_versions.contains(&version) => version,
19✔
55
                        _ => return Err(Error::UnknownVersion { supported_versions }),
×
56
                    },
57
                ("additionalfeeoutputindex", index) =>
50✔
58
                    additional_fee_output_index = match index.parse::<usize>() {
15✔
59
                        Ok(index) => Some(index),
15✔
60
                        Err(_error) => {
×
61
                            warn!(
×
62
                                "bad `additionalfeeoutputindex` query value '{}': {}",
×
63
                                index, _error
64
                            );
65
                            None
×
66
                        }
67
                    },
68
                ("maxadditionalfeecontribution", fee) =>
35✔
69
                    max_additional_fee_contribution =
15✔
70
                        match bitcoin::Amount::from_str_in(fee, bitcoin::Denomination::Satoshi) {
15✔
71
                            Ok(contribution) => Some(contribution),
15✔
72
                            Err(_error) => {
×
73
                                warn!(
×
74
                                    "bad `maxadditionalfeecontribution` query value '{}': {}",
×
75
                                    fee, _error
76
                                );
77
                                None
×
78
                            }
79
                        },
80
                ("minfeerate", fee_rate) =>
20✔
81
                    params.min_fee_rate = match fee_rate.parse::<f32>() {
12✔
82
                        Ok(fee_rate_sat_per_vb) => {
12✔
83
                            // TODO Parse with serde when rust-bitcoin supports it
12✔
84
                            let fee_rate_sat_per_kwu = fee_rate_sat_per_vb * 250.0_f32;
12✔
85
                            // since it's a minnimum, we want to round up
12✔
86
                            FeeRate::from_sat_per_kwu(fee_rate_sat_per_kwu.ceil() as u64)
12✔
87
                        }
88
                        Err(_) => return Err(Error::FeeRate),
×
89
                    },
90
                ("disableoutputsubstitution", v) =>
8✔
91
                    params.disable_output_substitution = v == "true",
×
92
                #[cfg(feature = "multiparty")]
93
                ("optimisticmerge", v) => params.optimistic_merge = v == "true",
8✔
UNCOV
94
                _ => (),
×
95
            }
96
        }
97

98
        match (max_additional_fee_contribution, additional_fee_output_index) {
24✔
99
            (Some(amount), Some(index)) =>
15✔
100
                params.additional_fee_contribution = Some((amount, index)),
15✔
101
            (Some(_), None) | (None, Some(_)) => {
102
                warn!("only one additional-fee parameter specified: {:?}", params);
×
103
            }
104
            _ => (),
9✔
105
        }
106

107
        log::debug!("parsed optional parameters: {:?}", params);
24✔
108
        Ok(params)
24✔
109
    }
24✔
110
}
111

112
#[derive(Debug)]
113
pub(crate) enum Error {
114
    UnknownVersion { supported_versions: &'static [usize] },
115
    FeeRate,
116
}
117

118
impl fmt::Display for Error {
119
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
×
120
        match self {
×
121
            Error::UnknownVersion { .. } => write!(f, "unknown version"),
×
122
            Error::FeeRate => write!(f, "could not parse feerate"),
×
123
        }
124
    }
×
125
}
126

127
impl std::error::Error for Error {
128
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
×
129
}
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