Skip to content

Commit 21d182a

Browse files
committed
Add taproot Inner inference
1 parent d3512bc commit 21d182a

File tree

3 files changed

+65
-9
lines changed

3 files changed

+65
-9
lines changed

src/interpreter/error.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
//
1414

1515
use bitcoin::hashes::{hash160, hex::ToHex};
16+
use bitcoin::util::taproot;
1617
use bitcoin::{self, secp256k1};
1718
use std::{error, fmt};
1819

@@ -28,6 +29,10 @@ pub enum Error {
2829
/// Inferring script spends is possible, but is hidden nodes are currently
2930
/// not supported in descriptor spec
3031
CannotInferTrDescriptors,
32+
/// Error parsing taproot control block
33+
ControlBlockParse(taproot::TaprootError),
34+
/// Tap control block(merkle proofs + tweak) verification error
35+
ControlBlockVerificationError,
3136
/// General Interpreter error.
3237
CouldNotEvaluate,
3338
/// EcdsaSig related error
@@ -92,6 +97,8 @@ pub enum Error {
9297
SchnorrSig(bitcoin::SchnorrSigError),
9398
/// Errors in signature hash calculations
9499
SighashError(bitcoin::util::sighash::Error),
100+
/// Taproot Annex Unsupported
101+
TapAnnexUnsupported,
95102
/// An uncompressed public key was encountered in a context where it is
96103
/// disallowed (e.g. in a Segwit script or p2wpkh output)
97104
UncompressedPubkey,
@@ -190,6 +197,10 @@ impl fmt::Display for Error {
190197
n
191198
),
192199
Error::CannotInferTrDescriptors => write!(f, "Cannot infer taproot descriptors"),
200+
Error::ControlBlockParse(ref e) => write!(f, "Control block parse error {}", e),
201+
Error::ControlBlockVerificationError => {
202+
f.write_str("Control block verification failed")
203+
}
193204
Error::EcdsaSig(ref s) => write!(f, "Ecdsa sig error: {}", s),
194205
Error::ExpectedPush => f.write_str("expected push in script"),
195206
Error::CouldNotEvaluate => f.write_str("Interpreter Error: Could not evaluate"),
@@ -235,6 +246,7 @@ impl fmt::Display for Error {
235246
Error::Secp(ref e) => fmt::Display::fmt(e, f),
236247
Error::SchnorrSig(ref s) => write!(f, "Schnorr sig error: {}", s),
237248
Error::SighashError(ref e) => fmt::Display::fmt(e, f),
249+
Error::TapAnnexUnsupported => f.write_str("Encounter Annex element"),
238250
Error::UncompressedPubkey => {
239251
f.write_str("uncompressed pubkey in non-legacy descriptor")
240252
}

src/interpreter/inner.rs

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
use bitcoin;
1616
use bitcoin::blockdata::witness::Witness;
1717
use bitcoin::hashes::{hash160, sha256, Hash};
18+
use bitcoin::schnorr::TapTweak;
19+
use bitcoin::util::taproot::{ControlBlock, TAPROOT_ANNEX_PREFIX};
1820

19-
use {BareCtx, Legacy, Segwitv0};
21+
use {BareCtx, Legacy, Segwitv0, Tap};
2022

2123
use super::{stack, BitcoinKey, Error, Stack, TaggedHash160};
2224
use miniscript::context::{NoChecksEcdsa, ScriptContext};
@@ -209,15 +211,48 @@ pub(super) fn from_txdata<'txin>(
209211
.map_err(|_| Error::XOnlyPublicKeyParseError)?;
210212
if wit_stack.len() == 1 {
211213
// Key spend
212-
// let sig =
213-
// Tr inference to be done in future commit
214-
panic!("TODO");
214+
Ok((
215+
Inner::PublicKey(output_key.into(), PubkeyType::Tr),
216+
wit_stack,
217+
None, // Tr script code None
218+
))
219+
} else {
220+
// wit_stack.len() >=2
221+
// Check for annex
222+
let ctrl_blk = wit_stack.pop().ok_or(Error::UnexpectedStackEnd)?;
223+
let ctrl_blk = ctrl_blk.as_push()?;
224+
let tap_script = wit_stack.pop().ok_or(Error::UnexpectedStackEnd)?;
225+
if ctrl_blk.len() > 0 && ctrl_blk[0] == TAPROOT_ANNEX_PREFIX {
226+
// Annex is non-standard, bitcoin consensus rules ignore it.
227+
// Our sighash structure and signature verification
228+
// does not support annex, return error
229+
return Err(Error::TapAnnexUnsupported);
230+
} else if wit_stack.len() >= 2 {
231+
let ctrl_blk = ControlBlock::from_slice(ctrl_blk)
232+
.map_err(|e| Error::ControlBlockParse(e))?;
233+
let tap_script = script_from_stackelem::<Tap>(&tap_script)?;
234+
let ms = taproot_to_no_checks(&tap_script);
235+
// Creating new contexts is cheap
236+
let secp = bitcoin::secp256k1::Secp256k1::verification_only();
237+
// Should not really need to call dangerous assumed tweaked here.
238+
// Should be fixed after RC
239+
if ctrl_blk.verify_taproot_commitment(
240+
&secp,
241+
&output_key.dangerous_assume_tweaked(),
242+
&tap_script.encode(),
243+
) {
244+
Ok((
245+
Inner::Script(ms, ScriptType::Tr),
246+
wit_stack,
247+
None, // Tr script code None
248+
))
249+
} else {
250+
return Err(Error::ControlBlockVerificationError);
251+
}
252+
} else {
253+
return Err(Error::UnexpectedStackBoolean);
254+
}
215255
}
216-
Ok((
217-
Inner::PublicKey(output_key.into(), PubkeyType::Tr),
218-
ssig_stack,
219-
None, // Tr script code None
220-
))
221256
}
222257
// ** pay to scripthash **
223258
} else if spk.is_p2sh() {

src/interpreter/stack.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ impl<'txin> Element<'txin> {
7070
_ => Err(Error::ExpectedPush),
7171
}
7272
}
73+
74+
// Get push element as slice, returning UnexpectedBool otherwise
75+
pub(super) fn as_push(&self) -> Result<&[u8], Error> {
76+
if let Element::Push(sl) = *self {
77+
Ok(sl)
78+
} else {
79+
Err(Error::UnexpectedStackBoolean)
80+
}
81+
}
7382
}
7483

7584
/// Stack Data structure representing the stack input to Miniscript. This Stack

0 commit comments

Comments
 (0)