use crate::{
error::*,
scripts::*,
transactions::{
utils, RevaultPresignedTransaction, RevaultTransaction, EMER_TX_FEERATE, 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!(
UnvaultEmergencyTransaction,
doc = "The transaction spending an unvault output to The Emergency Script."
);
impl RevaultPresignedTransaction for UnvaultEmergencyTransaction {}
impl UnvaultEmergencyTransaction {
pub fn new(
unvault_input: UnvaultTxIn,
emer_address: EmergencyAddress,
) -> Result<UnvaultEmergencyTransaction, TransactionCreationError> {
let emer_txo = EmergencyTxOut::new(emer_address.clone(), Amount::from_sat(u64::MAX));
let dummy_tx = utils::create_psbt(unvault_input.clone(), emer_txo)
.global
.unsigned_tx;
let total_weight = dummy_tx
.get_weight()
.checked_add(unvault_input.txout().max_sat_weight())
.expect("Weight computation bug");
let total_weight: u64 = total_weight.try_into().expect("usize in u64");
let fees = EMER_TX_FEERATE
.checked_mul(total_weight)
.expect("Weight computation bug");
assert!(fees < INSANE_FEES);
assert!(
total_weight <= MAX_STANDARD_TX_WEIGHT as u64,
"A single output and a single output"
);
let deposit_value = unvault_input.txout().txout().value;
let emer_value = deposit_value
.checked_sub(fees)
.expect("We would never create a dust unvault txo");
assert!(
emer_value < max_money(Network::Bitcoin),
"Checked in UnvaultTransaction constructor already"
);
let emer_txo = EmergencyTxOut::new(emer_address, Amount::from_sat(emer_value));
Ok(UnvaultEmergencyTransaction(utils::create_psbt(
unvault_input,
emer_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());
}
let input_count = psbt.global.unsigned_tx.input.len();
if psbt.inputs.len() != 1 {
return Err(PsbtValidationError::InvalidInputCount(input_count).into());
}
Ok(UnvaultEmergencyTransaction(psbt))
}
pub fn emergency_outpoint(&self) -> OutPoint {
OutPoint {
txid: self.txid(),
vout: 0,
}
}
}