rust-secp256k1-zkp icon indicating copy to clipboard operation
rust-secp256k1-zkp copied to clipboard

Musig2 support

Open sanket1729 opened this issue 3 years ago • 15 comments

This was taken by #29 , but significantly changed. In particular, I was super strict about adding unreachable! to make the APIs not return a result when possible. Each API has a doc test with guidelines of using it.

Things to pay special attention to:

  • Cross-check the use of unreachable! in the FFI calls to make sure they cannot panic for any other reasons.
  • Review the documentation about the usage of session_id and nonce re-use.

Edit: I wanted to add a extra-rand with time of day example, but doing so in the current MSRV is non-trivial. Will add an example after MSRV update

sanket1729 avatar Mar 21 '22 20:03 sanket1729

Marked as ready for review.

sanket1729 avatar Apr 15 '22 20:04 sanket1729

~Resolved~

sanket1729 avatar Apr 15 '22 20:04 sanket1729

Thanks for providing this! I played a bit with it and I'm curious about the reason for having separate Error enums. It feels a bit impractical to me as for example if I want to easily convert them to my library error type I need to implement From many times. Or maybe there is a specific reason behind this choice?

I also spotted quite some typos in the docs but not sure if it's the right time to be nitpicking (if it is let me know I can point them out).

Otherwise it's really neat :)!

Tibo-lg avatar Jun 23 '22 02:06 Tibo-lg

Or maybe there is a specific reason behind this choice?

@Tibo-lg Indeed. IMO, the return type of the function should only return the corresponding error variants that are expected from the function. If we collect all of them into a single enum, it is would impossible for the caller to know which variants are reachable for the corresponding function. This is handy for callers that want to act on an error instead of propagating forward.

match key_agg_cache.pubkey_ec_tweak_add(&secp, tweak) {
	Ok(_) => {},
	Err(MusigTweakError::InvalidTweak) => panic!("not possible in bip32 tweaking"),
}

If there is a single variant with everything, the caller has no choice but to propagate it forward. Unfortunately, there is a tradeoff that the implementors that want to forward the error instead of dealing with it would have more code to deal with. But overall, I think the tradeoff is clear as it clearly expresses the conditions the function errors with and allows sanely dealing with errors.

I also spotted quite some typos in the docs but not sure if it's the right time to be nitpicking (if it is let me know I can point them out).

Corrections welcome.

sanket1729 avatar Jun 24 '22 04:06 sanket1729

The MuSig implementation in libsecp256k1-zkp has significantly changed while it was updated to the BIP version 1. Therefore, we should update the libsecp256k1-zkp version we use here.

jonasnick avatar Mar 30 '23 12:03 jonasnick

@sanket1729 sanket1729 changed the title ~Comment out Wasm build on CI~ Musig2 support Mar 21, 2022

Well that escalated quickly.. I'm super excited about this though, guys!

stevenroose avatar Jul 17 '23 20:07 stevenroose

It would be nice to have serde serialization for pubnonces and partial signatures. (I may provide a patch for that)

Ademan avatar Jul 28 '23 22:07 Ademan

Exposing the inner bytes of ffi::MusigSecNonce and ffi::MusigSession would allow to serialize and store them.

secp256k1-zkp-sys/src/zkp.rs

 #[repr(C)]
 #[derive(Copy, Clone)]
-pub struct MusigSecNonce([c_uchar; MUSIG_SECNONCE_LEN]);
+pub struct MusigSecNonce(pub [c_uchar; MUSIG_SECNONCE_LEN]);
 impl_array_newtype!(MusigSecNonce, c_uchar, MUSIG_SECNONCE_LEN);
 impl_raw_debug!(MusigSecNonce);
 
@@ -1008,7 +1008,7 @@ impl MusigAggNonce {
 
 #[repr(C)]
 #[derive(Copy, Clone)]
-pub struct MusigSession([c_uchar; MUSIG_SESSION_LEN]);
+pub struct MusigSession(pub [c_uchar; MUSIG_SESSION_LEN]);
 impl_array_newtype!(MusigSession, c_uchar, MUSIG_SESSION_LEN);
 impl_raw_debug!(MusigSession);

src/zkp/musig.rs

 use crate::{Signing, Verification};
+use ffi::{MUSIG_SECNONCE_LEN, MUSIG_SESSION_LEN};
 use secp256k1::Parity;

impl MusigSecNonce {
     pub fn as_mut_ptr(&mut self) -> *mut ffi::MusigSecNonce {
         &mut self.0
     }
+
+    /// Function to return a copy of the internal array
+    pub fn serialize(&self) -> [u8; MUSIG_SECNONCE_LEN] {
+        let ffi::MusigSecNonce(array) = &self.0;
+        *array
+    }
+
+    /// Function to create a new MusigKeyAggCoef from a 32 byte array
+    pub fn from_slice(array: [u8; MUSIG_SECNONCE_LEN]) -> Self {
+        MusigSecNonce(ffi::MusigSecNonce(array))
+    }
 }
 
 impl CPtr for MusigSession {
     type Target = ffi::MusigSession;
@@ -1671,6 +1695,17 @@ impl MusigSession {
     pub fn as_mut_ptr(&mut self) -> *mut ffi::MusigSession {
         &mut self.0
     }
+
+    /// Function to return a copy of the internal array
+    pub fn serialize(&self) -> [u8; MUSIG_SESSION_LEN] {
+        let ffi::MusigSession(array) = &self.0;
+        *array
+    }
+
+    /// Function to create a new MusigKeyAggCoef from a 32 byte array
+    pub fn from_slice(array: [u8; MUSIG_SESSION_LEN]) -> Self {
+        MusigSession(ffi::MusigSession(array))
+    }
 }


ssantos21 avatar Sep 05 '23 02:09 ssantos21

Hello everyone. Sorry for the delay in working on this.

@ssantos21, there was a good security reason to disallow serialization APIs for SecNonce. I have prefix dangerous to those in the name and doc-comment explaining the reason. I am yet sure the use-case of serializing sessions, but we can do that as a follow-up PR.

I suggest we review this PR for correctness, any incomplete serialization features can be added as a followup.

Ready for review. cc @stevenroose @jonasnick @Ademan @ssantos21 @apoelstra

sanket1729 avatar Jan 07 '24 16:01 sanket1729

Not actively reviewing, but trying to use this.

The new_musig_nonce_pair function has this sentence in the documentation that is not correct english and confuses me:

If you cannot provide a sec_key, session_id UNIFORMLY RANDOM AND KEPT SECRET (even from other signers).

This makes me wonder, the name "SessionId" somehow made me thing it had to be shared between everyone in the same signing session. But from that sentence maybe it seems that it should be locally random and not shared?

stevenroose avatar Jan 16 '24 18:01 stevenroose

Are you planning serde serialization for the types?

stevenroose avatar Jan 17 '24 00:01 stevenroose

Suggestion: change the MusigKeyAggCache pubkey argument to an iterator so that the user doesn't necessarily have to allocate.

    pub fn new<C: Verification>(secp: &Secp256k1<C>, pubkeys: &[PublicKey]) -> Self {

EDIT: Same goes for nonces. I've been using this for a bit and it seems that a common situation is that you have "all other pubkeys" or "all other nonces", plus your own one and then you either have to re-allocate just to add your own, where with iterators, you could just .iter().chain(Some(&mine)).

So the argument would be impl IntoIterator<Item = PublicKey> and MusigPubNonce respectively. They are both Copy, so no need to take a ref iter.

stevenroose avatar Jan 18 '24 18:01 stevenroose

@stevenroose, thanks for the testing this and providing valuable user feedback. Working on suggested changes.

sanket1729 avatar Jan 29 '24 09:01 sanket1729

Is this PR still active? What can be done for the merge ?

jlest01 avatar Jul 07 '24 02:07 jlest01

cc @jonasnick can you take a fresh look at this?

apoelstra avatar Jul 29 '24 14:07 apoelstra

Damn we missing @sanket1729 here..

stevenroose avatar Oct 06 '24 23:10 stevenroose

Given https://github.com/bitcoin-core/secp256k1/pull/1479 was merged, does it make sense to focus on https://github.com/rust-bitcoin/rust-secp256k1/pull/716 ?

Ademan avatar Oct 07 '24 20:10 Ademan

Closing this in favor of upstream PR linked in the comment above

sanket1729 avatar Oct 09 '24 08:10 sanket1729