1#[cfg(feature = "use_backtrace")]
2use backtrace::Backtrace;
3
4use ValidationErrorKind::*;
5
6use crate::prelude::*;
7
8#[derive(Clone, Debug, PartialEq)]
10pub enum ValidationErrorKind {
11 TransactionFormat(String),
13 ScriptFormat(String),
15 Mismatch(String),
17 Policy(String),
19 TemporaryPolicy(String),
23 UnknownDestinations(String, Vec<usize>),
26}
27
28impl PartialEq for ValidationError {
30 fn eq(&self, other: &ValidationError) -> bool {
31 self.kind == other.kind && self.tag == other.tag
32 }
33}
34
35#[derive(Clone)]
37pub struct ValidationError {
38 pub tag: String,
40 pub kind: ValidationErrorKind,
42 #[cfg(feature = "use_backtrace")]
44 pub bt: Backtrace,
45}
46
47impl ValidationError {
48 #[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 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#[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#[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#[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]
209macro_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}