use crate::transactions::INSANE_FEES;
use bitcoinconsensus::Error as LibConsensusError;
use miniscript::{
bitcoin::{
consensus::encode::Error as EncodeError,
secp256k1,
util::psbt::{Input as PsbtInput, Output as PsbtOutput},
},
policy::compiler::CompilerError,
};
use std::{convert::From, error, fmt};
#[derive(Debug)]
pub enum ScriptCreationError {
BadParameters,
NonWildcardKeys,
NoXpub,
DerivedKeyParsing,
PolicyCompilation(CompilerError),
MiniscriptError(miniscript::Error),
}
impl fmt::Display for ScriptCreationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::BadParameters => write!(f, "Bad parameters"),
Self::PolicyCompilation(e) => write!(f, "Policy compilation error: '{}'", e),
Self::MiniscriptError(e) => write!(f, "Miniscript error: '{}'", e),
Self::NonWildcardKeys => write!(f, "Not all xpubs were wildcard"),
Self::NoXpub => write!(f, "No xpub present in generalist descriptor"),
Self::DerivedKeyParsing => write!(f, "Invalid derived public key, must always be of the form '[fingerprint/index]<66 hex chars>'"),
}
}
}
impl From<CompilerError> for ScriptCreationError {
fn from(e: CompilerError) -> Self {
Self::PolicyCompilation(e)
}
}
impl From<miniscript::Error> for ScriptCreationError {
fn from(e: miniscript::Error) -> Self {
Self::MiniscriptError(e)
}
}
impl PartialEq for ScriptCreationError {
fn eq(&self, other: &Self) -> bool {
self.to_string() == other.to_string()
}
}
impl error::Error for ScriptCreationError {}
#[derive(PartialEq, Debug)]
pub enum TxoutCreationError {
InvalidScriptPubkeyType,
}
impl fmt::Display for TxoutCreationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::InvalidScriptPubkeyType => write!(f, "Invalid ScriptPubKey type"),
}
}
}
impl error::Error for TxoutCreationError {}
#[derive(PartialEq, Eq, Debug)]
pub enum TransactionCreationError {
InsaneAmounts,
InsaneFees,
Dust,
NegativeFees,
TooLarge,
DuplicatedInput,
MissingCpfpTxOut,
InsufficientFunds,
FeerateTooHigh,
}
impl fmt::Display for TransactionCreationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::InsaneAmounts => {
write!(f, "Sum of the outputs value amounts to more than MAX_MONEY")
}
Self::InsaneFees => write!(f, "Fees larger than {} sats", INSANE_FEES),
Self::Dust => write!(f, "Spending or creating a dust output"),
Self::NegativeFees => write!(
f,
"The sum of the inputs value is less than the sum of the outputs value"
),
Self::TooLarge => write!(
f,
"Transaction too large: satisfied it could be >400k weight units"
),
Self::DuplicatedInput => write!(
f,
"Trying to create a Spend transaction with the same prevout twice"
),
Self::MissingCpfpTxOut => write!(
f,
"Attempting to create a CPFP transaction for a tx without a CPFP output"
),
Self::InsufficientFunds => write!(f, "Insufficient funds"),
Self::FeerateTooHigh => write!(
f,
"Feerate too high, can't afford without creating a too small \
output or having negative fees"
),
}
}
}
impl error::Error for TransactionCreationError {}
#[derive(PartialEq, Eq, Debug)]
pub enum InputSatisfactionError {
OutOfBounds,
AlreadyFinalized,
MissingWitnessScript,
InvalidSignature(
secp256k1::Signature,
secp256k1::PublicKey,
secp256k1::Message,
),
}
impl fmt::Display for InputSatisfactionError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::OutOfBounds => write!(f, "Index out of bounds of inputs list"),
Self::AlreadyFinalized => write!(f, "Input was already finalized"),
Self::MissingWitnessScript => write!(
f,
"Missing witness_script field in PSBT input. Wrong sighash function used?"
),
Self::InvalidSignature(sig, pk, hash) => write!(
f,
"Invalid signature '{:x?}' for key '{:x?}' and sighash '{:x?}'",
&sig, &pk, &hash
),
}
}
}
impl error::Error for InputSatisfactionError {}
#[derive(PartialEq, Debug)]
pub enum PsbtValidationError {
InvalidTransactionVersion(i32),
InputCountMismatch(usize, usize),
OutputCountMismatch(usize, usize),
InvalidInputCount(usize),
InvalidOutputCount(usize),
DuplicatedInput,
MissingRevocationInput,
MissingWitnessUtxo(PsbtInput),
MissingInWitnessScript(PsbtInput),
InvalidInWitnessScript(PsbtInput),
MissingOutWitnessScript(PsbtOutput),
InvalidOutWitnessScript(PsbtOutput),
InvalidInputField(PsbtInput),
InvalidOutputField(PsbtOutput),
InvalidPrevoutType(PsbtInput),
InvalidCountOuputWithDerivations(usize),
PartiallyFinalized,
InsaneAmounts,
TransactionTooLarge,
}
impl fmt::Display for PsbtValidationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::InvalidTransactionVersion(v) => write!(f, "Invalid transaction version: '{}'", v),
Self::InputCountMismatch(in_count, psbtin_count) => write!(
f,
"'{}' inputs but '{}' psbt inputs",
in_count, psbtin_count
),
Self::OutputCountMismatch(out_count, psbtout_count) => write!(
f,
"'{}' outputs but '{}' psbt outputs",
out_count, psbtout_count
),
Self::InvalidInputCount(c) => write!(f, "Invalid input count: '{}'", c),
Self::InvalidOutputCount(c) => write!(f, "Invalid output count: '{}'", c),
Self::DuplicatedInput => write!(f, "Transaction contains duplicated inputs"),
Self::MissingRevocationInput => {
write!(f, "Missing P2WSH input for revocation transaction")
}
Self::MissingWitnessUtxo(i) => write!(f, "Missing witness utxo for input '{:#?}'", i),
Self::MissingInWitnessScript(i) => {
write!(f, "Missing witness script for input '{:#?}'", i)
}
Self::InvalidInWitnessScript(i) => {
write!(f, "Invalid witness script for input '{:#?}'", i)
}
Self::MissingOutWitnessScript(o) => {
write!(f, "Missing witness script for output '{:#?}'", o)
}
Self::InvalidOutWitnessScript(o) => {
write!(f, "Invalid witness script for output '{:#?}'", o)
}
Self::InvalidInputField(i) => write!(f, "Invalid field in input: '{:#?}'", i),
Self::InvalidOutputField(o) => write!(f, "Invalid field in output: '{:#?}'", o),
Self::InvalidPrevoutType(i) => write!(
f,
"This input refers to an output of invalid type: '{:#?}'",
i
),
Self::InvalidCountOuputWithDerivations(c) => write!(
f,
"Incorrect number of PSBT outputs with bip32 derivation set: {}.",
c
),
Self::PartiallyFinalized => write!(f, "PSBT contains both final and non-final inputs"),
Self::InsaneAmounts => write!(
f,
"PSBT contains either overflowing amounts or creates more coins than it spends"
),
Self::TransactionTooLarge => write!(
f,
"Transaction too large: satisfied it could be >400k weight units"
),
}
}
}
impl error::Error for PsbtValidationError {}
#[derive(PartialEq, Debug)]
pub enum TransactionSerialisationError {
Encode(String),
Base64Decode(base64::DecodeError),
Validation(PsbtValidationError),
}
impl fmt::Display for TransactionSerialisationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Encode(s) => write!(f, "{}", s),
Self::Base64Decode(e) => write!(f, "Error decoding base64: '{}'", e),
Self::Validation(s) => write!(f, "Invalid Revault transaction: '{}'", s),
}
}
}
impl From<EncodeError> for TransactionSerialisationError {
fn from(e: EncodeError) -> Self {
Self::Encode(e.to_string())
}
}
impl From<base64::DecodeError> for TransactionSerialisationError {
fn from(e: base64::DecodeError) -> Self {
Self::Base64Decode(e)
}
}
impl From<PsbtValidationError> for TransactionSerialisationError {
fn from(e: PsbtValidationError) -> Self {
Self::Validation(e)
}
}
impl error::Error for TransactionSerialisationError {}
#[derive(Debug)]
pub enum Error {
ScriptCreation(ScriptCreationError),
TxoutCreation(TxoutCreationError),
TransactionCreation(TransactionCreationError),
InputSatisfaction(InputSatisfactionError),
TransactionFinalisation(String),
TransactionVerification(LibConsensusError),
TransactionSerialisation(TransactionSerialisationError),
}
impl From<ScriptCreationError> for Error {
fn from(e: ScriptCreationError) -> Self {
Self::ScriptCreation(e)
}
}
impl From<TxoutCreationError> for Error {
fn from(e: TxoutCreationError) -> Self {
Self::TxoutCreation(e)
}
}
impl From<TransactionCreationError> for Error {
fn from(e: TransactionCreationError) -> Self {
Self::TransactionCreation(e)
}
}
impl From<InputSatisfactionError> for Error {
fn from(e: InputSatisfactionError) -> Self {
Self::InputSatisfaction(e)
}
}
impl From<LibConsensusError> for Error {
fn from(e: LibConsensusError) -> Self {
Self::TransactionVerification(e)
}
}
impl From<TransactionSerialisationError> for Error {
fn from(e: TransactionSerialisationError) -> Self {
Self::TransactionSerialisation(e)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::ScriptCreation(ref e) => write!(f, "Revault script creation error: '{}'", e),
Error::TxoutCreation(ref e) => {
write!(f, "Revault transaction output creation error: '{}'", e)
}
Error::TransactionCreation(ref e) => {
write!(f, "Revault transaction creation error: '{}'", e)
}
Error::InputSatisfaction(ref e) => {
write!(f, "Revault input satisfaction error: '{}'", e)
}
Error::TransactionVerification(ref e) => {
write!(f, "Revault transaction verification error: '{:?}'", e)
}
Error::TransactionFinalisation(ref e) => {
write!(f, "Revault transaction finalisation error: '{}'", e)
}
Error::TransactionSerialisation(ref e) => {
write!(f, "Revault transaction serialisation error: '{}'", e)
}
}
}
}
impl error::Error for Error {}