use std::collections::BTreeMap;
use bitcoin::blockdata::transaction::NonStandardSighashType;
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d};
use bitcoin::psbt::PsbtSighashType;
use bitcoin::util::bip32::KeySource;
use bitcoin::util::sighash;
use bitcoin::util::taproot::{ControlBlock, LeafVersion, TapBranchHash, TapLeafHash};
use bitcoin::{
secp256k1, EcdsaSig, EcdsaSighashType, OutPoint, PublicKey, SchnorrSig, SchnorrSighashType,
Script, Transaction, TxIn, TxOut, Witness, XOnlyPublicKey,
};
use bitcoin_blockchain::locks::{LockHeight, LockTime, LockTimestamp, SeqNo};
use bitcoin_scripts::{RedeemScript, SigScript, WitnessScript};
#[cfg(feature = "serde")]
use serde_with::{hex::Hex, As, Same};
use crate::v0::InputV0;
use crate::{raw, InputMatchError, TxinError};
#[derive(Clone, Eq, PartialEq, Debug, Default)]
#[derive(StrictEncode, StrictDecode)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate")
)]
pub struct Input {
pub(crate) index: usize,
pub previous_outpoint: OutPoint,
pub sequence_number: Option<SeqNo>,
pub required_time_locktime: Option<LockTimestamp>,
pub required_height_locktime: Option<LockHeight>,
pub non_witness_utxo: Option<Transaction>,
pub witness_utxo: Option<TxOut>,
pub partial_sigs: BTreeMap<PublicKey, EcdsaSig>,
pub sighash_type: Option<PsbtSighashType>,
pub redeem_script: Option<RedeemScript>,
pub witness_script: Option<WitnessScript>,
#[cfg_attr(feature = "serde", serde(with = "As::<BTreeMap<Same, Same>>"))]
pub bip32_derivation: BTreeMap<secp256k1::PublicKey, KeySource>,
pub final_script_sig: Option<SigScript>,
pub final_script_witness: Option<Witness>,
#[cfg_attr(feature = "serde", serde(with = "As::<BTreeMap<Same, Hex>>"))]
pub ripemd160_preimages: BTreeMap<ripemd160::Hash, Vec<u8>>,
#[cfg_attr(feature = "serde", serde(with = "As::<BTreeMap<Same, Hex>>"))]
pub sha256_preimages: BTreeMap<sha256::Hash, Vec<u8>>,
#[cfg_attr(feature = "serde", serde(with = "As::<BTreeMap<Same, Hex>>"))]
pub hash160_preimages: BTreeMap<hash160::Hash, Vec<u8>>,
#[cfg_attr(feature = "serde", serde(with = "As::<BTreeMap<Same, Hex>>"))]
pub hash256_preimages: BTreeMap<sha256d::Hash, Vec<u8>>,
pub tap_key_sig: Option<SchnorrSig>,
#[cfg_attr(feature = "serde", serde(with = "As::<BTreeMap<Same, Same>>"))]
pub tap_script_sigs: BTreeMap<(XOnlyPublicKey, TapLeafHash), SchnorrSig>,
#[cfg_attr(feature = "serde", serde(with = "As::<BTreeMap<Same, Same>>"))]
pub tap_scripts: BTreeMap<ControlBlock, (Script, LeafVersion)>,
#[cfg_attr(
feature = "serde",
serde(with = "As::<BTreeMap<Same, (Vec<Same>, Same)>>")
)]
pub tap_key_origins: BTreeMap<XOnlyPublicKey, (Vec<TapLeafHash>, KeySource)>,
pub tap_internal_key: Option<XOnlyPublicKey>,
pub tap_merkle_root: Option<TapBranchHash>,
#[cfg_attr(feature = "serde", serde(with = "As::<BTreeMap<Same, Hex>>"))]
pub proprietary: BTreeMap<raw::ProprietaryKey, Vec<u8>>,
#[cfg_attr(feature = "serde", serde(with = "As::<BTreeMap<Same, Hex>>"))]
pub unknown: BTreeMap<raw::Key, Vec<u8>>,
}
impl Input {
pub fn new(index: usize, txin: TxIn) -> Result<Self, TxinError> {
let sequence_number = match txin.sequence.0 {
u32::MAX => None,
other => Some(other.into()),
};
let input = Input {
index,
previous_outpoint: txin.previous_output,
sequence_number,
..Input::default()
};
if !txin.script_sig.is_empty() {
return Err(TxinError::UnsignedTxHasScriptSigs(index));
}
if !txin.witness.is_empty() {
return Err(TxinError::UnsignedTxHasScriptWitnesses(index));
}
Ok(input)
}
pub fn with(index: usize, v0: InputV0, txin: TxIn) -> Self {
let sequence = match txin.sequence.0 {
u32::MAX => None,
other => Some(other.into()),
};
Input {
index,
previous_outpoint: txin.previous_output,
sequence_number: sequence,
required_time_locktime: None,
required_height_locktime: None,
non_witness_utxo: v0.non_witness_utxo,
witness_utxo: v0.witness_utxo,
partial_sigs: v0.partial_sigs,
sighash_type: v0.sighash_type,
redeem_script: v0.redeem_script.map(Into::into),
witness_script: v0.witness_script.map(Into::into),
bip32_derivation: v0.bip32_derivation,
final_script_sig: v0.final_script_sig.map(Into::into),
final_script_witness: v0.final_script_witness,
ripemd160_preimages: v0.ripemd160_preimages,
sha256_preimages: v0.sha256_preimages,
hash160_preimages: v0.hash160_preimages,
hash256_preimages: v0.hash256_preimages,
tap_key_sig: v0.tap_key_sig,
tap_script_sigs: v0.tap_script_sigs,
tap_scripts: v0.tap_scripts,
tap_key_origins: v0.tap_key_origins,
tap_internal_key: v0.tap_internal_key,
tap_merkle_root: v0.tap_merkle_root,
proprietary: v0.proprietary,
unknown: v0.unknown,
}
}
#[inline]
pub fn index(&self) -> usize { self.index }
#[inline]
pub fn locktime(&self) -> Option<LockTime> {
self.required_time_locktime
.map(LockTime::from)
.or_else(|| self.required_height_locktime.map(LockTime::from))
}
pub fn ecdsa_hash_ty(&self) -> Result<EcdsaSighashType, NonStandardSighashType> {
self.sighash_type
.map(|sighash_type| sighash_type.ecdsa_hash_ty())
.unwrap_or(Ok(EcdsaSighashType::All))
}
pub fn schnorr_hash_ty(&self) -> Result<SchnorrSighashType, sighash::Error> {
self.sighash_type
.map(|sighash_type| sighash_type.schnorr_hash_ty())
.unwrap_or(Ok(SchnorrSighashType::Default))
}
pub fn input_prevout(&self) -> Result<&TxOut, InputMatchError> {
let txid = self.previous_outpoint.txid;
if let Some(txout) = &self.witness_utxo {
Ok(txout)
} else if let Some(tx) = &self.non_witness_utxo {
if tx.txid() != txid {
return Err(InputMatchError::NoTxidMatch(txid));
}
let prev_index = self.previous_outpoint.vout;
tx.output
.get(prev_index as usize)
.ok_or(InputMatchError::UnmatchedInputNumber(prev_index))
} else {
Err(InputMatchError::NoInputTx)
}
}
pub fn to_unsigned_txin(&self) -> TxIn {
let sequence = bitcoin::Sequence(self.sequence_number.unwrap_or_default().into_consensus());
TxIn {
previous_output: self.previous_outpoint,
script_sig: empty!(),
sequence,
witness: empty!(),
}
}
pub fn extract_signed_txin(&self) -> TxIn {
let sequence = bitcoin::Sequence(self.sequence_number.unwrap_or_default().into_consensus());
TxIn {
previous_output: self.previous_outpoint,
script_sig: self
.final_script_sig
.as_ref()
.cloned()
.unwrap_or_default()
.into(),
sequence,
witness: self
.final_script_witness
.as_ref()
.cloned()
.unwrap_or_default(),
}
}
pub fn split(self) -> (InputV0, TxIn) {
let sequence = bitcoin::Sequence(self.sequence_number.unwrap_or_default().into_consensus());
(
InputV0 {
non_witness_utxo: self.non_witness_utxo,
witness_utxo: self.witness_utxo,
partial_sigs: self.partial_sigs,
sighash_type: self.sighash_type,
redeem_script: self.redeem_script.map(Into::into),
witness_script: self.witness_script.map(Into::into),
bip32_derivation: self.bip32_derivation,
final_script_sig: self.final_script_sig.map(Into::into),
final_script_witness: self.final_script_witness,
ripemd160_preimages: self.ripemd160_preimages,
sha256_preimages: self.sha256_preimages,
hash160_preimages: self.hash160_preimages,
hash256_preimages: self.hash256_preimages,
tap_key_sig: self.tap_key_sig,
tap_script_sigs: self.tap_script_sigs,
tap_scripts: self.tap_scripts,
tap_key_origins: self.tap_key_origins,
tap_internal_key: self.tap_internal_key,
tap_merkle_root: self.tap_merkle_root,
proprietary: self.proprietary,
unknown: self.unknown,
},
TxIn {
previous_output: self.previous_outpoint,
script_sig: Default::default(),
sequence,
witness: Default::default(),
},
)
}
}
impl From<Input> for InputV0 {
fn from(input: Input) -> Self {
InputV0 {
non_witness_utxo: input.non_witness_utxo,
witness_utxo: input.witness_utxo,
partial_sigs: input.partial_sigs,
sighash_type: input.sighash_type,
redeem_script: input.redeem_script.map(Into::into),
witness_script: input.witness_script.map(Into::into),
bip32_derivation: input.bip32_derivation,
final_script_sig: input.final_script_sig.map(Into::into),
final_script_witness: input.final_script_witness,
ripemd160_preimages: input.ripemd160_preimages,
sha256_preimages: input.sha256_preimages,
hash160_preimages: input.hash160_preimages,
hash256_preimages: input.hash256_preimages,
tap_key_sig: input.tap_key_sig,
tap_script_sigs: input.tap_script_sigs,
tap_scripts: input.tap_scripts,
tap_key_origins: input.tap_key_origins,
tap_internal_key: input.tap_internal_key,
tap_merkle_root: input.tap_merkle_root,
proprietary: input.proprietary,
unknown: input.unknown,
}
}
}