feat: add psbt error

This commit is contained in:
thunderbiscuit 2024-05-10 16:07:34 -04:00
parent 5d41984377
commit 7fc8da5451
No known key found for this signature in database
GPG Key ID: 88253696EB836462
3 changed files with 327 additions and 1 deletions

View File

@ -170,6 +170,43 @@ interface PersistenceError {
Write(string error_message); Write(string error_message);
}; };
[Error]
interface PsbtError {
InvalidMagic();
MissingUtxo();
InvalidSeparator();
PsbtUtxoOutOfBounds();
InvalidKey(string key);
InvalidProprietaryKey();
DuplicateKey(string key);
UnsignedTxHasScriptSigs();
UnsignedTxHasScriptWitnesses();
MustHaveUnsignedTx();
NoMorePairs();
UnexpectedUnsignedTx();
NonStandardSighashType(u32 sighash);
InvalidHash(string hash);
InvalidPreimageHashPair();
CombineInconsistentKeySources(string xpub);
ConsensusEncoding(string encoding_error);
NegativeFee();
FeeOverflow();
InvalidPublicKey(string error_message);
InvalidSecp256k1PublicKey(string secp256k1_error);
InvalidXOnlyPublicKey();
InvalidEcdsaSignature(string error_message);
InvalidTaprootSignature(string error_message);
InvalidControlBlock();
InvalidLeafVersion();
Taproot();
TapTree(string error_message);
XPubKey();
Version(string error_message);
PartialDataConsumption();
Io(string error_message);
OtherPsbtError();
};
[Error] [Error]
interface PsbtParseError { interface PsbtParseError {
PsbtEncoding(string error_message); PsbtEncoding(string error_message);

View File

@ -1,9 +1,10 @@
use crate::bitcoin::OutPoint; use crate::bitcoin::{OutPoint};
use bdk::bitcoin::address::ParseError; use bdk::bitcoin::address::ParseError;
use bdk::bitcoin::bip32::Error as BdkBip32Error; use bdk::bitcoin::bip32::Error as BdkBip32Error;
use bdk::bitcoin::psbt::PsbtParseError as BdkPsbtParseError; use bdk::bitcoin::psbt::PsbtParseError as BdkPsbtParseError;
use bdk::bitcoin::Network; use bdk::bitcoin::Network;
use bdk::bitcoin::psbt::Error as BdkPsbtError;
use bdk::chain::tx_graph::CalculateFeeError as BdkCalculateFeeError; use bdk::chain::tx_graph::CalculateFeeError as BdkCalculateFeeError;
use bdk::descriptor::DescriptorError as BdkDescriptorError; use bdk::descriptor::DescriptorError as BdkDescriptorError;
use bdk::keys::bip39::Error as BdkBip39Error; use bdk::keys::bip39::Error as BdkBip39Error;
@ -28,6 +29,8 @@ use bdk::bitcoin::psbt::ExtractTxError as BdkExtractTxError;
use bdk::chain::local_chain::CannotConnectError as BdkCannotConnectError; use bdk::chain::local_chain::CannotConnectError as BdkCannotConnectError;
use bdk::KeychainKind; use bdk::KeychainKind;
use std::convert::TryInto;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// error definitions // error definitions
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -418,6 +421,136 @@ pub enum PersistenceError {
Write { error_message: String }, Write { error_message: String },
} }
#[derive(Debug, thiserror::Error)]
pub enum PsbtError {
#[error("invalid magic")]
InvalidMagic,
#[error("UTXO information is not present in PSBT")]
MissingUtxo,
#[error("invalid separator")]
InvalidSeparator,
#[error("output index is out of bounds of non witness script output array")]
PsbtUtxoOutOfBounds,
#[error("invalid key: {key}")]
InvalidKey {
key: String,
},
#[error("non-proprietary key type found when proprietary key was expected")]
InvalidProprietaryKey,
#[error("duplicate key: {key}")]
DuplicateKey {
key: String,
},
#[error("the unsigned transaction has script sigs")]
UnsignedTxHasScriptSigs,
#[error("the unsigned transaction has script witnesses")]
UnsignedTxHasScriptWitnesses,
#[error("partially signed transactions must have an unsigned transaction")]
MustHaveUnsignedTx,
#[error("no more key-value pairs for this psbt map")]
NoMorePairs,
// Note: this error would be nice to unpack and provide the two transactions
#[error("different unsigned transaction")]
UnexpectedUnsignedTx,
#[error("non-standard sighash type: {sighash}")]
NonStandardSighashType {
sighash: u32,
},
#[error("invalid hash when parsing slice: {hash}")]
InvalidHash {
hash: String,
},
// Note: to provide the data returned in Rust, we need to dereference the fields
#[error("preimage does not match")]
InvalidPreimageHashPair,
#[error("combine conflict: {xpub}")]
CombineInconsistentKeySources {
xpub: String,
},
#[error("bitcoin consensus encoding error: {encoding_error}")]
ConsensusEncoding {
encoding_error: String,
},
#[error("PSBT has a negative fee which is not allowed")]
NegativeFee,
#[error("integer overflow in fee calculation")]
FeeOverflow,
#[error("invalid public key {error_message}")]
InvalidPublicKey {
error_message: String
},
#[error("invalid secp256k1 public key: {secp256k1_error}")]
InvalidSecp256k1PublicKey {
secp256k1_error: String
},
#[error("invalid xonly public key")]
InvalidXOnlyPublicKey,
#[error("invalid ECDSA signature: {error_message}")]
InvalidEcdsaSignature {
error_message: String
},
#[error("invalid taproot signature: {error_message}")]
InvalidTaprootSignature {
error_message: String
},
#[error("invalid control block")]
InvalidControlBlock,
#[error("invalid leaf version")]
InvalidLeafVersion,
#[error("taproot error")]
Taproot,
#[error("taproot tree error: {error_message}")]
TapTree {
error_message: String
},
#[error("xpub key error")]
XPubKey,
#[error("version error: {error_message}")]
Version {
error_message: String
},
#[error("data not consumed entirely when explicitly deserializing")]
PartialDataConsumption,
#[error("I/O error: {error_message}")]
Io {
error_message: String
},
#[error("other PSBT error")]
OtherPsbtError,
}
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum PsbtParseError { pub enum PsbtParseError {
#[error("error in internal psbt data structure: {error_message}")] #[error("error in internal psbt data structure: {error_message}")]
@ -973,6 +1106,52 @@ impl From<std::io::Error> for PersistenceError {
} }
} }
impl From<BdkPsbtError> for PsbtError {
fn from(error: BdkPsbtError) -> Self {
match error {
BdkPsbtError::InvalidMagic => PsbtError::InvalidMagic,
BdkPsbtError::MissingUtxo => PsbtError::MissingUtxo,
BdkPsbtError::InvalidSeparator => PsbtError::InvalidSeparator,
BdkPsbtError::PsbtUtxoOutOfbounds => PsbtError::PsbtUtxoOutOfBounds,
BdkPsbtError::InvalidKey(key) => PsbtError::InvalidKey { key: key.to_string() },
BdkPsbtError::InvalidProprietaryKey => PsbtError::InvalidProprietaryKey,
BdkPsbtError::DuplicateKey(key) => PsbtError::DuplicateKey { key: key.to_string() },
BdkPsbtError::UnsignedTxHasScriptSigs => PsbtError::UnsignedTxHasScriptSigs,
BdkPsbtError::UnsignedTxHasScriptWitnesses => PsbtError::UnsignedTxHasScriptWitnesses,
BdkPsbtError::MustHaveUnsignedTx => PsbtError::MustHaveUnsignedTx,
BdkPsbtError::NoMorePairs => PsbtError::NoMorePairs,
BdkPsbtError::UnexpectedUnsignedTx { .. } => PsbtError::UnexpectedUnsignedTx,
BdkPsbtError::NonStandardSighashType(sighash) => {
PsbtError::NonStandardSighashType { sighash }
},
BdkPsbtError::InvalidHash(hash) => PsbtError::InvalidHash { hash: hash.to_string() },
BdkPsbtError::InvalidPreimageHashPair { .. } => PsbtError::InvalidPreimageHashPair,
BdkPsbtError::CombineInconsistentKeySources(xpub) => {
PsbtError::CombineInconsistentKeySources { xpub: xpub.to_string() }
},
BdkPsbtError::ConsensusEncoding(encoding_error) => {
PsbtError::ConsensusEncoding { encoding_error: encoding_error.to_string() }
},
BdkPsbtError::NegativeFee => PsbtError::NegativeFee,
BdkPsbtError::FeeOverflow => PsbtError::FeeOverflow,
BdkPsbtError::InvalidPublicKey(e) => PsbtError::InvalidPublicKey { error_message: e.to_string() },
BdkPsbtError::InvalidSecp256k1PublicKey(e) => PsbtError::InvalidSecp256k1PublicKey { secp256k1_error: e.to_string() },
BdkPsbtError::InvalidXOnlyPublicKey => PsbtError::InvalidXOnlyPublicKey,
BdkPsbtError::InvalidEcdsaSignature(e) => PsbtError::InvalidEcdsaSignature { error_message: e.to_string() },
BdkPsbtError::InvalidTaprootSignature(e) => PsbtError::InvalidTaprootSignature { error_message: e.to_string() },
BdkPsbtError::InvalidControlBlock => PsbtError::InvalidControlBlock,
BdkPsbtError::InvalidLeafVersion => PsbtError::InvalidLeafVersion,
BdkPsbtError::Taproot(_) => PsbtError::Taproot,
BdkPsbtError::TapTree(e) => PsbtError::TapTree { error_message: e.to_string() },
BdkPsbtError::XPubKey(_) => PsbtError::XPubKey,
BdkPsbtError::Version(e) => PsbtError::Version { error_message: e.to_string() },
BdkPsbtError::PartialDataConsumption => PsbtError::PartialDataConsumption,
BdkPsbtError::Io(e) => PsbtError::Io { error_message: e.to_string() },
_ => PsbtError::OtherPsbtError,
}
}
}
impl From<BdkPsbtParseError> for PsbtParseError { impl From<BdkPsbtParseError> for PsbtParseError {
fn from(error: BdkPsbtParseError) -> Self { fn from(error: BdkPsbtParseError) -> Self {
match error { match error {
@ -1750,6 +1929,115 @@ mod test {
} }
} }
#[test]
fn test_error_psbt() {
let cases = vec![
(PsbtError::InvalidMagic, "invalid magic"),
(PsbtError::MissingUtxo, "UTXO information is not present in PSBT"),
(PsbtError::InvalidSeparator, "invalid separator"),
(PsbtError::PsbtUtxoOutOfBounds, "output index is out of bounds of non witness script output array"),
(
PsbtError::InvalidKey {
key: "key".to_string(),
},
"invalid key: key",
),
(PsbtError::InvalidProprietaryKey, "non-proprietary key type found when proprietary key was expected"),
(
PsbtError::DuplicateKey {
key: "key".to_string(),
},
"duplicate key: key",
),
(PsbtError::UnsignedTxHasScriptSigs, "the unsigned transaction has script sigs"),
(
PsbtError::UnsignedTxHasScriptWitnesses,
"the unsigned transaction has script witnesses",
),
(PsbtError::MustHaveUnsignedTx, "partially signed transactions must have an unsigned transaction"),
(PsbtError::NoMorePairs, "no more key-value pairs for this psbt map"),
(PsbtError::UnexpectedUnsignedTx, "different unsigned transaction"),
(
PsbtError::NonStandardSighashType { sighash: 200 },
"non-standard sighash type: 200",
),
(
PsbtError::InvalidHash {
hash: "abcde".to_string(),
},
"invalid hash when parsing slice: abcde",
),
(PsbtError::InvalidPreimageHashPair, "preimage does not match"),
(
PsbtError::CombineInconsistentKeySources {
xpub: "xpub".to_string(),
},
"combine conflict: xpub",
),
(
PsbtError::ConsensusEncoding {
encoding_error: "encoding error".to_string(),
},
"bitcoin consensus encoding error: encoding error",
),
(PsbtError::NegativeFee, "PSBT has a negative fee which is not allowed"),
(PsbtError::FeeOverflow, "integer overflow in fee calculation"),
(
PsbtError::InvalidPublicKey {
error_message: "invalid public key".to_string(),
},
"invalid public key invalid public key",
),
(
PsbtError::InvalidSecp256k1PublicKey {
secp256k1_error: "invalid secp256k1 public key".to_string(),
},
"invalid secp256k1 public key: invalid secp256k1 public key",
),
(PsbtError::InvalidXOnlyPublicKey, "invalid xonly public key"),
(
PsbtError::InvalidEcdsaSignature {
error_message: "invalid ecdsa signature".to_string(),
},
"invalid ECDSA signature: invalid ecdsa signature",
),
(
PsbtError::InvalidTaprootSignature {
error_message: "invalid taproot signature".to_string(),
},
"invalid taproot signature: invalid taproot signature",
),
(PsbtError::InvalidControlBlock, "invalid control block"),
(PsbtError::InvalidLeafVersion, "invalid leaf version"),
(PsbtError::Taproot, "taproot error"),
(
PsbtError::TapTree {
error_message: "tap tree error".to_string(),
},
"taproot tree error: tap tree error",
),
(PsbtError::XPubKey, "xpub key error"),
(
PsbtError::Version {
error_message: "version error".to_string(),
},
"version error: version error",
),
(PsbtError::PartialDataConsumption, "data not consumed entirely when explicitly deserializing"),
(
PsbtError::Io {
error_message: "io error".to_string(),
},
"I/O error: io error",
),
(PsbtError::OtherPsbtError, "other PSBT error"),
];
for (error, expected_message) in cases {
assert_eq!(error.to_string(), expected_message);
}
}
#[test] #[test]
fn test_error_psbt_parse() { fn test_error_psbt_parse() {
let cases = vec![ let cases = vec![

View File

@ -33,6 +33,7 @@ use crate::error::FeeRateError;
use crate::error::InspectError; use crate::error::InspectError;
use crate::error::ParseAmountError; use crate::error::ParseAmountError;
use crate::error::PersistenceError; use crate::error::PersistenceError;
use crate::error::PsbtError;
use crate::error::PsbtParseError; use crate::error::PsbtParseError;
use crate::error::SignerError; use crate::error::SignerError;
use crate::error::TransactionError; use crate::error::TransactionError;