Added Sighash calculation for psbt
This commit is contained in:
parent
e97dda0ffe
commit
c1eafff9ef
|
@ -19,6 +19,7 @@ use blockdata::transaction::Transaction;
|
|||
use util::psbt::raw;
|
||||
|
||||
use hashes::{self, sha256, hash160, sha256d, ripemd160};
|
||||
use hash_types::Txid;
|
||||
|
||||
/// Support hash-preimages in psbt
|
||||
#[derive(Debug)]
|
||||
|
@ -66,7 +67,29 @@ pub enum Error {
|
|||
preimage: Vec<u8>,
|
||||
/// Hash value
|
||||
hash: PsbtHash,
|
||||
}
|
||||
},
|
||||
/// If NonWitnessUtxo is used, the nonWitnessUtxo txid must
|
||||
/// be the same of prevout txid
|
||||
InvalidNonWitnessUtxo{
|
||||
/// Pre-image
|
||||
prevout_txid: Txid,
|
||||
/// Hash value
|
||||
non_witness_utxo_txid: Txid,
|
||||
},
|
||||
/// Incorrect P2sh/p2wsh script hash for the witness/redeem
|
||||
/// script
|
||||
InvalidWitnessScript{
|
||||
/// Expected Witness/Redeem Script Hash
|
||||
// returns a vec to unify the p2wsh(sha2) and p2sh(hash160)
|
||||
expected: Vec<u8>,
|
||||
/// Actual Witness script Hash
|
||||
actual: Vec<u8>,
|
||||
},
|
||||
/// Currently only p2wpkh and p2wsh scripts are possible in segwit
|
||||
UnrecognizedWitnessProgram,
|
||||
/// The psbt input must either have an associated nonWitnessUtxo or
|
||||
/// a WitnessUtxo
|
||||
MustHaveSpendingUtxo,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
|
@ -88,6 +111,18 @@ impl fmt::Display for Error {
|
|||
Error::InvalidPreimageHashPair{ref preimage, ref hash} => {
|
||||
// directly using debug forms of psbthash enums
|
||||
write!(f, "Preimage {:?} does not match hash {:?}", preimage, hash )
|
||||
},
|
||||
Error::InvalidNonWitnessUtxo{ref prevout_txid, ref non_witness_utxo_txid} => {
|
||||
write!(f, "NonWitnessUtxo txid {} must be the same as prevout txid {}", non_witness_utxo_txid, prevout_txid)
|
||||
},
|
||||
Error::InvalidWitnessScript{ref expected, ref actual} => {
|
||||
write!(f, "Invalid Witness/Redeem script: Expected {:?}, got {:?}", expected, actual)
|
||||
}
|
||||
Error::UnrecognizedWitnessProgram => {
|
||||
f.write_str("Witness program must be p2wpkh/p2wsh")
|
||||
}
|
||||
Error::MustHaveSpendingUtxo => {
|
||||
f.write_str("Input must either WitnessUtxo/ NonWitnessUtxo")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,9 +18,12 @@
|
|||
//! defined at https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
|
||||
//! except we define PSBTs containing non-standard SigHash types as invalid.
|
||||
|
||||
use blockdata::script::Script;
|
||||
use blockdata::transaction::Transaction;
|
||||
use blockdata::transaction::{SigHashType, Transaction};
|
||||
use hash_types::SigHash;
|
||||
use consensus::{encode, Encodable, Decodable};
|
||||
use util::bip143::SigHashCache;
|
||||
use blockdata::script::{Builder, Script};
|
||||
use blockdata::opcodes;
|
||||
|
||||
use std::io;
|
||||
|
||||
|
@ -88,6 +91,83 @@ impl PartiallySignedTransaction {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Calculate the Sighash for the Psbt Input at idx depending on whether input spends
|
||||
/// spends a segwit or not
|
||||
/// #Panics:
|
||||
/// Panics if the index >= number of inputs in psbt
|
||||
pub fn signature_hash(&self, idx: usize) -> Result<SigHash, self::Error> {
|
||||
let inp = &self.inputs[idx];
|
||||
let sighash_type = inp.sighash_type.unwrap_or_else(|| SigHashType::All);
|
||||
// Compute Script code for the Script. When this script is used as scriptPubkey,
|
||||
// the Script Code determines what goes into sighash.
|
||||
// For non-p2sh non-segwit outputs and non-p2sh wrapped segwit-outputs
|
||||
// script code is the public key.
|
||||
let sighash = if let Some(ref non_witness_utxo) = inp.non_witness_utxo {
|
||||
let spent_outpoint = self.global.unsigned_tx.input[idx].previous_output;
|
||||
if spent_outpoint.txid != non_witness_utxo.txid() {
|
||||
return Err(Error::InvalidNonWitnessUtxo {
|
||||
prevout_txid: spent_outpoint.txid,
|
||||
non_witness_utxo_txid: non_witness_utxo.txid()
|
||||
});
|
||||
}
|
||||
let script_pubkey = &non_witness_utxo.output[spent_outpoint.vout as usize].script_pubkey;
|
||||
if let Some(ref redeem_script) = inp.redeem_script {
|
||||
if redeem_script.to_p2sh() != *script_pubkey {
|
||||
return Err(Error::InvalidWitnessScript{
|
||||
expected: script_pubkey.to_bytes(),
|
||||
actual: redeem_script.to_p2sh().into_bytes(),
|
||||
});
|
||||
}
|
||||
self.global.unsigned_tx.signature_hash(
|
||||
idx, &redeem_script, sighash_type.as_u32()
|
||||
)
|
||||
} else {
|
||||
self.global.unsigned_tx.signature_hash(
|
||||
idx, &script_pubkey, sighash_type.as_u32()
|
||||
)
|
||||
}
|
||||
} else if let Some(ref witness_utxo) = inp.witness_utxo {
|
||||
let script_pubkey = if let Some(ref redeem_script) = inp.redeem_script {
|
||||
if redeem_script.to_p2sh() != witness_utxo.script_pubkey {
|
||||
return Err(Error::InvalidWitnessScript{
|
||||
expected: witness_utxo.script_pubkey.to_bytes(),
|
||||
actual: redeem_script.to_p2sh().into_bytes(),
|
||||
});
|
||||
}
|
||||
redeem_script
|
||||
} else {
|
||||
&witness_utxo.script_pubkey
|
||||
};
|
||||
|
||||
let amt = witness_utxo.value;
|
||||
let mut sighash_cache = SigHashCache::new(&self.global.unsigned_tx);
|
||||
if let Some(ref witness_script) = inp.witness_script {
|
||||
if witness_script.to_v0_p2wsh() != *script_pubkey {
|
||||
return Err(Error::InvalidWitnessScript{
|
||||
expected: script_pubkey.clone().into_bytes(),
|
||||
actual: witness_script.to_p2sh().into_bytes(),
|
||||
});
|
||||
}
|
||||
sighash_cache.signature_hash(idx, &witness_script, amt, sighash_type)
|
||||
} else if script_pubkey.is_v0_p2wpkh() {
|
||||
// Indirect way to get script code
|
||||
let builder = Builder::new();
|
||||
let script_code = builder
|
||||
.push_opcode(opcodes::all::OP_DUP)
|
||||
.push_opcode(opcodes::all::OP_HASH160)
|
||||
.push_slice(&script_pubkey[2..22])
|
||||
.push_opcode(opcodes::all::OP_EQUALVERIFY)
|
||||
.into_script();
|
||||
sighash_cache.signature_hash(idx, &script_code, amt, sighash_type)
|
||||
} else {
|
||||
return Err(Error::UnrecognizedWitnessProgram);
|
||||
}
|
||||
} else {
|
||||
return Err(Error::MustHaveSpendingUtxo);
|
||||
};
|
||||
Ok(sighash)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for PartiallySignedTransaction {
|
||||
|
|
Loading…
Reference in New Issue