Skip to content

Commit e511670

Browse files
committed
Merge rust-bitcoin/rust-bitcoin#722: Allow specifing a raw TapLeafHash in sighash computation
2959e04 Allow specifing a raw `TapLeafHash` in sighash computation (Alekos Filini) Pull request description: Still need to add some tests but the code should be ready for review. Please let me know if you have better ideas for the enum naming. --- Instead of always requiring the full raw script and leaf version, allow just specifying a raw leaf hash to the sighash computation functions. This is very useful when dealing with PSBTs, because the `PSBT_IN_TAP_BIP32_DERIVATION` field only maps a public key to a leaf hash, so a signer could just take it and produce a signature with it rathern than having to jump through hoops to recover the full raw script. ACKs for top commit: sanket1729: Tested locally. ACK 2959e04. Reviewed range-diff with 5aa1d02 apoelstra: ACK 2959e04 Tree-SHA512: 830be0b8382ac59b73e6481f61ec1effdcd32859c04382e6cd5a43ac689d6e528f9a8b27c026ee81f5d5b59d2e3c397f9c271145e001ff2dc4815764fc21a2c6
2 parents b945a5e + 2959e04 commit e511670

File tree

1 file changed

+104
-35
lines changed

1 file changed

+104
-35
lines changed

src/util/sighash.rs

Lines changed: 104 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,11 @@ pub enum Prevouts<'u> {
9494
const KEY_VERSION_0: u8 = 0u8;
9595

9696
/// Information related to the script path spending
97+
///
98+
/// This can be hashed into a [`TapLeafHash`].
9799
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
98100
pub struct ScriptPath<'s> {
99101
script: &'s Script,
100-
code_separator_pos: u32,
101102
leaf_version: LeafVersion,
102103
}
103104

@@ -220,16 +221,30 @@ impl<'u> Prevouts<'u> {
220221

221222
impl<'s> ScriptPath<'s> {
222223
/// Create a new ScriptPath structure
223-
pub fn new(script: &'s Script, code_separator_pos: u32, leaf_version: LeafVersion) -> Self {
224+
pub fn new(script: &'s Script, leaf_version: LeafVersion) -> Self {
224225
ScriptPath {
225226
script,
226-
code_separator_pos,
227227
leaf_version,
228228
}
229229
}
230-
/// Create a new ScriptPath structure using default values for `code_separator_pos` and `leaf_version`
230+
/// Create a new ScriptPath structure using default leaf version value
231231
pub fn with_defaults(script: &'s Script) -> Self {
232-
Self::new(script, 0xFFFFFFFFu32, LeafVersion::default())
232+
Self::new(script, LeafVersion::default())
233+
}
234+
/// Compute the leaf hash
235+
pub fn leaf_hash(&self) -> TapLeafHash {
236+
let mut enc = TapLeafHash::engine();
237+
238+
self.leaf_version.as_u8().consensus_encode(&mut enc).expect("Writing to hash enging should never fail");
239+
self.script.consensus_encode(&mut enc).expect("Writing to hash enging should never fail");
240+
241+
TapLeafHash::from_engine(enc)
242+
}
243+
}
244+
245+
impl<'s> From<ScriptPath<'s>> for TapLeafHash {
246+
fn from(script_path: ScriptPath<'s>) -> TapLeafHash {
247+
script_path.leaf_hash()
233248
}
234249
}
235250

@@ -298,7 +313,7 @@ impl<R: Deref<Target = Transaction>> SigHashCache<R> {
298313
input_index: usize,
299314
prevouts: &Prevouts,
300315
annex: Option<Annex>,
301-
script_path: Option<ScriptPath>,
316+
leaf_hash_code_separator: Option<(TapLeafHash, u32)>,
302317
sighash_type: SchnorrSigHashType,
303318
) -> Result<(), Error> {
304319
prevouts.check_all(&self.tx)?;
@@ -350,7 +365,7 @@ impl<R: Deref<Target = Transaction>> SigHashCache<R> {
350365
if annex.is_some() {
351366
spend_type |= 1u8;
352367
}
353-
if script_path.is_some() {
368+
if leaf_hash_code_separator.is_some() {
354369
spend_type |= 2u8;
355370
}
356371
spend_type.consensus_encode(&mut writer)?;
@@ -412,17 +427,7 @@ impl<R: Deref<Target = Transaction>> SigHashCache<R> {
412427
// ss += TaggedHash("TapLeaf", bytes([leaf_ver]) + ser_string(script))
413428
// ss += bytes([0])
414429
// ss += struct.pack("<i", codeseparator_pos)
415-
if let Some(ScriptPath {
416-
script,
417-
leaf_version,
418-
code_separator_pos,
419-
}) = script_path
420-
{
421-
let mut enc = TapLeafHash::engine();
422-
leaf_version.as_u8().consensus_encode(&mut enc)?;
423-
script.consensus_encode(&mut enc)?;
424-
let hash = TapLeafHash::from_engine(enc);
425-
430+
if let Some((hash, code_separator_pos)) = leaf_hash_code_separator {
426431
hash.into_inner().consensus_encode(&mut writer)?;
427432
KEY_VERSION_0.consensus_encode(&mut writer)?;
428433
code_separator_pos.consensus_encode(&mut writer)?;
@@ -437,7 +442,7 @@ impl<R: Deref<Target = Transaction>> SigHashCache<R> {
437442
input_index: usize,
438443
prevouts: &Prevouts,
439444
annex: Option<Annex>,
440-
script_path: Option<ScriptPath>,
445+
leaf_hash_code_separator: Option<(TapLeafHash, u32)>,
441446
sighash_type: SchnorrSigHashType,
442447
) -> Result<TapSighashHash, Error> {
443448
let mut enc = TapSighashHash::engine();
@@ -446,7 +451,49 @@ impl<R: Deref<Target = Transaction>> SigHashCache<R> {
446451
input_index,
447452
prevouts,
448453
annex,
449-
script_path,
454+
leaf_hash_code_separator,
455+
sighash_type,
456+
)?;
457+
Ok(TapSighashHash::from_engine(enc))
458+
}
459+
460+
/// Compute the BIP341 sighash for a key spend
461+
pub fn taproot_key_spend_signature_hash(
462+
&mut self,
463+
input_index: usize,
464+
prevouts: &Prevouts,
465+
sighash_type: SchnorrSigHashType,
466+
) -> Result<TapSighashHash, Error> {
467+
let mut enc = TapSighashHash::engine();
468+
self.taproot_encode_signing_data_to(
469+
&mut enc,
470+
input_index,
471+
prevouts,
472+
None,
473+
None,
474+
sighash_type,
475+
)?;
476+
Ok(TapSighashHash::from_engine(enc))
477+
}
478+
479+
/// Compute the BIP341 sighash for a script spend
480+
///
481+
/// Assumes the default `OP_CODESEPARATOR` position of `0xFFFFFFFF`. Custom values can be
482+
/// provided through the more fine-grained API of [`SigHashCache::taproot_encode_signing_data_to`].
483+
pub fn taproot_script_spend_signature_hash<S: Into<TapLeafHash>>(
484+
&mut self,
485+
input_index: usize,
486+
prevouts: &Prevouts,
487+
leaf_hash: S,
488+
sighash_type: SchnorrSigHashType,
489+
) -> Result<TapSighashHash, Error> {
490+
let mut enc = TapSighashHash::engine();
491+
self.taproot_encode_signing_data_to(
492+
&mut enc,
493+
input_index,
494+
prevouts,
495+
None,
496+
Some((leaf_hash.into(), 0xFFFFFFFF)),
450497
sighash_type,
451498
)?;
452499
Ok(TapSighashHash::from_engine(enc))
@@ -703,7 +750,7 @@ mod tests {
703750
use consensus::deserialize;
704751
use hashes::hex::FromHex;
705752
use hashes::{Hash, HashEngine};
706-
use util::sighash::{Annex, Error, Prevouts, ScriptPath, SigHashCache};
753+
use util::sighash::{Annex, Error, Prevouts, ScriptPath, SigHashCache, TapLeafHash};
707754
use util::taproot::TapSighashHash;
708755
use {Script, Transaction, TxIn, TxOut};
709756

@@ -728,55 +775,55 @@ mod tests {
728775
"01365724000000000023542156b39dab4f8f3508e0432cfb41fab110170acaa2d4c42539cb90a4dc7c093bc500",
729776
0,
730777
"33ca0ebfb4a945eeee9569fc0f5040221275f88690b7f8592ada88ce3bdf6703",
731-
SchnorrSigHashType::Default, None,None,
778+
SchnorrSigHashType::Default, None, None, None
732779
);
733780

734781
test_taproot_sighash(
735782
"0200000002fff49be59befe7566050737910f6ccdc5e749c7f8860ddc140386463d88c5ad0f3000000002cf68eb4a3d67f9d4c079249f7e4f27b8854815cb1ed13842d4fbf395f9e217fd605ee24090100000065235d9203f458520000000000160014b6d48333bb13b4c644e57c43a9a26df3a44b785e58020000000000001976a914eea9461a9e1e3f765d3af3e726162e0229fe3eb688ac58020000000000001976a9143a8869c9f2b5ea1d4ff3aeeb6a8fb2fffb1ad5fe88ac0ad7125c",
736783
"02591f220000000000225120f25ad35583ea31998d968871d7de1abd2a52f6fe4178b54ea158274806ff4ece48fb310000000000225120f25ad35583ea31998d968871d7de1abd2a52f6fe4178b54ea158274806ff4ece",
737784
1,
738785
"626ab955d58c9a8a600a0c580549d06dc7da4e802eb2a531f62a588e430967a8",
739-
SchnorrSigHashType::All, None,None,
786+
SchnorrSigHashType::All, None, None, None
740787
);
741788

742789
test_taproot_sighash(
743790
"0200000001350005f65aa830ced2079df348e2d8c2bdb4f10e2dde6a161d8a07b40d1ad87dae000000001611d0d603d9dc0e000000000017a914459b6d7d6bbb4d8837b4bf7e9a4556f952da2f5c8758020000000000001976a9141dd70e1299ffc2d5b51f6f87de9dfe9398c33cbb88ac58020000000000001976a9141dd70e1299ffc2d5b51f6f87de9dfe9398c33cbb88aca71c1f4f",
744791
"01c4811000000000002251201bf9297d0a2968ae6693aadd0fa514717afefd218087a239afb7418e2d22e65c",
745792
0,
746793
"dfa9437f9c9a1d1f9af271f79f2f5482f287cdb0d2e03fa92c8a9b216cc6061c",
747-
SchnorrSigHashType::AllPlusAnyoneCanPay, None,None,
794+
SchnorrSigHashType::AllPlusAnyoneCanPay, None, None, None
748795
);
749796

750797
test_taproot_sighash(
751798
"020000000185bed1a6da2bffbd60ec681a1bfb71c5111d6395b99b3f8b2bf90167111bcb18f5010000007c83ace802ded24a00000000001600142c4698f9f7a773866879755aa78c516fb332af8e5802000000000000160014d38639dfbac4259323b98a472405db0c461b31fa61073747",
752799
"0144c84d0000000000225120e3f2107989c88e67296ab2faca930efa2e3a5bd3ff0904835a11c9e807458621",
753800
0,
754801
"3129de36a5d05fff97ffca31eb75fcccbbbc27b3147a7a36a9e4b45d8b625067",
755-
SchnorrSigHashType::None, None,None,
802+
SchnorrSigHashType::None, None, None, None
756803
);
757804

758805
test_taproot_sighash(
759806
"eb93dbb901028c8515589dac980b6e7f8e4088b77ed866ca0d6d210a7218b6fd0f6b22dd6d7300000000eb4740a9047efc0e0000000000160014913da2128d8fcf292b3691db0e187414aa1783825802000000000000160014913da2128d8fcf292b3691db0e187414aa178382580200000000000017a9143dd27f01c6f7ef9bb9159937b17f17065ed01a0c875802000000000000160014d7630e19df70ada9905ede1722b800c0005f246641000000",
760807
"013fed110000000000225120eb536ae8c33580290630fc495046e998086a64f8f33b93b07967d9029b265c55",
761808
0,
762809
"2441e8b0e063a2083ee790f14f2045022f07258ddde5ee01de543c9e789d80ae",
763-
SchnorrSigHashType::NonePlusAnyoneCanPay, None,None,
810+
SchnorrSigHashType::NonePlusAnyoneCanPay, None, None, None
764811
);
765812

766813
test_taproot_sighash(
767814
"02000000017836b409a5fed32211407e44b971591f2032053f14701fb5b3a30c0ff382f2cc9c0100000061ac55f60288fb5600000000001976a9144ea02f6f182b082fb6ce47e36bbde390b6a41b5088ac58020000000000001976a9144ea02f6f182b082fb6ce47e36bbde390b6a41b5088ace4000000",
768815
"01efa558000000000022512007071ea3dc7e331b0687d0193d1e6d6ed10e645ef36f10ef8831d5e522ac9e80",
769816
0,
770817
"30239345177cadd0e3ea413d49803580abb6cb27971b481b7788a78d35117a88",
771-
SchnorrSigHashType::Single, None,None,
818+
SchnorrSigHashType::Single, None, None, None
772819
);
773820

774821
test_taproot_sighash(
775822
"0100000001aa6deae89d5e0aaca58714fc76ef6f3c8284224888089232d4e663843ed3ab3eae010000008b6657a60450cb4c0000000000160014a3d42b5413ef0c0701c4702f3cd7d4df222c147058020000000000001976a91430b4ed8723a4ee8992aa2c8814cfe5c3ad0ab9d988ac5802000000000000160014365b1166a6ed0a5e8e9dff17a6d00bbb43454bc758020000000000001976a914bc98c51a84fe7fad5dc380eb8b39586eff47241688ac4f313247",
776823
"0107af4e00000000002251202c36d243dfc06cb56a248e62df27ecba7417307511a81ae61aa41c597a929c69",
777824
0,
778825
"bf9c83f26c6dd16449e4921f813f551c4218e86f2ec906ca8611175b41b566df",
779-
SchnorrSigHashType::SinglePlusAnyoneCanPay, None,None,
826+
SchnorrSigHashType::SinglePlusAnyoneCanPay, None, None, None
780827
);
781828
}
782829

@@ -790,6 +837,7 @@ mod tests {
790837
SchnorrSigHashType::SinglePlusAnyoneCanPay,
791838
Some("507b979802e62d397acb29f56743a791894b99372872fc5af06a4f6e8d242d0615cda53062bb20e6ec79756fe39183f0c128adfe85559a8fa042b042c018aa8010143799e44f0893c40e1e"),
792839
None,
840+
None,
793841
);
794842
}
795843

@@ -803,6 +851,21 @@ mod tests {
803851
SchnorrSigHashType::All,
804852
None,
805853
Some("20cc4e1107aea1d170c5ff5b6817e1303010049724fb3caa7941792ea9d29b3e2bacab"),
854+
None,
855+
);
856+
}
857+
858+
#[test]
859+
fn test_sighashes_with_script_path_raw_hash() {
860+
test_taproot_sighash(
861+
"020000000189fc651483f9296b906455dd939813bf086b1bbe7c77635e157c8e14ae29062195010000004445b5c7044561320000000000160014331414dbdada7fb578f700f38fb69995fc9b5ab958020000000000001976a914268db0a8104cc6d8afd91233cc8b3d1ace8ac3ef88ac580200000000000017a914ec00dcb368d6a693e11986d265f659d2f59e8be2875802000000000000160014c715799a49a0bae3956df9c17cb4440a673ac0df6f010000",
862+
"011bec34000000000022512028055142ea437db73382e991861446040b61dd2185c4891d7daf6893d79f7182",
863+
0,
864+
"d66de5274a60400c7b08c86ba6b7f198f40660079edf53aca89d2a9501317f2e",
865+
SchnorrSigHashType::All,
866+
None,
867+
None,
868+
Some("15a2530514e399f8b5cf0b3d3112cf5b289eaa3e308ba2071b58392fdc6da68a"),
806869
);
807870
}
808871

@@ -816,6 +879,7 @@ mod tests {
816879
SchnorrSigHashType::All,
817880
Some("50a6272b470e1460e3332ade7bb14b81671c564fb6245761bd5bd531394b28860e0b3808ab229fb51791fb6ae6fa82d915b2efb8f6df83ae1f5ab3db13e30928875e2a22b749d89358de481f19286cd4caa792ce27f9559082d227a731c5486882cc707f83da361c51b7aadd9a0cf68fe7480c410fa137b454482d9a1ebf0f96d760b4d61426fc109c6e8e99a508372c45caa7b000a41f8251305da3f206c1849985ba03f3d9592832b4053afbd23ab25d0465df0bc25a36c223aacf8e04ec736a418c72dc319e4da3e972e349713ca600965e7c665f2090d5a70e241ac164115a1f5639f28b1773327715ca307ace64a2de7f0e3df70a2ffee3857689f909c0dad46d8a20fa373a4cc6eed6d4c9806bf146f0d76baae1"),
818881
Some("7520ab9160dd8299dc1367659be3e8f66781fe440d52940c7f8d314a89b9f2698d406ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6eadac"),
882+
None,
819883
);
820884
}
821885

@@ -888,6 +952,7 @@ mod tests {
888952
sighash_type: SchnorrSigHashType,
889953
annex_hex: Option<&str>,
890954
script_hex: Option<&str>,
955+
script_leaf_hash: Option<&str>,
891956
) {
892957
let tx_bytes = Vec::from_hex(tx_hex).unwrap();
893958
let tx: Transaction = deserialize(&tx_bytes).unwrap();
@@ -902,14 +967,18 @@ mod tests {
902967
None => None,
903968
};
904969

905-
let script_inner;
906-
let script_path = match script_hex {
907-
Some(script_hex) => {
908-
script_inner = Script::from_hex(script_hex).unwrap();
909-
Some(ScriptPath::with_defaults(&script_inner))
970+
let leaf_hash = match (script_hex, script_leaf_hash) {
971+
(Some(script_hex), _) => {
972+
let script_inner = Script::from_hex(script_hex).unwrap();
973+
Some(ScriptPath::with_defaults(&script_inner).leaf_hash())
910974
}
911-
None => None,
975+
(_, Some(script_leaf_hash)) => {
976+
Some(TapLeafHash::from_hex(script_leaf_hash).unwrap())
977+
}
978+
_ => None,
912979
};
980+
// All our tests use the default `0xFFFFFFFF` codeseparator value
981+
let leaf_hash = leaf_hash.map(|lh| (lh, 0xFFFFFFFF));
913982

914983
let prevouts = if sighash_type.split_anyonecanpay_flag().1 && tx_bytes[0] % 2 == 0 {
915984
// for anyonecanpay the `Prevouts::All` variant is good anyway, but sometimes we want to
@@ -922,7 +991,7 @@ mod tests {
922991
let mut sig_hash_cache = SigHashCache::new(&tx);
923992

924993
let hash = sig_hash_cache
925-
.taproot_signature_hash(input_index, &prevouts, annex, script_path, sighash_type)
994+
.taproot_signature_hash(input_index, &prevouts, annex, leaf_hash, sighash_type)
926995
.unwrap();
927996
let expected = Vec::from_hex(expected_hash).unwrap();
928997
assert_eq!(expected, hash.into_inner());

0 commit comments

Comments
 (0)