Skip to content

Commit 9d67260

Browse files
committed
Merge #301: Taproot interpreter support
be83ac7 Update Control block verification (sanket1729) 1929518 Add ToNoChecks trait (sanket1729) 8ef682f comments: clean up the comments around the "scriptcode" hack in Interpreter (Andrew Poelstra) be5a536 Integration test fixups (sanket1729) a9c7f41 Update interpreter API in psbt (sanket1729) a5b0b28 Cleanup iter APIs, retintroduce examples (sanket1729) 16d0830 Remove NoChecksSchnorr (sanket1729) 6c9e436 Interpreter sighash API support, no more closures (sanket1729) 21d182a Add taproot Inner inference (sanket1729) d3512bc Overall Iter struct so that there is no closure (sanket1729) c28ac99 Remove mut from Interpreter type for iterator (sanket1729) 32963a0 Rework Interpreter: Prepare of tap support (sanket1729) 73cd5a8 Update to 0.28.0-rc.1 (sanket1729) Pull request description: Add taproot interpreter support. This is the second to last PR for taproot miniscript support. The last PR will add some more high-level APIs for taproot psbt and integration tests. ACKs for top commit: apoelstra: ACK be83ac7 Tree-SHA512: 2521e1c54d44540f7873ccad4111f0787d448c3ab701fbca063eeb158593ec2a3c7bcedb7e8d070da4bf90c6ae024bd07d3fc08b46262d7184a1efbf6f585807
2 parents 239e61e + be83ac7 commit 9d67260

File tree

21 files changed

+1047
-685
lines changed

21 files changed

+1047
-685
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ rand = ["bitcoin/rand"]
1717

1818
[dependencies]
1919
# bitcoin = "0.27"
20-
bitcoin = {git = "https://github.com/rust-bitcoin/rust-bitcoin", rev = "0e2e55971275da64ceb62e8991a0a5fa962cb8b1"}
20+
bitcoin = "0.28.0-rc.1"
2121

2222
[dependencies.serde]
2323
version = "1.0"

examples/verify_tx.rs

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ extern crate miniscript;
1919

2020
use bitcoin::consensus::Decodable;
2121
use bitcoin::secp256k1; // secp256k1 re-exported from rust-bitcoin
22+
use bitcoin::util::sighash;
23+
use miniscript::interpreter::KeySigPair;
2224
use std::str::FromStr;
2325

2426
fn main() {
@@ -83,7 +85,7 @@ fn main() {
8385
0xa9, 0x14, 0x92, 0x09, 0xa8, 0xf9, 0x0c, 0x58, 0x4b, 0xb5, 0x97, 0x4d, 0x58, 0x68, 0x72,
8486
0x49, 0xe5, 0x32, 0xde, 0x59, 0xf4, 0xbc, 0x87,
8587
]);
86-
let mut interpreter = miniscript::Interpreter::from_txdata(
88+
let interpreter = miniscript::Interpreter::from_txdata(
8789
&spk_input_1,
8890
&transaction.input[0].script_sig,
8991
&transaction.input[0].witness,
@@ -105,10 +107,14 @@ fn main() {
105107
// the blockchain, standardness would've required they be
106108
// either valid or 0-length.
107109
println!("\nExample one");
108-
for elem in interpreter.iter(|_, _| true) {
110+
for elem in interpreter.iter_assume_sigs() {
109111
// Don't bother checking signatures
110112
match elem.expect("no evaluation error") {
111-
miniscript::interpreter::SatisfiedConstraint::PublicKey { key, sig } => {
113+
miniscript::interpreter::SatisfiedConstraint::PublicKey { key_sig } => {
114+
// Check that the signature is ecdsa sig
115+
let (key, sig) = key_sig
116+
.as_ecdsa()
117+
.expect("Expected Ecdsa sig, found schnorr sig");
112118
println!("Signed with {}: {}", key, sig);
113119
}
114120
_ => {}
@@ -122,7 +128,7 @@ fn main() {
122128
// from the MiniscriptKey which can supplied by `to_pk_ctx` parameter. For example,
123129
// when calculating the script pubkey of a descriptor with xpubs, the secp context and
124130
// child information maybe required.
125-
let mut interpreter = miniscript::Interpreter::from_txdata(
131+
let interpreter = miniscript::Interpreter::from_txdata(
126132
&spk_input_1,
127133
&transaction.input[0].script_sig,
128134
&transaction.input[0].witness,
@@ -131,21 +137,15 @@ fn main() {
131137
)
132138
.unwrap();
133139

134-
// We can set the amount passed to `sighash_verify` to 0 because this is a legacy
135-
// transaction and so the amount won't actually be checked by the signature
136-
let vfyfn = interpreter
137-
.sighash_verify(&secp, &transaction, 0, 0)
138-
.expect("Can only fail in sighash single when corresponding output is not present");
139-
// Restrict to sighash_all just to demonstrate how to add additional filters
140-
// `&_` needed here because of https://github.com/rust-lang/rust/issues/79187
141-
let vfyfn = move |pk: &_, bitcoinsig: miniscript::bitcoin::EcdsaSig| {
142-
bitcoinsig.hash_ty == bitcoin::EcdsaSigHashType::All && vfyfn(pk, bitcoinsig)
143-
};
140+
// We can set prevouts to be empty list because this is a legacy transaction
141+
// and this information is not required for sighash computation.
142+
let prevouts = sighash::Prevouts::All(&[]);
144143

145144
println!("\nExample two");
146-
for elem in interpreter.iter(vfyfn) {
145+
for elem in interpreter.iter(&secp, &transaction, 0, &prevouts) {
147146
match elem.expect("no evaluation error") {
148-
miniscript::interpreter::SatisfiedConstraint::PublicKey { key, sig } => {
147+
miniscript::interpreter::SatisfiedConstraint::PublicKey { key_sig } => {
148+
let (key, sig) = key_sig.as_ecdsa().unwrap();
149149
println!("Signed with {}: {}", key, sig);
150150
}
151151
_ => {}
@@ -156,7 +156,7 @@ fn main() {
156156
// what happens given an apparently invalid script
157157
let secp = secp256k1::Secp256k1::new();
158158
let message = secp256k1::Message::from_slice(&[0x01; 32][..]).expect("32-byte hash");
159-
let mut interpreter = miniscript::Interpreter::from_txdata(
159+
let interpreter = miniscript::Interpreter::from_txdata(
160160
&spk_input_1,
161161
&transaction.input[0].script_sig,
162162
&transaction.input[0].witness,
@@ -165,10 +165,13 @@ fn main() {
165165
)
166166
.unwrap();
167167

168-
let iter = interpreter.iter(|pk, ecdsa_sig| {
168+
let iter = interpreter.iter_custom(Box::new(|key_sig: &KeySigPair| {
169+
let (pk, ecdsa_sig) = key_sig.as_ecdsa().expect("Ecdsa Sig");
169170
ecdsa_sig.hash_ty == bitcoin::EcdsaSigHashType::All
170-
&& secp.verify_ecdsa(&message, &ecdsa_sig.sig, &pk.key).is_ok()
171-
});
171+
&& secp
172+
.verify_ecdsa(&message, &ecdsa_sig.sig, &pk.inner)
173+
.is_ok()
174+
}));
172175
println!("\nExample three");
173176
for elem in iter {
174177
let error = elem.expect_err("evaluation error");

integration_test/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ authors = ["Steven Roose <[email protected]>", "Sanket K <sanket1729@gmail.
77
miniscript = {path = "../"}
88

99
# Until 0.26 support is released on rust-bitcoincore-rpc
10-
bitcoincore-rpc = {git = "https://github.com/sanket1729/rust-bitcoincore-rpc",rev = "ae3ad6cac0a83454f267cb7d5191f6607bb80297"}
11-
bitcoin = {git = "https://github.com/rust-bitcoin/rust-bitcoin", rev = "0e2e55971275da64ceb62e8991a0a5fa962cb8b1"}
10+
bitcoincore-rpc = {git = "https://github.com/sanket1729/rust-bitcoincore-rpc",rev = "bcc35944b3dd636cdff9710f90f8e0cfcab28f27"}
11+
bitcoin = "0.28.0-rc.1"
1212
log = "0.4"

integration_test/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ fn main() {
207207
for sk in sks_reqd {
208208
let sig = secp.sign_ecdsa(&msg, &sk);
209209
let pk = pks[sks.iter().position(|&x| x == sk).unwrap()];
210-
psbts[i].inputs[0].partial_sigs.insert(pk, bitcoin::EcdsaSig { sig, hash_ty: sighash_ty });
210+
psbts[i].inputs[0].partial_sigs.insert(pk.inner, bitcoin::EcdsaSig { sig, hash_ty: sighash_ty });
211211
}
212212
// Add the hash preimages to the psbt
213213
psbts[i].inputs[0].sha256_preimages.insert(

integration_test/src/read_file.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ fn setup_keys(
6060

6161
let sk = secp256k1::SecretKey::from_slice(&sk[..]).expect("secret key");
6262
let pk = miniscript::bitcoin::PublicKey {
63-
key: secp256k1::PublicKey::from_secret_key(&secp_sign, &sk),
63+
inner: secp256k1::PublicKey::from_secret_key(&secp_sign, &sk),
6464
compressed: true,
6565
};
6666
pks.push(pk);

src/descriptor/key.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ use bitcoin::{
44
self,
55
hashes::Hash,
66
hashes::{hex::FromHex, HashEngine},
7-
schnorr::XOnlyPublicKey,
87
secp256k1,
98
secp256k1::{Secp256k1, Signing},
109
util::bip32,
11-
XpubIdentifier,
10+
XOnlyPublicKey, XpubIdentifier,
1211
};
1312

1413
use {MiniscriptKey, ToPublicKey};

src/descriptor/mod.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,10 +1023,7 @@ mod tests {
10231023
let secp = secp256k1::Secp256k1::new();
10241024
let sk =
10251025
secp256k1::SecretKey::from_slice(&b"sally was a secret key, she said"[..]).unwrap();
1026-
let pk = bitcoin::PublicKey {
1027-
key: secp256k1::PublicKey::from_secret_key(&secp, &sk),
1028-
compressed: true,
1029-
};
1026+
let pk = bitcoin::PublicKey::new(secp256k1::PublicKey::from_secret_key(&secp, &sk));
10301027
let msg = secp256k1::Message::from_slice(&b"michael was a message, amusingly"[..])
10311028
.expect("32 bytes");
10321029
let sig = secp.sign_ecdsa(&msg, &sk);

src/descriptor/sortedmulti.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,9 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
140140
// Sort pubkeys lexicographically according to BIP 67
141141
pks.sort_by(|a, b| {
142142
a.to_public_key()
143-
.key
143+
.inner
144144
.serialize()
145-
.partial_cmp(&b.to_public_key().key.serialize())
145+
.partial_cmp(&b.to_public_key().inner.serialize())
146146
.unwrap()
147147
});
148148
Terminal::Multi(self.k, pks)

src/interpreter/error.rs

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,30 @@
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

20+
use super::BitcoinKey;
21+
1922
/// Detailed Error type for Interpreter
2023
#[derive(Debug)]
2124
pub enum Error {
2225
/// Could not satisfy, absolute locktime not met
2326
AbsoluteLocktimeNotMet(u32),
27+
/// Cannot Infer a taproot descriptor
28+
/// Key spends cannot infer the internal key of the descriptor
29+
/// Inferring script spends is possible, but is hidden nodes are currently
30+
/// not supported in descriptor spec
31+
CannotInferTrDescriptors,
32+
/// Error parsing taproot control block
33+
ControlBlockParse(taproot::TaprootError),
34+
/// Tap control block(merkle proofs + tweak) verification error
35+
ControlBlockVerificationError,
2436
/// General Interpreter error.
2537
CouldNotEvaluate,
38+
/// EcdsaSig related error
39+
EcdsaSig(bitcoin::EcdsaSigError),
2640
/// We expected a push (including a `OP_1` but no other numeric pushes)
2741
ExpectedPush,
2842
/// The preimage to the hash function must be exactly 32 bytes.
@@ -39,8 +53,10 @@ pub enum Error {
3953
InsufficientSignaturesMultiSig,
4054
/// Invalid Sighash type
4155
InvalidSchnorrSigHashType(Vec<u8>),
56+
/// ecdsa Signature failed to verify
57+
InvalidEcdsaSignature(bitcoin::PublicKey),
4258
/// Signature failed to verify
43-
InvalidSignature(bitcoin::PublicKey),
59+
InvalidSchnorrSignature(bitcoin::XOnlyPublicKey),
4460
/// Last byte of this signature isn't a standard sighash type
4561
NonStandardSigHash(Vec<u8>),
4662
/// Miniscript error
@@ -60,21 +76,29 @@ pub enum Error {
6076
/// Any input witness apart from sat(sig) or nsat(0) leads to
6177
/// this error. This is network standardness assumption and miniscript only
6278
/// supports standard scripts
63-
PkEvaluationError(bitcoin::PublicKey),
79+
// note that BitcoinKey is not exported, create a data structure to convey the same
80+
// information in error
81+
PkEvaluationError(PkEvalErrInner),
6482
/// The Public Key hash check for the given pubkey. This occurs in `PkH`
6583
/// node when the given key does not match to Hash in script.
6684
PkHashVerifyFail(hash160::Hash),
6785
/// Parse Error while parsing a `stack::Element::Push` as a Pubkey. Both
6886
/// 33 byte and 65 bytes are supported.
6987
PubkeyParseError,
88+
/// Parse Error while parsing a `stack::Element::Push` as a XOnlyPublicKey (32 bytes)
89+
XOnlyPublicKeyParseError,
7090
/// Could not satisfy, relative locktime not met
7191
RelativeLocktimeNotMet(u32),
7292
/// Forward-secp related errors
7393
Secp(secp256k1::Error),
7494
/// Miniscript requires the entire top level script to be satisfied.
7595
ScriptSatisfactionError,
96+
/// Schnorr Signature error
97+
SchnorrSig(bitcoin::SchnorrSigError),
7698
/// Errors in signature hash calculations
7799
SighashError(bitcoin::util::sighash::Error),
100+
/// Taproot Annex Unsupported
101+
TapAnnexUnsupported,
78102
/// An uncompressed public key was encountered in a context where it is
79103
/// disallowed (e.g. in a Segwit script or p2wpkh output)
80104
UncompressedPubkey,
@@ -92,6 +116,34 @@ pub enum Error {
92116
VerifyFailed,
93117
}
94118

119+
/// A type of representing which keys errored during interpreter checksig evaluation
120+
// Note that we can't use BitcoinKey because it is not public
121+
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
122+
pub enum PkEvalErrInner {
123+
/// Full Key
124+
FullKey(bitcoin::PublicKey),
125+
/// XOnly Key
126+
XOnlyKey(bitcoin::XOnlyPublicKey),
127+
}
128+
129+
impl From<BitcoinKey> for PkEvalErrInner {
130+
fn from(pk: BitcoinKey) -> Self {
131+
match pk {
132+
BitcoinKey::Fullkey(pk) => PkEvalErrInner::FullKey(pk),
133+
BitcoinKey::XOnlyPublicKey(xpk) => PkEvalErrInner::XOnlyKey(xpk),
134+
}
135+
}
136+
}
137+
138+
impl fmt::Display for PkEvalErrInner {
139+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140+
match self {
141+
PkEvalErrInner::FullKey(pk) => pk.fmt(f),
142+
PkEvalErrInner::XOnlyKey(xpk) => xpk.fmt(f),
143+
}
144+
}
145+
}
146+
95147
#[doc(hidden)]
96148
impl From<secp256k1::Error> for Error {
97149
fn from(e: secp256k1::Error) -> Error {
@@ -106,6 +158,20 @@ impl From<bitcoin::util::sighash::Error> for Error {
106158
}
107159
}
108160

161+
#[doc(hidden)]
162+
impl From<bitcoin::EcdsaSigError> for Error {
163+
fn from(e: bitcoin::EcdsaSigError) -> Error {
164+
Error::EcdsaSig(e)
165+
}
166+
}
167+
168+
#[doc(hidden)]
169+
impl From<bitcoin::SchnorrSigError> for Error {
170+
fn from(e: bitcoin::SchnorrSigError) -> Error {
171+
Error::SchnorrSig(e)
172+
}
173+
}
174+
109175
#[doc(hidden)]
110176
impl From<::Error> for Error {
111177
fn from(e: ::Error) -> Error {
@@ -130,6 +196,12 @@ impl fmt::Display for Error {
130196
"required absolute locktime CLTV of {} blocks, not met",
131197
n
132198
),
199+
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+
}
204+
Error::EcdsaSig(ref s) => write!(f, "Ecdsa sig error: {}", s),
133205
Error::ExpectedPush => f.write_str("expected push in script"),
134206
Error::CouldNotEvaluate => f.write_str("Interpreter Error: Could not evaluate"),
135207
Error::HashPreimageLengthMismatch => f.write_str("Hash preimage should be 32 bytes"),
@@ -147,7 +219,8 @@ impl fmt::Display for Error {
147219
sig.to_hex()
148220
)
149221
}
150-
Error::InvalidSignature(pk) => write!(f, "bad signature with pk {}", pk),
222+
Error::InvalidEcdsaSignature(pk) => write!(f, "bad ecdsa signature with pk {}", pk),
223+
Error::InvalidSchnorrSignature(pk) => write!(f, "bad schnorr signature with pk {}", pk),
151224
Error::NonStandardSigHash(ref sig) => {
152225
write!(
153226
f,
@@ -165,12 +238,15 @@ impl fmt::Display for Error {
165238
Error::PkEvaluationError(ref key) => write!(f, "Incorrect Signature for pk {}", key),
166239
Error::PkHashVerifyFail(ref hash) => write!(f, "Pubkey Hash check failed {}", hash),
167240
Error::PubkeyParseError => f.write_str("could not parse pubkey"),
241+
Error::XOnlyPublicKeyParseError => f.write_str("could not parse x-only pubkey"),
168242
Error::RelativeLocktimeNotMet(n) => {
169243
write!(f, "required relative locktime CSV of {} blocks, not met", n)
170244
}
171245
Error::ScriptSatisfactionError => f.write_str("Top level script must be satisfied"),
172246
Error::Secp(ref e) => fmt::Display::fmt(e, f),
247+
Error::SchnorrSig(ref s) => write!(f, "Schnorr sig error: {}", s),
173248
Error::SighashError(ref e) => fmt::Display::fmt(e, f),
249+
Error::TapAnnexUnsupported => f.write_str("Encountered annex element"),
174250
Error::UncompressedPubkey => {
175251
f.write_str("uncompressed pubkey in non-legacy descriptor")
176252
}

0 commit comments

Comments
 (0)