-
Notifications
You must be signed in to change notification settings - Fork 301
Add Musig2 module #716
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 Musig2 module #716
Conversation
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.
Awesome!!! I would wait until the upstream PR merges (and releases) before merging this but I'm looking forward to it. I gave it a quick look anyway.
src/musig.rs
Outdated
// - Key agg cache is valid | ||
// - extra input is 32 bytes | ||
// This can only happen when the session id is all zeros | ||
Err(MusigNonceGenError::ZeroSession) |
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.
IMO this should be just panic
. It can only happen if someone passes wrong value to dangerous ID creation function.
447a94c
to
e730b8b
Compare
a91d293
to
8bbd0d2
Compare
This is a 10 thousand line diff, is something commited that shouldn't be? |
It updates the vendored library to bring in the upstream MuSig PR. |
Yes. For now, only the last three commits matter for review purposes. |
Cool, thanks. To clarify this is going to wait till upstream merges before being considered for merge, right? What sort of review are you chasing? |
@tcharding I will definitely not ack this until it's upstream is released. However I appreciate the experiment/demo. |
0a2361b
to
86e2b28
Compare
Yes, the idea is to wait for the upstream PR to be merged. |
secp256k1-sys/src/lib.rs
Outdated
impl MusigSecNonce { | ||
pub fn new() -> Self { | ||
MusigSecNonce([0; MUSIG_SECNONCE_LEN]) | ||
} |
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.
Isn't this highly misleading? If it's all-zeros it's not a nonce and thus broken. Where would one need 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.
Same as here: #716 (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.
This seems like a huge footgun.
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.
It's also not present in the current version of the PR.
secp256k1-sys/src/lib.rs
Outdated
MusigSecNonce([0; MUSIG_SECNONCE_LEN]) | ||
} | ||
|
||
/// Don't use this. Refer to the documentation of wrapper APIs in the crate. |
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 documentation of these methods is intended for the higher-level API implementors not for for end consumers so it should rather properly describe what's going on 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. Thanks.
secp256k1-sys/src/lib.rs
Outdated
impl_raw_debug!(MusigPubNonce); | ||
|
||
impl MusigPubNonce { | ||
pub fn new() -> Self { |
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 also broken.
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 as here: #716 (comment)
secp256k1-sys/src/lib.rs
Outdated
fn default() -> Self { | ||
Self::new() | ||
} | ||
} |
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.
It looks to me that none of these Default
s should exist. People should just use arrays or MaybeUninit<T>
to represent the uninitialized state.
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.
Are you suggesting something like this ?
let key_agg_cache = MaybeUninit::<ffi::MusigKeyAggCache>::uninit();
let mut key_agg_cache = key_agg_cache.assume_init();
This will cause UB (without MaybeUninit::write
).
The reason for pub fn new()
is that the internal array is private (ex: pub struct MusigKeyAggCache([c_uchar; MUSIG_KEYAGG_LEN]);
), which is consistent with the other structs in the code.
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, provide a function that constructs initialized types only.
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.
Oh, now I see that I was confused because these are the FFI structs. However, I still maintain they are highly confusing.
The correct usage (inside secp256k1::musig::MusigKeyAggCache::new
) is this:
let mut key_agg_cache = MaybeUninit::<ffi::MusigKeyAggCache>::uninit();
let mut agg_pk = MaybeUninit::<ffi::XOnlyPublicKey>::uninit();
unsafe {
if ffi::secp256k1_musig_pubkey_agg(
cx,
agg_pk.as_mut_ptr(),
key_agg_cache.as_mut_ptr(),
pubkeys.as_ptr(),
pubkey_ptrs.len(),
) == 0 {
panic!(...);
} else {
// secp256k1_musig_pubkey_agg overwrites the cache and the key so this is sound.
let key_agg_cache = key_agg_cache.assume_init();
let agg_pk = agg_pk.assume_init();
MusigKeyAggCache(key_agg_cache, 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.
Thanks for the clarification.
Done in 2ea5674
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've also applied the same approach to the other structs.
|
||
#[repr(C)] | ||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
pub struct MusigPartialSignature([c_uchar; MUSIG_PART_SIG_LEN]); |
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.
FTR these struct declarations looked wrong but are indeed correct based on the current API.
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.
Do you think they should be changed?
src/musig.rs
Outdated
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)] | ||
pub enum ParseError { | ||
/// Length mismatch | ||
ArgLenMismatch { |
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 usually name these InvalidLength
.
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. Thanks.
86e2b28
to
071ac15
Compare
Upstream was released yesterday |
Can you rebase and format each commit with the nightly formatter? That should fix CI. |
071ac15
to
7f76102
Compare
Yes, done. Thanks. |
Patch 1 can be removed now, right? Then your shellcheck CI fail should disappear. |
/// This is useful to reduce the communication between signers, because instead | ||
/// of everyone sending nonces to everyone else, there can be one party | ||
/// receiving all nonces, combining the nonces with this function and then | ||
/// sending only the combined nonce back to the signers. |
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.
In dd26c30:
In the upstream docs there's an extra line clarifying that if the aggregator lies, the signature will just be invalid. We should copy that here. (Will do in followup.)
/// let _agg_pk = key_agg_cache.agg_pk(); | ||
/// # } | ||
/// ``` | ||
pub fn new<C: Verification>(secp: &Secp256k1<C>, pubkeys: &[&PublicKey]) -> Self { |
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.
In dd26c30:
What if you pass an empty array of pubkeys? I'll play with this. Probably I'll just add an explict panic, though given that we have the same issue with partial sigs, I'm tempted to add an error variant for this, or maybe a nonemptyarray newtype. Will address in followup, at least to add a panic (I suspect the existing code will do weird potentially-UB things) but I might wind up just filing an issue.
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.
For your curiosity, what happens is that we try to pass a bad pointer to the C code, but our alignment trickery in secp256k1-sys catches it and panics (though I doubt the panic is very reliable). So on my system it's not actually UB, but it's pretty close.
src/musig.rs
Outdated
/// Generates a new session ID using thread RNG. | ||
#[cfg(all(feature = "rand", feature = "std"))] | ||
pub fn new() -> Self { | ||
Self::from_rng(&mut rand::thread_rng()) |
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.
In dd26c30:
Clippy is complaining about there being new
but not Default
. I think it's got a point, and that we should rename this function from_thread_rng
to make it more obvious that it's not a pure function. Will do in a followup.
I believe your comments have been addressed, and your review is blocking merge. I went through them all even though Github has literally hidden them and marked every one as "outdated". If I'm wrong I'd appreciate a heads up and for you to re-open your comments, maybe on my followup PR.
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.
ACK c6fc3d7; successfully ran local tests
Will open a followup PR tomorrow. |
Pretty-much all of the doctests included in rust-bitcoin#716 corresponded to an old version of the API. I'm unsure why it is that my local CI accepted this. The Github CI did not.
Pretty-much all of the doctests included in rust-bitcoin#716 corresponded to an old version of the API. I'm unsure why it is that my local CI accepted this. The Github CI did not.
Pretty-much all of the doctests included in rust-bitcoin#716 corresponded to an old version of the API. I'm unsure why it is that my local CI accepted this. The Github CI did not.
Pretty-much all of the doctests included in rust-bitcoin#716 corresponded to an old version of the API. I'm unsure why it is that my local CI accepted this. The Github CI did not.
Pretty-much all of the doctests included in rust-bitcoin#716 corresponded to an old version of the API. I'm unsure why it is that my local CI accepted this. The Github CI did not.
/// structure in memory can use the provided API functions for a safe standard | ||
/// workflow. | ||
/// | ||
/// Signers that pre-compute and save these nonces are not yet supported. Users |
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.
Stale? They can just call dangerous_into_bytes
and dangerous_from_bytes
.
/// Function to return a copy of the internal array. See WARNING before using this function. | ||
/// | ||
/// # Warning: | ||
/// This structure MUST NOT be copied or read or written to directly. A |
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 find this wording too paranoid and a lie(?). Not implementing Copy
and Clone
, seems reasonable, as it will lead the user to correct usage and reading the docs, great. But once they are using something named dangerous_*
it's clear that they should pay attention.
At that point seems unnecessary to tell them scary lies about MUST NOT be copied
? They must not reuse the nonce, that's all, right? They can copy it a hundred times, save it, restore it, etc., as long as they not re-use it. So they have been warned and I think misinformation will just potentially confuse them.
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.
Maybe "The nonce must not be reused, so to minimize the chance of it happening by accident this type doesn't implement Copy
or 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.
Yeah, I'm happy to tone this down (as well as the "not supported" language, which if interpreted as "you can't do it with the library" is false and if interpreted as "we won't help you" is meaningless because the software is provided as-is with no warranty).
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.
Updated #794. If you have further comments about the text please comment there.
@apoelstra yeah, IIRC I've reviewed all updates that they address my comments in the past. Sorry for not marking it clearly. (I've been traveling also, so was unable to do anything past week.) |
…ICIPANT_PUBKEYS 2481695 Add tests for BIP-373 PSBT_{IN,OUT}_MUSIG2_PARTICIPANT_PUBKEYS serialization and deserialization (Daniel Roberts) 3e8e6d9 Add BIP-373 PSBT_{IN,OUT}_MUSIG2_PARTICIPANT_PUBKEYS serialization and deserialization (Daniel Roberts) Pull request description: This change adds support for serializing and deserializing two PSBT keys from BIP-373: `PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS` and `PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS` This is a part of #4207 that can be implemented independently of the rest (which depends on rust-bitcoin/rust-secp256k1#716). I believe this satisfactorily avoids changing things multiple times on end users, *however* it's not *completely* transparent to end users, since any code that currently accesses these fields through `unknown` will need to be updated. Later, when `PSBT_IN_MUSIG2_PUB_NONCE` and `PSBT_IN_MUSIG2_PARTIAL_SIG` are supported, code will need to be updated a second time to retrieve them from the correct place instead of `unknown`. I'm of the opinion that this imposes a very minor maintenance burden, only consisting of *removing* deserialization code. ### Notes/Requests for feedback - For the most part I used my judgement rather than `cargo fmt` since `cargo fmt` already had a lot of other complaints, but of course I'll update if I need to. - To satisfy the requirement that every commit pass tests, the commit updating the psbt serde regression test should probably be squashed into the first commit, but I just wanted to confirm that before I did it. I suppose similarly, the test commit could be squashed as well? - I waffled between `musig2_participants` and `musig2_participant_pubkeys`, but I've decided to go with `musig2_participant_pubkeys` because that is consistent with Bitcoin Core ACKs for top commit: tcharding: ACK 2481695 apoelstra: ACK 2481695; successfully ran local tests Tree-SHA512: af884923593c9cbb24ff3f1f08219458538592fabde85d5d65bc2d9bc7bf0b1a73dac38d2c56303b4f3162088db129ea7e879c3d4b324e965933c121ef939a07
d611a4f musig: weaken/simplify warnings about nonce reuse (Andrew Poelstra) 8a43317 musig: add a bunch of unit tests (Andrew Poelstra) 40a8b65 musig: explicitly panic when given an empty slice of pubkeys to aggregate (Andrew Poelstra) ebdaec7 musig: clarify doc comment about aggregate nonce proxy (Andrew Poelstra) dc04575 musig: a couple small improvements of byte array APIs (Andrew Poelstra) c492c75 key: move pubkey_sort to method on Secp256k1; rename (Andrew Poelstra) ec66003 musig: remove SessionSecretRand::new constructor (Andrew Poelstra) 6d938d3 musig: add missing Panics sections to docs (Andrew Poelstra) 00c8c75 musig: remove outdated doc references to ZeroSession error (Andrew Poelstra) 3b0232a musig: fix all the doctests (Andrew Poelstra) 4dd861f stop using deprecated thread_rng (Andrew Poelstra) 9615ec8 context: whitelist new compiler warning (Andrew Poelstra) 7c56bcc clippy: whitelist a bunch of lints (Andrew Poelstra) 07922fd musig: fix a couple FFI bindings (Andrew Poelstra) f5f90af fmt: stop blacklisting secp256k1-sys; just fmt whole crate (Andrew Poelstra) Pull request description: This PR needs to be merged before the next release because the existing code has one instance of UB when passing an empty array to the aggregate nonce function. (Ok, there's a rust panic in our alignment code so maybe no bad pointers make it across the C boundary and we're ok. But it's near-UB.) This PR is the first one I created using jujutsu. One thing I notice is that the tool encourages you to produce way more commits than you would with git. Most of these are small. Let me know if you want me to squash any. ACKs for top commit: jlest01: ACK d611a4f jonasnick: ACK d611a4f modulo my comments on the PR (secret dependent branches) and that I only looked at the musig/libsecp-relevant bits. Tree-SHA512: a504912639bcb6296bd6fdf7a0533464ce9e9064d1c2bf06bf142b7749b4aaf75ed4bae10f9912b92191c64a453bcf56bbd001e3c99ab383e02de4676c7c6a69
/// Serialized length (in bytes) of a partial signature. | ||
/// This is the compact form (typically just the 32-byte scalar) that is used when communicating | ||
/// partial signatures to be combined into the final signature. | ||
pub const MUSIG_PART_SIG_SERIALIZED_LEN: usize = 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.
These musig constants are ~the only ones in rust-libsecp256k1 to use a _LEN
suffix (as opposed to _SIZE
) is there a reason for that? I'm working on a PR that touches them, and I think it makes sense to rename them to _SIZE
before the next release if that wasn't an intentional decision.
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.
Not intentional. Please rename them!
…ebase to version v0.6. 06ab8d0c1eb1c450f5e3f49aff3b2de6ce7eaa39 Update codebase to version 0.6 (jlest01) Pull request description: This PR updates the `secp256k1-sys` codebase to version v0.6. I'm not sure if there are more steps involved, but these commits work fine in rust-bitcoin/rust-secp256k1#716. ACKs for top commit: apoelstra: ACK 06ab8d0c1eb1c450f5e3f49aff3b2de6ce7eaa39; successfully ran local tests; thanks for iterating! Tree-SHA512: 9b17a977d000821b7044f3f1f1b7d1e3209197be270b1e6a577c9245e30f09c6d6c532c85ffae5df503bc2ce80ea0c79983bbe7f838d77efac79f022132409c2
c6fc3d75033ada214bbb17d0c82c7636a72994f5 run cargo fmt (Andrew Poelstra) 2f0f9d8532a67c6c9d54b7c2d616ea9ece8fd98d Add Musig2 example file (jlest01) dd26c3067044a89c503a32079c24227d6e1afca5 Add Musig2 module (jlest01) e90015e88c7604e26bc5f0dba4bc73179edc6221 Add function to sort public keys (jlest01) f384a2d28409460e92ff19d9acae665ff994fbe4 Add Rust FFI for Musig2 module (jlest01) a21d6ca0c3aaf34a1d7c3043aa7daa484e2f41cf Enable musig module in the `secp256k1-sys/build.rs` (jlest01) Pull request description: This PR adds a `musig` module based on bitcoin-core/secp256k1#1479. The structure is based on sanket1729's BlockstreamResearch/rust-secp256k1-zkp#48, but I removed the code related to adaptor signatures. There is an example file in `examples/musig.rs` and can be run with `cargo run --example musig --features "rand std"`. The `ffi` functions were added to `secp256k1-sys/src/lib.rs` and the API level functions to the new `src/musig.rs` file. ACKs for top commit: apoelstra: ACK c6fc3d75033ada214bbb17d0c82c7636a72994f5; successfully ran local tests Tree-SHA512: ea4cf0add66976604a01f7e2b44a2e4f82c3ff4a48edcb214ff8e2dc7c04992e7d5ae9e68a8f93ee21b8d478b96786083a335b46eefe4aa1af5e7c62421d1330
…ebase to version v0.6. 06ab8d0c1eb1c450f5e3f49aff3b2de6ce7eaa39 Update codebase to version 0.6 (jlest01) Pull request description: This PR updates the `secp256k1-sys` codebase to version v0.6. I'm not sure if there are more steps involved, but these commits work fine in rust-bitcoin/rust-secp256k1#716. ACKs for top commit: apoelstra: ACK 06ab8d0c1eb1c450f5e3f49aff3b2de6ce7eaa39; successfully ran local tests; thanks for iterating! Tree-SHA512: 9b17a977d000821b7044f3f1f1b7d1e3209197be270b1e6a577c9245e30f09c6d6c532c85ffae5df503bc2ce80ea0c79983bbe7f838d77efac79f022132409c2
c6fc3d75033ada214bbb17d0c82c7636a72994f5 run cargo fmt (Andrew Poelstra) 2f0f9d8532a67c6c9d54b7c2d616ea9ece8fd98d Add Musig2 example file (jlest01) dd26c3067044a89c503a32079c24227d6e1afca5 Add Musig2 module (jlest01) e90015e88c7604e26bc5f0dba4bc73179edc6221 Add function to sort public keys (jlest01) f384a2d28409460e92ff19d9acae665ff994fbe4 Add Rust FFI for Musig2 module (jlest01) a21d6ca0c3aaf34a1d7c3043aa7daa484e2f41cf Enable musig module in the `secp256k1-sys/build.rs` (jlest01) Pull request description: This PR adds a `musig` module based on bitcoin-core/secp256k1#1479. The structure is based on sanket1729's BlockstreamResearch/rust-secp256k1-zkp#48, but I removed the code related to adaptor signatures. There is an example file in `examples/musig.rs` and can be run with `cargo run --example musig --features "rand std"`. The `ffi` functions were added to `secp256k1-sys/src/lib.rs` and the API level functions to the new `src/musig.rs` file. ACKs for top commit: apoelstra: ACK c6fc3d75033ada214bbb17d0c82c7636a72994f5; successfully ran local tests Tree-SHA512: ea4cf0add66976604a01f7e2b44a2e4f82c3ff4a48edcb214ff8e2dc7c04992e7d5ae9e68a8f93ee21b8d478b96786083a335b46eefe4aa1af5e7c62421d1330
This PR adds a
musig
module based on bitcoin-core/secp256k1#1479.The structure is based on sanket1729's BlockstreamResearch/rust-secp256k1-zkp#48, but I removed the code related to adaptor signatures.
There is an example file in
examples/musig.rs
and can be run withcargo run --example musig --features "rand std"
.The
ffi
functions were added tosecp256k1-sys/src/lib.rs
and the API level functions to the newsrc/musig.rs
file.