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