use std::borrow::Borrow;
use crate::encode::{self, Encodable};
use crate::hash_types::Sighash;
use crate::hashes::{sha256d, Hash, sha256};
use crate::script::Script;
use std::ops::{Deref, DerefMut};
use std::io;
use crate::endian;
use crate::transaction::{EcdsaSighashType, Transaction, TxIn, TxOut, TxInWitness};
use crate::confidential;
use crate::Sequence;
use std::fmt;
use crate::taproot::{TapSighashHash, TapLeafHash};
use crate::BlockHash;
use crate::transaction::SighashTypeParseError;
#[derive(Debug)]
pub struct SighashCache<T: Deref<Target = Transaction>> {
tx: T,
common_cache: Option<CommonCache>,
segwit_cache: Option<SegwitCache>,
taproot_cache: Option<TaprootCache>,
}
#[derive(Debug)]
struct CommonCache {
prevouts: sha256::Hash,
sequences: sha256::Hash,
outputs: sha256::Hash,
issuances: sha256::Hash,
}
#[derive(Debug)]
struct SegwitCache {
prevouts: sha256d::Hash,
sequences: sha256d::Hash,
issuances: sha256d::Hash,
outputs: sha256d::Hash,
}
#[derive(Debug)]
struct TaprootCache {
script_pubkeys: sha256::Hash,
outpoint_flags: sha256::Hash,
asset_amounts: sha256::Hash,
issuance_rangeproofs: sha256::Hash,
output_witnesses: sha256::Hash,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum Prevouts<'u, T> where T: 'u + Borrow<TxOut> {
One(usize, T),
All(&'u [T]),
}
const KEY_VERSION_0: u8 = 0u8;
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct ScriptPath<'s> {
script: &'s Script,
code_separator_pos: u32,
leaf_version: u8,
}
#[derive(Debug)]
pub enum Error {
Encode(encode::Error),
IndexOutOfInputsBounds {
index: usize,
inputs_size: usize,
},
SingleWithoutCorrespondingOutput {
index: usize,
outputs_size: usize,
},
PrevoutsSize,
PrevoutIndex,
PrevoutKind,
WrongAnnex,
InvalidSighashType(u8),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Encode(ref e) => write!(f, "Writer errored: {:?}", e),
Error::IndexOutOfInputsBounds { index, inputs_size } => write!(f, "Requested index ({}) is greater or equal than the number of transaction inputs ({})", index, inputs_size),
Error::SingleWithoutCorrespondingOutput { index, outputs_size } => write!(f, "SIGHASH_SINGLE for input ({}) haven't a corresponding output (#outputs:{})", index, outputs_size),
Error::PrevoutsSize => write!(f, "Number of supplied prevouts differs from the number of inputs in transaction"),
Error::PrevoutIndex => write!(f, "The index requested is greater than available prevouts or different from the provided [Provided::Anyone] index"),
Error::PrevoutKind => write!(f, "A single prevout has been provided but all prevouts are needed without `ANYONECANPAY`"),
Error::WrongAnnex => write!(f, "Annex must be at least one byte long and the first bytes must be `0x50`"),
Error::InvalidSighashType(hash_ty) => write!(f, "Invalid schnorr Signature hash type : {} ", hash_ty),
}
}
}
impl ::std::error::Error for Error {}
impl<'u, T> Prevouts<'u, T> where T: Borrow<TxOut> {
fn check_all(&self, tx: &Transaction) -> Result<(), Error> {
if let Prevouts::All(prevouts) = self {
if prevouts.len() != tx.input.len() {
return Err(Error::PrevoutsSize);
}
}
Ok(())
}
fn get_all(&self) -> Result<&[T], Error> {
match self {
Prevouts::All(prevouts) => Ok(*prevouts),
_ => Err(Error::PrevoutKind),
}
}
fn get(&self, input_index: usize) -> Result<&TxOut, Error> {
match self {
Prevouts::One(index, prevout) => {
if input_index == *index {
Ok(prevout.borrow())
} else {
Err(Error::PrevoutIndex)
}
}
Prevouts::All(prevouts) => prevouts
.get(input_index)
.map(|x| x.borrow())
.ok_or(Error::PrevoutIndex),
}
}
}
impl<'s> ScriptPath<'s> {
pub fn new(script: &'s Script, code_separator_pos: u32, leaf_version: u8) -> Self {
ScriptPath {
script,
code_separator_pos,
leaf_version,
}
}
pub fn with_defaults(script: &'s Script) -> Self {
Self::new(script, 0xFFFFFFFFu32, 0xc4)
}
pub fn leaf_hash(&self) -> TapLeafHash {
let mut enc = TapLeafHash::engine();
self.leaf_version.consensus_encode(&mut enc).expect("Writing to hash enging should never fail");
self.script.consensus_encode(&mut enc).expect("Writing to hash enging should never fail");
TapLeafHash::from_engine(enc)
}
}
impl<'s> From<ScriptPath<'s>> for TapLeafHash {
fn from(script_path: ScriptPath<'s>) -> TapLeafHash {
script_path.leaf_hash()
}
}
impl<R: Deref<Target = Transaction>> SighashCache<R> {
pub fn new(tx: R) -> Self {
SighashCache {
tx,
common_cache: None,
taproot_cache: None,
segwit_cache: None,
}
}
#[allow(clippy::too_many_arguments)]
pub fn taproot_encode_signing_data_to<Write: io::Write, T: Borrow<TxOut>>(
&mut self,
mut writer: Write,
input_index: usize,
prevouts: &Prevouts<T>,
annex: Option<Annex>,
leaf_hash_code_separator: Option<(TapLeafHash, u32)>,
sighash_type: SchnorrSighashType,
genesis_hash: BlockHash,
) -> Result<(), Error> {
prevouts.check_all(&self.tx)?;
let (sighash, anyone_can_pay) = sighash_type.split_anyonecanpay_flag();
genesis_hash.consensus_encode(&mut writer)?;
genesis_hash.consensus_encode(&mut writer)?;
(sighash_type as u8).consensus_encode(&mut writer)?;
self.tx.version.consensus_encode(&mut writer)?;
self.tx.lock_time.consensus_encode(&mut writer)?;
if !anyone_can_pay {
self.taproot_cache(prevouts.get_all()?)
.outpoint_flags
.consensus_encode(&mut writer)?;
self.common_cache().prevouts.consensus_encode(&mut writer)?;
self.taproot_cache(prevouts.get_all()?)
.asset_amounts
.consensus_encode(&mut writer)?;
self.taproot_cache(prevouts.get_all()?)
.script_pubkeys
.consensus_encode(&mut writer)?;
self.common_cache()
.sequences
.consensus_encode(&mut writer)?;
self.common_cache()
.issuances
.consensus_encode(&mut writer)?;
self.taproot_cache(prevouts.get_all()?)
.issuance_rangeproofs
.consensus_encode(&mut writer)?;
}
if sighash != SchnorrSighashType::None && sighash != SchnorrSighashType::Single {
self.common_cache().outputs.consensus_encode(&mut writer)?;
self.taproot_cache(prevouts.get_all()?)
.output_witnesses
.consensus_encode(&mut writer)?;
}
let mut spend_type = 0u8;
if annex.is_some() {
spend_type |= 1u8;
}
if leaf_hash_code_separator.is_some() {
spend_type |= 2u8;
}
spend_type.consensus_encode(&mut writer)?;
if anyone_can_pay {
let txin =
&self
.tx
.input
.get(input_index)
.ok_or(Error::IndexOutOfInputsBounds {
index: input_index,
inputs_size: self.tx.input.len(),
})?;
let previous_output = prevouts.get(input_index)?;
txin.outpoint_flag().consensus_encode(&mut writer)?;
txin.previous_output.consensus_encode(&mut writer)?;
previous_output.asset.consensus_encode(&mut writer)?;
previous_output.value.consensus_encode(&mut writer)?;
previous_output
.script_pubkey
.consensus_encode(&mut writer)?;
txin.sequence.consensus_encode(&mut writer)?;
if txin.has_issuance(){
txin.asset_issuance.consensus_encode(&mut writer)?;
let mut eng = sha256::Hash::engine();
txin.witness.amount_rangeproof.consensus_encode(&mut eng)?;
txin.witness.inflation_keys_rangeproof.consensus_encode(&mut eng)?;
let sha_single_issuance_rangeproofs = sha256::Hash::from_engine(eng);
sha_single_issuance_rangeproofs.consensus_encode(&mut writer)?;
} else {
0u8.consensus_encode(&mut writer)?;
}
} else {
(input_index as u32).consensus_encode(&mut writer)?;
}
if let Some(annex) = annex {
let mut enc = sha256::Hash::engine();
annex.consensus_encode(&mut enc)?;
let hash = sha256::Hash::from_engine(enc);
hash.consensus_encode(&mut writer)?;
}
if sighash == SchnorrSighashType::Single {
let mut enc = sha256::Hash::engine();
let out = self.tx
.output
.get(input_index)
.ok_or(Error::SingleWithoutCorrespondingOutput {
index: input_index,
outputs_size: self.tx.output.len(),
})?;
out.consensus_encode(&mut enc)?;
let hash = sha256::Hash::from_engine(enc);
hash.consensus_encode(&mut writer)?;
let mut eng = sha256::Hash::engine();
out.witness.consensus_encode(&mut eng)?;
let sha_single_output_witness = sha256::Hash::from_engine(eng);
sha_single_output_witness.consensus_encode(&mut writer)?;
}
if let Some((hash, code_separator_pos)) = leaf_hash_code_separator {
hash.to_byte_array().consensus_encode(&mut writer)?;
KEY_VERSION_0.consensus_encode(&mut writer)?;
code_separator_pos.consensus_encode(&mut writer)?;
}
Ok(())
}
pub fn taproot_sighash<T: Borrow<TxOut>>(
&mut self,
input_index: usize,
prevouts: &Prevouts<T>,
annex: Option<Annex>,
leaf_hash_code_separator: Option<(TapLeafHash, u32)>,
sighash_type: SchnorrSighashType,
genesis_hash: BlockHash,
) -> Result<TapSighashHash, Error> {
let mut enc = TapSighashHash::engine();
self.taproot_encode_signing_data_to(
&mut enc,
input_index,
prevouts,
annex,
leaf_hash_code_separator,
sighash_type,
genesis_hash,
)?;
Ok(TapSighashHash::from_engine(enc))
}
pub fn taproot_key_spend_signature_hash<T: Borrow<TxOut>>(
&mut self,
input_index: usize,
prevouts: &Prevouts<T>,
sighash_type: SchnorrSighashType,
genesis_hash: BlockHash,
) -> Result<TapSighashHash, Error> {
let mut enc = TapSighashHash::engine();
self.taproot_encode_signing_data_to(
&mut enc,
input_index,
prevouts,
None,
None,
sighash_type,
genesis_hash,
)?;
Ok(TapSighashHash::from_engine(enc))
}
pub fn taproot_script_spend_signature_hash<S: Into<TapLeafHash>, T: Borrow<TxOut>>(
&mut self,
input_index: usize,
prevouts: &Prevouts<T>,
leaf_hash: S,
sighash_type: SchnorrSighashType,
genesis_hash: BlockHash,
) -> Result<TapSighashHash, Error> {
let mut enc = TapSighashHash::engine();
self.taproot_encode_signing_data_to(
&mut enc,
input_index,
prevouts,
None,
Some((leaf_hash.into(), 0xFFFFFFFF)),
sighash_type,
genesis_hash
)?;
Ok(TapSighashHash::from_engine(enc))
}
pub fn encode_segwitv0_signing_data_to<Write: io::Write>(
&mut self,
mut writer: Write,
input_index: usize,
script_code: &Script,
value: confidential::Value,
sighash_type: EcdsaSighashType,
) -> Result<(), encode::Error> {
let zero_hash = sha256d::Hash::all_zeros();
let (sighash, anyone_can_pay) = sighash_type.split_anyonecanpay_flag();
self.tx.version.consensus_encode(&mut writer)?;
if !anyone_can_pay {
self.segwit_cache().prevouts.consensus_encode(&mut writer)?;
} else {
zero_hash.consensus_encode(&mut writer)?;
}
if !anyone_can_pay && sighash != EcdsaSighashType::Single && sighash != EcdsaSighashType::None {
self.segwit_cache().sequences.consensus_encode(&mut writer)?;
} else {
zero_hash.consensus_encode(&mut writer)?;
}
if !anyone_can_pay {
self.segwit_cache().issuances.consensus_encode(&mut writer)?;
} else {
zero_hash.consensus_encode(&mut writer)?;
}
{
let txin = &self.tx.input[input_index];
txin.previous_output.consensus_encode(&mut writer)?;
script_code.consensus_encode(&mut writer)?;
value.consensus_encode(&mut writer)?;
txin.sequence.consensus_encode(&mut writer)?;
if txin.has_issuance(){
txin.asset_issuance.consensus_encode(&mut writer)?;
}
}
if sighash != EcdsaSighashType::Single && sighash != EcdsaSighashType::None {
self.segwit_cache().outputs.consensus_encode(&mut writer)?;
} else if sighash == EcdsaSighashType::Single && input_index < self.tx.output.len() {
let mut single_enc = Sighash::engine();
self.tx.output[input_index].consensus_encode(&mut single_enc)?;
Sighash::from_engine(single_enc).consensus_encode(&mut writer)?;
} else {
zero_hash.consensus_encode(&mut writer)?;
}
self.tx.lock_time.consensus_encode(&mut writer)?;
sighash_type.as_u32().consensus_encode(&mut writer)?;
Ok(())
}
pub fn segwitv0_sighash(
&mut self,
input_index: usize,
script_code: &Script,
value: confidential::Value,
sighash_type: EcdsaSighashType
) -> Sighash {
let mut enc = Sighash::engine();
self.encode_segwitv0_signing_data_to(&mut enc, input_index, script_code, value, sighash_type)
.expect("engines don't error");
Sighash::from_engine(enc)
}
pub fn encode_legacy_signing_data_to<Write: io::Write>(
&self,
mut writer: Write,
input_index: usize,
script_pubkey: &Script,
sighash_type: EcdsaSighashType,
) -> Result<(), encode::Error> {
assert!(input_index < self.tx.input.len()); let (sighash, anyone_can_pay) = sighash_type.split_anyonecanpay_flag();
if sighash == EcdsaSighashType::Single && input_index >= self.tx.output.len() {
writer.write_all(&[1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0])?;
return Ok(());
}
let mut tx = Transaction {
version: self.tx.version,
lock_time: self.tx.lock_time,
input: vec![],
output: vec![],
};
if anyone_can_pay {
tx.input = vec![TxIn {
previous_output: self.tx.input[input_index].previous_output,
is_pegin: self.tx.input[input_index].is_pegin,
script_sig: script_pubkey.clone(),
sequence: self.tx.input[input_index].sequence,
asset_issuance: self.tx.input[input_index].asset_issuance,
witness: TxInWitness::default(),
}];
} else {
tx.input = Vec::with_capacity(self.tx.input.len());
for (n, input) in self.tx.input.iter().enumerate() {
tx.input.push(TxIn {
previous_output: input.previous_output,
is_pegin: input.is_pegin,
script_sig: if n == input_index { script_pubkey.clone() } else { Script::new() },
sequence: if n != input_index && (sighash == EcdsaSighashType::Single || sighash == EcdsaSighashType::None) { Sequence::ZERO } else { input.sequence },
asset_issuance: input.asset_issuance,
witness: TxInWitness::default(),
});
}
}
tx.output = match sighash {
EcdsaSighashType::All => self.tx.output.clone(),
EcdsaSighashType::Single => {
let output_iter = self.tx.output.iter()
.take(input_index + 1) .enumerate() .map(|(n, out)| if n == input_index { out.clone() } else { TxOut::default() });
output_iter.collect()
}
EcdsaSighashType::None => vec![],
_ => unreachable!()
};
tx.version.consensus_encode(&mut writer)?;
tx.input.consensus_encode(&mut writer)?;
tx.output.consensus_encode(&mut writer)?;
tx.lock_time.consensus_encode(&mut writer)?;
let sighash_arr = endian::u32_to_array_le(sighash_type.as_u32());
sighash_arr.consensus_encode(&mut writer)?;
Ok(())
}
pub fn legacy_sighash(
&self,
input_index: usize,
script_pubkey: &Script,
sighash_type: EcdsaSighashType,
) -> Sighash {
let mut engine = Sighash::engine();
self.encode_legacy_signing_data_to(&mut engine, input_index, script_pubkey, sighash_type)
.expect("engines don't error");
Sighash::from_engine(engine)
}
#[inline]
fn common_cache(&mut self) -> &CommonCache {
Self::common_cache_minimal_borrow(&mut self.common_cache, &self.tx)
}
fn common_cache_minimal_borrow<'a>(
common_cache: &'a mut Option<CommonCache>,
tx: &R,
) -> &'a CommonCache {
common_cache.get_or_insert_with(|| {
let mut enc_prevouts = sha256::Hash::engine();
let mut enc_sequences = sha256::Hash::engine();
for txin in tx.input.iter() {
txin.previous_output
.consensus_encode(&mut enc_prevouts)
.unwrap();
txin.sequence.consensus_encode(&mut enc_sequences).unwrap();
}
CommonCache {
prevouts: sha256::Hash::from_engine(enc_prevouts),
sequences: sha256::Hash::from_engine(enc_sequences),
outputs: {
let mut enc = sha256::Hash::engine();
for txout in tx.output.iter() {
txout.consensus_encode(&mut enc).unwrap();
}
sha256::Hash::from_engine(enc)
},
issuances: {
let mut enc = sha256::Hash::engine();
for txin in tx.input.iter() {
if txin.has_issuance() {
txin.asset_issuance.consensus_encode(&mut enc).unwrap();
} else {
0u8.consensus_encode(&mut enc).unwrap();
}
}
sha256::Hash::from_engine(enc)
},
}
})
}
fn segwit_cache(&mut self) -> &SegwitCache {
let common_cache = &mut self.common_cache;
let tx = &self.tx;
self.segwit_cache.get_or_insert_with(|| {
let common_cache = Self::common_cache_minimal_borrow(common_cache, tx);
SegwitCache {
prevouts: sha256d::Hash::from_byte_array(
sha256::Hash::hash(common_cache.prevouts.as_ref()).to_byte_array(),
),
sequences: sha256d::Hash::from_byte_array(
sha256::Hash::hash(common_cache.sequences.as_ref()).to_byte_array(),
),
outputs: sha256d::Hash::from_byte_array(
sha256::Hash::hash(common_cache.outputs.as_ref()).to_byte_array(),
),
issuances: sha256d::Hash::from_byte_array(
sha256::Hash::hash(common_cache.issuances.as_ref()).to_byte_array(),
),
}
})
}
#[inline]
fn taproot_cache<T: Borrow<TxOut>>(&mut self, prevouts: &[T]) -> &TaprootCache {
Self::taproot_cache_minimal_borrow(&mut self.taproot_cache, &self.tx, prevouts)
}
fn taproot_cache_minimal_borrow<'a, T: Borrow<TxOut>>(
taproot_cache: &'a mut Option<TaprootCache>,
tx: &R,
prevouts: &[T],
) -> &'a TaprootCache {
taproot_cache.get_or_insert_with(|| {
let mut enc_asset_amounts = sha256::Hash::engine();
let mut enc_script_pubkeys = sha256::Hash::engine();
let mut enc_outpoint_flags = sha256::Hash::engine();
let mut enc_issuance_rangeproofs = sha256::Hash::engine();
let mut enc_output_witnesses = sha256::Hash::engine();
for prevout in prevouts {
prevout.borrow().asset.consensus_encode(&mut enc_asset_amounts).unwrap();
prevout.borrow().value.consensus_encode(&mut enc_asset_amounts).unwrap();
prevout
.borrow()
.script_pubkey
.consensus_encode(&mut enc_script_pubkeys)
.unwrap();
}
for inp in tx.input.iter() {
inp.outpoint_flag()
.consensus_encode(&mut enc_outpoint_flags).unwrap();
inp.witness.amount_rangeproof
.consensus_encode(&mut enc_issuance_rangeproofs).unwrap();
inp.witness.inflation_keys_rangeproof
.consensus_encode(&mut enc_issuance_rangeproofs).unwrap();
}
for out in tx.output.iter() {
out.witness.surjection_proof.consensus_encode(&mut enc_output_witnesses).unwrap();
out.witness.rangeproof.consensus_encode(&mut enc_output_witnesses).unwrap();
}
TaprootCache {
asset_amounts: sha256::Hash::from_engine(enc_asset_amounts),
script_pubkeys: sha256::Hash::from_engine(enc_script_pubkeys),
outpoint_flags: sha256::Hash::from_engine(enc_outpoint_flags),
issuance_rangeproofs: sha256::Hash::from_engine(enc_issuance_rangeproofs),
output_witnesses: sha256::Hash::from_engine(enc_output_witnesses),
}
})
}
}
impl<R: DerefMut<Target = Transaction>> SighashCache<R> {
pub fn witness_mut(&mut self, input_index: usize) -> Option<&mut Vec<Vec<u8>>> {
self.tx.input.get_mut(input_index).map(|i| &mut i.witness.script_witness)
}
}
impl From<encode::Error> for Error {
fn from(e: encode::Error) -> Self {
Error::Encode(e)
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Annex<'a>(&'a [u8]);
impl<'a> Annex<'a> {
pub fn new(annex_bytes: &'a [u8]) -> Result<Self, Error> {
if annex_bytes.first() == Some(&0x50) {
Ok(Annex(annex_bytes))
} else {
Err(Error::WrongAnnex)
}
}
pub fn as_bytes(&self) -> &[u8] {
self.0
}
}
impl<'a> Encodable for Annex<'a> {
fn consensus_encode<W: io::Write>(&self, writer: W) -> Result<usize, encode::Error> {
encode::consensus_encode_with_size(self.0, writer)
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum SchnorrSighashType {
Default = 0x00,
All = 0x01,
None = 0x02,
Single = 0x03,
AllPlusAnyoneCanPay = 0x81,
NonePlusAnyoneCanPay = 0x82,
SinglePlusAnyoneCanPay = 0x83,
Reserved = 0xFF,
}
serde_string_impl!(SchnorrSighashType, "a SchnorrSighashType data");
impl SchnorrSighashType {
pub fn split_anyonecanpay_flag(self) -> (SchnorrSighashType, bool) {
match self {
SchnorrSighashType::Default => (SchnorrSighashType::Default, false),
SchnorrSighashType::All => (SchnorrSighashType::All, false),
SchnorrSighashType::None => (SchnorrSighashType::None, false),
SchnorrSighashType::Single => (SchnorrSighashType::Single, false),
SchnorrSighashType::AllPlusAnyoneCanPay => (SchnorrSighashType::All, true),
SchnorrSighashType::NonePlusAnyoneCanPay => (SchnorrSighashType::None, true),
SchnorrSighashType::SinglePlusAnyoneCanPay => (SchnorrSighashType::Single, true),
SchnorrSighashType::Reserved => (SchnorrSighashType::Reserved, false),
}
}
pub fn from_u8(hash_ty: u8) -> Option<Self> {
match hash_ty {
0x00 => Some(SchnorrSighashType::Default),
0x01 => Some(SchnorrSighashType::All),
0x02 => Some(SchnorrSighashType::None),
0x03 => Some(SchnorrSighashType::Single),
0x81 => Some(SchnorrSighashType::AllPlusAnyoneCanPay),
0x82 => Some(SchnorrSighashType::NonePlusAnyoneCanPay),
0x83 => Some(SchnorrSighashType::SinglePlusAnyoneCanPay),
_x => None,
}
}
}
impl fmt::Display for SchnorrSighashType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
SchnorrSighashType::Default => "SIGHASH_DEFAULT",
SchnorrSighashType::All => "SIGHASH_ALL",
SchnorrSighashType::None => "SIGHASH_NONE",
SchnorrSighashType::Single => "SIGHASH_SINGLE",
SchnorrSighashType::AllPlusAnyoneCanPay => "SIGHASH_ALL|SIGHASH_ANYONECANPAY",
SchnorrSighashType::NonePlusAnyoneCanPay => "SIGHASH_NONE|SIGHASH_ANYONECANPAY",
SchnorrSighashType::SinglePlusAnyoneCanPay => "SIGHASH_SINGLE|SIGHASH_ANYONECANPAY",
SchnorrSighashType::Reserved => "SIGHASH_RESERVED",
};
f.write_str(s)
}
}
impl std::str::FromStr for SchnorrSighashType {
type Err = SighashTypeParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"SIGHASH_DEFAULT" => Ok(SchnorrSighashType::Default),
"SIGHASH_ALL" => Ok(SchnorrSighashType::All),
"SIGHASH_NONE" => Ok(SchnorrSighashType::None),
"SIGHASH_SINGLE" => Ok(SchnorrSighashType::Single),
"SIGHASH_ALL|SIGHASH_ANYONECANPAY" => Ok(SchnorrSighashType::AllPlusAnyoneCanPay),
"SIGHASH_NONE|SIGHASH_ANYONECANPAY" => Ok(SchnorrSighashType::NonePlusAnyoneCanPay),
"SIGHASH_SINGLE|SIGHASH_ANYONECANPAY" => Ok(SchnorrSighashType::SinglePlusAnyoneCanPay),
"SIGHASH_RESERVED" => Ok(SchnorrSighashType::Reserved),
_ => Err(SighashTypeParseError{ unrecognized: s.to_owned() }),
}
}
}
#[cfg(test)]
mod tests{
use super::*;
use crate::encode::deserialize;
use crate::hex::FromHex;
use std::str::FromStr;
fn test_segwit_sighash(tx: &str, script: &str, input_index: usize, value: &str, hash_type: EcdsaSighashType, expected_result: &str) {
let tx: Transaction = deserialize(&Vec::<u8>::from_hex(tx).unwrap()[..]).unwrap();
let script = Script::from(Vec::<u8>::from_hex(script).unwrap());
let raw_expected = crate::hashes::sha256::Hash::from_str(expected_result).unwrap();
let expected_result = Sighash::from_slice(&raw_expected[..]).unwrap();
let mut cache = SighashCache::new(&tx);
let value : confidential::Value = deserialize(&Vec::<u8>::from_hex(value).unwrap()[..]).unwrap();
let actual_result = cache.segwitv0_sighash(input_index, &script, value, hash_type);
assert_eq!(actual_result, expected_result);
}
#[test]
fn test_segwit_sighashes(){
test_segwit_sighash("010000000001715df5ccebaf02ff18d6fae7263fa69fed5de59c900f4749556eba41bc7bf2af0000000000000000000201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000124101100001f5175517551755175517551755175517551755175517551755175517551755101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000005f5e100000000000000", "76a914f54a5851e9372b87810a8e60cdd2e7cfd80b6e3188ac", 0, "0850863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352", EcdsaSighashType::All, "e201b4019129a03ca0304989731c6dccde232c854d86fce999b7411da1e90048");
test_segwit_sighash("010000000001715df5ccebaf02ff18d6fae7263fa69fed5de59c900f4749556eba41bc7bf2af0000000000000000000201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000124101100001f5175517551755175517551755175517551755175517551755175517551755101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000005f5e100000000000000", "76a914f54a5851e9372b87810a8e60cdd2e7cfd80b6e3188ac", 0, "0850863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352", EcdsaSighashType::None, "bfc6599816673083334ae82ac3459a2d0fef478d3e580e3ae203a28347502cb4");
test_segwit_sighash("010000000001715df5ccebaf02ff18d6fae7263fa69fed5de59c900f4749556eba41bc7bf2af0000000000000000000201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000124101100001f5175517551755175517551755175517551755175517551755175517551755101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000005f5e100000000000000", "76a914f54a5851e9372b87810a8e60cdd2e7cfd80b6e3188ac", 0, "0850863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352", EcdsaSighashType::Single, "4bc8546e32d31c5415444138184696e80f49e537a083bfcc89be2ab41d962e76");
test_segwit_sighash("010000000001715df5ccebaf02ff18d6fae7263fa69fed5de59c900f4749556eba41bc7bf2af0000000000000000000201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000124101100001f5175517551755175517551755175517551755175517551755175517551755101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000005f5e100000000000000", "76a914f54a5851e9372b87810a8e60cdd2e7cfd80b6e3188ac", 0, "0850863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352", EcdsaSighashType::AllPlusAnyoneCanPay, "b70ba5f4a1c2c48cd7f2104b2baa6a5c97987eb560916d39a5d427deb8b1dc2a");
test_segwit_sighash("010000000001715df5ccebaf02ff18d6fae7263fa69fed5de59c900f4749556eba41bc7bf2af0000000000000000000201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000124101100001f5175517551755175517551755175517551755175517551755175517551755101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000005f5e100000000000000", "76a914f54a5851e9372b87810a8e60cdd2e7cfd80b6e3188ac", 0, "0850863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352", EcdsaSighashType::NonePlusAnyoneCanPay, "6d6a4749c09ffd9a8df4c5de5d939325d896009e18f94bb095c9d7d695a8465e");
test_segwit_sighash("010000000001715df5ccebaf02ff18d6fae7263fa69fed5de59c900f4749556eba41bc7bf2af0000000000000000000201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000124101100001f5175517551755175517551755175517551755175517551755175517551755101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000005f5e100000000000000", "76a914f54a5851e9372b87810a8e60cdd2e7cfd80b6e3188ac", 0, "0850863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352", EcdsaSighashType::SinglePlusAnyoneCanPay, "7fc34367b42bf0e2bb78d8c20f45a64b81b2d4fbb59cbff8649322f619e88a0f");
test_segwit_sighash("010000000001715df5ccebaf02ff18d6fae7263fa69fed5de59c900f4749556eba41bc7bf2af0000000000000000000201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000124101100001f5175517551755175517551755175517551755175517551755175517551755101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000005f5e100000000000000", "76a914f54a5851e9372b87810a8e60cdd2e7cfd80b6e3188ac", 0, "010000000005f5e100", EcdsaSighashType::All, "71141639d982f1a1a8901e32fb1a9e15a0ea168b37d33300a3c9619fc3767388");
test_segwit_sighash("010000000001715df5ccebaf02ff18d6fae7263fa69fed5de59c900f4749556eba41bc7bf2af0000000000000000000201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000124101100001f5175517551755175517551755175517551755175517551755175517551755101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000005f5e100000000000000", "76a914f54a5851e9372b87810a8e60cdd2e7cfd80b6e3188ac", 0, "010000000005f5e100", EcdsaSighashType::None, "00730922d0e1d55b4b5fffafd087b06aeb44c4cedb58d8e182cbb9b87382cddb");
test_segwit_sighash("010000000001715df5ccebaf02ff18d6fae7263fa69fed5de59c900f4749556eba41bc7bf2af0000000000000000000201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000124101100001f5175517551755175517551755175517551755175517551755175517551755101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000005f5e100000000000000", "76a914f54a5851e9372b87810a8e60cdd2e7cfd80b6e3188ac", 0, "010000000005f5e100", EcdsaSighashType::Single, "100063ea0923ef4432dd51c5756383530f28b31ffe9d50b59a11b94a63c84c78");
test_segwit_sighash("010000000001715df5ccebaf02ff18d6fae7263fa69fed5de59c900f4749556eba41bc7bf2af0000000000000000000201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000124101100001f5175517551755175517551755175517551755175517551755175517551755101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000005f5e100000000000000", "76a914f54a5851e9372b87810a8e60cdd2e7cfd80b6e3188ac", 0, "010000000005f5e100", EcdsaSighashType::AllPlusAnyoneCanPay, "e1c4ddf5f723759f7d99d4f162155119160b1c6b765fdbdb25aedb2059769b74");
test_segwit_sighash("010000000001715df5ccebaf02ff18d6fae7263fa69fed5de59c900f4749556eba41bc7bf2af0000000000000000000201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000124101100001f5175517551755175517551755175517551755175517551755175517551755101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000005f5e100000000000000", "76a914f54a5851e9372b87810a8e60cdd2e7cfd80b6e3188ac", 0, "010000000005f5e100", EcdsaSighashType::NonePlusAnyoneCanPay, "b0be275e0c69e89ef5c482fdf330038c3b2994ebce3e3639bb81456d15a95a7a");
test_segwit_sighash("010000000001715df5ccebaf02ff18d6fae7263fa69fed5de59c900f4749556eba41bc7bf2af0000000000000000000201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000124101100001f5175517551755175517551755175517551755175517551755175517551755101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000005f5e100000000000000", "76a914f54a5851e9372b87810a8e60cdd2e7cfd80b6e3188ac", 0, "010000000005f5e100", EcdsaSighashType::SinglePlusAnyoneCanPay, "27c293da7a0f08e161fa2a77aeefa6743c929905597b5bcb28f2015fe648aa0c");
test_segwit_sighash("010000000001715df5ccebaf02ff18d6fae7263fa69fed5de59c900f4749556eba41bc7bf2af000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000003e801000000000000000a0201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000124101100001f5175517551755175517551755175517551755175517551755175517551755101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000005f5e100000000000000", "76a914f54a5851e9372b87810a8e60cdd2e7cfd80b6e3188ac", 0, "0850863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352", EcdsaSighashType::All, "ea946ee417d5a16a1038b2c3b54d1b7b12a9f98c0dcb4684bf005eb1c27d0c92");
}
fn test_legacy_sighash(tx: &str, script: &str, input_index: usize, hash_type: EcdsaSighashType, expected_result: &str) {
let tx: Transaction = deserialize(&Vec::<u8>::from_hex(tx).unwrap()[..]).unwrap();
let script = Script::from(Vec::<u8>::from_hex(script).unwrap());
let raw_expected = crate::hashes::sha256::Hash::from_str(expected_result).unwrap();
let expected_result = Sighash::from_slice(&raw_expected[..]).unwrap();
let sighash_cache = SighashCache::new(&tx);
let actual_result = sighash_cache.legacy_sighash(input_index, &script, hash_type);
assert_eq!(actual_result, expected_result);
}
#[test]
fn test_legacy_sighashes(){
test_legacy_sighash("010000000001715df5ccebaf02ff18d6fae7263fa69fed5de59c900f4749556eba41bc7bf2af0000000000000000000201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000124101100001f5175517551755175517551755175517551755175517551755175517551755101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000005f5e100000000000000", "76a914f54a5851e9372b87810a8e60cdd2e7cfd80b6e3188ac", 0, EcdsaSighashType::All, "769ad754a77282712895475eb17251bcb8f3cc35dc13406fa1188ef2707556cf");
test_legacy_sighash("010000000001715df5ccebaf02ff18d6fae7263fa69fed5de59c900f4749556eba41bc7bf2af0000000000000000000201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000124101100001f5175517551755175517551755175517551755175517551755175517551755101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000005f5e100000000000000", "76a914f54a5851e9372b87810a8e60cdd2e7cfd80b6e3188ac", 0, EcdsaSighashType::None, "b399ca018b4fec7d94e47092b72d25983db2d0d16eaa6a672050add66077ef40");
test_legacy_sighash("010000000001715df5ccebaf02ff18d6fae7263fa69fed5de59c900f4749556eba41bc7bf2af0000000000000000000201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000124101100001f5175517551755175517551755175517551755175517551755175517551755101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000005f5e100000000000000", "76a914f54a5851e9372b87810a8e60cdd2e7cfd80b6e3188ac", 0, EcdsaSighashType::Single, "4efef74996f840ed104c0b69461f33da2e364288f3015c55b2516a68e3ee60bc");
test_legacy_sighash("010000000001715df5ccebaf02ff18d6fae7263fa69fed5de59c900f4749556eba41bc7bf2af0000000000000000000201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000124101100001f5175517551755175517551755175517551755175517551755175517551755101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000005f5e100000000000000", "76a914f54a5851e9372b87810a8e60cdd2e7cfd80b6e3188ac", 0, EcdsaSighashType::AllPlusAnyoneCanPay, "a70a59ae29f1d9f4461f12e730e5cb75d3a75312666e8d911584aebb8e4afc5c");
test_legacy_sighash("010000000001715df5ccebaf02ff18d6fae7263fa69fed5de59c900f4749556eba41bc7bf2af0000000000000000000201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000124101100001f5175517551755175517551755175517551755175517551755175517551755101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000005f5e100000000000000", "76a914f54a5851e9372b87810a8e60cdd2e7cfd80b6e3188ac", 0, EcdsaSighashType::NonePlusAnyoneCanPay, "5f3694a35f3b994639d3fb1f6214ec166f9e0721c7ab3f216e465b9b2728d834");
test_legacy_sighash("010000000001715df5ccebaf02ff18d6fae7263fa69fed5de59c900f4749556eba41bc7bf2af0000000000000000000201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000124101100001f5175517551755175517551755175517551755175517551755175517551755101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000005f5e100000000000000", "76a914f54a5851e9372b87810a8e60cdd2e7cfd80b6e3188ac", 0, EcdsaSighashType::SinglePlusAnyoneCanPay, "4c18486c473dc31c264c477c55e9c17d70fddb9f567c7d411ce922261577167c");
test_legacy_sighash("010000000001715df5ccebaf02ff18d6fae7263fa69fed5de59c900f4749556eba41bc7bf2af000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000003e801000000000000000a0201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000124101100001f5175517551755175517551755175517551755175517551755175517551755101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000005f5e100000000000000", "76a914f54a5851e9372b87810a8e60cdd2e7cfd80b6e3188ac", 0, EcdsaSighashType::All, "9f00e1758a230aaf6c9bce777701a604f50b2ac5f2a07e1cd478d8a0e70fc195");
}
}