-
Notifications
You must be signed in to change notification settings - Fork 139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add initial support for KeyExpr #443
base: master
Are you sure you want to change the base?
Conversation
62f896d
to
009186f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some initial feedback apart from the no-std failures.
|
||
#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)] | ||
/// Enum for representing keys in miniscript | ||
pub enum KeyExpr<Pk: MiniscriptKey> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you manually implement Debug?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't the output of debug be same as display? If not, what should be the format while debugging ?
For example if the key is musig(A,musig(B,C),musig(D))
what should debug print for this ?
src/miniscript/musig_key.rs
Outdated
use crate::MiniscriptKey; | ||
|
||
#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)] | ||
/// Enum for representing keys in miniscript |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
*representing musig keys
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok
src/miniscript/musig_key.rs
Outdated
/// Single-key (e.g pk(a), here 'a' is a single key) | ||
SingleKey(Pk), | ||
|
||
/// Collection of keys in used for multi-signature |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
musig signature. Don't use the term multi-signature here as it refers OP_CHECKMULSIG based constructions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok
src/miniscript/musig_key.rs
Outdated
type Err = KeyExprError; | ||
fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
let (key_tree, _) = Tree::from_slice(s).unwrap(); | ||
fn expr_from_tree<Pk: MiniscriptKey + FromStr>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should
impl FromTree for KeyExpr instead of this local function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implemented FromTreef for KeyExpr.
src/miniscript/musig_key.rs
Outdated
tree: &Tree, | ||
) -> Result<KeyExpr<Pk>, KeyExprError> { | ||
if tree.name == "musig" { | ||
let mut key_expr_vect = vec![]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bit: use _vec
instead of '_vect'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok
src/miniscript/musig_key.rs
Outdated
} | ||
Ok(KeyExpr::MuSig(key_expr_vect)) | ||
} else { | ||
if tree.name != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Empty is also a valid string. There is no reason to reject it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, removed the if condition.
src/miniscript/musig_key.rs
Outdated
Ok(KeyExpr::MuSig(key_expr_vect)) | ||
} else { | ||
if tree.name != "" { | ||
let single_key = match Pk::from_str(tree.name) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use map_err instead of match
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/miniscript/musig_key.rs
Outdated
|
||
#[test] | ||
fn test_one() { | ||
let dummy_key = "musig(A,B,musig(C,musig(D,E)))"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test case "musig(,,)" should also work. There should be no special rule to rule out empty strings
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes now "musig(,,)" also passes the test.
src/miniscript/musig_key.rs
Outdated
|
||
#[test] | ||
fn test_one() { | ||
let dummy_key = "musig(A,B,musig(C,musig(D,E)))"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: name this musig_key
instead of dummy_key
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/miniscript/musig_key.rs
Outdated
let dummy_key = "musig(A,B,musig(C,musig(D,E)))"; | ||
let pk = KeyExpr::<String>::from_str(dummy_key).unwrap(); | ||
println!("{}", pk); | ||
assert_eq!(dummy_key, format!("{}", pk)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you also more test cases here? Testing single key only. Musig with a single key.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added some more test cases.
6b1058c
to
102efbd
Compare
src/miniscript/musig_key.rs
Outdated
fn next(&mut self) -> Option<Self::Item> { | ||
while !self.stack.is_empty() { | ||
let last = self.stack.pop().expect("Size checked above"); | ||
match &*last { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Since we don't have any overloaded Deref
for KeyExpr
, ig it's safe to just match it as:
match &*last { | |
match last { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, changed accordingly.
match &*last { | |
match last { |
src/miniscript/musig_key.rs
Outdated
KeyExpr::MuSig(key_vec) => { | ||
// push the elements in reverse order | ||
for key in key_vec.iter().rev() { | ||
self.stack.push(key) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Since for loops themselves are implemented using iterators, there shouldn't much difference in performance.
KeyExpr::MuSig(key_vec) => { | |
// push the elements in reverse order | |
for key in key_vec.iter().rev() { | |
self.stack.push(key) | |
} | |
} | |
KeyExpr::MuSig(key_vec) => key_vec.iter().rev().for_each(|key| self.stack.push(key)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, done.
src/miniscript/musig_key.rs
Outdated
} | ||
|
||
#[derive(Debug, Clone)] | ||
/// Iterator for keyexpr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can add linkable references in docstrings, ex:- for KeyExpr
here. Check this out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks! I didn't know that.
Fixed it now.
src/miniscript/musig_key.rs
Outdated
impl<Pk: MiniscriptKey + FromStr> FromStr for KeyExpr<Pk> { | ||
type Err = Error; | ||
fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
let (key_tree, _) = Tree::from_slice(s).unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should propagate the error rather than panicking here. From what little I understand, we only use unwrap
in cases we're sure errors shouldn't occur. In this case, s
might be a malformed string and should be handled by the calling function itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this seems the right way of handling the error in this case.
Done.
src/miniscript/musig_key.rs
Outdated
let mut key_expr_vec = vec![]; | ||
for sub_tree in tree.args.iter() { | ||
let temp_res = KeyExpr::<Pk>::from_tree(sub_tree)?; | ||
key_expr_vec.push(temp_res); | ||
} | ||
Ok(KeyExpr::MuSig(key_expr_vec)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Can be re-writtten as:
let mut key_expr_vec = vec![]; | |
for sub_tree in tree.args.iter() { | |
let temp_res = KeyExpr::<Pk>::from_tree(sub_tree)?; | |
key_expr_vec.push(temp_res); | |
} | |
Ok(KeyExpr::MuSig(key_expr_vec)) | |
let key_expr_vec = tree | |
.args | |
.iter() | |
.map(|subtree| KeyExpr::<Pk>::from_tree(subtree)) | |
.collect::<Result<Vec<KeyExpr<Pk>>, Error>>()?; | |
Ok(KeyExpr::MuSig(key_expr_vec)) | |
It's not the cleanest solution so really upto you if you wanna include this :p
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks better and more readable. Changed according to the suggestion.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
match *self { | ||
KeyExpr::SingleKey(ref pk) => write!(f, "{}", pk), | ||
KeyExpr::MuSig(ref my_vec) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious here as to why we want to print all the musig keys in Display rather than outputting the key-aggregation of the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I discussed this with @sanket1729 , and it seems that this is the right way for implementing display involving Musig. Because we want to show/print the individual keys and not the final aggregate key.
7d29de6
to
e5ca0ae
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Partial review
src/descriptor/bare.rs
Outdated
@@ -219,7 +220,7 @@ impl<Pk: MiniscriptKey> Pkh<Pk> { | |||
/// sighash suffix. Includes the weight of the VarInts encoding the | |||
/// scriptSig and witness stack length. | |||
pub fn max_satisfaction_weight(&self) -> usize { | |||
4 * (1 + 73 + BareCtx::pk_len(&self.pk)) | |||
4 * (1 + 73 + BareCtx::pk_len(&KeyExpr::SingleKey(self.pk.clone()))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's convert back pk_len
to accept a &Pk
so that we avoid the clone here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add a test for Checking the len of 1) pk(uncompressedkey)
in Legacy. 2) pk(compressed
) in Segwitv0 3) pk(XonlyKey
) in Tap and 4) pk('musig') in tap
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have posted what to change in the comment at the function definition in the context.rs file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Revert back to &Pk.
- Add a test
check_script_size
covering all the four suggested tests.
src/interpreter/mod.rs
Outdated
let res = self.stack.evaluate_pk(&mut self.verify_sig, *pk); | ||
let res = match pk.single_key() { | ||
Some(pk) => self.stack.evaluate_pk(&mut self.verify_sig, *pk), | ||
None => None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are doing this is wrong. We should never encounter this because know the variant here is single_key
. We should either use expect on single_key
with a message stating "Musig keys cannot be parsed from Script"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. fixed as suggested.
src/interpreter/mod.rs
Outdated
.stack | ||
.evaluate_pk(&mut self.verify_sig, subs[node_state.n_evaluated]) | ||
{ | ||
let temp = match subs[node_state.n_evaluated].single_key() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Use a better name than temp
. Same note about using expect
here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
@@ -80,7 +81,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> { | |||
Pk::RawPkHash: 'a, | |||
{ | |||
match *self { | |||
Terminal::PkK(ref p) => pred(p), | |||
Terminal::PkK(ref p) => p.for_each_key(pred), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a test for_each_key
for a fragment containing musig.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a test for this test_for_each_key
in miniscript/mod.rs
src/miniscript/context.rs
Outdated
@@ -332,7 +339,7 @@ where | |||
/// Note that this includes the serialization prefix. Returns | |||
/// 34/66 for Bare/Legacy based on key compressedness | |||
/// 34 for Segwitv0, 33 for Tap | |||
fn pk_len<Pk: MiniscriptKey>(pk: &Pk) -> usize; | |||
fn pk_len<Pk: MiniscriptKey>(pk: &KeyExpr<Pk>) -> usize; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned earlier, we should not change this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But, we should add another method here with default implementation as
fn key_expr_len<Pk: MiniscriptKey>(pk: &KeyExpr) {
match pk {
SingleKey(pk) => Ctx::pk_len(&pk),
Musig(expr) => 33, // not 32 as mentioned in another comment below
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, created the new method with the default implementation.
src/miniscript/context.rs
Outdated
fn pk_len<Pk: MiniscriptKey>(pk: &KeyExpr<Pk>) -> usize { | ||
match pk { | ||
KeyExpr::<Pk>::SingleKey(_) => 34, | ||
KeyExpr::<Pk>::MuSig(_) => 32, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here and everywhere, where we 32
. It should actually be 33
as it also includes the push len prefix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. changed everywhere.
src/miniscript/context.rs
Outdated
Terminal::PkK(ref pk) => { | ||
if pk.is_uncompressed() { | ||
Terminal::PkK(ref key) => { | ||
if key.iter().all(|pk| !pk.is_uncompressed()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this is going to sound weird, but you would need to change to
key.iter().any(|pk| pk.is_uncompressed)
. Checking that some is not uncompressed is not the same as checking something is compressed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. changed as suggested.
src/miniscript/context.rs
Outdated
Terminal::MultiA(_, ref keys) => { | ||
if keys | ||
.iter() | ||
.all(|keyexpr| keyexpr.iter().all(|pk| !pk.is_uncompressed())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment about compressed keys
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
src/miniscript/context.rs
Outdated
fn pk_len<Pk: MiniscriptKey>(pk: &KeyExpr<Pk>) -> usize { | ||
match pk { | ||
KeyExpr::<Pk>::SingleKey(_) => 33, | ||
KeyExpr::<Pk>::MuSig(_) => 32, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The len should always be 33
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed everywhere.
src/miniscript/context.rs
Outdated
Terminal::PkK(key) => match key { | ||
KeyExpr::<Pk>::SingleKey(_pk) => Ok(()), | ||
KeyExpr::<Pk>::MuSig(_) => { | ||
return Err(Error::NonStandardBareScript); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are returning an incorrect variant here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed it to
Err(Error::ContextError(ScriptContextError::MusigNotAllowed(String::from(Self::name_str()))))
9480c72
to
aef9672
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Almost there. Some last few edits.
src/lib.rs
Outdated
@@ -99,6 +99,9 @@ extern crate alloc; | |||
#[cfg(not(feature = "std"))] | |||
extern crate hashbrown; | |||
|
|||
#[cfg(not(feature = "std"))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added this because the last CI check was failing. But it seems that this has nothing to do with it.
Removed it now
src/miniscript/iter.rs
Outdated
for key in keys { | ||
let temp: Vec<Pk::RawPkHash> = | ||
key.iter().map(|pk| pk.to_pubkeyhash()).collect(); | ||
res.extend(temp) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does extend take an iterator? We should not need to collect this separately and have a separate allocation for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment about collect everywhere in leafpk, leafpkh
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. I have removed the collect in leapk, leapkh, leapk_pkh.
src/miniscript/iter.rs
Outdated
} | ||
_ => None, | ||
} | ||
} | ||
} | ||
/// Parent iter for all the below iters | ||
pub struct BaseIter<'a, Pk: MiniscriptKey, Ctx: ScriptContext> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need to be pub?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, this is not being outside this module. I have changed it to private.
src/miniscript/musig_key.rs
Outdated
Pk: 'a, | ||
Pk::RawPkHash: 'a, | ||
{ | ||
let keys_res = self.iter().all(|my_key| pred(my_key)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: rename my_key
to key
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/policy/mod.rs
Outdated
for key in keys { | ||
policy_vec.push((Terminal::<Pk, Ctx>::PkK(key.clone())).lift()?) | ||
} | ||
// Semantic::Threshold(keys.len(), policy_vec) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove this comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed.
src/policy/mod.rs
Outdated
use super::super::miniscript::Miniscript; | ||
use super::{Concrete, Liftable, Semantic}; | ||
use crate::prelude::*; | ||
use crate::DummyKey; | ||
// use crate::MiniscriptKey; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary import
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed.
79f9fed
to
99fe93b
Compare
As of now, miniscript does not have support for handling Multi-signature keys. The changes introduced in this PR would be the first steps for supporting such keys.
The idea is that we will have support for legacy keys
SingleKey(Pk)
and Musig KeysMusig(vec<KeyExpr>)