Skip to content

Commit 79f9fed

Browse files
committed
Add support for KeyExpr
1 parent d589fe9 commit 79f9fed

File tree

16 files changed

+1222
-167
lines changed

16 files changed

+1222
-167
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ rand = ["bitcoin/rand"]
2424
bitcoin = { version = "0.28.1", default-features = false }
2525
serde = { version = "1.0", optional = true }
2626
hashbrown = { version = "0.11", optional = true }
27+
secp256k1-zkp = { git = "https://github.com/sanket1729/rust-secp256k1-zkp", branch = "pr29", optional = true}
2728

2829
[dev-dependencies]
2930
bitcoind = {version = "0.26.1", features=["22_0"]}

src/descriptor/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use bitcoin::{self, secp256k1, Address, Network, Script, TxIn};
3434
use sync::Arc;
3535

3636
use self::checksum::verify_checksum;
37+
use crate::miniscript::musig_key::KeyExpr;
3738
use crate::miniscript::{Legacy, Miniscript, Segwitv0};
3839
use crate::prelude::*;
3940
use crate::{
@@ -180,7 +181,7 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
180181
// roundabout way to constuct `c:pk_k(pk)`
181182
let ms: Miniscript<Pk, BareCtx> =
182183
Miniscript::from_ast(miniscript::decode::Terminal::Check(Arc::new(
183-
Miniscript::from_ast(miniscript::decode::Terminal::PkK(pk))
184+
Miniscript::from_ast(miniscript::decode::Terminal::PkK(KeyExpr::SingleKey(pk)))
184185
.expect("Type check cannot fail"),
185186
)))
186187
.expect("Type check cannot fail");

src/interpreter/mod.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,9 @@ where
593593
Terminal::PkK(ref pk) => {
594594
debug_assert_eq!(node_state.n_evaluated, 0);
595595
debug_assert_eq!(node_state.n_satisfied, 0);
596+
let pk = pk
597+
.single_key()
598+
.expect("Musig keys cannot be parsed from Script");
596599
let res = self.stack.evaluate_pk(&mut self.verify_sig, *pk);
597600
if res.is_some() {
598601
return res;
@@ -868,10 +871,11 @@ where
868871
// evaluate each key with as a pk
869872
// note that evaluate_pk will error on non-empty incorrect sigs
870873
// push 1 on satisfied sigs and push 0 on empty sigs
871-
match self
872-
.stack
873-
.evaluate_pk(&mut self.verify_sig, subs[node_state.n_evaluated])
874-
{
874+
let pkk = subs[node_state.n_evaluated]
875+
.single_key()
876+
.expect("Musig keys cannot be parsed from Script");
877+
let res = self.stack.evaluate_pk(&mut self.verify_sig, *pkk);
878+
match res {
875879
Some(Ok(x)) => {
876880
self.push_evaluation_state(
877881
node_state.node,

src/lib.rs

+7
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ extern crate alloc;
9999
#[cfg(not(feature = "std"))]
100100
extern crate hashbrown;
101101

102+
#[cfg(feature = "std")]
103+
extern crate secp256k1_zkp;
104+
102105
#[cfg(any(feature = "std", test))]
103106
extern crate core;
104107

@@ -775,6 +778,8 @@ pub enum Error {
775778
TrNoScriptCode,
776779
/// No explicit script for Tr descriptors
777780
TrNoExplicitScript,
781+
/// Parsing error for single key
782+
SingleKeyParseError,
778783
}
779784

780785
// https://github.com/sipa/miniscript/pull/5 for discussion on this number
@@ -848,6 +853,7 @@ impl fmt::Display for Error {
848853
Error::TaprootSpendInfoUnavialable => write!(f, "Taproot Spend Info not computed."),
849854
Error::TrNoScriptCode => write!(f, "No script code for Tr descriptors"),
850855
Error::TrNoExplicitScript => write!(f, "No script code for Tr descriptors"),
856+
Error::SingleKeyParseError => f.write_str("not able to parse the single key"),
851857
}
852858
}
853859
}
@@ -888,6 +894,7 @@ impl error::Error for Error {
888894
| BareDescriptorAddr
889895
| TaprootSpendInfoUnavialable
890896
| TrNoScriptCode
897+
| SingleKeyParseError
891898
| TrNoExplicitScript => None,
892899
Script(e) => Some(e),
893900
AddrError(e) => Some(e),

src/miniscript/astelem.rs

+19-15
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use bitcoin::blockdata::{opcodes, script};
2626
use sync::Arc;
2727

2828
use crate::miniscript::context::SigType;
29+
use crate::miniscript::musig_key::KeyExpr;
2930
use crate::miniscript::types::{self, Property};
3031
use crate::miniscript::ScriptContext;
3132
use crate::prelude::*;
@@ -80,7 +81,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
8081
Pk::RawPkHash: 'a,
8182
{
8283
match *self {
83-
Terminal::PkK(ref p) => pred(p),
84+
Terminal::PkK(ref p) => p.for_each_key(pred),
8485
Terminal::PkH(ref p) => pred(p),
8586
Terminal::RawPkH(..)
8687
| Terminal::After(..)
@@ -112,9 +113,8 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
112113
&& c.real_for_each_key(pred)
113114
}
114115
Terminal::Thresh(_, ref subs) => subs.iter().all(|sub| sub.real_for_each_key(pred)),
115-
Terminal::Multi(_, ref keys) | Terminal::MultiA(_, ref keys) => {
116-
keys.iter().all(|key| pred(key))
117-
}
116+
Terminal::Multi(_, ref keys) => keys.iter().all(|key| pred(key)),
117+
Terminal::MultiA(_, ref keys) => keys.iter().all(|key| key.for_each_key(&mut *pred)),
118118
}
119119
}
120120

@@ -125,7 +125,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
125125
T: Translator<Pk, Q, E>,
126126
{
127127
let frag: Terminal<Q, CtxQ> = match *self {
128-
Terminal::PkK(ref p) => Terminal::PkK(t.pk(p)?),
128+
Terminal::PkK(ref p) => Terminal::PkK(p.translate_pk(t)?),
129129
Terminal::PkH(ref p) => Terminal::PkH(t.pk(p)?),
130130
Terminal::RawPkH(ref p) => Terminal::RawPkH(t.pkh(p)?),
131131
Terminal::After(n) => Terminal::After(n),
@@ -186,7 +186,8 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
186186
Terminal::Multi(k, keys?)
187187
}
188188
Terminal::MultiA(k, ref keys) => {
189-
let keys: Result<Vec<Q>, _> = keys.iter().map(|k| t.pk(k)).collect();
189+
let keys: Result<Vec<KeyExpr<Q>>, _> =
190+
keys.iter().map(|k| k.translate_pk(t)).collect();
190191
Terminal::MultiA(k, keys?)
191192
}
192193
};
@@ -455,7 +456,7 @@ impl_from_tree!(
455456
}
456457
let mut unwrapped = match (frag_name, top.args.len()) {
457458
("pk_k", 1) => {
458-
expression::terminal(&top.args[0], |x| Pk::from_str(x).map(Terminal::PkK))
459+
expression::unary(top, Terminal::PkK)
459460
}
460461
("pk_h", 1) => expression::terminal(&top.args[0], |x| Pk::from_str(x).map(Terminal::PkH)),
461462
("after", 1) => expression::terminal(&top.args[0], |x| {
@@ -522,15 +523,18 @@ impl_from_tree!(
522523
return Err(errstr("higher threshold than there were keys in multi"));
523524
}
524525

525-
let pks: Result<Vec<Pk>, _> = top.args[1..]
526-
.iter()
527-
.map(|sub| expression::terminal(sub, Pk::from_str))
528-
.collect();
529-
530526
if frag_name == "multi" {
527+
let pks: Result<Vec<Pk>, _> = top.args[1..]
528+
.iter()
529+
.map(|sub| expression::terminal(sub, Pk::from_str))
530+
.collect();
531531
pks.map(|pks| Terminal::Multi(k, pks))
532532
} else {
533533
// must be multi_a
534+
let pks: Result<Vec<KeyExpr<Pk>>, _> = top.args[1..]
535+
.iter()
536+
.map(|sub| KeyExpr::<Pk>::from_tree(sub))
537+
.collect();
534538
pks.map(|pks| Terminal::MultiA(k, pks))
535539
}
536540
}
@@ -734,7 +738,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
734738
builder = builder.push_ms_key::<_, Ctx>(&keys[0]);
735739
builder = builder.push_opcode(opcodes::all::OP_CHECKSIG);
736740
for pk in keys.iter().skip(1) {
737-
builder = builder.push_ms_key::<_, Ctx>(pk);
741+
builder = builder.push_ms_key::<_, Ctx>(&pk);
738742
builder = builder.push_opcode(opcodes::all::OP_CHECKSIGADD);
739743
}
740744
builder
@@ -753,7 +757,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
753757
/// will handle the segwit/non-segwit technicalities for you.
754758
pub fn script_size(&self) -> usize {
755759
match *self {
756-
Terminal::PkK(ref pk) => Ctx::pk_len(pk),
760+
Terminal::PkK(ref pk) => Ctx::key_expr_len(pk),
757761
Terminal::PkH(..) | Terminal::RawPkH(..) => 24,
758762
Terminal::After(n) => script_num_size(n as usize) + 1,
759763
Terminal::Older(n) => script_num_size(n as usize) + 1,
@@ -798,7 +802,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
798802
Terminal::MultiA(k, ref pks) => {
799803
script_num_size(k)
800804
+ 1 // NUMEQUAL
801-
+ pks.iter().map(|pk| Ctx::pk_len(pk)).sum::<usize>() // n keys
805+
+ pks.iter().map(|pk| Ctx::key_expr_len(pk)).sum::<usize>() // n keys
802806
+ pks.len() // n times CHECKSIGADD
803807
}
804808
}

src/miniscript/context.rs

+84-25
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use crate::miniscript::limits::{
2626
MAX_SCRIPT_SIZE, MAX_STACK_SIZE, MAX_STANDARD_P2WSH_SCRIPT_SIZE,
2727
MAX_STANDARD_P2WSH_STACK_ITEMS,
2828
};
29+
use crate::miniscript::musig_key::KeyExpr;
2930
use crate::miniscript::types;
3031
use crate::prelude::*;
3132
use crate::util::witness_to_scriptsig;
@@ -77,6 +78,8 @@ pub enum ScriptContextError {
7778
CheckMultiSigLimitExceeded,
7879
/// MultiA is only allowed in post tapscript
7980
MultiANotAllowed,
81+
/// Musig is only allowed in tapscript and taproot descriptors
82+
MusigNotAllowed(String),
8083
}
8184

8285
#[cfg(feature = "std")]
@@ -100,6 +103,7 @@ impl error::Error for ScriptContextError {
100103
| TaprootMultiDisabled
101104
| StackSizeLimitExceeded { .. }
102105
| CheckMultiSigLimitExceeded
106+
| MusigNotAllowed(_)
103107
| MultiANotAllowed => None,
104108
}
105109
}
@@ -180,6 +184,9 @@ impl fmt::Display for ScriptContextError {
180184
ScriptContextError::MultiANotAllowed => {
181185
write!(f, "Multi a(CHECKSIGADD) only allowed post tapscript")
182186
}
187+
ScriptContextError::MusigNotAllowed(ref err) => {
188+
write!(f, "Musig is only allowed in tapscript : err {}", err)
189+
}
183190
}
184191
}
185192
}
@@ -334,6 +341,13 @@ where
334341
/// 34 for Segwitv0, 33 for Tap
335342
fn pk_len<Pk: MiniscriptKey>(pk: &Pk) -> usize;
336343

344+
/// Get the len of the keyexpr
345+
fn key_expr_len<Pk: MiniscriptKey>(pk: &KeyExpr<Pk>) -> usize {
346+
match pk {
347+
KeyExpr::SingleKey(pk) => Self::pk_len(pk),
348+
KeyExpr::MuSig(_) => 33,
349+
}
350+
}
337351
/// Local helper function to display error messages with context
338352
fn name_str() -> &'static str;
339353
}
@@ -384,12 +398,21 @@ impl ScriptContext for Legacy {
384398
}
385399

386400
match ms.node {
387-
Terminal::PkK(ref key) if key.is_x_only_key() => {
388-
return Err(ScriptContextError::XOnlyKeysNotAllowed(
389-
key.to_string(),
390-
Self::name_str(),
391-
))
392-
}
401+
Terminal::PkK(ref key) => match key {
402+
KeyExpr::<Pk>::SingleKey(pk) => {
403+
if pk.is_x_only_key() {
404+
return Err(ScriptContextError::XOnlyKeysNotAllowed(
405+
pk.to_string(),
406+
Self::name_str(),
407+
));
408+
}
409+
}
410+
KeyExpr::<Pk>::MuSig(_) => {
411+
return Err(ScriptContextError::MusigNotAllowed(String::from(
412+
Self::name_str(),
413+
)))
414+
}
415+
},
393416
Terminal::Multi(_k, ref pks) => {
394417
if pks.len() > MAX_PUBKEYS_PER_MULTISIG {
395418
return Err(ScriptContextError::CheckMultiSigLimitExceeded);
@@ -490,17 +513,24 @@ impl ScriptContext for Segwitv0 {
490513
}
491514

492515
match ms.node {
493-
Terminal::PkK(ref pk) => {
494-
if pk.is_uncompressed() {
495-
return Err(ScriptContextError::CompressedOnly(pk.to_string()));
496-
} else if pk.is_x_only_key() {
497-
return Err(ScriptContextError::XOnlyKeysNotAllowed(
498-
pk.to_string(),
516+
Terminal::PkK(ref key) => match key {
517+
KeyExpr::<Pk>::SingleKey(pk) => {
518+
if pk.is_uncompressed() {
519+
return Err(ScriptContextError::CompressedOnly(pk.to_string()));
520+
} else if pk.is_x_only_key() {
521+
return Err(ScriptContextError::XOnlyKeysNotAllowed(
522+
pk.to_string(),
523+
Self::name_str(),
524+
));
525+
}
526+
Ok(())
527+
}
528+
KeyExpr::<Pk>::MuSig(_) => {
529+
return Err(ScriptContextError::MusigNotAllowed(String::from(
499530
Self::name_str(),
500-
));
531+
)));
501532
}
502-
Ok(())
503-
}
533+
},
504534
Terminal::Multi(_k, ref pks) => {
505535
if pks.len() > MAX_PUBKEYS_PER_MULTISIG {
506536
return Err(ScriptContextError::CheckMultiSigLimitExceeded);
@@ -618,13 +648,24 @@ impl ScriptContext for Tap {
618648
}
619649

620650
match ms.node {
621-
Terminal::PkK(ref pk) => {
622-
if pk.is_uncompressed() {
623-
return Err(ScriptContextError::UncompressedKeysNotAllowed);
651+
Terminal::PkK(ref key) => {
652+
if key.iter().any(|pk| pk.is_uncompressed()) {
653+
Err(ScriptContextError::UncompressedKeysNotAllowed)
654+
} else {
655+
Ok(())
624656
}
625-
Ok(())
626657
}
627658
Terminal::Multi(..) => Err(ScriptContextError::TaprootMultiDisabled),
659+
Terminal::MultiA(_, ref keys) => {
660+
if keys
661+
.iter()
662+
.all(|keyexpr| keyexpr.iter().any(|pk| pk.is_uncompressed()))
663+
{
664+
Err(ScriptContextError::UncompressedKeysNotAllowed)
665+
} else {
666+
Ok(())
667+
}
668+
}
628669
_ => Ok(()),
629670
}
630671
}
@@ -712,11 +753,23 @@ impl ScriptContext for BareCtx {
712753
return Err(ScriptContextError::MaxWitnessScriptSizeExceeded);
713754
}
714755
match ms.node {
715-
Terminal::PkK(ref key) if key.is_x_only_key() => {
716-
return Err(ScriptContextError::XOnlyKeysNotAllowed(
717-
key.to_string(),
718-
Self::name_str(),
719-
))
756+
Terminal::PkK(ref key) => {
757+
match key {
758+
KeyExpr::<Pk>::SingleKey(pk) => {
759+
if pk.is_x_only_key() {
760+
return Err(ScriptContextError::XOnlyKeysNotAllowed(
761+
key.to_string(),
762+
Self::name_str(),
763+
));
764+
}
765+
}
766+
KeyExpr::<Pk>::MuSig(_) => {
767+
return Err(ScriptContextError::MusigNotAllowed(String::from(
768+
Self::name_str(),
769+
)))
770+
}
771+
}
772+
Ok(())
720773
}
721774
Terminal::Multi(_k, ref pks) => {
722775
if pks.len() > MAX_PUBKEYS_PER_MULTISIG {
@@ -753,7 +806,13 @@ impl ScriptContext for BareCtx {
753806
match &ms.node {
754807
Terminal::Check(ref ms) => match &ms.node {
755808
Terminal::RawPkH(_pkh) => Ok(()),
756-
Terminal::PkK(_pk) | Terminal::PkH(_pk) => Ok(()),
809+
Terminal::PkH(_pk) => Ok(()),
810+
Terminal::PkK(key) => match key {
811+
KeyExpr::<Pk>::SingleKey(_pk) => Ok(()),
812+
KeyExpr::<Pk>::MuSig(_) => Err(Error::ContextError(
813+
ScriptContextError::MusigNotAllowed(String::from(Self::name_str())),
814+
)),
815+
},
757816
_ => Err(Error::NonStandardBareScript),
758817
},
759818
Terminal::Multi(_k, subs) if subs.len() <= 3 => Ok(()),

0 commit comments

Comments
 (0)