psbt_v2/v2/
error.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! PSBT v0 errors.
4
5use core::fmt;
6
7use bitcoin::sighash::{self, EcdsaSighashType, NonStandardSighashTypeError};
8use bitcoin::{transaction, PublicKey};
9
10use crate::error::{write_err, FundingUtxoError};
11use crate::v2::map::{global, input, output};
12
13/// Error while deserializing a PSBT.
14///
15/// This error is returned when deserializing a complete PSBT, not for deserializing parts
16/// of it or individual data types.
17#[derive(Debug)]
18#[non_exhaustive]
19pub enum DeserializeError {
20    /// Invalid magic bytes, expected the ASCII for "psbt" serialized in most significant byte order.
21    // TODO: Consider adding the invalid bytes.
22    InvalidMagic,
23    /// The separator for a PSBT must be `0xff`.
24    // TODO: Consider adding the invalid separator byte.
25    InvalidSeparator,
26    /// Signals that there are no more key-value pairs in a key-value map.
27    NoMorePairs,
28    /// Error decoding the global map.
29    DecodeGlobal(global::DecodeError),
30    /// Error decoding an input map.
31    DecodeInput(input::DecodeError),
32    /// Error decoding an output map.
33    DecodeOutput(output::DecodeError),
34}
35
36impl fmt::Display for DeserializeError {
37    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { todo!() }
38}
39
40#[cfg(feature = "std")]
41impl std::error::Error for DeserializeError {
42    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { todo!() }
43}
44
45impl From<global::DecodeError> for DeserializeError {
46    fn from(e: global::DecodeError) -> Self { Self::DecodeGlobal(e) }
47}
48
49impl From<input::DecodeError> for DeserializeError {
50    fn from(e: input::DecodeError) -> Self { Self::DecodeInput(e) }
51}
52
53impl From<output::DecodeError> for DeserializeError {
54    fn from(e: output::DecodeError) -> Self { Self::DecodeOutput(e) }
55}
56
57/// Input index out of bounds (actual index, maximum index allowed).
58#[derive(Debug, Clone, PartialEq, Eq)]
59#[non_exhaustive]
60pub enum IndexOutOfBoundsError {
61    /// The index is out of bounds for the `psbt.inputs` vector.
62    Inputs {
63        /// Attempted index access.
64        index: usize,
65        /// Length of the PBST inputs vector.
66        length: usize,
67    },
68    /// The index greater than the `psbt.global.input_count`.
69    Count {
70        /// Attempted index access.
71        index: usize,
72        /// Global input count.
73        count: usize,
74    },
75}
76
77impl fmt::Display for IndexOutOfBoundsError {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        use IndexOutOfBoundsError::*;
80
81        match *self {
82            Inputs { ref index, ref length } => write!(
83                f,
84                "index {} is out-of-bounds for PSBT inputs vector length {}",
85                index, length
86            ),
87            Count { ref index, ref count } =>
88                write!(f, "index {} is greater global.input_count {}", index, count),
89        }
90    }
91}
92
93#[cfg(feature = "std")]
94impl std::error::Error for IndexOutOfBoundsError {
95    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
96        use IndexOutOfBoundsError::*;
97
98        match *self {
99            Inputs { .. } | Count { .. } => None,
100        }
101    }
102}
103
104/// Errors encountered while calculating the sighash message.
105#[derive(Debug, Clone, PartialEq, Eq)]
106#[non_exhaustive]
107pub enum SignError {
108    /// Input index out of bounds.
109    IndexOutOfBounds(IndexOutOfBoundsError),
110    /// Invalid Sighash type.
111    InvalidSighashType,
112    /// Missing input utxo.
113    MissingInputUtxo,
114    /// Missing Redeem script.
115    MissingRedeemScript,
116    /// Missing spending utxo.
117    FundingUtxo(FundingUtxoError),
118    /// Missing witness script.
119    MissingWitnessScript,
120    /// Signing algorithm and key type does not match.
121    MismatchedAlgoKey,
122    /// Attempted to ECDSA sign an non-ECDSA input.
123    NotEcdsa,
124    /// The `scriptPubkey` is not a P2WPKH script.
125    NotWpkh,
126    /// Sighash computation error (segwit v0 input).
127    SegwitV0Sighash(transaction::InputsIndexError),
128    /// Sighash computation error (p2wpkh input).
129    P2wpkhSighash(sighash::P2wpkhError),
130    /// Sighash computation error (taproot input).
131    TaprootError(sighash::TaprootError),
132    /// Unable to determine the output type.
133    UnknownOutputType,
134    /// Unable to find key.
135    KeyNotFound,
136    /// Attempt to sign an input with the wrong signing algorithm.
137    WrongSigningAlgorithm,
138    /// Signing request currently unsupported.
139    Unsupported,
140}
141
142impl fmt::Display for SignError {
143    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144        use SignError::*;
145
146        match *self {
147            IndexOutOfBounds(ref e) => write_err!(f, "index out of bounds"; e),
148            InvalidSighashType => write!(f, "invalid sighash type"),
149            MissingInputUtxo => write!(f, "missing input utxo in PBST"),
150            MissingRedeemScript => write!(f, "missing redeem script"),
151            FundingUtxo(ref e) => write_err!(f, "input funding utxo error"; e),
152            MissingWitnessScript => write!(f, "missing witness script"),
153            MismatchedAlgoKey => write!(f, "signing algorithm and key type does not match"),
154            NotEcdsa => write!(f, "attempted to ECDSA sign an non-ECDSA input"),
155            NotWpkh => write!(f, "the scriptPubkey is not a P2WPKH script"),
156            SegwitV0Sighash(ref e) => write_err!(f, "segwit v0 sighash"; e),
157            P2wpkhSighash(ref e) => write_err!(f, "p2wpkh sighash"; e),
158            TaprootError(ref e) => write_err!(f, "taproot sighash"; e),
159            UnknownOutputType => write!(f, "unable to determine the output type"),
160            KeyNotFound => write!(f, "unable to find key"),
161            WrongSigningAlgorithm =>
162                write!(f, "attempt to sign an input with the wrong signing algorithm"),
163            Unsupported => write!(f, "signing request currently unsupported"),
164        }
165    }
166}
167
168#[cfg(feature = "std")]
169impl std::error::Error for SignError {
170    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
171        use SignError::*;
172
173        match *self {
174            SegwitV0Sighash(ref e) => Some(e),
175            P2wpkhSighash(ref e) => Some(e),
176            TaprootError(ref e) => Some(e),
177            IndexOutOfBounds(ref e) => Some(e),
178            FundingUtxo(ref e) => Some(e),
179            InvalidSighashType
180            | MissingInputUtxo
181            | MissingRedeemScript
182            | MissingWitnessScript
183            | MismatchedAlgoKey
184            | NotEcdsa
185            | NotWpkh
186            | UnknownOutputType
187            | KeyNotFound
188            | WrongSigningAlgorithm
189            | Unsupported => None,
190        }
191    }
192}
193
194impl From<sighash::P2wpkhError> for SignError {
195    fn from(e: sighash::P2wpkhError) -> Self { Self::P2wpkhSighash(e) }
196}
197
198impl From<IndexOutOfBoundsError> for SignError {
199    fn from(e: IndexOutOfBoundsError) -> Self { Self::IndexOutOfBounds(e) }
200}
201
202impl From<sighash::TaprootError> for SignError {
203    fn from(e: sighash::TaprootError) -> Self { SignError::TaprootError(e) }
204}
205
206impl From<FundingUtxoError> for SignError {
207    fn from(e: FundingUtxoError) -> Self { Self::FundingUtxo(e) }
208}
209
210/// Error when passing an un-modifiable PSBT to a `Constructor`.
211#[derive(Debug, Clone, PartialEq, Eq)]
212#[non_exhaustive]
213pub enum PsbtNotModifiableError {
214    /// The outputs modifiable flag is not set.
215    Outputs(OutputsNotModifiableError),
216    /// The inputs modifiable flag is not set.
217    Inputs(InputsNotModifiableError),
218}
219
220impl fmt::Display for PsbtNotModifiableError {
221    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222        use PsbtNotModifiableError::*;
223
224        match *self {
225            Outputs(ref e) => write_err!(f, "outputs not modifiable"; e),
226            Inputs(ref e) => write_err!(f, "inputs not modifiable"; e),
227        }
228    }
229}
230
231#[cfg(feature = "std")]
232impl std::error::Error for PsbtNotModifiableError {
233    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
234        use PsbtNotModifiableError::*;
235
236        match *self {
237            Outputs(ref e) => Some(e),
238            Inputs(ref e) => Some(e),
239        }
240    }
241}
242
243impl From<InputsNotModifiableError> for PsbtNotModifiableError {
244    fn from(e: InputsNotModifiableError) -> Self { Self::Inputs(e) }
245}
246
247impl From<OutputsNotModifiableError> for PsbtNotModifiableError {
248    fn from(e: OutputsNotModifiableError) -> Self { Self::Outputs(e) }
249}
250
251/// Error when passing an PSBT with inputs not modifiable to an input adding `Constructor`.
252#[derive(Debug, Clone, PartialEq, Eq)]
253#[non_exhaustive]
254pub struct InputsNotModifiableError;
255
256impl fmt::Display for InputsNotModifiableError {
257    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258        f.write_str("PSBT does not have the inputs modifiable flag set")
259    }
260}
261
262#[cfg(feature = "std")]
263impl std::error::Error for InputsNotModifiableError {}
264
265/// Error when passing an PSBT with outputs not modifiable to an output adding `Constructor`.
266#[derive(Debug, Clone, PartialEq, Eq)]
267#[non_exhaustive]
268pub struct OutputsNotModifiableError;
269
270impl fmt::Display for OutputsNotModifiableError {
271    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
272        f.write_str("PSBT does not have the outputs modifiable flag set")
273    }
274}
275
276#[cfg(feature = "std")]
277impl std::error::Error for OutputsNotModifiableError {
278    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
279}
280
281/// The input is not 100% unsigned.
282#[derive(Debug, Clone, PartialEq, Eq)]
283#[non_exhaustive]
284pub enum NotUnsignedError {
285    /// Input has already been finalized.
286    Finalized,
287    /// Input already has signature data.
288    SigData,
289}
290
291impl fmt::Display for NotUnsignedError {
292    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
293        use NotUnsignedError::*;
294
295        match *self {
296            Finalized => f.write_str("input has already been finalized"),
297            SigData => f.write_str("input already has signature data"),
298        }
299    }
300}
301
302#[cfg(feature = "std")]
303impl std::error::Error for NotUnsignedError {
304    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
305}
306
307/// Unable to determine lock time, multiple inputs have conflicting locking requirements.
308#[derive(Debug, Clone, PartialEq, Eq)]
309#[non_exhaustive]
310pub struct DetermineLockTimeError;
311
312impl fmt::Display for DetermineLockTimeError {
313    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314        f.write_str(
315            "unable to determine lock time, multiple inputs have conflicting locking requirements",
316        )
317    }
318}
319
320#[cfg(feature = "std")]
321impl std::error::Error for DetermineLockTimeError {
322    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
323}
324
325// TODO: Consider creating a type that has input_index and E and simplify all these similar error types?
326/// Error checking the partials sigs have correct sighash types.
327#[derive(Debug)]
328pub enum PartialSigsSighashTypeError {
329    /// Non-standard sighash type found in `input.sighash_type` field.
330    NonStandardInputSighashType {
331        /// The input index with the non-standard sighash type.
332        input_index: usize,
333        /// The non-standard sighash type error.
334        error: NonStandardSighashTypeError,
335    },
336    /// Non-standard sighash type found in `input.partial_sigs`.
337    NonStandardPartialSigsSighashType {
338        /// The input index with the non-standard sighash type.
339        input_index: usize,
340        /// The non-standard sighash type error.
341        error: NonStandardSighashTypeError,
342    },
343    /// Wrong sighash flag in partial signature.
344    WrongSighashFlag {
345        /// The input index with the wrong sighash flag.
346        input_index: usize,
347        /// The sighash type we got.
348        got: EcdsaSighashType,
349        /// The sighash type we require.
350        required: EcdsaSighashType,
351        /// The associated pubkey (key into the `input.partial_sigs` map).
352        pubkey: PublicKey,
353    },
354}
355
356impl fmt::Display for PartialSigsSighashTypeError {
357    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
358        use PartialSigsSighashTypeError::*;
359
360        match *self {
361            NonStandardInputSighashType { input_index, ref error } =>
362                write_err!(f, "non-standard sighash type for input {} in sighash_type field", input_index; error),
363            NonStandardPartialSigsSighashType { input_index, ref error } =>
364                write_err!(f, "non-standard sighash type for input {} in partial_sigs", input_index; error),
365            WrongSighashFlag { input_index, got, required, pubkey } => write!(
366                f,
367                "wrong sighash flag for input {} (got: {}, required: {}) pubkey: {}",
368                input_index, got, required, pubkey
369            ),
370        }
371    }
372}
373
374#[cfg(feature = "std")]
375impl std::error::Error for PartialSigsSighashTypeError {
376    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
377        use PartialSigsSighashTypeError::*;
378
379        // TODO: Is this correct for a struct error fields?
380        match *self {
381            NonStandardInputSighashType { input_index: _, ref error } => Some(error),
382            NonStandardPartialSigsSighashType { input_index: _, ref error } => Some(error),
383            WrongSighashFlag { .. } => None,
384        }
385    }
386}