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 util::psbt::raw;
|
||||||
|
|
||||||
use hashes::{self, sha256, hash160, sha256d, ripemd160};
|
use hashes::{self, sha256, hash160, sha256d, ripemd160};
|
||||||
|
use hash_types::Txid;
|
||||||
|
|
||||||
/// Support hash-preimages in psbt
|
/// Support hash-preimages in psbt
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -66,7 +67,29 @@ pub enum Error {
|
||||||
preimage: Vec<u8>,
|
preimage: Vec<u8>,
|
||||||
/// Hash value
|
/// Hash value
|
||||||
hash: PsbtHash,
|
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 {
|
impl fmt::Display for Error {
|
||||||
|
@ -88,6 +111,18 @@ impl fmt::Display for Error {
|
||||||
Error::InvalidPreimageHashPair{ref preimage, ref hash} => {
|
Error::InvalidPreimageHashPair{ref preimage, ref hash} => {
|
||||||
// directly using debug forms of psbthash enums
|
// directly using debug forms of psbthash enums
|
||||||
write!(f, "Preimage {:?} does not match hash {:?}", preimage, hash )
|
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
|
//! defined at https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
|
||||||
//! except we define PSBTs containing non-standard SigHash types as invalid.
|
//! except we define PSBTs containing non-standard SigHash types as invalid.
|
||||||
|
|
||||||
use blockdata::script::Script;
|
use blockdata::transaction::{SigHashType, Transaction};
|
||||||
use blockdata::transaction::Transaction;
|
use hash_types::SigHash;
|
||||||
use consensus::{encode, Encodable, Decodable};
|
use consensus::{encode, Encodable, Decodable};
|
||||||
|
use util::bip143::SigHashCache;
|
||||||
|
use blockdata::script::{Builder, Script};
|
||||||
|
use blockdata::opcodes;
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
|
@ -88,6 +91,83 @@ impl PartiallySignedTransaction {
|
||||||
|
|
||||||
Ok(())
|
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 {
|
impl Encodable for PartiallySignedTransaction {
|
||||||
|
|
Loading…
Reference in New Issue