lightning_signer/policy/
error.rs

1#[cfg(feature = "use_backtrace")]
2use backtrace::Backtrace;
3
4use ValidationErrorKind::*;
5
6use crate::prelude::*;
7
8/// Kind of validation error
9#[derive(Clone, Debug, PartialEq)]
10pub enum ValidationErrorKind {
11    /// The transaction could not be parsed or had non-standard elements
12    TransactionFormat(String),
13    /// A scriptPubkey could not be parsed or was non-standard for Lightning
14    ScriptFormat(String),
15    /// A script element didn't match the channel setup
16    Mismatch(String),
17    /// A policy was violated
18    Policy(String),
19    /// A policy was temporarily violated, but a retry is possible
20    /// (e.g. the funding is not yet considered confirmed because
21    /// the oracle is behind)
22    TemporaryPolicy(String),
23    /// A layer-1 transaction outputs to unknown destinations.
24    /// Includes the list of tx output indices that are unknown.
25    UnknownDestinations(String, Vec<usize>),
26}
27
28// Explicit PartialEq which ignores backtrace.
29impl PartialEq for ValidationError {
30    fn eq(&self, other: &ValidationError) -> bool {
31        self.kind == other.kind
32    }
33}
34
35/// Validation error
36#[derive(Clone)]
37pub struct ValidationError {
38    /// The kind of error
39    pub kind: ValidationErrorKind,
40    /// A non-resolved backtrace
41    #[cfg(feature = "use_backtrace")]
42    pub bt: Backtrace,
43}
44
45impl ValidationError {
46    /// Resolve the backtrace for display to the user
47    #[cfg(feature = "use_backtrace")]
48    pub fn resolved_backtrace(&self) -> Backtrace {
49        let mut mve = self.clone();
50        mve.bt.resolve();
51        mve.bt
52    }
53
54    /// Return a new ValidationError with the message prepended
55    pub fn prepend_msg(&self, premsg: String) -> ValidationError {
56        let modkind = match &self.kind {
57            TransactionFormat(s0) => TransactionFormat(premsg + &s0),
58            ScriptFormat(s0) => ScriptFormat(premsg + &s0),
59            Mismatch(s0) => Mismatch(premsg + &s0),
60            Policy(s0) => Policy(premsg + &s0),
61            TemporaryPolicy(s0) => TemporaryPolicy(premsg + &s0),
62            UnknownDestinations(s0, indices) => UnknownDestinations(premsg + &s0, indices.clone()),
63        };
64        ValidationError {
65            kind: modkind,
66            #[cfg(feature = "use_backtrace")]
67            bt: self.bt.clone(),
68        }
69    }
70}
71
72impl core::fmt::Display for ValidationError {
73    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
74        write!(f, "{:?}", self.kind)
75    }
76}
77
78impl core::fmt::Debug for ValidationError {
79    #[cfg(not(feature = "use_backtrace"))]
80    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
81        f.debug_struct("ValidationError").field("kind", &self.kind).finish()
82    }
83    #[cfg(feature = "use_backtrace")]
84    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
85        f.debug_struct("ValidationError")
86            .field("kind", &self.kind)
87            .field("bt", &self.resolved_backtrace())
88            .finish()
89    }
90}
91
92impl Into<String> for ValidationError {
93    fn into(self) -> String {
94        match self.kind {
95            TransactionFormat(s) => "transaction format: ".to_string() + &s,
96            ScriptFormat(s) => "script format: ".to_string() + &s,
97            Mismatch(s) => "script template mismatch: ".to_string() + &s,
98            Policy(s) => "policy failure: ".to_string() + &s,
99            TemporaryPolicy(s) => "temporary policy failure: ".to_string() + &s,
100            UnknownDestinations(s, indices) => {
101                format!("unknown destinations: {} {:?}", s, indices)
102            }
103        }
104    }
105}
106
107pub(crate) fn transaction_format_error(msg: impl Into<String>) -> ValidationError {
108    ValidationError {
109        kind: TransactionFormat(msg.into()),
110        #[cfg(feature = "use_backtrace")]
111        bt: Backtrace::new_unresolved(),
112    }
113}
114
115pub(crate) fn script_format_error(msg: impl Into<String>) -> ValidationError {
116    ValidationError {
117        kind: ScriptFormat(msg.into()),
118        #[cfg(feature = "use_backtrace")]
119        bt: Backtrace::new_unresolved(),
120    }
121}
122
123pub(crate) fn mismatch_error(msg: impl Into<String>) -> ValidationError {
124    ValidationError {
125        kind: Mismatch(msg.into()),
126        #[cfg(feature = "use_backtrace")]
127        bt: Backtrace::new_unresolved(),
128    }
129}
130
131pub(crate) fn policy_error(msg: impl Into<String>) -> ValidationError {
132    ValidationError {
133        kind: Policy(msg.into()),
134        #[cfg(feature = "use_backtrace")]
135        bt: Backtrace::new_unresolved(),
136    }
137}
138
139pub(crate) fn temporary_policy_error(msg: impl Into<String>) -> ValidationError {
140    ValidationError {
141        kind: TemporaryPolicy(msg.into()),
142        #[cfg(feature = "use_backtrace")]
143        bt: Backtrace::new_unresolved(),
144    }
145}
146
147pub(crate) fn unknown_destinations_error(unknowns: Vec<usize>) -> ValidationError {
148    ValidationError {
149        kind: UnknownDestinations("".to_string(), unknowns),
150        #[cfg(feature = "use_backtrace")]
151        bt: Backtrace::new_unresolved(),
152    }
153}
154
155// Ignore obj and tag for now, no filtering allowed
156#[allow(unused)]
157macro_rules! transaction_format_err {
158	($obj:expr, $tag:tt, $($arg:tt)*) => (
159            return Err(transaction_format_error(format!(
160                "{}: {}",
161                short_function!(),
162                format!($($arg)*)
163            )))
164        )
165}
166
167/// Return a policy error from the current function, by invoking
168/// policy_error on the policy object.
169#[doc(hidden)]
170#[macro_export]
171#[allow(unused)]
172macro_rules! policy_err {
173	($obj:expr, $tag:tt, $($arg:tt)*) => (
174        $obj.policy().policy_error($tag.into(), format!(
175            "{}: {}",
176            short_function!(),
177            format!($($arg)*)
178        ))?
179    )
180}
181
182/// Return a policy error from the current function, by invoking
183/// temporary_policy_error on the policy object.
184#[doc(hidden)]
185#[macro_export]
186#[allow(unused)]
187macro_rules! temporary_policy_err {
188	($obj:expr, $tag:tt, $($arg:tt)*) => (
189        $obj.policy().temporary_policy_error($tag.into(), format!(
190            "{}: {}",
191            short_function!(),
192            format!($($arg)*)
193        ))?
194    )
195}
196
197#[allow(unused)]
198#[macro_export]
199/// Log at the matching policy error level (ERROR or WARN).
200macro_rules! policy_log {
201	($obj:expr, $tag:tt, $($arg:tt)*) => (
202        $obj.policy().policy_log($tag.into(), format!(
203            "{}: {}",
204            short_function!(),
205            format!($($arg)*)
206        ))
207    )
208}
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213
214    #[test]
215    fn validation_error_test() {
216        assert_eq!(
217            format!("{}", transaction_format_error("testing".to_string())),
218            "TransactionFormat(\"testing\")"
219        );
220        assert_eq!(
221            Into::<String>::into(transaction_format_error("testing".to_string())),
222            "transaction format: testing"
223        );
224        assert_eq!(
225            format!("{}", script_format_error("testing".to_string())),
226            "ScriptFormat(\"testing\")"
227        );
228        assert_eq!(
229            Into::<String>::into(script_format_error("testing".to_string())),
230            "script format: testing"
231        );
232        assert_eq!(format!("{}", mismatch_error("testing".to_string())), "Mismatch(\"testing\")");
233        assert_eq!(
234            Into::<String>::into(mismatch_error("testing".to_string())),
235            "script template mismatch: testing"
236        );
237        assert_eq!(format!("{}", policy_error("testing".to_string())), "Policy(\"testing\")");
238        assert_eq!(
239            Into::<String>::into(policy_error("testing".to_string())),
240            "policy failure: testing"
241        );
242    }
243}