use crate::types::{
BitcoinOutpoint, BitcoinTransaction, LegacySighashArgs, Script, ScriptPubkey, ScriptType,
Sighash, TxOut, WitnessSighashArgs,
};
use coins_core::hashes::{Digest, Hash160, MarkedDigest, MarkedDigestOutput, Sha256};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Hash)]
pub enum SpendScript {
None,
Missing,
Known(Script),
}
impl SpendScript {
pub fn from_script_pubkey(script: &ScriptPubkey) -> SpendScript {
match script.standard_type() {
ScriptType::Sh(_) | ScriptType::Wsh(_) => SpendScript::Missing,
_ => SpendScript::None,
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Utxo {
pub outpoint: BitcoinOutpoint,
pub value: u64,
pub script_pubkey: ScriptPubkey,
spend_script: SpendScript,
}
impl Utxo {
pub fn new(
outpoint: BitcoinOutpoint,
value: u64,
script_pubkey: ScriptPubkey,
spend_script: SpendScript,
) -> Utxo {
let spend_script = match SpendScript::from_script_pubkey(&script_pubkey) {
SpendScript::None => SpendScript::None,
SpendScript::Missing => spend_script,
SpendScript::Known(_) => panic!("unreachable"),
};
Utxo {
outpoint,
value,
script_pubkey,
spend_script,
}
}
pub fn from_tx_output<T>(tx: &T, idx: usize) -> Utxo
where
T: BitcoinTransaction,
{
let output = &tx.outputs()[idx];
Utxo {
outpoint: BitcoinOutpoint::new(tx.txid(), idx as u32),
value: output.value,
script_pubkey: output.script_pubkey.clone(),
spend_script: SpendScript::from_script_pubkey(&output.script_pubkey),
}
}
pub fn from_output_and_outpoint(output: &TxOut, outpoint: &BitcoinOutpoint) -> Utxo {
Utxo {
outpoint: *outpoint,
value: output.value,
script_pubkey: output.script_pubkey.clone(),
spend_script: SpendScript::from_script_pubkey(&output.script_pubkey),
}
}
pub fn script_pubkey(&self) -> &ScriptPubkey {
&self.script_pubkey
}
pub fn spend_script(&self) -> &SpendScript {
&self.spend_script
}
pub fn signing_script(&self) -> Option<Script> {
match self.spend_script() {
SpendScript::None => {
let spk = self.script_pubkey();
match spk.standard_type() {
ScriptType::Pkh(_) => Some(spk.into()),
ScriptType::Wpkh(payload) => {
let mut v = vec![0x76, 0xa9, 0x14];
v.extend(payload.as_slice());
v.extend(&[0x88, 0xac]);
Some(v.into())
}
_ => None, }
}
SpendScript::Known(script) => Some(script.clone()),
SpendScript::Missing => None,
}
}
pub fn standard_type(&self) -> ScriptType {
self.script_pubkey.standard_type()
}
pub fn set_spend_script(&mut self, script: Script) -> bool {
match self.standard_type() {
ScriptType::Sh(data) => {
if data == Hash160::digest_marked(script.as_ref()) {
self.spend_script = SpendScript::Known(script);
return true;
}
}
ScriptType::Wsh(data) => {
if data.as_slice() == Sha256::digest(script.as_ref()).as_slice() {
self.spend_script = SpendScript::Known(script);
return true;
}
}
_ => return false,
}
false
}
pub fn sighash_args(&self, index: usize, flag: Sighash) -> Option<LegacySighashArgs> {
self.signing_script()
.map(|prevout_script| LegacySighashArgs {
index,
sighash_flag: flag,
prevout_script,
})
}
pub fn witness_sighash_args(&self, index: usize, flag: Sighash) -> Option<WitnessSighashArgs> {
self.signing_script()
.map(|prevout_script| WitnessSighashArgs {
index,
sighash_flag: flag,
prevout_script,
prevout_value: self.value,
})
}
}