use crate::{
error::*,
scripts::*,
transactions::{
utils, RevaultPresignedTransaction, RevaultTransaction, CANCEL_DEPOSIT_MIN_SATS,
INSANE_FEES, MAX_STANDARD_TX_WEIGHT,
},
txins::*,
txouts::*,
};
use miniscript::bitcoin::{
blockdata::constants::max_money, consensus::encode::Decodable,
util::psbt::PartiallySignedTransaction as Psbt, Amount, Network, OutPoint,
};
#[cfg(feature = "use-serde")]
use {
serde::de::{self, Deserialize, Deserializer},
serde::ser::{Serialize, Serializer},
};
use std::convert::TryInto;
impl_revault_transaction!(
CancelTransaction,
doc = "The transaction \"revaulting\" a spend attempt, i.e. spending the unvaulting transaction back to a deposit txo."
);
impl RevaultPresignedTransaction for CancelTransaction {}
impl CancelTransaction {
pub fn new(
unvault_input: UnvaultTxIn,
deposit_descriptor: &DerivedDepositDescriptor,
feerate: Amount,
) -> Result<CancelTransaction, TransactionCreationError> {
let dummy_deposit_txo = DepositTxOut::new(Amount::from_sat(u64::MAX), deposit_descriptor);
let dummy_tx = utils::create_psbt(unvault_input.clone(), dummy_deposit_txo)
.global
.unsigned_tx;
let total_weight = dummy_tx
.get_weight()
.checked_add(unvault_input.txout().max_sat_weight())
.expect("Properly computed weight won't overflow");
let total_weight: u64 = total_weight.try_into().expect("usize in u64");
let fees = feerate
.as_sat()
.checked_mul(total_weight)
.expect("Properly computed weight won't overflow");
assert!(fees < INSANE_FEES);
assert!(
total_weight <= MAX_STANDARD_TX_WEIGHT as u64,
"Single input and single output"
);
let unvault_value = unvault_input.txout().txout().value;
let revault_value = unvault_value
.checked_sub(fees)
.ok_or(TransactionCreationError::FeerateTooHigh)?;
if revault_value < CANCEL_DEPOSIT_MIN_SATS {
return Err(TransactionCreationError::FeerateTooHigh);
}
assert!(
revault_value < max_money(Network::Bitcoin),
"Checked in UnvaultTransaction constructor already"
);
let deposit_txo = DepositTxOut::new(Amount::from_sat(revault_value), deposit_descriptor);
Ok(CancelTransaction(utils::create_psbt(
unvault_input,
deposit_txo,
)))
}
pub fn from_raw_psbt(raw_psbt: &[u8]) -> Result<Self, TransactionSerialisationError> {
let psbt = Decodable::consensus_decode(raw_psbt)?;
let psbt = utils::psbt_common_sanity_checks(psbt)?;
let output_count = psbt.global.unsigned_tx.output.len();
if output_count != 1 {
return Err(PsbtValidationError::InvalidOutputCount(output_count).into());
}
for output in psbt.outputs.iter() {
if output.bip32_derivation.is_empty() {
return Err(PsbtValidationError::InvalidOutputField(output.clone()).into());
}
}
let output = &psbt.outputs[0];
if output.redeem_script.is_some() {
return Err(PsbtValidationError::InvalidOutputField(output.clone()).into());
}
if psbt.inputs.len() != 1 {
return Err(PsbtValidationError::InvalidInputCount(psbt.inputs.len()).into());
}
Ok(CancelTransaction(psbt))
}
pub fn deposit_txin(&self, deposit_descriptor: &DerivedDepositDescriptor) -> DepositTxIn {
let txo = &self.tx().output[0];
let prev_txout = DepositTxOut::new(Amount::from_sat(txo.value), deposit_descriptor);
DepositTxIn::new(
OutPoint {
txid: self.txid(),
vout: 0,
},
prev_txout,
)
}
}